mirror of
https://github.com/troglobit/finit.git
synced 2026-10-30 12:50:34 +00:00
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:
1 parent
e62b463852
commit
3231ade38a
24 files changed
+266
-108
No files matched your search
@@ -29,6 +29,10 @@ case "$allowed" in
|
||||
*) fail "Unexpected reply for group member: $allowed" ;;
|
||||
esac
|
||||
|
||||
# uid 2 is outside the group, so 0660 would stop it at connect; widen
|
||||
# the test socket so the refusal comes from the method, which is the
|
||||
# layer under test here.
|
||||
bus_open_to_all
|
||||
say "A caller outside the group may not"
|
||||
set +e
|
||||
denied=$(texec "$CLIENT" call-s-as-uid 2 "$BUS" /org/finit/manager \
|
||||
|
||||
@@ -56,6 +56,9 @@ case "$(cat /tmp/dbus-condrej.out)" in
|
||||
*) 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 \
|
||||
@@ -67,3 +70,18 @@ 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
|
||||
@@ -39,6 +39,10 @@ case "$(cat /tmp/dbus-stop.out)" in
|
||||
*) fail "Unexpected error reply: $(cat /tmp/dbus-stop.out)" ;;
|
||||
esac
|
||||
|
||||
# Non-root callers here are outside the --with-group group; widen the
|
||||
# test socket so they reach the per-method authz rather than being
|
||||
# stopped at connect by the 0660 mode.
|
||||
bus_open_to_all
|
||||
say "Manager1.Restart from non-root is rejected with AccessDenied"
|
||||
set +e
|
||||
texec "$CLIENT" call-s-as-uid 1 "$BUS" /org/finit/manager \
|
||||
|
||||
@@ -57,6 +57,9 @@ texec "$CLIENT" call-void "$BUS" /org/finit/service/keventd \
|
||||
|| fail "Service1.Restart returned non-zero"
|
||||
assert "Per-service Restart ok" 0 -eq 0
|
||||
|
||||
# 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 "Service1.Restart from non-root is rejected with AccessDenied"
|
||||
set +e
|
||||
texec "$CLIENT" call-void-as-uid 1 "$BUS" /org/finit/service/keventd \
|
||||
|
||||
@@ -18,3 +18,15 @@ fi
|
||||
|
||||
say "Wait for $BUS to appear"
|
||||
retry "texec test -S $BUS"
|
||||
|
||||
# The installed bus stays 0660 (root + the --with-group group), so a
|
||||
# caller outside that group is stopped by the socket before any method
|
||||
# runs. A few tests need to reach the per-method authorization behind
|
||||
# that gate -- to prove a non-member is refused there too, not only at
|
||||
# connect -- so they widen the *test* socket to 0666 first. Real
|
||||
# deployments keep 0660; this only touches the runtime socket in the
|
||||
# test namespace.
|
||||
bus_open_to_all()
|
||||
{
|
||||
texec chmod 0666 "$BUS"
|
||||
}
|
||||
@@ -4,3 +4,4 @@
|
||||
/.deps/
|
||||
/serv
|
||||
/dbus-auth-client
|
||||
/fuzz-msg-parse
|
||||
@@ -42,6 +42,8 @@
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -52,6 +54,17 @@
|
||||
|
||||
#include "link.h"
|
||||
|
||||
/* The -as-uid modes drop all privileges before exiting, and LeakSanitizer
|
||||
* cannot run under that (it needs ptrace on itself). Turn leak detection
|
||||
* off for this test client via ASan's startup hook; it is a short-lived
|
||||
* tool, and finit -- the process whose leaks actually matter -- is checked
|
||||
* on its own. Harmless when the build has no sanitizer. */
|
||||
const char *__asan_default_options(void);
|
||||
const char *__asan_default_options(void)
|
||||
{
|
||||
return "detect_leaks=0";
|
||||
}
|
||||
|
||||
/* ---------- manual SASL: only mode_auth uses this ---------- */
|
||||
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
@@ -153,9 +166,15 @@ static int report_rc(link_client_t *c, int rc)
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Drop effective uid to argv-supplied value (decimal). */
|
||||
/* Become the argv-supplied uid (decimal), the way a login does: reset
|
||||
* the group set from the target user's /etc/group entry before setuid,
|
||||
* so the connection carries that user's real credentials. A bare
|
||||
* setuid() would keep root's groups, and SO_PEERGROUPS would then hand
|
||||
* the server a caller who still holds root's privileges -- masking the
|
||||
* very authorization the -as-uid tests check. */
|
||||
static int drop_uid(const char *uid_arg, const char *progname)
|
||||
{
|
||||
struct passwd *pw;
|
||||
char *ep = NULL;
|
||||
long v;
|
||||
|
||||
@@ -165,6 +184,22 @@ static int drop_uid(const char *uid_arg, const char *progname)
|
||||
fprintf(stderr, "%s: bad uid: %s\n", progname, uid_arg);
|
||||
return 2;
|
||||
}
|
||||
|
||||
pw = getpwuid((uid_t)v);
|
||||
if (pw) {
|
||||
if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
|
||||
perror("initgroups");
|
||||
return 2;
|
||||
}
|
||||
if (setgid(pw->pw_gid) < 0) {
|
||||
perror("setgid");
|
||||
return 2;
|
||||
}
|
||||
} else if (setgroups(0, NULL) < 0) { /* no entry: drop all groups */
|
||||
perror("setgroups");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (setuid((uid_t)v) < 0) {
|
||||
perror("setuid");
|
||||
return 2;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "internal.h"
|
||||
|
||||
#define FRAME_MAX 512
|
||||
#define HDR_FIXED_SIZE 16 /* must agree with proto.c */
|
||||
#define HDR_FIXED_SIZE LINK_HDR_FIXED_SIZE /* from proto.h, no drift */
|
||||
|
||||
static void fail(const char *what, size_t size)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user