From: Lennart Poettering Date: Tue, 7 Jul 2020 09:58:06 +0000 (+0200) Subject: machined: refactor UID/GID machine translation X-Git-Tag: v246-rc2~70^2~4 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fsystemd.git;a=commitdiff_plain;h=74d1b7d2ad89fadc434752558ac444b8df7e21ca machined: refactor UID/GID machine translation Let's move the heavy lifting out of the bus call implemntations, and into generic code. This allows us to expose them easily via Varlink too in a later commit. --- diff --git a/src/machine/machine.c b/src/machine/machine.c index 8154c42ed77..ace84edbb4e 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -746,6 +746,143 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) { return 0; } +static int machine_owns_uid_internal( + Machine *machine, + const char *map_file, /* "uid_map" or "gid_map" */ + uid_t uid, + uid_t *ret_internal_uid) { + + _cleanup_fclose_ FILE *f = NULL; + const char *p; + + /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */ + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + assert(machine); + + /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to + * internally in the machine */ + + if (machine->class != MACHINE_CONTAINER) + goto negative; + + p = procfs_file_alloca(machine->leader, map_file); + f = fopen(p, "re"); + if (!f) { + log_debug_errno(errno, "Failed to open %s, ignoring.", p); + goto negative; + } + + for (;;) { + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f)) + return errno_or_else(EIO); + + return -EIO; + } + + /* The private user namespace is disabled, ignoring. */ + if (uid_shift == 0) + continue; + + if (uid < uid_shift || uid >= uid_shift + uid_range) + continue; + + converted = (uid - uid_shift + uid_base); + if (!uid_is_valid(converted)) + return -EINVAL; + + if (ret_internal_uid) + *ret_internal_uid = converted; + + return true; + } + +negative: + if (ret_internal_uid) + *ret_internal_uid = UID_INVALID; + + return false; +} + +int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) { + return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid); +} + +int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) { + return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid); +} + +static int machine_translate_uid_internal( + Machine *machine, + const char *map_file, /* "uid_map" or "gid_map" */ + uid_t uid, + uid_t *ret_host_uid) { + + _cleanup_fclose_ FILE *f = NULL; + const char *p; + + /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */ + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + assert(machine); + assert(uid_is_valid(uid)); + + if (machine->class != MACHINE_CONTAINER) + return -ESRCH; + + /* Translates a machine UID into a host UID */ + + p = procfs_file_alloca(machine->leader, map_file); + f = fopen(p, "re"); + if (!f) + return -errno; + + for (;;) { + uid_t uid_base, uid_shift, uid_range, converted; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); + if (k < 0 && feof(f)) + break; + if (k != 3) { + if (ferror(f)) + return errno_or_else(EIO); + + return -EIO; + } + + if (uid < uid_base || uid >= uid_base + uid_range) + continue; + + converted = uid - uid_base + uid_shift; + if (!uid_is_valid(converted)) + return -EINVAL; + + if (ret_host_uid) + *ret_host_uid = converted; + return 0; + } + + return -ESRCH; +} + +int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) { + return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid); +} + +int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) { + return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid); +} + static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { [MACHINE_CONTAINER] = "container", [MACHINE_VM] = "vm", diff --git a/src/machine/machine.h b/src/machine/machine.h index 0a39e610529..634c5fc6488 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -94,3 +94,9 @@ int machine_openpt(Machine *m, int flags, char **ret_slave); int machine_open_terminal(Machine *m, const char *path, int mode); int machine_get_uid_shift(Machine *m, uid_t *ret); + +int machine_owns_uid(Machine *m, uid_t host_uid, uid_t *ret_internal_uid); +int machine_owns_gid(Machine *m, gid_t host_gid, gid_t *ret_internal_gid); + +int machine_translate_uid(Machine *m, uid_t internal_uid, uid_t *ret_host_uid); +int machine_translate_gid(Machine *m, gid_t internal_gid, gid_t *ret_host_gid); diff --git a/src/machine/machined-core.c b/src/machine/machined-core.c index 6a404805eaf..c44bb94d8ad 100644 --- a/src/machine/machined-core.c +++ b/src/machine/machined-core.c @@ -3,6 +3,7 @@ #include "machined.h" #include "nscd-flush.h" #include "strv.h" +#include "user-util.h" static int on_nscd_cache_flush_event(sd_event_source *s, void *userdata) { /* Let's ask glibc's nscd daemon to flush its caches. We request this for the three database machines may show @@ -34,3 +35,72 @@ int manager_enqueue_nscd_cache_flush(Manager *m) { return 0; } + +int manager_find_machine_for_uid(Manager *m, uid_t uid, Machine **ret_machine, uid_t *ret_internal_uid) { + Machine *machine; + Iterator i; + int r; + + assert(m); + assert(uid_is_valid(uid)); + + /* Finds the machine for the specified host UID and returns it along with the UID translated into the + * internal UID inside the machine */ + + HASHMAP_FOREACH(machine, m->machines, i) { + uid_t converted; + + r = machine_owns_uid(machine, uid, &converted); + if (r < 0) + return r; + if (r) { + if (ret_machine) + *ret_machine = machine; + + if (ret_internal_uid) + *ret_internal_uid = converted; + + return true; + } + } + + if (ret_machine) + *ret_machine = NULL; + if (ret_internal_uid) + *ret_internal_uid = UID_INVALID; + + return false; +} + +int manager_find_machine_for_gid(Manager *m, gid_t gid, Machine **ret_machine, gid_t *ret_internal_gid) { + Machine *machine; + Iterator i; + int r; + + assert(m); + assert(gid_is_valid(gid)); + + HASHMAP_FOREACH(machine, m->machines, i) { + gid_t converted; + + r = machine_owns_gid(machine, gid, &converted); + if (r < 0) + return r; + if (r) { + if (ret_machine) + *ret_machine = machine; + + if (ret_internal_gid) + *ret_internal_gid = converted; + + return true; + } + } + + if (ret_machine) + *ret_machine = NULL; + if (ret_internal_gid) + *ret_internal_gid = GID_INVALID; + + return false; +} diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 2d9c180aa43..467f16b72a4 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -883,11 +883,11 @@ static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bu } static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_fclose_ FILE *f = NULL; Manager *m = userdata; - const char *name, *p; + const char *name; Machine *machine; uint32_t uid; + uid_t converted; int r; r = sd_bus_message_read(message, "su", &name, &uid); @@ -904,44 +904,20 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata, if (machine->class != MACHINE_CONTAINER) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); - p = procfs_file_alloca(machine->leader, "uid_map"); - f = fopen(p, "re"); - if (!f) - return -errno; - - for (;;) { - uid_t uid_base, uid_shift, uid_range, converted; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - if (uid < uid_base || uid >= uid_base + uid_range) - continue; - - converted = uid - uid_base + uid_shift; - if (!uid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - - return sd_bus_reply_method_return(message, "u", (uint32_t) converted); - } + r = machine_translate_uid(machine, uid, &converted); + if (r == -ESRCH) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name); + if (r < 0) + return r; - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name); + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); } static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *o = NULL; Manager *m = userdata; Machine *machine; - uid_t uid; - Iterator i; + uid_t uid, converted; int r; r = sd_bus_message_read(message, "u", &uid); @@ -952,63 +928,24 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s if (uid < 0x10000) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid); - HASHMAP_FOREACH(machine, m->machines, i) { - _cleanup_fclose_ FILE *f = NULL; - char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; - - if (machine->class != MACHINE_CONTAINER) - continue; - - xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); - f = fopen(p, "re"); - if (!f) { - log_warning_errno(errno, "Failed to open %s, ignoring,", p); - continue; - } - - for (;;) { - _cleanup_free_ char *o = NULL; - uid_t uid_base, uid_shift, uid_range, converted; - int k; - - errno = 0; - k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - /* The private user namespace is disabled, ignoring. */ - if (uid_shift == 0) - continue; - - if (uid < uid_shift || uid >= uid_shift + uid_range) - continue; - - converted = (uid - uid_shift + uid_base); - if (!uid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid); - - o = machine_bus_path(machine); - if (!o) - return -ENOMEM; + r = manager_find_machine_for_uid(m, uid, &machine, &converted); + if (r < 0) + return r; + if (!r) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid); - return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); - } - } + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid); + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); } -static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { - _cleanup_fclose_ FILE *f = NULL; - Manager *m = groupdata; - const char *name, *p; +static int method_map_from_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; Machine *machine; + gid_t converted; uint32_t gid; int r; @@ -1026,44 +963,20 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat if (machine->class != MACHINE_CONTAINER) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); - p = procfs_file_alloca(machine->leader, "gid_map"); - f = fopen(p, "re"); - if (!f) - return -errno; - - for (;;) { - gid_t gid_base, gid_shift, gid_range, converted; - int k; - - errno = 0; - k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - if (gid < gid_base || gid >= gid_base + gid_range) - continue; - - converted = gid - gid_base + gid_shift; - if (!gid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - - return sd_bus_reply_method_return(message, "u", (uint32_t) converted); - } + r = machine_translate_gid(machine, gid, &converted); + if (r == -ESRCH) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching group mappings.", name); + if (r < 0) + return r; - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name); + return sd_bus_reply_method_return(message, "u", (uint32_t) converted); } -static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) { - Manager *m = groupdata; +static int method_map_to_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *o = NULL; + Manager *m = userdata; Machine *machine; - gid_t gid; - Iterator i; + gid_t gid, converted; int r; r = sd_bus_message_read(message, "u", &gid); @@ -1074,56 +987,17 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, if (gid < 0x10000) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid); - HASHMAP_FOREACH(machine, m->machines, i) { - _cleanup_fclose_ FILE *f = NULL; - char p[STRLEN("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; - - if (machine->class != MACHINE_CONTAINER) - continue; - - xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); - f = fopen(p, "re"); - if (!f) { - log_warning_errno(errno, "Failed to open %s, ignoring,", p); - continue; - } - - for (;;) { - _cleanup_free_ char *o = NULL; - gid_t gid_base, gid_shift, gid_range, converted; - int k; - - errno = 0; - k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range); - if (k < 0 && feof(f)) - break; - if (k != 3) { - if (ferror(f)) - return errno_or_else(EIO); - - return -EIO; - } - - /* The private user namespace is disabled, ignoring. */ - if (gid_shift == 0) - continue; - - if (gid < gid_shift || gid >= gid_shift + gid_range) - continue; - - converted = (gid - gid_shift + gid_base); - if (!gid_is_valid(converted)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid); - - o = machine_bus_path(machine); - if (!o) - return -ENOMEM; + r = manager_find_machine_for_gid(m, gid, &machine, &converted); + if (r < 0) + return r; + if (!r) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid); - return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); - } - } + o = machine_bus_path(machine); + if (!o) + return -ENOMEM; - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid); + return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted); } const sd_bus_vtable manager_vtable[] = { diff --git a/src/machine/machined.h b/src/machine/machined.h index 0ec3fb9326f..8de659b11ab 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -6,12 +6,11 @@ #include "sd-bus.h" #include "sd-event.h" -#include "hashmap.h" -#include "list.h" - typedef struct Manager Manager; +#include "hashmap.h" #include "image-dbus.h" +#include "list.h" #include "machine-dbus.h" #include "machine.h" #include "operation.h" @@ -56,3 +55,6 @@ int manager_unit_is_active(Manager *manager, const char *unit); int manager_job_is_active(Manager *manager, const char *path); int manager_enqueue_nscd_cache_flush(Manager *m); + +int manager_find_machine_for_uid(Manager *m, uid_t host_uid, Machine **ret_machine, uid_t *ret_internal_uid); +int manager_find_machine_for_gid(Manager *m, gid_t host_gid, Machine **ret_machine, gid_t *ret_internal_gid);