1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <sched.h> 7 #include <stdio.h> 8 #include <unistd.h> 9 #include <sys/stat.h> 10 #include <sys/syscall.h> 11 12 #include <linux/mount.h> 13 #include <linux/types.h> 14 15 #include "kselftest_harness.h" 16 #include "../wrappers.h" 17 #include "../utils.h" 18 19 /* 20 * An idmapping that maps the mount-visible id range [0, ID_RANGE) onto the 21 * host/overlay-final id range [ID_HOST, ID_HOST + ID_RANGE). Through such an 22 * idmapped overlay mount, an overlay-final id of ID_HOST + n is reported as n, 23 * and an id of n requested through the mount is stored as ID_HOST + n. 24 */ 25 #define ID_NS 0 26 #define ID_HOST 10000 27 #define ID_RANGE 10000 28 29 /* 30 * For the composition test the lower layer's on-disk ids live in a 31 * separate range and are mapped by an idmapped lower layer onto the 32 * overlay-final range [ID_HOST, ID_HOST + ID_RANGE). 33 */ 34 #define LAYER_HOST 20000 35 36 #ifndef MOUNT_ATTR_IDMAP 37 #define MOUNT_ATTR_IDMAP 0x00100000 38 #endif 39 40 #ifndef __NR_mount_setattr 41 #define __NR_mount_setattr 442 42 #endif 43 44 static inline int sys_mount_setattr(int dfd, const char *path, 45 unsigned int flags, 46 struct mount_attr *attr, size_t size) 47 { 48 return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); 49 } 50 51 static bool ovl_supported(void) 52 { 53 int fd = sys_fsopen("overlay", 0); 54 55 if (fd < 0) 56 return false; 57 close(fd); 58 return true; 59 } 60 61 /* base/{l,u,w} owned by ID_HOST so they map to ID_NS through the idmap. */ 62 static int setup_layers(const char *base) 63 { 64 static const char *sub[] = { "", "/l", "/u", "/w" }; 65 char path[PATH_MAX]; 66 67 for (size_t i = 0; i < ARRAY_SIZE(sub); i++) { 68 snprintf(path, sizeof(path), "%s%s", base, sub[i]); 69 if (mkdir(path, 0755) && errno != EEXIST) 70 return -1; 71 if (i && chown(path, ID_HOST, ID_HOST)) 72 return -1; 73 } 74 return 0; 75 } 76 77 static int ovl_mount(const char *base, bool nfs_export) 78 { 79 char lower[PATH_MAX], upper[PATH_MAX], work[PATH_MAX]; 80 int fsfd, ovl; 81 82 snprintf(lower, sizeof(lower), "%s/l", base); 83 snprintf(upper, sizeof(upper), "%s/u", base); 84 snprintf(work, sizeof(work), "%s/w", base); 85 86 fsfd = sys_fsopen("overlay", 0); 87 if (fsfd < 0) 88 return -1; 89 90 if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) || 91 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "lowerdir", lower, 0) || 92 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", upper, 0) || 93 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", work, 0)) 94 goto err; 95 if (nfs_export && 96 (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "index", "on", 0) || 97 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "nfs_export", "on", 0))) 98 goto err; 99 if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) 100 goto err; 101 102 ovl = sys_fsmount(fsfd, 0, 0); 103 close(fsfd); 104 return ovl; 105 err: 106 close(fsfd); 107 return -1; 108 } 109 110 /* Idmap the (still detached, not yet visible) overlay mount @mfd. */ 111 static int ovl_idmap(int mfd) 112 { 113 struct mount_attr attr = { 114 .attr_set = MOUNT_ATTR_IDMAP, 115 }; 116 int ret, userns_fd; 117 118 /* 119 * get_userns_fd(fs_id, mount_id, range): a file whose filesystem id 120 * is fs_id + n is shown through the idmapped mount as mount_id + n. 121 * Here the overlay-final (fs side) range is [ID_HOST, ..) and the 122 * caller-visible (mount side) range is [ID_NS, ..). 123 */ 124 userns_fd = get_userns_fd(ID_HOST, ID_NS, ID_RANGE); 125 if (userns_fd < 0) 126 return -1; 127 128 attr.userns_fd = userns_fd; 129 ret = sys_mount_setattr(mfd, "", AT_EMPTY_PATH, &attr, sizeof(attr)); 130 close(userns_fd); 131 return ret; 132 } 133 134 /* Clone @path into a detached, idmapped mount usable as an overlay layer. */ 135 static int idmapped_layer_fd(const char *path, int nsid, int hostid, int range) 136 { 137 struct mount_attr attr = { 138 .attr_set = MOUNT_ATTR_IDMAP, 139 }; 140 int fd_tree, userns_fd; 141 142 fd_tree = sys_open_tree(AT_FDCWD, path, 143 OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); 144 if (fd_tree < 0) 145 return -1; 146 userns_fd = get_userns_fd(nsid, hostid, range); 147 if (userns_fd < 0) { 148 close(fd_tree); 149 return -1; 150 } 151 attr.userns_fd = userns_fd; 152 if (sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH, &attr, 153 sizeof(attr))) { 154 close(userns_fd); 155 close(fd_tree); 156 return -1; 157 } 158 close(userns_fd); 159 return fd_tree; 160 } 161 162 /* Overlay with a layer passed by fd (idmapped) plus a plain upper/work. */ 163 static int ovl_mount_lower_fd(const char *upper, const char *work, int fd_lower) 164 { 165 int fsfd, ovl; 166 167 fsfd = sys_fsopen("overlay", 0); 168 if (fsfd < 0) 169 return -1; 170 171 if (sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "test", 0) || 172 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "upperdir", upper, 0) || 173 sys_fsconfig(fsfd, FSCONFIG_SET_STRING, "workdir", work, 0) || 174 sys_fsconfig(fsfd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower) || 175 sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) 176 goto err; 177 178 ovl = sys_fsmount(fsfd, 0, 0); 179 close(fsfd); 180 return ovl; 181 err: 182 close(fsfd); 183 return -1; 184 } 185 186 /* 187 * Mount an overlay inside user namespace @u1 (so the overlay sb's s_user_ns is 188 * not the initial namespace) and idmap that overlay mount with @u2. Runs in a 189 * child that joins @u1; returns 0 on success. 190 */ 191 static int userns_overlay_child(int u1) 192 { 193 struct mount_attr attr = { 194 .attr_set = MOUNT_ATTR_IDMAP, 195 }; 196 struct stat st; 197 int ovl, u2; 198 199 /* Become root in the overlay sb's user namespace u1. */ 200 if (!switch_userns(u1, 0, 0, false)) 201 return fprintf(stderr, "userns: switch_userns: %m\n"), -1; 202 if (unshare(CLONE_NEWNS) || 203 sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) 204 return fprintf(stderr, "userns: unshare/slave: %m\n"), -1; 205 if (sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL)) 206 return fprintf(stderr, "userns: mount tmpfs: %m\n"), -1; 207 if (setup_layers("/tmp/ovl")) 208 return fprintf(stderr, "userns: setup_layers: %m\n"), -1; 209 if (mknod("/tmp/ovl/l/file", S_IFREG | 0644, 0) || 210 chown("/tmp/ovl/l/file", ID_HOST + 5, ID_HOST + 5)) 211 return fprintf(stderr, "userns: lower file: %m\n"), -1; 212 213 ovl = ovl_mount("/tmp/ovl", false); 214 if (ovl < 0) 215 return fprintf(stderr, "userns: ovl_mount: %m\n"), -1; 216 217 /* 218 * mount_setattr() requires CAP_SYS_ADMIN over the idmap user 219 * namespace, so it must be a child of u1. Create it now, from 220 * inside u1. 221 */ 222 u2 = get_userns_fd(ID_HOST, ID_NS, ID_RANGE); 223 if (u2 < 0) 224 return fprintf(stderr, "userns: get_userns_fd: %m\n"), -1; 225 attr.userns_fd = u2; 226 if (sys_mount_setattr(ovl, "", AT_EMPTY_PATH, &attr, sizeof(attr))) 227 return fprintf(stderr, "userns: mount_setattr: %m\n"), -1; 228 close(u2); 229 230 if (fstatat(ovl, "file", &st, 0)) 231 return fprintf(stderr, "userns: fstatat: %m\n"), -1; 232 if (st.st_uid != ID_NS + 5 || st.st_gid != ID_NS + 5) { 233 fprintf(stderr, "userns: got %u:%u expected %u:%u\n", 234 st.st_uid, st.st_gid, ID_NS + 5, ID_NS + 5); 235 return -1; 236 } 237 return 0; 238 } 239 240 FIXTURE(idmapped_overlay) { 241 char base[64]; 242 }; 243 244 FIXTURE_SETUP(idmapped_overlay) 245 { 246 /* Private mount namespace so test mounts need no cleanup. */ 247 ASSERT_EQ(unshare(CLONE_NEWNS), 0); 248 ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0); 249 250 /* tmpfs for the layers so we can chown them to arbitrary ids. */ 251 ASSERT_EQ(sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL), 0); 252 253 snprintf(self->base, sizeof(self->base), "/tmp/ovl"); 254 ASSERT_EQ(setup_layers(self->base), 0); 255 } 256 257 FIXTURE_TEARDOWN(idmapped_overlay) 258 { 259 } 260 261 /* A file owned by ID_HOST + 5 is reported as ID_NS + 5 through the idmap. */ 262 TEST_F(idmapped_overlay, getattr) 263 { 264 char path[PATH_MAX]; 265 struct stat st; 266 int ovl; 267 268 if (!ovl_supported()) 269 SKIP(return, "overlayfs not supported"); 270 271 snprintf(path, sizeof(path), "%s/l/file", self->base); 272 ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0); 273 ASSERT_EQ(chown(path, ID_HOST + 5, ID_HOST + 5), 0); 274 275 ovl = ovl_mount(self->base, false); 276 ASSERT_GE(ovl, 0); 277 ASSERT_EQ(ovl_idmap(ovl), 0); 278 279 ASSERT_EQ(fstatat(ovl, "file", &st, 0), 0); 280 EXPECT_EQ(st.st_uid, ID_NS + 5); 281 EXPECT_EQ(st.st_gid, ID_NS + 5); 282 283 EXPECT_EQ(close(ovl), 0); 284 } 285 286 /* 287 * Every creation path initializes the new owner through the mount idmap: 288 * created as caller id ID_NS, stored on the upper layer as overlay-final 289 * ID_HOST. Covers ovl_create() (regular file), ovl_mkdir(), ovl_mknod() 290 * and ovl_symlink() (which share ovl_create_object()), plus the separate 291 * ovl_tmpfile() path. 292 */ 293 TEST_F(idmapped_overlay, create) 294 { 295 static const char *names[] = { "reg", "dir", "fifo", "lnk" }; 296 char path[PATH_MAX]; 297 struct stat st; 298 int ovl, fd; 299 300 if (!ovl_supported()) 301 SKIP(return, "overlayfs not supported"); 302 303 ovl = ovl_mount(self->base, false); 304 ASSERT_GE(ovl, 0); 305 ASSERT_EQ(ovl_idmap(ovl), 0); 306 307 /* One object per creation operation, all as caller id ID_NS. */ 308 fd = openat(ovl, "reg", O_CREAT | O_WRONLY | O_EXCL, 0644); 309 ASSERT_GE(fd, 0); 310 EXPECT_EQ(close(fd), 0); 311 ASSERT_EQ(mkdirat(ovl, "dir", 0755), 0); 312 ASSERT_EQ(mknodat(ovl, "fifo", S_IFIFO | 0644, 0), 0); 313 ASSERT_EQ(symlinkat("target", ovl, "lnk"), 0); 314 315 for (size_t i = 0; i < ARRAY_SIZE(names); i++) { 316 /* Reported as ID_NS through the idmapped mount ... */ 317 ASSERT_EQ(fstatat(ovl, names[i], &st, AT_SYMLINK_NOFOLLOW), 0); 318 EXPECT_EQ(st.st_uid, ID_NS); 319 EXPECT_EQ(st.st_gid, ID_NS); 320 /* ... and stored as ID_HOST on the upper layer. */ 321 snprintf(path, sizeof(path), "%s/u/%s", self->base, names[i]); 322 ASSERT_EQ(lstat(path, &st), 0); 323 EXPECT_EQ(st.st_uid, ID_HOST); 324 EXPECT_EQ(st.st_gid, ID_HOST); 325 } 326 327 /* O_TMPFILE goes through the separate ovl_tmpfile() path. */ 328 fd = openat(ovl, ".", O_TMPFILE | O_WRONLY, 0644); 329 ASSERT_GE(fd, 0); 330 /* Inside the mount: caller id ID_NS. */ 331 ASSERT_EQ(fstat(fd, &st), 0); 332 EXPECT_EQ(st.st_uid, ID_NS); 333 EXPECT_EQ(st.st_gid, ID_NS); 334 /* Link it in so the upper backing file can be inspected too. */ 335 ASSERT_EQ(linkat(fd, "", ovl, "tmp", AT_EMPTY_PATH), 0); 336 EXPECT_EQ(close(fd), 0); 337 snprintf(path, sizeof(path), "%s/u/tmp", self->base); 338 ASSERT_EQ(lstat(path, &st), 0); 339 EXPECT_EQ(st.st_uid, ID_HOST); 340 EXPECT_EQ(st.st_gid, ID_HOST); 341 342 EXPECT_EQ(close(ovl), 0); 343 } 344 345 /* chown through the idmapped mount round-trips: ID_NS + 5 <-> ID_HOST + 5. */ 346 TEST_F(idmapped_overlay, chown) 347 { 348 char path[PATH_MAX]; 349 struct stat st; 350 int ovl, fd; 351 352 if (!ovl_supported()) 353 SKIP(return, "overlayfs not supported"); 354 355 ovl = ovl_mount(self->base, false); 356 ASSERT_GE(ovl, 0); 357 ASSERT_EQ(ovl_idmap(ovl), 0); 358 359 fd = openat(ovl, "f", O_CREAT | O_WRONLY | O_EXCL, 0644); 360 ASSERT_GE(fd, 0); 361 EXPECT_EQ(close(fd), 0); 362 363 ASSERT_EQ(fchownat(ovl, "f", ID_NS + 5, ID_NS + 5, 0), 0); 364 365 ASSERT_EQ(fstatat(ovl, "f", &st, 0), 0); 366 EXPECT_EQ(st.st_uid, ID_NS + 5); 367 EXPECT_EQ(st.st_gid, ID_NS + 5); 368 369 snprintf(path, sizeof(path), "%s/u/f", self->base); 370 ASSERT_EQ(stat(path, &st), 0); 371 EXPECT_EQ(st.st_uid, ID_HOST + 5); 372 EXPECT_EQ(st.st_gid, ID_HOST + 5); 373 374 EXPECT_EQ(close(ovl), 0); 375 } 376 377 /* 378 * Composition: an idmapped lower layer underneath an idmapped overlay mount. 379 * An on-disk id is mapped by the layer idmap into the overlay-final range and 380 * then by the mount idmap into the caller's range: 381 * 382 * on-disk LAYER_HOST+7 --layer--> ID_HOST+7 --mount--> ID_NS+7 383 */ 384 TEST_F(idmapped_overlay, composition) 385 { 386 char lower[PATH_MAX], upper[PATH_MAX], work[PATH_MAX], path[PATH_MAX]; 387 struct stat st; 388 int ovl, fd_lower; 389 390 if (!ovl_supported()) 391 SKIP(return, "overlayfs not supported"); 392 393 snprintf(lower, sizeof(lower), "%s/l", self->base); 394 snprintf(upper, sizeof(upper), "%s/u", self->base); 395 snprintf(work, sizeof(work), "%s/w", self->base); 396 397 /* Put the lower layer's ids in the on-disk [LAYER_HOST, ..) range. */ 398 ASSERT_EQ(chown(lower, LAYER_HOST, LAYER_HOST), 0); 399 snprintf(path, sizeof(path), "%s/l/file", self->base); 400 ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0); 401 ASSERT_EQ(chown(path, LAYER_HOST + 7, LAYER_HOST + 7), 0); 402 403 /* Idmapped lower: on-disk LAYER_HOST <-> overlay-final ID_HOST. */ 404 fd_lower = idmapped_layer_fd(lower, LAYER_HOST, ID_HOST, ID_RANGE); 405 ASSERT_GE(fd_lower, 0); 406 407 ovl = ovl_mount_lower_fd(upper, work, fd_lower); 408 ASSERT_GE(ovl, 0); 409 EXPECT_EQ(close(fd_lower), 0); 410 411 /* Idmap the overlay mount: overlay-final ID_HOST <-> caller ID_NS. */ 412 ASSERT_EQ(ovl_idmap(ovl), 0); 413 414 ASSERT_EQ(fstatat(ovl, "file", &st, 0), 0); 415 EXPECT_EQ(st.st_uid, ID_NS + 7); 416 EXPECT_EQ(st.st_gid, ID_NS + 7); 417 418 EXPECT_EQ(close(ovl), 0); 419 } 420 421 /* An idmapped overlay mount whose sb lives inside a user namespace. */ 422 TEST_F(idmapped_overlay, userns) 423 { 424 int u1; 425 pid_t pid; 426 427 if (!ovl_supported()) 428 SKIP(return, "overlayfs not supported"); 429 430 /* u1 backs the overlay sb: identity-mapped, but not the init ns. */ 431 u1 = get_userns_fd(0, 0, 65536); 432 if (u1 < 0) 433 SKIP(return, "user namespaces not available"); 434 435 pid = fork(); 436 ASSERT_GE(pid, 0); 437 if (pid == 0) { 438 int ret = userns_overlay_child(u1); 439 440 _exit(ret ? EXIT_FAILURE : EXIT_SUCCESS); 441 } 442 EXPECT_EQ(wait_for_pid(pid), 0); 443 444 EXPECT_EQ(close(u1), 0); 445 } 446 447 /* 448 * An nfs_export overlay can be idmapped, and decodable file handles round-trip 449 * through the idmapped mount with correctly mapped ownership. Overlay file 450 * handles encode object identity, not ownership, so the mount idmap does not 451 * affect them; it only maps the owner reported once a handle is reopened. 452 */ 453 TEST_F(idmapped_overlay, nfs_export_handles) 454 { 455 char path[PATH_MAX], mnt[128]; 456 union { 457 struct file_handle fh; 458 char buf[sizeof(struct file_handle) + MAX_HANDLE_SZ]; 459 } fhu; 460 struct file_handle *fh = &fhu.fh; 461 struct stat st; 462 int ovl, mfd, fd, mount_id; 463 464 if (!ovl_supported()) 465 SKIP(return, "overlayfs not supported"); 466 467 snprintf(path, sizeof(path), "%s/l/file", self->base); 468 ASSERT_EQ(mknod(path, S_IFREG | 0644, 0), 0); 469 ASSERT_EQ(chown(path, ID_HOST + 7, ID_HOST + 7), 0); 470 471 /* nfs_export=on gives decodable overlay file handles. */ 472 ovl = ovl_mount(self->base, true); 473 if (ovl < 0) 474 SKIP(return, "overlayfs nfs_export not supported"); 475 ASSERT_EQ(ovl_idmap(ovl), 0); 476 477 /* Attach the idmapped mount so handles can be resolved against it. */ 478 snprintf(mnt, sizeof(mnt), "%s/mnt", self->base); 479 ASSERT_EQ(mkdir(mnt, 0755), 0); 480 ASSERT_EQ(sys_move_mount(ovl, "", AT_FDCWD, mnt, 481 MOVE_MOUNT_F_EMPTY_PATH), 0); 482 483 snprintf(path, sizeof(path), "%s/file", mnt); 484 fh->handle_bytes = MAX_HANDLE_SZ; 485 ASSERT_EQ(name_to_handle_at(AT_FDCWD, path, fh, &mount_id, 0), 0); 486 487 mfd = open(mnt, O_RDONLY | O_DIRECTORY); 488 ASSERT_GE(mfd, 0); 489 fd = open_by_handle_at(mfd, fh, O_RDONLY); 490 EXPECT_EQ(close(mfd), 0); 491 ASSERT_GE(fd, 0); 492 493 ASSERT_EQ(fstat(fd, &st), 0); 494 EXPECT_EQ(st.st_uid, ID_NS + 7); 495 EXPECT_EQ(st.st_gid, ID_NS + 7); 496 497 EXPECT_EQ(close(fd), 0); 498 EXPECT_EQ(close(ovl), 0); 499 } 500 501 TEST_HARNESS_MAIN 502