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/resource.h> 26 #include <sys/sendfile.h> 27 #include <sys/socket.h> 28 #include <sys/stat.h> 29 #include <sys/sysmacros.h> 30 #include <sys/un.h> 31 #include <sys/vfs.h> 32 #include <unistd.h> 33 34 /* 35 * Intentionally included last to work around header conflict. 36 * See https://sourceware.org/glibc/wiki/Synchronizing_Headers. 37 */ 38 #include <linux/fs.h> 39 #include <linux/mount.h> 40 41 /* Defines AT_EXECVE_CHECK without type conflicts. */ 42 #define _ASM_GENERIC_FCNTL_H 43 #include <linux/fcntl.h> 44 45 #include "audit.h" 46 #include "common.h" 47 48 #ifndef renameat2 49 int renameat2(int olddirfd, const char *oldpath, int newdirfd, 50 const char *newpath, unsigned int flags) 51 { 52 return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 53 flags); 54 } 55 #endif 56 57 #ifndef open_tree 58 int open_tree(int dfd, const char *filename, unsigned int flags) 59 { 60 return syscall(__NR_open_tree, dfd, filename, flags); 61 } 62 #endif 63 64 static int sys_execveat(int dirfd, const char *pathname, char *const argv[], 65 char *const envp[], int flags) 66 { 67 return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); 68 } 69 70 #ifndef RENAME_EXCHANGE 71 #define RENAME_EXCHANGE (1 << 1) 72 #endif 73 74 static const char bin_true[] = "./true"; 75 76 /* Paths (sibling number and depth) */ 77 static const char dir_s1d1[] = TMP_DIR "/s1d1"; 78 static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 79 static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 80 static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 81 static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 82 static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 83 static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 84 static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 85 static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 86 87 static const char dir_s2d1[] = TMP_DIR "/s2d1"; 88 static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 89 static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 90 static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 91 static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 92 static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 93 static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 94 95 static const char dir_s3d1[] = TMP_DIR "/s3d1"; 96 static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; 97 /* dir_s3d2 is a mount point. */ 98 static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 99 static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 100 static const char file1_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3/f1"; 101 static const char dir_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4"; 102 static const char file1_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4/f1"; 103 104 /* 105 * layout1 hierarchy: 106 * 107 * tmp 108 * ├── s1d1 109 * │ ├── f1 110 * │ ├── f2 111 * │ └── s1d2 112 * │ ├── f1 113 * │ ├── f2 114 * │ └── s1d3 115 * │ ├── f1 116 * │ └── f2 117 * ├── s2d1 118 * │ ├── f1 119 * │ └── s2d2 120 * │ ├── f1 121 * │ └── s2d3 122 * │ ├── f1 123 * │ └── f2 124 * └── s3d1 125 * ├── f1 126 * └── s3d2 [mount point] 127 * ├── s3d3 128 * │ └── f1 129 * └── s3d4 130 * └── f1 131 */ 132 133 static bool fgrep(FILE *const inf, const char *const str) 134 { 135 char line[32]; 136 const int slen = strlen(str); 137 138 while (!feof(inf)) { 139 if (!fgets(line, sizeof(line), inf)) 140 break; 141 if (strncmp(line, str, slen)) 142 continue; 143 144 return true; 145 } 146 147 return false; 148 } 149 150 static bool supports_filesystem(const char *const filesystem) 151 { 152 char str[32]; 153 int len; 154 bool res = true; 155 FILE *const inf = fopen("/proc/filesystems", "r"); 156 157 /* 158 * Consider that the filesystem is supported if we cannot get the 159 * supported ones. 160 */ 161 if (!inf) 162 return true; 163 164 /* filesystem can be null for bind mounts. */ 165 if (!filesystem) 166 goto out; 167 168 len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); 169 if (len >= sizeof(str)) 170 /* Ignores too-long filesystem names. */ 171 goto out; 172 173 res = fgrep(inf, str); 174 175 out: 176 fclose(inf); 177 return res; 178 } 179 180 static bool cwd_matches_fs(unsigned int fs_magic) 181 { 182 struct statfs statfs_buf; 183 184 if (!fs_magic) 185 return true; 186 187 if (statfs(".", &statfs_buf)) 188 return true; 189 190 return statfs_buf.f_type == fs_magic; 191 } 192 193 static void mkdir_parents(struct __test_metadata *const _metadata, 194 const char *const path) 195 { 196 char *walker; 197 const char *parent; 198 int i, err; 199 200 ASSERT_NE(path[0], '\0'); 201 walker = strdup(path); 202 ASSERT_NE(NULL, walker); 203 parent = walker; 204 for (i = 1; walker[i]; i++) { 205 if (walker[i] != '/') 206 continue; 207 walker[i] = '\0'; 208 err = mkdir(parent, 0700); 209 ASSERT_FALSE(err && errno != EEXIST) 210 { 211 TH_LOG("Failed to create directory \"%s\": %s", parent, 212 strerror(errno)); 213 } 214 walker[i] = '/'; 215 } 216 free(walker); 217 } 218 219 static void create_directory(struct __test_metadata *const _metadata, 220 const char *const path) 221 { 222 mkdir_parents(_metadata, path); 223 ASSERT_EQ(0, mkdir(path, 0700)) 224 { 225 TH_LOG("Failed to create directory \"%s\": %s", path, 226 strerror(errno)); 227 } 228 } 229 230 static void create_file(struct __test_metadata *const _metadata, 231 const char *const path) 232 { 233 mkdir_parents(_metadata, path); 234 ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 235 { 236 TH_LOG("Failed to create file \"%s\": %s", path, 237 strerror(errno)); 238 } 239 } 240 241 static int remove_path(const char *const path) 242 { 243 char *walker; 244 int i, ret, err = 0; 245 246 walker = strdup(path); 247 if (!walker) { 248 err = ENOMEM; 249 goto out; 250 } 251 if (unlink(path) && rmdir(path)) { 252 if (errno != ENOENT && errno != ENOTDIR) 253 err = errno; 254 goto out; 255 } 256 for (i = strlen(walker); i > 0; i--) { 257 if (walker[i] != '/') 258 continue; 259 walker[i] = '\0'; 260 ret = rmdir(walker); 261 if (ret) { 262 if (errno != ENOTEMPTY && errno != EBUSY) 263 err = errno; 264 goto out; 265 } 266 if (strcmp(walker, TMP_DIR) == 0) 267 goto out; 268 } 269 270 out: 271 free(walker); 272 return err; 273 } 274 275 struct mnt_opt { 276 const char *const source; 277 const char *const type; 278 const unsigned long flags; 279 const char *const data; 280 }; 281 282 #define MNT_TMP_DATA "size=4m,mode=700" 283 284 static const struct mnt_opt mnt_tmp = { 285 .type = "tmpfs", 286 .data = MNT_TMP_DATA, 287 }; 288 289 static int mount_opt(const struct mnt_opt *const mnt, const char *const target) 290 { 291 return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags, 292 mnt->data); 293 } 294 295 static void prepare_layout_opt(struct __test_metadata *const _metadata, 296 const struct mnt_opt *const mnt) 297 { 298 disable_caps(_metadata); 299 umask(0077); 300 create_directory(_metadata, TMP_DIR); 301 302 /* 303 * Do not pollute the rest of the system: creates a private mount point 304 * for tests relying on pivot_root(2) and move_mount(2). 305 */ 306 set_cap(_metadata, CAP_SYS_ADMIN); 307 ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); 308 ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) 309 { 310 TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, 311 strerror(errno)); 312 /* 313 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() 314 * failed, so we need to explicitly do a minimal cleanup to 315 * avoid cascading errors with other tests that don't depend on 316 * the same filesystem. 317 */ 318 remove_path(TMP_DIR); 319 } 320 ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 321 clear_cap(_metadata, CAP_SYS_ADMIN); 322 } 323 324 static void prepare_layout(struct __test_metadata *const _metadata) 325 { 326 prepare_layout_opt(_metadata, &mnt_tmp); 327 } 328 329 static void cleanup_layout(struct __test_metadata *const _metadata) 330 { 331 set_cap(_metadata, CAP_SYS_ADMIN); 332 if (umount(TMP_DIR)) { 333 /* 334 * According to the test environment, the mount point of the 335 * current directory may be shared or not, which changes the 336 * visibility of the nested TMP_DIR mount point for the test's 337 * parent process doing this cleanup. 338 */ 339 ASSERT_EQ(EINVAL, errno); 340 } 341 clear_cap(_metadata, CAP_SYS_ADMIN); 342 EXPECT_EQ(0, remove_path(TMP_DIR)); 343 } 344 345 /* clang-format off */ 346 FIXTURE(layout0) {}; 347 /* clang-format on */ 348 349 FIXTURE_SETUP(layout0) 350 { 351 prepare_layout(_metadata); 352 } 353 354 FIXTURE_TEARDOWN_PARENT(layout0) 355 { 356 cleanup_layout(_metadata); 357 } 358 359 static void create_layout1(struct __test_metadata *const _metadata) 360 { 361 create_file(_metadata, file1_s1d1); 362 create_file(_metadata, file1_s1d2); 363 create_file(_metadata, file1_s1d3); 364 create_file(_metadata, file2_s1d1); 365 create_file(_metadata, file2_s1d2); 366 create_file(_metadata, file2_s1d3); 367 368 create_file(_metadata, file1_s2d1); 369 create_file(_metadata, file1_s2d2); 370 create_file(_metadata, file1_s2d3); 371 create_file(_metadata, file2_s2d3); 372 373 create_file(_metadata, file1_s3d1); 374 create_directory(_metadata, dir_s3d2); 375 set_cap(_metadata, CAP_SYS_ADMIN); 376 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 377 clear_cap(_metadata, CAP_SYS_ADMIN); 378 379 create_file(_metadata, file1_s3d3); 380 create_file(_metadata, file1_s3d4); 381 } 382 383 static void remove_layout1(struct __test_metadata *const _metadata) 384 { 385 EXPECT_EQ(0, remove_path(file2_s1d3)); 386 EXPECT_EQ(0, remove_path(file2_s1d2)); 387 EXPECT_EQ(0, remove_path(file2_s1d1)); 388 EXPECT_EQ(0, remove_path(file1_s1d3)); 389 EXPECT_EQ(0, remove_path(file1_s1d2)); 390 EXPECT_EQ(0, remove_path(file1_s1d1)); 391 EXPECT_EQ(0, remove_path(dir_s1d3)); 392 393 EXPECT_EQ(0, remove_path(file2_s2d3)); 394 EXPECT_EQ(0, remove_path(file1_s2d3)); 395 EXPECT_EQ(0, remove_path(file1_s2d2)); 396 EXPECT_EQ(0, remove_path(file1_s2d1)); 397 EXPECT_EQ(0, remove_path(dir_s2d2)); 398 399 EXPECT_EQ(0, remove_path(file1_s3d1)); 400 EXPECT_EQ(0, remove_path(file1_s3d3)); 401 EXPECT_EQ(0, remove_path(file1_s3d4)); 402 set_cap(_metadata, CAP_SYS_ADMIN); 403 umount(dir_s3d2); 404 clear_cap(_metadata, CAP_SYS_ADMIN); 405 EXPECT_EQ(0, remove_path(dir_s3d2)); 406 } 407 408 /* clang-format off */ 409 FIXTURE(layout1) {}; 410 /* clang-format on */ 411 412 FIXTURE_SETUP(layout1) 413 { 414 prepare_layout(_metadata); 415 416 create_layout1(_metadata); 417 } 418 419 FIXTURE_TEARDOWN_PARENT(layout1) 420 { 421 remove_layout1(_metadata); 422 423 cleanup_layout(_metadata); 424 } 425 426 /* 427 * This helper enables to use the ASSERT_* macros and print the line number 428 * pointing to the test caller. 429 */ 430 static int test_open_rel(const int dirfd, const char *const path, 431 const int flags) 432 { 433 int fd; 434 435 /* Works with file and directories. */ 436 fd = openat(dirfd, path, flags | O_CLOEXEC); 437 if (fd < 0) 438 return errno; 439 /* 440 * Mixing error codes from close(2) and open(2) should not lead to any 441 * (access type) confusion for this test. 442 */ 443 if (close(fd) != 0) 444 return errno; 445 return 0; 446 } 447 448 static int test_open(const char *const path, const int flags) 449 { 450 return test_open_rel(AT_FDCWD, path, flags); 451 } 452 453 TEST_F_FORK(layout1, no_restriction) 454 { 455 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 456 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 457 ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 458 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 459 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 460 ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 461 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 462 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 463 464 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 465 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 466 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 467 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 468 ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 469 ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 470 471 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 472 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 473 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 474 } 475 476 TEST_F_FORK(layout1, inval) 477 { 478 struct landlock_path_beneath_attr path_beneath = { 479 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 480 LANDLOCK_ACCESS_FS_WRITE_FILE, 481 .parent_fd = -1, 482 }; 483 struct landlock_ruleset_attr ruleset_attr = { 484 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 485 LANDLOCK_ACCESS_FS_WRITE_FILE, 486 }; 487 int ruleset_fd; 488 489 path_beneath.parent_fd = 490 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 491 ASSERT_LE(0, path_beneath.parent_fd); 492 493 ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 494 ASSERT_LE(0, ruleset_fd); 495 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 496 &path_beneath, 0)); 497 /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 498 ASSERT_EQ(EBADF, errno); 499 ASSERT_EQ(0, close(ruleset_fd)); 500 501 ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 502 ASSERT_LE(0, ruleset_fd); 503 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 504 &path_beneath, 0)); 505 /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 506 ASSERT_EQ(EBADFD, errno); 507 ASSERT_EQ(0, close(ruleset_fd)); 508 509 /* Gets a real ruleset. */ 510 ruleset_fd = 511 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 512 ASSERT_LE(0, ruleset_fd); 513 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 514 &path_beneath, 0)); 515 ASSERT_EQ(0, close(path_beneath.parent_fd)); 516 517 /* Tests without O_PATH. */ 518 path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 519 ASSERT_LE(0, path_beneath.parent_fd); 520 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 521 &path_beneath, 0)); 522 ASSERT_EQ(0, close(path_beneath.parent_fd)); 523 524 /* Tests with a ruleset FD. */ 525 path_beneath.parent_fd = ruleset_fd; 526 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 527 &path_beneath, 0)); 528 ASSERT_EQ(EBADFD, errno); 529 530 /* Checks unhandled allowed_access. */ 531 path_beneath.parent_fd = 532 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 533 ASSERT_LE(0, path_beneath.parent_fd); 534 535 /* Test with legitimate values. */ 536 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 537 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 538 &path_beneath, 0)); 539 ASSERT_EQ(EINVAL, errno); 540 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 541 542 /* Tests with denied-by-default access right. */ 543 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 544 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 545 &path_beneath, 0)); 546 ASSERT_EQ(EINVAL, errno); 547 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 548 549 /* Test with unknown (64-bits) value. */ 550 path_beneath.allowed_access |= (1ULL << 60); 551 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 552 &path_beneath, 0)); 553 ASSERT_EQ(EINVAL, errno); 554 path_beneath.allowed_access &= ~(1ULL << 60); 555 556 /* Test with no access. */ 557 path_beneath.allowed_access = 0; 558 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 559 &path_beneath, 0)); 560 ASSERT_EQ(ENOMSG, errno); 561 path_beneath.allowed_access &= ~(1ULL << 60); 562 563 ASSERT_EQ(0, close(path_beneath.parent_fd)); 564 565 /* Enforces the ruleset. */ 566 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 567 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 568 569 ASSERT_EQ(0, close(ruleset_fd)); 570 } 571 572 /* clang-format off */ 573 574 #define ACCESS_FILE ( \ 575 LANDLOCK_ACCESS_FS_EXECUTE | \ 576 LANDLOCK_ACCESS_FS_WRITE_FILE | \ 577 LANDLOCK_ACCESS_FS_READ_FILE | \ 578 LANDLOCK_ACCESS_FS_TRUNCATE | \ 579 LANDLOCK_ACCESS_FS_IOCTL_DEV | \ 580 LANDLOCK_ACCESS_FS_RESOLVE_UNIX) 581 582 #define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX 583 584 #define ACCESS_ALL ( \ 585 ACCESS_FILE | \ 586 LANDLOCK_ACCESS_FS_READ_DIR | \ 587 LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 588 LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 589 LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 590 LANDLOCK_ACCESS_FS_MAKE_DIR | \ 591 LANDLOCK_ACCESS_FS_MAKE_REG | \ 592 LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 593 LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 594 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 595 LANDLOCK_ACCESS_FS_MAKE_SYM | \ 596 LANDLOCK_ACCESS_FS_REFER) 597 598 /* clang-format on */ 599 600 TEST_F_FORK(layout1, file_and_dir_access_rights) 601 { 602 __u64 access; 603 int err; 604 struct landlock_path_beneath_attr path_beneath_file = {}, 605 path_beneath_dir = {}; 606 struct landlock_ruleset_attr ruleset_attr = { 607 .handled_access_fs = ACCESS_ALL, 608 }; 609 const int ruleset_fd = 610 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 611 612 ASSERT_LE(0, ruleset_fd); 613 614 /* Tests access rights for files. */ 615 path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 616 ASSERT_LE(0, path_beneath_file.parent_fd); 617 618 /* Tests access rights for directories. */ 619 path_beneath_dir.parent_fd = 620 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 621 ASSERT_LE(0, path_beneath_dir.parent_fd); 622 623 for (access = 1; access <= ACCESS_LAST; access <<= 1) { 624 path_beneath_dir.allowed_access = access; 625 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 626 LANDLOCK_RULE_PATH_BENEATH, 627 &path_beneath_dir, 0)); 628 629 path_beneath_file.allowed_access = access; 630 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 631 &path_beneath_file, 0); 632 if (access & ACCESS_FILE) { 633 ASSERT_EQ(0, err); 634 } else { 635 ASSERT_EQ(-1, err); 636 ASSERT_EQ(EINVAL, errno); 637 } 638 } 639 ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 640 ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 641 ASSERT_EQ(0, close(ruleset_fd)); 642 } 643 644 TEST_F_FORK(layout0, ruleset_with_unknown_access) 645 { 646 __u64 access_mask; 647 648 for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 649 access_mask >>= 1) { 650 struct landlock_ruleset_attr ruleset_attr = { 651 .handled_access_fs = access_mask, 652 }; 653 654 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 655 sizeof(ruleset_attr), 0)); 656 ASSERT_EQ(EINVAL, errno); 657 } 658 } 659 660 TEST_F_FORK(layout0, rule_with_unknown_access) 661 { 662 __u64 access; 663 struct landlock_path_beneath_attr path_beneath = {}; 664 const struct landlock_ruleset_attr ruleset_attr = { 665 .handled_access_fs = ACCESS_ALL, 666 }; 667 const int ruleset_fd = 668 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 669 670 ASSERT_LE(0, ruleset_fd); 671 672 path_beneath.parent_fd = 673 open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC); 674 ASSERT_LE(0, path_beneath.parent_fd); 675 676 for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) { 677 path_beneath.allowed_access = access; 678 EXPECT_EQ(-1, landlock_add_rule(ruleset_fd, 679 LANDLOCK_RULE_PATH_BENEATH, 680 &path_beneath, 0)); 681 EXPECT_EQ(EINVAL, errno); 682 } 683 ASSERT_EQ(0, close(path_beneath.parent_fd)); 684 ASSERT_EQ(0, close(ruleset_fd)); 685 } 686 687 TEST_F_FORK(layout1, rule_with_unhandled_access) 688 { 689 struct landlock_ruleset_attr ruleset_attr = { 690 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 691 }; 692 struct landlock_path_beneath_attr path_beneath = {}; 693 int ruleset_fd; 694 __u64 access; 695 696 ruleset_fd = 697 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 698 ASSERT_LE(0, ruleset_fd); 699 700 path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 701 ASSERT_LE(0, path_beneath.parent_fd); 702 703 for (access = 1; access > 0; access <<= 1) { 704 int err; 705 706 path_beneath.allowed_access = access; 707 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 708 &path_beneath, 0); 709 if (access == ruleset_attr.handled_access_fs) { 710 EXPECT_EQ(0, err); 711 } else { 712 EXPECT_EQ(-1, err); 713 EXPECT_EQ(EINVAL, errno); 714 } 715 } 716 717 EXPECT_EQ(0, close(path_beneath.parent_fd)); 718 EXPECT_EQ(0, close(ruleset_fd)); 719 } 720 721 static void add_path_beneath(struct __test_metadata *const _metadata, 722 const int ruleset_fd, const __u64 allowed_access, 723 const char *const path, __u32 flags) 724 { 725 struct landlock_path_beneath_attr path_beneath = { 726 .allowed_access = allowed_access, 727 }; 728 729 path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 730 ASSERT_LE(0, path_beneath.parent_fd) 731 { 732 TH_LOG("Failed to open directory \"%s\": %s", path, 733 strerror(errno)); 734 } 735 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 736 &path_beneath, flags)) 737 { 738 TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 739 strerror(errno)); 740 } 741 ASSERT_EQ(0, close(path_beneath.parent_fd)); 742 } 743 744 struct rule { 745 const char *path; 746 __u64 access; 747 }; 748 749 /* clang-format off */ 750 751 #define ACCESS_RO ( \ 752 LANDLOCK_ACCESS_FS_READ_FILE | \ 753 LANDLOCK_ACCESS_FS_READ_DIR) 754 755 #define ACCESS_RW ( \ 756 ACCESS_RO | \ 757 LANDLOCK_ACCESS_FS_WRITE_FILE) 758 759 /* clang-format on */ 760 761 static int create_ruleset(struct __test_metadata *const _metadata, 762 const __u64 handled_access_fs, 763 const struct rule rules[]) 764 { 765 int ruleset_fd, i; 766 struct landlock_ruleset_attr ruleset_attr = { 767 .handled_access_fs = handled_access_fs, 768 }; 769 770 ruleset_fd = 771 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 772 ASSERT_LE(0, ruleset_fd) 773 { 774 TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 775 } 776 777 if (rules) 778 for (i = 0; rules[i].path; i++) { 779 if (!rules[i].access) 780 continue; 781 782 add_path_beneath(_metadata, ruleset_fd, rules[i].access, 783 rules[i].path, 0); 784 } 785 return ruleset_fd; 786 } 787 788 static void enforce_fs(struct __test_metadata *const _metadata, 789 const __u64 access_fs, const struct rule rules[]) 790 { 791 const int ruleset_fd = create_ruleset(_metadata, access_fs, rules); 792 793 enforce_ruleset(_metadata, ruleset_fd); 794 EXPECT_EQ(0, close(ruleset_fd)); 795 } 796 797 TEST_F_FORK(layout0, proc_nsfs) 798 { 799 const struct rule rules[] = { 800 { 801 .path = "/dev/null", 802 .access = LANDLOCK_ACCESS_FS_READ_FILE | 803 LANDLOCK_ACCESS_FS_WRITE_FILE, 804 }, 805 {}, 806 }; 807 struct landlock_path_beneath_attr path_beneath; 808 const int ruleset_fd = create_ruleset( 809 _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 810 rules); 811 812 ASSERT_LE(0, ruleset_fd); 813 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 814 815 enforce_ruleset(_metadata, ruleset_fd); 816 817 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 818 ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 819 ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 820 ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 821 822 ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 823 ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 824 ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 825 /* 826 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 827 * disconnected path. Such path cannot be identified and must then be 828 * allowed. 829 */ 830 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 831 832 /* 833 * Checks that it is not possible to add nsfs-like filesystem 834 * references to a ruleset. 835 */ 836 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 837 LANDLOCK_ACCESS_FS_WRITE_FILE, 838 path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 839 ASSERT_LE(0, path_beneath.parent_fd); 840 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 841 &path_beneath, 0)); 842 ASSERT_EQ(EBADFD, errno); 843 ASSERT_EQ(0, close(path_beneath.parent_fd)); 844 } 845 846 TEST_F_FORK(layout0, unpriv) 847 { 848 const struct rule rules[] = { 849 { 850 .path = TMP_DIR, 851 .access = ACCESS_RO, 852 }, 853 {}, 854 }; 855 int ruleset_fd; 856 857 drop_caps(_metadata); 858 859 ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 860 ASSERT_LE(0, ruleset_fd); 861 ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 862 ASSERT_EQ(EPERM, errno); 863 864 /* enforce_ruleset() calls prctl(no_new_privs). */ 865 enforce_ruleset(_metadata, ruleset_fd); 866 ASSERT_EQ(0, close(ruleset_fd)); 867 } 868 869 TEST_F_FORK(layout1, effective_access) 870 { 871 const struct rule rules[] = { 872 { 873 .path = dir_s1d2, 874 .access = ACCESS_RO, 875 }, 876 { 877 .path = file1_s2d2, 878 .access = LANDLOCK_ACCESS_FS_READ_FILE | 879 LANDLOCK_ACCESS_FS_WRITE_FILE, 880 }, 881 {}, 882 }; 883 char buf; 884 int reg_fd; 885 886 enforce_fs(_metadata, ACCESS_RW, rules); 887 888 /* Tests on a directory (with or without O_PATH). */ 889 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 890 ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 891 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 892 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 893 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 894 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 895 896 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 897 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 898 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 899 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 900 901 /* Tests on a file (with or without O_PATH). */ 902 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 903 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 904 905 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 906 907 /* Checks effective read and write actions. */ 908 reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 909 ASSERT_LE(0, reg_fd); 910 ASSERT_EQ(1, write(reg_fd, ".", 1)); 911 ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 912 ASSERT_EQ(1, read(reg_fd, &buf, 1)); 913 ASSERT_EQ('.', buf); 914 ASSERT_EQ(0, close(reg_fd)); 915 916 /* Just in case, double-checks effective actions. */ 917 reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 918 ASSERT_LE(0, reg_fd); 919 ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 920 ASSERT_EQ(EBADF, errno); 921 ASSERT_EQ(0, close(reg_fd)); 922 } 923 924 TEST_F_FORK(layout1, unhandled_access) 925 { 926 const struct rule rules[] = { 927 { 928 .path = dir_s1d2, 929 .access = ACCESS_RO, 930 }, 931 {}, 932 }; 933 934 /* Here, we only handle read accesses, not write accesses. */ 935 enforce_fs(_metadata, ACCESS_RO, rules); 936 937 /* 938 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 939 * opening for write-only should be allowed, but not read-write. 940 */ 941 ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 942 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 943 944 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 945 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 946 } 947 948 TEST_F_FORK(layout1, ruleset_overlap) 949 { 950 const struct rule rules[] = { 951 /* These rules should be ORed among them. */ 952 { 953 .path = dir_s1d2, 954 .access = LANDLOCK_ACCESS_FS_READ_FILE | 955 LANDLOCK_ACCESS_FS_WRITE_FILE, 956 }, 957 { 958 .path = dir_s1d2, 959 .access = LANDLOCK_ACCESS_FS_READ_FILE | 960 LANDLOCK_ACCESS_FS_READ_DIR, 961 }, 962 {}, 963 }; 964 965 enforce_fs(_metadata, ACCESS_RW, rules); 966 967 /* Checks s1d1 hierarchy. */ 968 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 969 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 970 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 971 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 972 973 /* Checks s1d2 hierarchy. */ 974 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 975 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 976 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 977 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 978 979 /* Checks s1d3 hierarchy. */ 980 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 981 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 982 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 983 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 984 } 985 986 TEST_F_FORK(layout1, layer_rule_unions) 987 { 988 const struct rule layer1[] = { 989 { 990 .path = dir_s1d2, 991 .access = LANDLOCK_ACCESS_FS_READ_FILE, 992 }, 993 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 994 { 995 .path = dir_s1d3, 996 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 997 }, 998 {}, 999 }; 1000 const struct rule layer2[] = { 1001 /* Doesn't change anything from layer1. */ 1002 { 1003 .path = dir_s1d2, 1004 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1005 LANDLOCK_ACCESS_FS_WRITE_FILE, 1006 }, 1007 {}, 1008 }; 1009 const struct rule layer3[] = { 1010 /* Only allows write (but not read) to dir_s1d3. */ 1011 { 1012 .path = dir_s1d2, 1013 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1014 }, 1015 {}, 1016 }; 1017 1018 enforce_fs(_metadata, ACCESS_RW, layer1); 1019 1020 /* Checks s1d1 hierarchy with layer1. */ 1021 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1022 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1023 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1024 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1025 1026 /* Checks s1d2 hierarchy with layer1. */ 1027 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1028 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1029 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1030 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1031 1032 /* Checks s1d3 hierarchy with layer1. */ 1033 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1034 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1035 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 1036 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1037 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1038 1039 /* Doesn't change anything from layer1. */ 1040 enforce_fs(_metadata, ACCESS_RW, layer2); 1041 1042 /* Checks s1d1 hierarchy with layer2. */ 1043 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1044 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1045 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1046 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1047 1048 /* Checks s1d2 hierarchy with layer2. */ 1049 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1050 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1051 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1052 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1053 1054 /* Checks s1d3 hierarchy with layer2. */ 1055 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1056 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1057 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 1058 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1059 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1060 1061 /* Only allows write (but not read) to dir_s1d3. */ 1062 enforce_fs(_metadata, ACCESS_RW, layer3); 1063 1064 /* Checks s1d1 hierarchy with layer3. */ 1065 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1066 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1067 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1068 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1069 1070 /* Checks s1d2 hierarchy with layer3. */ 1071 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 1072 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1073 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1074 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1075 1076 /* Checks s1d3 hierarchy with layer3. */ 1077 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1078 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1079 /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 1080 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 1081 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1082 } 1083 1084 TEST_F_FORK(layout1, non_overlapping_accesses) 1085 { 1086 const struct rule layer1[] = { 1087 { 1088 .path = dir_s1d2, 1089 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1090 }, 1091 {}, 1092 }; 1093 const struct rule layer2[] = { 1094 { 1095 .path = dir_s1d3, 1096 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1097 }, 1098 {}, 1099 }; 1100 1101 ASSERT_EQ(0, unlink(file1_s1d1)); 1102 ASSERT_EQ(0, unlink(file1_s1d2)); 1103 1104 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 1105 1106 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1107 ASSERT_EQ(EACCES, errno); 1108 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1109 ASSERT_EQ(0, unlink(file1_s1d2)); 1110 1111 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, layer2); 1112 1113 /* Unchanged accesses for file creation. */ 1114 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1115 ASSERT_EQ(EACCES, errno); 1116 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1117 1118 /* Checks file removing. */ 1119 ASSERT_EQ(-1, unlink(file1_s1d2)); 1120 ASSERT_EQ(EACCES, errno); 1121 ASSERT_EQ(0, unlink(file1_s1d3)); 1122 } 1123 1124 TEST_F_FORK(layout1, interleaved_masked_accesses) 1125 { 1126 /* 1127 * Checks overly restrictive rules: 1128 * layer 1: allows R s1d1/s1d2/s1d3/file1 1129 * layer 2: allows RW s1d1/s1d2/s1d3 1130 * allows W s1d1/s1d2 1131 * denies R s1d1/s1d2 1132 * layer 3: allows R s1d1 1133 * layer 4: allows R s1d1/s1d2 1134 * denies W s1d1/s1d2 1135 * layer 5: allows R s1d1/s1d2 1136 * layer 6: allows X ---- 1137 * layer 7: allows W s1d1/s1d2 1138 * denies R s1d1/s1d2 1139 */ 1140 const struct rule layer1_read[] = { 1141 /* Allows read access to file1_s1d3 with the first layer. */ 1142 { 1143 .path = file1_s1d3, 1144 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1145 }, 1146 {}, 1147 }; 1148 /* First rule with write restrictions. */ 1149 const struct rule layer2_read_write[] = { 1150 /* Start by granting read-write access via its parent directory... */ 1151 { 1152 .path = dir_s1d3, 1153 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1154 LANDLOCK_ACCESS_FS_WRITE_FILE, 1155 }, 1156 /* ...but also denies read access via its grandparent directory. */ 1157 { 1158 .path = dir_s1d2, 1159 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1160 }, 1161 {}, 1162 }; 1163 const struct rule layer3_read[] = { 1164 /* Allows read access via its great-grandparent directory. */ 1165 { 1166 .path = dir_s1d1, 1167 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1168 }, 1169 {}, 1170 }; 1171 const struct rule layer4_read_write[] = { 1172 /* 1173 * Try to confuse the deny access by denying write (but not 1174 * read) access via its grandparent directory. 1175 */ 1176 { 1177 .path = dir_s1d2, 1178 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1179 }, 1180 {}, 1181 }; 1182 const struct rule layer5_read[] = { 1183 /* 1184 * Try to override layer2's deny read access by explicitly 1185 * allowing read access via file1_s1d3's grandparent. 1186 */ 1187 { 1188 .path = dir_s1d2, 1189 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1190 }, 1191 {}, 1192 }; 1193 const struct rule layer6_execute[] = { 1194 /* 1195 * Restricts an unrelated file hierarchy with a new access 1196 * (non-overlapping) type. 1197 */ 1198 { 1199 .path = dir_s2d1, 1200 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1201 }, 1202 {}, 1203 }; 1204 const struct rule layer7_read_write[] = { 1205 /* 1206 * Finally, denies read access to file1_s1d3 via its 1207 * grandparent. 1208 */ 1209 { 1210 .path = dir_s1d2, 1211 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1212 }, 1213 {}, 1214 }; 1215 1216 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer1_read); 1217 1218 /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1219 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1220 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1221 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1222 1223 enforce_fs(_metadata, 1224 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 1225 layer2_read_write); 1226 1227 /* Checks that previous access rights are unchanged with layer 2. */ 1228 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1229 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1230 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1231 1232 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer3_read); 1233 1234 /* Checks that previous access rights are unchanged with layer 3. */ 1235 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1236 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1237 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1238 1239 /* This time, denies write access for the file hierarchy. */ 1240 enforce_fs(_metadata, 1241 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 1242 layer4_read_write); 1243 1244 /* 1245 * Checks that the only change with layer 4 is that write access is 1246 * denied. 1247 */ 1248 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1249 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1250 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1251 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1252 1253 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer5_read); 1254 1255 /* Checks that previous access rights are unchanged with layer 5. */ 1256 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1257 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1258 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1259 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1260 1261 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, layer6_execute); 1262 1263 /* Checks that previous access rights are unchanged with layer 6. */ 1264 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1265 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1266 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1267 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1268 1269 enforce_fs(_metadata, 1270 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 1271 layer7_read_write); 1272 1273 /* Checks read access is now denied with layer 7. */ 1274 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1275 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1276 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1277 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1278 } 1279 1280 TEST_F_FORK(layout1, inherit_subset) 1281 { 1282 const struct rule rules[] = { 1283 { 1284 .path = dir_s1d2, 1285 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1286 LANDLOCK_ACCESS_FS_READ_DIR, 1287 }, 1288 {}, 1289 }; 1290 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1291 1292 enforce_ruleset(_metadata, ruleset_fd); 1293 1294 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1295 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1296 1297 /* Write access is forbidden. */ 1298 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1299 /* Readdir access is allowed. */ 1300 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1301 1302 /* Write access is forbidden. */ 1303 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1304 /* Readdir access is allowed. */ 1305 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1306 1307 /* 1308 * Tests shared rule extension: the following rules should not grant 1309 * any new access, only remove some. Once enforced, these rules are 1310 * ANDed with the previous ones. 1311 */ 1312 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1313 dir_s1d2, 0); 1314 /* 1315 * According to ruleset_fd, dir_s1d2 should now have the 1316 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1317 * access rights (even if this directory is opened a second time). 1318 * However, when enforcing this updated ruleset, the ruleset tied to 1319 * the current process (i.e. its domain) will still only have the 1320 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1321 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1322 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1323 * be a privilege escalation. 1324 */ 1325 enforce_ruleset(_metadata, ruleset_fd); 1326 1327 /* Same tests and results as above. */ 1328 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1329 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1330 1331 /* It is still forbidden to write in file1_s1d2. */ 1332 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1333 /* Readdir access is still allowed. */ 1334 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1335 1336 /* It is still forbidden to write in file1_s1d3. */ 1337 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1338 /* Readdir access is still allowed. */ 1339 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1340 1341 /* 1342 * Try to get more privileges by adding new access rights to the parent 1343 * directory: dir_s1d1. 1344 */ 1345 add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1, 0); 1346 enforce_ruleset(_metadata, ruleset_fd); 1347 1348 /* Same tests and results as above. */ 1349 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1350 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1351 1352 /* It is still forbidden to write in file1_s1d2. */ 1353 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1354 /* Readdir access is still allowed. */ 1355 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1356 1357 /* It is still forbidden to write in file1_s1d3. */ 1358 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1359 /* Readdir access is still allowed. */ 1360 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1361 1362 /* 1363 * Now, dir_s1d3 get a new rule tied to it, only allowing 1364 * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1365 * that there was no rule tied to it before. 1366 */ 1367 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1368 dir_s1d3, 0); 1369 enforce_ruleset(_metadata, ruleset_fd); 1370 ASSERT_EQ(0, close(ruleset_fd)); 1371 1372 /* 1373 * Same tests and results as above, except for open(dir_s1d3) which is 1374 * now denied because the new rule mask the rule previously inherited 1375 * from dir_s1d2. 1376 */ 1377 1378 /* Same tests and results as above. */ 1379 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1380 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1381 1382 /* It is still forbidden to write in file1_s1d2. */ 1383 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1384 /* Readdir access is still allowed. */ 1385 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1386 1387 /* It is still forbidden to write in file1_s1d3. */ 1388 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1389 /* 1390 * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1391 * the same layer. 1392 */ 1393 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1394 } 1395 1396 TEST_F_FORK(layout1, inherit_superset) 1397 { 1398 const struct rule rules[] = { 1399 { 1400 .path = dir_s1d3, 1401 .access = ACCESS_RO, 1402 }, 1403 {}, 1404 }; 1405 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1406 1407 enforce_ruleset(_metadata, ruleset_fd); 1408 1409 /* Readdir access is denied for dir_s1d2. */ 1410 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1411 /* Readdir access is allowed for dir_s1d3. */ 1412 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1413 /* File access is allowed for file1_s1d3. */ 1414 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1415 1416 /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1417 add_path_beneath(_metadata, ruleset_fd, 1418 LANDLOCK_ACCESS_FS_READ_FILE | 1419 LANDLOCK_ACCESS_FS_READ_DIR, 1420 dir_s1d2, 0); 1421 enforce_ruleset(_metadata, ruleset_fd); 1422 EXPECT_EQ(0, close(ruleset_fd)); 1423 1424 /* Readdir access is still denied for dir_s1d2. */ 1425 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1426 /* Readdir access is still allowed for dir_s1d3. */ 1427 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1428 /* File access is still allowed for file1_s1d3. */ 1429 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1430 } 1431 1432 TEST_F_FORK(layout0, max_layers) 1433 { 1434 int i, err; 1435 const struct rule rules[] = { 1436 { 1437 .path = TMP_DIR, 1438 .access = ACCESS_RO, 1439 }, 1440 {}, 1441 }; 1442 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1443 1444 for (i = 0; i < LANDLOCK_MAX_NUM_LAYERS; i++) 1445 enforce_ruleset(_metadata, ruleset_fd); 1446 1447 for (i = 0; i < 2; i++) { 1448 err = landlock_restrict_self(ruleset_fd, 0); 1449 ASSERT_EQ(-1, err); 1450 ASSERT_EQ(E2BIG, errno); 1451 } 1452 EXPECT_EQ(0, close(ruleset_fd)); 1453 } 1454 1455 TEST_F_FORK(layout1, empty_or_same_ruleset) 1456 { 1457 struct landlock_ruleset_attr ruleset_attr = {}; 1458 int ruleset_fd; 1459 1460 /* Tests empty handled_access_fs. */ 1461 ruleset_fd = 1462 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1463 ASSERT_LE(-1, ruleset_fd); 1464 ASSERT_EQ(ENOMSG, errno); 1465 1466 /* Enforces policy which denies read access to all files. */ 1467 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, NULL); 1468 1469 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1470 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1471 1472 /* Nests a policy which denies read access to all directories. */ 1473 ruleset_fd = 1474 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, NULL); 1475 enforce_ruleset(_metadata, ruleset_fd); 1476 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1477 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1478 1479 /* Enforces a second time with the same ruleset. */ 1480 enforce_ruleset(_metadata, ruleset_fd); 1481 ASSERT_EQ(0, close(ruleset_fd)); 1482 } 1483 1484 TEST_F_FORK(layout1, rule_on_mountpoint) 1485 { 1486 const struct rule rules[] = { 1487 { 1488 .path = dir_s1d1, 1489 .access = ACCESS_RO, 1490 }, 1491 { 1492 /* dir_s3d2 is a mount point. */ 1493 .path = dir_s3d2, 1494 .access = ACCESS_RO, 1495 }, 1496 {}, 1497 }; 1498 1499 enforce_fs(_metadata, ACCESS_RW, rules); 1500 1501 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1502 1503 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1504 1505 ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1506 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1507 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1508 } 1509 1510 TEST_F_FORK(layout1, rule_over_mountpoint) 1511 { 1512 const struct rule rules[] = { 1513 { 1514 .path = dir_s1d1, 1515 .access = ACCESS_RO, 1516 }, 1517 { 1518 /* dir_s3d2 is a mount point. */ 1519 .path = dir_s3d1, 1520 .access = ACCESS_RO, 1521 }, 1522 {}, 1523 }; 1524 1525 enforce_fs(_metadata, ACCESS_RW, rules); 1526 1527 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1528 1529 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1530 1531 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1532 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1533 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1534 } 1535 1536 /* 1537 * This test verifies that we can apply a landlock rule on the root directory 1538 * (which might require special handling). 1539 */ 1540 TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1541 { 1542 struct rule rules[] = { 1543 { 1544 .path = "/", 1545 .access = ACCESS_RO, 1546 }, 1547 {}, 1548 }; 1549 1550 enforce_fs(_metadata, ACCESS_RW, rules); 1551 1552 /* Checks allowed access. */ 1553 ASSERT_EQ(0, test_open("/", O_RDONLY)); 1554 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1555 1556 rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1557 enforce_fs(_metadata, ACCESS_RW, rules); 1558 1559 /* Checks denied access (on a directory). */ 1560 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1561 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1562 } 1563 1564 TEST_F_FORK(layout1, rule_over_root_deny) 1565 { 1566 const struct rule rules[] = { 1567 { 1568 .path = "/", 1569 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1570 }, 1571 {}, 1572 }; 1573 1574 enforce_fs(_metadata, ACCESS_RW, rules); 1575 1576 /* Checks denied access (on a directory). */ 1577 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1578 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1579 } 1580 1581 TEST_F_FORK(layout1, rule_inside_mount_ns) 1582 { 1583 const struct rule rules[] = { 1584 { 1585 .path = "s3d3", 1586 .access = ACCESS_RO, 1587 }, 1588 {}, 1589 }; 1590 1591 set_cap(_metadata, CAP_SYS_ADMIN); 1592 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1593 { 1594 TH_LOG("Failed to pivot root: %s", strerror(errno)); 1595 }; 1596 ASSERT_EQ(0, chdir("/")); 1597 clear_cap(_metadata, CAP_SYS_ADMIN); 1598 1599 enforce_fs(_metadata, ACCESS_RW, rules); 1600 1601 ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1602 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1603 } 1604 1605 TEST_F_FORK(layout1, mount_and_pivot) 1606 { 1607 const struct rule rules[] = { 1608 { 1609 .path = dir_s3d2, 1610 .access = ACCESS_RO, 1611 }, 1612 {}, 1613 }; 1614 1615 enforce_fs(_metadata, ACCESS_RW, rules); 1616 1617 set_cap(_metadata, CAP_SYS_ADMIN); 1618 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1619 ASSERT_EQ(EPERM, errno); 1620 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1621 ASSERT_EQ(EPERM, errno); 1622 clear_cap(_metadata, CAP_SYS_ADMIN); 1623 } 1624 1625 TEST_F_FORK(layout1, move_mount) 1626 { 1627 const struct rule rules[] = { 1628 { 1629 .path = dir_s3d2, 1630 .access = ACCESS_RO, 1631 }, 1632 {}, 1633 }; 1634 1635 set_cap(_metadata, CAP_SYS_ADMIN); 1636 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1637 dir_s1d2, 0)) 1638 { 1639 TH_LOG("Failed to move mount: %s", strerror(errno)); 1640 } 1641 1642 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1643 dir_s3d2, 0)); 1644 clear_cap(_metadata, CAP_SYS_ADMIN); 1645 1646 enforce_fs(_metadata, ACCESS_RW, rules); 1647 1648 set_cap(_metadata, CAP_SYS_ADMIN); 1649 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1650 dir_s1d2, 0)); 1651 ASSERT_EQ(EPERM, errno); 1652 clear_cap(_metadata, CAP_SYS_ADMIN); 1653 } 1654 1655 TEST_F_FORK(layout1, topology_changes_with_net_only) 1656 { 1657 const struct landlock_ruleset_attr ruleset_net = { 1658 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 1659 LANDLOCK_ACCESS_NET_CONNECT_TCP, 1660 }; 1661 1662 /* Add network restrictions. */ 1663 drop_access_rights(_metadata, &ruleset_net); 1664 1665 /* Mount, remount, move_mount, umount, and pivot_root checks. */ 1666 set_cap(_metadata, CAP_SYS_ADMIN); 1667 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s1d2)); 1668 ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC, NULL)); 1669 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1670 dir_s2d2, 0)); 1671 ASSERT_EQ(0, umount(dir_s2d2)); 1672 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1673 ASSERT_EQ(0, chdir("/")); 1674 clear_cap(_metadata, CAP_SYS_ADMIN); 1675 } 1676 1677 TEST_F_FORK(layout1, topology_changes_with_net_and_fs) 1678 { 1679 const struct landlock_ruleset_attr ruleset_net_fs = { 1680 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 1681 LANDLOCK_ACCESS_NET_CONNECT_TCP, 1682 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 1683 }; 1684 1685 /* Add network and filesystem restrictions. */ 1686 drop_access_rights(_metadata, &ruleset_net_fs); 1687 1688 /* Mount, remount, move_mount, umount, and pivot_root checks. */ 1689 set_cap(_metadata, CAP_SYS_ADMIN); 1690 ASSERT_EQ(-1, mount_opt(&mnt_tmp, dir_s1d2)); 1691 ASSERT_EQ(EPERM, errno); 1692 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC, NULL)); 1693 ASSERT_EQ(EPERM, errno); 1694 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1695 dir_s2d2, 0)); 1696 ASSERT_EQ(EPERM, errno); 1697 ASSERT_EQ(-1, umount(dir_s3d2)); 1698 ASSERT_EQ(EPERM, errno); 1699 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1700 ASSERT_EQ(EPERM, errno); 1701 clear_cap(_metadata, CAP_SYS_ADMIN); 1702 } 1703 1704 TEST_F_FORK(layout1, release_inodes) 1705 { 1706 const struct rule rules[] = { 1707 { 1708 .path = dir_s1d1, 1709 .access = ACCESS_RO, 1710 }, 1711 { 1712 .path = dir_s3d2, 1713 .access = ACCESS_RO, 1714 }, 1715 { 1716 .path = dir_s3d3, 1717 .access = ACCESS_RO, 1718 }, 1719 {}, 1720 }; 1721 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1722 1723 /* Unmount a file hierarchy while it is being used by a ruleset. */ 1724 set_cap(_metadata, CAP_SYS_ADMIN); 1725 ASSERT_EQ(0, umount(dir_s3d2)); 1726 clear_cap(_metadata, CAP_SYS_ADMIN); 1727 1728 enforce_ruleset(_metadata, ruleset_fd); 1729 EXPECT_EQ(0, close(ruleset_fd)); 1730 1731 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1732 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1733 /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1734 ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1735 } 1736 1737 /* 1738 * This test checks that a rule on a directory used as a mount point does not 1739 * grant access to the mount covering it. It is a generalization of the bind 1740 * mount case in layout3_fs.hostfs.release_inodes that tests hidden mount points. 1741 */ 1742 TEST_F_FORK(layout1, covered_rule) 1743 { 1744 const struct rule layer1[] = { 1745 { 1746 .path = dir_s3d2, 1747 .access = LANDLOCK_ACCESS_FS_READ_DIR, 1748 }, 1749 {}, 1750 }; 1751 int ruleset_fd; 1752 1753 /* Unmount to simplify FIXTURE_TEARDOWN. */ 1754 set_cap(_metadata, CAP_SYS_ADMIN); 1755 ASSERT_EQ(0, umount(dir_s3d2)); 1756 clear_cap(_metadata, CAP_SYS_ADMIN); 1757 1758 /* Creates a ruleset with the future hidden directory. */ 1759 ruleset_fd = 1760 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 1761 1762 /* Covers with a new mount point. */ 1763 set_cap(_metadata, CAP_SYS_ADMIN); 1764 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 1765 clear_cap(_metadata, CAP_SYS_ADMIN); 1766 1767 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1768 1769 enforce_ruleset(_metadata, ruleset_fd); 1770 ASSERT_EQ(0, close(ruleset_fd)); 1771 1772 /* Checks that access to the new mount point is denied. */ 1773 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1774 } 1775 1776 enum relative_access { 1777 REL_OPEN, 1778 REL_CHDIR, 1779 REL_CHROOT_ONLY, 1780 REL_CHROOT_CHDIR, 1781 }; 1782 1783 static void test_relative_path(struct __test_metadata *const _metadata, 1784 const enum relative_access rel) 1785 { 1786 /* 1787 * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1788 * is not a disconnected root directory). 1789 */ 1790 const struct rule layer1_base[] = { 1791 { 1792 .path = TMP_DIR, 1793 .access = ACCESS_RO, 1794 }, 1795 {}, 1796 }; 1797 const struct rule layer2_subs[] = { 1798 { 1799 .path = dir_s1d2, 1800 .access = ACCESS_RO, 1801 }, 1802 { 1803 .path = dir_s2d2, 1804 .access = ACCESS_RO, 1805 }, 1806 {}, 1807 }; 1808 int dirfd, ruleset_fd; 1809 1810 enforce_fs(_metadata, ACCESS_RW, layer1_base); 1811 1812 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1813 1814 ASSERT_LE(0, ruleset_fd); 1815 switch (rel) { 1816 case REL_OPEN: 1817 case REL_CHDIR: 1818 break; 1819 case REL_CHROOT_ONLY: 1820 ASSERT_EQ(0, chdir(dir_s2d2)); 1821 break; 1822 case REL_CHROOT_CHDIR: 1823 ASSERT_EQ(0, chdir(dir_s1d2)); 1824 break; 1825 default: 1826 ASSERT_TRUE(false); 1827 return; 1828 } 1829 1830 set_cap(_metadata, CAP_SYS_CHROOT); 1831 enforce_ruleset(_metadata, ruleset_fd); 1832 1833 switch (rel) { 1834 case REL_OPEN: 1835 dirfd = open(dir_s1d2, O_DIRECTORY); 1836 ASSERT_LE(0, dirfd); 1837 break; 1838 case REL_CHDIR: 1839 ASSERT_EQ(0, chdir(dir_s1d2)); 1840 dirfd = AT_FDCWD; 1841 break; 1842 case REL_CHROOT_ONLY: 1843 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1844 ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 1845 { 1846 TH_LOG("Failed to chroot: %s", strerror(errno)); 1847 } 1848 dirfd = AT_FDCWD; 1849 break; 1850 case REL_CHROOT_CHDIR: 1851 /* Do chroot into dir_s1d2. */ 1852 ASSERT_EQ(0, chroot(".")) 1853 { 1854 TH_LOG("Failed to chroot: %s", strerror(errno)); 1855 } 1856 dirfd = AT_FDCWD; 1857 break; 1858 } 1859 1860 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1861 test_open_rel(dirfd, "..", O_RDONLY)); 1862 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1863 1864 if (rel == REL_CHROOT_ONLY) { 1865 /* The current directory is dir_s2d2. */ 1866 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1867 } else { 1868 /* The current directory is dir_s1d2. */ 1869 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1870 } 1871 1872 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1873 /* Checks the root dir_s1d2. */ 1874 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1875 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1876 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1877 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1878 } 1879 1880 if (rel != REL_CHROOT_CHDIR) { 1881 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1882 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1883 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1884 O_RDONLY)); 1885 1886 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1887 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1888 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1889 O_RDONLY)); 1890 } 1891 1892 if (rel == REL_OPEN) 1893 ASSERT_EQ(0, close(dirfd)); 1894 ASSERT_EQ(0, close(ruleset_fd)); 1895 } 1896 1897 TEST_F_FORK(layout1, relative_open) 1898 { 1899 test_relative_path(_metadata, REL_OPEN); 1900 } 1901 1902 TEST_F_FORK(layout1, relative_chdir) 1903 { 1904 test_relative_path(_metadata, REL_CHDIR); 1905 } 1906 1907 TEST_F_FORK(layout1, relative_chroot_only) 1908 { 1909 test_relative_path(_metadata, REL_CHROOT_ONLY); 1910 } 1911 1912 TEST_F_FORK(layout1, relative_chroot_chdir) 1913 { 1914 test_relative_path(_metadata, REL_CHROOT_CHDIR); 1915 } 1916 1917 static void copy_file(struct __test_metadata *const _metadata, 1918 const char *const src_path, const char *const dst_path) 1919 { 1920 int dst_fd, src_fd; 1921 struct stat statbuf; 1922 1923 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1924 ASSERT_LE(0, dst_fd) 1925 { 1926 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1927 } 1928 src_fd = open(src_path, O_RDONLY | O_CLOEXEC); 1929 ASSERT_LE(0, src_fd) 1930 { 1931 TH_LOG("Failed to open \"%s\": %s", src_path, strerror(errno)); 1932 } 1933 ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1934 ASSERT_EQ(statbuf.st_size, 1935 sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 1936 ASSERT_EQ(0, close(src_fd)); 1937 ASSERT_EQ(0, close(dst_fd)); 1938 } 1939 1940 static void test_execute(struct __test_metadata *const _metadata, const int err, 1941 const char *const path) 1942 { 1943 int status; 1944 char *const argv[] = { (char *)path, NULL }; 1945 const pid_t child = fork(); 1946 1947 ASSERT_LE(0, child); 1948 if (child == 0) { 1949 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 1950 { 1951 TH_LOG("Failed to execute \"%s\": %s", path, 1952 strerror(errno)); 1953 }; 1954 ASSERT_EQ(err, errno); 1955 _exit(__test_passed(_metadata) ? 2 : 1); 1956 return; 1957 } 1958 ASSERT_EQ(child, waitpid(child, &status, 0)); 1959 ASSERT_EQ(1, WIFEXITED(status)); 1960 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 1961 { 1962 TH_LOG("Unexpected return code for \"%s\"", path); 1963 }; 1964 } 1965 1966 static void test_check_exec(struct __test_metadata *const _metadata, 1967 const int err, const char *const path) 1968 { 1969 int ret; 1970 char *const argv[] = { (char *)path, NULL }; 1971 1972 ret = sys_execveat(AT_FDCWD, path, argv, NULL, 1973 AT_EMPTY_PATH | AT_EXECVE_CHECK); 1974 if (err) { 1975 EXPECT_EQ(-1, ret); 1976 EXPECT_EQ(errno, err); 1977 } else { 1978 EXPECT_EQ(0, ret); 1979 } 1980 } 1981 1982 TEST_F_FORK(layout1, execute) 1983 { 1984 const struct rule rules[] = { 1985 { 1986 .path = dir_s1d2, 1987 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1988 }, 1989 {}, 1990 }; 1991 1992 copy_file(_metadata, bin_true, file1_s1d1); 1993 copy_file(_metadata, bin_true, file1_s1d2); 1994 copy_file(_metadata, bin_true, file1_s1d3); 1995 1996 /* Checks before file1_s1d1 being denied. */ 1997 test_execute(_metadata, 0, file1_s1d1); 1998 test_check_exec(_metadata, 0, file1_s1d1); 1999 2000 enforce_fs(_metadata, rules[0].access, rules); 2001 2002 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 2003 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2004 test_execute(_metadata, EACCES, file1_s1d1); 2005 test_check_exec(_metadata, EACCES, file1_s1d1); 2006 2007 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 2008 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2009 test_execute(_metadata, 0, file1_s1d2); 2010 test_check_exec(_metadata, 0, file1_s1d2); 2011 2012 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 2013 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2014 test_execute(_metadata, 0, file1_s1d3); 2015 test_check_exec(_metadata, 0, file1_s1d3); 2016 } 2017 2018 TEST_F_FORK(layout1, umount_sandboxer) 2019 { 2020 int pipe_child[2], pipe_parent[2]; 2021 char buf_parent; 2022 pid_t child; 2023 int status; 2024 2025 copy_file(_metadata, bin_sandbox_and_launch, file1_s3d3); 2026 ASSERT_EQ(0, pipe2(pipe_child, 0)); 2027 ASSERT_EQ(0, pipe2(pipe_parent, 0)); 2028 2029 child = fork(); 2030 ASSERT_LE(0, child); 2031 if (child == 0) { 2032 char pipe_child_str[12], pipe_parent_str[12]; 2033 char *const argv[] = { (char *)file1_s3d3, 2034 (char *)bin_wait_pipe, pipe_child_str, 2035 pipe_parent_str, NULL }; 2036 2037 /* Passes the pipe FDs to the executed binary and its child. */ 2038 EXPECT_EQ(0, close(pipe_child[0])); 2039 EXPECT_EQ(0, close(pipe_parent[1])); 2040 snprintf(pipe_child_str, sizeof(pipe_child_str), "%d", 2041 pipe_child[1]); 2042 snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d", 2043 pipe_parent[0]); 2044 2045 /* 2046 * We need bin_sandbox_and_launch (copied inside the mount as 2047 * file1_s3d3) to execute bin_wait_pipe (outside the mount) to 2048 * make sure the mount point will not be EBUSY because of 2049 * file1_s3d3 being in use. This avoids a potential race 2050 * condition between the following read() and umount() calls. 2051 */ 2052 ASSERT_EQ(0, execve(argv[0], argv, NULL)) 2053 { 2054 TH_LOG("Failed to execute \"%s\": %s", argv[0], 2055 strerror(errno)); 2056 }; 2057 _exit(1); 2058 return; 2059 } 2060 2061 EXPECT_EQ(0, close(pipe_child[1])); 2062 EXPECT_EQ(0, close(pipe_parent[0])); 2063 2064 /* Waits for the child to sandbox itself. */ 2065 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2066 2067 /* Tests that the sandboxer is tied to its mount point. */ 2068 set_cap(_metadata, CAP_SYS_ADMIN); 2069 EXPECT_EQ(-1, umount(dir_s3d2)); 2070 EXPECT_EQ(EBUSY, errno); 2071 clear_cap(_metadata, CAP_SYS_ADMIN); 2072 2073 /* Signals the child to launch a grandchild. */ 2074 EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2075 2076 /* Waits for the grandchild. */ 2077 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2078 2079 /* Tests that the domain's sandboxer is not tied to its mount point. */ 2080 set_cap(_metadata, CAP_SYS_ADMIN); 2081 EXPECT_EQ(0, umount(dir_s3d2)) 2082 { 2083 TH_LOG("Failed to umount \"%s\": %s", dir_s3d2, 2084 strerror(errno)); 2085 }; 2086 clear_cap(_metadata, CAP_SYS_ADMIN); 2087 2088 /* Signals the grandchild to terminate. */ 2089 EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2090 ASSERT_EQ(child, waitpid(child, &status, 0)); 2091 ASSERT_EQ(1, WIFEXITED(status)); 2092 ASSERT_EQ(0, WEXITSTATUS(status)); 2093 } 2094 2095 TEST_F_FORK(layout1, link) 2096 { 2097 const struct rule layer1[] = { 2098 { 2099 .path = dir_s1d2, 2100 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2101 }, 2102 {}, 2103 }; 2104 const struct rule layer2[] = { 2105 { 2106 .path = dir_s1d3, 2107 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2108 }, 2109 {}, 2110 }; 2111 2112 ASSERT_EQ(0, unlink(file1_s1d1)); 2113 ASSERT_EQ(0, unlink(file1_s1d2)); 2114 ASSERT_EQ(0, unlink(file1_s1d3)); 2115 2116 enforce_fs(_metadata, layer1[0].access, layer1); 2117 2118 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2119 ASSERT_EQ(EACCES, errno); 2120 2121 /* Denies linking because of reparenting. */ 2122 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2123 ASSERT_EQ(EXDEV, errno); 2124 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2125 ASSERT_EQ(EXDEV, errno); 2126 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2127 ASSERT_EQ(EXDEV, errno); 2128 2129 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2130 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2131 2132 /* Prepares for next unlinks. */ 2133 ASSERT_EQ(0, unlink(file2_s1d2)); 2134 ASSERT_EQ(0, unlink(file2_s1d3)); 2135 2136 enforce_fs(_metadata, layer2[0].access, layer2); 2137 2138 /* Checks that linkind doesn't require the ability to delete a file. */ 2139 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 2140 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 2141 } 2142 2143 static int test_rename(const char *const oldpath, const char *const newpath) 2144 { 2145 if (rename(oldpath, newpath)) 2146 return errno; 2147 return 0; 2148 } 2149 2150 static int test_exchange(const char *const oldpath, const char *const newpath) 2151 { 2152 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 2153 return errno; 2154 return 0; 2155 } 2156 2157 static int test_renameat(int olddirfd, const char *oldpath, int newdirfd, 2158 const char *newpath) 2159 { 2160 if (renameat2(olddirfd, oldpath, newdirfd, newpath, 0)) 2161 return errno; 2162 return 0; 2163 } 2164 2165 static int test_exchangeat(int olddirfd, const char *oldpath, int newdirfd, 2166 const char *newpath) 2167 { 2168 if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_EXCHANGE)) 2169 return errno; 2170 return 0; 2171 } 2172 2173 TEST_F_FORK(layout1, rename_file) 2174 { 2175 const struct rule rules[] = { 2176 { 2177 .path = dir_s1d3, 2178 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2179 }, 2180 { 2181 .path = dir_s2d2, 2182 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2183 }, 2184 {}, 2185 }; 2186 2187 ASSERT_EQ(0, unlink(file1_s1d2)); 2188 2189 enforce_fs(_metadata, rules[0].access, rules); 2190 2191 /* 2192 * Tries to replace a file, from a directory that allows file removal, 2193 * but to a different directory (which also allows file removal). 2194 */ 2195 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 2196 ASSERT_EQ(EXDEV, errno); 2197 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 2198 RENAME_EXCHANGE)); 2199 ASSERT_EQ(EXDEV, errno); 2200 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2201 RENAME_EXCHANGE)); 2202 ASSERT_EQ(EXDEV, errno); 2203 2204 /* 2205 * Tries to replace a file, from a directory that denies file removal, 2206 * to a different directory (which allows file removal). 2207 */ 2208 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2209 ASSERT_EQ(EACCES, errno); 2210 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 2211 RENAME_EXCHANGE)); 2212 ASSERT_EQ(EACCES, errno); 2213 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 2214 RENAME_EXCHANGE)); 2215 ASSERT_EQ(EXDEV, errno); 2216 2217 /* Exchanges files and directories that partially allow removal. */ 2218 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 2219 RENAME_EXCHANGE)); 2220 ASSERT_EQ(EACCES, errno); 2221 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 2222 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 2223 ASSERT_EQ(EACCES, errno); 2224 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 2225 RENAME_EXCHANGE)); 2226 ASSERT_EQ(EACCES, errno); 2227 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 2228 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2229 ASSERT_EQ(EACCES, errno); 2230 2231 /* Renames files with different parents. */ 2232 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2233 ASSERT_EQ(EXDEV, errno); 2234 ASSERT_EQ(0, unlink(file1_s1d3)); 2235 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2236 ASSERT_EQ(EACCES, errno); 2237 2238 /* Exchanges and renames files with same parent. */ 2239 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 2240 RENAME_EXCHANGE)); 2241 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 2242 2243 /* Exchanges files and directories with same parent, twice. */ 2244 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2245 RENAME_EXCHANGE)); 2246 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2247 RENAME_EXCHANGE)); 2248 } 2249 2250 TEST_F_FORK(layout1, rename_dir) 2251 { 2252 const struct rule rules[] = { 2253 { 2254 .path = dir_s1d2, 2255 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2256 }, 2257 { 2258 .path = dir_s2d1, 2259 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2260 }, 2261 {}, 2262 }; 2263 2264 /* Empties dir_s1d3 to allow renaming. */ 2265 ASSERT_EQ(0, unlink(file1_s1d3)); 2266 ASSERT_EQ(0, unlink(file2_s1d3)); 2267 2268 enforce_fs(_metadata, rules[0].access, rules); 2269 2270 /* Exchanges and renames directory to a different parent. */ 2271 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2272 RENAME_EXCHANGE)); 2273 ASSERT_EQ(EXDEV, errno); 2274 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 2275 ASSERT_EQ(EXDEV, errno); 2276 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2277 RENAME_EXCHANGE)); 2278 ASSERT_EQ(EXDEV, errno); 2279 2280 /* 2281 * Exchanges directory to the same parent, which doesn't allow 2282 * directory removal. 2283 */ 2284 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 2285 RENAME_EXCHANGE)); 2286 ASSERT_EQ(EACCES, errno); 2287 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 2288 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 2289 ASSERT_EQ(EACCES, errno); 2290 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 2291 RENAME_EXCHANGE)); 2292 ASSERT_EQ(EACCES, errno); 2293 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 2294 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2295 ASSERT_EQ(EACCES, errno); 2296 2297 /* 2298 * Exchanges and renames directory to the same parent, which allows 2299 * directory removal. 2300 */ 2301 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 2302 RENAME_EXCHANGE)); 2303 ASSERT_EQ(0, unlink(dir_s1d3)); 2304 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2305 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 2306 ASSERT_EQ(0, rmdir(dir_s1d3)); 2307 } 2308 2309 TEST_F_FORK(layout1, reparent_refer) 2310 { 2311 const struct rule layer1[] = { 2312 { 2313 .path = dir_s1d2, 2314 .access = LANDLOCK_ACCESS_FS_REFER, 2315 }, 2316 { 2317 .path = dir_s2d2, 2318 .access = LANDLOCK_ACCESS_FS_REFER, 2319 }, 2320 {}, 2321 }; 2322 2323 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2324 2325 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2326 ASSERT_EQ(EXDEV, errno); 2327 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2328 ASSERT_EQ(EXDEV, errno); 2329 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2330 ASSERT_EQ(EXDEV, errno); 2331 2332 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2333 ASSERT_EQ(EXDEV, errno); 2334 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2335 ASSERT_EQ(EXDEV, errno); 2336 /* 2337 * Moving should only be allowed when the source and the destination 2338 * parent directory have REFER. 2339 */ 2340 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2341 ASSERT_EQ(ENOTEMPTY, errno); 2342 ASSERT_EQ(0, unlink(file1_s2d3)); 2343 ASSERT_EQ(0, unlink(file2_s2d3)); 2344 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2345 } 2346 2347 /* Checks renames beneath dir_s1d1. */ 2348 static void refer_denied_by_default(struct __test_metadata *const _metadata, 2349 const struct rule layer1[], 2350 const int layer1_err, 2351 const struct rule layer2[]) 2352 { 2353 ASSERT_EQ(0, unlink(file1_s1d2)); 2354 2355 enforce_fs(_metadata, layer1[0].access, layer1); 2356 2357 /* 2358 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 2359 * layer1_err), then it allows some different-parent renames and links. 2360 */ 2361 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 2362 if (layer1_err == 0) 2363 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 2364 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 2365 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 2366 2367 enforce_fs(_metadata, layer2[0].access, layer2); 2368 2369 /* 2370 * Now, either the first or the second layer does not handle 2371 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 2372 * renames and links are denied, thus making the layer handling 2373 * LANDLOCK_ACCESS_FS_REFER null and void. 2374 */ 2375 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 2376 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 2377 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 2378 } 2379 2380 const struct rule layer_dir_s1d1_refer[] = { 2381 { 2382 .path = dir_s1d1, 2383 .access = LANDLOCK_ACCESS_FS_REFER, 2384 }, 2385 {}, 2386 }; 2387 2388 const struct rule layer_dir_s1d1_execute[] = { 2389 { 2390 /* Matches a parent directory. */ 2391 .path = dir_s1d1, 2392 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2393 }, 2394 {}, 2395 }; 2396 2397 const struct rule layer_dir_s2d1_execute[] = { 2398 { 2399 /* Does not match a parent directory. */ 2400 .path = dir_s2d1, 2401 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2402 }, 2403 {}, 2404 }; 2405 2406 /* 2407 * Tests precedence over renames: denied by default for different parent 2408 * directories, *with* a rule matching a parent directory, but not directly 2409 * denying access (with MAKE_REG nor REMOVE). 2410 */ 2411 TEST_F_FORK(layout1, refer_denied_by_default1) 2412 { 2413 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2414 layer_dir_s1d1_execute); 2415 } 2416 2417 /* 2418 * Same test but this time turning around the ABI version order: the first 2419 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2420 */ 2421 TEST_F_FORK(layout1, refer_denied_by_default2) 2422 { 2423 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 2424 layer_dir_s1d1_refer); 2425 } 2426 2427 /* 2428 * Tests precedence over renames: denied by default for different parent 2429 * directories, *without* a rule matching a parent directory, but not directly 2430 * denying access (with MAKE_REG nor REMOVE). 2431 */ 2432 TEST_F_FORK(layout1, refer_denied_by_default3) 2433 { 2434 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2435 layer_dir_s2d1_execute); 2436 } 2437 2438 /* 2439 * Same test but this time turning around the ABI version order: the first 2440 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2441 */ 2442 TEST_F_FORK(layout1, refer_denied_by_default4) 2443 { 2444 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 2445 layer_dir_s1d1_refer); 2446 } 2447 2448 /* 2449 * Tests walking through a denied root mount. 2450 */ 2451 TEST_F_FORK(layout1, refer_mount_root_deny) 2452 { 2453 int root_fd; 2454 2455 /* Creates a mount object from a non-mount point. */ 2456 set_cap(_metadata, CAP_SYS_ADMIN); 2457 root_fd = 2458 open_tree(AT_FDCWD, dir_s1d1, 2459 AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); 2460 clear_cap(_metadata, CAP_SYS_ADMIN); 2461 ASSERT_LE(0, root_fd); 2462 2463 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, NULL); 2464 2465 /* Link denied by Landlock: EACCES. */ 2466 EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0)); 2467 EXPECT_EQ(EACCES, errno); 2468 2469 /* renameat2() always returns EBUSY. */ 2470 EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0)); 2471 EXPECT_EQ(EBUSY, errno); 2472 2473 EXPECT_EQ(0, close(root_fd)); 2474 } 2475 2476 TEST_F_FORK(layout1, refer_part_mount_tree_is_allowed) 2477 { 2478 const struct rule layer1[] = { 2479 { 2480 /* Parent mount point. */ 2481 .path = dir_s3d1, 2482 .access = LANDLOCK_ACCESS_FS_REFER | 2483 LANDLOCK_ACCESS_FS_MAKE_REG, 2484 }, 2485 { 2486 /* 2487 * Removing the source file is allowed because its 2488 * access rights are already a superset of the 2489 * destination. 2490 */ 2491 .path = dir_s3d4, 2492 .access = LANDLOCK_ACCESS_FS_REFER | 2493 LANDLOCK_ACCESS_FS_MAKE_REG | 2494 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2495 }, 2496 {}, 2497 }; 2498 2499 ASSERT_EQ(0, unlink(file1_s3d3)); 2500 enforce_fs(_metadata, 2501 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 2502 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2503 layer1); 2504 2505 ASSERT_EQ(0, rename(file1_s3d4, file1_s3d3)); 2506 } 2507 2508 TEST_F_FORK(layout1, reparent_link) 2509 { 2510 const struct rule layer1[] = { 2511 { 2512 .path = dir_s1d2, 2513 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2514 }, 2515 { 2516 .path = dir_s1d3, 2517 .access = LANDLOCK_ACCESS_FS_REFER, 2518 }, 2519 { 2520 .path = dir_s2d2, 2521 .access = LANDLOCK_ACCESS_FS_REFER, 2522 }, 2523 { 2524 .path = dir_s2d3, 2525 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2526 }, 2527 {}, 2528 }; 2529 2530 enforce_fs(_metadata, 2531 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 2532 layer1); 2533 2534 ASSERT_EQ(0, unlink(file1_s1d1)); 2535 ASSERT_EQ(0, unlink(file1_s1d2)); 2536 ASSERT_EQ(0, unlink(file1_s1d3)); 2537 2538 /* Denies linking because of missing MAKE_REG. */ 2539 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2540 ASSERT_EQ(EACCES, errno); 2541 /* Denies linking because of missing source and destination REFER. */ 2542 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2543 ASSERT_EQ(EXDEV, errno); 2544 /* Denies linking because of missing source REFER. */ 2545 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2546 ASSERT_EQ(EXDEV, errno); 2547 2548 /* Denies linking because of missing MAKE_REG. */ 2549 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2550 ASSERT_EQ(EACCES, errno); 2551 /* Denies linking because of missing destination REFER. */ 2552 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2553 ASSERT_EQ(EXDEV, errno); 2554 2555 /* Allows linking because of REFER and MAKE_REG. */ 2556 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2557 ASSERT_EQ(0, unlink(file1_s2d2)); 2558 /* Reverse linking denied because of missing MAKE_REG. */ 2559 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2560 ASSERT_EQ(EACCES, errno); 2561 ASSERT_EQ(0, unlink(file1_s2d3)); 2562 /* Checks reverse linking. */ 2563 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2564 ASSERT_EQ(0, unlink(file1_s1d3)); 2565 2566 /* 2567 * This is OK for a file link, but it should not be allowed for a 2568 * directory rename (because of the superset of access rights. 2569 */ 2570 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2571 ASSERT_EQ(0, unlink(file1_s1d3)); 2572 2573 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2574 ASSERT_EQ(EXDEV, errno); 2575 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2576 ASSERT_EQ(EXDEV, errno); 2577 2578 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2579 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2580 } 2581 2582 TEST_F_FORK(layout1, reparent_rename) 2583 { 2584 /* Same rules as for reparent_link. */ 2585 const struct rule layer1[] = { 2586 { 2587 .path = dir_s1d2, 2588 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2589 }, 2590 { 2591 .path = dir_s1d3, 2592 .access = LANDLOCK_ACCESS_FS_REFER, 2593 }, 2594 { 2595 .path = dir_s2d2, 2596 .access = LANDLOCK_ACCESS_FS_REFER, 2597 }, 2598 { 2599 .path = dir_s2d3, 2600 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2601 }, 2602 {}, 2603 }; 2604 2605 enforce_fs(_metadata, 2606 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 2607 layer1); 2608 2609 ASSERT_EQ(0, unlink(file1_s1d2)); 2610 ASSERT_EQ(0, unlink(file1_s1d3)); 2611 2612 /* Denies renaming because of missing MAKE_REG. */ 2613 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2614 RENAME_EXCHANGE)); 2615 ASSERT_EQ(EACCES, errno); 2616 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2617 RENAME_EXCHANGE)); 2618 ASSERT_EQ(EACCES, errno); 2619 ASSERT_EQ(0, unlink(file1_s1d1)); 2620 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2621 ASSERT_EQ(EACCES, errno); 2622 /* Even denies same file exchange. */ 2623 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2624 RENAME_EXCHANGE)); 2625 ASSERT_EQ(EACCES, errno); 2626 2627 /* Denies renaming because of missing source and destination REFER. */ 2628 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2629 ASSERT_EQ(EXDEV, errno); 2630 /* 2631 * Denies renaming because of missing MAKE_REG, source and destination 2632 * REFER. 2633 */ 2634 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2635 RENAME_EXCHANGE)); 2636 ASSERT_EQ(EACCES, errno); 2637 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2638 RENAME_EXCHANGE)); 2639 ASSERT_EQ(EACCES, errno); 2640 2641 /* Denies renaming because of missing source REFER. */ 2642 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2643 ASSERT_EQ(EXDEV, errno); 2644 /* Denies renaming because of missing MAKE_REG. */ 2645 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2646 RENAME_EXCHANGE)); 2647 ASSERT_EQ(EACCES, errno); 2648 2649 /* Denies renaming because of missing MAKE_REG. */ 2650 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2651 ASSERT_EQ(EACCES, errno); 2652 /* Denies renaming because of missing destination REFER*/ 2653 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2654 ASSERT_EQ(EXDEV, errno); 2655 2656 /* Denies exchange because of one missing MAKE_REG. */ 2657 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2658 RENAME_EXCHANGE)); 2659 ASSERT_EQ(EACCES, errno); 2660 /* Allows renaming because of REFER and MAKE_REG. */ 2661 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2662 2663 /* Reverse renaming denied because of missing MAKE_REG. */ 2664 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2665 ASSERT_EQ(EACCES, errno); 2666 ASSERT_EQ(0, unlink(file1_s2d3)); 2667 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2668 2669 /* Tests reverse renaming. */ 2670 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2671 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2672 RENAME_EXCHANGE)); 2673 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2674 2675 /* 2676 * This is OK for a file rename, but it should not be allowed for a 2677 * directory rename (because of the superset of access rights). 2678 */ 2679 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2680 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2681 2682 /* 2683 * Tests superset restrictions applied to directories. Not only the 2684 * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2685 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2686 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2687 * directly by the moved dir_s2d3. 2688 */ 2689 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2690 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2691 /* 2692 * The first rename is allowed but not the exchange because dir_s1d3's 2693 * parent (dir_s1d2) doesn't have REFER. 2694 */ 2695 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2696 RENAME_EXCHANGE)); 2697 ASSERT_EQ(EXDEV, errno); 2698 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2699 RENAME_EXCHANGE)); 2700 ASSERT_EQ(EXDEV, errno); 2701 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2702 ASSERT_EQ(EXDEV, errno); 2703 2704 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2705 ASSERT_EQ(EXDEV, errno); 2706 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2707 ASSERT_EQ(EXDEV, errno); 2708 2709 /* Renaming in the same directory is always allowed. */ 2710 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2711 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2712 2713 ASSERT_EQ(0, unlink(file1_s1d2)); 2714 /* Denies because of missing source MAKE_REG and destination REFER. */ 2715 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2716 ASSERT_EQ(EXDEV, errno); 2717 2718 ASSERT_EQ(0, unlink(file1_s1d3)); 2719 /* Denies because of missing source MAKE_REG and REFER. */ 2720 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2721 ASSERT_EQ(EXDEV, errno); 2722 } 2723 2724 static void 2725 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2726 { 2727 const struct rule layer1[] = { 2728 { 2729 .path = dir_s1d2, 2730 .access = LANDLOCK_ACCESS_FS_REFER, 2731 }, 2732 { 2733 /* Interesting for the layer2 tests. */ 2734 .path = dir_s1d3, 2735 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2736 }, 2737 { 2738 .path = dir_s2d2, 2739 .access = LANDLOCK_ACCESS_FS_REFER, 2740 }, 2741 { 2742 .path = dir_s2d3, 2743 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2744 }, 2745 {}, 2746 }; 2747 enforce_fs(_metadata, 2748 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 2749 layer1); 2750 } 2751 2752 static void 2753 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2754 { 2755 const struct rule layer2[] = { 2756 { 2757 .path = dir_s2d3, 2758 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2759 }, 2760 {}, 2761 }; 2762 /* 2763 * Same checks as before but with a second layer and a new MAKE_DIR 2764 * rule (and no explicit handling of REFER). 2765 */ 2766 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2767 } 2768 2769 TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2770 { 2771 ASSERT_EQ(0, unlink(file1_s2d2)); 2772 ASSERT_EQ(0, unlink(file1_s2d3)); 2773 2774 reparent_exdev_layers_enforce1(_metadata); 2775 2776 /* 2777 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2778 * because it doesn't inherit new access rights. 2779 */ 2780 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2781 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2782 2783 /* 2784 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2785 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2786 * already allowed for dir_s1d3. 2787 */ 2788 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2789 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2790 2791 /* 2792 * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2793 * because it cannot inherit MAKE_REG right (which is dedicated to 2794 * directories). 2795 */ 2796 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2797 2798 reparent_exdev_layers_enforce2(_metadata); 2799 2800 /* 2801 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2802 * MAKE_DIR is not tied to dir_s2d2. 2803 */ 2804 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2805 ASSERT_EQ(EACCES, errno); 2806 2807 /* 2808 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2809 * would grants MAKE_REG and MAKE_DIR rights to it. 2810 */ 2811 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2812 ASSERT_EQ(EXDEV, errno); 2813 2814 /* 2815 * Moving the file2_s1d3 file below dir_s2d3 is denied because the 2816 * second layer does not handle REFER, which is always denied by 2817 * default. 2818 */ 2819 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 2820 ASSERT_EQ(EXDEV, errno); 2821 } 2822 2823 TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2824 { 2825 reparent_exdev_layers_enforce1(_metadata); 2826 2827 /* Checks EACCES predominance over EXDEV. */ 2828 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2829 ASSERT_EQ(EACCES, errno); 2830 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2831 ASSERT_EQ(EACCES, errno); 2832 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2833 ASSERT_EQ(EXDEV, errno); 2834 /* Modify layout! */ 2835 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 2836 2837 /* Without REFER source. */ 2838 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2839 ASSERT_EQ(EXDEV, errno); 2840 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2841 ASSERT_EQ(EXDEV, errno); 2842 2843 reparent_exdev_layers_enforce2(_metadata); 2844 2845 /* Checks EACCES predominance over EXDEV. */ 2846 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2847 ASSERT_EQ(EACCES, errno); 2848 /* Checks with actual file2_s1d2. */ 2849 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 2850 ASSERT_EQ(EACCES, errno); 2851 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2852 ASSERT_EQ(EXDEV, errno); 2853 /* 2854 * Modifying the layout is now denied because the second layer does not 2855 * handle REFER, which is always denied by default. 2856 */ 2857 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2858 ASSERT_EQ(EXDEV, errno); 2859 2860 /* Without REFER source, EACCES wins over EXDEV. */ 2861 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2862 ASSERT_EQ(EACCES, errno); 2863 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2864 ASSERT_EQ(EACCES, errno); 2865 } 2866 2867 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 2868 { 2869 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 2870 file2_s2d3; 2871 2872 ASSERT_EQ(0, unlink(file1_s1d2)); 2873 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2874 ASSERT_EQ(0, unlink(file2_s2d3)); 2875 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2876 2877 reparent_exdev_layers_enforce1(_metadata); 2878 2879 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2880 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2881 RENAME_EXCHANGE)); 2882 ASSERT_EQ(EACCES, errno); 2883 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2884 RENAME_EXCHANGE)); 2885 ASSERT_EQ(EACCES, errno); 2886 2887 /* 2888 * Checks with directories which creation could be allowed, but denied 2889 * because of access rights that would be inherited. 2890 */ 2891 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2892 dir_file2_s2d3, RENAME_EXCHANGE)); 2893 ASSERT_EQ(EXDEV, errno); 2894 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2895 dir_file1_s1d2, RENAME_EXCHANGE)); 2896 ASSERT_EQ(EXDEV, errno); 2897 2898 /* Checks with same access rights. */ 2899 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2900 RENAME_EXCHANGE)); 2901 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2902 RENAME_EXCHANGE)); 2903 2904 /* Checks with different (child-only) access rights. */ 2905 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2906 RENAME_EXCHANGE)); 2907 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2908 RENAME_EXCHANGE)); 2909 2910 /* 2911 * Checks that exchange between file and directory are consistent. 2912 * 2913 * Moving a file (file1_s2d2) to a directory which only grants more 2914 * directory-related access rights is allowed, and at the same time 2915 * moving a directory (dir_file2_s2d3) to another directory which 2916 * grants less access rights is allowed too. 2917 * 2918 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 2919 */ 2920 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2921 RENAME_EXCHANGE)); 2922 /* 2923 * However, moving back the directory is denied because it would get 2924 * more access rights than the current state and because file creation 2925 * is forbidden (in dir_s2d2). 2926 */ 2927 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2928 RENAME_EXCHANGE)); 2929 ASSERT_EQ(EACCES, errno); 2930 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2931 RENAME_EXCHANGE)); 2932 ASSERT_EQ(EACCES, errno); 2933 2934 reparent_exdev_layers_enforce2(_metadata); 2935 2936 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2937 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2938 RENAME_EXCHANGE)); 2939 ASSERT_EQ(EACCES, errno); 2940 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2941 RENAME_EXCHANGE)); 2942 ASSERT_EQ(EACCES, errno); 2943 2944 /* Checks with directories which creation is now denied. */ 2945 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2946 dir_file2_s2d3, RENAME_EXCHANGE)); 2947 ASSERT_EQ(EACCES, errno); 2948 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2949 dir_file1_s1d2, RENAME_EXCHANGE)); 2950 ASSERT_EQ(EACCES, errno); 2951 2952 /* Checks with different (child-only) access rights. */ 2953 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2954 RENAME_EXCHANGE)); 2955 /* Denied because of MAKE_DIR. */ 2956 ASSERT_EQ(EACCES, errno); 2957 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2958 RENAME_EXCHANGE)); 2959 ASSERT_EQ(EACCES, errno); 2960 2961 /* Checks with different (child-only) access rights. */ 2962 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2963 RENAME_EXCHANGE)); 2964 /* Denied because of MAKE_DIR. */ 2965 ASSERT_EQ(EACCES, errno); 2966 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2967 RENAME_EXCHANGE)); 2968 ASSERT_EQ(EACCES, errno); 2969 2970 /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 2971 } 2972 2973 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 2974 { 2975 const char *const dir_file2_s2d3 = file2_s2d3; 2976 2977 ASSERT_EQ(0, unlink(file2_s2d3)); 2978 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2979 2980 reparent_exdev_layers_enforce1(_metadata); 2981 reparent_exdev_layers_enforce2(_metadata); 2982 2983 /* Checks that exchange between file and directory are consistent. */ 2984 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2985 RENAME_EXCHANGE)); 2986 ASSERT_EQ(EACCES, errno); 2987 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2988 RENAME_EXCHANGE)); 2989 ASSERT_EQ(EACCES, errno); 2990 } 2991 2992 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 2993 { 2994 const char *const dir_file2_s2d3 = file2_s2d3; 2995 2996 ASSERT_EQ(0, unlink(file2_s2d3)); 2997 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2998 2999 reparent_exdev_layers_enforce1(_metadata); 3000 3001 /* 3002 * Checks that exchange between file and directory are consistent, 3003 * including with inverted arguments (see 3004 * layout1.reparent_exdev_layers_exchange1). 3005 */ 3006 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3007 RENAME_EXCHANGE)); 3008 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 3009 RENAME_EXCHANGE)); 3010 ASSERT_EQ(EACCES, errno); 3011 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3012 RENAME_EXCHANGE)); 3013 ASSERT_EQ(EACCES, errno); 3014 } 3015 3016 TEST_F_FORK(layout1, reparent_remove) 3017 { 3018 const struct rule layer1[] = { 3019 { 3020 .path = dir_s1d1, 3021 .access = LANDLOCK_ACCESS_FS_REFER | 3022 LANDLOCK_ACCESS_FS_REMOVE_DIR, 3023 }, 3024 { 3025 .path = dir_s1d2, 3026 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 3027 }, 3028 { 3029 .path = dir_s2d1, 3030 .access = LANDLOCK_ACCESS_FS_REFER | 3031 LANDLOCK_ACCESS_FS_REMOVE_FILE, 3032 }, 3033 {}, 3034 }; 3035 3036 enforce_fs(_metadata, 3037 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 3038 LANDLOCK_ACCESS_FS_REMOVE_FILE, 3039 layer1); 3040 3041 /* Access denied because of wrong/swapped remove file/dir. */ 3042 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 3043 ASSERT_EQ(EACCES, errno); 3044 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 3045 ASSERT_EQ(EACCES, errno); 3046 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 3047 RENAME_EXCHANGE)); 3048 ASSERT_EQ(EACCES, errno); 3049 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 3050 RENAME_EXCHANGE)); 3051 ASSERT_EQ(EACCES, errno); 3052 3053 /* Access allowed thanks to the matching rights. */ 3054 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 3055 ASSERT_EQ(EISDIR, errno); 3056 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 3057 ASSERT_EQ(ENOTDIR, errno); 3058 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 3059 ASSERT_EQ(ENOTDIR, errno); 3060 ASSERT_EQ(0, unlink(file1_s2d1)); 3061 ASSERT_EQ(0, unlink(file1_s1d3)); 3062 ASSERT_EQ(0, unlink(file2_s1d3)); 3063 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 3064 3065 /* Effectively removes a file and a directory by exchanging them. */ 3066 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 3067 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 3068 RENAME_EXCHANGE)); 3069 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 3070 RENAME_EXCHANGE)); 3071 ASSERT_EQ(EACCES, errno); 3072 } 3073 3074 TEST_F_FORK(layout1, reparent_dom_superset) 3075 { 3076 const struct rule layer1[] = { 3077 { 3078 .path = dir_s1d2, 3079 .access = LANDLOCK_ACCESS_FS_REFER, 3080 }, 3081 { 3082 .path = file1_s1d2, 3083 .access = LANDLOCK_ACCESS_FS_EXECUTE, 3084 }, 3085 { 3086 .path = dir_s1d3, 3087 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 3088 LANDLOCK_ACCESS_FS_EXECUTE, 3089 }, 3090 { 3091 .path = dir_s2d2, 3092 .access = LANDLOCK_ACCESS_FS_REFER | 3093 LANDLOCK_ACCESS_FS_EXECUTE | 3094 LANDLOCK_ACCESS_FS_MAKE_SOCK, 3095 }, 3096 { 3097 .path = dir_s2d3, 3098 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3099 LANDLOCK_ACCESS_FS_MAKE_FIFO, 3100 }, 3101 {}, 3102 }; 3103 3104 enforce_fs(_metadata, 3105 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE | 3106 LANDLOCK_ACCESS_FS_MAKE_SOCK | 3107 LANDLOCK_ACCESS_FS_READ_FILE | 3108 LANDLOCK_ACCESS_FS_MAKE_FIFO, 3109 layer1); 3110 3111 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 3112 ASSERT_EQ(EXDEV, errno); 3113 /* 3114 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 3115 * access right. 3116 */ 3117 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 3118 ASSERT_EQ(EXDEV, errno); 3119 /* 3120 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 3121 * superset of access rights compared to dir_s1d2, because file1_s1d2 3122 * already has these access rights anyway. 3123 */ 3124 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 3125 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 3126 3127 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 3128 ASSERT_EQ(EXDEV, errno); 3129 /* 3130 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 3131 * right. 3132 */ 3133 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 3134 ASSERT_EQ(EXDEV, errno); 3135 /* 3136 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 3137 * of access rights compared to dir_s1d2, because dir_s1d3 already has 3138 * these access rights anyway. 3139 */ 3140 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 3141 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 3142 3143 /* 3144 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 3145 * will be denied because the new inherited access rights from dir_s1d2 3146 * will be less than the destination (original) dir_s2d3. This is a 3147 * sinkhole scenario where we cannot move back files or directories. 3148 */ 3149 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 3150 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 3151 ASSERT_EQ(EXDEV, errno); 3152 ASSERT_EQ(0, unlink(file2_s1d2)); 3153 ASSERT_EQ(0, unlink(file2_s2d3)); 3154 /* 3155 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 3156 * MAKE_SOCK which were inherited from dir_s1d3. 3157 */ 3158 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 3159 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 3160 ASSERT_EQ(EXDEV, errno); 3161 } 3162 3163 TEST_F_FORK(layout1, remove_dir) 3164 { 3165 const struct rule rules[] = { 3166 { 3167 .path = dir_s1d2, 3168 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 3169 }, 3170 {}, 3171 }; 3172 3173 ASSERT_EQ(0, unlink(file1_s1d1)); 3174 ASSERT_EQ(0, unlink(file1_s1d2)); 3175 ASSERT_EQ(0, unlink(file1_s1d3)); 3176 ASSERT_EQ(0, unlink(file2_s1d3)); 3177 3178 enforce_fs(_metadata, rules[0].access, rules); 3179 3180 ASSERT_EQ(0, rmdir(dir_s1d3)); 3181 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 3182 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 3183 3184 /* dir_s1d2 itself cannot be removed. */ 3185 ASSERT_EQ(-1, rmdir(dir_s1d2)); 3186 ASSERT_EQ(EACCES, errno); 3187 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 3188 ASSERT_EQ(EACCES, errno); 3189 ASSERT_EQ(-1, rmdir(dir_s1d1)); 3190 ASSERT_EQ(EACCES, errno); 3191 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 3192 ASSERT_EQ(EACCES, errno); 3193 } 3194 3195 TEST_F_FORK(layout1, remove_file) 3196 { 3197 const struct rule rules[] = { 3198 { 3199 .path = dir_s1d2, 3200 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 3201 }, 3202 {}, 3203 }; 3204 3205 enforce_fs(_metadata, rules[0].access, rules); 3206 3207 ASSERT_EQ(-1, unlink(file1_s1d1)); 3208 ASSERT_EQ(EACCES, errno); 3209 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 3210 ASSERT_EQ(EACCES, errno); 3211 ASSERT_EQ(0, unlink(file1_s1d2)); 3212 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 3213 } 3214 3215 static void test_make_file(struct __test_metadata *const _metadata, 3216 const __u64 access, const mode_t mode, 3217 const dev_t dev) 3218 { 3219 const struct rule rules[] = { 3220 { 3221 .path = dir_s1d2, 3222 .access = access, 3223 }, 3224 {}, 3225 }; 3226 3227 ASSERT_EQ(0, unlink(file1_s1d1)); 3228 ASSERT_EQ(0, unlink(file2_s1d1)); 3229 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 3230 { 3231 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 3232 strerror(errno)); 3233 }; 3234 3235 ASSERT_EQ(0, unlink(file1_s1d2)); 3236 ASSERT_EQ(0, unlink(file2_s1d2)); 3237 3238 ASSERT_EQ(0, unlink(file1_s1d3)); 3239 ASSERT_EQ(0, unlink(file2_s1d3)); 3240 3241 enforce_fs(_metadata, access, rules); 3242 3243 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 3244 ASSERT_EQ(EACCES, errno); 3245 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3246 ASSERT_EQ(EACCES, errno); 3247 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3248 ASSERT_EQ(EACCES, errno); 3249 3250 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 3251 { 3252 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 3253 strerror(errno)); 3254 }; 3255 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3256 ASSERT_EQ(0, unlink(file2_s1d2)); 3257 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3258 3259 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 3260 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3261 ASSERT_EQ(0, unlink(file2_s1d3)); 3262 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3263 } 3264 3265 TEST_F_FORK(layout1, make_char) 3266 { 3267 /* Creates a /dev/null device. */ 3268 set_cap(_metadata, CAP_MKNOD); 3269 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 3270 makedev(1, 3)); 3271 } 3272 3273 TEST_F_FORK(layout1, make_block) 3274 { 3275 /* Creates a /dev/loop0 device. */ 3276 set_cap(_metadata, CAP_MKNOD); 3277 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 3278 makedev(7, 0)); 3279 } 3280 3281 TEST_F_FORK(layout1, make_reg_1) 3282 { 3283 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 3284 } 3285 3286 TEST_F_FORK(layout1, make_reg_2) 3287 { 3288 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 3289 } 3290 3291 TEST_F_FORK(layout1, make_sock) 3292 { 3293 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 3294 } 3295 3296 TEST_F_FORK(layout1, make_fifo) 3297 { 3298 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 3299 } 3300 3301 TEST_F_FORK(layout1, make_sym) 3302 { 3303 const struct rule rules[] = { 3304 { 3305 .path = dir_s1d2, 3306 .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 3307 }, 3308 {}, 3309 }; 3310 3311 ASSERT_EQ(0, unlink(file1_s1d1)); 3312 ASSERT_EQ(0, unlink(file2_s1d1)); 3313 ASSERT_EQ(0, symlink("none", file2_s1d1)); 3314 3315 ASSERT_EQ(0, unlink(file1_s1d2)); 3316 ASSERT_EQ(0, unlink(file2_s1d2)); 3317 3318 ASSERT_EQ(0, unlink(file1_s1d3)); 3319 ASSERT_EQ(0, unlink(file2_s1d3)); 3320 3321 enforce_fs(_metadata, rules[0].access, rules); 3322 3323 ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3324 ASSERT_EQ(EACCES, errno); 3325 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3326 ASSERT_EQ(EACCES, errno); 3327 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3328 ASSERT_EQ(EACCES, errno); 3329 3330 ASSERT_EQ(0, symlink("none", file1_s1d2)); 3331 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3332 ASSERT_EQ(0, unlink(file2_s1d2)); 3333 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3334 3335 ASSERT_EQ(0, symlink("none", file1_s1d3)); 3336 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3337 ASSERT_EQ(0, unlink(file2_s1d3)); 3338 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3339 } 3340 3341 TEST_F_FORK(layout1, make_dir) 3342 { 3343 const struct rule rules[] = { 3344 { 3345 .path = dir_s1d2, 3346 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3347 }, 3348 {}, 3349 }; 3350 3351 ASSERT_EQ(0, unlink(file1_s1d1)); 3352 ASSERT_EQ(0, unlink(file1_s1d2)); 3353 ASSERT_EQ(0, unlink(file1_s1d3)); 3354 3355 enforce_fs(_metadata, rules[0].access, rules); 3356 3357 /* Uses file_* as directory names. */ 3358 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3359 ASSERT_EQ(EACCES, errno); 3360 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3361 ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3362 } 3363 3364 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3365 const int open_flags) 3366 { 3367 static const char path_template[] = "/proc/self/fd/%d"; 3368 char procfd_path[sizeof(path_template) + 10]; 3369 const int procfd_path_size = 3370 snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3371 3372 ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3373 return open(procfd_path, open_flags); 3374 } 3375 3376 TEST_F_FORK(layout1, proc_unlinked_file) 3377 { 3378 const struct rule rules[] = { 3379 { 3380 .path = file1_s1d2, 3381 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3382 }, 3383 {}, 3384 }; 3385 int reg_fd, proc_fd; 3386 3387 enforce_fs(_metadata, 3388 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3389 rules); 3390 3391 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3392 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3393 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3394 ASSERT_LE(0, reg_fd); 3395 ASSERT_EQ(0, unlink(file1_s1d2)); 3396 3397 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3398 ASSERT_LE(0, proc_fd); 3399 ASSERT_EQ(0, close(proc_fd)); 3400 3401 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3402 ASSERT_EQ(-1, proc_fd) 3403 { 3404 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3405 strerror(errno)); 3406 } 3407 ASSERT_EQ(EACCES, errno); 3408 3409 ASSERT_EQ(0, close(reg_fd)); 3410 } 3411 3412 TEST_F_FORK(layout1, proc_pipe) 3413 { 3414 int proc_fd; 3415 int pipe_fds[2]; 3416 char buf = '\0'; 3417 const struct rule rules[] = { 3418 { 3419 .path = dir_s1d2, 3420 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3421 LANDLOCK_ACCESS_FS_WRITE_FILE, 3422 }, 3423 {}, 3424 }; 3425 3426 /* Limits read and write access to files tied to the filesystem. */ 3427 enforce_fs(_metadata, rules[0].access, rules); 3428 3429 /* Checks enforcement for normal files. */ 3430 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3431 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3432 3433 /* Checks access to pipes through FD. */ 3434 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3435 ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3436 { 3437 TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3438 } 3439 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3440 ASSERT_EQ('.', buf); 3441 3442 /* Checks write access to pipe through /proc/self/fd . */ 3443 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3444 ASSERT_LE(0, proc_fd); 3445 ASSERT_EQ(1, write(proc_fd, ".", 1)) 3446 { 3447 TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3448 pipe_fds[1], strerror(errno)); 3449 } 3450 ASSERT_EQ(0, close(proc_fd)); 3451 3452 /* Checks read access to pipe through /proc/self/fd . */ 3453 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3454 ASSERT_LE(0, proc_fd); 3455 buf = '\0'; 3456 ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3457 { 3458 TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3459 pipe_fds[1], strerror(errno)); 3460 } 3461 ASSERT_EQ(0, close(proc_fd)); 3462 3463 ASSERT_EQ(0, close(pipe_fds[0])); 3464 ASSERT_EQ(0, close(pipe_fds[1])); 3465 } 3466 3467 /* Invokes truncate(2) and returns its errno or 0. */ 3468 static int test_truncate(const char *const path) 3469 { 3470 if (truncate(path, 10) < 0) 3471 return errno; 3472 return 0; 3473 } 3474 3475 /* 3476 * Invokes creat(2) and returns its errno or 0. 3477 * Closes the opened file descriptor on success. 3478 */ 3479 static int test_creat(const char *const path) 3480 { 3481 int fd = creat(path, 0600); 3482 3483 if (fd < 0) 3484 return errno; 3485 3486 /* 3487 * Mixing error codes from close(2) and creat(2) should not lead to any 3488 * (access type) confusion for this test. 3489 */ 3490 if (close(fd) < 0) 3491 return errno; 3492 return 0; 3493 } 3494 3495 /* 3496 * Exercises file truncation when it's not restricted, 3497 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 3498 */ 3499 TEST_F_FORK(layout1, truncate_unhandled) 3500 { 3501 const char *const file_r = file1_s1d1; 3502 const char *const file_w = file2_s1d1; 3503 const char *const file_none = file1_s1d2; 3504 const struct rule rules[] = { 3505 { 3506 .path = file_r, 3507 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3508 }, 3509 { 3510 .path = file_w, 3511 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3512 }, 3513 /* Implicitly: No rights for file_none. */ 3514 {}, 3515 }; 3516 3517 /* Enables Landlock. */ 3518 enforce_fs(_metadata, 3519 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3520 rules); 3521 3522 /* 3523 * Checks read right: truncate and open with O_TRUNC work, unless the 3524 * file is attempted to be opened for writing. 3525 */ 3526 EXPECT_EQ(0, test_truncate(file_r)); 3527 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 3528 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 3529 EXPECT_EQ(EACCES, test_creat(file_r)); 3530 3531 /* 3532 * Checks write right: truncate and open with O_TRUNC work, unless the 3533 * file is attempted to be opened for reading. 3534 */ 3535 EXPECT_EQ(0, test_truncate(file_w)); 3536 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 3537 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 3538 EXPECT_EQ(0, test_creat(file_w)); 3539 3540 /* 3541 * Checks "no rights" case: truncate works but all open attempts fail, 3542 * including creat. 3543 */ 3544 EXPECT_EQ(0, test_truncate(file_none)); 3545 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3546 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3547 EXPECT_EQ(EACCES, test_creat(file_none)); 3548 } 3549 3550 TEST_F_FORK(layout1, truncate) 3551 { 3552 const char *const file_rwt = file1_s1d1; 3553 const char *const file_rw = file2_s1d1; 3554 const char *const file_rt = file1_s1d2; 3555 const char *const file_t = file2_s1d2; 3556 const char *const file_none = file1_s1d3; 3557 const char *const dir_t = dir_s2d1; 3558 const char *const file_in_dir_t = file1_s2d1; 3559 const char *const dir_w = dir_s3d1; 3560 const char *const file_in_dir_w = file1_s3d1; 3561 const struct rule rules[] = { 3562 { 3563 .path = file_rwt, 3564 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3565 LANDLOCK_ACCESS_FS_WRITE_FILE | 3566 LANDLOCK_ACCESS_FS_TRUNCATE, 3567 }, 3568 { 3569 .path = file_rw, 3570 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3571 LANDLOCK_ACCESS_FS_WRITE_FILE, 3572 }, 3573 { 3574 .path = file_rt, 3575 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3576 LANDLOCK_ACCESS_FS_TRUNCATE, 3577 }, 3578 { 3579 .path = file_t, 3580 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3581 }, 3582 /* Implicitly: No access rights for file_none. */ 3583 { 3584 .path = dir_t, 3585 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3586 }, 3587 { 3588 .path = dir_w, 3589 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3590 }, 3591 {}, 3592 }; 3593 3594 /* Enables Landlock. */ 3595 enforce_fs(_metadata, 3596 LANDLOCK_ACCESS_FS_READ_FILE | 3597 LANDLOCK_ACCESS_FS_WRITE_FILE | 3598 LANDLOCK_ACCESS_FS_TRUNCATE, 3599 rules); 3600 3601 /* Checks read, write and truncate rights: truncation works. */ 3602 EXPECT_EQ(0, test_truncate(file_rwt)); 3603 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 3604 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 3605 3606 /* Checks read and write rights: no truncate variant works. */ 3607 EXPECT_EQ(EACCES, test_truncate(file_rw)); 3608 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 3609 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 3610 3611 /* 3612 * Checks read and truncate rights: truncation works. 3613 * 3614 * Note: Files can get truncated using open() even with O_RDONLY. 3615 */ 3616 EXPECT_EQ(0, test_truncate(file_rt)); 3617 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 3618 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 3619 3620 /* Checks truncate right: truncate works, but can't open file. */ 3621 EXPECT_EQ(0, test_truncate(file_t)); 3622 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 3623 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 3624 3625 /* Checks "no rights" case: No form of truncation works. */ 3626 EXPECT_EQ(EACCES, test_truncate(file_none)); 3627 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3628 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3629 3630 /* 3631 * Checks truncate right on directory: truncate works on contained 3632 * files. 3633 */ 3634 EXPECT_EQ(0, test_truncate(file_in_dir_t)); 3635 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 3636 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 3637 3638 /* 3639 * Checks creat in dir_w: This requires the truncate right when 3640 * overwriting an existing file, but does not require it when the file 3641 * is new. 3642 */ 3643 EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 3644 3645 ASSERT_EQ(0, unlink(file_in_dir_w)); 3646 EXPECT_EQ(0, test_creat(file_in_dir_w)); 3647 } 3648 3649 /* Invokes ftruncate(2) and returns its errno or 0. */ 3650 static int test_ftruncate(int fd) 3651 { 3652 if (ftruncate(fd, 10) < 0) 3653 return errno; 3654 return 0; 3655 } 3656 3657 TEST_F_FORK(layout1, ftruncate) 3658 { 3659 /* 3660 * This test opens a new file descriptor at different stages of 3661 * Landlock restriction: 3662 * 3663 * without restriction: ftruncate works 3664 * something else but truncate restricted: ftruncate works 3665 * truncate restricted and permitted: ftruncate works 3666 * truncate restricted and not permitted: ftruncate fails 3667 * 3668 * Whether this works or not is expected to depend on the time when the 3669 * FD was opened, not to depend on the time when ftruncate() was 3670 * called. 3671 */ 3672 const char *const path = file1_s1d1; 3673 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 3674 LANDLOCK_ACCESS_FS_WRITE_FILE; 3675 const struct rule layer1[] = { 3676 { 3677 .path = path, 3678 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3679 }, 3680 {}, 3681 }; 3682 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 3683 const struct rule layer2[] = { 3684 { 3685 .path = path, 3686 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3687 }, 3688 {}, 3689 }; 3690 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 3691 LANDLOCK_ACCESS_FS_WRITE_FILE; 3692 const struct rule layer3[] = { 3693 { 3694 .path = path, 3695 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3696 }, 3697 {}, 3698 }; 3699 int fd_layer0, fd_layer1, fd_layer2, fd_layer3; 3700 3701 fd_layer0 = open(path, O_WRONLY); 3702 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3703 3704 enforce_fs(_metadata, handled1, layer1); 3705 3706 fd_layer1 = open(path, O_WRONLY); 3707 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3708 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3709 3710 enforce_fs(_metadata, handled2, layer2); 3711 3712 fd_layer2 = open(path, O_WRONLY); 3713 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3714 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3715 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3716 3717 enforce_fs(_metadata, handled3, layer3); 3718 3719 fd_layer3 = open(path, O_WRONLY); 3720 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3721 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3722 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3723 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 3724 3725 ASSERT_EQ(0, close(fd_layer0)); 3726 ASSERT_EQ(0, close(fd_layer1)); 3727 ASSERT_EQ(0, close(fd_layer2)); 3728 ASSERT_EQ(0, close(fd_layer3)); 3729 } 3730 3731 /* clang-format off */ 3732 FIXTURE(ftruncate) {}; 3733 /* clang-format on */ 3734 3735 FIXTURE_SETUP(ftruncate) 3736 { 3737 prepare_layout(_metadata); 3738 create_file(_metadata, file1_s1d1); 3739 } 3740 3741 FIXTURE_TEARDOWN_PARENT(ftruncate) 3742 { 3743 EXPECT_EQ(0, remove_path(file1_s1d1)); 3744 cleanup_layout(_metadata); 3745 } 3746 3747 FIXTURE_VARIANT(ftruncate) 3748 { 3749 const __u64 handled; 3750 const __u64 allowed; 3751 const int expected_open_result; 3752 const int expected_ftruncate_result; 3753 }; 3754 3755 /* clang-format off */ 3756 FIXTURE_VARIANT_ADD(ftruncate, w_w) { 3757 /* clang-format on */ 3758 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 3759 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE, 3760 .expected_open_result = 0, 3761 .expected_ftruncate_result = 0, 3762 }; 3763 3764 /* clang-format off */ 3765 FIXTURE_VARIANT_ADD(ftruncate, t_t) { 3766 /* clang-format on */ 3767 .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 3768 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE, 3769 .expected_open_result = 0, 3770 .expected_ftruncate_result = 0, 3771 }; 3772 3773 /* clang-format off */ 3774 FIXTURE_VARIANT_ADD(ftruncate, wt_w) { 3775 /* clang-format on */ 3776 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3777 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE, 3778 .expected_open_result = 0, 3779 .expected_ftruncate_result = EACCES, 3780 }; 3781 3782 /* clang-format off */ 3783 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 3784 /* clang-format on */ 3785 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3786 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3787 .expected_open_result = 0, 3788 .expected_ftruncate_result = 0, 3789 }; 3790 3791 /* clang-format off */ 3792 FIXTURE_VARIANT_ADD(ftruncate, wt_t) { 3793 /* clang-format on */ 3794 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 3795 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE, 3796 .expected_open_result = EACCES, 3797 }; 3798 3799 TEST_F_FORK(ftruncate, open_and_ftruncate) 3800 { 3801 const char *const path = file1_s1d1; 3802 const struct rule rules[] = { 3803 { 3804 .path = path, 3805 .access = variant->allowed, 3806 }, 3807 {}, 3808 }; 3809 int fd; 3810 3811 /* Enables Landlock. */ 3812 enforce_fs(_metadata, variant->handled, rules); 3813 3814 fd = open(path, O_WRONLY); 3815 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3816 if (fd >= 0) { 3817 EXPECT_EQ(variant->expected_ftruncate_result, 3818 test_ftruncate(fd)); 3819 ASSERT_EQ(0, close(fd)); 3820 } 3821 } 3822 3823 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 3824 { 3825 int child, fd, status; 3826 int socket_fds[2]; 3827 3828 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 3829 socket_fds)); 3830 3831 child = fork(); 3832 ASSERT_LE(0, child); 3833 if (child == 0) { 3834 /* 3835 * Enables Landlock in the child process, open a file descriptor 3836 * where truncation is forbidden and send it to the 3837 * non-landlocked parent process. 3838 */ 3839 const char *const path = file1_s1d1; 3840 const struct rule rules[] = { 3841 { 3842 .path = path, 3843 .access = variant->allowed, 3844 }, 3845 {}, 3846 }; 3847 int fd; 3848 3849 enforce_fs(_metadata, variant->handled, rules); 3850 3851 fd = open(path, O_WRONLY); 3852 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3853 3854 if (fd >= 0) { 3855 ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 3856 ASSERT_EQ(0, close(fd)); 3857 } 3858 3859 ASSERT_EQ(0, close(socket_fds[0])); 3860 3861 _exit(_metadata->exit_code); 3862 return; 3863 } 3864 3865 if (variant->expected_open_result == 0) { 3866 fd = recv_fd(socket_fds[1]); 3867 ASSERT_LE(0, fd); 3868 3869 EXPECT_EQ(variant->expected_ftruncate_result, 3870 test_ftruncate(fd)); 3871 ASSERT_EQ(0, close(fd)); 3872 } 3873 3874 ASSERT_EQ(child, waitpid(child, &status, 0)); 3875 ASSERT_EQ(1, WIFEXITED(status)); 3876 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 3877 3878 ASSERT_EQ(0, close(socket_fds[0])); 3879 ASSERT_EQ(0, close(socket_fds[1])); 3880 } 3881 3882 /* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */ 3883 static int test_fs_ioc_getflags_ioctl(int fd) 3884 { 3885 uint32_t flags; 3886 3887 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) 3888 return errno; 3889 return 0; 3890 } 3891 3892 TEST(memfd_ftruncate_and_ioctl) 3893 { 3894 int fd, i; 3895 3896 /* 3897 * We exercise the same test both with and without Landlock enabled, to 3898 * ensure that it behaves the same in both cases. 3899 */ 3900 for (i = 0; i < 2; i++) { 3901 /* Creates a new memfd. */ 3902 fd = memfd_create("name", MFD_CLOEXEC); 3903 ASSERT_LE(0, fd); 3904 3905 /* 3906 * Checks that operations associated with the opened file 3907 * (ftruncate, ioctl) are permitted on file descriptors that are 3908 * created in ways other than open(2). 3909 */ 3910 EXPECT_EQ(0, test_ftruncate(fd)); 3911 EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd)); 3912 3913 ASSERT_EQ(0, close(fd)); 3914 3915 /* Enables Landlock. */ 3916 enforce_fs(_metadata, ACCESS_ALL, NULL); 3917 } 3918 } 3919 3920 static int test_fionread_ioctl(int fd) 3921 { 3922 size_t sz = 0; 3923 3924 if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES) 3925 return errno; 3926 return 0; 3927 } 3928 3929 TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl) 3930 { 3931 int fd; 3932 3933 /* 3934 * Checks that for files opened with O_PATH, both ioctl(2) and 3935 * ftruncate(2) yield EBADF, as it is documented in open(2) for the 3936 * O_PATH flag. 3937 */ 3938 fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 3939 ASSERT_LE(0, fd); 3940 3941 EXPECT_EQ(EBADF, test_ftruncate(fd)); 3942 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 3943 3944 ASSERT_EQ(0, close(fd)); 3945 3946 /* Enables Landlock. */ 3947 enforce_fs(_metadata, ACCESS_ALL, NULL); 3948 3949 /* 3950 * Checks that after enabling Landlock, 3951 * - the file can still be opened with O_PATH 3952 * - both ioctl and truncate still yield EBADF (not EACCES). 3953 */ 3954 fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 3955 ASSERT_LE(0, fd); 3956 3957 EXPECT_EQ(EBADF, test_ftruncate(fd)); 3958 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 3959 3960 ASSERT_EQ(0, close(fd)); 3961 } 3962 3963 /* 3964 * ioctl_error - generically call the given ioctl with a pointer to a 3965 * sufficiently large zeroed-out memory region. 3966 * 3967 * Returns the IOCTLs error, or 0. 3968 */ 3969 static int ioctl_error(struct __test_metadata *const _metadata, int fd, 3970 unsigned int cmd) 3971 { 3972 char buf[128]; /* sufficiently large */ 3973 int res, stdinbak_fd, err; 3974 3975 /* 3976 * Depending on the IOCTL command, parts of the zeroed-out buffer might 3977 * be interpreted as file descriptor numbers. We do not want to 3978 * accidentally operate on file descriptor 0 (stdin), so we temporarily 3979 * move stdin to a different FD and close FD 0 for the IOCTL call. 3980 */ 3981 stdinbak_fd = dup(0); 3982 ASSERT_LT(0, stdinbak_fd); 3983 ASSERT_EQ(0, close(0)); 3984 3985 /* Invokes the IOCTL with a zeroed-out buffer. */ 3986 bzero(&buf, sizeof(buf)); 3987 res = ioctl(fd, cmd, &buf); 3988 err = errno; 3989 3990 /* Restores the old FD 0 and closes the backup FD. */ 3991 ASSERT_EQ(0, dup2(stdinbak_fd, 0)); 3992 ASSERT_EQ(0, close(stdinbak_fd)); 3993 3994 if (res < 0) 3995 return err; 3996 3997 return 0; 3998 } 3999 4000 /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */ 4001 struct space_resv { 4002 __s16 l_type; 4003 __s16 l_whence; 4004 __s64 l_start; 4005 __s64 l_len; /* len == 0 means until end of file */ 4006 __s32 l_sysid; 4007 __u32 l_pid; 4008 __s32 l_pad[4]; /* reserved area */ 4009 }; 4010 4011 #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) 4012 #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv) 4013 #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) 4014 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv) 4015 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv) 4016 4017 /* 4018 * Tests a series of blanket-permitted and denied IOCTLs. 4019 */ 4020 TEST_F_FORK(layout1, blanket_permitted_ioctls) 4021 { 4022 int fd; 4023 4024 /* Enables Landlock. */ 4025 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4026 4027 fd = open("/dev/null", O_RDWR | O_CLOEXEC); 4028 ASSERT_LE(0, fd); 4029 4030 /* 4031 * Checks permitted commands. 4032 * These ones may return errors, but should not be blocked by Landlock. 4033 */ 4034 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX)); 4035 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX)); 4036 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO)); 4037 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC)); 4038 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE)); 4039 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE)); 4040 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW)); 4041 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP)); 4042 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ)); 4043 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE)); 4044 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE)); 4045 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE)); 4046 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID)); 4047 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH)); 4048 4049 /* 4050 * Checks blocked commands. 4051 * A call to a blocked IOCTL command always returns EACCES. 4052 */ 4053 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 4054 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS)); 4055 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS)); 4056 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR)); 4057 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR)); 4058 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP)); 4059 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP)); 4060 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64)); 4061 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP)); 4062 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64)); 4063 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE)); 4064 4065 /* Default case is also blocked. */ 4066 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee)); 4067 4068 ASSERT_EQ(0, close(fd)); 4069 } 4070 4071 /* 4072 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right, 4073 * because they are not character or block devices. 4074 */ 4075 TEST_F_FORK(layout1, named_pipe_ioctl) 4076 { 4077 pid_t child_pid; 4078 int fd; 4079 const char *const path = file1_s1d1; 4080 4081 ASSERT_EQ(0, unlink(path)); 4082 ASSERT_EQ(0, mkfifo(path, 0600)); 4083 4084 /* Enables Landlock. */ 4085 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4086 4087 /* The child process opens the pipe for writing. */ 4088 child_pid = fork(); 4089 ASSERT_NE(-1, child_pid); 4090 if (child_pid == 0) { 4091 fd = open(path, O_WRONLY); 4092 close(fd); 4093 exit(0); 4094 } 4095 4096 fd = open(path, O_RDONLY); 4097 ASSERT_LE(0, fd); 4098 4099 /* FIONREAD is implemented by pipefifo_fops. */ 4100 EXPECT_EQ(0, test_fionread_ioctl(fd)); 4101 4102 ASSERT_EQ(0, close(fd)); 4103 ASSERT_EQ(0, unlink(path)); 4104 4105 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0)); 4106 } 4107 4108 /* 4109 * set_up_named_unix_server - Create a pathname unix socket 4110 * 4111 * If the socket type is not SOCK_DGRAM, also invoke listen(2). 4112 * 4113 * Return: The listening FD - it is the caller responsibility to close it. 4114 */ 4115 static int set_up_named_unix_server(struct __test_metadata *const _metadata, 4116 int type, const char *const path) 4117 { 4118 int fd; 4119 struct sockaddr_un addr = { 4120 .sun_family = AF_UNIX, 4121 }; 4122 4123 fd = socket(AF_UNIX, type, 0); 4124 ASSERT_LE(0, fd); 4125 4126 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4127 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4128 4129 ASSERT_EQ(0, bind(fd, (struct sockaddr *)&addr, sizeof(addr))); 4130 4131 if (type != SOCK_DGRAM) 4132 ASSERT_EQ(0, listen(fd, 10 /* qlen */)); 4133 return fd; 4134 } 4135 4136 /* 4137 * test_connect_named_unix - connect to the given named UNIX socket 4138 * 4139 * Return: The errno from connect(), or 0 4140 */ 4141 static int test_connect_named_unix(struct __test_metadata *const _metadata, 4142 int fd, const char *const path) 4143 { 4144 struct sockaddr_un addr = { 4145 .sun_family = AF_UNIX, 4146 }; 4147 4148 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4149 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4150 4151 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) 4152 return errno; 4153 return 0; 4154 } 4155 4156 /* For named UNIX domain sockets, no IOCTL restrictions apply. */ 4157 TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) 4158 { 4159 const char *const path = file1_s1d1; 4160 int srv_fd, cli_fd; 4161 4162 /* Sets up a server */ 4163 ASSERT_EQ(0, unlink(path)); 4164 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path); 4165 4166 /* Enables Landlock. */ 4167 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4168 4169 /* Sets up a client connection to it */ 4170 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4171 ASSERT_LE(0, cli_fd); 4172 4173 ASSERT_EQ(0, test_connect_named_unix(_metadata, cli_fd, path)); 4174 4175 /* FIONREAD and other IOCTLs should not be forbidden. */ 4176 EXPECT_EQ(0, test_fionread_ioctl(cli_fd)); 4177 4178 EXPECT_EQ(0, close(cli_fd)); 4179 EXPECT_EQ(0, close(srv_fd)); 4180 } 4181 4182 /* clang-format off */ 4183 FIXTURE(ioctl) {}; 4184 4185 FIXTURE_SETUP(ioctl) {}; 4186 4187 FIXTURE_TEARDOWN(ioctl) {}; 4188 /* clang-format on */ 4189 4190 FIXTURE_VARIANT(ioctl) 4191 { 4192 const __u64 handled; 4193 const __u64 allowed; 4194 const mode_t open_mode; 4195 /* 4196 * FIONREAD is used as a characteristic device-specific IOCTL command. 4197 * It is implemented in fs/ioctl.c for regular files, 4198 * but we do not blanket-permit it for devices. 4199 */ 4200 const int expected_fionread_result; 4201 }; 4202 4203 /* clang-format off */ 4204 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) { 4205 /* clang-format on */ 4206 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4207 .allowed = 0, 4208 .open_mode = O_RDWR, 4209 .expected_fionread_result = EACCES, 4210 }; 4211 4212 /* clang-format off */ 4213 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) { 4214 /* clang-format on */ 4215 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4216 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4217 .open_mode = O_RDWR, 4218 .expected_fionread_result = 0, 4219 }; 4220 4221 /* clang-format off */ 4222 FIXTURE_VARIANT_ADD(ioctl, unhandled) { 4223 /* clang-format on */ 4224 .handled = LANDLOCK_ACCESS_FS_EXECUTE, 4225 .allowed = LANDLOCK_ACCESS_FS_EXECUTE, 4226 .open_mode = O_RDWR, 4227 .expected_fionread_result = 0, 4228 }; 4229 4230 TEST_F_FORK(ioctl, handle_dir_access_file) 4231 { 4232 const int flag = 0; 4233 const struct rule rules[] = { 4234 { 4235 .path = "/dev", 4236 .access = variant->allowed, 4237 }, 4238 {}, 4239 }; 4240 int fd; 4241 4242 /* Enables Landlock. */ 4243 enforce_fs(_metadata, variant->handled, rules); 4244 4245 fd = open("/dev/zero", variant->open_mode); 4246 ASSERT_LE(0, fd); 4247 4248 /* Checks that IOCTL commands return the expected errors. */ 4249 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd)); 4250 4251 /* Checks that unrestrictable commands are unrestricted. */ 4252 EXPECT_EQ(0, ioctl(fd, FIOCLEX)); 4253 EXPECT_EQ(0, ioctl(fd, FIONCLEX)); 4254 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag)); 4255 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag)); 4256 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag)); 4257 4258 ASSERT_EQ(0, close(fd)); 4259 } 4260 4261 TEST_F_FORK(ioctl, handle_dir_access_dir) 4262 { 4263 const int flag = 0; 4264 const struct rule rules[] = { 4265 { 4266 .path = "/dev", 4267 .access = variant->allowed, 4268 }, 4269 {}, 4270 }; 4271 int dir_fd; 4272 4273 /* Enables Landlock. */ 4274 enforce_fs(_metadata, variant->handled, rules); 4275 4276 /* 4277 * Ignore variant->open_mode for this test, as we intend to open a 4278 * directory. If the directory can not be opened, the variant is 4279 * infeasible to test with an opened directory. 4280 */ 4281 dir_fd = open("/dev", O_RDONLY); 4282 if (dir_fd < 0) 4283 return; 4284 4285 /* 4286 * Checks that IOCTL commands return the expected errors. 4287 * We do not use the expected values from the fixture here. 4288 * 4289 * When using IOCTL on a directory, no Landlock restrictions apply. 4290 */ 4291 EXPECT_EQ(0, test_fionread_ioctl(dir_fd)); 4292 4293 /* Checks that unrestrictable commands are unrestricted. */ 4294 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX)); 4295 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX)); 4296 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag)); 4297 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag)); 4298 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag)); 4299 4300 ASSERT_EQ(0, close(dir_fd)); 4301 } 4302 4303 TEST_F_FORK(ioctl, handle_file_access_file) 4304 { 4305 const int flag = 0; 4306 const struct rule rules[] = { 4307 { 4308 .path = "/dev/zero", 4309 .access = variant->allowed, 4310 }, 4311 {}, 4312 }; 4313 int fd; 4314 4315 /* Enables Landlock. */ 4316 enforce_fs(_metadata, variant->handled, rules); 4317 4318 fd = open("/dev/zero", variant->open_mode); 4319 ASSERT_LE(0, fd) 4320 { 4321 TH_LOG("Failed to open /dev/zero: %s", strerror(errno)); 4322 } 4323 4324 /* Checks that IOCTL commands return the expected errors. */ 4325 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd)); 4326 4327 /* Checks that unrestrictable commands are unrestricted. */ 4328 EXPECT_EQ(0, ioctl(fd, FIOCLEX)); 4329 EXPECT_EQ(0, ioctl(fd, FIONCLEX)); 4330 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag)); 4331 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag)); 4332 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag)); 4333 4334 ASSERT_EQ(0, close(fd)); 4335 } 4336 4337 /* 4338 * test_sendto_named_unix - sendto to the given named UNIX socket 4339 * 4340 * sendto() is equivalent to sendmsg() in this respect. 4341 * 4342 * Return: The errno from sendto(), or 0 4343 */ 4344 static int test_sendto_named_unix(struct __test_metadata *const _metadata, 4345 int fd, const char *const path) 4346 { 4347 static const char buf[] = "dummy"; 4348 struct sockaddr_un addr = { 4349 .sun_family = AF_UNIX, 4350 }; 4351 4352 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4353 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4354 4355 if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, 4356 sizeof(addr)) == -1) 4357 return errno; 4358 return 0; 4359 } 4360 4361 /* clang-format off */ 4362 FIXTURE(scoped_domains) {}; 4363 /* clang-format on */ 4364 4365 #include "scoped_base_variants.h" 4366 4367 FIXTURE_SETUP(scoped_domains) 4368 { 4369 drop_caps(_metadata); 4370 }; 4371 4372 FIXTURE_TEARDOWN(scoped_domains) 4373 { 4374 } 4375 4376 /* 4377 * Flags for test_connect_to_parent and test_connect_to_child: 4378 * 4379 * USE_SENDTO: Use sendto() instead of connect() (for SOCK_DGRAM only) 4380 * ENFORCE_ALL: Enforce a Landlock domain even when the variant says 4381 * we shouldn't. We enforce a domain where the path is allow-listed, 4382 * and expect the behavior to be the same as if none was used. 4383 */ 4384 #define USE_SENDTO (1 << 0) 4385 #define ENFORCE_ALL (1 << 1) 4386 4387 static void test_connect_to_parent(struct __test_metadata *const _metadata, 4388 const FIXTURE_VARIANT(scoped_domains) * 4389 variant, 4390 int sock_type, int flags) 4391 { 4392 const char *const path = "sock"; 4393 const struct rule rules[] = { 4394 { 4395 .path = ".", 4396 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4397 }, 4398 {}, 4399 }; 4400 int cli_fd, srv_fd, res, status; 4401 pid_t child_pid; 4402 int readiness_pipe[2]; 4403 char buf[1]; 4404 4405 if (variant->domain_both) 4406 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4407 else if (flags & ENFORCE_ALL) 4408 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4409 4410 unlink(path); 4411 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC)); 4412 4413 child_pid = fork(); 4414 ASSERT_LE(0, child_pid); 4415 4416 if (child_pid == 0) { 4417 if (variant->domain_child) 4418 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4419 NULL); 4420 else if (flags & ENFORCE_ALL) 4421 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4422 rules); 4423 4424 /* Wait for server to be available. */ 4425 EXPECT_EQ(0, close(readiness_pipe[1])); 4426 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1)); 4427 EXPECT_EQ(0, close(readiness_pipe[0])); 4428 4429 /* Talk to server. */ 4430 cli_fd = socket(AF_UNIX, sock_type, 0); 4431 ASSERT_LE(0, cli_fd); 4432 4433 if (flags & USE_SENDTO) 4434 res = test_sendto_named_unix(_metadata, cli_fd, path); 4435 else 4436 res = test_connect_named_unix(_metadata, cli_fd, path); 4437 4438 EXPECT_EQ(variant->domain_child ? EACCES : 0, res); 4439 4440 /* Clean up. */ 4441 EXPECT_EQ(0, close(cli_fd)); 4442 4443 _exit(_metadata->exit_code); 4444 return; 4445 } 4446 4447 if (variant->domain_parent) 4448 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4449 else if (flags & ENFORCE_ALL) 4450 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4451 4452 srv_fd = set_up_named_unix_server(_metadata, sock_type, path); 4453 4454 /* Tell the child that it can connect. */ 4455 EXPECT_EQ(0, close(readiness_pipe[0])); 4456 EXPECT_EQ(sizeof(buf), write(readiness_pipe[1], buf, sizeof(buf))); 4457 EXPECT_EQ(0, close(readiness_pipe[1])); 4458 4459 /* Wait for child. */ 4460 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4461 EXPECT_EQ(1, WIFEXITED(status)); 4462 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 4463 4464 /* Clean up. */ 4465 EXPECT_EQ(0, close(srv_fd)); 4466 EXPECT_EQ(0, unlink(path)); 4467 } 4468 4469 static void test_connect_to_child(struct __test_metadata *const _metadata, 4470 const FIXTURE_VARIANT(scoped_domains) * 4471 variant, 4472 int sock_type, int flags) 4473 { 4474 const char *const path = "sock"; 4475 const struct rule rules[] = { 4476 { 4477 .path = ".", 4478 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4479 }, 4480 {}, 4481 }; 4482 int readiness_pipe[2]; 4483 int shutdown_pipe[2]; 4484 int cli_fd, srv_fd, res, status; 4485 pid_t child_pid; 4486 char buf[1]; 4487 4488 if (variant->domain_both) 4489 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4490 else if (flags & ENFORCE_ALL) 4491 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4492 4493 unlink(path); 4494 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC)); 4495 ASSERT_EQ(0, pipe2(shutdown_pipe, O_CLOEXEC)); 4496 4497 child_pid = fork(); 4498 ASSERT_LE(0, child_pid); 4499 4500 if (child_pid == 0) { 4501 if (variant->domain_child) 4502 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4503 NULL); 4504 else if (flags & ENFORCE_ALL) 4505 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4506 rules); 4507 4508 srv_fd = set_up_named_unix_server(_metadata, sock_type, path); 4509 4510 /* Tell the parent that it can connect. */ 4511 EXPECT_EQ(0, close(readiness_pipe[0])); 4512 EXPECT_EQ(sizeof(buf), 4513 write(readiness_pipe[1], buf, sizeof(buf))); 4514 EXPECT_EQ(0, close(readiness_pipe[1])); 4515 4516 /* Wait until it is time to shut down. */ 4517 EXPECT_EQ(0, close(shutdown_pipe[1])); 4518 EXPECT_EQ(1, read(shutdown_pipe[0], &buf, 1)); 4519 EXPECT_EQ(0, close(shutdown_pipe[0])); 4520 4521 /* Cleanup */ 4522 EXPECT_EQ(0, close(srv_fd)); 4523 EXPECT_EQ(0, unlink(path)); 4524 4525 _exit(_metadata->exit_code); 4526 return; 4527 } 4528 4529 if (variant->domain_parent) 4530 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4531 else if (flags & ENFORCE_ALL) 4532 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4533 4534 /* Wait for server to be available. */ 4535 EXPECT_EQ(0, close(readiness_pipe[1])); 4536 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1)); 4537 EXPECT_EQ(0, close(readiness_pipe[0])); 4538 4539 /* Talk to server. */ 4540 cli_fd = socket(AF_UNIX, sock_type, 0); 4541 ASSERT_LE(0, cli_fd); 4542 4543 if (flags & USE_SENDTO) 4544 res = test_sendto_named_unix(_metadata, cli_fd, path); 4545 else 4546 res = test_connect_named_unix(_metadata, cli_fd, path); 4547 4548 EXPECT_EQ(variant->domain_parent ? EACCES : 0, res); 4549 4550 /* Clean up. */ 4551 EXPECT_EQ(0, close(cli_fd)); 4552 4553 /* Tell the server to shut down. */ 4554 EXPECT_EQ(0, close(shutdown_pipe[0])); 4555 EXPECT_EQ(sizeof(buf), write(shutdown_pipe[1], buf, sizeof(buf))); 4556 EXPECT_EQ(0, close(shutdown_pipe[1])); 4557 4558 /* Wait for child. */ 4559 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4560 EXPECT_EQ(1, WIFEXITED(status)); 4561 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 4562 } 4563 4564 TEST_F(scoped_domains, unix_stream_connect_to_parent) 4565 { 4566 test_connect_to_parent(_metadata, variant, SOCK_STREAM, 0); 4567 } 4568 4569 TEST_F(scoped_domains, unix_dgram_connect_to_parent) 4570 { 4571 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, 0); 4572 } 4573 4574 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent) 4575 { 4576 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, USE_SENDTO); 4577 } 4578 4579 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent) 4580 { 4581 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, 0); 4582 } 4583 4584 TEST_F(scoped_domains, unix_stream_connect_to_parent_full) 4585 { 4586 test_connect_to_parent(_metadata, variant, SOCK_STREAM, ENFORCE_ALL); 4587 } 4588 4589 TEST_F(scoped_domains, unix_dgram_connect_to_parent_full) 4590 { 4591 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL); 4592 } 4593 4594 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent_full) 4595 { 4596 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, 4597 USE_SENDTO | ENFORCE_ALL); 4598 } 4599 4600 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent_full) 4601 { 4602 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL); 4603 } 4604 4605 TEST_F(scoped_domains, unix_stream_connect_to_child) 4606 { 4607 test_connect_to_child(_metadata, variant, SOCK_STREAM, 0); 4608 } 4609 4610 TEST_F(scoped_domains, unix_dgram_connect_to_child) 4611 { 4612 test_connect_to_child(_metadata, variant, SOCK_DGRAM, 0); 4613 } 4614 4615 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child) 4616 { 4617 test_connect_to_child(_metadata, variant, SOCK_DGRAM, USE_SENDTO); 4618 } 4619 4620 TEST_F(scoped_domains, unix_seqpacket_connect_to_child) 4621 { 4622 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, 0); 4623 } 4624 4625 TEST_F(scoped_domains, unix_stream_connect_to_child_full) 4626 { 4627 test_connect_to_child(_metadata, variant, SOCK_STREAM, ENFORCE_ALL); 4628 } 4629 4630 TEST_F(scoped_domains, unix_dgram_connect_to_child_full) 4631 { 4632 test_connect_to_child(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL); 4633 } 4634 4635 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child_full) 4636 { 4637 test_connect_to_child(_metadata, variant, SOCK_DGRAM, 4638 USE_SENDTO | ENFORCE_ALL); 4639 } 4640 4641 TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full) 4642 { 4643 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL); 4644 } 4645 4646 #undef USE_SENDTO 4647 #undef ENFORCE_ALL 4648 4649 static void read_core_pattern(struct __test_metadata *const _metadata, 4650 char *buf, size_t buf_size) 4651 { 4652 int fd; 4653 ssize_t ret; 4654 4655 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY | O_CLOEXEC); 4656 ASSERT_LE(0, fd); 4657 4658 ret = read(fd, buf, buf_size - 1); 4659 ASSERT_LE(0, ret); 4660 EXPECT_EQ(0, close(fd)); 4661 4662 buf[ret] = '\0'; 4663 } 4664 4665 static void set_core_pattern(struct __test_metadata *const _metadata, 4666 const char *pattern) 4667 { 4668 int fd; 4669 size_t len = strlen(pattern); 4670 4671 /* 4672 * Writing to /proc/sys/kernel/core_pattern requires EUID 0 because 4673 * sysctl_perm() checks that, ignoring capabilities like 4674 * CAP_SYS_ADMIN or CAP_DAC_OVERRIDE. 4675 * 4676 * Switching EUID clears the dumpable flag, which must be restored 4677 * afterwards to allow coredumps. 4678 */ 4679 set_cap(_metadata, CAP_SETUID); 4680 ASSERT_EQ(0, seteuid(0)); 4681 clear_cap(_metadata, CAP_SETUID); 4682 4683 fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC); 4684 ASSERT_LE(0, fd) 4685 { 4686 TH_LOG("Failed to open core_pattern for writing: %s", 4687 strerror(errno)); 4688 } 4689 4690 ASSERT_EQ(len, write(fd, pattern, len)); 4691 EXPECT_EQ(0, close(fd)); 4692 4693 set_cap(_metadata, CAP_SETUID); 4694 ASSERT_EQ(0, seteuid(getuid())); 4695 clear_cap(_metadata, CAP_SETUID); 4696 4697 /* Restore dumpable flag cleared by seteuid(). */ 4698 ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)); 4699 } 4700 4701 FIXTURE(coredump) 4702 { 4703 char original_core_pattern[256]; 4704 }; 4705 4706 FIXTURE_SETUP(coredump) 4707 { 4708 disable_caps(_metadata); 4709 read_core_pattern(_metadata, self->original_core_pattern, 4710 sizeof(self->original_core_pattern)); 4711 } 4712 4713 FIXTURE_TEARDOWN_PARENT(coredump) 4714 { 4715 set_core_pattern(_metadata, self->original_core_pattern); 4716 } 4717 4718 /* 4719 * Test that even when a process is restricted with 4720 * LANDLOCK_ACCESS_FS_RESOLVE_UNIX, the kernel can still initiate a connection 4721 * to the coredump socket on the processes' behalf. 4722 */ 4723 TEST_F_FORK(coredump, socket_not_restricted) 4724 { 4725 static const char core_pattern[] = "@/tmp/landlock_coredump_test.sock"; 4726 const char *const sock_path = core_pattern + 1; 4727 int srv_fd, conn_fd, status; 4728 pid_t child_pid; 4729 struct ucred cred; 4730 socklen_t cred_len = sizeof(cred); 4731 char buf[4096]; 4732 4733 /* Set up the coredump server socket. */ 4734 unlink(sock_path); 4735 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, sock_path); 4736 4737 /* Point coredumps at our socket. */ 4738 set_core_pattern(_metadata, core_pattern); 4739 4740 /* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */ 4741 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4742 4743 /* Fork a child that crashes. */ 4744 child_pid = fork(); 4745 ASSERT_LE(0, child_pid); 4746 if (child_pid == 0) { 4747 struct rlimit rl = { 4748 .rlim_cur = RLIM_INFINITY, 4749 .rlim_max = RLIM_INFINITY, 4750 }; 4751 4752 ASSERT_EQ(0, setrlimit(RLIMIT_CORE, &rl)); 4753 4754 /* Crash on purpose. */ 4755 kill(getpid(), SIGSEGV); 4756 _exit(1); 4757 } 4758 4759 /* 4760 * Accept the coredump connection. If Landlock incorrectly denies the 4761 * kernel's coredump connect, accept() will block forever, so the test 4762 * would time out. 4763 */ 4764 conn_fd = accept(srv_fd, NULL, NULL); 4765 ASSERT_LE(0, conn_fd); 4766 4767 /* Check that the connection came from the crashing child. */ 4768 ASSERT_EQ(0, getsockopt(conn_fd, SOL_SOCKET, SO_PEERCRED, &cred, 4769 &cred_len)); 4770 EXPECT_EQ(child_pid, cred.pid); 4771 4772 /* Drain the coredump data so the kernel can finish. */ 4773 while (read(conn_fd, buf, sizeof(buf)) > 0) 4774 ; 4775 4776 EXPECT_EQ(0, close(conn_fd)); 4777 4778 /* Wait for the child and verify it coredumped. */ 4779 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4780 ASSERT_TRUE(WIFSIGNALED(status)); 4781 ASSERT_TRUE(WCOREDUMP(status)); 4782 4783 EXPECT_EQ(0, close(srv_fd)); 4784 EXPECT_EQ(0, unlink(sock_path)); 4785 } 4786 4787 /* clang-format off */ 4788 FIXTURE(layout1_bind) {}; 4789 /* clang-format on */ 4790 4791 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 4792 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 4793 static const char bind_file2_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f2"; 4794 4795 /* Move targets for disconnected path tests. */ 4796 static const char dir_s4d1[] = TMP_DIR "/s4d1"; 4797 static const char file1_s4d1[] = TMP_DIR "/s4d1/f1"; 4798 static const char file2_s4d1[] = TMP_DIR "/s4d1/f2"; 4799 static const char dir_s4d2[] = TMP_DIR "/s4d1/s4d2"; 4800 static const char file1_s4d2[] = TMP_DIR "/s4d1/s4d2/f1"; 4801 static const char file1_name[] = "f1"; 4802 static const char file2_name[] = "f2"; 4803 4804 FIXTURE_SETUP(layout1_bind) 4805 { 4806 prepare_layout(_metadata); 4807 4808 create_layout1(_metadata); 4809 4810 set_cap(_metadata, CAP_SYS_ADMIN); 4811 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 4812 clear_cap(_metadata, CAP_SYS_ADMIN); 4813 } 4814 4815 FIXTURE_TEARDOWN_PARENT(layout1_bind) 4816 { 4817 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 4818 4819 remove_path(file1_s4d1); 4820 remove_path(file2_s4d1); 4821 4822 remove_layout1(_metadata); 4823 4824 cleanup_layout(_metadata); 4825 } 4826 4827 /* 4828 * layout1_bind hierarchy: 4829 * 4830 * tmp 4831 * ├── s1d1 4832 * │ ├── f1 4833 * │ ├── f2 4834 * │ └── s1d2 4835 * │ ├── f1 4836 * │ ├── f2 4837 * │ └── s1d3 [disconnected by path_disconnected] 4838 * │ ├── f1 4839 * │ └── f2 4840 * ├── s2d1 4841 * │ ├── f1 4842 * │ └── s2d2 [bind mount from s1d2] 4843 * │ ├── f1 4844 * │ ├── f2 4845 * │ └── s1d3 4846 * │ ├── f1 4847 * │ └── f2 4848 * ├── s3d1 4849 * │ └── s3d2 4850 * │ └── s3d3 4851 * └── s4d1 [renamed from s1d3 by path_disconnected] 4852 * ├── f1 4853 * ├── f2 4854 * └── s4d2 4855 * └── f1 4856 */ 4857 4858 TEST_F_FORK(layout1_bind, no_restriction) 4859 { 4860 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 4861 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4862 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 4863 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4864 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 4865 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4866 4867 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 4868 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 4869 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 4870 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 4871 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 4872 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 4873 4874 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 4875 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4876 4877 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 4878 } 4879 4880 TEST_F_FORK(layout1_bind, same_content_same_file) 4881 { 4882 /* 4883 * Sets access right on parent directories of both source and 4884 * destination mount points. 4885 */ 4886 const struct rule layer1_parent[] = { 4887 { 4888 .path = dir_s1d1, 4889 .access = ACCESS_RO, 4890 }, 4891 { 4892 .path = dir_s2d1, 4893 .access = ACCESS_RW, 4894 }, 4895 {}, 4896 }; 4897 /* 4898 * Sets access rights on the same bind-mounted directories. The result 4899 * should be ACCESS_RW for both directories, but not both hierarchies 4900 * because of the first layer. 4901 */ 4902 const struct rule layer2_mount_point[] = { 4903 { 4904 .path = dir_s1d2, 4905 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4906 }, 4907 { 4908 .path = dir_s2d2, 4909 .access = ACCESS_RW, 4910 }, 4911 {}, 4912 }; 4913 /* Only allow read-access to the s1d3 hierarchies. */ 4914 const struct rule layer3_source[] = { 4915 { 4916 .path = dir_s1d3, 4917 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4918 }, 4919 {}, 4920 }; 4921 /* Removes all access rights. */ 4922 const struct rule layer4_destination[] = { 4923 { 4924 .path = bind_file1_s1d3, 4925 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 4926 }, 4927 {}, 4928 }; 4929 4930 /* Sets rules for the parent directories. */ 4931 enforce_fs(_metadata, ACCESS_RW, layer1_parent); 4932 4933 /* Checks source hierarchy. */ 4934 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4935 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4936 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4937 4938 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4939 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4940 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4941 4942 /* Checks destination hierarchy. */ 4943 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 4944 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4945 4946 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4947 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4948 4949 /* Sets rules for the mount points. */ 4950 enforce_fs(_metadata, ACCESS_RW, layer2_mount_point); 4951 4952 /* Checks source hierarchy. */ 4953 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 4954 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4955 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4956 4957 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4958 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4959 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4960 4961 /* Checks destination hierarchy. */ 4962 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 4963 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 4964 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4965 4966 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4967 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4968 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4969 4970 /* Sets a (shared) rule only on the source. */ 4971 enforce_fs(_metadata, ACCESS_RW, layer3_source); 4972 4973 /* Checks source hierarchy. */ 4974 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 4975 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4976 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4977 4978 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4979 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4980 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 4981 4982 /* Checks destination hierarchy. */ 4983 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 4984 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 4985 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4986 4987 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4988 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 4989 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4990 4991 /* Sets a (shared) rule only on the destination. */ 4992 enforce_fs(_metadata, ACCESS_RW, layer4_destination); 4993 4994 /* Checks source hierarchy. */ 4995 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 4996 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4997 4998 /* Checks destination hierarchy. */ 4999 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 5000 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 5001 } 5002 5003 TEST_F_FORK(layout1_bind, reparent_cross_mount) 5004 { 5005 const struct rule layer1[] = { 5006 { 5007 /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 5008 .path = dir_s2d1, 5009 .access = LANDLOCK_ACCESS_FS_REFER, 5010 }, 5011 { 5012 .path = bind_dir_s1d3, 5013 .access = LANDLOCK_ACCESS_FS_EXECUTE, 5014 }, 5015 {}, 5016 }; 5017 5018 enforce_fs(_metadata, 5019 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, 5020 layer1); 5021 5022 /* Checks basic denied move. */ 5023 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 5024 ASSERT_EQ(EXDEV, errno); 5025 5026 /* Checks real cross-mount move (Landlock is not involved). */ 5027 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 5028 ASSERT_EQ(EXDEV, errno); 5029 5030 /* Checks move that will give more accesses. */ 5031 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 5032 ASSERT_EQ(EXDEV, errno); 5033 5034 /* Checks legitimate downgrade move. */ 5035 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 5036 } 5037 5038 /* 5039 * Make sure access to file through a disconnected path works as expected. 5040 * This test moves s1d3 to s4d1. 5041 */ 5042 TEST_F_FORK(layout1_bind, path_disconnected) 5043 { 5044 const struct rule layer1_allow_all[] = { 5045 { 5046 .path = TMP_DIR, 5047 .access = ACCESS_ALL, 5048 }, 5049 {}, 5050 }; 5051 const struct rule layer2_allow_just_f1[] = { 5052 { 5053 .path = file1_s1d3, 5054 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5055 }, 5056 {}, 5057 }; 5058 const struct rule layer3_only_s1d2[] = { 5059 { 5060 .path = dir_s1d2, 5061 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5062 }, 5063 {}, 5064 }; 5065 5066 /* Landlock should not deny access just because it is disconnected. */ 5067 int ruleset_fd_l1 = 5068 create_ruleset(_metadata, ACCESS_ALL, layer1_allow_all); 5069 5070 /* Creates the new ruleset now before we move the dir containing the file. */ 5071 int ruleset_fd_l2 = 5072 create_ruleset(_metadata, ACCESS_RW, layer2_allow_just_f1); 5073 int ruleset_fd_l3 = 5074 create_ruleset(_metadata, ACCESS_RW, layer3_only_s1d2); 5075 int bind_s1d3_fd; 5076 5077 enforce_ruleset(_metadata, ruleset_fd_l1); 5078 EXPECT_EQ(0, close(ruleset_fd_l1)); 5079 5080 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5081 ASSERT_LE(0, bind_s1d3_fd); 5082 5083 /* Tests access is possible before we move. */ 5084 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5085 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5086 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 5087 5088 /* Makes it disconnected. */ 5089 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5090 { 5091 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5092 strerror(errno)); 5093 } 5094 5095 /* Tests that access is still possible. */ 5096 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5097 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5098 5099 /* 5100 * Tests that ".." is not possible (not because of Landlock, but just 5101 * because it's disconnected). 5102 */ 5103 EXPECT_EQ(ENOENT, 5104 test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 5105 5106 /* This should still work with a narrower rule. */ 5107 enforce_ruleset(_metadata, ruleset_fd_l2); 5108 EXPECT_EQ(0, close(ruleset_fd_l2)); 5109 5110 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)); 5111 /* 5112 * Accessing a file through a disconnected file descriptor can still be 5113 * allowed by a rule tied to this file, even if it is no longer visible in 5114 * its mount point. 5115 */ 5116 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5117 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5118 5119 enforce_ruleset(_metadata, ruleset_fd_l3); 5120 EXPECT_EQ(0, close(ruleset_fd_l3)); 5121 5122 EXPECT_EQ(EACCES, test_open(file1_s4d1, O_RDONLY)); 5123 /* 5124 * Accessing a file through a disconnected file descriptor can still be 5125 * allowed by a rule tied to the original mount point, even if it is no 5126 * longer visible in its mount point. 5127 */ 5128 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5129 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5130 } 5131 5132 /* 5133 * Test that renameat with disconnected paths works under Landlock. This test 5134 * moves s1d3 to s4d2, so that we can have a rule allowing refers on the move 5135 * target's immediate parent. 5136 */ 5137 TEST_F_FORK(layout1_bind, path_disconnected_rename) 5138 { 5139 const struct rule layer1[] = { 5140 { 5141 .path = dir_s1d2, 5142 .access = LANDLOCK_ACCESS_FS_REFER | 5143 LANDLOCK_ACCESS_FS_MAKE_DIR | 5144 LANDLOCK_ACCESS_FS_REMOVE_DIR | 5145 LANDLOCK_ACCESS_FS_MAKE_REG | 5146 LANDLOCK_ACCESS_FS_REMOVE_FILE | 5147 LANDLOCK_ACCESS_FS_READ_FILE, 5148 }, 5149 { 5150 .path = dir_s4d1, 5151 .access = LANDLOCK_ACCESS_FS_REFER | 5152 LANDLOCK_ACCESS_FS_MAKE_DIR | 5153 LANDLOCK_ACCESS_FS_REMOVE_DIR | 5154 LANDLOCK_ACCESS_FS_MAKE_REG | 5155 LANDLOCK_ACCESS_FS_REMOVE_FILE | 5156 LANDLOCK_ACCESS_FS_READ_FILE, 5157 }, 5158 {} 5159 }; 5160 5161 /* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */ 5162 const struct rule layer2_only_s1d2[] = { 5163 { 5164 .path = dir_s1d2, 5165 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5166 }, 5167 {}, 5168 }; 5169 int ruleset_fd_l1, ruleset_fd_l2; 5170 pid_t child_pid; 5171 int bind_s1d3_fd, status; 5172 5173 ASSERT_EQ(0, mkdir(dir_s4d1, 0755)) 5174 { 5175 TH_LOG("Failed to create %s: %s", dir_s4d1, strerror(errno)); 5176 } 5177 ruleset_fd_l1 = create_ruleset(_metadata, ACCESS_ALL, layer1); 5178 ruleset_fd_l2 = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 5179 layer2_only_s1d2); 5180 5181 enforce_ruleset(_metadata, ruleset_fd_l1); 5182 EXPECT_EQ(0, close(ruleset_fd_l1)); 5183 5184 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5185 ASSERT_LE(0, bind_s1d3_fd); 5186 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5187 5188 /* Tests ENOENT priority over EACCES for disconnected directory. */ 5189 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5190 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5191 { 5192 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5193 strerror(errno)); 5194 } 5195 EXPECT_EQ(ENOENT, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5196 5197 /* 5198 * The file is no longer under s1d2 but we should still be able to access it 5199 * with layer 2 because its mount point is evaluated as the first valid 5200 * directory because it was initially a parent. Do a fork to test this so 5201 * we don't prevent ourselves from renaming it back later. 5202 */ 5203 child_pid = fork(); 5204 ASSERT_LE(0, child_pid); 5205 if (child_pid == 0) { 5206 enforce_ruleset(_metadata, ruleset_fd_l2); 5207 EXPECT_EQ(0, close(ruleset_fd_l2)); 5208 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5209 EXPECT_EQ(EACCES, test_open(file1_s4d2, O_RDONLY)); 5210 5211 /* 5212 * Tests that access widening checks indeed prevents us from renaming it 5213 * back. 5214 */ 5215 EXPECT_EQ(-1, rename(dir_s4d2, dir_s1d3)); 5216 EXPECT_EQ(EXDEV, errno); 5217 5218 /* 5219 * Including through the now disconnected fd (but it should return 5220 * EXDEV). 5221 */ 5222 EXPECT_EQ(-1, renameat(bind_s1d3_fd, file1_name, AT_FDCWD, 5223 file1_s2d2)); 5224 EXPECT_EQ(EXDEV, errno); 5225 _exit(_metadata->exit_code); 5226 return; 5227 } 5228 5229 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5230 EXPECT_EQ(1, WIFEXITED(status)); 5231 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5232 5233 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5234 { 5235 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d1, dir_s1d3, 5236 strerror(errno)); 5237 } 5238 5239 /* Now checks that we can access it under l2. */ 5240 child_pid = fork(); 5241 ASSERT_LE(0, child_pid); 5242 if (child_pid == 0) { 5243 enforce_ruleset(_metadata, ruleset_fd_l2); 5244 EXPECT_EQ(0, close(ruleset_fd_l2)); 5245 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5246 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5247 _exit(_metadata->exit_code); 5248 return; 5249 } 5250 5251 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5252 EXPECT_EQ(1, WIFEXITED(status)); 5253 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5254 5255 /* 5256 * Also test that we can rename via a disconnected path. We move the 5257 * dir back to the disconnected place first, then we rename file1 to 5258 * file2 through our dir fd. 5259 */ 5260 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5261 { 5262 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5263 strerror(errno)); 5264 } 5265 ASSERT_EQ(0, 5266 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)) 5267 { 5268 TH_LOG("Failed to rename %s to %s within disconnected %s: %s", 5269 file1_name, file2_name, bind_dir_s1d3, strerror(errno)); 5270 } 5271 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5272 ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2)) 5273 { 5274 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5275 file2_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5276 } 5277 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 5278 EXPECT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5279 5280 /* Move it back using the disconnected path as the target. */ 5281 ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name)) 5282 { 5283 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5284 file1_s1d2, file1_name, bind_dir_s1d3, strerror(errno)); 5285 } 5286 5287 /* Now make it connected again. */ 5288 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5289 { 5290 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d2, dir_s1d3, 5291 strerror(errno)); 5292 } 5293 5294 /* Checks again that we can access it under l2. */ 5295 enforce_ruleset(_metadata, ruleset_fd_l2); 5296 EXPECT_EQ(0, close(ruleset_fd_l2)); 5297 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5298 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5299 } 5300 5301 /* 5302 * Test that linkat(2) with disconnected paths works under Landlock. This 5303 * test moves s1d3 to s4d1. 5304 */ 5305 TEST_F_FORK(layout1_bind, path_disconnected_link) 5306 { 5307 /* Ruleset to be applied after renaming s1d3 to s4d1. */ 5308 const struct rule layer1[] = { 5309 { 5310 .path = dir_s4d1, 5311 .access = LANDLOCK_ACCESS_FS_REFER | 5312 LANDLOCK_ACCESS_FS_READ_FILE | 5313 LANDLOCK_ACCESS_FS_MAKE_REG | 5314 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5315 }, 5316 { 5317 .path = dir_s2d2, 5318 .access = LANDLOCK_ACCESS_FS_REFER | 5319 LANDLOCK_ACCESS_FS_READ_FILE | 5320 LANDLOCK_ACCESS_FS_MAKE_REG | 5321 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5322 }, 5323 {} 5324 }; 5325 int bind_s1d3_fd; 5326 5327 /* Removes unneeded files created by layout1, otherwise it will EEXIST. */ 5328 ASSERT_EQ(0, unlink(file1_s1d2)); 5329 ASSERT_EQ(0, unlink(file2_s1d3)); 5330 5331 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5332 ASSERT_LE(0, bind_s1d3_fd); 5333 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5334 5335 /* Disconnects bind_s1d3_fd. */ 5336 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5337 { 5338 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5339 strerror(errno)); 5340 } 5341 5342 /* Need this later to test different parent link. */ 5343 ASSERT_EQ(0, mkdir(dir_s4d2, 0755)) 5344 { 5345 TH_LOG("Failed to create %s: %s", dir_s4d2, strerror(errno)); 5346 } 5347 5348 enforce_fs(_metadata, ACCESS_ALL, layer1); 5349 5350 /* From disconnected to connected. */ 5351 ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0)) 5352 { 5353 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5354 file1_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5355 } 5356 5357 /* Tests that we can access via the new link... */ 5358 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)) 5359 { 5360 TH_LOG("Failed to open newly linked %s: %s", file1_s2d2, 5361 strerror(errno)); 5362 } 5363 5364 /* ...as well as the old one. */ 5365 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5366 { 5367 TH_LOG("Failed to open original %s: %s", file1_s4d1, 5368 strerror(errno)); 5369 } 5370 5371 /* From connected to disconnected. */ 5372 ASSERT_EQ(0, unlink(file1_s4d1)); 5373 ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0)) 5374 { 5375 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5376 file1_s2d2, file2_name, bind_dir_s1d3, strerror(errno)); 5377 } 5378 EXPECT_EQ(0, test_open(file2_s4d1, O_RDONLY)); 5379 ASSERT_EQ(0, unlink(file1_s2d2)); 5380 5381 /* From disconnected to disconnected (same parent). */ 5382 ASSERT_EQ(0, 5383 linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0)) 5384 { 5385 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5386 file2_name, file1_name, bind_dir_s1d3, strerror(errno)); 5387 } 5388 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5389 { 5390 TH_LOG("Failed to open newly linked %s: %s", file1_s4d1, 5391 strerror(errno)); 5392 } 5393 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)) 5394 { 5395 TH_LOG("Failed to open %s through newly created link under disconnected path: %s", 5396 file1_name, strerror(errno)); 5397 } 5398 ASSERT_EQ(0, unlink(file2_s4d1)); 5399 5400 /* From disconnected to disconnected (different parent). */ 5401 ASSERT_EQ(0, 5402 linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0)) 5403 { 5404 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5405 file1_name, "s4d2/f1", bind_dir_s1d3, strerror(errno)); 5406 } 5407 EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY)) 5408 { 5409 TH_LOG("Failed to open %s after link: %s", file1_s4d2, 5410 strerror(errno)); 5411 } 5412 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY)) 5413 { 5414 TH_LOG("Failed to open %s through disconnected path after link: %s", 5415 "s4d2/f1", strerror(errno)); 5416 } 5417 } 5418 5419 /* 5420 * layout4_disconnected_leafs with bind mount and renames: 5421 * 5422 * tmp 5423 * ├── s1d1 5424 * │ └── s1d2 [source of the bind mount] 5425 * │ ├── s1d31 5426 * │ │ └── s1d41 [now renamed beneath s3d1] 5427 * │ │ ├── f1 5428 * │ │ └── f2 5429 * │ └── s1d32 5430 * │ └── s1d42 [now renamed beneath s4d1] 5431 * │ ├── f3 5432 * │ └── f4 5433 * ├── s2d1 5434 * │ └── s2d2 [bind mount of s1d2] 5435 * │ ├── s1d31 5436 * │ │ └── s1d41 [opened FD, now renamed beneath s3d1] 5437 * │ │ ├── f1 5438 * │ │ └── f2 5439 * │ └── s1d32 5440 * │ └── s1d42 [opened FD, now renamed beneath s4d1] 5441 * │ ├── f3 5442 * │ └── f4 5443 * ├── s3d1 5444 * │ └── s1d41 [renamed here] 5445 * │ ├── f1 5446 * │ └── f2 5447 * └── s4d1 5448 * └── s1d42 [renamed here] 5449 * ├── f3 5450 * └── f4 5451 */ 5452 /* clang-format off */ 5453 FIXTURE(layout4_disconnected_leafs) { 5454 int s2d2_fd; 5455 }; 5456 /* clang-format on */ 5457 5458 FIXTURE_SETUP(layout4_disconnected_leafs) 5459 { 5460 prepare_layout(_metadata); 5461 5462 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1"); 5463 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2"); 5464 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3"); 5465 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4"); 5466 create_directory(_metadata, TMP_DIR "/s2d1/s2d2"); 5467 create_directory(_metadata, TMP_DIR "/s3d1"); 5468 create_directory(_metadata, TMP_DIR "/s4d1"); 5469 5470 self->s2d2_fd = 5471 open(TMP_DIR "/s2d1/s2d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 5472 ASSERT_LE(0, self->s2d2_fd); 5473 5474 set_cap(_metadata, CAP_SYS_ADMIN); 5475 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2", NULL, 5476 MS_BIND, NULL)); 5477 clear_cap(_metadata, CAP_SYS_ADMIN); 5478 } 5479 5480 FIXTURE_TEARDOWN_PARENT(layout4_disconnected_leafs) 5481 { 5482 /* umount(TMP_DIR "/s2d1") is handled by namespace lifetime. */ 5483 5484 /* Removes files after renames. */ 5485 remove_path(TMP_DIR "/s3d1/s1d41/f1"); 5486 remove_path(TMP_DIR "/s3d1/s1d41/f2"); 5487 remove_path(TMP_DIR "/s4d1/s1d42/f1"); 5488 remove_path(TMP_DIR "/s4d1/s1d42/f3"); 5489 remove_path(TMP_DIR "/s4d1/s1d42/f4"); 5490 remove_path(TMP_DIR "/s4d1/s1d42/f5"); 5491 5492 cleanup_layout(_metadata); 5493 } 5494 5495 FIXTURE_VARIANT(layout4_disconnected_leafs) 5496 { 5497 /* 5498 * Parent of the bind mount source. It should always be ignored when 5499 * testing against files under the s1d41 or s1d42 disconnected directories. 5500 */ 5501 const __u64 allowed_s1d1; 5502 /* 5503 * Source of bind mount (to s2d2). It should always be enforced when 5504 * testing against files under the s1d41 or s1d42 disconnected directories. 5505 */ 5506 const __u64 allowed_s1d2; 5507 /* 5508 * Original parent of s1d41. It should always be ignored when testing 5509 * against files under the s1d41 disconnected directory. 5510 */ 5511 const __u64 allowed_s1d31; 5512 /* 5513 * Original parent of s1d42. It should always be ignored when testing 5514 * against files under the s1d42 disconnected directory. 5515 */ 5516 const __u64 allowed_s1d32; 5517 /* 5518 * Opened and disconnected source directory. It should always be enforced 5519 * when testing against files under the s1d41 disconnected directory. 5520 */ 5521 const __u64 allowed_s1d41; 5522 /* 5523 * Opened and disconnected source directory. It should always be enforced 5524 * when testing against files under the s1d42 disconnected directory. 5525 */ 5526 const __u64 allowed_s1d42; 5527 /* 5528 * File in the s1d41 disconnected directory. It should always be enforced 5529 * when testing against itself under the s1d41 disconnected directory. 5530 */ 5531 const __u64 allowed_f1; 5532 /* 5533 * File in the s1d41 disconnected directory. It should always be enforced 5534 * when testing against itself under the s1d41 disconnected directory. 5535 */ 5536 const __u64 allowed_f2; 5537 /* 5538 * File in the s1d42 disconnected directory. It should always be enforced 5539 * when testing against itself under the s1d42 disconnected directory. 5540 */ 5541 const __u64 allowed_f3; 5542 /* 5543 * Parent of the bind mount destination. It should always be enforced when 5544 * testing against files under the s1d41 or s1d42 disconnected directories. 5545 */ 5546 const __u64 allowed_s2d1; 5547 /* 5548 * Directory covered by the bind mount. It should always be ignored when 5549 * testing against files under the s1d41 or s1d42 disconnected directories. 5550 */ 5551 const __u64 allowed_s2d2; 5552 /* 5553 * New parent of the renamed s1d41. It should always be ignored when 5554 * testing against files under the s1d41 disconnected directory. 5555 */ 5556 const __u64 allowed_s3d1; 5557 /* 5558 * New parent of the renamed s1d42. It should always be ignored when 5559 * testing against files under the s1d42 disconnected directory. 5560 */ 5561 const __u64 allowed_s4d1; 5562 5563 /* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */ 5564 const int expected_read_result; 5565 /* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */ 5566 const int expected_rename_result; 5567 /* 5568 * Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3, 5569 * RENAME_EXCHANGE). 5570 */ 5571 const int expected_exchange_result; 5572 /* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */ 5573 const int expected_same_dir_rename_result; 5574 }; 5575 5576 /* clang-format off */ 5577 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) { 5578 /* clang-format on */ 5579 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 5580 LANDLOCK_ACCESS_FS_READ_FILE | 5581 LANDLOCK_ACCESS_FS_EXECUTE | 5582 LANDLOCK_ACCESS_FS_MAKE_REG, 5583 .expected_read_result = EACCES, 5584 .expected_same_dir_rename_result = EACCES, 5585 .expected_rename_result = EACCES, 5586 .expected_exchange_result = EACCES, 5587 }; 5588 5589 /* clang-format off */ 5590 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) { 5591 /* clang-format on */ 5592 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5593 .expected_read_result = 0, 5594 .expected_same_dir_rename_result = EACCES, 5595 .expected_rename_result = EACCES, 5596 .expected_exchange_result = EACCES, 5597 }; 5598 5599 /* clang-format off */ 5600 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) { 5601 /* clang-format on */ 5602 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 5603 LANDLOCK_ACCESS_FS_MAKE_REG, 5604 .expected_read_result = 0, 5605 .expected_same_dir_rename_result = 0, 5606 .expected_rename_result = EXDEV, 5607 .expected_exchange_result = EXDEV, 5608 }; 5609 5610 /* clang-format off */ 5611 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) { 5612 /* clang-format on */ 5613 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5614 .expected_read_result = EACCES, 5615 .expected_same_dir_rename_result = 0, 5616 .expected_rename_result = 0, 5617 .expected_exchange_result = 0, 5618 }; 5619 5620 /* clang-format off */ 5621 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) { 5622 /* clang-format on */ 5623 .allowed_s1d31 = LANDLOCK_ACCESS_FS_REFER | 5624 LANDLOCK_ACCESS_FS_READ_FILE | 5625 LANDLOCK_ACCESS_FS_EXECUTE | 5626 LANDLOCK_ACCESS_FS_MAKE_REG, 5627 .allowed_s1d32 = LANDLOCK_ACCESS_FS_REFER | 5628 LANDLOCK_ACCESS_FS_READ_FILE | 5629 LANDLOCK_ACCESS_FS_EXECUTE | 5630 LANDLOCK_ACCESS_FS_MAKE_REG, 5631 .expected_read_result = EACCES, 5632 .expected_same_dir_rename_result = EACCES, 5633 .expected_rename_result = EACCES, 5634 .expected_exchange_result = EACCES, 5635 }; 5636 5637 /* clang-format off */ 5638 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) { 5639 /* clang-format on */ 5640 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5641 LANDLOCK_ACCESS_FS_READ_FILE, 5642 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5643 LANDLOCK_ACCESS_FS_READ_FILE, 5644 .expected_read_result = 0, 5645 .expected_same_dir_rename_result = EACCES, 5646 .expected_rename_result = EACCES, 5647 .expected_exchange_result = EACCES, 5648 }; 5649 5650 /* clang-format off */ 5651 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) { 5652 /* clang-format on */ 5653 .allowed_s1d41 = LANDLOCK_ACCESS_FS_READ_FILE | 5654 LANDLOCK_ACCESS_FS_MAKE_REG, 5655 .allowed_s1d42 = LANDLOCK_ACCESS_FS_READ_FILE | 5656 LANDLOCK_ACCESS_FS_MAKE_REG, 5657 .expected_read_result = 0, 5658 .expected_same_dir_rename_result = 0, 5659 .expected_rename_result = EXDEV, 5660 .expected_exchange_result = EXDEV, 5661 }; 5662 5663 /* clang-format off */ 5664 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) { 5665 /* clang-format on */ 5666 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5667 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5668 .expected_read_result = EACCES, 5669 .expected_same_dir_rename_result = 0, 5670 .expected_rename_result = 0, 5671 .expected_exchange_result = 0, 5672 }; 5673 5674 /* The destination directory has more access right. */ 5675 /* clang-format off */ 5676 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) { 5677 /* clang-format on */ 5678 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5679 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5680 LANDLOCK_ACCESS_FS_MAKE_REG | 5681 LANDLOCK_ACCESS_FS_EXECUTE, 5682 .expected_read_result = EACCES, 5683 .expected_same_dir_rename_result = 0, 5684 /* Access denied. */ 5685 .expected_rename_result = EXDEV, 5686 .expected_exchange_result = EXDEV, 5687 }; 5688 5689 /* The destination directory has less access right. */ 5690 /* clang-format off */ 5691 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) { 5692 /* clang-format on */ 5693 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5694 LANDLOCK_ACCESS_FS_MAKE_REG | 5695 LANDLOCK_ACCESS_FS_EXECUTE, 5696 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5697 .expected_read_result = EACCES, 5698 .expected_same_dir_rename_result = 0, 5699 /* Access allowed. */ 5700 .expected_rename_result = 0, 5701 .expected_exchange_result = EXDEV, 5702 }; 5703 5704 /* clang-format off */ 5705 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) { 5706 /* clang-format on */ 5707 .allowed_s2d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5708 LANDLOCK_ACCESS_FS_MAKE_REG, 5709 .expected_read_result = 0, 5710 .expected_same_dir_rename_result = 0, 5711 .expected_rename_result = EXDEV, 5712 .expected_exchange_result = EXDEV, 5713 }; 5714 5715 /* clang-format off */ 5716 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) { 5717 /* clang-format on */ 5718 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5719 .expected_read_result = 0, 5720 .expected_same_dir_rename_result = EACCES, 5721 .expected_rename_result = EACCES, 5722 .expected_exchange_result = EACCES, 5723 }; 5724 5725 /* clang-format off */ 5726 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) { 5727 /* clang-format on */ 5728 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 5729 LANDLOCK_ACCESS_FS_READ_FILE | 5730 LANDLOCK_ACCESS_FS_MAKE_REG, 5731 .expected_read_result = 0, 5732 .expected_same_dir_rename_result = 0, 5733 .expected_rename_result = 0, 5734 .expected_exchange_result = 0, 5735 }; 5736 5737 /* clang-format off */ 5738 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) { 5739 /* clang-format on */ 5740 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | 5741 LANDLOCK_ACCESS_FS_READ_FILE | 5742 LANDLOCK_ACCESS_FS_EXECUTE | 5743 LANDLOCK_ACCESS_FS_MAKE_REG, 5744 .expected_read_result = EACCES, 5745 .expected_same_dir_rename_result = EACCES, 5746 .expected_rename_result = EACCES, 5747 .expected_exchange_result = EACCES, 5748 }; 5749 5750 /* Tests collect_domain_accesses(). */ 5751 /* clang-format off */ 5752 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) { 5753 /* clang-format on */ 5754 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5755 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5756 .expected_read_result = 0, 5757 .expected_same_dir_rename_result = EACCES, 5758 .expected_rename_result = EACCES, 5759 .expected_exchange_result = EACCES, 5760 }; 5761 5762 /* clang-format off */ 5763 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) { 5764 /* clang-format on */ 5765 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5766 LANDLOCK_ACCESS_FS_MAKE_REG, 5767 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5768 LANDLOCK_ACCESS_FS_MAKE_REG, 5769 .expected_read_result = 0, 5770 .expected_same_dir_rename_result = 0, 5771 .expected_rename_result = EXDEV, 5772 .expected_exchange_result = EXDEV, 5773 }; 5774 5775 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, 5776 s3d1_s4d1_disconnected_rename_even){ 5777 /* clang-format on */ 5778 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5779 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5780 .expected_read_result = EACCES, 5781 .expected_same_dir_rename_result = 0, 5782 .expected_rename_result = 0, 5783 .expected_exchange_result = 0, 5784 }; 5785 5786 /* The destination directory has more access right. */ 5787 /* clang-format off */ 5788 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) { 5789 /* clang-format on */ 5790 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5791 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5792 LANDLOCK_ACCESS_FS_EXECUTE, 5793 .expected_read_result = EACCES, 5794 .expected_same_dir_rename_result = 0, 5795 /* Access denied. */ 5796 .expected_rename_result = EXDEV, 5797 .expected_exchange_result = EXDEV, 5798 }; 5799 5800 /* The destination directory has less access right. */ 5801 /* clang-format off */ 5802 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) { 5803 /* clang-format on */ 5804 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5805 LANDLOCK_ACCESS_FS_EXECUTE, 5806 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5807 .expected_read_result = EACCES, 5808 .expected_same_dir_rename_result = 0, 5809 /* Access allowed. */ 5810 .expected_rename_result = 0, 5811 .expected_exchange_result = EXDEV, 5812 }; 5813 5814 /* clang-format off */ 5815 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) { 5816 /* clang-format on */ 5817 .allowed_f1 = LANDLOCK_ACCESS_FS_READ_FILE, 5818 .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE, 5819 .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE, 5820 .expected_read_result = 0, 5821 .expected_same_dir_rename_result = EACCES, 5822 .expected_rename_result = EACCES, 5823 .expected_exchange_result = EACCES, 5824 }; 5825 5826 TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange) 5827 { 5828 const __u64 handled_access = 5829 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 5830 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 5831 const struct rule rules[] = { 5832 { 5833 .path = TMP_DIR "/s1d1", 5834 .access = variant->allowed_s1d1, 5835 }, 5836 { 5837 .path = TMP_DIR "/s1d1/s1d2", 5838 .access = variant->allowed_s1d2, 5839 }, 5840 { 5841 .path = TMP_DIR "/s1d1/s1d2/s1d31", 5842 .access = variant->allowed_s1d31, 5843 }, 5844 { 5845 .path = TMP_DIR "/s1d1/s1d2/s1d32", 5846 .access = variant->allowed_s1d32, 5847 }, 5848 { 5849 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5850 .access = variant->allowed_s1d41, 5851 }, 5852 { 5853 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5854 .access = variant->allowed_s1d42, 5855 }, 5856 { 5857 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1", 5858 .access = variant->allowed_f1, 5859 }, 5860 { 5861 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2", 5862 .access = variant->allowed_f2, 5863 }, 5864 { 5865 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3", 5866 .access = variant->allowed_f3, 5867 }, 5868 { 5869 .path = TMP_DIR "/s2d1", 5870 .access = variant->allowed_s2d1, 5871 }, 5872 /* s2d2_fd */ 5873 { 5874 .path = TMP_DIR "/s3d1", 5875 .access = variant->allowed_s3d1, 5876 }, 5877 { 5878 .path = TMP_DIR "/s4d1", 5879 .access = variant->allowed_s4d1, 5880 }, 5881 {}, 5882 }; 5883 int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; 5884 5885 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 5886 5887 /* Adds rule for the covered directory. */ 5888 if (variant->allowed_s2d2) { 5889 ASSERT_EQ(0, landlock_add_rule( 5890 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 5891 &(struct landlock_path_beneath_attr){ 5892 .parent_fd = self->s2d2_fd, 5893 .allowed_access = 5894 variant->allowed_s2d2, 5895 }, 5896 0)); 5897 } 5898 EXPECT_EQ(0, close(self->s2d2_fd)); 5899 5900 s1d41_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d31/s1d41", 5901 O_DIRECTORY | O_PATH | O_CLOEXEC); 5902 ASSERT_LE(0, s1d41_bind_fd); 5903 s1d42_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d32/s1d42", 5904 O_DIRECTORY | O_PATH | O_CLOEXEC); 5905 ASSERT_LE(0, s1d42_bind_fd); 5906 5907 /* Disconnects and checks source and destination directories. */ 5908 EXPECT_EQ(0, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5909 EXPECT_EQ(0, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5910 /* Renames to make it accessible through s3d1/s1d41 */ 5911 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5912 AT_FDCWD, TMP_DIR "/s3d1/s1d41")); 5913 /* Renames to make it accessible through s4d1/s1d42 */ 5914 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5915 AT_FDCWD, TMP_DIR "/s4d1/s1d42")); 5916 EXPECT_EQ(ENOENT, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5917 EXPECT_EQ(ENOENT, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5918 5919 enforce_ruleset(_metadata, ruleset_fd); 5920 EXPECT_EQ(0, close(ruleset_fd)); 5921 5922 EXPECT_EQ(variant->expected_read_result, 5923 test_open_rel(s1d41_bind_fd, "f1", O_RDONLY)); 5924 5925 EXPECT_EQ(variant->expected_rename_result, 5926 test_renameat(s1d41_bind_fd, "f1", s1d42_bind_fd, "f1")); 5927 EXPECT_EQ(variant->expected_exchange_result, 5928 test_exchangeat(s1d41_bind_fd, "f2", s1d42_bind_fd, "f3")); 5929 5930 EXPECT_EQ(variant->expected_same_dir_rename_result, 5931 test_renameat(s1d42_bind_fd, "f4", s1d42_bind_fd, "f5")); 5932 } 5933 5934 /* 5935 * layout5_disconnected_branch before rename: 5936 * 5937 * tmp 5938 * ├── s1d1 5939 * │ └── s1d2 [source of the first bind mount] 5940 * │ └── s1d3 5941 * │ ├── s1d41 5942 * │ │ ├── f1 5943 * │ │ └── f2 5944 * │ └── s1d42 5945 * │ ├── f3 5946 * │ └── f4 5947 * ├── s2d1 5948 * │ └── s2d2 [source of the second bind mount] 5949 * │ └── s2d3 5950 * │ └── s2d4 [first s1d2 bind mount] 5951 * │ └── s1d3 5952 * │ ├── s1d41 5953 * │ │ ├── f1 5954 * │ │ └── f2 5955 * │ └── s1d42 5956 * │ ├── f3 5957 * │ └── f4 5958 * ├── s3d1 5959 * │ └── s3d2 [second s2d2 bind mount] 5960 * │ └── s2d3 5961 * │ └── s2d4 [first s1d2 bind mount] 5962 * │ └── s1d3 5963 * │ ├── s1d41 5964 * │ │ ├── f1 5965 * │ │ └── f2 5966 * │ └── s1d42 5967 * │ ├── f3 5968 * │ └── f4 5969 * └── s4d1 5970 * 5971 * After rename: 5972 * 5973 * tmp 5974 * ├── s1d1 5975 * │ └── s1d2 [source of the first bind mount] 5976 * │ └── s1d3 5977 * │ ├── s1d41 5978 * │ │ ├── f1 5979 * │ │ └── f2 5980 * │ └── s1d42 5981 * │ ├── f3 5982 * │ └── f4 5983 * ├── s2d1 5984 * │ └── s2d2 [source of the second bind mount] 5985 * ├── s3d1 5986 * │ └── s3d2 [second s2d2 bind mount] 5987 * └── s4d1 5988 * └── s2d3 [renamed here] 5989 * └── s2d4 [first s1d2 bind mount] 5990 * └── s1d3 5991 * ├── s1d41 5992 * │ ├── f1 5993 * │ └── f2 5994 * └── s1d42 5995 * ├── f3 5996 * └── f4 5997 * 5998 * Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor: 5999 * 1. first bind mount: s1d3 -> s1d2 6000 * 2. second bind mount: s2d3 6001 * 3. tmp mount: s4d1 -> tmp [disconnected branch] 6002 * 4. second bind mount: s2d2 6003 * 5. tmp mount: s3d1 -> tmp 6004 * 6. parent mounts: [...] -> / 6005 * 6006 * The s4d1 directory is evaluated even if it is not in the s2d2 mount. 6007 */ 6008 6009 /* clang-format off */ 6010 FIXTURE(layout5_disconnected_branch) { 6011 int s2d4_fd, s3d2_fd; 6012 }; 6013 /* clang-format on */ 6014 6015 FIXTURE_SETUP(layout5_disconnected_branch) 6016 { 6017 prepare_layout(_metadata); 6018 6019 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 6020 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 6021 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 6022 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 6023 create_directory(_metadata, TMP_DIR "/s2d1/s2d2/s2d3/s2d4"); 6024 create_directory(_metadata, TMP_DIR "/s3d1/s3d2"); 6025 create_directory(_metadata, TMP_DIR "/s4d1"); 6026 6027 self->s2d4_fd = open(TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 6028 O_DIRECTORY | O_PATH | O_CLOEXEC); 6029 ASSERT_LE(0, self->s2d4_fd); 6030 6031 self->s3d2_fd = 6032 open(TMP_DIR "/s3d1/s3d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 6033 ASSERT_LE(0, self->s3d2_fd); 6034 6035 set_cap(_metadata, CAP_SYS_ADMIN); 6036 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 6037 NULL, MS_BIND, NULL)); 6038 ASSERT_EQ(0, mount(TMP_DIR "/s2d1/s2d2", TMP_DIR "/s3d1/s3d2", NULL, 6039 MS_BIND | MS_REC, NULL)); 6040 clear_cap(_metadata, CAP_SYS_ADMIN); 6041 } 6042 6043 FIXTURE_TEARDOWN_PARENT(layout5_disconnected_branch) 6044 { 6045 /* Bind mounts are handled by namespace lifetime. */ 6046 6047 /* Removes files after renames. */ 6048 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 6049 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 6050 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1"); 6051 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 6052 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 6053 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5"); 6054 6055 cleanup_layout(_metadata); 6056 } 6057 6058 FIXTURE_VARIANT(layout5_disconnected_branch) 6059 { 6060 /* 6061 * Parent of all files. It should always be enforced when testing against 6062 * files under the s1d41 or s1d42 disconnected directories. 6063 */ 6064 const __u64 allowed_base; 6065 /* 6066 * Parent of the first bind mount source. It should always be ignored when 6067 * testing against files under the s1d41 or s1d42 disconnected directories. 6068 */ 6069 const __u64 allowed_s1d1; 6070 const __u64 allowed_s1d2; 6071 const __u64 allowed_s1d3; 6072 const __u64 allowed_s2d1; 6073 const __u64 allowed_s2d2; 6074 const __u64 allowed_s2d3; 6075 const __u64 allowed_s2d4; 6076 const __u64 allowed_s3d1; 6077 const __u64 allowed_s3d2; 6078 const __u64 allowed_s4d1; 6079 6080 /* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */ 6081 const int expected_read_result; 6082 /* 6083 * Expected result of the call to renameat([fd:s1d3]/s1d41/f1, 6084 * [fd:s1d3]/s1d42/f1). 6085 */ 6086 const int expected_rename_result; 6087 /* 6088 * Expected result of the call to renameat([fd:s1d3]/s1d41/f2, 6089 * [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE). 6090 */ 6091 const int expected_exchange_result; 6092 /* 6093 * Expected result of the call to renameat([fd:s1d3]/s1d42/f4, 6094 * [fd:s1d3]/s1d42/f5). 6095 */ 6096 const int expected_same_dir_rename_result; 6097 }; 6098 6099 /* clang-format off */ 6100 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) { 6101 /* clang-format on */ 6102 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 6103 LANDLOCK_ACCESS_FS_READ_FILE | 6104 LANDLOCK_ACCESS_FS_EXECUTE | 6105 LANDLOCK_ACCESS_FS_MAKE_REG, 6106 .expected_read_result = EACCES, 6107 .expected_same_dir_rename_result = EACCES, 6108 .expected_rename_result = EACCES, 6109 .expected_exchange_result = EACCES, 6110 }; 6111 6112 /* clang-format off */ 6113 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) { 6114 /* clang-format on */ 6115 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6116 .expected_read_result = 0, 6117 .expected_same_dir_rename_result = EACCES, 6118 .expected_rename_result = EACCES, 6119 .expected_exchange_result = EACCES, 6120 }; 6121 6122 /* clang-format off */ 6123 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) { 6124 /* clang-format on */ 6125 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6126 LANDLOCK_ACCESS_FS_MAKE_REG, 6127 .expected_read_result = 0, 6128 .expected_same_dir_rename_result = 0, 6129 .expected_rename_result = EXDEV, 6130 .expected_exchange_result = EXDEV, 6131 }; 6132 6133 /* clang-format off */ 6134 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) { 6135 /* clang-format on */ 6136 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6137 .expected_read_result = EACCES, 6138 .expected_same_dir_rename_result = 0, 6139 .expected_rename_result = 0, 6140 .expected_exchange_result = 0, 6141 }; 6142 6143 /* clang-format off */ 6144 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) { 6145 /* clang-format on */ 6146 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6147 .expected_read_result = 0, 6148 .expected_same_dir_rename_result = EACCES, 6149 .expected_rename_result = EACCES, 6150 .expected_exchange_result = EACCES, 6151 }; 6152 6153 /* clang-format off */ 6154 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) { 6155 /* clang-format on */ 6156 .allowed_s1d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6157 LANDLOCK_ACCESS_FS_MAKE_REG, 6158 .expected_read_result = 0, 6159 .expected_same_dir_rename_result = 0, 6160 .expected_rename_result = EXDEV, 6161 .expected_exchange_result = EXDEV, 6162 }; 6163 6164 /* clang-format off */ 6165 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) { 6166 /* clang-format on */ 6167 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6168 .expected_read_result = EACCES, 6169 .expected_same_dir_rename_result = 0, 6170 .expected_rename_result = 0, 6171 .expected_exchange_result = 0, 6172 }; 6173 6174 /* clang-format off */ 6175 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) { 6176 /* clang-format on */ 6177 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | 6178 LANDLOCK_ACCESS_FS_READ_FILE | 6179 LANDLOCK_ACCESS_FS_EXECUTE | 6180 LANDLOCK_ACCESS_FS_MAKE_REG, 6181 .expected_read_result = 0, 6182 .expected_same_dir_rename_result = 0, 6183 .expected_rename_result = 0, 6184 .expected_exchange_result = 0, 6185 }; 6186 6187 /* clang-format off */ 6188 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) { 6189 /* clang-format on */ 6190 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 6191 LANDLOCK_ACCESS_FS_READ_FILE | 6192 LANDLOCK_ACCESS_FS_EXECUTE | 6193 LANDLOCK_ACCESS_FS_MAKE_REG, 6194 .expected_read_result = EACCES, 6195 .expected_same_dir_rename_result = EACCES, 6196 .expected_rename_result = EACCES, 6197 .expected_exchange_result = EACCES, 6198 }; 6199 6200 /* clang-format off */ 6201 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) { 6202 /* clang-format on */ 6203 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6204 .expected_read_result = 0, 6205 .expected_same_dir_rename_result = EACCES, 6206 .expected_rename_result = EACCES, 6207 .expected_exchange_result = EACCES, 6208 }; 6209 6210 /* clang-format off */ 6211 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) { 6212 /* clang-format on */ 6213 .allowed_s2d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6214 LANDLOCK_ACCESS_FS_MAKE_REG, 6215 .expected_read_result = 0, 6216 .expected_same_dir_rename_result = 0, 6217 .expected_rename_result = EXDEV, 6218 .expected_exchange_result = EXDEV, 6219 }; 6220 6221 /* clang-format off */ 6222 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) { 6223 /* clang-format on */ 6224 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6225 .expected_read_result = EACCES, 6226 .expected_same_dir_rename_result = 0, 6227 .expected_rename_result = 0, 6228 .expected_exchange_result = 0, 6229 }; 6230 6231 /* clang-format off */ 6232 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) { 6233 /* clang-format on */ 6234 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6235 .expected_read_result = 0, 6236 .expected_same_dir_rename_result = EACCES, 6237 .expected_rename_result = EACCES, 6238 .expected_exchange_result = EACCES, 6239 }; 6240 6241 /* clang-format off */ 6242 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) { 6243 /* clang-format on */ 6244 .allowed_s2d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6245 LANDLOCK_ACCESS_FS_MAKE_REG, 6246 .expected_read_result = 0, 6247 .expected_same_dir_rename_result = 0, 6248 .expected_rename_result = EXDEV, 6249 .expected_exchange_result = EXDEV, 6250 }; 6251 6252 /* clang-format off */ 6253 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) { 6254 /* clang-format on */ 6255 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6256 .expected_read_result = EACCES, 6257 .expected_same_dir_rename_result = 0, 6258 .expected_rename_result = 0, 6259 .expected_exchange_result = 0, 6260 }; 6261 6262 /* clang-format off */ 6263 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) { 6264 /* clang-format on */ 6265 .allowed_s2d4 = LANDLOCK_ACCESS_FS_REFER | 6266 LANDLOCK_ACCESS_FS_READ_FILE | 6267 LANDLOCK_ACCESS_FS_EXECUTE | 6268 LANDLOCK_ACCESS_FS_MAKE_REG, 6269 .expected_read_result = EACCES, 6270 .expected_same_dir_rename_result = EACCES, 6271 .expected_rename_result = EACCES, 6272 .expected_exchange_result = EACCES, 6273 }; 6274 6275 /* clang-format off */ 6276 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) { 6277 /* clang-format on */ 6278 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6279 .expected_read_result = 0, 6280 .expected_same_dir_rename_result = EACCES, 6281 .expected_rename_result = EACCES, 6282 .expected_exchange_result = EACCES, 6283 }; 6284 6285 /* clang-format off */ 6286 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) { 6287 /* clang-format on */ 6288 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6289 LANDLOCK_ACCESS_FS_MAKE_REG, 6290 .expected_read_result = 0, 6291 .expected_same_dir_rename_result = 0, 6292 .expected_rename_result = EXDEV, 6293 .expected_exchange_result = EXDEV, 6294 }; 6295 6296 /* clang-format off */ 6297 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) { 6298 /* clang-format on */ 6299 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6300 .expected_read_result = EACCES, 6301 .expected_same_dir_rename_result = 0, 6302 .expected_rename_result = 0, 6303 .expected_exchange_result = 0, 6304 }; 6305 6306 /* clang-format off */ 6307 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) { 6308 /* clang-format on */ 6309 .allowed_s3d2 = LANDLOCK_ACCESS_FS_REFER | 6310 LANDLOCK_ACCESS_FS_READ_FILE | 6311 LANDLOCK_ACCESS_FS_EXECUTE | 6312 LANDLOCK_ACCESS_FS_MAKE_REG, 6313 .expected_read_result = EACCES, 6314 .expected_same_dir_rename_result = EACCES, 6315 .expected_rename_result = EACCES, 6316 .expected_exchange_result = EACCES, 6317 }; 6318 6319 /* clang-format off */ 6320 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) { 6321 /* clang-format on */ 6322 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6323 .expected_read_result = 0, 6324 .expected_same_dir_rename_result = EACCES, 6325 .expected_rename_result = EACCES, 6326 .expected_exchange_result = EACCES, 6327 }; 6328 6329 /* clang-format off */ 6330 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) { 6331 /* clang-format on */ 6332 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6333 LANDLOCK_ACCESS_FS_MAKE_REG, 6334 .expected_read_result = 0, 6335 .expected_same_dir_rename_result = 0, 6336 .expected_rename_result = EXDEV, 6337 .expected_exchange_result = EXDEV, 6338 }; 6339 6340 /* clang-format off */ 6341 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) { 6342 /* clang-format on */ 6343 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6344 .expected_read_result = EACCES, 6345 .expected_same_dir_rename_result = 0, 6346 .expected_rename_result = 0, 6347 .expected_exchange_result = 0, 6348 }; 6349 6350 TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange) 6351 { 6352 const __u64 handled_access = 6353 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 6354 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 6355 const struct rule rules[] = { 6356 { 6357 .path = TMP_DIR "/s1d1", 6358 .access = variant->allowed_s1d1, 6359 }, 6360 { 6361 .path = TMP_DIR "/s1d1/s1d2", 6362 .access = variant->allowed_s1d2, 6363 }, 6364 { 6365 .path = TMP_DIR "/s1d1/s1d2/s1d3", 6366 .access = variant->allowed_s1d3, 6367 }, 6368 { 6369 .path = TMP_DIR "/s2d1", 6370 .access = variant->allowed_s2d1, 6371 }, 6372 { 6373 .path = TMP_DIR "/s2d1/s2d2", 6374 .access = variant->allowed_s2d2, 6375 }, 6376 { 6377 .path = TMP_DIR "/s2d1/s2d2/s2d3", 6378 .access = variant->allowed_s2d3, 6379 }, 6380 /* s2d4_fd */ 6381 { 6382 .path = TMP_DIR "/s3d1", 6383 .access = variant->allowed_s3d1, 6384 }, 6385 /* s3d2_fd */ 6386 { 6387 .path = TMP_DIR "/s4d1", 6388 .access = variant->allowed_s4d1, 6389 }, 6390 {}, 6391 }; 6392 int ruleset_fd, s1d3_bind_fd; 6393 6394 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 6395 ASSERT_LE(0, ruleset_fd); 6396 6397 /* Adds rules for the covered directories. */ 6398 if (variant->allowed_s2d4) { 6399 ASSERT_EQ(0, landlock_add_rule( 6400 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6401 &(struct landlock_path_beneath_attr){ 6402 .parent_fd = self->s2d4_fd, 6403 .allowed_access = 6404 variant->allowed_s2d4, 6405 }, 6406 0)); 6407 } 6408 EXPECT_EQ(0, close(self->s2d4_fd)); 6409 6410 if (variant->allowed_s3d2) { 6411 ASSERT_EQ(0, landlock_add_rule( 6412 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6413 &(struct landlock_path_beneath_attr){ 6414 .parent_fd = self->s3d2_fd, 6415 .allowed_access = 6416 variant->allowed_s3d2, 6417 }, 6418 0)); 6419 } 6420 EXPECT_EQ(0, close(self->s3d2_fd)); 6421 6422 s1d3_bind_fd = open(TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3", 6423 O_DIRECTORY | O_PATH | O_CLOEXEC); 6424 ASSERT_LE(0, s1d3_bind_fd); 6425 6426 /* Disconnects and checks source and destination directories. */ 6427 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6428 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6429 /* Renames to make it accessible through s3d1/s1d41 */ 6430 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s2d1/s2d2/s2d3", 6431 AT_FDCWD, TMP_DIR "/s4d1/s2d3")); 6432 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6433 EXPECT_EQ(ENOENT, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6434 6435 enforce_ruleset(_metadata, ruleset_fd); 6436 EXPECT_EQ(0, close(ruleset_fd)); 6437 6438 EXPECT_EQ(variant->expected_read_result, 6439 test_open_rel(s1d3_bind_fd, "s1d41/f1", O_RDONLY)); 6440 6441 EXPECT_EQ(variant->expected_rename_result, 6442 test_renameat(s1d3_bind_fd, "s1d41/f1", s1d3_bind_fd, 6443 "s1d42/f1")); 6444 EXPECT_EQ(variant->expected_exchange_result, 6445 test_exchangeat(s1d3_bind_fd, "s1d41/f2", s1d3_bind_fd, 6446 "s1d42/f3")); 6447 6448 EXPECT_EQ(variant->expected_same_dir_rename_result, 6449 test_renameat(s1d3_bind_fd, "s1d42/f4", s1d3_bind_fd, 6450 "s1d42/f5")); 6451 } 6452 6453 #define LOWER_BASE TMP_DIR "/lower" 6454 #define LOWER_DATA LOWER_BASE "/data" 6455 static const char lower_fl1[] = LOWER_DATA "/fl1"; 6456 static const char lower_dl1[] = LOWER_DATA "/dl1"; 6457 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 6458 static const char lower_fo1[] = LOWER_DATA "/fo1"; 6459 static const char lower_do1[] = LOWER_DATA "/do1"; 6460 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 6461 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 6462 6463 static const char (*lower_base_files[])[] = { 6464 &lower_fl1, 6465 &lower_fo1, 6466 NULL, 6467 }; 6468 static const char (*lower_base_directories[])[] = { 6469 &lower_dl1, 6470 &lower_do1, 6471 NULL, 6472 }; 6473 static const char (*lower_sub_files[])[] = { 6474 &lower_dl1_fl2, 6475 &lower_do1_fo2, 6476 &lower_do1_fl3, 6477 NULL, 6478 }; 6479 6480 #define UPPER_BASE TMP_DIR "/upper" 6481 #define UPPER_DATA UPPER_BASE "/data" 6482 #define UPPER_WORK UPPER_BASE "/work" 6483 static const char upper_fu1[] = UPPER_DATA "/fu1"; 6484 static const char upper_du1[] = UPPER_DATA "/du1"; 6485 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 6486 static const char upper_fo1[] = UPPER_DATA "/fo1"; 6487 static const char upper_do1[] = UPPER_DATA "/do1"; 6488 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 6489 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 6490 6491 static const char (*upper_base_files[])[] = { 6492 &upper_fu1, 6493 &upper_fo1, 6494 NULL, 6495 }; 6496 static const char (*upper_base_directories[])[] = { 6497 &upper_du1, 6498 &upper_do1, 6499 NULL, 6500 }; 6501 static const char (*upper_sub_files[])[] = { 6502 &upper_du1_fu2, 6503 &upper_do1_fo2, 6504 &upper_do1_fu3, 6505 NULL, 6506 }; 6507 6508 #define MERGE_BASE TMP_DIR "/merge" 6509 #define MERGE_DATA MERGE_BASE "/data" 6510 static const char merge_fl1[] = MERGE_DATA "/fl1"; 6511 static const char merge_dl1[] = MERGE_DATA "/dl1"; 6512 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 6513 static const char merge_fu1[] = MERGE_DATA "/fu1"; 6514 static const char merge_du1[] = MERGE_DATA "/du1"; 6515 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 6516 static const char merge_fo1[] = MERGE_DATA "/fo1"; 6517 static const char merge_do1[] = MERGE_DATA "/do1"; 6518 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 6519 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 6520 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 6521 6522 static const char (*merge_base_files[])[] = { 6523 &merge_fl1, 6524 &merge_fu1, 6525 &merge_fo1, 6526 NULL, 6527 }; 6528 static const char (*merge_base_directories[])[] = { 6529 &merge_dl1, 6530 &merge_du1, 6531 &merge_do1, 6532 NULL, 6533 }; 6534 static const char (*merge_sub_files[])[] = { 6535 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 6536 &merge_do1_fl3, &merge_do1_fu3, NULL, 6537 }; 6538 6539 /* 6540 * layout2_overlay hierarchy: 6541 * 6542 * tmp 6543 * ├── lower 6544 * │ └── data 6545 * │ ├── dl1 6546 * │ │ └── fl2 6547 * │ ├── do1 6548 * │ │ ├── fl3 6549 * │ │ └── fo2 6550 * │ ├── fl1 6551 * │ └── fo1 6552 * ├── merge 6553 * │ └── data 6554 * │ ├── dl1 6555 * │ │ └── fl2 6556 * │ ├── do1 6557 * │ │ ├── fl3 6558 * │ │ ├── fo2 6559 * │ │ └── fu3 6560 * │ ├── du1 6561 * │ │ └── fu2 6562 * │ ├── fl1 6563 * │ ├── fo1 6564 * │ └── fu1 6565 * └── upper 6566 * ├── data 6567 * │ ├── do1 6568 * │ │ ├── fo2 6569 * │ │ └── fu3 6570 * │ ├── du1 6571 * │ │ └── fu2 6572 * │ ├── fo1 6573 * │ └── fu1 6574 * └── work 6575 * └── work 6576 */ 6577 6578 FIXTURE(layout2_overlay) 6579 { 6580 bool skip_test; 6581 }; 6582 6583 FIXTURE_SETUP(layout2_overlay) 6584 { 6585 if (!supports_filesystem("overlay")) { 6586 self->skip_test = true; 6587 SKIP(return, "overlayfs is not supported (setup)"); 6588 } 6589 6590 prepare_layout(_metadata); 6591 6592 create_directory(_metadata, LOWER_BASE); 6593 set_cap(_metadata, CAP_SYS_ADMIN); 6594 /* Creates tmpfs mount points to get deterministic overlayfs. */ 6595 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 6596 clear_cap(_metadata, CAP_SYS_ADMIN); 6597 create_file(_metadata, lower_fl1); 6598 create_file(_metadata, lower_dl1_fl2); 6599 create_file(_metadata, lower_fo1); 6600 create_file(_metadata, lower_do1_fo2); 6601 create_file(_metadata, lower_do1_fl3); 6602 6603 create_directory(_metadata, UPPER_BASE); 6604 set_cap(_metadata, CAP_SYS_ADMIN); 6605 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 6606 clear_cap(_metadata, CAP_SYS_ADMIN); 6607 create_file(_metadata, upper_fu1); 6608 create_file(_metadata, upper_du1_fu2); 6609 create_file(_metadata, upper_fo1); 6610 create_file(_metadata, upper_do1_fo2); 6611 create_file(_metadata, upper_do1_fu3); 6612 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 6613 6614 create_directory(_metadata, MERGE_DATA); 6615 set_cap(_metadata, CAP_SYS_ADMIN); 6616 set_cap(_metadata, CAP_DAC_OVERRIDE); 6617 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 6618 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 6619 ",workdir=" UPPER_WORK)); 6620 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6621 clear_cap(_metadata, CAP_SYS_ADMIN); 6622 } 6623 6624 FIXTURE_TEARDOWN_PARENT(layout2_overlay) 6625 { 6626 if (self->skip_test) 6627 SKIP(return, "overlayfs is not supported (teardown)"); 6628 6629 EXPECT_EQ(0, remove_path(lower_do1_fl3)); 6630 EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 6631 EXPECT_EQ(0, remove_path(lower_fl1)); 6632 EXPECT_EQ(0, remove_path(lower_do1_fo2)); 6633 EXPECT_EQ(0, remove_path(lower_fo1)); 6634 6635 /* umount(LOWER_BASE)) is handled by namespace lifetime. */ 6636 EXPECT_EQ(0, remove_path(LOWER_BASE)); 6637 6638 EXPECT_EQ(0, remove_path(upper_do1_fu3)); 6639 EXPECT_EQ(0, remove_path(upper_du1_fu2)); 6640 EXPECT_EQ(0, remove_path(upper_fu1)); 6641 EXPECT_EQ(0, remove_path(upper_do1_fo2)); 6642 EXPECT_EQ(0, remove_path(upper_fo1)); 6643 EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 6644 6645 /* umount(UPPER_BASE)) is handled by namespace lifetime. */ 6646 EXPECT_EQ(0, remove_path(UPPER_BASE)); 6647 6648 /* umount(MERGE_DATA)) is handled by namespace lifetime. */ 6649 EXPECT_EQ(0, remove_path(MERGE_DATA)); 6650 6651 cleanup_layout(_metadata); 6652 } 6653 6654 TEST_F_FORK(layout2_overlay, no_restriction) 6655 { 6656 if (self->skip_test) 6657 SKIP(return, "overlayfs is not supported (test)"); 6658 6659 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 6660 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 6661 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 6662 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 6663 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 6664 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 6665 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 6666 6667 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 6668 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 6669 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 6670 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 6671 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 6672 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 6673 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 6674 6675 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 6676 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 6677 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 6678 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 6679 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 6680 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 6681 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 6682 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 6683 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 6684 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 6685 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 6686 } 6687 6688 #define for_each_path(path_list, path_entry, i) \ 6689 for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 6690 path_entry = *path_list[++i]) 6691 6692 TEST_F_FORK(layout2_overlay, same_content_different_file) 6693 { 6694 /* Sets access right on parent directories of both layers. */ 6695 const struct rule layer1_base[] = { 6696 { 6697 .path = LOWER_BASE, 6698 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6699 }, 6700 { 6701 .path = UPPER_BASE, 6702 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6703 }, 6704 { 6705 .path = MERGE_BASE, 6706 .access = ACCESS_RW, 6707 }, 6708 {}, 6709 }; 6710 const struct rule layer2_data[] = { 6711 { 6712 .path = LOWER_DATA, 6713 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6714 }, 6715 { 6716 .path = UPPER_DATA, 6717 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6718 }, 6719 { 6720 .path = MERGE_DATA, 6721 .access = ACCESS_RW, 6722 }, 6723 {}, 6724 }; 6725 /* Sets access right on directories inside both layers. */ 6726 const struct rule layer3_subdirs[] = { 6727 { 6728 .path = lower_dl1, 6729 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6730 }, 6731 { 6732 .path = lower_do1, 6733 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6734 }, 6735 { 6736 .path = upper_du1, 6737 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6738 }, 6739 { 6740 .path = upper_do1, 6741 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6742 }, 6743 { 6744 .path = merge_dl1, 6745 .access = ACCESS_RW, 6746 }, 6747 { 6748 .path = merge_du1, 6749 .access = ACCESS_RW, 6750 }, 6751 { 6752 .path = merge_do1, 6753 .access = ACCESS_RW, 6754 }, 6755 {}, 6756 }; 6757 /* Tighten access rights to the files. */ 6758 const struct rule layer4_files[] = { 6759 { 6760 .path = lower_dl1_fl2, 6761 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6762 }, 6763 { 6764 .path = lower_do1_fo2, 6765 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6766 }, 6767 { 6768 .path = lower_do1_fl3, 6769 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6770 }, 6771 { 6772 .path = upper_du1_fu2, 6773 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6774 }, 6775 { 6776 .path = upper_do1_fo2, 6777 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6778 }, 6779 { 6780 .path = upper_do1_fu3, 6781 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6782 }, 6783 { 6784 .path = merge_dl1_fl2, 6785 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6786 LANDLOCK_ACCESS_FS_WRITE_FILE, 6787 }, 6788 { 6789 .path = merge_du1_fu2, 6790 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6791 LANDLOCK_ACCESS_FS_WRITE_FILE, 6792 }, 6793 { 6794 .path = merge_do1_fo2, 6795 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6796 LANDLOCK_ACCESS_FS_WRITE_FILE, 6797 }, 6798 { 6799 .path = merge_do1_fl3, 6800 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6801 LANDLOCK_ACCESS_FS_WRITE_FILE, 6802 }, 6803 { 6804 .path = merge_do1_fu3, 6805 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6806 LANDLOCK_ACCESS_FS_WRITE_FILE, 6807 }, 6808 {}, 6809 }; 6810 const struct rule layer5_merge_only[] = { 6811 { 6812 .path = MERGE_DATA, 6813 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6814 LANDLOCK_ACCESS_FS_WRITE_FILE, 6815 }, 6816 {}, 6817 }; 6818 size_t i; 6819 const char *path_entry; 6820 6821 if (self->skip_test) 6822 SKIP(return, "overlayfs is not supported (test)"); 6823 6824 /* Sets rules on base directories (i.e. outside overlay scope). */ 6825 enforce_fs(_metadata, ACCESS_RW, layer1_base); 6826 6827 /* Checks lower layer. */ 6828 for_each_path(lower_base_files, path_entry, i) { 6829 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6830 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6831 } 6832 for_each_path(lower_base_directories, path_entry, i) { 6833 ASSERT_EQ(EACCES, 6834 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6835 } 6836 for_each_path(lower_sub_files, path_entry, i) { 6837 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6838 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6839 } 6840 /* Checks upper layer. */ 6841 for_each_path(upper_base_files, path_entry, i) { 6842 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6843 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6844 } 6845 for_each_path(upper_base_directories, path_entry, i) { 6846 ASSERT_EQ(EACCES, 6847 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6848 } 6849 for_each_path(upper_sub_files, path_entry, i) { 6850 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6851 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6852 } 6853 /* 6854 * Checks that access rights are independent from the lower and upper 6855 * layers: write access to upper files viewed through the merge point 6856 * is still allowed, and write access to lower file viewed (and copied) 6857 * through the merge point is still allowed. 6858 */ 6859 for_each_path(merge_base_files, path_entry, i) { 6860 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6861 } 6862 for_each_path(merge_base_directories, path_entry, i) { 6863 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6864 } 6865 for_each_path(merge_sub_files, path_entry, i) { 6866 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6867 } 6868 6869 /* Sets rules on data directories (i.e. inside overlay scope). */ 6870 enforce_fs(_metadata, ACCESS_RW, layer2_data); 6871 6872 /* Checks merge. */ 6873 for_each_path(merge_base_files, path_entry, i) { 6874 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6875 } 6876 for_each_path(merge_base_directories, path_entry, i) { 6877 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6878 } 6879 for_each_path(merge_sub_files, path_entry, i) { 6880 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6881 } 6882 6883 /* Same checks with tighter rules. */ 6884 enforce_fs(_metadata, ACCESS_RW, layer3_subdirs); 6885 6886 /* Checks changes for lower layer. */ 6887 for_each_path(lower_base_files, path_entry, i) { 6888 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6889 } 6890 /* Checks changes for upper layer. */ 6891 for_each_path(upper_base_files, path_entry, i) { 6892 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6893 } 6894 /* Checks all merge accesses. */ 6895 for_each_path(merge_base_files, path_entry, i) { 6896 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6897 } 6898 for_each_path(merge_base_directories, path_entry, i) { 6899 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6900 } 6901 for_each_path(merge_sub_files, path_entry, i) { 6902 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6903 } 6904 6905 /* Sets rules directly on overlayed files. */ 6906 enforce_fs(_metadata, ACCESS_RW, layer4_files); 6907 6908 /* Checks unchanged accesses on lower layer. */ 6909 for_each_path(lower_sub_files, path_entry, i) { 6910 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6911 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6912 } 6913 /* Checks unchanged accesses on upper layer. */ 6914 for_each_path(upper_sub_files, path_entry, i) { 6915 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6916 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6917 } 6918 /* Checks all merge accesses. */ 6919 for_each_path(merge_base_files, path_entry, i) { 6920 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6921 } 6922 for_each_path(merge_base_directories, path_entry, i) { 6923 ASSERT_EQ(EACCES, 6924 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6925 } 6926 for_each_path(merge_sub_files, path_entry, i) { 6927 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6928 } 6929 6930 /* Only allowes access to the merge hierarchy. */ 6931 enforce_fs(_metadata, ACCESS_RW, layer5_merge_only); 6932 6933 /* Checks new accesses on lower layer. */ 6934 for_each_path(lower_sub_files, path_entry, i) { 6935 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6936 } 6937 /* Checks new accesses on upper layer. */ 6938 for_each_path(upper_sub_files, path_entry, i) { 6939 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6940 } 6941 /* Checks all merge accesses. */ 6942 for_each_path(merge_base_files, path_entry, i) { 6943 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6944 } 6945 for_each_path(merge_base_directories, path_entry, i) { 6946 ASSERT_EQ(EACCES, 6947 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6948 } 6949 for_each_path(merge_sub_files, path_entry, i) { 6950 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6951 } 6952 } 6953 6954 FIXTURE(layout3_fs) 6955 { 6956 bool has_created_dir; 6957 bool has_created_file; 6958 bool skip_test; 6959 }; 6960 6961 FIXTURE_VARIANT(layout3_fs) 6962 { 6963 const struct mnt_opt mnt; 6964 const char *const file_path; 6965 unsigned int cwd_fs_magic; 6966 }; 6967 6968 /* clang-format off */ 6969 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 6970 /* clang-format on */ 6971 .mnt = { 6972 .type = "tmpfs", 6973 .data = MNT_TMP_DATA, 6974 }, 6975 .file_path = file1_s1d1, 6976 }; 6977 6978 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 6979 .mnt = { 6980 .type = "ramfs", 6981 .data = "mode=700", 6982 }, 6983 .file_path = TMP_DIR "/dir/file", 6984 }; 6985 6986 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 6987 .mnt = { 6988 .type = "cgroup2", 6989 }, 6990 .file_path = TMP_DIR "/test/cgroup.procs", 6991 }; 6992 6993 FIXTURE_VARIANT_ADD(layout3_fs, proc) { 6994 .mnt = { 6995 .type = "proc", 6996 }, 6997 .file_path = TMP_DIR "/self/status", 6998 }; 6999 7000 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 7001 .mnt = { 7002 .type = "sysfs", 7003 }, 7004 .file_path = TMP_DIR "/kernel/notes", 7005 }; 7006 7007 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 7008 .mnt = { 7009 .source = TMP_DIR, 7010 .flags = MS_BIND, 7011 }, 7012 .file_path = TMP_DIR "/dir/file", 7013 .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 7014 }; 7015 7016 static char *dirname_alloc(const char *path) 7017 { 7018 char *dup; 7019 7020 if (!path) 7021 return NULL; 7022 7023 dup = strdup(path); 7024 if (!dup) 7025 return NULL; 7026 7027 return dirname(dup); 7028 } 7029 7030 FIXTURE_SETUP(layout3_fs) 7031 { 7032 struct stat statbuf; 7033 char *dir_path = dirname_alloc(variant->file_path); 7034 7035 if (!supports_filesystem(variant->mnt.type) || 7036 !cwd_matches_fs(variant->cwd_fs_magic)) { 7037 self->skip_test = true; 7038 SKIP(return, "this filesystem is not supported (setup)"); 7039 } 7040 7041 prepare_layout_opt(_metadata, &variant->mnt); 7042 7043 /* Creates directory when required. */ 7044 if (stat(dir_path, &statbuf)) { 7045 set_cap(_metadata, CAP_DAC_OVERRIDE); 7046 EXPECT_EQ(0, mkdir(dir_path, 0700)) 7047 { 7048 TH_LOG("Failed to create directory \"%s\": %s", 7049 dir_path, strerror(errno)); 7050 } 7051 self->has_created_dir = true; 7052 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7053 } 7054 7055 /* Creates file when required. */ 7056 if (stat(variant->file_path, &statbuf)) { 7057 int fd; 7058 7059 set_cap(_metadata, CAP_DAC_OVERRIDE); 7060 fd = creat(variant->file_path, 0600); 7061 EXPECT_LE(0, fd) 7062 { 7063 TH_LOG("Failed to create file \"%s\": %s", 7064 variant->file_path, strerror(errno)); 7065 } 7066 EXPECT_EQ(0, close(fd)); 7067 self->has_created_file = true; 7068 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7069 } 7070 7071 free(dir_path); 7072 } 7073 7074 FIXTURE_TEARDOWN_PARENT(layout3_fs) 7075 { 7076 if (self->skip_test) 7077 SKIP(return, "this filesystem is not supported (teardown)"); 7078 7079 if (self->has_created_file) { 7080 set_cap(_metadata, CAP_DAC_OVERRIDE); 7081 /* 7082 * Don't check for error because the file might already 7083 * have been removed (cf. release_inode test). 7084 */ 7085 unlink(variant->file_path); 7086 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7087 } 7088 7089 if (self->has_created_dir) { 7090 char *dir_path = dirname_alloc(variant->file_path); 7091 7092 set_cap(_metadata, CAP_DAC_OVERRIDE); 7093 /* 7094 * Don't check for error because the directory might already 7095 * have been removed (cf. release_inode test). 7096 */ 7097 rmdir(dir_path); 7098 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7099 free(dir_path); 7100 } 7101 7102 cleanup_layout(_metadata); 7103 } 7104 7105 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 7106 FIXTURE_DATA(layout3_fs) * self, 7107 const FIXTURE_VARIANT(layout3_fs) * variant, 7108 const char *const rule_path) 7109 { 7110 const struct rule layer1_allow_read_file[] = { 7111 { 7112 .path = rule_path, 7113 .access = LANDLOCK_ACCESS_FS_READ_FILE, 7114 }, 7115 {}, 7116 }; 7117 const char *const dev_null_path = "/dev/null"; 7118 7119 if (self->skip_test) 7120 SKIP(return, "this filesystem is not supported (test)"); 7121 7122 /* Checks without Landlock. */ 7123 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7124 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7125 7126 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 7127 layer1_allow_read_file); 7128 7129 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7130 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7131 7132 /* Forbids directory reading. */ 7133 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, NULL); 7134 7135 /* Checks with Landlock and forbidden access. */ 7136 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7137 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7138 } 7139 7140 /* Matrix of tests to check file hierarchy evaluation. */ 7141 7142 TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 7143 { 7144 /* The current directory must not be the root for this test. */ 7145 layer3_fs_tag_inode(_metadata, self, variant, "."); 7146 } 7147 7148 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 7149 { 7150 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 7151 } 7152 7153 TEST_F_FORK(layout3_fs, tag_inode_dir_child) 7154 { 7155 char *dir_path = dirname_alloc(variant->file_path); 7156 7157 layer3_fs_tag_inode(_metadata, self, variant, dir_path); 7158 free(dir_path); 7159 } 7160 7161 TEST_F_FORK(layout3_fs, tag_inode_file) 7162 { 7163 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 7164 } 7165 7166 /* Light version of layout1.release_inodes */ 7167 TEST_F_FORK(layout3_fs, release_inodes) 7168 { 7169 const struct rule layer1[] = { 7170 { 7171 .path = TMP_DIR, 7172 .access = LANDLOCK_ACCESS_FS_READ_DIR, 7173 }, 7174 {}, 7175 }; 7176 int ruleset_fd; 7177 7178 if (self->skip_test) 7179 SKIP(return, "this filesystem is not supported (test)"); 7180 7181 /* Clean up for the teardown to not fail. */ 7182 if (self->has_created_file) 7183 EXPECT_EQ(0, remove_path(variant->file_path)); 7184 7185 if (self->has_created_dir) { 7186 char *dir_path = dirname_alloc(variant->file_path); 7187 7188 /* Don't check for error because of cgroup specificities. */ 7189 remove_path(dir_path); 7190 free(dir_path); 7191 } 7192 7193 ruleset_fd = 7194 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 7195 7196 /* Unmount the filesystem while it is being used by a ruleset. */ 7197 set_cap(_metadata, CAP_SYS_ADMIN); 7198 ASSERT_EQ(0, umount(TMP_DIR)); 7199 clear_cap(_metadata, CAP_SYS_ADMIN); 7200 7201 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 7202 set_cap(_metadata, CAP_SYS_ADMIN); 7203 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 7204 clear_cap(_metadata, CAP_SYS_ADMIN); 7205 7206 enforce_ruleset(_metadata, ruleset_fd); 7207 ASSERT_EQ(0, close(ruleset_fd)); 7208 7209 /* Checks that access to the new mount point is denied. */ 7210 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 7211 } 7212 7213 static int matches_log_fs_extra(struct __test_metadata *const _metadata, 7214 int audit_fd, const char *const blockers, 7215 const char *const path, const char *const extra) 7216 { 7217 static const char log_template[] = REGEX_LANDLOCK_PREFIX 7218 " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$"; 7219 char *absolute_path = NULL; 7220 size_t log_match_remaining = sizeof(log_template) + strlen(blockers) + 7221 PATH_MAX * 2 + 7222 (extra ? strlen(extra) : 0) + 1; 7223 char log_match[log_match_remaining]; 7224 char *log_match_cursor = log_match; 7225 size_t chunk_len; 7226 7227 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7228 REGEX_LANDLOCK_PREFIX " blockers=%s path=\"", 7229 blockers); 7230 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7231 return -E2BIG; 7232 7233 /* 7234 * It is assumed that absolute_path does not contain control 7235 * characters nor spaces, see audit_string_contains_control(). 7236 */ 7237 absolute_path = realpath(path, NULL); 7238 if (!absolute_path) 7239 return -errno; 7240 7241 log_match_remaining -= chunk_len; 7242 log_match_cursor += chunk_len; 7243 log_match_cursor = regex_escape(absolute_path, log_match_cursor, 7244 log_match_remaining); 7245 free(absolute_path); 7246 if (log_match_cursor < 0) 7247 return (long long)log_match_cursor; 7248 7249 log_match_remaining -= log_match_cursor - log_match; 7250 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7251 "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$", 7252 extra ?: ""); 7253 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7254 return -E2BIG; 7255 7256 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, 7257 NULL); 7258 } 7259 7260 static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd, 7261 const char *const blockers, const char *const path) 7262 { 7263 return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL); 7264 } 7265 7266 FIXTURE(audit_layout1) 7267 { 7268 struct audit_filter audit_filter; 7269 int audit_fd; 7270 }; 7271 7272 FIXTURE_SETUP(audit_layout1) 7273 { 7274 prepare_layout(_metadata); 7275 7276 create_layout1(_metadata); 7277 7278 set_cap(_metadata, CAP_AUDIT_CONTROL); 7279 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 7280 EXPECT_LE(0, self->audit_fd); 7281 disable_caps(_metadata); 7282 } 7283 7284 FIXTURE_TEARDOWN_PARENT(audit_layout1) 7285 { 7286 remove_layout1(_metadata); 7287 7288 cleanup_layout(_metadata); 7289 7290 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 7291 } 7292 7293 TEST_F(audit_layout1, execute_make) 7294 { 7295 struct audit_records records; 7296 7297 copy_file(_metadata, bin_true, file1_s1d1); 7298 test_execute(_metadata, 0, file1_s1d1); 7299 test_check_exec(_metadata, 0, file1_s1d1); 7300 7301 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL); 7302 7303 test_execute(_metadata, EACCES, file1_s1d1); 7304 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7305 file1_s1d1)); 7306 test_check_exec(_metadata, EACCES, file1_s1d1); 7307 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7308 file1_s1d1)); 7309 7310 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7311 EXPECT_EQ(0, records.access); 7312 EXPECT_EQ(0, records.domain); 7313 } 7314 7315 /* 7316 * Using a set of handled/denied access rights make it possible to check that 7317 * only the blocked ones are logged. 7318 */ 7319 7320 TEST_F(audit_layout1, execute_read) 7321 { 7322 struct audit_records records; 7323 7324 copy_file(_metadata, bin_true, file1_s1d1); 7325 test_execute(_metadata, 0, file1_s1d1); 7326 test_check_exec(_metadata, 0, file1_s1d1); 7327 7328 enforce_fs(_metadata, ACCESS_ALL, NULL); 7329 7330 /* 7331 * The only difference with the previous audit_layout1.execute_read test is 7332 * the extra ",fs\\.read_file" blocked by the executable file. 7333 */ 7334 test_execute(_metadata, EACCES, file1_s1d1); 7335 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7336 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7337 test_check_exec(_metadata, EACCES, file1_s1d1); 7338 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7339 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7340 7341 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7342 EXPECT_EQ(0, records.access); 7343 EXPECT_EQ(0, records.domain); 7344 } 7345 7346 TEST_F(audit_layout1, write_file) 7347 { 7348 struct audit_records records; 7349 7350 enforce_fs(_metadata, ACCESS_ALL, NULL); 7351 7352 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 7353 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7354 "fs\\.write_file", file1_s1d1)); 7355 7356 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7357 EXPECT_EQ(0, records.access); 7358 EXPECT_EQ(1, records.domain); 7359 } 7360 7361 TEST_F(audit_layout1, read_file) 7362 { 7363 struct audit_records records; 7364 7365 enforce_fs(_metadata, ACCESS_ALL, NULL); 7366 7367 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 7368 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file", 7369 file1_s1d1)); 7370 7371 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7372 EXPECT_EQ(0, records.access); 7373 EXPECT_EQ(1, records.domain); 7374 } 7375 7376 TEST_F(audit_layout1, read_dir) 7377 { 7378 struct audit_records records; 7379 7380 enforce_fs(_metadata, ACCESS_ALL, NULL); 7381 7382 EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY)); 7383 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir", 7384 dir_s1d1)); 7385 7386 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7387 EXPECT_EQ(0, records.access); 7388 EXPECT_EQ(1, records.domain); 7389 } 7390 7391 TEST_F(audit_layout1, remove_dir) 7392 { 7393 struct audit_records records; 7394 7395 EXPECT_EQ(0, unlink(file1_s1d3)); 7396 EXPECT_EQ(0, unlink(file2_s1d3)); 7397 7398 enforce_fs(_metadata, ACCESS_ALL, NULL); 7399 7400 EXPECT_EQ(-1, rmdir(dir_s1d3)); 7401 EXPECT_EQ(EACCES, errno); 7402 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7403 "fs\\.remove_dir", dir_s1d2)); 7404 7405 EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 7406 EXPECT_EQ(EACCES, errno); 7407 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7408 "fs\\.remove_dir", dir_s1d2)); 7409 7410 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7411 EXPECT_EQ(0, records.access); 7412 EXPECT_EQ(0, records.domain); 7413 } 7414 7415 TEST_F(audit_layout1, remove_file) 7416 { 7417 struct audit_records records; 7418 7419 enforce_fs(_metadata, ACCESS_ALL, NULL); 7420 7421 EXPECT_EQ(-1, unlink(file1_s1d3)); 7422 EXPECT_EQ(EACCES, errno); 7423 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7424 "fs\\.remove_file", dir_s1d3)); 7425 7426 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7427 EXPECT_EQ(0, records.access); 7428 EXPECT_EQ(1, records.domain); 7429 } 7430 7431 TEST_F(audit_layout1, make_char) 7432 { 7433 struct audit_records records; 7434 7435 EXPECT_EQ(0, unlink(file1_s1d3)); 7436 7437 enforce_fs(_metadata, ACCESS_ALL, NULL); 7438 7439 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, 0)); 7440 EXPECT_EQ(EACCES, errno); 7441 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char", 7442 dir_s1d3)); 7443 7444 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7445 EXPECT_EQ(0, records.access); 7446 EXPECT_EQ(1, records.domain); 7447 } 7448 7449 TEST_F(audit_layout1, make_dir) 7450 { 7451 struct audit_records records; 7452 7453 EXPECT_EQ(0, unlink(file1_s1d3)); 7454 7455 enforce_fs(_metadata, ACCESS_ALL, NULL); 7456 7457 EXPECT_EQ(-1, mkdir(file1_s1d3, 0755)); 7458 EXPECT_EQ(EACCES, errno); 7459 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir", 7460 dir_s1d3)); 7461 7462 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7463 EXPECT_EQ(0, records.access); 7464 EXPECT_EQ(1, records.domain); 7465 } 7466 7467 TEST_F(audit_layout1, make_reg) 7468 { 7469 struct audit_records records; 7470 7471 EXPECT_EQ(0, unlink(file1_s1d3)); 7472 7473 enforce_fs(_metadata, ACCESS_ALL, NULL); 7474 7475 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFREG | 0644, 0)); 7476 EXPECT_EQ(EACCES, errno); 7477 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg", 7478 dir_s1d3)); 7479 7480 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7481 EXPECT_EQ(0, records.access); 7482 EXPECT_EQ(1, records.domain); 7483 } 7484 7485 TEST_F(audit_layout1, make_sock) 7486 { 7487 struct audit_records records; 7488 7489 EXPECT_EQ(0, unlink(file1_s1d3)); 7490 7491 enforce_fs(_metadata, ACCESS_ALL, NULL); 7492 7493 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFSOCK | 0644, 0)); 7494 EXPECT_EQ(EACCES, errno); 7495 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock", 7496 dir_s1d3)); 7497 7498 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7499 EXPECT_EQ(0, records.access); 7500 EXPECT_EQ(1, records.domain); 7501 } 7502 7503 TEST_F(audit_layout1, make_fifo) 7504 { 7505 struct audit_records records; 7506 7507 EXPECT_EQ(0, unlink(file1_s1d3)); 7508 7509 enforce_fs(_metadata, ACCESS_ALL, NULL); 7510 7511 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFIFO | 0644, 0)); 7512 EXPECT_EQ(EACCES, errno); 7513 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo", 7514 dir_s1d3)); 7515 7516 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7517 EXPECT_EQ(0, records.access); 7518 EXPECT_EQ(1, records.domain); 7519 } 7520 7521 TEST_F(audit_layout1, make_block) 7522 { 7523 struct audit_records records; 7524 7525 EXPECT_EQ(0, unlink(file1_s1d3)); 7526 7527 enforce_fs(_metadata, ACCESS_ALL, NULL); 7528 7529 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0)); 7530 EXPECT_EQ(EACCES, errno); 7531 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7532 "fs\\.make_block", dir_s1d3)); 7533 7534 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7535 EXPECT_EQ(0, records.access); 7536 EXPECT_EQ(1, records.domain); 7537 } 7538 7539 TEST_F(audit_layout1, make_sym) 7540 { 7541 struct audit_records records; 7542 7543 EXPECT_EQ(0, unlink(file1_s1d3)); 7544 7545 enforce_fs(_metadata, ACCESS_ALL, NULL); 7546 7547 EXPECT_EQ(-1, symlink("target", file1_s1d3)); 7548 EXPECT_EQ(EACCES, errno); 7549 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym", 7550 dir_s1d3)); 7551 7552 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7553 EXPECT_EQ(0, records.access); 7554 EXPECT_EQ(1, records.domain); 7555 } 7556 7557 TEST_F(audit_layout1, refer_handled) 7558 { 7559 struct audit_records records; 7560 7561 EXPECT_EQ(0, unlink(file1_s1d3)); 7562 7563 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, NULL); 7564 7565 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7566 EXPECT_EQ(EXDEV, errno); 7567 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7568 dir_s1d1)); 7569 EXPECT_EQ(0, 7570 matches_log_domain_allocated(self->audit_fd, getpid(), NULL)); 7571 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7572 dir_s1d3)); 7573 7574 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7575 EXPECT_EQ(0, records.access); 7576 EXPECT_EQ(0, records.domain); 7577 } 7578 7579 TEST_F(audit_layout1, refer_make) 7580 { 7581 struct audit_records records; 7582 7583 EXPECT_EQ(0, unlink(file1_s1d3)); 7584 7585 enforce_fs(_metadata, 7586 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 7587 NULL); 7588 7589 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7590 EXPECT_EQ(EACCES, errno); 7591 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7592 dir_s1d1)); 7593 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7594 "fs\\.make_reg,fs\\.refer", dir_s1d3)); 7595 7596 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7597 EXPECT_EQ(0, records.access); 7598 EXPECT_EQ(0, records.domain); 7599 } 7600 7601 TEST_F(audit_layout1, refer_rename) 7602 { 7603 struct audit_records records; 7604 7605 EXPECT_EQ(0, unlink(file1_s1d3)); 7606 7607 enforce_fs(_metadata, ACCESS_ALL, NULL); 7608 7609 EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3)); 7610 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7611 "fs\\.remove_file,fs\\.refer", dir_s1d2)); 7612 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7613 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7614 dir_s2d3)); 7615 7616 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7617 EXPECT_EQ(0, records.access); 7618 EXPECT_EQ(0, records.domain); 7619 } 7620 7621 TEST_F(audit_layout1, refer_exchange) 7622 { 7623 struct audit_records records; 7624 7625 EXPECT_EQ(0, unlink(file1_s1d3)); 7626 7627 enforce_fs(_metadata, ACCESS_ALL, NULL); 7628 7629 /* 7630 * The only difference with the previous audit_layout1.refer_rename test is 7631 * the extra ",fs\\.make_reg" blocked by the source directory. 7632 */ 7633 EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3)); 7634 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7635 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7636 dir_s1d2)); 7637 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7638 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7639 dir_s2d3)); 7640 7641 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7642 EXPECT_EQ(0, records.access); 7643 EXPECT_EQ(0, records.domain); 7644 } 7645 7646 /* 7647 * This test checks that the audit record is correctly generated when the 7648 * operation is only partially denied. This is the case for rename(2) when the 7649 * source file is allowed to be referenced but the destination directory is not. 7650 * 7651 * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize 7652 * file path walks and prepare for audit support") and commit 058518c20920 7653 * ("landlock: Align partial refer access checks with final ones"). 7654 */ 7655 TEST_F(audit_layout1, refer_rename_half) 7656 { 7657 struct audit_records records; 7658 const struct rule layer1[] = { 7659 { 7660 .path = dir_s2d2, 7661 .access = LANDLOCK_ACCESS_FS_REFER, 7662 }, 7663 {}, 7664 }; 7665 7666 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 7667 7668 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 7669 ASSERT_EQ(EXDEV, errno); 7670 7671 /* Only half of the request is denied. */ 7672 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7673 dir_s1d1)); 7674 7675 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7676 EXPECT_EQ(0, records.access); 7677 EXPECT_EQ(1, records.domain); 7678 } 7679 7680 TEST_F(audit_layout1, truncate) 7681 { 7682 struct audit_records records; 7683 7684 enforce_fs(_metadata, ACCESS_ALL, NULL); 7685 7686 EXPECT_EQ(-1, truncate(file1_s1d3, 0)); 7687 EXPECT_EQ(EACCES, errno); 7688 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate", 7689 file1_s1d3)); 7690 7691 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7692 EXPECT_EQ(0, records.access); 7693 EXPECT_EQ(1, records.domain); 7694 } 7695 7696 TEST_F(audit_layout1, ioctl_dev) 7697 { 7698 struct audit_records records; 7699 int fd; 7700 7701 enforce_fs(_metadata, ACCESS_ALL & ~LANDLOCK_ACCESS_FS_READ_FILE, NULL); 7702 7703 fd = open("/dev/null", O_RDONLY | O_CLOEXEC); 7704 ASSERT_LE(0, fd); 7705 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 7706 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7707 "fs\\.ioctl_dev", "/dev/null", 7708 " ioctlcmd=0x541b")); 7709 7710 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7711 EXPECT_EQ(0, records.access); 7712 EXPECT_EQ(1, records.domain); 7713 } 7714 7715 TEST_F(audit_layout1, resolve_unix) 7716 { 7717 struct audit_records records; 7718 const char *const path = "sock"; 7719 int srv_fd, cli_fd, status; 7720 pid_t child_pid; 7721 7722 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path); 7723 7724 child_pid = fork(); 7725 ASSERT_LE(0, child_pid); 7726 if (!child_pid) { 7727 enforce_fs(_metadata, ACCESS_ALL, NULL); 7728 7729 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 7730 ASSERT_LE(0, cli_fd); 7731 EXPECT_EQ(EACCES, 7732 test_connect_named_unix(_metadata, cli_fd, path)); 7733 7734 EXPECT_EQ(0, close(cli_fd)); 7735 _exit(_metadata->exit_code); 7736 } 7737 7738 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 7739 EXPECT_EQ(1, WIFEXITED(status)); 7740 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 7741 7742 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7743 "fs\\.resolve_unix", path, NULL)); 7744 7745 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7746 EXPECT_EQ(0, records.access); 7747 EXPECT_EQ(1, records.domain); 7748 7749 EXPECT_EQ(0, close(srv_fd)); 7750 } 7751 7752 TEST_F(audit_layout1, mount) 7753 { 7754 struct audit_records records; 7755 7756 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL); 7757 7758 set_cap(_metadata, CAP_SYS_ADMIN); 7759 EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 7760 EXPECT_EQ(EPERM, errno); 7761 clear_cap(_metadata, CAP_SYS_ADMIN); 7762 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7763 "fs\\.change_topology", dir_s3d2)); 7764 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7765 EXPECT_EQ(0, records.access); 7766 EXPECT_EQ(1, records.domain); 7767 } 7768 7769 static bool debug_quiet_tests; 7770 7771 FIXTURE(audit_quiet_layout1) 7772 { 7773 struct audit_filter audit_filter; 7774 int audit_fd; 7775 }; 7776 7777 FIXTURE_SETUP(audit_quiet_layout1) 7778 { 7779 prepare_layout(_metadata); 7780 create_layout1(_metadata); 7781 7782 set_cap(_metadata, CAP_AUDIT_CONTROL); 7783 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 7784 EXPECT_LE(0, self->audit_fd); 7785 clear_cap(_metadata, CAP_AUDIT_CONTROL); 7786 7787 if (getenv("DEBUG_QUIET_TESTS")) 7788 debug_quiet_tests = true; 7789 } 7790 7791 FIXTURE_TEARDOWN_PARENT(audit_quiet_layout1) 7792 { 7793 remove_layout1(_metadata); 7794 cleanup_layout(_metadata); 7795 7796 set_cap(_metadata, CAP_AUDIT_CONTROL); 7797 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 7798 clear_cap(_metadata, CAP_AUDIT_CONTROL); 7799 } 7800 7801 struct a_rule { 7802 const char *path; 7803 __u64 access; 7804 bool quiet; 7805 }; 7806 7807 struct a_layer { 7808 __u64 handled_access_fs; 7809 __u64 quiet_access_fs; 7810 struct a_rule rules[6]; 7811 __u64 restrict_flags; 7812 }; 7813 7814 struct a_target { 7815 /* File/dir to try open. */ 7816 const char *target; 7817 /* Open mode (one of O_RDONLY, O_WRONLY, or O_RDWR). */ 7818 int open_mode; 7819 /* Should open succeed? */ 7820 bool expect_open_success; 7821 /* If open fails, whether to expect an audit log for read. */ 7822 bool audit_read_blocked; 7823 /* If open fails, whether to expect an audit log for write. */ 7824 bool audit_write_blocked; 7825 /* If ftruncate() is expected to be allowed. */ 7826 bool expect_truncate_success; 7827 /* If ftruncate fails, whether to expect an audit log. */ 7828 bool audit_truncate; 7829 /* 7830 * If ioctl() is expected to be allowed (ioctl not attempted if neither 7831 * this nor expect_ioctl_denied is set). 7832 */ 7833 bool expect_ioctl_allowed; 7834 /* If ioctl() is expected to be denied. */ 7835 bool expect_ioctl_denied; 7836 /* If ioctl fails, whether to expect an audit log. */ 7837 bool audit_ioctl; 7838 }; 7839 7840 #define AUDIT_QUIET_MAX_TARGETS 10 7841 7842 FIXTURE_VARIANT(audit_quiet_layout1) 7843 { 7844 struct a_layer layers[3]; 7845 struct a_target targets[AUDIT_QUIET_MAX_TARGETS]; 7846 }; 7847 7848 #define FS_R LANDLOCK_ACCESS_FS_READ_FILE 7849 #define FS_W LANDLOCK_ACCESS_FS_WRITE_FILE 7850 #define FS_TRUNC LANDLOCK_ACCESS_FS_TRUNCATE 7851 #define FS_IOCTL LANDLOCK_ACCESS_FS_IOCTL_DEV 7852 7853 static int sprint_access_bits(char *buf, size_t buflen, __u64 access) 7854 { 7855 size_t offset = 0; 7856 7857 if (buflen < strlen("rwti make_reg remove_file refer") + 1) 7858 abort(); 7859 7860 buf[0] = '\0'; 7861 if (access & FS_R) 7862 offset += snprintf(buf + offset, buflen - offset, "r"); 7863 if (access & FS_W) 7864 offset += snprintf(buf + offset, buflen - offset, "w"); 7865 if (access & FS_TRUNC) 7866 offset += snprintf(buf + offset, buflen - offset, "t"); 7867 if (access & FS_IOCTL) 7868 offset += snprintf(buf + offset, buflen - offset, "i"); 7869 if (access & LANDLOCK_ACCESS_FS_MAKE_REG) 7870 offset += snprintf(buf + offset, buflen - offset, ",make_reg"); 7871 if (access & LANDLOCK_ACCESS_FS_REMOVE_FILE) 7872 offset += 7873 snprintf(buf + offset, buflen - offset, ",remove_file"); 7874 if (access & LANDLOCK_ACCESS_FS_REFER) 7875 offset += snprintf(buf + offset, buflen - offset, ",refer"); 7876 7877 if (buf[0] == ',') { 7878 offset--; 7879 memmove(buf, buf + 1, offset); 7880 buf[offset] = '\0'; 7881 } 7882 7883 return offset; 7884 } 7885 7886 static int apply_a_layer(struct __test_metadata *const _metadata, 7887 const struct a_layer *l) 7888 { 7889 struct landlock_ruleset_attr rs_attr = { 7890 .handled_access_fs = l->handled_access_fs, 7891 .quiet_access_fs = l->quiet_access_fs, 7892 }; 7893 int rs_fd; 7894 int i; 7895 const struct a_rule *r; 7896 char handled_access_s[33], quiet_access_s[33], rule_access_s[33]; 7897 7898 if (!l->handled_access_fs) 7899 return 0; 7900 7901 rs_fd = landlock_create_ruleset(&rs_attr, sizeof(rs_attr), 0); 7902 ASSERT_LE(0, rs_fd); 7903 7904 for (i = 0; i < ARRAY_SIZE(l->rules); i++) { 7905 r = &l->rules[i]; 7906 if (!r->path) 7907 continue; 7908 7909 add_path_beneath(_metadata, rs_fd, r->access, r->path, 7910 r->quiet ? LANDLOCK_ADD_RULE_QUIET : 0); 7911 } 7912 7913 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 7914 ASSERT_EQ(0, landlock_restrict_self(rs_fd, l->restrict_flags)) 7915 { 7916 TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 7917 } 7918 ASSERT_EQ(0, close(rs_fd)); 7919 7920 if (debug_quiet_tests) { 7921 sprint_access_bits(handled_access_s, sizeof(handled_access_s), 7922 l->handled_access_fs); 7923 sprint_access_bits(quiet_access_s, sizeof(quiet_access_s), 7924 l->quiet_access_fs); 7925 TH_LOG("applied layer: handled=%s quiet=%s restrict_flags=0x%llx", 7926 handled_access_s, quiet_access_s, 7927 (unsigned long long)l->restrict_flags); 7928 for (i = 0; i < ARRAY_SIZE(l->rules); i++) { 7929 r = &l->rules[i]; 7930 if (!r->path) 7931 continue; 7932 7933 sprint_access_bits(rule_access_s, sizeof(rule_access_s), 7934 r->access); 7935 TH_LOG(" rule[%d]: path=%s access=%s quiet=%d", i, 7936 r->path, rule_access_s, r->quiet); 7937 } 7938 } 7939 return 0; 7940 } 7941 7942 void audit_quiet_layout1_test_body(struct __test_metadata *const _metadata, 7943 FIXTURE_DATA(audit_quiet_layout1) * self, 7944 const struct a_target *targets) 7945 { 7946 struct audit_records records = {}; 7947 int i; 7948 const struct a_target *target; 7949 int fd = -1; 7950 int open_mode; 7951 int ret; 7952 bool expect_audit; 7953 const char *blocker; 7954 7955 for (i = 0; i < AUDIT_QUIET_MAX_TARGETS; i++) { 7956 target = &targets[i]; 7957 if (!target->target) 7958 continue; 7959 7960 open_mode = target->open_mode & (O_RDONLY | O_WRONLY | O_RDWR); 7961 7962 EXPECT_TRUE(open_mode == O_RDONLY || open_mode == O_WRONLY || 7963 open_mode == O_RDWR); 7964 7965 if (target->expect_open_success) { 7966 EXPECT_FALSE(target->audit_read_blocked); 7967 EXPECT_FALSE(target->audit_write_blocked); 7968 } 7969 if (target->expect_truncate_success) 7970 EXPECT_TRUE(target->expect_open_success && 7971 !target->audit_truncate); 7972 7973 if (debug_quiet_tests) 7974 TH_LOG("Try open \"%s\" with %s%s", target->target, 7975 open_mode != O_WRONLY ? "r" : "", 7976 open_mode != O_RDONLY ? "w" : ""); 7977 7978 fd = openat(AT_FDCWD, target->target, open_mode | O_CLOEXEC); 7979 if (target->expect_open_success) { 7980 ASSERT_LE(0, fd) 7981 { 7982 TH_LOG("Failed to open \"%s\": %s", 7983 target->target, strerror(errno)); 7984 }; 7985 } else { 7986 ASSERT_EQ(-1, fd); 7987 ASSERT_EQ(EACCES, errno); 7988 } 7989 7990 expect_audit = true; 7991 7992 if (target->audit_read_blocked && target->audit_write_blocked) 7993 blocker = "fs\\.write_file,fs\\.read_file"; 7994 else if (target->audit_read_blocked) 7995 blocker = "fs\\.read_file"; 7996 else if (target->audit_write_blocked) 7997 blocker = "fs\\.write_file"; 7998 else 7999 expect_audit = false; 8000 8001 if (expect_audit) 8002 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 8003 blocker, target->target)); 8004 8005 /* Check that we see no (other) logs. */ 8006 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 8007 ASSERT_EQ(0, records.access); 8008 8009 if (target->expect_open_success && fd >= 0) { 8010 if (debug_quiet_tests) 8011 TH_LOG("Try ftruncate \"%s\"", target->target); 8012 8013 ret = ftruncate(fd, 0); 8014 if (target->expect_truncate_success) { 8015 ASSERT_EQ(0, ret); 8016 } else { 8017 ASSERT_EQ(-1, ret); 8018 if (open_mode != O_RDONLY) 8019 ASSERT_EQ(EACCES, errno); 8020 } 8021 8022 if (target->audit_truncate) 8023 ASSERT_EQ(0, matches_log_fs(_metadata, 8024 self->audit_fd, 8025 "fs\\.truncate", 8026 target->target)); 8027 8028 if (target->expect_ioctl_allowed || 8029 target->expect_ioctl_denied) { 8030 if (debug_quiet_tests) 8031 TH_LOG("Try ioctl FIONREAD on \"%s\"", 8032 target->target); 8033 8034 ret = ioctl_error(_metadata, fd, FIONREAD); 8035 if (target->expect_ioctl_allowed) { 8036 ASSERT_NE(EACCES, ret); 8037 } else { 8038 ASSERT_EQ(EACCES, ret); 8039 } 8040 } 8041 8042 if (target->audit_ioctl) 8043 ASSERT_EQ(0, matches_log_fs_extra( 8044 _metadata, self->audit_fd, 8045 "fs\\.ioctl_dev", 8046 target->target, 8047 " ioctlcmd=0x541b\\+")); 8048 8049 /* Check that we see no other logs. */ 8050 EXPECT_EQ(0, audit_count_records(self->audit_fd, 8051 &records)); 8052 ASSERT_EQ(0, records.access); 8053 ASSERT_EQ(0, close(fd)); 8054 } 8055 } 8056 } 8057 8058 TEST_F(audit_quiet_layout1, base) 8059 { 8060 int i; 8061 8062 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 8063 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 8064 8065 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 8066 } 8067 8068 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_simple) { 8069 .layers = { 8070 { 8071 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8072 .quiet_access_fs = FS_R, 8073 .rules = { 8074 { .path = dir_s1d1, .access = 0, .quiet = true }, 8075 }, 8076 }, 8077 }, 8078 .targets = { 8079 { 8080 .target = file1_s1d1, 8081 .open_mode = O_RDONLY, 8082 }, 8083 /* Not covered by quiet */ 8084 { 8085 .target = file1_s2d1, 8086 .open_mode = O_RDONLY, 8087 .audit_read_blocked = true, 8088 }, 8089 /* Access not quieted */ 8090 { 8091 .target = file1_s1d1, 8092 .open_mode = O_WRONLY, 8093 .audit_write_blocked = true, 8094 }, 8095 /* 8096 * Quiet flag only takes effect if all blocked access bits are 8097 * quieted, otherwise audit log emitted as normal (with all 8098 * blockers) 8099 */ 8100 { 8101 .target = file1_s1d1, 8102 .open_mode = O_RDWR, 8103 .audit_read_blocked = true, 8104 .audit_write_blocked = true, 8105 }, 8106 }, 8107 }; 8108 8109 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_read) { 8110 .layers = { 8111 { 8112 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8113 .quiet_access_fs = FS_W, 8114 .rules = { 8115 { .path = dir_s1d1, .access = FS_R, .quiet = true }, 8116 /* Quiet flags inherit down and are not overridden */ 8117 { .path = file1_s1d1, .access = FS_R, .quiet = false }, 8118 { .path = file1_s2d3, .access = 0, .quiet = true }, 8119 }, 8120 }, 8121 }, 8122 .targets = { 8123 /* Read ok */ 8124 { 8125 .target = file1_s1d1, 8126 .open_mode = O_RDONLY, 8127 .expect_open_success = true, 8128 }, 8129 /* Write quieted */ 8130 { 8131 .target = file1_s1d1, 8132 .open_mode = O_WRONLY, 8133 }, 8134 /* Read allowed, write quieted so no audit */ 8135 { 8136 .target = file1_s1d1, 8137 .open_mode = O_RDWR, 8138 }, 8139 /* Not covered by quiet */ 8140 { 8141 .target = file1_s2d2, 8142 .open_mode = O_WRONLY, 8143 .audit_write_blocked = true, 8144 }, 8145 { 8146 .target = file1_s2d2, 8147 .open_mode = O_RDWR, 8148 .audit_read_blocked = true, 8149 .audit_write_blocked = true, 8150 }, 8151 /* Single file quiet */ 8152 { 8153 .target = file1_s2d3, 8154 .open_mode = O_WRONLY, 8155 }, 8156 /* Wrong file */ 8157 { 8158 .target = file2_s2d3, 8159 .open_mode = O_WRONLY, 8160 .audit_write_blocked = true, 8161 }, 8162 /* Access not quieted */ 8163 { 8164 .target = file1_s2d3, 8165 .open_mode = O_RDONLY, 8166 .audit_read_blocked = true, 8167 }, 8168 /* Some access not quieted */ 8169 { 8170 .target = file1_s2d3, 8171 .open_mode = O_RDWR, 8172 .audit_read_blocked = true, 8173 .audit_write_blocked = true, 8174 }, 8175 }, 8176 }; 8177 8178 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_write) { 8179 .layers = { 8180 { 8181 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8182 .quiet_access_fs = FS_R, 8183 .rules = { 8184 { .path = dir_s1d1, .access = FS_W, .quiet = true }, 8185 }, 8186 }, 8187 }, 8188 .targets = { 8189 /* Read quieted */ 8190 { 8191 .target = file1_s1d1, 8192 .open_mode = O_RDONLY, 8193 }, 8194 /* Truncate not quieted */ 8195 { 8196 .target = file1_s1d1, 8197 .open_mode = O_WRONLY, 8198 .expect_open_success = true, 8199 .audit_truncate = true, 8200 }, 8201 /* Not covered by quiet */ 8202 { 8203 .target = file1_s2d1, 8204 .open_mode = O_RDONLY, 8205 .audit_read_blocked = true, 8206 }, 8207 /* Write allowed, read quieted so no audit */ 8208 { 8209 .target = file1_s1d1, 8210 .open_mode = O_RDWR, 8211 }, 8212 }, 8213 }; 8214 8215 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_write_quiet_trunc) { 8216 .layers = { 8217 { 8218 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8219 .quiet_access_fs = FS_TRUNC, 8220 .rules = { 8221 { .path = dir_s1d1, .access = FS_W, .quiet = true }, 8222 { .path = dir_s2d1, .access = FS_W, .quiet = false }, 8223 }, 8224 }, 8225 }, 8226 .targets = { 8227 /* Read not allowed and not quieted */ 8228 { 8229 .target = file1_s1d1, 8230 .open_mode = O_RDONLY, 8231 .audit_read_blocked = true, 8232 }, 8233 /* Truncate quieted */ 8234 { 8235 .target = file1_s1d1, 8236 .open_mode = O_WRONLY, 8237 .expect_open_success = true, 8238 }, 8239 /* Not covered by quiet (truncate) */ 8240 { 8241 .target = file1_s2d1, 8242 .open_mode = O_WRONLY, 8243 .expect_open_success = true, 8244 .audit_truncate = true, 8245 }, 8246 /* Not covered by quiet (read/write) */ 8247 { 8248 .target = file1_s3d1, 8249 .open_mode = O_RDWR, 8250 .audit_read_blocked = true, 8251 .audit_write_blocked = true, 8252 }, 8253 }, 8254 }; 8255 8256 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_rw_quiet_trunc) { 8257 .layers = { 8258 { 8259 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8260 .quiet_access_fs = FS_TRUNC, 8261 .rules = { 8262 { .path = dir_s1d1, .access = FS_R | FS_W, .quiet = true }, 8263 { .path = dir_s2d1, .access = FS_R | FS_W, .quiet = false }, 8264 }, 8265 }, 8266 }, 8267 .targets = { 8268 { 8269 .target = file1_s1d1, 8270 .open_mode = O_RDWR, 8271 .expect_open_success = true, 8272 }, 8273 { 8274 .target = file1_s2d1, 8275 .open_mode = O_RDWR, 8276 .expect_open_success = true, 8277 .audit_truncate = true, 8278 }, 8279 }, 8280 }; 8281 8282 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_all) { 8283 .layers = { 8284 { 8285 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8286 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8287 .rules = { 8288 { .path = dir_s1d1, .access = 0, .quiet = true }, 8289 { .path = file1_s2d1, .access = FS_R | FS_W, .quiet = true }, 8290 { .path = file1_s2d3, .access = 0, .quiet = true }, 8291 { .path = dir_s3d1, .access = FS_W, .quiet = false }, 8292 { .path = "/dev/zero", .access = FS_R, .quiet = false }, 8293 { .path = "/dev/null", .access = FS_R, .quiet = true }, 8294 }, 8295 }, 8296 }, 8297 .targets = { 8298 /* No logs */ 8299 { 8300 .target = file1_s1d1, 8301 .open_mode = O_RDONLY, 8302 }, 8303 { 8304 .target = file1_s1d1, 8305 .open_mode = O_WRONLY, 8306 }, 8307 { 8308 .target = file1_s1d1, 8309 .open_mode = O_RDWR, 8310 }, 8311 /* Truncate quieted - no log */ 8312 { 8313 .target = file1_s2d1, 8314 .open_mode = O_RDWR, 8315 .expect_open_success = true, 8316 }, 8317 /* Truncate not covered by quiet */ 8318 { 8319 .target = file1_s3d1, 8320 .open_mode = O_WRONLY, 8321 .expect_open_success = true, 8322 .audit_truncate = true, 8323 }, 8324 /* Not covered by quiet */ 8325 { 8326 .target = file1_s3d1, 8327 .open_mode = O_RDONLY, 8328 .audit_read_blocked = true, 8329 }, 8330 /* Single file quiet */ 8331 { 8332 .target = file1_s2d3, 8333 .open_mode = O_RDWR, 8334 }, 8335 /* Wrong file */ 8336 { 8337 .target = file2_s2d3, 8338 .open_mode = O_RDWR, 8339 .audit_read_blocked = true, 8340 .audit_write_blocked = true, 8341 }, 8342 /* Ioctl quieted */ 8343 { 8344 .target = "/dev/null", 8345 .open_mode = O_RDONLY, 8346 .expect_open_success = true, 8347 .expect_ioctl_denied = true, 8348 }, 8349 /* Ioctl not quieted */ 8350 { 8351 .target = "/dev/zero", 8352 .open_mode = O_RDONLY, 8353 .expect_open_success = true, 8354 .expect_ioctl_denied = true, 8355 .audit_ioctl = true, 8356 }, 8357 }, 8358 }; 8359 8360 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_across_mountpoint) { 8361 .layers = { 8362 { 8363 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8364 .quiet_access_fs = FS_R, 8365 .rules = { 8366 { .path = dir_s3d1, .access = 0, .quiet = true }, 8367 }, 8368 }, 8369 }, 8370 .targets = { 8371 { 8372 .target = file1_s3d3, 8373 .open_mode = O_RDONLY, 8374 }, 8375 /* Not covered by quiet */ 8376 { 8377 .target = file1_s1d1, 8378 .open_mode = O_RDONLY, 8379 .audit_read_blocked = true, 8380 }, 8381 { 8382 .target = file1_s1d1, 8383 .open_mode = O_RDWR, 8384 .audit_read_blocked = true, 8385 .audit_write_blocked = true, 8386 }, 8387 /* Access not quieted */ 8388 { 8389 .target = file1_s3d3, 8390 .open_mode = O_WRONLY, 8391 .audit_write_blocked = true, 8392 }, 8393 }, 8394 }; 8395 8396 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_all_quiet) { 8397 .layers = { 8398 { 8399 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8400 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8401 .rules = { 8402 { 8403 .path = dir_s1d1, 8404 .access = FS_R | FS_W | FS_TRUNC, 8405 .quiet = true 8406 }, 8407 { 8408 .path = "/dev/null", 8409 .access = FS_R | FS_W | FS_IOCTL, 8410 .quiet = true 8411 }, 8412 }, 8413 }, 8414 }, 8415 .targets = { 8416 { 8417 .target = file1_s1d1, 8418 .open_mode = O_RDWR, 8419 .expect_open_success = true, 8420 .expect_truncate_success = true, 8421 }, 8422 { 8423 .target = "/dev/null", 8424 .open_mode = O_RDONLY, 8425 .expect_open_success = true, 8426 .expect_ioctl_allowed = true, 8427 }, 8428 }, 8429 }; 8430 8431 /* 8432 * With LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, it doesn't matter what the 8433 * quiet flags below the layer say. 8434 */ 8435 FIXTURE_VARIANT_ADD(audit_quiet_layout1, subdomains_off) { 8436 .layers = { 8437 { 8438 .handled_access_fs = FS_R, 8439 .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, 8440 .rules = { 8441 { .path = "/", .access = FS_R, .quiet = false }, 8442 } 8443 }, 8444 { 8445 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8446 .quiet_access_fs = FS_R, 8447 .rules = { 8448 { .path = dir_s1d1, .access = 0, .quiet = true }, 8449 { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true }, 8450 { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false }, 8451 { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true }, 8452 { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false }, 8453 }, 8454 }, 8455 }, 8456 .targets = { 8457 { 8458 .target = file1_s1d1, 8459 .open_mode = O_RDWR, 8460 }, 8461 { 8462 .target = file1_s2d1, 8463 .open_mode = O_RDWR, 8464 }, 8465 { 8466 .target = file1_s2d2, 8467 .open_mode = O_RDWR, 8468 .expect_open_success = true, 8469 /* No audit_truncate */ 8470 }, 8471 { 8472 .target = file1_s2d3, 8473 .open_mode = O_RDWR, 8474 .expect_open_success = true, 8475 /* No audit_truncate */ 8476 }, 8477 { 8478 .target = "/dev/null", 8479 .open_mode = O_RDONLY, 8480 .expect_open_success = true, 8481 .expect_ioctl_denied = true, 8482 /* No audit_ioctl */ 8483 }, 8484 { 8485 .target = "/dev/zero", 8486 .open_mode = O_RDONLY, 8487 .expect_open_success = true, 8488 .expect_ioctl_denied = true, 8489 /* No audit_ioctl */ 8490 }, 8491 }, 8492 }; 8493 8494 /* 8495 * With LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, it doesn't matter what the 8496 * quiet flags on the layer say. 8497 */ 8498 FIXTURE_VARIANT_ADD(audit_quiet_layout1, same_exec_off) { 8499 .layers = { 8500 { 8501 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8502 .quiet_access_fs = FS_R, 8503 .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, 8504 .rules = { 8505 { .path = dir_s1d1, .access = 0, .quiet = true }, 8506 { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true }, 8507 { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false }, 8508 { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true }, 8509 { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false }, 8510 }, 8511 }, 8512 }, 8513 .targets = { 8514 { 8515 .target = file1_s1d1, 8516 .open_mode = O_RDWR, 8517 }, 8518 { 8519 .target = file1_s2d1, 8520 .open_mode = O_RDWR, 8521 }, 8522 { 8523 .target = file1_s2d2, 8524 .open_mode = O_RDWR, 8525 .expect_open_success = true, 8526 /* No audit_truncate */ 8527 }, 8528 { 8529 .target = file1_s2d3, 8530 .open_mode = O_RDWR, 8531 .expect_open_success = true, 8532 /* No audit_truncate */ 8533 }, 8534 { 8535 .target = "/dev/null", 8536 .open_mode = O_RDONLY, 8537 .expect_open_success = true, 8538 .expect_ioctl_denied = true, 8539 /* No audit_ioctl */ 8540 }, 8541 { 8542 .target = "/dev/zero", 8543 .open_mode = O_RDONLY, 8544 .expect_open_success = true, 8545 .expect_ioctl_denied = true, 8546 /* No audit_ioctl */ 8547 }, 8548 }, 8549 }; 8550 8551 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_1) { 8552 /* Here, rules that deny access are always quiet. */ 8553 .layers = { 8554 { 8555 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8556 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8557 .rules = { 8558 { 8559 .path = dir_s1d1, 8560 .access = FS_W, 8561 .quiet = true, 8562 }, 8563 { 8564 .path = dir_s2d1, 8565 .access = FS_R | FS_W | FS_TRUNC, 8566 .quiet = false, 8567 }, 8568 { 8569 .path = "/dev/null", 8570 .access = FS_R, 8571 .quiet = true, 8572 }, 8573 { 8574 .path = "/dev/zero", 8575 .access = FS_R | FS_W | FS_IOCTL, 8576 .quiet = false, 8577 }, 8578 }, 8579 }, 8580 { 8581 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8582 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8583 .rules = { 8584 { 8585 .path = dir_s1d1, 8586 .access = FS_R | FS_W | FS_TRUNC, 8587 .quiet = false, 8588 }, 8589 { 8590 .path = dir_s2d1, 8591 .access = FS_W, 8592 .quiet = true, 8593 }, 8594 { 8595 .path = "/dev/null", 8596 .access = FS_R | FS_W | FS_IOCTL, 8597 .quiet = false, 8598 }, 8599 { 8600 .path = "/dev/zero", 8601 .access = FS_R, 8602 .quiet = true, 8603 }, 8604 }, 8605 }, 8606 }, 8607 .targets = { 8608 { 8609 .target = file1_s1d1, 8610 .open_mode = O_RDONLY, 8611 }, 8612 { 8613 .target = file1_s1d1, 8614 .open_mode = O_WRONLY, 8615 .expect_open_success = true, 8616 }, 8617 { 8618 .target = file1_s2d1, 8619 .open_mode = O_RDONLY, 8620 }, 8621 { 8622 .target = file1_s2d1, 8623 .open_mode = O_WRONLY, 8624 .expect_open_success = true, 8625 }, 8626 { 8627 .target = "/dev/null", 8628 .open_mode = O_RDONLY, 8629 .expect_open_success = true, 8630 .expect_ioctl_denied = true, 8631 }, 8632 { 8633 .target = "/dev/zero", 8634 .open_mode = O_RDONLY, 8635 .expect_open_success = true, 8636 .expect_ioctl_denied = true, 8637 }, 8638 }, 8639 }; 8640 8641 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_2) { 8642 /* Here, rules that deny access are never quiet. */ 8643 .layers = { 8644 { 8645 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8646 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8647 .rules = { 8648 { 8649 .path = dir_s1d1, 8650 .access = FS_W, 8651 .quiet = false 8652 }, 8653 { 8654 .path = dir_s2d1, 8655 .access = FS_R | FS_W | FS_TRUNC, 8656 .quiet = true 8657 }, 8658 { 8659 .path = "/dev/null", 8660 .access = FS_R, 8661 .quiet = false 8662 }, 8663 { 8664 .path = "/dev/zero", 8665 .access = FS_R | FS_W | FS_IOCTL, 8666 .quiet = true 8667 }, 8668 }, 8669 }, 8670 { 8671 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8672 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8673 .rules = { 8674 { 8675 .path = dir_s1d1, 8676 .access = FS_R | FS_W | FS_TRUNC, 8677 .quiet = true 8678 }, 8679 { 8680 .path = dir_s2d1, 8681 .access = FS_W, 8682 .quiet = false 8683 }, 8684 { 8685 .path = "/dev/null", 8686 .access = FS_R | FS_W | FS_IOCTL, 8687 .quiet = true 8688 }, 8689 { 8690 .path = "/dev/zero", 8691 .access = FS_R, 8692 .quiet = false 8693 }, 8694 }, 8695 }, 8696 }, 8697 .targets = { 8698 { 8699 .target = file1_s1d1, 8700 .open_mode = O_RDONLY, 8701 .audit_read_blocked = true, 8702 }, 8703 { 8704 .target = file1_s1d1, 8705 .open_mode = O_WRONLY, 8706 .expect_open_success = true, 8707 .audit_truncate = true, 8708 }, 8709 { 8710 .target = file1_s2d1, 8711 .open_mode = O_RDONLY, 8712 .audit_read_blocked = true, 8713 }, 8714 { 8715 .target = file1_s2d1, 8716 .open_mode = O_WRONLY, 8717 .expect_open_success = true, 8718 .audit_truncate = true, 8719 }, 8720 { 8721 .target = "/dev/null", 8722 .open_mode = O_RDONLY, 8723 .expect_open_success = true, 8724 .expect_ioctl_denied = true, 8725 .audit_ioctl = true, 8726 }, 8727 { 8728 .target = "/dev/zero", 8729 .open_mode = O_RDONLY, 8730 .expect_open_success = true, 8731 .expect_ioctl_denied = true, 8732 .audit_ioctl = true, 8733 }, 8734 }, 8735 }; 8736 8737 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_3) { 8738 /* This time only the second layer quiets things. */ 8739 .layers = { 8740 { 8741 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8742 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8743 .rules = { 8744 { 8745 .path = dir_s1d1, 8746 .access = FS_W, 8747 .quiet = false, 8748 }, 8749 { 8750 .path = dir_s2d1, 8751 .access = FS_R | FS_W | FS_TRUNC, 8752 .quiet = false, 8753 }, 8754 { 8755 .path = "/dev/null", 8756 .access = FS_R, 8757 .quiet = false, 8758 }, 8759 { 8760 .path = "/dev/zero", 8761 .access = FS_R | FS_W | FS_IOCTL, 8762 .quiet = false, 8763 }, 8764 }, 8765 }, 8766 { 8767 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8768 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8769 .rules = { 8770 { 8771 .path = dir_s1d1, 8772 .access = FS_R | FS_W | FS_TRUNC, 8773 .quiet = false, 8774 }, 8775 { 8776 .path = dir_s2d1, 8777 .access = FS_W, 8778 .quiet = true, 8779 }, 8780 { 8781 .path = "/dev/null", 8782 .access = FS_R | FS_W | FS_IOCTL, 8783 .quiet = false, 8784 }, 8785 { 8786 .path = "/dev/zero", 8787 .access = FS_R, 8788 .quiet = true, 8789 }, 8790 }, 8791 }, 8792 }, 8793 .targets = { 8794 { 8795 .target = file1_s1d1, 8796 .open_mode = O_RDONLY, 8797 .audit_read_blocked = true, 8798 }, 8799 { 8800 .target = file1_s1d1, 8801 .open_mode = O_WRONLY, 8802 .expect_open_success = true, 8803 .audit_truncate = true, 8804 }, 8805 { 8806 .target = file1_s2d1, 8807 .open_mode = O_RDONLY, 8808 }, 8809 { 8810 .target = file1_s2d1, 8811 .open_mode = O_WRONLY, 8812 .expect_open_success = true, 8813 }, 8814 { 8815 .target = "/dev/null", 8816 .open_mode = O_RDONLY, 8817 .expect_open_success = true, 8818 .expect_ioctl_denied = true, 8819 .audit_ioctl = true, 8820 }, 8821 { 8822 .target = "/dev/zero", 8823 .open_mode = O_RDONLY, 8824 .expect_open_success = true, 8825 .expect_ioctl_denied = true, 8826 }, 8827 }, 8828 }; 8829 8830 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_quiet_access) { 8831 /* Here, rules that deny access are always quiet. */ 8832 .layers = { 8833 { 8834 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8835 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8836 .rules = { 8837 { 8838 .path = dir_s1d1, 8839 .access = FS_W, 8840 .quiet = true, 8841 }, 8842 { 8843 .path = dir_s2d1, 8844 .access = FS_R | FS_W | FS_TRUNC, 8845 .quiet = false, 8846 }, 8847 { 8848 .path = "/dev/null", 8849 .access = FS_R, 8850 .quiet = true, 8851 }, 8852 { 8853 .path = "/dev/zero", 8854 .access = FS_R | FS_W | FS_IOCTL, 8855 .quiet = false, 8856 }, 8857 }, 8858 }, 8859 { 8860 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8861 .quiet_access_fs = FS_IOCTL, 8862 .rules = { 8863 { 8864 .path = dir_s1d1, 8865 .access = FS_R | FS_W | FS_TRUNC, 8866 .quiet = false, 8867 }, 8868 { 8869 .path = dir_s2d1, 8870 .access = FS_W, 8871 .quiet = true, 8872 }, 8873 { 8874 .path = "/dev/null", 8875 .access = FS_R | FS_W | FS_IOCTL, 8876 .quiet = false, 8877 }, 8878 { 8879 .path = "/dev/zero", 8880 .access = FS_R, 8881 .quiet = true, 8882 }, 8883 }, 8884 }, 8885 }, 8886 .targets = { 8887 { 8888 .target = file1_s1d1, 8889 .open_mode = O_RDONLY, 8890 }, 8891 { 8892 .target = file1_s1d1, 8893 .open_mode = O_WRONLY, 8894 .expect_open_success = true, 8895 }, 8896 { 8897 .target = file1_s2d1, 8898 .open_mode = O_RDONLY, 8899 .audit_read_blocked = true, 8900 }, 8901 { 8902 .target = file1_s2d1, 8903 .open_mode = O_WRONLY, 8904 .expect_open_success = true, 8905 .audit_truncate = true, 8906 }, 8907 { 8908 .target = "/dev/null", 8909 .open_mode = O_RDONLY, 8910 .expect_open_success = true, 8911 .expect_ioctl_denied = true, 8912 }, 8913 { 8914 .target = "/dev/zero", 8915 .open_mode = O_RDONLY, 8916 .expect_open_success = true, 8917 .expect_ioctl_denied = true, 8918 }, 8919 }, 8920 }; 8921 8922 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_1) { 8923 /* Quiet from layer 1 */ 8924 .layers = { 8925 { 8926 .handled_access_fs = FS_R, 8927 .quiet_access_fs = FS_R, 8928 .rules = { 8929 { 8930 .path = file1_s1d1, 8931 .access = FS_R, 8932 .quiet = true, 8933 }, 8934 { 8935 .path = file2_s1d1, 8936 .access = 0, 8937 .quiet = true, 8938 }, 8939 { 8940 .path = file1_s1d2, 8941 .access = 0, 8942 .quiet = true, 8943 }, 8944 { 8945 .path = file2_s1d2, 8946 .access = FS_R, 8947 .quiet = true, 8948 }, 8949 }, 8950 }, 8951 { 8952 .handled_access_fs = FS_W, 8953 .quiet_access_fs = FS_W, 8954 .rules = { 8955 { 8956 .path = file1_s1d1, 8957 .access = FS_W, 8958 .quiet = false, 8959 }, 8960 /* Nothing for file2_s1d1 */ 8961 { 8962 .path = file1_s1d2, 8963 .access = FS_W, 8964 .quiet = false, 8965 }, 8966 /* Nothing for file2_s1d2 */ 8967 }, 8968 }, 8969 }, 8970 .targets = { 8971 { 8972 .target = file1_s1d1, 8973 .open_mode = O_RDWR, 8974 .expect_open_success = true, 8975 .expect_truncate_success = true, 8976 }, 8977 /* Missing both, youngest layer denies write, not quiet */ 8978 { 8979 .target = file2_s1d1, 8980 .open_mode = O_RDWR, 8981 .audit_write_blocked = true, 8982 }, 8983 /* Missing read, denied and quieted by layer 1 */ 8984 { 8985 .target = file1_s1d2, 8986 .open_mode = O_RDWR, 8987 }, 8988 /* Missing write, denied and not quieted by layer 2 */ 8989 { 8990 .target = file2_s1d2, 8991 .open_mode = O_RDWR, 8992 .audit_write_blocked = true, 8993 }, 8994 }, 8995 }; 8996 8997 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_2) { 8998 /* Quiet from layer 2 */ 8999 .layers = { 9000 { 9001 .handled_access_fs = FS_R, 9002 .quiet_access_fs = FS_R, 9003 .rules = { 9004 { 9005 .path = file1_s1d1, 9006 .access = FS_R, 9007 .quiet = false, 9008 }, 9009 /* Nothing for file2_s1d1 and file1_s1d2 */ 9010 { 9011 .path = file2_s1d2, 9012 .access = FS_R, 9013 .quiet = false, 9014 }, 9015 }, 9016 }, 9017 { 9018 .handled_access_fs = FS_W, 9019 .quiet_access_fs = FS_W, 9020 .rules = { 9021 { 9022 .path = file1_s1d1, 9023 .access = FS_W, 9024 .quiet = true, 9025 }, 9026 { 9027 .path = file2_s1d1, 9028 .access = 0, 9029 .quiet = true, 9030 }, 9031 { 9032 .path = file1_s1d2, 9033 .access = FS_W, 9034 .quiet = true, 9035 }, 9036 { 9037 .path = file2_s1d2, 9038 .access = 0, 9039 .quiet = true, 9040 }, 9041 }, 9042 }, 9043 }, 9044 .targets = { 9045 { 9046 .target = file1_s1d1, 9047 .open_mode = O_RDWR, 9048 .expect_open_success = true, 9049 .expect_truncate_success = true, 9050 }, 9051 /* Missing both, youngest layer denies write, quiet */ 9052 { 9053 .target = file2_s1d1, 9054 .open_mode = O_RDWR, 9055 }, 9056 /* Missing read, denied and not quieted by layer 1 */ 9057 { 9058 .target = file1_s1d2, 9059 .open_mode = O_RDWR, 9060 .audit_read_blocked = true, 9061 }, 9062 /* Missing write, denied and quieted by layer 2 */ 9063 { 9064 .target = file2_s1d2, 9065 .open_mode = O_RDWR, 9066 }, 9067 }, 9068 }; 9069 9070 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_3) { 9071 /* Quiet from both layers */ 9072 .layers = { 9073 { 9074 .handled_access_fs = FS_R, 9075 .quiet_access_fs = FS_R, 9076 .rules = { 9077 { 9078 .path = file1_s1d1, 9079 .access = FS_R, 9080 .quiet = true, 9081 }, 9082 { 9083 .path = file2_s1d1, 9084 .access = 0, 9085 .quiet = true, 9086 }, 9087 { 9088 .path = file1_s1d2, 9089 .access = 0, 9090 .quiet = true, 9091 }, 9092 { 9093 .path = file2_s1d2, 9094 .access = FS_R, 9095 .quiet = true, 9096 }, 9097 }, 9098 }, 9099 { 9100 .handled_access_fs = FS_W, 9101 .quiet_access_fs = FS_W, 9102 .rules = { 9103 { 9104 .path = file1_s1d1, 9105 .access = FS_W, 9106 .quiet = true, 9107 }, 9108 { 9109 .path = file2_s1d1, 9110 .access = 0, 9111 .quiet = true, 9112 }, 9113 { 9114 .path = file1_s1d2, 9115 .access = FS_W, 9116 .quiet = true, 9117 }, 9118 { 9119 .path = file2_s1d2, 9120 .access = 0, 9121 .quiet = true, 9122 }, 9123 }, 9124 }, 9125 }, 9126 .targets = { 9127 { 9128 .target = file1_s1d1, 9129 .open_mode = O_RDWR, 9130 .expect_open_success = true, 9131 .expect_truncate_success = true, 9132 }, 9133 { 9134 .target = file2_s1d1, 9135 .open_mode = O_RDWR, 9136 }, 9137 { 9138 .target = file1_s1d2, 9139 .open_mode = O_RDWR, 9140 }, 9141 { 9142 .target = file2_s1d2, 9143 .open_mode = O_RDWR, 9144 }, 9145 }, 9146 }; 9147 9148 FIXTURE_VARIANT_ADD(audit_quiet_layout1, without_quiet_then_with_quiet) { 9149 .layers = { 9150 { 9151 .handled_access_fs = FS_R | FS_W, 9152 .quiet_access_fs = FS_R, 9153 .rules = { 9154 { .path = dir_s1d1, .access = FS_W, .quiet = false }, 9155 { .path = dir_s1d1, .access = 0, .quiet = true }, 9156 }, 9157 }, 9158 }, 9159 .targets = { 9160 /* Read denied and quieted */ 9161 { 9162 .target = file1_s1d1, 9163 .open_mode = O_RDONLY, 9164 }, 9165 /* Write ok */ 9166 { 9167 .target = file1_s1d1, 9168 .open_mode = O_WRONLY, 9169 .expect_open_success = true, 9170 .expect_truncate_success = true, 9171 }, 9172 /* Write ok, read denied and quieted */ 9173 { 9174 .target = file1_s1d1, 9175 .open_mode = O_RDWR, 9176 }, 9177 /* Not covered by quiet */ 9178 { 9179 .target = file1_s2d1, 9180 .open_mode = O_RDONLY, 9181 .audit_read_blocked = true, 9182 }, 9183 }, 9184 }; 9185 9186 /* 9187 * The following TEST_F extend the above test cases to test more layers, with 9188 * the inserted layers having varying configurations. 9189 */ 9190 9191 /* Extra allow all layers, quiet or not, does not change any behaviour. */ 9192 TEST_F(audit_quiet_layout1, allow_all_layer) 9193 { 9194 struct a_layer allow_all_layer = { 9195 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9196 .quiet_access_fs = 0, 9197 .rules = { 9198 { 9199 .path = "/", 9200 .access = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9201 .quiet = false, 9202 }, 9203 }, 9204 }; 9205 int i; 9206 9207 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); 9208 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9209 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9210 9211 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9212 9213 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); 9214 9215 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9216 9217 /* 9218 * SELF_LOG flags or quiet bits from inner allowing layers should not 9219 * affect behaviour. 9220 */ 9221 allow_all_layer.quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL; 9222 allow_all_layer.rules[0].quiet = true; 9223 /* 9224 * Note: this only works because we're not checking counts of domain 9225 * alloc/dealloc logs 9226 */ 9227 allow_all_layer.restrict_flags = 9228 LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF | 9229 LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF; 9230 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); 9231 9232 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9233 } 9234 9235 /* 9236 * Add useless outer layers until we reach the layer limit. Should not change 9237 * anything. 9238 */ 9239 TEST_F(audit_quiet_layout1, many_outer_layers) 9240 { 9241 struct a_layer useless_layer = { 9242 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 9243 .quiet_access_fs = FS_R | FS_W | FS_TRUNC, 9244 .rules = { 9245 { .path = "/", .access = FS_R | FS_W | FS_TRUNC, .quiet = true }, 9246 }, 9247 }; 9248 int i; 9249 9250 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) { 9251 if (variant->layers[i].handled_access_fs == 0) 9252 break; 9253 } 9254 9255 for (; i < LANDLOCK_MAX_NUM_LAYERS; i++) 9256 ASSERT_EQ(0, apply_a_layer(_metadata, &useless_layer)); 9257 9258 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9259 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9260 9261 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9262 } 9263 9264 /* An inner layer that denies and quiets everything should result in no logs. */ 9265 TEST_F(audit_quiet_layout1, deny_all_quiet_layer) 9266 { 9267 struct a_layer deny_all_layer = { 9268 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9269 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9270 .rules = { 9271 { .path = "/", .access = 0, .quiet = true }, 9272 }, 9273 }; 9274 int i; 9275 FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {}; 9276 9277 /* Any open should fail with no logs. */ 9278 for (i = 0; i < ARRAY_SIZE(variant->targets); i++) { 9279 const struct a_target *target = &variant->targets[i]; 9280 9281 variant_2.targets[i] = (struct a_target){ 9282 .target = target->target, 9283 .open_mode = target->open_mode, 9284 /* We denied everything, open should always fail. */ 9285 .expect_open_success = false, 9286 }; 9287 } 9288 9289 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9290 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9291 ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer)); 9292 9293 audit_quiet_layout1_test_body(_metadata, self, variant_2.targets); 9294 } 9295 9296 /* 9297 * An inner layer that denies everything without quiet should produce logs for 9298 * all access. 9299 */ 9300 TEST_F(audit_quiet_layout1, deny_all_layer) 9301 { 9302 struct a_layer deny_all_layer = { 9303 .handled_access_fs = FS_R | FS_W, 9304 .quiet_access_fs = FS_R | FS_W, 9305 }; 9306 int i; 9307 FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {}; 9308 bool test_has_subdomains_off = false; 9309 9310 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) { 9311 if (variant->layers[i].restrict_flags & 9312 LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF) { 9313 test_has_subdomains_off = true; 9314 break; 9315 } 9316 } 9317 9318 for (i = 0; i < ARRAY_SIZE(variant->targets); i++) { 9319 const struct a_target *target = &variant->targets[i]; 9320 9321 variant_2.targets[i] = (struct a_target){ 9322 .target = target->target, 9323 .open_mode = target->open_mode, 9324 9325 /* We denied everything, open should always fail. */ 9326 .expect_open_success = false, 9327 /* Audit should always happen as long as open request contains read. */ 9328 .audit_read_blocked = !test_has_subdomains_off && 9329 target->open_mode != O_WRONLY, 9330 /* Audit should always happen as long as open request contains write. */ 9331 .audit_write_blocked = !test_has_subdomains_off && 9332 target->open_mode != O_RDONLY, 9333 }; 9334 } 9335 9336 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9337 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9338 ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer)); 9339 9340 audit_quiet_layout1_test_body(_metadata, self, variant_2.targets); 9341 } 9342 9343 /* Uses layout1_bind hierarchy */ 9344 FIXTURE(audit_quiet_rename) 9345 { 9346 struct audit_filter audit_filter; 9347 int audit_fd; 9348 }; 9349 9350 FIXTURE_SETUP(audit_quiet_rename) 9351 { 9352 prepare_layout(_metadata); 9353 create_layout1(_metadata); 9354 9355 set_cap(_metadata, CAP_SYS_ADMIN); 9356 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 9357 clear_cap(_metadata, CAP_SYS_ADMIN); 9358 9359 set_cap(_metadata, CAP_AUDIT_CONTROL); 9360 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 9361 EXPECT_LE(0, self->audit_fd); 9362 clear_cap(_metadata, CAP_AUDIT_CONTROL); 9363 9364 if (getenv("DEBUG_QUIET_TESTS")) 9365 debug_quiet_tests = true; 9366 } 9367 9368 FIXTURE_TEARDOWN_PARENT(audit_quiet_rename) 9369 { 9370 remove_layout1(_metadata); 9371 cleanup_layout(_metadata); 9372 9373 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 9374 9375 remove_path(file1_s4d1); 9376 remove_path(file2_s4d1); 9377 9378 set_cap(_metadata, CAP_AUDIT_CONTROL); 9379 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 9380 clear_cap(_metadata, CAP_AUDIT_CONTROL); 9381 } 9382 9383 static void simple_quiet_rename(struct __test_metadata *const _metadata, 9384 FIXTURE_DATA(audit_quiet_rename) *const self, 9385 __u64 handled_access, __u64 quiet_access, 9386 bool source_allow, bool dest_allow, 9387 bool source_quiet, bool dest_quiet, 9388 const char *source_blockers, 9389 const char *dest_blockers) 9390 { 9391 /* We will move file1_s1d1 to file1_s2d1 */ 9392 struct a_layer layer = { 9393 .handled_access_fs = handled_access, 9394 .quiet_access_fs = quiet_access, 9395 .rules = { 9396 { 9397 .path = dir_s1d1, 9398 .access = source_allow ? handled_access : 0, 9399 .quiet = source_quiet, 9400 }, 9401 { 9402 .path = dir_s2d1, 9403 .access = dest_allow ? handled_access : 0, 9404 .quiet = dest_quiet, 9405 }, 9406 }, 9407 }; 9408 struct audit_records records = {}; 9409 int ret, err; 9410 9411 /* Skip landlock_add_rule for useless rules. */ 9412 if (!source_allow && !source_quiet) 9413 layer.rules[0].path = NULL; 9414 if (!dest_allow && !dest_quiet) 9415 layer.rules[1].path = NULL; 9416 9417 EXPECT_EQ(0, unlink(file1_s2d1)); 9418 EXPECT_EQ(0, apply_a_layer(_metadata, &layer)); 9419 9420 if (debug_quiet_tests) 9421 TH_LOG("Try renameat \"%s\" to \"%s\"", file1_s1d1, file1_s2d1); 9422 ret = renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1); 9423 err = errno; 9424 if (ret != 0 && debug_quiet_tests) { 9425 TH_LOG("renameat error: %s", err == EXDEV ? "EXDEV" : 9426 err == EACCES ? "EACCES" : 9427 strerror(err)); 9428 } 9429 if (source_allow && dest_allow) { 9430 ASSERT_EQ(0, ret); 9431 } else { 9432 ASSERT_EQ(-1, ret); 9433 if (handled_access & (LANDLOCK_ACCESS_FS_MAKE_REG | 9434 LANDLOCK_ACCESS_FS_REMOVE_FILE)) { 9435 ASSERT_EQ(EACCES, err); 9436 } else { 9437 ASSERT_EQ(EXDEV, err); 9438 } 9439 9440 if (source_blockers) 9441 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9442 source_blockers, dir_s1d1)); 9443 if (dest_blockers) 9444 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9445 dest_blockers, dir_s2d1)); 9446 } 9447 /* 9448 * No other logs. records.domain not checked per reasoning in 9449 * audit_quiet_layout1_test_body. 9450 */ 9451 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9452 ASSERT_EQ(0, records.access); 9453 } 9454 9455 TEST_F(audit_quiet_rename, rename_ok) 9456 { 9457 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9458 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9459 LANDLOCK_ACCESS_FS_REFER; 9460 9461 simple_quiet_rename(_metadata, self, access, access, true, true, false, 9462 false, NULL, NULL); 9463 } 9464 9465 TEST_F(audit_quiet_rename, no_quiet) 9466 { 9467 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9468 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9469 LANDLOCK_ACCESS_FS_REFER; 9470 9471 simple_quiet_rename(_metadata, self, access, access, false, false, 9472 false, false, "fs\\.remove_file,fs\\.refer", 9473 "fs\\.make_reg,fs\\.refer"); 9474 } 9475 9476 TEST_F(audit_quiet_rename, quiet) 9477 { 9478 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9479 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9480 LANDLOCK_ACCESS_FS_REFER; 9481 9482 simple_quiet_rename(_metadata, self, access, access, false, false, true, 9483 true, NULL, NULL); 9484 } 9485 9486 TEST_F(audit_quiet_rename, source_no_quiet_dest_quiet) 9487 { 9488 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9489 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9490 LANDLOCK_ACCESS_FS_REFER; 9491 9492 simple_quiet_rename(_metadata, self, access, access, false, false, 9493 false, true, "fs\\.remove_file,fs\\.refer", NULL); 9494 } 9495 9496 TEST_F(audit_quiet_rename, source_quiet_dest_no_quiet) 9497 { 9498 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9499 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9500 LANDLOCK_ACCESS_FS_REFER; 9501 9502 simple_quiet_rename(_metadata, self, access, access, false, false, true, 9503 false, NULL, "fs\\.make_reg,fs\\.refer"); 9504 } 9505 9506 TEST_F(audit_quiet_rename, only_quiet_refer) 9507 { 9508 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9509 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9510 LANDLOCK_ACCESS_FS_REFER; 9511 9512 simple_quiet_rename(_metadata, self, access, LANDLOCK_ACCESS_FS_REFER, 9513 false, false, true, true, 9514 "fs\\.remove_file,fs\\.refer", 9515 "fs\\.make_reg,fs\\.refer"); 9516 } 9517 9518 TEST_F(audit_quiet_rename, source_allow_dest_quiet) 9519 { 9520 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9521 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9522 LANDLOCK_ACCESS_FS_REFER; 9523 9524 simple_quiet_rename(_metadata, self, access, access, true, false, false, 9525 true, NULL, NULL); 9526 } 9527 9528 TEST_F(audit_quiet_rename, source_quiet_dest_allow) 9529 { 9530 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9531 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9532 LANDLOCK_ACCESS_FS_REFER; 9533 9534 simple_quiet_rename(_metadata, self, access, access, false, true, true, 9535 false, NULL, NULL); 9536 } 9537 9538 TEST_F(audit_quiet_rename, handle_all_deny_quiet_refer) 9539 { 9540 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9541 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9542 LANDLOCK_ACCESS_FS_REFER; 9543 struct a_layer layer = { 9544 .handled_access_fs = access, 9545 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 9546 .rules = { 9547 { 9548 .path = dir_s1d1, 9549 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9550 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9551 .quiet = true, 9552 }, 9553 { 9554 .path = dir_s2d1, 9555 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9556 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9557 .quiet = true, 9558 }, 9559 }, 9560 }; 9561 struct audit_records records = {}; 9562 9563 EXPECT_EQ(0, unlink(file1_s2d1)); 9564 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9565 9566 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9567 ASSERT_EQ(EXDEV, errno); 9568 9569 /* No logs */ 9570 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9571 ASSERT_EQ(0, records.access); 9572 } 9573 9574 TEST_F(audit_quiet_rename, handle_all_deny_not_quiet_refer) 9575 { 9576 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9577 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9578 LANDLOCK_ACCESS_FS_REFER; 9579 struct a_layer layer = { 9580 .handled_access_fs = access, 9581 .quiet_access_fs = 0, 9582 .rules = { 9583 { 9584 .path = dir_s1d1, 9585 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9586 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9587 .quiet = false, 9588 }, 9589 { 9590 .path = dir_s2d1, 9591 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9592 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9593 .quiet = false, 9594 }, 9595 }, 9596 }; 9597 struct audit_records records = {}; 9598 9599 EXPECT_EQ(0, unlink(file1_s2d1)); 9600 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9601 9602 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9603 ASSERT_EQ(EXDEV, errno); 9604 9605 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 9606 dir_s1d1)); 9607 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 9608 dir_s2d1)); 9609 9610 /* No other logs */ 9611 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9612 ASSERT_EQ(0, records.access); 9613 } 9614 9615 TEST_F(audit_quiet_rename, handle_all_deny_refer_quiet_source_not_quiet_dest) 9616 { 9617 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9618 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9619 LANDLOCK_ACCESS_FS_REFER; 9620 struct a_layer layer = { 9621 .handled_access_fs = access, 9622 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 9623 .rules = { 9624 { 9625 .path = dir_s1d1, 9626 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9627 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9628 .quiet = true, 9629 }, 9630 { 9631 .path = dir_s2d1, 9632 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9633 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9634 .quiet = false, 9635 }, 9636 }, 9637 }; 9638 struct audit_records records = {}; 9639 9640 EXPECT_EQ(0, unlink(file1_s2d1)); 9641 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9642 9643 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9644 ASSERT_EQ(EXDEV, errno); 9645 9646 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 9647 dir_s2d1)); 9648 9649 /* No other logs */ 9650 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9651 ASSERT_EQ(0, records.access); 9652 } 9653 9654 TEST_F(audit_quiet_rename, quiet_same_dir) 9655 { 9656 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9657 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9658 LANDLOCK_ACCESS_FS_REFER; 9659 struct a_layer layer = { 9660 .handled_access_fs = access, 9661 .quiet_access_fs = access, 9662 .rules = { 9663 { 9664 .path = dir_s1d1, 9665 .access = 0, 9666 .quiet = true, 9667 }, 9668 }, 9669 }; 9670 struct audit_records records = {}; 9671 9672 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9673 9674 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1)); 9675 ASSERT_EQ(EACCES, errno); 9676 9677 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9678 ASSERT_EQ(0, records.access); 9679 } 9680 9681 TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored) 9682 { 9683 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9684 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9685 LANDLOCK_ACCESS_FS_REFER; 9686 struct a_layer layer = { 9687 .handled_access_fs = access, 9688 .quiet_access_fs = access, 9689 .rules = { 9690 { 9691 .path = file1_s1d1, 9692 .access = 0, 9693 .quiet = true, 9694 }, 9695 { 9696 .path = file1_s2d1, 9697 .access = 0, 9698 .quiet = true, 9699 }, 9700 }, 9701 }; 9702 struct audit_records records = {}; 9703 9704 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9705 9706 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9707 ASSERT_EQ(EACCES, errno); 9708 9709 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9710 "fs\\.remove_file,fs\\.refer", dir_s1d1)); 9711 /* We didn't unlink destination file */ 9712 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9713 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 9714 dir_s2d1)); 9715 9716 /* No other logs */ 9717 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9718 ASSERT_EQ(0, records.access); 9719 } 9720 9721 TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored_same_dir) 9722 { 9723 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9724 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9725 LANDLOCK_ACCESS_FS_REFER; 9726 struct a_layer layer = { 9727 .handled_access_fs = access, 9728 .quiet_access_fs = access, 9729 .rules = { 9730 { 9731 .path = file1_s1d1, 9732 .access = 0, 9733 .quiet = true, 9734 }, 9735 { 9736 .path = file2_s1d1, 9737 .access = 0, 9738 .quiet = true, 9739 }, 9740 }, 9741 }; 9742 struct audit_records records = {}; 9743 9744 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9745 9746 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1)); 9747 ASSERT_EQ(EACCES, errno); 9748 9749 ASSERT_EQ(0, 9750 matches_log_fs(_metadata, self->audit_fd, 9751 "fs\\.remove_file,fs\\.make_reg", dir_s1d1)); 9752 9753 /* No other logs */ 9754 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9755 ASSERT_EQ(0, records.access); 9756 } 9757 9758 TEST_F(audit_quiet_rename, two_layers_different_quiet1) 9759 { 9760 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9761 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9762 LANDLOCK_ACCESS_FS_REFER; 9763 struct a_layer layer1 = { 9764 .handled_access_fs = access, 9765 .quiet_access_fs = access, 9766 .rules = { 9767 { 9768 .path = dir_s1d1, 9769 .access = access, 9770 .quiet = false, 9771 }, 9772 { 9773 .path = dir_s2d1, 9774 .access = 0, 9775 .quiet = true, 9776 }, 9777 }, 9778 }; 9779 struct a_layer layer2 = { 9780 .handled_access_fs = access, 9781 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 9782 .rules = { 9783 { 9784 .path = dir_s1d1, 9785 .access = 0, 9786 .quiet = true, 9787 }, 9788 { 9789 .path = dir_s2d1, 9790 .access = access, 9791 .quiet = false, 9792 }, 9793 }, 9794 }; 9795 struct audit_records records = {}; 9796 9797 EXPECT_EQ(0, unlink(file1_s2d1)); 9798 9799 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 9800 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 9801 9802 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9803 ASSERT_EQ(EACCES, errno); 9804 9805 /* 9806 * The youngest denial will be layer 2. Refer is quieted but we are 9807 * also missing remove_file on source. 9808 */ 9809 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9810 "fs\\.remove_file,fs\\.refer", dir_s1d1)); 9811 /* No other logs */ 9812 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9813 ASSERT_EQ(0, records.access); 9814 } 9815 9816 TEST_F(audit_quiet_rename, two_layers_different_quiet2) 9817 { 9818 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9819 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9820 LANDLOCK_ACCESS_FS_REFER; 9821 struct a_layer layer1 = { 9822 .handled_access_fs = access, 9823 .quiet_access_fs = access, 9824 .rules = { 9825 { 9826 .path = dir_s1d1, 9827 .access = access, 9828 .quiet = false, 9829 }, 9830 { 9831 .path = dir_s2d1, 9832 .access = 0, 9833 .quiet = true, 9834 }, 9835 }, 9836 }; 9837 struct a_layer layer2 = { 9838 .handled_access_fs = LANDLOCK_ACCESS_FS_REFER, 9839 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 9840 .rules = { 9841 { 9842 .path = dir_s1d1, 9843 .access = 0, 9844 .quiet = true, 9845 }, 9846 { 9847 .path = dir_s2d1, 9848 .access = LANDLOCK_ACCESS_FS_REFER, 9849 .quiet = false, 9850 }, 9851 }, 9852 }; 9853 struct audit_records records = {}; 9854 9855 EXPECT_EQ(0, unlink(file1_s2d1)); 9856 9857 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 9858 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 9859 9860 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9861 ASSERT_EQ(EACCES, errno); 9862 9863 /* 9864 * The youngest denial will be layer 2, but refer is quieted (and that 9865 * layer does not handle any other accesses). 9866 */ 9867 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9868 ASSERT_EQ(0, records.access); 9869 } 9870 9871 TEST_F(audit_quiet_rename, two_layers_different_quiet3) 9872 { 9873 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9874 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9875 LANDLOCK_ACCESS_FS_REFER; 9876 struct a_layer layer1 = { 9877 .handled_access_fs = access, 9878 .quiet_access_fs = access, 9879 .rules = { 9880 { 9881 .path = dir_s1d1, 9882 .access = access, 9883 .quiet = false, 9884 }, 9885 { 9886 .path = dir_s2d1, 9887 .access = 0, 9888 .quiet = true, 9889 }, 9890 }, 9891 }; 9892 struct a_layer layer2 = { 9893 .handled_access_fs = access, 9894 .quiet_access_fs = access, 9895 .rules = { 9896 { 9897 .path = dir_s1d1, 9898 .access = 0, 9899 .quiet = true, 9900 }, 9901 { 9902 .path = dir_s2d1, 9903 .access = access, 9904 .quiet = false, 9905 }, 9906 }, 9907 }; 9908 struct audit_records records = {}; 9909 9910 EXPECT_EQ(0, unlink(file1_s2d1)); 9911 9912 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 9913 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 9914 9915 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9916 ASSERT_EQ(EACCES, errno); 9917 9918 /* 9919 * The youngest denial will be layer 2, in which everything is quieted. 9920 */ 9921 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9922 ASSERT_EQ(0, records.access); 9923 } 9924 9925 TEST_F(audit_quiet_rename, 9926 first_layer_quiet_deny_all_second_layer_not_quiet_deny_all) 9927 { 9928 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9929 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9930 LANDLOCK_ACCESS_FS_REFER; 9931 struct a_layer layer1 = { 9932 .handled_access_fs = access, 9933 .quiet_access_fs = access, 9934 .rules = { 9935 { 9936 .path = dir_s1d1, 9937 .access = 0, 9938 .quiet = true, 9939 }, 9940 { 9941 .path = dir_s2d1, 9942 .access = 0, 9943 .quiet = true, 9944 }, 9945 }, 9946 }; 9947 struct a_layer layer2 = { 9948 .handled_access_fs = access, 9949 .quiet_access_fs = access, 9950 .rules = {}, 9951 }; 9952 struct audit_records records = {}; 9953 9954 EXPECT_EQ(0, unlink(file1_s2d1)); 9955 9956 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 9957 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 9958 9959 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9960 ASSERT_EQ(EACCES, errno); 9961 9962 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9963 "fs\\.remove_file,fs\\.refer", dir_s1d1)); 9964 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9965 "fs\\.make_reg,fs\\.refer", dir_s2d1)); 9966 /* No other logs. */ 9967 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9968 ASSERT_EQ(0, records.access); 9969 } 9970 9971 TEST_F(audit_quiet_rename, 9972 first_layer_quiet_deny_all_second_layer_dest_not_quiet) 9973 { 9974 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9975 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9976 LANDLOCK_ACCESS_FS_REFER; 9977 struct a_layer layer1 = { 9978 .handled_access_fs = access, 9979 .quiet_access_fs = access, 9980 .rules = { 9981 { 9982 .path = dir_s1d1, 9983 .access = 0, 9984 .quiet = true, 9985 }, 9986 { 9987 .path = dir_s2d1, 9988 .access = 0, 9989 .quiet = true, 9990 }, 9991 }, 9992 }; 9993 struct a_layer layer2 = { 9994 .handled_access_fs = access, 9995 .quiet_access_fs = access, 9996 .rules = { 9997 { 9998 .path = dir_s1d1, 9999 .access = 0, 10000 .quiet = true, 10001 }, 10002 }, 10003 }; 10004 struct audit_records records = {}; 10005 10006 EXPECT_EQ(0, unlink(file1_s2d1)); 10007 10008 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 10009 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 10010 10011 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 10012 ASSERT_EQ(EACCES, errno); 10013 10014 /* Source is quieted but destination is not. */ 10015 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 10016 "fs\\.make_reg,fs\\.refer", dir_s2d1)); 10017 /* No other logs. */ 10018 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10019 ASSERT_EQ(0, records.access); 10020 } 10021 10022 TEST_F(audit_quiet_rename, rename_xchg) 10023 { 10024 struct a_layer layer = { 10025 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG | 10026 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10027 LANDLOCK_ACCESS_FS_REFER, 10028 .quiet_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG, 10029 .rules = { { 10030 .path = dir_s1d1, 10031 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE | 10032 LANDLOCK_ACCESS_FS_REFER, 10033 .quiet = true, 10034 }, 10035 { 10036 .path = dir_s2d1, 10037 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 10038 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10039 LANDLOCK_ACCESS_FS_REFER, 10040 .quiet = false, 10041 } }, 10042 }; 10043 struct audit_records records = {}; 10044 10045 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10046 10047 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1, 10048 RENAME_EXCHANGE)); 10049 ASSERT_EQ(EACCES, errno); 10050 10051 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10052 ASSERT_EQ(0, records.access); 10053 } 10054 10055 TEST_F(audit_quiet_rename, quiet_on_parent_mount) 10056 { 10057 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10058 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10059 LANDLOCK_ACCESS_FS_REFER; 10060 struct a_layer layer = { 10061 .handled_access_fs = access, 10062 .quiet_access_fs = access, 10063 .rules = { 10064 { 10065 .path = dir_s2d1, 10066 .access = 0, 10067 .quiet = true, 10068 }, 10069 }, 10070 }; 10071 struct audit_records records = {}; 10072 10073 EXPECT_EQ(0, unlink(file2_s1d3)); 10074 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10075 10076 ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD, 10077 bind_file2_s1d3)); 10078 ASSERT_EQ(EACCES, errno); 10079 10080 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10081 ASSERT_EQ(0, records.access); 10082 } 10083 10084 TEST_F(audit_quiet_rename, quiet_behind_mountpoint_ignored) 10085 { 10086 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10087 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10088 LANDLOCK_ACCESS_FS_REFER; 10089 struct a_layer layer = { 10090 .handled_access_fs = access, 10091 .quiet_access_fs = access, 10092 .rules = { 10093 { 10094 .path = dir_s1d1, 10095 .access = 0, 10096 .quiet = true, 10097 }, 10098 }, 10099 }; 10100 struct audit_records records = {}; 10101 10102 EXPECT_EQ(0, unlink(file2_s1d3)); 10103 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10104 10105 ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD, 10106 bind_file2_s1d3)); 10107 ASSERT_EQ(EACCES, errno); 10108 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 10109 "fs\\.remove_file,fs\\.make_reg", 10110 bind_dir_s1d3)); 10111 10112 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10113 ASSERT_EQ(0, records.access); 10114 } 10115 10116 TEST_F(audit_quiet_rename, quiet_on_parent_mount_disconnected) 10117 { 10118 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10119 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10120 LANDLOCK_ACCESS_FS_REFER; 10121 struct a_layer layer = { 10122 .handled_access_fs = access, 10123 .quiet_access_fs = access, 10124 .rules = { 10125 { 10126 .path = dir_s2d1, 10127 .access = 0, 10128 .quiet = true, 10129 }, 10130 }, 10131 }; 10132 struct audit_records records = {}; 10133 int bind_s1d3_fd; 10134 10135 EXPECT_EQ(0, unlink(file2_s1d3)); 10136 10137 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY); 10138 ASSERT_GE(bind_s1d3_fd, 0); 10139 10140 /* Make s1d3 disconnected. */ 10141 create_directory(_metadata, dir_s4d1); 10142 ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2)); 10143 10144 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10145 10146 ASSERT_EQ(-1, 10147 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)); 10148 ASSERT_EQ(EACCES, errno); 10149 10150 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10151 ASSERT_EQ(0, records.access); 10152 } 10153 10154 TEST_F(audit_quiet_rename, quiet_behind_mountpoint_disconnected) 10155 { 10156 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10157 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10158 LANDLOCK_ACCESS_FS_REFER; 10159 struct a_layer layer = { 10160 .handled_access_fs = access, 10161 .quiet_access_fs = access, 10162 .rules = { 10163 { 10164 .path = dir_s4d1, 10165 .access = 0, 10166 .quiet = true, 10167 }, 10168 }, 10169 }; 10170 struct audit_records records = {}; 10171 int bind_s1d3_fd; 10172 10173 EXPECT_EQ(0, unlink(file2_s1d3)); 10174 10175 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY); 10176 ASSERT_GE(bind_s1d3_fd, 0); 10177 10178 /* Make s1d3 disconnected. */ 10179 create_directory(_metadata, dir_s4d1); 10180 ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2)); 10181 10182 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10183 10184 ASSERT_EQ(-1, 10185 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)); 10186 ASSERT_EQ(EACCES, errno); 10187 10188 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10189 ASSERT_EQ(0, records.access); 10190 } 10191 10192 TEST_HARNESS_MAIN 10193