1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Landlock tests - Filesystem 4 * 5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 * Copyright © 2020 ANSSI 7 * Copyright © 2020-2022 Microsoft Corporation 8 */ 9 10 #define _GNU_SOURCE 11 #include <asm/termbits.h> 12 #include <fcntl.h> 13 #include <libgen.h> 14 #include <linux/fiemap.h> 15 #include <linux/landlock.h> 16 #include <linux/magic.h> 17 #include <sched.h> 18 #include <stddef.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <sys/capability.h> 22 #include <sys/ioctl.h> 23 #include <sys/mount.h> 24 #include <sys/prctl.h> 25 #include <sys/sendfile.h> 26 #include <sys/socket.h> 27 #include <sys/stat.h> 28 #include <sys/sysmacros.h> 29 #include <sys/un.h> 30 #include <sys/vfs.h> 31 #include <unistd.h> 32 33 /* 34 * Intentionally included last to work around header conflict. 35 * See https://sourceware.org/glibc/wiki/Synchronizing_Headers. 36 */ 37 #include <linux/fs.h> 38 #include <linux/mount.h> 39 40 /* Defines AT_EXECVE_CHECK without type conflicts. */ 41 #define _ASM_GENERIC_FCNTL_H 42 #include <linux/fcntl.h> 43 44 #include "audit.h" 45 #include "common.h" 46 47 #ifndef renameat2 48 int renameat2(int olddirfd, const char *oldpath, int newdirfd, 49 const char *newpath, unsigned int flags) 50 { 51 return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 52 flags); 53 } 54 #endif 55 56 #ifndef open_tree 57 int open_tree(int dfd, const char *filename, unsigned int flags) 58 { 59 return syscall(__NR_open_tree, dfd, filename, flags); 60 } 61 #endif 62 63 static int sys_execveat(int dirfd, const char *pathname, char *const argv[], 64 char *const envp[], int flags) 65 { 66 return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags); 67 } 68 69 #ifndef RENAME_EXCHANGE 70 #define RENAME_EXCHANGE (1 << 1) 71 #endif 72 73 static const char bin_true[] = "./true"; 74 75 /* Paths (sibling number and depth) */ 76 static const char dir_s1d1[] = TMP_DIR "/s1d1"; 77 static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 78 static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 79 static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 80 static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 81 static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 82 static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 83 static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 84 static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 85 86 static const char dir_s2d1[] = TMP_DIR "/s2d1"; 87 static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 88 static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 89 static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 90 static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 91 static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 92 static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 93 94 static const char dir_s3d1[] = TMP_DIR "/s3d1"; 95 static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; 96 /* dir_s3d2 is a mount point. */ 97 static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 98 static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 99 static const char file1_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3/f1"; 100 static const char dir_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4"; 101 static const char file1_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4/f1"; 102 103 /* 104 * layout1 hierarchy: 105 * 106 * tmp 107 * ├── s1d1 108 * │ ├── f1 109 * │ ├── f2 110 * │ └── s1d2 111 * │ ├── f1 112 * │ ├── f2 113 * │ └── s1d3 114 * │ ├── f1 115 * │ └── f2 116 * ├── s2d1 117 * │ ├── f1 118 * │ └── s2d2 119 * │ ├── f1 120 * │ └── s2d3 121 * │ ├── f1 122 * │ └── f2 123 * └── s3d1 124 * ├── f1 125 * └── s3d2 [mount point] 126 * ├── s3d3 127 * │ └── f1 128 * └── s3d4 129 * └── f1 130 */ 131 132 static bool fgrep(FILE *const inf, const char *const str) 133 { 134 char line[32]; 135 const int slen = strlen(str); 136 137 while (!feof(inf)) { 138 if (!fgets(line, sizeof(line), inf)) 139 break; 140 if (strncmp(line, str, slen)) 141 continue; 142 143 return true; 144 } 145 146 return false; 147 } 148 149 static bool supports_filesystem(const char *const filesystem) 150 { 151 char str[32]; 152 int len; 153 bool res = true; 154 FILE *const inf = fopen("/proc/filesystems", "r"); 155 156 /* 157 * Consider that the filesystem is supported if we cannot get the 158 * supported ones. 159 */ 160 if (!inf) 161 return true; 162 163 /* filesystem can be null for bind mounts. */ 164 if (!filesystem) 165 goto out; 166 167 len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); 168 if (len >= sizeof(str)) 169 /* Ignores too-long filesystem names. */ 170 goto out; 171 172 res = fgrep(inf, str); 173 174 out: 175 fclose(inf); 176 return res; 177 } 178 179 static bool cwd_matches_fs(unsigned int fs_magic) 180 { 181 struct statfs statfs_buf; 182 183 if (!fs_magic) 184 return true; 185 186 if (statfs(".", &statfs_buf)) 187 return true; 188 189 return statfs_buf.f_type == fs_magic; 190 } 191 192 static void mkdir_parents(struct __test_metadata *const _metadata, 193 const char *const path) 194 { 195 char *walker; 196 const char *parent; 197 int i, err; 198 199 ASSERT_NE(path[0], '\0'); 200 walker = strdup(path); 201 ASSERT_NE(NULL, walker); 202 parent = walker; 203 for (i = 1; walker[i]; i++) { 204 if (walker[i] != '/') 205 continue; 206 walker[i] = '\0'; 207 err = mkdir(parent, 0700); 208 ASSERT_FALSE(err && errno != EEXIST) 209 { 210 TH_LOG("Failed to create directory \"%s\": %s", parent, 211 strerror(errno)); 212 } 213 walker[i] = '/'; 214 } 215 free(walker); 216 } 217 218 static void create_directory(struct __test_metadata *const _metadata, 219 const char *const path) 220 { 221 mkdir_parents(_metadata, path); 222 ASSERT_EQ(0, mkdir(path, 0700)) 223 { 224 TH_LOG("Failed to create directory \"%s\": %s", path, 225 strerror(errno)); 226 } 227 } 228 229 static void create_file(struct __test_metadata *const _metadata, 230 const char *const path) 231 { 232 mkdir_parents(_metadata, path); 233 ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 234 { 235 TH_LOG("Failed to create file \"%s\": %s", path, 236 strerror(errno)); 237 } 238 } 239 240 static int remove_path(const char *const path) 241 { 242 char *walker; 243 int i, ret, err = 0; 244 245 walker = strdup(path); 246 if (!walker) { 247 err = ENOMEM; 248 goto out; 249 } 250 if (unlink(path) && rmdir(path)) { 251 if (errno != ENOENT && errno != ENOTDIR) 252 err = errno; 253 goto out; 254 } 255 for (i = strlen(walker); i > 0; i--) { 256 if (walker[i] != '/') 257 continue; 258 walker[i] = '\0'; 259 ret = rmdir(walker); 260 if (ret) { 261 if (errno != ENOTEMPTY && errno != EBUSY) 262 err = errno; 263 goto out; 264 } 265 if (strcmp(walker, TMP_DIR) == 0) 266 goto out; 267 } 268 269 out: 270 free(walker); 271 return err; 272 } 273 274 struct mnt_opt { 275 const char *const source; 276 const char *const type; 277 const unsigned long flags; 278 const char *const data; 279 }; 280 281 #define MNT_TMP_DATA "size=4m,mode=700" 282 283 static const struct mnt_opt mnt_tmp = { 284 .type = "tmpfs", 285 .data = MNT_TMP_DATA, 286 }; 287 288 static int mount_opt(const struct mnt_opt *const mnt, const char *const target) 289 { 290 return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags, 291 mnt->data); 292 } 293 294 static void prepare_layout_opt(struct __test_metadata *const _metadata, 295 const struct mnt_opt *const mnt) 296 { 297 disable_caps(_metadata); 298 umask(0077); 299 create_directory(_metadata, TMP_DIR); 300 301 /* 302 * Do not pollute the rest of the system: creates a private mount point 303 * for tests relying on pivot_root(2) and move_mount(2). 304 */ 305 set_cap(_metadata, CAP_SYS_ADMIN); 306 ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); 307 ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) 308 { 309 TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, 310 strerror(errno)); 311 /* 312 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() 313 * failed, so we need to explicitly do a minimal cleanup to 314 * avoid cascading errors with other tests that don't depend on 315 * the same filesystem. 316 */ 317 remove_path(TMP_DIR); 318 } 319 ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 320 clear_cap(_metadata, CAP_SYS_ADMIN); 321 } 322 323 static void prepare_layout(struct __test_metadata *const _metadata) 324 { 325 prepare_layout_opt(_metadata, &mnt_tmp); 326 } 327 328 static void cleanup_layout(struct __test_metadata *const _metadata) 329 { 330 set_cap(_metadata, CAP_SYS_ADMIN); 331 if (umount(TMP_DIR)) { 332 /* 333 * According to the test environment, the mount point of the 334 * current directory may be shared or not, which changes the 335 * visibility of the nested TMP_DIR mount point for the test's 336 * parent process doing this cleanup. 337 */ 338 ASSERT_EQ(EINVAL, errno); 339 } 340 clear_cap(_metadata, CAP_SYS_ADMIN); 341 EXPECT_EQ(0, remove_path(TMP_DIR)); 342 } 343 344 /* clang-format off */ 345 FIXTURE(layout0) {}; 346 /* clang-format on */ 347 348 FIXTURE_SETUP(layout0) 349 { 350 prepare_layout(_metadata); 351 } 352 353 FIXTURE_TEARDOWN_PARENT(layout0) 354 { 355 cleanup_layout(_metadata); 356 } 357 358 static void create_layout1(struct __test_metadata *const _metadata) 359 { 360 create_file(_metadata, file1_s1d1); 361 create_file(_metadata, file1_s1d2); 362 create_file(_metadata, file1_s1d3); 363 create_file(_metadata, file2_s1d1); 364 create_file(_metadata, file2_s1d2); 365 create_file(_metadata, file2_s1d3); 366 367 create_file(_metadata, file1_s2d1); 368 create_file(_metadata, file1_s2d2); 369 create_file(_metadata, file1_s2d3); 370 create_file(_metadata, file2_s2d3); 371 372 create_file(_metadata, file1_s3d1); 373 create_directory(_metadata, dir_s3d2); 374 set_cap(_metadata, CAP_SYS_ADMIN); 375 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 376 clear_cap(_metadata, CAP_SYS_ADMIN); 377 378 create_file(_metadata, file1_s3d3); 379 create_file(_metadata, file1_s3d4); 380 } 381 382 static void remove_layout1(struct __test_metadata *const _metadata) 383 { 384 EXPECT_EQ(0, remove_path(file2_s1d3)); 385 EXPECT_EQ(0, remove_path(file2_s1d2)); 386 EXPECT_EQ(0, remove_path(file2_s1d1)); 387 EXPECT_EQ(0, remove_path(file1_s1d3)); 388 EXPECT_EQ(0, remove_path(file1_s1d2)); 389 EXPECT_EQ(0, remove_path(file1_s1d1)); 390 EXPECT_EQ(0, remove_path(dir_s1d3)); 391 392 EXPECT_EQ(0, remove_path(file2_s2d3)); 393 EXPECT_EQ(0, remove_path(file1_s2d3)); 394 EXPECT_EQ(0, remove_path(file1_s2d2)); 395 EXPECT_EQ(0, remove_path(file1_s2d1)); 396 EXPECT_EQ(0, remove_path(dir_s2d2)); 397 398 EXPECT_EQ(0, remove_path(file1_s3d1)); 399 EXPECT_EQ(0, remove_path(file1_s3d3)); 400 EXPECT_EQ(0, remove_path(file1_s3d4)); 401 set_cap(_metadata, CAP_SYS_ADMIN); 402 umount(dir_s3d2); 403 clear_cap(_metadata, CAP_SYS_ADMIN); 404 EXPECT_EQ(0, remove_path(dir_s3d2)); 405 } 406 407 /* clang-format off */ 408 FIXTURE(layout1) {}; 409 /* clang-format on */ 410 411 FIXTURE_SETUP(layout1) 412 { 413 prepare_layout(_metadata); 414 415 create_layout1(_metadata); 416 } 417 418 FIXTURE_TEARDOWN_PARENT(layout1) 419 { 420 remove_layout1(_metadata); 421 422 cleanup_layout(_metadata); 423 } 424 425 /* 426 * This helper enables to use the ASSERT_* macros and print the line number 427 * pointing to the test caller. 428 */ 429 static int test_open_rel(const int dirfd, const char *const path, 430 const int flags) 431 { 432 int fd; 433 434 /* Works with file and directories. */ 435 fd = openat(dirfd, path, flags | O_CLOEXEC); 436 if (fd < 0) 437 return errno; 438 /* 439 * Mixing error codes from close(2) and open(2) should not lead to any 440 * (access type) confusion for this test. 441 */ 442 if (close(fd) != 0) 443 return errno; 444 return 0; 445 } 446 447 static int test_open(const char *const path, const int flags) 448 { 449 return test_open_rel(AT_FDCWD, path, flags); 450 } 451 452 TEST_F_FORK(layout1, no_restriction) 453 { 454 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 455 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 456 ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 457 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 458 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 459 ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 460 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 461 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 462 463 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 464 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 465 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 466 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 467 ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 468 ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 469 470 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 471 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 472 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 473 } 474 475 TEST_F_FORK(layout1, inval) 476 { 477 struct landlock_path_beneath_attr path_beneath = { 478 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 479 LANDLOCK_ACCESS_FS_WRITE_FILE, 480 .parent_fd = -1, 481 }; 482 struct landlock_ruleset_attr ruleset_attr = { 483 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 484 LANDLOCK_ACCESS_FS_WRITE_FILE, 485 }; 486 int ruleset_fd; 487 488 path_beneath.parent_fd = 489 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 490 ASSERT_LE(0, path_beneath.parent_fd); 491 492 ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 493 ASSERT_LE(0, ruleset_fd); 494 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 495 &path_beneath, 0)); 496 /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 497 ASSERT_EQ(EBADF, errno); 498 ASSERT_EQ(0, close(ruleset_fd)); 499 500 ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 501 ASSERT_LE(0, ruleset_fd); 502 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 503 &path_beneath, 0)); 504 /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 505 ASSERT_EQ(EBADFD, errno); 506 ASSERT_EQ(0, close(ruleset_fd)); 507 508 /* Gets a real ruleset. */ 509 ruleset_fd = 510 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 511 ASSERT_LE(0, ruleset_fd); 512 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 513 &path_beneath, 0)); 514 ASSERT_EQ(0, close(path_beneath.parent_fd)); 515 516 /* Tests without O_PATH. */ 517 path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 518 ASSERT_LE(0, path_beneath.parent_fd); 519 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 520 &path_beneath, 0)); 521 ASSERT_EQ(0, close(path_beneath.parent_fd)); 522 523 /* Tests with a ruleset FD. */ 524 path_beneath.parent_fd = ruleset_fd; 525 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 526 &path_beneath, 0)); 527 ASSERT_EQ(EBADFD, errno); 528 529 /* Checks unhandled allowed_access. */ 530 path_beneath.parent_fd = 531 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 532 ASSERT_LE(0, path_beneath.parent_fd); 533 534 /* Test with legitimate values. */ 535 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 536 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 537 &path_beneath, 0)); 538 ASSERT_EQ(EINVAL, errno); 539 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 540 541 /* Tests with denied-by-default access right. */ 542 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 543 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 544 &path_beneath, 0)); 545 ASSERT_EQ(EINVAL, errno); 546 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 547 548 /* Test with unknown (64-bits) value. */ 549 path_beneath.allowed_access |= (1ULL << 60); 550 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 551 &path_beneath, 0)); 552 ASSERT_EQ(EINVAL, errno); 553 path_beneath.allowed_access &= ~(1ULL << 60); 554 555 /* Test with no access. */ 556 path_beneath.allowed_access = 0; 557 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 558 &path_beneath, 0)); 559 ASSERT_EQ(ENOMSG, errno); 560 path_beneath.allowed_access &= ~(1ULL << 60); 561 562 ASSERT_EQ(0, close(path_beneath.parent_fd)); 563 564 /* Enforces the ruleset. */ 565 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 566 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 567 568 ASSERT_EQ(0, close(ruleset_fd)); 569 } 570 571 /* clang-format off */ 572 573 #define ACCESS_FILE ( \ 574 LANDLOCK_ACCESS_FS_EXECUTE | \ 575 LANDLOCK_ACCESS_FS_WRITE_FILE | \ 576 LANDLOCK_ACCESS_FS_READ_FILE | \ 577 LANDLOCK_ACCESS_FS_TRUNCATE | \ 578 LANDLOCK_ACCESS_FS_IOCTL_DEV) 579 580 #define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV 581 582 #define ACCESS_ALL ( \ 583 ACCESS_FILE | \ 584 LANDLOCK_ACCESS_FS_READ_DIR | \ 585 LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 586 LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 587 LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 588 LANDLOCK_ACCESS_FS_MAKE_DIR | \ 589 LANDLOCK_ACCESS_FS_MAKE_REG | \ 590 LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 591 LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 592 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 593 LANDLOCK_ACCESS_FS_MAKE_SYM | \ 594 LANDLOCK_ACCESS_FS_REFER) 595 596 /* clang-format on */ 597 598 TEST_F_FORK(layout1, file_and_dir_access_rights) 599 { 600 __u64 access; 601 int err; 602 struct landlock_path_beneath_attr path_beneath_file = {}, 603 path_beneath_dir = {}; 604 struct landlock_ruleset_attr ruleset_attr = { 605 .handled_access_fs = ACCESS_ALL, 606 }; 607 const int ruleset_fd = 608 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 609 610 ASSERT_LE(0, ruleset_fd); 611 612 /* Tests access rights for files. */ 613 path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 614 ASSERT_LE(0, path_beneath_file.parent_fd); 615 616 /* Tests access rights for directories. */ 617 path_beneath_dir.parent_fd = 618 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 619 ASSERT_LE(0, path_beneath_dir.parent_fd); 620 621 for (access = 1; access <= ACCESS_LAST; access <<= 1) { 622 path_beneath_dir.allowed_access = access; 623 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 624 LANDLOCK_RULE_PATH_BENEATH, 625 &path_beneath_dir, 0)); 626 627 path_beneath_file.allowed_access = access; 628 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 629 &path_beneath_file, 0); 630 if (access & ACCESS_FILE) { 631 ASSERT_EQ(0, err); 632 } else { 633 ASSERT_EQ(-1, err); 634 ASSERT_EQ(EINVAL, errno); 635 } 636 } 637 ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 638 ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 639 ASSERT_EQ(0, close(ruleset_fd)); 640 } 641 642 TEST_F_FORK(layout0, ruleset_with_unknown_access) 643 { 644 __u64 access_mask; 645 646 for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 647 access_mask >>= 1) { 648 struct landlock_ruleset_attr ruleset_attr = { 649 .handled_access_fs = access_mask, 650 }; 651 652 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 653 sizeof(ruleset_attr), 0)); 654 ASSERT_EQ(EINVAL, errno); 655 } 656 } 657 658 TEST_F_FORK(layout0, rule_with_unknown_access) 659 { 660 __u64 access; 661 struct landlock_path_beneath_attr path_beneath = {}; 662 const struct landlock_ruleset_attr ruleset_attr = { 663 .handled_access_fs = ACCESS_ALL, 664 }; 665 const int ruleset_fd = 666 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 667 668 ASSERT_LE(0, ruleset_fd); 669 670 path_beneath.parent_fd = 671 open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC); 672 ASSERT_LE(0, path_beneath.parent_fd); 673 674 for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) { 675 path_beneath.allowed_access = access; 676 EXPECT_EQ(-1, landlock_add_rule(ruleset_fd, 677 LANDLOCK_RULE_PATH_BENEATH, 678 &path_beneath, 0)); 679 EXPECT_EQ(EINVAL, errno); 680 } 681 ASSERT_EQ(0, close(path_beneath.parent_fd)); 682 ASSERT_EQ(0, close(ruleset_fd)); 683 } 684 685 TEST_F_FORK(layout1, rule_with_unhandled_access) 686 { 687 struct landlock_ruleset_attr ruleset_attr = { 688 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 689 }; 690 struct landlock_path_beneath_attr path_beneath = {}; 691 int ruleset_fd; 692 __u64 access; 693 694 ruleset_fd = 695 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 696 ASSERT_LE(0, ruleset_fd); 697 698 path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 699 ASSERT_LE(0, path_beneath.parent_fd); 700 701 for (access = 1; access > 0; access <<= 1) { 702 int err; 703 704 path_beneath.allowed_access = access; 705 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 706 &path_beneath, 0); 707 if (access == ruleset_attr.handled_access_fs) { 708 EXPECT_EQ(0, err); 709 } else { 710 EXPECT_EQ(-1, err); 711 EXPECT_EQ(EINVAL, errno); 712 } 713 } 714 715 EXPECT_EQ(0, close(path_beneath.parent_fd)); 716 EXPECT_EQ(0, close(ruleset_fd)); 717 } 718 719 static void add_path_beneath(struct __test_metadata *const _metadata, 720 const int ruleset_fd, const __u64 allowed_access, 721 const char *const path) 722 { 723 struct landlock_path_beneath_attr path_beneath = { 724 .allowed_access = allowed_access, 725 }; 726 727 path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 728 ASSERT_LE(0, path_beneath.parent_fd) 729 { 730 TH_LOG("Failed to open directory \"%s\": %s", path, 731 strerror(errno)); 732 } 733 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 734 &path_beneath, 0)) 735 { 736 TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 737 strerror(errno)); 738 } 739 ASSERT_EQ(0, close(path_beneath.parent_fd)); 740 } 741 742 struct rule { 743 const char *path; 744 __u64 access; 745 }; 746 747 /* clang-format off */ 748 749 #define ACCESS_RO ( \ 750 LANDLOCK_ACCESS_FS_READ_FILE | \ 751 LANDLOCK_ACCESS_FS_READ_DIR) 752 753 #define ACCESS_RW ( \ 754 ACCESS_RO | \ 755 LANDLOCK_ACCESS_FS_WRITE_FILE) 756 757 /* clang-format on */ 758 759 static int create_ruleset(struct __test_metadata *const _metadata, 760 const __u64 handled_access_fs, 761 const struct rule rules[]) 762 { 763 int ruleset_fd, i; 764 struct landlock_ruleset_attr ruleset_attr = { 765 .handled_access_fs = handled_access_fs, 766 }; 767 768 ASSERT_NE(NULL, rules) 769 { 770 TH_LOG("No rule list"); 771 } 772 ASSERT_NE(NULL, rules[0].path) 773 { 774 TH_LOG("Empty rule list"); 775 } 776 777 ruleset_fd = 778 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 779 ASSERT_LE(0, ruleset_fd) 780 { 781 TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 782 } 783 784 for (i = 0; rules[i].path; i++) { 785 if (!rules[i].access) 786 continue; 787 788 add_path_beneath(_metadata, ruleset_fd, rules[i].access, 789 rules[i].path); 790 } 791 return ruleset_fd; 792 } 793 794 TEST_F_FORK(layout0, proc_nsfs) 795 { 796 const struct rule rules[] = { 797 { 798 .path = "/dev/null", 799 .access = LANDLOCK_ACCESS_FS_READ_FILE | 800 LANDLOCK_ACCESS_FS_WRITE_FILE, 801 }, 802 {}, 803 }; 804 struct landlock_path_beneath_attr path_beneath; 805 const int ruleset_fd = create_ruleset( 806 _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 807 rules); 808 809 ASSERT_LE(0, ruleset_fd); 810 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 811 812 enforce_ruleset(_metadata, ruleset_fd); 813 814 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 815 ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 816 ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 817 ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 818 819 ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 820 ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 821 ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 822 /* 823 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 824 * disconnected path. Such path cannot be identified and must then be 825 * allowed. 826 */ 827 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 828 829 /* 830 * Checks that it is not possible to add nsfs-like filesystem 831 * references to a ruleset. 832 */ 833 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 834 LANDLOCK_ACCESS_FS_WRITE_FILE, 835 path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 836 ASSERT_LE(0, path_beneath.parent_fd); 837 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 838 &path_beneath, 0)); 839 ASSERT_EQ(EBADFD, errno); 840 ASSERT_EQ(0, close(path_beneath.parent_fd)); 841 } 842 843 TEST_F_FORK(layout0, unpriv) 844 { 845 const struct rule rules[] = { 846 { 847 .path = TMP_DIR, 848 .access = ACCESS_RO, 849 }, 850 {}, 851 }; 852 int ruleset_fd; 853 854 drop_caps(_metadata); 855 856 ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 857 ASSERT_LE(0, ruleset_fd); 858 ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 859 ASSERT_EQ(EPERM, errno); 860 861 /* enforce_ruleset() calls prctl(no_new_privs). */ 862 enforce_ruleset(_metadata, ruleset_fd); 863 ASSERT_EQ(0, close(ruleset_fd)); 864 } 865 866 TEST_F_FORK(layout1, effective_access) 867 { 868 const struct rule rules[] = { 869 { 870 .path = dir_s1d2, 871 .access = ACCESS_RO, 872 }, 873 { 874 .path = file1_s2d2, 875 .access = LANDLOCK_ACCESS_FS_READ_FILE | 876 LANDLOCK_ACCESS_FS_WRITE_FILE, 877 }, 878 {}, 879 }; 880 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 881 char buf; 882 int reg_fd; 883 884 ASSERT_LE(0, ruleset_fd); 885 enforce_ruleset(_metadata, ruleset_fd); 886 ASSERT_EQ(0, close(ruleset_fd)); 887 888 /* Tests on a directory (with or without O_PATH). */ 889 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 890 ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 891 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 892 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 893 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 894 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 895 896 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 897 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 898 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 899 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 900 901 /* Tests on a file (with or without O_PATH). */ 902 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 903 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 904 905 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 906 907 /* Checks effective read and write actions. */ 908 reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 909 ASSERT_LE(0, reg_fd); 910 ASSERT_EQ(1, write(reg_fd, ".", 1)); 911 ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 912 ASSERT_EQ(1, read(reg_fd, &buf, 1)); 913 ASSERT_EQ('.', buf); 914 ASSERT_EQ(0, close(reg_fd)); 915 916 /* Just in case, double-checks effective actions. */ 917 reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 918 ASSERT_LE(0, reg_fd); 919 ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 920 ASSERT_EQ(EBADF, errno); 921 ASSERT_EQ(0, close(reg_fd)); 922 } 923 924 TEST_F_FORK(layout1, unhandled_access) 925 { 926 const struct rule rules[] = { 927 { 928 .path = dir_s1d2, 929 .access = ACCESS_RO, 930 }, 931 {}, 932 }; 933 /* Here, we only handle read accesses, not write accesses. */ 934 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 935 936 ASSERT_LE(0, ruleset_fd); 937 enforce_ruleset(_metadata, ruleset_fd); 938 ASSERT_EQ(0, close(ruleset_fd)); 939 940 /* 941 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 942 * opening for write-only should be allowed, but not read-write. 943 */ 944 ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 945 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 946 947 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 948 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 949 } 950 951 TEST_F_FORK(layout1, ruleset_overlap) 952 { 953 const struct rule rules[] = { 954 /* These rules should be ORed among them. */ 955 { 956 .path = dir_s1d2, 957 .access = LANDLOCK_ACCESS_FS_READ_FILE | 958 LANDLOCK_ACCESS_FS_WRITE_FILE, 959 }, 960 { 961 .path = dir_s1d2, 962 .access = LANDLOCK_ACCESS_FS_READ_FILE | 963 LANDLOCK_ACCESS_FS_READ_DIR, 964 }, 965 {}, 966 }; 967 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 968 969 ASSERT_LE(0, ruleset_fd); 970 enforce_ruleset(_metadata, ruleset_fd); 971 ASSERT_EQ(0, close(ruleset_fd)); 972 973 /* Checks s1d1 hierarchy. */ 974 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 975 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 976 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 977 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 978 979 /* Checks s1d2 hierarchy. */ 980 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 981 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 982 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 983 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 984 985 /* Checks s1d3 hierarchy. */ 986 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 987 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 988 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 989 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 990 } 991 992 TEST_F_FORK(layout1, layer_rule_unions) 993 { 994 const struct rule layer1[] = { 995 { 996 .path = dir_s1d2, 997 .access = LANDLOCK_ACCESS_FS_READ_FILE, 998 }, 999 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 1000 { 1001 .path = dir_s1d3, 1002 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1003 }, 1004 {}, 1005 }; 1006 const struct rule layer2[] = { 1007 /* Doesn't change anything from layer1. */ 1008 { 1009 .path = dir_s1d2, 1010 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1011 LANDLOCK_ACCESS_FS_WRITE_FILE, 1012 }, 1013 {}, 1014 }; 1015 const struct rule layer3[] = { 1016 /* Only allows write (but not read) to dir_s1d3. */ 1017 { 1018 .path = dir_s1d2, 1019 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1020 }, 1021 {}, 1022 }; 1023 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1); 1024 1025 ASSERT_LE(0, ruleset_fd); 1026 enforce_ruleset(_metadata, ruleset_fd); 1027 ASSERT_EQ(0, close(ruleset_fd)); 1028 1029 /* Checks s1d1 hierarchy with layer1. */ 1030 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1031 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1032 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1033 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1034 1035 /* Checks s1d2 hierarchy with layer1. */ 1036 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1037 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1038 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1039 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1040 1041 /* Checks s1d3 hierarchy with layer1. */ 1042 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1043 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1044 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 1045 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1046 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1047 1048 /* Doesn't change anything from layer1. */ 1049 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2); 1050 ASSERT_LE(0, ruleset_fd); 1051 enforce_ruleset(_metadata, ruleset_fd); 1052 ASSERT_EQ(0, close(ruleset_fd)); 1053 1054 /* Checks s1d1 hierarchy with layer2. */ 1055 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1056 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1057 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1058 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1059 1060 /* Checks s1d2 hierarchy with layer2. */ 1061 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1062 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1063 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1064 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1065 1066 /* Checks s1d3 hierarchy with layer2. */ 1067 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1068 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1069 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 1070 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1071 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1072 1073 /* Only allows write (but not read) to dir_s1d3. */ 1074 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3); 1075 ASSERT_LE(0, ruleset_fd); 1076 enforce_ruleset(_metadata, ruleset_fd); 1077 ASSERT_EQ(0, close(ruleset_fd)); 1078 1079 /* Checks s1d1 hierarchy with layer3. */ 1080 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1081 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1082 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 1083 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1084 1085 /* Checks s1d2 hierarchy with layer3. */ 1086 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 1087 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1088 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1089 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1090 1091 /* Checks s1d3 hierarchy with layer3. */ 1092 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1093 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 1094 /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 1095 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 1096 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1097 } 1098 1099 TEST_F_FORK(layout1, non_overlapping_accesses) 1100 { 1101 const struct rule layer1[] = { 1102 { 1103 .path = dir_s1d2, 1104 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1105 }, 1106 {}, 1107 }; 1108 const struct rule layer2[] = { 1109 { 1110 .path = dir_s1d3, 1111 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1112 }, 1113 {}, 1114 }; 1115 int ruleset_fd; 1116 1117 ASSERT_EQ(0, unlink(file1_s1d1)); 1118 ASSERT_EQ(0, unlink(file1_s1d2)); 1119 1120 ruleset_fd = 1121 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 1122 ASSERT_LE(0, ruleset_fd); 1123 enforce_ruleset(_metadata, ruleset_fd); 1124 ASSERT_EQ(0, close(ruleset_fd)); 1125 1126 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1127 ASSERT_EQ(EACCES, errno); 1128 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1129 ASSERT_EQ(0, unlink(file1_s1d2)); 1130 1131 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 1132 layer2); 1133 ASSERT_LE(0, ruleset_fd); 1134 enforce_ruleset(_metadata, ruleset_fd); 1135 ASSERT_EQ(0, close(ruleset_fd)); 1136 1137 /* Unchanged accesses for file creation. */ 1138 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1139 ASSERT_EQ(EACCES, errno); 1140 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1141 1142 /* Checks file removing. */ 1143 ASSERT_EQ(-1, unlink(file1_s1d2)); 1144 ASSERT_EQ(EACCES, errno); 1145 ASSERT_EQ(0, unlink(file1_s1d3)); 1146 } 1147 1148 TEST_F_FORK(layout1, interleaved_masked_accesses) 1149 { 1150 /* 1151 * Checks overly restrictive rules: 1152 * layer 1: allows R s1d1/s1d2/s1d3/file1 1153 * layer 2: allows RW s1d1/s1d2/s1d3 1154 * allows W s1d1/s1d2 1155 * denies R s1d1/s1d2 1156 * layer 3: allows R s1d1 1157 * layer 4: allows R s1d1/s1d2 1158 * denies W s1d1/s1d2 1159 * layer 5: allows R s1d1/s1d2 1160 * layer 6: allows X ---- 1161 * layer 7: allows W s1d1/s1d2 1162 * denies R s1d1/s1d2 1163 */ 1164 const struct rule layer1_read[] = { 1165 /* Allows read access to file1_s1d3 with the first layer. */ 1166 { 1167 .path = file1_s1d3, 1168 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1169 }, 1170 {}, 1171 }; 1172 /* First rule with write restrictions. */ 1173 const struct rule layer2_read_write[] = { 1174 /* Start by granting read-write access via its parent directory... */ 1175 { 1176 .path = dir_s1d3, 1177 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1178 LANDLOCK_ACCESS_FS_WRITE_FILE, 1179 }, 1180 /* ...but also denies read access via its grandparent directory. */ 1181 { 1182 .path = dir_s1d2, 1183 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1184 }, 1185 {}, 1186 }; 1187 const struct rule layer3_read[] = { 1188 /* Allows read access via its great-grandparent directory. */ 1189 { 1190 .path = dir_s1d1, 1191 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1192 }, 1193 {}, 1194 }; 1195 const struct rule layer4_read_write[] = { 1196 /* 1197 * Try to confuse the deny access by denying write (but not 1198 * read) access via its grandparent directory. 1199 */ 1200 { 1201 .path = dir_s1d2, 1202 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1203 }, 1204 {}, 1205 }; 1206 const struct rule layer5_read[] = { 1207 /* 1208 * Try to override layer2's deny read access by explicitly 1209 * allowing read access via file1_s1d3's grandparent. 1210 */ 1211 { 1212 .path = dir_s1d2, 1213 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1214 }, 1215 {}, 1216 }; 1217 const struct rule layer6_execute[] = { 1218 /* 1219 * Restricts an unrelated file hierarchy with a new access 1220 * (non-overlapping) type. 1221 */ 1222 { 1223 .path = dir_s2d1, 1224 .access = LANDLOCK_ACCESS_FS_EXECUTE, 1225 }, 1226 {}, 1227 }; 1228 const struct rule layer7_read_write[] = { 1229 /* 1230 * Finally, denies read access to file1_s1d3 via its 1231 * grandparent. 1232 */ 1233 { 1234 .path = dir_s1d2, 1235 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1236 }, 1237 {}, 1238 }; 1239 int ruleset_fd; 1240 1241 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1242 layer1_read); 1243 ASSERT_LE(0, ruleset_fd); 1244 enforce_ruleset(_metadata, ruleset_fd); 1245 ASSERT_EQ(0, close(ruleset_fd)); 1246 1247 /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1248 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1249 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1250 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1251 1252 ruleset_fd = create_ruleset(_metadata, 1253 LANDLOCK_ACCESS_FS_READ_FILE | 1254 LANDLOCK_ACCESS_FS_WRITE_FILE, 1255 layer2_read_write); 1256 ASSERT_LE(0, ruleset_fd); 1257 enforce_ruleset(_metadata, ruleset_fd); 1258 ASSERT_EQ(0, close(ruleset_fd)); 1259 1260 /* Checks that previous access rights are unchanged with layer 2. */ 1261 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1262 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1263 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1264 1265 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1266 layer3_read); 1267 ASSERT_LE(0, ruleset_fd); 1268 enforce_ruleset(_metadata, ruleset_fd); 1269 ASSERT_EQ(0, close(ruleset_fd)); 1270 1271 /* Checks that previous access rights are unchanged with layer 3. */ 1272 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1273 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1274 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1275 1276 /* This time, denies write access for the file hierarchy. */ 1277 ruleset_fd = create_ruleset(_metadata, 1278 LANDLOCK_ACCESS_FS_READ_FILE | 1279 LANDLOCK_ACCESS_FS_WRITE_FILE, 1280 layer4_read_write); 1281 ASSERT_LE(0, ruleset_fd); 1282 enforce_ruleset(_metadata, ruleset_fd); 1283 ASSERT_EQ(0, close(ruleset_fd)); 1284 1285 /* 1286 * Checks that the only change with layer 4 is that write access is 1287 * denied. 1288 */ 1289 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1290 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1291 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1292 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1293 1294 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1295 layer5_read); 1296 ASSERT_LE(0, ruleset_fd); 1297 enforce_ruleset(_metadata, ruleset_fd); 1298 ASSERT_EQ(0, close(ruleset_fd)); 1299 1300 /* Checks that previous access rights are unchanged with layer 5. */ 1301 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1302 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1303 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1304 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1305 1306 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 1307 layer6_execute); 1308 ASSERT_LE(0, ruleset_fd); 1309 enforce_ruleset(_metadata, ruleset_fd); 1310 ASSERT_EQ(0, close(ruleset_fd)); 1311 1312 /* Checks that previous access rights are unchanged with layer 6. */ 1313 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1314 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1315 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1316 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1317 1318 ruleset_fd = create_ruleset(_metadata, 1319 LANDLOCK_ACCESS_FS_READ_FILE | 1320 LANDLOCK_ACCESS_FS_WRITE_FILE, 1321 layer7_read_write); 1322 ASSERT_LE(0, ruleset_fd); 1323 enforce_ruleset(_metadata, ruleset_fd); 1324 ASSERT_EQ(0, close(ruleset_fd)); 1325 1326 /* Checks read access is now denied with layer 7. */ 1327 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1328 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1329 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1330 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1331 } 1332 1333 TEST_F_FORK(layout1, inherit_subset) 1334 { 1335 const struct rule rules[] = { 1336 { 1337 .path = dir_s1d2, 1338 .access = LANDLOCK_ACCESS_FS_READ_FILE | 1339 LANDLOCK_ACCESS_FS_READ_DIR, 1340 }, 1341 {}, 1342 }; 1343 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1344 1345 ASSERT_LE(0, ruleset_fd); 1346 enforce_ruleset(_metadata, ruleset_fd); 1347 1348 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1349 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1350 1351 /* Write access is forbidden. */ 1352 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1353 /* Readdir access is allowed. */ 1354 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1355 1356 /* Write access is forbidden. */ 1357 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1358 /* Readdir access is allowed. */ 1359 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1360 1361 /* 1362 * Tests shared rule extension: the following rules should not grant 1363 * any new access, only remove some. Once enforced, these rules are 1364 * ANDed with the previous ones. 1365 */ 1366 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1367 dir_s1d2); 1368 /* 1369 * According to ruleset_fd, dir_s1d2 should now have the 1370 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1371 * access rights (even if this directory is opened a second time). 1372 * However, when enforcing this updated ruleset, the ruleset tied to 1373 * the current process (i.e. its domain) will still only have the 1374 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1375 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1376 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1377 * be a privilege escalation. 1378 */ 1379 enforce_ruleset(_metadata, ruleset_fd); 1380 1381 /* Same tests and results as above. */ 1382 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1383 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1384 1385 /* It is still forbidden to write in file1_s1d2. */ 1386 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1387 /* Readdir access is still allowed. */ 1388 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1389 1390 /* It is still forbidden to write in file1_s1d3. */ 1391 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1392 /* Readdir access is still allowed. */ 1393 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1394 1395 /* 1396 * Try to get more privileges by adding new access rights to the parent 1397 * directory: dir_s1d1. 1398 */ 1399 add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 1400 enforce_ruleset(_metadata, ruleset_fd); 1401 1402 /* Same tests and results as above. */ 1403 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1404 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1405 1406 /* It is still forbidden to write in file1_s1d2. */ 1407 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1408 /* Readdir access is still allowed. */ 1409 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1410 1411 /* It is still forbidden to write in file1_s1d3. */ 1412 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1413 /* Readdir access is still allowed. */ 1414 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1415 1416 /* 1417 * Now, dir_s1d3 get a new rule tied to it, only allowing 1418 * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1419 * that there was no rule tied to it before. 1420 */ 1421 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1422 dir_s1d3); 1423 enforce_ruleset(_metadata, ruleset_fd); 1424 ASSERT_EQ(0, close(ruleset_fd)); 1425 1426 /* 1427 * Same tests and results as above, except for open(dir_s1d3) which is 1428 * now denied because the new rule mask the rule previously inherited 1429 * from dir_s1d2. 1430 */ 1431 1432 /* Same tests and results as above. */ 1433 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1434 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1435 1436 /* It is still forbidden to write in file1_s1d2. */ 1437 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1438 /* Readdir access is still allowed. */ 1439 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1440 1441 /* It is still forbidden to write in file1_s1d3. */ 1442 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1443 /* 1444 * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1445 * the same layer. 1446 */ 1447 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1448 } 1449 1450 TEST_F_FORK(layout1, inherit_superset) 1451 { 1452 const struct rule rules[] = { 1453 { 1454 .path = dir_s1d3, 1455 .access = ACCESS_RO, 1456 }, 1457 {}, 1458 }; 1459 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1460 1461 ASSERT_LE(0, ruleset_fd); 1462 enforce_ruleset(_metadata, ruleset_fd); 1463 1464 /* Readdir access is denied for dir_s1d2. */ 1465 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1466 /* Readdir access is allowed for dir_s1d3. */ 1467 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1468 /* File access is allowed for file1_s1d3. */ 1469 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1470 1471 /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1472 add_path_beneath(_metadata, ruleset_fd, 1473 LANDLOCK_ACCESS_FS_READ_FILE | 1474 LANDLOCK_ACCESS_FS_READ_DIR, 1475 dir_s1d2); 1476 enforce_ruleset(_metadata, ruleset_fd); 1477 ASSERT_EQ(0, close(ruleset_fd)); 1478 1479 /* Readdir access is still denied for dir_s1d2. */ 1480 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1481 /* Readdir access is still allowed for dir_s1d3. */ 1482 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1483 /* File access is still allowed for file1_s1d3. */ 1484 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1485 } 1486 1487 TEST_F_FORK(layout0, max_layers) 1488 { 1489 int i, err; 1490 const struct rule rules[] = { 1491 { 1492 .path = TMP_DIR, 1493 .access = ACCESS_RO, 1494 }, 1495 {}, 1496 }; 1497 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1498 1499 ASSERT_LE(0, ruleset_fd); 1500 for (i = 0; i < 16; i++) 1501 enforce_ruleset(_metadata, ruleset_fd); 1502 1503 for (i = 0; i < 2; i++) { 1504 err = landlock_restrict_self(ruleset_fd, 0); 1505 ASSERT_EQ(-1, err); 1506 ASSERT_EQ(E2BIG, errno); 1507 } 1508 ASSERT_EQ(0, close(ruleset_fd)); 1509 } 1510 1511 TEST_F_FORK(layout1, empty_or_same_ruleset) 1512 { 1513 struct landlock_ruleset_attr ruleset_attr = {}; 1514 int ruleset_fd; 1515 1516 /* Tests empty handled_access_fs. */ 1517 ruleset_fd = 1518 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1519 ASSERT_LE(-1, ruleset_fd); 1520 ASSERT_EQ(ENOMSG, errno); 1521 1522 /* Enforces policy which deny read access to all files. */ 1523 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1524 ruleset_fd = 1525 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1526 ASSERT_LE(0, ruleset_fd); 1527 enforce_ruleset(_metadata, ruleset_fd); 1528 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1529 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1530 1531 /* Nests a policy which deny read access to all directories. */ 1532 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1533 ruleset_fd = 1534 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1535 ASSERT_LE(0, ruleset_fd); 1536 enforce_ruleset(_metadata, ruleset_fd); 1537 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1538 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1539 1540 /* Enforces a second time with the same ruleset. */ 1541 enforce_ruleset(_metadata, ruleset_fd); 1542 ASSERT_EQ(0, close(ruleset_fd)); 1543 } 1544 1545 TEST_F_FORK(layout1, rule_on_mountpoint) 1546 { 1547 const struct rule rules[] = { 1548 { 1549 .path = dir_s1d1, 1550 .access = ACCESS_RO, 1551 }, 1552 { 1553 /* dir_s3d2 is a mount point. */ 1554 .path = dir_s3d2, 1555 .access = ACCESS_RO, 1556 }, 1557 {}, 1558 }; 1559 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1560 1561 ASSERT_LE(0, ruleset_fd); 1562 enforce_ruleset(_metadata, ruleset_fd); 1563 ASSERT_EQ(0, close(ruleset_fd)); 1564 1565 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1566 1567 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1568 1569 ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1570 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1571 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1572 } 1573 1574 TEST_F_FORK(layout1, rule_over_mountpoint) 1575 { 1576 const struct rule rules[] = { 1577 { 1578 .path = dir_s1d1, 1579 .access = ACCESS_RO, 1580 }, 1581 { 1582 /* dir_s3d2 is a mount point. */ 1583 .path = dir_s3d1, 1584 .access = ACCESS_RO, 1585 }, 1586 {}, 1587 }; 1588 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1589 1590 ASSERT_LE(0, ruleset_fd); 1591 enforce_ruleset(_metadata, ruleset_fd); 1592 ASSERT_EQ(0, close(ruleset_fd)); 1593 1594 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1595 1596 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1597 1598 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1599 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1600 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1601 } 1602 1603 /* 1604 * This test verifies that we can apply a landlock rule on the root directory 1605 * (which might require special handling). 1606 */ 1607 TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1608 { 1609 struct rule rules[] = { 1610 { 1611 .path = "/", 1612 .access = ACCESS_RO, 1613 }, 1614 {}, 1615 }; 1616 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1617 1618 ASSERT_LE(0, ruleset_fd); 1619 enforce_ruleset(_metadata, ruleset_fd); 1620 ASSERT_EQ(0, close(ruleset_fd)); 1621 1622 /* Checks allowed access. */ 1623 ASSERT_EQ(0, test_open("/", O_RDONLY)); 1624 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1625 1626 rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1627 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1628 ASSERT_LE(0, ruleset_fd); 1629 enforce_ruleset(_metadata, ruleset_fd); 1630 ASSERT_EQ(0, close(ruleset_fd)); 1631 1632 /* Checks denied access (on a directory). */ 1633 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1634 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1635 } 1636 1637 TEST_F_FORK(layout1, rule_over_root_deny) 1638 { 1639 const struct rule rules[] = { 1640 { 1641 .path = "/", 1642 .access = LANDLOCK_ACCESS_FS_READ_FILE, 1643 }, 1644 {}, 1645 }; 1646 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1647 1648 ASSERT_LE(0, ruleset_fd); 1649 enforce_ruleset(_metadata, ruleset_fd); 1650 ASSERT_EQ(0, close(ruleset_fd)); 1651 1652 /* Checks denied access (on a directory). */ 1653 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1654 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1655 } 1656 1657 TEST_F_FORK(layout1, rule_inside_mount_ns) 1658 { 1659 const struct rule rules[] = { 1660 { 1661 .path = "s3d3", 1662 .access = ACCESS_RO, 1663 }, 1664 {}, 1665 }; 1666 int ruleset_fd; 1667 1668 set_cap(_metadata, CAP_SYS_ADMIN); 1669 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1670 { 1671 TH_LOG("Failed to pivot root: %s", strerror(errno)); 1672 }; 1673 ASSERT_EQ(0, chdir("/")); 1674 clear_cap(_metadata, CAP_SYS_ADMIN); 1675 1676 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1677 ASSERT_LE(0, ruleset_fd); 1678 enforce_ruleset(_metadata, ruleset_fd); 1679 ASSERT_EQ(0, close(ruleset_fd)); 1680 1681 ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1682 ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1683 } 1684 1685 TEST_F_FORK(layout1, mount_and_pivot) 1686 { 1687 const struct rule rules[] = { 1688 { 1689 .path = dir_s3d2, 1690 .access = ACCESS_RO, 1691 }, 1692 {}, 1693 }; 1694 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1695 1696 ASSERT_LE(0, ruleset_fd); 1697 enforce_ruleset(_metadata, ruleset_fd); 1698 ASSERT_EQ(0, close(ruleset_fd)); 1699 1700 set_cap(_metadata, CAP_SYS_ADMIN); 1701 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1702 ASSERT_EQ(EPERM, errno); 1703 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1704 ASSERT_EQ(EPERM, errno); 1705 clear_cap(_metadata, CAP_SYS_ADMIN); 1706 } 1707 1708 TEST_F_FORK(layout1, move_mount) 1709 { 1710 const struct rule rules[] = { 1711 { 1712 .path = dir_s3d2, 1713 .access = ACCESS_RO, 1714 }, 1715 {}, 1716 }; 1717 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1718 1719 ASSERT_LE(0, ruleset_fd); 1720 1721 set_cap(_metadata, CAP_SYS_ADMIN); 1722 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1723 dir_s1d2, 0)) 1724 { 1725 TH_LOG("Failed to move mount: %s", strerror(errno)); 1726 } 1727 1728 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1729 dir_s3d2, 0)); 1730 clear_cap(_metadata, CAP_SYS_ADMIN); 1731 1732 enforce_ruleset(_metadata, ruleset_fd); 1733 ASSERT_EQ(0, close(ruleset_fd)); 1734 1735 set_cap(_metadata, CAP_SYS_ADMIN); 1736 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1737 dir_s1d2, 0)); 1738 ASSERT_EQ(EPERM, errno); 1739 clear_cap(_metadata, CAP_SYS_ADMIN); 1740 } 1741 1742 TEST_F_FORK(layout1, topology_changes_with_net_only) 1743 { 1744 const struct landlock_ruleset_attr ruleset_net = { 1745 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 1746 LANDLOCK_ACCESS_NET_CONNECT_TCP, 1747 }; 1748 int ruleset_fd; 1749 1750 /* Add network restrictions. */ 1751 ruleset_fd = 1752 landlock_create_ruleset(&ruleset_net, sizeof(ruleset_net), 0); 1753 ASSERT_LE(0, ruleset_fd); 1754 enforce_ruleset(_metadata, ruleset_fd); 1755 ASSERT_EQ(0, close(ruleset_fd)); 1756 1757 /* Mount, remount, move_mount, umount, and pivot_root checks. */ 1758 set_cap(_metadata, CAP_SYS_ADMIN); 1759 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s1d2)); 1760 ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC, NULL)); 1761 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1762 dir_s2d2, 0)); 1763 ASSERT_EQ(0, umount(dir_s2d2)); 1764 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1765 ASSERT_EQ(0, chdir("/")); 1766 clear_cap(_metadata, CAP_SYS_ADMIN); 1767 } 1768 1769 TEST_F_FORK(layout1, topology_changes_with_net_and_fs) 1770 { 1771 const struct landlock_ruleset_attr ruleset_net_fs = { 1772 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | 1773 LANDLOCK_ACCESS_NET_CONNECT_TCP, 1774 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 1775 }; 1776 int ruleset_fd; 1777 1778 /* Add network and filesystem restrictions. */ 1779 ruleset_fd = landlock_create_ruleset(&ruleset_net_fs, 1780 sizeof(ruleset_net_fs), 0); 1781 ASSERT_LE(0, ruleset_fd); 1782 enforce_ruleset(_metadata, ruleset_fd); 1783 ASSERT_EQ(0, close(ruleset_fd)); 1784 1785 /* Mount, remount, move_mount, umount, and pivot_root checks. */ 1786 set_cap(_metadata, CAP_SYS_ADMIN); 1787 ASSERT_EQ(-1, mount_opt(&mnt_tmp, dir_s1d2)); 1788 ASSERT_EQ(EPERM, errno); 1789 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC, NULL)); 1790 ASSERT_EQ(EPERM, errno); 1791 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1792 dir_s2d2, 0)); 1793 ASSERT_EQ(EPERM, errno); 1794 ASSERT_EQ(-1, umount(dir_s3d2)); 1795 ASSERT_EQ(EPERM, errno); 1796 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1797 ASSERT_EQ(EPERM, errno); 1798 clear_cap(_metadata, CAP_SYS_ADMIN); 1799 } 1800 1801 TEST_F_FORK(layout1, release_inodes) 1802 { 1803 const struct rule rules[] = { 1804 { 1805 .path = dir_s1d1, 1806 .access = ACCESS_RO, 1807 }, 1808 { 1809 .path = dir_s3d2, 1810 .access = ACCESS_RO, 1811 }, 1812 { 1813 .path = dir_s3d3, 1814 .access = ACCESS_RO, 1815 }, 1816 {}, 1817 }; 1818 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1819 1820 ASSERT_LE(0, ruleset_fd); 1821 /* Unmount a file hierarchy while it is being used by a ruleset. */ 1822 set_cap(_metadata, CAP_SYS_ADMIN); 1823 ASSERT_EQ(0, umount(dir_s3d2)); 1824 clear_cap(_metadata, CAP_SYS_ADMIN); 1825 1826 enforce_ruleset(_metadata, ruleset_fd); 1827 ASSERT_EQ(0, close(ruleset_fd)); 1828 1829 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1830 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1831 /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1832 ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1833 } 1834 1835 /* 1836 * This test checks that a rule on a directory used as a mount point does not 1837 * grant access to the mount covering it. It is a generalization of the bind 1838 * mount case in layout3_fs.hostfs.release_inodes that tests hidden mount points. 1839 */ 1840 TEST_F_FORK(layout1, covered_rule) 1841 { 1842 const struct rule layer1[] = { 1843 { 1844 .path = dir_s3d2, 1845 .access = LANDLOCK_ACCESS_FS_READ_DIR, 1846 }, 1847 {}, 1848 }; 1849 int ruleset_fd; 1850 1851 /* Unmount to simplify FIXTURE_TEARDOWN. */ 1852 set_cap(_metadata, CAP_SYS_ADMIN); 1853 ASSERT_EQ(0, umount(dir_s3d2)); 1854 clear_cap(_metadata, CAP_SYS_ADMIN); 1855 1856 /* Creates a ruleset with the future hidden directory. */ 1857 ruleset_fd = 1858 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 1859 ASSERT_LE(0, ruleset_fd); 1860 1861 /* Covers with a new mount point. */ 1862 set_cap(_metadata, CAP_SYS_ADMIN); 1863 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 1864 clear_cap(_metadata, CAP_SYS_ADMIN); 1865 1866 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1867 1868 enforce_ruleset(_metadata, ruleset_fd); 1869 ASSERT_EQ(0, close(ruleset_fd)); 1870 1871 /* Checks that access to the new mount point is denied. */ 1872 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1873 } 1874 1875 enum relative_access { 1876 REL_OPEN, 1877 REL_CHDIR, 1878 REL_CHROOT_ONLY, 1879 REL_CHROOT_CHDIR, 1880 }; 1881 1882 static void test_relative_path(struct __test_metadata *const _metadata, 1883 const enum relative_access rel) 1884 { 1885 /* 1886 * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1887 * is not a disconnected root directory). 1888 */ 1889 const struct rule layer1_base[] = { 1890 { 1891 .path = TMP_DIR, 1892 .access = ACCESS_RO, 1893 }, 1894 {}, 1895 }; 1896 const struct rule layer2_subs[] = { 1897 { 1898 .path = dir_s1d2, 1899 .access = ACCESS_RO, 1900 }, 1901 { 1902 .path = dir_s2d2, 1903 .access = ACCESS_RO, 1904 }, 1905 {}, 1906 }; 1907 int dirfd, ruleset_fd; 1908 1909 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1910 ASSERT_LE(0, ruleset_fd); 1911 enforce_ruleset(_metadata, ruleset_fd); 1912 ASSERT_EQ(0, close(ruleset_fd)); 1913 1914 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1915 1916 ASSERT_LE(0, ruleset_fd); 1917 switch (rel) { 1918 case REL_OPEN: 1919 case REL_CHDIR: 1920 break; 1921 case REL_CHROOT_ONLY: 1922 ASSERT_EQ(0, chdir(dir_s2d2)); 1923 break; 1924 case REL_CHROOT_CHDIR: 1925 ASSERT_EQ(0, chdir(dir_s1d2)); 1926 break; 1927 default: 1928 ASSERT_TRUE(false); 1929 return; 1930 } 1931 1932 set_cap(_metadata, CAP_SYS_CHROOT); 1933 enforce_ruleset(_metadata, ruleset_fd); 1934 1935 switch (rel) { 1936 case REL_OPEN: 1937 dirfd = open(dir_s1d2, O_DIRECTORY); 1938 ASSERT_LE(0, dirfd); 1939 break; 1940 case REL_CHDIR: 1941 ASSERT_EQ(0, chdir(dir_s1d2)); 1942 dirfd = AT_FDCWD; 1943 break; 1944 case REL_CHROOT_ONLY: 1945 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1946 ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 1947 { 1948 TH_LOG("Failed to chroot: %s", strerror(errno)); 1949 } 1950 dirfd = AT_FDCWD; 1951 break; 1952 case REL_CHROOT_CHDIR: 1953 /* Do chroot into dir_s1d2. */ 1954 ASSERT_EQ(0, chroot(".")) 1955 { 1956 TH_LOG("Failed to chroot: %s", strerror(errno)); 1957 } 1958 dirfd = AT_FDCWD; 1959 break; 1960 } 1961 1962 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1963 test_open_rel(dirfd, "..", O_RDONLY)); 1964 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1965 1966 if (rel == REL_CHROOT_ONLY) { 1967 /* The current directory is dir_s2d2. */ 1968 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1969 } else { 1970 /* The current directory is dir_s1d2. */ 1971 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1972 } 1973 1974 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1975 /* Checks the root dir_s1d2. */ 1976 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1977 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1978 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1979 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1980 } 1981 1982 if (rel != REL_CHROOT_CHDIR) { 1983 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1984 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1985 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1986 O_RDONLY)); 1987 1988 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1989 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1990 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1991 O_RDONLY)); 1992 } 1993 1994 if (rel == REL_OPEN) 1995 ASSERT_EQ(0, close(dirfd)); 1996 ASSERT_EQ(0, close(ruleset_fd)); 1997 } 1998 1999 TEST_F_FORK(layout1, relative_open) 2000 { 2001 test_relative_path(_metadata, REL_OPEN); 2002 } 2003 2004 TEST_F_FORK(layout1, relative_chdir) 2005 { 2006 test_relative_path(_metadata, REL_CHDIR); 2007 } 2008 2009 TEST_F_FORK(layout1, relative_chroot_only) 2010 { 2011 test_relative_path(_metadata, REL_CHROOT_ONLY); 2012 } 2013 2014 TEST_F_FORK(layout1, relative_chroot_chdir) 2015 { 2016 test_relative_path(_metadata, REL_CHROOT_CHDIR); 2017 } 2018 2019 static void copy_file(struct __test_metadata *const _metadata, 2020 const char *const src_path, const char *const dst_path) 2021 { 2022 int dst_fd, src_fd; 2023 struct stat statbuf; 2024 2025 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 2026 ASSERT_LE(0, dst_fd) 2027 { 2028 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 2029 } 2030 src_fd = open(src_path, O_RDONLY | O_CLOEXEC); 2031 ASSERT_LE(0, src_fd) 2032 { 2033 TH_LOG("Failed to open \"%s\": %s", src_path, strerror(errno)); 2034 } 2035 ASSERT_EQ(0, fstat(src_fd, &statbuf)); 2036 ASSERT_EQ(statbuf.st_size, 2037 sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 2038 ASSERT_EQ(0, close(src_fd)); 2039 ASSERT_EQ(0, close(dst_fd)); 2040 } 2041 2042 static void test_execute(struct __test_metadata *const _metadata, const int err, 2043 const char *const path) 2044 { 2045 int status; 2046 char *const argv[] = { (char *)path, NULL }; 2047 const pid_t child = fork(); 2048 2049 ASSERT_LE(0, child); 2050 if (child == 0) { 2051 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 2052 { 2053 TH_LOG("Failed to execute \"%s\": %s", path, 2054 strerror(errno)); 2055 }; 2056 ASSERT_EQ(err, errno); 2057 _exit(__test_passed(_metadata) ? 2 : 1); 2058 return; 2059 } 2060 ASSERT_EQ(child, waitpid(child, &status, 0)); 2061 ASSERT_EQ(1, WIFEXITED(status)); 2062 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 2063 { 2064 TH_LOG("Unexpected return code for \"%s\"", path); 2065 }; 2066 } 2067 2068 static void test_check_exec(struct __test_metadata *const _metadata, 2069 const int err, const char *const path) 2070 { 2071 int ret; 2072 char *const argv[] = { (char *)path, NULL }; 2073 2074 ret = sys_execveat(AT_FDCWD, path, argv, NULL, 2075 AT_EMPTY_PATH | AT_EXECVE_CHECK); 2076 if (err) { 2077 EXPECT_EQ(-1, ret); 2078 EXPECT_EQ(errno, err); 2079 } else { 2080 EXPECT_EQ(0, ret); 2081 } 2082 } 2083 2084 TEST_F_FORK(layout1, execute) 2085 { 2086 const struct rule rules[] = { 2087 { 2088 .path = dir_s1d2, 2089 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2090 }, 2091 {}, 2092 }; 2093 const int ruleset_fd = 2094 create_ruleset(_metadata, rules[0].access, rules); 2095 2096 ASSERT_LE(0, ruleset_fd); 2097 copy_file(_metadata, bin_true, file1_s1d1); 2098 copy_file(_metadata, bin_true, file1_s1d2); 2099 copy_file(_metadata, bin_true, file1_s1d3); 2100 2101 /* Checks before file1_s1d1 being denied. */ 2102 test_execute(_metadata, 0, file1_s1d1); 2103 test_check_exec(_metadata, 0, file1_s1d1); 2104 2105 enforce_ruleset(_metadata, ruleset_fd); 2106 ASSERT_EQ(0, close(ruleset_fd)); 2107 2108 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 2109 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2110 test_execute(_metadata, EACCES, file1_s1d1); 2111 test_check_exec(_metadata, EACCES, file1_s1d1); 2112 2113 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 2114 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2115 test_execute(_metadata, 0, file1_s1d2); 2116 test_check_exec(_metadata, 0, file1_s1d2); 2117 2118 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 2119 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2120 test_execute(_metadata, 0, file1_s1d3); 2121 test_check_exec(_metadata, 0, file1_s1d3); 2122 } 2123 2124 TEST_F_FORK(layout1, umount_sandboxer) 2125 { 2126 int pipe_child[2], pipe_parent[2]; 2127 char buf_parent; 2128 pid_t child; 2129 int status; 2130 2131 copy_file(_metadata, bin_sandbox_and_launch, file1_s3d3); 2132 ASSERT_EQ(0, pipe2(pipe_child, 0)); 2133 ASSERT_EQ(0, pipe2(pipe_parent, 0)); 2134 2135 child = fork(); 2136 ASSERT_LE(0, child); 2137 if (child == 0) { 2138 char pipe_child_str[12], pipe_parent_str[12]; 2139 char *const argv[] = { (char *)file1_s3d3, 2140 (char *)bin_wait_pipe, pipe_child_str, 2141 pipe_parent_str, NULL }; 2142 2143 /* Passes the pipe FDs to the executed binary and its child. */ 2144 EXPECT_EQ(0, close(pipe_child[0])); 2145 EXPECT_EQ(0, close(pipe_parent[1])); 2146 snprintf(pipe_child_str, sizeof(pipe_child_str), "%d", 2147 pipe_child[1]); 2148 snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d", 2149 pipe_parent[0]); 2150 2151 /* 2152 * We need bin_sandbox_and_launch (copied inside the mount as 2153 * file1_s3d3) to execute bin_wait_pipe (outside the mount) to 2154 * make sure the mount point will not be EBUSY because of 2155 * file1_s3d3 being in use. This avoids a potential race 2156 * condition between the following read() and umount() calls. 2157 */ 2158 ASSERT_EQ(0, execve(argv[0], argv, NULL)) 2159 { 2160 TH_LOG("Failed to execute \"%s\": %s", argv[0], 2161 strerror(errno)); 2162 }; 2163 _exit(1); 2164 return; 2165 } 2166 2167 EXPECT_EQ(0, close(pipe_child[1])); 2168 EXPECT_EQ(0, close(pipe_parent[0])); 2169 2170 /* Waits for the child to sandbox itself. */ 2171 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2172 2173 /* Tests that the sandboxer is tied to its mount point. */ 2174 set_cap(_metadata, CAP_SYS_ADMIN); 2175 EXPECT_EQ(-1, umount(dir_s3d2)); 2176 EXPECT_EQ(EBUSY, errno); 2177 clear_cap(_metadata, CAP_SYS_ADMIN); 2178 2179 /* Signals the child to launch a grandchild. */ 2180 EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2181 2182 /* Waits for the grandchild. */ 2183 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 2184 2185 /* Tests that the domain's sandboxer is not tied to its mount point. */ 2186 set_cap(_metadata, CAP_SYS_ADMIN); 2187 EXPECT_EQ(0, umount(dir_s3d2)) 2188 { 2189 TH_LOG("Failed to umount \"%s\": %s", dir_s3d2, 2190 strerror(errno)); 2191 }; 2192 clear_cap(_metadata, CAP_SYS_ADMIN); 2193 2194 /* Signals the grandchild to terminate. */ 2195 EXPECT_EQ(1, write(pipe_parent[1], ".", 1)); 2196 ASSERT_EQ(child, waitpid(child, &status, 0)); 2197 ASSERT_EQ(1, WIFEXITED(status)); 2198 ASSERT_EQ(0, WEXITSTATUS(status)); 2199 } 2200 2201 TEST_F_FORK(layout1, link) 2202 { 2203 const struct rule layer1[] = { 2204 { 2205 .path = dir_s1d2, 2206 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2207 }, 2208 {}, 2209 }; 2210 const struct rule layer2[] = { 2211 { 2212 .path = dir_s1d3, 2213 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2214 }, 2215 {}, 2216 }; 2217 int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 2218 2219 ASSERT_LE(0, ruleset_fd); 2220 2221 ASSERT_EQ(0, unlink(file1_s1d1)); 2222 ASSERT_EQ(0, unlink(file1_s1d2)); 2223 ASSERT_EQ(0, unlink(file1_s1d3)); 2224 2225 enforce_ruleset(_metadata, ruleset_fd); 2226 ASSERT_EQ(0, close(ruleset_fd)); 2227 2228 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2229 ASSERT_EQ(EACCES, errno); 2230 2231 /* Denies linking because of reparenting. */ 2232 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2233 ASSERT_EQ(EXDEV, errno); 2234 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2235 ASSERT_EQ(EXDEV, errno); 2236 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2237 ASSERT_EQ(EXDEV, errno); 2238 2239 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2240 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2241 2242 /* Prepares for next unlinks. */ 2243 ASSERT_EQ(0, unlink(file2_s1d2)); 2244 ASSERT_EQ(0, unlink(file2_s1d3)); 2245 2246 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 2247 ASSERT_LE(0, ruleset_fd); 2248 enforce_ruleset(_metadata, ruleset_fd); 2249 ASSERT_EQ(0, close(ruleset_fd)); 2250 2251 /* Checks that linkind doesn't require the ability to delete a file. */ 2252 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 2253 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 2254 } 2255 2256 static int test_rename(const char *const oldpath, const char *const newpath) 2257 { 2258 if (rename(oldpath, newpath)) 2259 return errno; 2260 return 0; 2261 } 2262 2263 static int test_exchange(const char *const oldpath, const char *const newpath) 2264 { 2265 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 2266 return errno; 2267 return 0; 2268 } 2269 2270 static int test_renameat(int olddirfd, const char *oldpath, int newdirfd, 2271 const char *newpath) 2272 { 2273 if (renameat2(olddirfd, oldpath, newdirfd, newpath, 0)) 2274 return errno; 2275 return 0; 2276 } 2277 2278 static int test_exchangeat(int olddirfd, const char *oldpath, int newdirfd, 2279 const char *newpath) 2280 { 2281 if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_EXCHANGE)) 2282 return errno; 2283 return 0; 2284 } 2285 2286 TEST_F_FORK(layout1, rename_file) 2287 { 2288 const struct rule rules[] = { 2289 { 2290 .path = dir_s1d3, 2291 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2292 }, 2293 { 2294 .path = dir_s2d2, 2295 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2296 }, 2297 {}, 2298 }; 2299 const int ruleset_fd = 2300 create_ruleset(_metadata, rules[0].access, rules); 2301 2302 ASSERT_LE(0, ruleset_fd); 2303 2304 ASSERT_EQ(0, unlink(file1_s1d2)); 2305 2306 enforce_ruleset(_metadata, ruleset_fd); 2307 ASSERT_EQ(0, close(ruleset_fd)); 2308 2309 /* 2310 * Tries to replace a file, from a directory that allows file removal, 2311 * but to a different directory (which also allows file removal). 2312 */ 2313 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 2314 ASSERT_EQ(EXDEV, errno); 2315 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 2316 RENAME_EXCHANGE)); 2317 ASSERT_EQ(EXDEV, errno); 2318 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2319 RENAME_EXCHANGE)); 2320 ASSERT_EQ(EXDEV, errno); 2321 2322 /* 2323 * Tries to replace a file, from a directory that denies file removal, 2324 * to a different directory (which allows file removal). 2325 */ 2326 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2327 ASSERT_EQ(EACCES, errno); 2328 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 2329 RENAME_EXCHANGE)); 2330 ASSERT_EQ(EACCES, errno); 2331 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 2332 RENAME_EXCHANGE)); 2333 ASSERT_EQ(EXDEV, errno); 2334 2335 /* Exchanges files and directories that partially allow removal. */ 2336 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 2337 RENAME_EXCHANGE)); 2338 ASSERT_EQ(EACCES, errno); 2339 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 2340 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 2341 ASSERT_EQ(EACCES, errno); 2342 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 2343 RENAME_EXCHANGE)); 2344 ASSERT_EQ(EACCES, errno); 2345 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 2346 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2347 ASSERT_EQ(EACCES, errno); 2348 2349 /* Renames files with different parents. */ 2350 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2351 ASSERT_EQ(EXDEV, errno); 2352 ASSERT_EQ(0, unlink(file1_s1d3)); 2353 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2354 ASSERT_EQ(EACCES, errno); 2355 2356 /* Exchanges and renames files with same parent. */ 2357 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 2358 RENAME_EXCHANGE)); 2359 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 2360 2361 /* Exchanges files and directories with same parent, twice. */ 2362 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2363 RENAME_EXCHANGE)); 2364 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2365 RENAME_EXCHANGE)); 2366 } 2367 2368 TEST_F_FORK(layout1, rename_dir) 2369 { 2370 const struct rule rules[] = { 2371 { 2372 .path = dir_s1d2, 2373 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2374 }, 2375 { 2376 .path = dir_s2d1, 2377 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2378 }, 2379 {}, 2380 }; 2381 const int ruleset_fd = 2382 create_ruleset(_metadata, rules[0].access, rules); 2383 2384 ASSERT_LE(0, ruleset_fd); 2385 2386 /* Empties dir_s1d3 to allow renaming. */ 2387 ASSERT_EQ(0, unlink(file1_s1d3)); 2388 ASSERT_EQ(0, unlink(file2_s1d3)); 2389 2390 enforce_ruleset(_metadata, ruleset_fd); 2391 ASSERT_EQ(0, close(ruleset_fd)); 2392 2393 /* Exchanges and renames directory to a different parent. */ 2394 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2395 RENAME_EXCHANGE)); 2396 ASSERT_EQ(EXDEV, errno); 2397 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 2398 ASSERT_EQ(EXDEV, errno); 2399 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2400 RENAME_EXCHANGE)); 2401 ASSERT_EQ(EXDEV, errno); 2402 2403 /* 2404 * Exchanges directory to the same parent, which doesn't allow 2405 * directory removal. 2406 */ 2407 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 2408 RENAME_EXCHANGE)); 2409 ASSERT_EQ(EACCES, errno); 2410 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 2411 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 2412 ASSERT_EQ(EACCES, errno); 2413 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 2414 RENAME_EXCHANGE)); 2415 ASSERT_EQ(EACCES, errno); 2416 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 2417 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 2418 ASSERT_EQ(EACCES, errno); 2419 2420 /* 2421 * Exchanges and renames directory to the same parent, which allows 2422 * directory removal. 2423 */ 2424 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 2425 RENAME_EXCHANGE)); 2426 ASSERT_EQ(0, unlink(dir_s1d3)); 2427 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2428 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 2429 ASSERT_EQ(0, rmdir(dir_s1d3)); 2430 } 2431 2432 TEST_F_FORK(layout1, reparent_refer) 2433 { 2434 const struct rule layer1[] = { 2435 { 2436 .path = dir_s1d2, 2437 .access = LANDLOCK_ACCESS_FS_REFER, 2438 }, 2439 { 2440 .path = dir_s2d2, 2441 .access = LANDLOCK_ACCESS_FS_REFER, 2442 }, 2443 {}, 2444 }; 2445 int ruleset_fd = 2446 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2447 2448 ASSERT_LE(0, ruleset_fd); 2449 enforce_ruleset(_metadata, ruleset_fd); 2450 ASSERT_EQ(0, close(ruleset_fd)); 2451 2452 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2453 ASSERT_EQ(EXDEV, errno); 2454 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2455 ASSERT_EQ(EXDEV, errno); 2456 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2457 ASSERT_EQ(EXDEV, errno); 2458 2459 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2460 ASSERT_EQ(EXDEV, errno); 2461 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2462 ASSERT_EQ(EXDEV, errno); 2463 /* 2464 * Moving should only be allowed when the source and the destination 2465 * parent directory have REFER. 2466 */ 2467 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2468 ASSERT_EQ(ENOTEMPTY, errno); 2469 ASSERT_EQ(0, unlink(file1_s2d3)); 2470 ASSERT_EQ(0, unlink(file2_s2d3)); 2471 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2472 } 2473 2474 /* Checks renames beneath dir_s1d1. */ 2475 static void refer_denied_by_default(struct __test_metadata *const _metadata, 2476 const struct rule layer1[], 2477 const int layer1_err, 2478 const struct rule layer2[]) 2479 { 2480 int ruleset_fd; 2481 2482 ASSERT_EQ(0, unlink(file1_s1d2)); 2483 2484 ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 2485 ASSERT_LE(0, ruleset_fd); 2486 enforce_ruleset(_metadata, ruleset_fd); 2487 ASSERT_EQ(0, close(ruleset_fd)); 2488 2489 /* 2490 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 2491 * layer1_err), then it allows some different-parent renames and links. 2492 */ 2493 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 2494 if (layer1_err == 0) 2495 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 2496 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 2497 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 2498 2499 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 2500 ASSERT_LE(0, ruleset_fd); 2501 enforce_ruleset(_metadata, ruleset_fd); 2502 ASSERT_EQ(0, close(ruleset_fd)); 2503 2504 /* 2505 * Now, either the first or the second layer does not handle 2506 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 2507 * renames and links are denied, thus making the layer handling 2508 * LANDLOCK_ACCESS_FS_REFER null and void. 2509 */ 2510 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 2511 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 2512 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 2513 } 2514 2515 const struct rule layer_dir_s1d1_refer[] = { 2516 { 2517 .path = dir_s1d1, 2518 .access = LANDLOCK_ACCESS_FS_REFER, 2519 }, 2520 {}, 2521 }; 2522 2523 const struct rule layer_dir_s1d1_execute[] = { 2524 { 2525 /* Matches a parent directory. */ 2526 .path = dir_s1d1, 2527 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2528 }, 2529 {}, 2530 }; 2531 2532 const struct rule layer_dir_s2d1_execute[] = { 2533 { 2534 /* Does not match a parent directory. */ 2535 .path = dir_s2d1, 2536 .access = LANDLOCK_ACCESS_FS_EXECUTE, 2537 }, 2538 {}, 2539 }; 2540 2541 /* 2542 * Tests precedence over renames: denied by default for different parent 2543 * directories, *with* a rule matching a parent directory, but not directly 2544 * denying access (with MAKE_REG nor REMOVE). 2545 */ 2546 TEST_F_FORK(layout1, refer_denied_by_default1) 2547 { 2548 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2549 layer_dir_s1d1_execute); 2550 } 2551 2552 /* 2553 * Same test but this time turning around the ABI version order: the first 2554 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2555 */ 2556 TEST_F_FORK(layout1, refer_denied_by_default2) 2557 { 2558 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 2559 layer_dir_s1d1_refer); 2560 } 2561 2562 /* 2563 * Tests precedence over renames: denied by default for different parent 2564 * directories, *without* a rule matching a parent directory, but not directly 2565 * denying access (with MAKE_REG nor REMOVE). 2566 */ 2567 TEST_F_FORK(layout1, refer_denied_by_default3) 2568 { 2569 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2570 layer_dir_s2d1_execute); 2571 } 2572 2573 /* 2574 * Same test but this time turning around the ABI version order: the first 2575 * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2576 */ 2577 TEST_F_FORK(layout1, refer_denied_by_default4) 2578 { 2579 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 2580 layer_dir_s1d1_refer); 2581 } 2582 2583 /* 2584 * Tests walking through a denied root mount. 2585 */ 2586 TEST_F_FORK(layout1, refer_mount_root_deny) 2587 { 2588 const struct landlock_ruleset_attr ruleset_attr = { 2589 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_DIR, 2590 }; 2591 int root_fd, ruleset_fd; 2592 2593 /* Creates a mount object from a non-mount point. */ 2594 set_cap(_metadata, CAP_SYS_ADMIN); 2595 root_fd = 2596 open_tree(AT_FDCWD, dir_s1d1, 2597 AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); 2598 clear_cap(_metadata, CAP_SYS_ADMIN); 2599 ASSERT_LE(0, root_fd); 2600 2601 ruleset_fd = 2602 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 2603 ASSERT_LE(0, ruleset_fd); 2604 2605 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 2606 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 2607 EXPECT_EQ(0, close(ruleset_fd)); 2608 2609 /* Link denied by Landlock: EACCES. */ 2610 EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0)); 2611 EXPECT_EQ(EACCES, errno); 2612 2613 /* renameat2() always returns EBUSY. */ 2614 EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0)); 2615 EXPECT_EQ(EBUSY, errno); 2616 2617 EXPECT_EQ(0, close(root_fd)); 2618 } 2619 2620 TEST_F_FORK(layout1, refer_part_mount_tree_is_allowed) 2621 { 2622 const struct rule layer1[] = { 2623 { 2624 /* Parent mount point. */ 2625 .path = dir_s3d1, 2626 .access = LANDLOCK_ACCESS_FS_REFER | 2627 LANDLOCK_ACCESS_FS_MAKE_REG, 2628 }, 2629 { 2630 /* 2631 * Removing the source file is allowed because its 2632 * access rights are already a superset of the 2633 * destination. 2634 */ 2635 .path = dir_s3d4, 2636 .access = LANDLOCK_ACCESS_FS_REFER | 2637 LANDLOCK_ACCESS_FS_MAKE_REG | 2638 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2639 }, 2640 {}, 2641 }; 2642 int ruleset_fd; 2643 2644 ASSERT_EQ(0, unlink(file1_s3d3)); 2645 ruleset_fd = create_ruleset(_metadata, 2646 LANDLOCK_ACCESS_FS_REFER | 2647 LANDLOCK_ACCESS_FS_MAKE_REG | 2648 LANDLOCK_ACCESS_FS_REMOVE_FILE, 2649 layer1); 2650 2651 ASSERT_LE(0, ruleset_fd); 2652 enforce_ruleset(_metadata, ruleset_fd); 2653 ASSERT_EQ(0, close(ruleset_fd)); 2654 2655 ASSERT_EQ(0, rename(file1_s3d4, file1_s3d3)); 2656 } 2657 2658 TEST_F_FORK(layout1, reparent_link) 2659 { 2660 const struct rule layer1[] = { 2661 { 2662 .path = dir_s1d2, 2663 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2664 }, 2665 { 2666 .path = dir_s1d3, 2667 .access = LANDLOCK_ACCESS_FS_REFER, 2668 }, 2669 { 2670 .path = dir_s2d2, 2671 .access = LANDLOCK_ACCESS_FS_REFER, 2672 }, 2673 { 2674 .path = dir_s2d3, 2675 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2676 }, 2677 {}, 2678 }; 2679 const int ruleset_fd = create_ruleset( 2680 _metadata, 2681 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2682 2683 ASSERT_LE(0, ruleset_fd); 2684 enforce_ruleset(_metadata, ruleset_fd); 2685 ASSERT_EQ(0, close(ruleset_fd)); 2686 2687 ASSERT_EQ(0, unlink(file1_s1d1)); 2688 ASSERT_EQ(0, unlink(file1_s1d2)); 2689 ASSERT_EQ(0, unlink(file1_s1d3)); 2690 2691 /* Denies linking because of missing MAKE_REG. */ 2692 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2693 ASSERT_EQ(EACCES, errno); 2694 /* Denies linking because of missing source and destination REFER. */ 2695 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2696 ASSERT_EQ(EXDEV, errno); 2697 /* Denies linking because of missing source REFER. */ 2698 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2699 ASSERT_EQ(EXDEV, errno); 2700 2701 /* Denies linking because of missing MAKE_REG. */ 2702 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2703 ASSERT_EQ(EACCES, errno); 2704 /* Denies linking because of missing destination REFER. */ 2705 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2706 ASSERT_EQ(EXDEV, errno); 2707 2708 /* Allows linking because of REFER and MAKE_REG. */ 2709 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2710 ASSERT_EQ(0, unlink(file1_s2d2)); 2711 /* Reverse linking denied because of missing MAKE_REG. */ 2712 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2713 ASSERT_EQ(EACCES, errno); 2714 ASSERT_EQ(0, unlink(file1_s2d3)); 2715 /* Checks reverse linking. */ 2716 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2717 ASSERT_EQ(0, unlink(file1_s1d3)); 2718 2719 /* 2720 * This is OK for a file link, but it should not be allowed for a 2721 * directory rename (because of the superset of access rights. 2722 */ 2723 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2724 ASSERT_EQ(0, unlink(file1_s1d3)); 2725 2726 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2727 ASSERT_EQ(EXDEV, errno); 2728 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2729 ASSERT_EQ(EXDEV, errno); 2730 2731 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2732 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2733 } 2734 2735 TEST_F_FORK(layout1, reparent_rename) 2736 { 2737 /* Same rules as for reparent_link. */ 2738 const struct rule layer1[] = { 2739 { 2740 .path = dir_s1d2, 2741 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2742 }, 2743 { 2744 .path = dir_s1d3, 2745 .access = LANDLOCK_ACCESS_FS_REFER, 2746 }, 2747 { 2748 .path = dir_s2d2, 2749 .access = LANDLOCK_ACCESS_FS_REFER, 2750 }, 2751 { 2752 .path = dir_s2d3, 2753 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2754 }, 2755 {}, 2756 }; 2757 const int ruleset_fd = create_ruleset( 2758 _metadata, 2759 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2760 2761 ASSERT_LE(0, ruleset_fd); 2762 enforce_ruleset(_metadata, ruleset_fd); 2763 ASSERT_EQ(0, close(ruleset_fd)); 2764 2765 ASSERT_EQ(0, unlink(file1_s1d2)); 2766 ASSERT_EQ(0, unlink(file1_s1d3)); 2767 2768 /* Denies renaming because of missing MAKE_REG. */ 2769 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2770 RENAME_EXCHANGE)); 2771 ASSERT_EQ(EACCES, errno); 2772 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2773 RENAME_EXCHANGE)); 2774 ASSERT_EQ(EACCES, errno); 2775 ASSERT_EQ(0, unlink(file1_s1d1)); 2776 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2777 ASSERT_EQ(EACCES, errno); 2778 /* Even denies same file exchange. */ 2779 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2780 RENAME_EXCHANGE)); 2781 ASSERT_EQ(EACCES, errno); 2782 2783 /* Denies renaming because of missing source and destination REFER. */ 2784 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2785 ASSERT_EQ(EXDEV, errno); 2786 /* 2787 * Denies renaming because of missing MAKE_REG, source and destination 2788 * REFER. 2789 */ 2790 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2791 RENAME_EXCHANGE)); 2792 ASSERT_EQ(EACCES, errno); 2793 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2794 RENAME_EXCHANGE)); 2795 ASSERT_EQ(EACCES, errno); 2796 2797 /* Denies renaming because of missing source REFER. */ 2798 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2799 ASSERT_EQ(EXDEV, errno); 2800 /* Denies renaming because of missing MAKE_REG. */ 2801 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2802 RENAME_EXCHANGE)); 2803 ASSERT_EQ(EACCES, errno); 2804 2805 /* Denies renaming because of missing MAKE_REG. */ 2806 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2807 ASSERT_EQ(EACCES, errno); 2808 /* Denies renaming because of missing destination REFER*/ 2809 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2810 ASSERT_EQ(EXDEV, errno); 2811 2812 /* Denies exchange because of one missing MAKE_REG. */ 2813 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2814 RENAME_EXCHANGE)); 2815 ASSERT_EQ(EACCES, errno); 2816 /* Allows renaming because of REFER and MAKE_REG. */ 2817 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2818 2819 /* Reverse renaming denied because of missing MAKE_REG. */ 2820 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2821 ASSERT_EQ(EACCES, errno); 2822 ASSERT_EQ(0, unlink(file1_s2d3)); 2823 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2824 2825 /* Tests reverse renaming. */ 2826 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2827 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2828 RENAME_EXCHANGE)); 2829 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2830 2831 /* 2832 * This is OK for a file rename, but it should not be allowed for a 2833 * directory rename (because of the superset of access rights). 2834 */ 2835 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2836 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2837 2838 /* 2839 * Tests superset restrictions applied to directories. Not only the 2840 * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2841 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2842 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2843 * directly by the moved dir_s2d3. 2844 */ 2845 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2846 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2847 /* 2848 * The first rename is allowed but not the exchange because dir_s1d3's 2849 * parent (dir_s1d2) doesn't have REFER. 2850 */ 2851 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2852 RENAME_EXCHANGE)); 2853 ASSERT_EQ(EXDEV, errno); 2854 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2855 RENAME_EXCHANGE)); 2856 ASSERT_EQ(EXDEV, errno); 2857 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2858 ASSERT_EQ(EXDEV, errno); 2859 2860 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2861 ASSERT_EQ(EXDEV, errno); 2862 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2863 ASSERT_EQ(EXDEV, errno); 2864 2865 /* Renaming in the same directory is always allowed. */ 2866 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2867 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2868 2869 ASSERT_EQ(0, unlink(file1_s1d2)); 2870 /* Denies because of missing source MAKE_REG and destination REFER. */ 2871 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2872 ASSERT_EQ(EXDEV, errno); 2873 2874 ASSERT_EQ(0, unlink(file1_s1d3)); 2875 /* Denies because of missing source MAKE_REG and REFER. */ 2876 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2877 ASSERT_EQ(EXDEV, errno); 2878 } 2879 2880 static void 2881 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2882 { 2883 const struct rule layer1[] = { 2884 { 2885 .path = dir_s1d2, 2886 .access = LANDLOCK_ACCESS_FS_REFER, 2887 }, 2888 { 2889 /* Interesting for the layer2 tests. */ 2890 .path = dir_s1d3, 2891 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2892 }, 2893 { 2894 .path = dir_s2d2, 2895 .access = LANDLOCK_ACCESS_FS_REFER, 2896 }, 2897 { 2898 .path = dir_s2d3, 2899 .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2900 }, 2901 {}, 2902 }; 2903 const int ruleset_fd = create_ruleset( 2904 _metadata, 2905 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2906 2907 ASSERT_LE(0, ruleset_fd); 2908 enforce_ruleset(_metadata, ruleset_fd); 2909 ASSERT_EQ(0, close(ruleset_fd)); 2910 } 2911 2912 static void 2913 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2914 { 2915 const struct rule layer2[] = { 2916 { 2917 .path = dir_s2d3, 2918 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2919 }, 2920 {}, 2921 }; 2922 /* 2923 * Same checks as before but with a second layer and a new MAKE_DIR 2924 * rule (and no explicit handling of REFER). 2925 */ 2926 const int ruleset_fd = 2927 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2928 2929 ASSERT_LE(0, ruleset_fd); 2930 enforce_ruleset(_metadata, ruleset_fd); 2931 ASSERT_EQ(0, close(ruleset_fd)); 2932 } 2933 2934 TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2935 { 2936 ASSERT_EQ(0, unlink(file1_s2d2)); 2937 ASSERT_EQ(0, unlink(file1_s2d3)); 2938 2939 reparent_exdev_layers_enforce1(_metadata); 2940 2941 /* 2942 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2943 * because it doesn't inherit new access rights. 2944 */ 2945 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2946 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2947 2948 /* 2949 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2950 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2951 * already allowed for dir_s1d3. 2952 */ 2953 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2954 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2955 2956 /* 2957 * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2958 * because it cannot inherit MAKE_REG right (which is dedicated to 2959 * directories). 2960 */ 2961 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2962 2963 reparent_exdev_layers_enforce2(_metadata); 2964 2965 /* 2966 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2967 * MAKE_DIR is not tied to dir_s2d2. 2968 */ 2969 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2970 ASSERT_EQ(EACCES, errno); 2971 2972 /* 2973 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2974 * would grants MAKE_REG and MAKE_DIR rights to it. 2975 */ 2976 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2977 ASSERT_EQ(EXDEV, errno); 2978 2979 /* 2980 * Moving the file2_s1d3 file below dir_s2d3 is denied because the 2981 * second layer does not handle REFER, which is always denied by 2982 * default. 2983 */ 2984 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 2985 ASSERT_EQ(EXDEV, errno); 2986 } 2987 2988 TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2989 { 2990 reparent_exdev_layers_enforce1(_metadata); 2991 2992 /* Checks EACCES predominance over EXDEV. */ 2993 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2994 ASSERT_EQ(EACCES, errno); 2995 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2996 ASSERT_EQ(EACCES, errno); 2997 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2998 ASSERT_EQ(EXDEV, errno); 2999 /* Modify layout! */ 3000 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 3001 3002 /* Without REFER source. */ 3003 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 3004 ASSERT_EQ(EXDEV, errno); 3005 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 3006 ASSERT_EQ(EXDEV, errno); 3007 3008 reparent_exdev_layers_enforce2(_metadata); 3009 3010 /* Checks EACCES predominance over EXDEV. */ 3011 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 3012 ASSERT_EQ(EACCES, errno); 3013 /* Checks with actual file2_s1d2. */ 3014 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 3015 ASSERT_EQ(EACCES, errno); 3016 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 3017 ASSERT_EQ(EXDEV, errno); 3018 /* 3019 * Modifying the layout is now denied because the second layer does not 3020 * handle REFER, which is always denied by default. 3021 */ 3022 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 3023 ASSERT_EQ(EXDEV, errno); 3024 3025 /* Without REFER source, EACCES wins over EXDEV. */ 3026 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 3027 ASSERT_EQ(EACCES, errno); 3028 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 3029 ASSERT_EQ(EACCES, errno); 3030 } 3031 3032 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 3033 { 3034 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 3035 file2_s2d3; 3036 3037 ASSERT_EQ(0, unlink(file1_s1d2)); 3038 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3039 ASSERT_EQ(0, unlink(file2_s2d3)); 3040 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 3041 3042 reparent_exdev_layers_enforce1(_metadata); 3043 3044 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 3045 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 3046 RENAME_EXCHANGE)); 3047 ASSERT_EQ(EACCES, errno); 3048 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 3049 RENAME_EXCHANGE)); 3050 ASSERT_EQ(EACCES, errno); 3051 3052 /* 3053 * Checks with directories which creation could be allowed, but denied 3054 * because of access rights that would be inherited. 3055 */ 3056 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 3057 dir_file2_s2d3, RENAME_EXCHANGE)); 3058 ASSERT_EQ(EXDEV, errno); 3059 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 3060 dir_file1_s1d2, RENAME_EXCHANGE)); 3061 ASSERT_EQ(EXDEV, errno); 3062 3063 /* Checks with same access rights. */ 3064 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 3065 RENAME_EXCHANGE)); 3066 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 3067 RENAME_EXCHANGE)); 3068 3069 /* Checks with different (child-only) access rights. */ 3070 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 3071 RENAME_EXCHANGE)); 3072 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 3073 RENAME_EXCHANGE)); 3074 3075 /* 3076 * Checks that exchange between file and directory are consistent. 3077 * 3078 * Moving a file (file1_s2d2) to a directory which only grants more 3079 * directory-related access rights is allowed, and at the same time 3080 * moving a directory (dir_file2_s2d3) to another directory which 3081 * grants less access rights is allowed too. 3082 * 3083 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 3084 */ 3085 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 3086 RENAME_EXCHANGE)); 3087 /* 3088 * However, moving back the directory is denied because it would get 3089 * more access rights than the current state and because file creation 3090 * is forbidden (in dir_s2d2). 3091 */ 3092 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3093 RENAME_EXCHANGE)); 3094 ASSERT_EQ(EACCES, errno); 3095 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 3096 RENAME_EXCHANGE)); 3097 ASSERT_EQ(EACCES, errno); 3098 3099 reparent_exdev_layers_enforce2(_metadata); 3100 3101 /* Error predominance with file exchange: returns EXDEV and EACCES. */ 3102 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 3103 RENAME_EXCHANGE)); 3104 ASSERT_EQ(EACCES, errno); 3105 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 3106 RENAME_EXCHANGE)); 3107 ASSERT_EQ(EACCES, errno); 3108 3109 /* Checks with directories which creation is now denied. */ 3110 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 3111 dir_file2_s2d3, RENAME_EXCHANGE)); 3112 ASSERT_EQ(EACCES, errno); 3113 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 3114 dir_file1_s1d2, RENAME_EXCHANGE)); 3115 ASSERT_EQ(EACCES, errno); 3116 3117 /* Checks with different (child-only) access rights. */ 3118 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 3119 RENAME_EXCHANGE)); 3120 /* Denied because of MAKE_DIR. */ 3121 ASSERT_EQ(EACCES, errno); 3122 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 3123 RENAME_EXCHANGE)); 3124 ASSERT_EQ(EACCES, errno); 3125 3126 /* Checks with different (child-only) access rights. */ 3127 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 3128 RENAME_EXCHANGE)); 3129 /* Denied because of MAKE_DIR. */ 3130 ASSERT_EQ(EACCES, errno); 3131 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 3132 RENAME_EXCHANGE)); 3133 ASSERT_EQ(EACCES, errno); 3134 3135 /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 3136 } 3137 3138 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 3139 { 3140 const char *const dir_file2_s2d3 = file2_s2d3; 3141 3142 ASSERT_EQ(0, unlink(file2_s2d3)); 3143 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 3144 3145 reparent_exdev_layers_enforce1(_metadata); 3146 reparent_exdev_layers_enforce2(_metadata); 3147 3148 /* Checks that exchange between file and directory are consistent. */ 3149 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 3150 RENAME_EXCHANGE)); 3151 ASSERT_EQ(EACCES, errno); 3152 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3153 RENAME_EXCHANGE)); 3154 ASSERT_EQ(EACCES, errno); 3155 } 3156 3157 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 3158 { 3159 const char *const dir_file2_s2d3 = file2_s2d3; 3160 3161 ASSERT_EQ(0, unlink(file2_s2d3)); 3162 ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 3163 3164 reparent_exdev_layers_enforce1(_metadata); 3165 3166 /* 3167 * Checks that exchange between file and directory are consistent, 3168 * including with inverted arguments (see 3169 * layout1.reparent_exdev_layers_exchange1). 3170 */ 3171 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3172 RENAME_EXCHANGE)); 3173 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 3174 RENAME_EXCHANGE)); 3175 ASSERT_EQ(EACCES, errno); 3176 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 3177 RENAME_EXCHANGE)); 3178 ASSERT_EQ(EACCES, errno); 3179 } 3180 3181 TEST_F_FORK(layout1, reparent_remove) 3182 { 3183 const struct rule layer1[] = { 3184 { 3185 .path = dir_s1d1, 3186 .access = LANDLOCK_ACCESS_FS_REFER | 3187 LANDLOCK_ACCESS_FS_REMOVE_DIR, 3188 }, 3189 { 3190 .path = dir_s1d2, 3191 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 3192 }, 3193 { 3194 .path = dir_s2d1, 3195 .access = LANDLOCK_ACCESS_FS_REFER | 3196 LANDLOCK_ACCESS_FS_REMOVE_FILE, 3197 }, 3198 {}, 3199 }; 3200 const int ruleset_fd = create_ruleset( 3201 _metadata, 3202 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 3203 LANDLOCK_ACCESS_FS_REMOVE_FILE, 3204 layer1); 3205 3206 ASSERT_LE(0, ruleset_fd); 3207 enforce_ruleset(_metadata, ruleset_fd); 3208 ASSERT_EQ(0, close(ruleset_fd)); 3209 3210 /* Access denied because of wrong/swapped remove file/dir. */ 3211 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 3212 ASSERT_EQ(EACCES, errno); 3213 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 3214 ASSERT_EQ(EACCES, errno); 3215 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 3216 RENAME_EXCHANGE)); 3217 ASSERT_EQ(EACCES, errno); 3218 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 3219 RENAME_EXCHANGE)); 3220 ASSERT_EQ(EACCES, errno); 3221 3222 /* Access allowed thanks to the matching rights. */ 3223 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 3224 ASSERT_EQ(EISDIR, errno); 3225 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 3226 ASSERT_EQ(ENOTDIR, errno); 3227 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 3228 ASSERT_EQ(ENOTDIR, errno); 3229 ASSERT_EQ(0, unlink(file1_s2d1)); 3230 ASSERT_EQ(0, unlink(file1_s1d3)); 3231 ASSERT_EQ(0, unlink(file2_s1d3)); 3232 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 3233 3234 /* Effectively removes a file and a directory by exchanging them. */ 3235 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 3236 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 3237 RENAME_EXCHANGE)); 3238 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 3239 RENAME_EXCHANGE)); 3240 ASSERT_EQ(EACCES, errno); 3241 } 3242 3243 TEST_F_FORK(layout1, reparent_dom_superset) 3244 { 3245 const struct rule layer1[] = { 3246 { 3247 .path = dir_s1d2, 3248 .access = LANDLOCK_ACCESS_FS_REFER, 3249 }, 3250 { 3251 .path = file1_s1d2, 3252 .access = LANDLOCK_ACCESS_FS_EXECUTE, 3253 }, 3254 { 3255 .path = dir_s1d3, 3256 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 3257 LANDLOCK_ACCESS_FS_EXECUTE, 3258 }, 3259 { 3260 .path = dir_s2d2, 3261 .access = LANDLOCK_ACCESS_FS_REFER | 3262 LANDLOCK_ACCESS_FS_EXECUTE | 3263 LANDLOCK_ACCESS_FS_MAKE_SOCK, 3264 }, 3265 { 3266 .path = dir_s2d3, 3267 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3268 LANDLOCK_ACCESS_FS_MAKE_FIFO, 3269 }, 3270 {}, 3271 }; 3272 int ruleset_fd = create_ruleset(_metadata, 3273 LANDLOCK_ACCESS_FS_REFER | 3274 LANDLOCK_ACCESS_FS_EXECUTE | 3275 LANDLOCK_ACCESS_FS_MAKE_SOCK | 3276 LANDLOCK_ACCESS_FS_READ_FILE | 3277 LANDLOCK_ACCESS_FS_MAKE_FIFO, 3278 layer1); 3279 3280 ASSERT_LE(0, ruleset_fd); 3281 enforce_ruleset(_metadata, ruleset_fd); 3282 ASSERT_EQ(0, close(ruleset_fd)); 3283 3284 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 3285 ASSERT_EQ(EXDEV, errno); 3286 /* 3287 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 3288 * access right. 3289 */ 3290 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 3291 ASSERT_EQ(EXDEV, errno); 3292 /* 3293 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 3294 * superset of access rights compared to dir_s1d2, because file1_s1d2 3295 * already has these access rights anyway. 3296 */ 3297 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 3298 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 3299 3300 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 3301 ASSERT_EQ(EXDEV, errno); 3302 /* 3303 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 3304 * right. 3305 */ 3306 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 3307 ASSERT_EQ(EXDEV, errno); 3308 /* 3309 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 3310 * of access rights compared to dir_s1d2, because dir_s1d3 already has 3311 * these access rights anyway. 3312 */ 3313 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 3314 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 3315 3316 /* 3317 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 3318 * will be denied because the new inherited access rights from dir_s1d2 3319 * will be less than the destination (original) dir_s2d3. This is a 3320 * sinkhole scenario where we cannot move back files or directories. 3321 */ 3322 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 3323 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 3324 ASSERT_EQ(EXDEV, errno); 3325 ASSERT_EQ(0, unlink(file2_s1d2)); 3326 ASSERT_EQ(0, unlink(file2_s2d3)); 3327 /* 3328 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 3329 * MAKE_SOCK which were inherited from dir_s1d3. 3330 */ 3331 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 3332 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 3333 ASSERT_EQ(EXDEV, errno); 3334 } 3335 3336 TEST_F_FORK(layout1, remove_dir) 3337 { 3338 const struct rule rules[] = { 3339 { 3340 .path = dir_s1d2, 3341 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 3342 }, 3343 {}, 3344 }; 3345 const int ruleset_fd = 3346 create_ruleset(_metadata, rules[0].access, rules); 3347 3348 ASSERT_LE(0, ruleset_fd); 3349 3350 ASSERT_EQ(0, unlink(file1_s1d1)); 3351 ASSERT_EQ(0, unlink(file1_s1d2)); 3352 ASSERT_EQ(0, unlink(file1_s1d3)); 3353 ASSERT_EQ(0, unlink(file2_s1d3)); 3354 3355 enforce_ruleset(_metadata, ruleset_fd); 3356 ASSERT_EQ(0, close(ruleset_fd)); 3357 3358 ASSERT_EQ(0, rmdir(dir_s1d3)); 3359 ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 3360 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 3361 3362 /* dir_s1d2 itself cannot be removed. */ 3363 ASSERT_EQ(-1, rmdir(dir_s1d2)); 3364 ASSERT_EQ(EACCES, errno); 3365 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 3366 ASSERT_EQ(EACCES, errno); 3367 ASSERT_EQ(-1, rmdir(dir_s1d1)); 3368 ASSERT_EQ(EACCES, errno); 3369 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 3370 ASSERT_EQ(EACCES, errno); 3371 } 3372 3373 TEST_F_FORK(layout1, remove_file) 3374 { 3375 const struct rule rules[] = { 3376 { 3377 .path = dir_s1d2, 3378 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 3379 }, 3380 {}, 3381 }; 3382 const int ruleset_fd = 3383 create_ruleset(_metadata, rules[0].access, rules); 3384 3385 ASSERT_LE(0, ruleset_fd); 3386 enforce_ruleset(_metadata, ruleset_fd); 3387 ASSERT_EQ(0, close(ruleset_fd)); 3388 3389 ASSERT_EQ(-1, unlink(file1_s1d1)); 3390 ASSERT_EQ(EACCES, errno); 3391 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 3392 ASSERT_EQ(EACCES, errno); 3393 ASSERT_EQ(0, unlink(file1_s1d2)); 3394 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 3395 } 3396 3397 static void test_make_file(struct __test_metadata *const _metadata, 3398 const __u64 access, const mode_t mode, 3399 const dev_t dev) 3400 { 3401 const struct rule rules[] = { 3402 { 3403 .path = dir_s1d2, 3404 .access = access, 3405 }, 3406 {}, 3407 }; 3408 const int ruleset_fd = create_ruleset(_metadata, access, rules); 3409 3410 ASSERT_LE(0, ruleset_fd); 3411 3412 ASSERT_EQ(0, unlink(file1_s1d1)); 3413 ASSERT_EQ(0, unlink(file2_s1d1)); 3414 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 3415 { 3416 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 3417 strerror(errno)); 3418 }; 3419 3420 ASSERT_EQ(0, unlink(file1_s1d2)); 3421 ASSERT_EQ(0, unlink(file2_s1d2)); 3422 3423 ASSERT_EQ(0, unlink(file1_s1d3)); 3424 ASSERT_EQ(0, unlink(file2_s1d3)); 3425 3426 enforce_ruleset(_metadata, ruleset_fd); 3427 ASSERT_EQ(0, close(ruleset_fd)); 3428 3429 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 3430 ASSERT_EQ(EACCES, errno); 3431 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3432 ASSERT_EQ(EACCES, errno); 3433 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3434 ASSERT_EQ(EACCES, errno); 3435 3436 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 3437 { 3438 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 3439 strerror(errno)); 3440 }; 3441 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3442 ASSERT_EQ(0, unlink(file2_s1d2)); 3443 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3444 3445 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 3446 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3447 ASSERT_EQ(0, unlink(file2_s1d3)); 3448 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3449 } 3450 3451 TEST_F_FORK(layout1, make_char) 3452 { 3453 /* Creates a /dev/null device. */ 3454 set_cap(_metadata, CAP_MKNOD); 3455 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 3456 makedev(1, 3)); 3457 } 3458 3459 TEST_F_FORK(layout1, make_block) 3460 { 3461 /* Creates a /dev/loop0 device. */ 3462 set_cap(_metadata, CAP_MKNOD); 3463 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 3464 makedev(7, 0)); 3465 } 3466 3467 TEST_F_FORK(layout1, make_reg_1) 3468 { 3469 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 3470 } 3471 3472 TEST_F_FORK(layout1, make_reg_2) 3473 { 3474 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 3475 } 3476 3477 TEST_F_FORK(layout1, make_sock) 3478 { 3479 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 3480 } 3481 3482 TEST_F_FORK(layout1, make_fifo) 3483 { 3484 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 3485 } 3486 3487 TEST_F_FORK(layout1, make_sym) 3488 { 3489 const struct rule rules[] = { 3490 { 3491 .path = dir_s1d2, 3492 .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 3493 }, 3494 {}, 3495 }; 3496 const int ruleset_fd = 3497 create_ruleset(_metadata, rules[0].access, rules); 3498 3499 ASSERT_LE(0, ruleset_fd); 3500 3501 ASSERT_EQ(0, unlink(file1_s1d1)); 3502 ASSERT_EQ(0, unlink(file2_s1d1)); 3503 ASSERT_EQ(0, symlink("none", file2_s1d1)); 3504 3505 ASSERT_EQ(0, unlink(file1_s1d2)); 3506 ASSERT_EQ(0, unlink(file2_s1d2)); 3507 3508 ASSERT_EQ(0, unlink(file1_s1d3)); 3509 ASSERT_EQ(0, unlink(file2_s1d3)); 3510 3511 enforce_ruleset(_metadata, ruleset_fd); 3512 ASSERT_EQ(0, close(ruleset_fd)); 3513 3514 ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3515 ASSERT_EQ(EACCES, errno); 3516 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3517 ASSERT_EQ(EACCES, errno); 3518 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3519 ASSERT_EQ(EACCES, errno); 3520 3521 ASSERT_EQ(0, symlink("none", file1_s1d2)); 3522 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3523 ASSERT_EQ(0, unlink(file2_s1d2)); 3524 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3525 3526 ASSERT_EQ(0, symlink("none", file1_s1d3)); 3527 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3528 ASSERT_EQ(0, unlink(file2_s1d3)); 3529 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3530 } 3531 3532 TEST_F_FORK(layout1, make_dir) 3533 { 3534 const struct rule rules[] = { 3535 { 3536 .path = dir_s1d2, 3537 .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3538 }, 3539 {}, 3540 }; 3541 const int ruleset_fd = 3542 create_ruleset(_metadata, rules[0].access, rules); 3543 3544 ASSERT_LE(0, ruleset_fd); 3545 3546 ASSERT_EQ(0, unlink(file1_s1d1)); 3547 ASSERT_EQ(0, unlink(file1_s1d2)); 3548 ASSERT_EQ(0, unlink(file1_s1d3)); 3549 3550 enforce_ruleset(_metadata, ruleset_fd); 3551 ASSERT_EQ(0, close(ruleset_fd)); 3552 3553 /* Uses file_* as directory names. */ 3554 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3555 ASSERT_EQ(EACCES, errno); 3556 ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3557 ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3558 } 3559 3560 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3561 const int open_flags) 3562 { 3563 static const char path_template[] = "/proc/self/fd/%d"; 3564 char procfd_path[sizeof(path_template) + 10]; 3565 const int procfd_path_size = 3566 snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3567 3568 ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3569 return open(procfd_path, open_flags); 3570 } 3571 3572 TEST_F_FORK(layout1, proc_unlinked_file) 3573 { 3574 const struct rule rules[] = { 3575 { 3576 .path = file1_s1d2, 3577 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3578 }, 3579 {}, 3580 }; 3581 int reg_fd, proc_fd; 3582 const int ruleset_fd = create_ruleset( 3583 _metadata, 3584 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3585 rules); 3586 3587 ASSERT_LE(0, ruleset_fd); 3588 enforce_ruleset(_metadata, ruleset_fd); 3589 ASSERT_EQ(0, close(ruleset_fd)); 3590 3591 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3592 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3593 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3594 ASSERT_LE(0, reg_fd); 3595 ASSERT_EQ(0, unlink(file1_s1d2)); 3596 3597 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3598 ASSERT_LE(0, proc_fd); 3599 ASSERT_EQ(0, close(proc_fd)); 3600 3601 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3602 ASSERT_EQ(-1, proc_fd) 3603 { 3604 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3605 strerror(errno)); 3606 } 3607 ASSERT_EQ(EACCES, errno); 3608 3609 ASSERT_EQ(0, close(reg_fd)); 3610 } 3611 3612 TEST_F_FORK(layout1, proc_pipe) 3613 { 3614 int proc_fd; 3615 int pipe_fds[2]; 3616 char buf = '\0'; 3617 const struct rule rules[] = { 3618 { 3619 .path = dir_s1d2, 3620 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3621 LANDLOCK_ACCESS_FS_WRITE_FILE, 3622 }, 3623 {}, 3624 }; 3625 /* Limits read and write access to files tied to the filesystem. */ 3626 const int ruleset_fd = 3627 create_ruleset(_metadata, rules[0].access, rules); 3628 3629 ASSERT_LE(0, ruleset_fd); 3630 enforce_ruleset(_metadata, ruleset_fd); 3631 ASSERT_EQ(0, close(ruleset_fd)); 3632 3633 /* Checks enforcement for normal files. */ 3634 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3635 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3636 3637 /* Checks access to pipes through FD. */ 3638 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3639 ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3640 { 3641 TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3642 } 3643 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3644 ASSERT_EQ('.', buf); 3645 3646 /* Checks write access to pipe through /proc/self/fd . */ 3647 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3648 ASSERT_LE(0, proc_fd); 3649 ASSERT_EQ(1, write(proc_fd, ".", 1)) 3650 { 3651 TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3652 pipe_fds[1], strerror(errno)); 3653 } 3654 ASSERT_EQ(0, close(proc_fd)); 3655 3656 /* Checks read access to pipe through /proc/self/fd . */ 3657 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3658 ASSERT_LE(0, proc_fd); 3659 buf = '\0'; 3660 ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3661 { 3662 TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3663 pipe_fds[1], strerror(errno)); 3664 } 3665 ASSERT_EQ(0, close(proc_fd)); 3666 3667 ASSERT_EQ(0, close(pipe_fds[0])); 3668 ASSERT_EQ(0, close(pipe_fds[1])); 3669 } 3670 3671 /* Invokes truncate(2) and returns its errno or 0. */ 3672 static int test_truncate(const char *const path) 3673 { 3674 if (truncate(path, 10) < 0) 3675 return errno; 3676 return 0; 3677 } 3678 3679 /* 3680 * Invokes creat(2) and returns its errno or 0. 3681 * Closes the opened file descriptor on success. 3682 */ 3683 static int test_creat(const char *const path) 3684 { 3685 int fd = creat(path, 0600); 3686 3687 if (fd < 0) 3688 return errno; 3689 3690 /* 3691 * Mixing error codes from close(2) and creat(2) should not lead to any 3692 * (access type) confusion for this test. 3693 */ 3694 if (close(fd) < 0) 3695 return errno; 3696 return 0; 3697 } 3698 3699 /* 3700 * Exercises file truncation when it's not restricted, 3701 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 3702 */ 3703 TEST_F_FORK(layout1, truncate_unhandled) 3704 { 3705 const char *const file_r = file1_s1d1; 3706 const char *const file_w = file2_s1d1; 3707 const char *const file_none = file1_s1d2; 3708 const struct rule rules[] = { 3709 { 3710 .path = file_r, 3711 .access = LANDLOCK_ACCESS_FS_READ_FILE, 3712 }, 3713 { 3714 .path = file_w, 3715 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3716 }, 3717 /* Implicitly: No rights for file_none. */ 3718 {}, 3719 }; 3720 3721 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3722 LANDLOCK_ACCESS_FS_WRITE_FILE; 3723 int ruleset_fd; 3724 3725 /* Enables Landlock. */ 3726 ruleset_fd = create_ruleset(_metadata, handled, rules); 3727 3728 ASSERT_LE(0, ruleset_fd); 3729 enforce_ruleset(_metadata, ruleset_fd); 3730 ASSERT_EQ(0, close(ruleset_fd)); 3731 3732 /* 3733 * Checks read right: truncate and open with O_TRUNC work, unless the 3734 * file is attempted to be opened for writing. 3735 */ 3736 EXPECT_EQ(0, test_truncate(file_r)); 3737 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 3738 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 3739 EXPECT_EQ(EACCES, test_creat(file_r)); 3740 3741 /* 3742 * Checks write right: truncate and open with O_TRUNC work, unless the 3743 * file is attempted to be opened for reading. 3744 */ 3745 EXPECT_EQ(0, test_truncate(file_w)); 3746 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 3747 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 3748 EXPECT_EQ(0, test_creat(file_w)); 3749 3750 /* 3751 * Checks "no rights" case: truncate works but all open attempts fail, 3752 * including creat. 3753 */ 3754 EXPECT_EQ(0, test_truncate(file_none)); 3755 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3756 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3757 EXPECT_EQ(EACCES, test_creat(file_none)); 3758 } 3759 3760 TEST_F_FORK(layout1, truncate) 3761 { 3762 const char *const file_rwt = file1_s1d1; 3763 const char *const file_rw = file2_s1d1; 3764 const char *const file_rt = file1_s1d2; 3765 const char *const file_t = file2_s1d2; 3766 const char *const file_none = file1_s1d3; 3767 const char *const dir_t = dir_s2d1; 3768 const char *const file_in_dir_t = file1_s2d1; 3769 const char *const dir_w = dir_s3d1; 3770 const char *const file_in_dir_w = file1_s3d1; 3771 const struct rule rules[] = { 3772 { 3773 .path = file_rwt, 3774 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3775 LANDLOCK_ACCESS_FS_WRITE_FILE | 3776 LANDLOCK_ACCESS_FS_TRUNCATE, 3777 }, 3778 { 3779 .path = file_rw, 3780 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3781 LANDLOCK_ACCESS_FS_WRITE_FILE, 3782 }, 3783 { 3784 .path = file_rt, 3785 .access = LANDLOCK_ACCESS_FS_READ_FILE | 3786 LANDLOCK_ACCESS_FS_TRUNCATE, 3787 }, 3788 { 3789 .path = file_t, 3790 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3791 }, 3792 /* Implicitly: No access rights for file_none. */ 3793 { 3794 .path = dir_t, 3795 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3796 }, 3797 { 3798 .path = dir_w, 3799 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3800 }, 3801 {}, 3802 }; 3803 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3804 LANDLOCK_ACCESS_FS_WRITE_FILE | 3805 LANDLOCK_ACCESS_FS_TRUNCATE; 3806 int ruleset_fd; 3807 3808 /* Enables Landlock. */ 3809 ruleset_fd = create_ruleset(_metadata, handled, rules); 3810 3811 ASSERT_LE(0, ruleset_fd); 3812 enforce_ruleset(_metadata, ruleset_fd); 3813 ASSERT_EQ(0, close(ruleset_fd)); 3814 3815 /* Checks read, write and truncate rights: truncation works. */ 3816 EXPECT_EQ(0, test_truncate(file_rwt)); 3817 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 3818 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 3819 3820 /* Checks read and write rights: no truncate variant works. */ 3821 EXPECT_EQ(EACCES, test_truncate(file_rw)); 3822 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 3823 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 3824 3825 /* 3826 * Checks read and truncate rights: truncation works. 3827 * 3828 * Note: Files can get truncated using open() even with O_RDONLY. 3829 */ 3830 EXPECT_EQ(0, test_truncate(file_rt)); 3831 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 3832 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 3833 3834 /* Checks truncate right: truncate works, but can't open file. */ 3835 EXPECT_EQ(0, test_truncate(file_t)); 3836 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 3837 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 3838 3839 /* Checks "no rights" case: No form of truncation works. */ 3840 EXPECT_EQ(EACCES, test_truncate(file_none)); 3841 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3842 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3843 3844 /* 3845 * Checks truncate right on directory: truncate works on contained 3846 * files. 3847 */ 3848 EXPECT_EQ(0, test_truncate(file_in_dir_t)); 3849 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 3850 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 3851 3852 /* 3853 * Checks creat in dir_w: This requires the truncate right when 3854 * overwriting an existing file, but does not require it when the file 3855 * is new. 3856 */ 3857 EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 3858 3859 ASSERT_EQ(0, unlink(file_in_dir_w)); 3860 EXPECT_EQ(0, test_creat(file_in_dir_w)); 3861 } 3862 3863 /* Invokes ftruncate(2) and returns its errno or 0. */ 3864 static int test_ftruncate(int fd) 3865 { 3866 if (ftruncate(fd, 10) < 0) 3867 return errno; 3868 return 0; 3869 } 3870 3871 TEST_F_FORK(layout1, ftruncate) 3872 { 3873 /* 3874 * This test opens a new file descriptor at different stages of 3875 * Landlock restriction: 3876 * 3877 * without restriction: ftruncate works 3878 * something else but truncate restricted: ftruncate works 3879 * truncate restricted and permitted: ftruncate works 3880 * truncate restricted and not permitted: ftruncate fails 3881 * 3882 * Whether this works or not is expected to depend on the time when the 3883 * FD was opened, not to depend on the time when ftruncate() was 3884 * called. 3885 */ 3886 const char *const path = file1_s1d1; 3887 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 3888 LANDLOCK_ACCESS_FS_WRITE_FILE; 3889 const struct rule layer1[] = { 3890 { 3891 .path = path, 3892 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3893 }, 3894 {}, 3895 }; 3896 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 3897 const struct rule layer2[] = { 3898 { 3899 .path = path, 3900 .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3901 }, 3902 {}, 3903 }; 3904 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 3905 LANDLOCK_ACCESS_FS_WRITE_FILE; 3906 const struct rule layer3[] = { 3907 { 3908 .path = path, 3909 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3910 }, 3911 {}, 3912 }; 3913 int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; 3914 3915 fd_layer0 = open(path, O_WRONLY); 3916 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3917 3918 ruleset_fd = create_ruleset(_metadata, handled1, layer1); 3919 ASSERT_LE(0, ruleset_fd); 3920 enforce_ruleset(_metadata, ruleset_fd); 3921 ASSERT_EQ(0, close(ruleset_fd)); 3922 3923 fd_layer1 = open(path, O_WRONLY); 3924 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3925 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3926 3927 ruleset_fd = create_ruleset(_metadata, handled2, layer2); 3928 ASSERT_LE(0, ruleset_fd); 3929 enforce_ruleset(_metadata, ruleset_fd); 3930 ASSERT_EQ(0, close(ruleset_fd)); 3931 3932 fd_layer2 = open(path, O_WRONLY); 3933 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3934 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3935 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3936 3937 ruleset_fd = create_ruleset(_metadata, handled3, layer3); 3938 ASSERT_LE(0, ruleset_fd); 3939 enforce_ruleset(_metadata, ruleset_fd); 3940 ASSERT_EQ(0, close(ruleset_fd)); 3941 3942 fd_layer3 = open(path, O_WRONLY); 3943 EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3944 EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3945 EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3946 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 3947 3948 ASSERT_EQ(0, close(fd_layer0)); 3949 ASSERT_EQ(0, close(fd_layer1)); 3950 ASSERT_EQ(0, close(fd_layer2)); 3951 ASSERT_EQ(0, close(fd_layer3)); 3952 } 3953 3954 /* clang-format off */ 3955 FIXTURE(ftruncate) {}; 3956 /* clang-format on */ 3957 3958 FIXTURE_SETUP(ftruncate) 3959 { 3960 prepare_layout(_metadata); 3961 create_file(_metadata, file1_s1d1); 3962 } 3963 3964 FIXTURE_TEARDOWN_PARENT(ftruncate) 3965 { 3966 EXPECT_EQ(0, remove_path(file1_s1d1)); 3967 cleanup_layout(_metadata); 3968 } 3969 3970 FIXTURE_VARIANT(ftruncate) 3971 { 3972 const __u64 handled; 3973 const __u64 allowed; 3974 const int expected_open_result; 3975 const int expected_ftruncate_result; 3976 }; 3977 3978 /* clang-format off */ 3979 FIXTURE_VARIANT_ADD(ftruncate, w_w) { 3980 /* clang-format on */ 3981 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 3982 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE, 3983 .expected_open_result = 0, 3984 .expected_ftruncate_result = 0, 3985 }; 3986 3987 /* clang-format off */ 3988 FIXTURE_VARIANT_ADD(ftruncate, t_t) { 3989 /* clang-format on */ 3990 .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 3991 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE, 3992 .expected_open_result = 0, 3993 .expected_ftruncate_result = 0, 3994 }; 3995 3996 /* clang-format off */ 3997 FIXTURE_VARIANT_ADD(ftruncate, wt_w) { 3998 /* clang-format on */ 3999 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 4000 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE, 4001 .expected_open_result = 0, 4002 .expected_ftruncate_result = EACCES, 4003 }; 4004 4005 /* clang-format off */ 4006 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 4007 /* clang-format on */ 4008 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 4009 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 4010 .expected_open_result = 0, 4011 .expected_ftruncate_result = 0, 4012 }; 4013 4014 /* clang-format off */ 4015 FIXTURE_VARIANT_ADD(ftruncate, wt_t) { 4016 /* clang-format on */ 4017 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 4018 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE, 4019 .expected_open_result = EACCES, 4020 }; 4021 4022 TEST_F_FORK(ftruncate, open_and_ftruncate) 4023 { 4024 const char *const path = file1_s1d1; 4025 const struct rule rules[] = { 4026 { 4027 .path = path, 4028 .access = variant->allowed, 4029 }, 4030 {}, 4031 }; 4032 int fd, ruleset_fd; 4033 4034 /* Enables Landlock. */ 4035 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4036 ASSERT_LE(0, ruleset_fd); 4037 enforce_ruleset(_metadata, ruleset_fd); 4038 ASSERT_EQ(0, close(ruleset_fd)); 4039 4040 fd = open(path, O_WRONLY); 4041 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 4042 if (fd >= 0) { 4043 EXPECT_EQ(variant->expected_ftruncate_result, 4044 test_ftruncate(fd)); 4045 ASSERT_EQ(0, close(fd)); 4046 } 4047 } 4048 4049 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 4050 { 4051 int child, fd, status; 4052 int socket_fds[2]; 4053 4054 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 4055 socket_fds)); 4056 4057 child = fork(); 4058 ASSERT_LE(0, child); 4059 if (child == 0) { 4060 /* 4061 * Enables Landlock in the child process, open a file descriptor 4062 * where truncation is forbidden and send it to the 4063 * non-landlocked parent process. 4064 */ 4065 const char *const path = file1_s1d1; 4066 const struct rule rules[] = { 4067 { 4068 .path = path, 4069 .access = variant->allowed, 4070 }, 4071 {}, 4072 }; 4073 int fd, ruleset_fd; 4074 4075 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4076 ASSERT_LE(0, ruleset_fd); 4077 enforce_ruleset(_metadata, ruleset_fd); 4078 ASSERT_EQ(0, close(ruleset_fd)); 4079 4080 fd = open(path, O_WRONLY); 4081 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 4082 4083 if (fd >= 0) { 4084 ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 4085 ASSERT_EQ(0, close(fd)); 4086 } 4087 4088 ASSERT_EQ(0, close(socket_fds[0])); 4089 4090 _exit(_metadata->exit_code); 4091 return; 4092 } 4093 4094 if (variant->expected_open_result == 0) { 4095 fd = recv_fd(socket_fds[1]); 4096 ASSERT_LE(0, fd); 4097 4098 EXPECT_EQ(variant->expected_ftruncate_result, 4099 test_ftruncate(fd)); 4100 ASSERT_EQ(0, close(fd)); 4101 } 4102 4103 ASSERT_EQ(child, waitpid(child, &status, 0)); 4104 ASSERT_EQ(1, WIFEXITED(status)); 4105 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 4106 4107 ASSERT_EQ(0, close(socket_fds[0])); 4108 ASSERT_EQ(0, close(socket_fds[1])); 4109 } 4110 4111 /* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */ 4112 static int test_fs_ioc_getflags_ioctl(int fd) 4113 { 4114 uint32_t flags; 4115 4116 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) 4117 return errno; 4118 return 0; 4119 } 4120 4121 TEST(memfd_ftruncate_and_ioctl) 4122 { 4123 const struct landlock_ruleset_attr attr = { 4124 .handled_access_fs = ACCESS_ALL, 4125 }; 4126 int ruleset_fd, fd, i; 4127 4128 /* 4129 * We exercise the same test both with and without Landlock enabled, to 4130 * ensure that it behaves the same in both cases. 4131 */ 4132 for (i = 0; i < 2; i++) { 4133 /* Creates a new memfd. */ 4134 fd = memfd_create("name", MFD_CLOEXEC); 4135 ASSERT_LE(0, fd); 4136 4137 /* 4138 * Checks that operations associated with the opened file 4139 * (ftruncate, ioctl) are permitted on file descriptors that are 4140 * created in ways other than open(2). 4141 */ 4142 EXPECT_EQ(0, test_ftruncate(fd)); 4143 EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd)); 4144 4145 ASSERT_EQ(0, close(fd)); 4146 4147 /* Enables Landlock. */ 4148 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4149 ASSERT_LE(0, ruleset_fd); 4150 enforce_ruleset(_metadata, ruleset_fd); 4151 ASSERT_EQ(0, close(ruleset_fd)); 4152 } 4153 } 4154 4155 static int test_fionread_ioctl(int fd) 4156 { 4157 size_t sz = 0; 4158 4159 if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES) 4160 return errno; 4161 return 0; 4162 } 4163 4164 TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl) 4165 { 4166 const struct landlock_ruleset_attr attr = { 4167 .handled_access_fs = ACCESS_ALL, 4168 }; 4169 int ruleset_fd, fd; 4170 4171 /* 4172 * Checks that for files opened with O_PATH, both ioctl(2) and 4173 * ftruncate(2) yield EBADF, as it is documented in open(2) for the 4174 * O_PATH flag. 4175 */ 4176 fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 4177 ASSERT_LE(0, fd); 4178 4179 EXPECT_EQ(EBADF, test_ftruncate(fd)); 4180 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 4181 4182 ASSERT_EQ(0, close(fd)); 4183 4184 /* Enables Landlock. */ 4185 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4186 ASSERT_LE(0, ruleset_fd); 4187 enforce_ruleset(_metadata, ruleset_fd); 4188 ASSERT_EQ(0, close(ruleset_fd)); 4189 4190 /* 4191 * Checks that after enabling Landlock, 4192 * - the file can still be opened with O_PATH 4193 * - both ioctl and truncate still yield EBADF (not EACCES). 4194 */ 4195 fd = open(dir_s1d1, O_PATH | O_CLOEXEC); 4196 ASSERT_LE(0, fd); 4197 4198 EXPECT_EQ(EBADF, test_ftruncate(fd)); 4199 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd)); 4200 4201 ASSERT_EQ(0, close(fd)); 4202 } 4203 4204 /* 4205 * ioctl_error - generically call the given ioctl with a pointer to a 4206 * sufficiently large zeroed-out memory region. 4207 * 4208 * Returns the IOCTLs error, or 0. 4209 */ 4210 static int ioctl_error(struct __test_metadata *const _metadata, int fd, 4211 unsigned int cmd) 4212 { 4213 char buf[128]; /* sufficiently large */ 4214 int res, stdinbak_fd; 4215 4216 /* 4217 * Depending on the IOCTL command, parts of the zeroed-out buffer might 4218 * be interpreted as file descriptor numbers. We do not want to 4219 * accidentally operate on file descriptor 0 (stdin), so we temporarily 4220 * move stdin to a different FD and close FD 0 for the IOCTL call. 4221 */ 4222 stdinbak_fd = dup(0); 4223 ASSERT_LT(0, stdinbak_fd); 4224 ASSERT_EQ(0, close(0)); 4225 4226 /* Invokes the IOCTL with a zeroed-out buffer. */ 4227 bzero(&buf, sizeof(buf)); 4228 res = ioctl(fd, cmd, &buf); 4229 4230 /* Restores the old FD 0 and closes the backup FD. */ 4231 ASSERT_EQ(0, dup2(stdinbak_fd, 0)); 4232 ASSERT_EQ(0, close(stdinbak_fd)); 4233 4234 if (res < 0) 4235 return errno; 4236 4237 return 0; 4238 } 4239 4240 /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */ 4241 struct space_resv { 4242 __s16 l_type; 4243 __s16 l_whence; 4244 __s64 l_start; 4245 __s64 l_len; /* len == 0 means until end of file */ 4246 __s32 l_sysid; 4247 __u32 l_pid; 4248 __s32 l_pad[4]; /* reserved area */ 4249 }; 4250 4251 #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv) 4252 #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv) 4253 #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv) 4254 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv) 4255 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv) 4256 4257 /* 4258 * Tests a series of blanket-permitted and denied IOCTLs. 4259 */ 4260 TEST_F_FORK(layout1, blanket_permitted_ioctls) 4261 { 4262 const struct landlock_ruleset_attr attr = { 4263 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4264 }; 4265 int ruleset_fd, fd; 4266 4267 /* Enables Landlock. */ 4268 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4269 ASSERT_LE(0, ruleset_fd); 4270 enforce_ruleset(_metadata, ruleset_fd); 4271 ASSERT_EQ(0, close(ruleset_fd)); 4272 4273 fd = open("/dev/null", O_RDWR | O_CLOEXEC); 4274 ASSERT_LE(0, fd); 4275 4276 /* 4277 * Checks permitted commands. 4278 * These ones may return errors, but should not be blocked by Landlock. 4279 */ 4280 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX)); 4281 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX)); 4282 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO)); 4283 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC)); 4284 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE)); 4285 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE)); 4286 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW)); 4287 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP)); 4288 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ)); 4289 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE)); 4290 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE)); 4291 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE)); 4292 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID)); 4293 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH)); 4294 4295 /* 4296 * Checks blocked commands. 4297 * A call to a blocked IOCTL command always returns EACCES. 4298 */ 4299 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 4300 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS)); 4301 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS)); 4302 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR)); 4303 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR)); 4304 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP)); 4305 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP)); 4306 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64)); 4307 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP)); 4308 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64)); 4309 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE)); 4310 4311 /* Default case is also blocked. */ 4312 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee)); 4313 4314 ASSERT_EQ(0, close(fd)); 4315 } 4316 4317 /* 4318 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right, 4319 * because they are not character or block devices. 4320 */ 4321 TEST_F_FORK(layout1, named_pipe_ioctl) 4322 { 4323 pid_t child_pid; 4324 int fd, ruleset_fd; 4325 const char *const path = file1_s1d1; 4326 const struct landlock_ruleset_attr attr = { 4327 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4328 }; 4329 4330 ASSERT_EQ(0, unlink(path)); 4331 ASSERT_EQ(0, mkfifo(path, 0600)); 4332 4333 /* Enables Landlock. */ 4334 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4335 ASSERT_LE(0, ruleset_fd); 4336 enforce_ruleset(_metadata, ruleset_fd); 4337 ASSERT_EQ(0, close(ruleset_fd)); 4338 4339 /* The child process opens the pipe for writing. */ 4340 child_pid = fork(); 4341 ASSERT_NE(-1, child_pid); 4342 if (child_pid == 0) { 4343 fd = open(path, O_WRONLY); 4344 close(fd); 4345 exit(0); 4346 } 4347 4348 fd = open(path, O_RDONLY); 4349 ASSERT_LE(0, fd); 4350 4351 /* FIONREAD is implemented by pipefifo_fops. */ 4352 EXPECT_EQ(0, test_fionread_ioctl(fd)); 4353 4354 ASSERT_EQ(0, close(fd)); 4355 ASSERT_EQ(0, unlink(path)); 4356 4357 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0)); 4358 } 4359 4360 /* For named UNIX domain sockets, no IOCTL restrictions apply. */ 4361 TEST_F_FORK(layout1, named_unix_domain_socket_ioctl) 4362 { 4363 const char *const path = file1_s1d1; 4364 int srv_fd, cli_fd, ruleset_fd; 4365 socklen_t size; 4366 struct sockaddr_un srv_un, cli_un; 4367 const struct landlock_ruleset_attr attr = { 4368 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4369 }; 4370 4371 /* Sets up a server */ 4372 srv_un.sun_family = AF_UNIX; 4373 strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path)); 4374 4375 ASSERT_EQ(0, unlink(path)); 4376 srv_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4377 ASSERT_LE(0, srv_fd); 4378 4379 size = offsetof(struct sockaddr_un, sun_path) + strlen(srv_un.sun_path); 4380 ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, size)); 4381 ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */)); 4382 4383 /* Enables Landlock. */ 4384 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4385 ASSERT_LE(0, ruleset_fd); 4386 enforce_ruleset(_metadata, ruleset_fd); 4387 ASSERT_EQ(0, close(ruleset_fd)); 4388 4389 /* Sets up a client connection to it */ 4390 cli_un.sun_family = AF_UNIX; 4391 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4392 ASSERT_LE(0, cli_fd); 4393 4394 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path); 4395 ASSERT_EQ(0, bind(cli_fd, (struct sockaddr *)&cli_un, size)); 4396 4397 bzero(&cli_un, sizeof(cli_un)); 4398 cli_un.sun_family = AF_UNIX; 4399 strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path)); 4400 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path); 4401 4402 ASSERT_EQ(0, connect(cli_fd, (struct sockaddr *)&cli_un, size)); 4403 4404 /* FIONREAD and other IOCTLs should not be forbidden. */ 4405 EXPECT_EQ(0, test_fionread_ioctl(cli_fd)); 4406 4407 ASSERT_EQ(0, close(cli_fd)); 4408 } 4409 4410 /* clang-format off */ 4411 FIXTURE(ioctl) {}; 4412 4413 FIXTURE_SETUP(ioctl) {}; 4414 4415 FIXTURE_TEARDOWN(ioctl) {}; 4416 /* clang-format on */ 4417 4418 FIXTURE_VARIANT(ioctl) 4419 { 4420 const __u64 handled; 4421 const __u64 allowed; 4422 const mode_t open_mode; 4423 /* 4424 * FIONREAD is used as a characteristic device-specific IOCTL command. 4425 * It is implemented in fs/ioctl.c for regular files, 4426 * but we do not blanket-permit it for devices. 4427 */ 4428 const int expected_fionread_result; 4429 }; 4430 4431 /* clang-format off */ 4432 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) { 4433 /* clang-format on */ 4434 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4435 .allowed = 0, 4436 .open_mode = O_RDWR, 4437 .expected_fionread_result = EACCES, 4438 }; 4439 4440 /* clang-format off */ 4441 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) { 4442 /* clang-format on */ 4443 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4444 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4445 .open_mode = O_RDWR, 4446 .expected_fionread_result = 0, 4447 }; 4448 4449 /* clang-format off */ 4450 FIXTURE_VARIANT_ADD(ioctl, unhandled) { 4451 /* clang-format on */ 4452 .handled = LANDLOCK_ACCESS_FS_EXECUTE, 4453 .allowed = LANDLOCK_ACCESS_FS_EXECUTE, 4454 .open_mode = O_RDWR, 4455 .expected_fionread_result = 0, 4456 }; 4457 4458 TEST_F_FORK(ioctl, handle_dir_access_file) 4459 { 4460 const int flag = 0; 4461 const struct rule rules[] = { 4462 { 4463 .path = "/dev", 4464 .access = variant->allowed, 4465 }, 4466 {}, 4467 }; 4468 int file_fd, ruleset_fd; 4469 4470 /* Enables Landlock. */ 4471 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4472 ASSERT_LE(0, ruleset_fd); 4473 enforce_ruleset(_metadata, ruleset_fd); 4474 ASSERT_EQ(0, close(ruleset_fd)); 4475 4476 file_fd = open("/dev/zero", variant->open_mode); 4477 ASSERT_LE(0, file_fd); 4478 4479 /* Checks that IOCTL commands return the expected errors. */ 4480 EXPECT_EQ(variant->expected_fionread_result, 4481 test_fionread_ioctl(file_fd)); 4482 4483 /* Checks that unrestrictable commands are unrestricted. */ 4484 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); 4485 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); 4486 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); 4487 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); 4488 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); 4489 4490 ASSERT_EQ(0, close(file_fd)); 4491 } 4492 4493 TEST_F_FORK(ioctl, handle_dir_access_dir) 4494 { 4495 const int flag = 0; 4496 const struct rule rules[] = { 4497 { 4498 .path = "/dev", 4499 .access = variant->allowed, 4500 }, 4501 {}, 4502 }; 4503 int dir_fd, ruleset_fd; 4504 4505 /* Enables Landlock. */ 4506 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4507 ASSERT_LE(0, ruleset_fd); 4508 enforce_ruleset(_metadata, ruleset_fd); 4509 ASSERT_EQ(0, close(ruleset_fd)); 4510 4511 /* 4512 * Ignore variant->open_mode for this test, as we intend to open a 4513 * directory. If the directory can not be opened, the variant is 4514 * infeasible to test with an opened directory. 4515 */ 4516 dir_fd = open("/dev", O_RDONLY); 4517 if (dir_fd < 0) 4518 return; 4519 4520 /* 4521 * Checks that IOCTL commands return the expected errors. 4522 * We do not use the expected values from the fixture here. 4523 * 4524 * When using IOCTL on a directory, no Landlock restrictions apply. 4525 */ 4526 EXPECT_EQ(0, test_fionread_ioctl(dir_fd)); 4527 4528 /* Checks that unrestrictable commands are unrestricted. */ 4529 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX)); 4530 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX)); 4531 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag)); 4532 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag)); 4533 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag)); 4534 4535 ASSERT_EQ(0, close(dir_fd)); 4536 } 4537 4538 TEST_F_FORK(ioctl, handle_file_access_file) 4539 { 4540 const int flag = 0; 4541 const struct rule rules[] = { 4542 { 4543 .path = "/dev/zero", 4544 .access = variant->allowed, 4545 }, 4546 {}, 4547 }; 4548 int file_fd, ruleset_fd; 4549 4550 /* Enables Landlock. */ 4551 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4552 ASSERT_LE(0, ruleset_fd); 4553 enforce_ruleset(_metadata, ruleset_fd); 4554 ASSERT_EQ(0, close(ruleset_fd)); 4555 4556 file_fd = open("/dev/zero", variant->open_mode); 4557 ASSERT_LE(0, file_fd) 4558 { 4559 TH_LOG("Failed to open /dev/zero: %s", strerror(errno)); 4560 } 4561 4562 /* Checks that IOCTL commands return the expected errors. */ 4563 EXPECT_EQ(variant->expected_fionread_result, 4564 test_fionread_ioctl(file_fd)); 4565 4566 /* Checks that unrestrictable commands are unrestricted. */ 4567 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); 4568 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); 4569 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); 4570 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); 4571 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); 4572 4573 ASSERT_EQ(0, close(file_fd)); 4574 } 4575 4576 /* clang-format off */ 4577 FIXTURE(layout1_bind) {}; 4578 /* clang-format on */ 4579 4580 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 4581 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 4582 4583 /* Move targets for disconnected path tests. */ 4584 static const char dir_s4d1[] = TMP_DIR "/s4d1"; 4585 static const char file1_s4d1[] = TMP_DIR "/s4d1/f1"; 4586 static const char file2_s4d1[] = TMP_DIR "/s4d1/f2"; 4587 static const char dir_s4d2[] = TMP_DIR "/s4d1/s4d2"; 4588 static const char file1_s4d2[] = TMP_DIR "/s4d1/s4d2/f1"; 4589 static const char file1_name[] = "f1"; 4590 static const char file2_name[] = "f2"; 4591 4592 FIXTURE_SETUP(layout1_bind) 4593 { 4594 prepare_layout(_metadata); 4595 4596 create_layout1(_metadata); 4597 4598 set_cap(_metadata, CAP_SYS_ADMIN); 4599 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 4600 clear_cap(_metadata, CAP_SYS_ADMIN); 4601 } 4602 4603 FIXTURE_TEARDOWN_PARENT(layout1_bind) 4604 { 4605 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 4606 4607 remove_path(file1_s4d1); 4608 remove_path(file2_s4d1); 4609 4610 remove_layout1(_metadata); 4611 4612 cleanup_layout(_metadata); 4613 } 4614 4615 /* 4616 * layout1_bind hierarchy: 4617 * 4618 * tmp 4619 * ├── s1d1 4620 * │ ├── f1 4621 * │ ├── f2 4622 * │ └── s1d2 4623 * │ ├── f1 4624 * │ ├── f2 4625 * │ └── s1d3 [disconnected by path_disconnected] 4626 * │ ├── f1 4627 * │ └── f2 4628 * ├── s2d1 4629 * │ ├── f1 4630 * │ └── s2d2 [bind mount from s1d2] 4631 * │ ├── f1 4632 * │ ├── f2 4633 * │ └── s1d3 4634 * │ ├── f1 4635 * │ └── f2 4636 * ├── s3d1 4637 * │ └── s3d2 4638 * │ └── s3d3 4639 * └── s4d1 [renamed from s1d3 by path_disconnected] 4640 * ├── f1 4641 * ├── f2 4642 * └── s4d2 4643 * └── f1 4644 */ 4645 4646 TEST_F_FORK(layout1_bind, no_restriction) 4647 { 4648 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 4649 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4650 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 4651 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4652 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 4653 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4654 4655 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 4656 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 4657 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 4658 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 4659 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 4660 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 4661 4662 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 4663 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4664 4665 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 4666 } 4667 4668 TEST_F_FORK(layout1_bind, same_content_same_file) 4669 { 4670 /* 4671 * Sets access right on parent directories of both source and 4672 * destination mount points. 4673 */ 4674 const struct rule layer1_parent[] = { 4675 { 4676 .path = dir_s1d1, 4677 .access = ACCESS_RO, 4678 }, 4679 { 4680 .path = dir_s2d1, 4681 .access = ACCESS_RW, 4682 }, 4683 {}, 4684 }; 4685 /* 4686 * Sets access rights on the same bind-mounted directories. The result 4687 * should be ACCESS_RW for both directories, but not both hierarchies 4688 * because of the first layer. 4689 */ 4690 const struct rule layer2_mount_point[] = { 4691 { 4692 .path = dir_s1d2, 4693 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4694 }, 4695 { 4696 .path = dir_s2d2, 4697 .access = ACCESS_RW, 4698 }, 4699 {}, 4700 }; 4701 /* Only allow read-access to the s1d3 hierarchies. */ 4702 const struct rule layer3_source[] = { 4703 { 4704 .path = dir_s1d3, 4705 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4706 }, 4707 {}, 4708 }; 4709 /* Removes all access rights. */ 4710 const struct rule layer4_destination[] = { 4711 { 4712 .path = bind_file1_s1d3, 4713 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 4714 }, 4715 {}, 4716 }; 4717 int ruleset_fd; 4718 4719 /* Sets rules for the parent directories. */ 4720 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 4721 ASSERT_LE(0, ruleset_fd); 4722 enforce_ruleset(_metadata, ruleset_fd); 4723 ASSERT_EQ(0, close(ruleset_fd)); 4724 4725 /* Checks source hierarchy. */ 4726 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4727 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4728 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4729 4730 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4731 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4732 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4733 4734 /* Checks destination hierarchy. */ 4735 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 4736 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4737 4738 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4739 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4740 4741 /* Sets rules for the mount points. */ 4742 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 4743 ASSERT_LE(0, ruleset_fd); 4744 enforce_ruleset(_metadata, ruleset_fd); 4745 ASSERT_EQ(0, close(ruleset_fd)); 4746 4747 /* Checks source hierarchy. */ 4748 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 4749 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4750 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4751 4752 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4753 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4754 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4755 4756 /* Checks destination hierarchy. */ 4757 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 4758 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 4759 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4760 4761 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4762 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4763 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4764 4765 /* Sets a (shared) rule only on the source. */ 4766 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 4767 ASSERT_LE(0, ruleset_fd); 4768 enforce_ruleset(_metadata, ruleset_fd); 4769 ASSERT_EQ(0, close(ruleset_fd)); 4770 4771 /* Checks source hierarchy. */ 4772 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 4773 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4774 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4775 4776 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4777 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4778 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 4779 4780 /* Checks destination hierarchy. */ 4781 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 4782 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 4783 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4784 4785 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4786 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 4787 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4788 4789 /* Sets a (shared) rule only on the destination. */ 4790 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 4791 ASSERT_LE(0, ruleset_fd); 4792 enforce_ruleset(_metadata, ruleset_fd); 4793 ASSERT_EQ(0, close(ruleset_fd)); 4794 4795 /* Checks source hierarchy. */ 4796 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 4797 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4798 4799 /* Checks destination hierarchy. */ 4800 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 4801 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 4802 } 4803 4804 TEST_F_FORK(layout1_bind, reparent_cross_mount) 4805 { 4806 const struct rule layer1[] = { 4807 { 4808 /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 4809 .path = dir_s2d1, 4810 .access = LANDLOCK_ACCESS_FS_REFER, 4811 }, 4812 { 4813 .path = bind_dir_s1d3, 4814 .access = LANDLOCK_ACCESS_FS_EXECUTE, 4815 }, 4816 {}, 4817 }; 4818 int ruleset_fd = create_ruleset( 4819 _metadata, 4820 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 4821 4822 ASSERT_LE(0, ruleset_fd); 4823 enforce_ruleset(_metadata, ruleset_fd); 4824 ASSERT_EQ(0, close(ruleset_fd)); 4825 4826 /* Checks basic denied move. */ 4827 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 4828 ASSERT_EQ(EXDEV, errno); 4829 4830 /* Checks real cross-mount move (Landlock is not involved). */ 4831 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 4832 ASSERT_EQ(EXDEV, errno); 4833 4834 /* Checks move that will give more accesses. */ 4835 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 4836 ASSERT_EQ(EXDEV, errno); 4837 4838 /* Checks legitimate downgrade move. */ 4839 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 4840 } 4841 4842 /* 4843 * Make sure access to file through a disconnected path works as expected. 4844 * This test moves s1d3 to s4d1. 4845 */ 4846 TEST_F_FORK(layout1_bind, path_disconnected) 4847 { 4848 const struct rule layer1_allow_all[] = { 4849 { 4850 .path = TMP_DIR, 4851 .access = ACCESS_ALL, 4852 }, 4853 {}, 4854 }; 4855 const struct rule layer2_allow_just_f1[] = { 4856 { 4857 .path = file1_s1d3, 4858 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4859 }, 4860 {}, 4861 }; 4862 const struct rule layer3_only_s1d2[] = { 4863 { 4864 .path = dir_s1d2, 4865 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4866 }, 4867 {}, 4868 }; 4869 4870 /* Landlock should not deny access just because it is disconnected. */ 4871 int ruleset_fd_l1 = 4872 create_ruleset(_metadata, ACCESS_ALL, layer1_allow_all); 4873 4874 /* Creates the new ruleset now before we move the dir containing the file. */ 4875 int ruleset_fd_l2 = 4876 create_ruleset(_metadata, ACCESS_RW, layer2_allow_just_f1); 4877 int ruleset_fd_l3 = 4878 create_ruleset(_metadata, ACCESS_RW, layer3_only_s1d2); 4879 int bind_s1d3_fd; 4880 4881 ASSERT_LE(0, ruleset_fd_l1); 4882 ASSERT_LE(0, ruleset_fd_l2); 4883 ASSERT_LE(0, ruleset_fd_l3); 4884 4885 enforce_ruleset(_metadata, ruleset_fd_l1); 4886 EXPECT_EQ(0, close(ruleset_fd_l1)); 4887 4888 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 4889 ASSERT_LE(0, bind_s1d3_fd); 4890 4891 /* Tests access is possible before we move. */ 4892 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4893 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4894 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 4895 4896 /* Makes it disconnected. */ 4897 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 4898 { 4899 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 4900 strerror(errno)); 4901 } 4902 4903 /* Tests that access is still possible. */ 4904 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4905 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4906 4907 /* 4908 * Tests that ".." is not possible (not because of Landlock, but just 4909 * because it's disconnected). 4910 */ 4911 EXPECT_EQ(ENOENT, 4912 test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 4913 4914 /* This should still work with a narrower rule. */ 4915 enforce_ruleset(_metadata, ruleset_fd_l2); 4916 EXPECT_EQ(0, close(ruleset_fd_l2)); 4917 4918 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)); 4919 /* 4920 * Accessing a file through a disconnected file descriptor can still be 4921 * allowed by a rule tied to this file, even if it is no longer visible in 4922 * its mount point. 4923 */ 4924 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4925 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4926 4927 enforce_ruleset(_metadata, ruleset_fd_l3); 4928 EXPECT_EQ(0, close(ruleset_fd_l3)); 4929 4930 EXPECT_EQ(EACCES, test_open(file1_s4d1, O_RDONLY)); 4931 /* 4932 * Accessing a file through a disconnected file descriptor can still be 4933 * allowed by a rule tied to the original mount point, even if it is no 4934 * longer visible in its mount point. 4935 */ 4936 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4937 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4938 } 4939 4940 /* 4941 * Test that renameat with disconnected paths works under Landlock. This test 4942 * moves s1d3 to s4d2, so that we can have a rule allowing refers on the move 4943 * target's immediate parent. 4944 */ 4945 TEST_F_FORK(layout1_bind, path_disconnected_rename) 4946 { 4947 const struct rule layer1[] = { 4948 { 4949 .path = dir_s1d2, 4950 .access = LANDLOCK_ACCESS_FS_REFER | 4951 LANDLOCK_ACCESS_FS_MAKE_DIR | 4952 LANDLOCK_ACCESS_FS_REMOVE_DIR | 4953 LANDLOCK_ACCESS_FS_MAKE_REG | 4954 LANDLOCK_ACCESS_FS_REMOVE_FILE | 4955 LANDLOCK_ACCESS_FS_READ_FILE, 4956 }, 4957 { 4958 .path = dir_s4d1, 4959 .access = LANDLOCK_ACCESS_FS_REFER | 4960 LANDLOCK_ACCESS_FS_MAKE_DIR | 4961 LANDLOCK_ACCESS_FS_REMOVE_DIR | 4962 LANDLOCK_ACCESS_FS_MAKE_REG | 4963 LANDLOCK_ACCESS_FS_REMOVE_FILE | 4964 LANDLOCK_ACCESS_FS_READ_FILE, 4965 }, 4966 {} 4967 }; 4968 4969 /* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */ 4970 const struct rule layer2_only_s1d2[] = { 4971 { 4972 .path = dir_s1d2, 4973 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4974 }, 4975 {}, 4976 }; 4977 int ruleset_fd_l1, ruleset_fd_l2; 4978 pid_t child_pid; 4979 int bind_s1d3_fd, status; 4980 4981 ASSERT_EQ(0, mkdir(dir_s4d1, 0755)) 4982 { 4983 TH_LOG("Failed to create %s: %s", dir_s4d1, strerror(errno)); 4984 } 4985 ruleset_fd_l1 = create_ruleset(_metadata, ACCESS_ALL, layer1); 4986 ruleset_fd_l2 = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 4987 layer2_only_s1d2); 4988 ASSERT_LE(0, ruleset_fd_l1); 4989 ASSERT_LE(0, ruleset_fd_l2); 4990 4991 enforce_ruleset(_metadata, ruleset_fd_l1); 4992 EXPECT_EQ(0, close(ruleset_fd_l1)); 4993 4994 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 4995 ASSERT_LE(0, bind_s1d3_fd); 4996 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4997 4998 /* Tests ENOENT priority over EACCES for disconnected directory. */ 4999 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5000 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5001 { 5002 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5003 strerror(errno)); 5004 } 5005 EXPECT_EQ(ENOENT, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5006 5007 /* 5008 * The file is no longer under s1d2 but we should still be able to access it 5009 * with layer 2 because its mount point is evaluated as the first valid 5010 * directory because it was initially a parent. Do a fork to test this so 5011 * we don't prevent ourselves from renaming it back later. 5012 */ 5013 child_pid = fork(); 5014 ASSERT_LE(0, child_pid); 5015 if (child_pid == 0) { 5016 enforce_ruleset(_metadata, ruleset_fd_l2); 5017 EXPECT_EQ(0, close(ruleset_fd_l2)); 5018 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5019 EXPECT_EQ(EACCES, test_open(file1_s4d2, O_RDONLY)); 5020 5021 /* 5022 * Tests that access widening checks indeed prevents us from renaming it 5023 * back. 5024 */ 5025 EXPECT_EQ(-1, rename(dir_s4d2, dir_s1d3)); 5026 EXPECT_EQ(EXDEV, errno); 5027 5028 /* 5029 * Including through the now disconnected fd (but it should return 5030 * EXDEV). 5031 */ 5032 EXPECT_EQ(-1, renameat(bind_s1d3_fd, file1_name, AT_FDCWD, 5033 file1_s2d2)); 5034 EXPECT_EQ(EXDEV, errno); 5035 _exit(_metadata->exit_code); 5036 return; 5037 } 5038 5039 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5040 EXPECT_EQ(1, WIFEXITED(status)); 5041 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5042 5043 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5044 { 5045 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d1, dir_s1d3, 5046 strerror(errno)); 5047 } 5048 5049 /* Now checks that we can access it under l2. */ 5050 child_pid = fork(); 5051 ASSERT_LE(0, child_pid); 5052 if (child_pid == 0) { 5053 enforce_ruleset(_metadata, ruleset_fd_l2); 5054 EXPECT_EQ(0, close(ruleset_fd_l2)); 5055 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5056 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5057 _exit(_metadata->exit_code); 5058 return; 5059 } 5060 5061 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5062 EXPECT_EQ(1, WIFEXITED(status)); 5063 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5064 5065 /* 5066 * Also test that we can rename via a disconnected path. We move the 5067 * dir back to the disconnected place first, then we rename file1 to 5068 * file2 through our dir fd. 5069 */ 5070 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5071 { 5072 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5073 strerror(errno)); 5074 } 5075 ASSERT_EQ(0, 5076 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)) 5077 { 5078 TH_LOG("Failed to rename %s to %s within disconnected %s: %s", 5079 file1_name, file2_name, bind_dir_s1d3, strerror(errno)); 5080 } 5081 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5082 ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2)) 5083 { 5084 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5085 file2_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5086 } 5087 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 5088 EXPECT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5089 5090 /* Move it back using the disconnected path as the target. */ 5091 ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name)) 5092 { 5093 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5094 file1_s1d2, file1_name, bind_dir_s1d3, strerror(errno)); 5095 } 5096 5097 /* Now make it connected again. */ 5098 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5099 { 5100 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d2, dir_s1d3, 5101 strerror(errno)); 5102 } 5103 5104 /* Checks again that we can access it under l2. */ 5105 enforce_ruleset(_metadata, ruleset_fd_l2); 5106 EXPECT_EQ(0, close(ruleset_fd_l2)); 5107 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5108 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5109 } 5110 5111 /* 5112 * Test that linkat(2) with disconnected paths works under Landlock. This 5113 * test moves s1d3 to s4d1. 5114 */ 5115 TEST_F_FORK(layout1_bind, path_disconnected_link) 5116 { 5117 /* Ruleset to be applied after renaming s1d3 to s4d1. */ 5118 const struct rule layer1[] = { 5119 { 5120 .path = dir_s4d1, 5121 .access = LANDLOCK_ACCESS_FS_REFER | 5122 LANDLOCK_ACCESS_FS_READ_FILE | 5123 LANDLOCK_ACCESS_FS_MAKE_REG | 5124 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5125 }, 5126 { 5127 .path = dir_s2d2, 5128 .access = LANDLOCK_ACCESS_FS_REFER | 5129 LANDLOCK_ACCESS_FS_READ_FILE | 5130 LANDLOCK_ACCESS_FS_MAKE_REG | 5131 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5132 }, 5133 {} 5134 }; 5135 int ruleset_fd, bind_s1d3_fd; 5136 5137 /* Removes unneeded files created by layout1, otherwise it will EEXIST. */ 5138 ASSERT_EQ(0, unlink(file1_s1d2)); 5139 ASSERT_EQ(0, unlink(file2_s1d3)); 5140 5141 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5142 ASSERT_LE(0, bind_s1d3_fd); 5143 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5144 5145 /* Disconnects bind_s1d3_fd. */ 5146 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5147 { 5148 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5149 strerror(errno)); 5150 } 5151 5152 /* Need this later to test different parent link. */ 5153 ASSERT_EQ(0, mkdir(dir_s4d2, 0755)) 5154 { 5155 TH_LOG("Failed to create %s: %s", dir_s4d2, strerror(errno)); 5156 } 5157 5158 ruleset_fd = create_ruleset(_metadata, ACCESS_ALL, layer1); 5159 ASSERT_LE(0, ruleset_fd); 5160 enforce_ruleset(_metadata, ruleset_fd); 5161 EXPECT_EQ(0, close(ruleset_fd)); 5162 5163 /* From disconnected to connected. */ 5164 ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0)) 5165 { 5166 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5167 file1_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5168 } 5169 5170 /* Tests that we can access via the new link... */ 5171 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)) 5172 { 5173 TH_LOG("Failed to open newly linked %s: %s", file1_s2d2, 5174 strerror(errno)); 5175 } 5176 5177 /* ...as well as the old one. */ 5178 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5179 { 5180 TH_LOG("Failed to open original %s: %s", file1_s4d1, 5181 strerror(errno)); 5182 } 5183 5184 /* From connected to disconnected. */ 5185 ASSERT_EQ(0, unlink(file1_s4d1)); 5186 ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0)) 5187 { 5188 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5189 file1_s2d2, file2_name, bind_dir_s1d3, strerror(errno)); 5190 } 5191 EXPECT_EQ(0, test_open(file2_s4d1, O_RDONLY)); 5192 ASSERT_EQ(0, unlink(file1_s2d2)); 5193 5194 /* From disconnected to disconnected (same parent). */ 5195 ASSERT_EQ(0, 5196 linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0)) 5197 { 5198 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5199 file2_name, file1_name, bind_dir_s1d3, strerror(errno)); 5200 } 5201 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5202 { 5203 TH_LOG("Failed to open newly linked %s: %s", file1_s4d1, 5204 strerror(errno)); 5205 } 5206 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)) 5207 { 5208 TH_LOG("Failed to open %s through newly created link under disconnected path: %s", 5209 file1_name, strerror(errno)); 5210 } 5211 ASSERT_EQ(0, unlink(file2_s4d1)); 5212 5213 /* From disconnected to disconnected (different parent). */ 5214 ASSERT_EQ(0, 5215 linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0)) 5216 { 5217 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5218 file1_name, "s4d2/f1", bind_dir_s1d3, strerror(errno)); 5219 } 5220 EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY)) 5221 { 5222 TH_LOG("Failed to open %s after link: %s", file1_s4d2, 5223 strerror(errno)); 5224 } 5225 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY)) 5226 { 5227 TH_LOG("Failed to open %s through disconnected path after link: %s", 5228 "s4d2/f1", strerror(errno)); 5229 } 5230 } 5231 5232 /* 5233 * layout4_disconnected_leafs with bind mount and renames: 5234 * 5235 * tmp 5236 * ├── s1d1 5237 * │ └── s1d2 [source of the bind mount] 5238 * │ ├── s1d31 5239 * │ │ └── s1d41 [now renamed beneath s3d1] 5240 * │ │ ├── f1 5241 * │ │ └── f2 5242 * │ └── s1d32 5243 * │ └── s1d42 [now renamed beneath s4d1] 5244 * │ ├── f3 5245 * │ └── f4 5246 * ├── s2d1 5247 * │ └── s2d2 [bind mount of s1d2] 5248 * │ ├── s1d31 5249 * │ │ └── s1d41 [opened FD, now renamed beneath s3d1] 5250 * │ │ ├── f1 5251 * │ │ └── f2 5252 * │ └── s1d32 5253 * │ └── s1d42 [opened FD, now renamed beneath s4d1] 5254 * │ ├── f3 5255 * │ └── f4 5256 * ├── s3d1 5257 * │ └── s1d41 [renamed here] 5258 * │ ├── f1 5259 * │ └── f2 5260 * └── s4d1 5261 * └── s1d42 [renamed here] 5262 * ├── f3 5263 * └── f4 5264 */ 5265 /* clang-format off */ 5266 FIXTURE(layout4_disconnected_leafs) { 5267 int s2d2_fd; 5268 }; 5269 /* clang-format on */ 5270 5271 FIXTURE_SETUP(layout4_disconnected_leafs) 5272 { 5273 prepare_layout(_metadata); 5274 5275 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1"); 5276 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2"); 5277 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3"); 5278 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4"); 5279 create_directory(_metadata, TMP_DIR "/s2d1/s2d2"); 5280 create_directory(_metadata, TMP_DIR "/s3d1"); 5281 create_directory(_metadata, TMP_DIR "/s4d1"); 5282 5283 self->s2d2_fd = 5284 open(TMP_DIR "/s2d1/s2d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 5285 ASSERT_LE(0, self->s2d2_fd); 5286 5287 set_cap(_metadata, CAP_SYS_ADMIN); 5288 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2", NULL, 5289 MS_BIND, NULL)); 5290 clear_cap(_metadata, CAP_SYS_ADMIN); 5291 } 5292 5293 FIXTURE_TEARDOWN_PARENT(layout4_disconnected_leafs) 5294 { 5295 /* umount(TMP_DIR "/s2d1") is handled by namespace lifetime. */ 5296 5297 /* Removes files after renames. */ 5298 remove_path(TMP_DIR "/s3d1/s1d41/f1"); 5299 remove_path(TMP_DIR "/s3d1/s1d41/f2"); 5300 remove_path(TMP_DIR "/s4d1/s1d42/f1"); 5301 remove_path(TMP_DIR "/s4d1/s1d42/f3"); 5302 remove_path(TMP_DIR "/s4d1/s1d42/f4"); 5303 remove_path(TMP_DIR "/s4d1/s1d42/f5"); 5304 5305 cleanup_layout(_metadata); 5306 } 5307 5308 FIXTURE_VARIANT(layout4_disconnected_leafs) 5309 { 5310 /* 5311 * Parent of the bind mount source. It should always be ignored when 5312 * testing against files under the s1d41 or s1d42 disconnected directories. 5313 */ 5314 const __u64 allowed_s1d1; 5315 /* 5316 * Source of bind mount (to s2d2). It should always be enforced when 5317 * testing against files under the s1d41 or s1d42 disconnected directories. 5318 */ 5319 const __u64 allowed_s1d2; 5320 /* 5321 * Original parent of s1d41. It should always be ignored when testing 5322 * against files under the s1d41 disconnected directory. 5323 */ 5324 const __u64 allowed_s1d31; 5325 /* 5326 * Original parent of s1d42. It should always be ignored when testing 5327 * against files under the s1d42 disconnected directory. 5328 */ 5329 const __u64 allowed_s1d32; 5330 /* 5331 * Opened and disconnected source directory. It should always be enforced 5332 * when testing against files under the s1d41 disconnected directory. 5333 */ 5334 const __u64 allowed_s1d41; 5335 /* 5336 * Opened and disconnected source directory. It should always be enforced 5337 * when testing against files under the s1d42 disconnected directory. 5338 */ 5339 const __u64 allowed_s1d42; 5340 /* 5341 * File in the s1d41 disconnected directory. It should always be enforced 5342 * when testing against itself under the s1d41 disconnected directory. 5343 */ 5344 const __u64 allowed_f1; 5345 /* 5346 * File in the s1d41 disconnected directory. It should always be enforced 5347 * when testing against itself under the s1d41 disconnected directory. 5348 */ 5349 const __u64 allowed_f2; 5350 /* 5351 * File in the s1d42 disconnected directory. It should always be enforced 5352 * when testing against itself under the s1d42 disconnected directory. 5353 */ 5354 const __u64 allowed_f3; 5355 /* 5356 * Parent of the bind mount destination. It should always be enforced when 5357 * testing against files under the s1d41 or s1d42 disconnected directories. 5358 */ 5359 const __u64 allowed_s2d1; 5360 /* 5361 * Directory covered by the bind mount. It should always be ignored when 5362 * testing against files under the s1d41 or s1d42 disconnected directories. 5363 */ 5364 const __u64 allowed_s2d2; 5365 /* 5366 * New parent of the renamed s1d41. It should always be ignored when 5367 * testing against files under the s1d41 disconnected directory. 5368 */ 5369 const __u64 allowed_s3d1; 5370 /* 5371 * New parent of the renamed s1d42. It should always be ignored when 5372 * testing against files under the s1d42 disconnected directory. 5373 */ 5374 const __u64 allowed_s4d1; 5375 5376 /* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */ 5377 const int expected_read_result; 5378 /* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */ 5379 const int expected_rename_result; 5380 /* 5381 * Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3, 5382 * RENAME_EXCHANGE). 5383 */ 5384 const int expected_exchange_result; 5385 /* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */ 5386 const int expected_same_dir_rename_result; 5387 }; 5388 5389 /* clang-format off */ 5390 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) { 5391 /* clang-format on */ 5392 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 5393 LANDLOCK_ACCESS_FS_READ_FILE | 5394 LANDLOCK_ACCESS_FS_EXECUTE | 5395 LANDLOCK_ACCESS_FS_MAKE_REG, 5396 .expected_read_result = EACCES, 5397 .expected_same_dir_rename_result = EACCES, 5398 .expected_rename_result = EACCES, 5399 .expected_exchange_result = EACCES, 5400 }; 5401 5402 /* clang-format off */ 5403 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) { 5404 /* clang-format on */ 5405 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5406 .expected_read_result = 0, 5407 .expected_same_dir_rename_result = EACCES, 5408 .expected_rename_result = EACCES, 5409 .expected_exchange_result = EACCES, 5410 }; 5411 5412 /* clang-format off */ 5413 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) { 5414 /* clang-format on */ 5415 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 5416 LANDLOCK_ACCESS_FS_MAKE_REG, 5417 .expected_read_result = 0, 5418 .expected_same_dir_rename_result = 0, 5419 .expected_rename_result = EXDEV, 5420 .expected_exchange_result = EXDEV, 5421 }; 5422 5423 /* clang-format off */ 5424 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) { 5425 /* clang-format on */ 5426 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5427 .expected_read_result = EACCES, 5428 .expected_same_dir_rename_result = 0, 5429 .expected_rename_result = 0, 5430 .expected_exchange_result = 0, 5431 }; 5432 5433 /* clang-format off */ 5434 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) { 5435 /* clang-format on */ 5436 .allowed_s1d31 = LANDLOCK_ACCESS_FS_REFER | 5437 LANDLOCK_ACCESS_FS_READ_FILE | 5438 LANDLOCK_ACCESS_FS_EXECUTE | 5439 LANDLOCK_ACCESS_FS_MAKE_REG, 5440 .allowed_s1d32 = LANDLOCK_ACCESS_FS_REFER | 5441 LANDLOCK_ACCESS_FS_READ_FILE | 5442 LANDLOCK_ACCESS_FS_EXECUTE | 5443 LANDLOCK_ACCESS_FS_MAKE_REG, 5444 .expected_read_result = EACCES, 5445 .expected_same_dir_rename_result = EACCES, 5446 .expected_rename_result = EACCES, 5447 .expected_exchange_result = EACCES, 5448 }; 5449 5450 /* clang-format off */ 5451 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) { 5452 /* clang-format on */ 5453 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5454 LANDLOCK_ACCESS_FS_READ_FILE, 5455 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5456 LANDLOCK_ACCESS_FS_READ_FILE, 5457 .expected_read_result = 0, 5458 .expected_same_dir_rename_result = EACCES, 5459 .expected_rename_result = EACCES, 5460 .expected_exchange_result = EACCES, 5461 }; 5462 5463 /* clang-format off */ 5464 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) { 5465 /* clang-format on */ 5466 .allowed_s1d41 = LANDLOCK_ACCESS_FS_READ_FILE | 5467 LANDLOCK_ACCESS_FS_MAKE_REG, 5468 .allowed_s1d42 = LANDLOCK_ACCESS_FS_READ_FILE | 5469 LANDLOCK_ACCESS_FS_MAKE_REG, 5470 .expected_read_result = 0, 5471 .expected_same_dir_rename_result = 0, 5472 .expected_rename_result = EXDEV, 5473 .expected_exchange_result = EXDEV, 5474 }; 5475 5476 /* clang-format off */ 5477 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) { 5478 /* clang-format on */ 5479 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5480 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5481 .expected_read_result = EACCES, 5482 .expected_same_dir_rename_result = 0, 5483 .expected_rename_result = 0, 5484 .expected_exchange_result = 0, 5485 }; 5486 5487 /* The destination directory has more access right. */ 5488 /* clang-format off */ 5489 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) { 5490 /* clang-format on */ 5491 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5492 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5493 LANDLOCK_ACCESS_FS_MAKE_REG | 5494 LANDLOCK_ACCESS_FS_EXECUTE, 5495 .expected_read_result = EACCES, 5496 .expected_same_dir_rename_result = 0, 5497 /* Access denied. */ 5498 .expected_rename_result = EXDEV, 5499 .expected_exchange_result = EXDEV, 5500 }; 5501 5502 /* The destination directory has less access right. */ 5503 /* clang-format off */ 5504 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) { 5505 /* clang-format on */ 5506 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5507 LANDLOCK_ACCESS_FS_MAKE_REG | 5508 LANDLOCK_ACCESS_FS_EXECUTE, 5509 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5510 .expected_read_result = EACCES, 5511 .expected_same_dir_rename_result = 0, 5512 /* Access allowed. */ 5513 .expected_rename_result = 0, 5514 .expected_exchange_result = EXDEV, 5515 }; 5516 5517 /* clang-format off */ 5518 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) { 5519 /* clang-format on */ 5520 .allowed_s2d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5521 LANDLOCK_ACCESS_FS_MAKE_REG, 5522 .expected_read_result = 0, 5523 .expected_same_dir_rename_result = 0, 5524 .expected_rename_result = EXDEV, 5525 .expected_exchange_result = EXDEV, 5526 }; 5527 5528 /* clang-format off */ 5529 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) { 5530 /* clang-format on */ 5531 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5532 .expected_read_result = 0, 5533 .expected_same_dir_rename_result = EACCES, 5534 .expected_rename_result = EACCES, 5535 .expected_exchange_result = EACCES, 5536 }; 5537 5538 /* clang-format off */ 5539 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) { 5540 /* clang-format on */ 5541 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 5542 LANDLOCK_ACCESS_FS_READ_FILE | 5543 LANDLOCK_ACCESS_FS_MAKE_REG, 5544 .expected_read_result = 0, 5545 .expected_same_dir_rename_result = 0, 5546 .expected_rename_result = 0, 5547 .expected_exchange_result = 0, 5548 }; 5549 5550 /* clang-format off */ 5551 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) { 5552 /* clang-format on */ 5553 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | 5554 LANDLOCK_ACCESS_FS_READ_FILE | 5555 LANDLOCK_ACCESS_FS_EXECUTE | 5556 LANDLOCK_ACCESS_FS_MAKE_REG, 5557 .expected_read_result = EACCES, 5558 .expected_same_dir_rename_result = EACCES, 5559 .expected_rename_result = EACCES, 5560 .expected_exchange_result = EACCES, 5561 }; 5562 5563 /* Tests collect_domain_accesses(). */ 5564 /* clang-format off */ 5565 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) { 5566 /* clang-format on */ 5567 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5568 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5569 .expected_read_result = 0, 5570 .expected_same_dir_rename_result = EACCES, 5571 .expected_rename_result = EACCES, 5572 .expected_exchange_result = EACCES, 5573 }; 5574 5575 /* clang-format off */ 5576 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) { 5577 /* clang-format on */ 5578 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5579 LANDLOCK_ACCESS_FS_MAKE_REG, 5580 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5581 LANDLOCK_ACCESS_FS_MAKE_REG, 5582 .expected_read_result = 0, 5583 .expected_same_dir_rename_result = 0, 5584 .expected_rename_result = EXDEV, 5585 .expected_exchange_result = EXDEV, 5586 }; 5587 5588 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, 5589 s3d1_s4d1_disconnected_rename_even){ 5590 /* clang-format on */ 5591 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5592 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5593 .expected_read_result = EACCES, 5594 .expected_same_dir_rename_result = 0, 5595 .expected_rename_result = 0, 5596 .expected_exchange_result = 0, 5597 }; 5598 5599 /* The destination directory has more access right. */ 5600 /* clang-format off */ 5601 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) { 5602 /* clang-format on */ 5603 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5604 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5605 LANDLOCK_ACCESS_FS_EXECUTE, 5606 .expected_read_result = EACCES, 5607 .expected_same_dir_rename_result = 0, 5608 /* Access denied. */ 5609 .expected_rename_result = EXDEV, 5610 .expected_exchange_result = EXDEV, 5611 }; 5612 5613 /* The destination directory has less access right. */ 5614 /* clang-format off */ 5615 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) { 5616 /* clang-format on */ 5617 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5618 LANDLOCK_ACCESS_FS_EXECUTE, 5619 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5620 .expected_read_result = EACCES, 5621 .expected_same_dir_rename_result = 0, 5622 /* Access allowed. */ 5623 .expected_rename_result = 0, 5624 .expected_exchange_result = EXDEV, 5625 }; 5626 5627 /* clang-format off */ 5628 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) { 5629 /* clang-format on */ 5630 .allowed_f1 = LANDLOCK_ACCESS_FS_READ_FILE, 5631 .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE, 5632 .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE, 5633 .expected_read_result = 0, 5634 .expected_same_dir_rename_result = EACCES, 5635 .expected_rename_result = EACCES, 5636 .expected_exchange_result = EACCES, 5637 }; 5638 5639 TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange) 5640 { 5641 const __u64 handled_access = 5642 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 5643 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 5644 const struct rule rules[] = { 5645 { 5646 .path = TMP_DIR "/s1d1", 5647 .access = variant->allowed_s1d1, 5648 }, 5649 { 5650 .path = TMP_DIR "/s1d1/s1d2", 5651 .access = variant->allowed_s1d2, 5652 }, 5653 { 5654 .path = TMP_DIR "/s1d1/s1d2/s1d31", 5655 .access = variant->allowed_s1d31, 5656 }, 5657 { 5658 .path = TMP_DIR "/s1d1/s1d2/s1d32", 5659 .access = variant->allowed_s1d32, 5660 }, 5661 { 5662 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5663 .access = variant->allowed_s1d41, 5664 }, 5665 { 5666 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5667 .access = variant->allowed_s1d42, 5668 }, 5669 { 5670 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1", 5671 .access = variant->allowed_f1, 5672 }, 5673 { 5674 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2", 5675 .access = variant->allowed_f2, 5676 }, 5677 { 5678 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3", 5679 .access = variant->allowed_f3, 5680 }, 5681 { 5682 .path = TMP_DIR "/s2d1", 5683 .access = variant->allowed_s2d1, 5684 }, 5685 /* s2d2_fd */ 5686 { 5687 .path = TMP_DIR "/s3d1", 5688 .access = variant->allowed_s3d1, 5689 }, 5690 { 5691 .path = TMP_DIR "/s4d1", 5692 .access = variant->allowed_s4d1, 5693 }, 5694 {}, 5695 }; 5696 int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; 5697 5698 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 5699 ASSERT_LE(0, ruleset_fd); 5700 5701 /* Adds rule for the covered directory. */ 5702 if (variant->allowed_s2d2) { 5703 ASSERT_EQ(0, landlock_add_rule( 5704 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 5705 &(struct landlock_path_beneath_attr){ 5706 .parent_fd = self->s2d2_fd, 5707 .allowed_access = 5708 variant->allowed_s2d2, 5709 }, 5710 0)); 5711 } 5712 EXPECT_EQ(0, close(self->s2d2_fd)); 5713 5714 s1d41_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d31/s1d41", 5715 O_DIRECTORY | O_PATH | O_CLOEXEC); 5716 ASSERT_LE(0, s1d41_bind_fd); 5717 s1d42_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d32/s1d42", 5718 O_DIRECTORY | O_PATH | O_CLOEXEC); 5719 ASSERT_LE(0, s1d42_bind_fd); 5720 5721 /* Disconnects and checks source and destination directories. */ 5722 EXPECT_EQ(0, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5723 EXPECT_EQ(0, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5724 /* Renames to make it accessible through s3d1/s1d41 */ 5725 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5726 AT_FDCWD, TMP_DIR "/s3d1/s1d41")); 5727 /* Renames to make it accessible through s4d1/s1d42 */ 5728 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5729 AT_FDCWD, TMP_DIR "/s4d1/s1d42")); 5730 EXPECT_EQ(ENOENT, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5731 EXPECT_EQ(ENOENT, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5732 5733 enforce_ruleset(_metadata, ruleset_fd); 5734 EXPECT_EQ(0, close(ruleset_fd)); 5735 5736 EXPECT_EQ(variant->expected_read_result, 5737 test_open_rel(s1d41_bind_fd, "f1", O_RDONLY)); 5738 5739 EXPECT_EQ(variant->expected_rename_result, 5740 test_renameat(s1d41_bind_fd, "f1", s1d42_bind_fd, "f1")); 5741 EXPECT_EQ(variant->expected_exchange_result, 5742 test_exchangeat(s1d41_bind_fd, "f2", s1d42_bind_fd, "f3")); 5743 5744 EXPECT_EQ(variant->expected_same_dir_rename_result, 5745 test_renameat(s1d42_bind_fd, "f4", s1d42_bind_fd, "f5")); 5746 } 5747 5748 /* 5749 * layout5_disconnected_branch before rename: 5750 * 5751 * tmp 5752 * ├── s1d1 5753 * │ └── s1d2 [source of the first bind mount] 5754 * │ └── s1d3 5755 * │ ├── s1d41 5756 * │ │ ├── f1 5757 * │ │ └── f2 5758 * │ └── s1d42 5759 * │ ├── f3 5760 * │ └── f4 5761 * ├── s2d1 5762 * │ └── s2d2 [source of the second bind mount] 5763 * │ └── s2d3 5764 * │ └── s2d4 [first s1d2 bind mount] 5765 * │ └── s1d3 5766 * │ ├── s1d41 5767 * │ │ ├── f1 5768 * │ │ └── f2 5769 * │ └── s1d42 5770 * │ ├── f3 5771 * │ └── f4 5772 * ├── s3d1 5773 * │ └── s3d2 [second s2d2 bind mount] 5774 * │ └── s2d3 5775 * │ └── s2d4 [first s1d2 bind mount] 5776 * │ └── s1d3 5777 * │ ├── s1d41 5778 * │ │ ├── f1 5779 * │ │ └── f2 5780 * │ └── s1d42 5781 * │ ├── f3 5782 * │ └── f4 5783 * └── s4d1 5784 * 5785 * After rename: 5786 * 5787 * tmp 5788 * ├── s1d1 5789 * │ └── s1d2 [source of the first bind mount] 5790 * │ └── s1d3 5791 * │ ├── s1d41 5792 * │ │ ├── f1 5793 * │ │ └── f2 5794 * │ └── s1d42 5795 * │ ├── f3 5796 * │ └── f4 5797 * ├── s2d1 5798 * │ └── s2d2 [source of the second bind mount] 5799 * ├── s3d1 5800 * │ └── s3d2 [second s2d2 bind mount] 5801 * └── s4d1 5802 * └── s2d3 [renamed here] 5803 * └── s2d4 [first s1d2 bind mount] 5804 * └── s1d3 5805 * ├── s1d41 5806 * │ ├── f1 5807 * │ └── f2 5808 * └── s1d42 5809 * ├── f3 5810 * └── f4 5811 * 5812 * Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor: 5813 * 1. first bind mount: s1d3 -> s1d2 5814 * 2. second bind mount: s2d3 5815 * 3. tmp mount: s4d1 -> tmp [disconnected branch] 5816 * 4. second bind mount: s2d2 5817 * 5. tmp mount: s3d1 -> tmp 5818 * 6. parent mounts: [...] -> / 5819 * 5820 * The s4d1 directory is evaluated even if it is not in the s2d2 mount. 5821 */ 5822 5823 /* clang-format off */ 5824 FIXTURE(layout5_disconnected_branch) { 5825 int s2d4_fd, s3d2_fd; 5826 }; 5827 /* clang-format on */ 5828 5829 FIXTURE_SETUP(layout5_disconnected_branch) 5830 { 5831 prepare_layout(_metadata); 5832 5833 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 5834 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 5835 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 5836 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 5837 create_directory(_metadata, TMP_DIR "/s2d1/s2d2/s2d3/s2d4"); 5838 create_directory(_metadata, TMP_DIR "/s3d1/s3d2"); 5839 create_directory(_metadata, TMP_DIR "/s4d1"); 5840 5841 self->s2d4_fd = open(TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 5842 O_DIRECTORY | O_PATH | O_CLOEXEC); 5843 ASSERT_LE(0, self->s2d4_fd); 5844 5845 self->s3d2_fd = 5846 open(TMP_DIR "/s3d1/s3d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 5847 ASSERT_LE(0, self->s3d2_fd); 5848 5849 set_cap(_metadata, CAP_SYS_ADMIN); 5850 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 5851 NULL, MS_BIND, NULL)); 5852 ASSERT_EQ(0, mount(TMP_DIR "/s2d1/s2d2", TMP_DIR "/s3d1/s3d2", NULL, 5853 MS_BIND | MS_REC, NULL)); 5854 clear_cap(_metadata, CAP_SYS_ADMIN); 5855 } 5856 5857 FIXTURE_TEARDOWN_PARENT(layout5_disconnected_branch) 5858 { 5859 /* Bind mounts are handled by namespace lifetime. */ 5860 5861 /* Removes files after renames. */ 5862 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 5863 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 5864 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1"); 5865 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 5866 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 5867 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5"); 5868 5869 cleanup_layout(_metadata); 5870 } 5871 5872 FIXTURE_VARIANT(layout5_disconnected_branch) 5873 { 5874 /* 5875 * Parent of all files. It should always be enforced when testing against 5876 * files under the s1d41 or s1d42 disconnected directories. 5877 */ 5878 const __u64 allowed_base; 5879 /* 5880 * Parent of the first bind mount source. It should always be ignored when 5881 * testing against files under the s1d41 or s1d42 disconnected directories. 5882 */ 5883 const __u64 allowed_s1d1; 5884 const __u64 allowed_s1d2; 5885 const __u64 allowed_s1d3; 5886 const __u64 allowed_s2d1; 5887 const __u64 allowed_s2d2; 5888 const __u64 allowed_s2d3; 5889 const __u64 allowed_s2d4; 5890 const __u64 allowed_s3d1; 5891 const __u64 allowed_s3d2; 5892 const __u64 allowed_s4d1; 5893 5894 /* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */ 5895 const int expected_read_result; 5896 /* 5897 * Expected result of the call to renameat([fd:s1d3]/s1d41/f1, 5898 * [fd:s1d3]/s1d42/f1). 5899 */ 5900 const int expected_rename_result; 5901 /* 5902 * Expected result of the call to renameat([fd:s1d3]/s1d41/f2, 5903 * [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE). 5904 */ 5905 const int expected_exchange_result; 5906 /* 5907 * Expected result of the call to renameat([fd:s1d3]/s1d42/f4, 5908 * [fd:s1d3]/s1d42/f5). 5909 */ 5910 const int expected_same_dir_rename_result; 5911 }; 5912 5913 /* clang-format off */ 5914 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) { 5915 /* clang-format on */ 5916 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 5917 LANDLOCK_ACCESS_FS_READ_FILE | 5918 LANDLOCK_ACCESS_FS_EXECUTE | 5919 LANDLOCK_ACCESS_FS_MAKE_REG, 5920 .expected_read_result = EACCES, 5921 .expected_same_dir_rename_result = EACCES, 5922 .expected_rename_result = EACCES, 5923 .expected_exchange_result = EACCES, 5924 }; 5925 5926 /* clang-format off */ 5927 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) { 5928 /* clang-format on */ 5929 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5930 .expected_read_result = 0, 5931 .expected_same_dir_rename_result = EACCES, 5932 .expected_rename_result = EACCES, 5933 .expected_exchange_result = EACCES, 5934 }; 5935 5936 /* clang-format off */ 5937 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) { 5938 /* clang-format on */ 5939 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 5940 LANDLOCK_ACCESS_FS_MAKE_REG, 5941 .expected_read_result = 0, 5942 .expected_same_dir_rename_result = 0, 5943 .expected_rename_result = EXDEV, 5944 .expected_exchange_result = EXDEV, 5945 }; 5946 5947 /* clang-format off */ 5948 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) { 5949 /* clang-format on */ 5950 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5951 .expected_read_result = EACCES, 5952 .expected_same_dir_rename_result = 0, 5953 .expected_rename_result = 0, 5954 .expected_exchange_result = 0, 5955 }; 5956 5957 /* clang-format off */ 5958 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) { 5959 /* clang-format on */ 5960 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5961 .expected_read_result = 0, 5962 .expected_same_dir_rename_result = EACCES, 5963 .expected_rename_result = EACCES, 5964 .expected_exchange_result = EACCES, 5965 }; 5966 5967 /* clang-format off */ 5968 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) { 5969 /* clang-format on */ 5970 .allowed_s1d3 = LANDLOCK_ACCESS_FS_READ_FILE | 5971 LANDLOCK_ACCESS_FS_MAKE_REG, 5972 .expected_read_result = 0, 5973 .expected_same_dir_rename_result = 0, 5974 .expected_rename_result = EXDEV, 5975 .expected_exchange_result = EXDEV, 5976 }; 5977 5978 /* clang-format off */ 5979 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) { 5980 /* clang-format on */ 5981 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5982 .expected_read_result = EACCES, 5983 .expected_same_dir_rename_result = 0, 5984 .expected_rename_result = 0, 5985 .expected_exchange_result = 0, 5986 }; 5987 5988 /* clang-format off */ 5989 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) { 5990 /* clang-format on */ 5991 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | 5992 LANDLOCK_ACCESS_FS_READ_FILE | 5993 LANDLOCK_ACCESS_FS_EXECUTE | 5994 LANDLOCK_ACCESS_FS_MAKE_REG, 5995 .expected_read_result = 0, 5996 .expected_same_dir_rename_result = 0, 5997 .expected_rename_result = 0, 5998 .expected_exchange_result = 0, 5999 }; 6000 6001 /* clang-format off */ 6002 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) { 6003 /* clang-format on */ 6004 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 6005 LANDLOCK_ACCESS_FS_READ_FILE | 6006 LANDLOCK_ACCESS_FS_EXECUTE | 6007 LANDLOCK_ACCESS_FS_MAKE_REG, 6008 .expected_read_result = EACCES, 6009 .expected_same_dir_rename_result = EACCES, 6010 .expected_rename_result = EACCES, 6011 .expected_exchange_result = EACCES, 6012 }; 6013 6014 /* clang-format off */ 6015 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) { 6016 /* clang-format on */ 6017 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6018 .expected_read_result = 0, 6019 .expected_same_dir_rename_result = EACCES, 6020 .expected_rename_result = EACCES, 6021 .expected_exchange_result = EACCES, 6022 }; 6023 6024 /* clang-format off */ 6025 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) { 6026 /* clang-format on */ 6027 .allowed_s2d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6028 LANDLOCK_ACCESS_FS_MAKE_REG, 6029 .expected_read_result = 0, 6030 .expected_same_dir_rename_result = 0, 6031 .expected_rename_result = EXDEV, 6032 .expected_exchange_result = EXDEV, 6033 }; 6034 6035 /* clang-format off */ 6036 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) { 6037 /* clang-format on */ 6038 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6039 .expected_read_result = EACCES, 6040 .expected_same_dir_rename_result = 0, 6041 .expected_rename_result = 0, 6042 .expected_exchange_result = 0, 6043 }; 6044 6045 /* clang-format off */ 6046 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) { 6047 /* clang-format on */ 6048 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6049 .expected_read_result = 0, 6050 .expected_same_dir_rename_result = EACCES, 6051 .expected_rename_result = EACCES, 6052 .expected_exchange_result = EACCES, 6053 }; 6054 6055 /* clang-format off */ 6056 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) { 6057 /* clang-format on */ 6058 .allowed_s2d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6059 LANDLOCK_ACCESS_FS_MAKE_REG, 6060 .expected_read_result = 0, 6061 .expected_same_dir_rename_result = 0, 6062 .expected_rename_result = EXDEV, 6063 .expected_exchange_result = EXDEV, 6064 }; 6065 6066 /* clang-format off */ 6067 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) { 6068 /* clang-format on */ 6069 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6070 .expected_read_result = EACCES, 6071 .expected_same_dir_rename_result = 0, 6072 .expected_rename_result = 0, 6073 .expected_exchange_result = 0, 6074 }; 6075 6076 /* clang-format off */ 6077 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) { 6078 /* clang-format on */ 6079 .allowed_s2d4 = LANDLOCK_ACCESS_FS_REFER | 6080 LANDLOCK_ACCESS_FS_READ_FILE | 6081 LANDLOCK_ACCESS_FS_EXECUTE | 6082 LANDLOCK_ACCESS_FS_MAKE_REG, 6083 .expected_read_result = EACCES, 6084 .expected_same_dir_rename_result = EACCES, 6085 .expected_rename_result = EACCES, 6086 .expected_exchange_result = EACCES, 6087 }; 6088 6089 /* clang-format off */ 6090 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) { 6091 /* clang-format on */ 6092 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6093 .expected_read_result = 0, 6094 .expected_same_dir_rename_result = EACCES, 6095 .expected_rename_result = EACCES, 6096 .expected_exchange_result = EACCES, 6097 }; 6098 6099 /* clang-format off */ 6100 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) { 6101 /* clang-format on */ 6102 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6103 LANDLOCK_ACCESS_FS_MAKE_REG, 6104 .expected_read_result = 0, 6105 .expected_same_dir_rename_result = 0, 6106 .expected_rename_result = EXDEV, 6107 .expected_exchange_result = EXDEV, 6108 }; 6109 6110 /* clang-format off */ 6111 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) { 6112 /* clang-format on */ 6113 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6114 .expected_read_result = EACCES, 6115 .expected_same_dir_rename_result = 0, 6116 .expected_rename_result = 0, 6117 .expected_exchange_result = 0, 6118 }; 6119 6120 /* clang-format off */ 6121 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) { 6122 /* clang-format on */ 6123 .allowed_s3d2 = LANDLOCK_ACCESS_FS_REFER | 6124 LANDLOCK_ACCESS_FS_READ_FILE | 6125 LANDLOCK_ACCESS_FS_EXECUTE | 6126 LANDLOCK_ACCESS_FS_MAKE_REG, 6127 .expected_read_result = EACCES, 6128 .expected_same_dir_rename_result = EACCES, 6129 .expected_rename_result = EACCES, 6130 .expected_exchange_result = EACCES, 6131 }; 6132 6133 /* clang-format off */ 6134 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) { 6135 /* clang-format on */ 6136 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6137 .expected_read_result = 0, 6138 .expected_same_dir_rename_result = EACCES, 6139 .expected_rename_result = EACCES, 6140 .expected_exchange_result = EACCES, 6141 }; 6142 6143 /* clang-format off */ 6144 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) { 6145 /* clang-format on */ 6146 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6147 LANDLOCK_ACCESS_FS_MAKE_REG, 6148 .expected_read_result = 0, 6149 .expected_same_dir_rename_result = 0, 6150 .expected_rename_result = EXDEV, 6151 .expected_exchange_result = EXDEV, 6152 }; 6153 6154 /* clang-format off */ 6155 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) { 6156 /* clang-format on */ 6157 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6158 .expected_read_result = EACCES, 6159 .expected_same_dir_rename_result = 0, 6160 .expected_rename_result = 0, 6161 .expected_exchange_result = 0, 6162 }; 6163 6164 TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange) 6165 { 6166 const __u64 handled_access = 6167 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 6168 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 6169 const struct rule rules[] = { 6170 { 6171 .path = TMP_DIR "/s1d1", 6172 .access = variant->allowed_s1d1, 6173 }, 6174 { 6175 .path = TMP_DIR "/s1d1/s1d2", 6176 .access = variant->allowed_s1d2, 6177 }, 6178 { 6179 .path = TMP_DIR "/s1d1/s1d2/s1d3", 6180 .access = variant->allowed_s1d3, 6181 }, 6182 { 6183 .path = TMP_DIR "/s2d1", 6184 .access = variant->allowed_s2d1, 6185 }, 6186 { 6187 .path = TMP_DIR "/s2d1/s2d2", 6188 .access = variant->allowed_s2d2, 6189 }, 6190 { 6191 .path = TMP_DIR "/s2d1/s2d2/s2d3", 6192 .access = variant->allowed_s2d3, 6193 }, 6194 /* s2d4_fd */ 6195 { 6196 .path = TMP_DIR "/s3d1", 6197 .access = variant->allowed_s3d1, 6198 }, 6199 /* s3d2_fd */ 6200 { 6201 .path = TMP_DIR "/s4d1", 6202 .access = variant->allowed_s4d1, 6203 }, 6204 {}, 6205 }; 6206 int ruleset_fd, s1d3_bind_fd; 6207 6208 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 6209 ASSERT_LE(0, ruleset_fd); 6210 6211 /* Adds rules for the covered directories. */ 6212 if (variant->allowed_s2d4) { 6213 ASSERT_EQ(0, landlock_add_rule( 6214 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6215 &(struct landlock_path_beneath_attr){ 6216 .parent_fd = self->s2d4_fd, 6217 .allowed_access = 6218 variant->allowed_s2d4, 6219 }, 6220 0)); 6221 } 6222 EXPECT_EQ(0, close(self->s2d4_fd)); 6223 6224 if (variant->allowed_s3d2) { 6225 ASSERT_EQ(0, landlock_add_rule( 6226 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6227 &(struct landlock_path_beneath_attr){ 6228 .parent_fd = self->s3d2_fd, 6229 .allowed_access = 6230 variant->allowed_s3d2, 6231 }, 6232 0)); 6233 } 6234 EXPECT_EQ(0, close(self->s3d2_fd)); 6235 6236 s1d3_bind_fd = open(TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3", 6237 O_DIRECTORY | O_PATH | O_CLOEXEC); 6238 ASSERT_LE(0, s1d3_bind_fd); 6239 6240 /* Disconnects and checks source and destination directories. */ 6241 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6242 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6243 /* Renames to make it accessible through s3d1/s1d41 */ 6244 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s2d1/s2d2/s2d3", 6245 AT_FDCWD, TMP_DIR "/s4d1/s2d3")); 6246 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6247 EXPECT_EQ(ENOENT, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6248 6249 enforce_ruleset(_metadata, ruleset_fd); 6250 EXPECT_EQ(0, close(ruleset_fd)); 6251 6252 EXPECT_EQ(variant->expected_read_result, 6253 test_open_rel(s1d3_bind_fd, "s1d41/f1", O_RDONLY)); 6254 6255 EXPECT_EQ(variant->expected_rename_result, 6256 test_renameat(s1d3_bind_fd, "s1d41/f1", s1d3_bind_fd, 6257 "s1d42/f1")); 6258 EXPECT_EQ(variant->expected_exchange_result, 6259 test_exchangeat(s1d3_bind_fd, "s1d41/f2", s1d3_bind_fd, 6260 "s1d42/f3")); 6261 6262 EXPECT_EQ(variant->expected_same_dir_rename_result, 6263 test_renameat(s1d3_bind_fd, "s1d42/f4", s1d3_bind_fd, 6264 "s1d42/f5")); 6265 } 6266 6267 #define LOWER_BASE TMP_DIR "/lower" 6268 #define LOWER_DATA LOWER_BASE "/data" 6269 static const char lower_fl1[] = LOWER_DATA "/fl1"; 6270 static const char lower_dl1[] = LOWER_DATA "/dl1"; 6271 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 6272 static const char lower_fo1[] = LOWER_DATA "/fo1"; 6273 static const char lower_do1[] = LOWER_DATA "/do1"; 6274 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 6275 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 6276 6277 static const char (*lower_base_files[])[] = { 6278 &lower_fl1, 6279 &lower_fo1, 6280 NULL, 6281 }; 6282 static const char (*lower_base_directories[])[] = { 6283 &lower_dl1, 6284 &lower_do1, 6285 NULL, 6286 }; 6287 static const char (*lower_sub_files[])[] = { 6288 &lower_dl1_fl2, 6289 &lower_do1_fo2, 6290 &lower_do1_fl3, 6291 NULL, 6292 }; 6293 6294 #define UPPER_BASE TMP_DIR "/upper" 6295 #define UPPER_DATA UPPER_BASE "/data" 6296 #define UPPER_WORK UPPER_BASE "/work" 6297 static const char upper_fu1[] = UPPER_DATA "/fu1"; 6298 static const char upper_du1[] = UPPER_DATA "/du1"; 6299 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 6300 static const char upper_fo1[] = UPPER_DATA "/fo1"; 6301 static const char upper_do1[] = UPPER_DATA "/do1"; 6302 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 6303 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 6304 6305 static const char (*upper_base_files[])[] = { 6306 &upper_fu1, 6307 &upper_fo1, 6308 NULL, 6309 }; 6310 static const char (*upper_base_directories[])[] = { 6311 &upper_du1, 6312 &upper_do1, 6313 NULL, 6314 }; 6315 static const char (*upper_sub_files[])[] = { 6316 &upper_du1_fu2, 6317 &upper_do1_fo2, 6318 &upper_do1_fu3, 6319 NULL, 6320 }; 6321 6322 #define MERGE_BASE TMP_DIR "/merge" 6323 #define MERGE_DATA MERGE_BASE "/data" 6324 static const char merge_fl1[] = MERGE_DATA "/fl1"; 6325 static const char merge_dl1[] = MERGE_DATA "/dl1"; 6326 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 6327 static const char merge_fu1[] = MERGE_DATA "/fu1"; 6328 static const char merge_du1[] = MERGE_DATA "/du1"; 6329 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 6330 static const char merge_fo1[] = MERGE_DATA "/fo1"; 6331 static const char merge_do1[] = MERGE_DATA "/do1"; 6332 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 6333 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 6334 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 6335 6336 static const char (*merge_base_files[])[] = { 6337 &merge_fl1, 6338 &merge_fu1, 6339 &merge_fo1, 6340 NULL, 6341 }; 6342 static const char (*merge_base_directories[])[] = { 6343 &merge_dl1, 6344 &merge_du1, 6345 &merge_do1, 6346 NULL, 6347 }; 6348 static const char (*merge_sub_files[])[] = { 6349 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 6350 &merge_do1_fl3, &merge_do1_fu3, NULL, 6351 }; 6352 6353 /* 6354 * layout2_overlay hierarchy: 6355 * 6356 * tmp 6357 * ├── lower 6358 * │ └── data 6359 * │ ├── dl1 6360 * │ │ └── fl2 6361 * │ ├── do1 6362 * │ │ ├── fl3 6363 * │ │ └── fo2 6364 * │ ├── fl1 6365 * │ └── fo1 6366 * ├── merge 6367 * │ └── data 6368 * │ ├── dl1 6369 * │ │ └── fl2 6370 * │ ├── do1 6371 * │ │ ├── fl3 6372 * │ │ ├── fo2 6373 * │ │ └── fu3 6374 * │ ├── du1 6375 * │ │ └── fu2 6376 * │ ├── fl1 6377 * │ ├── fo1 6378 * │ └── fu1 6379 * └── upper 6380 * ├── data 6381 * │ ├── do1 6382 * │ │ ├── fo2 6383 * │ │ └── fu3 6384 * │ ├── du1 6385 * │ │ └── fu2 6386 * │ ├── fo1 6387 * │ └── fu1 6388 * └── work 6389 * └── work 6390 */ 6391 6392 FIXTURE(layout2_overlay) 6393 { 6394 bool skip_test; 6395 }; 6396 6397 FIXTURE_SETUP(layout2_overlay) 6398 { 6399 if (!supports_filesystem("overlay")) { 6400 self->skip_test = true; 6401 SKIP(return, "overlayfs is not supported (setup)"); 6402 } 6403 6404 prepare_layout(_metadata); 6405 6406 create_directory(_metadata, LOWER_BASE); 6407 set_cap(_metadata, CAP_SYS_ADMIN); 6408 /* Creates tmpfs mount points to get deterministic overlayfs. */ 6409 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 6410 clear_cap(_metadata, CAP_SYS_ADMIN); 6411 create_file(_metadata, lower_fl1); 6412 create_file(_metadata, lower_dl1_fl2); 6413 create_file(_metadata, lower_fo1); 6414 create_file(_metadata, lower_do1_fo2); 6415 create_file(_metadata, lower_do1_fl3); 6416 6417 create_directory(_metadata, UPPER_BASE); 6418 set_cap(_metadata, CAP_SYS_ADMIN); 6419 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 6420 clear_cap(_metadata, CAP_SYS_ADMIN); 6421 create_file(_metadata, upper_fu1); 6422 create_file(_metadata, upper_du1_fu2); 6423 create_file(_metadata, upper_fo1); 6424 create_file(_metadata, upper_do1_fo2); 6425 create_file(_metadata, upper_do1_fu3); 6426 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 6427 6428 create_directory(_metadata, MERGE_DATA); 6429 set_cap(_metadata, CAP_SYS_ADMIN); 6430 set_cap(_metadata, CAP_DAC_OVERRIDE); 6431 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 6432 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 6433 ",workdir=" UPPER_WORK)); 6434 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6435 clear_cap(_metadata, CAP_SYS_ADMIN); 6436 } 6437 6438 FIXTURE_TEARDOWN_PARENT(layout2_overlay) 6439 { 6440 if (self->skip_test) 6441 SKIP(return, "overlayfs is not supported (teardown)"); 6442 6443 EXPECT_EQ(0, remove_path(lower_do1_fl3)); 6444 EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 6445 EXPECT_EQ(0, remove_path(lower_fl1)); 6446 EXPECT_EQ(0, remove_path(lower_do1_fo2)); 6447 EXPECT_EQ(0, remove_path(lower_fo1)); 6448 6449 /* umount(LOWER_BASE)) is handled by namespace lifetime. */ 6450 EXPECT_EQ(0, remove_path(LOWER_BASE)); 6451 6452 EXPECT_EQ(0, remove_path(upper_do1_fu3)); 6453 EXPECT_EQ(0, remove_path(upper_du1_fu2)); 6454 EXPECT_EQ(0, remove_path(upper_fu1)); 6455 EXPECT_EQ(0, remove_path(upper_do1_fo2)); 6456 EXPECT_EQ(0, remove_path(upper_fo1)); 6457 EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 6458 6459 /* umount(UPPER_BASE)) is handled by namespace lifetime. */ 6460 EXPECT_EQ(0, remove_path(UPPER_BASE)); 6461 6462 /* umount(MERGE_DATA)) is handled by namespace lifetime. */ 6463 EXPECT_EQ(0, remove_path(MERGE_DATA)); 6464 6465 cleanup_layout(_metadata); 6466 } 6467 6468 TEST_F_FORK(layout2_overlay, no_restriction) 6469 { 6470 if (self->skip_test) 6471 SKIP(return, "overlayfs is not supported (test)"); 6472 6473 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 6474 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 6475 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 6476 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 6477 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 6478 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 6479 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 6480 6481 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 6482 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 6483 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 6484 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 6485 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 6486 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 6487 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 6488 6489 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 6490 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 6491 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 6492 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 6493 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 6494 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 6495 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 6496 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 6497 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 6498 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 6499 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 6500 } 6501 6502 #define for_each_path(path_list, path_entry, i) \ 6503 for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 6504 path_entry = *path_list[++i]) 6505 6506 TEST_F_FORK(layout2_overlay, same_content_different_file) 6507 { 6508 /* Sets access right on parent directories of both layers. */ 6509 const struct rule layer1_base[] = { 6510 { 6511 .path = LOWER_BASE, 6512 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6513 }, 6514 { 6515 .path = UPPER_BASE, 6516 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6517 }, 6518 { 6519 .path = MERGE_BASE, 6520 .access = ACCESS_RW, 6521 }, 6522 {}, 6523 }; 6524 const struct rule layer2_data[] = { 6525 { 6526 .path = LOWER_DATA, 6527 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6528 }, 6529 { 6530 .path = UPPER_DATA, 6531 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6532 }, 6533 { 6534 .path = MERGE_DATA, 6535 .access = ACCESS_RW, 6536 }, 6537 {}, 6538 }; 6539 /* Sets access right on directories inside both layers. */ 6540 const struct rule layer3_subdirs[] = { 6541 { 6542 .path = lower_dl1, 6543 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6544 }, 6545 { 6546 .path = lower_do1, 6547 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6548 }, 6549 { 6550 .path = upper_du1, 6551 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6552 }, 6553 { 6554 .path = upper_do1, 6555 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6556 }, 6557 { 6558 .path = merge_dl1, 6559 .access = ACCESS_RW, 6560 }, 6561 { 6562 .path = merge_du1, 6563 .access = ACCESS_RW, 6564 }, 6565 { 6566 .path = merge_do1, 6567 .access = ACCESS_RW, 6568 }, 6569 {}, 6570 }; 6571 /* Tighten access rights to the files. */ 6572 const struct rule layer4_files[] = { 6573 { 6574 .path = lower_dl1_fl2, 6575 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6576 }, 6577 { 6578 .path = lower_do1_fo2, 6579 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6580 }, 6581 { 6582 .path = lower_do1_fl3, 6583 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6584 }, 6585 { 6586 .path = upper_du1_fu2, 6587 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6588 }, 6589 { 6590 .path = upper_do1_fo2, 6591 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6592 }, 6593 { 6594 .path = upper_do1_fu3, 6595 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6596 }, 6597 { 6598 .path = merge_dl1_fl2, 6599 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6600 LANDLOCK_ACCESS_FS_WRITE_FILE, 6601 }, 6602 { 6603 .path = merge_du1_fu2, 6604 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6605 LANDLOCK_ACCESS_FS_WRITE_FILE, 6606 }, 6607 { 6608 .path = merge_do1_fo2, 6609 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6610 LANDLOCK_ACCESS_FS_WRITE_FILE, 6611 }, 6612 { 6613 .path = merge_do1_fl3, 6614 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6615 LANDLOCK_ACCESS_FS_WRITE_FILE, 6616 }, 6617 { 6618 .path = merge_do1_fu3, 6619 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6620 LANDLOCK_ACCESS_FS_WRITE_FILE, 6621 }, 6622 {}, 6623 }; 6624 const struct rule layer5_merge_only[] = { 6625 { 6626 .path = MERGE_DATA, 6627 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6628 LANDLOCK_ACCESS_FS_WRITE_FILE, 6629 }, 6630 {}, 6631 }; 6632 int ruleset_fd; 6633 size_t i; 6634 const char *path_entry; 6635 6636 if (self->skip_test) 6637 SKIP(return, "overlayfs is not supported (test)"); 6638 6639 /* Sets rules on base directories (i.e. outside overlay scope). */ 6640 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 6641 ASSERT_LE(0, ruleset_fd); 6642 enforce_ruleset(_metadata, ruleset_fd); 6643 ASSERT_EQ(0, close(ruleset_fd)); 6644 6645 /* Checks lower layer. */ 6646 for_each_path(lower_base_files, path_entry, i) { 6647 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6648 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6649 } 6650 for_each_path(lower_base_directories, path_entry, i) { 6651 ASSERT_EQ(EACCES, 6652 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6653 } 6654 for_each_path(lower_sub_files, path_entry, i) { 6655 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6656 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6657 } 6658 /* Checks upper layer. */ 6659 for_each_path(upper_base_files, path_entry, i) { 6660 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6661 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6662 } 6663 for_each_path(upper_base_directories, path_entry, i) { 6664 ASSERT_EQ(EACCES, 6665 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6666 } 6667 for_each_path(upper_sub_files, path_entry, i) { 6668 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6669 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6670 } 6671 /* 6672 * Checks that access rights are independent from the lower and upper 6673 * layers: write access to upper files viewed through the merge point 6674 * is still allowed, and write access to lower file viewed (and copied) 6675 * through the merge point is still allowed. 6676 */ 6677 for_each_path(merge_base_files, path_entry, i) { 6678 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6679 } 6680 for_each_path(merge_base_directories, path_entry, i) { 6681 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6682 } 6683 for_each_path(merge_sub_files, path_entry, i) { 6684 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6685 } 6686 6687 /* Sets rules on data directories (i.e. inside overlay scope). */ 6688 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 6689 ASSERT_LE(0, ruleset_fd); 6690 enforce_ruleset(_metadata, ruleset_fd); 6691 ASSERT_EQ(0, close(ruleset_fd)); 6692 6693 /* Checks merge. */ 6694 for_each_path(merge_base_files, path_entry, i) { 6695 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6696 } 6697 for_each_path(merge_base_directories, path_entry, i) { 6698 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6699 } 6700 for_each_path(merge_sub_files, path_entry, i) { 6701 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6702 } 6703 6704 /* Same checks with tighter rules. */ 6705 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 6706 ASSERT_LE(0, ruleset_fd); 6707 enforce_ruleset(_metadata, ruleset_fd); 6708 ASSERT_EQ(0, close(ruleset_fd)); 6709 6710 /* Checks changes for lower layer. */ 6711 for_each_path(lower_base_files, path_entry, i) { 6712 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6713 } 6714 /* Checks changes for upper layer. */ 6715 for_each_path(upper_base_files, path_entry, i) { 6716 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6717 } 6718 /* Checks all merge accesses. */ 6719 for_each_path(merge_base_files, path_entry, i) { 6720 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6721 } 6722 for_each_path(merge_base_directories, path_entry, i) { 6723 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6724 } 6725 for_each_path(merge_sub_files, path_entry, i) { 6726 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6727 } 6728 6729 /* Sets rules directly on overlayed files. */ 6730 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 6731 ASSERT_LE(0, ruleset_fd); 6732 enforce_ruleset(_metadata, ruleset_fd); 6733 ASSERT_EQ(0, close(ruleset_fd)); 6734 6735 /* Checks unchanged accesses on lower layer. */ 6736 for_each_path(lower_sub_files, path_entry, i) { 6737 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6738 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6739 } 6740 /* Checks unchanged accesses on upper layer. */ 6741 for_each_path(upper_sub_files, path_entry, i) { 6742 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6743 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6744 } 6745 /* Checks all merge accesses. */ 6746 for_each_path(merge_base_files, path_entry, i) { 6747 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6748 } 6749 for_each_path(merge_base_directories, path_entry, i) { 6750 ASSERT_EQ(EACCES, 6751 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6752 } 6753 for_each_path(merge_sub_files, path_entry, i) { 6754 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6755 } 6756 6757 /* Only allowes access to the merge hierarchy. */ 6758 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 6759 ASSERT_LE(0, ruleset_fd); 6760 enforce_ruleset(_metadata, ruleset_fd); 6761 ASSERT_EQ(0, close(ruleset_fd)); 6762 6763 /* Checks new accesses on lower layer. */ 6764 for_each_path(lower_sub_files, path_entry, i) { 6765 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6766 } 6767 /* Checks new accesses on upper layer. */ 6768 for_each_path(upper_sub_files, path_entry, i) { 6769 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6770 } 6771 /* Checks all merge accesses. */ 6772 for_each_path(merge_base_files, path_entry, i) { 6773 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6774 } 6775 for_each_path(merge_base_directories, path_entry, i) { 6776 ASSERT_EQ(EACCES, 6777 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6778 } 6779 for_each_path(merge_sub_files, path_entry, i) { 6780 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6781 } 6782 } 6783 6784 FIXTURE(layout3_fs) 6785 { 6786 bool has_created_dir; 6787 bool has_created_file; 6788 bool skip_test; 6789 }; 6790 6791 FIXTURE_VARIANT(layout3_fs) 6792 { 6793 const struct mnt_opt mnt; 6794 const char *const file_path; 6795 unsigned int cwd_fs_magic; 6796 }; 6797 6798 /* clang-format off */ 6799 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 6800 /* clang-format on */ 6801 .mnt = { 6802 .type = "tmpfs", 6803 .data = MNT_TMP_DATA, 6804 }, 6805 .file_path = file1_s1d1, 6806 }; 6807 6808 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 6809 .mnt = { 6810 .type = "ramfs", 6811 .data = "mode=700", 6812 }, 6813 .file_path = TMP_DIR "/dir/file", 6814 }; 6815 6816 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 6817 .mnt = { 6818 .type = "cgroup2", 6819 }, 6820 .file_path = TMP_DIR "/test/cgroup.procs", 6821 }; 6822 6823 FIXTURE_VARIANT_ADD(layout3_fs, proc) { 6824 .mnt = { 6825 .type = "proc", 6826 }, 6827 .file_path = TMP_DIR "/self/status", 6828 }; 6829 6830 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 6831 .mnt = { 6832 .type = "sysfs", 6833 }, 6834 .file_path = TMP_DIR "/kernel/notes", 6835 }; 6836 6837 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 6838 .mnt = { 6839 .source = TMP_DIR, 6840 .flags = MS_BIND, 6841 }, 6842 .file_path = TMP_DIR "/dir/file", 6843 .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 6844 }; 6845 6846 static char *dirname_alloc(const char *path) 6847 { 6848 char *dup; 6849 6850 if (!path) 6851 return NULL; 6852 6853 dup = strdup(path); 6854 if (!dup) 6855 return NULL; 6856 6857 return dirname(dup); 6858 } 6859 6860 FIXTURE_SETUP(layout3_fs) 6861 { 6862 struct stat statbuf; 6863 char *dir_path = dirname_alloc(variant->file_path); 6864 6865 if (!supports_filesystem(variant->mnt.type) || 6866 !cwd_matches_fs(variant->cwd_fs_magic)) { 6867 self->skip_test = true; 6868 SKIP(return, "this filesystem is not supported (setup)"); 6869 } 6870 6871 prepare_layout_opt(_metadata, &variant->mnt); 6872 6873 /* Creates directory when required. */ 6874 if (stat(dir_path, &statbuf)) { 6875 set_cap(_metadata, CAP_DAC_OVERRIDE); 6876 EXPECT_EQ(0, mkdir(dir_path, 0700)) 6877 { 6878 TH_LOG("Failed to create directory \"%s\": %s", 6879 dir_path, strerror(errno)); 6880 } 6881 self->has_created_dir = true; 6882 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6883 } 6884 6885 /* Creates file when required. */ 6886 if (stat(variant->file_path, &statbuf)) { 6887 int fd; 6888 6889 set_cap(_metadata, CAP_DAC_OVERRIDE); 6890 fd = creat(variant->file_path, 0600); 6891 EXPECT_LE(0, fd) 6892 { 6893 TH_LOG("Failed to create file \"%s\": %s", 6894 variant->file_path, strerror(errno)); 6895 } 6896 EXPECT_EQ(0, close(fd)); 6897 self->has_created_file = true; 6898 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6899 } 6900 6901 free(dir_path); 6902 } 6903 6904 FIXTURE_TEARDOWN_PARENT(layout3_fs) 6905 { 6906 if (self->skip_test) 6907 SKIP(return, "this filesystem is not supported (teardown)"); 6908 6909 if (self->has_created_file) { 6910 set_cap(_metadata, CAP_DAC_OVERRIDE); 6911 /* 6912 * Don't check for error because the file might already 6913 * have been removed (cf. release_inode test). 6914 */ 6915 unlink(variant->file_path); 6916 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6917 } 6918 6919 if (self->has_created_dir) { 6920 char *dir_path = dirname_alloc(variant->file_path); 6921 6922 set_cap(_metadata, CAP_DAC_OVERRIDE); 6923 /* 6924 * Don't check for error because the directory might already 6925 * have been removed (cf. release_inode test). 6926 */ 6927 rmdir(dir_path); 6928 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6929 free(dir_path); 6930 } 6931 6932 cleanup_layout(_metadata); 6933 } 6934 6935 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 6936 FIXTURE_DATA(layout3_fs) * self, 6937 const FIXTURE_VARIANT(layout3_fs) * variant, 6938 const char *const rule_path) 6939 { 6940 const struct rule layer1_allow_read_file[] = { 6941 { 6942 .path = rule_path, 6943 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6944 }, 6945 {}, 6946 }; 6947 const struct landlock_ruleset_attr layer2_deny_everything_attr = { 6948 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 6949 }; 6950 const char *const dev_null_path = "/dev/null"; 6951 int ruleset_fd; 6952 6953 if (self->skip_test) 6954 SKIP(return, "this filesystem is not supported (test)"); 6955 6956 /* Checks without Landlock. */ 6957 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 6958 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 6959 6960 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 6961 layer1_allow_read_file); 6962 EXPECT_LE(0, ruleset_fd); 6963 enforce_ruleset(_metadata, ruleset_fd); 6964 EXPECT_EQ(0, close(ruleset_fd)); 6965 6966 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 6967 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 6968 6969 /* Forbids directory reading. */ 6970 ruleset_fd = 6971 landlock_create_ruleset(&layer2_deny_everything_attr, 6972 sizeof(layer2_deny_everything_attr), 0); 6973 EXPECT_LE(0, ruleset_fd); 6974 enforce_ruleset(_metadata, ruleset_fd); 6975 EXPECT_EQ(0, close(ruleset_fd)); 6976 6977 /* Checks with Landlock and forbidden access. */ 6978 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 6979 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 6980 } 6981 6982 /* Matrix of tests to check file hierarchy evaluation. */ 6983 6984 TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 6985 { 6986 /* The current directory must not be the root for this test. */ 6987 layer3_fs_tag_inode(_metadata, self, variant, "."); 6988 } 6989 6990 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 6991 { 6992 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 6993 } 6994 6995 TEST_F_FORK(layout3_fs, tag_inode_dir_child) 6996 { 6997 char *dir_path = dirname_alloc(variant->file_path); 6998 6999 layer3_fs_tag_inode(_metadata, self, variant, dir_path); 7000 free(dir_path); 7001 } 7002 7003 TEST_F_FORK(layout3_fs, tag_inode_file) 7004 { 7005 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 7006 } 7007 7008 /* Light version of layout1.release_inodes */ 7009 TEST_F_FORK(layout3_fs, release_inodes) 7010 { 7011 const struct rule layer1[] = { 7012 { 7013 .path = TMP_DIR, 7014 .access = LANDLOCK_ACCESS_FS_READ_DIR, 7015 }, 7016 {}, 7017 }; 7018 int ruleset_fd; 7019 7020 if (self->skip_test) 7021 SKIP(return, "this filesystem is not supported (test)"); 7022 7023 /* Clean up for the teardown to not fail. */ 7024 if (self->has_created_file) 7025 EXPECT_EQ(0, remove_path(variant->file_path)); 7026 7027 if (self->has_created_dir) { 7028 char *dir_path = dirname_alloc(variant->file_path); 7029 7030 /* Don't check for error because of cgroup specificities. */ 7031 remove_path(dir_path); 7032 free(dir_path); 7033 } 7034 7035 ruleset_fd = 7036 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 7037 ASSERT_LE(0, ruleset_fd); 7038 7039 /* Unmount the filesystem while it is being used by a ruleset. */ 7040 set_cap(_metadata, CAP_SYS_ADMIN); 7041 ASSERT_EQ(0, umount(TMP_DIR)); 7042 clear_cap(_metadata, CAP_SYS_ADMIN); 7043 7044 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 7045 set_cap(_metadata, CAP_SYS_ADMIN); 7046 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 7047 clear_cap(_metadata, CAP_SYS_ADMIN); 7048 7049 enforce_ruleset(_metadata, ruleset_fd); 7050 ASSERT_EQ(0, close(ruleset_fd)); 7051 7052 /* Checks that access to the new mount point is denied. */ 7053 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 7054 } 7055 7056 static int matches_log_fs_extra(struct __test_metadata *const _metadata, 7057 int audit_fd, const char *const blockers, 7058 const char *const path, const char *const extra) 7059 { 7060 static const char log_template[] = REGEX_LANDLOCK_PREFIX 7061 " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$"; 7062 char *absolute_path = NULL; 7063 size_t log_match_remaining = sizeof(log_template) + strlen(blockers) + 7064 PATH_MAX * 2 + 7065 (extra ? strlen(extra) : 0) + 1; 7066 char log_match[log_match_remaining]; 7067 char *log_match_cursor = log_match; 7068 size_t chunk_len; 7069 7070 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7071 REGEX_LANDLOCK_PREFIX " blockers=%s path=\"", 7072 blockers); 7073 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7074 return -E2BIG; 7075 7076 /* 7077 * It is assume that absolute_path does not contain control characters nor 7078 * spaces, see audit_string_contains_control(). 7079 */ 7080 absolute_path = realpath(path, NULL); 7081 if (!absolute_path) 7082 return -errno; 7083 7084 log_match_remaining -= chunk_len; 7085 log_match_cursor += chunk_len; 7086 log_match_cursor = regex_escape(absolute_path, log_match_cursor, 7087 log_match_remaining); 7088 free(absolute_path); 7089 if (log_match_cursor < 0) 7090 return (long long)log_match_cursor; 7091 7092 log_match_remaining -= log_match_cursor - log_match; 7093 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7094 "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$", 7095 extra ?: ""); 7096 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7097 return -E2BIG; 7098 7099 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, 7100 NULL); 7101 } 7102 7103 static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd, 7104 const char *const blockers, const char *const path) 7105 { 7106 return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL); 7107 } 7108 7109 FIXTURE(audit_layout1) 7110 { 7111 struct audit_filter audit_filter; 7112 int audit_fd; 7113 }; 7114 7115 FIXTURE_SETUP(audit_layout1) 7116 { 7117 prepare_layout(_metadata); 7118 7119 create_layout1(_metadata); 7120 7121 set_cap(_metadata, CAP_AUDIT_CONTROL); 7122 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 7123 EXPECT_LE(0, self->audit_fd); 7124 disable_caps(_metadata); 7125 } 7126 7127 FIXTURE_TEARDOWN_PARENT(audit_layout1) 7128 { 7129 remove_layout1(_metadata); 7130 7131 cleanup_layout(_metadata); 7132 7133 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 7134 } 7135 7136 TEST_F(audit_layout1, execute_make) 7137 { 7138 struct audit_records records; 7139 7140 copy_file(_metadata, bin_true, file1_s1d1); 7141 test_execute(_metadata, 0, file1_s1d1); 7142 test_check_exec(_metadata, 0, file1_s1d1); 7143 7144 drop_access_rights(_metadata, 7145 &(struct landlock_ruleset_attr){ 7146 .handled_access_fs = 7147 LANDLOCK_ACCESS_FS_EXECUTE, 7148 }); 7149 7150 test_execute(_metadata, EACCES, file1_s1d1); 7151 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7152 file1_s1d1)); 7153 test_check_exec(_metadata, EACCES, file1_s1d1); 7154 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7155 file1_s1d1)); 7156 7157 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7158 EXPECT_EQ(0, records.access); 7159 EXPECT_EQ(0, records.domain); 7160 } 7161 7162 /* 7163 * Using a set of handled/denied access rights make it possible to check that 7164 * only the blocked ones are logged. 7165 */ 7166 7167 /* clang-format off */ 7168 static const __u64 access_fs_16 = 7169 LANDLOCK_ACCESS_FS_EXECUTE | 7170 LANDLOCK_ACCESS_FS_WRITE_FILE | 7171 LANDLOCK_ACCESS_FS_READ_FILE | 7172 LANDLOCK_ACCESS_FS_READ_DIR | 7173 LANDLOCK_ACCESS_FS_REMOVE_DIR | 7174 LANDLOCK_ACCESS_FS_REMOVE_FILE | 7175 LANDLOCK_ACCESS_FS_MAKE_CHAR | 7176 LANDLOCK_ACCESS_FS_MAKE_DIR | 7177 LANDLOCK_ACCESS_FS_MAKE_REG | 7178 LANDLOCK_ACCESS_FS_MAKE_SOCK | 7179 LANDLOCK_ACCESS_FS_MAKE_FIFO | 7180 LANDLOCK_ACCESS_FS_MAKE_BLOCK | 7181 LANDLOCK_ACCESS_FS_MAKE_SYM | 7182 LANDLOCK_ACCESS_FS_REFER | 7183 LANDLOCK_ACCESS_FS_TRUNCATE | 7184 LANDLOCK_ACCESS_FS_IOCTL_DEV; 7185 /* clang-format on */ 7186 7187 TEST_F(audit_layout1, execute_read) 7188 { 7189 struct audit_records records; 7190 7191 copy_file(_metadata, bin_true, file1_s1d1); 7192 test_execute(_metadata, 0, file1_s1d1); 7193 test_check_exec(_metadata, 0, file1_s1d1); 7194 7195 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7196 .handled_access_fs = access_fs_16, 7197 }); 7198 7199 /* 7200 * The only difference with the previous audit_layout1.execute_read test is 7201 * the extra ",fs\\.read_file" blocked by the executable file. 7202 */ 7203 test_execute(_metadata, EACCES, file1_s1d1); 7204 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7205 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7206 test_check_exec(_metadata, EACCES, file1_s1d1); 7207 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7208 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7209 7210 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7211 EXPECT_EQ(0, records.access); 7212 EXPECT_EQ(0, records.domain); 7213 } 7214 7215 TEST_F(audit_layout1, write_file) 7216 { 7217 struct audit_records records; 7218 7219 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7220 .handled_access_fs = access_fs_16, 7221 }); 7222 7223 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 7224 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7225 "fs\\.write_file", file1_s1d1)); 7226 7227 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7228 EXPECT_EQ(0, records.access); 7229 EXPECT_EQ(1, records.domain); 7230 } 7231 7232 TEST_F(audit_layout1, read_file) 7233 { 7234 struct audit_records records; 7235 7236 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7237 .handled_access_fs = access_fs_16, 7238 }); 7239 7240 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 7241 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file", 7242 file1_s1d1)); 7243 7244 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7245 EXPECT_EQ(0, records.access); 7246 EXPECT_EQ(1, records.domain); 7247 } 7248 7249 TEST_F(audit_layout1, read_dir) 7250 { 7251 struct audit_records records; 7252 7253 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7254 .handled_access_fs = access_fs_16, 7255 }); 7256 7257 EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY)); 7258 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir", 7259 dir_s1d1)); 7260 7261 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7262 EXPECT_EQ(0, records.access); 7263 EXPECT_EQ(1, records.domain); 7264 } 7265 7266 TEST_F(audit_layout1, remove_dir) 7267 { 7268 struct audit_records records; 7269 7270 EXPECT_EQ(0, unlink(file1_s1d3)); 7271 EXPECT_EQ(0, unlink(file2_s1d3)); 7272 7273 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7274 .handled_access_fs = access_fs_16, 7275 }); 7276 7277 EXPECT_EQ(-1, rmdir(dir_s1d3)); 7278 EXPECT_EQ(EACCES, errno); 7279 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7280 "fs\\.remove_dir", dir_s1d2)); 7281 7282 EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 7283 EXPECT_EQ(EACCES, errno); 7284 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7285 "fs\\.remove_dir", dir_s1d2)); 7286 7287 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7288 EXPECT_EQ(0, records.access); 7289 EXPECT_EQ(0, records.domain); 7290 } 7291 7292 TEST_F(audit_layout1, remove_file) 7293 { 7294 struct audit_records records; 7295 7296 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7297 .handled_access_fs = access_fs_16, 7298 }); 7299 7300 EXPECT_EQ(-1, unlink(file1_s1d3)); 7301 EXPECT_EQ(EACCES, errno); 7302 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7303 "fs\\.remove_file", dir_s1d3)); 7304 7305 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7306 EXPECT_EQ(0, records.access); 7307 EXPECT_EQ(1, records.domain); 7308 } 7309 7310 TEST_F(audit_layout1, make_char) 7311 { 7312 struct audit_records records; 7313 7314 EXPECT_EQ(0, unlink(file1_s1d3)); 7315 7316 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7317 .handled_access_fs = access_fs_16, 7318 }); 7319 7320 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, 0)); 7321 EXPECT_EQ(EACCES, errno); 7322 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char", 7323 dir_s1d3)); 7324 7325 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7326 EXPECT_EQ(0, records.access); 7327 EXPECT_EQ(1, records.domain); 7328 } 7329 7330 TEST_F(audit_layout1, make_dir) 7331 { 7332 struct audit_records records; 7333 7334 EXPECT_EQ(0, unlink(file1_s1d3)); 7335 7336 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7337 .handled_access_fs = access_fs_16, 7338 }); 7339 7340 EXPECT_EQ(-1, mkdir(file1_s1d3, 0755)); 7341 EXPECT_EQ(EACCES, errno); 7342 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir", 7343 dir_s1d3)); 7344 7345 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7346 EXPECT_EQ(0, records.access); 7347 EXPECT_EQ(1, records.domain); 7348 } 7349 7350 TEST_F(audit_layout1, make_reg) 7351 { 7352 struct audit_records records; 7353 7354 EXPECT_EQ(0, unlink(file1_s1d3)); 7355 7356 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7357 .handled_access_fs = access_fs_16, 7358 }); 7359 7360 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFREG | 0644, 0)); 7361 EXPECT_EQ(EACCES, errno); 7362 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg", 7363 dir_s1d3)); 7364 7365 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7366 EXPECT_EQ(0, records.access); 7367 EXPECT_EQ(1, records.domain); 7368 } 7369 7370 TEST_F(audit_layout1, make_sock) 7371 { 7372 struct audit_records records; 7373 7374 EXPECT_EQ(0, unlink(file1_s1d3)); 7375 7376 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7377 .handled_access_fs = access_fs_16, 7378 }); 7379 7380 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFSOCK | 0644, 0)); 7381 EXPECT_EQ(EACCES, errno); 7382 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock", 7383 dir_s1d3)); 7384 7385 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7386 EXPECT_EQ(0, records.access); 7387 EXPECT_EQ(1, records.domain); 7388 } 7389 7390 TEST_F(audit_layout1, make_fifo) 7391 { 7392 struct audit_records records; 7393 7394 EXPECT_EQ(0, unlink(file1_s1d3)); 7395 7396 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7397 .handled_access_fs = access_fs_16, 7398 }); 7399 7400 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFIFO | 0644, 0)); 7401 EXPECT_EQ(EACCES, errno); 7402 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo", 7403 dir_s1d3)); 7404 7405 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7406 EXPECT_EQ(0, records.access); 7407 EXPECT_EQ(1, records.domain); 7408 } 7409 7410 TEST_F(audit_layout1, make_block) 7411 { 7412 struct audit_records records; 7413 7414 EXPECT_EQ(0, unlink(file1_s1d3)); 7415 7416 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7417 .handled_access_fs = access_fs_16, 7418 }); 7419 7420 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0)); 7421 EXPECT_EQ(EACCES, errno); 7422 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7423 "fs\\.make_block", dir_s1d3)); 7424 7425 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7426 EXPECT_EQ(0, records.access); 7427 EXPECT_EQ(1, records.domain); 7428 } 7429 7430 TEST_F(audit_layout1, make_sym) 7431 { 7432 struct audit_records records; 7433 7434 EXPECT_EQ(0, unlink(file1_s1d3)); 7435 7436 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7437 .handled_access_fs = access_fs_16, 7438 }); 7439 7440 EXPECT_EQ(-1, symlink("target", file1_s1d3)); 7441 EXPECT_EQ(EACCES, errno); 7442 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym", 7443 dir_s1d3)); 7444 7445 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7446 EXPECT_EQ(0, records.access); 7447 EXPECT_EQ(1, records.domain); 7448 } 7449 7450 TEST_F(audit_layout1, refer_handled) 7451 { 7452 struct audit_records records; 7453 7454 EXPECT_EQ(0, unlink(file1_s1d3)); 7455 7456 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7457 .handled_access_fs = 7458 LANDLOCK_ACCESS_FS_REFER, 7459 }); 7460 7461 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7462 EXPECT_EQ(EXDEV, errno); 7463 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7464 dir_s1d1)); 7465 EXPECT_EQ(0, 7466 matches_log_domain_allocated(self->audit_fd, getpid(), NULL)); 7467 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7468 dir_s1d3)); 7469 7470 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7471 EXPECT_EQ(0, records.access); 7472 EXPECT_EQ(0, records.domain); 7473 } 7474 7475 TEST_F(audit_layout1, refer_make) 7476 { 7477 struct audit_records records; 7478 7479 EXPECT_EQ(0, unlink(file1_s1d3)); 7480 7481 drop_access_rights(_metadata, 7482 &(struct landlock_ruleset_attr){ 7483 .handled_access_fs = 7484 LANDLOCK_ACCESS_FS_MAKE_REG | 7485 LANDLOCK_ACCESS_FS_REFER, 7486 }); 7487 7488 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7489 EXPECT_EQ(EACCES, errno); 7490 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7491 dir_s1d1)); 7492 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7493 "fs\\.make_reg,fs\\.refer", dir_s1d3)); 7494 7495 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7496 EXPECT_EQ(0, records.access); 7497 EXPECT_EQ(0, records.domain); 7498 } 7499 7500 TEST_F(audit_layout1, refer_rename) 7501 { 7502 struct audit_records records; 7503 7504 EXPECT_EQ(0, unlink(file1_s1d3)); 7505 7506 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7507 .handled_access_fs = access_fs_16, 7508 }); 7509 7510 EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3)); 7511 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7512 "fs\\.remove_file,fs\\.refer", dir_s1d2)); 7513 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7514 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7515 dir_s2d3)); 7516 7517 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7518 EXPECT_EQ(0, records.access); 7519 EXPECT_EQ(0, records.domain); 7520 } 7521 7522 TEST_F(audit_layout1, refer_exchange) 7523 { 7524 struct audit_records records; 7525 7526 EXPECT_EQ(0, unlink(file1_s1d3)); 7527 7528 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7529 .handled_access_fs = access_fs_16, 7530 }); 7531 7532 /* 7533 * The only difference with the previous audit_layout1.refer_rename test is 7534 * the extra ",fs\\.make_reg" blocked by the source directory. 7535 */ 7536 EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3)); 7537 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7538 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7539 dir_s1d2)); 7540 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7541 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7542 dir_s2d3)); 7543 7544 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7545 EXPECT_EQ(0, records.access); 7546 EXPECT_EQ(0, records.domain); 7547 } 7548 7549 /* 7550 * This test checks that the audit record is correctly generated when the 7551 * operation is only partially denied. This is the case for rename(2) when the 7552 * source file is allowed to be referenced but the destination directory is not. 7553 * 7554 * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize 7555 * file path walks and prepare for audit support") and commit 058518c20920 7556 * ("landlock: Align partial refer access checks with final ones"). 7557 */ 7558 TEST_F(audit_layout1, refer_rename_half) 7559 { 7560 struct audit_records records; 7561 const struct rule layer1[] = { 7562 { 7563 .path = dir_s2d2, 7564 .access = LANDLOCK_ACCESS_FS_REFER, 7565 }, 7566 {}, 7567 }; 7568 int ruleset_fd = 7569 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 7570 7571 ASSERT_LE(0, ruleset_fd); 7572 enforce_ruleset(_metadata, ruleset_fd); 7573 ASSERT_EQ(0, close(ruleset_fd)); 7574 7575 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 7576 ASSERT_EQ(EXDEV, errno); 7577 7578 /* Only half of the request is denied. */ 7579 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7580 dir_s1d1)); 7581 7582 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7583 EXPECT_EQ(0, records.access); 7584 EXPECT_EQ(1, records.domain); 7585 } 7586 7587 TEST_F(audit_layout1, truncate) 7588 { 7589 struct audit_records records; 7590 7591 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7592 .handled_access_fs = access_fs_16, 7593 }); 7594 7595 EXPECT_EQ(-1, truncate(file1_s1d3, 0)); 7596 EXPECT_EQ(EACCES, errno); 7597 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate", 7598 file1_s1d3)); 7599 7600 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7601 EXPECT_EQ(0, records.access); 7602 EXPECT_EQ(1, records.domain); 7603 } 7604 7605 TEST_F(audit_layout1, ioctl_dev) 7606 { 7607 struct audit_records records; 7608 int fd; 7609 7610 drop_access_rights(_metadata, 7611 &(struct landlock_ruleset_attr){ 7612 .handled_access_fs = 7613 access_fs_16 & 7614 ~LANDLOCK_ACCESS_FS_READ_FILE, 7615 }); 7616 7617 fd = open("/dev/null", O_RDONLY | O_CLOEXEC); 7618 ASSERT_LE(0, fd); 7619 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 7620 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7621 "fs\\.ioctl_dev", "/dev/null", 7622 " ioctlcmd=0x541b")); 7623 7624 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7625 EXPECT_EQ(0, records.access); 7626 EXPECT_EQ(1, records.domain); 7627 } 7628 7629 TEST_F(audit_layout1, mount) 7630 { 7631 struct audit_records records; 7632 7633 drop_access_rights(_metadata, 7634 &(struct landlock_ruleset_attr){ 7635 .handled_access_fs = 7636 LANDLOCK_ACCESS_FS_EXECUTE, 7637 }); 7638 7639 set_cap(_metadata, CAP_SYS_ADMIN); 7640 EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 7641 EXPECT_EQ(EPERM, errno); 7642 clear_cap(_metadata, CAP_SYS_ADMIN); 7643 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7644 "fs\\.change_topology", dir_s3d2)); 7645 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7646 EXPECT_EQ(0, records.access); 7647 EXPECT_EQ(1, records.domain); 7648 } 7649 7650 TEST_HARNESS_MAIN 7651