feature: shutdown option in pam_faillock to protect FDE keys from lock-screen brute force
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 28/100
- Issue type
- Feature
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- c, linux
- Domain
- authentication, operating-systems, security
Research direction
This is a design proposal for linux-pam pam_faillock, not a ready first patch: read the issue’s questions for maintainers before coding. The sketch names struct options, deny=N parsing, write_tally() after update_tally(), reboot(RB_POWER_OFF), faillock.conf, faillock.conf.5.xml and pam_faillock.8.xml. Done means maintainers accept the option (vs a separate module), then a PR with tally persisted and sync before poweroff, skipped for root unless even_deny_root, and no behavior change when shutdown=0. There are no comments after months, so wait for maintainer direction.
Written by the indexing model from the issue text.
Description
Feature proposal: shutdown=N option in pam_faillock to protect FDE keys from lock-screen brute force
Background / Threat model
Laptops with full-disk encryption are only secure before the machine is booted. Once the user has logged in (even if the screen is then locked), the FDE master key lives in RAM. Users keep laptops unattended for bio break at school, coffee shops, at work.
This proposal addresses one specific attack vector: password brute-force at the lock screen — either by a human physically at the machine, or by an automated USB HID device (e.g. a rubber-ducky-style device that types password guesses at high speed). In this scenario, the attacker has physical access to a running, locked machine and tries to guess the password through the normal authentication path without leaving permanent marks on the machine that would be noticed by the returning user.
Against this attack, the existing deny=N mechanism locks the account but keeps the machine running — so the FDE key stays in RAM and the threat is not mitigated. Powering the machine off clears DRAM within seconds, destroying the key material before the attacker can extract it by any means.
Out of scope
This proposal explicitly does not address:
- Thunderbolt / PCIe / FireWire DMA attacks — the attacker never touches the auth path, so no failure counter is incremented. These are properly addressed by IOMMU / Kernel DMA Protection,
bolt/boltctl, and firmware-level settings. I intend to file a separate proposal covering the kernel/userspace side of DMA protection. - Cold-boot attacks — again, no auth attempt is made; the attacker physically removes RAM. Mitigations (memory encryption, rapid RAM clearing on shutdown) live elsewhere in the stack.
- Evil-maid boot-media attacks — addressed by Secure Boot and TPM-backed measured boot.
The scope here is narrow on purpose: the one thing pam_faillock can plausibly defend against is password-based brute force, because that's the one thing it sees.
This mitigation is currently only achievable through out-of-tree scripts glued on with pam_exec, pam_faillock, and shell wrappers. That works, but it has a large and unnecessary attack surface (exposed script paths, inherited environment, writable /usr/local, etc.) for what is fundamentally a very small piece of logic.
Why existing options are insufficient
deny=Nlocks the account but keeps the machine running — FDE key stays in RAM, so the threat is not mitigated.pam_execcan invokepowerofffrom a script, but:- The script path becomes an attack surface (anything that can write to it gains root-exec-on-auth-fail).
- Environment/FD inheritance needs careful handling to be safe.
- Users end up writing and maintaining bespoke shell scripts for what should be a one-line config option.
Proposal
Add an optional shutdown=N parameter to pam_faillock (mirroring the existing deny=N syntax):
# /etc/security/faillock.conf
shutdown = 3 # poweroff after 3 consecutive failures
deny = 5 # lock account after 5 (fallback)
Semantics:
N = 0(default): feature disabled. No behavior change vs. currentpam_faillock.N > 0: when the recorded consecutive failure count for a user reaches exactlyN, the module callsreboot(RB_POWER_OFF)directly.- Skipped for root /
admin_groupunlesseven_deny_rootis set (consistent withdenysemantics). - Tally is persisted to disk before the shutdown, and
sync()is called, so the evidence survives the reboot. - If
reboot(2)fails (e.g. the PAM service lacksCAP_SYS_BOOT), the failure is logged and the auth stack continues normally. No existing behavior is broken.
Why inside pam_faillock and not a separate module
pam_faillock already:
- Tracks per-user consecutive failure counts with a sliding window.
- Has the exact hook point needed (inside
write_tally(), right where thedenythreshold is currently checked). - Owns the tally-file semantics that any N-failures-based action has to integrate with.
Duplicating all of that into a separate module would be considerable redundant code. Adding one small, opt-in code path next to the existing deny logic is the minimal change.
Why a direct syscall rather than exec-a-command
This is deliberate. The motivation for the feature is reducing attack surface compared to the existing pam_exec-based workarounds. reboot(RB_POWER_OFF) from inside the module means:
- No external binary path that an attacker could swap.
- No environment variables or file descriptors to sanitize.
- No
fork+exec— simpler to reason about, no zombie handling, no race windows.
A configurable on_deny=<command> option was considered and rejected for exactly these reasons.
Interaction with deny
For the feature to be effective, shutdown <= deny must hold. If shutdown > deny, the account is locked at deny and subsequent failures aren't recorded — so shutdown would never fire. This is a documentable user-misconfiguration concern, similar to how fail_interval can be misused.
The intended usage pattern is:
shutdown— small, aggressive threshold — protects the machine (FDE keys in RAM against lock-screen brute force).deny— larger, fallback threshold — protects the account if shutdown can't fire (e.g. running withoutCAP_SYS_BOOT).
Questions for maintainers
- Is this direction acceptable in principle, or would you prefer this live as a separate module (e.g.
pam_failshutdown)? - Naming:
shutdown=Nfeels natural alongsidedeny=N, but is there a preferred convention? (poweroff=N?reboot_on_fail=N?) - Should the behavior be gated behind a compile-time option (
--enable-faillock-shutdownor similar) so distros that don't want it can opt out? - Any concerns about the default being
0/ disabled and the zero-behavior-change claim for existing deployments?
Implementation sketch
I have a working patch (~30 lines of code + config parsing + man page updates) ready to go. Happy to submit it as a PR once there's agreement on the design. The patch:
- Adds
unsigned short shutdowntostruct options. - Adds
shutdown=Nparsing alongside the existingdeny=Nparser. - Adds the syscall call site inside
write_tally()afterupdate_tally()returns success. - Updates
faillock.conf,faillock.conf.5.xml, andpam_faillock.8.xml.
Thanks for taking the time to review this proposal. This is my first open-source contribution. My GitHub account is new, but I am a long-time developer (20+ years).
- Dominant language
- C
- Stars
- 807
- Forks
- 368
- Avg merge
- 6d 16h
- Merged PRs (30d)
- 4
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from linux-pam/linux-pam
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 48/100
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
-
Difficulty 4/5 3-5 days Newbie friendliness 47/100
All issues in linux-pam/linux-pam
Similar issues
-
Issue-Bug Needs-Triage
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
ly05010419/MacNativeGames#2001 ·
Maintainers usually reply within 1 day
-
area/docdb kind/bug priority/medium status/awaiting-triage
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
yugabyte/yugabyte-db#37426 ·
Maintainers usually reply within 1 day
-
area/documentation
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
obsproject/obs-studio#14184 ·
Maintainers usually reply within 1 day