Debian 13 ISM Word-Count Enforcement Component Definition

debian-13-pam-wordcount · version 1.0.0 · OSCAL 1.1.2

technically-tested unpublished

Not accredited. Assurance authority: none; self-asserted by the contributing author. Endorsement: none.

Theoretical component. It describes a configuration that satisfies the named controls when applied; it is not an assertion that any particular system is running it. An SSP leveraging this component must supply the implementation evidence for its own hosts.

Assurance state is 'technically-tested': the configuration was applied to a clean Debian 13 install and verified end to end. It has NOT been independently assessed, and is not endorsed or accredited by the ACSC, NIST, or any assessment authority.

Documentcomponent-definition.json
sha256 ad9b64c03242c5fd9b2f74fe84c66dfaac8408471adcfa487bd002f4523e0515
Identityhttps://library.oscalops.net/component-definitions/debian-13-pam-wordcount/1.0.0/component-definition.json
SourceLocal development source — not publishable until pinned to a git commit
VersionsAll versions of debian-13-pam-wordcount

Debian 13 ISM word-count enforcement (pam_ism_wordcount)

Enforces the ism-1558 minimum word count for passwords that are a sequence of words: at least 4 words on non-classified, OFFICIAL: Sensitive and PROTECTED systems, 5 on SECRET and 6 on TOP SECRET. Single-token passwords are passed through untouched, which is what lets ism-1558 and ism-2080 hold together.

The default implementation is pam_ism_wordcount.so, a purpose-built PAM module that counts whitespace-delimited words in one read-only, in-process pass. A pam_exec(8) shell equivalent is shipped as a fallback for hosts that cannot deploy a compiled module.

Depends on debian-13-pwquality-base. The check reads the new password but never asks for it; pam_pwquality, earlier in the stack, obtains it. Without it pam-auth-update still gives pam_unix use_authtok, there is no token to use, and every password change fails: verified on a clean Debian 13 install. The apply script refuses to run without pam_pwquality in the stack.

Purpose. Enforce the ISM word-count requirement for passphrases on local Debian accounts without imposing a complexity requirement on single-token passwords.

Properties
os-distributiondebian
os-release13
os-codenametrixie
settingpam-auth-update:ism-wordcount-module
settingpam-auth-update:ism-wordcount
settingpam-module:pam_ism_wordcount.so
settingfile:/usr/local/sbin/ism-wordcount-check
settingfile:/etc/security/ism-wordcount.conf
enforcement-mechanismpam_ism_wordcount.so (in-process, default) or pam_exec.so running ism-wordcount-check (fallback)
dependency-postureNo packages beyond libpam-pwquality, libpwquality-tools and cracklib-runtime; pam_exec ships in libpam-modules. libpam-python and libpam-script were evaluated and rejected.
pam-stack-priority1020 (after pam_pwquality at 1024, ahead of pam_unix at 256)
additional-packages-requirednone at runtime; building the module needs libpam0g-dev and a C compiler on a build host

Australian Cyber Security Centre: Information Security Manual v2026.09.4

The ISM word-count requirement, as published in the ACSC ISM OSCAL catalogue release v2026.09.4.

ISM-1558 — Password strength

Partially implemented. This component enforces the word-count limb. Sequences, repeats and account-derived strings are rejected by debian-13-pam-pwquality; the provenance limbs are not enforced by any component.

ENFORCED, verified by behaviour through the PAM stack on all three applicability tiers: a password using a sequence of words must use at least 4 words on non-classified, OFFICIAL: Sensitive and PROTECTED systems, 5 on SECRET and 6 on TOP SECRET. This is implemented by pam_ism_wordcount.so, a purpose-built PAM module that counts whitespace-delimited words in a single read-only pass. It runs in-process, so the candidate never leaves the PAM process. A pam_exec(8) shell equivalent is shipped as a fallback for hosts that cannot deploy a compiled module; pam_exec ships in libpam-modules, so neither route requires an additional package.

