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 autogenerated.
# 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 systemauth
auth include postlogin
account required pam_nologin.so
account include systemauth
password include systemauth
# 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 systemauth
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 passwordauth
auth include postlogin
# Used with polkit to reauthorize users in remote session
account required pam_nologin.so
account required pam_access.so
account include passwordauth
password include passwordauth
# 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 passwordauth
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 systemauth
auth include postlogin
account sufficient pam_succeed_if.so uid = 0 use_uid quiet
account required pam_access.so
account include systemauth
password include systemauth
session include systemauth
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.