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