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