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> 1335ca4239SMickaël Salaün #include <linux/magic.h> 14e1199815SMickaël Salaün #include <sched.h> 15366617a6SJeff Xu #include <stdio.h> 16e1199815SMickaël Salaün #include <string.h> 17e1199815SMickaël Salaün #include <sys/capability.h> 18e1199815SMickaël Salaün #include <sys/mount.h> 19e1199815SMickaël Salaün #include <sys/prctl.h> 20e1199815SMickaël Salaün #include <sys/sendfile.h> 21e1199815SMickaël Salaün #include <sys/stat.h> 22e1199815SMickaël Salaün #include <sys/sysmacros.h> 2335ca4239SMickaël Salaün #include <sys/vfs.h> 24e1199815SMickaël Salaün #include <unistd.h> 25e1199815SMickaël Salaün 26e1199815SMickaël Salaün #include "common.h" 27e1199815SMickaël Salaün 2887129ef1SMickaël Salaün #ifndef renameat2 2987129ef1SMickaël Salaün int renameat2(int olddirfd, const char *oldpath, int newdirfd, 3087129ef1SMickaël Salaün const char *newpath, unsigned int flags) 3187129ef1SMickaël Salaün { 3287129ef1SMickaël Salaün return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath, 3387129ef1SMickaël Salaün flags); 3487129ef1SMickaël Salaün } 3587129ef1SMickaël Salaün #endif 3687129ef1SMickaël Salaün 3787129ef1SMickaël Salaün #ifndef RENAME_EXCHANGE 3887129ef1SMickaël Salaün #define RENAME_EXCHANGE (1 << 1) 3987129ef1SMickaël Salaün #endif 4087129ef1SMickaël Salaün 41e1199815SMickaël Salaün #define TMP_DIR "tmp" 42e1199815SMickaël Salaün #define BINARY_PATH "./true" 43e1199815SMickaël Salaün 44e1199815SMickaël Salaün /* Paths (sibling number and depth) */ 45e1199815SMickaël Salaün static const char dir_s1d1[] = TMP_DIR "/s1d1"; 46e1199815SMickaël Salaün static const char file1_s1d1[] = TMP_DIR "/s1d1/f1"; 47e1199815SMickaël Salaün static const char file2_s1d1[] = TMP_DIR "/s1d1/f2"; 48e1199815SMickaël Salaün static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2"; 49e1199815SMickaël Salaün static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1"; 50e1199815SMickaël Salaün static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2"; 51e1199815SMickaël Salaün static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3"; 52e1199815SMickaël Salaün static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1"; 53e1199815SMickaël Salaün static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2"; 54e1199815SMickaël Salaün 55e1199815SMickaël Salaün static const char dir_s2d1[] = TMP_DIR "/s2d1"; 56e1199815SMickaël Salaün static const char file1_s2d1[] = TMP_DIR "/s2d1/f1"; 57e1199815SMickaël Salaün static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2"; 58e1199815SMickaël Salaün static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1"; 59e1199815SMickaël Salaün static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3"; 60e1199815SMickaël Salaün static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1"; 61e1199815SMickaël Salaün static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; 62e1199815SMickaël Salaün 63e1199815SMickaël Salaün static const char dir_s3d1[] = TMP_DIR "/s3d1"; 64225351abSGünther Noack static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; 65e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 66e1199815SMickaël Salaün static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; 67e1199815SMickaël Salaün static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; 68e1199815SMickaël Salaün 69e1199815SMickaël Salaün /* 70e1199815SMickaël Salaün * layout1 hierarchy: 71e1199815SMickaël Salaün * 72e1199815SMickaël Salaün * tmp 73e1199815SMickaël Salaün * ├── s1d1 74e1199815SMickaël Salaün * │ ├── f1 75e1199815SMickaël Salaün * │ ├── f2 76e1199815SMickaël Salaün * │ └── s1d2 77e1199815SMickaël Salaün * │ ├── f1 78e1199815SMickaël Salaün * │ ├── f2 79e1199815SMickaël Salaün * │ └── s1d3 80e1199815SMickaël Salaün * │ ├── f1 81e1199815SMickaël Salaün * │ └── f2 82e1199815SMickaël Salaün * ├── s2d1 83e1199815SMickaël Salaün * │ ├── f1 84e1199815SMickaël Salaün * │ └── s2d2 85e1199815SMickaël Salaün * │ ├── f1 86e1199815SMickaël Salaün * │ └── s2d3 87e1199815SMickaël Salaün * │ ├── f1 88e1199815SMickaël Salaün * │ └── f2 89e1199815SMickaël Salaün * └── s3d1 90225351abSGünther Noack * ├── f1 91e1199815SMickaël Salaün * └── s3d2 92e1199815SMickaël Salaün * └── s3d3 93e1199815SMickaël Salaün */ 94e1199815SMickaël Salaün 95366617a6SJeff Xu static bool fgrep(FILE *const inf, const char *const str) 96366617a6SJeff Xu { 97366617a6SJeff Xu char line[32]; 98366617a6SJeff Xu const int slen = strlen(str); 99366617a6SJeff Xu 100366617a6SJeff Xu while (!feof(inf)) { 101366617a6SJeff Xu if (!fgets(line, sizeof(line), inf)) 102366617a6SJeff Xu break; 103366617a6SJeff Xu if (strncmp(line, str, slen)) 104366617a6SJeff Xu continue; 105366617a6SJeff Xu 106366617a6SJeff Xu return true; 107366617a6SJeff Xu } 108366617a6SJeff Xu 109366617a6SJeff Xu return false; 110366617a6SJeff Xu } 111366617a6SJeff Xu 1123de64b65SMickaël Salaün static bool supports_filesystem(const char *const filesystem) 113366617a6SJeff Xu { 1143de64b65SMickaël Salaün char str[32]; 1153de64b65SMickaël Salaün int len; 1162a201549SDing Xiang bool res = true; 117366617a6SJeff Xu FILE *const inf = fopen("/proc/filesystems", "r"); 118366617a6SJeff Xu 119366617a6SJeff Xu /* 120366617a6SJeff Xu * Consider that the filesystem is supported if we cannot get the 121366617a6SJeff Xu * supported ones. 122366617a6SJeff Xu */ 123366617a6SJeff Xu if (!inf) 124366617a6SJeff Xu return true; 125366617a6SJeff Xu 12604f9070eSMickaël Salaün /* filesystem can be null for bind mounts. */ 12704f9070eSMickaël Salaün if (!filesystem) 1282a201549SDing Xiang goto out; 12904f9070eSMickaël Salaün 1303de64b65SMickaël Salaün len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); 1313de64b65SMickaël Salaün if (len >= sizeof(str)) 1323de64b65SMickaël Salaün /* Ignores too-long filesystem names. */ 1332a201549SDing Xiang goto out; 1343de64b65SMickaël Salaün 1353de64b65SMickaël Salaün res = fgrep(inf, str); 1362a201549SDing Xiang 1372a201549SDing Xiang out: 138366617a6SJeff Xu fclose(inf); 139366617a6SJeff Xu return res; 140366617a6SJeff Xu } 141366617a6SJeff Xu 14235ca4239SMickaël Salaün static bool cwd_matches_fs(unsigned int fs_magic) 14335ca4239SMickaël Salaün { 14435ca4239SMickaël Salaün struct statfs statfs_buf; 14535ca4239SMickaël Salaün 14635ca4239SMickaël Salaün if (!fs_magic) 14735ca4239SMickaël Salaün return true; 14835ca4239SMickaël Salaün 14935ca4239SMickaël Salaün if (statfs(".", &statfs_buf)) 15035ca4239SMickaël Salaün return true; 15135ca4239SMickaël Salaün 15235ca4239SMickaël Salaün return statfs_buf.f_type == fs_magic; 15335ca4239SMickaël Salaün } 15435ca4239SMickaël Salaün 155e1199815SMickaël Salaün static void mkdir_parents(struct __test_metadata *const _metadata, 156e1199815SMickaël Salaün const char *const path) 157e1199815SMickaël Salaün { 158e1199815SMickaël Salaün char *walker; 159e1199815SMickaël Salaün const char *parent; 160e1199815SMickaël Salaün int i, err; 161e1199815SMickaël Salaün 162e1199815SMickaël Salaün ASSERT_NE(path[0], '\0'); 163e1199815SMickaël Salaün walker = strdup(path); 164e1199815SMickaël Salaün ASSERT_NE(NULL, walker); 165e1199815SMickaël Salaün parent = walker; 166e1199815SMickaël Salaün for (i = 1; walker[i]; i++) { 167e1199815SMickaël Salaün if (walker[i] != '/') 168e1199815SMickaël Salaün continue; 169e1199815SMickaël Salaün walker[i] = '\0'; 170e1199815SMickaël Salaün err = mkdir(parent, 0700); 171371183faSMickaël Salaün ASSERT_FALSE(err && errno != EEXIST) 172371183faSMickaël Salaün { 173371183faSMickaël Salaün TH_LOG("Failed to create directory \"%s\": %s", parent, 174371183faSMickaël Salaün strerror(errno)); 175e1199815SMickaël Salaün } 176e1199815SMickaël Salaün walker[i] = '/'; 177e1199815SMickaël Salaün } 178e1199815SMickaël Salaün free(walker); 179e1199815SMickaël Salaün } 180e1199815SMickaël Salaün 181e1199815SMickaël Salaün static void create_directory(struct __test_metadata *const _metadata, 182e1199815SMickaël Salaün const char *const path) 183e1199815SMickaël Salaün { 184e1199815SMickaël Salaün mkdir_parents(_metadata, path); 185371183faSMickaël Salaün ASSERT_EQ(0, mkdir(path, 0700)) 186371183faSMickaël Salaün { 187e1199815SMickaël Salaün TH_LOG("Failed to create directory \"%s\": %s", path, 188e1199815SMickaël Salaün strerror(errno)); 189e1199815SMickaël Salaün } 190e1199815SMickaël Salaün } 191e1199815SMickaël Salaün 192e1199815SMickaël Salaün static void create_file(struct __test_metadata *const _metadata, 193e1199815SMickaël Salaün const char *const path) 194e1199815SMickaël Salaün { 195e1199815SMickaël Salaün mkdir_parents(_metadata, path); 196371183faSMickaël Salaün ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0)) 197371183faSMickaël Salaün { 198e1199815SMickaël Salaün TH_LOG("Failed to create file \"%s\": %s", path, 199e1199815SMickaël Salaün strerror(errno)); 200e1199815SMickaël Salaün } 201e1199815SMickaël Salaün } 202e1199815SMickaël Salaün 203e1199815SMickaël Salaün static int remove_path(const char *const path) 204e1199815SMickaël Salaün { 205e1199815SMickaël Salaün char *walker; 206e1199815SMickaël Salaün int i, ret, err = 0; 207e1199815SMickaël Salaün 208e1199815SMickaël Salaün walker = strdup(path); 209e1199815SMickaël Salaün if (!walker) { 210e1199815SMickaël Salaün err = ENOMEM; 211e1199815SMickaël Salaün goto out; 212e1199815SMickaël Salaün } 213e1199815SMickaël Salaün if (unlink(path) && rmdir(path)) { 214f4056b92SMickaël Salaün if (errno != ENOENT && errno != ENOTDIR) 215e1199815SMickaël Salaün err = errno; 216e1199815SMickaël Salaün goto out; 217e1199815SMickaël Salaün } 218e1199815SMickaël Salaün for (i = strlen(walker); i > 0; i--) { 219e1199815SMickaël Salaün if (walker[i] != '/') 220e1199815SMickaël Salaün continue; 221e1199815SMickaël Salaün walker[i] = '\0'; 222e1199815SMickaël Salaün ret = rmdir(walker); 223e1199815SMickaël Salaün if (ret) { 224e1199815SMickaël Salaün if (errno != ENOTEMPTY && errno != EBUSY) 225e1199815SMickaël Salaün err = errno; 226e1199815SMickaël Salaün goto out; 227e1199815SMickaël Salaün } 228e1199815SMickaël Salaün if (strcmp(walker, TMP_DIR) == 0) 229e1199815SMickaël Salaün goto out; 230e1199815SMickaël Salaün } 231e1199815SMickaël Salaün 232e1199815SMickaël Salaün out: 233e1199815SMickaël Salaün free(walker); 234e1199815SMickaël Salaün return err; 235e1199815SMickaël Salaün } 236e1199815SMickaël Salaün 23755ab3fbeSMickaël Salaün struct mnt_opt { 23855ab3fbeSMickaël Salaün const char *const source; 23955ab3fbeSMickaël Salaün const char *const type; 24055ab3fbeSMickaël Salaün const unsigned long flags; 24155ab3fbeSMickaël Salaün const char *const data; 24255ab3fbeSMickaël Salaün }; 24355ab3fbeSMickaël Salaün 244*854357dbSHu Yadi #define MNT_TMP_DATA "size=4m,mode=700" 245*854357dbSHu Yadi 246*854357dbSHu Yadi static const struct mnt_opt mnt_tmp = { 24755ab3fbeSMickaël Salaün .type = "tmpfs", 248*854357dbSHu Yadi .data = MNT_TMP_DATA, 24955ab3fbeSMickaël Salaün }; 25055ab3fbeSMickaël Salaün 25155ab3fbeSMickaël Salaün static int mount_opt(const struct mnt_opt *const mnt, const char *const target) 25255ab3fbeSMickaël Salaün { 25355ab3fbeSMickaël Salaün return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags, 25455ab3fbeSMickaël Salaün mnt->data); 25555ab3fbeSMickaël Salaün } 25655ab3fbeSMickaël Salaün 25755ab3fbeSMickaël Salaün static void prepare_layout_opt(struct __test_metadata *const _metadata, 25855ab3fbeSMickaël Salaün const struct mnt_opt *const mnt) 259e1199815SMickaël Salaün { 260e1199815SMickaël Salaün disable_caps(_metadata); 261e1199815SMickaël Salaün umask(0077); 262e1199815SMickaël Salaün create_directory(_metadata, TMP_DIR); 263e1199815SMickaël Salaün 264e1199815SMickaël Salaün /* 265e1199815SMickaël Salaün * Do not pollute the rest of the system: creates a private mount point 266e1199815SMickaël Salaün * for tests relying on pivot_root(2) and move_mount(2). 267e1199815SMickaël Salaün */ 268e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 26904f9070eSMickaël Salaün ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); 27055ab3fbeSMickaël Salaün ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) 27155ab3fbeSMickaël Salaün { 27255ab3fbeSMickaël Salaün TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, 27355ab3fbeSMickaël Salaün strerror(errno)); 27455ab3fbeSMickaël Salaün /* 27555ab3fbeSMickaël Salaün * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() 27655ab3fbeSMickaël Salaün * failed, so we need to explicitly do a minimal cleanup to 27755ab3fbeSMickaël Salaün * avoid cascading errors with other tests that don't depend on 27855ab3fbeSMickaël Salaün * the same filesystem. 27955ab3fbeSMickaël Salaün */ 28055ab3fbeSMickaël Salaün remove_path(TMP_DIR); 28155ab3fbeSMickaël Salaün } 282e1199815SMickaël Salaün ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); 283e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 284e1199815SMickaël Salaün } 285e1199815SMickaël Salaün 28655ab3fbeSMickaël Salaün static void prepare_layout(struct __test_metadata *const _metadata) 28755ab3fbeSMickaël Salaün { 28855ab3fbeSMickaël Salaün prepare_layout_opt(_metadata, &mnt_tmp); 28955ab3fbeSMickaël Salaün } 29055ab3fbeSMickaël Salaün 291e1199815SMickaël Salaün static void cleanup_layout(struct __test_metadata *const _metadata) 292e1199815SMickaël Salaün { 293e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 294e1199815SMickaël Salaün EXPECT_EQ(0, umount(TMP_DIR)); 295e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 296e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(TMP_DIR)); 297e1199815SMickaël Salaün } 298e1199815SMickaël Salaün 299592efeb4SMickaël Salaün /* clang-format off */ 300592efeb4SMickaël Salaün FIXTURE(layout0) {}; 301592efeb4SMickaël Salaün /* clang-format on */ 302592efeb4SMickaël Salaün 303592efeb4SMickaël Salaün FIXTURE_SETUP(layout0) 304592efeb4SMickaël Salaün { 305592efeb4SMickaël Salaün prepare_layout(_metadata); 306592efeb4SMickaël Salaün } 307592efeb4SMickaël Salaün 308592efeb4SMickaël Salaün FIXTURE_TEARDOWN(layout0) 309592efeb4SMickaël Salaün { 310592efeb4SMickaël Salaün cleanup_layout(_metadata); 311592efeb4SMickaël Salaün } 312592efeb4SMickaël Salaün 313e1199815SMickaël Salaün static void create_layout1(struct __test_metadata *const _metadata) 314e1199815SMickaël Salaün { 315e1199815SMickaël Salaün create_file(_metadata, file1_s1d1); 316e1199815SMickaël Salaün create_file(_metadata, file1_s1d2); 317e1199815SMickaël Salaün create_file(_metadata, file1_s1d3); 318e1199815SMickaël Salaün create_file(_metadata, file2_s1d1); 319e1199815SMickaël Salaün create_file(_metadata, file2_s1d2); 320e1199815SMickaël Salaün create_file(_metadata, file2_s1d3); 321e1199815SMickaël Salaün 322e1199815SMickaël Salaün create_file(_metadata, file1_s2d1); 323e1199815SMickaël Salaün create_file(_metadata, file1_s2d2); 324e1199815SMickaël Salaün create_file(_metadata, file1_s2d3); 325e1199815SMickaël Salaün create_file(_metadata, file2_s2d3); 326e1199815SMickaël Salaün 327225351abSGünther Noack create_file(_metadata, file1_s3d1); 328e1199815SMickaël Salaün create_directory(_metadata, dir_s3d2); 329e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 33055ab3fbeSMickaël Salaün ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); 331e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 332e1199815SMickaël Salaün 333e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); 334e1199815SMickaël Salaün } 335e1199815SMickaël Salaün 336e1199815SMickaël Salaün static void remove_layout1(struct __test_metadata *const _metadata) 337e1199815SMickaël Salaün { 338e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d3)); 339e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d2)); 340e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s1d1)); 341e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d3)); 342e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d2)); 343e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s1d1)); 34404f9070eSMickaël Salaün EXPECT_EQ(0, remove_path(dir_s1d3)); 345e1199815SMickaël Salaün 346e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file2_s2d3)); 347e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d3)); 348e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d2)); 349e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(file1_s2d1)); 35004f9070eSMickaël Salaün EXPECT_EQ(0, remove_path(dir_s2d2)); 351e1199815SMickaël Salaün 352225351abSGünther Noack EXPECT_EQ(0, remove_path(file1_s3d1)); 353e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(dir_s3d3)); 354e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 355e1199815SMickaël Salaün umount(dir_s3d2); 356e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 357e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(dir_s3d2)); 358e1199815SMickaël Salaün } 359e1199815SMickaël Salaün 3604598d9abSMickaël Salaün /* clang-format off */ 3614598d9abSMickaël Salaün FIXTURE(layout1) {}; 3624598d9abSMickaël Salaün /* clang-format on */ 363e1199815SMickaël Salaün 364e1199815SMickaël Salaün FIXTURE_SETUP(layout1) 365e1199815SMickaël Salaün { 366e1199815SMickaël Salaün prepare_layout(_metadata); 367e1199815SMickaël Salaün 368e1199815SMickaël Salaün create_layout1(_metadata); 369e1199815SMickaël Salaün } 370e1199815SMickaël Salaün 371e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1) 372e1199815SMickaël Salaün { 373e1199815SMickaël Salaün remove_layout1(_metadata); 374e1199815SMickaël Salaün 375e1199815SMickaël Salaün cleanup_layout(_metadata); 376e1199815SMickaël Salaün } 377e1199815SMickaël Salaün 378e1199815SMickaël Salaün /* 379e1199815SMickaël Salaün * This helper enables to use the ASSERT_* macros and print the line number 380e1199815SMickaël Salaün * pointing to the test caller. 381e1199815SMickaël Salaün */ 382371183faSMickaël Salaün static int test_open_rel(const int dirfd, const char *const path, 383371183faSMickaël Salaün const int flags) 384e1199815SMickaël Salaün { 385e1199815SMickaël Salaün int fd; 386e1199815SMickaël Salaün 387e1199815SMickaël Salaün /* Works with file and directories. */ 388e1199815SMickaël Salaün fd = openat(dirfd, path, flags | O_CLOEXEC); 389e1199815SMickaël Salaün if (fd < 0) 390e1199815SMickaël Salaün return errno; 391e1199815SMickaël Salaün /* 392e1199815SMickaël Salaün * Mixing error codes from close(2) and open(2) should not lead to any 393e1199815SMickaël Salaün * (access type) confusion for this test. 394e1199815SMickaël Salaün */ 395e1199815SMickaël Salaün if (close(fd) != 0) 396e1199815SMickaël Salaün return errno; 397e1199815SMickaël Salaün return 0; 398e1199815SMickaël Salaün } 399e1199815SMickaël Salaün 400e1199815SMickaël Salaün static int test_open(const char *const path, const int flags) 401e1199815SMickaël Salaün { 402e1199815SMickaël Salaün return test_open_rel(AT_FDCWD, path, flags); 403e1199815SMickaël Salaün } 404e1199815SMickaël Salaün 405e1199815SMickaël Salaün TEST_F_FORK(layout1, no_restriction) 406e1199815SMickaël Salaün { 407e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 408e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 409e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY)); 410e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 411e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 412e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY)); 413e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 414e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 415e1199815SMickaël Salaün 416e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 417e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 418e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 419e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 420e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY)); 421e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY)); 422e1199815SMickaël Salaün 423e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 424e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 425e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 426e1199815SMickaël Salaün } 427e1199815SMickaël Salaün 428e1199815SMickaël Salaün TEST_F_FORK(layout1, inval) 429e1199815SMickaël Salaün { 430e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath = { 431e1199815SMickaël Salaün .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 432e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 433e1199815SMickaël Salaün .parent_fd = -1, 434e1199815SMickaël Salaün }; 435e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 436e1199815SMickaël Salaün .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | 437e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 438e1199815SMickaël Salaün }; 439e1199815SMickaël Salaün int ruleset_fd; 440e1199815SMickaël Salaün 441371183faSMickaël Salaün path_beneath.parent_fd = 442371183faSMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 443e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 444e1199815SMickaël Salaün 445e1199815SMickaël Salaün ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC); 446e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 447e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 448e1199815SMickaël Salaün &path_beneath, 0)); 449e1199815SMickaël Salaün /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */ 450e1199815SMickaël Salaün ASSERT_EQ(EBADF, errno); 451e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 452e1199815SMickaël Salaün 453e1199815SMickaël Salaün ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC); 454e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 455e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 456e1199815SMickaël Salaün &path_beneath, 0)); 457e1199815SMickaël Salaün /* Returns EBADFD because ruleset_fd is not a valid ruleset. */ 458e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 459e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 460e1199815SMickaël Salaün 461e1199815SMickaël Salaün /* Gets a real ruleset. */ 462371183faSMickaël Salaün ruleset_fd = 463371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 464e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 465e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 466e1199815SMickaël Salaün &path_beneath, 0)); 467e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 468e1199815SMickaël Salaün 469e1199815SMickaël Salaün /* Tests without O_PATH. */ 470e1199815SMickaël Salaün path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC); 471e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 472e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 473e1199815SMickaël Salaün &path_beneath, 0)); 474e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 475e1199815SMickaël Salaün 476e1199815SMickaël Salaün /* Tests with a ruleset FD. */ 477e1199815SMickaël Salaün path_beneath.parent_fd = ruleset_fd; 478e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 479e1199815SMickaël Salaün &path_beneath, 0)); 480e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 481e1199815SMickaël Salaün 482e1199815SMickaël Salaün /* Checks unhandled allowed_access. */ 483371183faSMickaël Salaün path_beneath.parent_fd = 484371183faSMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 485e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 486e1199815SMickaël Salaün 487e1199815SMickaël Salaün /* Test with legitimate values. */ 488e1199815SMickaël Salaün path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE; 489e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 490e1199815SMickaël Salaün &path_beneath, 0)); 491e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 492e1199815SMickaël Salaün path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE; 493e1199815SMickaël Salaün 49455e55920SMickaël Salaün /* Tests with denied-by-default access right. */ 49555e55920SMickaël Salaün path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER; 49655e55920SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 49755e55920SMickaël Salaün &path_beneath, 0)); 49855e55920SMickaël Salaün ASSERT_EQ(EINVAL, errno); 49955e55920SMickaël Salaün path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER; 50055e55920SMickaël Salaün 501e1199815SMickaël Salaün /* Test with unknown (64-bits) value. */ 502e1199815SMickaël Salaün path_beneath.allowed_access |= (1ULL << 60); 503e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 504e1199815SMickaël Salaün &path_beneath, 0)); 505e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 506e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 507e1199815SMickaël Salaün 508e1199815SMickaël Salaün /* Test with no access. */ 509e1199815SMickaël Salaün path_beneath.allowed_access = 0; 510e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 511e1199815SMickaël Salaün &path_beneath, 0)); 512e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 513e1199815SMickaël Salaün path_beneath.allowed_access &= ~(1ULL << 60); 514e1199815SMickaël Salaün 515e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 516e1199815SMickaël Salaün 517e1199815SMickaël Salaün /* Enforces the ruleset. */ 518e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 519e1199815SMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)); 520e1199815SMickaël Salaün 521e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 522e1199815SMickaël Salaün } 523e1199815SMickaël Salaün 5244598d9abSMickaël Salaün /* clang-format off */ 5254598d9abSMickaël Salaün 526e1199815SMickaël Salaün #define ACCESS_FILE ( \ 527e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | \ 528e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE | \ 529b9f5ce27SGünther Noack LANDLOCK_ACCESS_FS_READ_FILE | \ 530b9f5ce27SGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE) 531e1199815SMickaël Salaün 532b9f5ce27SGünther Noack #define ACCESS_LAST LANDLOCK_ACCESS_FS_TRUNCATE 533e1199815SMickaël Salaün 534e1199815SMickaël Salaün #define ACCESS_ALL ( \ 535e1199815SMickaël Salaün ACCESS_FILE | \ 536e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR | \ 537e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_DIR | \ 538e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE | \ 539e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_CHAR | \ 540e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_DIR | \ 541e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | \ 542e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK | \ 543e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO | \ 544e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ 545b91c3e4eSMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SYM | \ 546b9f5ce27SGünther Noack LANDLOCK_ACCESS_FS_REFER) 547e1199815SMickaël Salaün 5484598d9abSMickaël Salaün /* clang-format on */ 5494598d9abSMickaël Salaün 550d18955d0SMickaël Salaün TEST_F_FORK(layout1, file_and_dir_access_rights) 551e1199815SMickaël Salaün { 552e1199815SMickaël Salaün __u64 access; 553e1199815SMickaël Salaün int err; 554d18955d0SMickaël Salaün struct landlock_path_beneath_attr path_beneath_file = {}, 555d18955d0SMickaël Salaün path_beneath_dir = {}; 556e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 557e1199815SMickaël Salaün .handled_access_fs = ACCESS_ALL, 558e1199815SMickaël Salaün }; 559371183faSMickaël Salaün const int ruleset_fd = 560371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 561e1199815SMickaël Salaün 562e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 563e1199815SMickaël Salaün 564e1199815SMickaël Salaün /* Tests access rights for files. */ 565d18955d0SMickaël Salaün path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC); 566d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_file.parent_fd); 567d18955d0SMickaël Salaün 568d18955d0SMickaël Salaün /* Tests access rights for directories. */ 569d18955d0SMickaël Salaün path_beneath_dir.parent_fd = 570d18955d0SMickaël Salaün open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC); 571d18955d0SMickaël Salaün ASSERT_LE(0, path_beneath_dir.parent_fd); 572d18955d0SMickaël Salaün 573e1199815SMickaël Salaün for (access = 1; access <= ACCESS_LAST; access <<= 1) { 574d18955d0SMickaël Salaün path_beneath_dir.allowed_access = access; 575d18955d0SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 576d18955d0SMickaël Salaün LANDLOCK_RULE_PATH_BENEATH, 577d18955d0SMickaël Salaün &path_beneath_dir, 0)); 578d18955d0SMickaël Salaün 579d18955d0SMickaël Salaün path_beneath_file.allowed_access = access; 580e1199815SMickaël Salaün err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 581d18955d0SMickaël Salaün &path_beneath_file, 0); 582d18955d0SMickaël Salaün if (access & ACCESS_FILE) { 583e1199815SMickaël Salaün ASSERT_EQ(0, err); 584e1199815SMickaël Salaün } else { 585e1199815SMickaël Salaün ASSERT_EQ(-1, err); 586e1199815SMickaël Salaün ASSERT_EQ(EINVAL, errno); 587e1199815SMickaël Salaün } 588e1199815SMickaël Salaün } 589d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_file.parent_fd)); 590d18955d0SMickaël Salaün ASSERT_EQ(0, close(path_beneath_dir.parent_fd)); 591d18955d0SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 592e1199815SMickaël Salaün } 593e1199815SMickaël Salaün 594592efeb4SMickaël Salaün TEST_F_FORK(layout0, unknown_access_rights) 595c56b3bf5SMickaël Salaün { 596c56b3bf5SMickaël Salaün __u64 access_mask; 597c56b3bf5SMickaël Salaün 598c56b3bf5SMickaël Salaün for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST; 599c56b3bf5SMickaël Salaün access_mask >>= 1) { 600c56b3bf5SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 601c56b3bf5SMickaël Salaün .handled_access_fs = access_mask, 602c56b3bf5SMickaël Salaün }; 603c56b3bf5SMickaël Salaün 604c56b3bf5SMickaël Salaün ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 605c56b3bf5SMickaël Salaün sizeof(ruleset_attr), 0)); 606c56b3bf5SMickaël Salaün ASSERT_EQ(EINVAL, errno); 607c56b3bf5SMickaël Salaün } 608c56b3bf5SMickaël Salaün } 609c56b3bf5SMickaël Salaün 610e1199815SMickaël Salaün static void add_path_beneath(struct __test_metadata *const _metadata, 611e1199815SMickaël Salaün const int ruleset_fd, const __u64 allowed_access, 612e1199815SMickaël Salaün const char *const path) 613e1199815SMickaël Salaün { 614e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath = { 615e1199815SMickaël Salaün .allowed_access = allowed_access, 616e1199815SMickaël Salaün }; 617e1199815SMickaël Salaün 618e1199815SMickaël Salaün path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC); 619371183faSMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd) 620371183faSMickaël Salaün { 621e1199815SMickaël Salaün TH_LOG("Failed to open directory \"%s\": %s", path, 622e1199815SMickaël Salaün strerror(errno)); 623e1199815SMickaël Salaün } 624e1199815SMickaël Salaün ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 625371183faSMickaël Salaün &path_beneath, 0)) 626371183faSMickaël Salaün { 627e1199815SMickaël Salaün TH_LOG("Failed to update the ruleset with \"%s\": %s", path, 628e1199815SMickaël Salaün strerror(errno)); 629e1199815SMickaël Salaün } 630e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 631e1199815SMickaël Salaün } 632e1199815SMickaël Salaün 633e1199815SMickaël Salaün struct rule { 634e1199815SMickaël Salaün const char *path; 635e1199815SMickaël Salaün __u64 access; 636e1199815SMickaël Salaün }; 637e1199815SMickaël Salaün 6384598d9abSMickaël Salaün /* clang-format off */ 6394598d9abSMickaël Salaün 640e1199815SMickaël Salaün #define ACCESS_RO ( \ 641e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | \ 642e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR) 643e1199815SMickaël Salaün 644e1199815SMickaël Salaün #define ACCESS_RW ( \ 645e1199815SMickaël Salaün ACCESS_RO | \ 646e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE) 647e1199815SMickaël Salaün 6484598d9abSMickaël Salaün /* clang-format on */ 6494598d9abSMickaël Salaün 650e1199815SMickaël Salaün static int create_ruleset(struct __test_metadata *const _metadata, 651371183faSMickaël Salaün const __u64 handled_access_fs, 652371183faSMickaël Salaün const struct rule rules[]) 653e1199815SMickaël Salaün { 654e1199815SMickaël Salaün int ruleset_fd, i; 655e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = { 656e1199815SMickaël Salaün .handled_access_fs = handled_access_fs, 657e1199815SMickaël Salaün }; 658e1199815SMickaël Salaün 659371183faSMickaël Salaün ASSERT_NE(NULL, rules) 660371183faSMickaël Salaün { 661e1199815SMickaël Salaün TH_LOG("No rule list"); 662e1199815SMickaël Salaün } 663371183faSMickaël Salaün ASSERT_NE(NULL, rules[0].path) 664371183faSMickaël Salaün { 665e1199815SMickaël Salaün TH_LOG("Empty rule list"); 666e1199815SMickaël Salaün } 667e1199815SMickaël Salaün 668371183faSMickaël Salaün ruleset_fd = 669371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 670371183faSMickaël Salaün ASSERT_LE(0, ruleset_fd) 671371183faSMickaël Salaün { 672e1199815SMickaël Salaün TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 673e1199815SMickaël Salaün } 674e1199815SMickaël Salaün 675e1199815SMickaël Salaün for (i = 0; rules[i].path; i++) { 676e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, rules[i].access, 677e1199815SMickaël Salaün rules[i].path); 678e1199815SMickaël Salaün } 679e1199815SMickaël Salaün return ruleset_fd; 680e1199815SMickaël Salaün } 681e1199815SMickaël Salaün 682e1199815SMickaël Salaün static void enforce_ruleset(struct __test_metadata *const _metadata, 683e1199815SMickaël Salaün const int ruleset_fd) 684e1199815SMickaël Salaün { 685e1199815SMickaël Salaün ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 686371183faSMickaël Salaün ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 687371183faSMickaël Salaün { 688e1199815SMickaël Salaün TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 689e1199815SMickaël Salaün } 690e1199815SMickaël Salaün } 691e1199815SMickaël Salaün 692592efeb4SMickaël Salaün TEST_F_FORK(layout0, proc_nsfs) 693e1199815SMickaël Salaün { 694e1199815SMickaël Salaün const struct rule rules[] = { 695e1199815SMickaël Salaün { 696e1199815SMickaël Salaün .path = "/dev/null", 697e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 698e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 699e1199815SMickaël Salaün }, 700135464f9SMickaël Salaün {}, 701e1199815SMickaël Salaün }; 702e1199815SMickaël Salaün struct landlock_path_beneath_attr path_beneath; 703371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 704371183faSMickaël Salaün _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR, 705371183faSMickaël Salaün rules); 706e1199815SMickaël Salaün 707e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 708e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 709e1199815SMickaël Salaün 710e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 711e1199815SMickaël Salaün 712e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 713e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY)); 714e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/dev/null", O_RDONLY)); 715e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY)); 716e1199815SMickaël Salaün 717e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY)); 718e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY)); 719e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY)); 720e1199815SMickaël Salaün /* 721e1199815SMickaël Salaün * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a 722e1199815SMickaël Salaün * disconnected path. Such path cannot be identified and must then be 723e1199815SMickaël Salaün * allowed. 724e1199815SMickaël Salaün */ 725e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY)); 726e1199815SMickaël Salaün 727e1199815SMickaël Salaün /* 728e1199815SMickaël Salaün * Checks that it is not possible to add nsfs-like filesystem 729e1199815SMickaël Salaün * references to a ruleset. 730e1199815SMickaël Salaün */ 731e1199815SMickaël Salaün path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | 732e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 733e1199815SMickaël Salaün path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC); 734e1199815SMickaël Salaün ASSERT_LE(0, path_beneath.parent_fd); 735e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, 736e1199815SMickaël Salaün &path_beneath, 0)); 737e1199815SMickaël Salaün ASSERT_EQ(EBADFD, errno); 738e1199815SMickaël Salaün ASSERT_EQ(0, close(path_beneath.parent_fd)); 739e1199815SMickaël Salaün } 740e1199815SMickaël Salaün 741592efeb4SMickaël Salaün TEST_F_FORK(layout0, unpriv) 742371183faSMickaël Salaün { 743e1199815SMickaël Salaün const struct rule rules[] = { 744e1199815SMickaël Salaün { 745592efeb4SMickaël Salaün .path = TMP_DIR, 746e1199815SMickaël Salaün .access = ACCESS_RO, 747e1199815SMickaël Salaün }, 748135464f9SMickaël Salaün {}, 749e1199815SMickaël Salaün }; 750e1199815SMickaël Salaün int ruleset_fd; 751e1199815SMickaël Salaün 752e1199815SMickaël Salaün drop_caps(_metadata); 753e1199815SMickaël Salaün 754e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 755e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 756e1199815SMickaël Salaün ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0)); 757e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 758e1199815SMickaël Salaün 759e1199815SMickaël Salaün /* enforce_ruleset() calls prctl(no_new_privs). */ 760e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 761e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 762e1199815SMickaël Salaün } 763e1199815SMickaël Salaün 764e1199815SMickaël Salaün TEST_F_FORK(layout1, effective_access) 765e1199815SMickaël Salaün { 766e1199815SMickaël Salaün const struct rule rules[] = { 767e1199815SMickaël Salaün { 768e1199815SMickaël Salaün .path = dir_s1d2, 769e1199815SMickaël Salaün .access = ACCESS_RO, 770e1199815SMickaël Salaün }, 771e1199815SMickaël Salaün { 772e1199815SMickaël Salaün .path = file1_s2d2, 773e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 774e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 775e1199815SMickaël Salaün }, 776135464f9SMickaël Salaün {}, 777e1199815SMickaël Salaün }; 778e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 779e1199815SMickaël Salaün char buf; 780e1199815SMickaël Salaün int reg_fd; 781e1199815SMickaël Salaün 782e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 783e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 784e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 785e1199815SMickaël Salaün 786d1788ad9SMickaël Salaün /* Tests on a directory (with or without O_PATH). */ 787e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 788d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH)); 789e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 790d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH)); 791e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 792d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH)); 793d1788ad9SMickaël Salaün 794e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 795e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 796e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 797e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 798e1199815SMickaël Salaün 799d1788ad9SMickaël Salaün /* Tests on a file (with or without O_PATH). */ 800e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY)); 801d1788ad9SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH)); 802d1788ad9SMickaël Salaün 803e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 804e1199815SMickaël Salaün 805e1199815SMickaël Salaün /* Checks effective read and write actions. */ 806e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC); 807e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 808e1199815SMickaël Salaün ASSERT_EQ(1, write(reg_fd, ".", 1)); 809e1199815SMickaël Salaün ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET)); 810e1199815SMickaël Salaün ASSERT_EQ(1, read(reg_fd, &buf, 1)); 811e1199815SMickaël Salaün ASSERT_EQ('.', buf); 812e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 813e1199815SMickaël Salaün 814e1199815SMickaël Salaün /* Just in case, double-checks effective actions. */ 815e1199815SMickaël Salaün reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC); 816e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 817e1199815SMickaël Salaün ASSERT_EQ(-1, write(reg_fd, &buf, 1)); 818e1199815SMickaël Salaün ASSERT_EQ(EBADF, errno); 819e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 820e1199815SMickaël Salaün } 821e1199815SMickaël Salaün 822e1199815SMickaël Salaün TEST_F_FORK(layout1, unhandled_access) 823e1199815SMickaël Salaün { 824e1199815SMickaël Salaün const struct rule rules[] = { 825e1199815SMickaël Salaün { 826e1199815SMickaël Salaün .path = dir_s1d2, 827e1199815SMickaël Salaün .access = ACCESS_RO, 828e1199815SMickaël Salaün }, 829135464f9SMickaël Salaün {}, 830e1199815SMickaël Salaün }; 831e1199815SMickaël Salaün /* Here, we only handle read accesses, not write accesses. */ 832e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules); 833e1199815SMickaël Salaün 834e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 835e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 836e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 837e1199815SMickaël Salaün 838e1199815SMickaël Salaün /* 839e1199815SMickaël Salaün * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE, 840e1199815SMickaël Salaün * opening for write-only should be allowed, but not read-write. 841e1199815SMickaël Salaün */ 842e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY)); 843e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 844e1199815SMickaël Salaün 845e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 846e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 847e1199815SMickaël Salaün } 848e1199815SMickaël Salaün 849e1199815SMickaël Salaün TEST_F_FORK(layout1, ruleset_overlap) 850e1199815SMickaël Salaün { 851e1199815SMickaël Salaün const struct rule rules[] = { 852e1199815SMickaël Salaün /* These rules should be ORed among them. */ 853e1199815SMickaël Salaün { 854e1199815SMickaël Salaün .path = dir_s1d2, 855e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 856e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 857e1199815SMickaël Salaün }, 858e1199815SMickaël Salaün { 859e1199815SMickaël Salaün .path = dir_s1d2, 860e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 861e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 862e1199815SMickaël Salaün }, 863135464f9SMickaël Salaün {}, 864e1199815SMickaël Salaün }; 865e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 866e1199815SMickaël Salaün 867e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 868e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 869e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 870e1199815SMickaël Salaün 871e1199815SMickaël Salaün /* Checks s1d1 hierarchy. */ 872e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 873e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 874e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 875e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 876e1199815SMickaël Salaün 877e1199815SMickaël Salaün /* Checks s1d2 hierarchy. */ 878e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 879e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY)); 880e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 881e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 882e1199815SMickaël Salaün 883e1199815SMickaël Salaün /* Checks s1d3 hierarchy. */ 884e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 885e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 886e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 887e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 888e1199815SMickaël Salaün } 889e1199815SMickaël Salaün 8908ba0005fSMickaël Salaün TEST_F_FORK(layout1, layer_rule_unions) 8918ba0005fSMickaël Salaün { 8928ba0005fSMickaël Salaün const struct rule layer1[] = { 8938ba0005fSMickaël Salaün { 8948ba0005fSMickaël Salaün .path = dir_s1d2, 8958ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 8968ba0005fSMickaël Salaün }, 8978ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 8988ba0005fSMickaël Salaün { 8998ba0005fSMickaël Salaün .path = dir_s1d3, 9008ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 9018ba0005fSMickaël Salaün }, 9028ba0005fSMickaël Salaün {}, 9038ba0005fSMickaël Salaün }; 9048ba0005fSMickaël Salaün const struct rule layer2[] = { 9058ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 9068ba0005fSMickaël Salaün { 9078ba0005fSMickaël Salaün .path = dir_s1d2, 9088ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 9098ba0005fSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 9108ba0005fSMickaël Salaün }, 9118ba0005fSMickaël Salaün {}, 9128ba0005fSMickaël Salaün }; 9138ba0005fSMickaël Salaün const struct rule layer3[] = { 9148ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 9158ba0005fSMickaël Salaün { 9168ba0005fSMickaël Salaün .path = dir_s1d2, 9178ba0005fSMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 9188ba0005fSMickaël Salaün }, 9198ba0005fSMickaël Salaün {}, 9208ba0005fSMickaël Salaün }; 9218ba0005fSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1); 9228ba0005fSMickaël Salaün 9238ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 9248ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 9258ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 9268ba0005fSMickaël Salaün 9278ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer1. */ 9288ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 9298ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 9308ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 9318ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9328ba0005fSMickaël Salaün 9338ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer1. */ 9348ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 9358ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 9368ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 9378ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9388ba0005fSMickaël Salaün 9398ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer1. */ 9408ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 9418ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 9428ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 9438ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 9448ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9458ba0005fSMickaël Salaün 9468ba0005fSMickaël Salaün /* Doesn't change anything from layer1. */ 9478ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2); 9488ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 9498ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 9508ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 9518ba0005fSMickaël Salaün 9528ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer2. */ 9538ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 9548ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 9558ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 9568ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9578ba0005fSMickaël Salaün 9588ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer2. */ 9598ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 9608ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 9618ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 9628ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9638ba0005fSMickaël Salaün 9648ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer2. */ 9658ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 9668ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 9678ba0005fSMickaël Salaün /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */ 9688ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 9698ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9708ba0005fSMickaël Salaün 9718ba0005fSMickaël Salaün /* Only allows write (but not read) to dir_s1d3. */ 9728ba0005fSMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3); 9738ba0005fSMickaël Salaün ASSERT_LE(0, ruleset_fd); 9748ba0005fSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 9758ba0005fSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 9768ba0005fSMickaël Salaün 9778ba0005fSMickaël Salaün /* Checks s1d1 hierarchy with layer3. */ 9788ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 9798ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 9808ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 9818ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9828ba0005fSMickaël Salaün 9838ba0005fSMickaël Salaün /* Checks s1d2 hierarchy with layer3. */ 9848ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 9858ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 9868ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 9878ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9888ba0005fSMickaël Salaün 9898ba0005fSMickaël Salaün /* Checks s1d3 hierarchy with layer3. */ 9908ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 9918ba0005fSMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY)); 9928ba0005fSMickaël Salaün /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */ 9938ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR)); 9948ba0005fSMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 9958ba0005fSMickaël Salaün } 9968ba0005fSMickaël Salaün 997e1199815SMickaël Salaün TEST_F_FORK(layout1, non_overlapping_accesses) 998e1199815SMickaël Salaün { 999e1199815SMickaël Salaün const struct rule layer1[] = { 1000e1199815SMickaël Salaün { 1001e1199815SMickaël Salaün .path = dir_s1d2, 1002e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1003e1199815SMickaël Salaün }, 1004135464f9SMickaël Salaün {}, 1005e1199815SMickaël Salaün }; 1006e1199815SMickaël Salaün const struct rule layer2[] = { 1007e1199815SMickaël Salaün { 1008e1199815SMickaël Salaün .path = dir_s1d3, 1009e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1010e1199815SMickaël Salaün }, 1011135464f9SMickaël Salaün {}, 1012e1199815SMickaël Salaün }; 1013e1199815SMickaël Salaün int ruleset_fd; 1014e1199815SMickaël Salaün 1015e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 1016e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1017e1199815SMickaël Salaün 1018371183faSMickaël Salaün ruleset_fd = 1019371183faSMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1); 1020e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1021e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1022e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1023e1199815SMickaël Salaün 1024e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1025e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 1026e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1027e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1028e1199815SMickaël Salaün 1029e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, 1030e1199815SMickaël Salaün layer2); 1031e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1032e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1033e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1034e1199815SMickaël Salaün 1035e1199815SMickaël Salaün /* Unchanged accesses for file creation. */ 1036e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0)); 1037e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 1038e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0)); 1039e1199815SMickaël Salaün 1040e1199815SMickaël Salaün /* Checks file removing. */ 1041e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d2)); 1042e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 1043e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1044e1199815SMickaël Salaün } 1045e1199815SMickaël Salaün 1046e1199815SMickaël Salaün TEST_F_FORK(layout1, interleaved_masked_accesses) 1047e1199815SMickaël Salaün { 1048e1199815SMickaël Salaün /* 1049e1199815SMickaël Salaün * Checks overly restrictive rules: 1050e1199815SMickaël Salaün * layer 1: allows R s1d1/s1d2/s1d3/file1 1051e1199815SMickaël Salaün * layer 2: allows RW s1d1/s1d2/s1d3 1052e1199815SMickaël Salaün * allows W s1d1/s1d2 1053e1199815SMickaël Salaün * denies R s1d1/s1d2 1054e1199815SMickaël Salaün * layer 3: allows R s1d1 1055e1199815SMickaël Salaün * layer 4: allows R s1d1/s1d2 1056e1199815SMickaël Salaün * denies W s1d1/s1d2 1057e1199815SMickaël Salaün * layer 5: allows R s1d1/s1d2 1058e1199815SMickaël Salaün * layer 6: allows X ---- 1059e1199815SMickaël Salaün * layer 7: allows W s1d1/s1d2 1060e1199815SMickaël Salaün * denies R s1d1/s1d2 1061e1199815SMickaël Salaün */ 1062e1199815SMickaël Salaün const struct rule layer1_read[] = { 1063e1199815SMickaël Salaün /* Allows read access to file1_s1d3 with the first layer. */ 1064e1199815SMickaël Salaün { 1065e1199815SMickaël Salaün .path = file1_s1d3, 1066e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1067e1199815SMickaël Salaün }, 1068135464f9SMickaël Salaün {}, 1069e1199815SMickaël Salaün }; 1070e1199815SMickaël Salaün /* First rule with write restrictions. */ 1071e1199815SMickaël Salaün const struct rule layer2_read_write[] = { 1072e1199815SMickaël Salaün /* Start by granting read-write access via its parent directory... */ 1073e1199815SMickaël Salaün { 1074e1199815SMickaël Salaün .path = dir_s1d3, 1075e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 1076e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1077e1199815SMickaël Salaün }, 1078e1199815SMickaël Salaün /* ...but also denies read access via its grandparent directory. */ 1079e1199815SMickaël Salaün { 1080e1199815SMickaël Salaün .path = dir_s1d2, 1081e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1082e1199815SMickaël Salaün }, 1083135464f9SMickaël Salaün {}, 1084e1199815SMickaël Salaün }; 1085e1199815SMickaël Salaün const struct rule layer3_read[] = { 1086e1199815SMickaël Salaün /* Allows read access via its great-grandparent directory. */ 1087e1199815SMickaël Salaün { 1088e1199815SMickaël Salaün .path = dir_s1d1, 1089e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1090e1199815SMickaël Salaün }, 1091135464f9SMickaël Salaün {}, 1092e1199815SMickaël Salaün }; 1093e1199815SMickaël Salaün const struct rule layer4_read_write[] = { 1094e1199815SMickaël Salaün /* 1095e1199815SMickaël Salaün * Try to confuse the deny access by denying write (but not 1096e1199815SMickaël Salaün * read) access via its grandparent directory. 1097e1199815SMickaël Salaün */ 1098e1199815SMickaël Salaün { 1099e1199815SMickaël Salaün .path = dir_s1d2, 1100e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1101e1199815SMickaël Salaün }, 1102135464f9SMickaël Salaün {}, 1103e1199815SMickaël Salaün }; 1104e1199815SMickaël Salaün const struct rule layer5_read[] = { 1105e1199815SMickaël Salaün /* 1106e1199815SMickaël Salaün * Try to override layer2's deny read access by explicitly 1107e1199815SMickaël Salaün * allowing read access via file1_s1d3's grandparent. 1108e1199815SMickaël Salaün */ 1109e1199815SMickaël Salaün { 1110e1199815SMickaël Salaün .path = dir_s1d2, 1111e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1112e1199815SMickaël Salaün }, 1113135464f9SMickaël Salaün {}, 1114e1199815SMickaël Salaün }; 1115e1199815SMickaël Salaün const struct rule layer6_execute[] = { 1116e1199815SMickaël Salaün /* 1117e1199815SMickaël Salaün * Restricts an unrelated file hierarchy with a new access 1118e1199815SMickaël Salaün * (non-overlapping) type. 1119e1199815SMickaël Salaün */ 1120e1199815SMickaël Salaün { 1121e1199815SMickaël Salaün .path = dir_s2d1, 1122e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 1123e1199815SMickaël Salaün }, 1124135464f9SMickaël Salaün {}, 1125e1199815SMickaël Salaün }; 1126e1199815SMickaël Salaün const struct rule layer7_read_write[] = { 1127e1199815SMickaël Salaün /* 1128e1199815SMickaël Salaün * Finally, denies read access to file1_s1d3 via its 1129e1199815SMickaël Salaün * grandparent. 1130e1199815SMickaël Salaün */ 1131e1199815SMickaël Salaün { 1132e1199815SMickaël Salaün .path = dir_s1d2, 1133e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 1134e1199815SMickaël Salaün }, 1135135464f9SMickaël Salaün {}, 1136e1199815SMickaël Salaün }; 1137e1199815SMickaël Salaün int ruleset_fd; 1138e1199815SMickaël Salaün 1139e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1140e1199815SMickaël Salaün layer1_read); 1141e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1142e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1143e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1144e1199815SMickaël Salaün 1145e1199815SMickaël Salaün /* Checks that read access is granted for file1_s1d3 with layer 1. */ 1146e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1147e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1148e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1149e1199815SMickaël Salaün 1150371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1151371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1152371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1153371183faSMickaël Salaün layer2_read_write); 1154e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1155e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1156e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1157e1199815SMickaël Salaün 1158e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 2. */ 1159e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1160e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1161e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1162e1199815SMickaël Salaün 1163e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1164e1199815SMickaël Salaün layer3_read); 1165e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1166e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1167e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1168e1199815SMickaël Salaün 1169e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 3. */ 1170e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR)); 1171e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1172e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY)); 1173e1199815SMickaël Salaün 1174e1199815SMickaël Salaün /* This time, denies write access for the file hierarchy. */ 1175371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1176371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1177371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1178371183faSMickaël Salaün layer4_read_write); 1179e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1180e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1181e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1182e1199815SMickaël Salaün 1183e1199815SMickaël Salaün /* 1184e1199815SMickaël Salaün * Checks that the only change with layer 4 is that write access is 1185e1199815SMickaël Salaün * denied. 1186e1199815SMickaël Salaün */ 1187e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1188e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1189e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1190e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1191e1199815SMickaël Salaün 1192e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 1193e1199815SMickaël Salaün layer5_read); 1194e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1195e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1196e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1197e1199815SMickaël Salaün 1198e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 5. */ 1199e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1200e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1201e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1202e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1203e1199815SMickaël Salaün 1204e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, 1205e1199815SMickaël Salaün layer6_execute); 1206e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1207e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1208e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1209e1199815SMickaël Salaün 1210e1199815SMickaël Salaün /* Checks that previous access rights are unchanged with layer 6. */ 1211e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1212e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1213e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1214e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1215e1199815SMickaël Salaün 1216371183faSMickaël Salaün ruleset_fd = create_ruleset(_metadata, 1217371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1218371183faSMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 1219371183faSMickaël Salaün layer7_read_write); 1220e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1221e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1222e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1223e1199815SMickaël Salaün 1224e1199815SMickaël Salaün /* Checks read access is now denied with layer 7. */ 1225e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 1226e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1227e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY)); 1228e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY)); 1229e1199815SMickaël Salaün } 1230e1199815SMickaël Salaün 1231e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_subset) 1232e1199815SMickaël Salaün { 1233e1199815SMickaël Salaün const struct rule rules[] = { 1234e1199815SMickaël Salaün { 1235e1199815SMickaël Salaün .path = dir_s1d2, 1236e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 1237e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1238e1199815SMickaël Salaün }, 1239135464f9SMickaël Salaün {}, 1240e1199815SMickaël Salaün }; 1241e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1242e1199815SMickaël Salaün 1243e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1244e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1245e1199815SMickaël Salaün 1246e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1247e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1248e1199815SMickaël Salaün 1249e1199815SMickaël Salaün /* Write access is forbidden. */ 1250e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1251e1199815SMickaël Salaün /* Readdir access is allowed. */ 1252e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1253e1199815SMickaël Salaün 1254e1199815SMickaël Salaün /* Write access is forbidden. */ 1255e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1256e1199815SMickaël Salaün /* Readdir access is allowed. */ 1257e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1258e1199815SMickaël Salaün 1259e1199815SMickaël Salaün /* 1260e1199815SMickaël Salaün * Tests shared rule extension: the following rules should not grant 1261e1199815SMickaël Salaün * any new access, only remove some. Once enforced, these rules are 1262e1199815SMickaël Salaün * ANDed with the previous ones. 1263e1199815SMickaël Salaün */ 1264e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1265e1199815SMickaël Salaün dir_s1d2); 1266e1199815SMickaël Salaün /* 1267e1199815SMickaël Salaün * According to ruleset_fd, dir_s1d2 should now have the 1268e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE 1269e1199815SMickaël Salaün * access rights (even if this directory is opened a second time). 1270e1199815SMickaël Salaün * However, when enforcing this updated ruleset, the ruleset tied to 1271e1199815SMickaël Salaün * the current process (i.e. its domain) will still only have the 1272e1199815SMickaël Salaün * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and 1273e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_READ_DIR accesses, but 1274e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would 1275e1199815SMickaël Salaün * be a privilege escalation. 1276e1199815SMickaël Salaün */ 1277e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1278e1199815SMickaël Salaün 1279e1199815SMickaël Salaün /* Same tests and results as above. */ 1280e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1281e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1282e1199815SMickaël Salaün 1283e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1284e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1285e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1286e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1287e1199815SMickaël Salaün 1288e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1289e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1290e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1291e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1292e1199815SMickaël Salaün 1293e1199815SMickaël Salaün /* 1294e1199815SMickaël Salaün * Try to get more privileges by adding new access rights to the parent 1295e1199815SMickaël Salaün * directory: dir_s1d1. 1296e1199815SMickaël Salaün */ 1297e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1); 1298e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1299e1199815SMickaël Salaün 1300e1199815SMickaël Salaün /* Same tests and results as above. */ 1301e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1302e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1303e1199815SMickaël Salaün 1304e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1305e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1306e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1307e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1308e1199815SMickaël Salaün 1309e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1310e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1311e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1312e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1313e1199815SMickaël Salaün 1314e1199815SMickaël Salaün /* 1315e1199815SMickaël Salaün * Now, dir_s1d3 get a new rule tied to it, only allowing 1316e1199815SMickaël Salaün * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is 1317e1199815SMickaël Salaün * that there was no rule tied to it before. 1318e1199815SMickaël Salaün */ 1319e1199815SMickaël Salaün add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE, 1320e1199815SMickaël Salaün dir_s1d3); 1321e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1322e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1323e1199815SMickaël Salaün 1324e1199815SMickaël Salaün /* 1325e1199815SMickaël Salaün * Same tests and results as above, except for open(dir_s1d3) which is 1326e1199815SMickaël Salaün * now denied because the new rule mask the rule previously inherited 1327e1199815SMickaël Salaün * from dir_s1d2. 1328e1199815SMickaël Salaün */ 1329e1199815SMickaël Salaün 1330e1199815SMickaël Salaün /* Same tests and results as above. */ 1331e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 1332e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 1333e1199815SMickaël Salaün 1334e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d2. */ 1335e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 1336e1199815SMickaël Salaün /* Readdir access is still allowed. */ 1337e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1338e1199815SMickaël Salaün 1339e1199815SMickaël Salaün /* It is still forbidden to write in file1_s1d3. */ 1340e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 1341e1199815SMickaël Salaün /* 1342e1199815SMickaël Salaün * Readdir of dir_s1d3 is still allowed because of the OR policy inside 1343e1199815SMickaël Salaün * the same layer. 1344e1199815SMickaël Salaün */ 1345e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1346e1199815SMickaël Salaün } 1347e1199815SMickaël Salaün 1348e1199815SMickaël Salaün TEST_F_FORK(layout1, inherit_superset) 1349e1199815SMickaël Salaün { 1350e1199815SMickaël Salaün const struct rule rules[] = { 1351e1199815SMickaël Salaün { 1352e1199815SMickaël Salaün .path = dir_s1d3, 1353e1199815SMickaël Salaün .access = ACCESS_RO, 1354e1199815SMickaël Salaün }, 1355135464f9SMickaël Salaün {}, 1356e1199815SMickaël Salaün }; 1357e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1358e1199815SMickaël Salaün 1359e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1360e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1361e1199815SMickaël Salaün 1362e1199815SMickaël Salaün /* Readdir access is denied for dir_s1d2. */ 1363e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1364e1199815SMickaël Salaün /* Readdir access is allowed for dir_s1d3. */ 1365e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1366e1199815SMickaël Salaün /* File access is allowed for file1_s1d3. */ 1367e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1368e1199815SMickaël Salaün 1369e1199815SMickaël Salaün /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */ 1370371183faSMickaël Salaün add_path_beneath(_metadata, ruleset_fd, 1371371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 1372371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_DIR, 1373371183faSMickaël Salaün dir_s1d2); 1374e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1375e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1376e1199815SMickaël Salaün 1377e1199815SMickaël Salaün /* Readdir access is still denied for dir_s1d2. */ 1378e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 1379e1199815SMickaël Salaün /* Readdir access is still allowed for dir_s1d3. */ 1380e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 1381e1199815SMickaël Salaün /* File access is still allowed for file1_s1d3. */ 1382e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1383e1199815SMickaël Salaün } 1384e1199815SMickaël Salaün 1385592efeb4SMickaël Salaün TEST_F_FORK(layout0, max_layers) 1386e1199815SMickaël Salaün { 1387e1199815SMickaël Salaün int i, err; 1388e1199815SMickaël Salaün const struct rule rules[] = { 1389e1199815SMickaël Salaün { 1390592efeb4SMickaël Salaün .path = TMP_DIR, 1391e1199815SMickaël Salaün .access = ACCESS_RO, 1392e1199815SMickaël Salaün }, 1393135464f9SMickaël Salaün {}, 1394e1199815SMickaël Salaün }; 1395e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1396e1199815SMickaël Salaün 1397e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 139875c542d6SMickaël Salaün for (i = 0; i < 16; i++) 1399e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1400e1199815SMickaël Salaün 1401e1199815SMickaël Salaün for (i = 0; i < 2; i++) { 1402e1199815SMickaël Salaün err = landlock_restrict_self(ruleset_fd, 0); 1403e1199815SMickaël Salaün ASSERT_EQ(-1, err); 1404e1199815SMickaël Salaün ASSERT_EQ(E2BIG, errno); 1405e1199815SMickaël Salaün } 1406e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1407e1199815SMickaël Salaün } 1408e1199815SMickaël Salaün 1409e1199815SMickaël Salaün TEST_F_FORK(layout1, empty_or_same_ruleset) 1410e1199815SMickaël Salaün { 1411e1199815SMickaël Salaün struct landlock_ruleset_attr ruleset_attr = {}; 1412e1199815SMickaël Salaün int ruleset_fd; 1413e1199815SMickaël Salaün 1414e1199815SMickaël Salaün /* Tests empty handled_access_fs. */ 1415371183faSMickaël Salaün ruleset_fd = 1416371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1417e1199815SMickaël Salaün ASSERT_LE(-1, ruleset_fd); 1418e1199815SMickaël Salaün ASSERT_EQ(ENOMSG, errno); 1419e1199815SMickaël Salaün 1420e1199815SMickaël Salaün /* Enforces policy which deny read access to all files. */ 1421e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE; 1422371183faSMickaël Salaün ruleset_fd = 1423371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1424e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1425e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1426e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1427e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1428e1199815SMickaël Salaün 1429e1199815SMickaël Salaün /* Nests a policy which deny read access to all directories. */ 1430e1199815SMickaël Salaün ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR; 1431371183faSMickaël Salaün ruleset_fd = 1432371183faSMickaël Salaün landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); 1433e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1434e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1435e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 1436e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1437e1199815SMickaël Salaün 1438e1199815SMickaël Salaün /* Enforces a second time with the same ruleset. */ 1439e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1440e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1441e1199815SMickaël Salaün } 1442e1199815SMickaël Salaün 1443e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_on_mountpoint) 1444e1199815SMickaël Salaün { 1445e1199815SMickaël Salaün const struct rule rules[] = { 1446e1199815SMickaël Salaün { 1447e1199815SMickaël Salaün .path = dir_s1d1, 1448e1199815SMickaël Salaün .access = ACCESS_RO, 1449e1199815SMickaël Salaün }, 1450e1199815SMickaël Salaün { 1451e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1452e1199815SMickaël Salaün .path = dir_s3d2, 1453e1199815SMickaël Salaün .access = ACCESS_RO, 1454e1199815SMickaël Salaün }, 1455135464f9SMickaël Salaün {}, 1456e1199815SMickaël Salaün }; 1457e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1458e1199815SMickaël Salaün 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 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1464e1199815SMickaël Salaün 1465e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1466e1199815SMickaël Salaün 1467e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY)); 1468e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1469e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1470e1199815SMickaël Salaün } 1471e1199815SMickaël Salaün 1472e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_mountpoint) 1473e1199815SMickaël Salaün { 1474e1199815SMickaël Salaün const struct rule rules[] = { 1475e1199815SMickaël Salaün { 1476e1199815SMickaël Salaün .path = dir_s1d1, 1477e1199815SMickaël Salaün .access = ACCESS_RO, 1478e1199815SMickaël Salaün }, 1479e1199815SMickaël Salaün { 1480e1199815SMickaël Salaün /* dir_s3d2 is a mount point. */ 1481e1199815SMickaël Salaün .path = dir_s3d1, 1482e1199815SMickaël Salaün .access = ACCESS_RO, 1483e1199815SMickaël Salaün }, 1484135464f9SMickaël Salaün {}, 1485e1199815SMickaël Salaün }; 1486e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1487e1199815SMickaël Salaün 1488e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1489e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1490e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1491e1199815SMickaël Salaün 1492e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1493e1199815SMickaël Salaün 1494e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY)); 1495e1199815SMickaël Salaün 1496e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 1497e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY)); 1498e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY)); 1499e1199815SMickaël Salaün } 1500e1199815SMickaël Salaün 1501e1199815SMickaël Salaün /* 1502e1199815SMickaël Salaün * This test verifies that we can apply a landlock rule on the root directory 1503e1199815SMickaël Salaün * (which might require special handling). 1504e1199815SMickaël Salaün */ 1505e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_allow_then_deny) 1506e1199815SMickaël Salaün { 1507e1199815SMickaël Salaün struct rule rules[] = { 1508e1199815SMickaël Salaün { 1509e1199815SMickaël Salaün .path = "/", 1510e1199815SMickaël Salaün .access = ACCESS_RO, 1511e1199815SMickaël Salaün }, 1512135464f9SMickaël Salaün {}, 1513e1199815SMickaël Salaün }; 1514e1199815SMickaël Salaün int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1515e1199815SMickaël Salaün 1516e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1517e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1518e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1519e1199815SMickaël Salaün 1520e1199815SMickaël Salaün /* Checks allowed access. */ 1521e1199815SMickaël Salaün ASSERT_EQ(0, test_open("/", O_RDONLY)); 1522e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1523e1199815SMickaël Salaün 1524e1199815SMickaël Salaün rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE; 1525e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1526e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1527e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1528e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1529e1199815SMickaël Salaün 1530e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1531e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1532e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1533e1199815SMickaël Salaün } 1534e1199815SMickaël Salaün 1535e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_over_root_deny) 1536e1199815SMickaël Salaün { 1537e1199815SMickaël Salaün const struct rule rules[] = { 1538e1199815SMickaël Salaün { 1539e1199815SMickaël Salaün .path = "/", 1540e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 1541e1199815SMickaël Salaün }, 1542135464f9SMickaël Salaün {}, 1543e1199815SMickaël Salaün }; 1544e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1545e1199815SMickaël Salaün 1546e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1547e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1548e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1549e1199815SMickaël Salaün 1550e1199815SMickaël Salaün /* Checks denied access (on a directory). */ 1551e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1552e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY)); 1553e1199815SMickaël Salaün } 1554e1199815SMickaël Salaün 1555e1199815SMickaël Salaün TEST_F_FORK(layout1, rule_inside_mount_ns) 1556e1199815SMickaël Salaün { 1557e1199815SMickaël Salaün const struct rule rules[] = { 1558e1199815SMickaël Salaün { 1559e1199815SMickaël Salaün .path = "s3d3", 1560e1199815SMickaël Salaün .access = ACCESS_RO, 1561e1199815SMickaël Salaün }, 1562135464f9SMickaël Salaün {}, 1563e1199815SMickaël Salaün }; 1564e1199815SMickaël Salaün int ruleset_fd; 1565e1199815SMickaël Salaün 1566e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 156787129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)) 1568371183faSMickaël Salaün { 1569e1199815SMickaël Salaün TH_LOG("Failed to pivot root: %s", strerror(errno)); 1570e1199815SMickaël Salaün }; 1571e1199815SMickaël Salaün ASSERT_EQ(0, chdir("/")); 1572e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1573e1199815SMickaël Salaün 1574e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1575e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1576e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1577e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1578e1199815SMickaël Salaün 1579e1199815SMickaël Salaün ASSERT_EQ(0, test_open("s3d3", O_RDONLY)); 1580e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open("/", O_RDONLY)); 1581e1199815SMickaël Salaün } 1582e1199815SMickaël Salaün 1583e1199815SMickaël Salaün TEST_F_FORK(layout1, mount_and_pivot) 1584e1199815SMickaël Salaün { 1585e1199815SMickaël Salaün const struct rule rules[] = { 1586e1199815SMickaël Salaün { 1587e1199815SMickaël Salaün .path = dir_s3d2, 1588e1199815SMickaël Salaün .access = ACCESS_RO, 1589e1199815SMickaël Salaün }, 1590135464f9SMickaël Salaün {}, 1591e1199815SMickaël Salaün }; 1592e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1593e1199815SMickaël Salaün 1594e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1595e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1596e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1597e1199815SMickaël Salaün 1598e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1599e1199815SMickaël Salaün ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL)); 1600e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 160187129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3)); 1602e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1603e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1604e1199815SMickaël Salaün } 1605e1199815SMickaël Salaün 1606e1199815SMickaël Salaün TEST_F_FORK(layout1, move_mount) 1607e1199815SMickaël Salaün { 1608e1199815SMickaël Salaün const struct rule rules[] = { 1609e1199815SMickaël Salaün { 1610e1199815SMickaël Salaün .path = dir_s3d2, 1611e1199815SMickaël Salaün .access = ACCESS_RO, 1612e1199815SMickaël Salaün }, 1613135464f9SMickaël Salaün {}, 1614e1199815SMickaël Salaün }; 1615e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1616e1199815SMickaël Salaün 1617e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1618e1199815SMickaël Salaün 1619e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 162087129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1621371183faSMickaël Salaün dir_s1d2, 0)) 1622371183faSMickaël Salaün { 1623e1199815SMickaël Salaün TH_LOG("Failed to move mount: %s", strerror(errno)); 1624e1199815SMickaël Salaün } 1625e1199815SMickaël Salaün 162687129ef1SMickaël Salaün ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD, 1627e1199815SMickaël Salaün dir_s3d2, 0)); 1628e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1629e1199815SMickaël Salaün 1630e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1631e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1632e1199815SMickaël Salaün 1633e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 163487129ef1SMickaël Salaün ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD, 1635e1199815SMickaël Salaün dir_s1d2, 0)); 1636e1199815SMickaël Salaün ASSERT_EQ(EPERM, errno); 1637e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1638e1199815SMickaël Salaün } 1639e1199815SMickaël Salaün 1640e1199815SMickaël Salaün TEST_F_FORK(layout1, release_inodes) 1641e1199815SMickaël Salaün { 1642e1199815SMickaël Salaün const struct rule rules[] = { 1643e1199815SMickaël Salaün { 1644e1199815SMickaël Salaün .path = dir_s1d1, 1645e1199815SMickaël Salaün .access = ACCESS_RO, 1646e1199815SMickaël Salaün }, 1647e1199815SMickaël Salaün { 1648e1199815SMickaël Salaün .path = dir_s3d2, 1649e1199815SMickaël Salaün .access = ACCESS_RO, 1650e1199815SMickaël Salaün }, 1651e1199815SMickaël Salaün { 1652e1199815SMickaël Salaün .path = dir_s3d3, 1653e1199815SMickaël Salaün .access = ACCESS_RO, 1654e1199815SMickaël Salaün }, 1655135464f9SMickaël Salaün {}, 1656e1199815SMickaël Salaün }; 1657e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules); 1658e1199815SMickaël Salaün 1659e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1660e1199815SMickaël Salaün /* Unmount a file hierarchy while it is being used by a ruleset. */ 1661e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 1662e1199815SMickaël Salaün ASSERT_EQ(0, umount(dir_s3d2)); 1663e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 1664e1199815SMickaël Salaün 1665e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1666e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1667e1199815SMickaël Salaün 1668e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1669e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY)); 1670e1199815SMickaël Salaün /* This dir_s3d3 would not be allowed and does not exist anyway. */ 1671e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY)); 1672e1199815SMickaël Salaün } 1673e1199815SMickaël Salaün 1674e1199815SMickaël Salaün enum relative_access { 1675e1199815SMickaël Salaün REL_OPEN, 1676e1199815SMickaël Salaün REL_CHDIR, 1677e1199815SMickaël Salaün REL_CHROOT_ONLY, 1678e1199815SMickaël Salaün REL_CHROOT_CHDIR, 1679e1199815SMickaël Salaün }; 1680e1199815SMickaël Salaün 1681e1199815SMickaël Salaün static void test_relative_path(struct __test_metadata *const _metadata, 1682e1199815SMickaël Salaün const enum relative_access rel) 1683e1199815SMickaël Salaün { 1684e1199815SMickaël Salaün /* 1685e1199815SMickaël Salaün * Common layer to check that chroot doesn't ignore it (i.e. a chroot 1686e1199815SMickaël Salaün * is not a disconnected root directory). 1687e1199815SMickaël Salaün */ 1688e1199815SMickaël Salaün const struct rule layer1_base[] = { 1689e1199815SMickaël Salaün { 1690e1199815SMickaël Salaün .path = TMP_DIR, 1691e1199815SMickaël Salaün .access = ACCESS_RO, 1692e1199815SMickaël Salaün }, 1693135464f9SMickaël Salaün {}, 1694e1199815SMickaël Salaün }; 1695e1199815SMickaël Salaün const struct rule layer2_subs[] = { 1696e1199815SMickaël Salaün { 1697e1199815SMickaël Salaün .path = dir_s1d2, 1698e1199815SMickaël Salaün .access = ACCESS_RO, 1699e1199815SMickaël Salaün }, 1700e1199815SMickaël Salaün { 1701e1199815SMickaël Salaün .path = dir_s2d2, 1702e1199815SMickaël Salaün .access = ACCESS_RO, 1703e1199815SMickaël Salaün }, 1704135464f9SMickaël Salaün {}, 1705e1199815SMickaël Salaün }; 1706e1199815SMickaël Salaün int dirfd, ruleset_fd; 1707e1199815SMickaël Salaün 1708e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 1709e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1710e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1711e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1712e1199815SMickaël Salaün 1713e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs); 1714e1199815SMickaël Salaün 1715e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1716e1199815SMickaël Salaün switch (rel) { 1717e1199815SMickaël Salaün case REL_OPEN: 1718e1199815SMickaël Salaün case REL_CHDIR: 1719e1199815SMickaël Salaün break; 1720e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1721e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s2d2)); 1722e1199815SMickaël Salaün break; 1723e1199815SMickaël Salaün case REL_CHROOT_CHDIR: 1724e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1725e1199815SMickaël Salaün break; 1726e1199815SMickaël Salaün default: 1727e1199815SMickaël Salaün ASSERT_TRUE(false); 1728e1199815SMickaël Salaün return; 1729e1199815SMickaël Salaün } 1730e1199815SMickaël Salaün 1731e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_CHROOT); 1732e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1733e1199815SMickaël Salaün 1734e1199815SMickaël Salaün switch (rel) { 1735e1199815SMickaël Salaün case REL_OPEN: 1736e1199815SMickaël Salaün dirfd = open(dir_s1d2, O_DIRECTORY); 1737e1199815SMickaël Salaün ASSERT_LE(0, dirfd); 1738e1199815SMickaël Salaün break; 1739e1199815SMickaël Salaün case REL_CHDIR: 1740e1199815SMickaël Salaün ASSERT_EQ(0, chdir(dir_s1d2)); 1741e1199815SMickaël Salaün dirfd = AT_FDCWD; 1742e1199815SMickaël Salaün break; 1743e1199815SMickaël Salaün case REL_CHROOT_ONLY: 1744e1199815SMickaël Salaün /* Do chroot into dir_s1d2 (relative to dir_s2d2). */ 1745371183faSMickaël Salaün ASSERT_EQ(0, chroot("../../s1d1/s1d2")) 1746371183faSMickaël Salaün { 1747e1199815SMickaël Salaün TH_LOG("Failed to chroot: %s", strerror(errno)); 1748e1199815SMickaël Salaün } 1749e1199815SMickaël Salaün dirfd = AT_FDCWD; 1750e1199815SMickaël Salaün break; 1751e1199815SMickaël Salaün case REL_CHROOT_CHDIR: 1752e1199815SMickaël Salaün /* Do chroot into dir_s1d2. */ 1753371183faSMickaël Salaün ASSERT_EQ(0, chroot(".")) 1754371183faSMickaël Salaün { 1755e1199815SMickaël Salaün TH_LOG("Failed to chroot: %s", strerror(errno)); 1756e1199815SMickaël Salaün } 1757e1199815SMickaël Salaün dirfd = AT_FDCWD; 1758e1199815SMickaël Salaün break; 1759e1199815SMickaël Salaün } 1760e1199815SMickaël Salaün 1761e1199815SMickaël Salaün ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES, 1762e1199815SMickaël Salaün test_open_rel(dirfd, "..", O_RDONLY)); 1763e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY)); 1764e1199815SMickaël Salaün 1765e1199815SMickaël Salaün if (rel == REL_CHROOT_ONLY) { 1766e1199815SMickaël Salaün /* The current directory is dir_s2d2. */ 1767e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY)); 1768e1199815SMickaël Salaün } else { 1769e1199815SMickaël Salaün /* The current directory is dir_s1d2. */ 1770e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY)); 1771e1199815SMickaël Salaün } 1772e1199815SMickaël Salaün 1773e1199815SMickaël Salaün if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) { 1774e1199815SMickaël Salaün /* Checks the root dir_s1d2. */ 1775e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY)); 1776e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY)); 1777e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY)); 1778e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY)); 1779e1199815SMickaël Salaün } 1780e1199815SMickaël Salaün 1781e1199815SMickaël Salaün if (rel != REL_CHROOT_CHDIR) { 1782e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY)); 1783e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY)); 1784371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3", 1785371183faSMickaël Salaün O_RDONLY)); 1786e1199815SMickaël Salaün 1787e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY)); 1788e1199815SMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY)); 1789371183faSMickaël Salaün ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3", 1790371183faSMickaël Salaün O_RDONLY)); 1791e1199815SMickaël Salaün } 1792e1199815SMickaël Salaün 1793e1199815SMickaël Salaün if (rel == REL_OPEN) 1794e1199815SMickaël Salaün ASSERT_EQ(0, close(dirfd)); 1795e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1796e1199815SMickaël Salaün } 1797e1199815SMickaël Salaün 1798e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_open) 1799e1199815SMickaël Salaün { 1800e1199815SMickaël Salaün test_relative_path(_metadata, REL_OPEN); 1801e1199815SMickaël Salaün } 1802e1199815SMickaël Salaün 1803e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chdir) 1804e1199815SMickaël Salaün { 1805e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHDIR); 1806e1199815SMickaël Salaün } 1807e1199815SMickaël Salaün 1808e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_only) 1809e1199815SMickaël Salaün { 1810e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_ONLY); 1811e1199815SMickaël Salaün } 1812e1199815SMickaël Salaün 1813e1199815SMickaël Salaün TEST_F_FORK(layout1, relative_chroot_chdir) 1814e1199815SMickaël Salaün { 1815e1199815SMickaël Salaün test_relative_path(_metadata, REL_CHROOT_CHDIR); 1816e1199815SMickaël Salaün } 1817e1199815SMickaël Salaün 1818e1199815SMickaël Salaün static void copy_binary(struct __test_metadata *const _metadata, 1819e1199815SMickaël Salaün const char *const dst_path) 1820e1199815SMickaël Salaün { 1821e1199815SMickaël Salaün int dst_fd, src_fd; 1822e1199815SMickaël Salaün struct stat statbuf; 1823e1199815SMickaël Salaün 1824e1199815SMickaël Salaün dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC); 1825371183faSMickaël Salaün ASSERT_LE(0, dst_fd) 1826371183faSMickaël Salaün { 1827371183faSMickaël Salaün TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno)); 1828e1199815SMickaël Salaün } 1829e1199815SMickaël Salaün src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC); 1830371183faSMickaël Salaün ASSERT_LE(0, src_fd) 1831371183faSMickaël Salaün { 1832e1199815SMickaël Salaün TH_LOG("Failed to open \"" BINARY_PATH "\": %s", 1833e1199815SMickaël Salaün strerror(errno)); 1834e1199815SMickaël Salaün } 1835e1199815SMickaël Salaün ASSERT_EQ(0, fstat(src_fd, &statbuf)); 1836371183faSMickaël Salaün ASSERT_EQ(statbuf.st_size, 1837371183faSMickaël Salaün sendfile(dst_fd, src_fd, 0, statbuf.st_size)); 1838e1199815SMickaël Salaün ASSERT_EQ(0, close(src_fd)); 1839e1199815SMickaël Salaün ASSERT_EQ(0, close(dst_fd)); 1840e1199815SMickaël Salaün } 1841e1199815SMickaël Salaün 1842371183faSMickaël Salaün static void test_execute(struct __test_metadata *const _metadata, const int err, 1843371183faSMickaël Salaün const char *const path) 1844e1199815SMickaël Salaün { 1845e1199815SMickaël Salaün int status; 1846e1199815SMickaël Salaün char *const argv[] = { (char *)path, NULL }; 1847e1199815SMickaël Salaün const pid_t child = fork(); 1848e1199815SMickaël Salaün 1849e1199815SMickaël Salaün ASSERT_LE(0, child); 1850e1199815SMickaël Salaün if (child == 0) { 1851371183faSMickaël Salaün ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL)) 1852371183faSMickaël Salaün { 1853e1199815SMickaël Salaün TH_LOG("Failed to execute \"%s\": %s", path, 1854e1199815SMickaël Salaün strerror(errno)); 1855e1199815SMickaël Salaün }; 1856e1199815SMickaël Salaün ASSERT_EQ(err, errno); 1857e1199815SMickaël Salaün _exit(_metadata->passed ? 2 : 1); 1858e1199815SMickaël Salaün return; 1859e1199815SMickaël Salaün } 1860e1199815SMickaël Salaün ASSERT_EQ(child, waitpid(child, &status, 0)); 1861e1199815SMickaël Salaün ASSERT_EQ(1, WIFEXITED(status)); 1862371183faSMickaël Salaün ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status)) 1863371183faSMickaël Salaün { 1864e1199815SMickaël Salaün TH_LOG("Unexpected return code for \"%s\": %s", path, 1865e1199815SMickaël Salaün strerror(errno)); 1866e1199815SMickaël Salaün }; 1867e1199815SMickaël Salaün } 1868e1199815SMickaël Salaün 1869e1199815SMickaël Salaün TEST_F_FORK(layout1, execute) 1870e1199815SMickaël Salaün { 1871e1199815SMickaël Salaün const struct rule rules[] = { 1872e1199815SMickaël Salaün { 1873e1199815SMickaël Salaün .path = dir_s1d2, 1874e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 1875e1199815SMickaël Salaün }, 1876135464f9SMickaël Salaün {}, 1877e1199815SMickaël Salaün }; 1878371183faSMickaël Salaün const int ruleset_fd = 1879371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1880e1199815SMickaël Salaün 1881e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1882e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d1); 1883e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d2); 1884e1199815SMickaël Salaün copy_binary(_metadata, file1_s1d3); 1885e1199815SMickaël Salaün 1886e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1887e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1888e1199815SMickaël Salaün 1889e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 1890e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 1891e1199815SMickaël Salaün test_execute(_metadata, EACCES, file1_s1d1); 1892e1199815SMickaël Salaün 1893e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 1894e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 1895e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d2); 1896e1199815SMickaël Salaün 1897e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 1898e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 1899e1199815SMickaël Salaün test_execute(_metadata, 0, file1_s1d3); 1900e1199815SMickaël Salaün } 1901e1199815SMickaël Salaün 1902e1199815SMickaël Salaün TEST_F_FORK(layout1, link) 1903e1199815SMickaël Salaün { 19046a1bdd4aSMickaël Salaün const struct rule layer1[] = { 1905e1199815SMickaël Salaün { 1906e1199815SMickaël Salaün .path = dir_s1d2, 1907e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 1908e1199815SMickaël Salaün }, 1909135464f9SMickaël Salaün {}, 1910e1199815SMickaël Salaün }; 19116a1bdd4aSMickaël Salaün const struct rule layer2[] = { 19126a1bdd4aSMickaël Salaün { 19136a1bdd4aSMickaël Salaün .path = dir_s1d3, 19146a1bdd4aSMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 19156a1bdd4aSMickaël Salaün }, 19166a1bdd4aSMickaël Salaün {}, 19176a1bdd4aSMickaël Salaün }; 19186a1bdd4aSMickaël Salaün int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 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_s1d1)); 1923e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1924e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 1925e1199815SMickaël Salaün 1926e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1927e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1928e1199815SMickaël Salaün 1929e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 1930e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 19316a1bdd4aSMickaël Salaün 1932e1199815SMickaël Salaün /* Denies linking because of reparenting. */ 1933e1199815SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 1934e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 1935e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 1936e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 19376a1bdd4aSMickaël Salaün ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 19386a1bdd4aSMickaël Salaün ASSERT_EQ(EXDEV, errno); 1939e1199815SMickaël Salaün 1940e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 1941e1199815SMickaël Salaün ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 19426a1bdd4aSMickaël Salaün 19436a1bdd4aSMickaël Salaün /* Prepares for next unlinks. */ 19446a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 19456a1bdd4aSMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 19466a1bdd4aSMickaël Salaün 19476a1bdd4aSMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 19486a1bdd4aSMickaël Salaün ASSERT_LE(0, ruleset_fd); 19496a1bdd4aSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 19506a1bdd4aSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 19516a1bdd4aSMickaël Salaün 19526a1bdd4aSMickaël Salaün /* Checks that linkind doesn't require the ability to delete a file. */ 19536a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 19546a1bdd4aSMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 1955e1199815SMickaël Salaün } 1956e1199815SMickaël Salaün 195755e55920SMickaël Salaün static int test_rename(const char *const oldpath, const char *const newpath) 195855e55920SMickaël Salaün { 195955e55920SMickaël Salaün if (rename(oldpath, newpath)) 196055e55920SMickaël Salaün return errno; 196155e55920SMickaël Salaün return 0; 196255e55920SMickaël Salaün } 196355e55920SMickaël Salaün 196455e55920SMickaël Salaün static int test_exchange(const char *const oldpath, const char *const newpath) 196555e55920SMickaël Salaün { 196655e55920SMickaël Salaün if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE)) 196755e55920SMickaël Salaün return errno; 196855e55920SMickaël Salaün return 0; 196955e55920SMickaël Salaün } 197055e55920SMickaël Salaün 1971e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_file) 1972e1199815SMickaël Salaün { 1973e1199815SMickaël Salaün const struct rule rules[] = { 1974e1199815SMickaël Salaün { 1975e1199815SMickaël Salaün .path = dir_s1d3, 1976e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1977e1199815SMickaël Salaün }, 1978e1199815SMickaël Salaün { 1979e1199815SMickaël Salaün .path = dir_s2d2, 1980e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 1981e1199815SMickaël Salaün }, 1982135464f9SMickaël Salaün {}, 1983e1199815SMickaël Salaün }; 1984371183faSMickaël Salaün const int ruleset_fd = 1985371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 1986e1199815SMickaël Salaün 1987e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 1988e1199815SMickaël Salaün 1989e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 1990e1199815SMickaël Salaün 1991e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 1992e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 1993e1199815SMickaël Salaün 1994e1199815SMickaël Salaün /* 1995e1199815SMickaël Salaün * Tries to replace a file, from a directory that allows file removal, 1996e1199815SMickaël Salaün * but to a different directory (which also allows file removal). 1997e1199815SMickaël Salaün */ 1998e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3)); 1999e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2000e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3, 2001e1199815SMickaël Salaün RENAME_EXCHANGE)); 2002e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2003e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2004e1199815SMickaël Salaün RENAME_EXCHANGE)); 2005e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2006e1199815SMickaël Salaün 2007e1199815SMickaël Salaün /* 2008e1199815SMickaël Salaün * Tries to replace a file, from a directory that denies file removal, 2009e1199815SMickaël Salaün * to a different directory (which allows file removal). 2010e1199815SMickaël Salaün */ 2011e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 201255e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 2013e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3, 2014e1199815SMickaël Salaün RENAME_EXCHANGE)); 201555e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 2016e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3, 2017e1199815SMickaël Salaün RENAME_EXCHANGE)); 2018e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2019e1199815SMickaël Salaün 2020e1199815SMickaël Salaün /* Exchanges files and directories that partially allow removal. */ 2021e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1, 2022e1199815SMickaël Salaün RENAME_EXCHANGE)); 2023e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 20246a1bdd4aSMickaël Salaün /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */ 20256a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1)); 20266a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 2027e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2, 2028e1199815SMickaël Salaün RENAME_EXCHANGE)); 2029e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 20306a1bdd4aSMickaël Salaün /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */ 20316a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 20326a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 2033e1199815SMickaël Salaün 2034e1199815SMickaël Salaün /* Renames files with different parents. */ 2035e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2036e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2037e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2038e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 203955e55920SMickaël Salaün ASSERT_EQ(EACCES, errno); 2040e1199815SMickaël Salaün 2041e1199815SMickaël Salaün /* Exchanges and renames files with same parent. */ 2042e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3, 2043e1199815SMickaël Salaün RENAME_EXCHANGE)); 2044e1199815SMickaël Salaün ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3)); 2045e1199815SMickaël Salaün 2046e1199815SMickaël Salaün /* Exchanges files and directories with same parent, twice. */ 2047e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2048e1199815SMickaël Salaün RENAME_EXCHANGE)); 2049e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3, 2050e1199815SMickaël Salaün RENAME_EXCHANGE)); 2051e1199815SMickaël Salaün } 2052e1199815SMickaël Salaün 2053e1199815SMickaël Salaün TEST_F_FORK(layout1, rename_dir) 2054e1199815SMickaël Salaün { 2055e1199815SMickaël Salaün const struct rule rules[] = { 2056e1199815SMickaël Salaün { 2057e1199815SMickaël Salaün .path = dir_s1d2, 2058e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2059e1199815SMickaël Salaün }, 2060e1199815SMickaël Salaün { 2061e1199815SMickaël Salaün .path = dir_s2d1, 2062e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2063e1199815SMickaël Salaün }, 2064135464f9SMickaël Salaün {}, 2065e1199815SMickaël Salaün }; 2066371183faSMickaël Salaün const int ruleset_fd = 2067371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2068e1199815SMickaël Salaün 2069e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2070e1199815SMickaël Salaün 2071e1199815SMickaël Salaün /* Empties dir_s1d3 to allow renaming. */ 2072e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2073e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2074e1199815SMickaël Salaün 2075e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2076e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2077e1199815SMickaël Salaün 2078e1199815SMickaël Salaün /* Exchanges and renames directory to a different parent. */ 2079e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2080e1199815SMickaël Salaün RENAME_EXCHANGE)); 2081e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2082e1199815SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3)); 2083e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2084e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2085e1199815SMickaël Salaün RENAME_EXCHANGE)); 2086e1199815SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2087e1199815SMickaël Salaün 2088e1199815SMickaël Salaün /* 2089e1199815SMickaël Salaün * Exchanges directory to the same parent, which doesn't allow 2090e1199815SMickaël Salaün * directory removal. 2091e1199815SMickaël Salaün */ 2092e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1, 2093e1199815SMickaël Salaün RENAME_EXCHANGE)); 2094e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 20956a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */ 20966a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1)); 20976a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 2098e1199815SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2, 2099e1199815SMickaël Salaün RENAME_EXCHANGE)); 2100e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 21016a1bdd4aSMickaël Salaün /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */ 21026a1bdd4aSMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2)); 21036a1bdd4aSMickaël Salaün ASSERT_EQ(EACCES, errno); 2104e1199815SMickaël Salaün 2105e1199815SMickaël Salaün /* 2106e1199815SMickaël Salaün * Exchanges and renames directory to the same parent, which allows 2107e1199815SMickaël Salaün * directory removal. 2108e1199815SMickaël Salaün */ 2109e1199815SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2, 2110e1199815SMickaël Salaün RENAME_EXCHANGE)); 2111e1199815SMickaël Salaün ASSERT_EQ(0, unlink(dir_s1d3)); 2112e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2113e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3)); 2114e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 2115e1199815SMickaël Salaün } 2116e1199815SMickaël Salaün 2117f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_refer) 2118f4056b92SMickaël Salaün { 2119f4056b92SMickaël Salaün const struct rule layer1[] = { 2120f4056b92SMickaël Salaün { 2121f4056b92SMickaël Salaün .path = dir_s1d2, 2122f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2123f4056b92SMickaël Salaün }, 2124f4056b92SMickaël Salaün { 2125f4056b92SMickaël Salaün .path = dir_s2d2, 2126f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2127f4056b92SMickaël Salaün }, 2128f4056b92SMickaël Salaün {}, 2129f4056b92SMickaël Salaün }; 2130f4056b92SMickaël Salaün int ruleset_fd = 2131f4056b92SMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1); 2132f4056b92SMickaël Salaün 2133f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2134f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2135f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2136f4056b92SMickaël Salaün 2137f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1)); 2138f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2139f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2)); 2140f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2141f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3)); 2142f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2143f4056b92SMickaël Salaün 2144f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1)); 2145f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2146f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2)); 2147f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2148f4056b92SMickaël Salaün /* 2149f4056b92SMickaël Salaün * Moving should only be allowed when the source and the destination 2150f4056b92SMickaël Salaün * parent directory have REFER. 2151f4056b92SMickaël Salaün */ 2152f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3)); 2153f4056b92SMickaël Salaün ASSERT_EQ(ENOTEMPTY, errno); 2154f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2155f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2156f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3)); 2157f4056b92SMickaël Salaün } 2158f4056b92SMickaël Salaün 215955e55920SMickaël Salaün /* Checks renames beneath dir_s1d1. */ 216055e55920SMickaël Salaün static void refer_denied_by_default(struct __test_metadata *const _metadata, 216155e55920SMickaël Salaün const struct rule layer1[], 216255e55920SMickaël Salaün const int layer1_err, 216355e55920SMickaël Salaün const struct rule layer2[]) 216455e55920SMickaël Salaün { 216555e55920SMickaël Salaün int ruleset_fd; 216655e55920SMickaël Salaün 216755e55920SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 216855e55920SMickaël Salaün 216955e55920SMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1); 217055e55920SMickaël Salaün ASSERT_LE(0, ruleset_fd); 217155e55920SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 217255e55920SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 217355e55920SMickaël Salaün 217455e55920SMickaël Salaün /* 217555e55920SMickaël Salaün * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to 217655e55920SMickaël Salaün * layer1_err), then it allows some different-parent renames and links. 217755e55920SMickaël Salaün */ 217855e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2)); 217955e55920SMickaël Salaün if (layer1_err == 0) 218055e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1)); 218155e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2)); 218255e55920SMickaël Salaün ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1)); 218355e55920SMickaël Salaün 218455e55920SMickaël Salaün ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2); 218555e55920SMickaël Salaün ASSERT_LE(0, ruleset_fd); 218655e55920SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 218755e55920SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 218855e55920SMickaël Salaün 218955e55920SMickaël Salaün /* 219055e55920SMickaël Salaün * Now, either the first or the second layer does not handle 219155e55920SMickaël Salaün * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent 219255e55920SMickaël Salaün * renames and links are denied, thus making the layer handling 219355e55920SMickaël Salaün * LANDLOCK_ACCESS_FS_REFER null and void. 219455e55920SMickaël Salaün */ 219555e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2)); 219655e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2)); 219755e55920SMickaël Salaün ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1)); 219855e55920SMickaël Salaün } 219955e55920SMickaël Salaün 220055e55920SMickaël Salaün const struct rule layer_dir_s1d1_refer[] = { 220155e55920SMickaël Salaün { 220255e55920SMickaël Salaün .path = dir_s1d1, 220355e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 220455e55920SMickaël Salaün }, 220555e55920SMickaël Salaün {}, 220655e55920SMickaël Salaün }; 220755e55920SMickaël Salaün 220855e55920SMickaël Salaün const struct rule layer_dir_s1d1_execute[] = { 220955e55920SMickaël Salaün { 221055e55920SMickaël Salaün /* Matches a parent directory. */ 221155e55920SMickaël Salaün .path = dir_s1d1, 221255e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 221355e55920SMickaël Salaün }, 221455e55920SMickaël Salaün {}, 221555e55920SMickaël Salaün }; 221655e55920SMickaël Salaün 221755e55920SMickaël Salaün const struct rule layer_dir_s2d1_execute[] = { 221855e55920SMickaël Salaün { 221955e55920SMickaël Salaün /* Does not match a parent directory. */ 222055e55920SMickaël Salaün .path = dir_s2d1, 222155e55920SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 222255e55920SMickaël Salaün }, 222355e55920SMickaël Salaün {}, 222455e55920SMickaël Salaün }; 222555e55920SMickaël Salaün 222655e55920SMickaël Salaün /* 222755e55920SMickaël Salaün * Tests precedence over renames: denied by default for different parent 222855e55920SMickaël Salaün * directories, *with* a rule matching a parent directory, but not directly 222955e55920SMickaël Salaün * denying access (with MAKE_REG nor REMOVE). 223055e55920SMickaël Salaün */ 223155e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default1) 223255e55920SMickaël Salaün { 223355e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 223455e55920SMickaël Salaün layer_dir_s1d1_execute); 223555e55920SMickaël Salaün } 223655e55920SMickaël Salaün 223755e55920SMickaël Salaün /* 223855e55920SMickaël Salaün * Same test but this time turning around the ABI version order: the first 223955e55920SMickaël Salaün * layer does not handle LANDLOCK_ACCESS_FS_REFER. 224055e55920SMickaël Salaün */ 224155e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default2) 224255e55920SMickaël Salaün { 224355e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV, 224455e55920SMickaël Salaün layer_dir_s1d1_refer); 224555e55920SMickaël Salaün } 224655e55920SMickaël Salaün 224755e55920SMickaël Salaün /* 224855e55920SMickaël Salaün * Tests precedence over renames: denied by default for different parent 224955e55920SMickaël Salaün * directories, *without* a rule matching a parent directory, but not directly 225055e55920SMickaël Salaün * denying access (with MAKE_REG nor REMOVE). 225155e55920SMickaël Salaün */ 225255e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default3) 225355e55920SMickaël Salaün { 225455e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0, 225555e55920SMickaël Salaün layer_dir_s2d1_execute); 225655e55920SMickaël Salaün } 225755e55920SMickaël Salaün 225855e55920SMickaël Salaün /* 225955e55920SMickaël Salaün * Same test but this time turning around the ABI version order: the first 226055e55920SMickaël Salaün * layer does not handle LANDLOCK_ACCESS_FS_REFER. 226155e55920SMickaël Salaün */ 226255e55920SMickaël Salaün TEST_F_FORK(layout1, refer_denied_by_default4) 226355e55920SMickaël Salaün { 226455e55920SMickaël Salaün refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV, 226555e55920SMickaël Salaün layer_dir_s1d1_refer); 226655e55920SMickaël Salaün } 226755e55920SMickaël Salaün 2268f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_link) 2269f4056b92SMickaël Salaün { 2270f4056b92SMickaël Salaün const struct rule layer1[] = { 2271f4056b92SMickaël Salaün { 2272f4056b92SMickaël Salaün .path = dir_s1d2, 2273f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2274f4056b92SMickaël Salaün }, 2275f4056b92SMickaël Salaün { 2276f4056b92SMickaël Salaün .path = dir_s1d3, 2277f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2278f4056b92SMickaël Salaün }, 2279f4056b92SMickaël Salaün { 2280f4056b92SMickaël Salaün .path = dir_s2d2, 2281f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2282f4056b92SMickaël Salaün }, 2283f4056b92SMickaël Salaün { 2284f4056b92SMickaël Salaün .path = dir_s2d3, 2285f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2286f4056b92SMickaël Salaün }, 2287f4056b92SMickaël Salaün {}, 2288f4056b92SMickaël Salaün }; 2289f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2290f4056b92SMickaël Salaün _metadata, 2291f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2292f4056b92SMickaël Salaün 2293f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2294f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2295f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2296f4056b92SMickaël Salaün 2297f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2298f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2299f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2300f4056b92SMickaël Salaün 2301f4056b92SMickaël Salaün /* Denies linking because of missing MAKE_REG. */ 2302f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 2303f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2304f4056b92SMickaël Salaün /* Denies linking because of missing source and destination REFER. */ 2305f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2)); 2306f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2307f4056b92SMickaël Salaün /* Denies linking because of missing source REFER. */ 2308f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3)); 2309f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2310f4056b92SMickaël Salaün 2311f4056b92SMickaël Salaün /* Denies linking because of missing MAKE_REG. */ 2312f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1)); 2313f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2314f4056b92SMickaël Salaün /* Denies linking because of missing destination REFER. */ 2315f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2)); 2316f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2317f4056b92SMickaël Salaün 2318f4056b92SMickaël Salaün /* Allows linking because of REFER and MAKE_REG. */ 2319f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s2d2, file1_s1d3)); 2320f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d2)); 2321f4056b92SMickaël Salaün /* Reverse linking denied because of missing MAKE_REG. */ 2322f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2)); 2323f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2324f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2325f4056b92SMickaël Salaün /* Checks reverse linking. */ 2326f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file1_s2d3)); 2327f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2328f4056b92SMickaël Salaün 2329f4056b92SMickaël Salaün /* 2330f4056b92SMickaël Salaün * This is OK for a file link, but it should not be allowed for a 2331f4056b92SMickaël Salaün * directory rename (because of the superset of access rights. 2332f4056b92SMickaël Salaün */ 2333f4056b92SMickaël Salaün ASSERT_EQ(0, link(file1_s2d3, file1_s1d3)); 2334f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2335f4056b92SMickaël Salaün 2336f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3)); 2337f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2338f4056b92SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2)); 2339f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2340f4056b92SMickaël Salaün 2341f4056b92SMickaël Salaün ASSERT_EQ(0, link(file2_s1d2, file1_s1d2)); 2342f4056b92SMickaël Salaün ASSERT_EQ(0, link(file2_s1d3, file1_s1d3)); 2343f4056b92SMickaël Salaün } 2344f4056b92SMickaël Salaün 2345f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_rename) 2346f4056b92SMickaël Salaün { 2347f4056b92SMickaël Salaün /* Same rules as for reparent_link. */ 2348f4056b92SMickaël Salaün const struct rule layer1[] = { 2349f4056b92SMickaël Salaün { 2350f4056b92SMickaël Salaün .path = dir_s1d2, 2351f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2352f4056b92SMickaël Salaün }, 2353f4056b92SMickaël Salaün { 2354f4056b92SMickaël Salaün .path = dir_s1d3, 2355f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2356f4056b92SMickaël Salaün }, 2357f4056b92SMickaël Salaün { 2358f4056b92SMickaël Salaün .path = dir_s2d2, 2359f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2360f4056b92SMickaël Salaün }, 2361f4056b92SMickaël Salaün { 2362f4056b92SMickaël Salaün .path = dir_s2d3, 2363f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2364f4056b92SMickaël Salaün }, 2365f4056b92SMickaël Salaün {}, 2366f4056b92SMickaël Salaün }; 2367f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2368f4056b92SMickaël Salaün _metadata, 2369f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2370f4056b92SMickaël Salaün 2371f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2372f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2373f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2374f4056b92SMickaël Salaün 2375f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2376f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2377f4056b92SMickaël Salaün 2378f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2379f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1, 2380f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2381f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2382f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1, 2383f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2384f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2385f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2386f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 2387f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2388f4056b92SMickaël Salaün /* Even denies same file exchange. */ 2389f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1, 2390f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2391f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2392f4056b92SMickaël Salaün 2393f4056b92SMickaël Salaün /* Denies renaming because of missing source and destination REFER. */ 2394f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2)); 2395f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2396f4056b92SMickaël Salaün /* 2397f4056b92SMickaël Salaün * Denies renaming because of missing MAKE_REG, source and destination 2398f4056b92SMickaël Salaün * REFER. 2399f4056b92SMickaël Salaün */ 2400f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1, 2401f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2402f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2403f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1, 2404f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2405f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2406f4056b92SMickaël Salaün 2407f4056b92SMickaël Salaün /* Denies renaming because of missing source REFER. */ 2408f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3)); 2409f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2410f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2411f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3, 2412f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2413f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2414f4056b92SMickaël Salaün 2415f4056b92SMickaël Salaün /* Denies renaming because of missing MAKE_REG. */ 2416f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1)); 2417f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2418f4056b92SMickaël Salaün /* Denies renaming because of missing destination REFER*/ 2419f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2)); 2420f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2421f4056b92SMickaël Salaün 2422f4056b92SMickaël Salaün /* Denies exchange because of one missing MAKE_REG. */ 2423f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3, 2424f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2425f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2426f4056b92SMickaël Salaün /* Allows renaming because of REFER and MAKE_REG. */ 2427f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3)); 2428f4056b92SMickaël Salaün 2429f4056b92SMickaël Salaün /* Reverse renaming denied because of missing MAKE_REG. */ 2430f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2)); 2431f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2432f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2433f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2434f4056b92SMickaël Salaün 2435f4056b92SMickaël Salaün /* Tests reverse renaming. */ 2436f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2437f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3, 2438f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2439f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2440f4056b92SMickaël Salaün 2441f4056b92SMickaël Salaün /* 2442f4056b92SMickaël Salaün * This is OK for a file rename, but it should not be allowed for a 2443f4056b92SMickaël Salaün * directory rename (because of the superset of access rights). 2444f4056b92SMickaël Salaün */ 2445f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3)); 2446f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2447f4056b92SMickaël Salaün 2448f4056b92SMickaël Salaün /* 2449f4056b92SMickaël Salaün * Tests superset restrictions applied to directories. Not only the 2450f4056b92SMickaël Salaün * dir_s2d3's parent (dir_s2d2) should be taken into account but also 2451f4056b92SMickaël Salaün * access rights tied to dir_s2d3. dir_s2d2 is missing one access right 2452f4056b92SMickaël Salaün * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided 2453f4056b92SMickaël Salaün * directly by the moved dir_s2d3. 2454f4056b92SMickaël Salaün */ 2455f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3)); 2456f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3)); 2457f4056b92SMickaël Salaün /* 2458f4056b92SMickaël Salaün * The first rename is allowed but not the exchange because dir_s1d3's 2459f4056b92SMickaël Salaün * parent (dir_s1d2) doesn't have REFER. 2460f4056b92SMickaël Salaün */ 2461f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3, 2462f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2463f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2464f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3, 2465f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2466f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2467f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3)); 2468f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2469f4056b92SMickaël Salaün 2470f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3)); 2471f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2472f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2)); 2473f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2474f4056b92SMickaël Salaün 2475f4056b92SMickaël Salaün /* Renaming in the same directory is always allowed. */ 2476f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2)); 2477f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3)); 2478f4056b92SMickaël Salaün 2479f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2480f4056b92SMickaël Salaün /* Denies because of missing source MAKE_REG and destination REFER. */ 2481f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2)); 2482f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2483f4056b92SMickaël Salaün 2484f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2485f4056b92SMickaël Salaün /* Denies because of missing source MAKE_REG and REFER. */ 2486f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3)); 2487f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2488f4056b92SMickaël Salaün } 2489f4056b92SMickaël Salaün 2490f4056b92SMickaël Salaün static void 2491f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata) 2492f4056b92SMickaël Salaün { 2493f4056b92SMickaël Salaün const struct rule layer1[] = { 2494f4056b92SMickaël Salaün { 2495f4056b92SMickaël Salaün .path = dir_s1d2, 2496f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2497f4056b92SMickaël Salaün }, 2498f4056b92SMickaël Salaün { 2499f4056b92SMickaël Salaün /* Interesting for the layer2 tests. */ 2500f4056b92SMickaël Salaün .path = dir_s1d3, 2501f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2502f4056b92SMickaël Salaün }, 2503f4056b92SMickaël Salaün { 2504f4056b92SMickaël Salaün .path = dir_s2d2, 2505f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2506f4056b92SMickaël Salaün }, 2507f4056b92SMickaël Salaün { 2508f4056b92SMickaël Salaün .path = dir_s2d3, 2509f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_REG, 2510f4056b92SMickaël Salaün }, 2511f4056b92SMickaël Salaün {}, 2512f4056b92SMickaël Salaün }; 2513f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2514f4056b92SMickaël Salaün _metadata, 2515f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1); 2516f4056b92SMickaël Salaün 2517f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2518f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2519f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2520f4056b92SMickaël Salaün } 2521f4056b92SMickaël Salaün 2522f4056b92SMickaël Salaün static void 2523f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata) 2524f4056b92SMickaël Salaün { 2525f4056b92SMickaël Salaün const struct rule layer2[] = { 2526f4056b92SMickaël Salaün { 2527f4056b92SMickaël Salaün .path = dir_s2d3, 2528f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 2529f4056b92SMickaël Salaün }, 2530f4056b92SMickaël Salaün {}, 2531f4056b92SMickaël Salaün }; 2532f4056b92SMickaël Salaün /* 2533f4056b92SMickaël Salaün * Same checks as before but with a second layer and a new MAKE_DIR 2534f4056b92SMickaël Salaün * rule (and no explicit handling of REFER). 2535f4056b92SMickaël Salaün */ 2536f4056b92SMickaël Salaün const int ruleset_fd = 2537f4056b92SMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2); 2538f4056b92SMickaël Salaün 2539f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2540f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2541f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2542f4056b92SMickaël Salaün } 2543f4056b92SMickaël Salaün 2544f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename1) 2545f4056b92SMickaël Salaün { 2546f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d2)); 2547f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d3)); 2548f4056b92SMickaël Salaün 2549f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2550f4056b92SMickaël Salaün 2551f4056b92SMickaël Salaün /* 2552f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock 2553f4056b92SMickaël Salaün * because it doesn't inherit new access rights. 2554f4056b92SMickaël Salaün */ 2555f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2556f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2557f4056b92SMickaël Salaün 2558f4056b92SMickaël Salaün /* 2559f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it 2560f4056b92SMickaël Salaün * gets a new inherited access rights (MAKE_REG), because MAKE_REG is 2561f4056b92SMickaël Salaün * already allowed for dir_s1d3. 2562f4056b92SMickaël Salaün */ 2563f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3)); 2564f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3)); 2565f4056b92SMickaël Salaün 2566f4056b92SMickaël Salaün /* 2567f4056b92SMickaël Salaün * However, moving the file1_s1d3 file below dir_s2d3 is allowed 2568f4056b92SMickaël Salaün * because it cannot inherit MAKE_REG right (which is dedicated to 2569f4056b92SMickaël Salaün * directories). 2570f4056b92SMickaël Salaün */ 2571f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3)); 2572f4056b92SMickaël Salaün 2573f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2574f4056b92SMickaël Salaün 2575f4056b92SMickaël Salaün /* 2576f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d2 is now denied because 2577f4056b92SMickaël Salaün * MAKE_DIR is not tied to dir_s2d2. 2578f4056b92SMickaël Salaün */ 2579f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2)); 2580f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2581f4056b92SMickaël Salaün 2582f4056b92SMickaël Salaün /* 2583f4056b92SMickaël Salaün * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it 2584f4056b92SMickaël Salaün * would grants MAKE_REG and MAKE_DIR rights to it. 2585f4056b92SMickaël Salaün */ 2586f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2587f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2588f4056b92SMickaël Salaün 2589f4056b92SMickaël Salaün /* 259055e55920SMickaël Salaün * Moving the file2_s1d3 file below dir_s2d3 is denied because the 259155e55920SMickaël Salaün * second layer does not handle REFER, which is always denied by 259255e55920SMickaël Salaün * default. 2593f4056b92SMickaël Salaün */ 259455e55920SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3)); 259555e55920SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2596f4056b92SMickaël Salaün } 2597f4056b92SMickaël Salaün 2598f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_rename2) 2599f4056b92SMickaël Salaün { 2600f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2601f4056b92SMickaël Salaün 2602f4056b92SMickaël Salaün /* Checks EACCES predominance over EXDEV. */ 2603f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2604f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2605f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2)); 2606f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2607f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2608f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2609f4056b92SMickaël Salaün /* Modify layout! */ 2610f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3)); 2611f4056b92SMickaël Salaün 2612f4056b92SMickaël Salaün /* Without REFER source. */ 2613f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2614f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2615f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2616f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2617f4056b92SMickaël Salaün 2618f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2619f4056b92SMickaël Salaün 2620f4056b92SMickaël Salaün /* Checks EACCES predominance over EXDEV. */ 2621f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2)); 2622f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2623f4056b92SMickaël Salaün /* Checks with actual file2_s1d2. */ 2624f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2)); 2625f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2626f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3)); 2627f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 262855e55920SMickaël Salaün /* 262955e55920SMickaël Salaün * Modifying the layout is now denied because the second layer does not 263055e55920SMickaël Salaün * handle REFER, which is always denied by default. 263155e55920SMickaël Salaün */ 263255e55920SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 263355e55920SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2634f4056b92SMickaël Salaün 2635f4056b92SMickaël Salaün /* Without REFER source, EACCES wins over EXDEV. */ 2636f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2)); 2637f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2638f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2)); 2639f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2640f4056b92SMickaël Salaün } 2641f4056b92SMickaël Salaün 2642f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange1) 2643f4056b92SMickaël Salaün { 2644f4056b92SMickaël Salaün const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 = 2645f4056b92SMickaël Salaün file2_s2d3; 2646f4056b92SMickaël Salaün 2647f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2648f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 2649f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2650f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2651f4056b92SMickaël Salaün 2652f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2653f4056b92SMickaël Salaün 2654f4056b92SMickaël Salaün /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2655f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2656f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2657f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2658f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2659f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2660f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2661f4056b92SMickaël Salaün 2662f4056b92SMickaël Salaün /* 2663f4056b92SMickaël Salaün * Checks with directories which creation could be allowed, but denied 2664f4056b92SMickaël Salaün * because of access rights that would be inherited. 2665f4056b92SMickaël Salaün */ 2666f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2667f4056b92SMickaël Salaün dir_file2_s2d3, RENAME_EXCHANGE)); 2668f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2669f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2670f4056b92SMickaël Salaün dir_file1_s1d2, RENAME_EXCHANGE)); 2671f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2672f4056b92SMickaël Salaün 2673f4056b92SMickaël Salaün /* Checks with same access rights. */ 2674f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2675f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2676f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2677f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2678f4056b92SMickaël Salaün 2679f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2680f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2681f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2682f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2683f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2684f4056b92SMickaël Salaün 2685f4056b92SMickaël Salaün /* 2686f4056b92SMickaël Salaün * Checks that exchange between file and directory are consistent. 2687f4056b92SMickaël Salaün * 2688f4056b92SMickaël Salaün * Moving a file (file1_s2d2) to a directory which only grants more 2689f4056b92SMickaël Salaün * directory-related access rights is allowed, and at the same time 2690f4056b92SMickaël Salaün * moving a directory (dir_file2_s2d3) to another directory which 2691f4056b92SMickaël Salaün * grants less access rights is allowed too. 2692f4056b92SMickaël Salaün * 2693f4056b92SMickaël Salaün * See layout1.reparent_exdev_layers_exchange3 for inverted arguments. 2694f4056b92SMickaël Salaün */ 2695f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2696f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2697f4056b92SMickaël Salaün /* 2698f4056b92SMickaël Salaün * However, moving back the directory is denied because it would get 2699f4056b92SMickaël Salaün * more access rights than the current state and because file creation 2700f4056b92SMickaël Salaün * is forbidden (in dir_s2d2). 2701f4056b92SMickaël Salaün */ 2702f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2703f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2704f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2705f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2706f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2707f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2708f4056b92SMickaël Salaün 2709f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2710f4056b92SMickaël Salaün 2711f4056b92SMickaël Salaün /* Error predominance with file exchange: returns EXDEV and EACCES. */ 2712f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3, 2713f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2714f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2715f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1, 2716f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2717f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2718f4056b92SMickaël Salaün 2719f4056b92SMickaël Salaün /* Checks with directories which creation is now denied. */ 2720f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, 2721f4056b92SMickaël Salaün dir_file2_s2d3, RENAME_EXCHANGE)); 2722f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2723f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, 2724f4056b92SMickaël Salaün dir_file1_s1d2, RENAME_EXCHANGE)); 2725f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2726f4056b92SMickaël Salaün 2727f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2728f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3, 2729f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2730f4056b92SMickaël Salaün /* Denied because of MAKE_DIR. */ 2731f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2732f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3, 2733f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2734f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2735f4056b92SMickaël Salaün 2736f4056b92SMickaël Salaün /* Checks with different (child-only) access rights. */ 2737f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2, 2738f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2739f4056b92SMickaël Salaün /* Denied because of MAKE_DIR. */ 2740f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2741f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3, 2742f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2743f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2744f4056b92SMickaël Salaün 2745f4056b92SMickaël Salaün /* See layout1.reparent_exdev_layers_exchange2 for complement. */ 2746f4056b92SMickaël Salaün } 2747f4056b92SMickaël Salaün 2748f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange2) 2749f4056b92SMickaël Salaün { 2750f4056b92SMickaël Salaün const char *const dir_file2_s2d3 = file2_s2d3; 2751f4056b92SMickaël Salaün 2752f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2753f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2754f4056b92SMickaël Salaün 2755f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2756f4056b92SMickaël Salaün reparent_exdev_layers_enforce2(_metadata); 2757f4056b92SMickaël Salaün 2758f4056b92SMickaël Salaün /* Checks that exchange between file and directory are consistent. */ 2759f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2760f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2761f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2762f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2763f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2764f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2765f4056b92SMickaël Salaün } 2766f4056b92SMickaël Salaün 2767f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_exdev_layers_exchange3) 2768f4056b92SMickaël Salaün { 2769f4056b92SMickaël Salaün const char *const dir_file2_s2d3 = file2_s2d3; 2770f4056b92SMickaël Salaün 2771f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2772f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(file2_s2d3, 0700)); 2773f4056b92SMickaël Salaün 2774f4056b92SMickaël Salaün reparent_exdev_layers_enforce1(_metadata); 2775f4056b92SMickaël Salaün 2776f4056b92SMickaël Salaün /* 2777f4056b92SMickaël Salaün * Checks that exchange between file and directory are consistent, 2778f4056b92SMickaël Salaün * including with inverted arguments (see 2779f4056b92SMickaël Salaün * layout1.reparent_exdev_layers_exchange1). 2780f4056b92SMickaël Salaün */ 2781f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2782f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2783f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3, 2784f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2785f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2786f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2, 2787f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2788f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2789f4056b92SMickaël Salaün } 2790f4056b92SMickaël Salaün 2791f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_remove) 2792f4056b92SMickaël Salaün { 2793f4056b92SMickaël Salaün const struct rule layer1[] = { 2794f4056b92SMickaël Salaün { 2795f4056b92SMickaël Salaün .path = dir_s1d1, 2796f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2797f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_DIR, 2798f4056b92SMickaël Salaün }, 2799f4056b92SMickaël Salaün { 2800f4056b92SMickaël Salaün .path = dir_s1d2, 2801f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2802f4056b92SMickaël Salaün }, 2803f4056b92SMickaël Salaün { 2804f4056b92SMickaël Salaün .path = dir_s2d1, 2805f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2806f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE, 2807f4056b92SMickaël Salaün }, 2808f4056b92SMickaël Salaün {}, 2809f4056b92SMickaël Salaün }; 2810f4056b92SMickaël Salaün const int ruleset_fd = create_ruleset( 2811f4056b92SMickaël Salaün _metadata, 2812f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR | 2813f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REMOVE_FILE, 2814f4056b92SMickaël Salaün layer1); 2815f4056b92SMickaël Salaün 2816f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2817f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2818f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2819f4056b92SMickaël Salaün 2820f4056b92SMickaël Salaün /* Access denied because of wrong/swapped remove file/dir. */ 2821f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2)); 2822f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2823f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1)); 2824f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2825f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2, 2826f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2827f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2828f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3, 2829f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2830f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2831f4056b92SMickaël Salaün 2832f4056b92SMickaël Salaün /* Access allowed thanks to the matching rights. */ 2833f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2)); 2834f4056b92SMickaël Salaün ASSERT_EQ(EISDIR, errno); 2835f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1)); 2836f4056b92SMickaël Salaün ASSERT_EQ(ENOTDIR, errno); 2837f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2838f4056b92SMickaël Salaün ASSERT_EQ(ENOTDIR, errno); 2839f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s2d1)); 2840f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2841f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2842f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1)); 2843f4056b92SMickaël Salaün 2844f4056b92SMickaël Salaün /* Effectively removes a file and a directory by exchanging them. */ 2845f4056b92SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2846f4056b92SMickaël Salaün ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2847f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2848f4056b92SMickaël Salaün ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3, 2849f4056b92SMickaël Salaün RENAME_EXCHANGE)); 2850f4056b92SMickaël Salaün ASSERT_EQ(EACCES, errno); 2851f4056b92SMickaël Salaün } 2852f4056b92SMickaël Salaün 2853f4056b92SMickaël Salaün TEST_F_FORK(layout1, reparent_dom_superset) 2854f4056b92SMickaël Salaün { 2855f4056b92SMickaël Salaün const struct rule layer1[] = { 2856f4056b92SMickaël Salaün { 2857f4056b92SMickaël Salaün .path = dir_s1d2, 2858f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 2859f4056b92SMickaël Salaün }, 2860f4056b92SMickaël Salaün { 2861f4056b92SMickaël Salaün .path = file1_s1d2, 2862f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 2863f4056b92SMickaël Salaün }, 2864f4056b92SMickaël Salaün { 2865f4056b92SMickaël Salaün .path = dir_s1d3, 2866f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_SOCK | 2867f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE, 2868f4056b92SMickaël Salaün }, 2869f4056b92SMickaël Salaün { 2870f4056b92SMickaël Salaün .path = dir_s2d2, 2871f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER | 2872f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | 2873f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK, 2874f4056b92SMickaël Salaün }, 2875f4056b92SMickaël Salaün { 2876f4056b92SMickaël Salaün .path = dir_s2d3, 2877f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 2878f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO, 2879f4056b92SMickaël Salaün }, 2880f4056b92SMickaël Salaün {}, 2881f4056b92SMickaël Salaün }; 2882f4056b92SMickaël Salaün int ruleset_fd = create_ruleset(_metadata, 2883f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | 2884f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_EXECUTE | 2885f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_SOCK | 2886f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | 2887f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_MAKE_FIFO, 2888f4056b92SMickaël Salaün layer1); 2889f4056b92SMickaël Salaün 2890f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2891f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2892f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2893f4056b92SMickaël Salaün 2894f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1)); 2895f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2896f4056b92SMickaël Salaün /* 2897f4056b92SMickaël Salaün * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE 2898f4056b92SMickaël Salaün * access right. 2899f4056b92SMickaël Salaün */ 2900f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3)); 2901f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2902f4056b92SMickaël Salaün /* 2903f4056b92SMickaël Salaün * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a 2904f4056b92SMickaël Salaün * superset of access rights compared to dir_s1d2, because file1_s1d2 2905f4056b92SMickaël Salaün * already has these access rights anyway. 2906f4056b92SMickaël Salaün */ 2907f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2)); 2908f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2)); 2909f4056b92SMickaël Salaün 2910f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1)); 2911f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2912f4056b92SMickaël Salaün /* 2913f4056b92SMickaël Salaün * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access 2914f4056b92SMickaël Salaün * right. 2915f4056b92SMickaël Salaün */ 2916f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3)); 2917f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2918f4056b92SMickaël Salaün /* 2919f4056b92SMickaël Salaün * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset 2920f4056b92SMickaël Salaün * of access rights compared to dir_s1d2, because dir_s1d3 already has 2921f4056b92SMickaël Salaün * these access rights anyway. 2922f4056b92SMickaël Salaün */ 2923f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2)); 2924f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3)); 2925f4056b92SMickaël Salaün 2926f4056b92SMickaël Salaün /* 2927f4056b92SMickaël Salaün * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back 2928f4056b92SMickaël Salaün * will be denied because the new inherited access rights from dir_s1d2 2929f4056b92SMickaël Salaün * will be less than the destination (original) dir_s2d3. This is a 2930f4056b92SMickaël Salaün * sinkhole scenario where we cannot move back files or directories. 2931f4056b92SMickaël Salaün */ 2932f4056b92SMickaël Salaün ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2)); 2933f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3)); 2934f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2935f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 2936f4056b92SMickaël Salaün ASSERT_EQ(0, unlink(file2_s2d3)); 2937f4056b92SMickaël Salaün /* 2938f4056b92SMickaël Salaün * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and 2939f4056b92SMickaël Salaün * MAKE_SOCK which were inherited from dir_s1d3. 2940f4056b92SMickaël Salaün */ 2941f4056b92SMickaël Salaün ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2)); 2942f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3)); 2943f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 2944f4056b92SMickaël Salaün } 2945f4056b92SMickaël Salaün 2946e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_dir) 2947e1199815SMickaël Salaün { 2948e1199815SMickaël Salaün const struct rule rules[] = { 2949e1199815SMickaël Salaün { 2950e1199815SMickaël Salaün .path = dir_s1d2, 2951e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_DIR, 2952e1199815SMickaël Salaün }, 2953135464f9SMickaël Salaün {}, 2954e1199815SMickaël Salaün }; 2955371183faSMickaël Salaün const int ruleset_fd = 2956371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2957e1199815SMickaël Salaün 2958e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2959e1199815SMickaël Salaün 2960e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 2961e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 2962e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 2963e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 2964e1199815SMickaël Salaün 2965e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2966e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2967e1199815SMickaël Salaün 2968e1199815SMickaël Salaün ASSERT_EQ(0, rmdir(dir_s1d3)); 2969e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(dir_s1d3, 0700)); 2970e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR)); 2971e1199815SMickaël Salaün 2972e1199815SMickaël Salaün /* dir_s1d2 itself cannot be removed. */ 2973e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d2)); 2974e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2975e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR)); 2976e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2977e1199815SMickaël Salaün ASSERT_EQ(-1, rmdir(dir_s1d1)); 2978e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2979e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR)); 2980e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 2981e1199815SMickaël Salaün } 2982e1199815SMickaël Salaün 2983e1199815SMickaël Salaün TEST_F_FORK(layout1, remove_file) 2984e1199815SMickaël Salaün { 2985e1199815SMickaël Salaün const struct rule rules[] = { 2986e1199815SMickaël Salaün { 2987e1199815SMickaël Salaün .path = dir_s1d2, 2988e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REMOVE_FILE, 2989e1199815SMickaël Salaün }, 2990135464f9SMickaël Salaün {}, 2991e1199815SMickaël Salaün }; 2992371183faSMickaël Salaün const int ruleset_fd = 2993371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 2994e1199815SMickaël Salaün 2995e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 2996e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 2997e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 2998e1199815SMickaël Salaün 2999e1199815SMickaël Salaün ASSERT_EQ(-1, unlink(file1_s1d1)); 3000e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3001e1199815SMickaël Salaün ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0)); 3002e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3003e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3004e1199815SMickaël Salaün ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0)); 3005e1199815SMickaël Salaün } 3006e1199815SMickaël Salaün 3007e1199815SMickaël Salaün static void test_make_file(struct __test_metadata *const _metadata, 3008371183faSMickaël Salaün const __u64 access, const mode_t mode, 3009371183faSMickaël Salaün const dev_t dev) 3010e1199815SMickaël Salaün { 3011e1199815SMickaël Salaün const struct rule rules[] = { 3012e1199815SMickaël Salaün { 3013e1199815SMickaël Salaün .path = dir_s1d2, 3014e1199815SMickaël Salaün .access = access, 3015e1199815SMickaël Salaün }, 3016135464f9SMickaël Salaün {}, 3017e1199815SMickaël Salaün }; 3018e1199815SMickaël Salaün const int ruleset_fd = create_ruleset(_metadata, access, rules); 3019e1199815SMickaël Salaün 3020e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3021e1199815SMickaël Salaün 3022e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 3023e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 3024371183faSMickaël Salaün ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev)) 3025371183faSMickaël Salaün { 3026371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file2_s1d1, 3027371183faSMickaël Salaün strerror(errno)); 3028e1199815SMickaël Salaün }; 3029e1199815SMickaël Salaün 3030e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3031e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 3032e1199815SMickaël Salaün 3033e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 3034e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 3035e1199815SMickaël Salaün 3036e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3037e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3038e1199815SMickaël Salaün 3039e1199815SMickaël Salaün ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev)); 3040e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3041e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3042e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3043e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3044e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3045e1199815SMickaël Salaün 3046371183faSMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev)) 3047371183faSMickaël Salaün { 3048371183faSMickaël Salaün TH_LOG("Failed to make file \"%s\": %s", file1_s1d2, 3049371183faSMickaël Salaün strerror(errno)); 3050e1199815SMickaël Salaün }; 3051e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3052e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 3053e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3054e1199815SMickaël Salaün 3055e1199815SMickaël Salaün ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev)); 3056e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3057e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 3058e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3059e1199815SMickaël Salaün } 3060e1199815SMickaël Salaün 3061e1199815SMickaël Salaün TEST_F_FORK(layout1, make_char) 3062e1199815SMickaël Salaün { 3063e1199815SMickaël Salaün /* Creates a /dev/null device. */ 3064e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 3065e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR, 3066e1199815SMickaël Salaün makedev(1, 3)); 3067e1199815SMickaël Salaün } 3068e1199815SMickaël Salaün 3069e1199815SMickaël Salaün TEST_F_FORK(layout1, make_block) 3070e1199815SMickaël Salaün { 3071e1199815SMickaël Salaün /* Creates a /dev/loop0 device. */ 3072e1199815SMickaël Salaün set_cap(_metadata, CAP_MKNOD); 3073e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK, 3074e1199815SMickaël Salaün makedev(7, 0)); 3075e1199815SMickaël Salaün } 3076e1199815SMickaël Salaün 3077e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_1) 3078e1199815SMickaël Salaün { 3079e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0); 3080e1199815SMickaël Salaün } 3081e1199815SMickaël Salaün 3082e1199815SMickaël Salaün TEST_F_FORK(layout1, make_reg_2) 3083e1199815SMickaël Salaün { 3084e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0); 3085e1199815SMickaël Salaün } 3086e1199815SMickaël Salaün 3087e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sock) 3088e1199815SMickaël Salaün { 3089e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0); 3090e1199815SMickaël Salaün } 3091e1199815SMickaël Salaün 3092e1199815SMickaël Salaün TEST_F_FORK(layout1, make_fifo) 3093e1199815SMickaël Salaün { 3094e1199815SMickaël Salaün test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0); 3095e1199815SMickaël Salaün } 3096e1199815SMickaël Salaün 3097e1199815SMickaël Salaün TEST_F_FORK(layout1, make_sym) 3098e1199815SMickaël Salaün { 3099e1199815SMickaël Salaün const struct rule rules[] = { 3100e1199815SMickaël Salaün { 3101e1199815SMickaël Salaün .path = dir_s1d2, 3102e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_SYM, 3103e1199815SMickaël Salaün }, 3104135464f9SMickaël Salaün {}, 3105e1199815SMickaël Salaün }; 3106371183faSMickaël Salaün const int ruleset_fd = 3107371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3108e1199815SMickaël Salaün 3109e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3110e1199815SMickaël Salaün 3111e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 3112e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d1)); 3113e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file2_s1d1)); 3114e1199815SMickaël Salaün 3115e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3116e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 3117e1199815SMickaël Salaün 3118e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 3119e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 3120e1199815SMickaël Salaün 3121e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3122e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3123e1199815SMickaël Salaün 3124e1199815SMickaël Salaün ASSERT_EQ(-1, symlink("none", file1_s1d1)); 3125e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3126e1199815SMickaël Salaün ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1)); 3127e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3128e1199815SMickaël Salaün ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1)); 3129e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3130e1199815SMickaël Salaün 3131e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d2)); 3132e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d2, file2_s1d2)); 3133e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d2)); 3134e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2)); 3135e1199815SMickaël Salaün 3136e1199815SMickaël Salaün ASSERT_EQ(0, symlink("none", file1_s1d3)); 3137e1199815SMickaël Salaün ASSERT_EQ(0, link(file1_s1d3, file2_s1d3)); 3138e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file2_s1d3)); 3139e1199815SMickaël Salaün ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3)); 3140e1199815SMickaël Salaün } 3141e1199815SMickaël Salaün 3142e1199815SMickaël Salaün TEST_F_FORK(layout1, make_dir) 3143e1199815SMickaël Salaün { 3144e1199815SMickaël Salaün const struct rule rules[] = { 3145e1199815SMickaël Salaün { 3146e1199815SMickaël Salaün .path = dir_s1d2, 3147e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_MAKE_DIR, 3148e1199815SMickaël Salaün }, 3149135464f9SMickaël Salaün {}, 3150e1199815SMickaël Salaün }; 3151371183faSMickaël Salaün const int ruleset_fd = 3152371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3153e1199815SMickaël Salaün 3154e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3155e1199815SMickaël Salaün 3156e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d1)); 3157e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3158e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d3)); 3159e1199815SMickaël Salaün 3160e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3161e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3162e1199815SMickaël Salaün 3163e1199815SMickaël Salaün /* Uses file_* as directory names. */ 3164e1199815SMickaël Salaün ASSERT_EQ(-1, mkdir(file1_s1d1, 0700)); 3165e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3166e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d2, 0700)); 3167e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(file1_s1d3, 0700)); 3168e1199815SMickaël Salaün } 3169e1199815SMickaël Salaün 3170e1199815SMickaël Salaün static int open_proc_fd(struct __test_metadata *const _metadata, const int fd, 3171e1199815SMickaël Salaün const int open_flags) 3172e1199815SMickaël Salaün { 3173e1199815SMickaël Salaün static const char path_template[] = "/proc/self/fd/%d"; 3174e1199815SMickaël Salaün char procfd_path[sizeof(path_template) + 10]; 3175371183faSMickaël Salaün const int procfd_path_size = 3176371183faSMickaël Salaün snprintf(procfd_path, sizeof(procfd_path), path_template, fd); 3177e1199815SMickaël Salaün 3178e1199815SMickaël Salaün ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 3179e1199815SMickaël Salaün return open(procfd_path, open_flags); 3180e1199815SMickaël Salaün } 3181e1199815SMickaël Salaün 3182e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_unlinked_file) 3183e1199815SMickaël Salaün { 3184e1199815SMickaël Salaün const struct rule rules[] = { 3185e1199815SMickaël Salaün { 3186e1199815SMickaël Salaün .path = file1_s1d2, 3187e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3188e1199815SMickaël Salaün }, 3189135464f9SMickaël Salaün {}, 3190e1199815SMickaël Salaün }; 3191e1199815SMickaël Salaün int reg_fd, proc_fd; 3192371183faSMickaël Salaün const int ruleset_fd = create_ruleset( 3193371183faSMickaël Salaün _metadata, 3194371183faSMickaël Salaün LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE, 3195371183faSMickaël Salaün rules); 3196e1199815SMickaël Salaün 3197e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3198e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3199e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3200e1199815SMickaël Salaün 3201e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR)); 3202e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3203e1199815SMickaël Salaün reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC); 3204e1199815SMickaël Salaün ASSERT_LE(0, reg_fd); 3205e1199815SMickaël Salaün ASSERT_EQ(0, unlink(file1_s1d2)); 3206e1199815SMickaël Salaün 3207e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC); 3208e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3209e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3210e1199815SMickaël Salaün 3211e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC); 3212371183faSMickaël Salaün ASSERT_EQ(-1, proc_fd) 3213371183faSMickaël Salaün { 3214371183faSMickaël Salaün TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd, 3215371183faSMickaël Salaün strerror(errno)); 3216e1199815SMickaël Salaün } 3217e1199815SMickaël Salaün ASSERT_EQ(EACCES, errno); 3218e1199815SMickaël Salaün 3219e1199815SMickaël Salaün ASSERT_EQ(0, close(reg_fd)); 3220e1199815SMickaël Salaün } 3221e1199815SMickaël Salaün 3222e1199815SMickaël Salaün TEST_F_FORK(layout1, proc_pipe) 3223e1199815SMickaël Salaün { 3224e1199815SMickaël Salaün int proc_fd; 3225e1199815SMickaël Salaün int pipe_fds[2]; 3226e1199815SMickaël Salaün char buf = '\0'; 3227e1199815SMickaël Salaün const struct rule rules[] = { 3228e1199815SMickaël Salaün { 3229e1199815SMickaël Salaün .path = dir_s1d2, 3230e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 3231e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 3232e1199815SMickaël Salaün }, 3233135464f9SMickaël Salaün {}, 3234e1199815SMickaël Salaün }; 3235e1199815SMickaël Salaün /* Limits read and write access to files tied to the filesystem. */ 3236371183faSMickaël Salaün const int ruleset_fd = 3237371183faSMickaël Salaün create_ruleset(_metadata, rules[0].access, rules); 3238e1199815SMickaël Salaün 3239e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3240e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3241e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3242e1199815SMickaël Salaün 3243e1199815SMickaël Salaün /* Checks enforcement for normal files. */ 3244e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR)); 3245e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR)); 3246e1199815SMickaël Salaün 3247e1199815SMickaël Salaün /* Checks access to pipes through FD. */ 3248e1199815SMickaël Salaün ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC)); 3249371183faSMickaël Salaün ASSERT_EQ(1, write(pipe_fds[1], ".", 1)) 3250371183faSMickaël Salaün { 3251e1199815SMickaël Salaün TH_LOG("Failed to write in pipe: %s", strerror(errno)); 3252e1199815SMickaël Salaün } 3253e1199815SMickaël Salaün ASSERT_EQ(1, read(pipe_fds[0], &buf, 1)); 3254e1199815SMickaël Salaün ASSERT_EQ('.', buf); 3255e1199815SMickaël Salaün 3256e1199815SMickaël Salaün /* Checks write access to pipe through /proc/self/fd . */ 3257e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC); 3258e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3259371183faSMickaël Salaün ASSERT_EQ(1, write(proc_fd, ".", 1)) 3260371183faSMickaël Salaün { 3261e1199815SMickaël Salaün TH_LOG("Failed to write through /proc/self/fd/%d: %s", 3262e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 3263e1199815SMickaël Salaün } 3264e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3265e1199815SMickaël Salaün 3266e1199815SMickaël Salaün /* Checks read access to pipe through /proc/self/fd . */ 3267e1199815SMickaël Salaün proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC); 3268e1199815SMickaël Salaün ASSERT_LE(0, proc_fd); 3269e1199815SMickaël Salaün buf = '\0'; 3270371183faSMickaël Salaün ASSERT_EQ(1, read(proc_fd, &buf, 1)) 3271371183faSMickaël Salaün { 3272e1199815SMickaël Salaün TH_LOG("Failed to read through /proc/self/fd/%d: %s", 3273e1199815SMickaël Salaün pipe_fds[1], strerror(errno)); 3274e1199815SMickaël Salaün } 3275e1199815SMickaël Salaün ASSERT_EQ(0, close(proc_fd)); 3276e1199815SMickaël Salaün 3277e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[0])); 3278e1199815SMickaël Salaün ASSERT_EQ(0, close(pipe_fds[1])); 3279e1199815SMickaël Salaün } 3280e1199815SMickaël Salaün 3281225351abSGünther Noack /* Invokes truncate(2) and returns its errno or 0. */ 3282225351abSGünther Noack static int test_truncate(const char *const path) 3283225351abSGünther Noack { 3284225351abSGünther Noack if (truncate(path, 10) < 0) 3285225351abSGünther Noack return errno; 3286225351abSGünther Noack return 0; 3287225351abSGünther Noack } 3288225351abSGünther Noack 3289225351abSGünther Noack /* 3290225351abSGünther Noack * Invokes creat(2) and returns its errno or 0. 3291225351abSGünther Noack * Closes the opened file descriptor on success. 3292225351abSGünther Noack */ 3293225351abSGünther Noack static int test_creat(const char *const path) 3294225351abSGünther Noack { 3295225351abSGünther Noack int fd = creat(path, 0600); 3296225351abSGünther Noack 3297225351abSGünther Noack if (fd < 0) 3298225351abSGünther Noack return errno; 3299225351abSGünther Noack 3300225351abSGünther Noack /* 3301225351abSGünther Noack * Mixing error codes from close(2) and creat(2) should not lead to any 3302225351abSGünther Noack * (access type) confusion for this test. 3303225351abSGünther Noack */ 3304225351abSGünther Noack if (close(fd) < 0) 3305225351abSGünther Noack return errno; 3306225351abSGünther Noack return 0; 3307225351abSGünther Noack } 3308225351abSGünther Noack 3309225351abSGünther Noack /* 3310225351abSGünther Noack * Exercises file truncation when it's not restricted, 3311225351abSGünther Noack * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed. 3312225351abSGünther Noack */ 3313225351abSGünther Noack TEST_F_FORK(layout1, truncate_unhandled) 3314225351abSGünther Noack { 3315225351abSGünther Noack const char *const file_r = file1_s1d1; 3316225351abSGünther Noack const char *const file_w = file2_s1d1; 3317225351abSGünther Noack const char *const file_none = file1_s1d2; 3318225351abSGünther Noack const struct rule rules[] = { 3319225351abSGünther Noack { 3320225351abSGünther Noack .path = file_r, 3321225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE, 3322225351abSGünther Noack }, 3323225351abSGünther Noack { 3324225351abSGünther Noack .path = file_w, 3325225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3326225351abSGünther Noack }, 3327225351abSGünther Noack /* Implicitly: No rights for file_none. */ 3328225351abSGünther Noack {}, 3329225351abSGünther Noack }; 3330225351abSGünther Noack 3331225351abSGünther Noack const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3332225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE; 3333225351abSGünther Noack int ruleset_fd; 3334225351abSGünther Noack 3335225351abSGünther Noack /* Enable Landlock. */ 3336225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled, rules); 3337225351abSGünther Noack 3338225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3339225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3340225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3341225351abSGünther Noack 3342225351abSGünther Noack /* 3343225351abSGünther Noack * Checks read right: truncate and open with O_TRUNC work, unless the 3344225351abSGünther Noack * file is attempted to be opened for writing. 3345225351abSGünther Noack */ 3346225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_r)); 3347225351abSGünther Noack EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC)); 3348225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC)); 3349225351abSGünther Noack EXPECT_EQ(EACCES, test_creat(file_r)); 3350225351abSGünther Noack 3351225351abSGünther Noack /* 3352225351abSGünther Noack * Checks write right: truncate and open with O_TRUNC work, unless the 3353225351abSGünther Noack * file is attempted to be opened for reading. 3354225351abSGünther Noack */ 3355225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_w)); 3356225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC)); 3357225351abSGünther Noack EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC)); 3358225351abSGünther Noack EXPECT_EQ(0, test_creat(file_w)); 3359225351abSGünther Noack 3360225351abSGünther Noack /* 3361225351abSGünther Noack * Checks "no rights" case: truncate works but all open attempts fail, 3362225351abSGünther Noack * including creat. 3363225351abSGünther Noack */ 3364225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_none)); 3365225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3366225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3367225351abSGünther Noack EXPECT_EQ(EACCES, test_creat(file_none)); 3368225351abSGünther Noack } 3369225351abSGünther Noack 3370225351abSGünther Noack TEST_F_FORK(layout1, truncate) 3371225351abSGünther Noack { 3372225351abSGünther Noack const char *const file_rwt = file1_s1d1; 3373225351abSGünther Noack const char *const file_rw = file2_s1d1; 3374225351abSGünther Noack const char *const file_rt = file1_s1d2; 3375225351abSGünther Noack const char *const file_t = file2_s1d2; 3376225351abSGünther Noack const char *const file_none = file1_s1d3; 3377225351abSGünther Noack const char *const dir_t = dir_s2d1; 3378225351abSGünther Noack const char *const file_in_dir_t = file1_s2d1; 3379225351abSGünther Noack const char *const dir_w = dir_s3d1; 3380225351abSGünther Noack const char *const file_in_dir_w = file1_s3d1; 3381225351abSGünther Noack const struct rule rules[] = { 3382225351abSGünther Noack { 3383225351abSGünther Noack .path = file_rwt, 3384225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE | 3385225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE | 3386225351abSGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE, 3387225351abSGünther Noack }, 3388225351abSGünther Noack { 3389225351abSGünther Noack .path = file_rw, 3390225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE | 3391225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE, 3392225351abSGünther Noack }, 3393225351abSGünther Noack { 3394225351abSGünther Noack .path = file_rt, 3395225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_READ_FILE | 3396225351abSGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE, 3397225351abSGünther Noack }, 3398225351abSGünther Noack { 3399225351abSGünther Noack .path = file_t, 3400225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3401225351abSGünther Noack }, 3402225351abSGünther Noack /* Implicitly: No access rights for file_none. */ 3403225351abSGünther Noack { 3404225351abSGünther Noack .path = dir_t, 3405225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3406225351abSGünther Noack }, 3407225351abSGünther Noack { 3408225351abSGünther Noack .path = dir_w, 3409225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3410225351abSGünther Noack }, 3411225351abSGünther Noack {}, 3412225351abSGünther Noack }; 3413225351abSGünther Noack const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE | 3414225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE | 3415225351abSGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE; 3416225351abSGünther Noack int ruleset_fd; 3417225351abSGünther Noack 3418225351abSGünther Noack /* Enable Landlock. */ 3419225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled, rules); 3420225351abSGünther Noack 3421225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3422225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3423225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3424225351abSGünther Noack 3425225351abSGünther Noack /* Checks read, write and truncate rights: truncation works. */ 3426225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_rwt)); 3427225351abSGünther Noack EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC)); 3428225351abSGünther Noack EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC)); 3429225351abSGünther Noack 3430225351abSGünther Noack /* Checks read and write rights: no truncate variant works. */ 3431225351abSGünther Noack EXPECT_EQ(EACCES, test_truncate(file_rw)); 3432225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC)); 3433225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC)); 3434225351abSGünther Noack 3435225351abSGünther Noack /* 3436225351abSGünther Noack * Checks read and truncate rights: truncation works. 3437225351abSGünther Noack * 3438225351abSGünther Noack * Note: Files can get truncated using open() even with O_RDONLY. 3439225351abSGünther Noack */ 3440225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_rt)); 3441225351abSGünther Noack EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC)); 3442225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC)); 3443225351abSGünther Noack 3444225351abSGünther Noack /* Checks truncate right: truncate works, but can't open file. */ 3445225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_t)); 3446225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC)); 3447225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC)); 3448225351abSGünther Noack 3449225351abSGünther Noack /* Checks "no rights" case: No form of truncation works. */ 3450225351abSGünther Noack EXPECT_EQ(EACCES, test_truncate(file_none)); 3451225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC)); 3452225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC)); 3453225351abSGünther Noack 3454225351abSGünther Noack /* 3455225351abSGünther Noack * Checks truncate right on directory: truncate works on contained 3456225351abSGünther Noack * files. 3457225351abSGünther Noack */ 3458225351abSGünther Noack EXPECT_EQ(0, test_truncate(file_in_dir_t)); 3459225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC)); 3460225351abSGünther Noack EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC)); 3461225351abSGünther Noack 3462225351abSGünther Noack /* 3463225351abSGünther Noack * Checks creat in dir_w: This requires the truncate right when 3464225351abSGünther Noack * overwriting an existing file, but does not require it when the file 3465225351abSGünther Noack * is new. 3466225351abSGünther Noack */ 3467225351abSGünther Noack EXPECT_EQ(EACCES, test_creat(file_in_dir_w)); 3468225351abSGünther Noack 3469225351abSGünther Noack ASSERT_EQ(0, unlink(file_in_dir_w)); 3470225351abSGünther Noack EXPECT_EQ(0, test_creat(file_in_dir_w)); 3471225351abSGünther Noack } 3472225351abSGünther Noack 3473225351abSGünther Noack /* Invokes ftruncate(2) and returns its errno or 0. */ 3474225351abSGünther Noack static int test_ftruncate(int fd) 3475225351abSGünther Noack { 3476225351abSGünther Noack if (ftruncate(fd, 10) < 0) 3477225351abSGünther Noack return errno; 3478225351abSGünther Noack return 0; 3479225351abSGünther Noack } 3480225351abSGünther Noack 3481225351abSGünther Noack TEST_F_FORK(layout1, ftruncate) 3482225351abSGünther Noack { 3483225351abSGünther Noack /* 3484225351abSGünther Noack * This test opens a new file descriptor at different stages of 3485225351abSGünther Noack * Landlock restriction: 3486225351abSGünther Noack * 3487225351abSGünther Noack * without restriction: ftruncate works 3488225351abSGünther Noack * something else but truncate restricted: ftruncate works 3489225351abSGünther Noack * truncate restricted and permitted: ftruncate works 3490225351abSGünther Noack * truncate restricted and not permitted: ftruncate fails 3491225351abSGünther Noack * 3492225351abSGünther Noack * Whether this works or not is expected to depend on the time when the 3493225351abSGünther Noack * FD was opened, not to depend on the time when ftruncate() was 3494225351abSGünther Noack * called. 3495225351abSGünther Noack */ 3496225351abSGünther Noack const char *const path = file1_s1d1; 3497225351abSGünther Noack const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE | 3498225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE; 3499225351abSGünther Noack const struct rule layer1[] = { 3500225351abSGünther Noack { 3501225351abSGünther Noack .path = path, 3502225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3503225351abSGünther Noack }, 3504225351abSGünther Noack {}, 3505225351abSGünther Noack }; 3506225351abSGünther Noack const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE; 3507225351abSGünther Noack const struct rule layer2[] = { 3508225351abSGünther Noack { 3509225351abSGünther Noack .path = path, 3510225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_TRUNCATE, 3511225351abSGünther Noack }, 3512225351abSGünther Noack {}, 3513225351abSGünther Noack }; 3514225351abSGünther Noack const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE | 3515225351abSGünther Noack LANDLOCK_ACCESS_FS_WRITE_FILE; 3516225351abSGünther Noack const struct rule layer3[] = { 3517225351abSGünther Noack { 3518225351abSGünther Noack .path = path, 3519225351abSGünther Noack .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3520225351abSGünther Noack }, 3521225351abSGünther Noack {}, 3522225351abSGünther Noack }; 3523225351abSGünther Noack int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd; 3524225351abSGünther Noack 3525225351abSGünther Noack fd_layer0 = open(path, O_WRONLY); 3526225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3527225351abSGünther Noack 3528225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled1, layer1); 3529225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3530225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3531225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3532225351abSGünther Noack 3533225351abSGünther Noack fd_layer1 = open(path, O_WRONLY); 3534225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3535225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3536225351abSGünther Noack 3537225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled2, layer2); 3538225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3539225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3540225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3541225351abSGünther Noack 3542225351abSGünther Noack fd_layer2 = open(path, O_WRONLY); 3543225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3544225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3545225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3546225351abSGünther Noack 3547225351abSGünther Noack ruleset_fd = create_ruleset(_metadata, handled3, layer3); 3548225351abSGünther Noack ASSERT_LE(0, ruleset_fd); 3549225351abSGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3550225351abSGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3551225351abSGünther Noack 3552225351abSGünther Noack fd_layer3 = open(path, O_WRONLY); 3553225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer0)); 3554225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer1)); 3555225351abSGünther Noack EXPECT_EQ(0, test_ftruncate(fd_layer2)); 3556225351abSGünther Noack EXPECT_EQ(EACCES, test_ftruncate(fd_layer3)); 3557225351abSGünther Noack 3558225351abSGünther Noack ASSERT_EQ(0, close(fd_layer0)); 3559225351abSGünther Noack ASSERT_EQ(0, close(fd_layer1)); 3560225351abSGünther Noack ASSERT_EQ(0, close(fd_layer2)); 3561225351abSGünther Noack ASSERT_EQ(0, close(fd_layer3)); 3562225351abSGünther Noack } 3563225351abSGünther Noack 35644598d9abSMickaël Salaün /* clang-format off */ 356541729af2SGünther Noack FIXTURE(ftruncate) {}; 356641729af2SGünther Noack /* clang-format on */ 356741729af2SGünther Noack 356841729af2SGünther Noack FIXTURE_SETUP(ftruncate) 356941729af2SGünther Noack { 357041729af2SGünther Noack prepare_layout(_metadata); 357141729af2SGünther Noack create_file(_metadata, file1_s1d1); 357241729af2SGünther Noack } 357341729af2SGünther Noack 357441729af2SGünther Noack FIXTURE_TEARDOWN(ftruncate) 357541729af2SGünther Noack { 357641729af2SGünther Noack EXPECT_EQ(0, remove_path(file1_s1d1)); 357741729af2SGünther Noack cleanup_layout(_metadata); 357841729af2SGünther Noack } 357941729af2SGünther Noack 358041729af2SGünther Noack FIXTURE_VARIANT(ftruncate) 358141729af2SGünther Noack { 358241729af2SGünther Noack const __u64 handled; 358341729af2SGünther Noack const __u64 permitted; 358441729af2SGünther Noack const int expected_open_result; 358541729af2SGünther Noack const int expected_ftruncate_result; 358641729af2SGünther Noack }; 358741729af2SGünther Noack 358841729af2SGünther Noack /* clang-format off */ 358941729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, w_w) { 359041729af2SGünther Noack /* clang-format on */ 359141729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE, 359241729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 359341729af2SGünther Noack .expected_open_result = 0, 359441729af2SGünther Noack .expected_ftruncate_result = 0, 359541729af2SGünther Noack }; 359641729af2SGünther Noack 359741729af2SGünther Noack /* clang-format off */ 359841729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, t_t) { 359941729af2SGünther Noack /* clang-format on */ 360041729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_TRUNCATE, 360141729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 360241729af2SGünther Noack .expected_open_result = 0, 360341729af2SGünther Noack .expected_ftruncate_result = 0, 360441729af2SGünther Noack }; 360541729af2SGünther Noack 360641729af2SGünther Noack /* clang-format off */ 360741729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_w) { 360841729af2SGünther Noack /* clang-format on */ 360941729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 361041729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE, 361141729af2SGünther Noack .expected_open_result = 0, 361241729af2SGünther Noack .expected_ftruncate_result = EACCES, 361341729af2SGünther Noack }; 361441729af2SGünther Noack 361541729af2SGünther Noack /* clang-format off */ 361641729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_wt) { 361741729af2SGünther Noack /* clang-format on */ 361841729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 361941729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_WRITE_FILE | 362041729af2SGünther Noack LANDLOCK_ACCESS_FS_TRUNCATE, 362141729af2SGünther Noack .expected_open_result = 0, 362241729af2SGünther Noack .expected_ftruncate_result = 0, 362341729af2SGünther Noack }; 362441729af2SGünther Noack 362541729af2SGünther Noack /* clang-format off */ 362641729af2SGünther Noack FIXTURE_VARIANT_ADD(ftruncate, wt_t) { 362741729af2SGünther Noack /* clang-format on */ 362841729af2SGünther Noack .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, 362941729af2SGünther Noack .permitted = LANDLOCK_ACCESS_FS_TRUNCATE, 363041729af2SGünther Noack .expected_open_result = EACCES, 363141729af2SGünther Noack }; 363241729af2SGünther Noack 363341729af2SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate) 363441729af2SGünther Noack { 363541729af2SGünther Noack const char *const path = file1_s1d1; 363641729af2SGünther Noack const struct rule rules[] = { 363741729af2SGünther Noack { 363841729af2SGünther Noack .path = path, 363941729af2SGünther Noack .access = variant->permitted, 364041729af2SGünther Noack }, 364141729af2SGünther Noack {}, 364241729af2SGünther Noack }; 364341729af2SGünther Noack int fd, ruleset_fd; 364441729af2SGünther Noack 364541729af2SGünther Noack /* Enable Landlock. */ 364641729af2SGünther Noack ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 364741729af2SGünther Noack ASSERT_LE(0, ruleset_fd); 364841729af2SGünther Noack enforce_ruleset(_metadata, ruleset_fd); 364941729af2SGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 365041729af2SGünther Noack 365141729af2SGünther Noack fd = open(path, O_WRONLY); 365241729af2SGünther Noack EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 365341729af2SGünther Noack if (fd >= 0) { 365441729af2SGünther Noack EXPECT_EQ(variant->expected_ftruncate_result, 365541729af2SGünther Noack test_ftruncate(fd)); 365641729af2SGünther Noack ASSERT_EQ(0, close(fd)); 365741729af2SGünther Noack } 365841729af2SGünther Noack } 365941729af2SGünther Noack 3660a1a202a5SGünther Noack TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes) 3661a1a202a5SGünther Noack { 3662a1a202a5SGünther Noack int child, fd, status; 3663a1a202a5SGünther Noack int socket_fds[2]; 3664a1a202a5SGünther Noack 3665a1a202a5SGünther Noack ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, 3666a1a202a5SGünther Noack socket_fds)); 3667a1a202a5SGünther Noack 3668a1a202a5SGünther Noack child = fork(); 3669a1a202a5SGünther Noack ASSERT_LE(0, child); 3670a1a202a5SGünther Noack if (child == 0) { 3671a1a202a5SGünther Noack /* 3672a1a202a5SGünther Noack * Enables Landlock in the child process, open a file descriptor 3673a1a202a5SGünther Noack * where truncation is forbidden and send it to the 3674a1a202a5SGünther Noack * non-landlocked parent process. 3675a1a202a5SGünther Noack */ 3676a1a202a5SGünther Noack const char *const path = file1_s1d1; 3677a1a202a5SGünther Noack const struct rule rules[] = { 3678a1a202a5SGünther Noack { 3679a1a202a5SGünther Noack .path = path, 3680a1a202a5SGünther Noack .access = variant->permitted, 3681a1a202a5SGünther Noack }, 3682a1a202a5SGünther Noack {}, 3683a1a202a5SGünther Noack }; 3684a1a202a5SGünther Noack int fd, ruleset_fd; 3685a1a202a5SGünther Noack 3686a1a202a5SGünther Noack ruleset_fd = create_ruleset(_metadata, variant->handled, rules); 3687a1a202a5SGünther Noack ASSERT_LE(0, ruleset_fd); 3688a1a202a5SGünther Noack enforce_ruleset(_metadata, ruleset_fd); 3689a1a202a5SGünther Noack ASSERT_EQ(0, close(ruleset_fd)); 3690a1a202a5SGünther Noack 3691a1a202a5SGünther Noack fd = open(path, O_WRONLY); 3692a1a202a5SGünther Noack ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0)); 3693a1a202a5SGünther Noack 3694a1a202a5SGünther Noack if (fd >= 0) { 3695a1a202a5SGünther Noack ASSERT_EQ(0, send_fd(socket_fds[0], fd)); 3696a1a202a5SGünther Noack ASSERT_EQ(0, close(fd)); 3697a1a202a5SGünther Noack } 3698a1a202a5SGünther Noack 3699a1a202a5SGünther Noack ASSERT_EQ(0, close(socket_fds[0])); 3700a1a202a5SGünther Noack 3701a1a202a5SGünther Noack _exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE); 3702a1a202a5SGünther Noack return; 3703a1a202a5SGünther Noack } 3704a1a202a5SGünther Noack 3705a1a202a5SGünther Noack if (variant->expected_open_result == 0) { 3706a1a202a5SGünther Noack fd = recv_fd(socket_fds[1]); 3707a1a202a5SGünther Noack ASSERT_LE(0, fd); 3708a1a202a5SGünther Noack 3709a1a202a5SGünther Noack EXPECT_EQ(variant->expected_ftruncate_result, 3710a1a202a5SGünther Noack test_ftruncate(fd)); 3711a1a202a5SGünther Noack ASSERT_EQ(0, close(fd)); 3712a1a202a5SGünther Noack } 3713a1a202a5SGünther Noack 3714a1a202a5SGünther Noack ASSERT_EQ(child, waitpid(child, &status, 0)); 3715a1a202a5SGünther Noack ASSERT_EQ(1, WIFEXITED(status)); 3716a1a202a5SGünther Noack ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 3717a1a202a5SGünther Noack 3718a1a202a5SGünther Noack ASSERT_EQ(0, close(socket_fds[0])); 3719a1a202a5SGünther Noack ASSERT_EQ(0, close(socket_fds[1])); 3720a1a202a5SGünther Noack } 3721a1a202a5SGünther Noack 37220d8c658bSGünther Noack TEST(memfd_ftruncate) 37230d8c658bSGünther Noack { 37240d8c658bSGünther Noack int fd; 37250d8c658bSGünther Noack 37260d8c658bSGünther Noack fd = memfd_create("name", MFD_CLOEXEC); 37270d8c658bSGünther Noack ASSERT_LE(0, fd); 37280d8c658bSGünther Noack 37290d8c658bSGünther Noack /* 37300d8c658bSGünther Noack * Checks that ftruncate is permitted on file descriptors that are 37310d8c658bSGünther Noack * created in ways other than open(2). 37320d8c658bSGünther Noack */ 37330d8c658bSGünther Noack EXPECT_EQ(0, test_ftruncate(fd)); 37340d8c658bSGünther Noack 37350d8c658bSGünther Noack ASSERT_EQ(0, close(fd)); 37360d8c658bSGünther Noack } 37370d8c658bSGünther Noack 373841729af2SGünther Noack /* clang-format off */ 37394598d9abSMickaël Salaün FIXTURE(layout1_bind) {}; 37404598d9abSMickaël Salaün /* clang-format on */ 3741e1199815SMickaël Salaün 3742e1199815SMickaël Salaün FIXTURE_SETUP(layout1_bind) 3743e1199815SMickaël Salaün { 3744e1199815SMickaël Salaün prepare_layout(_metadata); 3745e1199815SMickaël Salaün 3746e1199815SMickaël Salaün create_layout1(_metadata); 3747e1199815SMickaël Salaün 3748e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3749e1199815SMickaël Salaün ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL)); 3750e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3751e1199815SMickaël Salaün } 3752e1199815SMickaël Salaün 3753e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout1_bind) 3754e1199815SMickaël Salaün { 3755e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 3756e1199815SMickaël Salaün EXPECT_EQ(0, umount(dir_s2d2)); 3757e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 3758e1199815SMickaël Salaün 3759e1199815SMickaël Salaün remove_layout1(_metadata); 3760e1199815SMickaël Salaün 3761e1199815SMickaël Salaün cleanup_layout(_metadata); 3762e1199815SMickaël Salaün } 3763e1199815SMickaël Salaün 3764e1199815SMickaël Salaün static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3"; 3765e1199815SMickaël Salaün static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1"; 3766e1199815SMickaël Salaün 3767e1199815SMickaël Salaün /* 3768e1199815SMickaël Salaün * layout1_bind hierarchy: 3769e1199815SMickaël Salaün * 3770e1199815SMickaël Salaün * tmp 3771e1199815SMickaël Salaün * ├── s1d1 3772e1199815SMickaël Salaün * │ ├── f1 3773e1199815SMickaël Salaün * │ ├── f2 3774e1199815SMickaël Salaün * │ └── s1d2 3775e1199815SMickaël Salaün * │ ├── f1 3776e1199815SMickaël Salaün * │ ├── f2 3777e1199815SMickaël Salaün * │ └── s1d3 3778e1199815SMickaël Salaün * │ ├── f1 3779e1199815SMickaël Salaün * │ └── f2 3780e1199815SMickaël Salaün * ├── s2d1 3781e1199815SMickaël Salaün * │ ├── f1 3782e1199815SMickaël Salaün * │ └── s2d2 3783e1199815SMickaël Salaün * │ ├── f1 3784e1199815SMickaël Salaün * │ ├── f2 3785e1199815SMickaël Salaün * │ └── s1d3 3786e1199815SMickaël Salaün * │ ├── f1 3787e1199815SMickaël Salaün * │ └── f2 3788e1199815SMickaël Salaün * └── s3d1 3789e1199815SMickaël Salaün * └── s3d2 3790e1199815SMickaël Salaün * └── s3d3 3791e1199815SMickaël Salaün */ 3792e1199815SMickaël Salaün 3793e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, no_restriction) 3794e1199815SMickaël Salaün { 3795e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY)); 3796e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3797e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY)); 3798e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3799e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY)); 3800e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3801e1199815SMickaël Salaün 3802e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY)); 3803e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY)); 3804e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY)); 3805e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY)); 3806e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY)); 3807e1199815SMickaël Salaün ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY)); 3808e1199815SMickaël Salaün 3809e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY)); 3810e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3811e1199815SMickaël Salaün 3812e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY)); 3813e1199815SMickaël Salaün } 3814e1199815SMickaël Salaün 3815e1199815SMickaël Salaün TEST_F_FORK(layout1_bind, same_content_same_file) 3816e1199815SMickaël Salaün { 3817e1199815SMickaël Salaün /* 3818e1199815SMickaël Salaün * Sets access right on parent directories of both source and 3819e1199815SMickaël Salaün * destination mount points. 3820e1199815SMickaël Salaün */ 3821e1199815SMickaël Salaün const struct rule layer1_parent[] = { 3822e1199815SMickaël Salaün { 3823e1199815SMickaël Salaün .path = dir_s1d1, 3824e1199815SMickaël Salaün .access = ACCESS_RO, 3825e1199815SMickaël Salaün }, 3826e1199815SMickaël Salaün { 3827e1199815SMickaël Salaün .path = dir_s2d1, 3828e1199815SMickaël Salaün .access = ACCESS_RW, 3829e1199815SMickaël Salaün }, 3830135464f9SMickaël Salaün {}, 3831e1199815SMickaël Salaün }; 3832e1199815SMickaël Salaün /* 3833e1199815SMickaël Salaün * Sets access rights on the same bind-mounted directories. The result 3834e1199815SMickaël Salaün * should be ACCESS_RW for both directories, but not both hierarchies 3835e1199815SMickaël Salaün * because of the first layer. 3836e1199815SMickaël Salaün */ 3837e1199815SMickaël Salaün const struct rule layer2_mount_point[] = { 3838e1199815SMickaël Salaün { 3839e1199815SMickaël Salaün .path = dir_s1d2, 3840e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3841e1199815SMickaël Salaün }, 3842e1199815SMickaël Salaün { 3843e1199815SMickaël Salaün .path = dir_s2d2, 3844e1199815SMickaël Salaün .access = ACCESS_RW, 3845e1199815SMickaël Salaün }, 3846135464f9SMickaël Salaün {}, 3847e1199815SMickaël Salaün }; 3848e1199815SMickaël Salaün /* Only allow read-access to the s1d3 hierarchies. */ 3849e1199815SMickaël Salaün const struct rule layer3_source[] = { 3850e1199815SMickaël Salaün { 3851e1199815SMickaël Salaün .path = dir_s1d3, 3852e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 3853e1199815SMickaël Salaün }, 3854135464f9SMickaël Salaün {}, 3855e1199815SMickaël Salaün }; 3856e1199815SMickaël Salaün /* Removes all access rights. */ 3857e1199815SMickaël Salaün const struct rule layer4_destination[] = { 3858e1199815SMickaël Salaün { 3859e1199815SMickaël Salaün .path = bind_file1_s1d3, 3860e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_WRITE_FILE, 3861e1199815SMickaël Salaün }, 3862135464f9SMickaël Salaün {}, 3863e1199815SMickaël Salaün }; 3864e1199815SMickaël Salaün int ruleset_fd; 3865e1199815SMickaël Salaün 3866e1199815SMickaël Salaün /* Sets rules for the parent directories. */ 3867e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent); 3868e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3869e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3870e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3871e1199815SMickaël Salaün 3872e1199815SMickaël Salaün /* Checks source hierarchy. */ 3873e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY)); 3874e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3875e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3876e1199815SMickaël Salaün 3877e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3878e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3879e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3880e1199815SMickaël Salaün 3881e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3882e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR)); 3883e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3884e1199815SMickaël Salaün 3885e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3886e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3887e1199815SMickaël Salaün 3888e1199815SMickaël Salaün /* Sets rules for the mount points. */ 3889e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point); 3890e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3891e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3892e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3893e1199815SMickaël Salaün 3894e1199815SMickaël Salaün /* Checks source hierarchy. */ 3895e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY)); 3896e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY)); 3897e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY)); 3898e1199815SMickaël Salaün 3899e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY)); 3900e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3901e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3902e1199815SMickaël Salaün 3903e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3904e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY)); 3905e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY)); 3906e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY)); 3907e1199815SMickaël Salaün 3908e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR)); 3909e1199815SMickaël Salaün ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3910e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3911e1199815SMickaël Salaün 3912e1199815SMickaël Salaün /* Sets a (shared) rule only on the source. */ 3913e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source); 3914e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3915e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3916e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3917e1199815SMickaël Salaün 3918e1199815SMickaël Salaün /* Checks source hierarchy. */ 3919e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY)); 3920e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY)); 3921e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY)); 3922e1199815SMickaël Salaün 3923e1199815SMickaël Salaün ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); 3924e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3925e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY)); 3926e1199815SMickaël Salaün 3927e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3928e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY)); 3929e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY)); 3930e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY)); 3931e1199815SMickaël Salaün 3932e1199815SMickaël Salaün ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY)); 3933e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3934e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY)); 3935e1199815SMickaël Salaün 3936e1199815SMickaël Salaün /* Sets a (shared) rule only on the destination. */ 3937e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination); 3938e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3939e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3940e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3941e1199815SMickaël Salaün 3942e1199815SMickaël Salaün /* Checks source hierarchy. */ 3943e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY)); 3944e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY)); 3945e1199815SMickaël Salaün 3946e1199815SMickaël Salaün /* Checks destination hierarchy. */ 3947e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY)); 3948e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY)); 3949e1199815SMickaël Salaün } 3950e1199815SMickaël Salaün 3951f4056b92SMickaël Salaün TEST_F_FORK(layout1_bind, reparent_cross_mount) 3952f4056b92SMickaël Salaün { 3953f4056b92SMickaël Salaün const struct rule layer1[] = { 3954f4056b92SMickaël Salaün { 3955f4056b92SMickaël Salaün /* dir_s2d1 is beneath the dir_s2d2 mount point. */ 3956f4056b92SMickaël Salaün .path = dir_s2d1, 3957f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_REFER, 3958f4056b92SMickaël Salaün }, 3959f4056b92SMickaël Salaün { 3960f4056b92SMickaël Salaün .path = bind_dir_s1d3, 3961f4056b92SMickaël Salaün .access = LANDLOCK_ACCESS_FS_EXECUTE, 3962f4056b92SMickaël Salaün }, 3963f4056b92SMickaël Salaün {}, 3964f4056b92SMickaël Salaün }; 3965f4056b92SMickaël Salaün int ruleset_fd = create_ruleset( 3966f4056b92SMickaël Salaün _metadata, 3967f4056b92SMickaël Salaün LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1); 3968f4056b92SMickaël Salaün 3969f4056b92SMickaël Salaün ASSERT_LE(0, ruleset_fd); 3970f4056b92SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 3971f4056b92SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 3972f4056b92SMickaël Salaün 3973f4056b92SMickaël Salaün /* Checks basic denied move. */ 3974f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2)); 3975f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3976f4056b92SMickaël Salaün 3977f4056b92SMickaël Salaün /* Checks real cross-mount move (Landlock is not involved). */ 3978f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2)); 3979f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3980f4056b92SMickaël Salaün 3981f4056b92SMickaël Salaün /* Checks move that will give more accesses. */ 3982f4056b92SMickaël Salaün ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3)); 3983f4056b92SMickaël Salaün ASSERT_EQ(EXDEV, errno); 3984f4056b92SMickaël Salaün 3985f4056b92SMickaël Salaün /* Checks legitimate downgrade move. */ 3986f4056b92SMickaël Salaün ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2)); 3987f4056b92SMickaël Salaün } 3988f4056b92SMickaël Salaün 3989e1199815SMickaël Salaün #define LOWER_BASE TMP_DIR "/lower" 3990e1199815SMickaël Salaün #define LOWER_DATA LOWER_BASE "/data" 3991e1199815SMickaël Salaün static const char lower_fl1[] = LOWER_DATA "/fl1"; 3992e1199815SMickaël Salaün static const char lower_dl1[] = LOWER_DATA "/dl1"; 3993e1199815SMickaël Salaün static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2"; 3994e1199815SMickaël Salaün static const char lower_fo1[] = LOWER_DATA "/fo1"; 3995e1199815SMickaël Salaün static const char lower_do1[] = LOWER_DATA "/do1"; 3996e1199815SMickaël Salaün static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2"; 3997e1199815SMickaël Salaün static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3"; 3998e1199815SMickaël Salaün 3999e1199815SMickaël Salaün static const char (*lower_base_files[])[] = { 4000e1199815SMickaël Salaün &lower_fl1, 4001e1199815SMickaël Salaün &lower_fo1, 4002135464f9SMickaël Salaün NULL, 4003e1199815SMickaël Salaün }; 4004e1199815SMickaël Salaün static const char (*lower_base_directories[])[] = { 4005e1199815SMickaël Salaün &lower_dl1, 4006e1199815SMickaël Salaün &lower_do1, 4007135464f9SMickaël Salaün NULL, 4008e1199815SMickaël Salaün }; 4009e1199815SMickaël Salaün static const char (*lower_sub_files[])[] = { 4010e1199815SMickaël Salaün &lower_dl1_fl2, 4011e1199815SMickaël Salaün &lower_do1_fo2, 4012e1199815SMickaël Salaün &lower_do1_fl3, 4013135464f9SMickaël Salaün NULL, 4014e1199815SMickaël Salaün }; 4015e1199815SMickaël Salaün 4016e1199815SMickaël Salaün #define UPPER_BASE TMP_DIR "/upper" 4017e1199815SMickaël Salaün #define UPPER_DATA UPPER_BASE "/data" 4018e1199815SMickaël Salaün #define UPPER_WORK UPPER_BASE "/work" 4019e1199815SMickaël Salaün static const char upper_fu1[] = UPPER_DATA "/fu1"; 4020e1199815SMickaël Salaün static const char upper_du1[] = UPPER_DATA "/du1"; 4021e1199815SMickaël Salaün static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2"; 4022e1199815SMickaël Salaün static const char upper_fo1[] = UPPER_DATA "/fo1"; 4023e1199815SMickaël Salaün static const char upper_do1[] = UPPER_DATA "/do1"; 4024e1199815SMickaël Salaün static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2"; 4025e1199815SMickaël Salaün static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3"; 4026e1199815SMickaël Salaün 4027e1199815SMickaël Salaün static const char (*upper_base_files[])[] = { 4028e1199815SMickaël Salaün &upper_fu1, 4029e1199815SMickaël Salaün &upper_fo1, 4030135464f9SMickaël Salaün NULL, 4031e1199815SMickaël Salaün }; 4032e1199815SMickaël Salaün static const char (*upper_base_directories[])[] = { 4033e1199815SMickaël Salaün &upper_du1, 4034e1199815SMickaël Salaün &upper_do1, 4035135464f9SMickaël Salaün NULL, 4036e1199815SMickaël Salaün }; 4037e1199815SMickaël Salaün static const char (*upper_sub_files[])[] = { 4038e1199815SMickaël Salaün &upper_du1_fu2, 4039e1199815SMickaël Salaün &upper_do1_fo2, 4040e1199815SMickaël Salaün &upper_do1_fu3, 4041135464f9SMickaël Salaün NULL, 4042e1199815SMickaël Salaün }; 4043e1199815SMickaël Salaün 4044e1199815SMickaël Salaün #define MERGE_BASE TMP_DIR "/merge" 4045e1199815SMickaël Salaün #define MERGE_DATA MERGE_BASE "/data" 4046e1199815SMickaël Salaün static const char merge_fl1[] = MERGE_DATA "/fl1"; 4047e1199815SMickaël Salaün static const char merge_dl1[] = MERGE_DATA "/dl1"; 4048e1199815SMickaël Salaün static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2"; 4049e1199815SMickaël Salaün static const char merge_fu1[] = MERGE_DATA "/fu1"; 4050e1199815SMickaël Salaün static const char merge_du1[] = MERGE_DATA "/du1"; 4051e1199815SMickaël Salaün static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2"; 4052e1199815SMickaël Salaün static const char merge_fo1[] = MERGE_DATA "/fo1"; 4053e1199815SMickaël Salaün static const char merge_do1[] = MERGE_DATA "/do1"; 4054e1199815SMickaël Salaün static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2"; 4055e1199815SMickaël Salaün static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3"; 4056e1199815SMickaël Salaün static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3"; 4057e1199815SMickaël Salaün 4058e1199815SMickaël Salaün static const char (*merge_base_files[])[] = { 4059e1199815SMickaël Salaün &merge_fl1, 4060e1199815SMickaël Salaün &merge_fu1, 4061e1199815SMickaël Salaün &merge_fo1, 4062135464f9SMickaël Salaün NULL, 4063e1199815SMickaël Salaün }; 4064e1199815SMickaël Salaün static const char (*merge_base_directories[])[] = { 4065e1199815SMickaël Salaün &merge_dl1, 4066e1199815SMickaël Salaün &merge_du1, 4067e1199815SMickaël Salaün &merge_do1, 4068135464f9SMickaël Salaün NULL, 4069e1199815SMickaël Salaün }; 4070e1199815SMickaël Salaün static const char (*merge_sub_files[])[] = { 4071371183faSMickaël Salaün &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2, 4072371183faSMickaël Salaün &merge_do1_fl3, &merge_do1_fu3, NULL, 4073e1199815SMickaël Salaün }; 4074e1199815SMickaël Salaün 4075e1199815SMickaël Salaün /* 4076e1199815SMickaël Salaün * layout2_overlay hierarchy: 4077e1199815SMickaël Salaün * 4078e1199815SMickaël Salaün * tmp 4079e1199815SMickaël Salaün * ├── lower 4080e1199815SMickaël Salaün * │ └── data 4081e1199815SMickaël Salaün * │ ├── dl1 4082e1199815SMickaël Salaün * │ │ └── fl2 4083e1199815SMickaël Salaün * │ ├── do1 4084e1199815SMickaël Salaün * │ │ ├── fl3 4085e1199815SMickaël Salaün * │ │ └── fo2 4086e1199815SMickaël Salaün * │ ├── fl1 4087e1199815SMickaël Salaün * │ └── fo1 4088e1199815SMickaël Salaün * ├── merge 4089e1199815SMickaël Salaün * │ └── data 4090e1199815SMickaël Salaün * │ ├── dl1 4091e1199815SMickaël Salaün * │ │ └── fl2 4092e1199815SMickaël Salaün * │ ├── do1 4093e1199815SMickaël Salaün * │ │ ├── fl3 4094e1199815SMickaël Salaün * │ │ ├── fo2 4095e1199815SMickaël Salaün * │ │ └── fu3 4096e1199815SMickaël Salaün * │ ├── du1 4097e1199815SMickaël Salaün * │ │ └── fu2 4098e1199815SMickaël Salaün * │ ├── fl1 4099e1199815SMickaël Salaün * │ ├── fo1 4100e1199815SMickaël Salaün * │ └── fu1 4101e1199815SMickaël Salaün * └── upper 4102e1199815SMickaël Salaün * ├── data 4103e1199815SMickaël Salaün * │ ├── do1 4104e1199815SMickaël Salaün * │ │ ├── fo2 4105e1199815SMickaël Salaün * │ │ └── fu3 4106e1199815SMickaël Salaün * │ ├── du1 4107e1199815SMickaël Salaün * │ │ └── fu2 4108e1199815SMickaël Salaün * │ ├── fo1 4109e1199815SMickaël Salaün * │ └── fu1 4110e1199815SMickaël Salaün * └── work 4111e1199815SMickaël Salaün * └── work 4112e1199815SMickaël Salaün */ 4113e1199815SMickaël Salaün 41143de64b65SMickaël Salaün FIXTURE(layout2_overlay) 41153de64b65SMickaël Salaün { 41163de64b65SMickaël Salaün bool skip_test; 41173de64b65SMickaël Salaün }; 4118e1199815SMickaël Salaün 4119e1199815SMickaël Salaün FIXTURE_SETUP(layout2_overlay) 4120e1199815SMickaël Salaün { 41213de64b65SMickaël Salaün if (!supports_filesystem("overlay")) { 41223de64b65SMickaël Salaün self->skip_test = true; 41233de64b65SMickaël Salaün SKIP(return, "overlayfs is not supported (setup)"); 41243de64b65SMickaël Salaün } 4125366617a6SJeff Xu 4126e1199815SMickaël Salaün prepare_layout(_metadata); 4127e1199815SMickaël Salaün 4128e1199815SMickaël Salaün create_directory(_metadata, LOWER_BASE); 4129e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4130e1199815SMickaël Salaün /* Creates tmpfs mount points to get deterministic overlayfs. */ 413155ab3fbeSMickaël Salaün ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); 4132e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4133e1199815SMickaël Salaün create_file(_metadata, lower_fl1); 4134e1199815SMickaël Salaün create_file(_metadata, lower_dl1_fl2); 4135e1199815SMickaël Salaün create_file(_metadata, lower_fo1); 4136e1199815SMickaël Salaün create_file(_metadata, lower_do1_fo2); 4137e1199815SMickaël Salaün create_file(_metadata, lower_do1_fl3); 4138e1199815SMickaël Salaün 4139e1199815SMickaël Salaün create_directory(_metadata, UPPER_BASE); 4140e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 414155ab3fbeSMickaël Salaün ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); 4142e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4143e1199815SMickaël Salaün create_file(_metadata, upper_fu1); 4144e1199815SMickaël Salaün create_file(_metadata, upper_du1_fu2); 4145e1199815SMickaël Salaün create_file(_metadata, upper_fo1); 4146e1199815SMickaël Salaün create_file(_metadata, upper_do1_fo2); 4147e1199815SMickaël Salaün create_file(_metadata, upper_do1_fu3); 4148e1199815SMickaël Salaün ASSERT_EQ(0, mkdir(UPPER_WORK, 0700)); 4149e1199815SMickaël Salaün 4150e1199815SMickaël Salaün create_directory(_metadata, MERGE_DATA); 4151e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4152e1199815SMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 4153e1199815SMickaël Salaün ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0, 4154371183faSMickaël Salaün "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA 4155e1199815SMickaël Salaün ",workdir=" UPPER_WORK)); 4156e1199815SMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 4157e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4158e1199815SMickaël Salaün } 4159e1199815SMickaël Salaün 4160e1199815SMickaël Salaün FIXTURE_TEARDOWN(layout2_overlay) 4161e1199815SMickaël Salaün { 41623de64b65SMickaël Salaün if (self->skip_test) 41633de64b65SMickaël Salaün SKIP(return, "overlayfs is not supported (teardown)"); 4164366617a6SJeff Xu 4165e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fl3)); 4166e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_dl1_fl2)); 4167e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fl1)); 4168e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_do1_fo2)); 4169e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(lower_fo1)); 4170e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4171e1199815SMickaël Salaün EXPECT_EQ(0, umount(LOWER_BASE)); 4172e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4173e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(LOWER_BASE)); 4174e1199815SMickaël Salaün 4175e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fu3)); 4176e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_du1_fu2)); 4177e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fu1)); 4178e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_do1_fo2)); 4179e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(upper_fo1)); 4180e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_WORK "/work")); 4181e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4182e1199815SMickaël Salaün EXPECT_EQ(0, umount(UPPER_BASE)); 4183e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4184e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(UPPER_BASE)); 4185e1199815SMickaël Salaün 4186e1199815SMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 4187e1199815SMickaël Salaün EXPECT_EQ(0, umount(MERGE_DATA)); 4188e1199815SMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 4189e1199815SMickaël Salaün EXPECT_EQ(0, remove_path(MERGE_DATA)); 4190e1199815SMickaël Salaün 4191e1199815SMickaël Salaün cleanup_layout(_metadata); 4192e1199815SMickaël Salaün } 4193e1199815SMickaël Salaün 4194e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, no_restriction) 4195e1199815SMickaël Salaün { 41963de64b65SMickaël Salaün if (self->skip_test) 41973de64b65SMickaël Salaün SKIP(return, "overlayfs is not supported (test)"); 4198366617a6SJeff Xu 4199e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); 4200e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); 4201e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY)); 4202e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY)); 4203e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1, O_RDONLY)); 4204e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY)); 4205e1199815SMickaël Salaün ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY)); 4206e1199815SMickaël Salaün 4207e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY)); 4208e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1, O_RDONLY)); 4209e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY)); 4210e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY)); 4211e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1, O_RDONLY)); 4212e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY)); 4213e1199815SMickaël Salaün ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY)); 4214e1199815SMickaël Salaün 4215e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY)); 4216e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY)); 4217e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY)); 4218e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY)); 4219e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1, O_RDONLY)); 4220e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY)); 4221e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY)); 4222e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1, O_RDONLY)); 4223e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY)); 4224e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY)); 4225e1199815SMickaël Salaün ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY)); 4226e1199815SMickaël Salaün } 4227e1199815SMickaël Salaün 4228e1199815SMickaël Salaün #define for_each_path(path_list, path_entry, i) \ 4229e1199815SMickaël Salaün for (i = 0, path_entry = *path_list[i]; path_list[i]; \ 4230e1199815SMickaël Salaün path_entry = *path_list[++i]) 4231e1199815SMickaël Salaün 4232e1199815SMickaël Salaün TEST_F_FORK(layout2_overlay, same_content_different_file) 4233e1199815SMickaël Salaün { 4234e1199815SMickaël Salaün /* Sets access right on parent directories of both layers. */ 4235e1199815SMickaël Salaün const struct rule layer1_base[] = { 4236e1199815SMickaël Salaün { 4237e1199815SMickaël Salaün .path = LOWER_BASE, 4238e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4239e1199815SMickaël Salaün }, 4240e1199815SMickaël Salaün { 4241e1199815SMickaël Salaün .path = UPPER_BASE, 4242e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4243e1199815SMickaël Salaün }, 4244e1199815SMickaël Salaün { 4245e1199815SMickaël Salaün .path = MERGE_BASE, 4246e1199815SMickaël Salaün .access = ACCESS_RW, 4247e1199815SMickaël Salaün }, 4248135464f9SMickaël Salaün {}, 4249e1199815SMickaël Salaün }; 4250e1199815SMickaël Salaün const struct rule layer2_data[] = { 4251e1199815SMickaël Salaün { 4252e1199815SMickaël Salaün .path = LOWER_DATA, 4253e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4254e1199815SMickaël Salaün }, 4255e1199815SMickaël Salaün { 4256e1199815SMickaël Salaün .path = UPPER_DATA, 4257e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4258e1199815SMickaël Salaün }, 4259e1199815SMickaël Salaün { 4260e1199815SMickaël Salaün .path = MERGE_DATA, 4261e1199815SMickaël Salaün .access = ACCESS_RW, 4262e1199815SMickaël Salaün }, 4263135464f9SMickaël Salaün {}, 4264e1199815SMickaël Salaün }; 4265e1199815SMickaël Salaün /* Sets access right on directories inside both layers. */ 4266e1199815SMickaël Salaün const struct rule layer3_subdirs[] = { 4267e1199815SMickaël Salaün { 4268e1199815SMickaël Salaün .path = lower_dl1, 4269e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4270e1199815SMickaël Salaün }, 4271e1199815SMickaël Salaün { 4272e1199815SMickaël Salaün .path = lower_do1, 4273e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4274e1199815SMickaël Salaün }, 4275e1199815SMickaël Salaün { 4276e1199815SMickaël Salaün .path = upper_du1, 4277e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4278e1199815SMickaël Salaün }, 4279e1199815SMickaël Salaün { 4280e1199815SMickaël Salaün .path = upper_do1, 4281e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4282e1199815SMickaël Salaün }, 4283e1199815SMickaël Salaün { 4284e1199815SMickaël Salaün .path = merge_dl1, 4285e1199815SMickaël Salaün .access = ACCESS_RW, 4286e1199815SMickaël Salaün }, 4287e1199815SMickaël Salaün { 4288e1199815SMickaël Salaün .path = merge_du1, 4289e1199815SMickaël Salaün .access = ACCESS_RW, 4290e1199815SMickaël Salaün }, 4291e1199815SMickaël Salaün { 4292e1199815SMickaël Salaün .path = merge_do1, 4293e1199815SMickaël Salaün .access = ACCESS_RW, 4294e1199815SMickaël Salaün }, 4295135464f9SMickaël Salaün {}, 4296e1199815SMickaël Salaün }; 4297e1199815SMickaël Salaün /* Tighten access rights to the files. */ 4298e1199815SMickaël Salaün const struct rule layer4_files[] = { 4299e1199815SMickaël Salaün { 4300e1199815SMickaël Salaün .path = lower_dl1_fl2, 4301e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4302e1199815SMickaël Salaün }, 4303e1199815SMickaël Salaün { 4304e1199815SMickaël Salaün .path = lower_do1_fo2, 4305e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4306e1199815SMickaël Salaün }, 4307e1199815SMickaël Salaün { 4308e1199815SMickaël Salaün .path = lower_do1_fl3, 4309e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4310e1199815SMickaël Salaün }, 4311e1199815SMickaël Salaün { 4312e1199815SMickaël Salaün .path = upper_du1_fu2, 4313e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4314e1199815SMickaël Salaün }, 4315e1199815SMickaël Salaün { 4316e1199815SMickaël Salaün .path = upper_do1_fo2, 4317e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4318e1199815SMickaël Salaün }, 4319e1199815SMickaël Salaün { 4320e1199815SMickaël Salaün .path = upper_do1_fu3, 4321e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 4322e1199815SMickaël Salaün }, 4323e1199815SMickaël Salaün { 4324e1199815SMickaël Salaün .path = merge_dl1_fl2, 4325e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4326e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4327e1199815SMickaël Salaün }, 4328e1199815SMickaël Salaün { 4329e1199815SMickaël Salaün .path = merge_du1_fu2, 4330e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4331e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4332e1199815SMickaël Salaün }, 4333e1199815SMickaël Salaün { 4334e1199815SMickaël Salaün .path = merge_do1_fo2, 4335e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4336e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4337e1199815SMickaël Salaün }, 4338e1199815SMickaël Salaün { 4339e1199815SMickaël Salaün .path = merge_do1_fl3, 4340e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4341e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4342e1199815SMickaël Salaün }, 4343e1199815SMickaël Salaün { 4344e1199815SMickaël Salaün .path = merge_do1_fu3, 4345e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4346e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4347e1199815SMickaël Salaün }, 4348135464f9SMickaël Salaün {}, 4349e1199815SMickaël Salaün }; 4350e1199815SMickaël Salaün const struct rule layer5_merge_only[] = { 4351e1199815SMickaël Salaün { 4352e1199815SMickaël Salaün .path = MERGE_DATA, 4353e1199815SMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE | 4354e1199815SMickaël Salaün LANDLOCK_ACCESS_FS_WRITE_FILE, 4355e1199815SMickaël Salaün }, 4356135464f9SMickaël Salaün {}, 4357e1199815SMickaël Salaün }; 4358e1199815SMickaël Salaün int ruleset_fd; 4359e1199815SMickaël Salaün size_t i; 4360e1199815SMickaël Salaün const char *path_entry; 4361e1199815SMickaël Salaün 43623de64b65SMickaël Salaün if (self->skip_test) 43633de64b65SMickaël Salaün SKIP(return, "overlayfs is not supported (test)"); 4364366617a6SJeff Xu 4365e1199815SMickaël Salaün /* Sets rules on base directories (i.e. outside overlay scope). */ 4366e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); 4367e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4368e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4369e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4370e1199815SMickaël Salaün 4371e1199815SMickaël Salaün /* Checks lower layer. */ 4372e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 4373e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4374e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4375e1199815SMickaël Salaün } 4376e1199815SMickaël Salaün for_each_path(lower_base_directories, path_entry, i) { 4377371183faSMickaël Salaün ASSERT_EQ(EACCES, 4378371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4379e1199815SMickaël Salaün } 4380e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 4381e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4382e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4383e1199815SMickaël Salaün } 4384e1199815SMickaël Salaün /* Checks upper layer. */ 4385e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 4386e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4387e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4388e1199815SMickaël Salaün } 4389e1199815SMickaël Salaün for_each_path(upper_base_directories, path_entry, i) { 4390371183faSMickaël Salaün ASSERT_EQ(EACCES, 4391371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4392e1199815SMickaël Salaün } 4393e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 4394e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4395e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4396e1199815SMickaël Salaün } 4397e1199815SMickaël Salaün /* 4398e1199815SMickaël Salaün * Checks that access rights are independent from the lower and upper 4399e1199815SMickaël Salaün * layers: write access to upper files viewed through the merge point 4400e1199815SMickaël Salaün * is still allowed, and write access to lower file viewed (and copied) 4401e1199815SMickaël Salaün * through the merge point is still allowed. 4402e1199815SMickaël Salaün */ 4403e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4404e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4405e1199815SMickaël Salaün } 4406e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4407e1199815SMickaël Salaün ASSERT_EQ(0, 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 /* Sets rules on data directories (i.e. inside overlay scope). */ 4414e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data); 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 merge. */ 4420e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4421e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4422e1199815SMickaël Salaün } 4423e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4424e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4425e1199815SMickaël Salaün } 4426e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4427e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4428e1199815SMickaël Salaün } 4429e1199815SMickaël Salaün 4430e1199815SMickaël Salaün /* Same checks with tighter rules. */ 4431e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs); 4432e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4433e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4434e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4435e1199815SMickaël Salaün 4436e1199815SMickaël Salaün /* Checks changes for lower layer. */ 4437e1199815SMickaël Salaün for_each_path(lower_base_files, path_entry, i) { 4438e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4439e1199815SMickaël Salaün } 4440e1199815SMickaël Salaün /* Checks changes for upper layer. */ 4441e1199815SMickaël Salaün for_each_path(upper_base_files, path_entry, i) { 4442e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4443e1199815SMickaël Salaün } 4444e1199815SMickaël Salaün /* Checks all merge accesses. */ 4445e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4446e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4447e1199815SMickaël Salaün } 4448e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4449e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4450e1199815SMickaël Salaün } 4451e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4452e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4453e1199815SMickaël Salaün } 4454e1199815SMickaël Salaün 4455e1199815SMickaël Salaün /* Sets rules directly on overlayed files. */ 4456e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files); 4457e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4458e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4459e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4460e1199815SMickaël Salaün 4461e1199815SMickaël Salaün /* Checks unchanged accesses on lower layer. */ 4462e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 4463e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4464e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4465e1199815SMickaël Salaün } 4466e1199815SMickaël Salaün /* Checks unchanged accesses on upper layer. */ 4467e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 4468e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDONLY)); 4469e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY)); 4470e1199815SMickaël Salaün } 4471e1199815SMickaël Salaün /* Checks all merge accesses. */ 4472e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4473e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4474e1199815SMickaël Salaün } 4475e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4476371183faSMickaël Salaün ASSERT_EQ(EACCES, 4477371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4478e1199815SMickaël Salaün } 4479e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4480e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4481e1199815SMickaël Salaün } 4482e1199815SMickaël Salaün 4483e1199815SMickaël Salaün /* Only allowes access to the merge hierarchy. */ 4484e1199815SMickaël Salaün ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only); 4485e1199815SMickaël Salaün ASSERT_LE(0, ruleset_fd); 4486e1199815SMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 4487e1199815SMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 4488e1199815SMickaël Salaün 4489e1199815SMickaël Salaün /* Checks new accesses on lower layer. */ 4490e1199815SMickaël Salaün for_each_path(lower_sub_files, path_entry, i) { 4491e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4492e1199815SMickaël Salaün } 4493e1199815SMickaël Salaün /* Checks new accesses on upper layer. */ 4494e1199815SMickaël Salaün for_each_path(upper_sub_files, path_entry, i) { 4495e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY)); 4496e1199815SMickaël Salaün } 4497e1199815SMickaël Salaün /* Checks all merge accesses. */ 4498e1199815SMickaël Salaün for_each_path(merge_base_files, path_entry, i) { 4499e1199815SMickaël Salaün ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR)); 4500e1199815SMickaël Salaün } 4501e1199815SMickaël Salaün for_each_path(merge_base_directories, path_entry, i) { 4502371183faSMickaël Salaün ASSERT_EQ(EACCES, 4503371183faSMickaël Salaün test_open(path_entry, O_RDONLY | O_DIRECTORY)); 4504e1199815SMickaël Salaün } 4505e1199815SMickaël Salaün for_each_path(merge_sub_files, path_entry, i) { 4506e1199815SMickaël Salaün ASSERT_EQ(0, test_open(path_entry, O_RDWR)); 4507e1199815SMickaël Salaün } 4508e1199815SMickaël Salaün } 4509e1199815SMickaël Salaün 451004f9070eSMickaël Salaün FIXTURE(layout3_fs) 451104f9070eSMickaël Salaün { 451204f9070eSMickaël Salaün bool has_created_dir; 451304f9070eSMickaël Salaün bool has_created_file; 451404f9070eSMickaël Salaün char *dir_path; 451504f9070eSMickaël Salaün bool skip_test; 451604f9070eSMickaël Salaün }; 451704f9070eSMickaël Salaün 451804f9070eSMickaël Salaün FIXTURE_VARIANT(layout3_fs) 451904f9070eSMickaël Salaün { 452004f9070eSMickaël Salaün const struct mnt_opt mnt; 452104f9070eSMickaël Salaün const char *const file_path; 452235ca4239SMickaël Salaün unsigned int cwd_fs_magic; 452304f9070eSMickaël Salaün }; 452404f9070eSMickaël Salaün 452504f9070eSMickaël Salaün /* clang-format off */ 452604f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { 452704f9070eSMickaël Salaün /* clang-format on */ 4528*854357dbSHu Yadi .mnt = { 4529*854357dbSHu Yadi .type = "tmpfs", 4530*854357dbSHu Yadi .data = MNT_TMP_DATA, 4531*854357dbSHu Yadi }, 453204f9070eSMickaël Salaün .file_path = file1_s1d1, 453304f9070eSMickaël Salaün }; 453404f9070eSMickaël Salaün 453504f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { 453604f9070eSMickaël Salaün .mnt = { 453704f9070eSMickaël Salaün .type = "ramfs", 453804f9070eSMickaël Salaün .data = "mode=700", 453904f9070eSMickaël Salaün }, 454004f9070eSMickaël Salaün .file_path = TMP_DIR "/dir/file", 454104f9070eSMickaël Salaün }; 454204f9070eSMickaël Salaün 454304f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { 454404f9070eSMickaël Salaün .mnt = { 454504f9070eSMickaël Salaün .type = "cgroup2", 454604f9070eSMickaël Salaün }, 454704f9070eSMickaël Salaün .file_path = TMP_DIR "/test/cgroup.procs", 454804f9070eSMickaël Salaün }; 454904f9070eSMickaël Salaün 455004f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, proc) { 455104f9070eSMickaël Salaün .mnt = { 455204f9070eSMickaël Salaün .type = "proc", 455304f9070eSMickaël Salaün }, 455404f9070eSMickaël Salaün .file_path = TMP_DIR "/self/status", 455504f9070eSMickaël Salaün }; 455604f9070eSMickaël Salaün 455704f9070eSMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { 455804f9070eSMickaël Salaün .mnt = { 455904f9070eSMickaël Salaün .type = "sysfs", 456004f9070eSMickaël Salaün }, 456104f9070eSMickaël Salaün .file_path = TMP_DIR "/kernel/notes", 456204f9070eSMickaël Salaün }; 456304f9070eSMickaël Salaün 456435ca4239SMickaël Salaün FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { 456535ca4239SMickaël Salaün .mnt = { 456635ca4239SMickaël Salaün .source = TMP_DIR, 456735ca4239SMickaël Salaün .flags = MS_BIND, 456835ca4239SMickaël Salaün }, 456935ca4239SMickaël Salaün .file_path = TMP_DIR "/dir/file", 457035ca4239SMickaël Salaün .cwd_fs_magic = HOSTFS_SUPER_MAGIC, 457135ca4239SMickaël Salaün }; 457235ca4239SMickaël Salaün 457304f9070eSMickaël Salaün FIXTURE_SETUP(layout3_fs) 457404f9070eSMickaël Salaün { 457504f9070eSMickaël Salaün struct stat statbuf; 457604f9070eSMickaël Salaün const char *slash; 457704f9070eSMickaël Salaün size_t dir_len; 457804f9070eSMickaël Salaün 457935ca4239SMickaël Salaün if (!supports_filesystem(variant->mnt.type) || 458035ca4239SMickaël Salaün !cwd_matches_fs(variant->cwd_fs_magic)) { 458104f9070eSMickaël Salaün self->skip_test = true; 458204f9070eSMickaël Salaün SKIP(return, "this filesystem is not supported (setup)"); 458304f9070eSMickaël Salaün } 458404f9070eSMickaël Salaün 458504f9070eSMickaël Salaün slash = strrchr(variant->file_path, '/'); 458604f9070eSMickaël Salaün ASSERT_NE(slash, NULL); 458704f9070eSMickaël Salaün dir_len = (size_t)slash - (size_t)variant->file_path; 458804f9070eSMickaël Salaün ASSERT_LT(0, dir_len); 458904f9070eSMickaël Salaün self->dir_path = malloc(dir_len + 1); 459004f9070eSMickaël Salaün self->dir_path[dir_len] = '\0'; 459104f9070eSMickaël Salaün strncpy(self->dir_path, variant->file_path, dir_len); 459204f9070eSMickaël Salaün 459304f9070eSMickaël Salaün prepare_layout_opt(_metadata, &variant->mnt); 459404f9070eSMickaël Salaün 459504f9070eSMickaël Salaün /* Creates directory when required. */ 459604f9070eSMickaël Salaün if (stat(self->dir_path, &statbuf)) { 459704f9070eSMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 459804f9070eSMickaël Salaün EXPECT_EQ(0, mkdir(self->dir_path, 0700)) 459904f9070eSMickaël Salaün { 460004f9070eSMickaël Salaün TH_LOG("Failed to create directory \"%s\": %s", 460104f9070eSMickaël Salaün self->dir_path, strerror(errno)); 460204f9070eSMickaël Salaün free(self->dir_path); 460304f9070eSMickaël Salaün self->dir_path = NULL; 460404f9070eSMickaël Salaün } 460504f9070eSMickaël Salaün self->has_created_dir = true; 460604f9070eSMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 460704f9070eSMickaël Salaün } 460804f9070eSMickaël Salaün 460904f9070eSMickaël Salaün /* Creates file when required. */ 461004f9070eSMickaël Salaün if (stat(variant->file_path, &statbuf)) { 461104f9070eSMickaël Salaün int fd; 461204f9070eSMickaël Salaün 461304f9070eSMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 461404f9070eSMickaël Salaün fd = creat(variant->file_path, 0600); 461504f9070eSMickaël Salaün EXPECT_LE(0, fd) 461604f9070eSMickaël Salaün { 461704f9070eSMickaël Salaün TH_LOG("Failed to create file \"%s\": %s", 461804f9070eSMickaël Salaün variant->file_path, strerror(errno)); 461904f9070eSMickaël Salaün } 462004f9070eSMickaël Salaün EXPECT_EQ(0, close(fd)); 462104f9070eSMickaël Salaün self->has_created_file = true; 462204f9070eSMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 462304f9070eSMickaël Salaün } 462404f9070eSMickaël Salaün } 462504f9070eSMickaël Salaün 462604f9070eSMickaël Salaün FIXTURE_TEARDOWN(layout3_fs) 462704f9070eSMickaël Salaün { 462804f9070eSMickaël Salaün if (self->skip_test) 462904f9070eSMickaël Salaün SKIP(return, "this filesystem is not supported (teardown)"); 463004f9070eSMickaël Salaün 463104f9070eSMickaël Salaün if (self->has_created_file) { 463204f9070eSMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 463304f9070eSMickaël Salaün /* 463404f9070eSMickaël Salaün * Don't check for error because the file might already 463504f9070eSMickaël Salaün * have been removed (cf. release_inode test). 463604f9070eSMickaël Salaün */ 463704f9070eSMickaël Salaün unlink(variant->file_path); 463804f9070eSMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 463904f9070eSMickaël Salaün } 464004f9070eSMickaël Salaün 464104f9070eSMickaël Salaün if (self->has_created_dir) { 464204f9070eSMickaël Salaün set_cap(_metadata, CAP_DAC_OVERRIDE); 464304f9070eSMickaël Salaün /* 464404f9070eSMickaël Salaün * Don't check for error because the directory might already 464504f9070eSMickaël Salaün * have been removed (cf. release_inode test). 464604f9070eSMickaël Salaün */ 464704f9070eSMickaël Salaün rmdir(self->dir_path); 464804f9070eSMickaël Salaün clear_cap(_metadata, CAP_DAC_OVERRIDE); 464904f9070eSMickaël Salaün } 465004f9070eSMickaël Salaün free(self->dir_path); 465104f9070eSMickaël Salaün self->dir_path = NULL; 465204f9070eSMickaël Salaün 465304f9070eSMickaël Salaün cleanup_layout(_metadata); 465404f9070eSMickaël Salaün } 465504f9070eSMickaël Salaün 465604f9070eSMickaël Salaün static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, 465704f9070eSMickaël Salaün FIXTURE_DATA(layout3_fs) * self, 465804f9070eSMickaël Salaün const FIXTURE_VARIANT(layout3_fs) * variant, 465904f9070eSMickaël Salaün const char *const rule_path) 466004f9070eSMickaël Salaün { 466104f9070eSMickaël Salaün const struct rule layer1_allow_read_file[] = { 466204f9070eSMickaël Salaün { 466304f9070eSMickaël Salaün .path = rule_path, 466404f9070eSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_FILE, 466504f9070eSMickaël Salaün }, 466604f9070eSMickaël Salaün {}, 466704f9070eSMickaël Salaün }; 466804f9070eSMickaël Salaün const struct landlock_ruleset_attr layer2_deny_everything_attr = { 466904f9070eSMickaël Salaün .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 467004f9070eSMickaël Salaün }; 467104f9070eSMickaël Salaün const char *const dev_null_path = "/dev/null"; 467204f9070eSMickaël Salaün int ruleset_fd; 467304f9070eSMickaël Salaün 467404f9070eSMickaël Salaün if (self->skip_test) 467504f9070eSMickaël Salaün SKIP(return, "this filesystem is not supported (test)"); 467604f9070eSMickaël Salaün 467704f9070eSMickaël Salaün /* Checks without Landlock. */ 467804f9070eSMickaël Salaün EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 467904f9070eSMickaël Salaün EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 468004f9070eSMickaël Salaün 468104f9070eSMickaël Salaün ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, 468204f9070eSMickaël Salaün layer1_allow_read_file); 468304f9070eSMickaël Salaün EXPECT_LE(0, ruleset_fd); 468404f9070eSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 468504f9070eSMickaël Salaün EXPECT_EQ(0, close(ruleset_fd)); 468604f9070eSMickaël Salaün 468704f9070eSMickaël Salaün EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 468804f9070eSMickaël Salaün EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 468904f9070eSMickaël Salaün 469004f9070eSMickaël Salaün /* Forbids directory reading. */ 469104f9070eSMickaël Salaün ruleset_fd = 469204f9070eSMickaël Salaün landlock_create_ruleset(&layer2_deny_everything_attr, 469304f9070eSMickaël Salaün sizeof(layer2_deny_everything_attr), 0); 469404f9070eSMickaël Salaün EXPECT_LE(0, ruleset_fd); 469504f9070eSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 469604f9070eSMickaël Salaün EXPECT_EQ(0, close(ruleset_fd)); 469704f9070eSMickaël Salaün 469804f9070eSMickaël Salaün /* Checks with Landlock and forbidden access. */ 469904f9070eSMickaël Salaün EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); 470004f9070eSMickaël Salaün EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); 470104f9070eSMickaël Salaün } 470204f9070eSMickaël Salaün 470304f9070eSMickaël Salaün /* Matrix of tests to check file hierarchy evaluation. */ 470404f9070eSMickaël Salaün 470504f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_dir_parent) 470604f9070eSMickaël Salaün { 470704f9070eSMickaël Salaün /* The current directory must not be the root for this test. */ 470804f9070eSMickaël Salaün layer3_fs_tag_inode(_metadata, self, variant, "."); 470904f9070eSMickaël Salaün } 471004f9070eSMickaël Salaün 471104f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) 471204f9070eSMickaël Salaün { 471304f9070eSMickaël Salaün layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); 471404f9070eSMickaël Salaün } 471504f9070eSMickaël Salaün 471604f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_dir_child) 471704f9070eSMickaël Salaün { 471804f9070eSMickaël Salaün layer3_fs_tag_inode(_metadata, self, variant, self->dir_path); 471904f9070eSMickaël Salaün } 472004f9070eSMickaël Salaün 472104f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, tag_inode_file) 472204f9070eSMickaël Salaün { 472304f9070eSMickaël Salaün layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); 472404f9070eSMickaël Salaün } 472504f9070eSMickaël Salaün 472604f9070eSMickaël Salaün /* Light version of layout1.release_inodes */ 472704f9070eSMickaël Salaün TEST_F_FORK(layout3_fs, release_inodes) 472804f9070eSMickaël Salaün { 472904f9070eSMickaël Salaün const struct rule layer1[] = { 473004f9070eSMickaël Salaün { 473104f9070eSMickaël Salaün .path = TMP_DIR, 473204f9070eSMickaël Salaün .access = LANDLOCK_ACCESS_FS_READ_DIR, 473304f9070eSMickaël Salaün }, 473404f9070eSMickaël Salaün {}, 473504f9070eSMickaël Salaün }; 473604f9070eSMickaël Salaün int ruleset_fd; 473704f9070eSMickaël Salaün 473804f9070eSMickaël Salaün if (self->skip_test) 473904f9070eSMickaël Salaün SKIP(return, "this filesystem is not supported (test)"); 474004f9070eSMickaël Salaün 474104f9070eSMickaël Salaün /* Clean up for the teardown to not fail. */ 474204f9070eSMickaël Salaün if (self->has_created_file) 474304f9070eSMickaël Salaün EXPECT_EQ(0, remove_path(variant->file_path)); 474404f9070eSMickaël Salaün 474504f9070eSMickaël Salaün if (self->has_created_dir) 474604f9070eSMickaël Salaün /* Don't check for error because of cgroup specificities. */ 474704f9070eSMickaël Salaün remove_path(self->dir_path); 474804f9070eSMickaël Salaün 474904f9070eSMickaël Salaün ruleset_fd = 475004f9070eSMickaël Salaün create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); 475104f9070eSMickaël Salaün ASSERT_LE(0, ruleset_fd); 475204f9070eSMickaël Salaün 475304f9070eSMickaël Salaün /* Unmount the filesystem while it is being used by a ruleset. */ 475404f9070eSMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 475504f9070eSMickaël Salaün ASSERT_EQ(0, umount(TMP_DIR)); 475604f9070eSMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 475704f9070eSMickaël Salaün 475804f9070eSMickaël Salaün /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ 475904f9070eSMickaël Salaün set_cap(_metadata, CAP_SYS_ADMIN); 476004f9070eSMickaël Salaün ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); 476104f9070eSMickaël Salaün clear_cap(_metadata, CAP_SYS_ADMIN); 476204f9070eSMickaël Salaün 476304f9070eSMickaël Salaün enforce_ruleset(_metadata, ruleset_fd); 476404f9070eSMickaël Salaün ASSERT_EQ(0, close(ruleset_fd)); 476504f9070eSMickaël Salaün 476604f9070eSMickaël Salaün /* Checks that access to the new mount point is denied. */ 476704f9070eSMickaël Salaün ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); 476804f9070eSMickaël Salaün } 476904f9070eSMickaël Salaün 4770e1199815SMickaël Salaün TEST_HARNESS_MAIN 4771