Skip to content

Defer to system-auth in the polkit stack written by fingerprint/FIDO2 setup - #9873

Open
Wheel-Smith wants to merge 2 commits into
omacom:quattrofrom
Wheel-Smith:fix/polkit-faillock-system-auth
Open

Defer to system-auth in the polkit stack written by fingerprint/FIDO2 setup#9873
Wheel-Smith wants to merge 2 commits into
omacom:quattrofrom
Wheel-Smith:fix/polkit-faillock-system-auth

Conversation

@Wheel-Smith

Copy link
Copy Markdown

Summary

Reported privately to security@omarchy.org first, per SECURITY.md; opening this PR at the maintainer's request.

omarchy setup security fingerprint and omarchy setup security fido2 create an /etc/pam.d/polkit-1 that does not defer to system-auth, which drops pam_faillock from the polkit authentication stack. On an affected machine, polkit password prompts have no lockout after repeated failures, those failures are not recorded in the shared faillock tally, and they do not count toward the lockout that protects login and sudo. sudo and login themselves are unaffected.

Root cause

setup_pam_config branches on whether /etc/pam.d/polkit-1 already exists:

if [[ -f /etc/pam.d/polkit-1 ]]; then
  ... sed the fingerprint/FIDO2 lines into the existing file ...
else
  ... create /etc/pam.d/polkit-1 from scratch ...
fi

On a stock Arch system that test is effectively always false: since pam 1.5.3 the polkit package ships its stack as a vendor file at /usr/lib/pam.d/polkit-1, and /etc/pam.d/polkit-1 does not exist. So the else branch runs and writes a new /etc/pam.d/polkit-1. Per pam.d(5), a file in /etc/pam.d overrides the vendor file of the same name, so this hand-written stack replaces the distro one.

The hand-written stack lists pam_unix directly instead of including system-auth, so pam_faillock (and pam_env, pam_time, pam_limits, pam_systemd_home) never load on the polkit path. The vendor file it shadows is four lines of include system-auth, and the sudo stack the same setup edits keeps its include system-auth — so only polkit is affected. omarchy-setup-security-fido2 contains the identical branch and defect.

Impact

At any polkit "Authentication Required" prompt (GUI privilege escalation: package installs, system settings, mounting, etc.) on an affected machine, a local attacker — session access, or local code running as the user that can trigger a polkit-authenticated action — can guess the password with no lockout (the policy elsewhere is deny=10), without leaving a faillock audit trail, and without consuming the shared lockout budget that would otherwise trip on login/sudo and alert the user.

This is a weakening of a brute-force/lockout control rather than a direct compromise; it is local-only and affects only machines that enabled fingerprint or FIDO2 auth. It also persisted after omarchy remove security fingerprint / fido2, because the created file was left in place; with this change, once the hardware-auth lines are removed the remaining stack is the vendor-equivalent include system-auth.

Changes

  • bin/omarchy-setup-security-fingerprint, bin/omarchy-setup-security-fido2 — the file-creation branch now defers to system-auth, mirroring the vendor file and the sudo stack, so pam_faillock is restored. The clamshell gate and the pam_fprintd / pam_u2f sufficient lines are kept in front unchanged.

  • migrations/1788256455.sh — repairs machines the old setup already configured, since the forward fix does not rewrite an existing /etc/pam.d/polkit-1. It only acts on an Omarchy-created polkit-1 (unowned by any package) that lacks include system-auth and carries an Omarchy hardware-auth marker (the clamshell gate, pam_fprintd, or the FIDO2 authfile); it preserves the configured hardware-auth lines, backs up the original, re-verifies, and is idempotent. Administrator-authored files are left untouched.

  • test/shell.d/security-polkit-faillock-test.sh — asserts the stack each setup creates defers to system-auth. There was previously no test on the created polkit content (the FIDO2 test mocks that write to /dev/null), which is why this went unnoticed. The test fails on the pre-fix scripts and passes with this change.

Validation

Applied on an affected machine and confirmed end-to-end:

  • Before: polkit failures record nothing and never lock (12 attempts, empty tally).
  • After: polkit failures are recorded against the shared tally (Source polkit-1) and the stack locks out at deny=10, matching sudo and login; the fingerprint line and clamshell gate are preserved.
  • The migration is idempotent (a second run makes no change) and backs up the original before rewriting.

./test/all passes (the only failures on my checkout are the pre-existing ones that require a sibling omarchy-pkgs checkout and root-owned bind mounts, unrelated to this change).

… setup

The fingerprint and FIDO2 setup commands create /etc/pam.d/polkit-1 from
scratch on Arch, where the polkit package ships its stack in
/usr/lib/pam.d/polkit-1 and /etc/pam.d/polkit-1 does not exist. The
hand-rolled stack listed pam_unix directly instead of including system-auth,
which dropped pam_faillock from the polkit path: polkit prompts had no
brute-force lockout, their failures were not recorded, and they did not count
toward the lockout protecting login and sudo. Defer to system-auth, matching
the vendor file and the sudo stack, keeping the clamshell gate and the
pam_fprintd / pam_u2f sufficient lines in front.

Add a migration to repair installs the old setup already configured, since the
forward fix does not rewrite an existing polkit-1. It acts only on an
Omarchy-created polkit-1 that lacks the system-auth include and carries a
hardware-auth marker, preserves the configured auth lines, backs up the
original, and is idempotent.

