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\"", path); 1865 }; 1866 } 1867 1868 TEST_F_FORK(layout1, execute) 1869 { 1870 const struct rule rules[] = { 1871 { 1872 .path = dir_s1d2, 1873 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1874 }, 1875 {}, 1876 }; 1877 const int ruleset_fd = 1878 create_ruleset(_metadata, rules[0].access, rules); 1879 1880 ASSERT_LE(0, ruleset_fd); 1881 copy_binary(_metadata, file1_s1d1); 1882 copy_binary(_metadata, file1_s1d2); 1883 copy_binary(_metadata, file1_s1d3); 1884 1885 enforce_ruleset(_metadata, ruleset_fd); 1886 ASSERT_EQ(0, close(ruleset_fd)); 1887 1888 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1889 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1890 test_execute(_metadata, EACCES, file1_s1d1); 1891 1892 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1893 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1894 test_execute(_metadata, 0, file1_s1d2); 1895 1896 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1897 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1898 test_execute(_metadata, 0, file1_s1d3); 1899 } 1900 1901 TEST_F_FORK(layout1, link) 1902 { 1903 const struct rule layer1[] = { 1904 { 1905 .path = dir_s1d2, 1906 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1907 }, 1908 {}, 1909 }; 1910 const struct rule layer2[] = { 1911 { 1912 .path = dir_s1d3, 1913 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1914 }, 1915 {}, 1916 }; 1917 int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 1918 1919 ASSERT_LE(0, ruleset_fd); 1920 1921 ASSERT_EQ(0, unlink(file1_s1d1)); 1922 ASSERT_EQ(0, unlink(file1_s1d2)); 1923 ASSERT_EQ(0, unlink(file1_s1d3)); 1924 1925 enforce_ruleset(_metadata, ruleset_fd); 1926 ASSERT_EQ(0, close(ruleset_fd)); 1927 1928 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1929 ASSERT_EQ(EACCES, errno); 1930 1931 /* Denies linking because of reparenting. */ 1932 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1933 ASSERT_EQ(EXDEV, errno); 1934 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1935 ASSERT_EQ(EXDEV, errno); 1936 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 1937 ASSERT_EQ(EXDEV, errno); 1938 1939 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1940 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 1941 1942 /* Prepares for next unlinks. */ 1943 ASSERT_EQ(0, unlink(file2_s1d2)); 1944 ASSERT_EQ(0, unlink(file2_s1d3)); 1945 1946 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 1947 ASSERT_LE(0, ruleset_fd); 1948 enforce_ruleset(_metadata, ruleset_fd); 1949 ASSERT_EQ(0, close(ruleset_fd)); 1950 1951 /* Checks that linkind doesn't require the ability to delete a file. */ 1952 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 1953 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1954 } 1955 1956 static int test_rename(const char *const oldpath, const char *const newpath) 1957 { 1958 if (rename(oldpath, newpath)) 1959 return errno; 1960 return 0; 1961 } 1962 1963 static int test_exchange(const char *const oldpath, const char *const newpath) 1964 { 1965 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 1966 return errno; 1967 return 0; 1968 } 1969 1970 TEST_F_FORK(layout1, rename_file) 1971 { 1972 const struct rule rules[] = { 1973 { 1974 .path = dir_s1d3, 1975 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1976 }, 1977 { 1978 .path = dir_s2d2, 1979 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1980 }, 1981 {}, 1982 }; 1983 const int ruleset_fd = 1984 create_ruleset(_metadata, rules[0].access, rules); 1985 1986 ASSERT_LE(0, ruleset_fd); 1987 1988 ASSERT_EQ(0, unlink(file1_s1d2)); 1989 1990 enforce_ruleset(_metadata, ruleset_fd); 1991 ASSERT_EQ(0, close(ruleset_fd)); 1992 1993 /* 1994 * Tries to replace a file, from a directory that allows file removal, 1995 * but to a different directory (which also allows file removal). 1996 */ 1997 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1998 ASSERT_EQ(EXDEV, errno); 1999 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 2000 RENAME_EXCHANGE)); 2001 ASSERT_EQ(EXDEV, errno); 2002 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2003 RENAME_EXCHANGE)); 2004 ASSERT_EQ(EXDEV, errno); 2005 2006 /* 2007 * Tries to replace a file, from a directory that denies file removal, 2008 * to a different directory (which allows file removal). 2009 */ 2010 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2011 ASSERT_EQ(EACCES, errno); 2012 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 2013 RENAME_EXCHANGE)); 2014 ASSERT_EQ(EACCES, errno); 2015 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 2016 RENAME_EXCHANGE)); 2017 ASSERT_EQ(EXDEV, errno); 2018 2019 /* Exchanges files and directories that partially allow removal. */ 2020 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 2021 RENAME_EXCHANGE)); 2022 ASSERT_EQ(EACCES, errno); 2023 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 2024 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 2025 ASSERT_EQ(EACCES, errno); 2026 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 2027 RENAME_EXCHANGE)); 2028 ASSERT_EQ(EACCES, errno); 2029 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 2030 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2031 ASSERT_EQ(EACCES, errno); 2032 2033 /* Renames files with different parents. */ 2034 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2035 ASSERT_EQ(EXDEV, errno); 2036 ASSERT_EQ(0, unlink(file1_s1d3)); 2037 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2038 ASSERT_EQ(EACCES, errno); 2039 2040 /* Exchanges and renames files with same parent. */ 2041 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 2042 RENAME_EXCHANGE)); 2043 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 2044 2045 /* Exchanges files and directories with same parent, twice. */ 2046 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2047 RENAME_EXCHANGE)); 2048 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2049 RENAME_EXCHANGE)); 2050 } 2051 2052 TEST_F_FORK(layout1, rename_dir) 2053 { 2054 const struct rule rules[] = { 2055 { 2056 .path = dir_s1d2, 2057 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2058 }, 2059 { 2060 .path = dir_s2d1, 2061 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2062 }, 2063 {}, 2064 }; 2065 const int ruleset_fd = 2066 create_ruleset(_metadata, rules[0].access, rules); 2067 2068 ASSERT_LE(0, ruleset_fd); 2069 2070 /* Empties dir_s1d3 to allow renaming. */ 2071 ASSERT_EQ(0, unlink(file1_s1d3)); 2072 ASSERT_EQ(0, unlink(file2_s1d3)); 2073 2074 enforce_ruleset(_metadata, ruleset_fd); 2075 ASSERT_EQ(0, close(ruleset_fd)); 2076 2077 /* Exchanges and renames directory to a different parent. */ 2078 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2079 RENAME_EXCHANGE)); 2080 ASSERT_EQ(EXDEV, errno); 2081 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 2082 ASSERT_EQ(EXDEV, errno); 2083 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2084 RENAME_EXCHANGE)); 2085 ASSERT_EQ(EXDEV, errno); 2086 2087 /* 2088 * Exchanges directory to the same parent, which doesn't allow 2089 * directory removal. 2090 */ 2091 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 2092 RENAME_EXCHANGE)); 2093 ASSERT_EQ(EACCES, errno); 2094 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 2095 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 2096 ASSERT_EQ(EACCES, errno); 2097 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 2098 RENAME_EXCHANGE)); 2099 ASSERT_EQ(EACCES, errno); 2100 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 2101 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2102 ASSERT_EQ(EACCES, errno); 2103 2104 /* 2105 * Exchanges and renames directory to the same parent, which allows 2106 * directory removal. 2107 */ 2108 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 2109 RENAME_EXCHANGE)); 2110 ASSERT_EQ(0, unlink(dir_s1d3)); 2111 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2112 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 2113 ASSERT_EQ(0, rmdir(dir_s1d3)); 2114 } 2115 2116 TEST_F_FORK(layout1, reparent_refer) 2117 { 2118 const struct rule layer1[] = { 2119 { 2120 .path = dir_s1d2, 2121 .access = LANDLOCK_ACCESS_FS_REFER, 2122 }, 2123 { 2124 .path = dir_s2d2, 2125 .access = LANDLOCK_ACCESS_FS_REFER, 2126 }, 2127 {}, 2128 }; 2129 int ruleset_fd = 2130 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2131 2132 ASSERT_LE(0, ruleset_fd); 2133 enforce_ruleset(_metadata, ruleset_fd); 2134 ASSERT_EQ(0, close(ruleset_fd)); 2135 2136 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2137 ASSERT_EQ(EXDEV, errno); 2138 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2139 ASSERT_EQ(EXDEV, errno); 2140 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2141 ASSERT_EQ(EXDEV, errno); 2142 2143 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2144 ASSERT_EQ(EXDEV, errno); 2145 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2146 ASSERT_EQ(EXDEV, errno); 2147 /* 2148 * Moving should only be allowed when the source and the destination 2149 * parent directory have REFER. 2150 */ 2151 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2152 ASSERT_EQ(ENOTEMPTY, errno); 2153 ASSERT_EQ(0, unlink(file1_s2d3)); 2154 ASSERT_EQ(0, unlink(file2_s2d3)); 2155 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2156 } 2157 2158 /* Checks renames beneath dir_s1d1. */ 2159 static void refer_denied_by_default(struct __test_metadata *const _metadata, 2160 const struct rule layer1[], 2161 const int layer1_err, 2162 const struct rule layer2[]) 2163 { 2164 int ruleset_fd; 2165 2166 ASSERT_EQ(0, unlink(file1_s1d2)); 2167 2168 ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 2169 ASSERT_LE(0, ruleset_fd); 2170 enforce_ruleset(_metadata, ruleset_fd); 2171 ASSERT_EQ(0, close(ruleset_fd)); 2172 2173 /* 2174 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 2175 * layer1_err), then it allows some different-parent renames and links. 2176 */ 2177 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 2178 if (layer1_err == 0) 2179 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 2180 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 2181 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 2182 2183 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 2184 ASSERT_LE(0, ruleset_fd); 2185 enforce_ruleset(_metadata, ruleset_fd); 2186 ASSERT_EQ(0, close(ruleset_fd)); 2187 2188 /* 2189 * Now, either the first or the second layer does not handle 2190 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 2191 * renames and links are denied, thus making the layer handling 2192 * LANDLOCK_ACCESS_FS_REFER null and void. 2193 */ 2194 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 2195 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 2196 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 2197 } 2198 2199 const struct rule layer_dir_s1d1_refer[] = { 2200 { 2201 .path = dir_s1d1, 2202 .access = LANDLOCK_ACCESS_FS_REFER, 2203 }, 2204 {}, 2205 }; 2206 2207 const struct rule layer_dir_s1d1_execute[] = { 2208 { 2209 /* Matches a parent directory. */ 2210 .path = dir_s1d1, 2211 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2212 }, 2213 {}, 2214 }; 2215 2216 const struct rule layer_dir_s2d1_execute[] = { 2217 { 2218 /* Does not match a parent directory. */ 2219 .path = dir_s2d1, 2220 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2221 }, 2222 {}, 2223 }; 2224 2225 /* 2226 * Tests precedence over renames: denied by default for different parent 2227 * directories, *with* a rule matching a parent directory, but not directly 2228 * denying access (with MAKE_REG nor REMOVE). 2229 */ 2230 TEST_F_FORK(layout1, refer_denied_by_default1) 2231 { 2232 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2233 layer_dir_s1d1_execute); 2234 } 2235 2236 /* 2237 * Same test but this time turning around the ABI version order: the first 2238 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2239 */ 2240 TEST_F_FORK(layout1, refer_denied_by_default2) 2241 { 2242 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 2243 layer_dir_s1d1_refer); 2244 } 2245 2246 /* 2247 * Tests precedence over renames: denied by default for different parent 2248 * directories, *without* a rule matching a parent directory, but not directly 2249 * denying access (with MAKE_REG nor REMOVE). 2250 */ 2251 TEST_F_FORK(layout1, refer_denied_by_default3) 2252 { 2253 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2254 layer_dir_s2d1_execute); 2255 } 2256 2257 /* 2258 * Same test but this time turning around the ABI version order: the first 2259 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2260 */ 2261 TEST_F_FORK(layout1, refer_denied_by_default4) 2262 { 2263 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 2264 layer_dir_s1d1_refer); 2265 } 2266 2267 TEST_F_FORK(layout1, reparent_link) 2268 { 2269 const struct rule layer1[] = { 2270 { 2271 .path = dir_s1d2, 2272 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2273 }, 2274 { 2275 .path = dir_s1d3, 2276 .access = LANDLOCK_ACCESS_FS_REFER, 2277 }, 2278 { 2279 .path = dir_s2d2, 2280 .access = LANDLOCK_ACCESS_FS_REFER, 2281 }, 2282 { 2283 .path = dir_s2d3, 2284 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2285 }, 2286 {}, 2287 }; 2288 const int ruleset_fd = create_ruleset( 2289 _metadata, 2290 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2291 2292 ASSERT_LE(0, ruleset_fd); 2293 enforce_ruleset(_metadata, ruleset_fd); 2294 ASSERT_EQ(0, close(ruleset_fd)); 2295 2296 ASSERT_EQ(0, unlink(file1_s1d1)); 2297 ASSERT_EQ(0, unlink(file1_s1d2)); 2298 ASSERT_EQ(0, unlink(file1_s1d3)); 2299 2300 /* Denies linking because of missing MAKE_REG. */ 2301 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2302 ASSERT_EQ(EACCES, errno); 2303 /* Denies linking because of missing source and destination REFER. */ 2304 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2305 ASSERT_EQ(EXDEV, errno); 2306 /* Denies linking because of missing source REFER. */ 2307 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2308 ASSERT_EQ(EXDEV, errno); 2309 2310 /* Denies linking because of missing MAKE_REG. */ 2311 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2312 ASSERT_EQ(EACCES, errno); 2313 /* Denies linking because of missing destination REFER. */ 2314 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2315 ASSERT_EQ(EXDEV, errno); 2316 2317 /* Allows linking because of REFER and MAKE_REG. */ 2318 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2319 ASSERT_EQ(0, unlink(file1_s2d2)); 2320 /* Reverse linking denied because of missing MAKE_REG. */ 2321 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2322 ASSERT_EQ(EACCES, errno); 2323 ASSERT_EQ(0, unlink(file1_s2d3)); 2324 /* Checks reverse linking. */ 2325 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2326 ASSERT_EQ(0, unlink(file1_s1d3)); 2327 2328 /* 2329 * This is OK for a file link, but it should not be allowed for a 2330 * directory rename (because of the superset of access rights. 2331 */ 2332 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2333 ASSERT_EQ(0, unlink(file1_s1d3)); 2334 2335 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2336 ASSERT_EQ(EXDEV, errno); 2337 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2338 ASSERT_EQ(EXDEV, errno); 2339 2340 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2341 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2342 } 2343 2344 TEST_F_FORK(layout1, reparent_rename) 2345 { 2346 /* Same rules as for reparent_link. */ 2347 const struct rule layer1[] = { 2348 { 2349 .path = dir_s1d2, 2350 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2351 }, 2352 { 2353 .path = dir_s1d3, 2354 .access = LANDLOCK_ACCESS_FS_REFER, 2355 }, 2356 { 2357 .path = dir_s2d2, 2358 .access = LANDLOCK_ACCESS_FS_REFER, 2359 }, 2360 { 2361 .path = dir_s2d3, 2362 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2363 }, 2364 {}, 2365 }; 2366 const int ruleset_fd = create_ruleset( 2367 _metadata, 2368 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2369 2370 ASSERT_LE(0, ruleset_fd); 2371 enforce_ruleset(_metadata, ruleset_fd); 2372 ASSERT_EQ(0, close(ruleset_fd)); 2373 2374 ASSERT_EQ(0, unlink(file1_s1d2)); 2375 ASSERT_EQ(0, unlink(file1_s1d3)); 2376 2377 /* Denies renaming because of missing MAKE_REG. */ 2378 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2379 RENAME_EXCHANGE)); 2380 ASSERT_EQ(EACCES, errno); 2381 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2382 RENAME_EXCHANGE)); 2383 ASSERT_EQ(EACCES, errno); 2384 ASSERT_EQ(0, unlink(file1_s1d1)); 2385 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2386 ASSERT_EQ(EACCES, errno); 2387 /* Even denies same file exchange. */ 2388 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2389 RENAME_EXCHANGE)); 2390 ASSERT_EQ(EACCES, errno); 2391 2392 /* Denies renaming because of missing source and destination REFER. */ 2393 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2394 ASSERT_EQ(EXDEV, errno); 2395 /* 2396 * Denies renaming because of missing MAKE_REG, source and destination 2397 * REFER. 2398 */ 2399 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2400 RENAME_EXCHANGE)); 2401 ASSERT_EQ(EACCES, errno); 2402 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2403 RENAME_EXCHANGE)); 2404 ASSERT_EQ(EACCES, errno); 2405 2406 /* Denies renaming because of missing source REFER. */ 2407 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2408 ASSERT_EQ(EXDEV, errno); 2409 /* Denies renaming because of missing MAKE_REG. */ 2410 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2411 RENAME_EXCHANGE)); 2412 ASSERT_EQ(EACCES, errno); 2413 2414 /* Denies renaming because of missing MAKE_REG. */ 2415 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2416 ASSERT_EQ(EACCES, errno); 2417 /* Denies renaming because of missing destination REFER*/ 2418 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2419 ASSERT_EQ(EXDEV, errno); 2420 2421 /* Denies exchange because of one missing MAKE_REG. */ 2422 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2423 RENAME_EXCHANGE)); 2424 ASSERT_EQ(EACCES, errno); 2425 /* Allows renaming because of REFER and MAKE_REG. */ 2426 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2427 2428 /* Reverse renaming denied because of missing MAKE_REG. */ 2429 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2430 ASSERT_EQ(EACCES, errno); 2431 ASSERT_EQ(0, unlink(file1_s2d3)); 2432 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2433 2434 /* Tests reverse renaming. */ 2435 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2436 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2437 RENAME_EXCHANGE)); 2438 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2439 2440 /* 2441 * This is OK for a file rename, but it should not be allowed for a 2442 * directory rename (because of the superset of access rights). 2443 */ 2444 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2445 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2446 2447 /* 2448 * Tests superset restrictions applied to directories. Not only the 2449 * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2450 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2451 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2452 * directly by the moved dir_s2d3. 2453 */ 2454 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2455 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2456 /* 2457 * The first rename is allowed but not the exchange because dir_s1d3's 2458 * parent (dir_s1d2) doesn't have REFER. 2459 */ 2460 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2461 RENAME_EXCHANGE)); 2462 ASSERT_EQ(EXDEV, errno); 2463 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2464 RENAME_EXCHANGE)); 2465 ASSERT_EQ(EXDEV, errno); 2466 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2467 ASSERT_EQ(EXDEV, errno); 2468 2469 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2470 ASSERT_EQ(EXDEV, errno); 2471 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2472 ASSERT_EQ(EXDEV, errno); 2473 2474 /* Renaming in the same directory is always allowed. */ 2475 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2476 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2477 2478 ASSERT_EQ(0, unlink(file1_s1d2)); 2479 /* Denies because of missing source MAKE_REG and destination REFER. */ 2480 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2481 ASSERT_EQ(EXDEV, errno); 2482 2483 ASSERT_EQ(0, unlink(file1_s1d3)); 2484 /* Denies because of missing source MAKE_REG and REFER. */ 2485 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2486 ASSERT_EQ(EXDEV, errno); 2487 } 2488 2489 static void 2490 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2491 { 2492 const struct rule layer1[] = { 2493 { 2494 .path = dir_s1d2, 2495 .access = LANDLOCK_ACCESS_FS_REFER, 2496 }, 2497 { 2498 /* Interesting for the layer2 tests. */ 2499 .path = dir_s1d3, 2500 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2501 }, 2502 { 2503 .path = dir_s2d2, 2504 .access = LANDLOCK_ACCESS_FS_REFER, 2505 }, 2506 { 2507 .path = dir_s2d3, 2508 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2509 }, 2510 {}, 2511 }; 2512 const int ruleset_fd = create_ruleset( 2513 _metadata, 2514 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2515 2516 ASSERT_LE(0, ruleset_fd); 2517 enforce_ruleset(_metadata, ruleset_fd); 2518 ASSERT_EQ(0, close(ruleset_fd)); 2519 } 2520 2521 static void 2522 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2523 { 2524 const struct rule layer2[] = { 2525 { 2526 .path = dir_s2d3, 2527 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2528 }, 2529 {}, 2530 }; 2531 /* 2532 * Same checks as before but with a second layer and a new MAKE_DIR 2533 * rule (and no explicit handling of REFER). 2534 */ 2535 const int ruleset_fd = 2536 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2537 2538 ASSERT_LE(0, ruleset_fd); 2539 enforce_ruleset(_metadata, ruleset_fd); 2540 ASSERT_EQ(0, close(ruleset_fd)); 2541 } 2542 2543 TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2544 { 2545 ASSERT_EQ(0, unlink(file1_s2d2)); 2546 ASSERT_EQ(0, unlink(file1_s2d3)); 2547 2548 reparent_exdev_layers_enforce1(_metadata); 2549 2550 /* 2551 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2552 * because it doesn't inherit new access rights. 2553 */ 2554 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2555 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2556 2557 /* 2558 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2559 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2560 * already allowed for dir_s1d3. 2561 */ 2562 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2563 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2564 2565 /* 2566 * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2567 * because it cannot inherit MAKE_REG right (which is dedicated to 2568 * directories). 2569 */ 2570 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2571 2572 reparent_exdev_layers_enforce2(_metadata); 2573 2574 /* 2575 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2576 * MAKE_DIR is not tied to dir_s2d2. 2577 */ 2578 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2579 ASSERT_EQ(EACCES, errno); 2580 2581 /* 2582 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2583 * would grants MAKE_REG and MAKE_DIR rights to it. 2584 */ 2585 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2586 ASSERT_EQ(EXDEV, errno); 2587 2588 /* 2589 * Moving the file2_s1d3 file below dir_s2d3 is denied because the 2590 * second layer does not handle REFER, which is always denied by 2591 * default. 2592 */ 2593 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 2594 ASSERT_EQ(EXDEV, errno); 2595 } 2596 2597 TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2598 { 2599 reparent_exdev_layers_enforce1(_metadata); 2600 2601 /* Checks EACCES predominance over EXDEV. */ 2602 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2603 ASSERT_EQ(EACCES, errno); 2604 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2605 ASSERT_EQ(EACCES, errno); 2606 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2607 ASSERT_EQ(EXDEV, errno); 2608 /* Modify layout! */ 2609 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 2610 2611 /* Without REFER source. */ 2612 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2613 ASSERT_EQ(EXDEV, errno); 2614 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2615 ASSERT_EQ(EXDEV, errno); 2616 2617 reparent_exdev_layers_enforce2(_metadata); 2618 2619 /* Checks EACCES predominance over EXDEV. */ 2620 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2621 ASSERT_EQ(EACCES, errno); 2622 /* Checks with actual file2_s1d2. */ 2623 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 2624 ASSERT_EQ(EACCES, errno); 2625 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2626 ASSERT_EQ(EXDEV, errno); 2627 /* 2628 * Modifying the layout is now denied because the second layer does not 2629 * handle REFER, which is always denied by default. 2630 */ 2631 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2632 ASSERT_EQ(EXDEV, errno); 2633 2634 /* Without REFER source, EACCES wins over EXDEV. */ 2635 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2636 ASSERT_EQ(EACCES, errno); 2637 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2638 ASSERT_EQ(EACCES, errno); 2639 } 2640 2641 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 2642 { 2643 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 2644 file2_s2d3; 2645 2646 ASSERT_EQ(0, unlink(file1_s1d2)); 2647 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2648 ASSERT_EQ(0, unlink(file2_s2d3)); 2649 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2650 2651 reparent_exdev_layers_enforce1(_metadata); 2652 2653 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2654 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2655 RENAME_EXCHANGE)); 2656 ASSERT_EQ(EACCES, errno); 2657 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2658 RENAME_EXCHANGE)); 2659 ASSERT_EQ(EACCES, errno); 2660 2661 /* 2662 * Checks with directories which creation could be allowed, but denied 2663 * because of access rights that would be inherited. 2664 */ 2665 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2666 dir_file2_s2d3, RENAME_EXCHANGE)); 2667 ASSERT_EQ(EXDEV, errno); 2668 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2669 dir_file1_s1d2, RENAME_EXCHANGE)); 2670 ASSERT_EQ(EXDEV, errno); 2671 2672 /* Checks with same access rights. */ 2673 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2674 RENAME_EXCHANGE)); 2675 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2676 RENAME_EXCHANGE)); 2677 2678 /* Checks with different (child-only) access rights. */ 2679 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2680 RENAME_EXCHANGE)); 2681 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2682 RENAME_EXCHANGE)); 2683 2684 /* 2685 * Checks that exchange between file and directory are consistent. 2686 * 2687 * Moving a file (file1_s2d2) to a directory which only grants more 2688 * directory-related access rights is allowed, and at the same time 2689 * moving a directory (dir_file2_s2d3) to another directory which 2690 * grants less access rights is allowed too. 2691 * 2692 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 2693 */ 2694 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2695 RENAME_EXCHANGE)); 2696 /* 2697 * However, moving back the directory is denied because it would get 2698 * more access rights than the current state and because file creation 2699 * is forbidden (in dir_s2d2). 2700 */ 2701 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2702 RENAME_EXCHANGE)); 2703 ASSERT_EQ(EACCES, errno); 2704 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2705 RENAME_EXCHANGE)); 2706 ASSERT_EQ(EACCES, errno); 2707 2708 reparent_exdev_layers_enforce2(_metadata); 2709 2710 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2711 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2712 RENAME_EXCHANGE)); 2713 ASSERT_EQ(EACCES, errno); 2714 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2715 RENAME_EXCHANGE)); 2716 ASSERT_EQ(EACCES, errno); 2717 2718 /* Checks with directories which creation is now denied. */ 2719 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2720 dir_file2_s2d3, RENAME_EXCHANGE)); 2721 ASSERT_EQ(EACCES, errno); 2722 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2723 dir_file1_s1d2, RENAME_EXCHANGE)); 2724 ASSERT_EQ(EACCES, errno); 2725 2726 /* Checks with different (child-only) access rights. */ 2727 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2728 RENAME_EXCHANGE)); 2729 /* Denied because of MAKE_DIR. */ 2730 ASSERT_EQ(EACCES, errno); 2731 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2732 RENAME_EXCHANGE)); 2733 ASSERT_EQ(EACCES, errno); 2734 2735 /* Checks with different (child-only) access rights. */ 2736 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2737 RENAME_EXCHANGE)); 2738 /* Denied because of MAKE_DIR. */ 2739 ASSERT_EQ(EACCES, errno); 2740 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2741 RENAME_EXCHANGE)); 2742 ASSERT_EQ(EACCES, errno); 2743 2744 /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 2745 } 2746 2747 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 2748 { 2749 const char *const dir_file2_s2d3 = file2_s2d3; 2750 2751 ASSERT_EQ(0, unlink(file2_s2d3)); 2752 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2753 2754 reparent_exdev_layers_enforce1(_metadata); 2755 reparent_exdev_layers_enforce2(_metadata); 2756 2757 /* Checks that exchange between file and directory are consistent. */ 2758 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2759 RENAME_EXCHANGE)); 2760 ASSERT_EQ(EACCES, errno); 2761 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2762 RENAME_EXCHANGE)); 2763 ASSERT_EQ(EACCES, errno); 2764 } 2765 2766 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 2767 { 2768 const char *const dir_file2_s2d3 = file2_s2d3; 2769 2770 ASSERT_EQ(0, unlink(file2_s2d3)); 2771 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2772 2773 reparent_exdev_layers_enforce1(_metadata); 2774 2775 /* 2776 * Checks that exchange between file and directory are consistent, 2777 * including with inverted arguments (see 2778 * layout1.reparent_exdev_layers_exchange1). 2779 */ 2780 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2781 RENAME_EXCHANGE)); 2782 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2783 RENAME_EXCHANGE)); 2784 ASSERT_EQ(EACCES, errno); 2785 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2786 RENAME_EXCHANGE)); 2787 ASSERT_EQ(EACCES, errno); 2788 } 2789 2790 TEST_F_FORK(layout1, reparent_remove) 2791 { 2792 const struct rule layer1[] = { 2793 { 2794 .path = dir_s1d1, 2795 .access = LANDLOCK_ACCESS_FS_REFER | 2796 LANDLOCK_ACCESS_FS_REMOVE_DIR, 2797 }, 2798 { 2799 .path = dir_s1d2, 2800 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2801 }, 2802 { 2803 .path = dir_s2d1, 2804 .access = LANDLOCK_ACCESS_FS_REFER | 2805 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2806 }, 2807 {}, 2808 }; 2809 const int ruleset_fd = create_ruleset( 2810 _metadata, 2811 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 2812 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2813 layer1); 2814 2815 ASSERT_LE(0, ruleset_fd); 2816 enforce_ruleset(_metadata, ruleset_fd); 2817 ASSERT_EQ(0, close(ruleset_fd)); 2818 2819 /* Access denied because of wrong/swapped remove file/dir. */ 2820 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 2821 ASSERT_EQ(EACCES, errno); 2822 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 2823 ASSERT_EQ(EACCES, errno); 2824 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 2825 RENAME_EXCHANGE)); 2826 ASSERT_EQ(EACCES, errno); 2827 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 2828 RENAME_EXCHANGE)); 2829 ASSERT_EQ(EACCES, errno); 2830 2831 /* Access allowed thanks to the matching rights. */ 2832 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 2833 ASSERT_EQ(EISDIR, errno); 2834 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 2835 ASSERT_EQ(ENOTDIR, errno); 2836 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2837 ASSERT_EQ(ENOTDIR, errno); 2838 ASSERT_EQ(0, unlink(file1_s2d1)); 2839 ASSERT_EQ(0, unlink(file1_s1d3)); 2840 ASSERT_EQ(0, unlink(file2_s1d3)); 2841 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 2842 2843 /* Effectively removes a file and a directory by exchanging them. */ 2844 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2845 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2846 RENAME_EXCHANGE)); 2847 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2848 RENAME_EXCHANGE)); 2849 ASSERT_EQ(EACCES, errno); 2850 } 2851 2852 TEST_F_FORK(layout1, reparent_dom_superset) 2853 { 2854 const struct rule layer1[] = { 2855 { 2856 .path = dir_s1d2, 2857 .access = LANDLOCK_ACCESS_FS_REFER, 2858 }, 2859 { 2860 .path = file1_s1d2, 2861 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2862 }, 2863 { 2864 .path = dir_s1d3, 2865 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 2866 LANDLOCK_ACCESS_FS_EXECUTE, 2867 }, 2868 { 2869 .path = dir_s2d2, 2870 .access = LANDLOCK_ACCESS_FS_REFER | 2871 LANDLOCK_ACCESS_FS_EXECUTE | 2872 LANDLOCK_ACCESS_FS_MAKE_SOCK, 2873 }, 2874 { 2875 .path = dir_s2d3, 2876 .access = LANDLOCK_ACCESS_FS_READ_FILE | 2877 LANDLOCK_ACCESS_FS_MAKE_FIFO, 2878 }, 2879 {}, 2880 }; 2881 int ruleset_fd = create_ruleset(_metadata, 2882 LANDLOCK_ACCESS_FS_REFER | 2883 LANDLOCK_ACCESS_FS_EXECUTE | 2884 LANDLOCK_ACCESS_FS_MAKE_SOCK | 2885 LANDLOCK_ACCESS_FS_READ_FILE | 2886 LANDLOCK_ACCESS_FS_MAKE_FIFO, 2887 layer1); 2888 2889 ASSERT_LE(0, ruleset_fd); 2890 enforce_ruleset(_metadata, ruleset_fd); 2891 ASSERT_EQ(0, close(ruleset_fd)); 2892 2893 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 2894 ASSERT_EQ(EXDEV, errno); 2895 /* 2896 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 2897 * access right. 2898 */ 2899 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 2900 ASSERT_EQ(EXDEV, errno); 2901 /* 2902 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 2903 * superset of access rights compared to dir_s1d2, because file1_s1d2 2904 * already has these access rights anyway. 2905 */ 2906 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 2907 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 2908 2909 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2910 ASSERT_EQ(EXDEV, errno); 2911 /* 2912 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 2913 * right. 2914 */ 2915 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2916 ASSERT_EQ(EXDEV, errno); 2917 /* 2918 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 2919 * of access rights compared to dir_s1d2, because dir_s1d3 already has 2920 * these access rights anyway. 2921 */ 2922 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2923 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2924 2925 /* 2926 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 2927 * will be denied because the new inherited access rights from dir_s1d2 2928 * will be less than the destination (original) dir_s2d3. This is a 2929 * sinkhole scenario where we cannot move back files or directories. 2930 */ 2931 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 2932 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2933 ASSERT_EQ(EXDEV, errno); 2934 ASSERT_EQ(0, unlink(file2_s1d2)); 2935 ASSERT_EQ(0, unlink(file2_s2d3)); 2936 /* 2937 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 2938 * MAKE_SOCK which were inherited from dir_s1d3. 2939 */ 2940 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 2941 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 2942 ASSERT_EQ(EXDEV, errno); 2943 } 2944 2945 TEST_F_FORK(layout1, remove_dir) 2946 { 2947 const struct rule rules[] = { 2948 { 2949 .path = dir_s1d2, 2950 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2951 }, 2952 {}, 2953 }; 2954 const int ruleset_fd = 2955 create_ruleset(_metadata, rules[0].access, rules); 2956 2957 ASSERT_LE(0, ruleset_fd); 2958 2959 ASSERT_EQ(0, unlink(file1_s1d1)); 2960 ASSERT_EQ(0, unlink(file1_s1d2)); 2961 ASSERT_EQ(0, unlink(file1_s1d3)); 2962 ASSERT_EQ(0, unlink(file2_s1d3)); 2963 2964 enforce_ruleset(_metadata, ruleset_fd); 2965 ASSERT_EQ(0, close(ruleset_fd)); 2966 2967 ASSERT_EQ(0, rmdir(dir_s1d3)); 2968 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2969 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 2970 2971 /* dir_s1d2 itself cannot be removed. */ 2972 ASSERT_EQ(-1, rmdir(dir_s1d2)); 2973 ASSERT_EQ(EACCES, errno); 2974 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 2975 ASSERT_EQ(EACCES, errno); 2976 ASSERT_EQ(-1, rmdir(dir_s1d1)); 2977 ASSERT_EQ(EACCES, errno); 2978 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 2979 ASSERT_EQ(EACCES, errno); 2980 } 2981 2982 TEST_F_FORK(layout1, remove_file) 2983 { 2984 const struct rule rules[] = { 2985 { 2986 .path = dir_s1d2, 2987 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2988 }, 2989 {}, 2990 }; 2991 const int ruleset_fd = 2992 create_ruleset(_metadata, rules[0].access, rules); 2993 2994 ASSERT_LE(0, ruleset_fd); 2995 enforce_ruleset(_metadata, ruleset_fd); 2996 ASSERT_EQ(0, close(ruleset_fd)); 2997 2998 ASSERT_EQ(-1, unlink(file1_s1d1)); 2999 ASSERT_EQ(EACCES, errno); 3000 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 3001 ASSERT_EQ(EACCES, errno); 3002 ASSERT_EQ(0, unlink(file1_s1d2)); 3003 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 3004 } 3005 3006 static void test_make_file(struct __test_metadata *const _metadata, 3007 const __u64 access, const mode_t mode, 3008 const dev_t dev) 3009 { 3010 const struct rule rules[] = { 3011 { 3012 .path = dir_s1d2, 3013 .access = access, 3014 }, 3015 {}, 3016 }; 3017 const int ruleset_fd = create_ruleset(_metadata, access, rules); 3018 3019 ASSERT_LE(0, ruleset_fd); 3020 3021 ASSERT_EQ(0, unlink(file1_s1d1)); 3022 ASSERT_EQ(0, unlink(file2_s1d1)); 3023 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 3024 { 3025 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 3026 strerror(errno)); 3027 }; 3028 3029 ASSERT_EQ(0, unlink(file1_s1d2)); 3030 ASSERT_EQ(0, unlink(file2_s1d2)); 3031 3032 ASSERT_EQ(0, unlink(file1_s1d3)); 3033 ASSERT_EQ(0, unlink(file2_s1d3)); 3034 3035 enforce_ruleset(_metadata, ruleset_fd); 3036 ASSERT_EQ(0, close(ruleset_fd)); 3037 3038 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 3039 ASSERT_EQ(EACCES, errno); 3040 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3041 ASSERT_EQ(EACCES, errno); 3042 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3043 ASSERT_EQ(EACCES, errno); 3044 3045 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 3046 { 3047 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 3048 strerror(errno)); 3049 }; 3050 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3051 ASSERT_EQ(0, unlink(file2_s1d2)); 3052 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3053 3054 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 3055 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3056 ASSERT_EQ(0, unlink(file2_s1d3)); 3057 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3058 } 3059 3060 TEST_F_FORK(layout1, make_char) 3061 { 3062 /* Creates a /dev/null device. */ 3063 set_cap(_metadata, CAP_MKNOD); 3064 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 3065 makedev(1, 3)); 3066 } 3067 3068 TEST_F_FORK(layout1, make_block) 3069 { 3070 /* Creates a /dev/loop0 device. */ 3071 set_cap(_metadata, CAP_MKNOD); 3072 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 3073 makedev(7, 0)); 3074 } 3075 3076 TEST_F_FORK(layout1, make_reg_1) 3077 { 3078 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 3079 } 3080 3081 TEST_F_FORK(layout1, make_reg_2) 3082 { 3083 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 3084 } 3085 3086 TEST_F_FORK(layout1, make_sock) 3087 { 3088 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 3089 } 3090 3091 TEST_F_FORK(layout1, make_fifo) 3092 { 3093 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 3094 } 3095 3096 TEST_F_FORK(layout1, make_sym) 3097 { 3098 const struct rule rules[] = { 3099 { 3100 .path = dir_s1d2, 3101 .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 3102 }, 3103 {}, 3104 }; 3105 const int ruleset_fd = 3106 create_ruleset(_metadata, rules[0].access, rules); 3107 3108 ASSERT_LE(0, ruleset_fd); 3109 3110 ASSERT_EQ(0, unlink(file1_s1d1)); 3111 ASSERT_EQ(0, unlink(file2_s1d1)); 3112 ASSERT_EQ(0, symlink("none", file2_s1d1)); 3113 3114 ASSERT_EQ(0, unlink(file1_s1d2)); 3115 ASSERT_EQ(0, unlink(file2_s1d2)); 3116 3117 ASSERT_EQ(0, unlink(file1_s1d3)); 3118 ASSERT_EQ(0, unlink(file2_s1d3)); 3119 3120 enforce_ruleset(_metadata, ruleset_fd); 3121 ASSERT_EQ(0, close(ruleset_fd)); 3122 3123 ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3124 ASSERT_EQ(EACCES, errno); 3125 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3126 ASSERT_EQ(EACCES, errno); 3127 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3128 ASSERT_EQ(EACCES, errno); 3129 3130 ASSERT_EQ(0, symlink("none", file1_s1d2)); 3131 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3132 ASSERT_EQ(0, unlink(file2_s1d2)); 3133 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3134 3135 ASSERT_EQ(0, symlink("none", file1_s1d3)); 3136 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3137 ASSERT_EQ(0, unlink(file2_s1d3)); 3138 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3139 } 3140 3141 TEST_F_FORK(layout1, make_dir) 3142 { 3143 const struct rule rules[] = { 3144 { 3145 .path = dir_s1d2, 3146 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3147 }, 3148 {}, 3149 }; 3150 const int ruleset_fd = 3151 create_ruleset(_metadata, rules[0].access, rules); 3152 3153 ASSERT_LE(0, ruleset_fd); 3154 3155 ASSERT_EQ(0, unlink(file1_s1d1)); 3156 ASSERT_EQ(0, unlink(file1_s1d2)); 3157 ASSERT_EQ(0, unlink(file1_s1d3)); 3158 3159 enforce_ruleset(_metadata, ruleset_fd); 3160 ASSERT_EQ(0, close(ruleset_fd)); 3161 3162 /* Uses file_* as directory names. */ 3163 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3164 ASSERT_EQ(EACCES, errno); 3165 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3166 ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3167 } 3168 3169 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3170 const int open_flags) 3171 { 3172 static const char path_template[] = "/proc/self/fd/%d"; 3173 char procfd_path[sizeof(path_template) + 10]; 3174 const int procfd_path_size = 3175 snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3176 3177 ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3178 return open(procfd_path, open_flags); 3179 } 3180 3181 TEST_F_FORK(layout1, proc_unlinked_file) 3182 { 3183 const struct rule rules[] = { 3184 { 3185 .path = file1_s1d2, 3186 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3187 }, 3188 {}, 3189 }; 3190 int reg_fd, proc_fd; 3191 const int ruleset_fd = create_ruleset( 3192 _metadata, 3193 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3194 rules); 3195 3196 ASSERT_LE(0, ruleset_fd); 3197 enforce_ruleset(_metadata, ruleset_fd); 3198 ASSERT_EQ(0, close(ruleset_fd)); 3199 3200 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3201 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3202 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3203 ASSERT_LE(0, reg_fd); 3204 ASSERT_EQ(0, unlink(file1_s1d2)); 3205 3206 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3207 ASSERT_LE(0, proc_fd); 3208 ASSERT_EQ(0, close(proc_fd)); 3209 3210 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3211 ASSERT_EQ(-1, proc_fd) 3212 { 3213 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3214 strerror(errno)); 3215 } 3216 ASSERT_EQ(EACCES, errno); 3217 3218 ASSERT_EQ(0, close(reg_fd)); 3219 } 3220 3221 TEST_F_FORK(layout1, proc_pipe) 3222 { 3223 int proc_fd; 3224 int pipe_fds[2]; 3225 char buf = '\0'; 3226 const struct rule rules[] = { 3227 { 3228 .path = dir_s1d2, 3229 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3230 LANDLOCK_ACCESS_FS_WRITE_FILE, 3231 }, 3232 {}, 3233 }; 3234 /* Limits read and write access to files tied to the filesystem. */ 3235 const int ruleset_fd = 3236 create_ruleset(_metadata, rules[0].access, rules); 3237 3238 ASSERT_LE(0, ruleset_fd); 3239 enforce_ruleset(_metadata, ruleset_fd); 3240 ASSERT_EQ(0, close(ruleset_fd)); 3241 3242 /* Checks enforcement for normal files. */ 3243 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3244 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3245 3246 /* Checks access to pipes through FD. */ 3247 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3248 ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3249 { 3250 TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3251 } 3252 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3253 ASSERT_EQ('.', buf); 3254 3255 /* Checks write access to pipe through /proc/self/fd . */ 3256 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3257 ASSERT_LE(0, proc_fd); 3258 ASSERT_EQ(1, write(proc_fd, ".", 1)) 3259 { 3260 TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3261 pipe_fds[1], strerror(errno)); 3262 } 3263 ASSERT_EQ(0, close(proc_fd)); 3264 3265 /* Checks read access to pipe through /proc/self/fd . */ 3266 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3267 ASSERT_LE(0, proc_fd); 3268 buf = '\0'; 3269 ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3270 { 3271 TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3272 pipe_fds[1], strerror(errno)); 3273 } 3274 ASSERT_EQ(0, close(proc_fd)); 3275 3276 ASSERT_EQ(0, close(pipe_fds[0])); 3277 ASSERT_EQ(0, close(pipe_fds[1])); 3278 } 3279 3280 /* Invokes truncate(2) and returns its errno or 0. */ 3281 static int test_truncate(const char *const path) 3282 { 3283 if (truncate(path, 10) < 0) 3284 return errno; 3285 return 0; 3286 } 3287 3288 /* 3289 * Invokes creat(2) and returns its errno or 0. 3290 * Closes the opened file descriptor on success. 3291 */ 3292 static int test_creat(const char *const path) 3293 { 3294 int fd = creat(path, 0600); 3295 3296 if (fd < 0) 3297 return errno; 3298 3299 /* 3300 * Mixing error codes from close(2) and creat(2) should not lead to any 3301 * (access type) confusion for this test. 3302 */ 3303 if (close(fd) < 0) 3304 return errno; 3305 return 0; 3306 } 3307 3308 /* 3309 * Exercises file truncation when it's not restricted, 3310 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 3311 */ 3312 TEST_F_FORK(layout1, truncate_unhandled) 3313 { 3314 const char *const file_r = file1_s1d1; 3315 const char *const file_w = file2_s1d1; 3316 const char *const file_none = file1_s1d2; 3317 const struct rule rules[] = { 3318 { 3319 .path = file_r, 3320 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3321 }, 3322 { 3323 .path = file_w, 3324 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3325 }, 3326 /* Implicitly: No rights for file_none. */ 3327 {}, 3328 }; 3329 3330 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3331 LANDLOCK_ACCESS_FS_WRITE_FILE; 3332 int ruleset_fd; 3333 3334 /* Enable Landlock. */ 3335 ruleset_fd = create_ruleset(_metadata, handled, rules); 3336 3337 ASSERT_LE(0, ruleset_fd); 3338 enforce_ruleset(_metadata, ruleset_fd); 3339 ASSERT_EQ(0, close(ruleset_fd)); 3340 3341 /* 3342 * Checks read right: truncate and open with O_TRUNC work, unless the 3343 * file is attempted to be opened for writing. 3344 */ 3345 EXPECT_EQ(0, test_truncate(file_r)); 3346 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 3347 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 3348 EXPECT_EQ(EACCES, test_creat(file_r)); 3349 3350 /* 3351 * Checks write right: truncate and open with O_TRUNC work, unless the 3352 * file is attempted to be opened for reading. 3353 */ 3354 EXPECT_EQ(0, test_truncate(file_w)); 3355 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 3356 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 3357 EXPECT_EQ(0, test_creat(file_w)); 3358 3359 /* 3360 * Checks "no rights" case: truncate works but all open attempts fail, 3361 * including creat. 3362 */ 3363 EXPECT_EQ(0, test_truncate(file_none)); 3364 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3365 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3366 EXPECT_EQ(EACCES, test_creat(file_none)); 3367 } 3368 3369 TEST_F_FORK(layout1, truncate) 3370 { 3371 const char *const file_rwt = file1_s1d1; 3372 const char *const file_rw = file2_s1d1; 3373 const char *const file_rt = file1_s1d2; 3374 const char *const file_t = file2_s1d2; 3375 const char *const file_none = file1_s1d3; 3376 const char *const dir_t = dir_s2d1; 3377 const char *const file_in_dir_t = file1_s2d1; 3378 const char *const dir_w = dir_s3d1; 3379 const char *const file_in_dir_w = file1_s3d1; 3380 const struct rule rules[] = { 3381 { 3382 .path = file_rwt, 3383 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3384 LANDLOCK_ACCESS_FS_WRITE_FILE | 3385 LANDLOCK_ACCESS_FS_TRUNCATE, 3386 }, 3387 { 3388 .path = file_rw, 3389 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3390 LANDLOCK_ACCESS_FS_WRITE_FILE, 3391 }, 3392 { 3393 .path = file_rt, 3394 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3395 LANDLOCK_ACCESS_FS_TRUNCATE, 3396 }, 3397 { 3398 .path = file_t, 3399 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3400 }, 3401 /* Implicitly: No access rights for file_none. */ 3402 { 3403 .path = dir_t, 3404 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3405 }, 3406 { 3407 .path = dir_w, 3408 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3409 }, 3410 {}, 3411 }; 3412 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3413 LANDLOCK_ACCESS_FS_WRITE_FILE | 3414 LANDLOCK_ACCESS_FS_TRUNCATE; 3415 int ruleset_fd; 3416 3417 /* Enable Landlock. */ 3418 ruleset_fd = create_ruleset(_metadata, handled, rules); 3419 3420 ASSERT_LE(0, ruleset_fd); 3421 enforce_ruleset(_metadata, ruleset_fd); 3422 ASSERT_EQ(0, close(ruleset_fd)); 3423 3424 /* Checks read, write and truncate rights: truncation works. */ 3425 EXPECT_EQ(0, test_truncate(file_rwt)); 3426 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 3427 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 3428 3429 /* Checks read and write rights: no truncate variant works. */ 3430 EXPECT_EQ(EACCES, test_truncate(file_rw)); 3431 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 3432 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 3433 3434 /* 3435 * Checks read and truncate rights: truncation works. 3436 * 3437 * Note: Files can get truncated using open() even with O_RDONLY. 3438 */ 3439 EXPECT_EQ(0, test_truncate(file_rt)); 3440 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 3441 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 3442 3443 /* Checks truncate right: truncate works, but can't open file. */ 3444 EXPECT_EQ(0, test_truncate(file_t)); 3445 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 3446 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 3447 3448 /* Checks "no rights" case: No form of truncation works. */ 3449 EXPECT_EQ(EACCES, test_truncate(file_none)); 3450 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3451 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3452 3453 /* 3454 * Checks truncate right on directory: truncate works on contained 3455 * files. 3456 */ 3457 EXPECT_EQ(0, test_truncate(file_in_dir_t)); 3458 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 3459 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 3460 3461 /* 3462 * Checks creat in dir_w: This requires the truncate right when 3463 * overwriting an existing file, but does not require it when the file 3464 * is new. 3465 */ 3466 EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 3467 3468 ASSERT_EQ(0, unlink(file_in_dir_w)); 3469 EXPECT_EQ(0, test_creat(file_in_dir_w)); 3470 } 3471 3472 /* Invokes ftruncate(2) and returns its errno or 0. */ 3473 static int test_ftruncate(int fd) 3474 { 3475 if (ftruncate(fd, 10) < 0) 3476 return errno; 3477 return 0; 3478 } 3479 3480 TEST_F_FORK(layout1, ftruncate) 3481 { 3482 /* 3483 * This test opens a new file descriptor at different stages of 3484 * Landlock restriction: 3485 * 3486 * without restriction: ftruncate works 3487 * something else but truncate restricted: ftruncate works 3488 * truncate restricted and permitted: ftruncate works 3489 * truncate restricted and not permitted: ftruncate fails 3490 * 3491 * Whether this works or not is expected to depend on the time when the 3492 * FD was opened, not to depend on the time when ftruncate() was 3493 * called. 3494 */ 3495 const char *const path = file1_s1d1; 3496 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 3497 LANDLOCK_ACCESS_FS_WRITE_FILE; 3498 const struct rule layer1[] = { 3499 { 3500 .path = path, 3501 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3502 }, 3503 {}, 3504 }; 3505 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 3506 const struct rule layer2[] = { 3507 { 3508 .path = path, 3509 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3510 }, 3511 {}, 3512 }; 3513 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 3514 LANDLOCK_ACCESS_FS_WRITE_FILE; 3515 const struct rule layer3[] = { 3516 { 3517 .path = path, 3518 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3519 }, 3520 {}, 3521 }; 3522 int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; 3523 3524 fd_layer0 = open(path, O_WRONLY); 3525 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3526 3527 ruleset_fd = create_ruleset(_metadata, handled1, layer1); 3528 ASSERT_LE(0, ruleset_fd); 3529 enforce_ruleset(_metadata, ruleset_fd); 3530 ASSERT_EQ(0, close(ruleset_fd)); 3531 3532 fd_layer1 = open(path, O_WRONLY); 3533 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3534 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3535 3536 ruleset_fd = create_ruleset(_metadata, handled2, layer2); 3537 ASSERT_LE(0, ruleset_fd); 3538 enforce_ruleset(_metadata, ruleset_fd); 3539 ASSERT_EQ(0, close(ruleset_fd)); 3540 3541 fd_layer2 = open(path, O_WRONLY); 3542 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3543 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3544 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3545 3546 ruleset_fd = create_ruleset(_metadata, handled3, layer3); 3547 ASSERT_LE(0, ruleset_fd); 3548 enforce_ruleset(_metadata, ruleset_fd); 3549 ASSERT_EQ(0, close(ruleset_fd)); 3550 3551 fd_layer3 = open(path, O_WRONLY); 3552 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3553 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3554 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3555 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 3556 3557 ASSERT_EQ(0, close(fd_layer0)); 3558 ASSERT_EQ(0, close(fd_layer1)); 3559 ASSERT_EQ(0, close(fd_layer2)); 3560 ASSERT_EQ(0, close(fd_layer3)); 3561 } 3562 3563 /* clang-format off */ 3564 FIXTURE(ftruncate) {}; 3565 /* clang-format on */ 3566 3567 FIXTURE_SETUP(ftruncate) 3568 { 3569 prepare_layout(_metadata); 3570 create_file(_metadata, file1_s1d1); 3571 } 3572 3573 FIXTURE_TEARDOWN(ftruncate) 3574 { 3575 EXPECT_EQ(0, remove_path(file1_s1d1)); 3576 cleanup_layout(_metadata); 3577 } 3578 3579 FIXTURE_VARIANT(ftruncate) 3580 { 3581 const __u64 handled; 3582 const __u64 permitted; 3583 const int expected_open_result; 3584 const int expected_ftruncate_result; 3585 }; 3586 3587 /* clang-format off */ 3588 FIXTURE_VARIANT_ADD(ftruncate, w_w) { 3589 /* clang-format on */ 3590 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 3591 .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 3592 .expected_open_result = 0, 3593 .expected_ftruncate_result = 0, 3594 }; 3595 3596 /* clang-format off */ 3597 FIXTURE_VARIANT_ADD(ftruncate, t_t) { 3598 /* clang-format on */ 3599 .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 3600 .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 3601 .expected_open_result = 0, 3602 .expected_ftruncate_result = 0, 3603 }; 3604 3605 /* clang-format off */ 3606 FIXTURE_VARIANT_ADD(ftruncate, wt_w) { 3607 /* clang-format on */ 3608 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3609 .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 3610 .expected_open_result = 0, 3611 .expected_ftruncate_result = EACCES, 3612 }; 3613 3614 /* clang-format off */ 3615 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 3616 /* clang-format on */ 3617 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3618 .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE | 3619 LANDLOCK_ACCESS_FS_TRUNCATE, 3620 .expected_open_result = 0, 3621 .expected_ftruncate_result = 0, 3622 }; 3623 3624 /* clang-format off */ 3625 FIXTURE_VARIANT_ADD(ftruncate, wt_t) { 3626 /* clang-format on */ 3627 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3628 .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 3629 .expected_open_result = EACCES, 3630 }; 3631 3632 TEST_F_FORK(ftruncate, open_and_ftruncate) 3633 { 3634 const char *const path = file1_s1d1; 3635 const struct rule rules[] = { 3636 { 3637 .path = path, 3638 .access = variant->permitted, 3639 }, 3640 {}, 3641 }; 3642 int fd, ruleset_fd; 3643 3644 /* Enable Landlock. */ 3645 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 3646 ASSERT_LE(0, ruleset_fd); 3647 enforce_ruleset(_metadata, ruleset_fd); 3648 ASSERT_EQ(0, close(ruleset_fd)); 3649 3650 fd = open(path, O_WRONLY); 3651 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3652 if (fd >= 0) { 3653 EXPECT_EQ(variant->expected_ftruncate_result, 3654 test_ftruncate(fd)); 3655 ASSERT_EQ(0, close(fd)); 3656 } 3657 } 3658 3659 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 3660 { 3661 int child, fd, status; 3662 int socket_fds[2]; 3663 3664 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 3665 socket_fds)); 3666 3667 child = fork(); 3668 ASSERT_LE(0, child); 3669 if (child == 0) { 3670 /* 3671 * Enables Landlock in the child process, open a file descriptor 3672 * where truncation is forbidden and send it to the 3673 * non-landlocked parent process. 3674 */ 3675 const char *const path = file1_s1d1; 3676 const struct rule rules[] = { 3677 { 3678 .path = path, 3679 .access = variant->permitted, 3680 }, 3681 {}, 3682 }; 3683 int fd, ruleset_fd; 3684 3685 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 3686 ASSERT_LE(0, ruleset_fd); 3687 enforce_ruleset(_metadata, ruleset_fd); 3688 ASSERT_EQ(0, close(ruleset_fd)); 3689 3690 fd = open(path, O_WRONLY); 3691 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3692 3693 if (fd >= 0) { 3694 ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 3695 ASSERT_EQ(0, close(fd)); 3696 } 3697 3698 ASSERT_EQ(0, close(socket_fds[0])); 3699 3700 _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 3701 return; 3702 } 3703 3704 if (variant->expected_open_result == 0) { 3705 fd = recv_fd(socket_fds[1]); 3706 ASSERT_LE(0, fd); 3707 3708 EXPECT_EQ(variant->expected_ftruncate_result, 3709 test_ftruncate(fd)); 3710 ASSERT_EQ(0, close(fd)); 3711 } 3712 3713 ASSERT_EQ(child, waitpid(child, &status, 0)); 3714 ASSERT_EQ(1, WIFEXITED(status)); 3715 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 3716 3717 ASSERT_EQ(0, close(socket_fds[0])); 3718 ASSERT_EQ(0, close(socket_fds[1])); 3719 } 3720 3721 TEST(memfd_ftruncate) 3722 { 3723 int fd; 3724 3725 fd = memfd_create("name", MFD_CLOEXEC); 3726 ASSERT_LE(0, fd); 3727 3728 /* 3729 * Checks that ftruncate is permitted on file descriptors that are 3730 * created in ways other than open(2). 3731 */ 3732 EXPECT_EQ(0, test_ftruncate(fd)); 3733 3734 ASSERT_EQ(0, close(fd)); 3735 } 3736 3737 /* clang-format off */ 3738 FIXTURE(layout1_bind) {}; 3739 /* clang-format on */ 3740 3741 FIXTURE_SETUP(layout1_bind) 3742 { 3743 prepare_layout(_metadata); 3744 3745 create_layout1(_metadata); 3746 3747 set_cap(_metadata, CAP_SYS_ADMIN); 3748 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 3749 clear_cap(_metadata, CAP_SYS_ADMIN); 3750 } 3751 3752 FIXTURE_TEARDOWN(layout1_bind) 3753 { 3754 set_cap(_metadata, CAP_SYS_ADMIN); 3755 EXPECT_EQ(0, umount(dir_s2d2)); 3756 clear_cap(_metadata, CAP_SYS_ADMIN); 3757 3758 remove_layout1(_metadata); 3759 3760 cleanup_layout(_metadata); 3761 } 3762 3763 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 3764 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 3765 3766 /* 3767 * layout1_bind hierarchy: 3768 * 3769 * tmp 3770 * ├── s1d1 3771 * │ ├── f1 3772 * │ ├── f2 3773 * │ └── s1d2 3774 * │ ├── f1 3775 * │ ├── f2 3776 * │ └── s1d3 3777 * │ ├── f1 3778 * │ └── f2 3779 * ├── s2d1 3780 * │ ├── f1 3781 * │ └── s2d2 3782 * │ ├── f1 3783 * │ ├── f2 3784 * │ └── s1d3 3785 * │ ├── f1 3786 * │ └── f2 3787 * └── s3d1 3788 * └── s3d2 3789 * └── s3d3 3790 */ 3791 3792 TEST_F_FORK(layout1_bind, no_restriction) 3793 { 3794 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 3795 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3796 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 3797 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3798 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 3799 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3800 3801 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 3802 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 3803 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 3804 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 3805 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 3806 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 3807 3808 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 3809 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3810 3811 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 3812 } 3813 3814 TEST_F_FORK(layout1_bind, same_content_same_file) 3815 { 3816 /* 3817 * Sets access right on parent directories of both source and 3818 * destination mount points. 3819 */ 3820 const struct rule layer1_parent[] = { 3821 { 3822 .path = dir_s1d1, 3823 .access = ACCESS_RO, 3824 }, 3825 { 3826 .path = dir_s2d1, 3827 .access = ACCESS_RW, 3828 }, 3829 {}, 3830 }; 3831 /* 3832 * Sets access rights on the same bind-mounted directories. The result 3833 * should be ACCESS_RW for both directories, but not both hierarchies 3834 * because of the first layer. 3835 */ 3836 const struct rule layer2_mount_point[] = { 3837 { 3838 .path = dir_s1d2, 3839 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3840 }, 3841 { 3842 .path = dir_s2d2, 3843 .access = ACCESS_RW, 3844 }, 3845 {}, 3846 }; 3847 /* Only allow read-access to the s1d3 hierarchies. */ 3848 const struct rule layer3_source[] = { 3849 { 3850 .path = dir_s1d3, 3851 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3852 }, 3853 {}, 3854 }; 3855 /* Removes all access rights. */ 3856 const struct rule layer4_destination[] = { 3857 { 3858 .path = bind_file1_s1d3, 3859 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3860 }, 3861 {}, 3862 }; 3863 int ruleset_fd; 3864 3865 /* Sets rules for the parent directories. */ 3866 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 3867 ASSERT_LE(0, ruleset_fd); 3868 enforce_ruleset(_metadata, ruleset_fd); 3869 ASSERT_EQ(0, close(ruleset_fd)); 3870 3871 /* Checks source hierarchy. */ 3872 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3873 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3874 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3875 3876 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3877 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3878 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3879 3880 /* Checks destination hierarchy. */ 3881 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 3882 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3883 3884 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3885 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3886 3887 /* Sets rules for the mount points. */ 3888 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 3889 ASSERT_LE(0, ruleset_fd); 3890 enforce_ruleset(_metadata, ruleset_fd); 3891 ASSERT_EQ(0, close(ruleset_fd)); 3892 3893 /* Checks source hierarchy. */ 3894 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 3895 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3896 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3897 3898 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3899 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3900 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3901 3902 /* Checks destination hierarchy. */ 3903 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 3904 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 3905 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3906 3907 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3908 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3909 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3910 3911 /* Sets a (shared) rule only on the source. */ 3912 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 3913 ASSERT_LE(0, ruleset_fd); 3914 enforce_ruleset(_metadata, ruleset_fd); 3915 ASSERT_EQ(0, close(ruleset_fd)); 3916 3917 /* Checks source hierarchy. */ 3918 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 3919 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3920 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3921 3922 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3923 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3924 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 3925 3926 /* Checks destination hierarchy. */ 3927 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 3928 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 3929 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3930 3931 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3932 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3933 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3934 3935 /* Sets a (shared) rule only on the destination. */ 3936 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 3937 ASSERT_LE(0, ruleset_fd); 3938 enforce_ruleset(_metadata, ruleset_fd); 3939 ASSERT_EQ(0, close(ruleset_fd)); 3940 3941 /* Checks source hierarchy. */ 3942 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 3943 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3944 3945 /* Checks destination hierarchy. */ 3946 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 3947 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3948 } 3949 3950 TEST_F_FORK(layout1_bind, reparent_cross_mount) 3951 { 3952 const struct rule layer1[] = { 3953 { 3954 /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 3955 .path = dir_s2d1, 3956 .access = LANDLOCK_ACCESS_FS_REFER, 3957 }, 3958 { 3959 .path = bind_dir_s1d3, 3960 .access = LANDLOCK_ACCESS_FS_EXECUTE, 3961 }, 3962 {}, 3963 }; 3964 int ruleset_fd = create_ruleset( 3965 _metadata, 3966 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 3967 3968 ASSERT_LE(0, ruleset_fd); 3969 enforce_ruleset(_metadata, ruleset_fd); 3970 ASSERT_EQ(0, close(ruleset_fd)); 3971 3972 /* Checks basic denied move. */ 3973 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 3974 ASSERT_EQ(EXDEV, errno); 3975 3976 /* Checks real cross-mount move (Landlock is not involved). */ 3977 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 3978 ASSERT_EQ(EXDEV, errno); 3979 3980 /* Checks move that will give more accesses. */ 3981 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 3982 ASSERT_EQ(EXDEV, errno); 3983 3984 /* Checks legitimate downgrade move. */ 3985 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 3986 } 3987 3988 #define LOWER_BASE TMP_DIR "/lower" 3989 #define LOWER_DATA LOWER_BASE "/data" 3990 static const char lower_fl1[] = LOWER_DATA "/fl1"; 3991 static const char lower_dl1[] = LOWER_DATA "/dl1"; 3992 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 3993 static const char lower_fo1[] = LOWER_DATA "/fo1"; 3994 static const char lower_do1[] = LOWER_DATA "/do1"; 3995 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 3996 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 3997 3998 static const char (*lower_base_files[])[] = { 3999 &lower_fl1, 4000 &lower_fo1, 4001 NULL, 4002 }; 4003 static const char (*lower_base_directories[])[] = { 4004 &lower_dl1, 4005 &lower_do1, 4006 NULL, 4007 }; 4008 static const char (*lower_sub_files[])[] = { 4009 &lower_dl1_fl2, 4010 &lower_do1_fo2, 4011 &lower_do1_fl3, 4012 NULL, 4013 }; 4014 4015 #define UPPER_BASE TMP_DIR "/upper" 4016 #define UPPER_DATA UPPER_BASE "/data" 4017 #define UPPER_WORK UPPER_BASE "/work" 4018 static const char upper_fu1[] = UPPER_DATA "/fu1"; 4019 static const char upper_du1[] = UPPER_DATA "/du1"; 4020 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 4021 static const char upper_fo1[] = UPPER_DATA "/fo1"; 4022 static const char upper_do1[] = UPPER_DATA "/do1"; 4023 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 4024 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 4025 4026 static const char (*upper_base_files[])[] = { 4027 &upper_fu1, 4028 &upper_fo1, 4029 NULL, 4030 }; 4031 static const char (*upper_base_directories[])[] = { 4032 &upper_du1, 4033 &upper_do1, 4034 NULL, 4035 }; 4036 static const char (*upper_sub_files[])[] = { 4037 &upper_du1_fu2, 4038 &upper_do1_fo2, 4039 &upper_do1_fu3, 4040 NULL, 4041 }; 4042 4043 #define MERGE_BASE TMP_DIR "/merge" 4044 #define MERGE_DATA MERGE_BASE "/data" 4045 static const char merge_fl1[] = MERGE_DATA "/fl1"; 4046 static const char merge_dl1[] = MERGE_DATA "/dl1"; 4047 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 4048 static const char merge_fu1[] = MERGE_DATA "/fu1"; 4049 static const char merge_du1[] = MERGE_DATA "/du1"; 4050 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 4051 static const char merge_fo1[] = MERGE_DATA "/fo1"; 4052 static const char merge_do1[] = MERGE_DATA "/do1"; 4053 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 4054 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 4055 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 4056 4057 static const char (*merge_base_files[])[] = { 4058 &merge_fl1, 4059 &merge_fu1, 4060 &merge_fo1, 4061 NULL, 4062 }; 4063 static const char (*merge_base_directories[])[] = { 4064 &merge_dl1, 4065 &merge_du1, 4066 &merge_do1, 4067 NULL, 4068 }; 4069 static const char (*merge_sub_files[])[] = { 4070 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 4071 &merge_do1_fl3, &merge_do1_fu3, NULL, 4072 }; 4073 4074 /* 4075 * layout2_overlay hierarchy: 4076 * 4077 * tmp 4078 * ├── lower 4079 * │ └── data 4080 * │ ├── dl1 4081 * │ │ └── fl2 4082 * │ ├── do1 4083 * │ │ ├── fl3 4084 * │ │ └── fo2 4085 * │ ├── fl1 4086 * │ └── fo1 4087 * ├── merge 4088 * │ └── data 4089 * │ ├── dl1 4090 * │ │ └── fl2 4091 * │ ├── do1 4092 * │ │ ├── fl3 4093 * │ │ ├── fo2 4094 * │ │ └── fu3 4095 * │ ├── du1 4096 * │ │ └── fu2 4097 * │ ├── fl1 4098 * │ ├── fo1 4099 * │ └── fu1 4100 * └── upper 4101 * ├── data 4102 * │ ├── do1 4103 * │ │ ├── fo2 4104 * │ │ └── fu3 4105 * │ ├── du1 4106 * │ │ └── fu2 4107 * │ ├── fo1 4108 * │ └── fu1 4109 * └── work 4110 * └── work 4111 */ 4112 4113 FIXTURE(layout2_overlay) 4114 { 4115 bool skip_test; 4116 }; 4117 4118 FIXTURE_SETUP(layout2_overlay) 4119 { 4120 if (!supports_filesystem("overlay")) { 4121 self->skip_test = true; 4122 SKIP(return, "overlayfs is not supported (setup)"); 4123 } 4124 4125 prepare_layout(_metadata); 4126 4127 create_directory(_metadata, LOWER_BASE); 4128 set_cap(_metadata, CAP_SYS_ADMIN); 4129 /* Creates tmpfs mount points to get deterministic overlayfs. */ 4130 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 4131 clear_cap(_metadata, CAP_SYS_ADMIN); 4132 create_file(_metadata, lower_fl1); 4133 create_file(_metadata, lower_dl1_fl2); 4134 create_file(_metadata, lower_fo1); 4135 create_file(_metadata, lower_do1_fo2); 4136 create_file(_metadata, lower_do1_fl3); 4137 4138 create_directory(_metadata, UPPER_BASE); 4139 set_cap(_metadata, CAP_SYS_ADMIN); 4140 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 4141 clear_cap(_metadata, CAP_SYS_ADMIN); 4142 create_file(_metadata, upper_fu1); 4143 create_file(_metadata, upper_du1_fu2); 4144 create_file(_metadata, upper_fo1); 4145 create_file(_metadata, upper_do1_fo2); 4146 create_file(_metadata, upper_do1_fu3); 4147 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 4148 4149 create_directory(_metadata, MERGE_DATA); 4150 set_cap(_metadata, CAP_SYS_ADMIN); 4151 set_cap(_metadata, CAP_DAC_OVERRIDE); 4152 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 4153 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 4154 ",workdir=" UPPER_WORK)); 4155 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4156 clear_cap(_metadata, CAP_SYS_ADMIN); 4157 } 4158 4159 FIXTURE_TEARDOWN(layout2_overlay) 4160 { 4161 if (self->skip_test) 4162 SKIP(return, "overlayfs is not supported (teardown)"); 4163 4164 EXPECT_EQ(0, remove_path(lower_do1_fl3)); 4165 EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 4166 EXPECT_EQ(0, remove_path(lower_fl1)); 4167 EXPECT_EQ(0, remove_path(lower_do1_fo2)); 4168 EXPECT_EQ(0, remove_path(lower_fo1)); 4169 set_cap(_metadata, CAP_SYS_ADMIN); 4170 EXPECT_EQ(0, umount(LOWER_BASE)); 4171 clear_cap(_metadata, CAP_SYS_ADMIN); 4172 EXPECT_EQ(0, remove_path(LOWER_BASE)); 4173 4174 EXPECT_EQ(0, remove_path(upper_do1_fu3)); 4175 EXPECT_EQ(0, remove_path(upper_du1_fu2)); 4176 EXPECT_EQ(0, remove_path(upper_fu1)); 4177 EXPECT_EQ(0, remove_path(upper_do1_fo2)); 4178 EXPECT_EQ(0, remove_path(upper_fo1)); 4179 EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 4180 set_cap(_metadata, CAP_SYS_ADMIN); 4181 EXPECT_EQ(0, umount(UPPER_BASE)); 4182 clear_cap(_metadata, CAP_SYS_ADMIN); 4183 EXPECT_EQ(0, remove_path(UPPER_BASE)); 4184 4185 set_cap(_metadata, CAP_SYS_ADMIN); 4186 EXPECT_EQ(0, umount(MERGE_DATA)); 4187 clear_cap(_metadata, CAP_SYS_ADMIN); 4188 EXPECT_EQ(0, remove_path(MERGE_DATA)); 4189 4190 cleanup_layout(_metadata); 4191 } 4192 4193 TEST_F_FORK(layout2_overlay, no_restriction) 4194 { 4195 if (self->skip_test) 4196 SKIP(return, "overlayfs is not supported (test)"); 4197 4198 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 4199 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 4200 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 4201 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 4202 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 4203 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 4204 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 4205 4206 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 4207 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 4208 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 4209 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 4210 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 4211 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 4212 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 4213 4214 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 4215 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 4216 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 4217 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 4218 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 4219 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 4220 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 4221 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 4222 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 4223 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 4224 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 4225 } 4226 4227 #define for_each_path(path_list, path_entry, i) \ 4228 for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 4229 path_entry = *path_list[++i]) 4230 4231 TEST_F_FORK(layout2_overlay, same_content_different_file) 4232 { 4233 /* Sets access right on parent directories of both layers. */ 4234 const struct rule layer1_base[] = { 4235 { 4236 .path = LOWER_BASE, 4237 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4238 }, 4239 { 4240 .path = UPPER_BASE, 4241 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4242 }, 4243 { 4244 .path = MERGE_BASE, 4245 .access = ACCESS_RW, 4246 }, 4247 {}, 4248 }; 4249 const struct rule layer2_data[] = { 4250 { 4251 .path = LOWER_DATA, 4252 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4253 }, 4254 { 4255 .path = UPPER_DATA, 4256 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4257 }, 4258 { 4259 .path = MERGE_DATA, 4260 .access = ACCESS_RW, 4261 }, 4262 {}, 4263 }; 4264 /* Sets access right on directories inside both layers. */ 4265 const struct rule layer3_subdirs[] = { 4266 { 4267 .path = lower_dl1, 4268 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4269 }, 4270 { 4271 .path = lower_do1, 4272 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4273 }, 4274 { 4275 .path = upper_du1, 4276 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4277 }, 4278 { 4279 .path = upper_do1, 4280 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4281 }, 4282 { 4283 .path = merge_dl1, 4284 .access = ACCESS_RW, 4285 }, 4286 { 4287 .path = merge_du1, 4288 .access = ACCESS_RW, 4289 }, 4290 { 4291 .path = merge_do1, 4292 .access = ACCESS_RW, 4293 }, 4294 {}, 4295 }; 4296 /* Tighten access rights to the files. */ 4297 const struct rule layer4_files[] = { 4298 { 4299 .path = lower_dl1_fl2, 4300 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4301 }, 4302 { 4303 .path = lower_do1_fo2, 4304 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4305 }, 4306 { 4307 .path = lower_do1_fl3, 4308 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4309 }, 4310 { 4311 .path = upper_du1_fu2, 4312 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4313 }, 4314 { 4315 .path = upper_do1_fo2, 4316 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4317 }, 4318 { 4319 .path = upper_do1_fu3, 4320 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4321 }, 4322 { 4323 .path = merge_dl1_fl2, 4324 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4325 LANDLOCK_ACCESS_FS_WRITE_FILE, 4326 }, 4327 { 4328 .path = merge_du1_fu2, 4329 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4330 LANDLOCK_ACCESS_FS_WRITE_FILE, 4331 }, 4332 { 4333 .path = merge_do1_fo2, 4334 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4335 LANDLOCK_ACCESS_FS_WRITE_FILE, 4336 }, 4337 { 4338 .path = merge_do1_fl3, 4339 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4340 LANDLOCK_ACCESS_FS_WRITE_FILE, 4341 }, 4342 { 4343 .path = merge_do1_fu3, 4344 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4345 LANDLOCK_ACCESS_FS_WRITE_FILE, 4346 }, 4347 {}, 4348 }; 4349 const struct rule layer5_merge_only[] = { 4350 { 4351 .path = MERGE_DATA, 4352 .access = LANDLOCK_ACCESS_FS_READ_FILE | 4353 LANDLOCK_ACCESS_FS_WRITE_FILE, 4354 }, 4355 {}, 4356 }; 4357 int ruleset_fd; 4358 size_t i; 4359 const char *path_entry; 4360 4361 if (self->skip_test) 4362 SKIP(return, "overlayfs is not supported (test)"); 4363 4364 /* Sets rules on base directories (i.e. outside overlay scope). */ 4365 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 4366 ASSERT_LE(0, ruleset_fd); 4367 enforce_ruleset(_metadata, ruleset_fd); 4368 ASSERT_EQ(0, close(ruleset_fd)); 4369 4370 /* Checks lower layer. */ 4371 for_each_path(lower_base_files, path_entry, i) { 4372 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4373 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4374 } 4375 for_each_path(lower_base_directories, path_entry, i) { 4376 ASSERT_EQ(EACCES, 4377 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4378 } 4379 for_each_path(lower_sub_files, path_entry, i) { 4380 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4381 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4382 } 4383 /* Checks upper layer. */ 4384 for_each_path(upper_base_files, path_entry, i) { 4385 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4386 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4387 } 4388 for_each_path(upper_base_directories, path_entry, i) { 4389 ASSERT_EQ(EACCES, 4390 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4391 } 4392 for_each_path(upper_sub_files, path_entry, i) { 4393 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4394 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4395 } 4396 /* 4397 * Checks that access rights are independent from the lower and upper 4398 * layers: write access to upper files viewed through the merge point 4399 * is still allowed, and write access to lower file viewed (and copied) 4400 * through the merge point is still allowed. 4401 */ 4402 for_each_path(merge_base_files, path_entry, i) { 4403 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4404 } 4405 for_each_path(merge_base_directories, path_entry, i) { 4406 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4407 } 4408 for_each_path(merge_sub_files, path_entry, i) { 4409 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4410 } 4411 4412 /* Sets rules on data directories (i.e. inside overlay scope). */ 4413 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 4414 ASSERT_LE(0, ruleset_fd); 4415 enforce_ruleset(_metadata, ruleset_fd); 4416 ASSERT_EQ(0, close(ruleset_fd)); 4417 4418 /* Checks merge. */ 4419 for_each_path(merge_base_files, path_entry, i) { 4420 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4421 } 4422 for_each_path(merge_base_directories, path_entry, i) { 4423 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4424 } 4425 for_each_path(merge_sub_files, path_entry, i) { 4426 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4427 } 4428 4429 /* Same checks with tighter rules. */ 4430 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 4431 ASSERT_LE(0, ruleset_fd); 4432 enforce_ruleset(_metadata, ruleset_fd); 4433 ASSERT_EQ(0, close(ruleset_fd)); 4434 4435 /* Checks changes for lower layer. */ 4436 for_each_path(lower_base_files, path_entry, i) { 4437 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4438 } 4439 /* Checks changes for upper layer. */ 4440 for_each_path(upper_base_files, path_entry, i) { 4441 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4442 } 4443 /* Checks all merge accesses. */ 4444 for_each_path(merge_base_files, path_entry, i) { 4445 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4446 } 4447 for_each_path(merge_base_directories, path_entry, i) { 4448 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4449 } 4450 for_each_path(merge_sub_files, path_entry, i) { 4451 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4452 } 4453 4454 /* Sets rules directly on overlayed files. */ 4455 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 4456 ASSERT_LE(0, ruleset_fd); 4457 enforce_ruleset(_metadata, ruleset_fd); 4458 ASSERT_EQ(0, close(ruleset_fd)); 4459 4460 /* Checks unchanged accesses on lower layer. */ 4461 for_each_path(lower_sub_files, path_entry, i) { 4462 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4463 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4464 } 4465 /* Checks unchanged accesses on upper layer. */ 4466 for_each_path(upper_sub_files, path_entry, i) { 4467 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4468 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4469 } 4470 /* Checks all merge accesses. */ 4471 for_each_path(merge_base_files, path_entry, i) { 4472 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4473 } 4474 for_each_path(merge_base_directories, path_entry, i) { 4475 ASSERT_EQ(EACCES, 4476 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4477 } 4478 for_each_path(merge_sub_files, path_entry, i) { 4479 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4480 } 4481 4482 /* Only allowes access to the merge hierarchy. */ 4483 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 4484 ASSERT_LE(0, ruleset_fd); 4485 enforce_ruleset(_metadata, ruleset_fd); 4486 ASSERT_EQ(0, close(ruleset_fd)); 4487 4488 /* Checks new accesses on lower layer. */ 4489 for_each_path(lower_sub_files, path_entry, i) { 4490 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4491 } 4492 /* Checks new accesses on upper layer. */ 4493 for_each_path(upper_sub_files, path_entry, i) { 4494 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4495 } 4496 /* Checks all merge accesses. */ 4497 for_each_path(merge_base_files, path_entry, i) { 4498 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4499 } 4500 for_each_path(merge_base_directories, path_entry, i) { 4501 ASSERT_EQ(EACCES, 4502 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4503 } 4504 for_each_path(merge_sub_files, path_entry, i) { 4505 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4506 } 4507 } 4508 4509 FIXTURE(layout3_fs) 4510 { 4511 bool has_created_dir; 4512 bool has_created_file; 4513 char *dir_path; 4514 bool skip_test; 4515 }; 4516 4517 FIXTURE_VARIANT(layout3_fs) 4518 { 4519 const struct mnt_opt mnt; 4520 const char *const file_path; 4521 unsigned int cwd_fs_magic; 4522 }; 4523 4524 /* clang-format off */ 4525 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 4526 /* clang-format on */ 4527 .mnt = { 4528 .type = "tmpfs", 4529 .data = MNT_TMP_DATA, 4530 }, 4531 .file_path = file1_s1d1, 4532 }; 4533 4534 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 4535 .mnt = { 4536 .type = "ramfs", 4537 .data = "mode=700", 4538 }, 4539 .file_path = TMP_DIR "/dir/file", 4540 }; 4541 4542 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 4543 .mnt = { 4544 .type = "cgroup2", 4545 }, 4546 .file_path = TMP_DIR "/test/cgroup.procs", 4547 }; 4548 4549 FIXTURE_VARIANT_ADD(layout3_fs, proc) { 4550 .mnt = { 4551 .type = "proc", 4552 }, 4553 .file_path = TMP_DIR "/self/status", 4554 }; 4555 4556 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 4557 .mnt = { 4558 .type = "sysfs", 4559 }, 4560 .file_path = TMP_DIR "/kernel/notes", 4561 }; 4562 4563 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 4564 .mnt = { 4565 .source = TMP_DIR, 4566 .flags = MS_BIND, 4567 }, 4568 .file_path = TMP_DIR "/dir/file", 4569 .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 4570 }; 4571 4572 FIXTURE_SETUP(layout3_fs) 4573 { 4574 struct stat statbuf; 4575 const char *slash; 4576 size_t dir_len; 4577 4578 if (!supports_filesystem(variant->mnt.type) || 4579 !cwd_matches_fs(variant->cwd_fs_magic)) { 4580 self->skip_test = true; 4581 SKIP(return, "this filesystem is not supported (setup)"); 4582 } 4583 4584 slash = strrchr(variant->file_path, '/'); 4585 ASSERT_NE(slash, NULL); 4586 dir_len = (size_t)slash - (size_t)variant->file_path; 4587 ASSERT_LT(0, dir_len); 4588 self->dir_path = malloc(dir_len + 1); 4589 self->dir_path[dir_len] = '\0'; 4590 strncpy(self->dir_path, variant->file_path, dir_len); 4591 4592 prepare_layout_opt(_metadata, &variant->mnt); 4593 4594 /* Creates directory when required. */ 4595 if (stat(self->dir_path, &statbuf)) { 4596 set_cap(_metadata, CAP_DAC_OVERRIDE); 4597 EXPECT_EQ(0, mkdir(self->dir_path, 0700)) 4598 { 4599 TH_LOG("Failed to create directory \"%s\": %s", 4600 self->dir_path, strerror(errno)); 4601 free(self->dir_path); 4602 self->dir_path = NULL; 4603 } 4604 self->has_created_dir = true; 4605 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4606 } 4607 4608 /* Creates file when required. */ 4609 if (stat(variant->file_path, &statbuf)) { 4610 int fd; 4611 4612 set_cap(_metadata, CAP_DAC_OVERRIDE); 4613 fd = creat(variant->file_path, 0600); 4614 EXPECT_LE(0, fd) 4615 { 4616 TH_LOG("Failed to create file \"%s\": %s", 4617 variant->file_path, strerror(errno)); 4618 } 4619 EXPECT_EQ(0, close(fd)); 4620 self->has_created_file = true; 4621 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4622 } 4623 } 4624 4625 FIXTURE_TEARDOWN(layout3_fs) 4626 { 4627 if (self->skip_test) 4628 SKIP(return, "this filesystem is not supported (teardown)"); 4629 4630 if (self->has_created_file) { 4631 set_cap(_metadata, CAP_DAC_OVERRIDE); 4632 /* 4633 * Don't check for error because the file might already 4634 * have been removed (cf. release_inode test). 4635 */ 4636 unlink(variant->file_path); 4637 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4638 } 4639 4640 if (self->has_created_dir) { 4641 set_cap(_metadata, CAP_DAC_OVERRIDE); 4642 /* 4643 * Don't check for error because the directory might already 4644 * have been removed (cf. release_inode test). 4645 */ 4646 rmdir(self->dir_path); 4647 clear_cap(_metadata, CAP_DAC_OVERRIDE); 4648 } 4649 free(self->dir_path); 4650 self->dir_path = NULL; 4651 4652 cleanup_layout(_metadata); 4653 } 4654 4655 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 4656 FIXTURE_DATA(layout3_fs) * self, 4657 const FIXTURE_VARIANT(layout3_fs) * variant, 4658 const char *const rule_path) 4659 { 4660 const struct rule layer1_allow_read_file[] = { 4661 { 4662 .path = rule_path, 4663 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4664 }, 4665 {}, 4666 }; 4667 const struct landlock_ruleset_attr layer2_deny_everything_attr = { 4668 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 4669 }; 4670 const char *const dev_null_path = "/dev/null"; 4671 int ruleset_fd; 4672 4673 if (self->skip_test) 4674 SKIP(return, "this filesystem is not supported (test)"); 4675 4676 /* Checks without Landlock. */ 4677 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 4678 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 4679 4680 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 4681 layer1_allow_read_file); 4682 EXPECT_LE(0, ruleset_fd); 4683 enforce_ruleset(_metadata, ruleset_fd); 4684 EXPECT_EQ(0, close(ruleset_fd)); 4685 4686 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 4687 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 4688 4689 /* Forbids directory reading. */ 4690 ruleset_fd = 4691 landlock_create_ruleset(&layer2_deny_everything_attr, 4692 sizeof(layer2_deny_everything_attr), 0); 4693 EXPECT_LE(0, ruleset_fd); 4694 enforce_ruleset(_metadata, ruleset_fd); 4695 EXPECT_EQ(0, close(ruleset_fd)); 4696 4697 /* Checks with Landlock and forbidden access. */ 4698 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 4699 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 4700 } 4701 4702 /* Matrix of tests to check file hierarchy evaluation. */ 4703 4704 TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 4705 { 4706 /* The current directory must not be the root for this test. */ 4707 layer3_fs_tag_inode(_metadata, self, variant, "."); 4708 } 4709 4710 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 4711 { 4712 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 4713 } 4714 4715 TEST_F_FORK(layout3_fs, tag_inode_dir_child) 4716 { 4717 layer3_fs_tag_inode(_metadata, self, variant, self->dir_path); 4718 } 4719 4720 TEST_F_FORK(layout3_fs, tag_inode_file) 4721 { 4722 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 4723 } 4724 4725 /* Light version of layout1.release_inodes */ 4726 TEST_F_FORK(layout3_fs, release_inodes) 4727 { 4728 const struct rule layer1[] = { 4729 { 4730 .path = TMP_DIR, 4731 .access = LANDLOCK_ACCESS_FS_READ_DIR, 4732 }, 4733 {}, 4734 }; 4735 int ruleset_fd; 4736 4737 if (self->skip_test) 4738 SKIP(return, "this filesystem is not supported (test)"); 4739 4740 /* Clean up for the teardown to not fail. */ 4741 if (self->has_created_file) 4742 EXPECT_EQ(0, remove_path(variant->file_path)); 4743 4744 if (self->has_created_dir) 4745 /* Don't check for error because of cgroup specificities. */ 4746 remove_path(self->dir_path); 4747 4748 ruleset_fd = 4749 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 4750 ASSERT_LE(0, ruleset_fd); 4751 4752 /* Unmount the filesystem while it is being used by a ruleset. */ 4753 set_cap(_metadata, CAP_SYS_ADMIN); 4754 ASSERT_EQ(0, umount(TMP_DIR)); 4755 clear_cap(_metadata, CAP_SYS_ADMIN); 4756 4757 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 4758 set_cap(_metadata, CAP_SYS_ADMIN); 4759 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 4760 clear_cap(_metadata, CAP_SYS_ADMIN); 4761 4762 enforce_ruleset(_metadata, ruleset_fd); 4763 ASSERT_EQ(0, close(ruleset_fd)); 4764 4765 /* Checks that access to the new mount point is denied. */ 4766 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 4767 } 4768 4769 TEST_HARNESS_MAIN 4770