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 /* Arbitrary command with nonzero bits in both 16-bit halves. */ 4150 static const unsigned int unknown_ioctl_cmd = 0xc00ffeee; 4151 4152 /* 4153 * ioctl_error - generically call the given ioctl with a pointer to a 4154 * sufficiently large zeroed-out memory region. 4155 * 4156 * Returns the IOCTLs error, or 0. 4157 */ 4158 static int ioctl_error(struct __test_metadata *const _metadata, int fd, 4159 unsigned int cmd) 4160 { 4161 char buf[128]; /* sufficiently large */ 4162 int res, stdinbak_fd, err; 4163 4164 /* 4165 * Depending on the IOCTL command, parts of the zeroed-out buffer might 4166 * be interpreted as file descriptor numbers. We do not want to 4167 * accidentally operate on file descriptor 0 (stdin), so we temporarily 4168 * move stdin to a different FD and close FD 0 for the IOCTL call. 4169 */ 4170 stdinbak_fd = dup(0); 4171 ASSERT_LT(0, stdinbak_fd); 4172 ASSERT_EQ(0, close(0)); 4173 4174 /* Invokes the IOCTL with a zeroed-out buffer. */ 4175 bzero(&buf, sizeof(buf)); 4176 res = ioctl(fd, cmd, &buf); 4177 err = errno; 4178 4179 /* Restores the old FD 0 and closes the backup FD. */ 4180 ASSERT_EQ(0, dup2(stdinbak_fd, 0)); 4181 ASSERT_EQ(0, close(stdinbak_fd)); 4182 4183 if (res < 0) 4184 return err; 4185 4186 return 0; 4187 } 4188 4189 /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */ 4190 struct space_resv { 4191 __s16 l_type; 4192 __s16 l_whence; 4193 __s64 l_start; 4194 __s64 l_len; /* len == 0 means until end of file */ 4195 __s32 l_sysid; 4196 __u32 l_pid; 4197 __s32 l_pad[4]; /* reserved area */ 4198 }; 4199 4200 #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) 4201 #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv) 4202 #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) 4203 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv) 4204 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv) 4205 4206 /* 4207 * Tests a series of blanket-permitted and denied IOCTLs. 4208 */ 4209 TEST_F_FORK(layout1, blanket_permitted_ioctls) 4210 { 4211 int fd; 4212 4213 /* Enables Landlock. */ 4214 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4215 4216 fd = open("/dev/null", O_RDWR | O_CLOEXEC); 4217 ASSERT_LE(0, fd); 4218 4219 /* 4220 * Checks permitted commands. 4221 * These ones may return errors, but should not be blocked by Landlock. 4222 */ 4223 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX)); 4224 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX)); 4225 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO)); 4226 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC)); 4227 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE)); 4228 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE)); 4229 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW)); 4230 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP)); 4231 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ)); 4232 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE)); 4233 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE)); 4234 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE)); 4235 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID)); 4236 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH)); 4237 4238 /* 4239 * Checks blocked commands. 4240 * A call to a blocked IOCTL command always returns EACCES. 4241 */ 4242 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 4243 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS)); 4244 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS)); 4245 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR)); 4246 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR)); 4247 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP)); 4248 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP)); 4249 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64)); 4250 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP)); 4251 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64)); 4252 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE)); 4253 4254 /* Default case is also blocked. */ 4255 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, unknown_ioctl_cmd)); 4256 4257 ASSERT_EQ(0, close(fd)); 4258 } 4259 4260 /* 4261 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right, 4262 * because they are not character or block devices. 4263 */ 4264 TEST_F_FORK(layout1, named_pipe_ioctl) 4265 { 4266 pid_t child_pid; 4267 int fd; 4268 const char *const path = file1_s1d1; 4269 4270 ASSERT_EQ(0, unlink(path)); 4271 ASSERT_EQ(0, mkfifo(path, 0600)); 4272 4273 /* Enables Landlock. */ 4274 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4275 4276 /* The child process opens the pipe for writing. */ 4277 child_pid = fork(); 4278 ASSERT_NE(-1, child_pid); 4279 if (child_pid == 0) { 4280 fd = open(path, O_WRONLY); 4281 close(fd); 4282 exit(0); 4283 } 4284 4285 fd = open(path, O_RDONLY); 4286 ASSERT_LE(0, fd); 4287 4288 /* FIONREAD is implemented by pipefifo_fops. */ 4289 EXPECT_EQ(0, test_fionread_ioctl(fd)); 4290 4291 ASSERT_EQ(0, close(fd)); 4292 ASSERT_EQ(0, unlink(path)); 4293 4294 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0)); 4295 } 4296 4297 /* 4298 * set_up_named_unix_server - Create a pathname unix socket 4299 * 4300 * If the socket type is not SOCK_DGRAM, also invoke listen(2). 4301 * 4302 * Return: The listening FD - it is the caller responsibility to close it. 4303 */ 4304 static int set_up_named_unix_server(struct __test_metadata *const _metadata, 4305 int type, const char *const path) 4306 { 4307 int fd; 4308 struct sockaddr_un addr = { 4309 .sun_family = AF_UNIX, 4310 }; 4311 4312 fd = socket(AF_UNIX, type, 0); 4313 ASSERT_LE(0, fd); 4314 4315 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4316 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4317 4318 ASSERT_EQ(0, bind(fd, (struct sockaddr *)&addr, sizeof(addr))); 4319 4320 if (type != SOCK_DGRAM) 4321 ASSERT_EQ(0, listen(fd, 10 /* qlen */)); 4322 return fd; 4323 } 4324 4325 /* 4326 * test_connect_named_unix - connect to the given named UNIX socket 4327 * 4328 * Return: The errno from connect(), or 0 4329 */ 4330 static int test_connect_named_unix(struct __test_metadata *const _metadata, 4331 int fd, const char *const path) 4332 { 4333 struct sockaddr_un addr = { 4334 .sun_family = AF_UNIX, 4335 }; 4336 4337 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4338 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4339 4340 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) 4341 return errno; 4342 return 0; 4343 } 4344 4345 /* For named UNIX domain sockets, no IOCTL restrictions apply. */ 4346 TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) 4347 { 4348 const char *const path = file1_s1d1; 4349 int srv_fd, cli_fd; 4350 4351 /* Sets up a server */ 4352 ASSERT_EQ(0, unlink(path)); 4353 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path); 4354 4355 /* Enables Landlock. */ 4356 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL); 4357 4358 /* Sets up a client connection to it */ 4359 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4360 ASSERT_LE(0, cli_fd); 4361 4362 ASSERT_EQ(0, test_connect_named_unix(_metadata, cli_fd, path)); 4363 4364 /* FIONREAD and other IOCTLs should not be forbidden. */ 4365 EXPECT_EQ(0, test_fionread_ioctl(cli_fd)); 4366 4367 EXPECT_EQ(0, close(cli_fd)); 4368 EXPECT_EQ(0, close(srv_fd)); 4369 } 4370 4371 /* clang-format off */ 4372 FIXTURE(ioctl) {}; 4373 4374 FIXTURE_SETUP(ioctl) {}; 4375 4376 FIXTURE_TEARDOWN(ioctl) {}; 4377 /* clang-format on */ 4378 4379 FIXTURE_VARIANT(ioctl) 4380 { 4381 const __u64 handled; 4382 const __u64 allowed; 4383 const mode_t open_mode; 4384 /* 4385 * FIONREAD is used as a characteristic device-specific IOCTL command. 4386 * It is implemented in fs/ioctl.c for regular files, 4387 * but we do not blanket-permit it for devices. 4388 */ 4389 const int expected_fionread_result; 4390 }; 4391 4392 /* clang-format off */ 4393 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) { 4394 /* clang-format on */ 4395 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4396 .allowed = 0, 4397 .open_mode = O_RDWR, 4398 .expected_fionread_result = EACCES, 4399 }; 4400 4401 /* clang-format off */ 4402 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) { 4403 /* clang-format on */ 4404 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4405 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4406 .open_mode = O_RDWR, 4407 .expected_fionread_result = 0, 4408 }; 4409 4410 /* clang-format off */ 4411 FIXTURE_VARIANT_ADD(ioctl, unhandled) { 4412 /* clang-format on */ 4413 .handled = LANDLOCK_ACCESS_FS_EXECUTE, 4414 .allowed = LANDLOCK_ACCESS_FS_EXECUTE, 4415 .open_mode = O_RDWR, 4416 .expected_fionread_result = 0, 4417 }; 4418 4419 TEST_F_FORK(ioctl, handle_dir_access_file) 4420 { 4421 const int flag = 0; 4422 const struct rule rules[] = { 4423 { 4424 .path = "/dev", 4425 .access = variant->allowed, 4426 }, 4427 {}, 4428 }; 4429 int fd; 4430 4431 /* Enables Landlock. */ 4432 enforce_fs(_metadata, variant->handled, rules); 4433 4434 fd = open("/dev/zero", variant->open_mode); 4435 ASSERT_LE(0, fd); 4436 4437 /* Checks that IOCTL commands return the expected errors. */ 4438 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd)); 4439 4440 /* Checks that unrestrictable commands are unrestricted. */ 4441 EXPECT_EQ(0, ioctl(fd, FIOCLEX)); 4442 EXPECT_EQ(0, ioctl(fd, FIONCLEX)); 4443 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag)); 4444 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag)); 4445 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag)); 4446 4447 ASSERT_EQ(0, close(fd)); 4448 } 4449 4450 TEST_F_FORK(ioctl, handle_dir_access_dir) 4451 { 4452 const int flag = 0; 4453 const struct rule rules[] = { 4454 { 4455 .path = "/dev", 4456 .access = variant->allowed, 4457 }, 4458 {}, 4459 }; 4460 int dir_fd; 4461 4462 /* Enables Landlock. */ 4463 enforce_fs(_metadata, variant->handled, rules); 4464 4465 /* 4466 * Ignore variant->open_mode for this test, as we intend to open a 4467 * directory. If the directory can not be opened, the variant is 4468 * infeasible to test with an opened directory. 4469 */ 4470 dir_fd = open("/dev", O_RDONLY); 4471 if (dir_fd < 0) 4472 return; 4473 4474 /* 4475 * Checks that IOCTL commands return the expected errors. 4476 * We do not use the expected values from the fixture here. 4477 * 4478 * When using IOCTL on a directory, no Landlock restrictions apply. 4479 */ 4480 EXPECT_EQ(0, test_fionread_ioctl(dir_fd)); 4481 4482 /* Checks that unrestrictable commands are unrestricted. */ 4483 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX)); 4484 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX)); 4485 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag)); 4486 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag)); 4487 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag)); 4488 4489 ASSERT_EQ(0, close(dir_fd)); 4490 } 4491 4492 TEST_F_FORK(ioctl, handle_file_access_file) 4493 { 4494 const int flag = 0; 4495 const struct rule rules[] = { 4496 { 4497 .path = "/dev/zero", 4498 .access = variant->allowed, 4499 }, 4500 {}, 4501 }; 4502 int fd; 4503 4504 /* Enables Landlock. */ 4505 enforce_fs(_metadata, variant->handled, rules); 4506 4507 fd = open("/dev/zero", variant->open_mode); 4508 ASSERT_LE(0, fd) 4509 { 4510 TH_LOG("Failed to open /dev/zero: %s", strerror(errno)); 4511 } 4512 4513 /* Checks that IOCTL commands return the expected errors. */ 4514 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd)); 4515 4516 /* Checks that unrestrictable commands are unrestricted. */ 4517 EXPECT_EQ(0, ioctl(fd, FIOCLEX)); 4518 EXPECT_EQ(0, ioctl(fd, FIONCLEX)); 4519 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag)); 4520 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag)); 4521 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag)); 4522 4523 ASSERT_EQ(0, close(fd)); 4524 } 4525 4526 /* 4527 * test_sendto_named_unix - sendto to the given named UNIX socket 4528 * 4529 * sendto() is equivalent to sendmsg() in this respect. 4530 * 4531 * Return: The errno from sendto(), or 0 4532 */ 4533 static int test_sendto_named_unix(struct __test_metadata *const _metadata, 4534 int fd, const char *const path) 4535 { 4536 static const char buf[] = "dummy"; 4537 struct sockaddr_un addr = { 4538 .sun_family = AF_UNIX, 4539 }; 4540 4541 ASSERT_LT(strlen(path), sizeof(addr.sun_path)); 4542 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); 4543 4544 if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, 4545 sizeof(addr)) == -1) 4546 return errno; 4547 return 0; 4548 } 4549 4550 /* clang-format off */ 4551 FIXTURE(scoped_domains) {}; 4552 /* clang-format on */ 4553 4554 #include "scoped_base_variants.h" 4555 4556 FIXTURE_SETUP(scoped_domains) 4557 { 4558 drop_caps(_metadata); 4559 }; 4560 4561 FIXTURE_TEARDOWN(scoped_domains) 4562 { 4563 } 4564 4565 /* 4566 * Flags for test_connect_to_parent and test_connect_to_child: 4567 * 4568 * USE_SENDTO: Use sendto() instead of connect() (for SOCK_DGRAM only) 4569 * ENFORCE_ALL: Enforce a Landlock domain even when the variant says 4570 * we shouldn't. We enforce a domain where the path is allow-listed, 4571 * and expect the behavior to be the same as if none was used. 4572 */ 4573 #define USE_SENDTO (1 << 0) 4574 #define ENFORCE_ALL (1 << 1) 4575 4576 static void test_connect_to_parent(struct __test_metadata *const _metadata, 4577 const FIXTURE_VARIANT(scoped_domains) * 4578 variant, 4579 int sock_type, int flags) 4580 { 4581 const char *const path = "sock"; 4582 const struct rule rules[] = { 4583 { 4584 .path = ".", 4585 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4586 }, 4587 {}, 4588 }; 4589 int cli_fd, srv_fd, res, status; 4590 pid_t child_pid; 4591 int readiness_pipe[2]; 4592 char buf[1]; 4593 4594 if (variant->domain_both) 4595 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4596 else if (flags & ENFORCE_ALL) 4597 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4598 4599 unlink(path); 4600 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC)); 4601 4602 child_pid = fork(); 4603 ASSERT_LE(0, child_pid); 4604 4605 if (child_pid == 0) { 4606 if (variant->domain_child) 4607 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4608 NULL); 4609 else if (flags & ENFORCE_ALL) 4610 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4611 rules); 4612 4613 /* Wait for server to be available. */ 4614 EXPECT_EQ(0, close(readiness_pipe[1])); 4615 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1)); 4616 EXPECT_EQ(0, close(readiness_pipe[0])); 4617 4618 /* Talk to server. */ 4619 cli_fd = socket(AF_UNIX, sock_type, 0); 4620 ASSERT_LE(0, cli_fd); 4621 4622 if (flags & USE_SENDTO) 4623 res = test_sendto_named_unix(_metadata, cli_fd, path); 4624 else 4625 res = test_connect_named_unix(_metadata, cli_fd, path); 4626 4627 EXPECT_EQ(variant->domain_child ? EACCES : 0, res); 4628 4629 /* Clean up. */ 4630 EXPECT_EQ(0, close(cli_fd)); 4631 4632 _exit(_metadata->exit_code); 4633 return; 4634 } 4635 4636 if (variant->domain_parent) 4637 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4638 else if (flags & ENFORCE_ALL) 4639 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4640 4641 srv_fd = set_up_named_unix_server(_metadata, sock_type, path); 4642 4643 /* Tell the child that it can connect. */ 4644 EXPECT_EQ(0, close(readiness_pipe[0])); 4645 EXPECT_EQ(sizeof(buf), write(readiness_pipe[1], buf, sizeof(buf))); 4646 EXPECT_EQ(0, close(readiness_pipe[1])); 4647 4648 /* Wait for child. */ 4649 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4650 EXPECT_EQ(1, WIFEXITED(status)); 4651 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 4652 4653 /* Clean up. */ 4654 EXPECT_EQ(0, close(srv_fd)); 4655 EXPECT_EQ(0, unlink(path)); 4656 } 4657 4658 static void test_connect_to_child(struct __test_metadata *const _metadata, 4659 const FIXTURE_VARIANT(scoped_domains) * 4660 variant, 4661 int sock_type, int flags) 4662 { 4663 const char *const path = "sock"; 4664 const struct rule rules[] = { 4665 { 4666 .path = ".", 4667 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4668 }, 4669 {}, 4670 }; 4671 int readiness_pipe[2]; 4672 int shutdown_pipe[2]; 4673 int cli_fd, srv_fd, res, status; 4674 pid_t child_pid; 4675 char buf[1]; 4676 4677 if (variant->domain_both) 4678 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4679 else if (flags & ENFORCE_ALL) 4680 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4681 4682 unlink(path); 4683 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC)); 4684 ASSERT_EQ(0, pipe2(shutdown_pipe, O_CLOEXEC)); 4685 4686 child_pid = fork(); 4687 ASSERT_LE(0, child_pid); 4688 4689 if (child_pid == 0) { 4690 if (variant->domain_child) 4691 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4692 NULL); 4693 else if (flags & ENFORCE_ALL) 4694 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, 4695 rules); 4696 4697 srv_fd = set_up_named_unix_server(_metadata, sock_type, path); 4698 4699 /* Tell the parent that it can connect. */ 4700 EXPECT_EQ(0, close(readiness_pipe[0])); 4701 EXPECT_EQ(sizeof(buf), 4702 write(readiness_pipe[1], buf, sizeof(buf))); 4703 EXPECT_EQ(0, close(readiness_pipe[1])); 4704 4705 /* Wait until it is time to shut down. */ 4706 EXPECT_EQ(0, close(shutdown_pipe[1])); 4707 EXPECT_EQ(1, read(shutdown_pipe[0], &buf, 1)); 4708 EXPECT_EQ(0, close(shutdown_pipe[0])); 4709 4710 /* Cleanup */ 4711 EXPECT_EQ(0, close(srv_fd)); 4712 EXPECT_EQ(0, unlink(path)); 4713 4714 _exit(_metadata->exit_code); 4715 return; 4716 } 4717 4718 if (variant->domain_parent) 4719 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4720 else if (flags & ENFORCE_ALL) 4721 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules); 4722 4723 /* Wait for server to be available. */ 4724 EXPECT_EQ(0, close(readiness_pipe[1])); 4725 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1)); 4726 EXPECT_EQ(0, close(readiness_pipe[0])); 4727 4728 /* Talk to server. */ 4729 cli_fd = socket(AF_UNIX, sock_type, 0); 4730 ASSERT_LE(0, cli_fd); 4731 4732 if (flags & USE_SENDTO) 4733 res = test_sendto_named_unix(_metadata, cli_fd, path); 4734 else 4735 res = test_connect_named_unix(_metadata, cli_fd, path); 4736 4737 EXPECT_EQ(variant->domain_parent ? EACCES : 0, res); 4738 4739 /* Clean up. */ 4740 EXPECT_EQ(0, close(cli_fd)); 4741 4742 /* Tell the server to shut down. */ 4743 EXPECT_EQ(0, close(shutdown_pipe[0])); 4744 EXPECT_EQ(sizeof(buf), write(shutdown_pipe[1], buf, sizeof(buf))); 4745 EXPECT_EQ(0, close(shutdown_pipe[1])); 4746 4747 /* Wait for child. */ 4748 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4749 EXPECT_EQ(1, WIFEXITED(status)); 4750 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 4751 } 4752 4753 TEST_F(scoped_domains, unix_stream_connect_to_parent) 4754 { 4755 test_connect_to_parent(_metadata, variant, SOCK_STREAM, 0); 4756 } 4757 4758 TEST_F(scoped_domains, unix_dgram_connect_to_parent) 4759 { 4760 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, 0); 4761 } 4762 4763 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent) 4764 { 4765 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, USE_SENDTO); 4766 } 4767 4768 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent) 4769 { 4770 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, 0); 4771 } 4772 4773 TEST_F(scoped_domains, unix_stream_connect_to_parent_full) 4774 { 4775 test_connect_to_parent(_metadata, variant, SOCK_STREAM, ENFORCE_ALL); 4776 } 4777 4778 TEST_F(scoped_domains, unix_dgram_connect_to_parent_full) 4779 { 4780 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL); 4781 } 4782 4783 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent_full) 4784 { 4785 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, 4786 USE_SENDTO | ENFORCE_ALL); 4787 } 4788 4789 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent_full) 4790 { 4791 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL); 4792 } 4793 4794 TEST_F(scoped_domains, unix_stream_connect_to_child) 4795 { 4796 test_connect_to_child(_metadata, variant, SOCK_STREAM, 0); 4797 } 4798 4799 TEST_F(scoped_domains, unix_dgram_connect_to_child) 4800 { 4801 test_connect_to_child(_metadata, variant, SOCK_DGRAM, 0); 4802 } 4803 4804 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child) 4805 { 4806 test_connect_to_child(_metadata, variant, SOCK_DGRAM, USE_SENDTO); 4807 } 4808 4809 TEST_F(scoped_domains, unix_seqpacket_connect_to_child) 4810 { 4811 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, 0); 4812 } 4813 4814 TEST_F(scoped_domains, unix_stream_connect_to_child_full) 4815 { 4816 test_connect_to_child(_metadata, variant, SOCK_STREAM, ENFORCE_ALL); 4817 } 4818 4819 TEST_F(scoped_domains, unix_dgram_connect_to_child_full) 4820 { 4821 test_connect_to_child(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL); 4822 } 4823 4824 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child_full) 4825 { 4826 test_connect_to_child(_metadata, variant, SOCK_DGRAM, 4827 USE_SENDTO | ENFORCE_ALL); 4828 } 4829 4830 TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full) 4831 { 4832 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL); 4833 } 4834 4835 #undef USE_SENDTO 4836 #undef ENFORCE_ALL 4837 4838 static void read_core_pattern(struct __test_metadata *const _metadata, 4839 char *buf, size_t buf_size) 4840 { 4841 int fd; 4842 ssize_t ret; 4843 4844 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY | O_CLOEXEC); 4845 ASSERT_LE(0, fd); 4846 4847 ret = read(fd, buf, buf_size - 1); 4848 ASSERT_LE(0, ret); 4849 EXPECT_EQ(0, close(fd)); 4850 4851 buf[ret] = '\0'; 4852 } 4853 4854 static void set_core_pattern(struct __test_metadata *const _metadata, 4855 const char *pattern) 4856 { 4857 int fd; 4858 size_t len = strlen(pattern); 4859 4860 /* 4861 * Writing to /proc/sys/kernel/core_pattern requires EUID 0 because 4862 * sysctl_perm() checks that, ignoring capabilities like 4863 * CAP_SYS_ADMIN or CAP_DAC_OVERRIDE. 4864 * 4865 * Switching EUID clears the dumpable flag, which must be restored 4866 * afterwards to allow coredumps. 4867 */ 4868 set_cap(_metadata, CAP_SETUID); 4869 ASSERT_EQ(0, seteuid(0)); 4870 clear_cap(_metadata, CAP_SETUID); 4871 4872 fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC); 4873 ASSERT_LE(0, fd) 4874 { 4875 TH_LOG("Failed to open core_pattern for writing: %s", 4876 strerror(errno)); 4877 } 4878 4879 ASSERT_EQ(len, write(fd, pattern, len)); 4880 EXPECT_EQ(0, close(fd)); 4881 4882 set_cap(_metadata, CAP_SETUID); 4883 ASSERT_EQ(0, seteuid(getuid())); 4884 clear_cap(_metadata, CAP_SETUID); 4885 4886 /* Restore dumpable flag cleared by seteuid(). */ 4887 ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)); 4888 } 4889 4890 FIXTURE(coredump) 4891 { 4892 char original_core_pattern[256]; 4893 }; 4894 4895 FIXTURE_SETUP(coredump) 4896 { 4897 disable_caps(_metadata); 4898 read_core_pattern(_metadata, self->original_core_pattern, 4899 sizeof(self->original_core_pattern)); 4900 } 4901 4902 FIXTURE_TEARDOWN_PARENT(coredump) 4903 { 4904 set_core_pattern(_metadata, self->original_core_pattern); 4905 } 4906 4907 /* 4908 * Test that even when a process is restricted with 4909 * LANDLOCK_ACCESS_FS_RESOLVE_UNIX, the kernel can still initiate a connection 4910 * to the coredump socket on the processes' behalf. 4911 */ 4912 TEST_F_FORK(coredump, socket_not_restricted) 4913 { 4914 static const char core_pattern[] = "@/tmp/landlock_coredump_test.sock"; 4915 const char *const sock_path = core_pattern + 1; 4916 int srv_fd, conn_fd, status; 4917 pid_t child_pid; 4918 struct ucred cred; 4919 socklen_t cred_len = sizeof(cred); 4920 char buf[4096]; 4921 4922 /* Set up the coredump server socket. */ 4923 unlink(sock_path); 4924 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, sock_path); 4925 4926 /* Point coredumps at our socket. */ 4927 set_core_pattern(_metadata, core_pattern); 4928 4929 /* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */ 4930 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL); 4931 4932 /* Fork a child that crashes. */ 4933 child_pid = fork(); 4934 ASSERT_LE(0, child_pid); 4935 if (child_pid == 0) { 4936 struct rlimit rl = { 4937 .rlim_cur = RLIM_INFINITY, 4938 .rlim_max = RLIM_INFINITY, 4939 }; 4940 4941 ASSERT_EQ(0, setrlimit(RLIMIT_CORE, &rl)); 4942 4943 /* Crash on purpose. */ 4944 kill(getpid(), SIGSEGV); 4945 _exit(1); 4946 } 4947 4948 /* 4949 * Accept the coredump connection. If Landlock incorrectly denies the 4950 * kernel's coredump connect, accept() will block forever, so the test 4951 * would time out. 4952 */ 4953 conn_fd = accept(srv_fd, NULL, NULL); 4954 ASSERT_LE(0, conn_fd); 4955 4956 /* Check that the connection came from the crashing child. */ 4957 ASSERT_EQ(0, getsockopt(conn_fd, SOL_SOCKET, SO_PEERCRED, &cred, 4958 &cred_len)); 4959 EXPECT_EQ(child_pid, cred.pid); 4960 4961 /* Drain the coredump data so the kernel can finish. */ 4962 while (read(conn_fd, buf, sizeof(buf)) > 0) 4963 ; 4964 4965 EXPECT_EQ(0, close(conn_fd)); 4966 4967 /* Wait for the child and verify it coredumped. */ 4968 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 4969 ASSERT_TRUE(WIFSIGNALED(status)); 4970 ASSERT_TRUE(WCOREDUMP(status)); 4971 4972 EXPECT_EQ(0, close(srv_fd)); 4973 EXPECT_EQ(0, unlink(sock_path)); 4974 } 4975 4976 /* clang-format off */ 4977 FIXTURE(layout1_bind) {}; 4978 /* clang-format on */ 4979 4980 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 4981 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 4982 static const char bind_file2_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f2"; 4983 4984 /* Move targets for disconnected path tests. */ 4985 static const char dir_s4d1[] = TMP_DIR "/s4d1"; 4986 static const char file1_s4d1[] = TMP_DIR "/s4d1/f1"; 4987 static const char file2_s4d1[] = TMP_DIR "/s4d1/f2"; 4988 static const char dir_s4d2[] = TMP_DIR "/s4d1/s4d2"; 4989 static const char file1_s4d2[] = TMP_DIR "/s4d1/s4d2/f1"; 4990 static const char file1_name[] = "f1"; 4991 static const char file2_name[] = "f2"; 4992 4993 FIXTURE_SETUP(layout1_bind) 4994 { 4995 prepare_layout(_metadata); 4996 4997 create_layout1(_metadata); 4998 4999 set_cap(_metadata, CAP_SYS_ADMIN); 5000 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 5001 clear_cap(_metadata, CAP_SYS_ADMIN); 5002 } 5003 5004 FIXTURE_TEARDOWN_PARENT(layout1_bind) 5005 { 5006 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 5007 5008 remove_path(file1_s4d1); 5009 remove_path(file2_s4d1); 5010 5011 remove_layout1(_metadata); 5012 5013 cleanup_layout(_metadata); 5014 } 5015 5016 /* 5017 * layout1_bind hierarchy: 5018 * 5019 * tmp 5020 * ├── s1d1 5021 * │ ├── f1 5022 * │ ├── f2 5023 * │ └── s1d2 5024 * │ ├── f1 5025 * │ ├── f2 5026 * │ └── s1d3 [disconnected by path_disconnected] 5027 * │ ├── f1 5028 * │ └── f2 5029 * ├── s2d1 5030 * │ ├── f1 5031 * │ └── s2d2 [bind mount from s1d2] 5032 * │ ├── f1 5033 * │ ├── f2 5034 * │ └── s1d3 5035 * │ ├── f1 5036 * │ └── f2 5037 * ├── s3d1 5038 * │ └── s3d2 5039 * │ └── s3d3 5040 * └── s4d1 [renamed from s1d3 by path_disconnected] 5041 * ├── f1 5042 * ├── f2 5043 * └── s4d2 5044 * └── f1 5045 */ 5046 5047 TEST_F_FORK(layout1_bind, no_restriction) 5048 { 5049 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 5050 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 5051 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 5052 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5053 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 5054 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5055 5056 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 5057 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 5058 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 5059 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 5060 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 5061 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 5062 5063 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 5064 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 5065 5066 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 5067 } 5068 5069 TEST_F_FORK(layout1_bind, same_content_same_file) 5070 { 5071 /* 5072 * Sets access right on parent directories of both source and 5073 * destination mount points. 5074 */ 5075 const struct rule layer1_parent[] = { 5076 { 5077 .path = dir_s1d1, 5078 .access = ACCESS_RO, 5079 }, 5080 { 5081 .path = dir_s2d1, 5082 .access = ACCESS_RW, 5083 }, 5084 {}, 5085 }; 5086 /* 5087 * Sets access rights on the same bind-mounted directories. The result 5088 * should be ACCESS_RW for both directories, but not both hierarchies 5089 * because of the first layer. 5090 */ 5091 const struct rule layer2_mount_point[] = { 5092 { 5093 .path = dir_s1d2, 5094 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5095 }, 5096 { 5097 .path = dir_s2d2, 5098 .access = ACCESS_RW, 5099 }, 5100 {}, 5101 }; 5102 /* Only allow read-access to the s1d3 hierarchies. */ 5103 const struct rule layer3_source[] = { 5104 { 5105 .path = dir_s1d3, 5106 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5107 }, 5108 {}, 5109 }; 5110 /* Removes all access rights. */ 5111 const struct rule layer4_destination[] = { 5112 { 5113 .path = bind_file1_s1d3, 5114 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 5115 }, 5116 {}, 5117 }; 5118 5119 /* Sets rules for the parent directories. */ 5120 enforce_fs(_metadata, ACCESS_RW, layer1_parent); 5121 5122 /* Checks source hierarchy. */ 5123 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 5124 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 5125 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 5126 5127 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5128 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 5129 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 5130 5131 /* Checks destination hierarchy. */ 5132 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 5133 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 5134 5135 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 5136 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 5137 5138 /* Sets rules for the mount points. */ 5139 enforce_fs(_metadata, ACCESS_RW, layer2_mount_point); 5140 5141 /* Checks source hierarchy. */ 5142 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 5143 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 5144 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 5145 5146 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5147 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 5148 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 5149 5150 /* Checks destination hierarchy. */ 5151 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 5152 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 5153 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 5154 5155 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 5156 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 5157 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 5158 5159 /* Sets a (shared) rule only on the source. */ 5160 enforce_fs(_metadata, ACCESS_RW, layer3_source); 5161 5162 /* Checks source hierarchy. */ 5163 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 5164 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 5165 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 5166 5167 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5168 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 5169 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 5170 5171 /* Checks destination hierarchy. */ 5172 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 5173 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 5174 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 5175 5176 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 5177 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 5178 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 5179 5180 /* Sets a (shared) rule only on the destination. */ 5181 enforce_fs(_metadata, ACCESS_RW, layer4_destination); 5182 5183 /* Checks source hierarchy. */ 5184 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 5185 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 5186 5187 /* Checks destination hierarchy. */ 5188 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 5189 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 5190 } 5191 5192 TEST_F_FORK(layout1_bind, reparent_cross_mount) 5193 { 5194 const struct rule layer1[] = { 5195 { 5196 /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 5197 .path = dir_s2d1, 5198 .access = LANDLOCK_ACCESS_FS_REFER, 5199 }, 5200 { 5201 .path = bind_dir_s1d3, 5202 .access = LANDLOCK_ACCESS_FS_EXECUTE, 5203 }, 5204 {}, 5205 }; 5206 5207 enforce_fs(_metadata, 5208 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, 5209 layer1); 5210 5211 /* Checks basic denied move. */ 5212 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 5213 ASSERT_EQ(EXDEV, errno); 5214 5215 /* Checks real cross-mount move (Landlock is not involved). */ 5216 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 5217 ASSERT_EQ(EXDEV, errno); 5218 5219 /* Checks move that will give more accesses. */ 5220 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 5221 ASSERT_EQ(EXDEV, errno); 5222 5223 /* Checks legitimate downgrade move. */ 5224 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 5225 } 5226 5227 /* 5228 * Make sure access to file through a disconnected path works as expected. 5229 * This test moves s1d3 to s4d1. 5230 */ 5231 TEST_F_FORK(layout1_bind, path_disconnected) 5232 { 5233 const struct rule layer1_allow_all[] = { 5234 { 5235 .path = TMP_DIR, 5236 .access = ACCESS_ALL, 5237 }, 5238 {}, 5239 }; 5240 const struct rule layer2_allow_just_f1[] = { 5241 { 5242 .path = file1_s1d3, 5243 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5244 }, 5245 {}, 5246 }; 5247 const struct rule layer3_only_s1d2[] = { 5248 { 5249 .path = dir_s1d2, 5250 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5251 }, 5252 {}, 5253 }; 5254 5255 /* Landlock should not deny access just because it is disconnected. */ 5256 int ruleset_fd_l1 = 5257 create_ruleset(_metadata, ACCESS_ALL, layer1_allow_all); 5258 5259 /* Creates the new ruleset now before we move the dir containing the file. */ 5260 int ruleset_fd_l2 = 5261 create_ruleset(_metadata, ACCESS_RW, layer2_allow_just_f1); 5262 int ruleset_fd_l3 = 5263 create_ruleset(_metadata, ACCESS_RW, layer3_only_s1d2); 5264 int bind_s1d3_fd; 5265 5266 enforce_ruleset(_metadata, ruleset_fd_l1); 5267 EXPECT_EQ(0, close(ruleset_fd_l1)); 5268 5269 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5270 ASSERT_LE(0, bind_s1d3_fd); 5271 5272 /* Tests access is possible before we move. */ 5273 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5274 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5275 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 5276 5277 /* Makes it disconnected. */ 5278 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5279 { 5280 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5281 strerror(errno)); 5282 } 5283 5284 /* Tests that access is still possible. */ 5285 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5286 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5287 5288 /* 5289 * Tests that ".." is not possible (not because of Landlock, but just 5290 * because it's disconnected). 5291 */ 5292 EXPECT_EQ(ENOENT, 5293 test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 5294 5295 /* This should still work with a narrower rule. */ 5296 enforce_ruleset(_metadata, ruleset_fd_l2); 5297 EXPECT_EQ(0, close(ruleset_fd_l2)); 5298 5299 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)); 5300 /* 5301 * Accessing a file through a disconnected file descriptor can still be 5302 * allowed by a rule tied to this file, even if it is no longer visible in 5303 * its mount point. 5304 */ 5305 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5306 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5307 5308 enforce_ruleset(_metadata, ruleset_fd_l3); 5309 EXPECT_EQ(0, close(ruleset_fd_l3)); 5310 5311 EXPECT_EQ(EACCES, test_open(file1_s4d1, O_RDONLY)); 5312 /* 5313 * Accessing a file through a disconnected file descriptor can still be 5314 * allowed by a rule tied to the original mount point, even if it is no 5315 * longer visible in its mount point. 5316 */ 5317 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5318 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5319 } 5320 5321 /* 5322 * Test that renameat with disconnected paths works under Landlock. This test 5323 * moves s1d3 to s4d2, so that we can have a rule allowing refers on the move 5324 * target's immediate parent. 5325 */ 5326 TEST_F_FORK(layout1_bind, path_disconnected_rename) 5327 { 5328 const struct rule layer1[] = { 5329 { 5330 .path = dir_s1d2, 5331 .access = LANDLOCK_ACCESS_FS_REFER | 5332 LANDLOCK_ACCESS_FS_MAKE_DIR | 5333 LANDLOCK_ACCESS_FS_REMOVE_DIR | 5334 LANDLOCK_ACCESS_FS_MAKE_REG | 5335 LANDLOCK_ACCESS_FS_REMOVE_FILE | 5336 LANDLOCK_ACCESS_FS_READ_FILE, 5337 }, 5338 { 5339 .path = dir_s4d1, 5340 .access = LANDLOCK_ACCESS_FS_REFER | 5341 LANDLOCK_ACCESS_FS_MAKE_DIR | 5342 LANDLOCK_ACCESS_FS_REMOVE_DIR | 5343 LANDLOCK_ACCESS_FS_MAKE_REG | 5344 LANDLOCK_ACCESS_FS_REMOVE_FILE | 5345 LANDLOCK_ACCESS_FS_READ_FILE, 5346 }, 5347 {} 5348 }; 5349 5350 /* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */ 5351 const struct rule layer2_only_s1d2[] = { 5352 { 5353 .path = dir_s1d2, 5354 .access = LANDLOCK_ACCESS_FS_READ_FILE, 5355 }, 5356 {}, 5357 }; 5358 int ruleset_fd_l1, ruleset_fd_l2; 5359 pid_t child_pid; 5360 int bind_s1d3_fd, status; 5361 5362 ASSERT_EQ(0, mkdir(dir_s4d1, 0755)) 5363 { 5364 TH_LOG("Failed to create %s: %s", dir_s4d1, strerror(errno)); 5365 } 5366 ruleset_fd_l1 = create_ruleset(_metadata, ACCESS_ALL, layer1); 5367 ruleset_fd_l2 = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 5368 layer2_only_s1d2); 5369 5370 enforce_ruleset(_metadata, ruleset_fd_l1); 5371 EXPECT_EQ(0, close(ruleset_fd_l1)); 5372 5373 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5374 ASSERT_LE(0, bind_s1d3_fd); 5375 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5376 5377 /* Tests ENOENT priority over EACCES for disconnected directory. */ 5378 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5379 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5380 { 5381 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5382 strerror(errno)); 5383 } 5384 EXPECT_EQ(ENOENT, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5385 5386 /* 5387 * The file is no longer under s1d2 but we should still be able to access it 5388 * with layer 2 because its mount point is evaluated as the first valid 5389 * directory because it was initially a parent. Do a fork to test this so 5390 * we don't prevent ourselves from renaming it back later. 5391 */ 5392 child_pid = fork(); 5393 ASSERT_LE(0, child_pid); 5394 if (child_pid == 0) { 5395 enforce_ruleset(_metadata, ruleset_fd_l2); 5396 EXPECT_EQ(0, close(ruleset_fd_l2)); 5397 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5398 EXPECT_EQ(EACCES, test_open(file1_s4d2, O_RDONLY)); 5399 5400 /* 5401 * Tests that access widening checks indeed prevents us from renaming it 5402 * back. 5403 */ 5404 EXPECT_EQ(-1, rename(dir_s4d2, dir_s1d3)); 5405 EXPECT_EQ(EXDEV, errno); 5406 5407 /* 5408 * Including through the now disconnected fd (but it should return 5409 * EXDEV). 5410 */ 5411 EXPECT_EQ(-1, renameat(bind_s1d3_fd, file1_name, AT_FDCWD, 5412 file1_s2d2)); 5413 EXPECT_EQ(EXDEV, errno); 5414 _exit(_metadata->exit_code); 5415 return; 5416 } 5417 5418 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5419 EXPECT_EQ(1, WIFEXITED(status)); 5420 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5421 5422 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5423 { 5424 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d1, dir_s1d3, 5425 strerror(errno)); 5426 } 5427 5428 /* Now checks that we can access it under l2. */ 5429 child_pid = fork(); 5430 ASSERT_LE(0, child_pid); 5431 if (child_pid == 0) { 5432 enforce_ruleset(_metadata, ruleset_fd_l2); 5433 EXPECT_EQ(0, close(ruleset_fd_l2)); 5434 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5435 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5436 _exit(_metadata->exit_code); 5437 return; 5438 } 5439 5440 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5441 EXPECT_EQ(1, WIFEXITED(status)); 5442 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5443 5444 /* 5445 * Also test that we can rename via a disconnected path. We move the 5446 * dir back to the disconnected place first, then we rename file1 to 5447 * file2 through our dir fd. 5448 */ 5449 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5450 { 5451 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5452 strerror(errno)); 5453 } 5454 ASSERT_EQ(0, 5455 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)) 5456 { 5457 TH_LOG("Failed to rename %s to %s within disconnected %s: %s", 5458 file1_name, file2_name, bind_dir_s1d3, strerror(errno)); 5459 } 5460 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5461 ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2)) 5462 { 5463 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5464 file2_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5465 } 5466 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 5467 EXPECT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5468 5469 /* Move it back using the disconnected path as the target. */ 5470 ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name)) 5471 { 5472 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5473 file1_s1d2, file1_name, bind_dir_s1d3, strerror(errno)); 5474 } 5475 5476 /* Now make it connected again. */ 5477 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5478 { 5479 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d2, dir_s1d3, 5480 strerror(errno)); 5481 } 5482 5483 /* Checks again that we can access it under l2. */ 5484 enforce_ruleset(_metadata, ruleset_fd_l2); 5485 EXPECT_EQ(0, close(ruleset_fd_l2)); 5486 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5487 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5488 } 5489 5490 /* 5491 * Test that linkat(2) with disconnected paths works under Landlock. This 5492 * test moves s1d3 to s4d1. 5493 */ 5494 TEST_F_FORK(layout1_bind, path_disconnected_link) 5495 { 5496 /* Ruleset to be applied after renaming s1d3 to s4d1. */ 5497 const struct rule layer1[] = { 5498 { 5499 .path = dir_s4d1, 5500 .access = LANDLOCK_ACCESS_FS_REFER | 5501 LANDLOCK_ACCESS_FS_READ_FILE | 5502 LANDLOCK_ACCESS_FS_MAKE_REG | 5503 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5504 }, 5505 { 5506 .path = dir_s2d2, 5507 .access = LANDLOCK_ACCESS_FS_REFER | 5508 LANDLOCK_ACCESS_FS_READ_FILE | 5509 LANDLOCK_ACCESS_FS_MAKE_REG | 5510 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5511 }, 5512 {} 5513 }; 5514 int bind_s1d3_fd; 5515 5516 /* Removes unneeded files created by layout1, otherwise it will EEXIST. */ 5517 ASSERT_EQ(0, unlink(file1_s1d2)); 5518 ASSERT_EQ(0, unlink(file2_s1d3)); 5519 5520 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5521 ASSERT_LE(0, bind_s1d3_fd); 5522 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5523 5524 /* Disconnects bind_s1d3_fd. */ 5525 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5526 { 5527 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5528 strerror(errno)); 5529 } 5530 5531 /* Need this later to test different parent link. */ 5532 ASSERT_EQ(0, mkdir(dir_s4d2, 0755)) 5533 { 5534 TH_LOG("Failed to create %s: %s", dir_s4d2, strerror(errno)); 5535 } 5536 5537 enforce_fs(_metadata, ACCESS_ALL, layer1); 5538 5539 /* From disconnected to connected. */ 5540 ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0)) 5541 { 5542 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5543 file1_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5544 } 5545 5546 /* Tests that we can access via the new link... */ 5547 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)) 5548 { 5549 TH_LOG("Failed to open newly linked %s: %s", file1_s2d2, 5550 strerror(errno)); 5551 } 5552 5553 /* ...as well as the old one. */ 5554 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5555 { 5556 TH_LOG("Failed to open original %s: %s", file1_s4d1, 5557 strerror(errno)); 5558 } 5559 5560 /* From connected to disconnected. */ 5561 ASSERT_EQ(0, unlink(file1_s4d1)); 5562 ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0)) 5563 { 5564 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5565 file1_s2d2, file2_name, bind_dir_s1d3, strerror(errno)); 5566 } 5567 EXPECT_EQ(0, test_open(file2_s4d1, O_RDONLY)); 5568 ASSERT_EQ(0, unlink(file1_s2d2)); 5569 5570 /* From disconnected to disconnected (same parent). */ 5571 ASSERT_EQ(0, 5572 linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0)) 5573 { 5574 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5575 file2_name, file1_name, bind_dir_s1d3, strerror(errno)); 5576 } 5577 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5578 { 5579 TH_LOG("Failed to open newly linked %s: %s", file1_s4d1, 5580 strerror(errno)); 5581 } 5582 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)) 5583 { 5584 TH_LOG("Failed to open %s through newly created link under disconnected path: %s", 5585 file1_name, strerror(errno)); 5586 } 5587 ASSERT_EQ(0, unlink(file2_s4d1)); 5588 5589 /* From disconnected to disconnected (different parent). */ 5590 ASSERT_EQ(0, 5591 linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0)) 5592 { 5593 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5594 file1_name, "s4d2/f1", bind_dir_s1d3, strerror(errno)); 5595 } 5596 EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY)) 5597 { 5598 TH_LOG("Failed to open %s after link: %s", file1_s4d2, 5599 strerror(errno)); 5600 } 5601 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY)) 5602 { 5603 TH_LOG("Failed to open %s through disconnected path after link: %s", 5604 "s4d2/f1", strerror(errno)); 5605 } 5606 } 5607 5608 /* 5609 * layout4_disconnected_leafs with bind mount and renames: 5610 * 5611 * tmp 5612 * ├── s1d1 5613 * │ └── s1d2 [source of the bind mount] 5614 * │ ├── s1d31 5615 * │ │ └── s1d41 [now renamed beneath s3d1] 5616 * │ │ ├── f1 5617 * │ │ └── f2 5618 * │ └── s1d32 5619 * │ └── s1d42 [now renamed beneath s4d1] 5620 * │ ├── f3 5621 * │ └── f4 5622 * ├── s2d1 5623 * │ └── s2d2 [bind mount of s1d2] 5624 * │ ├── s1d31 5625 * │ │ └── s1d41 [opened FD, now renamed beneath s3d1] 5626 * │ │ ├── f1 5627 * │ │ └── f2 5628 * │ └── s1d32 5629 * │ └── s1d42 [opened FD, now renamed beneath s4d1] 5630 * │ ├── f3 5631 * │ └── f4 5632 * ├── s3d1 5633 * │ └── s1d41 [renamed here] 5634 * │ ├── f1 5635 * │ └── f2 5636 * └── s4d1 5637 * └── s1d42 [renamed here] 5638 * ├── f3 5639 * └── f4 5640 */ 5641 /* clang-format off */ 5642 FIXTURE(layout4_disconnected_leafs) { 5643 int s2d2_fd; 5644 }; 5645 /* clang-format on */ 5646 5647 FIXTURE_SETUP(layout4_disconnected_leafs) 5648 { 5649 prepare_layout(_metadata); 5650 5651 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1"); 5652 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2"); 5653 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3"); 5654 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4"); 5655 create_directory(_metadata, TMP_DIR "/s2d1/s2d2"); 5656 create_directory(_metadata, TMP_DIR "/s3d1"); 5657 create_directory(_metadata, TMP_DIR "/s4d1"); 5658 5659 self->s2d2_fd = 5660 open(TMP_DIR "/s2d1/s2d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 5661 ASSERT_LE(0, self->s2d2_fd); 5662 5663 set_cap(_metadata, CAP_SYS_ADMIN); 5664 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2", NULL, 5665 MS_BIND, NULL)); 5666 clear_cap(_metadata, CAP_SYS_ADMIN); 5667 } 5668 5669 FIXTURE_TEARDOWN_PARENT(layout4_disconnected_leafs) 5670 { 5671 /* umount(TMP_DIR "/s2d1") is handled by namespace lifetime. */ 5672 5673 /* Removes files after renames. */ 5674 remove_path(TMP_DIR "/s3d1/s1d41/f1"); 5675 remove_path(TMP_DIR "/s3d1/s1d41/f2"); 5676 remove_path(TMP_DIR "/s4d1/s1d42/f1"); 5677 remove_path(TMP_DIR "/s4d1/s1d42/f3"); 5678 remove_path(TMP_DIR "/s4d1/s1d42/f4"); 5679 remove_path(TMP_DIR "/s4d1/s1d42/f5"); 5680 5681 cleanup_layout(_metadata); 5682 } 5683 5684 FIXTURE_VARIANT(layout4_disconnected_leafs) 5685 { 5686 /* 5687 * Parent of the bind mount source. It should always be ignored when 5688 * testing against files under the s1d41 or s1d42 disconnected directories. 5689 */ 5690 const __u64 allowed_s1d1; 5691 /* 5692 * Source of bind mount (to s2d2). It should always be enforced when 5693 * testing against files under the s1d41 or s1d42 disconnected directories. 5694 */ 5695 const __u64 allowed_s1d2; 5696 /* 5697 * Original parent of s1d41. It should always be ignored when testing 5698 * against files under the s1d41 disconnected directory. 5699 */ 5700 const __u64 allowed_s1d31; 5701 /* 5702 * Original parent of s1d42. It should always be ignored when testing 5703 * against files under the s1d42 disconnected directory. 5704 */ 5705 const __u64 allowed_s1d32; 5706 /* 5707 * Opened and disconnected source directory. It should always be enforced 5708 * when testing against files under the s1d41 disconnected directory. 5709 */ 5710 const __u64 allowed_s1d41; 5711 /* 5712 * Opened and disconnected source directory. It should always be enforced 5713 * when testing against files under the s1d42 disconnected directory. 5714 */ 5715 const __u64 allowed_s1d42; 5716 /* 5717 * File in the s1d41 disconnected directory. It should always be enforced 5718 * when testing against itself under the s1d41 disconnected directory. 5719 */ 5720 const __u64 allowed_f1; 5721 /* 5722 * File in the s1d41 disconnected directory. It should always be enforced 5723 * when testing against itself under the s1d41 disconnected directory. 5724 */ 5725 const __u64 allowed_f2; 5726 /* 5727 * File in the s1d42 disconnected directory. It should always be enforced 5728 * when testing against itself under the s1d42 disconnected directory. 5729 */ 5730 const __u64 allowed_f3; 5731 /* 5732 * Parent of the bind mount destination. It should always be enforced when 5733 * testing against files under the s1d41 or s1d42 disconnected directories. 5734 */ 5735 const __u64 allowed_s2d1; 5736 /* 5737 * Directory covered by the bind mount. It should always be ignored when 5738 * testing against files under the s1d41 or s1d42 disconnected directories. 5739 */ 5740 const __u64 allowed_s2d2; 5741 /* 5742 * New parent of the renamed s1d41. It should always be ignored when 5743 * testing against files under the s1d41 disconnected directory. 5744 */ 5745 const __u64 allowed_s3d1; 5746 /* 5747 * New parent of the renamed s1d42. It should always be ignored when 5748 * testing against files under the s1d42 disconnected directory. 5749 */ 5750 const __u64 allowed_s4d1; 5751 5752 /* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */ 5753 const int expected_read_result; 5754 /* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */ 5755 const int expected_rename_result; 5756 /* 5757 * Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3, 5758 * RENAME_EXCHANGE). 5759 */ 5760 const int expected_exchange_result; 5761 /* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */ 5762 const int expected_same_dir_rename_result; 5763 }; 5764 5765 /* clang-format off */ 5766 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) { 5767 /* clang-format on */ 5768 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 5769 LANDLOCK_ACCESS_FS_READ_FILE | 5770 LANDLOCK_ACCESS_FS_EXECUTE | 5771 LANDLOCK_ACCESS_FS_MAKE_REG, 5772 .expected_read_result = EACCES, 5773 .expected_same_dir_rename_result = EACCES, 5774 .expected_rename_result = EACCES, 5775 .expected_exchange_result = EACCES, 5776 }; 5777 5778 /* clang-format off */ 5779 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) { 5780 /* clang-format on */ 5781 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5782 .expected_read_result = 0, 5783 .expected_same_dir_rename_result = EACCES, 5784 .expected_rename_result = EACCES, 5785 .expected_exchange_result = EACCES, 5786 }; 5787 5788 /* clang-format off */ 5789 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) { 5790 /* clang-format on */ 5791 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 5792 LANDLOCK_ACCESS_FS_MAKE_REG, 5793 .expected_read_result = 0, 5794 .expected_same_dir_rename_result = 0, 5795 .expected_rename_result = EXDEV, 5796 .expected_exchange_result = EXDEV, 5797 }; 5798 5799 /* clang-format off */ 5800 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) { 5801 /* clang-format on */ 5802 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5803 .expected_read_result = EACCES, 5804 .expected_same_dir_rename_result = 0, 5805 .expected_rename_result = 0, 5806 .expected_exchange_result = 0, 5807 }; 5808 5809 /* clang-format off */ 5810 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) { 5811 /* clang-format on */ 5812 .allowed_s1d31 = LANDLOCK_ACCESS_FS_REFER | 5813 LANDLOCK_ACCESS_FS_READ_FILE | 5814 LANDLOCK_ACCESS_FS_EXECUTE | 5815 LANDLOCK_ACCESS_FS_MAKE_REG, 5816 .allowed_s1d32 = LANDLOCK_ACCESS_FS_REFER | 5817 LANDLOCK_ACCESS_FS_READ_FILE | 5818 LANDLOCK_ACCESS_FS_EXECUTE | 5819 LANDLOCK_ACCESS_FS_MAKE_REG, 5820 .expected_read_result = EACCES, 5821 .expected_same_dir_rename_result = EACCES, 5822 .expected_rename_result = EACCES, 5823 .expected_exchange_result = EACCES, 5824 }; 5825 5826 /* clang-format off */ 5827 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) { 5828 /* clang-format on */ 5829 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5830 LANDLOCK_ACCESS_FS_READ_FILE, 5831 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5832 LANDLOCK_ACCESS_FS_READ_FILE, 5833 .expected_read_result = 0, 5834 .expected_same_dir_rename_result = EACCES, 5835 .expected_rename_result = EACCES, 5836 .expected_exchange_result = EACCES, 5837 }; 5838 5839 /* clang-format off */ 5840 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) { 5841 /* clang-format on */ 5842 .allowed_s1d41 = LANDLOCK_ACCESS_FS_READ_FILE | 5843 LANDLOCK_ACCESS_FS_MAKE_REG, 5844 .allowed_s1d42 = LANDLOCK_ACCESS_FS_READ_FILE | 5845 LANDLOCK_ACCESS_FS_MAKE_REG, 5846 .expected_read_result = 0, 5847 .expected_same_dir_rename_result = 0, 5848 .expected_rename_result = EXDEV, 5849 .expected_exchange_result = EXDEV, 5850 }; 5851 5852 /* clang-format off */ 5853 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) { 5854 /* clang-format on */ 5855 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5856 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5857 .expected_read_result = EACCES, 5858 .expected_same_dir_rename_result = 0, 5859 .expected_rename_result = 0, 5860 .expected_exchange_result = 0, 5861 }; 5862 5863 /* The destination directory has more access right. */ 5864 /* clang-format off */ 5865 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) { 5866 /* clang-format on */ 5867 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5868 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5869 LANDLOCK_ACCESS_FS_MAKE_REG | 5870 LANDLOCK_ACCESS_FS_EXECUTE, 5871 .expected_read_result = EACCES, 5872 .expected_same_dir_rename_result = 0, 5873 /* Access denied. */ 5874 .expected_rename_result = EXDEV, 5875 .expected_exchange_result = EXDEV, 5876 }; 5877 5878 /* The destination directory has less access right. */ 5879 /* clang-format off */ 5880 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) { 5881 /* clang-format on */ 5882 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5883 LANDLOCK_ACCESS_FS_MAKE_REG | 5884 LANDLOCK_ACCESS_FS_EXECUTE, 5885 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5886 .expected_read_result = EACCES, 5887 .expected_same_dir_rename_result = 0, 5888 /* Access allowed. */ 5889 .expected_rename_result = 0, 5890 .expected_exchange_result = EXDEV, 5891 }; 5892 5893 /* clang-format off */ 5894 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) { 5895 /* clang-format on */ 5896 .allowed_s2d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5897 LANDLOCK_ACCESS_FS_MAKE_REG, 5898 .expected_read_result = 0, 5899 .expected_same_dir_rename_result = 0, 5900 .expected_rename_result = EXDEV, 5901 .expected_exchange_result = EXDEV, 5902 }; 5903 5904 /* clang-format off */ 5905 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) { 5906 /* clang-format on */ 5907 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5908 .expected_read_result = 0, 5909 .expected_same_dir_rename_result = EACCES, 5910 .expected_rename_result = EACCES, 5911 .expected_exchange_result = EACCES, 5912 }; 5913 5914 /* clang-format off */ 5915 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) { 5916 /* clang-format on */ 5917 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 5918 LANDLOCK_ACCESS_FS_READ_FILE | 5919 LANDLOCK_ACCESS_FS_MAKE_REG, 5920 .expected_read_result = 0, 5921 .expected_same_dir_rename_result = 0, 5922 .expected_rename_result = 0, 5923 .expected_exchange_result = 0, 5924 }; 5925 5926 /* clang-format off */ 5927 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) { 5928 /* clang-format on */ 5929 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | 5930 LANDLOCK_ACCESS_FS_READ_FILE | 5931 LANDLOCK_ACCESS_FS_EXECUTE | 5932 LANDLOCK_ACCESS_FS_MAKE_REG, 5933 .expected_read_result = EACCES, 5934 .expected_same_dir_rename_result = EACCES, 5935 .expected_rename_result = EACCES, 5936 .expected_exchange_result = EACCES, 5937 }; 5938 5939 /* Tests collect_domain_accesses(). */ 5940 /* clang-format off */ 5941 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) { 5942 /* clang-format on */ 5943 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5944 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5945 .expected_read_result = 0, 5946 .expected_same_dir_rename_result = EACCES, 5947 .expected_rename_result = EACCES, 5948 .expected_exchange_result = EACCES, 5949 }; 5950 5951 /* clang-format off */ 5952 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) { 5953 /* clang-format on */ 5954 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5955 LANDLOCK_ACCESS_FS_MAKE_REG, 5956 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5957 LANDLOCK_ACCESS_FS_MAKE_REG, 5958 .expected_read_result = 0, 5959 .expected_same_dir_rename_result = 0, 5960 .expected_rename_result = EXDEV, 5961 .expected_exchange_result = EXDEV, 5962 }; 5963 5964 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, 5965 s3d1_s4d1_disconnected_rename_even){ 5966 /* clang-format on */ 5967 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5968 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5969 .expected_read_result = EACCES, 5970 .expected_same_dir_rename_result = 0, 5971 .expected_rename_result = 0, 5972 .expected_exchange_result = 0, 5973 }; 5974 5975 /* The destination directory has more access right. */ 5976 /* clang-format off */ 5977 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) { 5978 /* clang-format on */ 5979 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5980 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5981 LANDLOCK_ACCESS_FS_EXECUTE, 5982 .expected_read_result = EACCES, 5983 .expected_same_dir_rename_result = 0, 5984 /* Access denied. */ 5985 .expected_rename_result = EXDEV, 5986 .expected_exchange_result = EXDEV, 5987 }; 5988 5989 /* The destination directory has less access right. */ 5990 /* clang-format off */ 5991 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) { 5992 /* clang-format on */ 5993 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5994 LANDLOCK_ACCESS_FS_EXECUTE, 5995 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5996 .expected_read_result = EACCES, 5997 .expected_same_dir_rename_result = 0, 5998 /* Access allowed. */ 5999 .expected_rename_result = 0, 6000 .expected_exchange_result = EXDEV, 6001 }; 6002 6003 /* clang-format off */ 6004 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) { 6005 /* clang-format on */ 6006 .allowed_f1 = LANDLOCK_ACCESS_FS_READ_FILE, 6007 .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE, 6008 .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE, 6009 .expected_read_result = 0, 6010 .expected_same_dir_rename_result = EACCES, 6011 .expected_rename_result = EACCES, 6012 .expected_exchange_result = EACCES, 6013 }; 6014 6015 TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange) 6016 { 6017 const __u64 handled_access = 6018 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 6019 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 6020 const struct rule rules[] = { 6021 { 6022 .path = TMP_DIR "/s1d1", 6023 .access = variant->allowed_s1d1, 6024 }, 6025 { 6026 .path = TMP_DIR "/s1d1/s1d2", 6027 .access = variant->allowed_s1d2, 6028 }, 6029 { 6030 .path = TMP_DIR "/s1d1/s1d2/s1d31", 6031 .access = variant->allowed_s1d31, 6032 }, 6033 { 6034 .path = TMP_DIR "/s1d1/s1d2/s1d32", 6035 .access = variant->allowed_s1d32, 6036 }, 6037 { 6038 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 6039 .access = variant->allowed_s1d41, 6040 }, 6041 { 6042 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 6043 .access = variant->allowed_s1d42, 6044 }, 6045 { 6046 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1", 6047 .access = variant->allowed_f1, 6048 }, 6049 { 6050 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2", 6051 .access = variant->allowed_f2, 6052 }, 6053 { 6054 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3", 6055 .access = variant->allowed_f3, 6056 }, 6057 { 6058 .path = TMP_DIR "/s2d1", 6059 .access = variant->allowed_s2d1, 6060 }, 6061 /* s2d2_fd */ 6062 { 6063 .path = TMP_DIR "/s3d1", 6064 .access = variant->allowed_s3d1, 6065 }, 6066 { 6067 .path = TMP_DIR "/s4d1", 6068 .access = variant->allowed_s4d1, 6069 }, 6070 {}, 6071 }; 6072 int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; 6073 6074 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 6075 6076 /* Adds rule for the covered directory. */ 6077 if (variant->allowed_s2d2) { 6078 ASSERT_EQ(0, landlock_add_rule( 6079 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6080 &(struct landlock_path_beneath_attr){ 6081 .parent_fd = self->s2d2_fd, 6082 .allowed_access = 6083 variant->allowed_s2d2, 6084 }, 6085 0)); 6086 } 6087 EXPECT_EQ(0, close(self->s2d2_fd)); 6088 6089 s1d41_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d31/s1d41", 6090 O_DIRECTORY | O_PATH | O_CLOEXEC); 6091 ASSERT_LE(0, s1d41_bind_fd); 6092 s1d42_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d32/s1d42", 6093 O_DIRECTORY | O_PATH | O_CLOEXEC); 6094 ASSERT_LE(0, s1d42_bind_fd); 6095 6096 /* Disconnects and checks source and destination directories. */ 6097 EXPECT_EQ(0, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 6098 EXPECT_EQ(0, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 6099 /* Renames to make it accessible through s3d1/s1d41 */ 6100 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 6101 AT_FDCWD, TMP_DIR "/s3d1/s1d41")); 6102 /* Renames to make it accessible through s4d1/s1d42 */ 6103 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 6104 AT_FDCWD, TMP_DIR "/s4d1/s1d42")); 6105 EXPECT_EQ(ENOENT, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 6106 EXPECT_EQ(ENOENT, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 6107 6108 enforce_ruleset(_metadata, ruleset_fd); 6109 EXPECT_EQ(0, close(ruleset_fd)); 6110 6111 EXPECT_EQ(variant->expected_read_result, 6112 test_open_rel(s1d41_bind_fd, "f1", O_RDONLY)); 6113 6114 EXPECT_EQ(variant->expected_rename_result, 6115 test_renameat(s1d41_bind_fd, "f1", s1d42_bind_fd, "f1")); 6116 EXPECT_EQ(variant->expected_exchange_result, 6117 test_exchangeat(s1d41_bind_fd, "f2", s1d42_bind_fd, "f3")); 6118 6119 EXPECT_EQ(variant->expected_same_dir_rename_result, 6120 test_renameat(s1d42_bind_fd, "f4", s1d42_bind_fd, "f5")); 6121 } 6122 6123 /* 6124 * layout5_disconnected_branch before rename: 6125 * 6126 * tmp 6127 * ├── s1d1 6128 * │ └── s1d2 [source of the first bind mount] 6129 * │ └── s1d3 6130 * │ ├── s1d41 6131 * │ │ ├── f1 6132 * │ │ └── f2 6133 * │ └── s1d42 6134 * │ ├── f3 6135 * │ └── f4 6136 * ├── s2d1 6137 * │ └── s2d2 [source of the second bind mount] 6138 * │ └── s2d3 6139 * │ └── s2d4 [first s1d2 bind mount] 6140 * │ └── s1d3 6141 * │ ├── s1d41 6142 * │ │ ├── f1 6143 * │ │ └── f2 6144 * │ └── s1d42 6145 * │ ├── f3 6146 * │ └── f4 6147 * ├── s3d1 6148 * │ └── s3d2 [second s2d2 bind mount] 6149 * │ └── s2d3 6150 * │ └── s2d4 [first s1d2 bind mount] 6151 * │ └── s1d3 6152 * │ ├── s1d41 6153 * │ │ ├── f1 6154 * │ │ └── f2 6155 * │ └── s1d42 6156 * │ ├── f3 6157 * │ └── f4 6158 * └── s4d1 6159 * 6160 * After rename: 6161 * 6162 * tmp 6163 * ├── s1d1 6164 * │ └── s1d2 [source of the first bind mount] 6165 * │ └── s1d3 6166 * │ ├── s1d41 6167 * │ │ ├── f1 6168 * │ │ └── f2 6169 * │ └── s1d42 6170 * │ ├── f3 6171 * │ └── f4 6172 * ├── s2d1 6173 * │ └── s2d2 [source of the second bind mount] 6174 * ├── s3d1 6175 * │ └── s3d2 [second s2d2 bind mount] 6176 * └── s4d1 6177 * └── s2d3 [renamed here] 6178 * └── s2d4 [first s1d2 bind mount] 6179 * └── s1d3 6180 * ├── s1d41 6181 * │ ├── f1 6182 * │ └── f2 6183 * └── s1d42 6184 * ├── f3 6185 * └── f4 6186 * 6187 * Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor: 6188 * 1. first bind mount: s1d3 -> s1d2 6189 * 2. second bind mount: s2d3 6190 * 3. tmp mount: s4d1 -> tmp [disconnected branch] 6191 * 4. second bind mount: s2d2 6192 * 5. tmp mount: s3d1 -> tmp 6193 * 6. parent mounts: [...] -> / 6194 * 6195 * The s4d1 directory is evaluated even if it is not in the s2d2 mount. 6196 */ 6197 6198 /* clang-format off */ 6199 FIXTURE(layout5_disconnected_branch) { 6200 int s2d4_fd, s3d2_fd; 6201 }; 6202 /* clang-format on */ 6203 6204 FIXTURE_SETUP(layout5_disconnected_branch) 6205 { 6206 prepare_layout(_metadata); 6207 6208 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 6209 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 6210 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 6211 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 6212 create_directory(_metadata, TMP_DIR "/s2d1/s2d2/s2d3/s2d4"); 6213 create_directory(_metadata, TMP_DIR "/s3d1/s3d2"); 6214 create_directory(_metadata, TMP_DIR "/s4d1"); 6215 6216 self->s2d4_fd = open(TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 6217 O_DIRECTORY | O_PATH | O_CLOEXEC); 6218 ASSERT_LE(0, self->s2d4_fd); 6219 6220 self->s3d2_fd = 6221 open(TMP_DIR "/s3d1/s3d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 6222 ASSERT_LE(0, self->s3d2_fd); 6223 6224 set_cap(_metadata, CAP_SYS_ADMIN); 6225 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 6226 NULL, MS_BIND, NULL)); 6227 ASSERT_EQ(0, mount(TMP_DIR "/s2d1/s2d2", TMP_DIR "/s3d1/s3d2", NULL, 6228 MS_BIND | MS_REC, NULL)); 6229 clear_cap(_metadata, CAP_SYS_ADMIN); 6230 } 6231 6232 FIXTURE_TEARDOWN_PARENT(layout5_disconnected_branch) 6233 { 6234 /* Bind mounts are handled by namespace lifetime. */ 6235 6236 /* Removes files after renames. */ 6237 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 6238 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 6239 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1"); 6240 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 6241 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 6242 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5"); 6243 6244 cleanup_layout(_metadata); 6245 } 6246 6247 FIXTURE_VARIANT(layout5_disconnected_branch) 6248 { 6249 /* 6250 * Parent of all files. It should always be enforced when testing against 6251 * files under the s1d41 or s1d42 disconnected directories. 6252 */ 6253 const __u64 allowed_base; 6254 /* 6255 * Parent of the first bind mount source. It should always be ignored when 6256 * testing against files under the s1d41 or s1d42 disconnected directories. 6257 */ 6258 const __u64 allowed_s1d1; 6259 const __u64 allowed_s1d2; 6260 const __u64 allowed_s1d3; 6261 const __u64 allowed_s2d1; 6262 const __u64 allowed_s2d2; 6263 const __u64 allowed_s2d3; 6264 const __u64 allowed_s2d4; 6265 const __u64 allowed_s3d1; 6266 const __u64 allowed_s3d2; 6267 const __u64 allowed_s4d1; 6268 6269 /* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */ 6270 const int expected_read_result; 6271 /* 6272 * Expected result of the call to renameat([fd:s1d3]/s1d41/f1, 6273 * [fd:s1d3]/s1d42/f1). 6274 */ 6275 const int expected_rename_result; 6276 /* 6277 * Expected result of the call to renameat([fd:s1d3]/s1d41/f2, 6278 * [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE). 6279 */ 6280 const int expected_exchange_result; 6281 /* 6282 * Expected result of the call to renameat([fd:s1d3]/s1d42/f4, 6283 * [fd:s1d3]/s1d42/f5). 6284 */ 6285 const int expected_same_dir_rename_result; 6286 }; 6287 6288 /* clang-format off */ 6289 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) { 6290 /* clang-format on */ 6291 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 6292 LANDLOCK_ACCESS_FS_READ_FILE | 6293 LANDLOCK_ACCESS_FS_EXECUTE | 6294 LANDLOCK_ACCESS_FS_MAKE_REG, 6295 .expected_read_result = EACCES, 6296 .expected_same_dir_rename_result = EACCES, 6297 .expected_rename_result = EACCES, 6298 .expected_exchange_result = EACCES, 6299 }; 6300 6301 /* clang-format off */ 6302 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) { 6303 /* clang-format on */ 6304 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6305 .expected_read_result = 0, 6306 .expected_same_dir_rename_result = EACCES, 6307 .expected_rename_result = EACCES, 6308 .expected_exchange_result = EACCES, 6309 }; 6310 6311 /* clang-format off */ 6312 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) { 6313 /* clang-format on */ 6314 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6315 LANDLOCK_ACCESS_FS_MAKE_REG, 6316 .expected_read_result = 0, 6317 .expected_same_dir_rename_result = 0, 6318 .expected_rename_result = EXDEV, 6319 .expected_exchange_result = EXDEV, 6320 }; 6321 6322 /* clang-format off */ 6323 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) { 6324 /* clang-format on */ 6325 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6326 .expected_read_result = EACCES, 6327 .expected_same_dir_rename_result = 0, 6328 .expected_rename_result = 0, 6329 .expected_exchange_result = 0, 6330 }; 6331 6332 /* clang-format off */ 6333 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) { 6334 /* clang-format on */ 6335 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6336 .expected_read_result = 0, 6337 .expected_same_dir_rename_result = EACCES, 6338 .expected_rename_result = EACCES, 6339 .expected_exchange_result = EACCES, 6340 }; 6341 6342 /* clang-format off */ 6343 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) { 6344 /* clang-format on */ 6345 .allowed_s1d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6346 LANDLOCK_ACCESS_FS_MAKE_REG, 6347 .expected_read_result = 0, 6348 .expected_same_dir_rename_result = 0, 6349 .expected_rename_result = EXDEV, 6350 .expected_exchange_result = EXDEV, 6351 }; 6352 6353 /* clang-format off */ 6354 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) { 6355 /* clang-format on */ 6356 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6357 .expected_read_result = EACCES, 6358 .expected_same_dir_rename_result = 0, 6359 .expected_rename_result = 0, 6360 .expected_exchange_result = 0, 6361 }; 6362 6363 /* clang-format off */ 6364 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) { 6365 /* clang-format on */ 6366 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | 6367 LANDLOCK_ACCESS_FS_READ_FILE | 6368 LANDLOCK_ACCESS_FS_EXECUTE | 6369 LANDLOCK_ACCESS_FS_MAKE_REG, 6370 .expected_read_result = 0, 6371 .expected_same_dir_rename_result = 0, 6372 .expected_rename_result = 0, 6373 .expected_exchange_result = 0, 6374 }; 6375 6376 /* clang-format off */ 6377 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) { 6378 /* clang-format on */ 6379 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 6380 LANDLOCK_ACCESS_FS_READ_FILE | 6381 LANDLOCK_ACCESS_FS_EXECUTE | 6382 LANDLOCK_ACCESS_FS_MAKE_REG, 6383 .expected_read_result = EACCES, 6384 .expected_same_dir_rename_result = EACCES, 6385 .expected_rename_result = EACCES, 6386 .expected_exchange_result = EACCES, 6387 }; 6388 6389 /* clang-format off */ 6390 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) { 6391 /* clang-format on */ 6392 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6393 .expected_read_result = 0, 6394 .expected_same_dir_rename_result = EACCES, 6395 .expected_rename_result = EACCES, 6396 .expected_exchange_result = EACCES, 6397 }; 6398 6399 /* clang-format off */ 6400 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) { 6401 /* clang-format on */ 6402 .allowed_s2d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6403 LANDLOCK_ACCESS_FS_MAKE_REG, 6404 .expected_read_result = 0, 6405 .expected_same_dir_rename_result = 0, 6406 .expected_rename_result = EXDEV, 6407 .expected_exchange_result = EXDEV, 6408 }; 6409 6410 /* clang-format off */ 6411 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) { 6412 /* clang-format on */ 6413 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6414 .expected_read_result = EACCES, 6415 .expected_same_dir_rename_result = 0, 6416 .expected_rename_result = 0, 6417 .expected_exchange_result = 0, 6418 }; 6419 6420 /* clang-format off */ 6421 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) { 6422 /* clang-format on */ 6423 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6424 .expected_read_result = 0, 6425 .expected_same_dir_rename_result = EACCES, 6426 .expected_rename_result = EACCES, 6427 .expected_exchange_result = EACCES, 6428 }; 6429 6430 /* clang-format off */ 6431 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) { 6432 /* clang-format on */ 6433 .allowed_s2d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6434 LANDLOCK_ACCESS_FS_MAKE_REG, 6435 .expected_read_result = 0, 6436 .expected_same_dir_rename_result = 0, 6437 .expected_rename_result = EXDEV, 6438 .expected_exchange_result = EXDEV, 6439 }; 6440 6441 /* clang-format off */ 6442 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) { 6443 /* clang-format on */ 6444 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6445 .expected_read_result = EACCES, 6446 .expected_same_dir_rename_result = 0, 6447 .expected_rename_result = 0, 6448 .expected_exchange_result = 0, 6449 }; 6450 6451 /* clang-format off */ 6452 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) { 6453 /* clang-format on */ 6454 .allowed_s2d4 = LANDLOCK_ACCESS_FS_REFER | 6455 LANDLOCK_ACCESS_FS_READ_FILE | 6456 LANDLOCK_ACCESS_FS_EXECUTE | 6457 LANDLOCK_ACCESS_FS_MAKE_REG, 6458 .expected_read_result = EACCES, 6459 .expected_same_dir_rename_result = EACCES, 6460 .expected_rename_result = EACCES, 6461 .expected_exchange_result = EACCES, 6462 }; 6463 6464 /* clang-format off */ 6465 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) { 6466 /* clang-format on */ 6467 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6468 .expected_read_result = 0, 6469 .expected_same_dir_rename_result = EACCES, 6470 .expected_rename_result = EACCES, 6471 .expected_exchange_result = EACCES, 6472 }; 6473 6474 /* clang-format off */ 6475 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) { 6476 /* clang-format on */ 6477 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6478 LANDLOCK_ACCESS_FS_MAKE_REG, 6479 .expected_read_result = 0, 6480 .expected_same_dir_rename_result = 0, 6481 .expected_rename_result = EXDEV, 6482 .expected_exchange_result = EXDEV, 6483 }; 6484 6485 /* clang-format off */ 6486 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) { 6487 /* clang-format on */ 6488 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6489 .expected_read_result = EACCES, 6490 .expected_same_dir_rename_result = 0, 6491 .expected_rename_result = 0, 6492 .expected_exchange_result = 0, 6493 }; 6494 6495 /* clang-format off */ 6496 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) { 6497 /* clang-format on */ 6498 .allowed_s3d2 = LANDLOCK_ACCESS_FS_REFER | 6499 LANDLOCK_ACCESS_FS_READ_FILE | 6500 LANDLOCK_ACCESS_FS_EXECUTE | 6501 LANDLOCK_ACCESS_FS_MAKE_REG, 6502 .expected_read_result = EACCES, 6503 .expected_same_dir_rename_result = EACCES, 6504 .expected_rename_result = EACCES, 6505 .expected_exchange_result = EACCES, 6506 }; 6507 6508 /* clang-format off */ 6509 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) { 6510 /* clang-format on */ 6511 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6512 .expected_read_result = 0, 6513 .expected_same_dir_rename_result = EACCES, 6514 .expected_rename_result = EACCES, 6515 .expected_exchange_result = EACCES, 6516 }; 6517 6518 /* clang-format off */ 6519 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) { 6520 /* clang-format on */ 6521 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6522 LANDLOCK_ACCESS_FS_MAKE_REG, 6523 .expected_read_result = 0, 6524 .expected_same_dir_rename_result = 0, 6525 .expected_rename_result = EXDEV, 6526 .expected_exchange_result = EXDEV, 6527 }; 6528 6529 /* clang-format off */ 6530 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) { 6531 /* clang-format on */ 6532 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6533 .expected_read_result = EACCES, 6534 .expected_same_dir_rename_result = 0, 6535 .expected_rename_result = 0, 6536 .expected_exchange_result = 0, 6537 }; 6538 6539 TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange) 6540 { 6541 const __u64 handled_access = 6542 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 6543 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 6544 const struct rule rules[] = { 6545 { 6546 .path = TMP_DIR "/s1d1", 6547 .access = variant->allowed_s1d1, 6548 }, 6549 { 6550 .path = TMP_DIR "/s1d1/s1d2", 6551 .access = variant->allowed_s1d2, 6552 }, 6553 { 6554 .path = TMP_DIR "/s1d1/s1d2/s1d3", 6555 .access = variant->allowed_s1d3, 6556 }, 6557 { 6558 .path = TMP_DIR "/s2d1", 6559 .access = variant->allowed_s2d1, 6560 }, 6561 { 6562 .path = TMP_DIR "/s2d1/s2d2", 6563 .access = variant->allowed_s2d2, 6564 }, 6565 { 6566 .path = TMP_DIR "/s2d1/s2d2/s2d3", 6567 .access = variant->allowed_s2d3, 6568 }, 6569 /* s2d4_fd */ 6570 { 6571 .path = TMP_DIR "/s3d1", 6572 .access = variant->allowed_s3d1, 6573 }, 6574 /* s3d2_fd */ 6575 { 6576 .path = TMP_DIR "/s4d1", 6577 .access = variant->allowed_s4d1, 6578 }, 6579 {}, 6580 }; 6581 int ruleset_fd, s1d3_bind_fd; 6582 6583 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 6584 ASSERT_LE(0, ruleset_fd); 6585 6586 /* Adds rules for the covered directories. */ 6587 if (variant->allowed_s2d4) { 6588 ASSERT_EQ(0, landlock_add_rule( 6589 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6590 &(struct landlock_path_beneath_attr){ 6591 .parent_fd = self->s2d4_fd, 6592 .allowed_access = 6593 variant->allowed_s2d4, 6594 }, 6595 0)); 6596 } 6597 EXPECT_EQ(0, close(self->s2d4_fd)); 6598 6599 if (variant->allowed_s3d2) { 6600 ASSERT_EQ(0, landlock_add_rule( 6601 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6602 &(struct landlock_path_beneath_attr){ 6603 .parent_fd = self->s3d2_fd, 6604 .allowed_access = 6605 variant->allowed_s3d2, 6606 }, 6607 0)); 6608 } 6609 EXPECT_EQ(0, close(self->s3d2_fd)); 6610 6611 s1d3_bind_fd = open(TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3", 6612 O_DIRECTORY | O_PATH | O_CLOEXEC); 6613 ASSERT_LE(0, s1d3_bind_fd); 6614 6615 /* Disconnects and checks source and destination directories. */ 6616 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6617 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6618 /* Renames to make it accessible through s3d1/s1d41 */ 6619 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s2d1/s2d2/s2d3", 6620 AT_FDCWD, TMP_DIR "/s4d1/s2d3")); 6621 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6622 EXPECT_EQ(ENOENT, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6623 6624 enforce_ruleset(_metadata, ruleset_fd); 6625 EXPECT_EQ(0, close(ruleset_fd)); 6626 6627 EXPECT_EQ(variant->expected_read_result, 6628 test_open_rel(s1d3_bind_fd, "s1d41/f1", O_RDONLY)); 6629 6630 EXPECT_EQ(variant->expected_rename_result, 6631 test_renameat(s1d3_bind_fd, "s1d41/f1", s1d3_bind_fd, 6632 "s1d42/f1")); 6633 EXPECT_EQ(variant->expected_exchange_result, 6634 test_exchangeat(s1d3_bind_fd, "s1d41/f2", s1d3_bind_fd, 6635 "s1d42/f3")); 6636 6637 EXPECT_EQ(variant->expected_same_dir_rename_result, 6638 test_renameat(s1d3_bind_fd, "s1d42/f4", s1d3_bind_fd, 6639 "s1d42/f5")); 6640 } 6641 6642 #define LOWER_BASE TMP_DIR "/lower" 6643 #define LOWER_DATA LOWER_BASE "/data" 6644 static const char lower_fl1[] = LOWER_DATA "/fl1"; 6645 static const char lower_dl1[] = LOWER_DATA "/dl1"; 6646 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 6647 static const char lower_fo1[] = LOWER_DATA "/fo1"; 6648 static const char lower_do1[] = LOWER_DATA "/do1"; 6649 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 6650 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 6651 /* lower_pl1 is a FIFO and is deliberately not in the lists below. */ 6652 static const char lower_pl1[] = LOWER_DATA "/pl1"; 6653 6654 static const char (*lower_base_files[])[] = { 6655 &lower_fl1, 6656 &lower_fo1, 6657 NULL, 6658 }; 6659 static const char (*lower_base_directories[])[] = { 6660 &lower_dl1, 6661 &lower_do1, 6662 NULL, 6663 }; 6664 static const char (*lower_sub_files[])[] = { 6665 &lower_dl1_fl2, 6666 &lower_do1_fo2, 6667 &lower_do1_fl3, 6668 NULL, 6669 }; 6670 6671 #define UPPER_BASE TMP_DIR "/upper" 6672 #define UPPER_DATA UPPER_BASE "/data" 6673 #define UPPER_WORK UPPER_BASE "/work" 6674 static const char upper_fu1[] = UPPER_DATA "/fu1"; 6675 static const char upper_du1[] = UPPER_DATA "/du1"; 6676 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 6677 static const char upper_fo1[] = UPPER_DATA "/fo1"; 6678 static const char upper_do1[] = UPPER_DATA "/do1"; 6679 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 6680 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 6681 6682 static const char (*upper_base_files[])[] = { 6683 &upper_fu1, 6684 &upper_fo1, 6685 NULL, 6686 }; 6687 static const char (*upper_base_directories[])[] = { 6688 &upper_du1, 6689 &upper_do1, 6690 NULL, 6691 }; 6692 static const char (*upper_sub_files[])[] = { 6693 &upper_du1_fu2, 6694 &upper_do1_fo2, 6695 &upper_do1_fu3, 6696 NULL, 6697 }; 6698 6699 #define MERGE_BASE TMP_DIR "/merge" 6700 #define MERGE_DATA MERGE_BASE "/data" 6701 static const char merge_fl1[] = MERGE_DATA "/fl1"; 6702 /* merge_pl1 is a FIFO and is deliberately not in the lists below. */ 6703 static const char merge_pl1[] = MERGE_DATA "/pl1"; 6704 static const char merge_dl1[] = MERGE_DATA "/dl1"; 6705 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 6706 static const char merge_fu1[] = MERGE_DATA "/fu1"; 6707 static const char merge_du1[] = MERGE_DATA "/du1"; 6708 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 6709 static const char merge_fo1[] = MERGE_DATA "/fo1"; 6710 static const char merge_do1[] = MERGE_DATA "/do1"; 6711 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 6712 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 6713 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 6714 6715 static const char (*merge_base_files[])[] = { 6716 &merge_fl1, 6717 &merge_fu1, 6718 &merge_fo1, 6719 NULL, 6720 }; 6721 static const char (*merge_base_directories[])[] = { 6722 &merge_dl1, 6723 &merge_du1, 6724 &merge_do1, 6725 NULL, 6726 }; 6727 static const char (*merge_sub_files[])[] = { 6728 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 6729 &merge_do1_fl3, &merge_do1_fu3, NULL, 6730 }; 6731 6732 /* 6733 * layout2_overlay hierarchy: 6734 * 6735 * tmp 6736 * ├── lower 6737 * │ └── data 6738 * │ ├── dl1 6739 * │ │ └── fl2 6740 * │ ├── do1 6741 * │ │ ├── fl3 6742 * │ │ └── fo2 6743 * │ ├── fl1 6744 * │ ├── fo1 6745 * │ └── pl1 [FIFO] 6746 * ├── merge 6747 * │ └── data 6748 * │ ├── dl1 6749 * │ │ └── fl2 6750 * │ ├── do1 6751 * │ │ ├── fl3 6752 * │ │ ├── fo2 6753 * │ │ └── fu3 6754 * │ ├── du1 6755 * │ │ └── fu2 6756 * │ ├── fl1 6757 * │ ├── fo1 6758 * │ ├── fu1 6759 * │ └── pl1 [FIFO] 6760 * └── upper 6761 * ├── data 6762 * │ ├── do1 6763 * │ │ ├── fo2 6764 * │ │ └── fu3 6765 * │ ├── du1 6766 * │ │ └── fu2 6767 * │ ├── fo1 6768 * │ └── fu1 6769 * └── work 6770 * └── work 6771 */ 6772 6773 FIXTURE(layout2_overlay) 6774 { 6775 bool skip_test; 6776 }; 6777 6778 FIXTURE_SETUP(layout2_overlay) 6779 { 6780 if (!supports_filesystem("overlay")) { 6781 self->skip_test = true; 6782 SKIP(return, "overlayfs is not supported (setup)"); 6783 } 6784 6785 prepare_layout(_metadata); 6786 6787 create_directory(_metadata, LOWER_BASE); 6788 set_cap(_metadata, CAP_SYS_ADMIN); 6789 /* Creates tmpfs mount points to get deterministic overlayfs. */ 6790 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 6791 clear_cap(_metadata, CAP_SYS_ADMIN); 6792 create_file(_metadata, lower_fl1); 6793 create_file(_metadata, lower_dl1_fl2); 6794 create_file(_metadata, lower_fo1); 6795 create_file(_metadata, lower_do1_fo2); 6796 create_file(_metadata, lower_do1_fl3); 6797 ASSERT_EQ(0, mknod(lower_pl1, S_IFIFO | 0600, 0)); 6798 6799 create_directory(_metadata, UPPER_BASE); 6800 set_cap(_metadata, CAP_SYS_ADMIN); 6801 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 6802 clear_cap(_metadata, CAP_SYS_ADMIN); 6803 create_file(_metadata, upper_fu1); 6804 create_file(_metadata, upper_du1_fu2); 6805 create_file(_metadata, upper_fo1); 6806 create_file(_metadata, upper_do1_fo2); 6807 create_file(_metadata, upper_do1_fu3); 6808 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 6809 6810 create_directory(_metadata, MERGE_DATA); 6811 set_cap(_metadata, CAP_SYS_ADMIN); 6812 set_cap(_metadata, CAP_DAC_OVERRIDE); 6813 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 6814 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 6815 ",workdir=" UPPER_WORK)); 6816 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6817 clear_cap(_metadata, CAP_SYS_ADMIN); 6818 } 6819 6820 FIXTURE_TEARDOWN_PARENT(layout2_overlay) 6821 { 6822 if (self->skip_test) 6823 SKIP(return, "overlayfs is not supported (teardown)"); 6824 6825 EXPECT_EQ(0, remove_path(lower_do1_fl3)); 6826 EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 6827 EXPECT_EQ(0, remove_path(lower_fl1)); 6828 EXPECT_EQ(0, remove_path(lower_do1_fo2)); 6829 EXPECT_EQ(0, remove_path(lower_fo1)); 6830 EXPECT_EQ(0, remove_path(lower_pl1)); 6831 6832 /* umount(LOWER_BASE)) is handled by namespace lifetime. */ 6833 EXPECT_EQ(0, remove_path(LOWER_BASE)); 6834 6835 EXPECT_EQ(0, remove_path(upper_do1_fu3)); 6836 EXPECT_EQ(0, remove_path(upper_du1_fu2)); 6837 EXPECT_EQ(0, remove_path(upper_fu1)); 6838 EXPECT_EQ(0, remove_path(upper_do1_fo2)); 6839 EXPECT_EQ(0, remove_path(upper_fo1)); 6840 EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 6841 6842 /* umount(UPPER_BASE)) is handled by namespace lifetime. */ 6843 EXPECT_EQ(0, remove_path(UPPER_BASE)); 6844 6845 /* umount(MERGE_DATA)) is handled by namespace lifetime. */ 6846 EXPECT_EQ(0, remove_path(MERGE_DATA)); 6847 6848 cleanup_layout(_metadata); 6849 } 6850 6851 TEST_F_FORK(layout2_overlay, no_restriction) 6852 { 6853 if (self->skip_test) 6854 SKIP(return, "overlayfs is not supported (test)"); 6855 6856 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 6857 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 6858 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 6859 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 6860 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 6861 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 6862 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 6863 6864 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 6865 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 6866 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 6867 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 6868 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 6869 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 6870 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 6871 6872 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 6873 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 6874 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 6875 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 6876 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 6877 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 6878 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 6879 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 6880 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 6881 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 6882 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 6883 } 6884 6885 #define for_each_path(path_list, path_entry, i) \ 6886 for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 6887 path_entry = *path_list[++i]) 6888 6889 TEST_F_FORK(layout2_overlay, same_content_different_file) 6890 { 6891 /* Sets access right on parent directories of both layers. */ 6892 const struct rule layer1_base[] = { 6893 { 6894 .path = LOWER_BASE, 6895 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6896 }, 6897 { 6898 .path = UPPER_BASE, 6899 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6900 }, 6901 { 6902 .path = MERGE_BASE, 6903 .access = ACCESS_RW, 6904 }, 6905 {}, 6906 }; 6907 const struct rule layer2_data[] = { 6908 { 6909 .path = LOWER_DATA, 6910 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6911 }, 6912 { 6913 .path = UPPER_DATA, 6914 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6915 }, 6916 { 6917 .path = MERGE_DATA, 6918 .access = ACCESS_RW, 6919 }, 6920 {}, 6921 }; 6922 /* Sets access right on directories inside both layers. */ 6923 const struct rule layer3_subdirs[] = { 6924 { 6925 .path = lower_dl1, 6926 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6927 }, 6928 { 6929 .path = lower_do1, 6930 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6931 }, 6932 { 6933 .path = upper_du1, 6934 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6935 }, 6936 { 6937 .path = upper_do1, 6938 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6939 }, 6940 { 6941 .path = merge_dl1, 6942 .access = ACCESS_RW, 6943 }, 6944 { 6945 .path = merge_du1, 6946 .access = ACCESS_RW, 6947 }, 6948 { 6949 .path = merge_do1, 6950 .access = ACCESS_RW, 6951 }, 6952 {}, 6953 }; 6954 /* Tighten access rights to the files. */ 6955 const struct rule layer4_files[] = { 6956 { 6957 .path = lower_dl1_fl2, 6958 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6959 }, 6960 { 6961 .path = lower_do1_fo2, 6962 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6963 }, 6964 { 6965 .path = lower_do1_fl3, 6966 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6967 }, 6968 { 6969 .path = upper_du1_fu2, 6970 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6971 }, 6972 { 6973 .path = upper_do1_fo2, 6974 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6975 }, 6976 { 6977 .path = upper_do1_fu3, 6978 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6979 }, 6980 { 6981 .path = merge_dl1_fl2, 6982 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6983 LANDLOCK_ACCESS_FS_WRITE_FILE, 6984 }, 6985 { 6986 .path = merge_du1_fu2, 6987 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6988 LANDLOCK_ACCESS_FS_WRITE_FILE, 6989 }, 6990 { 6991 .path = merge_do1_fo2, 6992 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6993 LANDLOCK_ACCESS_FS_WRITE_FILE, 6994 }, 6995 { 6996 .path = merge_do1_fl3, 6997 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6998 LANDLOCK_ACCESS_FS_WRITE_FILE, 6999 }, 7000 { 7001 .path = merge_do1_fu3, 7002 .access = LANDLOCK_ACCESS_FS_READ_FILE | 7003 LANDLOCK_ACCESS_FS_WRITE_FILE, 7004 }, 7005 {}, 7006 }; 7007 const struct rule layer5_merge_only[] = { 7008 { 7009 .path = MERGE_DATA, 7010 .access = LANDLOCK_ACCESS_FS_READ_FILE | 7011 LANDLOCK_ACCESS_FS_WRITE_FILE, 7012 }, 7013 {}, 7014 }; 7015 size_t i; 7016 const char *path_entry; 7017 7018 if (self->skip_test) 7019 SKIP(return, "overlayfs is not supported (test)"); 7020 7021 /* Sets rules on base directories (i.e. outside overlay scope). */ 7022 enforce_fs(_metadata, ACCESS_RW, layer1_base); 7023 7024 /* Checks lower layer. */ 7025 for_each_path(lower_base_files, path_entry, i) { 7026 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 7027 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 7028 } 7029 for_each_path(lower_base_directories, path_entry, i) { 7030 ASSERT_EQ(EACCES, 7031 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 7032 } 7033 for_each_path(lower_sub_files, path_entry, i) { 7034 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 7035 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 7036 } 7037 /* Checks upper layer. */ 7038 for_each_path(upper_base_files, path_entry, i) { 7039 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 7040 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 7041 } 7042 for_each_path(upper_base_directories, path_entry, i) { 7043 ASSERT_EQ(EACCES, 7044 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 7045 } 7046 for_each_path(upper_sub_files, path_entry, i) { 7047 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 7048 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 7049 } 7050 /* 7051 * Checks that access rights are independent from the lower and upper 7052 * layers: write access to upper files viewed through the merge point 7053 * is still allowed, and write access to lower file viewed (and copied) 7054 * through the merge point is still allowed. 7055 */ 7056 for_each_path(merge_base_files, path_entry, i) { 7057 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 7058 } 7059 for_each_path(merge_base_directories, path_entry, i) { 7060 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 7061 } 7062 for_each_path(merge_sub_files, path_entry, i) { 7063 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 7064 } 7065 7066 /* Sets rules on data directories (i.e. inside overlay scope). */ 7067 enforce_fs(_metadata, ACCESS_RW, layer2_data); 7068 7069 /* Checks merge. */ 7070 for_each_path(merge_base_files, path_entry, i) { 7071 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 7072 } 7073 for_each_path(merge_base_directories, path_entry, i) { 7074 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 7075 } 7076 for_each_path(merge_sub_files, path_entry, i) { 7077 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 7078 } 7079 7080 /* Same checks with tighter rules. */ 7081 enforce_fs(_metadata, ACCESS_RW, layer3_subdirs); 7082 7083 /* Checks changes for lower layer. */ 7084 for_each_path(lower_base_files, path_entry, i) { 7085 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 7086 } 7087 /* Checks changes for upper layer. */ 7088 for_each_path(upper_base_files, path_entry, i) { 7089 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 7090 } 7091 /* Checks all merge accesses. */ 7092 for_each_path(merge_base_files, path_entry, i) { 7093 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 7094 } 7095 for_each_path(merge_base_directories, path_entry, i) { 7096 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 7097 } 7098 for_each_path(merge_sub_files, path_entry, i) { 7099 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 7100 } 7101 7102 /* Sets rules directly on overlayed files. */ 7103 enforce_fs(_metadata, ACCESS_RW, layer4_files); 7104 7105 /* Checks unchanged accesses on lower layer. */ 7106 for_each_path(lower_sub_files, path_entry, i) { 7107 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 7108 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 7109 } 7110 /* Checks unchanged accesses on upper layer. */ 7111 for_each_path(upper_sub_files, path_entry, i) { 7112 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 7113 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 7114 } 7115 /* Checks all merge accesses. */ 7116 for_each_path(merge_base_files, path_entry, i) { 7117 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 7118 } 7119 for_each_path(merge_base_directories, path_entry, i) { 7120 ASSERT_EQ(EACCES, 7121 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 7122 } 7123 for_each_path(merge_sub_files, path_entry, i) { 7124 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 7125 } 7126 7127 /* Only allows access to the merge hierarchy. */ 7128 enforce_fs(_metadata, ACCESS_RW, layer5_merge_only); 7129 7130 /* Checks new accesses on lower layer. */ 7131 for_each_path(lower_sub_files, path_entry, i) { 7132 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 7133 } 7134 /* Checks new accesses on upper layer. */ 7135 for_each_path(upper_sub_files, path_entry, i) { 7136 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 7137 } 7138 /* Checks all merge accesses. */ 7139 for_each_path(merge_base_files, path_entry, i) { 7140 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 7141 } 7142 for_each_path(merge_base_directories, path_entry, i) { 7143 ASSERT_EQ(EACCES, 7144 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 7145 } 7146 for_each_path(merge_sub_files, path_entry, i) { 7147 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 7148 } 7149 } 7150 7151 TEST_F_FORK(layout2_overlay, rename_in_overlay_without_make_reg) 7152 { 7153 const char *const merge_pl1_renamed = MERGE_DATA "/pl1_renamed"; 7154 7155 if (self->skip_test) 7156 SKIP(return, "overlayfs is not supported (test)"); 7157 7158 /* 7159 * merge_pl1 is a FIFO which only exists in the lower layer. Before 7160 * the rename, the upper layer has no entry under this name. 7161 */ 7162 ASSERT_TRUE(is_fifo(merge_pl1)); 7163 ASSERT_TRUE(is_missing(UPPER_DATA "/pl1")); 7164 7165 /* MAKE_REG is restricted, but MAKE_FIFO is not. */ 7166 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, NULL); 7167 7168 /* 7169 * Rename the FIFO through OverlayFS. merge_pl1 originates from the 7170 * lower layer, so this triggers a copy-up and creates the whiteout in 7171 * the upper layer to hide the lower layer FIFO file. Even though 7172 * MAKE_REG is restricted, the rename on the OverlayFS works. 7173 */ 7174 EXPECT_EQ(0, rename(merge_pl1, merge_pl1_renamed)); 7175 7176 /* Check that the rename worked. */ 7177 EXPECT_TRUE(is_fifo(merge_pl1_renamed)); 7178 EXPECT_TRUE(is_missing(merge_pl1)); 7179 7180 /* 7181 * Check that the whiteout object was created on the underlying "upper" 7182 * filesystem during the rename. This is OK because the whiteout object 7183 * was created by OverlayFS, not by the calling task. 7184 */ 7185 EXPECT_TRUE(is_whiteout(UPPER_DATA "/pl1")); 7186 } 7187 7188 FIXTURE(layout3_fs) 7189 { 7190 bool has_created_dir; 7191 bool has_created_file; 7192 bool skip_test; 7193 }; 7194 7195 FIXTURE_VARIANT(layout3_fs) 7196 { 7197 const struct mnt_opt mnt; 7198 const char *const file_path; 7199 unsigned int cwd_fs_magic; 7200 }; 7201 7202 /* clang-format off */ 7203 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 7204 /* clang-format on */ 7205 .mnt = { 7206 .type = "tmpfs", 7207 .data = MNT_TMP_DATA, 7208 }, 7209 .file_path = file1_s1d1, 7210 }; 7211 7212 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 7213 .mnt = { 7214 .type = "ramfs", 7215 .data = "mode=700", 7216 }, 7217 .file_path = TMP_DIR "/dir/file", 7218 }; 7219 7220 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 7221 .mnt = { 7222 .type = "cgroup2", 7223 }, 7224 .file_path = TMP_DIR "/test/cgroup.procs", 7225 }; 7226 7227 FIXTURE_VARIANT_ADD(layout3_fs, proc) { 7228 .mnt = { 7229 .type = "proc", 7230 }, 7231 .file_path = TMP_DIR "/self/status", 7232 }; 7233 7234 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 7235 .mnt = { 7236 .type = "sysfs", 7237 }, 7238 .file_path = TMP_DIR "/kernel/notes", 7239 }; 7240 7241 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 7242 .mnt = { 7243 .source = TMP_DIR, 7244 .flags = MS_BIND, 7245 }, 7246 .file_path = TMP_DIR "/dir/file", 7247 .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 7248 }; 7249 7250 static char *dirname_alloc(const char *path) 7251 { 7252 char *dup; 7253 7254 if (!path) 7255 return NULL; 7256 7257 dup = strdup(path); 7258 if (!dup) 7259 return NULL; 7260 7261 return dirname(dup); 7262 } 7263 7264 FIXTURE_SETUP(layout3_fs) 7265 { 7266 struct stat statbuf; 7267 char *dir_path = dirname_alloc(variant->file_path); 7268 7269 if (!supports_filesystem(variant->mnt.type) || 7270 !cwd_matches_fs(variant->cwd_fs_magic)) { 7271 self->skip_test = true; 7272 SKIP(return, "this filesystem is not supported (setup)"); 7273 } 7274 7275 prepare_layout_opt(_metadata, &variant->mnt); 7276 7277 /* Creates directory when required. */ 7278 if (stat(dir_path, &statbuf)) { 7279 set_cap(_metadata, CAP_DAC_OVERRIDE); 7280 EXPECT_EQ(0, mkdir(dir_path, 0700)) 7281 { 7282 TH_LOG("Failed to create directory \"%s\": %s", 7283 dir_path, strerror(errno)); 7284 } 7285 self->has_created_dir = true; 7286 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7287 } 7288 7289 /* Creates file when required. */ 7290 if (stat(variant->file_path, &statbuf)) { 7291 int fd; 7292 7293 set_cap(_metadata, CAP_DAC_OVERRIDE); 7294 fd = creat(variant->file_path, 0600); 7295 EXPECT_LE(0, fd) 7296 { 7297 TH_LOG("Failed to create file \"%s\": %s", 7298 variant->file_path, strerror(errno)); 7299 } 7300 EXPECT_EQ(0, close(fd)); 7301 self->has_created_file = true; 7302 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7303 } 7304 7305 free(dir_path); 7306 } 7307 7308 FIXTURE_TEARDOWN_PARENT(layout3_fs) 7309 { 7310 if (self->skip_test) 7311 SKIP(return, "this filesystem is not supported (teardown)"); 7312 7313 if (self->has_created_file) { 7314 set_cap(_metadata, CAP_DAC_OVERRIDE); 7315 /* 7316 * Don't check for error because the file might already 7317 * have been removed (cf. release_inode test). 7318 */ 7319 unlink(variant->file_path); 7320 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7321 } 7322 7323 if (self->has_created_dir) { 7324 char *dir_path = dirname_alloc(variant->file_path); 7325 7326 set_cap(_metadata, CAP_DAC_OVERRIDE); 7327 /* 7328 * Don't check for error because the directory might already 7329 * have been removed (cf. release_inode test). 7330 */ 7331 rmdir(dir_path); 7332 clear_cap(_metadata, CAP_DAC_OVERRIDE); 7333 free(dir_path); 7334 } 7335 7336 cleanup_layout(_metadata); 7337 } 7338 7339 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 7340 FIXTURE_DATA(layout3_fs) * self, 7341 const FIXTURE_VARIANT(layout3_fs) * variant, 7342 const char *const rule_path) 7343 { 7344 const struct rule layer1_allow_read_file[] = { 7345 { 7346 .path = rule_path, 7347 .access = LANDLOCK_ACCESS_FS_READ_FILE, 7348 }, 7349 {}, 7350 }; 7351 const char *const dev_null_path = "/dev/null"; 7352 7353 if (self->skip_test) 7354 SKIP(return, "this filesystem is not supported (test)"); 7355 7356 /* Checks without Landlock. */ 7357 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7358 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7359 7360 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 7361 layer1_allow_read_file); 7362 7363 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7364 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7365 7366 /* Forbids directory reading. */ 7367 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, NULL); 7368 7369 /* Checks with Landlock and forbidden access. */ 7370 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 7371 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 7372 } 7373 7374 /* Matrix of tests to check file hierarchy evaluation. */ 7375 7376 TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 7377 { 7378 /* The current directory must not be the root for this test. */ 7379 layer3_fs_tag_inode(_metadata, self, variant, "."); 7380 } 7381 7382 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 7383 { 7384 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 7385 } 7386 7387 TEST_F_FORK(layout3_fs, tag_inode_dir_child) 7388 { 7389 char *dir_path = dirname_alloc(variant->file_path); 7390 7391 layer3_fs_tag_inode(_metadata, self, variant, dir_path); 7392 free(dir_path); 7393 } 7394 7395 TEST_F_FORK(layout3_fs, tag_inode_file) 7396 { 7397 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 7398 } 7399 7400 /* Light version of layout1.release_inodes */ 7401 TEST_F_FORK(layout3_fs, release_inodes) 7402 { 7403 const struct rule layer1[] = { 7404 { 7405 .path = TMP_DIR, 7406 .access = LANDLOCK_ACCESS_FS_READ_DIR, 7407 }, 7408 {}, 7409 }; 7410 int ruleset_fd; 7411 7412 if (self->skip_test) 7413 SKIP(return, "this filesystem is not supported (test)"); 7414 7415 /* Clean up for the teardown to not fail. */ 7416 if (self->has_created_file) 7417 EXPECT_EQ(0, remove_path(variant->file_path)); 7418 7419 if (self->has_created_dir) { 7420 char *dir_path = dirname_alloc(variant->file_path); 7421 7422 /* Don't check for error because of cgroup specificities. */ 7423 remove_path(dir_path); 7424 free(dir_path); 7425 } 7426 7427 ruleset_fd = 7428 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 7429 7430 /* Unmount the filesystem while it is being used by a ruleset. */ 7431 set_cap(_metadata, CAP_SYS_ADMIN); 7432 ASSERT_EQ(0, umount(TMP_DIR)); 7433 clear_cap(_metadata, CAP_SYS_ADMIN); 7434 7435 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 7436 set_cap(_metadata, CAP_SYS_ADMIN); 7437 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 7438 clear_cap(_metadata, CAP_SYS_ADMIN); 7439 7440 enforce_ruleset(_metadata, ruleset_fd); 7441 ASSERT_EQ(0, close(ruleset_fd)); 7442 7443 /* Checks that access to the new mount point is denied. */ 7444 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 7445 } 7446 7447 static int matches_log_fs_extra(struct __test_metadata *const _metadata, 7448 int audit_fd, const char *const blockers, 7449 const char *const path, const char *const extra) 7450 { 7451 static const char log_template[] = REGEX_LANDLOCK_PREFIX 7452 " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$"; 7453 char *absolute_path = NULL; 7454 size_t log_match_remaining = sizeof(log_template) + strlen(blockers) + 7455 PATH_MAX * 2 + 7456 (extra ? strlen(extra) : 0) + 1; 7457 char log_match[log_match_remaining]; 7458 char *log_match_cursor = log_match; 7459 size_t chunk_len; 7460 7461 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7462 REGEX_LANDLOCK_PREFIX " blockers=%s path=\"", 7463 blockers); 7464 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7465 return -E2BIG; 7466 7467 /* 7468 * It is assumed that absolute_path does not contain control 7469 * characters nor spaces, see audit_string_contains_control(). 7470 */ 7471 absolute_path = realpath(path, NULL); 7472 if (!absolute_path) 7473 return -errno; 7474 7475 log_match_remaining -= chunk_len; 7476 log_match_cursor += chunk_len; 7477 log_match_cursor = regex_escape(absolute_path, log_match_cursor, 7478 log_match_remaining); 7479 free(absolute_path); 7480 if (log_match_cursor < 0) 7481 return (long long)log_match_cursor; 7482 7483 log_match_remaining -= log_match_cursor - log_match; 7484 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7485 "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$", 7486 extra ?: ""); 7487 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7488 return -E2BIG; 7489 7490 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, 7491 NULL); 7492 } 7493 7494 static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd, 7495 const char *const blockers, const char *const path) 7496 { 7497 return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL); 7498 } 7499 7500 FIXTURE(audit_layout1) 7501 { 7502 struct audit_filter audit_filter; 7503 int audit_fd; 7504 }; 7505 7506 FIXTURE_SETUP(audit_layout1) 7507 { 7508 prepare_layout(_metadata); 7509 7510 create_layout1(_metadata); 7511 7512 set_cap(_metadata, CAP_AUDIT_CONTROL); 7513 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 7514 EXPECT_LE(0, self->audit_fd); 7515 disable_caps(_metadata); 7516 } 7517 7518 FIXTURE_TEARDOWN_PARENT(audit_layout1) 7519 { 7520 remove_layout1(_metadata); 7521 7522 cleanup_layout(_metadata); 7523 7524 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 7525 } 7526 7527 TEST_F(audit_layout1, execute_make) 7528 { 7529 struct audit_records records; 7530 7531 copy_file(_metadata, bin_true, file1_s1d1); 7532 test_execute(_metadata, 0, file1_s1d1); 7533 test_check_exec(_metadata, 0, file1_s1d1); 7534 7535 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL); 7536 7537 test_execute(_metadata, EACCES, file1_s1d1); 7538 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7539 file1_s1d1)); 7540 test_check_exec(_metadata, EACCES, file1_s1d1); 7541 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7542 file1_s1d1)); 7543 7544 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7545 EXPECT_EQ(0, records.access); 7546 EXPECT_EQ(0, records.domain); 7547 } 7548 7549 /* 7550 * Using a set of handled/denied access rights make it possible to check that 7551 * only the blocked ones are logged. 7552 */ 7553 7554 TEST_F(audit_layout1, execute_read) 7555 { 7556 struct audit_records records; 7557 7558 copy_file(_metadata, bin_true, file1_s1d1); 7559 test_execute(_metadata, 0, file1_s1d1); 7560 test_check_exec(_metadata, 0, file1_s1d1); 7561 7562 enforce_fs(_metadata, ACCESS_ALL, NULL); 7563 7564 /* 7565 * The only difference with the previous audit_layout1.execute_read test is 7566 * the extra ",fs\\.read_file" blocked by the executable file. 7567 */ 7568 test_execute(_metadata, EACCES, file1_s1d1); 7569 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7570 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7571 test_check_exec(_metadata, EACCES, file1_s1d1); 7572 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7573 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7574 7575 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7576 EXPECT_EQ(0, records.access); 7577 EXPECT_EQ(0, records.domain); 7578 } 7579 7580 TEST_F(audit_layout1, write_file) 7581 { 7582 struct audit_records records; 7583 7584 enforce_fs(_metadata, ACCESS_ALL, NULL); 7585 7586 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 7587 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7588 "fs\\.write_file", file1_s1d1)); 7589 7590 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7591 EXPECT_EQ(0, records.access); 7592 EXPECT_EQ(1, records.domain); 7593 } 7594 7595 TEST_F(audit_layout1, read_file) 7596 { 7597 struct audit_records records; 7598 7599 enforce_fs(_metadata, ACCESS_ALL, NULL); 7600 7601 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 7602 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file", 7603 file1_s1d1)); 7604 7605 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7606 EXPECT_EQ(0, records.access); 7607 EXPECT_EQ(1, records.domain); 7608 } 7609 7610 TEST_F(audit_layout1, read_dir) 7611 { 7612 struct audit_records records; 7613 7614 enforce_fs(_metadata, ACCESS_ALL, NULL); 7615 7616 EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY)); 7617 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir", 7618 dir_s1d1)); 7619 7620 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7621 EXPECT_EQ(0, records.access); 7622 EXPECT_EQ(1, records.domain); 7623 } 7624 7625 TEST_F(audit_layout1, remove_dir) 7626 { 7627 struct audit_records records; 7628 7629 EXPECT_EQ(0, unlink(file1_s1d3)); 7630 EXPECT_EQ(0, unlink(file2_s1d3)); 7631 7632 enforce_fs(_metadata, ACCESS_ALL, NULL); 7633 7634 EXPECT_EQ(-1, rmdir(dir_s1d3)); 7635 EXPECT_EQ(EACCES, errno); 7636 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7637 "fs\\.remove_dir", dir_s1d2)); 7638 7639 EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 7640 EXPECT_EQ(EACCES, errno); 7641 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7642 "fs\\.remove_dir", dir_s1d2)); 7643 7644 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7645 EXPECT_EQ(0, records.access); 7646 EXPECT_EQ(0, records.domain); 7647 } 7648 7649 TEST_F(audit_layout1, remove_file) 7650 { 7651 struct audit_records records; 7652 7653 enforce_fs(_metadata, ACCESS_ALL, NULL); 7654 7655 EXPECT_EQ(-1, unlink(file1_s1d3)); 7656 EXPECT_EQ(EACCES, errno); 7657 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7658 "fs\\.remove_file", dir_s1d3)); 7659 7660 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7661 EXPECT_EQ(0, records.access); 7662 EXPECT_EQ(1, records.domain); 7663 } 7664 7665 TEST_F(audit_layout1, make_char) 7666 { 7667 struct audit_records records; 7668 7669 EXPECT_EQ(0, unlink(file1_s1d3)); 7670 7671 enforce_fs(_metadata, ACCESS_ALL, NULL); 7672 7673 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, makedev(7, 0))); 7674 EXPECT_EQ(EACCES, errno); 7675 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char", 7676 dir_s1d3)); 7677 7678 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7679 EXPECT_EQ(0, records.access); 7680 EXPECT_EQ(1, records.domain); 7681 } 7682 7683 TEST_F(audit_layout1, make_whiteout) 7684 { 7685 struct audit_records records; 7686 7687 EXPECT_EQ(0, unlink(file1_s1d3)); 7688 7689 enforce_fs(_metadata, ACCESS_ALL, NULL); 7690 7691 /* Whiteout creation is denied and logged as fs.make_reg. */ 7692 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, makedev(0, 0))); 7693 EXPECT_EQ(EACCES, errno); 7694 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg", 7695 dir_s1d3)); 7696 7697 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7698 EXPECT_EQ(0, records.access); 7699 EXPECT_EQ(1, records.domain); 7700 } 7701 7702 TEST_F(audit_layout1, make_dir) 7703 { 7704 struct audit_records records; 7705 7706 EXPECT_EQ(0, unlink(file1_s1d3)); 7707 7708 enforce_fs(_metadata, ACCESS_ALL, NULL); 7709 7710 EXPECT_EQ(-1, mkdir(file1_s1d3, 0755)); 7711 EXPECT_EQ(EACCES, errno); 7712 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir", 7713 dir_s1d3)); 7714 7715 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7716 EXPECT_EQ(0, records.access); 7717 EXPECT_EQ(1, records.domain); 7718 } 7719 7720 TEST_F(audit_layout1, make_reg) 7721 { 7722 struct audit_records records; 7723 7724 EXPECT_EQ(0, unlink(file1_s1d3)); 7725 7726 enforce_fs(_metadata, ACCESS_ALL, NULL); 7727 7728 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFREG | 0644, 0)); 7729 EXPECT_EQ(EACCES, errno); 7730 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg", 7731 dir_s1d3)); 7732 7733 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7734 EXPECT_EQ(0, records.access); 7735 EXPECT_EQ(1, records.domain); 7736 } 7737 7738 TEST_F(audit_layout1, make_sock) 7739 { 7740 struct audit_records records; 7741 7742 EXPECT_EQ(0, unlink(file1_s1d3)); 7743 7744 enforce_fs(_metadata, ACCESS_ALL, NULL); 7745 7746 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFSOCK | 0644, 0)); 7747 EXPECT_EQ(EACCES, errno); 7748 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock", 7749 dir_s1d3)); 7750 7751 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7752 EXPECT_EQ(0, records.access); 7753 EXPECT_EQ(1, records.domain); 7754 } 7755 7756 TEST_F(audit_layout1, make_fifo) 7757 { 7758 struct audit_records records; 7759 7760 EXPECT_EQ(0, unlink(file1_s1d3)); 7761 7762 enforce_fs(_metadata, ACCESS_ALL, NULL); 7763 7764 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFIFO | 0644, 0)); 7765 EXPECT_EQ(EACCES, errno); 7766 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo", 7767 dir_s1d3)); 7768 7769 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7770 EXPECT_EQ(0, records.access); 7771 EXPECT_EQ(1, records.domain); 7772 } 7773 7774 TEST_F(audit_layout1, make_block) 7775 { 7776 struct audit_records records; 7777 7778 EXPECT_EQ(0, unlink(file1_s1d3)); 7779 7780 enforce_fs(_metadata, ACCESS_ALL, NULL); 7781 7782 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0)); 7783 EXPECT_EQ(EACCES, errno); 7784 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7785 "fs\\.make_block", dir_s1d3)); 7786 7787 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7788 EXPECT_EQ(0, records.access); 7789 EXPECT_EQ(1, records.domain); 7790 } 7791 7792 TEST_F(audit_layout1, make_sym) 7793 { 7794 struct audit_records records; 7795 7796 EXPECT_EQ(0, unlink(file1_s1d3)); 7797 7798 enforce_fs(_metadata, ACCESS_ALL, NULL); 7799 7800 EXPECT_EQ(-1, symlink("target", file1_s1d3)); 7801 EXPECT_EQ(EACCES, errno); 7802 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym", 7803 dir_s1d3)); 7804 7805 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7806 EXPECT_EQ(0, records.access); 7807 EXPECT_EQ(1, records.domain); 7808 } 7809 7810 TEST_F(audit_layout1, refer_handled) 7811 { 7812 struct audit_records records; 7813 7814 EXPECT_EQ(0, unlink(file1_s1d3)); 7815 7816 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, NULL); 7817 7818 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7819 EXPECT_EQ(EXDEV, errno); 7820 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7821 dir_s1d1)); 7822 EXPECT_EQ(0, 7823 matches_log_domain_allocated(self->audit_fd, getpid(), NULL)); 7824 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7825 dir_s1d3)); 7826 7827 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7828 EXPECT_EQ(0, records.access); 7829 EXPECT_EQ(0, records.domain); 7830 } 7831 7832 TEST_F(audit_layout1, refer_make) 7833 { 7834 struct audit_records records; 7835 7836 EXPECT_EQ(0, unlink(file1_s1d3)); 7837 7838 enforce_fs(_metadata, 7839 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, 7840 NULL); 7841 7842 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7843 EXPECT_EQ(EACCES, errno); 7844 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7845 dir_s1d1)); 7846 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7847 "fs\\.make_reg,fs\\.refer", dir_s1d3)); 7848 7849 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7850 EXPECT_EQ(0, records.access); 7851 EXPECT_EQ(0, records.domain); 7852 } 7853 7854 TEST_F(audit_layout1, refer_rename) 7855 { 7856 struct audit_records records; 7857 7858 EXPECT_EQ(0, unlink(file1_s1d3)); 7859 7860 enforce_fs(_metadata, ACCESS_ALL, NULL); 7861 7862 EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3)); 7863 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7864 "fs\\.remove_file,fs\\.refer", dir_s1d2)); 7865 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7866 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7867 dir_s2d3)); 7868 7869 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7870 EXPECT_EQ(0, records.access); 7871 EXPECT_EQ(0, records.domain); 7872 } 7873 7874 TEST_F(audit_layout1, refer_exchange) 7875 { 7876 struct audit_records records; 7877 7878 EXPECT_EQ(0, unlink(file1_s1d3)); 7879 7880 enforce_fs(_metadata, ACCESS_ALL, NULL); 7881 7882 /* 7883 * The only difference with the previous audit_layout1.refer_rename test is 7884 * the extra ",fs\\.make_reg" blocked by the source directory. 7885 */ 7886 EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3)); 7887 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7888 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7889 dir_s1d2)); 7890 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7891 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7892 dir_s2d3)); 7893 7894 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7895 EXPECT_EQ(0, records.access); 7896 EXPECT_EQ(0, records.domain); 7897 } 7898 7899 /* 7900 * This test checks that the audit record is correctly generated when the 7901 * operation is only partially denied. This is the case for rename(2) when the 7902 * source file is allowed to be referenced but the destination directory is not. 7903 * 7904 * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize 7905 * file path walks and prepare for audit support") and commit 058518c20920 7906 * ("landlock: Align partial refer access checks with final ones"). 7907 */ 7908 TEST_F(audit_layout1, refer_rename_half) 7909 { 7910 struct audit_records records; 7911 const struct rule layer1[] = { 7912 { 7913 .path = dir_s2d2, 7914 .access = LANDLOCK_ACCESS_FS_REFER, 7915 }, 7916 {}, 7917 }; 7918 7919 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 7920 7921 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 7922 ASSERT_EQ(EXDEV, errno); 7923 7924 /* Only half of the request is denied. */ 7925 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7926 dir_s1d1)); 7927 7928 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7929 EXPECT_EQ(0, records.access); 7930 EXPECT_EQ(1, records.domain); 7931 } 7932 7933 TEST_F(audit_layout1, truncate) 7934 { 7935 struct audit_records records; 7936 7937 enforce_fs(_metadata, ACCESS_ALL, NULL); 7938 7939 EXPECT_EQ(-1, truncate(file1_s1d3, 0)); 7940 EXPECT_EQ(EACCES, errno); 7941 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate", 7942 file1_s1d3)); 7943 7944 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7945 EXPECT_EQ(0, records.access); 7946 EXPECT_EQ(1, records.domain); 7947 } 7948 7949 /* Checks that audit records preserve every ioctl command bit. */ 7950 TEST_F(audit_layout1, ioctl_dev) 7951 { 7952 struct audit_records records; 7953 int fd; 7954 7955 enforce_fs(_metadata, ACCESS_ALL & ~LANDLOCK_ACCESS_FS_READ_FILE, NULL); 7956 7957 fd = open("/dev/null", O_RDONLY | O_CLOEXEC); 7958 ASSERT_LE(0, fd); 7959 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, unknown_ioctl_cmd)); 7960 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7961 "fs\\.ioctl_dev", "/dev/null", 7962 " ioctlcmd=0xc00ffeee")); 7963 7964 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7965 EXPECT_EQ(0, records.access); 7966 EXPECT_EQ(1, records.domain); 7967 } 7968 7969 TEST_F(audit_layout1, resolve_unix) 7970 { 7971 struct audit_records records; 7972 const char *const path = "sock"; 7973 int srv_fd, cli_fd, status; 7974 pid_t child_pid; 7975 7976 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path); 7977 7978 child_pid = fork(); 7979 ASSERT_LE(0, child_pid); 7980 if (!child_pid) { 7981 enforce_fs(_metadata, ACCESS_ALL, NULL); 7982 7983 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 7984 ASSERT_LE(0, cli_fd); 7985 EXPECT_EQ(EACCES, 7986 test_connect_named_unix(_metadata, cli_fd, path)); 7987 7988 EXPECT_EQ(0, close(cli_fd)); 7989 _exit(_metadata->exit_code); 7990 } 7991 7992 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); 7993 EXPECT_EQ(1, WIFEXITED(status)); 7994 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 7995 7996 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7997 "fs\\.resolve_unix", path, NULL)); 7998 7999 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 8000 EXPECT_EQ(0, records.access); 8001 EXPECT_EQ(1, records.domain); 8002 8003 EXPECT_EQ(0, close(srv_fd)); 8004 } 8005 8006 TEST_F(audit_layout1, mount) 8007 { 8008 struct audit_records records; 8009 8010 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL); 8011 8012 set_cap(_metadata, CAP_SYS_ADMIN); 8013 EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 8014 EXPECT_EQ(EPERM, errno); 8015 clear_cap(_metadata, CAP_SYS_ADMIN); 8016 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 8017 "fs\\.change_topology", dir_s3d2)); 8018 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 8019 EXPECT_EQ(0, records.access); 8020 EXPECT_EQ(1, records.domain); 8021 } 8022 8023 static bool debug_quiet_tests; 8024 8025 FIXTURE(audit_quiet_layout1) 8026 { 8027 struct audit_filter audit_filter; 8028 int audit_fd; 8029 }; 8030 8031 FIXTURE_SETUP(audit_quiet_layout1) 8032 { 8033 prepare_layout(_metadata); 8034 create_layout1(_metadata); 8035 8036 set_cap(_metadata, CAP_AUDIT_CONTROL); 8037 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 8038 EXPECT_LE(0, self->audit_fd); 8039 clear_cap(_metadata, CAP_AUDIT_CONTROL); 8040 8041 if (getenv("DEBUG_QUIET_TESTS")) 8042 debug_quiet_tests = true; 8043 } 8044 8045 FIXTURE_TEARDOWN_PARENT(audit_quiet_layout1) 8046 { 8047 remove_layout1(_metadata); 8048 cleanup_layout(_metadata); 8049 8050 set_cap(_metadata, CAP_AUDIT_CONTROL); 8051 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 8052 clear_cap(_metadata, CAP_AUDIT_CONTROL); 8053 } 8054 8055 struct a_rule { 8056 const char *path; 8057 __u64 access; 8058 bool quiet; 8059 }; 8060 8061 struct a_layer { 8062 __u64 handled_access_fs; 8063 __u64 quiet_access_fs; 8064 struct a_rule rules[6]; 8065 __u64 restrict_flags; 8066 }; 8067 8068 struct a_target { 8069 /* File/dir to try open. */ 8070 const char *target; 8071 /* Open mode (one of O_RDONLY, O_WRONLY, or O_RDWR). */ 8072 int open_mode; 8073 /* Should open succeed? */ 8074 bool expect_open_success; 8075 /* If open fails, whether to expect an audit log for read. */ 8076 bool audit_read_blocked; 8077 /* If open fails, whether to expect an audit log for write. */ 8078 bool audit_write_blocked; 8079 /* If ftruncate() is expected to be allowed. */ 8080 bool expect_truncate_success; 8081 /* If ftruncate fails, whether to expect an audit log. */ 8082 bool audit_truncate; 8083 /* 8084 * If ioctl() is expected to be allowed (ioctl not attempted if neither 8085 * this nor expect_ioctl_denied is set). 8086 */ 8087 bool expect_ioctl_allowed; 8088 /* If ioctl() is expected to be denied. */ 8089 bool expect_ioctl_denied; 8090 /* If ioctl fails, whether to expect an audit log. */ 8091 bool audit_ioctl; 8092 }; 8093 8094 #define AUDIT_QUIET_MAX_TARGETS 10 8095 8096 FIXTURE_VARIANT(audit_quiet_layout1) 8097 { 8098 struct a_layer layers[3]; 8099 struct a_target targets[AUDIT_QUIET_MAX_TARGETS]; 8100 }; 8101 8102 #define FS_R LANDLOCK_ACCESS_FS_READ_FILE 8103 #define FS_W LANDLOCK_ACCESS_FS_WRITE_FILE 8104 #define FS_TRUNC LANDLOCK_ACCESS_FS_TRUNCATE 8105 #define FS_IOCTL LANDLOCK_ACCESS_FS_IOCTL_DEV 8106 8107 static int sprint_access_bits(char *buf, size_t buflen, __u64 access) 8108 { 8109 size_t offset = 0; 8110 8111 if (buflen < strlen("rwti make_reg remove_file refer") + 1) 8112 abort(); 8113 8114 buf[0] = '\0'; 8115 if (access & FS_R) 8116 offset += snprintf(buf + offset, buflen - offset, "r"); 8117 if (access & FS_W) 8118 offset += snprintf(buf + offset, buflen - offset, "w"); 8119 if (access & FS_TRUNC) 8120 offset += snprintf(buf + offset, buflen - offset, "t"); 8121 if (access & FS_IOCTL) 8122 offset += snprintf(buf + offset, buflen - offset, "i"); 8123 if (access & LANDLOCK_ACCESS_FS_MAKE_REG) 8124 offset += snprintf(buf + offset, buflen - offset, ",make_reg"); 8125 if (access & LANDLOCK_ACCESS_FS_REMOVE_FILE) 8126 offset += 8127 snprintf(buf + offset, buflen - offset, ",remove_file"); 8128 if (access & LANDLOCK_ACCESS_FS_REFER) 8129 offset += snprintf(buf + offset, buflen - offset, ",refer"); 8130 8131 if (buf[0] == ',') { 8132 offset--; 8133 memmove(buf, buf + 1, offset); 8134 buf[offset] = '\0'; 8135 } 8136 8137 return offset; 8138 } 8139 8140 static int apply_a_layer(struct __test_metadata *const _metadata, 8141 const struct a_layer *l) 8142 { 8143 struct landlock_ruleset_attr rs_attr = { 8144 .handled_access_fs = l->handled_access_fs, 8145 .quiet_access_fs = l->quiet_access_fs, 8146 }; 8147 int rs_fd; 8148 int i; 8149 const struct a_rule *r; 8150 char handled_access_s[33], quiet_access_s[33], rule_access_s[33]; 8151 8152 if (!l->handled_access_fs) 8153 return 0; 8154 8155 rs_fd = landlock_create_ruleset(&rs_attr, sizeof(rs_attr), 0); 8156 ASSERT_LE(0, rs_fd); 8157 8158 for (i = 0; i < ARRAY_SIZE(l->rules); i++) { 8159 r = &l->rules[i]; 8160 if (!r->path) 8161 continue; 8162 8163 add_path_beneath(_metadata, rs_fd, r->access, r->path, 8164 r->quiet ? LANDLOCK_ADD_RULE_QUIET : 0); 8165 } 8166 8167 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 8168 ASSERT_EQ(0, landlock_restrict_self(rs_fd, l->restrict_flags)) 8169 { 8170 TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 8171 } 8172 ASSERT_EQ(0, close(rs_fd)); 8173 8174 if (debug_quiet_tests) { 8175 sprint_access_bits(handled_access_s, sizeof(handled_access_s), 8176 l->handled_access_fs); 8177 sprint_access_bits(quiet_access_s, sizeof(quiet_access_s), 8178 l->quiet_access_fs); 8179 TH_LOG("applied layer: handled=%s quiet=%s restrict_flags=0x%llx", 8180 handled_access_s, quiet_access_s, 8181 (unsigned long long)l->restrict_flags); 8182 for (i = 0; i < ARRAY_SIZE(l->rules); i++) { 8183 r = &l->rules[i]; 8184 if (!r->path) 8185 continue; 8186 8187 sprint_access_bits(rule_access_s, sizeof(rule_access_s), 8188 r->access); 8189 TH_LOG(" rule[%d]: path=%s access=%s quiet=%d", i, 8190 r->path, rule_access_s, r->quiet); 8191 } 8192 } 8193 return 0; 8194 } 8195 8196 void audit_quiet_layout1_test_body(struct __test_metadata *const _metadata, 8197 FIXTURE_DATA(audit_quiet_layout1) * self, 8198 const struct a_target *targets) 8199 { 8200 struct audit_records records = {}; 8201 int i; 8202 const struct a_target *target; 8203 int fd = -1; 8204 int open_mode; 8205 int ret; 8206 bool expect_audit; 8207 const char *blocker; 8208 8209 for (i = 0; i < AUDIT_QUIET_MAX_TARGETS; i++) { 8210 target = &targets[i]; 8211 if (!target->target) 8212 continue; 8213 8214 open_mode = target->open_mode & (O_RDONLY | O_WRONLY | O_RDWR); 8215 8216 EXPECT_TRUE(open_mode == O_RDONLY || open_mode == O_WRONLY || 8217 open_mode == O_RDWR); 8218 8219 if (target->expect_open_success) { 8220 EXPECT_FALSE(target->audit_read_blocked); 8221 EXPECT_FALSE(target->audit_write_blocked); 8222 } 8223 if (target->expect_truncate_success) 8224 EXPECT_TRUE(target->expect_open_success && 8225 !target->audit_truncate); 8226 8227 if (debug_quiet_tests) 8228 TH_LOG("Try open \"%s\" with %s%s", target->target, 8229 open_mode != O_WRONLY ? "r" : "", 8230 open_mode != O_RDONLY ? "w" : ""); 8231 8232 fd = openat(AT_FDCWD, target->target, open_mode | O_CLOEXEC); 8233 if (target->expect_open_success) { 8234 ASSERT_LE(0, fd) 8235 { 8236 TH_LOG("Failed to open \"%s\": %s", 8237 target->target, strerror(errno)); 8238 }; 8239 } else { 8240 ASSERT_EQ(-1, fd); 8241 ASSERT_EQ(EACCES, errno); 8242 } 8243 8244 expect_audit = true; 8245 8246 if (target->audit_read_blocked && target->audit_write_blocked) 8247 blocker = "fs\\.write_file,fs\\.read_file"; 8248 else if (target->audit_read_blocked) 8249 blocker = "fs\\.read_file"; 8250 else if (target->audit_write_blocked) 8251 blocker = "fs\\.write_file"; 8252 else 8253 expect_audit = false; 8254 8255 if (expect_audit) 8256 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 8257 blocker, target->target)); 8258 8259 /* Check that we see no (other) logs. */ 8260 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 8261 ASSERT_EQ(0, records.access); 8262 8263 if (target->expect_open_success && fd >= 0) { 8264 if (debug_quiet_tests) 8265 TH_LOG("Try ftruncate \"%s\"", target->target); 8266 8267 ret = ftruncate(fd, 0); 8268 if (target->expect_truncate_success) { 8269 ASSERT_EQ(0, ret); 8270 } else { 8271 ASSERT_EQ(-1, ret); 8272 if (open_mode != O_RDONLY) 8273 ASSERT_EQ(EACCES, errno); 8274 } 8275 8276 if (target->audit_truncate) 8277 ASSERT_EQ(0, matches_log_fs(_metadata, 8278 self->audit_fd, 8279 "fs\\.truncate", 8280 target->target)); 8281 8282 if (target->expect_ioctl_allowed || 8283 target->expect_ioctl_denied) { 8284 if (debug_quiet_tests) 8285 TH_LOG("Try ioctl FIONREAD on \"%s\"", 8286 target->target); 8287 8288 ret = ioctl_error(_metadata, fd, FIONREAD); 8289 if (target->expect_ioctl_allowed) { 8290 ASSERT_NE(EACCES, ret); 8291 } else { 8292 ASSERT_EQ(EACCES, ret); 8293 } 8294 } 8295 8296 if (target->audit_ioctl) 8297 ASSERT_EQ(0, matches_log_fs_extra( 8298 _metadata, self->audit_fd, 8299 "fs\\.ioctl_dev", 8300 target->target, 8301 " ioctlcmd=0x541b\\+")); 8302 8303 /* Check that we see no other logs. */ 8304 EXPECT_EQ(0, audit_count_records(self->audit_fd, 8305 &records)); 8306 ASSERT_EQ(0, records.access); 8307 ASSERT_EQ(0, close(fd)); 8308 } 8309 } 8310 } 8311 8312 TEST_F(audit_quiet_layout1, base) 8313 { 8314 int i; 8315 8316 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 8317 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 8318 8319 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 8320 } 8321 8322 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_simple) { 8323 .layers = { 8324 { 8325 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8326 .quiet_access_fs = FS_R, 8327 .rules = { 8328 { .path = dir_s1d1, .access = 0, .quiet = true }, 8329 }, 8330 }, 8331 }, 8332 .targets = { 8333 { 8334 .target = file1_s1d1, 8335 .open_mode = O_RDONLY, 8336 }, 8337 /* Not covered by quiet */ 8338 { 8339 .target = file1_s2d1, 8340 .open_mode = O_RDONLY, 8341 .audit_read_blocked = true, 8342 }, 8343 /* Access not quieted */ 8344 { 8345 .target = file1_s1d1, 8346 .open_mode = O_WRONLY, 8347 .audit_write_blocked = true, 8348 }, 8349 /* 8350 * Quiet flag only takes effect if all blocked access bits are 8351 * quieted, otherwise audit log emitted as normal (with all 8352 * blockers) 8353 */ 8354 { 8355 .target = file1_s1d1, 8356 .open_mode = O_RDWR, 8357 .audit_read_blocked = true, 8358 .audit_write_blocked = true, 8359 }, 8360 }, 8361 }; 8362 8363 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_read) { 8364 .layers = { 8365 { 8366 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8367 .quiet_access_fs = FS_W, 8368 .rules = { 8369 { .path = dir_s1d1, .access = FS_R, .quiet = true }, 8370 /* Quiet flags inherit down and are not overridden */ 8371 { .path = file1_s1d1, .access = FS_R, .quiet = false }, 8372 { .path = file1_s2d3, .access = 0, .quiet = true }, 8373 }, 8374 }, 8375 }, 8376 .targets = { 8377 /* Read ok */ 8378 { 8379 .target = file1_s1d1, 8380 .open_mode = O_RDONLY, 8381 .expect_open_success = true, 8382 }, 8383 /* Write quieted */ 8384 { 8385 .target = file1_s1d1, 8386 .open_mode = O_WRONLY, 8387 }, 8388 /* Read allowed, write quieted so no audit */ 8389 { 8390 .target = file1_s1d1, 8391 .open_mode = O_RDWR, 8392 }, 8393 /* Not covered by quiet */ 8394 { 8395 .target = file1_s2d2, 8396 .open_mode = O_WRONLY, 8397 .audit_write_blocked = true, 8398 }, 8399 { 8400 .target = file1_s2d2, 8401 .open_mode = O_RDWR, 8402 .audit_read_blocked = true, 8403 .audit_write_blocked = true, 8404 }, 8405 /* Single file quiet */ 8406 { 8407 .target = file1_s2d3, 8408 .open_mode = O_WRONLY, 8409 }, 8410 /* Wrong file */ 8411 { 8412 .target = file2_s2d3, 8413 .open_mode = O_WRONLY, 8414 .audit_write_blocked = true, 8415 }, 8416 /* Access not quieted */ 8417 { 8418 .target = file1_s2d3, 8419 .open_mode = O_RDONLY, 8420 .audit_read_blocked = true, 8421 }, 8422 /* Some access not quieted */ 8423 { 8424 .target = file1_s2d3, 8425 .open_mode = O_RDWR, 8426 .audit_read_blocked = true, 8427 .audit_write_blocked = true, 8428 }, 8429 }, 8430 }; 8431 8432 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_write) { 8433 .layers = { 8434 { 8435 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8436 .quiet_access_fs = FS_R, 8437 .rules = { 8438 { .path = dir_s1d1, .access = FS_W, .quiet = true }, 8439 }, 8440 }, 8441 }, 8442 .targets = { 8443 /* Read quieted */ 8444 { 8445 .target = file1_s1d1, 8446 .open_mode = O_RDONLY, 8447 }, 8448 /* Truncate not quieted */ 8449 { 8450 .target = file1_s1d1, 8451 .open_mode = O_WRONLY, 8452 .expect_open_success = true, 8453 .audit_truncate = true, 8454 }, 8455 /* Not covered by quiet */ 8456 { 8457 .target = file1_s2d1, 8458 .open_mode = O_RDONLY, 8459 .audit_read_blocked = true, 8460 }, 8461 /* Write allowed, read quieted so no audit */ 8462 { 8463 .target = file1_s1d1, 8464 .open_mode = O_RDWR, 8465 }, 8466 }, 8467 }; 8468 8469 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_write_quiet_trunc) { 8470 .layers = { 8471 { 8472 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8473 .quiet_access_fs = FS_TRUNC, 8474 .rules = { 8475 { .path = dir_s1d1, .access = FS_W, .quiet = true }, 8476 { .path = dir_s2d1, .access = FS_W, .quiet = false }, 8477 }, 8478 }, 8479 }, 8480 .targets = { 8481 /* Read not allowed and not quieted */ 8482 { 8483 .target = file1_s1d1, 8484 .open_mode = O_RDONLY, 8485 .audit_read_blocked = true, 8486 }, 8487 /* Truncate quieted */ 8488 { 8489 .target = file1_s1d1, 8490 .open_mode = O_WRONLY, 8491 .expect_open_success = true, 8492 }, 8493 /* Not covered by quiet (truncate) */ 8494 { 8495 .target = file1_s2d1, 8496 .open_mode = O_WRONLY, 8497 .expect_open_success = true, 8498 .audit_truncate = true, 8499 }, 8500 /* Not covered by quiet (read/write) */ 8501 { 8502 .target = file1_s3d1, 8503 .open_mode = O_RDWR, 8504 .audit_read_blocked = true, 8505 .audit_write_blocked = true, 8506 }, 8507 }, 8508 }; 8509 8510 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_rw_quiet_trunc) { 8511 .layers = { 8512 { 8513 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8514 .quiet_access_fs = FS_TRUNC, 8515 .rules = { 8516 { .path = dir_s1d1, .access = FS_R | FS_W, .quiet = true }, 8517 { .path = dir_s2d1, .access = FS_R | FS_W, .quiet = false }, 8518 }, 8519 }, 8520 }, 8521 .targets = { 8522 { 8523 .target = file1_s1d1, 8524 .open_mode = O_RDWR, 8525 .expect_open_success = true, 8526 }, 8527 { 8528 .target = file1_s2d1, 8529 .open_mode = O_RDWR, 8530 .expect_open_success = true, 8531 .audit_truncate = true, 8532 }, 8533 }, 8534 }; 8535 8536 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_all) { 8537 .layers = { 8538 { 8539 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8540 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8541 .rules = { 8542 { .path = dir_s1d1, .access = 0, .quiet = true }, 8543 { .path = file1_s2d1, .access = FS_R | FS_W, .quiet = true }, 8544 { .path = file1_s2d3, .access = 0, .quiet = true }, 8545 { .path = dir_s3d1, .access = FS_W, .quiet = false }, 8546 { .path = "/dev/zero", .access = FS_R, .quiet = false }, 8547 { .path = "/dev/null", .access = FS_R, .quiet = true }, 8548 }, 8549 }, 8550 }, 8551 .targets = { 8552 /* No logs */ 8553 { 8554 .target = file1_s1d1, 8555 .open_mode = O_RDONLY, 8556 }, 8557 { 8558 .target = file1_s1d1, 8559 .open_mode = O_WRONLY, 8560 }, 8561 { 8562 .target = file1_s1d1, 8563 .open_mode = O_RDWR, 8564 }, 8565 /* Truncate quieted - no log */ 8566 { 8567 .target = file1_s2d1, 8568 .open_mode = O_RDWR, 8569 .expect_open_success = true, 8570 }, 8571 /* Truncate not covered by quiet */ 8572 { 8573 .target = file1_s3d1, 8574 .open_mode = O_WRONLY, 8575 .expect_open_success = true, 8576 .audit_truncate = true, 8577 }, 8578 /* Not covered by quiet */ 8579 { 8580 .target = file1_s3d1, 8581 .open_mode = O_RDONLY, 8582 .audit_read_blocked = true, 8583 }, 8584 /* Single file quiet */ 8585 { 8586 .target = file1_s2d3, 8587 .open_mode = O_RDWR, 8588 }, 8589 /* Wrong file */ 8590 { 8591 .target = file2_s2d3, 8592 .open_mode = O_RDWR, 8593 .audit_read_blocked = true, 8594 .audit_write_blocked = true, 8595 }, 8596 /* Ioctl quieted */ 8597 { 8598 .target = "/dev/null", 8599 .open_mode = O_RDONLY, 8600 .expect_open_success = true, 8601 .expect_ioctl_denied = true, 8602 }, 8603 /* Ioctl not quieted */ 8604 { 8605 .target = "/dev/zero", 8606 .open_mode = O_RDONLY, 8607 .expect_open_success = true, 8608 .expect_ioctl_denied = true, 8609 .audit_ioctl = true, 8610 }, 8611 }, 8612 }; 8613 8614 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_across_mountpoint) { 8615 .layers = { 8616 { 8617 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 8618 .quiet_access_fs = FS_R, 8619 .rules = { 8620 { .path = dir_s3d1, .access = 0, .quiet = true }, 8621 }, 8622 }, 8623 }, 8624 .targets = { 8625 { 8626 .target = file1_s3d3, 8627 .open_mode = O_RDONLY, 8628 }, 8629 /* Not covered by quiet */ 8630 { 8631 .target = file1_s1d1, 8632 .open_mode = O_RDONLY, 8633 .audit_read_blocked = true, 8634 }, 8635 { 8636 .target = file1_s1d1, 8637 .open_mode = O_RDWR, 8638 .audit_read_blocked = true, 8639 .audit_write_blocked = true, 8640 }, 8641 /* Access not quieted */ 8642 { 8643 .target = file1_s3d3, 8644 .open_mode = O_WRONLY, 8645 .audit_write_blocked = true, 8646 }, 8647 }, 8648 }; 8649 8650 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_all_quiet) { 8651 .layers = { 8652 { 8653 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8654 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8655 .rules = { 8656 { 8657 .path = dir_s1d1, 8658 .access = FS_R | FS_W | FS_TRUNC, 8659 .quiet = true 8660 }, 8661 { 8662 .path = "/dev/null", 8663 .access = FS_R | FS_W | FS_IOCTL, 8664 .quiet = true 8665 }, 8666 }, 8667 }, 8668 }, 8669 .targets = { 8670 { 8671 .target = file1_s1d1, 8672 .open_mode = O_RDWR, 8673 .expect_open_success = true, 8674 .expect_truncate_success = true, 8675 }, 8676 { 8677 .target = "/dev/null", 8678 .open_mode = O_RDONLY, 8679 .expect_open_success = true, 8680 .expect_ioctl_allowed = true, 8681 }, 8682 }, 8683 }; 8684 8685 /* 8686 * With LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, it doesn't matter what the 8687 * quiet flags below the layer say. 8688 */ 8689 FIXTURE_VARIANT_ADD(audit_quiet_layout1, subdomains_off) { 8690 .layers = { 8691 { 8692 .handled_access_fs = FS_R, 8693 .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, 8694 .rules = { 8695 { .path = "/", .access = FS_R, .quiet = false }, 8696 } 8697 }, 8698 { 8699 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8700 .quiet_access_fs = FS_R, 8701 .rules = { 8702 { .path = dir_s1d1, .access = 0, .quiet = true }, 8703 { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true }, 8704 { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false }, 8705 { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true }, 8706 { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false }, 8707 }, 8708 }, 8709 }, 8710 .targets = { 8711 { 8712 .target = file1_s1d1, 8713 .open_mode = O_RDWR, 8714 }, 8715 { 8716 .target = file1_s2d1, 8717 .open_mode = O_RDWR, 8718 }, 8719 { 8720 .target = file1_s2d2, 8721 .open_mode = O_RDWR, 8722 .expect_open_success = true, 8723 /* No audit_truncate */ 8724 }, 8725 { 8726 .target = file1_s2d3, 8727 .open_mode = O_RDWR, 8728 .expect_open_success = true, 8729 /* No audit_truncate */ 8730 }, 8731 { 8732 .target = "/dev/null", 8733 .open_mode = O_RDONLY, 8734 .expect_open_success = true, 8735 .expect_ioctl_denied = true, 8736 /* No audit_ioctl */ 8737 }, 8738 { 8739 .target = "/dev/zero", 8740 .open_mode = O_RDONLY, 8741 .expect_open_success = true, 8742 .expect_ioctl_denied = true, 8743 /* No audit_ioctl */ 8744 }, 8745 }, 8746 }; 8747 8748 /* 8749 * With LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, it doesn't matter what the 8750 * quiet flags on the layer say. 8751 */ 8752 FIXTURE_VARIANT_ADD(audit_quiet_layout1, same_exec_off) { 8753 .layers = { 8754 { 8755 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8756 .quiet_access_fs = FS_R, 8757 .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, 8758 .rules = { 8759 { .path = dir_s1d1, .access = 0, .quiet = true }, 8760 { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true }, 8761 { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false }, 8762 { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true }, 8763 { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false }, 8764 }, 8765 }, 8766 }, 8767 .targets = { 8768 { 8769 .target = file1_s1d1, 8770 .open_mode = O_RDWR, 8771 }, 8772 { 8773 .target = file1_s2d1, 8774 .open_mode = O_RDWR, 8775 }, 8776 { 8777 .target = file1_s2d2, 8778 .open_mode = O_RDWR, 8779 .expect_open_success = true, 8780 /* No audit_truncate */ 8781 }, 8782 { 8783 .target = file1_s2d3, 8784 .open_mode = O_RDWR, 8785 .expect_open_success = true, 8786 /* No audit_truncate */ 8787 }, 8788 { 8789 .target = "/dev/null", 8790 .open_mode = O_RDONLY, 8791 .expect_open_success = true, 8792 .expect_ioctl_denied = true, 8793 /* No audit_ioctl */ 8794 }, 8795 { 8796 .target = "/dev/zero", 8797 .open_mode = O_RDONLY, 8798 .expect_open_success = true, 8799 .expect_ioctl_denied = true, 8800 /* No audit_ioctl */ 8801 }, 8802 }, 8803 }; 8804 8805 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_1) { 8806 /* Here, rules that deny access are always quiet. */ 8807 .layers = { 8808 { 8809 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8810 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8811 .rules = { 8812 { 8813 .path = dir_s1d1, 8814 .access = FS_W, 8815 .quiet = true, 8816 }, 8817 { 8818 .path = dir_s2d1, 8819 .access = FS_R | FS_W | FS_TRUNC, 8820 .quiet = false, 8821 }, 8822 { 8823 .path = "/dev/null", 8824 .access = FS_R, 8825 .quiet = true, 8826 }, 8827 { 8828 .path = "/dev/zero", 8829 .access = FS_R | FS_W | FS_IOCTL, 8830 .quiet = false, 8831 }, 8832 }, 8833 }, 8834 { 8835 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8836 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8837 .rules = { 8838 { 8839 .path = dir_s1d1, 8840 .access = FS_R | FS_W | FS_TRUNC, 8841 .quiet = false, 8842 }, 8843 { 8844 .path = dir_s2d1, 8845 .access = FS_W, 8846 .quiet = true, 8847 }, 8848 { 8849 .path = "/dev/null", 8850 .access = FS_R | FS_W | FS_IOCTL, 8851 .quiet = false, 8852 }, 8853 { 8854 .path = "/dev/zero", 8855 .access = FS_R, 8856 .quiet = true, 8857 }, 8858 }, 8859 }, 8860 }, 8861 .targets = { 8862 { 8863 .target = file1_s1d1, 8864 .open_mode = O_RDONLY, 8865 }, 8866 { 8867 .target = file1_s1d1, 8868 .open_mode = O_WRONLY, 8869 .expect_open_success = true, 8870 }, 8871 { 8872 .target = file1_s2d1, 8873 .open_mode = O_RDONLY, 8874 }, 8875 { 8876 .target = file1_s2d1, 8877 .open_mode = O_WRONLY, 8878 .expect_open_success = true, 8879 }, 8880 { 8881 .target = "/dev/null", 8882 .open_mode = O_RDONLY, 8883 .expect_open_success = true, 8884 .expect_ioctl_denied = true, 8885 }, 8886 { 8887 .target = "/dev/zero", 8888 .open_mode = O_RDONLY, 8889 .expect_open_success = true, 8890 .expect_ioctl_denied = true, 8891 }, 8892 }, 8893 }; 8894 8895 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_2) { 8896 /* Here, rules that deny access are never quiet. */ 8897 .layers = { 8898 { 8899 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8900 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8901 .rules = { 8902 { 8903 .path = dir_s1d1, 8904 .access = FS_W, 8905 .quiet = false 8906 }, 8907 { 8908 .path = dir_s2d1, 8909 .access = FS_R | FS_W | FS_TRUNC, 8910 .quiet = true 8911 }, 8912 { 8913 .path = "/dev/null", 8914 .access = FS_R, 8915 .quiet = false 8916 }, 8917 { 8918 .path = "/dev/zero", 8919 .access = FS_R | FS_W | FS_IOCTL, 8920 .quiet = true 8921 }, 8922 }, 8923 }, 8924 { 8925 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8926 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8927 .rules = { 8928 { 8929 .path = dir_s1d1, 8930 .access = FS_R | FS_W | FS_TRUNC, 8931 .quiet = true 8932 }, 8933 { 8934 .path = dir_s2d1, 8935 .access = FS_W, 8936 .quiet = false 8937 }, 8938 { 8939 .path = "/dev/null", 8940 .access = FS_R | FS_W | FS_IOCTL, 8941 .quiet = true 8942 }, 8943 { 8944 .path = "/dev/zero", 8945 .access = FS_R, 8946 .quiet = false 8947 }, 8948 }, 8949 }, 8950 }, 8951 .targets = { 8952 { 8953 .target = file1_s1d1, 8954 .open_mode = O_RDONLY, 8955 .audit_read_blocked = true, 8956 }, 8957 { 8958 .target = file1_s1d1, 8959 .open_mode = O_WRONLY, 8960 .expect_open_success = true, 8961 .audit_truncate = true, 8962 }, 8963 { 8964 .target = file1_s2d1, 8965 .open_mode = O_RDONLY, 8966 .audit_read_blocked = true, 8967 }, 8968 { 8969 .target = file1_s2d1, 8970 .open_mode = O_WRONLY, 8971 .expect_open_success = true, 8972 .audit_truncate = true, 8973 }, 8974 { 8975 .target = "/dev/null", 8976 .open_mode = O_RDONLY, 8977 .expect_open_success = true, 8978 .expect_ioctl_denied = true, 8979 .audit_ioctl = true, 8980 }, 8981 { 8982 .target = "/dev/zero", 8983 .open_mode = O_RDONLY, 8984 .expect_open_success = true, 8985 .expect_ioctl_denied = true, 8986 .audit_ioctl = true, 8987 }, 8988 }, 8989 }; 8990 8991 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_3) { 8992 /* This time only the second layer quiets things. */ 8993 .layers = { 8994 { 8995 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8996 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 8997 .rules = { 8998 { 8999 .path = dir_s1d1, 9000 .access = FS_W, 9001 .quiet = false, 9002 }, 9003 { 9004 .path = dir_s2d1, 9005 .access = FS_R | FS_W | FS_TRUNC, 9006 .quiet = false, 9007 }, 9008 { 9009 .path = "/dev/null", 9010 .access = FS_R, 9011 .quiet = false, 9012 }, 9013 { 9014 .path = "/dev/zero", 9015 .access = FS_R | FS_W | FS_IOCTL, 9016 .quiet = false, 9017 }, 9018 }, 9019 }, 9020 { 9021 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9022 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9023 .rules = { 9024 { 9025 .path = dir_s1d1, 9026 .access = FS_R | FS_W | FS_TRUNC, 9027 .quiet = false, 9028 }, 9029 { 9030 .path = dir_s2d1, 9031 .access = FS_W, 9032 .quiet = true, 9033 }, 9034 { 9035 .path = "/dev/null", 9036 .access = FS_R | FS_W | FS_IOCTL, 9037 .quiet = false, 9038 }, 9039 { 9040 .path = "/dev/zero", 9041 .access = FS_R, 9042 .quiet = true, 9043 }, 9044 }, 9045 }, 9046 }, 9047 .targets = { 9048 { 9049 .target = file1_s1d1, 9050 .open_mode = O_RDONLY, 9051 .audit_read_blocked = true, 9052 }, 9053 { 9054 .target = file1_s1d1, 9055 .open_mode = O_WRONLY, 9056 .expect_open_success = true, 9057 .audit_truncate = true, 9058 }, 9059 { 9060 .target = file1_s2d1, 9061 .open_mode = O_RDONLY, 9062 }, 9063 { 9064 .target = file1_s2d1, 9065 .open_mode = O_WRONLY, 9066 .expect_open_success = true, 9067 }, 9068 { 9069 .target = "/dev/null", 9070 .open_mode = O_RDONLY, 9071 .expect_open_success = true, 9072 .expect_ioctl_denied = true, 9073 .audit_ioctl = true, 9074 }, 9075 { 9076 .target = "/dev/zero", 9077 .open_mode = O_RDONLY, 9078 .expect_open_success = true, 9079 .expect_ioctl_denied = true, 9080 }, 9081 }, 9082 }; 9083 9084 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_quiet_access) { 9085 /* Here, rules that deny access are always quiet. */ 9086 .layers = { 9087 { 9088 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9089 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9090 .rules = { 9091 { 9092 .path = dir_s1d1, 9093 .access = FS_W, 9094 .quiet = true, 9095 }, 9096 { 9097 .path = dir_s2d1, 9098 .access = FS_R | FS_W | FS_TRUNC, 9099 .quiet = false, 9100 }, 9101 { 9102 .path = "/dev/null", 9103 .access = FS_R, 9104 .quiet = true, 9105 }, 9106 { 9107 .path = "/dev/zero", 9108 .access = FS_R | FS_W | FS_IOCTL, 9109 .quiet = false, 9110 }, 9111 }, 9112 }, 9113 { 9114 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9115 .quiet_access_fs = FS_IOCTL, 9116 .rules = { 9117 { 9118 .path = dir_s1d1, 9119 .access = FS_R | FS_W | FS_TRUNC, 9120 .quiet = false, 9121 }, 9122 { 9123 .path = dir_s2d1, 9124 .access = FS_W, 9125 .quiet = true, 9126 }, 9127 { 9128 .path = "/dev/null", 9129 .access = FS_R | FS_W | FS_IOCTL, 9130 .quiet = false, 9131 }, 9132 { 9133 .path = "/dev/zero", 9134 .access = FS_R, 9135 .quiet = true, 9136 }, 9137 }, 9138 }, 9139 }, 9140 .targets = { 9141 { 9142 .target = file1_s1d1, 9143 .open_mode = O_RDONLY, 9144 }, 9145 { 9146 .target = file1_s1d1, 9147 .open_mode = O_WRONLY, 9148 .expect_open_success = true, 9149 }, 9150 { 9151 .target = file1_s2d1, 9152 .open_mode = O_RDONLY, 9153 .audit_read_blocked = true, 9154 }, 9155 { 9156 .target = file1_s2d1, 9157 .open_mode = O_WRONLY, 9158 .expect_open_success = true, 9159 .audit_truncate = true, 9160 }, 9161 { 9162 .target = "/dev/null", 9163 .open_mode = O_RDONLY, 9164 .expect_open_success = true, 9165 .expect_ioctl_denied = true, 9166 }, 9167 { 9168 .target = "/dev/zero", 9169 .open_mode = O_RDONLY, 9170 .expect_open_success = true, 9171 .expect_ioctl_denied = true, 9172 }, 9173 }, 9174 }; 9175 9176 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_1) { 9177 /* Quiet from layer 1 */ 9178 .layers = { 9179 { 9180 .handled_access_fs = FS_R, 9181 .quiet_access_fs = FS_R, 9182 .rules = { 9183 { 9184 .path = file1_s1d1, 9185 .access = FS_R, 9186 .quiet = true, 9187 }, 9188 { 9189 .path = file2_s1d1, 9190 .access = 0, 9191 .quiet = true, 9192 }, 9193 { 9194 .path = file1_s1d2, 9195 .access = 0, 9196 .quiet = true, 9197 }, 9198 { 9199 .path = file2_s1d2, 9200 .access = FS_R, 9201 .quiet = true, 9202 }, 9203 }, 9204 }, 9205 { 9206 .handled_access_fs = FS_W, 9207 .quiet_access_fs = FS_W, 9208 .rules = { 9209 { 9210 .path = file1_s1d1, 9211 .access = FS_W, 9212 .quiet = false, 9213 }, 9214 /* Nothing for file2_s1d1 */ 9215 { 9216 .path = file1_s1d2, 9217 .access = FS_W, 9218 .quiet = false, 9219 }, 9220 /* Nothing for file2_s1d2 */ 9221 }, 9222 }, 9223 }, 9224 .targets = { 9225 { 9226 .target = file1_s1d1, 9227 .open_mode = O_RDWR, 9228 .expect_open_success = true, 9229 .expect_truncate_success = true, 9230 }, 9231 /* Missing both, youngest layer denies write, not quiet */ 9232 { 9233 .target = file2_s1d1, 9234 .open_mode = O_RDWR, 9235 .audit_write_blocked = true, 9236 }, 9237 /* Missing read, denied and quieted by layer 1 */ 9238 { 9239 .target = file1_s1d2, 9240 .open_mode = O_RDWR, 9241 }, 9242 /* Missing write, denied and not quieted by layer 2 */ 9243 { 9244 .target = file2_s1d2, 9245 .open_mode = O_RDWR, 9246 .audit_write_blocked = true, 9247 }, 9248 }, 9249 }; 9250 9251 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_2) { 9252 /* Quiet from layer 2 */ 9253 .layers = { 9254 { 9255 .handled_access_fs = FS_R, 9256 .quiet_access_fs = FS_R, 9257 .rules = { 9258 { 9259 .path = file1_s1d1, 9260 .access = FS_R, 9261 .quiet = false, 9262 }, 9263 /* Nothing for file2_s1d1 and file1_s1d2 */ 9264 { 9265 .path = file2_s1d2, 9266 .access = FS_R, 9267 .quiet = false, 9268 }, 9269 }, 9270 }, 9271 { 9272 .handled_access_fs = FS_W, 9273 .quiet_access_fs = FS_W, 9274 .rules = { 9275 { 9276 .path = file1_s1d1, 9277 .access = FS_W, 9278 .quiet = true, 9279 }, 9280 { 9281 .path = file2_s1d1, 9282 .access = 0, 9283 .quiet = true, 9284 }, 9285 { 9286 .path = file1_s1d2, 9287 .access = FS_W, 9288 .quiet = true, 9289 }, 9290 { 9291 .path = file2_s1d2, 9292 .access = 0, 9293 .quiet = true, 9294 }, 9295 }, 9296 }, 9297 }, 9298 .targets = { 9299 { 9300 .target = file1_s1d1, 9301 .open_mode = O_RDWR, 9302 .expect_open_success = true, 9303 .expect_truncate_success = true, 9304 }, 9305 /* Missing both, youngest layer denies write, quiet */ 9306 { 9307 .target = file2_s1d1, 9308 .open_mode = O_RDWR, 9309 }, 9310 /* Missing read, denied and not quieted by layer 1 */ 9311 { 9312 .target = file1_s1d2, 9313 .open_mode = O_RDWR, 9314 .audit_read_blocked = true, 9315 }, 9316 /* Missing write, denied and quieted by layer 2 */ 9317 { 9318 .target = file2_s1d2, 9319 .open_mode = O_RDWR, 9320 }, 9321 }, 9322 }; 9323 9324 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_3) { 9325 /* Quiet from both layers */ 9326 .layers = { 9327 { 9328 .handled_access_fs = FS_R, 9329 .quiet_access_fs = FS_R, 9330 .rules = { 9331 { 9332 .path = file1_s1d1, 9333 .access = FS_R, 9334 .quiet = true, 9335 }, 9336 { 9337 .path = file2_s1d1, 9338 .access = 0, 9339 .quiet = true, 9340 }, 9341 { 9342 .path = file1_s1d2, 9343 .access = 0, 9344 .quiet = true, 9345 }, 9346 { 9347 .path = file2_s1d2, 9348 .access = FS_R, 9349 .quiet = true, 9350 }, 9351 }, 9352 }, 9353 { 9354 .handled_access_fs = FS_W, 9355 .quiet_access_fs = FS_W, 9356 .rules = { 9357 { 9358 .path = file1_s1d1, 9359 .access = FS_W, 9360 .quiet = true, 9361 }, 9362 { 9363 .path = file2_s1d1, 9364 .access = 0, 9365 .quiet = true, 9366 }, 9367 { 9368 .path = file1_s1d2, 9369 .access = FS_W, 9370 .quiet = true, 9371 }, 9372 { 9373 .path = file2_s1d2, 9374 .access = 0, 9375 .quiet = true, 9376 }, 9377 }, 9378 }, 9379 }, 9380 .targets = { 9381 { 9382 .target = file1_s1d1, 9383 .open_mode = O_RDWR, 9384 .expect_open_success = true, 9385 .expect_truncate_success = true, 9386 }, 9387 { 9388 .target = file2_s1d1, 9389 .open_mode = O_RDWR, 9390 }, 9391 { 9392 .target = file1_s1d2, 9393 .open_mode = O_RDWR, 9394 }, 9395 { 9396 .target = file2_s1d2, 9397 .open_mode = O_RDWR, 9398 }, 9399 }, 9400 }; 9401 9402 FIXTURE_VARIANT_ADD(audit_quiet_layout1, without_quiet_then_with_quiet) { 9403 .layers = { 9404 { 9405 .handled_access_fs = FS_R | FS_W, 9406 .quiet_access_fs = FS_R, 9407 .rules = { 9408 { .path = dir_s1d1, .access = FS_W, .quiet = false }, 9409 { .path = dir_s1d1, .access = 0, .quiet = true }, 9410 }, 9411 }, 9412 }, 9413 .targets = { 9414 /* Read denied and quieted */ 9415 { 9416 .target = file1_s1d1, 9417 .open_mode = O_RDONLY, 9418 }, 9419 /* Write ok */ 9420 { 9421 .target = file1_s1d1, 9422 .open_mode = O_WRONLY, 9423 .expect_open_success = true, 9424 .expect_truncate_success = true, 9425 }, 9426 /* Write ok, read denied and quieted */ 9427 { 9428 .target = file1_s1d1, 9429 .open_mode = O_RDWR, 9430 }, 9431 /* Not covered by quiet */ 9432 { 9433 .target = file1_s2d1, 9434 .open_mode = O_RDONLY, 9435 .audit_read_blocked = true, 9436 }, 9437 }, 9438 }; 9439 9440 /* 9441 * The following TEST_F extend the above test cases to test more layers, with 9442 * the inserted layers having varying configurations. 9443 */ 9444 9445 /* Extra allow all layers, quiet or not, does not change any behaviour. */ 9446 TEST_F(audit_quiet_layout1, allow_all_layer) 9447 { 9448 struct a_layer allow_all_layer = { 9449 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9450 .quiet_access_fs = 0, 9451 .rules = { 9452 { 9453 .path = "/", 9454 .access = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9455 .quiet = false, 9456 }, 9457 }, 9458 }; 9459 int i; 9460 9461 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); 9462 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9463 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9464 9465 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9466 9467 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); 9468 9469 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9470 9471 /* 9472 * SELF_LOG flags or quiet bits from inner allowing layers should not 9473 * affect behaviour. 9474 */ 9475 allow_all_layer.quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL; 9476 allow_all_layer.rules[0].quiet = true; 9477 /* 9478 * Note: this only works because we're not checking counts of domain 9479 * alloc/dealloc logs 9480 */ 9481 allow_all_layer.restrict_flags = 9482 LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF | 9483 LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF; 9484 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer)); 9485 9486 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9487 } 9488 9489 /* 9490 * Add useless outer layers until we reach the layer limit. Should not change 9491 * anything. 9492 */ 9493 TEST_F(audit_quiet_layout1, many_outer_layers) 9494 { 9495 struct a_layer useless_layer = { 9496 .handled_access_fs = FS_R | FS_W | FS_TRUNC, 9497 .quiet_access_fs = FS_R | FS_W | FS_TRUNC, 9498 .rules = { 9499 { .path = "/", .access = FS_R | FS_W | FS_TRUNC, .quiet = true }, 9500 }, 9501 }; 9502 int i; 9503 9504 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) { 9505 if (variant->layers[i].handled_access_fs == 0) 9506 break; 9507 } 9508 9509 for (; i < LANDLOCK_MAX_NUM_LAYERS; i++) 9510 ASSERT_EQ(0, apply_a_layer(_metadata, &useless_layer)); 9511 9512 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9513 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9514 9515 audit_quiet_layout1_test_body(_metadata, self, variant->targets); 9516 } 9517 9518 /* An inner layer that denies and quiets everything should result in no logs. */ 9519 TEST_F(audit_quiet_layout1, deny_all_quiet_layer) 9520 { 9521 struct a_layer deny_all_layer = { 9522 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9523 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL, 9524 .rules = { 9525 { .path = "/", .access = 0, .quiet = true }, 9526 }, 9527 }; 9528 int i; 9529 FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {}; 9530 9531 /* Any open should fail with no logs. */ 9532 for (i = 0; i < ARRAY_SIZE(variant->targets); i++) { 9533 const struct a_target *target = &variant->targets[i]; 9534 9535 variant_2.targets[i] = (struct a_target){ 9536 .target = target->target, 9537 .open_mode = target->open_mode, 9538 /* We denied everything, open should always fail. */ 9539 .expect_open_success = false, 9540 }; 9541 } 9542 9543 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9544 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9545 ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer)); 9546 9547 audit_quiet_layout1_test_body(_metadata, self, variant_2.targets); 9548 } 9549 9550 /* 9551 * An inner layer that denies everything without quiet should produce logs for 9552 * all access. 9553 */ 9554 TEST_F(audit_quiet_layout1, deny_all_layer) 9555 { 9556 struct a_layer deny_all_layer = { 9557 .handled_access_fs = FS_R | FS_W, 9558 .quiet_access_fs = FS_R | FS_W, 9559 }; 9560 int i; 9561 FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {}; 9562 bool test_has_subdomains_off = false; 9563 9564 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) { 9565 if (variant->layers[i].restrict_flags & 9566 LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF) { 9567 test_has_subdomains_off = true; 9568 break; 9569 } 9570 } 9571 9572 for (i = 0; i < ARRAY_SIZE(variant->targets); i++) { 9573 const struct a_target *target = &variant->targets[i]; 9574 9575 variant_2.targets[i] = (struct a_target){ 9576 .target = target->target, 9577 .open_mode = target->open_mode, 9578 9579 /* We denied everything, open should always fail. */ 9580 .expect_open_success = false, 9581 /* Audit should always happen as long as open request contains read. */ 9582 .audit_read_blocked = !test_has_subdomains_off && 9583 target->open_mode != O_WRONLY, 9584 /* Audit should always happen as long as open request contains write. */ 9585 .audit_write_blocked = !test_has_subdomains_off && 9586 target->open_mode != O_RDONLY, 9587 }; 9588 } 9589 9590 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) 9591 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i])); 9592 ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer)); 9593 9594 audit_quiet_layout1_test_body(_metadata, self, variant_2.targets); 9595 } 9596 9597 /* Uses layout1_bind hierarchy */ 9598 FIXTURE(audit_quiet_rename) 9599 { 9600 struct audit_filter audit_filter; 9601 int audit_fd; 9602 }; 9603 9604 FIXTURE_SETUP(audit_quiet_rename) 9605 { 9606 prepare_layout(_metadata); 9607 create_layout1(_metadata); 9608 9609 set_cap(_metadata, CAP_SYS_ADMIN); 9610 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 9611 clear_cap(_metadata, CAP_SYS_ADMIN); 9612 9613 set_cap(_metadata, CAP_AUDIT_CONTROL); 9614 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 9615 EXPECT_LE(0, self->audit_fd); 9616 clear_cap(_metadata, CAP_AUDIT_CONTROL); 9617 9618 if (getenv("DEBUG_QUIET_TESTS")) 9619 debug_quiet_tests = true; 9620 } 9621 9622 FIXTURE_TEARDOWN_PARENT(audit_quiet_rename) 9623 { 9624 remove_layout1(_metadata); 9625 cleanup_layout(_metadata); 9626 9627 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 9628 9629 remove_path(file1_s4d1); 9630 remove_path(file2_s4d1); 9631 9632 set_cap(_metadata, CAP_AUDIT_CONTROL); 9633 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 9634 clear_cap(_metadata, CAP_AUDIT_CONTROL); 9635 } 9636 9637 static void simple_quiet_rename(struct __test_metadata *const _metadata, 9638 FIXTURE_DATA(audit_quiet_rename) *const self, 9639 __u64 handled_access, __u64 quiet_access, 9640 bool source_allow, bool dest_allow, 9641 bool source_quiet, bool dest_quiet, 9642 const char *source_blockers, 9643 const char *dest_blockers) 9644 { 9645 /* We will move file1_s1d1 to file1_s2d1 */ 9646 struct a_layer layer = { 9647 .handled_access_fs = handled_access, 9648 .quiet_access_fs = quiet_access, 9649 .rules = { 9650 { 9651 .path = dir_s1d1, 9652 .access = source_allow ? handled_access : 0, 9653 .quiet = source_quiet, 9654 }, 9655 { 9656 .path = dir_s2d1, 9657 .access = dest_allow ? handled_access : 0, 9658 .quiet = dest_quiet, 9659 }, 9660 }, 9661 }; 9662 struct audit_records records = {}; 9663 int ret, err; 9664 9665 /* Skip landlock_add_rule for useless rules. */ 9666 if (!source_allow && !source_quiet) 9667 layer.rules[0].path = NULL; 9668 if (!dest_allow && !dest_quiet) 9669 layer.rules[1].path = NULL; 9670 9671 EXPECT_EQ(0, unlink(file1_s2d1)); 9672 EXPECT_EQ(0, apply_a_layer(_metadata, &layer)); 9673 9674 if (debug_quiet_tests) 9675 TH_LOG("Try renameat \"%s\" to \"%s\"", file1_s1d1, file1_s2d1); 9676 ret = renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1); 9677 err = errno; 9678 if (ret != 0 && debug_quiet_tests) { 9679 TH_LOG("renameat error: %s", err == EXDEV ? "EXDEV" : 9680 err == EACCES ? "EACCES" : 9681 strerror(err)); 9682 } 9683 if (source_allow && dest_allow) { 9684 ASSERT_EQ(0, ret); 9685 } else { 9686 ASSERT_EQ(-1, ret); 9687 if (handled_access & (LANDLOCK_ACCESS_FS_MAKE_REG | 9688 LANDLOCK_ACCESS_FS_REMOVE_FILE)) { 9689 ASSERT_EQ(EACCES, err); 9690 } else { 9691 ASSERT_EQ(EXDEV, err); 9692 } 9693 9694 if (source_blockers) 9695 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9696 source_blockers, dir_s1d1)); 9697 if (dest_blockers) 9698 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9699 dest_blockers, dir_s2d1)); 9700 } 9701 /* 9702 * No other logs. records.domain not checked per reasoning in 9703 * audit_quiet_layout1_test_body. 9704 */ 9705 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9706 ASSERT_EQ(0, records.access); 9707 } 9708 9709 TEST_F(audit_quiet_rename, rename_ok) 9710 { 9711 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9712 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9713 LANDLOCK_ACCESS_FS_REFER; 9714 9715 simple_quiet_rename(_metadata, self, access, access, true, true, false, 9716 false, NULL, NULL); 9717 } 9718 9719 TEST_F(audit_quiet_rename, no_quiet) 9720 { 9721 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9722 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9723 LANDLOCK_ACCESS_FS_REFER; 9724 9725 simple_quiet_rename(_metadata, self, access, access, false, false, 9726 false, false, "fs\\.remove_file,fs\\.refer", 9727 "fs\\.make_reg,fs\\.refer"); 9728 } 9729 9730 TEST_F(audit_quiet_rename, quiet) 9731 { 9732 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9733 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9734 LANDLOCK_ACCESS_FS_REFER; 9735 9736 simple_quiet_rename(_metadata, self, access, access, false, false, true, 9737 true, NULL, NULL); 9738 } 9739 9740 TEST_F(audit_quiet_rename, source_no_quiet_dest_quiet) 9741 { 9742 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9743 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9744 LANDLOCK_ACCESS_FS_REFER; 9745 9746 simple_quiet_rename(_metadata, self, access, access, false, false, 9747 false, true, "fs\\.remove_file,fs\\.refer", NULL); 9748 } 9749 9750 TEST_F(audit_quiet_rename, source_quiet_dest_no_quiet) 9751 { 9752 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9753 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9754 LANDLOCK_ACCESS_FS_REFER; 9755 9756 simple_quiet_rename(_metadata, self, access, access, false, false, true, 9757 false, NULL, "fs\\.make_reg,fs\\.refer"); 9758 } 9759 9760 TEST_F(audit_quiet_rename, only_quiet_refer) 9761 { 9762 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9763 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9764 LANDLOCK_ACCESS_FS_REFER; 9765 9766 simple_quiet_rename(_metadata, self, access, LANDLOCK_ACCESS_FS_REFER, 9767 false, false, true, true, 9768 "fs\\.remove_file,fs\\.refer", 9769 "fs\\.make_reg,fs\\.refer"); 9770 } 9771 9772 TEST_F(audit_quiet_rename, source_allow_dest_quiet) 9773 { 9774 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9775 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9776 LANDLOCK_ACCESS_FS_REFER; 9777 9778 simple_quiet_rename(_metadata, self, access, access, true, false, false, 9779 true, NULL, NULL); 9780 } 9781 9782 TEST_F(audit_quiet_rename, source_quiet_dest_allow) 9783 { 9784 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9785 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9786 LANDLOCK_ACCESS_FS_REFER; 9787 9788 simple_quiet_rename(_metadata, self, access, access, false, true, true, 9789 false, NULL, NULL); 9790 } 9791 9792 TEST_F(audit_quiet_rename, handle_all_deny_quiet_refer) 9793 { 9794 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9795 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9796 LANDLOCK_ACCESS_FS_REFER; 9797 struct a_layer layer = { 9798 .handled_access_fs = access, 9799 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 9800 .rules = { 9801 { 9802 .path = dir_s1d1, 9803 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9804 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9805 .quiet = true, 9806 }, 9807 { 9808 .path = dir_s2d1, 9809 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9810 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9811 .quiet = true, 9812 }, 9813 }, 9814 }; 9815 struct audit_records records = {}; 9816 9817 EXPECT_EQ(0, unlink(file1_s2d1)); 9818 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9819 9820 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9821 ASSERT_EQ(EXDEV, errno); 9822 9823 /* No logs */ 9824 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9825 ASSERT_EQ(0, records.access); 9826 } 9827 9828 TEST_F(audit_quiet_rename, handle_all_deny_not_quiet_refer) 9829 { 9830 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9831 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9832 LANDLOCK_ACCESS_FS_REFER; 9833 struct a_layer layer = { 9834 .handled_access_fs = access, 9835 .quiet_access_fs = 0, 9836 .rules = { 9837 { 9838 .path = dir_s1d1, 9839 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9840 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9841 .quiet = false, 9842 }, 9843 { 9844 .path = dir_s2d1, 9845 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9846 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9847 .quiet = false, 9848 }, 9849 }, 9850 }; 9851 struct audit_records records = {}; 9852 9853 EXPECT_EQ(0, unlink(file1_s2d1)); 9854 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9855 9856 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9857 ASSERT_EQ(EXDEV, errno); 9858 9859 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 9860 dir_s1d1)); 9861 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 9862 dir_s2d1)); 9863 9864 /* No other logs */ 9865 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9866 ASSERT_EQ(0, records.access); 9867 } 9868 9869 TEST_F(audit_quiet_rename, handle_all_deny_refer_quiet_source_not_quiet_dest) 9870 { 9871 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9872 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9873 LANDLOCK_ACCESS_FS_REFER; 9874 struct a_layer layer = { 9875 .handled_access_fs = access, 9876 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 9877 .rules = { 9878 { 9879 .path = dir_s1d1, 9880 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9881 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9882 .quiet = true, 9883 }, 9884 { 9885 .path = dir_s2d1, 9886 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 9887 LANDLOCK_ACCESS_FS_REMOVE_FILE, 9888 .quiet = false, 9889 }, 9890 }, 9891 }; 9892 struct audit_records records = {}; 9893 9894 EXPECT_EQ(0, unlink(file1_s2d1)); 9895 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9896 9897 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9898 ASSERT_EQ(EXDEV, errno); 9899 9900 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 9901 dir_s2d1)); 9902 9903 /* No other logs */ 9904 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9905 ASSERT_EQ(0, records.access); 9906 } 9907 9908 TEST_F(audit_quiet_rename, quiet_same_dir) 9909 { 9910 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9911 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9912 LANDLOCK_ACCESS_FS_REFER; 9913 struct a_layer layer = { 9914 .handled_access_fs = access, 9915 .quiet_access_fs = access, 9916 .rules = { 9917 { 9918 .path = dir_s1d1, 9919 .access = 0, 9920 .quiet = true, 9921 }, 9922 }, 9923 }; 9924 struct audit_records records = {}; 9925 9926 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9927 9928 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1)); 9929 ASSERT_EQ(EACCES, errno); 9930 9931 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9932 ASSERT_EQ(0, records.access); 9933 } 9934 9935 TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored) 9936 { 9937 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9938 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9939 LANDLOCK_ACCESS_FS_REFER; 9940 struct a_layer layer = { 9941 .handled_access_fs = access, 9942 .quiet_access_fs = access, 9943 .rules = { 9944 { 9945 .path = file1_s1d1, 9946 .access = 0, 9947 .quiet = true, 9948 }, 9949 { 9950 .path = file1_s2d1, 9951 .access = 0, 9952 .quiet = true, 9953 }, 9954 }, 9955 }; 9956 struct audit_records records = {}; 9957 9958 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9959 9960 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 9961 ASSERT_EQ(EACCES, errno); 9962 9963 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9964 "fs\\.remove_file,fs\\.refer", dir_s1d1)); 9965 /* We didn't unlink destination file */ 9966 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 9967 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 9968 dir_s2d1)); 9969 9970 /* No other logs */ 9971 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 9972 ASSERT_EQ(0, records.access); 9973 } 9974 9975 TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored_same_dir) 9976 { 9977 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 9978 LANDLOCK_ACCESS_FS_REMOVE_FILE | 9979 LANDLOCK_ACCESS_FS_REFER; 9980 struct a_layer layer = { 9981 .handled_access_fs = access, 9982 .quiet_access_fs = access, 9983 .rules = { 9984 { 9985 .path = file1_s1d1, 9986 .access = 0, 9987 .quiet = true, 9988 }, 9989 { 9990 .path = file2_s1d1, 9991 .access = 0, 9992 .quiet = true, 9993 }, 9994 }, 9995 }; 9996 struct audit_records records = {}; 9997 9998 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 9999 10000 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1)); 10001 ASSERT_EQ(EACCES, errno); 10002 10003 ASSERT_EQ(0, 10004 matches_log_fs(_metadata, self->audit_fd, 10005 "fs\\.remove_file,fs\\.make_reg", dir_s1d1)); 10006 10007 /* No other logs */ 10008 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10009 ASSERT_EQ(0, records.access); 10010 } 10011 10012 TEST_F(audit_quiet_rename, two_layers_different_quiet1) 10013 { 10014 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10015 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10016 LANDLOCK_ACCESS_FS_REFER; 10017 struct a_layer layer1 = { 10018 .handled_access_fs = access, 10019 .quiet_access_fs = access, 10020 .rules = { 10021 { 10022 .path = dir_s1d1, 10023 .access = access, 10024 .quiet = false, 10025 }, 10026 { 10027 .path = dir_s2d1, 10028 .access = 0, 10029 .quiet = true, 10030 }, 10031 }, 10032 }; 10033 struct a_layer layer2 = { 10034 .handled_access_fs = access, 10035 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 10036 .rules = { 10037 { 10038 .path = dir_s1d1, 10039 .access = 0, 10040 .quiet = true, 10041 }, 10042 { 10043 .path = dir_s2d1, 10044 .access = access, 10045 .quiet = false, 10046 }, 10047 }, 10048 }; 10049 struct audit_records records = {}; 10050 10051 EXPECT_EQ(0, unlink(file1_s2d1)); 10052 10053 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 10054 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 10055 10056 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 10057 ASSERT_EQ(EACCES, errno); 10058 10059 /* 10060 * The youngest denial will be layer 2. Refer is quieted but we are 10061 * also missing remove_file on source. 10062 */ 10063 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 10064 "fs\\.remove_file,fs\\.refer", dir_s1d1)); 10065 /* No other logs */ 10066 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10067 ASSERT_EQ(0, records.access); 10068 } 10069 10070 TEST_F(audit_quiet_rename, two_layers_different_quiet2) 10071 { 10072 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10073 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10074 LANDLOCK_ACCESS_FS_REFER; 10075 struct a_layer layer1 = { 10076 .handled_access_fs = access, 10077 .quiet_access_fs = access, 10078 .rules = { 10079 { 10080 .path = dir_s1d1, 10081 .access = access, 10082 .quiet = false, 10083 }, 10084 { 10085 .path = dir_s2d1, 10086 .access = 0, 10087 .quiet = true, 10088 }, 10089 }, 10090 }; 10091 struct a_layer layer2 = { 10092 .handled_access_fs = LANDLOCK_ACCESS_FS_REFER, 10093 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER, 10094 .rules = { 10095 { 10096 .path = dir_s1d1, 10097 .access = 0, 10098 .quiet = true, 10099 }, 10100 { 10101 .path = dir_s2d1, 10102 .access = LANDLOCK_ACCESS_FS_REFER, 10103 .quiet = false, 10104 }, 10105 }, 10106 }; 10107 struct audit_records records = {}; 10108 10109 EXPECT_EQ(0, unlink(file1_s2d1)); 10110 10111 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 10112 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 10113 10114 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 10115 ASSERT_EQ(EACCES, errno); 10116 10117 /* 10118 * The youngest denial will be layer 2, but refer is quieted (and that 10119 * layer does not handle any other accesses). 10120 */ 10121 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10122 ASSERT_EQ(0, records.access); 10123 } 10124 10125 TEST_F(audit_quiet_rename, two_layers_different_quiet3) 10126 { 10127 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10128 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10129 LANDLOCK_ACCESS_FS_REFER; 10130 struct a_layer layer1 = { 10131 .handled_access_fs = access, 10132 .quiet_access_fs = access, 10133 .rules = { 10134 { 10135 .path = dir_s1d1, 10136 .access = access, 10137 .quiet = false, 10138 }, 10139 { 10140 .path = dir_s2d1, 10141 .access = 0, 10142 .quiet = true, 10143 }, 10144 }, 10145 }; 10146 struct a_layer layer2 = { 10147 .handled_access_fs = access, 10148 .quiet_access_fs = access, 10149 .rules = { 10150 { 10151 .path = dir_s1d1, 10152 .access = 0, 10153 .quiet = true, 10154 }, 10155 { 10156 .path = dir_s2d1, 10157 .access = access, 10158 .quiet = false, 10159 }, 10160 }, 10161 }; 10162 struct audit_records records = {}; 10163 10164 EXPECT_EQ(0, unlink(file1_s2d1)); 10165 10166 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 10167 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 10168 10169 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 10170 ASSERT_EQ(EACCES, errno); 10171 10172 /* 10173 * The youngest denial will be layer 2, in which everything is quieted. 10174 */ 10175 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10176 ASSERT_EQ(0, records.access); 10177 } 10178 10179 TEST_F(audit_quiet_rename, 10180 first_layer_quiet_deny_all_second_layer_not_quiet_deny_all) 10181 { 10182 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10183 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10184 LANDLOCK_ACCESS_FS_REFER; 10185 struct a_layer layer1 = { 10186 .handled_access_fs = access, 10187 .quiet_access_fs = access, 10188 .rules = { 10189 { 10190 .path = dir_s1d1, 10191 .access = 0, 10192 .quiet = true, 10193 }, 10194 { 10195 .path = dir_s2d1, 10196 .access = 0, 10197 .quiet = true, 10198 }, 10199 }, 10200 }; 10201 struct a_layer layer2 = { 10202 .handled_access_fs = access, 10203 .quiet_access_fs = access, 10204 .rules = {}, 10205 }; 10206 struct audit_records records = {}; 10207 10208 EXPECT_EQ(0, unlink(file1_s2d1)); 10209 10210 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 10211 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 10212 10213 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 10214 ASSERT_EQ(EACCES, errno); 10215 10216 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 10217 "fs\\.remove_file,fs\\.refer", dir_s1d1)); 10218 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 10219 "fs\\.make_reg,fs\\.refer", dir_s2d1)); 10220 /* No other logs. */ 10221 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10222 ASSERT_EQ(0, records.access); 10223 } 10224 10225 TEST_F(audit_quiet_rename, 10226 first_layer_quiet_deny_all_second_layer_dest_not_quiet) 10227 { 10228 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10229 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10230 LANDLOCK_ACCESS_FS_REFER; 10231 struct a_layer layer1 = { 10232 .handled_access_fs = access, 10233 .quiet_access_fs = access, 10234 .rules = { 10235 { 10236 .path = dir_s1d1, 10237 .access = 0, 10238 .quiet = true, 10239 }, 10240 { 10241 .path = dir_s2d1, 10242 .access = 0, 10243 .quiet = true, 10244 }, 10245 }, 10246 }; 10247 struct a_layer layer2 = { 10248 .handled_access_fs = access, 10249 .quiet_access_fs = access, 10250 .rules = { 10251 { 10252 .path = dir_s1d1, 10253 .access = 0, 10254 .quiet = true, 10255 }, 10256 }, 10257 }; 10258 struct audit_records records = {}; 10259 10260 EXPECT_EQ(0, unlink(file1_s2d1)); 10261 10262 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1)); 10263 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2)); 10264 10265 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1)); 10266 ASSERT_EQ(EACCES, errno); 10267 10268 /* Source is quieted but destination is not. */ 10269 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 10270 "fs\\.make_reg,fs\\.refer", dir_s2d1)); 10271 /* No other logs. */ 10272 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10273 ASSERT_EQ(0, records.access); 10274 } 10275 10276 TEST_F(audit_quiet_rename, rename_xchg) 10277 { 10278 struct a_layer layer = { 10279 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG | 10280 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10281 LANDLOCK_ACCESS_FS_REFER, 10282 .quiet_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG, 10283 .rules = { { 10284 .path = dir_s1d1, 10285 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE | 10286 LANDLOCK_ACCESS_FS_REFER, 10287 .quiet = true, 10288 }, 10289 { 10290 .path = dir_s2d1, 10291 .access = LANDLOCK_ACCESS_FS_MAKE_REG | 10292 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10293 LANDLOCK_ACCESS_FS_REFER, 10294 .quiet = false, 10295 } }, 10296 }; 10297 struct audit_records records = {}; 10298 10299 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10300 10301 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1, 10302 RENAME_EXCHANGE)); 10303 ASSERT_EQ(EACCES, errno); 10304 10305 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10306 ASSERT_EQ(0, records.access); 10307 } 10308 10309 TEST_F(audit_quiet_rename, quiet_on_parent_mount) 10310 { 10311 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10312 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10313 LANDLOCK_ACCESS_FS_REFER; 10314 struct a_layer layer = { 10315 .handled_access_fs = access, 10316 .quiet_access_fs = access, 10317 .rules = { 10318 { 10319 .path = dir_s2d1, 10320 .access = 0, 10321 .quiet = true, 10322 }, 10323 }, 10324 }; 10325 struct audit_records records = {}; 10326 10327 EXPECT_EQ(0, unlink(file2_s1d3)); 10328 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10329 10330 ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD, 10331 bind_file2_s1d3)); 10332 ASSERT_EQ(EACCES, errno); 10333 10334 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10335 ASSERT_EQ(0, records.access); 10336 } 10337 10338 TEST_F(audit_quiet_rename, quiet_behind_mountpoint_ignored) 10339 { 10340 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10341 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10342 LANDLOCK_ACCESS_FS_REFER; 10343 struct a_layer layer = { 10344 .handled_access_fs = access, 10345 .quiet_access_fs = access, 10346 .rules = { 10347 { 10348 .path = dir_s1d1, 10349 .access = 0, 10350 .quiet = true, 10351 }, 10352 }, 10353 }; 10354 struct audit_records records = {}; 10355 10356 EXPECT_EQ(0, unlink(file2_s1d3)); 10357 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10358 10359 ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD, 10360 bind_file2_s1d3)); 10361 ASSERT_EQ(EACCES, errno); 10362 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 10363 "fs\\.remove_file,fs\\.make_reg", 10364 bind_dir_s1d3)); 10365 10366 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10367 ASSERT_EQ(0, records.access); 10368 } 10369 10370 TEST_F(audit_quiet_rename, quiet_on_parent_mount_disconnected) 10371 { 10372 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10373 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10374 LANDLOCK_ACCESS_FS_REFER; 10375 struct a_layer layer = { 10376 .handled_access_fs = access, 10377 .quiet_access_fs = access, 10378 .rules = { 10379 { 10380 .path = dir_s2d1, 10381 .access = 0, 10382 .quiet = true, 10383 }, 10384 }, 10385 }; 10386 struct audit_records records = {}; 10387 int bind_s1d3_fd; 10388 10389 EXPECT_EQ(0, unlink(file2_s1d3)); 10390 10391 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY); 10392 ASSERT_GE(bind_s1d3_fd, 0); 10393 10394 /* Make s1d3 disconnected. */ 10395 create_directory(_metadata, dir_s4d1); 10396 ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2)); 10397 10398 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10399 10400 ASSERT_EQ(-1, 10401 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)); 10402 ASSERT_EQ(EACCES, errno); 10403 10404 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10405 ASSERT_EQ(0, records.access); 10406 } 10407 10408 TEST_F(audit_quiet_rename, quiet_behind_mountpoint_disconnected) 10409 { 10410 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG | 10411 LANDLOCK_ACCESS_FS_REMOVE_FILE | 10412 LANDLOCK_ACCESS_FS_REFER; 10413 struct a_layer layer = { 10414 .handled_access_fs = access, 10415 .quiet_access_fs = access, 10416 .rules = { 10417 { 10418 .path = dir_s4d1, 10419 .access = 0, 10420 .quiet = true, 10421 }, 10422 }, 10423 }; 10424 struct audit_records records = {}; 10425 int bind_s1d3_fd; 10426 10427 EXPECT_EQ(0, unlink(file2_s1d3)); 10428 10429 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY); 10430 ASSERT_GE(bind_s1d3_fd, 0); 10431 10432 /* Make s1d3 disconnected. */ 10433 create_directory(_metadata, dir_s4d1); 10434 ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2)); 10435 10436 ASSERT_EQ(0, apply_a_layer(_metadata, &layer)); 10437 10438 ASSERT_EQ(-1, 10439 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)); 10440 ASSERT_EQ(EACCES, errno); 10441 10442 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 10443 ASSERT_EQ(0, records.access); 10444 } 10445 10446 /* clang-format off */ 10447 FIXTURE(trace_layout1) { 10448 /* clang-format on */ 10449 int tracefs_ok; 10450 }; 10451 10452 FIXTURE_SETUP(trace_layout1) 10453 { 10454 struct stat st; 10455 10456 /* 10457 * Check tracefs availability before creating the layout, following the 10458 * layout3_fs pattern: skip before any layout creation to avoid leaving 10459 * stale TMP_DIR on skip. 10460 */ 10461 if (stat(TRACEFS_LANDLOCK_DIR, &st)) { 10462 self->tracefs_ok = 0; 10463 SKIP(return, "tracefs not available"); 10464 } 10465 self->tracefs_ok = 1; 10466 10467 /* Isolate tracefs state (PID filter, event enables). */ 10468 set_cap(_metadata, CAP_SYS_ADMIN); 10469 ASSERT_EQ(0, unshare(CLONE_NEWNS)); 10470 ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)); 10471 clear_cap(_metadata, CAP_SYS_ADMIN); 10472 10473 prepare_layout(_metadata); 10474 create_layout1(_metadata); 10475 10476 set_cap(_metadata, CAP_DAC_OVERRIDE); 10477 ASSERT_EQ(0, tracefs_fixture_setup()); 10478 ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, true)); 10479 ASSERT_EQ(0, tracefs_clear()); 10480 ASSERT_EQ(0, tracefs_set_pid_filter(getpid())); 10481 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10482 } 10483 10484 FIXTURE_TEARDOWN_PARENT(trace_layout1) 10485 { 10486 if (!self->tracefs_ok) 10487 return; 10488 10489 set_cap(_metadata, CAP_DAC_OVERRIDE); 10490 tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, false); 10491 tracefs_clear_pid_filter(); 10492 tracefs_fixture_teardown(); 10493 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10494 10495 remove_layout1(_metadata); 10496 cleanup_layout(_metadata); 10497 } 10498 10499 /* 10500 * Verifies that check_rule_inode events include correct field values: domain, 10501 * dev, ino, access_request, and grants. All values are verified against stat() 10502 * of the rule path on a deterministic tmpfs layout. 10503 */ 10504 TEST_F(trace_layout1, check_rule_fs_fields) 10505 { 10506 struct stat dir_stat; 10507 char expected_dev[32]; 10508 char expected_ino[32]; 10509 char *buf; 10510 char field[64]; 10511 10512 if (!self->tracefs_ok) 10513 SKIP(return, "tracefs not available"); 10514 10515 ASSERT_EQ(0, stat(dir_s1d1, &dir_stat)); 10516 snprintf(expected_dev, sizeof(expected_dev), "%u:%u", 10517 major(dir_stat.st_dev), minor(dir_stat.st_dev)); 10518 snprintf(expected_ino, sizeof(expected_ino), "%lu", dir_stat.st_ino); 10519 10520 set_cap(_metadata, CAP_DAC_OVERRIDE); 10521 ASSERT_EQ(0, tracefs_clear()); 10522 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10523 10524 sandbox_child_fs_access(_metadata, dir_s1d1, 10525 LANDLOCK_ACCESS_FS_READ_DIR, 10526 LANDLOCK_ACCESS_FS_READ_DIR, dir_s1d1); 10527 10528 set_cap(_metadata, CAP_DAC_OVERRIDE); 10529 buf = tracefs_read_trace(); 10530 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10531 ASSERT_NE(NULL, buf); 10532 10533 EXPECT_EQ(1, 10534 tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK))) 10535 { 10536 TH_LOG("Expected 1 check_rule_inode event\n%s", buf); 10537 } 10538 10539 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10540 "dev", field, sizeof(field))); 10541 EXPECT_STREQ(expected_dev, field) 10542 { 10543 TH_LOG("Expected dev=%s, got %s", expected_dev, field); 10544 } 10545 10546 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10547 "ino", field, sizeof(field))); 10548 EXPECT_STREQ(expected_ino, field) 10549 { 10550 TH_LOG("Expected ino=%s, got %s", expected_ino, field); 10551 } 10552 10553 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10554 "access_request", field, 10555 sizeof(field))); 10556 EXPECT_STREQ("read_dir", field) 10557 { 10558 TH_LOG("Expected access_request=read_dir, got %s", field); 10559 } 10560 10561 /* 10562 * The domain handles only READ_DIR, so the rule carries the 10563 * unhandled-rights padding; intersecting with the request leaves just 10564 * the requested read_dir (no padding, no hex). 10565 */ 10566 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10567 "grants", field, sizeof(field))); 10568 EXPECT_STREQ("{read_dir}", field) 10569 { 10570 TH_LOG("Expected grants={read_dir}, got %s", field); 10571 } 10572 10573 free(buf); 10574 } 10575 10576 /* 10577 * Verifies check_rule_inode behavior with multiple rules. With rules at s1d1 10578 * and s1d2 (a child of s1d1), accessing s1d2 produces only 1 event because the 10579 * pathwalk short-circuits after the first rule fully unmasks the single layer. 10580 */ 10581 TEST_F(trace_layout1, check_rule_fs_multiple_rules) 10582 { 10583 pid_t pid; 10584 int status; 10585 char *buf; 10586 int count; 10587 10588 if (!self->tracefs_ok) 10589 SKIP(return, "tracefs not available"); 10590 10591 set_cap(_metadata, CAP_DAC_OVERRIDE); 10592 ASSERT_EQ(0, tracefs_clear()); 10593 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10594 10595 pid = fork(); 10596 ASSERT_LE(0, pid); 10597 10598 if (pid == 0) { 10599 struct landlock_ruleset_attr attr = { 10600 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, 10601 }; 10602 struct landlock_path_beneath_attr path_beneath = { 10603 .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR, 10604 }; 10605 int ruleset_fd, fd; 10606 10607 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 10608 if (ruleset_fd < 0) 10609 _exit(1); 10610 10611 path_beneath.parent_fd = 10612 open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 10613 if (path_beneath.parent_fd < 0) 10614 _exit(1); 10615 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 10616 &path_beneath, 0)) 10617 _exit(1); 10618 close(path_beneath.parent_fd); 10619 10620 path_beneath.parent_fd = 10621 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 10622 if (path_beneath.parent_fd < 0) 10623 _exit(1); 10624 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 10625 &path_beneath, 0)) 10626 _exit(1); 10627 close(path_beneath.parent_fd); 10628 10629 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 10630 if (landlock_restrict_self(ruleset_fd, 0)) 10631 _exit(1); 10632 close(ruleset_fd); 10633 10634 fd = open(dir_s1d2, O_RDONLY | O_DIRECTORY | O_CLOEXEC); 10635 if (fd >= 0) 10636 close(fd); 10637 _exit(0); 10638 } 10639 10640 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 10641 ASSERT_TRUE(WIFEXITED(status)); 10642 EXPECT_EQ(0, WEXITSTATUS(status)); 10643 10644 set_cap(_metadata, CAP_DAC_OVERRIDE); 10645 buf = tracefs_read_trace(); 10646 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10647 ASSERT_NE(NULL, buf); 10648 10649 /* 10650 * Only one check_rule_inode event: the rule on dir_s1d2 fully unmasks 10651 * the single layer, so the pathwalk short-circuits before reaching the 10652 * dir_s1d1 rule. 10653 */ 10654 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)); 10655 EXPECT_EQ(1, count) 10656 { 10657 TH_LOG("Expected 1 check_rule_inode event, got %d\n%s", count, 10658 buf); 10659 } 10660 10661 free(buf); 10662 } 10663 10664 /* 10665 * Verifies the grants array is intersected with the request: a handled, 10666 * granted, but unrequested right (execute) is filtered out, leaving only the 10667 * requested read_dir. 10668 */ 10669 TEST_F(trace_layout1, check_rule_fs_request_subset) 10670 { 10671 char *buf; 10672 char field[64]; 10673 10674 if (!self->tracefs_ok) 10675 SKIP(return, "tracefs not available"); 10676 10677 set_cap(_metadata, CAP_DAC_OVERRIDE); 10678 ASSERT_EQ(0, tracefs_clear()); 10679 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10680 10681 /* 10682 * Handle and grant READ_DIR|EXECUTE; the open only requests read_dir. 10683 */ 10684 sandbox_child_fs_access( 10685 _metadata, dir_s1d1, 10686 LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE, 10687 LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE, 10688 dir_s1d1); 10689 10690 set_cap(_metadata, CAP_DAC_OVERRIDE); 10691 buf = tracefs_read_trace(); 10692 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10693 ASSERT_NE(NULL, buf); 10694 10695 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10696 "access_request", field, 10697 sizeof(field))); 10698 EXPECT_STREQ("read_dir", field); 10699 10700 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10701 "grants", field, sizeof(field))); 10702 EXPECT_STREQ("{read_dir}", field); 10703 10704 free(buf); 10705 } 10706 10707 /* 10708 * Verifies that the optional TRUNCATE access right, which hook_file_open() 10709 * speculatively evaluates on every open, appears in the access_request= and 10710 * grants= fields. Opening file1_s1d1 read-only needs only read_file, but the 10711 * open hook also evaluates truncate; the domain handles and the rule grants 10712 * both, so the event reports access_request=read_file|truncate and 10713 * grants={read_file|truncate}, and the open is allowed. 10714 */ 10715 TEST_F(trace_layout1, check_rule_fs_optional_access) 10716 { 10717 pid_t pid; 10718 int status; 10719 char *buf; 10720 char field[64]; 10721 int count; 10722 10723 if (!self->tracefs_ok) 10724 SKIP(return, "tracefs not available"); 10725 10726 set_cap(_metadata, CAP_DAC_OVERRIDE); 10727 ASSERT_EQ(0, tracefs_clear()); 10728 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10729 10730 pid = fork(); 10731 ASSERT_LE(0, pid); 10732 10733 if (pid == 0) { 10734 struct landlock_ruleset_attr attr = { 10735 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 10736 LANDLOCK_ACCESS_FS_TRUNCATE, 10737 }; 10738 struct landlock_path_beneath_attr path_beneath = { 10739 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 10740 LANDLOCK_ACCESS_FS_TRUNCATE, 10741 }; 10742 int ruleset_fd, fd; 10743 10744 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 10745 if (ruleset_fd < 0) 10746 _exit(1); 10747 10748 path_beneath.parent_fd = 10749 open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 10750 if (path_beneath.parent_fd < 0) 10751 _exit(1); 10752 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 10753 &path_beneath, 0)) 10754 _exit(1); 10755 close(path_beneath.parent_fd); 10756 10757 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 10758 if (landlock_restrict_self(ruleset_fd, 0)) 10759 _exit(1); 10760 close(ruleset_fd); 10761 10762 /* Read-only open needs only read_file; truncate is optional. */ 10763 fd = open(file1_s1d1, O_RDONLY | O_CLOEXEC); 10764 if (fd < 0) 10765 _exit(1); 10766 close(fd); 10767 _exit(0); 10768 } 10769 10770 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 10771 ASSERT_TRUE(WIFEXITED(status)); 10772 /* The open is allowed: the required read_file is granted. */ 10773 EXPECT_EQ(0, WEXITSTATUS(status)); 10774 10775 set_cap(_metadata, CAP_DAC_OVERRIDE); 10776 buf = tracefs_read_trace(); 10777 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10778 ASSERT_NE(NULL, buf); 10779 10780 /* The rule at dir_s1d1 matches when opening file1_s1d1. */ 10781 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)); 10782 EXPECT_EQ(1, count) 10783 { 10784 TH_LOG("Expected 1 check_rule_inode event, got %d\n%s", count, 10785 buf); 10786 } 10787 10788 /* The open hook adds the optional truncate to the request. */ 10789 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10790 "access_request", field, 10791 sizeof(field))); 10792 EXPECT_STREQ("read_file|truncate", field); 10793 10794 /* The rule grants both, so truncate appears in the grants array. */ 10795 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK), 10796 "grants", field, sizeof(field))); 10797 EXPECT_STREQ("{read_file|truncate}", field); 10798 10799 free(buf); 10800 } 10801 10802 /* 10803 * Verifies that check_rule_inode fires for a rule matching the inode even when 10804 * it grants none of the requested rights, so the grants set is empty. Landlock 10805 * cannot know a rule ignores the request before reading it, so the event is 10806 * still emitted (grants={}), which lets a tracer see that the rule matched. 10807 * The domain handles READ_DIR|EXECUTE, dir_s1d2 grants only EXECUTE and its 10808 * parent dir_s1d1 grants only READ_DIR. Reading dir_s1d2 (requesting read_dir) 10809 * first matches the dir_s1d2 rule, which grants nothing requested (grants={}); 10810 * walking up to dir_s1d1 then grants read_dir (grants={read_dir}) and allows 10811 * the access. 10812 */ 10813 TEST_F(trace_layout1, check_rule_fs_empty_grant) 10814 { 10815 pid_t pid; 10816 int status; 10817 char *buf; 10818 int count; 10819 10820 if (!self->tracefs_ok) 10821 SKIP(return, "tracefs not available"); 10822 10823 set_cap(_metadata, CAP_DAC_OVERRIDE); 10824 ASSERT_EQ(0, tracefs_clear()); 10825 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10826 10827 pid = fork(); 10828 ASSERT_LE(0, pid); 10829 10830 if (pid == 0) { 10831 struct landlock_ruleset_attr attr = { 10832 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR | 10833 LANDLOCK_ACCESS_FS_EXECUTE, 10834 }; 10835 struct landlock_path_beneath_attr path_beneath = {}; 10836 int ruleset_fd, fd; 10837 10838 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 10839 if (ruleset_fd < 0) 10840 _exit(1); 10841 10842 /* Parent dir_s1d1 grants only READ_DIR. */ 10843 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_DIR; 10844 path_beneath.parent_fd = 10845 open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 10846 if (path_beneath.parent_fd < 0) 10847 _exit(1); 10848 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 10849 &path_beneath, 0)) 10850 _exit(1); 10851 close(path_beneath.parent_fd); 10852 10853 /* Child dir_s1d2 grants only EXECUTE. */ 10854 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_EXECUTE; 10855 path_beneath.parent_fd = 10856 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 10857 if (path_beneath.parent_fd < 0) 10858 _exit(1); 10859 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 10860 &path_beneath, 0)) 10861 _exit(1); 10862 close(path_beneath.parent_fd); 10863 10864 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 10865 if (landlock_restrict_self(ruleset_fd, 0)) 10866 _exit(1); 10867 close(ruleset_fd); 10868 10869 fd = open(dir_s1d2, O_RDONLY | O_DIRECTORY | O_CLOEXEC); 10870 if (fd < 0) 10871 _exit(1); 10872 close(fd); 10873 _exit(0); 10874 } 10875 10876 ASSERT_EQ(pid, waitpid(pid, &status, 0)); 10877 ASSERT_TRUE(WIFEXITED(status)); 10878 EXPECT_EQ(0, WEXITSTATUS(status)); 10879 10880 set_cap(_metadata, CAP_DAC_OVERRIDE); 10881 buf = tracefs_read_trace(); 10882 clear_cap(_metadata, CAP_DAC_OVERRIDE); 10883 ASSERT_NE(NULL, buf); 10884 10885 /* 10886 * dir_s1d2 (grants nothing requested) then dir_s1d1 (grants read_dir). 10887 */ 10888 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)); 10889 EXPECT_EQ(2, count) 10890 { 10891 TH_LOG("Expected 2 check_rule_inode events, got %d\n%s", count, 10892 buf); 10893 } 10894 10895 /* The dir_s1d2 rule matches the inode but grants none of read_dir. */ 10896 EXPECT_EQ( 10897 1, 10898 tracefs_count_matches( 10899 buf, 10900 TRACE_PREFIX( 10901 TRACE_TASK) "landlock_check_rule_inode: domain=[0-9a-f]\\+ " 10902 "access_request=read_dir " 10903 "dev=[0-9]\\+:[0-9]\\+ ino=[0-9]\\+ " 10904 "grants={}$")) 10905 { 10906 TH_LOG("Expected a grants={} event\n%s", buf); 10907 } 10908 10909 /* Walking up to dir_s1d1 grants the requested read_dir. */ 10910 EXPECT_EQ( 10911 1, 10912 tracefs_count_matches( 10913 buf, 10914 TRACE_PREFIX( 10915 TRACE_TASK) "landlock_check_rule_inode: domain=[0-9a-f]\\+ " 10916 "access_request=read_dir " 10917 "dev=[0-9]\\+:[0-9]\\+ ino=[0-9]\\+ " 10918 "grants={read_dir}$")) 10919 { 10920 TH_LOG("Expected a grants={read_dir} event\n%s", buf); 10921 } 10922 10923 free(buf); 10924 } 10925 10926 TEST_HARNESS_MAIN 10927