// SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE #include #include #include #include #include #include #include #include "../kselftest_harness.h" /* * The fields of a uid_map/gid_map line are stored in u32. Values that * do not fit, and negative values, must be rejected with EINVAL instead * of being silently truncated, and in-range values must still be * accepted. */ static int write_file(const char *path, const char *buf) { ssize_t len = strlen(buf); ssize_t ret; int fd; fd = open(path, O_WRONLY | O_CLOEXEC); if (fd < 0) return -errno; ret = write(fd, buf, len); if (ret < 0) ret = -errno; close(fd); if (ret < 0) return ret; return ret == len ? 0 : -EIO; } static ssize_t read_file(const char *path, char *buf, size_t size) { ssize_t ret; int fd; fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) return -errno; ret = read(fd, buf, size - 1); if (ret < 0) ret = -errno; else buf[ret] = '\0'; close(fd); return ret; } /* Enter a fresh user namespace, remembering the outer ids. */ static int enter_userns(uid_t *uid, gid_t *gid) { *uid = geteuid(); *gid = getegid(); if (unshare(CLONE_NEWUSER) < 0) return -errno; /* Required before an unprivileged gid_map write. */ return write_file("/proc/self/setgroups", "deny"); } static void expect_rejected(struct __test_metadata *_metadata, const char *path, const char *line) { char buf[256]; TH_LOG("writing \"%s\" to %s", line, path); EXPECT_EQ(write_file(path, line), -EINVAL); /* Nothing may have been installed. */ EXPECT_EQ(read_file(path, buf, sizeof(buf)), 0) { TH_LOG("installed: %s", buf); } } static void expect_accepted(struct __test_metadata *_metadata, const char *path, const char *line, unsigned int first, unsigned int lower_first, unsigned int count) { unsigned int f, l, c; char buf[256]; TH_LOG("writing \"%s\" to %s", line, path); ASSERT_EQ(write_file(path, line), 0); ASSERT_GT(read_file(path, buf, sizeof(buf)), 0); ASSERT_EQ(sscanf(buf, "%u %u %u", &f, &l, &c), 3); EXPECT_EQ(f, first); EXPECT_EQ(l, lower_first); EXPECT_EQ(c, count); /* Exactly one extent. */ EXPECT_EQ(strchr(buf, '\n'), buf + strlen(buf) - 1); } TEST(uid_map_first_above_u32) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "4294967301 %u 1", uid); expect_rejected(_metadata, "/proc/self/uid_map", line); } TEST(uid_map_lower_first_above_u32) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "0 %llu 1", (1ULL << 32) + uid); expect_rejected(_metadata, "/proc/self/uid_map", line); } TEST(uid_map_count_above_u32) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "0 %u 4294967297", uid); expect_rejected(_metadata, "/proc/self/uid_map", line); } TEST(gid_map_first_above_u32) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "4294967301 %u 1", gid); expect_rejected(_metadata, "/proc/self/gid_map", line); } TEST(uid_map_first_negative) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "-1 %u 1", uid); expect_rejected(_metadata, "/proc/self/uid_map", line); } TEST(uid_map_lower_first_negative) { uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); expect_rejected(_metadata, "/proc/self/uid_map", "0 -1 1"); } TEST(uid_map_count_negative) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "0 %u -1", uid); expect_rejected(_metadata, "/proc/self/uid_map", line); } TEST(uid_map_in_range) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "5 %u 1", uid); expect_accepted(_metadata, "/proc/self/uid_map", line, 5, uid, 1); } TEST(uid_map_zero) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "0 %u 1", uid); expect_accepted(_metadata, "/proc/self/uid_map", line, 0, uid, 1); } /* 4294967295 is (u32)-1, which map_write() refuses as a starting id. */ TEST(uid_map_largest_first) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "4294967294 %u 1", uid); expect_accepted(_metadata, "/proc/self/uid_map", line, 4294967294U, uid, 1); } TEST(uid_map_extra_spaces) { char line[64]; uid_t uid; gid_t gid; int ret; ret = enter_userns(&uid, &gid); if (ret) SKIP(return, "cannot set up user namespace: %s", strerror(-ret)); snprintf(line, sizeof(line), "0 %u 1", uid); expect_accepted(_metadata, "/proc/self/uid_map", line, 0, uid, 1); } /* * An unprivileged writer may only map a single id, so the largest count * needs a privileged parent writing the map of a child namespace. With * first and lower_first 0, the no-wrap rule allows up to 4294967295. */ TEST(uid_map_largest_count) { unsigned int first, lower_first, count; int to_child[2], from_child[2]; char path[64], buf[256]; pid_t pid; char c; if (geteuid() != 0) SKIP(return, "needs root in the initial user namespace"); ASSERT_GT(read_file("/proc/self/uid_map", buf, sizeof(buf)), 0); ASSERT_EQ(sscanf(buf, "%u %u %u", &first, &lower_first, &count), 3); if (first != 0 || lower_first != 0 || count != 4294967295U) SKIP(return, "not in the initial user namespace"); ASSERT_EQ(pipe(to_child), 0); ASSERT_EQ(pipe(from_child), 0); pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { close(to_child[1]); close(from_child[0]); c = unshare(CLONE_NEWUSER) ? 'F' : 'K'; if (write(from_child[1], &c, 1) != 1) _exit(1); /* Stay alive until the parent is done with our uid_map. */ if (read(to_child[0], &c, 1) < 0) _exit(1); _exit(0); } close(to_child[0]); close(from_child[1]); ASSERT_EQ(read(from_child[0], &c, 1), 1); ASSERT_EQ(c, 'K'); snprintf(path, sizeof(path), "/proc/%d/uid_map", pid); expect_accepted(_metadata, path, "0 0 4294967295", 0, 0, 4294967295U); close(to_child[1]); EXPECT_EQ(waitpid(pid, NULL, 0), pid); } TEST_HARNESS_MAIN