From dfef02c675135afeea9f0b58856166fd8c48767b Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 Jan 2025 13:50:51 +0100 Subject: [PATCH 1/7] process-util: drop duplicate assertions --- src/basic/process-util.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 6af1349dd5..6ec9a5c248 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1504,9 +1504,6 @@ int safe_fork_full( } if (FLAGS_SET(flags, FORK_DETACH)) { - assert(!FLAGS_SET(flags, FORK_WAIT)); - assert(!ret_pid); - /* Fork off intermediary child if needed */ r = is_reaper_process(); From 407ebf29ccb601007d6289f3b57539e8021300af Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 Jan 2025 12:25:04 +0100 Subject: [PATCH 2/7] basic: move nsfs ioctls from missing_fs to missing_namespace Addresses https://github.com/systemd/systemd/pull/35833#discussion_r1905333757 --- src/basic/missing_fs.h | 23 +++++------------------ src/basic/missing_namespace.h | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h index ed6bdf0426..60b0a3a2a7 100644 --- a/src/basic/missing_fs.h +++ b/src/basic/missing_fs.h @@ -89,30 +89,17 @@ assert_cc(MS_STRICTATIME == (1 << 24)); assert_cc(MS_LAZYTIME == (1<<25)); #endif -/* Not exposed yet. Defined at fs/ext4/ext4.h */ -#ifndef EXT4_IOC_RESIZE_FS -#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) -#endif - -/* linux/nsfs.h */ -#ifndef NS_GET_USERNS /* 6786741dbf99e44fb0c0ed85a37582b8a26f1c3b (4.9) */ -#define NS_GET_USERNS _IO(0xb7, 0x1) -#endif - -#ifndef NS_GET_NSTYPE /* e5ff5ce6e20ee22511398bb31fb912466cf82a36 (4.11) */ -#define NS_GET_NSTYPE _IO(0xb7, 0x3) -#endif - -#ifndef NS_GET_OWNER_UID /* d95fa3c76a66b6d76b1e109ea505c55e66360f3c (4.11) */ -#define NS_GET_OWNER_UID _IO(0xb7, 0x4) -#endif - #ifndef FS_PROJINHERIT_FL # define FS_PROJINHERIT_FL 0x20000000 #else assert_cc(FS_PROJINHERIT_FL == 0x20000000); #endif +/* Not exposed yet. Defined at fs/ext4/ext4.h */ +#ifndef EXT4_IOC_RESIZE_FS +#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) +#endif + /* linux/fscrypt.h */ #ifndef FS_KEY_DESCRIPTOR_SIZE # define FS_KEY_DESCRIPTOR_SIZE 8 diff --git a/src/basic/missing_namespace.h b/src/basic/missing_namespace.h index 318c0143b8..1c7285151e 100644 --- a/src/basic/missing_namespace.h +++ b/src/basic/missing_namespace.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + /* Root namespace inode numbers, as per include/linux/proc_ns.h in the kernel source tree, since v3.8: * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=98f842e675f96ffac96e6c50315790912b2812be */ @@ -10,3 +12,16 @@ #define PROC_PID_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFC)) #define PROC_CGROUP_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFB)) #define PROC_TIME_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFA)) + +/* linux/nsfs.h */ +#ifndef NS_GET_USERNS /* 6786741dbf99e44fb0c0ed85a37582b8a26f1c3b (4.9) */ +#define NS_GET_USERNS _IO(0xb7, 0x1) +#endif + +#ifndef NS_GET_NSTYPE /* e5ff5ce6e20ee22511398bb31fb912466cf82a36 (4.11) */ +#define NS_GET_NSTYPE _IO(0xb7, 0x3) +#endif + +#ifndef NS_GET_OWNER_UID /* d95fa3c76a66b6d76b1e109ea505c55e66360f3c (4.11) */ +#define NS_GET_OWNER_UID _IO(0xb7, 0x4) +#endif From 1a669ea7e136879743b79087150e19034d2045a1 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 11 Jan 2025 15:41:55 +0100 Subject: [PATCH 3/7] README: document kernel version for idmapped mounts --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index 75072382d1..3aba52921a 100644 --- a/README +++ b/README @@ -58,6 +58,7 @@ REQUIREMENTS: Linux kernel ≥ 5.8 for LOOP_CONFIGURE and STATX_ATTR_MOUNT_ROOT ≥ 5.9 for close_range() + ≥ 5.12 for idmapped mount ≥ 5.14 for cgroup.kill ≥ 6.3 for MFD_EXEC/MFD_NOEXEC_SEAL and tmpfs noswap option ≥ 6.5 for name_to_handle_at() AT_HANDLE_FID, SO_PEERPIDFD/SO_PASSPIDFD, From c7704ecd0425a7ba5230275bdd4f06f1713c5cf8 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 11 Jan 2025 15:42:15 +0100 Subject: [PATCH 4/7] namespace-util: group userns functions together --- src/basic/namespace-util.c | 237 +++++++++++++++++++------------------ src/basic/namespace-util.h | 12 +- 2 files changed, 125 insertions(+), 124 deletions(-) diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index c7b1bcca2d..2ae5003459 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -459,6 +459,44 @@ int detach_mount_namespace_userns(int userns_fd) { return detach_mount_namespace(); } +int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) { + _cleanup_free_ char *buffer = NULL; + const char *range, *shift; + int r; + uid_t uid_shift, uid_range = 65536; + + assert(s); + + range = strchr(s, ':'); + if (range) { + buffer = strndup(s, range - s); + if (!buffer) + return log_oom(); + shift = buffer; + + range++; + r = safe_atou32(range, &uid_range); + if (r < 0) + return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range); + } else + shift = s; + + r = parse_uid(shift, &uid_shift); + if (r < 0) + return log_error_errno(r, "Failed to parse UID \"%s\": %m", s); + + if (!userns_shift_range_valid(uid_shift, uid_range)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID); + + if (ret_uid_shift) + *ret_uid_shift = uid_shift; + + if (ret_uid_range) + *ret_uid_range = uid_range; + + return 0; +} + int userns_acquire_empty(void) { _cleanup_(sigkill_waitp) pid_t pid = 0; _cleanup_close_ int userns_fd = -EBADF; @@ -520,124 +558,6 @@ int userns_acquire(const char *uid_map, const char *gid_map) { return TAKE_FD(userns_fd); } -int netns_acquire(void) { - _cleanup_(sigkill_waitp) pid_t pid = 0; - _cleanup_close_ int netns_fd = -EBADF; - int r; - - /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills - * the process again. This way we have a netns fd that is not bound to any process. */ - - r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid); - if (r < 0) - return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m"); - if (r == 0) - /* Child. We do nothing here, just freeze until somebody kills us. */ - freeze(); - - r = namespace_open(pid, - /* ret_pidns_fd = */ NULL, - /* ret_mntns_fd = */ NULL, - &netns_fd, - /* ret_userns_fd = */ NULL, - /* ret_root_fd = */ NULL); - if (r < 0) - return log_debug_errno(r, "Failed to open netns fd: %m"); - - return TAKE_FD(netns_fd); -} - -int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) { - _cleanup_free_ char *buffer = NULL; - const char *range, *shift; - int r; - uid_t uid_shift, uid_range = 65536; - - assert(s); - - range = strchr(s, ':'); - if (range) { - buffer = strndup(s, range - s); - if (!buffer) - return log_oom(); - shift = buffer; - - range++; - r = safe_atou32(range, &uid_range); - if (r < 0) - return log_error_errno(r, "Failed to parse UID range \"%s\": %m", range); - } else - shift = s; - - r = parse_uid(shift, &uid_shift); - if (r < 0) - return log_error_errno(r, "Failed to parse UID \"%s\": %m", s); - - if (!userns_shift_range_valid(uid_shift, uid_range)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID range cannot be empty or go beyond " UID_FMT ".", UID_INVALID); - - if (ret_uid_shift) - *ret_uid_shift = uid_shift; - - if (ret_uid_range) - *ret_uid_range = uid_range; - - return 0; -} - -int is_idmapping_supported(const char *path) { - _cleanup_close_ int mount_fd = -EBADF, userns_fd = -EBADF, dir_fd = -EBADF; - _cleanup_free_ char *uid_map = NULL, *gid_map = NULL; - int r; - - assert(path); - - if (!mount_new_api_supported()) - return false; - - r = strextendf(&uid_map, UID_FMT " " UID_FMT " " UID_FMT "\n", UID_NOBODY, UID_NOBODY, 1u); - if (r < 0) - return r; - - r = strextendf(&gid_map, GID_FMT " " GID_FMT " " GID_FMT "\n", GID_NOBODY, GID_NOBODY, 1u); - if (r < 0) - return r; - - userns_fd = r = userns_acquire(uid_map, gid_map); - if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL) - return false; - if (r == -ENOSPC) { - log_debug_errno(r, "Failed to acquire new user namespace, user.max_user_namespaces seems to be exhausted or maybe even zero, assuming ID-mapping is not supported: %m"); - return false; - } - if (r < 0) - return log_debug_errno(r, "Failed to acquire new user namespace for checking if '%s' supports ID-mapping: %m", path); - - dir_fd = r = RET_NERRNO(open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); - if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) - return false; - if (r < 0) - return log_debug_errno(r, "Failed to open '%s', cannot determine if ID-mapping is supported: %m", path); - - mount_fd = r = RET_NERRNO(open_tree(dir_fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC)); - if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL) - return false; - if (r < 0) - return log_debug_errno(r, "Failed to open mount tree '%s', cannot determine if ID-mapping is supported: %m", path); - - r = RET_NERRNO(mount_setattr(mount_fd, "", AT_EMPTY_PATH, - &(struct mount_attr) { - .attr_set = MOUNT_ATTR_IDMAP | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RDONLY | MOUNT_ATTR_NODEV, - .userns_fd = userns_fd, - }, sizeof(struct mount_attr))); - if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL) - return false; - if (r < 0) - return log_debug_errno(r, "Failed to set mount attribute to '%s', cannot determine if ID-mapping is supported: %m", 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; @@ -763,3 +683,84 @@ int process_is_owned_by_uid(const PidRef *pidref, uid_t uid) { close_and_replace(userns_fd, parent_fd); } } + +int is_idmapping_supported(const char *path) { + _cleanup_close_ int mount_fd = -EBADF, userns_fd = -EBADF, dir_fd = -EBADF; + _cleanup_free_ char *uid_map = NULL, *gid_map = NULL; + int r; + + assert(path); + + if (!mount_new_api_supported()) + return false; + + r = strextendf(&uid_map, UID_FMT " " UID_FMT " " UID_FMT "\n", UID_NOBODY, UID_NOBODY, 1u); + if (r < 0) + return r; + + r = strextendf(&gid_map, GID_FMT " " GID_FMT " " GID_FMT "\n", GID_NOBODY, GID_NOBODY, 1u); + if (r < 0) + return r; + + userns_fd = r = userns_acquire(uid_map, gid_map); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL) + return false; + if (r == -ENOSPC) { + log_debug_errno(r, "Failed to acquire new user namespace, user.max_user_namespaces seems to be exhausted or maybe even zero, assuming ID-mapping is not supported: %m"); + return false; + } + if (r < 0) + return log_debug_errno(r, "Failed to acquire new user namespace for checking if '%s' supports ID-mapping: %m", path); + + dir_fd = r = RET_NERRNO(open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) + return false; + if (r < 0) + return log_debug_errno(r, "Failed to open '%s', cannot determine if ID-mapping is supported: %m", path); + + mount_fd = r = RET_NERRNO(open_tree(dir_fd, "", AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC)); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL) + return false; + if (r < 0) + return log_debug_errno(r, "Failed to open mount tree '%s', cannot determine if ID-mapping is supported: %m", path); + + r = RET_NERRNO(mount_setattr(mount_fd, "", AT_EMPTY_PATH, + &(struct mount_attr) { + .attr_set = MOUNT_ATTR_IDMAP | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_RDONLY | MOUNT_ATTR_NODEV, + .userns_fd = userns_fd, + }, sizeof(struct mount_attr))); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r) || ERRNO_IS_NEG_PRIVILEGE(r) || r == -EINVAL) + return false; + if (r < 0) + return log_debug_errno(r, "Failed to set mount attribute to '%s', cannot determine if ID-mapping is supported: %m", path); + + return true; +} + +int netns_acquire(void) { + _cleanup_(sigkill_waitp) pid_t pid = 0; + _cleanup_close_ int netns_fd = -EBADF; + int r; + + /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills + * the process again. This way we have a netns fd that is not bound to any process. */ + + r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid); + if (r < 0) + return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m"); + if (r == 0) + /* Child. We do nothing here, just freeze until somebody kills us. */ + freeze(); + + r = namespace_open(pid, + /* ret_pidns_fd = */ NULL, + /* ret_mntns_fd = */ NULL, + &netns_fd, + /* ret_userns_fd = */ NULL, + /* ret_root_fd = */ NULL); + if (r < 0) + return log_debug_errno(r, "Failed to open netns fd: %m"); + + return TAKE_FD(netns_fd); +} + diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h index d1ac4f6ed4..58347cbf88 100644 --- a/src/basic/namespace-util.h +++ b/src/basic/namespace-util.h @@ -83,15 +83,15 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) { return true; } -int userns_acquire_empty(void); -int userns_acquire(const char *uid_map, const char *gid_map); - -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_acquire_empty(void); +int userns_acquire(const char *uid_map, const char *gid_map); int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid); int process_is_owned_by_uid(const PidRef *pidref, uid_t uid); + +int is_idmapping_supported(const char *path); + +int netns_acquire(void); From 8c2c8235a64be93e9e8d18927ad997efda156a95 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 Jan 2025 12:43:05 +0100 Subject: [PATCH 5/7] namespace-util: introduce userns_enter_and_pin() helper which generalizes forking a process into userns and freeze() Addresses https://github.com/systemd/systemd/pull/35833/files#r1905508153 --- src/basic/namespace-util.c | 23 ++++++++++++++++---- src/basic/namespace-util.h | 1 + src/basic/uid-range.c | 43 ++------------------------------------ 3 files changed, 22 insertions(+), 45 deletions(-) diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index 2ae5003459..6ece5108df 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -558,7 +558,7 @@ int userns_acquire(const char *uid_map, const char *gid_map) { return TAKE_FD(userns_fd); } -int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) { +int userns_enter_and_pin(int userns_fd, pid_t *ret_pid) { _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR; _cleanup_(sigkill_waitp) pid_t pid = 0; ssize_t n; @@ -566,12 +566,13 @@ int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) { int r; assert(userns_fd >= 0); + assert(ret_pid); if (pipe2(pfd, O_CLOEXEC) < 0) return -errno; r = safe_fork_full( - "(sd-baseuns)", + "(sd-pinuserns)", /* stdio_fds= */ NULL, (int[]) { pfd[1], userns_fd }, 2, FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL, @@ -608,6 +609,20 @@ int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) { assert(n == 1); assert(x == 'x'); + *ret_pid = TAKE_PID(pid); + return 0; +} + +int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) { + _cleanup_(sigkill_waitp) pid_t pid = 0; + int r; + + assert(userns_fd >= 0); + + r = userns_enter_and_pin(userns_fd, &pid); + if (r < 0) + return r; + uid_t uid; r = uid_map_search_root(pid, "uid_map", &uid); if (r < 0) @@ -630,13 +645,13 @@ int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) { } int process_is_owned_by_uid(const PidRef *pidref, uid_t uid) { - assert(uid_is_valid(uid)); - int r; /* Checks if the specified process either is owned directly by the specified user, or if it is inside * a user namespace owned by it. */ + assert(uid_is_valid(uid)); + uid_t process_uid; r = pidref_get_uid(pidref, &process_uid); if (r < 0) diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h index 58347cbf88..49e2de3bb3 100644 --- a/src/basic/namespace-util.h +++ b/src/basic/namespace-util.h @@ -87,6 +87,7 @@ int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_r int userns_acquire_empty(void); int userns_acquire(const char *uid_map, const char *gid_map); +int userns_enter_and_pin(int userns_fd, pid_t *ret_pid); 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 687ddacc57..cbae1bbf78 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -9,6 +9,7 @@ #include "fd-util.h" #include "format-util.h" #include "macro.h" +#include "namespace-util.h" #include "path-util.h" #include "process-util.h" #include "sort-util.h" @@ -264,10 +265,7 @@ 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) { - _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); @@ -275,46 +273,9 @@ int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange assert(mode < _UID_RANGE_USERNS_MODE_MAX); assert(ret); - if (pipe2(pfd, O_CLOEXEC) < 0) - return -errno; - - r = safe_fork_full( - "(sd-mkuserns)", - /* stdio_fds= */ NULL, - (int[]) { pfd[1], userns_fd }, 2, - FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL, - &pid); + r = userns_enter_and_pin(userns_fd, &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 fifo: %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'); const char *p = procfs_file_alloca( pid, From a0e3dbdef502558173bbafb7851377c1900b4a0b Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 Jan 2025 12:44:33 +0100 Subject: [PATCH 6/7] uid-range: make uid_map_search_root() take UIDRangeUsernsMode --- src/basic/namespace-util.c | 4 ++-- src/basic/uid-range.c | 6 +++--- src/basic/uid-range.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index 6ece5108df..8522123987 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -624,12 +624,12 @@ int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid) { return r; uid_t uid; - r = uid_map_search_root(pid, "uid_map", &uid); + r = uid_map_search_root(pid, UID_RANGE_USERNS_OUTSIDE, &uid); if (r < 0) return r; gid_t gid; - r = uid_map_search_root(pid, "gid_map", &gid); + r = uid_map_search_root(pid, GID_RANGE_USERNS_OUTSIDE, &gid); if (r < 0) return r; diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c index cbae1bbf78..99984d8a4e 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -324,13 +324,13 @@ 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 uid_map_search_root(pid_t pid, UIDRangeUsernsMode mode, uid_t *ret) { int r; assert(pid_is_valid(pid)); - assert(filename); + assert(IN_SET(mode, UID_RANGE_USERNS_OUTSIDE, GID_RANGE_USERNS_OUTSIDE)); - const char *p = procfs_file_alloca(pid, filename); + const char *p = procfs_file_alloca(pid, mode == UID_RANGE_USERNS_OUTSIDE ? "uid_map" : "gid_map"); _cleanup_fclose_ FILE *f = fopen(p, "re"); if (!f) { if (errno != ENOENT) diff --git a/src/basic/uid-range.h b/src/basic/uid-range.h index 41ca671dd6..21e5c7a17b 100644 --- a/src/basic/uid-range.h +++ b/src/basic/uid-range.h @@ -15,7 +15,7 @@ typedef struct UIDRange { size_t n_entries; } UIDRange; -UIDRange *uid_range_free(UIDRange *range); +UIDRange* uid_range_free(UIDRange *range); DEFINE_TRIVIAL_CLEANUP_FUNC(UIDRange*, uid_range_free); int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce); @@ -77,4 +77,4 @@ int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange 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); +int uid_map_search_root(pid_t pid, UIDRangeUsernsMode mode, uid_t *ret); From 95a5c658a3d7d7cc47ee20f5e0d0c4c059c60c66 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 Jan 2025 14:02:28 +0100 Subject: [PATCH 7/7] namespace-util: use pidref_namespace_open_by_type() where appropriate Several logs are dropped, since all callers log loudly already. --- src/basic/namespace-util.c | 49 ++++++++++---------------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index 8522123987..7be8b16c29 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -498,28 +498,22 @@ int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_r } int userns_acquire_empty(void) { - _cleanup_(sigkill_waitp) pid_t pid = 0; - _cleanup_close_ int userns_fd = -EBADF; + _cleanup_(pidref_done_sigkill_wait) PidRef pid = PIDREF_NULL; int r; - r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); + r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); if (r < 0) return r; if (r == 0) /* Child. We do nothing here, just freeze until somebody kills us. */ freeze(); - r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL); - if (r < 0) - return log_error_errno(r, "Failed to open userns fd: %m"); - - return TAKE_FD(userns_fd); + return pidref_namespace_open_by_type(&pid, NAMESPACE_USER); } int userns_acquire(const char *uid_map, const char *gid_map) { char path[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; - _cleanup_(sigkill_waitp) pid_t pid = 0; - _cleanup_close_ int userns_fd = -EBADF; + _cleanup_(pidref_done_sigkill_wait) PidRef pid = PIDREF_NULL; int r; assert(uid_map); @@ -529,33 +523,24 @@ int userns_acquire(const char *uid_map, const char *gid_map) { * and then kills the process again. This way we have a userns fd that is not bound to any * process. We can use that for file system mounts and similar. */ - r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); + r = pidref_safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); if (r < 0) return r; if (r == 0) /* Child. We do nothing here, just freeze until somebody kills us. */ freeze(); - xsprintf(path, "/proc/" PID_FMT "/uid_map", pid); + xsprintf(path, "/proc/" PID_FMT "/uid_map", pid.pid); r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) return log_debug_errno(r, "Failed to write UID map: %m"); - xsprintf(path, "/proc/" PID_FMT "/gid_map", pid); + xsprintf(path, "/proc/" PID_FMT "/gid_map", pid.pid); r = write_string_file(path, gid_map, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) return log_debug_errno(r, "Failed to write GID map: %m"); - r = namespace_open(pid, - /* ret_pidns_fd = */ NULL, - /* ret_mntns_fd = */ NULL, - /* ret_netns_fd = */ NULL, - &userns_fd, - /* ret_root_fd = */ NULL); - if (r < 0) - return log_debug_errno(r, "Failed to open userns fd: %m"); - - return TAKE_FD(userns_fd); + return pidref_namespace_open_by_type(&pid, NAMESPACE_USER); } int userns_enter_and_pin(int userns_fd, pid_t *ret_pid) { @@ -753,29 +738,19 @@ int is_idmapping_supported(const char *path) { } int netns_acquire(void) { - _cleanup_(sigkill_waitp) pid_t pid = 0; - _cleanup_close_ int netns_fd = -EBADF; + _cleanup_(pidref_done_sigkill_wait) PidRef pid = PIDREF_NULL; int r; /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills * the process again. This way we have a netns fd that is not bound to any process. */ - r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid); + r = pidref_safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid); if (r < 0) - return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m"); + return log_debug_errno(r, "Failed to fork process into new netns: %m"); if (r == 0) /* Child. We do nothing here, just freeze until somebody kills us. */ freeze(); - r = namespace_open(pid, - /* ret_pidns_fd = */ NULL, - /* ret_mntns_fd = */ NULL, - &netns_fd, - /* ret_userns_fd = */ NULL, - /* ret_root_fd = */ NULL); - if (r < 0) - return log_debug_errno(r, "Failed to open netns fd: %m"); - - return TAKE_FD(netns_fd); + return pidref_namespace_open_by_type(&pid, NAMESPACE_NET); }