Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/landlock: Add user space tests

Test all Landlock system calls, ptrace hooks semantic and filesystem
access-control with multiple layouts.

Test coverage for security/landlock/ is 93.6% of lines. The code not
covered only deals with internal kernel errors (e.g. memory allocation)
and race conditions.

Cc: James Morris <jmorris@namei.org>
Cc: Jann Horn <jannh@google.com>
Cc: Serge E. Hallyn <serge@hallyn.com>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Mickaël Salaün <mic@linux.microsoft.com>
Reviewed-by: Vincent Dagonneau <vincent.dagonneau@ssi.gouv.fr>
Reviewed-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20210422154123.13086-11-mic@digikod.net
Signed-off-by: James Morris <jamorris@linux.microsoft.com>

authored by

Mickaël Salaün and committed by
James Morris
e1199815 265885da

+3570
+1
MAINTAINERS
··· 10005 10005 T: git https://github.com/landlock-lsm/linux.git 10006 10006 F: include/uapi/linux/landlock.h 10007 10007 F: security/landlock/ 10008 + F: tools/testing/selftests/landlock/ 10008 10009 K: landlock 10009 10010 K: LANDLOCK 10010 10011
+1
tools/testing/selftests/Makefile
··· 25 25 TARGETS += kcmp 26 26 TARGETS += kexec 27 27 TARGETS += kvm 28 + TARGETS += landlock 28 29 TARGETS += lib 29 30 TARGETS += livepatch 30 31 TARGETS += lkdtm
+2
tools/testing/selftests/landlock/.gitignore
··· 1 + /*_test 2 + /true
+24
tools/testing/selftests/landlock/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + CFLAGS += -Wall -O2 4 + 5 + src_test := $(wildcard *_test.c) 6 + 7 + TEST_GEN_PROGS := $(src_test:.c=) 8 + 9 + TEST_GEN_PROGS_EXTENDED := true 10 + 11 + KSFT_KHDR_INSTALL := 1 12 + OVERRIDE_TARGETS := 1 13 + include ../lib.mk 14 + 15 + khdr_dir = $(top_srcdir)/usr/include 16 + 17 + $(khdr_dir)/linux/landlock.h: khdr 18 + @: 19 + 20 + $(OUTPUT)/true: true.c 21 + $(LINK.c) $< $(LDLIBS) -o $@ -static 22 + 23 + $(OUTPUT)/%_test: %_test.c $(khdr_dir)/linux/landlock.h ../kselftest_harness.h common.h 24 + $(LINK.c) $< $(LDLIBS) -o $@ -lcap -I$(khdr_dir)
+219
tools/testing/selftests/landlock/base_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Common user space base 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + */ 8 + 9 + #define _GNU_SOURCE 10 + #include <errno.h> 11 + #include <fcntl.h> 12 + #include <linux/landlock.h> 13 + #include <string.h> 14 + #include <sys/prctl.h> 15 + #include <sys/socket.h> 16 + #include <sys/types.h> 17 + 18 + #include "common.h" 19 + 20 + #ifndef O_PATH 21 + #define O_PATH 010000000 22 + #endif 23 + 24 + TEST(inconsistent_attr) { 25 + const long page_size = sysconf(_SC_PAGESIZE); 26 + char *const buf = malloc(page_size + 1); 27 + struct landlock_ruleset_attr *const ruleset_attr = (void *)buf; 28 + 29 + ASSERT_NE(NULL, buf); 30 + 31 + /* Checks copy_from_user(). */ 32 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 0, 0)); 33 + /* The size if less than sizeof(struct landlock_attr_enforce). */ 34 + ASSERT_EQ(EINVAL, errno); 35 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 1, 0)); 36 + ASSERT_EQ(EINVAL, errno); 37 + 38 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 1, 0)); 39 + /* The size if less than sizeof(struct landlock_attr_enforce). */ 40 + ASSERT_EQ(EFAULT, errno); 41 + 42 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 43 + sizeof(struct landlock_ruleset_attr), 0)); 44 + ASSERT_EQ(EFAULT, errno); 45 + 46 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0)); 47 + ASSERT_EQ(E2BIG, errno); 48 + 49 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 50 + sizeof(struct landlock_ruleset_attr), 0)); 51 + ASSERT_EQ(ENOMSG, errno); 52 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0)); 53 + ASSERT_EQ(ENOMSG, errno); 54 + 55 + /* Checks non-zero value. */ 56 + buf[page_size - 2] = '.'; 57 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0)); 58 + ASSERT_EQ(E2BIG, errno); 59 + 60 + ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0)); 61 + ASSERT_EQ(E2BIG, errno); 62 + 63 + free(buf); 64 + } 65 + 66 + TEST(empty_path_beneath_attr) { 67 + const struct landlock_ruleset_attr ruleset_attr = { 68 + .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE, 69 + }; 70 + const int ruleset_fd = landlock_create_ruleset(&ruleset_attr, 71 + sizeof(ruleset_attr), 0); 72 + 73 + ASSERT_LE(0, ruleset_fd); 74 + 75 + /* Similar to struct landlock_path_beneath_attr.parent_fd = 0 */ 76 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 77 + NULL, 0)); 78 + ASSERT_EQ(EFAULT, errno); 79 + ASSERT_EQ(0, close(ruleset_fd)); 80 + } 81 + 82 + TEST(inval_fd_enforce) { 83 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 84 + 85 + ASSERT_EQ(-1, landlock_restrict_self(-1, 0)); 86 + ASSERT_EQ(EBADF, errno); 87 + } 88 + 89 + TEST(unpriv_enforce_without_no_new_privs) { 90 + int err; 91 + 92 + drop_caps(_metadata); 93 + err = landlock_restrict_self(-1, 0); 94 + ASSERT_EQ(EPERM, errno); 95 + ASSERT_EQ(err, -1); 96 + } 97 + 98 + TEST(ruleset_fd_io) 99 + { 100 + struct landlock_ruleset_attr ruleset_attr = { 101 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 102 + }; 103 + int ruleset_fd; 104 + char buf; 105 + 106 + drop_caps(_metadata); 107 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 108 + sizeof(ruleset_attr), 0); 109 + ASSERT_LE(0, ruleset_fd); 110 + 111 + ASSERT_EQ(-1, write(ruleset_fd, ".", 1)); 112 + ASSERT_EQ(EINVAL, errno); 113 + ASSERT_EQ(-1, read(ruleset_fd, &buf, 1)); 114 + ASSERT_EQ(EINVAL, errno); 115 + 116 + ASSERT_EQ(0, close(ruleset_fd)); 117 + } 118 + 119 + /* Tests enforcement of a ruleset FD transferred through a UNIX socket. */ 120 + TEST(ruleset_fd_transfer) 121 + { 122 + struct landlock_ruleset_attr ruleset_attr = { 123 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR, 124 + }; 125 + struct landlock_path_beneath_attr path_beneath_attr = { 126 + .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR, 127 + }; 128 + int ruleset_fd_tx, dir_fd; 129 + union { 130 + /* Aligned ancillary data buffer. */ 131 + char buf[CMSG_SPACE(sizeof(ruleset_fd_tx))]; 132 + struct cmsghdr _align; 133 + } cmsg_tx = {}; 134 + char data_tx = '.'; 135 + struct iovec io = { 136 + .iov_base = &data_tx, 137 + .iov_len = sizeof(data_tx), 138 + }; 139 + struct msghdr msg = { 140 + .msg_iov = &io, 141 + .msg_iovlen = 1, 142 + .msg_control = &cmsg_tx.buf, 143 + .msg_controllen = sizeof(cmsg_tx.buf), 144 + }; 145 + struct cmsghdr *cmsg; 146 + int socket_fds[2]; 147 + pid_t child; 148 + int status; 149 + 150 + drop_caps(_metadata); 151 + 152 + /* Creates a test ruleset with a simple rule. */ 153 + ruleset_fd_tx = landlock_create_ruleset(&ruleset_attr, 154 + sizeof(ruleset_attr), 0); 155 + ASSERT_LE(0, ruleset_fd_tx); 156 + path_beneath_attr.parent_fd = open("/tmp", O_PATH | O_NOFOLLOW | 157 + O_DIRECTORY | O_CLOEXEC); 158 + ASSERT_LE(0, path_beneath_attr.parent_fd); 159 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd_tx, LANDLOCK_RULE_PATH_BENEATH, 160 + &path_beneath_attr, 0)); 161 + ASSERT_EQ(0, close(path_beneath_attr.parent_fd)); 162 + 163 + cmsg = CMSG_FIRSTHDR(&msg); 164 + ASSERT_NE(NULL, cmsg); 165 + cmsg->cmsg_len = CMSG_LEN(sizeof(ruleset_fd_tx)); 166 + cmsg->cmsg_level = SOL_SOCKET; 167 + cmsg->cmsg_type = SCM_RIGHTS; 168 + memcpy(CMSG_DATA(cmsg), &ruleset_fd_tx, sizeof(ruleset_fd_tx)); 169 + 170 + /* Sends the ruleset FD over a socketpair and then close it. */ 171 + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, socket_fds)); 172 + ASSERT_EQ(sizeof(data_tx), sendmsg(socket_fds[0], &msg, 0)); 173 + ASSERT_EQ(0, close(socket_fds[0])); 174 + ASSERT_EQ(0, close(ruleset_fd_tx)); 175 + 176 + child = fork(); 177 + ASSERT_LE(0, child); 178 + if (child == 0) { 179 + int ruleset_fd_rx; 180 + 181 + *(char *)msg.msg_iov->iov_base = '\0'; 182 + ASSERT_EQ(sizeof(data_tx), recvmsg(socket_fds[1], &msg, MSG_CMSG_CLOEXEC)); 183 + ASSERT_EQ('.', *(char *)msg.msg_iov->iov_base); 184 + ASSERT_EQ(0, close(socket_fds[1])); 185 + cmsg = CMSG_FIRSTHDR(&msg); 186 + ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(ruleset_fd_tx))); 187 + memcpy(&ruleset_fd_rx, CMSG_DATA(cmsg), sizeof(ruleset_fd_tx)); 188 + 189 + /* Enforces the received ruleset on the child. */ 190 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 191 + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd_rx, 0)); 192 + ASSERT_EQ(0, close(ruleset_fd_rx)); 193 + 194 + /* Checks that the ruleset enforcement. */ 195 + ASSERT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); 196 + ASSERT_EQ(EACCES, errno); 197 + dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 198 + ASSERT_LE(0, dir_fd); 199 + ASSERT_EQ(0, close(dir_fd)); 200 + _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 201 + return; 202 + } 203 + 204 + ASSERT_EQ(0, close(socket_fds[1])); 205 + 206 + /* Checks that the parent is unrestricted. */ 207 + dir_fd = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 208 + ASSERT_LE(0, dir_fd); 209 + ASSERT_EQ(0, close(dir_fd)); 210 + dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 211 + ASSERT_LE(0, dir_fd); 212 + ASSERT_EQ(0, close(dir_fd)); 213 + 214 + ASSERT_EQ(child, waitpid(child, &status, 0)); 215 + ASSERT_EQ(1, WIFEXITED(status)); 216 + ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 217 + } 218 + 219 + TEST_HARNESS_MAIN
+183
tools/testing/selftests/landlock/common.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Landlock test helpers 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + * Copyright © 2021 Microsoft Corporation 8 + */ 9 + 10 + #include <errno.h> 11 + #include <linux/landlock.h> 12 + #include <sys/capability.h> 13 + #include <sys/syscall.h> 14 + #include <sys/types.h> 15 + #include <sys/wait.h> 16 + #include <unistd.h> 17 + 18 + #include "../kselftest_harness.h" 19 + 20 + #ifndef ARRAY_SIZE 21 + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 22 + #endif 23 + 24 + /* 25 + * TEST_F_FORK() is useful when a test drop privileges but the corresponding 26 + * FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory 27 + * where write actions are denied). For convenience, FIXTURE_TEARDOWN() is 28 + * also called when the test failed, but not when FIXTURE_SETUP() failed. For 29 + * this to be possible, we must not call abort() but instead exit smoothly 30 + * (hence the step print). 31 + */ 32 + #define TEST_F_FORK(fixture_name, test_name) \ 33 + static void fixture_name##_##test_name##_child( \ 34 + struct __test_metadata *_metadata, \ 35 + FIXTURE_DATA(fixture_name) *self, \ 36 + const FIXTURE_VARIANT(fixture_name) *variant); \ 37 + TEST_F(fixture_name, test_name) \ 38 + { \ 39 + int status; \ 40 + const pid_t child = fork(); \ 41 + if (child < 0) \ 42 + abort(); \ 43 + if (child == 0) { \ 44 + _metadata->no_print = 1; \ 45 + fixture_name##_##test_name##_child(_metadata, self, variant); \ 46 + if (_metadata->skip) \ 47 + _exit(255); \ 48 + if (_metadata->passed) \ 49 + _exit(0); \ 50 + _exit(_metadata->step); \ 51 + } \ 52 + if (child != waitpid(child, &status, 0)) \ 53 + abort(); \ 54 + if (WIFSIGNALED(status) || !WIFEXITED(status)) { \ 55 + _metadata->passed = 0; \ 56 + _metadata->step = 1; \ 57 + return; \ 58 + } \ 59 + switch (WEXITSTATUS(status)) { \ 60 + case 0: \ 61 + _metadata->passed = 1; \ 62 + break; \ 63 + case 255: \ 64 + _metadata->passed = 1; \ 65 + _metadata->skip = 1; \ 66 + break; \ 67 + default: \ 68 + _metadata->passed = 0; \ 69 + _metadata->step = WEXITSTATUS(status); \ 70 + break; \ 71 + } \ 72 + } \ 73 + static void fixture_name##_##test_name##_child( \ 74 + struct __test_metadata __attribute__((unused)) *_metadata, \ 75 + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ 76 + const FIXTURE_VARIANT(fixture_name) \ 77 + __attribute__((unused)) *variant) 78 + 79 + #ifndef landlock_create_ruleset 80 + static inline int landlock_create_ruleset( 81 + const struct landlock_ruleset_attr *const attr, 82 + const size_t size, const __u32 flags) 83 + { 84 + return syscall(__NR_landlock_create_ruleset, attr, size, flags); 85 + } 86 + #endif 87 + 88 + #ifndef landlock_add_rule 89 + static inline int landlock_add_rule(const int ruleset_fd, 90 + const enum landlock_rule_type rule_type, 91 + const void *const rule_attr, const __u32 flags) 92 + { 93 + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, 94 + rule_attr, flags); 95 + } 96 + #endif 97 + 98 + #ifndef landlock_restrict_self 99 + static inline int landlock_restrict_self(const int ruleset_fd, 100 + const __u32 flags) 101 + { 102 + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 103 + } 104 + #endif 105 + 106 + static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) 107 + { 108 + cap_t cap_p; 109 + /* Only these three capabilities are useful for the tests. */ 110 + const cap_value_t caps[] = { 111 + CAP_DAC_OVERRIDE, 112 + CAP_MKNOD, 113 + CAP_SYS_ADMIN, 114 + CAP_SYS_CHROOT, 115 + }; 116 + 117 + cap_p = cap_get_proc(); 118 + EXPECT_NE(NULL, cap_p) { 119 + TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 120 + } 121 + EXPECT_NE(-1, cap_clear(cap_p)) { 122 + TH_LOG("Failed to cap_clear: %s", strerror(errno)); 123 + } 124 + if (!drop_all) { 125 + EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED, 126 + ARRAY_SIZE(caps), caps, CAP_SET)) { 127 + TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 128 + } 129 + } 130 + EXPECT_NE(-1, cap_set_proc(cap_p)) { 131 + TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 132 + } 133 + EXPECT_NE(-1, cap_free(cap_p)) { 134 + TH_LOG("Failed to cap_free: %s", strerror(errno)); 135 + } 136 + } 137 + 138 + /* We cannot put such helpers in a library because of kselftest_harness.h . */ 139 + __attribute__((__unused__)) 140 + static void disable_caps(struct __test_metadata *const _metadata) 141 + { 142 + _init_caps(_metadata, false); 143 + } 144 + 145 + __attribute__((__unused__)) 146 + static void drop_caps(struct __test_metadata *const _metadata) 147 + { 148 + _init_caps(_metadata, true); 149 + } 150 + 151 + static void _effective_cap(struct __test_metadata *const _metadata, 152 + const cap_value_t caps, const cap_flag_value_t value) 153 + { 154 + cap_t cap_p; 155 + 156 + cap_p = cap_get_proc(); 157 + EXPECT_NE(NULL, cap_p) { 158 + TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 159 + } 160 + EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value)) { 161 + TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 162 + } 163 + EXPECT_NE(-1, cap_set_proc(cap_p)) { 164 + TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 165 + } 166 + EXPECT_NE(-1, cap_free(cap_p)) { 167 + TH_LOG("Failed to cap_free: %s", strerror(errno)); 168 + } 169 + } 170 + 171 + __attribute__((__unused__)) 172 + static void set_cap(struct __test_metadata *const _metadata, 173 + const cap_value_t caps) 174 + { 175 + _effective_cap(_metadata, caps, CAP_SET); 176 + } 177 + 178 + __attribute__((__unused__)) 179 + static void clear_cap(struct __test_metadata *const _metadata, 180 + const cap_value_t caps) 181 + { 182 + _effective_cap(_metadata, caps, CAP_CLEAR); 183 + }
+7
tools/testing/selftests/landlock/config
··· 1 + CONFIG_OVERLAY_FS=y 2 + CONFIG_SECURITY_LANDLOCK=y 3 + CONFIG_SECURITY_PATH=y 4 + CONFIG_SECURITY=y 5 + CONFIG_SHMEM=y 6 + CONFIG_TMPFS_XATTR=y 7 + CONFIG_TMPFS=y
+2791
tools/testing/selftests/landlock/fs_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Filesystem 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2020 ANSSI 7 + * Copyright © 2020-2021 Microsoft Corporation 8 + */ 9 + 10 + #define _GNU_SOURCE 11 + #include <fcntl.h> 12 + #include <linux/landlock.h> 13 + #include <sched.h> 14 + #include <string.h> 15 + #include <sys/capability.h> 16 + #include <sys/mount.h> 17 + #include <sys/prctl.h> 18 + #include <sys/sendfile.h> 19 + #include <sys/stat.h> 20 + #include <sys/sysmacros.h> 21 + #include <unistd.h> 22 + 23 + #include "common.h" 24 + 25 + #define TMP_DIR "tmp" 26 + #define BINARY_PATH "./true" 27 + 28 + /* Paths (sibling number and depth) */ 29 + static const char dir_s1d1[] = TMP_DIR "/s1d1"; 30 + static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 31 + static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 32 + static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 33 + static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 34 + static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 35 + static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 36 + static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 37 + static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 38 + 39 + static const char dir_s2d1[] = TMP_DIR "/s2d1"; 40 + static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 41 + static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 42 + static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 43 + static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 44 + static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 45 + static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 46 + 47 + static const char dir_s3d1[] = TMP_DIR "/s3d1"; 48 + /* dir_s3d2 is a mount point. */ 49 + static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 50 + static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 51 + 52 + /* 53 + * layout1 hierarchy: 54 + * 55 + * tmp 56 + * ├── s1d1 57 + * │   ├── f1 58 + * │   ├── f2 59 + * │   └── s1d2 60 + * │   ├── f1 61 + * │   ├── f2 62 + * │   └── s1d3 63 + * │   ├── f1 64 + * │   └── f2 65 + * ├── s2d1 66 + * │   ├── f1 67 + * │   └── s2d2 68 + * │   ├── f1 69 + * │   └── s2d3 70 + * │   ├── f1 71 + * │   └── f2 72 + * └── s3d1 73 + * └── s3d2 74 + * └── s3d3 75 + */ 76 + 77 + static void mkdir_parents(struct __test_metadata *const _metadata, 78 + const char *const path) 79 + { 80 + char *walker; 81 + const char *parent; 82 + int i, err; 83 + 84 + ASSERT_NE(path[0], '\0'); 85 + walker = strdup(path); 86 + ASSERT_NE(NULL, walker); 87 + parent = walker; 88 + for (i = 1; walker[i]; i++) { 89 + if (walker[i] != '/') 90 + continue; 91 + walker[i] = '\0'; 92 + err = mkdir(parent, 0700); 93 + ASSERT_FALSE(err && errno != EEXIST) { 94 + TH_LOG("Failed to create directory \"%s\": %s", 95 + parent, strerror(errno)); 96 + } 97 + walker[i] = '/'; 98 + } 99 + free(walker); 100 + } 101 + 102 + static void create_directory(struct __test_metadata *const _metadata, 103 + const char *const path) 104 + { 105 + mkdir_parents(_metadata, path); 106 + ASSERT_EQ(0, mkdir(path, 0700)) { 107 + TH_LOG("Failed to create directory \"%s\": %s", path, 108 + strerror(errno)); 109 + } 110 + } 111 + 112 + static void create_file(struct __test_metadata *const _metadata, 113 + const char *const path) 114 + { 115 + mkdir_parents(_metadata, path); 116 + ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) { 117 + TH_LOG("Failed to create file \"%s\": %s", path, 118 + strerror(errno)); 119 + } 120 + } 121 + 122 + static int remove_path(const char *const path) 123 + { 124 + char *walker; 125 + int i, ret, err = 0; 126 + 127 + walker = strdup(path); 128 + if (!walker) { 129 + err = ENOMEM; 130 + goto out; 131 + } 132 + if (unlink(path) && rmdir(path)) { 133 + if (errno != ENOENT) 134 + err = errno; 135 + goto out; 136 + } 137 + for (i = strlen(walker); i > 0; i--) { 138 + if (walker[i] != '/') 139 + continue; 140 + walker[i] = '\0'; 141 + ret = rmdir(walker); 142 + if (ret) { 143 + if (errno != ENOTEMPTY && errno != EBUSY) 144 + err = errno; 145 + goto out; 146 + } 147 + if (strcmp(walker, TMP_DIR) == 0) 148 + goto out; 149 + } 150 + 151 + out: 152 + free(walker); 153 + return err; 154 + } 155 + 156 + static void prepare_layout(struct __test_metadata *const _metadata) 157 + { 158 + disable_caps(_metadata); 159 + umask(0077); 160 + create_directory(_metadata, TMP_DIR); 161 + 162 + /* 163 + * Do not pollute the rest of the system: creates a private mount point 164 + * for tests relying on pivot_root(2) and move_mount(2). 165 + */ 166 + set_cap(_metadata, CAP_SYS_ADMIN); 167 + ASSERT_EQ(0, unshare(CLONE_NEWNS)); 168 + ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700")); 169 + ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 170 + clear_cap(_metadata, CAP_SYS_ADMIN); 171 + } 172 + 173 + static void cleanup_layout(struct __test_metadata *const _metadata) 174 + { 175 + set_cap(_metadata, CAP_SYS_ADMIN); 176 + EXPECT_EQ(0, umount(TMP_DIR)); 177 + clear_cap(_metadata, CAP_SYS_ADMIN); 178 + EXPECT_EQ(0, remove_path(TMP_DIR)); 179 + } 180 + 181 + static void create_layout1(struct __test_metadata *const _metadata) 182 + { 183 + create_file(_metadata, file1_s1d1); 184 + create_file(_metadata, file1_s1d2); 185 + create_file(_metadata, file1_s1d3); 186 + create_file(_metadata, file2_s1d1); 187 + create_file(_metadata, file2_s1d2); 188 + create_file(_metadata, file2_s1d3); 189 + 190 + create_file(_metadata, file1_s2d1); 191 + create_file(_metadata, file1_s2d2); 192 + create_file(_metadata, file1_s2d3); 193 + create_file(_metadata, file2_s2d3); 194 + 195 + create_directory(_metadata, dir_s3d2); 196 + set_cap(_metadata, CAP_SYS_ADMIN); 197 + ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700")); 198 + clear_cap(_metadata, CAP_SYS_ADMIN); 199 + 200 + ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 201 + } 202 + 203 + static void remove_layout1(struct __test_metadata *const _metadata) 204 + { 205 + EXPECT_EQ(0, remove_path(file2_s1d3)); 206 + EXPECT_EQ(0, remove_path(file2_s1d2)); 207 + EXPECT_EQ(0, remove_path(file2_s1d1)); 208 + EXPECT_EQ(0, remove_path(file1_s1d3)); 209 + EXPECT_EQ(0, remove_path(file1_s1d2)); 210 + EXPECT_EQ(0, remove_path(file1_s1d1)); 211 + 212 + EXPECT_EQ(0, remove_path(file2_s2d3)); 213 + EXPECT_EQ(0, remove_path(file1_s2d3)); 214 + EXPECT_EQ(0, remove_path(file1_s2d2)); 215 + EXPECT_EQ(0, remove_path(file1_s2d1)); 216 + 217 + EXPECT_EQ(0, remove_path(dir_s3d3)); 218 + set_cap(_metadata, CAP_SYS_ADMIN); 219 + umount(dir_s3d2); 220 + clear_cap(_metadata, CAP_SYS_ADMIN); 221 + EXPECT_EQ(0, remove_path(dir_s3d2)); 222 + } 223 + 224 + FIXTURE(layout1) { 225 + }; 226 + 227 + FIXTURE_SETUP(layout1) 228 + { 229 + prepare_layout(_metadata); 230 + 231 + create_layout1(_metadata); 232 + } 233 + 234 + FIXTURE_TEARDOWN(layout1) 235 + { 236 + remove_layout1(_metadata); 237 + 238 + cleanup_layout(_metadata); 239 + } 240 + 241 + /* 242 + * This helper enables to use the ASSERT_* macros and print the line number 243 + * pointing to the test caller. 244 + */ 245 + static int test_open_rel(const int dirfd, const char *const path, const int flags) 246 + { 247 + int fd; 248 + 249 + /* Works with file and directories. */ 250 + fd = openat(dirfd, path, flags | O_CLOEXEC); 251 + if (fd < 0) 252 + return errno; 253 + /* 254 + * Mixing error codes from close(2) and open(2) should not lead to any 255 + * (access type) confusion for this test. 256 + */ 257 + if (close(fd) != 0) 258 + return errno; 259 + return 0; 260 + } 261 + 262 + static int test_open(const char *const path, const int flags) 263 + { 264 + return test_open_rel(AT_FDCWD, path, flags); 265 + } 266 + 267 + TEST_F_FORK(layout1, no_restriction) 268 + { 269 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 270 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 271 + ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 272 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 273 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 274 + ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 275 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 276 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 277 + 278 + ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 279 + ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 280 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 281 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 282 + ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 283 + ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 284 + 285 + ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 286 + ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 287 + ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 288 + } 289 + 290 + TEST_F_FORK(layout1, inval) 291 + { 292 + struct landlock_path_beneath_attr path_beneath = { 293 + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 294 + LANDLOCK_ACCESS_FS_WRITE_FILE, 295 + .parent_fd = -1, 296 + }; 297 + struct landlock_ruleset_attr ruleset_attr = { 298 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 299 + LANDLOCK_ACCESS_FS_WRITE_FILE, 300 + }; 301 + int ruleset_fd; 302 + 303 + path_beneath.parent_fd = open(dir_s1d2, O_PATH | O_DIRECTORY | 304 + O_CLOEXEC); 305 + ASSERT_LE(0, path_beneath.parent_fd); 306 + 307 + ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 308 + ASSERT_LE(0, ruleset_fd); 309 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 310 + &path_beneath, 0)); 311 + /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 312 + ASSERT_EQ(EBADF, errno); 313 + ASSERT_EQ(0, close(ruleset_fd)); 314 + 315 + ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 316 + ASSERT_LE(0, ruleset_fd); 317 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 318 + &path_beneath, 0)); 319 + /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 320 + ASSERT_EQ(EBADFD, errno); 321 + ASSERT_EQ(0, close(ruleset_fd)); 322 + 323 + /* Gets a real ruleset. */ 324 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 325 + sizeof(ruleset_attr), 0); 326 + ASSERT_LE(0, ruleset_fd); 327 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 328 + &path_beneath, 0)); 329 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 330 + 331 + /* Tests without O_PATH. */ 332 + path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 333 + ASSERT_LE(0, path_beneath.parent_fd); 334 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 335 + &path_beneath, 0)); 336 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 337 + 338 + /* Tests with a ruleset FD. */ 339 + path_beneath.parent_fd = ruleset_fd; 340 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 341 + &path_beneath, 0)); 342 + ASSERT_EQ(EBADFD, errno); 343 + 344 + /* Checks unhandled allowed_access. */ 345 + path_beneath.parent_fd = open(dir_s1d2, O_PATH | O_DIRECTORY | 346 + O_CLOEXEC); 347 + ASSERT_LE(0, path_beneath.parent_fd); 348 + 349 + /* Test with legitimate values. */ 350 + path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 351 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 352 + &path_beneath, 0)); 353 + ASSERT_EQ(EINVAL, errno); 354 + path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 355 + 356 + /* Test with unknown (64-bits) value. */ 357 + path_beneath.allowed_access |= (1ULL << 60); 358 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 359 + &path_beneath, 0)); 360 + ASSERT_EQ(EINVAL, errno); 361 + path_beneath.allowed_access &= ~(1ULL << 60); 362 + 363 + /* Test with no access. */ 364 + path_beneath.allowed_access = 0; 365 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 366 + &path_beneath, 0)); 367 + ASSERT_EQ(ENOMSG, errno); 368 + path_beneath.allowed_access &= ~(1ULL << 60); 369 + 370 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 371 + 372 + /* Enforces the ruleset. */ 373 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 374 + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 375 + 376 + ASSERT_EQ(0, close(ruleset_fd)); 377 + } 378 + 379 + #define ACCESS_FILE ( \ 380 + LANDLOCK_ACCESS_FS_EXECUTE | \ 381 + LANDLOCK_ACCESS_FS_WRITE_FILE | \ 382 + LANDLOCK_ACCESS_FS_READ_FILE) 383 + 384 + #define ACCESS_LAST LANDLOCK_ACCESS_FS_MAKE_SYM 385 + 386 + #define ACCESS_ALL ( \ 387 + ACCESS_FILE | \ 388 + LANDLOCK_ACCESS_FS_READ_DIR | \ 389 + LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 390 + LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 391 + LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 392 + LANDLOCK_ACCESS_FS_MAKE_DIR | \ 393 + LANDLOCK_ACCESS_FS_MAKE_REG | \ 394 + LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 395 + LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 396 + LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 397 + ACCESS_LAST) 398 + 399 + TEST_F_FORK(layout1, file_access_rights) 400 + { 401 + __u64 access; 402 + int err; 403 + struct landlock_path_beneath_attr path_beneath = {}; 404 + struct landlock_ruleset_attr ruleset_attr = { 405 + .handled_access_fs = ACCESS_ALL, 406 + }; 407 + const int ruleset_fd = landlock_create_ruleset(&ruleset_attr, 408 + sizeof(ruleset_attr), 0); 409 + 410 + ASSERT_LE(0, ruleset_fd); 411 + 412 + /* Tests access rights for files. */ 413 + path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 414 + ASSERT_LE(0, path_beneath.parent_fd); 415 + for (access = 1; access <= ACCESS_LAST; access <<= 1) { 416 + path_beneath.allowed_access = access; 417 + err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 418 + &path_beneath, 0); 419 + if ((access | ACCESS_FILE) == ACCESS_FILE) { 420 + ASSERT_EQ(0, err); 421 + } else { 422 + ASSERT_EQ(-1, err); 423 + ASSERT_EQ(EINVAL, errno); 424 + } 425 + } 426 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 427 + } 428 + 429 + static void add_path_beneath(struct __test_metadata *const _metadata, 430 + const int ruleset_fd, const __u64 allowed_access, 431 + const char *const path) 432 + { 433 + struct landlock_path_beneath_attr path_beneath = { 434 + .allowed_access = allowed_access, 435 + }; 436 + 437 + path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 438 + ASSERT_LE(0, path_beneath.parent_fd) { 439 + TH_LOG("Failed to open directory \"%s\": %s", path, 440 + strerror(errno)); 441 + } 442 + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 443 + &path_beneath, 0)) { 444 + TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 445 + strerror(errno)); 446 + } 447 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 448 + } 449 + 450 + struct rule { 451 + const char *path; 452 + __u64 access; 453 + }; 454 + 455 + #define ACCESS_RO ( \ 456 + LANDLOCK_ACCESS_FS_READ_FILE | \ 457 + LANDLOCK_ACCESS_FS_READ_DIR) 458 + 459 + #define ACCESS_RW ( \ 460 + ACCESS_RO | \ 461 + LANDLOCK_ACCESS_FS_WRITE_FILE) 462 + 463 + static int create_ruleset(struct __test_metadata *const _metadata, 464 + const __u64 handled_access_fs, const struct rule rules[]) 465 + { 466 + int ruleset_fd, i; 467 + struct landlock_ruleset_attr ruleset_attr = { 468 + .handled_access_fs = handled_access_fs, 469 + }; 470 + 471 + ASSERT_NE(NULL, rules) { 472 + TH_LOG("No rule list"); 473 + } 474 + ASSERT_NE(NULL, rules[0].path) { 475 + TH_LOG("Empty rule list"); 476 + } 477 + 478 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 479 + sizeof(ruleset_attr), 0); 480 + ASSERT_LE(0, ruleset_fd) { 481 + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 482 + } 483 + 484 + for (i = 0; rules[i].path; i++) { 485 + add_path_beneath(_metadata, ruleset_fd, rules[i].access, 486 + rules[i].path); 487 + } 488 + return ruleset_fd; 489 + } 490 + 491 + static void enforce_ruleset(struct __test_metadata *const _metadata, 492 + const int ruleset_fd) 493 + { 494 + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 495 + ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) { 496 + TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 497 + } 498 + } 499 + 500 + TEST_F_FORK(layout1, proc_nsfs) 501 + { 502 + const struct rule rules[] = { 503 + { 504 + .path = "/dev/null", 505 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 506 + LANDLOCK_ACCESS_FS_WRITE_FILE, 507 + }, 508 + {} 509 + }; 510 + struct landlock_path_beneath_attr path_beneath; 511 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access | 512 + LANDLOCK_ACCESS_FS_READ_DIR, rules); 513 + 514 + ASSERT_LE(0, ruleset_fd); 515 + ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 516 + 517 + enforce_ruleset(_metadata, ruleset_fd); 518 + 519 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 520 + ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 521 + ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 522 + ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 523 + 524 + ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 525 + ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 526 + ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 527 + /* 528 + * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 529 + * disconnected path. Such path cannot be identified and must then be 530 + * allowed. 531 + */ 532 + ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 533 + 534 + /* 535 + * Checks that it is not possible to add nsfs-like filesystem 536 + * references to a ruleset. 537 + */ 538 + path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 539 + LANDLOCK_ACCESS_FS_WRITE_FILE, 540 + path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 541 + ASSERT_LE(0, path_beneath.parent_fd); 542 + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 543 + &path_beneath, 0)); 544 + ASSERT_EQ(EBADFD, errno); 545 + ASSERT_EQ(0, close(path_beneath.parent_fd)); 546 + } 547 + 548 + TEST_F_FORK(layout1, unpriv) { 549 + const struct rule rules[] = { 550 + { 551 + .path = dir_s1d2, 552 + .access = ACCESS_RO, 553 + }, 554 + {} 555 + }; 556 + int ruleset_fd; 557 + 558 + drop_caps(_metadata); 559 + 560 + ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 561 + ASSERT_LE(0, ruleset_fd); 562 + ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 563 + ASSERT_EQ(EPERM, errno); 564 + 565 + /* enforce_ruleset() calls prctl(no_new_privs). */ 566 + enforce_ruleset(_metadata, ruleset_fd); 567 + ASSERT_EQ(0, close(ruleset_fd)); 568 + } 569 + 570 + TEST_F_FORK(layout1, effective_access) 571 + { 572 + const struct rule rules[] = { 573 + { 574 + .path = dir_s1d2, 575 + .access = ACCESS_RO, 576 + }, 577 + { 578 + .path = file1_s2d2, 579 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 580 + LANDLOCK_ACCESS_FS_WRITE_FILE, 581 + }, 582 + {} 583 + }; 584 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 585 + char buf; 586 + int reg_fd; 587 + 588 + ASSERT_LE(0, ruleset_fd); 589 + enforce_ruleset(_metadata, ruleset_fd); 590 + ASSERT_EQ(0, close(ruleset_fd)); 591 + 592 + /* Tests on a directory. */ 593 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 594 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 595 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 596 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 597 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 598 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 599 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 600 + 601 + /* Tests on a file. */ 602 + ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 603 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 604 + 605 + /* Checks effective read and write actions. */ 606 + reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 607 + ASSERT_LE(0, reg_fd); 608 + ASSERT_EQ(1, write(reg_fd, ".", 1)); 609 + ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 610 + ASSERT_EQ(1, read(reg_fd, &buf, 1)); 611 + ASSERT_EQ('.', buf); 612 + ASSERT_EQ(0, close(reg_fd)); 613 + 614 + /* Just in case, double-checks effective actions. */ 615 + reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 616 + ASSERT_LE(0, reg_fd); 617 + ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 618 + ASSERT_EQ(EBADF, errno); 619 + ASSERT_EQ(0, close(reg_fd)); 620 + } 621 + 622 + TEST_F_FORK(layout1, unhandled_access) 623 + { 624 + const struct rule rules[] = { 625 + { 626 + .path = dir_s1d2, 627 + .access = ACCESS_RO, 628 + }, 629 + {} 630 + }; 631 + /* Here, we only handle read accesses, not write accesses. */ 632 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 633 + 634 + ASSERT_LE(0, ruleset_fd); 635 + enforce_ruleset(_metadata, ruleset_fd); 636 + ASSERT_EQ(0, close(ruleset_fd)); 637 + 638 + /* 639 + * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 640 + * opening for write-only should be allowed, but not read-write. 641 + */ 642 + ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 643 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 644 + 645 + ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 646 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 647 + } 648 + 649 + TEST_F_FORK(layout1, ruleset_overlap) 650 + { 651 + const struct rule rules[] = { 652 + /* These rules should be ORed among them. */ 653 + { 654 + .path = dir_s1d2, 655 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 656 + LANDLOCK_ACCESS_FS_WRITE_FILE, 657 + }, 658 + { 659 + .path = dir_s1d2, 660 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 661 + LANDLOCK_ACCESS_FS_READ_DIR, 662 + }, 663 + {} 664 + }; 665 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 666 + 667 + ASSERT_LE(0, ruleset_fd); 668 + enforce_ruleset(_metadata, ruleset_fd); 669 + ASSERT_EQ(0, close(ruleset_fd)); 670 + 671 + /* Checks s1d1 hierarchy. */ 672 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 673 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 674 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 675 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 676 + 677 + /* Checks s1d2 hierarchy. */ 678 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 679 + ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 680 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 681 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 682 + 683 + /* Checks s1d3 hierarchy. */ 684 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 685 + ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 686 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 687 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 688 + } 689 + 690 + TEST_F_FORK(layout1, non_overlapping_accesses) 691 + { 692 + const struct rule layer1[] = { 693 + { 694 + .path = dir_s1d2, 695 + .access = LANDLOCK_ACCESS_FS_MAKE_REG, 696 + }, 697 + {} 698 + }; 699 + const struct rule layer2[] = { 700 + { 701 + .path = dir_s1d3, 702 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 703 + }, 704 + {} 705 + }; 706 + int ruleset_fd; 707 + 708 + ASSERT_EQ(0, unlink(file1_s1d1)); 709 + ASSERT_EQ(0, unlink(file1_s1d2)); 710 + 711 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 712 + layer1); 713 + ASSERT_LE(0, ruleset_fd); 714 + enforce_ruleset(_metadata, ruleset_fd); 715 + ASSERT_EQ(0, close(ruleset_fd)); 716 + 717 + ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 718 + ASSERT_EQ(EACCES, errno); 719 + ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 720 + ASSERT_EQ(0, unlink(file1_s1d2)); 721 + 722 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 723 + layer2); 724 + ASSERT_LE(0, ruleset_fd); 725 + enforce_ruleset(_metadata, ruleset_fd); 726 + ASSERT_EQ(0, close(ruleset_fd)); 727 + 728 + /* Unchanged accesses for file creation. */ 729 + ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 730 + ASSERT_EQ(EACCES, errno); 731 + ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 732 + 733 + /* Checks file removing. */ 734 + ASSERT_EQ(-1, unlink(file1_s1d2)); 735 + ASSERT_EQ(EACCES, errno); 736 + ASSERT_EQ(0, unlink(file1_s1d3)); 737 + } 738 + 739 + TEST_F_FORK(layout1, interleaved_masked_accesses) 740 + { 741 + /* 742 + * Checks overly restrictive rules: 743 + * layer 1: allows R s1d1/s1d2/s1d3/file1 744 + * layer 2: allows RW s1d1/s1d2/s1d3 745 + * allows W s1d1/s1d2 746 + * denies R s1d1/s1d2 747 + * layer 3: allows R s1d1 748 + * layer 4: allows R s1d1/s1d2 749 + * denies W s1d1/s1d2 750 + * layer 5: allows R s1d1/s1d2 751 + * layer 6: allows X ---- 752 + * layer 7: allows W s1d1/s1d2 753 + * denies R s1d1/s1d2 754 + */ 755 + const struct rule layer1_read[] = { 756 + /* Allows read access to file1_s1d3 with the first layer. */ 757 + { 758 + .path = file1_s1d3, 759 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 760 + }, 761 + {} 762 + }; 763 + /* First rule with write restrictions. */ 764 + const struct rule layer2_read_write[] = { 765 + /* Start by granting read-write access via its parent directory... */ 766 + { 767 + .path = dir_s1d3, 768 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 769 + LANDLOCK_ACCESS_FS_WRITE_FILE, 770 + }, 771 + /* ...but also denies read access via its grandparent directory. */ 772 + { 773 + .path = dir_s1d2, 774 + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 775 + }, 776 + {} 777 + }; 778 + const struct rule layer3_read[] = { 779 + /* Allows read access via its great-grandparent directory. */ 780 + { 781 + .path = dir_s1d1, 782 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 783 + }, 784 + {} 785 + }; 786 + const struct rule layer4_read_write[] = { 787 + /* 788 + * Try to confuse the deny access by denying write (but not 789 + * read) access via its grandparent directory. 790 + */ 791 + { 792 + .path = dir_s1d2, 793 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 794 + }, 795 + {} 796 + }; 797 + const struct rule layer5_read[] = { 798 + /* 799 + * Try to override layer2's deny read access by explicitly 800 + * allowing read access via file1_s1d3's grandparent. 801 + */ 802 + { 803 + .path = dir_s1d2, 804 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 805 + }, 806 + {} 807 + }; 808 + const struct rule layer6_execute[] = { 809 + /* 810 + * Restricts an unrelated file hierarchy with a new access 811 + * (non-overlapping) type. 812 + */ 813 + { 814 + .path = dir_s2d1, 815 + .access = LANDLOCK_ACCESS_FS_EXECUTE, 816 + }, 817 + {} 818 + }; 819 + const struct rule layer7_read_write[] = { 820 + /* 821 + * Finally, denies read access to file1_s1d3 via its 822 + * grandparent. 823 + */ 824 + { 825 + .path = dir_s1d2, 826 + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 827 + }, 828 + {} 829 + }; 830 + int ruleset_fd; 831 + 832 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 833 + layer1_read); 834 + ASSERT_LE(0, ruleset_fd); 835 + enforce_ruleset(_metadata, ruleset_fd); 836 + ASSERT_EQ(0, close(ruleset_fd)); 837 + 838 + /* Checks that read access is granted for file1_s1d3 with layer 1. */ 839 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 840 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 841 + ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 842 + 843 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE | 844 + LANDLOCK_ACCESS_FS_WRITE_FILE, layer2_read_write); 845 + ASSERT_LE(0, ruleset_fd); 846 + enforce_ruleset(_metadata, ruleset_fd); 847 + ASSERT_EQ(0, close(ruleset_fd)); 848 + 849 + /* Checks that previous access rights are unchanged with layer 2. */ 850 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 851 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 852 + ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 853 + 854 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 855 + layer3_read); 856 + ASSERT_LE(0, ruleset_fd); 857 + enforce_ruleset(_metadata, ruleset_fd); 858 + ASSERT_EQ(0, close(ruleset_fd)); 859 + 860 + /* Checks that previous access rights are unchanged with layer 3. */ 861 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 862 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 863 + ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 864 + 865 + /* This time, denies write access for the file hierarchy. */ 866 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE | 867 + LANDLOCK_ACCESS_FS_WRITE_FILE, layer4_read_write); 868 + ASSERT_LE(0, ruleset_fd); 869 + enforce_ruleset(_metadata, ruleset_fd); 870 + ASSERT_EQ(0, close(ruleset_fd)); 871 + 872 + /* 873 + * Checks that the only change with layer 4 is that write access is 874 + * denied. 875 + */ 876 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 877 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 878 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 879 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 880 + 881 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 882 + layer5_read); 883 + ASSERT_LE(0, ruleset_fd); 884 + enforce_ruleset(_metadata, ruleset_fd); 885 + ASSERT_EQ(0, close(ruleset_fd)); 886 + 887 + /* Checks that previous access rights are unchanged with layer 5. */ 888 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 889 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 890 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 891 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 892 + 893 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 894 + layer6_execute); 895 + ASSERT_LE(0, ruleset_fd); 896 + enforce_ruleset(_metadata, ruleset_fd); 897 + ASSERT_EQ(0, close(ruleset_fd)); 898 + 899 + /* Checks that previous access rights are unchanged with layer 6. */ 900 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 901 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 902 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 903 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 904 + 905 + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE | 906 + LANDLOCK_ACCESS_FS_WRITE_FILE, layer7_read_write); 907 + ASSERT_LE(0, ruleset_fd); 908 + enforce_ruleset(_metadata, ruleset_fd); 909 + ASSERT_EQ(0, close(ruleset_fd)); 910 + 911 + /* Checks read access is now denied with layer 7. */ 912 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 913 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 914 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 915 + ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 916 + } 917 + 918 + TEST_F_FORK(layout1, inherit_subset) 919 + { 920 + const struct rule rules[] = { 921 + { 922 + .path = dir_s1d2, 923 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 924 + LANDLOCK_ACCESS_FS_READ_DIR, 925 + }, 926 + {} 927 + }; 928 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 929 + 930 + ASSERT_LE(0, ruleset_fd); 931 + enforce_ruleset(_metadata, ruleset_fd); 932 + 933 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 934 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 935 + 936 + /* Write access is forbidden. */ 937 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 938 + /* Readdir access is allowed. */ 939 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 940 + 941 + /* Write access is forbidden. */ 942 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 943 + /* Readdir access is allowed. */ 944 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 945 + 946 + /* 947 + * Tests shared rule extension: the following rules should not grant 948 + * any new access, only remove some. Once enforced, these rules are 949 + * ANDed with the previous ones. 950 + */ 951 + add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 952 + dir_s1d2); 953 + /* 954 + * According to ruleset_fd, dir_s1d2 should now have the 955 + * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 956 + * access rights (even if this directory is opened a second time). 957 + * However, when enforcing this updated ruleset, the ruleset tied to 958 + * the current process (i.e. its domain) will still only have the 959 + * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 960 + * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 961 + * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 962 + * be a privilege escalation. 963 + */ 964 + enforce_ruleset(_metadata, ruleset_fd); 965 + 966 + /* Same tests and results as above. */ 967 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 968 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 969 + 970 + /* It is still forbidden to write in file1_s1d2. */ 971 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 972 + /* Readdir access is still allowed. */ 973 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 974 + 975 + /* It is still forbidden to write in file1_s1d3. */ 976 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 977 + /* Readdir access is still allowed. */ 978 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 979 + 980 + /* 981 + * Try to get more privileges by adding new access rights to the parent 982 + * directory: dir_s1d1. 983 + */ 984 + add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 985 + enforce_ruleset(_metadata, ruleset_fd); 986 + 987 + /* Same tests and results as above. */ 988 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 989 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 990 + 991 + /* It is still forbidden to write in file1_s1d2. */ 992 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 993 + /* Readdir access is still allowed. */ 994 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 995 + 996 + /* It is still forbidden to write in file1_s1d3. */ 997 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 998 + /* Readdir access is still allowed. */ 999 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1000 + 1001 + /* 1002 + * Now, dir_s1d3 get a new rule tied to it, only allowing 1003 + * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1004 + * that there was no rule tied to it before. 1005 + */ 1006 + add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1007 + dir_s1d3); 1008 + enforce_ruleset(_metadata, ruleset_fd); 1009 + ASSERT_EQ(0, close(ruleset_fd)); 1010 + 1011 + /* 1012 + * Same tests and results as above, except for open(dir_s1d3) which is 1013 + * now denied because the new rule mask the rule previously inherited 1014 + * from dir_s1d2. 1015 + */ 1016 + 1017 + /* Same tests and results as above. */ 1018 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1019 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1020 + 1021 + /* It is still forbidden to write in file1_s1d2. */ 1022 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1023 + /* Readdir access is still allowed. */ 1024 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1025 + 1026 + /* It is still forbidden to write in file1_s1d3. */ 1027 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1028 + /* 1029 + * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1030 + * the same layer. 1031 + */ 1032 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1033 + } 1034 + 1035 + TEST_F_FORK(layout1, inherit_superset) 1036 + { 1037 + const struct rule rules[] = { 1038 + { 1039 + .path = dir_s1d3, 1040 + .access = ACCESS_RO, 1041 + }, 1042 + {} 1043 + }; 1044 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1045 + 1046 + ASSERT_LE(0, ruleset_fd); 1047 + enforce_ruleset(_metadata, ruleset_fd); 1048 + 1049 + /* Readdir access is denied for dir_s1d2. */ 1050 + ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1051 + /* Readdir access is allowed for dir_s1d3. */ 1052 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1053 + /* File access is allowed for file1_s1d3. */ 1054 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1055 + 1056 + /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1057 + add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_READ_FILE | 1058 + LANDLOCK_ACCESS_FS_READ_DIR, dir_s1d2); 1059 + enforce_ruleset(_metadata, ruleset_fd); 1060 + ASSERT_EQ(0, close(ruleset_fd)); 1061 + 1062 + /* Readdir access is still denied for dir_s1d2. */ 1063 + ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1064 + /* Readdir access is still allowed for dir_s1d3. */ 1065 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1066 + /* File access is still allowed for file1_s1d3. */ 1067 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1068 + } 1069 + 1070 + TEST_F_FORK(layout1, max_layers) 1071 + { 1072 + int i, err; 1073 + const struct rule rules[] = { 1074 + { 1075 + .path = dir_s1d2, 1076 + .access = ACCESS_RO, 1077 + }, 1078 + {} 1079 + }; 1080 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1081 + 1082 + ASSERT_LE(0, ruleset_fd); 1083 + for (i = 0; i < 64; i++) 1084 + enforce_ruleset(_metadata, ruleset_fd); 1085 + 1086 + for (i = 0; i < 2; i++) { 1087 + err = landlock_restrict_self(ruleset_fd, 0); 1088 + ASSERT_EQ(-1, err); 1089 + ASSERT_EQ(E2BIG, errno); 1090 + } 1091 + ASSERT_EQ(0, close(ruleset_fd)); 1092 + } 1093 + 1094 + TEST_F_FORK(layout1, empty_or_same_ruleset) 1095 + { 1096 + struct landlock_ruleset_attr ruleset_attr = {}; 1097 + int ruleset_fd; 1098 + 1099 + /* Tests empty handled_access_fs. */ 1100 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 1101 + sizeof(ruleset_attr), 0); 1102 + ASSERT_LE(-1, ruleset_fd); 1103 + ASSERT_EQ(ENOMSG, errno); 1104 + 1105 + /* Enforces policy which deny read access to all files. */ 1106 + ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1107 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 1108 + sizeof(ruleset_attr), 0); 1109 + ASSERT_LE(0, ruleset_fd); 1110 + enforce_ruleset(_metadata, ruleset_fd); 1111 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1112 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1113 + 1114 + /* Nests a policy which deny read access to all directories. */ 1115 + ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1116 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 1117 + sizeof(ruleset_attr), 0); 1118 + ASSERT_LE(0, ruleset_fd); 1119 + enforce_ruleset(_metadata, ruleset_fd); 1120 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1121 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1122 + 1123 + /* Enforces a second time with the same ruleset. */ 1124 + enforce_ruleset(_metadata, ruleset_fd); 1125 + ASSERT_EQ(0, close(ruleset_fd)); 1126 + } 1127 + 1128 + TEST_F_FORK(layout1, rule_on_mountpoint) 1129 + { 1130 + const struct rule rules[] = { 1131 + { 1132 + .path = dir_s1d1, 1133 + .access = ACCESS_RO, 1134 + }, 1135 + { 1136 + /* dir_s3d2 is a mount point. */ 1137 + .path = dir_s3d2, 1138 + .access = ACCESS_RO, 1139 + }, 1140 + {} 1141 + }; 1142 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1143 + 1144 + ASSERT_LE(0, ruleset_fd); 1145 + enforce_ruleset(_metadata, ruleset_fd); 1146 + ASSERT_EQ(0, close(ruleset_fd)); 1147 + 1148 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1149 + 1150 + ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1151 + 1152 + ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1153 + ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1154 + ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1155 + } 1156 + 1157 + TEST_F_FORK(layout1, rule_over_mountpoint) 1158 + { 1159 + const struct rule rules[] = { 1160 + { 1161 + .path = dir_s1d1, 1162 + .access = ACCESS_RO, 1163 + }, 1164 + { 1165 + /* dir_s3d2 is a mount point. */ 1166 + .path = dir_s3d1, 1167 + .access = ACCESS_RO, 1168 + }, 1169 + {} 1170 + }; 1171 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1172 + 1173 + ASSERT_LE(0, ruleset_fd); 1174 + enforce_ruleset(_metadata, ruleset_fd); 1175 + ASSERT_EQ(0, close(ruleset_fd)); 1176 + 1177 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1178 + 1179 + ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1180 + 1181 + ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1182 + ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1183 + ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1184 + } 1185 + 1186 + /* 1187 + * This test verifies that we can apply a landlock rule on the root directory 1188 + * (which might require special handling). 1189 + */ 1190 + TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1191 + { 1192 + struct rule rules[] = { 1193 + { 1194 + .path = "/", 1195 + .access = ACCESS_RO, 1196 + }, 1197 + {} 1198 + }; 1199 + int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1200 + 1201 + ASSERT_LE(0, ruleset_fd); 1202 + enforce_ruleset(_metadata, ruleset_fd); 1203 + ASSERT_EQ(0, close(ruleset_fd)); 1204 + 1205 + /* Checks allowed access. */ 1206 + ASSERT_EQ(0, test_open("/", O_RDONLY)); 1207 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1208 + 1209 + rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1210 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1211 + ASSERT_LE(0, ruleset_fd); 1212 + enforce_ruleset(_metadata, ruleset_fd); 1213 + ASSERT_EQ(0, close(ruleset_fd)); 1214 + 1215 + /* Checks denied access (on a directory). */ 1216 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1217 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1218 + } 1219 + 1220 + TEST_F_FORK(layout1, rule_over_root_deny) 1221 + { 1222 + const struct rule rules[] = { 1223 + { 1224 + .path = "/", 1225 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 1226 + }, 1227 + {} 1228 + }; 1229 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1230 + 1231 + ASSERT_LE(0, ruleset_fd); 1232 + enforce_ruleset(_metadata, ruleset_fd); 1233 + ASSERT_EQ(0, close(ruleset_fd)); 1234 + 1235 + /* Checks denied access (on a directory). */ 1236 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1237 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1238 + } 1239 + 1240 + TEST_F_FORK(layout1, rule_inside_mount_ns) 1241 + { 1242 + const struct rule rules[] = { 1243 + { 1244 + .path = "s3d3", 1245 + .access = ACCESS_RO, 1246 + }, 1247 + {} 1248 + }; 1249 + int ruleset_fd; 1250 + 1251 + set_cap(_metadata, CAP_SYS_ADMIN); 1252 + ASSERT_EQ(0, syscall(SYS_pivot_root, dir_s3d2, dir_s3d3)) { 1253 + TH_LOG("Failed to pivot root: %s", strerror(errno)); 1254 + }; 1255 + ASSERT_EQ(0, chdir("/")); 1256 + clear_cap(_metadata, CAP_SYS_ADMIN); 1257 + 1258 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1259 + ASSERT_LE(0, ruleset_fd); 1260 + enforce_ruleset(_metadata, ruleset_fd); 1261 + ASSERT_EQ(0, close(ruleset_fd)); 1262 + 1263 + ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1264 + ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1265 + } 1266 + 1267 + TEST_F_FORK(layout1, mount_and_pivot) 1268 + { 1269 + const struct rule rules[] = { 1270 + { 1271 + .path = dir_s3d2, 1272 + .access = ACCESS_RO, 1273 + }, 1274 + {} 1275 + }; 1276 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1277 + 1278 + ASSERT_LE(0, ruleset_fd); 1279 + enforce_ruleset(_metadata, ruleset_fd); 1280 + ASSERT_EQ(0, close(ruleset_fd)); 1281 + 1282 + set_cap(_metadata, CAP_SYS_ADMIN); 1283 + ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1284 + ASSERT_EQ(EPERM, errno); 1285 + ASSERT_EQ(-1, syscall(SYS_pivot_root, dir_s3d2, dir_s3d3)); 1286 + ASSERT_EQ(EPERM, errno); 1287 + clear_cap(_metadata, CAP_SYS_ADMIN); 1288 + } 1289 + 1290 + TEST_F_FORK(layout1, move_mount) 1291 + { 1292 + const struct rule rules[] = { 1293 + { 1294 + .path = dir_s3d2, 1295 + .access = ACCESS_RO, 1296 + }, 1297 + {} 1298 + }; 1299 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1300 + 1301 + ASSERT_LE(0, ruleset_fd); 1302 + 1303 + set_cap(_metadata, CAP_SYS_ADMIN); 1304 + ASSERT_EQ(0, syscall(SYS_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1305 + dir_s1d2, 0)) { 1306 + TH_LOG("Failed to move mount: %s", strerror(errno)); 1307 + } 1308 + 1309 + ASSERT_EQ(0, syscall(SYS_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1310 + dir_s3d2, 0)); 1311 + clear_cap(_metadata, CAP_SYS_ADMIN); 1312 + 1313 + enforce_ruleset(_metadata, ruleset_fd); 1314 + ASSERT_EQ(0, close(ruleset_fd)); 1315 + 1316 + set_cap(_metadata, CAP_SYS_ADMIN); 1317 + ASSERT_EQ(-1, syscall(SYS_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1318 + dir_s1d2, 0)); 1319 + ASSERT_EQ(EPERM, errno); 1320 + clear_cap(_metadata, CAP_SYS_ADMIN); 1321 + } 1322 + 1323 + TEST_F_FORK(layout1, release_inodes) 1324 + { 1325 + const struct rule rules[] = { 1326 + { 1327 + .path = dir_s1d1, 1328 + .access = ACCESS_RO, 1329 + }, 1330 + { 1331 + .path = dir_s3d2, 1332 + .access = ACCESS_RO, 1333 + }, 1334 + { 1335 + .path = dir_s3d3, 1336 + .access = ACCESS_RO, 1337 + }, 1338 + {} 1339 + }; 1340 + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1341 + 1342 + ASSERT_LE(0, ruleset_fd); 1343 + /* Unmount a file hierarchy while it is being used by a ruleset. */ 1344 + set_cap(_metadata, CAP_SYS_ADMIN); 1345 + ASSERT_EQ(0, umount(dir_s3d2)); 1346 + clear_cap(_metadata, CAP_SYS_ADMIN); 1347 + 1348 + enforce_ruleset(_metadata, ruleset_fd); 1349 + ASSERT_EQ(0, close(ruleset_fd)); 1350 + 1351 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1352 + ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1353 + /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1354 + ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1355 + } 1356 + 1357 + enum relative_access { 1358 + REL_OPEN, 1359 + REL_CHDIR, 1360 + REL_CHROOT_ONLY, 1361 + REL_CHROOT_CHDIR, 1362 + }; 1363 + 1364 + static void test_relative_path(struct __test_metadata *const _metadata, 1365 + const enum relative_access rel) 1366 + { 1367 + /* 1368 + * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1369 + * is not a disconnected root directory). 1370 + */ 1371 + const struct rule layer1_base[] = { 1372 + { 1373 + .path = TMP_DIR, 1374 + .access = ACCESS_RO, 1375 + }, 1376 + {} 1377 + }; 1378 + const struct rule layer2_subs[] = { 1379 + { 1380 + .path = dir_s1d2, 1381 + .access = ACCESS_RO, 1382 + }, 1383 + { 1384 + .path = dir_s2d2, 1385 + .access = ACCESS_RO, 1386 + }, 1387 + {} 1388 + }; 1389 + int dirfd, ruleset_fd; 1390 + 1391 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1392 + ASSERT_LE(0, ruleset_fd); 1393 + enforce_ruleset(_metadata, ruleset_fd); 1394 + ASSERT_EQ(0, close(ruleset_fd)); 1395 + 1396 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1397 + 1398 + ASSERT_LE(0, ruleset_fd); 1399 + switch (rel) { 1400 + case REL_OPEN: 1401 + case REL_CHDIR: 1402 + break; 1403 + case REL_CHROOT_ONLY: 1404 + ASSERT_EQ(0, chdir(dir_s2d2)); 1405 + break; 1406 + case REL_CHROOT_CHDIR: 1407 + ASSERT_EQ(0, chdir(dir_s1d2)); 1408 + break; 1409 + default: 1410 + ASSERT_TRUE(false); 1411 + return; 1412 + } 1413 + 1414 + set_cap(_metadata, CAP_SYS_CHROOT); 1415 + enforce_ruleset(_metadata, ruleset_fd); 1416 + 1417 + switch (rel) { 1418 + case REL_OPEN: 1419 + dirfd = open(dir_s1d2, O_DIRECTORY); 1420 + ASSERT_LE(0, dirfd); 1421 + break; 1422 + case REL_CHDIR: 1423 + ASSERT_EQ(0, chdir(dir_s1d2)); 1424 + dirfd = AT_FDCWD; 1425 + break; 1426 + case REL_CHROOT_ONLY: 1427 + /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1428 + ASSERT_EQ(0, chroot("../../s1d1/s1d2")) { 1429 + TH_LOG("Failed to chroot: %s", strerror(errno)); 1430 + } 1431 + dirfd = AT_FDCWD; 1432 + break; 1433 + case REL_CHROOT_CHDIR: 1434 + /* Do chroot into dir_s1d2. */ 1435 + ASSERT_EQ(0, chroot(".")) { 1436 + TH_LOG("Failed to chroot: %s", strerror(errno)); 1437 + } 1438 + dirfd = AT_FDCWD; 1439 + break; 1440 + } 1441 + 1442 + ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1443 + test_open_rel(dirfd, "..", O_RDONLY)); 1444 + ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1445 + 1446 + if (rel == REL_CHROOT_ONLY) { 1447 + /* The current directory is dir_s2d2. */ 1448 + ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1449 + } else { 1450 + /* The current directory is dir_s1d2. */ 1451 + ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1452 + } 1453 + 1454 + if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1455 + /* Checks the root dir_s1d2. */ 1456 + ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1457 + ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1458 + ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1459 + ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1460 + } 1461 + 1462 + if (rel != REL_CHROOT_CHDIR) { 1463 + ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1464 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1465 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", O_RDONLY)); 1466 + 1467 + ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1468 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1469 + ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", O_RDONLY)); 1470 + } 1471 + 1472 + if (rel == REL_OPEN) 1473 + ASSERT_EQ(0, close(dirfd)); 1474 + ASSERT_EQ(0, close(ruleset_fd)); 1475 + } 1476 + 1477 + TEST_F_FORK(layout1, relative_open) 1478 + { 1479 + test_relative_path(_metadata, REL_OPEN); 1480 + } 1481 + 1482 + TEST_F_FORK(layout1, relative_chdir) 1483 + { 1484 + test_relative_path(_metadata, REL_CHDIR); 1485 + } 1486 + 1487 + TEST_F_FORK(layout1, relative_chroot_only) 1488 + { 1489 + test_relative_path(_metadata, REL_CHROOT_ONLY); 1490 + } 1491 + 1492 + TEST_F_FORK(layout1, relative_chroot_chdir) 1493 + { 1494 + test_relative_path(_metadata, REL_CHROOT_CHDIR); 1495 + } 1496 + 1497 + static void copy_binary(struct __test_metadata *const _metadata, 1498 + const char *const dst_path) 1499 + { 1500 + int dst_fd, src_fd; 1501 + struct stat statbuf; 1502 + 1503 + dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1504 + ASSERT_LE(0, dst_fd) { 1505 + TH_LOG("Failed to open \"%s\": %s", dst_path, 1506 + strerror(errno)); 1507 + } 1508 + src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1509 + ASSERT_LE(0, src_fd) { 1510 + TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1511 + strerror(errno)); 1512 + } 1513 + ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1514 + ASSERT_EQ(statbuf.st_size, sendfile(dst_fd, src_fd, 0, 1515 + statbuf.st_size)); 1516 + ASSERT_EQ(0, close(src_fd)); 1517 + ASSERT_EQ(0, close(dst_fd)); 1518 + } 1519 + 1520 + static void test_execute(struct __test_metadata *const _metadata, 1521 + const int err, const char *const path) 1522 + { 1523 + int status; 1524 + char *const argv[] = {(char *)path, NULL}; 1525 + const pid_t child = fork(); 1526 + 1527 + ASSERT_LE(0, child); 1528 + if (child == 0) { 1529 + ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) { 1530 + TH_LOG("Failed to execute \"%s\": %s", path, 1531 + strerror(errno)); 1532 + }; 1533 + ASSERT_EQ(err, errno); 1534 + _exit(_metadata->passed ? 2 : 1); 1535 + return; 1536 + } 1537 + ASSERT_EQ(child, waitpid(child, &status, 0)); 1538 + ASSERT_EQ(1, WIFEXITED(status)); 1539 + ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) { 1540 + TH_LOG("Unexpected return code for \"%s\": %s", path, 1541 + strerror(errno)); 1542 + }; 1543 + } 1544 + 1545 + TEST_F_FORK(layout1, execute) 1546 + { 1547 + const struct rule rules[] = { 1548 + { 1549 + .path = dir_s1d2, 1550 + .access = LANDLOCK_ACCESS_FS_EXECUTE, 1551 + }, 1552 + {} 1553 + }; 1554 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1555 + rules); 1556 + 1557 + ASSERT_LE(0, ruleset_fd); 1558 + copy_binary(_metadata, file1_s1d1); 1559 + copy_binary(_metadata, file1_s1d2); 1560 + copy_binary(_metadata, file1_s1d3); 1561 + 1562 + enforce_ruleset(_metadata, ruleset_fd); 1563 + ASSERT_EQ(0, close(ruleset_fd)); 1564 + 1565 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1566 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1567 + test_execute(_metadata, EACCES, file1_s1d1); 1568 + 1569 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1570 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1571 + test_execute(_metadata, 0, file1_s1d2); 1572 + 1573 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1574 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1575 + test_execute(_metadata, 0, file1_s1d3); 1576 + } 1577 + 1578 + TEST_F_FORK(layout1, link) 1579 + { 1580 + const struct rule rules[] = { 1581 + { 1582 + .path = dir_s1d2, 1583 + .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1584 + }, 1585 + {} 1586 + }; 1587 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1588 + rules); 1589 + 1590 + ASSERT_LE(0, ruleset_fd); 1591 + 1592 + ASSERT_EQ(0, unlink(file1_s1d1)); 1593 + ASSERT_EQ(0, unlink(file1_s1d2)); 1594 + ASSERT_EQ(0, unlink(file1_s1d3)); 1595 + 1596 + enforce_ruleset(_metadata, ruleset_fd); 1597 + ASSERT_EQ(0, close(ruleset_fd)); 1598 + 1599 + ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1600 + ASSERT_EQ(EACCES, errno); 1601 + /* Denies linking because of reparenting. */ 1602 + ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1603 + ASSERT_EQ(EXDEV, errno); 1604 + ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1605 + ASSERT_EQ(EXDEV, errno); 1606 + 1607 + ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1608 + ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 1609 + } 1610 + 1611 + TEST_F_FORK(layout1, rename_file) 1612 + { 1613 + const struct rule rules[] = { 1614 + { 1615 + .path = dir_s1d3, 1616 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1617 + }, 1618 + { 1619 + .path = dir_s2d2, 1620 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1621 + }, 1622 + {} 1623 + }; 1624 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1625 + rules); 1626 + 1627 + ASSERT_LE(0, ruleset_fd); 1628 + 1629 + ASSERT_EQ(0, unlink(file1_s1d1)); 1630 + ASSERT_EQ(0, unlink(file1_s1d2)); 1631 + 1632 + enforce_ruleset(_metadata, ruleset_fd); 1633 + ASSERT_EQ(0, close(ruleset_fd)); 1634 + 1635 + /* 1636 + * Tries to replace a file, from a directory that allows file removal, 1637 + * but to a different directory (which also allows file removal). 1638 + */ 1639 + ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1640 + ASSERT_EQ(EXDEV, errno); 1641 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 1642 + RENAME_EXCHANGE)); 1643 + ASSERT_EQ(EXDEV, errno); 1644 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 1645 + RENAME_EXCHANGE)); 1646 + ASSERT_EQ(EXDEV, errno); 1647 + 1648 + /* 1649 + * Tries to replace a file, from a directory that denies file removal, 1650 + * to a different directory (which allows file removal). 1651 + */ 1652 + ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1653 + ASSERT_EQ(EXDEV, errno); 1654 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 1655 + RENAME_EXCHANGE)); 1656 + ASSERT_EQ(EXDEV, errno); 1657 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 1658 + RENAME_EXCHANGE)); 1659 + ASSERT_EQ(EXDEV, errno); 1660 + 1661 + /* Exchanges files and directories that partially allow removal. */ 1662 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 1663 + RENAME_EXCHANGE)); 1664 + ASSERT_EQ(EACCES, errno); 1665 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 1666 + RENAME_EXCHANGE)); 1667 + ASSERT_EQ(EACCES, errno); 1668 + 1669 + /* Renames files with different parents. */ 1670 + ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 1671 + ASSERT_EQ(EXDEV, errno); 1672 + ASSERT_EQ(0, unlink(file1_s1d3)); 1673 + ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1674 + ASSERT_EQ(EXDEV, errno); 1675 + 1676 + /* Exchanges and renames files with same parent. */ 1677 + ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 1678 + RENAME_EXCHANGE)); 1679 + ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 1680 + 1681 + /* Exchanges files and directories with same parent, twice. */ 1682 + ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1683 + RENAME_EXCHANGE)); 1684 + ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1685 + RENAME_EXCHANGE)); 1686 + } 1687 + 1688 + TEST_F_FORK(layout1, rename_dir) 1689 + { 1690 + const struct rule rules[] = { 1691 + { 1692 + .path = dir_s1d2, 1693 + .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1694 + }, 1695 + { 1696 + .path = dir_s2d1, 1697 + .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1698 + }, 1699 + {} 1700 + }; 1701 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1702 + rules); 1703 + 1704 + ASSERT_LE(0, ruleset_fd); 1705 + 1706 + /* Empties dir_s1d3 to allow renaming. */ 1707 + ASSERT_EQ(0, unlink(file1_s1d3)); 1708 + ASSERT_EQ(0, unlink(file2_s1d3)); 1709 + 1710 + enforce_ruleset(_metadata, ruleset_fd); 1711 + ASSERT_EQ(0, close(ruleset_fd)); 1712 + 1713 + /* Exchanges and renames directory to a different parent. */ 1714 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 1715 + RENAME_EXCHANGE)); 1716 + ASSERT_EQ(EXDEV, errno); 1717 + ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 1718 + ASSERT_EQ(EXDEV, errno); 1719 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 1720 + RENAME_EXCHANGE)); 1721 + ASSERT_EQ(EXDEV, errno); 1722 + 1723 + /* 1724 + * Exchanges directory to the same parent, which doesn't allow 1725 + * directory removal. 1726 + */ 1727 + ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 1728 + RENAME_EXCHANGE)); 1729 + ASSERT_EQ(EACCES, errno); 1730 + ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 1731 + RENAME_EXCHANGE)); 1732 + ASSERT_EQ(EACCES, errno); 1733 + 1734 + /* 1735 + * Exchanges and renames directory to the same parent, which allows 1736 + * directory removal. 1737 + */ 1738 + ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 1739 + RENAME_EXCHANGE)); 1740 + ASSERT_EQ(0, unlink(dir_s1d3)); 1741 + ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 1742 + ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 1743 + ASSERT_EQ(0, rmdir(dir_s1d3)); 1744 + } 1745 + 1746 + TEST_F_FORK(layout1, remove_dir) 1747 + { 1748 + const struct rule rules[] = { 1749 + { 1750 + .path = dir_s1d2, 1751 + .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1752 + }, 1753 + {} 1754 + }; 1755 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1756 + rules); 1757 + 1758 + ASSERT_LE(0, ruleset_fd); 1759 + 1760 + ASSERT_EQ(0, unlink(file1_s1d1)); 1761 + ASSERT_EQ(0, unlink(file1_s1d2)); 1762 + ASSERT_EQ(0, unlink(file1_s1d3)); 1763 + ASSERT_EQ(0, unlink(file2_s1d3)); 1764 + 1765 + enforce_ruleset(_metadata, ruleset_fd); 1766 + ASSERT_EQ(0, close(ruleset_fd)); 1767 + 1768 + ASSERT_EQ(0, rmdir(dir_s1d3)); 1769 + ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 1770 + ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 1771 + 1772 + /* dir_s1d2 itself cannot be removed. */ 1773 + ASSERT_EQ(-1, rmdir(dir_s1d2)); 1774 + ASSERT_EQ(EACCES, errno); 1775 + ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 1776 + ASSERT_EQ(EACCES, errno); 1777 + ASSERT_EQ(-1, rmdir(dir_s1d1)); 1778 + ASSERT_EQ(EACCES, errno); 1779 + ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 1780 + ASSERT_EQ(EACCES, errno); 1781 + } 1782 + 1783 + TEST_F_FORK(layout1, remove_file) 1784 + { 1785 + const struct rule rules[] = { 1786 + { 1787 + .path = dir_s1d2, 1788 + .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1789 + }, 1790 + {} 1791 + }; 1792 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1793 + rules); 1794 + 1795 + ASSERT_LE(0, ruleset_fd); 1796 + enforce_ruleset(_metadata, ruleset_fd); 1797 + ASSERT_EQ(0, close(ruleset_fd)); 1798 + 1799 + ASSERT_EQ(-1, unlink(file1_s1d1)); 1800 + ASSERT_EQ(EACCES, errno); 1801 + ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 1802 + ASSERT_EQ(EACCES, errno); 1803 + ASSERT_EQ(0, unlink(file1_s1d2)); 1804 + ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 1805 + } 1806 + 1807 + static void test_make_file(struct __test_metadata *const _metadata, 1808 + const __u64 access, const mode_t mode, const dev_t dev) 1809 + { 1810 + const struct rule rules[] = { 1811 + { 1812 + .path = dir_s1d2, 1813 + .access = access, 1814 + }, 1815 + {} 1816 + }; 1817 + const int ruleset_fd = create_ruleset(_metadata, access, rules); 1818 + 1819 + ASSERT_LE(0, ruleset_fd); 1820 + 1821 + ASSERT_EQ(0, unlink(file1_s1d1)); 1822 + ASSERT_EQ(0, unlink(file2_s1d1)); 1823 + ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) { 1824 + TH_LOG("Failed to make file \"%s\": %s", 1825 + file2_s1d1, strerror(errno)); 1826 + }; 1827 + 1828 + ASSERT_EQ(0, unlink(file1_s1d2)); 1829 + ASSERT_EQ(0, unlink(file2_s1d2)); 1830 + 1831 + ASSERT_EQ(0, unlink(file1_s1d3)); 1832 + ASSERT_EQ(0, unlink(file2_s1d3)); 1833 + 1834 + enforce_ruleset(_metadata, ruleset_fd); 1835 + ASSERT_EQ(0, close(ruleset_fd)); 1836 + 1837 + ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 1838 + ASSERT_EQ(EACCES, errno); 1839 + ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1840 + ASSERT_EQ(EACCES, errno); 1841 + ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 1842 + ASSERT_EQ(EACCES, errno); 1843 + 1844 + ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) { 1845 + TH_LOG("Failed to make file \"%s\": %s", 1846 + file1_s1d2, strerror(errno)); 1847 + }; 1848 + ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 1849 + ASSERT_EQ(0, unlink(file2_s1d2)); 1850 + ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 1851 + 1852 + ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 1853 + ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1854 + ASSERT_EQ(0, unlink(file2_s1d3)); 1855 + ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 1856 + } 1857 + 1858 + TEST_F_FORK(layout1, make_char) 1859 + { 1860 + /* Creates a /dev/null device. */ 1861 + set_cap(_metadata, CAP_MKNOD); 1862 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 1863 + makedev(1, 3)); 1864 + } 1865 + 1866 + TEST_F_FORK(layout1, make_block) 1867 + { 1868 + /* Creates a /dev/loop0 device. */ 1869 + set_cap(_metadata, CAP_MKNOD); 1870 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 1871 + makedev(7, 0)); 1872 + } 1873 + 1874 + TEST_F_FORK(layout1, make_reg_1) 1875 + { 1876 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 1877 + } 1878 + 1879 + TEST_F_FORK(layout1, make_reg_2) 1880 + { 1881 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 1882 + } 1883 + 1884 + TEST_F_FORK(layout1, make_sock) 1885 + { 1886 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 1887 + } 1888 + 1889 + TEST_F_FORK(layout1, make_fifo) 1890 + { 1891 + test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 1892 + } 1893 + 1894 + TEST_F_FORK(layout1, make_sym) 1895 + { 1896 + const struct rule rules[] = { 1897 + { 1898 + .path = dir_s1d2, 1899 + .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 1900 + }, 1901 + {} 1902 + }; 1903 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1904 + rules); 1905 + 1906 + ASSERT_LE(0, ruleset_fd); 1907 + 1908 + ASSERT_EQ(0, unlink(file1_s1d1)); 1909 + ASSERT_EQ(0, unlink(file2_s1d1)); 1910 + ASSERT_EQ(0, symlink("none", file2_s1d1)); 1911 + 1912 + ASSERT_EQ(0, unlink(file1_s1d2)); 1913 + ASSERT_EQ(0, unlink(file2_s1d2)); 1914 + 1915 + ASSERT_EQ(0, unlink(file1_s1d3)); 1916 + ASSERT_EQ(0, unlink(file2_s1d3)); 1917 + 1918 + enforce_ruleset(_metadata, ruleset_fd); 1919 + ASSERT_EQ(0, close(ruleset_fd)); 1920 + 1921 + ASSERT_EQ(-1, symlink("none", file1_s1d1)); 1922 + ASSERT_EQ(EACCES, errno); 1923 + ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1924 + ASSERT_EQ(EACCES, errno); 1925 + ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 1926 + ASSERT_EQ(EACCES, errno); 1927 + 1928 + ASSERT_EQ(0, symlink("none", file1_s1d2)); 1929 + ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 1930 + ASSERT_EQ(0, unlink(file2_s1d2)); 1931 + ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 1932 + 1933 + ASSERT_EQ(0, symlink("none", file1_s1d3)); 1934 + ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1935 + ASSERT_EQ(0, unlink(file2_s1d3)); 1936 + ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 1937 + } 1938 + 1939 + TEST_F_FORK(layout1, make_dir) 1940 + { 1941 + const struct rule rules[] = { 1942 + { 1943 + .path = dir_s1d2, 1944 + .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 1945 + }, 1946 + {} 1947 + }; 1948 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 1949 + rules); 1950 + 1951 + ASSERT_LE(0, ruleset_fd); 1952 + 1953 + ASSERT_EQ(0, unlink(file1_s1d1)); 1954 + ASSERT_EQ(0, unlink(file1_s1d2)); 1955 + ASSERT_EQ(0, unlink(file1_s1d3)); 1956 + 1957 + enforce_ruleset(_metadata, ruleset_fd); 1958 + ASSERT_EQ(0, close(ruleset_fd)); 1959 + 1960 + /* Uses file_* as directory names. */ 1961 + ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 1962 + ASSERT_EQ(EACCES, errno); 1963 + ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 1964 + ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 1965 + } 1966 + 1967 + static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 1968 + const int open_flags) 1969 + { 1970 + static const char path_template[] = "/proc/self/fd/%d"; 1971 + char procfd_path[sizeof(path_template) + 10]; 1972 + const int procfd_path_size = snprintf(procfd_path, sizeof(procfd_path), 1973 + path_template, fd); 1974 + 1975 + ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 1976 + return open(procfd_path, open_flags); 1977 + } 1978 + 1979 + TEST_F_FORK(layout1, proc_unlinked_file) 1980 + { 1981 + const struct rule rules[] = { 1982 + { 1983 + .path = file1_s1d2, 1984 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 1985 + }, 1986 + {} 1987 + }; 1988 + int reg_fd, proc_fd; 1989 + const int ruleset_fd = create_ruleset(_metadata, 1990 + LANDLOCK_ACCESS_FS_READ_FILE | 1991 + LANDLOCK_ACCESS_FS_WRITE_FILE, rules); 1992 + 1993 + ASSERT_LE(0, ruleset_fd); 1994 + enforce_ruleset(_metadata, ruleset_fd); 1995 + ASSERT_EQ(0, close(ruleset_fd)); 1996 + 1997 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 1998 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1999 + reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 2000 + ASSERT_LE(0, reg_fd); 2001 + ASSERT_EQ(0, unlink(file1_s1d2)); 2002 + 2003 + proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 2004 + ASSERT_LE(0, proc_fd); 2005 + ASSERT_EQ(0, close(proc_fd)); 2006 + 2007 + proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 2008 + ASSERT_EQ(-1, proc_fd) { 2009 + TH_LOG("Successfully opened /proc/self/fd/%d: %s", 2010 + reg_fd, strerror(errno)); 2011 + } 2012 + ASSERT_EQ(EACCES, errno); 2013 + 2014 + ASSERT_EQ(0, close(reg_fd)); 2015 + } 2016 + 2017 + TEST_F_FORK(layout1, proc_pipe) 2018 + { 2019 + int proc_fd; 2020 + int pipe_fds[2]; 2021 + char buf = '\0'; 2022 + const struct rule rules[] = { 2023 + { 2024 + .path = dir_s1d2, 2025 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2026 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2027 + }, 2028 + {} 2029 + }; 2030 + /* Limits read and write access to files tied to the filesystem. */ 2031 + const int ruleset_fd = create_ruleset(_metadata, rules[0].access, 2032 + rules); 2033 + 2034 + ASSERT_LE(0, ruleset_fd); 2035 + enforce_ruleset(_metadata, ruleset_fd); 2036 + ASSERT_EQ(0, close(ruleset_fd)); 2037 + 2038 + /* Checks enforcement for normal files. */ 2039 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 2040 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 2041 + 2042 + /* Checks access to pipes through FD. */ 2043 + ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 2044 + ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) { 2045 + TH_LOG("Failed to write in pipe: %s", strerror(errno)); 2046 + } 2047 + ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 2048 + ASSERT_EQ('.', buf); 2049 + 2050 + /* Checks write access to pipe through /proc/self/fd . */ 2051 + proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 2052 + ASSERT_LE(0, proc_fd); 2053 + ASSERT_EQ(1, write(proc_fd, ".", 1)) { 2054 + TH_LOG("Failed to write through /proc/self/fd/%d: %s", 2055 + pipe_fds[1], strerror(errno)); 2056 + } 2057 + ASSERT_EQ(0, close(proc_fd)); 2058 + 2059 + /* Checks read access to pipe through /proc/self/fd . */ 2060 + proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 2061 + ASSERT_LE(0, proc_fd); 2062 + buf = '\0'; 2063 + ASSERT_EQ(1, read(proc_fd, &buf, 1)) { 2064 + TH_LOG("Failed to read through /proc/self/fd/%d: %s", 2065 + pipe_fds[1], strerror(errno)); 2066 + } 2067 + ASSERT_EQ(0, close(proc_fd)); 2068 + 2069 + ASSERT_EQ(0, close(pipe_fds[0])); 2070 + ASSERT_EQ(0, close(pipe_fds[1])); 2071 + } 2072 + 2073 + FIXTURE(layout1_bind) { 2074 + }; 2075 + 2076 + FIXTURE_SETUP(layout1_bind) 2077 + { 2078 + prepare_layout(_metadata); 2079 + 2080 + create_layout1(_metadata); 2081 + 2082 + set_cap(_metadata, CAP_SYS_ADMIN); 2083 + ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 2084 + clear_cap(_metadata, CAP_SYS_ADMIN); 2085 + } 2086 + 2087 + FIXTURE_TEARDOWN(layout1_bind) 2088 + { 2089 + set_cap(_metadata, CAP_SYS_ADMIN); 2090 + EXPECT_EQ(0, umount(dir_s2d2)); 2091 + clear_cap(_metadata, CAP_SYS_ADMIN); 2092 + 2093 + remove_layout1(_metadata); 2094 + 2095 + cleanup_layout(_metadata); 2096 + } 2097 + 2098 + static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 2099 + static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 2100 + 2101 + /* 2102 + * layout1_bind hierarchy: 2103 + * 2104 + * tmp 2105 + * ├── s1d1 2106 + * │   ├── f1 2107 + * │   ├── f2 2108 + * │   └── s1d2 2109 + * │   ├── f1 2110 + * │   ├── f2 2111 + * │   └── s1d3 2112 + * │   ├── f1 2113 + * │   └── f2 2114 + * ├── s2d1 2115 + * │   ├── f1 2116 + * │   └── s2d2 2117 + * │   ├── f1 2118 + * │   ├── f2 2119 + * │   └── s1d3 2120 + * │   ├── f1 2121 + * │   └── f2 2122 + * └── s3d1 2123 + * └── s3d2 2124 + * └── s3d3 2125 + */ 2126 + 2127 + TEST_F_FORK(layout1_bind, no_restriction) 2128 + { 2129 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 2130 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2131 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 2132 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2133 + ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 2134 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2135 + 2136 + ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 2137 + ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 2138 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 2139 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 2140 + ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 2141 + ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 2142 + 2143 + ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 2144 + ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 2145 + 2146 + ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 2147 + } 2148 + 2149 + TEST_F_FORK(layout1_bind, same_content_same_file) 2150 + { 2151 + /* 2152 + * Sets access right on parent directories of both source and 2153 + * destination mount points. 2154 + */ 2155 + const struct rule layer1_parent[] = { 2156 + { 2157 + .path = dir_s1d1, 2158 + .access = ACCESS_RO, 2159 + }, 2160 + { 2161 + .path = dir_s2d1, 2162 + .access = ACCESS_RW, 2163 + }, 2164 + {} 2165 + }; 2166 + /* 2167 + * Sets access rights on the same bind-mounted directories. The result 2168 + * should be ACCESS_RW for both directories, but not both hierarchies 2169 + * because of the first layer. 2170 + */ 2171 + const struct rule layer2_mount_point[] = { 2172 + { 2173 + .path = dir_s1d2, 2174 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2175 + }, 2176 + { 2177 + .path = dir_s2d2, 2178 + .access = ACCESS_RW, 2179 + }, 2180 + {} 2181 + }; 2182 + /* Only allow read-access to the s1d3 hierarchies. */ 2183 + const struct rule layer3_source[] = { 2184 + { 2185 + .path = dir_s1d3, 2186 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2187 + }, 2188 + {} 2189 + }; 2190 + /* Removes all access rights. */ 2191 + const struct rule layer4_destination[] = { 2192 + { 2193 + .path = bind_file1_s1d3, 2194 + .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 2195 + }, 2196 + {} 2197 + }; 2198 + int ruleset_fd; 2199 + 2200 + /* Sets rules for the parent directories. */ 2201 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 2202 + ASSERT_LE(0, ruleset_fd); 2203 + enforce_ruleset(_metadata, ruleset_fd); 2204 + ASSERT_EQ(0, close(ruleset_fd)); 2205 + 2206 + /* Checks source hierarchy. */ 2207 + ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2208 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 2209 + ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 2210 + 2211 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2212 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2213 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2214 + 2215 + /* Checks destination hierarchy. */ 2216 + ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 2217 + ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 2218 + 2219 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 2220 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2221 + 2222 + /* Sets rules for the mount points. */ 2223 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 2224 + ASSERT_LE(0, ruleset_fd); 2225 + enforce_ruleset(_metadata, ruleset_fd); 2226 + ASSERT_EQ(0, close(ruleset_fd)); 2227 + 2228 + /* Checks source hierarchy. */ 2229 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 2230 + ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 2231 + ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 2232 + 2233 + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2234 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2235 + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2236 + 2237 + /* Checks destination hierarchy. */ 2238 + ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 2239 + ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 2240 + ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 2241 + 2242 + ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 2243 + ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2244 + ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 2245 + 2246 + /* Sets a (shared) rule only on the source. */ 2247 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 2248 + ASSERT_LE(0, ruleset_fd); 2249 + enforce_ruleset(_metadata, ruleset_fd); 2250 + ASSERT_EQ(0, close(ruleset_fd)); 2251 + 2252 + /* Checks source hierarchy. */ 2253 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 2254 + ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2255 + ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2256 + 2257 + ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2258 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 2259 + ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 2260 + 2261 + /* Checks destination hierarchy. */ 2262 + ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 2263 + ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 2264 + ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2265 + 2266 + ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 2267 + ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 2268 + ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 2269 + 2270 + /* Sets a (shared) rule only on the destination. */ 2271 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 2272 + ASSERT_LE(0, ruleset_fd); 2273 + enforce_ruleset(_metadata, ruleset_fd); 2274 + ASSERT_EQ(0, close(ruleset_fd)); 2275 + 2276 + /* Checks source hierarchy. */ 2277 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 2278 + ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 2279 + 2280 + /* Checks destination hierarchy. */ 2281 + ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 2282 + ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 2283 + } 2284 + 2285 + #define LOWER_BASE TMP_DIR "/lower" 2286 + #define LOWER_DATA LOWER_BASE "/data" 2287 + static const char lower_fl1[] = LOWER_DATA "/fl1"; 2288 + static const char lower_dl1[] = LOWER_DATA "/dl1"; 2289 + static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 2290 + static const char lower_fo1[] = LOWER_DATA "/fo1"; 2291 + static const char lower_do1[] = LOWER_DATA "/do1"; 2292 + static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 2293 + static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 2294 + 2295 + static const char (*lower_base_files[])[] = { 2296 + &lower_fl1, 2297 + &lower_fo1, 2298 + NULL 2299 + }; 2300 + static const char (*lower_base_directories[])[] = { 2301 + &lower_dl1, 2302 + &lower_do1, 2303 + NULL 2304 + }; 2305 + static const char (*lower_sub_files[])[] = { 2306 + &lower_dl1_fl2, 2307 + &lower_do1_fo2, 2308 + &lower_do1_fl3, 2309 + NULL 2310 + }; 2311 + 2312 + #define UPPER_BASE TMP_DIR "/upper" 2313 + #define UPPER_DATA UPPER_BASE "/data" 2314 + #define UPPER_WORK UPPER_BASE "/work" 2315 + static const char upper_fu1[] = UPPER_DATA "/fu1"; 2316 + static const char upper_du1[] = UPPER_DATA "/du1"; 2317 + static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 2318 + static const char upper_fo1[] = UPPER_DATA "/fo1"; 2319 + static const char upper_do1[] = UPPER_DATA "/do1"; 2320 + static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 2321 + static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 2322 + 2323 + static const char (*upper_base_files[])[] = { 2324 + &upper_fu1, 2325 + &upper_fo1, 2326 + NULL 2327 + }; 2328 + static const char (*upper_base_directories[])[] = { 2329 + &upper_du1, 2330 + &upper_do1, 2331 + NULL 2332 + }; 2333 + static const char (*upper_sub_files[])[] = { 2334 + &upper_du1_fu2, 2335 + &upper_do1_fo2, 2336 + &upper_do1_fu3, 2337 + NULL 2338 + }; 2339 + 2340 + #define MERGE_BASE TMP_DIR "/merge" 2341 + #define MERGE_DATA MERGE_BASE "/data" 2342 + static const char merge_fl1[] = MERGE_DATA "/fl1"; 2343 + static const char merge_dl1[] = MERGE_DATA "/dl1"; 2344 + static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 2345 + static const char merge_fu1[] = MERGE_DATA "/fu1"; 2346 + static const char merge_du1[] = MERGE_DATA "/du1"; 2347 + static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 2348 + static const char merge_fo1[] = MERGE_DATA "/fo1"; 2349 + static const char merge_do1[] = MERGE_DATA "/do1"; 2350 + static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 2351 + static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 2352 + static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 2353 + 2354 + static const char (*merge_base_files[])[] = { 2355 + &merge_fl1, 2356 + &merge_fu1, 2357 + &merge_fo1, 2358 + NULL 2359 + }; 2360 + static const char (*merge_base_directories[])[] = { 2361 + &merge_dl1, 2362 + &merge_du1, 2363 + &merge_do1, 2364 + NULL 2365 + }; 2366 + static const char (*merge_sub_files[])[] = { 2367 + &merge_dl1_fl2, 2368 + &merge_du1_fu2, 2369 + &merge_do1_fo2, 2370 + &merge_do1_fl3, 2371 + &merge_do1_fu3, 2372 + NULL 2373 + }; 2374 + 2375 + /* 2376 + * layout2_overlay hierarchy: 2377 + * 2378 + * tmp 2379 + * ├── lower 2380 + * │   └── data 2381 + * │   ├── dl1 2382 + * │   │   └── fl2 2383 + * │   ├── do1 2384 + * │   │   ├── fl3 2385 + * │   │   └── fo2 2386 + * │   ├── fl1 2387 + * │   └── fo1 2388 + * ├── merge 2389 + * │   └── data 2390 + * │   ├── dl1 2391 + * │   │   └── fl2 2392 + * │   ├── do1 2393 + * │   │   ├── fl3 2394 + * │   │   ├── fo2 2395 + * │   │   └── fu3 2396 + * │   ├── du1 2397 + * │   │   └── fu2 2398 + * │   ├── fl1 2399 + * │   ├── fo1 2400 + * │   └── fu1 2401 + * └── upper 2402 + * ├── data 2403 + * │   ├── do1 2404 + * │   │   ├── fo2 2405 + * │   │   └── fu3 2406 + * │   ├── du1 2407 + * │   │   └── fu2 2408 + * │   ├── fo1 2409 + * │   └── fu1 2410 + * └── work 2411 + * └── work 2412 + */ 2413 + 2414 + FIXTURE(layout2_overlay) { 2415 + }; 2416 + 2417 + FIXTURE_SETUP(layout2_overlay) 2418 + { 2419 + prepare_layout(_metadata); 2420 + 2421 + create_directory(_metadata, LOWER_BASE); 2422 + set_cap(_metadata, CAP_SYS_ADMIN); 2423 + /* Creates tmpfs mount points to get deterministic overlayfs. */ 2424 + ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700")); 2425 + clear_cap(_metadata, CAP_SYS_ADMIN); 2426 + create_file(_metadata, lower_fl1); 2427 + create_file(_metadata, lower_dl1_fl2); 2428 + create_file(_metadata, lower_fo1); 2429 + create_file(_metadata, lower_do1_fo2); 2430 + create_file(_metadata, lower_do1_fl3); 2431 + 2432 + create_directory(_metadata, UPPER_BASE); 2433 + set_cap(_metadata, CAP_SYS_ADMIN); 2434 + ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700")); 2435 + clear_cap(_metadata, CAP_SYS_ADMIN); 2436 + create_file(_metadata, upper_fu1); 2437 + create_file(_metadata, upper_du1_fu2); 2438 + create_file(_metadata, upper_fo1); 2439 + create_file(_metadata, upper_do1_fo2); 2440 + create_file(_metadata, upper_do1_fu3); 2441 + ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 2442 + 2443 + create_directory(_metadata, MERGE_DATA); 2444 + set_cap(_metadata, CAP_SYS_ADMIN); 2445 + set_cap(_metadata, CAP_DAC_OVERRIDE); 2446 + ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 2447 + "lowerdir=" LOWER_DATA 2448 + ",upperdir=" UPPER_DATA 2449 + ",workdir=" UPPER_WORK)); 2450 + clear_cap(_metadata, CAP_DAC_OVERRIDE); 2451 + clear_cap(_metadata, CAP_SYS_ADMIN); 2452 + } 2453 + 2454 + FIXTURE_TEARDOWN(layout2_overlay) 2455 + { 2456 + EXPECT_EQ(0, remove_path(lower_do1_fl3)); 2457 + EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 2458 + EXPECT_EQ(0, remove_path(lower_fl1)); 2459 + EXPECT_EQ(0, remove_path(lower_do1_fo2)); 2460 + EXPECT_EQ(0, remove_path(lower_fo1)); 2461 + set_cap(_metadata, CAP_SYS_ADMIN); 2462 + EXPECT_EQ(0, umount(LOWER_BASE)); 2463 + clear_cap(_metadata, CAP_SYS_ADMIN); 2464 + EXPECT_EQ(0, remove_path(LOWER_BASE)); 2465 + 2466 + EXPECT_EQ(0, remove_path(upper_do1_fu3)); 2467 + EXPECT_EQ(0, remove_path(upper_du1_fu2)); 2468 + EXPECT_EQ(0, remove_path(upper_fu1)); 2469 + EXPECT_EQ(0, remove_path(upper_do1_fo2)); 2470 + EXPECT_EQ(0, remove_path(upper_fo1)); 2471 + EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 2472 + set_cap(_metadata, CAP_SYS_ADMIN); 2473 + EXPECT_EQ(0, umount(UPPER_BASE)); 2474 + clear_cap(_metadata, CAP_SYS_ADMIN); 2475 + EXPECT_EQ(0, remove_path(UPPER_BASE)); 2476 + 2477 + set_cap(_metadata, CAP_SYS_ADMIN); 2478 + EXPECT_EQ(0, umount(MERGE_DATA)); 2479 + clear_cap(_metadata, CAP_SYS_ADMIN); 2480 + EXPECT_EQ(0, remove_path(MERGE_DATA)); 2481 + 2482 + cleanup_layout(_metadata); 2483 + } 2484 + 2485 + TEST_F_FORK(layout2_overlay, no_restriction) 2486 + { 2487 + ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 2488 + ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 2489 + ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 2490 + ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 2491 + ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 2492 + ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 2493 + ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 2494 + 2495 + ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 2496 + ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 2497 + ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 2498 + ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 2499 + ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 2500 + ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 2501 + ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 2502 + 2503 + ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 2504 + ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 2505 + ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 2506 + ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 2507 + ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 2508 + ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 2509 + ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 2510 + ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 2511 + ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 2512 + ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 2513 + ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 2514 + } 2515 + 2516 + #define for_each_path(path_list, path_entry, i) \ 2517 + for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 2518 + path_entry = *path_list[++i]) 2519 + 2520 + TEST_F_FORK(layout2_overlay, same_content_different_file) 2521 + { 2522 + /* Sets access right on parent directories of both layers. */ 2523 + const struct rule layer1_base[] = { 2524 + { 2525 + .path = LOWER_BASE, 2526 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2527 + }, 2528 + { 2529 + .path = UPPER_BASE, 2530 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2531 + }, 2532 + { 2533 + .path = MERGE_BASE, 2534 + .access = ACCESS_RW, 2535 + }, 2536 + {} 2537 + }; 2538 + const struct rule layer2_data[] = { 2539 + { 2540 + .path = LOWER_DATA, 2541 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2542 + }, 2543 + { 2544 + .path = UPPER_DATA, 2545 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2546 + }, 2547 + { 2548 + .path = MERGE_DATA, 2549 + .access = ACCESS_RW, 2550 + }, 2551 + {} 2552 + }; 2553 + /* Sets access right on directories inside both layers. */ 2554 + const struct rule layer3_subdirs[] = { 2555 + { 2556 + .path = lower_dl1, 2557 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2558 + }, 2559 + { 2560 + .path = lower_do1, 2561 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2562 + }, 2563 + { 2564 + .path = upper_du1, 2565 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2566 + }, 2567 + { 2568 + .path = upper_do1, 2569 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2570 + }, 2571 + { 2572 + .path = merge_dl1, 2573 + .access = ACCESS_RW, 2574 + }, 2575 + { 2576 + .path = merge_du1, 2577 + .access = ACCESS_RW, 2578 + }, 2579 + { 2580 + .path = merge_do1, 2581 + .access = ACCESS_RW, 2582 + }, 2583 + {} 2584 + }; 2585 + /* Tighten access rights to the files. */ 2586 + const struct rule layer4_files[] = { 2587 + { 2588 + .path = lower_dl1_fl2, 2589 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2590 + }, 2591 + { 2592 + .path = lower_do1_fo2, 2593 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2594 + }, 2595 + { 2596 + .path = lower_do1_fl3, 2597 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2598 + }, 2599 + { 2600 + .path = upper_du1_fu2, 2601 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2602 + }, 2603 + { 2604 + .path = upper_do1_fo2, 2605 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2606 + }, 2607 + { 2608 + .path = upper_do1_fu3, 2609 + .access = LANDLOCK_ACCESS_FS_READ_FILE, 2610 + }, 2611 + { 2612 + .path = merge_dl1_fl2, 2613 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2614 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2615 + }, 2616 + { 2617 + .path = merge_du1_fu2, 2618 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2619 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2620 + }, 2621 + { 2622 + .path = merge_do1_fo2, 2623 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2624 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2625 + }, 2626 + { 2627 + .path = merge_do1_fl3, 2628 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2629 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2630 + }, 2631 + { 2632 + .path = merge_do1_fu3, 2633 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2634 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2635 + }, 2636 + {} 2637 + }; 2638 + const struct rule layer5_merge_only[] = { 2639 + { 2640 + .path = MERGE_DATA, 2641 + .access = LANDLOCK_ACCESS_FS_READ_FILE | 2642 + LANDLOCK_ACCESS_FS_WRITE_FILE, 2643 + }, 2644 + {} 2645 + }; 2646 + int ruleset_fd; 2647 + size_t i; 2648 + const char *path_entry; 2649 + 2650 + /* Sets rules on base directories (i.e. outside overlay scope). */ 2651 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 2652 + ASSERT_LE(0, ruleset_fd); 2653 + enforce_ruleset(_metadata, ruleset_fd); 2654 + ASSERT_EQ(0, close(ruleset_fd)); 2655 + 2656 + /* Checks lower layer. */ 2657 + for_each_path(lower_base_files, path_entry, i) { 2658 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2659 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2660 + } 2661 + for_each_path(lower_base_directories, path_entry, i) { 2662 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2663 + } 2664 + for_each_path(lower_sub_files, path_entry, i) { 2665 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2666 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2667 + } 2668 + /* Checks upper layer. */ 2669 + for_each_path(upper_base_files, path_entry, i) { 2670 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2671 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2672 + } 2673 + for_each_path(upper_base_directories, path_entry, i) { 2674 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2675 + } 2676 + for_each_path(upper_sub_files, path_entry, i) { 2677 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2678 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2679 + } 2680 + /* 2681 + * Checks that access rights are independent from the lower and upper 2682 + * layers: write access to upper files viewed through the merge point 2683 + * is still allowed, and write access to lower file viewed (and copied) 2684 + * through the merge point is still allowed. 2685 + */ 2686 + for_each_path(merge_base_files, path_entry, i) { 2687 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2688 + } 2689 + for_each_path(merge_base_directories, path_entry, i) { 2690 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2691 + } 2692 + for_each_path(merge_sub_files, path_entry, i) { 2693 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2694 + } 2695 + 2696 + /* Sets rules on data directories (i.e. inside overlay scope). */ 2697 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 2698 + ASSERT_LE(0, ruleset_fd); 2699 + enforce_ruleset(_metadata, ruleset_fd); 2700 + ASSERT_EQ(0, close(ruleset_fd)); 2701 + 2702 + /* Checks merge. */ 2703 + for_each_path(merge_base_files, path_entry, i) { 2704 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2705 + } 2706 + for_each_path(merge_base_directories, path_entry, i) { 2707 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2708 + } 2709 + for_each_path(merge_sub_files, path_entry, i) { 2710 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2711 + } 2712 + 2713 + /* Same checks with tighter rules. */ 2714 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 2715 + ASSERT_LE(0, ruleset_fd); 2716 + enforce_ruleset(_metadata, ruleset_fd); 2717 + ASSERT_EQ(0, close(ruleset_fd)); 2718 + 2719 + /* Checks changes for lower layer. */ 2720 + for_each_path(lower_base_files, path_entry, i) { 2721 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2722 + } 2723 + /* Checks changes for upper layer. */ 2724 + for_each_path(upper_base_files, path_entry, i) { 2725 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2726 + } 2727 + /* Checks all merge accesses. */ 2728 + for_each_path(merge_base_files, path_entry, i) { 2729 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2730 + } 2731 + for_each_path(merge_base_directories, path_entry, i) { 2732 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2733 + } 2734 + for_each_path(merge_sub_files, path_entry, i) { 2735 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2736 + } 2737 + 2738 + /* Sets rules directly on overlayed files. */ 2739 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 2740 + ASSERT_LE(0, ruleset_fd); 2741 + enforce_ruleset(_metadata, ruleset_fd); 2742 + ASSERT_EQ(0, close(ruleset_fd)); 2743 + 2744 + /* Checks unchanged accesses on lower layer. */ 2745 + for_each_path(lower_sub_files, path_entry, i) { 2746 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2747 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2748 + } 2749 + /* Checks unchanged accesses on upper layer. */ 2750 + for_each_path(upper_sub_files, path_entry, i) { 2751 + ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2752 + ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2753 + } 2754 + /* Checks all merge accesses. */ 2755 + for_each_path(merge_base_files, path_entry, i) { 2756 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2757 + } 2758 + for_each_path(merge_base_directories, path_entry, i) { 2759 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2760 + } 2761 + for_each_path(merge_sub_files, path_entry, i) { 2762 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2763 + } 2764 + 2765 + /* Only allowes access to the merge hierarchy. */ 2766 + ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 2767 + ASSERT_LE(0, ruleset_fd); 2768 + enforce_ruleset(_metadata, ruleset_fd); 2769 + ASSERT_EQ(0, close(ruleset_fd)); 2770 + 2771 + /* Checks new accesses on lower layer. */ 2772 + for_each_path(lower_sub_files, path_entry, i) { 2773 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2774 + } 2775 + /* Checks new accesses on upper layer. */ 2776 + for_each_path(upper_sub_files, path_entry, i) { 2777 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2778 + } 2779 + /* Checks all merge accesses. */ 2780 + for_each_path(merge_base_files, path_entry, i) { 2781 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2782 + } 2783 + for_each_path(merge_base_directories, path_entry, i) { 2784 + ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2785 + } 2786 + for_each_path(merge_sub_files, path_entry, i) { 2787 + ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2788 + } 2789 + } 2790 + 2791 + TEST_HARNESS_MAIN
+337
tools/testing/selftests/landlock/ptrace_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Landlock tests - Ptrace 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + */ 8 + 9 + #define _GNU_SOURCE 10 + #include <errno.h> 11 + #include <fcntl.h> 12 + #include <linux/landlock.h> 13 + #include <signal.h> 14 + #include <sys/prctl.h> 15 + #include <sys/ptrace.h> 16 + #include <sys/types.h> 17 + #include <sys/wait.h> 18 + #include <unistd.h> 19 + 20 + #include "common.h" 21 + 22 + static void create_domain(struct __test_metadata *const _metadata) 23 + { 24 + int ruleset_fd; 25 + struct landlock_ruleset_attr ruleset_attr = { 26 + .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK, 27 + }; 28 + 29 + ruleset_fd = landlock_create_ruleset(&ruleset_attr, 30 + sizeof(ruleset_attr), 0); 31 + EXPECT_LE(0, ruleset_fd) { 32 + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 33 + } 34 + EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 35 + EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 36 + EXPECT_EQ(0, close(ruleset_fd)); 37 + } 38 + 39 + static int test_ptrace_read(const pid_t pid) 40 + { 41 + static const char path_template[] = "/proc/%d/environ"; 42 + char procenv_path[sizeof(path_template) + 10]; 43 + int procenv_path_size, fd; 44 + 45 + procenv_path_size = snprintf(procenv_path, sizeof(procenv_path), 46 + path_template, pid); 47 + if (procenv_path_size >= sizeof(procenv_path)) 48 + return E2BIG; 49 + 50 + fd = open(procenv_path, O_RDONLY | O_CLOEXEC); 51 + if (fd < 0) 52 + return errno; 53 + /* 54 + * Mixing error codes from close(2) and open(2) should not lead to any 55 + * (access type) confusion for this test. 56 + */ 57 + if (close(fd) != 0) 58 + return errno; 59 + return 0; 60 + } 61 + 62 + FIXTURE(hierarchy) { }; 63 + 64 + FIXTURE_VARIANT(hierarchy) { 65 + const bool domain_both; 66 + const bool domain_parent; 67 + const bool domain_child; 68 + }; 69 + 70 + /* 71 + * Test multiple tracing combinations between a parent process P1 and a child 72 + * process P2. 73 + * 74 + * Yama's scoped ptrace is presumed disabled. If enabled, this optional 75 + * restriction is enforced in addition to any Landlock check, which means that 76 + * all P2 requests to trace P1 would be denied. 77 + */ 78 + 79 + /* 80 + * No domain 81 + * 82 + * P1-. P1 -> P2 : allow 83 + * \ P2 -> P1 : allow 84 + * 'P2 85 + */ 86 + FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) { 87 + .domain_both = false, 88 + .domain_parent = false, 89 + .domain_child = false, 90 + }; 91 + 92 + /* 93 + * Child domain 94 + * 95 + * P1--. P1 -> P2 : allow 96 + * \ P2 -> P1 : deny 97 + * .'-----. 98 + * | P2 | 99 + * '------' 100 + */ 101 + FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) { 102 + .domain_both = false, 103 + .domain_parent = false, 104 + .domain_child = true, 105 + }; 106 + 107 + /* 108 + * Parent domain 109 + * .------. 110 + * | P1 --. P1 -> P2 : deny 111 + * '------' \ P2 -> P1 : allow 112 + * ' 113 + * P2 114 + */ 115 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) { 116 + .domain_both = false, 117 + .domain_parent = true, 118 + .domain_child = false, 119 + }; 120 + 121 + /* 122 + * Parent + child domain (siblings) 123 + * .------. 124 + * | P1 ---. P1 -> P2 : deny 125 + * '------' \ P2 -> P1 : deny 126 + * .---'--. 127 + * | P2 | 128 + * '------' 129 + */ 130 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) { 131 + .domain_both = false, 132 + .domain_parent = true, 133 + .domain_child = true, 134 + }; 135 + 136 + /* 137 + * Same domain (inherited) 138 + * .-------------. 139 + * | P1----. | P1 -> P2 : allow 140 + * | \ | P2 -> P1 : allow 141 + * | ' | 142 + * | P2 | 143 + * '-------------' 144 + */ 145 + FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) { 146 + .domain_both = true, 147 + .domain_parent = false, 148 + .domain_child = false, 149 + }; 150 + 151 + /* 152 + * Inherited + child domain 153 + * .-----------------. 154 + * | P1----. | P1 -> P2 : allow 155 + * | \ | P2 -> P1 : deny 156 + * | .-'----. | 157 + * | | P2 | | 158 + * | '------' | 159 + * '-----------------' 160 + */ 161 + FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) { 162 + .domain_both = true, 163 + .domain_parent = false, 164 + .domain_child = true, 165 + }; 166 + 167 + /* 168 + * Inherited + parent domain 169 + * .-----------------. 170 + * |.------. | P1 -> P2 : deny 171 + * || P1 ----. | P2 -> P1 : allow 172 + * |'------' \ | 173 + * | ' | 174 + * | P2 | 175 + * '-----------------' 176 + */ 177 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) { 178 + .domain_both = true, 179 + .domain_parent = true, 180 + .domain_child = false, 181 + }; 182 + 183 + /* 184 + * Inherited + parent and child domain (siblings) 185 + * .-----------------. 186 + * | .------. | P1 -> P2 : deny 187 + * | | P1 . | P2 -> P1 : deny 188 + * | '------'\ | 189 + * | \ | 190 + * | .--'---. | 191 + * | | P2 | | 192 + * | '------' | 193 + * '-----------------' 194 + */ 195 + FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) { 196 + .domain_both = true, 197 + .domain_parent = true, 198 + .domain_child = true, 199 + }; 200 + 201 + FIXTURE_SETUP(hierarchy) 202 + { } 203 + 204 + FIXTURE_TEARDOWN(hierarchy) 205 + { } 206 + 207 + /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */ 208 + TEST_F(hierarchy, trace) 209 + { 210 + pid_t child, parent; 211 + int status, err_proc_read; 212 + int pipe_child[2], pipe_parent[2]; 213 + char buf_parent; 214 + long ret; 215 + 216 + /* 217 + * Removes all effective and permitted capabilities to not interfere 218 + * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS. 219 + */ 220 + drop_caps(_metadata); 221 + 222 + parent = getpid(); 223 + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); 224 + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); 225 + if (variant->domain_both) { 226 + create_domain(_metadata); 227 + if (!_metadata->passed) 228 + /* Aborts before forking. */ 229 + return; 230 + } 231 + 232 + child = fork(); 233 + ASSERT_LE(0, child); 234 + if (child == 0) { 235 + char buf_child; 236 + 237 + ASSERT_EQ(0, close(pipe_parent[1])); 238 + ASSERT_EQ(0, close(pipe_child[0])); 239 + if (variant->domain_child) 240 + create_domain(_metadata); 241 + 242 + /* Waits for the parent to be in a domain, if any. */ 243 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 244 + 245 + /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the parent. */ 246 + err_proc_read = test_ptrace_read(parent); 247 + ret = ptrace(PTRACE_ATTACH, parent, NULL, 0); 248 + if (variant->domain_child) { 249 + EXPECT_EQ(-1, ret); 250 + EXPECT_EQ(EPERM, errno); 251 + EXPECT_EQ(EACCES, err_proc_read); 252 + } else { 253 + EXPECT_EQ(0, ret); 254 + EXPECT_EQ(0, err_proc_read); 255 + } 256 + if (ret == 0) { 257 + ASSERT_EQ(parent, waitpid(parent, &status, 0)); 258 + ASSERT_EQ(1, WIFSTOPPED(status)); 259 + ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0)); 260 + } 261 + 262 + /* Tests child PTRACE_TRACEME. */ 263 + ret = ptrace(PTRACE_TRACEME); 264 + if (variant->domain_parent) { 265 + EXPECT_EQ(-1, ret); 266 + EXPECT_EQ(EPERM, errno); 267 + } else { 268 + EXPECT_EQ(0, ret); 269 + } 270 + 271 + /* 272 + * Signals that the PTRACE_ATTACH test is done and the 273 + * PTRACE_TRACEME test is ongoing. 274 + */ 275 + ASSERT_EQ(1, write(pipe_child[1], ".", 1)); 276 + 277 + if (!variant->domain_parent) { 278 + ASSERT_EQ(0, raise(SIGSTOP)); 279 + } 280 + 281 + /* Waits for the parent PTRACE_ATTACH test. */ 282 + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); 283 + _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 284 + return; 285 + } 286 + 287 + ASSERT_EQ(0, close(pipe_child[1])); 288 + ASSERT_EQ(0, close(pipe_parent[0])); 289 + if (variant->domain_parent) 290 + create_domain(_metadata); 291 + 292 + /* Signals that the parent is in a domain, if any. */ 293 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 294 + 295 + /* 296 + * Waits for the child to test PTRACE_ATTACH on the parent and start 297 + * testing PTRACE_TRACEME. 298 + */ 299 + ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1)); 300 + 301 + /* Tests child PTRACE_TRACEME. */ 302 + if (!variant->domain_parent) { 303 + ASSERT_EQ(child, waitpid(child, &status, 0)); 304 + ASSERT_EQ(1, WIFSTOPPED(status)); 305 + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); 306 + } else { 307 + /* The child should not be traced by the parent. */ 308 + EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0)); 309 + EXPECT_EQ(ESRCH, errno); 310 + } 311 + 312 + /* Tests PTRACE_ATTACH and PTRACE_MODE_READ on the child. */ 313 + err_proc_read = test_ptrace_read(child); 314 + ret = ptrace(PTRACE_ATTACH, child, NULL, 0); 315 + if (variant->domain_parent) { 316 + EXPECT_EQ(-1, ret); 317 + EXPECT_EQ(EPERM, errno); 318 + EXPECT_EQ(EACCES, err_proc_read); 319 + } else { 320 + EXPECT_EQ(0, ret); 321 + EXPECT_EQ(0, err_proc_read); 322 + } 323 + if (ret == 0) { 324 + ASSERT_EQ(child, waitpid(child, &status, 0)); 325 + ASSERT_EQ(1, WIFSTOPPED(status)); 326 + ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0)); 327 + } 328 + 329 + /* Signals that the parent PTRACE_ATTACH test is done. */ 330 + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); 331 + ASSERT_EQ(child, waitpid(child, &status, 0)); 332 + if (WIFSIGNALED(status) || !WIFEXITED(status) || 333 + WEXITSTATUS(status) != EXIT_SUCCESS) 334 + _metadata->passed = 0; 335 + } 336 + 337 + TEST_HARNESS_MAIN
+5
tools/testing/selftests/landlock/true.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + int main(void) 3 + { 4 + return 0; 5 + }