Reference · Decisions
0037: Who may show a sheet
Proposed: only the shell the system service started for your session may show and answer approval sheets, not any process that runs the same file.
Status: proposed. Builds on 0020 (identity from the kernel) and 0033 (greet first). Audit P6-1 (S6).
On this page
Context
wold answers "who may show the account's asks and answer them" (subscribe account, session.answer,
Need::Shower) by the executable of the peer: /usr/bin/wol-shell (portal::SHOWERS). Any process
of the account can run that file. With LD_PRELOAD=evil.so /usr/bin/wol-shell, or by tracing its own
child before the exec, it passes the check, receives every SessionAsk of the account and answers
"Allow once" for a sheet such as SystemUpdate, which wold then runs as root with nobody at the
computer. The update is signed and newer, so integrity holds; consent does not.
A path is not an identity. Identity by inheritance is: the shower is the process wold's session started, and it holds something only that process holds.
Landed first (defence in depth, no new moving part)
PeerIdentity::injected (crates/api/src/peer.rs), checked in caller() for the executables in
SHOWERS only, refuses a peer whose pinned /proc/<pid>:
- has
LD_*,GTK_MODULES,GTK3_MODULES,GTK_PATH,GIO_EXTRA_MODULES,GDK_PIXBUF_MODULE_FILE,GST_PLUGIN_*,QT_PLUGIN_PATHorGSETTINGS_SCHEMA_DIRin its environment (or an empty one); - maps executable code from outside
/usr,/lib,/lib64(a library of the account's, a memfd: this also catches a preload that wiped its ownenviron); - has a tracer (
TracerPid), which survives an exec; - lives in another mount namespace than wold (a private
/usroverlay); - or cannot be read at all (unknown is refused).
Not done: a parent check. The compositor starts
exec-onceprograms by a double fork, so the legitimate shell is an orphan whose parent is init, exactly like one an attacker double-forks. This raises the price; it is not the identity. It does not stop a process that gets code into the real shell after it started (ptrace needs the shell to be a descendant underptrace_scope=1; making the shell non-dumpable closes that and is a one-line change in the shell, see Consequences). A user whose profile exportsLD_LIBRARY_PATHgets no notifications until the shell is started clean.
Decision (proposed: the token)
wol-login(root; it starts the session) draws 256 random bits per session, sends them to wold (session.token {uid, token}, accepted from root only) and keeps the descriptor of a memfd that holds them, sealed.- The memfd is inherited down to
wol-session(clear close-on-exec across the setuid/exec only for this chain).wol-sessionforks the shell itself, before it execsstart-hyprland, passes it the memfd (close-on-exec cleared in that child only) and sets close-on-exec in its own image, so the compositor and every app started under it never hold it. The shell waits for the compositor socket (it already retries), and wol-session restarts it if it dies. - The shell presents the token as the argument of
subscribe accountand ofsession.answer(never in an environment or argv); wold compares in constant time, per uid, and forgets it when the session ends. A mismatch is refused like a wrong executable. The path check stays too. - The shell sets
PR_SET_DUMPABLE=0first thing, so no process of the account can ptrace it or read its memory or descriptors (pidfd_getfd).
Options not taken
- A systemd user unit and a cgroup check: the account owns
~/.config/systemd/user, whose units override the vendor's; it proves nothing against the account. - A different uid for the shell: it is a GTK client of the account's compositor and files; too big.
ptrace_scope=2or 3: a lead call (breaks debuggers and Wine tools), and not sufficient alone.
Consequences if accepted
- The compositor
exec-onceline forwol-shellgoes;wol-sessionowns the shell's lifetime (restart, crash log). Tests (desktop/overlay, golden journeys) that start the shell from the compositor change. - A second login of the same account (fast user switching) has its own token per session.
- Residual, accepted: code the real shell loads by its own plug-in path (themes, plugins) runs with
its token. Plugins are
[[hypr]]/Lua with allow-lists (plugins.md), not libraries.
Related
- Wolkit: the UI kitViews from signals, components, the interface guideline parts, a navigator, services and a test harness, in one Crystal shard.
- Plugin Maker and Theme StudioMake a plugin or a theme and see it live in a sandbox, then package it for the plugin index.
- SecurityFirewall, hardening, daily update checks, a first-use malware check and disk encryption.
- libwol: the app APIOne stable C ABI for apps in any language: windows, menus, notifications, files, power and more.
- Optional modulesAI, developer, creative and console stacks, installed after the base and removable.
From the developer documentation, wonderlandos/wolos docs/adr/0037-shower-identity.md.