Files
omarchycn/bin/omarchy-setup-security-fingerprint
T
acrogenesis d593847728 Fail the suite on privileged writes through unquoted heredocs
An installer that writes a root-owned file through a heredoc with an
unquoted delimiter (<<EOF rather than <<'EOF') has the installing user's
shell expand the body first, so a user-controlled value is baked in as a
literal. Send that into /etc and root later reads or executes a path the
unprivileged user picked: a udev rule carrying
$HOME/.local/share/omarchy/bin/... resolves through a symlink that user
owns, so replacing the symlink gets their code run as root.

Add the static check. A heredoc is flagged when its delimiter is
unquoted, its body contains an install-time expansion (escaped \$VAR does
not count, since that is left for a root daemon to expand at runtime),
and its output reaches /etc, /usr, /opt, /srv, /boot or /var/lib via sudo
tee, sudo dd, a redirect, or an install/cp/mv of the generated scratch
file. Destinations written as variables are resolved from the file's own
assignments.

Sites that genuinely need install-time expansion declare it inline:

  # omarchy:heredoc-expands paths=none -- $servers is a validated IP list

paths= is machine-checked against the expansions the scanner finds to be
path-shaped, so this cannot become a rubber stamp: adding a $HOME/... to
an already-annotated heredoc makes the declaration false and trips the
check again. Path expansions anchored under a root-owned prefix, as in
"/etc/systemd/system/$unit", are correctly not path-shaped.

Annotate the sites the scan reports, each of which expands a scalar: DNS
addresses in omarchy-dns, a literal PAM line in
omarchy-setup-security-fingerprint, kernel cmdline parameters and
usernames in omarchy-upgrade-to-quattro. omarchy-provision-owner expanded
a unit name that was already a constant, so its delimiter is now quoted
and the name hardcoded; the generated unit file is byte-identical.
omarchy-windows-vm declares paths=storage,shared, the only site that
interpolates a user-chosen path.

Fixtures prove non-vacuity in both directions: the write routes other
than a pipe into sudo tee, the shapes that must stay quiet, udev rules
and a shutdown unit taken verbatim from this repository's history, and
the rubber-stamp case where a paths=none annotation on a baked $HOME path
still fails.
2026-08-29 19:23:02 -06:00

115 lines
4.4 KiB
Bash
Executable File

#!/bin/bash
# omarchy:summary=Set up fingerprint authentication for sudo, polkit, and lock screen
# omarchy:requires-sudo=true
set -e
setup_pam_config() {
# A clamshell gate runs before pam_fprintd in every stack: when the lid is
# shut the reader is unreachable, so it skips fingerprint (success=1) and PAM
# drops straight to the password prompt instead of blocking on the reader
# until it times out. Lid open → fingerprint, then password as the fallback.
#
# pam_exec needs a literal absolute path (no env expansion). Point at the
# fixed /usr/bin path the omarchy package always provides, so the gate keeps
# working across package installs and dev-link — the latter overlays
# $OMARCHY_PATH trees but leaves /usr/bin untouched.
local fprintd_gate="auth [success=1 default=ignore] pam_exec.so quiet /usr/bin/omarchy-hw-laptop-closed"
# Configure sudo
if ! grep -q pam_fprintd.so /etc/pam.d/sudo; then
echo "Configuring sudo for fingerprint authentication..."
sudo sed -i '1i auth sufficient pam_fprintd.so' /etc/pam.d/sudo
fi
if ! grep -q 'omarchy-hw-laptop-closed' /etc/pam.d/sudo; then
echo "Adding clamshell gate to sudo..."
# Insert immediately before pam_fprintd so success=1 skips exactly it.
sudo sed -i "/pam_fprintd\.so/i $fprintd_gate" /etc/pam.d/sudo
fi
# Configure polkit
if [[ -f /etc/pam.d/polkit-1 ]]; then
if ! grep -q 'pam_fprintd.so' /etc/pam.d/polkit-1; then
echo "Configuring polkit for fingerprint authentication..."
sudo sed -i '1i auth sufficient pam_fprintd.so' /etc/pam.d/polkit-1
fi
if ! grep -q 'omarchy-hw-laptop-closed' /etc/pam.d/polkit-1; then
echo "Adding clamshell gate to polkit..."
sudo sed -i "/pam_fprintd\.so/i $fprintd_gate" /etc/pam.d/polkit-1
fi
else
echo "Creating polkit configuration with fingerprint authentication..."
# omarchy:heredoc-expands paths=none -- $fprintd_gate is the literal PAM
# line defined above, shared with the two sed insertions so the gate cannot
# drift between files. The only path in it is the fixed /usr/bin one.
sudo tee /etc/pam.d/polkit-1 >/dev/null <<EOF
$fprintd_gate
auth sufficient pam_fprintd.so
auth required pam_unix.so
account required pam_unix.so
password required pam_unix.so
session required pam_unix.so
EOF
fi
}
setup_lock_fingerprint_pam() {
echo "Configuring lock screen for fingerprint authentication..."
sudo tee /etc/pam.d/omarchy-lock-fingerprint >/dev/null <<'EOF'
#%PAM-1.0
auth required pam_fprintd.so
account include system-local-login
EOF
}
echo -e "\e[32mSetting up fingerprint scanner for authentication.\n\e[0m"
# Bail before installing anything if there's no reader to talk to.
if ! omarchy-hw-fingerprint; then
echo -e "\e[31mNo fingerprint sensor detected.\e[0m"
exit 1
fi
# Install required packages
echo "Installing required packages..."
# libfprint-git provides+conflicts libfprint; pacman -S --noconfirm
# defaults the conflict prompt to N and aborts. Pre-remove it (deps-only,
# so an installed fprintd stays put) so stock libfprint installs cleanly.
if pacman -Q libfprint-git &>/dev/null; then
sudo pacman -Rdd --noconfirm libfprint-git
fi
omarchy-pkg-add libfprint fprintd usbutils
# Enroll first fingerprint
echo -e "\e[32m\nLet's setup your right index finger as the first fingerprint.\e[0m"
echo -e "Keep moving the finger around on sensor until the process completes.\n"
if sudo fprintd-enroll "$USER"; then
echo -e "\e[32m\nFingerprint enrolled successfully!\e[0m"
# Verify
echo -e "\nNow let's verify that it's working correctly.\n"
if fprintd-verify; then
# PAM comes last, once a print is enrolled and verified. Detection only
# proves a reader is there, not that libfprint can drive it — an Elan MOC
# sensor outside the elanmoc table gets this far and then fails to enroll.
# Editing the stacks up front would leave those machines pointing at
# pam_fprintd with nothing to match.
setup_pam_config
setup_lock_fingerprint_pam
echo -e "\e[32m\nPerfect! Fingerprint authentication is now configured.\e[0m"
echo "You can use your fingerprint for sudo, polkit, and lock screen (Super + Ctrl + L)."
else
echo -e "\e[31m\nVerification failed. You may want to try enrolling again.\e[0m"
fi
else
echo -e "\e[31m\nEnrollment failed. Please try again.\e[0m"
exit 1
fi