Harden the polkit migration: exact-layout match, retry on failure, and 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>
This commit is contained in:
1 parent
8a13eac872
commit
21e7975352
2 files changed
+232
-32
No files matched your search
+59
-32
@@ -10,43 +10,70 @@ polkit=/etc/pam.d/polkit-1
|
||||
# failures never counted toward the shared tally. The setup commands now defer to
|
||||
# system-auth; this repairs the files the old ones already wrote.
|
||||
#
|
||||
# In scope only when the file exists, no package owns it (Omarchy wrote it), it
|
||||
# does not already defer to system-auth, and it carries an Omarchy hardware-auth
|
||||
# marker (the clamshell gate, pam_fprintd, or the FIDO2 authfile). That pins the
|
||||
# repair to the exact stack the setup commands produced and leaves an
|
||||
# administrator's own polkit-1 untouched. Idempotent by construction: once
|
||||
# system-auth is included, this run and every other user's run no-op.
|
||||
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
|
||||
# Only the exact stack those commands produced is touched. 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`; all four bare pam_unix lines must be present; and
|
||||
# system-auth must not already be included. This matches the fingerprint, FIDO2,
|
||||
# combined, and post-removal (markerless, both remove commands strip only their
|
||||
# own marker lines) layouts, and refuses any administrator-authored stack that
|
||||
# carries other directives. The repair replaces only the bare pam_unix lines, so
|
||||
# comments and the hardware-auth lines are preserved verbatim.
|
||||
|
||||
is_omarchy_vulnerable_stack() {
|
||||
local file=$1 line
|
||||
local re_gate='^auth[[:space:]]+\[success=1 default=ignore\][[:space:]]+pam_exec\.so quiet /usr/bin/omarchy-hw-laptop-closed[[:space:]]*$'
|
||||
local re_fprintd='^auth[[:space:]]+sufficient[[:space:]]+pam_fprintd\.so[[:space:]]*$'
|
||||
local re_u2f='^auth[[:space:]]+sufficient[[:space:]]+pam_u2f\.so cue authfile=/etc/fido2/fido2[[:space:]]*$'
|
||||
local re_bare='^(auth|account|password|session)[[:space:]]+required[[:space:]]+pam_unix\.so[[:space:]]*$'
|
||||
local phase
|
||||
|
||||
# Already fixed, or partially converted: leave it alone (also makes reruns no-op).
|
||||
if grep -qE '(auth|account|password|session)[[:space:]]+include[[:space:]]+system-auth' "$file"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# The vulnerable signature: all four phases delegated to a bare pam_unix.
|
||||
for phase in auth account password session; do
|
||||
grep -qE "^${phase}[[:space:]]+required[[:space:]]+pam_unix\.so[[:space:]]*\$" "$file" || return 1
|
||||
done
|
||||
|
||||
# Every meaningful line must be one Omarchy itself wrote; anything else means
|
||||
# an administrator has edited this file, so it is not ours to rewrite.
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
[[ -z ${line//[[:space:]]/} ]] && continue
|
||||
[[ $line == \#* ]] && continue
|
||||
[[ $line =~ $re_gate || $line =~ $re_fprintd || $line =~ $re_u2f || $line =~ $re_bare ]] && continue
|
||||
return 1
|
||||
done <"$file"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ -f $polkit ]] && is_omarchy_vulnerable_stack "$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 ! sudo cp -a "$polkit" "$backup"; then
|
||||
echo "Could not back up $polkit; leaving it unchanged so the migration retries." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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
|
||||
# Replace only the bare pam_unix lines; keep comments, blank lines, and the
|
||||
# hardware-auth (gate / pam_fprintd / pam_u2f) lines exactly as they are.
|
||||
rebuilt=$(sed -E 's/^(auth|account|password|session)([[:space:]]+)required[[:space:]]+pam_unix\.so[[:space:]]*$/\1\2include system-auth/' "$polkit")
|
||||
|
||||
if ! printf '%s\n' "$rebuilt" | sudo tee "$polkit" >/dev/null; then
|
||||
echo "Could not write $polkit; restoring the original." >&2
|
||||
sudo cp -a "$backup" "$polkit" || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -qE '^auth[[:space:]]+include[[:space:]]+system-auth' "$polkit"; then
|
||||
echo "Restored polkit brute-force protection. Previous file saved at $backup."
|
||||
else
|
||||
echo "Administrator privileges are required to repair $polkit. Run omarchy-migrate again from a terminal." >&2
|
||||
echo "polkit repair could not be verified; restoring the original file." >&2
|
||||
sudo cp -a "$backup" "$polkit"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
Reference in new issue
Block a user