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