From: Luca Boccassi Date: Fri, 12 Sep 2025 18:59:26 +0000 (+0100) Subject: machine: do not allow unprivileged users to register other users' processes as machines X-Git-Tag: v258~2^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=119d332d9c2cf1974b235c8d9e4e3ad821cf436a;p=thirdparty%2Fsystemd.git machine: do not allow unprivileged users to register other users' processes as machines Registering a process as a machine means a caller can get machined to send sigterm to it, and more. If an unpriv user is registering, ensure the registered process is actually owned by the user. Follow-up for adaff8eb35d9c471af81fddaa4403bc5843a256f --- diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index 776654b51d1..b2b2f1f2db0 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -15,6 +15,7 @@ #include "machine-varlink.h" #include "machined.h" #include "mount-util.h" +#include "namespace-util.h" #include "operation.h" #include "pidref.h" #include "socket-util.h" @@ -186,6 +187,15 @@ int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink if (r < 0) return r; + /* Ensure an unprivileged user cannot claim any process they don't control as their own machine */ + if (machine->uid != 0) { + r = process_is_owned_by_uid(&machine->leader, machine->uid); + if (r < 0) + return r; + if (r == 0) + return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL); + } + r = machine_link(manager, machine); if (r == -EEXIST) return sd_varlink_error(link, VARLINK_ERROR_MACHINE_EXISTS, NULL); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 883c8c1338c..91e5e218698 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -29,6 +29,7 @@ #include "machine-dbus.h" #include "machine-pool.h" #include "machined.h" +#include "namespace-util.h" #include "operation.h" #include "os-util.h" #include "path-util.h" @@ -321,6 +322,15 @@ static int method_create_or_register_machine( if (r < 0) return r; + /* Ensure an unprivileged user cannot claim any process they don't control as their own machine */ + if (uid != 0) { + r = process_is_owned_by_uid(&leader_pidref, uid); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only root may register machines for other users"); + } + const char *details[] = { "name", name, "class", machine_class_to_string(c), diff --git a/test/units/TEST-13-NSPAWN.unpriv.sh b/test/units/TEST-13-NSPAWN.unpriv.sh index 03af7ebc9cc..17ccb19c8eb 100755 --- a/test/units/TEST-13-NSPAWN.unpriv.sh +++ b/test/units/TEST-13-NSPAWN.unpriv.sh @@ -15,7 +15,7 @@ fi at_exit() { rm -rf /home/testuser/.local/state/machines/zurps ||: machinectl terminate zurps ||: - rm -f /usr/share/polkit-1/rules.d/registermachinetest.rules + rm -f /etc/polkit-1/rules.d/registermachinetest.rules } trap at_exit EXIT @@ -33,7 +33,8 @@ systemd-dissect --shift /home/testuser/.local/state/machines/zurps foreign # Install a PK rule that allows 'testuser' user to register a machine even # though they are not on an fg console, just for testing -cat >/usr/share/polkit-1/rules.d/registermachinetest.rules <<'EOF' +mkdir -p /etc/polkit-1/rules.d +cat >/etc/polkit-1/rules.d/registermachinetest.rules <<'EOF' polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.machine1.register-machine" && subject.user == "testuser") { @@ -54,4 +55,18 @@ machinectl terminate zurps (! run0 -u testuser systemctl is-active --user systemd-nspawn@zurps.service) +(! run0 -u testuser \ + busctl call \ + org.freedesktop.machine1 \ + /org/freedesktop/machine1 \ + org.freedesktop.machine1.Manager \ + RegisterMachine \ + 'sayssus' \ + shouldnotwork1 \ + 16 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 \ + "" \ + container \ + "$(systemctl show -p MainPID --value systemd-logind.service)" \ + "$PWD") + loginctl disable-linger testuser