Pluggable Authentication Modules

PAM is a system of libraries that handle the authentication of applications/services on the system. The main feature of PAM is that the nature of the authentication is dynamically configurable through files in the /etc/pam.d directory.

PAM separates the tasks of authentication into 4 independent management groups:

  • Account → provides account verification of the kind: is the user authorised to use this service? Is the password expired? Has the user reached its maximum number of sessions?

  • Authentication → authenticates users and their credentials (via password, public keys, biometrics, Kerberos, etc)

  • Password → deals with the updates to the authentication mechanisms and there is only module of each of them

  • Session → deals with actions that need to be executed before the service is provided to the user (i.e. logging, mounting file systems, auditing, etc)

The syntax of all files in /etc/pam.d follows the pattern:

               management_group          control         module         module-arguments

There control keyword defines PAM’s behaviour should the module fail in its authentication tasks. There are 6 possible values:

– required: failure of this module will lead to returning failure but only after the remaining stacked modules have been invoked

– requisite: failure will return failure and yield control back to the application of calling PAM stack

– sufficient: success of this module when no previous required module exists yields control back to the application or calling PAM stack

– optional: success or failure is only relevant when this module is the only one in the stack

– include: includes all lines of the given group (account, auth, password or session) from the specified file

– substack: includes all lines of the given group from the specified file but success or failure of the substack won’t yield control back to the application or calling PAM stack

Let’s list now the PAM modules available and their intended use:

pam_access.so: enforces the access restrictions laid out in /etc/security/access.conf.
pam_cap.so: enforces the policies specified in /etc/security/capability.conf.
pam_chroot.so: enforces the chroot settings in /etc/security/chroot.conf.
pam_console.so: enables certain console features that are not there by default
pam_cracklib.so: checks the passwords provided against a dictionary
pam_debug.so: this module is used to debug PAM
pam_deny.so: this module always denies access and it is only suitable for OTHER entries
pam_echo.so: used to print user messages
pam_env.so: used to set/unset environment variables
pam_exec.so: used to call external commands from within PAM
pam_faildelay.so: change the re-authentication delay per-application
pam_faillock.so: used to keep track of failed logins during time intervals to lock accounts
pam_fprintd.so: used for fingerprinting authentication
pam_ftp.so: used to provide anonymous ftp mode of access
pam_gnome_keyring.so: used for GNOME keyring password manager
pam_group.so: authenticates groups rather than users as per /etc/security/group.conf
pam_issue.so: used to prepend an issue file to the username prompt
pam_keyinit.so: used to manage session keyrings
pam_krb5.so: used to authenticate with Kerberos 5
pam_lastlog.so: used to display the user’s last login
pam_ldap.so: used for LDAP authentication
pam_limits.so: used to enforce the resource limits stated in /etc/secutity/limits.conf
pam_listfile.so: used to allow/deny services based on a customised file
pam_localuser.so: used to restrict access to users listed in /etc/passwd
pam_loginuid.so: used to set the loginuid and should not be used in su or sudo
pam_mail.so: used to inform the user about new email upon login
pam_mkhomedir.so: used to create the user’s HOME if it doesn’t exist (i.e. LDAP, NIS, etc)
pam_motd.so: used to display the motd file
pam_namespace.so: used to polyinstantiate directories based on SELinux settings
pam_nologin.so: prevents non-root users from logging if a watchfile exists
pam_passwdqc.so: used to enforce password quality
pam_permit.so: used to always permit access
pam_pkcs11.so: used for PKCS#11 token libraries
pam_postgresok.so: used for PostgreSQL databases
pam_pwhistory.so: used to remember last password used
pam_pwquality.so: used to enforce password quality
pam_rhosts.so: used for rhosts
pam_rootok.so: gives access if uid=0
pam_securetty.so: enforces the restrictions laid out in /etc/secure/securetty
pam_selinux.so: used to enforce SELinux security
pam_sepermit.so: used to enforce SELinux security depending on its enforcement status
pam_shells.so: used to check for valid login shell
pam_ssh.so: used to authenticate with SSH private keys
pam_sss.so: used for System Security Services Daemon or SSSD
pam_succeed_if.so: used to allow/deny service based on condition (usually UID)
pam_systemd.so: registers user sessions with the systemd login manager
pam_time.so: used to enforce login time restrictions specified in /etc/security/time.conf
pam_timestamp.so: used to authenticate based on cached successful attempts
pam_tty_audit.so: enables auditing of user’s TTYs
pam_umask.so: used to set the umask
pam_unix.so: traditional password authentication using /etc/passwd & /etc/shadow
pam_userdb.so: used to verify user/passwd pairs against a BerkeleyDB
pam_warn.so: logs the service, terminal, user, remote user and remote host to syslog
pam_wheel.so: permits access only to members of the wheel group
pam_xauth.so: used to forward xauth keys between users

Now that we have an idea of what each modules does, let’s have a look at the files in /etc/pam.d that determine the authentication process of the different services.

root:~> cat /etc/pam.d/password-­auth
# This file is auto­generated.
# User changes will be destroyed the next time authconfig is run.
auth required pam_env.so
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 1000 quiet_success
auth required pam_deny.so

account required pam_unix.so
account sufficient pam_localuser.so
account sufficient pam_succeed_if.so uid < 1000 quiet
account required pam_permit.so

