From: Lennart Poettering Date: Thu, 14 Nov 2024 08:55:26 +0000 (+0100) Subject: namespace-util: add helper to get base UID from userns X-Git-Tag: v258-rc1~1673^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3d57b6692f0a014df2c8e9a41ac61e5da4275506;p=thirdparty%2Fsystemd.git namespace-util: add helper to get base UID from userns --- diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index d45d4d33c5d..77937473778 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -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; +} diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h index 319efec4ac8..1bd61e64457 100644 --- a/src/basic/namespace-util.h +++ b/src/basic/namespace-util.h @@ -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); diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c index a7658811ece..687ddacc579 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -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; + } + } +} diff --git a/src/basic/uid-range.h b/src/basic/uid-range.h index 1f687b2713e..41ca671dd66 100644 --- a/src/basic/uid-range.h +++ b/src/basic/uid-range.h @@ -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); diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index 6a0459e1fae..3f9c8b83de2 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -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");