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