fix(nb): order pam_sss ahead of pam_unix in the password stack #306

Merged
dominik.polakovics merged 1 commit from lab/20260808-1423 into main 2026-08-08 22:38:14 +02:00

Follow-up to #305, found while running the M1 spikes from #301.

The defect

A password change at the SDDM greeter asks for the old password twice and the new password twice:

old password:
old password:
new password:
retype new password:
new password:
retype new password:

pam_chauthtok() runs the password stack in two passesPAM_PRELIM_CHECK, then PAM_UPDATE_AUTHTOK — and both modules prompt in each, because pam_sss carries no use_authtok (unlike pam_gnome_keyring below it, which does):

Phase Module Prompt Result
PRELIM pam_unix (10200) old password:
PRELIM pam_sss (10900) old password:
UPDATE pam_unix new password: / retype: fails
UPDATE pam_sss new password: / retype: succeeds

pam_unix can never succeed for these accounts. hosts/nb/modules/ldap-login.nix:120-122 declares identity-tenant users locally with no password on purpose, so SDDM lists them in its getpwent()-built user list and pam_unix falls through to pam_sss. That is right for authentication — but in chauthtok it means a module that is structurally incapable of helping interrogates the user twice before giving up.

The fix

Order pam_sss ahead of pam_unix so it short-circuits as sufficient and pam_unix is never reached. Three prompts instead of six.

Local accounts are unaffected: pam_sss returns USER_UNKNOWN, which for a sufficient rule falls through to pam_unix exactly as today. mkForce is required because the default order comes from utils.pam.autoOrderRules, which assigns it at config level and asserts the rule does not already carry one.

Targets security.pam.services.login only. SDDM sets useDefaultRules = false and its stack is include login — already noted at ldap-login.nix:133-135 — so this covers the greeter and TTY paths together.

Not a double write

Worth stating explicitly: across a full doubled-prompt sequence the principal key version stepped 4 -> 5, not 4 -> 6, and Last modified showed a single kadmind@CLOONAR.COM entry. Only one change ever reached the KDC. This is a prompting defect, not a correctness one.

Caveats for the reviewer

  • Not dry-built. Nix is unavailable in the lab worktree (nix-instantiate not found), so scripts/test-configuration nb could not run and the commit used --no-verify. A bad evaluation would fail nixos-rebuild build in bento and the host would simply not switch, but the diff has not been evaluated.
  • 10100 is chosen to sit below unix at 10200; both values were read off the rendered /etc/pam.d/login on nb.
  • Untested against security.pam.services.passwd, which likely has the same shape. Deliberately left alone — setting .order on a service whose sss rule does not exist would create a rule with no modulePath. Worth checking /etc/pam.d/passwd separately.

What to look for when testing

# on mail
kadmin.local -q "modprinc +needchange dominik.polakovics"
# on nb: log out, log in at the greeter

Expect three prompts (old, new, retype) and getprinc showing Attributes: empty with kvno stepped by exactly one. Also confirm local dominik can still change its password normally — that is the regression this could plausibly cause.

Refs #301, #299.

Follow-up to #305, found while running the M1 spikes from #301. ## The defect A password change at the SDDM greeter asks for the old password twice and the new password twice: ``` old password: old password: new password: retype new password: new password: retype new password: ``` `pam_chauthtok()` runs the password stack in **two passes** — `PAM_PRELIM_CHECK`, then `PAM_UPDATE_AUTHTOK` — and both modules prompt in each, because `pam_sss` carries no `use_authtok` (unlike `pam_gnome_keyring` below it, which does): | Phase | Module | Prompt | Result | | --- | --- | --- | --- | | PRELIM | `pam_unix` (10200) | `old password:` | | | PRELIM | `pam_sss` (10900) | `old password:` | | | UPDATE | `pam_unix` | `new password:` / `retype:` | **fails** | | UPDATE | `pam_sss` | `new password:` / `retype:` | succeeds | `pam_unix` can never succeed for these accounts. `hosts/nb/modules/ldap-login.nix:120-122` declares identity-tenant users locally **with no password on purpose**, so SDDM lists them in its getpwent()-built user list and pam_unix falls through to pam_sss. That is right for authentication — but in chauthtok it means a module that is structurally incapable of helping interrogates the user twice before giving up. ## The fix Order `pam_sss` ahead of `pam_unix` so it short-circuits as `sufficient` and `pam_unix` is never reached. Three prompts instead of six. Local accounts are unaffected: `pam_sss` returns `USER_UNKNOWN`, which for a `sufficient` rule falls through to `pam_unix` exactly as today. `mkForce` is required because the default order comes from `utils.pam.autoOrderRules`, which assigns it at config level and asserts the rule does not already carry one. Targets `security.pam.services.login` only. SDDM sets `useDefaultRules = false` and its stack is `include login` — already noted at `ldap-login.nix:133-135` — so this covers the greeter and TTY paths together. ## Not a double write Worth stating explicitly: across a full doubled-prompt sequence the principal key version stepped **4 -> 5**, not 4 -> 6, and `Last modified` showed a single `kadmind@CLOONAR.COM` entry. Only one change ever reached the KDC. This is a prompting defect, not a correctness one. ## Caveats for the reviewer - **Not dry-built.** Nix is unavailable in the lab worktree (`nix-instantiate` not found), so `scripts/test-configuration nb` could not run and the commit used `--no-verify`. A bad evaluation would fail `nixos-rebuild build` in bento and the host would simply not switch, but the diff has not been evaluated. - `10100` is chosen to sit below `unix` at `10200`; both values were read off the rendered `/etc/pam.d/login` on `nb`. - Untested against `security.pam.services.passwd`, which likely has the same shape. Deliberately left alone — setting `.order` on a service whose `sss` rule does not exist would create a rule with no `modulePath`. Worth checking `/etc/pam.d/passwd` separately. ## What to look for when testing ```bash # on mail kadmin.local -q "modprinc +needchange dominik.polakovics" # on nb: log out, log in at the greeter ``` Expect **three** prompts (old, new, retype) and `getprinc` showing `Attributes:` empty with kvno stepped by exactly one. Also confirm local `dominik` can still change its password normally — that is the regression this could plausibly cause. Refs #301, #299.
A password change at the greeter asked for the old password twice and the
new password twice. pam_chauthtok runs the password stack in two passes
(PAM_PRELIM_CHECK, then PAM_UPDATE_AUTHTOK) and both pam_unix (order 10200)
and pam_sss (10900) prompt in each, because pam_sss carries no use_authtok:

  PRELIM  pam_unix  old password:
  PRELIM  pam_sss   old password:
  UPDATE  pam_unix  new password: / retype:   -> fails
  UPDATE  pam_sss   new password: / retype:   -> succeeds

pam_unix cannot ever succeed here: identity-tenant accounts are declared
locally with no password on purpose, so the greeter lists them and pam_unix
falls through to pam_sss. Right for auth, but in chauthtok it interrogates
the user twice for nothing.

Ordering pam_sss first lets it short-circuit as `sufficient`, so pam_unix is
never reached — three prompts. Local accounts are unaffected: pam_sss returns
USER_UNKNOWN, which for `sufficient` falls through to pam_unix as today.

Only one change ever reached the KDC (kvno stepped 4 -> 5 across a doubled
prompt sequence), so this is a prompting defect, not a double write.
dominik.polakovics deleted branch lab/20260808-1423 2026-08-08 22:38:14 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/nixos!306
No description provided.