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