Skip to content

fix: polkit org.ublue.privileged.user.setup defaults no/no/auth_admin — system_files/shared/usr/share/polkit-1/actions/org.ublue.privileged.user.setup.policy - #1108

Open
kubestellar-hive[bot] wants to merge 2 commits into
mainfrom
sec/fix-polkit-priv-setup-defaults
Open

kubestellar-hive[bot] wants to merge 2 commits into
mainfrom
sec/fix-polkit-priv-setup-defaults

Conversation

@kubestellar-hive

Copy link
Copy Markdown
Contributor

Security Fix

Ground claimed (hold-gated coordination): system_files/shared/usr/share/polkit-1/actions/org.ublue.privileged.user.setup.policy only. Disjoint from common#932 (etc/sudoers.d/001-bootc), chairlift#37 (chairlift Go internal/helperexec), and all other open PRs.

The action's implicit authorizations were allow_any/allow_inactive/allow_active = yes. Because 20-privileged-user-setup.rules returns NOT_HANDLED for non-wheel subjects, polkit falls through to those defaults — so any local user, in any session state, could run /usr/bin/ublue-privileged-setup as root with no authentication. The wheel-only restriction was never effective.

Change: defaults become no / no / auth_admin. Wheel members keep passwordless access via the unchanged rules.d rule; all other subjects are denied unless an admin authenticates. Matches the hardening already used by the sibling org.frostyard.ChairLift.bootc.policy in the same directory. XML well-formedness verified.

Closes #1107


Filed by sec-check agent (ACMM L4/L5 — hold-gated mode). Hold-gated: human review required.

— hive: agent=sec-check backend=copilot model=kimi-k3 copilot=1.0.78

allow_any/allow_inactive/allow_active=yes made the wheel-only gate in
20-privileged-user-setup.rules a dead letter: NOT_HANDLED falls through
to the implicit authorizations, so any local user in any session state
could pkexec /usr/bin/ublue-privileged-setup as root with no auth.

Set defaults to no/no/auth_admin. Wheel keeps passwordless access via
the existing rules.d rule; everyone else is denied unless an admin
authenticates. Matches the sibling org.frostyard.ChairLift.bootc.policy
hardening shipped in the same directory.

Closes #1107

Signed-off-by: sec-check[bot] <sec-check[bot]@users.noreply.github.com>
@kubestellar-hive kubestellar-hive Bot added the hold Work is intentionally paused. label Sep 12, 2026
@kubestellar-hive kubestellar-hive Bot added security Security impact or hardening work. agent/security Filed or owned by the security agent. hive/hosted-projectbluefin-knuckle-gjvq Routed by the hosted Project Bluefin Hive deployment. labels Sep 12, 2026
@kubestellar-hive kubestellar-hive Bot changed the title [sec-check] fix: polkit org.ublue.privileged.user.setup defaults no/no/auth_admin — system_files/shared/usr/share/polkit-1/actions/org.ublue.privileged.user.setup.policy fix: polkit org.ublue.privileged.user.setup defaults no/no/auth_admin — system_files/shared/usr/share/polkit-1/actions/org.ublue.privileged.user.setup.policy Sep 17, 2026
@kubestellar-hive kubestellar-hive Bot added the agent/scanner Filed or owned by the scanner agent. label Sep 17, 2026

@Danathar Danathar left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The finding in #1107 is real — today any local user can run ublue-privileged-setup as root without authenticating — but this change would ship a login regression to every image, so it can't land as-is.

Who calls this, and when. system_files/shared/usr/share/ublue-os/user-setup.hooks.d/99-privileged.sh in bluefin, utah and bluefin-lts is:

pkexec /usr/bin/ublue-privileged-setup

and ublue-user-setup.service is a user unit WantedBy=graphical-session.target, so that pkexec runs for every user at every graphical login.

The wheel rule never matches — not just for non-wheel users. 20-privileged-user-setup.rules checks

action.id == "org.freedesktop.policykit.exec" && action.lookup("program") == "/usr/bin/ublue-privileged-setup"

But the policy annotates org.freedesktop.policykit.exec.path, and when pkexec finds an action whose exec.path matches the program it authorizes against that action id — org.ublue.policykit.privileged.user.setup — not org.freedesktop.policykit.exec. So the rule returns NOT_HANDLED for everyone, wheel members included, and the <defaults> decide. Today the defaults are yes, which is why nobody has noticed.

What this PR does on a real system. With allow_active = auth_admin and a rule that never says YES, every graphical login — including the admin's — pops a polkit authentication dialog for ublue-privileged-setup. Dismiss it and the hook fails (harmless, hookrunner.sh keeps going), but the dialog comes back next login. That's the "auth prompt at every login" class of bug.

What it should be (still one small PR, but three files):

  1. Fix the rule so it can actually match: action.id == "org.ublue.policykit.privileged.user.setup" (keep the program check as belt-and-braces), returning YES for wheel.
  2. Then tighten the defaults — but to no/no/no, not auth_admin. Non-wheel users have no business being prompted for an admin password at login; they should be denied silently and the hook should skip.
  3. Make 99-privileged.sh (in the consuming repos — it's a hook, not in common) check id -nG | grep -qw wheel before calling pkexec, so non-admin users don't even hit polkit.

And please verify on a VM login, wheel and non-wheel, before merging — this is exactly the kind of change the composed E2E doesn't exercise.

The XML change itself is well-formed; it's the surrounding mechanism that's wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent/scanner Filed or owned by the scanner agent. agent/security Filed or owned by the security agent. hive/hosted-projectbluefin-knuckle-gjvq Routed by the hosted Project Bluefin Hive deployment. hold Work is intentionally paused. security Security impact or hardening work.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[sec-check] org.ublue.privileged.user.setup.policy defaults (allow_any/inactive/active=yes) nullify the wheel-only rules.d gate

1 participant