From: Lennart Poettering Date: Fri, 23 May 2025 13:30:22 +0000 (+0200) Subject: machined: track UID owner of machines X-Git-Tag: v258-rc1~79^2~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=276d20018623ef14956ce87975be48da5de63f29;p=thirdparty%2Fsystemd.git machined: track UID owner of machines Now that unpriv clients can register machines, let's register their UID too. This allows us to do two things: 1. make sure the scope delegation is assigned to the right UID (so that the unpriv user can actually create cgroups below the delegated scope) 2. permit certain types of access (i.e. killing, or pty access) to the client without auth if it owns the machine. --- diff --git a/man/org.freedesktop.machine1.xml b/man/org.freedesktop.machine1.xml index ea3a7062159..c52aed0dbc0 100644 --- a/man/org.freedesktop.machine1.xml +++ b/man/org.freedesktop.machine1.xml @@ -525,6 +525,8 @@ node /org/freedesktop/machine1/machine/rawhide { readonly s SSHPrivateKeyPath = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s State = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly u UID = ...; }; interface org.freedesktop.DBus.Peer { ... }; interface org.freedesktop.DBus.Introspectable { ... }; @@ -620,6 +622,8 @@ node /org/freedesktop/machine1/machine/rawhide { + + @@ -683,6 +687,8 @@ node /org/freedesktop/machine1/machine/rawhide { Subgroup contains the sub-control-group path this machine's processes reside in, relative to the specified unit's control group. + + UID contains the numeric UNIX UID of the user who registered the machine. @@ -726,7 +732,8 @@ $ gdbus introspect --system \ CopyToWithFlags() were added in version 252. GetSSHInfo(), VSockCID, SSHAddress, and SSHPrivateKeyPath were added in version 256. - LeaderPIDFDId and Subgroup were added in version 258. + LeaderPIDFDId, Subgroup, and UID were + added in version 258. diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 8935b594656..321ddbfdccd 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -60,10 +60,12 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu NULL }; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, "org.freedesktop.machine1.manage-machines", details, + m->uid, + /* flags= */ 0, &m->manager->polkit_registry, error); if (r < 0) @@ -90,10 +92,12 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus NULL }; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, "org.freedesktop.machine1.manage-machines", details, + m->uid, + /* flags= */ 0, &m->manager->polkit_registry, error); if (r < 0) @@ -138,10 +142,12 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro NULL }; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, "org.freedesktop.machine1.manage-machines", details, + m->uid, + /* flags= */ 0, &m->manager->polkit_registry, error); if (r < 0) @@ -258,10 +264,12 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ NULL }; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", details, + m->uid, + /* flags= */ 0, &m->manager->polkit_registry, error); if (r < 0) @@ -299,10 +307,12 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu NULL }; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", details, + m->uid, + /* flags= */ 0, &m->manager->polkit_registry, error); if (r < 0) @@ -383,10 +393,12 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu NULL }; - r = bus_verify_polkit_async( + r = bus_verify_polkit_async_full( message, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", details, + m->uid, + /* flags= */ 0, &m->manager->polkit_registry, error); if (r < 0) @@ -446,6 +458,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu NULL }; + /* NB: For now not opened up to owner of machine without auth */ r = bus_verify_polkit_async( message, "org.freedesktop.machine1.manage-machines", @@ -531,6 +544,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro NULL }; + /* NB: For now not opened up to owner of machine without auth */ r = bus_verify_polkit_async( message, "org.freedesktop.machine1.manage-machines", @@ -574,6 +588,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda NULL }; + /* NB: For now not opened up to owner of machine without auth */ r = bus_verify_polkit_async( message, "org.freedesktop.machine1.manage-machines", @@ -727,6 +742,7 @@ static const sd_bus_vtable machine_vtable[] = { SD_BUS_PROPERTY("SSHAddress", "s", NULL, offsetof(Machine, ssh_address), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SSHPrivateKeyPath", "s", NULL, offsetof(Machine, ssh_private_key_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), + SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Machine, uid), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD("Terminate", NULL, diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index b5754e2c49f..8c437efc175 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -168,6 +168,10 @@ int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink return r; } + r = sd_varlink_get_peer_uid(link, &machine->uid); + if (r < 0) + return r; + r = machine_link(manager, machine); if (r == -EEXIST) return sd_varlink_error(link, VARLINK_ERROR_MACHINE_EXISTS, NULL); @@ -278,12 +282,14 @@ int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, Manager *manager = ASSERT_PTR(machine->manager); int r; - r = varlink_verify_polkit_async( + r = varlink_verify_polkit_async_full( link, manager->bus, "org.freedesktop.machine1.manage-machines", (const char**) STRV_MAKE("name", machine->name, "verb", "unregister"), + machine->uid, + /* flags= */ 0, &manager->polkit_registry); if (r <= 0) return r; @@ -300,12 +306,14 @@ int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters, Manager *manager = ASSERT_PTR(machine->manager); int r; - r = varlink_verify_polkit_async( + r = varlink_verify_polkit_async_full( link, manager->bus, "org.freedesktop.machine1.manage-machines", (const char**) STRV_MAKE("name", machine->name, "verb", "terminate"), + machine->uid, + /* flags= */ 0, &manager->polkit_registry); if (r <= 0) return r; @@ -368,12 +376,14 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met return sd_varlink_error_invalid_parameter_name(link, "whom"); } - r = varlink_verify_polkit_async( + r = varlink_verify_polkit_async_full( link, manager->bus, "org.freedesktop.machine1.manage-machines", (const char**) STRV_MAKE("name", machine->name, "verb", "kill"), + machine->uid, + /* flags= */ 0, &manager->polkit_registry); if (r <= 0) return r; @@ -510,11 +520,13 @@ int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met return r; polkit_details = machine_open_polkit_details(p.mode, machine->name, user, path, command_line); - r = varlink_verify_polkit_async( + r = varlink_verify_polkit_async_full( link, manager->bus, machine_open_polkit_action(p.mode, machine->class), (const char**) polkit_details, + machine->uid, + /* flags= */ 0, &manager->polkit_registry); if (r <= 0) return r; @@ -788,6 +800,7 @@ int vl_method_bind_mount(sd_varlink *link, sd_json_variant *parameters, sd_varli if (machine->class != MACHINE_CONTAINER) return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NOT_SUPPORTED, NULL); + /* NB: For now not opened up to owner of machine without auth */ r = varlink_verify_polkit_async( link, manager->bus, @@ -899,6 +912,7 @@ int vl_method_copy_internal(sd_varlink *link, sd_json_variant *parameters, sd_va if (machine->class != MACHINE_CONTAINER) return sd_varlink_error(link, VARLINK_ERROR_MACHINE_NOT_SUPPORTED, NULL); + /* NB: For now not opened up to owner of machine without auth */ r = varlink_verify_polkit_async( link, manager->bus, @@ -928,6 +942,7 @@ int vl_method_open_root_directory_internal(sd_varlink *link, sd_json_variant *pa Manager *manager = ASSERT_PTR(machine->manager); int r; + /* NB: For now not opened up to owner of machine without auth */ r = varlink_verify_polkit_async( link, manager->bus, diff --git a/src/machine/machine.c b/src/machine/machine.c index 7ce512dd62a..91c14501842 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -183,8 +183,10 @@ int machine_save(Machine *m) { fprintf(f, "# This is private data. Do not parse.\n" - "NAME=%s\n", - m->name); + "NAME=%s\n" + "UID=" UID_FMT "\n", + m->name, + m->uid); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */ env_file_fputs_assignment(f, "SCOPE=", m->unit); @@ -261,7 +263,7 @@ static void machine_unlink(Machine *m) { int machine_load(Machine *m) { _cleanup_free_ char *name = NULL, *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *leader_pidfdid = NULL, - *class = NULL, *netif = NULL, *vsock_cid = NULL; + *class = NULL, *netif = NULL, *vsock_cid = NULL, *uid = NULL; int r; assert(m); @@ -285,7 +287,8 @@ int machine_load(Machine *m) { "NETIF", &netif, "VSOCK_CID", &vsock_cid, "SSH_ADDRESS", &m->ssh_address, - "SSH_PRIVATE_KEY_PATH", &m->ssh_private_key_path); + "SSH_PRIVATE_KEY_PATH", &m->ssh_private_key_path, + "UID", &uid); if (r == -ENOENT) return 0; if (r < 0) @@ -369,6 +372,10 @@ int machine_load(Machine *m) { log_warning_errno(r, "Failed to parse AF_VSOCK CID, ignoring: %s", vsock_cid); } + r = parse_uid(uid, &m->uid); + if (r < 0) + log_warning_errno(r, "Failed to parse owning UID, ignoring: %s", uid); + return r; } @@ -426,14 +433,28 @@ static int machine_start_scope( if (r < 0) return r; - r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)", - "Delegate", "b", 1, - "CollectMode", "s", "inactive-or-failed", - "AddRef", "b", 1, - "TasksMax", "t", UINT64_C(16384)); + r = sd_bus_message_append( + m, "(sv)(sv)(sv)(sv)", + "Delegate", "b", 1, + "CollectMode", "s", "inactive-or-failed", + "AddRef", "b", 1, + "TasksMax", "t", UINT64_C(16384)); if (r < 0) return r; + if (machine->uid != 0) { + _cleanup_free_ char *u = NULL; + + if (asprintf(&u, UID_FMT, machine->uid) < 0) + return -ENOMEM; + + r = sd_bus_message_append( + m, "(sv)", + "User", "s", u); + if (r < 0) + return r; + } + if (more_properties) { r = sd_bus_message_copy(m, more_properties, true); if (r < 0) diff --git a/src/machine/machine.h b/src/machine/machine.h index 8762263a8bf..dddc0c8000c 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -38,6 +38,8 @@ typedef struct Machine { char *name; sd_id128_t id; + uid_t uid; + MachineClass class; char *state_file; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 7731fae7089..d31ef407f46 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -514,6 +514,7 @@ typedef struct MachineStatusInfo { struct dual_timestamp timestamp; int *netif; size_t n_netif; + uid_t uid; } MachineStatusInfo; static void machine_status_info_clear(MachineStatusInfo *info) { @@ -567,6 +568,9 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { } else if (i->class) printf("\t Class: %s\n", i->class); + if (i->uid != 0) + printf("\t UID: " UID_FMT "\n", i->uid); + if (i->root_directory) printf("\t Root: %s\n", i->root_directory); @@ -662,6 +666,7 @@ static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bo { "TimestampMonotonic", "t", NULL, offsetof(MachineStatusInfo, timestamp.monotonic) }, { "Id", "ay", bus_map_id128, offsetof(MachineStatusInfo, id) }, { "NetworkInterfaces", "ai", map_netif, 0 }, + { "UID", "u", NULL, offsetof(MachineStatusInfo, uid) }, {} }; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 8aa00ba3edd..30f722a4497 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -300,6 +300,16 @@ static int method_create_or_register_machine( if (hashmap_get(manager->machines, name)) return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name); + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + uid_t uid; + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + const char *details[] = { "name", name, "class", machine_class_to_string(c), @@ -324,6 +334,7 @@ static int method_create_or_register_machine( m->leader = TAKE_PIDREF(pidref); m->class = c; m->id = id; + m->uid = uid; if (!isempty(service)) { m->service = strdup(service); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 8551d703e7b..7dac3cb0d20 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -485,7 +485,8 @@ static int list_machine_one_and_maybe_read_metadata(sd_varlink *link, Machine *m JSON_BUILD_PAIR_STRING_NON_EMPTY("sshPrivateKeyPath", m->ssh_private_key_path), JSON_BUILD_PAIR_VARIANT_NON_NULL("addresses", addr_array), JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("OSRelease", os_release), - JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("UIDShift", shift, UID_INVALID)); + JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("UIDShift", shift, UID_INVALID), + SD_JSON_BUILD_PAIR_UNSIGNED("UID", m->uid)); if (r < 0) return r; diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index 1bb7c00fa18..ad7dff228f1 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -98,7 +98,9 @@ static SD_VARLINK_DEFINE_METHOD_FULL( SD_VARLINK_FIELD_COMMENT("Return the base UID/GID of the machine"), SD_VARLINK_DEFINE_OUTPUT(UIDShift, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Subcgroup path of the machine, relative to the unit's cgroup path"), - SD_VARLINK_DEFINE_OUTPUT(Subgroup, SD_VARLINK_STRING, SD_VARLINK_NULLABLE)); + SD_VARLINK_DEFINE_OUTPUT(Subgroup, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Numeric UNIX UID of the user who registered the machine"), + SD_VARLINK_DEFINE_OUTPUT(UID, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); static SD_VARLINK_DEFINE_ENUM_TYPE( MachineOpenMode,