machined: refactor UID/GID machine translation
authorLennart Poettering <lennart@poettering.net>
Tue, 7 Jul 2020 09:58:06 +0000 (11:58 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 14 Jul 2020 15:06:23 +0000 (17:06 +0200)
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.

src/machine/machine.c
src/machine/machine.h
src/machine/machined-core.c
src/machine/machined-dbus.c
src/machine/machined.h

index 8154c42..ace84ed 100644 (file)
@@ -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",
index 0a39e61..634c5fc 100644 (file)
@@ -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);
index 6a40480..c44bb94 100644 (file)
@@ -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;
+}
index 2d9c180..467f16b 100644 (file)
@@ -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[] = {
index 0ec3fb9..8de659b 100644 (file)
@@ -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);