1. Overview
Password management is an important security measure for any Linux system. Without a secure password, unauthorized people can access the system, leaving it vulnerable to malicious attacks. It’s therefore essential to define password policies that force the user to consider a complex one that is difficult to guess or crack.
In this tutorial, we’ll learn how to set up a firm password policy. We’ll focus mainly on the PAM (Pluggable Authentication Modules) and its related configuration files.
2. Setting PAM Modules
The Linux Pluggable Authentication Modules (PAM) library suite provides a Linux system administrator with techniques for authenticating users. It facilitates the swapping of authentication methods between secure applications flexibly and centrally by using configuration files rather than rewriting application code.
PAM combines multiple low-level authentication modules into a high-level API that provides dynamic authentication support for applications.
This tool distributes authentication functions across four different management groups, including a password management group, as well as the account, authentication, and session groups. Depending on the desired policy, these groups support a variety of PAM modules.
The configuration file for PAM is /etc/pam.conf and it includes the general behavior of the PAM suites. The directory /etc/pam.d contains a dedicated configuration file for each application that uses PAM.
The common syntax of those files contains a list of rules formatted as follows:
group-type control-flag module module-arguments
For each line there is :
- group-type: the authentication management group type (auth, account, password, and session)
- control-flag: the PAM-API behavior if the module’s authentication task fails (requisite, required, sufficient, and optional)
- module: the PAM module in question
- module-flag: the options defining the module behavior
To manage password complexity there are /etc/pam.d/common-password and /etc/pam.d/system-auth files used respectively for Debian and Red Hat-based systems.
By default, here’s how /etc/pam.d/system-auth looks like:
auth required pam_env.so
auth sufficient pam_fprintd.so
auth required pam_deny.so
<...>
account required pam_unix.so
account sufficient pam_localuser.so
<...>
password requisite pam_cracklib.so try_first_pass retry=3 type=
password sufficient pam_unix.so md5 shadow nullok try_first_pass use_authtok
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
<...>
Many PAM modules are involved when managing password policies, such as pam_cracklib, pam_unix, pam_tally2, and pam_pwhistory.
3. Setting Password Policy
According to the NIST password guidelines, password strength is primarily determined by password length. For this reason, many organizations are imposing that passwords are a complex mix of at least 12 to 16 characters. Additional settings, such as managing the password history, password lock and unlock, and aging data, are also frequent.
The tests in the following parts are executed in an Ubuntu 16 operating system. Therefore, we’ll use for the most part the file /etc/pam.d/common-password.
3.1. Password Quality
Generally speaking, the quality of a password is measured by its minimum length and the use of different sorts of characters (upper, lower, digit, and other). The module pam_cracklib supports both these laters as well as additional tests.
For Debian-based distribution, we use the command line tool apt to install the module pam_cracklib as follows:
$ sudo apt-get install libpam-cracklib
By default, pam_cracklib performs basic checks. It starts with checking if it’s a dictionary word. If it’s not the case, the process moves along to checking for palindromes, rotated passwords, reversed characters, or the same password with only a case change.
Let’s see the most common options to use with this module:
- minlen=n: defines the minimum password length
- dcredit=n: impose at most n digits when positive, and at least n digits when negative
- ucredit=n: impose at most n uppercase letters when positive, and at least n uppercase letters when negative
- lcredit=n: impose at most n lowercase letters when positive, and at least n lowercase letters when negative
- ocredit=n: impose at most n special characters when positive, and at least n special characters when negative
In /etc/pam.d/common-password, we just append a line to the password management group. The example below allows forcing a password that contains at least: nine characters, one upper case letter, two digits, and one other character:
password required pam_cracklib.so retry=3 minlen=9 ucredit=-1 ocredit=-1 dcredit=-2
Here, the option retry=3 allows the user to enter the password at least three times before having an error. The default value of retry is 1.
The module pam_cracklib offers extra options to perform additional checks, including:
- difok=n: defines the number of character changes in the new password, the default value is 5
- minclass=n: imposes at least n characters from each of the four character classes
- maxrepeat=n: rejects passwords that contain more than n similar consecutive characters; n=0 by default
- maxsequence=n: accepts no more than n monotonic character sequences, such as “1234” or “abcd“; by default n=0 meaning that the option is disabled
- reject_username: rejects a password that contains the username in straight or reversed form
3.2. Password History
Many users tend to have a bunch of passwords to alternate between them too frequently. It’s possible in Linux systems to prevent reusing old passwords by saving them in a dedicated location (/etc/security/opasswd).
By using the pam_unix module, we just append the following line in /etc/pam.d/common-password:
password required pam_unix.so sha512 shadow remember=40
The option remember=40 saves for each user the last 40 passwords to force password changes.
There’s also pam_pwhistory, a dedicated PAM module to replace the password history functionality implemented by pam_unix. It works with the same option (remember) and syntax as pam_unix. It supports also other functions such as customizing the location where to archive passwords (file=/path/filename) and prompting the user n times before returning an error(retry=n).
3.3. Lockout on Failure
Due to brute-force attacks, it’s imperative to implement a “lockout on failure” functionality. This is implemented by the pam_tally2 module in Linux systems. This module keeps track of access attempts, reset the count on success, and block access after a given number of failed attempts.
On top of /etc/pam.d/common-password, with the authentication management group, we add the following line:
auth required pam_tally2.so deny=3 unlock_time=3600 even_deny_root
This setting locks accounts after three failed attempts (deny=3) and automatically unlocks them an hour later (unlock_time=3600 in seconds). The admin can manually remove the lock if unlock_time is not specified.
The option even_deny_root locks also the root account.
3.4. Password Aging
The file /etc/login.defs contains the default settings for newly created accounts including those related to passwords. In this file, we can set parameters like minimum password age, expiration date (maximum age), and warning period:
# Password aging controls:
[...]
PASS_MAX_DAYS 30
PASS_MIN_DAYS 0
PASS_MIN_LEN 5
PASS_WARN_AGE 5
Managing this type of data is also supported by commands like chage to modify aging details and passwd to define/update the password itself.
4. Conclusion
In this article, we learned firstly the basic syntax of the PAM authentication tool. Then we saw different approaches using the PAM module to enforce a complex password in Linux systems.
To conclude, password policies are one of the pillars of system security. Considering a strong one is inevitable to prevent unauthorized access.