diff --git a/bin/omarchy-setup-security-fido2 b/bin/omarchy-setup-security-fido2 index 85fe2039..75a87124 100755 --- a/bin/omarchy-setup-security-fido2 +++ b/bin/omarchy-setup-security-fido2 @@ -31,11 +31,11 @@ setup_pam_config() { echo "Creating polkit configuration with FIDO2 authentication..." sudo tee /etc/pam.d/polkit-1 >/dev/null <<'EOF' auth sufficient pam_u2f.so cue authfile=/etc/fido2/fido2 -auth required pam_unix.so +auth include system-auth -account required pam_unix.so -password required pam_unix.so -session required pam_unix.so +account include system-auth +password include system-auth +session include system-auth EOF fi } diff --git a/bin/omarchy-setup-security-fingerprint b/bin/omarchy-setup-security-fingerprint index 383aa376..fd84d2db 100755 --- a/bin/omarchy-setup-security-fingerprint +++ b/bin/omarchy-setup-security-fingerprint @@ -47,11 +47,11 @@ setup_pam_config() { sudo tee /etc/pam.d/polkit-1 >/dev/null </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 + + echo "Rewriting $polkit to defer to system-auth (restores pam_faillock lockout)..." + + # Keep the configured hardware-auth auth lines verbatim (the clamshell gate and + # the pam_fprintd / pam_u2f 'sufficient' lines); only the bare pam_unix stack is + # replaced with the system-auth includes the vendor file and the sudo stack use. + hw_auth=$(grep -E '^auth' "$polkit" | grep -vE 'pam_unix\.so') + + rebuilt=$( + [[ -n $hw_auth ]] && printf '%s\n' "$hw_auth" + printf 'auth include system-auth\n' + printf 'account include system-auth\n' + printf 'password include system-auth\n' + printf 'session include system-auth\n' + ) + + backup="$polkit.omarchy-bak.$(date +%s)" + if sudo cp -a "$polkit" "$backup"; then + printf '%s\n' "$rebuilt" | sudo tee "$polkit" >/dev/null + + if grep -qE '^auth[[:space:]]+include[[:space:]]+system-auth' "$polkit"; then + echo "Restored polkit brute-force protection. Previous file saved at $backup." + 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 diff --git a/test/shell.d/security-polkit-faillock-test.sh b/test/shell.d/security-polkit-faillock-test.sh new file mode 100644 index 00000000..74582457 --- /dev/null +++ b/test/shell.d/security-polkit-faillock-test.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +# The fingerprint and FIDO2 setup commands each create /etc/pam.d/polkit-1 from +# scratch when the file does not already exist -- which is the normal case on +# Arch, where the polkit package ships its PAM stack in /usr/lib/pam.d/polkit-1 +# and /etc/pam.d/polkit-1 is absent. A hand-rolled stack that lists pam_unix +# directly instead of `include system-auth` silently drops pam_faillock, so +# polkit prompts would have no brute-force lockout and their failures would not +# count toward the shared tally. Assert the created stack defers to system-auth. + +for setup in omarchy-setup-security-fingerprint omarchy-setup-security-fido2; do + script="$ROOT/bin/$setup" + + # Pull the here-doc body the setup writes to /etc/pam.d/polkit-1. + body=$(sed -n "/tee \/etc\/pam.d\/polkit-1/,/^EOF\$/p" "$script") + + [[ -n $body ]] || fail "$setup writes a polkit-1 stack" + + for phase in auth account password session; do + grep -qE "^${phase}[[:space:]]+include[[:space:]]+system-auth" <<<"$body" || + fail "$setup polkit-1 $phase defers to system-auth (keeps faillock)" "$body" + done + + ! grep -qE "^(account|password|session)[[:space:]]+required[[:space:]]+pam_unix" <<<"$body" || + fail "$setup polkit-1 does not hand-roll a bare pam_unix stack" "$body" + + pass "$setup creates a polkit-1 stack that includes system-auth" +done