1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Landlock tests - Filesystem
4 *
5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6 * Copyright © 2020 ANSSI
7 * Copyright © 2020-2022 Microsoft Corporation
8 */
9
10 #define _GNU_SOURCE
11 #include <asm/termbits.h>
12 #include <fcntl.h>
13 #include <libgen.h>
14 #include <linux/fiemap.h>
15 #include <linux/landlock.h>
16 #include <linux/magic.h>
17 #include <sched.h>
18 #include <stddef.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/capability.h>
22 #include <sys/ioctl.h>
23 #include <sys/mount.h>
24 #include <sys/prctl.h>
25 #include <sys/sendfile.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/sysmacros.h>
29 #include <sys/un.h>
30 #include <sys/vfs.h>
31 #include <unistd.h>
32
33 /*
34 * Intentionally included last to work around header conflict.
35 * See https://sourceware.org/glibc/wiki/Synchronizing_Headers.
36 */
37 #include <linux/fs.h>
38 #include <linux/mount.h>
39
40 #include "common.h"
41
42 #ifndef renameat2
renameat2(int olddirfd,const char * oldpath,int newdirfd,const char * newpath,unsigned int flags)43 int renameat2(int olddirfd, const char *oldpath, int newdirfd,
44 const char *newpath, unsigned int flags)
45 {
46 return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath,
47 flags);
48 }
49 #endif
50
51 #ifndef open_tree
open_tree(int dfd,const char * filename,unsigned int flags)52 int open_tree(int dfd, const char *filename, unsigned int flags)
53 {
54 return syscall(__NR_open_tree, dfd, filename, flags);
55 }
56 #endif
57
58 #ifndef RENAME_EXCHANGE
59 #define RENAME_EXCHANGE (1 << 1)
60 #endif
61
62 #define TMP_DIR "tmp"
63 #define BINARY_PATH "./true"
64
65 /* Paths (sibling number and depth) */
66 static const char dir_s1d1[] = TMP_DIR "/s1d1";
67 static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";
68 static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";
69 static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";
70 static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";
71 static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";
72 static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";
73 static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";
74 static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";
75
76 static const char dir_s2d1[] = TMP_DIR "/s2d1";
77 static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";
78 static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";
79 static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";
80 static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";
81 static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";
82 static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";
83
84 static const char dir_s3d1[] = TMP_DIR "/s3d1";
85 static const char file1_s3d1[] = TMP_DIR "/s3d1/f1";
86 /* dir_s3d2 is a mount point. */
87 static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";
88 static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";
89
90 /*
91 * layout1 hierarchy:
92 *
93 * tmp
94 * ├── s1d1
95 * │ ├── f1
96 * │ ├── f2
97 * │ └── s1d2
98 * │ ├── f1
99 * │ ├── f2
100 * │ └── s1d3
101 * │ ├── f1
102 * │ └── f2
103 * ├── s2d1
104 * │ ├── f1
105 * │ └── s2d2
106 * │ ├── f1
107 * │ └── s2d3
108 * │ ├── f1
109 * │ └── f2
110 * └── s3d1
111 * ├── f1
112 * └── s3d2
113 * └── s3d3
114 */
115
fgrep(FILE * const inf,const char * const str)116 static bool fgrep(FILE *const inf, const char *const str)
117 {
118 char line[32];
119 const int slen = strlen(str);
120
121 while (!feof(inf)) {
122 if (!fgets(line, sizeof(line), inf))
123 break;
124 if (strncmp(line, str, slen))
125 continue;
126
127 return true;
128 }
129
130 return false;
131 }
132
supports_filesystem(const char * const filesystem)133 static bool supports_filesystem(const char *const filesystem)
134 {
135 char str[32];
136 int len;
137 bool res = true;
138 FILE *const inf = fopen("/proc/filesystems", "r");
139
140 /*
141 * Consider that the filesystem is supported if we cannot get the
142 * supported ones.
143 */
144 if (!inf)
145 return true;
146
147 /* filesystem can be null for bind mounts. */
148 if (!filesystem)
149 goto out;
150
151 len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem);
152 if (len >= sizeof(str))
153 /* Ignores too-long filesystem names. */
154 goto out;
155
156 res = fgrep(inf, str);
157
158 out:
159 fclose(inf);
160 return res;
161 }
162
cwd_matches_fs(unsigned int fs_magic)163 static bool cwd_matches_fs(unsigned int fs_magic)
164 {
165 struct statfs statfs_buf;
166
167 if (!fs_magic)
168 return true;
169
170 if (statfs(".", &statfs_buf))
171 return true;
172
173 return statfs_buf.f_type == fs_magic;
174 }
175
mkdir_parents(struct __test_metadata * const _metadata,const char * const path)176 static void mkdir_parents(struct __test_metadata *const _metadata,
177 const char *const path)
178 {
179 char *walker;
180 const char *parent;
181 int i, err;
182
183 ASSERT_NE(path[0], '\0');
184 walker = strdup(path);
185 ASSERT_NE(NULL, walker);
186 parent = walker;
187 for (i = 1; walker[i]; i++) {
188 if (walker[i] != '/')
189 continue;
190 walker[i] = '\0';
191 err = mkdir(parent, 0700);
192 ASSERT_FALSE(err && errno != EEXIST)
193 {
194 TH_LOG("Failed to create directory \"%s\": %s", parent,
195 strerror(errno));
196 }
197 walker[i] = '/';
198 }
199 free(walker);
200 }
201
create_directory(struct __test_metadata * const _metadata,const char * const path)202 static void create_directory(struct __test_metadata *const _metadata,
203 const char *const path)
204 {
205 mkdir_parents(_metadata, path);
206 ASSERT_EQ(0, mkdir(path, 0700))
207 {
208 TH_LOG("Failed to create directory \"%s\": %s", path,
209 strerror(errno));
210 }
211 }
212
create_file(struct __test_metadata * const _metadata,const char * const path)213 static void create_file(struct __test_metadata *const _metadata,
214 const char *const path)
215 {
216 mkdir_parents(_metadata, path);
217 ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0))
218 {
219 TH_LOG("Failed to create file \"%s\": %s", path,
220 strerror(errno));
221 }
222 }
223
remove_path(const char * const path)224 static int remove_path(const char *const path)
225 {
226 char *walker;
227 int i, ret, err = 0;
228
229 walker = strdup(path);
230 if (!walker) {
231 err = ENOMEM;
232 goto out;
233 }
234 if (unlink(path) && rmdir(path)) {
235 if (errno != ENOENT && errno != ENOTDIR)
236 err = errno;
237 goto out;
238 }
239 for (i = strlen(walker); i > 0; i--) {
240 if (walker[i] != '/')
241 continue;
242 walker[i] = '\0';
243 ret = rmdir(walker);
244 if (ret) {
245 if (errno != ENOTEMPTY && errno != EBUSY)
246 err = errno;
247 goto out;
248 }
249 if (strcmp(walker, TMP_DIR) == 0)
250 goto out;
251 }
252
253 out:
254 free(walker);
255 return err;
256 }
257
258 struct mnt_opt {
259 const char *const source;
260 const char *const type;
261 const unsigned long flags;
262 const char *const data;
263 };
264
265 #define MNT_TMP_DATA "size=4m,mode=700"
266
267 static const struct mnt_opt mnt_tmp = {
268 .type = "tmpfs",
269 .data = MNT_TMP_DATA,
270 };
271
mount_opt(const struct mnt_opt * const mnt,const char * const target)272 static int mount_opt(const struct mnt_opt *const mnt, const char *const target)
273 {
274 return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags,
275 mnt->data);
276 }
277
prepare_layout_opt(struct __test_metadata * const _metadata,const struct mnt_opt * const mnt)278 static void prepare_layout_opt(struct __test_metadata *const _metadata,
279 const struct mnt_opt *const mnt)
280 {
281 disable_caps(_metadata);
282 umask(0077);
283 create_directory(_metadata, TMP_DIR);
284
285 /*
286 * Do not pollute the rest of the system: creates a private mount point
287 * for tests relying on pivot_root(2) and move_mount(2).
288 */
289 set_cap(_metadata, CAP_SYS_ADMIN);
290 ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP));
291 ASSERT_EQ(0, mount_opt(mnt, TMP_DIR))
292 {
293 TH_LOG("Failed to mount the %s filesystem: %s", mnt->type,
294 strerror(errno));
295 /*
296 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP()
297 * failed, so we need to explicitly do a minimal cleanup to
298 * avoid cascading errors with other tests that don't depend on
299 * the same filesystem.
300 */
301 remove_path(TMP_DIR);
302 }
303 ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
304 clear_cap(_metadata, CAP_SYS_ADMIN);
305 }
306
prepare_layout(struct __test_metadata * const _metadata)307 static void prepare_layout(struct __test_metadata *const _metadata)
308 {
309 prepare_layout_opt(_metadata, &mnt_tmp);
310 }
311
cleanup_layout(struct __test_metadata * const _metadata)312 static void cleanup_layout(struct __test_metadata *const _metadata)
313 {
314 set_cap(_metadata, CAP_SYS_ADMIN);
315 if (umount(TMP_DIR)) {
316 /*
317 * According to the test environment, the mount point of the
318 * current directory may be shared or not, which changes the
319 * visibility of the nested TMP_DIR mount point for the test's
320 * parent process doing this cleanup.
321 */
322 ASSERT_EQ(EINVAL, errno);
323 }
324 clear_cap(_metadata, CAP_SYS_ADMIN);
325 EXPECT_EQ(0, remove_path(TMP_DIR));
326 }
327
328 /* clang-format off */
FIXTURE(layout0)329 FIXTURE(layout0) {};
330 /* clang-format on */
331
FIXTURE_SETUP(layout0)332 FIXTURE_SETUP(layout0)
333 {
334 prepare_layout(_metadata);
335 }
336
FIXTURE_TEARDOWN_PARENT(layout0)337 FIXTURE_TEARDOWN_PARENT(layout0)
338 {
339 cleanup_layout(_metadata);
340 }
341
create_layout1(struct __test_metadata * const _metadata)342 static void create_layout1(struct __test_metadata *const _metadata)
343 {
344 create_file(_metadata, file1_s1d1);
345 create_file(_metadata, file1_s1d2);
346 create_file(_metadata, file1_s1d3);
347 create_file(_metadata, file2_s1d1);
348 create_file(_metadata, file2_s1d2);
349 create_file(_metadata, file2_s1d3);
350
351 create_file(_metadata, file1_s2d1);
352 create_file(_metadata, file1_s2d2);
353 create_file(_metadata, file1_s2d3);
354 create_file(_metadata, file2_s2d3);
355
356 create_file(_metadata, file1_s3d1);
357 create_directory(_metadata, dir_s3d2);
358 set_cap(_metadata, CAP_SYS_ADMIN);
359 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));
360 clear_cap(_metadata, CAP_SYS_ADMIN);
361
362 ASSERT_EQ(0, mkdir(dir_s3d3, 0700));
363 }
364
remove_layout1(struct __test_metadata * const _metadata)365 static void remove_layout1(struct __test_metadata *const _metadata)
366 {
367 EXPECT_EQ(0, remove_path(file2_s1d3));
368 EXPECT_EQ(0, remove_path(file2_s1d2));
369 EXPECT_EQ(0, remove_path(file2_s1d1));
370 EXPECT_EQ(0, remove_path(file1_s1d3));
371 EXPECT_EQ(0, remove_path(file1_s1d2));
372 EXPECT_EQ(0, remove_path(file1_s1d1));
373 EXPECT_EQ(0, remove_path(dir_s1d3));
374
375 EXPECT_EQ(0, remove_path(file2_s2d3));
376 EXPECT_EQ(0, remove_path(file1_s2d3));
377 EXPECT_EQ(0, remove_path(file1_s2d2));
378 EXPECT_EQ(0, remove_path(file1_s2d1));
379 EXPECT_EQ(0, remove_path(dir_s2d2));
380
381 EXPECT_EQ(0, remove_path(file1_s3d1));
382 EXPECT_EQ(0, remove_path(dir_s3d3));
383 set_cap(_metadata, CAP_SYS_ADMIN);
384 umount(dir_s3d2);
385 clear_cap(_metadata, CAP_SYS_ADMIN);
386 EXPECT_EQ(0, remove_path(dir_s3d2));
387 }
388
389 /* clang-format off */
FIXTURE(layout1)390 FIXTURE(layout1) {};
391 /* clang-format on */
392
FIXTURE_SETUP(layout1)393 FIXTURE_SETUP(layout1)
394 {
395 prepare_layout(_metadata);
396
397 create_layout1(_metadata);
398 }
399
FIXTURE_TEARDOWN_PARENT(layout1)400 FIXTURE_TEARDOWN_PARENT(layout1)
401 {
402 remove_layout1(_metadata);
403
404 cleanup_layout(_metadata);
405 }
406
407 /*
408 * This helper enables to use the ASSERT_* macros and print the line number
409 * pointing to the test caller.
410 */
test_open_rel(const int dirfd,const char * const path,const int flags)411 static int test_open_rel(const int dirfd, const char *const path,
412 const int flags)
413 {
414 int fd;
415
416 /* Works with file and directories. */
417 fd = openat(dirfd, path, flags | O_CLOEXEC);
418 if (fd < 0)
419 return errno;
420 /*
421 * Mixing error codes from close(2) and open(2) should not lead to any
422 * (access type) confusion for this test.
423 */
424 if (close(fd) != 0)
425 return errno;
426 return 0;
427 }
428
test_open(const char * const path,const int flags)429 static int test_open(const char *const path, const int flags)
430 {
431 return test_open_rel(AT_FDCWD, path, flags);
432 }
433
TEST_F_FORK(layout1,no_restriction)434 TEST_F_FORK(layout1, no_restriction)
435 {
436 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
437 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
438 ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));
439 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
440 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
441 ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));
442 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
443 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
444
445 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
446 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
447 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
448 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
449 ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));
450 ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));
451
452 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
453 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
454 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
455 }
456
TEST_F_FORK(layout1,inval)457 TEST_F_FORK(layout1, inval)
458 {
459 struct landlock_path_beneath_attr path_beneath = {
460 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
461 LANDLOCK_ACCESS_FS_WRITE_FILE,
462 .parent_fd = -1,
463 };
464 struct landlock_ruleset_attr ruleset_attr = {
465 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
466 LANDLOCK_ACCESS_FS_WRITE_FILE,
467 };
468 int ruleset_fd;
469
470 path_beneath.parent_fd =
471 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
472 ASSERT_LE(0, path_beneath.parent_fd);
473
474 ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
475 ASSERT_LE(0, ruleset_fd);
476 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
477 &path_beneath, 0));
478 /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */
479 ASSERT_EQ(EBADF, errno);
480 ASSERT_EQ(0, close(ruleset_fd));
481
482 ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);
483 ASSERT_LE(0, ruleset_fd);
484 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
485 &path_beneath, 0));
486 /* Returns EBADFD because ruleset_fd is not a valid ruleset. */
487 ASSERT_EQ(EBADFD, errno);
488 ASSERT_EQ(0, close(ruleset_fd));
489
490 /* Gets a real ruleset. */
491 ruleset_fd =
492 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
493 ASSERT_LE(0, ruleset_fd);
494 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
495 &path_beneath, 0));
496 ASSERT_EQ(0, close(path_beneath.parent_fd));
497
498 /* Tests without O_PATH. */
499 path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);
500 ASSERT_LE(0, path_beneath.parent_fd);
501 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
502 &path_beneath, 0));
503 ASSERT_EQ(0, close(path_beneath.parent_fd));
504
505 /* Tests with a ruleset FD. */
506 path_beneath.parent_fd = ruleset_fd;
507 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
508 &path_beneath, 0));
509 ASSERT_EQ(EBADFD, errno);
510
511 /* Checks unhandled allowed_access. */
512 path_beneath.parent_fd =
513 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
514 ASSERT_LE(0, path_beneath.parent_fd);
515
516 /* Test with legitimate values. */
517 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;
518 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
519 &path_beneath, 0));
520 ASSERT_EQ(EINVAL, errno);
521 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;
522
523 /* Tests with denied-by-default access right. */
524 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER;
525 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
526 &path_beneath, 0));
527 ASSERT_EQ(EINVAL, errno);
528 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;
529
530 /* Test with unknown (64-bits) value. */
531 path_beneath.allowed_access |= (1ULL << 60);
532 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
533 &path_beneath, 0));
534 ASSERT_EQ(EINVAL, errno);
535 path_beneath.allowed_access &= ~(1ULL << 60);
536
537 /* Test with no access. */
538 path_beneath.allowed_access = 0;
539 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
540 &path_beneath, 0));
541 ASSERT_EQ(ENOMSG, errno);
542 path_beneath.allowed_access &= ~(1ULL << 60);
543
544 ASSERT_EQ(0, close(path_beneath.parent_fd));
545
546 /* Enforces the ruleset. */
547 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
548 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
549
550 ASSERT_EQ(0, close(ruleset_fd));
551 }
552
553 /* clang-format off */
554
555 #define ACCESS_FILE ( \
556 LANDLOCK_ACCESS_FS_EXECUTE | \
557 LANDLOCK_ACCESS_FS_WRITE_FILE | \
558 LANDLOCK_ACCESS_FS_READ_FILE | \
559 LANDLOCK_ACCESS_FS_TRUNCATE | \
560 LANDLOCK_ACCESS_FS_IOCTL_DEV)
561
562 #define ACCESS_LAST LANDLOCK_ACCESS_FS_IOCTL_DEV
563
564 #define ACCESS_ALL ( \
565 ACCESS_FILE | \
566 LANDLOCK_ACCESS_FS_READ_DIR | \
567 LANDLOCK_ACCESS_FS_REMOVE_DIR | \
568 LANDLOCK_ACCESS_FS_REMOVE_FILE | \
569 LANDLOCK_ACCESS_FS_MAKE_CHAR | \
570 LANDLOCK_ACCESS_FS_MAKE_DIR | \
571 LANDLOCK_ACCESS_FS_MAKE_REG | \
572 LANDLOCK_ACCESS_FS_MAKE_SOCK | \
573 LANDLOCK_ACCESS_FS_MAKE_FIFO | \
574 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
575 LANDLOCK_ACCESS_FS_MAKE_SYM | \
576 LANDLOCK_ACCESS_FS_REFER)
577
578 /* clang-format on */
579
TEST_F_FORK(layout1,file_and_dir_access_rights)580 TEST_F_FORK(layout1, file_and_dir_access_rights)
581 {
582 __u64 access;
583 int err;
584 struct landlock_path_beneath_attr path_beneath_file = {},
585 path_beneath_dir = {};
586 struct landlock_ruleset_attr ruleset_attr = {
587 .handled_access_fs = ACCESS_ALL,
588 };
589 const int ruleset_fd =
590 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
591
592 ASSERT_LE(0, ruleset_fd);
593
594 /* Tests access rights for files. */
595 path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
596 ASSERT_LE(0, path_beneath_file.parent_fd);
597
598 /* Tests access rights for directories. */
599 path_beneath_dir.parent_fd =
600 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
601 ASSERT_LE(0, path_beneath_dir.parent_fd);
602
603 for (access = 1; access <= ACCESS_LAST; access <<= 1) {
604 path_beneath_dir.allowed_access = access;
605 ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
606 LANDLOCK_RULE_PATH_BENEATH,
607 &path_beneath_dir, 0));
608
609 path_beneath_file.allowed_access = access;
610 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
611 &path_beneath_file, 0);
612 if (access & ACCESS_FILE) {
613 ASSERT_EQ(0, err);
614 } else {
615 ASSERT_EQ(-1, err);
616 ASSERT_EQ(EINVAL, errno);
617 }
618 }
619 ASSERT_EQ(0, close(path_beneath_file.parent_fd));
620 ASSERT_EQ(0, close(path_beneath_dir.parent_fd));
621 ASSERT_EQ(0, close(ruleset_fd));
622 }
623
TEST_F_FORK(layout0,ruleset_with_unknown_access)624 TEST_F_FORK(layout0, ruleset_with_unknown_access)
625 {
626 __u64 access_mask;
627
628 for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;
629 access_mask >>= 1) {
630 struct landlock_ruleset_attr ruleset_attr = {
631 .handled_access_fs = access_mask,
632 };
633
634 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
635 sizeof(ruleset_attr), 0));
636 ASSERT_EQ(EINVAL, errno);
637 }
638 }
639
TEST_F_FORK(layout0,rule_with_unknown_access)640 TEST_F_FORK(layout0, rule_with_unknown_access)
641 {
642 __u64 access;
643 struct landlock_path_beneath_attr path_beneath = {};
644 const struct landlock_ruleset_attr ruleset_attr = {
645 .handled_access_fs = ACCESS_ALL,
646 };
647 const int ruleset_fd =
648 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
649
650 ASSERT_LE(0, ruleset_fd);
651
652 path_beneath.parent_fd =
653 open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC);
654 ASSERT_LE(0, path_beneath.parent_fd);
655
656 for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) {
657 path_beneath.allowed_access = access;
658 EXPECT_EQ(-1, landlock_add_rule(ruleset_fd,
659 LANDLOCK_RULE_PATH_BENEATH,
660 &path_beneath, 0));
661 EXPECT_EQ(EINVAL, errno);
662 }
663 ASSERT_EQ(0, close(path_beneath.parent_fd));
664 ASSERT_EQ(0, close(ruleset_fd));
665 }
666
TEST_F_FORK(layout1,rule_with_unhandled_access)667 TEST_F_FORK(layout1, rule_with_unhandled_access)
668 {
669 struct landlock_ruleset_attr ruleset_attr = {
670 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
671 };
672 struct landlock_path_beneath_attr path_beneath = {};
673 int ruleset_fd;
674 __u64 access;
675
676 ruleset_fd =
677 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
678 ASSERT_LE(0, ruleset_fd);
679
680 path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
681 ASSERT_LE(0, path_beneath.parent_fd);
682
683 for (access = 1; access > 0; access <<= 1) {
684 int err;
685
686 path_beneath.allowed_access = access;
687 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
688 &path_beneath, 0);
689 if (access == ruleset_attr.handled_access_fs) {
690 EXPECT_EQ(0, err);
691 } else {
692 EXPECT_EQ(-1, err);
693 EXPECT_EQ(EINVAL, errno);
694 }
695 }
696
697 EXPECT_EQ(0, close(path_beneath.parent_fd));
698 EXPECT_EQ(0, close(ruleset_fd));
699 }
700
add_path_beneath(struct __test_metadata * const _metadata,const int ruleset_fd,const __u64 allowed_access,const char * const path)701 static void add_path_beneath(struct __test_metadata *const _metadata,
702 const int ruleset_fd, const __u64 allowed_access,
703 const char *const path)
704 {
705 struct landlock_path_beneath_attr path_beneath = {
706 .allowed_access = allowed_access,
707 };
708
709 path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);
710 ASSERT_LE(0, path_beneath.parent_fd)
711 {
712 TH_LOG("Failed to open directory \"%s\": %s", path,
713 strerror(errno));
714 }
715 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
716 &path_beneath, 0))
717 {
718 TH_LOG("Failed to update the ruleset with \"%s\": %s", path,
719 strerror(errno));
720 }
721 ASSERT_EQ(0, close(path_beneath.parent_fd));
722 }
723
724 struct rule {
725 const char *path;
726 __u64 access;
727 };
728
729 /* clang-format off */
730
731 #define ACCESS_RO ( \
732 LANDLOCK_ACCESS_FS_READ_FILE | \
733 LANDLOCK_ACCESS_FS_READ_DIR)
734
735 #define ACCESS_RW ( \
736 ACCESS_RO | \
737 LANDLOCK_ACCESS_FS_WRITE_FILE)
738
739 /* clang-format on */
740
create_ruleset(struct __test_metadata * const _metadata,const __u64 handled_access_fs,const struct rule rules[])741 static int create_ruleset(struct __test_metadata *const _metadata,
742 const __u64 handled_access_fs,
743 const struct rule rules[])
744 {
745 int ruleset_fd, i;
746 struct landlock_ruleset_attr ruleset_attr = {
747 .handled_access_fs = handled_access_fs,
748 };
749
750 ASSERT_NE(NULL, rules)
751 {
752 TH_LOG("No rule list");
753 }
754 ASSERT_NE(NULL, rules[0].path)
755 {
756 TH_LOG("Empty rule list");
757 }
758
759 ruleset_fd =
760 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
761 ASSERT_LE(0, ruleset_fd)
762 {
763 TH_LOG("Failed to create a ruleset: %s", strerror(errno));
764 }
765
766 for (i = 0; rules[i].path; i++) {
767 if (!rules[i].access)
768 continue;
769
770 add_path_beneath(_metadata, ruleset_fd, rules[i].access,
771 rules[i].path);
772 }
773 return ruleset_fd;
774 }
775
TEST_F_FORK(layout0,proc_nsfs)776 TEST_F_FORK(layout0, proc_nsfs)
777 {
778 const struct rule rules[] = {
779 {
780 .path = "/dev/null",
781 .access = LANDLOCK_ACCESS_FS_READ_FILE |
782 LANDLOCK_ACCESS_FS_WRITE_FILE,
783 },
784 {},
785 };
786 struct landlock_path_beneath_attr path_beneath;
787 const int ruleset_fd = create_ruleset(
788 _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR,
789 rules);
790
791 ASSERT_LE(0, ruleset_fd);
792 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
793
794 enforce_ruleset(_metadata, ruleset_fd);
795
796 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
797 ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));
798 ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));
799 ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));
800
801 ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));
802 ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));
803 ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));
804 /*
805 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a
806 * disconnected path. Such path cannot be identified and must then be
807 * allowed.
808 */
809 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
810
811 /*
812 * Checks that it is not possible to add nsfs-like filesystem
813 * references to a ruleset.
814 */
815 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
816 LANDLOCK_ACCESS_FS_WRITE_FILE,
817 path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);
818 ASSERT_LE(0, path_beneath.parent_fd);
819 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
820 &path_beneath, 0));
821 ASSERT_EQ(EBADFD, errno);
822 ASSERT_EQ(0, close(path_beneath.parent_fd));
823 }
824
TEST_F_FORK(layout0,unpriv)825 TEST_F_FORK(layout0, unpriv)
826 {
827 const struct rule rules[] = {
828 {
829 .path = TMP_DIR,
830 .access = ACCESS_RO,
831 },
832 {},
833 };
834 int ruleset_fd;
835
836 drop_caps(_metadata);
837
838 ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
839 ASSERT_LE(0, ruleset_fd);
840 ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
841 ASSERT_EQ(EPERM, errno);
842
843 /* enforce_ruleset() calls prctl(no_new_privs). */
844 enforce_ruleset(_metadata, ruleset_fd);
845 ASSERT_EQ(0, close(ruleset_fd));
846 }
847
TEST_F_FORK(layout1,effective_access)848 TEST_F_FORK(layout1, effective_access)
849 {
850 const struct rule rules[] = {
851 {
852 .path = dir_s1d2,
853 .access = ACCESS_RO,
854 },
855 {
856 .path = file1_s2d2,
857 .access = LANDLOCK_ACCESS_FS_READ_FILE |
858 LANDLOCK_ACCESS_FS_WRITE_FILE,
859 },
860 {},
861 };
862 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
863 char buf;
864 int reg_fd;
865
866 ASSERT_LE(0, ruleset_fd);
867 enforce_ruleset(_metadata, ruleset_fd);
868 ASSERT_EQ(0, close(ruleset_fd));
869
870 /* Tests on a directory (with or without O_PATH). */
871 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
872 ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH));
873 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
874 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH));
875 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
876 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH));
877
878 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
879 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
880 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
881 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
882
883 /* Tests on a file (with or without O_PATH). */
884 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));
885 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH));
886
887 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
888
889 /* Checks effective read and write actions. */
890 reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);
891 ASSERT_LE(0, reg_fd);
892 ASSERT_EQ(1, write(reg_fd, ".", 1));
893 ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));
894 ASSERT_EQ(1, read(reg_fd, &buf, 1));
895 ASSERT_EQ('.', buf);
896 ASSERT_EQ(0, close(reg_fd));
897
898 /* Just in case, double-checks effective actions. */
899 reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);
900 ASSERT_LE(0, reg_fd);
901 ASSERT_EQ(-1, write(reg_fd, &buf, 1));
902 ASSERT_EQ(EBADF, errno);
903 ASSERT_EQ(0, close(reg_fd));
904 }
905
TEST_F_FORK(layout1,unhandled_access)906 TEST_F_FORK(layout1, unhandled_access)
907 {
908 const struct rule rules[] = {
909 {
910 .path = dir_s1d2,
911 .access = ACCESS_RO,
912 },
913 {},
914 };
915 /* Here, we only handle read accesses, not write accesses. */
916 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
917
918 ASSERT_LE(0, ruleset_fd);
919 enforce_ruleset(_metadata, ruleset_fd);
920 ASSERT_EQ(0, close(ruleset_fd));
921
922 /*
923 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,
924 * opening for write-only should be allowed, but not read-write.
925 */
926 ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));
927 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
928
929 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
930 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
931 }
932
TEST_F_FORK(layout1,ruleset_overlap)933 TEST_F_FORK(layout1, ruleset_overlap)
934 {
935 const struct rule rules[] = {
936 /* These rules should be ORed among them. */
937 {
938 .path = dir_s1d2,
939 .access = LANDLOCK_ACCESS_FS_READ_FILE |
940 LANDLOCK_ACCESS_FS_WRITE_FILE,
941 },
942 {
943 .path = dir_s1d2,
944 .access = LANDLOCK_ACCESS_FS_READ_FILE |
945 LANDLOCK_ACCESS_FS_READ_DIR,
946 },
947 {},
948 };
949 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
950
951 ASSERT_LE(0, ruleset_fd);
952 enforce_ruleset(_metadata, ruleset_fd);
953 ASSERT_EQ(0, close(ruleset_fd));
954
955 /* Checks s1d1 hierarchy. */
956 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
957 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
958 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
959 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
960
961 /* Checks s1d2 hierarchy. */
962 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
963 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
964 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
965 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
966
967 /* Checks s1d3 hierarchy. */
968 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
969 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
970 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
971 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
972 }
973
TEST_F_FORK(layout1,layer_rule_unions)974 TEST_F_FORK(layout1, layer_rule_unions)
975 {
976 const struct rule layer1[] = {
977 {
978 .path = dir_s1d2,
979 .access = LANDLOCK_ACCESS_FS_READ_FILE,
980 },
981 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
982 {
983 .path = dir_s1d3,
984 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
985 },
986 {},
987 };
988 const struct rule layer2[] = {
989 /* Doesn't change anything from layer1. */
990 {
991 .path = dir_s1d2,
992 .access = LANDLOCK_ACCESS_FS_READ_FILE |
993 LANDLOCK_ACCESS_FS_WRITE_FILE,
994 },
995 {},
996 };
997 const struct rule layer3[] = {
998 /* Only allows write (but not read) to dir_s1d3. */
999 {
1000 .path = dir_s1d2,
1001 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1002 },
1003 {},
1004 };
1005 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1);
1006
1007 ASSERT_LE(0, ruleset_fd);
1008 enforce_ruleset(_metadata, ruleset_fd);
1009 ASSERT_EQ(0, close(ruleset_fd));
1010
1011 /* Checks s1d1 hierarchy with layer1. */
1012 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1013 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1014 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
1015 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1016
1017 /* Checks s1d2 hierarchy with layer1. */
1018 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1019 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1020 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
1021 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1022
1023 /* Checks s1d3 hierarchy with layer1. */
1024 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1025 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
1026 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
1027 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1028 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1029
1030 /* Doesn't change anything from layer1. */
1031 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2);
1032 ASSERT_LE(0, ruleset_fd);
1033 enforce_ruleset(_metadata, ruleset_fd);
1034 ASSERT_EQ(0, close(ruleset_fd));
1035
1036 /* Checks s1d1 hierarchy with layer2. */
1037 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1038 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1039 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
1040 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1041
1042 /* Checks s1d2 hierarchy with layer2. */
1043 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1044 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1045 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
1046 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1047
1048 /* Checks s1d3 hierarchy with layer2. */
1049 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1050 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
1051 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
1052 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1053 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1054
1055 /* Only allows write (but not read) to dir_s1d3. */
1056 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3);
1057 ASSERT_LE(0, ruleset_fd);
1058 enforce_ruleset(_metadata, ruleset_fd);
1059 ASSERT_EQ(0, close(ruleset_fd));
1060
1061 /* Checks s1d1 hierarchy with layer3. */
1062 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1063 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1064 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
1065 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1066
1067 /* Checks s1d2 hierarchy with layer3. */
1068 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
1069 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1070 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
1071 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1072
1073 /* Checks s1d3 hierarchy with layer3. */
1074 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
1075 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
1076 /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */
1077 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR));
1078 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1079 }
1080
TEST_F_FORK(layout1,non_overlapping_accesses)1081 TEST_F_FORK(layout1, non_overlapping_accesses)
1082 {
1083 const struct rule layer1[] = {
1084 {
1085 .path = dir_s1d2,
1086 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
1087 },
1088 {},
1089 };
1090 const struct rule layer2[] = {
1091 {
1092 .path = dir_s1d3,
1093 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1094 },
1095 {},
1096 };
1097 int ruleset_fd;
1098
1099 ASSERT_EQ(0, unlink(file1_s1d1));
1100 ASSERT_EQ(0, unlink(file1_s1d2));
1101
1102 ruleset_fd =
1103 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1);
1104 ASSERT_LE(0, ruleset_fd);
1105 enforce_ruleset(_metadata, ruleset_fd);
1106 ASSERT_EQ(0, close(ruleset_fd));
1107
1108 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
1109 ASSERT_EQ(EACCES, errno);
1110 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
1111 ASSERT_EQ(0, unlink(file1_s1d2));
1112
1113 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE,
1114 layer2);
1115 ASSERT_LE(0, ruleset_fd);
1116 enforce_ruleset(_metadata, ruleset_fd);
1117 ASSERT_EQ(0, close(ruleset_fd));
1118
1119 /* Unchanged accesses for file creation. */
1120 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
1121 ASSERT_EQ(EACCES, errno);
1122 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
1123
1124 /* Checks file removing. */
1125 ASSERT_EQ(-1, unlink(file1_s1d2));
1126 ASSERT_EQ(EACCES, errno);
1127 ASSERT_EQ(0, unlink(file1_s1d3));
1128 }
1129
TEST_F_FORK(layout1,interleaved_masked_accesses)1130 TEST_F_FORK(layout1, interleaved_masked_accesses)
1131 {
1132 /*
1133 * Checks overly restrictive rules:
1134 * layer 1: allows R s1d1/s1d2/s1d3/file1
1135 * layer 2: allows RW s1d1/s1d2/s1d3
1136 * allows W s1d1/s1d2
1137 * denies R s1d1/s1d2
1138 * layer 3: allows R s1d1
1139 * layer 4: allows R s1d1/s1d2
1140 * denies W s1d1/s1d2
1141 * layer 5: allows R s1d1/s1d2
1142 * layer 6: allows X ----
1143 * layer 7: allows W s1d1/s1d2
1144 * denies R s1d1/s1d2
1145 */
1146 const struct rule layer1_read[] = {
1147 /* Allows read access to file1_s1d3 with the first layer. */
1148 {
1149 .path = file1_s1d3,
1150 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1151 },
1152 {},
1153 };
1154 /* First rule with write restrictions. */
1155 const struct rule layer2_read_write[] = {
1156 /* Start by granting read-write access via its parent directory... */
1157 {
1158 .path = dir_s1d3,
1159 .access = LANDLOCK_ACCESS_FS_READ_FILE |
1160 LANDLOCK_ACCESS_FS_WRITE_FILE,
1161 },
1162 /* ...but also denies read access via its grandparent directory. */
1163 {
1164 .path = dir_s1d2,
1165 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1166 },
1167 {},
1168 };
1169 const struct rule layer3_read[] = {
1170 /* Allows read access via its great-grandparent directory. */
1171 {
1172 .path = dir_s1d1,
1173 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1174 },
1175 {},
1176 };
1177 const struct rule layer4_read_write[] = {
1178 /*
1179 * Try to confuse the deny access by denying write (but not
1180 * read) access via its grandparent directory.
1181 */
1182 {
1183 .path = dir_s1d2,
1184 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1185 },
1186 {},
1187 };
1188 const struct rule layer5_read[] = {
1189 /*
1190 * Try to override layer2's deny read access by explicitly
1191 * allowing read access via file1_s1d3's grandparent.
1192 */
1193 {
1194 .path = dir_s1d2,
1195 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1196 },
1197 {},
1198 };
1199 const struct rule layer6_execute[] = {
1200 /*
1201 * Restricts an unrelated file hierarchy with a new access
1202 * (non-overlapping) type.
1203 */
1204 {
1205 .path = dir_s2d1,
1206 .access = LANDLOCK_ACCESS_FS_EXECUTE,
1207 },
1208 {},
1209 };
1210 const struct rule layer7_read_write[] = {
1211 /*
1212 * Finally, denies read access to file1_s1d3 via its
1213 * grandparent.
1214 */
1215 {
1216 .path = dir_s1d2,
1217 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1218 },
1219 {},
1220 };
1221 int ruleset_fd;
1222
1223 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1224 layer1_read);
1225 ASSERT_LE(0, ruleset_fd);
1226 enforce_ruleset(_metadata, ruleset_fd);
1227 ASSERT_EQ(0, close(ruleset_fd));
1228
1229 /* Checks that read access is granted for file1_s1d3 with layer 1. */
1230 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1231 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1232 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1233
1234 ruleset_fd = create_ruleset(_metadata,
1235 LANDLOCK_ACCESS_FS_READ_FILE |
1236 LANDLOCK_ACCESS_FS_WRITE_FILE,
1237 layer2_read_write);
1238 ASSERT_LE(0, ruleset_fd);
1239 enforce_ruleset(_metadata, ruleset_fd);
1240 ASSERT_EQ(0, close(ruleset_fd));
1241
1242 /* Checks that previous access rights are unchanged with layer 2. */
1243 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1244 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1245 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1246
1247 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1248 layer3_read);
1249 ASSERT_LE(0, ruleset_fd);
1250 enforce_ruleset(_metadata, ruleset_fd);
1251 ASSERT_EQ(0, close(ruleset_fd));
1252
1253 /* Checks that previous access rights are unchanged with layer 3. */
1254 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1255 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1256 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1257
1258 /* This time, denies write access for the file hierarchy. */
1259 ruleset_fd = create_ruleset(_metadata,
1260 LANDLOCK_ACCESS_FS_READ_FILE |
1261 LANDLOCK_ACCESS_FS_WRITE_FILE,
1262 layer4_read_write);
1263 ASSERT_LE(0, ruleset_fd);
1264 enforce_ruleset(_metadata, ruleset_fd);
1265 ASSERT_EQ(0, close(ruleset_fd));
1266
1267 /*
1268 * Checks that the only change with layer 4 is that write access is
1269 * denied.
1270 */
1271 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1272 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1273 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1274 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1275
1276 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
1277 layer5_read);
1278 ASSERT_LE(0, ruleset_fd);
1279 enforce_ruleset(_metadata, ruleset_fd);
1280 ASSERT_EQ(0, close(ruleset_fd));
1281
1282 /* Checks that previous access rights are unchanged with layer 5. */
1283 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1284 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1285 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1286 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1287
1288 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_EXECUTE,
1289 layer6_execute);
1290 ASSERT_LE(0, ruleset_fd);
1291 enforce_ruleset(_metadata, ruleset_fd);
1292 ASSERT_EQ(0, close(ruleset_fd));
1293
1294 /* Checks that previous access rights are unchanged with layer 6. */
1295 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1296 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1297 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1298 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1299
1300 ruleset_fd = create_ruleset(_metadata,
1301 LANDLOCK_ACCESS_FS_READ_FILE |
1302 LANDLOCK_ACCESS_FS_WRITE_FILE,
1303 layer7_read_write);
1304 ASSERT_LE(0, ruleset_fd);
1305 enforce_ruleset(_metadata, ruleset_fd);
1306 ASSERT_EQ(0, close(ruleset_fd));
1307
1308 /* Checks read access is now denied with layer 7. */
1309 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
1310 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1311 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1312 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1313 }
1314
TEST_F_FORK(layout1,inherit_subset)1315 TEST_F_FORK(layout1, inherit_subset)
1316 {
1317 const struct rule rules[] = {
1318 {
1319 .path = dir_s1d2,
1320 .access = LANDLOCK_ACCESS_FS_READ_FILE |
1321 LANDLOCK_ACCESS_FS_READ_DIR,
1322 },
1323 {},
1324 };
1325 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1326
1327 ASSERT_LE(0, ruleset_fd);
1328 enforce_ruleset(_metadata, ruleset_fd);
1329
1330 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1331 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1332
1333 /* Write access is forbidden. */
1334 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1335 /* Readdir access is allowed. */
1336 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1337
1338 /* Write access is forbidden. */
1339 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1340 /* Readdir access is allowed. */
1341 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1342
1343 /*
1344 * Tests shared rule extension: the following rules should not grant
1345 * any new access, only remove some. Once enforced, these rules are
1346 * ANDed with the previous ones.
1347 */
1348 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1349 dir_s1d2);
1350 /*
1351 * According to ruleset_fd, dir_s1d2 should now have the
1352 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE
1353 * access rights (even if this directory is opened a second time).
1354 * However, when enforcing this updated ruleset, the ruleset tied to
1355 * the current process (i.e. its domain) will still only have the
1356 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and
1357 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but
1358 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would
1359 * be a privilege escalation.
1360 */
1361 enforce_ruleset(_metadata, ruleset_fd);
1362
1363 /* Same tests and results as above. */
1364 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1365 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1366
1367 /* It is still forbidden to write in file1_s1d2. */
1368 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1369 /* Readdir access is still allowed. */
1370 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1371
1372 /* It is still forbidden to write in file1_s1d3. */
1373 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1374 /* Readdir access is still allowed. */
1375 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1376
1377 /*
1378 * Try to get more privileges by adding new access rights to the parent
1379 * directory: dir_s1d1.
1380 */
1381 add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1);
1382 enforce_ruleset(_metadata, ruleset_fd);
1383
1384 /* Same tests and results as above. */
1385 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1386 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1387
1388 /* It is still forbidden to write in file1_s1d2. */
1389 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1390 /* Readdir access is still allowed. */
1391 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1392
1393 /* It is still forbidden to write in file1_s1d3. */
1394 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1395 /* Readdir access is still allowed. */
1396 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1397
1398 /*
1399 * Now, dir_s1d3 get a new rule tied to it, only allowing
1400 * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is
1401 * that there was no rule tied to it before.
1402 */
1403 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1404 dir_s1d3);
1405 enforce_ruleset(_metadata, ruleset_fd);
1406 ASSERT_EQ(0, close(ruleset_fd));
1407
1408 /*
1409 * Same tests and results as above, except for open(dir_s1d3) which is
1410 * now denied because the new rule mask the rule previously inherited
1411 * from dir_s1d2.
1412 */
1413
1414 /* Same tests and results as above. */
1415 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1416 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1417
1418 /* It is still forbidden to write in file1_s1d2. */
1419 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1420 /* Readdir access is still allowed. */
1421 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1422
1423 /* It is still forbidden to write in file1_s1d3. */
1424 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1425 /*
1426 * Readdir of dir_s1d3 is still allowed because of the OR policy inside
1427 * the same layer.
1428 */
1429 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1430 }
1431
TEST_F_FORK(layout1,inherit_superset)1432 TEST_F_FORK(layout1, inherit_superset)
1433 {
1434 const struct rule rules[] = {
1435 {
1436 .path = dir_s1d3,
1437 .access = ACCESS_RO,
1438 },
1439 {},
1440 };
1441 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1442
1443 ASSERT_LE(0, ruleset_fd);
1444 enforce_ruleset(_metadata, ruleset_fd);
1445
1446 /* Readdir access is denied for dir_s1d2. */
1447 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1448 /* Readdir access is allowed for dir_s1d3. */
1449 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1450 /* File access is allowed for file1_s1d3. */
1451 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1452
1453 /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */
1454 add_path_beneath(_metadata, ruleset_fd,
1455 LANDLOCK_ACCESS_FS_READ_FILE |
1456 LANDLOCK_ACCESS_FS_READ_DIR,
1457 dir_s1d2);
1458 enforce_ruleset(_metadata, ruleset_fd);
1459 ASSERT_EQ(0, close(ruleset_fd));
1460
1461 /* Readdir access is still denied for dir_s1d2. */
1462 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1463 /* Readdir access is still allowed for dir_s1d3. */
1464 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1465 /* File access is still allowed for file1_s1d3. */
1466 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1467 }
1468
TEST_F_FORK(layout0,max_layers)1469 TEST_F_FORK(layout0, max_layers)
1470 {
1471 int i, err;
1472 const struct rule rules[] = {
1473 {
1474 .path = TMP_DIR,
1475 .access = ACCESS_RO,
1476 },
1477 {},
1478 };
1479 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1480
1481 ASSERT_LE(0, ruleset_fd);
1482 for (i = 0; i < 16; i++)
1483 enforce_ruleset(_metadata, ruleset_fd);
1484
1485 for (i = 0; i < 2; i++) {
1486 err = landlock_restrict_self(ruleset_fd, 0);
1487 ASSERT_EQ(-1, err);
1488 ASSERT_EQ(E2BIG, errno);
1489 }
1490 ASSERT_EQ(0, close(ruleset_fd));
1491 }
1492
TEST_F_FORK(layout1,empty_or_same_ruleset)1493 TEST_F_FORK(layout1, empty_or_same_ruleset)
1494 {
1495 struct landlock_ruleset_attr ruleset_attr = {};
1496 int ruleset_fd;
1497
1498 /* Tests empty handled_access_fs. */
1499 ruleset_fd =
1500 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1501 ASSERT_LE(-1, ruleset_fd);
1502 ASSERT_EQ(ENOMSG, errno);
1503
1504 /* Enforces policy which deny read access to all files. */
1505 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE;
1506 ruleset_fd =
1507 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1508 ASSERT_LE(0, ruleset_fd);
1509 enforce_ruleset(_metadata, ruleset_fd);
1510 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1511 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1512
1513 /* Nests a policy which deny read access to all directories. */
1514 ruleset_attr.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR;
1515 ruleset_fd =
1516 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1517 ASSERT_LE(0, ruleset_fd);
1518 enforce_ruleset(_metadata, ruleset_fd);
1519 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1520 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1521
1522 /* Enforces a second time with the same ruleset. */
1523 enforce_ruleset(_metadata, ruleset_fd);
1524 ASSERT_EQ(0, close(ruleset_fd));
1525 }
1526
TEST_F_FORK(layout1,rule_on_mountpoint)1527 TEST_F_FORK(layout1, rule_on_mountpoint)
1528 {
1529 const struct rule rules[] = {
1530 {
1531 .path = dir_s1d1,
1532 .access = ACCESS_RO,
1533 },
1534 {
1535 /* dir_s3d2 is a mount point. */
1536 .path = dir_s3d2,
1537 .access = ACCESS_RO,
1538 },
1539 {},
1540 };
1541 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1542
1543 ASSERT_LE(0, ruleset_fd);
1544 enforce_ruleset(_metadata, ruleset_fd);
1545 ASSERT_EQ(0, close(ruleset_fd));
1546
1547 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1548
1549 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1550
1551 ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));
1552 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1553 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1554 }
1555
TEST_F_FORK(layout1,rule_over_mountpoint)1556 TEST_F_FORK(layout1, rule_over_mountpoint)
1557 {
1558 const struct rule rules[] = {
1559 {
1560 .path = dir_s1d1,
1561 .access = ACCESS_RO,
1562 },
1563 {
1564 /* dir_s3d2 is a mount point. */
1565 .path = dir_s3d1,
1566 .access = ACCESS_RO,
1567 },
1568 {},
1569 };
1570 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1571
1572 ASSERT_LE(0, ruleset_fd);
1573 enforce_ruleset(_metadata, ruleset_fd);
1574 ASSERT_EQ(0, close(ruleset_fd));
1575
1576 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1577
1578 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1579
1580 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
1581 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1582 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1583 }
1584
1585 /*
1586 * This test verifies that we can apply a landlock rule on the root directory
1587 * (which might require special handling).
1588 */
TEST_F_FORK(layout1,rule_over_root_allow_then_deny)1589 TEST_F_FORK(layout1, rule_over_root_allow_then_deny)
1590 {
1591 struct rule rules[] = {
1592 {
1593 .path = "/",
1594 .access = ACCESS_RO,
1595 },
1596 {},
1597 };
1598 int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1599
1600 ASSERT_LE(0, ruleset_fd);
1601 enforce_ruleset(_metadata, ruleset_fd);
1602 ASSERT_EQ(0, close(ruleset_fd));
1603
1604 /* Checks allowed access. */
1605 ASSERT_EQ(0, test_open("/", O_RDONLY));
1606 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1607
1608 rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;
1609 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1610 ASSERT_LE(0, ruleset_fd);
1611 enforce_ruleset(_metadata, ruleset_fd);
1612 ASSERT_EQ(0, close(ruleset_fd));
1613
1614 /* Checks denied access (on a directory). */
1615 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1616 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1617 }
1618
TEST_F_FORK(layout1,rule_over_root_deny)1619 TEST_F_FORK(layout1, rule_over_root_deny)
1620 {
1621 const struct rule rules[] = {
1622 {
1623 .path = "/",
1624 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1625 },
1626 {},
1627 };
1628 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1629
1630 ASSERT_LE(0, ruleset_fd);
1631 enforce_ruleset(_metadata, ruleset_fd);
1632 ASSERT_EQ(0, close(ruleset_fd));
1633
1634 /* Checks denied access (on a directory). */
1635 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1636 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1637 }
1638
TEST_F_FORK(layout1,rule_inside_mount_ns)1639 TEST_F_FORK(layout1, rule_inside_mount_ns)
1640 {
1641 const struct rule rules[] = {
1642 {
1643 .path = "s3d3",
1644 .access = ACCESS_RO,
1645 },
1646 {},
1647 };
1648 int ruleset_fd;
1649
1650 set_cap(_metadata, CAP_SYS_ADMIN);
1651 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3))
1652 {
1653 TH_LOG("Failed to pivot root: %s", strerror(errno));
1654 };
1655 ASSERT_EQ(0, chdir("/"));
1656 clear_cap(_metadata, CAP_SYS_ADMIN);
1657
1658 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1659 ASSERT_LE(0, ruleset_fd);
1660 enforce_ruleset(_metadata, ruleset_fd);
1661 ASSERT_EQ(0, close(ruleset_fd));
1662
1663 ASSERT_EQ(0, test_open("s3d3", O_RDONLY));
1664 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1665 }
1666
TEST_F_FORK(layout1,mount_and_pivot)1667 TEST_F_FORK(layout1, mount_and_pivot)
1668 {
1669 const struct rule rules[] = {
1670 {
1671 .path = dir_s3d2,
1672 .access = ACCESS_RO,
1673 },
1674 {},
1675 };
1676 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1677
1678 ASSERT_LE(0, ruleset_fd);
1679 enforce_ruleset(_metadata, ruleset_fd);
1680 ASSERT_EQ(0, close(ruleset_fd));
1681
1682 set_cap(_metadata, CAP_SYS_ADMIN);
1683 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
1684 ASSERT_EQ(EPERM, errno);
1685 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1686 ASSERT_EQ(EPERM, errno);
1687 clear_cap(_metadata, CAP_SYS_ADMIN);
1688 }
1689
TEST_F_FORK(layout1,move_mount)1690 TEST_F_FORK(layout1, move_mount)
1691 {
1692 const struct rule rules[] = {
1693 {
1694 .path = dir_s3d2,
1695 .access = ACCESS_RO,
1696 },
1697 {},
1698 };
1699 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1700
1701 ASSERT_LE(0, ruleset_fd);
1702
1703 set_cap(_metadata, CAP_SYS_ADMIN);
1704 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1705 dir_s1d2, 0))
1706 {
1707 TH_LOG("Failed to move mount: %s", strerror(errno));
1708 }
1709
1710 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
1711 dir_s3d2, 0));
1712 clear_cap(_metadata, CAP_SYS_ADMIN);
1713
1714 enforce_ruleset(_metadata, ruleset_fd);
1715 ASSERT_EQ(0, close(ruleset_fd));
1716
1717 set_cap(_metadata, CAP_SYS_ADMIN);
1718 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1719 dir_s1d2, 0));
1720 ASSERT_EQ(EPERM, errno);
1721 clear_cap(_metadata, CAP_SYS_ADMIN);
1722 }
1723
TEST_F_FORK(layout1,topology_changes_with_net_only)1724 TEST_F_FORK(layout1, topology_changes_with_net_only)
1725 {
1726 const struct landlock_ruleset_attr ruleset_net = {
1727 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
1728 LANDLOCK_ACCESS_NET_CONNECT_TCP,
1729 };
1730 int ruleset_fd;
1731
1732 /* Add network restrictions. */
1733 ruleset_fd =
1734 landlock_create_ruleset(&ruleset_net, sizeof(ruleset_net), 0);
1735 ASSERT_LE(0, ruleset_fd);
1736 enforce_ruleset(_metadata, ruleset_fd);
1737 ASSERT_EQ(0, close(ruleset_fd));
1738
1739 /* Mount, remount, move_mount, umount, and pivot_root checks. */
1740 set_cap(_metadata, CAP_SYS_ADMIN);
1741 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s1d2));
1742 ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC, NULL));
1743 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
1744 dir_s2d2, 0));
1745 ASSERT_EQ(0, umount(dir_s2d2));
1746 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1747 ASSERT_EQ(0, chdir("/"));
1748 clear_cap(_metadata, CAP_SYS_ADMIN);
1749 }
1750
TEST_F_FORK(layout1,topology_changes_with_net_and_fs)1751 TEST_F_FORK(layout1, topology_changes_with_net_and_fs)
1752 {
1753 const struct landlock_ruleset_attr ruleset_net_fs = {
1754 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
1755 LANDLOCK_ACCESS_NET_CONNECT_TCP,
1756 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
1757 };
1758 int ruleset_fd;
1759
1760 /* Add network and filesystem restrictions. */
1761 ruleset_fd = landlock_create_ruleset(&ruleset_net_fs,
1762 sizeof(ruleset_net_fs), 0);
1763 ASSERT_LE(0, ruleset_fd);
1764 enforce_ruleset(_metadata, ruleset_fd);
1765 ASSERT_EQ(0, close(ruleset_fd));
1766
1767 /* Mount, remount, move_mount, umount, and pivot_root checks. */
1768 set_cap(_metadata, CAP_SYS_ADMIN);
1769 ASSERT_EQ(-1, mount_opt(&mnt_tmp, dir_s1d2));
1770 ASSERT_EQ(EPERM, errno);
1771 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC, NULL));
1772 ASSERT_EQ(EPERM, errno);
1773 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1774 dir_s2d2, 0));
1775 ASSERT_EQ(EPERM, errno);
1776 ASSERT_EQ(-1, umount(dir_s3d2));
1777 ASSERT_EQ(EPERM, errno);
1778 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1779 ASSERT_EQ(EPERM, errno);
1780 clear_cap(_metadata, CAP_SYS_ADMIN);
1781 }
1782
TEST_F_FORK(layout1,release_inodes)1783 TEST_F_FORK(layout1, release_inodes)
1784 {
1785 const struct rule rules[] = {
1786 {
1787 .path = dir_s1d1,
1788 .access = ACCESS_RO,
1789 },
1790 {
1791 .path = dir_s3d2,
1792 .access = ACCESS_RO,
1793 },
1794 {
1795 .path = dir_s3d3,
1796 .access = ACCESS_RO,
1797 },
1798 {},
1799 };
1800 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1801
1802 ASSERT_LE(0, ruleset_fd);
1803 /* Unmount a file hierarchy while it is being used by a ruleset. */
1804 set_cap(_metadata, CAP_SYS_ADMIN);
1805 ASSERT_EQ(0, umount(dir_s3d2));
1806 clear_cap(_metadata, CAP_SYS_ADMIN);
1807
1808 enforce_ruleset(_metadata, ruleset_fd);
1809 ASSERT_EQ(0, close(ruleset_fd));
1810
1811 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1812 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
1813 /* This dir_s3d3 would not be allowed and does not exist anyway. */
1814 ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));
1815 }
1816
1817 enum relative_access {
1818 REL_OPEN,
1819 REL_CHDIR,
1820 REL_CHROOT_ONLY,
1821 REL_CHROOT_CHDIR,
1822 };
1823
test_relative_path(struct __test_metadata * const _metadata,const enum relative_access rel)1824 static void test_relative_path(struct __test_metadata *const _metadata,
1825 const enum relative_access rel)
1826 {
1827 /*
1828 * Common layer to check that chroot doesn't ignore it (i.e. a chroot
1829 * is not a disconnected root directory).
1830 */
1831 const struct rule layer1_base[] = {
1832 {
1833 .path = TMP_DIR,
1834 .access = ACCESS_RO,
1835 },
1836 {},
1837 };
1838 const struct rule layer2_subs[] = {
1839 {
1840 .path = dir_s1d2,
1841 .access = ACCESS_RO,
1842 },
1843 {
1844 .path = dir_s2d2,
1845 .access = ACCESS_RO,
1846 },
1847 {},
1848 };
1849 int dirfd, ruleset_fd;
1850
1851 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
1852 ASSERT_LE(0, ruleset_fd);
1853 enforce_ruleset(_metadata, ruleset_fd);
1854 ASSERT_EQ(0, close(ruleset_fd));
1855
1856 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);
1857
1858 ASSERT_LE(0, ruleset_fd);
1859 switch (rel) {
1860 case REL_OPEN:
1861 case REL_CHDIR:
1862 break;
1863 case REL_CHROOT_ONLY:
1864 ASSERT_EQ(0, chdir(dir_s2d2));
1865 break;
1866 case REL_CHROOT_CHDIR:
1867 ASSERT_EQ(0, chdir(dir_s1d2));
1868 break;
1869 default:
1870 ASSERT_TRUE(false);
1871 return;
1872 }
1873
1874 set_cap(_metadata, CAP_SYS_CHROOT);
1875 enforce_ruleset(_metadata, ruleset_fd);
1876
1877 switch (rel) {
1878 case REL_OPEN:
1879 dirfd = open(dir_s1d2, O_DIRECTORY);
1880 ASSERT_LE(0, dirfd);
1881 break;
1882 case REL_CHDIR:
1883 ASSERT_EQ(0, chdir(dir_s1d2));
1884 dirfd = AT_FDCWD;
1885 break;
1886 case REL_CHROOT_ONLY:
1887 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */
1888 ASSERT_EQ(0, chroot("../../s1d1/s1d2"))
1889 {
1890 TH_LOG("Failed to chroot: %s", strerror(errno));
1891 }
1892 dirfd = AT_FDCWD;
1893 break;
1894 case REL_CHROOT_CHDIR:
1895 /* Do chroot into dir_s1d2. */
1896 ASSERT_EQ(0, chroot("."))
1897 {
1898 TH_LOG("Failed to chroot: %s", strerror(errno));
1899 }
1900 dirfd = AT_FDCWD;
1901 break;
1902 }
1903
1904 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,
1905 test_open_rel(dirfd, "..", O_RDONLY));
1906 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));
1907
1908 if (rel == REL_CHROOT_ONLY) {
1909 /* The current directory is dir_s2d2. */
1910 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));
1911 } else {
1912 /* The current directory is dir_s1d2. */
1913 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));
1914 }
1915
1916 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {
1917 /* Checks the root dir_s1d2. */
1918 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));
1919 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));
1920 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));
1921 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));
1922 }
1923
1924 if (rel != REL_CHROOT_CHDIR) {
1925 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));
1926 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));
1927 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",
1928 O_RDONLY));
1929
1930 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));
1931 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));
1932 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",
1933 O_RDONLY));
1934 }
1935
1936 if (rel == REL_OPEN)
1937 ASSERT_EQ(0, close(dirfd));
1938 ASSERT_EQ(0, close(ruleset_fd));
1939 }
1940
TEST_F_FORK(layout1,relative_open)1941 TEST_F_FORK(layout1, relative_open)
1942 {
1943 test_relative_path(_metadata, REL_OPEN);
1944 }
1945
TEST_F_FORK(layout1,relative_chdir)1946 TEST_F_FORK(layout1, relative_chdir)
1947 {
1948 test_relative_path(_metadata, REL_CHDIR);
1949 }
1950
TEST_F_FORK(layout1,relative_chroot_only)1951 TEST_F_FORK(layout1, relative_chroot_only)
1952 {
1953 test_relative_path(_metadata, REL_CHROOT_ONLY);
1954 }
1955
TEST_F_FORK(layout1,relative_chroot_chdir)1956 TEST_F_FORK(layout1, relative_chroot_chdir)
1957 {
1958 test_relative_path(_metadata, REL_CHROOT_CHDIR);
1959 }
1960
copy_binary(struct __test_metadata * const _metadata,const char * const dst_path)1961 static void copy_binary(struct __test_metadata *const _metadata,
1962 const char *const dst_path)
1963 {
1964 int dst_fd, src_fd;
1965 struct stat statbuf;
1966
1967 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);
1968 ASSERT_LE(0, dst_fd)
1969 {
1970 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));
1971 }
1972 src_fd = open(BINARY_PATH, O_RDONLY | O_CLOEXEC);
1973 ASSERT_LE(0, src_fd)
1974 {
1975 TH_LOG("Failed to open \"" BINARY_PATH "\": %s",
1976 strerror(errno));
1977 }
1978 ASSERT_EQ(0, fstat(src_fd, &statbuf));
1979 ASSERT_EQ(statbuf.st_size,
1980 sendfile(dst_fd, src_fd, 0, statbuf.st_size));
1981 ASSERT_EQ(0, close(src_fd));
1982 ASSERT_EQ(0, close(dst_fd));
1983 }
1984
test_execute(struct __test_metadata * const _metadata,const int err,const char * const path)1985 static void test_execute(struct __test_metadata *const _metadata, const int err,
1986 const char *const path)
1987 {
1988 int status;
1989 char *const argv[] = { (char *)path, NULL };
1990 const pid_t child = fork();
1991
1992 ASSERT_LE(0, child);
1993 if (child == 0) {
1994 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))
1995 {
1996 TH_LOG("Failed to execute \"%s\": %s", path,
1997 strerror(errno));
1998 };
1999 ASSERT_EQ(err, errno);
2000 _exit(__test_passed(_metadata) ? 2 : 1);
2001 return;
2002 }
2003 ASSERT_EQ(child, waitpid(child, &status, 0));
2004 ASSERT_EQ(1, WIFEXITED(status));
2005 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))
2006 {
2007 TH_LOG("Unexpected return code for \"%s\": %s", path,
2008 strerror(errno));
2009 };
2010 }
2011
TEST_F_FORK(layout1,execute)2012 TEST_F_FORK(layout1, execute)
2013 {
2014 const struct rule rules[] = {
2015 {
2016 .path = dir_s1d2,
2017 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2018 },
2019 {},
2020 };
2021 const int ruleset_fd =
2022 create_ruleset(_metadata, rules[0].access, rules);
2023
2024 ASSERT_LE(0, ruleset_fd);
2025 copy_binary(_metadata, file1_s1d1);
2026 copy_binary(_metadata, file1_s1d2);
2027 copy_binary(_metadata, file1_s1d3);
2028
2029 enforce_ruleset(_metadata, ruleset_fd);
2030 ASSERT_EQ(0, close(ruleset_fd));
2031
2032 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
2033 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
2034 test_execute(_metadata, EACCES, file1_s1d1);
2035
2036 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
2037 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
2038 test_execute(_metadata, 0, file1_s1d2);
2039
2040 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
2041 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
2042 test_execute(_metadata, 0, file1_s1d3);
2043 }
2044
TEST_F_FORK(layout1,link)2045 TEST_F_FORK(layout1, link)
2046 {
2047 const struct rule layer1[] = {
2048 {
2049 .path = dir_s1d2,
2050 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2051 },
2052 {},
2053 };
2054 const struct rule layer2[] = {
2055 {
2056 .path = dir_s1d3,
2057 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2058 },
2059 {},
2060 };
2061 int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
2062
2063 ASSERT_LE(0, ruleset_fd);
2064
2065 ASSERT_EQ(0, unlink(file1_s1d1));
2066 ASSERT_EQ(0, unlink(file1_s1d2));
2067 ASSERT_EQ(0, unlink(file1_s1d3));
2068
2069 enforce_ruleset(_metadata, ruleset_fd);
2070 ASSERT_EQ(0, close(ruleset_fd));
2071
2072 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2073 ASSERT_EQ(EACCES, errno);
2074
2075 /* Denies linking because of reparenting. */
2076 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2077 ASSERT_EQ(EXDEV, errno);
2078 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2079 ASSERT_EQ(EXDEV, errno);
2080 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2081 ASSERT_EQ(EXDEV, errno);
2082
2083 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2084 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2085
2086 /* Prepares for next unlinks. */
2087 ASSERT_EQ(0, unlink(file2_s1d2));
2088 ASSERT_EQ(0, unlink(file2_s1d3));
2089
2090 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
2091 ASSERT_LE(0, ruleset_fd);
2092 enforce_ruleset(_metadata, ruleset_fd);
2093 ASSERT_EQ(0, close(ruleset_fd));
2094
2095 /* Checks that linkind doesn't require the ability to delete a file. */
2096 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
2097 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
2098 }
2099
test_rename(const char * const oldpath,const char * const newpath)2100 static int test_rename(const char *const oldpath, const char *const newpath)
2101 {
2102 if (rename(oldpath, newpath))
2103 return errno;
2104 return 0;
2105 }
2106
test_exchange(const char * const oldpath,const char * const newpath)2107 static int test_exchange(const char *const oldpath, const char *const newpath)
2108 {
2109 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))
2110 return errno;
2111 return 0;
2112 }
2113
TEST_F_FORK(layout1,rename_file)2114 TEST_F_FORK(layout1, rename_file)
2115 {
2116 const struct rule rules[] = {
2117 {
2118 .path = dir_s1d3,
2119 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2120 },
2121 {
2122 .path = dir_s2d2,
2123 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2124 },
2125 {},
2126 };
2127 const int ruleset_fd =
2128 create_ruleset(_metadata, rules[0].access, rules);
2129
2130 ASSERT_LE(0, ruleset_fd);
2131
2132 ASSERT_EQ(0, unlink(file1_s1d2));
2133
2134 enforce_ruleset(_metadata, ruleset_fd);
2135 ASSERT_EQ(0, close(ruleset_fd));
2136
2137 /*
2138 * Tries to replace a file, from a directory that allows file removal,
2139 * but to a different directory (which also allows file removal).
2140 */
2141 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));
2142 ASSERT_EQ(EXDEV, errno);
2143 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,
2144 RENAME_EXCHANGE));
2145 ASSERT_EQ(EXDEV, errno);
2146 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2147 RENAME_EXCHANGE));
2148 ASSERT_EQ(EXDEV, errno);
2149
2150 /*
2151 * Tries to replace a file, from a directory that denies file removal,
2152 * to a different directory (which allows file removal).
2153 */
2154 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2155 ASSERT_EQ(EACCES, errno);
2156 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,
2157 RENAME_EXCHANGE));
2158 ASSERT_EQ(EACCES, errno);
2159 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,
2160 RENAME_EXCHANGE));
2161 ASSERT_EQ(EXDEV, errno);
2162
2163 /* Exchanges files and directories that partially allow removal. */
2164 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,
2165 RENAME_EXCHANGE));
2166 ASSERT_EQ(EACCES, errno);
2167 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */
2168 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));
2169 ASSERT_EQ(EACCES, errno);
2170 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,
2171 RENAME_EXCHANGE));
2172 ASSERT_EQ(EACCES, errno);
2173 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */
2174 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
2175 ASSERT_EQ(EACCES, errno);
2176
2177 /* Renames files with different parents. */
2178 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2179 ASSERT_EQ(EXDEV, errno);
2180 ASSERT_EQ(0, unlink(file1_s1d3));
2181 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2182 ASSERT_EQ(EACCES, errno);
2183
2184 /* Exchanges and renames files with same parent. */
2185 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,
2186 RENAME_EXCHANGE));
2187 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));
2188
2189 /* Exchanges files and directories with same parent, twice. */
2190 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2191 RENAME_EXCHANGE));
2192 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2193 RENAME_EXCHANGE));
2194 }
2195
TEST_F_FORK(layout1,rename_dir)2196 TEST_F_FORK(layout1, rename_dir)
2197 {
2198 const struct rule rules[] = {
2199 {
2200 .path = dir_s1d2,
2201 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2202 },
2203 {
2204 .path = dir_s2d1,
2205 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2206 },
2207 {},
2208 };
2209 const int ruleset_fd =
2210 create_ruleset(_metadata, rules[0].access, rules);
2211
2212 ASSERT_LE(0, ruleset_fd);
2213
2214 /* Empties dir_s1d3 to allow renaming. */
2215 ASSERT_EQ(0, unlink(file1_s1d3));
2216 ASSERT_EQ(0, unlink(file2_s1d3));
2217
2218 enforce_ruleset(_metadata, ruleset_fd);
2219 ASSERT_EQ(0, close(ruleset_fd));
2220
2221 /* Exchanges and renames directory to a different parent. */
2222 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2223 RENAME_EXCHANGE));
2224 ASSERT_EQ(EXDEV, errno);
2225 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));
2226 ASSERT_EQ(EXDEV, errno);
2227 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2228 RENAME_EXCHANGE));
2229 ASSERT_EQ(EXDEV, errno);
2230
2231 /*
2232 * Exchanges directory to the same parent, which doesn't allow
2233 * directory removal.
2234 */
2235 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,
2236 RENAME_EXCHANGE));
2237 ASSERT_EQ(EACCES, errno);
2238 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */
2239 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));
2240 ASSERT_EQ(EACCES, errno);
2241 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,
2242 RENAME_EXCHANGE));
2243 ASSERT_EQ(EACCES, errno);
2244 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */
2245 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
2246 ASSERT_EQ(EACCES, errno);
2247
2248 /*
2249 * Exchanges and renames directory to the same parent, which allows
2250 * directory removal.
2251 */
2252 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,
2253 RENAME_EXCHANGE));
2254 ASSERT_EQ(0, unlink(dir_s1d3));
2255 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2256 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));
2257 ASSERT_EQ(0, rmdir(dir_s1d3));
2258 }
2259
TEST_F_FORK(layout1,reparent_refer)2260 TEST_F_FORK(layout1, reparent_refer)
2261 {
2262 const struct rule layer1[] = {
2263 {
2264 .path = dir_s1d2,
2265 .access = LANDLOCK_ACCESS_FS_REFER,
2266 },
2267 {
2268 .path = dir_s2d2,
2269 .access = LANDLOCK_ACCESS_FS_REFER,
2270 },
2271 {},
2272 };
2273 int ruleset_fd =
2274 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
2275
2276 ASSERT_LE(0, ruleset_fd);
2277 enforce_ruleset(_metadata, ruleset_fd);
2278 ASSERT_EQ(0, close(ruleset_fd));
2279
2280 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));
2281 ASSERT_EQ(EXDEV, errno);
2282 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));
2283 ASSERT_EQ(EXDEV, errno);
2284 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
2285 ASSERT_EQ(EXDEV, errno);
2286
2287 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));
2288 ASSERT_EQ(EXDEV, errno);
2289 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));
2290 ASSERT_EQ(EXDEV, errno);
2291 /*
2292 * Moving should only be allowed when the source and the destination
2293 * parent directory have REFER.
2294 */
2295 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));
2296 ASSERT_EQ(ENOTEMPTY, errno);
2297 ASSERT_EQ(0, unlink(file1_s2d3));
2298 ASSERT_EQ(0, unlink(file2_s2d3));
2299 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));
2300 }
2301
2302 /* Checks renames beneath dir_s1d1. */
refer_denied_by_default(struct __test_metadata * const _metadata,const struct rule layer1[],const int layer1_err,const struct rule layer2[])2303 static void refer_denied_by_default(struct __test_metadata *const _metadata,
2304 const struct rule layer1[],
2305 const int layer1_err,
2306 const struct rule layer2[])
2307 {
2308 int ruleset_fd;
2309
2310 ASSERT_EQ(0, unlink(file1_s1d2));
2311
2312 ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
2313 ASSERT_LE(0, ruleset_fd);
2314 enforce_ruleset(_metadata, ruleset_fd);
2315 ASSERT_EQ(0, close(ruleset_fd));
2316
2317 /*
2318 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to
2319 * layer1_err), then it allows some different-parent renames and links.
2320 */
2321 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));
2322 if (layer1_err == 0)
2323 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));
2324 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));
2325 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));
2326
2327 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
2328 ASSERT_LE(0, ruleset_fd);
2329 enforce_ruleset(_metadata, ruleset_fd);
2330 ASSERT_EQ(0, close(ruleset_fd));
2331
2332 /*
2333 * Now, either the first or the second layer does not handle
2334 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent
2335 * renames and links are denied, thus making the layer handling
2336 * LANDLOCK_ACCESS_FS_REFER null and void.
2337 */
2338 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));
2339 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));
2340 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));
2341 }
2342
2343 const struct rule layer_dir_s1d1_refer[] = {
2344 {
2345 .path = dir_s1d1,
2346 .access = LANDLOCK_ACCESS_FS_REFER,
2347 },
2348 {},
2349 };
2350
2351 const struct rule layer_dir_s1d1_execute[] = {
2352 {
2353 /* Matches a parent directory. */
2354 .path = dir_s1d1,
2355 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2356 },
2357 {},
2358 };
2359
2360 const struct rule layer_dir_s2d1_execute[] = {
2361 {
2362 /* Does not match a parent directory. */
2363 .path = dir_s2d1,
2364 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2365 },
2366 {},
2367 };
2368
2369 /*
2370 * Tests precedence over renames: denied by default for different parent
2371 * directories, *with* a rule matching a parent directory, but not directly
2372 * denying access (with MAKE_REG nor REMOVE).
2373 */
TEST_F_FORK(layout1,refer_denied_by_default1)2374 TEST_F_FORK(layout1, refer_denied_by_default1)
2375 {
2376 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
2377 layer_dir_s1d1_execute);
2378 }
2379
2380 /*
2381 * Same test but this time turning around the ABI version order: the first
2382 * layer does not handle LANDLOCK_ACCESS_FS_REFER.
2383 */
TEST_F_FORK(layout1,refer_denied_by_default2)2384 TEST_F_FORK(layout1, refer_denied_by_default2)
2385 {
2386 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,
2387 layer_dir_s1d1_refer);
2388 }
2389
2390 /*
2391 * Tests precedence over renames: denied by default for different parent
2392 * directories, *without* a rule matching a parent directory, but not directly
2393 * denying access (with MAKE_REG nor REMOVE).
2394 */
TEST_F_FORK(layout1,refer_denied_by_default3)2395 TEST_F_FORK(layout1, refer_denied_by_default3)
2396 {
2397 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
2398 layer_dir_s2d1_execute);
2399 }
2400
2401 /*
2402 * Same test but this time turning around the ABI version order: the first
2403 * layer does not handle LANDLOCK_ACCESS_FS_REFER.
2404 */
TEST_F_FORK(layout1,refer_denied_by_default4)2405 TEST_F_FORK(layout1, refer_denied_by_default4)
2406 {
2407 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,
2408 layer_dir_s1d1_refer);
2409 }
2410
2411 /*
2412 * Tests walking through a denied root mount.
2413 */
TEST_F_FORK(layout1,refer_mount_root_deny)2414 TEST_F_FORK(layout1, refer_mount_root_deny)
2415 {
2416 const struct landlock_ruleset_attr ruleset_attr = {
2417 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_DIR,
2418 };
2419 int root_fd, ruleset_fd;
2420
2421 /* Creates a mount object from a non-mount point. */
2422 set_cap(_metadata, CAP_SYS_ADMIN);
2423 root_fd =
2424 open_tree(AT_FDCWD, dir_s1d1,
2425 AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
2426 clear_cap(_metadata, CAP_SYS_ADMIN);
2427 ASSERT_LE(0, root_fd);
2428
2429 ruleset_fd =
2430 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
2431 ASSERT_LE(0, ruleset_fd);
2432
2433 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
2434 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
2435 EXPECT_EQ(0, close(ruleset_fd));
2436
2437 /* Link denied by Landlock: EACCES. */
2438 EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0));
2439 EXPECT_EQ(EACCES, errno);
2440
2441 /* renameat2() always returns EBUSY. */
2442 EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0));
2443 EXPECT_EQ(EBUSY, errno);
2444
2445 EXPECT_EQ(0, close(root_fd));
2446 }
2447
TEST_F_FORK(layout1,reparent_link)2448 TEST_F_FORK(layout1, reparent_link)
2449 {
2450 const struct rule layer1[] = {
2451 {
2452 .path = dir_s1d2,
2453 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2454 },
2455 {
2456 .path = dir_s1d3,
2457 .access = LANDLOCK_ACCESS_FS_REFER,
2458 },
2459 {
2460 .path = dir_s2d2,
2461 .access = LANDLOCK_ACCESS_FS_REFER,
2462 },
2463 {
2464 .path = dir_s2d3,
2465 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2466 },
2467 {},
2468 };
2469 const int ruleset_fd = create_ruleset(
2470 _metadata,
2471 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2472
2473 ASSERT_LE(0, ruleset_fd);
2474 enforce_ruleset(_metadata, ruleset_fd);
2475 ASSERT_EQ(0, close(ruleset_fd));
2476
2477 ASSERT_EQ(0, unlink(file1_s1d1));
2478 ASSERT_EQ(0, unlink(file1_s1d2));
2479 ASSERT_EQ(0, unlink(file1_s1d3));
2480
2481 /* Denies linking because of missing MAKE_REG. */
2482 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2483 ASSERT_EQ(EACCES, errno);
2484 /* Denies linking because of missing source and destination REFER. */
2485 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2486 ASSERT_EQ(EXDEV, errno);
2487 /* Denies linking because of missing source REFER. */
2488 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));
2489 ASSERT_EQ(EXDEV, errno);
2490
2491 /* Denies linking because of missing MAKE_REG. */
2492 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));
2493 ASSERT_EQ(EACCES, errno);
2494 /* Denies linking because of missing destination REFER. */
2495 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));
2496 ASSERT_EQ(EXDEV, errno);
2497
2498 /* Allows linking because of REFER and MAKE_REG. */
2499 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));
2500 ASSERT_EQ(0, unlink(file1_s2d2));
2501 /* Reverse linking denied because of missing MAKE_REG. */
2502 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));
2503 ASSERT_EQ(EACCES, errno);
2504 ASSERT_EQ(0, unlink(file1_s2d3));
2505 /* Checks reverse linking. */
2506 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));
2507 ASSERT_EQ(0, unlink(file1_s1d3));
2508
2509 /*
2510 * This is OK for a file link, but it should not be allowed for a
2511 * directory rename (because of the superset of access rights.
2512 */
2513 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));
2514 ASSERT_EQ(0, unlink(file1_s1d3));
2515
2516 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2517 ASSERT_EQ(EXDEV, errno);
2518 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2519 ASSERT_EQ(EXDEV, errno);
2520
2521 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2522 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2523 }
2524
TEST_F_FORK(layout1,reparent_rename)2525 TEST_F_FORK(layout1, reparent_rename)
2526 {
2527 /* Same rules as for reparent_link. */
2528 const struct rule layer1[] = {
2529 {
2530 .path = dir_s1d2,
2531 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2532 },
2533 {
2534 .path = dir_s1d3,
2535 .access = LANDLOCK_ACCESS_FS_REFER,
2536 },
2537 {
2538 .path = dir_s2d2,
2539 .access = LANDLOCK_ACCESS_FS_REFER,
2540 },
2541 {
2542 .path = dir_s2d3,
2543 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2544 },
2545 {},
2546 };
2547 const int ruleset_fd = create_ruleset(
2548 _metadata,
2549 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2550
2551 ASSERT_LE(0, ruleset_fd);
2552 enforce_ruleset(_metadata, ruleset_fd);
2553 ASSERT_EQ(0, close(ruleset_fd));
2554
2555 ASSERT_EQ(0, unlink(file1_s1d2));
2556 ASSERT_EQ(0, unlink(file1_s1d3));
2557
2558 /* Denies renaming because of missing MAKE_REG. */
2559 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,
2560 RENAME_EXCHANGE));
2561 ASSERT_EQ(EACCES, errno);
2562 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,
2563 RENAME_EXCHANGE));
2564 ASSERT_EQ(EACCES, errno);
2565 ASSERT_EQ(0, unlink(file1_s1d1));
2566 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
2567 ASSERT_EQ(EACCES, errno);
2568 /* Even denies same file exchange. */
2569 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,
2570 RENAME_EXCHANGE));
2571 ASSERT_EQ(EACCES, errno);
2572
2573 /* Denies renaming because of missing source and destination REFER. */
2574 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));
2575 ASSERT_EQ(EXDEV, errno);
2576 /*
2577 * Denies renaming because of missing MAKE_REG, source and destination
2578 * REFER.
2579 */
2580 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,
2581 RENAME_EXCHANGE));
2582 ASSERT_EQ(EACCES, errno);
2583 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,
2584 RENAME_EXCHANGE));
2585 ASSERT_EQ(EACCES, errno);
2586
2587 /* Denies renaming because of missing source REFER. */
2588 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2589 ASSERT_EQ(EXDEV, errno);
2590 /* Denies renaming because of missing MAKE_REG. */
2591 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,
2592 RENAME_EXCHANGE));
2593 ASSERT_EQ(EACCES, errno);
2594
2595 /* Denies renaming because of missing MAKE_REG. */
2596 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));
2597 ASSERT_EQ(EACCES, errno);
2598 /* Denies renaming because of missing destination REFER*/
2599 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2600 ASSERT_EQ(EXDEV, errno);
2601
2602 /* Denies exchange because of one missing MAKE_REG. */
2603 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,
2604 RENAME_EXCHANGE));
2605 ASSERT_EQ(EACCES, errno);
2606 /* Allows renaming because of REFER and MAKE_REG. */
2607 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));
2608
2609 /* Reverse renaming denied because of missing MAKE_REG. */
2610 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));
2611 ASSERT_EQ(EACCES, errno);
2612 ASSERT_EQ(0, unlink(file1_s2d3));
2613 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2614
2615 /* Tests reverse renaming. */
2616 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2617 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,
2618 RENAME_EXCHANGE));
2619 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2620
2621 /*
2622 * This is OK for a file rename, but it should not be allowed for a
2623 * directory rename (because of the superset of access rights).
2624 */
2625 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2626 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2627
2628 /*
2629 * Tests superset restrictions applied to directories. Not only the
2630 * dir_s2d3's parent (dir_s2d2) should be taken into account but also
2631 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right
2632 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided
2633 * directly by the moved dir_s2d3.
2634 */
2635 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));
2636 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));
2637 /*
2638 * The first rename is allowed but not the exchange because dir_s1d3's
2639 * parent (dir_s1d2) doesn't have REFER.
2640 */
2641 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2642 RENAME_EXCHANGE));
2643 ASSERT_EQ(EXDEV, errno);
2644 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,
2645 RENAME_EXCHANGE));
2646 ASSERT_EQ(EXDEV, errno);
2647 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));
2648 ASSERT_EQ(EXDEV, errno);
2649
2650 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));
2651 ASSERT_EQ(EXDEV, errno);
2652 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));
2653 ASSERT_EQ(EXDEV, errno);
2654
2655 /* Renaming in the same directory is always allowed. */
2656 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));
2657 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));
2658
2659 ASSERT_EQ(0, unlink(file1_s1d2));
2660 /* Denies because of missing source MAKE_REG and destination REFER. */
2661 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));
2662 ASSERT_EQ(EXDEV, errno);
2663
2664 ASSERT_EQ(0, unlink(file1_s1d3));
2665 /* Denies because of missing source MAKE_REG and REFER. */
2666 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));
2667 ASSERT_EQ(EXDEV, errno);
2668 }
2669
2670 static void
reparent_exdev_layers_enforce1(struct __test_metadata * const _metadata)2671 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)
2672 {
2673 const struct rule layer1[] = {
2674 {
2675 .path = dir_s1d2,
2676 .access = LANDLOCK_ACCESS_FS_REFER,
2677 },
2678 {
2679 /* Interesting for the layer2 tests. */
2680 .path = dir_s1d3,
2681 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2682 },
2683 {
2684 .path = dir_s2d2,
2685 .access = LANDLOCK_ACCESS_FS_REFER,
2686 },
2687 {
2688 .path = dir_s2d3,
2689 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2690 },
2691 {},
2692 };
2693 const int ruleset_fd = create_ruleset(
2694 _metadata,
2695 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2696
2697 ASSERT_LE(0, ruleset_fd);
2698 enforce_ruleset(_metadata, ruleset_fd);
2699 ASSERT_EQ(0, close(ruleset_fd));
2700 }
2701
2702 static void
reparent_exdev_layers_enforce2(struct __test_metadata * const _metadata)2703 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)
2704 {
2705 const struct rule layer2[] = {
2706 {
2707 .path = dir_s2d3,
2708 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,
2709 },
2710 {},
2711 };
2712 /*
2713 * Same checks as before but with a second layer and a new MAKE_DIR
2714 * rule (and no explicit handling of REFER).
2715 */
2716 const int ruleset_fd =
2717 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);
2718
2719 ASSERT_LE(0, ruleset_fd);
2720 enforce_ruleset(_metadata, ruleset_fd);
2721 ASSERT_EQ(0, close(ruleset_fd));
2722 }
2723
TEST_F_FORK(layout1,reparent_exdev_layers_rename1)2724 TEST_F_FORK(layout1, reparent_exdev_layers_rename1)
2725 {
2726 ASSERT_EQ(0, unlink(file1_s2d2));
2727 ASSERT_EQ(0, unlink(file1_s2d3));
2728
2729 reparent_exdev_layers_enforce1(_metadata);
2730
2731 /*
2732 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock
2733 * because it doesn't inherit new access rights.
2734 */
2735 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
2736 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
2737
2738 /*
2739 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it
2740 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is
2741 * already allowed for dir_s1d3.
2742 */
2743 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));
2744 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));
2745
2746 /*
2747 * However, moving the file1_s1d3 file below dir_s2d3 is allowed
2748 * because it cannot inherit MAKE_REG right (which is dedicated to
2749 * directories).
2750 */
2751 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2752
2753 reparent_exdev_layers_enforce2(_metadata);
2754
2755 /*
2756 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because
2757 * MAKE_DIR is not tied to dir_s2d2.
2758 */
2759 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));
2760 ASSERT_EQ(EACCES, errno);
2761
2762 /*
2763 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it
2764 * would grants MAKE_REG and MAKE_DIR rights to it.
2765 */
2766 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
2767 ASSERT_EQ(EXDEV, errno);
2768
2769 /*
2770 * Moving the file2_s1d3 file below dir_s2d3 is denied because the
2771 * second layer does not handle REFER, which is always denied by
2772 * default.
2773 */
2774 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));
2775 ASSERT_EQ(EXDEV, errno);
2776 }
2777
TEST_F_FORK(layout1,reparent_exdev_layers_rename2)2778 TEST_F_FORK(layout1, reparent_exdev_layers_rename2)
2779 {
2780 reparent_exdev_layers_enforce1(_metadata);
2781
2782 /* Checks EACCES predominance over EXDEV. */
2783 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2784 ASSERT_EQ(EACCES, errno);
2785 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));
2786 ASSERT_EQ(EACCES, errno);
2787 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
2788 ASSERT_EQ(EXDEV, errno);
2789 /* Modify layout! */
2790 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));
2791
2792 /* Without REFER source. */
2793 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
2794 ASSERT_EQ(EXDEV, errno);
2795 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
2796 ASSERT_EQ(EXDEV, errno);
2797
2798 reparent_exdev_layers_enforce2(_metadata);
2799
2800 /* Checks EACCES predominance over EXDEV. */
2801 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2802 ASSERT_EQ(EACCES, errno);
2803 /* Checks with actual file2_s1d2. */
2804 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));
2805 ASSERT_EQ(EACCES, errno);
2806 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
2807 ASSERT_EQ(EXDEV, errno);
2808 /*
2809 * Modifying the layout is now denied because the second layer does not
2810 * handle REFER, which is always denied by default.
2811 */
2812 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
2813 ASSERT_EQ(EXDEV, errno);
2814
2815 /* Without REFER source, EACCES wins over EXDEV. */
2816 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
2817 ASSERT_EQ(EACCES, errno);
2818 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
2819 ASSERT_EQ(EACCES, errno);
2820 }
2821
TEST_F_FORK(layout1,reparent_exdev_layers_exchange1)2822 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1)
2823 {
2824 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =
2825 file2_s2d3;
2826
2827 ASSERT_EQ(0, unlink(file1_s1d2));
2828 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
2829 ASSERT_EQ(0, unlink(file2_s2d3));
2830 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2831
2832 reparent_exdev_layers_enforce1(_metadata);
2833
2834 /* Error predominance with file exchange: returns EXDEV and EACCES. */
2835 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
2836 RENAME_EXCHANGE));
2837 ASSERT_EQ(EACCES, errno);
2838 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
2839 RENAME_EXCHANGE));
2840 ASSERT_EQ(EACCES, errno);
2841
2842 /*
2843 * Checks with directories which creation could be allowed, but denied
2844 * because of access rights that would be inherited.
2845 */
2846 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
2847 dir_file2_s2d3, RENAME_EXCHANGE));
2848 ASSERT_EQ(EXDEV, errno);
2849 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
2850 dir_file1_s1d2, RENAME_EXCHANGE));
2851 ASSERT_EQ(EXDEV, errno);
2852
2853 /* Checks with same access rights. */
2854 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
2855 RENAME_EXCHANGE));
2856 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2857 RENAME_EXCHANGE));
2858
2859 /* Checks with different (child-only) access rights. */
2860 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
2861 RENAME_EXCHANGE));
2862 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
2863 RENAME_EXCHANGE));
2864
2865 /*
2866 * Checks that exchange between file and directory are consistent.
2867 *
2868 * Moving a file (file1_s2d2) to a directory which only grants more
2869 * directory-related access rights is allowed, and at the same time
2870 * moving a directory (dir_file2_s2d3) to another directory which
2871 * grants less access rights is allowed too.
2872 *
2873 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.
2874 */
2875 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2876 RENAME_EXCHANGE));
2877 /*
2878 * However, moving back the directory is denied because it would get
2879 * more access rights than the current state and because file creation
2880 * is forbidden (in dir_s2d2).
2881 */
2882 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2883 RENAME_EXCHANGE));
2884 ASSERT_EQ(EACCES, errno);
2885 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2886 RENAME_EXCHANGE));
2887 ASSERT_EQ(EACCES, errno);
2888
2889 reparent_exdev_layers_enforce2(_metadata);
2890
2891 /* Error predominance with file exchange: returns EXDEV and EACCES. */
2892 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
2893 RENAME_EXCHANGE));
2894 ASSERT_EQ(EACCES, errno);
2895 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
2896 RENAME_EXCHANGE));
2897 ASSERT_EQ(EACCES, errno);
2898
2899 /* Checks with directories which creation is now denied. */
2900 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
2901 dir_file2_s2d3, RENAME_EXCHANGE));
2902 ASSERT_EQ(EACCES, errno);
2903 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
2904 dir_file1_s1d2, RENAME_EXCHANGE));
2905 ASSERT_EQ(EACCES, errno);
2906
2907 /* Checks with different (child-only) access rights. */
2908 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
2909 RENAME_EXCHANGE));
2910 /* Denied because of MAKE_DIR. */
2911 ASSERT_EQ(EACCES, errno);
2912 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2913 RENAME_EXCHANGE));
2914 ASSERT_EQ(EACCES, errno);
2915
2916 /* Checks with different (child-only) access rights. */
2917 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
2918 RENAME_EXCHANGE));
2919 /* Denied because of MAKE_DIR. */
2920 ASSERT_EQ(EACCES, errno);
2921 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
2922 RENAME_EXCHANGE));
2923 ASSERT_EQ(EACCES, errno);
2924
2925 /* See layout1.reparent_exdev_layers_exchange2 for complement. */
2926 }
2927
TEST_F_FORK(layout1,reparent_exdev_layers_exchange2)2928 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2)
2929 {
2930 const char *const dir_file2_s2d3 = file2_s2d3;
2931
2932 ASSERT_EQ(0, unlink(file2_s2d3));
2933 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2934
2935 reparent_exdev_layers_enforce1(_metadata);
2936 reparent_exdev_layers_enforce2(_metadata);
2937
2938 /* Checks that exchange between file and directory are consistent. */
2939 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2940 RENAME_EXCHANGE));
2941 ASSERT_EQ(EACCES, errno);
2942 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2943 RENAME_EXCHANGE));
2944 ASSERT_EQ(EACCES, errno);
2945 }
2946
TEST_F_FORK(layout1,reparent_exdev_layers_exchange3)2947 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3)
2948 {
2949 const char *const dir_file2_s2d3 = file2_s2d3;
2950
2951 ASSERT_EQ(0, unlink(file2_s2d3));
2952 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
2953
2954 reparent_exdev_layers_enforce1(_metadata);
2955
2956 /*
2957 * Checks that exchange between file and directory are consistent,
2958 * including with inverted arguments (see
2959 * layout1.reparent_exdev_layers_exchange1).
2960 */
2961 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2962 RENAME_EXCHANGE));
2963 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
2964 RENAME_EXCHANGE));
2965 ASSERT_EQ(EACCES, errno);
2966 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
2967 RENAME_EXCHANGE));
2968 ASSERT_EQ(EACCES, errno);
2969 }
2970
TEST_F_FORK(layout1,reparent_remove)2971 TEST_F_FORK(layout1, reparent_remove)
2972 {
2973 const struct rule layer1[] = {
2974 {
2975 .path = dir_s1d1,
2976 .access = LANDLOCK_ACCESS_FS_REFER |
2977 LANDLOCK_ACCESS_FS_REMOVE_DIR,
2978 },
2979 {
2980 .path = dir_s1d2,
2981 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2982 },
2983 {
2984 .path = dir_s2d1,
2985 .access = LANDLOCK_ACCESS_FS_REFER |
2986 LANDLOCK_ACCESS_FS_REMOVE_FILE,
2987 },
2988 {},
2989 };
2990 const int ruleset_fd = create_ruleset(
2991 _metadata,
2992 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |
2993 LANDLOCK_ACCESS_FS_REMOVE_FILE,
2994 layer1);
2995
2996 ASSERT_LE(0, ruleset_fd);
2997 enforce_ruleset(_metadata, ruleset_fd);
2998 ASSERT_EQ(0, close(ruleset_fd));
2999
3000 /* Access denied because of wrong/swapped remove file/dir. */
3001 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));
3002 ASSERT_EQ(EACCES, errno);
3003 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));
3004 ASSERT_EQ(EACCES, errno);
3005 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,
3006 RENAME_EXCHANGE));
3007 ASSERT_EQ(EACCES, errno);
3008 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,
3009 RENAME_EXCHANGE));
3010 ASSERT_EQ(EACCES, errno);
3011
3012 /* Access allowed thanks to the matching rights. */
3013 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));
3014 ASSERT_EQ(EISDIR, errno);
3015 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));
3016 ASSERT_EQ(ENOTDIR, errno);
3017 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
3018 ASSERT_EQ(ENOTDIR, errno);
3019 ASSERT_EQ(0, unlink(file1_s2d1));
3020 ASSERT_EQ(0, unlink(file1_s1d3));
3021 ASSERT_EQ(0, unlink(file2_s1d3));
3022 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));
3023
3024 /* Effectively removes a file and a directory by exchanging them. */
3025 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
3026 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
3027 RENAME_EXCHANGE));
3028 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
3029 RENAME_EXCHANGE));
3030 ASSERT_EQ(EACCES, errno);
3031 }
3032
TEST_F_FORK(layout1,reparent_dom_superset)3033 TEST_F_FORK(layout1, reparent_dom_superset)
3034 {
3035 const struct rule layer1[] = {
3036 {
3037 .path = dir_s1d2,
3038 .access = LANDLOCK_ACCESS_FS_REFER,
3039 },
3040 {
3041 .path = file1_s1d2,
3042 .access = LANDLOCK_ACCESS_FS_EXECUTE,
3043 },
3044 {
3045 .path = dir_s1d3,
3046 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK |
3047 LANDLOCK_ACCESS_FS_EXECUTE,
3048 },
3049 {
3050 .path = dir_s2d2,
3051 .access = LANDLOCK_ACCESS_FS_REFER |
3052 LANDLOCK_ACCESS_FS_EXECUTE |
3053 LANDLOCK_ACCESS_FS_MAKE_SOCK,
3054 },
3055 {
3056 .path = dir_s2d3,
3057 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3058 LANDLOCK_ACCESS_FS_MAKE_FIFO,
3059 },
3060 {},
3061 };
3062 int ruleset_fd = create_ruleset(_metadata,
3063 LANDLOCK_ACCESS_FS_REFER |
3064 LANDLOCK_ACCESS_FS_EXECUTE |
3065 LANDLOCK_ACCESS_FS_MAKE_SOCK |
3066 LANDLOCK_ACCESS_FS_READ_FILE |
3067 LANDLOCK_ACCESS_FS_MAKE_FIFO,
3068 layer1);
3069
3070 ASSERT_LE(0, ruleset_fd);
3071 enforce_ruleset(_metadata, ruleset_fd);
3072 ASSERT_EQ(0, close(ruleset_fd));
3073
3074 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));
3075 ASSERT_EQ(EXDEV, errno);
3076 /*
3077 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE
3078 * access right.
3079 */
3080 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));
3081 ASSERT_EQ(EXDEV, errno);
3082 /*
3083 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a
3084 * superset of access rights compared to dir_s1d2, because file1_s1d2
3085 * already has these access rights anyway.
3086 */
3087 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));
3088 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));
3089
3090 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
3091 ASSERT_EQ(EXDEV, errno);
3092 /*
3093 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access
3094 * right.
3095 */
3096 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
3097 ASSERT_EQ(EXDEV, errno);
3098 /*
3099 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset
3100 * of access rights compared to dir_s1d2, because dir_s1d3 already has
3101 * these access rights anyway.
3102 */
3103 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
3104 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
3105
3106 /*
3107 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back
3108 * will be denied because the new inherited access rights from dir_s1d2
3109 * will be less than the destination (original) dir_s2d3. This is a
3110 * sinkhole scenario where we cannot move back files or directories.
3111 */
3112 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));
3113 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
3114 ASSERT_EQ(EXDEV, errno);
3115 ASSERT_EQ(0, unlink(file2_s1d2));
3116 ASSERT_EQ(0, unlink(file2_s2d3));
3117 /*
3118 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and
3119 * MAKE_SOCK which were inherited from dir_s1d3.
3120 */
3121 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));
3122 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));
3123 ASSERT_EQ(EXDEV, errno);
3124 }
3125
TEST_F_FORK(layout1,remove_dir)3126 TEST_F_FORK(layout1, remove_dir)
3127 {
3128 const struct rule rules[] = {
3129 {
3130 .path = dir_s1d2,
3131 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
3132 },
3133 {},
3134 };
3135 const int ruleset_fd =
3136 create_ruleset(_metadata, rules[0].access, rules);
3137
3138 ASSERT_LE(0, ruleset_fd);
3139
3140 ASSERT_EQ(0, unlink(file1_s1d1));
3141 ASSERT_EQ(0, unlink(file1_s1d2));
3142 ASSERT_EQ(0, unlink(file1_s1d3));
3143 ASSERT_EQ(0, unlink(file2_s1d3));
3144
3145 enforce_ruleset(_metadata, ruleset_fd);
3146 ASSERT_EQ(0, close(ruleset_fd));
3147
3148 ASSERT_EQ(0, rmdir(dir_s1d3));
3149 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
3150 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
3151
3152 /* dir_s1d2 itself cannot be removed. */
3153 ASSERT_EQ(-1, rmdir(dir_s1d2));
3154 ASSERT_EQ(EACCES, errno);
3155 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));
3156 ASSERT_EQ(EACCES, errno);
3157 ASSERT_EQ(-1, rmdir(dir_s1d1));
3158 ASSERT_EQ(EACCES, errno);
3159 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));
3160 ASSERT_EQ(EACCES, errno);
3161 }
3162
TEST_F_FORK(layout1,remove_file)3163 TEST_F_FORK(layout1, remove_file)
3164 {
3165 const struct rule rules[] = {
3166 {
3167 .path = dir_s1d2,
3168 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
3169 },
3170 {},
3171 };
3172 const int ruleset_fd =
3173 create_ruleset(_metadata, rules[0].access, rules);
3174
3175 ASSERT_LE(0, ruleset_fd);
3176 enforce_ruleset(_metadata, ruleset_fd);
3177 ASSERT_EQ(0, close(ruleset_fd));
3178
3179 ASSERT_EQ(-1, unlink(file1_s1d1));
3180 ASSERT_EQ(EACCES, errno);
3181 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));
3182 ASSERT_EQ(EACCES, errno);
3183 ASSERT_EQ(0, unlink(file1_s1d2));
3184 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));
3185 }
3186
test_make_file(struct __test_metadata * const _metadata,const __u64 access,const mode_t mode,const dev_t dev)3187 static void test_make_file(struct __test_metadata *const _metadata,
3188 const __u64 access, const mode_t mode,
3189 const dev_t dev)
3190 {
3191 const struct rule rules[] = {
3192 {
3193 .path = dir_s1d2,
3194 .access = access,
3195 },
3196 {},
3197 };
3198 const int ruleset_fd = create_ruleset(_metadata, access, rules);
3199
3200 ASSERT_LE(0, ruleset_fd);
3201
3202 ASSERT_EQ(0, unlink(file1_s1d1));
3203 ASSERT_EQ(0, unlink(file2_s1d1));
3204 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))
3205 {
3206 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,
3207 strerror(errno));
3208 };
3209
3210 ASSERT_EQ(0, unlink(file1_s1d2));
3211 ASSERT_EQ(0, unlink(file2_s1d2));
3212
3213 ASSERT_EQ(0, unlink(file1_s1d3));
3214 ASSERT_EQ(0, unlink(file2_s1d3));
3215
3216 enforce_ruleset(_metadata, ruleset_fd);
3217 ASSERT_EQ(0, close(ruleset_fd));
3218
3219 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));
3220 ASSERT_EQ(EACCES, errno);
3221 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3222 ASSERT_EQ(EACCES, errno);
3223 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3224 ASSERT_EQ(EACCES, errno);
3225
3226 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))
3227 {
3228 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,
3229 strerror(errno));
3230 };
3231 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3232 ASSERT_EQ(0, unlink(file2_s1d2));
3233 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3234
3235 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));
3236 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3237 ASSERT_EQ(0, unlink(file2_s1d3));
3238 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3239 }
3240
TEST_F_FORK(layout1,make_char)3241 TEST_F_FORK(layout1, make_char)
3242 {
3243 /* Creates a /dev/null device. */
3244 set_cap(_metadata, CAP_MKNOD);
3245 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,
3246 makedev(1, 3));
3247 }
3248
TEST_F_FORK(layout1,make_block)3249 TEST_F_FORK(layout1, make_block)
3250 {
3251 /* Creates a /dev/loop0 device. */
3252 set_cap(_metadata, CAP_MKNOD);
3253 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,
3254 makedev(7, 0));
3255 }
3256
TEST_F_FORK(layout1,make_reg_1)3257 TEST_F_FORK(layout1, make_reg_1)
3258 {
3259 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
3260 }
3261
TEST_F_FORK(layout1,make_reg_2)3262 TEST_F_FORK(layout1, make_reg_2)
3263 {
3264 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
3265 }
3266
TEST_F_FORK(layout1,make_sock)3267 TEST_F_FORK(layout1, make_sock)
3268 {
3269 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
3270 }
3271
TEST_F_FORK(layout1,make_fifo)3272 TEST_F_FORK(layout1, make_fifo)
3273 {
3274 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
3275 }
3276
TEST_F_FORK(layout1,make_sym)3277 TEST_F_FORK(layout1, make_sym)
3278 {
3279 const struct rule rules[] = {
3280 {
3281 .path = dir_s1d2,
3282 .access = LANDLOCK_ACCESS_FS_MAKE_SYM,
3283 },
3284 {},
3285 };
3286 const int ruleset_fd =
3287 create_ruleset(_metadata, rules[0].access, rules);
3288
3289 ASSERT_LE(0, ruleset_fd);
3290
3291 ASSERT_EQ(0, unlink(file1_s1d1));
3292 ASSERT_EQ(0, unlink(file2_s1d1));
3293 ASSERT_EQ(0, symlink("none", file2_s1d1));
3294
3295 ASSERT_EQ(0, unlink(file1_s1d2));
3296 ASSERT_EQ(0, unlink(file2_s1d2));
3297
3298 ASSERT_EQ(0, unlink(file1_s1d3));
3299 ASSERT_EQ(0, unlink(file2_s1d3));
3300
3301 enforce_ruleset(_metadata, ruleset_fd);
3302 ASSERT_EQ(0, close(ruleset_fd));
3303
3304 ASSERT_EQ(-1, symlink("none", file1_s1d1));
3305 ASSERT_EQ(EACCES, errno);
3306 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3307 ASSERT_EQ(EACCES, errno);
3308 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3309 ASSERT_EQ(EACCES, errno);
3310
3311 ASSERT_EQ(0, symlink("none", file1_s1d2));
3312 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3313 ASSERT_EQ(0, unlink(file2_s1d2));
3314 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3315
3316 ASSERT_EQ(0, symlink("none", file1_s1d3));
3317 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3318 ASSERT_EQ(0, unlink(file2_s1d3));
3319 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3320 }
3321
TEST_F_FORK(layout1,make_dir)3322 TEST_F_FORK(layout1, make_dir)
3323 {
3324 const struct rule rules[] = {
3325 {
3326 .path = dir_s1d2,
3327 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,
3328 },
3329 {},
3330 };
3331 const int ruleset_fd =
3332 create_ruleset(_metadata, rules[0].access, rules);
3333
3334 ASSERT_LE(0, ruleset_fd);
3335
3336 ASSERT_EQ(0, unlink(file1_s1d1));
3337 ASSERT_EQ(0, unlink(file1_s1d2));
3338 ASSERT_EQ(0, unlink(file1_s1d3));
3339
3340 enforce_ruleset(_metadata, ruleset_fd);
3341 ASSERT_EQ(0, close(ruleset_fd));
3342
3343 /* Uses file_* as directory names. */
3344 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));
3345 ASSERT_EQ(EACCES, errno);
3346 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
3347 ASSERT_EQ(0, mkdir(file1_s1d3, 0700));
3348 }
3349
open_proc_fd(struct __test_metadata * const _metadata,const int fd,const int open_flags)3350 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,
3351 const int open_flags)
3352 {
3353 static const char path_template[] = "/proc/self/fd/%d";
3354 char procfd_path[sizeof(path_template) + 10];
3355 const int procfd_path_size =
3356 snprintf(procfd_path, sizeof(procfd_path), path_template, fd);
3357
3358 ASSERT_LT(procfd_path_size, sizeof(procfd_path));
3359 return open(procfd_path, open_flags);
3360 }
3361
TEST_F_FORK(layout1,proc_unlinked_file)3362 TEST_F_FORK(layout1, proc_unlinked_file)
3363 {
3364 const struct rule rules[] = {
3365 {
3366 .path = file1_s1d2,
3367 .access = LANDLOCK_ACCESS_FS_READ_FILE,
3368 },
3369 {},
3370 };
3371 int reg_fd, proc_fd;
3372 const int ruleset_fd = create_ruleset(
3373 _metadata,
3374 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
3375 rules);
3376
3377 ASSERT_LE(0, ruleset_fd);
3378 enforce_ruleset(_metadata, ruleset_fd);
3379 ASSERT_EQ(0, close(ruleset_fd));
3380
3381 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
3382 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3383 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);
3384 ASSERT_LE(0, reg_fd);
3385 ASSERT_EQ(0, unlink(file1_s1d2));
3386
3387 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);
3388 ASSERT_LE(0, proc_fd);
3389 ASSERT_EQ(0, close(proc_fd));
3390
3391 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);
3392 ASSERT_EQ(-1, proc_fd)
3393 {
3394 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,
3395 strerror(errno));
3396 }
3397 ASSERT_EQ(EACCES, errno);
3398
3399 ASSERT_EQ(0, close(reg_fd));
3400 }
3401
TEST_F_FORK(layout1,proc_pipe)3402 TEST_F_FORK(layout1, proc_pipe)
3403 {
3404 int proc_fd;
3405 int pipe_fds[2];
3406 char buf = '\0';
3407 const struct rule rules[] = {
3408 {
3409 .path = dir_s1d2,
3410 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3411 LANDLOCK_ACCESS_FS_WRITE_FILE,
3412 },
3413 {},
3414 };
3415 /* Limits read and write access to files tied to the filesystem. */
3416 const int ruleset_fd =
3417 create_ruleset(_metadata, rules[0].access, rules);
3418
3419 ASSERT_LE(0, ruleset_fd);
3420 enforce_ruleset(_metadata, ruleset_fd);
3421 ASSERT_EQ(0, close(ruleset_fd));
3422
3423 /* Checks enforcement for normal files. */
3424 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
3425 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
3426
3427 /* Checks access to pipes through FD. */
3428 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));
3429 ASSERT_EQ(1, write(pipe_fds[1], ".", 1))
3430 {
3431 TH_LOG("Failed to write in pipe: %s", strerror(errno));
3432 }
3433 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));
3434 ASSERT_EQ('.', buf);
3435
3436 /* Checks write access to pipe through /proc/self/fd . */
3437 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);
3438 ASSERT_LE(0, proc_fd);
3439 ASSERT_EQ(1, write(proc_fd, ".", 1))
3440 {
3441 TH_LOG("Failed to write through /proc/self/fd/%d: %s",
3442 pipe_fds[1], strerror(errno));
3443 }
3444 ASSERT_EQ(0, close(proc_fd));
3445
3446 /* Checks read access to pipe through /proc/self/fd . */
3447 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);
3448 ASSERT_LE(0, proc_fd);
3449 buf = '\0';
3450 ASSERT_EQ(1, read(proc_fd, &buf, 1))
3451 {
3452 TH_LOG("Failed to read through /proc/self/fd/%d: %s",
3453 pipe_fds[1], strerror(errno));
3454 }
3455 ASSERT_EQ(0, close(proc_fd));
3456
3457 ASSERT_EQ(0, close(pipe_fds[0]));
3458 ASSERT_EQ(0, close(pipe_fds[1]));
3459 }
3460
3461 /* Invokes truncate(2) and returns its errno or 0. */
test_truncate(const char * const path)3462 static int test_truncate(const char *const path)
3463 {
3464 if (truncate(path, 10) < 0)
3465 return errno;
3466 return 0;
3467 }
3468
3469 /*
3470 * Invokes creat(2) and returns its errno or 0.
3471 * Closes the opened file descriptor on success.
3472 */
test_creat(const char * const path)3473 static int test_creat(const char *const path)
3474 {
3475 int fd = creat(path, 0600);
3476
3477 if (fd < 0)
3478 return errno;
3479
3480 /*
3481 * Mixing error codes from close(2) and creat(2) should not lead to any
3482 * (access type) confusion for this test.
3483 */
3484 if (close(fd) < 0)
3485 return errno;
3486 return 0;
3487 }
3488
3489 /*
3490 * Exercises file truncation when it's not restricted,
3491 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.
3492 */
TEST_F_FORK(layout1,truncate_unhandled)3493 TEST_F_FORK(layout1, truncate_unhandled)
3494 {
3495 const char *const file_r = file1_s1d1;
3496 const char *const file_w = file2_s1d1;
3497 const char *const file_none = file1_s1d2;
3498 const struct rule rules[] = {
3499 {
3500 .path = file_r,
3501 .access = LANDLOCK_ACCESS_FS_READ_FILE,
3502 },
3503 {
3504 .path = file_w,
3505 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3506 },
3507 /* Implicitly: No rights for file_none. */
3508 {},
3509 };
3510
3511 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3512 LANDLOCK_ACCESS_FS_WRITE_FILE;
3513 int ruleset_fd;
3514
3515 /* Enables Landlock. */
3516 ruleset_fd = create_ruleset(_metadata, handled, rules);
3517
3518 ASSERT_LE(0, ruleset_fd);
3519 enforce_ruleset(_metadata, ruleset_fd);
3520 ASSERT_EQ(0, close(ruleset_fd));
3521
3522 /*
3523 * Checks read right: truncate and open with O_TRUNC work, unless the
3524 * file is attempted to be opened for writing.
3525 */
3526 EXPECT_EQ(0, test_truncate(file_r));
3527 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));
3528 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));
3529 EXPECT_EQ(EACCES, test_creat(file_r));
3530
3531 /*
3532 * Checks write right: truncate and open with O_TRUNC work, unless the
3533 * file is attempted to be opened for reading.
3534 */
3535 EXPECT_EQ(0, test_truncate(file_w));
3536 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));
3537 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));
3538 EXPECT_EQ(0, test_creat(file_w));
3539
3540 /*
3541 * Checks "no rights" case: truncate works but all open attempts fail,
3542 * including creat.
3543 */
3544 EXPECT_EQ(0, test_truncate(file_none));
3545 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3546 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3547 EXPECT_EQ(EACCES, test_creat(file_none));
3548 }
3549
TEST_F_FORK(layout1,truncate)3550 TEST_F_FORK(layout1, truncate)
3551 {
3552 const char *const file_rwt = file1_s1d1;
3553 const char *const file_rw = file2_s1d1;
3554 const char *const file_rt = file1_s1d2;
3555 const char *const file_t = file2_s1d2;
3556 const char *const file_none = file1_s1d3;
3557 const char *const dir_t = dir_s2d1;
3558 const char *const file_in_dir_t = file1_s2d1;
3559 const char *const dir_w = dir_s3d1;
3560 const char *const file_in_dir_w = file1_s3d1;
3561 const struct rule rules[] = {
3562 {
3563 .path = file_rwt,
3564 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3565 LANDLOCK_ACCESS_FS_WRITE_FILE |
3566 LANDLOCK_ACCESS_FS_TRUNCATE,
3567 },
3568 {
3569 .path = file_rw,
3570 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3571 LANDLOCK_ACCESS_FS_WRITE_FILE,
3572 },
3573 {
3574 .path = file_rt,
3575 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3576 LANDLOCK_ACCESS_FS_TRUNCATE,
3577 },
3578 {
3579 .path = file_t,
3580 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3581 },
3582 /* Implicitly: No access rights for file_none. */
3583 {
3584 .path = dir_t,
3585 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3586 },
3587 {
3588 .path = dir_w,
3589 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3590 },
3591 {},
3592 };
3593 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3594 LANDLOCK_ACCESS_FS_WRITE_FILE |
3595 LANDLOCK_ACCESS_FS_TRUNCATE;
3596 int ruleset_fd;
3597
3598 /* Enables Landlock. */
3599 ruleset_fd = create_ruleset(_metadata, handled, rules);
3600
3601 ASSERT_LE(0, ruleset_fd);
3602 enforce_ruleset(_metadata, ruleset_fd);
3603 ASSERT_EQ(0, close(ruleset_fd));
3604
3605 /* Checks read, write and truncate rights: truncation works. */
3606 EXPECT_EQ(0, test_truncate(file_rwt));
3607 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));
3608 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));
3609
3610 /* Checks read and write rights: no truncate variant works. */
3611 EXPECT_EQ(EACCES, test_truncate(file_rw));
3612 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));
3613 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));
3614
3615 /*
3616 * Checks read and truncate rights: truncation works.
3617 *
3618 * Note: Files can get truncated using open() even with O_RDONLY.
3619 */
3620 EXPECT_EQ(0, test_truncate(file_rt));
3621 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));
3622 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));
3623
3624 /* Checks truncate right: truncate works, but can't open file. */
3625 EXPECT_EQ(0, test_truncate(file_t));
3626 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));
3627 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));
3628
3629 /* Checks "no rights" case: No form of truncation works. */
3630 EXPECT_EQ(EACCES, test_truncate(file_none));
3631 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3632 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3633
3634 /*
3635 * Checks truncate right on directory: truncate works on contained
3636 * files.
3637 */
3638 EXPECT_EQ(0, test_truncate(file_in_dir_t));
3639 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));
3640 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));
3641
3642 /*
3643 * Checks creat in dir_w: This requires the truncate right when
3644 * overwriting an existing file, but does not require it when the file
3645 * is new.
3646 */
3647 EXPECT_EQ(EACCES, test_creat(file_in_dir_w));
3648
3649 ASSERT_EQ(0, unlink(file_in_dir_w));
3650 EXPECT_EQ(0, test_creat(file_in_dir_w));
3651 }
3652
3653 /* Invokes ftruncate(2) and returns its errno or 0. */
test_ftruncate(int fd)3654 static int test_ftruncate(int fd)
3655 {
3656 if (ftruncate(fd, 10) < 0)
3657 return errno;
3658 return 0;
3659 }
3660
TEST_F_FORK(layout1,ftruncate)3661 TEST_F_FORK(layout1, ftruncate)
3662 {
3663 /*
3664 * This test opens a new file descriptor at different stages of
3665 * Landlock restriction:
3666 *
3667 * without restriction: ftruncate works
3668 * something else but truncate restricted: ftruncate works
3669 * truncate restricted and permitted: ftruncate works
3670 * truncate restricted and not permitted: ftruncate fails
3671 *
3672 * Whether this works or not is expected to depend on the time when the
3673 * FD was opened, not to depend on the time when ftruncate() was
3674 * called.
3675 */
3676 const char *const path = file1_s1d1;
3677 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |
3678 LANDLOCK_ACCESS_FS_WRITE_FILE;
3679 const struct rule layer1[] = {
3680 {
3681 .path = path,
3682 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3683 },
3684 {},
3685 };
3686 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;
3687 const struct rule layer2[] = {
3688 {
3689 .path = path,
3690 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3691 },
3692 {},
3693 };
3694 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |
3695 LANDLOCK_ACCESS_FS_WRITE_FILE;
3696 const struct rule layer3[] = {
3697 {
3698 .path = path,
3699 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3700 },
3701 {},
3702 };
3703 int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd;
3704
3705 fd_layer0 = open(path, O_WRONLY);
3706 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3707
3708 ruleset_fd = create_ruleset(_metadata, handled1, layer1);
3709 ASSERT_LE(0, ruleset_fd);
3710 enforce_ruleset(_metadata, ruleset_fd);
3711 ASSERT_EQ(0, close(ruleset_fd));
3712
3713 fd_layer1 = open(path, O_WRONLY);
3714 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3715 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3716
3717 ruleset_fd = create_ruleset(_metadata, handled2, layer2);
3718 ASSERT_LE(0, ruleset_fd);
3719 enforce_ruleset(_metadata, ruleset_fd);
3720 ASSERT_EQ(0, close(ruleset_fd));
3721
3722 fd_layer2 = open(path, O_WRONLY);
3723 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3724 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3725 EXPECT_EQ(0, test_ftruncate(fd_layer2));
3726
3727 ruleset_fd = create_ruleset(_metadata, handled3, layer3);
3728 ASSERT_LE(0, ruleset_fd);
3729 enforce_ruleset(_metadata, ruleset_fd);
3730 ASSERT_EQ(0, close(ruleset_fd));
3731
3732 fd_layer3 = open(path, O_WRONLY);
3733 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3734 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3735 EXPECT_EQ(0, test_ftruncate(fd_layer2));
3736 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));
3737
3738 ASSERT_EQ(0, close(fd_layer0));
3739 ASSERT_EQ(0, close(fd_layer1));
3740 ASSERT_EQ(0, close(fd_layer2));
3741 ASSERT_EQ(0, close(fd_layer3));
3742 }
3743
3744 /* clang-format off */
FIXTURE(ftruncate)3745 FIXTURE(ftruncate) {};
3746 /* clang-format on */
3747
FIXTURE_SETUP(ftruncate)3748 FIXTURE_SETUP(ftruncate)
3749 {
3750 prepare_layout(_metadata);
3751 create_file(_metadata, file1_s1d1);
3752 }
3753
FIXTURE_TEARDOWN_PARENT(ftruncate)3754 FIXTURE_TEARDOWN_PARENT(ftruncate)
3755 {
3756 EXPECT_EQ(0, remove_path(file1_s1d1));
3757 cleanup_layout(_metadata);
3758 }
3759
FIXTURE_VARIANT(ftruncate)3760 FIXTURE_VARIANT(ftruncate)
3761 {
3762 const __u64 handled;
3763 const __u64 allowed;
3764 const int expected_open_result;
3765 const int expected_ftruncate_result;
3766 };
3767
3768 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,w_w)3769 FIXTURE_VARIANT_ADD(ftruncate, w_w) {
3770 /* clang-format on */
3771 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE,
3772 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
3773 .expected_open_result = 0,
3774 .expected_ftruncate_result = 0,
3775 };
3776
3777 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,t_t)3778 FIXTURE_VARIANT_ADD(ftruncate, t_t) {
3779 /* clang-format on */
3780 .handled = LANDLOCK_ACCESS_FS_TRUNCATE,
3781 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,
3782 .expected_open_result = 0,
3783 .expected_ftruncate_result = 0,
3784 };
3785
3786 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_w)3787 FIXTURE_VARIANT_ADD(ftruncate, wt_w) {
3788 /* clang-format on */
3789 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3790 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
3791 .expected_open_result = 0,
3792 .expected_ftruncate_result = EACCES,
3793 };
3794
3795 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_wt)3796 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) {
3797 /* clang-format on */
3798 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3799 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3800 .expected_open_result = 0,
3801 .expected_ftruncate_result = 0,
3802 };
3803
3804 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_t)3805 FIXTURE_VARIANT_ADD(ftruncate, wt_t) {
3806 /* clang-format on */
3807 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3808 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,
3809 .expected_open_result = EACCES,
3810 };
3811
TEST_F_FORK(ftruncate,open_and_ftruncate)3812 TEST_F_FORK(ftruncate, open_and_ftruncate)
3813 {
3814 const char *const path = file1_s1d1;
3815 const struct rule rules[] = {
3816 {
3817 .path = path,
3818 .access = variant->allowed,
3819 },
3820 {},
3821 };
3822 int fd, ruleset_fd;
3823
3824 /* Enables Landlock. */
3825 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
3826 ASSERT_LE(0, ruleset_fd);
3827 enforce_ruleset(_metadata, ruleset_fd);
3828 ASSERT_EQ(0, close(ruleset_fd));
3829
3830 fd = open(path, O_WRONLY);
3831 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
3832 if (fd >= 0) {
3833 EXPECT_EQ(variant->expected_ftruncate_result,
3834 test_ftruncate(fd));
3835 ASSERT_EQ(0, close(fd));
3836 }
3837 }
3838
TEST_F_FORK(ftruncate,open_and_ftruncate_in_different_processes)3839 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
3840 {
3841 int child, fd, status;
3842 int socket_fds[2];
3843
3844 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
3845 socket_fds));
3846
3847 child = fork();
3848 ASSERT_LE(0, child);
3849 if (child == 0) {
3850 /*
3851 * Enables Landlock in the child process, open a file descriptor
3852 * where truncation is forbidden and send it to the
3853 * non-landlocked parent process.
3854 */
3855 const char *const path = file1_s1d1;
3856 const struct rule rules[] = {
3857 {
3858 .path = path,
3859 .access = variant->allowed,
3860 },
3861 {},
3862 };
3863 int fd, ruleset_fd;
3864
3865 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
3866 ASSERT_LE(0, ruleset_fd);
3867 enforce_ruleset(_metadata, ruleset_fd);
3868 ASSERT_EQ(0, close(ruleset_fd));
3869
3870 fd = open(path, O_WRONLY);
3871 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
3872
3873 if (fd >= 0) {
3874 ASSERT_EQ(0, send_fd(socket_fds[0], fd));
3875 ASSERT_EQ(0, close(fd));
3876 }
3877
3878 ASSERT_EQ(0, close(socket_fds[0]));
3879
3880 _exit(_metadata->exit_code);
3881 return;
3882 }
3883
3884 if (variant->expected_open_result == 0) {
3885 fd = recv_fd(socket_fds[1]);
3886 ASSERT_LE(0, fd);
3887
3888 EXPECT_EQ(variant->expected_ftruncate_result,
3889 test_ftruncate(fd));
3890 ASSERT_EQ(0, close(fd));
3891 }
3892
3893 ASSERT_EQ(child, waitpid(child, &status, 0));
3894 ASSERT_EQ(1, WIFEXITED(status));
3895 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
3896
3897 ASSERT_EQ(0, close(socket_fds[0]));
3898 ASSERT_EQ(0, close(socket_fds[1]));
3899 }
3900
3901 /* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */
test_fs_ioc_getflags_ioctl(int fd)3902 static int test_fs_ioc_getflags_ioctl(int fd)
3903 {
3904 uint32_t flags;
3905
3906 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)
3907 return errno;
3908 return 0;
3909 }
3910
TEST(memfd_ftruncate_and_ioctl)3911 TEST(memfd_ftruncate_and_ioctl)
3912 {
3913 const struct landlock_ruleset_attr attr = {
3914 .handled_access_fs = ACCESS_ALL,
3915 };
3916 int ruleset_fd, fd, i;
3917
3918 /*
3919 * We exercise the same test both with and without Landlock enabled, to
3920 * ensure that it behaves the same in both cases.
3921 */
3922 for (i = 0; i < 2; i++) {
3923 /* Creates a new memfd. */
3924 fd = memfd_create("name", MFD_CLOEXEC);
3925 ASSERT_LE(0, fd);
3926
3927 /*
3928 * Checks that operations associated with the opened file
3929 * (ftruncate, ioctl) are permitted on file descriptors that are
3930 * created in ways other than open(2).
3931 */
3932 EXPECT_EQ(0, test_ftruncate(fd));
3933 EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd));
3934
3935 ASSERT_EQ(0, close(fd));
3936
3937 /* Enables Landlock. */
3938 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
3939 ASSERT_LE(0, ruleset_fd);
3940 enforce_ruleset(_metadata, ruleset_fd);
3941 ASSERT_EQ(0, close(ruleset_fd));
3942 }
3943 }
3944
test_fionread_ioctl(int fd)3945 static int test_fionread_ioctl(int fd)
3946 {
3947 size_t sz = 0;
3948
3949 if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES)
3950 return errno;
3951 return 0;
3952 }
3953
TEST_F_FORK(layout1,o_path_ftruncate_and_ioctl)3954 TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)
3955 {
3956 const struct landlock_ruleset_attr attr = {
3957 .handled_access_fs = ACCESS_ALL,
3958 };
3959 int ruleset_fd, fd;
3960
3961 /*
3962 * Checks that for files opened with O_PATH, both ioctl(2) and
3963 * ftruncate(2) yield EBADF, as it is documented in open(2) for the
3964 * O_PATH flag.
3965 */
3966 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);
3967 ASSERT_LE(0, fd);
3968
3969 EXPECT_EQ(EBADF, test_ftruncate(fd));
3970 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));
3971
3972 ASSERT_EQ(0, close(fd));
3973
3974 /* Enables Landlock. */
3975 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
3976 ASSERT_LE(0, ruleset_fd);
3977 enforce_ruleset(_metadata, ruleset_fd);
3978 ASSERT_EQ(0, close(ruleset_fd));
3979
3980 /*
3981 * Checks that after enabling Landlock,
3982 * - the file can still be opened with O_PATH
3983 * - both ioctl and truncate still yield EBADF (not EACCES).
3984 */
3985 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);
3986 ASSERT_LE(0, fd);
3987
3988 EXPECT_EQ(EBADF, test_ftruncate(fd));
3989 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));
3990
3991 ASSERT_EQ(0, close(fd));
3992 }
3993
3994 /*
3995 * ioctl_error - generically call the given ioctl with a pointer to a
3996 * sufficiently large zeroed-out memory region.
3997 *
3998 * Returns the IOCTLs error, or 0.
3999 */
ioctl_error(struct __test_metadata * const _metadata,int fd,unsigned int cmd)4000 static int ioctl_error(struct __test_metadata *const _metadata, int fd,
4001 unsigned int cmd)
4002 {
4003 char buf[128]; /* sufficiently large */
4004 int res, stdinbak_fd;
4005
4006 /*
4007 * Depending on the IOCTL command, parts of the zeroed-out buffer might
4008 * be interpreted as file descriptor numbers. We do not want to
4009 * accidentally operate on file descriptor 0 (stdin), so we temporarily
4010 * move stdin to a different FD and close FD 0 for the IOCTL call.
4011 */
4012 stdinbak_fd = dup(0);
4013 ASSERT_LT(0, stdinbak_fd);
4014 ASSERT_EQ(0, close(0));
4015
4016 /* Invokes the IOCTL with a zeroed-out buffer. */
4017 bzero(&buf, sizeof(buf));
4018 res = ioctl(fd, cmd, &buf);
4019
4020 /* Restores the old FD 0 and closes the backup FD. */
4021 ASSERT_EQ(0, dup2(stdinbak_fd, 0));
4022 ASSERT_EQ(0, close(stdinbak_fd));
4023
4024 if (res < 0)
4025 return errno;
4026
4027 return 0;
4028 }
4029
4030 /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */
4031 struct space_resv {
4032 __s16 l_type;
4033 __s16 l_whence;
4034 __s64 l_start;
4035 __s64 l_len; /* len == 0 means until end of file */
4036 __s32 l_sysid;
4037 __u32 l_pid;
4038 __s32 l_pad[4]; /* reserved area */
4039 };
4040
4041 #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv)
4042 #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv)
4043 #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv)
4044 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv)
4045 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv)
4046
4047 /*
4048 * Tests a series of blanket-permitted and denied IOCTLs.
4049 */
TEST_F_FORK(layout1,blanket_permitted_ioctls)4050 TEST_F_FORK(layout1, blanket_permitted_ioctls)
4051 {
4052 const struct landlock_ruleset_attr attr = {
4053 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4054 };
4055 int ruleset_fd, fd;
4056
4057 /* Enables Landlock. */
4058 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4059 ASSERT_LE(0, ruleset_fd);
4060 enforce_ruleset(_metadata, ruleset_fd);
4061 ASSERT_EQ(0, close(ruleset_fd));
4062
4063 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
4064 ASSERT_LE(0, fd);
4065
4066 /*
4067 * Checks permitted commands.
4068 * These ones may return errors, but should not be blocked by Landlock.
4069 */
4070 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX));
4071 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX));
4072 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO));
4073 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC));
4074 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE));
4075 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE));
4076 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW));
4077 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP));
4078 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ));
4079 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE));
4080 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE));
4081 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE));
4082 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID));
4083 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH));
4084
4085 /*
4086 * Checks blocked commands.
4087 * A call to a blocked IOCTL command always returns EACCES.
4088 */
4089 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));
4090 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS));
4091 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS));
4092 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR));
4093 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR));
4094 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP));
4095 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP));
4096 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64));
4097 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP));
4098 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64));
4099 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE));
4100
4101 /* Default case is also blocked. */
4102 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee));
4103
4104 ASSERT_EQ(0, close(fd));
4105 }
4106
4107 /*
4108 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right,
4109 * because they are not character or block devices.
4110 */
TEST_F_FORK(layout1,named_pipe_ioctl)4111 TEST_F_FORK(layout1, named_pipe_ioctl)
4112 {
4113 pid_t child_pid;
4114 int fd, ruleset_fd;
4115 const char *const path = file1_s1d1;
4116 const struct landlock_ruleset_attr attr = {
4117 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4118 };
4119
4120 ASSERT_EQ(0, unlink(path));
4121 ASSERT_EQ(0, mkfifo(path, 0600));
4122
4123 /* Enables Landlock. */
4124 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4125 ASSERT_LE(0, ruleset_fd);
4126 enforce_ruleset(_metadata, ruleset_fd);
4127 ASSERT_EQ(0, close(ruleset_fd));
4128
4129 /* The child process opens the pipe for writing. */
4130 child_pid = fork();
4131 ASSERT_NE(-1, child_pid);
4132 if (child_pid == 0) {
4133 fd = open(path, O_WRONLY);
4134 close(fd);
4135 exit(0);
4136 }
4137
4138 fd = open(path, O_RDONLY);
4139 ASSERT_LE(0, fd);
4140
4141 /* FIONREAD is implemented by pipefifo_fops. */
4142 EXPECT_EQ(0, test_fionread_ioctl(fd));
4143
4144 ASSERT_EQ(0, close(fd));
4145 ASSERT_EQ(0, unlink(path));
4146
4147 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0));
4148 }
4149
4150 /* For named UNIX domain sockets, no IOCTL restrictions apply. */
TEST_F_FORK(layout1,named_unix_domain_socket_ioctl)4151 TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
4152 {
4153 const char *const path = file1_s1d1;
4154 int srv_fd, cli_fd, ruleset_fd;
4155 socklen_t size;
4156 struct sockaddr_un srv_un, cli_un;
4157 const struct landlock_ruleset_attr attr = {
4158 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4159 };
4160
4161 /* Sets up a server */
4162 srv_un.sun_family = AF_UNIX;
4163 strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));
4164
4165 ASSERT_EQ(0, unlink(path));
4166 srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
4167 ASSERT_LE(0, srv_fd);
4168
4169 size = offsetof(struct sockaddr_un, sun_path) + strlen(srv_un.sun_path);
4170 ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, size));
4171 ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */));
4172
4173 /* Enables Landlock. */
4174 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4175 ASSERT_LE(0, ruleset_fd);
4176 enforce_ruleset(_metadata, ruleset_fd);
4177 ASSERT_EQ(0, close(ruleset_fd));
4178
4179 /* Sets up a client connection to it */
4180 cli_un.sun_family = AF_UNIX;
4181 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
4182 ASSERT_LE(0, cli_fd);
4183
4184 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);
4185 ASSERT_EQ(0, bind(cli_fd, (struct sockaddr *)&cli_un, size));
4186
4187 bzero(&cli_un, sizeof(cli_un));
4188 cli_un.sun_family = AF_UNIX;
4189 strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path));
4190 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);
4191
4192 ASSERT_EQ(0, connect(cli_fd, (struct sockaddr *)&cli_un, size));
4193
4194 /* FIONREAD and other IOCTLs should not be forbidden. */
4195 EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
4196
4197 ASSERT_EQ(0, close(cli_fd));
4198 }
4199
4200 /* clang-format off */
FIXTURE(ioctl)4201 FIXTURE(ioctl) {};
4202
FIXTURE_SETUP(ioctl)4203 FIXTURE_SETUP(ioctl) {};
4204
FIXTURE_TEARDOWN(ioctl)4205 FIXTURE_TEARDOWN(ioctl) {};
4206 /* clang-format on */
4207
FIXTURE_VARIANT(ioctl)4208 FIXTURE_VARIANT(ioctl)
4209 {
4210 const __u64 handled;
4211 const __u64 allowed;
4212 const mode_t open_mode;
4213 /*
4214 * FIONREAD is used as a characteristic device-specific IOCTL command.
4215 * It is implemented in fs/ioctl.c for regular files,
4216 * but we do not blanket-permit it for devices.
4217 */
4218 const int expected_fionread_result;
4219 };
4220
4221 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,handled_i_allowed_none)4222 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) {
4223 /* clang-format on */
4224 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4225 .allowed = 0,
4226 .open_mode = O_RDWR,
4227 .expected_fionread_result = EACCES,
4228 };
4229
4230 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,handled_i_allowed_i)4231 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) {
4232 /* clang-format on */
4233 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4234 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4235 .open_mode = O_RDWR,
4236 .expected_fionread_result = 0,
4237 };
4238
4239 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,unhandled)4240 FIXTURE_VARIANT_ADD(ioctl, unhandled) {
4241 /* clang-format on */
4242 .handled = LANDLOCK_ACCESS_FS_EXECUTE,
4243 .allowed = LANDLOCK_ACCESS_FS_EXECUTE,
4244 .open_mode = O_RDWR,
4245 .expected_fionread_result = 0,
4246 };
4247
TEST_F_FORK(ioctl,handle_dir_access_file)4248 TEST_F_FORK(ioctl, handle_dir_access_file)
4249 {
4250 const int flag = 0;
4251 const struct rule rules[] = {
4252 {
4253 .path = "/dev",
4254 .access = variant->allowed,
4255 },
4256 {},
4257 };
4258 int file_fd, ruleset_fd;
4259
4260 /* Enables Landlock. */
4261 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4262 ASSERT_LE(0, ruleset_fd);
4263 enforce_ruleset(_metadata, ruleset_fd);
4264 ASSERT_EQ(0, close(ruleset_fd));
4265
4266 file_fd = open("/dev/zero", variant->open_mode);
4267 ASSERT_LE(0, file_fd);
4268
4269 /* Checks that IOCTL commands return the expected errors. */
4270 EXPECT_EQ(variant->expected_fionread_result,
4271 test_fionread_ioctl(file_fd));
4272
4273 /* Checks that unrestrictable commands are unrestricted. */
4274 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
4275 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
4276 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
4277 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
4278 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));
4279
4280 ASSERT_EQ(0, close(file_fd));
4281 }
4282
TEST_F_FORK(ioctl,handle_dir_access_dir)4283 TEST_F_FORK(ioctl, handle_dir_access_dir)
4284 {
4285 const int flag = 0;
4286 const struct rule rules[] = {
4287 {
4288 .path = "/dev",
4289 .access = variant->allowed,
4290 },
4291 {},
4292 };
4293 int dir_fd, ruleset_fd;
4294
4295 /* Enables Landlock. */
4296 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4297 ASSERT_LE(0, ruleset_fd);
4298 enforce_ruleset(_metadata, ruleset_fd);
4299 ASSERT_EQ(0, close(ruleset_fd));
4300
4301 /*
4302 * Ignore variant->open_mode for this test, as we intend to open a
4303 * directory. If the directory can not be opened, the variant is
4304 * infeasible to test with an opened directory.
4305 */
4306 dir_fd = open("/dev", O_RDONLY);
4307 if (dir_fd < 0)
4308 return;
4309
4310 /*
4311 * Checks that IOCTL commands return the expected errors.
4312 * We do not use the expected values from the fixture here.
4313 *
4314 * When using IOCTL on a directory, no Landlock restrictions apply.
4315 */
4316 EXPECT_EQ(0, test_fionread_ioctl(dir_fd));
4317
4318 /* Checks that unrestrictable commands are unrestricted. */
4319 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX));
4320 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX));
4321 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag));
4322 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag));
4323 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag));
4324
4325 ASSERT_EQ(0, close(dir_fd));
4326 }
4327
TEST_F_FORK(ioctl,handle_file_access_file)4328 TEST_F_FORK(ioctl, handle_file_access_file)
4329 {
4330 const int flag = 0;
4331 const struct rule rules[] = {
4332 {
4333 .path = "/dev/zero",
4334 .access = variant->allowed,
4335 },
4336 {},
4337 };
4338 int file_fd, ruleset_fd;
4339
4340 /* Enables Landlock. */
4341 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4342 ASSERT_LE(0, ruleset_fd);
4343 enforce_ruleset(_metadata, ruleset_fd);
4344 ASSERT_EQ(0, close(ruleset_fd));
4345
4346 file_fd = open("/dev/zero", variant->open_mode);
4347 ASSERT_LE(0, file_fd)
4348 {
4349 TH_LOG("Failed to open /dev/zero: %s", strerror(errno));
4350 }
4351
4352 /* Checks that IOCTL commands return the expected errors. */
4353 EXPECT_EQ(variant->expected_fionread_result,
4354 test_fionread_ioctl(file_fd));
4355
4356 /* Checks that unrestrictable commands are unrestricted. */
4357 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
4358 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
4359 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
4360 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
4361 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));
4362
4363 ASSERT_EQ(0, close(file_fd));
4364 }
4365
4366 /* clang-format off */
FIXTURE(layout1_bind)4367 FIXTURE(layout1_bind) {};
4368 /* clang-format on */
4369
FIXTURE_SETUP(layout1_bind)4370 FIXTURE_SETUP(layout1_bind)
4371 {
4372 prepare_layout(_metadata);
4373
4374 create_layout1(_metadata);
4375
4376 set_cap(_metadata, CAP_SYS_ADMIN);
4377 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
4378 clear_cap(_metadata, CAP_SYS_ADMIN);
4379 }
4380
FIXTURE_TEARDOWN_PARENT(layout1_bind)4381 FIXTURE_TEARDOWN_PARENT(layout1_bind)
4382 {
4383 /* umount(dir_s2d2)) is handled by namespace lifetime. */
4384
4385 remove_layout1(_metadata);
4386
4387 cleanup_layout(_metadata);
4388 }
4389
4390 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
4391 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";
4392
4393 /*
4394 * layout1_bind hierarchy:
4395 *
4396 * tmp
4397 * ├── s1d1
4398 * │ ├── f1
4399 * │ ├── f2
4400 * │ └── s1d2
4401 * │ ├── f1
4402 * │ ├── f2
4403 * │ └── s1d3
4404 * │ ├── f1
4405 * │ └── f2
4406 * ├── s2d1
4407 * │ ├── f1
4408 * │ └── s2d2
4409 * │ ├── f1
4410 * │ ├── f2
4411 * │ └── s1d3
4412 * │ ├── f1
4413 * │ └── f2
4414 * └── s3d1
4415 * └── s3d2
4416 * └── s3d3
4417 */
4418
TEST_F_FORK(layout1_bind,no_restriction)4419 TEST_F_FORK(layout1_bind, no_restriction)
4420 {
4421 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
4422 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
4423 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
4424 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
4425 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
4426 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
4427
4428 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
4429 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
4430 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
4431 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
4432 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));
4433 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));
4434
4435 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));
4436 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
4437
4438 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
4439 }
4440
TEST_F_FORK(layout1_bind,same_content_same_file)4441 TEST_F_FORK(layout1_bind, same_content_same_file)
4442 {
4443 /*
4444 * Sets access right on parent directories of both source and
4445 * destination mount points.
4446 */
4447 const struct rule layer1_parent[] = {
4448 {
4449 .path = dir_s1d1,
4450 .access = ACCESS_RO,
4451 },
4452 {
4453 .path = dir_s2d1,
4454 .access = ACCESS_RW,
4455 },
4456 {},
4457 };
4458 /*
4459 * Sets access rights on the same bind-mounted directories. The result
4460 * should be ACCESS_RW for both directories, but not both hierarchies
4461 * because of the first layer.
4462 */
4463 const struct rule layer2_mount_point[] = {
4464 {
4465 .path = dir_s1d2,
4466 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4467 },
4468 {
4469 .path = dir_s2d2,
4470 .access = ACCESS_RW,
4471 },
4472 {},
4473 };
4474 /* Only allow read-access to the s1d3 hierarchies. */
4475 const struct rule layer3_source[] = {
4476 {
4477 .path = dir_s1d3,
4478 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4479 },
4480 {},
4481 };
4482 /* Removes all access rights. */
4483 const struct rule layer4_destination[] = {
4484 {
4485 .path = bind_file1_s1d3,
4486 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
4487 },
4488 {},
4489 };
4490 int ruleset_fd;
4491
4492 /* Sets rules for the parent directories. */
4493 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);
4494 ASSERT_LE(0, ruleset_fd);
4495 enforce_ruleset(_metadata, ruleset_fd);
4496 ASSERT_EQ(0, close(ruleset_fd));
4497
4498 /* Checks source hierarchy. */
4499 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
4500 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
4501 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
4502
4503 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
4504 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
4505 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
4506
4507 /* Checks destination hierarchy. */
4508 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));
4509 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
4510
4511 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
4512 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
4513
4514 /* Sets rules for the mount points. */
4515 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);
4516 ASSERT_LE(0, ruleset_fd);
4517 enforce_ruleset(_metadata, ruleset_fd);
4518 ASSERT_EQ(0, close(ruleset_fd));
4519
4520 /* Checks source hierarchy. */
4521 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
4522 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
4523 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
4524
4525 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
4526 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
4527 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
4528
4529 /* Checks destination hierarchy. */
4530 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));
4531 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));
4532 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
4533
4534 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
4535 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
4536 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
4537
4538 /* Sets a (shared) rule only on the source. */
4539 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);
4540 ASSERT_LE(0, ruleset_fd);
4541 enforce_ruleset(_metadata, ruleset_fd);
4542 ASSERT_EQ(0, close(ruleset_fd));
4543
4544 /* Checks source hierarchy. */
4545 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
4546 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
4547 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
4548
4549 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
4550 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
4551 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
4552
4553 /* Checks destination hierarchy. */
4554 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));
4555 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));
4556 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
4557
4558 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
4559 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
4560 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
4561
4562 /* Sets a (shared) rule only on the destination. */
4563 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);
4564 ASSERT_LE(0, ruleset_fd);
4565 enforce_ruleset(_metadata, ruleset_fd);
4566 ASSERT_EQ(0, close(ruleset_fd));
4567
4568 /* Checks source hierarchy. */
4569 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
4570 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
4571
4572 /* Checks destination hierarchy. */
4573 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));
4574 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
4575 }
4576
TEST_F_FORK(layout1_bind,reparent_cross_mount)4577 TEST_F_FORK(layout1_bind, reparent_cross_mount)
4578 {
4579 const struct rule layer1[] = {
4580 {
4581 /* dir_s2d1 is beneath the dir_s2d2 mount point. */
4582 .path = dir_s2d1,
4583 .access = LANDLOCK_ACCESS_FS_REFER,
4584 },
4585 {
4586 .path = bind_dir_s1d3,
4587 .access = LANDLOCK_ACCESS_FS_EXECUTE,
4588 },
4589 {},
4590 };
4591 int ruleset_fd = create_ruleset(
4592 _metadata,
4593 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1);
4594
4595 ASSERT_LE(0, ruleset_fd);
4596 enforce_ruleset(_metadata, ruleset_fd);
4597 ASSERT_EQ(0, close(ruleset_fd));
4598
4599 /* Checks basic denied move. */
4600 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));
4601 ASSERT_EQ(EXDEV, errno);
4602
4603 /* Checks real cross-mount move (Landlock is not involved). */
4604 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));
4605 ASSERT_EQ(EXDEV, errno);
4606
4607 /* Checks move that will give more accesses. */
4608 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));
4609 ASSERT_EQ(EXDEV, errno);
4610
4611 /* Checks legitimate downgrade move. */
4612 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));
4613 }
4614
4615 #define LOWER_BASE TMP_DIR "/lower"
4616 #define LOWER_DATA LOWER_BASE "/data"
4617 static const char lower_fl1[] = LOWER_DATA "/fl1";
4618 static const char lower_dl1[] = LOWER_DATA "/dl1";
4619 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
4620 static const char lower_fo1[] = LOWER_DATA "/fo1";
4621 static const char lower_do1[] = LOWER_DATA "/do1";
4622 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
4623 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
4624
4625 static const char (*lower_base_files[])[] = {
4626 &lower_fl1,
4627 &lower_fo1,
4628 NULL,
4629 };
4630 static const char (*lower_base_directories[])[] = {
4631 &lower_dl1,
4632 &lower_do1,
4633 NULL,
4634 };
4635 static const char (*lower_sub_files[])[] = {
4636 &lower_dl1_fl2,
4637 &lower_do1_fo2,
4638 &lower_do1_fl3,
4639 NULL,
4640 };
4641
4642 #define UPPER_BASE TMP_DIR "/upper"
4643 #define UPPER_DATA UPPER_BASE "/data"
4644 #define UPPER_WORK UPPER_BASE "/work"
4645 static const char upper_fu1[] = UPPER_DATA "/fu1";
4646 static const char upper_du1[] = UPPER_DATA "/du1";
4647 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
4648 static const char upper_fo1[] = UPPER_DATA "/fo1";
4649 static const char upper_do1[] = UPPER_DATA "/do1";
4650 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
4651 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
4652
4653 static const char (*upper_base_files[])[] = {
4654 &upper_fu1,
4655 &upper_fo1,
4656 NULL,
4657 };
4658 static const char (*upper_base_directories[])[] = {
4659 &upper_du1,
4660 &upper_do1,
4661 NULL,
4662 };
4663 static const char (*upper_sub_files[])[] = {
4664 &upper_du1_fu2,
4665 &upper_do1_fo2,
4666 &upper_do1_fu3,
4667 NULL,
4668 };
4669
4670 #define MERGE_BASE TMP_DIR "/merge"
4671 #define MERGE_DATA MERGE_BASE "/data"
4672 static const char merge_fl1[] = MERGE_DATA "/fl1";
4673 static const char merge_dl1[] = MERGE_DATA "/dl1";
4674 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
4675 static const char merge_fu1[] = MERGE_DATA "/fu1";
4676 static const char merge_du1[] = MERGE_DATA "/du1";
4677 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
4678 static const char merge_fo1[] = MERGE_DATA "/fo1";
4679 static const char merge_do1[] = MERGE_DATA "/do1";
4680 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
4681 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
4682 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
4683
4684 static const char (*merge_base_files[])[] = {
4685 &merge_fl1,
4686 &merge_fu1,
4687 &merge_fo1,
4688 NULL,
4689 };
4690 static const char (*merge_base_directories[])[] = {
4691 &merge_dl1,
4692 &merge_du1,
4693 &merge_do1,
4694 NULL,
4695 };
4696 static const char (*merge_sub_files[])[] = {
4697 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,
4698 &merge_do1_fl3, &merge_do1_fu3, NULL,
4699 };
4700
4701 /*
4702 * layout2_overlay hierarchy:
4703 *
4704 * tmp
4705 * ├── lower
4706 * │ └── data
4707 * │ ├── dl1
4708 * │ │ └── fl2
4709 * │ ├── do1
4710 * │ │ ├── fl3
4711 * │ │ └── fo2
4712 * │ ├── fl1
4713 * │ └── fo1
4714 * ├── merge
4715 * │ └── data
4716 * │ ├── dl1
4717 * │ │ └── fl2
4718 * │ ├── do1
4719 * │ │ ├── fl3
4720 * │ │ ├── fo2
4721 * │ │ └── fu3
4722 * │ ├── du1
4723 * │ │ └── fu2
4724 * │ ├── fl1
4725 * │ ├── fo1
4726 * │ └── fu1
4727 * └── upper
4728 * ├── data
4729 * │ ├── do1
4730 * │ │ ├── fo2
4731 * │ │ └── fu3
4732 * │ ├── du1
4733 * │ │ └── fu2
4734 * │ ├── fo1
4735 * │ └── fu1
4736 * └── work
4737 * └── work
4738 */
4739
FIXTURE(layout2_overlay)4740 FIXTURE(layout2_overlay)
4741 {
4742 bool skip_test;
4743 };
4744
FIXTURE_SETUP(layout2_overlay)4745 FIXTURE_SETUP(layout2_overlay)
4746 {
4747 if (!supports_filesystem("overlay")) {
4748 self->skip_test = true;
4749 SKIP(return, "overlayfs is not supported (setup)");
4750 }
4751
4752 prepare_layout(_metadata);
4753
4754 create_directory(_metadata, LOWER_BASE);
4755 set_cap(_metadata, CAP_SYS_ADMIN);
4756 /* Creates tmpfs mount points to get deterministic overlayfs. */
4757 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));
4758 clear_cap(_metadata, CAP_SYS_ADMIN);
4759 create_file(_metadata, lower_fl1);
4760 create_file(_metadata, lower_dl1_fl2);
4761 create_file(_metadata, lower_fo1);
4762 create_file(_metadata, lower_do1_fo2);
4763 create_file(_metadata, lower_do1_fl3);
4764
4765 create_directory(_metadata, UPPER_BASE);
4766 set_cap(_metadata, CAP_SYS_ADMIN);
4767 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));
4768 clear_cap(_metadata, CAP_SYS_ADMIN);
4769 create_file(_metadata, upper_fu1);
4770 create_file(_metadata, upper_du1_fu2);
4771 create_file(_metadata, upper_fo1);
4772 create_file(_metadata, upper_do1_fo2);
4773 create_file(_metadata, upper_do1_fu3);
4774 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));
4775
4776 create_directory(_metadata, MERGE_DATA);
4777 set_cap(_metadata, CAP_SYS_ADMIN);
4778 set_cap(_metadata, CAP_DAC_OVERRIDE);
4779 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,
4780 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA
4781 ",workdir=" UPPER_WORK));
4782 clear_cap(_metadata, CAP_DAC_OVERRIDE);
4783 clear_cap(_metadata, CAP_SYS_ADMIN);
4784 }
4785
FIXTURE_TEARDOWN_PARENT(layout2_overlay)4786 FIXTURE_TEARDOWN_PARENT(layout2_overlay)
4787 {
4788 if (self->skip_test)
4789 SKIP(return, "overlayfs is not supported (teardown)");
4790
4791 EXPECT_EQ(0, remove_path(lower_do1_fl3));
4792 EXPECT_EQ(0, remove_path(lower_dl1_fl2));
4793 EXPECT_EQ(0, remove_path(lower_fl1));
4794 EXPECT_EQ(0, remove_path(lower_do1_fo2));
4795 EXPECT_EQ(0, remove_path(lower_fo1));
4796
4797 /* umount(LOWER_BASE)) is handled by namespace lifetime. */
4798 EXPECT_EQ(0, remove_path(LOWER_BASE));
4799
4800 EXPECT_EQ(0, remove_path(upper_do1_fu3));
4801 EXPECT_EQ(0, remove_path(upper_du1_fu2));
4802 EXPECT_EQ(0, remove_path(upper_fu1));
4803 EXPECT_EQ(0, remove_path(upper_do1_fo2));
4804 EXPECT_EQ(0, remove_path(upper_fo1));
4805 EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));
4806
4807 /* umount(UPPER_BASE)) is handled by namespace lifetime. */
4808 EXPECT_EQ(0, remove_path(UPPER_BASE));
4809
4810 /* umount(MERGE_DATA)) is handled by namespace lifetime. */
4811 EXPECT_EQ(0, remove_path(MERGE_DATA));
4812
4813 cleanup_layout(_metadata);
4814 }
4815
TEST_F_FORK(layout2_overlay,no_restriction)4816 TEST_F_FORK(layout2_overlay, no_restriction)
4817 {
4818 if (self->skip_test)
4819 SKIP(return, "overlayfs is not supported (test)");
4820
4821 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
4822 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
4823 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
4824 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));
4825 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));
4826 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));
4827 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));
4828
4829 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));
4830 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));
4831 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));
4832 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));
4833 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));
4834 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));
4835 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));
4836
4837 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));
4838 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));
4839 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));
4840 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));
4841 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));
4842 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));
4843 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));
4844 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));
4845 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));
4846 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));
4847 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));
4848 }
4849
4850 #define for_each_path(path_list, path_entry, i) \
4851 for (i = 0, path_entry = *path_list[i]; path_list[i]; \
4852 path_entry = *path_list[++i])
4853
TEST_F_FORK(layout2_overlay,same_content_different_file)4854 TEST_F_FORK(layout2_overlay, same_content_different_file)
4855 {
4856 /* Sets access right on parent directories of both layers. */
4857 const struct rule layer1_base[] = {
4858 {
4859 .path = LOWER_BASE,
4860 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4861 },
4862 {
4863 .path = UPPER_BASE,
4864 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4865 },
4866 {
4867 .path = MERGE_BASE,
4868 .access = ACCESS_RW,
4869 },
4870 {},
4871 };
4872 const struct rule layer2_data[] = {
4873 {
4874 .path = LOWER_DATA,
4875 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4876 },
4877 {
4878 .path = UPPER_DATA,
4879 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4880 },
4881 {
4882 .path = MERGE_DATA,
4883 .access = ACCESS_RW,
4884 },
4885 {},
4886 };
4887 /* Sets access right on directories inside both layers. */
4888 const struct rule layer3_subdirs[] = {
4889 {
4890 .path = lower_dl1,
4891 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4892 },
4893 {
4894 .path = lower_do1,
4895 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4896 },
4897 {
4898 .path = upper_du1,
4899 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4900 },
4901 {
4902 .path = upper_do1,
4903 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4904 },
4905 {
4906 .path = merge_dl1,
4907 .access = ACCESS_RW,
4908 },
4909 {
4910 .path = merge_du1,
4911 .access = ACCESS_RW,
4912 },
4913 {
4914 .path = merge_do1,
4915 .access = ACCESS_RW,
4916 },
4917 {},
4918 };
4919 /* Tighten access rights to the files. */
4920 const struct rule layer4_files[] = {
4921 {
4922 .path = lower_dl1_fl2,
4923 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4924 },
4925 {
4926 .path = lower_do1_fo2,
4927 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4928 },
4929 {
4930 .path = lower_do1_fl3,
4931 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4932 },
4933 {
4934 .path = upper_du1_fu2,
4935 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4936 },
4937 {
4938 .path = upper_do1_fo2,
4939 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4940 },
4941 {
4942 .path = upper_do1_fu3,
4943 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4944 },
4945 {
4946 .path = merge_dl1_fl2,
4947 .access = LANDLOCK_ACCESS_FS_READ_FILE |
4948 LANDLOCK_ACCESS_FS_WRITE_FILE,
4949 },
4950 {
4951 .path = merge_du1_fu2,
4952 .access = LANDLOCK_ACCESS_FS_READ_FILE |
4953 LANDLOCK_ACCESS_FS_WRITE_FILE,
4954 },
4955 {
4956 .path = merge_do1_fo2,
4957 .access = LANDLOCK_ACCESS_FS_READ_FILE |
4958 LANDLOCK_ACCESS_FS_WRITE_FILE,
4959 },
4960 {
4961 .path = merge_do1_fl3,
4962 .access = LANDLOCK_ACCESS_FS_READ_FILE |
4963 LANDLOCK_ACCESS_FS_WRITE_FILE,
4964 },
4965 {
4966 .path = merge_do1_fu3,
4967 .access = LANDLOCK_ACCESS_FS_READ_FILE |
4968 LANDLOCK_ACCESS_FS_WRITE_FILE,
4969 },
4970 {},
4971 };
4972 const struct rule layer5_merge_only[] = {
4973 {
4974 .path = MERGE_DATA,
4975 .access = LANDLOCK_ACCESS_FS_READ_FILE |
4976 LANDLOCK_ACCESS_FS_WRITE_FILE,
4977 },
4978 {},
4979 };
4980 int ruleset_fd;
4981 size_t i;
4982 const char *path_entry;
4983
4984 if (self->skip_test)
4985 SKIP(return, "overlayfs is not supported (test)");
4986
4987 /* Sets rules on base directories (i.e. outside overlay scope). */
4988 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
4989 ASSERT_LE(0, ruleset_fd);
4990 enforce_ruleset(_metadata, ruleset_fd);
4991 ASSERT_EQ(0, close(ruleset_fd));
4992
4993 /* Checks lower layer. */
4994 for_each_path(lower_base_files, path_entry, i) {
4995 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
4996 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
4997 }
4998 for_each_path(lower_base_directories, path_entry, i) {
4999 ASSERT_EQ(EACCES,
5000 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5001 }
5002 for_each_path(lower_sub_files, path_entry, i) {
5003 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5004 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5005 }
5006 /* Checks upper layer. */
5007 for_each_path(upper_base_files, path_entry, i) {
5008 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5009 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5010 }
5011 for_each_path(upper_base_directories, path_entry, i) {
5012 ASSERT_EQ(EACCES,
5013 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5014 }
5015 for_each_path(upper_sub_files, path_entry, i) {
5016 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5017 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5018 }
5019 /*
5020 * Checks that access rights are independent from the lower and upper
5021 * layers: write access to upper files viewed through the merge point
5022 * is still allowed, and write access to lower file viewed (and copied)
5023 * through the merge point is still allowed.
5024 */
5025 for_each_path(merge_base_files, path_entry, i) {
5026 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5027 }
5028 for_each_path(merge_base_directories, path_entry, i) {
5029 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
5030 }
5031 for_each_path(merge_sub_files, path_entry, i) {
5032 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5033 }
5034
5035 /* Sets rules on data directories (i.e. inside overlay scope). */
5036 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);
5037 ASSERT_LE(0, ruleset_fd);
5038 enforce_ruleset(_metadata, ruleset_fd);
5039 ASSERT_EQ(0, close(ruleset_fd));
5040
5041 /* Checks merge. */
5042 for_each_path(merge_base_files, path_entry, i) {
5043 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5044 }
5045 for_each_path(merge_base_directories, path_entry, i) {
5046 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
5047 }
5048 for_each_path(merge_sub_files, path_entry, i) {
5049 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5050 }
5051
5052 /* Same checks with tighter rules. */
5053 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);
5054 ASSERT_LE(0, ruleset_fd);
5055 enforce_ruleset(_metadata, ruleset_fd);
5056 ASSERT_EQ(0, close(ruleset_fd));
5057
5058 /* Checks changes for lower layer. */
5059 for_each_path(lower_base_files, path_entry, i) {
5060 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5061 }
5062 /* Checks changes for upper layer. */
5063 for_each_path(upper_base_files, path_entry, i) {
5064 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5065 }
5066 /* Checks all merge accesses. */
5067 for_each_path(merge_base_files, path_entry, i) {
5068 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
5069 }
5070 for_each_path(merge_base_directories, path_entry, i) {
5071 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
5072 }
5073 for_each_path(merge_sub_files, path_entry, i) {
5074 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5075 }
5076
5077 /* Sets rules directly on overlayed files. */
5078 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);
5079 ASSERT_LE(0, ruleset_fd);
5080 enforce_ruleset(_metadata, ruleset_fd);
5081 ASSERT_EQ(0, close(ruleset_fd));
5082
5083 /* Checks unchanged accesses on lower layer. */
5084 for_each_path(lower_sub_files, path_entry, i) {
5085 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5086 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5087 }
5088 /* Checks unchanged accesses on upper layer. */
5089 for_each_path(upper_sub_files, path_entry, i) {
5090 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5091 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5092 }
5093 /* Checks all merge accesses. */
5094 for_each_path(merge_base_files, path_entry, i) {
5095 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
5096 }
5097 for_each_path(merge_base_directories, path_entry, i) {
5098 ASSERT_EQ(EACCES,
5099 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5100 }
5101 for_each_path(merge_sub_files, path_entry, i) {
5102 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5103 }
5104
5105 /* Only allowes access to the merge hierarchy. */
5106 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);
5107 ASSERT_LE(0, ruleset_fd);
5108 enforce_ruleset(_metadata, ruleset_fd);
5109 ASSERT_EQ(0, close(ruleset_fd));
5110
5111 /* Checks new accesses on lower layer. */
5112 for_each_path(lower_sub_files, path_entry, i) {
5113 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5114 }
5115 /* Checks new accesses on upper layer. */
5116 for_each_path(upper_sub_files, path_entry, i) {
5117 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5118 }
5119 /* Checks all merge accesses. */
5120 for_each_path(merge_base_files, path_entry, i) {
5121 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
5122 }
5123 for_each_path(merge_base_directories, path_entry, i) {
5124 ASSERT_EQ(EACCES,
5125 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5126 }
5127 for_each_path(merge_sub_files, path_entry, i) {
5128 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5129 }
5130 }
5131
FIXTURE(layout3_fs)5132 FIXTURE(layout3_fs)
5133 {
5134 bool has_created_dir;
5135 bool has_created_file;
5136 bool skip_test;
5137 };
5138
FIXTURE_VARIANT(layout3_fs)5139 FIXTURE_VARIANT(layout3_fs)
5140 {
5141 const struct mnt_opt mnt;
5142 const char *const file_path;
5143 unsigned int cwd_fs_magic;
5144 };
5145
5146 /* clang-format off */
FIXTURE_VARIANT_ADD(layout3_fs,tmpfs)5147 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {
5148 /* clang-format on */
5149 .mnt = {
5150 .type = "tmpfs",
5151 .data = MNT_TMP_DATA,
5152 },
5153 .file_path = file1_s1d1,
5154 };
5155
FIXTURE_VARIANT_ADD(layout3_fs,ramfs)5156 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) {
5157 .mnt = {
5158 .type = "ramfs",
5159 .data = "mode=700",
5160 },
5161 .file_path = TMP_DIR "/dir/file",
5162 };
5163
FIXTURE_VARIANT_ADD(layout3_fs,cgroup2)5164 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {
5165 .mnt = {
5166 .type = "cgroup2",
5167 },
5168 .file_path = TMP_DIR "/test/cgroup.procs",
5169 };
5170
FIXTURE_VARIANT_ADD(layout3_fs,proc)5171 FIXTURE_VARIANT_ADD(layout3_fs, proc) {
5172 .mnt = {
5173 .type = "proc",
5174 },
5175 .file_path = TMP_DIR "/self/status",
5176 };
5177
FIXTURE_VARIANT_ADD(layout3_fs,sysfs)5178 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) {
5179 .mnt = {
5180 .type = "sysfs",
5181 },
5182 .file_path = TMP_DIR "/kernel/notes",
5183 };
5184
FIXTURE_VARIANT_ADD(layout3_fs,hostfs)5185 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) {
5186 .mnt = {
5187 .source = TMP_DIR,
5188 .flags = MS_BIND,
5189 },
5190 .file_path = TMP_DIR "/dir/file",
5191 .cwd_fs_magic = HOSTFS_SUPER_MAGIC,
5192 };
5193
dirname_alloc(const char * path)5194 static char *dirname_alloc(const char *path)
5195 {
5196 char *dup;
5197
5198 if (!path)
5199 return NULL;
5200
5201 dup = strdup(path);
5202 if (!dup)
5203 return NULL;
5204
5205 return dirname(dup);
5206 }
5207
FIXTURE_SETUP(layout3_fs)5208 FIXTURE_SETUP(layout3_fs)
5209 {
5210 struct stat statbuf;
5211 char *dir_path = dirname_alloc(variant->file_path);
5212
5213 if (!supports_filesystem(variant->mnt.type) ||
5214 !cwd_matches_fs(variant->cwd_fs_magic)) {
5215 self->skip_test = true;
5216 SKIP(return, "this filesystem is not supported (setup)");
5217 }
5218
5219 prepare_layout_opt(_metadata, &variant->mnt);
5220
5221 /* Creates directory when required. */
5222 if (stat(dir_path, &statbuf)) {
5223 set_cap(_metadata, CAP_DAC_OVERRIDE);
5224 EXPECT_EQ(0, mkdir(dir_path, 0700))
5225 {
5226 TH_LOG("Failed to create directory \"%s\": %s",
5227 dir_path, strerror(errno));
5228 }
5229 self->has_created_dir = true;
5230 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5231 }
5232
5233 /* Creates file when required. */
5234 if (stat(variant->file_path, &statbuf)) {
5235 int fd;
5236
5237 set_cap(_metadata, CAP_DAC_OVERRIDE);
5238 fd = creat(variant->file_path, 0600);
5239 EXPECT_LE(0, fd)
5240 {
5241 TH_LOG("Failed to create file \"%s\": %s",
5242 variant->file_path, strerror(errno));
5243 }
5244 EXPECT_EQ(0, close(fd));
5245 self->has_created_file = true;
5246 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5247 }
5248
5249 free(dir_path);
5250 }
5251
FIXTURE_TEARDOWN_PARENT(layout3_fs)5252 FIXTURE_TEARDOWN_PARENT(layout3_fs)
5253 {
5254 if (self->skip_test)
5255 SKIP(return, "this filesystem is not supported (teardown)");
5256
5257 if (self->has_created_file) {
5258 set_cap(_metadata, CAP_DAC_OVERRIDE);
5259 /*
5260 * Don't check for error because the file might already
5261 * have been removed (cf. release_inode test).
5262 */
5263 unlink(variant->file_path);
5264 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5265 }
5266
5267 if (self->has_created_dir) {
5268 char *dir_path = dirname_alloc(variant->file_path);
5269
5270 set_cap(_metadata, CAP_DAC_OVERRIDE);
5271 /*
5272 * Don't check for error because the directory might already
5273 * have been removed (cf. release_inode test).
5274 */
5275 rmdir(dir_path);
5276 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5277 free(dir_path);
5278 }
5279
5280 cleanup_layout(_metadata);
5281 }
5282
layer3_fs_tag_inode(struct __test_metadata * const _metadata,FIXTURE_DATA (layout3_fs)* self,const FIXTURE_VARIANT (layout3_fs)* variant,const char * const rule_path)5283 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,
5284 FIXTURE_DATA(layout3_fs) * self,
5285 const FIXTURE_VARIANT(layout3_fs) * variant,
5286 const char *const rule_path)
5287 {
5288 const struct rule layer1_allow_read_file[] = {
5289 {
5290 .path = rule_path,
5291 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5292 },
5293 {},
5294 };
5295 const struct landlock_ruleset_attr layer2_deny_everything_attr = {
5296 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
5297 };
5298 const char *const dev_null_path = "/dev/null";
5299 int ruleset_fd;
5300
5301 if (self->skip_test)
5302 SKIP(return, "this filesystem is not supported (test)");
5303
5304 /* Checks without Landlock. */
5305 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
5306 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
5307
5308 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
5309 layer1_allow_read_file);
5310 EXPECT_LE(0, ruleset_fd);
5311 enforce_ruleset(_metadata, ruleset_fd);
5312 EXPECT_EQ(0, close(ruleset_fd));
5313
5314 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
5315 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
5316
5317 /* Forbids directory reading. */
5318 ruleset_fd =
5319 landlock_create_ruleset(&layer2_deny_everything_attr,
5320 sizeof(layer2_deny_everything_attr), 0);
5321 EXPECT_LE(0, ruleset_fd);
5322 enforce_ruleset(_metadata, ruleset_fd);
5323 EXPECT_EQ(0, close(ruleset_fd));
5324
5325 /* Checks with Landlock and forbidden access. */
5326 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
5327 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
5328 }
5329
5330 /* Matrix of tests to check file hierarchy evaluation. */
5331
TEST_F_FORK(layout3_fs,tag_inode_dir_parent)5332 TEST_F_FORK(layout3_fs, tag_inode_dir_parent)
5333 {
5334 /* The current directory must not be the root for this test. */
5335 layer3_fs_tag_inode(_metadata, self, variant, ".");
5336 }
5337
TEST_F_FORK(layout3_fs,tag_inode_dir_mnt)5338 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt)
5339 {
5340 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);
5341 }
5342
TEST_F_FORK(layout3_fs,tag_inode_dir_child)5343 TEST_F_FORK(layout3_fs, tag_inode_dir_child)
5344 {
5345 char *dir_path = dirname_alloc(variant->file_path);
5346
5347 layer3_fs_tag_inode(_metadata, self, variant, dir_path);
5348 free(dir_path);
5349 }
5350
TEST_F_FORK(layout3_fs,tag_inode_file)5351 TEST_F_FORK(layout3_fs, tag_inode_file)
5352 {
5353 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);
5354 }
5355
5356 /* Light version of layout1.release_inodes */
TEST_F_FORK(layout3_fs,release_inodes)5357 TEST_F_FORK(layout3_fs, release_inodes)
5358 {
5359 const struct rule layer1[] = {
5360 {
5361 .path = TMP_DIR,
5362 .access = LANDLOCK_ACCESS_FS_READ_DIR,
5363 },
5364 {},
5365 };
5366 int ruleset_fd;
5367
5368 if (self->skip_test)
5369 SKIP(return, "this filesystem is not supported (test)");
5370
5371 /* Clean up for the teardown to not fail. */
5372 if (self->has_created_file)
5373 EXPECT_EQ(0, remove_path(variant->file_path));
5374
5375 if (self->has_created_dir) {
5376 char *dir_path = dirname_alloc(variant->file_path);
5377
5378 /* Don't check for error because of cgroup specificities. */
5379 remove_path(dir_path);
5380 free(dir_path);
5381 }
5382
5383 ruleset_fd =
5384 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
5385 ASSERT_LE(0, ruleset_fd);
5386
5387 /* Unmount the filesystem while it is being used by a ruleset. */
5388 set_cap(_metadata, CAP_SYS_ADMIN);
5389 ASSERT_EQ(0, umount(TMP_DIR));
5390 clear_cap(_metadata, CAP_SYS_ADMIN);
5391
5392 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */
5393 set_cap(_metadata, CAP_SYS_ADMIN);
5394 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));
5395 clear_cap(_metadata, CAP_SYS_ADMIN);
5396
5397 enforce_ruleset(_metadata, ruleset_fd);
5398 ASSERT_EQ(0, close(ruleset_fd));
5399
5400 /* Checks that access to the new mount point is denied. */
5401 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));
5402 }
5403
5404 TEST_HARNESS_MAIN
5405