diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cec7aeab..37c56b74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: run: | ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ --enable-x11-common-plugin --enable-testserv-plugin --with-watchdog \ - --with-keventd --with-libsystemd \ + --with-keventd \ CFLAGS="-fsanitize=address -ggdb" make -j9 clean make -j9 V=1 diff --git a/test/Makefile.am b/test/Makefile.am index a06d9d20..6e8cfd1a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = skel/bin/busybox-x86_64.sha256 skel/sbin/service.sh skel/etc/env skel/etc/rcS.d/S01abc.sh skel/etc/rcS.d/S02def.sh \ skel/cdrom/.empty skel/dev/shm/.empty skel/dev/pts/.empty \ skel/etc/inittab skel/etc/hostname skel/etc/fstab \ + skel/usr/share/dbus-1/system.conf skel/etc/machine-id \ skel/etc/passwd skel/etc/group skel/etc/ld.so.conf \ skel/etc/init.d/rcS skel/etc/init.d/rcK skel/tmp/.empty \ skel/etc/finit.d/.empty skel/etc/finit.d/available/.empty \ @@ -73,6 +74,7 @@ EXTRA_DIST += testserv.sh EXTRA_DIST += unexpected-restart.sh EXTRA_DIST += dbus-auth.sh EXTRA_DIST += dbus-authz.sh +EXTRA_DIST += dbus-broker.sh EXTRA_DIST += dbus-bus.sh EXTRA_DIST += dbus-manager.sh EXTRA_DIST += dbus-service.sh @@ -143,6 +145,10 @@ TESTS += dbus-service.sh TESTS += dbus-cond.sh TESTS += dbus-initctl.sh TESTS += dbus-introspect.sh +# Needs the plugin to bring up the bus, not just the built-in one +if BUILD_DBUS_PLUGIN +TESTS += dbus-broker.sh +endif endif check-recursive: setup-chroot diff --git a/test/check.sh b/test/check.sh index c12a2614..e76d4af7 100755 --- a/test/check.sh +++ b/test/check.sh @@ -26,7 +26,7 @@ fi ./configure --prefix=/usr --exec-prefix= --sysconfdir=/etc --localstatedir=/var \ --enable-dbus --enable-x11-common-plugin --enable-testserv-plugin \ - --with-watchdog --with-keventd --with-libsystemd \ + --with-watchdog --with-keventd \ CFLAGS='-fsanitize=address -ggdb' if [ "$run_make" -eq 1 ]; then diff --git a/test/dbus-broker.sh b/test/dbus-broker.sh new file mode 100755 index 00000000..e82897f5 --- /dev/null +++ b/test/dbus-broker.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Finit against a real message bus. +# +# Every other dbus-*.sh test drives libink's own client, so the wire +# format is only ever checked against the implementation that produced +# it. Here the dbus plugin brings up a real dbus-daemon, Finit finds +# it and claims org.finit, and then dbus-send talks to Finit: a client +# that shares no code with us. +# +# It also covers the only path the brokerless bus cannot reach, where +# one connection carries every caller and Finit has to ask the broker +# who sent each privileged call. +# +# Skipped when the host has no dbus-daemon or dbus-send to stage. + +set -eu + +TEST_DIR=$(dirname "$0") + +# shellcheck source=/dev/null +. "$TEST_DIR/lib/setup.sh" + +# Staged by lib/sysroot.mk from the host, when it has them. +for prog in dbus-daemon dbus-send; do + texec sh -c "command -v $prog >/dev/null" \ + || skip "no $prog in the test root, need it on the host" +done + +say 'The dbus plugin started a system bus' +retry 'assert_file_exists /var/run/dbus/system_bus_socket' + +say 'Finit claims org.finit on the system bus' +retry "texec dbus-send --system --print-reply --dest=org.freedesktop.DBus \ + /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.finit" 60 0.5 + +say 'A read-only method answers through the broker' +list=$(texec dbus-send --system --print-reply --dest=org.finit \ + /org/finit/manager org.finit.Manager1.ListServices) +assert "ListServices returned the dbus service" \ + "$(printf '%s' "$list" | grep -c '"dbus"')" -ge 1 + +say 'Properties.Get answers through the broker' +rl=$(texec dbus-send --system --print-reply --dest=org.finit \ + /org/finit/manager org.freedesktop.DBus.Properties.Get \ + string:org.finit.Manager1 string:Runlevel) +assert "Runlevel property is 2" "$(printf '%s' "$rl" | grep -c '"2"')" -eq 1 + +# The point of the exercise: a privileged call over the broker means +# Finit parks it, asks the driver who the sender is, and dispatches on +# the answer. Root is allowed, so reaching NoSuchService proves the +# whole round trip worked rather than a blanket denial. The repeat +# call goes the same way, only answered from the sender cache. +for pass in first repeat; do + say "A privileged method resolves the caller, $pass call" + priv=$(texec dbus-send --system --print-reply --dest=org.finit \ + /org/finit/manager org.finit.Manager1.Restart string:nosuchservice 2>&1 || true) + case "$priv" in + *NoSuchService*) assert "Caller identified on the $pass call" 0 -eq 0 ;; + *) fail "Unexpected reply to the $pass privileged call: $priv" ;; + esac +done diff --git a/test/lib/sysroot.mk b/test/lib/sysroot.mk index ea2d8c7c..ae71e7f2 100644 --- a/test/lib/sysroot.mk +++ b/test/lib/sysroot.mk @@ -36,11 +36,23 @@ BBURL ?= $(BBHOME)/$(BBVER)/$(BBBIN) # glibc dlopen()s NSS modules at runtime, so ldd does not list them, but # without libnss_files getpwnam() cannot resolve users inside the chroot -_libs_nss = $(firstword $(wildcard /lib/$(ARCH)-linux-gnu/libnss_files.so.2 \ +_libs_nss := $(firstword $(wildcard /lib/$(ARCH)-linux-gnu/libnss_files.so.2 \ /usr/lib/$(ARCH)-linux-gnu/libnss_files.so.2 \ /lib64/libnss_files.so.2 /lib/libnss_files.so.2)) -_libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+') $(_libs_nss) -libs = $(foreach path,$(_libs_src),$(abspath $(DEST))$(path)) +# A real broker and a real client, staged when the host has them, so +# one test can check Finit against dbus-daemon instead of only against +# libink's own client. Absent is fine, dbus-broker.sh skips. +dbus_bins := $(foreach b,dbus-daemon dbus-send,$(firstword $(wildcard /usr/bin/$(b) /bin/$(b)))) +# Given several binaries ldd prefixes each with a 'path:' header, and +# that trailing colon would land in a make target. Excluding it here +# is enough, no need for one ldd per binary. +_libs_dbus := $(if $(dbus_bins),$(shell ldd $(dbus_bins) | grep -Eo '/[^ :]+')) + +# The binaries stage exactly like the libraries: same host path, same +# path under DEST, copied by the rule below. +_libs_src := $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+') $(_libs_nss) \ + $(_libs_dbus) $(dbus_bins) +libs := $(foreach path,$(sort $(_libs_src)),$(abspath $(DEST))$(path)) all: $(libs) $(DEST)/bin/$(BBBIN) @(cd $(DEST); \ diff --git a/test/skel/etc/machine-id b/test/skel/etc/machine-id new file mode 100644 index 00000000..cc671894 --- /dev/null +++ b/test/skel/etc/machine-id @@ -0,0 +1 @@ +0123456789abcdef0123456789abcdef diff --git a/test/skel/usr/share/dbus-1/system.conf b/test/skel/usr/share/dbus-1/system.conf new file mode 100644 index 00000000..5d256837 --- /dev/null +++ b/test/skel/usr/share/dbus-1/system.conf @@ -0,0 +1,36 @@ + + + + + system + + + /etc/dbus-1/system.d + unix:path=/var/run/dbus/system_bus_socket + EXTERNAL + + + + + + + + + + + + + + diff --git a/test/src/Makefile.am b/test/src/Makefile.am index 87d6e362..6897b3ab 100644 --- a/test/src/Makefile.am +++ b/test/src/Makefile.am @@ -2,11 +2,12 @@ noinst_PROGRAMS = serv serv_SOURCES = serv.c serv_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE -I$(top_builddir) -if LIBSYSTEMD -serv_CPPFLAGS += -I$(top_srcdir)/libsystemd $(lite_CFLAGS) +# serv is what notify.sh drives to test notify:systemd, so it always +# needs sd_notify(). The source is in-tree and it links straight in, +# unrelated to whether --with-libsystemd installs the shared library. +serv_CPPFLAGS += -DHAVE_LIBSYSTEMD=1 -I$(top_srcdir)/libsystemd $(lite_CFLAGS) serv_SOURCES += $(top_srcdir)/libsystemd/sd-daemon.c serv_LDADD = $(lite_LIBS) -endif if DBUS noinst_PROGRAMS += dbus-auth-client