1e1199815SMickaël Salaün // SPDX-License-Identifier: GPL-2.0 2e1199815SMickaël Salaün /* 3e1199815SMickaël Salaün * Landlock tests - Filesystem 4e1199815SMickaël Salaün * 5e1199815SMickaël Salaün * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6e1199815SMickaël Salaün * Copyright © 2020 ANSSI 7e1199815SMickaël Salaün * Copyright © 2020-2021 Microsoft Corporation 8e1199815SMickaël Salaün */ 9e1199815SMickaël Salaün 10e1199815SMickaël Salaün #define _GNU_SOURCE 11e1199815SMickaël Salaün #include <fcntl.h> 12e1199815SMickaël Salaün #include <linux/landlock.h> 13e1199815SMickaël Salaün #include <sched.h> 14e1199815SMickaël Salaün #include <string.h> 15e1199815SMickaël Salaün #include <sys/capability.h> 16e1199815SMickaël Salaün #include <sys/mount.h> 17e1199815SMickaël Salaün #include <sys/prctl.h> 18e1199815SMickaël Salaün #include <sys/sendfile.h> 19e1199815SMickaël Salaün #include <sys/stat.h> 20e1199815SMickaël Salaün #include <sys/sysmacros.h> 21e1199815SMickaël Salaün #include <unistd.h> 22e1199815SMickaël Salaün 23e1199815SMickaël Salaün #include "common.h" 24e1199815SMickaël Salaün 2587129ef1SMickaël Salaün #ifndef renameat2 2687129ef1SMickaël Salaün int renameat2(int olddirfd, const char *oldpath, int newdirfd, 2787129ef1SMickaël Salaün const char *newpath, unsigned int flags) 2887129ef1SMickaël Salaün { 2987129ef1SMickaël Salaün return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 3087129ef1SMickaël Salaün flags); 3187129ef1SMickaël Salaün } 3287129ef1SMickaël Salaün #endif 3387129ef1SMickaël Salaün 3487129ef1SMickaël Salaün #ifndef RENAME_EXCHANGE 3587129ef1SMickaël Salaün #define RENAME_EXCHANGE (1 << 1) 3687129ef1SMickaël Salaün #endif 3787129ef1SMickaël Salaün 38e1199815SMickaël Salaün #define TMP_DIR "tmp" 39e1199815SMickaël Salaün #define BINARY_PATH "./true" 40e1199815SMickaël Salaün 41e1199815SMickaël Salaün /* Paths (sibling number and depth) */ 42e1199815SMickaël Salaün static const char dir_s1d1[] = TMP_DIR "/s1d1"; 43e1199815SMickaël Salaün static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 44e1199815SMickaël Salaün static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 45e1199815SMickaël Salaün static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 46e1199815SMickaël Salaün static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 47e1199815SMickaël Salaün static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 48e1199815SMickaël Salaün static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 49e1199815SMickaël Salaün static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 50e1199815SMickaël Salaün static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 51e1199815SMickaël Salaün 52e1199815SMickaël Salaün static const char dir_s2d1[] = TMP_DIR "/s2d1"; 53e1199815SMickaël Salaün static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 54e1199815SMickaël Salaün static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 55e1199815SMickaël Salaün static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 56e1199815SMickaël Salaün static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 57e1199815SMickaël Salaün static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 58e1199815SMickaël Salaün static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 59e1199815SMickaël Salaün 60e1199815SMickaël Salaün static const char dir_s3d1[] = TMP_DIR "/s3d1"; 61e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 62e1199815SMickaël Salaün static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 63e1199815SMickaël Salaün static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 64e1199815SMickaël Salaün 65e1199815SMickaël Salaün /* 66e1199815SMickaël Salaün * layout1 hierarchy: 67e1199815SMickaël Salaün * 68e1199815SMickaël Salaün * tmp 69e1199815SMickaël Salaün * ├── s1d1 70e1199815SMickaël Salaün * │ ├── f1 71e1199815SMickaël Salaün * │ ├── f2 72e1199815SMickaël Salaün * │ └── s1d2 73e1199815SMickaël Salaün * │ ├── f1 74e1199815SMickaël Salaün * │ ├── f2 75e1199815SMickaël Salaün * │ └── s1d3 76e1199815SMickaël Salaün * │ ├── f1 77e1199815SMickaël Salaün * │ └── f2 78e1199815SMickaël Salaün * ├── s2d1 79e1199815SMickaël Salaün * │ ├── f1 80e1199815SMickaël Salaün * │ └── s2d2 81e1199815SMickaël Salaün * │ ├── f1 82e1199815SMickaël Salaün * │ └── s2d3 83e1199815SMickaël Salaün * │ ├── f1 84e1199815SMickaël Salaün * │ └── f2 85e1199815SMickaël Salaün * └── s3d1 86e1199815SMickaël Salaün * └── s3d2 87e1199815SMickaël Salaün * └── s3d3 88e1199815SMickaël Salaün */ 89e1199815SMickaël Salaün 90e1199815SMickaël Salaün static void mkdir_parents(struct __test_metadata *const _metadata, 91e1199815SMickaël Salaün const char *const path) 92e1199815SMickaël Salaün { 93e1199815SMickaël Salaün char *walker; 94e1199815SMickaël Salaün const char *parent; 95e1199815SMickaël Salaün int i, err; 96e1199815SMickaël Salaün 97e1199815SMickaël Salaün ASSERT_NE(path[0], '\0'); 98e1199815SMickaël Salaün walker = strdup(path); 99e1199815SMickaël Salaün ASSERT_NE(NULL, walker); 100e1199815SMickaël Salaün parent = walker; 101e1199815SMickaël Salaün for (i = 1; walker[i]; i++) { 102e1199815SMickaël Salaün if (walker[i] != '/') 103e1199815SMickaël Salaün continue; 104e1199815SMickaël Salaün walker[i] = '\0'; 105e1199815SMickaël Salaün err = mkdir(parent, 0700); 106371183faSMickaël Salaün ASSERT_FALSE(err && errno != EEXIST) 107371183faSMickaël Salaün { 108371183faSMickaël Salaün TH_LOG("Failed to create directory \"%s\": %s", parent, 109371183faSMickaël Salaün strerror(errno)); 110e1199815SMickaël Salaün } 111e1199815SMickaël Salaün walker[i] = '/'; 112e1199815SMickaël Salaün } 113e1199815SMickaël Salaün free(walker); 114e1199815SMickaël Salaün } 115e1199815SMickaël Salaün 116e1199815SMickaël Salaün static void create_directory(struct __test_metadata *const _metadata, 117e1199815SMickaël Salaün const char *const path) 118e1199815SMickaël Salaün { 119e1199815SMickaël Salaün mkdir_parents(_metadata, path); 120371183faSMickaël Salaün ASSERT_EQ(0, mkdir(path, 0700)) 121371183faSMickaël Salaün { 122e1199815SMickaël Salaün TH_LOG("Failed to create directory \"%s\": %s", path, 123e1199815SMickaël Salaün strerror(errno)); 124e1199815SMickaël Salaün } 125e1199815SMickaël Salaün } 126e1199815SMickaël Salaün 127e1199815SMickaël Salaün static void create_file(struct __test_metadata *const _metadata, 128e1199815SMickaël Salaün const char *const path) 129e1199815SMickaël Salaün { 130e1199815SMickaël Salaün mkdir_parents(_metadata, path); 131371183faSMickaël Salaün ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 132371183faSMickaël Salaün { 133e1199815SMickaël Salaün TH_LOG("Failed to create file \"%s\": %s", path, 134e1199815SMickaël Salaün strerror(errno)); 135e1199815SMickaël Salaün } 136e1199815SMickaël Salaün } 137e1199815SMickaël Salaün 138e1199815SMickaël Salaün static int remove_path(const char *const path) 139e1199815SMickaël Salaün { 140e1199815SMickaël Salaün char *walker; 141e1199815SMickaël Salaün int i, ret, err = 0; 142e1199815SMickaël Salaün 143e1199815SMickaël Salaün walker = strdup(path); 144e1199815SMickaël Salaün if (!walker) { 145e1199815SMickaël Salaün err = ENOMEM; 146e1199815SMickaël Salaün goto out; 147e1199815SMickaël Salaün } 148e1199815SMickaël Salaün if (unlink(path) && rmdir(path)) { 149e1199815SMickaël Salaün if (errno != ENOENT) 150e1199815SMickaël Salaün err = errno; 151e1199815SMickaël Salaün goto out; 152e1199815SMickaël Salaün } 153e1199815SMickaël Salaün for (i = strlen(walker); i > 0; i--) { 154e1199815SMickaël Salaün if (walker[i] != '/') 155e1199815SMickaël Salaün continue; 156e1199815SMickaël Salaün walker[i] = '\0'; 157e1199815SMickaël Salaün ret = rmdir(walker); 158e1199815SMickaël Salaün if (ret) { 159e1199815SMickaël Salaün if (errno != ENOTEMPTY && errno != EBUSY) 160e1199815SMickaël Salaün err = errno; 161e1199815SMickaël Salaün goto out; 162e1199815SMickaël Salaün } 163e1199815SMickaël Salaün if (strcmp(walker, TMP_DIR) == 0) 164e1199815SMickaël Salaün goto out; 165e1199815SMickaël Salaün } 166e1199815SMickaël Salaün 167e1199815SMickaël Salaün out: 168e1199815SMickaël Salaün free(walker); 169e1199815SMickaël Salaün return err; 170e1199815SMickaël Salaün } 171e1199815SMickaël Salaün 172e1199815SMickaël Salaün static void prepare_layout(struct __test_metadata *const _metadata) 173e1199815SMickaël Salaün { 174e1199815SMickaël Salaün disable_caps(_metadata); 175e1199815SMickaël Salaün umask(0077); 176e1199815SMickaël Salaün create_directory(_metadata, TMP_DIR); 177e1199815SMickaël Salaün 178e1199815SMickaël Salaün /* 179e1199815SMickaël Salaün * Do not pollute the rest of the system: creates a private mount point 180e1199815SMickaël Salaün * for tests relying on pivot_root(2) and move_mount(2). 181e1199815SMickaël Salaün */ 182e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 183e1199815SMickaël Salaün ASSERT_EQ(0, unshare(CLONE_NEWNS)); 184e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700")); 185e1199815SMickaël Salaün ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 186e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 187e1199815SMickaël Salaün } 188e1199815SMickaël Salaün 189e1199815SMickaël Salaün static void cleanup_layout(struct __test_metadata *const _metadata) 190e1199815SMickaël Salaün { 191e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 192e1199815SMickaël Salaün EXPECT_EQ(0, umount(TMP_DIR)); 193e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 194e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(TMP_DIR)); 195e1199815SMickaël Salaün } 196e1199815SMickaël Salaün 197e1199815SMickaël Salaün static void create_layout1(struct __test_metadata *const _metadata) 198e1199815SMickaël Salaün { 199e1199815SMickaël Salaün create_file(_metadata, file1_s1d1); 200e1199815SMickaël Salaün create_file(_metadata, file1_s1d2); 201e1199815SMickaël Salaün create_file(_metadata, file1_s1d3); 202e1199815SMickaël Salaün create_file(_metadata, file2_s1d1); 203e1199815SMickaël Salaün create_file(_metadata, file2_s1d2); 204e1199815SMickaël Salaün create_file(_metadata, file2_s1d3); 205e1199815SMickaël Salaün 206e1199815SMickaël Salaün create_file(_metadata, file1_s2d1); 207e1199815SMickaël Salaün create_file(_metadata, file1_s2d2); 208e1199815SMickaël Salaün create_file(_metadata, file1_s2d3); 209e1199815SMickaël Salaün create_file(_metadata, file2_s2d3); 210e1199815SMickaël Salaün 211e1199815SMickaël Salaün create_directory(_metadata, dir_s3d2); 212e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 213e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700")); 214e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 215e1199815SMickaël Salaün 216e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 217e1199815SMickaël Salaün } 218e1199815SMickaël Salaün 219e1199815SMickaël Salaün static void remove_layout1(struct __test_metadata *const _metadata) 220e1199815SMickaël Salaün { 221e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d3)); 222e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d2)); 223e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d1)); 224e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d3)); 225e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d2)); 226e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d1)); 227e1199815SMickaël Salaün 228e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s2d3)); 229e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d3)); 230e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d2)); 231e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d1)); 232e1199815SMickaël Salaün 233e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(dir_s3d3)); 234e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 235e1199815SMickaël Salaün umount(dir_s3d2); 236e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 237e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(dir_s3d2)); 238e1199815SMickaël Salaün } 239e1199815SMickaël Salaün 2404598d9abSMickaël Salaün /* clang-format off */ 2414598d9abSMickaël Salaün FIXTURE(layout1) {}; 2424598d9abSMickaël Salaün /* clang-format on */ 243e1199815SMickaël Salaün 244e1199815SMickaël Salaün FIXTURE_SETUP(layout1) 245e1199815SMickaël Salaün { 246e1199815SMickaël Salaün prepare_layout(_metadata); 247e1199815SMickaël Salaün 248e1199815SMickaël Salaün create_layout1(_metadata); 249e1199815SMickaël Salaün } 250e1199815SMickaël Salaün 251e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1) 252e1199815SMickaël Salaün { 253e1199815SMickaël Salaün remove_layout1(_metadata); 254e1199815SMickaël Salaün 255e1199815SMickaël Salaün cleanup_layout(_metadata); 256e1199815SMickaël Salaün } 257e1199815SMickaël Salaün 258e1199815SMickaël Salaün /* 259e1199815SMickaël Salaün * This helper enables to use the ASSERT_* macros and print the line number 260e1199815SMickaël Salaün * pointing to the test caller. 261e1199815SMickaël Salaün */ 262371183faSMickaël Salaün static int test_open_rel(const int dirfd, const char *const path, 263371183faSMickaël Salaün const int flags) 264e1199815SMickaël Salaün { 265e1199815SMickaël Salaün int fd; 266e1199815SMickaël Salaün 267e1199815SMickaël Salaün /* Works with file and directories. */ 268e1199815SMickaël Salaün fd = openat(dirfd, path, flags | O_CLOEXEC); 269e1199815SMickaël Salaün if (fd < 0) 270e1199815SMickaël Salaün return errno; 271e1199815SMickaël Salaün /* 272e1199815SMickaël Salaün * Mixing error codes from close(2) and open(2) should not lead to any 273e1199815SMickaël Salaün * (access type) confusion for this test. 274e1199815SMickaël Salaün */ 275e1199815SMickaël Salaün if (close(fd) != 0) 276e1199815SMickaël Salaün return errno; 277e1199815SMickaël Salaün return 0; 278e1199815SMickaël Salaün } 279e1199815SMickaël Salaün 280e1199815SMickaël Salaün static int test_open(const char *const path, const int flags) 281e1199815SMickaël Salaün { 282e1199815SMickaël Salaün return test_open_rel(AT_FDCWD, path, flags); 283e1199815SMickaël Salaün } 284e1199815SMickaël Salaün 285e1199815SMickaël Salaün TEST_F_FORK(layout1, no_restriction) 286e1199815SMickaël Salaün { 287e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 288e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 289e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 290e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 291e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 292e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 293e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 294e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 295e1199815SMickaël Salaün 296e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 297e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 298e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 299e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 300e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 301e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 302e1199815SMickaël Salaün 303e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 304e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 305e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 306e1199815SMickaël Salaün } 307e1199815SMickaël Salaün 308e1199815SMickaël Salaün TEST_F_FORK(layout1, inval) 309e1199815SMickaël Salaün { 310e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath = { 311e1199815SMickaël Salaün .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 312e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 313e1199815SMickaël Salaün .parent_fd = -1, 314e1199815SMickaël Salaün }; 315e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 316e1199815SMickaël Salaün .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 317e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 318e1199815SMickaël Salaün }; 319e1199815SMickaël Salaün int ruleset_fd; 320e1199815SMickaël Salaün 321371183faSMickaël Salaün path_beneath.parent_fd = 322371183faSMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 323e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 324e1199815SMickaël Salaün 325e1199815SMickaël Salaün ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 326e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 327e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 328e1199815SMickaël Salaün &path_beneath, 0)); 329e1199815SMickaël Salaün /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 330e1199815SMickaël Salaün ASSERT_EQ(EBADF, errno); 331e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 332e1199815SMickaël Salaün 333e1199815SMickaël Salaün ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 334e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 335e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 336e1199815SMickaël Salaün &path_beneath, 0)); 337e1199815SMickaël Salaün /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 338e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 339e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 340e1199815SMickaël Salaün 341e1199815SMickaël Salaün /* Gets a real ruleset. */ 342371183faSMickaël Salaün ruleset_fd = 343371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 344e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 345e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 346e1199815SMickaël Salaün &path_beneath, 0)); 347e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 348e1199815SMickaël Salaün 349e1199815SMickaël Salaün /* Tests without O_PATH. */ 350e1199815SMickaël Salaün path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 351e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 352e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 353e1199815SMickaël Salaün &path_beneath, 0)); 354e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 355e1199815SMickaël Salaün 356e1199815SMickaël Salaün /* Tests with a ruleset FD. */ 357e1199815SMickaël Salaün path_beneath.parent_fd = ruleset_fd; 358e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 359e1199815SMickaël Salaün &path_beneath, 0)); 360e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 361e1199815SMickaël Salaün 362e1199815SMickaël Salaün /* Checks unhandled allowed_access. */ 363371183faSMickaël Salaün path_beneath.parent_fd = 364371183faSMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 365e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 366e1199815SMickaël Salaün 367e1199815SMickaël Salaün /* Test with legitimate values. */ 368e1199815SMickaël Salaün path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 369e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 370e1199815SMickaël Salaün &path_beneath, 0)); 371e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 372e1199815SMickaël Salaün path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 373e1199815SMickaël Salaün 374e1199815SMickaël Salaün /* Test with unknown (64-bits) value. */ 375e1199815SMickaël Salaün path_beneath.allowed_access |= (1ULL << 60); 376e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 377e1199815SMickaël Salaün &path_beneath, 0)); 378e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 379e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 380e1199815SMickaël Salaün 381e1199815SMickaël Salaün /* Test with no access. */ 382e1199815SMickaël Salaün path_beneath.allowed_access = 0; 383e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 384e1199815SMickaël Salaün &path_beneath, 0)); 385e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 386e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 387e1199815SMickaël Salaün 388e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 389e1199815SMickaël Salaün 390e1199815SMickaël Salaün /* Enforces the ruleset. */ 391e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 392e1199815SMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 393e1199815SMickaël Salaün 394e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 395e1199815SMickaël Salaün } 396e1199815SMickaël Salaün 3974598d9abSMickaël Salaün /* clang-format off */ 3984598d9abSMickaël Salaün 399e1199815SMickaël Salaün #define ACCESS_FILE ( \ 400e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | \ 401e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE | \ 402e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE) 403e1199815SMickaël Salaün 404e1199815SMickaël Salaün #define ACCESS_LAST LANDLOCK_ACCESS_FS_MAKE_SYM 405e1199815SMickaël Salaün 406e1199815SMickaël Salaün #define ACCESS_ALL ( \ 407e1199815SMickaël Salaün ACCESS_FILE | \ 408e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR | \ 409e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 410e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 411e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 412e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_DIR | \ 413e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | \ 414e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 415e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 416e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 417e1199815SMickaël Salaün ACCESS_LAST) 418e1199815SMickaël Salaün 4194598d9abSMickaël Salaün /* clang-format on */ 4204598d9abSMickaël Salaün 421d18955d0SMickaël Salaün TEST_F_FORK(layout1, file_and_dir_access_rights) 422e1199815SMickaël Salaün { 423e1199815SMickaël Salaün __u64 access; 424e1199815SMickaël Salaün int err; 425d18955d0SMickaël Salaün struct landlock_path_beneath_attr path_beneath_file = {}, 426d18955d0SMickaël Salaün path_beneath_dir = {}; 427e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 428e1199815SMickaël Salaün .handled_access_fs = ACCESS_ALL, 429e1199815SMickaël Salaün }; 430371183faSMickaël Salaün const int ruleset_fd = 431371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 432e1199815SMickaël Salaün 433e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 434e1199815SMickaël Salaün 435e1199815SMickaël Salaün /* Tests access rights for files. */ 436d18955d0SMickaël Salaün path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 437d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_file.parent_fd); 438d18955d0SMickaël Salaün 439d18955d0SMickaël Salaün /* Tests access rights for directories. */ 440d18955d0SMickaël Salaün path_beneath_dir.parent_fd = 441d18955d0SMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 442d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_dir.parent_fd); 443d18955d0SMickaël Salaün 444e1199815SMickaël Salaün for (access = 1; access <= ACCESS_LAST; access <<= 1) { 445d18955d0SMickaël Salaün path_beneath_dir.allowed_access = access; 446d18955d0SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 447d18955d0SMickaël Salaün LANDLOCK_RULE_PATH_BENEATH, 448d18955d0SMickaël Salaün &path_beneath_dir, 0)); 449d18955d0SMickaël Salaün 450d18955d0SMickaël Salaün path_beneath_file.allowed_access = access; 451e1199815SMickaël Salaün err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 452d18955d0SMickaël Salaün &path_beneath_file, 0); 453d18955d0SMickaël Salaün if (access & ACCESS_FILE) { 454e1199815SMickaël Salaün ASSERT_EQ(0, err); 455e1199815SMickaël Salaün } else { 456e1199815SMickaël Salaün ASSERT_EQ(-1, err); 457e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 458e1199815SMickaël Salaün } 459e1199815SMickaël Salaün } 460d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 461d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 462d18955d0SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 463e1199815SMickaël Salaün } 464e1199815SMickaël Salaün 465c56b3bf5SMickaël Salaün TEST_F_FORK(layout1, unknown_access_rights) 466c56b3bf5SMickaël Salaün { 467c56b3bf5SMickaël Salaün __u64 access_mask; 468c56b3bf5SMickaël Salaün 469c56b3bf5SMickaël Salaün for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 470c56b3bf5SMickaël Salaün access_mask >>= 1) { 471c56b3bf5SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 472c56b3bf5SMickaël Salaün .handled_access_fs = access_mask, 473c56b3bf5SMickaël Salaün }; 474c56b3bf5SMickaël Salaün 475c56b3bf5SMickaël Salaün ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 476c56b3bf5SMickaël Salaün sizeof(ruleset_attr), 0)); 477c56b3bf5SMickaël Salaün ASSERT_EQ(EINVAL, errno); 478c56b3bf5SMickaël Salaün } 479c56b3bf5SMickaël Salaün } 480c56b3bf5SMickaël Salaün 481e1199815SMickaël Salaün static void add_path_beneath(struct __test_metadata *const _metadata, 482e1199815SMickaël Salaün const int ruleset_fd, const __u64 allowed_access, 483e1199815SMickaël Salaün const char *const path) 484e1199815SMickaël Salaün { 485e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath = { 486e1199815SMickaël Salaün .allowed_access = allowed_access, 487e1199815SMickaël Salaün }; 488e1199815SMickaël Salaün 489e1199815SMickaël Salaün path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 490371183faSMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd) 491371183faSMickaël Salaün { 492e1199815SMickaël Salaün TH_LOG("Failed to open directory \"%s\": %s", path, 493e1199815SMickaël Salaün strerror(errno)); 494e1199815SMickaël Salaün } 495e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 496371183faSMickaël Salaün &path_beneath, 0)) 497371183faSMickaël Salaün { 498e1199815SMickaël Salaün TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 499e1199815SMickaël Salaün strerror(errno)); 500e1199815SMickaël Salaün } 501e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 502e1199815SMickaël Salaün } 503e1199815SMickaël Salaün 504e1199815SMickaël Salaün struct rule { 505e1199815SMickaël Salaün const char *path; 506e1199815SMickaël Salaün __u64 access; 507e1199815SMickaël Salaün }; 508e1199815SMickaël Salaün 5094598d9abSMickaël Salaün /* clang-format off */ 5104598d9abSMickaël Salaün 511e1199815SMickaël Salaün #define ACCESS_RO ( \ 512e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | \ 513e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR) 514e1199815SMickaël Salaün 515e1199815SMickaël Salaün #define ACCESS_RW ( \ 516e1199815SMickaël Salaün ACCESS_RO | \ 517e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE) 518e1199815SMickaël Salaün 5194598d9abSMickaël Salaün /* clang-format on */ 5204598d9abSMickaël Salaün 521e1199815SMickaël Salaün static int create_ruleset(struct __test_metadata *const _metadata, 522371183faSMickaël Salaün const __u64 handled_access_fs, 523371183faSMickaël Salaün const struct rule rules[]) 524e1199815SMickaël Salaün { 525e1199815SMickaël Salaün int ruleset_fd, i; 526e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 527e1199815SMickaël Salaün .handled_access_fs = handled_access_fs, 528e1199815SMickaël Salaün }; 529e1199815SMickaël Salaün 530371183faSMickaël Salaün ASSERT_NE(NULL, rules) 531371183faSMickaël Salaün { 532e1199815SMickaël Salaün TH_LOG("No rule list"); 533e1199815SMickaël Salaün } 534371183faSMickaël Salaün ASSERT_NE(NULL, rules[0].path) 535371183faSMickaël Salaün { 536e1199815SMickaël Salaün TH_LOG("Empty rule list"); 537e1199815SMickaël Salaün } 538e1199815SMickaël Salaün 539371183faSMickaël Salaün ruleset_fd = 540371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 541371183faSMickaël Salaün ASSERT_LE(0, ruleset_fd) 542371183faSMickaël Salaün { 543e1199815SMickaël Salaün TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 544e1199815SMickaël Salaün } 545e1199815SMickaël Salaün 546e1199815SMickaël Salaün for (i = 0; rules[i].path; i++) { 547e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, rules[i].access, 548e1199815SMickaël Salaün rules[i].path); 549e1199815SMickaël Salaün } 550e1199815SMickaël Salaün return ruleset_fd; 551e1199815SMickaël Salaün } 552e1199815SMickaël Salaün 553e1199815SMickaël Salaün static void enforce_ruleset(struct __test_metadata *const _metadata, 554e1199815SMickaël Salaün const int ruleset_fd) 555e1199815SMickaël Salaün { 556e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 557371183faSMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 558371183faSMickaël Salaün { 559e1199815SMickaël Salaün TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 560e1199815SMickaël Salaün } 561e1199815SMickaël Salaün } 562e1199815SMickaël Salaün 563e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_nsfs) 564e1199815SMickaël Salaün { 565e1199815SMickaël Salaün const struct rule rules[] = { 566e1199815SMickaël Salaün { 567e1199815SMickaël Salaün .path = "/dev/null", 568e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 569e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 570e1199815SMickaël Salaün }, 571135464f9SMickaël Salaün {}, 572e1199815SMickaël Salaün }; 573e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath; 574371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 575371183faSMickaël Salaün _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 576371183faSMickaël Salaün rules); 577e1199815SMickaël Salaün 578e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 579e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 580e1199815SMickaël Salaün 581e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 582e1199815SMickaël Salaün 583e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 584e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 585e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 586e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 587e1199815SMickaël Salaün 588e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 589e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 590e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 591e1199815SMickaël Salaün /* 592e1199815SMickaël Salaün * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 593e1199815SMickaël Salaün * disconnected path. Such path cannot be identified and must then be 594e1199815SMickaël Salaün * allowed. 595e1199815SMickaël Salaün */ 596e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 597e1199815SMickaël Salaün 598e1199815SMickaël Salaün /* 599e1199815SMickaël Salaün * Checks that it is not possible to add nsfs-like filesystem 600e1199815SMickaël Salaün * references to a ruleset. 601e1199815SMickaël Salaün */ 602e1199815SMickaël Salaün path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 603e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 604e1199815SMickaël Salaün path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 605e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 606e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 607e1199815SMickaël Salaün &path_beneath, 0)); 608e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 609e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 610e1199815SMickaël Salaün } 611e1199815SMickaël Salaün 612371183faSMickaël Salaün TEST_F_FORK(layout1, unpriv) 613371183faSMickaël Salaün { 614e1199815SMickaël Salaün const struct rule rules[] = { 615e1199815SMickaël Salaün { 616e1199815SMickaël Salaün .path = dir_s1d2, 617e1199815SMickaël Salaün .access = ACCESS_RO, 618e1199815SMickaël Salaün }, 619135464f9SMickaël Salaün {}, 620e1199815SMickaël Salaün }; 621e1199815SMickaël Salaün int ruleset_fd; 622e1199815SMickaël Salaün 623e1199815SMickaël Salaün drop_caps(_metadata); 624e1199815SMickaël Salaün 625e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 626e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 627e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 628e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 629e1199815SMickaël Salaün 630e1199815SMickaël Salaün /* enforce_ruleset() calls prctl(no_new_privs). */ 631e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 632e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 633e1199815SMickaël Salaün } 634e1199815SMickaël Salaün 635e1199815SMickaël Salaün TEST_F_FORK(layout1, effective_access) 636e1199815SMickaël Salaün { 637e1199815SMickaël Salaün const struct rule rules[] = { 638e1199815SMickaël Salaün { 639e1199815SMickaël Salaün .path = dir_s1d2, 640e1199815SMickaël Salaün .access = ACCESS_RO, 641e1199815SMickaël Salaün }, 642e1199815SMickaël Salaün { 643e1199815SMickaël Salaün .path = file1_s2d2, 644e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 645e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 646e1199815SMickaël Salaün }, 647135464f9SMickaël Salaün {}, 648e1199815SMickaël Salaün }; 649e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 650e1199815SMickaël Salaün char buf; 651e1199815SMickaël Salaün int reg_fd; 652e1199815SMickaël Salaün 653e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 654e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 655e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 656e1199815SMickaël Salaün 657d1788ad9SMickaël Salaün /* Tests on a directory (with or without O_PATH). */ 658e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 659d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 660e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 661d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 662e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 663d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 664d1788ad9SMickaël Salaün 665e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 666e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 667e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 668e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 669e1199815SMickaël Salaün 670d1788ad9SMickaël Salaün /* Tests on a file (with or without O_PATH). */ 671e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 672d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 673d1788ad9SMickaël Salaün 674e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 675e1199815SMickaël Salaün 676e1199815SMickaël Salaün /* Checks effective read and write actions. */ 677e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 678e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 679e1199815SMickaël Salaün ASSERT_EQ(1, write(reg_fd, ".", 1)); 680e1199815SMickaël Salaün ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 681e1199815SMickaël Salaün ASSERT_EQ(1, read(reg_fd, &buf, 1)); 682e1199815SMickaël Salaün ASSERT_EQ('.', buf); 683e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 684e1199815SMickaël Salaün 685e1199815SMickaël Salaün /* Just in case, double-checks effective actions. */ 686e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 687e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 688e1199815SMickaël Salaün ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 689e1199815SMickaël Salaün ASSERT_EQ(EBADF, errno); 690e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 691e1199815SMickaël Salaün } 692e1199815SMickaël Salaün 693e1199815SMickaël Salaün TEST_F_FORK(layout1, unhandled_access) 694e1199815SMickaël Salaün { 695e1199815SMickaël Salaün const struct rule rules[] = { 696e1199815SMickaël Salaün { 697e1199815SMickaël Salaün .path = dir_s1d2, 698e1199815SMickaël Salaün .access = ACCESS_RO, 699e1199815SMickaël Salaün }, 700135464f9SMickaël Salaün {}, 701e1199815SMickaël Salaün }; 702e1199815SMickaël Salaün /* Here, we only handle read accesses, not write accesses. */ 703e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 704e1199815SMickaël Salaün 705e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 706e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 707e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 708e1199815SMickaël Salaün 709e1199815SMickaël Salaün /* 710e1199815SMickaël Salaün * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 711e1199815SMickaël Salaün * opening for write-only should be allowed, but not read-write. 712e1199815SMickaël Salaün */ 713e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 714e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 715e1199815SMickaël Salaün 716e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 717e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 718e1199815SMickaël Salaün } 719e1199815SMickaël Salaün 720e1199815SMickaël Salaün TEST_F_FORK(layout1, ruleset_overlap) 721e1199815SMickaël Salaün { 722e1199815SMickaël Salaün const struct rule rules[] = { 723e1199815SMickaël Salaün /* These rules should be ORed among them. */ 724e1199815SMickaël Salaün { 725e1199815SMickaël Salaün .path = dir_s1d2, 726e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 727e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 728e1199815SMickaël Salaün }, 729e1199815SMickaël Salaün { 730e1199815SMickaël Salaün .path = dir_s1d2, 731e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 732e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 733e1199815SMickaël Salaün }, 734135464f9SMickaël Salaün {}, 735e1199815SMickaël Salaün }; 736e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 737e1199815SMickaël Salaün 738e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 739e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 740e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 741e1199815SMickaël Salaün 742e1199815SMickaël Salaün /* Checks s1d1 hierarchy. */ 743e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 744e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 745e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 746e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 747e1199815SMickaël Salaün 748e1199815SMickaël Salaün /* Checks s1d2 hierarchy. */ 749e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 750e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 751e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 752e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 753e1199815SMickaël Salaün 754e1199815SMickaël Salaün /* Checks s1d3 hierarchy. */ 755e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 756e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 757e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 758e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 759e1199815SMickaël Salaün } 760e1199815SMickaël Salaün 761*8ba0005fSMickaël Salaün TEST_F_FORK(layout1, layer_rule_unions) 762*8ba0005fSMickaël Salaün { 763*8ba0005fSMickaël Salaün const struct rule layer1[] = { 764*8ba0005fSMickaël Salaün { 765*8ba0005fSMickaël Salaün .path = dir_s1d2, 766*8ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 767*8ba0005fSMickaël Salaün }, 768*8ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 769*8ba0005fSMickaël Salaün { 770*8ba0005fSMickaël Salaün .path = dir_s1d3, 771*8ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 772*8ba0005fSMickaël Salaün }, 773*8ba0005fSMickaël Salaün {}, 774*8ba0005fSMickaël Salaün }; 775*8ba0005fSMickaël Salaün const struct rule layer2[] = { 776*8ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 777*8ba0005fSMickaël Salaün { 778*8ba0005fSMickaël Salaün .path = dir_s1d2, 779*8ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 780*8ba0005fSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 781*8ba0005fSMickaël Salaün }, 782*8ba0005fSMickaël Salaün {}, 783*8ba0005fSMickaël Salaün }; 784*8ba0005fSMickaël Salaün const struct rule layer3[] = { 785*8ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 786*8ba0005fSMickaël Salaün { 787*8ba0005fSMickaël Salaün .path = dir_s1d2, 788*8ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 789*8ba0005fSMickaël Salaün }, 790*8ba0005fSMickaël Salaün {}, 791*8ba0005fSMickaël Salaün }; 792*8ba0005fSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1); 793*8ba0005fSMickaël Salaün 794*8ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 795*8ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 796*8ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 797*8ba0005fSMickaël Salaün 798*8ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer1. */ 799*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 800*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 801*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 802*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 803*8ba0005fSMickaël Salaün 804*8ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer1. */ 805*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 806*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 807*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 808*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 809*8ba0005fSMickaël Salaün 810*8ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer1. */ 811*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 812*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 813*8ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 814*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 815*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 816*8ba0005fSMickaël Salaün 817*8ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 818*8ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2); 819*8ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 820*8ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 821*8ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 822*8ba0005fSMickaël Salaün 823*8ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer2. */ 824*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 825*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 826*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 827*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 828*8ba0005fSMickaël Salaün 829*8ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer2. */ 830*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 831*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 832*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 833*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 834*8ba0005fSMickaël Salaün 835*8ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer2. */ 836*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 837*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 838*8ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 839*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 840*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 841*8ba0005fSMickaël Salaün 842*8ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 843*8ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3); 844*8ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 845*8ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 846*8ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 847*8ba0005fSMickaël Salaün 848*8ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer3. */ 849*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 850*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 851*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 852*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 853*8ba0005fSMickaël Salaün 854*8ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer3. */ 855*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 856*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 857*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 858*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 859*8ba0005fSMickaël Salaün 860*8ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer3. */ 861*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 862*8ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 863*8ba0005fSMickaël Salaün /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 864*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 865*8ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 866*8ba0005fSMickaël Salaün } 867*8ba0005fSMickaël Salaün 868e1199815SMickaël Salaün TEST_F_FORK(layout1, non_overlapping_accesses) 869e1199815SMickaël Salaün { 870e1199815SMickaël Salaün const struct rule layer1[] = { 871e1199815SMickaël Salaün { 872e1199815SMickaël Salaün .path = dir_s1d2, 873e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 874e1199815SMickaël Salaün }, 875135464f9SMickaël Salaün {}, 876e1199815SMickaël Salaün }; 877e1199815SMickaël Salaün const struct rule layer2[] = { 878e1199815SMickaël Salaün { 879e1199815SMickaël Salaün .path = dir_s1d3, 880e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 881e1199815SMickaël Salaün }, 882135464f9SMickaël Salaün {}, 883e1199815SMickaël Salaün }; 884e1199815SMickaël Salaün int ruleset_fd; 885e1199815SMickaël Salaün 886e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 887e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 888e1199815SMickaël Salaün 889371183faSMickaël Salaün ruleset_fd = 890371183faSMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 891e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 892e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 893e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 894e1199815SMickaël Salaün 895e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 896e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 897e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 898e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 899e1199815SMickaël Salaün 900e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 901e1199815SMickaël Salaün layer2); 902e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 903e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 904e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 905e1199815SMickaël Salaün 906e1199815SMickaël Salaün /* Unchanged accesses for file creation. */ 907e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 908e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 909e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 910e1199815SMickaël Salaün 911e1199815SMickaël Salaün /* Checks file removing. */ 912e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d2)); 913e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 914e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 915e1199815SMickaël Salaün } 916e1199815SMickaël Salaün 917e1199815SMickaël Salaün TEST_F_FORK(layout1, interleaved_masked_accesses) 918e1199815SMickaël Salaün { 919e1199815SMickaël Salaün /* 920e1199815SMickaël Salaün * Checks overly restrictive rules: 921e1199815SMickaël Salaün * layer 1: allows R s1d1/s1d2/s1d3/file1 922e1199815SMickaël Salaün * layer 2: allows RW s1d1/s1d2/s1d3 923e1199815SMickaël Salaün * allows W s1d1/s1d2 924e1199815SMickaël Salaün * denies R s1d1/s1d2 925e1199815SMickaël Salaün * layer 3: allows R s1d1 926e1199815SMickaël Salaün * layer 4: allows R s1d1/s1d2 927e1199815SMickaël Salaün * denies W s1d1/s1d2 928e1199815SMickaël Salaün * layer 5: allows R s1d1/s1d2 929e1199815SMickaël Salaün * layer 6: allows X ---- 930e1199815SMickaël Salaün * layer 7: allows W s1d1/s1d2 931e1199815SMickaël Salaün * denies R s1d1/s1d2 932e1199815SMickaël Salaün */ 933e1199815SMickaël Salaün const struct rule layer1_read[] = { 934e1199815SMickaël Salaün /* Allows read access to file1_s1d3 with the first layer. */ 935e1199815SMickaël Salaün { 936e1199815SMickaël Salaün .path = file1_s1d3, 937e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 938e1199815SMickaël Salaün }, 939135464f9SMickaël Salaün {}, 940e1199815SMickaël Salaün }; 941e1199815SMickaël Salaün /* First rule with write restrictions. */ 942e1199815SMickaël Salaün const struct rule layer2_read_write[] = { 943e1199815SMickaël Salaün /* Start by granting read-write access via its parent directory... */ 944e1199815SMickaël Salaün { 945e1199815SMickaël Salaün .path = dir_s1d3, 946e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 947e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 948e1199815SMickaël Salaün }, 949e1199815SMickaël Salaün /* ...but also denies read access via its grandparent directory. */ 950e1199815SMickaël Salaün { 951e1199815SMickaël Salaün .path = dir_s1d2, 952e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 953e1199815SMickaël Salaün }, 954135464f9SMickaël Salaün {}, 955e1199815SMickaël Salaün }; 956e1199815SMickaël Salaün const struct rule layer3_read[] = { 957e1199815SMickaël Salaün /* Allows read access via its great-grandparent directory. */ 958e1199815SMickaël Salaün { 959e1199815SMickaël Salaün .path = dir_s1d1, 960e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 961e1199815SMickaël Salaün }, 962135464f9SMickaël Salaün {}, 963e1199815SMickaël Salaün }; 964e1199815SMickaël Salaün const struct rule layer4_read_write[] = { 965e1199815SMickaël Salaün /* 966e1199815SMickaël Salaün * Try to confuse the deny access by denying write (but not 967e1199815SMickaël Salaün * read) access via its grandparent directory. 968e1199815SMickaël Salaün */ 969e1199815SMickaël Salaün { 970e1199815SMickaël Salaün .path = dir_s1d2, 971e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 972e1199815SMickaël Salaün }, 973135464f9SMickaël Salaün {}, 974e1199815SMickaël Salaün }; 975e1199815SMickaël Salaün const struct rule layer5_read[] = { 976e1199815SMickaël Salaün /* 977e1199815SMickaël Salaün * Try to override layer2's deny read access by explicitly 978e1199815SMickaël Salaün * allowing read access via file1_s1d3's grandparent. 979e1199815SMickaël Salaün */ 980e1199815SMickaël Salaün { 981e1199815SMickaël Salaün .path = dir_s1d2, 982e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 983e1199815SMickaël Salaün }, 984135464f9SMickaël Salaün {}, 985e1199815SMickaël Salaün }; 986e1199815SMickaël Salaün const struct rule layer6_execute[] = { 987e1199815SMickaël Salaün /* 988e1199815SMickaël Salaün * Restricts an unrelated file hierarchy with a new access 989e1199815SMickaël Salaün * (non-overlapping) type. 990e1199815SMickaël Salaün */ 991e1199815SMickaël Salaün { 992e1199815SMickaël Salaün .path = dir_s2d1, 993e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 994e1199815SMickaël Salaün }, 995135464f9SMickaël Salaün {}, 996e1199815SMickaël Salaün }; 997e1199815SMickaël Salaün const struct rule layer7_read_write[] = { 998e1199815SMickaël Salaün /* 999e1199815SMickaël Salaün * Finally, denies read access to file1_s1d3 via its 1000e1199815SMickaël Salaün * grandparent. 1001e1199815SMickaël Salaün */ 1002e1199815SMickaël Salaün { 1003e1199815SMickaël Salaün .path = dir_s1d2, 1004e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1005e1199815SMickaël Salaün }, 1006135464f9SMickaël Salaün {}, 1007e1199815SMickaël Salaün }; 1008e1199815SMickaël Salaün int ruleset_fd; 1009e1199815SMickaël Salaün 1010e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1011e1199815SMickaël Salaün layer1_read); 1012e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1013e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1014e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1015e1199815SMickaël Salaün 1016e1199815SMickaël Salaün /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1017e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1018e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1019e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1020e1199815SMickaël Salaün 1021371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1022371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1023371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1024371183faSMickaël Salaün layer2_read_write); 1025e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1026e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1027e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1028e1199815SMickaël Salaün 1029e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 2. */ 1030e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1031e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1032e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1033e1199815SMickaël Salaün 1034e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1035e1199815SMickaël Salaün layer3_read); 1036e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1037e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1038e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1039e1199815SMickaël Salaün 1040e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 3. */ 1041e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1042e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1043e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1044e1199815SMickaël Salaün 1045e1199815SMickaël Salaün /* This time, denies write access for the file hierarchy. */ 1046371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1047371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1048371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1049371183faSMickaël Salaün layer4_read_write); 1050e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1051e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1052e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1053e1199815SMickaël Salaün 1054e1199815SMickaël Salaün /* 1055e1199815SMickaël Salaün * Checks that the only change with layer 4 is that write access is 1056e1199815SMickaël Salaün * denied. 1057e1199815SMickaël Salaün */ 1058e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1059e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1060e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1061e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1062e1199815SMickaël Salaün 1063e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1064e1199815SMickaël Salaün layer5_read); 1065e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1066e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1067e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1068e1199815SMickaël Salaün 1069e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 5. */ 1070e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1071e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1072e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1073e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1074e1199815SMickaël Salaün 1075e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 1076e1199815SMickaël Salaün layer6_execute); 1077e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1078e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1079e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1080e1199815SMickaël Salaün 1081e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 6. */ 1082e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1083e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1084e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1085e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1086e1199815SMickaël Salaün 1087371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1088371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1089371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1090371183faSMickaël Salaün layer7_read_write); 1091e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1092e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1093e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1094e1199815SMickaël Salaün 1095e1199815SMickaël Salaün /* Checks read access is now denied with layer 7. */ 1096e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1097e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1098e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1099e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1100e1199815SMickaël Salaün } 1101e1199815SMickaël Salaün 1102e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_subset) 1103e1199815SMickaël Salaün { 1104e1199815SMickaël Salaün const struct rule rules[] = { 1105e1199815SMickaël Salaün { 1106e1199815SMickaël Salaün .path = dir_s1d2, 1107e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 1108e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1109e1199815SMickaël Salaün }, 1110135464f9SMickaël Salaün {}, 1111e1199815SMickaël Salaün }; 1112e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1113e1199815SMickaël Salaün 1114e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1115e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1116e1199815SMickaël Salaün 1117e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1118e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1119e1199815SMickaël Salaün 1120e1199815SMickaël Salaün /* Write access is forbidden. */ 1121e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1122e1199815SMickaël Salaün /* Readdir access is allowed. */ 1123e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1124e1199815SMickaël Salaün 1125e1199815SMickaël Salaün /* Write access is forbidden. */ 1126e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1127e1199815SMickaël Salaün /* Readdir access is allowed. */ 1128e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1129e1199815SMickaël Salaün 1130e1199815SMickaël Salaün /* 1131e1199815SMickaël Salaün * Tests shared rule extension: the following rules should not grant 1132e1199815SMickaël Salaün * any new access, only remove some. Once enforced, these rules are 1133e1199815SMickaël Salaün * ANDed with the previous ones. 1134e1199815SMickaël Salaün */ 1135e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1136e1199815SMickaël Salaün dir_s1d2); 1137e1199815SMickaël Salaün /* 1138e1199815SMickaël Salaün * According to ruleset_fd, dir_s1d2 should now have the 1139e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1140e1199815SMickaël Salaün * access rights (even if this directory is opened a second time). 1141e1199815SMickaël Salaün * However, when enforcing this updated ruleset, the ruleset tied to 1142e1199815SMickaël Salaün * the current process (i.e. its domain) will still only have the 1143e1199815SMickaël Salaün * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1144e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1145e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1146e1199815SMickaël Salaün * be a privilege escalation. 1147e1199815SMickaël Salaün */ 1148e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1149e1199815SMickaël Salaün 1150e1199815SMickaël Salaün /* Same tests and results as above. */ 1151e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1152e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1153e1199815SMickaël Salaün 1154e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1155e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1156e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1157e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1158e1199815SMickaël Salaün 1159e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1160e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1161e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1162e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1163e1199815SMickaël Salaün 1164e1199815SMickaël Salaün /* 1165e1199815SMickaël Salaün * Try to get more privileges by adding new access rights to the parent 1166e1199815SMickaël Salaün * directory: dir_s1d1. 1167e1199815SMickaël Salaün */ 1168e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 1169e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1170e1199815SMickaël Salaün 1171e1199815SMickaël Salaün /* Same tests and results as above. */ 1172e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1173e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1174e1199815SMickaël Salaün 1175e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1176e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1177e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1178e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1179e1199815SMickaël Salaün 1180e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1181e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1182e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1183e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1184e1199815SMickaël Salaün 1185e1199815SMickaël Salaün /* 1186e1199815SMickaël Salaün * Now, dir_s1d3 get a new rule tied to it, only allowing 1187e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1188e1199815SMickaël Salaün * that there was no rule tied to it before. 1189e1199815SMickaël Salaün */ 1190e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1191e1199815SMickaël Salaün dir_s1d3); 1192e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1193e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1194e1199815SMickaël Salaün 1195e1199815SMickaël Salaün /* 1196e1199815SMickaël Salaün * Same tests and results as above, except for open(dir_s1d3) which is 1197e1199815SMickaël Salaün * now denied because the new rule mask the rule previously inherited 1198e1199815SMickaël Salaün * from dir_s1d2. 1199e1199815SMickaël Salaün */ 1200e1199815SMickaël Salaün 1201e1199815SMickaël Salaün /* Same tests and results as above. */ 1202e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1203e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1204e1199815SMickaël Salaün 1205e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1206e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1207e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1208e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1209e1199815SMickaël Salaün 1210e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1211e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1212e1199815SMickaël Salaün /* 1213e1199815SMickaël Salaün * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1214e1199815SMickaël Salaün * the same layer. 1215e1199815SMickaël Salaün */ 1216e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1217e1199815SMickaël Salaün } 1218e1199815SMickaël Salaün 1219e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_superset) 1220e1199815SMickaël Salaün { 1221e1199815SMickaël Salaün const struct rule rules[] = { 1222e1199815SMickaël Salaün { 1223e1199815SMickaël Salaün .path = dir_s1d3, 1224e1199815SMickaël Salaün .access = ACCESS_RO, 1225e1199815SMickaël Salaün }, 1226135464f9SMickaël Salaün {}, 1227e1199815SMickaël Salaün }; 1228e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1229e1199815SMickaël Salaün 1230e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1231e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1232e1199815SMickaël Salaün 1233e1199815SMickaël Salaün /* Readdir access is denied for dir_s1d2. */ 1234e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1235e1199815SMickaël Salaün /* Readdir access is allowed for dir_s1d3. */ 1236e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1237e1199815SMickaël Salaün /* File access is allowed for file1_s1d3. */ 1238e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1239e1199815SMickaël Salaün 1240e1199815SMickaël Salaün /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1241371183faSMickaël Salaün add_path_beneath(_metadata, ruleset_fd, 1242371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1243371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1244371183faSMickaël Salaün dir_s1d2); 1245e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1246e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1247e1199815SMickaël Salaün 1248e1199815SMickaël Salaün /* Readdir access is still denied for dir_s1d2. */ 1249e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1250e1199815SMickaël Salaün /* Readdir access is still allowed for dir_s1d3. */ 1251e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1252e1199815SMickaël Salaün /* File access is still allowed for file1_s1d3. */ 1253e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1254e1199815SMickaël Salaün } 1255e1199815SMickaël Salaün 1256e1199815SMickaël Salaün TEST_F_FORK(layout1, max_layers) 1257e1199815SMickaël Salaün { 1258e1199815SMickaël Salaün int i, err; 1259e1199815SMickaël Salaün const struct rule rules[] = { 1260e1199815SMickaël Salaün { 1261e1199815SMickaël Salaün .path = dir_s1d2, 1262e1199815SMickaël Salaün .access = ACCESS_RO, 1263e1199815SMickaël Salaün }, 1264135464f9SMickaël Salaün {}, 1265e1199815SMickaël Salaün }; 1266e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1267e1199815SMickaël Salaün 1268e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 126975c542d6SMickaël Salaün for (i = 0; i < 16; i++) 1270e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1271e1199815SMickaël Salaün 1272e1199815SMickaël Salaün for (i = 0; i < 2; i++) { 1273e1199815SMickaël Salaün err = landlock_restrict_self(ruleset_fd, 0); 1274e1199815SMickaël Salaün ASSERT_EQ(-1, err); 1275e1199815SMickaël Salaün ASSERT_EQ(E2BIG, errno); 1276e1199815SMickaël Salaün } 1277e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1278e1199815SMickaël Salaün } 1279e1199815SMickaël Salaün 1280e1199815SMickaël Salaün TEST_F_FORK(layout1, empty_or_same_ruleset) 1281e1199815SMickaël Salaün { 1282e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = {}; 1283e1199815SMickaël Salaün int ruleset_fd; 1284e1199815SMickaël Salaün 1285e1199815SMickaël Salaün /* Tests empty handled_access_fs. */ 1286371183faSMickaël Salaün ruleset_fd = 1287371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1288e1199815SMickaël Salaün ASSERT_LE(-1, ruleset_fd); 1289e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 1290e1199815SMickaël Salaün 1291e1199815SMickaël Salaün /* Enforces policy which deny read access to all files. */ 1292e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1293371183faSMickaël Salaün ruleset_fd = 1294371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1295e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1296e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1297e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1298e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1299e1199815SMickaël Salaün 1300e1199815SMickaël Salaün /* Nests a policy which deny read access to all directories. */ 1301e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1302371183faSMickaël Salaün ruleset_fd = 1303371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1304e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1305e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1306e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1307e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1308e1199815SMickaël Salaün 1309e1199815SMickaël Salaün /* Enforces a second time with the same ruleset. */ 1310e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1311e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1312e1199815SMickaël Salaün } 1313e1199815SMickaël Salaün 1314e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_on_mountpoint) 1315e1199815SMickaël Salaün { 1316e1199815SMickaël Salaün const struct rule rules[] = { 1317e1199815SMickaël Salaün { 1318e1199815SMickaël Salaün .path = dir_s1d1, 1319e1199815SMickaël Salaün .access = ACCESS_RO, 1320e1199815SMickaël Salaün }, 1321e1199815SMickaël Salaün { 1322e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1323e1199815SMickaël Salaün .path = dir_s3d2, 1324e1199815SMickaël Salaün .access = ACCESS_RO, 1325e1199815SMickaël Salaün }, 1326135464f9SMickaël Salaün {}, 1327e1199815SMickaël Salaün }; 1328e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1329e1199815SMickaël Salaün 1330e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1331e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1332e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1333e1199815SMickaël Salaün 1334e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1335e1199815SMickaël Salaün 1336e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1337e1199815SMickaël Salaün 1338e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1339e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1340e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1341e1199815SMickaël Salaün } 1342e1199815SMickaël Salaün 1343e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_mountpoint) 1344e1199815SMickaël Salaün { 1345e1199815SMickaël Salaün const struct rule rules[] = { 1346e1199815SMickaël Salaün { 1347e1199815SMickaël Salaün .path = dir_s1d1, 1348e1199815SMickaël Salaün .access = ACCESS_RO, 1349e1199815SMickaël Salaün }, 1350e1199815SMickaël Salaün { 1351e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1352e1199815SMickaël Salaün .path = dir_s3d1, 1353e1199815SMickaël Salaün .access = ACCESS_RO, 1354e1199815SMickaël Salaün }, 1355135464f9SMickaël Salaün {}, 1356e1199815SMickaël Salaün }; 1357e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1358e1199815SMickaël Salaün 1359e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1360e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1361e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1362e1199815SMickaël Salaün 1363e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1364e1199815SMickaël Salaün 1365e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1366e1199815SMickaël Salaün 1367e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1368e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1369e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1370e1199815SMickaël Salaün } 1371e1199815SMickaël Salaün 1372e1199815SMickaël Salaün /* 1373e1199815SMickaël Salaün * This test verifies that we can apply a landlock rule on the root directory 1374e1199815SMickaël Salaün * (which might require special handling). 1375e1199815SMickaël Salaün */ 1376e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1377e1199815SMickaël Salaün { 1378e1199815SMickaël Salaün struct rule rules[] = { 1379e1199815SMickaël Salaün { 1380e1199815SMickaël Salaün .path = "/", 1381e1199815SMickaël Salaün .access = ACCESS_RO, 1382e1199815SMickaël Salaün }, 1383135464f9SMickaël Salaün {}, 1384e1199815SMickaël Salaün }; 1385e1199815SMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1386e1199815SMickaël Salaün 1387e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1388e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1389e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1390e1199815SMickaël Salaün 1391e1199815SMickaël Salaün /* Checks allowed access. */ 1392e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY)); 1393e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1394e1199815SMickaël Salaün 1395e1199815SMickaël Salaün rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1396e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1397e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1398e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1399e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1400e1199815SMickaël Salaün 1401e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1402e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1403e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1404e1199815SMickaël Salaün } 1405e1199815SMickaël Salaün 1406e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_deny) 1407e1199815SMickaël Salaün { 1408e1199815SMickaël Salaün const struct rule rules[] = { 1409e1199815SMickaël Salaün { 1410e1199815SMickaël Salaün .path = "/", 1411e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1412e1199815SMickaël Salaün }, 1413135464f9SMickaël Salaün {}, 1414e1199815SMickaël Salaün }; 1415e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1416e1199815SMickaël Salaün 1417e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1418e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1419e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1420e1199815SMickaël Salaün 1421e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1422e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1423e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1424e1199815SMickaël Salaün } 1425e1199815SMickaël Salaün 1426e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_inside_mount_ns) 1427e1199815SMickaël Salaün { 1428e1199815SMickaël Salaün const struct rule rules[] = { 1429e1199815SMickaël Salaün { 1430e1199815SMickaël Salaün .path = "s3d3", 1431e1199815SMickaël Salaün .access = ACCESS_RO, 1432e1199815SMickaël Salaün }, 1433135464f9SMickaël Salaün {}, 1434e1199815SMickaël Salaün }; 1435e1199815SMickaël Salaün int ruleset_fd; 1436e1199815SMickaël Salaün 1437e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 143887129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1439371183faSMickaël Salaün { 1440e1199815SMickaël Salaün TH_LOG("Failed to pivot root: %s", strerror(errno)); 1441e1199815SMickaël Salaün }; 1442e1199815SMickaël Salaün ASSERT_EQ(0, chdir("/")); 1443e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1444e1199815SMickaël Salaün 1445e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1446e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1447e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1448e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1449e1199815SMickaël Salaün 1450e1199815SMickaël Salaün ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1451e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1452e1199815SMickaël Salaün } 1453e1199815SMickaël Salaün 1454e1199815SMickaël Salaün TEST_F_FORK(layout1, mount_and_pivot) 1455e1199815SMickaël Salaün { 1456e1199815SMickaël Salaün const struct rule rules[] = { 1457e1199815SMickaël Salaün { 1458e1199815SMickaël Salaün .path = dir_s3d2, 1459e1199815SMickaël Salaün .access = ACCESS_RO, 1460e1199815SMickaël Salaün }, 1461135464f9SMickaël Salaün {}, 1462e1199815SMickaël Salaün }; 1463e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1464e1199815SMickaël Salaün 1465e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1466e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1467e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1468e1199815SMickaël Salaün 1469e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1470e1199815SMickaël Salaün ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1471e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 147287129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1473e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1474e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1475e1199815SMickaël Salaün } 1476e1199815SMickaël Salaün 1477e1199815SMickaël Salaün TEST_F_FORK(layout1, move_mount) 1478e1199815SMickaël Salaün { 1479e1199815SMickaël Salaün const struct rule rules[] = { 1480e1199815SMickaël Salaün { 1481e1199815SMickaël Salaün .path = dir_s3d2, 1482e1199815SMickaël Salaün .access = ACCESS_RO, 1483e1199815SMickaël Salaün }, 1484135464f9SMickaël Salaün {}, 1485e1199815SMickaël Salaün }; 1486e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1487e1199815SMickaël Salaün 1488e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1489e1199815SMickaël Salaün 1490e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 149187129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1492371183faSMickaël Salaün dir_s1d2, 0)) 1493371183faSMickaël Salaün { 1494e1199815SMickaël Salaün TH_LOG("Failed to move mount: %s", strerror(errno)); 1495e1199815SMickaël Salaün } 1496e1199815SMickaël Salaün 149787129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1498e1199815SMickaël Salaün dir_s3d2, 0)); 1499e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1500e1199815SMickaël Salaün 1501e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1502e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1503e1199815SMickaël Salaün 1504e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 150587129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1506e1199815SMickaël Salaün dir_s1d2, 0)); 1507e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1508e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1509e1199815SMickaël Salaün } 1510e1199815SMickaël Salaün 1511e1199815SMickaël Salaün TEST_F_FORK(layout1, release_inodes) 1512e1199815SMickaël Salaün { 1513e1199815SMickaël Salaün const struct rule rules[] = { 1514e1199815SMickaël Salaün { 1515e1199815SMickaël Salaün .path = dir_s1d1, 1516e1199815SMickaël Salaün .access = ACCESS_RO, 1517e1199815SMickaël Salaün }, 1518e1199815SMickaël Salaün { 1519e1199815SMickaël Salaün .path = dir_s3d2, 1520e1199815SMickaël Salaün .access = ACCESS_RO, 1521e1199815SMickaël Salaün }, 1522e1199815SMickaël Salaün { 1523e1199815SMickaël Salaün .path = dir_s3d3, 1524e1199815SMickaël Salaün .access = ACCESS_RO, 1525e1199815SMickaël Salaün }, 1526135464f9SMickaël Salaün {}, 1527e1199815SMickaël Salaün }; 1528e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1529e1199815SMickaël Salaün 1530e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1531e1199815SMickaël Salaün /* Unmount a file hierarchy while it is being used by a ruleset. */ 1532e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1533e1199815SMickaël Salaün ASSERT_EQ(0, umount(dir_s3d2)); 1534e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1535e1199815SMickaël Salaün 1536e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1537e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1538e1199815SMickaël Salaün 1539e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1540e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1541e1199815SMickaël Salaün /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1542e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1543e1199815SMickaël Salaün } 1544e1199815SMickaël Salaün 1545e1199815SMickaël Salaün enum relative_access { 1546e1199815SMickaël Salaün REL_OPEN, 1547e1199815SMickaël Salaün REL_CHDIR, 1548e1199815SMickaël Salaün REL_CHROOT_ONLY, 1549e1199815SMickaël Salaün REL_CHROOT_CHDIR, 1550e1199815SMickaël Salaün }; 1551e1199815SMickaël Salaün 1552e1199815SMickaël Salaün static void test_relative_path(struct __test_metadata *const _metadata, 1553e1199815SMickaël Salaün const enum relative_access rel) 1554e1199815SMickaël Salaün { 1555e1199815SMickaël Salaün /* 1556e1199815SMickaël Salaün * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1557e1199815SMickaël Salaün * is not a disconnected root directory). 1558e1199815SMickaël Salaün */ 1559e1199815SMickaël Salaün const struct rule layer1_base[] = { 1560e1199815SMickaël Salaün { 1561e1199815SMickaël Salaün .path = TMP_DIR, 1562e1199815SMickaël Salaün .access = ACCESS_RO, 1563e1199815SMickaël Salaün }, 1564135464f9SMickaël Salaün {}, 1565e1199815SMickaël Salaün }; 1566e1199815SMickaël Salaün const struct rule layer2_subs[] = { 1567e1199815SMickaël Salaün { 1568e1199815SMickaël Salaün .path = dir_s1d2, 1569e1199815SMickaël Salaün .access = ACCESS_RO, 1570e1199815SMickaël Salaün }, 1571e1199815SMickaël Salaün { 1572e1199815SMickaël Salaün .path = dir_s2d2, 1573e1199815SMickaël Salaün .access = ACCESS_RO, 1574e1199815SMickaël Salaün }, 1575135464f9SMickaël Salaün {}, 1576e1199815SMickaël Salaün }; 1577e1199815SMickaël Salaün int dirfd, ruleset_fd; 1578e1199815SMickaël Salaün 1579e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1580e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1581e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1582e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1583e1199815SMickaël Salaün 1584e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1585e1199815SMickaël Salaün 1586e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1587e1199815SMickaël Salaün switch (rel) { 1588e1199815SMickaël Salaün case REL_OPEN: 1589e1199815SMickaël Salaün case REL_CHDIR: 1590e1199815SMickaël Salaün break; 1591e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1592e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s2d2)); 1593e1199815SMickaël Salaün break; 1594e1199815SMickaël Salaün case REL_CHROOT_CHDIR: 1595e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1596e1199815SMickaël Salaün break; 1597e1199815SMickaël Salaün default: 1598e1199815SMickaël Salaün ASSERT_TRUE(false); 1599e1199815SMickaël Salaün return; 1600e1199815SMickaël Salaün } 1601e1199815SMickaël Salaün 1602e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_CHROOT); 1603e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1604e1199815SMickaël Salaün 1605e1199815SMickaël Salaün switch (rel) { 1606e1199815SMickaël Salaün case REL_OPEN: 1607e1199815SMickaël Salaün dirfd = open(dir_s1d2, O_DIRECTORY); 1608e1199815SMickaël Salaün ASSERT_LE(0, dirfd); 1609e1199815SMickaël Salaün break; 1610e1199815SMickaël Salaün case REL_CHDIR: 1611e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1612e1199815SMickaël Salaün dirfd = AT_FDCWD; 1613e1199815SMickaël Salaün break; 1614e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1615e1199815SMickaël Salaün /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1616371183faSMickaël Salaün ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 1617371183faSMickaël Salaün { 1618e1199815SMickaël Salaün TH_LOG("Failed to chroot: %s", strerror(errno)); 1619e1199815SMickaël Salaün } 1620e1199815SMickaël Salaün dirfd = AT_FDCWD; 1621e1199815SMickaël Salaün break; 1622e1199815SMickaël Salaün case REL_CHROOT_CHDIR: 1623e1199815SMickaël Salaün /* Do chroot into dir_s1d2. */ 1624371183faSMickaël Salaün ASSERT_EQ(0, chroot(".")) 1625371183faSMickaël Salaün { 1626e1199815SMickaël Salaün TH_LOG("Failed to chroot: %s", strerror(errno)); 1627e1199815SMickaël Salaün } 1628e1199815SMickaël Salaün dirfd = AT_FDCWD; 1629e1199815SMickaël Salaün break; 1630e1199815SMickaël Salaün } 1631e1199815SMickaël Salaün 1632e1199815SMickaël Salaün ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1633e1199815SMickaël Salaün test_open_rel(dirfd, "..", O_RDONLY)); 1634e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1635e1199815SMickaël Salaün 1636e1199815SMickaël Salaün if (rel == REL_CHROOT_ONLY) { 1637e1199815SMickaël Salaün /* The current directory is dir_s2d2. */ 1638e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1639e1199815SMickaël Salaün } else { 1640e1199815SMickaël Salaün /* The current directory is dir_s1d2. */ 1641e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1642e1199815SMickaël Salaün } 1643e1199815SMickaël Salaün 1644e1199815SMickaël Salaün if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1645e1199815SMickaël Salaün /* Checks the root dir_s1d2. */ 1646e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1647e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1648e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1649e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1650e1199815SMickaël Salaün } 1651e1199815SMickaël Salaün 1652e1199815SMickaël Salaün if (rel != REL_CHROOT_CHDIR) { 1653e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1654e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1655371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1656371183faSMickaël Salaün O_RDONLY)); 1657e1199815SMickaël Salaün 1658e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1659e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1660371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1661371183faSMickaël Salaün O_RDONLY)); 1662e1199815SMickaël Salaün } 1663e1199815SMickaël Salaün 1664e1199815SMickaël Salaün if (rel == REL_OPEN) 1665e1199815SMickaël Salaün ASSERT_EQ(0, close(dirfd)); 1666e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1667e1199815SMickaël Salaün } 1668e1199815SMickaël Salaün 1669e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_open) 1670e1199815SMickaël Salaün { 1671e1199815SMickaël Salaün test_relative_path(_metadata, REL_OPEN); 1672e1199815SMickaël Salaün } 1673e1199815SMickaël Salaün 1674e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chdir) 1675e1199815SMickaël Salaün { 1676e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHDIR); 1677e1199815SMickaël Salaün } 1678e1199815SMickaël Salaün 1679e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_only) 1680e1199815SMickaël Salaün { 1681e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_ONLY); 1682e1199815SMickaël Salaün } 1683e1199815SMickaël Salaün 1684e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_chdir) 1685e1199815SMickaël Salaün { 1686e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_CHDIR); 1687e1199815SMickaël Salaün } 1688e1199815SMickaël Salaün 1689e1199815SMickaël Salaün static void copy_binary(struct __test_metadata *const _metadata, 1690e1199815SMickaël Salaün const char *const dst_path) 1691e1199815SMickaël Salaün { 1692e1199815SMickaël Salaün int dst_fd, src_fd; 1693e1199815SMickaël Salaün struct stat statbuf; 1694e1199815SMickaël Salaün 1695e1199815SMickaël Salaün dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1696371183faSMickaël Salaün ASSERT_LE(0, dst_fd) 1697371183faSMickaël Salaün { 1698371183faSMickaël Salaün TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1699e1199815SMickaël Salaün } 1700e1199815SMickaël Salaün src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1701371183faSMickaël Salaün ASSERT_LE(0, src_fd) 1702371183faSMickaël Salaün { 1703e1199815SMickaël Salaün TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1704e1199815SMickaël Salaün strerror(errno)); 1705e1199815SMickaël Salaün } 1706e1199815SMickaël Salaün ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1707371183faSMickaël Salaün ASSERT_EQ(statbuf.st_size, 1708371183faSMickaël Salaün sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 1709e1199815SMickaël Salaün ASSERT_EQ(0, close(src_fd)); 1710e1199815SMickaël Salaün ASSERT_EQ(0, close(dst_fd)); 1711e1199815SMickaël Salaün } 1712e1199815SMickaël Salaün 1713371183faSMickaël Salaün static void test_execute(struct __test_metadata *const _metadata, const int err, 1714371183faSMickaël Salaün const char *const path) 1715e1199815SMickaël Salaün { 1716e1199815SMickaël Salaün int status; 1717e1199815SMickaël Salaün char *const argv[] = { (char *)path, NULL }; 1718e1199815SMickaël Salaün const pid_t child = fork(); 1719e1199815SMickaël Salaün 1720e1199815SMickaël Salaün ASSERT_LE(0, child); 1721e1199815SMickaël Salaün if (child == 0) { 1722371183faSMickaël Salaün ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 1723371183faSMickaël Salaün { 1724e1199815SMickaël Salaün TH_LOG("Failed to execute \"%s\": %s", path, 1725e1199815SMickaël Salaün strerror(errno)); 1726e1199815SMickaël Salaün }; 1727e1199815SMickaël Salaün ASSERT_EQ(err, errno); 1728e1199815SMickaël Salaün _exit(_metadata->passed ? 2 : 1); 1729e1199815SMickaël Salaün return; 1730e1199815SMickaël Salaün } 1731e1199815SMickaël Salaün ASSERT_EQ(child, waitpid(child, &status, 0)); 1732e1199815SMickaël Salaün ASSERT_EQ(1, WIFEXITED(status)); 1733371183faSMickaël Salaün ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 1734371183faSMickaël Salaün { 1735e1199815SMickaël Salaün TH_LOG("Unexpected return code for \"%s\": %s", path, 1736e1199815SMickaël Salaün strerror(errno)); 1737e1199815SMickaël Salaün }; 1738e1199815SMickaël Salaün } 1739e1199815SMickaël Salaün 1740e1199815SMickaël Salaün TEST_F_FORK(layout1, execute) 1741e1199815SMickaël Salaün { 1742e1199815SMickaël Salaün const struct rule rules[] = { 1743e1199815SMickaël Salaün { 1744e1199815SMickaël Salaün .path = dir_s1d2, 1745e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 1746e1199815SMickaël Salaün }, 1747135464f9SMickaël Salaün {}, 1748e1199815SMickaël Salaün }; 1749371183faSMickaël Salaün const int ruleset_fd = 1750371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1751e1199815SMickaël Salaün 1752e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1753e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d1); 1754e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d2); 1755e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d3); 1756e1199815SMickaël Salaün 1757e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1758e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1759e1199815SMickaël Salaün 1760e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1761e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1762e1199815SMickaël Salaün test_execute(_metadata, EACCES, file1_s1d1); 1763e1199815SMickaël Salaün 1764e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1765e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1766e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d2); 1767e1199815SMickaël Salaün 1768e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1769e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1770e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d3); 1771e1199815SMickaël Salaün } 1772e1199815SMickaël Salaün 1773e1199815SMickaël Salaün TEST_F_FORK(layout1, link) 1774e1199815SMickaël Salaün { 17756a1bdd4aSMickaël Salaün const struct rule layer1[] = { 1776e1199815SMickaël Salaün { 1777e1199815SMickaël Salaün .path = dir_s1d2, 1778e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1779e1199815SMickaël Salaün }, 1780135464f9SMickaël Salaün {}, 1781e1199815SMickaël Salaün }; 17826a1bdd4aSMickaël Salaün const struct rule layer2[] = { 17836a1bdd4aSMickaël Salaün { 17846a1bdd4aSMickaël Salaün .path = dir_s1d3, 17856a1bdd4aSMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 17866a1bdd4aSMickaël Salaün }, 17876a1bdd4aSMickaël Salaün {}, 17886a1bdd4aSMickaël Salaün }; 17896a1bdd4aSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 1790e1199815SMickaël Salaün 1791e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1792e1199815SMickaël Salaün 1793e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 1794e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1795e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1796e1199815SMickaël Salaün 1797e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1798e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1799e1199815SMickaël Salaün 1800e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1801e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 18026a1bdd4aSMickaël Salaün 1803e1199815SMickaël Salaün /* Denies linking because of reparenting. */ 1804e1199815SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1805e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1806e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1807e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 18086a1bdd4aSMickaël Salaün ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 18096a1bdd4aSMickaël Salaün ASSERT_EQ(EXDEV, errno); 1810e1199815SMickaël Salaün 1811e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1812e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 18136a1bdd4aSMickaël Salaün 18146a1bdd4aSMickaël Salaün /* Prepares for next unlinks. */ 18156a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 18166a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 18176a1bdd4aSMickaël Salaün 18186a1bdd4aSMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 18196a1bdd4aSMickaël Salaün ASSERT_LE(0, ruleset_fd); 18206a1bdd4aSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 18216a1bdd4aSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 18226a1bdd4aSMickaël Salaün 18236a1bdd4aSMickaël Salaün /* Checks that linkind doesn't require the ability to delete a file. */ 18246a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 18256a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1826e1199815SMickaël Salaün } 1827e1199815SMickaël Salaün 1828e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_file) 1829e1199815SMickaël Salaün { 1830e1199815SMickaël Salaün const struct rule rules[] = { 1831e1199815SMickaël Salaün { 1832e1199815SMickaël Salaün .path = dir_s1d3, 1833e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1834e1199815SMickaël Salaün }, 1835e1199815SMickaël Salaün { 1836e1199815SMickaël Salaün .path = dir_s2d2, 1837e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1838e1199815SMickaël Salaün }, 1839135464f9SMickaël Salaün {}, 1840e1199815SMickaël Salaün }; 1841371183faSMickaël Salaün const int ruleset_fd = 1842371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1843e1199815SMickaël Salaün 1844e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1845e1199815SMickaël Salaün 1846e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1847e1199815SMickaël Salaün 1848e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1849e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1850e1199815SMickaël Salaün 1851e1199815SMickaël Salaün /* 1852e1199815SMickaël Salaün * Tries to replace a file, from a directory that allows file removal, 1853e1199815SMickaël Salaün * but to a different directory (which also allows file removal). 1854e1199815SMickaël Salaün */ 1855e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1856e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1857e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 1858e1199815SMickaël Salaün RENAME_EXCHANGE)); 1859e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1860e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 1861e1199815SMickaël Salaün RENAME_EXCHANGE)); 1862e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1863e1199815SMickaël Salaün 1864e1199815SMickaël Salaün /* 1865e1199815SMickaël Salaün * Tries to replace a file, from a directory that denies file removal, 1866e1199815SMickaël Salaün * to a different directory (which allows file removal). 1867e1199815SMickaël Salaün */ 1868e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1869e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1870e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 1871e1199815SMickaël Salaün RENAME_EXCHANGE)); 1872e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1873e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 1874e1199815SMickaël Salaün RENAME_EXCHANGE)); 1875e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1876e1199815SMickaël Salaün 1877e1199815SMickaël Salaün /* Exchanges files and directories that partially allow removal. */ 1878e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 1879e1199815SMickaël Salaün RENAME_EXCHANGE)); 1880e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 18816a1bdd4aSMickaël Salaün /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 18826a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 18836a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1884e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 1885e1199815SMickaël Salaün RENAME_EXCHANGE)); 1886e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 18876a1bdd4aSMickaël Salaün /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 18886a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 18896a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1890e1199815SMickaël Salaün 1891e1199815SMickaël Salaün /* Renames files with different parents. */ 1892e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 1893e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1894e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1895e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1896e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1897e1199815SMickaël Salaün 1898e1199815SMickaël Salaün /* Exchanges and renames files with same parent. */ 1899e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 1900e1199815SMickaël Salaün RENAME_EXCHANGE)); 1901e1199815SMickaël Salaün ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 1902e1199815SMickaël Salaün 1903e1199815SMickaël Salaün /* Exchanges files and directories with same parent, twice. */ 1904e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1905e1199815SMickaël Salaün RENAME_EXCHANGE)); 1906e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1907e1199815SMickaël Salaün RENAME_EXCHANGE)); 1908e1199815SMickaël Salaün } 1909e1199815SMickaël Salaün 1910e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_dir) 1911e1199815SMickaël Salaün { 1912e1199815SMickaël Salaün const struct rule rules[] = { 1913e1199815SMickaël Salaün { 1914e1199815SMickaël Salaün .path = dir_s1d2, 1915e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1916e1199815SMickaël Salaün }, 1917e1199815SMickaël Salaün { 1918e1199815SMickaël Salaün .path = dir_s2d1, 1919e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1920e1199815SMickaël Salaün }, 1921135464f9SMickaël Salaün {}, 1922e1199815SMickaël Salaün }; 1923371183faSMickaël Salaün const int ruleset_fd = 1924371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1925e1199815SMickaël Salaün 1926e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1927e1199815SMickaël Salaün 1928e1199815SMickaël Salaün /* Empties dir_s1d3 to allow renaming. */ 1929e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1930e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 1931e1199815SMickaël Salaün 1932e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1933e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1934e1199815SMickaël Salaün 1935e1199815SMickaël Salaün /* Exchanges and renames directory to a different parent. */ 1936e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 1937e1199815SMickaël Salaün RENAME_EXCHANGE)); 1938e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1939e1199815SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 1940e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1941e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 1942e1199815SMickaël Salaün RENAME_EXCHANGE)); 1943e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1944e1199815SMickaël Salaün 1945e1199815SMickaël Salaün /* 1946e1199815SMickaël Salaün * Exchanges directory to the same parent, which doesn't allow 1947e1199815SMickaël Salaün * directory removal. 1948e1199815SMickaël Salaün */ 1949e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 1950e1199815SMickaël Salaün RENAME_EXCHANGE)); 1951e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19526a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 19536a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 19546a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1955e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 1956e1199815SMickaël Salaün RENAME_EXCHANGE)); 1957e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19586a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 19596a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 19606a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1961e1199815SMickaël Salaün 1962e1199815SMickaël Salaün /* 1963e1199815SMickaël Salaün * Exchanges and renames directory to the same parent, which allows 1964e1199815SMickaël Salaün * directory removal. 1965e1199815SMickaël Salaün */ 1966e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 1967e1199815SMickaël Salaün RENAME_EXCHANGE)); 1968e1199815SMickaël Salaün ASSERT_EQ(0, unlink(dir_s1d3)); 1969e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 1970e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 1971e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 1972e1199815SMickaël Salaün } 1973e1199815SMickaël Salaün 1974e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_dir) 1975e1199815SMickaël Salaün { 1976e1199815SMickaël Salaün const struct rule rules[] = { 1977e1199815SMickaël Salaün { 1978e1199815SMickaël Salaün .path = dir_s1d2, 1979e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1980e1199815SMickaël Salaün }, 1981135464f9SMickaël Salaün {}, 1982e1199815SMickaël Salaün }; 1983371183faSMickaël Salaün const int ruleset_fd = 1984371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1985e1199815SMickaël Salaün 1986e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1987e1199815SMickaël Salaün 1988e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 1989e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1990e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1991e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 1992e1199815SMickaël Salaün 1993e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1994e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1995e1199815SMickaël Salaün 1996e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 1997e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 1998e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 1999e1199815SMickaël Salaün 2000e1199815SMickaël Salaün /* dir_s1d2 itself cannot be removed. */ 2001e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d2)); 2002e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2003e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 2004e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2005e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d1)); 2006e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2007e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 2008e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2009e1199815SMickaël Salaün } 2010e1199815SMickaël Salaün 2011e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_file) 2012e1199815SMickaël Salaün { 2013e1199815SMickaël Salaün const struct rule rules[] = { 2014e1199815SMickaël Salaün { 2015e1199815SMickaël Salaün .path = dir_s1d2, 2016e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2017e1199815SMickaël Salaün }, 2018135464f9SMickaël Salaün {}, 2019e1199815SMickaël Salaün }; 2020371183faSMickaël Salaün const int ruleset_fd = 2021371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2022e1199815SMickaël Salaün 2023e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2024e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2025e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2026e1199815SMickaël Salaün 2027e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d1)); 2028e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2029e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 2030e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2031e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2032e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 2033e1199815SMickaël Salaün } 2034e1199815SMickaël Salaün 2035e1199815SMickaël Salaün static void test_make_file(struct __test_metadata *const _metadata, 2036371183faSMickaël Salaün const __u64 access, const mode_t mode, 2037371183faSMickaël Salaün const dev_t dev) 2038e1199815SMickaël Salaün { 2039e1199815SMickaël Salaün const struct rule rules[] = { 2040e1199815SMickaël Salaün { 2041e1199815SMickaël Salaün .path = dir_s1d2, 2042e1199815SMickaël Salaün .access = access, 2043e1199815SMickaël Salaün }, 2044135464f9SMickaël Salaün {}, 2045e1199815SMickaël Salaün }; 2046e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, access, rules); 2047e1199815SMickaël Salaün 2048e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2049e1199815SMickaël Salaün 2050e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2051e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 2052371183faSMickaël Salaün ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 2053371183faSMickaël Salaün { 2054371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 2055371183faSMickaël Salaün strerror(errno)); 2056e1199815SMickaël Salaün }; 2057e1199815SMickaël Salaün 2058e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2059e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2060e1199815SMickaël Salaün 2061e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2062e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2063e1199815SMickaël Salaün 2064e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2065e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2066e1199815SMickaël Salaün 2067e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 2068e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2069e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2070e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2071e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2072e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2073e1199815SMickaël Salaün 2074371183faSMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 2075371183faSMickaël Salaün { 2076371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 2077371183faSMickaël Salaün strerror(errno)); 2078e1199815SMickaël Salaün }; 2079e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 2080e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2081e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 2082e1199815SMickaël Salaün 2083e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 2084e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 2085e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2086e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 2087e1199815SMickaël Salaün } 2088e1199815SMickaël Salaün 2089e1199815SMickaël Salaün TEST_F_FORK(layout1, make_char) 2090e1199815SMickaël Salaün { 2091e1199815SMickaël Salaün /* Creates a /dev/null device. */ 2092e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 2093e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 2094e1199815SMickaël Salaün makedev(1, 3)); 2095e1199815SMickaël Salaün } 2096e1199815SMickaël Salaün 2097e1199815SMickaël Salaün TEST_F_FORK(layout1, make_block) 2098e1199815SMickaël Salaün { 2099e1199815SMickaël Salaün /* Creates a /dev/loop0 device. */ 2100e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 2101e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 2102e1199815SMickaël Salaün makedev(7, 0)); 2103e1199815SMickaël Salaün } 2104e1199815SMickaël Salaün 2105e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_1) 2106e1199815SMickaël Salaün { 2107e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 2108e1199815SMickaël Salaün } 2109e1199815SMickaël Salaün 2110e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_2) 2111e1199815SMickaël Salaün { 2112e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 2113e1199815SMickaël Salaün } 2114e1199815SMickaël Salaün 2115e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sock) 2116e1199815SMickaël Salaün { 2117e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 2118e1199815SMickaël Salaün } 2119e1199815SMickaël Salaün 2120e1199815SMickaël Salaün TEST_F_FORK(layout1, make_fifo) 2121e1199815SMickaël Salaün { 2122e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 2123e1199815SMickaël Salaün } 2124e1199815SMickaël Salaün 2125e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sym) 2126e1199815SMickaël Salaün { 2127e1199815SMickaël Salaün const struct rule rules[] = { 2128e1199815SMickaël Salaün { 2129e1199815SMickaël Salaün .path = dir_s1d2, 2130e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 2131e1199815SMickaël Salaün }, 2132135464f9SMickaël Salaün {}, 2133e1199815SMickaël Salaün }; 2134371183faSMickaël Salaün const int ruleset_fd = 2135371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2136e1199815SMickaël Salaün 2137e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2138e1199815SMickaël Salaün 2139e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2140e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 2141e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file2_s1d1)); 2142e1199815SMickaël Salaün 2143e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2144e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2145e1199815SMickaël Salaün 2146e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2147e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2148e1199815SMickaël Salaün 2149e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2150e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2151e1199815SMickaël Salaün 2152e1199815SMickaël Salaün ASSERT_EQ(-1, symlink("none", file1_s1d1)); 2153e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2154e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2155e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2156e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2157e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2158e1199815SMickaël Salaün 2159e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d2)); 2160e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 2161e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2162e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 2163e1199815SMickaël Salaün 2164e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d3)); 2165e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 2166e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2167e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 2168e1199815SMickaël Salaün } 2169e1199815SMickaël Salaün 2170e1199815SMickaël Salaün TEST_F_FORK(layout1, make_dir) 2171e1199815SMickaël Salaün { 2172e1199815SMickaël Salaün const struct rule rules[] = { 2173e1199815SMickaël Salaün { 2174e1199815SMickaël Salaün .path = dir_s1d2, 2175e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2176e1199815SMickaël Salaün }, 2177135464f9SMickaël Salaün {}, 2178e1199815SMickaël Salaün }; 2179371183faSMickaël Salaün const int ruleset_fd = 2180371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2181e1199815SMickaël Salaün 2182e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2183e1199815SMickaël Salaün 2184e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2185e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2186e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2187e1199815SMickaël Salaün 2188e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2189e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2190e1199815SMickaël Salaün 2191e1199815SMickaël Salaün /* Uses file_* as directory names. */ 2192e1199815SMickaël Salaün ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 2193e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2194e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2195e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 2196e1199815SMickaël Salaün } 2197e1199815SMickaël Salaün 2198e1199815SMickaël Salaün static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 2199e1199815SMickaël Salaün const int open_flags) 2200e1199815SMickaël Salaün { 2201e1199815SMickaël Salaün static const char path_template[] = "/proc/self/fd/%d"; 2202e1199815SMickaël Salaün char procfd_path[sizeof(path_template) + 10]; 2203371183faSMickaël Salaün const int procfd_path_size = 2204371183faSMickaël Salaün snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 2205e1199815SMickaël Salaün 2206e1199815SMickaël Salaün ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 2207e1199815SMickaël Salaün return open(procfd_path, open_flags); 2208e1199815SMickaël Salaün } 2209e1199815SMickaël Salaün 2210e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_unlinked_file) 2211e1199815SMickaël Salaün { 2212e1199815SMickaël Salaün const struct rule rules[] = { 2213e1199815SMickaël Salaün { 2214e1199815SMickaël Salaün .path = file1_s1d2, 2215e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2216e1199815SMickaël Salaün }, 2217135464f9SMickaël Salaün {}, 2218e1199815SMickaël Salaün }; 2219e1199815SMickaël Salaün int reg_fd, proc_fd; 2220371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 2221371183faSMickaël Salaün _metadata, 2222371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 2223371183faSMickaël Salaün rules); 2224e1199815SMickaël Salaün 2225e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2226e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2227e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2228e1199815SMickaël Salaün 2229e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 2230e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2231e1199815SMickaël Salaün reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 2232e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 2233e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2234e1199815SMickaël Salaün 2235e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 2236e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 2237e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 2238e1199815SMickaël Salaün 2239e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 2240371183faSMickaël Salaün ASSERT_EQ(-1, proc_fd) 2241371183faSMickaël Salaün { 2242371183faSMickaël Salaün TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 2243371183faSMickaël Salaün strerror(errno)); 2244e1199815SMickaël Salaün } 2245e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2246e1199815SMickaël Salaün 2247e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 2248e1199815SMickaël Salaün } 2249e1199815SMickaël Salaün 2250e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_pipe) 2251e1199815SMickaël Salaün { 2252e1199815SMickaël Salaün int proc_fd; 2253e1199815SMickaël Salaün int pipe_fds[2]; 2254e1199815SMickaël Salaün char buf = '\0'; 2255e1199815SMickaël Salaün const struct rule rules[] = { 2256e1199815SMickaël Salaün { 2257e1199815SMickaël Salaün .path = dir_s1d2, 2258e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2259e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 2260e1199815SMickaël Salaün }, 2261135464f9SMickaël Salaün {}, 2262e1199815SMickaël Salaün }; 2263e1199815SMickaël Salaün /* Limits read and write access to files tied to the filesystem. */ 2264371183faSMickaël Salaün const int ruleset_fd = 2265371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2266e1199815SMickaël Salaün 2267e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2268e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2269e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2270e1199815SMickaël Salaün 2271e1199815SMickaël Salaün /* Checks enforcement for normal files. */ 2272e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 2273e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 2274e1199815SMickaël Salaün 2275e1199815SMickaël Salaün /* Checks access to pipes through FD. */ 2276e1199815SMickaël Salaün ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 2277371183faSMickaël Salaün ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 2278371183faSMickaël Salaün { 2279e1199815SMickaël Salaün TH_LOG("Failed to write in pipe: %s", strerror(errno)); 2280e1199815SMickaël Salaün } 2281e1199815SMickaël Salaün ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 2282e1199815SMickaël Salaün ASSERT_EQ('.', buf); 2283e1199815SMickaël Salaün 2284e1199815SMickaël Salaün /* Checks write access to pipe through /proc/self/fd . */ 2285e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 2286e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 2287371183faSMickaël Salaün ASSERT_EQ(1, write(proc_fd, ".", 1)) 2288371183faSMickaël Salaün { 2289e1199815SMickaël Salaün TH_LOG("Failed to write through /proc/self/fd/%d: %s", 2290e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 2291e1199815SMickaël Salaün } 2292e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 2293e1199815SMickaël Salaün 2294e1199815SMickaël Salaün /* Checks read access to pipe through /proc/self/fd . */ 2295e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 2296e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 2297e1199815SMickaël Salaün buf = '\0'; 2298371183faSMickaël Salaün ASSERT_EQ(1, read(proc_fd, &buf, 1)) 2299371183faSMickaël Salaün { 2300e1199815SMickaël Salaün TH_LOG("Failed to read through /proc/self/fd/%d: %s", 2301e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 2302e1199815SMickaël Salaün } 2303e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 2304e1199815SMickaël Salaün 2305e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[0])); 2306e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[1])); 2307e1199815SMickaël Salaün } 2308e1199815SMickaël Salaün 23094598d9abSMickaël Salaün /* clang-format off */ 23104598d9abSMickaël Salaün FIXTURE(layout1_bind) {}; 23114598d9abSMickaël Salaün /* clang-format on */ 2312e1199815SMickaël Salaün 2313e1199815SMickaël Salaün FIXTURE_SETUP(layout1_bind) 2314e1199815SMickaël Salaün { 2315e1199815SMickaël Salaün prepare_layout(_metadata); 2316e1199815SMickaël Salaün 2317e1199815SMickaël Salaün create_layout1(_metadata); 2318e1199815SMickaël Salaün 2319e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2320e1199815SMickaël Salaün ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 2321e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2322e1199815SMickaël Salaün } 2323e1199815SMickaël Salaün 2324e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1_bind) 2325e1199815SMickaël Salaün { 2326e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2327e1199815SMickaël Salaün EXPECT_EQ(0, umount(dir_s2d2)); 2328e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2329e1199815SMickaël Salaün 2330e1199815SMickaël Salaün remove_layout1(_metadata); 2331e1199815SMickaël Salaün 2332e1199815SMickaël Salaün cleanup_layout(_metadata); 2333e1199815SMickaël Salaün } 2334e1199815SMickaël Salaün 2335e1199815SMickaël Salaün static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 2336e1199815SMickaël Salaün static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 2337e1199815SMickaël Salaün 2338e1199815SMickaël Salaün /* 2339e1199815SMickaël Salaün * layout1_bind hierarchy: 2340e1199815SMickaël Salaün * 2341e1199815SMickaël Salaün * tmp 2342e1199815SMickaël Salaün * ├── s1d1 2343e1199815SMickaël Salaün * │ ├── f1 2344e1199815SMickaël Salaün * │ ├── f2 2345e1199815SMickaël Salaün * │ └── s1d2 2346e1199815SMickaël Salaün * │ ├── f1 2347e1199815SMickaël Salaün * │ ├── f2 2348e1199815SMickaël Salaün * │ └── s1d3 2349e1199815SMickaël Salaün * │ ├── f1 2350e1199815SMickaël Salaün * │ └── f2 2351e1199815SMickaël Salaün * ├── s2d1 2352e1199815SMickaël Salaün * │ ├── f1 2353e1199815SMickaël Salaün * │ └── s2d2 2354e1199815SMickaël Salaün * │ ├── f1 2355e1199815SMickaël Salaün * │ ├── f2 2356e1199815SMickaël Salaün * │ └── s1d3 2357e1199815SMickaël Salaün * │ ├── f1 2358e1199815SMickaël Salaün * │ └── f2 2359e1199815SMickaël Salaün * └── s3d1 2360e1199815SMickaël Salaün * └── s3d2 2361e1199815SMickaël Salaün * └── s3d3 2362e1199815SMickaël Salaün */ 2363e1199815SMickaël Salaün 2364e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, no_restriction) 2365e1199815SMickaël Salaün { 2366e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 2367e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2368e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 2369e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2370e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 2371e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2372e1199815SMickaël Salaün 2373e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 2374e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 2375e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 2376e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 2377e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 2378e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 2379e1199815SMickaël Salaün 2380e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 2381e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 2382e1199815SMickaël Salaün 2383e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 2384e1199815SMickaël Salaün } 2385e1199815SMickaël Salaün 2386e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, same_content_same_file) 2387e1199815SMickaël Salaün { 2388e1199815SMickaël Salaün /* 2389e1199815SMickaël Salaün * Sets access right on parent directories of both source and 2390e1199815SMickaël Salaün * destination mount points. 2391e1199815SMickaël Salaün */ 2392e1199815SMickaël Salaün const struct rule layer1_parent[] = { 2393e1199815SMickaël Salaün { 2394e1199815SMickaël Salaün .path = dir_s1d1, 2395e1199815SMickaël Salaün .access = ACCESS_RO, 2396e1199815SMickaël Salaün }, 2397e1199815SMickaël Salaün { 2398e1199815SMickaël Salaün .path = dir_s2d1, 2399e1199815SMickaël Salaün .access = ACCESS_RW, 2400e1199815SMickaël Salaün }, 2401135464f9SMickaël Salaün {}, 2402e1199815SMickaël Salaün }; 2403e1199815SMickaël Salaün /* 2404e1199815SMickaël Salaün * Sets access rights on the same bind-mounted directories. The result 2405e1199815SMickaël Salaün * should be ACCESS_RW for both directories, but not both hierarchies 2406e1199815SMickaël Salaün * because of the first layer. 2407e1199815SMickaël Salaün */ 2408e1199815SMickaël Salaün const struct rule layer2_mount_point[] = { 2409e1199815SMickaël Salaün { 2410e1199815SMickaël Salaün .path = dir_s1d2, 2411e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2412e1199815SMickaël Salaün }, 2413e1199815SMickaël Salaün { 2414e1199815SMickaël Salaün .path = dir_s2d2, 2415e1199815SMickaël Salaün .access = ACCESS_RW, 2416e1199815SMickaël Salaün }, 2417135464f9SMickaël Salaün {}, 2418e1199815SMickaël Salaün }; 2419e1199815SMickaël Salaün /* Only allow read-access to the s1d3 hierarchies. */ 2420e1199815SMickaël Salaün const struct rule layer3_source[] = { 2421e1199815SMickaël Salaün { 2422e1199815SMickaël Salaün .path = dir_s1d3, 2423e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2424e1199815SMickaël Salaün }, 2425135464f9SMickaël Salaün {}, 2426e1199815SMickaël Salaün }; 2427e1199815SMickaël Salaün /* Removes all access rights. */ 2428e1199815SMickaël Salaün const struct rule layer4_destination[] = { 2429e1199815SMickaël Salaün { 2430e1199815SMickaël Salaün .path = bind_file1_s1d3, 2431e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 2432e1199815SMickaël Salaün }, 2433135464f9SMickaël Salaün {}, 2434e1199815SMickaël Salaün }; 2435e1199815SMickaël Salaün int ruleset_fd; 2436e1199815SMickaël Salaün 2437e1199815SMickaël Salaün /* Sets rules for the parent directories. */ 2438e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 2439e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2440e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2441e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2442e1199815SMickaël Salaün 2443e1199815SMickaël Salaün /* Checks source hierarchy. */ 2444e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 2445e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 2446e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 2447e1199815SMickaël Salaün 2448e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2449e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2450e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2451e1199815SMickaël Salaün 2452e1199815SMickaël Salaün /* Checks destination hierarchy. */ 2453e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 2454e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 2455e1199815SMickaël Salaün 2456e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 2457e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2458e1199815SMickaël Salaün 2459e1199815SMickaël Salaün /* Sets rules for the mount points. */ 2460e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 2461e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2462e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2463e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2464e1199815SMickaël Salaün 2465e1199815SMickaël Salaün /* Checks source hierarchy. */ 2466e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 2467e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 2468e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 2469e1199815SMickaël Salaün 2470e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 2471e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2472e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2473e1199815SMickaël Salaün 2474e1199815SMickaël Salaün /* Checks destination hierarchy. */ 2475e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 2476e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 2477e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 2478e1199815SMickaël Salaün 2479e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 2480e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2481e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 2482e1199815SMickaël Salaün 2483e1199815SMickaël Salaün /* Sets a (shared) rule only on the source. */ 2484e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 2485e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2486e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2487e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2488e1199815SMickaël Salaün 2489e1199815SMickaël Salaün /* Checks source hierarchy. */ 2490e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 2491e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 2492e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 2493e1199815SMickaël Salaün 2494e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 2495e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 2496e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 2497e1199815SMickaël Salaün 2498e1199815SMickaël Salaün /* Checks destination hierarchy. */ 2499e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 2500e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 2501e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 2502e1199815SMickaël Salaün 2503e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 2504e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 2505e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 2506e1199815SMickaël Salaün 2507e1199815SMickaël Salaün /* Sets a (shared) rule only on the destination. */ 2508e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 2509e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2510e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2511e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2512e1199815SMickaël Salaün 2513e1199815SMickaël Salaün /* Checks source hierarchy. */ 2514e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 2515e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 2516e1199815SMickaël Salaün 2517e1199815SMickaël Salaün /* Checks destination hierarchy. */ 2518e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 2519e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 2520e1199815SMickaël Salaün } 2521e1199815SMickaël Salaün 2522e1199815SMickaël Salaün #define LOWER_BASE TMP_DIR "/lower" 2523e1199815SMickaël Salaün #define LOWER_DATA LOWER_BASE "/data" 2524e1199815SMickaël Salaün static const char lower_fl1[] = LOWER_DATA "/fl1"; 2525e1199815SMickaël Salaün static const char lower_dl1[] = LOWER_DATA "/dl1"; 2526e1199815SMickaël Salaün static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 2527e1199815SMickaël Salaün static const char lower_fo1[] = LOWER_DATA "/fo1"; 2528e1199815SMickaël Salaün static const char lower_do1[] = LOWER_DATA "/do1"; 2529e1199815SMickaël Salaün static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 2530e1199815SMickaël Salaün static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 2531e1199815SMickaël Salaün 2532e1199815SMickaël Salaün static const char (*lower_base_files[])[] = { 2533e1199815SMickaël Salaün &lower_fl1, 2534e1199815SMickaël Salaün &lower_fo1, 2535135464f9SMickaël Salaün NULL, 2536e1199815SMickaël Salaün }; 2537e1199815SMickaël Salaün static const char (*lower_base_directories[])[] = { 2538e1199815SMickaël Salaün &lower_dl1, 2539e1199815SMickaël Salaün &lower_do1, 2540135464f9SMickaël Salaün NULL, 2541e1199815SMickaël Salaün }; 2542e1199815SMickaël Salaün static const char (*lower_sub_files[])[] = { 2543e1199815SMickaël Salaün &lower_dl1_fl2, 2544e1199815SMickaël Salaün &lower_do1_fo2, 2545e1199815SMickaël Salaün &lower_do1_fl3, 2546135464f9SMickaël Salaün NULL, 2547e1199815SMickaël Salaün }; 2548e1199815SMickaël Salaün 2549e1199815SMickaël Salaün #define UPPER_BASE TMP_DIR "/upper" 2550e1199815SMickaël Salaün #define UPPER_DATA UPPER_BASE "/data" 2551e1199815SMickaël Salaün #define UPPER_WORK UPPER_BASE "/work" 2552e1199815SMickaël Salaün static const char upper_fu1[] = UPPER_DATA "/fu1"; 2553e1199815SMickaël Salaün static const char upper_du1[] = UPPER_DATA "/du1"; 2554e1199815SMickaël Salaün static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 2555e1199815SMickaël Salaün static const char upper_fo1[] = UPPER_DATA "/fo1"; 2556e1199815SMickaël Salaün static const char upper_do1[] = UPPER_DATA "/do1"; 2557e1199815SMickaël Salaün static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 2558e1199815SMickaël Salaün static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 2559e1199815SMickaël Salaün 2560e1199815SMickaël Salaün static const char (*upper_base_files[])[] = { 2561e1199815SMickaël Salaün &upper_fu1, 2562e1199815SMickaël Salaün &upper_fo1, 2563135464f9SMickaël Salaün NULL, 2564e1199815SMickaël Salaün }; 2565e1199815SMickaël Salaün static const char (*upper_base_directories[])[] = { 2566e1199815SMickaël Salaün &upper_du1, 2567e1199815SMickaël Salaün &upper_do1, 2568135464f9SMickaël Salaün NULL, 2569e1199815SMickaël Salaün }; 2570e1199815SMickaël Salaün static const char (*upper_sub_files[])[] = { 2571e1199815SMickaël Salaün &upper_du1_fu2, 2572e1199815SMickaël Salaün &upper_do1_fo2, 2573e1199815SMickaël Salaün &upper_do1_fu3, 2574135464f9SMickaël Salaün NULL, 2575e1199815SMickaël Salaün }; 2576e1199815SMickaël Salaün 2577e1199815SMickaël Salaün #define MERGE_BASE TMP_DIR "/merge" 2578e1199815SMickaël Salaün #define MERGE_DATA MERGE_BASE "/data" 2579e1199815SMickaël Salaün static const char merge_fl1[] = MERGE_DATA "/fl1"; 2580e1199815SMickaël Salaün static const char merge_dl1[] = MERGE_DATA "/dl1"; 2581e1199815SMickaël Salaün static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 2582e1199815SMickaël Salaün static const char merge_fu1[] = MERGE_DATA "/fu1"; 2583e1199815SMickaël Salaün static const char merge_du1[] = MERGE_DATA "/du1"; 2584e1199815SMickaël Salaün static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 2585e1199815SMickaël Salaün static const char merge_fo1[] = MERGE_DATA "/fo1"; 2586e1199815SMickaël Salaün static const char merge_do1[] = MERGE_DATA "/do1"; 2587e1199815SMickaël Salaün static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 2588e1199815SMickaël Salaün static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 2589e1199815SMickaël Salaün static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 2590e1199815SMickaël Salaün 2591e1199815SMickaël Salaün static const char (*merge_base_files[])[] = { 2592e1199815SMickaël Salaün &merge_fl1, 2593e1199815SMickaël Salaün &merge_fu1, 2594e1199815SMickaël Salaün &merge_fo1, 2595135464f9SMickaël Salaün NULL, 2596e1199815SMickaël Salaün }; 2597e1199815SMickaël Salaün static const char (*merge_base_directories[])[] = { 2598e1199815SMickaël Salaün &merge_dl1, 2599e1199815SMickaël Salaün &merge_du1, 2600e1199815SMickaël Salaün &merge_do1, 2601135464f9SMickaël Salaün NULL, 2602e1199815SMickaël Salaün }; 2603e1199815SMickaël Salaün static const char (*merge_sub_files[])[] = { 2604371183faSMickaël Salaün &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 2605371183faSMickaël Salaün &merge_do1_fl3, &merge_do1_fu3, NULL, 2606e1199815SMickaël Salaün }; 2607e1199815SMickaël Salaün 2608e1199815SMickaël Salaün /* 2609e1199815SMickaël Salaün * layout2_overlay hierarchy: 2610e1199815SMickaël Salaün * 2611e1199815SMickaël Salaün * tmp 2612e1199815SMickaël Salaün * ├── lower 2613e1199815SMickaël Salaün * │ └── data 2614e1199815SMickaël Salaün * │ ├── dl1 2615e1199815SMickaël Salaün * │ │ └── fl2 2616e1199815SMickaël Salaün * │ ├── do1 2617e1199815SMickaël Salaün * │ │ ├── fl3 2618e1199815SMickaël Salaün * │ │ └── fo2 2619e1199815SMickaël Salaün * │ ├── fl1 2620e1199815SMickaël Salaün * │ └── fo1 2621e1199815SMickaël Salaün * ├── merge 2622e1199815SMickaël Salaün * │ └── data 2623e1199815SMickaël Salaün * │ ├── dl1 2624e1199815SMickaël Salaün * │ │ └── fl2 2625e1199815SMickaël Salaün * │ ├── do1 2626e1199815SMickaël Salaün * │ │ ├── fl3 2627e1199815SMickaël Salaün * │ │ ├── fo2 2628e1199815SMickaël Salaün * │ │ └── fu3 2629e1199815SMickaël Salaün * │ ├── du1 2630e1199815SMickaël Salaün * │ │ └── fu2 2631e1199815SMickaël Salaün * │ ├── fl1 2632e1199815SMickaël Salaün * │ ├── fo1 2633e1199815SMickaël Salaün * │ └── fu1 2634e1199815SMickaël Salaün * └── upper 2635e1199815SMickaël Salaün * ├── data 2636e1199815SMickaël Salaün * │ ├── do1 2637e1199815SMickaël Salaün * │ │ ├── fo2 2638e1199815SMickaël Salaün * │ │ └── fu3 2639e1199815SMickaël Salaün * │ ├── du1 2640e1199815SMickaël Salaün * │ │ └── fu2 2641e1199815SMickaël Salaün * │ ├── fo1 2642e1199815SMickaël Salaün * │ └── fu1 2643e1199815SMickaël Salaün * └── work 2644e1199815SMickaël Salaün * └── work 2645e1199815SMickaël Salaün */ 2646e1199815SMickaël Salaün 26474598d9abSMickaël Salaün /* clang-format off */ 26484598d9abSMickaël Salaün FIXTURE(layout2_overlay) {}; 26494598d9abSMickaël Salaün /* clang-format on */ 2650e1199815SMickaël Salaün 2651e1199815SMickaël Salaün FIXTURE_SETUP(layout2_overlay) 2652e1199815SMickaël Salaün { 2653e1199815SMickaël Salaün prepare_layout(_metadata); 2654e1199815SMickaël Salaün 2655e1199815SMickaël Salaün create_directory(_metadata, LOWER_BASE); 2656e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2657e1199815SMickaël Salaün /* Creates tmpfs mount points to get deterministic overlayfs. */ 2658e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700")); 2659e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2660e1199815SMickaël Salaün create_file(_metadata, lower_fl1); 2661e1199815SMickaël Salaün create_file(_metadata, lower_dl1_fl2); 2662e1199815SMickaël Salaün create_file(_metadata, lower_fo1); 2663e1199815SMickaël Salaün create_file(_metadata, lower_do1_fo2); 2664e1199815SMickaël Salaün create_file(_metadata, lower_do1_fl3); 2665e1199815SMickaël Salaün 2666e1199815SMickaël Salaün create_directory(_metadata, UPPER_BASE); 2667e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2668e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700")); 2669e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2670e1199815SMickaël Salaün create_file(_metadata, upper_fu1); 2671e1199815SMickaël Salaün create_file(_metadata, upper_du1_fu2); 2672e1199815SMickaël Salaün create_file(_metadata, upper_fo1); 2673e1199815SMickaël Salaün create_file(_metadata, upper_do1_fo2); 2674e1199815SMickaël Salaün create_file(_metadata, upper_do1_fu3); 2675e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 2676e1199815SMickaël Salaün 2677e1199815SMickaël Salaün create_directory(_metadata, MERGE_DATA); 2678e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2679e1199815SMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 2680e1199815SMickaël Salaün ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 2681371183faSMickaël Salaün "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 2682e1199815SMickaël Salaün ",workdir=" UPPER_WORK)); 2683e1199815SMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 2684e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2685e1199815SMickaël Salaün } 2686e1199815SMickaël Salaün 2687e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout2_overlay) 2688e1199815SMickaël Salaün { 2689e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fl3)); 2690e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 2691e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fl1)); 2692e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fo2)); 2693e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fo1)); 2694e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2695e1199815SMickaël Salaün EXPECT_EQ(0, umount(LOWER_BASE)); 2696e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2697e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(LOWER_BASE)); 2698e1199815SMickaël Salaün 2699e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fu3)); 2700e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_du1_fu2)); 2701e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fu1)); 2702e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fo2)); 2703e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fo1)); 2704e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 2705e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2706e1199815SMickaël Salaün EXPECT_EQ(0, umount(UPPER_BASE)); 2707e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2708e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_BASE)); 2709e1199815SMickaël Salaün 2710e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 2711e1199815SMickaël Salaün EXPECT_EQ(0, umount(MERGE_DATA)); 2712e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 2713e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(MERGE_DATA)); 2714e1199815SMickaël Salaün 2715e1199815SMickaël Salaün cleanup_layout(_metadata); 2716e1199815SMickaël Salaün } 2717e1199815SMickaël Salaün 2718e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, no_restriction) 2719e1199815SMickaël Salaün { 2720e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 2721e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 2722e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 2723e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 2724e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 2725e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 2726e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 2727e1199815SMickaël Salaün 2728e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 2729e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 2730e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 2731e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 2732e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 2733e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 2734e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 2735e1199815SMickaël Salaün 2736e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 2737e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 2738e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 2739e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 2740e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 2741e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 2742e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 2743e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 2744e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 2745e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 2746e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 2747e1199815SMickaël Salaün } 2748e1199815SMickaël Salaün 2749e1199815SMickaël Salaün #define for_each_path(path_list, path_entry, i) \ 2750e1199815SMickaël Salaün for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 2751e1199815SMickaël Salaün path_entry = *path_list[++i]) 2752e1199815SMickaël Salaün 2753e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, same_content_different_file) 2754e1199815SMickaël Salaün { 2755e1199815SMickaël Salaün /* Sets access right on parent directories of both layers. */ 2756e1199815SMickaël Salaün const struct rule layer1_base[] = { 2757e1199815SMickaël Salaün { 2758e1199815SMickaël Salaün .path = LOWER_BASE, 2759e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2760e1199815SMickaël Salaün }, 2761e1199815SMickaël Salaün { 2762e1199815SMickaël Salaün .path = UPPER_BASE, 2763e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2764e1199815SMickaël Salaün }, 2765e1199815SMickaël Salaün { 2766e1199815SMickaël Salaün .path = MERGE_BASE, 2767e1199815SMickaël Salaün .access = ACCESS_RW, 2768e1199815SMickaël Salaün }, 2769135464f9SMickaël Salaün {}, 2770e1199815SMickaël Salaün }; 2771e1199815SMickaël Salaün const struct rule layer2_data[] = { 2772e1199815SMickaël Salaün { 2773e1199815SMickaël Salaün .path = LOWER_DATA, 2774e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2775e1199815SMickaël Salaün }, 2776e1199815SMickaël Salaün { 2777e1199815SMickaël Salaün .path = UPPER_DATA, 2778e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2779e1199815SMickaël Salaün }, 2780e1199815SMickaël Salaün { 2781e1199815SMickaël Salaün .path = MERGE_DATA, 2782e1199815SMickaël Salaün .access = ACCESS_RW, 2783e1199815SMickaël Salaün }, 2784135464f9SMickaël Salaün {}, 2785e1199815SMickaël Salaün }; 2786e1199815SMickaël Salaün /* Sets access right on directories inside both layers. */ 2787e1199815SMickaël Salaün const struct rule layer3_subdirs[] = { 2788e1199815SMickaël Salaün { 2789e1199815SMickaël Salaün .path = lower_dl1, 2790e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2791e1199815SMickaël Salaün }, 2792e1199815SMickaël Salaün { 2793e1199815SMickaël Salaün .path = lower_do1, 2794e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2795e1199815SMickaël Salaün }, 2796e1199815SMickaël Salaün { 2797e1199815SMickaël Salaün .path = upper_du1, 2798e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2799e1199815SMickaël Salaün }, 2800e1199815SMickaël Salaün { 2801e1199815SMickaël Salaün .path = upper_do1, 2802e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2803e1199815SMickaël Salaün }, 2804e1199815SMickaël Salaün { 2805e1199815SMickaël Salaün .path = merge_dl1, 2806e1199815SMickaël Salaün .access = ACCESS_RW, 2807e1199815SMickaël Salaün }, 2808e1199815SMickaël Salaün { 2809e1199815SMickaël Salaün .path = merge_du1, 2810e1199815SMickaël Salaün .access = ACCESS_RW, 2811e1199815SMickaël Salaün }, 2812e1199815SMickaël Salaün { 2813e1199815SMickaël Salaün .path = merge_do1, 2814e1199815SMickaël Salaün .access = ACCESS_RW, 2815e1199815SMickaël Salaün }, 2816135464f9SMickaël Salaün {}, 2817e1199815SMickaël Salaün }; 2818e1199815SMickaël Salaün /* Tighten access rights to the files. */ 2819e1199815SMickaël Salaün const struct rule layer4_files[] = { 2820e1199815SMickaël Salaün { 2821e1199815SMickaël Salaün .path = lower_dl1_fl2, 2822e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2823e1199815SMickaël Salaün }, 2824e1199815SMickaël Salaün { 2825e1199815SMickaël Salaün .path = lower_do1_fo2, 2826e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2827e1199815SMickaël Salaün }, 2828e1199815SMickaël Salaün { 2829e1199815SMickaël Salaün .path = lower_do1_fl3, 2830e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2831e1199815SMickaël Salaün }, 2832e1199815SMickaël Salaün { 2833e1199815SMickaël Salaün .path = upper_du1_fu2, 2834e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2835e1199815SMickaël Salaün }, 2836e1199815SMickaël Salaün { 2837e1199815SMickaël Salaün .path = upper_do1_fo2, 2838e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2839e1199815SMickaël Salaün }, 2840e1199815SMickaël Salaün { 2841e1199815SMickaël Salaün .path = upper_do1_fu3, 2842e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 2843e1199815SMickaël Salaün }, 2844e1199815SMickaël Salaün { 2845e1199815SMickaël Salaün .path = merge_dl1_fl2, 2846e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2847e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 2848e1199815SMickaël Salaün }, 2849e1199815SMickaël Salaün { 2850e1199815SMickaël Salaün .path = merge_du1_fu2, 2851e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2852e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 2853e1199815SMickaël Salaün }, 2854e1199815SMickaël Salaün { 2855e1199815SMickaël Salaün .path = merge_do1_fo2, 2856e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2857e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 2858e1199815SMickaël Salaün }, 2859e1199815SMickaël Salaün { 2860e1199815SMickaël Salaün .path = merge_do1_fl3, 2861e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2862e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 2863e1199815SMickaël Salaün }, 2864e1199815SMickaël Salaün { 2865e1199815SMickaël Salaün .path = merge_do1_fu3, 2866e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2867e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 2868e1199815SMickaël Salaün }, 2869135464f9SMickaël Salaün {}, 2870e1199815SMickaël Salaün }; 2871e1199815SMickaël Salaün const struct rule layer5_merge_only[] = { 2872e1199815SMickaël Salaün { 2873e1199815SMickaël Salaün .path = MERGE_DATA, 2874e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2875e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 2876e1199815SMickaël Salaün }, 2877135464f9SMickaël Salaün {}, 2878e1199815SMickaël Salaün }; 2879e1199815SMickaël Salaün int ruleset_fd; 2880e1199815SMickaël Salaün size_t i; 2881e1199815SMickaël Salaün const char *path_entry; 2882e1199815SMickaël Salaün 2883e1199815SMickaël Salaün /* Sets rules on base directories (i.e. outside overlay scope). */ 2884e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 2885e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2886e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2887e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2888e1199815SMickaël Salaün 2889e1199815SMickaël Salaün /* Checks lower layer. */ 2890e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 2891e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2892e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2893e1199815SMickaël Salaün } 2894e1199815SMickaël Salaün for_each_path(lower_base_directories, path_entry, i) { 2895371183faSMickaël Salaün ASSERT_EQ(EACCES, 2896371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2897e1199815SMickaël Salaün } 2898e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 2899e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2900e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2901e1199815SMickaël Salaün } 2902e1199815SMickaël Salaün /* Checks upper layer. */ 2903e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 2904e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2905e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2906e1199815SMickaël Salaün } 2907e1199815SMickaël Salaün for_each_path(upper_base_directories, path_entry, i) { 2908371183faSMickaël Salaün ASSERT_EQ(EACCES, 2909371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2910e1199815SMickaël Salaün } 2911e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 2912e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2913e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2914e1199815SMickaël Salaün } 2915e1199815SMickaël Salaün /* 2916e1199815SMickaël Salaün * Checks that access rights are independent from the lower and upper 2917e1199815SMickaël Salaün * layers: write access to upper files viewed through the merge point 2918e1199815SMickaël Salaün * is still allowed, and write access to lower file viewed (and copied) 2919e1199815SMickaël Salaün * through the merge point is still allowed. 2920e1199815SMickaël Salaün */ 2921e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 2922e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2923e1199815SMickaël Salaün } 2924e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 2925e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2926e1199815SMickaël Salaün } 2927e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 2928e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2929e1199815SMickaël Salaün } 2930e1199815SMickaël Salaün 2931e1199815SMickaël Salaün /* Sets rules on data directories (i.e. inside overlay scope). */ 2932e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 2933e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2934e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2935e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2936e1199815SMickaël Salaün 2937e1199815SMickaël Salaün /* Checks merge. */ 2938e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 2939e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2940e1199815SMickaël Salaün } 2941e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 2942e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2943e1199815SMickaël Salaün } 2944e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 2945e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2946e1199815SMickaël Salaün } 2947e1199815SMickaël Salaün 2948e1199815SMickaël Salaün /* Same checks with tighter rules. */ 2949e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 2950e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2951e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2952e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2953e1199815SMickaël Salaün 2954e1199815SMickaël Salaün /* Checks changes for lower layer. */ 2955e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 2956e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2957e1199815SMickaël Salaün } 2958e1199815SMickaël Salaün /* Checks changes for upper layer. */ 2959e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 2960e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 2961e1199815SMickaël Salaün } 2962e1199815SMickaël Salaün /* Checks all merge accesses. */ 2963e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 2964e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2965e1199815SMickaël Salaün } 2966e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 2967e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2968e1199815SMickaël Salaün } 2969e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 2970e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2971e1199815SMickaël Salaün } 2972e1199815SMickaël Salaün 2973e1199815SMickaël Salaün /* Sets rules directly on overlayed files. */ 2974e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 2975e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2976e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2977e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2978e1199815SMickaël Salaün 2979e1199815SMickaël Salaün /* Checks unchanged accesses on lower layer. */ 2980e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 2981e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2982e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2983e1199815SMickaël Salaün } 2984e1199815SMickaël Salaün /* Checks unchanged accesses on upper layer. */ 2985e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 2986e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 2987e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 2988e1199815SMickaël Salaün } 2989e1199815SMickaël Salaün /* Checks all merge accesses. */ 2990e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 2991e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 2992e1199815SMickaël Salaün } 2993e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 2994371183faSMickaël Salaün ASSERT_EQ(EACCES, 2995371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 2996e1199815SMickaël Salaün } 2997e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 2998e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 2999e1199815SMickaël Salaün } 3000e1199815SMickaël Salaün 3001e1199815SMickaël Salaün /* Only allowes access to the merge hierarchy. */ 3002e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 3003e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3004e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3005e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3006e1199815SMickaël Salaün 3007e1199815SMickaël Salaün /* Checks new accesses on lower layer. */ 3008e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 3009e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 3010e1199815SMickaël Salaün } 3011e1199815SMickaël Salaün /* Checks new accesses on upper layer. */ 3012e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 3013e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 3014e1199815SMickaël Salaün } 3015e1199815SMickaël Salaün /* Checks all merge accesses. */ 3016e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 3017e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 3018e1199815SMickaël Salaün } 3019e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 3020371183faSMickaël Salaün ASSERT_EQ(EACCES, 3021371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3022e1199815SMickaël Salaün } 3023e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 3024e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3025e1199815SMickaël Salaün } 3026e1199815SMickaël Salaün } 3027e1199815SMickaël Salaün 3028e1199815SMickaël Salaün TEST_HARNESS_MAIN 3029