#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] = {
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;
+}
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);
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);
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;
}
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;
+ }
+ }
+}
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);
}
}
+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");