Files
finit/test/dbus-cond.sh
T
Joachim Wiberg 3231ade38a 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>
2026-08-16 08:57:38 +02:00

88 lines
3.2 KiB
Bash
Executable File

#!/bin/sh
# libink: org.finit.Cond1 vtable + ConditionChanged signal.
#
# Covers user-condition manipulation: Get, Set (with signal fan-out),
# the usr/* policy guard (non-usr conditions are rejected), and the
# non-root authorization gate.
set -eu
TEST_DIR=$(dirname "$0")
# shellcheck source=/dev/null
. "$TEST_DIR/lib/setup.sh"
# shellcheck source=/dev/null
. "$TEST_DIR/lib/dbus-setup.sh"
say "Cond1.Get returns 'off' for an unset condition"
result=$(texec "$CLIENT" call-s "$BUS" /org/finit/cond \
org.finit.Cond1 Get "no-such-cond")
case "$result" in
OK*) : ;; # ok, the cond reports a state, fall through
*) fail "Cond1.Get failed: $result" ;;
esac
say "Cond1.Set fires Cond1.ConditionChanged and Get reflects the change"
rm -f /tmp/dbus-cond.out
( texec "$CLIENT" monitor-signal "$BUS" \
"type='signal',interface='org.finit.Cond1',member='ConditionChanged'" \
5000 > /tmp/dbus-cond.out 2>&1 ) &
cond_mon_pid=$!
sleep 0.5
texec "$CLIENT" call-s "$BUS" /org/finit/cond \
org.finit.Cond1 Set "dbus-test-cond" >/dev/null \
|| fail "Cond1.Set returned non-zero"
set +e
wait "$cond_mon_pid"
cond_mon_rc=$?
set -e
assert "Cond1 monitor saw a signal (rc=$cond_mon_rc)" "$cond_mon_rc" -eq 0
case "$(cat /tmp/dbus-cond.out)" in
*"SIGNAL org.finit.Cond1 ConditionChanged"*"usr/dbus-test-cond"*on*)
assert "ConditionChanged carries usr/dbus-test-cond and 'on'" 0 -eq 0 ;;
*)
fail "Unexpected Cond1 signal: $(cat /tmp/dbus-cond.out)" ;;
esac
say "Cond1.Set/Clear on non-usr/* is rejected"
set +e
texec "$CLIENT" call-s "$BUS" /org/finit/cond \
org.finit.Cond1 Set "pid/sshd" >/tmp/dbus-condrej.out 2>&1
condrej_rc=$?
set -e
assert "pid/* rejected (rc=$condrej_rc)" "$condrej_rc" -eq 1
case "$(cat /tmp/dbus-condrej.out)" in
*InvalidArgs*) assert "Error is InvalidArgs" 0 -eq 0 ;;
*) fail "Unexpected reply: $(cat /tmp/dbus-condrej.out)" ;;
esac
# See dbus-manager.sh: widen the test socket so a non-member reaches
# the method-level authz behind the 0660 gate.
bus_open_to_all
say "Cond1.Set from non-root is rejected with AccessDenied"
set +e
texec "$CLIENT" call-s-as-uid 1 "$BUS" /org/finit/cond \
org.finit.Cond1 Set "would-be-cond" >/tmp/dbus-condauthz.out 2>&1
ca_rc=$?
set -e
assert "Non-root Cond1.Set rejected (rc=$ca_rc)" "$ca_rc" -eq 1
case "$(cat /tmp/dbus-condauthz.out)" in
*AccessDenied*) assert "Cond1 authz fires" 0 -eq 0 ;;
*) fail "Unexpected reply: $(cat /tmp/dbus-condauthz.out)" ;;
esac
# initctl's own error path: a non-usr name comes back InvalidArgs from
# the server, which drove cond_dbus_call to read the error reply after
# freeing the client (a use-after-free ASan catches on this build).
# Exercise it here so the fix stays fixed.
say "initctl cond set of a rejected name reports cleanly, no use-after-free"
set +e
out=$(texec sh -c 'initctl cond set aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 2>&1)
ic_rc=$?
set -e
assert "initctl reported the rejection (rc=$ic_rc)" "$ic_rc" -ne 0
case "$out" in
*[Ff]ailed*|*InvalidArgs*|*restricted*) assert "error surfaced, not a crash" 0 -eq 0 ;;
*) fail "Unexpected initctl output: $out" ;;
esac