feat(fingerprint): password OR fingerprint in parallel at one prompt
Some checks failed
Check / eval (push) Failing after 2m45s

Bernardo promoted the PROPOSED item live: with fingerprint PAM on,
sudo/login should accept whichever factor comes first instead of
pam_fprintd's wait-for-the-reader-then-password. Stock PAM cannot
express parallel factors (linux-pam#301), so this packages
pam-fprint-grosshack v0.3.0 (pkgs/, pinned from GitLab — the
field-standard fprintd fork), source-reviewed before packaging: every
failure path (no reader, no prints, fprintd absent/hung, timeout,
password typed) returns PAM_AUTHINFO_UNAVAIL and falls through; a
typed password is only ferried via PAM_AUTHTOK to the stock
`auth sufficient pam_unix.so … try_first_pass` rule — the module never
validates passwords itself, so it cannot lock out password login.

New option nomarchy.hardware.fingerprint.parallel, default TRUE (the
better UX is what opting into fingerprint PAM buys; false = stock
sequential). Wiring swaps the modulePath of stock fprintd's rule slot
(mkForce) so the sufficient-before-pam_unix ordering is inherited, not
recomputed. README + downstream template rows added.

Verified: V2 — checks.hardware-toggles extended to three nodes, green:
parallel node asserts the grosshack auth line precedes pam_unix in
/etc/pam.d/sudo and that with NO reader a correct password still
passes sudo while a wrong one fails (the lockout-safety invariant);
seqpam node gets stock pam_fprintd and no grosshack; nopam gets
neither. flake check + option-docs + template-sot green.
V3 pending (HARDWARE-QUEUE, AMD dev box): the real type-or-touch race,
fprintd-stopped fallback, hyprlock/greeter after a fingerprint win.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 19:03:56 +01:00
parent c840202018
commit 7aae204014
9 changed files with 175 additions and 14 deletions

View File

@@ -112,6 +112,20 @@ in
menu) when set; otherwise false.
'';
};
parallel = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
With fingerprint PAM on, accept the password OR a fingerprint at the
same prompt (type or touch, whichever comes first) instead of stock
pam_fprintd's sequential wait-for-the-reader-then-password. Uses the
pam-fprint-grosshack module (an fprintd fork source-reviewed; every
failure path falls through to the normal password rule, so password
login can never be locked out by it). Set false for the stock
sequential behavior.
'';
};
};
npu.enable = lib.mkEnableOption ''
@@ -205,7 +219,20 @@ in
"passwd" "chsh" "chfn" "chpasswd"
"polkit-1" "swaylock"
"groupadd" "groupdel" "groupmod" "groupmems"
] (_: { fprintAuth = cfg.fingerprint.pam; });
] (_: {
fprintAuth = cfg.fingerprint.pam;
} // lib.optionalAttrs (cfg.fingerprint.pam && cfg.fingerprint.parallel) {
# Parallel mode: same rule slot as stock fprintd (so ordering —
# sufficient, before pam_unix — is inherited), different module.
# grosshack prompts for the password itself while polling the
# reader; whichever lands first wins. A typed password makes the
# rule FAIL with the token stored, and the stock
# `auth sufficient pam_unix.so … try_first_pass` right after it
# does the actual validation — password stays sufficient on its
# own, so a broken reader/fprintd can never lock login out.
rules.auth.fprintd.modulePath = lib.mkForce
"${pkgs.pam-fprint-grosshack}/lib/security/pam_fprintd_grosshack.so";
});
})
# ── Newest kernel for very-new hardware (opt-in escape hatch) ──────