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