1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Landlock tests - Filesystem 4 * 5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 * Copyright © 2020 ANSSI 7 * Copyright © 2020-2022 Microsoft Corporation 8 */ 9 10 #define _GNU_SOURCE 11 #include <asm/termbits.h> 12 #include <fcntl.h> 13 #include <libgen.h> 14 #include <linux/fiemap.h> 15 #include <linux/landlock.h> 16 #include <linux/magic.h> 17 #include <sched.h> 18 #include <stddef.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <sys/capability.h> 22 #include <sys/ioctl.h> 23 #include <sys/mount.h> 24 #include <sys/prctl.h> 25 #include <sys/resource.h> 26 #include <sys/sendfile.h> 27 #include <sys/socket.h> 28 #include <sys/stat.h> 29 #include <sys/sysmacros.h> 30 #include <sys/un.h> 31 #include <sys/vfs.h> 32 #include <unistd.h> 33 34 /* 35 * Intentionally included last to work around header conflict. 36 * See https://sourceware.org/glibc/wiki/Synchronizing_Headers. 37 */ 38 #include <linux/fs.h> 39 #include <linux/mount.h> 40 41 /* Defines AT_EXECVE_CHECK without type conflicts. */ 42 #define _ASM_GENERIC_FCNTL_H 43 #include <linux/fcntl.h> 44 45 #include "audit.h" 46 #include "common.h" 47 48 #ifndef renameat2 49 int renameat2(int olddirfd, const char *oldpath, int newdirfd, 50 const char *newpath, unsigned int flags) 51 { 52 return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 53 flags); 54 } 55 #endif 56 57 #ifndef open_tree 58 int open_tree(int dfd, const char *filename, unsigned int flags) 59 { 60 return syscall(__NR_open_tree, dfd, filename, flags); 61 } 62 #endif 63 64 static int sys_execveat(int dirfd, const char *pathname, char *const argv[], 65 char *const envp[], int flags) 66 { 67 return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); 68 } 69 70 #ifndef RENAME_EXCHANGE 71 #define RENAME_EXCHANGE (1 << 1) 72 #endif 73 74 static const char bin_true[] = "./true"; 75 76 /* Paths (sibling number and depth) */ 77 static const char dir_s1d1[] = TMP_DIR "/s1d1"; 78 static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 79 static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 80 static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 81 static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 82 static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 83 static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 84 static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 85 static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 86 87 static const char dir_s2d1[] = TMP_DIR "/s2d1"; 88 static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 89 static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 90 static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 91 static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 92 static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 93 static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 94 95 static const char dir_s3d1[] = TMP_DIR "/s3d1"; 96 static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; 97 /* dir_s3d2 is a mount point. */ 98 static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 99 static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 100 static const char file1_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3/f1"; 101 static const char dir_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4"; 102 static const char file1_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4/f1"; 103 104 /* 105 * layout1 hierarchy: 106 * 107 * tmp 108 * ├── s1d1 109 * │ ├── f1 110 * │ ├── f2 111 * │ └── s1d2 112 * │ ├── f1 113 * │ ├── f2 114 * │ └── s1d3 115 * │ ├── f1 116 * │ └── f2 117 * ├── s2d1 118 * │ ├── f1 119 * │ └── s2d2 120 * │ ├── f1 121 * │ └── s2d3 122 * │ ├── f1 123 * │ └── f2 124 * └── s3d1 125 * ├── f1 126 * └── s3d2 [mount point] 127 * ├── s3d3 128 * │ └── f1 129 * └── s3d4 130 * └── f1 131 */ 132 133 static bool fgrep(FILE *const inf, const char *const str) 134 { 135 char line[32]; 136 const int slen = strlen(str); 137 138 while (!feof(inf)) { 139 if (!fgets(line, sizeof(line), inf)) 140 break; 141 if (strncmp(line, str, slen)) 142 continue; 143 144 return true; 145 } 146 147 return false; 148 } 149 150 static bool supports_filesystem(const char *const filesystem) 151 { 152 char str[32]; 153 int len; 154 bool res = true; 155 FILE *const inf = fopen("/proc/filesystems", "r"); 156 157 /* 158 * Consider that the filesystem is supported if we cannot get the 159 * supported ones. 160 */ 161 if (!inf) 162 return true; 163 164 /* filesystem can be null for bind mounts. */ 165 if (!filesystem) 166 goto out; 167 168 len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); 169 if (len >= sizeof(str)) 170 /* Ignores too-long filesystem names. */ 171 goto out; 172 173 res = fgrep(inf, str); 174 175 out: 176 fclose(inf); 177 return res; 178 } 179 180 static bool cwd_matches_fs(unsigned int fs_magic) 181 { 182 struct statfs statfs_buf; 183 184 if (!fs_magic) 185 return true; 186 187 if (statfs(".", &statfs_buf)) 188 return true; 189 190 return statfs_buf.f_type == fs_magic; 191 } 192 193 static void mkdir_parents(struct __test_metadata *const _metadata, 194 const char *const path) 195 { 196 char *walker; 197 const char *parent; 198 int i, err; 199 200 ASSERT_NE(path[0], '\0'); 201 walker = strdup(path); 202 ASSERT_NE(NULL, walker); 203 parent = walker; 204 for (i = 1; walker[i]; i++) { 205 if (walker[i] != '/') 206 continue; 207 walker[i] = '\0'; 208 err = mkdir(parent, 0700); 209 ASSERT_FALSE(err && errno != EEXIST) 210 { 211 TH_LOG("Failed to create directory \"%s\": %s", parent, 212 strerror(errno)); 213 } 214 walker[i] = '/'; 215 } 216 free(walker); 217 } 218 219 static void create_directory(struct __test_metadata *const _metadata, 220 const char *const path) 221 { 222 mkdir_parents(_metadata, path); 223 ASSERT_EQ(0, mkdir(path, 0700)) 224 { 225 TH_LOG("Failed to create directory \"%s\": %s", path, 226 strerror(errno)); 227 } 228 } 229 230 static void create_file(struct __test_metadata *const _metadata, 231 const char *const path) 232 { 233 mkdir_parents(_metadata, path); 234 ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 235 { 236 TH_LOG("Failed to create file \"%s\": %s", path, 237 strerror(errno)); 238 } 239 } 240 241 static int remove_path(const char *const path) 242 { 243 char *walker; 244 int i, ret, err = 0; 245 246 walker = strdup(path); 247 if (!walker) { 248 err = ENOMEM; 249 goto out; 250 } 251 if (unlink(path) && rmdir(path)) { 252 if (errno != ENOENT && errno != ENOTDIR) 253 err = errno; 254 goto out; 255 } 256 for (i = strlen(walker); i > 0; i--) { 257 if (walker[i] != '/') 258 continue; 259 walker[i] = '\0'; 260 ret = rmdir(walker); 261 if (ret) { 262 if (errno != ENOTEMPTY && errno != EBUSY) 263 err = errno; 264 goto out; 265 } 266 if (strcmp(walker, TMP_DIR) == 0) 267 goto out; 268 } 269 270 out: 271 free(walker); 272 return err; 273 } 274 275 struct mnt_opt { 276 const char *const source; 277 const char *const type; 278 const unsigned long flags; 279 const char *const data; 280 }; 281 282 #define MNT_TMP_DATA "size=4m,mode=700" 283 284 static const struct mnt_opt mnt_tmp = { 285 .type = "tmpfs", 286 .data = MNT_TMP_DATA, 287 }; 288 289 static int mount_opt(const struct mnt_opt *const mnt, const char *const target) 290 { 291 return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags, 292 mnt->data); 293 } 294 295 static void prepare_layout_opt(struct __test_metadata *const _metadata, 296 const struct mnt_opt *const mnt) 297 { 298 disable_caps(_metadata); 299 umask(0077); 300 create_directory(_metadata, TMP_DIR); 301 302 /* 303 * Do not pollute the rest of the system: creates a private mount point 304 * for tests relying on pivot_root(2) and move_mount(2). 305 */ 306 set_cap(_metadata, CAP_SYS_ADMIN); 307 ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); 308 ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) 309 { 310 TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, 311 strerror(errno)); 312 /* 313 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() 314 * failed, so we need to explicitly do a minimal cleanup to 315 * avoid cascading errors with other tests that don't depend on 316 * the same filesystem. 317 */ 318 remove_path(TMP_DIR); 319 } 320 ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 321 clear_cap(_metadata, CAP_SYS_ADMIN); 322 } 323 324 static void prepare_layout(struct __test_metadata *const _metadata) 325 { 326 prepare_layout_opt(_metadata, &mnt_tmp); 327 } 328 329 static void cleanup_layout(struct __test_metadata *const _metadata) 330 { 331 set_cap(_metadata, CAP_SYS_ADMIN); 332 if (umount(TMP_DIR)) { 333 /* 334 * According to the test environment, the mount point of the 335 * current directory may be shared or not, which changes the 336 * visibility of the nested TMP_DIR mount point for the test's 337 * parent process doing this cleanup. 338 */ 339 ASSERT_EQ(EINVAL, errno); 340 } 341 clear_cap(_metadata, CAP_SYS_ADMIN); 342 EXPECT_EQ(0, remove_path(TMP_DIR)); 343 } 344 345 /* clang-format off */ 346 FIXTURE(layout0) {}; 347 /* clang-format on */ 348 349 FIXTURE_SETUP(layout0) 350 { 351 prepare_layout(_metadata); 352 } 353 354 FIXTURE_TEARDOWN_PARENT(layout0) 355 { 356 cleanup_layout(_metadata); 357 } 358 359 static void create_layout1(struct __test_metadata *const _metadata) 360 { 361 create_file(_metadata, file1_s1d1); 362 create_file(_metadata, file1_s1d2); 363 create_file(_metadata, file1_s1d3); 364 create_file(_metadata, file2_s1d1); 365 create_file(_metadata, file2_s1d2); 366 create_file(_metadata, file2_s1d3); 367 368 create_file(_metadata, file1_s2d1); 369 create_file(_metadata, file1_s2d2); 370 create_file(_metadata, file1_s2d3); 371 create_file(_metadata, file2_s2d3); 372 373 create_file(_metadata, file1_s3d1); 374 create_directory(_metadata, dir_s3d2); 375 set_cap(_metadata, CAP_SYS_ADMIN); 376 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 377 clear_cap(_metadata, CAP_SYS_ADMIN); 378 379 create_file(_metadata, file1_s3d3); 380 create_file(_metadata, file1_s3d4); 381 } 382 383 static void remove_layout1(struct __test_metadata *const _metadata) 384 { 385 EXPECT_EQ(0, remove_path(file2_s1d3)); 386 EXPECT_EQ(0, remove_path(file2_s1d2)); 387 EXPECT_EQ(0, remove_path(file2_s1d1)); 388 EXPECT_EQ(0, remove_path(file1_s1d3)); 389 EXPECT_EQ(0, remove_path(file1_s1d2)); 390 EXPECT_EQ(0, remove_path(file1_s1d1)); 391 EXPECT_EQ(0, remove_path(dir_s1d3)); 392 393 EXPECT_EQ(0, remove_path(file2_s2d3)); 394 EXPECT_EQ(0, remove_path(file1_s2d3)); 395 EXPECT_EQ(0, remove_path(file1_s2d2)); 396 EXPECT_EQ(0, remove_path(file1_s2d1)); 397 EXPECT_EQ(0, remove_path(dir_s2d2)); 398 399 EXPECT_EQ(0, remove_path(file1_s3d1)); 400 EXPECT_EQ(0, remove_path(file1_s3d3)); 401 EXPECT_EQ(0, remove_path(file1_s3d4)); 402 set_cap(_metadata, CAP_SYS_ADMIN); 403 umount(dir_s3d2); 404 clear_cap(_metadata, CAP_SYS_ADMIN); 405 EXPECT_EQ(0, remove_path(dir_s3d2)); 406 } 407 408 /* clang-format off */ 409 FIXTURE(layout1) {}; 410 /* clang-format on */ 411 412 FIXTURE_SETUP(layout1) 413 { 414 prepare_layout(_metadata); 415 416 create_layout1(_metadata); 417 } 418 419 FIXTURE_TEARDOWN_PARENT(layout1) 420 { 421 remove_layout1(_metadata); 422 423 cleanup_layout(_metadata); 424 } 425 426 /* 427 * This helper enables to use the ASSERT_* macros and print the line number 428 * pointing to the test caller. 429 */ 430 static int test_open_rel(const int dirfd, const char *const path, 431 const int flags) 432 { 433 int fd; 434 435 /* Works with file and directories. */ 436 fd = openat(dirfd, path, flags | O_CLOEXEC); 437 if (fd < 0) 438 return errno; 439 /* 440 * Mixing error codes from close(2) and open(2) should not lead to any 441 * (access type) confusion for this test. 442 */ 443 if (close(fd) != 0) 444 return errno; 445 return 0; 446 } 447 448 static int test_open(const char *const path, const int flags) 449 { 450 return test_open_rel(AT_FDCWD, path, flags); 451 } 452 453 TEST_F_FORK(layout1, no_restriction) 454 { 455 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 456 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 457 ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 458 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 459 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 460 ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 461 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 462 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 463 464 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 465 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 466 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 467 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 468 ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 469 ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 470 471 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 472 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 473 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 474 } 475 476 TEST_F_FORK(layout1, inval) 477 { 478 struct landlock_path_beneath_attr path_beneath = { 479 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 480 LANDLOCK_ACCESS_FS_WRITE_FILE, 481 .parent_fd = -1, 482 }; 483 struct landlock_ruleset_attr ruleset_attr = { 484 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 485 LANDLOCK_ACCESS_FS_WRITE_FILE, 486 }; 487 int ruleset_fd; 488 489 path_beneath.parent_fd = 490 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 491 ASSERT_LE(0, path_beneath.parent_fd); 492 493 ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 494 ASSERT_LE(0, ruleset_fd); 495 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 496 &path_beneath, 0)); 497 /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 498 ASSERT_EQ(EBADF, errno); 499 ASSERT_EQ(0, close(ruleset_fd)); 500 501 ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 502 ASSERT_LE(0, ruleset_fd); 503 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 504 &path_beneath, 0)); 505 /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 506 ASSERT_EQ(EBADFD, errno); 507 ASSERT_EQ(0, close(ruleset_fd)); 508 509 /* Gets a real ruleset. */ 510 ruleset_fd = 511 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 512 ASSERT_LE(0, ruleset_fd); 513 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 514 &path_beneath, 0)); 515 ASSERT_EQ(0, close(path_beneath.parent_fd)); 516 517 /* Tests without O_PATH. */ 518 path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 519 ASSERT_LE(0, path_beneath.parent_fd); 520 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 521 &path_beneath, 0)); 522 ASSERT_EQ(0, close(path_beneath.parent_fd)); 523 524 /* Tests with a ruleset FD. */ 525 path_beneath.parent_fd = ruleset_fd; 526 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 527 &path_beneath, 0)); 528 ASSERT_EQ(EBADFD, errno); 529 530 /* Checks unhandled allowed_access. */ 531 path_beneath.parent_fd = 532 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 533 ASSERT_LE(0, path_beneath.parent_fd); 534 535 /* Test with legitimate values. */ 536 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 537 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 538 &path_beneath, 0)); 539 ASSERT_EQ(EINVAL, errno); 540 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 541 542 /* Tests with denied-by-default access right. */ 543 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 544 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 545 &path_beneath, 0)); 546 ASSERT_EQ(EINVAL, errno); 547 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 548 549 /* Test with unknown (64-bits) value. */ 550 path_beneath.allowed_access |= (1ULL << 60); 551 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 552 &path_beneath, 0)); 553 ASSERT_EQ(EINVAL, errno); 554 path_beneath.allowed_access &= ~(1ULL << 60); 555 556 /* Test with no access. */ 557 path_beneath.allowed_access = 0; 558 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 559 &path_beneath, 0)); 560 ASSERT_EQ(ENOMSG, errno); 561 path_beneath.allowed_access &= ~(1ULL << 60); 562 563 ASSERT_EQ(0, close(path_beneath.parent_fd)); 564 565 /* Enforces the ruleset. */ 566 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 567 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 568 569 ASSERT_EQ(0, close(ruleset_fd)); 570 } 571 572 /* clang-format off */ 573 574 #define ACCESS_FILE ( \ 575 LANDLOCK_ACCESS_FS_EXECUTE | \ 576 LANDLOCK_ACCESS_FS_WRITE_FILE | \ 577 LANDLOCK_ACCESS_FS_READ_FILE | \ 578 LANDLOCK_ACCESS_FS_TRUNCATE | \ 579 LANDLOCK_ACCESS_FS_IOCTL_DEV | \ 580 LANDLOCK_ACCESS_FS_RESOLVE_UNIX) 581 582 #define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX 583 584 #define ACCESS_ALL ( \ 585 ACCESS_FILE | \ 586 LANDLOCK_ACCESS_FS_READ_DIR | \ 587 LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 588 LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 589 LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 590 LANDLOCK_ACCESS_FS_MAKE_DIR | \ 591 LANDLOCK_ACCESS_FS_MAKE_REG | \ 592 LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 593 LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 594 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 595 LANDLOCK_ACCESS_FS_MAKE_SYM | \ 596 LANDLOCK_ACCESS_FS_REFER) 597 598 /* clang-format on */ 599 600 TEST_F_FORK(layout1, file_and_dir_access_rights) 601 { 602 __u64 access; 603 int err; 604 struct landlock_path_beneath_attr path_beneath_file = {}, 605 path_beneath_dir = {}; 606 struct landlock_ruleset_attr ruleset_attr = { 607 .handled_access_fs = ACCESS_ALL, 608 }; 609 const int ruleset_fd = 610 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 611 612 ASSERT_LE(0, ruleset_fd); 613 614 /* Tests access rights for files. */ 615 path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 616 ASSERT_LE(0, path_beneath_file.parent_fd); 617 618 /* Tests access rights for directories. */ 619 path_beneath_dir.parent_fd = 620 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 621 ASSERT_LE(0, path_beneath_dir.parent_fd); 622 623 for (access = 1; access <= ACCESS_LAST; access <<= 1) { 624 path_beneath_dir.allowed_access = access; 625 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 626 LANDLOCK_RULE_PATH_BENEATH, 627 &path_beneath_dir, 0)); 628 629 path_beneath_file.allowed_access = access; 630 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 631 &path_beneath_file, 0); 632 if (access & ACCESS_FILE) { 633 ASSERT_EQ(0, err); 634 } else { 635 ASSERT_EQ(-1, err); 636 ASSERT_EQ(EINVAL, errno); 637 } 638 } 639 ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 640 ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 641 ASSERT_EQ(0, close(ruleset_fd)); 642 } 643 644 TEST_F_FORK(layout0, ruleset_with_unknown_access) 645 { 646 __u64 access_mask; 647 648 for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 649 access_mask >>= 1) { 650 struct landlock_ruleset_attr ruleset_attr = { 651 .handled_access_fs = access_mask, 652 }; 653 654 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 655 sizeof(ruleset_attr), 0)); 656 ASSERT_EQ(EINVAL, errno); 657 } 658 } 659 660 TEST_F_FORK(layout0, rule_with_unknown_access) 661 { 662 __u64 access; 663 struct landlock_path_beneath_attr path_beneath = {}; 664 const struct landlock_ruleset_attr ruleset_attr = { 665 .handled_access_fs = ACCESS_ALL, 666 }; 667 const int ruleset_fd = 668 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 669 670 ASSERT_LE(0, ruleset_fd); 671 672 path_beneath.parent_fd = 673 open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC); 674 ASSERT_LE(0, path_beneath.parent_fd); 675 676 for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) { 677 path_beneath.allowed_access = access; 678 EXPECT_EQ(-1, landlock_add_rule(ruleset_fd, 679 LANDLOCK_RULE_PATH_BENEATH, 680 &path_beneath, 0)); 681 EXPECT_EQ(EINVAL, errno); 682 } 683 ASSERT_EQ(0, close(path_beneath.parent_fd)); 684 ASSERT_EQ(0, close(ruleset_fd)); 685 } 686 687 TEST_F_FORK(layout1, rule_with_unhandled_access) 688 { 689 struct landlock_ruleset_attr ruleset_attr = { 690 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 691 }; 692 struct landlock_path_beneath_attr path_beneath = {}; 693 int ruleset_fd; 694 __u64 access; 695 696 ruleset_fd = 697 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 698 ASSERT_LE(0, ruleset_fd); 699 700 path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 701 ASSERT_LE(0, path_beneath.parent_fd); 702 703 for (access = 1; access > 0; access <<= 1) { 704 int err; 705 706 path_beneath.allowed_access = access; 707 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 708 &path_beneath, 0); 709 if (access == ruleset_attr.handled_access_fs) { 710 EXPECT_EQ(0, err); 711 } else { 712 EXPECT_EQ(-1, err); 713 EXPECT_EQ(EINVAL, errno); 714 } 715 } 716 717 EXPECT_EQ(0, close(path_beneath.parent_fd)); 718 EXPECT_EQ(0, close(ruleset_fd)); 719 } 720 721 static void add_path_beneath(struct __test_metadata *const _metadata, 722 const int ruleset_fd, const __u64 allowed_access, 723 const char *const path) 724 { 725 struct landlock_path_beneath_attr path_beneath = { 726 .allowed_access = allowed_access, 727 }; 728 729 path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 730 ASSERT_LE(0, path_beneath.parent_fd) 731 { 732 TH_LOG("Failed to open directory \"%s\": %s", path, 733 strerror(errno)); 734 } 735 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 736 &path_beneath, 0)) 737 { 738 TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 739 strerror(errno)); 740 } 741 ASSERT_EQ(0, close(path_beneath.parent_fd)); 742 } 743 744 struct rule { 745 const char *path; 746 __u64 access; 747 }; 748 749 /* clang-format off */ 750 751 #define ACCESS_RO ( \ 752 LANDLOCK_ACCESS_FS_READ_FILE | \ 753 LANDLOCK_ACCESS_FS_READ_DIR) 754 755 #define ACCESS_RW ( \ 756 ACCESS_RO | \ 757 LANDLOCK_ACCESS_FS_WRITE_FILE) 758 759 /* clang-format on */ 760 761 static int create_ruleset(struct __test_metadata *const _metadata, 762 const __u64 handled_access_fs, 763 const struct rule rules[]) 764 { 765 int ruleset_fd, i; 766 struct landlock_ruleset_attr ruleset_attr = { 767 .handled_access_fs = handled_access_fs, 768 }; 769 770 ruleset_fd = 771 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 772 ASSERT_LE(0, ruleset_fd) 773 { 774 TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 775 } 776 777 if (rules) 778 for (i = 0; rules[i].path; i++) { 779 if (!rules[i].access) 780 continue; 781 782 add_path_beneath(_metadata, ruleset_fd, rules[i].access, 783 rules[i].path); 784 } 785 return ruleset_fd; 786 } 787 788 static void enforce_fs(struct __test_metadata *const _metadata, 789 const __u64 access_fs, const struct rule rules[]) 790 { 791 const int ruleset_fd = create_ruleset(_metadata, access_fs, rules); 792 793 enforce_ruleset(_metadata, ruleset_fd); 794 EXPECT_EQ(0, close(ruleset_fd)); 795 } 796 797 TEST_F_FORK(layout0, proc_nsfs) 798 { 799 const struct rule rules[] = { 800 { 801 .path = "/dev/null", 802 .access = LANDLOCK_ACCESS_FS_READ_FILE | 803 LANDLOCK_ACCESS_FS_WRITE_FILE, 804 }, 805 {}, 806 }; 807 struct landlock_path_beneath_attr path_beneath; 808 const int ruleset_fd = create_ruleset( 809 _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 810 rules); 811 812 ASSERT_LE(0, ruleset_fd); 813 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 814 815 enforce_ruleset(_metadata, ruleset_fd); 816 817 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 818 ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 819 ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 820 ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 821 822 ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 823 ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 824 ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 825 /* 826 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 827 * disconnected path. Such path cannot be identified and must then be 828 * allowed. 829 */ 830 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 831 832 /* 833 * Checks that it is not possible to add nsfs-like filesystem 834 * references to a ruleset. 835 */ 836 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 837 LANDLOCK_ACCESS_FS_WRITE_FILE, 838 path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 839 ASSERT_LE(0, path_beneath.parent_fd); 840 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 841 &path_beneath, 0)); 842 ASSERT_EQ(EBADFD, errno); 843 ASSERT_EQ(0, close(path_beneath.parent_fd)); 844 } 845 846 TEST_F_FORK(layout0, unpriv) 847 { 848 const struct rule rules[] = { 849 { 850 .path = TMP_DIR, 851 .access = ACCESS_RO, 852 }, 853 {}, 854 }; 855 int ruleset_fd; 856 857 drop_caps(_metadata); 858 859 ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 860 ASSERT_LE(0, ruleset_fd); 861 ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 862 ASSERT_EQ(EPERM, errno); 863 864 /* enforce_ruleset() calls prctl(no_new_privs). */ 865 enforce_ruleset(_metadata, ruleset_fd); 866 ASSERT_EQ(0, close(ruleset_fd)); 867 } 868 869 TEST_F_FORK(layout1, effective_access) 870 { 871 const struct rule rules[] = { 872 { 873 .path = dir_s1d2, 874 .access = ACCESS_RO, 875 }, 876 { 877 .path = file1_s2d2, 878 .access = LANDLOCK_ACCESS_FS_READ_FILE | 879 LANDLOCK_ACCESS_FS_WRITE_FILE, 880 }, 881 {}, 882 }; 883 char buf; 884 int reg_fd; 885 886 enforce_fs(_metadata, ACCESS_RW, rules); 887 888 /* Tests on a directory (with or without O_PATH). */ 889 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 890 ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 891 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 892 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 893 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 894 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 895 896 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 897 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 898 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 899 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 900 901 /* Tests on a file (with or without O_PATH). */ 902 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 903 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 904 905 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 906 907 /* Checks effective read and write actions. */ 908 reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 909 ASSERT_LE(0, reg_fd); 910 ASSERT_EQ(1, write(reg_fd, ".", 1)); 911 ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 912 ASSERT_EQ(1, read(reg_fd, &buf, 1)); 913 ASSERT_EQ('.', buf); 914 ASSERT_EQ(0, close(reg_fd)); 915 916 /* Just in case, double-checks effective actions. */ 917 reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 918 ASSERT_LE(0, reg_fd); 919 ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 920 ASSERT_EQ(EBADF, errno); 921 ASSERT_EQ(0, close(reg_fd)); 922 } 923 924 TEST_F_FORK(layout1, unhandled_access) 925 { 926 const struct rule rules[] = { 927 { 928 .path = dir_s1d2, 929 .access = ACCESS_RO, 930 }, 931 {}, 932 }; 933 934 /* Here, we only handle read accesses, not write accesses. */ 935 enforce_fs(_metadata, ACCESS_RO, rules); 936 937 /* 938 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 939 * opening for write-only should be allowed, but not read-write. 940 */ 941 ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 942 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 943 944 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 945 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 946 } 947 948 TEST_F_FORK(layout1, ruleset_overlap) 949 { 950 const struct rule rules[] = { 951 /* These rules should be ORed among them. */ 952 { 953 .path = dir_s1d2, 954 .access = LANDLOCK_ACCESS_FS_READ_FILE | 955 LANDLOCK_ACCESS_FS_WRITE_FILE, 956 }, 957 { 958 .path = dir_s1d2, 959 .access = LANDLOCK_ACCESS_FS_READ_FILE | 960 LANDLOCK_ACCESS_FS_READ_DIR, 961 }, 962 {}, 963 }; 964 965 enforce_fs(_metadata, ACCESS_RW, rules); 966 967 /* Checks s1d1 hierarchy. */ 968 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 969 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 970 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 971 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 972 973 /* Checks s1d2 hierarchy. */ 974 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 975 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 976 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 977 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 978 979 /* Checks s1d3 hierarchy. */ 980 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 981 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 982 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 983 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 984 } 985 986 TEST_F_FORK(layout1, layer_rule_unions) 987 { 988 const struct rule layer1[] = { 989 { 990 .path = dir_s1d2, 991 .access = LANDLOCK_ACCESS_FS_READ_FILE, 992 }, 993 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 994 { 995 .path = dir_s1d3, 996 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 997 }, 998 {}, 999 }; 1000 const struct rule layer2[] = { 1001 /* Doesn't change anything from layer1. */ 1002 { 1003 .path = dir_s1d2, 1004 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1005 LANDLOCK_ACCESS_FS_WRITE_FILE, 1006 }, 1007 {}, 1008 }; 1009 const struct rule layer3[] = { 1010 /* Only allows write (but not read) to dir_s1d3. */ 1011 { 1012 .path = dir_s1d2, 1013 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1014 }, 1015 {}, 1016 }; 1017 1018 enforce_fs(_metadata, ACCESS_RW, layer1); 1019 1020 /* Checks s1d1 hierarchy with layer1. */ 1021 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1022 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1023 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1024 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1025 1026 /* Checks s1d2 hierarchy with layer1. */ 1027 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1028 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1029 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1030 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1031 1032 /* Checks s1d3 hierarchy with layer1. */ 1033 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1034 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1035 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 1036 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1037 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1038 1039 /* Doesn't change anything from layer1. */ 1040 enforce_fs(_metadata, ACCESS_RW, layer2); 1041 1042 /* Checks s1d1 hierarchy with layer2. */ 1043 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1044 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1045 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1046 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1047 1048 /* Checks s1d2 hierarchy with layer2. */ 1049 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1050 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1051 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1052 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1053 1054 /* Checks s1d3 hierarchy with layer2. */ 1055 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1056 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1057 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 1058 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1059 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1060 1061 /* Only allows write (but not read) to dir_s1d3. */ 1062 enforce_fs(_metadata, ACCESS_RW, layer3); 1063 1064 /* Checks s1d1 hierarchy with layer3. */ 1065 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1066 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1067 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1068 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1069 1070 /* Checks s1d2 hierarchy with layer3. */ 1071 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 1072 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1073 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1074 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1075 1076 /* Checks s1d3 hierarchy with layer3. */ 1077 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1078 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1079 /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 1080 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 1081 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1082 } 1083 1084 TEST_F_FORK(layout1, non_overlapping_accesses) 1085 { 1086 const struct rule layer1[] = { 1087 { 1088 .path = dir_s1d2, 1089 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1090 }, 1091 {}, 1092 }; 1093 const struct rule layer2[] = { 1094 { 1095 .path = dir_s1d3, 1096 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1097 }, 1098 {}, 1099 }; 1100 1101 ASSERT_EQ(0, unlink(file1_s1d1)); 1102 ASSERT_EQ(0, unlink(file1_s1d2)); 1103 1104 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 1105 1106 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1107 ASSERT_EQ(EACCES, errno); 1108 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1109 ASSERT_EQ(0, unlink(file1_s1d2)); 1110 1111 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, layer2); 1112 1113 /* Unchanged accesses for file creation. */ 1114 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1115 ASSERT_EQ(EACCES, errno); 1116 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1117 1118 /* Checks file removing. */ 1119 ASSERT_EQ(-1, unlink(file1_s1d2)); 1120 ASSERT_EQ(EACCES, errno); 1121 ASSERT_EQ(0, unlink(file1_s1d3)); 1122 } 1123 1124 TEST_F_FORK(layout1, interleaved_masked_accesses) 1125 { 1126 /* 1127 * Checks overly restrictive rules: 1128 * layer 1: allows R s1d1/s1d2/s1d3/file1 1129 * layer 2: allows RW s1d1/s1d2/s1d3 1130 * allows W s1d1/s1d2 1131 * denies R s1d1/s1d2 1132 * layer 3: allows R s1d1 1133 * layer 4: allows R s1d1/s1d2 1134 * denies W s1d1/s1d2 1135 * layer 5: allows R s1d1/s1d2 1136 * layer 6: allows X ---- 1137 * layer 7: allows W s1d1/s1d2 1138 * denies R s1d1/s1d2 1139 */ 1140 const struct rule layer1_read[] = { 1141 /* Allows read access to file1_s1d3 with the first layer. */ 1142 { 1143 .path = file1_s1d3, 1144 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1145 }, 1146 {}, 1147 }; 1148 /* First rule with write restrictions. */ 1149 const struct rule layer2_read_write[] = { 1150 /* Start by granting read-write access via its parent directory... */ 1151 { 1152 .path = dir_s1d3, 1153 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1154 LANDLOCK_ACCESS_FS_WRITE_FILE, 1155 }, 1156 /* ...but also denies read access via its grandparent directory. */ 1157 { 1158 .path = dir_s1d2, 1159 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1160 }, 1161 {}, 1162 }; 1163 const struct rule layer3_read[] = { 1164 /* Allows read access via its great-grandparent directory. */ 1165 { 1166 .path = dir_s1d1, 1167 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1168 }, 1169 {}, 1170 }; 1171 const struct rule layer4_read_write[] = { 1172 /* 1173 * Try to confuse the deny access by denying write (but not 1174 * read) access via its grandparent directory. 1175 */ 1176 { 1177 .path = dir_s1d2, 1178 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1179 }, 1180 {}, 1181 }; 1182 const struct rule layer5_read[] = { 1183 /* 1184 * Try to override layer2's deny read access by explicitly 1185 * allowing read access via file1_s1d3's grandparent. 1186 */ 1187 { 1188 .path = dir_s1d2, 1189 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1190 }, 1191 {}, 1192 }; 1193 const struct rule layer6_execute[] = { 1194 /* 1195 * Restricts an unrelated file hierarchy with a new access 1196 * (non-overlapping) type. 1197 */ 1198 { 1199 .path = dir_s2d1, 1200 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1201 }, 1202 {}, 1203 }; 1204 const struct rule layer7_read_write[] = { 1205 /* 1206 * Finally, denies read access to file1_s1d3 via its 1207 * grandparent. 1208 */ 1209 { 1210 .path = dir_s1d2, 1211 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1212 }, 1213 {}, 1214 }; 1215 1216 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer1_read); 1217 1218 /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1219 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1220 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1221 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1222 1223 enforce_fs(_metadata, 1224 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 1225 layer2_read_write); 1226 1227 /* Checks that previous access rights are unchanged with layer 2. */ 1228 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1229 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1230 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1231 1232 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer3_read); 1233 1234 /* Checks that previous access rights are unchanged with layer 3. */ 1235 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1236 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1237 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1238 1239 /* This time, denies write access for the file hierarchy. */ 1240 enforce_fs(_metadata, 1241 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 1242 layer4_read_write); 1243 1244 /* 1245 * Checks that the only change with layer 4 is that write access is 1246 * denied. 1247 */ 1248 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1249 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1250 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1251 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1252 1253 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer5_read); 1254 1255 /* Checks that previous access rights are unchanged with layer 5. */ 1256 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1257 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1258 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1259 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1260 1261 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, layer6_execute); 1262 1263 /* Checks that previous access rights are unchanged with layer 6. */ 1264 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1265 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1266 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1267 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1268 1269 enforce_fs(_metadata, 1270 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 1271 layer7_read_write); 1272 1273 /* Checks read access is now denied with layer 7. */ 1274 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1275 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1276 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1277 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1278 } 1279 1280 TEST_F_FORK(layout1, inherit_subset) 1281 { 1282 const struct rule rules[] = { 1283 { 1284 .path = dir_s1d2, 1285 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1286 LANDLOCK_ACCESS_FS_READ_DIR, 1287 }, 1288 {}, 1289 }; 1290 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1291 1292 enforce_ruleset(_metadata, ruleset_fd); 1293 1294 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1295 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1296 1297 /* Write access is forbidden. */ 1298 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1299 /* Readdir access is allowed. */ 1300 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1301 1302 /* Write access is forbidden. */ 1303 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1304 /* Readdir access is allowed. */ 1305 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1306 1307 /* 1308 * Tests shared rule extension: the following rules should not grant 1309 * any new access, only remove some. Once enforced, these rules are 1310 * ANDed with the previous ones. 1311 */ 1312 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1313 dir_s1d2); 1314 /* 1315 * According to ruleset_fd, dir_s1d2 should now have the 1316 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1317 * access rights (even if this directory is opened a second time). 1318 * However, when enforcing this updated ruleset, the ruleset tied to 1319 * the current process (i.e. its domain) will still only have the 1320 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1321 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1322 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1323 * be a privilege escalation. 1324 */ 1325 enforce_ruleset(_metadata, ruleset_fd); 1326 1327 /* Same tests and results as above. */ 1328 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1329 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1330 1331 /* It is still forbidden to write in file1_s1d2. */ 1332 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1333 /* Readdir access is still allowed. */ 1334 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1335 1336 /* It is still forbidden to write in file1_s1d3. */ 1337 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1338 /* Readdir access is still allowed. */ 1339 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1340 1341 /* 1342 * Try to get more privileges by adding new access rights to the parent 1343 * directory: dir_s1d1. 1344 */ 1345 add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 1346 enforce_ruleset(_metadata, ruleset_fd); 1347 1348 /* Same tests and results as above. */ 1349 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1350 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1351 1352 /* It is still forbidden to write in file1_s1d2. */ 1353 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1354 /* Readdir access is still allowed. */ 1355 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1356 1357 /* It is still forbidden to write in file1_s1d3. */ 1358 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1359 /* Readdir access is still allowed. */ 1360 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1361 1362 /* 1363 * Now, dir_s1d3 get a new rule tied to it, only allowing 1364 * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1365 * that there was no rule tied to it before. 1366 */ 1367 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1368 dir_s1d3); 1369 enforce_ruleset(_metadata, ruleset_fd); 1370 ASSERT_EQ(0, close(ruleset_fd)); 1371 1372 /* 1373 * Same tests and results as above, except for open(dir_s1d3) which is 1374 * now denied because the new rule mask the rule previously inherited 1375 * from dir_s1d2. 1376 */ 1377 1378 /* Same tests and results as above. */ 1379 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1380 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1381 1382 /* It is still forbidden to write in file1_s1d2. */ 1383 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1384 /* Readdir access is still allowed. */ 1385 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1386 1387 /* It is still forbidden to write in file1_s1d3. */ 1388 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1389 /* 1390 * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1391 * the same layer. 1392 */ 1393 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1394 } 1395 1396 TEST_F_FORK(layout1, inherit_superset) 1397 { 1398 const struct rule rules[] = { 1399 { 1400 .path = dir_s1d3, 1401 .access = ACCESS_RO, 1402 }, 1403 {}, 1404 }; 1405 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1406 1407 enforce_ruleset(_metadata, ruleset_fd); 1408 1409 /* Readdir access is denied for dir_s1d2. */ 1410 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1411 /* Readdir access is allowed for dir_s1d3. */ 1412 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1413 /* File access is allowed for file1_s1d3. */ 1414 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1415 1416 /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1417 add_path_beneath(_metadata, ruleset_fd, 1418 LANDLOCK_ACCESS_FS_READ_FILE | 1419 LANDLOCK_ACCESS_FS_READ_DIR, 1420 dir_s1d2); 1421 enforce_ruleset(_metadata, ruleset_fd); 1422 EXPECT_EQ(0, close(ruleset_fd)); 1423 1424 /* Readdir access is still denied for dir_s1d2. */ 1425 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1426 /* Readdir access is still allowed for dir_s1d3. */ 1427 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1428 /* File access is still allowed for file1_s1d3. */ 1429 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1430 } 1431 1432 TEST_F_FORK(layout0, max_layers) 1433 { 1434 int i, err; 1435 const struct rule rules[] = { 1436 { 1437 .path = TMP_DIR, 1438 .access = ACCESS_RO, 1439 }, 1440 {}, 1441 }; 1442 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1443 1444 for (i = 0; i < 16; i++) 1445 enforce_ruleset(_metadata, ruleset_fd); 1446 1447 for (i = 0; i < 2; i++) { 1448 err = landlock_restrict_self(ruleset_fd, 0); 1449 ASSERT_EQ(-1, err); 1450 ASSERT_EQ(E2BIG, errno); 1451 } 1452 EXPECT_EQ(0, close(ruleset_fd)); 1453 } 1454 1455 TEST_F_FORK(layout1, empty_or_same_ruleset) 1456 { 1457 struct landlock_ruleset_attr ruleset_attr = {}; 1458 int ruleset_fd; 1459 1460 /* Tests empty handled_access_fs. */ 1461 ruleset_fd = 1462 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1463 ASSERT_LE(-1, ruleset_fd); 1464 ASSERT_EQ(ENOMSG, errno); 1465 1466 /* Enforces policy which denies read access to all files. */ 1467 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, NULL); 1468 1469 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1470 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1471 1472 /* Nests a policy which denies read access to all directories. */ 1473 ruleset_fd = 1474 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, NULL); 1475 enforce_ruleset(_metadata, ruleset_fd); 1476 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1477 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1478 1479 /* Enforces a second time with the same ruleset. */ 1480 enforce_ruleset(_metadata, ruleset_fd); 1481 ASSERT_EQ(0, close(ruleset_fd)); 1482 } 1483 1484 TEST_F_FORK(layout1, rule_on_mountpoint) 1485 { 1486 const struct rule rules[] = { 1487 { 1488 .path = dir_s1d1, 1489 .access = ACCESS_RO, 1490 }, 1491 { 1492 /* dir_s3d2 is a mount point. */ 1493 .path = dir_s3d2, 1494 .access = ACCESS_RO, 1495 }, 1496 {}, 1497 }; 1498 1499 enforce_fs(_metadata, ACCESS_RW, rules); 1500 1501 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1502 1503 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1504 1505 ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1506 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1507 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1508 } 1509 1510 TEST_F_FORK(layout1, rule_over_mountpoint) 1511 { 1512 const struct rule rules[] = { 1513 { 1514 .path = dir_s1d1, 1515 .access = ACCESS_RO, 1516 }, 1517 { 1518 /* dir_s3d2 is a mount point. */ 1519 .path = dir_s3d1, 1520 .access = ACCESS_RO, 1521 }, 1522 {}, 1523 }; 1524 1525 enforce_fs(_metadata, ACCESS_RW, rules); 1526 1527 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1528 1529 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1530 1531 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1532 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1533 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1534 } 1535 1536 /* 1537 * This test verifies that we can apply a landlock rule on the root directory 1538 * (which might require special handling). 1539 */ 1540 TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1541 { 1542 struct rule rules[] = { 1543 { 1544 .path = "/", 1545 .access = ACCESS_RO, 1546 }, 1547 {}, 1548 }; 1549 1550 enforce_fs(_metadata, ACCESS_RW, rules); 1551 1552 /* Checks allowed access. */ 1553 ASSERT_EQ(0, test_open("/", O_RDONLY)); 1554 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1555 1556 rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1557 enforce_fs(_metadata, ACCESS_RW, rules); 1558 1559 /* Checks denied access (on a directory). */ 1560 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1561 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1562 } 1563 1564 TEST_F_FORK(layout1, rule_over_root_deny) 1565 { 1566 const struct rule rules[] = { 1567 { 1568 .path = "/", 1569 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1570 }, 1571 {}, 1572 }; 1573 1574 enforce_fs(_metadata, ACCESS_RW, rules); 1575 1576 /* Checks denied access (on a directory). */ 1577 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1578 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1579 } 1580 1581 TEST_F_FORK(layout1, rule_inside_mount_ns) 1582 { 1583 const struct rule rules[] = { 1584 { 1585 .path = "s3d3", 1586 .access = ACCESS_RO, 1587 }, 1588 {}, 1589 }; 1590 1591 set_cap(_metadata, CAP_SYS_ADMIN); 1592 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1593 { 1594 TH_LOG("Failed to pivot root: %s", strerror(errno)); 1595 }; 1596 ASSERT_EQ(0, chdir("/")); 1597 clear_cap(_metadata, CAP_SYS_ADMIN); 1598 1599 enforce_fs(_metadata, ACCESS_RW, rules); 1600 1601 ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1602 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1603 } 1604 1605 TEST_F_FORK(layout1, mount_and_pivot) 1606 { 1607 const struct rule rules[] = { 1608 { 1609 .path = dir_s3d2, 1610 .access = ACCESS_RO, 1611 }, 1612 {}, 1613 }; 1614 1615 enforce_fs(_metadata, ACCESS_RW, rules); 1616 1617 set_cap(_metadata, CAP_SYS_ADMIN); 1618 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1619 ASSERT_EQ(EPERM, errno); 1620 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1621 ASSERT_EQ(EPERM, errno); 1622 clear_cap(_metadata, CAP_SYS_ADMIN); 1623 } 1624 1625 TEST_F_FORK(layout1, move_mount) 1626 { 1627 const struct rule rules[] = { 1628 { 1629 .path = dir_s3d2, 1630 .access = ACCESS_RO, 1631 }, 1632 {}, 1633 }; 1634 1635 set_cap(_metadata, CAP_SYS_ADMIN); 1636 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1637 dir_s1d2, 0)) 1638 { 1639 TH_LOG("Failed to move mount: %s", strerror(errno)); 1640 } 1641 1642 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1643 dir_s3d2, 0)); 1644 clear_cap(_metadata, CAP_SYS_ADMIN); 1645 1646 enforce_fs(_metadata, ACCESS_RW, rules); 1647 1648 set_cap(_metadata, CAP_SYS_ADMIN); 1649 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1650 dir_s1d2, 0)); 1651 ASSERT_EQ(EPERM, errno); 1652 clear_cap(_metadata, CAP_SYS_ADMIN); 1653 } 1654 1655 TEST_F_FORK(layout1, topology_changes_with_net_only) 1656 { 1657 const struct landlock_ruleset_attr ruleset_net = { 1658 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 1659 LANDLOCK_ACCESS_NET_CONNECT_TCP, 1660 }; 1661 1662 /* Add network restrictions. */ 1663 drop_access_rights(_metadata, &ruleset_net); 1664 1665 /* Mount, remount, move_mount, umount, and pivot_root checks. */ 1666 set_cap(_metadata, CAP_SYS_ADMIN); 1667 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s1d2)); 1668 ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC, NULL)); 1669 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1670 dir_s2d2, 0)); 1671 ASSERT_EQ(0, umount(dir_s2d2)); 1672 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1673 ASSERT_EQ(0, chdir("/")); 1674 clear_cap(_metadata, CAP_SYS_ADMIN); 1675 } 1676 1677 TEST_F_FORK(layout1, topology_changes_with_net_and_fs) 1678 { 1679 const struct landlock_ruleset_attr ruleset_net_fs = { 1680 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 1681 LANDLOCK_ACCESS_NET_CONNECT_TCP, 1682 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 1683 }; 1684 1685 /* Add network and filesystem restrictions. */ 1686 drop_access_rights(_metadata, &ruleset_net_fs); 1687 1688 /* Mount, remount, move_mount, umount, and pivot_root checks. */ 1689 set_cap(_metadata, CAP_SYS_ADMIN); 1690 ASSERT_EQ(-1, mount_opt(&mnt_tmp, dir_s1d2)); 1691 ASSERT_EQ(EPERM, errno); 1692 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC, NULL)); 1693 ASSERT_EQ(EPERM, errno); 1694 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1695 dir_s2d2, 0)); 1696 ASSERT_EQ(EPERM, errno); 1697 ASSERT_EQ(-1, umount(dir_s3d2)); 1698 ASSERT_EQ(EPERM, errno); 1699 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1700 ASSERT_EQ(EPERM, errno); 1701 clear_cap(_metadata, CAP_SYS_ADMIN); 1702 } 1703 1704 TEST_F_FORK(layout1, release_inodes) 1705 { 1706 const struct rule rules[] = { 1707 { 1708 .path = dir_s1d1, 1709 .access = ACCESS_RO, 1710 }, 1711 { 1712 .path = dir_s3d2, 1713 .access = ACCESS_RO, 1714 }, 1715 { 1716 .path = dir_s3d3, 1717 .access = ACCESS_RO, 1718 }, 1719 {}, 1720 }; 1721 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1722 1723 /* Unmount a file hierarchy while it is being used by a ruleset. */ 1724 set_cap(_metadata, CAP_SYS_ADMIN); 1725 ASSERT_EQ(0, umount(dir_s3d2)); 1726 clear_cap(_metadata, CAP_SYS_ADMIN); 1727 1728 enforce_ruleset(_metadata, ruleset_fd); 1729 EXPECT_EQ(0, close(ruleset_fd)); 1730 1731 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1732 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1733 /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1734 ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1735 } 1736 1737 /* 1738 * This test checks that a rule on a directory used as a mount point does not 1739 * grant access to the mount covering it. It is a generalization of the bind 1740 * mount case in layout3_fs.hostfs.release_inodes that tests hidden mount points. 1741 */ 1742 TEST_F_FORK(layout1, covered_rule) 1743 { 1744 const struct rule layer1[] = { 1745 { 1746 .path = dir_s3d2, 1747 .access = LANDLOCK_ACCESS_FS_READ_DIR, 1748 }, 1749 {}, 1750 }; 1751 int ruleset_fd; 1752 1753 /* Unmount to simplify FIXTURE_TEARDOWN. */ 1754 set_cap(_metadata, CAP_SYS_ADMIN); 1755 ASSERT_EQ(0, umount(dir_s3d2)); 1756 clear_cap(_metadata, CAP_SYS_ADMIN); 1757 1758 /* Creates a ruleset with the future hidden directory. */ 1759 ruleset_fd = 1760 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 1761 1762 /* Covers with a new mount point. */ 1763 set_cap(_metadata, CAP_SYS_ADMIN); 1764 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 1765 clear_cap(_metadata, CAP_SYS_ADMIN); 1766 1767 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1768 1769 enforce_ruleset(_metadata, ruleset_fd); 1770 ASSERT_EQ(0, close(ruleset_fd)); 1771 1772 /* Checks that access to the new mount point is denied. */ 1773 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1774 } 1775 1776 enum relative_access { 1777 REL_OPEN, 1778 REL_CHDIR, 1779 REL_CHROOT_ONLY, 1780 REL_CHROOT_CHDIR, 1781 }; 1782 1783 static void test_relative_path(struct __test_metadata *const _metadata, 1784 const enum relative_access rel) 1785 { 1786 /* 1787 * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1788 * is not a disconnected root directory). 1789 */ 1790 const struct rule layer1_base[] = { 1791 { 1792 .path = TMP_DIR, 1793 .access = ACCESS_RO, 1794 }, 1795 {}, 1796 }; 1797 const struct rule layer2_subs[] = { 1798 { 1799 .path = dir_s1d2, 1800 .access = ACCESS_RO, 1801 }, 1802 { 1803 .path = dir_s2d2, 1804 .access = ACCESS_RO, 1805 }, 1806 {}, 1807 }; 1808 int dirfd, ruleset_fd; 1809 1810 enforce_fs(_metadata, ACCESS_RW, layer1_base); 1811 1812 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1813 1814 ASSERT_LE(0, ruleset_fd); 1815 switch (rel) { 1816 case REL_OPEN: 1817 case REL_CHDIR: 1818 break; 1819 case REL_CHROOT_ONLY: 1820 ASSERT_EQ(0, chdir(dir_s2d2)); 1821 break; 1822 case REL_CHROOT_CHDIR: 1823 ASSERT_EQ(0, chdir(dir_s1d2)); 1824 break; 1825 default: 1826 ASSERT_TRUE(false); 1827 return; 1828 } 1829 1830 set_cap(_metadata, CAP_SYS_CHROOT); 1831 enforce_ruleset(_metadata, ruleset_fd); 1832 1833 switch (rel) { 1834 case REL_OPEN: 1835 dirfd = open(dir_s1d2, O_DIRECTORY); 1836 ASSERT_LE(0, dirfd); 1837 break; 1838 case REL_CHDIR: 1839 ASSERT_EQ(0, chdir(dir_s1d2)); 1840 dirfd = AT_FDCWD; 1841 break; 1842 case REL_CHROOT_ONLY: 1843 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1844 ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 1845 { 1846 TH_LOG("Failed to chroot: %s", strerror(errno)); 1847 } 1848 dirfd = AT_FDCWD; 1849 break; 1850 case REL_CHROOT_CHDIR: 1851 /* Do chroot into dir_s1d2. */ 1852 ASSERT_EQ(0, chroot(".")) 1853 { 1854 TH_LOG("Failed to chroot: %s", strerror(errno)); 1855 } 1856 dirfd = AT_FDCWD; 1857 break; 1858 } 1859 1860 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1861 test_open_rel(dirfd, "..", O_RDONLY)); 1862 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1863 1864 if (rel == REL_CHROOT_ONLY) { 1865 /* The current directory is dir_s2d2. */ 1866 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1867 } else { 1868 /* The current directory is dir_s1d2. */ 1869 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1870 } 1871 1872 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1873 /* Checks the root dir_s1d2. */ 1874 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1875 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1876 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1877 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1878 } 1879 1880 if (rel != REL_CHROOT_CHDIR) { 1881 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1882 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1883 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1884 O_RDONLY)); 1885 1886 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1887 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1888 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1889 O_RDONLY)); 1890 } 1891 1892 if (rel == REL_OPEN) 1893 ASSERT_EQ(0, close(dirfd)); 1894 ASSERT_EQ(0, close(ruleset_fd)); 1895 } 1896 1897 TEST_F_FORK(layout1, relative_open) 1898 { 1899 test_relative_path(_metadata, REL_OPEN); 1900 } 1901 1902 TEST_F_FORK(layout1, relative_chdir) 1903 { 1904 test_relative_path(_metadata, REL_CHDIR); 1905 } 1906 1907 TEST_F_FORK(layout1, relative_chroot_only) 1908 { 1909 test_relative_path(_metadata, REL_CHROOT_ONLY); 1910 } 1911 1912 TEST_F_FORK(layout1, relative_chroot_chdir) 1913 { 1914 test_relative_path(_metadata, REL_CHROOT_CHDIR); 1915 } 1916 1917 static void copy_file(struct __test_metadata *const _metadata, 1918 const char *const src_path, const char *const dst_path) 1919 { 1920 int dst_fd, src_fd; 1921 struct stat statbuf; 1922 1923 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1924 ASSERT_LE(0, dst_fd) 1925 { 1926 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1927 } 1928 src_fd = open(src_path, O_RDONLY | O_CLOEXEC); 1929 ASSERT_LE(0, src_fd) 1930 { 1931 TH_LOG("Failed to open \"%s\": %s", src_path, strerror(errno)); 1932 } 1933 ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1934 ASSERT_EQ(statbuf.st_size, 1935 sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 1936 ASSERT_EQ(0, close(src_fd)); 1937 ASSERT_EQ(0, close(dst_fd)); 1938 } 1939 1940 static void test_execute(struct __test_metadata *const _metadata, const int err, 1941 const char *const path) 1942 { 1943 int status; 1944 char *const argv[] = { (char *)path, NULL }; 1945 const pid_t child = fork(); 1946 1947 ASSERT_LE(0, child); 1948 if (child == 0) { 1949 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 1950 { 1951 TH_LOG("Failed to execute \"%s\": %s", path, 1952 strerror(errno)); 1953 }; 1954 ASSERT_EQ(err, errno); 1955 _exit(__test_passed(_metadata) ? 2 : 1); 1956 return; 1957 } 1958 ASSERT_EQ(child, waitpid(child, &status, 0)); 1959 ASSERT_EQ(1, WIFEXITED(status)); 1960 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 1961 { 1962 TH_LOG("Unexpected return code for \"%s\"", path); 1963 }; 1964 } 1965 1966 static void test_check_exec(struct __test_metadata *const _metadata, 1967 const int err, const char *const path) 1968 { 1969 int ret; 1970 char *const argv[] = { (char *)path, NULL }; 1971 1972 ret = sys_execveat(AT_FDCWD, path, argv, NULL, 1973 AT_EMPTY_PATH | AT_EXECVE_CHECK); 1974 if (err) { 1975 EXPECT_EQ(-1, ret); 1976 EXPECT_EQ(errno, err); 1977 } else { 1978 EXPECT_EQ(0, ret); 1979 } 1980 } 1981 1982 TEST_F_FORK(layout1, execute) 1983 { 1984 const struct rule rules[] = { 1985 { 1986 .path = dir_s1d2, 1987 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1988 }, 1989 {}, 1990 }; 1991 1992 copy_file(_metadata, bin_true, file1_s1d1); 1993 copy_file(_metadata, bin_true, file1_s1d2); 1994 copy_file(_metadata, bin_true, file1_s1d3); 1995 1996 /* Checks before file1_s1d1 being denied. */ 1997 test_execute(_metadata, 0, file1_s1d1); 1998 test_check_exec(_metadata, 0, file1_s1d1); 1999 2000 enforce_fs(_metadata, rules[0].access, rules); 2001 2002 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 2003 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2004 test_execute(_metadata, EACCES, file1_s1d1); 2005 test_check_exec(_metadata, EACCES, file1_s1d1); 2006 2007 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 2008 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2009 test_execute(_metadata, 0, file1_s1d2); 2010 test_check_exec(_metadata, 0, file1_s1d2); 2011 2012 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 2013 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2014 test_execute(_metadata, 0, file1_s1d3); 2015 test_check_exec(_metadata, 0, file1_s1d3); 2016 } 2017 2018 TEST_F_FORK(layout1, umount_sandboxer) 2019 { 2020 int pipe_child[2], pipe_parent[2]; 2021 char buf_parent; 2022 pid_t child; 2023 int status; 2024 2025 copy_file(_metadata, bin_sandbox_and_launch, file1_s3d3); 2026 ASSERT_EQ(0, pipe2(pipe_child, 0)); 2027 ASSERT_EQ(0, pipe2(pipe_parent, 0)); 2028 2029 child = fork(); 2030 ASSERT_LE(0, child); 2031 if (child == 0) { 2032 char pipe_child_str[12], pipe_parent_str[12]; 2033 char *const argv[] = { (char *)file1_s3d3, 2034 (char *)bin_wait_pipe, pipe_child_str, 2035 pipe_parent_str, NULL }; 2036 2037 /* Passes the pipe FDs to the executed binary and its child. */ 2038 EXPECT_EQ(0, close(pipe_child[0])); 2039 EXPECT_EQ(0, close(pipe_parent[1])); 2040 snprintf(pipe_child_str, sizeof(pipe_child_str), "%d", 2041 pipe_child[1]); 2042 snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d", 2043 pipe_parent[0]); 2044 2045 /* 2046 * We need bin_sandbox_and_launch (copied inside the mount as 2047 * file1_s3d3) to execute bin_wait_pipe (outside the mount) to 2048 * make sure the mount point will not be EBUSY because of 2049 * file1_s3d3 being in use. This avoids a potential race 2050 * condition between the following read() and umount() calls. 2051 */ 2052 ASSERT_EQ(0, execve(argv[0], argv, NULL)) 2053 { 2054 TH_LOG("Failed to execute \"%s\": %s", argv[0], 2055 strerror(errno)); 2056 }; 2057 _exit(1); 2058 return; 2059 } 2060 2061 EXPECT_EQ(0, close(pipe_child[1])); 2062 EXPECT_EQ(0, close(pipe_parent[0])); 2063 2064 /* Waits for the child to sandbox itself. */ 2065 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2066 2067 /* Tests that the sandboxer is tied to its mount point. */ 2068 set_cap(_metadata, CAP_SYS_ADMIN); 2069 EXPECT_EQ(-1, umount(dir_s3d2)); 2070 EXPECT_EQ(EBUSY, errno); 2071 clear_cap(_metadata, CAP_SYS_ADMIN); 2072 2073 /* Signals the child to launch a grandchild. */ 2074 EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2075 2076 /* Waits for the grandchild. */ 2077 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2078 2079 /* Tests that the domain's sandboxer is not tied to its mount point. */ 2080 set_cap(_metadata, CAP_SYS_ADMIN); 2081 EXPECT_EQ(0, umount(dir_s3d2)) 2082 { 2083 TH_LOG("Failed to umount \"%s\": %s", dir_s3d2, 2084 strerror(errno)); 2085 }; 2086 clear_cap(_metadata, CAP_SYS_ADMIN); 2087 2088 /* Signals the grandchild to terminate. */ 2089 EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2090 ASSERT_EQ(child, waitpid(child, &status, 0)); 2091 ASSERT_EQ(1, WIFEXITED(status)); 2092 ASSERT_EQ(0, WEXITSTATUS(status)); 2093 } 2094 2095 TEST_F_FORK(layout1, link) 2096 { 2097 const struct rule layer1[] = { 2098 { 2099 .path = dir_s1d2, 2100 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2101 }, 2102 {}, 2103 }; 2104 const struct rule layer2[] = { 2105 { 2106 .path = dir_s1d3, 2107 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2108 }, 2109 {}, 2110 }; 2111 2112 ASSERT_EQ(0, unlink(file1_s1d1)); 2113 ASSERT_EQ(0, unlink(file1_s1d2)); 2114 ASSERT_EQ(0, unlink(file1_s1d3)); 2115 2116 enforce_fs(_metadata, layer1[0].access, layer1); 2117 2118 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2119 ASSERT_EQ(EACCES, errno); 2120 2121 /* Denies linking because of reparenting. */ 2122 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2123 ASSERT_EQ(EXDEV, errno); 2124 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2125 ASSERT_EQ(EXDEV, errno); 2126 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2127 ASSERT_EQ(EXDEV, errno); 2128 2129 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2130 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2131 2132 /* Prepares for next unlinks. */ 2133 ASSERT_EQ(0, unlink(file2_s1d2)); 2134 ASSERT_EQ(0, unlink(file2_s1d3)); 2135 2136 enforce_fs(_metadata, layer2[0].access, layer2); 2137 2138 /* Checks that linkind doesn't require the ability to delete a file. */ 2139 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 2140 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 2141 } 2142 2143 static int test_rename(const char *const oldpath, const char *const newpath) 2144 { 2145 if (rename(oldpath, newpath)) 2146 return errno; 2147 return 0; 2148 } 2149 2150 static int test_exchange(const char *const oldpath, const char *const newpath) 2151 { 2152 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 2153 return errno; 2154 return 0; 2155 } 2156 2157 static int test_renameat(int olddirfd, const char *oldpath, int newdirfd, 2158 const char *newpath) 2159 { 2160 if (renameat2(olddirfd, oldpath, newdirfd, newpath, 0)) 2161 return errno; 2162 return 0; 2163 } 2164 2165 static int test_exchangeat(int olddirfd, const char *oldpath, int newdirfd, 2166 const char *newpath) 2167 { 2168 if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_EXCHANGE)) 2169 return errno; 2170 return 0; 2171 } 2172 2173 TEST_F_FORK(layout1, rename_file) 2174 { 2175 const struct rule rules[] = { 2176 { 2177 .path = dir_s1d3, 2178 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2179 }, 2180 { 2181 .path = dir_s2d2, 2182 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2183 }, 2184 {}, 2185 }; 2186 2187 ASSERT_EQ(0, unlink(file1_s1d2)); 2188 2189 enforce_fs(_metadata, rules[0].access, rules); 2190 2191 /* 2192 * Tries to replace a file, from a directory that allows file removal, 2193 * but to a different directory (which also allows file removal). 2194 */ 2195 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 2196 ASSERT_EQ(EXDEV, errno); 2197 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 2198 RENAME_EXCHANGE)); 2199 ASSERT_EQ(EXDEV, errno); 2200 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2201 RENAME_EXCHANGE)); 2202 ASSERT_EQ(EXDEV, errno); 2203 2204 /* 2205 * Tries to replace a file, from a directory that denies file removal, 2206 * to a different directory (which allows file removal). 2207 */ 2208 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2209 ASSERT_EQ(EACCES, errno); 2210 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 2211 RENAME_EXCHANGE)); 2212 ASSERT_EQ(EACCES, errno); 2213 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 2214 RENAME_EXCHANGE)); 2215 ASSERT_EQ(EXDEV, errno); 2216 2217 /* Exchanges files and directories that partially allow removal. */ 2218 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 2219 RENAME_EXCHANGE)); 2220 ASSERT_EQ(EACCES, errno); 2221 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 2222 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 2223 ASSERT_EQ(EACCES, errno); 2224 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 2225 RENAME_EXCHANGE)); 2226 ASSERT_EQ(EACCES, errno); 2227 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 2228 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2229 ASSERT_EQ(EACCES, errno); 2230 2231 /* Renames files with different parents. */ 2232 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2233 ASSERT_EQ(EXDEV, errno); 2234 ASSERT_EQ(0, unlink(file1_s1d3)); 2235 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2236 ASSERT_EQ(EACCES, errno); 2237 2238 /* Exchanges and renames files with same parent. */ 2239 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 2240 RENAME_EXCHANGE)); 2241 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 2242 2243 /* Exchanges files and directories with same parent, twice. */ 2244 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2245 RENAME_EXCHANGE)); 2246 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2247 RENAME_EXCHANGE)); 2248 } 2249 2250 TEST_F_FORK(layout1, rename_dir) 2251 { 2252 const struct rule rules[] = { 2253 { 2254 .path = dir_s1d2, 2255 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2256 }, 2257 { 2258 .path = dir_s2d1, 2259 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2260 }, 2261 {}, 2262 }; 2263 2264 /* Empties dir_s1d3 to allow renaming. */ 2265 ASSERT_EQ(0, unlink(file1_s1d3)); 2266 ASSERT_EQ(0, unlink(file2_s1d3)); 2267 2268 enforce_fs(_metadata, rules[0].access, rules); 2269 2270 /* Exchanges and renames directory to a different parent. */ 2271 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2272 RENAME_EXCHANGE)); 2273 ASSERT_EQ(EXDEV, errno); 2274 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 2275 ASSERT_EQ(EXDEV, errno); 2276 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2277 RENAME_EXCHANGE)); 2278 ASSERT_EQ(EXDEV, errno); 2279 2280 /* 2281 * Exchanges directory to the same parent, which doesn't allow 2282 * directory removal. 2283 */ 2284 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 2285 RENAME_EXCHANGE)); 2286 ASSERT_EQ(EACCES, errno); 2287 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 2288 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 2289 ASSERT_EQ(EACCES, errno); 2290 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 2291 RENAME_EXCHANGE)); 2292 ASSERT_EQ(EACCES, errno); 2293 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 2294 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2295 ASSERT_EQ(EACCES, errno); 2296 2297 /* 2298 * Exchanges and renames directory to the same parent, which allows 2299 * directory removal. 2300 */ 2301 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 2302 RENAME_EXCHANGE)); 2303 ASSERT_EQ(0, unlink(dir_s1d3)); 2304 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2305 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 2306 ASSERT_EQ(0, rmdir(dir_s1d3)); 2307 } 2308 2309 TEST_F_FORK(layout1, reparent_refer) 2310 { 2311 const struct rule layer1[] = { 2312 { 2313 .path = dir_s1d2, 2314 .access = LANDLOCK_ACCESS_FS_REFER, 2315 }, 2316 { 2317 .path = dir_s2d2, 2318 .access = LANDLOCK_ACCESS_FS_REFER, 2319 }, 2320 {}, 2321 }; 2322 2323 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2324 2325 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2326 ASSERT_EQ(EXDEV, errno); 2327 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2328 ASSERT_EQ(EXDEV, errno); 2329 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2330 ASSERT_EQ(EXDEV, errno); 2331 2332 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2333 ASSERT_EQ(EXDEV, errno); 2334 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2335 ASSERT_EQ(EXDEV, errno); 2336 /* 2337 * Moving should only be allowed when the source and the destination 2338 * parent directory have REFER. 2339 */ 2340 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2341 ASSERT_EQ(ENOTEMPTY, errno); 2342 ASSERT_EQ(0, unlink(file1_s2d3)); 2343 ASSERT_EQ(0, unlink(file2_s2d3)); 2344 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2345 } 2346 2347 /* Checks renames beneath dir_s1d1. */ 2348 static void refer_denied_by_default(struct __test_metadata *const _metadata, 2349 const struct rule layer1[], 2350 const int layer1_err, 2351 const struct rule layer2[]) 2352 { 2353 ASSERT_EQ(0, unlink(file1_s1d2)); 2354 2355 enforce_fs(_metadata, layer1[0].access, layer1); 2356 2357 /* 2358 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 2359 * layer1_err), then it allows some different-parent renames and links. 2360 */ 2361 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 2362 if (layer1_err == 0) 2363 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 2364 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 2365 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 2366 2367 enforce_fs(_metadata, layer2[0].access, layer2); 2368 2369 /* 2370 * Now, either the first or the second layer does not handle 2371 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 2372 * renames and links are denied, thus making the layer handling 2373 * LANDLOCK_ACCESS_FS_REFER null and void. 2374 */ 2375 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 2376 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 2377 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 2378 } 2379 2380 const struct rule layer_dir_s1d1_refer[] = { 2381 { 2382 .path = dir_s1d1, 2383 .access = LANDLOCK_ACCESS_FS_REFER, 2384 }, 2385 {}, 2386 }; 2387 2388 const struct rule layer_dir_s1d1_execute[] = { 2389 { 2390 /* Matches a parent directory. */ 2391 .path = dir_s1d1, 2392 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2393 }, 2394 {}, 2395 }; 2396 2397 const struct rule layer_dir_s2d1_execute[] = { 2398 { 2399 /* Does not match a parent directory. */ 2400 .path = dir_s2d1, 2401 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2402 }, 2403 {}, 2404 }; 2405 2406 /* 2407 * Tests precedence over renames: denied by default for different parent 2408 * directories, *with* a rule matching a parent directory, but not directly 2409 * denying access (with MAKE_REG nor REMOVE). 2410 */ 2411 TEST_F_FORK(layout1, refer_denied_by_default1) 2412 { 2413 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2414 layer_dir_s1d1_execute); 2415 } 2416 2417 /* 2418 * Same test but this time turning around the ABI version order: the first 2419 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2420 */ 2421 TEST_F_FORK(layout1, refer_denied_by_default2) 2422 { 2423 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 2424 layer_dir_s1d1_refer); 2425 } 2426 2427 /* 2428 * Tests precedence over renames: denied by default for different parent 2429 * directories, *without* a rule matching a parent directory, but not directly 2430 * denying access (with MAKE_REG nor REMOVE). 2431 */ 2432 TEST_F_FORK(layout1, refer_denied_by_default3) 2433 { 2434 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2435 layer_dir_s2d1_execute); 2436 } 2437 2438 /* 2439 * Same test but this time turning around the ABI version order: the first 2440 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2441 */ 2442 TEST_F_FORK(layout1, refer_denied_by_default4) 2443 { 2444 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 2445 layer_dir_s1d1_refer); 2446 } 2447 2448 /* 2449 * Tests walking through a denied root mount. 2450 */ 2451 TEST_F_FORK(layout1, refer_mount_root_deny) 2452 { 2453 int root_fd; 2454 2455 /* Creates a mount object from a non-mount point. */ 2456 set_cap(_metadata, CAP_SYS_ADMIN); 2457 root_fd = 2458 open_tree(AT_FDCWD, dir_s1d1, 2459 AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); 2460 clear_cap(_metadata, CAP_SYS_ADMIN); 2461 ASSERT_LE(0, root_fd); 2462 2463 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, NULL); 2464 2465 /* Link denied by Landlock: EACCES. */ 2466 EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0)); 2467 EXPECT_EQ(EACCES, errno); 2468 2469 /* renameat2() always returns EBUSY. */ 2470 EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0)); 2471 EXPECT_EQ(EBUSY, errno); 2472 2473 EXPECT_EQ(0, close(root_fd)); 2474 } 2475 2476 TEST_F_FORK(layout1, refer_part_mount_tree_is_allowed) 2477 { 2478 const struct rule layer1[] = { 2479 { 2480 /* Parent mount point. */ 2481 .path = dir_s3d1, 2482 .access = LANDLOCK_ACCESS_FS_REFER | 2483 LANDLOCK_ACCESS_FS_MAKE_REG, 2484 }, 2485 { 2486 /* 2487 * Removing the source file is allowed because its 2488 * access rights are already a superset of the 2489 * destination. 2490 */ 2491 .path = dir_s3d4, 2492 .access = LANDLOCK_ACCESS_FS_REFER | 2493 LANDLOCK_ACCESS_FS_MAKE_REG | 2494 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2495 }, 2496 {}, 2497 }; 2498 2499 ASSERT_EQ(0, unlink(file1_s3d3)); 2500 enforce_fs(_metadata, 2501 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 2502 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2503 layer1); 2504 2505 ASSERT_EQ(0, rename(file1_s3d4, file1_s3d3)); 2506 } 2507 2508 TEST_F_FORK(layout1, reparent_link) 2509 { 2510 const struct rule layer1[] = { 2511 { 2512 .path = dir_s1d2, 2513 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2514 }, 2515 { 2516 .path = dir_s1d3, 2517 .access = LANDLOCK_ACCESS_FS_REFER, 2518 }, 2519 { 2520 .path = dir_s2d2, 2521 .access = LANDLOCK_ACCESS_FS_REFER, 2522 }, 2523 { 2524 .path = dir_s2d3, 2525 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2526 }, 2527 {}, 2528 }; 2529 2530 enforce_fs(_metadata, 2531 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 2532 layer1); 2533 2534 ASSERT_EQ(0, unlink(file1_s1d1)); 2535 ASSERT_EQ(0, unlink(file1_s1d2)); 2536 ASSERT_EQ(0, unlink(file1_s1d3)); 2537 2538 /* Denies linking because of missing MAKE_REG. */ 2539 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2540 ASSERT_EQ(EACCES, errno); 2541 /* Denies linking because of missing source and destination REFER. */ 2542 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2543 ASSERT_EQ(EXDEV, errno); 2544 /* Denies linking because of missing source REFER. */ 2545 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2546 ASSERT_EQ(EXDEV, errno); 2547 2548 /* Denies linking because of missing MAKE_REG. */ 2549 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2550 ASSERT_EQ(EACCES, errno); 2551 /* Denies linking because of missing destination REFER. */ 2552 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2553 ASSERT_EQ(EXDEV, errno); 2554 2555 /* Allows linking because of REFER and MAKE_REG. */ 2556 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2557 ASSERT_EQ(0, unlink(file1_s2d2)); 2558 /* Reverse linking denied because of missing MAKE_REG. */ 2559 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2560 ASSERT_EQ(EACCES, errno); 2561 ASSERT_EQ(0, unlink(file1_s2d3)); 2562 /* Checks reverse linking. */ 2563 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2564 ASSERT_EQ(0, unlink(file1_s1d3)); 2565 2566 /* 2567 * This is OK for a file link, but it should not be allowed for a 2568 * directory rename (because of the superset of access rights. 2569 */ 2570 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2571 ASSERT_EQ(0, unlink(file1_s1d3)); 2572 2573 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2574 ASSERT_EQ(EXDEV, errno); 2575 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2576 ASSERT_EQ(EXDEV, errno); 2577 2578 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2579 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2580 } 2581 2582 TEST_F_FORK(layout1, reparent_rename) 2583 { 2584 /* Same rules as for reparent_link. */ 2585 const struct rule layer1[] = { 2586 { 2587 .path = dir_s1d2, 2588 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2589 }, 2590 { 2591 .path = dir_s1d3, 2592 .access = LANDLOCK_ACCESS_FS_REFER, 2593 }, 2594 { 2595 .path = dir_s2d2, 2596 .access = LANDLOCK_ACCESS_FS_REFER, 2597 }, 2598 { 2599 .path = dir_s2d3, 2600 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2601 }, 2602 {}, 2603 }; 2604 2605 enforce_fs(_metadata, 2606 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 2607 layer1); 2608 2609 ASSERT_EQ(0, unlink(file1_s1d2)); 2610 ASSERT_EQ(0, unlink(file1_s1d3)); 2611 2612 /* Denies renaming because of missing MAKE_REG. */ 2613 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2614 RENAME_EXCHANGE)); 2615 ASSERT_EQ(EACCES, errno); 2616 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2617 RENAME_EXCHANGE)); 2618 ASSERT_EQ(EACCES, errno); 2619 ASSERT_EQ(0, unlink(file1_s1d1)); 2620 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2621 ASSERT_EQ(EACCES, errno); 2622 /* Even denies same file exchange. */ 2623 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2624 RENAME_EXCHANGE)); 2625 ASSERT_EQ(EACCES, errno); 2626 2627 /* Denies renaming because of missing source and destination REFER. */ 2628 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2629 ASSERT_EQ(EXDEV, errno); 2630 /* 2631 * Denies renaming because of missing MAKE_REG, source and destination 2632 * REFER. 2633 */ 2634 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2635 RENAME_EXCHANGE)); 2636 ASSERT_EQ(EACCES, errno); 2637 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2638 RENAME_EXCHANGE)); 2639 ASSERT_EQ(EACCES, errno); 2640 2641 /* Denies renaming because of missing source REFER. */ 2642 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2643 ASSERT_EQ(EXDEV, errno); 2644 /* Denies renaming because of missing MAKE_REG. */ 2645 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2646 RENAME_EXCHANGE)); 2647 ASSERT_EQ(EACCES, errno); 2648 2649 /* Denies renaming because of missing MAKE_REG. */ 2650 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2651 ASSERT_EQ(EACCES, errno); 2652 /* Denies renaming because of missing destination REFER*/ 2653 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2654 ASSERT_EQ(EXDEV, errno); 2655 2656 /* Denies exchange because of one missing MAKE_REG. */ 2657 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2658 RENAME_EXCHANGE)); 2659 ASSERT_EQ(EACCES, errno); 2660 /* Allows renaming because of REFER and MAKE_REG. */ 2661 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2662 2663 /* Reverse renaming denied because of missing MAKE_REG. */ 2664 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2665 ASSERT_EQ(EACCES, errno); 2666 ASSERT_EQ(0, unlink(file1_s2d3)); 2667 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2668 2669 /* Tests reverse renaming. */ 2670 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2671 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2672 RENAME_EXCHANGE)); 2673 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2674 2675 /* 2676 * This is OK for a file rename, but it should not be allowed for a 2677 * directory rename (because of the superset of access rights). 2678 */ 2679 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2680 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2681 2682 /* 2683 * Tests superset restrictions applied to directories. Not only the 2684 * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2685 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2686 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2687 * directly by the moved dir_s2d3. 2688 */ 2689 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2690 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2691 /* 2692 * The first rename is allowed but not the exchange because dir_s1d3's 2693 * parent (dir_s1d2) doesn't have REFER. 2694 */ 2695 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2696 RENAME_EXCHANGE)); 2697 ASSERT_EQ(EXDEV, errno); 2698 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2699 RENAME_EXCHANGE)); 2700 ASSERT_EQ(EXDEV, errno); 2701 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2702 ASSERT_EQ(EXDEV, errno); 2703 2704 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2705 ASSERT_EQ(EXDEV, errno); 2706 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2707 ASSERT_EQ(EXDEV, errno); 2708 2709 /* Renaming in the same directory is always allowed. */ 2710 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2711 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2712 2713 ASSERT_EQ(0, unlink(file1_s1d2)); 2714 /* Denies because of missing source MAKE_REG and destination REFER. */ 2715 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2716 ASSERT_EQ(EXDEV, errno); 2717 2718 ASSERT_EQ(0, unlink(file1_s1d3)); 2719 /* Denies because of missing source MAKE_REG and REFER. */ 2720 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2721 ASSERT_EQ(EXDEV, errno); 2722 } 2723 2724 static void 2725 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2726 { 2727 const struct rule layer1[] = { 2728 { 2729 .path = dir_s1d2, 2730 .access = LANDLOCK_ACCESS_FS_REFER, 2731 }, 2732 { 2733 /* Interesting for the layer2 tests. */ 2734 .path = dir_s1d3, 2735 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2736 }, 2737 { 2738 .path = dir_s2d2, 2739 .access = LANDLOCK_ACCESS_FS_REFER, 2740 }, 2741 { 2742 .path = dir_s2d3, 2743 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2744 }, 2745 {}, 2746 }; 2747 enforce_fs(_metadata, 2748 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 2749 layer1); 2750 } 2751 2752 static void 2753 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2754 { 2755 const struct rule layer2[] = { 2756 { 2757 .path = dir_s2d3, 2758 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2759 }, 2760 {}, 2761 }; 2762 /* 2763 * Same checks as before but with a second layer and a new MAKE_DIR 2764 * rule (and no explicit handling of REFER). 2765 */ 2766 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2767 } 2768 2769 TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2770 { 2771 ASSERT_EQ(0, unlink(file1_s2d2)); 2772 ASSERT_EQ(0, unlink(file1_s2d3)); 2773 2774 reparent_exdev_layers_enforce1(_metadata); 2775 2776 /* 2777 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2778 * because it doesn't inherit new access rights. 2779 */ 2780 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2781 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2782 2783 /* 2784 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2785 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2786 * already allowed for dir_s1d3. 2787 */ 2788 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2789 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2790 2791 /* 2792 * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2793 * because it cannot inherit MAKE_REG right (which is dedicated to 2794 * directories). 2795 */ 2796 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2797 2798 reparent_exdev_layers_enforce2(_metadata); 2799 2800 /* 2801 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2802 * MAKE_DIR is not tied to dir_s2d2. 2803 */ 2804 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2805 ASSERT_EQ(EACCES, errno); 2806 2807 /* 2808 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2809 * would grants MAKE_REG and MAKE_DIR rights to it. 2810 */ 2811 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2812 ASSERT_EQ(EXDEV, errno); 2813 2814 /* 2815 * Moving the file2_s1d3 file below dir_s2d3 is denied because the 2816 * second layer does not handle REFER, which is always denied by 2817 * default. 2818 */ 2819 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 2820 ASSERT_EQ(EXDEV, errno); 2821 } 2822 2823 TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2824 { 2825 reparent_exdev_layers_enforce1(_metadata); 2826 2827 /* Checks EACCES predominance over EXDEV. */ 2828 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2829 ASSERT_EQ(EACCES, errno); 2830 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2831 ASSERT_EQ(EACCES, errno); 2832 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2833 ASSERT_EQ(EXDEV, errno); 2834 /* Modify layout! */ 2835 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 2836 2837 /* Without REFER source. */ 2838 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2839 ASSERT_EQ(EXDEV, errno); 2840 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2841 ASSERT_EQ(EXDEV, errno); 2842 2843 reparent_exdev_layers_enforce2(_metadata); 2844 2845 /* Checks EACCES predominance over EXDEV. */ 2846 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2847 ASSERT_EQ(EACCES, errno); 2848 /* Checks with actual file2_s1d2. */ 2849 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 2850 ASSERT_EQ(EACCES, errno); 2851 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2852 ASSERT_EQ(EXDEV, errno); 2853 /* 2854 * Modifying the layout is now denied because the second layer does not 2855 * handle REFER, which is always denied by default. 2856 */ 2857 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2858 ASSERT_EQ(EXDEV, errno); 2859 2860 /* Without REFER source, EACCES wins over EXDEV. */ 2861 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2862 ASSERT_EQ(EACCES, errno); 2863 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2864 ASSERT_EQ(EACCES, errno); 2865 } 2866 2867 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 2868 { 2869 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 2870 file2_s2d3; 2871 2872 ASSERT_EQ(0, unlink(file1_s1d2)); 2873 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2874 ASSERT_EQ(0, unlink(file2_s2d3)); 2875 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2876 2877 reparent_exdev_layers_enforce1(_metadata); 2878 2879 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2880 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2881 RENAME_EXCHANGE)); 2882 ASSERT_EQ(EACCES, errno); 2883 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2884 RENAME_EXCHANGE)); 2885 ASSERT_EQ(EACCES, errno); 2886 2887 /* 2888 * Checks with directories which creation could be allowed, but denied 2889 * because of access rights that would be inherited. 2890 */ 2891 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2892 dir_file2_s2d3, RENAME_EXCHANGE)); 2893 ASSERT_EQ(EXDEV, errno); 2894 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2895 dir_file1_s1d2, RENAME_EXCHANGE)); 2896 ASSERT_EQ(EXDEV, errno); 2897 2898 /* Checks with same access rights. */ 2899 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2900 RENAME_EXCHANGE)); 2901 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2902 RENAME_EXCHANGE)); 2903 2904 /* Checks with different (child-only) access rights. */ 2905 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2906 RENAME_EXCHANGE)); 2907 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2908 RENAME_EXCHANGE)); 2909 2910 /* 2911 * Checks that exchange between file and directory are consistent. 2912 * 2913 * Moving a file (file1_s2d2) to a directory which only grants more 2914 * directory-related access rights is allowed, and at the same time 2915 * moving a directory (dir_file2_s2d3) to another directory which 2916 * grants less access rights is allowed too. 2917 * 2918 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 2919 */ 2920 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2921 RENAME_EXCHANGE)); 2922 /* 2923 * However, moving back the directory is denied because it would get 2924 * more access rights than the current state and because file creation 2925 * is forbidden (in dir_s2d2). 2926 */ 2927 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2928 RENAME_EXCHANGE)); 2929 ASSERT_EQ(EACCES, errno); 2930 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2931 RENAME_EXCHANGE)); 2932 ASSERT_EQ(EACCES, errno); 2933 2934 reparent_exdev_layers_enforce2(_metadata); 2935 2936 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2937 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2938 RENAME_EXCHANGE)); 2939 ASSERT_EQ(EACCES, errno); 2940 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2941 RENAME_EXCHANGE)); 2942 ASSERT_EQ(EACCES, errno); 2943 2944 /* Checks with directories which creation is now denied. */ 2945 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2946 dir_file2_s2d3, RENAME_EXCHANGE)); 2947 ASSERT_EQ(EACCES, errno); 2948 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2949 dir_file1_s1d2, RENAME_EXCHANGE)); 2950 ASSERT_EQ(EACCES, errno); 2951 2952 /* Checks with different (child-only) access rights. */ 2953 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2954 RENAME_EXCHANGE)); 2955 /* Denied because of MAKE_DIR. */ 2956 ASSERT_EQ(EACCES, errno); 2957 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2958 RENAME_EXCHANGE)); 2959 ASSERT_EQ(EACCES, errno); 2960 2961 /* Checks with different (child-only) access rights. */ 2962 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2963 RENAME_EXCHANGE)); 2964 /* Denied because of MAKE_DIR. */ 2965 ASSERT_EQ(EACCES, errno); 2966 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2967 RENAME_EXCHANGE)); 2968 ASSERT_EQ(EACCES, errno); 2969 2970 /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 2971 } 2972 2973 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 2974 { 2975 const char *const dir_file2_s2d3 = file2_s2d3; 2976 2977 ASSERT_EQ(0, unlink(file2_s2d3)); 2978 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2979 2980 reparent_exdev_layers_enforce1(_metadata); 2981 reparent_exdev_layers_enforce2(_metadata); 2982 2983 /* Checks that exchange between file and directory are consistent. */ 2984 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2985 RENAME_EXCHANGE)); 2986 ASSERT_EQ(EACCES, errno); 2987 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2988 RENAME_EXCHANGE)); 2989 ASSERT_EQ(EACCES, errno); 2990 } 2991 2992 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 2993 { 2994 const char *const dir_file2_s2d3 = file2_s2d3; 2995 2996 ASSERT_EQ(0, unlink(file2_s2d3)); 2997 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2998 2999 reparent_exdev_layers_enforce1(_metadata); 3000 3001 /* 3002 * Checks that exchange between file and directory are consistent, 3003 * including with inverted arguments (see 3004 * layout1.reparent_exdev_layers_exchange1). 3005 */ 3006 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3007 RENAME_EXCHANGE)); 3008 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 3009 RENAME_EXCHANGE)); 3010 ASSERT_EQ(EACCES, errno); 3011 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3012 RENAME_EXCHANGE)); 3013 ASSERT_EQ(EACCES, errno); 3014 } 3015 3016 TEST_F_FORK(layout1, reparent_remove) 3017 { 3018 const struct rule layer1[] = { 3019 { 3020 .path = dir_s1d1, 3021 .access = LANDLOCK_ACCESS_FS_REFER | 3022 LANDLOCK_ACCESS_FS_REMOVE_DIR, 3023 }, 3024 { 3025 .path = dir_s1d2, 3026 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 3027 }, 3028 { 3029 .path = dir_s2d1, 3030 .access = LANDLOCK_ACCESS_FS_REFER | 3031 LANDLOCK_ACCESS_FS_REMOVE_FILE, 3032 }, 3033 {}, 3034 }; 3035 3036 enforce_fs(_metadata, 3037 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 3038 LANDLOCK_ACCESS_FS_REMOVE_FILE, 3039 layer1); 3040 3041 /* Access denied because of wrong/swapped remove file/dir. */ 3042 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 3043 ASSERT_EQ(EACCES, errno); 3044 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 3045 ASSERT_EQ(EACCES, errno); 3046 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 3047 RENAME_EXCHANGE)); 3048 ASSERT_EQ(EACCES, errno); 3049 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 3050 RENAME_EXCHANGE)); 3051 ASSERT_EQ(EACCES, errno); 3052 3053 /* Access allowed thanks to the matching rights. */ 3054 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 3055 ASSERT_EQ(EISDIR, errno); 3056 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 3057 ASSERT_EQ(ENOTDIR, errno); 3058 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 3059 ASSERT_EQ(ENOTDIR, errno); 3060 ASSERT_EQ(0, unlink(file1_s2d1)); 3061 ASSERT_EQ(0, unlink(file1_s1d3)); 3062 ASSERT_EQ(0, unlink(file2_s1d3)); 3063 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 3064 3065 /* Effectively removes a file and a directory by exchanging them. */ 3066 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 3067 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 3068 RENAME_EXCHANGE)); 3069 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 3070 RENAME_EXCHANGE)); 3071 ASSERT_EQ(EACCES, errno); 3072 } 3073 3074 TEST_F_FORK(layout1, reparent_dom_superset) 3075 { 3076 const struct rule layer1[] = { 3077 { 3078 .path = dir_s1d2, 3079 .access = LANDLOCK_ACCESS_FS_REFER, 3080 }, 3081 { 3082 .path = file1_s1d2, 3083 .access = LANDLOCK_ACCESS_FS_EXECUTE, 3084 }, 3085 { 3086 .path = dir_s1d3, 3087 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 3088 LANDLOCK_ACCESS_FS_EXECUTE, 3089 }, 3090 { 3091 .path = dir_s2d2, 3092 .access = LANDLOCK_ACCESS_FS_REFER | 3093 LANDLOCK_ACCESS_FS_EXECUTE | 3094 LANDLOCK_ACCESS_FS_MAKE_SOCK, 3095 }, 3096 { 3097 .path = dir_s2d3, 3098 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3099 LANDLOCK_ACCESS_FS_MAKE_FIFO, 3100 }, 3101 {}, 3102 }; 3103 3104 enforce_fs(_metadata, 3105 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE | 3106 LANDLOCK_ACCESS_FS_MAKE_SOCK | 3107 LANDLOCK_ACCESS_FS_READ_FILE | 3108 LANDLOCK_ACCESS_FS_MAKE_FIFO, 3109 layer1); 3110 3111 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 3112 ASSERT_EQ(EXDEV, errno); 3113 /* 3114 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 3115 * access right. 3116 */ 3117 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 3118 ASSERT_EQ(EXDEV, errno); 3119 /* 3120 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 3121 * superset of access rights compared to dir_s1d2, because file1_s1d2 3122 * already has these access rights anyway. 3123 */ 3124 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 3125 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 3126 3127 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 3128 ASSERT_EQ(EXDEV, errno); 3129 /* 3130 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 3131 * right. 3132 */ 3133 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 3134 ASSERT_EQ(EXDEV, errno); 3135 /* 3136 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 3137 * of access rights compared to dir_s1d2, because dir_s1d3 already has 3138 * these access rights anyway. 3139 */ 3140 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 3141 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 3142 3143 /* 3144 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 3145 * will be denied because the new inherited access rights from dir_s1d2 3146 * will be less than the destination (original) dir_s2d3. This is a 3147 * sinkhole scenario where we cannot move back files or directories. 3148 */ 3149 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 3150 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 3151 ASSERT_EQ(EXDEV, errno); 3152 ASSERT_EQ(0, unlink(file2_s1d2)); 3153 ASSERT_EQ(0, unlink(file2_s2d3)); 3154 /* 3155 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 3156 * MAKE_SOCK which were inherited from dir_s1d3. 3157 */ 3158 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 3159 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 3160 ASSERT_EQ(EXDEV, errno); 3161 } 3162 3163 TEST_F_FORK(layout1, remove_dir) 3164 { 3165 const struct rule rules[] = { 3166 { 3167 .path = dir_s1d2, 3168 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 3169 }, 3170 {}, 3171 }; 3172 3173 ASSERT_EQ(0, unlink(file1_s1d1)); 3174 ASSERT_EQ(0, unlink(file1_s1d2)); 3175 ASSERT_EQ(0, unlink(file1_s1d3)); 3176 ASSERT_EQ(0, unlink(file2_s1d3)); 3177 3178 enforce_fs(_metadata, rules[0].access, rules); 3179 3180 ASSERT_EQ(0, rmdir(dir_s1d3)); 3181 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 3182 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 3183 3184 /* dir_s1d2 itself cannot be removed. */ 3185 ASSERT_EQ(-1, rmdir(dir_s1d2)); 3186 ASSERT_EQ(EACCES, errno); 3187 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 3188 ASSERT_EQ(EACCES, errno); 3189 ASSERT_EQ(-1, rmdir(dir_s1d1)); 3190 ASSERT_EQ(EACCES, errno); 3191 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 3192 ASSERT_EQ(EACCES, errno); 3193 } 3194 3195 TEST_F_FORK(layout1, remove_file) 3196 { 3197 const struct rule rules[] = { 3198 { 3199 .path = dir_s1d2, 3200 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 3201 }, 3202 {}, 3203 }; 3204 3205 enforce_fs(_metadata, rules[0].access, rules); 3206 3207 ASSERT_EQ(-1, unlink(file1_s1d1)); 3208 ASSERT_EQ(EACCES, errno); 3209 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 3210 ASSERT_EQ(EACCES, errno); 3211 ASSERT_EQ(0, unlink(file1_s1d2)); 3212 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 3213 } 3214 3215 static void test_make_file(struct __test_metadata *const _metadata, 3216 const __u64 access, const mode_t mode, 3217 const dev_t dev) 3218 { 3219 const struct rule rules[] = { 3220 { 3221 .path = dir_s1d2, 3222 .access = access, 3223 }, 3224 {}, 3225 }; 3226 3227 ASSERT_EQ(0, unlink(file1_s1d1)); 3228 ASSERT_EQ(0, unlink(file2_s1d1)); 3229 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 3230 { 3231 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 3232 strerror(errno)); 3233 }; 3234 3235 ASSERT_EQ(0, unlink(file1_s1d2)); 3236 ASSERT_EQ(0, unlink(file2_s1d2)); 3237 3238 ASSERT_EQ(0, unlink(file1_s1d3)); 3239 ASSERT_EQ(0, unlink(file2_s1d3)); 3240 3241 enforce_fs(_metadata, access, rules); 3242 3243 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 3244 ASSERT_EQ(EACCES, errno); 3245 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3246 ASSERT_EQ(EACCES, errno); 3247 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3248 ASSERT_EQ(EACCES, errno); 3249 3250 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 3251 { 3252 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 3253 strerror(errno)); 3254 }; 3255 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3256 ASSERT_EQ(0, unlink(file2_s1d2)); 3257 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3258 3259 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 3260 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3261 ASSERT_EQ(0, unlink(file2_s1d3)); 3262 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3263 } 3264 3265 TEST_F_FORK(layout1, make_char) 3266 { 3267 /* Creates a /dev/null device. */ 3268 set_cap(_metadata, CAP_MKNOD); 3269 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 3270 makedev(1, 3)); 3271 } 3272 3273 TEST_F_FORK(layout1, make_block) 3274 { 3275 /* Creates a /dev/loop0 device. */ 3276 set_cap(_metadata, CAP_MKNOD); 3277 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 3278 makedev(7, 0)); 3279 } 3280 3281 TEST_F_FORK(layout1, make_reg_1) 3282 { 3283 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 3284 } 3285 3286 TEST_F_FORK(layout1, make_reg_2) 3287 { 3288 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 3289 } 3290 3291 TEST_F_FORK(layout1, make_sock) 3292 { 3293 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 3294 } 3295 3296 TEST_F_FORK(layout1, make_fifo) 3297 { 3298 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 3299 } 3300 3301 TEST_F_FORK(layout1, make_sym) 3302 { 3303 const struct rule rules[] = { 3304 { 3305 .path = dir_s1d2, 3306 .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 3307 }, 3308 {}, 3309 }; 3310 3311 ASSERT_EQ(0, unlink(file1_s1d1)); 3312 ASSERT_EQ(0, unlink(file2_s1d1)); 3313 ASSERT_EQ(0, symlink("none", file2_s1d1)); 3314 3315 ASSERT_EQ(0, unlink(file1_s1d2)); 3316 ASSERT_EQ(0, unlink(file2_s1d2)); 3317 3318 ASSERT_EQ(0, unlink(file1_s1d3)); 3319 ASSERT_EQ(0, unlink(file2_s1d3)); 3320 3321 enforce_fs(_metadata, rules[0].access, rules); 3322 3323 ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3324 ASSERT_EQ(EACCES, errno); 3325 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3326 ASSERT_EQ(EACCES, errno); 3327 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3328 ASSERT_EQ(EACCES, errno); 3329 3330 ASSERT_EQ(0, symlink("none", file1_s1d2)); 3331 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3332 ASSERT_EQ(0, unlink(file2_s1d2)); 3333 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3334 3335 ASSERT_EQ(0, symlink("none", file1_s1d3)); 3336 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3337 ASSERT_EQ(0, unlink(file2_s1d3)); 3338 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3339 } 3340 3341 TEST_F_FORK(layout1, make_dir) 3342 { 3343 const struct rule rules[] = { 3344 { 3345 .path = dir_s1d2, 3346 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3347 }, 3348 {}, 3349 }; 3350 3351 ASSERT_EQ(0, unlink(file1_s1d1)); 3352 ASSERT_EQ(0, unlink(file1_s1d2)); 3353 ASSERT_EQ(0, unlink(file1_s1d3)); 3354 3355 enforce_fs(_metadata, rules[0].access, rules); 3356 3357 /* Uses file_* as directory names. */ 3358 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3359 ASSERT_EQ(EACCES, errno); 3360 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3361 ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3362 } 3363 3364 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3365 const int open_flags) 3366 { 3367 static const char path_template[] = "/proc/self/fd/%d"; 3368 char procfd_path[sizeof(path_template) + 10]; 3369 const int procfd_path_size = 3370 snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3371 3372 ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3373 return open(procfd_path, open_flags); 3374 } 3375 3376 TEST_F_FORK(layout1, proc_unlinked_file) 3377 { 3378 const struct rule rules[] = { 3379 { 3380 .path = file1_s1d2, 3381 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3382 }, 3383 {}, 3384 }; 3385 int reg_fd, proc_fd; 3386 3387 enforce_fs(_metadata, 3388 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3389 rules); 3390 3391 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3392 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3393 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3394 ASSERT_LE(0, reg_fd); 3395 ASSERT_EQ(0, unlink(file1_s1d2)); 3396 3397 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3398 ASSERT_LE(0, proc_fd); 3399 ASSERT_EQ(0, close(proc_fd)); 3400 3401 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3402 ASSERT_EQ(-1, proc_fd) 3403 { 3404 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3405 strerror(errno)); 3406 } 3407 ASSERT_EQ(EACCES, errno); 3408 3409 ASSERT_EQ(0, close(reg_fd)); 3410 } 3411 3412 TEST_F_FORK(layout1, proc_pipe) 3413 { 3414 int proc_fd; 3415 int pipe_fds[2]; 3416 char buf = '\0'; 3417 const struct rule rules[] = { 3418 { 3419 .path = dir_s1d2, 3420 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3421 LANDLOCK_ACCESS_FS_WRITE_FILE, 3422 }, 3423 {}, 3424 }; 3425 3426 /* Limits read and write access to files tied to the filesystem. */ 3427 enforce_fs(_metadata, rules[0].access, rules); 3428 3429 /* Checks enforcement for normal files. */ 3430 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3431 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3432 3433 /* Checks access to pipes through FD. */ 3434 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3435 ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3436 { 3437 TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3438 } 3439 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3440 ASSERT_EQ('.', buf); 3441 3442 /* Checks write access to pipe through /proc/self/fd . */ 3443 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3444 ASSERT_LE(0, proc_fd); 3445 ASSERT_EQ(1, write(proc_fd, ".", 1)) 3446 { 3447 TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3448 pipe_fds[1], strerror(errno)); 3449 } 3450 ASSERT_EQ(0, close(proc_fd)); 3451 3452 /* Checks read access to pipe through /proc/self/fd . */ 3453 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3454 ASSERT_LE(0, proc_fd); 3455 buf = '\0'; 3456 ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3457 { 3458 TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3459 pipe_fds[1], strerror(errno)); 3460 } 3461 ASSERT_EQ(0, close(proc_fd)); 3462 3463 ASSERT_EQ(0, close(pipe_fds[0])); 3464 ASSERT_EQ(0, close(pipe_fds[1])); 3465 } 3466 3467 /* Invokes truncate(2) and returns its errno or 0. */ 3468 static int test_truncate(const char *const path) 3469 { 3470 if (truncate(path, 10) < 0) 3471 return errno; 3472 return 0; 3473 } 3474 3475 /* 3476 * Invokes creat(2) and returns its errno or 0. 3477 * Closes the opened file descriptor on success. 3478 */ 3479 static int test_creat(const char *const path) 3480 { 3481 int fd = creat(path, 0600); 3482 3483 if (fd < 0) 3484 return errno; 3485 3486 /* 3487 * Mixing error codes from close(2) and creat(2) should not lead to any 3488 * (access type) confusion for this test. 3489 */ 3490 if (close(fd) < 0) 3491 return errno; 3492 return 0; 3493 } 3494 3495 /* 3496 * Exercises file truncation when it's not restricted, 3497 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 3498 */ 3499 TEST_F_FORK(layout1, truncate_unhandled) 3500 { 3501 const char *const file_r = file1_s1d1; 3502 const char *const file_w = file2_s1d1; 3503 const char *const file_none = file1_s1d2; 3504 const struct rule rules[] = { 3505 { 3506 .path = file_r, 3507 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3508 }, 3509 { 3510 .path = file_w, 3511 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3512 }, 3513 /* Implicitly: No rights for file_none. */ 3514 {}, 3515 }; 3516 3517 /* Enables Landlock. */ 3518 enforce_fs(_metadata, 3519 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3520 rules); 3521 3522 /* 3523 * Checks read right: truncate and open with O_TRUNC work, unless the 3524 * file is attempted to be opened for writing. 3525 */ 3526 EXPECT_EQ(0, test_truncate(file_r)); 3527 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 3528 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 3529 EXPECT_EQ(EACCES, test_creat(file_r)); 3530 3531 /* 3532 * Checks write right: truncate and open with O_TRUNC work, unless the 3533 * file is attempted to be opened for reading. 3534 */ 3535 EXPECT_EQ(0, test_truncate(file_w)); 3536 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 3537 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 3538 EXPECT_EQ(0, test_creat(file_w)); 3539 3540 /* 3541 * Checks "no rights" case: truncate works but all open attempts fail, 3542 * including creat. 3543 */ 3544 EXPECT_EQ(0, test_truncate(file_none)); 3545 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3546 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3547 EXPECT_EQ(EACCES, test_creat(file_none)); 3548 } 3549 3550 TEST_F_FORK(layout1, truncate) 3551 { 3552 const char *const file_rwt = file1_s1d1; 3553 const char *const file_rw = file2_s1d1; 3554 const char *const file_rt = file1_s1d2; 3555 const char *const file_t = file2_s1d2; 3556 const char *const file_none = file1_s1d3; 3557 const char *const dir_t = dir_s2d1; 3558 const char *const file_in_dir_t = file1_s2d1; 3559 const char *const dir_w = dir_s3d1; 3560 const char *const file_in_dir_w = file1_s3d1; 3561 const struct rule rules[] = { 3562 { 3563 .path = file_rwt, 3564 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3565 LANDLOCK_ACCESS_FS_WRITE_FILE | 3566 LANDLOCK_ACCESS_FS_TRUNCATE, 3567 }, 3568 { 3569 .path = file_rw, 3570 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3571 LANDLOCK_ACCESS_FS_WRITE_FILE, 3572 }, 3573 { 3574 .path = file_rt, 3575 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3576 LANDLOCK_ACCESS_FS_TRUNCATE, 3577 }, 3578 { 3579 .path = file_t, 3580 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3581 }, 3582 /* Implicitly: No access rights for file_none. */ 3583 { 3584 .path = dir_t, 3585 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3586 }, 3587 { 3588 .path = dir_w, 3589 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3590 }, 3591 {}, 3592 }; 3593 3594 /* Enables Landlock. */ 3595 enforce_fs(_metadata, 3596 LANDLOCK_ACCESS_FS_READ_FILE | 3597 LANDLOCK_ACCESS_FS_WRITE_FILE | 3598 LANDLOCK_ACCESS_FS_TRUNCATE, 3599 rules); 3600 3601 /* Checks read, write and truncate rights: truncation works. */ 3602 EXPECT_EQ(0, test_truncate(file_rwt)); 3603 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 3604 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 3605 3606 /* Checks read and write rights: no truncate variant works. */ 3607 EXPECT_EQ(EACCES, test_truncate(file_rw)); 3608 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 3609 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 3610 3611 /* 3612 * Checks read and truncate rights: truncation works. 3613 * 3614 * Note: Files can get truncated using open() even with O_RDONLY. 3615 */ 3616 EXPECT_EQ(0, test_truncate(file_rt)); 3617 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 3618 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 3619 3620 /* Checks truncate right: truncate works, but can't open file. */ 3621 EXPECT_EQ(0, test_truncate(file_t)); 3622 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 3623 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 3624 3625 /* Checks "no rights" case: No form of truncation works. */ 3626 EXPECT_EQ(EACCES, test_truncate(file_none)); 3627 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3628 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3629 3630 /* 3631 * Checks truncate right on directory: truncate works on contained 3632 * files. 3633 */ 3634 EXPECT_EQ(0, test_truncate(file_in_dir_t)); 3635 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 3636 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 3637 3638 /* 3639 * Checks creat in dir_w: This requires the truncate right when 3640 * overwriting an existing file, but does not require it when the file 3641 * is new. 3642 */ 3643 EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 3644 3645 ASSERT_EQ(0, unlink(file_in_dir_w)); 3646 EXPECT_EQ(0, test_creat(file_in_dir_w)); 3647 } 3648 3649 /* Invokes ftruncate(2) and returns its errno or 0. */ 3650 static int test_ftruncate(int fd) 3651 { 3652 if (ftruncate(fd, 10) < 0) 3653 return errno; 3654 return 0; 3655 } 3656 3657 TEST_F_FORK(layout1, ftruncate) 3658 { 3659 /* 3660 * This test opens a new file descriptor at different stages of 3661 * Landlock restriction: 3662 * 3663 * without restriction: ftruncate works 3664 * something else but truncate restricted: ftruncate works 3665 * truncate restricted and permitted: ftruncate works 3666 * truncate restricted and not permitted: ftruncate fails 3667 * 3668 * Whether this works or not is expected to depend on the time when the 3669 * FD was opened, not to depend on the time when ftruncate() was 3670 * called. 3671 */ 3672 const char *const path = file1_s1d1; 3673 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 3674 LANDLOCK_ACCESS_FS_WRITE_FILE; 3675 const struct rule layer1[] = { 3676 { 3677 .path = path, 3678 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3679 }, 3680 {}, 3681 }; 3682 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 3683 const struct rule layer2[] = { 3684 { 3685 .path = path, 3686 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3687 }, 3688 {}, 3689 }; 3690 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 3691 LANDLOCK_ACCESS_FS_WRITE_FILE; 3692 const struct rule layer3[] = { 3693 { 3694 .path = path, 3695 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3696 }, 3697 {}, 3698 }; 3699 int fd_layer0, fd_layer1, fd_layer2, fd_layer3; 3700 3701 fd_layer0 = open(path, O_WRONLY); 3702 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3703 3704 enforce_fs(_metadata, handled1, layer1); 3705 3706 fd_layer1 = open(path, O_WRONLY); 3707 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3708 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3709 3710 enforce_fs(_metadata, handled2, layer2); 3711 3712 fd_layer2 = open(path, O_WRONLY); 3713 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3714 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3715 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3716 3717 enforce_fs(_metadata, handled3, layer3); 3718 3719 fd_layer3 = open(path, O_WRONLY); 3720 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3721 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3722 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3723 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 3724 3725 ASSERT_EQ(0, close(fd_layer0)); 3726 ASSERT_EQ(0, close(fd_layer1)); 3727 ASSERT_EQ(0, close(fd_layer2)); 3728 ASSERT_EQ(0, close(fd_layer3)); 3729 } 3730 3731 /* clang-format off */ 3732 FIXTURE(ftruncate) {}; 3733 /* clang-format on */ 3734 3735 FIXTURE_SETUP(ftruncate) 3736 { 3737 prepare_layout(_metadata); 3738 create_file(_metadata, file1_s1d1); 3739 } 3740 3741 FIXTURE_TEARDOWN_PARENT(ftruncate) 3742 { 3743 EXPECT_EQ(0, remove_path(file1_s1d1)); 3744 cleanup_layout(_metadata); 3745 } 3746 3747 FIXTURE_VARIANT(ftruncate) 3748 { 3749 const __u64 handled; 3750 const __u64 allowed; 3751 const int expected_open_result; 3752 const int expected_ftruncate_result; 3753 }; 3754 3755 /* clang-format off */ 3756 FIXTURE_VARIANT_ADD(ftruncate, w_w) { 3757 /* clang-format on */ 3758 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 3759 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE, 3760 .expected_open_result = 0, 3761 .expected_ftruncate_result = 0, 3762 }; 3763 3764 /* clang-format off */ 3765 FIXTURE_VARIANT_ADD(ftruncate, t_t) { 3766 /* clang-format on */ 3767 .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 3768 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE, 3769 .expected_open_result = 0, 3770 .expected_ftruncate_result = 0, 3771 }; 3772 3773 /* clang-format off */ 3774 FIXTURE_VARIANT_ADD(ftruncate, wt_w) { 3775 /* clang-format on */ 3776 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3777 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE, 3778 .expected_open_result = 0, 3779 .expected_ftruncate_result = EACCES, 3780 }; 3781 3782 /* clang-format off */ 3783 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 3784 /* clang-format on */ 3785 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3786 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3787 .expected_open_result = 0, 3788 .expected_ftruncate_result = 0, 3789 }; 3790 3791 /* clang-format off */ 3792 FIXTURE_VARIANT_ADD(ftruncate, wt_t) { 3793 /* clang-format on */ 3794 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3795 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE, 3796 .expected_open_result = EACCES, 3797 }; 3798 3799 TEST_F_FORK(ftruncate, open_and_ftruncate) 3800 { 3801 const char *const path = file1_s1d1; 3802 const struct rule rules[] = { 3803 { 3804 .path = path, 3805 .access = variant->allowed, 3806 }, 3807 {}, 3808 }; 3809 int fd; 3810 3811 /* Enables Landlock. */ 3812 enforce_fs(_metadata, variant->handled, rules); 3813 3814 fd = open(path, O_WRONLY); 3815 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3816 if (fd >= 0) { 3817 EXPECT_EQ(variant->expected_ftruncate_result, 3818 test_ftruncate(fd)); 3819 ASSERT_EQ(0, close(fd)); 3820 } 3821 } 3822 3823 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 3824 { 3825 int child, fd, status; 3826 int socket_fds[2]; 3827 3828 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 3829 socket_fds)); 3830 3831 child = fork(); 3832 ASSERT_LE(0, child); 3833 if (child == 0) { 3834 /* 3835 * Enables Landlock in the child process, open a file descriptor 3836 * where truncation is forbidden and send it to the 3837 * non-landlocked parent process. 3838 */ 3839 const char *const path = file1_s1d1; 3840 const struct rule rules[] = { 3841 { 3842 .path = path, 3843 .access = variant->allowed, 3844 }, 3845 {}, 3846 }; 3847 int fd; 3848 3849 enforce_fs(_metadata, variant->handled, rules); 3850 3851 fd = open(path, O_WRONLY); 3852 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3853 3854 if (fd >= 0) { 3855 ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 3856 ASSERT_EQ(0, close(fd)); 3857 } 3858 3859 ASSERT_EQ(0, close(socket_fds[0])); 3860 3861 _exit(_metadata->exit_code); 3862 return; 3863 } 3864 3865 if (variant->expected_open_result == 0) { 3866 fd = recv_fd(socket_fds[1]); 3867 ASSERT_LE(0, fd); 3868 3869 EXPECT_EQ(variant->expected_ftruncate_result, 3870 test_ftruncate(fd)); 3871 ASSERT_EQ(0, close(fd)); 3872 } 3873 3874 ASSERT_EQ(child, waitpid(child, &status, 0)); 3875 ASSERT_EQ(1, WIFEXITED(status)); 3876 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 3877 3878 ASSERT_EQ(0, close(socket_fds[0])); 3879 ASSERT_EQ(0, close(socket_fds[1])); 3880 } 3881 3882 /* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */ 3883 static int test_fs_ioc_getflags_ioctl(int fd) 3884 { 3885 uint32_t flags; 3886 3887 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) 3888 return errno; 3889 return 0; 3890 } 3891 3892 TEST(memfd_ftruncate_and_ioctl) 3893 { 3894 int fd, i; 3895 3896 /* 3897 * We exercise the same test both with and without Landlock enabled, to 3898 * ensure that it behaves the same in both cases. 3899 */ 3900 for (i = 0; i < 2; i++) { 3901 /* Creates a new memfd. */ 3902 fd = memfd_create("name", MFD_CLOEXEC); 3903 ASSERT_LE(0, fd); 3904 3905 /* 3906 * Checks that operations associated with the opened file 3907 * (ftruncate, ioctl) are permitted on file descriptors that are 3908 * created in ways other than open(2). 3909 */ 3910 EXPECT_EQ(0, test_ftruncate(fd)); 3911 EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd)); 3912 3913 ASSERT_EQ(0, close(fd)); 3914 3915 /* Enables Landlock. */ 3916 enforce_fs(_metadata, ACCESS_ALL, NULL); 3917 } 3918 } 3919 3920 static int test_fionread_ioctl(int fd) 3921 { 3922 size_t sz = 0; 3923 3924 if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES) 3925 return errno; 3926 return 0; 3927 } 3928 3929 TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl) 3930 { 3931 int fd; 3932 3933 /* 3934 * Checks that for files opened with O_PATH, both ioctl(2) and 3935 * ftruncate(2) yield EBADF, as it is documented in open(2) for the 3936 * O_PATH flag. 3937 */ 3938 fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 3939 ASSERT_LE(0, fd); 3940 3941 EXPECT_EQ(EBADF, test_ftruncate(fd)); 3942 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 3943 3944 ASSERT_EQ(0, close(fd)); 3945 3946 /* Enables Landlock. */ 3947 enforce_fs(_metadata, ACCESS_ALL, NULL); 3948 3949 /* 3950 * Checks that after enabling Landlock, 3951 * - the file can still be opened with O_PATH 3952 * - both ioctl and truncate still yield EBADF (not EACCES). 3953 */ 3954 fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 3955 ASSERT_LE(0, fd); 3956 3957 EXPECT_EQ(EBADF, test_ftruncate(fd)); 3958 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 3959 3960 ASSERT_EQ(0, close(fd)); 3961 } 3962 3963 /* 3964 * ioctl_error - generically call the given ioctl with a pointer to a 3965 * sufficiently large zeroed-out memory region. 3966 * 3967 * Returns the IOCTLs error, or 0. 3968 */ 3969 static int ioctl_error(struct __test_metadata *const _metadata, int fd, 3970 unsigned int cmd) 3971 { 3972 char buf[128]; /* sufficiently large */ 3973 int res, stdinbak_fd; 3974 3975 /* 3976 * Depending on the IOCTL command, parts of the zeroed-out buffer might 3977 * be interpreted as file descriptor numbers. We do not want to 3978 * accidentally operate on file descriptor 0 (stdin), so we temporarily 3979 * move stdin to a different FD and close FD 0 for the IOCTL call. 3980 */ 3981 stdinbak_fd = dup(0); 3982 ASSERT_LT(0, stdinbak_fd); 3983 ASSERT_EQ(0, close(0)); 3984 3985 /* Invokes the IOCTL with a zeroed-out buffer. */ 3986 bzero(&buf, sizeof(buf)); 3987 res = ioctl(fd, cmd, &buf); 3988 3989 /* Restores the old FD 0 and closes the backup FD. */ 3990 ASSERT_EQ(0, dup2(stdinbak_fd, 0)); 3991 ASSERT_EQ(0, close(stdinbak_fd)); 3992 3993 if (res < 0) 3994 return errno; 3995 3996 return 0; 3997 } 3998 3999 /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */ 4000 struct space_resv { 4001 __s16 l_type; 4002 __s16 l_whence; 4003 __s64 l_start; 4004 __s64 l_len; /* len == 0 means until end of file */ 4005 __s32 l_sysid; 4006 __u32 l_pid; 4007 __s32 l_pad[4]; /* reserved area */ 4008 }; 4009 4010 #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) 4011 #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv) 4012 #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) 4013 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv) 4014 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv) 4015 4016 /* 4017 * Tests a series of blanket-permitted and denied IOCTLs. 4018 */ 4019 TEST_F_FORK(layout1, blanket_permitted_ioctls) 4020 { 4021 int fd; 4022 4023 /* Enables Landlock. */ 4024 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4025 4026 fd = open("/dev/null", O_RDWR | O_CLOEXEC); 4027 ASSERT_LE(0, fd); 4028 4029 /* 4030 * Checks permitted commands. 4031 * These ones may return errors, but should not be blocked by Landlock. 4032 */ 4033 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX)); 4034 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX)); 4035 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO)); 4036 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC)); 4037 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE)); 4038 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE)); 4039 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW)); 4040 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP)); 4041 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ)); 4042 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE)); 4043 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE)); 4044 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE)); 4045 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID)); 4046 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH)); 4047 4048 /* 4049 * Checks blocked commands. 4050 * A call to a blocked IOCTL command always returns EACCES. 4051 */ 4052 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 4053 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS)); 4054 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS)); 4055 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR)); 4056 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR)); 4057 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP)); 4058 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP)); 4059 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64)); 4060 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP)); 4061 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64)); 4062 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE)); 4063 4064 /* Default case is also blocked. */ 4065 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee)); 4066 4067 ASSERT_EQ(0, close(fd)); 4068 } 4069 4070 /* 4071 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right, 4072 * because they are not character or block devices. 4073 */ 4074 TEST_F_FORK(layout1, named_pipe_ioctl) 4075 { 4076 pid_t child_pid; 4077 int fd; 4078 const char *const path = file1_s1d1; 4079 4080 ASSERT_EQ(0, unlink(path)); 4081 ASSERT_EQ(0, mkfifo(path, 0600)); 4082 4083 /* Enables Landlock. */ 4084 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4085 4086 /* The child process opens the pipe for writing. */ 4087 child_pid = fork(); 4088 ASSERT_NE(-1, child_pid); 4089 if (child_pid == 0) { 4090 fd = open(path, O_WRONLY); 4091 close(fd); 4092 exit(0); 4093 } 4094 4095 fd = open(path, O_RDONLY); 4096 ASSERT_LE(0, fd); 4097 4098 /* FIONREAD is implemented by pipefifo_fops. */ 4099 EXPECT_EQ(0, test_fionread_ioctl(fd)); 4100 4101 ASSERT_EQ(0, close(fd)); 4102 ASSERT_EQ(0, unlink(path)); 4103 4104 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0)); 4105 } 4106 4107 /* 4108 * set_up_named_unix_server - Create a pathname unix socket 4109 * 4110 * If the socket type is not SOCK_DGRAM, also invoke listen(2). 4111 * 4112 * Return: The listening FD - it is the caller responsibility to close it. 4113 */ 4114 static int set_up_named_unix_server(struct __test_metadata *const _metadata, 4115 int type, const char *const path) 4116 { 4117 int fd; 4118 struct sockaddr_un addr = { 4119 .sun_family = AF_UNIX, 4120 }; 4121 4122 fd = socket(AF_UNIX, type, 0); 4123 ASSERT_LE(0, fd); 4124 4125 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4126 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4127 4128 ASSERT_EQ(0, bind(fd, (struct sockaddr *)&addr, sizeof(addr))); 4129 4130 if (type != SOCK_DGRAM) 4131 ASSERT_EQ(0, listen(fd, 10 /* qlen */)); 4132 return fd; 4133 } 4134 4135 /* 4136 * test_connect_named_unix - connect to the given named UNIX socket 4137 * 4138 * Return: The errno from connect(), or 0 4139 */ 4140 static int test_connect_named_unix(struct __test_metadata *const _metadata, 4141 int fd, const char *const path) 4142 { 4143 struct sockaddr_un addr = { 4144 .sun_family = AF_UNIX, 4145 }; 4146 4147 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4148 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4149 4150 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) 4151 return errno; 4152 return 0; 4153 } 4154 4155 /* For named UNIX domain sockets, no IOCTL restrictions apply. */ 4156 TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) 4157 { 4158 const char *const path = file1_s1d1; 4159 int srv_fd, cli_fd; 4160 4161 /* Sets up a server */ 4162 ASSERT_EQ(0, unlink(path)); 4163 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path); 4164 4165 /* Enables Landlock. */ 4166 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4167 4168 /* Sets up a client connection to it */ 4169 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4170 ASSERT_LE(0, cli_fd); 4171 4172 ASSERT_EQ(0, test_connect_named_unix(_metadata, cli_fd, path)); 4173 4174 /* FIONREAD and other IOCTLs should not be forbidden. */ 4175 EXPECT_EQ(0, test_fionread_ioctl(cli_fd)); 4176 4177 EXPECT_EQ(0, close(cli_fd)); 4178 EXPECT_EQ(0, close(srv_fd)); 4179 } 4180 4181 /* clang-format off */ 4182 FIXTURE(ioctl) {}; 4183 4184 FIXTURE_SETUP(ioctl) {}; 4185 4186 FIXTURE_TEARDOWN(ioctl) {}; 4187 /* clang-format on */ 4188 4189 FIXTURE_VARIANT(ioctl) 4190 { 4191 const __u64 handled; 4192 const __u64 allowed; 4193 const mode_t open_mode; 4194 /* 4195 * FIONREAD is used as a characteristic device-specific IOCTL command. 4196 * It is implemented in fs/ioctl.c for regular files, 4197 * but we do not blanket-permit it for devices. 4198 */ 4199 const int expected_fionread_result; 4200 }; 4201 4202 /* clang-format off */ 4203 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) { 4204 /* clang-format on */ 4205 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4206 .allowed = 0, 4207 .open_mode = O_RDWR, 4208 .expected_fionread_result = EACCES, 4209 }; 4210 4211 /* clang-format off */ 4212 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) { 4213 /* clang-format on */ 4214 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4215 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4216 .open_mode = O_RDWR, 4217 .expected_fionread_result = 0, 4218 }; 4219 4220 /* clang-format off */ 4221 FIXTURE_VARIANT_ADD(ioctl, unhandled) { 4222 /* clang-format on */ 4223 .handled = LANDLOCK_ACCESS_FS_EXECUTE, 4224 .allowed = LANDLOCK_ACCESS_FS_EXECUTE, 4225 .open_mode = O_RDWR, 4226 .expected_fionread_result = 0, 4227 }; 4228 4229 TEST_F_FORK(ioctl, handle_dir_access_file) 4230 { 4231 const int flag = 0; 4232 const struct rule rules[] = { 4233 { 4234 .path = "/dev", 4235 .access = variant->allowed, 4236 }, 4237 {}, 4238 }; 4239 int fd; 4240 4241 /* Enables Landlock. */ 4242 enforce_fs(_metadata, variant->handled, rules); 4243 4244 fd = open("/dev/zero", variant->open_mode); 4245 ASSERT_LE(0, fd); 4246 4247 /* Checks that IOCTL commands return the expected errors. */ 4248 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd)); 4249 4250 /* Checks that unrestrictable commands are unrestricted. */ 4251 EXPECT_EQ(0, ioctl(fd, FIOCLEX)); 4252 EXPECT_EQ(0, ioctl(fd, FIONCLEX)); 4253 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag)); 4254 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag)); 4255 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag)); 4256 4257 ASSERT_EQ(0, close(fd)); 4258 } 4259 4260 TEST_F_FORK(ioctl, handle_dir_access_dir) 4261 { 4262 const int flag = 0; 4263 const struct rule rules[] = { 4264 { 4265 .path = "/dev", 4266 .access = variant->allowed, 4267 }, 4268 {}, 4269 }; 4270 int dir_fd; 4271 4272 /* Enables Landlock. */ 4273 enforce_fs(_metadata, variant->handled, rules); 4274 4275 /* 4276 * Ignore variant->open_mode for this test, as we intend to open a 4277 * directory. If the directory can not be opened, the variant is 4278 * infeasible to test with an opened directory. 4279 */ 4280 dir_fd = open("/dev", O_RDONLY); 4281 if (dir_fd < 0) 4282 return; 4283 4284 /* 4285 * Checks that IOCTL commands return the expected errors. 4286 * We do not use the expected values from the fixture here. 4287 * 4288 * When using IOCTL on a directory, no Landlock restrictions apply. 4289 */ 4290 EXPECT_EQ(0, test_fionread_ioctl(dir_fd)); 4291 4292 /* Checks that unrestrictable commands are unrestricted. */ 4293 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX)); 4294 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX)); 4295 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag)); 4296 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag)); 4297 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag)); 4298 4299 ASSERT_EQ(0, close(dir_fd)); 4300 } 4301 4302 TEST_F_FORK(ioctl, handle_file_access_file) 4303 { 4304 const int flag = 0; 4305 const struct rule rules[] = { 4306 { 4307 .path = "/dev/zero", 4308 .access = variant->allowed, 4309 }, 4310 {}, 4311 }; 4312 int fd; 4313 4314 /* Enables Landlock. */ 4315 enforce_fs(_metadata, variant->handled, rules); 4316 4317 fd = open("/dev/zero", variant->open_mode); 4318 ASSERT_LE(0, fd) 4319 { 4320 TH_LOG("Failed to open /dev/zero: %s", strerror(errno)); 4321 } 4322 4323 /* Checks that IOCTL commands return the expected errors. */ 4324 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd)); 4325 4326 /* Checks that unrestrictable commands are unrestricted. */ 4327 EXPECT_EQ(0, ioctl(fd, FIOCLEX)); 4328 EXPECT_EQ(0, ioctl(fd, FIONCLEX)); 4329 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag)); 4330 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag)); 4331 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag)); 4332 4333 ASSERT_EQ(0, close(fd)); 4334 } 4335 4336 /* 4337 * test_sendto_named_unix - sendto to the given named UNIX socket 4338 * 4339 * sendto() is equivalent to sendmsg() in this respect. 4340 * 4341 * Return: The errno from sendto(), or 0 4342 */ 4343 static int test_sendto_named_unix(struct __test_metadata *const _metadata, 4344 int fd, const char *const path) 4345 { 4346 static const char buf[] = "dummy"; 4347 struct sockaddr_un addr = { 4348 .sun_family = AF_UNIX, 4349 }; 4350 4351 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4352 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4353 4354 if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, 4355 sizeof(addr)) == -1) 4356 return errno; 4357 return 0; 4358 } 4359 4360 /* clang-format off */ 4361 FIXTURE(scoped_domains) {}; 4362 /* clang-format on */ 4363 4364 #include "scoped_base_variants.h" 4365 4366 FIXTURE_SETUP(scoped_domains) 4367 { 4368 drop_caps(_metadata); 4369 }; 4370 4371 FIXTURE_TEARDOWN(scoped_domains) 4372 { 4373 } 4374 4375 /* 4376 * Flags for test_connect_to_parent and test_connect_to_child: 4377 * 4378 * USE_SENDTO: Use sendto() instead of connect() (for SOCK_DGRAM only) 4379 * ENFORCE_ALL: Enforce a Landlock domain even when the variant says 4380 * we shouldn't. We enforce a domain where the path is allow-listed, 4381 * and expect the behavior to be the same as if none was used. 4382 */ 4383 #define USE_SENDTO (1 << 0) 4384 #define ENFORCE_ALL (1 << 1) 4385 4386 static void test_connect_to_parent(struct __test_metadata *const _metadata, 4387 const FIXTURE_VARIANT(scoped_domains) * 4388 variant, 4389 int sock_type, int flags) 4390 { 4391 const char *const path = "sock"; 4392 const struct rule rules[] = { 4393 { 4394 .path = ".", 4395 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4396 }, 4397 {}, 4398 }; 4399 int cli_fd, srv_fd, res, status; 4400 pid_t child_pid; 4401 int readiness_pipe[2]; 4402 char buf[1]; 4403 4404 if (variant->domain_both) 4405 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4406 else if (flags & ENFORCE_ALL) 4407 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4408 4409 unlink(path); 4410 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC)); 4411 4412 child_pid = fork(); 4413 ASSERT_LE(0, child_pid); 4414 4415 if (child_pid == 0) { 4416 if (variant->domain_child) 4417 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4418 NULL); 4419 else if (flags & ENFORCE_ALL) 4420 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4421 rules); 4422 4423 /* Wait for server to be available. */ 4424 EXPECT_EQ(0, close(readiness_pipe[1])); 4425 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1)); 4426 EXPECT_EQ(0, close(readiness_pipe[0])); 4427 4428 /* Talk to server. */ 4429 cli_fd = socket(AF_UNIX, sock_type, 0); 4430 ASSERT_LE(0, cli_fd); 4431 4432 if (flags & USE_SENDTO) 4433 res = test_sendto_named_unix(_metadata, cli_fd, path); 4434 else 4435 res = test_connect_named_unix(_metadata, cli_fd, path); 4436 4437 EXPECT_EQ(variant->domain_child ? EACCES : 0, res); 4438 4439 /* Clean up. */ 4440 EXPECT_EQ(0, close(cli_fd)); 4441 4442 _exit(_metadata->exit_code); 4443 return; 4444 } 4445 4446 if (variant->domain_parent) 4447 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4448 else if (flags & ENFORCE_ALL) 4449 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4450 4451 srv_fd = set_up_named_unix_server(_metadata, sock_type, path); 4452 4453 /* Tell the child that it can connect. */ 4454 EXPECT_EQ(0, close(readiness_pipe[0])); 4455 EXPECT_EQ(sizeof(buf), write(readiness_pipe[1], buf, sizeof(buf))); 4456 EXPECT_EQ(0, close(readiness_pipe[1])); 4457 4458 /* Wait for child. */ 4459 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4460 EXPECT_EQ(1, WIFEXITED(status)); 4461 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 4462 4463 /* Clean up. */ 4464 EXPECT_EQ(0, close(srv_fd)); 4465 EXPECT_EQ(0, unlink(path)); 4466 } 4467 4468 static void test_connect_to_child(struct __test_metadata *const _metadata, 4469 const FIXTURE_VARIANT(scoped_domains) * 4470 variant, 4471 int sock_type, int flags) 4472 { 4473 const char *const path = "sock"; 4474 const struct rule rules[] = { 4475 { 4476 .path = ".", 4477 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4478 }, 4479 {}, 4480 }; 4481 int readiness_pipe[2]; 4482 int shutdown_pipe[2]; 4483 int cli_fd, srv_fd, res, status; 4484 pid_t child_pid; 4485 char buf[1]; 4486 4487 if (variant->domain_both) 4488 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4489 else if (flags & ENFORCE_ALL) 4490 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4491 4492 unlink(path); 4493 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC)); 4494 ASSERT_EQ(0, pipe2(shutdown_pipe, O_CLOEXEC)); 4495 4496 child_pid = fork(); 4497 ASSERT_LE(0, child_pid); 4498 4499 if (child_pid == 0) { 4500 if (variant->domain_child) 4501 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4502 NULL); 4503 else if (flags & ENFORCE_ALL) 4504 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4505 rules); 4506 4507 srv_fd = set_up_named_unix_server(_metadata, sock_type, path); 4508 4509 /* Tell the parent that it can connect. */ 4510 EXPECT_EQ(0, close(readiness_pipe[0])); 4511 EXPECT_EQ(sizeof(buf), 4512 write(readiness_pipe[1], buf, sizeof(buf))); 4513 EXPECT_EQ(0, close(readiness_pipe[1])); 4514 4515 /* Wait until it is time to shut down. */ 4516 EXPECT_EQ(0, close(shutdown_pipe[1])); 4517 EXPECT_EQ(1, read(shutdown_pipe[0], &buf, 1)); 4518 EXPECT_EQ(0, close(shutdown_pipe[0])); 4519 4520 /* Cleanup */ 4521 EXPECT_EQ(0, close(srv_fd)); 4522 EXPECT_EQ(0, unlink(path)); 4523 4524 _exit(_metadata->exit_code); 4525 return; 4526 } 4527 4528 if (variant->domain_parent) 4529 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4530 else if (flags & ENFORCE_ALL) 4531 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4532 4533 /* Wait for server to be available. */ 4534 EXPECT_EQ(0, close(readiness_pipe[1])); 4535 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1)); 4536 EXPECT_EQ(0, close(readiness_pipe[0])); 4537 4538 /* Talk to server. */ 4539 cli_fd = socket(AF_UNIX, sock_type, 0); 4540 ASSERT_LE(0, cli_fd); 4541 4542 if (flags & USE_SENDTO) 4543 res = test_sendto_named_unix(_metadata, cli_fd, path); 4544 else 4545 res = test_connect_named_unix(_metadata, cli_fd, path); 4546 4547 EXPECT_EQ(variant->domain_parent ? EACCES : 0, res); 4548 4549 /* Clean up. */ 4550 EXPECT_EQ(0, close(cli_fd)); 4551 4552 /* Tell the server to shut down. */ 4553 EXPECT_EQ(0, close(shutdown_pipe[0])); 4554 EXPECT_EQ(sizeof(buf), write(shutdown_pipe[1], buf, sizeof(buf))); 4555 EXPECT_EQ(0, close(shutdown_pipe[1])); 4556 4557 /* Wait for child. */ 4558 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4559 EXPECT_EQ(1, WIFEXITED(status)); 4560 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 4561 } 4562 4563 TEST_F(scoped_domains, unix_stream_connect_to_parent) 4564 { 4565 test_connect_to_parent(_metadata, variant, SOCK_STREAM, 0); 4566 } 4567 4568 TEST_F(scoped_domains, unix_dgram_connect_to_parent) 4569 { 4570 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, 0); 4571 } 4572 4573 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent) 4574 { 4575 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, USE_SENDTO); 4576 } 4577 4578 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent) 4579 { 4580 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, 0); 4581 } 4582 4583 TEST_F(scoped_domains, unix_stream_connect_to_parent_full) 4584 { 4585 test_connect_to_parent(_metadata, variant, SOCK_STREAM, ENFORCE_ALL); 4586 } 4587 4588 TEST_F(scoped_domains, unix_dgram_connect_to_parent_full) 4589 { 4590 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL); 4591 } 4592 4593 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent_full) 4594 { 4595 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, 4596 USE_SENDTO | ENFORCE_ALL); 4597 } 4598 4599 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent_full) 4600 { 4601 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL); 4602 } 4603 4604 TEST_F(scoped_domains, unix_stream_connect_to_child) 4605 { 4606 test_connect_to_child(_metadata, variant, SOCK_STREAM, 0); 4607 } 4608 4609 TEST_F(scoped_domains, unix_dgram_connect_to_child) 4610 { 4611 test_connect_to_child(_metadata, variant, SOCK_DGRAM, 0); 4612 } 4613 4614 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child) 4615 { 4616 test_connect_to_child(_metadata, variant, SOCK_DGRAM, USE_SENDTO); 4617 } 4618 4619 TEST_F(scoped_domains, unix_seqpacket_connect_to_child) 4620 { 4621 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, 0); 4622 } 4623 4624 TEST_F(scoped_domains, unix_stream_connect_to_child_full) 4625 { 4626 test_connect_to_child(_metadata, variant, SOCK_STREAM, ENFORCE_ALL); 4627 } 4628 4629 TEST_F(scoped_domains, unix_dgram_connect_to_child_full) 4630 { 4631 test_connect_to_child(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL); 4632 } 4633 4634 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child_full) 4635 { 4636 test_connect_to_child(_metadata, variant, SOCK_DGRAM, 4637 USE_SENDTO | ENFORCE_ALL); 4638 } 4639 4640 TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full) 4641 { 4642 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL); 4643 } 4644 4645 #undef USE_SENDTO 4646 #undef ENFORCE_ALL 4647 4648 static void read_core_pattern(struct __test_metadata *const _metadata, 4649 char *buf, size_t buf_size) 4650 { 4651 int fd; 4652 ssize_t ret; 4653 4654 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY | O_CLOEXEC); 4655 ASSERT_LE(0, fd); 4656 4657 ret = read(fd, buf, buf_size - 1); 4658 ASSERT_LE(0, ret); 4659 EXPECT_EQ(0, close(fd)); 4660 4661 buf[ret] = '\0'; 4662 } 4663 4664 static void set_core_pattern(struct __test_metadata *const _metadata, 4665 const char *pattern) 4666 { 4667 int fd; 4668 size_t len = strlen(pattern); 4669 4670 /* 4671 * Writing to /proc/sys/kernel/core_pattern requires EUID 0 because 4672 * sysctl_perm() checks that, ignoring capabilities like 4673 * CAP_SYS_ADMIN or CAP_DAC_OVERRIDE. 4674 * 4675 * Switching EUID clears the dumpable flag, which must be restored 4676 * afterwards to allow coredumps. 4677 */ 4678 set_cap(_metadata, CAP_SETUID); 4679 ASSERT_EQ(0, seteuid(0)); 4680 clear_cap(_metadata, CAP_SETUID); 4681 4682 fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC); 4683 ASSERT_LE(0, fd) 4684 { 4685 TH_LOG("Failed to open core_pattern for writing: %s", 4686 strerror(errno)); 4687 } 4688 4689 ASSERT_EQ(len, write(fd, pattern, len)); 4690 EXPECT_EQ(0, close(fd)); 4691 4692 set_cap(_metadata, CAP_SETUID); 4693 ASSERT_EQ(0, seteuid(getuid())); 4694 clear_cap(_metadata, CAP_SETUID); 4695 4696 /* Restore dumpable flag cleared by seteuid(). */ 4697 ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)); 4698 } 4699 4700 FIXTURE(coredump) 4701 { 4702 char original_core_pattern[256]; 4703 }; 4704 4705 FIXTURE_SETUP(coredump) 4706 { 4707 disable_caps(_metadata); 4708 read_core_pattern(_metadata, self->original_core_pattern, 4709 sizeof(self->original_core_pattern)); 4710 } 4711 4712 FIXTURE_TEARDOWN_PARENT(coredump) 4713 { 4714 set_core_pattern(_metadata, self->original_core_pattern); 4715 } 4716 4717 /* 4718 * Test that even when a process is restricted with 4719 * LANDLOCK_ACCESS_FS_RESOLVE_UNIX, the kernel can still initiate a connection 4720 * to the coredump socket on the processes' behalf. 4721 */ 4722 TEST_F_FORK(coredump, socket_not_restricted) 4723 { 4724 static const char core_pattern[] = "@/tmp/landlock_coredump_test.sock"; 4725 const char *const sock_path = core_pattern + 1; 4726 int srv_fd, conn_fd, status; 4727 pid_t child_pid; 4728 struct ucred cred; 4729 socklen_t cred_len = sizeof(cred); 4730 char buf[4096]; 4731 4732 /* Set up the coredump server socket. */ 4733 unlink(sock_path); 4734 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, sock_path); 4735 4736 /* Point coredumps at our socket. */ 4737 set_core_pattern(_metadata, core_pattern); 4738 4739 /* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */ 4740 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4741 4742 /* Fork a child that crashes. */ 4743 child_pid = fork(); 4744 ASSERT_LE(0, child_pid); 4745 if (child_pid == 0) { 4746 struct rlimit rl = { 4747 .rlim_cur = RLIM_INFINITY, 4748 .rlim_max = RLIM_INFINITY, 4749 }; 4750 4751 ASSERT_EQ(0, setrlimit(RLIMIT_CORE, &rl)); 4752 4753 /* Crash on purpose. */ 4754 kill(getpid(), SIGSEGV); 4755 _exit(1); 4756 } 4757 4758 /* 4759 * Accept the coredump connection. If Landlock incorrectly denies the 4760 * kernel's coredump connect, accept() will block forever, so the test 4761 * would time out. 4762 */ 4763 conn_fd = accept(srv_fd, NULL, NULL); 4764 ASSERT_LE(0, conn_fd); 4765 4766 /* Check that the connection came from the crashing child. */ 4767 ASSERT_EQ(0, getsockopt(conn_fd, SOL_SOCKET, SO_PEERCRED, &cred, 4768 &cred_len)); 4769 EXPECT_EQ(child_pid, cred.pid); 4770 4771 /* Drain the coredump data so the kernel can finish. */ 4772 while (read(conn_fd, buf, sizeof(buf)) > 0) 4773 ; 4774 4775 EXPECT_EQ(0, close(conn_fd)); 4776 4777 /* Wait for the child and verify it coredumped. */ 4778 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4779 ASSERT_TRUE(WIFSIGNALED(status)); 4780 ASSERT_TRUE(WCOREDUMP(status)); 4781 4782 EXPECT_EQ(0, close(srv_fd)); 4783 EXPECT_EQ(0, unlink(sock_path)); 4784 } 4785 4786 /* clang-format off */ 4787 FIXTURE(layout1_bind) {}; 4788 /* clang-format on */ 4789 4790 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 4791 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 4792 4793 /* Move targets for disconnected path tests. */ 4794 static const char dir_s4d1[] = TMP_DIR "/s4d1"; 4795 static const char file1_s4d1[] = TMP_DIR "/s4d1/f1"; 4796 static const char file2_s4d1[] = TMP_DIR "/s4d1/f2"; 4797 static const char dir_s4d2[] = TMP_DIR "/s4d1/s4d2"; 4798 static const char file1_s4d2[] = TMP_DIR "/s4d1/s4d2/f1"; 4799 static const char file1_name[] = "f1"; 4800 static const char file2_name[] = "f2"; 4801 4802 FIXTURE_SETUP(layout1_bind) 4803 { 4804 prepare_layout(_metadata); 4805 4806 create_layout1(_metadata); 4807 4808 set_cap(_metadata, CAP_SYS_ADMIN); 4809 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 4810 clear_cap(_metadata, CAP_SYS_ADMIN); 4811 } 4812 4813 FIXTURE_TEARDOWN_PARENT(layout1_bind) 4814 { 4815 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 4816 4817 remove_path(file1_s4d1); 4818 remove_path(file2_s4d1); 4819 4820 remove_layout1(_metadata); 4821 4822 cleanup_layout(_metadata); 4823 } 4824 4825 /* 4826 * layout1_bind hierarchy: 4827 * 4828 * tmp 4829 * ├── s1d1 4830 * │ ├── f1 4831 * │ ├── f2 4832 * │ └── s1d2 4833 * │ ├── f1 4834 * │ ├── f2 4835 * │ └── s1d3 [disconnected by path_disconnected] 4836 * │ ├── f1 4837 * │ └── f2 4838 * ├── s2d1 4839 * │ ├── f1 4840 * │ └── s2d2 [bind mount from s1d2] 4841 * │ ├── f1 4842 * │ ├── f2 4843 * │ └── s1d3 4844 * │ ├── f1 4845 * │ └── f2 4846 * ├── s3d1 4847 * │ └── s3d2 4848 * │ └── s3d3 4849 * └── s4d1 [renamed from s1d3 by path_disconnected] 4850 * ├── f1 4851 * ├── f2 4852 * └── s4d2 4853 * └── f1 4854 */ 4855 4856 TEST_F_FORK(layout1_bind, no_restriction) 4857 { 4858 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 4859 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4860 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 4861 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4862 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 4863 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4864 4865 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 4866 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 4867 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 4868 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 4869 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 4870 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 4871 4872 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 4873 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4874 4875 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 4876 } 4877 4878 TEST_F_FORK(layout1_bind, same_content_same_file) 4879 { 4880 /* 4881 * Sets access right on parent directories of both source and 4882 * destination mount points. 4883 */ 4884 const struct rule layer1_parent[] = { 4885 { 4886 .path = dir_s1d1, 4887 .access = ACCESS_RO, 4888 }, 4889 { 4890 .path = dir_s2d1, 4891 .access = ACCESS_RW, 4892 }, 4893 {}, 4894 }; 4895 /* 4896 * Sets access rights on the same bind-mounted directories. The result 4897 * should be ACCESS_RW for both directories, but not both hierarchies 4898 * because of the first layer. 4899 */ 4900 const struct rule layer2_mount_point[] = { 4901 { 4902 .path = dir_s1d2, 4903 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4904 }, 4905 { 4906 .path = dir_s2d2, 4907 .access = ACCESS_RW, 4908 }, 4909 {}, 4910 }; 4911 /* Only allow read-access to the s1d3 hierarchies. */ 4912 const struct rule layer3_source[] = { 4913 { 4914 .path = dir_s1d3, 4915 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4916 }, 4917 {}, 4918 }; 4919 /* Removes all access rights. */ 4920 const struct rule layer4_destination[] = { 4921 { 4922 .path = bind_file1_s1d3, 4923 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 4924 }, 4925 {}, 4926 }; 4927 4928 /* Sets rules for the parent directories. */ 4929 enforce_fs(_metadata, ACCESS_RW, layer1_parent); 4930 4931 /* Checks source hierarchy. */ 4932 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4933 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4934 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4935 4936 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4937 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4938 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4939 4940 /* Checks destination hierarchy. */ 4941 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 4942 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4943 4944 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4945 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4946 4947 /* Sets rules for the mount points. */ 4948 enforce_fs(_metadata, ACCESS_RW, layer2_mount_point); 4949 4950 /* Checks source hierarchy. */ 4951 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 4952 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4953 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4954 4955 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4956 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4957 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4958 4959 /* Checks destination hierarchy. */ 4960 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 4961 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 4962 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4963 4964 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4965 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4966 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4967 4968 /* Sets a (shared) rule only on the source. */ 4969 enforce_fs(_metadata, ACCESS_RW, layer3_source); 4970 4971 /* Checks source hierarchy. */ 4972 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 4973 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4974 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4975 4976 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4977 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4978 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 4979 4980 /* Checks destination hierarchy. */ 4981 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 4982 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 4983 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4984 4985 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4986 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 4987 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4988 4989 /* Sets a (shared) rule only on the destination. */ 4990 enforce_fs(_metadata, ACCESS_RW, layer4_destination); 4991 4992 /* Checks source hierarchy. */ 4993 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 4994 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4995 4996 /* Checks destination hierarchy. */ 4997 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 4998 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 4999 } 5000 5001 TEST_F_FORK(layout1_bind, reparent_cross_mount) 5002 { 5003 const struct rule layer1[] = { 5004 { 5005 /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 5006 .path = dir_s2d1, 5007 .access = LANDLOCK_ACCESS_FS_REFER, 5008 }, 5009 { 5010 .path = bind_dir_s1d3, 5011 .access = LANDLOCK_ACCESS_FS_EXECUTE, 5012 }, 5013 {}, 5014 }; 5015 5016 enforce_fs(_metadata, 5017 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, 5018 layer1); 5019 5020 /* Checks basic denied move. */ 5021 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 5022 ASSERT_EQ(EXDEV, errno); 5023 5024 /* Checks real cross-mount move (Landlock is not involved). */ 5025 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 5026 ASSERT_EQ(EXDEV, errno); 5027 5028 /* Checks move that will give more accesses. */ 5029 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 5030 ASSERT_EQ(EXDEV, errno); 5031 5032 /* Checks legitimate downgrade move. */ 5033 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 5034 } 5035 5036 /* 5037 * Make sure access to file through a disconnected path works as expected. 5038 * This test moves s1d3 to s4d1. 5039 */ 5040 TEST_F_FORK(layout1_bind, path_disconnected) 5041 { 5042 const struct rule layer1_allow_all[] = { 5043 { 5044 .path = TMP_DIR, 5045 .access = ACCESS_ALL, 5046 }, 5047 {}, 5048 }; 5049 const struct rule layer2_allow_just_f1[] = { 5050 { 5051 .path = file1_s1d3, 5052 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5053 }, 5054 {}, 5055 }; 5056 const struct rule layer3_only_s1d2[] = { 5057 { 5058 .path = dir_s1d2, 5059 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5060 }, 5061 {}, 5062 }; 5063 5064 /* Landlock should not deny access just because it is disconnected. */ 5065 int ruleset_fd_l1 = 5066 create_ruleset(_metadata, ACCESS_ALL, layer1_allow_all); 5067 5068 /* Creates the new ruleset now before we move the dir containing the file. */ 5069 int ruleset_fd_l2 = 5070 create_ruleset(_metadata, ACCESS_RW, layer2_allow_just_f1); 5071 int ruleset_fd_l3 = 5072 create_ruleset(_metadata, ACCESS_RW, layer3_only_s1d2); 5073 int bind_s1d3_fd; 5074 5075 enforce_ruleset(_metadata, ruleset_fd_l1); 5076 EXPECT_EQ(0, close(ruleset_fd_l1)); 5077 5078 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5079 ASSERT_LE(0, bind_s1d3_fd); 5080 5081 /* Tests access is possible before we move. */ 5082 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5083 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5084 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 5085 5086 /* Makes it disconnected. */ 5087 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5088 { 5089 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5090 strerror(errno)); 5091 } 5092 5093 /* Tests that access is still possible. */ 5094 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5095 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5096 5097 /* 5098 * Tests that ".." is not possible (not because of Landlock, but just 5099 * because it's disconnected). 5100 */ 5101 EXPECT_EQ(ENOENT, 5102 test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 5103 5104 /* This should still work with a narrower rule. */ 5105 enforce_ruleset(_metadata, ruleset_fd_l2); 5106 EXPECT_EQ(0, close(ruleset_fd_l2)); 5107 5108 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)); 5109 /* 5110 * Accessing a file through a disconnected file descriptor can still be 5111 * allowed by a rule tied to this file, even if it is no longer visible in 5112 * its mount point. 5113 */ 5114 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5115 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5116 5117 enforce_ruleset(_metadata, ruleset_fd_l3); 5118 EXPECT_EQ(0, close(ruleset_fd_l3)); 5119 5120 EXPECT_EQ(EACCES, test_open(file1_s4d1, O_RDONLY)); 5121 /* 5122 * Accessing a file through a disconnected file descriptor can still be 5123 * allowed by a rule tied to the original mount point, even if it is no 5124 * longer visible in its mount point. 5125 */ 5126 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5127 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5128 } 5129 5130 /* 5131 * Test that renameat with disconnected paths works under Landlock. This test 5132 * moves s1d3 to s4d2, so that we can have a rule allowing refers on the move 5133 * target's immediate parent. 5134 */ 5135 TEST_F_FORK(layout1_bind, path_disconnected_rename) 5136 { 5137 const struct rule layer1[] = { 5138 { 5139 .path = dir_s1d2, 5140 .access = LANDLOCK_ACCESS_FS_REFER | 5141 LANDLOCK_ACCESS_FS_MAKE_DIR | 5142 LANDLOCK_ACCESS_FS_REMOVE_DIR | 5143 LANDLOCK_ACCESS_FS_MAKE_REG | 5144 LANDLOCK_ACCESS_FS_REMOVE_FILE | 5145 LANDLOCK_ACCESS_FS_READ_FILE, 5146 }, 5147 { 5148 .path = dir_s4d1, 5149 .access = LANDLOCK_ACCESS_FS_REFER | 5150 LANDLOCK_ACCESS_FS_MAKE_DIR | 5151 LANDLOCK_ACCESS_FS_REMOVE_DIR | 5152 LANDLOCK_ACCESS_FS_MAKE_REG | 5153 LANDLOCK_ACCESS_FS_REMOVE_FILE | 5154 LANDLOCK_ACCESS_FS_READ_FILE, 5155 }, 5156 {} 5157 }; 5158 5159 /* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */ 5160 const struct rule layer2_only_s1d2[] = { 5161 { 5162 .path = dir_s1d2, 5163 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5164 }, 5165 {}, 5166 }; 5167 int ruleset_fd_l1, ruleset_fd_l2; 5168 pid_t child_pid; 5169 int bind_s1d3_fd, status; 5170 5171 ASSERT_EQ(0, mkdir(dir_s4d1, 0755)) 5172 { 5173 TH_LOG("Failed to create %s: %s", dir_s4d1, strerror(errno)); 5174 } 5175 ruleset_fd_l1 = create_ruleset(_metadata, ACCESS_ALL, layer1); 5176 ruleset_fd_l2 = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 5177 layer2_only_s1d2); 5178 5179 enforce_ruleset(_metadata, ruleset_fd_l1); 5180 EXPECT_EQ(0, close(ruleset_fd_l1)); 5181 5182 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5183 ASSERT_LE(0, bind_s1d3_fd); 5184 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5185 5186 /* Tests ENOENT priority over EACCES for disconnected directory. */ 5187 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5188 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5189 { 5190 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5191 strerror(errno)); 5192 } 5193 EXPECT_EQ(ENOENT, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5194 5195 /* 5196 * The file is no longer under s1d2 but we should still be able to access it 5197 * with layer 2 because its mount point is evaluated as the first valid 5198 * directory because it was initially a parent. Do a fork to test this so 5199 * we don't prevent ourselves from renaming it back later. 5200 */ 5201 child_pid = fork(); 5202 ASSERT_LE(0, child_pid); 5203 if (child_pid == 0) { 5204 enforce_ruleset(_metadata, ruleset_fd_l2); 5205 EXPECT_EQ(0, close(ruleset_fd_l2)); 5206 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5207 EXPECT_EQ(EACCES, test_open(file1_s4d2, O_RDONLY)); 5208 5209 /* 5210 * Tests that access widening checks indeed prevents us from renaming it 5211 * back. 5212 */ 5213 EXPECT_EQ(-1, rename(dir_s4d2, dir_s1d3)); 5214 EXPECT_EQ(EXDEV, errno); 5215 5216 /* 5217 * Including through the now disconnected fd (but it should return 5218 * EXDEV). 5219 */ 5220 EXPECT_EQ(-1, renameat(bind_s1d3_fd, file1_name, AT_FDCWD, 5221 file1_s2d2)); 5222 EXPECT_EQ(EXDEV, errno); 5223 _exit(_metadata->exit_code); 5224 return; 5225 } 5226 5227 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5228 EXPECT_EQ(1, WIFEXITED(status)); 5229 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5230 5231 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5232 { 5233 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d1, dir_s1d3, 5234 strerror(errno)); 5235 } 5236 5237 /* Now checks that we can access it under l2. */ 5238 child_pid = fork(); 5239 ASSERT_LE(0, child_pid); 5240 if (child_pid == 0) { 5241 enforce_ruleset(_metadata, ruleset_fd_l2); 5242 EXPECT_EQ(0, close(ruleset_fd_l2)); 5243 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5244 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5245 _exit(_metadata->exit_code); 5246 return; 5247 } 5248 5249 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5250 EXPECT_EQ(1, WIFEXITED(status)); 5251 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5252 5253 /* 5254 * Also test that we can rename via a disconnected path. We move the 5255 * dir back to the disconnected place first, then we rename file1 to 5256 * file2 through our dir fd. 5257 */ 5258 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5259 { 5260 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5261 strerror(errno)); 5262 } 5263 ASSERT_EQ(0, 5264 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)) 5265 { 5266 TH_LOG("Failed to rename %s to %s within disconnected %s: %s", 5267 file1_name, file2_name, bind_dir_s1d3, strerror(errno)); 5268 } 5269 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5270 ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2)) 5271 { 5272 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5273 file2_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5274 } 5275 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 5276 EXPECT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5277 5278 /* Move it back using the disconnected path as the target. */ 5279 ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name)) 5280 { 5281 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5282 file1_s1d2, file1_name, bind_dir_s1d3, strerror(errno)); 5283 } 5284 5285 /* Now make it connected again. */ 5286 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5287 { 5288 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d2, dir_s1d3, 5289 strerror(errno)); 5290 } 5291 5292 /* Checks again that we can access it under l2. */ 5293 enforce_ruleset(_metadata, ruleset_fd_l2); 5294 EXPECT_EQ(0, close(ruleset_fd_l2)); 5295 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5296 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5297 } 5298 5299 /* 5300 * Test that linkat(2) with disconnected paths works under Landlock. This 5301 * test moves s1d3 to s4d1. 5302 */ 5303 TEST_F_FORK(layout1_bind, path_disconnected_link) 5304 { 5305 /* Ruleset to be applied after renaming s1d3 to s4d1. */ 5306 const struct rule layer1[] = { 5307 { 5308 .path = dir_s4d1, 5309 .access = LANDLOCK_ACCESS_FS_REFER | 5310 LANDLOCK_ACCESS_FS_READ_FILE | 5311 LANDLOCK_ACCESS_FS_MAKE_REG | 5312 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5313 }, 5314 { 5315 .path = dir_s2d2, 5316 .access = LANDLOCK_ACCESS_FS_REFER | 5317 LANDLOCK_ACCESS_FS_READ_FILE | 5318 LANDLOCK_ACCESS_FS_MAKE_REG | 5319 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5320 }, 5321 {} 5322 }; 5323 int bind_s1d3_fd; 5324 5325 /* Removes unneeded files created by layout1, otherwise it will EEXIST. */ 5326 ASSERT_EQ(0, unlink(file1_s1d2)); 5327 ASSERT_EQ(0, unlink(file2_s1d3)); 5328 5329 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5330 ASSERT_LE(0, bind_s1d3_fd); 5331 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5332 5333 /* Disconnects bind_s1d3_fd. */ 5334 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5335 { 5336 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5337 strerror(errno)); 5338 } 5339 5340 /* Need this later to test different parent link. */ 5341 ASSERT_EQ(0, mkdir(dir_s4d2, 0755)) 5342 { 5343 TH_LOG("Failed to create %s: %s", dir_s4d2, strerror(errno)); 5344 } 5345 5346 enforce_fs(_metadata, ACCESS_ALL, layer1); 5347 5348 /* From disconnected to connected. */ 5349 ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0)) 5350 { 5351 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5352 file1_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5353 } 5354 5355 /* Tests that we can access via the new link... */ 5356 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)) 5357 { 5358 TH_LOG("Failed to open newly linked %s: %s", file1_s2d2, 5359 strerror(errno)); 5360 } 5361 5362 /* ...as well as the old one. */ 5363 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5364 { 5365 TH_LOG("Failed to open original %s: %s", file1_s4d1, 5366 strerror(errno)); 5367 } 5368 5369 /* From connected to disconnected. */ 5370 ASSERT_EQ(0, unlink(file1_s4d1)); 5371 ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0)) 5372 { 5373 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5374 file1_s2d2, file2_name, bind_dir_s1d3, strerror(errno)); 5375 } 5376 EXPECT_EQ(0, test_open(file2_s4d1, O_RDONLY)); 5377 ASSERT_EQ(0, unlink(file1_s2d2)); 5378 5379 /* From disconnected to disconnected (same parent). */ 5380 ASSERT_EQ(0, 5381 linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0)) 5382 { 5383 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5384 file2_name, file1_name, bind_dir_s1d3, strerror(errno)); 5385 } 5386 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5387 { 5388 TH_LOG("Failed to open newly linked %s: %s", file1_s4d1, 5389 strerror(errno)); 5390 } 5391 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)) 5392 { 5393 TH_LOG("Failed to open %s through newly created link under disconnected path: %s", 5394 file1_name, strerror(errno)); 5395 } 5396 ASSERT_EQ(0, unlink(file2_s4d1)); 5397 5398 /* From disconnected to disconnected (different parent). */ 5399 ASSERT_EQ(0, 5400 linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0)) 5401 { 5402 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5403 file1_name, "s4d2/f1", bind_dir_s1d3, strerror(errno)); 5404 } 5405 EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY)) 5406 { 5407 TH_LOG("Failed to open %s after link: %s", file1_s4d2, 5408 strerror(errno)); 5409 } 5410 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY)) 5411 { 5412 TH_LOG("Failed to open %s through disconnected path after link: %s", 5413 "s4d2/f1", strerror(errno)); 5414 } 5415 } 5416 5417 /* 5418 * layout4_disconnected_leafs with bind mount and renames: 5419 * 5420 * tmp 5421 * ├── s1d1 5422 * │ └── s1d2 [source of the bind mount] 5423 * │ ├── s1d31 5424 * │ │ └── s1d41 [now renamed beneath s3d1] 5425 * │ │ ├── f1 5426 * │ │ └── f2 5427 * │ └── s1d32 5428 * │ └── s1d42 [now renamed beneath s4d1] 5429 * │ ├── f3 5430 * │ └── f4 5431 * ├── s2d1 5432 * │ └── s2d2 [bind mount of s1d2] 5433 * │ ├── s1d31 5434 * │ │ └── s1d41 [opened FD, now renamed beneath s3d1] 5435 * │ │ ├── f1 5436 * │ │ └── f2 5437 * │ └── s1d32 5438 * │ └── s1d42 [opened FD, now renamed beneath s4d1] 5439 * │ ├── f3 5440 * │ └── f4 5441 * ├── s3d1 5442 * │ └── s1d41 [renamed here] 5443 * │ ├── f1 5444 * │ └── f2 5445 * └── s4d1 5446 * └── s1d42 [renamed here] 5447 * ├── f3 5448 * └── f4 5449 */ 5450 /* clang-format off */ 5451 FIXTURE(layout4_disconnected_leafs) { 5452 int s2d2_fd; 5453 }; 5454 /* clang-format on */ 5455 5456 FIXTURE_SETUP(layout4_disconnected_leafs) 5457 { 5458 prepare_layout(_metadata); 5459 5460 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1"); 5461 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2"); 5462 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3"); 5463 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4"); 5464 create_directory(_metadata, TMP_DIR "/s2d1/s2d2"); 5465 create_directory(_metadata, TMP_DIR "/s3d1"); 5466 create_directory(_metadata, TMP_DIR "/s4d1"); 5467 5468 self->s2d2_fd = 5469 open(TMP_DIR "/s2d1/s2d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 5470 ASSERT_LE(0, self->s2d2_fd); 5471 5472 set_cap(_metadata, CAP_SYS_ADMIN); 5473 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2", NULL, 5474 MS_BIND, NULL)); 5475 clear_cap(_metadata, CAP_SYS_ADMIN); 5476 } 5477 5478 FIXTURE_TEARDOWN_PARENT(layout4_disconnected_leafs) 5479 { 5480 /* umount(TMP_DIR "/s2d1") is handled by namespace lifetime. */ 5481 5482 /* Removes files after renames. */ 5483 remove_path(TMP_DIR "/s3d1/s1d41/f1"); 5484 remove_path(TMP_DIR "/s3d1/s1d41/f2"); 5485 remove_path(TMP_DIR "/s4d1/s1d42/f1"); 5486 remove_path(TMP_DIR "/s4d1/s1d42/f3"); 5487 remove_path(TMP_DIR "/s4d1/s1d42/f4"); 5488 remove_path(TMP_DIR "/s4d1/s1d42/f5"); 5489 5490 cleanup_layout(_metadata); 5491 } 5492 5493 FIXTURE_VARIANT(layout4_disconnected_leafs) 5494 { 5495 /* 5496 * Parent of the bind mount source. It should always be ignored when 5497 * testing against files under the s1d41 or s1d42 disconnected directories. 5498 */ 5499 const __u64 allowed_s1d1; 5500 /* 5501 * Source of bind mount (to s2d2). It should always be enforced when 5502 * testing against files under the s1d41 or s1d42 disconnected directories. 5503 */ 5504 const __u64 allowed_s1d2; 5505 /* 5506 * Original parent of s1d41. It should always be ignored when testing 5507 * against files under the s1d41 disconnected directory. 5508 */ 5509 const __u64 allowed_s1d31; 5510 /* 5511 * Original parent of s1d42. It should always be ignored when testing 5512 * against files under the s1d42 disconnected directory. 5513 */ 5514 const __u64 allowed_s1d32; 5515 /* 5516 * Opened and disconnected source directory. It should always be enforced 5517 * when testing against files under the s1d41 disconnected directory. 5518 */ 5519 const __u64 allowed_s1d41; 5520 /* 5521 * Opened and disconnected source directory. It should always be enforced 5522 * when testing against files under the s1d42 disconnected directory. 5523 */ 5524 const __u64 allowed_s1d42; 5525 /* 5526 * File in the s1d41 disconnected directory. It should always be enforced 5527 * when testing against itself under the s1d41 disconnected directory. 5528 */ 5529 const __u64 allowed_f1; 5530 /* 5531 * File in the s1d41 disconnected directory. It should always be enforced 5532 * when testing against itself under the s1d41 disconnected directory. 5533 */ 5534 const __u64 allowed_f2; 5535 /* 5536 * File in the s1d42 disconnected directory. It should always be enforced 5537 * when testing against itself under the s1d42 disconnected directory. 5538 */ 5539 const __u64 allowed_f3; 5540 /* 5541 * Parent of the bind mount destination. It should always be enforced when 5542 * testing against files under the s1d41 or s1d42 disconnected directories. 5543 */ 5544 const __u64 allowed_s2d1; 5545 /* 5546 * Directory covered by the bind mount. It should always be ignored when 5547 * testing against files under the s1d41 or s1d42 disconnected directories. 5548 */ 5549 const __u64 allowed_s2d2; 5550 /* 5551 * New parent of the renamed s1d41. It should always be ignored when 5552 * testing against files under the s1d41 disconnected directory. 5553 */ 5554 const __u64 allowed_s3d1; 5555 /* 5556 * New parent of the renamed s1d42. It should always be ignored when 5557 * testing against files under the s1d42 disconnected directory. 5558 */ 5559 const __u64 allowed_s4d1; 5560 5561 /* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */ 5562 const int expected_read_result; 5563 /* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */ 5564 const int expected_rename_result; 5565 /* 5566 * Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3, 5567 * RENAME_EXCHANGE). 5568 */ 5569 const int expected_exchange_result; 5570 /* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */ 5571 const int expected_same_dir_rename_result; 5572 }; 5573 5574 /* clang-format off */ 5575 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) { 5576 /* clang-format on */ 5577 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 5578 LANDLOCK_ACCESS_FS_READ_FILE | 5579 LANDLOCK_ACCESS_FS_EXECUTE | 5580 LANDLOCK_ACCESS_FS_MAKE_REG, 5581 .expected_read_result = EACCES, 5582 .expected_same_dir_rename_result = EACCES, 5583 .expected_rename_result = EACCES, 5584 .expected_exchange_result = EACCES, 5585 }; 5586 5587 /* clang-format off */ 5588 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) { 5589 /* clang-format on */ 5590 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5591 .expected_read_result = 0, 5592 .expected_same_dir_rename_result = EACCES, 5593 .expected_rename_result = EACCES, 5594 .expected_exchange_result = EACCES, 5595 }; 5596 5597 /* clang-format off */ 5598 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) { 5599 /* clang-format on */ 5600 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 5601 LANDLOCK_ACCESS_FS_MAKE_REG, 5602 .expected_read_result = 0, 5603 .expected_same_dir_rename_result = 0, 5604 .expected_rename_result = EXDEV, 5605 .expected_exchange_result = EXDEV, 5606 }; 5607 5608 /* clang-format off */ 5609 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) { 5610 /* clang-format on */ 5611 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5612 .expected_read_result = EACCES, 5613 .expected_same_dir_rename_result = 0, 5614 .expected_rename_result = 0, 5615 .expected_exchange_result = 0, 5616 }; 5617 5618 /* clang-format off */ 5619 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) { 5620 /* clang-format on */ 5621 .allowed_s1d31 = LANDLOCK_ACCESS_FS_REFER | 5622 LANDLOCK_ACCESS_FS_READ_FILE | 5623 LANDLOCK_ACCESS_FS_EXECUTE | 5624 LANDLOCK_ACCESS_FS_MAKE_REG, 5625 .allowed_s1d32 = LANDLOCK_ACCESS_FS_REFER | 5626 LANDLOCK_ACCESS_FS_READ_FILE | 5627 LANDLOCK_ACCESS_FS_EXECUTE | 5628 LANDLOCK_ACCESS_FS_MAKE_REG, 5629 .expected_read_result = EACCES, 5630 .expected_same_dir_rename_result = EACCES, 5631 .expected_rename_result = EACCES, 5632 .expected_exchange_result = EACCES, 5633 }; 5634 5635 /* clang-format off */ 5636 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) { 5637 /* clang-format on */ 5638 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5639 LANDLOCK_ACCESS_FS_READ_FILE, 5640 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5641 LANDLOCK_ACCESS_FS_READ_FILE, 5642 .expected_read_result = 0, 5643 .expected_same_dir_rename_result = EACCES, 5644 .expected_rename_result = EACCES, 5645 .expected_exchange_result = EACCES, 5646 }; 5647 5648 /* clang-format off */ 5649 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) { 5650 /* clang-format on */ 5651 .allowed_s1d41 = LANDLOCK_ACCESS_FS_READ_FILE | 5652 LANDLOCK_ACCESS_FS_MAKE_REG, 5653 .allowed_s1d42 = LANDLOCK_ACCESS_FS_READ_FILE | 5654 LANDLOCK_ACCESS_FS_MAKE_REG, 5655 .expected_read_result = 0, 5656 .expected_same_dir_rename_result = 0, 5657 .expected_rename_result = EXDEV, 5658 .expected_exchange_result = EXDEV, 5659 }; 5660 5661 /* clang-format off */ 5662 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) { 5663 /* clang-format on */ 5664 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5665 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5666 .expected_read_result = EACCES, 5667 .expected_same_dir_rename_result = 0, 5668 .expected_rename_result = 0, 5669 .expected_exchange_result = 0, 5670 }; 5671 5672 /* The destination directory has more access right. */ 5673 /* clang-format off */ 5674 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) { 5675 /* clang-format on */ 5676 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5677 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5678 LANDLOCK_ACCESS_FS_MAKE_REG | 5679 LANDLOCK_ACCESS_FS_EXECUTE, 5680 .expected_read_result = EACCES, 5681 .expected_same_dir_rename_result = 0, 5682 /* Access denied. */ 5683 .expected_rename_result = EXDEV, 5684 .expected_exchange_result = EXDEV, 5685 }; 5686 5687 /* The destination directory has less access right. */ 5688 /* clang-format off */ 5689 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) { 5690 /* clang-format on */ 5691 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5692 LANDLOCK_ACCESS_FS_MAKE_REG | 5693 LANDLOCK_ACCESS_FS_EXECUTE, 5694 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5695 .expected_read_result = EACCES, 5696 .expected_same_dir_rename_result = 0, 5697 /* Access allowed. */ 5698 .expected_rename_result = 0, 5699 .expected_exchange_result = EXDEV, 5700 }; 5701 5702 /* clang-format off */ 5703 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) { 5704 /* clang-format on */ 5705 .allowed_s2d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5706 LANDLOCK_ACCESS_FS_MAKE_REG, 5707 .expected_read_result = 0, 5708 .expected_same_dir_rename_result = 0, 5709 .expected_rename_result = EXDEV, 5710 .expected_exchange_result = EXDEV, 5711 }; 5712 5713 /* clang-format off */ 5714 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) { 5715 /* clang-format on */ 5716 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5717 .expected_read_result = 0, 5718 .expected_same_dir_rename_result = EACCES, 5719 .expected_rename_result = EACCES, 5720 .expected_exchange_result = EACCES, 5721 }; 5722 5723 /* clang-format off */ 5724 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) { 5725 /* clang-format on */ 5726 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 5727 LANDLOCK_ACCESS_FS_READ_FILE | 5728 LANDLOCK_ACCESS_FS_MAKE_REG, 5729 .expected_read_result = 0, 5730 .expected_same_dir_rename_result = 0, 5731 .expected_rename_result = 0, 5732 .expected_exchange_result = 0, 5733 }; 5734 5735 /* clang-format off */ 5736 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) { 5737 /* clang-format on */ 5738 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | 5739 LANDLOCK_ACCESS_FS_READ_FILE | 5740 LANDLOCK_ACCESS_FS_EXECUTE | 5741 LANDLOCK_ACCESS_FS_MAKE_REG, 5742 .expected_read_result = EACCES, 5743 .expected_same_dir_rename_result = EACCES, 5744 .expected_rename_result = EACCES, 5745 .expected_exchange_result = EACCES, 5746 }; 5747 5748 /* Tests collect_domain_accesses(). */ 5749 /* clang-format off */ 5750 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) { 5751 /* clang-format on */ 5752 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5753 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5754 .expected_read_result = 0, 5755 .expected_same_dir_rename_result = EACCES, 5756 .expected_rename_result = EACCES, 5757 .expected_exchange_result = EACCES, 5758 }; 5759 5760 /* clang-format off */ 5761 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) { 5762 /* clang-format on */ 5763 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5764 LANDLOCK_ACCESS_FS_MAKE_REG, 5765 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5766 LANDLOCK_ACCESS_FS_MAKE_REG, 5767 .expected_read_result = 0, 5768 .expected_same_dir_rename_result = 0, 5769 .expected_rename_result = EXDEV, 5770 .expected_exchange_result = EXDEV, 5771 }; 5772 5773 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, 5774 s3d1_s4d1_disconnected_rename_even){ 5775 /* clang-format on */ 5776 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5777 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5778 .expected_read_result = EACCES, 5779 .expected_same_dir_rename_result = 0, 5780 .expected_rename_result = 0, 5781 .expected_exchange_result = 0, 5782 }; 5783 5784 /* The destination directory has more access right. */ 5785 /* clang-format off */ 5786 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) { 5787 /* clang-format on */ 5788 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5789 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5790 LANDLOCK_ACCESS_FS_EXECUTE, 5791 .expected_read_result = EACCES, 5792 .expected_same_dir_rename_result = 0, 5793 /* Access denied. */ 5794 .expected_rename_result = EXDEV, 5795 .expected_exchange_result = EXDEV, 5796 }; 5797 5798 /* The destination directory has less access right. */ 5799 /* clang-format off */ 5800 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) { 5801 /* clang-format on */ 5802 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5803 LANDLOCK_ACCESS_FS_EXECUTE, 5804 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5805 .expected_read_result = EACCES, 5806 .expected_same_dir_rename_result = 0, 5807 /* Access allowed. */ 5808 .expected_rename_result = 0, 5809 .expected_exchange_result = EXDEV, 5810 }; 5811 5812 /* clang-format off */ 5813 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) { 5814 /* clang-format on */ 5815 .allowed_f1 = LANDLOCK_ACCESS_FS_READ_FILE, 5816 .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE, 5817 .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE, 5818 .expected_read_result = 0, 5819 .expected_same_dir_rename_result = EACCES, 5820 .expected_rename_result = EACCES, 5821 .expected_exchange_result = EACCES, 5822 }; 5823 5824 TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange) 5825 { 5826 const __u64 handled_access = 5827 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 5828 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 5829 const struct rule rules[] = { 5830 { 5831 .path = TMP_DIR "/s1d1", 5832 .access = variant->allowed_s1d1, 5833 }, 5834 { 5835 .path = TMP_DIR "/s1d1/s1d2", 5836 .access = variant->allowed_s1d2, 5837 }, 5838 { 5839 .path = TMP_DIR "/s1d1/s1d2/s1d31", 5840 .access = variant->allowed_s1d31, 5841 }, 5842 { 5843 .path = TMP_DIR "/s1d1/s1d2/s1d32", 5844 .access = variant->allowed_s1d32, 5845 }, 5846 { 5847 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5848 .access = variant->allowed_s1d41, 5849 }, 5850 { 5851 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5852 .access = variant->allowed_s1d42, 5853 }, 5854 { 5855 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1", 5856 .access = variant->allowed_f1, 5857 }, 5858 { 5859 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2", 5860 .access = variant->allowed_f2, 5861 }, 5862 { 5863 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3", 5864 .access = variant->allowed_f3, 5865 }, 5866 { 5867 .path = TMP_DIR "/s2d1", 5868 .access = variant->allowed_s2d1, 5869 }, 5870 /* s2d2_fd */ 5871 { 5872 .path = TMP_DIR "/s3d1", 5873 .access = variant->allowed_s3d1, 5874 }, 5875 { 5876 .path = TMP_DIR "/s4d1", 5877 .access = variant->allowed_s4d1, 5878 }, 5879 {}, 5880 }; 5881 int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; 5882 5883 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 5884 5885 /* Adds rule for the covered directory. */ 5886 if (variant->allowed_s2d2) { 5887 ASSERT_EQ(0, landlock_add_rule( 5888 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 5889 &(struct landlock_path_beneath_attr){ 5890 .parent_fd = self->s2d2_fd, 5891 .allowed_access = 5892 variant->allowed_s2d2, 5893 }, 5894 0)); 5895 } 5896 EXPECT_EQ(0, close(self->s2d2_fd)); 5897 5898 s1d41_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d31/s1d41", 5899 O_DIRECTORY | O_PATH | O_CLOEXEC); 5900 ASSERT_LE(0, s1d41_bind_fd); 5901 s1d42_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d32/s1d42", 5902 O_DIRECTORY | O_PATH | O_CLOEXEC); 5903 ASSERT_LE(0, s1d42_bind_fd); 5904 5905 /* Disconnects and checks source and destination directories. */ 5906 EXPECT_EQ(0, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5907 EXPECT_EQ(0, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5908 /* Renames to make it accessible through s3d1/s1d41 */ 5909 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5910 AT_FDCWD, TMP_DIR "/s3d1/s1d41")); 5911 /* Renames to make it accessible through s4d1/s1d42 */ 5912 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5913 AT_FDCWD, TMP_DIR "/s4d1/s1d42")); 5914 EXPECT_EQ(ENOENT, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5915 EXPECT_EQ(ENOENT, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5916 5917 enforce_ruleset(_metadata, ruleset_fd); 5918 EXPECT_EQ(0, close(ruleset_fd)); 5919 5920 EXPECT_EQ(variant->expected_read_result, 5921 test_open_rel(s1d41_bind_fd, "f1", O_RDONLY)); 5922 5923 EXPECT_EQ(variant->expected_rename_result, 5924 test_renameat(s1d41_bind_fd, "f1", s1d42_bind_fd, "f1")); 5925 EXPECT_EQ(variant->expected_exchange_result, 5926 test_exchangeat(s1d41_bind_fd, "f2", s1d42_bind_fd, "f3")); 5927 5928 EXPECT_EQ(variant->expected_same_dir_rename_result, 5929 test_renameat(s1d42_bind_fd, "f4", s1d42_bind_fd, "f5")); 5930 } 5931 5932 /* 5933 * layout5_disconnected_branch before rename: 5934 * 5935 * tmp 5936 * ├── s1d1 5937 * │ └── s1d2 [source of the first bind mount] 5938 * │ └── s1d3 5939 * │ ├── s1d41 5940 * │ │ ├── f1 5941 * │ │ └── f2 5942 * │ └── s1d42 5943 * │ ├── f3 5944 * │ └── f4 5945 * ├── s2d1 5946 * │ └── s2d2 [source of the second bind mount] 5947 * │ └── s2d3 5948 * │ └── s2d4 [first s1d2 bind mount] 5949 * │ └── s1d3 5950 * │ ├── s1d41 5951 * │ │ ├── f1 5952 * │ │ └── f2 5953 * │ └── s1d42 5954 * │ ├── f3 5955 * │ └── f4 5956 * ├── s3d1 5957 * │ └── s3d2 [second s2d2 bind mount] 5958 * │ └── s2d3 5959 * │ └── s2d4 [first s1d2 bind mount] 5960 * │ └── s1d3 5961 * │ ├── s1d41 5962 * │ │ ├── f1 5963 * │ │ └── f2 5964 * │ └── s1d42 5965 * │ ├── f3 5966 * │ └── f4 5967 * └── s4d1 5968 * 5969 * After rename: 5970 * 5971 * tmp 5972 * ├── s1d1 5973 * │ └── s1d2 [source of the first bind mount] 5974 * │ └── s1d3 5975 * │ ├── s1d41 5976 * │ │ ├── f1 5977 * │ │ └── f2 5978 * │ └── s1d42 5979 * │ ├── f3 5980 * │ └── f4 5981 * ├── s2d1 5982 * │ └── s2d2 [source of the second bind mount] 5983 * ├── s3d1 5984 * │ └── s3d2 [second s2d2 bind mount] 5985 * └── s4d1 5986 * └── s2d3 [renamed here] 5987 * └── s2d4 [first s1d2 bind mount] 5988 * └── s1d3 5989 * ├── s1d41 5990 * │ ├── f1 5991 * │ └── f2 5992 * └── s1d42 5993 * ├── f3 5994 * └── f4 5995 * 5996 * Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor: 5997 * 1. first bind mount: s1d3 -> s1d2 5998 * 2. second bind mount: s2d3 5999 * 3. tmp mount: s4d1 -> tmp [disconnected branch] 6000 * 4. second bind mount: s2d2 6001 * 5. tmp mount: s3d1 -> tmp 6002 * 6. parent mounts: [...] -> / 6003 * 6004 * The s4d1 directory is evaluated even if it is not in the s2d2 mount. 6005 */ 6006 6007 /* clang-format off */ 6008 FIXTURE(layout5_disconnected_branch) { 6009 int s2d4_fd, s3d2_fd; 6010 }; 6011 /* clang-format on */ 6012 6013 FIXTURE_SETUP(layout5_disconnected_branch) 6014 { 6015 prepare_layout(_metadata); 6016 6017 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 6018 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 6019 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 6020 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 6021 create_directory(_metadata, TMP_DIR "/s2d1/s2d2/s2d3/s2d4"); 6022 create_directory(_metadata, TMP_DIR "/s3d1/s3d2"); 6023 create_directory(_metadata, TMP_DIR "/s4d1"); 6024 6025 self->s2d4_fd = open(TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 6026 O_DIRECTORY | O_PATH | O_CLOEXEC); 6027 ASSERT_LE(0, self->s2d4_fd); 6028 6029 self->s3d2_fd = 6030 open(TMP_DIR "/s3d1/s3d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 6031 ASSERT_LE(0, self->s3d2_fd); 6032 6033 set_cap(_metadata, CAP_SYS_ADMIN); 6034 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 6035 NULL, MS_BIND, NULL)); 6036 ASSERT_EQ(0, mount(TMP_DIR "/s2d1/s2d2", TMP_DIR "/s3d1/s3d2", NULL, 6037 MS_BIND | MS_REC, NULL)); 6038 clear_cap(_metadata, CAP_SYS_ADMIN); 6039 } 6040 6041 FIXTURE_TEARDOWN_PARENT(layout5_disconnected_branch) 6042 { 6043 /* Bind mounts are handled by namespace lifetime. */ 6044 6045 /* Removes files after renames. */ 6046 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 6047 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 6048 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1"); 6049 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 6050 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 6051 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5"); 6052 6053 cleanup_layout(_metadata); 6054 } 6055 6056 FIXTURE_VARIANT(layout5_disconnected_branch) 6057 { 6058 /* 6059 * Parent of all files. It should always be enforced when testing against 6060 * files under the s1d41 or s1d42 disconnected directories. 6061 */ 6062 const __u64 allowed_base; 6063 /* 6064 * Parent of the first bind mount source. It should always be ignored when 6065 * testing against files under the s1d41 or s1d42 disconnected directories. 6066 */ 6067 const __u64 allowed_s1d1; 6068 const __u64 allowed_s1d2; 6069 const __u64 allowed_s1d3; 6070 const __u64 allowed_s2d1; 6071 const __u64 allowed_s2d2; 6072 const __u64 allowed_s2d3; 6073 const __u64 allowed_s2d4; 6074 const __u64 allowed_s3d1; 6075 const __u64 allowed_s3d2; 6076 const __u64 allowed_s4d1; 6077 6078 /* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */ 6079 const int expected_read_result; 6080 /* 6081 * Expected result of the call to renameat([fd:s1d3]/s1d41/f1, 6082 * [fd:s1d3]/s1d42/f1). 6083 */ 6084 const int expected_rename_result; 6085 /* 6086 * Expected result of the call to renameat([fd:s1d3]/s1d41/f2, 6087 * [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE). 6088 */ 6089 const int expected_exchange_result; 6090 /* 6091 * Expected result of the call to renameat([fd:s1d3]/s1d42/f4, 6092 * [fd:s1d3]/s1d42/f5). 6093 */ 6094 const int expected_same_dir_rename_result; 6095 }; 6096 6097 /* clang-format off */ 6098 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) { 6099 /* clang-format on */ 6100 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 6101 LANDLOCK_ACCESS_FS_READ_FILE | 6102 LANDLOCK_ACCESS_FS_EXECUTE | 6103 LANDLOCK_ACCESS_FS_MAKE_REG, 6104 .expected_read_result = EACCES, 6105 .expected_same_dir_rename_result = EACCES, 6106 .expected_rename_result = EACCES, 6107 .expected_exchange_result = EACCES, 6108 }; 6109 6110 /* clang-format off */ 6111 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) { 6112 /* clang-format on */ 6113 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6114 .expected_read_result = 0, 6115 .expected_same_dir_rename_result = EACCES, 6116 .expected_rename_result = EACCES, 6117 .expected_exchange_result = EACCES, 6118 }; 6119 6120 /* clang-format off */ 6121 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) { 6122 /* clang-format on */ 6123 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6124 LANDLOCK_ACCESS_FS_MAKE_REG, 6125 .expected_read_result = 0, 6126 .expected_same_dir_rename_result = 0, 6127 .expected_rename_result = EXDEV, 6128 .expected_exchange_result = EXDEV, 6129 }; 6130 6131 /* clang-format off */ 6132 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) { 6133 /* clang-format on */ 6134 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6135 .expected_read_result = EACCES, 6136 .expected_same_dir_rename_result = 0, 6137 .expected_rename_result = 0, 6138 .expected_exchange_result = 0, 6139 }; 6140 6141 /* clang-format off */ 6142 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) { 6143 /* clang-format on */ 6144 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6145 .expected_read_result = 0, 6146 .expected_same_dir_rename_result = EACCES, 6147 .expected_rename_result = EACCES, 6148 .expected_exchange_result = EACCES, 6149 }; 6150 6151 /* clang-format off */ 6152 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) { 6153 /* clang-format on */ 6154 .allowed_s1d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6155 LANDLOCK_ACCESS_FS_MAKE_REG, 6156 .expected_read_result = 0, 6157 .expected_same_dir_rename_result = 0, 6158 .expected_rename_result = EXDEV, 6159 .expected_exchange_result = EXDEV, 6160 }; 6161 6162 /* clang-format off */ 6163 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) { 6164 /* clang-format on */ 6165 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6166 .expected_read_result = EACCES, 6167 .expected_same_dir_rename_result = 0, 6168 .expected_rename_result = 0, 6169 .expected_exchange_result = 0, 6170 }; 6171 6172 /* clang-format off */ 6173 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) { 6174 /* clang-format on */ 6175 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | 6176 LANDLOCK_ACCESS_FS_READ_FILE | 6177 LANDLOCK_ACCESS_FS_EXECUTE | 6178 LANDLOCK_ACCESS_FS_MAKE_REG, 6179 .expected_read_result = 0, 6180 .expected_same_dir_rename_result = 0, 6181 .expected_rename_result = 0, 6182 .expected_exchange_result = 0, 6183 }; 6184 6185 /* clang-format off */ 6186 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) { 6187 /* clang-format on */ 6188 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 6189 LANDLOCK_ACCESS_FS_READ_FILE | 6190 LANDLOCK_ACCESS_FS_EXECUTE | 6191 LANDLOCK_ACCESS_FS_MAKE_REG, 6192 .expected_read_result = EACCES, 6193 .expected_same_dir_rename_result = EACCES, 6194 .expected_rename_result = EACCES, 6195 .expected_exchange_result = EACCES, 6196 }; 6197 6198 /* clang-format off */ 6199 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) { 6200 /* clang-format on */ 6201 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6202 .expected_read_result = 0, 6203 .expected_same_dir_rename_result = EACCES, 6204 .expected_rename_result = EACCES, 6205 .expected_exchange_result = EACCES, 6206 }; 6207 6208 /* clang-format off */ 6209 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) { 6210 /* clang-format on */ 6211 .allowed_s2d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6212 LANDLOCK_ACCESS_FS_MAKE_REG, 6213 .expected_read_result = 0, 6214 .expected_same_dir_rename_result = 0, 6215 .expected_rename_result = EXDEV, 6216 .expected_exchange_result = EXDEV, 6217 }; 6218 6219 /* clang-format off */ 6220 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) { 6221 /* clang-format on */ 6222 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6223 .expected_read_result = EACCES, 6224 .expected_same_dir_rename_result = 0, 6225 .expected_rename_result = 0, 6226 .expected_exchange_result = 0, 6227 }; 6228 6229 /* clang-format off */ 6230 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) { 6231 /* clang-format on */ 6232 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6233 .expected_read_result = 0, 6234 .expected_same_dir_rename_result = EACCES, 6235 .expected_rename_result = EACCES, 6236 .expected_exchange_result = EACCES, 6237 }; 6238 6239 /* clang-format off */ 6240 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) { 6241 /* clang-format on */ 6242 .allowed_s2d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6243 LANDLOCK_ACCESS_FS_MAKE_REG, 6244 .expected_read_result = 0, 6245 .expected_same_dir_rename_result = 0, 6246 .expected_rename_result = EXDEV, 6247 .expected_exchange_result = EXDEV, 6248 }; 6249 6250 /* clang-format off */ 6251 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) { 6252 /* clang-format on */ 6253 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6254 .expected_read_result = EACCES, 6255 .expected_same_dir_rename_result = 0, 6256 .expected_rename_result = 0, 6257 .expected_exchange_result = 0, 6258 }; 6259 6260 /* clang-format off */ 6261 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) { 6262 /* clang-format on */ 6263 .allowed_s2d4 = LANDLOCK_ACCESS_FS_REFER | 6264 LANDLOCK_ACCESS_FS_READ_FILE | 6265 LANDLOCK_ACCESS_FS_EXECUTE | 6266 LANDLOCK_ACCESS_FS_MAKE_REG, 6267 .expected_read_result = EACCES, 6268 .expected_same_dir_rename_result = EACCES, 6269 .expected_rename_result = EACCES, 6270 .expected_exchange_result = EACCES, 6271 }; 6272 6273 /* clang-format off */ 6274 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) { 6275 /* clang-format on */ 6276 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6277 .expected_read_result = 0, 6278 .expected_same_dir_rename_result = EACCES, 6279 .expected_rename_result = EACCES, 6280 .expected_exchange_result = EACCES, 6281 }; 6282 6283 /* clang-format off */ 6284 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) { 6285 /* clang-format on */ 6286 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6287 LANDLOCK_ACCESS_FS_MAKE_REG, 6288 .expected_read_result = 0, 6289 .expected_same_dir_rename_result = 0, 6290 .expected_rename_result = EXDEV, 6291 .expected_exchange_result = EXDEV, 6292 }; 6293 6294 /* clang-format off */ 6295 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) { 6296 /* clang-format on */ 6297 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6298 .expected_read_result = EACCES, 6299 .expected_same_dir_rename_result = 0, 6300 .expected_rename_result = 0, 6301 .expected_exchange_result = 0, 6302 }; 6303 6304 /* clang-format off */ 6305 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) { 6306 /* clang-format on */ 6307 .allowed_s3d2 = LANDLOCK_ACCESS_FS_REFER | 6308 LANDLOCK_ACCESS_FS_READ_FILE | 6309 LANDLOCK_ACCESS_FS_EXECUTE | 6310 LANDLOCK_ACCESS_FS_MAKE_REG, 6311 .expected_read_result = EACCES, 6312 .expected_same_dir_rename_result = EACCES, 6313 .expected_rename_result = EACCES, 6314 .expected_exchange_result = EACCES, 6315 }; 6316 6317 /* clang-format off */ 6318 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) { 6319 /* clang-format on */ 6320 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6321 .expected_read_result = 0, 6322 .expected_same_dir_rename_result = EACCES, 6323 .expected_rename_result = EACCES, 6324 .expected_exchange_result = EACCES, 6325 }; 6326 6327 /* clang-format off */ 6328 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) { 6329 /* clang-format on */ 6330 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6331 LANDLOCK_ACCESS_FS_MAKE_REG, 6332 .expected_read_result = 0, 6333 .expected_same_dir_rename_result = 0, 6334 .expected_rename_result = EXDEV, 6335 .expected_exchange_result = EXDEV, 6336 }; 6337 6338 /* clang-format off */ 6339 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) { 6340 /* clang-format on */ 6341 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6342 .expected_read_result = EACCES, 6343 .expected_same_dir_rename_result = 0, 6344 .expected_rename_result = 0, 6345 .expected_exchange_result = 0, 6346 }; 6347 6348 TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange) 6349 { 6350 const __u64 handled_access = 6351 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 6352 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 6353 const struct rule rules[] = { 6354 { 6355 .path = TMP_DIR "/s1d1", 6356 .access = variant->allowed_s1d1, 6357 }, 6358 { 6359 .path = TMP_DIR "/s1d1/s1d2", 6360 .access = variant->allowed_s1d2, 6361 }, 6362 { 6363 .path = TMP_DIR "/s1d1/s1d2/s1d3", 6364 .access = variant->allowed_s1d3, 6365 }, 6366 { 6367 .path = TMP_DIR "/s2d1", 6368 .access = variant->allowed_s2d1, 6369 }, 6370 { 6371 .path = TMP_DIR "/s2d1/s2d2", 6372 .access = variant->allowed_s2d2, 6373 }, 6374 { 6375 .path = TMP_DIR "/s2d1/s2d2/s2d3", 6376 .access = variant->allowed_s2d3, 6377 }, 6378 /* s2d4_fd */ 6379 { 6380 .path = TMP_DIR "/s3d1", 6381 .access = variant->allowed_s3d1, 6382 }, 6383 /* s3d2_fd */ 6384 { 6385 .path = TMP_DIR "/s4d1", 6386 .access = variant->allowed_s4d1, 6387 }, 6388 {}, 6389 }; 6390 int ruleset_fd, s1d3_bind_fd; 6391 6392 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 6393 ASSERT_LE(0, ruleset_fd); 6394 6395 /* Adds rules for the covered directories. */ 6396 if (variant->allowed_s2d4) { 6397 ASSERT_EQ(0, landlock_add_rule( 6398 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6399 &(struct landlock_path_beneath_attr){ 6400 .parent_fd = self->s2d4_fd, 6401 .allowed_access = 6402 variant->allowed_s2d4, 6403 }, 6404 0)); 6405 } 6406 EXPECT_EQ(0, close(self->s2d4_fd)); 6407 6408 if (variant->allowed_s3d2) { 6409 ASSERT_EQ(0, landlock_add_rule( 6410 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6411 &(struct landlock_path_beneath_attr){ 6412 .parent_fd = self->s3d2_fd, 6413 .allowed_access = 6414 variant->allowed_s3d2, 6415 }, 6416 0)); 6417 } 6418 EXPECT_EQ(0, close(self->s3d2_fd)); 6419 6420 s1d3_bind_fd = open(TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3", 6421 O_DIRECTORY | O_PATH | O_CLOEXEC); 6422 ASSERT_LE(0, s1d3_bind_fd); 6423 6424 /* Disconnects and checks source and destination directories. */ 6425 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6426 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6427 /* Renames to make it accessible through s3d1/s1d41 */ 6428 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s2d1/s2d2/s2d3", 6429 AT_FDCWD, TMP_DIR "/s4d1/s2d3")); 6430 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6431 EXPECT_EQ(ENOENT, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6432 6433 enforce_ruleset(_metadata, ruleset_fd); 6434 EXPECT_EQ(0, close(ruleset_fd)); 6435 6436 EXPECT_EQ(variant->expected_read_result, 6437 test_open_rel(s1d3_bind_fd, "s1d41/f1", O_RDONLY)); 6438 6439 EXPECT_EQ(variant->expected_rename_result, 6440 test_renameat(s1d3_bind_fd, "s1d41/f1", s1d3_bind_fd, 6441 "s1d42/f1")); 6442 EXPECT_EQ(variant->expected_exchange_result, 6443 test_exchangeat(s1d3_bind_fd, "s1d41/f2", s1d3_bind_fd, 6444 "s1d42/f3")); 6445 6446 EXPECT_EQ(variant->expected_same_dir_rename_result, 6447 test_renameat(s1d3_bind_fd, "s1d42/f4", s1d3_bind_fd, 6448 "s1d42/f5")); 6449 } 6450 6451 #define LOWER_BASE TMP_DIR "/lower" 6452 #define LOWER_DATA LOWER_BASE "/data" 6453 static const char lower_fl1[] = LOWER_DATA "/fl1"; 6454 static const char lower_dl1[] = LOWER_DATA "/dl1"; 6455 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 6456 static const char lower_fo1[] = LOWER_DATA "/fo1"; 6457 static const char lower_do1[] = LOWER_DATA "/do1"; 6458 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 6459 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 6460 6461 static const char (*lower_base_files[])[] = { 6462 &lower_fl1, 6463 &lower_fo1, 6464 NULL, 6465 }; 6466 static const char (*lower_base_directories[])[] = { 6467 &lower_dl1, 6468 &lower_do1, 6469 NULL, 6470 }; 6471 static const char (*lower_sub_files[])[] = { 6472 &lower_dl1_fl2, 6473 &lower_do1_fo2, 6474 &lower_do1_fl3, 6475 NULL, 6476 }; 6477 6478 #define UPPER_BASE TMP_DIR "/upper" 6479 #define UPPER_DATA UPPER_BASE "/data" 6480 #define UPPER_WORK UPPER_BASE "/work" 6481 static const char upper_fu1[] = UPPER_DATA "/fu1"; 6482 static const char upper_du1[] = UPPER_DATA "/du1"; 6483 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 6484 static const char upper_fo1[] = UPPER_DATA "/fo1"; 6485 static const char upper_do1[] = UPPER_DATA "/do1"; 6486 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 6487 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 6488 6489 static const char (*upper_base_files[])[] = { 6490 &upper_fu1, 6491 &upper_fo1, 6492 NULL, 6493 }; 6494 static const char (*upper_base_directories[])[] = { 6495 &upper_du1, 6496 &upper_do1, 6497 NULL, 6498 }; 6499 static const char (*upper_sub_files[])[] = { 6500 &upper_du1_fu2, 6501 &upper_do1_fo2, 6502 &upper_do1_fu3, 6503 NULL, 6504 }; 6505 6506 #define MERGE_BASE TMP_DIR "/merge" 6507 #define MERGE_DATA MERGE_BASE "/data" 6508 static const char merge_fl1[] = MERGE_DATA "/fl1"; 6509 static const char merge_dl1[] = MERGE_DATA "/dl1"; 6510 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 6511 static const char merge_fu1[] = MERGE_DATA "/fu1"; 6512 static const char merge_du1[] = MERGE_DATA "/du1"; 6513 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 6514 static const char merge_fo1[] = MERGE_DATA "/fo1"; 6515 static const char merge_do1[] = MERGE_DATA "/do1"; 6516 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 6517 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 6518 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 6519 6520 static const char (*merge_base_files[])[] = { 6521 &merge_fl1, 6522 &merge_fu1, 6523 &merge_fo1, 6524 NULL, 6525 }; 6526 static const char (*merge_base_directories[])[] = { 6527 &merge_dl1, 6528 &merge_du1, 6529 &merge_do1, 6530 NULL, 6531 }; 6532 static const char (*merge_sub_files[])[] = { 6533 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 6534 &merge_do1_fl3, &merge_do1_fu3, NULL, 6535 }; 6536 6537 /* 6538 * layout2_overlay hierarchy: 6539 * 6540 * tmp 6541 * ├── lower 6542 * │ └── data 6543 * │ ├── dl1 6544 * │ │ └── fl2 6545 * │ ├── do1 6546 * │ │ ├── fl3 6547 * │ │ └── fo2 6548 * │ ├── fl1 6549 * │ └── fo1 6550 * ├── merge 6551 * │ └── data 6552 * │ ├── dl1 6553 * │ │ └── fl2 6554 * │ ├── do1 6555 * │ │ ├── fl3 6556 * │ │ ├── fo2 6557 * │ │ └── fu3 6558 * │ ├── du1 6559 * │ │ └── fu2 6560 * │ ├── fl1 6561 * │ ├── fo1 6562 * │ └── fu1 6563 * └── upper 6564 * ├── data 6565 * │ ├── do1 6566 * │ │ ├── fo2 6567 * │ │ └── fu3 6568 * │ ├── du1 6569 * │ │ └── fu2 6570 * │ ├── fo1 6571 * │ └── fu1 6572 * └── work 6573 * └── work 6574 */ 6575 6576 FIXTURE(layout2_overlay) 6577 { 6578 bool skip_test; 6579 }; 6580 6581 FIXTURE_SETUP(layout2_overlay) 6582 { 6583 if (!supports_filesystem("overlay")) { 6584 self->skip_test = true; 6585 SKIP(return, "overlayfs is not supported (setup)"); 6586 } 6587 6588 prepare_layout(_metadata); 6589 6590 create_directory(_metadata, LOWER_BASE); 6591 set_cap(_metadata, CAP_SYS_ADMIN); 6592 /* Creates tmpfs mount points to get deterministic overlayfs. */ 6593 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 6594 clear_cap(_metadata, CAP_SYS_ADMIN); 6595 create_file(_metadata, lower_fl1); 6596 create_file(_metadata, lower_dl1_fl2); 6597 create_file(_metadata, lower_fo1); 6598 create_file(_metadata, lower_do1_fo2); 6599 create_file(_metadata, lower_do1_fl3); 6600 6601 create_directory(_metadata, UPPER_BASE); 6602 set_cap(_metadata, CAP_SYS_ADMIN); 6603 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 6604 clear_cap(_metadata, CAP_SYS_ADMIN); 6605 create_file(_metadata, upper_fu1); 6606 create_file(_metadata, upper_du1_fu2); 6607 create_file(_metadata, upper_fo1); 6608 create_file(_metadata, upper_do1_fo2); 6609 create_file(_metadata, upper_do1_fu3); 6610 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 6611 6612 create_directory(_metadata, MERGE_DATA); 6613 set_cap(_metadata, CAP_SYS_ADMIN); 6614 set_cap(_metadata, CAP_DAC_OVERRIDE); 6615 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 6616 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 6617 ",workdir=" UPPER_WORK)); 6618 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6619 clear_cap(_metadata, CAP_SYS_ADMIN); 6620 } 6621 6622 FIXTURE_TEARDOWN_PARENT(layout2_overlay) 6623 { 6624 if (self->skip_test) 6625 SKIP(return, "overlayfs is not supported (teardown)"); 6626 6627 EXPECT_EQ(0, remove_path(lower_do1_fl3)); 6628 EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 6629 EXPECT_EQ(0, remove_path(lower_fl1)); 6630 EXPECT_EQ(0, remove_path(lower_do1_fo2)); 6631 EXPECT_EQ(0, remove_path(lower_fo1)); 6632 6633 /* umount(LOWER_BASE)) is handled by namespace lifetime. */ 6634 EXPECT_EQ(0, remove_path(LOWER_BASE)); 6635 6636 EXPECT_EQ(0, remove_path(upper_do1_fu3)); 6637 EXPECT_EQ(0, remove_path(upper_du1_fu2)); 6638 EXPECT_EQ(0, remove_path(upper_fu1)); 6639 EXPECT_EQ(0, remove_path(upper_do1_fo2)); 6640 EXPECT_EQ(0, remove_path(upper_fo1)); 6641 EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 6642 6643 /* umount(UPPER_BASE)) is handled by namespace lifetime. */ 6644 EXPECT_EQ(0, remove_path(UPPER_BASE)); 6645 6646 /* umount(MERGE_DATA)) is handled by namespace lifetime. */ 6647 EXPECT_EQ(0, remove_path(MERGE_DATA)); 6648 6649 cleanup_layout(_metadata); 6650 } 6651 6652 TEST_F_FORK(layout2_overlay, no_restriction) 6653 { 6654 if (self->skip_test) 6655 SKIP(return, "overlayfs is not supported (test)"); 6656 6657 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 6658 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 6659 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 6660 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 6661 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 6662 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 6663 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 6664 6665 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 6666 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 6667 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 6668 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 6669 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 6670 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 6671 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 6672 6673 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 6674 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 6675 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 6676 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 6677 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 6678 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 6679 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 6680 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 6681 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 6682 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 6683 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 6684 } 6685 6686 #define for_each_path(path_list, path_entry, i) \ 6687 for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 6688 path_entry = *path_list[++i]) 6689 6690 TEST_F_FORK(layout2_overlay, same_content_different_file) 6691 { 6692 /* Sets access right on parent directories of both layers. */ 6693 const struct rule layer1_base[] = { 6694 { 6695 .path = LOWER_BASE, 6696 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6697 }, 6698 { 6699 .path = UPPER_BASE, 6700 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6701 }, 6702 { 6703 .path = MERGE_BASE, 6704 .access = ACCESS_RW, 6705 }, 6706 {}, 6707 }; 6708 const struct rule layer2_data[] = { 6709 { 6710 .path = LOWER_DATA, 6711 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6712 }, 6713 { 6714 .path = UPPER_DATA, 6715 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6716 }, 6717 { 6718 .path = MERGE_DATA, 6719 .access = ACCESS_RW, 6720 }, 6721 {}, 6722 }; 6723 /* Sets access right on directories inside both layers. */ 6724 const struct rule layer3_subdirs[] = { 6725 { 6726 .path = lower_dl1, 6727 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6728 }, 6729 { 6730 .path = lower_do1, 6731 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6732 }, 6733 { 6734 .path = upper_du1, 6735 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6736 }, 6737 { 6738 .path = upper_do1, 6739 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6740 }, 6741 { 6742 .path = merge_dl1, 6743 .access = ACCESS_RW, 6744 }, 6745 { 6746 .path = merge_du1, 6747 .access = ACCESS_RW, 6748 }, 6749 { 6750 .path = merge_do1, 6751 .access = ACCESS_RW, 6752 }, 6753 {}, 6754 }; 6755 /* Tighten access rights to the files. */ 6756 const struct rule layer4_files[] = { 6757 { 6758 .path = lower_dl1_fl2, 6759 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6760 }, 6761 { 6762 .path = lower_do1_fo2, 6763 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6764 }, 6765 { 6766 .path = lower_do1_fl3, 6767 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6768 }, 6769 { 6770 .path = upper_du1_fu2, 6771 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6772 }, 6773 { 6774 .path = upper_do1_fo2, 6775 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6776 }, 6777 { 6778 .path = upper_do1_fu3, 6779 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6780 }, 6781 { 6782 .path = merge_dl1_fl2, 6783 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6784 LANDLOCK_ACCESS_FS_WRITE_FILE, 6785 }, 6786 { 6787 .path = merge_du1_fu2, 6788 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6789 LANDLOCK_ACCESS_FS_WRITE_FILE, 6790 }, 6791 { 6792 .path = merge_do1_fo2, 6793 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6794 LANDLOCK_ACCESS_FS_WRITE_FILE, 6795 }, 6796 { 6797 .path = merge_do1_fl3, 6798 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6799 LANDLOCK_ACCESS_FS_WRITE_FILE, 6800 }, 6801 { 6802 .path = merge_do1_fu3, 6803 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6804 LANDLOCK_ACCESS_FS_WRITE_FILE, 6805 }, 6806 {}, 6807 }; 6808 const struct rule layer5_merge_only[] = { 6809 { 6810 .path = MERGE_DATA, 6811 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6812 LANDLOCK_ACCESS_FS_WRITE_FILE, 6813 }, 6814 {}, 6815 }; 6816 size_t i; 6817 const char *path_entry; 6818 6819 if (self->skip_test) 6820 SKIP(return, "overlayfs is not supported (test)"); 6821 6822 /* Sets rules on base directories (i.e. outside overlay scope). */ 6823 enforce_fs(_metadata, ACCESS_RW, layer1_base); 6824 6825 /* Checks lower layer. */ 6826 for_each_path(lower_base_files, path_entry, i) { 6827 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6828 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6829 } 6830 for_each_path(lower_base_directories, path_entry, i) { 6831 ASSERT_EQ(EACCES, 6832 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6833 } 6834 for_each_path(lower_sub_files, path_entry, i) { 6835 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6836 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6837 } 6838 /* Checks upper layer. */ 6839 for_each_path(upper_base_files, path_entry, i) { 6840 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6841 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6842 } 6843 for_each_path(upper_base_directories, path_entry, i) { 6844 ASSERT_EQ(EACCES, 6845 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6846 } 6847 for_each_path(upper_sub_files, path_entry, i) { 6848 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6849 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6850 } 6851 /* 6852 * Checks that access rights are independent from the lower and upper 6853 * layers: write access to upper files viewed through the merge point 6854 * is still allowed, and write access to lower file viewed (and copied) 6855 * through the merge point is still allowed. 6856 */ 6857 for_each_path(merge_base_files, path_entry, i) { 6858 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6859 } 6860 for_each_path(merge_base_directories, path_entry, i) { 6861 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6862 } 6863 for_each_path(merge_sub_files, path_entry, i) { 6864 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6865 } 6866 6867 /* Sets rules on data directories (i.e. inside overlay scope). */ 6868 enforce_fs(_metadata, ACCESS_RW, layer2_data); 6869 6870 /* Checks merge. */ 6871 for_each_path(merge_base_files, path_entry, i) { 6872 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6873 } 6874 for_each_path(merge_base_directories, path_entry, i) { 6875 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6876 } 6877 for_each_path(merge_sub_files, path_entry, i) { 6878 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6879 } 6880 6881 /* Same checks with tighter rules. */ 6882 enforce_fs(_metadata, ACCESS_RW, layer3_subdirs); 6883 6884 /* Checks changes for lower layer. */ 6885 for_each_path(lower_base_files, path_entry, i) { 6886 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6887 } 6888 /* Checks changes for upper layer. */ 6889 for_each_path(upper_base_files, path_entry, i) { 6890 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6891 } 6892 /* Checks all merge accesses. */ 6893 for_each_path(merge_base_files, path_entry, i) { 6894 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6895 } 6896 for_each_path(merge_base_directories, path_entry, i) { 6897 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6898 } 6899 for_each_path(merge_sub_files, path_entry, i) { 6900 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6901 } 6902 6903 /* Sets rules directly on overlayed files. */ 6904 enforce_fs(_metadata, ACCESS_RW, layer4_files); 6905 6906 /* Checks unchanged accesses on lower layer. */ 6907 for_each_path(lower_sub_files, path_entry, i) { 6908 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6909 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6910 } 6911 /* Checks unchanged accesses on upper layer. */ 6912 for_each_path(upper_sub_files, path_entry, i) { 6913 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6914 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6915 } 6916 /* Checks all merge accesses. */ 6917 for_each_path(merge_base_files, path_entry, i) { 6918 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6919 } 6920 for_each_path(merge_base_directories, path_entry, i) { 6921 ASSERT_EQ(EACCES, 6922 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6923 } 6924 for_each_path(merge_sub_files, path_entry, i) { 6925 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6926 } 6927 6928 /* Only allowes access to the merge hierarchy. */ 6929 enforce_fs(_metadata, ACCESS_RW, layer5_merge_only); 6930 6931 /* Checks new accesses on lower layer. */ 6932 for_each_path(lower_sub_files, path_entry, i) { 6933 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6934 } 6935 /* Checks new accesses on upper layer. */ 6936 for_each_path(upper_sub_files, path_entry, i) { 6937 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6938 } 6939 /* Checks all merge accesses. */ 6940 for_each_path(merge_base_files, path_entry, i) { 6941 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6942 } 6943 for_each_path(merge_base_directories, path_entry, i) { 6944 ASSERT_EQ(EACCES, 6945 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6946 } 6947 for_each_path(merge_sub_files, path_entry, i) { 6948 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6949 } 6950 } 6951 6952 FIXTURE(layout3_fs) 6953 { 6954 bool has_created_dir; 6955 bool has_created_file; 6956 bool skip_test; 6957 }; 6958 6959 FIXTURE_VARIANT(layout3_fs) 6960 { 6961 const struct mnt_opt mnt; 6962 const char *const file_path; 6963 unsigned int cwd_fs_magic; 6964 }; 6965 6966 /* clang-format off */ 6967 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 6968 /* clang-format on */ 6969 .mnt = { 6970 .type = "tmpfs", 6971 .data = MNT_TMP_DATA, 6972 }, 6973 .file_path = file1_s1d1, 6974 }; 6975 6976 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 6977 .mnt = { 6978 .type = "ramfs", 6979 .data = "mode=700", 6980 }, 6981 .file_path = TMP_DIR "/dir/file", 6982 }; 6983 6984 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 6985 .mnt = { 6986 .type = "cgroup2", 6987 }, 6988 .file_path = TMP_DIR "/test/cgroup.procs", 6989 }; 6990 6991 FIXTURE_VARIANT_ADD(layout3_fs, proc) { 6992 .mnt = { 6993 .type = "proc", 6994 }, 6995 .file_path = TMP_DIR "/self/status", 6996 }; 6997 6998 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 6999 .mnt = { 7000 .type = "sysfs", 7001 }, 7002 .file_path = TMP_DIR "/kernel/notes", 7003 }; 7004 7005 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 7006 .mnt = { 7007 .source = TMP_DIR, 7008 .flags = MS_BIND, 7009 }, 7010 .file_path = TMP_DIR "/dir/file", 7011 .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 7012 }; 7013 7014 static char *dirname_alloc(const char *path) 7015 { 7016 char *dup; 7017 7018 if (!path) 7019 return NULL; 7020 7021 dup = strdup(path); 7022 if (!dup) 7023 return NULL; 7024 7025 return dirname(dup); 7026 } 7027 7028 FIXTURE_SETUP(layout3_fs) 7029 { 7030 struct stat statbuf; 7031 char *dir_path = dirname_alloc(variant->file_path); 7032 7033 if (!supports_filesystem(variant->mnt.type) || 7034 !cwd_matches_fs(variant->cwd_fs_magic)) { 7035 self->skip_test = true; 7036 SKIP(return, "this filesystem is not supported (setup)"); 7037 } 7038 7039 prepare_layout_opt(_metadata, &variant->mnt); 7040 7041 /* Creates directory when required. */ 7042 if (stat(dir_path, &statbuf)) { 7043 set_cap(_metadata, CAP_DAC_OVERRIDE); 7044 EXPECT_EQ(0, mkdir(dir_path, 0700)) 7045 { 7046 TH_LOG("Failed to create directory \"%s\": %s", 7047 dir_path, strerror(errno)); 7048 } 7049 self->has_created_dir = true; 7050 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7051 } 7052 7053 /* Creates file when required. */ 7054 if (stat(variant->file_path, &statbuf)) { 7055 int fd; 7056 7057 set_cap(_metadata, CAP_DAC_OVERRIDE); 7058 fd = creat(variant->file_path, 0600); 7059 EXPECT_LE(0, fd) 7060 { 7061 TH_LOG("Failed to create file \"%s\": %s", 7062 variant->file_path, strerror(errno)); 7063 } 7064 EXPECT_EQ(0, close(fd)); 7065 self->has_created_file = true; 7066 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7067 } 7068 7069 free(dir_path); 7070 } 7071 7072 FIXTURE_TEARDOWN_PARENT(layout3_fs) 7073 { 7074 if (self->skip_test) 7075 SKIP(return, "this filesystem is not supported (teardown)"); 7076 7077 if (self->has_created_file) { 7078 set_cap(_metadata, CAP_DAC_OVERRIDE); 7079 /* 7080 * Don't check for error because the file might already 7081 * have been removed (cf. release_inode test). 7082 */ 7083 unlink(variant->file_path); 7084 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7085 } 7086 7087 if (self->has_created_dir) { 7088 char *dir_path = dirname_alloc(variant->file_path); 7089 7090 set_cap(_metadata, CAP_DAC_OVERRIDE); 7091 /* 7092 * Don't check for error because the directory might already 7093 * have been removed (cf. release_inode test). 7094 */ 7095 rmdir(dir_path); 7096 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7097 free(dir_path); 7098 } 7099 7100 cleanup_layout(_metadata); 7101 } 7102 7103 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 7104 FIXTURE_DATA(layout3_fs) * self, 7105 const FIXTURE_VARIANT(layout3_fs) * variant, 7106 const char *const rule_path) 7107 { 7108 const struct rule layer1_allow_read_file[] = { 7109 { 7110 .path = rule_path, 7111 .access = LANDLOCK_ACCESS_FS_READ_FILE, 7112 }, 7113 {}, 7114 }; 7115 const char *const dev_null_path = "/dev/null"; 7116 7117 if (self->skip_test) 7118 SKIP(return, "this filesystem is not supported (test)"); 7119 7120 /* Checks without Landlock. */ 7121 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7122 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7123 7124 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 7125 layer1_allow_read_file); 7126 7127 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7128 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7129 7130 /* Forbids directory reading. */ 7131 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, NULL); 7132 7133 /* Checks with Landlock and forbidden access. */ 7134 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7135 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7136 } 7137 7138 /* Matrix of tests to check file hierarchy evaluation. */ 7139 7140 TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 7141 { 7142 /* The current directory must not be the root for this test. */ 7143 layer3_fs_tag_inode(_metadata, self, variant, "."); 7144 } 7145 7146 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 7147 { 7148 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 7149 } 7150 7151 TEST_F_FORK(layout3_fs, tag_inode_dir_child) 7152 { 7153 char *dir_path = dirname_alloc(variant->file_path); 7154 7155 layer3_fs_tag_inode(_metadata, self, variant, dir_path); 7156 free(dir_path); 7157 } 7158 7159 TEST_F_FORK(layout3_fs, tag_inode_file) 7160 { 7161 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 7162 } 7163 7164 /* Light version of layout1.release_inodes */ 7165 TEST_F_FORK(layout3_fs, release_inodes) 7166 { 7167 const struct rule layer1[] = { 7168 { 7169 .path = TMP_DIR, 7170 .access = LANDLOCK_ACCESS_FS_READ_DIR, 7171 }, 7172 {}, 7173 }; 7174 int ruleset_fd; 7175 7176 if (self->skip_test) 7177 SKIP(return, "this filesystem is not supported (test)"); 7178 7179 /* Clean up for the teardown to not fail. */ 7180 if (self->has_created_file) 7181 EXPECT_EQ(0, remove_path(variant->file_path)); 7182 7183 if (self->has_created_dir) { 7184 char *dir_path = dirname_alloc(variant->file_path); 7185 7186 /* Don't check for error because of cgroup specificities. */ 7187 remove_path(dir_path); 7188 free(dir_path); 7189 } 7190 7191 ruleset_fd = 7192 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 7193 7194 /* Unmount the filesystem while it is being used by a ruleset. */ 7195 set_cap(_metadata, CAP_SYS_ADMIN); 7196 ASSERT_EQ(0, umount(TMP_DIR)); 7197 clear_cap(_metadata, CAP_SYS_ADMIN); 7198 7199 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 7200 set_cap(_metadata, CAP_SYS_ADMIN); 7201 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 7202 clear_cap(_metadata, CAP_SYS_ADMIN); 7203 7204 enforce_ruleset(_metadata, ruleset_fd); 7205 ASSERT_EQ(0, close(ruleset_fd)); 7206 7207 /* Checks that access to the new mount point is denied. */ 7208 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 7209 } 7210 7211 static int matches_log_fs_extra(struct __test_metadata *const _metadata, 7212 int audit_fd, const char *const blockers, 7213 const char *const path, const char *const extra) 7214 { 7215 static const char log_template[] = REGEX_LANDLOCK_PREFIX 7216 " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$"; 7217 char *absolute_path = NULL; 7218 size_t log_match_remaining = sizeof(log_template) + strlen(blockers) + 7219 PATH_MAX * 2 + 7220 (extra ? strlen(extra) : 0) + 1; 7221 char log_match[log_match_remaining]; 7222 char *log_match_cursor = log_match; 7223 size_t chunk_len; 7224 7225 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7226 REGEX_LANDLOCK_PREFIX " blockers=%s path=\"", 7227 blockers); 7228 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7229 return -E2BIG; 7230 7231 /* 7232 * It is assumed that absolute_path does not contain control 7233 * characters nor spaces, see audit_string_contains_control(). 7234 */ 7235 absolute_path = realpath(path, NULL); 7236 if (!absolute_path) 7237 return -errno; 7238 7239 log_match_remaining -= chunk_len; 7240 log_match_cursor += chunk_len; 7241 log_match_cursor = regex_escape(absolute_path, log_match_cursor, 7242 log_match_remaining); 7243 free(absolute_path); 7244 if (log_match_cursor < 0) 7245 return (long long)log_match_cursor; 7246 7247 log_match_remaining -= log_match_cursor - log_match; 7248 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7249 "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$", 7250 extra ?: ""); 7251 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7252 return -E2BIG; 7253 7254 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, 7255 NULL); 7256 } 7257 7258 static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd, 7259 const char *const blockers, const char *const path) 7260 { 7261 return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL); 7262 } 7263 7264 FIXTURE(audit_layout1) 7265 { 7266 struct audit_filter audit_filter; 7267 int audit_fd; 7268 }; 7269 7270 FIXTURE_SETUP(audit_layout1) 7271 { 7272 prepare_layout(_metadata); 7273 7274 create_layout1(_metadata); 7275 7276 set_cap(_metadata, CAP_AUDIT_CONTROL); 7277 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 7278 EXPECT_LE(0, self->audit_fd); 7279 disable_caps(_metadata); 7280 } 7281 7282 FIXTURE_TEARDOWN_PARENT(audit_layout1) 7283 { 7284 remove_layout1(_metadata); 7285 7286 cleanup_layout(_metadata); 7287 7288 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 7289 } 7290 7291 TEST_F(audit_layout1, execute_make) 7292 { 7293 struct audit_records records; 7294 7295 copy_file(_metadata, bin_true, file1_s1d1); 7296 test_execute(_metadata, 0, file1_s1d1); 7297 test_check_exec(_metadata, 0, file1_s1d1); 7298 7299 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL); 7300 7301 test_execute(_metadata, EACCES, file1_s1d1); 7302 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7303 file1_s1d1)); 7304 test_check_exec(_metadata, EACCES, file1_s1d1); 7305 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7306 file1_s1d1)); 7307 7308 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7309 EXPECT_EQ(0, records.access); 7310 EXPECT_EQ(0, records.domain); 7311 } 7312 7313 /* 7314 * Using a set of handled/denied access rights make it possible to check that 7315 * only the blocked ones are logged. 7316 */ 7317 7318 TEST_F(audit_layout1, execute_read) 7319 { 7320 struct audit_records records; 7321 7322 copy_file(_metadata, bin_true, file1_s1d1); 7323 test_execute(_metadata, 0, file1_s1d1); 7324 test_check_exec(_metadata, 0, file1_s1d1); 7325 7326 enforce_fs(_metadata, ACCESS_ALL, NULL); 7327 7328 /* 7329 * The only difference with the previous audit_layout1.execute_read test is 7330 * the extra ",fs\\.read_file" blocked by the executable file. 7331 */ 7332 test_execute(_metadata, EACCES, file1_s1d1); 7333 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7334 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7335 test_check_exec(_metadata, EACCES, file1_s1d1); 7336 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7337 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7338 7339 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7340 EXPECT_EQ(0, records.access); 7341 EXPECT_EQ(0, records.domain); 7342 } 7343 7344 TEST_F(audit_layout1, write_file) 7345 { 7346 struct audit_records records; 7347 7348 enforce_fs(_metadata, ACCESS_ALL, NULL); 7349 7350 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 7351 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7352 "fs\\.write_file", file1_s1d1)); 7353 7354 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7355 EXPECT_EQ(0, records.access); 7356 EXPECT_EQ(1, records.domain); 7357 } 7358 7359 TEST_F(audit_layout1, read_file) 7360 { 7361 struct audit_records records; 7362 7363 enforce_fs(_metadata, ACCESS_ALL, NULL); 7364 7365 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 7366 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file", 7367 file1_s1d1)); 7368 7369 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7370 EXPECT_EQ(0, records.access); 7371 EXPECT_EQ(1, records.domain); 7372 } 7373 7374 TEST_F(audit_layout1, read_dir) 7375 { 7376 struct audit_records records; 7377 7378 enforce_fs(_metadata, ACCESS_ALL, NULL); 7379 7380 EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY)); 7381 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir", 7382 dir_s1d1)); 7383 7384 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7385 EXPECT_EQ(0, records.access); 7386 EXPECT_EQ(1, records.domain); 7387 } 7388 7389 TEST_F(audit_layout1, remove_dir) 7390 { 7391 struct audit_records records; 7392 7393 EXPECT_EQ(0, unlink(file1_s1d3)); 7394 EXPECT_EQ(0, unlink(file2_s1d3)); 7395 7396 enforce_fs(_metadata, ACCESS_ALL, NULL); 7397 7398 EXPECT_EQ(-1, rmdir(dir_s1d3)); 7399 EXPECT_EQ(EACCES, errno); 7400 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7401 "fs\\.remove_dir", dir_s1d2)); 7402 7403 EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 7404 EXPECT_EQ(EACCES, errno); 7405 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7406 "fs\\.remove_dir", dir_s1d2)); 7407 7408 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7409 EXPECT_EQ(0, records.access); 7410 EXPECT_EQ(0, records.domain); 7411 } 7412 7413 TEST_F(audit_layout1, remove_file) 7414 { 7415 struct audit_records records; 7416 7417 enforce_fs(_metadata, ACCESS_ALL, NULL); 7418 7419 EXPECT_EQ(-1, unlink(file1_s1d3)); 7420 EXPECT_EQ(EACCES, errno); 7421 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7422 "fs\\.remove_file", dir_s1d3)); 7423 7424 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7425 EXPECT_EQ(0, records.access); 7426 EXPECT_EQ(1, records.domain); 7427 } 7428 7429 TEST_F(audit_layout1, make_char) 7430 { 7431 struct audit_records records; 7432 7433 EXPECT_EQ(0, unlink(file1_s1d3)); 7434 7435 enforce_fs(_metadata, ACCESS_ALL, NULL); 7436 7437 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, 0)); 7438 EXPECT_EQ(EACCES, errno); 7439 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char", 7440 dir_s1d3)); 7441 7442 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7443 EXPECT_EQ(0, records.access); 7444 EXPECT_EQ(1, records.domain); 7445 } 7446 7447 TEST_F(audit_layout1, make_dir) 7448 { 7449 struct audit_records records; 7450 7451 EXPECT_EQ(0, unlink(file1_s1d3)); 7452 7453 enforce_fs(_metadata, ACCESS_ALL, NULL); 7454 7455 EXPECT_EQ(-1, mkdir(file1_s1d3, 0755)); 7456 EXPECT_EQ(EACCES, errno); 7457 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir", 7458 dir_s1d3)); 7459 7460 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7461 EXPECT_EQ(0, records.access); 7462 EXPECT_EQ(1, records.domain); 7463 } 7464 7465 TEST_F(audit_layout1, make_reg) 7466 { 7467 struct audit_records records; 7468 7469 EXPECT_EQ(0, unlink(file1_s1d3)); 7470 7471 enforce_fs(_metadata, ACCESS_ALL, NULL); 7472 7473 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFREG | 0644, 0)); 7474 EXPECT_EQ(EACCES, errno); 7475 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg", 7476 dir_s1d3)); 7477 7478 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7479 EXPECT_EQ(0, records.access); 7480 EXPECT_EQ(1, records.domain); 7481 } 7482 7483 TEST_F(audit_layout1, make_sock) 7484 { 7485 struct audit_records records; 7486 7487 EXPECT_EQ(0, unlink(file1_s1d3)); 7488 7489 enforce_fs(_metadata, ACCESS_ALL, NULL); 7490 7491 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFSOCK | 0644, 0)); 7492 EXPECT_EQ(EACCES, errno); 7493 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock", 7494 dir_s1d3)); 7495 7496 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7497 EXPECT_EQ(0, records.access); 7498 EXPECT_EQ(1, records.domain); 7499 } 7500 7501 TEST_F(audit_layout1, make_fifo) 7502 { 7503 struct audit_records records; 7504 7505 EXPECT_EQ(0, unlink(file1_s1d3)); 7506 7507 enforce_fs(_metadata, ACCESS_ALL, NULL); 7508 7509 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFIFO | 0644, 0)); 7510 EXPECT_EQ(EACCES, errno); 7511 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo", 7512 dir_s1d3)); 7513 7514 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7515 EXPECT_EQ(0, records.access); 7516 EXPECT_EQ(1, records.domain); 7517 } 7518 7519 TEST_F(audit_layout1, make_block) 7520 { 7521 struct audit_records records; 7522 7523 EXPECT_EQ(0, unlink(file1_s1d3)); 7524 7525 enforce_fs(_metadata, ACCESS_ALL, NULL); 7526 7527 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0)); 7528 EXPECT_EQ(EACCES, errno); 7529 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7530 "fs\\.make_block", dir_s1d3)); 7531 7532 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7533 EXPECT_EQ(0, records.access); 7534 EXPECT_EQ(1, records.domain); 7535 } 7536 7537 TEST_F(audit_layout1, make_sym) 7538 { 7539 struct audit_records records; 7540 7541 EXPECT_EQ(0, unlink(file1_s1d3)); 7542 7543 enforce_fs(_metadata, ACCESS_ALL, NULL); 7544 7545 EXPECT_EQ(-1, symlink("target", file1_s1d3)); 7546 EXPECT_EQ(EACCES, errno); 7547 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym", 7548 dir_s1d3)); 7549 7550 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7551 EXPECT_EQ(0, records.access); 7552 EXPECT_EQ(1, records.domain); 7553 } 7554 7555 TEST_F(audit_layout1, refer_handled) 7556 { 7557 struct audit_records records; 7558 7559 EXPECT_EQ(0, unlink(file1_s1d3)); 7560 7561 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, NULL); 7562 7563 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7564 EXPECT_EQ(EXDEV, errno); 7565 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7566 dir_s1d1)); 7567 EXPECT_EQ(0, 7568 matches_log_domain_allocated(self->audit_fd, getpid(), NULL)); 7569 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7570 dir_s1d3)); 7571 7572 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7573 EXPECT_EQ(0, records.access); 7574 EXPECT_EQ(0, records.domain); 7575 } 7576 7577 TEST_F(audit_layout1, refer_make) 7578 { 7579 struct audit_records records; 7580 7581 EXPECT_EQ(0, unlink(file1_s1d3)); 7582 7583 enforce_fs(_metadata, 7584 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 7585 NULL); 7586 7587 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7588 EXPECT_EQ(EACCES, errno); 7589 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7590 dir_s1d1)); 7591 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7592 "fs\\.make_reg,fs\\.refer", dir_s1d3)); 7593 7594 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7595 EXPECT_EQ(0, records.access); 7596 EXPECT_EQ(0, records.domain); 7597 } 7598 7599 TEST_F(audit_layout1, refer_rename) 7600 { 7601 struct audit_records records; 7602 7603 EXPECT_EQ(0, unlink(file1_s1d3)); 7604 7605 enforce_fs(_metadata, ACCESS_ALL, NULL); 7606 7607 EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3)); 7608 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7609 "fs\\.remove_file,fs\\.refer", dir_s1d2)); 7610 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7611 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7612 dir_s2d3)); 7613 7614 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7615 EXPECT_EQ(0, records.access); 7616 EXPECT_EQ(0, records.domain); 7617 } 7618 7619 TEST_F(audit_layout1, refer_exchange) 7620 { 7621 struct audit_records records; 7622 7623 EXPECT_EQ(0, unlink(file1_s1d3)); 7624 7625 enforce_fs(_metadata, ACCESS_ALL, NULL); 7626 7627 /* 7628 * The only difference with the previous audit_layout1.refer_rename test is 7629 * the extra ",fs\\.make_reg" blocked by the source directory. 7630 */ 7631 EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3)); 7632 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7633 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7634 dir_s1d2)); 7635 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7636 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7637 dir_s2d3)); 7638 7639 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7640 EXPECT_EQ(0, records.access); 7641 EXPECT_EQ(0, records.domain); 7642 } 7643 7644 /* 7645 * This test checks that the audit record is correctly generated when the 7646 * operation is only partially denied. This is the case for rename(2) when the 7647 * source file is allowed to be referenced but the destination directory is not. 7648 * 7649 * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize 7650 * file path walks and prepare for audit support") and commit 058518c20920 7651 * ("landlock: Align partial refer access checks with final ones"). 7652 */ 7653 TEST_F(audit_layout1, refer_rename_half) 7654 { 7655 struct audit_records records; 7656 const struct rule layer1[] = { 7657 { 7658 .path = dir_s2d2, 7659 .access = LANDLOCK_ACCESS_FS_REFER, 7660 }, 7661 {}, 7662 }; 7663 7664 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 7665 7666 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 7667 ASSERT_EQ(EXDEV, errno); 7668 7669 /* Only half of the request is denied. */ 7670 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7671 dir_s1d1)); 7672 7673 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7674 EXPECT_EQ(0, records.access); 7675 EXPECT_EQ(1, records.domain); 7676 } 7677 7678 TEST_F(audit_layout1, truncate) 7679 { 7680 struct audit_records records; 7681 7682 enforce_fs(_metadata, ACCESS_ALL, NULL); 7683 7684 EXPECT_EQ(-1, truncate(file1_s1d3, 0)); 7685 EXPECT_EQ(EACCES, errno); 7686 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate", 7687 file1_s1d3)); 7688 7689 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7690 EXPECT_EQ(0, records.access); 7691 EXPECT_EQ(1, records.domain); 7692 } 7693 7694 TEST_F(audit_layout1, ioctl_dev) 7695 { 7696 struct audit_records records; 7697 int fd; 7698 7699 enforce_fs(_metadata, ACCESS_ALL & ~LANDLOCK_ACCESS_FS_READ_FILE, NULL); 7700 7701 fd = open("/dev/null", O_RDONLY | O_CLOEXEC); 7702 ASSERT_LE(0, fd); 7703 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 7704 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7705 "fs\\.ioctl_dev", "/dev/null", 7706 " ioctlcmd=0x541b")); 7707 7708 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7709 EXPECT_EQ(0, records.access); 7710 EXPECT_EQ(1, records.domain); 7711 } 7712 7713 TEST_F(audit_layout1, resolve_unix) 7714 { 7715 struct audit_records records; 7716 const char *const path = "sock"; 7717 int srv_fd, cli_fd, status; 7718 pid_t child_pid; 7719 7720 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path); 7721 7722 child_pid = fork(); 7723 ASSERT_LE(0, child_pid); 7724 if (!child_pid) { 7725 enforce_fs(_metadata, ACCESS_ALL, NULL); 7726 7727 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 7728 ASSERT_LE(0, cli_fd); 7729 EXPECT_EQ(EACCES, 7730 test_connect_named_unix(_metadata, cli_fd, path)); 7731 7732 EXPECT_EQ(0, close(cli_fd)); 7733 _exit(_metadata->exit_code); 7734 } 7735 7736 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 7737 EXPECT_EQ(1, WIFEXITED(status)); 7738 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 7739 7740 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7741 "fs\\.resolve_unix", path, NULL)); 7742 7743 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7744 EXPECT_EQ(0, records.access); 7745 EXPECT_EQ(1, records.domain); 7746 7747 EXPECT_EQ(0, close(srv_fd)); 7748 } 7749 7750 TEST_F(audit_layout1, mount) 7751 { 7752 struct audit_records records; 7753 7754 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL); 7755 7756 set_cap(_metadata, CAP_SYS_ADMIN); 7757 EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 7758 EXPECT_EQ(EPERM, errno); 7759 clear_cap(_metadata, CAP_SYS_ADMIN); 7760 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7761 "fs\\.change_topology", dir_s3d2)); 7762 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7763 EXPECT_EQ(0, records.access); 7764 EXPECT_EQ(1, records.domain); 7765 } 7766 7767 TEST_HARNESS_MAIN 7768