| 1 | |
| 2 | #define _GNU_SOURCE
|
| 3 | #include <errno.h>
|
| 4 | #include <fcntl.h>
|
| 5 | #include <sched.h>
|
| 6 | #include <stdio.h>
|
| 7 | #include <string.h>
|
| 8 | #include <sys/wait.h>
|
| 9 | #include <unistd.h>
|
| 10 | #include "../kselftest_harness.h"
|
| 11 |
|
| 12 | |
| 13 | * The fields of a uid_map/gid_map line are stored in u32. Values that
|
| 14 | * do not fit, and negative values, must be rejected with EINVAL instead
|
| 15 | * of being silently truncated, and in-range values must still be
|
| 16 | * accepted.
|
| 17 |
|
| 18 |
|
| 19 | static int write_file(const char *path, const char *buf)
|
| 20 | {
|
| 21 | ssize_t len = strlen(buf);
|
| 22 | ssize_t ret;
|
| 23 | int fd;
|
| 24 |
|
| 25 | fd = open(path, O_WRONLY | O_CLOEXEC);
|
| 26 | if (fd < 0)
|
| 27 | return -errno;
|
| 28 | ret = write(fd, buf, len);
|
| 29 | if (ret < 0)
|
| 30 | ret = -errno;
|
| 31 | close(fd);
|
| 32 | if (ret < 0)
|
| 33 | return ret;
|
| 34 | return ret == len ? 0 : -EIO;
|
| 35 | }
|
| 36 |
|
| 37 | static ssize_t read_file(const char *path, char *buf, size_t size)
|
| 38 | {
|
| 39 | ssize_t ret;
|
| 40 | int fd;
|
| 41 |
|
| 42 | fd = open(path, O_RDONLY | O_CLOEXEC);
|
| 43 | if (fd < 0)
|
| 44 | return -errno;
|
| 45 | ret = read(fd, buf, size - 1);
|
| 46 | if (ret < 0)
|
| 47 | ret = -errno;
|
| 48 | else
|
| 49 | buf[ret] = '\0';
|
| 50 | close(fd);
|
| 51 | return ret;
|
| 52 | }
|
| 53 |
|
| 54 |
|
| 55 | static int enter_userns(uid_t *uid, gid_t *gid)
|
| 56 | {
|
| 57 | *uid = geteuid();
|
| 58 | *gid = getegid();
|
| 59 | if (unshare(CLONE_NEWUSER) < 0)
|
| 60 | return -errno;
|
| 61 |
|
| 62 | return write_file("/proc/self/setgroups", "deny");
|
| 63 | }
|
| 64 |
|
| 65 | static void expect_rejected(struct __test_metadata *_metadata,
|
| 66 | const char *path, const char *line)
|
| 67 | {
|
| 68 | char buf[256];
|
| 69 |
|
| 70 | TH_LOG("writing \"%s\" to %s", line, path);
|
| 71 | EXPECT_EQ(write_file(path, line), -EINVAL);
|
| 72 |
|
| 73 | EXPECT_EQ(read_file(path, buf, sizeof(buf)), 0) {
|
| 74 | TH_LOG("installed: %s", buf);
|
| 75 | }
|
| 76 | }
|
| 77 |
|
| 78 | static void expect_accepted(struct __test_metadata *_metadata,
|
| 79 | const char *path, const char *line,
|
| 80 | unsigned int first, unsigned int lower_first,
|
| 81 | unsigned int count)
|
| 82 | {
|
| 83 | unsigned int f, l, c;
|
| 84 | char buf[256];
|
| 85 |
|
| 86 | TH_LOG("writing \"%s\" to %s", line, path);
|
| 87 | ASSERT_EQ(write_file(path, line), 0);
|
| 88 |
|
| 89 | ASSERT_GT(read_file(path, buf, sizeof(buf)), 0);
|
| 90 | ASSERT_EQ(sscanf(buf, "%u %u %u", &f, &l, &c), 3);
|
| 91 | EXPECT_EQ(f, first);
|
| 92 | EXPECT_EQ(l, lower_first);
|
| 93 | EXPECT_EQ(c, count);
|
| 94 |
|
| 95 | EXPECT_EQ(strchr(buf, '\n'), buf + strlen(buf) - 1);
|
| 96 | }
|
| 97 |
|
| 98 | TEST(uid_map_first_above_u32)
|
| 99 | {
|
| 100 | char line[64];
|
| 101 | uid_t uid;
|
| 102 | gid_t gid;
|
| 103 | int ret;
|
| 104 |
|
| 105 | ret = enter_userns(&uid, &gid);
|
| 106 | if (ret)
|
| 107 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 108 | snprintf(line, sizeof(line), "4294967301 %u 1", uid);
|
| 109 | expect_rejected(_metadata, "/proc/self/uid_map", line);
|
| 110 | }
|
| 111 |
|
| 112 | TEST(uid_map_lower_first_above_u32)
|
| 113 | {
|
| 114 | char line[64];
|
| 115 | uid_t uid;
|
| 116 | gid_t gid;
|
| 117 | int ret;
|
| 118 |
|
| 119 | ret = enter_userns(&uid, &gid);
|
| 120 | if (ret)
|
| 121 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 122 | snprintf(line, sizeof(line), "0 %llu 1", (1ULL << 32) + uid);
|
| 123 | expect_rejected(_metadata, "/proc/self/uid_map", line);
|
| 124 | }
|
| 125 |
|
| 126 | TEST(uid_map_count_above_u32)
|
| 127 | {
|
| 128 | char line[64];
|
| 129 | uid_t uid;
|
| 130 | gid_t gid;
|
| 131 | int ret;
|
| 132 |
|
| 133 | ret = enter_userns(&uid, &gid);
|
| 134 | if (ret)
|
| 135 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 136 | snprintf(line, sizeof(line), "0 %u 4294967297", uid);
|
| 137 | expect_rejected(_metadata, "/proc/self/uid_map", line);
|
| 138 | }
|
| 139 |
|
| 140 | TEST(gid_map_first_above_u32)
|
| 141 | {
|
| 142 | char line[64];
|
| 143 | uid_t uid;
|
| 144 | gid_t gid;
|
| 145 | int ret;
|
| 146 |
|
| 147 | ret = enter_userns(&uid, &gid);
|
| 148 | if (ret)
|
| 149 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 150 | snprintf(line, sizeof(line), "4294967301 %u 1", gid);
|
| 151 | expect_rejected(_metadata, "/proc/self/gid_map", line);
|
| 152 | }
|
| 153 |
|
| 154 | TEST(uid_map_first_negative)
|
| 155 | {
|
| 156 | char line[64];
|
| 157 | uid_t uid;
|
| 158 | gid_t gid;
|
| 159 | int ret;
|
| 160 |
|
| 161 | ret = enter_userns(&uid, &gid);
|
| 162 | if (ret)
|
| 163 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 164 | snprintf(line, sizeof(line), "-1 %u 1", uid);
|
| 165 | expect_rejected(_metadata, "/proc/self/uid_map", line);
|
| 166 | }
|
| 167 |
|
| 168 | TEST(uid_map_lower_first_negative)
|
| 169 | {
|
| 170 | uid_t uid;
|
| 171 | gid_t gid;
|
| 172 | int ret;
|
| 173 |
|
| 174 | ret = enter_userns(&uid, &gid);
|
| 175 | if (ret)
|
| 176 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 177 | expect_rejected(_metadata, "/proc/self/uid_map", "0 -1 1");
|
| 178 | }
|
| 179 |
|
| 180 | TEST(uid_map_count_negative)
|
| 181 | {
|
| 182 | char line[64];
|
| 183 | uid_t uid;
|
| 184 | gid_t gid;
|
| 185 | int ret;
|
| 186 |
|
| 187 | ret = enter_userns(&uid, &gid);
|
| 188 | if (ret)
|
| 189 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 190 | snprintf(line, sizeof(line), "0 %u -1", uid);
|
| 191 | expect_rejected(_metadata, "/proc/self/uid_map", line);
|
| 192 | }
|
| 193 |
|
| 194 | TEST(uid_map_in_range)
|
| 195 | {
|
| 196 | char line[64];
|
| 197 | uid_t uid;
|
| 198 | gid_t gid;
|
| 199 | int ret;
|
| 200 |
|
| 201 | ret = enter_userns(&uid, &gid);
|
| 202 | if (ret)
|
| 203 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 204 | snprintf(line, sizeof(line), "5 %u 1", uid);
|
| 205 | expect_accepted(_metadata, "/proc/self/uid_map", line, 5, uid, 1);
|
| 206 | }
|
| 207 |
|
| 208 | TEST(uid_map_zero)
|
| 209 | {
|
| 210 | char line[64];
|
| 211 | uid_t uid;
|
| 212 | gid_t gid;
|
| 213 | int ret;
|
| 214 |
|
| 215 | ret = enter_userns(&uid, &gid);
|
| 216 | if (ret)
|
| 217 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 218 | snprintf(line, sizeof(line), "0 %u 1", uid);
|
| 219 | expect_accepted(_metadata, "/proc/self/uid_map", line, 0, uid, 1);
|
| 220 | }
|
| 221 |
|
| 222 |
|
| 223 | TEST(uid_map_largest_first)
|
| 224 | {
|
| 225 | char line[64];
|
| 226 | uid_t uid;
|
| 227 | gid_t gid;
|
| 228 | int ret;
|
| 229 |
|
| 230 | ret = enter_userns(&uid, &gid);
|
| 231 | if (ret)
|
| 232 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 233 | snprintf(line, sizeof(line), "4294967294 %u 1", uid);
|
| 234 | expect_accepted(_metadata, "/proc/self/uid_map", line,
|
| 235 | 4294967294U, uid, 1);
|
| 236 | }
|
| 237 |
|
| 238 | TEST(uid_map_extra_spaces)
|
| 239 | {
|
| 240 | char line[64];
|
| 241 | uid_t uid;
|
| 242 | gid_t gid;
|
| 243 | int ret;
|
| 244 |
|
| 245 | ret = enter_userns(&uid, &gid);
|
| 246 | if (ret)
|
| 247 | SKIP(return, "cannot set up user namespace: %s", strerror(-ret));
|
| 248 | snprintf(line, sizeof(line), "0 %u 1", uid);
|
| 249 | expect_accepted(_metadata, "/proc/self/uid_map", line, 0, uid, 1);
|
| 250 | }
|
| 251 |
|
| 252 | |
| 253 | * An unprivileged writer may only map a single id, so the largest count
|
| 254 | * needs a privileged parent writing the map of a child namespace. With
|
| 255 | * first and lower_first 0, the no-wrap rule allows up to 4294967295.
|
| 256 |
|
| 257 | TEST(uid_map_largest_count)
|
| 258 | {
|
| 259 | unsigned int first, lower_first, count;
|
| 260 | int to_child[2], from_child[2];
|
| 261 | char path[64], buf[256];
|
| 262 | pid_t pid;
|
| 263 | char c;
|
| 264 |
|
| 265 | if (geteuid() != 0)
|
| 266 | SKIP(return, "needs root in the initial user namespace");
|
| 267 | ASSERT_GT(read_file("/proc/self/uid_map", buf, sizeof(buf)), 0);
|
| 268 | ASSERT_EQ(sscanf(buf, "%u %u %u", &first, &lower_first, &count), 3);
|
| 269 | if (first != 0 || lower_first != 0 || count != 4294967295U)
|
| 270 | SKIP(return, "not in the initial user namespace");
|
| 271 |
|
| 272 | ASSERT_EQ(pipe(to_child), 0);
|
| 273 | ASSERT_EQ(pipe(from_child), 0);
|
| 274 | pid = fork();
|
| 275 | ASSERT_GE(pid, 0);
|
| 276 | if (pid == 0) {
|
| 277 | close(to_child[1]);
|
| 278 | close(from_child[0]);
|
| 279 | c = unshare(CLONE_NEWUSER) ? 'F' : 'K';
|
| 280 | if (write(from_child[1], &c, 1) != 1)
|
| 281 | _exit(1);
|
| 282 |
|
| 283 | if (read(to_child[0], &c, 1) < 0)
|
| 284 | _exit(1);
|
| 285 | _exit(0);
|
| 286 | }
|
| 287 | close(to_child[0]);
|
| 288 | close(from_child[1]);
|
| 289 |
|
| 290 | ASSERT_EQ(read(from_child[0], &c, 1), 1);
|
| 291 | ASSERT_EQ(c, 'K');
|
| 292 | snprintf(path, sizeof(path), "/proc/%d/uid_map", pid);
|
| 293 | expect_accepted(_metadata, path, "0 0 4294967295", 0, 0, 4294967295U);
|
| 294 |
|
| 295 | close(to_child[1]);
|
| 296 | EXPECT_EQ(waitpid(pid, NULL, 0), pid);
|
| 297 | }
|
| 298 |
|
| 299 | TEST_HARNESS_MAIN
|