]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
namespace-util: add helper to get base UID from userns
authorLennart Poettering <lennart@poettering.net>
Thu, 14 Nov 2024 08:55:26 +0000 (09:55 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 7 Jan 2025 22:55:34 +0000 (23:55 +0100)
src/basic/namespace-util.c
src/basic/namespace-util.h
src/basic/uid-range.c
src/basic/uid-range.h
src/test/test-namespace.c

index d45d4d33c5d50abbedebddf21a966a2cdbf6e37a..779374737785b8d77de82f404e43e63a86622d68 100644 (file)
@@ -22,6 +22,7 @@
 #include "process-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
+#include "uid-range.h"
 #include "user-util.h"
 
 const struct namespace_info namespace_info[_NAMESPACE_TYPE_MAX + 1] = {
@@ -636,3 +637,74 @@ int is_idmapping_supported(const char *path) {
 
         return true;
 }
+
+int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) {
+        _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
+        _cleanup_(sigkill_waitp) pid_t pid = 0;
+        ssize_t n;
+        char x;
+        int r;
+
+        assert(userns_fd >= 0);
+
+        if (pipe2(pfd, O_CLOEXEC) < 0)
+                return -errno;
+
+        r = safe_fork_full(
+                        "(sd-baseuns)",
+                        /* stdio_fds= */ NULL,
+                        (int[]) { pfd[1], userns_fd }, 2,
+                        FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL,
+                        &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child. */
+
+                if (setns(userns_fd, CLONE_NEWUSER) < 0) {
+                        log_debug_errno(errno, "Failed to join userns: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                userns_fd = safe_close(userns_fd);
+
+                n = write(pfd[1], &(const char) { 'x' }, 1);
+                if (n < 0) {
+                        log_debug_errno(errno, "Failed to write to pipe: %m");
+                        _exit(EXIT_FAILURE);
+                }
+                assert(n == 1);
+
+                freeze();
+        }
+
+        pfd[1] = safe_close(pfd[1]);
+
+        n = read(pfd[0], &x, 1);
+        if (n < 0)
+                return -errno;
+        if (n == 0)
+                return -EPROTO;
+        assert(n == 1);
+        assert(x == 'x');
+
+        uid_t uid;
+        r = uid_map_search_root(pid, "uid_map", &uid);
+        if (r < 0)
+                return r;
+
+        gid_t gid;
+        r = uid_map_search_root(pid, "gid_map", &gid);
+        if (r < 0)
+                return r;
+
+        if (!ret_gid && uid != gid)
+                return -EUCLEAN;
+
+        if (ret_uid)
+                *ret_uid = uid;
+        if (ret_gid)
+                *ret_gid = gid;
+
+        return 0;
+}
index 319efec4ac8d5c136e4002c668f0d03abbaf01a5..1bd61e64457581cee7ac5da28117827ad1a1e2fc 100644 (file)
@@ -91,3 +91,5 @@ int netns_acquire(void);
 int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
 
 int is_idmapping_supported(const char *path);
+
+int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);
index a7658811ece4d1b8bd8696b04b0b895730ef9f27..687ddacc5791ff02864074250fc15e101951a26e 100644 (file)
@@ -186,9 +186,6 @@ int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_rang
         int r;
 
         assert(f);
-        assert(ret_base);
-        assert(ret_shift);
-        assert(ret_range);
 
         errno = 0;
         r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
@@ -197,10 +194,15 @@ int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_rang
         assert(r >= 0);
         if (r != 3)
                 return -EBADMSG;
+        if (uid_range <= 0)
+                return -EBADMSG;
 
-        *ret_base = uid_base;
-        *ret_shift = uid_shift;
-        *ret_range = uid_range;
+        if (ret_base)
+                *ret_base = uid_base;
+        if (ret_shift)
+                *ret_shift = uid_shift;
+        if (ret_range)
+                *ret_range = uid_range;
 
         return 0;
 }
@@ -360,3 +362,37 @@ bool uid_range_equal(const UIDRange *a, const UIDRange *b) {
 
         return true;
 }
+
+int uid_map_search_root(pid_t pid, const char *filename, uid_t *ret) {
+        int r;
+
+        assert(pid_is_valid(pid));
+        assert(filename);
+
+        const char *p = procfs_file_alloca(pid, filename);
+        _cleanup_fclose_ FILE *f = fopen(p, "re");
+        if (!f) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                r = proc_mounted();
+                if (r < 0)
+                        return -ENOENT; /* original error, if we can't determine /proc/ state */
+
+                return r ? -ENOPKG : -ENOSYS;
+        }
+
+        for (;;) {
+                uid_t uid_base = UID_INVALID, uid_shift = UID_INVALID;
+
+                r = uid_map_read_one(f, &uid_base, &uid_shift, /* ret_uid_range= */ NULL);
+                if (r < 0)
+                        return r;
+
+                if (uid_base == 0) {
+                        if (ret)
+                                *ret = uid_shift;
+                        return 0;
+                }
+        }
+}
index 1f687b2713e1d6cefd4ecc064be83d2eef2ce793..41ca671dd66c0a3e6c603db9a5d03c57cde987ec 100644 (file)
@@ -76,3 +76,5 @@ int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **
 int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret);
 
 bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr);
+
+int uid_map_search_root(pid_t pid, const char *filename, uid_t *ret);
index 6a0459e1faea83e436720b51a5483bdca9aeeb07..3f9c8b83de2cc4c0fd468c7b697acb042100f52d 100644 (file)
@@ -252,6 +252,30 @@ TEST(namespace_is_init) {
         }
 }
 
+TEST(userns_get_base_uid) {
+        _cleanup_close_ int fd = -EBADF;
+
+        fd = userns_acquire("0 1 1", "0 2 1");
+        if (ERRNO_IS_NEG_NOT_SUPPORTED(fd))
+                return (void) log_tests_skipped("userns is not supported");
+        if (ERRNO_IS_NEG_PRIVILEGE(fd))
+                return (void) log_tests_skipped("lacking userns privileges");
+
+        uid_t base_uid, base_gid;
+        ASSERT_OK(userns_get_base_uid(fd, &base_uid, &base_gid));
+        ASSERT_EQ(base_uid, 1U);
+        ASSERT_EQ(base_gid, 2U);
+
+        ASSERT_ERROR(userns_get_base_uid(fd, &base_uid, NULL), EUCLEAN);
+
+        fd = safe_close(fd);
+
+        fd = userns_acquire_empty();
+        ASSERT_OK(fd);
+
+        ASSERT_ERROR(userns_get_base_uid(fd, &base_uid, &base_gid), ENOMSG);
+}
+
 static int intro(void) {
         if (!have_namespaces())
                 return log_tests_skipped("Don't have namespace support or lacking privileges");