5404 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Landlock tests - Filesystem4 *5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>6 * Copyright © 2020 ANSSI7 * Copyright © 2020-2022 Microsoft Corporation8 */9 10#define _GNU_SOURCE11#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#include "common.h"41 42#ifndef renameat243int renameat2(int olddirfd, const char *oldpath, int newdirfd,44 const char *newpath, unsigned int flags)45{46 return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath,47 flags);48}49#endif50 51#ifndef open_tree52int open_tree(int dfd, const char *filename, unsigned int flags)53{54 return syscall(__NR_open_tree, dfd, filename, flags);55}56#endif57 58#ifndef RENAME_EXCHANGE59#define RENAME_EXCHANGE (1 << 1)60#endif61 62#define BINARY_PATH "./true"63 64/* Paths (sibling number and depth) */65static const char dir_s1d1[] = TMP_DIR "/s1d1";66static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";67static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";68static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";69static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";70static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";71static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";72static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";73static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";74 75static const char dir_s2d1[] = TMP_DIR "/s2d1";76static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";77static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";78static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";79static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";80static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";81static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";82 83static const char dir_s3d1[] = TMP_DIR "/s3d1";84static const char file1_s3d1[] = TMP_DIR "/s3d1/f1";85/* dir_s3d2 is a mount point. */86static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";87static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";88 89/*90 * layout1 hierarchy:91 *92 * tmp93 * ├── s1d194 * │ ├── f195 * │ ├── f296 * │ └── s1d297 * │ ├── f198 * │ ├── f299 * │ └── s1d3100 * │ ├── f1101 * │ └── f2102 * ├── s2d1103 * │ ├── f1104 * │ └── s2d2105 * │ ├── f1106 * │ └── s2d3107 * │ ├── f1108 * │ └── f2109 * └── s3d1110 * ├── f1111 * └── s3d2112 * └── s3d3113 */114 115static bool fgrep(FILE *const inf, const char *const str)116{117 char line[32];118 const int slen = strlen(str);119 120 while (!feof(inf)) {121 if (!fgets(line, sizeof(line), inf))122 break;123 if (strncmp(line, str, slen))124 continue;125 126 return true;127 }128 129 return false;130}131 132static bool supports_filesystem(const char *const filesystem)133{134 char str[32];135 int len;136 bool res = true;137 FILE *const inf = fopen("/proc/filesystems", "r");138 139 /*140 * Consider that the filesystem is supported if we cannot get the141 * supported ones.142 */143 if (!inf)144 return true;145 146 /* filesystem can be null for bind mounts. */147 if (!filesystem)148 goto out;149 150 len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem);151 if (len >= sizeof(str))152 /* Ignores too-long filesystem names. */153 goto out;154 155 res = fgrep(inf, str);156 157out:158 fclose(inf);159 return res;160}161 162static bool cwd_matches_fs(unsigned int fs_magic)163{164 struct statfs statfs_buf;165 166 if (!fs_magic)167 return true;168 169 if (statfs(".", &statfs_buf))170 return true;171 172 return statfs_buf.f_type == fs_magic;173}174 175static void mkdir_parents(struct __test_metadata *const _metadata,176 const char *const path)177{178 char *walker;179 const char *parent;180 int i, err;181 182 ASSERT_NE(path[0], '\0');183 walker = strdup(path);184 ASSERT_NE(NULL, walker);185 parent = walker;186 for (i = 1; walker[i]; i++) {187 if (walker[i] != '/')188 continue;189 walker[i] = '\0';190 err = mkdir(parent, 0700);191 ASSERT_FALSE(err && errno != EEXIST)192 {193 TH_LOG("Failed to create directory \"%s\": %s", parent,194 strerror(errno));195 }196 walker[i] = '/';197 }198 free(walker);199}200 201static void create_directory(struct __test_metadata *const _metadata,202 const char *const path)203{204 mkdir_parents(_metadata, path);205 ASSERT_EQ(0, mkdir(path, 0700))206 {207 TH_LOG("Failed to create directory \"%s\": %s", path,208 strerror(errno));209 }210}211 212static void create_file(struct __test_metadata *const _metadata,213 const char *const path)214{215 mkdir_parents(_metadata, path);216 ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0))217 {218 TH_LOG("Failed to create file \"%s\": %s", path,219 strerror(errno));220 }221}222 223static int remove_path(const char *const path)224{225 char *walker;226 int i, ret, err = 0;227 228 walker = strdup(path);229 if (!walker) {230 err = ENOMEM;231 goto out;232 }233 if (unlink(path) && rmdir(path)) {234 if (errno != ENOENT && errno != ENOTDIR)235 err = errno;236 goto out;237 }238 for (i = strlen(walker); i > 0; i--) {239 if (walker[i] != '/')240 continue;241 walker[i] = '\0';242 ret = rmdir(walker);243 if (ret) {244 if (errno != ENOTEMPTY && errno != EBUSY)245 err = errno;246 goto out;247 }248 if (strcmp(walker, TMP_DIR) == 0)249 goto out;250 }251 252out:253 free(walker);254 return err;255}256 257struct mnt_opt {258 const char *const source;259 const char *const type;260 const unsigned long flags;261 const char *const data;262};263 264#define MNT_TMP_DATA "size=4m,mode=700"265 266static const struct mnt_opt mnt_tmp = {267 .type = "tmpfs",268 .data = MNT_TMP_DATA,269};270 271static int mount_opt(const struct mnt_opt *const mnt, const char *const target)272{273 return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags,274 mnt->data);275}276 277static void prepare_layout_opt(struct __test_metadata *const _metadata,278 const struct mnt_opt *const mnt)279{280 disable_caps(_metadata);281 umask(0077);282 create_directory(_metadata, TMP_DIR);283 284 /*285 * Do not pollute the rest of the system: creates a private mount point286 * for tests relying on pivot_root(2) and move_mount(2).287 */288 set_cap(_metadata, CAP_SYS_ADMIN);289 ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP));290 ASSERT_EQ(0, mount_opt(mnt, TMP_DIR))291 {292 TH_LOG("Failed to mount the %s filesystem: %s", mnt->type,293 strerror(errno));294 /*295 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP()296 * failed, so we need to explicitly do a minimal cleanup to297 * avoid cascading errors with other tests that don't depend on298 * the same filesystem.299 */300 remove_path(TMP_DIR);301 }302 ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));303 clear_cap(_metadata, CAP_SYS_ADMIN);304}305 306static void prepare_layout(struct __test_metadata *const _metadata)307{308 prepare_layout_opt(_metadata, &mnt_tmp);309}310 311static void cleanup_layout(struct __test_metadata *const _metadata)312{313 set_cap(_metadata, CAP_SYS_ADMIN);314 if (umount(TMP_DIR)) {315 /*316 * According to the test environment, the mount point of the317 * current directory may be shared or not, which changes the318 * visibility of the nested TMP_DIR mount point for the test's319 * parent process doing this cleanup.320 */321 ASSERT_EQ(EINVAL, errno);322 }323 clear_cap(_metadata, CAP_SYS_ADMIN);324 EXPECT_EQ(0, remove_path(TMP_DIR));325}326 327/* clang-format off */328FIXTURE(layout0) {};329/* clang-format on */330 331FIXTURE_SETUP(layout0)332{333 prepare_layout(_metadata);334}335 336FIXTURE_TEARDOWN_PARENT(layout0)337{338 cleanup_layout(_metadata);339}340 341static void create_layout1(struct __test_metadata *const _metadata)342{343 create_file(_metadata, file1_s1d1);344 create_file(_metadata, file1_s1d2);345 create_file(_metadata, file1_s1d3);346 create_file(_metadata, file2_s1d1);347 create_file(_metadata, file2_s1d2);348 create_file(_metadata, file2_s1d3);349 350 create_file(_metadata, file1_s2d1);351 create_file(_metadata, file1_s2d2);352 create_file(_metadata, file1_s2d3);353 create_file(_metadata, file2_s2d3);354 355 create_file(_metadata, file1_s3d1);356 create_directory(_metadata, dir_s3d2);357 set_cap(_metadata, CAP_SYS_ADMIN);358 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));359 clear_cap(_metadata, CAP_SYS_ADMIN);360 361 ASSERT_EQ(0, mkdir(dir_s3d3, 0700));362}363 364static void remove_layout1(struct __test_metadata *const _metadata)365{366 EXPECT_EQ(0, remove_path(file2_s1d3));367 EXPECT_EQ(0, remove_path(file2_s1d2));368 EXPECT_EQ(0, remove_path(file2_s1d1));369 EXPECT_EQ(0, remove_path(file1_s1d3));370 EXPECT_EQ(0, remove_path(file1_s1d2));371 EXPECT_EQ(0, remove_path(file1_s1d1));372 EXPECT_EQ(0, remove_path(dir_s1d3));373 374 EXPECT_EQ(0, remove_path(file2_s2d3));375 EXPECT_EQ(0, remove_path(file1_s2d3));376 EXPECT_EQ(0, remove_path(file1_s2d2));377 EXPECT_EQ(0, remove_path(file1_s2d1));378 EXPECT_EQ(0, remove_path(dir_s2d2));379 380 EXPECT_EQ(0, remove_path(file1_s3d1));381 EXPECT_EQ(0, remove_path(dir_s3d3));382 set_cap(_metadata, CAP_SYS_ADMIN);383 umount(dir_s3d2);384 clear_cap(_metadata, CAP_SYS_ADMIN);385 EXPECT_EQ(0, remove_path(dir_s3d2));386}387 388/* clang-format off */389FIXTURE(layout1) {};390/* clang-format on */391 392FIXTURE_SETUP(layout1)393{394 prepare_layout(_metadata);395 396 create_layout1(_metadata);397}398 399FIXTURE_TEARDOWN_PARENT(layout1)400{401 remove_layout1(_metadata);402 403 cleanup_layout(_metadata);404}405 406/*407 * This helper enables to use the ASSERT_* macros and print the line number408 * pointing to the test caller.409 */410static int test_open_rel(const int dirfd, const char *const path,411 const int flags)412{413 int fd;414 415 /* Works with file and directories. */416 fd = openat(dirfd, path, flags | O_CLOEXEC);417 if (fd < 0)418 return errno;419 /*420 * Mixing error codes from close(2) and open(2) should not lead to any421 * (access type) confusion for this test.422 */423 if (close(fd) != 0)424 return errno;425 return 0;426}427 428static int test_open(const char *const path, const int flags)429{430 return test_open_rel(AT_FDCWD, path, flags);431}432 433TEST_F_FORK(layout1, no_restriction)434{435 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));436 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));437 ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));438 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));439 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));440 ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));441 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));442 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));443 444 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));445 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));446 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));447 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));448 ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));449 ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));450 451 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));452 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));453 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));454}455 456TEST_F_FORK(layout1, inval)457{458 struct landlock_path_beneath_attr path_beneath = {459 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |460 LANDLOCK_ACCESS_FS_WRITE_FILE,461 .parent_fd = -1,462 };463 struct landlock_ruleset_attr ruleset_attr = {464 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |465 LANDLOCK_ACCESS_FS_WRITE_FILE,466 };467 int ruleset_fd;468 469 path_beneath.parent_fd =470 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);471 ASSERT_LE(0, path_beneath.parent_fd);472 473 ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);474 ASSERT_LE(0, ruleset_fd);475 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,476 &path_beneath, 0));477 /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */478 ASSERT_EQ(EBADF, errno);479 ASSERT_EQ(0, close(ruleset_fd));480 481 ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);482 ASSERT_LE(0, ruleset_fd);483 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,484 &path_beneath, 0));485 /* Returns EBADFD because ruleset_fd is not a valid ruleset. */486 ASSERT_EQ(EBADFD, errno);487 ASSERT_EQ(0, close(ruleset_fd));488 489 /* Gets a real ruleset. */490 ruleset_fd =491 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);492 ASSERT_LE(0, ruleset_fd);493 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,494 &path_beneath, 0));495 ASSERT_EQ(0, close(path_beneath.parent_fd));496 497 /* Tests without O_PATH. */498 path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);499 ASSERT_LE(0, path_beneath.parent_fd);500 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,501 &path_beneath, 0));502 ASSERT_EQ(0, close(path_beneath.parent_fd));503 504 /* Tests with a ruleset FD. */505 path_beneath.parent_fd = ruleset_fd;506 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,507 &path_beneath, 0));508 ASSERT_EQ(EBADFD, errno);509 510 /* Checks unhandled allowed_access. */511 path_beneath.parent_fd =512 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);513 ASSERT_LE(0, path_beneath.parent_fd);514 515 /* Test with legitimate values. */516 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;517 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,518 &path_beneath, 0));519 ASSERT_EQ(EINVAL, errno);520 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;521 522 /* Tests with denied-by-default access right. */523 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER;524 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,525 &path_beneath, 0));526 ASSERT_EQ(EINVAL, errno);527 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;528 529 /* Test with unknown (64-bits) value. */530 path_beneath.allowed_access |= (1ULL << 60);531 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,532 &path_beneath, 0));533 ASSERT_EQ(EINVAL, errno);534 path_beneath.allowed_access &= ~(1ULL << 60);535 536 /* Test with no access. */537 path_beneath.allowed_access = 0;538 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,539 &path_beneath, 0));540 ASSERT_EQ(ENOMSG, errno);541 path_beneath.allowed_access &= ~(1ULL << 60);542 543 ASSERT_EQ(0, close(path_beneath.parent_fd));544 545 /* Enforces the ruleset. */546 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));547 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));548 549 ASSERT_EQ(0, close(ruleset_fd));550}551 552/* clang-format off */553 554#define ACCESS_FILE ( \555 LANDLOCK_ACCESS_FS_EXECUTE | \556 LANDLOCK_ACCESS_FS_WRITE_FILE | \557 LANDLOCK_ACCESS_FS_READ_FILE | \558 LANDLOCK_ACCESS_FS_TRUNCATE | \559 LANDLOCK_ACCESS_FS_IOCTL_DEV)560 561#define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV562 563#define ACCESS_ALL ( \564 ACCESS_FILE | \565 LANDLOCK_ACCESS_FS_READ_DIR | \566 LANDLOCK_ACCESS_FS_REMOVE_DIR | \567 LANDLOCK_ACCESS_FS_REMOVE_FILE | \568 LANDLOCK_ACCESS_FS_MAKE_CHAR | \569 LANDLOCK_ACCESS_FS_MAKE_DIR | \570 LANDLOCK_ACCESS_FS_MAKE_REG | \571 LANDLOCK_ACCESS_FS_MAKE_SOCK | \572 LANDLOCK_ACCESS_FS_MAKE_FIFO | \573 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \574 LANDLOCK_ACCESS_FS_MAKE_SYM | \575 LANDLOCK_ACCESS_FS_REFER)576 577/* clang-format on */578 579TEST_F_FORK(layout1, file_and_dir_access_rights)580{581 __u64 access;582 int err;583 struct landlock_path_beneath_attr path_beneath_file = {},584 path_beneath_dir = {};585 struct landlock_ruleset_attr ruleset_attr = {586 .handled_access_fs = ACCESS_ALL,587 };588 const int ruleset_fd =589 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);590 591 ASSERT_LE(0, ruleset_fd);592 593 /* Tests access rights for files. */594 path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);595 ASSERT_LE(0, path_beneath_file.parent_fd);596 597 /* Tests access rights for directories. */598 path_beneath_dir.parent_fd =599 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);600 ASSERT_LE(0, path_beneath_dir.parent_fd);601 602 for (access = 1; access <= ACCESS_LAST; access <<= 1) {603 path_beneath_dir.allowed_access = access;604 ASSERT_EQ(0, landlock_add_rule(ruleset_fd,605 LANDLOCK_RULE_PATH_BENEATH,606 &path_beneath_dir, 0));607 608 path_beneath_file.allowed_access = access;609 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,610 &path_beneath_file, 0);611 if (access & ACCESS_FILE) {612 ASSERT_EQ(0, err);613 } else {614 ASSERT_EQ(-1, err);615 ASSERT_EQ(EINVAL, errno);616 }617 }618 ASSERT_EQ(0, close(path_beneath_file.parent_fd));619 ASSERT_EQ(0, close(path_beneath_dir.parent_fd));620 ASSERT_EQ(0, close(ruleset_fd));621}622 623TEST_F_FORK(layout0, ruleset_with_unknown_access)624{625 __u64 access_mask;626 627 for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;628 access_mask >>= 1) {629 struct landlock_ruleset_attr ruleset_attr = {630 .handled_access_fs = access_mask,631 };632 633 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,634 sizeof(ruleset_attr), 0));635 ASSERT_EQ(EINVAL, errno);636 }637}638 639TEST_F_FORK(layout0, rule_with_unknown_access)640{641 __u64 access;642 struct landlock_path_beneath_attr path_beneath = {};643 const struct landlock_ruleset_attr ruleset_attr = {644 .handled_access_fs = ACCESS_ALL,645 };646 const int ruleset_fd =647 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);648 649 ASSERT_LE(0, ruleset_fd);650 651 path_beneath.parent_fd =652 open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC);653 ASSERT_LE(0, path_beneath.parent_fd);654 655 for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) {656 path_beneath.allowed_access = access;657 EXPECT_EQ(-1, landlock_add_rule(ruleset_fd,658 LANDLOCK_RULE_PATH_BENEATH,659 &path_beneath, 0));660 EXPECT_EQ(EINVAL, errno);661 }662 ASSERT_EQ(0, close(path_beneath.parent_fd));663 ASSERT_EQ(0, close(ruleset_fd));664}665 666TEST_F_FORK(layout1, rule_with_unhandled_access)667{668 struct landlock_ruleset_attr ruleset_attr = {669 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,670 };671 struct landlock_path_beneath_attr path_beneath = {};672 int ruleset_fd;673 __u64 access;674 675 ruleset_fd =676 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);677 ASSERT_LE(0, ruleset_fd);678 679 path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);680 ASSERT_LE(0, path_beneath.parent_fd);681 682 for (access = 1; access > 0; access <<= 1) {683 int err;684 685 path_beneath.allowed_access = access;686 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,687 &path_beneath, 0);688 if (access == ruleset_attr.handled_access_fs) {689 EXPECT_EQ(0, err);690 } else {691 EXPECT_EQ(-1, err);692 EXPECT_EQ(EINVAL, errno);693 }694 }695 696 EXPECT_EQ(0, close(path_beneath.parent_fd));697 EXPECT_EQ(0, close(ruleset_fd));698}699 700static void add_path_beneath(struct __test_metadata *const _metadata,701 const int ruleset_fd, const __u64 allowed_access,702 const char *const path)703{704 struct landlock_path_beneath_attr path_beneath = {705 .allowed_access = allowed_access,706 };707 708 path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);709 ASSERT_LE(0, path_beneath.parent_fd)710 {711 TH_LOG("Failed to open directory \"%s\": %s", path,712 strerror(errno));713 }714 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,715 &path_beneath, 0))716 {717 TH_LOG("Failed to update the ruleset with \"%s\": %s", path,718 strerror(errno));719 }720 ASSERT_EQ(0, close(path_beneath.parent_fd));721}722 723struct rule {724 const char *path;725 __u64 access;726};727 728/* clang-format off */729 730#define ACCESS_RO ( \731 LANDLOCK_ACCESS_FS_READ_FILE | \732 LANDLOCK_ACCESS_FS_READ_DIR)733 734#define ACCESS_RW ( \735 ACCESS_RO | \736 LANDLOCK_ACCESS_FS_WRITE_FILE)737 738/* clang-format on */739 740static int create_ruleset(struct __test_metadata *const _metadata,741 const __u64 handled_access_fs,742 const struct rule rules[])743{744 int ruleset_fd, i;745 struct landlock_ruleset_attr ruleset_attr = {746 .handled_access_fs = handled_access_fs,747 };748 749 ASSERT_NE(NULL, rules)750 {751 TH_LOG("No rule list");752 }753 ASSERT_NE(NULL, rules[0].path)754 {755 TH_LOG("Empty rule list");756 }757 758 ruleset_fd =759 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);760 ASSERT_LE(0, ruleset_fd)761 {762 TH_LOG("Failed to create a ruleset: %s", strerror(errno));763 }764 765 for (i = 0; rules[i].path; i++) {766 if (!rules[i].access)767 continue;768 769 add_path_beneath(_metadata, ruleset_fd, rules[i].access,770 rules[i].path);771 }772 return ruleset_fd;773}774 775TEST_F_FORK(layout0, proc_nsfs)776{777 const struct rule rules[] = {778 {779 .path = "/dev/null",780 .access = LANDLOCK_ACCESS_FS_READ_FILE |781 LANDLOCK_ACCESS_FS_WRITE_FILE,782 },783 {},784 };785 struct landlock_path_beneath_attr path_beneath;786 const int ruleset_fd = create_ruleset(787 _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR,788 rules);789 790 ASSERT_LE(0, ruleset_fd);791 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));792 793 enforce_ruleset(_metadata, ruleset_fd);794 795 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));796 ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));797 ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));798 ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));799 800 ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));801 ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));802 ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));803 /*804 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a805 * disconnected path. Such path cannot be identified and must then be806 * allowed.807 */808 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));809 810 /*811 * Checks that it is not possible to add nsfs-like filesystem812 * references to a ruleset.813 */814 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |815 LANDLOCK_ACCESS_FS_WRITE_FILE,816 path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);817 ASSERT_LE(0, path_beneath.parent_fd);818 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,819 &path_beneath, 0));820 ASSERT_EQ(EBADFD, errno);821 ASSERT_EQ(0, close(path_beneath.parent_fd));822}823 824TEST_F_FORK(layout0, unpriv)825{826 const struct rule rules[] = {827 {828 .path = TMP_DIR,829 .access = ACCESS_RO,830 },831 {},832 };833 int ruleset_fd;834 835 drop_caps(_metadata);836 837 ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);838 ASSERT_LE(0, ruleset_fd);839 ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));840 ASSERT_EQ(EPERM, errno);841 842 /* enforce_ruleset() calls prctl(no_new_privs). */843 enforce_ruleset(_metadata, ruleset_fd);844 ASSERT_EQ(0, close(ruleset_fd));845}846 847TEST_F_FORK(layout1, effective_access)848{849 const struct rule rules[] = {850 {851 .path = dir_s1d2,852 .access = ACCESS_RO,853 },854 {855 .path = file1_s2d2,856 .access = LANDLOCK_ACCESS_FS_READ_FILE |857 LANDLOCK_ACCESS_FS_WRITE_FILE,858 },859 {},860 };861 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);862 char buf;863 int reg_fd;864 865 ASSERT_LE(0, ruleset_fd);866 enforce_ruleset(_metadata, ruleset_fd);867 ASSERT_EQ(0, close(ruleset_fd));868 869 /* Tests on a directory (with or without O_PATH). */870 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));871 ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH));872 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));873 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH));874 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));875 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH));876 877 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));878 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));879 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));880 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));881 882 /* Tests on a file (with or without O_PATH). */883 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));884 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH));885 886 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));887 888 /* Checks effective read and write actions. */889 reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);890 ASSERT_LE(0, reg_fd);891 ASSERT_EQ(1, write(reg_fd, ".", 1));892 ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));893 ASSERT_EQ(1, read(reg_fd, &buf, 1));894 ASSERT_EQ('.', buf);895 ASSERT_EQ(0, close(reg_fd));896 897 /* Just in case, double-checks effective actions. */898 reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);899 ASSERT_LE(0, reg_fd);900 ASSERT_EQ(-1, write(reg_fd, &buf, 1));901 ASSERT_EQ(EBADF, errno);902 ASSERT_EQ(0, close(reg_fd));903}904 905TEST_F_FORK(layout1, unhandled_access)906{907 const struct rule rules[] = {908 {909 .path = dir_s1d2,910 .access = ACCESS_RO,911 },912 {},913 };914 /* Here, we only handle read accesses, not write accesses. */915 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);916 917 ASSERT_LE(0, ruleset_fd);918 enforce_ruleset(_metadata, ruleset_fd);919 ASSERT_EQ(0, close(ruleset_fd));920 921 /*922 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,923 * opening for write-only should be allowed, but not read-write.924 */925 ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));926 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));927 928 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));929 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));930}931 932TEST_F_FORK(layout1, ruleset_overlap)933{934 const struct rule rules[] = {935 /* These rules should be ORed among them. */936 {937 .path = dir_s1d2,938 .access = LANDLOCK_ACCESS_FS_READ_FILE |939 LANDLOCK_ACCESS_FS_WRITE_FILE,940 },941 {942 .path = dir_s1d2,943 .access = LANDLOCK_ACCESS_FS_READ_FILE |944 LANDLOCK_ACCESS_FS_READ_DIR,945 },946 {},947 };948 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);949 950 ASSERT_LE(0, ruleset_fd);951 enforce_ruleset(_metadata, ruleset_fd);952 ASSERT_EQ(0, close(ruleset_fd));953 954 /* Checks s1d1 hierarchy. */955 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));956 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));957 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));958 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));959 960 /* Checks s1d2 hierarchy. */961 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));962 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));963 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));964 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));965 966 /* Checks s1d3 hierarchy. */967 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));968 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));969 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));970 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));971}972 973TEST_F_FORK(layout1, layer_rule_unions)974{975 const struct rule layer1[] = {976 {977 .path = dir_s1d2,978 .access = LANDLOCK_ACCESS_FS_READ_FILE,979 },980 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */981 {982 .path = dir_s1d3,983 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,984 },985 {},986 };987 const struct rule layer2[] = {988 /* Doesn't change anything from layer1. */989 {990 .path = dir_s1d2,991 .access = LANDLOCK_ACCESS_FS_READ_FILE |992 LANDLOCK_ACCESS_FS_WRITE_FILE,993 },994 {},995 };996 const struct rule layer3[] = {997 /* Only allows write (but not read) to dir_s1d3. */998 {999 .path = dir_s1d2,1000 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,1001 },1002 {},1003 };1004 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1);1005 1006 ASSERT_LE(0, ruleset_fd);1007 enforce_ruleset(_metadata, ruleset_fd);1008 ASSERT_EQ(0, close(ruleset_fd));1009 1010 /* Checks s1d1 hierarchy with layer1. */1011 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1012 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1013 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));1014 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1015 1016 /* Checks s1d2 hierarchy with layer1. */1017 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));1018 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1019 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));1020 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1021 1022 /* Checks s1d3 hierarchy with layer1. */1023 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1024 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));1025 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */1026 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1027 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1028 1029 /* Doesn't change anything from layer1. */1030 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2);1031 ASSERT_LE(0, ruleset_fd);1032 enforce_ruleset(_metadata, ruleset_fd);1033 ASSERT_EQ(0, close(ruleset_fd));1034 1035 /* Checks s1d1 hierarchy with layer2. */1036 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1037 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1038 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));1039 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1040 1041 /* Checks s1d2 hierarchy with layer2. */1042 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));1043 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1044 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));1045 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1046 1047 /* Checks s1d3 hierarchy with layer2. */1048 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1049 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));1050 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */1051 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1052 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1053 1054 /* Only allows write (but not read) to dir_s1d3. */1055 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3);1056 ASSERT_LE(0, ruleset_fd);1057 enforce_ruleset(_metadata, ruleset_fd);1058 ASSERT_EQ(0, close(ruleset_fd));1059 1060 /* Checks s1d1 hierarchy with layer3. */1061 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1062 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1063 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));1064 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1065 1066 /* Checks s1d2 hierarchy with layer3. */1067 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));1068 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1069 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));1070 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1071 1072 /* Checks s1d3 hierarchy with layer3. */1073 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));1074 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));1075 /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */1076 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR));1077 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1078}1079 1080TEST_F_FORK(layout1, non_overlapping_accesses)1081{1082 const struct rule layer1[] = {1083 {1084 .path = dir_s1d2,1085 .access = LANDLOCK_ACCESS_FS_MAKE_REG,1086 },1087 {},1088 };1089 const struct rule layer2[] = {1090 {1091 .path = dir_s1d3,1092 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,1093 },1094 {},1095 };1096 int ruleset_fd;1097 1098 ASSERT_EQ(0, unlink(file1_s1d1));1099 ASSERT_EQ(0, unlink(file1_s1d2));1100 1101 ruleset_fd =1102 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1);1103 ASSERT_LE(0, ruleset_fd);1104 enforce_ruleset(_metadata, ruleset_fd);1105 ASSERT_EQ(0, close(ruleset_fd));1106 1107 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));1108 ASSERT_EQ(EACCES, errno);1109 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));1110 ASSERT_EQ(0, unlink(file1_s1d2));1111 1112 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE,1113 layer2);1114 ASSERT_LE(0, ruleset_fd);1115 enforce_ruleset(_metadata, ruleset_fd);1116 ASSERT_EQ(0, close(ruleset_fd));1117 1118 /* Unchanged accesses for file creation. */1119 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));1120 ASSERT_EQ(EACCES, errno);1121 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));1122 1123 /* Checks file removing. */1124 ASSERT_EQ(-1, unlink(file1_s1d2));1125 ASSERT_EQ(EACCES, errno);1126 ASSERT_EQ(0, unlink(file1_s1d3));1127}1128 1129TEST_F_FORK(layout1, interleaved_masked_accesses)1130{1131 /*1132 * Checks overly restrictive rules:1133 * layer 1: allows R s1d1/s1d2/s1d3/file11134 * layer 2: allows RW s1d1/s1d2/s1d31135 * allows W s1d1/s1d21136 * denies R s1d1/s1d21137 * layer 3: allows R s1d11138 * layer 4: allows R s1d1/s1d21139 * denies W s1d1/s1d21140 * layer 5: allows R s1d1/s1d21141 * layer 6: allows X ----1142 * layer 7: allows W s1d1/s1d21143 * denies R s1d1/s1d21144 */1145 const struct rule layer1_read[] = {1146 /* Allows read access to file1_s1d3 with the first layer. */1147 {1148 .path = file1_s1d3,1149 .access = LANDLOCK_ACCESS_FS_READ_FILE,1150 },1151 {},1152 };1153 /* First rule with write restrictions. */1154 const struct rule layer2_read_write[] = {1155 /* Start by granting read-write access via its parent directory... */1156 {1157 .path = dir_s1d3,1158 .access = LANDLOCK_ACCESS_FS_READ_FILE |1159 LANDLOCK_ACCESS_FS_WRITE_FILE,1160 },1161 /* ...but also denies read access via its grandparent directory. */1162 {1163 .path = dir_s1d2,1164 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,1165 },1166 {},1167 };1168 const struct rule layer3_read[] = {1169 /* Allows read access via its great-grandparent directory. */1170 {1171 .path = dir_s1d1,1172 .access = LANDLOCK_ACCESS_FS_READ_FILE,1173 },1174 {},1175 };1176 const struct rule layer4_read_write[] = {1177 /*1178 * Try to confuse the deny access by denying write (but not1179 * read) access via its grandparent directory.1180 */1181 {1182 .path = dir_s1d2,1183 .access = LANDLOCK_ACCESS_FS_READ_FILE,1184 },1185 {},1186 };1187 const struct rule layer5_read[] = {1188 /*1189 * Try to override layer2's deny read access by explicitly1190 * allowing read access via file1_s1d3's grandparent.1191 */1192 {1193 .path = dir_s1d2,1194 .access = LANDLOCK_ACCESS_FS_READ_FILE,1195 },1196 {},1197 };1198 const struct rule layer6_execute[] = {1199 /*1200 * Restricts an unrelated file hierarchy with a new access1201 * (non-overlapping) type.1202 */1203 {1204 .path = dir_s2d1,1205 .access = LANDLOCK_ACCESS_FS_EXECUTE,1206 },1207 {},1208 };1209 const struct rule layer7_read_write[] = {1210 /*1211 * Finally, denies read access to file1_s1d3 via its1212 * grandparent.1213 */1214 {1215 .path = dir_s1d2,1216 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,1217 },1218 {},1219 };1220 int ruleset_fd;1221 1222 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,1223 layer1_read);1224 ASSERT_LE(0, ruleset_fd);1225 enforce_ruleset(_metadata, ruleset_fd);1226 ASSERT_EQ(0, close(ruleset_fd));1227 1228 /* Checks that read access is granted for file1_s1d3 with layer 1. */1229 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1230 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1231 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));1232 1233 ruleset_fd = create_ruleset(_metadata,1234 LANDLOCK_ACCESS_FS_READ_FILE |1235 LANDLOCK_ACCESS_FS_WRITE_FILE,1236 layer2_read_write);1237 ASSERT_LE(0, ruleset_fd);1238 enforce_ruleset(_metadata, ruleset_fd);1239 ASSERT_EQ(0, close(ruleset_fd));1240 1241 /* Checks that previous access rights are unchanged with layer 2. */1242 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1243 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1244 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));1245 1246 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,1247 layer3_read);1248 ASSERT_LE(0, ruleset_fd);1249 enforce_ruleset(_metadata, ruleset_fd);1250 ASSERT_EQ(0, close(ruleset_fd));1251 1252 /* Checks that previous access rights are unchanged with layer 3. */1253 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));1254 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1255 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));1256 1257 /* This time, denies write access for the file hierarchy. */1258 ruleset_fd = create_ruleset(_metadata,1259 LANDLOCK_ACCESS_FS_READ_FILE |1260 LANDLOCK_ACCESS_FS_WRITE_FILE,1261 layer4_read_write);1262 ASSERT_LE(0, ruleset_fd);1263 enforce_ruleset(_metadata, ruleset_fd);1264 ASSERT_EQ(0, close(ruleset_fd));1265 1266 /*1267 * Checks that the only change with layer 4 is that write access is1268 * denied.1269 */1270 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1271 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1272 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1273 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1274 1275 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,1276 layer5_read);1277 ASSERT_LE(0, ruleset_fd);1278 enforce_ruleset(_metadata, ruleset_fd);1279 ASSERT_EQ(0, close(ruleset_fd));1280 1281 /* Checks that previous access rights are unchanged with layer 5. */1282 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1283 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1284 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1285 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1286 1287 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE,1288 layer6_execute);1289 ASSERT_LE(0, ruleset_fd);1290 enforce_ruleset(_metadata, ruleset_fd);1291 ASSERT_EQ(0, close(ruleset_fd));1292 1293 /* Checks that previous access rights are unchanged with layer 6. */1294 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1295 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1296 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1297 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1298 1299 ruleset_fd = create_ruleset(_metadata,1300 LANDLOCK_ACCESS_FS_READ_FILE |1301 LANDLOCK_ACCESS_FS_WRITE_FILE,1302 layer7_read_write);1303 ASSERT_LE(0, ruleset_fd);1304 enforce_ruleset(_metadata, ruleset_fd);1305 ASSERT_EQ(0, close(ruleset_fd));1306 1307 /* Checks read access is now denied with layer 7. */1308 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));1309 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1310 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));1311 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));1312}1313 1314TEST_F_FORK(layout1, inherit_subset)1315{1316 const struct rule rules[] = {1317 {1318 .path = dir_s1d2,1319 .access = LANDLOCK_ACCESS_FS_READ_FILE |1320 LANDLOCK_ACCESS_FS_READ_DIR,1321 },1322 {},1323 };1324 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1325 1326 ASSERT_LE(0, ruleset_fd);1327 enforce_ruleset(_metadata, ruleset_fd);1328 1329 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1330 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1331 1332 /* Write access is forbidden. */1333 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1334 /* Readdir access is allowed. */1335 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1336 1337 /* Write access is forbidden. */1338 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1339 /* Readdir access is allowed. */1340 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1341 1342 /*1343 * Tests shared rule extension: the following rules should not grant1344 * any new access, only remove some. Once enforced, these rules are1345 * ANDed with the previous ones.1346 */1347 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,1348 dir_s1d2);1349 /*1350 * According to ruleset_fd, dir_s1d2 should now have the1351 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE1352 * access rights (even if this directory is opened a second time).1353 * However, when enforcing this updated ruleset, the ruleset tied to1354 * the current process (i.e. its domain) will still only have the1355 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and1356 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but1357 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would1358 * be a privilege escalation.1359 */1360 enforce_ruleset(_metadata, ruleset_fd);1361 1362 /* Same tests and results as above. */1363 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1364 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1365 1366 /* It is still forbidden to write in file1_s1d2. */1367 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1368 /* Readdir access is still allowed. */1369 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1370 1371 /* It is still forbidden to write in file1_s1d3. */1372 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1373 /* Readdir access is still allowed. */1374 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1375 1376 /*1377 * Try to get more privileges by adding new access rights to the parent1378 * directory: dir_s1d1.1379 */1380 add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1);1381 enforce_ruleset(_metadata, ruleset_fd);1382 1383 /* Same tests and results as above. */1384 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1385 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1386 1387 /* It is still forbidden to write in file1_s1d2. */1388 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1389 /* Readdir access is still allowed. */1390 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1391 1392 /* It is still forbidden to write in file1_s1d3. */1393 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1394 /* Readdir access is still allowed. */1395 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1396 1397 /*1398 * Now, dir_s1d3 get a new rule tied to it, only allowing1399 * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is1400 * that there was no rule tied to it before.1401 */1402 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,1403 dir_s1d3);1404 enforce_ruleset(_metadata, ruleset_fd);1405 ASSERT_EQ(0, close(ruleset_fd));1406 1407 /*1408 * Same tests and results as above, except for open(dir_s1d3) which is1409 * now denied because the new rule mask the rule previously inherited1410 * from dir_s1d2.1411 */1412 1413 /* Same tests and results as above. */1414 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));1415 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));1416 1417 /* It is still forbidden to write in file1_s1d2. */1418 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));1419 /* Readdir access is still allowed. */1420 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1421 1422 /* It is still forbidden to write in file1_s1d3. */1423 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));1424 /*1425 * Readdir of dir_s1d3 is still allowed because of the OR policy inside1426 * the same layer.1427 */1428 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1429}1430 1431TEST_F_FORK(layout1, inherit_superset)1432{1433 const struct rule rules[] = {1434 {1435 .path = dir_s1d3,1436 .access = ACCESS_RO,1437 },1438 {},1439 };1440 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1441 1442 ASSERT_LE(0, ruleset_fd);1443 enforce_ruleset(_metadata, ruleset_fd);1444 1445 /* Readdir access is denied for dir_s1d2. */1446 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1447 /* Readdir access is allowed for dir_s1d3. */1448 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1449 /* File access is allowed for file1_s1d3. */1450 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1451 1452 /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */1453 add_path_beneath(_metadata, ruleset_fd,1454 LANDLOCK_ACCESS_FS_READ_FILE |1455 LANDLOCK_ACCESS_FS_READ_DIR,1456 dir_s1d2);1457 enforce_ruleset(_metadata, ruleset_fd);1458 ASSERT_EQ(0, close(ruleset_fd));1459 1460 /* Readdir access is still denied for dir_s1d2. */1461 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));1462 /* Readdir access is still allowed for dir_s1d3. */1463 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));1464 /* File access is still allowed for file1_s1d3. */1465 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));1466}1467 1468TEST_F_FORK(layout0, max_layers)1469{1470 int i, err;1471 const struct rule rules[] = {1472 {1473 .path = TMP_DIR,1474 .access = ACCESS_RO,1475 },1476 {},1477 };1478 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1479 1480 ASSERT_LE(0, ruleset_fd);1481 for (i = 0; i < 16; i++)1482 enforce_ruleset(_metadata, ruleset_fd);1483 1484 for (i = 0; i < 2; i++) {1485 err = landlock_restrict_self(ruleset_fd, 0);1486 ASSERT_EQ(-1, err);1487 ASSERT_EQ(E2BIG, errno);1488 }1489 ASSERT_EQ(0, close(ruleset_fd));1490}1491 1492TEST_F_FORK(layout1, empty_or_same_ruleset)1493{1494 struct landlock_ruleset_attr ruleset_attr = {};1495 int ruleset_fd;1496 1497 /* Tests empty handled_access_fs. */1498 ruleset_fd =1499 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);1500 ASSERT_LE(-1, ruleset_fd);1501 ASSERT_EQ(ENOMSG, errno);1502 1503 /* Enforces policy which deny read access to all files. */1504 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE;1505 ruleset_fd =1506 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);1507 ASSERT_LE(0, ruleset_fd);1508 enforce_ruleset(_metadata, ruleset_fd);1509 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1510 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1511 1512 /* Nests a policy which deny read access to all directories. */1513 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR;1514 ruleset_fd =1515 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);1516 ASSERT_LE(0, ruleset_fd);1517 enforce_ruleset(_metadata, ruleset_fd);1518 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));1519 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));1520 1521 /* Enforces a second time with the same ruleset. */1522 enforce_ruleset(_metadata, ruleset_fd);1523 ASSERT_EQ(0, close(ruleset_fd));1524}1525 1526TEST_F_FORK(layout1, rule_on_mountpoint)1527{1528 const struct rule rules[] = {1529 {1530 .path = dir_s1d1,1531 .access = ACCESS_RO,1532 },1533 {1534 /* dir_s3d2 is a mount point. */1535 .path = dir_s3d2,1536 .access = ACCESS_RO,1537 },1538 {},1539 };1540 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1541 1542 ASSERT_LE(0, ruleset_fd);1543 enforce_ruleset(_metadata, ruleset_fd);1544 ASSERT_EQ(0, close(ruleset_fd));1545 1546 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1547 1548 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));1549 1550 ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));1551 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));1552 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));1553}1554 1555TEST_F_FORK(layout1, rule_over_mountpoint)1556{1557 const struct rule rules[] = {1558 {1559 .path = dir_s1d1,1560 .access = ACCESS_RO,1561 },1562 {1563 /* dir_s3d2 is a mount point. */1564 .path = dir_s3d1,1565 .access = ACCESS_RO,1566 },1567 {},1568 };1569 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1570 1571 ASSERT_LE(0, ruleset_fd);1572 enforce_ruleset(_metadata, ruleset_fd);1573 ASSERT_EQ(0, close(ruleset_fd));1574 1575 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1576 1577 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));1578 1579 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));1580 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));1581 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));1582}1583 1584/*1585 * This test verifies that we can apply a landlock rule on the root directory1586 * (which might require special handling).1587 */1588TEST_F_FORK(layout1, rule_over_root_allow_then_deny)1589{1590 struct rule rules[] = {1591 {1592 .path = "/",1593 .access = ACCESS_RO,1594 },1595 {},1596 };1597 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1598 1599 ASSERT_LE(0, ruleset_fd);1600 enforce_ruleset(_metadata, ruleset_fd);1601 ASSERT_EQ(0, close(ruleset_fd));1602 1603 /* Checks allowed access. */1604 ASSERT_EQ(0, test_open("/", O_RDONLY));1605 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));1606 1607 rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;1608 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1609 ASSERT_LE(0, ruleset_fd);1610 enforce_ruleset(_metadata, ruleset_fd);1611 ASSERT_EQ(0, close(ruleset_fd));1612 1613 /* Checks denied access (on a directory). */1614 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));1615 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));1616}1617 1618TEST_F_FORK(layout1, rule_over_root_deny)1619{1620 const struct rule rules[] = {1621 {1622 .path = "/",1623 .access = LANDLOCK_ACCESS_FS_READ_FILE,1624 },1625 {},1626 };1627 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1628 1629 ASSERT_LE(0, ruleset_fd);1630 enforce_ruleset(_metadata, ruleset_fd);1631 ASSERT_EQ(0, close(ruleset_fd));1632 1633 /* Checks denied access (on a directory). */1634 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));1635 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));1636}1637 1638TEST_F_FORK(layout1, rule_inside_mount_ns)1639{1640 const struct rule rules[] = {1641 {1642 .path = "s3d3",1643 .access = ACCESS_RO,1644 },1645 {},1646 };1647 int ruleset_fd;1648 1649 set_cap(_metadata, CAP_SYS_ADMIN);1650 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3))1651 {1652 TH_LOG("Failed to pivot root: %s", strerror(errno));1653 };1654 ASSERT_EQ(0, chdir("/"));1655 clear_cap(_metadata, CAP_SYS_ADMIN);1656 1657 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1658 ASSERT_LE(0, ruleset_fd);1659 enforce_ruleset(_metadata, ruleset_fd);1660 ASSERT_EQ(0, close(ruleset_fd));1661 1662 ASSERT_EQ(0, test_open("s3d3", O_RDONLY));1663 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));1664}1665 1666TEST_F_FORK(layout1, mount_and_pivot)1667{1668 const struct rule rules[] = {1669 {1670 .path = dir_s3d2,1671 .access = ACCESS_RO,1672 },1673 {},1674 };1675 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1676 1677 ASSERT_LE(0, ruleset_fd);1678 enforce_ruleset(_metadata, ruleset_fd);1679 ASSERT_EQ(0, close(ruleset_fd));1680 1681 set_cap(_metadata, CAP_SYS_ADMIN);1682 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));1683 ASSERT_EQ(EPERM, errno);1684 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));1685 ASSERT_EQ(EPERM, errno);1686 clear_cap(_metadata, CAP_SYS_ADMIN);1687}1688 1689TEST_F_FORK(layout1, move_mount)1690{1691 const struct rule rules[] = {1692 {1693 .path = dir_s3d2,1694 .access = ACCESS_RO,1695 },1696 {},1697 };1698 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1699 1700 ASSERT_LE(0, ruleset_fd);1701 1702 set_cap(_metadata, CAP_SYS_ADMIN);1703 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,1704 dir_s1d2, 0))1705 {1706 TH_LOG("Failed to move mount: %s", strerror(errno));1707 }1708 1709 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,1710 dir_s3d2, 0));1711 clear_cap(_metadata, CAP_SYS_ADMIN);1712 1713 enforce_ruleset(_metadata, ruleset_fd);1714 ASSERT_EQ(0, close(ruleset_fd));1715 1716 set_cap(_metadata, CAP_SYS_ADMIN);1717 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,1718 dir_s1d2, 0));1719 ASSERT_EQ(EPERM, errno);1720 clear_cap(_metadata, CAP_SYS_ADMIN);1721}1722 1723TEST_F_FORK(layout1, topology_changes_with_net_only)1724{1725 const struct landlock_ruleset_attr ruleset_net = {1726 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |1727 LANDLOCK_ACCESS_NET_CONNECT_TCP,1728 };1729 int ruleset_fd;1730 1731 /* Add network restrictions. */1732 ruleset_fd =1733 landlock_create_ruleset(&ruleset_net, sizeof(ruleset_net), 0);1734 ASSERT_LE(0, ruleset_fd);1735 enforce_ruleset(_metadata, ruleset_fd);1736 ASSERT_EQ(0, close(ruleset_fd));1737 1738 /* Mount, remount, move_mount, umount, and pivot_root checks. */1739 set_cap(_metadata, CAP_SYS_ADMIN);1740 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s1d2));1741 ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC, NULL));1742 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,1743 dir_s2d2, 0));1744 ASSERT_EQ(0, umount(dir_s2d2));1745 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));1746 ASSERT_EQ(0, chdir("/"));1747 clear_cap(_metadata, CAP_SYS_ADMIN);1748}1749 1750TEST_F_FORK(layout1, topology_changes_with_net_and_fs)1751{1752 const struct landlock_ruleset_attr ruleset_net_fs = {1753 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |1754 LANDLOCK_ACCESS_NET_CONNECT_TCP,1755 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,1756 };1757 int ruleset_fd;1758 1759 /* Add network and filesystem restrictions. */1760 ruleset_fd = landlock_create_ruleset(&ruleset_net_fs,1761 sizeof(ruleset_net_fs), 0);1762 ASSERT_LE(0, ruleset_fd);1763 enforce_ruleset(_metadata, ruleset_fd);1764 ASSERT_EQ(0, close(ruleset_fd));1765 1766 /* Mount, remount, move_mount, umount, and pivot_root checks. */1767 set_cap(_metadata, CAP_SYS_ADMIN);1768 ASSERT_EQ(-1, mount_opt(&mnt_tmp, dir_s1d2));1769 ASSERT_EQ(EPERM, errno);1770 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC, NULL));1771 ASSERT_EQ(EPERM, errno);1772 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,1773 dir_s2d2, 0));1774 ASSERT_EQ(EPERM, errno);1775 ASSERT_EQ(-1, umount(dir_s3d2));1776 ASSERT_EQ(EPERM, errno);1777 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));1778 ASSERT_EQ(EPERM, errno);1779 clear_cap(_metadata, CAP_SYS_ADMIN);1780}1781 1782TEST_F_FORK(layout1, release_inodes)1783{1784 const struct rule rules[] = {1785 {1786 .path = dir_s1d1,1787 .access = ACCESS_RO,1788 },1789 {1790 .path = dir_s3d2,1791 .access = ACCESS_RO,1792 },1793 {1794 .path = dir_s3d3,1795 .access = ACCESS_RO,1796 },1797 {},1798 };1799 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);1800 1801 ASSERT_LE(0, ruleset_fd);1802 /* Unmount a file hierarchy while it is being used by a ruleset. */1803 set_cap(_metadata, CAP_SYS_ADMIN);1804 ASSERT_EQ(0, umount(dir_s3d2));1805 clear_cap(_metadata, CAP_SYS_ADMIN);1806 1807 enforce_ruleset(_metadata, ruleset_fd);1808 ASSERT_EQ(0, close(ruleset_fd));1809 1810 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));1811 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));1812 /* This dir_s3d3 would not be allowed and does not exist anyway. */1813 ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));1814}1815 1816enum relative_access {1817 REL_OPEN,1818 REL_CHDIR,1819 REL_CHROOT_ONLY,1820 REL_CHROOT_CHDIR,1821};1822 1823static void test_relative_path(struct __test_metadata *const _metadata,1824 const enum relative_access rel)1825{1826 /*1827 * Common layer to check that chroot doesn't ignore it (i.e. a chroot1828 * is not a disconnected root directory).1829 */1830 const struct rule layer1_base[] = {1831 {1832 .path = TMP_DIR,1833 .access = ACCESS_RO,1834 },1835 {},1836 };1837 const struct rule layer2_subs[] = {1838 {1839 .path = dir_s1d2,1840 .access = ACCESS_RO,1841 },1842 {1843 .path = dir_s2d2,1844 .access = ACCESS_RO,1845 },1846 {},1847 };1848 int dirfd, ruleset_fd;1849 1850 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);1851 ASSERT_LE(0, ruleset_fd);1852 enforce_ruleset(_metadata, ruleset_fd);1853 ASSERT_EQ(0, close(ruleset_fd));1854 1855 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);1856 1857 ASSERT_LE(0, ruleset_fd);1858 switch (rel) {1859 case REL_OPEN:1860 case REL_CHDIR:1861 break;1862 case REL_CHROOT_ONLY:1863 ASSERT_EQ(0, chdir(dir_s2d2));1864 break;1865 case REL_CHROOT_CHDIR:1866 ASSERT_EQ(0, chdir(dir_s1d2));1867 break;1868 default:1869 ASSERT_TRUE(false);1870 return;1871 }1872 1873 set_cap(_metadata, CAP_SYS_CHROOT);1874 enforce_ruleset(_metadata, ruleset_fd);1875 1876 switch (rel) {1877 case REL_OPEN:1878 dirfd = open(dir_s1d2, O_DIRECTORY);1879 ASSERT_LE(0, dirfd);1880 break;1881 case REL_CHDIR:1882 ASSERT_EQ(0, chdir(dir_s1d2));1883 dirfd = AT_FDCWD;1884 break;1885 case REL_CHROOT_ONLY:1886 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */1887 ASSERT_EQ(0, chroot("../../s1d1/s1d2"))1888 {1889 TH_LOG("Failed to chroot: %s", strerror(errno));1890 }1891 dirfd = AT_FDCWD;1892 break;1893 case REL_CHROOT_CHDIR:1894 /* Do chroot into dir_s1d2. */1895 ASSERT_EQ(0, chroot("."))1896 {1897 TH_LOG("Failed to chroot: %s", strerror(errno));1898 }1899 dirfd = AT_FDCWD;1900 break;1901 }1902 1903 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,1904 test_open_rel(dirfd, "..", O_RDONLY));1905 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));1906 1907 if (rel == REL_CHROOT_ONLY) {1908 /* The current directory is dir_s2d2. */1909 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));1910 } else {1911 /* The current directory is dir_s1d2. */1912 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));1913 }1914 1915 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {1916 /* Checks the root dir_s1d2. */1917 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));1918 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));1919 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));1920 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));1921 }1922 1923 if (rel != REL_CHROOT_CHDIR) {1924 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));1925 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));1926 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",1927 O_RDONLY));1928 1929 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));1930 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));1931 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",1932 O_RDONLY));1933 }1934 1935 if (rel == REL_OPEN)1936 ASSERT_EQ(0, close(dirfd));1937 ASSERT_EQ(0, close(ruleset_fd));1938}1939 1940TEST_F_FORK(layout1, relative_open)1941{1942 test_relative_path(_metadata, REL_OPEN);1943}1944 1945TEST_F_FORK(layout1, relative_chdir)1946{1947 test_relative_path(_metadata, REL_CHDIR);1948}1949 1950TEST_F_FORK(layout1, relative_chroot_only)1951{1952 test_relative_path(_metadata, REL_CHROOT_ONLY);1953}1954 1955TEST_F_FORK(layout1, relative_chroot_chdir)1956{1957 test_relative_path(_metadata, REL_CHROOT_CHDIR);1958}1959 1960static void copy_binary(struct __test_metadata *const _metadata,1961 const char *const dst_path)1962{1963 int dst_fd, src_fd;1964 struct stat statbuf;1965 1966 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);1967 ASSERT_LE(0, dst_fd)1968 {1969 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));1970 }1971 src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC);1972 ASSERT_LE(0, src_fd)1973 {1974 TH_LOG("Failed to open \"" BINARY_PATH "\": %s",1975 strerror(errno));1976 }1977 ASSERT_EQ(0, fstat(src_fd, &statbuf));1978 ASSERT_EQ(statbuf.st_size,1979 sendfile(dst_fd, src_fd, 0, statbuf.st_size));1980 ASSERT_EQ(0, close(src_fd));1981 ASSERT_EQ(0, close(dst_fd));1982}1983 1984static void test_execute(struct __test_metadata *const _metadata, const int err,1985 const char *const path)1986{1987 int status;1988 char *const argv[] = { (char *)path, NULL };1989 const pid_t child = fork();1990 1991 ASSERT_LE(0, child);1992 if (child == 0) {1993 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))1994 {1995 TH_LOG("Failed to execute \"%s\": %s", path,1996 strerror(errno));1997 };1998 ASSERT_EQ(err, errno);1999 _exit(__test_passed(_metadata) ? 2 : 1);2000 return;2001 }2002 ASSERT_EQ(child, waitpid(child, &status, 0));2003 ASSERT_EQ(1, WIFEXITED(status));2004 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))2005 {2006 TH_LOG("Unexpected return code for \"%s\": %s", path,2007 strerror(errno));2008 };2009}2010 2011TEST_F_FORK(layout1, execute)2012{2013 const struct rule rules[] = {2014 {2015 .path = dir_s1d2,2016 .access = LANDLOCK_ACCESS_FS_EXECUTE,2017 },2018 {},2019 };2020 const int ruleset_fd =2021 create_ruleset(_metadata, rules[0].access, rules);2022 2023 ASSERT_LE(0, ruleset_fd);2024 copy_binary(_metadata, file1_s1d1);2025 copy_binary(_metadata, file1_s1d2);2026 copy_binary(_metadata, file1_s1d3);2027 2028 enforce_ruleset(_metadata, ruleset_fd);2029 ASSERT_EQ(0, close(ruleset_fd));2030 2031 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));2032 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));2033 test_execute(_metadata, EACCES, file1_s1d1);2034 2035 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));2036 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));2037 test_execute(_metadata, 0, file1_s1d2);2038 2039 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));2040 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));2041 test_execute(_metadata, 0, file1_s1d3);2042}2043 2044TEST_F_FORK(layout1, link)2045{2046 const struct rule layer1[] = {2047 {2048 .path = dir_s1d2,2049 .access = LANDLOCK_ACCESS_FS_MAKE_REG,2050 },2051 {},2052 };2053 const struct rule layer2[] = {2054 {2055 .path = dir_s1d3,2056 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2057 },2058 {},2059 };2060 int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);2061 2062 ASSERT_LE(0, ruleset_fd);2063 2064 ASSERT_EQ(0, unlink(file1_s1d1));2065 ASSERT_EQ(0, unlink(file1_s1d2));2066 ASSERT_EQ(0, unlink(file1_s1d3));2067 2068 enforce_ruleset(_metadata, ruleset_fd);2069 ASSERT_EQ(0, close(ruleset_fd));2070 2071 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));2072 ASSERT_EQ(EACCES, errno);2073 2074 /* Denies linking because of reparenting. */2075 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));2076 ASSERT_EQ(EXDEV, errno);2077 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));2078 ASSERT_EQ(EXDEV, errno);2079 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));2080 ASSERT_EQ(EXDEV, errno);2081 2082 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));2083 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));2084 2085 /* Prepares for next unlinks. */2086 ASSERT_EQ(0, unlink(file2_s1d2));2087 ASSERT_EQ(0, unlink(file2_s1d3));2088 2089 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);2090 ASSERT_LE(0, ruleset_fd);2091 enforce_ruleset(_metadata, ruleset_fd);2092 ASSERT_EQ(0, close(ruleset_fd));2093 2094 /* Checks that linkind doesn't require the ability to delete a file. */2095 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));2096 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));2097}2098 2099static int test_rename(const char *const oldpath, const char *const newpath)2100{2101 if (rename(oldpath, newpath))2102 return errno;2103 return 0;2104}2105 2106static int test_exchange(const char *const oldpath, const char *const newpath)2107{2108 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))2109 return errno;2110 return 0;2111}2112 2113TEST_F_FORK(layout1, rename_file)2114{2115 const struct rule rules[] = {2116 {2117 .path = dir_s1d3,2118 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2119 },2120 {2121 .path = dir_s2d2,2122 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2123 },2124 {},2125 };2126 const int ruleset_fd =2127 create_ruleset(_metadata, rules[0].access, rules);2128 2129 ASSERT_LE(0, ruleset_fd);2130 2131 ASSERT_EQ(0, unlink(file1_s1d2));2132 2133 enforce_ruleset(_metadata, ruleset_fd);2134 ASSERT_EQ(0, close(ruleset_fd));2135 2136 /*2137 * Tries to replace a file, from a directory that allows file removal,2138 * but to a different directory (which also allows file removal).2139 */2140 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));2141 ASSERT_EQ(EXDEV, errno);2142 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,2143 RENAME_EXCHANGE));2144 ASSERT_EQ(EXDEV, errno);2145 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,2146 RENAME_EXCHANGE));2147 ASSERT_EQ(EXDEV, errno);2148 2149 /*2150 * Tries to replace a file, from a directory that denies file removal,2151 * to a different directory (which allows file removal).2152 */2153 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));2154 ASSERT_EQ(EACCES, errno);2155 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,2156 RENAME_EXCHANGE));2157 ASSERT_EQ(EACCES, errno);2158 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,2159 RENAME_EXCHANGE));2160 ASSERT_EQ(EXDEV, errno);2161 2162 /* Exchanges files and directories that partially allow removal. */2163 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,2164 RENAME_EXCHANGE));2165 ASSERT_EQ(EACCES, errno);2166 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */2167 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));2168 ASSERT_EQ(EACCES, errno);2169 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,2170 RENAME_EXCHANGE));2171 ASSERT_EQ(EACCES, errno);2172 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */2173 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));2174 ASSERT_EQ(EACCES, errno);2175 2176 /* Renames files with different parents. */2177 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));2178 ASSERT_EQ(EXDEV, errno);2179 ASSERT_EQ(0, unlink(file1_s1d3));2180 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));2181 ASSERT_EQ(EACCES, errno);2182 2183 /* Exchanges and renames files with same parent. */2184 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,2185 RENAME_EXCHANGE));2186 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));2187 2188 /* Exchanges files and directories with same parent, twice. */2189 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,2190 RENAME_EXCHANGE));2191 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,2192 RENAME_EXCHANGE));2193}2194 2195TEST_F_FORK(layout1, rename_dir)2196{2197 const struct rule rules[] = {2198 {2199 .path = dir_s1d2,2200 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,2201 },2202 {2203 .path = dir_s2d1,2204 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,2205 },2206 {},2207 };2208 const int ruleset_fd =2209 create_ruleset(_metadata, rules[0].access, rules);2210 2211 ASSERT_LE(0, ruleset_fd);2212 2213 /* Empties dir_s1d3 to allow renaming. */2214 ASSERT_EQ(0, unlink(file1_s1d3));2215 ASSERT_EQ(0, unlink(file2_s1d3));2216 2217 enforce_ruleset(_metadata, ruleset_fd);2218 ASSERT_EQ(0, close(ruleset_fd));2219 2220 /* Exchanges and renames directory to a different parent. */2221 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,2222 RENAME_EXCHANGE));2223 ASSERT_EQ(EXDEV, errno);2224 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));2225 ASSERT_EQ(EXDEV, errno);2226 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,2227 RENAME_EXCHANGE));2228 ASSERT_EQ(EXDEV, errno);2229 2230 /*2231 * Exchanges directory to the same parent, which doesn't allow2232 * directory removal.2233 */2234 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,2235 RENAME_EXCHANGE));2236 ASSERT_EQ(EACCES, errno);2237 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */2238 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));2239 ASSERT_EQ(EACCES, errno);2240 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,2241 RENAME_EXCHANGE));2242 ASSERT_EQ(EACCES, errno);2243 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */2244 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));2245 ASSERT_EQ(EACCES, errno);2246 2247 /*2248 * Exchanges and renames directory to the same parent, which allows2249 * directory removal.2250 */2251 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,2252 RENAME_EXCHANGE));2253 ASSERT_EQ(0, unlink(dir_s1d3));2254 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));2255 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));2256 ASSERT_EQ(0, rmdir(dir_s1d3));2257}2258 2259TEST_F_FORK(layout1, reparent_refer)2260{2261 const struct rule layer1[] = {2262 {2263 .path = dir_s1d2,2264 .access = LANDLOCK_ACCESS_FS_REFER,2265 },2266 {2267 .path = dir_s2d2,2268 .access = LANDLOCK_ACCESS_FS_REFER,2269 },2270 {},2271 };2272 int ruleset_fd =2273 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);2274 2275 ASSERT_LE(0, ruleset_fd);2276 enforce_ruleset(_metadata, ruleset_fd);2277 ASSERT_EQ(0, close(ruleset_fd));2278 2279 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));2280 ASSERT_EQ(EXDEV, errno);2281 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));2282 ASSERT_EQ(EXDEV, errno);2283 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));2284 ASSERT_EQ(EXDEV, errno);2285 2286 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));2287 ASSERT_EQ(EXDEV, errno);2288 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));2289 ASSERT_EQ(EXDEV, errno);2290 /*2291 * Moving should only be allowed when the source and the destination2292 * parent directory have REFER.2293 */2294 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));2295 ASSERT_EQ(ENOTEMPTY, errno);2296 ASSERT_EQ(0, unlink(file1_s2d3));2297 ASSERT_EQ(0, unlink(file2_s2d3));2298 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));2299}2300 2301/* Checks renames beneath dir_s1d1. */2302static void refer_denied_by_default(struct __test_metadata *const _metadata,2303 const struct rule layer1[],2304 const int layer1_err,2305 const struct rule layer2[])2306{2307 int ruleset_fd;2308 2309 ASSERT_EQ(0, unlink(file1_s1d2));2310 2311 ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);2312 ASSERT_LE(0, ruleset_fd);2313 enforce_ruleset(_metadata, ruleset_fd);2314 ASSERT_EQ(0, close(ruleset_fd));2315 2316 /*2317 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to2318 * layer1_err), then it allows some different-parent renames and links.2319 */2320 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));2321 if (layer1_err == 0)2322 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));2323 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));2324 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));2325 2326 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);2327 ASSERT_LE(0, ruleset_fd);2328 enforce_ruleset(_metadata, ruleset_fd);2329 ASSERT_EQ(0, close(ruleset_fd));2330 2331 /*2332 * Now, either the first or the second layer does not handle2333 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent2334 * renames and links are denied, thus making the layer handling2335 * LANDLOCK_ACCESS_FS_REFER null and void.2336 */2337 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));2338 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));2339 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));2340}2341 2342const struct rule layer_dir_s1d1_refer[] = {2343 {2344 .path = dir_s1d1,2345 .access = LANDLOCK_ACCESS_FS_REFER,2346 },2347 {},2348};2349 2350const struct rule layer_dir_s1d1_execute[] = {2351 {2352 /* Matches a parent directory. */2353 .path = dir_s1d1,2354 .access = LANDLOCK_ACCESS_FS_EXECUTE,2355 },2356 {},2357};2358 2359const struct rule layer_dir_s2d1_execute[] = {2360 {2361 /* Does not match a parent directory. */2362 .path = dir_s2d1,2363 .access = LANDLOCK_ACCESS_FS_EXECUTE,2364 },2365 {},2366};2367 2368/*2369 * Tests precedence over renames: denied by default for different parent2370 * directories, *with* a rule matching a parent directory, but not directly2371 * denying access (with MAKE_REG nor REMOVE).2372 */2373TEST_F_FORK(layout1, refer_denied_by_default1)2374{2375 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,2376 layer_dir_s1d1_execute);2377}2378 2379/*2380 * Same test but this time turning around the ABI version order: the first2381 * layer does not handle LANDLOCK_ACCESS_FS_REFER.2382 */2383TEST_F_FORK(layout1, refer_denied_by_default2)2384{2385 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,2386 layer_dir_s1d1_refer);2387}2388 2389/*2390 * Tests precedence over renames: denied by default for different parent2391 * directories, *without* a rule matching a parent directory, but not directly2392 * denying access (with MAKE_REG nor REMOVE).2393 */2394TEST_F_FORK(layout1, refer_denied_by_default3)2395{2396 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,2397 layer_dir_s2d1_execute);2398}2399 2400/*2401 * Same test but this time turning around the ABI version order: the first2402 * layer does not handle LANDLOCK_ACCESS_FS_REFER.2403 */2404TEST_F_FORK(layout1, refer_denied_by_default4)2405{2406 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,2407 layer_dir_s1d1_refer);2408}2409 2410/*2411 * Tests walking through a denied root mount.2412 */2413TEST_F_FORK(layout1, refer_mount_root_deny)2414{2415 const struct landlock_ruleset_attr ruleset_attr = {2416 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_DIR,2417 };2418 int root_fd, ruleset_fd;2419 2420 /* Creates a mount object from a non-mount point. */2421 set_cap(_metadata, CAP_SYS_ADMIN);2422 root_fd =2423 open_tree(AT_FDCWD, dir_s1d1,2424 AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);2425 clear_cap(_metadata, CAP_SYS_ADMIN);2426 ASSERT_LE(0, root_fd);2427 2428 ruleset_fd =2429 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);2430 ASSERT_LE(0, ruleset_fd);2431 2432 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));2433 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));2434 EXPECT_EQ(0, close(ruleset_fd));2435 2436 /* Link denied by Landlock: EACCES. */2437 EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0));2438 EXPECT_EQ(EACCES, errno);2439 2440 /* renameat2() always returns EBUSY. */2441 EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0));2442 EXPECT_EQ(EBUSY, errno);2443 2444 EXPECT_EQ(0, close(root_fd));2445}2446 2447TEST_F_FORK(layout1, reparent_link)2448{2449 const struct rule layer1[] = {2450 {2451 .path = dir_s1d2,2452 .access = LANDLOCK_ACCESS_FS_MAKE_REG,2453 },2454 {2455 .path = dir_s1d3,2456 .access = LANDLOCK_ACCESS_FS_REFER,2457 },2458 {2459 .path = dir_s2d2,2460 .access = LANDLOCK_ACCESS_FS_REFER,2461 },2462 {2463 .path = dir_s2d3,2464 .access = LANDLOCK_ACCESS_FS_MAKE_REG,2465 },2466 {},2467 };2468 const int ruleset_fd = create_ruleset(2469 _metadata,2470 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);2471 2472 ASSERT_LE(0, ruleset_fd);2473 enforce_ruleset(_metadata, ruleset_fd);2474 ASSERT_EQ(0, close(ruleset_fd));2475 2476 ASSERT_EQ(0, unlink(file1_s1d1));2477 ASSERT_EQ(0, unlink(file1_s1d2));2478 ASSERT_EQ(0, unlink(file1_s1d3));2479 2480 /* Denies linking because of missing MAKE_REG. */2481 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));2482 ASSERT_EQ(EACCES, errno);2483 /* Denies linking because of missing source and destination REFER. */2484 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));2485 ASSERT_EQ(EXDEV, errno);2486 /* Denies linking because of missing source REFER. */2487 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));2488 ASSERT_EQ(EXDEV, errno);2489 2490 /* Denies linking because of missing MAKE_REG. */2491 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));2492 ASSERT_EQ(EACCES, errno);2493 /* Denies linking because of missing destination REFER. */2494 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));2495 ASSERT_EQ(EXDEV, errno);2496 2497 /* Allows linking because of REFER and MAKE_REG. */2498 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));2499 ASSERT_EQ(0, unlink(file1_s2d2));2500 /* Reverse linking denied because of missing MAKE_REG. */2501 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));2502 ASSERT_EQ(EACCES, errno);2503 ASSERT_EQ(0, unlink(file1_s2d3));2504 /* Checks reverse linking. */2505 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));2506 ASSERT_EQ(0, unlink(file1_s1d3));2507 2508 /*2509 * This is OK for a file link, but it should not be allowed for a2510 * directory rename (because of the superset of access rights.2511 */2512 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));2513 ASSERT_EQ(0, unlink(file1_s1d3));2514 2515 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));2516 ASSERT_EQ(EXDEV, errno);2517 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));2518 ASSERT_EQ(EXDEV, errno);2519 2520 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));2521 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));2522}2523 2524TEST_F_FORK(layout1, reparent_rename)2525{2526 /* Same rules as for reparent_link. */2527 const struct rule layer1[] = {2528 {2529 .path = dir_s1d2,2530 .access = LANDLOCK_ACCESS_FS_MAKE_REG,2531 },2532 {2533 .path = dir_s1d3,2534 .access = LANDLOCK_ACCESS_FS_REFER,2535 },2536 {2537 .path = dir_s2d2,2538 .access = LANDLOCK_ACCESS_FS_REFER,2539 },2540 {2541 .path = dir_s2d3,2542 .access = LANDLOCK_ACCESS_FS_MAKE_REG,2543 },2544 {},2545 };2546 const int ruleset_fd = create_ruleset(2547 _metadata,2548 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);2549 2550 ASSERT_LE(0, ruleset_fd);2551 enforce_ruleset(_metadata, ruleset_fd);2552 ASSERT_EQ(0, close(ruleset_fd));2553 2554 ASSERT_EQ(0, unlink(file1_s1d2));2555 ASSERT_EQ(0, unlink(file1_s1d3));2556 2557 /* Denies renaming because of missing MAKE_REG. */2558 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,2559 RENAME_EXCHANGE));2560 ASSERT_EQ(EACCES, errno);2561 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,2562 RENAME_EXCHANGE));2563 ASSERT_EQ(EACCES, errno);2564 ASSERT_EQ(0, unlink(file1_s1d1));2565 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));2566 ASSERT_EQ(EACCES, errno);2567 /* Even denies same file exchange. */2568 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,2569 RENAME_EXCHANGE));2570 ASSERT_EQ(EACCES, errno);2571 2572 /* Denies renaming because of missing source and destination REFER. */2573 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));2574 ASSERT_EQ(EXDEV, errno);2575 /*2576 * Denies renaming because of missing MAKE_REG, source and destination2577 * REFER.2578 */2579 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,2580 RENAME_EXCHANGE));2581 ASSERT_EQ(EACCES, errno);2582 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,2583 RENAME_EXCHANGE));2584 ASSERT_EQ(EACCES, errno);2585 2586 /* Denies renaming because of missing source REFER. */2587 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));2588 ASSERT_EQ(EXDEV, errno);2589 /* Denies renaming because of missing MAKE_REG. */2590 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,2591 RENAME_EXCHANGE));2592 ASSERT_EQ(EACCES, errno);2593 2594 /* Denies renaming because of missing MAKE_REG. */2595 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));2596 ASSERT_EQ(EACCES, errno);2597 /* Denies renaming because of missing destination REFER*/2598 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));2599 ASSERT_EQ(EXDEV, errno);2600 2601 /* Denies exchange because of one missing MAKE_REG. */2602 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,2603 RENAME_EXCHANGE));2604 ASSERT_EQ(EACCES, errno);2605 /* Allows renaming because of REFER and MAKE_REG. */2606 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));2607 2608 /* Reverse renaming denied because of missing MAKE_REG. */2609 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));2610 ASSERT_EQ(EACCES, errno);2611 ASSERT_EQ(0, unlink(file1_s2d3));2612 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2613 2614 /* Tests reverse renaming. */2615 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));2616 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,2617 RENAME_EXCHANGE));2618 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2619 2620 /*2621 * This is OK for a file rename, but it should not be allowed for a2622 * directory rename (because of the superset of access rights).2623 */2624 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));2625 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2626 2627 /*2628 * Tests superset restrictions applied to directories. Not only the2629 * dir_s2d3's parent (dir_s2d2) should be taken into account but also2630 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right2631 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided2632 * directly by the moved dir_s2d3.2633 */2634 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));2635 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));2636 /*2637 * The first rename is allowed but not the exchange because dir_s1d3's2638 * parent (dir_s1d2) doesn't have REFER.2639 */2640 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,2641 RENAME_EXCHANGE));2642 ASSERT_EQ(EXDEV, errno);2643 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,2644 RENAME_EXCHANGE));2645 ASSERT_EQ(EXDEV, errno);2646 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));2647 ASSERT_EQ(EXDEV, errno);2648 2649 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));2650 ASSERT_EQ(EXDEV, errno);2651 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));2652 ASSERT_EQ(EXDEV, errno);2653 2654 /* Renaming in the same directory is always allowed. */2655 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));2656 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));2657 2658 ASSERT_EQ(0, unlink(file1_s1d2));2659 /* Denies because of missing source MAKE_REG and destination REFER. */2660 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));2661 ASSERT_EQ(EXDEV, errno);2662 2663 ASSERT_EQ(0, unlink(file1_s1d3));2664 /* Denies because of missing source MAKE_REG and REFER. */2665 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));2666 ASSERT_EQ(EXDEV, errno);2667}2668 2669static void2670reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)2671{2672 const struct rule layer1[] = {2673 {2674 .path = dir_s1d2,2675 .access = LANDLOCK_ACCESS_FS_REFER,2676 },2677 {2678 /* Interesting for the layer2 tests. */2679 .path = dir_s1d3,2680 .access = LANDLOCK_ACCESS_FS_MAKE_REG,2681 },2682 {2683 .path = dir_s2d2,2684 .access = LANDLOCK_ACCESS_FS_REFER,2685 },2686 {2687 .path = dir_s2d3,2688 .access = LANDLOCK_ACCESS_FS_MAKE_REG,2689 },2690 {},2691 };2692 const int ruleset_fd = create_ruleset(2693 _metadata,2694 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);2695 2696 ASSERT_LE(0, ruleset_fd);2697 enforce_ruleset(_metadata, ruleset_fd);2698 ASSERT_EQ(0, close(ruleset_fd));2699}2700 2701static void2702reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)2703{2704 const struct rule layer2[] = {2705 {2706 .path = dir_s2d3,2707 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,2708 },2709 {},2710 };2711 /*2712 * Same checks as before but with a second layer and a new MAKE_DIR2713 * rule (and no explicit handling of REFER).2714 */2715 const int ruleset_fd =2716 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);2717 2718 ASSERT_LE(0, ruleset_fd);2719 enforce_ruleset(_metadata, ruleset_fd);2720 ASSERT_EQ(0, close(ruleset_fd));2721}2722 2723TEST_F_FORK(layout1, reparent_exdev_layers_rename1)2724{2725 ASSERT_EQ(0, unlink(file1_s2d2));2726 ASSERT_EQ(0, unlink(file1_s2d3));2727 2728 reparent_exdev_layers_enforce1(_metadata);2729 2730 /*2731 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock2732 * because it doesn't inherit new access rights.2733 */2734 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));2735 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));2736 2737 /*2738 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it2739 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is2740 * already allowed for dir_s1d3.2741 */2742 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));2743 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));2744 2745 /*2746 * However, moving the file1_s1d3 file below dir_s2d3 is allowed2747 * because it cannot inherit MAKE_REG right (which is dedicated to2748 * directories).2749 */2750 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));2751 2752 reparent_exdev_layers_enforce2(_metadata);2753 2754 /*2755 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because2756 * MAKE_DIR is not tied to dir_s2d2.2757 */2758 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));2759 ASSERT_EQ(EACCES, errno);2760 2761 /*2762 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it2763 * would grants MAKE_REG and MAKE_DIR rights to it.2764 */2765 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));2766 ASSERT_EQ(EXDEV, errno);2767 2768 /*2769 * Moving the file2_s1d3 file below dir_s2d3 is denied because the2770 * second layer does not handle REFER, which is always denied by2771 * default.2772 */2773 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));2774 ASSERT_EQ(EXDEV, errno);2775}2776 2777TEST_F_FORK(layout1, reparent_exdev_layers_rename2)2778{2779 reparent_exdev_layers_enforce1(_metadata);2780 2781 /* Checks EACCES predominance over EXDEV. */2782 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));2783 ASSERT_EQ(EACCES, errno);2784 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));2785 ASSERT_EQ(EACCES, errno);2786 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));2787 ASSERT_EQ(EXDEV, errno);2788 /* Modify layout! */2789 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));2790 2791 /* Without REFER source. */2792 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));2793 ASSERT_EQ(EXDEV, errno);2794 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));2795 ASSERT_EQ(EXDEV, errno);2796 2797 reparent_exdev_layers_enforce2(_metadata);2798 2799 /* Checks EACCES predominance over EXDEV. */2800 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));2801 ASSERT_EQ(EACCES, errno);2802 /* Checks with actual file2_s1d2. */2803 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));2804 ASSERT_EQ(EACCES, errno);2805 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));2806 ASSERT_EQ(EXDEV, errno);2807 /*2808 * Modifying the layout is now denied because the second layer does not2809 * handle REFER, which is always denied by default.2810 */2811 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));2812 ASSERT_EQ(EXDEV, errno);2813 2814 /* Without REFER source, EACCES wins over EXDEV. */2815 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));2816 ASSERT_EQ(EACCES, errno);2817 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));2818 ASSERT_EQ(EACCES, errno);2819}2820 2821TEST_F_FORK(layout1, reparent_exdev_layers_exchange1)2822{2823 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =2824 file2_s2d3;2825 2826 ASSERT_EQ(0, unlink(file1_s1d2));2827 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));2828 ASSERT_EQ(0, unlink(file2_s2d3));2829 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));2830 2831 reparent_exdev_layers_enforce1(_metadata);2832 2833 /* Error predominance with file exchange: returns EXDEV and EACCES. */2834 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,2835 RENAME_EXCHANGE));2836 ASSERT_EQ(EACCES, errno);2837 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,2838 RENAME_EXCHANGE));2839 ASSERT_EQ(EACCES, errno);2840 2841 /*2842 * Checks with directories which creation could be allowed, but denied2843 * because of access rights that would be inherited.2844 */2845 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,2846 dir_file2_s2d3, RENAME_EXCHANGE));2847 ASSERT_EQ(EXDEV, errno);2848 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,2849 dir_file1_s1d2, RENAME_EXCHANGE));2850 ASSERT_EQ(EXDEV, errno);2851 2852 /* Checks with same access rights. */2853 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,2854 RENAME_EXCHANGE));2855 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,2856 RENAME_EXCHANGE));2857 2858 /* Checks with different (child-only) access rights. */2859 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,2860 RENAME_EXCHANGE));2861 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,2862 RENAME_EXCHANGE));2863 2864 /*2865 * Checks that exchange between file and directory are consistent.2866 *2867 * Moving a file (file1_s2d2) to a directory which only grants more2868 * directory-related access rights is allowed, and at the same time2869 * moving a directory (dir_file2_s2d3) to another directory which2870 * grants less access rights is allowed too.2871 *2872 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.2873 */2874 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2875 RENAME_EXCHANGE));2876 /*2877 * However, moving back the directory is denied because it would get2878 * more access rights than the current state and because file creation2879 * is forbidden (in dir_s2d2).2880 */2881 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2882 RENAME_EXCHANGE));2883 ASSERT_EQ(EACCES, errno);2884 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2885 RENAME_EXCHANGE));2886 ASSERT_EQ(EACCES, errno);2887 2888 reparent_exdev_layers_enforce2(_metadata);2889 2890 /* Error predominance with file exchange: returns EXDEV and EACCES. */2891 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,2892 RENAME_EXCHANGE));2893 ASSERT_EQ(EACCES, errno);2894 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,2895 RENAME_EXCHANGE));2896 ASSERT_EQ(EACCES, errno);2897 2898 /* Checks with directories which creation is now denied. */2899 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,2900 dir_file2_s2d3, RENAME_EXCHANGE));2901 ASSERT_EQ(EACCES, errno);2902 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,2903 dir_file1_s1d2, RENAME_EXCHANGE));2904 ASSERT_EQ(EACCES, errno);2905 2906 /* Checks with different (child-only) access rights. */2907 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,2908 RENAME_EXCHANGE));2909 /* Denied because of MAKE_DIR. */2910 ASSERT_EQ(EACCES, errno);2911 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,2912 RENAME_EXCHANGE));2913 ASSERT_EQ(EACCES, errno);2914 2915 /* Checks with different (child-only) access rights. */2916 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,2917 RENAME_EXCHANGE));2918 /* Denied because of MAKE_DIR. */2919 ASSERT_EQ(EACCES, errno);2920 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,2921 RENAME_EXCHANGE));2922 ASSERT_EQ(EACCES, errno);2923 2924 /* See layout1.reparent_exdev_layers_exchange2 for complement. */2925}2926 2927TEST_F_FORK(layout1, reparent_exdev_layers_exchange2)2928{2929 const char *const dir_file2_s2d3 = file2_s2d3;2930 2931 ASSERT_EQ(0, unlink(file2_s2d3));2932 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));2933 2934 reparent_exdev_layers_enforce1(_metadata);2935 reparent_exdev_layers_enforce2(_metadata);2936 2937 /* Checks that exchange between file and directory are consistent. */2938 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2939 RENAME_EXCHANGE));2940 ASSERT_EQ(EACCES, errno);2941 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2942 RENAME_EXCHANGE));2943 ASSERT_EQ(EACCES, errno);2944}2945 2946TEST_F_FORK(layout1, reparent_exdev_layers_exchange3)2947{2948 const char *const dir_file2_s2d3 = file2_s2d3;2949 2950 ASSERT_EQ(0, unlink(file2_s2d3));2951 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));2952 2953 reparent_exdev_layers_enforce1(_metadata);2954 2955 /*2956 * Checks that exchange between file and directory are consistent,2957 * including with inverted arguments (see2958 * layout1.reparent_exdev_layers_exchange1).2959 */2960 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2961 RENAME_EXCHANGE));2962 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,2963 RENAME_EXCHANGE));2964 ASSERT_EQ(EACCES, errno);2965 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,2966 RENAME_EXCHANGE));2967 ASSERT_EQ(EACCES, errno);2968}2969 2970TEST_F_FORK(layout1, reparent_remove)2971{2972 const struct rule layer1[] = {2973 {2974 .path = dir_s1d1,2975 .access = LANDLOCK_ACCESS_FS_REFER |2976 LANDLOCK_ACCESS_FS_REMOVE_DIR,2977 },2978 {2979 .path = dir_s1d2,2980 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,2981 },2982 {2983 .path = dir_s2d1,2984 .access = LANDLOCK_ACCESS_FS_REFER |2985 LANDLOCK_ACCESS_FS_REMOVE_FILE,2986 },2987 {},2988 };2989 const int ruleset_fd = create_ruleset(2990 _metadata,2991 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |2992 LANDLOCK_ACCESS_FS_REMOVE_FILE,2993 layer1);2994 2995 ASSERT_LE(0, ruleset_fd);2996 enforce_ruleset(_metadata, ruleset_fd);2997 ASSERT_EQ(0, close(ruleset_fd));2998 2999 /* Access denied because of wrong/swapped remove file/dir. */3000 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));3001 ASSERT_EQ(EACCES, errno);3002 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));3003 ASSERT_EQ(EACCES, errno);3004 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,3005 RENAME_EXCHANGE));3006 ASSERT_EQ(EACCES, errno);3007 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,3008 RENAME_EXCHANGE));3009 ASSERT_EQ(EACCES, errno);3010 3011 /* Access allowed thanks to the matching rights. */3012 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));3013 ASSERT_EQ(EISDIR, errno);3014 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));3015 ASSERT_EQ(ENOTDIR, errno);3016 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));3017 ASSERT_EQ(ENOTDIR, errno);3018 ASSERT_EQ(0, unlink(file1_s2d1));3019 ASSERT_EQ(0, unlink(file1_s1d3));3020 ASSERT_EQ(0, unlink(file2_s1d3));3021 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));3022 3023 /* Effectively removes a file and a directory by exchanging them. */3024 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));3025 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,3026 RENAME_EXCHANGE));3027 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,3028 RENAME_EXCHANGE));3029 ASSERT_EQ(EACCES, errno);3030}3031 3032TEST_F_FORK(layout1, reparent_dom_superset)3033{3034 const struct rule layer1[] = {3035 {3036 .path = dir_s1d2,3037 .access = LANDLOCK_ACCESS_FS_REFER,3038 },3039 {3040 .path = file1_s1d2,3041 .access = LANDLOCK_ACCESS_FS_EXECUTE,3042 },3043 {3044 .path = dir_s1d3,3045 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK |3046 LANDLOCK_ACCESS_FS_EXECUTE,3047 },3048 {3049 .path = dir_s2d2,3050 .access = LANDLOCK_ACCESS_FS_REFER |3051 LANDLOCK_ACCESS_FS_EXECUTE |3052 LANDLOCK_ACCESS_FS_MAKE_SOCK,3053 },3054 {3055 .path = dir_s2d3,3056 .access = LANDLOCK_ACCESS_FS_READ_FILE |3057 LANDLOCK_ACCESS_FS_MAKE_FIFO,3058 },3059 {},3060 };3061 int ruleset_fd = create_ruleset(_metadata,3062 LANDLOCK_ACCESS_FS_REFER |3063 LANDLOCK_ACCESS_FS_EXECUTE |3064 LANDLOCK_ACCESS_FS_MAKE_SOCK |3065 LANDLOCK_ACCESS_FS_READ_FILE |3066 LANDLOCK_ACCESS_FS_MAKE_FIFO,3067 layer1);3068 3069 ASSERT_LE(0, ruleset_fd);3070 enforce_ruleset(_metadata, ruleset_fd);3071 ASSERT_EQ(0, close(ruleset_fd));3072 3073 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));3074 ASSERT_EQ(EXDEV, errno);3075 /*3076 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE3077 * access right.3078 */3079 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));3080 ASSERT_EQ(EXDEV, errno);3081 /*3082 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a3083 * superset of access rights compared to dir_s1d2, because file1_s1d23084 * already has these access rights anyway.3085 */3086 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));3087 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));3088 3089 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));3090 ASSERT_EQ(EXDEV, errno);3091 /*3092 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access3093 * right.3094 */3095 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));3096 ASSERT_EQ(EXDEV, errno);3097 /*3098 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset3099 * of access rights compared to dir_s1d2, because dir_s1d3 already has3100 * these access rights anyway.3101 */3102 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));3103 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));3104 3105 /*3106 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back3107 * will be denied because the new inherited access rights from dir_s1d23108 * will be less than the destination (original) dir_s2d3. This is a3109 * sinkhole scenario where we cannot move back files or directories.3110 */3111 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));3112 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));3113 ASSERT_EQ(EXDEV, errno);3114 ASSERT_EQ(0, unlink(file2_s1d2));3115 ASSERT_EQ(0, unlink(file2_s2d3));3116 /*3117 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and3118 * MAKE_SOCK which were inherited from dir_s1d3.3119 */3120 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));3121 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));3122 ASSERT_EQ(EXDEV, errno);3123}3124 3125TEST_F_FORK(layout1, remove_dir)3126{3127 const struct rule rules[] = {3128 {3129 .path = dir_s1d2,3130 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,3131 },3132 {},3133 };3134 const int ruleset_fd =3135 create_ruleset(_metadata, rules[0].access, rules);3136 3137 ASSERT_LE(0, ruleset_fd);3138 3139 ASSERT_EQ(0, unlink(file1_s1d1));3140 ASSERT_EQ(0, unlink(file1_s1d2));3141 ASSERT_EQ(0, unlink(file1_s1d3));3142 ASSERT_EQ(0, unlink(file2_s1d3));3143 3144 enforce_ruleset(_metadata, ruleset_fd);3145 ASSERT_EQ(0, close(ruleset_fd));3146 3147 ASSERT_EQ(0, rmdir(dir_s1d3));3148 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));3149 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));3150 3151 /* dir_s1d2 itself cannot be removed. */3152 ASSERT_EQ(-1, rmdir(dir_s1d2));3153 ASSERT_EQ(EACCES, errno);3154 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));3155 ASSERT_EQ(EACCES, errno);3156 ASSERT_EQ(-1, rmdir(dir_s1d1));3157 ASSERT_EQ(EACCES, errno);3158 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));3159 ASSERT_EQ(EACCES, errno);3160}3161 3162TEST_F_FORK(layout1, remove_file)3163{3164 const struct rule rules[] = {3165 {3166 .path = dir_s1d2,3167 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,3168 },3169 {},3170 };3171 const int ruleset_fd =3172 create_ruleset(_metadata, rules[0].access, rules);3173 3174 ASSERT_LE(0, ruleset_fd);3175 enforce_ruleset(_metadata, ruleset_fd);3176 ASSERT_EQ(0, close(ruleset_fd));3177 3178 ASSERT_EQ(-1, unlink(file1_s1d1));3179 ASSERT_EQ(EACCES, errno);3180 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));3181 ASSERT_EQ(EACCES, errno);3182 ASSERT_EQ(0, unlink(file1_s1d2));3183 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));3184}3185 3186static void test_make_file(struct __test_metadata *const _metadata,3187 const __u64 access, const mode_t mode,3188 const dev_t dev)3189{3190 const struct rule rules[] = {3191 {3192 .path = dir_s1d2,3193 .access = access,3194 },3195 {},3196 };3197 const int ruleset_fd = create_ruleset(_metadata, access, rules);3198 3199 ASSERT_LE(0, ruleset_fd);3200 3201 ASSERT_EQ(0, unlink(file1_s1d1));3202 ASSERT_EQ(0, unlink(file2_s1d1));3203 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))3204 {3205 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,3206 strerror(errno));3207 };3208 3209 ASSERT_EQ(0, unlink(file1_s1d2));3210 ASSERT_EQ(0, unlink(file2_s1d2));3211 3212 ASSERT_EQ(0, unlink(file1_s1d3));3213 ASSERT_EQ(0, unlink(file2_s1d3));3214 3215 enforce_ruleset(_metadata, ruleset_fd);3216 ASSERT_EQ(0, close(ruleset_fd));3217 3218 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));3219 ASSERT_EQ(EACCES, errno);3220 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));3221 ASSERT_EQ(EACCES, errno);3222 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));3223 ASSERT_EQ(EACCES, errno);3224 3225 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))3226 {3227 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,3228 strerror(errno));3229 };3230 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));3231 ASSERT_EQ(0, unlink(file2_s1d2));3232 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));3233 3234 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));3235 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));3236 ASSERT_EQ(0, unlink(file2_s1d3));3237 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));3238}3239 3240TEST_F_FORK(layout1, make_char)3241{3242 /* Creates a /dev/null device. */3243 set_cap(_metadata, CAP_MKNOD);3244 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,3245 makedev(1, 3));3246}3247 3248TEST_F_FORK(layout1, make_block)3249{3250 /* Creates a /dev/loop0 device. */3251 set_cap(_metadata, CAP_MKNOD);3252 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,3253 makedev(7, 0));3254}3255 3256TEST_F_FORK(layout1, make_reg_1)3257{3258 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);3259}3260 3261TEST_F_FORK(layout1, make_reg_2)3262{3263 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);3264}3265 3266TEST_F_FORK(layout1, make_sock)3267{3268 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);3269}3270 3271TEST_F_FORK(layout1, make_fifo)3272{3273 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);3274}3275 3276TEST_F_FORK(layout1, make_sym)3277{3278 const struct rule rules[] = {3279 {3280 .path = dir_s1d2,3281 .access = LANDLOCK_ACCESS_FS_MAKE_SYM,3282 },3283 {},3284 };3285 const int ruleset_fd =3286 create_ruleset(_metadata, rules[0].access, rules);3287 3288 ASSERT_LE(0, ruleset_fd);3289 3290 ASSERT_EQ(0, unlink(file1_s1d1));3291 ASSERT_EQ(0, unlink(file2_s1d1));3292 ASSERT_EQ(0, symlink("none", file2_s1d1));3293 3294 ASSERT_EQ(0, unlink(file1_s1d2));3295 ASSERT_EQ(0, unlink(file2_s1d2));3296 3297 ASSERT_EQ(0, unlink(file1_s1d3));3298 ASSERT_EQ(0, unlink(file2_s1d3));3299 3300 enforce_ruleset(_metadata, ruleset_fd);3301 ASSERT_EQ(0, close(ruleset_fd));3302 3303 ASSERT_EQ(-1, symlink("none", file1_s1d1));3304 ASSERT_EQ(EACCES, errno);3305 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));3306 ASSERT_EQ(EACCES, errno);3307 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));3308 ASSERT_EQ(EACCES, errno);3309 3310 ASSERT_EQ(0, symlink("none", file1_s1d2));3311 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));3312 ASSERT_EQ(0, unlink(file2_s1d2));3313 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));3314 3315 ASSERT_EQ(0, symlink("none", file1_s1d3));3316 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));3317 ASSERT_EQ(0, unlink(file2_s1d3));3318 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));3319}3320 3321TEST_F_FORK(layout1, make_dir)3322{3323 const struct rule rules[] = {3324 {3325 .path = dir_s1d2,3326 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,3327 },3328 {},3329 };3330 const int ruleset_fd =3331 create_ruleset(_metadata, rules[0].access, rules);3332 3333 ASSERT_LE(0, ruleset_fd);3334 3335 ASSERT_EQ(0, unlink(file1_s1d1));3336 ASSERT_EQ(0, unlink(file1_s1d2));3337 ASSERT_EQ(0, unlink(file1_s1d3));3338 3339 enforce_ruleset(_metadata, ruleset_fd);3340 ASSERT_EQ(0, close(ruleset_fd));3341 3342 /* Uses file_* as directory names. */3343 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));3344 ASSERT_EQ(EACCES, errno);3345 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));3346 ASSERT_EQ(0, mkdir(file1_s1d3, 0700));3347}3348 3349static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,3350 const int open_flags)3351{3352 static const char path_template[] = "/proc/self/fd/%d";3353 char procfd_path[sizeof(path_template) + 10];3354 const int procfd_path_size =3355 snprintf(procfd_path, sizeof(procfd_path), path_template, fd);3356 3357 ASSERT_LT(procfd_path_size, sizeof(procfd_path));3358 return open(procfd_path, open_flags);3359}3360 3361TEST_F_FORK(layout1, proc_unlinked_file)3362{3363 const struct rule rules[] = {3364 {3365 .path = file1_s1d2,3366 .access = LANDLOCK_ACCESS_FS_READ_FILE,3367 },3368 {},3369 };3370 int reg_fd, proc_fd;3371 const int ruleset_fd = create_ruleset(3372 _metadata,3373 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,3374 rules);3375 3376 ASSERT_LE(0, ruleset_fd);3377 enforce_ruleset(_metadata, ruleset_fd);3378 ASSERT_EQ(0, close(ruleset_fd));3379 3380 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));3381 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));3382 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);3383 ASSERT_LE(0, reg_fd);3384 ASSERT_EQ(0, unlink(file1_s1d2));3385 3386 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);3387 ASSERT_LE(0, proc_fd);3388 ASSERT_EQ(0, close(proc_fd));3389 3390 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);3391 ASSERT_EQ(-1, proc_fd)3392 {3393 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,3394 strerror(errno));3395 }3396 ASSERT_EQ(EACCES, errno);3397 3398 ASSERT_EQ(0, close(reg_fd));3399}3400 3401TEST_F_FORK(layout1, proc_pipe)3402{3403 int proc_fd;3404 int pipe_fds[2];3405 char buf = '\0';3406 const struct rule rules[] = {3407 {3408 .path = dir_s1d2,3409 .access = LANDLOCK_ACCESS_FS_READ_FILE |3410 LANDLOCK_ACCESS_FS_WRITE_FILE,3411 },3412 {},3413 };3414 /* Limits read and write access to files tied to the filesystem. */3415 const int ruleset_fd =3416 create_ruleset(_metadata, rules[0].access, rules);3417 3418 ASSERT_LE(0, ruleset_fd);3419 enforce_ruleset(_metadata, ruleset_fd);3420 ASSERT_EQ(0, close(ruleset_fd));3421 3422 /* Checks enforcement for normal files. */3423 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));3424 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));3425 3426 /* Checks access to pipes through FD. */3427 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));3428 ASSERT_EQ(1, write(pipe_fds[1], ".", 1))3429 {3430 TH_LOG("Failed to write in pipe: %s", strerror(errno));3431 }3432 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));3433 ASSERT_EQ('.', buf);3434 3435 /* Checks write access to pipe through /proc/self/fd . */3436 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);3437 ASSERT_LE(0, proc_fd);3438 ASSERT_EQ(1, write(proc_fd, ".", 1))3439 {3440 TH_LOG("Failed to write through /proc/self/fd/%d: %s",3441 pipe_fds[1], strerror(errno));3442 }3443 ASSERT_EQ(0, close(proc_fd));3444 3445 /* Checks read access to pipe through /proc/self/fd . */3446 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);3447 ASSERT_LE(0, proc_fd);3448 buf = '\0';3449 ASSERT_EQ(1, read(proc_fd, &buf, 1))3450 {3451 TH_LOG("Failed to read through /proc/self/fd/%d: %s",3452 pipe_fds[1], strerror(errno));3453 }3454 ASSERT_EQ(0, close(proc_fd));3455 3456 ASSERT_EQ(0, close(pipe_fds[0]));3457 ASSERT_EQ(0, close(pipe_fds[1]));3458}3459 3460/* Invokes truncate(2) and returns its errno or 0. */3461static int test_truncate(const char *const path)3462{3463 if (truncate(path, 10) < 0)3464 return errno;3465 return 0;3466}3467 3468/*3469 * Invokes creat(2) and returns its errno or 0.3470 * Closes the opened file descriptor on success.3471 */3472static int test_creat(const char *const path)3473{3474 int fd = creat(path, 0600);3475 3476 if (fd < 0)3477 return errno;3478 3479 /*3480 * Mixing error codes from close(2) and creat(2) should not lead to any3481 * (access type) confusion for this test.3482 */3483 if (close(fd) < 0)3484 return errno;3485 return 0;3486}3487 3488/*3489 * Exercises file truncation when it's not restricted,3490 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.3491 */3492TEST_F_FORK(layout1, truncate_unhandled)3493{3494 const char *const file_r = file1_s1d1;3495 const char *const file_w = file2_s1d1;3496 const char *const file_none = file1_s1d2;3497 const struct rule rules[] = {3498 {3499 .path = file_r,3500 .access = LANDLOCK_ACCESS_FS_READ_FILE,3501 },3502 {3503 .path = file_w,3504 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,3505 },3506 /* Implicitly: No rights for file_none. */3507 {},3508 };3509 3510 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |3511 LANDLOCK_ACCESS_FS_WRITE_FILE;3512 int ruleset_fd;3513 3514 /* Enables Landlock. */3515 ruleset_fd = create_ruleset(_metadata, handled, rules);3516 3517 ASSERT_LE(0, ruleset_fd);3518 enforce_ruleset(_metadata, ruleset_fd);3519 ASSERT_EQ(0, close(ruleset_fd));3520 3521 /*3522 * Checks read right: truncate and open with O_TRUNC work, unless the3523 * file is attempted to be opened for writing.3524 */3525 EXPECT_EQ(0, test_truncate(file_r));3526 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));3527 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));3528 EXPECT_EQ(EACCES, test_creat(file_r));3529 3530 /*3531 * Checks write right: truncate and open with O_TRUNC work, unless the3532 * file is attempted to be opened for reading.3533 */3534 EXPECT_EQ(0, test_truncate(file_w));3535 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));3536 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));3537 EXPECT_EQ(0, test_creat(file_w));3538 3539 /*3540 * Checks "no rights" case: truncate works but all open attempts fail,3541 * including creat.3542 */3543 EXPECT_EQ(0, test_truncate(file_none));3544 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));3545 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));3546 EXPECT_EQ(EACCES, test_creat(file_none));3547}3548 3549TEST_F_FORK(layout1, truncate)3550{3551 const char *const file_rwt = file1_s1d1;3552 const char *const file_rw = file2_s1d1;3553 const char *const file_rt = file1_s1d2;3554 const char *const file_t = file2_s1d2;3555 const char *const file_none = file1_s1d3;3556 const char *const dir_t = dir_s2d1;3557 const char *const file_in_dir_t = file1_s2d1;3558 const char *const dir_w = dir_s3d1;3559 const char *const file_in_dir_w = file1_s3d1;3560 const struct rule rules[] = {3561 {3562 .path = file_rwt,3563 .access = LANDLOCK_ACCESS_FS_READ_FILE |3564 LANDLOCK_ACCESS_FS_WRITE_FILE |3565 LANDLOCK_ACCESS_FS_TRUNCATE,3566 },3567 {3568 .path = file_rw,3569 .access = LANDLOCK_ACCESS_FS_READ_FILE |3570 LANDLOCK_ACCESS_FS_WRITE_FILE,3571 },3572 {3573 .path = file_rt,3574 .access = LANDLOCK_ACCESS_FS_READ_FILE |3575 LANDLOCK_ACCESS_FS_TRUNCATE,3576 },3577 {3578 .path = file_t,3579 .access = LANDLOCK_ACCESS_FS_TRUNCATE,3580 },3581 /* Implicitly: No access rights for file_none. */3582 {3583 .path = dir_t,3584 .access = LANDLOCK_ACCESS_FS_TRUNCATE,3585 },3586 {3587 .path = dir_w,3588 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,3589 },3590 {},3591 };3592 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |3593 LANDLOCK_ACCESS_FS_WRITE_FILE |3594 LANDLOCK_ACCESS_FS_TRUNCATE;3595 int ruleset_fd;3596 3597 /* Enables Landlock. */3598 ruleset_fd = create_ruleset(_metadata, handled, rules);3599 3600 ASSERT_LE(0, ruleset_fd);3601 enforce_ruleset(_metadata, ruleset_fd);3602 ASSERT_EQ(0, close(ruleset_fd));3603 3604 /* Checks read, write and truncate rights: truncation works. */3605 EXPECT_EQ(0, test_truncate(file_rwt));3606 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));3607 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));3608 3609 /* Checks read and write rights: no truncate variant works. */3610 EXPECT_EQ(EACCES, test_truncate(file_rw));3611 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));3612 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));3613 3614 /*3615 * Checks read and truncate rights: truncation works.3616 *3617 * Note: Files can get truncated using open() even with O_RDONLY.3618 */3619 EXPECT_EQ(0, test_truncate(file_rt));3620 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));3621 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));3622 3623 /* Checks truncate right: truncate works, but can't open file. */3624 EXPECT_EQ(0, test_truncate(file_t));3625 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));3626 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));3627 3628 /* Checks "no rights" case: No form of truncation works. */3629 EXPECT_EQ(EACCES, test_truncate(file_none));3630 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));3631 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));3632 3633 /*3634 * Checks truncate right on directory: truncate works on contained3635 * files.3636 */3637 EXPECT_EQ(0, test_truncate(file_in_dir_t));3638 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));3639 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));3640 3641 /*3642 * Checks creat in dir_w: This requires the truncate right when3643 * overwriting an existing file, but does not require it when the file3644 * is new.3645 */3646 EXPECT_EQ(EACCES, test_creat(file_in_dir_w));3647 3648 ASSERT_EQ(0, unlink(file_in_dir_w));3649 EXPECT_EQ(0, test_creat(file_in_dir_w));3650}3651 3652/* Invokes ftruncate(2) and returns its errno or 0. */3653static int test_ftruncate(int fd)3654{3655 if (ftruncate(fd, 10) < 0)3656 return errno;3657 return 0;3658}3659 3660TEST_F_FORK(layout1, ftruncate)3661{3662 /*3663 * This test opens a new file descriptor at different stages of3664 * Landlock restriction:3665 *3666 * without restriction: ftruncate works3667 * something else but truncate restricted: ftruncate works3668 * truncate restricted and permitted: ftruncate works3669 * truncate restricted and not permitted: ftruncate fails3670 *3671 * Whether this works or not is expected to depend on the time when the3672 * FD was opened, not to depend on the time when ftruncate() was3673 * called.3674 */3675 const char *const path = file1_s1d1;3676 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |3677 LANDLOCK_ACCESS_FS_WRITE_FILE;3678 const struct rule layer1[] = {3679 {3680 .path = path,3681 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,3682 },3683 {},3684 };3685 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;3686 const struct rule layer2[] = {3687 {3688 .path = path,3689 .access = LANDLOCK_ACCESS_FS_TRUNCATE,3690 },3691 {},3692 };3693 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |3694 LANDLOCK_ACCESS_FS_WRITE_FILE;3695 const struct rule layer3[] = {3696 {3697 .path = path,3698 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,3699 },3700 {},3701 };3702 int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd;3703 3704 fd_layer0 = open(path, O_WRONLY);3705 EXPECT_EQ(0, test_ftruncate(fd_layer0));3706 3707 ruleset_fd = create_ruleset(_metadata, handled1, layer1);3708 ASSERT_LE(0, ruleset_fd);3709 enforce_ruleset(_metadata, ruleset_fd);3710 ASSERT_EQ(0, close(ruleset_fd));3711 3712 fd_layer1 = open(path, O_WRONLY);3713 EXPECT_EQ(0, test_ftruncate(fd_layer0));3714 EXPECT_EQ(0, test_ftruncate(fd_layer1));3715 3716 ruleset_fd = create_ruleset(_metadata, handled2, layer2);3717 ASSERT_LE(0, ruleset_fd);3718 enforce_ruleset(_metadata, ruleset_fd);3719 ASSERT_EQ(0, close(ruleset_fd));3720 3721 fd_layer2 = open(path, O_WRONLY);3722 EXPECT_EQ(0, test_ftruncate(fd_layer0));3723 EXPECT_EQ(0, test_ftruncate(fd_layer1));3724 EXPECT_EQ(0, test_ftruncate(fd_layer2));3725 3726 ruleset_fd = create_ruleset(_metadata, handled3, layer3);3727 ASSERT_LE(0, ruleset_fd);3728 enforce_ruleset(_metadata, ruleset_fd);3729 ASSERT_EQ(0, close(ruleset_fd));3730 3731 fd_layer3 = open(path, O_WRONLY);3732 EXPECT_EQ(0, test_ftruncate(fd_layer0));3733 EXPECT_EQ(0, test_ftruncate(fd_layer1));3734 EXPECT_EQ(0, test_ftruncate(fd_layer2));3735 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));3736 3737 ASSERT_EQ(0, close(fd_layer0));3738 ASSERT_EQ(0, close(fd_layer1));3739 ASSERT_EQ(0, close(fd_layer2));3740 ASSERT_EQ(0, close(fd_layer3));3741}3742 3743/* clang-format off */3744FIXTURE(ftruncate) {};3745/* clang-format on */3746 3747FIXTURE_SETUP(ftruncate)3748{3749 prepare_layout(_metadata);3750 create_file(_metadata, file1_s1d1);3751}3752 3753FIXTURE_TEARDOWN_PARENT(ftruncate)3754{3755 EXPECT_EQ(0, remove_path(file1_s1d1));3756 cleanup_layout(_metadata);3757}3758 3759FIXTURE_VARIANT(ftruncate)3760{3761 const __u64 handled;3762 const __u64 allowed;3763 const int expected_open_result;3764 const int expected_ftruncate_result;3765};3766 3767/* clang-format off */3768FIXTURE_VARIANT_ADD(ftruncate, w_w) {3769 /* clang-format on */3770 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE,3771 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,3772 .expected_open_result = 0,3773 .expected_ftruncate_result = 0,3774};3775 3776/* clang-format off */3777FIXTURE_VARIANT_ADD(ftruncate, t_t) {3778 /* clang-format on */3779 .handled = LANDLOCK_ACCESS_FS_TRUNCATE,3780 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,3781 .expected_open_result = 0,3782 .expected_ftruncate_result = 0,3783};3784 3785/* clang-format off */3786FIXTURE_VARIANT_ADD(ftruncate, wt_w) {3787 /* clang-format on */3788 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3789 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,3790 .expected_open_result = 0,3791 .expected_ftruncate_result = EACCES,3792};3793 3794/* clang-format off */3795FIXTURE_VARIANT_ADD(ftruncate, wt_wt) {3796 /* clang-format on */3797 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3798 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3799 .expected_open_result = 0,3800 .expected_ftruncate_result = 0,3801};3802 3803/* clang-format off */3804FIXTURE_VARIANT_ADD(ftruncate, wt_t) {3805 /* clang-format on */3806 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,3807 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,3808 .expected_open_result = EACCES,3809};3810 3811TEST_F_FORK(ftruncate, open_and_ftruncate)3812{3813 const char *const path = file1_s1d1;3814 const struct rule rules[] = {3815 {3816 .path = path,3817 .access = variant->allowed,3818 },3819 {},3820 };3821 int fd, ruleset_fd;3822 3823 /* Enables Landlock. */3824 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);3825 ASSERT_LE(0, ruleset_fd);3826 enforce_ruleset(_metadata, ruleset_fd);3827 ASSERT_EQ(0, close(ruleset_fd));3828 3829 fd = open(path, O_WRONLY);3830 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));3831 if (fd >= 0) {3832 EXPECT_EQ(variant->expected_ftruncate_result,3833 test_ftruncate(fd));3834 ASSERT_EQ(0, close(fd));3835 }3836}3837 3838TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)3839{3840 int child, fd, status;3841 int socket_fds[2];3842 3843 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,3844 socket_fds));3845 3846 child = fork();3847 ASSERT_LE(0, child);3848 if (child == 0) {3849 /*3850 * Enables Landlock in the child process, open a file descriptor3851 * where truncation is forbidden and send it to the3852 * non-landlocked parent process.3853 */3854 const char *const path = file1_s1d1;3855 const struct rule rules[] = {3856 {3857 .path = path,3858 .access = variant->allowed,3859 },3860 {},3861 };3862 int fd, ruleset_fd;3863 3864 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);3865 ASSERT_LE(0, ruleset_fd);3866 enforce_ruleset(_metadata, ruleset_fd);3867 ASSERT_EQ(0, close(ruleset_fd));3868 3869 fd = open(path, O_WRONLY);3870 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));3871 3872 if (fd >= 0) {3873 ASSERT_EQ(0, send_fd(socket_fds[0], fd));3874 ASSERT_EQ(0, close(fd));3875 }3876 3877 ASSERT_EQ(0, close(socket_fds[0]));3878 3879 _exit(_metadata->exit_code);3880 return;3881 }3882 3883 if (variant->expected_open_result == 0) {3884 fd = recv_fd(socket_fds[1]);3885 ASSERT_LE(0, fd);3886 3887 EXPECT_EQ(variant->expected_ftruncate_result,3888 test_ftruncate(fd));3889 ASSERT_EQ(0, close(fd));3890 }3891 3892 ASSERT_EQ(child, waitpid(child, &status, 0));3893 ASSERT_EQ(1, WIFEXITED(status));3894 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));3895 3896 ASSERT_EQ(0, close(socket_fds[0]));3897 ASSERT_EQ(0, close(socket_fds[1]));3898}3899 3900/* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */3901static int test_fs_ioc_getflags_ioctl(int fd)3902{3903 uint32_t flags;3904 3905 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)3906 return errno;3907 return 0;3908}3909 3910TEST(memfd_ftruncate_and_ioctl)3911{3912 const struct landlock_ruleset_attr attr = {3913 .handled_access_fs = ACCESS_ALL,3914 };3915 int ruleset_fd, fd, i;3916 3917 /*3918 * We exercise the same test both with and without Landlock enabled, to3919 * ensure that it behaves the same in both cases.3920 */3921 for (i = 0; i < 2; i++) {3922 /* Creates a new memfd. */3923 fd = memfd_create("name", MFD_CLOEXEC);3924 ASSERT_LE(0, fd);3925 3926 /*3927 * Checks that operations associated with the opened file3928 * (ftruncate, ioctl) are permitted on file descriptors that are3929 * created in ways other than open(2).3930 */3931 EXPECT_EQ(0, test_ftruncate(fd));3932 EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd));3933 3934 ASSERT_EQ(0, close(fd));3935 3936 /* Enables Landlock. */3937 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);3938 ASSERT_LE(0, ruleset_fd);3939 enforce_ruleset(_metadata, ruleset_fd);3940 ASSERT_EQ(0, close(ruleset_fd));3941 }3942}3943 3944static int test_fionread_ioctl(int fd)3945{3946 size_t sz = 0;3947 3948 if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES)3949 return errno;3950 return 0;3951}3952 3953TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)3954{3955 const struct landlock_ruleset_attr attr = {3956 .handled_access_fs = ACCESS_ALL,3957 };3958 int ruleset_fd, fd;3959 3960 /*3961 * Checks that for files opened with O_PATH, both ioctl(2) and3962 * ftruncate(2) yield EBADF, as it is documented in open(2) for the3963 * O_PATH flag.3964 */3965 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);3966 ASSERT_LE(0, fd);3967 3968 EXPECT_EQ(EBADF, test_ftruncate(fd));3969 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));3970 3971 ASSERT_EQ(0, close(fd));3972 3973 /* Enables Landlock. */3974 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);3975 ASSERT_LE(0, ruleset_fd);3976 enforce_ruleset(_metadata, ruleset_fd);3977 ASSERT_EQ(0, close(ruleset_fd));3978 3979 /*3980 * Checks that after enabling Landlock,3981 * - the file can still be opened with O_PATH3982 * - both ioctl and truncate still yield EBADF (not EACCES).3983 */3984 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);3985 ASSERT_LE(0, fd);3986 3987 EXPECT_EQ(EBADF, test_ftruncate(fd));3988 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));3989 3990 ASSERT_EQ(0, close(fd));3991}3992 3993/*3994 * ioctl_error - generically call the given ioctl with a pointer to a3995 * sufficiently large zeroed-out memory region.3996 *3997 * Returns the IOCTLs error, or 0.3998 */3999static int ioctl_error(struct __test_metadata *const _metadata, int fd,4000 unsigned int cmd)4001{4002 char buf[128]; /* sufficiently large */4003 int res, stdinbak_fd;4004 4005 /*4006 * Depending on the IOCTL command, parts of the zeroed-out buffer might4007 * be interpreted as file descriptor numbers. We do not want to4008 * accidentally operate on file descriptor 0 (stdin), so we temporarily4009 * move stdin to a different FD and close FD 0 for the IOCTL call.4010 */4011 stdinbak_fd = dup(0);4012 ASSERT_LT(0, stdinbak_fd);4013 ASSERT_EQ(0, close(0));4014 4015 /* Invokes the IOCTL with a zeroed-out buffer. */4016 bzero(&buf, sizeof(buf));4017 res = ioctl(fd, cmd, &buf);4018 4019 /* Restores the old FD 0 and closes the backup FD. */4020 ASSERT_EQ(0, dup2(stdinbak_fd, 0));4021 ASSERT_EQ(0, close(stdinbak_fd));4022 4023 if (res < 0)4024 return errno;4025 4026 return 0;4027}4028 4029/* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */4030struct space_resv {4031 __s16 l_type;4032 __s16 l_whence;4033 __s64 l_start;4034 __s64 l_len; /* len == 0 means until end of file */4035 __s32 l_sysid;4036 __u32 l_pid;4037 __s32 l_pad[4]; /* reserved area */4038};4039 4040#define FS_IOC_RESVSP _IOW('X', 40, struct space_resv)4041#define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv)4042#define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv)4043#define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv)4044#define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv)4045 4046/*4047 * Tests a series of blanket-permitted and denied IOCTLs.4048 */4049TEST_F_FORK(layout1, blanket_permitted_ioctls)4050{4051 const struct landlock_ruleset_attr attr = {4052 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,4053 };4054 int ruleset_fd, fd;4055 4056 /* Enables Landlock. */4057 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);4058 ASSERT_LE(0, ruleset_fd);4059 enforce_ruleset(_metadata, ruleset_fd);4060 ASSERT_EQ(0, close(ruleset_fd));4061 4062 fd = open("/dev/null", O_RDWR | O_CLOEXEC);4063 ASSERT_LE(0, fd);4064 4065 /*4066 * Checks permitted commands.4067 * These ones may return errors, but should not be blocked by Landlock.4068 */4069 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX));4070 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX));4071 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO));4072 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC));4073 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE));4074 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE));4075 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW));4076 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP));4077 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ));4078 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE));4079 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE));4080 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE));4081 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID));4082 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH));4083 4084 /*4085 * Checks blocked commands.4086 * A call to a blocked IOCTL command always returns EACCES.4087 */4088 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));4089 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS));4090 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS));4091 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR));4092 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR));4093 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP));4094 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP));4095 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64));4096 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP));4097 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64));4098 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE));4099 4100 /* Default case is also blocked. */4101 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee));4102 4103 ASSERT_EQ(0, close(fd));4104}4105 4106/*4107 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right,4108 * because they are not character or block devices.4109 */4110TEST_F_FORK(layout1, named_pipe_ioctl)4111{4112 pid_t child_pid;4113 int fd, ruleset_fd;4114 const char *const path = file1_s1d1;4115 const struct landlock_ruleset_attr attr = {4116 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,4117 };4118 4119 ASSERT_EQ(0, unlink(path));4120 ASSERT_EQ(0, mkfifo(path, 0600));4121 4122 /* Enables Landlock. */4123 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);4124 ASSERT_LE(0, ruleset_fd);4125 enforce_ruleset(_metadata, ruleset_fd);4126 ASSERT_EQ(0, close(ruleset_fd));4127 4128 /* The child process opens the pipe for writing. */4129 child_pid = fork();4130 ASSERT_NE(-1, child_pid);4131 if (child_pid == 0) {4132 fd = open(path, O_WRONLY);4133 close(fd);4134 exit(0);4135 }4136 4137 fd = open(path, O_RDONLY);4138 ASSERT_LE(0, fd);4139 4140 /* FIONREAD is implemented by pipefifo_fops. */4141 EXPECT_EQ(0, test_fionread_ioctl(fd));4142 4143 ASSERT_EQ(0, close(fd));4144 ASSERT_EQ(0, unlink(path));4145 4146 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0));4147}4148 4149/* For named UNIX domain sockets, no IOCTL restrictions apply. */4150TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)4151{4152 const char *const path = file1_s1d1;4153 int srv_fd, cli_fd, ruleset_fd;4154 socklen_t size;4155 struct sockaddr_un srv_un, cli_un;4156 const struct landlock_ruleset_attr attr = {4157 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,4158 };4159 4160 /* Sets up a server */4161 srv_un.sun_family = AF_UNIX;4162 strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));4163 4164 ASSERT_EQ(0, unlink(path));4165 srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);4166 ASSERT_LE(0, srv_fd);4167 4168 size = offsetof(struct sockaddr_un, sun_path) + strlen(srv_un.sun_path);4169 ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, size));4170 ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */));4171 4172 /* Enables Landlock. */4173 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);4174 ASSERT_LE(0, ruleset_fd);4175 enforce_ruleset(_metadata, ruleset_fd);4176 ASSERT_EQ(0, close(ruleset_fd));4177 4178 /* Sets up a client connection to it */4179 cli_un.sun_family = AF_UNIX;4180 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);4181 ASSERT_LE(0, cli_fd);4182 4183 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);4184 ASSERT_EQ(0, bind(cli_fd, (struct sockaddr *)&cli_un, size));4185 4186 bzero(&cli_un, sizeof(cli_un));4187 cli_un.sun_family = AF_UNIX;4188 strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path));4189 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);4190 4191 ASSERT_EQ(0, connect(cli_fd, (struct sockaddr *)&cli_un, size));4192 4193 /* FIONREAD and other IOCTLs should not be forbidden. */4194 EXPECT_EQ(0, test_fionread_ioctl(cli_fd));4195 4196 ASSERT_EQ(0, close(cli_fd));4197}4198 4199/* clang-format off */4200FIXTURE(ioctl) {};4201 4202FIXTURE_SETUP(ioctl) {};4203 4204FIXTURE_TEARDOWN(ioctl) {};4205/* clang-format on */4206 4207FIXTURE_VARIANT(ioctl)4208{4209 const __u64 handled;4210 const __u64 allowed;4211 const mode_t open_mode;4212 /*4213 * FIONREAD is used as a characteristic device-specific IOCTL command.4214 * It is implemented in fs/ioctl.c for regular files,4215 * but we do not blanket-permit it for devices.4216 */4217 const int expected_fionread_result;4218};4219 4220/* clang-format off */4221FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) {4222 /* clang-format on */4223 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,4224 .allowed = 0,4225 .open_mode = O_RDWR,4226 .expected_fionread_result = EACCES,4227};4228 4229/* clang-format off */4230FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) {4231 /* clang-format on */4232 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,4233 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV,4234 .open_mode = O_RDWR,4235 .expected_fionread_result = 0,4236};4237 4238/* clang-format off */4239FIXTURE_VARIANT_ADD(ioctl, unhandled) {4240 /* clang-format on */4241 .handled = LANDLOCK_ACCESS_FS_EXECUTE,4242 .allowed = LANDLOCK_ACCESS_FS_EXECUTE,4243 .open_mode = O_RDWR,4244 .expected_fionread_result = 0,4245};4246 4247TEST_F_FORK(ioctl, handle_dir_access_file)4248{4249 const int flag = 0;4250 const struct rule rules[] = {4251 {4252 .path = "/dev",4253 .access = variant->allowed,4254 },4255 {},4256 };4257 int file_fd, ruleset_fd;4258 4259 /* Enables Landlock. */4260 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);4261 ASSERT_LE(0, ruleset_fd);4262 enforce_ruleset(_metadata, ruleset_fd);4263 ASSERT_EQ(0, close(ruleset_fd));4264 4265 file_fd = open("/dev/zero", variant->open_mode);4266 ASSERT_LE(0, file_fd);4267 4268 /* Checks that IOCTL commands return the expected errors. */4269 EXPECT_EQ(variant->expected_fionread_result,4270 test_fionread_ioctl(file_fd));4271 4272 /* Checks that unrestrictable commands are unrestricted. */4273 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));4274 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));4275 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));4276 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));4277 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));4278 4279 ASSERT_EQ(0, close(file_fd));4280}4281 4282TEST_F_FORK(ioctl, handle_dir_access_dir)4283{4284 const int flag = 0;4285 const struct rule rules[] = {4286 {4287 .path = "/dev",4288 .access = variant->allowed,4289 },4290 {},4291 };4292 int dir_fd, ruleset_fd;4293 4294 /* Enables Landlock. */4295 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);4296 ASSERT_LE(0, ruleset_fd);4297 enforce_ruleset(_metadata, ruleset_fd);4298 ASSERT_EQ(0, close(ruleset_fd));4299 4300 /*4301 * Ignore variant->open_mode for this test, as we intend to open a4302 * directory. If the directory can not be opened, the variant is4303 * infeasible to test with an opened directory.4304 */4305 dir_fd = open("/dev", O_RDONLY);4306 if (dir_fd < 0)4307 return;4308 4309 /*4310 * Checks that IOCTL commands return the expected errors.4311 * We do not use the expected values from the fixture here.4312 *4313 * When using IOCTL on a directory, no Landlock restrictions apply.4314 */4315 EXPECT_EQ(0, test_fionread_ioctl(dir_fd));4316 4317 /* Checks that unrestrictable commands are unrestricted. */4318 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX));4319 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX));4320 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag));4321 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag));4322 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag));4323 4324 ASSERT_EQ(0, close(dir_fd));4325}4326 4327TEST_F_FORK(ioctl, handle_file_access_file)4328{4329 const int flag = 0;4330 const struct rule rules[] = {4331 {4332 .path = "/dev/zero",4333 .access = variant->allowed,4334 },4335 {},4336 };4337 int file_fd, ruleset_fd;4338 4339 /* Enables Landlock. */4340 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);4341 ASSERT_LE(0, ruleset_fd);4342 enforce_ruleset(_metadata, ruleset_fd);4343 ASSERT_EQ(0, close(ruleset_fd));4344 4345 file_fd = open("/dev/zero", variant->open_mode);4346 ASSERT_LE(0, file_fd)4347 {4348 TH_LOG("Failed to open /dev/zero: %s", strerror(errno));4349 }4350 4351 /* Checks that IOCTL commands return the expected errors. */4352 EXPECT_EQ(variant->expected_fionread_result,4353 test_fionread_ioctl(file_fd));4354 4355 /* Checks that unrestrictable commands are unrestricted. */4356 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));4357 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));4358 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));4359 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));4360 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));4361 4362 ASSERT_EQ(0, close(file_fd));4363}4364 4365/* clang-format off */4366FIXTURE(layout1_bind) {};4367/* clang-format on */4368 4369FIXTURE_SETUP(layout1_bind)4370{4371 prepare_layout(_metadata);4372 4373 create_layout1(_metadata);4374 4375 set_cap(_metadata, CAP_SYS_ADMIN);4376 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));4377 clear_cap(_metadata, CAP_SYS_ADMIN);4378}4379 4380FIXTURE_TEARDOWN_PARENT(layout1_bind)4381{4382 /* umount(dir_s2d2)) is handled by namespace lifetime. */4383 4384 remove_layout1(_metadata);4385 4386 cleanup_layout(_metadata);4387}4388 4389static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";4390static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";4391 4392/*4393 * layout1_bind hierarchy:4394 *4395 * tmp4396 * ├── s1d14397 * │ ├── f14398 * │ ├── f24399 * │ └── s1d24400 * │ ├── f14401 * │ ├── f24402 * │ └── s1d34403 * │ ├── f14404 * │ └── f24405 * ├── s2d14406 * │ ├── f14407 * │ └── s2d24408 * │ ├── f14409 * │ ├── f24410 * │ └── s1d34411 * │ ├── f14412 * │ └── f24413 * └── s3d14414 * └── s3d24415 * └── s3d34416 */4417 4418TEST_F_FORK(layout1_bind, no_restriction)4419{4420 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));4421 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));4422 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));4423 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));4424 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));4425 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));4426 4427 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));4428 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));4429 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));4430 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));4431 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));4432 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));4433 4434 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));4435 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));4436 4437 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));4438}4439 4440TEST_F_FORK(layout1_bind, same_content_same_file)4441{4442 /*4443 * Sets access right on parent directories of both source and4444 * destination mount points.4445 */4446 const struct rule layer1_parent[] = {4447 {4448 .path = dir_s1d1,4449 .access = ACCESS_RO,4450 },4451 {4452 .path = dir_s2d1,4453 .access = ACCESS_RW,4454 },4455 {},4456 };4457 /*4458 * Sets access rights on the same bind-mounted directories. The result4459 * should be ACCESS_RW for both directories, but not both hierarchies4460 * because of the first layer.4461 */4462 const struct rule layer2_mount_point[] = {4463 {4464 .path = dir_s1d2,4465 .access = LANDLOCK_ACCESS_FS_READ_FILE,4466 },4467 {4468 .path = dir_s2d2,4469 .access = ACCESS_RW,4470 },4471 {},4472 };4473 /* Only allow read-access to the s1d3 hierarchies. */4474 const struct rule layer3_source[] = {4475 {4476 .path = dir_s1d3,4477 .access = LANDLOCK_ACCESS_FS_READ_FILE,4478 },4479 {},4480 };4481 /* Removes all access rights. */4482 const struct rule layer4_destination[] = {4483 {4484 .path = bind_file1_s1d3,4485 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,4486 },4487 {},4488 };4489 int ruleset_fd;4490 4491 /* Sets rules for the parent directories. */4492 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);4493 ASSERT_LE(0, ruleset_fd);4494 enforce_ruleset(_metadata, ruleset_fd);4495 ASSERT_EQ(0, close(ruleset_fd));4496 4497 /* Checks source hierarchy. */4498 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));4499 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));4500 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));4501 4502 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));4503 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));4504 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));4505 4506 /* Checks destination hierarchy. */4507 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));4508 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));4509 4510 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));4511 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));4512 4513 /* Sets rules for the mount points. */4514 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);4515 ASSERT_LE(0, ruleset_fd);4516 enforce_ruleset(_metadata, ruleset_fd);4517 ASSERT_EQ(0, close(ruleset_fd));4518 4519 /* Checks source hierarchy. */4520 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));4521 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));4522 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));4523 4524 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));4525 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));4526 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));4527 4528 /* Checks destination hierarchy. */4529 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));4530 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));4531 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));4532 4533 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));4534 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));4535 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));4536 4537 /* Sets a (shared) rule only on the source. */4538 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);4539 ASSERT_LE(0, ruleset_fd);4540 enforce_ruleset(_metadata, ruleset_fd);4541 ASSERT_EQ(0, close(ruleset_fd));4542 4543 /* Checks source hierarchy. */4544 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));4545 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));4546 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));4547 4548 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));4549 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));4550 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));4551 4552 /* Checks destination hierarchy. */4553 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));4554 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));4555 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));4556 4557 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));4558 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));4559 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));4560 4561 /* Sets a (shared) rule only on the destination. */4562 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);4563 ASSERT_LE(0, ruleset_fd);4564 enforce_ruleset(_metadata, ruleset_fd);4565 ASSERT_EQ(0, close(ruleset_fd));4566 4567 /* Checks source hierarchy. */4568 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));4569 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));4570 4571 /* Checks destination hierarchy. */4572 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));4573 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));4574}4575 4576TEST_F_FORK(layout1_bind, reparent_cross_mount)4577{4578 const struct rule layer1[] = {4579 {4580 /* dir_s2d1 is beneath the dir_s2d2 mount point. */4581 .path = dir_s2d1,4582 .access = LANDLOCK_ACCESS_FS_REFER,4583 },4584 {4585 .path = bind_dir_s1d3,4586 .access = LANDLOCK_ACCESS_FS_EXECUTE,4587 },4588 {},4589 };4590 int ruleset_fd = create_ruleset(4591 _metadata,4592 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1);4593 4594 ASSERT_LE(0, ruleset_fd);4595 enforce_ruleset(_metadata, ruleset_fd);4596 ASSERT_EQ(0, close(ruleset_fd));4597 4598 /* Checks basic denied move. */4599 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));4600 ASSERT_EQ(EXDEV, errno);4601 4602 /* Checks real cross-mount move (Landlock is not involved). */4603 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));4604 ASSERT_EQ(EXDEV, errno);4605 4606 /* Checks move that will give more accesses. */4607 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));4608 ASSERT_EQ(EXDEV, errno);4609 4610 /* Checks legitimate downgrade move. */4611 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));4612}4613 4614#define LOWER_BASE TMP_DIR "/lower"4615#define LOWER_DATA LOWER_BASE "/data"4616static const char lower_fl1[] = LOWER_DATA "/fl1";4617static const char lower_dl1[] = LOWER_DATA "/dl1";4618static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";4619static const char lower_fo1[] = LOWER_DATA "/fo1";4620static const char lower_do1[] = LOWER_DATA "/do1";4621static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";4622static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";4623 4624static const char (*lower_base_files[])[] = {4625 &lower_fl1,4626 &lower_fo1,4627 NULL,4628};4629static const char (*lower_base_directories[])[] = {4630 &lower_dl1,4631 &lower_do1,4632 NULL,4633};4634static const char (*lower_sub_files[])[] = {4635 &lower_dl1_fl2,4636 &lower_do1_fo2,4637 &lower_do1_fl3,4638 NULL,4639};4640 4641#define UPPER_BASE TMP_DIR "/upper"4642#define UPPER_DATA UPPER_BASE "/data"4643#define UPPER_WORK UPPER_BASE "/work"4644static const char upper_fu1[] = UPPER_DATA "/fu1";4645static const char upper_du1[] = UPPER_DATA "/du1";4646static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";4647static const char upper_fo1[] = UPPER_DATA "/fo1";4648static const char upper_do1[] = UPPER_DATA "/do1";4649static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";4650static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";4651 4652static const char (*upper_base_files[])[] = {4653 &upper_fu1,4654 &upper_fo1,4655 NULL,4656};4657static const char (*upper_base_directories[])[] = {4658 &upper_du1,4659 &upper_do1,4660 NULL,4661};4662static const char (*upper_sub_files[])[] = {4663 &upper_du1_fu2,4664 &upper_do1_fo2,4665 &upper_do1_fu3,4666 NULL,4667};4668 4669#define MERGE_BASE TMP_DIR "/merge"4670#define MERGE_DATA MERGE_BASE "/data"4671static const char merge_fl1[] = MERGE_DATA "/fl1";4672static const char merge_dl1[] = MERGE_DATA "/dl1";4673static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";4674static const char merge_fu1[] = MERGE_DATA "/fu1";4675static const char merge_du1[] = MERGE_DATA "/du1";4676static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";4677static const char merge_fo1[] = MERGE_DATA "/fo1";4678static const char merge_do1[] = MERGE_DATA "/do1";4679static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";4680static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";4681static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";4682 4683static const char (*merge_base_files[])[] = {4684 &merge_fl1,4685 &merge_fu1,4686 &merge_fo1,4687 NULL,4688};4689static const char (*merge_base_directories[])[] = {4690 &merge_dl1,4691 &merge_du1,4692 &merge_do1,4693 NULL,4694};4695static const char (*merge_sub_files[])[] = {4696 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,4697 &merge_do1_fl3, &merge_do1_fu3, NULL,4698};4699 4700/*4701 * layout2_overlay hierarchy:4702 *4703 * tmp4704 * ├── lower4705 * │ └── data4706 * │ ├── dl14707 * │ │ └── fl24708 * │ ├── do14709 * │ │ ├── fl34710 * │ │ └── fo24711 * │ ├── fl14712 * │ └── fo14713 * ├── merge4714 * │ └── data4715 * │ ├── dl14716 * │ │ └── fl24717 * │ ├── do14718 * │ │ ├── fl34719 * │ │ ├── fo24720 * │ │ └── fu34721 * │ ├── du14722 * │ │ └── fu24723 * │ ├── fl14724 * │ ├── fo14725 * │ └── fu14726 * └── upper4727 * ├── data4728 * │ ├── do14729 * │ │ ├── fo24730 * │ │ └── fu34731 * │ ├── du14732 * │ │ └── fu24733 * │ ├── fo14734 * │ └── fu14735 * └── work4736 * └── work4737 */4738 4739FIXTURE(layout2_overlay)4740{4741 bool skip_test;4742};4743 4744FIXTURE_SETUP(layout2_overlay)4745{4746 if (!supports_filesystem("overlay")) {4747 self->skip_test = true;4748 SKIP(return, "overlayfs is not supported (setup)");4749 }4750 4751 prepare_layout(_metadata);4752 4753 create_directory(_metadata, LOWER_BASE);4754 set_cap(_metadata, CAP_SYS_ADMIN);4755 /* Creates tmpfs mount points to get deterministic overlayfs. */4756 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));4757 clear_cap(_metadata, CAP_SYS_ADMIN);4758 create_file(_metadata, lower_fl1);4759 create_file(_metadata, lower_dl1_fl2);4760 create_file(_metadata, lower_fo1);4761 create_file(_metadata, lower_do1_fo2);4762 create_file(_metadata, lower_do1_fl3);4763 4764 create_directory(_metadata, UPPER_BASE);4765 set_cap(_metadata, CAP_SYS_ADMIN);4766 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));4767 clear_cap(_metadata, CAP_SYS_ADMIN);4768 create_file(_metadata, upper_fu1);4769 create_file(_metadata, upper_du1_fu2);4770 create_file(_metadata, upper_fo1);4771 create_file(_metadata, upper_do1_fo2);4772 create_file(_metadata, upper_do1_fu3);4773 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));4774 4775 create_directory(_metadata, MERGE_DATA);4776 set_cap(_metadata, CAP_SYS_ADMIN);4777 set_cap(_metadata, CAP_DAC_OVERRIDE);4778 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,4779 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA4780 ",workdir=" UPPER_WORK));4781 clear_cap(_metadata, CAP_DAC_OVERRIDE);4782 clear_cap(_metadata, CAP_SYS_ADMIN);4783}4784 4785FIXTURE_TEARDOWN_PARENT(layout2_overlay)4786{4787 if (self->skip_test)4788 SKIP(return, "overlayfs is not supported (teardown)");4789 4790 EXPECT_EQ(0, remove_path(lower_do1_fl3));4791 EXPECT_EQ(0, remove_path(lower_dl1_fl2));4792 EXPECT_EQ(0, remove_path(lower_fl1));4793 EXPECT_EQ(0, remove_path(lower_do1_fo2));4794 EXPECT_EQ(0, remove_path(lower_fo1));4795 4796 /* umount(LOWER_BASE)) is handled by namespace lifetime. */4797 EXPECT_EQ(0, remove_path(LOWER_BASE));4798 4799 EXPECT_EQ(0, remove_path(upper_do1_fu3));4800 EXPECT_EQ(0, remove_path(upper_du1_fu2));4801 EXPECT_EQ(0, remove_path(upper_fu1));4802 EXPECT_EQ(0, remove_path(upper_do1_fo2));4803 EXPECT_EQ(0, remove_path(upper_fo1));4804 EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));4805 4806 /* umount(UPPER_BASE)) is handled by namespace lifetime. */4807 EXPECT_EQ(0, remove_path(UPPER_BASE));4808 4809 /* umount(MERGE_DATA)) is handled by namespace lifetime. */4810 EXPECT_EQ(0, remove_path(MERGE_DATA));4811 4812 cleanup_layout(_metadata);4813}4814 4815TEST_F_FORK(layout2_overlay, no_restriction)4816{4817 if (self->skip_test)4818 SKIP(return, "overlayfs is not supported (test)");4819 4820 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));4821 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));4822 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));4823 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));4824 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));4825 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));4826 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));4827 4828 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));4829 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));4830 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));4831 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));4832 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));4833 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));4834 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));4835 4836 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));4837 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));4838 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));4839 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));4840 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));4841 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));4842 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));4843 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));4844 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));4845 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));4846 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));4847}4848 4849#define for_each_path(path_list, path_entry, i) \4850 for (i = 0, path_entry = *path_list[i]; path_list[i]; \4851 path_entry = *path_list[++i])4852 4853TEST_F_FORK(layout2_overlay, same_content_different_file)4854{4855 /* Sets access right on parent directories of both layers. */4856 const struct rule layer1_base[] = {4857 {4858 .path = LOWER_BASE,4859 .access = LANDLOCK_ACCESS_FS_READ_FILE,4860 },4861 {4862 .path = UPPER_BASE,4863 .access = LANDLOCK_ACCESS_FS_READ_FILE,4864 },4865 {4866 .path = MERGE_BASE,4867 .access = ACCESS_RW,4868 },4869 {},4870 };4871 const struct rule layer2_data[] = {4872 {4873 .path = LOWER_DATA,4874 .access = LANDLOCK_ACCESS_FS_READ_FILE,4875 },4876 {4877 .path = UPPER_DATA,4878 .access = LANDLOCK_ACCESS_FS_READ_FILE,4879 },4880 {4881 .path = MERGE_DATA,4882 .access = ACCESS_RW,4883 },4884 {},4885 };4886 /* Sets access right on directories inside both layers. */4887 const struct rule layer3_subdirs[] = {4888 {4889 .path = lower_dl1,4890 .access = LANDLOCK_ACCESS_FS_READ_FILE,4891 },4892 {4893 .path = lower_do1,4894 .access = LANDLOCK_ACCESS_FS_READ_FILE,4895 },4896 {4897 .path = upper_du1,4898 .access = LANDLOCK_ACCESS_FS_READ_FILE,4899 },4900 {4901 .path = upper_do1,4902 .access = LANDLOCK_ACCESS_FS_READ_FILE,4903 },4904 {4905 .path = merge_dl1,4906 .access = ACCESS_RW,4907 },4908 {4909 .path = merge_du1,4910 .access = ACCESS_RW,4911 },4912 {4913 .path = merge_do1,4914 .access = ACCESS_RW,4915 },4916 {},4917 };4918 /* Tighten access rights to the files. */4919 const struct rule layer4_files[] = {4920 {4921 .path = lower_dl1_fl2,4922 .access = LANDLOCK_ACCESS_FS_READ_FILE,4923 },4924 {4925 .path = lower_do1_fo2,4926 .access = LANDLOCK_ACCESS_FS_READ_FILE,4927 },4928 {4929 .path = lower_do1_fl3,4930 .access = LANDLOCK_ACCESS_FS_READ_FILE,4931 },4932 {4933 .path = upper_du1_fu2,4934 .access = LANDLOCK_ACCESS_FS_READ_FILE,4935 },4936 {4937 .path = upper_do1_fo2,4938 .access = LANDLOCK_ACCESS_FS_READ_FILE,4939 },4940 {4941 .path = upper_do1_fu3,4942 .access = LANDLOCK_ACCESS_FS_READ_FILE,4943 },4944 {4945 .path = merge_dl1_fl2,4946 .access = LANDLOCK_ACCESS_FS_READ_FILE |4947 LANDLOCK_ACCESS_FS_WRITE_FILE,4948 },4949 {4950 .path = merge_du1_fu2,4951 .access = LANDLOCK_ACCESS_FS_READ_FILE |4952 LANDLOCK_ACCESS_FS_WRITE_FILE,4953 },4954 {4955 .path = merge_do1_fo2,4956 .access = LANDLOCK_ACCESS_FS_READ_FILE |4957 LANDLOCK_ACCESS_FS_WRITE_FILE,4958 },4959 {4960 .path = merge_do1_fl3,4961 .access = LANDLOCK_ACCESS_FS_READ_FILE |4962 LANDLOCK_ACCESS_FS_WRITE_FILE,4963 },4964 {4965 .path = merge_do1_fu3,4966 .access = LANDLOCK_ACCESS_FS_READ_FILE |4967 LANDLOCK_ACCESS_FS_WRITE_FILE,4968 },4969 {},4970 };4971 const struct rule layer5_merge_only[] = {4972 {4973 .path = MERGE_DATA,4974 .access = LANDLOCK_ACCESS_FS_READ_FILE |4975 LANDLOCK_ACCESS_FS_WRITE_FILE,4976 },4977 {},4978 };4979 int ruleset_fd;4980 size_t i;4981 const char *path_entry;4982 4983 if (self->skip_test)4984 SKIP(return, "overlayfs is not supported (test)");4985 4986 /* Sets rules on base directories (i.e. outside overlay scope). */4987 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);4988 ASSERT_LE(0, ruleset_fd);4989 enforce_ruleset(_metadata, ruleset_fd);4990 ASSERT_EQ(0, close(ruleset_fd));4991 4992 /* Checks lower layer. */4993 for_each_path(lower_base_files, path_entry, i) {4994 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));4995 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));4996 }4997 for_each_path(lower_base_directories, path_entry, i) {4998 ASSERT_EQ(EACCES,4999 test_open(path_entry, O_RDONLY | O_DIRECTORY));5000 }5001 for_each_path(lower_sub_files, path_entry, i) {5002 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5003 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5004 }5005 /* Checks upper layer. */5006 for_each_path(upper_base_files, path_entry, i) {5007 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5008 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5009 }5010 for_each_path(upper_base_directories, path_entry, i) {5011 ASSERT_EQ(EACCES,5012 test_open(path_entry, O_RDONLY | O_DIRECTORY));5013 }5014 for_each_path(upper_sub_files, path_entry, i) {5015 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5016 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5017 }5018 /*5019 * Checks that access rights are independent from the lower and upper5020 * layers: write access to upper files viewed through the merge point5021 * is still allowed, and write access to lower file viewed (and copied)5022 * through the merge point is still allowed.5023 */5024 for_each_path(merge_base_files, path_entry, i) {5025 ASSERT_EQ(0, test_open(path_entry, O_RDWR));5026 }5027 for_each_path(merge_base_directories, path_entry, i) {5028 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));5029 }5030 for_each_path(merge_sub_files, path_entry, i) {5031 ASSERT_EQ(0, test_open(path_entry, O_RDWR));5032 }5033 5034 /* Sets rules on data directories (i.e. inside overlay scope). */5035 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);5036 ASSERT_LE(0, ruleset_fd);5037 enforce_ruleset(_metadata, ruleset_fd);5038 ASSERT_EQ(0, close(ruleset_fd));5039 5040 /* Checks merge. */5041 for_each_path(merge_base_files, path_entry, i) {5042 ASSERT_EQ(0, test_open(path_entry, O_RDWR));5043 }5044 for_each_path(merge_base_directories, path_entry, i) {5045 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));5046 }5047 for_each_path(merge_sub_files, path_entry, i) {5048 ASSERT_EQ(0, test_open(path_entry, O_RDWR));5049 }5050 5051 /* Same checks with tighter rules. */5052 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);5053 ASSERT_LE(0, ruleset_fd);5054 enforce_ruleset(_metadata, ruleset_fd);5055 ASSERT_EQ(0, close(ruleset_fd));5056 5057 /* Checks changes for lower layer. */5058 for_each_path(lower_base_files, path_entry, i) {5059 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5060 }5061 /* Checks changes for upper layer. */5062 for_each_path(upper_base_files, path_entry, i) {5063 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5064 }5065 /* Checks all merge accesses. */5066 for_each_path(merge_base_files, path_entry, i) {5067 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));5068 }5069 for_each_path(merge_base_directories, path_entry, i) {5070 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));5071 }5072 for_each_path(merge_sub_files, path_entry, i) {5073 ASSERT_EQ(0, test_open(path_entry, O_RDWR));5074 }5075 5076 /* Sets rules directly on overlayed files. */5077 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);5078 ASSERT_LE(0, ruleset_fd);5079 enforce_ruleset(_metadata, ruleset_fd);5080 ASSERT_EQ(0, close(ruleset_fd));5081 5082 /* Checks unchanged accesses on lower layer. */5083 for_each_path(lower_sub_files, path_entry, i) {5084 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5085 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5086 }5087 /* Checks unchanged accesses on upper layer. */5088 for_each_path(upper_sub_files, path_entry, i) {5089 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));5090 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));5091 }5092 /* Checks all merge accesses. */5093 for_each_path(merge_base_files, path_entry, i) {5094 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));5095 }5096 for_each_path(merge_base_directories, path_entry, i) {5097 ASSERT_EQ(EACCES,5098 test_open(path_entry, O_RDONLY | O_DIRECTORY));5099 }5100 for_each_path(merge_sub_files, path_entry, i) {5101 ASSERT_EQ(0, test_open(path_entry, O_RDWR));5102 }5103 5104 /* Only allowes access to the merge hierarchy. */5105 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);5106 ASSERT_LE(0, ruleset_fd);5107 enforce_ruleset(_metadata, ruleset_fd);5108 ASSERT_EQ(0, close(ruleset_fd));5109 5110 /* Checks new accesses on lower layer. */5111 for_each_path(lower_sub_files, path_entry, i) {5112 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5113 }5114 /* Checks new accesses on upper layer. */5115 for_each_path(upper_sub_files, path_entry, i) {5116 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));5117 }5118 /* Checks all merge accesses. */5119 for_each_path(merge_base_files, path_entry, i) {5120 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));5121 }5122 for_each_path(merge_base_directories, path_entry, i) {5123 ASSERT_EQ(EACCES,5124 test_open(path_entry, O_RDONLY | O_DIRECTORY));5125 }5126 for_each_path(merge_sub_files, path_entry, i) {5127 ASSERT_EQ(0, test_open(path_entry, O_RDWR));5128 }5129}5130 5131FIXTURE(layout3_fs)5132{5133 bool has_created_dir;5134 bool has_created_file;5135 bool skip_test;5136};5137 5138FIXTURE_VARIANT(layout3_fs)5139{5140 const struct mnt_opt mnt;5141 const char *const file_path;5142 unsigned int cwd_fs_magic;5143};5144 5145/* clang-format off */5146FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {5147 /* clang-format on */5148 .mnt = {5149 .type = "tmpfs",5150 .data = MNT_TMP_DATA,5151 },5152 .file_path = file1_s1d1,5153};5154 5155FIXTURE_VARIANT_ADD(layout3_fs, ramfs) {5156 .mnt = {5157 .type = "ramfs",5158 .data = "mode=700",5159 },5160 .file_path = TMP_DIR "/dir/file",5161};5162 5163FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {5164 .mnt = {5165 .type = "cgroup2",5166 },5167 .file_path = TMP_DIR "/test/cgroup.procs",5168};5169 5170FIXTURE_VARIANT_ADD(layout3_fs, proc) {5171 .mnt = {5172 .type = "proc",5173 },5174 .file_path = TMP_DIR "/self/status",5175};5176 5177FIXTURE_VARIANT_ADD(layout3_fs, sysfs) {5178 .mnt = {5179 .type = "sysfs",5180 },5181 .file_path = TMP_DIR "/kernel/notes",5182};5183 5184FIXTURE_VARIANT_ADD(layout3_fs, hostfs) {5185 .mnt = {5186 .source = TMP_DIR,5187 .flags = MS_BIND,5188 },5189 .file_path = TMP_DIR "/dir/file",5190 .cwd_fs_magic = HOSTFS_SUPER_MAGIC,5191};5192 5193static char *dirname_alloc(const char *path)5194{5195 char *dup;5196 5197 if (!path)5198 return NULL;5199 5200 dup = strdup(path);5201 if (!dup)5202 return NULL;5203 5204 return dirname(dup);5205}5206 5207FIXTURE_SETUP(layout3_fs)5208{5209 struct stat statbuf;5210 char *dir_path = dirname_alloc(variant->file_path);5211 5212 if (!supports_filesystem(variant->mnt.type) ||5213 !cwd_matches_fs(variant->cwd_fs_magic)) {5214 self->skip_test = true;5215 SKIP(return, "this filesystem is not supported (setup)");5216 }5217 5218 prepare_layout_opt(_metadata, &variant->mnt);5219 5220 /* Creates directory when required. */5221 if (stat(dir_path, &statbuf)) {5222 set_cap(_metadata, CAP_DAC_OVERRIDE);5223 EXPECT_EQ(0, mkdir(dir_path, 0700))5224 {5225 TH_LOG("Failed to create directory \"%s\": %s",5226 dir_path, strerror(errno));5227 }5228 self->has_created_dir = true;5229 clear_cap(_metadata, CAP_DAC_OVERRIDE);5230 }5231 5232 /* Creates file when required. */5233 if (stat(variant->file_path, &statbuf)) {5234 int fd;5235 5236 set_cap(_metadata, CAP_DAC_OVERRIDE);5237 fd = creat(variant->file_path, 0600);5238 EXPECT_LE(0, fd)5239 {5240 TH_LOG("Failed to create file \"%s\": %s",5241 variant->file_path, strerror(errno));5242 }5243 EXPECT_EQ(0, close(fd));5244 self->has_created_file = true;5245 clear_cap(_metadata, CAP_DAC_OVERRIDE);5246 }5247 5248 free(dir_path);5249}5250 5251FIXTURE_TEARDOWN_PARENT(layout3_fs)5252{5253 if (self->skip_test)5254 SKIP(return, "this filesystem is not supported (teardown)");5255 5256 if (self->has_created_file) {5257 set_cap(_metadata, CAP_DAC_OVERRIDE);5258 /*5259 * Don't check for error because the file might already5260 * have been removed (cf. release_inode test).5261 */5262 unlink(variant->file_path);5263 clear_cap(_metadata, CAP_DAC_OVERRIDE);5264 }5265 5266 if (self->has_created_dir) {5267 char *dir_path = dirname_alloc(variant->file_path);5268 5269 set_cap(_metadata, CAP_DAC_OVERRIDE);5270 /*5271 * Don't check for error because the directory might already5272 * have been removed (cf. release_inode test).5273 */5274 rmdir(dir_path);5275 clear_cap(_metadata, CAP_DAC_OVERRIDE);5276 free(dir_path);5277 }5278 5279 cleanup_layout(_metadata);5280}5281 5282static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,5283 FIXTURE_DATA(layout3_fs) * self,5284 const FIXTURE_VARIANT(layout3_fs) * variant,5285 const char *const rule_path)5286{5287 const struct rule layer1_allow_read_file[] = {5288 {5289 .path = rule_path,5290 .access = LANDLOCK_ACCESS_FS_READ_FILE,5291 },5292 {},5293 };5294 const struct landlock_ruleset_attr layer2_deny_everything_attr = {5295 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,5296 };5297 const char *const dev_null_path = "/dev/null";5298 int ruleset_fd;5299 5300 if (self->skip_test)5301 SKIP(return, "this filesystem is not supported (test)");5302 5303 /* Checks without Landlock. */5304 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));5305 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));5306 5307 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,5308 layer1_allow_read_file);5309 EXPECT_LE(0, ruleset_fd);5310 enforce_ruleset(_metadata, ruleset_fd);5311 EXPECT_EQ(0, close(ruleset_fd));5312 5313 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));5314 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));5315 5316 /* Forbids directory reading. */5317 ruleset_fd =5318 landlock_create_ruleset(&layer2_deny_everything_attr,5319 sizeof(layer2_deny_everything_attr), 0);5320 EXPECT_LE(0, ruleset_fd);5321 enforce_ruleset(_metadata, ruleset_fd);5322 EXPECT_EQ(0, close(ruleset_fd));5323 5324 /* Checks with Landlock and forbidden access. */5325 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));5326 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));5327}5328 5329/* Matrix of tests to check file hierarchy evaluation. */5330 5331TEST_F_FORK(layout3_fs, tag_inode_dir_parent)5332{5333 /* The current directory must not be the root for this test. */5334 layer3_fs_tag_inode(_metadata, self, variant, ".");5335}5336 5337TEST_F_FORK(layout3_fs, tag_inode_dir_mnt)5338{5339 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);5340}5341 5342TEST_F_FORK(layout3_fs, tag_inode_dir_child)5343{5344 char *dir_path = dirname_alloc(variant->file_path);5345 5346 layer3_fs_tag_inode(_metadata, self, variant, dir_path);5347 free(dir_path);5348}5349 5350TEST_F_FORK(layout3_fs, tag_inode_file)5351{5352 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);5353}5354 5355/* Light version of layout1.release_inodes */5356TEST_F_FORK(layout3_fs, release_inodes)5357{5358 const struct rule layer1[] = {5359 {5360 .path = TMP_DIR,5361 .access = LANDLOCK_ACCESS_FS_READ_DIR,5362 },5363 {},5364 };5365 int ruleset_fd;5366 5367 if (self->skip_test)5368 SKIP(return, "this filesystem is not supported (test)");5369 5370 /* Clean up for the teardown to not fail. */5371 if (self->has_created_file)5372 EXPECT_EQ(0, remove_path(variant->file_path));5373 5374 if (self->has_created_dir) {5375 char *dir_path = dirname_alloc(variant->file_path);5376 5377 /* Don't check for error because of cgroup specificities. */5378 remove_path(dir_path);5379 free(dir_path);5380 }5381 5382 ruleset_fd =5383 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);5384 ASSERT_LE(0, ruleset_fd);5385 5386 /* Unmount the filesystem while it is being used by a ruleset. */5387 set_cap(_metadata, CAP_SYS_ADMIN);5388 ASSERT_EQ(0, umount(TMP_DIR));5389 clear_cap(_metadata, CAP_SYS_ADMIN);5390 5391 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */5392 set_cap(_metadata, CAP_SYS_ADMIN);5393 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));5394 clear_cap(_metadata, CAP_SYS_ADMIN);5395 5396 enforce_ruleset(_metadata, ruleset_fd);5397 ASSERT_EQ(0, close(ruleset_fd));5398 5399 /* Checks that access to the new mount point is denied. */5400 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));5401}5402 5403TEST_HARNESS_MAIN5404