password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password required pam_deny.so

session optional pam_keyinit.so revoke
session required pam_limits.so
­session optional pam_systemd.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so

The auth section sets the environment (pam_env), prompts the user for the password and if it is the right one it returns success (pam_unix). If the password is null ( “nullok” ) it moves on to the next module that returns success if the UID is that of a normal user (pam_succeed_if). Otherwise the service access should be denied (pam_deny).

The account section prompts for the password (pam_unix) and allows the service if the user is local (pam_localuser) or the “UID < 1000” (pam_succeed_if) or by default (pam_permit).

The password section checks the password quality (pam_pwquality) only for local users and changes it if that is what is intended. It prompts for a password (pam_unix), accepts null passwords, uses the SHA512 encryption algorithm and syncs /etc/passwd & /etc/shadow. The password-auth file is refered to by quite a few others with include & substack calls as it performs the challenge/response password authentication.

Finally, the session section gets the keyring from the invoking session, enforces the limits set in /etc/security/limits.conf, optionally registers the session in the systemd session manager and finally prompts for the password.

The system-auth file is exactly the same as password-auth except that it also allows fingerprinting authorisation. So a call to system-auth is in practical terms the same as a call to password-auth.

Let’s proceed to have a look at the PAM modules used in the textual or non-graphical login process (multi-user.target) from the security perspective.

# cat /etc/pam.d/login
auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so
auth substack system­auth
auth include postlogin

account required pam_nologin.so
account include system­auth

password include system­auth

# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
session optional pam_console.so

# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open
session required pam_namespace.so
session required pam_limits.so
session optional pam_keyinit.so force revoke
session include system­auth
session include postlogin
­session optional pam_ck_connector.so

In the auth section we see that the first item enforces the security restrictions stated in /etc/security/securetty and then calls the auth stack in password-auth.

The account section checks if the nologin watchfile exists to restrict non-root logins, and then proceeds to execute the account stack of password-auth.

The password section is a simple call to the password stack of password-auth.

Finally, the session section enforces SELinux security and limits.conf restrictions, and enables polyinstantiation of directories.

We see that access restrictions in /etc/security/access.conf are ignored as no module enforces them. To change that we would have to add the following line just after the pam_nologin.so module call:

      account      required       pam_access.so

Additionally, we might want to add a warning to unauthorised users to stay away from the system (that helps a lot…). We can do that by writing a message to a file (i.e. /etc/issue or /etc/motd) and showing it just before password prompt with pam_issue or after successful login with pam_motd. We can add the pam_issue call just before the auth call to the password-auth substack…

      auth         optional       pam_issue.so issue=/etc/issue

… or the pam_motd call just after the postlogin call …

      session      optional       pam_motd.so motd=/etc/motd

The changes in the login file will only work for textual logins (no GNOME, KDE or the like). We need to make sure that the same security level is applied to graphical logins by adding the pam_access call to the gdm-password file (and to all gdm-* files to be safe):

# cat /etc/pam.d/gdm-­password
auth [success=done ignore=ignore default=bad] pam_selinux_permit.so
auth substack password-­auth
auth optional pam_gnome_keyring.so
auth include postlogin

account required pam_nologin.so
account required pam_access.so
account include password­-auth

password substack password-­auth
password optional pam_gnome_keyring.so use_authtok

session required pam_selinux.so close
session required pam_loginuid.so
session optional pam_console.so
­session optional pam_ck_connector.so
session required pam_selinux.so open
session optional pam_keyinit.so force revoke
session required pam_namespace.so
session include password-­auth
session optional pam_gnome_keyring.so auto_start
session include postlogin

If we setup access.conf according to our security requirements and enforce it by calling the pam_access module in all the login & gdm-* files, the system should be pretty safe from unauthorised local logins. But unless we add the same change to the /etc/pam.d/sshd file, a user banned in access.conf will still be able to login remotely with the secure shell:

# cat /etc/pam.d/sshd
auth required pam_sepermit.so
auth substack password­auth
auth include postlogin

# Used with polkit to reauthorize users in remote session
account required pam_nologin.so
account required pam_access.so
account include password­auth

password include password­auth

# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so

# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open env_params
session optional pam_keyinit.so force revoke
session include password­auth
session include postlogin

With sshd secured, we should next look at su to prevent su logins to banned accounts. Not only should we add the pam_access call but we should also uncomment the call that forces user membership to the wheel group to be able to use su. If we need non-privileged users to switch to other accounts, it is far safer to do that through changes in /etc/ssh/sshd_config.

# cat /etc/pam.d/su
auth sufficient pam_rootok.so

# Uncomment the following line to implicitly trust users in the “wheel” group.
#auth sufficient pam_wheel.so trust use_uid

# Uncomment the following line to require a user to be in the “wheel” group.
auth required pam_wheel.so use_uid
auth substack system­auth
auth include postlogin

account sufficient pam_succeed_if.so uid = 0 use_uid quiet
account required pam_access.so
account include system­auth

password include system­auth

session include system­auth
session include postlogin
session optional pam_xauth.so

We can also make sure that the pam_access call is in the /etc/pam.d/sudo file but when it comes to sudo security it is far better enforce it through /etc/sudoers.

<< Access Control Lists