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