Fall back to polkit when the DNS sudoers grant is missing (#7492)
grant_covers re-implemented etc/sudoers.d/omarchy-dns in bash -- one of the three providers, and %wheel -- but never asked whether the rule was installed. It ships in the etc/ tree that omarchy-settings copies, so every machine still on an older settings package answers yes to a grant it does not have. require_root then execs into sudo with no way back, and the panel's one-click toggle dies on a password prompt it has no terminal to show. Ask sudo instead. `sudo -l` alone reports whether a command is permitted, which the blanket %wheel rule answers yes to for everything, but the long listing prints the matched entry's tags -- !authenticate is the grant and nothing else. It runs nothing, and under -n it prompts for nothing, so a machine without the rule falls through to polkit and gets a prompt on screen. The provider list and the wheel check go away with it; sudo owns that policy now, and it stays right if the rule is ever edited or removed. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3765e8010b
commit
1e70cca144
+15
-32
@@ -33,45 +33,28 @@ provider_from_arg() {
|
|||||||
# where $OMARCHY_PATH points at a checkout.
|
# where $OMARCHY_PATH points at a checkout.
|
||||||
PACKAGED_PATH=/usr/bin/omarchy-dns
|
PACKAGED_PATH=/usr/bin/omarchy-dns
|
||||||
|
|
||||||
# True when etc/sudoers.d/omarchy-dns covers this invocation. Both halves of the
|
# True when sudo would run this exact command without stopping for a password.
|
||||||
# rule have to hold -- one of the three providers, and %wheel -- or sudo asks
|
# `sudo -l` on its own reports whether a command is permitted, which the blanket
|
||||||
# for a password like any other command.
|
# %wheel rule answers yes to for everything; the long listing prints the matched
|
||||||
grant_covers() {
|
# entry's tags, so !authenticate is the grant in etc/sudoers.d/omarchy-dns and
|
||||||
local provider="${1:-}"
|
# nothing else. Listing runs nothing and, under -n, prompts for nothing, so a
|
||||||
local group
|
# machine whose omarchy-settings predates that file falls through to polkit
|
||||||
|
# instead of dying on a password prompt it has no terminal to show.
|
||||||
case "$provider" in
|
sudo_grants_passwordless() {
|
||||||
Cloudflare | Google | DHCP) ;;
|
sudo -n -l -l "$PACKAGED_PATH" "$@" 2>/dev/null | grep -q '!authenticate'
|
||||||
*) return 1 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
for group in $(id -nG 2>/dev/null); do
|
|
||||||
[[ $group == wheel ]] && return 0
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
require_root() {
|
require_root() {
|
||||||
if (( EUID == 0 )); then
|
if (( EUID == 0 )); then
|
||||||
return
|
return
|
||||||
fi
|
elif [[ -t 0 ]] || sudo_grants_passwordless "$@"; then
|
||||||
|
# A terminal can carry sudo's own password prompt. Without one, sudo is
|
||||||
# A terminal can carry sudo's own password prompt. Without one, sudo is still
|
# right only where the grant reaches; polkit can at least put a prompt on
|
||||||
# right when etc/sudoers.d/omarchy-dns covers this invocation: that grant is
|
# screen, and offer to authenticate as someone else.
|
||||||
# what keeps the panel's one-click provider switch from raising a polkit
|
|
||||||
# prompt. Custom and a caller outside %wheel still go through polkit, which
|
|
||||||
# can at least offer to authenticate as someone else.
|
|
||||||
#
|
|
||||||
# Do not swap this for a `sudo -n -l` probe. That reports whether a command is
|
|
||||||
# permitted, not whether it is passwordless, so the blanket %wheel rule
|
|
||||||
# answers yes for every argument and Custom dies on `sudo -n` instead of
|
|
||||||
# falling through to pkexec.
|
|
||||||
if [[ -t 0 ]] || grant_covers "${1:-}"; then
|
|
||||||
exec sudo "$PACKAGED_PATH" "$@"
|
exec sudo "$PACKAGED_PATH" "$@"
|
||||||
fi
|
else
|
||||||
|
|
||||||
exec pkexec "$PACKAGED_PATH" "$@"
|
exec pkexec "$PACKAGED_PATH" "$@"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
networkmanager_global_dns() {
|
networkmanager_global_dns() {
|
||||||
|
|||||||
@@ -22,12 +22,11 @@ fi
|
|||||||
grep -Fx 'PACKAGED_PATH=/usr/bin/omarchy-dns' "$dns" >/dev/null ||
|
grep -Fx 'PACKAGED_PATH=/usr/bin/omarchy-dns' "$dns" >/dev/null ||
|
||||||
fail "omarchy-dns elevates the path the sudoers rule names"
|
fail "omarchy-dns elevates the path the sudoers rule names"
|
||||||
|
|
||||||
# sudo -l answers whether a command is permitted, not whether it is
|
# `sudo -l` on its own answers whether a command is permitted, not whether it
|
||||||
# passwordless, and Omarchy ships a blanket %wheel rule that permits
|
# is passwordless, and Omarchy ships a blanket %wheel rule that permits
|
||||||
# everything. A probe built on it sends Custom into `sudo -n`, which fails
|
# everything. Only the long listing prints the matched entry's tags.
|
||||||
# outright instead of falling through to pkexec.
|
grep -E 'sudo -n -l -l' "$dns" >/dev/null ||
|
||||||
! grep -E '^[[:space:]]*[^#[:space:]].*sudo -n -l' "$dns" >/dev/null ||
|
fail "omarchy-dns reads the grant from the long sudo listing"
|
||||||
fail "omarchy-dns does not decide elevation with a sudo -l probe"
|
|
||||||
|
|
||||||
pass "dns sudoers rule is scoped to the stock providers"
|
pass "dns sudoers rule is scoped to the stock providers"
|
||||||
|
|
||||||
@@ -44,25 +43,32 @@ trap 'rm -rf "$test_tmp"' EXIT
|
|||||||
stub_bin="$test_tmp/bin"
|
stub_bin="$test_tmp/bin"
|
||||||
mkdir -p "$stub_bin"
|
mkdir -p "$stub_bin"
|
||||||
|
|
||||||
# Both stubs stand in for the exec at the end of require_root, so the DNS writes
|
# pkexec stands in for the exec at the end of require_root, so the DNS writes
|
||||||
# below it never run, and neither real sudo nor real pkexec is reached.
|
# below it never run and real pkexec is never reached.
|
||||||
for command in sudo pkexec; do
|
cat >"$stub_bin/pkexec" <<'SH'
|
||||||
cat >"$stub_bin/$command" <<SH
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
printf '$command %s\n' "\$*" >"\$ELEVATION_LOG"
|
printf 'pkexec %s\n' "$*" >"$ELEVATION_LOG"
|
||||||
SH
|
SH
|
||||||
chmod +x "$stub_bin/$command"
|
chmod +x "$stub_bin/pkexec"
|
||||||
done
|
|
||||||
|
|
||||||
# The rule is %wheel-scoped, so group membership decides the route as much as
|
# The sudo stub plays both parts: it answers the passwordless probe from
|
||||||
# the provider does. Stub id rather than reading the real groups: the suite has
|
# STUB_GRANTED, the providers etc/sudoers.d/omarchy-dns covers on this machine,
|
||||||
# to give the same answer on a build user outside wheel.
|
# and logs the elevation otherwise. STUB_GRANTED empty stands for an install
|
||||||
cat >"$stub_bin/id" <<'SH'
|
# whose omarchy-settings predates the file.
|
||||||
|
cat >"$stub_bin/sudo" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
[[ ${1:-} == -nG ]] || exec /usr/bin/id "$@"
|
if [[ $1 == -n && $2 == -l ]]; then
|
||||||
printf '%s\n' "${STUB_GROUPS-users wheel}"
|
for granted in ${STUB_GRANTED-Cloudflare Google DHCP}; do
|
||||||
|
[[ ${!#} == "$granted" ]] || continue
|
||||||
|
echo " Options: !authenticate"
|
||||||
|
exit 0
|
||||||
|
done
|
||||||
|
echo " Matched: ${!#}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
printf 'sudo %s\n' "$*" >"$ELEVATION_LOG"
|
||||||
SH
|
SH
|
||||||
chmod +x "$stub_bin/id"
|
chmod +x "$stub_bin/sudo"
|
||||||
|
|
||||||
elevation_for() {
|
elevation_for() {
|
||||||
: >"$test_tmp/elevation"
|
: >"$test_tmp/elevation"
|
||||||
@@ -90,8 +96,12 @@ custom=$(elevation_for Custom)
|
|||||||
[[ $custom == "pkexec /usr/bin/omarchy-dns Custom" ]] ||
|
[[ $custom == "pkexec /usr/bin/omarchy-dns Custom" ]] ||
|
||||||
fail "omarchy-dns leaves Custom on the polkit path, since no sudoers rule covers it" "got: $custom"
|
fail "omarchy-dns leaves Custom on the polkit path, since no sudoers rule covers it" "got: $custom"
|
||||||
|
|
||||||
non_wheel=$(STUB_GROUPS="users" elevation_for Cloudflare)
|
# The grant is what makes sudo passwordless, so its absence -- an install still
|
||||||
[[ $non_wheel == "pkexec /usr/bin/omarchy-dns Cloudflare" ]] ||
|
# on an older omarchy-settings, or a user outside %wheel the rule cannot match
|
||||||
fail "omarchy-dns leaves a user outside %wheel on the polkit path, since the rule cannot match them" "got: $non_wheel"
|
# -- has to route to polkit. Betting on sudo here leaves the panel's one-click
|
||||||
|
# toggle execing into a password prompt it has no terminal to show.
|
||||||
|
ungranted=$(STUB_GRANTED="" elevation_for Cloudflare)
|
||||||
|
[[ $ungranted == "pkexec /usr/bin/omarchy-dns Cloudflare" ]] ||
|
||||||
|
fail "omarchy-dns falls back to polkit where the sudoers grant is not installed" "got: $ungranted"
|
||||||
|
|
||||||
pass "omarchy-dns falls back to polkit wherever the grant does not reach"
|
pass "omarchy-dns falls back to polkit wherever the grant does not reach"
|
||||||
|
|||||||
Reference in New Issue
Block a user