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 755e55920SMickaë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> 14366617a6SJeff Xu #include <stdio.h> 15e1199815SMickaël Salaün #include <string.h> 16e1199815SMickaël Salaün #include <sys/capability.h> 17e1199815SMickaël Salaün #include <sys/mount.h> 18e1199815SMickaël Salaün #include <sys/prctl.h> 19e1199815SMickaël Salaün #include <sys/sendfile.h> 20e1199815SMickaël Salaün #include <sys/stat.h> 21e1199815SMickaël Salaün #include <sys/sysmacros.h> 22e1199815SMickaël Salaün #include <unistd.h> 23e1199815SMickaël Salaün 24e1199815SMickaël Salaün #include "common.h" 25e1199815SMickaël Salaün 2687129ef1SMickaël Salaün #ifndef renameat2 2787129ef1SMickaël Salaün int renameat2(int olddirfd, const char *oldpath, int newdirfd, 2887129ef1SMickaël Salaün const char *newpath, unsigned int flags) 2987129ef1SMickaël Salaün { 3087129ef1SMickaël Salaün return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 3187129ef1SMickaël Salaün flags); 3287129ef1SMickaël Salaün } 3387129ef1SMickaël Salaün #endif 3487129ef1SMickaël Salaün 3587129ef1SMickaël Salaün #ifndef RENAME_EXCHANGE 3687129ef1SMickaël Salaün #define RENAME_EXCHANGE (1 << 1) 3787129ef1SMickaël Salaün #endif 3887129ef1SMickaël Salaün 39e1199815SMickaël Salaün #define TMP_DIR "tmp" 40e1199815SMickaël Salaün #define BINARY_PATH "./true" 41e1199815SMickaël Salaün 42e1199815SMickaël Salaün /* Paths (sibling number and depth) */ 43e1199815SMickaël Salaün static const char dir_s1d1[] = TMP_DIR "/s1d1"; 44e1199815SMickaël Salaün static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 45e1199815SMickaël Salaün static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 46e1199815SMickaël Salaün static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 47e1199815SMickaël Salaün static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 48e1199815SMickaël Salaün static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 49e1199815SMickaël Salaün static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 50e1199815SMickaël Salaün static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 51e1199815SMickaël Salaün static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 52e1199815SMickaël Salaün 53e1199815SMickaël Salaün static const char dir_s2d1[] = TMP_DIR "/s2d1"; 54e1199815SMickaël Salaün static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 55e1199815SMickaël Salaün static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 56e1199815SMickaël Salaün static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 57e1199815SMickaël Salaün static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 58e1199815SMickaël Salaün static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 59e1199815SMickaël Salaün static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 60e1199815SMickaël Salaün 61e1199815SMickaël Salaün static const char dir_s3d1[] = TMP_DIR "/s3d1"; 62225351abSGünther Noack static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; 63e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 64e1199815SMickaël Salaün static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 65e1199815SMickaël Salaün static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 66e1199815SMickaël Salaün 67e1199815SMickaël Salaün /* 68e1199815SMickaël Salaün * layout1 hierarchy: 69e1199815SMickaël Salaün * 70e1199815SMickaël Salaün * tmp 71e1199815SMickaël Salaün * ├── s1d1 72e1199815SMickaël Salaün * │ ├── f1 73e1199815SMickaël Salaün * │ ├── f2 74e1199815SMickaël Salaün * │ └── s1d2 75e1199815SMickaël Salaün * │ ├── f1 76e1199815SMickaël Salaün * │ ├── f2 77e1199815SMickaël Salaün * │ └── s1d3 78e1199815SMickaël Salaün * │ ├── f1 79e1199815SMickaël Salaün * │ └── f2 80e1199815SMickaël Salaün * ├── s2d1 81e1199815SMickaël Salaün * │ ├── f1 82e1199815SMickaël Salaün * │ └── s2d2 83e1199815SMickaël Salaün * │ ├── f1 84e1199815SMickaël Salaün * │ └── s2d3 85e1199815SMickaël Salaün * │ ├── f1 86e1199815SMickaël Salaün * │ └── f2 87e1199815SMickaël Salaün * └── s3d1 88225351abSGünther Noack * ├── f1 89e1199815SMickaël Salaün * └── s3d2 90e1199815SMickaël Salaün * └── s3d3 91e1199815SMickaël Salaün */ 92e1199815SMickaël Salaün 93366617a6SJeff Xu static bool fgrep(FILE *const inf, const char *const str) 94366617a6SJeff Xu { 95366617a6SJeff Xu char line[32]; 96366617a6SJeff Xu const int slen = strlen(str); 97366617a6SJeff Xu 98366617a6SJeff Xu while (!feof(inf)) { 99366617a6SJeff Xu if (!fgets(line, sizeof(line), inf)) 100366617a6SJeff Xu break; 101366617a6SJeff Xu if (strncmp(line, str, slen)) 102366617a6SJeff Xu continue; 103366617a6SJeff Xu 104366617a6SJeff Xu return true; 105366617a6SJeff Xu } 106366617a6SJeff Xu 107366617a6SJeff Xu return false; 108366617a6SJeff Xu } 109366617a6SJeff Xu 110366617a6SJeff Xu static bool supports_overlayfs(void) 111366617a6SJeff Xu { 112366617a6SJeff Xu bool res; 113366617a6SJeff Xu FILE *const inf = fopen("/proc/filesystems", "r"); 114366617a6SJeff Xu 115366617a6SJeff Xu /* 116366617a6SJeff Xu * Consider that the filesystem is supported if we cannot get the 117366617a6SJeff Xu * supported ones. 118366617a6SJeff Xu */ 119366617a6SJeff Xu if (!inf) 120366617a6SJeff Xu return true; 121366617a6SJeff Xu 122366617a6SJeff Xu res = fgrep(inf, "nodev\toverlay\n"); 123366617a6SJeff Xu fclose(inf); 124366617a6SJeff Xu return res; 125366617a6SJeff Xu } 126366617a6SJeff Xu 127e1199815SMickaël Salaün static void mkdir_parents(struct __test_metadata *const _metadata, 128e1199815SMickaël Salaün const char *const path) 129e1199815SMickaël Salaün { 130e1199815SMickaël Salaün char *walker; 131e1199815SMickaël Salaün const char *parent; 132e1199815SMickaël Salaün int i, err; 133e1199815SMickaël Salaün 134e1199815SMickaël Salaün ASSERT_NE(path[0], '\0'); 135e1199815SMickaël Salaün walker = strdup(path); 136e1199815SMickaël Salaün ASSERT_NE(NULL, walker); 137e1199815SMickaël Salaün parent = walker; 138e1199815SMickaël Salaün for (i = 1; walker[i]; i++) { 139e1199815SMickaël Salaün if (walker[i] != '/') 140e1199815SMickaël Salaün continue; 141e1199815SMickaël Salaün walker[i] = '\0'; 142e1199815SMickaël Salaün err = mkdir(parent, 0700); 143371183faSMickaël Salaün ASSERT_FALSE(err && errno != EEXIST) 144371183faSMickaël Salaün { 145371183faSMickaël Salaün TH_LOG("Failed to create directory \"%s\": %s", parent, 146371183faSMickaël Salaün strerror(errno)); 147e1199815SMickaël Salaün } 148e1199815SMickaël Salaün walker[i] = '/'; 149e1199815SMickaël Salaün } 150e1199815SMickaël Salaün free(walker); 151e1199815SMickaël Salaün } 152e1199815SMickaël Salaün 153e1199815SMickaël Salaün static void create_directory(struct __test_metadata *const _metadata, 154e1199815SMickaël Salaün const char *const path) 155e1199815SMickaël Salaün { 156e1199815SMickaël Salaün mkdir_parents(_metadata, path); 157371183faSMickaël Salaün ASSERT_EQ(0, mkdir(path, 0700)) 158371183faSMickaël Salaün { 159e1199815SMickaël Salaün TH_LOG("Failed to create directory \"%s\": %s", path, 160e1199815SMickaël Salaün strerror(errno)); 161e1199815SMickaël Salaün } 162e1199815SMickaël Salaün } 163e1199815SMickaël Salaün 164e1199815SMickaël Salaün static void create_file(struct __test_metadata *const _metadata, 165e1199815SMickaël Salaün const char *const path) 166e1199815SMickaël Salaün { 167e1199815SMickaël Salaün mkdir_parents(_metadata, path); 168371183faSMickaël Salaün ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 169371183faSMickaël Salaün { 170e1199815SMickaël Salaün TH_LOG("Failed to create file \"%s\": %s", path, 171e1199815SMickaël Salaün strerror(errno)); 172e1199815SMickaël Salaün } 173e1199815SMickaël Salaün } 174e1199815SMickaël Salaün 175e1199815SMickaël Salaün static int remove_path(const char *const path) 176e1199815SMickaël Salaün { 177e1199815SMickaël Salaün char *walker; 178e1199815SMickaël Salaün int i, ret, err = 0; 179e1199815SMickaël Salaün 180e1199815SMickaël Salaün walker = strdup(path); 181e1199815SMickaël Salaün if (!walker) { 182e1199815SMickaël Salaün err = ENOMEM; 183e1199815SMickaël Salaün goto out; 184e1199815SMickaël Salaün } 185e1199815SMickaël Salaün if (unlink(path) && rmdir(path)) { 186f4056b92SMickaël Salaün if (errno != ENOENT && errno != ENOTDIR) 187e1199815SMickaël Salaün err = errno; 188e1199815SMickaël Salaün goto out; 189e1199815SMickaël Salaün } 190e1199815SMickaël Salaün for (i = strlen(walker); i > 0; i--) { 191e1199815SMickaël Salaün if (walker[i] != '/') 192e1199815SMickaël Salaün continue; 193e1199815SMickaël Salaün walker[i] = '\0'; 194e1199815SMickaël Salaün ret = rmdir(walker); 195e1199815SMickaël Salaün if (ret) { 196e1199815SMickaël Salaün if (errno != ENOTEMPTY && errno != EBUSY) 197e1199815SMickaël Salaün err = errno; 198e1199815SMickaël Salaün goto out; 199e1199815SMickaël Salaün } 200e1199815SMickaël Salaün if (strcmp(walker, TMP_DIR) == 0) 201e1199815SMickaël Salaün goto out; 202e1199815SMickaël Salaün } 203e1199815SMickaël Salaün 204e1199815SMickaël Salaün out: 205e1199815SMickaël Salaün free(walker); 206e1199815SMickaël Salaün return err; 207e1199815SMickaël Salaün } 208e1199815SMickaël Salaün 209e1199815SMickaël Salaün static void prepare_layout(struct __test_metadata *const _metadata) 210e1199815SMickaël Salaün { 211e1199815SMickaël Salaün disable_caps(_metadata); 212e1199815SMickaël Salaün umask(0077); 213e1199815SMickaël Salaün create_directory(_metadata, TMP_DIR); 214e1199815SMickaël Salaün 215e1199815SMickaël Salaün /* 216e1199815SMickaël Salaün * Do not pollute the rest of the system: creates a private mount point 217e1199815SMickaël Salaün * for tests relying on pivot_root(2) and move_mount(2). 218e1199815SMickaël Salaün */ 219e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 220e1199815SMickaël Salaün ASSERT_EQ(0, unshare(CLONE_NEWNS)); 221e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700")); 222e1199815SMickaël Salaün ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 223e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 224e1199815SMickaël Salaün } 225e1199815SMickaël Salaün 226e1199815SMickaël Salaün static void cleanup_layout(struct __test_metadata *const _metadata) 227e1199815SMickaël Salaün { 228e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 229e1199815SMickaël Salaün EXPECT_EQ(0, umount(TMP_DIR)); 230e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 231e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(TMP_DIR)); 232e1199815SMickaël Salaün } 233e1199815SMickaël Salaün 234*592efeb4SMickaël Salaün /* clang-format off */ 235*592efeb4SMickaël Salaün FIXTURE(layout0) {}; 236*592efeb4SMickaël Salaün /* clang-format on */ 237*592efeb4SMickaël Salaün 238*592efeb4SMickaël Salaün FIXTURE_SETUP(layout0) 239*592efeb4SMickaël Salaün { 240*592efeb4SMickaël Salaün prepare_layout(_metadata); 241*592efeb4SMickaël Salaün } 242*592efeb4SMickaël Salaün 243*592efeb4SMickaël Salaün FIXTURE_TEARDOWN(layout0) 244*592efeb4SMickaël Salaün { 245*592efeb4SMickaël Salaün cleanup_layout(_metadata); 246*592efeb4SMickaël Salaün } 247*592efeb4SMickaël Salaün 248e1199815SMickaël Salaün static void create_layout1(struct __test_metadata *const _metadata) 249e1199815SMickaël Salaün { 250e1199815SMickaël Salaün create_file(_metadata, file1_s1d1); 251e1199815SMickaël Salaün create_file(_metadata, file1_s1d2); 252e1199815SMickaël Salaün create_file(_metadata, file1_s1d3); 253e1199815SMickaël Salaün create_file(_metadata, file2_s1d1); 254e1199815SMickaël Salaün create_file(_metadata, file2_s1d2); 255e1199815SMickaël Salaün create_file(_metadata, file2_s1d3); 256e1199815SMickaël Salaün 257e1199815SMickaël Salaün create_file(_metadata, file1_s2d1); 258e1199815SMickaël Salaün create_file(_metadata, file1_s2d2); 259e1199815SMickaël Salaün create_file(_metadata, file1_s2d3); 260e1199815SMickaël Salaün create_file(_metadata, file2_s2d3); 261e1199815SMickaël Salaün 262225351abSGünther Noack create_file(_metadata, file1_s3d1); 263e1199815SMickaël Salaün create_directory(_metadata, dir_s3d2); 264e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 265e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700")); 266e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 267e1199815SMickaël Salaün 268e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 269e1199815SMickaël Salaün } 270e1199815SMickaël Salaün 271e1199815SMickaël Salaün static void remove_layout1(struct __test_metadata *const _metadata) 272e1199815SMickaël Salaün { 273e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d3)); 274e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d2)); 275e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d1)); 276e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d3)); 277e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d2)); 278e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d1)); 279e1199815SMickaël Salaün 280e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s2d3)); 281e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d3)); 282e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d2)); 283e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d1)); 284e1199815SMickaël Salaün 285225351abSGünther Noack EXPECT_EQ(0, remove_path(file1_s3d1)); 286e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(dir_s3d3)); 287e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 288e1199815SMickaël Salaün umount(dir_s3d2); 289e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 290e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(dir_s3d2)); 291e1199815SMickaël Salaün } 292e1199815SMickaël Salaün 2934598d9abSMickaël Salaün /* clang-format off */ 2944598d9abSMickaël Salaün FIXTURE(layout1) {}; 2954598d9abSMickaël Salaün /* clang-format on */ 296e1199815SMickaël Salaün 297e1199815SMickaël Salaün FIXTURE_SETUP(layout1) 298e1199815SMickaël Salaün { 299e1199815SMickaël Salaün prepare_layout(_metadata); 300e1199815SMickaël Salaün 301e1199815SMickaël Salaün create_layout1(_metadata); 302e1199815SMickaël Salaün } 303e1199815SMickaël Salaün 304e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1) 305e1199815SMickaël Salaün { 306e1199815SMickaël Salaün remove_layout1(_metadata); 307e1199815SMickaël Salaün 308e1199815SMickaël Salaün cleanup_layout(_metadata); 309e1199815SMickaël Salaün } 310e1199815SMickaël Salaün 311e1199815SMickaël Salaün /* 312e1199815SMickaël Salaün * This helper enables to use the ASSERT_* macros and print the line number 313e1199815SMickaël Salaün * pointing to the test caller. 314e1199815SMickaël Salaün */ 315371183faSMickaël Salaün static int test_open_rel(const int dirfd, const char *const path, 316371183faSMickaël Salaün const int flags) 317e1199815SMickaël Salaün { 318e1199815SMickaël Salaün int fd; 319e1199815SMickaël Salaün 320e1199815SMickaël Salaün /* Works with file and directories. */ 321e1199815SMickaël Salaün fd = openat(dirfd, path, flags | O_CLOEXEC); 322e1199815SMickaël Salaün if (fd < 0) 323e1199815SMickaël Salaün return errno; 324e1199815SMickaël Salaün /* 325e1199815SMickaël Salaün * Mixing error codes from close(2) and open(2) should not lead to any 326e1199815SMickaël Salaün * (access type) confusion for this test. 327e1199815SMickaël Salaün */ 328e1199815SMickaël Salaün if (close(fd) != 0) 329e1199815SMickaël Salaün return errno; 330e1199815SMickaël Salaün return 0; 331e1199815SMickaël Salaün } 332e1199815SMickaël Salaün 333e1199815SMickaël Salaün static int test_open(const char *const path, const int flags) 334e1199815SMickaël Salaün { 335e1199815SMickaël Salaün return test_open_rel(AT_FDCWD, path, flags); 336e1199815SMickaël Salaün } 337e1199815SMickaël Salaün 338e1199815SMickaël Salaün TEST_F_FORK(layout1, no_restriction) 339e1199815SMickaël Salaün { 340e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 341e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 342e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 343e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 344e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 345e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 346e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 347e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 348e1199815SMickaël Salaün 349e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 350e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 351e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 352e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 353e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 354e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 355e1199815SMickaël Salaün 356e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 357e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 358e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 359e1199815SMickaël Salaün } 360e1199815SMickaël Salaün 361e1199815SMickaël Salaün TEST_F_FORK(layout1, inval) 362e1199815SMickaël Salaün { 363e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath = { 364e1199815SMickaël Salaün .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 365e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 366e1199815SMickaël Salaün .parent_fd = -1, 367e1199815SMickaël Salaün }; 368e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 369e1199815SMickaël Salaün .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 370e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 371e1199815SMickaël Salaün }; 372e1199815SMickaël Salaün int ruleset_fd; 373e1199815SMickaël Salaün 374371183faSMickaël Salaün path_beneath.parent_fd = 375371183faSMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 376e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 377e1199815SMickaël Salaün 378e1199815SMickaël Salaün ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 379e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 380e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 381e1199815SMickaël Salaün &path_beneath, 0)); 382e1199815SMickaël Salaün /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 383e1199815SMickaël Salaün ASSERT_EQ(EBADF, errno); 384e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 385e1199815SMickaël Salaün 386e1199815SMickaël Salaün ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 387e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 388e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 389e1199815SMickaël Salaün &path_beneath, 0)); 390e1199815SMickaël Salaün /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 391e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 392e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 393e1199815SMickaël Salaün 394e1199815SMickaël Salaün /* Gets a real ruleset. */ 395371183faSMickaël Salaün ruleset_fd = 396371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 397e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 398e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 399e1199815SMickaël Salaün &path_beneath, 0)); 400e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 401e1199815SMickaël Salaün 402e1199815SMickaël Salaün /* Tests without O_PATH. */ 403e1199815SMickaël Salaün path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 404e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 405e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 406e1199815SMickaël Salaün &path_beneath, 0)); 407e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 408e1199815SMickaël Salaün 409e1199815SMickaël Salaün /* Tests with a ruleset FD. */ 410e1199815SMickaël Salaün path_beneath.parent_fd = ruleset_fd; 411e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 412e1199815SMickaël Salaün &path_beneath, 0)); 413e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 414e1199815SMickaël Salaün 415e1199815SMickaël Salaün /* Checks unhandled allowed_access. */ 416371183faSMickaël Salaün path_beneath.parent_fd = 417371183faSMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 418e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 419e1199815SMickaël Salaün 420e1199815SMickaël Salaün /* Test with legitimate values. */ 421e1199815SMickaël Salaün path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 422e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 423e1199815SMickaël Salaün &path_beneath, 0)); 424e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 425e1199815SMickaël Salaün path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 426e1199815SMickaël Salaün 42755e55920SMickaël Salaün /* Tests with denied-by-default access right. */ 42855e55920SMickaël Salaün path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 42955e55920SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 43055e55920SMickaël Salaün &path_beneath, 0)); 43155e55920SMickaël Salaün ASSERT_EQ(EINVAL, errno); 43255e55920SMickaël Salaün path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 43355e55920SMickaël Salaün 434e1199815SMickaël Salaün /* Test with unknown (64-bits) value. */ 435e1199815SMickaël Salaün path_beneath.allowed_access |= (1ULL << 60); 436e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 437e1199815SMickaël Salaün &path_beneath, 0)); 438e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 439e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 440e1199815SMickaël Salaün 441e1199815SMickaël Salaün /* Test with no access. */ 442e1199815SMickaël Salaün path_beneath.allowed_access = 0; 443e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 444e1199815SMickaël Salaün &path_beneath, 0)); 445e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 446e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 447e1199815SMickaël Salaün 448e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 449e1199815SMickaël Salaün 450e1199815SMickaël Salaün /* Enforces the ruleset. */ 451e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 452e1199815SMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 453e1199815SMickaël Salaün 454e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 455e1199815SMickaël Salaün } 456e1199815SMickaël Salaün 4574598d9abSMickaël Salaün /* clang-format off */ 4584598d9abSMickaël Salaün 459e1199815SMickaël Salaün #define ACCESS_FILE ( \ 460e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | \ 461e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE | \ 462b9f5ce27SGünther Noack LANDLOCK_ACCESS_FS_READ_FILE | \ 463b9f5ce27SGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE) 464e1199815SMickaël Salaün 465b9f5ce27SGünther Noack #define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE 466e1199815SMickaël Salaün 467e1199815SMickaël Salaün #define ACCESS_ALL ( \ 468e1199815SMickaël Salaün ACCESS_FILE | \ 469e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR | \ 470e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 471e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 472e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 473e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_DIR | \ 474e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | \ 475e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 476e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 477e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 478b91c3e4eSMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SYM | \ 479b9f5ce27SGünther Noack LANDLOCK_ACCESS_FS_REFER) 480e1199815SMickaël Salaün 4814598d9abSMickaël Salaün /* clang-format on */ 4824598d9abSMickaël Salaün 483d18955d0SMickaël Salaün TEST_F_FORK(layout1, file_and_dir_access_rights) 484e1199815SMickaël Salaün { 485e1199815SMickaël Salaün __u64 access; 486e1199815SMickaël Salaün int err; 487d18955d0SMickaël Salaün struct landlock_path_beneath_attr path_beneath_file = {}, 488d18955d0SMickaël Salaün path_beneath_dir = {}; 489e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 490e1199815SMickaël Salaün .handled_access_fs = ACCESS_ALL, 491e1199815SMickaël Salaün }; 492371183faSMickaël Salaün const int ruleset_fd = 493371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 494e1199815SMickaël Salaün 495e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 496e1199815SMickaël Salaün 497e1199815SMickaël Salaün /* Tests access rights for files. */ 498d18955d0SMickaël Salaün path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 499d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_file.parent_fd); 500d18955d0SMickaël Salaün 501d18955d0SMickaël Salaün /* Tests access rights for directories. */ 502d18955d0SMickaël Salaün path_beneath_dir.parent_fd = 503d18955d0SMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 504d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_dir.parent_fd); 505d18955d0SMickaël Salaün 506e1199815SMickaël Salaün for (access = 1; access <= ACCESS_LAST; access <<= 1) { 507d18955d0SMickaël Salaün path_beneath_dir.allowed_access = access; 508d18955d0SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 509d18955d0SMickaël Salaün LANDLOCK_RULE_PATH_BENEATH, 510d18955d0SMickaël Salaün &path_beneath_dir, 0)); 511d18955d0SMickaël Salaün 512d18955d0SMickaël Salaün path_beneath_file.allowed_access = access; 513e1199815SMickaël Salaün err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 514d18955d0SMickaël Salaün &path_beneath_file, 0); 515d18955d0SMickaël Salaün if (access & ACCESS_FILE) { 516e1199815SMickaël Salaün ASSERT_EQ(0, err); 517e1199815SMickaël Salaün } else { 518e1199815SMickaël Salaün ASSERT_EQ(-1, err); 519e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 520e1199815SMickaël Salaün } 521e1199815SMickaël Salaün } 522d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 523d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 524d18955d0SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 525e1199815SMickaël Salaün } 526e1199815SMickaël Salaün 527*592efeb4SMickaël Salaün TEST_F_FORK(layout0, unknown_access_rights) 528c56b3bf5SMickaël Salaün { 529c56b3bf5SMickaël Salaün __u64 access_mask; 530c56b3bf5SMickaël Salaün 531c56b3bf5SMickaël Salaün for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 532c56b3bf5SMickaël Salaün access_mask >>= 1) { 533c56b3bf5SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 534c56b3bf5SMickaël Salaün .handled_access_fs = access_mask, 535c56b3bf5SMickaël Salaün }; 536c56b3bf5SMickaël Salaün 537c56b3bf5SMickaël Salaün ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 538c56b3bf5SMickaël Salaün sizeof(ruleset_attr), 0)); 539c56b3bf5SMickaël Salaün ASSERT_EQ(EINVAL, errno); 540c56b3bf5SMickaël Salaün } 541c56b3bf5SMickaël Salaün } 542c56b3bf5SMickaël Salaün 543e1199815SMickaël Salaün static void add_path_beneath(struct __test_metadata *const _metadata, 544e1199815SMickaël Salaün const int ruleset_fd, const __u64 allowed_access, 545e1199815SMickaël Salaün const char *const path) 546e1199815SMickaël Salaün { 547e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath = { 548e1199815SMickaël Salaün .allowed_access = allowed_access, 549e1199815SMickaël Salaün }; 550e1199815SMickaël Salaün 551e1199815SMickaël Salaün path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 552371183faSMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd) 553371183faSMickaël Salaün { 554e1199815SMickaël Salaün TH_LOG("Failed to open directory \"%s\": %s", path, 555e1199815SMickaël Salaün strerror(errno)); 556e1199815SMickaël Salaün } 557e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 558371183faSMickaël Salaün &path_beneath, 0)) 559371183faSMickaël Salaün { 560e1199815SMickaël Salaün TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 561e1199815SMickaël Salaün strerror(errno)); 562e1199815SMickaël Salaün } 563e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 564e1199815SMickaël Salaün } 565e1199815SMickaël Salaün 566e1199815SMickaël Salaün struct rule { 567e1199815SMickaël Salaün const char *path; 568e1199815SMickaël Salaün __u64 access; 569e1199815SMickaël Salaün }; 570e1199815SMickaël Salaün 5714598d9abSMickaël Salaün /* clang-format off */ 5724598d9abSMickaël Salaün 573e1199815SMickaël Salaün #define ACCESS_RO ( \ 574e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | \ 575e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR) 576e1199815SMickaël Salaün 577e1199815SMickaël Salaün #define ACCESS_RW ( \ 578e1199815SMickaël Salaün ACCESS_RO | \ 579e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE) 580e1199815SMickaël Salaün 5814598d9abSMickaël Salaün /* clang-format on */ 5824598d9abSMickaël Salaün 583e1199815SMickaël Salaün static int create_ruleset(struct __test_metadata *const _metadata, 584371183faSMickaël Salaün const __u64 handled_access_fs, 585371183faSMickaël Salaün const struct rule rules[]) 586e1199815SMickaël Salaün { 587e1199815SMickaël Salaün int ruleset_fd, i; 588e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 589e1199815SMickaël Salaün .handled_access_fs = handled_access_fs, 590e1199815SMickaël Salaün }; 591e1199815SMickaël Salaün 592371183faSMickaël Salaün ASSERT_NE(NULL, rules) 593371183faSMickaël Salaün { 594e1199815SMickaël Salaün TH_LOG("No rule list"); 595e1199815SMickaël Salaün } 596371183faSMickaël Salaün ASSERT_NE(NULL, rules[0].path) 597371183faSMickaël Salaün { 598e1199815SMickaël Salaün TH_LOG("Empty rule list"); 599e1199815SMickaël Salaün } 600e1199815SMickaël Salaün 601371183faSMickaël Salaün ruleset_fd = 602371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 603371183faSMickaël Salaün ASSERT_LE(0, ruleset_fd) 604371183faSMickaël Salaün { 605e1199815SMickaël Salaün TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 606e1199815SMickaël Salaün } 607e1199815SMickaël Salaün 608e1199815SMickaël Salaün for (i = 0; rules[i].path; i++) { 609e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, rules[i].access, 610e1199815SMickaël Salaün rules[i].path); 611e1199815SMickaël Salaün } 612e1199815SMickaël Salaün return ruleset_fd; 613e1199815SMickaël Salaün } 614e1199815SMickaël Salaün 615e1199815SMickaël Salaün static void enforce_ruleset(struct __test_metadata *const _metadata, 616e1199815SMickaël Salaün const int ruleset_fd) 617e1199815SMickaël Salaün { 618e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 619371183faSMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 620371183faSMickaël Salaün { 621e1199815SMickaël Salaün TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 622e1199815SMickaël Salaün } 623e1199815SMickaël Salaün } 624e1199815SMickaël Salaün 625*592efeb4SMickaël Salaün TEST_F_FORK(layout0, proc_nsfs) 626e1199815SMickaël Salaün { 627e1199815SMickaël Salaün const struct rule rules[] = { 628e1199815SMickaël Salaün { 629e1199815SMickaël Salaün .path = "/dev/null", 630e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 631e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 632e1199815SMickaël Salaün }, 633135464f9SMickaël Salaün {}, 634e1199815SMickaël Salaün }; 635e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath; 636371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 637371183faSMickaël Salaün _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 638371183faSMickaël Salaün rules); 639e1199815SMickaël Salaün 640e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 641e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 642e1199815SMickaël Salaün 643e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 644e1199815SMickaël Salaün 645e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 646e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 647e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 648e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 649e1199815SMickaël Salaün 650e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 651e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 652e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 653e1199815SMickaël Salaün /* 654e1199815SMickaël Salaün * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 655e1199815SMickaël Salaün * disconnected path. Such path cannot be identified and must then be 656e1199815SMickaël Salaün * allowed. 657e1199815SMickaël Salaün */ 658e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 659e1199815SMickaël Salaün 660e1199815SMickaël Salaün /* 661e1199815SMickaël Salaün * Checks that it is not possible to add nsfs-like filesystem 662e1199815SMickaël Salaün * references to a ruleset. 663e1199815SMickaël Salaün */ 664e1199815SMickaël Salaün path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 665e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 666e1199815SMickaël Salaün path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 667e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 668e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 669e1199815SMickaël Salaün &path_beneath, 0)); 670e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 671e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 672e1199815SMickaël Salaün } 673e1199815SMickaël Salaün 674*592efeb4SMickaël Salaün TEST_F_FORK(layout0, unpriv) 675371183faSMickaël Salaün { 676e1199815SMickaël Salaün const struct rule rules[] = { 677e1199815SMickaël Salaün { 678*592efeb4SMickaël Salaün .path = TMP_DIR, 679e1199815SMickaël Salaün .access = ACCESS_RO, 680e1199815SMickaël Salaün }, 681135464f9SMickaël Salaün {}, 682e1199815SMickaël Salaün }; 683e1199815SMickaël Salaün int ruleset_fd; 684e1199815SMickaël Salaün 685e1199815SMickaël Salaün drop_caps(_metadata); 686e1199815SMickaël Salaün 687e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 688e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 689e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 690e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 691e1199815SMickaël Salaün 692e1199815SMickaël Salaün /* enforce_ruleset() calls prctl(no_new_privs). */ 693e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 694e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 695e1199815SMickaël Salaün } 696e1199815SMickaël Salaün 697e1199815SMickaël Salaün TEST_F_FORK(layout1, effective_access) 698e1199815SMickaël Salaün { 699e1199815SMickaël Salaün const struct rule rules[] = { 700e1199815SMickaël Salaün { 701e1199815SMickaël Salaün .path = dir_s1d2, 702e1199815SMickaël Salaün .access = ACCESS_RO, 703e1199815SMickaël Salaün }, 704e1199815SMickaël Salaün { 705e1199815SMickaël Salaün .path = file1_s2d2, 706e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 707e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 708e1199815SMickaël Salaün }, 709135464f9SMickaël Salaün {}, 710e1199815SMickaël Salaün }; 711e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 712e1199815SMickaël Salaün char buf; 713e1199815SMickaël Salaün int reg_fd; 714e1199815SMickaël Salaün 715e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 716e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 717e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 718e1199815SMickaël Salaün 719d1788ad9SMickaël Salaün /* Tests on a directory (with or without O_PATH). */ 720e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 721d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 722e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 723d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 724e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 725d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 726d1788ad9SMickaël Salaün 727e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 728e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 729e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 730e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 731e1199815SMickaël Salaün 732d1788ad9SMickaël Salaün /* Tests on a file (with or without O_PATH). */ 733e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 734d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 735d1788ad9SMickaël Salaün 736e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 737e1199815SMickaël Salaün 738e1199815SMickaël Salaün /* Checks effective read and write actions. */ 739e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 740e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 741e1199815SMickaël Salaün ASSERT_EQ(1, write(reg_fd, ".", 1)); 742e1199815SMickaël Salaün ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 743e1199815SMickaël Salaün ASSERT_EQ(1, read(reg_fd, &buf, 1)); 744e1199815SMickaël Salaün ASSERT_EQ('.', buf); 745e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 746e1199815SMickaël Salaün 747e1199815SMickaël Salaün /* Just in case, double-checks effective actions. */ 748e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 749e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 750e1199815SMickaël Salaün ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 751e1199815SMickaël Salaün ASSERT_EQ(EBADF, errno); 752e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 753e1199815SMickaël Salaün } 754e1199815SMickaël Salaün 755e1199815SMickaël Salaün TEST_F_FORK(layout1, unhandled_access) 756e1199815SMickaël Salaün { 757e1199815SMickaël Salaün const struct rule rules[] = { 758e1199815SMickaël Salaün { 759e1199815SMickaël Salaün .path = dir_s1d2, 760e1199815SMickaël Salaün .access = ACCESS_RO, 761e1199815SMickaël Salaün }, 762135464f9SMickaël Salaün {}, 763e1199815SMickaël Salaün }; 764e1199815SMickaël Salaün /* Here, we only handle read accesses, not write accesses. */ 765e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 766e1199815SMickaël Salaün 767e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 768e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 769e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 770e1199815SMickaël Salaün 771e1199815SMickaël Salaün /* 772e1199815SMickaël Salaün * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 773e1199815SMickaël Salaün * opening for write-only should be allowed, but not read-write. 774e1199815SMickaël Salaün */ 775e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 776e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 777e1199815SMickaël Salaün 778e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 779e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 780e1199815SMickaël Salaün } 781e1199815SMickaël Salaün 782e1199815SMickaël Salaün TEST_F_FORK(layout1, ruleset_overlap) 783e1199815SMickaël Salaün { 784e1199815SMickaël Salaün const struct rule rules[] = { 785e1199815SMickaël Salaün /* These rules should be ORed among them. */ 786e1199815SMickaël Salaün { 787e1199815SMickaël Salaün .path = dir_s1d2, 788e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 789e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 790e1199815SMickaël Salaün }, 791e1199815SMickaël Salaün { 792e1199815SMickaël Salaün .path = dir_s1d2, 793e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 794e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 795e1199815SMickaël Salaün }, 796135464f9SMickaël Salaün {}, 797e1199815SMickaël Salaün }; 798e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 799e1199815SMickaël Salaün 800e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 801e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 802e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 803e1199815SMickaël Salaün 804e1199815SMickaël Salaün /* Checks s1d1 hierarchy. */ 805e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 806e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 807e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 808e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 809e1199815SMickaël Salaün 810e1199815SMickaël Salaün /* Checks s1d2 hierarchy. */ 811e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 812e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 813e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 814e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 815e1199815SMickaël Salaün 816e1199815SMickaël Salaün /* Checks s1d3 hierarchy. */ 817e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 818e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 819e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 820e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 821e1199815SMickaël Salaün } 822e1199815SMickaël Salaün 8238ba0005fSMickaël Salaün TEST_F_FORK(layout1, layer_rule_unions) 8248ba0005fSMickaël Salaün { 8258ba0005fSMickaël Salaün const struct rule layer1[] = { 8268ba0005fSMickaël Salaün { 8278ba0005fSMickaël Salaün .path = dir_s1d2, 8288ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 8298ba0005fSMickaël Salaün }, 8308ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 8318ba0005fSMickaël Salaün { 8328ba0005fSMickaël Salaün .path = dir_s1d3, 8338ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 8348ba0005fSMickaël Salaün }, 8358ba0005fSMickaël Salaün {}, 8368ba0005fSMickaël Salaün }; 8378ba0005fSMickaël Salaün const struct rule layer2[] = { 8388ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 8398ba0005fSMickaël Salaün { 8408ba0005fSMickaël Salaün .path = dir_s1d2, 8418ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 8428ba0005fSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 8438ba0005fSMickaël Salaün }, 8448ba0005fSMickaël Salaün {}, 8458ba0005fSMickaël Salaün }; 8468ba0005fSMickaël Salaün const struct rule layer3[] = { 8478ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 8488ba0005fSMickaël Salaün { 8498ba0005fSMickaël Salaün .path = dir_s1d2, 8508ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 8518ba0005fSMickaël Salaün }, 8528ba0005fSMickaël Salaün {}, 8538ba0005fSMickaël Salaün }; 8548ba0005fSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1); 8558ba0005fSMickaël Salaün 8568ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 8578ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 8588ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 8598ba0005fSMickaël Salaün 8608ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer1. */ 8618ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 8628ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 8638ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 8648ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8658ba0005fSMickaël Salaün 8668ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer1. */ 8678ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 8688ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 8698ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 8708ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8718ba0005fSMickaël Salaün 8728ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer1. */ 8738ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 8748ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 8758ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 8768ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 8778ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8788ba0005fSMickaël Salaün 8798ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 8808ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2); 8818ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 8828ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 8838ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 8848ba0005fSMickaël Salaün 8858ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer2. */ 8868ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 8878ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 8888ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 8898ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8908ba0005fSMickaël Salaün 8918ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer2. */ 8928ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 8938ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 8948ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 8958ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 8968ba0005fSMickaël Salaün 8978ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer2. */ 8988ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 8998ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 9008ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 9018ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 9028ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9038ba0005fSMickaël Salaün 9048ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 9058ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3); 9068ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 9078ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 9088ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 9098ba0005fSMickaël Salaün 9108ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer3. */ 9118ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 9128ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 9138ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 9148ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9158ba0005fSMickaël Salaün 9168ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer3. */ 9178ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 9188ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 9198ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 9208ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9218ba0005fSMickaël Salaün 9228ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer3. */ 9238ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 9248ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 9258ba0005fSMickaël Salaün /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 9268ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 9278ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9288ba0005fSMickaël Salaün } 9298ba0005fSMickaël Salaün 930e1199815SMickaël Salaün TEST_F_FORK(layout1, non_overlapping_accesses) 931e1199815SMickaël Salaün { 932e1199815SMickaël Salaün const struct rule layer1[] = { 933e1199815SMickaël Salaün { 934e1199815SMickaël Salaün .path = dir_s1d2, 935e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 936e1199815SMickaël Salaün }, 937135464f9SMickaël Salaün {}, 938e1199815SMickaël Salaün }; 939e1199815SMickaël Salaün const struct rule layer2[] = { 940e1199815SMickaël Salaün { 941e1199815SMickaël Salaün .path = dir_s1d3, 942e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 943e1199815SMickaël Salaün }, 944135464f9SMickaël Salaün {}, 945e1199815SMickaël Salaün }; 946e1199815SMickaël Salaün int ruleset_fd; 947e1199815SMickaël Salaün 948e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 949e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 950e1199815SMickaël Salaün 951371183faSMickaël Salaün ruleset_fd = 952371183faSMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 953e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 954e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 955e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 956e1199815SMickaël Salaün 957e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 958e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 959e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 960e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 961e1199815SMickaël Salaün 962e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 963e1199815SMickaël Salaün layer2); 964e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 965e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 966e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 967e1199815SMickaël Salaün 968e1199815SMickaël Salaün /* Unchanged accesses for file creation. */ 969e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 970e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 971e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 972e1199815SMickaël Salaün 973e1199815SMickaël Salaün /* Checks file removing. */ 974e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d2)); 975e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 976e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 977e1199815SMickaël Salaün } 978e1199815SMickaël Salaün 979e1199815SMickaël Salaün TEST_F_FORK(layout1, interleaved_masked_accesses) 980e1199815SMickaël Salaün { 981e1199815SMickaël Salaün /* 982e1199815SMickaël Salaün * Checks overly restrictive rules: 983e1199815SMickaël Salaün * layer 1: allows R s1d1/s1d2/s1d3/file1 984e1199815SMickaël Salaün * layer 2: allows RW s1d1/s1d2/s1d3 985e1199815SMickaël Salaün * allows W s1d1/s1d2 986e1199815SMickaël Salaün * denies R s1d1/s1d2 987e1199815SMickaël Salaün * layer 3: allows R s1d1 988e1199815SMickaël Salaün * layer 4: allows R s1d1/s1d2 989e1199815SMickaël Salaün * denies W s1d1/s1d2 990e1199815SMickaël Salaün * layer 5: allows R s1d1/s1d2 991e1199815SMickaël Salaün * layer 6: allows X ---- 992e1199815SMickaël Salaün * layer 7: allows W s1d1/s1d2 993e1199815SMickaël Salaün * denies R s1d1/s1d2 994e1199815SMickaël Salaün */ 995e1199815SMickaël Salaün const struct rule layer1_read[] = { 996e1199815SMickaël Salaün /* Allows read access to file1_s1d3 with the first layer. */ 997e1199815SMickaël Salaün { 998e1199815SMickaël Salaün .path = file1_s1d3, 999e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1000e1199815SMickaël Salaün }, 1001135464f9SMickaël Salaün {}, 1002e1199815SMickaël Salaün }; 1003e1199815SMickaël Salaün /* First rule with write restrictions. */ 1004e1199815SMickaël Salaün const struct rule layer2_read_write[] = { 1005e1199815SMickaël Salaün /* Start by granting read-write access via its parent directory... */ 1006e1199815SMickaël Salaün { 1007e1199815SMickaël Salaün .path = dir_s1d3, 1008e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 1009e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1010e1199815SMickaël Salaün }, 1011e1199815SMickaël Salaün /* ...but also denies read access via its grandparent directory. */ 1012e1199815SMickaël Salaün { 1013e1199815SMickaël Salaün .path = dir_s1d2, 1014e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1015e1199815SMickaël Salaün }, 1016135464f9SMickaël Salaün {}, 1017e1199815SMickaël Salaün }; 1018e1199815SMickaël Salaün const struct rule layer3_read[] = { 1019e1199815SMickaël Salaün /* Allows read access via its great-grandparent directory. */ 1020e1199815SMickaël Salaün { 1021e1199815SMickaël Salaün .path = dir_s1d1, 1022e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1023e1199815SMickaël Salaün }, 1024135464f9SMickaël Salaün {}, 1025e1199815SMickaël Salaün }; 1026e1199815SMickaël Salaün const struct rule layer4_read_write[] = { 1027e1199815SMickaël Salaün /* 1028e1199815SMickaël Salaün * Try to confuse the deny access by denying write (but not 1029e1199815SMickaël Salaün * read) access via its grandparent directory. 1030e1199815SMickaël Salaün */ 1031e1199815SMickaël Salaün { 1032e1199815SMickaël Salaün .path = dir_s1d2, 1033e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1034e1199815SMickaël Salaün }, 1035135464f9SMickaël Salaün {}, 1036e1199815SMickaël Salaün }; 1037e1199815SMickaël Salaün const struct rule layer5_read[] = { 1038e1199815SMickaël Salaün /* 1039e1199815SMickaël Salaün * Try to override layer2's deny read access by explicitly 1040e1199815SMickaël Salaün * allowing read access via file1_s1d3's grandparent. 1041e1199815SMickaël Salaün */ 1042e1199815SMickaël Salaün { 1043e1199815SMickaël Salaün .path = dir_s1d2, 1044e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1045e1199815SMickaël Salaün }, 1046135464f9SMickaël Salaün {}, 1047e1199815SMickaël Salaün }; 1048e1199815SMickaël Salaün const struct rule layer6_execute[] = { 1049e1199815SMickaël Salaün /* 1050e1199815SMickaël Salaün * Restricts an unrelated file hierarchy with a new access 1051e1199815SMickaël Salaün * (non-overlapping) type. 1052e1199815SMickaël Salaün */ 1053e1199815SMickaël Salaün { 1054e1199815SMickaël Salaün .path = dir_s2d1, 1055e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 1056e1199815SMickaël Salaün }, 1057135464f9SMickaël Salaün {}, 1058e1199815SMickaël Salaün }; 1059e1199815SMickaël Salaün const struct rule layer7_read_write[] = { 1060e1199815SMickaël Salaün /* 1061e1199815SMickaël Salaün * Finally, denies read access to file1_s1d3 via its 1062e1199815SMickaël Salaün * grandparent. 1063e1199815SMickaël Salaün */ 1064e1199815SMickaël Salaün { 1065e1199815SMickaël Salaün .path = dir_s1d2, 1066e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1067e1199815SMickaël Salaün }, 1068135464f9SMickaël Salaün {}, 1069e1199815SMickaël Salaün }; 1070e1199815SMickaël Salaün int ruleset_fd; 1071e1199815SMickaël Salaün 1072e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1073e1199815SMickaël Salaün layer1_read); 1074e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1075e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1076e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1077e1199815SMickaël Salaün 1078e1199815SMickaël Salaün /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1079e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1080e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1081e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1082e1199815SMickaël Salaün 1083371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1084371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1085371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1086371183faSMickaël Salaün layer2_read_write); 1087e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1088e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1089e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1090e1199815SMickaël Salaün 1091e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 2. */ 1092e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1093e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1094e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1095e1199815SMickaël Salaün 1096e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1097e1199815SMickaël Salaün layer3_read); 1098e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1099e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1100e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1101e1199815SMickaël Salaün 1102e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 3. */ 1103e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1104e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1105e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1106e1199815SMickaël Salaün 1107e1199815SMickaël Salaün /* This time, denies write access for the file hierarchy. */ 1108371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1109371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1110371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1111371183faSMickaël Salaün layer4_read_write); 1112e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1113e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1114e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1115e1199815SMickaël Salaün 1116e1199815SMickaël Salaün /* 1117e1199815SMickaël Salaün * Checks that the only change with layer 4 is that write access is 1118e1199815SMickaël Salaün * denied. 1119e1199815SMickaël Salaün */ 1120e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1121e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1122e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1123e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1124e1199815SMickaël Salaün 1125e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1126e1199815SMickaël Salaün layer5_read); 1127e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1128e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1129e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1130e1199815SMickaël Salaün 1131e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 5. */ 1132e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1133e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1134e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1135e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1136e1199815SMickaël Salaün 1137e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 1138e1199815SMickaël Salaün layer6_execute); 1139e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1140e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1141e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1142e1199815SMickaël Salaün 1143e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 6. */ 1144e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1145e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1146e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1147e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1148e1199815SMickaël Salaün 1149371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1150371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1151371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1152371183faSMickaël Salaün layer7_read_write); 1153e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1154e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1155e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1156e1199815SMickaël Salaün 1157e1199815SMickaël Salaün /* Checks read access is now denied with layer 7. */ 1158e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1159e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1160e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1161e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1162e1199815SMickaël Salaün } 1163e1199815SMickaël Salaün 1164e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_subset) 1165e1199815SMickaël Salaün { 1166e1199815SMickaël Salaün const struct rule rules[] = { 1167e1199815SMickaël Salaün { 1168e1199815SMickaël Salaün .path = dir_s1d2, 1169e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 1170e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1171e1199815SMickaël Salaün }, 1172135464f9SMickaël Salaün {}, 1173e1199815SMickaël Salaün }; 1174e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1175e1199815SMickaël Salaün 1176e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1177e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1178e1199815SMickaël Salaün 1179e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1180e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1181e1199815SMickaël Salaün 1182e1199815SMickaël Salaün /* Write access is forbidden. */ 1183e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1184e1199815SMickaël Salaün /* Readdir access is allowed. */ 1185e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1186e1199815SMickaël Salaün 1187e1199815SMickaël Salaün /* Write access is forbidden. */ 1188e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1189e1199815SMickaël Salaün /* Readdir access is allowed. */ 1190e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1191e1199815SMickaël Salaün 1192e1199815SMickaël Salaün /* 1193e1199815SMickaël Salaün * Tests shared rule extension: the following rules should not grant 1194e1199815SMickaël Salaün * any new access, only remove some. Once enforced, these rules are 1195e1199815SMickaël Salaün * ANDed with the previous ones. 1196e1199815SMickaël Salaün */ 1197e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1198e1199815SMickaël Salaün dir_s1d2); 1199e1199815SMickaël Salaün /* 1200e1199815SMickaël Salaün * According to ruleset_fd, dir_s1d2 should now have the 1201e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1202e1199815SMickaël Salaün * access rights (even if this directory is opened a second time). 1203e1199815SMickaël Salaün * However, when enforcing this updated ruleset, the ruleset tied to 1204e1199815SMickaël Salaün * the current process (i.e. its domain) will still only have the 1205e1199815SMickaël Salaün * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1206e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1207e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1208e1199815SMickaël Salaün * be a privilege escalation. 1209e1199815SMickaël Salaün */ 1210e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1211e1199815SMickaël Salaün 1212e1199815SMickaël Salaün /* Same tests and results as above. */ 1213e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1214e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1215e1199815SMickaël Salaün 1216e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1217e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1218e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1219e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1220e1199815SMickaël Salaün 1221e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1222e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1223e1199815SMickaël Salaün /* Readdir access is still allowed. */ 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 * Try to get more privileges by adding new access rights to the parent 1228e1199815SMickaël Salaün * directory: dir_s1d1. 1229e1199815SMickaël Salaün */ 1230e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 1231e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1232e1199815SMickaël Salaün 1233e1199815SMickaël Salaün /* Same tests and results as above. */ 1234e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1235e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1236e1199815SMickaël Salaün 1237e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1238e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1239e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1240e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1241e1199815SMickaël Salaün 1242e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1243e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1244e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1245e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1246e1199815SMickaël Salaün 1247e1199815SMickaël Salaün /* 1248e1199815SMickaël Salaün * Now, dir_s1d3 get a new rule tied to it, only allowing 1249e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1250e1199815SMickaël Salaün * that there was no rule tied to it before. 1251e1199815SMickaël Salaün */ 1252e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1253e1199815SMickaël Salaün dir_s1d3); 1254e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1255e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1256e1199815SMickaël Salaün 1257e1199815SMickaël Salaün /* 1258e1199815SMickaël Salaün * Same tests and results as above, except for open(dir_s1d3) which is 1259e1199815SMickaël Salaün * now denied because the new rule mask the rule previously inherited 1260e1199815SMickaël Salaün * from dir_s1d2. 1261e1199815SMickaël Salaün */ 1262e1199815SMickaël Salaün 1263e1199815SMickaël Salaün /* Same tests and results as above. */ 1264e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1265e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1266e1199815SMickaël Salaün 1267e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1268e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1269e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1270e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1271e1199815SMickaël Salaün 1272e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1273e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1274e1199815SMickaël Salaün /* 1275e1199815SMickaël Salaün * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1276e1199815SMickaël Salaün * the same layer. 1277e1199815SMickaël Salaün */ 1278e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1279e1199815SMickaël Salaün } 1280e1199815SMickaël Salaün 1281e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_superset) 1282e1199815SMickaël Salaün { 1283e1199815SMickaël Salaün const struct rule rules[] = { 1284e1199815SMickaël Salaün { 1285e1199815SMickaël Salaün .path = dir_s1d3, 1286e1199815SMickaël Salaün .access = ACCESS_RO, 1287e1199815SMickaël Salaün }, 1288135464f9SMickaël Salaün {}, 1289e1199815SMickaël Salaün }; 1290e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1291e1199815SMickaël Salaün 1292e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1293e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1294e1199815SMickaël Salaün 1295e1199815SMickaël Salaün /* Readdir access is denied for dir_s1d2. */ 1296e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1297e1199815SMickaël Salaün /* Readdir access is allowed for dir_s1d3. */ 1298e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1299e1199815SMickaël Salaün /* File access is allowed for file1_s1d3. */ 1300e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1301e1199815SMickaël Salaün 1302e1199815SMickaël Salaün /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1303371183faSMickaël Salaün add_path_beneath(_metadata, ruleset_fd, 1304371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1305371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1306371183faSMickaël Salaün dir_s1d2); 1307e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1308e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1309e1199815SMickaël Salaün 1310e1199815SMickaël Salaün /* Readdir access is still denied for dir_s1d2. */ 1311e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1312e1199815SMickaël Salaün /* Readdir access is still allowed for dir_s1d3. */ 1313e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1314e1199815SMickaël Salaün /* File access is still allowed for file1_s1d3. */ 1315e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1316e1199815SMickaël Salaün } 1317e1199815SMickaël Salaün 1318*592efeb4SMickaël Salaün TEST_F_FORK(layout0, max_layers) 1319e1199815SMickaël Salaün { 1320e1199815SMickaël Salaün int i, err; 1321e1199815SMickaël Salaün const struct rule rules[] = { 1322e1199815SMickaël Salaün { 1323*592efeb4SMickaël Salaün .path = TMP_DIR, 1324e1199815SMickaël Salaün .access = ACCESS_RO, 1325e1199815SMickaël Salaün }, 1326135464f9SMickaël Salaün {}, 1327e1199815SMickaël Salaün }; 1328e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1329e1199815SMickaël Salaün 1330e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 133175c542d6SMickaël Salaün for (i = 0; i < 16; i++) 1332e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1333e1199815SMickaël Salaün 1334e1199815SMickaël Salaün for (i = 0; i < 2; i++) { 1335e1199815SMickaël Salaün err = landlock_restrict_self(ruleset_fd, 0); 1336e1199815SMickaël Salaün ASSERT_EQ(-1, err); 1337e1199815SMickaël Salaün ASSERT_EQ(E2BIG, errno); 1338e1199815SMickaël Salaün } 1339e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1340e1199815SMickaël Salaün } 1341e1199815SMickaël Salaün 1342e1199815SMickaël Salaün TEST_F_FORK(layout1, empty_or_same_ruleset) 1343e1199815SMickaël Salaün { 1344e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = {}; 1345e1199815SMickaël Salaün int ruleset_fd; 1346e1199815SMickaël Salaün 1347e1199815SMickaël Salaün /* Tests empty handled_access_fs. */ 1348371183faSMickaël Salaün ruleset_fd = 1349371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1350e1199815SMickaël Salaün ASSERT_LE(-1, ruleset_fd); 1351e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 1352e1199815SMickaël Salaün 1353e1199815SMickaël Salaün /* Enforces policy which deny read access to all files. */ 1354e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1355371183faSMickaël Salaün ruleset_fd = 1356371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1357e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1358e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1359e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1360e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1361e1199815SMickaël Salaün 1362e1199815SMickaël Salaün /* Nests a policy which deny read access to all directories. */ 1363e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1364371183faSMickaël Salaün ruleset_fd = 1365371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1366e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1367e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1368e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1369e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1370e1199815SMickaël Salaün 1371e1199815SMickaël Salaün /* Enforces a second time with the same ruleset. */ 1372e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1373e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1374e1199815SMickaël Salaün } 1375e1199815SMickaël Salaün 1376e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_on_mountpoint) 1377e1199815SMickaël Salaün { 1378e1199815SMickaël Salaün const struct rule rules[] = { 1379e1199815SMickaël Salaün { 1380e1199815SMickaël Salaün .path = dir_s1d1, 1381e1199815SMickaël Salaün .access = ACCESS_RO, 1382e1199815SMickaël Salaün }, 1383e1199815SMickaël Salaün { 1384e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1385e1199815SMickaël Salaün .path = dir_s3d2, 1386e1199815SMickaël Salaün .access = ACCESS_RO, 1387e1199815SMickaël Salaün }, 1388135464f9SMickaël Salaün {}, 1389e1199815SMickaël Salaün }; 1390e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1391e1199815SMickaël Salaün 1392e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1393e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1394e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1395e1199815SMickaël Salaün 1396e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1397e1199815SMickaël Salaün 1398e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1399e1199815SMickaël Salaün 1400e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1401e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1402e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1403e1199815SMickaël Salaün } 1404e1199815SMickaël Salaün 1405e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_mountpoint) 1406e1199815SMickaël Salaün { 1407e1199815SMickaël Salaün const struct rule rules[] = { 1408e1199815SMickaël Salaün { 1409e1199815SMickaël Salaün .path = dir_s1d1, 1410e1199815SMickaël Salaün .access = ACCESS_RO, 1411e1199815SMickaël Salaün }, 1412e1199815SMickaël Salaün { 1413e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1414e1199815SMickaël Salaün .path = dir_s3d1, 1415e1199815SMickaël Salaün .access = ACCESS_RO, 1416e1199815SMickaël Salaün }, 1417135464f9SMickaël Salaün {}, 1418e1199815SMickaël Salaün }; 1419e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1420e1199815SMickaël Salaün 1421e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1422e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1423e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1424e1199815SMickaël Salaün 1425e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1426e1199815SMickaël Salaün 1427e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1428e1199815SMickaël Salaün 1429e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1430e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1431e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1432e1199815SMickaël Salaün } 1433e1199815SMickaël Salaün 1434e1199815SMickaël Salaün /* 1435e1199815SMickaël Salaün * This test verifies that we can apply a landlock rule on the root directory 1436e1199815SMickaël Salaün * (which might require special handling). 1437e1199815SMickaël Salaün */ 1438e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1439e1199815SMickaël Salaün { 1440e1199815SMickaël Salaün struct rule rules[] = { 1441e1199815SMickaël Salaün { 1442e1199815SMickaël Salaün .path = "/", 1443e1199815SMickaël Salaün .access = ACCESS_RO, 1444e1199815SMickaël Salaün }, 1445135464f9SMickaël Salaün {}, 1446e1199815SMickaël Salaün }; 1447e1199815SMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1448e1199815SMickaël Salaün 1449e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1450e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1451e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1452e1199815SMickaël Salaün 1453e1199815SMickaël Salaün /* Checks allowed access. */ 1454e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY)); 1455e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1456e1199815SMickaël Salaün 1457e1199815SMickaël Salaün rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1458e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1459e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1460e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1461e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1462e1199815SMickaël Salaün 1463e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1464e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1465e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1466e1199815SMickaël Salaün } 1467e1199815SMickaël Salaün 1468e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_deny) 1469e1199815SMickaël Salaün { 1470e1199815SMickaël Salaün const struct rule rules[] = { 1471e1199815SMickaël Salaün { 1472e1199815SMickaël Salaün .path = "/", 1473e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1474e1199815SMickaël Salaün }, 1475135464f9SMickaël Salaün {}, 1476e1199815SMickaël Salaün }; 1477e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1478e1199815SMickaël Salaün 1479e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1480e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1481e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1482e1199815SMickaël Salaün 1483e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1484e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1485e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1486e1199815SMickaël Salaün } 1487e1199815SMickaël Salaün 1488e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_inside_mount_ns) 1489e1199815SMickaël Salaün { 1490e1199815SMickaël Salaün const struct rule rules[] = { 1491e1199815SMickaël Salaün { 1492e1199815SMickaël Salaün .path = "s3d3", 1493e1199815SMickaël Salaün .access = ACCESS_RO, 1494e1199815SMickaël Salaün }, 1495135464f9SMickaël Salaün {}, 1496e1199815SMickaël Salaün }; 1497e1199815SMickaël Salaün int ruleset_fd; 1498e1199815SMickaël Salaün 1499e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 150087129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1501371183faSMickaël Salaün { 1502e1199815SMickaël Salaün TH_LOG("Failed to pivot root: %s", strerror(errno)); 1503e1199815SMickaël Salaün }; 1504e1199815SMickaël Salaün ASSERT_EQ(0, chdir("/")); 1505e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1506e1199815SMickaël Salaün 1507e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1508e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 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 ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1513e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1514e1199815SMickaël Salaün } 1515e1199815SMickaël Salaün 1516e1199815SMickaël Salaün TEST_F_FORK(layout1, mount_and_pivot) 1517e1199815SMickaël Salaün { 1518e1199815SMickaël Salaün const struct rule rules[] = { 1519e1199815SMickaël Salaün { 1520e1199815SMickaël Salaün .path = dir_s3d2, 1521e1199815SMickaël Salaün .access = ACCESS_RO, 1522e1199815SMickaël Salaün }, 1523135464f9SMickaël Salaün {}, 1524e1199815SMickaël Salaün }; 1525e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1526e1199815SMickaël Salaün 1527e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1528e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1529e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1530e1199815SMickaël Salaün 1531e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1532e1199815SMickaël Salaün ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1533e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 153487129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1535e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1536e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1537e1199815SMickaël Salaün } 1538e1199815SMickaël Salaün 1539e1199815SMickaël Salaün TEST_F_FORK(layout1, move_mount) 1540e1199815SMickaël Salaün { 1541e1199815SMickaël Salaün const struct rule rules[] = { 1542e1199815SMickaël Salaün { 1543e1199815SMickaël Salaün .path = dir_s3d2, 1544e1199815SMickaël Salaün .access = ACCESS_RO, 1545e1199815SMickaël Salaün }, 1546135464f9SMickaël Salaün {}, 1547e1199815SMickaël Salaün }; 1548e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1549e1199815SMickaël Salaün 1550e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1551e1199815SMickaël Salaün 1552e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 155387129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1554371183faSMickaël Salaün dir_s1d2, 0)) 1555371183faSMickaël Salaün { 1556e1199815SMickaël Salaün TH_LOG("Failed to move mount: %s", strerror(errno)); 1557e1199815SMickaël Salaün } 1558e1199815SMickaël Salaün 155987129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1560e1199815SMickaël Salaün dir_s3d2, 0)); 1561e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1562e1199815SMickaël Salaün 1563e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1564e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1565e1199815SMickaël Salaün 1566e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 156787129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1568e1199815SMickaël Salaün dir_s1d2, 0)); 1569e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1570e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1571e1199815SMickaël Salaün } 1572e1199815SMickaël Salaün 1573e1199815SMickaël Salaün TEST_F_FORK(layout1, release_inodes) 1574e1199815SMickaël Salaün { 1575e1199815SMickaël Salaün const struct rule rules[] = { 1576e1199815SMickaël Salaün { 1577e1199815SMickaël Salaün .path = dir_s1d1, 1578e1199815SMickaël Salaün .access = ACCESS_RO, 1579e1199815SMickaël Salaün }, 1580e1199815SMickaël Salaün { 1581e1199815SMickaël Salaün .path = dir_s3d2, 1582e1199815SMickaël Salaün .access = ACCESS_RO, 1583e1199815SMickaël Salaün }, 1584e1199815SMickaël Salaün { 1585e1199815SMickaël Salaün .path = dir_s3d3, 1586e1199815SMickaël Salaün .access = ACCESS_RO, 1587e1199815SMickaël Salaün }, 1588135464f9SMickaël Salaün {}, 1589e1199815SMickaël Salaün }; 1590e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1591e1199815SMickaël Salaün 1592e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1593e1199815SMickaël Salaün /* Unmount a file hierarchy while it is being used by a ruleset. */ 1594e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1595e1199815SMickaël Salaün ASSERT_EQ(0, umount(dir_s3d2)); 1596e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1597e1199815SMickaël Salaün 1598e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1599e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1600e1199815SMickaël Salaün 1601e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1602e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1603e1199815SMickaël Salaün /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1604e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1605e1199815SMickaël Salaün } 1606e1199815SMickaël Salaün 1607e1199815SMickaël Salaün enum relative_access { 1608e1199815SMickaël Salaün REL_OPEN, 1609e1199815SMickaël Salaün REL_CHDIR, 1610e1199815SMickaël Salaün REL_CHROOT_ONLY, 1611e1199815SMickaël Salaün REL_CHROOT_CHDIR, 1612e1199815SMickaël Salaün }; 1613e1199815SMickaël Salaün 1614e1199815SMickaël Salaün static void test_relative_path(struct __test_metadata *const _metadata, 1615e1199815SMickaël Salaün const enum relative_access rel) 1616e1199815SMickaël Salaün { 1617e1199815SMickaël Salaün /* 1618e1199815SMickaël Salaün * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1619e1199815SMickaël Salaün * is not a disconnected root directory). 1620e1199815SMickaël Salaün */ 1621e1199815SMickaël Salaün const struct rule layer1_base[] = { 1622e1199815SMickaël Salaün { 1623e1199815SMickaël Salaün .path = TMP_DIR, 1624e1199815SMickaël Salaün .access = ACCESS_RO, 1625e1199815SMickaël Salaün }, 1626135464f9SMickaël Salaün {}, 1627e1199815SMickaël Salaün }; 1628e1199815SMickaël Salaün const struct rule layer2_subs[] = { 1629e1199815SMickaël Salaün { 1630e1199815SMickaël Salaün .path = dir_s1d2, 1631e1199815SMickaël Salaün .access = ACCESS_RO, 1632e1199815SMickaël Salaün }, 1633e1199815SMickaël Salaün { 1634e1199815SMickaël Salaün .path = dir_s2d2, 1635e1199815SMickaël Salaün .access = ACCESS_RO, 1636e1199815SMickaël Salaün }, 1637135464f9SMickaël Salaün {}, 1638e1199815SMickaël Salaün }; 1639e1199815SMickaël Salaün int dirfd, ruleset_fd; 1640e1199815SMickaël Salaün 1641e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1642e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1643e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1644e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1645e1199815SMickaël Salaün 1646e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1647e1199815SMickaël Salaün 1648e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1649e1199815SMickaël Salaün switch (rel) { 1650e1199815SMickaël Salaün case REL_OPEN: 1651e1199815SMickaël Salaün case REL_CHDIR: 1652e1199815SMickaël Salaün break; 1653e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1654e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s2d2)); 1655e1199815SMickaël Salaün break; 1656e1199815SMickaël Salaün case REL_CHROOT_CHDIR: 1657e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1658e1199815SMickaël Salaün break; 1659e1199815SMickaël Salaün default: 1660e1199815SMickaël Salaün ASSERT_TRUE(false); 1661e1199815SMickaël Salaün return; 1662e1199815SMickaël Salaün } 1663e1199815SMickaël Salaün 1664e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_CHROOT); 1665e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1666e1199815SMickaël Salaün 1667e1199815SMickaël Salaün switch (rel) { 1668e1199815SMickaël Salaün case REL_OPEN: 1669e1199815SMickaël Salaün dirfd = open(dir_s1d2, O_DIRECTORY); 1670e1199815SMickaël Salaün ASSERT_LE(0, dirfd); 1671e1199815SMickaël Salaün break; 1672e1199815SMickaël Salaün case REL_CHDIR: 1673e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1674e1199815SMickaël Salaün dirfd = AT_FDCWD; 1675e1199815SMickaël Salaün break; 1676e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1677e1199815SMickaël Salaün /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1678371183faSMickaël Salaün ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 1679371183faSMickaël Salaün { 1680e1199815SMickaël Salaün TH_LOG("Failed to chroot: %s", strerror(errno)); 1681e1199815SMickaël Salaün } 1682e1199815SMickaël Salaün dirfd = AT_FDCWD; 1683e1199815SMickaël Salaün break; 1684e1199815SMickaël Salaün case REL_CHROOT_CHDIR: 1685e1199815SMickaël Salaün /* Do chroot into dir_s1d2. */ 1686371183faSMickaël Salaün ASSERT_EQ(0, chroot(".")) 1687371183faSMickaël Salaün { 1688e1199815SMickaël Salaün TH_LOG("Failed to chroot: %s", strerror(errno)); 1689e1199815SMickaël Salaün } 1690e1199815SMickaël Salaün dirfd = AT_FDCWD; 1691e1199815SMickaël Salaün break; 1692e1199815SMickaël Salaün } 1693e1199815SMickaël Salaün 1694e1199815SMickaël Salaün ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1695e1199815SMickaël Salaün test_open_rel(dirfd, "..", O_RDONLY)); 1696e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1697e1199815SMickaël Salaün 1698e1199815SMickaël Salaün if (rel == REL_CHROOT_ONLY) { 1699e1199815SMickaël Salaün /* The current directory is dir_s2d2. */ 1700e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1701e1199815SMickaël Salaün } else { 1702e1199815SMickaël Salaün /* The current directory is dir_s1d2. */ 1703e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1704e1199815SMickaël Salaün } 1705e1199815SMickaël Salaün 1706e1199815SMickaël Salaün if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1707e1199815SMickaël Salaün /* Checks the root dir_s1d2. */ 1708e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1709e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1710e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1711e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1712e1199815SMickaël Salaün } 1713e1199815SMickaël Salaün 1714e1199815SMickaël Salaün if (rel != REL_CHROOT_CHDIR) { 1715e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1716e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1717371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1718371183faSMickaël Salaün O_RDONLY)); 1719e1199815SMickaël Salaün 1720e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1721e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1722371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1723371183faSMickaël Salaün O_RDONLY)); 1724e1199815SMickaël Salaün } 1725e1199815SMickaël Salaün 1726e1199815SMickaël Salaün if (rel == REL_OPEN) 1727e1199815SMickaël Salaün ASSERT_EQ(0, close(dirfd)); 1728e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1729e1199815SMickaël Salaün } 1730e1199815SMickaël Salaün 1731e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_open) 1732e1199815SMickaël Salaün { 1733e1199815SMickaël Salaün test_relative_path(_metadata, REL_OPEN); 1734e1199815SMickaël Salaün } 1735e1199815SMickaël Salaün 1736e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chdir) 1737e1199815SMickaël Salaün { 1738e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHDIR); 1739e1199815SMickaël Salaün } 1740e1199815SMickaël Salaün 1741e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_only) 1742e1199815SMickaël Salaün { 1743e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_ONLY); 1744e1199815SMickaël Salaün } 1745e1199815SMickaël Salaün 1746e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_chdir) 1747e1199815SMickaël Salaün { 1748e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_CHDIR); 1749e1199815SMickaël Salaün } 1750e1199815SMickaël Salaün 1751e1199815SMickaël Salaün static void copy_binary(struct __test_metadata *const _metadata, 1752e1199815SMickaël Salaün const char *const dst_path) 1753e1199815SMickaël Salaün { 1754e1199815SMickaël Salaün int dst_fd, src_fd; 1755e1199815SMickaël Salaün struct stat statbuf; 1756e1199815SMickaël Salaün 1757e1199815SMickaël Salaün dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1758371183faSMickaël Salaün ASSERT_LE(0, dst_fd) 1759371183faSMickaël Salaün { 1760371183faSMickaël Salaün TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1761e1199815SMickaël Salaün } 1762e1199815SMickaël Salaün src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1763371183faSMickaël Salaün ASSERT_LE(0, src_fd) 1764371183faSMickaël Salaün { 1765e1199815SMickaël Salaün TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1766e1199815SMickaël Salaün strerror(errno)); 1767e1199815SMickaël Salaün } 1768e1199815SMickaël Salaün ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1769371183faSMickaël Salaün ASSERT_EQ(statbuf.st_size, 1770371183faSMickaël Salaün sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 1771e1199815SMickaël Salaün ASSERT_EQ(0, close(src_fd)); 1772e1199815SMickaël Salaün ASSERT_EQ(0, close(dst_fd)); 1773e1199815SMickaël Salaün } 1774e1199815SMickaël Salaün 1775371183faSMickaël Salaün static void test_execute(struct __test_metadata *const _metadata, const int err, 1776371183faSMickaël Salaün const char *const path) 1777e1199815SMickaël Salaün { 1778e1199815SMickaël Salaün int status; 1779e1199815SMickaël Salaün char *const argv[] = { (char *)path, NULL }; 1780e1199815SMickaël Salaün const pid_t child = fork(); 1781e1199815SMickaël Salaün 1782e1199815SMickaël Salaün ASSERT_LE(0, child); 1783e1199815SMickaël Salaün if (child == 0) { 1784371183faSMickaël Salaün ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 1785371183faSMickaël Salaün { 1786e1199815SMickaël Salaün TH_LOG("Failed to execute \"%s\": %s", path, 1787e1199815SMickaël Salaün strerror(errno)); 1788e1199815SMickaël Salaün }; 1789e1199815SMickaël Salaün ASSERT_EQ(err, errno); 1790e1199815SMickaël Salaün _exit(_metadata->passed ? 2 : 1); 1791e1199815SMickaël Salaün return; 1792e1199815SMickaël Salaün } 1793e1199815SMickaël Salaün ASSERT_EQ(child, waitpid(child, &status, 0)); 1794e1199815SMickaël Salaün ASSERT_EQ(1, WIFEXITED(status)); 1795371183faSMickaël Salaün ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 1796371183faSMickaël Salaün { 1797e1199815SMickaël Salaün TH_LOG("Unexpected return code for \"%s\": %s", path, 1798e1199815SMickaël Salaün strerror(errno)); 1799e1199815SMickaël Salaün }; 1800e1199815SMickaël Salaün } 1801e1199815SMickaël Salaün 1802e1199815SMickaël Salaün TEST_F_FORK(layout1, execute) 1803e1199815SMickaël Salaün { 1804e1199815SMickaël Salaün const struct rule rules[] = { 1805e1199815SMickaël Salaün { 1806e1199815SMickaël Salaün .path = dir_s1d2, 1807e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 1808e1199815SMickaël Salaün }, 1809135464f9SMickaël Salaün {}, 1810e1199815SMickaël Salaün }; 1811371183faSMickaël Salaün const int ruleset_fd = 1812371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1813e1199815SMickaël Salaün 1814e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1815e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d1); 1816e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d2); 1817e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d3); 1818e1199815SMickaël Salaün 1819e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1820e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1821e1199815SMickaël Salaün 1822e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1823e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1824e1199815SMickaël Salaün test_execute(_metadata, EACCES, file1_s1d1); 1825e1199815SMickaël Salaün 1826e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1827e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1828e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d2); 1829e1199815SMickaël Salaün 1830e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1831e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1832e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d3); 1833e1199815SMickaël Salaün } 1834e1199815SMickaël Salaün 1835e1199815SMickaël Salaün TEST_F_FORK(layout1, link) 1836e1199815SMickaël Salaün { 18376a1bdd4aSMickaël Salaün const struct rule layer1[] = { 1838e1199815SMickaël Salaün { 1839e1199815SMickaël Salaün .path = dir_s1d2, 1840e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1841e1199815SMickaël Salaün }, 1842135464f9SMickaël Salaün {}, 1843e1199815SMickaël Salaün }; 18446a1bdd4aSMickaël Salaün const struct rule layer2[] = { 18456a1bdd4aSMickaël Salaün { 18466a1bdd4aSMickaël Salaün .path = dir_s1d3, 18476a1bdd4aSMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 18486a1bdd4aSMickaël Salaün }, 18496a1bdd4aSMickaël Salaün {}, 18506a1bdd4aSMickaël Salaün }; 18516a1bdd4aSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 1852e1199815SMickaël Salaün 1853e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1854e1199815SMickaël Salaün 1855e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 1856e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1857e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1858e1199815SMickaël Salaün 1859e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1860e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1861e1199815SMickaël Salaün 1862e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1863e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 18646a1bdd4aSMickaël Salaün 1865e1199815SMickaël Salaün /* Denies linking because of reparenting. */ 1866e1199815SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1867e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1868e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1869e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 18706a1bdd4aSMickaël Salaün ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 18716a1bdd4aSMickaël Salaün ASSERT_EQ(EXDEV, errno); 1872e1199815SMickaël Salaün 1873e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1874e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 18756a1bdd4aSMickaël Salaün 18766a1bdd4aSMickaël Salaün /* Prepares for next unlinks. */ 18776a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 18786a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 18796a1bdd4aSMickaël Salaün 18806a1bdd4aSMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 18816a1bdd4aSMickaël Salaün ASSERT_LE(0, ruleset_fd); 18826a1bdd4aSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 18836a1bdd4aSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 18846a1bdd4aSMickaël Salaün 18856a1bdd4aSMickaël Salaün /* Checks that linkind doesn't require the ability to delete a file. */ 18866a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 18876a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1888e1199815SMickaël Salaün } 1889e1199815SMickaël Salaün 189055e55920SMickaël Salaün static int test_rename(const char *const oldpath, const char *const newpath) 189155e55920SMickaël Salaün { 189255e55920SMickaël Salaün if (rename(oldpath, newpath)) 189355e55920SMickaël Salaün return errno; 189455e55920SMickaël Salaün return 0; 189555e55920SMickaël Salaün } 189655e55920SMickaël Salaün 189755e55920SMickaël Salaün static int test_exchange(const char *const oldpath, const char *const newpath) 189855e55920SMickaël Salaün { 189955e55920SMickaël Salaün if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 190055e55920SMickaël Salaün return errno; 190155e55920SMickaël Salaün return 0; 190255e55920SMickaël Salaün } 190355e55920SMickaël Salaün 1904e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_file) 1905e1199815SMickaël Salaün { 1906e1199815SMickaël Salaün const struct rule rules[] = { 1907e1199815SMickaël Salaün { 1908e1199815SMickaël Salaün .path = dir_s1d3, 1909e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1910e1199815SMickaël Salaün }, 1911e1199815SMickaël Salaün { 1912e1199815SMickaël Salaün .path = dir_s2d2, 1913e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1914e1199815SMickaël Salaün }, 1915135464f9SMickaël Salaün {}, 1916e1199815SMickaël Salaün }; 1917371183faSMickaël Salaün const int ruleset_fd = 1918371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1919e1199815SMickaël Salaün 1920e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1921e1199815SMickaël Salaün 1922e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1923e1199815SMickaël Salaün 1924e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1925e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1926e1199815SMickaël Salaün 1927e1199815SMickaël Salaün /* 1928e1199815SMickaël Salaün * Tries to replace a file, from a directory that allows file removal, 1929e1199815SMickaël Salaün * but to a different directory (which also allows file removal). 1930e1199815SMickaël Salaün */ 1931e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1932e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1933e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 1934e1199815SMickaël Salaün RENAME_EXCHANGE)); 1935e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1936e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 1937e1199815SMickaël Salaün RENAME_EXCHANGE)); 1938e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1939e1199815SMickaël Salaün 1940e1199815SMickaël Salaün /* 1941e1199815SMickaël Salaün * Tries to replace a file, from a directory that denies file removal, 1942e1199815SMickaël Salaün * to a different directory (which allows file removal). 1943e1199815SMickaël Salaün */ 1944e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 194555e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 1946e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 1947e1199815SMickaël Salaün RENAME_EXCHANGE)); 194855e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 1949e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 1950e1199815SMickaël Salaün RENAME_EXCHANGE)); 1951e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1952e1199815SMickaël Salaün 1953e1199815SMickaël Salaün /* Exchanges files and directories that partially allow removal. */ 1954e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 1955e1199815SMickaël Salaün RENAME_EXCHANGE)); 1956e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19576a1bdd4aSMickaël Salaün /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 19586a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 19596a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1960e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 1961e1199815SMickaël Salaün RENAME_EXCHANGE)); 1962e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19636a1bdd4aSMickaël Salaün /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 19646a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 19656a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 1966e1199815SMickaël Salaün 1967e1199815SMickaël Salaün /* Renames files with different parents. */ 1968e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 1969e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1970e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1971e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 197255e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 1973e1199815SMickaël Salaün 1974e1199815SMickaël Salaün /* Exchanges and renames files with same parent. */ 1975e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 1976e1199815SMickaël Salaün RENAME_EXCHANGE)); 1977e1199815SMickaël Salaün ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 1978e1199815SMickaël Salaün 1979e1199815SMickaël Salaün /* Exchanges files and directories with same parent, twice. */ 1980e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1981e1199815SMickaël Salaün RENAME_EXCHANGE)); 1982e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 1983e1199815SMickaël Salaün RENAME_EXCHANGE)); 1984e1199815SMickaël Salaün } 1985e1199815SMickaël Salaün 1986e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_dir) 1987e1199815SMickaël Salaün { 1988e1199815SMickaël Salaün const struct rule rules[] = { 1989e1199815SMickaël Salaün { 1990e1199815SMickaël Salaün .path = dir_s1d2, 1991e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1992e1199815SMickaël Salaün }, 1993e1199815SMickaël Salaün { 1994e1199815SMickaël Salaün .path = dir_s2d1, 1995e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 1996e1199815SMickaël Salaün }, 1997135464f9SMickaël Salaün {}, 1998e1199815SMickaël Salaün }; 1999371183faSMickaël Salaün const int ruleset_fd = 2000371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2001e1199815SMickaël Salaün 2002e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2003e1199815SMickaël Salaün 2004e1199815SMickaël Salaün /* Empties dir_s1d3 to allow renaming. */ 2005e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2006e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2007e1199815SMickaël Salaün 2008e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2009e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2010e1199815SMickaël Salaün 2011e1199815SMickaël Salaün /* Exchanges and renames directory to a different parent. */ 2012e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2013e1199815SMickaël Salaün RENAME_EXCHANGE)); 2014e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2015e1199815SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 2016e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2017e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2018e1199815SMickaël Salaün RENAME_EXCHANGE)); 2019e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2020e1199815SMickaël Salaün 2021e1199815SMickaël Salaün /* 2022e1199815SMickaël Salaün * Exchanges directory to the same parent, which doesn't allow 2023e1199815SMickaël Salaün * directory removal. 2024e1199815SMickaël Salaün */ 2025e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 2026e1199815SMickaël Salaün RENAME_EXCHANGE)); 2027e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 20286a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 20296a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 20306a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 2031e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 2032e1199815SMickaël Salaün RENAME_EXCHANGE)); 2033e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 20346a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 20356a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 20366a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 2037e1199815SMickaël Salaün 2038e1199815SMickaël Salaün /* 2039e1199815SMickaël Salaün * Exchanges and renames directory to the same parent, which allows 2040e1199815SMickaël Salaün * directory removal. 2041e1199815SMickaël Salaün */ 2042e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 2043e1199815SMickaël Salaün RENAME_EXCHANGE)); 2044e1199815SMickaël Salaün ASSERT_EQ(0, unlink(dir_s1d3)); 2045e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2046e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 2047e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 2048e1199815SMickaël Salaün } 2049e1199815SMickaël Salaün 2050f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_refer) 2051f4056b92SMickaël Salaün { 2052f4056b92SMickaël Salaün const struct rule layer1[] = { 2053f4056b92SMickaël Salaün { 2054f4056b92SMickaël Salaün .path = dir_s1d2, 2055f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2056f4056b92SMickaël Salaün }, 2057f4056b92SMickaël Salaün { 2058f4056b92SMickaël Salaün .path = dir_s2d2, 2059f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2060f4056b92SMickaël Salaün }, 2061f4056b92SMickaël Salaün {}, 2062f4056b92SMickaël Salaün }; 2063f4056b92SMickaël Salaün int ruleset_fd = 2064f4056b92SMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2065f4056b92SMickaël Salaün 2066f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2067f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2068f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2069f4056b92SMickaël Salaün 2070f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2071f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2072f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2073f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2074f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2075f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2076f4056b92SMickaël Salaün 2077f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2078f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2079f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2080f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2081f4056b92SMickaël Salaün /* 2082f4056b92SMickaël Salaün * Moving should only be allowed when the source and the destination 2083f4056b92SMickaël Salaün * parent directory have REFER. 2084f4056b92SMickaël Salaün */ 2085f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2086f4056b92SMickaël Salaün ASSERT_EQ(ENOTEMPTY, errno); 2087f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2088f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2089f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2090f4056b92SMickaël Salaün } 2091f4056b92SMickaël Salaün 209255e55920SMickaël Salaün /* Checks renames beneath dir_s1d1. */ 209355e55920SMickaël Salaün static void refer_denied_by_default(struct __test_metadata *const _metadata, 209455e55920SMickaël Salaün const struct rule layer1[], 209555e55920SMickaël Salaün const int layer1_err, 209655e55920SMickaël Salaün const struct rule layer2[]) 209755e55920SMickaël Salaün { 209855e55920SMickaël Salaün int ruleset_fd; 209955e55920SMickaël Salaün 210055e55920SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 210155e55920SMickaël Salaün 210255e55920SMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 210355e55920SMickaël Salaün ASSERT_LE(0, ruleset_fd); 210455e55920SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 210555e55920SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 210655e55920SMickaël Salaün 210755e55920SMickaël Salaün /* 210855e55920SMickaël Salaün * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 210955e55920SMickaël Salaün * layer1_err), then it allows some different-parent renames and links. 211055e55920SMickaël Salaün */ 211155e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 211255e55920SMickaël Salaün if (layer1_err == 0) 211355e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 211455e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 211555e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 211655e55920SMickaël Salaün 211755e55920SMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 211855e55920SMickaël Salaün ASSERT_LE(0, ruleset_fd); 211955e55920SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 212055e55920SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 212155e55920SMickaël Salaün 212255e55920SMickaël Salaün /* 212355e55920SMickaël Salaün * Now, either the first or the second layer does not handle 212455e55920SMickaël Salaün * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 212555e55920SMickaël Salaün * renames and links are denied, thus making the layer handling 212655e55920SMickaël Salaün * LANDLOCK_ACCESS_FS_REFER null and void. 212755e55920SMickaël Salaün */ 212855e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 212955e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 213055e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 213155e55920SMickaël Salaün } 213255e55920SMickaël Salaün 213355e55920SMickaël Salaün const struct rule layer_dir_s1d1_refer[] = { 213455e55920SMickaël Salaün { 213555e55920SMickaël Salaün .path = dir_s1d1, 213655e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 213755e55920SMickaël Salaün }, 213855e55920SMickaël Salaün {}, 213955e55920SMickaël Salaün }; 214055e55920SMickaël Salaün 214155e55920SMickaël Salaün const struct rule layer_dir_s1d1_execute[] = { 214255e55920SMickaël Salaün { 214355e55920SMickaël Salaün /* Matches a parent directory. */ 214455e55920SMickaël Salaün .path = dir_s1d1, 214555e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 214655e55920SMickaël Salaün }, 214755e55920SMickaël Salaün {}, 214855e55920SMickaël Salaün }; 214955e55920SMickaël Salaün 215055e55920SMickaël Salaün const struct rule layer_dir_s2d1_execute[] = { 215155e55920SMickaël Salaün { 215255e55920SMickaël Salaün /* Does not match a parent directory. */ 215355e55920SMickaël Salaün .path = dir_s2d1, 215455e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 215555e55920SMickaël Salaün }, 215655e55920SMickaël Salaün {}, 215755e55920SMickaël Salaün }; 215855e55920SMickaël Salaün 215955e55920SMickaël Salaün /* 216055e55920SMickaël Salaün * Tests precedence over renames: denied by default for different parent 216155e55920SMickaël Salaün * directories, *with* a rule matching a parent directory, but not directly 216255e55920SMickaël Salaün * denying access (with MAKE_REG nor REMOVE). 216355e55920SMickaël Salaün */ 216455e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default1) 216555e55920SMickaël Salaün { 216655e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 216755e55920SMickaël Salaün layer_dir_s1d1_execute); 216855e55920SMickaël Salaün } 216955e55920SMickaël Salaün 217055e55920SMickaël Salaün /* 217155e55920SMickaël Salaün * Same test but this time turning around the ABI version order: the first 217255e55920SMickaël Salaün * layer does not handle LANDLOCK_ACCESS_FS_REFER. 217355e55920SMickaël Salaün */ 217455e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default2) 217555e55920SMickaël Salaün { 217655e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 217755e55920SMickaël Salaün layer_dir_s1d1_refer); 217855e55920SMickaël Salaün } 217955e55920SMickaël Salaün 218055e55920SMickaël Salaün /* 218155e55920SMickaël Salaün * Tests precedence over renames: denied by default for different parent 218255e55920SMickaël Salaün * directories, *without* a rule matching a parent directory, but not directly 218355e55920SMickaël Salaün * denying access (with MAKE_REG nor REMOVE). 218455e55920SMickaël Salaün */ 218555e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default3) 218655e55920SMickaël Salaün { 218755e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 218855e55920SMickaël Salaün layer_dir_s2d1_execute); 218955e55920SMickaël Salaün } 219055e55920SMickaël Salaün 219155e55920SMickaël Salaün /* 219255e55920SMickaël Salaün * Same test but this time turning around the ABI version order: the first 219355e55920SMickaël Salaün * layer does not handle LANDLOCK_ACCESS_FS_REFER. 219455e55920SMickaël Salaün */ 219555e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default4) 219655e55920SMickaël Salaün { 219755e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 219855e55920SMickaël Salaün layer_dir_s1d1_refer); 219955e55920SMickaël Salaün } 220055e55920SMickaël Salaün 2201f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_link) 2202f4056b92SMickaël Salaün { 2203f4056b92SMickaël Salaün const struct rule layer1[] = { 2204f4056b92SMickaël Salaün { 2205f4056b92SMickaël Salaün .path = dir_s1d2, 2206f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2207f4056b92SMickaël Salaün }, 2208f4056b92SMickaël Salaün { 2209f4056b92SMickaël Salaün .path = dir_s1d3, 2210f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2211f4056b92SMickaël Salaün }, 2212f4056b92SMickaël Salaün { 2213f4056b92SMickaël Salaün .path = dir_s2d2, 2214f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2215f4056b92SMickaël Salaün }, 2216f4056b92SMickaël Salaün { 2217f4056b92SMickaël Salaün .path = dir_s2d3, 2218f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2219f4056b92SMickaël Salaün }, 2220f4056b92SMickaël Salaün {}, 2221f4056b92SMickaël Salaün }; 2222f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2223f4056b92SMickaël Salaün _metadata, 2224f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2225f4056b92SMickaël Salaün 2226f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2227f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2228f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2229f4056b92SMickaël Salaün 2230f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2231f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2232f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2233f4056b92SMickaël Salaün 2234f4056b92SMickaël Salaün /* Denies linking because of missing MAKE_REG. */ 2235f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2236f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2237f4056b92SMickaël Salaün /* Denies linking because of missing source and destination REFER. */ 2238f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2239f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2240f4056b92SMickaël Salaün /* Denies linking because of missing source REFER. */ 2241f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2242f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2243f4056b92SMickaël Salaün 2244f4056b92SMickaël Salaün /* Denies linking because of missing MAKE_REG. */ 2245f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2246f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2247f4056b92SMickaël Salaün /* Denies linking because of missing destination REFER. */ 2248f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2249f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2250f4056b92SMickaël Salaün 2251f4056b92SMickaël Salaün /* Allows linking because of REFER and MAKE_REG. */ 2252f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2253f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d2)); 2254f4056b92SMickaël Salaün /* Reverse linking denied because of missing MAKE_REG. */ 2255f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2256f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2257f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2258f4056b92SMickaël Salaün /* Checks reverse linking. */ 2259f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2260f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2261f4056b92SMickaël Salaün 2262f4056b92SMickaël Salaün /* 2263f4056b92SMickaël Salaün * This is OK for a file link, but it should not be allowed for a 2264f4056b92SMickaël Salaün * directory rename (because of the superset of access rights. 2265f4056b92SMickaël Salaün */ 2266f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2267f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2268f4056b92SMickaël Salaün 2269f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2270f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2271f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2272f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2273f4056b92SMickaël Salaün 2274f4056b92SMickaël Salaün ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2275f4056b92SMickaël Salaün ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2276f4056b92SMickaël Salaün } 2277f4056b92SMickaël Salaün 2278f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_rename) 2279f4056b92SMickaël Salaün { 2280f4056b92SMickaël Salaün /* Same rules as for reparent_link. */ 2281f4056b92SMickaël Salaün const struct rule layer1[] = { 2282f4056b92SMickaël Salaün { 2283f4056b92SMickaël Salaün .path = dir_s1d2, 2284f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2285f4056b92SMickaël Salaün }, 2286f4056b92SMickaël Salaün { 2287f4056b92SMickaël Salaün .path = dir_s1d3, 2288f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2289f4056b92SMickaël Salaün }, 2290f4056b92SMickaël Salaün { 2291f4056b92SMickaël Salaün .path = dir_s2d2, 2292f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2293f4056b92SMickaël Salaün }, 2294f4056b92SMickaël Salaün { 2295f4056b92SMickaël Salaün .path = dir_s2d3, 2296f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2297f4056b92SMickaël Salaün }, 2298f4056b92SMickaël Salaün {}, 2299f4056b92SMickaël Salaün }; 2300f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2301f4056b92SMickaël Salaün _metadata, 2302f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2303f4056b92SMickaël Salaün 2304f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2305f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2306f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2307f4056b92SMickaël Salaün 2308f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2309f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2310f4056b92SMickaël Salaün 2311f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2312f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2313f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2314f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2315f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2316f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2317f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2318f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2319f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2320f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2321f4056b92SMickaël Salaün /* Even denies same file exchange. */ 2322f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2323f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2324f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2325f4056b92SMickaël Salaün 2326f4056b92SMickaël Salaün /* Denies renaming because of missing source and destination REFER. */ 2327f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2328f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2329f4056b92SMickaël Salaün /* 2330f4056b92SMickaël Salaün * Denies renaming because of missing MAKE_REG, source and destination 2331f4056b92SMickaël Salaün * REFER. 2332f4056b92SMickaël Salaün */ 2333f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2334f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2335f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2336f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2337f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2338f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2339f4056b92SMickaël Salaün 2340f4056b92SMickaël Salaün /* Denies renaming because of missing source REFER. */ 2341f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2342f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2343f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2344f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2345f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2346f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2347f4056b92SMickaël Salaün 2348f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2349f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2350f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2351f4056b92SMickaël Salaün /* Denies renaming because of missing destination REFER*/ 2352f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2353f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2354f4056b92SMickaël Salaün 2355f4056b92SMickaël Salaün /* Denies exchange because of one missing MAKE_REG. */ 2356f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2357f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2358f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2359f4056b92SMickaël Salaün /* Allows renaming because of REFER and MAKE_REG. */ 2360f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2361f4056b92SMickaël Salaün 2362f4056b92SMickaël Salaün /* Reverse renaming denied because of missing MAKE_REG. */ 2363f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2364f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2365f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2366f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2367f4056b92SMickaël Salaün 2368f4056b92SMickaël Salaün /* Tests reverse renaming. */ 2369f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2370f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2371f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2372f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2373f4056b92SMickaël Salaün 2374f4056b92SMickaël Salaün /* 2375f4056b92SMickaël Salaün * This is OK for a file rename, but it should not be allowed for a 2376f4056b92SMickaël Salaün * directory rename (because of the superset of access rights). 2377f4056b92SMickaël Salaün */ 2378f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2379f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2380f4056b92SMickaël Salaün 2381f4056b92SMickaël Salaün /* 2382f4056b92SMickaël Salaün * Tests superset restrictions applied to directories. Not only the 2383f4056b92SMickaël Salaün * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2384f4056b92SMickaël Salaün * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2385f4056b92SMickaël Salaün * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2386f4056b92SMickaël Salaün * directly by the moved dir_s2d3. 2387f4056b92SMickaël Salaün */ 2388f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2389f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2390f4056b92SMickaël Salaün /* 2391f4056b92SMickaël Salaün * The first rename is allowed but not the exchange because dir_s1d3's 2392f4056b92SMickaël Salaün * parent (dir_s1d2) doesn't have REFER. 2393f4056b92SMickaël Salaün */ 2394f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2395f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2396f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2397f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2398f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2399f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2400f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2401f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2402f4056b92SMickaël Salaün 2403f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2404f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2405f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2406f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2407f4056b92SMickaël Salaün 2408f4056b92SMickaël Salaün /* Renaming in the same directory is always allowed. */ 2409f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2410f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2411f4056b92SMickaël Salaün 2412f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2413f4056b92SMickaël Salaün /* Denies because of missing source MAKE_REG and destination REFER. */ 2414f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2415f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2416f4056b92SMickaël Salaün 2417f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2418f4056b92SMickaël Salaün /* Denies because of missing source MAKE_REG and REFER. */ 2419f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2420f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2421f4056b92SMickaël Salaün } 2422f4056b92SMickaël Salaün 2423f4056b92SMickaël Salaün static void 2424f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2425f4056b92SMickaël Salaün { 2426f4056b92SMickaël Salaün const struct rule layer1[] = { 2427f4056b92SMickaël Salaün { 2428f4056b92SMickaël Salaün .path = dir_s1d2, 2429f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2430f4056b92SMickaël Salaün }, 2431f4056b92SMickaël Salaün { 2432f4056b92SMickaël Salaün /* Interesting for the layer2 tests. */ 2433f4056b92SMickaël Salaün .path = dir_s1d3, 2434f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2435f4056b92SMickaël Salaün }, 2436f4056b92SMickaël Salaün { 2437f4056b92SMickaël Salaün .path = dir_s2d2, 2438f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2439f4056b92SMickaël Salaün }, 2440f4056b92SMickaël Salaün { 2441f4056b92SMickaël Salaün .path = dir_s2d3, 2442f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2443f4056b92SMickaël Salaün }, 2444f4056b92SMickaël Salaün {}, 2445f4056b92SMickaël Salaün }; 2446f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2447f4056b92SMickaël Salaün _metadata, 2448f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2449f4056b92SMickaël Salaün 2450f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2451f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2452f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2453f4056b92SMickaël Salaün } 2454f4056b92SMickaël Salaün 2455f4056b92SMickaël Salaün static void 2456f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2457f4056b92SMickaël Salaün { 2458f4056b92SMickaël Salaün const struct rule layer2[] = { 2459f4056b92SMickaël Salaün { 2460f4056b92SMickaël Salaün .path = dir_s2d3, 2461f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2462f4056b92SMickaël Salaün }, 2463f4056b92SMickaël Salaün {}, 2464f4056b92SMickaël Salaün }; 2465f4056b92SMickaël Salaün /* 2466f4056b92SMickaël Salaün * Same checks as before but with a second layer and a new MAKE_DIR 2467f4056b92SMickaël Salaün * rule (and no explicit handling of REFER). 2468f4056b92SMickaël Salaün */ 2469f4056b92SMickaël Salaün const int ruleset_fd = 2470f4056b92SMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2471f4056b92SMickaël Salaün 2472f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2473f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2474f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2475f4056b92SMickaël Salaün } 2476f4056b92SMickaël Salaün 2477f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2478f4056b92SMickaël Salaün { 2479f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d2)); 2480f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2481f4056b92SMickaël Salaün 2482f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2483f4056b92SMickaël Salaün 2484f4056b92SMickaël Salaün /* 2485f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2486f4056b92SMickaël Salaün * because it doesn't inherit new access rights. 2487f4056b92SMickaël Salaün */ 2488f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2489f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2490f4056b92SMickaël Salaün 2491f4056b92SMickaël Salaün /* 2492f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2493f4056b92SMickaël Salaün * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2494f4056b92SMickaël Salaün * already allowed for dir_s1d3. 2495f4056b92SMickaël Salaün */ 2496f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2497f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2498f4056b92SMickaël Salaün 2499f4056b92SMickaël Salaün /* 2500f4056b92SMickaël Salaün * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2501f4056b92SMickaël Salaün * because it cannot inherit MAKE_REG right (which is dedicated to 2502f4056b92SMickaël Salaün * directories). 2503f4056b92SMickaël Salaün */ 2504f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2505f4056b92SMickaël Salaün 2506f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2507f4056b92SMickaël Salaün 2508f4056b92SMickaël Salaün /* 2509f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2510f4056b92SMickaël Salaün * MAKE_DIR is not tied to dir_s2d2. 2511f4056b92SMickaël Salaün */ 2512f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2513f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2514f4056b92SMickaël Salaün 2515f4056b92SMickaël Salaün /* 2516f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2517f4056b92SMickaël Salaün * would grants MAKE_REG and MAKE_DIR rights to it. 2518f4056b92SMickaël Salaün */ 2519f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2520f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2521f4056b92SMickaël Salaün 2522f4056b92SMickaël Salaün /* 252355e55920SMickaël Salaün * Moving the file2_s1d3 file below dir_s2d3 is denied because the 252455e55920SMickaël Salaün * second layer does not handle REFER, which is always denied by 252555e55920SMickaël Salaün * default. 2526f4056b92SMickaël Salaün */ 252755e55920SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 252855e55920SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2529f4056b92SMickaël Salaün } 2530f4056b92SMickaël Salaün 2531f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2532f4056b92SMickaël Salaün { 2533f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2534f4056b92SMickaël Salaün 2535f4056b92SMickaël Salaün /* Checks EACCES predominance over EXDEV. */ 2536f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2537f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2538f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2539f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2540f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2541f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2542f4056b92SMickaël Salaün /* Modify layout! */ 2543f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 2544f4056b92SMickaël Salaün 2545f4056b92SMickaël Salaün /* Without REFER source. */ 2546f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2547f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2548f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2549f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2550f4056b92SMickaël Salaün 2551f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2552f4056b92SMickaël Salaün 2553f4056b92SMickaël Salaün /* Checks EACCES predominance over EXDEV. */ 2554f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2555f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2556f4056b92SMickaël Salaün /* Checks with actual file2_s1d2. */ 2557f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 2558f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2559f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2560f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 256155e55920SMickaël Salaün /* 256255e55920SMickaël Salaün * Modifying the layout is now denied because the second layer does not 256355e55920SMickaël Salaün * handle REFER, which is always denied by default. 256455e55920SMickaël Salaün */ 256555e55920SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 256655e55920SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2567f4056b92SMickaël Salaün 2568f4056b92SMickaël Salaün /* Without REFER source, EACCES wins over EXDEV. */ 2569f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2570f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2571f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2572f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2573f4056b92SMickaël Salaün } 2574f4056b92SMickaël Salaün 2575f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 2576f4056b92SMickaël Salaün { 2577f4056b92SMickaël Salaün const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 2578f4056b92SMickaël Salaün file2_s2d3; 2579f4056b92SMickaël Salaün 2580f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2581f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2582f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2583f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2584f4056b92SMickaël Salaün 2585f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2586f4056b92SMickaël Salaün 2587f4056b92SMickaël Salaün /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2588f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2589f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2590f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2591f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2592f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2593f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2594f4056b92SMickaël Salaün 2595f4056b92SMickaël Salaün /* 2596f4056b92SMickaël Salaün * Checks with directories which creation could be allowed, but denied 2597f4056b92SMickaël Salaün * because of access rights that would be inherited. 2598f4056b92SMickaël Salaün */ 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(EXDEV, 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(EXDEV, errno); 2605f4056b92SMickaël Salaün 2606f4056b92SMickaël Salaün /* Checks with same access rights. */ 2607f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2608f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2609f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2610f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2611f4056b92SMickaël Salaün 2612f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2613f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2614f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2615f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2616f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2617f4056b92SMickaël Salaün 2618f4056b92SMickaël Salaün /* 2619f4056b92SMickaël Salaün * Checks that exchange between file and directory are consistent. 2620f4056b92SMickaël Salaün * 2621f4056b92SMickaël Salaün * Moving a file (file1_s2d2) to a directory which only grants more 2622f4056b92SMickaël Salaün * directory-related access rights is allowed, and at the same time 2623f4056b92SMickaël Salaün * moving a directory (dir_file2_s2d3) to another directory which 2624f4056b92SMickaël Salaün * grants less access rights is allowed too. 2625f4056b92SMickaël Salaün * 2626f4056b92SMickaël Salaün * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 2627f4056b92SMickaël Salaün */ 2628f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2629f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2630f4056b92SMickaël Salaün /* 2631f4056b92SMickaël Salaün * However, moving back the directory is denied because it would get 2632f4056b92SMickaël Salaün * more access rights than the current state and because file creation 2633f4056b92SMickaël Salaün * is forbidden (in dir_s2d2). 2634f4056b92SMickaël Salaün */ 2635f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2636f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2637f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 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 2642f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2643f4056b92SMickaël Salaün 2644f4056b92SMickaël Salaün /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2645f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2646f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2647f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2648f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2649f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2650f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2651f4056b92SMickaël Salaün 2652f4056b92SMickaël Salaün /* Checks with directories which creation is now denied. */ 2653f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2654f4056b92SMickaël Salaün dir_file2_s2d3, RENAME_EXCHANGE)); 2655f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2656f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2657f4056b92SMickaël Salaün dir_file1_s1d2, RENAME_EXCHANGE)); 2658f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2659f4056b92SMickaël Salaün 2660f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2661f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2662f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2663f4056b92SMickaël Salaün /* Denied because of MAKE_DIR. */ 2664f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2665f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2666f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2667f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2668f4056b92SMickaël Salaün 2669f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2670f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2671f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2672f4056b92SMickaël Salaün /* Denied because of MAKE_DIR. */ 2673f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2674f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2675f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2676f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2677f4056b92SMickaël Salaün 2678f4056b92SMickaël Salaün /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 2679f4056b92SMickaël Salaün } 2680f4056b92SMickaël Salaün 2681f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 2682f4056b92SMickaël Salaün { 2683f4056b92SMickaël Salaün const char *const dir_file2_s2d3 = file2_s2d3; 2684f4056b92SMickaël Salaün 2685f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2686f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2687f4056b92SMickaël Salaün 2688f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2689f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2690f4056b92SMickaël Salaün 2691f4056b92SMickaël Salaün /* Checks that exchange between file and directory are consistent. */ 2692f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2693f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2694f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2695f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2696f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2697f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2698f4056b92SMickaël Salaün } 2699f4056b92SMickaël Salaün 2700f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 2701f4056b92SMickaël Salaün { 2702f4056b92SMickaël Salaün const char *const dir_file2_s2d3 = file2_s2d3; 2703f4056b92SMickaël Salaün 2704f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2705f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2706f4056b92SMickaël Salaün 2707f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2708f4056b92SMickaël Salaün 2709f4056b92SMickaël Salaün /* 2710f4056b92SMickaël Salaün * Checks that exchange between file and directory are consistent, 2711f4056b92SMickaël Salaün * including with inverted arguments (see 2712f4056b92SMickaël Salaün * layout1.reparent_exdev_layers_exchange1). 2713f4056b92SMickaël Salaün */ 2714f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2715f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2716f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2717f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2718f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2719f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2720f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2721f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2722f4056b92SMickaël Salaün } 2723f4056b92SMickaël Salaün 2724f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_remove) 2725f4056b92SMickaël Salaün { 2726f4056b92SMickaël Salaün const struct rule layer1[] = { 2727f4056b92SMickaël Salaün { 2728f4056b92SMickaël Salaün .path = dir_s1d1, 2729f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2730f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_DIR, 2731f4056b92SMickaël Salaün }, 2732f4056b92SMickaël Salaün { 2733f4056b92SMickaël Salaün .path = dir_s1d2, 2734f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2735f4056b92SMickaël Salaün }, 2736f4056b92SMickaël Salaün { 2737f4056b92SMickaël Salaün .path = dir_s2d1, 2738f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2739f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE, 2740f4056b92SMickaël Salaün }, 2741f4056b92SMickaël Salaün {}, 2742f4056b92SMickaël Salaün }; 2743f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2744f4056b92SMickaël Salaün _metadata, 2745f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 2746f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE, 2747f4056b92SMickaël Salaün layer1); 2748f4056b92SMickaël Salaün 2749f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2750f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2751f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2752f4056b92SMickaël Salaün 2753f4056b92SMickaël Salaün /* Access denied because of wrong/swapped remove file/dir. */ 2754f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 2755f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2756f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 2757f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2758f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 2759f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2760f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2761f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 2762f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2763f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2764f4056b92SMickaël Salaün 2765f4056b92SMickaël Salaün /* Access allowed thanks to the matching rights. */ 2766f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 2767f4056b92SMickaël Salaün ASSERT_EQ(EISDIR, errno); 2768f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 2769f4056b92SMickaël Salaün ASSERT_EQ(ENOTDIR, errno); 2770f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2771f4056b92SMickaël Salaün ASSERT_EQ(ENOTDIR, errno); 2772f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d1)); 2773f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2774f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2775f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 2776f4056b92SMickaël Salaün 2777f4056b92SMickaël Salaün /* Effectively removes a file and a directory by exchanging them. */ 2778f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2779f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2780f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2781f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2782f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2783f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2784f4056b92SMickaël Salaün } 2785f4056b92SMickaël Salaün 2786f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_dom_superset) 2787f4056b92SMickaël Salaün { 2788f4056b92SMickaël Salaün const struct rule layer1[] = { 2789f4056b92SMickaël Salaün { 2790f4056b92SMickaël Salaün .path = dir_s1d2, 2791f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2792f4056b92SMickaël Salaün }, 2793f4056b92SMickaël Salaün { 2794f4056b92SMickaël Salaün .path = file1_s1d2, 2795f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 2796f4056b92SMickaël Salaün }, 2797f4056b92SMickaël Salaün { 2798f4056b92SMickaël Salaün .path = dir_s1d3, 2799f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 2800f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE, 2801f4056b92SMickaël Salaün }, 2802f4056b92SMickaël Salaün { 2803f4056b92SMickaël Salaün .path = dir_s2d2, 2804f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2805f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | 2806f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK, 2807f4056b92SMickaël Salaün }, 2808f4056b92SMickaël Salaün { 2809f4056b92SMickaël Salaün .path = dir_s2d3, 2810f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2811f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO, 2812f4056b92SMickaël Salaün }, 2813f4056b92SMickaël Salaün {}, 2814f4056b92SMickaël Salaün }; 2815f4056b92SMickaël Salaün int ruleset_fd = create_ruleset(_metadata, 2816f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | 2817f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | 2818f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK | 2819f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 2820f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO, 2821f4056b92SMickaël Salaün layer1); 2822f4056b92SMickaël Salaün 2823f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2824f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2825f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2826f4056b92SMickaël Salaün 2827f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 2828f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2829f4056b92SMickaël Salaün /* 2830f4056b92SMickaël Salaün * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 2831f4056b92SMickaël Salaün * access right. 2832f4056b92SMickaël Salaün */ 2833f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 2834f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2835f4056b92SMickaël Salaün /* 2836f4056b92SMickaël Salaün * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 2837f4056b92SMickaël Salaün * superset of access rights compared to dir_s1d2, because file1_s1d2 2838f4056b92SMickaël Salaün * already has these access rights anyway. 2839f4056b92SMickaël Salaün */ 2840f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 2841f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 2842f4056b92SMickaël Salaün 2843f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2844f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2845f4056b92SMickaël Salaün /* 2846f4056b92SMickaël Salaün * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 2847f4056b92SMickaël Salaün * right. 2848f4056b92SMickaël Salaün */ 2849f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2850f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2851f4056b92SMickaël Salaün /* 2852f4056b92SMickaël Salaün * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 2853f4056b92SMickaël Salaün * of access rights compared to dir_s1d2, because dir_s1d3 already has 2854f4056b92SMickaël Salaün * these access rights anyway. 2855f4056b92SMickaël Salaün */ 2856f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2857f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2858f4056b92SMickaël Salaün 2859f4056b92SMickaël Salaün /* 2860f4056b92SMickaël Salaün * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 2861f4056b92SMickaël Salaün * will be denied because the new inherited access rights from dir_s1d2 2862f4056b92SMickaël Salaün * will be less than the destination (original) dir_s2d3. This is a 2863f4056b92SMickaël Salaün * sinkhole scenario where we cannot move back files or directories. 2864f4056b92SMickaël Salaün */ 2865f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 2866f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2867f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2868f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2869f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2870f4056b92SMickaël Salaün /* 2871f4056b92SMickaël Salaün * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 2872f4056b92SMickaël Salaün * MAKE_SOCK which were inherited from dir_s1d3. 2873f4056b92SMickaël Salaün */ 2874f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 2875f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 2876f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2877f4056b92SMickaël Salaün } 2878f4056b92SMickaël Salaün 2879e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_dir) 2880e1199815SMickaël Salaün { 2881e1199815SMickaël Salaün const struct rule rules[] = { 2882e1199815SMickaël Salaün { 2883e1199815SMickaël Salaün .path = dir_s1d2, 2884e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2885e1199815SMickaël Salaün }, 2886135464f9SMickaël Salaün {}, 2887e1199815SMickaël Salaün }; 2888371183faSMickaël Salaün const int ruleset_fd = 2889371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2890e1199815SMickaël Salaün 2891e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2892e1199815SMickaël Salaün 2893e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2894e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2895e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2896e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2897e1199815SMickaël Salaün 2898e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2899e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2900e1199815SMickaël Salaün 2901e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 2902e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2903e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 2904e1199815SMickaël Salaün 2905e1199815SMickaël Salaün /* dir_s1d2 itself cannot be removed. */ 2906e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d2)); 2907e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2908e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 2909e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2910e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d1)); 2911e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2912e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 2913e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2914e1199815SMickaël Salaün } 2915e1199815SMickaël Salaün 2916e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_file) 2917e1199815SMickaël Salaün { 2918e1199815SMickaël Salaün const struct rule rules[] = { 2919e1199815SMickaël Salaün { 2920e1199815SMickaël Salaün .path = dir_s1d2, 2921e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2922e1199815SMickaël Salaün }, 2923135464f9SMickaël Salaün {}, 2924e1199815SMickaël Salaün }; 2925371183faSMickaël Salaün const int ruleset_fd = 2926371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2927e1199815SMickaël Salaün 2928e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2929e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2930e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2931e1199815SMickaël Salaün 2932e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d1)); 2933e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2934e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 2935e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2936e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2937e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 2938e1199815SMickaël Salaün } 2939e1199815SMickaël Salaün 2940e1199815SMickaël Salaün static void test_make_file(struct __test_metadata *const _metadata, 2941371183faSMickaël Salaün const __u64 access, const mode_t mode, 2942371183faSMickaël Salaün const dev_t dev) 2943e1199815SMickaël Salaün { 2944e1199815SMickaël Salaün const struct rule rules[] = { 2945e1199815SMickaël Salaün { 2946e1199815SMickaël Salaün .path = dir_s1d2, 2947e1199815SMickaël Salaün .access = access, 2948e1199815SMickaël Salaün }, 2949135464f9SMickaël Salaün {}, 2950e1199815SMickaël Salaün }; 2951e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, access, rules); 2952e1199815SMickaël Salaün 2953e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2954e1199815SMickaël Salaün 2955e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2956e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 2957371183faSMickaël Salaün ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 2958371183faSMickaël Salaün { 2959371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 2960371183faSMickaël Salaün strerror(errno)); 2961e1199815SMickaël Salaün }; 2962e1199815SMickaël Salaün 2963e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2964e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2965e1199815SMickaël Salaün 2966e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2967e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2968e1199815SMickaël Salaün 2969e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2970e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2971e1199815SMickaël Salaün 2972e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 2973e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2974e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2975e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2976e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2977e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2978e1199815SMickaël Salaün 2979371183faSMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 2980371183faSMickaël Salaün { 2981371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 2982371183faSMickaël Salaün strerror(errno)); 2983e1199815SMickaël Salaün }; 2984e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 2985e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2986e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 2987e1199815SMickaël Salaün 2988e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 2989e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 2990e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2991e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 2992e1199815SMickaël Salaün } 2993e1199815SMickaël Salaün 2994e1199815SMickaël Salaün TEST_F_FORK(layout1, make_char) 2995e1199815SMickaël Salaün { 2996e1199815SMickaël Salaün /* Creates a /dev/null device. */ 2997e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 2998e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 2999e1199815SMickaël Salaün makedev(1, 3)); 3000e1199815SMickaël Salaün } 3001e1199815SMickaël Salaün 3002e1199815SMickaël Salaün TEST_F_FORK(layout1, make_block) 3003e1199815SMickaël Salaün { 3004e1199815SMickaël Salaün /* Creates a /dev/loop0 device. */ 3005e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 3006e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 3007e1199815SMickaël Salaün makedev(7, 0)); 3008e1199815SMickaël Salaün } 3009e1199815SMickaël Salaün 3010e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_1) 3011e1199815SMickaël Salaün { 3012e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 3013e1199815SMickaël Salaün } 3014e1199815SMickaël Salaün 3015e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_2) 3016e1199815SMickaël Salaün { 3017e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 3018e1199815SMickaël Salaün } 3019e1199815SMickaël Salaün 3020e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sock) 3021e1199815SMickaël Salaün { 3022e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 3023e1199815SMickaël Salaün } 3024e1199815SMickaël Salaün 3025e1199815SMickaël Salaün TEST_F_FORK(layout1, make_fifo) 3026e1199815SMickaël Salaün { 3027e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 3028e1199815SMickaël Salaün } 3029e1199815SMickaël Salaün 3030e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sym) 3031e1199815SMickaël Salaün { 3032e1199815SMickaël Salaün const struct rule rules[] = { 3033e1199815SMickaël Salaün { 3034e1199815SMickaël Salaün .path = dir_s1d2, 3035e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 3036e1199815SMickaël Salaün }, 3037135464f9SMickaël Salaün {}, 3038e1199815SMickaël Salaün }; 3039371183faSMickaël Salaün const int ruleset_fd = 3040371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3041e1199815SMickaël Salaün 3042e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3043e1199815SMickaël Salaün 3044e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 3045e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 3046e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file2_s1d1)); 3047e1199815SMickaël Salaün 3048e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3049e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 3050e1199815SMickaël Salaün 3051e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 3052e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 3053e1199815SMickaël Salaün 3054e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3055e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3056e1199815SMickaël Salaün 3057e1199815SMickaël Salaün ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3058e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3059e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3060e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3061e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3062e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3063e1199815SMickaël Salaün 3064e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d2)); 3065e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3066e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 3067e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3068e1199815SMickaël Salaün 3069e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d3)); 3070e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3071e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 3072e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3073e1199815SMickaël Salaün } 3074e1199815SMickaël Salaün 3075e1199815SMickaël Salaün TEST_F_FORK(layout1, make_dir) 3076e1199815SMickaël Salaün { 3077e1199815SMickaël Salaün const struct rule rules[] = { 3078e1199815SMickaël Salaün { 3079e1199815SMickaël Salaün .path = dir_s1d2, 3080e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3081e1199815SMickaël Salaün }, 3082135464f9SMickaël Salaün {}, 3083e1199815SMickaël Salaün }; 3084371183faSMickaël Salaün const int ruleset_fd = 3085371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3086e1199815SMickaël Salaün 3087e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3088e1199815SMickaël Salaün 3089e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 3090e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3091e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 3092e1199815SMickaël Salaün 3093e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3094e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3095e1199815SMickaël Salaün 3096e1199815SMickaël Salaün /* Uses file_* as directory names. */ 3097e1199815SMickaël Salaün ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3098e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3099e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3100e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3101e1199815SMickaël Salaün } 3102e1199815SMickaël Salaün 3103e1199815SMickaël Salaün static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3104e1199815SMickaël Salaün const int open_flags) 3105e1199815SMickaël Salaün { 3106e1199815SMickaël Salaün static const char path_template[] = "/proc/self/fd/%d"; 3107e1199815SMickaël Salaün char procfd_path[sizeof(path_template) + 10]; 3108371183faSMickaël Salaün const int procfd_path_size = 3109371183faSMickaël Salaün snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3110e1199815SMickaël Salaün 3111e1199815SMickaël Salaün ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3112e1199815SMickaël Salaün return open(procfd_path, open_flags); 3113e1199815SMickaël Salaün } 3114e1199815SMickaël Salaün 3115e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_unlinked_file) 3116e1199815SMickaël Salaün { 3117e1199815SMickaël Salaün const struct rule rules[] = { 3118e1199815SMickaël Salaün { 3119e1199815SMickaël Salaün .path = file1_s1d2, 3120e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3121e1199815SMickaël Salaün }, 3122135464f9SMickaël Salaün {}, 3123e1199815SMickaël Salaün }; 3124e1199815SMickaël Salaün int reg_fd, proc_fd; 3125371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 3126371183faSMickaël Salaün _metadata, 3127371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3128371183faSMickaël Salaün rules); 3129e1199815SMickaël Salaün 3130e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3131e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3132e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3133e1199815SMickaël Salaün 3134e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3135e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3136e1199815SMickaël Salaün reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3137e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 3138e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3139e1199815SMickaël Salaün 3140e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3141e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3142e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3143e1199815SMickaël Salaün 3144e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3145371183faSMickaël Salaün ASSERT_EQ(-1, proc_fd) 3146371183faSMickaël Salaün { 3147371183faSMickaël Salaün TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3148371183faSMickaël Salaün strerror(errno)); 3149e1199815SMickaël Salaün } 3150e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3151e1199815SMickaël Salaün 3152e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 3153e1199815SMickaël Salaün } 3154e1199815SMickaël Salaün 3155e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_pipe) 3156e1199815SMickaël Salaün { 3157e1199815SMickaël Salaün int proc_fd; 3158e1199815SMickaël Salaün int pipe_fds[2]; 3159e1199815SMickaël Salaün char buf = '\0'; 3160e1199815SMickaël Salaün const struct rule rules[] = { 3161e1199815SMickaël Salaün { 3162e1199815SMickaël Salaün .path = dir_s1d2, 3163e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3164e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3165e1199815SMickaël Salaün }, 3166135464f9SMickaël Salaün {}, 3167e1199815SMickaël Salaün }; 3168e1199815SMickaël Salaün /* Limits read and write access to files tied to the filesystem. */ 3169371183faSMickaël Salaün const int ruleset_fd = 3170371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3171e1199815SMickaël Salaün 3172e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3173e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3174e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3175e1199815SMickaël Salaün 3176e1199815SMickaël Salaün /* Checks enforcement for normal files. */ 3177e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3178e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3179e1199815SMickaël Salaün 3180e1199815SMickaël Salaün /* Checks access to pipes through FD. */ 3181e1199815SMickaël Salaün ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3182371183faSMickaël Salaün ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3183371183faSMickaël Salaün { 3184e1199815SMickaël Salaün TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3185e1199815SMickaël Salaün } 3186e1199815SMickaël Salaün ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3187e1199815SMickaël Salaün ASSERT_EQ('.', buf); 3188e1199815SMickaël Salaün 3189e1199815SMickaël Salaün /* Checks write access to pipe through /proc/self/fd . */ 3190e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3191e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3192371183faSMickaël Salaün ASSERT_EQ(1, write(proc_fd, ".", 1)) 3193371183faSMickaël Salaün { 3194e1199815SMickaël Salaün TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3195e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 3196e1199815SMickaël Salaün } 3197e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3198e1199815SMickaël Salaün 3199e1199815SMickaël Salaün /* Checks read access to pipe through /proc/self/fd . */ 3200e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3201e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3202e1199815SMickaël Salaün buf = '\0'; 3203371183faSMickaël Salaün ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3204371183faSMickaël Salaün { 3205e1199815SMickaël Salaün TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3206e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 3207e1199815SMickaël Salaün } 3208e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3209e1199815SMickaël Salaün 3210e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[0])); 3211e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[1])); 3212e1199815SMickaël Salaün } 3213e1199815SMickaël Salaün 3214225351abSGünther Noack /* Invokes truncate(2) and returns its errno or 0. */ 3215225351abSGünther Noack static int test_truncate(const char *const path) 3216225351abSGünther Noack { 3217225351abSGünther Noack if (truncate(path, 10) < 0) 3218225351abSGünther Noack return errno; 3219225351abSGünther Noack return 0; 3220225351abSGünther Noack } 3221225351abSGünther Noack 3222225351abSGünther Noack /* 3223225351abSGünther Noack * Invokes creat(2) and returns its errno or 0. 3224225351abSGünther Noack * Closes the opened file descriptor on success. 3225225351abSGünther Noack */ 3226225351abSGünther Noack static int test_creat(const char *const path) 3227225351abSGünther Noack { 3228225351abSGünther Noack int fd = creat(path, 0600); 3229225351abSGünther Noack 3230225351abSGünther Noack if (fd < 0) 3231225351abSGünther Noack return errno; 3232225351abSGünther Noack 3233225351abSGünther Noack /* 3234225351abSGünther Noack * Mixing error codes from close(2) and creat(2) should not lead to any 3235225351abSGünther Noack * (access type) confusion for this test. 3236225351abSGünther Noack */ 3237225351abSGünther Noack if (close(fd) < 0) 3238225351abSGünther Noack return errno; 3239225351abSGünther Noack return 0; 3240225351abSGünther Noack } 3241225351abSGünther Noack 3242225351abSGünther Noack /* 3243225351abSGünther Noack * Exercises file truncation when it's not restricted, 3244225351abSGünther Noack * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 3245225351abSGünther Noack */ 3246225351abSGünther Noack TEST_F_FORK(layout1, truncate_unhandled) 3247225351abSGünther Noack { 3248225351abSGünther Noack const char *const file_r = file1_s1d1; 3249225351abSGünther Noack const char *const file_w = file2_s1d1; 3250225351abSGünther Noack const char *const file_none = file1_s1d2; 3251225351abSGünther Noack const struct rule rules[] = { 3252225351abSGünther Noack { 3253225351abSGünther Noack .path = file_r, 3254225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE, 3255225351abSGünther Noack }, 3256225351abSGünther Noack { 3257225351abSGünther Noack .path = file_w, 3258225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3259225351abSGünther Noack }, 3260225351abSGünther Noack /* Implicitly: No rights for file_none. */ 3261225351abSGünther Noack {}, 3262225351abSGünther Noack }; 3263225351abSGünther Noack 3264225351abSGünther Noack const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3265225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE; 3266225351abSGünther Noack int ruleset_fd; 3267225351abSGünther Noack 3268225351abSGünther Noack /* Enable Landlock. */ 3269225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled, rules); 3270225351abSGünther Noack 3271225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3272225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3273225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3274225351abSGünther Noack 3275225351abSGünther Noack /* 3276225351abSGünther Noack * Checks read right: truncate and open with O_TRUNC work, unless the 3277225351abSGünther Noack * file is attempted to be opened for writing. 3278225351abSGünther Noack */ 3279225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_r)); 3280225351abSGünther Noack EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 3281225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 3282225351abSGünther Noack EXPECT_EQ(EACCES, test_creat(file_r)); 3283225351abSGünther Noack 3284225351abSGünther Noack /* 3285225351abSGünther Noack * Checks write right: truncate and open with O_TRUNC work, unless the 3286225351abSGünther Noack * file is attempted to be opened for reading. 3287225351abSGünther Noack */ 3288225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_w)); 3289225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 3290225351abSGünther Noack EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 3291225351abSGünther Noack EXPECT_EQ(0, test_creat(file_w)); 3292225351abSGünther Noack 3293225351abSGünther Noack /* 3294225351abSGünther Noack * Checks "no rights" case: truncate works but all open attempts fail, 3295225351abSGünther Noack * including creat. 3296225351abSGünther Noack */ 3297225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_none)); 3298225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3299225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3300225351abSGünther Noack EXPECT_EQ(EACCES, test_creat(file_none)); 3301225351abSGünther Noack } 3302225351abSGünther Noack 3303225351abSGünther Noack TEST_F_FORK(layout1, truncate) 3304225351abSGünther Noack { 3305225351abSGünther Noack const char *const file_rwt = file1_s1d1; 3306225351abSGünther Noack const char *const file_rw = file2_s1d1; 3307225351abSGünther Noack const char *const file_rt = file1_s1d2; 3308225351abSGünther Noack const char *const file_t = file2_s1d2; 3309225351abSGünther Noack const char *const file_none = file1_s1d3; 3310225351abSGünther Noack const char *const dir_t = dir_s2d1; 3311225351abSGünther Noack const char *const file_in_dir_t = file1_s2d1; 3312225351abSGünther Noack const char *const dir_w = dir_s3d1; 3313225351abSGünther Noack const char *const file_in_dir_w = file1_s3d1; 3314225351abSGünther Noack const struct rule rules[] = { 3315225351abSGünther Noack { 3316225351abSGünther Noack .path = file_rwt, 3317225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE | 3318225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE | 3319225351abSGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE, 3320225351abSGünther Noack }, 3321225351abSGünther Noack { 3322225351abSGünther Noack .path = file_rw, 3323225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE | 3324225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE, 3325225351abSGünther Noack }, 3326225351abSGünther Noack { 3327225351abSGünther Noack .path = file_rt, 3328225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE | 3329225351abSGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE, 3330225351abSGünther Noack }, 3331225351abSGünther Noack { 3332225351abSGünther Noack .path = file_t, 3333225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3334225351abSGünther Noack }, 3335225351abSGünther Noack /* Implicitly: No access rights for file_none. */ 3336225351abSGünther Noack { 3337225351abSGünther Noack .path = dir_t, 3338225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3339225351abSGünther Noack }, 3340225351abSGünther Noack { 3341225351abSGünther Noack .path = dir_w, 3342225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3343225351abSGünther Noack }, 3344225351abSGünther Noack {}, 3345225351abSGünther Noack }; 3346225351abSGünther Noack const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3347225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE | 3348225351abSGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE; 3349225351abSGünther Noack int ruleset_fd; 3350225351abSGünther Noack 3351225351abSGünther Noack /* Enable Landlock. */ 3352225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled, rules); 3353225351abSGünther Noack 3354225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3355225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3356225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3357225351abSGünther Noack 3358225351abSGünther Noack /* Checks read, write and truncate rights: truncation works. */ 3359225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_rwt)); 3360225351abSGünther Noack EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 3361225351abSGünther Noack EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 3362225351abSGünther Noack 3363225351abSGünther Noack /* Checks read and write rights: no truncate variant works. */ 3364225351abSGünther Noack EXPECT_EQ(EACCES, test_truncate(file_rw)); 3365225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 3366225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 3367225351abSGünther Noack 3368225351abSGünther Noack /* 3369225351abSGünther Noack * Checks read and truncate rights: truncation works. 3370225351abSGünther Noack * 3371225351abSGünther Noack * Note: Files can get truncated using open() even with O_RDONLY. 3372225351abSGünther Noack */ 3373225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_rt)); 3374225351abSGünther Noack EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 3375225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 3376225351abSGünther Noack 3377225351abSGünther Noack /* Checks truncate right: truncate works, but can't open file. */ 3378225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_t)); 3379225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 3380225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 3381225351abSGünther Noack 3382225351abSGünther Noack /* Checks "no rights" case: No form of truncation works. */ 3383225351abSGünther Noack EXPECT_EQ(EACCES, test_truncate(file_none)); 3384225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3385225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3386225351abSGünther Noack 3387225351abSGünther Noack /* 3388225351abSGünther Noack * Checks truncate right on directory: truncate works on contained 3389225351abSGünther Noack * files. 3390225351abSGünther Noack */ 3391225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_in_dir_t)); 3392225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 3393225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 3394225351abSGünther Noack 3395225351abSGünther Noack /* 3396225351abSGünther Noack * Checks creat in dir_w: This requires the truncate right when 3397225351abSGünther Noack * overwriting an existing file, but does not require it when the file 3398225351abSGünther Noack * is new. 3399225351abSGünther Noack */ 3400225351abSGünther Noack EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 3401225351abSGünther Noack 3402225351abSGünther Noack ASSERT_EQ(0, unlink(file_in_dir_w)); 3403225351abSGünther Noack EXPECT_EQ(0, test_creat(file_in_dir_w)); 3404225351abSGünther Noack } 3405225351abSGünther Noack 3406225351abSGünther Noack /* Invokes ftruncate(2) and returns its errno or 0. */ 3407225351abSGünther Noack static int test_ftruncate(int fd) 3408225351abSGünther Noack { 3409225351abSGünther Noack if (ftruncate(fd, 10) < 0) 3410225351abSGünther Noack return errno; 3411225351abSGünther Noack return 0; 3412225351abSGünther Noack } 3413225351abSGünther Noack 3414225351abSGünther Noack TEST_F_FORK(layout1, ftruncate) 3415225351abSGünther Noack { 3416225351abSGünther Noack /* 3417225351abSGünther Noack * This test opens a new file descriptor at different stages of 3418225351abSGünther Noack * Landlock restriction: 3419225351abSGünther Noack * 3420225351abSGünther Noack * without restriction: ftruncate works 3421225351abSGünther Noack * something else but truncate restricted: ftruncate works 3422225351abSGünther Noack * truncate restricted and permitted: ftruncate works 3423225351abSGünther Noack * truncate restricted and not permitted: ftruncate fails 3424225351abSGünther Noack * 3425225351abSGünther Noack * Whether this works or not is expected to depend on the time when the 3426225351abSGünther Noack * FD was opened, not to depend on the time when ftruncate() was 3427225351abSGünther Noack * called. 3428225351abSGünther Noack */ 3429225351abSGünther Noack const char *const path = file1_s1d1; 3430225351abSGünther Noack const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 3431225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE; 3432225351abSGünther Noack const struct rule layer1[] = { 3433225351abSGünther Noack { 3434225351abSGünther Noack .path = path, 3435225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3436225351abSGünther Noack }, 3437225351abSGünther Noack {}, 3438225351abSGünther Noack }; 3439225351abSGünther Noack const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 3440225351abSGünther Noack const struct rule layer2[] = { 3441225351abSGünther Noack { 3442225351abSGünther Noack .path = path, 3443225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3444225351abSGünther Noack }, 3445225351abSGünther Noack {}, 3446225351abSGünther Noack }; 3447225351abSGünther Noack const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 3448225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE; 3449225351abSGünther Noack const struct rule layer3[] = { 3450225351abSGünther Noack { 3451225351abSGünther Noack .path = path, 3452225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3453225351abSGünther Noack }, 3454225351abSGünther Noack {}, 3455225351abSGünther Noack }; 3456225351abSGünther Noack int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; 3457225351abSGünther Noack 3458225351abSGünther Noack fd_layer0 = open(path, O_WRONLY); 3459225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3460225351abSGünther Noack 3461225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled1, layer1); 3462225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3463225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3464225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3465225351abSGünther Noack 3466225351abSGünther Noack fd_layer1 = open(path, O_WRONLY); 3467225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3468225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3469225351abSGünther Noack 3470225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled2, layer2); 3471225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3472225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3473225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3474225351abSGünther Noack 3475225351abSGünther Noack fd_layer2 = open(path, O_WRONLY); 3476225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3477225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3478225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3479225351abSGünther Noack 3480225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled3, layer3); 3481225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3482225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3483225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3484225351abSGünther Noack 3485225351abSGünther Noack fd_layer3 = open(path, O_WRONLY); 3486225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3487225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3488225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3489225351abSGünther Noack EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 3490225351abSGünther Noack 3491225351abSGünther Noack ASSERT_EQ(0, close(fd_layer0)); 3492225351abSGünther Noack ASSERT_EQ(0, close(fd_layer1)); 3493225351abSGünther Noack ASSERT_EQ(0, close(fd_layer2)); 3494225351abSGünther Noack ASSERT_EQ(0, close(fd_layer3)); 3495225351abSGünther Noack } 3496225351abSGünther Noack 34974598d9abSMickaël Salaün /* clang-format off */ 349841729af2SGünther Noack FIXTURE(ftruncate) {}; 349941729af2SGünther Noack /* clang-format on */ 350041729af2SGünther Noack 350141729af2SGünther Noack FIXTURE_SETUP(ftruncate) 350241729af2SGünther Noack { 350341729af2SGünther Noack prepare_layout(_metadata); 350441729af2SGünther Noack create_file(_metadata, file1_s1d1); 350541729af2SGünther Noack } 350641729af2SGünther Noack 350741729af2SGünther Noack FIXTURE_TEARDOWN(ftruncate) 350841729af2SGünther Noack { 350941729af2SGünther Noack EXPECT_EQ(0, remove_path(file1_s1d1)); 351041729af2SGünther Noack cleanup_layout(_metadata); 351141729af2SGünther Noack } 351241729af2SGünther Noack 351341729af2SGünther Noack FIXTURE_VARIANT(ftruncate) 351441729af2SGünther Noack { 351541729af2SGünther Noack const __u64 handled; 351641729af2SGünther Noack const __u64 permitted; 351741729af2SGünther Noack const int expected_open_result; 351841729af2SGünther Noack const int expected_ftruncate_result; 351941729af2SGünther Noack }; 352041729af2SGünther Noack 352141729af2SGünther Noack /* clang-format off */ 352241729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, w_w) { 352341729af2SGünther Noack /* clang-format on */ 352441729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 352541729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 352641729af2SGünther Noack .expected_open_result = 0, 352741729af2SGünther Noack .expected_ftruncate_result = 0, 352841729af2SGünther Noack }; 352941729af2SGünther Noack 353041729af2SGünther Noack /* clang-format off */ 353141729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, t_t) { 353241729af2SGünther Noack /* clang-format on */ 353341729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 353441729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 353541729af2SGünther Noack .expected_open_result = 0, 353641729af2SGünther Noack .expected_ftruncate_result = 0, 353741729af2SGünther Noack }; 353841729af2SGünther Noack 353941729af2SGünther Noack /* clang-format off */ 354041729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_w) { 354141729af2SGünther Noack /* clang-format on */ 354241729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 354341729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 354441729af2SGünther Noack .expected_open_result = 0, 354541729af2SGünther Noack .expected_ftruncate_result = EACCES, 354641729af2SGünther Noack }; 354741729af2SGünther Noack 354841729af2SGünther Noack /* clang-format off */ 354941729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 355041729af2SGünther Noack /* clang-format on */ 355141729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 355241729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE | 355341729af2SGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE, 355441729af2SGünther Noack .expected_open_result = 0, 355541729af2SGünther Noack .expected_ftruncate_result = 0, 355641729af2SGünther Noack }; 355741729af2SGünther Noack 355841729af2SGünther Noack /* clang-format off */ 355941729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_t) { 356041729af2SGünther Noack /* clang-format on */ 356141729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 356241729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 356341729af2SGünther Noack .expected_open_result = EACCES, 356441729af2SGünther Noack }; 356541729af2SGünther Noack 356641729af2SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate) 356741729af2SGünther Noack { 356841729af2SGünther Noack const char *const path = file1_s1d1; 356941729af2SGünther Noack const struct rule rules[] = { 357041729af2SGünther Noack { 357141729af2SGünther Noack .path = path, 357241729af2SGünther Noack .access = variant->permitted, 357341729af2SGünther Noack }, 357441729af2SGünther Noack {}, 357541729af2SGünther Noack }; 357641729af2SGünther Noack int fd, ruleset_fd; 357741729af2SGünther Noack 357841729af2SGünther Noack /* Enable Landlock. */ 357941729af2SGünther Noack ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 358041729af2SGünther Noack ASSERT_LE(0, ruleset_fd); 358141729af2SGünther Noack enforce_ruleset(_metadata, ruleset_fd); 358241729af2SGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 358341729af2SGünther Noack 358441729af2SGünther Noack fd = open(path, O_WRONLY); 358541729af2SGünther Noack EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 358641729af2SGünther Noack if (fd >= 0) { 358741729af2SGünther Noack EXPECT_EQ(variant->expected_ftruncate_result, 358841729af2SGünther Noack test_ftruncate(fd)); 358941729af2SGünther Noack ASSERT_EQ(0, close(fd)); 359041729af2SGünther Noack } 359141729af2SGünther Noack } 359241729af2SGünther Noack 3593a1a202a5SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 3594a1a202a5SGünther Noack { 3595a1a202a5SGünther Noack int child, fd, status; 3596a1a202a5SGünther Noack int socket_fds[2]; 3597a1a202a5SGünther Noack 3598a1a202a5SGünther Noack ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 3599a1a202a5SGünther Noack socket_fds)); 3600a1a202a5SGünther Noack 3601a1a202a5SGünther Noack child = fork(); 3602a1a202a5SGünther Noack ASSERT_LE(0, child); 3603a1a202a5SGünther Noack if (child == 0) { 3604a1a202a5SGünther Noack /* 3605a1a202a5SGünther Noack * Enables Landlock in the child process, open a file descriptor 3606a1a202a5SGünther Noack * where truncation is forbidden and send it to the 3607a1a202a5SGünther Noack * non-landlocked parent process. 3608a1a202a5SGünther Noack */ 3609a1a202a5SGünther Noack const char *const path = file1_s1d1; 3610a1a202a5SGünther Noack const struct rule rules[] = { 3611a1a202a5SGünther Noack { 3612a1a202a5SGünther Noack .path = path, 3613a1a202a5SGünther Noack .access = variant->permitted, 3614a1a202a5SGünther Noack }, 3615a1a202a5SGünther Noack {}, 3616a1a202a5SGünther Noack }; 3617a1a202a5SGünther Noack int fd, ruleset_fd; 3618a1a202a5SGünther Noack 3619a1a202a5SGünther Noack ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 3620a1a202a5SGünther Noack ASSERT_LE(0, ruleset_fd); 3621a1a202a5SGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3622a1a202a5SGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3623a1a202a5SGünther Noack 3624a1a202a5SGünther Noack fd = open(path, O_WRONLY); 3625a1a202a5SGünther Noack ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3626a1a202a5SGünther Noack 3627a1a202a5SGünther Noack if (fd >= 0) { 3628a1a202a5SGünther Noack ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 3629a1a202a5SGünther Noack ASSERT_EQ(0, close(fd)); 3630a1a202a5SGünther Noack } 3631a1a202a5SGünther Noack 3632a1a202a5SGünther Noack ASSERT_EQ(0, close(socket_fds[0])); 3633a1a202a5SGünther Noack 3634a1a202a5SGünther Noack _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 3635a1a202a5SGünther Noack return; 3636a1a202a5SGünther Noack } 3637a1a202a5SGünther Noack 3638a1a202a5SGünther Noack if (variant->expected_open_result == 0) { 3639a1a202a5SGünther Noack fd = recv_fd(socket_fds[1]); 3640a1a202a5SGünther Noack ASSERT_LE(0, fd); 3641a1a202a5SGünther Noack 3642a1a202a5SGünther Noack EXPECT_EQ(variant->expected_ftruncate_result, 3643a1a202a5SGünther Noack test_ftruncate(fd)); 3644a1a202a5SGünther Noack ASSERT_EQ(0, close(fd)); 3645a1a202a5SGünther Noack } 3646a1a202a5SGünther Noack 3647a1a202a5SGünther Noack ASSERT_EQ(child, waitpid(child, &status, 0)); 3648a1a202a5SGünther Noack ASSERT_EQ(1, WIFEXITED(status)); 3649a1a202a5SGünther Noack ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 3650a1a202a5SGünther Noack 3651a1a202a5SGünther Noack ASSERT_EQ(0, close(socket_fds[0])); 3652a1a202a5SGünther Noack ASSERT_EQ(0, close(socket_fds[1])); 3653a1a202a5SGünther Noack } 3654a1a202a5SGünther Noack 36550d8c658bSGünther Noack TEST(memfd_ftruncate) 36560d8c658bSGünther Noack { 36570d8c658bSGünther Noack int fd; 36580d8c658bSGünther Noack 36590d8c658bSGünther Noack fd = memfd_create("name", MFD_CLOEXEC); 36600d8c658bSGünther Noack ASSERT_LE(0, fd); 36610d8c658bSGünther Noack 36620d8c658bSGünther Noack /* 36630d8c658bSGünther Noack * Checks that ftruncate is permitted on file descriptors that are 36640d8c658bSGünther Noack * created in ways other than open(2). 36650d8c658bSGünther Noack */ 36660d8c658bSGünther Noack EXPECT_EQ(0, test_ftruncate(fd)); 36670d8c658bSGünther Noack 36680d8c658bSGünther Noack ASSERT_EQ(0, close(fd)); 36690d8c658bSGünther Noack } 36700d8c658bSGünther Noack 367141729af2SGünther Noack /* clang-format off */ 36724598d9abSMickaël Salaün FIXTURE(layout1_bind) {}; 36734598d9abSMickaël Salaün /* clang-format on */ 3674e1199815SMickaël Salaün 3675e1199815SMickaël Salaün FIXTURE_SETUP(layout1_bind) 3676e1199815SMickaël Salaün { 3677e1199815SMickaël Salaün prepare_layout(_metadata); 3678e1199815SMickaël Salaün 3679e1199815SMickaël Salaün create_layout1(_metadata); 3680e1199815SMickaël Salaün 3681e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3682e1199815SMickaël Salaün ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 3683e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3684e1199815SMickaël Salaün } 3685e1199815SMickaël Salaün 3686e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1_bind) 3687e1199815SMickaël Salaün { 3688e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3689e1199815SMickaël Salaün EXPECT_EQ(0, umount(dir_s2d2)); 3690e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3691e1199815SMickaël Salaün 3692e1199815SMickaël Salaün remove_layout1(_metadata); 3693e1199815SMickaël Salaün 3694e1199815SMickaël Salaün cleanup_layout(_metadata); 3695e1199815SMickaël Salaün } 3696e1199815SMickaël Salaün 3697e1199815SMickaël Salaün static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 3698e1199815SMickaël Salaün static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 3699e1199815SMickaël Salaün 3700e1199815SMickaël Salaün /* 3701e1199815SMickaël Salaün * layout1_bind hierarchy: 3702e1199815SMickaël Salaün * 3703e1199815SMickaël Salaün * tmp 3704e1199815SMickaël Salaün * ├── s1d1 3705e1199815SMickaël Salaün * │ ├── f1 3706e1199815SMickaël Salaün * │ ├── f2 3707e1199815SMickaël Salaün * │ └── s1d2 3708e1199815SMickaël Salaün * │ ├── f1 3709e1199815SMickaël Salaün * │ ├── f2 3710e1199815SMickaël Salaün * │ └── s1d3 3711e1199815SMickaël Salaün * │ ├── f1 3712e1199815SMickaël Salaün * │ └── f2 3713e1199815SMickaël Salaün * ├── s2d1 3714e1199815SMickaël Salaün * │ ├── f1 3715e1199815SMickaël Salaün * │ └── s2d2 3716e1199815SMickaël Salaün * │ ├── f1 3717e1199815SMickaël Salaün * │ ├── f2 3718e1199815SMickaël Salaün * │ └── s1d3 3719e1199815SMickaël Salaün * │ ├── f1 3720e1199815SMickaël Salaün * │ └── f2 3721e1199815SMickaël Salaün * └── s3d1 3722e1199815SMickaël Salaün * └── s3d2 3723e1199815SMickaël Salaün * └── s3d3 3724e1199815SMickaël Salaün */ 3725e1199815SMickaël Salaün 3726e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, no_restriction) 3727e1199815SMickaël Salaün { 3728e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 3729e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3730e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 3731e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3732e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 3733e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3734e1199815SMickaël Salaün 3735e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 3736e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 3737e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 3738e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 3739e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 3740e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 3741e1199815SMickaël Salaün 3742e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 3743e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3744e1199815SMickaël Salaün 3745e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 3746e1199815SMickaël Salaün } 3747e1199815SMickaël Salaün 3748e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, same_content_same_file) 3749e1199815SMickaël Salaün { 3750e1199815SMickaël Salaün /* 3751e1199815SMickaël Salaün * Sets access right on parent directories of both source and 3752e1199815SMickaël Salaün * destination mount points. 3753e1199815SMickaël Salaün */ 3754e1199815SMickaël Salaün const struct rule layer1_parent[] = { 3755e1199815SMickaël Salaün { 3756e1199815SMickaël Salaün .path = dir_s1d1, 3757e1199815SMickaël Salaün .access = ACCESS_RO, 3758e1199815SMickaël Salaün }, 3759e1199815SMickaël Salaün { 3760e1199815SMickaël Salaün .path = dir_s2d1, 3761e1199815SMickaël Salaün .access = ACCESS_RW, 3762e1199815SMickaël Salaün }, 3763135464f9SMickaël Salaün {}, 3764e1199815SMickaël Salaün }; 3765e1199815SMickaël Salaün /* 3766e1199815SMickaël Salaün * Sets access rights on the same bind-mounted directories. The result 3767e1199815SMickaël Salaün * should be ACCESS_RW for both directories, but not both hierarchies 3768e1199815SMickaël Salaün * because of the first layer. 3769e1199815SMickaël Salaün */ 3770e1199815SMickaël Salaün const struct rule layer2_mount_point[] = { 3771e1199815SMickaël Salaün { 3772e1199815SMickaël Salaün .path = dir_s1d2, 3773e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3774e1199815SMickaël Salaün }, 3775e1199815SMickaël Salaün { 3776e1199815SMickaël Salaün .path = dir_s2d2, 3777e1199815SMickaël Salaün .access = ACCESS_RW, 3778e1199815SMickaël Salaün }, 3779135464f9SMickaël Salaün {}, 3780e1199815SMickaël Salaün }; 3781e1199815SMickaël Salaün /* Only allow read-access to the s1d3 hierarchies. */ 3782e1199815SMickaël Salaün const struct rule layer3_source[] = { 3783e1199815SMickaël Salaün { 3784e1199815SMickaël Salaün .path = dir_s1d3, 3785e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3786e1199815SMickaël Salaün }, 3787135464f9SMickaël Salaün {}, 3788e1199815SMickaël Salaün }; 3789e1199815SMickaël Salaün /* Removes all access rights. */ 3790e1199815SMickaël Salaün const struct rule layer4_destination[] = { 3791e1199815SMickaël Salaün { 3792e1199815SMickaël Salaün .path = bind_file1_s1d3, 3793e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3794e1199815SMickaël Salaün }, 3795135464f9SMickaël Salaün {}, 3796e1199815SMickaël Salaün }; 3797e1199815SMickaël Salaün int ruleset_fd; 3798e1199815SMickaël Salaün 3799e1199815SMickaël Salaün /* Sets rules for the parent directories. */ 3800e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 3801e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3802e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3803e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3804e1199815SMickaël Salaün 3805e1199815SMickaël Salaün /* Checks source hierarchy. */ 3806e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3807e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3808e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3809e1199815SMickaël Salaün 3810e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3811e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3812e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3813e1199815SMickaël Salaün 3814e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3815e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 3816e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3817e1199815SMickaël Salaün 3818e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3819e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3820e1199815SMickaël Salaün 3821e1199815SMickaël Salaün /* Sets rules for the mount points. */ 3822e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 3823e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3824e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3825e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3826e1199815SMickaël Salaün 3827e1199815SMickaël Salaün /* Checks source hierarchy. */ 3828e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 3829e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3830e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3831e1199815SMickaël Salaün 3832e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3833e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3834e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3835e1199815SMickaël Salaün 3836e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3837e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 3838e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 3839e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3840e1199815SMickaël Salaün 3841e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3842e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3843e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3844e1199815SMickaël Salaün 3845e1199815SMickaël Salaün /* Sets a (shared) rule only on the source. */ 3846e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 3847e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3848e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3849e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3850e1199815SMickaël Salaün 3851e1199815SMickaël Salaün /* Checks source hierarchy. */ 3852e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 3853e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3854e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3855e1199815SMickaël Salaün 3856e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3857e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3858e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 3859e1199815SMickaël Salaün 3860e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3861e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 3862e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 3863e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3864e1199815SMickaël Salaün 3865e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3866e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3867e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3868e1199815SMickaël Salaün 3869e1199815SMickaël Salaün /* Sets a (shared) rule only on the destination. */ 3870e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 3871e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3872e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3873e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3874e1199815SMickaël Salaün 3875e1199815SMickaël Salaün /* Checks source hierarchy. */ 3876e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 3877e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3878e1199815SMickaël Salaün 3879e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3880e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 3881e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3882e1199815SMickaël Salaün } 3883e1199815SMickaël Salaün 3884f4056b92SMickaël Salaün TEST_F_FORK(layout1_bind, reparent_cross_mount) 3885f4056b92SMickaël Salaün { 3886f4056b92SMickaël Salaün const struct rule layer1[] = { 3887f4056b92SMickaël Salaün { 3888f4056b92SMickaël Salaün /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 3889f4056b92SMickaël Salaün .path = dir_s2d1, 3890f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 3891f4056b92SMickaël Salaün }, 3892f4056b92SMickaël Salaün { 3893f4056b92SMickaël Salaün .path = bind_dir_s1d3, 3894f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 3895f4056b92SMickaël Salaün }, 3896f4056b92SMickaël Salaün {}, 3897f4056b92SMickaël Salaün }; 3898f4056b92SMickaël Salaün int ruleset_fd = create_ruleset( 3899f4056b92SMickaël Salaün _metadata, 3900f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 3901f4056b92SMickaël Salaün 3902f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3903f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3904f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3905f4056b92SMickaël Salaün 3906f4056b92SMickaël Salaün /* Checks basic denied move. */ 3907f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 3908f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3909f4056b92SMickaël Salaün 3910f4056b92SMickaël Salaün /* Checks real cross-mount move (Landlock is not involved). */ 3911f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 3912f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3913f4056b92SMickaël Salaün 3914f4056b92SMickaël Salaün /* Checks move that will give more accesses. */ 3915f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 3916f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3917f4056b92SMickaël Salaün 3918f4056b92SMickaël Salaün /* Checks legitimate downgrade move. */ 3919f4056b92SMickaël Salaün ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 3920f4056b92SMickaël Salaün } 3921f4056b92SMickaël Salaün 3922e1199815SMickaël Salaün #define LOWER_BASE TMP_DIR "/lower" 3923e1199815SMickaël Salaün #define LOWER_DATA LOWER_BASE "/data" 3924e1199815SMickaël Salaün static const char lower_fl1[] = LOWER_DATA "/fl1"; 3925e1199815SMickaël Salaün static const char lower_dl1[] = LOWER_DATA "/dl1"; 3926e1199815SMickaël Salaün static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 3927e1199815SMickaël Salaün static const char lower_fo1[] = LOWER_DATA "/fo1"; 3928e1199815SMickaël Salaün static const char lower_do1[] = LOWER_DATA "/do1"; 3929e1199815SMickaël Salaün static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 3930e1199815SMickaël Salaün static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 3931e1199815SMickaël Salaün 3932e1199815SMickaël Salaün static const char (*lower_base_files[])[] = { 3933e1199815SMickaël Salaün &lower_fl1, 3934e1199815SMickaël Salaün &lower_fo1, 3935135464f9SMickaël Salaün NULL, 3936e1199815SMickaël Salaün }; 3937e1199815SMickaël Salaün static const char (*lower_base_directories[])[] = { 3938e1199815SMickaël Salaün &lower_dl1, 3939e1199815SMickaël Salaün &lower_do1, 3940135464f9SMickaël Salaün NULL, 3941e1199815SMickaël Salaün }; 3942e1199815SMickaël Salaün static const char (*lower_sub_files[])[] = { 3943e1199815SMickaël Salaün &lower_dl1_fl2, 3944e1199815SMickaël Salaün &lower_do1_fo2, 3945e1199815SMickaël Salaün &lower_do1_fl3, 3946135464f9SMickaël Salaün NULL, 3947e1199815SMickaël Salaün }; 3948e1199815SMickaël Salaün 3949e1199815SMickaël Salaün #define UPPER_BASE TMP_DIR "/upper" 3950e1199815SMickaël Salaün #define UPPER_DATA UPPER_BASE "/data" 3951e1199815SMickaël Salaün #define UPPER_WORK UPPER_BASE "/work" 3952e1199815SMickaël Salaün static const char upper_fu1[] = UPPER_DATA "/fu1"; 3953e1199815SMickaël Salaün static const char upper_du1[] = UPPER_DATA "/du1"; 3954e1199815SMickaël Salaün static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 3955e1199815SMickaël Salaün static const char upper_fo1[] = UPPER_DATA "/fo1"; 3956e1199815SMickaël Salaün static const char upper_do1[] = UPPER_DATA "/do1"; 3957e1199815SMickaël Salaün static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 3958e1199815SMickaël Salaün static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 3959e1199815SMickaël Salaün 3960e1199815SMickaël Salaün static const char (*upper_base_files[])[] = { 3961e1199815SMickaël Salaün &upper_fu1, 3962e1199815SMickaël Salaün &upper_fo1, 3963135464f9SMickaël Salaün NULL, 3964e1199815SMickaël Salaün }; 3965e1199815SMickaël Salaün static const char (*upper_base_directories[])[] = { 3966e1199815SMickaël Salaün &upper_du1, 3967e1199815SMickaël Salaün &upper_do1, 3968135464f9SMickaël Salaün NULL, 3969e1199815SMickaël Salaün }; 3970e1199815SMickaël Salaün static const char (*upper_sub_files[])[] = { 3971e1199815SMickaël Salaün &upper_du1_fu2, 3972e1199815SMickaël Salaün &upper_do1_fo2, 3973e1199815SMickaël Salaün &upper_do1_fu3, 3974135464f9SMickaël Salaün NULL, 3975e1199815SMickaël Salaün }; 3976e1199815SMickaël Salaün 3977e1199815SMickaël Salaün #define MERGE_BASE TMP_DIR "/merge" 3978e1199815SMickaël Salaün #define MERGE_DATA MERGE_BASE "/data" 3979e1199815SMickaël Salaün static const char merge_fl1[] = MERGE_DATA "/fl1"; 3980e1199815SMickaël Salaün static const char merge_dl1[] = MERGE_DATA "/dl1"; 3981e1199815SMickaël Salaün static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 3982e1199815SMickaël Salaün static const char merge_fu1[] = MERGE_DATA "/fu1"; 3983e1199815SMickaël Salaün static const char merge_du1[] = MERGE_DATA "/du1"; 3984e1199815SMickaël Salaün static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 3985e1199815SMickaël Salaün static const char merge_fo1[] = MERGE_DATA "/fo1"; 3986e1199815SMickaël Salaün static const char merge_do1[] = MERGE_DATA "/do1"; 3987e1199815SMickaël Salaün static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 3988e1199815SMickaël Salaün static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 3989e1199815SMickaël Salaün static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 3990e1199815SMickaël Salaün 3991e1199815SMickaël Salaün static const char (*merge_base_files[])[] = { 3992e1199815SMickaël Salaün &merge_fl1, 3993e1199815SMickaël Salaün &merge_fu1, 3994e1199815SMickaël Salaün &merge_fo1, 3995135464f9SMickaël Salaün NULL, 3996e1199815SMickaël Salaün }; 3997e1199815SMickaël Salaün static const char (*merge_base_directories[])[] = { 3998e1199815SMickaël Salaün &merge_dl1, 3999e1199815SMickaël Salaün &merge_du1, 4000e1199815SMickaël Salaün &merge_do1, 4001135464f9SMickaël Salaün NULL, 4002e1199815SMickaël Salaün }; 4003e1199815SMickaël Salaün static const char (*merge_sub_files[])[] = { 4004371183faSMickaël Salaün &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 4005371183faSMickaël Salaün &merge_do1_fl3, &merge_do1_fu3, NULL, 4006e1199815SMickaël Salaün }; 4007e1199815SMickaël Salaün 4008e1199815SMickaël Salaün /* 4009e1199815SMickaël Salaün * layout2_overlay hierarchy: 4010e1199815SMickaël Salaün * 4011e1199815SMickaël Salaün * tmp 4012e1199815SMickaël Salaün * ├── lower 4013e1199815SMickaël Salaün * │ └── data 4014e1199815SMickaël Salaün * │ ├── dl1 4015e1199815SMickaël Salaün * │ │ └── fl2 4016e1199815SMickaël Salaün * │ ├── do1 4017e1199815SMickaël Salaün * │ │ ├── fl3 4018e1199815SMickaël Salaün * │ │ └── fo2 4019e1199815SMickaël Salaün * │ ├── fl1 4020e1199815SMickaël Salaün * │ └── fo1 4021e1199815SMickaël Salaün * ├── merge 4022e1199815SMickaël Salaün * │ └── data 4023e1199815SMickaël Salaün * │ ├── dl1 4024e1199815SMickaël Salaün * │ │ └── fl2 4025e1199815SMickaël Salaün * │ ├── do1 4026e1199815SMickaël Salaün * │ │ ├── fl3 4027e1199815SMickaël Salaün * │ │ ├── fo2 4028e1199815SMickaël Salaün * │ │ └── fu3 4029e1199815SMickaël Salaün * │ ├── du1 4030e1199815SMickaël Salaün * │ │ └── fu2 4031e1199815SMickaël Salaün * │ ├── fl1 4032e1199815SMickaël Salaün * │ ├── fo1 4033e1199815SMickaël Salaün * │ └── fu1 4034e1199815SMickaël Salaün * └── upper 4035e1199815SMickaël Salaün * ├── data 4036e1199815SMickaël Salaün * │ ├── do1 4037e1199815SMickaël Salaün * │ │ ├── fo2 4038e1199815SMickaël Salaün * │ │ └── fu3 4039e1199815SMickaël Salaün * │ ├── du1 4040e1199815SMickaël Salaün * │ │ └── fu2 4041e1199815SMickaël Salaün * │ ├── fo1 4042e1199815SMickaël Salaün * │ └── fu1 4043e1199815SMickaël Salaün * └── work 4044e1199815SMickaël Salaün * └── work 4045e1199815SMickaël Salaün */ 4046e1199815SMickaël Salaün 40474598d9abSMickaël Salaün /* clang-format off */ 40484598d9abSMickaël Salaün FIXTURE(layout2_overlay) {}; 40494598d9abSMickaël Salaün /* clang-format on */ 4050e1199815SMickaël Salaün 4051e1199815SMickaël Salaün FIXTURE_SETUP(layout2_overlay) 4052e1199815SMickaël Salaün { 4053366617a6SJeff Xu if (!supports_overlayfs()) 4054366617a6SJeff Xu SKIP(return, "overlayfs is not supported"); 4055366617a6SJeff Xu 4056e1199815SMickaël Salaün prepare_layout(_metadata); 4057e1199815SMickaël Salaün 4058e1199815SMickaël Salaün create_directory(_metadata, LOWER_BASE); 4059e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4060e1199815SMickaël Salaün /* Creates tmpfs mount points to get deterministic overlayfs. */ 4061e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700")); 4062e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4063e1199815SMickaël Salaün create_file(_metadata, lower_fl1); 4064e1199815SMickaël Salaün create_file(_metadata, lower_dl1_fl2); 4065e1199815SMickaël Salaün create_file(_metadata, lower_fo1); 4066e1199815SMickaël Salaün create_file(_metadata, lower_do1_fo2); 4067e1199815SMickaël Salaün create_file(_metadata, lower_do1_fl3); 4068e1199815SMickaël Salaün 4069e1199815SMickaël Salaün create_directory(_metadata, UPPER_BASE); 4070e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4071e1199815SMickaël Salaün ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700")); 4072e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4073e1199815SMickaël Salaün create_file(_metadata, upper_fu1); 4074e1199815SMickaël Salaün create_file(_metadata, upper_du1_fu2); 4075e1199815SMickaël Salaün create_file(_metadata, upper_fo1); 4076e1199815SMickaël Salaün create_file(_metadata, upper_do1_fo2); 4077e1199815SMickaël Salaün create_file(_metadata, upper_do1_fu3); 4078e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 4079e1199815SMickaël Salaün 4080e1199815SMickaël Salaün create_directory(_metadata, MERGE_DATA); 4081e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4082e1199815SMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 4083e1199815SMickaël Salaün ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 4084371183faSMickaël Salaün "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 4085e1199815SMickaël Salaün ",workdir=" UPPER_WORK)); 4086e1199815SMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 4087e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4088e1199815SMickaël Salaün } 4089e1199815SMickaël Salaün 4090e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout2_overlay) 4091e1199815SMickaël Salaün { 4092366617a6SJeff Xu if (!supports_overlayfs()) 4093366617a6SJeff Xu SKIP(return, "overlayfs is not supported"); 4094366617a6SJeff Xu 4095e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fl3)); 4096e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 4097e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fl1)); 4098e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fo2)); 4099e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fo1)); 4100e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4101e1199815SMickaël Salaün EXPECT_EQ(0, umount(LOWER_BASE)); 4102e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4103e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(LOWER_BASE)); 4104e1199815SMickaël Salaün 4105e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fu3)); 4106e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_du1_fu2)); 4107e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fu1)); 4108e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fo2)); 4109e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fo1)); 4110e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 4111e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4112e1199815SMickaël Salaün EXPECT_EQ(0, umount(UPPER_BASE)); 4113e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4114e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_BASE)); 4115e1199815SMickaël Salaün 4116e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4117e1199815SMickaël Salaün EXPECT_EQ(0, umount(MERGE_DATA)); 4118e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4119e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(MERGE_DATA)); 4120e1199815SMickaël Salaün 4121e1199815SMickaël Salaün cleanup_layout(_metadata); 4122e1199815SMickaël Salaün } 4123e1199815SMickaël Salaün 4124e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, no_restriction) 4125e1199815SMickaël Salaün { 4126366617a6SJeff Xu if (!supports_overlayfs()) 4127366617a6SJeff Xu SKIP(return, "overlayfs is not supported"); 4128366617a6SJeff Xu 4129e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 4130e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 4131e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 4132e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 4133e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 4134e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 4135e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 4136e1199815SMickaël Salaün 4137e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 4138e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 4139e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 4140e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 4141e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 4142e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 4143e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 4144e1199815SMickaël Salaün 4145e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 4146e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 4147e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 4148e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 4149e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 4150e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 4151e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 4152e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 4153e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 4154e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 4155e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 4156e1199815SMickaël Salaün } 4157e1199815SMickaël Salaün 4158e1199815SMickaël Salaün #define for_each_path(path_list, path_entry, i) \ 4159e1199815SMickaël Salaün for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 4160e1199815SMickaël Salaün path_entry = *path_list[++i]) 4161e1199815SMickaël Salaün 4162e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, same_content_different_file) 4163e1199815SMickaël Salaün { 4164e1199815SMickaël Salaün /* Sets access right on parent directories of both layers. */ 4165e1199815SMickaël Salaün const struct rule layer1_base[] = { 4166e1199815SMickaël Salaün { 4167e1199815SMickaël Salaün .path = LOWER_BASE, 4168e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4169e1199815SMickaël Salaün }, 4170e1199815SMickaël Salaün { 4171e1199815SMickaël Salaün .path = UPPER_BASE, 4172e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4173e1199815SMickaël Salaün }, 4174e1199815SMickaël Salaün { 4175e1199815SMickaël Salaün .path = MERGE_BASE, 4176e1199815SMickaël Salaün .access = ACCESS_RW, 4177e1199815SMickaël Salaün }, 4178135464f9SMickaël Salaün {}, 4179e1199815SMickaël Salaün }; 4180e1199815SMickaël Salaün const struct rule layer2_data[] = { 4181e1199815SMickaël Salaün { 4182e1199815SMickaël Salaün .path = LOWER_DATA, 4183e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4184e1199815SMickaël Salaün }, 4185e1199815SMickaël Salaün { 4186e1199815SMickaël Salaün .path = UPPER_DATA, 4187e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4188e1199815SMickaël Salaün }, 4189e1199815SMickaël Salaün { 4190e1199815SMickaël Salaün .path = MERGE_DATA, 4191e1199815SMickaël Salaün .access = ACCESS_RW, 4192e1199815SMickaël Salaün }, 4193135464f9SMickaël Salaün {}, 4194e1199815SMickaël Salaün }; 4195e1199815SMickaël Salaün /* Sets access right on directories inside both layers. */ 4196e1199815SMickaël Salaün const struct rule layer3_subdirs[] = { 4197e1199815SMickaël Salaün { 4198e1199815SMickaël Salaün .path = lower_dl1, 4199e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4200e1199815SMickaël Salaün }, 4201e1199815SMickaël Salaün { 4202e1199815SMickaël Salaün .path = lower_do1, 4203e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4204e1199815SMickaël Salaün }, 4205e1199815SMickaël Salaün { 4206e1199815SMickaël Salaün .path = upper_du1, 4207e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4208e1199815SMickaël Salaün }, 4209e1199815SMickaël Salaün { 4210e1199815SMickaël Salaün .path = upper_do1, 4211e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4212e1199815SMickaël Salaün }, 4213e1199815SMickaël Salaün { 4214e1199815SMickaël Salaün .path = merge_dl1, 4215e1199815SMickaël Salaün .access = ACCESS_RW, 4216e1199815SMickaël Salaün }, 4217e1199815SMickaël Salaün { 4218e1199815SMickaël Salaün .path = merge_du1, 4219e1199815SMickaël Salaün .access = ACCESS_RW, 4220e1199815SMickaël Salaün }, 4221e1199815SMickaël Salaün { 4222e1199815SMickaël Salaün .path = merge_do1, 4223e1199815SMickaël Salaün .access = ACCESS_RW, 4224e1199815SMickaël Salaün }, 4225135464f9SMickaël Salaün {}, 4226e1199815SMickaël Salaün }; 4227e1199815SMickaël Salaün /* Tighten access rights to the files. */ 4228e1199815SMickaël Salaün const struct rule layer4_files[] = { 4229e1199815SMickaël Salaün { 4230e1199815SMickaël Salaün .path = lower_dl1_fl2, 4231e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4232e1199815SMickaël Salaün }, 4233e1199815SMickaël Salaün { 4234e1199815SMickaël Salaün .path = lower_do1_fo2, 4235e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4236e1199815SMickaël Salaün }, 4237e1199815SMickaël Salaün { 4238e1199815SMickaël Salaün .path = lower_do1_fl3, 4239e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4240e1199815SMickaël Salaün }, 4241e1199815SMickaël Salaün { 4242e1199815SMickaël Salaün .path = upper_du1_fu2, 4243e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4244e1199815SMickaël Salaün }, 4245e1199815SMickaël Salaün { 4246e1199815SMickaël Salaün .path = upper_do1_fo2, 4247e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4248e1199815SMickaël Salaün }, 4249e1199815SMickaël Salaün { 4250e1199815SMickaël Salaün .path = upper_do1_fu3, 4251e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4252e1199815SMickaël Salaün }, 4253e1199815SMickaël Salaün { 4254e1199815SMickaël Salaün .path = merge_dl1_fl2, 4255e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4256e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4257e1199815SMickaël Salaün }, 4258e1199815SMickaël Salaün { 4259e1199815SMickaël Salaün .path = merge_du1_fu2, 4260e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4261e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4262e1199815SMickaël Salaün }, 4263e1199815SMickaël Salaün { 4264e1199815SMickaël Salaün .path = merge_do1_fo2, 4265e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4266e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4267e1199815SMickaël Salaün }, 4268e1199815SMickaël Salaün { 4269e1199815SMickaël Salaün .path = merge_do1_fl3, 4270e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4271e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4272e1199815SMickaël Salaün }, 4273e1199815SMickaël Salaün { 4274e1199815SMickaël Salaün .path = merge_do1_fu3, 4275e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4276e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4277e1199815SMickaël Salaün }, 4278135464f9SMickaël Salaün {}, 4279e1199815SMickaël Salaün }; 4280e1199815SMickaël Salaün const struct rule layer5_merge_only[] = { 4281e1199815SMickaël Salaün { 4282e1199815SMickaël Salaün .path = MERGE_DATA, 4283e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4284e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4285e1199815SMickaël Salaün }, 4286135464f9SMickaël Salaün {}, 4287e1199815SMickaël Salaün }; 4288e1199815SMickaël Salaün int ruleset_fd; 4289e1199815SMickaël Salaün size_t i; 4290e1199815SMickaël Salaün const char *path_entry; 4291e1199815SMickaël Salaün 4292366617a6SJeff Xu if (!supports_overlayfs()) 4293366617a6SJeff Xu SKIP(return, "overlayfs is not supported"); 4294366617a6SJeff Xu 4295e1199815SMickaël Salaün /* Sets rules on base directories (i.e. outside overlay scope). */ 4296e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 4297e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4298e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4299e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4300e1199815SMickaël Salaün 4301e1199815SMickaël Salaün /* Checks lower layer. */ 4302e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 4303e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4304e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4305e1199815SMickaël Salaün } 4306e1199815SMickaël Salaün for_each_path(lower_base_directories, path_entry, i) { 4307371183faSMickaël Salaün ASSERT_EQ(EACCES, 4308371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4309e1199815SMickaël Salaün } 4310e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 4311e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4312e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4313e1199815SMickaël Salaün } 4314e1199815SMickaël Salaün /* Checks upper layer. */ 4315e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 4316e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4317e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4318e1199815SMickaël Salaün } 4319e1199815SMickaël Salaün for_each_path(upper_base_directories, path_entry, i) { 4320371183faSMickaël Salaün ASSERT_EQ(EACCES, 4321371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4322e1199815SMickaël Salaün } 4323e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 4324e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4325e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4326e1199815SMickaël Salaün } 4327e1199815SMickaël Salaün /* 4328e1199815SMickaël Salaün * Checks that access rights are independent from the lower and upper 4329e1199815SMickaël Salaün * layers: write access to upper files viewed through the merge point 4330e1199815SMickaël Salaün * is still allowed, and write access to lower file viewed (and copied) 4331e1199815SMickaël Salaün * through the merge point is still allowed. 4332e1199815SMickaël Salaün */ 4333e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4334e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4335e1199815SMickaël Salaün } 4336e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4337e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4338e1199815SMickaël Salaün } 4339e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4340e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4341e1199815SMickaël Salaün } 4342e1199815SMickaël Salaün 4343e1199815SMickaël Salaün /* Sets rules on data directories (i.e. inside overlay scope). */ 4344e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 4345e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4346e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4347e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4348e1199815SMickaël Salaün 4349e1199815SMickaël Salaün /* Checks merge. */ 4350e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4351e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4352e1199815SMickaël Salaün } 4353e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4354e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4355e1199815SMickaël Salaün } 4356e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4357e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4358e1199815SMickaël Salaün } 4359e1199815SMickaël Salaün 4360e1199815SMickaël Salaün /* Same checks with tighter rules. */ 4361e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 4362e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4363e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4364e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4365e1199815SMickaël Salaün 4366e1199815SMickaël Salaün /* Checks changes for lower layer. */ 4367e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 4368e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4369e1199815SMickaël Salaün } 4370e1199815SMickaël Salaün /* Checks changes for upper layer. */ 4371e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 4372e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4373e1199815SMickaël Salaün } 4374e1199815SMickaël Salaün /* Checks all merge accesses. */ 4375e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4376e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4377e1199815SMickaël Salaün } 4378e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4379e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4380e1199815SMickaël Salaün } 4381e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4382e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4383e1199815SMickaël Salaün } 4384e1199815SMickaël Salaün 4385e1199815SMickaël Salaün /* Sets rules directly on overlayed files. */ 4386e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 4387e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4388e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4389e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4390e1199815SMickaël Salaün 4391e1199815SMickaël Salaün /* Checks unchanged accesses on lower layer. */ 4392e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 4393e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4394e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4395e1199815SMickaël Salaün } 4396e1199815SMickaël Salaün /* Checks unchanged accesses on upper layer. */ 4397e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 4398e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4399e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4400e1199815SMickaël Salaün } 4401e1199815SMickaël Salaün /* Checks all merge accesses. */ 4402e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4403e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4404e1199815SMickaël Salaün } 4405e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4406371183faSMickaël Salaün ASSERT_EQ(EACCES, 4407371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4408e1199815SMickaël Salaün } 4409e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4410e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4411e1199815SMickaël Salaün } 4412e1199815SMickaël Salaün 4413e1199815SMickaël Salaün /* Only allowes access to the merge hierarchy. */ 4414e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 4415e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4416e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4417e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4418e1199815SMickaël Salaün 4419e1199815SMickaël Salaün /* Checks new accesses on lower layer. */ 4420e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 4421e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4422e1199815SMickaël Salaün } 4423e1199815SMickaël Salaün /* Checks new accesses on upper layer. */ 4424e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 4425e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4426e1199815SMickaël Salaün } 4427e1199815SMickaël Salaün /* Checks all merge accesses. */ 4428e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4429e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4430e1199815SMickaël Salaün } 4431e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4432371183faSMickaël Salaün ASSERT_EQ(EACCES, 4433371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4434e1199815SMickaël Salaün } 4435e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4436e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4437e1199815SMickaël Salaün } 4438e1199815SMickaël Salaün } 4439e1199815SMickaël Salaün 4440e1199815SMickaël Salaün TEST_HARNESS_MAIN 4441