mirror of
https://github.com/troglobit/finit.git
synced 2026-10-25 12:04:36 +00:00
libink/dbus: identify the caller behind a broker
On the local bus SO_PEERCRED says who is calling and the kernel is the one saying it. Behind a broker one connection carries every caller, so that credential describes dbus-daemon and nothing else, and every privileged method was refused there, root included. Ask the bus driver instead. libink parks the call and hands us the sender; we ask GetConnectionUnixUser and answer when the reply lands, through the same event loop as everything else. Nothing blocks: blocking in PID 1 is why libuEv exists. That needs calls libink can make on a connection it already has, so it gained those too. Answers are cached, since a bus never reuses a unique name while it runs. Not across a restart though: a new dbus-daemon numbers from scratch and :1.7 becomes somebody else, so the cache goes when the broker does. A sender name too long to key on is refused rather than truncated, two callers sharing a truncated key would share an identity. Privilege is no longer uid 0 alone. The socket is already owned by the --with-group group, so refusing its members every method that changes anything left a wheel user able to open the bus and unable to reboot. Both gates now say the same thing. Group membership needs NSS, which the C library loads with dlopen(), so the lookup is compiled out where Finit is built to link statically. That leaves such a build root-only, which is worth saying out loud rather than leaving to be discovered. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
1 parent
d710a23513
commit
63aabaa6df
17 files changed
+774
-90
No files matched your search
+2
-1
@@ -44,7 +44,8 @@ Below are a few of the main switches to configure:
|
||||
|
||||
* `--enable-static`: Build Finit statically. The plugins will be
|
||||
built-ins (.o files) and all external libraries, except the C library
|
||||
will be linked statically.
|
||||
will be linked statically. Privileged D-Bus methods then accept only
|
||||
`root`, see [Authorization](dbus.md#authorization)
|
||||
|
||||
* `--enable-kernel-cmdline`: Enable Finit pre-4.1 parsing of init args from
|
||||
`/proc/cmdline`, this is *not recommended* since Finit may be running as the
|
||||
|
||||
+27
-10
@@ -204,20 +204,37 @@ conditions belong to Finit's state machine.
|
||||
Authorization
|
||||
-------------
|
||||
|
||||
Privileged methods reject any caller whose peer `uid` isn't 0. On the
|
||||
**local** bus the kernel's `SO_PEERCRED` socket option tells Finit exactly
|
||||
who's calling, so privilege escalation through the bus is impossible.
|
||||
Privileged methods accept `root`, and any caller belonging to the group given
|
||||
to `--with-group` at build time. That is the same set the socket mode already
|
||||
admits, so the two gates agree instead of the socket letting the group in and
|
||||
every method turning it away.
|
||||
|
||||
On the **system** bus, all incoming traffic is treated as unprivileged: it
|
||||
arrives through `dbus-daemon` (typically running as root) and Finit cannot yet
|
||||
ask the daemon for the real requester's uid via `GetConnectionUnixUser`. This
|
||||
means external tooling can freely `Get`/`Introspect`/`ListServices`, but every
|
||||
state-changing method returns `org.freedesktop.DBus.Error.AccessDenied`.
|
||||
Per-sender uid lookup is on the roadmap.
|
||||
A build configured with `--enable-static` accepts only `root`. Group
|
||||
membership comes from NSS, which the C library loads with `dlopen()`, and a
|
||||
build meant to link statically cannot count on that, so the lookup is compiled
|
||||
out rather than left to fail open. The socket mode is unchanged, so the group
|
||||
can still connect, it just cannot invoke a privileged method.
|
||||
|
||||
On the **local** bus the kernel decides this at `connect()`, supplementary
|
||||
groups included, and `SO_PEERCRED` tells Finit exactly who is calling, so
|
||||
privilege escalation through the bus is impossible.
|
||||
|
||||
On the **system** bus one connection carries every caller, so `SO_PEERCRED`
|
||||
describes `dbus-daemon` rather than whoever asked. For a privileged method
|
||||
Finit asks the bus driver `GetConnectionUnixUser` about the message sender and
|
||||
holds the call until the answer arrives. Nothing blocks: the reply comes back
|
||||
through the same event loop as everything else, and the held call is then
|
||||
dispatched or refused on its merits.
|
||||
|
||||
Answers are cached per sender. A bus never reuses a unique name while it
|
||||
runs, so an answer holds for as long as that bus does; Finit empties the cache
|
||||
when the broker goes away, since a new one numbers its clients from scratch.
|
||||
A caller Finit cannot identify is refused, so the failure mode is a denial
|
||||
rather than an escalation.
|
||||
|
||||
When a privileged method is rejected the error name is exactly
|
||||
`org.freedesktop.DBus.Error.AccessDenied`, and the body carries a short reason
|
||||
string (e.g. `"permission denied: Start requires root"`).
|
||||
string.
|
||||
|
||||
`initctl` integration
|
||||
---------------------
|
||||
|
||||
Reference in new issue
Block a user