Password Quality and Hashing¶
Linux checks a new password's strength with the pam_pwquality module, configured in /etc/security/pwquality.conf, and hashes it with whatever algorithm the pam_unix line in /etc/pam.d/common-password names. Both need to be right, and an image commonly has the module missing entirely.
Install the module¶
dpkg -l libpam-pwquality | grep ^ii || sudo apt install -y libpam-pwquality cracklib-runtime
grep pam_pwquality /etc/pam.d/common-password
Installing the package (CIS 5.2.1.3; cracklib-runtime supplies the dictionary the dictcheck setting uses, Debian CIS 5.3.1.4) adds a pam_pwquality.so line to common-password automatically through pam-auth-update (5.2.2.3). If the grep prints nothing after the install, the profile was disabled: sudo pam-auth-update --enable pwquality. If that says there's no such profile, create it and enable it:
sudo tee /usr/share/pam-configs/pwquality > /dev/null <<'EOF'
Name: Pwquality password strength checking
Default: yes
Priority: 1024
Conflicts: cracklib
Password-Type: Primary
Password:
requisite pam_pwquality.so retry=3
EOF
sudo pam-auth-update --enable pwquality
On Mint and Debian, the /etc/pam.d/common-* files are generated from the profiles in /usr/share/pam-configs/ by pam-auth-update. A line you add to common-password by hand works until something runs pam-auth-update again (a package upgrade can), which is why the profile is the durable way.
The quality settings¶
/etc/security/pwquality.conf. Every line ships commented out; uncomment and set. The format is name = value with spaces around the equals sign.
| Setting | Value | Meaning |
|---|---|---|
minlen |
8 to 16 (use 14; CIS 5.2.3.2.2 asks for 14 or more, STIG UBTU-22-611035 for 15) | Minimum length. |
difok |
2 or more (CIS 5.2.3.2.1); 8 for a STIG baseline (UBTU-22-611040) | Characters that must differ from the old password |
dcredit |
-1 | At least one digit (negative numbers mean "required", positive mean "bonus") |
ucredit |
-1 | At least one uppercase letter |
lcredit |
-1 | At least one lowercase letter |
ocredit |
-1 | At least one symbol |
minclass |
3 or 4 | Alternative to the four credit lines: how many character classes must appear |
maxrepeat |
3 (CIS 5.2.3.2.4) | No more than 3 of the same character in a row |
maxsequence |
3 (CIS 5.2.3.2.5) | No abcd or 1234 runs longer than 3 |
dictcheck |
1 (CIS 5.2.3.2.6) | Reject dictionary words |
enforcing |
1, or absent (never enforcing = 0; CIS 5.2.3.2.7, STIG UBTU-22-611045) |
0 would turn every check into a warning |
The four credit lines and dictcheck are STIG CAT II items too (UBTU-22-611010 to 611030).
| usercheck | 1 | Reject passwords containing the username |
| enforce_for_root | (present; CIS 5.2.3.2.8) | Root's own password changes obey the rules too |
Complexity through minclass or the credit lines is CIS 5.2.3.2.3 (Level 2); the rest are Level 1.
sudo nano /etc/security/pwquality.conf
The file ships with every setting commented out and explained, which makes it a good one to read top to bottom once. For each row of the table, find the line (Ctrl+W), delete the #, and set the value:
minlen = 14
dcredit = -1
The last occurrence of a setting wins, so don't leave a second uncommented copy further down.
The hashing algorithm¶
grep pam_unix.so /etc/pam.d/common-password
The line should include yescrypt (the default on Mint 21 and Debian 12) or sha512 (CIS 5.2.3.4.3; the STIG accepts only sha512 because it's the FIPS-approved one, UBTU-22-611070), and use_authtok (5.2.3.4.4) so it hashes the password that pwquality and pwhistory already checked rather than asking again. It must not include nullok (5.2.3.4.1) or remember= (5.2.3.4.2; history belongs on the pwhistory line, next page). If it says md5, sha256, or has no algorithm, an attacker who copies /etc/shadow cracks it in minutes. Edit the word in place:
sudo sed -i '/pam_unix.so/ s/\b\(md5\|sha256\|des\)\b/yescrypt/' /etc/pam.d/common-password
Passwords already hashed with the weak algorithm stay weak until they're changed; the Passwords and Shells page finds them.
Verify¶
grep pam_pwquality /etc/pam.d/common-password
grep -E '^(minlen|dcredit|ucredit|lcredit|ocredit|minclass)' /etc/security/pwquality.conf
grep pam_unix.so /etc/pam.d/common-password
passwd # try setting your own password to "password1" and read the refusal
Example¶
grep pam_pwquality /etc/pam.d/common-password prints nothing and dpkg -l libpam-pwquality shows it isn't installed. Install it, then set minlen = 14 and the four credit lines. grep pam_unix.so /etc/pam.d/common-password ends in md5; change it to yescrypt. Now sudo awk -F: '$2 ~ /^\$1\$/' /etc/shadow lists three users with MD5 hashes; reset those three passwords.
Try it¶
- Set
minlen = 14and try to give a test user a 10-character password. Read the message. - Set
dictcheck = 1and trypassword123.
Build it¶
A pwquality.sh that uncomments and sets each row of the table with sed, then prints the uncommented lines. Test it on a fresh copy of the file first.