-
Notifications
You must be signed in to change notification settings - Fork 0
Polkit Agent
sentinel-polkit-agent is the per-session D-Bus service that registers
with polkit so polkit consults Sentinel for graphical privilege
escalation (pkexec, polkit-mediated D-Bus actions, etc). It ships
since v0.4.0.
[App requests privileged action]
│
▼ D-Bus
[polkitd]
│ needs authentication for action_id with cookie
│
▼ D-Bus
[sentinel-polkit-agent] ← XDG-autostarted by the compositor
│ 1. BeginAuthentication(action_id, cookie, identities)
│ 2. spawn sentinel-helper → user clicks Allow / Deny / timeout
│ 3. on Allow: enqueue {action_id, expires_at = now + 5s} in approval queue
│ 4. connect to /run/polkit/agent-helper.socket → systemd spawns
│ polkit-agent-helper@<n>.service running helper-1 as root
│ 5. send "<user>\n<cookie>\n"
│
▼ socket
[polkit-agent-helper-1] ← spawned by systemd, runs PAM
│ pam_authenticate(/etc/pam.d/polkit-1)
│
▼
[pam_sentinel.so] ← in-process, dlopen'd by helper-1
│ 1. resolve PAM_USER → uid via passwd
│ 2. connect to $XDG_RUNTIME_DIR/sentinel-agent.sock
│ 3. read response: "OK\n" or "NO\n"
│ 4. OK → return PAM_SUCCESS (no recursive dialog)
│ NO → return PAM_IGNORE (stack falls through to password prompt)
The agent's accept loop verifies each connection via SO_PEERCRED:
peer uid must be 0 (root), peer comm must start with
polkit-agent-h… (the kernel-truncated form of
polkit-agent-helper-1). On match it pops one non-expired approval
from the queue and writes "OK"; otherwise "NO".
| Threat | Mitigation |
|---|---|
| Same-uid attacker enqueues a fake approval | Out of scope: same uid can drive polkit directly. |
| Same-uid attacker spoofs the agent socket | Agent unlinks any pre-existing socket on startup; XDG autostart wins the bind race vs. interactive sessions. |
| Cross-uid attacker connects | Socket mode 0600, owner = the user. Other unix users can't connect. |
Spoofed polkit-agent-helper-1
|
Helper-1 only validates the polkit cookie via a privileged D-Bus channel back to polkitd. A spoofed helper can't supply a real cookie. |
| Replay across actions | Approvals are one-shot (popped on first read) and short-lived (5s TTL). |
Modern systemd-based distros run helper-1 inside
polkit-agent-helper@.service with an aggressive sandbox
(ProtectHome=yes, PrivateDevices, RestrictAddressFamilies=AF_UNIX,
MemoryDenyWriteExecute=yes, SystemCallFilter=@system-service).
ProtectHome=yes masks /run/user/<uid> inside the sandbox — which
would hide our bypass socket. Sentinel ships a unit drop-in at
/etc/systemd/system/polkit-agent-helper@.service.d/sentinel.conf
that disables ProtectHome for this unit. The other sandbox flags stay
in place; helper-1 doesn't need anything in $HOME.
pam_sentinel.so is installed mode 0755 (not 0644) — the same
sandbox refuses to dlopen .so files without the execute bit.
After install, log out and back in (the XDG autostart entry is only processed at session start), then:
pgrep -af sentinel-polkit-agent
ls -la $XDG_RUNTIME_DIR/sentinel-agent.sock
pkexec true # exactly one Sentinel dialog
journalctl -t sentinel-polkit-agent -f # live agent log
journalctl -t pam_sentinel -f # live PAM logThe PAM log line on success is agent_bypass: approval accepted (uid 1000).
sudo rm /etc/xdg/autostart/sentinel-polkit-agent.desktop
sudo rm /etc/systemd/system/polkit-agent-helper@.service.d/sentinel.conf
sudo systemctl daemon-reload
# log out + log back inpam_sentinel.so keeps gating sudo (if you wired it) — only the
polkit-side bypass goes back to the next module in the PAM stack.
polkit's RegisterAuthenticationAgent checks that the agent's process
is in the same logind session as the subject we're registering for.
That session is derived from the kernel sessionid (set by
audit_setloginuid at login, inherited through forks).
Compositors fork XDG autostart entries as direct children, so the
agent inherits the graphical sessionid. Processes spawned via
systemctl --user start are children of user@.service — a
different sessionid (the manager session, not the graphical one) —
and polkit refuses the registration with "Passed session and the
session the caller is in differs."
Other working polkit agents (polkit-gnome, polkit-kde) ship the same way. Sentinel follows the convention.
GPL-3.0-or-later. Provided as-is, without warranty of any kind (GPL §15-16). Use at your own risk on production systems.