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 struct sockaddr_un srv_un = { 4366 .sun_family = AF_UNIX, 4367 }; 4368 struct sockaddr_un cli_un = { 4369 .sun_family = AF_UNIX, 4370 }; 4371 const struct landlock_ruleset_attr attr = { 4372 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4373 }; 4374 4375 /* Sets up a server */ 4376 ASSERT_EQ(0, unlink(path)); 4377 srv_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4378 ASSERT_LE(0, srv_fd); 4379 4380 strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path)); 4381 ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, sizeof(srv_un))); 4382 4383 ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */)); 4384 4385 /* Enables Landlock. */ 4386 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0); 4387 ASSERT_LE(0, ruleset_fd); 4388 enforce_ruleset(_metadata, ruleset_fd); 4389 ASSERT_EQ(0, close(ruleset_fd)); 4390 4391 /* Sets up a client connection to it */ 4392 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0); 4393 ASSERT_LE(0, cli_fd); 4394 4395 strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path)); 4396 ASSERT_EQ(0, 4397 connect(cli_fd, (struct sockaddr *)&cli_un, sizeof(cli_un))); 4398 4399 /* FIONREAD and other IOCTLs should not be forbidden. */ 4400 EXPECT_EQ(0, test_fionread_ioctl(cli_fd)); 4401 4402 EXPECT_EQ(0, close(cli_fd)); 4403 EXPECT_EQ(0, close(srv_fd)); 4404 } 4405 4406 /* clang-format off */ 4407 FIXTURE(ioctl) {}; 4408 4409 FIXTURE_SETUP(ioctl) {}; 4410 4411 FIXTURE_TEARDOWN(ioctl) {}; 4412 /* clang-format on */ 4413 4414 FIXTURE_VARIANT(ioctl) 4415 { 4416 const __u64 handled; 4417 const __u64 allowed; 4418 const mode_t open_mode; 4419 /* 4420 * FIONREAD is used as a characteristic device-specific IOCTL command. 4421 * It is implemented in fs/ioctl.c for regular files, 4422 * but we do not blanket-permit it for devices. 4423 */ 4424 const int expected_fionread_result; 4425 }; 4426 4427 /* clang-format off */ 4428 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) { 4429 /* clang-format on */ 4430 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4431 .allowed = 0, 4432 .open_mode = O_RDWR, 4433 .expected_fionread_result = EACCES, 4434 }; 4435 4436 /* clang-format off */ 4437 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) { 4438 /* clang-format on */ 4439 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4440 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV, 4441 .open_mode = O_RDWR, 4442 .expected_fionread_result = 0, 4443 }; 4444 4445 /* clang-format off */ 4446 FIXTURE_VARIANT_ADD(ioctl, unhandled) { 4447 /* clang-format on */ 4448 .handled = LANDLOCK_ACCESS_FS_EXECUTE, 4449 .allowed = LANDLOCK_ACCESS_FS_EXECUTE, 4450 .open_mode = O_RDWR, 4451 .expected_fionread_result = 0, 4452 }; 4453 4454 TEST_F_FORK(ioctl, handle_dir_access_file) 4455 { 4456 const int flag = 0; 4457 const struct rule rules[] = { 4458 { 4459 .path = "/dev", 4460 .access = variant->allowed, 4461 }, 4462 {}, 4463 }; 4464 int file_fd, ruleset_fd; 4465 4466 /* Enables Landlock. */ 4467 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4468 ASSERT_LE(0, ruleset_fd); 4469 enforce_ruleset(_metadata, ruleset_fd); 4470 ASSERT_EQ(0, close(ruleset_fd)); 4471 4472 file_fd = open("/dev/zero", variant->open_mode); 4473 ASSERT_LE(0, file_fd); 4474 4475 /* Checks that IOCTL commands return the expected errors. */ 4476 EXPECT_EQ(variant->expected_fionread_result, 4477 test_fionread_ioctl(file_fd)); 4478 4479 /* Checks that unrestrictable commands are unrestricted. */ 4480 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); 4481 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); 4482 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); 4483 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); 4484 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); 4485 4486 ASSERT_EQ(0, close(file_fd)); 4487 } 4488 4489 TEST_F_FORK(ioctl, handle_dir_access_dir) 4490 { 4491 const int flag = 0; 4492 const struct rule rules[] = { 4493 { 4494 .path = "/dev", 4495 .access = variant->allowed, 4496 }, 4497 {}, 4498 }; 4499 int dir_fd, ruleset_fd; 4500 4501 /* Enables Landlock. */ 4502 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4503 ASSERT_LE(0, ruleset_fd); 4504 enforce_ruleset(_metadata, ruleset_fd); 4505 ASSERT_EQ(0, close(ruleset_fd)); 4506 4507 /* 4508 * Ignore variant->open_mode for this test, as we intend to open a 4509 * directory. If the directory can not be opened, the variant is 4510 * infeasible to test with an opened directory. 4511 */ 4512 dir_fd = open("/dev", O_RDONLY); 4513 if (dir_fd < 0) 4514 return; 4515 4516 /* 4517 * Checks that IOCTL commands return the expected errors. 4518 * We do not use the expected values from the fixture here. 4519 * 4520 * When using IOCTL on a directory, no Landlock restrictions apply. 4521 */ 4522 EXPECT_EQ(0, test_fionread_ioctl(dir_fd)); 4523 4524 /* Checks that unrestrictable commands are unrestricted. */ 4525 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX)); 4526 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX)); 4527 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag)); 4528 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag)); 4529 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag)); 4530 4531 ASSERT_EQ(0, close(dir_fd)); 4532 } 4533 4534 TEST_F_FORK(ioctl, handle_file_access_file) 4535 { 4536 const int flag = 0; 4537 const struct rule rules[] = { 4538 { 4539 .path = "/dev/zero", 4540 .access = variant->allowed, 4541 }, 4542 {}, 4543 }; 4544 int file_fd, ruleset_fd; 4545 4546 /* Enables Landlock. */ 4547 ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 4548 ASSERT_LE(0, ruleset_fd); 4549 enforce_ruleset(_metadata, ruleset_fd); 4550 ASSERT_EQ(0, close(ruleset_fd)); 4551 4552 file_fd = open("/dev/zero", variant->open_mode); 4553 ASSERT_LE(0, file_fd) 4554 { 4555 TH_LOG("Failed to open /dev/zero: %s", strerror(errno)); 4556 } 4557 4558 /* Checks that IOCTL commands return the expected errors. */ 4559 EXPECT_EQ(variant->expected_fionread_result, 4560 test_fionread_ioctl(file_fd)); 4561 4562 /* Checks that unrestrictable commands are unrestricted. */ 4563 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); 4564 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); 4565 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); 4566 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); 4567 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); 4568 4569 ASSERT_EQ(0, close(file_fd)); 4570 } 4571 4572 /* clang-format off */ 4573 FIXTURE(layout1_bind) {}; 4574 /* clang-format on */ 4575 4576 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 4577 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 4578 4579 /* Move targets for disconnected path tests. */ 4580 static const char dir_s4d1[] = TMP_DIR "/s4d1"; 4581 static const char file1_s4d1[] = TMP_DIR "/s4d1/f1"; 4582 static const char file2_s4d1[] = TMP_DIR "/s4d1/f2"; 4583 static const char dir_s4d2[] = TMP_DIR "/s4d1/s4d2"; 4584 static const char file1_s4d2[] = TMP_DIR "/s4d1/s4d2/f1"; 4585 static const char file1_name[] = "f1"; 4586 static const char file2_name[] = "f2"; 4587 4588 FIXTURE_SETUP(layout1_bind) 4589 { 4590 prepare_layout(_metadata); 4591 4592 create_layout1(_metadata); 4593 4594 set_cap(_metadata, CAP_SYS_ADMIN); 4595 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 4596 clear_cap(_metadata, CAP_SYS_ADMIN); 4597 } 4598 4599 FIXTURE_TEARDOWN_PARENT(layout1_bind) 4600 { 4601 /* umount(dir_s2d2)) is handled by namespace lifetime. */ 4602 4603 remove_path(file1_s4d1); 4604 remove_path(file2_s4d1); 4605 4606 remove_layout1(_metadata); 4607 4608 cleanup_layout(_metadata); 4609 } 4610 4611 /* 4612 * layout1_bind hierarchy: 4613 * 4614 * tmp 4615 * ├── s1d1 4616 * │ ├── f1 4617 * │ ├── f2 4618 * │ └── s1d2 4619 * │ ├── f1 4620 * │ ├── f2 4621 * │ └── s1d3 [disconnected by path_disconnected] 4622 * │ ├── f1 4623 * │ └── f2 4624 * ├── s2d1 4625 * │ ├── f1 4626 * │ └── s2d2 [bind mount from s1d2] 4627 * │ ├── f1 4628 * │ ├── f2 4629 * │ └── s1d3 4630 * │ ├── f1 4631 * │ └── f2 4632 * ├── s3d1 4633 * │ └── s3d2 4634 * │ └── s3d3 4635 * └── s4d1 [renamed from s1d3 by path_disconnected] 4636 * ├── f1 4637 * ├── f2 4638 * └── s4d2 4639 * └── f1 4640 */ 4641 4642 TEST_F_FORK(layout1_bind, no_restriction) 4643 { 4644 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 4645 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4646 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 4647 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4648 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 4649 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4650 4651 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 4652 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 4653 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 4654 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 4655 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 4656 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 4657 4658 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 4659 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4660 4661 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 4662 } 4663 4664 TEST_F_FORK(layout1_bind, same_content_same_file) 4665 { 4666 /* 4667 * Sets access right on parent directories of both source and 4668 * destination mount points. 4669 */ 4670 const struct rule layer1_parent[] = { 4671 { 4672 .path = dir_s1d1, 4673 .access = ACCESS_RO, 4674 }, 4675 { 4676 .path = dir_s2d1, 4677 .access = ACCESS_RW, 4678 }, 4679 {}, 4680 }; 4681 /* 4682 * Sets access rights on the same bind-mounted directories. The result 4683 * should be ACCESS_RW for both directories, but not both hierarchies 4684 * because of the first layer. 4685 */ 4686 const struct rule layer2_mount_point[] = { 4687 { 4688 .path = dir_s1d2, 4689 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4690 }, 4691 { 4692 .path = dir_s2d2, 4693 .access = ACCESS_RW, 4694 }, 4695 {}, 4696 }; 4697 /* Only allow read-access to the s1d3 hierarchies. */ 4698 const struct rule layer3_source[] = { 4699 { 4700 .path = dir_s1d3, 4701 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4702 }, 4703 {}, 4704 }; 4705 /* Removes all access rights. */ 4706 const struct rule layer4_destination[] = { 4707 { 4708 .path = bind_file1_s1d3, 4709 .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 4710 }, 4711 {}, 4712 }; 4713 int ruleset_fd; 4714 4715 /* Sets rules for the parent directories. */ 4716 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 4717 ASSERT_LE(0, ruleset_fd); 4718 enforce_ruleset(_metadata, ruleset_fd); 4719 ASSERT_EQ(0, close(ruleset_fd)); 4720 4721 /* Checks source hierarchy. */ 4722 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 4723 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4724 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4725 4726 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4727 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4728 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4729 4730 /* Checks destination hierarchy. */ 4731 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 4732 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4733 4734 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4735 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4736 4737 /* Sets rules for the mount points. */ 4738 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 4739 ASSERT_LE(0, ruleset_fd); 4740 enforce_ruleset(_metadata, ruleset_fd); 4741 ASSERT_EQ(0, close(ruleset_fd)); 4742 4743 /* Checks source hierarchy. */ 4744 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 4745 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 4746 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 4747 4748 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 4749 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4750 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4751 4752 /* Checks destination hierarchy. */ 4753 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 4754 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 4755 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 4756 4757 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 4758 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4759 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4760 4761 /* Sets a (shared) rule only on the source. */ 4762 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 4763 ASSERT_LE(0, ruleset_fd); 4764 enforce_ruleset(_metadata, ruleset_fd); 4765 ASSERT_EQ(0, close(ruleset_fd)); 4766 4767 /* Checks source hierarchy. */ 4768 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 4769 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 4770 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 4771 4772 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 4773 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4774 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 4775 4776 /* Checks destination hierarchy. */ 4777 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 4778 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 4779 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 4780 4781 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 4782 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 4783 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 4784 4785 /* Sets a (shared) rule only on the destination. */ 4786 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 4787 ASSERT_LE(0, ruleset_fd); 4788 enforce_ruleset(_metadata, ruleset_fd); 4789 ASSERT_EQ(0, close(ruleset_fd)); 4790 4791 /* Checks source hierarchy. */ 4792 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 4793 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 4794 4795 /* Checks destination hierarchy. */ 4796 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 4797 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 4798 } 4799 4800 TEST_F_FORK(layout1_bind, reparent_cross_mount) 4801 { 4802 const struct rule layer1[] = { 4803 { 4804 /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 4805 .path = dir_s2d1, 4806 .access = LANDLOCK_ACCESS_FS_REFER, 4807 }, 4808 { 4809 .path = bind_dir_s1d3, 4810 .access = LANDLOCK_ACCESS_FS_EXECUTE, 4811 }, 4812 {}, 4813 }; 4814 int ruleset_fd = create_ruleset( 4815 _metadata, 4816 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 4817 4818 ASSERT_LE(0, ruleset_fd); 4819 enforce_ruleset(_metadata, ruleset_fd); 4820 ASSERT_EQ(0, close(ruleset_fd)); 4821 4822 /* Checks basic denied move. */ 4823 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 4824 ASSERT_EQ(EXDEV, errno); 4825 4826 /* Checks real cross-mount move (Landlock is not involved). */ 4827 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 4828 ASSERT_EQ(EXDEV, errno); 4829 4830 /* Checks move that will give more accesses. */ 4831 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 4832 ASSERT_EQ(EXDEV, errno); 4833 4834 /* Checks legitimate downgrade move. */ 4835 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 4836 } 4837 4838 /* 4839 * Make sure access to file through a disconnected path works as expected. 4840 * This test moves s1d3 to s4d1. 4841 */ 4842 TEST_F_FORK(layout1_bind, path_disconnected) 4843 { 4844 const struct rule layer1_allow_all[] = { 4845 { 4846 .path = TMP_DIR, 4847 .access = ACCESS_ALL, 4848 }, 4849 {}, 4850 }; 4851 const struct rule layer2_allow_just_f1[] = { 4852 { 4853 .path = file1_s1d3, 4854 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4855 }, 4856 {}, 4857 }; 4858 const struct rule layer3_only_s1d2[] = { 4859 { 4860 .path = dir_s1d2, 4861 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4862 }, 4863 {}, 4864 }; 4865 4866 /* Landlock should not deny access just because it is disconnected. */ 4867 int ruleset_fd_l1 = 4868 create_ruleset(_metadata, ACCESS_ALL, layer1_allow_all); 4869 4870 /* Creates the new ruleset now before we move the dir containing the file. */ 4871 int ruleset_fd_l2 = 4872 create_ruleset(_metadata, ACCESS_RW, layer2_allow_just_f1); 4873 int ruleset_fd_l3 = 4874 create_ruleset(_metadata, ACCESS_RW, layer3_only_s1d2); 4875 int bind_s1d3_fd; 4876 4877 ASSERT_LE(0, ruleset_fd_l1); 4878 ASSERT_LE(0, ruleset_fd_l2); 4879 ASSERT_LE(0, ruleset_fd_l3); 4880 4881 enforce_ruleset(_metadata, ruleset_fd_l1); 4882 EXPECT_EQ(0, close(ruleset_fd_l1)); 4883 4884 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 4885 ASSERT_LE(0, bind_s1d3_fd); 4886 4887 /* Tests access is possible before we move. */ 4888 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4889 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4890 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 4891 4892 /* Makes it disconnected. */ 4893 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 4894 { 4895 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 4896 strerror(errno)); 4897 } 4898 4899 /* Tests that access is still possible. */ 4900 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4901 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4902 4903 /* 4904 * Tests that ".." is not possible (not because of Landlock, but just 4905 * because it's disconnected). 4906 */ 4907 EXPECT_EQ(ENOENT, 4908 test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY)); 4909 4910 /* This should still work with a narrower rule. */ 4911 enforce_ruleset(_metadata, ruleset_fd_l2); 4912 EXPECT_EQ(0, close(ruleset_fd_l2)); 4913 4914 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)); 4915 /* 4916 * Accessing a file through a disconnected file descriptor can still be 4917 * allowed by a rule tied to this file, even if it is no longer visible in 4918 * its mount point. 4919 */ 4920 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4921 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4922 4923 enforce_ruleset(_metadata, ruleset_fd_l3); 4924 EXPECT_EQ(0, close(ruleset_fd_l3)); 4925 4926 EXPECT_EQ(EACCES, test_open(file1_s4d1, O_RDONLY)); 4927 /* 4928 * Accessing a file through a disconnected file descriptor can still be 4929 * allowed by a rule tied to the original mount point, even if it is no 4930 * longer visible in its mount point. 4931 */ 4932 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4933 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 4934 } 4935 4936 /* 4937 * Test that renameat with disconnected paths works under Landlock. This test 4938 * moves s1d3 to s4d2, so that we can have a rule allowing refers on the move 4939 * target's immediate parent. 4940 */ 4941 TEST_F_FORK(layout1_bind, path_disconnected_rename) 4942 { 4943 const struct rule layer1[] = { 4944 { 4945 .path = dir_s1d2, 4946 .access = LANDLOCK_ACCESS_FS_REFER | 4947 LANDLOCK_ACCESS_FS_MAKE_DIR | 4948 LANDLOCK_ACCESS_FS_REMOVE_DIR | 4949 LANDLOCK_ACCESS_FS_MAKE_REG | 4950 LANDLOCK_ACCESS_FS_REMOVE_FILE | 4951 LANDLOCK_ACCESS_FS_READ_FILE, 4952 }, 4953 { 4954 .path = dir_s4d1, 4955 .access = LANDLOCK_ACCESS_FS_REFER | 4956 LANDLOCK_ACCESS_FS_MAKE_DIR | 4957 LANDLOCK_ACCESS_FS_REMOVE_DIR | 4958 LANDLOCK_ACCESS_FS_MAKE_REG | 4959 LANDLOCK_ACCESS_FS_REMOVE_FILE | 4960 LANDLOCK_ACCESS_FS_READ_FILE, 4961 }, 4962 {} 4963 }; 4964 4965 /* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */ 4966 const struct rule layer2_only_s1d2[] = { 4967 { 4968 .path = dir_s1d2, 4969 .access = LANDLOCK_ACCESS_FS_READ_FILE, 4970 }, 4971 {}, 4972 }; 4973 int ruleset_fd_l1, ruleset_fd_l2; 4974 pid_t child_pid; 4975 int bind_s1d3_fd, status; 4976 4977 ASSERT_EQ(0, mkdir(dir_s4d1, 0755)) 4978 { 4979 TH_LOG("Failed to create %s: %s", dir_s4d1, strerror(errno)); 4980 } 4981 ruleset_fd_l1 = create_ruleset(_metadata, ACCESS_ALL, layer1); 4982 ruleset_fd_l2 = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 4983 layer2_only_s1d2); 4984 ASSERT_LE(0, ruleset_fd_l1); 4985 ASSERT_LE(0, ruleset_fd_l2); 4986 4987 enforce_ruleset(_metadata, ruleset_fd_l1); 4988 EXPECT_EQ(0, close(ruleset_fd_l1)); 4989 4990 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 4991 ASSERT_LE(0, bind_s1d3_fd); 4992 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 4993 4994 /* Tests ENOENT priority over EACCES for disconnected directory. */ 4995 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 4996 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 4997 { 4998 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 4999 strerror(errno)); 5000 } 5001 EXPECT_EQ(ENOENT, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY)); 5002 5003 /* 5004 * The file is no longer under s1d2 but we should still be able to access it 5005 * with layer 2 because its mount point is evaluated as the first valid 5006 * directory because it was initially a parent. Do a fork to test this so 5007 * we don't prevent ourselves from renaming it back later. 5008 */ 5009 child_pid = fork(); 5010 ASSERT_LE(0, child_pid); 5011 if (child_pid == 0) { 5012 enforce_ruleset(_metadata, ruleset_fd_l2); 5013 EXPECT_EQ(0, close(ruleset_fd_l2)); 5014 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5015 EXPECT_EQ(EACCES, test_open(file1_s4d2, O_RDONLY)); 5016 5017 /* 5018 * Tests that access widening checks indeed prevents us from renaming it 5019 * back. 5020 */ 5021 EXPECT_EQ(-1, rename(dir_s4d2, dir_s1d3)); 5022 EXPECT_EQ(EXDEV, errno); 5023 5024 /* 5025 * Including through the now disconnected fd (but it should return 5026 * EXDEV). 5027 */ 5028 EXPECT_EQ(-1, renameat(bind_s1d3_fd, file1_name, AT_FDCWD, 5029 file1_s2d2)); 5030 EXPECT_EQ(EXDEV, errno); 5031 _exit(_metadata->exit_code); 5032 return; 5033 } 5034 5035 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5036 EXPECT_EQ(1, WIFEXITED(status)); 5037 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5038 5039 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5040 { 5041 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d1, dir_s1d3, 5042 strerror(errno)); 5043 } 5044 5045 /* Now checks that we can access it under l2. */ 5046 child_pid = fork(); 5047 ASSERT_LE(0, child_pid); 5048 if (child_pid == 0) { 5049 enforce_ruleset(_metadata, ruleset_fd_l2); 5050 EXPECT_EQ(0, close(ruleset_fd_l2)); 5051 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5052 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5053 _exit(_metadata->exit_code); 5054 return; 5055 } 5056 5057 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0)); 5058 EXPECT_EQ(1, WIFEXITED(status)); 5059 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 5060 5061 /* 5062 * Also test that we can rename via a disconnected path. We move the 5063 * dir back to the disconnected place first, then we rename file1 to 5064 * file2 through our dir fd. 5065 */ 5066 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) 5067 { 5068 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2, 5069 strerror(errno)); 5070 } 5071 ASSERT_EQ(0, 5072 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)) 5073 { 5074 TH_LOG("Failed to rename %s to %s within disconnected %s: %s", 5075 file1_name, file2_name, bind_dir_s1d3, strerror(errno)); 5076 } 5077 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY)); 5078 ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2)) 5079 { 5080 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5081 file2_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5082 } 5083 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 5084 EXPECT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 5085 5086 /* Move it back using the disconnected path as the target. */ 5087 ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name)) 5088 { 5089 TH_LOG("Failed to rename %s to %s through disconnected %s: %s", 5090 file1_s1d2, file1_name, bind_dir_s1d3, strerror(errno)); 5091 } 5092 5093 /* Now make it connected again. */ 5094 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) 5095 { 5096 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d2, dir_s1d3, 5097 strerror(errno)); 5098 } 5099 5100 /* Checks again that we can access it under l2. */ 5101 enforce_ruleset(_metadata, ruleset_fd_l2); 5102 EXPECT_EQ(0, close(ruleset_fd_l2)); 5103 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5104 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 5105 } 5106 5107 /* 5108 * Test that linkat(2) with disconnected paths works under Landlock. This 5109 * test moves s1d3 to s4d1. 5110 */ 5111 TEST_F_FORK(layout1_bind, path_disconnected_link) 5112 { 5113 /* Ruleset to be applied after renaming s1d3 to s4d1. */ 5114 const struct rule layer1[] = { 5115 { 5116 .path = dir_s4d1, 5117 .access = LANDLOCK_ACCESS_FS_REFER | 5118 LANDLOCK_ACCESS_FS_READ_FILE | 5119 LANDLOCK_ACCESS_FS_MAKE_REG | 5120 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5121 }, 5122 { 5123 .path = dir_s2d2, 5124 .access = LANDLOCK_ACCESS_FS_REFER | 5125 LANDLOCK_ACCESS_FS_READ_FILE | 5126 LANDLOCK_ACCESS_FS_MAKE_REG | 5127 LANDLOCK_ACCESS_FS_REMOVE_FILE, 5128 }, 5129 {} 5130 }; 5131 int ruleset_fd, bind_s1d3_fd; 5132 5133 /* Removes unneeded files created by layout1, otherwise it will EEXIST. */ 5134 ASSERT_EQ(0, unlink(file1_s1d2)); 5135 ASSERT_EQ(0, unlink(file2_s1d3)); 5136 5137 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC); 5138 ASSERT_LE(0, bind_s1d3_fd); 5139 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)); 5140 5141 /* Disconnects bind_s1d3_fd. */ 5142 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) 5143 { 5144 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1, 5145 strerror(errno)); 5146 } 5147 5148 /* Need this later to test different parent link. */ 5149 ASSERT_EQ(0, mkdir(dir_s4d2, 0755)) 5150 { 5151 TH_LOG("Failed to create %s: %s", dir_s4d2, strerror(errno)); 5152 } 5153 5154 ruleset_fd = create_ruleset(_metadata, ACCESS_ALL, layer1); 5155 ASSERT_LE(0, ruleset_fd); 5156 enforce_ruleset(_metadata, ruleset_fd); 5157 EXPECT_EQ(0, close(ruleset_fd)); 5158 5159 /* From disconnected to connected. */ 5160 ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0)) 5161 { 5162 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5163 file1_name, file1_s2d2, bind_dir_s1d3, strerror(errno)); 5164 } 5165 5166 /* Tests that we can access via the new link... */ 5167 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)) 5168 { 5169 TH_LOG("Failed to open newly linked %s: %s", file1_s2d2, 5170 strerror(errno)); 5171 } 5172 5173 /* ...as well as the old one. */ 5174 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5175 { 5176 TH_LOG("Failed to open original %s: %s", file1_s4d1, 5177 strerror(errno)); 5178 } 5179 5180 /* From connected to disconnected. */ 5181 ASSERT_EQ(0, unlink(file1_s4d1)); 5182 ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0)) 5183 { 5184 TH_LOG("Failed to link %s to %s via disconnected %s: %s", 5185 file1_s2d2, file2_name, bind_dir_s1d3, strerror(errno)); 5186 } 5187 EXPECT_EQ(0, test_open(file2_s4d1, O_RDONLY)); 5188 ASSERT_EQ(0, unlink(file1_s2d2)); 5189 5190 /* From disconnected to disconnected (same parent). */ 5191 ASSERT_EQ(0, 5192 linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0)) 5193 { 5194 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5195 file2_name, file1_name, bind_dir_s1d3, strerror(errno)); 5196 } 5197 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) 5198 { 5199 TH_LOG("Failed to open newly linked %s: %s", file1_s4d1, 5200 strerror(errno)); 5201 } 5202 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)) 5203 { 5204 TH_LOG("Failed to open %s through newly created link under disconnected path: %s", 5205 file1_name, strerror(errno)); 5206 } 5207 ASSERT_EQ(0, unlink(file2_s4d1)); 5208 5209 /* From disconnected to disconnected (different parent). */ 5210 ASSERT_EQ(0, 5211 linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0)) 5212 { 5213 TH_LOG("Failed to link %s to %s within disconnected %s: %s", 5214 file1_name, "s4d2/f1", bind_dir_s1d3, strerror(errno)); 5215 } 5216 EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY)) 5217 { 5218 TH_LOG("Failed to open %s after link: %s", file1_s4d2, 5219 strerror(errno)); 5220 } 5221 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY)) 5222 { 5223 TH_LOG("Failed to open %s through disconnected path after link: %s", 5224 "s4d2/f1", strerror(errno)); 5225 } 5226 } 5227 5228 /* 5229 * layout4_disconnected_leafs with bind mount and renames: 5230 * 5231 * tmp 5232 * ├── s1d1 5233 * │ └── s1d2 [source of the bind mount] 5234 * │ ├── s1d31 5235 * │ │ └── s1d41 [now renamed beneath s3d1] 5236 * │ │ ├── f1 5237 * │ │ └── f2 5238 * │ └── s1d32 5239 * │ └── s1d42 [now renamed beneath s4d1] 5240 * │ ├── f3 5241 * │ └── f4 5242 * ├── s2d1 5243 * │ └── s2d2 [bind mount of s1d2] 5244 * │ ├── s1d31 5245 * │ │ └── s1d41 [opened FD, now renamed beneath s3d1] 5246 * │ │ ├── f1 5247 * │ │ └── f2 5248 * │ └── s1d32 5249 * │ └── s1d42 [opened FD, now renamed beneath s4d1] 5250 * │ ├── f3 5251 * │ └── f4 5252 * ├── s3d1 5253 * │ └── s1d41 [renamed here] 5254 * │ ├── f1 5255 * │ └── f2 5256 * └── s4d1 5257 * └── s1d42 [renamed here] 5258 * ├── f3 5259 * └── f4 5260 */ 5261 /* clang-format off */ 5262 FIXTURE(layout4_disconnected_leafs) { 5263 int s2d2_fd; 5264 }; 5265 /* clang-format on */ 5266 5267 FIXTURE_SETUP(layout4_disconnected_leafs) 5268 { 5269 prepare_layout(_metadata); 5270 5271 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1"); 5272 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2"); 5273 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3"); 5274 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4"); 5275 create_directory(_metadata, TMP_DIR "/s2d1/s2d2"); 5276 create_directory(_metadata, TMP_DIR "/s3d1"); 5277 create_directory(_metadata, TMP_DIR "/s4d1"); 5278 5279 self->s2d2_fd = 5280 open(TMP_DIR "/s2d1/s2d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 5281 ASSERT_LE(0, self->s2d2_fd); 5282 5283 set_cap(_metadata, CAP_SYS_ADMIN); 5284 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2", NULL, 5285 MS_BIND, NULL)); 5286 clear_cap(_metadata, CAP_SYS_ADMIN); 5287 } 5288 5289 FIXTURE_TEARDOWN_PARENT(layout4_disconnected_leafs) 5290 { 5291 /* umount(TMP_DIR "/s2d1") is handled by namespace lifetime. */ 5292 5293 /* Removes files after renames. */ 5294 remove_path(TMP_DIR "/s3d1/s1d41/f1"); 5295 remove_path(TMP_DIR "/s3d1/s1d41/f2"); 5296 remove_path(TMP_DIR "/s4d1/s1d42/f1"); 5297 remove_path(TMP_DIR "/s4d1/s1d42/f3"); 5298 remove_path(TMP_DIR "/s4d1/s1d42/f4"); 5299 remove_path(TMP_DIR "/s4d1/s1d42/f5"); 5300 5301 cleanup_layout(_metadata); 5302 } 5303 5304 FIXTURE_VARIANT(layout4_disconnected_leafs) 5305 { 5306 /* 5307 * Parent of the bind mount source. It should always be ignored when 5308 * testing against files under the s1d41 or s1d42 disconnected directories. 5309 */ 5310 const __u64 allowed_s1d1; 5311 /* 5312 * Source of bind mount (to s2d2). It should always be enforced when 5313 * testing against files under the s1d41 or s1d42 disconnected directories. 5314 */ 5315 const __u64 allowed_s1d2; 5316 /* 5317 * Original parent of s1d41. It should always be ignored when testing 5318 * against files under the s1d41 disconnected directory. 5319 */ 5320 const __u64 allowed_s1d31; 5321 /* 5322 * Original parent of s1d42. It should always be ignored when testing 5323 * against files under the s1d42 disconnected directory. 5324 */ 5325 const __u64 allowed_s1d32; 5326 /* 5327 * Opened and disconnected source directory. It should always be enforced 5328 * when testing against files under the s1d41 disconnected directory. 5329 */ 5330 const __u64 allowed_s1d41; 5331 /* 5332 * Opened and disconnected source directory. It should always be enforced 5333 * when testing against files under the s1d42 disconnected directory. 5334 */ 5335 const __u64 allowed_s1d42; 5336 /* 5337 * File in the s1d41 disconnected directory. It should always be enforced 5338 * when testing against itself under the s1d41 disconnected directory. 5339 */ 5340 const __u64 allowed_f1; 5341 /* 5342 * File in the s1d41 disconnected directory. It should always be enforced 5343 * when testing against itself under the s1d41 disconnected directory. 5344 */ 5345 const __u64 allowed_f2; 5346 /* 5347 * File in the s1d42 disconnected directory. It should always be enforced 5348 * when testing against itself under the s1d42 disconnected directory. 5349 */ 5350 const __u64 allowed_f3; 5351 /* 5352 * Parent of the bind mount destination. It should always be enforced when 5353 * testing against files under the s1d41 or s1d42 disconnected directories. 5354 */ 5355 const __u64 allowed_s2d1; 5356 /* 5357 * Directory covered by the bind mount. It should always be ignored when 5358 * testing against files under the s1d41 or s1d42 disconnected directories. 5359 */ 5360 const __u64 allowed_s2d2; 5361 /* 5362 * New parent of the renamed s1d41. It should always be ignored when 5363 * testing against files under the s1d41 disconnected directory. 5364 */ 5365 const __u64 allowed_s3d1; 5366 /* 5367 * New parent of the renamed s1d42. It should always be ignored when 5368 * testing against files under the s1d42 disconnected directory. 5369 */ 5370 const __u64 allowed_s4d1; 5371 5372 /* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */ 5373 const int expected_read_result; 5374 /* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */ 5375 const int expected_rename_result; 5376 /* 5377 * Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3, 5378 * RENAME_EXCHANGE). 5379 */ 5380 const int expected_exchange_result; 5381 /* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */ 5382 const int expected_same_dir_rename_result; 5383 }; 5384 5385 /* clang-format off */ 5386 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) { 5387 /* clang-format on */ 5388 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 5389 LANDLOCK_ACCESS_FS_READ_FILE | 5390 LANDLOCK_ACCESS_FS_EXECUTE | 5391 LANDLOCK_ACCESS_FS_MAKE_REG, 5392 .expected_read_result = EACCES, 5393 .expected_same_dir_rename_result = EACCES, 5394 .expected_rename_result = EACCES, 5395 .expected_exchange_result = EACCES, 5396 }; 5397 5398 /* clang-format off */ 5399 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) { 5400 /* clang-format on */ 5401 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5402 .expected_read_result = 0, 5403 .expected_same_dir_rename_result = EACCES, 5404 .expected_rename_result = EACCES, 5405 .expected_exchange_result = EACCES, 5406 }; 5407 5408 /* clang-format off */ 5409 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) { 5410 /* clang-format on */ 5411 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 5412 LANDLOCK_ACCESS_FS_MAKE_REG, 5413 .expected_read_result = 0, 5414 .expected_same_dir_rename_result = 0, 5415 .expected_rename_result = EXDEV, 5416 .expected_exchange_result = EXDEV, 5417 }; 5418 5419 /* clang-format off */ 5420 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) { 5421 /* clang-format on */ 5422 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5423 .expected_read_result = EACCES, 5424 .expected_same_dir_rename_result = 0, 5425 .expected_rename_result = 0, 5426 .expected_exchange_result = 0, 5427 }; 5428 5429 /* clang-format off */ 5430 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) { 5431 /* clang-format on */ 5432 .allowed_s1d31 = LANDLOCK_ACCESS_FS_REFER | 5433 LANDLOCK_ACCESS_FS_READ_FILE | 5434 LANDLOCK_ACCESS_FS_EXECUTE | 5435 LANDLOCK_ACCESS_FS_MAKE_REG, 5436 .allowed_s1d32 = LANDLOCK_ACCESS_FS_REFER | 5437 LANDLOCK_ACCESS_FS_READ_FILE | 5438 LANDLOCK_ACCESS_FS_EXECUTE | 5439 LANDLOCK_ACCESS_FS_MAKE_REG, 5440 .expected_read_result = EACCES, 5441 .expected_same_dir_rename_result = EACCES, 5442 .expected_rename_result = EACCES, 5443 .expected_exchange_result = EACCES, 5444 }; 5445 5446 /* clang-format off */ 5447 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) { 5448 /* clang-format on */ 5449 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5450 LANDLOCK_ACCESS_FS_READ_FILE, 5451 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5452 LANDLOCK_ACCESS_FS_READ_FILE, 5453 .expected_read_result = 0, 5454 .expected_same_dir_rename_result = EACCES, 5455 .expected_rename_result = EACCES, 5456 .expected_exchange_result = EACCES, 5457 }; 5458 5459 /* clang-format off */ 5460 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) { 5461 /* clang-format on */ 5462 .allowed_s1d41 = LANDLOCK_ACCESS_FS_READ_FILE | 5463 LANDLOCK_ACCESS_FS_MAKE_REG, 5464 .allowed_s1d42 = LANDLOCK_ACCESS_FS_READ_FILE | 5465 LANDLOCK_ACCESS_FS_MAKE_REG, 5466 .expected_read_result = 0, 5467 .expected_same_dir_rename_result = 0, 5468 .expected_rename_result = EXDEV, 5469 .expected_exchange_result = EXDEV, 5470 }; 5471 5472 /* clang-format off */ 5473 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) { 5474 /* clang-format on */ 5475 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5476 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5477 .expected_read_result = EACCES, 5478 .expected_same_dir_rename_result = 0, 5479 .expected_rename_result = 0, 5480 .expected_exchange_result = 0, 5481 }; 5482 5483 /* The destination directory has more access right. */ 5484 /* clang-format off */ 5485 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) { 5486 /* clang-format on */ 5487 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5488 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | 5489 LANDLOCK_ACCESS_FS_MAKE_REG | 5490 LANDLOCK_ACCESS_FS_EXECUTE, 5491 .expected_read_result = EACCES, 5492 .expected_same_dir_rename_result = 0, 5493 /* Access denied. */ 5494 .expected_rename_result = EXDEV, 5495 .expected_exchange_result = EXDEV, 5496 }; 5497 5498 /* The destination directory has less access right. */ 5499 /* clang-format off */ 5500 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) { 5501 /* clang-format on */ 5502 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | 5503 LANDLOCK_ACCESS_FS_MAKE_REG | 5504 LANDLOCK_ACCESS_FS_EXECUTE, 5505 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5506 .expected_read_result = EACCES, 5507 .expected_same_dir_rename_result = 0, 5508 /* Access allowed. */ 5509 .expected_rename_result = 0, 5510 .expected_exchange_result = EXDEV, 5511 }; 5512 5513 /* clang-format off */ 5514 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) { 5515 /* clang-format on */ 5516 .allowed_s2d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5517 LANDLOCK_ACCESS_FS_MAKE_REG, 5518 .expected_read_result = 0, 5519 .expected_same_dir_rename_result = 0, 5520 .expected_rename_result = EXDEV, 5521 .expected_exchange_result = EXDEV, 5522 }; 5523 5524 /* clang-format off */ 5525 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) { 5526 /* clang-format on */ 5527 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5528 .expected_read_result = 0, 5529 .expected_same_dir_rename_result = EACCES, 5530 .expected_rename_result = EACCES, 5531 .expected_exchange_result = EACCES, 5532 }; 5533 5534 /* clang-format off */ 5535 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) { 5536 /* clang-format on */ 5537 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 5538 LANDLOCK_ACCESS_FS_READ_FILE | 5539 LANDLOCK_ACCESS_FS_MAKE_REG, 5540 .expected_read_result = 0, 5541 .expected_same_dir_rename_result = 0, 5542 .expected_rename_result = 0, 5543 .expected_exchange_result = 0, 5544 }; 5545 5546 /* clang-format off */ 5547 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) { 5548 /* clang-format on */ 5549 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | 5550 LANDLOCK_ACCESS_FS_READ_FILE | 5551 LANDLOCK_ACCESS_FS_EXECUTE | 5552 LANDLOCK_ACCESS_FS_MAKE_REG, 5553 .expected_read_result = EACCES, 5554 .expected_same_dir_rename_result = EACCES, 5555 .expected_rename_result = EACCES, 5556 .expected_exchange_result = EACCES, 5557 }; 5558 5559 /* Tests collect_domain_accesses(). */ 5560 /* clang-format off */ 5561 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) { 5562 /* clang-format on */ 5563 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5564 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5565 .expected_read_result = 0, 5566 .expected_same_dir_rename_result = EACCES, 5567 .expected_rename_result = EACCES, 5568 .expected_exchange_result = EACCES, 5569 }; 5570 5571 /* clang-format off */ 5572 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) { 5573 /* clang-format on */ 5574 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5575 LANDLOCK_ACCESS_FS_MAKE_REG, 5576 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 5577 LANDLOCK_ACCESS_FS_MAKE_REG, 5578 .expected_read_result = 0, 5579 .expected_same_dir_rename_result = 0, 5580 .expected_rename_result = EXDEV, 5581 .expected_exchange_result = EXDEV, 5582 }; 5583 5584 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, 5585 s3d1_s4d1_disconnected_rename_even){ 5586 /* clang-format on */ 5587 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5588 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5589 .expected_read_result = EACCES, 5590 .expected_same_dir_rename_result = 0, 5591 .expected_rename_result = 0, 5592 .expected_exchange_result = 0, 5593 }; 5594 5595 /* The destination directory has more access right. */ 5596 /* clang-format off */ 5597 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) { 5598 /* clang-format on */ 5599 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5600 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5601 LANDLOCK_ACCESS_FS_EXECUTE, 5602 .expected_read_result = EACCES, 5603 .expected_same_dir_rename_result = 0, 5604 /* Access denied. */ 5605 .expected_rename_result = EXDEV, 5606 .expected_exchange_result = EXDEV, 5607 }; 5608 5609 /* The destination directory has less access right. */ 5610 /* clang-format off */ 5611 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) { 5612 /* clang-format on */ 5613 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG | 5614 LANDLOCK_ACCESS_FS_EXECUTE, 5615 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5616 .expected_read_result = EACCES, 5617 .expected_same_dir_rename_result = 0, 5618 /* Access allowed. */ 5619 .expected_rename_result = 0, 5620 .expected_exchange_result = EXDEV, 5621 }; 5622 5623 /* clang-format off */ 5624 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) { 5625 /* clang-format on */ 5626 .allowed_f1 = LANDLOCK_ACCESS_FS_READ_FILE, 5627 .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE, 5628 .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE, 5629 .expected_read_result = 0, 5630 .expected_same_dir_rename_result = EACCES, 5631 .expected_rename_result = EACCES, 5632 .expected_exchange_result = EACCES, 5633 }; 5634 5635 TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange) 5636 { 5637 const __u64 handled_access = 5638 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 5639 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 5640 const struct rule rules[] = { 5641 { 5642 .path = TMP_DIR "/s1d1", 5643 .access = variant->allowed_s1d1, 5644 }, 5645 { 5646 .path = TMP_DIR "/s1d1/s1d2", 5647 .access = variant->allowed_s1d2, 5648 }, 5649 { 5650 .path = TMP_DIR "/s1d1/s1d2/s1d31", 5651 .access = variant->allowed_s1d31, 5652 }, 5653 { 5654 .path = TMP_DIR "/s1d1/s1d2/s1d32", 5655 .access = variant->allowed_s1d32, 5656 }, 5657 { 5658 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5659 .access = variant->allowed_s1d41, 5660 }, 5661 { 5662 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5663 .access = variant->allowed_s1d42, 5664 }, 5665 { 5666 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1", 5667 .access = variant->allowed_f1, 5668 }, 5669 { 5670 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2", 5671 .access = variant->allowed_f2, 5672 }, 5673 { 5674 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3", 5675 .access = variant->allowed_f3, 5676 }, 5677 { 5678 .path = TMP_DIR "/s2d1", 5679 .access = variant->allowed_s2d1, 5680 }, 5681 /* s2d2_fd */ 5682 { 5683 .path = TMP_DIR "/s3d1", 5684 .access = variant->allowed_s3d1, 5685 }, 5686 { 5687 .path = TMP_DIR "/s4d1", 5688 .access = variant->allowed_s4d1, 5689 }, 5690 {}, 5691 }; 5692 int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; 5693 5694 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 5695 ASSERT_LE(0, ruleset_fd); 5696 5697 /* Adds rule for the covered directory. */ 5698 if (variant->allowed_s2d2) { 5699 ASSERT_EQ(0, landlock_add_rule( 5700 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 5701 &(struct landlock_path_beneath_attr){ 5702 .parent_fd = self->s2d2_fd, 5703 .allowed_access = 5704 variant->allowed_s2d2, 5705 }, 5706 0)); 5707 } 5708 EXPECT_EQ(0, close(self->s2d2_fd)); 5709 5710 s1d41_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d31/s1d41", 5711 O_DIRECTORY | O_PATH | O_CLOEXEC); 5712 ASSERT_LE(0, s1d41_bind_fd); 5713 s1d42_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d32/s1d42", 5714 O_DIRECTORY | O_PATH | O_CLOEXEC); 5715 ASSERT_LE(0, s1d42_bind_fd); 5716 5717 /* Disconnects and checks source and destination directories. */ 5718 EXPECT_EQ(0, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5719 EXPECT_EQ(0, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5720 /* Renames to make it accessible through s3d1/s1d41 */ 5721 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d31/s1d41", 5722 AT_FDCWD, TMP_DIR "/s3d1/s1d41")); 5723 /* Renames to make it accessible through s4d1/s1d42 */ 5724 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d32/s1d42", 5725 AT_FDCWD, TMP_DIR "/s4d1/s1d42")); 5726 EXPECT_EQ(ENOENT, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY)); 5727 EXPECT_EQ(ENOENT, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY)); 5728 5729 enforce_ruleset(_metadata, ruleset_fd); 5730 EXPECT_EQ(0, close(ruleset_fd)); 5731 5732 EXPECT_EQ(variant->expected_read_result, 5733 test_open_rel(s1d41_bind_fd, "f1", O_RDONLY)); 5734 5735 EXPECT_EQ(variant->expected_rename_result, 5736 test_renameat(s1d41_bind_fd, "f1", s1d42_bind_fd, "f1")); 5737 EXPECT_EQ(variant->expected_exchange_result, 5738 test_exchangeat(s1d41_bind_fd, "f2", s1d42_bind_fd, "f3")); 5739 5740 EXPECT_EQ(variant->expected_same_dir_rename_result, 5741 test_renameat(s1d42_bind_fd, "f4", s1d42_bind_fd, "f5")); 5742 } 5743 5744 /* 5745 * layout5_disconnected_branch before rename: 5746 * 5747 * tmp 5748 * ├── s1d1 5749 * │ └── s1d2 [source of the first bind mount] 5750 * │ └── s1d3 5751 * │ ├── s1d41 5752 * │ │ ├── f1 5753 * │ │ └── f2 5754 * │ └── s1d42 5755 * │ ├── f3 5756 * │ └── f4 5757 * ├── s2d1 5758 * │ └── s2d2 [source of the second bind mount] 5759 * │ └── s2d3 5760 * │ └── s2d4 [first s1d2 bind mount] 5761 * │ └── s1d3 5762 * │ ├── s1d41 5763 * │ │ ├── f1 5764 * │ │ └── f2 5765 * │ └── s1d42 5766 * │ ├── f3 5767 * │ └── f4 5768 * ├── s3d1 5769 * │ └── s3d2 [second s2d2 bind mount] 5770 * │ └── s2d3 5771 * │ └── s2d4 [first s1d2 bind mount] 5772 * │ └── s1d3 5773 * │ ├── s1d41 5774 * │ │ ├── f1 5775 * │ │ └── f2 5776 * │ └── s1d42 5777 * │ ├── f3 5778 * │ └── f4 5779 * └── s4d1 5780 * 5781 * After rename: 5782 * 5783 * tmp 5784 * ├── s1d1 5785 * │ └── s1d2 [source of the first bind mount] 5786 * │ └── s1d3 5787 * │ ├── s1d41 5788 * │ │ ├── f1 5789 * │ │ └── f2 5790 * │ └── s1d42 5791 * │ ├── f3 5792 * │ └── f4 5793 * ├── s2d1 5794 * │ └── s2d2 [source of the second bind mount] 5795 * ├── s3d1 5796 * │ └── s3d2 [second s2d2 bind mount] 5797 * └── s4d1 5798 * └── s2d3 [renamed here] 5799 * └── s2d4 [first s1d2 bind mount] 5800 * └── s1d3 5801 * ├── s1d41 5802 * │ ├── f1 5803 * │ └── f2 5804 * └── s1d42 5805 * ├── f3 5806 * └── f4 5807 * 5808 * Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor: 5809 * 1. first bind mount: s1d3 -> s1d2 5810 * 2. second bind mount: s2d3 5811 * 3. tmp mount: s4d1 -> tmp [disconnected branch] 5812 * 4. second bind mount: s2d2 5813 * 5. tmp mount: s3d1 -> tmp 5814 * 6. parent mounts: [...] -> / 5815 * 5816 * The s4d1 directory is evaluated even if it is not in the s2d2 mount. 5817 */ 5818 5819 /* clang-format off */ 5820 FIXTURE(layout5_disconnected_branch) { 5821 int s2d4_fd, s3d2_fd; 5822 }; 5823 /* clang-format on */ 5824 5825 FIXTURE_SETUP(layout5_disconnected_branch) 5826 { 5827 prepare_layout(_metadata); 5828 5829 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 5830 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 5831 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 5832 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 5833 create_directory(_metadata, TMP_DIR "/s2d1/s2d2/s2d3/s2d4"); 5834 create_directory(_metadata, TMP_DIR "/s3d1/s3d2"); 5835 create_directory(_metadata, TMP_DIR "/s4d1"); 5836 5837 self->s2d4_fd = open(TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 5838 O_DIRECTORY | O_PATH | O_CLOEXEC); 5839 ASSERT_LE(0, self->s2d4_fd); 5840 5841 self->s3d2_fd = 5842 open(TMP_DIR "/s3d1/s3d2", O_DIRECTORY | O_PATH | O_CLOEXEC); 5843 ASSERT_LE(0, self->s3d2_fd); 5844 5845 set_cap(_metadata, CAP_SYS_ADMIN); 5846 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2/s2d3/s2d4", 5847 NULL, MS_BIND, NULL)); 5848 ASSERT_EQ(0, mount(TMP_DIR "/s2d1/s2d2", TMP_DIR "/s3d1/s3d2", NULL, 5849 MS_BIND | MS_REC, NULL)); 5850 clear_cap(_metadata, CAP_SYS_ADMIN); 5851 } 5852 5853 FIXTURE_TEARDOWN_PARENT(layout5_disconnected_branch) 5854 { 5855 /* Bind mounts are handled by namespace lifetime. */ 5856 5857 /* Removes files after renames. */ 5858 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1"); 5859 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2"); 5860 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1"); 5861 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3"); 5862 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4"); 5863 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5"); 5864 5865 cleanup_layout(_metadata); 5866 } 5867 5868 FIXTURE_VARIANT(layout5_disconnected_branch) 5869 { 5870 /* 5871 * Parent of all files. It should always be enforced when testing against 5872 * files under the s1d41 or s1d42 disconnected directories. 5873 */ 5874 const __u64 allowed_base; 5875 /* 5876 * Parent of the first bind mount source. It should always be ignored when 5877 * testing against files under the s1d41 or s1d42 disconnected directories. 5878 */ 5879 const __u64 allowed_s1d1; 5880 const __u64 allowed_s1d2; 5881 const __u64 allowed_s1d3; 5882 const __u64 allowed_s2d1; 5883 const __u64 allowed_s2d2; 5884 const __u64 allowed_s2d3; 5885 const __u64 allowed_s2d4; 5886 const __u64 allowed_s3d1; 5887 const __u64 allowed_s3d2; 5888 const __u64 allowed_s4d1; 5889 5890 /* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */ 5891 const int expected_read_result; 5892 /* 5893 * Expected result of the call to renameat([fd:s1d3]/s1d41/f1, 5894 * [fd:s1d3]/s1d42/f1). 5895 */ 5896 const int expected_rename_result; 5897 /* 5898 * Expected result of the call to renameat([fd:s1d3]/s1d41/f2, 5899 * [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE). 5900 */ 5901 const int expected_exchange_result; 5902 /* 5903 * Expected result of the call to renameat([fd:s1d3]/s1d42/f4, 5904 * [fd:s1d3]/s1d42/f5). 5905 */ 5906 const int expected_same_dir_rename_result; 5907 }; 5908 5909 /* clang-format off */ 5910 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) { 5911 /* clang-format on */ 5912 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER | 5913 LANDLOCK_ACCESS_FS_READ_FILE | 5914 LANDLOCK_ACCESS_FS_EXECUTE | 5915 LANDLOCK_ACCESS_FS_MAKE_REG, 5916 .expected_read_result = EACCES, 5917 .expected_same_dir_rename_result = EACCES, 5918 .expected_rename_result = EACCES, 5919 .expected_exchange_result = EACCES, 5920 }; 5921 5922 /* clang-format off */ 5923 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) { 5924 /* clang-format on */ 5925 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5926 .expected_read_result = 0, 5927 .expected_same_dir_rename_result = EACCES, 5928 .expected_rename_result = EACCES, 5929 .expected_exchange_result = EACCES, 5930 }; 5931 5932 /* clang-format off */ 5933 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) { 5934 /* clang-format on */ 5935 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE | 5936 LANDLOCK_ACCESS_FS_MAKE_REG, 5937 .expected_read_result = 0, 5938 .expected_same_dir_rename_result = 0, 5939 .expected_rename_result = EXDEV, 5940 .expected_exchange_result = EXDEV, 5941 }; 5942 5943 /* clang-format off */ 5944 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) { 5945 /* clang-format on */ 5946 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5947 .expected_read_result = EACCES, 5948 .expected_same_dir_rename_result = 0, 5949 .expected_rename_result = 0, 5950 .expected_exchange_result = 0, 5951 }; 5952 5953 /* clang-format off */ 5954 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) { 5955 /* clang-format on */ 5956 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 5957 .expected_read_result = 0, 5958 .expected_same_dir_rename_result = EACCES, 5959 .expected_rename_result = EACCES, 5960 .expected_exchange_result = EACCES, 5961 }; 5962 5963 /* clang-format off */ 5964 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) { 5965 /* clang-format on */ 5966 .allowed_s1d3 = LANDLOCK_ACCESS_FS_READ_FILE | 5967 LANDLOCK_ACCESS_FS_MAKE_REG, 5968 .expected_read_result = 0, 5969 .expected_same_dir_rename_result = 0, 5970 .expected_rename_result = EXDEV, 5971 .expected_exchange_result = EXDEV, 5972 }; 5973 5974 /* clang-format off */ 5975 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) { 5976 /* clang-format on */ 5977 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 5978 .expected_read_result = EACCES, 5979 .expected_same_dir_rename_result = 0, 5980 .expected_rename_result = 0, 5981 .expected_exchange_result = 0, 5982 }; 5983 5984 /* clang-format off */ 5985 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) { 5986 /* clang-format on */ 5987 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | 5988 LANDLOCK_ACCESS_FS_READ_FILE | 5989 LANDLOCK_ACCESS_FS_EXECUTE | 5990 LANDLOCK_ACCESS_FS_MAKE_REG, 5991 .expected_read_result = 0, 5992 .expected_same_dir_rename_result = 0, 5993 .expected_rename_result = 0, 5994 .expected_exchange_result = 0, 5995 }; 5996 5997 /* clang-format off */ 5998 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) { 5999 /* clang-format on */ 6000 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | 6001 LANDLOCK_ACCESS_FS_READ_FILE | 6002 LANDLOCK_ACCESS_FS_EXECUTE | 6003 LANDLOCK_ACCESS_FS_MAKE_REG, 6004 .expected_read_result = EACCES, 6005 .expected_same_dir_rename_result = EACCES, 6006 .expected_rename_result = EACCES, 6007 .expected_exchange_result = EACCES, 6008 }; 6009 6010 /* clang-format off */ 6011 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) { 6012 /* clang-format on */ 6013 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6014 .expected_read_result = 0, 6015 .expected_same_dir_rename_result = EACCES, 6016 .expected_rename_result = EACCES, 6017 .expected_exchange_result = EACCES, 6018 }; 6019 6020 /* clang-format off */ 6021 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) { 6022 /* clang-format on */ 6023 .allowed_s2d2 = LANDLOCK_ACCESS_FS_READ_FILE | 6024 LANDLOCK_ACCESS_FS_MAKE_REG, 6025 .expected_read_result = 0, 6026 .expected_same_dir_rename_result = 0, 6027 .expected_rename_result = EXDEV, 6028 .expected_exchange_result = EXDEV, 6029 }; 6030 6031 /* clang-format off */ 6032 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) { 6033 /* clang-format on */ 6034 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6035 .expected_read_result = EACCES, 6036 .expected_same_dir_rename_result = 0, 6037 .expected_rename_result = 0, 6038 .expected_exchange_result = 0, 6039 }; 6040 6041 /* clang-format off */ 6042 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) { 6043 /* clang-format on */ 6044 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6045 .expected_read_result = 0, 6046 .expected_same_dir_rename_result = EACCES, 6047 .expected_rename_result = EACCES, 6048 .expected_exchange_result = EACCES, 6049 }; 6050 6051 /* clang-format off */ 6052 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) { 6053 /* clang-format on */ 6054 .allowed_s2d3 = LANDLOCK_ACCESS_FS_READ_FILE | 6055 LANDLOCK_ACCESS_FS_MAKE_REG, 6056 .expected_read_result = 0, 6057 .expected_same_dir_rename_result = 0, 6058 .expected_rename_result = EXDEV, 6059 .expected_exchange_result = EXDEV, 6060 }; 6061 6062 /* clang-format off */ 6063 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) { 6064 /* clang-format on */ 6065 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6066 .expected_read_result = EACCES, 6067 .expected_same_dir_rename_result = 0, 6068 .expected_rename_result = 0, 6069 .expected_exchange_result = 0, 6070 }; 6071 6072 /* clang-format off */ 6073 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) { 6074 /* clang-format on */ 6075 .allowed_s2d4 = LANDLOCK_ACCESS_FS_REFER | 6076 LANDLOCK_ACCESS_FS_READ_FILE | 6077 LANDLOCK_ACCESS_FS_EXECUTE | 6078 LANDLOCK_ACCESS_FS_MAKE_REG, 6079 .expected_read_result = EACCES, 6080 .expected_same_dir_rename_result = EACCES, 6081 .expected_rename_result = EACCES, 6082 .expected_exchange_result = EACCES, 6083 }; 6084 6085 /* clang-format off */ 6086 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) { 6087 /* clang-format on */ 6088 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6089 .expected_read_result = 0, 6090 .expected_same_dir_rename_result = EACCES, 6091 .expected_rename_result = EACCES, 6092 .expected_exchange_result = EACCES, 6093 }; 6094 6095 /* clang-format off */ 6096 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) { 6097 /* clang-format on */ 6098 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6099 LANDLOCK_ACCESS_FS_MAKE_REG, 6100 .expected_read_result = 0, 6101 .expected_same_dir_rename_result = 0, 6102 .expected_rename_result = EXDEV, 6103 .expected_exchange_result = EXDEV, 6104 }; 6105 6106 /* clang-format off */ 6107 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) { 6108 /* clang-format on */ 6109 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6110 .expected_read_result = EACCES, 6111 .expected_same_dir_rename_result = 0, 6112 .expected_rename_result = 0, 6113 .expected_exchange_result = 0, 6114 }; 6115 6116 /* clang-format off */ 6117 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) { 6118 /* clang-format on */ 6119 .allowed_s3d2 = LANDLOCK_ACCESS_FS_REFER | 6120 LANDLOCK_ACCESS_FS_READ_FILE | 6121 LANDLOCK_ACCESS_FS_EXECUTE | 6122 LANDLOCK_ACCESS_FS_MAKE_REG, 6123 .expected_read_result = EACCES, 6124 .expected_same_dir_rename_result = EACCES, 6125 .expected_rename_result = EACCES, 6126 .expected_exchange_result = EACCES, 6127 }; 6128 6129 /* clang-format off */ 6130 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) { 6131 /* clang-format on */ 6132 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, 6133 .expected_read_result = 0, 6134 .expected_same_dir_rename_result = EACCES, 6135 .expected_rename_result = EACCES, 6136 .expected_exchange_result = EACCES, 6137 }; 6138 6139 /* clang-format off */ 6140 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) { 6141 /* clang-format on */ 6142 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | 6143 LANDLOCK_ACCESS_FS_MAKE_REG, 6144 .expected_read_result = 0, 6145 .expected_same_dir_rename_result = 0, 6146 .expected_rename_result = EXDEV, 6147 .expected_exchange_result = EXDEV, 6148 }; 6149 6150 /* clang-format off */ 6151 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) { 6152 /* clang-format on */ 6153 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, 6154 .expected_read_result = EACCES, 6155 .expected_same_dir_rename_result = 0, 6156 .expected_rename_result = 0, 6157 .expected_exchange_result = 0, 6158 }; 6159 6160 TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange) 6161 { 6162 const __u64 handled_access = 6163 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE | 6164 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG; 6165 const struct rule rules[] = { 6166 { 6167 .path = TMP_DIR "/s1d1", 6168 .access = variant->allowed_s1d1, 6169 }, 6170 { 6171 .path = TMP_DIR "/s1d1/s1d2", 6172 .access = variant->allowed_s1d2, 6173 }, 6174 { 6175 .path = TMP_DIR "/s1d1/s1d2/s1d3", 6176 .access = variant->allowed_s1d3, 6177 }, 6178 { 6179 .path = TMP_DIR "/s2d1", 6180 .access = variant->allowed_s2d1, 6181 }, 6182 { 6183 .path = TMP_DIR "/s2d1/s2d2", 6184 .access = variant->allowed_s2d2, 6185 }, 6186 { 6187 .path = TMP_DIR "/s2d1/s2d2/s2d3", 6188 .access = variant->allowed_s2d3, 6189 }, 6190 /* s2d4_fd */ 6191 { 6192 .path = TMP_DIR "/s3d1", 6193 .access = variant->allowed_s3d1, 6194 }, 6195 /* s3d2_fd */ 6196 { 6197 .path = TMP_DIR "/s4d1", 6198 .access = variant->allowed_s4d1, 6199 }, 6200 {}, 6201 }; 6202 int ruleset_fd, s1d3_bind_fd; 6203 6204 ruleset_fd = create_ruleset(_metadata, handled_access, rules); 6205 ASSERT_LE(0, ruleset_fd); 6206 6207 /* Adds rules for the covered directories. */ 6208 if (variant->allowed_s2d4) { 6209 ASSERT_EQ(0, landlock_add_rule( 6210 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6211 &(struct landlock_path_beneath_attr){ 6212 .parent_fd = self->s2d4_fd, 6213 .allowed_access = 6214 variant->allowed_s2d4, 6215 }, 6216 0)); 6217 } 6218 EXPECT_EQ(0, close(self->s2d4_fd)); 6219 6220 if (variant->allowed_s3d2) { 6221 ASSERT_EQ(0, landlock_add_rule( 6222 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 6223 &(struct landlock_path_beneath_attr){ 6224 .parent_fd = self->s3d2_fd, 6225 .allowed_access = 6226 variant->allowed_s3d2, 6227 }, 6228 0)); 6229 } 6230 EXPECT_EQ(0, close(self->s3d2_fd)); 6231 6232 s1d3_bind_fd = open(TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3", 6233 O_DIRECTORY | O_PATH | O_CLOEXEC); 6234 ASSERT_LE(0, s1d3_bind_fd); 6235 6236 /* Disconnects and checks source and destination directories. */ 6237 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6238 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6239 /* Renames to make it accessible through s3d1/s1d41 */ 6240 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s2d1/s2d2/s2d3", 6241 AT_FDCWD, TMP_DIR "/s4d1/s2d3")); 6242 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY)); 6243 EXPECT_EQ(ENOENT, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY)); 6244 6245 enforce_ruleset(_metadata, ruleset_fd); 6246 EXPECT_EQ(0, close(ruleset_fd)); 6247 6248 EXPECT_EQ(variant->expected_read_result, 6249 test_open_rel(s1d3_bind_fd, "s1d41/f1", O_RDONLY)); 6250 6251 EXPECT_EQ(variant->expected_rename_result, 6252 test_renameat(s1d3_bind_fd, "s1d41/f1", s1d3_bind_fd, 6253 "s1d42/f1")); 6254 EXPECT_EQ(variant->expected_exchange_result, 6255 test_exchangeat(s1d3_bind_fd, "s1d41/f2", s1d3_bind_fd, 6256 "s1d42/f3")); 6257 6258 EXPECT_EQ(variant->expected_same_dir_rename_result, 6259 test_renameat(s1d3_bind_fd, "s1d42/f4", s1d3_bind_fd, 6260 "s1d42/f5")); 6261 } 6262 6263 #define LOWER_BASE TMP_DIR "/lower" 6264 #define LOWER_DATA LOWER_BASE "/data" 6265 static const char lower_fl1[] = LOWER_DATA "/fl1"; 6266 static const char lower_dl1[] = LOWER_DATA "/dl1"; 6267 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 6268 static const char lower_fo1[] = LOWER_DATA "/fo1"; 6269 static const char lower_do1[] = LOWER_DATA "/do1"; 6270 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 6271 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 6272 6273 static const char (*lower_base_files[])[] = { 6274 &lower_fl1, 6275 &lower_fo1, 6276 NULL, 6277 }; 6278 static const char (*lower_base_directories[])[] = { 6279 &lower_dl1, 6280 &lower_do1, 6281 NULL, 6282 }; 6283 static const char (*lower_sub_files[])[] = { 6284 &lower_dl1_fl2, 6285 &lower_do1_fo2, 6286 &lower_do1_fl3, 6287 NULL, 6288 }; 6289 6290 #define UPPER_BASE TMP_DIR "/upper" 6291 #define UPPER_DATA UPPER_BASE "/data" 6292 #define UPPER_WORK UPPER_BASE "/work" 6293 static const char upper_fu1[] = UPPER_DATA "/fu1"; 6294 static const char upper_du1[] = UPPER_DATA "/du1"; 6295 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 6296 static const char upper_fo1[] = UPPER_DATA "/fo1"; 6297 static const char upper_do1[] = UPPER_DATA "/do1"; 6298 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 6299 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 6300 6301 static const char (*upper_base_files[])[] = { 6302 &upper_fu1, 6303 &upper_fo1, 6304 NULL, 6305 }; 6306 static const char (*upper_base_directories[])[] = { 6307 &upper_du1, 6308 &upper_do1, 6309 NULL, 6310 }; 6311 static const char (*upper_sub_files[])[] = { 6312 &upper_du1_fu2, 6313 &upper_do1_fo2, 6314 &upper_do1_fu3, 6315 NULL, 6316 }; 6317 6318 #define MERGE_BASE TMP_DIR "/merge" 6319 #define MERGE_DATA MERGE_BASE "/data" 6320 static const char merge_fl1[] = MERGE_DATA "/fl1"; 6321 static const char merge_dl1[] = MERGE_DATA "/dl1"; 6322 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 6323 static const char merge_fu1[] = MERGE_DATA "/fu1"; 6324 static const char merge_du1[] = MERGE_DATA "/du1"; 6325 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 6326 static const char merge_fo1[] = MERGE_DATA "/fo1"; 6327 static const char merge_do1[] = MERGE_DATA "/do1"; 6328 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 6329 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 6330 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 6331 6332 static const char (*merge_base_files[])[] = { 6333 &merge_fl1, 6334 &merge_fu1, 6335 &merge_fo1, 6336 NULL, 6337 }; 6338 static const char (*merge_base_directories[])[] = { 6339 &merge_dl1, 6340 &merge_du1, 6341 &merge_do1, 6342 NULL, 6343 }; 6344 static const char (*merge_sub_files[])[] = { 6345 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 6346 &merge_do1_fl3, &merge_do1_fu3, NULL, 6347 }; 6348 6349 /* 6350 * layout2_overlay hierarchy: 6351 * 6352 * tmp 6353 * ├── lower 6354 * │ └── data 6355 * │ ├── dl1 6356 * │ │ └── fl2 6357 * │ ├── do1 6358 * │ │ ├── fl3 6359 * │ │ └── fo2 6360 * │ ├── fl1 6361 * │ └── fo1 6362 * ├── merge 6363 * │ └── data 6364 * │ ├── dl1 6365 * │ │ └── fl2 6366 * │ ├── do1 6367 * │ │ ├── fl3 6368 * │ │ ├── fo2 6369 * │ │ └── fu3 6370 * │ ├── du1 6371 * │ │ └── fu2 6372 * │ ├── fl1 6373 * │ ├── fo1 6374 * │ └── fu1 6375 * └── upper 6376 * ├── data 6377 * │ ├── do1 6378 * │ │ ├── fo2 6379 * │ │ └── fu3 6380 * │ ├── du1 6381 * │ │ └── fu2 6382 * │ ├── fo1 6383 * │ └── fu1 6384 * └── work 6385 * └── work 6386 */ 6387 6388 FIXTURE(layout2_overlay) 6389 { 6390 bool skip_test; 6391 }; 6392 6393 FIXTURE_SETUP(layout2_overlay) 6394 { 6395 if (!supports_filesystem("overlay")) { 6396 self->skip_test = true; 6397 SKIP(return, "overlayfs is not supported (setup)"); 6398 } 6399 6400 prepare_layout(_metadata); 6401 6402 create_directory(_metadata, LOWER_BASE); 6403 set_cap(_metadata, CAP_SYS_ADMIN); 6404 /* Creates tmpfs mount points to get deterministic overlayfs. */ 6405 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 6406 clear_cap(_metadata, CAP_SYS_ADMIN); 6407 create_file(_metadata, lower_fl1); 6408 create_file(_metadata, lower_dl1_fl2); 6409 create_file(_metadata, lower_fo1); 6410 create_file(_metadata, lower_do1_fo2); 6411 create_file(_metadata, lower_do1_fl3); 6412 6413 create_directory(_metadata, UPPER_BASE); 6414 set_cap(_metadata, CAP_SYS_ADMIN); 6415 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 6416 clear_cap(_metadata, CAP_SYS_ADMIN); 6417 create_file(_metadata, upper_fu1); 6418 create_file(_metadata, upper_du1_fu2); 6419 create_file(_metadata, upper_fo1); 6420 create_file(_metadata, upper_do1_fo2); 6421 create_file(_metadata, upper_do1_fu3); 6422 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 6423 6424 create_directory(_metadata, MERGE_DATA); 6425 set_cap(_metadata, CAP_SYS_ADMIN); 6426 set_cap(_metadata, CAP_DAC_OVERRIDE); 6427 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 6428 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 6429 ",workdir=" UPPER_WORK)); 6430 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6431 clear_cap(_metadata, CAP_SYS_ADMIN); 6432 } 6433 6434 FIXTURE_TEARDOWN_PARENT(layout2_overlay) 6435 { 6436 if (self->skip_test) 6437 SKIP(return, "overlayfs is not supported (teardown)"); 6438 6439 EXPECT_EQ(0, remove_path(lower_do1_fl3)); 6440 EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 6441 EXPECT_EQ(0, remove_path(lower_fl1)); 6442 EXPECT_EQ(0, remove_path(lower_do1_fo2)); 6443 EXPECT_EQ(0, remove_path(lower_fo1)); 6444 6445 /* umount(LOWER_BASE)) is handled by namespace lifetime. */ 6446 EXPECT_EQ(0, remove_path(LOWER_BASE)); 6447 6448 EXPECT_EQ(0, remove_path(upper_do1_fu3)); 6449 EXPECT_EQ(0, remove_path(upper_du1_fu2)); 6450 EXPECT_EQ(0, remove_path(upper_fu1)); 6451 EXPECT_EQ(0, remove_path(upper_do1_fo2)); 6452 EXPECT_EQ(0, remove_path(upper_fo1)); 6453 EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 6454 6455 /* umount(UPPER_BASE)) is handled by namespace lifetime. */ 6456 EXPECT_EQ(0, remove_path(UPPER_BASE)); 6457 6458 /* umount(MERGE_DATA)) is handled by namespace lifetime. */ 6459 EXPECT_EQ(0, remove_path(MERGE_DATA)); 6460 6461 cleanup_layout(_metadata); 6462 } 6463 6464 TEST_F_FORK(layout2_overlay, no_restriction) 6465 { 6466 if (self->skip_test) 6467 SKIP(return, "overlayfs is not supported (test)"); 6468 6469 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 6470 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 6471 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 6472 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 6473 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 6474 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 6475 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 6476 6477 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 6478 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 6479 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 6480 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 6481 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 6482 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 6483 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 6484 6485 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 6486 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 6487 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 6488 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 6489 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 6490 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 6491 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 6492 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 6493 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 6494 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 6495 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 6496 } 6497 6498 #define for_each_path(path_list, path_entry, i) \ 6499 for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 6500 path_entry = *path_list[++i]) 6501 6502 TEST_F_FORK(layout2_overlay, same_content_different_file) 6503 { 6504 /* Sets access right on parent directories of both layers. */ 6505 const struct rule layer1_base[] = { 6506 { 6507 .path = LOWER_BASE, 6508 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6509 }, 6510 { 6511 .path = UPPER_BASE, 6512 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6513 }, 6514 { 6515 .path = MERGE_BASE, 6516 .access = ACCESS_RW, 6517 }, 6518 {}, 6519 }; 6520 const struct rule layer2_data[] = { 6521 { 6522 .path = LOWER_DATA, 6523 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6524 }, 6525 { 6526 .path = UPPER_DATA, 6527 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6528 }, 6529 { 6530 .path = MERGE_DATA, 6531 .access = ACCESS_RW, 6532 }, 6533 {}, 6534 }; 6535 /* Sets access right on directories inside both layers. */ 6536 const struct rule layer3_subdirs[] = { 6537 { 6538 .path = lower_dl1, 6539 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6540 }, 6541 { 6542 .path = lower_do1, 6543 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6544 }, 6545 { 6546 .path = upper_du1, 6547 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6548 }, 6549 { 6550 .path = upper_do1, 6551 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6552 }, 6553 { 6554 .path = merge_dl1, 6555 .access = ACCESS_RW, 6556 }, 6557 { 6558 .path = merge_du1, 6559 .access = ACCESS_RW, 6560 }, 6561 { 6562 .path = merge_do1, 6563 .access = ACCESS_RW, 6564 }, 6565 {}, 6566 }; 6567 /* Tighten access rights to the files. */ 6568 const struct rule layer4_files[] = { 6569 { 6570 .path = lower_dl1_fl2, 6571 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6572 }, 6573 { 6574 .path = lower_do1_fo2, 6575 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6576 }, 6577 { 6578 .path = lower_do1_fl3, 6579 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6580 }, 6581 { 6582 .path = upper_du1_fu2, 6583 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6584 }, 6585 { 6586 .path = upper_do1_fo2, 6587 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6588 }, 6589 { 6590 .path = upper_do1_fu3, 6591 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6592 }, 6593 { 6594 .path = merge_dl1_fl2, 6595 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6596 LANDLOCK_ACCESS_FS_WRITE_FILE, 6597 }, 6598 { 6599 .path = merge_du1_fu2, 6600 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6601 LANDLOCK_ACCESS_FS_WRITE_FILE, 6602 }, 6603 { 6604 .path = merge_do1_fo2, 6605 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6606 LANDLOCK_ACCESS_FS_WRITE_FILE, 6607 }, 6608 { 6609 .path = merge_do1_fl3, 6610 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6611 LANDLOCK_ACCESS_FS_WRITE_FILE, 6612 }, 6613 { 6614 .path = merge_do1_fu3, 6615 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6616 LANDLOCK_ACCESS_FS_WRITE_FILE, 6617 }, 6618 {}, 6619 }; 6620 const struct rule layer5_merge_only[] = { 6621 { 6622 .path = MERGE_DATA, 6623 .access = LANDLOCK_ACCESS_FS_READ_FILE | 6624 LANDLOCK_ACCESS_FS_WRITE_FILE, 6625 }, 6626 {}, 6627 }; 6628 int ruleset_fd; 6629 size_t i; 6630 const char *path_entry; 6631 6632 if (self->skip_test) 6633 SKIP(return, "overlayfs is not supported (test)"); 6634 6635 /* Sets rules on base directories (i.e. outside overlay scope). */ 6636 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 6637 ASSERT_LE(0, ruleset_fd); 6638 enforce_ruleset(_metadata, ruleset_fd); 6639 ASSERT_EQ(0, close(ruleset_fd)); 6640 6641 /* Checks lower layer. */ 6642 for_each_path(lower_base_files, path_entry, i) { 6643 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6644 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6645 } 6646 for_each_path(lower_base_directories, path_entry, i) { 6647 ASSERT_EQ(EACCES, 6648 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6649 } 6650 for_each_path(lower_sub_files, path_entry, i) { 6651 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6652 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6653 } 6654 /* Checks upper layer. */ 6655 for_each_path(upper_base_files, path_entry, i) { 6656 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6657 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6658 } 6659 for_each_path(upper_base_directories, path_entry, i) { 6660 ASSERT_EQ(EACCES, 6661 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6662 } 6663 for_each_path(upper_sub_files, path_entry, i) { 6664 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6665 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6666 } 6667 /* 6668 * Checks that access rights are independent from the lower and upper 6669 * layers: write access to upper files viewed through the merge point 6670 * is still allowed, and write access to lower file viewed (and copied) 6671 * through the merge point is still allowed. 6672 */ 6673 for_each_path(merge_base_files, path_entry, i) { 6674 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6675 } 6676 for_each_path(merge_base_directories, path_entry, i) { 6677 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6678 } 6679 for_each_path(merge_sub_files, path_entry, i) { 6680 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6681 } 6682 6683 /* Sets rules on data directories (i.e. inside overlay scope). */ 6684 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 6685 ASSERT_LE(0, ruleset_fd); 6686 enforce_ruleset(_metadata, ruleset_fd); 6687 ASSERT_EQ(0, close(ruleset_fd)); 6688 6689 /* Checks merge. */ 6690 for_each_path(merge_base_files, path_entry, i) { 6691 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6692 } 6693 for_each_path(merge_base_directories, path_entry, i) { 6694 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6695 } 6696 for_each_path(merge_sub_files, path_entry, i) { 6697 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6698 } 6699 6700 /* Same checks with tighter rules. */ 6701 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 6702 ASSERT_LE(0, ruleset_fd); 6703 enforce_ruleset(_metadata, ruleset_fd); 6704 ASSERT_EQ(0, close(ruleset_fd)); 6705 6706 /* Checks changes for lower layer. */ 6707 for_each_path(lower_base_files, path_entry, i) { 6708 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6709 } 6710 /* Checks changes for upper layer. */ 6711 for_each_path(upper_base_files, path_entry, i) { 6712 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6713 } 6714 /* Checks all merge accesses. */ 6715 for_each_path(merge_base_files, path_entry, i) { 6716 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6717 } 6718 for_each_path(merge_base_directories, path_entry, i) { 6719 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6720 } 6721 for_each_path(merge_sub_files, path_entry, i) { 6722 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6723 } 6724 6725 /* Sets rules directly on overlayed files. */ 6726 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 6727 ASSERT_LE(0, ruleset_fd); 6728 enforce_ruleset(_metadata, ruleset_fd); 6729 ASSERT_EQ(0, close(ruleset_fd)); 6730 6731 /* Checks unchanged accesses on lower layer. */ 6732 for_each_path(lower_sub_files, path_entry, i) { 6733 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6734 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6735 } 6736 /* Checks unchanged accesses on upper layer. */ 6737 for_each_path(upper_sub_files, path_entry, i) { 6738 ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 6739 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 6740 } 6741 /* Checks all merge accesses. */ 6742 for_each_path(merge_base_files, path_entry, i) { 6743 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6744 } 6745 for_each_path(merge_base_directories, path_entry, i) { 6746 ASSERT_EQ(EACCES, 6747 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6748 } 6749 for_each_path(merge_sub_files, path_entry, i) { 6750 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6751 } 6752 6753 /* Only allowes access to the merge hierarchy. */ 6754 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 6755 ASSERT_LE(0, ruleset_fd); 6756 enforce_ruleset(_metadata, ruleset_fd); 6757 ASSERT_EQ(0, close(ruleset_fd)); 6758 6759 /* Checks new accesses on lower layer. */ 6760 for_each_path(lower_sub_files, path_entry, i) { 6761 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6762 } 6763 /* Checks new accesses on upper layer. */ 6764 for_each_path(upper_sub_files, path_entry, i) { 6765 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 6766 } 6767 /* Checks all merge accesses. */ 6768 for_each_path(merge_base_files, path_entry, i) { 6769 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 6770 } 6771 for_each_path(merge_base_directories, path_entry, i) { 6772 ASSERT_EQ(EACCES, 6773 test_open(path_entry, O_RDONLY | O_DIRECTORY)); 6774 } 6775 for_each_path(merge_sub_files, path_entry, i) { 6776 ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 6777 } 6778 } 6779 6780 FIXTURE(layout3_fs) 6781 { 6782 bool has_created_dir; 6783 bool has_created_file; 6784 bool skip_test; 6785 }; 6786 6787 FIXTURE_VARIANT(layout3_fs) 6788 { 6789 const struct mnt_opt mnt; 6790 const char *const file_path; 6791 unsigned int cwd_fs_magic; 6792 }; 6793 6794 /* clang-format off */ 6795 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 6796 /* clang-format on */ 6797 .mnt = { 6798 .type = "tmpfs", 6799 .data = MNT_TMP_DATA, 6800 }, 6801 .file_path = file1_s1d1, 6802 }; 6803 6804 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 6805 .mnt = { 6806 .type = "ramfs", 6807 .data = "mode=700", 6808 }, 6809 .file_path = TMP_DIR "/dir/file", 6810 }; 6811 6812 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 6813 .mnt = { 6814 .type = "cgroup2", 6815 }, 6816 .file_path = TMP_DIR "/test/cgroup.procs", 6817 }; 6818 6819 FIXTURE_VARIANT_ADD(layout3_fs, proc) { 6820 .mnt = { 6821 .type = "proc", 6822 }, 6823 .file_path = TMP_DIR "/self/status", 6824 }; 6825 6826 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 6827 .mnt = { 6828 .type = "sysfs", 6829 }, 6830 .file_path = TMP_DIR "/kernel/notes", 6831 }; 6832 6833 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 6834 .mnt = { 6835 .source = TMP_DIR, 6836 .flags = MS_BIND, 6837 }, 6838 .file_path = TMP_DIR "/dir/file", 6839 .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 6840 }; 6841 6842 static char *dirname_alloc(const char *path) 6843 { 6844 char *dup; 6845 6846 if (!path) 6847 return NULL; 6848 6849 dup = strdup(path); 6850 if (!dup) 6851 return NULL; 6852 6853 return dirname(dup); 6854 } 6855 6856 FIXTURE_SETUP(layout3_fs) 6857 { 6858 struct stat statbuf; 6859 char *dir_path = dirname_alloc(variant->file_path); 6860 6861 if (!supports_filesystem(variant->mnt.type) || 6862 !cwd_matches_fs(variant->cwd_fs_magic)) { 6863 self->skip_test = true; 6864 SKIP(return, "this filesystem is not supported (setup)"); 6865 } 6866 6867 prepare_layout_opt(_metadata, &variant->mnt); 6868 6869 /* Creates directory when required. */ 6870 if (stat(dir_path, &statbuf)) { 6871 set_cap(_metadata, CAP_DAC_OVERRIDE); 6872 EXPECT_EQ(0, mkdir(dir_path, 0700)) 6873 { 6874 TH_LOG("Failed to create directory \"%s\": %s", 6875 dir_path, strerror(errno)); 6876 } 6877 self->has_created_dir = true; 6878 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6879 } 6880 6881 /* Creates file when required. */ 6882 if (stat(variant->file_path, &statbuf)) { 6883 int fd; 6884 6885 set_cap(_metadata, CAP_DAC_OVERRIDE); 6886 fd = creat(variant->file_path, 0600); 6887 EXPECT_LE(0, fd) 6888 { 6889 TH_LOG("Failed to create file \"%s\": %s", 6890 variant->file_path, strerror(errno)); 6891 } 6892 EXPECT_EQ(0, close(fd)); 6893 self->has_created_file = true; 6894 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6895 } 6896 6897 free(dir_path); 6898 } 6899 6900 FIXTURE_TEARDOWN_PARENT(layout3_fs) 6901 { 6902 if (self->skip_test) 6903 SKIP(return, "this filesystem is not supported (teardown)"); 6904 6905 if (self->has_created_file) { 6906 set_cap(_metadata, CAP_DAC_OVERRIDE); 6907 /* 6908 * Don't check for error because the file might already 6909 * have been removed (cf. release_inode test). 6910 */ 6911 unlink(variant->file_path); 6912 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6913 } 6914 6915 if (self->has_created_dir) { 6916 char *dir_path = dirname_alloc(variant->file_path); 6917 6918 set_cap(_metadata, CAP_DAC_OVERRIDE); 6919 /* 6920 * Don't check for error because the directory might already 6921 * have been removed (cf. release_inode test). 6922 */ 6923 rmdir(dir_path); 6924 clear_cap(_metadata, CAP_DAC_OVERRIDE); 6925 free(dir_path); 6926 } 6927 6928 cleanup_layout(_metadata); 6929 } 6930 6931 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 6932 FIXTURE_DATA(layout3_fs) * self, 6933 const FIXTURE_VARIANT(layout3_fs) * variant, 6934 const char *const rule_path) 6935 { 6936 const struct rule layer1_allow_read_file[] = { 6937 { 6938 .path = rule_path, 6939 .access = LANDLOCK_ACCESS_FS_READ_FILE, 6940 }, 6941 {}, 6942 }; 6943 const struct landlock_ruleset_attr layer2_deny_everything_attr = { 6944 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 6945 }; 6946 const char *const dev_null_path = "/dev/null"; 6947 int ruleset_fd; 6948 6949 if (self->skip_test) 6950 SKIP(return, "this filesystem is not supported (test)"); 6951 6952 /* Checks without Landlock. */ 6953 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 6954 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 6955 6956 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 6957 layer1_allow_read_file); 6958 EXPECT_LE(0, ruleset_fd); 6959 enforce_ruleset(_metadata, ruleset_fd); 6960 EXPECT_EQ(0, close(ruleset_fd)); 6961 6962 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 6963 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 6964 6965 /* Forbids directory reading. */ 6966 ruleset_fd = 6967 landlock_create_ruleset(&layer2_deny_everything_attr, 6968 sizeof(layer2_deny_everything_attr), 0); 6969 EXPECT_LE(0, ruleset_fd); 6970 enforce_ruleset(_metadata, ruleset_fd); 6971 EXPECT_EQ(0, close(ruleset_fd)); 6972 6973 /* Checks with Landlock and forbidden access. */ 6974 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 6975 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 6976 } 6977 6978 /* Matrix of tests to check file hierarchy evaluation. */ 6979 6980 TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 6981 { 6982 /* The current directory must not be the root for this test. */ 6983 layer3_fs_tag_inode(_metadata, self, variant, "."); 6984 } 6985 6986 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 6987 { 6988 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 6989 } 6990 6991 TEST_F_FORK(layout3_fs, tag_inode_dir_child) 6992 { 6993 char *dir_path = dirname_alloc(variant->file_path); 6994 6995 layer3_fs_tag_inode(_metadata, self, variant, dir_path); 6996 free(dir_path); 6997 } 6998 6999 TEST_F_FORK(layout3_fs, tag_inode_file) 7000 { 7001 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 7002 } 7003 7004 /* Light version of layout1.release_inodes */ 7005 TEST_F_FORK(layout3_fs, release_inodes) 7006 { 7007 const struct rule layer1[] = { 7008 { 7009 .path = TMP_DIR, 7010 .access = LANDLOCK_ACCESS_FS_READ_DIR, 7011 }, 7012 {}, 7013 }; 7014 int ruleset_fd; 7015 7016 if (self->skip_test) 7017 SKIP(return, "this filesystem is not supported (test)"); 7018 7019 /* Clean up for the teardown to not fail. */ 7020 if (self->has_created_file) 7021 EXPECT_EQ(0, remove_path(variant->file_path)); 7022 7023 if (self->has_created_dir) { 7024 char *dir_path = dirname_alloc(variant->file_path); 7025 7026 /* Don't check for error because of cgroup specificities. */ 7027 remove_path(dir_path); 7028 free(dir_path); 7029 } 7030 7031 ruleset_fd = 7032 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 7033 ASSERT_LE(0, ruleset_fd); 7034 7035 /* Unmount the filesystem while it is being used by a ruleset. */ 7036 set_cap(_metadata, CAP_SYS_ADMIN); 7037 ASSERT_EQ(0, umount(TMP_DIR)); 7038 clear_cap(_metadata, CAP_SYS_ADMIN); 7039 7040 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 7041 set_cap(_metadata, CAP_SYS_ADMIN); 7042 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 7043 clear_cap(_metadata, CAP_SYS_ADMIN); 7044 7045 enforce_ruleset(_metadata, ruleset_fd); 7046 ASSERT_EQ(0, close(ruleset_fd)); 7047 7048 /* Checks that access to the new mount point is denied. */ 7049 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 7050 } 7051 7052 static int matches_log_fs_extra(struct __test_metadata *const _metadata, 7053 int audit_fd, const char *const blockers, 7054 const char *const path, const char *const extra) 7055 { 7056 static const char log_template[] = REGEX_LANDLOCK_PREFIX 7057 " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$"; 7058 char *absolute_path = NULL; 7059 size_t log_match_remaining = sizeof(log_template) + strlen(blockers) + 7060 PATH_MAX * 2 + 7061 (extra ? strlen(extra) : 0) + 1; 7062 char log_match[log_match_remaining]; 7063 char *log_match_cursor = log_match; 7064 size_t chunk_len; 7065 7066 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7067 REGEX_LANDLOCK_PREFIX " blockers=%s path=\"", 7068 blockers); 7069 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7070 return -E2BIG; 7071 7072 /* 7073 * It is assumed that absolute_path does not contain control 7074 * characters nor spaces, see audit_string_contains_control(). 7075 */ 7076 absolute_path = realpath(path, NULL); 7077 if (!absolute_path) 7078 return -errno; 7079 7080 log_match_remaining -= chunk_len; 7081 log_match_cursor += chunk_len; 7082 log_match_cursor = regex_escape(absolute_path, log_match_cursor, 7083 log_match_remaining); 7084 free(absolute_path); 7085 if (log_match_cursor < 0) 7086 return (long long)log_match_cursor; 7087 7088 log_match_remaining -= log_match_cursor - log_match; 7089 chunk_len = snprintf(log_match_cursor, log_match_remaining, 7090 "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$", 7091 extra ?: ""); 7092 if (chunk_len < 0 || chunk_len >= log_match_remaining) 7093 return -E2BIG; 7094 7095 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match, 7096 NULL); 7097 } 7098 7099 static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd, 7100 const char *const blockers, const char *const path) 7101 { 7102 return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL); 7103 } 7104 7105 FIXTURE(audit_layout1) 7106 { 7107 struct audit_filter audit_filter; 7108 int audit_fd; 7109 }; 7110 7111 FIXTURE_SETUP(audit_layout1) 7112 { 7113 prepare_layout(_metadata); 7114 7115 create_layout1(_metadata); 7116 7117 set_cap(_metadata, CAP_AUDIT_CONTROL); 7118 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter); 7119 EXPECT_LE(0, self->audit_fd); 7120 disable_caps(_metadata); 7121 } 7122 7123 FIXTURE_TEARDOWN_PARENT(audit_layout1) 7124 { 7125 remove_layout1(_metadata); 7126 7127 cleanup_layout(_metadata); 7128 7129 EXPECT_EQ(0, audit_cleanup(-1, NULL)); 7130 } 7131 7132 TEST_F(audit_layout1, execute_make) 7133 { 7134 struct audit_records records; 7135 7136 copy_file(_metadata, bin_true, file1_s1d1); 7137 test_execute(_metadata, 0, file1_s1d1); 7138 test_check_exec(_metadata, 0, file1_s1d1); 7139 7140 drop_access_rights(_metadata, 7141 &(struct landlock_ruleset_attr){ 7142 .handled_access_fs = 7143 LANDLOCK_ACCESS_FS_EXECUTE, 7144 }); 7145 7146 test_execute(_metadata, EACCES, file1_s1d1); 7147 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7148 file1_s1d1)); 7149 test_check_exec(_metadata, EACCES, file1_s1d1); 7150 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute", 7151 file1_s1d1)); 7152 7153 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7154 EXPECT_EQ(0, records.access); 7155 EXPECT_EQ(0, records.domain); 7156 } 7157 7158 /* 7159 * Using a set of handled/denied access rights make it possible to check that 7160 * only the blocked ones are logged. 7161 */ 7162 7163 /* clang-format off */ 7164 static const __u64 access_fs_16 = 7165 LANDLOCK_ACCESS_FS_EXECUTE | 7166 LANDLOCK_ACCESS_FS_WRITE_FILE | 7167 LANDLOCK_ACCESS_FS_READ_FILE | 7168 LANDLOCK_ACCESS_FS_READ_DIR | 7169 LANDLOCK_ACCESS_FS_REMOVE_DIR | 7170 LANDLOCK_ACCESS_FS_REMOVE_FILE | 7171 LANDLOCK_ACCESS_FS_MAKE_CHAR | 7172 LANDLOCK_ACCESS_FS_MAKE_DIR | 7173 LANDLOCK_ACCESS_FS_MAKE_REG | 7174 LANDLOCK_ACCESS_FS_MAKE_SOCK | 7175 LANDLOCK_ACCESS_FS_MAKE_FIFO | 7176 LANDLOCK_ACCESS_FS_MAKE_BLOCK | 7177 LANDLOCK_ACCESS_FS_MAKE_SYM | 7178 LANDLOCK_ACCESS_FS_REFER | 7179 LANDLOCK_ACCESS_FS_TRUNCATE | 7180 LANDLOCK_ACCESS_FS_IOCTL_DEV; 7181 /* clang-format on */ 7182 7183 TEST_F(audit_layout1, execute_read) 7184 { 7185 struct audit_records records; 7186 7187 copy_file(_metadata, bin_true, file1_s1d1); 7188 test_execute(_metadata, 0, file1_s1d1); 7189 test_check_exec(_metadata, 0, file1_s1d1); 7190 7191 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7192 .handled_access_fs = access_fs_16, 7193 }); 7194 7195 /* 7196 * The only difference with the previous audit_layout1.execute_read test is 7197 * the extra ",fs\\.read_file" blocked by the executable file. 7198 */ 7199 test_execute(_metadata, EACCES, file1_s1d1); 7200 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7201 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7202 test_check_exec(_metadata, EACCES, file1_s1d1); 7203 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7204 "fs\\.execute,fs\\.read_file", file1_s1d1)); 7205 7206 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7207 EXPECT_EQ(0, records.access); 7208 EXPECT_EQ(0, records.domain); 7209 } 7210 7211 TEST_F(audit_layout1, write_file) 7212 { 7213 struct audit_records records; 7214 7215 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7216 .handled_access_fs = access_fs_16, 7217 }); 7218 7219 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 7220 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7221 "fs\\.write_file", file1_s1d1)); 7222 7223 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7224 EXPECT_EQ(0, records.access); 7225 EXPECT_EQ(1, records.domain); 7226 } 7227 7228 TEST_F(audit_layout1, read_file) 7229 { 7230 struct audit_records records; 7231 7232 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7233 .handled_access_fs = access_fs_16, 7234 }); 7235 7236 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 7237 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file", 7238 file1_s1d1)); 7239 7240 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7241 EXPECT_EQ(0, records.access); 7242 EXPECT_EQ(1, records.domain); 7243 } 7244 7245 TEST_F(audit_layout1, read_dir) 7246 { 7247 struct audit_records records; 7248 7249 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7250 .handled_access_fs = access_fs_16, 7251 }); 7252 7253 EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY)); 7254 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir", 7255 dir_s1d1)); 7256 7257 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7258 EXPECT_EQ(0, records.access); 7259 EXPECT_EQ(1, records.domain); 7260 } 7261 7262 TEST_F(audit_layout1, remove_dir) 7263 { 7264 struct audit_records records; 7265 7266 EXPECT_EQ(0, unlink(file1_s1d3)); 7267 EXPECT_EQ(0, unlink(file2_s1d3)); 7268 7269 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7270 .handled_access_fs = access_fs_16, 7271 }); 7272 7273 EXPECT_EQ(-1, rmdir(dir_s1d3)); 7274 EXPECT_EQ(EACCES, errno); 7275 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7276 "fs\\.remove_dir", dir_s1d2)); 7277 7278 EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 7279 EXPECT_EQ(EACCES, errno); 7280 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7281 "fs\\.remove_dir", dir_s1d2)); 7282 7283 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7284 EXPECT_EQ(0, records.access); 7285 EXPECT_EQ(0, records.domain); 7286 } 7287 7288 TEST_F(audit_layout1, remove_file) 7289 { 7290 struct audit_records records; 7291 7292 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7293 .handled_access_fs = access_fs_16, 7294 }); 7295 7296 EXPECT_EQ(-1, unlink(file1_s1d3)); 7297 EXPECT_EQ(EACCES, errno); 7298 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7299 "fs\\.remove_file", dir_s1d3)); 7300 7301 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7302 EXPECT_EQ(0, records.access); 7303 EXPECT_EQ(1, records.domain); 7304 } 7305 7306 TEST_F(audit_layout1, make_char) 7307 { 7308 struct audit_records records; 7309 7310 EXPECT_EQ(0, unlink(file1_s1d3)); 7311 7312 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7313 .handled_access_fs = access_fs_16, 7314 }); 7315 7316 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, 0)); 7317 EXPECT_EQ(EACCES, errno); 7318 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char", 7319 dir_s1d3)); 7320 7321 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7322 EXPECT_EQ(0, records.access); 7323 EXPECT_EQ(1, records.domain); 7324 } 7325 7326 TEST_F(audit_layout1, make_dir) 7327 { 7328 struct audit_records records; 7329 7330 EXPECT_EQ(0, unlink(file1_s1d3)); 7331 7332 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7333 .handled_access_fs = access_fs_16, 7334 }); 7335 7336 EXPECT_EQ(-1, mkdir(file1_s1d3, 0755)); 7337 EXPECT_EQ(EACCES, errno); 7338 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir", 7339 dir_s1d3)); 7340 7341 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7342 EXPECT_EQ(0, records.access); 7343 EXPECT_EQ(1, records.domain); 7344 } 7345 7346 TEST_F(audit_layout1, make_reg) 7347 { 7348 struct audit_records records; 7349 7350 EXPECT_EQ(0, unlink(file1_s1d3)); 7351 7352 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7353 .handled_access_fs = access_fs_16, 7354 }); 7355 7356 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFREG | 0644, 0)); 7357 EXPECT_EQ(EACCES, errno); 7358 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg", 7359 dir_s1d3)); 7360 7361 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7362 EXPECT_EQ(0, records.access); 7363 EXPECT_EQ(1, records.domain); 7364 } 7365 7366 TEST_F(audit_layout1, make_sock) 7367 { 7368 struct audit_records records; 7369 7370 EXPECT_EQ(0, unlink(file1_s1d3)); 7371 7372 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7373 .handled_access_fs = access_fs_16, 7374 }); 7375 7376 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFSOCK | 0644, 0)); 7377 EXPECT_EQ(EACCES, errno); 7378 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock", 7379 dir_s1d3)); 7380 7381 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7382 EXPECT_EQ(0, records.access); 7383 EXPECT_EQ(1, records.domain); 7384 } 7385 7386 TEST_F(audit_layout1, make_fifo) 7387 { 7388 struct audit_records records; 7389 7390 EXPECT_EQ(0, unlink(file1_s1d3)); 7391 7392 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7393 .handled_access_fs = access_fs_16, 7394 }); 7395 7396 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFIFO | 0644, 0)); 7397 EXPECT_EQ(EACCES, errno); 7398 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo", 7399 dir_s1d3)); 7400 7401 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7402 EXPECT_EQ(0, records.access); 7403 EXPECT_EQ(1, records.domain); 7404 } 7405 7406 TEST_F(audit_layout1, make_block) 7407 { 7408 struct audit_records records; 7409 7410 EXPECT_EQ(0, unlink(file1_s1d3)); 7411 7412 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7413 .handled_access_fs = access_fs_16, 7414 }); 7415 7416 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0)); 7417 EXPECT_EQ(EACCES, errno); 7418 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7419 "fs\\.make_block", dir_s1d3)); 7420 7421 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7422 EXPECT_EQ(0, records.access); 7423 EXPECT_EQ(1, records.domain); 7424 } 7425 7426 TEST_F(audit_layout1, make_sym) 7427 { 7428 struct audit_records records; 7429 7430 EXPECT_EQ(0, unlink(file1_s1d3)); 7431 7432 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7433 .handled_access_fs = access_fs_16, 7434 }); 7435 7436 EXPECT_EQ(-1, symlink("target", file1_s1d3)); 7437 EXPECT_EQ(EACCES, errno); 7438 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym", 7439 dir_s1d3)); 7440 7441 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7442 EXPECT_EQ(0, records.access); 7443 EXPECT_EQ(1, records.domain); 7444 } 7445 7446 TEST_F(audit_layout1, refer_handled) 7447 { 7448 struct audit_records records; 7449 7450 EXPECT_EQ(0, unlink(file1_s1d3)); 7451 7452 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7453 .handled_access_fs = 7454 LANDLOCK_ACCESS_FS_REFER, 7455 }); 7456 7457 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7458 EXPECT_EQ(EXDEV, errno); 7459 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7460 dir_s1d1)); 7461 EXPECT_EQ(0, 7462 matches_log_domain_allocated(self->audit_fd, getpid(), NULL)); 7463 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7464 dir_s1d3)); 7465 7466 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7467 EXPECT_EQ(0, records.access); 7468 EXPECT_EQ(0, records.domain); 7469 } 7470 7471 TEST_F(audit_layout1, refer_make) 7472 { 7473 struct audit_records records; 7474 7475 EXPECT_EQ(0, unlink(file1_s1d3)); 7476 7477 drop_access_rights(_metadata, 7478 &(struct landlock_ruleset_attr){ 7479 .handled_access_fs = 7480 LANDLOCK_ACCESS_FS_MAKE_REG | 7481 LANDLOCK_ACCESS_FS_REFER, 7482 }); 7483 7484 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3)); 7485 EXPECT_EQ(EACCES, errno); 7486 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7487 dir_s1d1)); 7488 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7489 "fs\\.make_reg,fs\\.refer", dir_s1d3)); 7490 7491 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7492 EXPECT_EQ(0, records.access); 7493 EXPECT_EQ(0, records.domain); 7494 } 7495 7496 TEST_F(audit_layout1, refer_rename) 7497 { 7498 struct audit_records records; 7499 7500 EXPECT_EQ(0, unlink(file1_s1d3)); 7501 7502 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7503 .handled_access_fs = access_fs_16, 7504 }); 7505 7506 EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3)); 7507 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7508 "fs\\.remove_file,fs\\.refer", dir_s1d2)); 7509 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7510 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7511 dir_s2d3)); 7512 7513 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7514 EXPECT_EQ(0, records.access); 7515 EXPECT_EQ(0, records.domain); 7516 } 7517 7518 TEST_F(audit_layout1, refer_exchange) 7519 { 7520 struct audit_records records; 7521 7522 EXPECT_EQ(0, unlink(file1_s1d3)); 7523 7524 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7525 .handled_access_fs = access_fs_16, 7526 }); 7527 7528 /* 7529 * The only difference with the previous audit_layout1.refer_rename test is 7530 * the extra ",fs\\.make_reg" blocked by the source directory. 7531 */ 7532 EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3)); 7533 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7534 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7535 dir_s1d2)); 7536 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7537 "fs\\.remove_file,fs\\.make_reg,fs\\.refer", 7538 dir_s2d3)); 7539 7540 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7541 EXPECT_EQ(0, records.access); 7542 EXPECT_EQ(0, records.domain); 7543 } 7544 7545 /* 7546 * This test checks that the audit record is correctly generated when the 7547 * operation is only partially denied. This is the case for rename(2) when the 7548 * source file is allowed to be referenced but the destination directory is not. 7549 * 7550 * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize 7551 * file path walks and prepare for audit support") and commit 058518c20920 7552 * ("landlock: Align partial refer access checks with final ones"). 7553 */ 7554 TEST_F(audit_layout1, refer_rename_half) 7555 { 7556 struct audit_records records; 7557 const struct rule layer1[] = { 7558 { 7559 .path = dir_s2d2, 7560 .access = LANDLOCK_ACCESS_FS_REFER, 7561 }, 7562 {}, 7563 }; 7564 int ruleset_fd = 7565 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 7566 7567 ASSERT_LE(0, ruleset_fd); 7568 enforce_ruleset(_metadata, ruleset_fd); 7569 ASSERT_EQ(0, close(ruleset_fd)); 7570 7571 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 7572 ASSERT_EQ(EXDEV, errno); 7573 7574 /* Only half of the request is denied. */ 7575 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer", 7576 dir_s1d1)); 7577 7578 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7579 EXPECT_EQ(0, records.access); 7580 EXPECT_EQ(1, records.domain); 7581 } 7582 7583 TEST_F(audit_layout1, truncate) 7584 { 7585 struct audit_records records; 7586 7587 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){ 7588 .handled_access_fs = access_fs_16, 7589 }); 7590 7591 EXPECT_EQ(-1, truncate(file1_s1d3, 0)); 7592 EXPECT_EQ(EACCES, errno); 7593 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate", 7594 file1_s1d3)); 7595 7596 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7597 EXPECT_EQ(0, records.access); 7598 EXPECT_EQ(1, records.domain); 7599 } 7600 7601 TEST_F(audit_layout1, ioctl_dev) 7602 { 7603 struct audit_records records; 7604 int fd; 7605 7606 drop_access_rights(_metadata, 7607 &(struct landlock_ruleset_attr){ 7608 .handled_access_fs = 7609 access_fs_16 & 7610 ~LANDLOCK_ACCESS_FS_READ_FILE, 7611 }); 7612 7613 fd = open("/dev/null", O_RDONLY | O_CLOEXEC); 7614 ASSERT_LE(0, fd); 7615 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD)); 7616 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd, 7617 "fs\\.ioctl_dev", "/dev/null", 7618 " ioctlcmd=0x541b")); 7619 7620 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7621 EXPECT_EQ(0, records.access); 7622 EXPECT_EQ(1, records.domain); 7623 } 7624 7625 TEST_F(audit_layout1, mount) 7626 { 7627 struct audit_records records; 7628 7629 drop_access_rights(_metadata, 7630 &(struct landlock_ruleset_attr){ 7631 .handled_access_fs = 7632 LANDLOCK_ACCESS_FS_EXECUTE, 7633 }); 7634 7635 set_cap(_metadata, CAP_SYS_ADMIN); 7636 EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 7637 EXPECT_EQ(EPERM, errno); 7638 clear_cap(_metadata, CAP_SYS_ADMIN); 7639 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, 7640 "fs\\.change_topology", dir_s3d2)); 7641 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records)); 7642 EXPECT_EQ(0, records.access); 7643 EXPECT_EQ(1, records.domain); 7644 } 7645 7646 TEST_HARNESS_MAIN 7647