From: Lennart Poettering Date: Thu, 20 Apr 2023 16:51:55 +0000 (+0200) Subject: uid-range: add new uid_range_load_userns_by_fd() helper X-Git-Tag: v256-rc1~283^2~25 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=20ba086e775838112e777f8f6dc3c03af4f3d772;p=thirdparty%2Fsystemd.git uid-range: add new uid_range_load_userns_by_fd() helper This is similar to uid_range_load_userns() but instead of reading the uid_map off a process it reads it off a userns fd. (Of course the kernel has no API for this right now, hence we fork off a throw-away process which joins the user namespace, and then read off the data from there.) --- diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c index 4c35df5ea88..f1b086711d8 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -10,6 +10,7 @@ #include "format-util.h" #include "macro.h" #include "path-util.h" +#include "process-util.h" #include "sort-util.h" #include "stat-util.h" #include "uid-range.h" @@ -260,6 +261,66 @@ int uid_range_load_userns(UIDRange **ret, const char *path, UIDRangeUsernsMode m return 0; } +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); + assert(mode >= 0); + 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); + 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, + IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map"); + + return uid_range_load_userns(ret, p, mode); +} + bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) { if (!range) diff --git a/src/basic/uid-range.h b/src/basic/uid-range.h index d6b62f23651..f4d3d41db51 100644 --- a/src/basic/uid-range.h +++ b/src/basic/uid-range.h @@ -43,5 +43,6 @@ typedef enum UIDRangeUsernsMode { } UIDRangeUsernsMode; int uid_range_load_userns(UIDRange **ret, const char *path, UIDRangeUsernsMode mode); +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);