{
  "id": "CB-UBU-AUTH-010",
  "standard": "ubuntu",
  "version": "0.1.2",
  "url": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-AUTH-010",
  "json": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-AUTH-010.json",
  "yaml": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-AUTH-010.yml",
  "profiles_effective": [
    "core",
    "server",
    "server-hardened",
    "desktop",
    "desktop-hardened"
  ],
  "license": "Text CC BY 4.0, code Apache-2.0",
  "attribution": "CIRSEC Baselines, https://baselines.cirsec.org",
  "control": {
    "id": "CB-UBU-AUTH-010",
    "title": "Enforce password quality with pam_pwquality",
    "finding": {
      "title": "Users can set short or trivial passwords",
      "description": "Weak local passwords are guessed quickly, online or from a stolen shadow file."
    },
    "status": "active",
    "profiles": [
      "core"
    ],
    "risk": {
      "severity": "medium"
    },
    "assessment": "automated",
    "tags": [
      "pam",
      "password"
    ],
    "description": "`pam_pwquality` (package `libpam-pwquality`) checks every new password when it is set with\n`passwd`. Its settings live in `/etc/security/pwquality.conf` and `/etc/security/pwquality.conf.d/`:\n\n- `minlen`: minimum length;\n- `minclass`: minimum number of character classes (lower, upper, digit, other);\n- `difok`: characters that must differ from the old password;\n- `maxrepeat` / `maxsequence`: longest run of the same or consecutive characters;\n- `dictcheck`: reject words from the cracklib dictionary;\n- `enforce_for_root`: apply the rules when root sets a password too;\n- `enforcing`: reject a weak password instead of only printing a warning (default 1).\n",
    "rationale": "Length is what makes a password resist guessing. The other rules reject the patterns attackers\ntry first (repeated characters, sequences, dictionary words, a one-character change).\n",
    "impact": "Users must pick passwords that follow the rules when they next change them. Existing passwords\nare not checked.\n",
    "notes": "Without `libpam-pwquality`, Ubuntu only applies the weak built-in `obscure` check of `pam_unix`.\nInstalling the package enables the module in `/etc/pam.d/common-password` through `pam-auth-update`.\n",
    "setting": {
      "location": "/etc/security/pwquality.conf.d/60-cirsec.conf",
      "default_value": "Module not installed",
      "recommended_value": "minlen >= {{ minlen }}, minclass >= {{ minclass }}, difok >= 2, maxrepeat <= 3, maxsequence <= 3, dictcheck, enforce_for_root, enforcing not 0"
    },
    "parameters": [
      {
        "name": "minlen",
        "type": "integer",
        "description": "Minimum password length.",
        "default": 14,
        "profile_defaults": {
          "server-hardened": 16,
          "desktop-hardened": 16
        },
        "minimum": 8,
        "maximum": 64
      },
      {
        "name": "minclass",
        "type": "integer",
        "description": "Minimum number of character classes.",
        "default": 3,
        "minimum": 0,
        "maximum": 4
      }
    ],
    "audit": {
      "manual": "1. `dpkg -s libpam-pwquality` must show the package installed.\n2. `/etc/pam.d/common-password` must contain a `pam_pwquality.so` line.\n3. The last value of each setting in `/etc/security/pwquality.conf` then\n   `/etc/security/pwquality.conf.d/*.conf` must be: `minlen` >= {{ minlen }},\n   `minclass` >= {{ minclass }}, `difok` >= 2, `maxrepeat` and `maxsequence` between 1 and 3,\n   `dictcheck` not 0, and `enforce_for_root` present.\n4. Neither the last `enforcing` value of these files nor the `pam_pwquality.so` line is\n   `enforcing=0`.\n",
      "checks": [
        {
          "id": "package",
          "engine": "package",
          "spec": {
            "name": "libpam-pwquality",
            "state": "installed"
          }
        },
        {
          "id": "module",
          "engine": "file-content",
          "description": "pam_pwquality is in the password stack.",
          "spec": {
            "path": "/etc/pam.d/common-password",
            "pattern": "^\\s*password\\s+.*pam_pwquality\\.so"
          }
        },
        {
          "id": "settings",
          "engine": "shell",
          "description": "Effective pwquality settings.",
          "spec": {
            "script": "files=\"/etc/security/pwquality.conf $(ls /etc/security/pwquality.conf.d/*.conf 2>/dev/null)\"\nget() { cat $files 2>/dev/null | sed -n \"s/^[[:space:]]*$1[[:space:]]*=[[:space:]]*\\([-0-9]*\\).*/\\1/p\" | tail -n 1; }\nminlen=$(get minlen) minclass=$(get minclass) difok=$(get difok)\nmaxrepeat=$(get maxrepeat) maxsequence=$(get maxsequence) dictcheck=$(get dictcheck)\nroot=$(cat $files 2>/dev/null | grep -c '^[[:space:]]*enforce_for_root')\necho \"minlen=${minlen:-9} minclass=${minclass:-0} difok=${difok:-1} maxrepeat=${maxrepeat:-0} maxsequence=${maxsequence:-0} dictcheck=${dictcheck:-1} enforce_for_root=$root\"\n[ \"${minlen:-9}\" -ge \"$CB_MINLEN\" ] && [ \"${minclass:-0}\" -ge \"$CB_MINCLASS\" ] && [ \"${difok:-1}\" -ge 2 ] \\\n  && [ \"${maxrepeat:-0}\" -ge 1 ] && [ \"${maxrepeat:-0}\" -le 3 ] \\\n  && [ \"${maxsequence:-0}\" -ge 1 ] && [ \"${maxsequence:-0}\" -le 3 ] \\\n  && [ \"${dictcheck:-1}\" != 0 ] && [ \"$root\" -gt 0 ]\n"
          }
        },
        {
          "id": "enforcing",
          "engine": "shell",
          "description": "pwquality rejects weak passwords instead of only warning.",
          "spec": {
            "script": "files=\"/etc/security/pwquality.conf $(ls /etc/security/pwquality.conf.d/*.conf 2>/dev/null)\"\ne=$(cat $files 2>/dev/null | sed -n 's/^[[:space:]]*enforcing[[:space:]]*=[[:space:]]*\\([0-9]*\\).*/\\1/p' | tail -n 1)\nl=$(grep -E '^[[:space:]]*password[[:space:]].*pam_pwquality\\.so.*enforcing=0' /etc/pam.d/common-password)\necho \"enforcing=${e:-1 (default)}${l:+; module line: $l}\"\n[ \"${e:-1}\" != 0 ] && [ -z \"$l\" ]\n"
          }
        }
      ]
    },
    "remediation": {
      "manual": "1. Run `apt install libpam-pwquality`.\n2. Create `/etc/security/pwquality.conf.d/60-cirsec.conf`:\n   ```\n   minlen = {{ minlen }}\n   minclass = {{ minclass }}\n   difok = 2\n   maxrepeat = 3\n   maxsequence = 3\n   dictcheck = 1\n   enforce_for_root\n   enforcing = 1\n   ```\n3. Remove any `enforcing=0` option from the `pam_pwquality.so` line.\n",
      "effects": {
        "restart": "none",
        "disruption": "low",
        "lockout_risk": false
      },
      "rollback": "Delete `/etc/security/pwquality.conf.d/60-cirsec.conf`. Remove the package to drop the module.\n",
      "scripts": [
        {
          "id": "package",
          "from_check": "package"
        },
        {
          "id": "settings",
          "engine": "shell",
          "content": "f=/etc/security/pwquality.conf.d/60-cirsec.conf\ncb_backup \"$f\"\nmkdir -p /etc/security/pwquality.conf.d\nprintf '%s\\n' '# Written by CIRSEC Baselines CB-UBU-AUTH-010' \"minlen = $CB_MINLEN\" \"minclass = $CB_MINCLASS\" \\\n  'difok = 2' 'maxrepeat = 3' 'maxsequence = 3' 'dictcheck = 1' 'enforce_for_root' 'enforcing = 1' >\"$f\"\nchmod 644 \"$f\"\n"
        }
      ]
    },
    "references": [
      {
        "title": "pam_pwquality(8), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man8/pam_pwquality.8.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "pwquality.conf(5), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man5/pwquality.conf.5.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "NIST SP 800-63B - Digital Identity Guidelines, Authentication",
        "url": "https://pages.nist.gov/800-63-4/sp800-63b.html",
        "publisher": "NIST",
        "kind": "standard",
        "accessed": "2026-10-19"
      },
      {
        "title": "ANSSI-BP-028 v2.0 - Recommandations de configuration d'un système GNU/Linux (R31)",
        "url": "https://messervices.cyber.gouv.fr/documents-guides/fr_np_linux_configuration-v2.0.pdf",
        "publisher": "ANSSI",
        "kind": "guide",
        "accessed": "2026-10-19"
      }
    ],
    "mappings": [
      {
        "framework": "anssi-linux",
        "framework_version": "2.0",
        "id": "R31",
        "relation": "equivalent"
      },
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "IA-5(1)",
        "relation": "partial"
      }
    ],
    "threats": [
      {
        "framework": "mitre-attack",
        "id": "T1110"
      }
    ],
    "authors": [
      {
        "name": "Alice",
        "github": "Sn0wAlice"
      }
    ],
    "verified": [
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "20.04"
        ],
        "environment": "Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic",
        "notes": "Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "22.04"
        ],
        "environment": "Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic",
        "notes": "Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "24.04"
        ],
        "environment": "Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic",
        "notes": "Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "26.04"
        ],
        "environment": "Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic",
        "notes": "Weak and short passwords rejected, strong one accepted with pamtester; minlen 14 core, 16 server-hardened; enforcing checked; pwquality.conf.d read."
      }
    ],
    "introduced_in": "0.1.0",
    "created": "2026-10-19",
    "updated": "2026-10-19"
  }
}