Crucially, the check applies ONLY to passwords that are a sequence of words. A single-token password is not one, so it is passed through untouched. That is what allows this control and ism-2080 to hold simultaneously; see the remarks for why a character-class module cannot.

NOT ENFORCED: a sequence of categorised words, a real sentence in natural language, and song lyrics, quotes or other publicly available material all satisfy a word count while being exactly what this control prohibits. Verified: all three are still accepted. Determining whether words were randomly selected is not something any available module can do.

Residual risk Word count is enforced; randomness and provenance of the words are not. Natural-language sentences, lyrics and categorised word lists remain accepted.

Remarks

Why not pam_passwdqc: it can enforce a word count, but passwdqc requires each min value to be no larger than the preceding one, so 'disabled' can only ever apply to the weakest character-class tiers. It can therefore never express 'reject all non-passphrases regardless of class'. Tested: under min=disabled,disabled,15,15,15 with passphrase=4 a two-word candidate is correctly rejected, but a 15-character single-token password is ALSO rejected -- which breaches ism-2080, since ism-1558 governs only passwords using a sequence of words. passwdqc's min is additionally an effective length rather than a raw character count, so it cannot assert the literal 'minimum of 15 characters' that ism-0421 states.

The pam_exec approach keys off word structure instead, which is what the control actually describes, and leaves single-token passwords alone.

Implementation choice: a purpose-built C module is the default because it removes the only real exposure of the shell route, where expose_authtok passes the cleartext candidate across a pipe to a child process. The module reads PAM_AUTHTOK in-process and the candidate is never copied, logged, or passed to another process.

C rather than Rust: the check is a single read-only pass counting whitespace transitions, with no allocation, no copying and no index arithmetic, so the memory-safety hazards C is criticised for are absent rather than merely managed. A Rust module would still require #[no_mangle] extern "C" and an unsafe read of a raw *const c_char from pam_get_item, placing unsafe code at exactly the point of risk, while adding a rustc build dependency and a much larger artefact. The source is 91 lines and compiles clean under -Wall -Wextra -Werror; the resulting object is roughly 15 kB and links only libpam and libc.

Alternatives evaluated and rejected: libpam-python would also run in-process but depends on libpython3.13 and installs 11 packages including the full CPython standard library, linking a Python interpreter into sshd, login, sudo and cron; its Debian changelog records 'Fix CVE-2019-16729 again' in 2024, five years after the original. libpam-script uses the same external script model as pam_exec and so changes nothing.

The pam_exec fallback, where used, is hardened: pinned to type=password, ulimit -c 0 so a credential cannot reach a core file, umask 077, the shell read builtin rather than a command substitution so no second process holds the cleartext, and no branch that logs or passes the candidate as a command argument.

Residual mitigation: pwqgen(1) or pwmake(1) generate passphrases so users never select words themselves, which is the only practical answer to the provenance limbs.

Configuration and scripts

Files

ResourceFileSHA-256
ism-1558 word-count checkscripts/ism-wordcount-checkc9f4008474a815df91fddcfe707d809c0fff207d831ed27362205abdf526d646
pam-auth-update profile for the word-count checkconfig/pam-configs/ism-wordcountf9d3ae6b5ed756e558b3eb7a542a620f04fee77a3e5020e6906e921c3fedb247
pam_ism_wordcount sourcesrc/pam_ism_wordcount.c9b90684ccdbd5fdedb3f2940d65b26e7631e7706e4384696057d11edb0ed5b92
Build file for the word-count modulesrc/Makefile223b10fddc67b9a242cdd6f18f3f84e55bd28b1589c72d86da0d261cea9aacc2
pam-auth-update profile for the in-process moduleconfig/pam-configs/ism-wordcount-module75c74b0466a39f6a66694c2dca9e11225bb02b0829919017094b05e7d6645e40
Apply scriptscripts/apply-wordcount.shf5dc40e4a8016a887776246a4437a1d7d920065013a3e1bbd1aa7726a8d0fef8
Verification scriptscripts/verify-wordcount.sh6eab210aa9c8edf77481d35e108cd7b165e0a8e7b0ae2b44d630840f34b1d84f

External references