Ubuntu · Accounts & authentication

Enforce password quality with pam_pwquality

When it fails: Users can set short or trivial passwords. Weak local passwords are guessed quickly, online or from a stolen shadow file.

CB-UBU-AUTH-010mediumactiveautomatedautomated fixprofile core

As released in v0.1.1 (beta). Current state.

Description

pam_pwquality (package libpam-pwquality) checks every new password when it is set with passwd. Its settings live in /etc/security/pwquality.conf and /etc/security/pwquality.conf.d/:

  • minlen: minimum length;
  • minclass: minimum number of character classes (lower, upper, digit, other);
  • difok: characters that must differ from the old password;
  • maxrepeat / maxsequence: longest run of the same or consecutive characters;
  • dictcheck: reject words from the cracklib dictionary;
  • enforce_for_root: apply the rules when root sets a password too;
  • enforcing: reject a weak password instead of only printing a warning (default 1).

Without libpam-pwquality, Ubuntu only applies the weak built-in obscure check of pam_unix. Installing the package enables the module in /etc/pam.d/common-password through pam-auth-update.

Rationale

Length is what makes a password resist guessing. The other rules reject the patterns attackers try first (repeated characters, sequences, dictionary words, a one-character change).

Impact

Users must pick passwords that follow the rules when they next change them. Existing passwords are not checked.

Setting

Location/etc/security/pwquality.conf.d/60-cirsec.conf
DefaultModule not installed
Recommendedminlen >= {{ minlen }}, minclass >= {{ minclass }}, difok >= 2, maxrepeat <= 3, maxsequence <= 3, dictcheck, enforce_for_root, enforcing not 0

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
minleninteger. Minimum password length.14141614168 to 64
minclassinteger. Minimum number of character classes.333330 to 4

The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.

Audit

  1. dpkg -s libpam-pwquality must show the package installed.
  2. /etc/pam.d/common-password must contain a pam_pwquality.so line.
  3. The last value of each setting in /etc/security/pwquality.conf then /etc/security/pwquality.conf.d/*.conf must be: minlen >= {{ minlen }}, minclass >= {{ minclass }}, difok >= 2, maxrepeat and maxsequence between 1 and 3, dictcheck not 0, and enforce_for_root present.
  4. Neither the last enforcing value of these files nor the pam_pwquality.so line is enforcing=0.

Automated: every check must pass. Scanners report each check by its id.

package

name: libpam-pwquality
state: installed

module

pam_pwquality is in the password stack.

path: /etc/pam.d/common-password
pattern: ^\s*password\s+.*pam_pwquality\.so

settings

Effective pwquality settings.

script: |
  files="/etc/security/pwquality.conf $(ls /etc/security/pwquality.conf.d/*.conf 2>/dev/null)"
  get() { cat $files 2>/dev/null | sed -n "s/^[[:space:]]*$1[[:space:]]*=[[:space:]]*\([-0-9]*\).*/\1/p" | tail -n 1; }
  minlen=$(get minlen) minclass=$(get minclass) difok=$(get difok)
  maxrepeat=$(get maxrepeat) maxsequence=$(get maxsequence) dictcheck=$(get dictcheck)
  root=$(cat $files 2>/dev/null | grep -c '^[[:space:]]*enforce_for_root')
  echo "minlen=${minlen:-9} minclass=${minclass:-0} difok=${difok:-1} maxrepeat=${maxrepeat:-0} maxsequence=${maxsequence:-0} dictcheck=${dictcheck:-1} enforce_for_root=$root"
  [ "${minlen:-9}" -ge "$CB_MINLEN" ] && [ "${minclass:-0}" -ge "$CB_MINCLASS" ] && [ "${difok:-1}" -ge 2 ] \
    && [ "${maxrepeat:-0}" -ge 1 ] && [ "${maxrepeat:-0}" -le 3 ] \
    && [ "${maxsequence:-0}" -ge 1 ] && [ "${maxsequence:-0}" -le 3 ] \
    && [ "${dictcheck:-1}" != 0 ] && [ "$root" -gt 0 ]

enforcing

pwquality rejects weak passwords instead of only warning.

script: |
  files="/etc/security/pwquality.conf $(ls /etc/security/pwquality.conf.d/*.conf 2>/dev/null)"
  e=$(cat $files 2>/dev/null | sed -n 's/^[[:space:]]*enforcing[[:space:]]*=[[:space:]]*\([0-9]*\).*/\1/p' | tail -n 1)
  l=$(grep -E '^[[:space:]]*password[[:space:]].*pam_pwquality\.so.*enforcing=0' /etc/pam.d/common-password)
  echo "enforcing=${e:-1 (default)}${l:+; module line: $l}"
  [ "${e:-1}" != 0 ] && [ -z "$l" ]

Remediation

  1. Run apt install libpam-pwquality.
  2. Create /etc/security/pwquality.conf.d/60-cirsec.conf:
    minlen = {{ minlen }}
    minclass = {{ minclass }}
    difok = 2
    maxrepeat = 3
    maxsequence = 3
    dictcheck = 1
    enforce_for_root
    enforcing = 1
    
  3. Remove any enforcing=0 option from the pam_pwquality.so line.
Restartnone
Disruptionlow

Rollback

Delete /etc/security/pwquality.conf.d/60-cirsec.conf. Remove the package to drop the module.

package

Generated from check package: the engine writes the fix for every output, it sets the expected value and the check runs again after.

settings

f=/etc/security/pwquality.conf.d/60-cirsec.conf
cb_backup "$f"
mkdir -p /etc/security/pwquality.conf.d
printf '%s\n' '# Written by CIRSEC Baselines CB-UBU-AUTH-010' "minlen = $CB_MINLEN" "minclass = $CB_MINCLASS" \
  'difok = 2' 'maxrepeat = 3' 'maxsequence = 3' 'dictcheck = 1' 'enforce_for_root' 'enforcing = 1' >"$f"
chmod 644 "$f"

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read.
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read.
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read.
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read.
Introduced inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr