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