mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +09:00
namespace-util: add helper to get base UID from userns
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user