Backport of the FIDO2 authfile fix (PR #7904 by @mdisec, merged to quattro as
23dab9ec) onto the v4-0-1 release branch.
pamu2fcfg wrote to /tmp/fido2 and the registration was then moved into place
with sudo mv. Any other local user can pre-create /tmp/fido2, and rename(2) does
not dereference the final component, so the privileged move installed the
attacker's symlink itself as pam_u2f's global authfile -- a file consulted by
sufficient lines in /etc/pam.d/sudo and /etc/pam.d/polkit-1.
The same move also carried the staged file's ownership into /etc, so on every
install to date /etc/fido2/fido2 is owned by the invoking user at mode 0644.
That needs no attacker, no race and no second account: anything running as that
uid can append its own credential and satisfy the machine's sudo prompt without
knowing the password.
The setup now creates a unique staging file as root beside the final authfile
and pipes pamu2fcfg into it, so root never reopens a caller-owned pathname,
rejects failed or empty enrollment output, publishes with an atomic mv -Tf,
cleans the exact staging file after every failure, refuses non-regular authfile
states, and installs root:root 0644. A migration repairs machines set up by
earlier versions by replacing the inode rather than chowning in place: a process
that already holds a writable descriptor on the legacy user-owned mapping keeps
it, so the repair has to leave that inode behind where PAM no longer reads it.
Clean cherry-pick: all six files are byte-identical to quattro, so merging
v4-0-1 into quattro resolves without a conflict. The migration's timestamp
(1787494718) is older than others already on this branch, which is harmless:
omarchy-migrate marks each migration by filename rather than tracking a
watermark, so this one runs on every machine that has not run this exact file.
test/shell passes: 195 files, including the three this adds -- the setup's
staging and failure paths, the migration's repair and unrepairable states, and
the removal's symlink handling. test/cli passes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DEMYa9UWtroz93DhMTtcV
Backport of the DNS PATH pin (PR #8172 by @mdisec, merged to quattro as
4637735a) onto the v4-0-1 release branch.
omarchy dev link prepends a user-writable checkout's bin/ to sudo's secure_path
so privileged Omarchy commands resolve to the development versions, and that
reaches the subprocesses they launch too. This branch carries the same
passwordless grant -- etc/sudoers.d/omarchy-dns lets wheel run
/usr/bin/omarchy-dns Cloudflare, Google and DHCP without a password -- so the
packaged script ran as root while resolving bare helpers (dirname, install,
tee, rm, nmcli, systemctl, awk) through the caller's secure_path. Write access
to a dev checkout became arbitrary root execution, with no password prompt in
the way.
Pin PATH to trusted system directories once EUID is 0. The restriction lands
only after elevation, so the unprivileged wrapper phase keeps the caller's PATH
and can still find sudo or pkexec; every helper the privileged half uses is a
system utility, so it needs nothing from the checkout.
Clean cherry-pick: both files are byte-identical to quattro, so merging v4-0-1
into quattro resolves without a conflict. Verified by mutation: with the pin
removed, test/shell.d/dns-sudoers-test.sh fails at the poisoned-helper case;
restored, all four of its cases pass. test/shell (189 files) and test/cli pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DEMYa9UWtroz93DhMTtcV