return 0;
}
+
+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. */
+
+ uid_t process_uid;
+ r = pidref_get_uid(pidref, &process_uid);
+ if (r < 0)
+ return r;
+ if (process_uid == uid)
+ return true;
+
+ _cleanup_close_ int userns_fd = -EBADF;
+ userns_fd = pidref_namespace_open_by_type(pidref, NAMESPACE_USER);
+ if (userns_fd == -ENOPKG) /* If userns is not supported, then they don't matter for ownership */
+ return false;
+ if (userns_fd < 0)
+ return userns_fd;
+
+ for (unsigned iteration = 0;; iteration++) {
+ uid_t ns_uid;
+
+ /* This process is in our own userns? Then we are done, in our own userns only the UIDs
+ * themselves matter. */
+ r = is_our_namespace(userns_fd, NAMESPACE_USER);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return false;
+
+ if (ioctl(userns_fd, NS_GET_OWNER_UID, &ns_uid) < 0)
+ return -errno;
+ if (ns_uid == uid)
+ return true;
+
+ /* Paranoia check */
+ if (iteration > 16)
+ return log_debug_errno(SYNTHETIC_ERRNO(ELOOP), "Giving up while tracing parents of user namespaces after %u steps.", iteration);
+
+ /* Go up the tree */
+ _cleanup_close_ int parent_fd = ioctl(userns_fd, NS_GET_USERNS);
+ if (parent_fd < 0) {
+ if (errno == EPERM) /* EPERM means we left our own userns */
+ return false;
+
+ return -errno;
+ }
+
+ close_and_replace(userns_fd, parent_fd);
+ }
+}
#include "alloc-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "namespace.h"
#include "process-util.h"
#include "string-util.h"
#include "tests.h"
+#include "uid-range.h"
#include "user-util.h"
#include "virt.h"
ASSERT_ERROR(userns_get_base_uid(fd, &base_uid, &base_gid), ENOMSG);
}
+TEST(process_is_owned_by_uid) {
+ int r;
+
+ /* Test our own PID */
+ _cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
+ ASSERT_OK(pidref_set_self(&pid));
+ ASSERT_OK_POSITIVE(process_is_owned_by_uid(&pid, getuid()));
+ pidref_done(&pid);
+
+ if (getuid() != 0)
+ return (void) log_tests_skipped("lacking userns privileges");
+
+ _cleanup_(uid_range_freep) UIDRange *range = NULL;
+ ASSERT_OK(uid_range_load_userns(/* path= */ NULL, UID_RANGE_USERNS_INSIDE, &range));
+ if (!uid_range_contains(range, 1))
+ return (void) log_tests_skipped("UID 1 not included in userns UID delegation, skipping test");
+
+ /* Test a child that runs as uid 1 */
+ _cleanup_close_pair_ int p[2] = EBADF_PAIR;
+ ASSERT_OK_ERRNO(pipe2(p, O_CLOEXEC));
+
+ r = pidref_safe_fork("(child)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, &pid);
+ ASSERT_OK(r);
+ if (r == 0) {
+ p[0] = safe_close(p[0]);
+ ASSERT_OK(fully_set_uid_gid(1, 1, NULL, 0));
+ ASSERT_OK_EQ_ERRNO(write(p[1], &(const char[]) { 'x' }, 1), 1);
+ p[1] = safe_close(p[1]);
+ freeze();
+ }
+
+ p[1] = safe_close(p[1]);
+ char x = 0;
+ ASSERT_OK_EQ_ERRNO(read(p[0], &x, 1), 1);
+ ASSERT_EQ(x, 'x');
+ p[0] = safe_close(p[0]);
+
+ ASSERT_OK_ZERO(process_is_owned_by_uid(&pid, getuid()));
+
+ ASSERT_OK(pidref_kill(&pid, SIGKILL));
+ ASSERT_OK(pidref_wait_for_terminate(&pid, /* ret= */ NULL));
+
+ /* Test a child that runs in a userns as uid 1, but the userns is owned by us */
+ ASSERT_OK_ERRNO(pipe2(p, O_CLOEXEC));
+
+ _cleanup_close_pair_ int pp[2] = EBADF_PAIR;
+ ASSERT_OK_ERRNO(pipe2(pp, O_CLOEXEC));
+
+ r = pidref_safe_fork("(child)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
+ ASSERT_OK(r);
+ if (r == 0) {
+ p[0] = safe_close(p[0]);
+ pp[1] = safe_close(pp[1]);
+
+ x = 0;
+ ASSERT_OK_EQ_ERRNO(read(pp[0], &x, 1), 1);
+ ASSERT_EQ(x, 'x');
+ pp[0] = safe_close(pp[0]);
+
+ ASSERT_OK(reset_uid_gid());
+
+ ASSERT_OK_EQ_ERRNO(write(p[1], &(const char[]) { 'x' }, 1), 1);
+ p[1] = safe_close(p[1]);
+ freeze();
+ }
+
+ p[1] = safe_close(p[1]);
+ pp[0] = safe_close(pp[0]);
+
+ ASSERT_OK(write_string_file(procfs_file_alloca(pid.pid, "uid_map"), "0 1 1\n", 0));
+ ASSERT_OK(write_string_file(procfs_file_alloca(pid.pid, "setgroups"), "deny", 0));
+ ASSERT_OK(write_string_file(procfs_file_alloca(pid.pid, "gid_map"), "0 1 1\n", 0));
+
+ ASSERT_OK_EQ_ERRNO(write(pp[1], &(const char[]) { 'x' }, 1), 1);
+ pp[1] = safe_close(pp[1]);
+
+ x = 0;
+ ASSERT_OK_EQ_ERRNO(read(p[0], &x, 1), 1);
+ ASSERT_EQ(x, 'x');
+ p[0] = safe_close(p[0]);
+
+ ASSERT_OK_POSITIVE(process_is_owned_by_uid(&pid, getuid()));
+
+ ASSERT_OK(pidref_kill(&pid, SIGKILL));
+ ASSERT_OK(pidref_wait_for_terminate(&pid, /* ret= */ NULL));
+}
+
static int intro(void) {
if (!have_namespaces())
return log_tests_skipped("Don't have namespace support or lacking privileges");