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 <fcntl.h> 12 #include <linux/landlock.h> 13 #include <linux/magic.h> 14 #include <sched.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include <sys/capability.h> 18 #include <sys/mount.h> 19 #include <sys/prctl.h> 20 #include <sys/sendfile.h> 21 #include <sys/stat.h> 22 #include <sys/sysmacros.h> 23 #include <sys/vfs.h> 24 #include <unistd.h> 25 26 #include "common.h" 27 28 #ifndef renameat2 29 int renameat2(int olddirfd, const char *oldpath, int newdirfd, 30 const char *newpath, unsigned int flags) 31 { 32 return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 33 flags); 34 } 35 #endif 36 37 #ifndef RENAME_EXCHANGE 38 #define RENAME_EXCHANGE (1 << 1) 39 #endif 40 41 #define TMP_DIR "tmp" 42 #define BINARY_PATH "./true" 43 44 /* Paths (sibling number and depth) */ 45 static const char dir_s1d1[] = TMP_DIR "/s1d1"; 46 static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 47 static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 48 static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 49 static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 50 static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 51 static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 52 static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 53 static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 54 55 static const char dir_s2d1[] = TMP_DIR "/s2d1"; 56 static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 57 static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 58 static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 59 static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 60 static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 61 static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 62 63 static const char dir_s3d1[] = TMP_DIR "/s3d1"; 64 static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; 65 /* dir_s3d2 is a mount point. */ 66 static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 67 static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 68 69 /* 70 * layout1 hierarchy: 71 * 72 * tmp 73 * ├── s1d1 74 * │ ├── f1 75 * │ ├── f2 76 * │ └── s1d2 77 * │ ├── f1 78 * │ ├── f2 79 * │ └── s1d3 80 * │ ├── f1 81 * │ └── f2 82 * ├── s2d1 83 * │ ├── f1 84 * │ └── s2d2 85 * │ ├── f1 86 * │ └── s2d3 87 * │ ├── f1 88 * │ └── f2 89 * └── s3d1 90 * ├── f1 91 * └── s3d2 92 * └── s3d3 93 */ 94 95 static bool fgrep(FILE *const inf, const char *const str) 96 { 97 char line[32]; 98 const int slen = strlen(str); 99 100 while (!feof(inf)) { 101 if (!fgets(line, sizeof(line), inf)) 102 break; 103 if (strncmp(line, str, slen)) 104 continue; 105 106 return true; 107 } 108 109 return false; 110 } 111 112 static bool supports_filesystem(const char *const filesystem) 113 { 114 char str[32]; 115 int len; 116 bool res = true; 117 FILE *const inf = fopen("/proc/filesystems", "r"); 118 119 /* 120 * Consider that the filesystem is supported if we cannot get the 121 * supported ones. 122 */ 123 if (!inf) 124 return true; 125 126 /* filesystem can be null for bind mounts. */ 127 if (!filesystem) 128 goto out; 129 130 len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); 131 if (len >= sizeof(str)) 132 /* Ignores too-long filesystem names. */ 133 goto out; 134 135 res = fgrep(inf, str); 136 137 out: 138 fclose(inf); 139 return res; 140 } 141 142 static bool cwd_matches_fs(unsigned int fs_magic) 143 { 144 struct statfs statfs_buf; 145 146 if (!fs_magic) 147 return true; 148 149 if (statfs(".", &statfs_buf)) 150 return true; 151 152 return statfs_buf.f_type == fs_magic; 153 } 154 155 static void mkdir_parents(struct __test_metadata *const _metadata, 156 const char *const path) 157 { 158 char *walker; 159 const char *parent; 160 int i, err; 161 162 ASSERT_NE(path[0], '\0'); 163 walker = strdup(path); 164 ASSERT_NE(NULL, walker); 165 parent = walker; 166 for (i = 1; walker[i]; i++) { 167 if (walker[i] != '/') 168 continue; 169 walker[i] = '\0'; 170 err = mkdir(parent, 0700); 171 ASSERT_FALSE(err && errno != EEXIST) 172 { 173 TH_LOG("Failed to create directory \"%s\": %s", parent, 174 strerror(errno)); 175 } 176 walker[i] = '/'; 177 } 178 free(walker); 179 } 180 181 static void create_directory(struct __test_metadata *const _metadata, 182 const char *const path) 183 { 184 mkdir_parents(_metadata, path); 185 ASSERT_EQ(0, mkdir(path, 0700)) 186 { 187 TH_LOG("Failed to create directory \"%s\": %s", path, 188 strerror(errno)); 189 } 190 } 191 192 static void create_file(struct __test_metadata *const _metadata, 193 const char *const path) 194 { 195 mkdir_parents(_metadata, path); 196 ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 197 { 198 TH_LOG("Failed to create file \"%s\": %s", path, 199 strerror(errno)); 200 } 201 } 202 203 static int remove_path(const char *const path) 204 { 205 char *walker; 206 int i, ret, err = 0; 207 208 walker = strdup(path); 209 if (!walker) { 210 err = ENOMEM; 211 goto out; 212 } 213 if (unlink(path) && rmdir(path)) { 214 if (errno != ENOENT && errno != ENOTDIR) 215 err = errno; 216 goto out; 217 } 218 for (i = strlen(walker); i > 0; i--) { 219 if (walker[i] != '/') 220 continue; 221 walker[i] = '\0'; 222 ret = rmdir(walker); 223 if (ret) { 224 if (errno != ENOTEMPTY && errno != EBUSY) 225 err = errno; 226 goto out; 227 } 228 if (strcmp(walker, TMP_DIR) == 0) 229 goto out; 230 } 231 232 out: 233 free(walker); 234 return err; 235 } 236 237 struct mnt_opt { 238 const char *const source; 239 const char *const type; 240 const unsigned long flags; 241 const char *const data; 242 }; 243 244 const struct mnt_opt mnt_tmp = { 245 .type = "tmpfs", 246 .data = "size=4m,mode=700", 247 }; 248 249 static int mount_opt(const struct mnt_opt *const mnt, const char *const target) 250 { 251 return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags, 252 mnt->data); 253 } 254 255 static void prepare_layout_opt(struct __test_metadata *const _metadata, 256 const struct mnt_opt *const mnt) 257 { 258 disable_caps(_metadata); 259 umask(0077); 260 create_directory(_metadata, TMP_DIR); 261 262 /* 263 * Do not pollute the rest of the system: creates a private mount point 264 * for tests relying on pivot_root(2) and move_mount(2). 265 */ 266 set_cap(_metadata, CAP_SYS_ADMIN); 267 ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); 268 ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) 269 { 270 TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, 271 strerror(errno)); 272 /* 273 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() 274 * failed, so we need to explicitly do a minimal cleanup to 275 * avoid cascading errors with other tests that don't depend on 276 * the same filesystem. 277 */ 278 remove_path(TMP_DIR); 279 } 280 ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 281 clear_cap(_metadata, CAP_SYS_ADMIN); 282 } 283 284 static void prepare_layout(struct __test_metadata *const _metadata) 285 { 286 prepare_layout_opt(_metadata, &mnt_tmp); 287 } 288 289 static void cleanup_layout(struct __test_metadata *const _metadata) 290 { 291 set_cap(_metadata, CAP_SYS_ADMIN); 292 EXPECT_EQ(0, umount(TMP_DIR)); 293 clear_cap(_metadata, CAP_SYS_ADMIN); 294 EXPECT_EQ(0, remove_path(TMP_DIR)); 295 } 296 297 /* clang-format off */ 298 FIXTURE(layout0) {}; 299 /* clang-format on */ 300 301 FIXTURE_SETUP(layout0) 302 { 303 prepare_layout(_metadata); 304 } 305 306 FIXTURE_TEARDOWN(layout0) 307 { 308 cleanup_layout(_metadata); 309 } 310 311 static void create_layout1(struct __test_metadata *const _metadata) 312 { 313 create_file(_metadata, file1_s1d1); 314 create_file(_metadata, file1_s1d2); 315 create_file(_metadata, file1_s1d3); 316 create_file(_metadata, file2_s1d1); 317 create_file(_metadata, file2_s1d2); 318 create_file(_metadata, file2_s1d3); 319 320 create_file(_metadata, file1_s2d1); 321 create_file(_metadata, file1_s2d2); 322 create_file(_metadata, file1_s2d3); 323 create_file(_metadata, file2_s2d3); 324 325 create_file(_metadata, file1_s3d1); 326 create_directory(_metadata, dir_s3d2); 327 set_cap(_metadata, CAP_SYS_ADMIN); 328 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 329 clear_cap(_metadata, CAP_SYS_ADMIN); 330 331 ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 332 } 333 334 static void remove_layout1(struct __test_metadata *const _metadata) 335 { 336 EXPECT_EQ(0, remove_path(file2_s1d3)); 337 EXPECT_EQ(0, remove_path(file2_s1d2)); 338 EXPECT_EQ(0, remove_path(file2_s1d1)); 339 EXPECT_EQ(0, remove_path(file1_s1d3)); 340 EXPECT_EQ(0, remove_path(file1_s1d2)); 341 EXPECT_EQ(0, remove_path(file1_s1d1)); 342 EXPECT_EQ(0, remove_path(dir_s1d3)); 343 344 EXPECT_EQ(0, remove_path(file2_s2d3)); 345 EXPECT_EQ(0, remove_path(file1_s2d3)); 346 EXPECT_EQ(0, remove_path(file1_s2d2)); 347 EXPECT_EQ(0, remove_path(file1_s2d1)); 348 EXPECT_EQ(0, remove_path(dir_s2d2)); 349 350 EXPECT_EQ(0, remove_path(file1_s3d1)); 351 EXPECT_EQ(0, remove_path(dir_s3d3)); 352 set_cap(_metadata, CAP_SYS_ADMIN); 353 umount(dir_s3d2); 354 clear_cap(_metadata, CAP_SYS_ADMIN); 355 EXPECT_EQ(0, remove_path(dir_s3d2)); 356 } 357 358 /* clang-format off */ 359 FIXTURE(layout1) {}; 360 /* clang-format on */ 361 362 FIXTURE_SETUP(layout1) 363 { 364 prepare_layout(_metadata); 365 366 create_layout1(_metadata); 367 } 368 369 FIXTURE_TEARDOWN(layout1) 370 { 371 remove_layout1(_metadata); 372 373 cleanup_layout(_metadata); 374 } 375 376 /* 377 * This helper enables to use the ASSERT_* macros and print the line number 378 * pointing to the test caller. 379 */ 380 static int test_open_rel(const int dirfd, const char *const path, 381 const int flags) 382 { 383 int fd; 384 385 /* Works with file and directories. */ 386 fd = openat(dirfd, path, flags | O_CLOEXEC); 387 if (fd < 0) 388 return errno; 389 /* 390 * Mixing error codes from close(2) and open(2) should not lead to any 391 * (access type) confusion for this test. 392 */ 393 if (close(fd) != 0) 394 return errno; 395 return 0; 396 } 397 398 static int test_open(const char *const path, const int flags) 399 { 400 return test_open_rel(AT_FDCWD, path, flags); 401 } 402 403 TEST_F_FORK(layout1, no_restriction) 404 { 405 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 406 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 407 ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 408 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 409 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 410 ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 411 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 412 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 413 414 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 415 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 416 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 417 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 418 ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 419 ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 420 421 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 422 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 423 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 424 } 425 426 TEST_F_FORK(layout1, inval) 427 { 428 struct landlock_path_beneath_attr path_beneath = { 429 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 430 LANDLOCK_ACCESS_FS_WRITE_FILE, 431 .parent_fd = -1, 432 }; 433 struct landlock_ruleset_attr ruleset_attr = { 434 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 435 LANDLOCK_ACCESS_FS_WRITE_FILE, 436 }; 437 int ruleset_fd; 438 439 path_beneath.parent_fd = 440 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 441 ASSERT_LE(0, path_beneath.parent_fd); 442 443 ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 444 ASSERT_LE(0, ruleset_fd); 445 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 446 &path_beneath, 0)); 447 /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 448 ASSERT_EQ(EBADF, errno); 449 ASSERT_EQ(0, close(ruleset_fd)); 450 451 ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 452 ASSERT_LE(0, ruleset_fd); 453 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 454 &path_beneath, 0)); 455 /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 456 ASSERT_EQ(EBADFD, errno); 457 ASSERT_EQ(0, close(ruleset_fd)); 458 459 /* Gets a real ruleset. */ 460 ruleset_fd = 461 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 462 ASSERT_LE(0, ruleset_fd); 463 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 464 &path_beneath, 0)); 465 ASSERT_EQ(0, close(path_beneath.parent_fd)); 466 467 /* Tests without O_PATH. */ 468 path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 469 ASSERT_LE(0, path_beneath.parent_fd); 470 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 471 &path_beneath, 0)); 472 ASSERT_EQ(0, close(path_beneath.parent_fd)); 473 474 /* Tests with a ruleset FD. */ 475 path_beneath.parent_fd = ruleset_fd; 476 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 477 &path_beneath, 0)); 478 ASSERT_EQ(EBADFD, errno); 479 480 /* Checks unhandled allowed_access. */ 481 path_beneath.parent_fd = 482 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 483 ASSERT_LE(0, path_beneath.parent_fd); 484 485 /* Test with legitimate values. */ 486 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 487 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 488 &path_beneath, 0)); 489 ASSERT_EQ(EINVAL, errno); 490 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 491 492 /* Tests with denied-by-default access right. */ 493 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 494 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 495 &path_beneath, 0)); 496 ASSERT_EQ(EINVAL, errno); 497 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 498 499 /* Test with unknown (64-bits) value. */ 500 path_beneath.allowed_access |= (1ULL << 60); 501 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 502 &path_beneath, 0)); 503 ASSERT_EQ(EINVAL, errno); 504 path_beneath.allowed_access &= ~(1ULL << 60); 505 506 /* Test with no access. */ 507 path_beneath.allowed_access = 0; 508 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 509 &path_beneath, 0)); 510 ASSERT_EQ(ENOMSG, errno); 511 path_beneath.allowed_access &= ~(1ULL << 60); 512 513 ASSERT_EQ(0, close(path_beneath.parent_fd)); 514 515 /* Enforces the ruleset. */ 516 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 517 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 518 519 ASSERT_EQ(0, close(ruleset_fd)); 520 } 521 522 /* clang-format off */ 523 524 #define ACCESS_FILE ( \ 525 LANDLOCK_ACCESS_FS_EXECUTE | \ 526 LANDLOCK_ACCESS_FS_WRITE_FILE | \ 527 LANDLOCK_ACCESS_FS_READ_FILE | \ 528 LANDLOCK_ACCESS_FS_TRUNCATE) 529 530 #define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE 531 532 #define ACCESS_ALL ( \ 533 ACCESS_FILE | \ 534 LANDLOCK_ACCESS_FS_READ_DIR | \ 535 LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 536 LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 537 LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 538 LANDLOCK_ACCESS_FS_MAKE_DIR | \ 539 LANDLOCK_ACCESS_FS_MAKE_REG | \ 540 LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 541 LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 542 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 543 LANDLOCK_ACCESS_FS_MAKE_SYM | \ 544 LANDLOCK_ACCESS_FS_REFER) 545 546 /* clang-format on */ 547 548 TEST_F_FORK(layout1, file_and_dir_access_rights) 549 { 550 __u64 access; 551 int err; 552 struct landlock_path_beneath_attr path_beneath_file = {}, 553 path_beneath_dir = {}; 554 struct landlock_ruleset_attr ruleset_attr = { 555 .handled_access_fs = ACCESS_ALL, 556 }; 557 const int ruleset_fd = 558 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 559 560 ASSERT_LE(0, ruleset_fd); 561 562 /* Tests access rights for files. */ 563 path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 564 ASSERT_LE(0, path_beneath_file.parent_fd); 565 566 /* Tests access rights for directories. */ 567 path_beneath_dir.parent_fd = 568 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 569 ASSERT_LE(0, path_beneath_dir.parent_fd); 570 571 for (access = 1; access <= ACCESS_LAST; access <<= 1) { 572 path_beneath_dir.allowed_access = access; 573 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 574 LANDLOCK_RULE_PATH_BENEATH, 575 &path_beneath_dir, 0)); 576 577 path_beneath_file.allowed_access = access; 578 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 579 &path_beneath_file, 0); 580 if (access & ACCESS_FILE) { 581 ASSERT_EQ(0, err); 582 } else { 583 ASSERT_EQ(-1, err); 584 ASSERT_EQ(EINVAL, errno); 585 } 586 } 587 ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 588 ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 589 ASSERT_EQ(0, close(ruleset_fd)); 590 } 591 592 TEST_F_FORK(layout0, unknown_access_rights) 593 { 594 __u64 access_mask; 595 596 for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 597 access_mask >>= 1) { 598 struct landlock_ruleset_attr ruleset_attr = { 599 .handled_access_fs = access_mask, 600 }; 601 602 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 603 sizeof(ruleset_attr), 0)); 604 ASSERT_EQ(EINVAL, errno); 605 } 606 } 607 608 static void add_path_beneath(struct __test_metadata *const _metadata, 609 const int ruleset_fd, const __u64 allowed_access, 610 const char *const path) 611 { 612 struct landlock_path_beneath_attr path_beneath = { 613 .allowed_access = allowed_access, 614 }; 615 616 path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 617 ASSERT_LE(0, path_beneath.parent_fd) 618 { 619 TH_LOG("Failed to open directory \"%s\": %s", path, 620 strerror(errno)); 621 } 622 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 623 &path_beneath, 0)) 624 { 625 TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 626 strerror(errno)); 627 } 628 ASSERT_EQ(0, close(path_beneath.parent_fd)); 629 } 630 631 struct rule { 632 const char *path; 633 __u64 access; 634 }; 635 636 /* clang-format off */ 637 638 #define ACCESS_RO ( \ 639 LANDLOCK_ACCESS_FS_READ_FILE | \ 640 LANDLOCK_ACCESS_FS_READ_DIR) 641 642 #define ACCESS_RW ( \ 643 ACCESS_RO | \ 644 LANDLOCK_ACCESS_FS_WRITE_FILE) 645 646 /* clang-format on */ 647 648 static int create_ruleset(struct __test_metadata *const _metadata, 649 const __u64 handled_access_fs, 650 const struct rule rules[]) 651 { 652 int ruleset_fd, i; 653 struct landlock_ruleset_attr ruleset_attr = { 654 .handled_access_fs = handled_access_fs, 655 }; 656 657 ASSERT_NE(NULL, rules) 658 { 659 TH_LOG("No rule list"); 660 } 661 ASSERT_NE(NULL, rules[0].path) 662 { 663 TH_LOG("Empty rule list"); 664 } 665 666 ruleset_fd = 667 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 668 ASSERT_LE(0, ruleset_fd) 669 { 670 TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 671 } 672 673 for (i = 0; rules[i].path; i++) { 674 add_path_beneath(_metadata, ruleset_fd, rules[i].access, 675 rules[i].path); 676 } 677 return ruleset_fd; 678 } 679 680 static void enforce_ruleset(struct __test_metadata *const _metadata, 681 const int ruleset_fd) 682 { 683 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 684 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 685 { 686 TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 687 } 688 } 689 690 TEST_F_FORK(layout0, proc_nsfs) 691 { 692 const struct rule rules[] = { 693 { 694 .path = "/dev/null", 695 .access = LANDLOCK_ACCESS_FS_READ_FILE | 696 LANDLOCK_ACCESS_FS_WRITE_FILE, 697 }, 698 {}, 699 }; 700 struct landlock_path_beneath_attr path_beneath; 701 const int ruleset_fd = create_ruleset( 702 _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 703 rules); 704 705 ASSERT_LE(0, ruleset_fd); 706 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 707 708 enforce_ruleset(_metadata, ruleset_fd); 709 710 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 711 ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 712 ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 713 ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 714 715 ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 716 ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 717 ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 718 /* 719 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 720 * disconnected path. Such path cannot be identified and must then be 721 * allowed. 722 */ 723 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 724 725 /* 726 * Checks that it is not possible to add nsfs-like filesystem 727 * references to a ruleset. 728 */ 729 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 730 LANDLOCK_ACCESS_FS_WRITE_FILE, 731 path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 732 ASSERT_LE(0, path_beneath.parent_fd); 733 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 734 &path_beneath, 0)); 735 ASSERT_EQ(EBADFD, errno); 736 ASSERT_EQ(0, close(path_beneath.parent_fd)); 737 } 738 739 TEST_F_FORK(layout0, unpriv) 740 { 741 const struct rule rules[] = { 742 { 743 .path = TMP_DIR, 744 .access = ACCESS_RO, 745 }, 746 {}, 747 }; 748 int ruleset_fd; 749 750 drop_caps(_metadata); 751 752 ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 753 ASSERT_LE(0, ruleset_fd); 754 ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 755 ASSERT_EQ(EPERM, errno); 756 757 /* enforce_ruleset() calls prctl(no_new_privs). */ 758 enforce_ruleset(_metadata, ruleset_fd); 759 ASSERT_EQ(0, close(ruleset_fd)); 760 } 761 762 TEST_F_FORK(layout1, effective_access) 763 { 764 const struct rule rules[] = { 765 { 766 .path = dir_s1d2, 767 .access = ACCESS_RO, 768 }, 769 { 770 .path = file1_s2d2, 771 .access = LANDLOCK_ACCESS_FS_READ_FILE | 772 LANDLOCK_ACCESS_FS_WRITE_FILE, 773 }, 774 {}, 775 }; 776 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 777 char buf; 778 int reg_fd; 779 780 ASSERT_LE(0, ruleset_fd); 781 enforce_ruleset(_metadata, ruleset_fd); 782 ASSERT_EQ(0, close(ruleset_fd)); 783 784 /* Tests on a directory (with or without O_PATH). */ 785 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 786 ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 787 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 788 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 789 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 790 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 791 792 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 793 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 794 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 795 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 796 797 /* Tests on a file (with or without O_PATH). */ 798 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 799 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 800 801 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 802 803 /* Checks effective read and write actions. */ 804 reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 805 ASSERT_LE(0, reg_fd); 806 ASSERT_EQ(1, write(reg_fd, ".", 1)); 807 ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 808 ASSERT_EQ(1, read(reg_fd, &buf, 1)); 809 ASSERT_EQ('.', buf); 810 ASSERT_EQ(0, close(reg_fd)); 811 812 /* Just in case, double-checks effective actions. */ 813 reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 814 ASSERT_LE(0, reg_fd); 815 ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 816 ASSERT_EQ(EBADF, errno); 817 ASSERT_EQ(0, close(reg_fd)); 818 } 819 820 TEST_F_FORK(layout1, unhandled_access) 821 { 822 const struct rule rules[] = { 823 { 824 .path = dir_s1d2, 825 .access = ACCESS_RO, 826 }, 827 {}, 828 }; 829 /* Here, we only handle read accesses, not write accesses. */ 830 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 831 832 ASSERT_LE(0, ruleset_fd); 833 enforce_ruleset(_metadata, ruleset_fd); 834 ASSERT_EQ(0, close(ruleset_fd)); 835 836 /* 837 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 838 * opening for write-only should be allowed, but not read-write. 839 */ 840 ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 841 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 842 843 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 844 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 845 } 846 847 TEST_F_FORK(layout1, ruleset_overlap) 848 { 849 const struct rule rules[] = { 850 /* These rules should be ORed among them. */ 851 { 852 .path = dir_s1d2, 853 .access = LANDLOCK_ACCESS_FS_READ_FILE | 854 LANDLOCK_ACCESS_FS_WRITE_FILE, 855 }, 856 { 857 .path = dir_s1d2, 858 .access = LANDLOCK_ACCESS_FS_READ_FILE | 859 LANDLOCK_ACCESS_FS_READ_DIR, 860 }, 861 {}, 862 }; 863 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 864 865 ASSERT_LE(0, ruleset_fd); 866 enforce_ruleset(_metadata, ruleset_fd); 867 ASSERT_EQ(0, close(ruleset_fd)); 868 869 /* Checks s1d1 hierarchy. */ 870 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 871 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 872 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 873 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 874 875 /* Checks s1d2 hierarchy. */ 876 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 877 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 878 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 879 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 880 881 /* Checks s1d3 hierarchy. */ 882 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 883 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 884 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 885 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 886 } 887 888 TEST_F_FORK(layout1, layer_rule_unions) 889 { 890 const struct rule layer1[] = { 891 { 892 .path = dir_s1d2, 893 .access = LANDLOCK_ACCESS_FS_READ_FILE, 894 }, 895 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 896 { 897 .path = dir_s1d3, 898 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 899 }, 900 {}, 901 }; 902 const struct rule layer2[] = { 903 /* Doesn't change anything from layer1. */ 904 { 905 .path = dir_s1d2, 906 .access = LANDLOCK_ACCESS_FS_READ_FILE | 907 LANDLOCK_ACCESS_FS_WRITE_FILE, 908 }, 909 {}, 910 }; 911 const struct rule layer3[] = { 912 /* Only allows write (but not read) to dir_s1d3. */ 913 { 914 .path = dir_s1d2, 915 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 916 }, 917 {}, 918 }; 919 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1); 920 921 ASSERT_LE(0, ruleset_fd); 922 enforce_ruleset(_metadata, ruleset_fd); 923 ASSERT_EQ(0, close(ruleset_fd)); 924 925 /* Checks s1d1 hierarchy with layer1. */ 926 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 927 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 928 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 929 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 930 931 /* Checks s1d2 hierarchy with layer1. */ 932 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 933 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 934 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 935 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 936 937 /* Checks s1d3 hierarchy with layer1. */ 938 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 939 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 940 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 941 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 942 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 943 944 /* Doesn't change anything from layer1. */ 945 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2); 946 ASSERT_LE(0, ruleset_fd); 947 enforce_ruleset(_metadata, ruleset_fd); 948 ASSERT_EQ(0, close(ruleset_fd)); 949 950 /* Checks s1d1 hierarchy with layer2. */ 951 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 952 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 953 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 954 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 955 956 /* Checks s1d2 hierarchy with layer2. */ 957 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 958 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 959 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 960 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 961 962 /* Checks s1d3 hierarchy with layer2. */ 963 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 964 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 965 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 966 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 967 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 968 969 /* Only allows write (but not read) to dir_s1d3. */ 970 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3); 971 ASSERT_LE(0, ruleset_fd); 972 enforce_ruleset(_metadata, ruleset_fd); 973 ASSERT_EQ(0, close(ruleset_fd)); 974 975 /* Checks s1d1 hierarchy with layer3. */ 976 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 977 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 978 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 979 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 980 981 /* Checks s1d2 hierarchy with layer3. */ 982 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 983 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 984 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 985 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 986 987 /* Checks s1d3 hierarchy with layer3. */ 988 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 989 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 990 /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 991 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 992 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 993 } 994 995 TEST_F_FORK(layout1, non_overlapping_accesses) 996 { 997 const struct rule layer1[] = { 998 { 999 .path = dir_s1d2, 1000 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1001 }, 1002 {}, 1003 }; 1004 const struct rule layer2[] = { 1005 { 1006 .path = dir_s1d3, 1007 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1008 }, 1009 {}, 1010 }; 1011 int ruleset_fd; 1012 1013 ASSERT_EQ(0, unlink(file1_s1d1)); 1014 ASSERT_EQ(0, unlink(file1_s1d2)); 1015 1016 ruleset_fd = 1017 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 1018 ASSERT_LE(0, ruleset_fd); 1019 enforce_ruleset(_metadata, ruleset_fd); 1020 ASSERT_EQ(0, close(ruleset_fd)); 1021 1022 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1023 ASSERT_EQ(EACCES, errno); 1024 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1025 ASSERT_EQ(0, unlink(file1_s1d2)); 1026 1027 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 1028 layer2); 1029 ASSERT_LE(0, ruleset_fd); 1030 enforce_ruleset(_metadata, ruleset_fd); 1031 ASSERT_EQ(0, close(ruleset_fd)); 1032 1033 /* Unchanged accesses for file creation. */ 1034 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1035 ASSERT_EQ(EACCES, errno); 1036 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1037 1038 /* Checks file removing. */ 1039 ASSERT_EQ(-1, unlink(file1_s1d2)); 1040 ASSERT_EQ(EACCES, errno); 1041 ASSERT_EQ(0, unlink(file1_s1d3)); 1042 } 1043 1044 TEST_F_FORK(layout1, interleaved_masked_accesses) 1045 { 1046 /* 1047 * Checks overly restrictive rules: 1048 * layer 1: allows R s1d1/s1d2/s1d3/file1 1049 * layer 2: allows RW s1d1/s1d2/s1d3 1050 * allows W s1d1/s1d2 1051 * denies R s1d1/s1d2 1052 * layer 3: allows R s1d1 1053 * layer 4: allows R s1d1/s1d2 1054 * denies W s1d1/s1d2 1055 * layer 5: allows R s1d1/s1d2 1056 * layer 6: allows X ---- 1057 * layer 7: allows W s1d1/s1d2 1058 * denies R s1d1/s1d2 1059 */ 1060 const struct rule layer1_read[] = { 1061 /* Allows read access to file1_s1d3 with the first layer. */ 1062 { 1063 .path = file1_s1d3, 1064 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1065 }, 1066 {}, 1067 }; 1068 /* First rule with write restrictions. */ 1069 const struct rule layer2_read_write[] = { 1070 /* Start by granting read-write access via its parent directory... */ 1071 { 1072 .path = dir_s1d3, 1073 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1074 LANDLOCK_ACCESS_FS_WRITE_FILE, 1075 }, 1076 /* ...but also denies read access via its grandparent directory. */ 1077 { 1078 .path = dir_s1d2, 1079 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1080 }, 1081 {}, 1082 }; 1083 const struct rule layer3_read[] = { 1084 /* Allows read access via its great-grandparent directory. */ 1085 { 1086 .path = dir_s1d1, 1087 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1088 }, 1089 {}, 1090 }; 1091 const struct rule layer4_read_write[] = { 1092 /* 1093 * Try to confuse the deny access by denying write (but not 1094 * read) access via its grandparent directory. 1095 */ 1096 { 1097 .path = dir_s1d2, 1098 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1099 }, 1100 {}, 1101 }; 1102 const struct rule layer5_read[] = { 1103 /* 1104 * Try to override layer2's deny read access by explicitly 1105 * allowing read access via file1_s1d3's grandparent. 1106 */ 1107 { 1108 .path = dir_s1d2, 1109 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1110 }, 1111 {}, 1112 }; 1113 const struct rule layer6_execute[] = { 1114 /* 1115 * Restricts an unrelated file hierarchy with a new access 1116 * (non-overlapping) type. 1117 */ 1118 { 1119 .path = dir_s2d1, 1120 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1121 }, 1122 {}, 1123 }; 1124 const struct rule layer7_read_write[] = { 1125 /* 1126 * Finally, denies read access to file1_s1d3 via its 1127 * grandparent. 1128 */ 1129 { 1130 .path = dir_s1d2, 1131 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1132 }, 1133 {}, 1134 }; 1135 int ruleset_fd; 1136 1137 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1138 layer1_read); 1139 ASSERT_LE(0, ruleset_fd); 1140 enforce_ruleset(_metadata, ruleset_fd); 1141 ASSERT_EQ(0, close(ruleset_fd)); 1142 1143 /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1144 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1145 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1146 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1147 1148 ruleset_fd = create_ruleset(_metadata, 1149 LANDLOCK_ACCESS_FS_READ_FILE | 1150 LANDLOCK_ACCESS_FS_WRITE_FILE, 1151 layer2_read_write); 1152 ASSERT_LE(0, ruleset_fd); 1153 enforce_ruleset(_metadata, ruleset_fd); 1154 ASSERT_EQ(0, close(ruleset_fd)); 1155 1156 /* Checks that previous access rights are unchanged with layer 2. */ 1157 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1158 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1159 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1160 1161 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1162 layer3_read); 1163 ASSERT_LE(0, ruleset_fd); 1164 enforce_ruleset(_metadata, ruleset_fd); 1165 ASSERT_EQ(0, close(ruleset_fd)); 1166 1167 /* Checks that previous access rights are unchanged with layer 3. */ 1168 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1169 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1170 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1171 1172 /* This time, denies write access for the file hierarchy. */ 1173 ruleset_fd = create_ruleset(_metadata, 1174 LANDLOCK_ACCESS_FS_READ_FILE | 1175 LANDLOCK_ACCESS_FS_WRITE_FILE, 1176 layer4_read_write); 1177 ASSERT_LE(0, ruleset_fd); 1178 enforce_ruleset(_metadata, ruleset_fd); 1179 ASSERT_EQ(0, close(ruleset_fd)); 1180 1181 /* 1182 * Checks that the only change with layer 4 is that write access is 1183 * denied. 1184 */ 1185 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1186 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1187 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1188 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1189 1190 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1191 layer5_read); 1192 ASSERT_LE(0, ruleset_fd); 1193 enforce_ruleset(_metadata, ruleset_fd); 1194 ASSERT_EQ(0, close(ruleset_fd)); 1195 1196 /* Checks that previous access rights are unchanged with layer 5. */ 1197 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1198 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1199 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1200 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1201 1202 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 1203 layer6_execute); 1204 ASSERT_LE(0, ruleset_fd); 1205 enforce_ruleset(_metadata, ruleset_fd); 1206 ASSERT_EQ(0, close(ruleset_fd)); 1207 1208 /* Checks that previous access rights are unchanged with layer 6. */ 1209 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1210 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1211 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1212 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1213 1214 ruleset_fd = create_ruleset(_metadata, 1215 LANDLOCK_ACCESS_FS_READ_FILE | 1216 LANDLOCK_ACCESS_FS_WRITE_FILE, 1217 layer7_read_write); 1218 ASSERT_LE(0, ruleset_fd); 1219 enforce_ruleset(_metadata, ruleset_fd); 1220 ASSERT_EQ(0, close(ruleset_fd)); 1221 1222 /* Checks read access is now denied with layer 7. */ 1223 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1224 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1225 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1226 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1227 } 1228 1229 TEST_F_FORK(layout1, inherit_subset) 1230 { 1231 const struct rule rules[] = { 1232 { 1233 .path = dir_s1d2, 1234 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1235 LANDLOCK_ACCESS_FS_READ_DIR, 1236 }, 1237 {}, 1238 }; 1239 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1240 1241 ASSERT_LE(0, ruleset_fd); 1242 enforce_ruleset(_metadata, ruleset_fd); 1243 1244 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1245 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1246 1247 /* Write access is forbidden. */ 1248 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1249 /* Readdir access is allowed. */ 1250 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1251 1252 /* Write access is forbidden. */ 1253 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1254 /* Readdir access is allowed. */ 1255 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1256 1257 /* 1258 * Tests shared rule extension: the following rules should not grant 1259 * any new access, only remove some. Once enforced, these rules are 1260 * ANDed with the previous ones. 1261 */ 1262 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1263 dir_s1d2); 1264 /* 1265 * According to ruleset_fd, dir_s1d2 should now have the 1266 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1267 * access rights (even if this directory is opened a second time). 1268 * However, when enforcing this updated ruleset, the ruleset tied to 1269 * the current process (i.e. its domain) will still only have the 1270 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1271 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1272 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1273 * be a privilege escalation. 1274 */ 1275 enforce_ruleset(_metadata, ruleset_fd); 1276 1277 /* Same tests and results as above. */ 1278 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1279 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1280 1281 /* It is still forbidden to write in file1_s1d2. */ 1282 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1283 /* Readdir access is still allowed. */ 1284 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1285 1286 /* It is still forbidden to write in file1_s1d3. */ 1287 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1288 /* Readdir access is still allowed. */ 1289 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1290 1291 /* 1292 * Try to get more privileges by adding new access rights to the parent 1293 * directory: dir_s1d1. 1294 */ 1295 add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 1296 enforce_ruleset(_metadata, ruleset_fd); 1297 1298 /* Same tests and results as above. */ 1299 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1300 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1301 1302 /* It is still forbidden to write in file1_s1d2. */ 1303 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1304 /* Readdir access is still allowed. */ 1305 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1306 1307 /* It is still forbidden to write in file1_s1d3. */ 1308 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1309 /* Readdir access is still allowed. */ 1310 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1311 1312 /* 1313 * Now, dir_s1d3 get a new rule tied to it, only allowing 1314 * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1315 * that there was no rule tied to it before. 1316 */ 1317 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1318 dir_s1d3); 1319 enforce_ruleset(_metadata, ruleset_fd); 1320 ASSERT_EQ(0, close(ruleset_fd)); 1321 1322 /* 1323 * Same tests and results as above, except for open(dir_s1d3) which is 1324 * now denied because the new rule mask the rule previously inherited 1325 * from dir_s1d2. 1326 */ 1327 1328 /* Same tests and results as above. */ 1329 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1330 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1331 1332 /* It is still forbidden to write in file1_s1d2. */ 1333 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1334 /* Readdir access is still allowed. */ 1335 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1336 1337 /* It is still forbidden to write in file1_s1d3. */ 1338 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1339 /* 1340 * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1341 * the same layer. 1342 */ 1343 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1344 } 1345 1346 TEST_F_FORK(layout1, inherit_superset) 1347 { 1348 const struct rule rules[] = { 1349 { 1350 .path = dir_s1d3, 1351 .access = ACCESS_RO, 1352 }, 1353 {}, 1354 }; 1355 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1356 1357 ASSERT_LE(0, ruleset_fd); 1358 enforce_ruleset(_metadata, ruleset_fd); 1359 1360 /* Readdir access is denied for dir_s1d2. */ 1361 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1362 /* Readdir access is allowed for dir_s1d3. */ 1363 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1364 /* File access is allowed for file1_s1d3. */ 1365 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1366 1367 /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1368 add_path_beneath(_metadata, ruleset_fd, 1369 LANDLOCK_ACCESS_FS_READ_FILE | 1370 LANDLOCK_ACCESS_FS_READ_DIR, 1371 dir_s1d2); 1372 enforce_ruleset(_metadata, ruleset_fd); 1373 ASSERT_EQ(0, close(ruleset_fd)); 1374 1375 /* Readdir access is still denied for dir_s1d2. */ 1376 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1377 /* Readdir access is still allowed for dir_s1d3. */ 1378 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1379 /* File access is still allowed for file1_s1d3. */ 1380 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1381 } 1382 1383 TEST_F_FORK(layout0, max_layers) 1384 { 1385 int i, err; 1386 const struct rule rules[] = { 1387 { 1388 .path = TMP_DIR, 1389 .access = ACCESS_RO, 1390 }, 1391 {}, 1392 }; 1393 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1394 1395 ASSERT_LE(0, ruleset_fd); 1396 for (i = 0; i < 16; i++) 1397 enforce_ruleset(_metadata, ruleset_fd); 1398 1399 for (i = 0; i < 2; i++) { 1400 err = landlock_restrict_self(ruleset_fd, 0); 1401 ASSERT_EQ(-1, err); 1402 ASSERT_EQ(E2BIG, errno); 1403 } 1404 ASSERT_EQ(0, close(ruleset_fd)); 1405 } 1406 1407 TEST_F_FORK(layout1, empty_or_same_ruleset) 1408 { 1409 struct landlock_ruleset_attr ruleset_attr = {}; 1410 int ruleset_fd; 1411 1412 /* Tests empty handled_access_fs. */ 1413 ruleset_fd = 1414 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1415 ASSERT_LE(-1, ruleset_fd); 1416 ASSERT_EQ(ENOMSG, errno); 1417 1418 /* Enforces policy which deny read access to all files. */ 1419 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1420 ruleset_fd = 1421 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1422 ASSERT_LE(0, ruleset_fd); 1423 enforce_ruleset(_metadata, ruleset_fd); 1424 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1425 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1426 1427 /* Nests a policy which deny read access to all directories. */ 1428 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1429 ruleset_fd = 1430 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1431 ASSERT_LE(0, ruleset_fd); 1432 enforce_ruleset(_metadata, ruleset_fd); 1433 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1434 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1435 1436 /* Enforces a second time with the same ruleset. */ 1437 enforce_ruleset(_metadata, ruleset_fd); 1438 ASSERT_EQ(0, close(ruleset_fd)); 1439 } 1440 1441 TEST_F_FORK(layout1, rule_on_mountpoint) 1442 { 1443 const struct rule rules[] = { 1444 { 1445 .path = dir_s1d1, 1446 .access = ACCESS_RO, 1447 }, 1448 { 1449 /* dir_s3d2 is a mount point. */ 1450 .path = dir_s3d2, 1451 .access = ACCESS_RO, 1452 }, 1453 {}, 1454 }; 1455 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1456 1457 ASSERT_LE(0, ruleset_fd); 1458 enforce_ruleset(_metadata, ruleset_fd); 1459 ASSERT_EQ(0, close(ruleset_fd)); 1460 1461 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1462 1463 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1464 1465 ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1466 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1467 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1468 } 1469 1470 TEST_F_FORK(layout1, rule_over_mountpoint) 1471 { 1472 const struct rule rules[] = { 1473 { 1474 .path = dir_s1d1, 1475 .access = ACCESS_RO, 1476 }, 1477 { 1478 /* dir_s3d2 is a mount point. */ 1479 .path = dir_s3d1, 1480 .access = ACCESS_RO, 1481 }, 1482 {}, 1483 }; 1484 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1485 1486 ASSERT_LE(0, ruleset_fd); 1487 enforce_ruleset(_metadata, ruleset_fd); 1488 ASSERT_EQ(0, close(ruleset_fd)); 1489 1490 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1491 1492 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1493 1494 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1495 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1496 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1497 } 1498 1499 /* 1500 * This test verifies that we can apply a landlock rule on the root directory 1501 * (which might require special handling). 1502 */ 1503 TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1504 { 1505 struct rule rules[] = { 1506 { 1507 .path = "/", 1508 .access = ACCESS_RO, 1509 }, 1510 {}, 1511 }; 1512 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1513 1514 ASSERT_LE(0, ruleset_fd); 1515 enforce_ruleset(_metadata, ruleset_fd); 1516 ASSERT_EQ(0, close(ruleset_fd)); 1517 1518 /* Checks allowed access. */ 1519 ASSERT_EQ(0, test_open("/", O_RDONLY)); 1520 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1521 1522 rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1523 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1524 ASSERT_LE(0, ruleset_fd); 1525 enforce_ruleset(_metadata, ruleset_fd); 1526 ASSERT_EQ(0, close(ruleset_fd)); 1527 1528 /* Checks denied access (on a directory). */ 1529 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1530 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1531 } 1532 1533 TEST_F_FORK(layout1, rule_over_root_deny) 1534 { 1535 const struct rule rules[] = { 1536 { 1537 .path = "/", 1538 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1539 }, 1540 {}, 1541 }; 1542 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1543 1544 ASSERT_LE(0, ruleset_fd); 1545 enforce_ruleset(_metadata, ruleset_fd); 1546 ASSERT_EQ(0, close(ruleset_fd)); 1547 1548 /* Checks denied access (on a directory). */ 1549 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1550 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1551 } 1552 1553 TEST_F_FORK(layout1, rule_inside_mount_ns) 1554 { 1555 const struct rule rules[] = { 1556 { 1557 .path = "s3d3", 1558 .access = ACCESS_RO, 1559 }, 1560 {}, 1561 }; 1562 int ruleset_fd; 1563 1564 set_cap(_metadata, CAP_SYS_ADMIN); 1565 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1566 { 1567 TH_LOG("Failed to pivot root: %s", strerror(errno)); 1568 }; 1569 ASSERT_EQ(0, chdir("/")); 1570 clear_cap(_metadata, CAP_SYS_ADMIN); 1571 1572 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1573 ASSERT_LE(0, ruleset_fd); 1574 enforce_ruleset(_metadata, ruleset_fd); 1575 ASSERT_EQ(0, close(ruleset_fd)); 1576 1577 ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1578 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1579 } 1580 1581 TEST_F_FORK(layout1, mount_and_pivot) 1582 { 1583 const struct rule rules[] = { 1584 { 1585 .path = dir_s3d2, 1586 .access = ACCESS_RO, 1587 }, 1588 {}, 1589 }; 1590 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1591 1592 ASSERT_LE(0, ruleset_fd); 1593 enforce_ruleset(_metadata, ruleset_fd); 1594 ASSERT_EQ(0, close(ruleset_fd)); 1595 1596 set_cap(_metadata, CAP_SYS_ADMIN); 1597 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1598 ASSERT_EQ(EPERM, errno); 1599 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1600 ASSERT_EQ(EPERM, errno); 1601 clear_cap(_metadata, CAP_SYS_ADMIN); 1602 } 1603 1604 TEST_F_FORK(layout1, move_mount) 1605 { 1606 const struct rule rules[] = { 1607 { 1608 .path = dir_s3d2, 1609 .access = ACCESS_RO, 1610 }, 1611 {}, 1612 }; 1613 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1614 1615 ASSERT_LE(0, ruleset_fd); 1616 1617 set_cap(_metadata, CAP_SYS_ADMIN); 1618 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1619 dir_s1d2, 0)) 1620 { 1621 TH_LOG("Failed to move mount: %s", strerror(errno)); 1622 } 1623 1624 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1625 dir_s3d2, 0)); 1626 clear_cap(_metadata, CAP_SYS_ADMIN); 1627 1628 enforce_ruleset(_metadata, ruleset_fd); 1629 ASSERT_EQ(0, close(ruleset_fd)); 1630 1631 set_cap(_metadata, CAP_SYS_ADMIN); 1632 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1633 dir_s1d2, 0)); 1634 ASSERT_EQ(EPERM, errno); 1635 clear_cap(_metadata, CAP_SYS_ADMIN); 1636 } 1637 1638 TEST_F_FORK(layout1, release_inodes) 1639 { 1640 const struct rule rules[] = { 1641 { 1642 .path = dir_s1d1, 1643 .access = ACCESS_RO, 1644 }, 1645 { 1646 .path = dir_s3d2, 1647 .access = ACCESS_RO, 1648 }, 1649 { 1650 .path = dir_s3d3, 1651 .access = ACCESS_RO, 1652 }, 1653 {}, 1654 }; 1655 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1656 1657 ASSERT_LE(0, ruleset_fd); 1658 /* Unmount a file hierarchy while it is being used by a ruleset. */ 1659 set_cap(_metadata, CAP_SYS_ADMIN); 1660 ASSERT_EQ(0, umount(dir_s3d2)); 1661 clear_cap(_metadata, CAP_SYS_ADMIN); 1662 1663 enforce_ruleset(_metadata, ruleset_fd); 1664 ASSERT_EQ(0, close(ruleset_fd)); 1665 1666 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1667 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1668 /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1669 ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1670 } 1671 1672 enum relative_access { 1673 REL_OPEN, 1674 REL_CHDIR, 1675 REL_CHROOT_ONLY, 1676 REL_CHROOT_CHDIR, 1677 }; 1678 1679 static void test_relative_path(struct __test_metadata *const _metadata, 1680 const enum relative_access rel) 1681 { 1682 /* 1683 * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1684 * is not a disconnected root directory). 1685 */ 1686 const struct rule layer1_base[] = { 1687 { 1688 .path = TMP_DIR, 1689 .access = ACCESS_RO, 1690 }, 1691 {}, 1692 }; 1693 const struct rule layer2_subs[] = { 1694 { 1695 .path = dir_s1d2, 1696 .access = ACCESS_RO, 1697 }, 1698 { 1699 .path = dir_s2d2, 1700 .access = ACCESS_RO, 1701 }, 1702 {}, 1703 }; 1704 int dirfd, ruleset_fd; 1705 1706 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1707 ASSERT_LE(0, ruleset_fd); 1708 enforce_ruleset(_metadata, ruleset_fd); 1709 ASSERT_EQ(0, close(ruleset_fd)); 1710 1711 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1712 1713 ASSERT_LE(0, ruleset_fd); 1714 switch (rel) { 1715 case REL_OPEN: 1716 case REL_CHDIR: 1717 break; 1718 case REL_CHROOT_ONLY: 1719 ASSERT_EQ(0, chdir(dir_s2d2)); 1720 break; 1721 case REL_CHROOT_CHDIR: 1722 ASSERT_EQ(0, chdir(dir_s1d2)); 1723 break; 1724 default: 1725 ASSERT_TRUE(false); 1726 return; 1727 } 1728 1729 set_cap(_metadata, CAP_SYS_CHROOT); 1730 enforce_ruleset(_metadata, ruleset_fd); 1731 1732 switch (rel) { 1733 case REL_OPEN: 1734 dirfd = open(dir_s1d2, O_DIRECTORY); 1735 ASSERT_LE(0, dirfd); 1736 break; 1737 case REL_CHDIR: 1738 ASSERT_EQ(0, chdir(dir_s1d2)); 1739 dirfd = AT_FDCWD; 1740 break; 1741 case REL_CHROOT_ONLY: 1742 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1743 ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 1744 { 1745 TH_LOG("Failed to chroot: %s", strerror(errno)); 1746 } 1747 dirfd = AT_FDCWD; 1748 break; 1749 case REL_CHROOT_CHDIR: 1750 /* Do chroot into dir_s1d2. */ 1751 ASSERT_EQ(0, chroot(".")) 1752 { 1753 TH_LOG("Failed to chroot: %s", strerror(errno)); 1754 } 1755 dirfd = AT_FDCWD; 1756 break; 1757 } 1758 1759 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1760 test_open_rel(dirfd, "..", O_RDONLY)); 1761 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1762 1763 if (rel == REL_CHROOT_ONLY) { 1764 /* The current directory is dir_s2d2. */ 1765 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1766 } else { 1767 /* The current directory is dir_s1d2. */ 1768 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1769 } 1770 1771 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1772 /* Checks the root dir_s1d2. */ 1773 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1774 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1775 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1776 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1777 } 1778 1779 if (rel != REL_CHROOT_CHDIR) { 1780 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1781 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1782 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1783 O_RDONLY)); 1784 1785 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1786 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1787 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1788 O_RDONLY)); 1789 } 1790 1791 if (rel == REL_OPEN) 1792 ASSERT_EQ(0, close(dirfd)); 1793 ASSERT_EQ(0, close(ruleset_fd)); 1794 } 1795 1796 TEST_F_FORK(layout1, relative_open) 1797 { 1798 test_relative_path(_metadata, REL_OPEN); 1799 } 1800 1801 TEST_F_FORK(layout1, relative_chdir) 1802 { 1803 test_relative_path(_metadata, REL_CHDIR); 1804 } 1805 1806 TEST_F_FORK(layout1, relative_chroot_only) 1807 { 1808 test_relative_path(_metadata, REL_CHROOT_ONLY); 1809 } 1810 1811 TEST_F_FORK(layout1, relative_chroot_chdir) 1812 { 1813 test_relative_path(_metadata, REL_CHROOT_CHDIR); 1814 } 1815 1816 static void copy_binary(struct __test_metadata *const _metadata, 1817 const char *const dst_path) 1818 { 1819 int dst_fd, src_fd; 1820 struct stat statbuf; 1821 1822 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1823 ASSERT_LE(0, dst_fd) 1824 { 1825 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1826 } 1827 src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1828 ASSERT_LE(0, src_fd) 1829 { 1830 TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1831 strerror(errno)); 1832 } 1833 ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1834 ASSERT_EQ(statbuf.st_size, 1835 sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 1836 ASSERT_EQ(0, close(src_fd)); 1837 ASSERT_EQ(0, close(dst_fd)); 1838 } 1839 1840 static void test_execute(struct __test_metadata *const _metadata, const int err, 1841 const char *const path) 1842 { 1843 int status; 1844 char *const argv[] = { (char *)path, NULL }; 1845 const pid_t child = fork(); 1846 1847 ASSERT_LE(0, child); 1848 if (child == 0) { 1849 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 1850 { 1851 TH_LOG("Failed to execute \"%s\": %s", path, 1852 strerror(errno)); 1853 }; 1854 ASSERT_EQ(err, errno); 1855 _exit(_metadata->passed ? 2 : 1); 1856 return; 1857 } 1858 ASSERT_EQ(child, waitpid(child, &status, 0)); 1859 ASSERT_EQ(1, WIFEXITED(status)); 1860 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 1861 { 1862 TH_LOG("Unexpected return code for \"%s\": %s", path, 1863 strerror(errno)); 1864 }; 1865 } 1866 1867 TEST_F_FORK(layout1, execute) 1868 { 1869 const struct rule rules[] = { 1870 { 1871 .path = dir_s1d2, 1872 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1873 }, 1874 {}, 1875 }; 1876 const int ruleset_fd = 1877 create_ruleset(_metadata, rules[0].access, rules); 1878 1879 ASSERT_LE(0, ruleset_fd); 1880 copy_binary(_metadata, file1_s1d1); 1881 copy_binary(_metadata, file1_s1d2); 1882 copy_binary(_metadata, file1_s1d3); 1883 1884 enforce_ruleset(_metadata, ruleset_fd); 1885 ASSERT_EQ(0, close(ruleset_fd)); 1886 1887 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1888 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1889 test_execute(_metadata, EACCES, file1_s1d1); 1890 1891 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1892 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1893 test_execute(_metadata, 0, file1_s1d2); 1894 1895 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1896 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1897 test_execute(_metadata, 0, file1_s1d3); 1898 } 1899 1900 TEST_F_FORK(layout1, link) 1901 { 1902 const struct rule layer1[] = { 1903 { 1904 .path = dir_s1d2, 1905 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1906 }, 1907 {}, 1908 }; 1909 const struct rule layer2[] = { 1910 { 1911 .path = dir_s1d3, 1912 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1913 }, 1914 {}, 1915 }; 1916 int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 1917 1918 ASSERT_LE(0, ruleset_fd); 1919 1920 ASSERT_EQ(0, unlink(file1_s1d1)); 1921 ASSERT_EQ(0, unlink(file1_s1d2)); 1922 ASSERT_EQ(0, unlink(file1_s1d3)); 1923 1924 enforce_ruleset(_metadata, ruleset_fd); 1925 ASSERT_EQ(0, close(ruleset_fd)); 1926 1927 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1928 ASSERT_EQ(EACCES, errno); 1929 1930 /* Denies linking because of reparenting. */ 1931 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1932 ASSERT_EQ(EXDEV, errno); 1933 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1934 ASSERT_EQ(EXDEV, errno); 1935 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 1936 ASSERT_EQ(EXDEV, errno); 1937 1938 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1939 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 1940 1941 /* Prepares for next unlinks. */ 1942 ASSERT_EQ(0, unlink(file2_s1d2)); 1943 ASSERT_EQ(0, unlink(file2_s1d3)); 1944 1945 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 1946 ASSERT_LE(0, ruleset_fd); 1947 enforce_ruleset(_metadata, ruleset_fd); 1948 ASSERT_EQ(0, close(ruleset_fd)); 1949 1950 /* Checks that linkind doesn't require the ability to delete a file. */ 1951 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 1952 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1953 } 1954 1955 static int test_rename(const char *const oldpath, const char *const newpath) 1956 { 1957 if (rename(oldpath, newpath)) 1958 return errno; 1959 return 0; 1960 } 1961 1962 static int test_exchange(const char *const oldpath, const char *const newpath) 1963 { 1964 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 1965 return errno; 1966 return 0; 1967 } 1968 1969 TEST_F_FORK(layout1, rename_file) 1970 { 1971 const struct rule rules[] = { 1972 { 1973 .path = dir_s1d3, 1974 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1975 }, 1976 { 1977 .path = dir_s2d2, 1978 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1979 }, 1980 {}, 1981 }; 1982 const int ruleset_fd = 1983 create_ruleset(_metadata, rules[0].access, rules); 1984 1985 ASSERT_LE(0, ruleset_fd); 1986 1987 ASSERT_EQ(0, unlink(file1_s1d2)); 1988 1989 enforce_ruleset(_metadata, ruleset_fd); 1990 ASSERT_EQ(0, close(ruleset_fd)); 1991 1992 /* 1993 * Tries to replace a file, from a directory that allows file removal, 1994 * but to a different directory (which also allows file removal). 1995 */ 1996 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1997 ASSERT_EQ(EXDEV, errno); 1998 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 1999 RENAME_EXCHANGE)); 2000 ASSERT_EQ(EXDEV, errno); 2001 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2002 RENAME_EXCHANGE)); 2003 ASSERT_EQ(EXDEV, errno); 2004 2005 /* 2006 * Tries to replace a file, from a directory that denies file removal, 2007 * to a different directory (which allows file removal). 2008 */ 2009 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2010 ASSERT_EQ(EACCES, errno); 2011 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 2012 RENAME_EXCHANGE)); 2013 ASSERT_EQ(EACCES, errno); 2014 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 2015 RENAME_EXCHANGE)); 2016 ASSERT_EQ(EXDEV, errno); 2017 2018 /* Exchanges files and directories that partially allow removal. */ 2019 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 2020 RENAME_EXCHANGE)); 2021 ASSERT_EQ(EACCES, errno); 2022 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 2023 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 2024 ASSERT_EQ(EACCES, errno); 2025 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 2026 RENAME_EXCHANGE)); 2027 ASSERT_EQ(EACCES, errno); 2028 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 2029 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2030 ASSERT_EQ(EACCES, errno); 2031 2032 /* Renames files with different parents. */ 2033 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2034 ASSERT_EQ(EXDEV, errno); 2035 ASSERT_EQ(0, unlink(file1_s1d3)); 2036 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2037 ASSERT_EQ(EACCES, errno); 2038 2039 /* Exchanges and renames files with same parent. */ 2040 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 2041 RENAME_EXCHANGE)); 2042 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 2043 2044 /* Exchanges files and directories with same parent, twice. */ 2045 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2046 RENAME_EXCHANGE)); 2047 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2048 RENAME_EXCHANGE)); 2049 } 2050 2051 TEST_F_FORK(layout1, rename_dir) 2052 { 2053 const struct rule rules[] = { 2054 { 2055 .path = dir_s1d2, 2056 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2057 }, 2058 { 2059 .path = dir_s2d1, 2060 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2061 }, 2062 {}, 2063 }; 2064 const int ruleset_fd = 2065 create_ruleset(_metadata, rules[0].access, rules); 2066 2067 ASSERT_LE(0, ruleset_fd); 2068 2069 /* Empties dir_s1d3 to allow renaming. */ 2070 ASSERT_EQ(0, unlink(file1_s1d3)); 2071 ASSERT_EQ(0, unlink(file2_s1d3)); 2072 2073 enforce_ruleset(_metadata, ruleset_fd); 2074 ASSERT_EQ(0, close(ruleset_fd)); 2075 2076 /* Exchanges and renames directory to a different parent. */ 2077 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2078 RENAME_EXCHANGE)); 2079 ASSERT_EQ(EXDEV, errno); 2080 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 2081 ASSERT_EQ(EXDEV, errno); 2082 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2083 RENAME_EXCHANGE)); 2084 ASSERT_EQ(EXDEV, errno); 2085 2086 /* 2087 * Exchanges directory to the same parent, which doesn't allow 2088 * directory removal. 2089 */ 2090 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 2091 RENAME_EXCHANGE)); 2092 ASSERT_EQ(EACCES, errno); 2093 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 2094 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 2095 ASSERT_EQ(EACCES, errno); 2096 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 2097 RENAME_EXCHANGE)); 2098 ASSERT_EQ(EACCES, errno); 2099 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 2100 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2101 ASSERT_EQ(EACCES, errno); 2102 2103 /* 2104 * Exchanges and renames directory to the same parent, which allows 2105 * directory removal. 2106 */ 2107 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 2108 RENAME_EXCHANGE)); 2109 ASSERT_EQ(0, unlink(dir_s1d3)); 2110 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2111 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 2112 ASSERT_EQ(0, rmdir(dir_s1d3)); 2113 } 2114 2115 TEST_F_FORK(layout1, reparent_refer) 2116 { 2117 const struct rule layer1[] = { 2118 { 2119 .path = dir_s1d2, 2120 .access = LANDLOCK_ACCESS_FS_REFER, 2121 }, 2122 { 2123 .path = dir_s2d2, 2124 .access = LANDLOCK_ACCESS_FS_REFER, 2125 }, 2126 {}, 2127 }; 2128 int ruleset_fd = 2129 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2130 2131 ASSERT_LE(0, ruleset_fd); 2132 enforce_ruleset(_metadata, ruleset_fd); 2133 ASSERT_EQ(0, close(ruleset_fd)); 2134 2135 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2136 ASSERT_EQ(EXDEV, errno); 2137 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2138 ASSERT_EQ(EXDEV, errno); 2139 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2140 ASSERT_EQ(EXDEV, errno); 2141 2142 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2143 ASSERT_EQ(EXDEV, errno); 2144 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2145 ASSERT_EQ(EXDEV, errno); 2146 /* 2147 * Moving should only be allowed when the source and the destination 2148 * parent directory have REFER. 2149 */ 2150 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2151 ASSERT_EQ(ENOTEMPTY, errno); 2152 ASSERT_EQ(0, unlink(file1_s2d3)); 2153 ASSERT_EQ(0, unlink(file2_s2d3)); 2154 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2155 } 2156 2157 /* Checks renames beneath dir_s1d1. */ 2158 static void refer_denied_by_default(struct __test_metadata *const _metadata, 2159 const struct rule layer1[], 2160 const int layer1_err, 2161 const struct rule layer2[]) 2162 { 2163 int ruleset_fd; 2164 2165 ASSERT_EQ(0, unlink(file1_s1d2)); 2166 2167 ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 2168 ASSERT_LE(0, ruleset_fd); 2169 enforce_ruleset(_metadata, ruleset_fd); 2170 ASSERT_EQ(0, close(ruleset_fd)); 2171 2172 /* 2173 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 2174 * layer1_err), then it allows some different-parent renames and links. 2175 */ 2176 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 2177 if (layer1_err == 0) 2178 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 2179 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 2180 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 2181 2182 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 2183 ASSERT_LE(0, ruleset_fd); 2184 enforce_ruleset(_metadata, ruleset_fd); 2185 ASSERT_EQ(0, close(ruleset_fd)); 2186 2187 /* 2188 * Now, either the first or the second layer does not handle 2189 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 2190 * renames and links are denied, thus making the layer handling 2191 * LANDLOCK_ACCESS_FS_REFER null and void. 2192 */ 2193 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 2194 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 2195 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 2196 } 2197 2198 const struct rule layer_dir_s1d1_refer[] = { 2199 { 2200 .path = dir_s1d1, 2201 .access = LANDLOCK_ACCESS_FS_REFER, 2202 }, 2203 {}, 2204 }; 2205 2206 const struct rule layer_dir_s1d1_execute[] = { 2207 { 2208 /* Matches a parent directory. */ 2209 .path = dir_s1d1, 2210 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2211 }, 2212 {}, 2213 }; 2214 2215 const struct rule layer_dir_s2d1_execute[] = { 2216 { 2217 /* Does not match a parent directory. */ 2218 .path = dir_s2d1, 2219 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2220 }, 2221 {}, 2222 }; 2223 2224 /* 2225 * Tests precedence over renames: denied by default for different parent 2226 * directories, *with* a rule matching a parent directory, but not directly 2227 * denying access (with MAKE_REG nor REMOVE). 2228 */ 2229 TEST_F_FORK(layout1, refer_denied_by_default1) 2230 { 2231 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2232 layer_dir_s1d1_execute); 2233 } 2234 2235 /* 2236 * Same test but this time turning around the ABI version order: the first 2237 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2238 */ 2239 TEST_F_FORK(layout1, refer_denied_by_default2) 2240 { 2241 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 2242 layer_dir_s1d1_refer); 2243 } 2244 2245 /* 2246 * Tests precedence over renames: denied by default for different parent 2247 * directories, *without* a rule matching a parent directory, but not directly 2248 * denying access (with MAKE_REG nor REMOVE). 2249 */ 2250 TEST_F_FORK(layout1, refer_denied_by_default3) 2251 { 2252 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2253 layer_dir_s2d1_execute); 2254 } 2255 2256 /* 2257 * Same test but this time turning around the ABI version order: the first 2258 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2259 */ 2260 TEST_F_FORK(layout1, refer_denied_by_default4) 2261 { 2262 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 2263 layer_dir_s1d1_refer); 2264 } 2265 2266 TEST_F_FORK(layout1, reparent_link) 2267 { 2268 const struct rule layer1[] = { 2269 { 2270 .path = dir_s1d2, 2271 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2272 }, 2273 { 2274 .path = dir_s1d3, 2275 .access = LANDLOCK_ACCESS_FS_REFER, 2276 }, 2277 { 2278 .path = dir_s2d2, 2279 .access = LANDLOCK_ACCESS_FS_REFER, 2280 }, 2281 { 2282 .path = dir_s2d3, 2283 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2284 }, 2285 {}, 2286 }; 2287 const int ruleset_fd = create_ruleset( 2288 _metadata, 2289 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2290 2291 ASSERT_LE(0, ruleset_fd); 2292 enforce_ruleset(_metadata, ruleset_fd); 2293 ASSERT_EQ(0, close(ruleset_fd)); 2294 2295 ASSERT_EQ(0, unlink(file1_s1d1)); 2296 ASSERT_EQ(0, unlink(file1_s1d2)); 2297 ASSERT_EQ(0, unlink(file1_s1d3)); 2298 2299 /* Denies linking because of missing MAKE_REG. */ 2300 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2301 ASSERT_EQ(EACCES, errno); 2302 /* Denies linking because of missing source and destination REFER. */ 2303 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2304 ASSERT_EQ(EXDEV, errno); 2305 /* Denies linking because of missing source REFER. */ 2306 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2307 ASSERT_EQ(EXDEV, errno); 2308 2309 /* Denies linking because of missing MAKE_REG. */ 2310 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2311 ASSERT_EQ(EACCES, errno); 2312 /* Denies linking because of missing destination REFER. */ 2313 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2314 ASSERT_EQ(EXDEV, errno); 2315 2316 /* Allows linking because of REFER and MAKE_REG. */ 2317 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2318 ASSERT_EQ(0, unlink(file1_s2d2)); 2319 /* Reverse linking denied because of missing MAKE_REG. */ 2320 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2321 ASSERT_EQ(EACCES, errno); 2322 ASSERT_EQ(0, unlink(file1_s2d3)); 2323 /* Checks reverse linking. */ 2324 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2325 ASSERT_EQ(0, unlink(file1_s1d3)); 2326 2327 /* 2328 * This is OK for a file link, but it should not be allowed for a 2329 * directory rename (because of the superset of access rights. 2330 */ 2331 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2332 ASSERT_EQ(0, unlink(file1_s1d3)); 2333 2334 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2335 ASSERT_EQ(EXDEV, errno); 2336 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2337 ASSERT_EQ(EXDEV, errno); 2338 2339 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2340 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2341 } 2342 2343 TEST_F_FORK(layout1, reparent_rename) 2344 { 2345 /* Same rules as for reparent_link. */ 2346 const struct rule layer1[] = { 2347 { 2348 .path = dir_s1d2, 2349 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2350 }, 2351 { 2352 .path = dir_s1d3, 2353 .access = LANDLOCK_ACCESS_FS_REFER, 2354 }, 2355 { 2356 .path = dir_s2d2, 2357 .access = LANDLOCK_ACCESS_FS_REFER, 2358 }, 2359 { 2360 .path = dir_s2d3, 2361 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2362 }, 2363 {}, 2364 }; 2365 const int ruleset_fd = create_ruleset( 2366 _metadata, 2367 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2368 2369 ASSERT_LE(0, ruleset_fd); 2370 enforce_ruleset(_metadata, ruleset_fd); 2371 ASSERT_EQ(0, close(ruleset_fd)); 2372 2373 ASSERT_EQ(0, unlink(file1_s1d2)); 2374 ASSERT_EQ(0, unlink(file1_s1d3)); 2375 2376 /* Denies renaming because of missing MAKE_REG. */ 2377 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2378 RENAME_EXCHANGE)); 2379 ASSERT_EQ(EACCES, errno); 2380 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2381 RENAME_EXCHANGE)); 2382 ASSERT_EQ(EACCES, errno); 2383 ASSERT_EQ(0, unlink(file1_s1d1)); 2384 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2385 ASSERT_EQ(EACCES, errno); 2386 /* Even denies same file exchange. */ 2387 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2388 RENAME_EXCHANGE)); 2389 ASSERT_EQ(EACCES, errno); 2390 2391 /* Denies renaming because of missing source and destination REFER. */ 2392 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2393 ASSERT_EQ(EXDEV, errno); 2394 /* 2395 * Denies renaming because of missing MAKE_REG, source and destination 2396 * REFER. 2397 */ 2398 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2399 RENAME_EXCHANGE)); 2400 ASSERT_EQ(EACCES, errno); 2401 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2402 RENAME_EXCHANGE)); 2403 ASSERT_EQ(EACCES, errno); 2404 2405 /* Denies renaming because of missing source REFER. */ 2406 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2407 ASSERT_EQ(EXDEV, errno); 2408 /* Denies renaming because of missing MAKE_REG. */ 2409 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2410 RENAME_EXCHANGE)); 2411 ASSERT_EQ(EACCES, errno); 2412 2413 /* Denies renaming because of missing MAKE_REG. */ 2414 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2415 ASSERT_EQ(EACCES, errno); 2416 /* Denies renaming because of missing destination REFER*/ 2417 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2418 ASSERT_EQ(EXDEV, errno); 2419 2420 /* Denies exchange because of one missing MAKE_REG. */ 2421 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2422 RENAME_EXCHANGE)); 2423 ASSERT_EQ(EACCES, errno); 2424 /* Allows renaming because of REFER and MAKE_REG. */ 2425 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2426 2427 /* Reverse renaming denied because of missing MAKE_REG. */ 2428 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2429 ASSERT_EQ(EACCES, errno); 2430 ASSERT_EQ(0, unlink(file1_s2d3)); 2431 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2432 2433 /* Tests reverse renaming. */ 2434 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2435 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2436 RENAME_EXCHANGE)); 2437 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2438 2439 /* 2440 * This is OK for a file rename, but it should not be allowed for a 2441 * directory rename (because of the superset of access rights). 2442 */ 2443 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2444 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2445 2446 /* 2447 * Tests superset restrictions applied to directories. Not only the 2448 * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2449 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2450 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2451 * directly by the moved dir_s2d3. 2452 */ 2453 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2454 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2455 /* 2456 * The first rename is allowed but not the exchange because dir_s1d3's 2457 * parent (dir_s1d2) doesn't have REFER. 2458 */ 2459 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2460 RENAME_EXCHANGE)); 2461 ASSERT_EQ(EXDEV, errno); 2462 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2463 RENAME_EXCHANGE)); 2464 ASSERT_EQ(EXDEV, errno); 2465 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2466 ASSERT_EQ(EXDEV, errno); 2467 2468 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2469 ASSERT_EQ(EXDEV, errno); 2470 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2471 ASSERT_EQ(EXDEV, errno); 2472 2473 /* Renaming in the same directory is always allowed. */ 2474 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2475 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2476 2477 ASSERT_EQ(0, unlink(file1_s1d2)); 2478 /* Denies because of missing source MAKE_REG and destination REFER. */ 2479 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2480 ASSERT_EQ(EXDEV, errno); 2481 2482 ASSERT_EQ(0, unlink(file1_s1d3)); 2483 /* Denies because of missing source MAKE_REG and REFER. */ 2484 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2485 ASSERT_EQ(EXDEV, errno); 2486 } 2487 2488 static void 2489 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2490 { 2491 const struct rule layer1[] = { 2492 { 2493 .path = dir_s1d2, 2494 .access = LANDLOCK_ACCESS_FS_REFER, 2495 }, 2496 { 2497 /* Interesting for the layer2 tests. */ 2498 .path = dir_s1d3, 2499 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2500 }, 2501 { 2502 .path = dir_s2d2, 2503 .access = LANDLOCK_ACCESS_FS_REFER, 2504 }, 2505 { 2506 .path = dir_s2d3, 2507 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2508 }, 2509 {}, 2510 }; 2511 const int ruleset_fd = create_ruleset( 2512 _metadata, 2513 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2514 2515 ASSERT_LE(0, ruleset_fd); 2516 enforce_ruleset(_metadata, ruleset_fd); 2517 ASSERT_EQ(0, close(ruleset_fd)); 2518 } 2519 2520 static void 2521 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2522 { 2523 const struct rule layer2[] = { 2524 { 2525 .path = dir_s2d3, 2526 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2527 }, 2528 {}, 2529 }; 2530 /* 2531 * Same checks as before but with a second layer and a new MAKE_DIR 2532 * rule (and no explicit handling of REFER). 2533 */ 2534 const int ruleset_fd = 2535 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2536 2537 ASSERT_LE(0, ruleset_fd); 2538 enforce_ruleset(_metadata, ruleset_fd); 2539 ASSERT_EQ(0, close(ruleset_fd)); 2540 } 2541 2542 TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2543 { 2544 ASSERT_EQ(0, unlink(file1_s2d2)); 2545 ASSERT_EQ(0, unlink(file1_s2d3)); 2546 2547 reparent_exdev_layers_enforce1(_metadata); 2548 2549 /* 2550 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2551 * because it doesn't inherit new access rights. 2552 */ 2553 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2554 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2555 2556 /* 2557 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2558 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2559 * already allowed for dir_s1d3. 2560 */ 2561 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2562 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2563 2564 /* 2565 * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2566 * because it cannot inherit MAKE_REG right (which is dedicated to 2567 * directories). 2568 */ 2569 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2570 2571 reparent_exdev_layers_enforce2(_metadata); 2572 2573 /* 2574 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2575 * MAKE_DIR is not tied to dir_s2d2. 2576 */ 2577 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2578 ASSERT_EQ(EACCES, errno); 2579 2580 /* 2581 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2582 * would grants MAKE_REG and MAKE_DIR rights to it. 2583 */ 2584 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2585 ASSERT_EQ(EXDEV, errno); 2586 2587 /* 2588 * Moving the file2_s1d3 file below dir_s2d3 is denied because the 2589 * second layer does not handle REFER, which is always denied by 2590 * default. 2591 */ 2592 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 2593 ASSERT_EQ(EXDEV, errno); 2594 } 2595 2596 TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2597 { 2598 reparent_exdev_layers_enforce1(_metadata); 2599 2600 /* Checks EACCES predominance over EXDEV. */ 2601 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2602 ASSERT_EQ(EACCES, errno); 2603 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2604 ASSERT_EQ(EACCES, errno); 2605 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2606 ASSERT_EQ(EXDEV, errno); 2607 /* Modify layout! */ 2608 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 2609 2610 /* Without REFER source. */ 2611 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2612 ASSERT_EQ(EXDEV, errno); 2613 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2614 ASSERT_EQ(EXDEV, errno); 2615 2616 reparent_exdev_layers_enforce2(_metadata); 2617 2618 /* Checks EACCES predominance over EXDEV. */ 2619 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2620 ASSERT_EQ(EACCES, errno); 2621 /* Checks with actual file2_s1d2. */ 2622 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 2623 ASSERT_EQ(EACCES, errno); 2624 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2625 ASSERT_EQ(EXDEV, errno); 2626 /* 2627 * Modifying the layout is now denied because the second layer does not 2628 * handle REFER, which is always denied by default. 2629 */ 2630 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2631 ASSERT_EQ(EXDEV, errno); 2632 2633 /* Without REFER source, EACCES wins over EXDEV. */ 2634 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2635 ASSERT_EQ(EACCES, errno); 2636 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2637 ASSERT_EQ(EACCES, errno); 2638 } 2639 2640 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 2641 { 2642 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 2643 file2_s2d3; 2644 2645 ASSERT_EQ(0, unlink(file1_s1d2)); 2646 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2647 ASSERT_EQ(0, unlink(file2_s2d3)); 2648 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2649 2650 reparent_exdev_layers_enforce1(_metadata); 2651 2652 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2653 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2654 RENAME_EXCHANGE)); 2655 ASSERT_EQ(EACCES, errno); 2656 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2657 RENAME_EXCHANGE)); 2658 ASSERT_EQ(EACCES, errno); 2659 2660 /* 2661 * Checks with directories which creation could be allowed, but denied 2662 * because of access rights that would be inherited. 2663 */ 2664 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2665 dir_file2_s2d3, RENAME_EXCHANGE)); 2666 ASSERT_EQ(EXDEV, errno); 2667 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2668 dir_file1_s1d2, RENAME_EXCHANGE)); 2669 ASSERT_EQ(EXDEV, errno); 2670 2671 /* Checks with same access rights. */ 2672 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2673 RENAME_EXCHANGE)); 2674 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2675 RENAME_EXCHANGE)); 2676 2677 /* Checks with different (child-only) access rights. */ 2678 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2679 RENAME_EXCHANGE)); 2680 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2681 RENAME_EXCHANGE)); 2682 2683 /* 2684 * Checks that exchange between file and directory are consistent. 2685 * 2686 * Moving a file (file1_s2d2) to a directory which only grants more 2687 * directory-related access rights is allowed, and at the same time 2688 * moving a directory (dir_file2_s2d3) to another directory which 2689 * grants less access rights is allowed too. 2690 * 2691 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 2692 */ 2693 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2694 RENAME_EXCHANGE)); 2695 /* 2696 * However, moving back the directory is denied because it would get 2697 * more access rights than the current state and because file creation 2698 * is forbidden (in dir_s2d2). 2699 */ 2700 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2701 RENAME_EXCHANGE)); 2702 ASSERT_EQ(EACCES, errno); 2703 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2704 RENAME_EXCHANGE)); 2705 ASSERT_EQ(EACCES, errno); 2706 2707 reparent_exdev_layers_enforce2(_metadata); 2708 2709 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2710 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2711 RENAME_EXCHANGE)); 2712 ASSERT_EQ(EACCES, errno); 2713 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2714 RENAME_EXCHANGE)); 2715 ASSERT_EQ(EACCES, errno); 2716 2717 /* Checks with directories which creation is now denied. */ 2718 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2719 dir_file2_s2d3, RENAME_EXCHANGE)); 2720 ASSERT_EQ(EACCES, errno); 2721 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2722 dir_file1_s1d2, RENAME_EXCHANGE)); 2723 ASSERT_EQ(EACCES, errno); 2724 2725 /* Checks with different (child-only) access rights. */ 2726 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2727 RENAME_EXCHANGE)); 2728 /* Denied because of MAKE_DIR. */ 2729 ASSERT_EQ(EACCES, errno); 2730 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2731 RENAME_EXCHANGE)); 2732 ASSERT_EQ(EACCES, errno); 2733 2734 /* Checks with different (child-only) access rights. */ 2735 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2736 RENAME_EXCHANGE)); 2737 /* Denied because of MAKE_DIR. */ 2738 ASSERT_EQ(EACCES, errno); 2739 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2740 RENAME_EXCHANGE)); 2741 ASSERT_EQ(EACCES, errno); 2742 2743 /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 2744 } 2745 2746 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 2747 { 2748 const char *const dir_file2_s2d3 = file2_s2d3; 2749 2750 ASSERT_EQ(0, unlink(file2_s2d3)); 2751 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2752 2753 reparent_exdev_layers_enforce1(_metadata); 2754 reparent_exdev_layers_enforce2(_metadata); 2755 2756 /* Checks that exchange between file and directory are consistent. */ 2757 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2758 RENAME_EXCHANGE)); 2759 ASSERT_EQ(EACCES, errno); 2760 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2761 RENAME_EXCHANGE)); 2762 ASSERT_EQ(EACCES, errno); 2763 } 2764 2765 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 2766 { 2767 const char *const dir_file2_s2d3 = file2_s2d3; 2768 2769 ASSERT_EQ(0, unlink(file2_s2d3)); 2770 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2771 2772 reparent_exdev_layers_enforce1(_metadata); 2773 2774 /* 2775 * Checks that exchange between file and directory are consistent, 2776 * including with inverted arguments (see 2777 * layout1.reparent_exdev_layers_exchange1). 2778 */ 2779 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2780 RENAME_EXCHANGE)); 2781 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2782 RENAME_EXCHANGE)); 2783 ASSERT_EQ(EACCES, errno); 2784 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2785 RENAME_EXCHANGE)); 2786 ASSERT_EQ(EACCES, errno); 2787 } 2788 2789 TEST_F_FORK(layout1, reparent_remove) 2790 { 2791 const struct rule layer1[] = { 2792 { 2793 .path = dir_s1d1, 2794 .access = LANDLOCK_ACCESS_FS_REFER | 2795 LANDLOCK_ACCESS_FS_REMOVE_DIR, 2796 }, 2797 { 2798 .path = dir_s1d2, 2799 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2800 }, 2801 { 2802 .path = dir_s2d1, 2803 .access = LANDLOCK_ACCESS_FS_REFER | 2804 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2805 }, 2806 {}, 2807 }; 2808 const int ruleset_fd = create_ruleset( 2809 _metadata, 2810 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 2811 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2812 layer1); 2813 2814 ASSERT_LE(0, ruleset_fd); 2815 enforce_ruleset(_metadata, ruleset_fd); 2816 ASSERT_EQ(0, close(ruleset_fd)); 2817 2818 /* Access denied because of wrong/swapped remove file/dir. */ 2819 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 2820 ASSERT_EQ(EACCES, errno); 2821 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 2822 ASSERT_EQ(EACCES, errno); 2823 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 2824 RENAME_EXCHANGE)); 2825 ASSERT_EQ(EACCES, errno); 2826 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 2827 RENAME_EXCHANGE)); 2828 ASSERT_EQ(EACCES, errno); 2829 2830 /* Access allowed thanks to the matching rights. */ 2831 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 2832 ASSERT_EQ(EISDIR, errno); 2833 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 2834 ASSERT_EQ(ENOTDIR, errno); 2835 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2836 ASSERT_EQ(ENOTDIR, errno); 2837 ASSERT_EQ(0, unlink(file1_s2d1)); 2838 ASSERT_EQ(0, unlink(file1_s1d3)); 2839 ASSERT_EQ(0, unlink(file2_s1d3)); 2840 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 2841 2842 /* Effectively removes a file and a directory by exchanging them. */ 2843 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2844 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2845 RENAME_EXCHANGE)); 2846 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2847 RENAME_EXCHANGE)); 2848 ASSERT_EQ(EACCES, errno); 2849 } 2850 2851 TEST_F_FORK(layout1, reparent_dom_superset) 2852 { 2853 const struct rule layer1[] = { 2854 { 2855 .path = dir_s1d2, 2856 .access = LANDLOCK_ACCESS_FS_REFER, 2857 }, 2858 { 2859 .path = file1_s1d2, 2860 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2861 }, 2862 { 2863 .path = dir_s1d3, 2864 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 2865 LANDLOCK_ACCESS_FS_EXECUTE, 2866 }, 2867 { 2868 .path = dir_s2d2, 2869 .access = LANDLOCK_ACCESS_FS_REFER | 2870 LANDLOCK_ACCESS_FS_EXECUTE | 2871 LANDLOCK_ACCESS_FS_MAKE_SOCK, 2872 }, 2873 { 2874 .path = dir_s2d3, 2875 .access = LANDLOCK_ACCESS_FS_READ_FILE | 2876 LANDLOCK_ACCESS_FS_MAKE_FIFO, 2877 }, 2878 {}, 2879 }; 2880 int ruleset_fd = create_ruleset(_metadata, 2881 LANDLOCK_ACCESS_FS_REFER | 2882 LANDLOCK_ACCESS_FS_EXECUTE | 2883 LANDLOCK_ACCESS_FS_MAKE_SOCK | 2884 LANDLOCK_ACCESS_FS_READ_FILE | 2885 LANDLOCK_ACCESS_FS_MAKE_FIFO, 2886 layer1); 2887 2888 ASSERT_LE(0, ruleset_fd); 2889 enforce_ruleset(_metadata, ruleset_fd); 2890 ASSERT_EQ(0, close(ruleset_fd)); 2891 2892 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 2893 ASSERT_EQ(EXDEV, errno); 2894 /* 2895 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 2896 * access right. 2897 */ 2898 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 2899 ASSERT_EQ(EXDEV, errno); 2900 /* 2901 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 2902 * superset of access rights compared to dir_s1d2, because file1_s1d2 2903 * already has these access rights anyway. 2904 */ 2905 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 2906 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 2907 2908 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2909 ASSERT_EQ(EXDEV, errno); 2910 /* 2911 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 2912 * right. 2913 */ 2914 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2915 ASSERT_EQ(EXDEV, errno); 2916 /* 2917 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 2918 * of access rights compared to dir_s1d2, because dir_s1d3 already has 2919 * these access rights anyway. 2920 */ 2921 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2922 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2923 2924 /* 2925 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 2926 * will be denied because the new inherited access rights from dir_s1d2 2927 * will be less than the destination (original) dir_s2d3. This is a 2928 * sinkhole scenario where we cannot move back files or directories. 2929 */ 2930 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 2931 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2932 ASSERT_EQ(EXDEV, errno); 2933 ASSERT_EQ(0, unlink(file2_s1d2)); 2934 ASSERT_EQ(0, unlink(file2_s2d3)); 2935 /* 2936 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 2937 * MAKE_SOCK which were inherited from dir_s1d3. 2938 */ 2939 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 2940 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 2941 ASSERT_EQ(EXDEV, errno); 2942 } 2943 2944 TEST_F_FORK(layout1, remove_dir) 2945 { 2946 const struct rule rules[] = { 2947 { 2948 .path = dir_s1d2, 2949 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2950 }, 2951 {}, 2952 }; 2953 const int ruleset_fd = 2954 create_ruleset(_metadata, rules[0].access, rules); 2955 2956 ASSERT_LE(0, ruleset_fd); 2957 2958 ASSERT_EQ(0, unlink(file1_s1d1)); 2959 ASSERT_EQ(0, unlink(file1_s1d2)); 2960 ASSERT_EQ(0, unlink(file1_s1d3)); 2961 ASSERT_EQ(0, unlink(file2_s1d3)); 2962 2963 enforce_ruleset(_metadata, ruleset_fd); 2964 ASSERT_EQ(0, close(ruleset_fd)); 2965 2966 ASSERT_EQ(0, rmdir(dir_s1d3)); 2967 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2968 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 2969 2970 /* dir_s1d2 itself cannot be removed. */ 2971 ASSERT_EQ(-1, rmdir(dir_s1d2)); 2972 ASSERT_EQ(EACCES, errno); 2973 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 2974 ASSERT_EQ(EACCES, errno); 2975 ASSERT_EQ(-1, rmdir(dir_s1d1)); 2976 ASSERT_EQ(EACCES, errno); 2977 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 2978 ASSERT_EQ(EACCES, errno); 2979 } 2980 2981 TEST_F_FORK(layout1, remove_file) 2982 { 2983 const struct rule rules[] = { 2984 { 2985 .path = dir_s1d2, 2986 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2987 }, 2988 {}, 2989 }; 2990 const int ruleset_fd = 2991 create_ruleset(_metadata, rules[0].access, rules); 2992 2993 ASSERT_LE(0, ruleset_fd); 2994 enforce_ruleset(_metadata, ruleset_fd); 2995 ASSERT_EQ(0, close(ruleset_fd)); 2996 2997 ASSERT_EQ(-1, unlink(file1_s1d1)); 2998 ASSERT_EQ(EACCES, errno); 2999 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 3000 ASSERT_EQ(EACCES, errno); 3001 ASSERT_EQ(0, unlink(file1_s1d2)); 3002 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 3003 } 3004 3005 static void test_make_file(struct __test_metadata *const _metadata, 3006 const __u64 access, const mode_t mode, 3007 const dev_t dev) 3008 { 3009 const struct rule rules[] = { 3010 { 3011 .path = dir_s1d2, 3012 .access = access, 3013 }, 3014 {}, 3015 }; 3016 const int ruleset_fd = create_ruleset(_metadata, access, rules); 3017 3018 ASSERT_LE(0, ruleset_fd); 3019 3020 ASSERT_EQ(0, unlink(file1_s1d1)); 3021 ASSERT_EQ(0, unlink(file2_s1d1)); 3022 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 3023 { 3024 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 3025 strerror(errno)); 3026 }; 3027 3028 ASSERT_EQ(0, unlink(file1_s1d2)); 3029 ASSERT_EQ(0, unlink(file2_s1d2)); 3030 3031 ASSERT_EQ(0, unlink(file1_s1d3)); 3032 ASSERT_EQ(0, unlink(file2_s1d3)); 3033 3034 enforce_ruleset(_metadata, ruleset_fd); 3035 ASSERT_EQ(0, close(ruleset_fd)); 3036 3037 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 3038 ASSERT_EQ(EACCES, errno); 3039 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3040 ASSERT_EQ(EACCES, errno); 3041 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3042 ASSERT_EQ(EACCES, errno); 3043 3044 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 3045 { 3046 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 3047 strerror(errno)); 3048 }; 3049 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3050 ASSERT_EQ(0, unlink(file2_s1d2)); 3051 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3052 3053 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 3054 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3055 ASSERT_EQ(0, unlink(file2_s1d3)); 3056 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3057 } 3058 3059 TEST_F_FORK(layout1, make_char) 3060 { 3061 /* Creates a /dev/null device. */ 3062 set_cap(_metadata, CAP_MKNOD); 3063 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 3064 makedev(1, 3)); 3065 } 3066 3067 TEST_F_FORK(layout1, make_block) 3068 { 3069 /* Creates a /dev/loop0 device. */ 3070 set_cap(_metadata, CAP_MKNOD); 3071 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 3072 makedev(7, 0)); 3073 } 3074 3075 TEST_F_FORK(layout1, make_reg_1) 3076 { 3077 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 3078 } 3079 3080 TEST_F_FORK(layout1, make_reg_2) 3081 { 3082 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 3083 } 3084 3085 TEST_F_FORK(layout1, make_sock) 3086 { 3087 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 3088 } 3089 3090 TEST_F_FORK(layout1, make_fifo) 3091 { 3092 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 3093 } 3094 3095 TEST_F_FORK(layout1, make_sym) 3096 { 3097 const struct rule rules[] = { 3098 { 3099 .path = dir_s1d2, 3100 .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 3101 }, 3102 {}, 3103 }; 3104 const int ruleset_fd = 3105 create_ruleset(_metadata, rules[0].access, rules); 3106 3107 ASSERT_LE(0, ruleset_fd); 3108 3109 ASSERT_EQ(0, unlink(file1_s1d1)); 3110 ASSERT_EQ(0, unlink(file2_s1d1)); 3111 ASSERT_EQ(0, symlink("none", file2_s1d1)); 3112 3113 ASSERT_EQ(0, unlink(file1_s1d2)); 3114 ASSERT_EQ(0, unlink(file2_s1d2)); 3115 3116 ASSERT_EQ(0, unlink(file1_s1d3)); 3117 ASSERT_EQ(0, unlink(file2_s1d3)); 3118 3119 enforce_ruleset(_metadata, ruleset_fd); 3120 ASSERT_EQ(0, close(ruleset_fd)); 3121 3122 ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3123 ASSERT_EQ(EACCES, errno); 3124 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3125 ASSERT_EQ(EACCES, errno); 3126 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3127 ASSERT_EQ(EACCES, errno); 3128 3129 ASSERT_EQ(0, symlink("none", file1_s1d2)); 3130 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3131 ASSERT_EQ(0, unlink(file2_s1d2)); 3132 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3133 3134 ASSERT_EQ(0, symlink("none", file1_s1d3)); 3135 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3136 ASSERT_EQ(0, unlink(file2_s1d3)); 3137 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3138 } 3139 3140 TEST_F_FORK(layout1, make_dir) 3141 { 3142 const struct rule rules[] = { 3143 { 3144 .path = dir_s1d2, 3145 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3146 }, 3147 {}, 3148 }; 3149 const int ruleset_fd = 3150 create_ruleset(_metadata, rules[0].access, rules); 3151 3152 ASSERT_LE(0, ruleset_fd); 3153 3154 ASSERT_EQ(0, unlink(file1_s1d1)); 3155 ASSERT_EQ(0, unlink(file1_s1d2)); 3156 ASSERT_EQ(0, unlink(file1_s1d3)); 3157 3158 enforce_ruleset(_metadata, ruleset_fd); 3159 ASSERT_EQ(0, close(ruleset_fd)); 3160 3161 /* Uses file_* as directory names. */ 3162 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3163 ASSERT_EQ(EACCES, errno); 3164 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3165 ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3166 } 3167 3168 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3169 const int open_flags) 3170 { 3171 static const char path_template[] = "/proc/self/fd/%d"; 3172 char procfd_path[sizeof(path_template) + 10]; 3173 const int procfd_path_size = 3174 snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3175 3176 ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3177 return open(procfd_path, open_flags); 3178 } 3179 3180 TEST_F_FORK(layout1, proc_unlinked_file) 3181 { 3182 const struct rule rules[] = { 3183 { 3184 .path = file1_s1d2, 3185 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3186 }, 3187 {}, 3188 }; 3189 int reg_fd, proc_fd; 3190 const int ruleset_fd = create_ruleset( 3191 _metadata, 3192 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3193 rules); 3194 3195 ASSERT_LE(0, ruleset_fd); 3196 enforce_ruleset(_metadata, ruleset_fd); 3197 ASSERT_EQ(0, close(ruleset_fd)); 3198 3199 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3200 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3201 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3202 ASSERT_LE(0, reg_fd); 3203 ASSERT_EQ(0, unlink(file1_s1d2)); 3204 3205 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3206 ASSERT_LE(0, proc_fd); 3207 ASSERT_EQ(0, close(proc_fd)); 3208 3209 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3210 ASSERT_EQ(-1, proc_fd) 3211 { 3212 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3213 strerror(errno)); 3214 } 3215 ASSERT_EQ(EACCES, errno); 3216 3217 ASSERT_EQ(0, close(reg_fd)); 3218 } 3219 3220 TEST_F_FORK(layout1, proc_pipe) 3221 { 3222 int proc_fd; 3223 int pipe_fds[2]; 3224 char buf = '\0'; 3225 const struct rule rules[] = { 3226 { 3227 .path = dir_s1d2, 3228 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3229 LANDLOCK_ACCESS_FS_WRITE_FILE, 3230 }, 3231 {}, 3232 }; 3233 /* Limits read and write access to files tied to the filesystem. */ 3234 const int ruleset_fd = 3235 create_ruleset(_metadata, rules[0].access, rules); 3236 3237 ASSERT_LE(0, ruleset_fd); 3238 enforce_ruleset(_metadata, ruleset_fd); 3239 ASSERT_EQ(0, close(ruleset_fd)); 3240 3241 /* Checks enforcement for normal files. */ 3242 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3243 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3244 3245 /* Checks access to pipes through FD. */ 3246 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3247 ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3248 { 3249 TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3250 } 3251 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3252 ASSERT_EQ('.', buf); 3253 3254 /* Checks write access to pipe through /proc/self/fd . */ 3255 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3256 ASSERT_LE(0, proc_fd); 3257 ASSERT_EQ(1, write(proc_fd, ".", 1)) 3258 { 3259 TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3260 pipe_fds[1], strerror(errno)); 3261 } 3262 ASSERT_EQ(0, close(proc_fd)); 3263 3264 /* Checks read access to pipe through /proc/self/fd . */ 3265 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3266 ASSERT_LE(0, proc_fd); 3267 buf = '\0'; 3268 ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3269 { 3270 TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3271 pipe_fds[1], strerror(errno)); 3272 } 3273 ASSERT_EQ(0, close(proc_fd)); 3274 3275 ASSERT_EQ(0, close(pipe_fds[0])); 3276 ASSERT_EQ(0, close(pipe_fds[1])); 3277 } 3278 3279 /* Invokes truncate(2) and returns its errno or 0. */ 3280 static int test_truncate(const char *const path) 3281 { 3282 if (truncate(path, 10) < 0) 3283 return errno; 3284 return 0; 3285 } 3286 3287 /* 3288 * Invokes creat(2) and returns its errno or 0. 3289 * Closes the opened file descriptor on success. 3290 */ 3291 static int test_creat(const char *const path) 3292 { 3293 int fd = creat(path, 0600); 3294 3295 if (fd < 0) 3296 return errno; 3297 3298 /* 3299 * Mixing error codes from close(2) and creat(2) should not lead to any 3300 * (access type) confusion for this test. 3301 */ 3302 if (close(fd) < 0) 3303 return errno; 3304 return 0; 3305 } 3306 3307 /* 3308 * Exercises file truncation when it's not restricted, 3309 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 3310 */ 3311 TEST_F_FORK(layout1, truncate_unhandled) 3312 { 3313 const char *const file_r = file1_s1d1; 3314 const char *const file_w = file2_s1d1; 3315 const char *const file_none = file1_s1d2; 3316 const struct rule rules[] = { 3317 { 3318 .path = file_r, 3319 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3320 }, 3321 { 3322 .path = file_w, 3323 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3324 }, 3325 /* Implicitly: No rights for file_none. */ 3326 {}, 3327 }; 3328 3329 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3330 LANDLOCK_ACCESS_FS_WRITE_FILE; 3331 int ruleset_fd; 3332 3333 /* Enable Landlock. */ 3334 ruleset_fd = create_ruleset(_metadata, handled, rules); 3335 3336 ASSERT_LE(0, ruleset_fd); 3337 enforce_ruleset(_metadata, ruleset_fd); 3338 ASSERT_EQ(0, close(ruleset_fd)); 3339 3340 /* 3341 * Checks read right: truncate and open with O_TRUNC work, unless the 3342 * file is attempted to be opened for writing. 3343 */ 3344 EXPECT_EQ(0, test_truncate(file_r)); 3345 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 3346 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 3347 EXPECT_EQ(EACCES, test_creat(file_r)); 3348 3349 /* 3350 * Checks write right: truncate and open with O_TRUNC work, unless the 3351 * file is attempted to be opened for reading. 3352 */ 3353 EXPECT_EQ(0, test_truncate(file_w)); 3354 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 3355 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 3356 EXPECT_EQ(0, test_creat(file_w)); 3357 3358 /* 3359 * Checks "no rights" case: truncate works but all open attempts fail, 3360 * including creat. 3361 */ 3362 EXPECT_EQ(0, test_truncate(file_none)); 3363 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3364 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3365 EXPECT_EQ(EACCES, test_creat(file_none)); 3366 } 3367 3368 TEST_F_FORK(layout1, truncate) 3369 { 3370 const char *const file_rwt = file1_s1d1; 3371 const char *const file_rw = file2_s1d1; 3372 const char *const file_rt = file1_s1d2; 3373 const char *const file_t = file2_s1d2; 3374 const char *const file_none = file1_s1d3; 3375 const char *const dir_t = dir_s2d1; 3376 const char *const file_in_dir_t = file1_s2d1; 3377 const char *const dir_w = dir_s3d1; 3378 const char *const file_in_dir_w = file1_s3d1; 3379 const struct rule rules[] = { 3380 { 3381 .path = file_rwt, 3382 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3383 LANDLOCK_ACCESS_FS_WRITE_FILE | 3384 LANDLOCK_ACCESS_FS_TRUNCATE, 3385 }, 3386 { 3387 .path = file_rw, 3388 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3389 LANDLOCK_ACCESS_FS_WRITE_FILE, 3390 }, 3391 { 3392 .path = file_rt, 3393 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3394 LANDLOCK_ACCESS_FS_TRUNCATE, 3395 }, 3396 { 3397 .path = file_t, 3398 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3399 }, 3400 /* Implicitly: No access rights for file_none. */ 3401 { 3402 .path = dir_t, 3403 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3404 }, 3405 { 3406 .path = dir_w, 3407 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3408 }, 3409 {}, 3410 }; 3411 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3412 LANDLOCK_ACCESS_FS_WRITE_FILE | 3413 LANDLOCK_ACCESS_FS_TRUNCATE; 3414 int ruleset_fd; 3415 3416 /* Enable Landlock. */ 3417 ruleset_fd = create_ruleset(_metadata, handled, rules); 3418 3419 ASSERT_LE(0, ruleset_fd); 3420 enforce_ruleset(_metadata, ruleset_fd); 3421 ASSERT_EQ(0, close(ruleset_fd)); 3422 3423 /* Checks read, write and truncate rights: truncation works. */ 3424 EXPECT_EQ(0, test_truncate(file_rwt)); 3425 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 3426 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 3427 3428 /* Checks read and write rights: no truncate variant works. */ 3429 EXPECT_EQ(EACCES, test_truncate(file_rw)); 3430 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 3431 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 3432 3433 /* 3434 * Checks read and truncate rights: truncation works. 3435 * 3436 * Note: Files can get truncated using open() even with O_RDONLY. 3437 */ 3438 EXPECT_EQ(0, test_truncate(file_rt)); 3439 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 3440 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 3441 3442 /* Checks truncate right: truncate works, but can't open file. */ 3443 EXPECT_EQ(0, test_truncate(file_t)); 3444 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 3445 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 3446 3447 /* Checks "no rights" case: No form of truncation works. */ 3448 EXPECT_EQ(EACCES, test_truncate(file_none)); 3449 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3450 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3451 3452 /* 3453 * Checks truncate right on directory: truncate works on contained 3454 * files. 3455 */ 3456 EXPECT_EQ(0, test_truncate(file_in_dir_t)); 3457 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 3458 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 3459 3460 /* 3461 * Checks creat in dir_w: This requires the truncate right when 3462 * overwriting an existing file, but does not require it when the file 3463 * is new. 3464 */ 3465 EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 3466 3467 ASSERT_EQ(0, unlink(file_in_dir_w)); 3468 EXPECT_EQ(0, test_creat(file_in_dir_w)); 3469 } 3470 3471 /* Invokes ftruncate(2) and returns its errno or 0. */ 3472 static int test_ftruncate(int fd) 3473 { 3474 if (ftruncate(fd, 10) < 0) 3475 return errno; 3476 return 0; 3477 } 3478 3479 TEST_F_FORK(layout1, ftruncate) 3480 { 3481 /* 3482 * This test opens a new file descriptor at different stages of 3483 * Landlock restriction: 3484 * 3485 * without restriction: ftruncate works 3486 * something else but truncate restricted: ftruncate works 3487 * truncate restricted and permitted: ftruncate works 3488 * truncate restricted and not permitted: ftruncate fails 3489 * 3490 * Whether this works or not is expected to depend on the time when the 3491 * FD was opened, not to depend on the time when ftruncate() was 3492 * called. 3493 */ 3494 const char *const path = file1_s1d1; 3495 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 3496 LANDLOCK_ACCESS_FS_WRITE_FILE; 3497 const struct rule layer1[] = { 3498 { 3499 .path = path, 3500 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3501 }, 3502 {}, 3503 }; 3504 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 3505 const struct rule layer2[] = { 3506 { 3507 .path = path, 3508 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3509 }, 3510 {}, 3511 }; 3512 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 3513 LANDLOCK_ACCESS_FS_WRITE_FILE; 3514 const struct rule layer3[] = { 3515 { 3516 .path = path, 3517 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3518 }, 3519 {}, 3520 }; 3521 int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; 3522 3523 fd_layer0 = open(path, O_WRONLY); 3524 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3525 3526 ruleset_fd = create_ruleset(_metadata, handled1, layer1); 3527 ASSERT_LE(0, ruleset_fd); 3528 enforce_ruleset(_metadata, ruleset_fd); 3529 ASSERT_EQ(0, close(ruleset_fd)); 3530 3531 fd_layer1 = open(path, O_WRONLY); 3532 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3533 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3534 3535 ruleset_fd = create_ruleset(_metadata, handled2, layer2); 3536 ASSERT_LE(0, ruleset_fd); 3537 enforce_ruleset(_metadata, ruleset_fd); 3538 ASSERT_EQ(0, close(ruleset_fd)); 3539 3540 fd_layer2 = open(path, O_WRONLY); 3541 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3542 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3543 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3544 3545 ruleset_fd = create_ruleset(_metadata, handled3, layer3); 3546 ASSERT_LE(0, ruleset_fd); 3547 enforce_ruleset(_metadata, ruleset_fd); 3548 ASSERT_EQ(0, close(ruleset_fd)); 3549 3550 fd_layer3 = open(path, O_WRONLY); 3551 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3552 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3553 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3554 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 3555 3556 ASSERT_EQ(0, close(fd_layer0)); 3557 ASSERT_EQ(0, close(fd_layer1)); 3558 ASSERT_EQ(0, close(fd_layer2)); 3559 ASSERT_EQ(0, close(fd_layer3)); 3560 } 3561 3562 /* clang-format off */ 3563 FIXTURE(ftruncate) {}; 3564 /* clang-format on */ 3565 3566 FIXTURE_SETUP(ftruncate) 3567 { 3568 prepare_layout(_metadata); 3569 create_file(_metadata, file1_s1d1); 3570 } 3571 3572 FIXTURE_TEARDOWN(ftruncate) 3573 { 3574 EXPECT_EQ(0, remove_path(file1_s1d1)); 3575 cleanup_layout(_metadata); 3576 } 3577 3578 FIXTURE_VARIANT(ftruncate) 3579 { 3580 const __u64 handled; 3581 const __u64 permitted; 3582 const int expected_open_result; 3583 const int expected_ftruncate_result; 3584 }; 3585 3586 /* clang-format off */ 3587 FIXTURE_VARIANT_ADD(ftruncate, w_w) { 3588 /* clang-format on */ 3589 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 3590 .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 3591 .expected_open_result = 0, 3592 .expected_ftruncate_result = 0, 3593 }; 3594 3595 /* clang-format off */ 3596 FIXTURE_VARIANT_ADD(ftruncate, t_t) { 3597 /* clang-format on */ 3598 .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 3599 .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 3600 .expected_open_result = 0, 3601 .expected_ftruncate_result = 0, 3602 }; 3603 3604 /* clang-format off */ 3605 FIXTURE_VARIANT_ADD(ftruncate, wt_w) { 3606 /* clang-format on */ 3607 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3608 .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 3609 .expected_open_result = 0, 3610 .expected_ftruncate_result = EACCES, 3611 }; 3612 3613 /* clang-format off */ 3614 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 3615 /* clang-format on */ 3616 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3617 .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE | 3618 LANDLOCK_ACCESS_FS_TRUNCATE, 3619 .expected_open_result = 0, 3620 .expected_ftruncate_result = 0, 3621 }; 3622 3623 /* clang-format off */ 3624 FIXTURE_VARIANT_ADD(ftruncate, wt_t) { 3625 /* clang-format on */ 3626 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3627 .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 3628 .expected_open_result = EACCES, 3629 }; 3630 3631 TEST_F_FORK(ftruncate, open_and_ftruncate) 3632 { 3633 const char *const path = file1_s1d1; 3634 const struct rule rules[] = { 3635 { 3636 .path = path, 3637 .access = variant->permitted, 3638 }, 3639 {}, 3640 }; 3641 int fd, ruleset_fd; 3642 3643 /* Enable Landlock. */ 3644 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 3645 ASSERT_LE(0, ruleset_fd); 3646 enforce_ruleset(_metadata, ruleset_fd); 3647 ASSERT_EQ(0, close(ruleset_fd)); 3648 3649 fd = open(path, O_WRONLY); 3650 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3651 if (fd >= 0) { 3652 EXPECT_EQ(variant->expected_ftruncate_result, 3653 test_ftruncate(fd)); 3654 ASSERT_EQ(0, close(fd)); 3655 } 3656 } 3657 3658 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 3659 { 3660 int child, fd, status; 3661 int socket_fds[2]; 3662 3663 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 3664 socket_fds)); 3665 3666 child = fork(); 3667 ASSERT_LE(0, child); 3668 if (child == 0) { 3669 /* 3670 * Enables Landlock in the child process, open a file descriptor 3671 * where truncation is forbidden and send it to the 3672 * non-landlocked parent process. 3673 */ 3674 const char *const path = file1_s1d1; 3675 const struct rule rules[] = { 3676 { 3677 .path = path, 3678 .access = variant->permitted, 3679 }, 3680 {}, 3681 }; 3682 int fd, ruleset_fd; 3683 3684 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 3685 ASSERT_LE(0, ruleset_fd); 3686 enforce_ruleset(_metadata, ruleset_fd); 3687 ASSERT_EQ(0, close(ruleset_fd)); 3688 3689 fd = open(path, O_WRONLY); 3690 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3691 3692 if (fd >= 0) { 3693 ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 3694 ASSERT_EQ(0, close(fd)); 3695 } 3696 3697 ASSERT_EQ(0, close(socket_fds[0])); 3698 3699 _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 3700 return; 3701 } 3702 3703 if (variant->expected_open_result == 0) { 3704 fd = recv_fd(socket_fds[1]); 3705 ASSERT_LE(0, fd); 3706 3707 EXPECT_EQ(variant->expected_ftruncate_result, 3708 test_ftruncate(fd)); 3709 ASSERT_EQ(0, close(fd)); 3710 } 3711 3712 ASSERT_EQ(child, waitpid(child, &status, 0)); 3713 ASSERT_EQ(1, WIFEXITED(status)); 3714 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 3715 3716 ASSERT_EQ(0, close(socket_fds[0])); 3717 ASSERT_EQ(0, close(socket_fds[1])); 3718 } 3719 3720 TEST(memfd_ftruncate) 3721 { 3722 int fd; 3723 3724 fd = memfd_create("name", MFD_CLOEXEC); 3725 ASSERT_LE(0, fd); 3726 3727 /* 3728 * Checks that ftruncate is permitted on file descriptors that are 3729 * created in ways other than open(2). 3730 */ 3731 EXPECT_EQ(0, test_ftruncate(fd)); 3732 3733 ASSERT_EQ(0, close(fd)); 3734 } 3735 3736 /* clang-format off */ 3737 FIXTURE(layout1_bind) {}; 3738 /* clang-format on */ 3739 3740 FIXTURE_SETUP(layout1_bind) 3741 { 3742 prepare_layout(_metadata); 3743 3744 create_layout1(_metadata); 3745 3746 set_cap(_metadata, CAP_SYS_ADMIN); 3747 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 3748 clear_cap(_metadata, CAP_SYS_ADMIN); 3749 } 3750 3751 FIXTURE_TEARDOWN(layout1_bind) 3752 { 3753 set_cap(_metadata, CAP_SYS_ADMIN); 3754 EXPECT_EQ(0, umount(dir_s2d2)); 3755 clear_cap(_metadata, CAP_SYS_ADMIN); 3756 3757 remove_layout1(_metadata); 3758 3759 cleanup_layout(_metadata); 3760 } 3761 3762 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 3763 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 3764 3765 /* 3766 * layout1_bind hierarchy: 3767 * 3768 * tmp 3769 * ├── s1d1 3770 * │ ├── f1 3771 * │ ├── f2 3772 * │ └── s1d2 3773 * │ ├── f1 3774 * │ ├── f2 3775 * │ └── s1d3 3776 * │ ├── f1 3777 * │ └── f2 3778 * ├── s2d1 3779 * │ ├── f1 3780 * │ └── s2d2 3781 * │ ├── f1 3782 * │ ├── f2 3783 * │ └── s1d3 3784 * │ ├── f1 3785 * │ └── f2 3786 * └── s3d1 3787 * └── s3d2 3788 * └── s3d3 3789 */ 3790 3791 TEST_F_FORK(layout1_bind, no_restriction) 3792 { 3793 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 3794 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3795 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 3796 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3797 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 3798 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3799 3800 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 3801 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 3802 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 3803 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 3804 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 3805 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 3806 3807 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 3808 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3809 3810 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 3811 } 3812 3813 TEST_F_FORK(layout1_bind, same_content_same_file) 3814 { 3815 /* 3816 * Sets access right on parent directories of both source and 3817 * destination mount points. 3818 */ 3819 const struct rule layer1_parent[] = { 3820 { 3821 .path = dir_s1d1, 3822 .access = ACCESS_RO, 3823 }, 3824 { 3825 .path = dir_s2d1, 3826 .access = ACCESS_RW, 3827 }, 3828 {}, 3829 }; 3830 /* 3831 * Sets access rights on the same bind-mounted directories. The result 3832 * should be ACCESS_RW for both directories, but not both hierarchies 3833 * because of the first layer. 3834 */ 3835 const struct rule layer2_mount_point[] = { 3836 { 3837 .path = dir_s1d2, 3838 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3839 }, 3840 { 3841 .path = dir_s2d2, 3842 .access = ACCESS_RW, 3843 }, 3844 {}, 3845 }; 3846 /* Only allow read-access to the s1d3 hierarchies. */ 3847 const struct rule layer3_source[] = { 3848 { 3849 .path = dir_s1d3, 3850 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3851 }, 3852 {}, 3853 }; 3854 /* Removes all access rights. */ 3855 const struct rule layer4_destination[] = { 3856 { 3857 .path = bind_file1_s1d3, 3858 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3859 }, 3860 {}, 3861 }; 3862 int ruleset_fd; 3863 3864 /* Sets rules for the parent directories. */ 3865 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 3866 ASSERT_LE(0, ruleset_fd); 3867 enforce_ruleset(_metadata, ruleset_fd); 3868 ASSERT_EQ(0, close(ruleset_fd)); 3869 3870 /* Checks source hierarchy. */ 3871 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3872 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3873 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3874 3875 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3876 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3877 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3878 3879 /* Checks destination hierarchy. */ 3880 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 3881 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3882 3883 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3884 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3885 3886 /* Sets rules for the mount points. */ 3887 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 3888 ASSERT_LE(0, ruleset_fd); 3889 enforce_ruleset(_metadata, ruleset_fd); 3890 ASSERT_EQ(0, close(ruleset_fd)); 3891 3892 /* Checks source hierarchy. */ 3893 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 3894 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3895 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3896 3897 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3898 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3899 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3900 3901 /* Checks destination hierarchy. */ 3902 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 3903 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 3904 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3905 3906 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3907 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3908 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3909 3910 /* Sets a (shared) rule only on the source. */ 3911 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 3912 ASSERT_LE(0, ruleset_fd); 3913 enforce_ruleset(_metadata, ruleset_fd); 3914 ASSERT_EQ(0, close(ruleset_fd)); 3915 3916 /* Checks source hierarchy. */ 3917 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 3918 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3919 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3920 3921 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3922 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3923 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 3924 3925 /* Checks destination hierarchy. */ 3926 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 3927 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 3928 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3929 3930 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3931 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3932 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3933 3934 /* Sets a (shared) rule only on the destination. */ 3935 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 3936 ASSERT_LE(0, ruleset_fd); 3937 enforce_ruleset(_metadata, ruleset_fd); 3938 ASSERT_EQ(0, close(ruleset_fd)); 3939 3940 /* Checks source hierarchy. */ 3941 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 3942 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3943 3944 /* Checks destination hierarchy. */ 3945 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 3946 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3947 } 3948 3949 TEST_F_FORK(layout1_bind, reparent_cross_mount) 3950 { 3951 const struct rule layer1[] = { 3952 { 3953 /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 3954 .path = dir_s2d1, 3955 .access = LANDLOCK_ACCESS_FS_REFER, 3956 }, 3957 { 3958 .path = bind_dir_s1d3, 3959 .access = LANDLOCK_ACCESS_FS_EXECUTE, 3960 }, 3961 {}, 3962 }; 3963 int ruleset_fd = create_ruleset( 3964 _metadata, 3965 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 3966 3967 ASSERT_LE(0, ruleset_fd); 3968 enforce_ruleset(_metadata, ruleset_fd); 3969 ASSERT_EQ(0, close(ruleset_fd)); 3970 3971 /* Checks basic denied move. */ 3972 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 3973 ASSERT_EQ(EXDEV, errno); 3974 3975 /* Checks real cross-mount move (Landlock is not involved). */ 3976 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 3977 ASSERT_EQ(EXDEV, errno); 3978 3979 /* Checks move that will give more accesses. */ 3980 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 3981 ASSERT_EQ(EXDEV, errno); 3982 3983 /* Checks legitimate downgrade move. */ 3984 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 3985 } 3986 3987 #define LOWER_BASE TMP_DIR "/lower" 3988 #define LOWER_DATA LOWER_BASE "/data" 3989 static const char lower_fl1[] = LOWER_DATA "/fl1"; 3990 static const char lower_dl1[] = LOWER_DATA "/dl1"; 3991 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 3992 static const char lower_fo1[] = LOWER_DATA "/fo1"; 3993 static const char lower_do1[] = LOWER_DATA "/do1"; 3994 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 3995 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 3996 3997 static const char (*lower_base_files[])[] = { 3998 &lower_fl1, 3999 &lower_fo1, 4000 NULL, 4001 }; 4002 static const char (*lower_base_directories[])[] = { 4003 &lower_dl1, 4004 &lower_do1, 4005 NULL, 4006 }; 4007 static const char (*lower_sub_files[])[] = { 4008 &lower_dl1_fl2, 4009 &lower_do1_fo2, 4010 &lower_do1_fl3, 4011 NULL, 4012 }; 4013 4014 #define UPPER_BASE TMP_DIR "/upper" 4015 #define UPPER_DATA UPPER_BASE "/data" 4016 #define UPPER_WORK UPPER_BASE "/work" 4017 static const char upper_fu1[] = UPPER_DATA "/fu1"; 4018 static const char upper_du1[] = UPPER_DATA "/du1"; 4019 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 4020 static const char upper_fo1[] = UPPER_DATA "/fo1"; 4021 static const char upper_do1[] = UPPER_DATA "/do1"; 4022 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 4023 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 4024 4025 static const char (*upper_base_files[])[] = { 4026 &upper_fu1, 4027 &upper_fo1, 4028 NULL, 4029 }; 4030 static const char (*upper_base_directories[])[] = { 4031 &upper_du1, 4032 &upper_do1, 4033 NULL, 4034 }; 4035 static const char (*upper_sub_files[])[] = { 4036 &upper_du1_fu2, 4037 &upper_do1_fo2, 4038 &upper_do1_fu3, 4039 NULL, 4040 }; 4041 4042 #define MERGE_BASE TMP_DIR "/merge" 4043 #define MERGE_DATA MERGE_BASE "/data" 4044 static const char merge_fl1[] = MERGE_DATA "/fl1"; 4045 static const char merge_dl1[] = MERGE_DATA "/dl1"; 4046 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 4047 static const char merge_fu1[] = MERGE_DATA "/fu1"; 4048 static const char merge_du1[] = MERGE_DATA "/du1"; 4049 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 4050 static const char merge_fo1[] = MERGE_DATA "/fo1"; 4051 static const char merge_do1[] = MERGE_DATA "/do1"; 4052 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 4053 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 4054 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 4055 4056 static const char (*merge_base_files[])[] = { 4057 &merge_fl1, 4058 &merge_fu1, 4059 &merge_fo1, 4060 NULL, 4061 }; 4062 static const char (*merge_base_directories[])[] = { 4063 &merge_dl1, 4064 &merge_du1, 4065 &merge_do1, 4066 NULL, 4067 }; 4068 static const char (*merge_sub_files[])[] = { 4069 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 4070 &merge_do1_fl3, &merge_do1_fu3, NULL, 4071 }; 4072 4073 /* 4074 * layout2_overlay hierarchy: 4075 * 4076 * tmp 4077 * ├── lower 4078 * │ └── data 4079 * │ ├── dl1 4080 * │ │ └── fl2 4081 * │ ├── do1 4082 * │ │ ├── fl3 4083 * │ │ └── fo2 4084 * │ ├── fl1 4085 * │ └── fo1 4086 * ├── merge 4087 * │ └── data 4088 * │ ├── dl1 4089 * │ │ └── fl2 4090 * │ ├── do1 4091 * │ │ ├── fl3 4092 * │ │ ├── fo2 4093 * │ │ └── fu3 4094 * │ ├── du1 4095 * │ │ └── fu2 4096 * │ ├── fl1 4097 * │ ├── fo1 4098 * │ └── fu1 4099 * └── upper 4100 * ├── data 4101 * │ ├── do1 4102 * │ │ ├── fo2 4103 * │ │ └── fu3 4104 * │ ├── du1 4105 * │ │ └── fu2 4106 * │ ├── fo1 4107 * │ └── fu1 4108 * └── work 4109 * └── work 4110 */ 4111 4112 FIXTURE(layout2_overlay) 4113 { 4114 bool skip_test; 4115 }; 4116 4117 FIXTURE_SETUP(layout2_overlay) 4118 { 4119 if (!supports_filesystem("overlay")) { 4120 self->skip_test = true; 4121 SKIP(return, "overlayfs is not supported (setup)"); 4122 } 4123 4124 prepare_layout(_metadata); 4125 4126 create_directory(_metadata, LOWER_BASE); 4127 set_cap(_metadata, CAP_SYS_ADMIN); 4128 /* Creates tmpfs mount points to get deterministic overlayfs. */ 4129 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 4130 clear_cap(_metadata, CAP_SYS_ADMIN); 4131 create_file(_metadata, lower_fl1); 4132 create_file(_metadata, lower_dl1_fl2); 4133 create_file(_metadata, lower_fo1); 4134 create_file(_metadata, lower_do1_fo2); 4135 create_file(_metadata, lower_do1_fl3); 4136 4137 create_directory(_metadata, UPPER_BASE); 4138 set_cap(_metadata, CAP_SYS_ADMIN); 4139 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 4140 clear_cap(_metadata, CAP_SYS_ADMIN); 4141 create_file(_metadata, upper_fu1); 4142 create_file(_metadata, upper_du1_fu2); 4143 create_file(_metadata, upper_fo1); 4144 create_file(_metadata, upper_do1_fo2); 4145 create_file(_metadata, upper_do1_fu3); 4146 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 4147 4148 create_directory(_metadata, MERGE_DATA); 4149 set_cap(_metadata, CAP_SYS_ADMIN); 4150 set_cap(_metadata, CAP_DAC_OVERRIDE); 4151 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 4152 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 4153 ",workdir=" UPPER_WORK)); 4154 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4155 clear_cap(_metadata, CAP_SYS_ADMIN); 4156 } 4157 4158 FIXTURE_TEARDOWN(layout2_overlay) 4159 { 4160 if (self->skip_test) 4161 SKIP(return, "overlayfs is not supported (teardown)"); 4162 4163 EXPECT_EQ(0, remove_path(lower_do1_fl3)); 4164 EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 4165 EXPECT_EQ(0, remove_path(lower_fl1)); 4166 EXPECT_EQ(0, remove_path(lower_do1_fo2)); 4167 EXPECT_EQ(0, remove_path(lower_fo1)); 4168 set_cap(_metadata, CAP_SYS_ADMIN); 4169 EXPECT_EQ(0, umount(LOWER_BASE)); 4170 clear_cap(_metadata, CAP_SYS_ADMIN); 4171 EXPECT_EQ(0, remove_path(LOWER_BASE)); 4172 4173 EXPECT_EQ(0, remove_path(upper_do1_fu3)); 4174 EXPECT_EQ(0, remove_path(upper_du1_fu2)); 4175 EXPECT_EQ(0, remove_path(upper_fu1)); 4176 EXPECT_EQ(0, remove_path(upper_do1_fo2)); 4177 EXPECT_EQ(0, remove_path(upper_fo1)); 4178 EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 4179 set_cap(_metadata, CAP_SYS_ADMIN); 4180 EXPECT_EQ(0, umount(UPPER_BASE)); 4181 clear_cap(_metadata, CAP_SYS_ADMIN); 4182 EXPECT_EQ(0, remove_path(UPPER_BASE)); 4183 4184 set_cap(_metadata, CAP_SYS_ADMIN); 4185 EXPECT_EQ(0, umount(MERGE_DATA)); 4186 clear_cap(_metadata, CAP_SYS_ADMIN); 4187 EXPECT_EQ(0, remove_path(MERGE_DATA)); 4188 4189 cleanup_layout(_metadata); 4190 } 4191 4192 TEST_F_FORK(layout2_overlay, no_restriction) 4193 { 4194 if (self->skip_test) 4195 SKIP(return, "overlayfs is not supported (test)"); 4196 4197 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 4198 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 4199 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 4200 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 4201 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 4202 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 4203 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 4204 4205 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 4206 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 4207 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 4208 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 4209 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 4210 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 4211 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 4212 4213 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 4214 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 4215 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 4216 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 4217 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 4218 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 4219 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 4220 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 4221 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 4222 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 4223 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 4224 } 4225 4226 #define for_each_path(path_list, path_entry, i) \ 4227 for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 4228 path_entry = *path_list[++i]) 4229 4230 TEST_F_FORK(layout2_overlay, same_content_different_file) 4231 { 4232 /* Sets access right on parent directories of both layers. */ 4233 const struct rule layer1_base[] = { 4234 { 4235 .path = LOWER_BASE, 4236 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4237 }, 4238 { 4239 .path = UPPER_BASE, 4240 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4241 }, 4242 { 4243 .path = MERGE_BASE, 4244 .access = ACCESS_RW, 4245 }, 4246 {}, 4247 }; 4248 const struct rule layer2_data[] = { 4249 { 4250 .path = LOWER_DATA, 4251 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4252 }, 4253 { 4254 .path = UPPER_DATA, 4255 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4256 }, 4257 { 4258 .path = MERGE_DATA, 4259 .access = ACCESS_RW, 4260 }, 4261 {}, 4262 }; 4263 /* Sets access right on directories inside both layers. */ 4264 const struct rule layer3_subdirs[] = { 4265 { 4266 .path = lower_dl1, 4267 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4268 }, 4269 { 4270 .path = lower_do1, 4271 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4272 }, 4273 { 4274 .path = upper_du1, 4275 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4276 }, 4277 { 4278 .path = upper_do1, 4279 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4280 }, 4281 { 4282 .path = merge_dl1, 4283 .access = ACCESS_RW, 4284 }, 4285 { 4286 .path = merge_du1, 4287 .access = ACCESS_RW, 4288 }, 4289 { 4290 .path = merge_do1, 4291 .access = ACCESS_RW, 4292 }, 4293 {}, 4294 }; 4295 /* Tighten access rights to the files. */ 4296 const struct rule layer4_files[] = { 4297 { 4298 .path = lower_dl1_fl2, 4299 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4300 }, 4301 { 4302 .path = lower_do1_fo2, 4303 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4304 }, 4305 { 4306 .path = lower_do1_fl3, 4307 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4308 }, 4309 { 4310 .path = upper_du1_fu2, 4311 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4312 }, 4313 { 4314 .path = upper_do1_fo2, 4315 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4316 }, 4317 { 4318 .path = upper_do1_fu3, 4319 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4320 }, 4321 { 4322 .path = merge_dl1_fl2, 4323 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4324 LANDLOCK_ACCESS_FS_WRITE_FILE, 4325 }, 4326 { 4327 .path = merge_du1_fu2, 4328 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4329 LANDLOCK_ACCESS_FS_WRITE_FILE, 4330 }, 4331 { 4332 .path = merge_do1_fo2, 4333 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4334 LANDLOCK_ACCESS_FS_WRITE_FILE, 4335 }, 4336 { 4337 .path = merge_do1_fl3, 4338 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4339 LANDLOCK_ACCESS_FS_WRITE_FILE, 4340 }, 4341 { 4342 .path = merge_do1_fu3, 4343 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4344 LANDLOCK_ACCESS_FS_WRITE_FILE, 4345 }, 4346 {}, 4347 }; 4348 const struct rule layer5_merge_only[] = { 4349 { 4350 .path = MERGE_DATA, 4351 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4352 LANDLOCK_ACCESS_FS_WRITE_FILE, 4353 }, 4354 {}, 4355 }; 4356 int ruleset_fd; 4357 size_t i; 4358 const char *path_entry; 4359 4360 if (self->skip_test) 4361 SKIP(return, "overlayfs is not supported (test)"); 4362 4363 /* Sets rules on base directories (i.e. outside overlay scope). */ 4364 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 4365 ASSERT_LE(0, ruleset_fd); 4366 enforce_ruleset(_metadata, ruleset_fd); 4367 ASSERT_EQ(0, close(ruleset_fd)); 4368 4369 /* Checks lower layer. */ 4370 for_each_path(lower_base_files, path_entry, i) { 4371 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4372 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4373 } 4374 for_each_path(lower_base_directories, path_entry, i) { 4375 ASSERT_EQ(EACCES, 4376 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4377 } 4378 for_each_path(lower_sub_files, path_entry, i) { 4379 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4380 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4381 } 4382 /* Checks upper layer. */ 4383 for_each_path(upper_base_files, path_entry, i) { 4384 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4385 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4386 } 4387 for_each_path(upper_base_directories, path_entry, i) { 4388 ASSERT_EQ(EACCES, 4389 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4390 } 4391 for_each_path(upper_sub_files, path_entry, i) { 4392 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4393 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4394 } 4395 /* 4396 * Checks that access rights are independent from the lower and upper 4397 * layers: write access to upper files viewed through the merge point 4398 * is still allowed, and write access to lower file viewed (and copied) 4399 * through the merge point is still allowed. 4400 */ 4401 for_each_path(merge_base_files, path_entry, i) { 4402 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4403 } 4404 for_each_path(merge_base_directories, path_entry, i) { 4405 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4406 } 4407 for_each_path(merge_sub_files, path_entry, i) { 4408 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4409 } 4410 4411 /* Sets rules on data directories (i.e. inside overlay scope). */ 4412 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 4413 ASSERT_LE(0, ruleset_fd); 4414 enforce_ruleset(_metadata, ruleset_fd); 4415 ASSERT_EQ(0, close(ruleset_fd)); 4416 4417 /* Checks merge. */ 4418 for_each_path(merge_base_files, path_entry, i) { 4419 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4420 } 4421 for_each_path(merge_base_directories, path_entry, i) { 4422 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4423 } 4424 for_each_path(merge_sub_files, path_entry, i) { 4425 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4426 } 4427 4428 /* Same checks with tighter rules. */ 4429 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 4430 ASSERT_LE(0, ruleset_fd); 4431 enforce_ruleset(_metadata, ruleset_fd); 4432 ASSERT_EQ(0, close(ruleset_fd)); 4433 4434 /* Checks changes for lower layer. */ 4435 for_each_path(lower_base_files, path_entry, i) { 4436 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4437 } 4438 /* Checks changes for upper layer. */ 4439 for_each_path(upper_base_files, path_entry, i) { 4440 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4441 } 4442 /* Checks all merge accesses. */ 4443 for_each_path(merge_base_files, path_entry, i) { 4444 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4445 } 4446 for_each_path(merge_base_directories, path_entry, i) { 4447 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4448 } 4449 for_each_path(merge_sub_files, path_entry, i) { 4450 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4451 } 4452 4453 /* Sets rules directly on overlayed files. */ 4454 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 4455 ASSERT_LE(0, ruleset_fd); 4456 enforce_ruleset(_metadata, ruleset_fd); 4457 ASSERT_EQ(0, close(ruleset_fd)); 4458 4459 /* Checks unchanged accesses on lower layer. */ 4460 for_each_path(lower_sub_files, path_entry, i) { 4461 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4462 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4463 } 4464 /* Checks unchanged accesses on upper layer. */ 4465 for_each_path(upper_sub_files, path_entry, i) { 4466 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4467 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4468 } 4469 /* Checks all merge accesses. */ 4470 for_each_path(merge_base_files, path_entry, i) { 4471 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4472 } 4473 for_each_path(merge_base_directories, path_entry, i) { 4474 ASSERT_EQ(EACCES, 4475 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4476 } 4477 for_each_path(merge_sub_files, path_entry, i) { 4478 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4479 } 4480 4481 /* Only allowes access to the merge hierarchy. */ 4482 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 4483 ASSERT_LE(0, ruleset_fd); 4484 enforce_ruleset(_metadata, ruleset_fd); 4485 ASSERT_EQ(0, close(ruleset_fd)); 4486 4487 /* Checks new accesses on lower layer. */ 4488 for_each_path(lower_sub_files, path_entry, i) { 4489 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4490 } 4491 /* Checks new accesses on upper layer. */ 4492 for_each_path(upper_sub_files, path_entry, i) { 4493 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4494 } 4495 /* Checks all merge accesses. */ 4496 for_each_path(merge_base_files, path_entry, i) { 4497 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4498 } 4499 for_each_path(merge_base_directories, path_entry, i) { 4500 ASSERT_EQ(EACCES, 4501 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4502 } 4503 for_each_path(merge_sub_files, path_entry, i) { 4504 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4505 } 4506 } 4507 4508 FIXTURE(layout3_fs) 4509 { 4510 bool has_created_dir; 4511 bool has_created_file; 4512 char *dir_path; 4513 bool skip_test; 4514 }; 4515 4516 FIXTURE_VARIANT(layout3_fs) 4517 { 4518 const struct mnt_opt mnt; 4519 const char *const file_path; 4520 unsigned int cwd_fs_magic; 4521 }; 4522 4523 /* clang-format off */ 4524 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 4525 /* clang-format on */ 4526 .mnt = mnt_tmp, 4527 .file_path = file1_s1d1, 4528 }; 4529 4530 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 4531 .mnt = { 4532 .type = "ramfs", 4533 .data = "mode=700", 4534 }, 4535 .file_path = TMP_DIR "/dir/file", 4536 }; 4537 4538 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 4539 .mnt = { 4540 .type = "cgroup2", 4541 }, 4542 .file_path = TMP_DIR "/test/cgroup.procs", 4543 }; 4544 4545 FIXTURE_VARIANT_ADD(layout3_fs, proc) { 4546 .mnt = { 4547 .type = "proc", 4548 }, 4549 .file_path = TMP_DIR "/self/status", 4550 }; 4551 4552 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 4553 .mnt = { 4554 .type = "sysfs", 4555 }, 4556 .file_path = TMP_DIR "/kernel/notes", 4557 }; 4558 4559 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 4560 .mnt = { 4561 .source = TMP_DIR, 4562 .flags = MS_BIND, 4563 }, 4564 .file_path = TMP_DIR "/dir/file", 4565 .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 4566 }; 4567 4568 FIXTURE_SETUP(layout3_fs) 4569 { 4570 struct stat statbuf; 4571 const char *slash; 4572 size_t dir_len; 4573 4574 if (!supports_filesystem(variant->mnt.type) || 4575 !cwd_matches_fs(variant->cwd_fs_magic)) { 4576 self->skip_test = true; 4577 SKIP(return, "this filesystem is not supported (setup)"); 4578 } 4579 4580 slash = strrchr(variant->file_path, '/'); 4581 ASSERT_NE(slash, NULL); 4582 dir_len = (size_t)slash - (size_t)variant->file_path; 4583 ASSERT_LT(0, dir_len); 4584 self->dir_path = malloc(dir_len + 1); 4585 self->dir_path[dir_len] = '\0'; 4586 strncpy(self->dir_path, variant->file_path, dir_len); 4587 4588 prepare_layout_opt(_metadata, &variant->mnt); 4589 4590 /* Creates directory when required. */ 4591 if (stat(self->dir_path, &statbuf)) { 4592 set_cap(_metadata, CAP_DAC_OVERRIDE); 4593 EXPECT_EQ(0, mkdir(self->dir_path, 0700)) 4594 { 4595 TH_LOG("Failed to create directory \"%s\": %s", 4596 self->dir_path, strerror(errno)); 4597 free(self->dir_path); 4598 self->dir_path = NULL; 4599 } 4600 self->has_created_dir = true; 4601 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4602 } 4603 4604 /* Creates file when required. */ 4605 if (stat(variant->file_path, &statbuf)) { 4606 int fd; 4607 4608 set_cap(_metadata, CAP_DAC_OVERRIDE); 4609 fd = creat(variant->file_path, 0600); 4610 EXPECT_LE(0, fd) 4611 { 4612 TH_LOG("Failed to create file \"%s\": %s", 4613 variant->file_path, strerror(errno)); 4614 } 4615 EXPECT_EQ(0, close(fd)); 4616 self->has_created_file = true; 4617 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4618 } 4619 } 4620 4621 FIXTURE_TEARDOWN(layout3_fs) 4622 { 4623 if (self->skip_test) 4624 SKIP(return, "this filesystem is not supported (teardown)"); 4625 4626 if (self->has_created_file) { 4627 set_cap(_metadata, CAP_DAC_OVERRIDE); 4628 /* 4629 * Don't check for error because the file might already 4630 * have been removed (cf. release_inode test). 4631 */ 4632 unlink(variant->file_path); 4633 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4634 } 4635 4636 if (self->has_created_dir) { 4637 set_cap(_metadata, CAP_DAC_OVERRIDE); 4638 /* 4639 * Don't check for error because the directory might already 4640 * have been removed (cf. release_inode test). 4641 */ 4642 rmdir(self->dir_path); 4643 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4644 } 4645 free(self->dir_path); 4646 self->dir_path = NULL; 4647 4648 cleanup_layout(_metadata); 4649 } 4650 4651 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 4652 FIXTURE_DATA(layout3_fs) * self, 4653 const FIXTURE_VARIANT(layout3_fs) * variant, 4654 const char *const rule_path) 4655 { 4656 const struct rule layer1_allow_read_file[] = { 4657 { 4658 .path = rule_path, 4659 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4660 }, 4661 {}, 4662 }; 4663 const struct landlock_ruleset_attr layer2_deny_everything_attr = { 4664 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 4665 }; 4666 const char *const dev_null_path = "/dev/null"; 4667 int ruleset_fd; 4668 4669 if (self->skip_test) 4670 SKIP(return, "this filesystem is not supported (test)"); 4671 4672 /* Checks without Landlock. */ 4673 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 4674 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 4675 4676 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 4677 layer1_allow_read_file); 4678 EXPECT_LE(0, ruleset_fd); 4679 enforce_ruleset(_metadata, ruleset_fd); 4680 EXPECT_EQ(0, close(ruleset_fd)); 4681 4682 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 4683 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 4684 4685 /* Forbids directory reading. */ 4686 ruleset_fd = 4687 landlock_create_ruleset(&layer2_deny_everything_attr, 4688 sizeof(layer2_deny_everything_attr), 0); 4689 EXPECT_LE(0, ruleset_fd); 4690 enforce_ruleset(_metadata, ruleset_fd); 4691 EXPECT_EQ(0, close(ruleset_fd)); 4692 4693 /* Checks with Landlock and forbidden access. */ 4694 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 4695 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 4696 } 4697 4698 /* Matrix of tests to check file hierarchy evaluation. */ 4699 4700 TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 4701 { 4702 /* The current directory must not be the root for this test. */ 4703 layer3_fs_tag_inode(_metadata, self, variant, "."); 4704 } 4705 4706 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 4707 { 4708 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 4709 } 4710 4711 TEST_F_FORK(layout3_fs, tag_inode_dir_child) 4712 { 4713 layer3_fs_tag_inode(_metadata, self, variant, self->dir_path); 4714 } 4715 4716 TEST_F_FORK(layout3_fs, tag_inode_file) 4717 { 4718 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 4719 } 4720 4721 /* Light version of layout1.release_inodes */ 4722 TEST_F_FORK(layout3_fs, release_inodes) 4723 { 4724 const struct rule layer1[] = { 4725 { 4726 .path = TMP_DIR, 4727 .access = LANDLOCK_ACCESS_FS_READ_DIR, 4728 }, 4729 {}, 4730 }; 4731 int ruleset_fd; 4732 4733 if (self->skip_test) 4734 SKIP(return, "this filesystem is not supported (test)"); 4735 4736 /* Clean up for the teardown to not fail. */ 4737 if (self->has_created_file) 4738 EXPECT_EQ(0, remove_path(variant->file_path)); 4739 4740 if (self->has_created_dir) 4741 /* Don't check for error because of cgroup specificities. */ 4742 remove_path(self->dir_path); 4743 4744 ruleset_fd = 4745 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 4746 ASSERT_LE(0, ruleset_fd); 4747 4748 /* Unmount the filesystem while it is being used by a ruleset. */ 4749 set_cap(_metadata, CAP_SYS_ADMIN); 4750 ASSERT_EQ(0, umount(TMP_DIR)); 4751 clear_cap(_metadata, CAP_SYS_ADMIN); 4752 4753 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 4754 set_cap(_metadata, CAP_SYS_ADMIN); 4755 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 4756 clear_cap(_metadata, CAP_SYS_ADMIN); 4757 4758 enforce_ruleset(_metadata, ruleset_fd); 4759 ASSERT_EQ(0, close(ruleset_fd)); 4760 4761 /* Checks that access to the new mount point is denied. */ 4762 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 4763 } 4764 4765 TEST_HARNESS_MAIN 4766