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