dbus: fixes from a code review of the branch

A pass over the whole branch before merge, mostly in libink since
that is the new code and the part exposed to the wire.  Grouped here
rather than scattered so the review is easy to read in one place.

libink parser and dispatch:

 - Bound reader lengths so a 32-bit size_t can't wrap a wire length
   past the guard and read out of bounds.  Reachable pre-auth on any
   bus, so it matters on the 32-bit targets Finit runs on.
 - Drop a peer when a reply send fails instead of limping on with a
   half-written frame; a built-in whose send failed used to fall
   through and put a second frame on the wire.

initctl:

 - Copy a D-Bus error name out of the reply before closing the client;
   the reply points into memory the close frees.  Both error paths now
   share one helper so this can't creep back.

Authorization:

 - Take the caller's groups from the kernel (SO_PEERCRED plus
   SO_PEERGROUPS) rather than getpwuid()/getgrouplist(), which go
   through NSS and can block PID 1 on a slow LDAP or SSSD backend.
   The check is now a lookup against the group resolved once at init,
   with no NSS and no 256 KiB array on the stack.  A caller reaching
   us through a broker carries no group set, so system-bus privileged
   methods are root-only; the local bus keeps group support.  See
   libink/README.md for the note on lifting that.

Shutdown:

 - Call dbus_exit() from the shutdown path so the server, its peers,
   and the socket are let go cleanly.  The teardown existed but nobody
   called it.

Tests, CI, docs:

 - A fuzz target for the message parser, run as a quick sweep in the
   suite and properly under libFuzzer in CI, with the corpus carried
   between runs.  The -as-uid tests drop groups the way a login does
   so SO_PEERGROUPS sees the right set, and widen the test socket to
   reach the per-method check behind the 0660 gate.  Bring the GitHub
   actions up to versions that run on Node 24, and tidy a few small
   things a /simplify pass turned up.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg committed 2026-08-16 08:57:38 +02:00
1 parent e62b463852
commit 3231ade38a
24 files changed
+266 -108

No files matched your search

+7 -2
View File
@@ -56,8 +56,13 @@ All relevant changes are documented in this file.
`List`, `Dump` for [user-defined conditions](conditions.md),
with a `ConditionChanged (ss)` signal.
Privileged methods reject non-root callers based on the kernel-
authenticated peer uid (`SO_PEERCRED`); read-only methods are open.
Privileged methods accept root and members of the `--with-group`
group; everyone else is refused. On the local bus the caller's uid
and groups come straight from the kernel (`SO_PEERCRED` +
`SO_PEERGROUPS`), so the check never blocks PID 1 on an NSS lookup.
Over the system bus the uid is resolved from the broker
(`GetConnectionUnixUser`) and privileged methods are root-only.
Read-only methods are open.
See [D-Bus Integration](dbus.md) for the full surface, build flag,
and `dbus-send`/`dbus-monitor` examples.
+1 -2
View File
@@ -44,8 +44,7 @@ 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. Privileged D-Bus methods then accept only
`root`, see [Authorization](dbus.md#authorization)
will be linked statically.
* `--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
+14 -14
View File
@@ -209,22 +209,22 @@ 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.
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 **local** bus the kernel settles this at `connect()`: `SO_PEERCRED`
gives Finit the caller's uid and `SO_PEERGROUPS` its group set, both straight
from the kernel. Finit matches the group against `--with-group` itself, so
the check never touches NSS -- `getpwuid`/`getgrouplist` can block on a slow
LDAP or SSSD backend, and PID 1 must never block. Because the group set is
the caller's real credentials, 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.
describes `dbus-daemon` rather than whoever asked. 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. The broker does not report the caller's groups in
that reply, so **privileged methods over the system bus are root-only**; group
membership is honoured on the local bus only. Lifting that is future work,
see `libink/README.md`.
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