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 7*55e55920SMickaël Salaün * Copyright © 2020-2022 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)) { 149f4056b92SMickaël Salaün if (errno != ENOENT && errno != ENOTDIR) 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 374*55e55920SMickaël Salaün /* Tests with denied-by-default access right. */ 375*55e55920SMickaël Salaün path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 376*55e55920SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 377*55e55920SMickaël Salaün &path_beneath, 0)); 378*55e55920SMickaël Salaün ASSERT_EQ(EINVAL, errno); 379*55e55920SMickaël Salaün path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 380*55e55920SMickaël Salaün 381e1199815SMickaël Salaün /* Test with unknown (64-bits) value. */ 382e1199815SMickaël Salaün path_beneath.allowed_access |= (1ULL << 60); 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(EINVAL, errno); 386e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 387e1199815SMickaël Salaün 388e1199815SMickaël Salaün /* Test with no access. */ 389e1199815SMickaël Salaün path_beneath.allowed_access = 0; 390e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 391e1199815SMickaël Salaün &path_beneath, 0)); 392e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 393e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 394e1199815SMickaël Salaün 395e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 396e1199815SMickaël Salaün 397e1199815SMickaël Salaün /* Enforces the ruleset. */ 398e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 399e1199815SMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 400e1199815SMickaël Salaün 401e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 402e1199815SMickaël Salaün } 403e1199815SMickaël Salaün 4044598d9abSMickaël Salaün /* clang-format off */ 4054598d9abSMickaël Salaün 406e1199815SMickaël Salaün #define ACCESS_FILE ( \ 407e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | \ 408e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE | \ 409e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE) 410e1199815SMickaël Salaün 411b91c3e4eSMickaël Salaün #define ACCESS_LAST LANDLOCK_ACCESS_FS_REFER 412e1199815SMickaël Salaün 413e1199815SMickaël Salaün #define ACCESS_ALL ( \ 414e1199815SMickaël Salaün ACCESS_FILE | \ 415e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR | \ 416e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 417e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 418e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 419e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_DIR | \ 420e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | \ 421e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 422e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 423e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 424b91c3e4eSMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SYM | \ 425e1199815SMickaël Salaün ACCESS_LAST) 426e1199815SMickaël Salaün 4274598d9abSMickaël Salaün /* clang-format on */ 4284598d9abSMickaël Salaün 429d18955d0SMickaël Salaün TEST_F_FORK(layout1, file_and_dir_access_rights) 430e1199815SMickaël Salaün { 431e1199815SMickaël Salaün __u64 access; 432e1199815SMickaël Salaün int err; 433d18955d0SMickaël Salaün struct landlock_path_beneath_attr path_beneath_file = {}, 434d18955d0SMickaël Salaün path_beneath_dir = {}; 435e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 436e1199815SMickaël Salaün .handled_access_fs = ACCESS_ALL, 437e1199815SMickaël Salaün }; 438371183faSMickaël Salaün const int ruleset_fd = 439371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 440e1199815SMickaël Salaün 441e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 442e1199815SMickaël Salaün 443e1199815SMickaël Salaün /* Tests access rights for files. */ 444d18955d0SMickaël Salaün path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 445d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_file.parent_fd); 446d18955d0SMickaël Salaün 447d18955d0SMickaël Salaün /* Tests access rights for directories. */ 448d18955d0SMickaël Salaün path_beneath_dir.parent_fd = 449d18955d0SMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 450d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_dir.parent_fd); 451d18955d0SMickaël Salaün 452e1199815SMickaël Salaün for (access = 1; access <= ACCESS_LAST; access <<= 1) { 453d18955d0SMickaël Salaün path_beneath_dir.allowed_access = access; 454d18955d0SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 455d18955d0SMickaël Salaün LANDLOCK_RULE_PATH_BENEATH, 456d18955d0SMickaël Salaün &path_beneath_dir, 0)); 457d18955d0SMickaël Salaün 458d18955d0SMickaël Salaün path_beneath_file.allowed_access = access; 459e1199815SMickaël Salaün err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 460d18955d0SMickaël Salaün &path_beneath_file, 0); 461d18955d0SMickaël Salaün if (access & ACCESS_FILE) { 462e1199815SMickaël Salaün ASSERT_EQ(0, err); 463e1199815SMickaël Salaün } else { 464e1199815SMickaël Salaün ASSERT_EQ(-1, err); 465e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 466e1199815SMickaël Salaün } 467e1199815SMickaël Salaün } 468d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 469d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 470d18955d0SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 471e1199815SMickaël Salaün } 472e1199815SMickaël Salaün 473c56b3bf5SMickaël Salaün TEST_F_FORK(layout1, unknown_access_rights) 474c56b3bf5SMickaël Salaün { 475c56b3bf5SMickaël Salaün __u64 access_mask; 476c56b3bf5SMickaël Salaün 477c56b3bf5SMickaël Salaün for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 478c56b3bf5SMickaël Salaün access_mask >>= 1) { 479c56b3bf5SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 480c56b3bf5SMickaël Salaün .handled_access_fs = access_mask, 481c56b3bf5SMickaël Salaün }; 482c56b3bf5SMickaël Salaün 483c56b3bf5SMickaël Salaün ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 484c56b3bf5SMickaël Salaün sizeof(ruleset_attr), 0)); 485c56b3bf5SMickaël Salaün ASSERT_EQ(EINVAL, errno); 486c56b3bf5SMickaël Salaün } 487c56b3bf5SMickaël Salaün } 488c56b3bf5SMickaël Salaün 489e1199815SMickaël Salaün static void add_path_beneath(struct __test_metadata *const _metadata, 490e1199815SMickaël Salaün const int ruleset_fd, const __u64 allowed_access, 491e1199815SMickaël Salaün const char *const path) 492e1199815SMickaël Salaün { 493e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath = { 494e1199815SMickaël Salaün .allowed_access = allowed_access, 495e1199815SMickaël Salaün }; 496e1199815SMickaël Salaün 497e1199815SMickaël Salaün path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 498371183faSMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd) 499371183faSMickaël Salaün { 500e1199815SMickaël Salaün TH_LOG("Failed to open directory \"%s\": %s", path, 501e1199815SMickaël Salaün strerror(errno)); 502e1199815SMickaël Salaün } 503e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 504371183faSMickaël Salaün &path_beneath, 0)) 505371183faSMickaël Salaün { 506e1199815SMickaël Salaün TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 507e1199815SMickaël Salaün strerror(errno)); 508e1199815SMickaël Salaün } 509e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 510e1199815SMickaël Salaün } 511e1199815SMickaël Salaün 512e1199815SMickaël Salaün struct rule { 513e1199815SMickaël Salaün const char *path; 514e1199815SMickaël Salaün __u64 access; 515e1199815SMickaël Salaün }; 516e1199815SMickaël Salaün 5174598d9abSMickaël Salaün /* clang-format off */ 5184598d9abSMickaël Salaün 519e1199815SMickaël Salaün #define ACCESS_RO ( \ 520e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | \ 521e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR) 522e1199815SMickaël Salaün 523e1199815SMickaël Salaün #define ACCESS_RW ( \ 524e1199815SMickaël Salaün ACCESS_RO | \ 525e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE) 526e1199815SMickaël Salaün 5274598d9abSMickaël Salaün /* clang-format on */ 5284598d9abSMickaël Salaün 529e1199815SMickaël Salaün static int create_ruleset(struct __test_metadata *const _metadata, 530371183faSMickaël Salaün const __u64 handled_access_fs, 531371183faSMickaël Salaün const struct rule rules[]) 532e1199815SMickaël Salaün { 533e1199815SMickaël Salaün int ruleset_fd, i; 534e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 535e1199815SMickaël Salaün .handled_access_fs = handled_access_fs, 536e1199815SMickaël Salaün }; 537e1199815SMickaël Salaün 538371183faSMickaël Salaün ASSERT_NE(NULL, rules) 539371183faSMickaël Salaün { 540e1199815SMickaël Salaün TH_LOG("No rule list"); 541e1199815SMickaël Salaün } 542371183faSMickaël Salaün ASSERT_NE(NULL, rules[0].path) 543371183faSMickaël Salaün { 544e1199815SMickaël Salaün TH_LOG("Empty rule list"); 545e1199815SMickaël Salaün } 546e1199815SMickaël Salaün 547371183faSMickaël Salaün ruleset_fd = 548371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 549371183faSMickaël Salaün ASSERT_LE(0, ruleset_fd) 550371183faSMickaël Salaün { 551e1199815SMickaël Salaün TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 552e1199815SMickaël Salaün } 553e1199815SMickaël Salaün 554e1199815SMickaël Salaün for (i = 0; rules[i].path; i++) { 555e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, rules[i].access, 556e1199815SMickaël Salaün rules[i].path); 557e1199815SMickaël Salaün } 558e1199815SMickaël Salaün return ruleset_fd; 559e1199815SMickaël Salaün } 560e1199815SMickaël Salaün 561e1199815SMickaël Salaün static void enforce_ruleset(struct __test_metadata *const _metadata, 562e1199815SMickaël Salaün const int ruleset_fd) 563e1199815SMickaël Salaün { 564e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 565371183faSMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 566371183faSMickaël Salaün { 567e1199815SMickaël Salaün TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 568e1199815SMickaël Salaün } 569e1199815SMickaël Salaün } 570e1199815SMickaël Salaün 571e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_nsfs) 572e1199815SMickaël Salaün { 573e1199815SMickaël Salaün const struct rule rules[] = { 574e1199815SMickaël Salaün { 575e1199815SMickaël Salaün .path = "/dev/null", 576e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 577e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 578e1199815SMickaël Salaün }, 579135464f9SMickaël Salaün {}, 580e1199815SMickaël Salaün }; 581e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath; 582371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 583371183faSMickaël Salaün _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 584371183faSMickaël Salaün rules); 585e1199815SMickaël Salaün 586e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 587e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 588e1199815SMickaël Salaün 589e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 590e1199815SMickaël Salaün 591e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 592e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 593e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 594e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 595e1199815SMickaël Salaün 596e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 597e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 598e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 599e1199815SMickaël Salaün /* 600e1199815SMickaël Salaün * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 601e1199815SMickaël Salaün * disconnected path. Such path cannot be identified and must then be 602e1199815SMickaël Salaün * allowed. 603e1199815SMickaël Salaün */ 604e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 605e1199815SMickaël Salaün 606e1199815SMickaël Salaün /* 607e1199815SMickaël Salaün * Checks that it is not possible to add nsfs-like filesystem 608e1199815SMickaël Salaün * references to a ruleset. 609e1199815SMickaël Salaün */ 610e1199815SMickaël Salaün path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 611e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 612e1199815SMickaël Salaün path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 613e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 614e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 615e1199815SMickaël Salaün &path_beneath, 0)); 616e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 617e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 618e1199815SMickaël Salaün } 619e1199815SMickaël Salaün 620371183faSMickaël Salaün TEST_F_FORK(layout1, unpriv) 621371183faSMickaël Salaün { 622e1199815SMickaël Salaün const struct rule rules[] = { 623e1199815SMickaël Salaün { 624e1199815SMickaël Salaün .path = dir_s1d2, 625e1199815SMickaël Salaün .access = ACCESS_RO, 626e1199815SMickaël Salaün }, 627135464f9SMickaël Salaün {}, 628e1199815SMickaël Salaün }; 629e1199815SMickaël Salaün int ruleset_fd; 630e1199815SMickaël Salaün 631e1199815SMickaël Salaün drop_caps(_metadata); 632e1199815SMickaël Salaün 633e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 634e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 635e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 636e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 637e1199815SMickaël Salaün 638e1199815SMickaël Salaün /* enforce_ruleset() calls prctl(no_new_privs). */ 639e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 640e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 641e1199815SMickaël Salaün } 642e1199815SMickaël Salaün 643e1199815SMickaël Salaün TEST_F_FORK(layout1, effective_access) 644e1199815SMickaël Salaün { 645e1199815SMickaël Salaün const struct rule rules[] = { 646e1199815SMickaël Salaün { 647e1199815SMickaël Salaün .path = dir_s1d2, 648e1199815SMickaël Salaün .access = ACCESS_RO, 649e1199815SMickaël Salaün }, 650e1199815SMickaël Salaün { 651e1199815SMickaël Salaün .path = file1_s2d2, 652e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 653e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 654e1199815SMickaël Salaün }, 655135464f9SMickaël Salaün {}, 656e1199815SMickaël Salaün }; 657e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 658e1199815SMickaël Salaün char buf; 659e1199815SMickaël Salaün int reg_fd; 660e1199815SMickaël Salaün 661e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 662e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 663e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 664e1199815SMickaël Salaün 665d1788ad9SMickaël Salaün /* Tests on a directory (with or without O_PATH). */ 666e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 667d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 668e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 669d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 670e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 671d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 672d1788ad9SMickaël Salaün 673e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 674e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 675e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 676e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 677e1199815SMickaël Salaün 678d1788ad9SMickaël Salaün /* Tests on a file (with or without O_PATH). */ 679e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 680d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 681d1788ad9SMickaël Salaün 682e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 683e1199815SMickaël Salaün 684e1199815SMickaël Salaün /* Checks effective read and write actions. */ 685e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 686e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 687e1199815SMickaël Salaün ASSERT_EQ(1, write(reg_fd, ".", 1)); 688e1199815SMickaël Salaün ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 689e1199815SMickaël Salaün ASSERT_EQ(1, read(reg_fd, &buf, 1)); 690e1199815SMickaël Salaün ASSERT_EQ('.', buf); 691e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 692e1199815SMickaël Salaün 693e1199815SMickaël Salaün /* Just in case, double-checks effective actions. */ 694e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 695e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 696e1199815SMickaël Salaün ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 697e1199815SMickaël Salaün ASSERT_EQ(EBADF, errno); 698e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 699e1199815SMickaël Salaün } 700e1199815SMickaël Salaün 701e1199815SMickaël Salaün TEST_F_FORK(layout1, unhandled_access) 702e1199815SMickaël Salaün { 703e1199815SMickaël Salaün const struct rule rules[] = { 704e1199815SMickaël Salaün { 705e1199815SMickaël Salaün .path = dir_s1d2, 706e1199815SMickaël Salaün .access = ACCESS_RO, 707e1199815SMickaël Salaün }, 708135464f9SMickaël Salaün {}, 709e1199815SMickaël Salaün }; 710e1199815SMickaël Salaün /* Here, we only handle read accesses, not write accesses. */ 711e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 712e1199815SMickaël Salaün 713e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 714e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 715e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 716e1199815SMickaël Salaün 717e1199815SMickaël Salaün /* 718e1199815SMickaël Salaün * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 719e1199815SMickaël Salaün * opening for write-only should be allowed, but not read-write. 720e1199815SMickaël Salaün */ 721e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 722e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 723e1199815SMickaël Salaün 724e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 725e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 726e1199815SMickaël Salaün } 727e1199815SMickaël Salaün 728e1199815SMickaël Salaün TEST_F_FORK(layout1, ruleset_overlap) 729e1199815SMickaël Salaün { 730e1199815SMickaël Salaün const struct rule rules[] = { 731e1199815SMickaël Salaün /* These rules should be ORed among them. */ 732e1199815SMickaël Salaün { 733e1199815SMickaël Salaün .path = dir_s1d2, 734e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 735e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 736e1199815SMickaël Salaün }, 737e1199815SMickaël Salaün { 738e1199815SMickaël Salaün .path = dir_s1d2, 739e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 740e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 741e1199815SMickaël Salaün }, 742135464f9SMickaël Salaün {}, 743e1199815SMickaël Salaün }; 744e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 745e1199815SMickaël Salaün 746e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 747e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 748e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 749e1199815SMickaël Salaün 750e1199815SMickaël Salaün /* Checks s1d1 hierarchy. */ 751e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 752e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 753e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 754e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 755e1199815SMickaël Salaün 756e1199815SMickaël Salaün /* Checks s1d2 hierarchy. */ 757e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 758e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 759e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 760e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 761e1199815SMickaël Salaün 762e1199815SMickaël Salaün /* Checks s1d3 hierarchy. */ 763e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 764e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 765e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 766e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 767e1199815SMickaël Salaün } 768e1199815SMickaël Salaün 7698ba0005fSMickaël Salaün TEST_F_FORK(layout1, layer_rule_unions) 7708ba0005fSMickaël Salaün { 7718ba0005fSMickaël Salaün const struct rule layer1[] = { 7728ba0005fSMickaël Salaün { 7738ba0005fSMickaël Salaün .path = dir_s1d2, 7748ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 7758ba0005fSMickaël Salaün }, 7768ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 7778ba0005fSMickaël Salaün { 7788ba0005fSMickaël Salaün .path = dir_s1d3, 7798ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 7808ba0005fSMickaël Salaün }, 7818ba0005fSMickaël Salaün {}, 7828ba0005fSMickaël Salaün }; 7838ba0005fSMickaël Salaün const struct rule layer2[] = { 7848ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 7858ba0005fSMickaël Salaün { 7868ba0005fSMickaël Salaün .path = dir_s1d2, 7878ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 7888ba0005fSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 7898ba0005fSMickaël Salaün }, 7908ba0005fSMickaël Salaün {}, 7918ba0005fSMickaël Salaün }; 7928ba0005fSMickaël Salaün const struct rule layer3[] = { 7938ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 7948ba0005fSMickaël Salaün { 7958ba0005fSMickaël Salaün .path = dir_s1d2, 7968ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 7978ba0005fSMickaël Salaün }, 7988ba0005fSMickaël Salaün {}, 7998ba0005fSMickaël Salaün }; 8008ba0005fSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1); 8018ba0005fSMickaël Salaün 8028ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 8038ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 8048ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 8058ba0005fSMickaël Salaün 8068ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer1. */ 8078ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 8088ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 8098ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 8108ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8118ba0005fSMickaël Salaün 8128ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer1. */ 8138ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 8148ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 8158ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 8168ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8178ba0005fSMickaël Salaün 8188ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer1. */ 8198ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 8208ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 8218ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 8228ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 8238ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8248ba0005fSMickaël Salaün 8258ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 8268ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2); 8278ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 8288ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 8298ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 8308ba0005fSMickaël Salaün 8318ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer2. */ 8328ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 8338ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 8348ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 8358ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8368ba0005fSMickaël Salaün 8378ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer2. */ 8388ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 8398ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 8408ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 8418ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8428ba0005fSMickaël Salaün 8438ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer2. */ 8448ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 8458ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 8468ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 8478ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 8488ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8498ba0005fSMickaël Salaün 8508ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 8518ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3); 8528ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 8538ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 8548ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 8558ba0005fSMickaël Salaün 8568ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer3. */ 8578ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 8588ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 8598ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 8608ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8618ba0005fSMickaël Salaün 8628ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer3. */ 8638ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 8648ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 8658ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 8668ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8678ba0005fSMickaël Salaün 8688ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer3. */ 8698ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 8708ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 8718ba0005fSMickaël Salaün /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 8728ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 8738ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8748ba0005fSMickaël Salaün } 8758ba0005fSMickaël Salaün 876e1199815SMickaël Salaün TEST_F_FORK(layout1, non_overlapping_accesses) 877e1199815SMickaël Salaün { 878e1199815SMickaël Salaün const struct rule layer1[] = { 879e1199815SMickaël Salaün { 880e1199815SMickaël Salaün .path = dir_s1d2, 881e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 882e1199815SMickaël Salaün }, 883135464f9SMickaël Salaün {}, 884e1199815SMickaël Salaün }; 885e1199815SMickaël Salaün const struct rule layer2[] = { 886e1199815SMickaël Salaün { 887e1199815SMickaël Salaün .path = dir_s1d3, 888e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 889e1199815SMickaël Salaün }, 890135464f9SMickaël Salaün {}, 891e1199815SMickaël Salaün }; 892e1199815SMickaël Salaün int ruleset_fd; 893e1199815SMickaël Salaün 894e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 895e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 896e1199815SMickaël Salaün 897371183faSMickaël Salaün ruleset_fd = 898371183faSMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 899e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 900e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 901e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 902e1199815SMickaël Salaün 903e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 904e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 905e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 906e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 907e1199815SMickaël Salaün 908e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 909e1199815SMickaël Salaün layer2); 910e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 911e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 912e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 913e1199815SMickaël Salaün 914e1199815SMickaël Salaün /* Unchanged accesses for file creation. */ 915e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 916e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 917e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 918e1199815SMickaël Salaün 919e1199815SMickaël Salaün /* Checks file removing. */ 920e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d2)); 921e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 922e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 923e1199815SMickaël Salaün } 924e1199815SMickaël Salaün 925e1199815SMickaël Salaün TEST_F_FORK(layout1, interleaved_masked_accesses) 926e1199815SMickaël Salaün { 927e1199815SMickaël Salaün /* 928e1199815SMickaël Salaün * Checks overly restrictive rules: 929e1199815SMickaël Salaün * layer 1: allows R s1d1/s1d2/s1d3/file1 930e1199815SMickaël Salaün * layer 2: allows RW s1d1/s1d2/s1d3 931e1199815SMickaël Salaün * allows W s1d1/s1d2 932e1199815SMickaël Salaün * denies R s1d1/s1d2 933e1199815SMickaël Salaün * layer 3: allows R s1d1 934e1199815SMickaël Salaün * layer 4: allows R s1d1/s1d2 935e1199815SMickaël Salaün * denies W s1d1/s1d2 936e1199815SMickaël Salaün * layer 5: allows R s1d1/s1d2 937e1199815SMickaël Salaün * layer 6: allows X ---- 938e1199815SMickaël Salaün * layer 7: allows W s1d1/s1d2 939e1199815SMickaël Salaün * denies R s1d1/s1d2 940e1199815SMickaël Salaün */ 941e1199815SMickaël Salaün const struct rule layer1_read[] = { 942e1199815SMickaël Salaün /* Allows read access to file1_s1d3 with the first layer. */ 943e1199815SMickaël Salaün { 944e1199815SMickaël Salaün .path = file1_s1d3, 945e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 946e1199815SMickaël Salaün }, 947135464f9SMickaël Salaün {}, 948e1199815SMickaël Salaün }; 949e1199815SMickaël Salaün /* First rule with write restrictions. */ 950e1199815SMickaël Salaün const struct rule layer2_read_write[] = { 951e1199815SMickaël Salaün /* Start by granting read-write access via its parent directory... */ 952e1199815SMickaël Salaün { 953e1199815SMickaël Salaün .path = dir_s1d3, 954e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 955e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 956e1199815SMickaël Salaün }, 957e1199815SMickaël Salaün /* ...but also denies read access via its grandparent directory. */ 958e1199815SMickaël Salaün { 959e1199815SMickaël Salaün .path = dir_s1d2, 960e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 961e1199815SMickaël Salaün }, 962135464f9SMickaël Salaün {}, 963e1199815SMickaël Salaün }; 964e1199815SMickaël Salaün const struct rule layer3_read[] = { 965e1199815SMickaël Salaün /* Allows read access via its great-grandparent directory. */ 966e1199815SMickaël Salaün { 967e1199815SMickaël Salaün .path = dir_s1d1, 968e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 969e1199815SMickaël Salaün }, 970135464f9SMickaël Salaün {}, 971e1199815SMickaël Salaün }; 972e1199815SMickaël Salaün const struct rule layer4_read_write[] = { 973e1199815SMickaël Salaün /* 974e1199815SMickaël Salaün * Try to confuse the deny access by denying write (but not 975e1199815SMickaël Salaün * read) access via its grandparent directory. 976e1199815SMickaël Salaün */ 977e1199815SMickaël Salaün { 978e1199815SMickaël Salaün .path = dir_s1d2, 979e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 980e1199815SMickaël Salaün }, 981135464f9SMickaël Salaün {}, 982e1199815SMickaël Salaün }; 983e1199815SMickaël Salaün const struct rule layer5_read[] = { 984e1199815SMickaël Salaün /* 985e1199815SMickaël Salaün * Try to override layer2's deny read access by explicitly 986e1199815SMickaël Salaün * allowing read access via file1_s1d3's grandparent. 987e1199815SMickaël Salaün */ 988e1199815SMickaël Salaün { 989e1199815SMickaël Salaün .path = dir_s1d2, 990e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 991e1199815SMickaël Salaün }, 992135464f9SMickaël Salaün {}, 993e1199815SMickaël Salaün }; 994e1199815SMickaël Salaün const struct rule layer6_execute[] = { 995e1199815SMickaël Salaün /* 996e1199815SMickaël Salaün * Restricts an unrelated file hierarchy with a new access 997e1199815SMickaël Salaün * (non-overlapping) type. 998e1199815SMickaël Salaün */ 999e1199815SMickaël Salaün { 1000e1199815SMickaël Salaün .path = dir_s2d1, 1001e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 1002e1199815SMickaël Salaün }, 1003135464f9SMickaël Salaün {}, 1004e1199815SMickaël Salaün }; 1005e1199815SMickaël Salaün const struct rule layer7_read_write[] = { 1006e1199815SMickaël Salaün /* 1007e1199815SMickaël Salaün * Finally, denies read access to file1_s1d3 via its 1008e1199815SMickaël Salaün * grandparent. 1009e1199815SMickaël Salaün */ 1010e1199815SMickaël Salaün { 1011e1199815SMickaël Salaün .path = dir_s1d2, 1012e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1013e1199815SMickaël Salaün }, 1014135464f9SMickaël Salaün {}, 1015e1199815SMickaël Salaün }; 1016e1199815SMickaël Salaün int ruleset_fd; 1017e1199815SMickaël Salaün 1018e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1019e1199815SMickaël Salaün layer1_read); 1020e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1021e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1022e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1023e1199815SMickaël Salaün 1024e1199815SMickaël Salaün /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1025e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1026e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1027e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1028e1199815SMickaël Salaün 1029371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1030371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1031371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1032371183faSMickaël Salaün layer2_read_write); 1033e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1034e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1035e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1036e1199815SMickaël Salaün 1037e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 2. */ 1038e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1039e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1040e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1041e1199815SMickaël Salaün 1042e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1043e1199815SMickaël Salaün layer3_read); 1044e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1045e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1046e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1047e1199815SMickaël Salaün 1048e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 3. */ 1049e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1050e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1051e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1052e1199815SMickaël Salaün 1053e1199815SMickaël Salaün /* This time, denies write access for the file hierarchy. */ 1054371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1055371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1056371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1057371183faSMickaël Salaün layer4_read_write); 1058e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1059e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1060e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1061e1199815SMickaël Salaün 1062e1199815SMickaël Salaün /* 1063e1199815SMickaël Salaün * Checks that the only change with layer 4 is that write access is 1064e1199815SMickaël Salaün * denied. 1065e1199815SMickaël Salaün */ 1066e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1067e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1068e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1069e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1070e1199815SMickaël Salaün 1071e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1072e1199815SMickaël Salaün layer5_read); 1073e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1074e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1075e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1076e1199815SMickaël Salaün 1077e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 5. */ 1078e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1079e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1080e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1081e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1082e1199815SMickaël Salaün 1083e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 1084e1199815SMickaël Salaün layer6_execute); 1085e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1086e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1087e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1088e1199815SMickaël Salaün 1089e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 6. */ 1090e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1091e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1092e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1093e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1094e1199815SMickaël Salaün 1095371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1096371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1097371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1098371183faSMickaël Salaün layer7_read_write); 1099e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1100e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1101e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1102e1199815SMickaël Salaün 1103e1199815SMickaël Salaün /* Checks read access is now denied with layer 7. */ 1104e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1105e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1106e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1107e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1108e1199815SMickaël Salaün } 1109e1199815SMickaël Salaün 1110e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_subset) 1111e1199815SMickaël Salaün { 1112e1199815SMickaël Salaün const struct rule rules[] = { 1113e1199815SMickaël Salaün { 1114e1199815SMickaël Salaün .path = dir_s1d2, 1115e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 1116e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1117e1199815SMickaël Salaün }, 1118135464f9SMickaël Salaün {}, 1119e1199815SMickaël Salaün }; 1120e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1121e1199815SMickaël Salaün 1122e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1123e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1124e1199815SMickaël Salaün 1125e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1126e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1127e1199815SMickaël Salaün 1128e1199815SMickaël Salaün /* Write access is forbidden. */ 1129e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1130e1199815SMickaël Salaün /* Readdir access is allowed. */ 1131e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1132e1199815SMickaël Salaün 1133e1199815SMickaël Salaün /* Write access is forbidden. */ 1134e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1135e1199815SMickaël Salaün /* Readdir access is allowed. */ 1136e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1137e1199815SMickaël Salaün 1138e1199815SMickaël Salaün /* 1139e1199815SMickaël Salaün * Tests shared rule extension: the following rules should not grant 1140e1199815SMickaël Salaün * any new access, only remove some. Once enforced, these rules are 1141e1199815SMickaël Salaün * ANDed with the previous ones. 1142e1199815SMickaël Salaün */ 1143e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1144e1199815SMickaël Salaün dir_s1d2); 1145e1199815SMickaël Salaün /* 1146e1199815SMickaël Salaün * According to ruleset_fd, dir_s1d2 should now have the 1147e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1148e1199815SMickaël Salaün * access rights (even if this directory is opened a second time). 1149e1199815SMickaël Salaün * However, when enforcing this updated ruleset, the ruleset tied to 1150e1199815SMickaël Salaün * the current process (i.e. its domain) will still only have the 1151e1199815SMickaël Salaün * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1152e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1153e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1154e1199815SMickaël Salaün * be a privilege escalation. 1155e1199815SMickaël Salaün */ 1156e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1157e1199815SMickaël Salaün 1158e1199815SMickaël Salaün /* Same tests and results as above. */ 1159e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1160e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1161e1199815SMickaël Salaün 1162e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1163e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1164e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1165e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1166e1199815SMickaël Salaün 1167e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1168e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1169e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1170e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1171e1199815SMickaël Salaün 1172e1199815SMickaël Salaün /* 1173e1199815SMickaël Salaün * Try to get more privileges by adding new access rights to the parent 1174e1199815SMickaël Salaün * directory: dir_s1d1. 1175e1199815SMickaël Salaün */ 1176e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 1177e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1178e1199815SMickaël Salaün 1179e1199815SMickaël Salaün /* Same tests and results as above. */ 1180e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1181e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1182e1199815SMickaël Salaün 1183e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1184e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1185e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1186e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1187e1199815SMickaël Salaün 1188e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1189e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1190e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1191e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1192e1199815SMickaël Salaün 1193e1199815SMickaël Salaün /* 1194e1199815SMickaël Salaün * Now, dir_s1d3 get a new rule tied to it, only allowing 1195e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1196e1199815SMickaël Salaün * that there was no rule tied to it before. 1197e1199815SMickaël Salaün */ 1198e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1199e1199815SMickaël Salaün dir_s1d3); 1200e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1201e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1202e1199815SMickaël Salaün 1203e1199815SMickaël Salaün /* 1204e1199815SMickaël Salaün * Same tests and results as above, except for open(dir_s1d3) which is 1205e1199815SMickaël Salaün * now denied because the new rule mask the rule previously inherited 1206e1199815SMickaël Salaün * from dir_s1d2. 1207e1199815SMickaël Salaün */ 1208e1199815SMickaël Salaün 1209e1199815SMickaël Salaün /* Same tests and results as above. */ 1210e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1211e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1212e1199815SMickaël Salaün 1213e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1214e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1215e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1216e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1217e1199815SMickaël Salaün 1218e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1219e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1220e1199815SMickaël Salaün /* 1221e1199815SMickaël Salaün * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1222e1199815SMickaël Salaün * the same layer. 1223e1199815SMickaël Salaün */ 1224e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1225e1199815SMickaël Salaün } 1226e1199815SMickaël Salaün 1227e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_superset) 1228e1199815SMickaël Salaün { 1229e1199815SMickaël Salaün const struct rule rules[] = { 1230e1199815SMickaël Salaün { 1231e1199815SMickaël Salaün .path = dir_s1d3, 1232e1199815SMickaël Salaün .access = ACCESS_RO, 1233e1199815SMickaël Salaün }, 1234135464f9SMickaël Salaün {}, 1235e1199815SMickaël Salaün }; 1236e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1237e1199815SMickaël Salaün 1238e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1239e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1240e1199815SMickaël Salaün 1241e1199815SMickaël Salaün /* Readdir access is denied for dir_s1d2. */ 1242e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1243e1199815SMickaël Salaün /* Readdir access is allowed for dir_s1d3. */ 1244e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1245e1199815SMickaël Salaün /* File access is allowed for file1_s1d3. */ 1246e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1247e1199815SMickaël Salaün 1248e1199815SMickaël Salaün /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1249371183faSMickaël Salaün add_path_beneath(_metadata, ruleset_fd, 1250371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1251371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1252371183faSMickaël Salaün dir_s1d2); 1253e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1254e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1255e1199815SMickaël Salaün 1256e1199815SMickaël Salaün /* Readdir access is still denied for dir_s1d2. */ 1257e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1258e1199815SMickaël Salaün /* Readdir access is still allowed for dir_s1d3. */ 1259e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1260e1199815SMickaël Salaün /* File access is still allowed for file1_s1d3. */ 1261e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1262e1199815SMickaël Salaün } 1263e1199815SMickaël Salaün 1264e1199815SMickaël Salaün TEST_F_FORK(layout1, max_layers) 1265e1199815SMickaël Salaün { 1266e1199815SMickaël Salaün int i, err; 1267e1199815SMickaël Salaün const struct rule rules[] = { 1268e1199815SMickaël Salaün { 1269e1199815SMickaël Salaün .path = dir_s1d2, 1270e1199815SMickaël Salaün .access = ACCESS_RO, 1271e1199815SMickaël Salaün }, 1272135464f9SMickaël Salaün {}, 1273e1199815SMickaël Salaün }; 1274e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1275e1199815SMickaël Salaün 1276e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 127775c542d6SMickaël Salaün for (i = 0; i < 16; i++) 1278e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1279e1199815SMickaël Salaün 1280e1199815SMickaël Salaün for (i = 0; i < 2; i++) { 1281e1199815SMickaël Salaün err = landlock_restrict_self(ruleset_fd, 0); 1282e1199815SMickaël Salaün ASSERT_EQ(-1, err); 1283e1199815SMickaël Salaün ASSERT_EQ(E2BIG, errno); 1284e1199815SMickaël Salaün } 1285e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1286e1199815SMickaël Salaün } 1287e1199815SMickaël Salaün 1288e1199815SMickaël Salaün TEST_F_FORK(layout1, empty_or_same_ruleset) 1289e1199815SMickaël Salaün { 1290e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = {}; 1291e1199815SMickaël Salaün int ruleset_fd; 1292e1199815SMickaël Salaün 1293e1199815SMickaël Salaün /* Tests empty handled_access_fs. */ 1294371183faSMickaël Salaün ruleset_fd = 1295371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1296e1199815SMickaël Salaün ASSERT_LE(-1, ruleset_fd); 1297e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 1298e1199815SMickaël Salaün 1299e1199815SMickaël Salaün /* Enforces policy which deny read access to all files. */ 1300e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1301371183faSMickaël Salaün ruleset_fd = 1302371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1303e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1304e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1305e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1306e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1307e1199815SMickaël Salaün 1308e1199815SMickaël Salaün /* Nests a policy which deny read access to all directories. */ 1309e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1310371183faSMickaël Salaün ruleset_fd = 1311371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1312e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1313e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1314e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1315e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1316e1199815SMickaël Salaün 1317e1199815SMickaël Salaün /* Enforces a second time with the same ruleset. */ 1318e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1319e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1320e1199815SMickaël Salaün } 1321e1199815SMickaël Salaün 1322e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_on_mountpoint) 1323e1199815SMickaël Salaün { 1324e1199815SMickaël Salaün const struct rule rules[] = { 1325e1199815SMickaël Salaün { 1326e1199815SMickaël Salaün .path = dir_s1d1, 1327e1199815SMickaël Salaün .access = ACCESS_RO, 1328e1199815SMickaël Salaün }, 1329e1199815SMickaël Salaün { 1330e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1331e1199815SMickaël Salaün .path = dir_s3d2, 1332e1199815SMickaël Salaün .access = ACCESS_RO, 1333e1199815SMickaël Salaün }, 1334135464f9SMickaël Salaün {}, 1335e1199815SMickaël Salaün }; 1336e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1337e1199815SMickaël Salaün 1338e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1339e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1340e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1341e1199815SMickaël Salaün 1342e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1343e1199815SMickaël Salaün 1344e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1345e1199815SMickaël Salaün 1346e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1347e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1348e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1349e1199815SMickaël Salaün } 1350e1199815SMickaël Salaün 1351e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_mountpoint) 1352e1199815SMickaël Salaün { 1353e1199815SMickaël Salaün const struct rule rules[] = { 1354e1199815SMickaël Salaün { 1355e1199815SMickaël Salaün .path = dir_s1d1, 1356e1199815SMickaël Salaün .access = ACCESS_RO, 1357e1199815SMickaël Salaün }, 1358e1199815SMickaël Salaün { 1359e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1360e1199815SMickaël Salaün .path = dir_s3d1, 1361e1199815SMickaël Salaün .access = ACCESS_RO, 1362e1199815SMickaël Salaün }, 1363135464f9SMickaël Salaün {}, 1364e1199815SMickaël Salaün }; 1365e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1366e1199815SMickaël Salaün 1367e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1368e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1369e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1370e1199815SMickaël Salaün 1371e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1372e1199815SMickaël Salaün 1373e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1374e1199815SMickaël Salaün 1375e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1376e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1377e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1378e1199815SMickaël Salaün } 1379e1199815SMickaël Salaün 1380e1199815SMickaël Salaün /* 1381e1199815SMickaël Salaün * This test verifies that we can apply a landlock rule on the root directory 1382e1199815SMickaël Salaün * (which might require special handling). 1383e1199815SMickaël Salaün */ 1384e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1385e1199815SMickaël Salaün { 1386e1199815SMickaël Salaün struct rule rules[] = { 1387e1199815SMickaël Salaün { 1388e1199815SMickaël Salaün .path = "/", 1389e1199815SMickaël Salaün .access = ACCESS_RO, 1390e1199815SMickaël Salaün }, 1391135464f9SMickaël Salaün {}, 1392e1199815SMickaël Salaün }; 1393e1199815SMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1394e1199815SMickaël Salaün 1395e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1396e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1397e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1398e1199815SMickaël Salaün 1399e1199815SMickaël Salaün /* Checks allowed access. */ 1400e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY)); 1401e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1402e1199815SMickaël Salaün 1403e1199815SMickaël Salaün rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1404e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1405e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1406e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1407e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1408e1199815SMickaël Salaün 1409e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1410e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1411e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1412e1199815SMickaël Salaün } 1413e1199815SMickaël Salaün 1414e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_deny) 1415e1199815SMickaël Salaün { 1416e1199815SMickaël Salaün const struct rule rules[] = { 1417e1199815SMickaël Salaün { 1418e1199815SMickaël Salaün .path = "/", 1419e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1420e1199815SMickaël Salaün }, 1421135464f9SMickaël Salaün {}, 1422e1199815SMickaël Salaün }; 1423e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1424e1199815SMickaël Salaün 1425e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1426e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1427e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1428e1199815SMickaël Salaün 1429e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1430e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1431e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1432e1199815SMickaël Salaün } 1433e1199815SMickaël Salaün 1434e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_inside_mount_ns) 1435e1199815SMickaël Salaün { 1436e1199815SMickaël Salaün const struct rule rules[] = { 1437e1199815SMickaël Salaün { 1438e1199815SMickaël Salaün .path = "s3d3", 1439e1199815SMickaël Salaün .access = ACCESS_RO, 1440e1199815SMickaël Salaün }, 1441135464f9SMickaël Salaün {}, 1442e1199815SMickaël Salaün }; 1443e1199815SMickaël Salaün int ruleset_fd; 1444e1199815SMickaël Salaün 1445e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 144687129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1447371183faSMickaël Salaün { 1448e1199815SMickaël Salaün TH_LOG("Failed to pivot root: %s", strerror(errno)); 1449e1199815SMickaël Salaün }; 1450e1199815SMickaël Salaün ASSERT_EQ(0, chdir("/")); 1451e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1452e1199815SMickaël Salaün 1453e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1454e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1455e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1456e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1457e1199815SMickaël Salaün 1458e1199815SMickaël Salaün ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1459e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1460e1199815SMickaël Salaün } 1461e1199815SMickaël Salaün 1462e1199815SMickaël Salaün TEST_F_FORK(layout1, mount_and_pivot) 1463e1199815SMickaël Salaün { 1464e1199815SMickaël Salaün const struct rule rules[] = { 1465e1199815SMickaël Salaün { 1466e1199815SMickaël Salaün .path = dir_s3d2, 1467e1199815SMickaël Salaün .access = ACCESS_RO, 1468e1199815SMickaël Salaün }, 1469135464f9SMickaël Salaün {}, 1470e1199815SMickaël Salaün }; 1471e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1472e1199815SMickaël Salaün 1473e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1474e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1475e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1476e1199815SMickaël Salaün 1477e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1478e1199815SMickaël Salaün ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1479e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 148087129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1481e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1482e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1483e1199815SMickaël Salaün } 1484e1199815SMickaël Salaün 1485e1199815SMickaël Salaün TEST_F_FORK(layout1, move_mount) 1486e1199815SMickaël Salaün { 1487e1199815SMickaël Salaün const struct rule rules[] = { 1488e1199815SMickaël Salaün { 1489e1199815SMickaël Salaün .path = dir_s3d2, 1490e1199815SMickaël Salaün .access = ACCESS_RO, 1491e1199815SMickaël Salaün }, 1492135464f9SMickaël Salaün {}, 1493e1199815SMickaël Salaün }; 1494e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1495e1199815SMickaël Salaün 1496e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1497e1199815SMickaël Salaün 1498e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 149987129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1500371183faSMickaël Salaün dir_s1d2, 0)) 1501371183faSMickaël Salaün { 1502e1199815SMickaël Salaün TH_LOG("Failed to move mount: %s", strerror(errno)); 1503e1199815SMickaël Salaün } 1504e1199815SMickaël Salaün 150587129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1506e1199815SMickaël Salaün dir_s3d2, 0)); 1507e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1508e1199815SMickaël Salaün 1509e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1510e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1511e1199815SMickaël Salaün 1512e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 151387129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1514e1199815SMickaël Salaün dir_s1d2, 0)); 1515e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1516e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1517e1199815SMickaël Salaün } 1518e1199815SMickaël Salaün 1519e1199815SMickaël Salaün TEST_F_FORK(layout1, release_inodes) 1520e1199815SMickaël Salaün { 1521e1199815SMickaël Salaün const struct rule rules[] = { 1522e1199815SMickaël Salaün { 1523e1199815SMickaël Salaün .path = dir_s1d1, 1524e1199815SMickaël Salaün .access = ACCESS_RO, 1525e1199815SMickaël Salaün }, 1526e1199815SMickaël Salaün { 1527e1199815SMickaël Salaün .path = dir_s3d2, 1528e1199815SMickaël Salaün .access = ACCESS_RO, 1529e1199815SMickaël Salaün }, 1530e1199815SMickaël Salaün { 1531e1199815SMickaël Salaün .path = dir_s3d3, 1532e1199815SMickaël Salaün .access = ACCESS_RO, 1533e1199815SMickaël Salaün }, 1534135464f9SMickaël Salaün {}, 1535e1199815SMickaël Salaün }; 1536e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1537e1199815SMickaël Salaün 1538e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1539e1199815SMickaël Salaün /* Unmount a file hierarchy while it is being used by a ruleset. */ 1540e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1541e1199815SMickaël Salaün ASSERT_EQ(0, umount(dir_s3d2)); 1542e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1543e1199815SMickaël Salaün 1544e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1545e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1546e1199815SMickaël Salaün 1547e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1548e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1549e1199815SMickaël Salaün /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1550e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1551e1199815SMickaël Salaün } 1552e1199815SMickaël Salaün 1553e1199815SMickaël Salaün enum relative_access { 1554e1199815SMickaël Salaün REL_OPEN, 1555e1199815SMickaël Salaün REL_CHDIR, 1556e1199815SMickaël Salaün REL_CHROOT_ONLY, 1557e1199815SMickaël Salaün REL_CHROOT_CHDIR, 1558e1199815SMickaël Salaün }; 1559e1199815SMickaël Salaün 1560e1199815SMickaël Salaün static void test_relative_path(struct __test_metadata *const _metadata, 1561e1199815SMickaël Salaün const enum relative_access rel) 1562e1199815SMickaël Salaün { 1563e1199815SMickaël Salaün /* 1564e1199815SMickaël Salaün * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1565e1199815SMickaël Salaün * is not a disconnected root directory). 1566e1199815SMickaël Salaün */ 1567e1199815SMickaël Salaün const struct rule layer1_base[] = { 1568e1199815SMickaël Salaün { 1569e1199815SMickaël Salaün .path = TMP_DIR, 1570e1199815SMickaël Salaün .access = ACCESS_RO, 1571e1199815SMickaël Salaün }, 1572135464f9SMickaël Salaün {}, 1573e1199815SMickaël Salaün }; 1574e1199815SMickaël Salaün const struct rule layer2_subs[] = { 1575e1199815SMickaël Salaün { 1576e1199815SMickaël Salaün .path = dir_s1d2, 1577e1199815SMickaël Salaün .access = ACCESS_RO, 1578e1199815SMickaël Salaün }, 1579e1199815SMickaël Salaün { 1580e1199815SMickaël Salaün .path = dir_s2d2, 1581e1199815SMickaël Salaün .access = ACCESS_RO, 1582e1199815SMickaël Salaün }, 1583135464f9SMickaël Salaün {}, 1584e1199815SMickaël Salaün }; 1585e1199815SMickaël Salaün int dirfd, ruleset_fd; 1586e1199815SMickaël Salaün 1587e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1588e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1589e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1590e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1591e1199815SMickaël Salaün 1592e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1593e1199815SMickaël Salaün 1594e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1595e1199815SMickaël Salaün switch (rel) { 1596e1199815SMickaël Salaün case REL_OPEN: 1597e1199815SMickaël Salaün case REL_CHDIR: 1598e1199815SMickaël Salaün break; 1599e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1600e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s2d2)); 1601e1199815SMickaël Salaün break; 1602e1199815SMickaël Salaün case REL_CHROOT_CHDIR: 1603e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1604e1199815SMickaël Salaün break; 1605e1199815SMickaël Salaün default: 1606e1199815SMickaël Salaün ASSERT_TRUE(false); 1607e1199815SMickaël Salaün return; 1608e1199815SMickaël Salaün } 1609e1199815SMickaël Salaün 1610e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_CHROOT); 1611e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1612e1199815SMickaël Salaün 1613e1199815SMickaël Salaün switch (rel) { 1614e1199815SMickaël Salaün case REL_OPEN: 1615e1199815SMickaël Salaün dirfd = open(dir_s1d2, O_DIRECTORY); 1616e1199815SMickaël Salaün ASSERT_LE(0, dirfd); 1617e1199815SMickaël Salaün break; 1618e1199815SMickaël Salaün case REL_CHDIR: 1619e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1620e1199815SMickaël Salaün dirfd = AT_FDCWD; 1621e1199815SMickaël Salaün break; 1622e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1623e1199815SMickaël Salaün /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1624371183faSMickaël Salaün ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 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 case REL_CHROOT_CHDIR: 1631e1199815SMickaël Salaün /* Do chroot into dir_s1d2. */ 1632371183faSMickaël Salaün ASSERT_EQ(0, chroot(".")) 1633371183faSMickaël Salaün { 1634e1199815SMickaël Salaün TH_LOG("Failed to chroot: %s", strerror(errno)); 1635e1199815SMickaël Salaün } 1636e1199815SMickaël Salaün dirfd = AT_FDCWD; 1637e1199815SMickaël Salaün break; 1638e1199815SMickaël Salaün } 1639e1199815SMickaël Salaün 1640e1199815SMickaël Salaün ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1641e1199815SMickaël Salaün test_open_rel(dirfd, "..", O_RDONLY)); 1642e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1643e1199815SMickaël Salaün 1644e1199815SMickaël Salaün if (rel == REL_CHROOT_ONLY) { 1645e1199815SMickaël Salaün /* The current directory is dir_s2d2. */ 1646e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1647e1199815SMickaël Salaün } else { 1648e1199815SMickaël Salaün /* The current directory is dir_s1d2. */ 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_ONLY || rel == REL_CHROOT_CHDIR) { 1653e1199815SMickaël Salaün /* Checks the root dir_s1d2. */ 1654e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1655e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1656e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1657e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1658e1199815SMickaël Salaün } 1659e1199815SMickaël Salaün 1660e1199815SMickaël Salaün if (rel != REL_CHROOT_CHDIR) { 1661e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1662e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1663371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1664371183faSMickaël Salaün O_RDONLY)); 1665e1199815SMickaël Salaün 1666e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1667e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1668371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1669371183faSMickaël Salaün O_RDONLY)); 1670e1199815SMickaël Salaün } 1671e1199815SMickaël Salaün 1672e1199815SMickaël Salaün if (rel == REL_OPEN) 1673e1199815SMickaël Salaün ASSERT_EQ(0, close(dirfd)); 1674e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1675e1199815SMickaël Salaün } 1676e1199815SMickaël Salaün 1677e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_open) 1678e1199815SMickaël Salaün { 1679e1199815SMickaël Salaün test_relative_path(_metadata, REL_OPEN); 1680e1199815SMickaël Salaün } 1681e1199815SMickaël Salaün 1682e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chdir) 1683e1199815SMickaël Salaün { 1684e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHDIR); 1685e1199815SMickaël Salaün } 1686e1199815SMickaël Salaün 1687e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_only) 1688e1199815SMickaël Salaün { 1689e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_ONLY); 1690e1199815SMickaël Salaün } 1691e1199815SMickaël Salaün 1692e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_chdir) 1693e1199815SMickaël Salaün { 1694e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_CHDIR); 1695e1199815SMickaël Salaün } 1696e1199815SMickaël Salaün 1697e1199815SMickaël Salaün static void copy_binary(struct __test_metadata *const _metadata, 1698e1199815SMickaël Salaün const char *const dst_path) 1699e1199815SMickaël Salaün { 1700e1199815SMickaël Salaün int dst_fd, src_fd; 1701e1199815SMickaël Salaün struct stat statbuf; 1702e1199815SMickaël Salaün 1703e1199815SMickaël Salaün dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1704371183faSMickaël Salaün ASSERT_LE(0, dst_fd) 1705371183faSMickaël Salaün { 1706371183faSMickaël Salaün TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1707e1199815SMickaël Salaün } 1708e1199815SMickaël Salaün src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1709371183faSMickaël Salaün ASSERT_LE(0, src_fd) 1710371183faSMickaël Salaün { 1711e1199815SMickaël Salaün TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1712e1199815SMickaël Salaün strerror(errno)); 1713e1199815SMickaël Salaün } 1714e1199815SMickaël Salaün ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1715371183faSMickaël Salaün ASSERT_EQ(statbuf.st_size, 1716371183faSMickaël Salaün sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 1717e1199815SMickaël Salaün ASSERT_EQ(0, close(src_fd)); 1718e1199815SMickaël Salaün ASSERT_EQ(0, close(dst_fd)); 1719e1199815SMickaël Salaün } 1720e1199815SMickaël Salaün 1721371183faSMickaël Salaün static void test_execute(struct __test_metadata *const _metadata, const int err, 1722371183faSMickaël Salaün const char *const path) 1723e1199815SMickaël Salaün { 1724e1199815SMickaël Salaün int status; 1725e1199815SMickaël Salaün char *const argv[] = { (char *)path, NULL }; 1726e1199815SMickaël Salaün const pid_t child = fork(); 1727e1199815SMickaël Salaün 1728e1199815SMickaël Salaün ASSERT_LE(0, child); 1729e1199815SMickaël Salaün if (child == 0) { 1730371183faSMickaël Salaün ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 1731371183faSMickaël Salaün { 1732e1199815SMickaël Salaün TH_LOG("Failed to execute \"%s\": %s", path, 1733e1199815SMickaël Salaün strerror(errno)); 1734e1199815SMickaël Salaün }; 1735e1199815SMickaël Salaün ASSERT_EQ(err, errno); 1736e1199815SMickaël Salaün _exit(_metadata->passed ? 2 : 1); 1737e1199815SMickaël Salaün return; 1738e1199815SMickaël Salaün } 1739e1199815SMickaël Salaün ASSERT_EQ(child, waitpid(child, &status, 0)); 1740e1199815SMickaël Salaün ASSERT_EQ(1, WIFEXITED(status)); 1741371183faSMickaël Salaün ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 1742371183faSMickaël Salaün { 1743e1199815SMickaël Salaün TH_LOG("Unexpected return code for \"%s\": %s", path, 1744e1199815SMickaël Salaün strerror(errno)); 1745e1199815SMickaël Salaün }; 1746e1199815SMickaël Salaün } 1747e1199815SMickaël Salaün 1748e1199815SMickaël Salaün TEST_F_FORK(layout1, execute) 1749e1199815SMickaël Salaün { 1750e1199815SMickaël Salaün const struct rule rules[] = { 1751e1199815SMickaël Salaün { 1752e1199815SMickaël Salaün .path = dir_s1d2, 1753e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 1754e1199815SMickaël Salaün }, 1755135464f9SMickaël Salaün {}, 1756e1199815SMickaël Salaün }; 1757371183faSMickaël Salaün const int ruleset_fd = 1758371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1759e1199815SMickaël Salaün 1760e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1761e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d1); 1762e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d2); 1763e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d3); 1764e1199815SMickaël Salaün 1765e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1766e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1767e1199815SMickaël Salaün 1768e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1769e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1770e1199815SMickaël Salaün test_execute(_metadata, EACCES, file1_s1d1); 1771e1199815SMickaël Salaün 1772e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1773e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1774e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d2); 1775e1199815SMickaël Salaün 1776e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1777e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1778e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d3); 1779e1199815SMickaël Salaün } 1780e1199815SMickaël Salaün 1781e1199815SMickaël Salaün TEST_F_FORK(layout1, link) 1782e1199815SMickaël Salaün { 17836a1bdd4aSMickaël Salaün const struct rule layer1[] = { 1784e1199815SMickaël Salaün { 1785e1199815SMickaël Salaün .path = dir_s1d2, 1786e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1787e1199815SMickaël Salaün }, 1788135464f9SMickaël Salaün {}, 1789e1199815SMickaël Salaün }; 17906a1bdd4aSMickaël Salaün const struct rule layer2[] = { 17916a1bdd4aSMickaël Salaün { 17926a1bdd4aSMickaël Salaün .path = dir_s1d3, 17936a1bdd4aSMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 17946a1bdd4aSMickaël Salaün }, 17956a1bdd4aSMickaël Salaün {}, 17966a1bdd4aSMickaël Salaün }; 17976a1bdd4aSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 1798e1199815SMickaël Salaün 1799e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1800e1199815SMickaël Salaün 1801e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 1802e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1803e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1804e1199815SMickaël Salaün 1805e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1806e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1807e1199815SMickaël Salaün 1808e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1809e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 18106a1bdd4aSMickaël Salaün 1811e1199815SMickaël Salaün /* Denies linking because of reparenting. */ 1812e1199815SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1813e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1814e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1815e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 18166a1bdd4aSMickaël Salaün ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 18176a1bdd4aSMickaël Salaün ASSERT_EQ(EXDEV, errno); 1818e1199815SMickaël Salaün 1819e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1820e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 18216a1bdd4aSMickaël Salaün 18226a1bdd4aSMickaël Salaün /* Prepares for next unlinks. */ 18236a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 18246a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 18256a1bdd4aSMickaël Salaün 18266a1bdd4aSMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 18276a1bdd4aSMickaël Salaün ASSERT_LE(0, ruleset_fd); 18286a1bdd4aSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 18296a1bdd4aSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 18306a1bdd4aSMickaël Salaün 18316a1bdd4aSMickaël Salaün /* Checks that linkind doesn't require the ability to delete a file. */ 18326a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 18336a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1834e1199815SMickaël Salaün } 1835e1199815SMickaël Salaün 1836*55e55920SMickaël Salaün static int test_rename(const char *const oldpath, const char *const newpath) 1837*55e55920SMickaël Salaün { 1838*55e55920SMickaël Salaün if (rename(oldpath, newpath)) 1839*55e55920SMickaël Salaün return errno; 1840*55e55920SMickaël Salaün return 0; 1841*55e55920SMickaël Salaün } 1842*55e55920SMickaël Salaün 1843*55e55920SMickaël Salaün static int test_exchange(const char *const oldpath, const char *const newpath) 1844*55e55920SMickaël Salaün { 1845*55e55920SMickaël Salaün if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 1846*55e55920SMickaël Salaün return errno; 1847*55e55920SMickaël Salaün return 0; 1848*55e55920SMickaël Salaün } 1849*55e55920SMickaël Salaün 1850e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_file) 1851e1199815SMickaël Salaün { 1852e1199815SMickaël Salaün const struct rule rules[] = { 1853e1199815SMickaël Salaün { 1854e1199815SMickaël Salaün .path = dir_s1d3, 1855e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1856e1199815SMickaël Salaün }, 1857e1199815SMickaël Salaün { 1858e1199815SMickaël Salaün .path = dir_s2d2, 1859e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1860e1199815SMickaël Salaün }, 1861135464f9SMickaël Salaün {}, 1862e1199815SMickaël Salaün }; 1863371183faSMickaël Salaün const int ruleset_fd = 1864371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1865e1199815SMickaël Salaün 1866e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1867e1199815SMickaël Salaün 1868e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1869e1199815SMickaël Salaün 1870e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1871e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1872e1199815SMickaël Salaün 1873e1199815SMickaël Salaün /* 1874e1199815SMickaël Salaün * Tries to replace a file, from a directory that allows file removal, 1875e1199815SMickaël Salaün * but to a different directory (which also allows file removal). 1876e1199815SMickaël Salaün */ 1877e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1878e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1879e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 1880e1199815SMickaël Salaün RENAME_EXCHANGE)); 1881e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1882e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 1883e1199815SMickaël Salaün RENAME_EXCHANGE)); 1884e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1885e1199815SMickaël Salaün 1886e1199815SMickaël Salaün /* 1887e1199815SMickaël Salaün * Tries to replace a file, from a directory that denies file removal, 1888e1199815SMickaël Salaün * to a different directory (which allows file removal). 1889e1199815SMickaël Salaün */ 1890e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1891*55e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 1892e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 1893e1199815SMickaël Salaün RENAME_EXCHANGE)); 1894*55e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 1895e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 1896e1199815SMickaël Salaün RENAME_EXCHANGE)); 1897e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1898e1199815SMickaël Salaün 1899e1199815SMickaël Salaün /* Exchanges files and directories that partially allow removal. */ 1900e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 1901e1199815SMickaël Salaün RENAME_EXCHANGE)); 1902e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19036a1bdd4aSMickaël Salaün /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 19046a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 19056a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1906e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 1907e1199815SMickaël Salaün RENAME_EXCHANGE)); 1908e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19096a1bdd4aSMickaël Salaün /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 19106a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 19116a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1912e1199815SMickaël Salaün 1913e1199815SMickaël Salaün /* Renames files with different parents. */ 1914e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 1915e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1916e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1917e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 1918*55e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 1919e1199815SMickaël Salaün 1920e1199815SMickaël Salaün /* Exchanges and renames files with same parent. */ 1921e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 1922e1199815SMickaël Salaün RENAME_EXCHANGE)); 1923e1199815SMickaël Salaün ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 1924e1199815SMickaël Salaün 1925e1199815SMickaël Salaün /* Exchanges files and directories with same parent, twice. */ 1926e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1927e1199815SMickaël Salaün RENAME_EXCHANGE)); 1928e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1929e1199815SMickaël Salaün RENAME_EXCHANGE)); 1930e1199815SMickaël Salaün } 1931e1199815SMickaël Salaün 1932e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_dir) 1933e1199815SMickaël Salaün { 1934e1199815SMickaël Salaün const struct rule rules[] = { 1935e1199815SMickaël Salaün { 1936e1199815SMickaël Salaün .path = dir_s1d2, 1937e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1938e1199815SMickaël Salaün }, 1939e1199815SMickaël Salaün { 1940e1199815SMickaël Salaün .path = dir_s2d1, 1941e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1942e1199815SMickaël Salaün }, 1943135464f9SMickaël Salaün {}, 1944e1199815SMickaël Salaün }; 1945371183faSMickaël Salaün const int ruleset_fd = 1946371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1947e1199815SMickaël Salaün 1948e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1949e1199815SMickaël Salaün 1950e1199815SMickaël Salaün /* Empties dir_s1d3 to allow renaming. */ 1951e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1952e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 1953e1199815SMickaël Salaün 1954e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1955e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1956e1199815SMickaël Salaün 1957e1199815SMickaël Salaün /* Exchanges and renames directory to a different parent. */ 1958e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 1959e1199815SMickaël Salaün RENAME_EXCHANGE)); 1960e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1961e1199815SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 1962e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1963e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 1964e1199815SMickaël Salaün RENAME_EXCHANGE)); 1965e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1966e1199815SMickaël Salaün 1967e1199815SMickaël Salaün /* 1968e1199815SMickaël Salaün * Exchanges directory to the same parent, which doesn't allow 1969e1199815SMickaël Salaün * directory removal. 1970e1199815SMickaël Salaün */ 1971e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 1972e1199815SMickaël Salaün RENAME_EXCHANGE)); 1973e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19746a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 19756a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 19766a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1977e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 1978e1199815SMickaël Salaün RENAME_EXCHANGE)); 1979e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19806a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 19816a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 19826a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1983e1199815SMickaël Salaün 1984e1199815SMickaël Salaün /* 1985e1199815SMickaël Salaün * Exchanges and renames directory to the same parent, which allows 1986e1199815SMickaël Salaün * directory removal. 1987e1199815SMickaël Salaün */ 1988e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 1989e1199815SMickaël Salaün RENAME_EXCHANGE)); 1990e1199815SMickaël Salaün ASSERT_EQ(0, unlink(dir_s1d3)); 1991e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 1992e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 1993e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 1994e1199815SMickaël Salaün } 1995e1199815SMickaël Salaün 1996f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_refer) 1997f4056b92SMickaël Salaün { 1998f4056b92SMickaël Salaün const struct rule layer1[] = { 1999f4056b92SMickaël Salaün { 2000f4056b92SMickaël Salaün .path = dir_s1d2, 2001f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2002f4056b92SMickaël Salaün }, 2003f4056b92SMickaël Salaün { 2004f4056b92SMickaël Salaün .path = dir_s2d2, 2005f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2006f4056b92SMickaël Salaün }, 2007f4056b92SMickaël Salaün {}, 2008f4056b92SMickaël Salaün }; 2009f4056b92SMickaël Salaün int ruleset_fd = 2010f4056b92SMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2011f4056b92SMickaël Salaün 2012f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2013f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2014f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2015f4056b92SMickaël Salaün 2016f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2017f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2018f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2019f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2020f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2021f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2022f4056b92SMickaël Salaün 2023f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2024f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2025f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2026f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2027f4056b92SMickaël Salaün /* 2028f4056b92SMickaël Salaün * Moving should only be allowed when the source and the destination 2029f4056b92SMickaël Salaün * parent directory have REFER. 2030f4056b92SMickaël Salaün */ 2031f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2032f4056b92SMickaël Salaün ASSERT_EQ(ENOTEMPTY, errno); 2033f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2034f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2035f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2036f4056b92SMickaël Salaün } 2037f4056b92SMickaël Salaün 2038*55e55920SMickaël Salaün /* Checks renames beneath dir_s1d1. */ 2039*55e55920SMickaël Salaün static void refer_denied_by_default(struct __test_metadata *const _metadata, 2040*55e55920SMickaël Salaün const struct rule layer1[], 2041*55e55920SMickaël Salaün const int layer1_err, 2042*55e55920SMickaël Salaün const struct rule layer2[]) 2043*55e55920SMickaël Salaün { 2044*55e55920SMickaël Salaün int ruleset_fd; 2045*55e55920SMickaël Salaün 2046*55e55920SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2047*55e55920SMickaël Salaün 2048*55e55920SMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 2049*55e55920SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2050*55e55920SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2051*55e55920SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2052*55e55920SMickaël Salaün 2053*55e55920SMickaël Salaün /* 2054*55e55920SMickaël Salaün * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 2055*55e55920SMickaël Salaün * layer1_err), then it allows some different-parent renames and links. 2056*55e55920SMickaël Salaün */ 2057*55e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 2058*55e55920SMickaël Salaün if (layer1_err == 0) 2059*55e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 2060*55e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 2061*55e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 2062*55e55920SMickaël Salaün 2063*55e55920SMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 2064*55e55920SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2065*55e55920SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2066*55e55920SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2067*55e55920SMickaël Salaün 2068*55e55920SMickaël Salaün /* 2069*55e55920SMickaël Salaün * Now, either the first or the second layer does not handle 2070*55e55920SMickaël Salaün * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 2071*55e55920SMickaël Salaün * renames and links are denied, thus making the layer handling 2072*55e55920SMickaël Salaün * LANDLOCK_ACCESS_FS_REFER null and void. 2073*55e55920SMickaël Salaün */ 2074*55e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 2075*55e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 2076*55e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 2077*55e55920SMickaël Salaün } 2078*55e55920SMickaël Salaün 2079*55e55920SMickaël Salaün const struct rule layer_dir_s1d1_refer[] = { 2080*55e55920SMickaël Salaün { 2081*55e55920SMickaël Salaün .path = dir_s1d1, 2082*55e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2083*55e55920SMickaël Salaün }, 2084*55e55920SMickaël Salaün {}, 2085*55e55920SMickaël Salaün }; 2086*55e55920SMickaël Salaün 2087*55e55920SMickaël Salaün const struct rule layer_dir_s1d1_execute[] = { 2088*55e55920SMickaël Salaün { 2089*55e55920SMickaël Salaün /* Matches a parent directory. */ 2090*55e55920SMickaël Salaün .path = dir_s1d1, 2091*55e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 2092*55e55920SMickaël Salaün }, 2093*55e55920SMickaël Salaün {}, 2094*55e55920SMickaël Salaün }; 2095*55e55920SMickaël Salaün 2096*55e55920SMickaël Salaün const struct rule layer_dir_s2d1_execute[] = { 2097*55e55920SMickaël Salaün { 2098*55e55920SMickaël Salaün /* Does not match a parent directory. */ 2099*55e55920SMickaël Salaün .path = dir_s2d1, 2100*55e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 2101*55e55920SMickaël Salaün }, 2102*55e55920SMickaël Salaün {}, 2103*55e55920SMickaël Salaün }; 2104*55e55920SMickaël Salaün 2105*55e55920SMickaël Salaün /* 2106*55e55920SMickaël Salaün * Tests precedence over renames: denied by default for different parent 2107*55e55920SMickaël Salaün * directories, *with* a rule matching a parent directory, but not directly 2108*55e55920SMickaël Salaün * denying access (with MAKE_REG nor REMOVE). 2109*55e55920SMickaël Salaün */ 2110*55e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default1) 2111*55e55920SMickaël Salaün { 2112*55e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2113*55e55920SMickaël Salaün layer_dir_s1d1_execute); 2114*55e55920SMickaël Salaün } 2115*55e55920SMickaël Salaün 2116*55e55920SMickaël Salaün /* 2117*55e55920SMickaël Salaün * Same test but this time turning around the ABI version order: the first 2118*55e55920SMickaël Salaün * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2119*55e55920SMickaël Salaün */ 2120*55e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default2) 2121*55e55920SMickaël Salaün { 2122*55e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 2123*55e55920SMickaël Salaün layer_dir_s1d1_refer); 2124*55e55920SMickaël Salaün } 2125*55e55920SMickaël Salaün 2126*55e55920SMickaël Salaün /* 2127*55e55920SMickaël Salaün * Tests precedence over renames: denied by default for different parent 2128*55e55920SMickaël Salaün * directories, *without* a rule matching a parent directory, but not directly 2129*55e55920SMickaël Salaün * denying access (with MAKE_REG nor REMOVE). 2130*55e55920SMickaël Salaün */ 2131*55e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default3) 2132*55e55920SMickaël Salaün { 2133*55e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 2134*55e55920SMickaël Salaün layer_dir_s2d1_execute); 2135*55e55920SMickaël Salaün } 2136*55e55920SMickaël Salaün 2137*55e55920SMickaël Salaün /* 2138*55e55920SMickaël Salaün * Same test but this time turning around the ABI version order: the first 2139*55e55920SMickaël Salaün * layer does not handle LANDLOCK_ACCESS_FS_REFER. 2140*55e55920SMickaël Salaün */ 2141*55e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default4) 2142*55e55920SMickaël Salaün { 2143*55e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 2144*55e55920SMickaël Salaün layer_dir_s1d1_refer); 2145*55e55920SMickaël Salaün } 2146*55e55920SMickaël Salaün 2147f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_link) 2148f4056b92SMickaël Salaün { 2149f4056b92SMickaël Salaün const struct rule layer1[] = { 2150f4056b92SMickaël Salaün { 2151f4056b92SMickaël Salaün .path = dir_s1d2, 2152f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2153f4056b92SMickaël Salaün }, 2154f4056b92SMickaël Salaün { 2155f4056b92SMickaël Salaün .path = dir_s1d3, 2156f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2157f4056b92SMickaël Salaün }, 2158f4056b92SMickaël Salaün { 2159f4056b92SMickaël Salaün .path = dir_s2d2, 2160f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2161f4056b92SMickaël Salaün }, 2162f4056b92SMickaël Salaün { 2163f4056b92SMickaël Salaün .path = dir_s2d3, 2164f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2165f4056b92SMickaël Salaün }, 2166f4056b92SMickaël Salaün {}, 2167f4056b92SMickaël Salaün }; 2168f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2169f4056b92SMickaël Salaün _metadata, 2170f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2171f4056b92SMickaël Salaün 2172f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2173f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2174f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2175f4056b92SMickaël Salaün 2176f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2177f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2178f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2179f4056b92SMickaël Salaün 2180f4056b92SMickaël Salaün /* Denies linking because of missing MAKE_REG. */ 2181f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2182f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2183f4056b92SMickaël Salaün /* Denies linking because of missing source and destination REFER. */ 2184f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2185f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2186f4056b92SMickaël Salaün /* Denies linking because of missing source REFER. */ 2187f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2188f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2189f4056b92SMickaël Salaün 2190f4056b92SMickaël Salaün /* Denies linking because of missing MAKE_REG. */ 2191f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2192f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2193f4056b92SMickaël Salaün /* Denies linking because of missing destination REFER. */ 2194f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2195f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2196f4056b92SMickaël Salaün 2197f4056b92SMickaël Salaün /* Allows linking because of REFER and MAKE_REG. */ 2198f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2199f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d2)); 2200f4056b92SMickaël Salaün /* Reverse linking denied because of missing MAKE_REG. */ 2201f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2202f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2203f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2204f4056b92SMickaël Salaün /* Checks reverse linking. */ 2205f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2206f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2207f4056b92SMickaël Salaün 2208f4056b92SMickaël Salaün /* 2209f4056b92SMickaël Salaün * This is OK for a file link, but it should not be allowed for a 2210f4056b92SMickaël Salaün * directory rename (because of the superset of access rights. 2211f4056b92SMickaël Salaün */ 2212f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2213f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2214f4056b92SMickaël Salaün 2215f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2216f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2217f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2218f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2219f4056b92SMickaël Salaün 2220f4056b92SMickaël Salaün ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2221f4056b92SMickaël Salaün ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2222f4056b92SMickaël Salaün } 2223f4056b92SMickaël Salaün 2224f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_rename) 2225f4056b92SMickaël Salaün { 2226f4056b92SMickaël Salaün /* Same rules as for reparent_link. */ 2227f4056b92SMickaël Salaün const struct rule layer1[] = { 2228f4056b92SMickaël Salaün { 2229f4056b92SMickaël Salaün .path = dir_s1d2, 2230f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2231f4056b92SMickaël Salaün }, 2232f4056b92SMickaël Salaün { 2233f4056b92SMickaël Salaün .path = dir_s1d3, 2234f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2235f4056b92SMickaël Salaün }, 2236f4056b92SMickaël Salaün { 2237f4056b92SMickaël Salaün .path = dir_s2d2, 2238f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2239f4056b92SMickaël Salaün }, 2240f4056b92SMickaël Salaün { 2241f4056b92SMickaël Salaün .path = dir_s2d3, 2242f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2243f4056b92SMickaël Salaün }, 2244f4056b92SMickaël Salaün {}, 2245f4056b92SMickaël Salaün }; 2246f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2247f4056b92SMickaël Salaün _metadata, 2248f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2249f4056b92SMickaël Salaün 2250f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2251f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2252f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2253f4056b92SMickaël Salaün 2254f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2255f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2256f4056b92SMickaël Salaün 2257f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2258f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2259f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2260f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2261f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2262f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2263f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2264f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2265f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2266f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2267f4056b92SMickaël Salaün /* Even denies same file exchange. */ 2268f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2269f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2270f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2271f4056b92SMickaël Salaün 2272f4056b92SMickaël Salaün /* Denies renaming because of missing source and destination REFER. */ 2273f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2274f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2275f4056b92SMickaël Salaün /* 2276f4056b92SMickaël Salaün * Denies renaming because of missing MAKE_REG, source and destination 2277f4056b92SMickaël Salaün * REFER. 2278f4056b92SMickaël Salaün */ 2279f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2280f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2281f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2282f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2283f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2284f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2285f4056b92SMickaël Salaün 2286f4056b92SMickaël Salaün /* Denies renaming because of missing source REFER. */ 2287f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2288f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2289f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2290f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2291f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2292f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2293f4056b92SMickaël Salaün 2294f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2295f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2296f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2297f4056b92SMickaël Salaün /* Denies renaming because of missing destination REFER*/ 2298f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2299f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2300f4056b92SMickaël Salaün 2301f4056b92SMickaël Salaün /* Denies exchange because of one missing MAKE_REG. */ 2302f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2303f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2304f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2305f4056b92SMickaël Salaün /* Allows renaming because of REFER and MAKE_REG. */ 2306f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2307f4056b92SMickaël Salaün 2308f4056b92SMickaël Salaün /* Reverse renaming denied because of missing MAKE_REG. */ 2309f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2310f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2311f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2312f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2313f4056b92SMickaël Salaün 2314f4056b92SMickaël Salaün /* Tests reverse renaming. */ 2315f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2316f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2317f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2318f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2319f4056b92SMickaël Salaün 2320f4056b92SMickaël Salaün /* 2321f4056b92SMickaël Salaün * This is OK for a file rename, but it should not be allowed for a 2322f4056b92SMickaël Salaün * directory rename (because of the superset of access rights). 2323f4056b92SMickaël Salaün */ 2324f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2325f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2326f4056b92SMickaël Salaün 2327f4056b92SMickaël Salaün /* 2328f4056b92SMickaël Salaün * Tests superset restrictions applied to directories. Not only the 2329f4056b92SMickaël Salaün * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2330f4056b92SMickaël Salaün * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2331f4056b92SMickaël Salaün * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2332f4056b92SMickaël Salaün * directly by the moved dir_s2d3. 2333f4056b92SMickaël Salaün */ 2334f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2335f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2336f4056b92SMickaël Salaün /* 2337f4056b92SMickaël Salaün * The first rename is allowed but not the exchange because dir_s1d3's 2338f4056b92SMickaël Salaün * parent (dir_s1d2) doesn't have REFER. 2339f4056b92SMickaël Salaün */ 2340f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2341f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2342f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2343f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2344f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2345f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2346f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2347f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2348f4056b92SMickaël Salaün 2349f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2350f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2351f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2352f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2353f4056b92SMickaël Salaün 2354f4056b92SMickaël Salaün /* Renaming in the same directory is always allowed. */ 2355f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2356f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2357f4056b92SMickaël Salaün 2358f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2359f4056b92SMickaël Salaün /* Denies because of missing source MAKE_REG and destination REFER. */ 2360f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2361f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2362f4056b92SMickaël Salaün 2363f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2364f4056b92SMickaël Salaün /* Denies because of missing source MAKE_REG and REFER. */ 2365f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2366f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2367f4056b92SMickaël Salaün } 2368f4056b92SMickaël Salaün 2369f4056b92SMickaël Salaün static void 2370f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2371f4056b92SMickaël Salaün { 2372f4056b92SMickaël Salaün const struct rule layer1[] = { 2373f4056b92SMickaël Salaün { 2374f4056b92SMickaël Salaün .path = dir_s1d2, 2375f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2376f4056b92SMickaël Salaün }, 2377f4056b92SMickaël Salaün { 2378f4056b92SMickaël Salaün /* Interesting for the layer2 tests. */ 2379f4056b92SMickaël Salaün .path = dir_s1d3, 2380f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2381f4056b92SMickaël Salaün }, 2382f4056b92SMickaël Salaün { 2383f4056b92SMickaël Salaün .path = dir_s2d2, 2384f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2385f4056b92SMickaël Salaün }, 2386f4056b92SMickaël Salaün { 2387f4056b92SMickaël Salaün .path = dir_s2d3, 2388f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2389f4056b92SMickaël Salaün }, 2390f4056b92SMickaël Salaün {}, 2391f4056b92SMickaël Salaün }; 2392f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2393f4056b92SMickaël Salaün _metadata, 2394f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2395f4056b92SMickaël Salaün 2396f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2397f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2398f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2399f4056b92SMickaël Salaün } 2400f4056b92SMickaël Salaün 2401f4056b92SMickaël Salaün static void 2402f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2403f4056b92SMickaël Salaün { 2404f4056b92SMickaël Salaün const struct rule layer2[] = { 2405f4056b92SMickaël Salaün { 2406f4056b92SMickaël Salaün .path = dir_s2d3, 2407f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2408f4056b92SMickaël Salaün }, 2409f4056b92SMickaël Salaün {}, 2410f4056b92SMickaël Salaün }; 2411f4056b92SMickaël Salaün /* 2412f4056b92SMickaël Salaün * Same checks as before but with a second layer and a new MAKE_DIR 2413f4056b92SMickaël Salaün * rule (and no explicit handling of REFER). 2414f4056b92SMickaël Salaün */ 2415f4056b92SMickaël Salaün const int ruleset_fd = 2416f4056b92SMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2417f4056b92SMickaël Salaün 2418f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2419f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2420f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2421f4056b92SMickaël Salaün } 2422f4056b92SMickaël Salaün 2423f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2424f4056b92SMickaël Salaün { 2425f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d2)); 2426f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2427f4056b92SMickaël Salaün 2428f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2429f4056b92SMickaël Salaün 2430f4056b92SMickaël Salaün /* 2431f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2432f4056b92SMickaël Salaün * because it doesn't inherit new access rights. 2433f4056b92SMickaël Salaün */ 2434f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2435f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2436f4056b92SMickaël Salaün 2437f4056b92SMickaël Salaün /* 2438f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2439f4056b92SMickaël Salaün * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2440f4056b92SMickaël Salaün * already allowed for dir_s1d3. 2441f4056b92SMickaël Salaün */ 2442f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2443f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2444f4056b92SMickaël Salaün 2445f4056b92SMickaël Salaün /* 2446f4056b92SMickaël Salaün * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2447f4056b92SMickaël Salaün * because it cannot inherit MAKE_REG right (which is dedicated to 2448f4056b92SMickaël Salaün * directories). 2449f4056b92SMickaël Salaün */ 2450f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2451f4056b92SMickaël Salaün 2452f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2453f4056b92SMickaël Salaün 2454f4056b92SMickaël Salaün /* 2455f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2456f4056b92SMickaël Salaün * MAKE_DIR is not tied to dir_s2d2. 2457f4056b92SMickaël Salaün */ 2458f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2459f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2460f4056b92SMickaël Salaün 2461f4056b92SMickaël Salaün /* 2462f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2463f4056b92SMickaël Salaün * would grants MAKE_REG and MAKE_DIR rights to it. 2464f4056b92SMickaël Salaün */ 2465f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2466f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2467f4056b92SMickaël Salaün 2468f4056b92SMickaël Salaün /* 2469*55e55920SMickaël Salaün * Moving the file2_s1d3 file below dir_s2d3 is denied because the 2470*55e55920SMickaël Salaün * second layer does not handle REFER, which is always denied by 2471*55e55920SMickaël Salaün * default. 2472f4056b92SMickaël Salaün */ 2473*55e55920SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 2474*55e55920SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2475f4056b92SMickaël Salaün } 2476f4056b92SMickaël Salaün 2477f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2478f4056b92SMickaël Salaün { 2479f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2480f4056b92SMickaël Salaün 2481f4056b92SMickaël Salaün /* Checks EACCES predominance over EXDEV. */ 2482f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2483f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2484f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2485f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2486f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2487f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2488f4056b92SMickaël Salaün /* Modify layout! */ 2489f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 2490f4056b92SMickaël Salaün 2491f4056b92SMickaël Salaün /* Without REFER source. */ 2492f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2493f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2494f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2495f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2496f4056b92SMickaël Salaün 2497f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2498f4056b92SMickaël Salaün 2499f4056b92SMickaël Salaün /* Checks EACCES predominance over EXDEV. */ 2500f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2501f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2502f4056b92SMickaël Salaün /* Checks with actual file2_s1d2. */ 2503f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 2504f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2505f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2506f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2507*55e55920SMickaël Salaün /* 2508*55e55920SMickaël Salaün * Modifying the layout is now denied because the second layer does not 2509*55e55920SMickaël Salaün * handle REFER, which is always denied by default. 2510*55e55920SMickaël Salaün */ 2511*55e55920SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2512*55e55920SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2513f4056b92SMickaël Salaün 2514f4056b92SMickaël Salaün /* Without REFER source, EACCES wins over EXDEV. */ 2515f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2516f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2517f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2518f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2519f4056b92SMickaël Salaün } 2520f4056b92SMickaël Salaün 2521f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 2522f4056b92SMickaël Salaün { 2523f4056b92SMickaël Salaün const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 2524f4056b92SMickaël Salaün file2_s2d3; 2525f4056b92SMickaël Salaün 2526f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2527f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2528f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2529f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2530f4056b92SMickaël Salaün 2531f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2532f4056b92SMickaël Salaün 2533f4056b92SMickaël Salaün /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2534f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2535f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2536f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2537f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2538f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2539f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2540f4056b92SMickaël Salaün 2541f4056b92SMickaël Salaün /* 2542f4056b92SMickaël Salaün * Checks with directories which creation could be allowed, but denied 2543f4056b92SMickaël Salaün * because of access rights that would be inherited. 2544f4056b92SMickaël Salaün */ 2545f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2546f4056b92SMickaël Salaün dir_file2_s2d3, RENAME_EXCHANGE)); 2547f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2548f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2549f4056b92SMickaël Salaün dir_file1_s1d2, RENAME_EXCHANGE)); 2550f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2551f4056b92SMickaël Salaün 2552f4056b92SMickaël Salaün /* Checks with same access rights. */ 2553f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2554f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2555f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2556f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2557f4056b92SMickaël Salaün 2558f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2559f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2560f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2561f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2562f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2563f4056b92SMickaël Salaün 2564f4056b92SMickaël Salaün /* 2565f4056b92SMickaël Salaün * Checks that exchange between file and directory are consistent. 2566f4056b92SMickaël Salaün * 2567f4056b92SMickaël Salaün * Moving a file (file1_s2d2) to a directory which only grants more 2568f4056b92SMickaël Salaün * directory-related access rights is allowed, and at the same time 2569f4056b92SMickaël Salaün * moving a directory (dir_file2_s2d3) to another directory which 2570f4056b92SMickaël Salaün * grants less access rights is allowed too. 2571f4056b92SMickaël Salaün * 2572f4056b92SMickaël Salaün * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 2573f4056b92SMickaël Salaün */ 2574f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2575f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2576f4056b92SMickaël Salaün /* 2577f4056b92SMickaël Salaün * However, moving back the directory is denied because it would get 2578f4056b92SMickaël Salaün * more access rights than the current state and because file creation 2579f4056b92SMickaël Salaün * is forbidden (in dir_s2d2). 2580f4056b92SMickaël Salaün */ 2581f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2582f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2583f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2584f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2585f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2586f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2587f4056b92SMickaël Salaün 2588f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2589f4056b92SMickaël Salaün 2590f4056b92SMickaël Salaün /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2591f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2592f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2593f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2594f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2595f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2596f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2597f4056b92SMickaël Salaün 2598f4056b92SMickaël Salaün /* Checks with directories which creation is now denied. */ 2599f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2600f4056b92SMickaël Salaün dir_file2_s2d3, RENAME_EXCHANGE)); 2601f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2602f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2603f4056b92SMickaël Salaün dir_file1_s1d2, RENAME_EXCHANGE)); 2604f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2605f4056b92SMickaël Salaün 2606f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2607f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2608f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2609f4056b92SMickaël Salaün /* Denied because of MAKE_DIR. */ 2610f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2611f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2612f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2613f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2614f4056b92SMickaël Salaün 2615f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2616f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2617f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2618f4056b92SMickaël Salaün /* Denied because of MAKE_DIR. */ 2619f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2620f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2621f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2622f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2623f4056b92SMickaël Salaün 2624f4056b92SMickaël Salaün /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 2625f4056b92SMickaël Salaün } 2626f4056b92SMickaël Salaün 2627f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 2628f4056b92SMickaël Salaün { 2629f4056b92SMickaël Salaün const char *const dir_file2_s2d3 = file2_s2d3; 2630f4056b92SMickaël Salaün 2631f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2632f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2633f4056b92SMickaël Salaün 2634f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2635f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2636f4056b92SMickaël Salaün 2637f4056b92SMickaël Salaün /* Checks that exchange between file and directory are consistent. */ 2638f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2639f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2640f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2641f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2642f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2643f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2644f4056b92SMickaël Salaün } 2645f4056b92SMickaël Salaün 2646f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 2647f4056b92SMickaël Salaün { 2648f4056b92SMickaël Salaün const char *const dir_file2_s2d3 = file2_s2d3; 2649f4056b92SMickaël Salaün 2650f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2651f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2652f4056b92SMickaël Salaün 2653f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2654f4056b92SMickaël Salaün 2655f4056b92SMickaël Salaün /* 2656f4056b92SMickaël Salaün * Checks that exchange between file and directory are consistent, 2657f4056b92SMickaël Salaün * including with inverted arguments (see 2658f4056b92SMickaël Salaün * layout1.reparent_exdev_layers_exchange1). 2659f4056b92SMickaël Salaün */ 2660f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2661f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2662f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2663f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2664f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2665f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2666f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2667f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2668f4056b92SMickaël Salaün } 2669f4056b92SMickaël Salaün 2670f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_remove) 2671f4056b92SMickaël Salaün { 2672f4056b92SMickaël Salaün const struct rule layer1[] = { 2673f4056b92SMickaël Salaün { 2674f4056b92SMickaël Salaün .path = dir_s1d1, 2675f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2676f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_DIR, 2677f4056b92SMickaël Salaün }, 2678f4056b92SMickaël Salaün { 2679f4056b92SMickaël Salaün .path = dir_s1d2, 2680f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2681f4056b92SMickaël Salaün }, 2682f4056b92SMickaël Salaün { 2683f4056b92SMickaël Salaün .path = dir_s2d1, 2684f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2685f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE, 2686f4056b92SMickaël Salaün }, 2687f4056b92SMickaël Salaün {}, 2688f4056b92SMickaël Salaün }; 2689f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2690f4056b92SMickaël Salaün _metadata, 2691f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 2692f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE, 2693f4056b92SMickaël Salaün layer1); 2694f4056b92SMickaël Salaün 2695f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2696f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2697f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2698f4056b92SMickaël Salaün 2699f4056b92SMickaël Salaün /* Access denied because of wrong/swapped remove file/dir. */ 2700f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 2701f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2702f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 2703f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2704f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 2705f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2706f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2707f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 2708f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2709f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2710f4056b92SMickaël Salaün 2711f4056b92SMickaël Salaün /* Access allowed thanks to the matching rights. */ 2712f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 2713f4056b92SMickaël Salaün ASSERT_EQ(EISDIR, errno); 2714f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 2715f4056b92SMickaël Salaün ASSERT_EQ(ENOTDIR, errno); 2716f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2717f4056b92SMickaël Salaün ASSERT_EQ(ENOTDIR, errno); 2718f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d1)); 2719f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2720f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2721f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 2722f4056b92SMickaël Salaün 2723f4056b92SMickaël Salaün /* Effectively removes a file and a directory by exchanging them. */ 2724f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2725f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2726f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2727f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2728f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2729f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2730f4056b92SMickaël Salaün } 2731f4056b92SMickaël Salaün 2732f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_dom_superset) 2733f4056b92SMickaël Salaün { 2734f4056b92SMickaël Salaün const struct rule layer1[] = { 2735f4056b92SMickaël Salaün { 2736f4056b92SMickaël Salaün .path = dir_s1d2, 2737f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2738f4056b92SMickaël Salaün }, 2739f4056b92SMickaël Salaün { 2740f4056b92SMickaël Salaün .path = file1_s1d2, 2741f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 2742f4056b92SMickaël Salaün }, 2743f4056b92SMickaël Salaün { 2744f4056b92SMickaël Salaün .path = dir_s1d3, 2745f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 2746f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE, 2747f4056b92SMickaël Salaün }, 2748f4056b92SMickaël Salaün { 2749f4056b92SMickaël Salaün .path = dir_s2d2, 2750f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2751f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | 2752f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK, 2753f4056b92SMickaël Salaün }, 2754f4056b92SMickaël Salaün { 2755f4056b92SMickaël Salaün .path = dir_s2d3, 2756f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2757f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO, 2758f4056b92SMickaël Salaün }, 2759f4056b92SMickaël Salaün {}, 2760f4056b92SMickaël Salaün }; 2761f4056b92SMickaël Salaün int ruleset_fd = create_ruleset(_metadata, 2762f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | 2763f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | 2764f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK | 2765f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 2766f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO, 2767f4056b92SMickaël Salaün layer1); 2768f4056b92SMickaël Salaün 2769f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2770f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2771f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2772f4056b92SMickaël Salaün 2773f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 2774f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2775f4056b92SMickaël Salaün /* 2776f4056b92SMickaël Salaün * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 2777f4056b92SMickaël Salaün * access right. 2778f4056b92SMickaël Salaün */ 2779f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 2780f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2781f4056b92SMickaël Salaün /* 2782f4056b92SMickaël Salaün * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 2783f4056b92SMickaël Salaün * superset of access rights compared to dir_s1d2, because file1_s1d2 2784f4056b92SMickaël Salaün * already has these access rights anyway. 2785f4056b92SMickaël Salaün */ 2786f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 2787f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 2788f4056b92SMickaël Salaün 2789f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2790f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2791f4056b92SMickaël Salaün /* 2792f4056b92SMickaël Salaün * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 2793f4056b92SMickaël Salaün * right. 2794f4056b92SMickaël Salaün */ 2795f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2796f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2797f4056b92SMickaël Salaün /* 2798f4056b92SMickaël Salaün * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 2799f4056b92SMickaël Salaün * of access rights compared to dir_s1d2, because dir_s1d3 already has 2800f4056b92SMickaël Salaün * these access rights anyway. 2801f4056b92SMickaël Salaün */ 2802f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2803f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2804f4056b92SMickaël Salaün 2805f4056b92SMickaël Salaün /* 2806f4056b92SMickaël Salaün * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 2807f4056b92SMickaël Salaün * will be denied because the new inherited access rights from dir_s1d2 2808f4056b92SMickaël Salaün * will be less than the destination (original) dir_s2d3. This is a 2809f4056b92SMickaël Salaün * sinkhole scenario where we cannot move back files or directories. 2810f4056b92SMickaël Salaün */ 2811f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 2812f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2813f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2814f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2815f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2816f4056b92SMickaël Salaün /* 2817f4056b92SMickaël Salaün * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 2818f4056b92SMickaël Salaün * MAKE_SOCK which were inherited from dir_s1d3. 2819f4056b92SMickaël Salaün */ 2820f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 2821f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 2822f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2823f4056b92SMickaël Salaün } 2824f4056b92SMickaël Salaün 2825e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_dir) 2826e1199815SMickaël Salaün { 2827e1199815SMickaël Salaün const struct rule rules[] = { 2828e1199815SMickaël Salaün { 2829e1199815SMickaël Salaün .path = dir_s1d2, 2830e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2831e1199815SMickaël Salaün }, 2832135464f9SMickaël Salaün {}, 2833e1199815SMickaël Salaün }; 2834371183faSMickaël Salaün const int ruleset_fd = 2835371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2836e1199815SMickaël Salaün 2837e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2838e1199815SMickaël Salaün 2839e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2840e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2841e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2842e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2843e1199815SMickaël Salaün 2844e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2845e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2846e1199815SMickaël Salaün 2847e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 2848e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2849e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 2850e1199815SMickaël Salaün 2851e1199815SMickaël Salaün /* dir_s1d2 itself cannot be removed. */ 2852e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d2)); 2853e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2854e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 2855e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2856e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d1)); 2857e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2858e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 2859e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2860e1199815SMickaël Salaün } 2861e1199815SMickaël Salaün 2862e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_file) 2863e1199815SMickaël Salaün { 2864e1199815SMickaël Salaün const struct rule rules[] = { 2865e1199815SMickaël Salaün { 2866e1199815SMickaël Salaün .path = dir_s1d2, 2867e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2868e1199815SMickaël Salaün }, 2869135464f9SMickaël Salaün {}, 2870e1199815SMickaël Salaün }; 2871371183faSMickaël Salaün const int ruleset_fd = 2872371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2873e1199815SMickaël Salaün 2874e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2875e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2876e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2877e1199815SMickaël Salaün 2878e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d1)); 2879e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2880e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 2881e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2882e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2883e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 2884e1199815SMickaël Salaün } 2885e1199815SMickaël Salaün 2886e1199815SMickaël Salaün static void test_make_file(struct __test_metadata *const _metadata, 2887371183faSMickaël Salaün const __u64 access, const mode_t mode, 2888371183faSMickaël Salaün const dev_t dev) 2889e1199815SMickaël Salaün { 2890e1199815SMickaël Salaün const struct rule rules[] = { 2891e1199815SMickaël Salaün { 2892e1199815SMickaël Salaün .path = dir_s1d2, 2893e1199815SMickaël Salaün .access = access, 2894e1199815SMickaël Salaün }, 2895135464f9SMickaël Salaün {}, 2896e1199815SMickaël Salaün }; 2897e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, access, rules); 2898e1199815SMickaël Salaün 2899e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2900e1199815SMickaël Salaün 2901e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2902e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 2903371183faSMickaël Salaün ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 2904371183faSMickaël Salaün { 2905371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 2906371183faSMickaël Salaün strerror(errno)); 2907e1199815SMickaël Salaün }; 2908e1199815SMickaël Salaün 2909e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2910e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2911e1199815SMickaël Salaün 2912e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2913e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2914e1199815SMickaël Salaün 2915e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2916e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2917e1199815SMickaël Salaün 2918e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 2919e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2920e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2921e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2922e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2923e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2924e1199815SMickaël Salaün 2925371183faSMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 2926371183faSMickaël Salaün { 2927371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 2928371183faSMickaël Salaün strerror(errno)); 2929e1199815SMickaël Salaün }; 2930e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 2931e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2932e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 2933e1199815SMickaël Salaün 2934e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 2935e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 2936e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2937e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 2938e1199815SMickaël Salaün } 2939e1199815SMickaël Salaün 2940e1199815SMickaël Salaün TEST_F_FORK(layout1, make_char) 2941e1199815SMickaël Salaün { 2942e1199815SMickaël Salaün /* Creates a /dev/null device. */ 2943e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 2944e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 2945e1199815SMickaël Salaün makedev(1, 3)); 2946e1199815SMickaël Salaün } 2947e1199815SMickaël Salaün 2948e1199815SMickaël Salaün TEST_F_FORK(layout1, make_block) 2949e1199815SMickaël Salaün { 2950e1199815SMickaël Salaün /* Creates a /dev/loop0 device. */ 2951e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 2952e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 2953e1199815SMickaël Salaün makedev(7, 0)); 2954e1199815SMickaël Salaün } 2955e1199815SMickaël Salaün 2956e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_1) 2957e1199815SMickaël Salaün { 2958e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 2959e1199815SMickaël Salaün } 2960e1199815SMickaël Salaün 2961e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_2) 2962e1199815SMickaël Salaün { 2963e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 2964e1199815SMickaël Salaün } 2965e1199815SMickaël Salaün 2966e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sock) 2967e1199815SMickaël Salaün { 2968e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 2969e1199815SMickaël Salaün } 2970e1199815SMickaël Salaün 2971e1199815SMickaël Salaün TEST_F_FORK(layout1, make_fifo) 2972e1199815SMickaël Salaün { 2973e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 2974e1199815SMickaël Salaün } 2975e1199815SMickaël Salaün 2976e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sym) 2977e1199815SMickaël Salaün { 2978e1199815SMickaël Salaün const struct rule rules[] = { 2979e1199815SMickaël Salaün { 2980e1199815SMickaël Salaün .path = dir_s1d2, 2981e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 2982e1199815SMickaël Salaün }, 2983135464f9SMickaël Salaün {}, 2984e1199815SMickaël Salaün }; 2985371183faSMickaël Salaün const int ruleset_fd = 2986371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2987e1199815SMickaël Salaün 2988e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2989e1199815SMickaël Salaün 2990e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2991e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 2992e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file2_s1d1)); 2993e1199815SMickaël Salaün 2994e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2995e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2996e1199815SMickaël Salaün 2997e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2998e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2999e1199815SMickaël Salaün 3000e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3001e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3002e1199815SMickaël Salaün 3003e1199815SMickaël Salaün ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3004e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3005e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3006e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3007e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3008e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3009e1199815SMickaël Salaün 3010e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d2)); 3011e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3012e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 3013e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3014e1199815SMickaël Salaün 3015e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d3)); 3016e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3017e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 3018e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3019e1199815SMickaël Salaün } 3020e1199815SMickaël Salaün 3021e1199815SMickaël Salaün TEST_F_FORK(layout1, make_dir) 3022e1199815SMickaël Salaün { 3023e1199815SMickaël Salaün const struct rule rules[] = { 3024e1199815SMickaël Salaün { 3025e1199815SMickaël Salaün .path = dir_s1d2, 3026e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3027e1199815SMickaël Salaün }, 3028135464f9SMickaël Salaün {}, 3029e1199815SMickaël Salaün }; 3030371183faSMickaël Salaün const int ruleset_fd = 3031371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3032e1199815SMickaël Salaün 3033e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3034e1199815SMickaël Salaün 3035e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 3036e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3037e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 3038e1199815SMickaël Salaün 3039e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3040e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3041e1199815SMickaël Salaün 3042e1199815SMickaël Salaün /* Uses file_* as directory names. */ 3043e1199815SMickaël Salaün ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3044e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3045e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3046e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3047e1199815SMickaël Salaün } 3048e1199815SMickaël Salaün 3049e1199815SMickaël Salaün static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3050e1199815SMickaël Salaün const int open_flags) 3051e1199815SMickaël Salaün { 3052e1199815SMickaël Salaün static const char path_template[] = "/proc/self/fd/%d"; 3053e1199815SMickaël Salaün char procfd_path[sizeof(path_template) + 10]; 3054371183faSMickaël Salaün const int procfd_path_size = 3055371183faSMickaël Salaün snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3056e1199815SMickaël Salaün 3057e1199815SMickaël Salaün ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3058e1199815SMickaël Salaün return open(procfd_path, open_flags); 3059e1199815SMickaël Salaün } 3060e1199815SMickaël Salaün 3061e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_unlinked_file) 3062e1199815SMickaël Salaün { 3063e1199815SMickaël Salaün const struct rule rules[] = { 3064e1199815SMickaël Salaün { 3065e1199815SMickaël Salaün .path = file1_s1d2, 3066e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3067e1199815SMickaël Salaün }, 3068135464f9SMickaël Salaün {}, 3069e1199815SMickaël Salaün }; 3070e1199815SMickaël Salaün int reg_fd, proc_fd; 3071371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 3072371183faSMickaël Salaün _metadata, 3073371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3074371183faSMickaël Salaün rules); 3075e1199815SMickaël Salaün 3076e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3077e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3078e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3079e1199815SMickaël Salaün 3080e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3081e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3082e1199815SMickaël Salaün reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3083e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 3084e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3085e1199815SMickaël Salaün 3086e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3087e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3088e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3089e1199815SMickaël Salaün 3090e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3091371183faSMickaël Salaün ASSERT_EQ(-1, proc_fd) 3092371183faSMickaël Salaün { 3093371183faSMickaël Salaün TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3094371183faSMickaël Salaün strerror(errno)); 3095e1199815SMickaël Salaün } 3096e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3097e1199815SMickaël Salaün 3098e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 3099e1199815SMickaël Salaün } 3100e1199815SMickaël Salaün 3101e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_pipe) 3102e1199815SMickaël Salaün { 3103e1199815SMickaël Salaün int proc_fd; 3104e1199815SMickaël Salaün int pipe_fds[2]; 3105e1199815SMickaël Salaün char buf = '\0'; 3106e1199815SMickaël Salaün const struct rule rules[] = { 3107e1199815SMickaël Salaün { 3108e1199815SMickaël Salaün .path = dir_s1d2, 3109e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3110e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3111e1199815SMickaël Salaün }, 3112135464f9SMickaël Salaün {}, 3113e1199815SMickaël Salaün }; 3114e1199815SMickaël Salaün /* Limits read and write access to files tied to the filesystem. */ 3115371183faSMickaël Salaün const int ruleset_fd = 3116371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3117e1199815SMickaël Salaün 3118e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3119e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3120e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3121e1199815SMickaël Salaün 3122e1199815SMickaël Salaün /* Checks enforcement for normal files. */ 3123e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3124e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3125e1199815SMickaël Salaün 3126e1199815SMickaël Salaün /* Checks access to pipes through FD. */ 3127e1199815SMickaël Salaün ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3128371183faSMickaël Salaün ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3129371183faSMickaël Salaün { 3130e1199815SMickaël Salaün TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3131e1199815SMickaël Salaün } 3132e1199815SMickaël Salaün ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3133e1199815SMickaël Salaün ASSERT_EQ('.', buf); 3134e1199815SMickaël Salaün 3135e1199815SMickaël Salaün /* Checks write access to pipe through /proc/self/fd . */ 3136e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3137e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3138371183faSMickaël Salaün ASSERT_EQ(1, write(proc_fd, ".", 1)) 3139371183faSMickaël Salaün { 3140e1199815SMickaël Salaün TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3141e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 3142e1199815SMickaël Salaün } 3143e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3144e1199815SMickaël Salaün 3145e1199815SMickaël Salaün /* Checks read access to pipe through /proc/self/fd . */ 3146e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3147e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3148e1199815SMickaël Salaün buf = '\0'; 3149371183faSMickaël Salaün ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3150371183faSMickaël Salaün { 3151e1199815SMickaël Salaün TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3152e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 3153e1199815SMickaël Salaün } 3154e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3155e1199815SMickaël Salaün 3156e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[0])); 3157e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[1])); 3158e1199815SMickaël Salaün } 3159e1199815SMickaël Salaün 31604598d9abSMickaël Salaün /* clang-format off */ 31614598d9abSMickaël Salaün FIXTURE(layout1_bind) {}; 31624598d9abSMickaël Salaün /* clang-format on */ 3163e1199815SMickaël Salaün 3164e1199815SMickaël Salaün FIXTURE_SETUP(layout1_bind) 3165e1199815SMickaël Salaün { 3166e1199815SMickaël Salaün prepare_layout(_metadata); 3167e1199815SMickaël Salaün 3168e1199815SMickaël Salaün create_layout1(_metadata); 3169e1199815SMickaël Salaün 3170e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3171e1199815SMickaël Salaün ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 3172e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3173e1199815SMickaël Salaün } 3174e1199815SMickaël Salaün 3175e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1_bind) 3176e1199815SMickaël Salaün { 3177e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3178e1199815SMickaël Salaün EXPECT_EQ(0, umount(dir_s2d2)); 3179e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3180e1199815SMickaël Salaün 3181e1199815SMickaël Salaün remove_layout1(_metadata); 3182e1199815SMickaël Salaün 3183e1199815SMickaël Salaün cleanup_layout(_metadata); 3184e1199815SMickaël Salaün } 3185e1199815SMickaël Salaün 3186e1199815SMickaël Salaün static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 3187e1199815SMickaël Salaün static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 3188e1199815SMickaël Salaün 3189e1199815SMickaël Salaün /* 3190e1199815SMickaël Salaün * layout1_bind hierarchy: 3191e1199815SMickaël Salaün * 3192e1199815SMickaël Salaün * tmp 3193e1199815SMickaël Salaün * ├── s1d1 3194e1199815SMickaël Salaün * │ ├── f1 3195e1199815SMickaël Salaün * │ ├── f2 3196e1199815SMickaël Salaün * │ └── s1d2 3197e1199815SMickaël Salaün * │ ├── f1 3198e1199815SMickaël Salaün * │ ├── f2 3199e1199815SMickaël Salaün * │ └── s1d3 3200e1199815SMickaël Salaün * │ ├── f1 3201e1199815SMickaël Salaün * │ └── f2 3202e1199815SMickaël Salaün * ├── s2d1 3203e1199815SMickaël Salaün * │ ├── f1 3204e1199815SMickaël Salaün * │ └── s2d2 3205e1199815SMickaël Salaün * │ ├── f1 3206e1199815SMickaël Salaün * │ ├── f2 3207e1199815SMickaël Salaün * │ └── s1d3 3208e1199815SMickaël Salaün * │ ├── f1 3209e1199815SMickaël Salaün * │ └── f2 3210e1199815SMickaël Salaün * └── s3d1 3211e1199815SMickaël Salaün * └── s3d2 3212e1199815SMickaël Salaün * └── s3d3 3213e1199815SMickaël Salaün */ 3214e1199815SMickaël Salaün 3215e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, no_restriction) 3216e1199815SMickaël Salaün { 3217e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 3218e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3219e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 3220e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3221e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 3222e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3223e1199815SMickaël Salaün 3224e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 3225e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 3226e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 3227e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 3228e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 3229e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 3230e1199815SMickaël Salaün 3231e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 3232e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3233e1199815SMickaël Salaün 3234e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 3235e1199815SMickaël Salaün } 3236e1199815SMickaël Salaün 3237e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, same_content_same_file) 3238e1199815SMickaël Salaün { 3239e1199815SMickaël Salaün /* 3240e1199815SMickaël Salaün * Sets access right on parent directories of both source and 3241e1199815SMickaël Salaün * destination mount points. 3242e1199815SMickaël Salaün */ 3243e1199815SMickaël Salaün const struct rule layer1_parent[] = { 3244e1199815SMickaël Salaün { 3245e1199815SMickaël Salaün .path = dir_s1d1, 3246e1199815SMickaël Salaün .access = ACCESS_RO, 3247e1199815SMickaël Salaün }, 3248e1199815SMickaël Salaün { 3249e1199815SMickaël Salaün .path = dir_s2d1, 3250e1199815SMickaël Salaün .access = ACCESS_RW, 3251e1199815SMickaël Salaün }, 3252135464f9SMickaël Salaün {}, 3253e1199815SMickaël Salaün }; 3254e1199815SMickaël Salaün /* 3255e1199815SMickaël Salaün * Sets access rights on the same bind-mounted directories. The result 3256e1199815SMickaël Salaün * should be ACCESS_RW for both directories, but not both hierarchies 3257e1199815SMickaël Salaün * because of the first layer. 3258e1199815SMickaël Salaün */ 3259e1199815SMickaël Salaün const struct rule layer2_mount_point[] = { 3260e1199815SMickaël Salaün { 3261e1199815SMickaël Salaün .path = dir_s1d2, 3262e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3263e1199815SMickaël Salaün }, 3264e1199815SMickaël Salaün { 3265e1199815SMickaël Salaün .path = dir_s2d2, 3266e1199815SMickaël Salaün .access = ACCESS_RW, 3267e1199815SMickaël Salaün }, 3268135464f9SMickaël Salaün {}, 3269e1199815SMickaël Salaün }; 3270e1199815SMickaël Salaün /* Only allow read-access to the s1d3 hierarchies. */ 3271e1199815SMickaël Salaün const struct rule layer3_source[] = { 3272e1199815SMickaël Salaün { 3273e1199815SMickaël Salaün .path = dir_s1d3, 3274e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3275e1199815SMickaël Salaün }, 3276135464f9SMickaël Salaün {}, 3277e1199815SMickaël Salaün }; 3278e1199815SMickaël Salaün /* Removes all access rights. */ 3279e1199815SMickaël Salaün const struct rule layer4_destination[] = { 3280e1199815SMickaël Salaün { 3281e1199815SMickaël Salaün .path = bind_file1_s1d3, 3282e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3283e1199815SMickaël Salaün }, 3284135464f9SMickaël Salaün {}, 3285e1199815SMickaël Salaün }; 3286e1199815SMickaël Salaün int ruleset_fd; 3287e1199815SMickaël Salaün 3288e1199815SMickaël Salaün /* Sets rules for the parent directories. */ 3289e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 3290e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3291e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3292e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3293e1199815SMickaël Salaün 3294e1199815SMickaël Salaün /* Checks source hierarchy. */ 3295e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3296e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3297e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3298e1199815SMickaël Salaün 3299e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3300e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3301e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3302e1199815SMickaël Salaün 3303e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3304e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 3305e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3306e1199815SMickaël Salaün 3307e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3308e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3309e1199815SMickaël Salaün 3310e1199815SMickaël Salaün /* Sets rules for the mount points. */ 3311e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 3312e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3313e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3314e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3315e1199815SMickaël Salaün 3316e1199815SMickaël Salaün /* Checks source hierarchy. */ 3317e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 3318e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3319e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3320e1199815SMickaël Salaün 3321e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3322e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3323e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3324e1199815SMickaël Salaün 3325e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3326e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 3327e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 3328e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3329e1199815SMickaël Salaün 3330e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3331e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3332e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3333e1199815SMickaël Salaün 3334e1199815SMickaël Salaün /* Sets a (shared) rule only on the source. */ 3335e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 3336e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3337e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3338e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3339e1199815SMickaël Salaün 3340e1199815SMickaël Salaün /* Checks source hierarchy. */ 3341e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 3342e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3343e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3344e1199815SMickaël Salaün 3345e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3346e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3347e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 3348e1199815SMickaël Salaün 3349e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3350e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 3351e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 3352e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3353e1199815SMickaël Salaün 3354e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3355e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3356e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3357e1199815SMickaël Salaün 3358e1199815SMickaël Salaün /* Sets a (shared) rule only on the destination. */ 3359e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 3360e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3361e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3362e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3363e1199815SMickaël Salaün 3364e1199815SMickaël Salaün /* Checks source hierarchy. */ 3365e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 3366e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3367e1199815SMickaël Salaün 3368e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3369e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 3370e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3371e1199815SMickaël Salaün } 3372e1199815SMickaël Salaün 3373f4056b92SMickaël Salaün TEST_F_FORK(layout1_bind, reparent_cross_mount) 3374f4056b92SMickaël Salaün { 3375f4056b92SMickaël Salaün const struct rule layer1[] = { 3376f4056b92SMickaël Salaün { 3377f4056b92SMickaël Salaün /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 3378f4056b92SMickaël Salaün .path = dir_s2d1, 3379f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 3380f4056b92SMickaël Salaün }, 3381f4056b92SMickaël Salaün { 3382f4056b92SMickaël Salaün .path = bind_dir_s1d3, 3383f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 3384f4056b92SMickaël Salaün }, 3385f4056b92SMickaël Salaün {}, 3386f4056b92SMickaël Salaün }; 3387f4056b92SMickaël Salaün int ruleset_fd = create_ruleset( 3388f4056b92SMickaël Salaün _metadata, 3389f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 3390f4056b92SMickaël Salaün 3391f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3392f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3393f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3394f4056b92SMickaël Salaün 3395f4056b92SMickaël Salaün /* Checks basic denied move. */ 3396f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 3397f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3398f4056b92SMickaël Salaün 3399f4056b92SMickaël Salaün /* Checks real cross-mount move (Landlock is not involved). */ 3400f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 3401f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3402f4056b92SMickaël Salaün 3403f4056b92SMickaël Salaün /* Checks move that will give more accesses. */ 3404f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 3405f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3406f4056b92SMickaël Salaün 3407f4056b92SMickaël Salaün /* Checks legitimate downgrade move. */ 3408f4056b92SMickaël Salaün ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 3409f4056b92SMickaël Salaün } 3410f4056b92SMickaël Salaün 3411e1199815SMickaël Salaün #define LOWER_BASE TMP_DIR "/lower" 3412e1199815SMickaël Salaün #define LOWER_DATA LOWER_BASE "/data" 3413e1199815SMickaël Salaün static const char lower_fl1[] = LOWER_DATA "/fl1"; 3414e1199815SMickaël Salaün static const char lower_dl1[] = LOWER_DATA "/dl1"; 3415e1199815SMickaël Salaün static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 3416e1199815SMickaël Salaün static const char lower_fo1[] = LOWER_DATA "/fo1"; 3417e1199815SMickaël Salaün static const char lower_do1[] = LOWER_DATA "/do1"; 3418e1199815SMickaël Salaün static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 3419e1199815SMickaël Salaün static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 3420e1199815SMickaël Salaün 3421e1199815SMickaël Salaün static const char (*lower_base_files[])[] = { 3422e1199815SMickaël Salaün &lower_fl1, 3423e1199815SMickaël Salaün &lower_fo1, 3424135464f9SMickaël Salaün NULL, 3425e1199815SMickaël Salaün }; 3426e1199815SMickaël Salaün static const char (*lower_base_directories[])[] = { 3427e1199815SMickaël Salaün &lower_dl1, 3428e1199815SMickaël Salaün &lower_do1, 3429135464f9SMickaël Salaün NULL, 3430e1199815SMickaël Salaün }; 3431e1199815SMickaël Salaün static const char (*lower_sub_files[])[] = { 3432e1199815SMickaël Salaün &lower_dl1_fl2, 3433e1199815SMickaël Salaün &lower_do1_fo2, 3434e1199815SMickaël Salaün &lower_do1_fl3, 3435135464f9SMickaël Salaün NULL, 3436e1199815SMickaël Salaün }; 3437e1199815SMickaël Salaün 3438e1199815SMickaël Salaün #define UPPER_BASE TMP_DIR "/upper" 3439e1199815SMickaël Salaün #define UPPER_DATA UPPER_BASE "/data" 3440e1199815SMickaël Salaün #define UPPER_WORK UPPER_BASE "/work" 3441e1199815SMickaël Salaün static const char upper_fu1[] = UPPER_DATA "/fu1"; 3442e1199815SMickaël Salaün static const char upper_du1[] = UPPER_DATA "/du1"; 3443e1199815SMickaël Salaün static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 3444e1199815SMickaël Salaün static const char upper_fo1[] = UPPER_DATA "/fo1"; 3445e1199815SMickaël Salaün static const char upper_do1[] = UPPER_DATA "/do1"; 3446e1199815SMickaël Salaün static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 3447e1199815SMickaël Salaün static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 3448e1199815SMickaël Salaün 3449e1199815SMickaël Salaün static const char (*upper_base_files[])[] = { 3450e1199815SMickaël Salaün &upper_fu1, 3451e1199815SMickaël Salaün &upper_fo1, 3452135464f9SMickaël Salaün NULL, 3453e1199815SMickaël Salaün }; 3454e1199815SMickaël Salaün static const char (*upper_base_directories[])[] = { 3455e1199815SMickaël Salaün &upper_du1, 3456e1199815SMickaël Salaün &upper_do1, 3457135464f9SMickaël Salaün NULL, 3458e1199815SMickaël Salaün }; 3459e1199815SMickaël Salaün static const char (*upper_sub_files[])[] = { 3460e1199815SMickaël Salaün &upper_du1_fu2, 3461e1199815SMickaël Salaün &upper_do1_fo2, 3462e1199815SMickaël Salaün &upper_do1_fu3, 3463135464f9SMickaël Salaün NULL, 3464e1199815SMickaël Salaün }; 3465e1199815SMickaël Salaün 3466e1199815SMickaël Salaün #define MERGE_BASE TMP_DIR "/merge" 3467e1199815SMickaël Salaün #define MERGE_DATA MERGE_BASE "/data" 3468e1199815SMickaël Salaün static const char merge_fl1[] = MERGE_DATA "/fl1"; 3469e1199815SMickaël Salaün static const char merge_dl1[] = MERGE_DATA "/dl1"; 3470e1199815SMickaël Salaün static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 3471e1199815SMickaël Salaün static const char merge_fu1[] = MERGE_DATA "/fu1"; 3472e1199815SMickaël Salaün static const char merge_du1[] = MERGE_DATA "/du1"; 3473e1199815SMickaël Salaün static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 3474e1199815SMickaël Salaün static const char merge_fo1[] = MERGE_DATA "/fo1"; 3475e1199815SMickaël Salaün static const char merge_do1[] = MERGE_DATA "/do1"; 3476e1199815SMickaël Salaün static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 3477e1199815SMickaël Salaün static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 3478e1199815SMickaël Salaün static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 3479e1199815SMickaël Salaün 3480e1199815SMickaël Salaün static const char (*merge_base_files[])[] = { 3481e1199815SMickaël Salaün &merge_fl1, 3482e1199815SMickaël Salaün &merge_fu1, 3483e1199815SMickaël Salaün &merge_fo1, 3484135464f9SMickaël Salaün NULL, 3485e1199815SMickaël Salaün }; 3486e1199815SMickaël Salaün static const char (*merge_base_directories[])[] = { 3487e1199815SMickaël Salaün &merge_dl1, 3488e1199815SMickaël Salaün &merge_du1, 3489e1199815SMickaël Salaün &merge_do1, 3490135464f9SMickaël Salaün NULL, 3491e1199815SMickaël Salaün }; 3492e1199815SMickaël Salaün static const char (*merge_sub_files[])[] = { 3493371183faSMickaël Salaün &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 3494371183faSMickaël Salaün &merge_do1_fl3, &merge_do1_fu3, NULL, 3495e1199815SMickaël Salaün }; 3496e1199815SMickaël Salaün 3497e1199815SMickaël Salaün /* 3498e1199815SMickaël Salaün * layout2_overlay hierarchy: 3499e1199815SMickaël Salaün * 3500e1199815SMickaël Salaün * tmp 3501e1199815SMickaël Salaün * ├── lower 3502e1199815SMickaël Salaün * │ └── data 3503e1199815SMickaël Salaün * │ ├── dl1 3504e1199815SMickaël Salaün * │ │ └── fl2 3505e1199815SMickaël Salaün * │ ├── do1 3506e1199815SMickaël Salaün * │ │ ├── fl3 3507e1199815SMickaël Salaün * │ │ └── fo2 3508e1199815SMickaël Salaün * │ ├── fl1 3509e1199815SMickaël Salaün * │ └── fo1 3510e1199815SMickaël Salaün * ├── merge 3511e1199815SMickaël Salaün * │ └── data 3512e1199815SMickaël Salaün * │ ├── dl1 3513e1199815SMickaël Salaün * │ │ └── fl2 3514e1199815SMickaël Salaün * │ ├── do1 3515e1199815SMickaël Salaün * │ │ ├── fl3 3516e1199815SMickaël Salaün * │ │ ├── fo2 3517e1199815SMickaël Salaün * │ │ └── fu3 3518e1199815SMickaël Salaün * │ ├── du1 3519e1199815SMickaël Salaün * │ │ └── fu2 3520e1199815SMickaël Salaün * │ ├── fl1 3521e1199815SMickaël Salaün * │ ├── fo1 3522e1199815SMickaël Salaün * │ └── fu1 3523e1199815SMickaël Salaün * └── upper 3524e1199815SMickaël Salaün * ├── data 3525e1199815SMickaël Salaün * │ ├── do1 3526e1199815SMickaël Salaün * │ │ ├── fo2 3527e1199815SMickaël Salaün * │ │ └── fu3 3528e1199815SMickaël Salaün * │ ├── du1 3529e1199815SMickaël Salaün * │ │ └── fu2 3530e1199815SMickaël Salaün * │ ├── fo1 3531e1199815SMickaël Salaün * │ └── fu1 3532e1199815SMickaël Salaün * └── work 3533e1199815SMickaël Salaün * └── work 3534e1199815SMickaël Salaün */ 3535e1199815SMickaël Salaün 35364598d9abSMickaël Salaün /* clang-format off */ 35374598d9abSMickaël Salaün FIXTURE(layout2_overlay) {}; 35384598d9abSMickaël Salaün /* clang-format on */ 3539e1199815SMickaël Salaün 3540e1199815SMickaël Salaün FIXTURE_SETUP(layout2_overlay) 3541e1199815SMickaël Salaün { 3542e1199815SMickaël Salaün prepare_layout(_metadata); 3543e1199815SMickaël Salaün 3544e1199815SMickaël Salaün create_directory(_metadata, LOWER_BASE); 3545e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3546e1199815SMickaël Salaün /* Creates tmpfs mount points to get deterministic overlayfs. */ 3547e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700")); 3548e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3549e1199815SMickaël Salaün create_file(_metadata, lower_fl1); 3550e1199815SMickaël Salaün create_file(_metadata, lower_dl1_fl2); 3551e1199815SMickaël Salaün create_file(_metadata, lower_fo1); 3552e1199815SMickaël Salaün create_file(_metadata, lower_do1_fo2); 3553e1199815SMickaël Salaün create_file(_metadata, lower_do1_fl3); 3554e1199815SMickaël Salaün 3555e1199815SMickaël Salaün create_directory(_metadata, UPPER_BASE); 3556e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3557e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700")); 3558e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3559e1199815SMickaël Salaün create_file(_metadata, upper_fu1); 3560e1199815SMickaël Salaün create_file(_metadata, upper_du1_fu2); 3561e1199815SMickaël Salaün create_file(_metadata, upper_fo1); 3562e1199815SMickaël Salaün create_file(_metadata, upper_do1_fo2); 3563e1199815SMickaël Salaün create_file(_metadata, upper_do1_fu3); 3564e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 3565e1199815SMickaël Salaün 3566e1199815SMickaël Salaün create_directory(_metadata, MERGE_DATA); 3567e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3568e1199815SMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 3569e1199815SMickaël Salaün ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 3570371183faSMickaël Salaün "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 3571e1199815SMickaël Salaün ",workdir=" UPPER_WORK)); 3572e1199815SMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 3573e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3574e1199815SMickaël Salaün } 3575e1199815SMickaël Salaün 3576e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout2_overlay) 3577e1199815SMickaël Salaün { 3578e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fl3)); 3579e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 3580e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fl1)); 3581e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fo2)); 3582e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fo1)); 3583e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3584e1199815SMickaël Salaün EXPECT_EQ(0, umount(LOWER_BASE)); 3585e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3586e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(LOWER_BASE)); 3587e1199815SMickaël Salaün 3588e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fu3)); 3589e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_du1_fu2)); 3590e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fu1)); 3591e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fo2)); 3592e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fo1)); 3593e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 3594e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3595e1199815SMickaël Salaün EXPECT_EQ(0, umount(UPPER_BASE)); 3596e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3597e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_BASE)); 3598e1199815SMickaël Salaün 3599e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3600e1199815SMickaël Salaün EXPECT_EQ(0, umount(MERGE_DATA)); 3601e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3602e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(MERGE_DATA)); 3603e1199815SMickaël Salaün 3604e1199815SMickaël Salaün cleanup_layout(_metadata); 3605e1199815SMickaël Salaün } 3606e1199815SMickaël Salaün 3607e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, no_restriction) 3608e1199815SMickaël Salaün { 3609e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 3610e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 3611e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 3612e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 3613e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 3614e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 3615e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 3616e1199815SMickaël Salaün 3617e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 3618e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 3619e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 3620e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 3621e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 3622e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 3623e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 3624e1199815SMickaël Salaün 3625e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 3626e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 3627e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 3628e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 3629e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 3630e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 3631e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 3632e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 3633e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 3634e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 3635e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 3636e1199815SMickaël Salaün } 3637e1199815SMickaël Salaün 3638e1199815SMickaël Salaün #define for_each_path(path_list, path_entry, i) \ 3639e1199815SMickaël Salaün for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 3640e1199815SMickaël Salaün path_entry = *path_list[++i]) 3641e1199815SMickaël Salaün 3642e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, same_content_different_file) 3643e1199815SMickaël Salaün { 3644e1199815SMickaël Salaün /* Sets access right on parent directories of both layers. */ 3645e1199815SMickaël Salaün const struct rule layer1_base[] = { 3646e1199815SMickaël Salaün { 3647e1199815SMickaël Salaün .path = LOWER_BASE, 3648e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3649e1199815SMickaël Salaün }, 3650e1199815SMickaël Salaün { 3651e1199815SMickaël Salaün .path = UPPER_BASE, 3652e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3653e1199815SMickaël Salaün }, 3654e1199815SMickaël Salaün { 3655e1199815SMickaël Salaün .path = MERGE_BASE, 3656e1199815SMickaël Salaün .access = ACCESS_RW, 3657e1199815SMickaël Salaün }, 3658135464f9SMickaël Salaün {}, 3659e1199815SMickaël Salaün }; 3660e1199815SMickaël Salaün const struct rule layer2_data[] = { 3661e1199815SMickaël Salaün { 3662e1199815SMickaël Salaün .path = LOWER_DATA, 3663e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3664e1199815SMickaël Salaün }, 3665e1199815SMickaël Salaün { 3666e1199815SMickaël Salaün .path = UPPER_DATA, 3667e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3668e1199815SMickaël Salaün }, 3669e1199815SMickaël Salaün { 3670e1199815SMickaël Salaün .path = MERGE_DATA, 3671e1199815SMickaël Salaün .access = ACCESS_RW, 3672e1199815SMickaël Salaün }, 3673135464f9SMickaël Salaün {}, 3674e1199815SMickaël Salaün }; 3675e1199815SMickaël Salaün /* Sets access right on directories inside both layers. */ 3676e1199815SMickaël Salaün const struct rule layer3_subdirs[] = { 3677e1199815SMickaël Salaün { 3678e1199815SMickaël Salaün .path = lower_dl1, 3679e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3680e1199815SMickaël Salaün }, 3681e1199815SMickaël Salaün { 3682e1199815SMickaël Salaün .path = lower_do1, 3683e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3684e1199815SMickaël Salaün }, 3685e1199815SMickaël Salaün { 3686e1199815SMickaël Salaün .path = upper_du1, 3687e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3688e1199815SMickaël Salaün }, 3689e1199815SMickaël Salaün { 3690e1199815SMickaël Salaün .path = upper_do1, 3691e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3692e1199815SMickaël Salaün }, 3693e1199815SMickaël Salaün { 3694e1199815SMickaël Salaün .path = merge_dl1, 3695e1199815SMickaël Salaün .access = ACCESS_RW, 3696e1199815SMickaël Salaün }, 3697e1199815SMickaël Salaün { 3698e1199815SMickaël Salaün .path = merge_du1, 3699e1199815SMickaël Salaün .access = ACCESS_RW, 3700e1199815SMickaël Salaün }, 3701e1199815SMickaël Salaün { 3702e1199815SMickaël Salaün .path = merge_do1, 3703e1199815SMickaël Salaün .access = ACCESS_RW, 3704e1199815SMickaël Salaün }, 3705135464f9SMickaël Salaün {}, 3706e1199815SMickaël Salaün }; 3707e1199815SMickaël Salaün /* Tighten access rights to the files. */ 3708e1199815SMickaël Salaün const struct rule layer4_files[] = { 3709e1199815SMickaël Salaün { 3710e1199815SMickaël Salaün .path = lower_dl1_fl2, 3711e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3712e1199815SMickaël Salaün }, 3713e1199815SMickaël Salaün { 3714e1199815SMickaël Salaün .path = lower_do1_fo2, 3715e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3716e1199815SMickaël Salaün }, 3717e1199815SMickaël Salaün { 3718e1199815SMickaël Salaün .path = lower_do1_fl3, 3719e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3720e1199815SMickaël Salaün }, 3721e1199815SMickaël Salaün { 3722e1199815SMickaël Salaün .path = upper_du1_fu2, 3723e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3724e1199815SMickaël Salaün }, 3725e1199815SMickaël Salaün { 3726e1199815SMickaël Salaün .path = upper_do1_fo2, 3727e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3728e1199815SMickaël Salaün }, 3729e1199815SMickaël Salaün { 3730e1199815SMickaël Salaün .path = upper_do1_fu3, 3731e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3732e1199815SMickaël Salaün }, 3733e1199815SMickaël Salaün { 3734e1199815SMickaël Salaün .path = merge_dl1_fl2, 3735e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3736e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3737e1199815SMickaël Salaün }, 3738e1199815SMickaël Salaün { 3739e1199815SMickaël Salaün .path = merge_du1_fu2, 3740e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3741e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3742e1199815SMickaël Salaün }, 3743e1199815SMickaël Salaün { 3744e1199815SMickaël Salaün .path = merge_do1_fo2, 3745e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3746e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3747e1199815SMickaël Salaün }, 3748e1199815SMickaël Salaün { 3749e1199815SMickaël Salaün .path = merge_do1_fl3, 3750e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3751e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3752e1199815SMickaël Salaün }, 3753e1199815SMickaël Salaün { 3754e1199815SMickaël Salaün .path = merge_do1_fu3, 3755e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3756e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3757e1199815SMickaël Salaün }, 3758135464f9SMickaël Salaün {}, 3759e1199815SMickaël Salaün }; 3760e1199815SMickaël Salaün const struct rule layer5_merge_only[] = { 3761e1199815SMickaël Salaün { 3762e1199815SMickaël Salaün .path = MERGE_DATA, 3763e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3764e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3765e1199815SMickaël Salaün }, 3766135464f9SMickaël Salaün {}, 3767e1199815SMickaël Salaün }; 3768e1199815SMickaël Salaün int ruleset_fd; 3769e1199815SMickaël Salaün size_t i; 3770e1199815SMickaël Salaün const char *path_entry; 3771e1199815SMickaël Salaün 3772e1199815SMickaël Salaün /* Sets rules on base directories (i.e. outside overlay scope). */ 3773e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 3774e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3775e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3776e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3777e1199815SMickaël Salaün 3778e1199815SMickaël Salaün /* Checks lower layer. */ 3779e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 3780e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 3781e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 3782e1199815SMickaël Salaün } 3783e1199815SMickaël Salaün for_each_path(lower_base_directories, path_entry, i) { 3784371183faSMickaël Salaün ASSERT_EQ(EACCES, 3785371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3786e1199815SMickaël Salaün } 3787e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 3788e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 3789e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 3790e1199815SMickaël Salaün } 3791e1199815SMickaël Salaün /* Checks upper layer. */ 3792e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 3793e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 3794e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 3795e1199815SMickaël Salaün } 3796e1199815SMickaël Salaün for_each_path(upper_base_directories, path_entry, i) { 3797371183faSMickaël Salaün ASSERT_EQ(EACCES, 3798371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3799e1199815SMickaël Salaün } 3800e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 3801e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 3802e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 3803e1199815SMickaël Salaün } 3804e1199815SMickaël Salaün /* 3805e1199815SMickaël Salaün * Checks that access rights are independent from the lower and upper 3806e1199815SMickaël Salaün * layers: write access to upper files viewed through the merge point 3807e1199815SMickaël Salaün * is still allowed, and write access to lower file viewed (and copied) 3808e1199815SMickaël Salaün * through the merge point is still allowed. 3809e1199815SMickaël Salaün */ 3810e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 3811e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3812e1199815SMickaël Salaün } 3813e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 3814e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3815e1199815SMickaël Salaün } 3816e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 3817e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3818e1199815SMickaël Salaün } 3819e1199815SMickaël Salaün 3820e1199815SMickaël Salaün /* Sets rules on data directories (i.e. inside overlay scope). */ 3821e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 3822e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3823e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3824e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3825e1199815SMickaël Salaün 3826e1199815SMickaël Salaün /* Checks merge. */ 3827e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 3828e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3829e1199815SMickaël Salaün } 3830e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 3831e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3832e1199815SMickaël Salaün } 3833e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 3834e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3835e1199815SMickaël Salaün } 3836e1199815SMickaël Salaün 3837e1199815SMickaël Salaün /* Same checks with tighter rules. */ 3838e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 3839e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3840e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3841e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3842e1199815SMickaël Salaün 3843e1199815SMickaël Salaün /* Checks changes for lower layer. */ 3844e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 3845e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 3846e1199815SMickaël Salaün } 3847e1199815SMickaël Salaün /* Checks changes for upper layer. */ 3848e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 3849e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 3850e1199815SMickaël Salaün } 3851e1199815SMickaël Salaün /* Checks all merge accesses. */ 3852e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 3853e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 3854e1199815SMickaël Salaün } 3855e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 3856e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3857e1199815SMickaël Salaün } 3858e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 3859e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3860e1199815SMickaël Salaün } 3861e1199815SMickaël Salaün 3862e1199815SMickaël Salaün /* Sets rules directly on overlayed files. */ 3863e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 3864e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3865e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3866e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3867e1199815SMickaël Salaün 3868e1199815SMickaël Salaün /* Checks unchanged accesses on lower layer. */ 3869e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 3870e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 3871e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 3872e1199815SMickaël Salaün } 3873e1199815SMickaël Salaün /* Checks unchanged accesses on upper layer. */ 3874e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 3875e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 3876e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 3877e1199815SMickaël Salaün } 3878e1199815SMickaël Salaün /* Checks all merge accesses. */ 3879e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 3880e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 3881e1199815SMickaël Salaün } 3882e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 3883371183faSMickaël Salaün ASSERT_EQ(EACCES, 3884371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3885e1199815SMickaël Salaün } 3886e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 3887e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3888e1199815SMickaël Salaün } 3889e1199815SMickaël Salaün 3890e1199815SMickaël Salaün /* Only allowes access to the merge hierarchy. */ 3891e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 3892e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3893e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3894e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3895e1199815SMickaël Salaün 3896e1199815SMickaël Salaün /* Checks new accesses on lower layer. */ 3897e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 3898e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 3899e1199815SMickaël Salaün } 3900e1199815SMickaël Salaün /* Checks new accesses on upper layer. */ 3901e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 3902e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 3903e1199815SMickaël Salaün } 3904e1199815SMickaël Salaün /* Checks all merge accesses. */ 3905e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 3906e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 3907e1199815SMickaël Salaün } 3908e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 3909371183faSMickaël Salaün ASSERT_EQ(EACCES, 3910371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 3911e1199815SMickaël Salaün } 3912e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 3913e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 3914e1199815SMickaël Salaün } 3915e1199815SMickaël Salaün } 3916e1199815SMickaël Salaün 3917e1199815SMickaël Salaün TEST_HARNESS_MAIN 3918