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