mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 00:47:10 +09:00
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.
This commit is contained in:
@@ -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 {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="State"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UID"/>
|
||||
|
||||
<!--End of Autogenerated section-->
|
||||
|
||||
<refsect2>
|
||||
@@ -683,6 +687,8 @@ node /org/freedesktop/machine1/machine/rawhide {
|
||||
|
||||
<para><varname>Subgroup</varname> contains the sub-control-group path this machine's processes reside
|
||||
in, relative to the specified unit's control group.</para>
|
||||
|
||||
<para><varname>UID</varname> contains the numeric UNIX UID of the user who registered the machine.</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
@@ -726,7 +732,8 @@ $ gdbus introspect --system \
|
||||
<function>CopyToWithFlags()</function> were added in version 252.</para>
|
||||
<para><function>GetSSHInfo()</function>, <varname>VSockCID</varname>, <varname>SSHAddress</varname>,
|
||||
and <varname>SSHPrivateKeyPath</varname> were added in version 256.</para>
|
||||
<para><varname>LeaderPIDFDId</varname> and <varname>Subgroup</varname> were added in version 258.</para>
|
||||
<para><varname>LeaderPIDFDId</varname>, <varname>Subgroup</varname>, and <varname>UID</varname> were
|
||||
added in version 258.</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -38,6 +38,8 @@ typedef struct Machine {
|
||||
char *name;
|
||||
sd_id128_t id;
|
||||
|
||||
uid_t uid;
|
||||
|
||||
MachineClass class;
|
||||
|
||||
char *state_file;
|
||||
|
||||
@@ -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) },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user