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