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