Add a test asserting the stack each setup creates defers to system-auth; the
created polkit content was previously untested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The migration can permanently skip failed repairs and leaves markerless legacy configurations vulnerable.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Restores pam_faillock protection for polkit authentication configured with fingerprint or FIDO2.

Changes:

  • Delegates generated polkit PAM stacks to system-auth.
  • Adds migration logic for existing installations.
  • Adds regression tests for generated configurations.
File summaries
File Description
test/shell.d/security-polkit-faillock-test.sh Verifies generated stacks include system-auth, but lacks migration coverage.
migrations/1788256455.sh Repairs legacy overrides, but misses markerless configurations and incorrectly succeeds on failures.
bin/omarchy-setup-security-fingerprint Uses system-auth in generated polkit configuration.
bin/omarchy-setup-security-fido2 Uses system-auth in generated polkit configuration.
Review details

Suppressed comments (1)

migrations/1788256455.sh:22

  • This predicate does not prove that Omarchy created the file: administrator-authored files under /etc/pam.d are normally package-unowned, and any custom stack containing pam_fprintd or this FIDO2 authfile matches. The rebuild then discards all non-auth custom directives and comments. Restrict the migration to exact known Omarchy layouts, or patch only the known bare pam_unix entries while preserving unrelated content.
  grep -qE 'omarchy-hw-laptop-closed|pam_fprintd\.so|authfile=/etc/fido2/fido2' "$polkit"; then
  • Files reviewed: 2/4 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread migrations/1788256455.sh Outdated
Comment on lines +45 to +52
else
echo "polkit repair could not be verified; restoring the original file." >&2
sudo cp -a "$backup" "$polkit"
fi
else
echo "Administrator privileges are required to repair $polkit. Run omarchy-migrate again from a terminal." >&2
fi
fi
Comment thread migrations/1788256455.sh Outdated
if [[ -f $polkit ]] &&
! pacman -Qo "$polkit" &>/dev/null &&
! grep -qE '^auth[[:space:]]+include[[:space:]]+system-auth' "$polkit" &&
grep -qE 'omarchy-hw-laptop-closed|pam_fprintd\.so|authfile=/etc/fido2/fido2' "$polkit"; then
Comment thread migrations/1788256455.sh Outdated
Comment on lines +19 to +22
if [[ -f $polkit ]] &&
! pacman -Qo "$polkit" &>/dev/null &&
! grep -qE '^auth[[:space:]]+include[[:space:]]+system-auth' "$polkit" &&
grep -qE 'omarchy-hw-laptop-closed|pam_fprintd\.so|authfile=/etc/fido2/fido2' "$polkit"; then
…d tests

Address review feedback on the polkit faillock migration:

- Match only the exact stack the setup commands wrote and replace just the bare
  pam_unix lines, preserving comments and the hardware-auth lines. An
  administrator-authored polkit-1 carrying any other directive is left
  untouched, rather than rebuilt from scratch.

- Also repair the markerless post-removal layout: both remove commands strip
  their own marker lines but leave the bare pam_unix stack behind, so keying on
  a hardware-auth marker skipped those machines permanently.

- Exit non-zero when the backup cannot be created or the rewrite cannot be
  verified (after restoring). omarchy-migrate runs under set -e and records a
  migration complete unconditionally after it returns, so a failed repair must
  fail loudly to be retried instead of silently marked done.

- Add test/shell.d/security-polkit-migration-test.sh covering the fingerprint,
  FIDO2, combined, and markerless layouts, comment preservation, idempotence, an
  untouched administrator stack, and the refused-sudo and unverifiable-write
  failure paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Wheel-Smith

Copy link
Copy Markdown
Author

Thanks for the careful review, @ErikMelton (and @Adolanium for redirecting it here from #8170). All three points were fair; I've addressed them in the follow-up commit, which reworks the migration and adds a test for it.

1. Overwriting administrator-authored PAM policy. The marker-based predicate is gone. The migration now recognizes only the exact stack the setup commands wrote — every non-blank, non-comment line must be one of the known hardware-auth auth lines (the clamshell gate, pam_fprintd, or the FIDO2 pam_u2f line) or a bare X required pam_unix.so, with all four bare lines present and system-auth not already included. Instead of rebuilding from scratch, it now replaces only the bare pam_unix lines via sed, so comments and the hardware-auth lines are preserved verbatim. Any file carrying a directive Omarchy never writes is left untouched.

2. Post-removal installations remaining vulnerable. Correct — both remove commands strip only their own marker lines and leave the bare four-line pam_unix override behind. The new layout check keys on that bare stack rather than a hardware-auth marker, so the markerless post-removal case is now repaired too.

3. Failed repairs marked complete. Both failure paths now exit non-zero — when the backup cannot be created, and when the rewrite cannot be verified (after restoring the original). Since omarchy-migrate runs under set -e and records the marker after the migration returns, exiting non-zero leaves it pending to retry rather than silently marked done.

I also added test/shell.d/security-polkit-migration-test.sh, which exercises the migration directly (retargeted path + stubbed sudo, following sshd-hardening-migration-test.sh): the fingerprint, FIDO2, combined, and markerless layouts are repaired with their hardware-auth lines and comments preserved; an already-fixed stack and an administrator-authored stack are left untouched; and both the refused-sudo and unverifiable-write paths exit non-zero with the original file restored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants