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

source code of linux/tools/testing/selftests/landlock/fs_test.c