1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Landlock tests - Filesystem
4 *
5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6 * Copyright © 2020 ANSSI
7 * Copyright © 2020-2022 Microsoft Corporation
8 */
9
10 #define _GNU_SOURCE
11 #include <asm/termbits.h>
12 #include <fcntl.h>
13 #include <libgen.h>
14 #include <linux/fiemap.h>
15 #include <linux/landlock.h>
16 #include <linux/magic.h>
17 #include <sched.h>
18 #include <stddef.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/capability.h>
22 #include <sys/ioctl.h>
23 #include <sys/mount.h>
24 #include <sys/prctl.h>
25 #include <sys/resource.h>
26 #include <sys/sendfile.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/sysmacros.h>
30 #include <sys/un.h>
31 #include <sys/vfs.h>
32 #include <unistd.h>
33
34 /*
35 * Intentionally included last to work around header conflict.
36 * See https://sourceware.org/glibc/wiki/Synchronizing_Headers.
37 */
38 #include <linux/fs.h>
39 #include <linux/mount.h>
40
41 /* Defines AT_EXECVE_CHECK without type conflicts. */
42 #define _ASM_GENERIC_FCNTL_H
43 #include <linux/fcntl.h>
44
45 #include "audit.h"
46 #include "common.h"
47 #include "trace.h"
48
49 #define TRACE_TASK "fs_test"
50
51 #ifndef renameat2
renameat2(int olddirfd,const char * oldpath,int newdirfd,const char * newpath,unsigned int flags)52 int renameat2(int olddirfd, const char *oldpath, int newdirfd,
53 const char *newpath, unsigned int flags)
54 {
55 return syscall(__NR_renameat2, olddirfd, oldpath, newdirfd, newpath,
56 flags);
57 }
58 #endif
59
60 #ifndef open_tree
open_tree(int dfd,const char * filename,unsigned int flags)61 int open_tree(int dfd, const char *filename, unsigned int flags)
62 {
63 return syscall(__NR_open_tree, dfd, filename, flags);
64 }
65 #endif
66
sys_execveat(int dirfd,const char * pathname,char * const argv[],char * const envp[],int flags)67 static int sys_execveat(int dirfd, const char *pathname, char *const argv[],
68 char *const envp[], int flags)
69 {
70 return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
71 }
72
73 #ifndef RENAME_EXCHANGE
74 #define RENAME_EXCHANGE (1 << 1)
75 #endif
76
77 static const char bin_true[] = "./true";
78
79 /* Paths (sibling number and depth) */
80 static const char dir_s1d1[] = TMP_DIR "/s1d1";
81 static const char file1_s1d1[] = TMP_DIR "/s1d1/f1";
82 static const char file2_s1d1[] = TMP_DIR "/s1d1/f2";
83 static const char dir_s1d2[] = TMP_DIR "/s1d1/s1d2";
84 static const char file1_s1d2[] = TMP_DIR "/s1d1/s1d2/f1";
85 static const char file2_s1d2[] = TMP_DIR "/s1d1/s1d2/f2";
86 static const char dir_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3";
87 static const char file1_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f1";
88 static const char file2_s1d3[] = TMP_DIR "/s1d1/s1d2/s1d3/f2";
89
90 static const char dir_s2d1[] = TMP_DIR "/s2d1";
91 static const char file1_s2d1[] = TMP_DIR "/s2d1/f1";
92 static const char dir_s2d2[] = TMP_DIR "/s2d1/s2d2";
93 static const char file1_s2d2[] = TMP_DIR "/s2d1/s2d2/f1";
94 static const char dir_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3";
95 static const char file1_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f1";
96 static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2";
97
98 static const char dir_s3d1[] = TMP_DIR "/s3d1";
99 static const char file1_s3d1[] = TMP_DIR "/s3d1/f1";
100 /* dir_s3d2 is a mount point. */
101 static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2";
102 static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3";
103 static const char file1_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3/f1";
104 static const char dir_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4";
105 static const char file1_s3d4[] = TMP_DIR "/s3d1/s3d2/s3d4/f1";
106
107 /*
108 * layout1 hierarchy:
109 *
110 * tmp
111 * ├── s1d1
112 * │ ├── f1
113 * │ ├── f2
114 * │ └── s1d2
115 * │ ├── f1
116 * │ ├── f2
117 * │ └── s1d3
118 * │ ├── f1
119 * │ └── f2
120 * ├── s2d1
121 * │ ├── f1
122 * │ └── s2d2
123 * │ ├── f1
124 * │ └── s2d3
125 * │ ├── f1
126 * │ └── f2
127 * └── s3d1
128 * ├── f1
129 * └── s3d2 [mount point]
130 * ├── s3d3
131 * │ └── f1
132 * └── s3d4
133 * └── f1
134 */
135
fgrep(FILE * const inf,const char * const str)136 static bool fgrep(FILE *const inf, const char *const str)
137 {
138 char line[32];
139 const int slen = strlen(str);
140
141 while (!feof(inf)) {
142 if (!fgets(line, sizeof(line), inf))
143 break;
144 if (strncmp(line, str, slen))
145 continue;
146
147 return true;
148 }
149
150 return false;
151 }
152
supports_filesystem(const char * const filesystem)153 static bool supports_filesystem(const char *const filesystem)
154 {
155 char str[32];
156 int len;
157 bool res = true;
158 FILE *const inf = fopen("/proc/filesystems", "r");
159
160 /*
161 * Consider that the filesystem is supported if we cannot get the
162 * supported ones.
163 */
164 if (!inf)
165 return true;
166
167 /* filesystem can be null for bind mounts. */
168 if (!filesystem)
169 goto out;
170
171 len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem);
172 if (len >= sizeof(str))
173 /* Ignores too-long filesystem names. */
174 goto out;
175
176 res = fgrep(inf, str);
177
178 out:
179 fclose(inf);
180 return res;
181 }
182
cwd_matches_fs(unsigned int fs_magic)183 static bool cwd_matches_fs(unsigned int fs_magic)
184 {
185 struct statfs statfs_buf;
186
187 if (!fs_magic)
188 return true;
189
190 if (statfs(".", &statfs_buf))
191 return true;
192
193 return statfs_buf.f_type == fs_magic;
194 }
195
mkdir_parents(struct __test_metadata * const _metadata,const char * const path)196 static void mkdir_parents(struct __test_metadata *const _metadata,
197 const char *const path)
198 {
199 char *walker;
200 const char *parent;
201 int i, err;
202
203 ASSERT_NE(path[0], '\0');
204 walker = strdup(path);
205 ASSERT_NE(NULL, walker);
206 parent = walker;
207 for (i = 1; walker[i]; i++) {
208 if (walker[i] != '/')
209 continue;
210 walker[i] = '\0';
211 err = mkdir(parent, 0700);
212 ASSERT_FALSE(err && errno != EEXIST)
213 {
214 TH_LOG("Failed to create directory \"%s\": %s", parent,
215 strerror(errno));
216 }
217 walker[i] = '/';
218 }
219 free(walker);
220 }
221
create_directory(struct __test_metadata * const _metadata,const char * const path)222 static void create_directory(struct __test_metadata *const _metadata,
223 const char *const path)
224 {
225 mkdir_parents(_metadata, path);
226 ASSERT_EQ(0, mkdir(path, 0700))
227 {
228 TH_LOG("Failed to create directory \"%s\": %s", path,
229 strerror(errno));
230 }
231 }
232
create_file(struct __test_metadata * const _metadata,const char * const path)233 static void create_file(struct __test_metadata *const _metadata,
234 const char *const path)
235 {
236 mkdir_parents(_metadata, path);
237 ASSERT_EQ(0, mknod(path, S_IFREG | 0700, 0))
238 {
239 TH_LOG("Failed to create file \"%s\": %s", path,
240 strerror(errno));
241 }
242 }
243
remove_path(const char * const path)244 static int remove_path(const char *const path)
245 {
246 char *walker;
247 int i, ret, err = 0;
248
249 walker = strdup(path);
250 if (!walker) {
251 err = ENOMEM;
252 goto out;
253 }
254 if (unlink(path) && rmdir(path)) {
255 if (errno != ENOENT && errno != ENOTDIR)
256 err = errno;
257 goto out;
258 }
259 for (i = strlen(walker); i > 0; i--) {
260 if (walker[i] != '/')
261 continue;
262 walker[i] = '\0';
263 ret = rmdir(walker);
264 if (ret) {
265 if (errno != ENOTEMPTY && errno != EBUSY)
266 err = errno;
267 goto out;
268 }
269 if (strcmp(walker, TMP_DIR) == 0)
270 goto out;
271 }
272
273 out:
274 free(walker);
275 return err;
276 }
277
278 struct mnt_opt {
279 const char *const source;
280 const char *const type;
281 const unsigned long flags;
282 const char *const data;
283 };
284
285 #define MNT_TMP_DATA "size=4m,mode=700"
286
287 static const struct mnt_opt mnt_tmp = {
288 .type = "tmpfs",
289 .data = MNT_TMP_DATA,
290 };
291
mount_opt(const struct mnt_opt * const mnt,const char * const target)292 static int mount_opt(const struct mnt_opt *const mnt, const char *const target)
293 {
294 return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags,
295 mnt->data);
296 }
297
prepare_layout_opt(struct __test_metadata * const _metadata,const struct mnt_opt * const mnt)298 static void prepare_layout_opt(struct __test_metadata *const _metadata,
299 const struct mnt_opt *const mnt)
300 {
301 disable_caps(_metadata);
302 umask(0077);
303 create_directory(_metadata, TMP_DIR);
304
305 /*
306 * Do not pollute the rest of the system: creates a private mount point
307 * for tests relying on pivot_root(2) and move_mount(2).
308 */
309 set_cap(_metadata, CAP_SYS_ADMIN);
310 ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP));
311 ASSERT_EQ(0, mount_opt(mnt, TMP_DIR))
312 {
313 TH_LOG("Failed to mount the %s filesystem: %s", mnt->type,
314 strerror(errno));
315 /*
316 * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP()
317 * failed, so we need to explicitly do a minimal cleanup to
318 * avoid cascading errors with other tests that don't depend on
319 * the same filesystem.
320 */
321 remove_path(TMP_DIR);
322 }
323 ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
324 clear_cap(_metadata, CAP_SYS_ADMIN);
325 }
326
prepare_layout(struct __test_metadata * const _metadata)327 static void prepare_layout(struct __test_metadata *const _metadata)
328 {
329 prepare_layout_opt(_metadata, &mnt_tmp);
330 }
331
cleanup_layout(struct __test_metadata * const _metadata)332 static void cleanup_layout(struct __test_metadata *const _metadata)
333 {
334 set_cap(_metadata, CAP_SYS_ADMIN);
335 if (umount(TMP_DIR)) {
336 /*
337 * According to the test environment, the mount point of the
338 * current directory may be shared or not, which changes the
339 * visibility of the nested TMP_DIR mount point for the test's
340 * parent process doing this cleanup.
341 */
342 ASSERT_EQ(EINVAL, errno);
343 }
344 clear_cap(_metadata, CAP_SYS_ADMIN);
345 EXPECT_EQ(0, remove_path(TMP_DIR));
346 }
347
348 /* clang-format off */
FIXTURE(layout0)349 FIXTURE(layout0) {};
350 /* clang-format on */
351
FIXTURE_SETUP(layout0)352 FIXTURE_SETUP(layout0)
353 {
354 prepare_layout(_metadata);
355 }
356
FIXTURE_TEARDOWN_PARENT(layout0)357 FIXTURE_TEARDOWN_PARENT(layout0)
358 {
359 cleanup_layout(_metadata);
360 }
361
create_layout1(struct __test_metadata * const _metadata)362 static void create_layout1(struct __test_metadata *const _metadata)
363 {
364 create_file(_metadata, file1_s1d1);
365 create_file(_metadata, file1_s1d2);
366 create_file(_metadata, file1_s1d3);
367 create_file(_metadata, file2_s1d1);
368 create_file(_metadata, file2_s1d2);
369 create_file(_metadata, file2_s1d3);
370
371 create_file(_metadata, file1_s2d1);
372 create_file(_metadata, file1_s2d2);
373 create_file(_metadata, file1_s2d3);
374 create_file(_metadata, file2_s2d3);
375
376 create_file(_metadata, file1_s3d1);
377 create_directory(_metadata, dir_s3d2);
378 set_cap(_metadata, CAP_SYS_ADMIN);
379 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));
380 clear_cap(_metadata, CAP_SYS_ADMIN);
381
382 create_file(_metadata, file1_s3d3);
383 create_file(_metadata, file1_s3d4);
384 }
385
remove_layout1(struct __test_metadata * const _metadata)386 static void remove_layout1(struct __test_metadata *const _metadata)
387 {
388 EXPECT_EQ(0, remove_path(file2_s1d3));
389 EXPECT_EQ(0, remove_path(file2_s1d2));
390 EXPECT_EQ(0, remove_path(file2_s1d1));
391 EXPECT_EQ(0, remove_path(file1_s1d3));
392 EXPECT_EQ(0, remove_path(file1_s1d2));
393 EXPECT_EQ(0, remove_path(file1_s1d1));
394 EXPECT_EQ(0, remove_path(dir_s1d3));
395
396 EXPECT_EQ(0, remove_path(file2_s2d3));
397 EXPECT_EQ(0, remove_path(file1_s2d3));
398 EXPECT_EQ(0, remove_path(file1_s2d2));
399 EXPECT_EQ(0, remove_path(file1_s2d1));
400 EXPECT_EQ(0, remove_path(dir_s2d2));
401
402 EXPECT_EQ(0, remove_path(file1_s3d1));
403 EXPECT_EQ(0, remove_path(file1_s3d3));
404 EXPECT_EQ(0, remove_path(file1_s3d4));
405 set_cap(_metadata, CAP_SYS_ADMIN);
406 umount(dir_s3d2);
407 clear_cap(_metadata, CAP_SYS_ADMIN);
408 EXPECT_EQ(0, remove_path(dir_s3d2));
409 }
410
411 /* clang-format off */
FIXTURE(layout1)412 FIXTURE(layout1) {};
413 /* clang-format on */
414
FIXTURE_SETUP(layout1)415 FIXTURE_SETUP(layout1)
416 {
417 prepare_layout(_metadata);
418
419 create_layout1(_metadata);
420 }
421
FIXTURE_TEARDOWN_PARENT(layout1)422 FIXTURE_TEARDOWN_PARENT(layout1)
423 {
424 remove_layout1(_metadata);
425
426 cleanup_layout(_metadata);
427 }
428
429 /*
430 * This helper enables to use the ASSERT_* macros and print the line number
431 * pointing to the test caller.
432 */
test_open_rel(const int dirfd,const char * const path,const int flags)433 static int test_open_rel(const int dirfd, const char *const path,
434 const int flags)
435 {
436 int fd;
437
438 /* Works with file and directories. */
439 fd = openat(dirfd, path, flags | O_CLOEXEC);
440 if (fd < 0)
441 return errno;
442 /*
443 * Mixing error codes from close(2) and open(2) should not lead to any
444 * (access type) confusion for this test.
445 */
446 if (close(fd) != 0)
447 return errno;
448 return 0;
449 }
450
test_open(const char * const path,const int flags)451 static int test_open(const char *const path, const int flags)
452 {
453 return test_open_rel(AT_FDCWD, path, flags);
454 }
455
TEST_F_FORK(layout1,no_restriction)456 TEST_F_FORK(layout1, no_restriction)
457 {
458 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
459 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
460 ASSERT_EQ(0, test_open(file2_s1d1, O_RDONLY));
461 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
462 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
463 ASSERT_EQ(0, test_open(file2_s1d2, O_RDONLY));
464 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
465 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
466
467 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
468 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
469 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
470 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
471 ASSERT_EQ(0, test_open(dir_s2d3, O_RDONLY));
472 ASSERT_EQ(0, test_open(file1_s2d3, O_RDONLY));
473
474 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
475 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
476 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
477 }
478
TEST_F_FORK(layout1,inval)479 TEST_F_FORK(layout1, inval)
480 {
481 struct landlock_path_beneath_attr path_beneath = {
482 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
483 LANDLOCK_ACCESS_FS_WRITE_FILE,
484 .parent_fd = -1,
485 };
486 struct landlock_ruleset_attr ruleset_attr = {
487 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
488 LANDLOCK_ACCESS_FS_WRITE_FILE,
489 };
490 int ruleset_fd;
491
492 path_beneath.parent_fd =
493 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
494 ASSERT_LE(0, path_beneath.parent_fd);
495
496 ruleset_fd = open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
497 ASSERT_LE(0, ruleset_fd);
498 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
499 &path_beneath, 0));
500 /* Returns EBADF because ruleset_fd is not a landlock-ruleset FD. */
501 ASSERT_EQ(EBADF, errno);
502 ASSERT_EQ(0, close(ruleset_fd));
503
504 ruleset_fd = open(dir_s1d1, O_DIRECTORY | O_CLOEXEC);
505 ASSERT_LE(0, ruleset_fd);
506 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
507 &path_beneath, 0));
508 /* Returns EBADFD because ruleset_fd is not a valid ruleset. */
509 ASSERT_EQ(EBADFD, errno);
510 ASSERT_EQ(0, close(ruleset_fd));
511
512 /* Gets a real ruleset. */
513 ruleset_fd =
514 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
515 ASSERT_LE(0, ruleset_fd);
516 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
517 &path_beneath, 0));
518 ASSERT_EQ(0, close(path_beneath.parent_fd));
519
520 /* Tests without O_PATH. */
521 path_beneath.parent_fd = open(dir_s1d2, O_DIRECTORY | O_CLOEXEC);
522 ASSERT_LE(0, path_beneath.parent_fd);
523 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
524 &path_beneath, 0));
525 ASSERT_EQ(0, close(path_beneath.parent_fd));
526
527 /* Tests with a ruleset FD. */
528 path_beneath.parent_fd = ruleset_fd;
529 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
530 &path_beneath, 0));
531 ASSERT_EQ(EBADFD, errno);
532
533 /* Checks unhandled allowed_access. */
534 path_beneath.parent_fd =
535 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
536 ASSERT_LE(0, path_beneath.parent_fd);
537
538 /* Test with legitimate values. */
539 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_EXECUTE;
540 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
541 &path_beneath, 0));
542 ASSERT_EQ(EINVAL, errno);
543 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_EXECUTE;
544
545 /* Tests with denied-by-default access right. */
546 path_beneath.allowed_access |= LANDLOCK_ACCESS_FS_REFER;
547 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
548 &path_beneath, 0));
549 ASSERT_EQ(EINVAL, errno);
550 path_beneath.allowed_access &= ~LANDLOCK_ACCESS_FS_REFER;
551
552 /* Test with unknown (64-bits) value. */
553 path_beneath.allowed_access |= (1ULL << 60);
554 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
555 &path_beneath, 0));
556 ASSERT_EQ(EINVAL, errno);
557 path_beneath.allowed_access &= ~(1ULL << 60);
558
559 /* Test with no access. */
560 path_beneath.allowed_access = 0;
561 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
562 &path_beneath, 0));
563 ASSERT_EQ(ENOMSG, errno);
564 path_beneath.allowed_access &= ~(1ULL << 60);
565
566 ASSERT_EQ(0, close(path_beneath.parent_fd));
567
568 /* Enforces the ruleset. */
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 ASSERT_EQ(0, close(ruleset_fd));
573 }
574
575 /* clang-format off */
576
577 #define ACCESS_FILE ( \
578 LANDLOCK_ACCESS_FS_EXECUTE | \
579 LANDLOCK_ACCESS_FS_WRITE_FILE | \
580 LANDLOCK_ACCESS_FS_READ_FILE | \
581 LANDLOCK_ACCESS_FS_TRUNCATE | \
582 LANDLOCK_ACCESS_FS_IOCTL_DEV | \
583 LANDLOCK_ACCESS_FS_RESOLVE_UNIX)
584
585 #define ACCESS_LAST LANDLOCK_ACCESS_FS_RESOLVE_UNIX
586
587 #define ACCESS_ALL ( \
588 ACCESS_FILE | \
589 LANDLOCK_ACCESS_FS_READ_DIR | \
590 LANDLOCK_ACCESS_FS_REMOVE_DIR | \
591 LANDLOCK_ACCESS_FS_REMOVE_FILE | \
592 LANDLOCK_ACCESS_FS_MAKE_CHAR | \
593 LANDLOCK_ACCESS_FS_MAKE_DIR | \
594 LANDLOCK_ACCESS_FS_MAKE_REG | \
595 LANDLOCK_ACCESS_FS_MAKE_SOCK | \
596 LANDLOCK_ACCESS_FS_MAKE_FIFO | \
597 LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
598 LANDLOCK_ACCESS_FS_MAKE_SYM | \
599 LANDLOCK_ACCESS_FS_REFER)
600
601 /* clang-format on */
602
TEST_F_FORK(layout1,file_and_dir_access_rights)603 TEST_F_FORK(layout1, file_and_dir_access_rights)
604 {
605 __u64 access;
606 int err;
607 struct landlock_path_beneath_attr path_beneath_file = {},
608 path_beneath_dir = {};
609 struct landlock_ruleset_attr ruleset_attr = {
610 .handled_access_fs = ACCESS_ALL,
611 };
612 const int ruleset_fd =
613 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
614
615 ASSERT_LE(0, ruleset_fd);
616
617 /* Tests access rights for files. */
618 path_beneath_file.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
619 ASSERT_LE(0, path_beneath_file.parent_fd);
620
621 /* Tests access rights for directories. */
622 path_beneath_dir.parent_fd =
623 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
624 ASSERT_LE(0, path_beneath_dir.parent_fd);
625
626 for (access = 1; access <= ACCESS_LAST; access <<= 1) {
627 path_beneath_dir.allowed_access = access;
628 ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
629 LANDLOCK_RULE_PATH_BENEATH,
630 &path_beneath_dir, 0));
631
632 path_beneath_file.allowed_access = access;
633 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
634 &path_beneath_file, 0);
635 if (access & ACCESS_FILE) {
636 ASSERT_EQ(0, err);
637 } else {
638 ASSERT_EQ(-1, err);
639 ASSERT_EQ(EINVAL, errno);
640 }
641 }
642 ASSERT_EQ(0, close(path_beneath_file.parent_fd));
643 ASSERT_EQ(0, close(path_beneath_dir.parent_fd));
644 ASSERT_EQ(0, close(ruleset_fd));
645 }
646
TEST_F_FORK(layout0,ruleset_with_unknown_access)647 TEST_F_FORK(layout0, ruleset_with_unknown_access)
648 {
649 __u64 access_mask;
650
651 for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;
652 access_mask >>= 1) {
653 struct landlock_ruleset_attr ruleset_attr = {
654 .handled_access_fs = access_mask,
655 };
656
657 ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
658 sizeof(ruleset_attr), 0));
659 ASSERT_EQ(EINVAL, errno);
660 }
661 }
662
TEST_F_FORK(layout0,rule_with_unknown_access)663 TEST_F_FORK(layout0, rule_with_unknown_access)
664 {
665 __u64 access;
666 struct landlock_path_beneath_attr path_beneath = {};
667 const struct landlock_ruleset_attr ruleset_attr = {
668 .handled_access_fs = ACCESS_ALL,
669 };
670 const int ruleset_fd =
671 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
672
673 ASSERT_LE(0, ruleset_fd);
674
675 path_beneath.parent_fd =
676 open(TMP_DIR, O_PATH | O_DIRECTORY | O_CLOEXEC);
677 ASSERT_LE(0, path_beneath.parent_fd);
678
679 for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) {
680 path_beneath.allowed_access = access;
681 EXPECT_EQ(-1, landlock_add_rule(ruleset_fd,
682 LANDLOCK_RULE_PATH_BENEATH,
683 &path_beneath, 0));
684 EXPECT_EQ(EINVAL, errno);
685 }
686 ASSERT_EQ(0, close(path_beneath.parent_fd));
687 ASSERT_EQ(0, close(ruleset_fd));
688 }
689
TEST_F_FORK(layout1,rule_with_unhandled_access)690 TEST_F_FORK(layout1, rule_with_unhandled_access)
691 {
692 struct landlock_ruleset_attr ruleset_attr = {
693 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
694 };
695 struct landlock_path_beneath_attr path_beneath = {};
696 int ruleset_fd;
697 __u64 access;
698
699 ruleset_fd =
700 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
701 ASSERT_LE(0, ruleset_fd);
702
703 path_beneath.parent_fd = open(file1_s1d2, O_PATH | O_CLOEXEC);
704 ASSERT_LE(0, path_beneath.parent_fd);
705
706 for (access = 1; access > 0; access <<= 1) {
707 int err;
708
709 path_beneath.allowed_access = access;
710 err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
711 &path_beneath, 0);
712 if (access == ruleset_attr.handled_access_fs) {
713 EXPECT_EQ(0, err);
714 } else {
715 EXPECT_EQ(-1, err);
716 EXPECT_EQ(EINVAL, errno);
717 }
718 }
719
720 EXPECT_EQ(0, close(path_beneath.parent_fd));
721 EXPECT_EQ(0, close(ruleset_fd));
722 }
723
add_path_beneath(struct __test_metadata * const _metadata,const int ruleset_fd,const __u64 allowed_access,const char * const path,__u32 flags)724 static void add_path_beneath(struct __test_metadata *const _metadata,
725 const int ruleset_fd, const __u64 allowed_access,
726 const char *const path, __u32 flags)
727 {
728 struct landlock_path_beneath_attr path_beneath = {
729 .allowed_access = allowed_access,
730 };
731
732 path_beneath.parent_fd = open(path, O_PATH | O_CLOEXEC);
733 ASSERT_LE(0, path_beneath.parent_fd)
734 {
735 TH_LOG("Failed to open directory \"%s\": %s", path,
736 strerror(errno));
737 }
738 ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
739 &path_beneath, flags))
740 {
741 TH_LOG("Failed to update the ruleset with \"%s\": %s", path,
742 strerror(errno));
743 }
744 ASSERT_EQ(0, close(path_beneath.parent_fd));
745 }
746
747 struct rule {
748 const char *path;
749 __u64 access;
750 };
751
752 /* clang-format off */
753
754 #define ACCESS_RO ( \
755 LANDLOCK_ACCESS_FS_READ_FILE | \
756 LANDLOCK_ACCESS_FS_READ_DIR)
757
758 #define ACCESS_RW ( \
759 ACCESS_RO | \
760 LANDLOCK_ACCESS_FS_WRITE_FILE)
761
762 /* clang-format on */
763
create_ruleset(struct __test_metadata * const _metadata,const __u64 handled_access_fs,const struct rule rules[])764 static int create_ruleset(struct __test_metadata *const _metadata,
765 const __u64 handled_access_fs,
766 const struct rule rules[])
767 {
768 int ruleset_fd, i;
769 struct landlock_ruleset_attr ruleset_attr = {
770 .handled_access_fs = handled_access_fs,
771 };
772
773 ruleset_fd =
774 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
775 ASSERT_LE(0, ruleset_fd)
776 {
777 TH_LOG("Failed to create a ruleset: %s", strerror(errno));
778 }
779
780 if (rules)
781 for (i = 0; rules[i].path; i++) {
782 if (!rules[i].access)
783 continue;
784
785 add_path_beneath(_metadata, ruleset_fd, rules[i].access,
786 rules[i].path, 0);
787 }
788 return ruleset_fd;
789 }
790
enforce_fs(struct __test_metadata * const _metadata,const __u64 access_fs,const struct rule rules[])791 static void enforce_fs(struct __test_metadata *const _metadata,
792 const __u64 access_fs, const struct rule rules[])
793 {
794 const int ruleset_fd = create_ruleset(_metadata, access_fs, rules);
795
796 enforce_ruleset(_metadata, ruleset_fd);
797 EXPECT_EQ(0, close(ruleset_fd));
798 }
799
TEST_F_FORK(layout0,proc_nsfs)800 TEST_F_FORK(layout0, proc_nsfs)
801 {
802 const struct rule rules[] = {
803 {
804 .path = "/dev/null",
805 .access = LANDLOCK_ACCESS_FS_READ_FILE |
806 LANDLOCK_ACCESS_FS_WRITE_FILE,
807 },
808 {},
809 };
810 struct landlock_path_beneath_attr path_beneath;
811 const int ruleset_fd = create_ruleset(
812 _metadata, rules[0].access | LANDLOCK_ACCESS_FS_READ_DIR,
813 rules);
814
815 ASSERT_LE(0, ruleset_fd);
816 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
817
818 enforce_ruleset(_metadata, ruleset_fd);
819
820 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
821 ASSERT_EQ(EACCES, test_open("/dev", O_RDONLY));
822 ASSERT_EQ(0, test_open("/dev/null", O_RDONLY));
823 ASSERT_EQ(EACCES, test_open("/dev/full", O_RDONLY));
824
825 ASSERT_EQ(EACCES, test_open("/proc", O_RDONLY));
826 ASSERT_EQ(EACCES, test_open("/proc/self", O_RDONLY));
827 ASSERT_EQ(EACCES, test_open("/proc/self/ns", O_RDONLY));
828 /*
829 * Because nsfs is an internal filesystem, /proc/self/ns/mnt is a
830 * disconnected path. Such path cannot be identified and must then be
831 * allowed.
832 */
833 ASSERT_EQ(0, test_open("/proc/self/ns/mnt", O_RDONLY));
834
835 /*
836 * Checks that it is not possible to add nsfs-like filesystem
837 * references to a ruleset.
838 */
839 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
840 LANDLOCK_ACCESS_FS_WRITE_FILE,
841 path_beneath.parent_fd = open("/proc/self/ns/mnt", O_PATH | O_CLOEXEC);
842 ASSERT_LE(0, path_beneath.parent_fd);
843 ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
844 &path_beneath, 0));
845 ASSERT_EQ(EBADFD, errno);
846 ASSERT_EQ(0, close(path_beneath.parent_fd));
847 }
848
TEST_F_FORK(layout0,unpriv)849 TEST_F_FORK(layout0, unpriv)
850 {
851 const struct rule rules[] = {
852 {
853 .path = TMP_DIR,
854 .access = ACCESS_RO,
855 },
856 {},
857 };
858 int ruleset_fd;
859
860 drop_caps(_metadata);
861
862 ruleset_fd = create_ruleset(_metadata, ACCESS_RO, rules);
863 ASSERT_LE(0, ruleset_fd);
864 ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
865 ASSERT_EQ(EPERM, errno);
866
867 /* enforce_ruleset() calls prctl(no_new_privs). */
868 enforce_ruleset(_metadata, ruleset_fd);
869 ASSERT_EQ(0, close(ruleset_fd));
870 }
871
TEST_F_FORK(layout1,effective_access)872 TEST_F_FORK(layout1, effective_access)
873 {
874 const struct rule rules[] = {
875 {
876 .path = dir_s1d2,
877 .access = ACCESS_RO,
878 },
879 {
880 .path = file1_s2d2,
881 .access = LANDLOCK_ACCESS_FS_READ_FILE |
882 LANDLOCK_ACCESS_FS_WRITE_FILE,
883 },
884 {},
885 };
886 char buf;
887 int reg_fd;
888
889 enforce_fs(_metadata, ACCESS_RW, rules);
890
891 /* Tests on a directory (with or without O_PATH). */
892 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
893 ASSERT_EQ(0, test_open("/", O_RDONLY | O_PATH));
894 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
895 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_PATH));
896 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
897 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY | O_PATH));
898
899 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
900 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
901 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
902 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
903
904 /* Tests on a file (with or without O_PATH). */
905 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY));
906 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_PATH));
907
908 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
909
910 /* Checks effective read and write actions. */
911 reg_fd = open(file1_s2d2, O_RDWR | O_CLOEXEC);
912 ASSERT_LE(0, reg_fd);
913 ASSERT_EQ(1, write(reg_fd, ".", 1));
914 ASSERT_LE(0, lseek(reg_fd, 0, SEEK_SET));
915 ASSERT_EQ(1, read(reg_fd, &buf, 1));
916 ASSERT_EQ('.', buf);
917 ASSERT_EQ(0, close(reg_fd));
918
919 /* Just in case, double-checks effective actions. */
920 reg_fd = open(file1_s2d2, O_RDONLY | O_CLOEXEC);
921 ASSERT_LE(0, reg_fd);
922 ASSERT_EQ(-1, write(reg_fd, &buf, 1));
923 ASSERT_EQ(EBADF, errno);
924 ASSERT_EQ(0, close(reg_fd));
925 }
926
TEST_F_FORK(layout1,unhandled_access)927 TEST_F_FORK(layout1, unhandled_access)
928 {
929 const struct rule rules[] = {
930 {
931 .path = dir_s1d2,
932 .access = ACCESS_RO,
933 },
934 {},
935 };
936
937 /* Here, we only handle read accesses, not write accesses. */
938 enforce_fs(_metadata, ACCESS_RO, rules);
939
940 /*
941 * Because the policy does not handle LANDLOCK_ACCESS_FS_WRITE_FILE,
942 * opening for write-only should be allowed, but not read-write.
943 */
944 ASSERT_EQ(0, test_open(file1_s1d1, O_WRONLY));
945 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
946
947 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
948 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
949 }
950
TEST_F_FORK(layout1,ruleset_overlap)951 TEST_F_FORK(layout1, ruleset_overlap)
952 {
953 const struct rule rules[] = {
954 /* These rules should be ORed among them. */
955 {
956 .path = dir_s1d2,
957 .access = LANDLOCK_ACCESS_FS_READ_FILE |
958 LANDLOCK_ACCESS_FS_WRITE_FILE,
959 },
960 {
961 .path = dir_s1d2,
962 .access = LANDLOCK_ACCESS_FS_READ_FILE |
963 LANDLOCK_ACCESS_FS_READ_DIR,
964 },
965 {},
966 };
967
968 enforce_fs(_metadata, ACCESS_RW, rules);
969
970 /* Checks s1d1 hierarchy. */
971 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
972 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
973 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
974 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
975
976 /* Checks s1d2 hierarchy. */
977 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
978 ASSERT_EQ(0, test_open(file1_s1d2, O_WRONLY));
979 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
980 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
981
982 /* Checks s1d3 hierarchy. */
983 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
984 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
985 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
986 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
987 }
988
TEST_F_FORK(layout1,layer_rule_unions)989 TEST_F_FORK(layout1, layer_rule_unions)
990 {
991 const struct rule layer1[] = {
992 {
993 .path = dir_s1d2,
994 .access = LANDLOCK_ACCESS_FS_READ_FILE,
995 },
996 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
997 {
998 .path = dir_s1d3,
999 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1000 },
1001 {},
1002 };
1003 const struct rule layer2[] = {
1004 /* Doesn't change anything from layer1. */
1005 {
1006 .path = dir_s1d2,
1007 .access = LANDLOCK_ACCESS_FS_READ_FILE |
1008 LANDLOCK_ACCESS_FS_WRITE_FILE,
1009 },
1010 {},
1011 };
1012 const struct rule layer3[] = {
1013 /* Only allows write (but not read) to dir_s1d3. */
1014 {
1015 .path = dir_s1d2,
1016 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1017 },
1018 {},
1019 };
1020
1021 enforce_fs(_metadata, ACCESS_RW, layer1);
1022
1023 /* Checks s1d1 hierarchy with layer1. */
1024 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1025 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1026 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
1027 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1028
1029 /* Checks s1d2 hierarchy with layer1. */
1030 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1031 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1032 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
1033 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1034
1035 /* Checks s1d3 hierarchy with layer1. */
1036 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1037 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
1038 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
1039 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1040 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1041
1042 /* Doesn't change anything from layer1. */
1043 enforce_fs(_metadata, ACCESS_RW, layer2);
1044
1045 /* Checks s1d1 hierarchy with layer2. */
1046 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1047 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1048 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
1049 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1050
1051 /* Checks s1d2 hierarchy with layer2. */
1052 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
1053 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1054 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
1055 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1056
1057 /* Checks s1d3 hierarchy with layer2. */
1058 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1059 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
1060 /* dir_s1d3 should allow READ_FILE and WRITE_FILE (O_RDWR). */
1061 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1062 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1063
1064 /* Only allows write (but not read) to dir_s1d3. */
1065 enforce_fs(_metadata, ACCESS_RW, layer3);
1066
1067 /* Checks s1d1 hierarchy with layer3. */
1068 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1069 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1070 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
1071 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1072
1073 /* Checks s1d2 hierarchy with layer3. */
1074 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
1075 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1076 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
1077 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1078
1079 /* Checks s1d3 hierarchy with layer3. */
1080 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
1081 ASSERT_EQ(0, test_open(file1_s1d3, O_WRONLY));
1082 /* dir_s1d3 should now deny READ_FILE and WRITE_FILE (O_RDWR). */
1083 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDWR));
1084 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1085 }
1086
TEST_F_FORK(layout1,non_overlapping_accesses)1087 TEST_F_FORK(layout1, non_overlapping_accesses)
1088 {
1089 const struct rule layer1[] = {
1090 {
1091 .path = dir_s1d2,
1092 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
1093 },
1094 {},
1095 };
1096 const struct rule layer2[] = {
1097 {
1098 .path = dir_s1d3,
1099 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
1100 },
1101 {},
1102 };
1103
1104 ASSERT_EQ(0, unlink(file1_s1d1));
1105 ASSERT_EQ(0, unlink(file1_s1d2));
1106
1107 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, layer1);
1108
1109 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
1110 ASSERT_EQ(EACCES, errno);
1111 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
1112 ASSERT_EQ(0, unlink(file1_s1d2));
1113
1114 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REMOVE_FILE, layer2);
1115
1116 /* Unchanged accesses for file creation. */
1117 ASSERT_EQ(-1, mknod(file1_s1d1, S_IFREG | 0700, 0));
1118 ASSERT_EQ(EACCES, errno);
1119 ASSERT_EQ(0, mknod(file1_s1d2, S_IFREG | 0700, 0));
1120
1121 /* Checks file removing. */
1122 ASSERT_EQ(-1, unlink(file1_s1d2));
1123 ASSERT_EQ(EACCES, errno);
1124 ASSERT_EQ(0, unlink(file1_s1d3));
1125 }
1126
TEST_F_FORK(layout1,interleaved_masked_accesses)1127 TEST_F_FORK(layout1, interleaved_masked_accesses)
1128 {
1129 /*
1130 * Checks overly restrictive rules:
1131 * layer 1: allows R s1d1/s1d2/s1d3/file1
1132 * layer 2: allows RW s1d1/s1d2/s1d3
1133 * allows W s1d1/s1d2
1134 * denies R s1d1/s1d2
1135 * layer 3: allows R s1d1
1136 * layer 4: allows R s1d1/s1d2
1137 * denies W s1d1/s1d2
1138 * layer 5: allows R s1d1/s1d2
1139 * layer 6: allows X ----
1140 * layer 7: allows W s1d1/s1d2
1141 * denies R s1d1/s1d2
1142 */
1143 const struct rule layer1_read[] = {
1144 /* Allows read access to file1_s1d3 with the first layer. */
1145 {
1146 .path = file1_s1d3,
1147 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1148 },
1149 {},
1150 };
1151 /* First rule with write restrictions. */
1152 const struct rule layer2_read_write[] = {
1153 /* Start by granting read-write access via its parent directory... */
1154 {
1155 .path = dir_s1d3,
1156 .access = LANDLOCK_ACCESS_FS_READ_FILE |
1157 LANDLOCK_ACCESS_FS_WRITE_FILE,
1158 },
1159 /* ...but also denies read access via its grandparent directory. */
1160 {
1161 .path = dir_s1d2,
1162 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1163 },
1164 {},
1165 };
1166 const struct rule layer3_read[] = {
1167 /* Allows read access via its great-grandparent directory. */
1168 {
1169 .path = dir_s1d1,
1170 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1171 },
1172 {},
1173 };
1174 const struct rule layer4_read_write[] = {
1175 /*
1176 * Try to confuse the deny access by denying write (but not
1177 * read) access via its grandparent directory.
1178 */
1179 {
1180 .path = dir_s1d2,
1181 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1182 },
1183 {},
1184 };
1185 const struct rule layer5_read[] = {
1186 /*
1187 * Try to override layer2's deny read access by explicitly
1188 * allowing read access via file1_s1d3's grandparent.
1189 */
1190 {
1191 .path = dir_s1d2,
1192 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1193 },
1194 {},
1195 };
1196 const struct rule layer6_execute[] = {
1197 /*
1198 * Restricts an unrelated file hierarchy with a new access
1199 * (non-overlapping) type.
1200 */
1201 {
1202 .path = dir_s2d1,
1203 .access = LANDLOCK_ACCESS_FS_EXECUTE,
1204 },
1205 {},
1206 };
1207 const struct rule layer7_read_write[] = {
1208 /*
1209 * Finally, denies read access to file1_s1d3 via its
1210 * grandparent.
1211 */
1212 {
1213 .path = dir_s1d2,
1214 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
1215 },
1216 {},
1217 };
1218
1219 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer1_read);
1220
1221 /* Checks that read access is granted for file1_s1d3 with layer 1. */
1222 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1223 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1224 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1225
1226 enforce_fs(_metadata,
1227 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
1228 layer2_read_write);
1229
1230 /* Checks that previous access rights are unchanged with layer 2. */
1231 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1232 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1233 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1234
1235 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer3_read);
1236
1237 /* Checks that previous access rights are unchanged with layer 3. */
1238 ASSERT_EQ(0, test_open(file1_s1d3, O_RDWR));
1239 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1240 ASSERT_EQ(0, test_open(file2_s1d3, O_WRONLY));
1241
1242 /* This time, denies write access for the file hierarchy. */
1243 enforce_fs(_metadata,
1244 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
1245 layer4_read_write);
1246
1247 /*
1248 * Checks that the only change with layer 4 is that write access is
1249 * denied.
1250 */
1251 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1252 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1253 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1254 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1255
1256 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, layer5_read);
1257
1258 /* Checks that previous access rights are unchanged with layer 5. */
1259 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1260 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1261 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1262 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1263
1264 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, layer6_execute);
1265
1266 /* Checks that previous access rights are unchanged with layer 6. */
1267 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1268 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1269 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1270 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1271
1272 enforce_fs(_metadata,
1273 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
1274 layer7_read_write);
1275
1276 /* Checks read access is now denied with layer 7. */
1277 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
1278 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1279 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_WRONLY));
1280 ASSERT_EQ(EACCES, test_open(file2_s1d3, O_RDONLY));
1281 }
1282
TEST_F_FORK(layout1,inherit_subset)1283 TEST_F_FORK(layout1, inherit_subset)
1284 {
1285 const struct rule rules[] = {
1286 {
1287 .path = dir_s1d2,
1288 .access = LANDLOCK_ACCESS_FS_READ_FILE |
1289 LANDLOCK_ACCESS_FS_READ_DIR,
1290 },
1291 {},
1292 };
1293 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1294
1295 enforce_ruleset(_metadata, ruleset_fd);
1296
1297 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1298 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1299
1300 /* Write access is forbidden. */
1301 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1302 /* Readdir access is allowed. */
1303 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1304
1305 /* Write access is forbidden. */
1306 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1307 /* Readdir access is allowed. */
1308 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1309
1310 /*
1311 * Tests shared rule extension: the following rules should not grant
1312 * any new access, only remove some. Once enforced, these rules are
1313 * ANDed with the previous ones.
1314 */
1315 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1316 dir_s1d2, 0);
1317 /*
1318 * According to ruleset_fd, dir_s1d2 should now have the
1319 * LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_WRITE_FILE
1320 * access rights (even if this directory is opened a second time).
1321 * However, when enforcing this updated ruleset, the ruleset tied to
1322 * the current process (i.e. its domain) will still only have the
1323 * dir_s1d2 with LANDLOCK_ACCESS_FS_READ_FILE and
1324 * LANDLOCK_ACCESS_FS_READ_DIR accesses, but
1325 * LANDLOCK_ACCESS_FS_WRITE_FILE must not be allowed because it would
1326 * be a privilege escalation.
1327 */
1328 enforce_ruleset(_metadata, ruleset_fd);
1329
1330 /* Same tests and results as above. */
1331 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1332 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1333
1334 /* It is still forbidden to write in file1_s1d2. */
1335 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1336 /* Readdir access is still allowed. */
1337 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1338
1339 /* It is still forbidden to write in file1_s1d3. */
1340 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1341 /* Readdir access is still allowed. */
1342 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1343
1344 /*
1345 * Try to get more privileges by adding new access rights to the parent
1346 * directory: dir_s1d1.
1347 */
1348 add_path_beneath(_metadata, ruleset_fd, ACCESS_RW, dir_s1d1, 0);
1349 enforce_ruleset(_metadata, ruleset_fd);
1350
1351 /* Same tests and results as above. */
1352 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1353 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1354
1355 /* It is still forbidden to write in file1_s1d2. */
1356 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1357 /* Readdir access is still allowed. */
1358 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1359
1360 /* It is still forbidden to write in file1_s1d3. */
1361 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1362 /* Readdir access is still allowed. */
1363 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1364
1365 /*
1366 * Now, dir_s1d3 get a new rule tied to it, only allowing
1367 * LANDLOCK_ACCESS_FS_WRITE_FILE. The (kernel internal) difference is
1368 * that there was no rule tied to it before.
1369 */
1370 add_path_beneath(_metadata, ruleset_fd, LANDLOCK_ACCESS_FS_WRITE_FILE,
1371 dir_s1d3, 0);
1372 enforce_ruleset(_metadata, ruleset_fd);
1373 ASSERT_EQ(0, close(ruleset_fd));
1374
1375 /*
1376 * Same tests and results as above, except for open(dir_s1d3) which is
1377 * now denied because the new rule mask the rule previously inherited
1378 * from dir_s1d2.
1379 */
1380
1381 /* Same tests and results as above. */
1382 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
1383 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
1384
1385 /* It is still forbidden to write in file1_s1d2. */
1386 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
1387 /* Readdir access is still allowed. */
1388 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1389
1390 /* It is still forbidden to write in file1_s1d3. */
1391 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
1392 /*
1393 * Readdir of dir_s1d3 is still allowed because of the OR policy inside
1394 * the same layer.
1395 */
1396 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1397 }
1398
TEST_F_FORK(layout1,inherit_superset)1399 TEST_F_FORK(layout1, inherit_superset)
1400 {
1401 const struct rule rules[] = {
1402 {
1403 .path = dir_s1d3,
1404 .access = ACCESS_RO,
1405 },
1406 {},
1407 };
1408 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1409
1410 enforce_ruleset(_metadata, ruleset_fd);
1411
1412 /* Readdir access is denied for dir_s1d2. */
1413 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1414 /* Readdir access is allowed for dir_s1d3. */
1415 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1416 /* File access is allowed for file1_s1d3. */
1417 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1418
1419 /* Now dir_s1d2, parent of dir_s1d3, gets a new rule tied to it. */
1420 add_path_beneath(_metadata, ruleset_fd,
1421 LANDLOCK_ACCESS_FS_READ_FILE |
1422 LANDLOCK_ACCESS_FS_READ_DIR,
1423 dir_s1d2, 0);
1424 enforce_ruleset(_metadata, ruleset_fd);
1425 EXPECT_EQ(0, close(ruleset_fd));
1426
1427 /* Readdir access is still denied for dir_s1d2. */
1428 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
1429 /* Readdir access is still allowed for dir_s1d3. */
1430 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
1431 /* File access is still allowed for file1_s1d3. */
1432 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
1433 }
1434
TEST_F_FORK(layout0,max_layers)1435 TEST_F_FORK(layout0, max_layers)
1436 {
1437 int i, err;
1438 const struct rule rules[] = {
1439 {
1440 .path = TMP_DIR,
1441 .access = ACCESS_RO,
1442 },
1443 {},
1444 };
1445 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1446
1447 for (i = 0; i < LANDLOCK_MAX_NUM_LAYERS; i++)
1448 enforce_ruleset(_metadata, ruleset_fd);
1449
1450 for (i = 0; i < 2; i++) {
1451 err = landlock_restrict_self(ruleset_fd, 0);
1452 ASSERT_EQ(-1, err);
1453 ASSERT_EQ(E2BIG, errno);
1454 }
1455 EXPECT_EQ(0, close(ruleset_fd));
1456 }
1457
TEST_F_FORK(layout1,empty_or_same_ruleset)1458 TEST_F_FORK(layout1, empty_or_same_ruleset)
1459 {
1460 struct landlock_ruleset_attr ruleset_attr = {};
1461 int ruleset_fd;
1462
1463 /* Tests empty handled_access_fs. */
1464 ruleset_fd =
1465 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
1466 ASSERT_LE(-1, ruleset_fd);
1467 ASSERT_EQ(ENOMSG, errno);
1468
1469 /* Enforces policy which denies read access to all files. */
1470 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, NULL);
1471
1472 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1473 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1474
1475 /* Nests a policy which denies read access to all directories. */
1476 ruleset_fd =
1477 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, NULL);
1478 enforce_ruleset(_metadata, ruleset_fd);
1479 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
1480 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1481
1482 /* Enforces a second time with the same ruleset. */
1483 enforce_ruleset(_metadata, ruleset_fd);
1484 ASSERT_EQ(0, close(ruleset_fd));
1485 }
1486
TEST_F_FORK(layout1,rule_on_mountpoint)1487 TEST_F_FORK(layout1, rule_on_mountpoint)
1488 {
1489 const struct rule rules[] = {
1490 {
1491 .path = dir_s1d1,
1492 .access = ACCESS_RO,
1493 },
1494 {
1495 /* dir_s3d2 is a mount point. */
1496 .path = dir_s3d2,
1497 .access = ACCESS_RO,
1498 },
1499 {},
1500 };
1501
1502 enforce_fs(_metadata, ACCESS_RW, rules);
1503
1504 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1505
1506 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1507
1508 ASSERT_EQ(EACCES, test_open(dir_s3d1, O_RDONLY));
1509 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1510 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1511 }
1512
TEST_F_FORK(layout1,rule_over_mountpoint)1513 TEST_F_FORK(layout1, rule_over_mountpoint)
1514 {
1515 const struct rule rules[] = {
1516 {
1517 .path = dir_s1d1,
1518 .access = ACCESS_RO,
1519 },
1520 {
1521 /* dir_s3d2 is a mount point. */
1522 .path = dir_s3d1,
1523 .access = ACCESS_RO,
1524 },
1525 {},
1526 };
1527
1528 enforce_fs(_metadata, ACCESS_RW, rules);
1529
1530 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1531
1532 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY));
1533
1534 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
1535 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1536 ASSERT_EQ(0, test_open(dir_s3d3, O_RDONLY));
1537 }
1538
1539 /*
1540 * This test verifies that we can apply a landlock rule on the root directory
1541 * (which might require special handling).
1542 */
TEST_F_FORK(layout1,rule_over_root_allow_then_deny)1543 TEST_F_FORK(layout1, rule_over_root_allow_then_deny)
1544 {
1545 struct rule rules[] = {
1546 {
1547 .path = "/",
1548 .access = ACCESS_RO,
1549 },
1550 {},
1551 };
1552
1553 enforce_fs(_metadata, ACCESS_RW, rules);
1554
1555 /* Checks allowed access. */
1556 ASSERT_EQ(0, test_open("/", O_RDONLY));
1557 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
1558
1559 rules[0].access = LANDLOCK_ACCESS_FS_READ_FILE;
1560 enforce_fs(_metadata, ACCESS_RW, rules);
1561
1562 /* Checks denied access (on a directory). */
1563 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1564 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1565 }
1566
TEST_F_FORK(layout1,rule_over_root_deny)1567 TEST_F_FORK(layout1, rule_over_root_deny)
1568 {
1569 const struct rule rules[] = {
1570 {
1571 .path = "/",
1572 .access = LANDLOCK_ACCESS_FS_READ_FILE,
1573 },
1574 {},
1575 };
1576
1577 enforce_fs(_metadata, ACCESS_RW, rules);
1578
1579 /* Checks denied access (on a directory). */
1580 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1581 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY));
1582 }
1583
TEST_F_FORK(layout1,rule_inside_mount_ns)1584 TEST_F_FORK(layout1, rule_inside_mount_ns)
1585 {
1586 const struct rule rules[] = {
1587 {
1588 .path = "s3d3",
1589 .access = ACCESS_RO,
1590 },
1591 {},
1592 };
1593
1594 set_cap(_metadata, CAP_SYS_ADMIN);
1595 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3))
1596 {
1597 TH_LOG("Failed to pivot root: %s", strerror(errno));
1598 };
1599 ASSERT_EQ(0, chdir("/"));
1600 clear_cap(_metadata, CAP_SYS_ADMIN);
1601
1602 enforce_fs(_metadata, ACCESS_RW, rules);
1603
1604 ASSERT_EQ(0, test_open("s3d3", O_RDONLY));
1605 ASSERT_EQ(EACCES, test_open("/", O_RDONLY));
1606 }
1607
TEST_F_FORK(layout1,mount_and_pivot)1608 TEST_F_FORK(layout1, mount_and_pivot)
1609 {
1610 const struct rule rules[] = {
1611 {
1612 .path = dir_s3d2,
1613 .access = ACCESS_RO,
1614 },
1615 {},
1616 };
1617
1618 enforce_fs(_metadata, ACCESS_RW, rules);
1619
1620 set_cap(_metadata, CAP_SYS_ADMIN);
1621 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
1622 ASSERT_EQ(EPERM, errno);
1623 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1624 ASSERT_EQ(EPERM, errno);
1625 clear_cap(_metadata, CAP_SYS_ADMIN);
1626 }
1627
TEST_F_FORK(layout1,move_mount)1628 TEST_F_FORK(layout1, move_mount)
1629 {
1630 const struct rule rules[] = {
1631 {
1632 .path = dir_s3d2,
1633 .access = ACCESS_RO,
1634 },
1635 {},
1636 };
1637
1638 set_cap(_metadata, CAP_SYS_ADMIN);
1639 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1640 dir_s1d2, 0))
1641 {
1642 TH_LOG("Failed to move mount: %s", strerror(errno));
1643 }
1644
1645 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
1646 dir_s3d2, 0));
1647 clear_cap(_metadata, CAP_SYS_ADMIN);
1648
1649 enforce_fs(_metadata, ACCESS_RW, rules);
1650
1651 set_cap(_metadata, CAP_SYS_ADMIN);
1652 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1653 dir_s1d2, 0));
1654 ASSERT_EQ(EPERM, errno);
1655 clear_cap(_metadata, CAP_SYS_ADMIN);
1656 }
1657
TEST_F_FORK(layout1,topology_changes_with_net_only)1658 TEST_F_FORK(layout1, topology_changes_with_net_only)
1659 {
1660 const struct landlock_ruleset_attr ruleset_net = {
1661 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
1662 LANDLOCK_ACCESS_NET_CONNECT_TCP,
1663 };
1664
1665 /* Add network restrictions. */
1666 drop_access_rights(_metadata, &ruleset_net);
1667
1668 /* Mount, remount, move_mount, umount, and pivot_root checks. */
1669 set_cap(_metadata, CAP_SYS_ADMIN);
1670 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s1d2));
1671 ASSERT_EQ(0, mount(NULL, dir_s1d2, NULL, MS_PRIVATE | MS_REC, NULL));
1672 ASSERT_EQ(0, syscall(__NR_move_mount, AT_FDCWD, dir_s1d2, AT_FDCWD,
1673 dir_s2d2, 0));
1674 ASSERT_EQ(0, umount(dir_s2d2));
1675 ASSERT_EQ(0, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1676 ASSERT_EQ(0, chdir("/"));
1677 clear_cap(_metadata, CAP_SYS_ADMIN);
1678 }
1679
TEST_F_FORK(layout1,topology_changes_with_net_and_fs)1680 TEST_F_FORK(layout1, topology_changes_with_net_and_fs)
1681 {
1682 const struct landlock_ruleset_attr ruleset_net_fs = {
1683 .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
1684 LANDLOCK_ACCESS_NET_CONNECT_TCP,
1685 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,
1686 };
1687
1688 /* Add network and filesystem restrictions. */
1689 drop_access_rights(_metadata, &ruleset_net_fs);
1690
1691 /* Mount, remount, move_mount, umount, and pivot_root checks. */
1692 set_cap(_metadata, CAP_SYS_ADMIN);
1693 ASSERT_EQ(-1, mount_opt(&mnt_tmp, dir_s1d2));
1694 ASSERT_EQ(EPERM, errno);
1695 ASSERT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_PRIVATE | MS_REC, NULL));
1696 ASSERT_EQ(EPERM, errno);
1697 ASSERT_EQ(-1, syscall(__NR_move_mount, AT_FDCWD, dir_s3d2, AT_FDCWD,
1698 dir_s2d2, 0));
1699 ASSERT_EQ(EPERM, errno);
1700 ASSERT_EQ(-1, umount(dir_s3d2));
1701 ASSERT_EQ(EPERM, errno);
1702 ASSERT_EQ(-1, syscall(__NR_pivot_root, dir_s3d2, dir_s3d3));
1703 ASSERT_EQ(EPERM, errno);
1704 clear_cap(_metadata, CAP_SYS_ADMIN);
1705 }
1706
TEST_F_FORK(layout1,release_inodes)1707 TEST_F_FORK(layout1, release_inodes)
1708 {
1709 const struct rule rules[] = {
1710 {
1711 .path = dir_s1d1,
1712 .access = ACCESS_RO,
1713 },
1714 {
1715 .path = dir_s3d2,
1716 .access = ACCESS_RO,
1717 },
1718 {
1719 .path = dir_s3d3,
1720 .access = ACCESS_RO,
1721 },
1722 {},
1723 };
1724 const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
1725
1726 /* Unmount a file hierarchy while it is being used by a ruleset. */
1727 set_cap(_metadata, CAP_SYS_ADMIN);
1728 ASSERT_EQ(0, umount(dir_s3d2));
1729 clear_cap(_metadata, CAP_SYS_ADMIN);
1730
1731 enforce_ruleset(_metadata, ruleset_fd);
1732 EXPECT_EQ(0, close(ruleset_fd));
1733
1734 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
1735 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
1736 /* This dir_s3d3 would not be allowed and does not exist anyway. */
1737 ASSERT_EQ(ENOENT, test_open(dir_s3d3, O_RDONLY));
1738 }
1739
1740 /*
1741 * This test checks that a rule on a directory used as a mount point does not
1742 * grant access to the mount covering it. It is a generalization of the bind
1743 * mount case in layout3_fs.hostfs.release_inodes that tests hidden mount points.
1744 */
TEST_F_FORK(layout1,covered_rule)1745 TEST_F_FORK(layout1, covered_rule)
1746 {
1747 const struct rule layer1[] = {
1748 {
1749 .path = dir_s3d2,
1750 .access = LANDLOCK_ACCESS_FS_READ_DIR,
1751 },
1752 {},
1753 };
1754 int ruleset_fd;
1755
1756 /* Unmount to simplify FIXTURE_TEARDOWN. */
1757 set_cap(_metadata, CAP_SYS_ADMIN);
1758 ASSERT_EQ(0, umount(dir_s3d2));
1759 clear_cap(_metadata, CAP_SYS_ADMIN);
1760
1761 /* Creates a ruleset with the future hidden directory. */
1762 ruleset_fd =
1763 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
1764
1765 /* Covers with a new mount point. */
1766 set_cap(_metadata, CAP_SYS_ADMIN);
1767 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));
1768 clear_cap(_metadata, CAP_SYS_ADMIN);
1769
1770 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1771
1772 enforce_ruleset(_metadata, ruleset_fd);
1773 ASSERT_EQ(0, close(ruleset_fd));
1774
1775 /* Checks that access to the new mount point is denied. */
1776 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
1777 }
1778
1779 enum relative_access {
1780 REL_OPEN,
1781 REL_CHDIR,
1782 REL_CHROOT_ONLY,
1783 REL_CHROOT_CHDIR,
1784 };
1785
test_relative_path(struct __test_metadata * const _metadata,const enum relative_access rel)1786 static void test_relative_path(struct __test_metadata *const _metadata,
1787 const enum relative_access rel)
1788 {
1789 /*
1790 * Common layer to check that chroot doesn't ignore it (i.e. a chroot
1791 * is not a disconnected root directory).
1792 */
1793 const struct rule layer1_base[] = {
1794 {
1795 .path = TMP_DIR,
1796 .access = ACCESS_RO,
1797 },
1798 {},
1799 };
1800 const struct rule layer2_subs[] = {
1801 {
1802 .path = dir_s1d2,
1803 .access = ACCESS_RO,
1804 },
1805 {
1806 .path = dir_s2d2,
1807 .access = ACCESS_RO,
1808 },
1809 {},
1810 };
1811 int dirfd, ruleset_fd;
1812
1813 enforce_fs(_metadata, ACCESS_RW, layer1_base);
1814
1815 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);
1816
1817 ASSERT_LE(0, ruleset_fd);
1818 switch (rel) {
1819 case REL_OPEN:
1820 case REL_CHDIR:
1821 break;
1822 case REL_CHROOT_ONLY:
1823 ASSERT_EQ(0, chdir(dir_s2d2));
1824 break;
1825 case REL_CHROOT_CHDIR:
1826 ASSERT_EQ(0, chdir(dir_s1d2));
1827 break;
1828 default:
1829 ASSERT_TRUE(false);
1830 return;
1831 }
1832
1833 set_cap(_metadata, CAP_SYS_CHROOT);
1834 enforce_ruleset(_metadata, ruleset_fd);
1835
1836 switch (rel) {
1837 case REL_OPEN:
1838 dirfd = open(dir_s1d2, O_DIRECTORY);
1839 ASSERT_LE(0, dirfd);
1840 break;
1841 case REL_CHDIR:
1842 ASSERT_EQ(0, chdir(dir_s1d2));
1843 dirfd = AT_FDCWD;
1844 break;
1845 case REL_CHROOT_ONLY:
1846 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */
1847 ASSERT_EQ(0, chroot("../../s1d1/s1d2"))
1848 {
1849 TH_LOG("Failed to chroot: %s", strerror(errno));
1850 }
1851 dirfd = AT_FDCWD;
1852 break;
1853 case REL_CHROOT_CHDIR:
1854 /* Do chroot into dir_s1d2. */
1855 ASSERT_EQ(0, chroot("."))
1856 {
1857 TH_LOG("Failed to chroot: %s", strerror(errno));
1858 }
1859 dirfd = AT_FDCWD;
1860 break;
1861 }
1862
1863 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,
1864 test_open_rel(dirfd, "..", O_RDONLY));
1865 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));
1866
1867 if (rel == REL_CHROOT_ONLY) {
1868 /* The current directory is dir_s2d2. */
1869 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));
1870 } else {
1871 /* The current directory is dir_s1d2. */
1872 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));
1873 }
1874
1875 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {
1876 /* Checks the root dir_s1d2. */
1877 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));
1878 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));
1879 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));
1880 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));
1881 }
1882
1883 if (rel != REL_CHROOT_CHDIR) {
1884 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));
1885 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));
1886 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",
1887 O_RDONLY));
1888
1889 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));
1890 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));
1891 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",
1892 O_RDONLY));
1893 }
1894
1895 if (rel == REL_OPEN)
1896 ASSERT_EQ(0, close(dirfd));
1897 ASSERT_EQ(0, close(ruleset_fd));
1898 }
1899
TEST_F_FORK(layout1,relative_open)1900 TEST_F_FORK(layout1, relative_open)
1901 {
1902 test_relative_path(_metadata, REL_OPEN);
1903 }
1904
TEST_F_FORK(layout1,relative_chdir)1905 TEST_F_FORK(layout1, relative_chdir)
1906 {
1907 test_relative_path(_metadata, REL_CHDIR);
1908 }
1909
TEST_F_FORK(layout1,relative_chroot_only)1910 TEST_F_FORK(layout1, relative_chroot_only)
1911 {
1912 test_relative_path(_metadata, REL_CHROOT_ONLY);
1913 }
1914
TEST_F_FORK(layout1,relative_chroot_chdir)1915 TEST_F_FORK(layout1, relative_chroot_chdir)
1916 {
1917 test_relative_path(_metadata, REL_CHROOT_CHDIR);
1918 }
1919
copy_file(struct __test_metadata * const _metadata,const char * const src_path,const char * const dst_path)1920 static void copy_file(struct __test_metadata *const _metadata,
1921 const char *const src_path, const char *const dst_path)
1922 {
1923 int dst_fd, src_fd;
1924 struct stat statbuf;
1925
1926 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);
1927 ASSERT_LE(0, dst_fd)
1928 {
1929 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));
1930 }
1931 src_fd = open(src_path, O_RDONLY | O_CLOEXEC);
1932 ASSERT_LE(0, src_fd)
1933 {
1934 TH_LOG("Failed to open \"%s\": %s", src_path, strerror(errno));
1935 }
1936 ASSERT_EQ(0, fstat(src_fd, &statbuf));
1937 ASSERT_EQ(statbuf.st_size,
1938 sendfile(dst_fd, src_fd, 0, statbuf.st_size));
1939 ASSERT_EQ(0, close(src_fd));
1940 ASSERT_EQ(0, close(dst_fd));
1941 }
1942
test_execute(struct __test_metadata * const _metadata,const int err,const char * const path)1943 static void test_execute(struct __test_metadata *const _metadata, const int err,
1944 const char *const path)
1945 {
1946 int status;
1947 char *const argv[] = { (char *)path, NULL };
1948 const pid_t child = fork();
1949
1950 ASSERT_LE(0, child);
1951 if (child == 0) {
1952 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))
1953 {
1954 TH_LOG("Failed to execute \"%s\": %s", path,
1955 strerror(errno));
1956 };
1957 ASSERT_EQ(err, errno);
1958 _exit(__test_passed(_metadata) ? 2 : 1);
1959 return;
1960 }
1961 ASSERT_EQ(child, waitpid(child, &status, 0));
1962 ASSERT_EQ(1, WIFEXITED(status));
1963 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))
1964 {
1965 TH_LOG("Unexpected return code for \"%s\"", path);
1966 };
1967 }
1968
test_check_exec(struct __test_metadata * const _metadata,const int err,const char * const path)1969 static void test_check_exec(struct __test_metadata *const _metadata,
1970 const int err, const char *const path)
1971 {
1972 int ret;
1973 char *const argv[] = { (char *)path, NULL };
1974
1975 ret = sys_execveat(AT_FDCWD, path, argv, NULL,
1976 AT_EMPTY_PATH | AT_EXECVE_CHECK);
1977 if (err) {
1978 EXPECT_EQ(-1, ret);
1979 EXPECT_EQ(errno, err);
1980 } else {
1981 EXPECT_EQ(0, ret);
1982 }
1983 }
1984
TEST_F_FORK(layout1,execute)1985 TEST_F_FORK(layout1, execute)
1986 {
1987 const struct rule rules[] = {
1988 {
1989 .path = dir_s1d2,
1990 .access = LANDLOCK_ACCESS_FS_EXECUTE,
1991 },
1992 {},
1993 };
1994
1995 copy_file(_metadata, bin_true, file1_s1d1);
1996 copy_file(_metadata, bin_true, file1_s1d2);
1997 copy_file(_metadata, bin_true, file1_s1d3);
1998
1999 /* Checks before file1_s1d1 being denied. */
2000 test_execute(_metadata, 0, file1_s1d1);
2001 test_check_exec(_metadata, 0, file1_s1d1);
2002
2003 enforce_fs(_metadata, rules[0].access, rules);
2004
2005 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
2006 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
2007 test_execute(_metadata, EACCES, file1_s1d1);
2008 test_check_exec(_metadata, EACCES, file1_s1d1);
2009
2010 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
2011 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
2012 test_execute(_metadata, 0, file1_s1d2);
2013 test_check_exec(_metadata, 0, file1_s1d2);
2014
2015 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
2016 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
2017 test_execute(_metadata, 0, file1_s1d3);
2018 test_check_exec(_metadata, 0, file1_s1d3);
2019 }
2020
TEST_F_FORK(layout1,umount_sandboxer)2021 TEST_F_FORK(layout1, umount_sandboxer)
2022 {
2023 int pipe_child[2], pipe_parent[2];
2024 char buf_parent;
2025 pid_t child;
2026 int status;
2027
2028 copy_file(_metadata, bin_sandbox_and_launch, file1_s3d3);
2029 ASSERT_EQ(0, pipe2(pipe_child, 0));
2030 ASSERT_EQ(0, pipe2(pipe_parent, 0));
2031
2032 child = fork();
2033 ASSERT_LE(0, child);
2034 if (child == 0) {
2035 char pipe_child_str[12], pipe_parent_str[12];
2036 char *const argv[] = { (char *)file1_s3d3,
2037 (char *)bin_wait_pipe, pipe_child_str,
2038 pipe_parent_str, NULL };
2039
2040 /* Passes the pipe FDs to the executed binary and its child. */
2041 EXPECT_EQ(0, close(pipe_child[0]));
2042 EXPECT_EQ(0, close(pipe_parent[1]));
2043 snprintf(pipe_child_str, sizeof(pipe_child_str), "%d",
2044 pipe_child[1]);
2045 snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d",
2046 pipe_parent[0]);
2047
2048 /*
2049 * We need bin_sandbox_and_launch (copied inside the mount as
2050 * file1_s3d3) to execute bin_wait_pipe (outside the mount) to
2051 * make sure the mount point will not be EBUSY because of
2052 * file1_s3d3 being in use. This avoids a potential race
2053 * condition between the following read() and umount() calls.
2054 */
2055 ASSERT_EQ(0, execve(argv[0], argv, NULL))
2056 {
2057 TH_LOG("Failed to execute \"%s\": %s", argv[0],
2058 strerror(errno));
2059 };
2060 _exit(1);
2061 return;
2062 }
2063
2064 EXPECT_EQ(0, close(pipe_child[1]));
2065 EXPECT_EQ(0, close(pipe_parent[0]));
2066
2067 /* Waits for the child to sandbox itself. */
2068 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1));
2069
2070 /* Tests that the sandboxer is tied to its mount point. */
2071 set_cap(_metadata, CAP_SYS_ADMIN);
2072 EXPECT_EQ(-1, umount(dir_s3d2));
2073 EXPECT_EQ(EBUSY, errno);
2074 clear_cap(_metadata, CAP_SYS_ADMIN);
2075
2076 /* Signals the child to launch a grandchild. */
2077 EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
2078
2079 /* Waits for the grandchild. */
2080 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1));
2081
2082 /* Tests that the domain's sandboxer is not tied to its mount point. */
2083 set_cap(_metadata, CAP_SYS_ADMIN);
2084 EXPECT_EQ(0, umount(dir_s3d2))
2085 {
2086 TH_LOG("Failed to umount \"%s\": %s", dir_s3d2,
2087 strerror(errno));
2088 };
2089 clear_cap(_metadata, CAP_SYS_ADMIN);
2090
2091 /* Signals the grandchild to terminate. */
2092 EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
2093 ASSERT_EQ(child, waitpid(child, &status, 0));
2094 ASSERT_EQ(1, WIFEXITED(status));
2095 ASSERT_EQ(0, WEXITSTATUS(status));
2096 }
2097
TEST_F_FORK(layout1,link)2098 TEST_F_FORK(layout1, link)
2099 {
2100 const struct rule layer1[] = {
2101 {
2102 .path = dir_s1d2,
2103 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2104 },
2105 {},
2106 };
2107 const struct rule layer2[] = {
2108 {
2109 .path = dir_s1d3,
2110 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2111 },
2112 {},
2113 };
2114
2115 ASSERT_EQ(0, unlink(file1_s1d1));
2116 ASSERT_EQ(0, unlink(file1_s1d2));
2117 ASSERT_EQ(0, unlink(file1_s1d3));
2118
2119 enforce_fs(_metadata, layer1[0].access, layer1);
2120
2121 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2122 ASSERT_EQ(EACCES, errno);
2123
2124 /* Denies linking because of reparenting. */
2125 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2126 ASSERT_EQ(EXDEV, errno);
2127 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2128 ASSERT_EQ(EXDEV, errno);
2129 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2130 ASSERT_EQ(EXDEV, errno);
2131
2132 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2133 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2134
2135 /* Prepares for next unlinks. */
2136 ASSERT_EQ(0, unlink(file2_s1d2));
2137 ASSERT_EQ(0, unlink(file2_s1d3));
2138
2139 enforce_fs(_metadata, layer2[0].access, layer2);
2140
2141 /* Checks that linkind doesn't require the ability to delete a file. */
2142 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
2143 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
2144 }
2145
test_rename(const char * const oldpath,const char * const newpath)2146 static int test_rename(const char *const oldpath, const char *const newpath)
2147 {
2148 if (rename(oldpath, newpath))
2149 return errno;
2150 return 0;
2151 }
2152
test_exchange(const char * const oldpath,const char * const newpath)2153 static int test_exchange(const char *const oldpath, const char *const newpath)
2154 {
2155 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))
2156 return errno;
2157 return 0;
2158 }
2159
test_renameat(int olddirfd,const char * oldpath,int newdirfd,const char * newpath)2160 static int test_renameat(int olddirfd, const char *oldpath, int newdirfd,
2161 const char *newpath)
2162 {
2163 if (renameat2(olddirfd, oldpath, newdirfd, newpath, 0))
2164 return errno;
2165 return 0;
2166 }
2167
test_exchangeat(int olddirfd,const char * oldpath,int newdirfd,const char * newpath)2168 static int test_exchangeat(int olddirfd, const char *oldpath, int newdirfd,
2169 const char *newpath)
2170 {
2171 if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_EXCHANGE))
2172 return errno;
2173 return 0;
2174 }
2175
TEST_F_FORK(layout1,rename_file)2176 TEST_F_FORK(layout1, rename_file)
2177 {
2178 const struct rule rules[] = {
2179 {
2180 .path = dir_s1d3,
2181 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2182 },
2183 {
2184 .path = dir_s2d2,
2185 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2186 },
2187 {},
2188 };
2189
2190 ASSERT_EQ(0, unlink(file1_s1d2));
2191
2192 enforce_fs(_metadata, rules[0].access, rules);
2193
2194 /*
2195 * Tries to replace a file, from a directory that allows file removal,
2196 * but to a different directory (which also allows file removal).
2197 */
2198 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));
2199 ASSERT_EQ(EXDEV, errno);
2200 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,
2201 RENAME_EXCHANGE));
2202 ASSERT_EQ(EXDEV, errno);
2203 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2204 RENAME_EXCHANGE));
2205 ASSERT_EQ(EXDEV, errno);
2206
2207 /*
2208 * Tries to replace a file, from a directory that denies file removal,
2209 * to a different directory (which allows file removal).
2210 */
2211 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2212 ASSERT_EQ(EACCES, errno);
2213 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,
2214 RENAME_EXCHANGE));
2215 ASSERT_EQ(EACCES, errno);
2216 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,
2217 RENAME_EXCHANGE));
2218 ASSERT_EQ(EXDEV, errno);
2219
2220 /* Exchanges files and directories that partially allow removal. */
2221 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,
2222 RENAME_EXCHANGE));
2223 ASSERT_EQ(EACCES, errno);
2224 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */
2225 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));
2226 ASSERT_EQ(EACCES, errno);
2227 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,
2228 RENAME_EXCHANGE));
2229 ASSERT_EQ(EACCES, errno);
2230 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */
2231 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
2232 ASSERT_EQ(EACCES, errno);
2233
2234 /* Renames files with different parents. */
2235 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2236 ASSERT_EQ(EXDEV, errno);
2237 ASSERT_EQ(0, unlink(file1_s1d3));
2238 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2239 ASSERT_EQ(EACCES, errno);
2240
2241 /* Exchanges and renames files with same parent. */
2242 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,
2243 RENAME_EXCHANGE));
2244 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));
2245
2246 /* Exchanges files and directories with same parent, twice. */
2247 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2248 RENAME_EXCHANGE));
2249 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2250 RENAME_EXCHANGE));
2251 }
2252
TEST_F_FORK(layout1,rename_whiteout_denied)2253 TEST_F_FORK(layout1, rename_whiteout_denied)
2254 {
2255 /* The affected file is a FIFO. */
2256 ASSERT_EQ(0, unlink(file1_s3d3));
2257 ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
2258
2259 /* Deny MAKE_REG, but allow MAKE_FIFO. */
2260 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, NULL);
2261
2262 /*
2263 * Try to rename a file with RENAME_WHITEOUT.
2264 * file1_s3d3 is in dir_s3d2 (tmpfs), so it supports RENAME_WHITEOUT.
2265 * Denied, because whiteout creation is guarded with MAKE_REG.
2266 */
2267 EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
2268 TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
2269 EXPECT_EQ(EACCES, errno);
2270 }
2271
is_whiteout(const char * const path)2272 static bool is_whiteout(const char *const path)
2273 {
2274 struct stat st;
2275
2276 if (stat(path, &st) == -1)
2277 return false;
2278
2279 return S_ISCHR(st.st_mode) && st.st_rdev == makedev(0, 0);
2280 }
2281
is_fifo(const char * const path)2282 static bool is_fifo(const char *const path)
2283 {
2284 struct stat st;
2285
2286 return stat(path, &st) == 0 && S_ISFIFO(st.st_mode);
2287 }
2288
is_missing(const char * const path)2289 static bool is_missing(const char *const path)
2290 {
2291 struct stat st;
2292
2293 return stat(path, &st) == -1 && errno == ENOENT;
2294 }
2295
TEST_F_FORK(layout1,rename_whiteout_allowed)2296 TEST_F_FORK(layout1, rename_whiteout_allowed)
2297 {
2298 const struct rule rules[] = {
2299 {
2300 .path = dir_s3d3,
2301 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2302 },
2303 {},
2304 };
2305
2306 /* The affected file is a FIFO. */
2307 ASSERT_EQ(0, unlink(file1_s3d3));
2308 ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
2309
2310 /* Allow MAKE_REG below dir_s3d3. */
2311 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, rules);
2312
2313 /*
2314 * Rename a file with RENAME_WHITEOUT within the same directory.
2315 * Allowed, because MAKE_REG is granted for the whiteout object which
2316 * gets created in the source location.
2317 */
2318 EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
2319 TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
2320
2321 /* A whiteout object took the place of the moved FIFO. */
2322 EXPECT_TRUE(is_whiteout(file1_s3d3));
2323 EXPECT_TRUE(is_fifo(TMP_DIR "/s3d1/s3d2/s3d3/f2"));
2324 }
2325
TEST_F_FORK(layout1,rename_whiteout_reparenting)2326 TEST_F_FORK(layout1, rename_whiteout_reparenting)
2327 {
2328 const struct rule rules[] = {
2329 {
2330 .path = dir_s3d2,
2331 .access = LANDLOCK_ACCESS_FS_REFER,
2332 },
2333 {
2334 .path = dir_s3d3,
2335 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2336 },
2337 {},
2338 };
2339
2340 /* The moved files are FIFOs. */
2341 ASSERT_EQ(0, unlink(file1_s3d3));
2342 ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
2343 ASSERT_EQ(0, unlink(file1_s3d4));
2344 ASSERT_EQ(0, mknod(file1_s3d4, S_IFIFO | 0600, 0));
2345
2346 /* Allow REFER below dir_s3d2, but MAKE_REG only below dir_s3d3. */
2347 enforce_fs(_metadata,
2348 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
2349 rules);
2350
2351 /*
2352 * The whiteout object is created in the source directory: Moving the
2353 * FIFO out of dir_s3d4 is denied because MAKE_REG is not granted
2354 * there, even though it is granted in the destination directory
2355 * dir_s3d3.
2356 */
2357 EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d4, AT_FDCWD,
2358 TMP_DIR "/s3d1/s3d2/s3d3/f2", RENAME_WHITEOUT));
2359 EXPECT_EQ(EACCES, errno);
2360
2361 /*
2362 * Moving the FIFO out of dir_s3d3 is allowed, because MAKE_REG is
2363 * granted there for the created whiteout object.
2364 */
2365 EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD,
2366 TMP_DIR "/s3d1/s3d2/s3d4/f2", RENAME_WHITEOUT));
2367
2368 /* A whiteout object took the place of the moved FIFO. */
2369 EXPECT_TRUE(is_whiteout(file1_s3d3));
2370 EXPECT_TRUE(is_fifo(TMP_DIR "/s3d1/s3d2/s3d4/f2"));
2371 }
2372
TEST_F_FORK(layout1,rename_whiteout_exchange)2373 TEST_F_FORK(layout1, rename_whiteout_exchange)
2374 {
2375 const char *const whiteout_s3d3 = TMP_DIR "/s3d1/s3d2/s3d3/f2";
2376 const struct rule rules[] = {
2377 {
2378 .path = dir_s3d2,
2379 .access = LANDLOCK_ACCESS_FS_REFER,
2380 },
2381 {
2382 .path = dir_s3d3,
2383 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2384 },
2385 {},
2386 };
2387
2388 /* The exchanged files are FIFOs and an existing whiteout object. */
2389 ASSERT_EQ(0, unlink(file1_s3d3));
2390 ASSERT_EQ(0, mknod(file1_s3d3, S_IFIFO | 0600, 0));
2391 ASSERT_EQ(0, mknod(whiteout_s3d3, S_IFCHR | 0600, makedev(0, 0)));
2392 ASSERT_EQ(0, unlink(file1_s3d4));
2393 ASSERT_EQ(0, mknod(file1_s3d4, S_IFIFO | 0600, 0));
2394
2395 /* Allow REFER below dir_s3d2, but MAKE_REG only below dir_s3d3. */
2396 enforce_fs(_metadata,
2397 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
2398 rules);
2399
2400 /*
2401 * With RENAME_EXCHANGE, the whiteout object moves into the source
2402 * directory of the rename: Exchanging the FIFO in dir_s3d4 with the
2403 * whiteout object is denied because MAKE_REG is not granted in
2404 * dir_s3d4, even though it is granted in the whiteout object's own
2405 * directory dir_s3d3.
2406 */
2407 EXPECT_EQ(-1, renameat2(AT_FDCWD, file1_s3d4, AT_FDCWD, whiteout_s3d3,
2408 RENAME_EXCHANGE));
2409 EXPECT_EQ(EACCES, errno);
2410
2411 /*
2412 * Exchanging the FIFO in dir_s3d3 with the whiteout object is
2413 * allowed, because MAKE_REG is granted in the directory into which
2414 * the whiteout object moves.
2415 */
2416 EXPECT_EQ(0, renameat2(AT_FDCWD, file1_s3d3, AT_FDCWD, whiteout_s3d3,
2417 RENAME_EXCHANGE));
2418
2419 /* The FIFO and the whiteout object swapped places. */
2420 EXPECT_TRUE(is_whiteout(file1_s3d3));
2421 EXPECT_TRUE(is_fifo(whiteout_s3d3));
2422 }
2423
TEST_F_FORK(layout1,rename_dir)2424 TEST_F_FORK(layout1, rename_dir)
2425 {
2426 const struct rule rules[] = {
2427 {
2428 .path = dir_s1d2,
2429 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2430 },
2431 {
2432 .path = dir_s2d1,
2433 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2434 },
2435 {},
2436 };
2437
2438 /* Empties dir_s1d3 to allow renaming. */
2439 ASSERT_EQ(0, unlink(file1_s1d3));
2440 ASSERT_EQ(0, unlink(file2_s1d3));
2441
2442 enforce_fs(_metadata, rules[0].access, rules);
2443
2444 /* Exchanges and renames directory to a different parent. */
2445 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2446 RENAME_EXCHANGE));
2447 ASSERT_EQ(EXDEV, errno);
2448 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));
2449 ASSERT_EQ(EXDEV, errno);
2450 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2451 RENAME_EXCHANGE));
2452 ASSERT_EQ(EXDEV, errno);
2453
2454 /*
2455 * Exchanges directory to the same parent, which doesn't allow
2456 * directory removal.
2457 */
2458 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,
2459 RENAME_EXCHANGE));
2460 ASSERT_EQ(EACCES, errno);
2461 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */
2462 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));
2463 ASSERT_EQ(EACCES, errno);
2464 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,
2465 RENAME_EXCHANGE));
2466 ASSERT_EQ(EACCES, errno);
2467 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */
2468 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
2469 ASSERT_EQ(EACCES, errno);
2470
2471 /*
2472 * Exchanges and renames directory to the same parent, which allows
2473 * directory removal.
2474 */
2475 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,
2476 RENAME_EXCHANGE));
2477 ASSERT_EQ(0, unlink(dir_s1d3));
2478 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2479 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));
2480 ASSERT_EQ(0, rmdir(dir_s1d3));
2481 }
2482
TEST_F_FORK(layout1,reparent_refer)2483 TEST_F_FORK(layout1, reparent_refer)
2484 {
2485 const struct rule layer1[] = {
2486 {
2487 .path = dir_s1d2,
2488 .access = LANDLOCK_ACCESS_FS_REFER,
2489 },
2490 {
2491 .path = dir_s2d2,
2492 .access = LANDLOCK_ACCESS_FS_REFER,
2493 },
2494 {},
2495 };
2496
2497 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
2498
2499 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));
2500 ASSERT_EQ(EXDEV, errno);
2501 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));
2502 ASSERT_EQ(EXDEV, errno);
2503 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
2504 ASSERT_EQ(EXDEV, errno);
2505
2506 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));
2507 ASSERT_EQ(EXDEV, errno);
2508 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));
2509 ASSERT_EQ(EXDEV, errno);
2510 /*
2511 * Moving should only be allowed when the source and the destination
2512 * parent directory have REFER.
2513 */
2514 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));
2515 ASSERT_EQ(ENOTEMPTY, errno);
2516 ASSERT_EQ(0, unlink(file1_s2d3));
2517 ASSERT_EQ(0, unlink(file2_s2d3));
2518 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));
2519 }
2520
2521 /* Checks renames beneath dir_s1d1. */
refer_denied_by_default(struct __test_metadata * const _metadata,const struct rule layer1[],const int layer1_err,const struct rule layer2[])2522 static void refer_denied_by_default(struct __test_metadata *const _metadata,
2523 const struct rule layer1[],
2524 const int layer1_err,
2525 const struct rule layer2[])
2526 {
2527 ASSERT_EQ(0, unlink(file1_s1d2));
2528
2529 enforce_fs(_metadata, layer1[0].access, layer1);
2530
2531 /*
2532 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to
2533 * layer1_err), then it allows some different-parent renames and links.
2534 */
2535 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));
2536 if (layer1_err == 0)
2537 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));
2538 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));
2539 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));
2540
2541 enforce_fs(_metadata, layer2[0].access, layer2);
2542
2543 /*
2544 * Now, either the first or the second layer does not handle
2545 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent
2546 * renames and links are denied, thus making the layer handling
2547 * LANDLOCK_ACCESS_FS_REFER null and void.
2548 */
2549 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));
2550 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));
2551 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));
2552 }
2553
2554 const struct rule layer_dir_s1d1_refer[] = {
2555 {
2556 .path = dir_s1d1,
2557 .access = LANDLOCK_ACCESS_FS_REFER,
2558 },
2559 {},
2560 };
2561
2562 const struct rule layer_dir_s1d1_execute[] = {
2563 {
2564 /* Matches a parent directory. */
2565 .path = dir_s1d1,
2566 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2567 },
2568 {},
2569 };
2570
2571 const struct rule layer_dir_s2d1_execute[] = {
2572 {
2573 /* Does not match a parent directory. */
2574 .path = dir_s2d1,
2575 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2576 },
2577 {},
2578 };
2579
2580 /*
2581 * Tests precedence over renames: denied by default for different parent
2582 * directories, *with* a rule matching a parent directory, but not directly
2583 * denying access (with MAKE_REG nor REMOVE).
2584 */
TEST_F_FORK(layout1,refer_denied_by_default1)2585 TEST_F_FORK(layout1, refer_denied_by_default1)
2586 {
2587 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
2588 layer_dir_s1d1_execute);
2589 }
2590
2591 /*
2592 * Same test but this time turning around the ABI version order: the first
2593 * layer does not handle LANDLOCK_ACCESS_FS_REFER.
2594 */
TEST_F_FORK(layout1,refer_denied_by_default2)2595 TEST_F_FORK(layout1, refer_denied_by_default2)
2596 {
2597 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,
2598 layer_dir_s1d1_refer);
2599 }
2600
2601 /*
2602 * Tests precedence over renames: denied by default for different parent
2603 * directories, *without* a rule matching a parent directory, but not directly
2604 * denying access (with MAKE_REG nor REMOVE).
2605 */
TEST_F_FORK(layout1,refer_denied_by_default3)2606 TEST_F_FORK(layout1, refer_denied_by_default3)
2607 {
2608 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
2609 layer_dir_s2d1_execute);
2610 }
2611
2612 /*
2613 * Same test but this time turning around the ABI version order: the first
2614 * layer does not handle LANDLOCK_ACCESS_FS_REFER.
2615 */
TEST_F_FORK(layout1,refer_denied_by_default4)2616 TEST_F_FORK(layout1, refer_denied_by_default4)
2617 {
2618 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,
2619 layer_dir_s1d1_refer);
2620 }
2621
2622 /*
2623 * Tests walking through a denied root mount.
2624 */
TEST_F_FORK(layout1,refer_mount_root_deny)2625 TEST_F_FORK(layout1, refer_mount_root_deny)
2626 {
2627 int root_fd;
2628
2629 /* Creates a mount object from a non-mount point. */
2630 set_cap(_metadata, CAP_SYS_ADMIN);
2631 root_fd =
2632 open_tree(AT_FDCWD, dir_s1d1,
2633 AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
2634 clear_cap(_metadata, CAP_SYS_ADMIN);
2635 ASSERT_LE(0, root_fd);
2636
2637 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, NULL);
2638
2639 /* Link denied by Landlock: EACCES. */
2640 EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0));
2641 EXPECT_EQ(EACCES, errno);
2642
2643 /* renameat2() always returns EBUSY. */
2644 EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0));
2645 EXPECT_EQ(EBUSY, errno);
2646
2647 EXPECT_EQ(0, close(root_fd));
2648 }
2649
TEST_F_FORK(layout1,refer_part_mount_tree_is_allowed)2650 TEST_F_FORK(layout1, refer_part_mount_tree_is_allowed)
2651 {
2652 const struct rule layer1[] = {
2653 {
2654 /* Parent mount point. */
2655 .path = dir_s3d1,
2656 .access = LANDLOCK_ACCESS_FS_REFER |
2657 LANDLOCK_ACCESS_FS_MAKE_REG,
2658 },
2659 {
2660 /*
2661 * Removing the source file is allowed because its
2662 * access rights are already a superset of the
2663 * destination.
2664 */
2665 .path = dir_s3d4,
2666 .access = LANDLOCK_ACCESS_FS_REFER |
2667 LANDLOCK_ACCESS_FS_MAKE_REG |
2668 LANDLOCK_ACCESS_FS_REMOVE_FILE,
2669 },
2670 {},
2671 };
2672
2673 ASSERT_EQ(0, unlink(file1_s3d3));
2674 enforce_fs(_metadata,
2675 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG |
2676 LANDLOCK_ACCESS_FS_REMOVE_FILE,
2677 layer1);
2678
2679 ASSERT_EQ(0, rename(file1_s3d4, file1_s3d3));
2680 }
2681
TEST_F_FORK(layout1,reparent_link)2682 TEST_F_FORK(layout1, reparent_link)
2683 {
2684 const struct rule layer1[] = {
2685 {
2686 .path = dir_s1d2,
2687 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2688 },
2689 {
2690 .path = dir_s1d3,
2691 .access = LANDLOCK_ACCESS_FS_REFER,
2692 },
2693 {
2694 .path = dir_s2d2,
2695 .access = LANDLOCK_ACCESS_FS_REFER,
2696 },
2697 {
2698 .path = dir_s2d3,
2699 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2700 },
2701 {},
2702 };
2703
2704 enforce_fs(_metadata,
2705 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
2706 layer1);
2707
2708 ASSERT_EQ(0, unlink(file1_s1d1));
2709 ASSERT_EQ(0, unlink(file1_s1d2));
2710 ASSERT_EQ(0, unlink(file1_s1d3));
2711
2712 /* Denies linking because of missing MAKE_REG. */
2713 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2714 ASSERT_EQ(EACCES, errno);
2715 /* Denies linking because of missing source and destination REFER. */
2716 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2717 ASSERT_EQ(EXDEV, errno);
2718 /* Denies linking because of missing source REFER. */
2719 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));
2720 ASSERT_EQ(EXDEV, errno);
2721
2722 /* Denies linking because of missing MAKE_REG. */
2723 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));
2724 ASSERT_EQ(EACCES, errno);
2725 /* Denies linking because of missing destination REFER. */
2726 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));
2727 ASSERT_EQ(EXDEV, errno);
2728
2729 /* Allows linking because of REFER and MAKE_REG. */
2730 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));
2731 ASSERT_EQ(0, unlink(file1_s2d2));
2732 /* Reverse linking denied because of missing MAKE_REG. */
2733 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));
2734 ASSERT_EQ(EACCES, errno);
2735 ASSERT_EQ(0, unlink(file1_s2d3));
2736 /* Checks reverse linking. */
2737 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));
2738 ASSERT_EQ(0, unlink(file1_s1d3));
2739
2740 /*
2741 * This is OK for a file link, but it should not be allowed for a
2742 * directory rename (because of the superset of access rights.
2743 */
2744 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));
2745 ASSERT_EQ(0, unlink(file1_s1d3));
2746
2747 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2748 ASSERT_EQ(EXDEV, errno);
2749 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2750 ASSERT_EQ(EXDEV, errno);
2751
2752 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2753 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2754 }
2755
TEST_F_FORK(layout1,reparent_rename)2756 TEST_F_FORK(layout1, reparent_rename)
2757 {
2758 /* Same rules as for reparent_link. */
2759 const struct rule layer1[] = {
2760 {
2761 .path = dir_s1d2,
2762 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2763 },
2764 {
2765 .path = dir_s1d3,
2766 .access = LANDLOCK_ACCESS_FS_REFER,
2767 },
2768 {
2769 .path = dir_s2d2,
2770 .access = LANDLOCK_ACCESS_FS_REFER,
2771 },
2772 {
2773 .path = dir_s2d3,
2774 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2775 },
2776 {},
2777 };
2778
2779 enforce_fs(_metadata,
2780 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
2781 layer1);
2782
2783 ASSERT_EQ(0, unlink(file1_s1d2));
2784 ASSERT_EQ(0, unlink(file1_s1d3));
2785
2786 /* Denies renaming because of missing MAKE_REG. */
2787 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,
2788 RENAME_EXCHANGE));
2789 ASSERT_EQ(EACCES, errno);
2790 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,
2791 RENAME_EXCHANGE));
2792 ASSERT_EQ(EACCES, errno);
2793 ASSERT_EQ(0, unlink(file1_s1d1));
2794 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
2795 ASSERT_EQ(EACCES, errno);
2796 /* Even denies same file exchange. */
2797 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,
2798 RENAME_EXCHANGE));
2799 ASSERT_EQ(EACCES, errno);
2800
2801 /* Denies renaming because of missing source and destination REFER. */
2802 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));
2803 ASSERT_EQ(EXDEV, errno);
2804 /*
2805 * Denies renaming because of missing MAKE_REG, source and destination
2806 * REFER.
2807 */
2808 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,
2809 RENAME_EXCHANGE));
2810 ASSERT_EQ(EACCES, errno);
2811 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,
2812 RENAME_EXCHANGE));
2813 ASSERT_EQ(EACCES, errno);
2814
2815 /* Denies renaming because of missing source REFER. */
2816 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2817 ASSERT_EQ(EXDEV, errno);
2818 /* Denies renaming because of missing MAKE_REG. */
2819 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,
2820 RENAME_EXCHANGE));
2821 ASSERT_EQ(EACCES, errno);
2822
2823 /* Denies renaming because of missing MAKE_REG. */
2824 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));
2825 ASSERT_EQ(EACCES, errno);
2826 /* Denies renaming because of missing destination REFER*/
2827 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2828 ASSERT_EQ(EXDEV, errno);
2829
2830 /* Denies exchange because of one missing MAKE_REG. */
2831 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,
2832 RENAME_EXCHANGE));
2833 ASSERT_EQ(EACCES, errno);
2834 /* Allows renaming because of REFER and MAKE_REG. */
2835 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));
2836
2837 /* Reverse renaming denied because of missing MAKE_REG. */
2838 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));
2839 ASSERT_EQ(EACCES, errno);
2840 ASSERT_EQ(0, unlink(file1_s2d3));
2841 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2842
2843 /* Tests reverse renaming. */
2844 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2845 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,
2846 RENAME_EXCHANGE));
2847 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2848
2849 /*
2850 * This is OK for a file rename, but it should not be allowed for a
2851 * directory rename (because of the superset of access rights).
2852 */
2853 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2854 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2855
2856 /*
2857 * Tests superset restrictions applied to directories. Not only the
2858 * dir_s2d3's parent (dir_s2d2) should be taken into account but also
2859 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right
2860 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided
2861 * directly by the moved dir_s2d3.
2862 */
2863 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));
2864 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));
2865 /*
2866 * The first rename is allowed but not the exchange because dir_s1d3's
2867 * parent (dir_s1d2) doesn't have REFER.
2868 */
2869 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2870 RENAME_EXCHANGE));
2871 ASSERT_EQ(EXDEV, errno);
2872 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,
2873 RENAME_EXCHANGE));
2874 ASSERT_EQ(EXDEV, errno);
2875 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));
2876 ASSERT_EQ(EXDEV, errno);
2877
2878 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));
2879 ASSERT_EQ(EXDEV, errno);
2880 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));
2881 ASSERT_EQ(EXDEV, errno);
2882
2883 /* Renaming in the same directory is always allowed. */
2884 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));
2885 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));
2886
2887 ASSERT_EQ(0, unlink(file1_s1d2));
2888 /* Denies because of missing source MAKE_REG and destination REFER. */
2889 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));
2890 ASSERT_EQ(EXDEV, errno);
2891
2892 ASSERT_EQ(0, unlink(file1_s1d3));
2893 /* Denies because of missing source MAKE_REG and REFER. */
2894 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));
2895 ASSERT_EQ(EXDEV, errno);
2896 }
2897
2898 static void
reparent_exdev_layers_enforce1(struct __test_metadata * const _metadata)2899 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)
2900 {
2901 const struct rule layer1[] = {
2902 {
2903 .path = dir_s1d2,
2904 .access = LANDLOCK_ACCESS_FS_REFER,
2905 },
2906 {
2907 /* Interesting for the layer2 tests. */
2908 .path = dir_s1d3,
2909 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2910 },
2911 {
2912 .path = dir_s2d2,
2913 .access = LANDLOCK_ACCESS_FS_REFER,
2914 },
2915 {
2916 .path = dir_s2d3,
2917 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2918 },
2919 {},
2920 };
2921 enforce_fs(_metadata,
2922 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
2923 layer1);
2924 }
2925
2926 static void
reparent_exdev_layers_enforce2(struct __test_metadata * const _metadata)2927 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)
2928 {
2929 const struct rule layer2[] = {
2930 {
2931 .path = dir_s2d3,
2932 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,
2933 },
2934 {},
2935 };
2936 /*
2937 * Same checks as before but with a second layer and a new MAKE_DIR
2938 * rule (and no explicit handling of REFER).
2939 */
2940 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);
2941 }
2942
TEST_F_FORK(layout1,reparent_exdev_layers_rename1)2943 TEST_F_FORK(layout1, reparent_exdev_layers_rename1)
2944 {
2945 ASSERT_EQ(0, unlink(file1_s2d2));
2946 ASSERT_EQ(0, unlink(file1_s2d3));
2947
2948 reparent_exdev_layers_enforce1(_metadata);
2949
2950 /*
2951 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock
2952 * because it doesn't inherit new access rights.
2953 */
2954 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
2955 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
2956
2957 /*
2958 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it
2959 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is
2960 * already allowed for dir_s1d3.
2961 */
2962 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));
2963 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));
2964
2965 /*
2966 * However, moving the file1_s1d3 file below dir_s2d3 is allowed
2967 * because it cannot inherit MAKE_REG right (which is dedicated to
2968 * directories).
2969 */
2970 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2971
2972 reparent_exdev_layers_enforce2(_metadata);
2973
2974 /*
2975 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because
2976 * MAKE_DIR is not tied to dir_s2d2.
2977 */
2978 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));
2979 ASSERT_EQ(EACCES, errno);
2980
2981 /*
2982 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it
2983 * would grants MAKE_REG and MAKE_DIR rights to it.
2984 */
2985 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
2986 ASSERT_EQ(EXDEV, errno);
2987
2988 /*
2989 * Moving the file2_s1d3 file below dir_s2d3 is denied because the
2990 * second layer does not handle REFER, which is always denied by
2991 * default.
2992 */
2993 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));
2994 ASSERT_EQ(EXDEV, errno);
2995 }
2996
TEST_F_FORK(layout1,reparent_exdev_layers_rename2)2997 TEST_F_FORK(layout1, reparent_exdev_layers_rename2)
2998 {
2999 reparent_exdev_layers_enforce1(_metadata);
3000
3001 /* Checks EACCES predominance over EXDEV. */
3002 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
3003 ASSERT_EQ(EACCES, errno);
3004 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));
3005 ASSERT_EQ(EACCES, errno);
3006 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
3007 ASSERT_EQ(EXDEV, errno);
3008 /* Modify layout! */
3009 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));
3010
3011 /* Without REFER source. */
3012 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
3013 ASSERT_EQ(EXDEV, errno);
3014 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
3015 ASSERT_EQ(EXDEV, errno);
3016
3017 reparent_exdev_layers_enforce2(_metadata);
3018
3019 /* Checks EACCES predominance over EXDEV. */
3020 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
3021 ASSERT_EQ(EACCES, errno);
3022 /* Checks with actual file2_s1d2. */
3023 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));
3024 ASSERT_EQ(EACCES, errno);
3025 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
3026 ASSERT_EQ(EXDEV, errno);
3027 /*
3028 * Modifying the layout is now denied because the second layer does not
3029 * handle REFER, which is always denied by default.
3030 */
3031 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
3032 ASSERT_EQ(EXDEV, errno);
3033
3034 /* Without REFER source, EACCES wins over EXDEV. */
3035 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
3036 ASSERT_EQ(EACCES, errno);
3037 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
3038 ASSERT_EQ(EACCES, errno);
3039 }
3040
TEST_F_FORK(layout1,reparent_exdev_layers_exchange1)3041 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1)
3042 {
3043 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =
3044 file2_s2d3;
3045
3046 ASSERT_EQ(0, unlink(file1_s1d2));
3047 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
3048 ASSERT_EQ(0, unlink(file2_s2d3));
3049 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
3050
3051 reparent_exdev_layers_enforce1(_metadata);
3052
3053 /* Error predominance with file exchange: returns EXDEV and EACCES. */
3054 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
3055 RENAME_EXCHANGE));
3056 ASSERT_EQ(EACCES, errno);
3057 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
3058 RENAME_EXCHANGE));
3059 ASSERT_EQ(EACCES, errno);
3060
3061 /*
3062 * Checks with directories which creation could be allowed, but denied
3063 * because of access rights that would be inherited.
3064 */
3065 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
3066 dir_file2_s2d3, RENAME_EXCHANGE));
3067 ASSERT_EQ(EXDEV, errno);
3068 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
3069 dir_file1_s1d2, RENAME_EXCHANGE));
3070 ASSERT_EQ(EXDEV, errno);
3071
3072 /* Checks with same access rights. */
3073 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
3074 RENAME_EXCHANGE));
3075 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
3076 RENAME_EXCHANGE));
3077
3078 /* Checks with different (child-only) access rights. */
3079 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
3080 RENAME_EXCHANGE));
3081 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
3082 RENAME_EXCHANGE));
3083
3084 /*
3085 * Checks that exchange between file and directory are consistent.
3086 *
3087 * Moving a file (file1_s2d2) to a directory which only grants more
3088 * directory-related access rights is allowed, and at the same time
3089 * moving a directory (dir_file2_s2d3) to another directory which
3090 * grants less access rights is allowed too.
3091 *
3092 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.
3093 */
3094 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3095 RENAME_EXCHANGE));
3096 /*
3097 * However, moving back the directory is denied because it would get
3098 * more access rights than the current state and because file creation
3099 * is forbidden (in dir_s2d2).
3100 */
3101 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3102 RENAME_EXCHANGE));
3103 ASSERT_EQ(EACCES, errno);
3104 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3105 RENAME_EXCHANGE));
3106 ASSERT_EQ(EACCES, errno);
3107
3108 reparent_exdev_layers_enforce2(_metadata);
3109
3110 /* Error predominance with file exchange: returns EXDEV and EACCES. */
3111 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
3112 RENAME_EXCHANGE));
3113 ASSERT_EQ(EACCES, errno);
3114 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
3115 RENAME_EXCHANGE));
3116 ASSERT_EQ(EACCES, errno);
3117
3118 /* Checks with directories which creation is now denied. */
3119 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
3120 dir_file2_s2d3, RENAME_EXCHANGE));
3121 ASSERT_EQ(EACCES, errno);
3122 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
3123 dir_file1_s1d2, RENAME_EXCHANGE));
3124 ASSERT_EQ(EACCES, errno);
3125
3126 /* Checks with different (child-only) access rights. */
3127 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
3128 RENAME_EXCHANGE));
3129 /* Denied because of MAKE_DIR. */
3130 ASSERT_EQ(EACCES, errno);
3131 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
3132 RENAME_EXCHANGE));
3133 ASSERT_EQ(EACCES, errno);
3134
3135 /* Checks with different (child-only) access rights. */
3136 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
3137 RENAME_EXCHANGE));
3138 /* Denied because of MAKE_DIR. */
3139 ASSERT_EQ(EACCES, errno);
3140 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
3141 RENAME_EXCHANGE));
3142 ASSERT_EQ(EACCES, errno);
3143
3144 /* See layout1.reparent_exdev_layers_exchange2 for complement. */
3145 }
3146
TEST_F_FORK(layout1,reparent_exdev_layers_exchange2)3147 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2)
3148 {
3149 const char *const dir_file2_s2d3 = file2_s2d3;
3150
3151 ASSERT_EQ(0, unlink(file2_s2d3));
3152 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
3153
3154 reparent_exdev_layers_enforce1(_metadata);
3155 reparent_exdev_layers_enforce2(_metadata);
3156
3157 /* Checks that exchange between file and directory are consistent. */
3158 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3159 RENAME_EXCHANGE));
3160 ASSERT_EQ(EACCES, errno);
3161 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3162 RENAME_EXCHANGE));
3163 ASSERT_EQ(EACCES, errno);
3164 }
3165
TEST_F_FORK(layout1,reparent_exdev_layers_exchange3)3166 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3)
3167 {
3168 const char *const dir_file2_s2d3 = file2_s2d3;
3169
3170 ASSERT_EQ(0, unlink(file2_s2d3));
3171 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
3172
3173 reparent_exdev_layers_enforce1(_metadata);
3174
3175 /*
3176 * Checks that exchange between file and directory are consistent,
3177 * including with inverted arguments (see
3178 * layout1.reparent_exdev_layers_exchange1).
3179 */
3180 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3181 RENAME_EXCHANGE));
3182 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3183 RENAME_EXCHANGE));
3184 ASSERT_EQ(EACCES, errno);
3185 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3186 RENAME_EXCHANGE));
3187 ASSERT_EQ(EACCES, errno);
3188 }
3189
TEST_F_FORK(layout1,reparent_remove)3190 TEST_F_FORK(layout1, reparent_remove)
3191 {
3192 const struct rule layer1[] = {
3193 {
3194 .path = dir_s1d1,
3195 .access = LANDLOCK_ACCESS_FS_REFER |
3196 LANDLOCK_ACCESS_FS_REMOVE_DIR,
3197 },
3198 {
3199 .path = dir_s1d2,
3200 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
3201 },
3202 {
3203 .path = dir_s2d1,
3204 .access = LANDLOCK_ACCESS_FS_REFER |
3205 LANDLOCK_ACCESS_FS_REMOVE_FILE,
3206 },
3207 {},
3208 };
3209
3210 enforce_fs(_metadata,
3211 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |
3212 LANDLOCK_ACCESS_FS_REMOVE_FILE,
3213 layer1);
3214
3215 /* Access denied because of wrong/swapped remove file/dir. */
3216 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));
3217 ASSERT_EQ(EACCES, errno);
3218 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));
3219 ASSERT_EQ(EACCES, errno);
3220 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,
3221 RENAME_EXCHANGE));
3222 ASSERT_EQ(EACCES, errno);
3223 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,
3224 RENAME_EXCHANGE));
3225 ASSERT_EQ(EACCES, errno);
3226
3227 /* Access allowed thanks to the matching rights. */
3228 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));
3229 ASSERT_EQ(EISDIR, errno);
3230 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));
3231 ASSERT_EQ(ENOTDIR, errno);
3232 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
3233 ASSERT_EQ(ENOTDIR, errno);
3234 ASSERT_EQ(0, unlink(file1_s2d1));
3235 ASSERT_EQ(0, unlink(file1_s1d3));
3236 ASSERT_EQ(0, unlink(file2_s1d3));
3237 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));
3238
3239 /* Effectively removes a file and a directory by exchanging them. */
3240 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
3241 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
3242 RENAME_EXCHANGE));
3243 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
3244 RENAME_EXCHANGE));
3245 ASSERT_EQ(EACCES, errno);
3246 }
3247
TEST_F_FORK(layout1,reparent_dom_superset)3248 TEST_F_FORK(layout1, reparent_dom_superset)
3249 {
3250 const struct rule layer1[] = {
3251 {
3252 .path = dir_s1d2,
3253 .access = LANDLOCK_ACCESS_FS_REFER,
3254 },
3255 {
3256 .path = file1_s1d2,
3257 .access = LANDLOCK_ACCESS_FS_EXECUTE,
3258 },
3259 {
3260 .path = dir_s1d3,
3261 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK |
3262 LANDLOCK_ACCESS_FS_EXECUTE,
3263 },
3264 {
3265 .path = dir_s2d2,
3266 .access = LANDLOCK_ACCESS_FS_REFER |
3267 LANDLOCK_ACCESS_FS_EXECUTE |
3268 LANDLOCK_ACCESS_FS_MAKE_SOCK,
3269 },
3270 {
3271 .path = dir_s2d3,
3272 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3273 LANDLOCK_ACCESS_FS_MAKE_FIFO,
3274 },
3275 {},
3276 };
3277
3278 enforce_fs(_metadata,
3279 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE |
3280 LANDLOCK_ACCESS_FS_MAKE_SOCK |
3281 LANDLOCK_ACCESS_FS_READ_FILE |
3282 LANDLOCK_ACCESS_FS_MAKE_FIFO,
3283 layer1);
3284
3285 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));
3286 ASSERT_EQ(EXDEV, errno);
3287 /*
3288 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE
3289 * access right.
3290 */
3291 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));
3292 ASSERT_EQ(EXDEV, errno);
3293 /*
3294 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a
3295 * superset of access rights compared to dir_s1d2, because file1_s1d2
3296 * already has these access rights anyway.
3297 */
3298 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));
3299 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));
3300
3301 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
3302 ASSERT_EQ(EXDEV, errno);
3303 /*
3304 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access
3305 * right.
3306 */
3307 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
3308 ASSERT_EQ(EXDEV, errno);
3309 /*
3310 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset
3311 * of access rights compared to dir_s1d2, because dir_s1d3 already has
3312 * these access rights anyway.
3313 */
3314 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
3315 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
3316
3317 /*
3318 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back
3319 * will be denied because the new inherited access rights from dir_s1d2
3320 * will be less than the destination (original) dir_s2d3. This is a
3321 * sinkhole scenario where we cannot move back files or directories.
3322 */
3323 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));
3324 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
3325 ASSERT_EQ(EXDEV, errno);
3326 ASSERT_EQ(0, unlink(file2_s1d2));
3327 ASSERT_EQ(0, unlink(file2_s2d3));
3328 /*
3329 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and
3330 * MAKE_SOCK which were inherited from dir_s1d3.
3331 */
3332 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));
3333 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));
3334 ASSERT_EQ(EXDEV, errno);
3335 }
3336
TEST_F_FORK(layout1,remove_dir)3337 TEST_F_FORK(layout1, remove_dir)
3338 {
3339 const struct rule rules[] = {
3340 {
3341 .path = dir_s1d2,
3342 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
3343 },
3344 {},
3345 };
3346
3347 ASSERT_EQ(0, unlink(file1_s1d1));
3348 ASSERT_EQ(0, unlink(file1_s1d2));
3349 ASSERT_EQ(0, unlink(file1_s1d3));
3350 ASSERT_EQ(0, unlink(file2_s1d3));
3351
3352 enforce_fs(_metadata, rules[0].access, rules);
3353
3354 ASSERT_EQ(0, rmdir(dir_s1d3));
3355 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
3356 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
3357
3358 /* dir_s1d2 itself cannot be removed. */
3359 ASSERT_EQ(-1, rmdir(dir_s1d2));
3360 ASSERT_EQ(EACCES, errno);
3361 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));
3362 ASSERT_EQ(EACCES, errno);
3363 ASSERT_EQ(-1, rmdir(dir_s1d1));
3364 ASSERT_EQ(EACCES, errno);
3365 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));
3366 ASSERT_EQ(EACCES, errno);
3367 }
3368
TEST_F_FORK(layout1,remove_file)3369 TEST_F_FORK(layout1, remove_file)
3370 {
3371 const struct rule rules[] = {
3372 {
3373 .path = dir_s1d2,
3374 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
3375 },
3376 {},
3377 };
3378
3379 enforce_fs(_metadata, rules[0].access, rules);
3380
3381 ASSERT_EQ(-1, unlink(file1_s1d1));
3382 ASSERT_EQ(EACCES, errno);
3383 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));
3384 ASSERT_EQ(EACCES, errno);
3385 ASSERT_EQ(0, unlink(file1_s1d2));
3386 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));
3387 }
3388
test_make_file(struct __test_metadata * const _metadata,const __u64 access,const mode_t mode,const dev_t dev)3389 static void test_make_file(struct __test_metadata *const _metadata,
3390 const __u64 access, const mode_t mode,
3391 const dev_t dev)
3392 {
3393 const struct rule rules[] = {
3394 {
3395 .path = dir_s1d2,
3396 .access = access,
3397 },
3398 {},
3399 };
3400
3401 ASSERT_EQ(0, unlink(file1_s1d1));
3402 ASSERT_EQ(0, unlink(file2_s1d1));
3403 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))
3404 {
3405 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,
3406 strerror(errno));
3407 };
3408
3409 ASSERT_EQ(0, unlink(file1_s1d2));
3410 ASSERT_EQ(0, unlink(file2_s1d2));
3411
3412 ASSERT_EQ(0, unlink(file1_s1d3));
3413 ASSERT_EQ(0, unlink(file2_s1d3));
3414
3415 enforce_fs(_metadata, access, rules);
3416
3417 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));
3418 ASSERT_EQ(EACCES, errno);
3419 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3420 ASSERT_EQ(EACCES, errno);
3421 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3422 ASSERT_EQ(EACCES, errno);
3423
3424 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))
3425 {
3426 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,
3427 strerror(errno));
3428 };
3429 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3430 ASSERT_EQ(0, unlink(file2_s1d2));
3431 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3432
3433 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));
3434 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3435 ASSERT_EQ(0, unlink(file2_s1d3));
3436 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3437 }
3438
TEST_F_FORK(layout1,make_char)3439 TEST_F_FORK(layout1, make_char)
3440 {
3441 /* Creates a /dev/null device. */
3442 set_cap(_metadata, CAP_MKNOD);
3443 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,
3444 makedev(1, 3));
3445 }
3446
TEST_F_FORK(layout1,make_whiteout)3447 TEST_F_FORK(layout1, make_whiteout)
3448 {
3449 /*
3450 * Creates a whiteout object (creation guarded by MAKE_REG).
3451 *
3452 * Contrary to the other character devices, this does not require
3453 * CAP_MKNOD, cf. vfs_mknod().
3454 */
3455 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFCHR,
3456 makedev(0, 0));
3457 }
3458
TEST_F_FORK(layout1,make_block)3459 TEST_F_FORK(layout1, make_block)
3460 {
3461 /* Creates a /dev/loop0 device. */
3462 set_cap(_metadata, CAP_MKNOD);
3463 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,
3464 makedev(7, 0));
3465 }
3466
TEST_F_FORK(layout1,make_reg_1)3467 TEST_F_FORK(layout1, make_reg_1)
3468 {
3469 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
3470 }
3471
TEST_F_FORK(layout1,make_reg_2)3472 TEST_F_FORK(layout1, make_reg_2)
3473 {
3474 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
3475 }
3476
TEST_F_FORK(layout1,make_sock)3477 TEST_F_FORK(layout1, make_sock)
3478 {
3479 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
3480 }
3481
TEST_F_FORK(layout1,make_fifo)3482 TEST_F_FORK(layout1, make_fifo)
3483 {
3484 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
3485 }
3486
TEST_F_FORK(layout1,make_sym)3487 TEST_F_FORK(layout1, make_sym)
3488 {
3489 const struct rule rules[] = {
3490 {
3491 .path = dir_s1d2,
3492 .access = LANDLOCK_ACCESS_FS_MAKE_SYM,
3493 },
3494 {},
3495 };
3496
3497 ASSERT_EQ(0, unlink(file1_s1d1));
3498 ASSERT_EQ(0, unlink(file2_s1d1));
3499 ASSERT_EQ(0, symlink("none", file2_s1d1));
3500
3501 ASSERT_EQ(0, unlink(file1_s1d2));
3502 ASSERT_EQ(0, unlink(file2_s1d2));
3503
3504 ASSERT_EQ(0, unlink(file1_s1d3));
3505 ASSERT_EQ(0, unlink(file2_s1d3));
3506
3507 enforce_fs(_metadata, rules[0].access, rules);
3508
3509 ASSERT_EQ(-1, symlink("none", file1_s1d1));
3510 ASSERT_EQ(EACCES, errno);
3511 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3512 ASSERT_EQ(EACCES, errno);
3513 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3514 ASSERT_EQ(EACCES, errno);
3515
3516 ASSERT_EQ(0, symlink("none", file1_s1d2));
3517 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3518 ASSERT_EQ(0, unlink(file2_s1d2));
3519 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3520
3521 ASSERT_EQ(0, symlink("none", file1_s1d3));
3522 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3523 ASSERT_EQ(0, unlink(file2_s1d3));
3524 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3525 }
3526
TEST_F_FORK(layout1,make_dir)3527 TEST_F_FORK(layout1, make_dir)
3528 {
3529 const struct rule rules[] = {
3530 {
3531 .path = dir_s1d2,
3532 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,
3533 },
3534 {},
3535 };
3536
3537 ASSERT_EQ(0, unlink(file1_s1d1));
3538 ASSERT_EQ(0, unlink(file1_s1d2));
3539 ASSERT_EQ(0, unlink(file1_s1d3));
3540
3541 enforce_fs(_metadata, rules[0].access, rules);
3542
3543 /* Uses file_* as directory names. */
3544 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));
3545 ASSERT_EQ(EACCES, errno);
3546 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
3547 ASSERT_EQ(0, mkdir(file1_s1d3, 0700));
3548 }
3549
open_proc_fd(struct __test_metadata * const _metadata,const int fd,const int open_flags)3550 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,
3551 const int open_flags)
3552 {
3553 static const char path_template[] = "/proc/self/fd/%d";
3554 char procfd_path[sizeof(path_template) + 10];
3555 const int procfd_path_size =
3556 snprintf(procfd_path, sizeof(procfd_path), path_template, fd);
3557
3558 ASSERT_LT(procfd_path_size, sizeof(procfd_path));
3559 return open(procfd_path, open_flags);
3560 }
3561
TEST_F_FORK(layout1,proc_unlinked_file)3562 TEST_F_FORK(layout1, proc_unlinked_file)
3563 {
3564 const struct rule rules[] = {
3565 {
3566 .path = file1_s1d2,
3567 .access = LANDLOCK_ACCESS_FS_READ_FILE,
3568 },
3569 {},
3570 };
3571 int reg_fd, proc_fd;
3572
3573 enforce_fs(_metadata,
3574 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
3575 rules);
3576
3577 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
3578 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3579 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);
3580 ASSERT_LE(0, reg_fd);
3581 ASSERT_EQ(0, unlink(file1_s1d2));
3582
3583 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);
3584 ASSERT_LE(0, proc_fd);
3585 ASSERT_EQ(0, close(proc_fd));
3586
3587 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);
3588 ASSERT_EQ(-1, proc_fd)
3589 {
3590 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,
3591 strerror(errno));
3592 }
3593 ASSERT_EQ(EACCES, errno);
3594
3595 ASSERT_EQ(0, close(reg_fd));
3596 }
3597
TEST_F_FORK(layout1,proc_pipe)3598 TEST_F_FORK(layout1, proc_pipe)
3599 {
3600 int proc_fd;
3601 int pipe_fds[2];
3602 char buf = '\0';
3603 const struct rule rules[] = {
3604 {
3605 .path = dir_s1d2,
3606 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3607 LANDLOCK_ACCESS_FS_WRITE_FILE,
3608 },
3609 {},
3610 };
3611
3612 /* Limits read and write access to files tied to the filesystem. */
3613 enforce_fs(_metadata, rules[0].access, rules);
3614
3615 /* Checks enforcement for normal files. */
3616 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
3617 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
3618
3619 /* Checks access to pipes through FD. */
3620 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));
3621 ASSERT_EQ(1, write(pipe_fds[1], ".", 1))
3622 {
3623 TH_LOG("Failed to write in pipe: %s", strerror(errno));
3624 }
3625 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));
3626 ASSERT_EQ('.', buf);
3627
3628 /* Checks write access to pipe through /proc/self/fd . */
3629 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);
3630 ASSERT_LE(0, proc_fd);
3631 ASSERT_EQ(1, write(proc_fd, ".", 1))
3632 {
3633 TH_LOG("Failed to write through /proc/self/fd/%d: %s",
3634 pipe_fds[1], strerror(errno));
3635 }
3636 ASSERT_EQ(0, close(proc_fd));
3637
3638 /* Checks read access to pipe through /proc/self/fd . */
3639 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);
3640 ASSERT_LE(0, proc_fd);
3641 buf = '\0';
3642 ASSERT_EQ(1, read(proc_fd, &buf, 1))
3643 {
3644 TH_LOG("Failed to read through /proc/self/fd/%d: %s",
3645 pipe_fds[1], strerror(errno));
3646 }
3647 ASSERT_EQ(0, close(proc_fd));
3648
3649 ASSERT_EQ(0, close(pipe_fds[0]));
3650 ASSERT_EQ(0, close(pipe_fds[1]));
3651 }
3652
3653 /* Invokes truncate(2) and returns its errno or 0. */
test_truncate(const char * const path)3654 static int test_truncate(const char *const path)
3655 {
3656 if (truncate(path, 10) < 0)
3657 return errno;
3658 return 0;
3659 }
3660
3661 /*
3662 * Invokes creat(2) and returns its errno or 0.
3663 * Closes the opened file descriptor on success.
3664 */
test_creat(const char * const path)3665 static int test_creat(const char *const path)
3666 {
3667 int fd = creat(path, 0600);
3668
3669 if (fd < 0)
3670 return errno;
3671
3672 /*
3673 * Mixing error codes from close(2) and creat(2) should not lead to any
3674 * (access type) confusion for this test.
3675 */
3676 if (close(fd) < 0)
3677 return errno;
3678 return 0;
3679 }
3680
3681 /*
3682 * Exercises file truncation when it's not restricted,
3683 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.
3684 */
TEST_F_FORK(layout1,truncate_unhandled)3685 TEST_F_FORK(layout1, truncate_unhandled)
3686 {
3687 const char *const file_r = file1_s1d1;
3688 const char *const file_w = file2_s1d1;
3689 const char *const file_none = file1_s1d2;
3690 const struct rule rules[] = {
3691 {
3692 .path = file_r,
3693 .access = LANDLOCK_ACCESS_FS_READ_FILE,
3694 },
3695 {
3696 .path = file_w,
3697 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3698 },
3699 /* Implicitly: No rights for file_none. */
3700 {},
3701 };
3702
3703 /* Enables Landlock. */
3704 enforce_fs(_metadata,
3705 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
3706 rules);
3707
3708 /*
3709 * Checks read right: truncate and open with O_TRUNC work, unless the
3710 * file is attempted to be opened for writing.
3711 */
3712 EXPECT_EQ(0, test_truncate(file_r));
3713 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));
3714 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));
3715 EXPECT_EQ(EACCES, test_creat(file_r));
3716
3717 /*
3718 * Checks write right: truncate and open with O_TRUNC work, unless the
3719 * file is attempted to be opened for reading.
3720 */
3721 EXPECT_EQ(0, test_truncate(file_w));
3722 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));
3723 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));
3724 EXPECT_EQ(0, test_creat(file_w));
3725
3726 /*
3727 * Checks "no rights" case: truncate works but all open attempts fail,
3728 * including creat.
3729 */
3730 EXPECT_EQ(0, test_truncate(file_none));
3731 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3732 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3733 EXPECT_EQ(EACCES, test_creat(file_none));
3734 }
3735
TEST_F_FORK(layout1,truncate)3736 TEST_F_FORK(layout1, truncate)
3737 {
3738 const char *const file_rwt = file1_s1d1;
3739 const char *const file_rw = file2_s1d1;
3740 const char *const file_rt = file1_s1d2;
3741 const char *const file_t = file2_s1d2;
3742 const char *const file_none = file1_s1d3;
3743 const char *const dir_t = dir_s2d1;
3744 const char *const file_in_dir_t = file1_s2d1;
3745 const char *const dir_w = dir_s3d1;
3746 const char *const file_in_dir_w = file1_s3d1;
3747 const struct rule rules[] = {
3748 {
3749 .path = file_rwt,
3750 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3751 LANDLOCK_ACCESS_FS_WRITE_FILE |
3752 LANDLOCK_ACCESS_FS_TRUNCATE,
3753 },
3754 {
3755 .path = file_rw,
3756 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3757 LANDLOCK_ACCESS_FS_WRITE_FILE,
3758 },
3759 {
3760 .path = file_rt,
3761 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3762 LANDLOCK_ACCESS_FS_TRUNCATE,
3763 },
3764 {
3765 .path = file_t,
3766 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3767 },
3768 /* Implicitly: No access rights for file_none. */
3769 {
3770 .path = dir_t,
3771 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3772 },
3773 {
3774 .path = dir_w,
3775 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3776 },
3777 {},
3778 };
3779
3780 /* Enables Landlock. */
3781 enforce_fs(_metadata,
3782 LANDLOCK_ACCESS_FS_READ_FILE |
3783 LANDLOCK_ACCESS_FS_WRITE_FILE |
3784 LANDLOCK_ACCESS_FS_TRUNCATE,
3785 rules);
3786
3787 /* Checks read, write and truncate rights: truncation works. */
3788 EXPECT_EQ(0, test_truncate(file_rwt));
3789 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));
3790 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));
3791
3792 /* Checks read and write rights: no truncate variant works. */
3793 EXPECT_EQ(EACCES, test_truncate(file_rw));
3794 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));
3795 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));
3796
3797 /*
3798 * Checks read and truncate rights: truncation works.
3799 *
3800 * Note: Files can get truncated using open() even with O_RDONLY.
3801 */
3802 EXPECT_EQ(0, test_truncate(file_rt));
3803 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));
3804 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));
3805
3806 /* Checks truncate right: truncate works, but can't open file. */
3807 EXPECT_EQ(0, test_truncate(file_t));
3808 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));
3809 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));
3810
3811 /* Checks "no rights" case: No form of truncation works. */
3812 EXPECT_EQ(EACCES, test_truncate(file_none));
3813 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3814 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3815
3816 /*
3817 * Checks truncate right on directory: truncate works on contained
3818 * files.
3819 */
3820 EXPECT_EQ(0, test_truncate(file_in_dir_t));
3821 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));
3822 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));
3823
3824 /*
3825 * Checks creat in dir_w: This requires the truncate right when
3826 * overwriting an existing file, but does not require it when the file
3827 * is new.
3828 */
3829 EXPECT_EQ(EACCES, test_creat(file_in_dir_w));
3830
3831 ASSERT_EQ(0, unlink(file_in_dir_w));
3832 EXPECT_EQ(0, test_creat(file_in_dir_w));
3833 }
3834
3835 /* Invokes ftruncate(2) and returns its errno or 0. */
test_ftruncate(int fd)3836 static int test_ftruncate(int fd)
3837 {
3838 if (ftruncate(fd, 10) < 0)
3839 return errno;
3840 return 0;
3841 }
3842
TEST_F_FORK(layout1,ftruncate)3843 TEST_F_FORK(layout1, ftruncate)
3844 {
3845 /*
3846 * This test opens a new file descriptor at different stages of
3847 * Landlock restriction:
3848 *
3849 * without restriction: ftruncate works
3850 * something else but truncate restricted: ftruncate works
3851 * truncate restricted and permitted: ftruncate works
3852 * truncate restricted and not permitted: ftruncate fails
3853 *
3854 * Whether this works or not is expected to depend on the time when the
3855 * FD was opened, not to depend on the time when ftruncate() was
3856 * called.
3857 */
3858 const char *const path = file1_s1d1;
3859 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |
3860 LANDLOCK_ACCESS_FS_WRITE_FILE;
3861 const struct rule layer1[] = {
3862 {
3863 .path = path,
3864 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3865 },
3866 {},
3867 };
3868 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;
3869 const struct rule layer2[] = {
3870 {
3871 .path = path,
3872 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3873 },
3874 {},
3875 };
3876 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |
3877 LANDLOCK_ACCESS_FS_WRITE_FILE;
3878 const struct rule layer3[] = {
3879 {
3880 .path = path,
3881 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3882 },
3883 {},
3884 };
3885 int fd_layer0, fd_layer1, fd_layer2, fd_layer3;
3886
3887 fd_layer0 = open(path, O_WRONLY);
3888 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3889
3890 enforce_fs(_metadata, handled1, layer1);
3891
3892 fd_layer1 = open(path, O_WRONLY);
3893 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3894 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3895
3896 enforce_fs(_metadata, handled2, layer2);
3897
3898 fd_layer2 = open(path, O_WRONLY);
3899 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3900 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3901 EXPECT_EQ(0, test_ftruncate(fd_layer2));
3902
3903 enforce_fs(_metadata, handled3, layer3);
3904
3905 fd_layer3 = open(path, O_WRONLY);
3906 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3907 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3908 EXPECT_EQ(0, test_ftruncate(fd_layer2));
3909 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));
3910
3911 ASSERT_EQ(0, close(fd_layer0));
3912 ASSERT_EQ(0, close(fd_layer1));
3913 ASSERT_EQ(0, close(fd_layer2));
3914 ASSERT_EQ(0, close(fd_layer3));
3915 }
3916
3917 /* clang-format off */
FIXTURE(ftruncate)3918 FIXTURE(ftruncate) {};
3919 /* clang-format on */
3920
FIXTURE_SETUP(ftruncate)3921 FIXTURE_SETUP(ftruncate)
3922 {
3923 prepare_layout(_metadata);
3924 create_file(_metadata, file1_s1d1);
3925 }
3926
FIXTURE_TEARDOWN_PARENT(ftruncate)3927 FIXTURE_TEARDOWN_PARENT(ftruncate)
3928 {
3929 EXPECT_EQ(0, remove_path(file1_s1d1));
3930 cleanup_layout(_metadata);
3931 }
3932
FIXTURE_VARIANT(ftruncate)3933 FIXTURE_VARIANT(ftruncate)
3934 {
3935 const __u64 handled;
3936 const __u64 allowed;
3937 const int expected_open_result;
3938 const int expected_ftruncate_result;
3939 };
3940
3941 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,w_w)3942 FIXTURE_VARIANT_ADD(ftruncate, w_w) {
3943 /* clang-format on */
3944 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE,
3945 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
3946 .expected_open_result = 0,
3947 .expected_ftruncate_result = 0,
3948 };
3949
3950 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,t_t)3951 FIXTURE_VARIANT_ADD(ftruncate, t_t) {
3952 /* clang-format on */
3953 .handled = LANDLOCK_ACCESS_FS_TRUNCATE,
3954 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,
3955 .expected_open_result = 0,
3956 .expected_ftruncate_result = 0,
3957 };
3958
3959 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_w)3960 FIXTURE_VARIANT_ADD(ftruncate, wt_w) {
3961 /* clang-format on */
3962 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3963 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
3964 .expected_open_result = 0,
3965 .expected_ftruncate_result = EACCES,
3966 };
3967
3968 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_wt)3969 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) {
3970 /* clang-format on */
3971 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3972 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3973 .expected_open_result = 0,
3974 .expected_ftruncate_result = 0,
3975 };
3976
3977 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_t)3978 FIXTURE_VARIANT_ADD(ftruncate, wt_t) {
3979 /* clang-format on */
3980 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3981 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,
3982 .expected_open_result = EACCES,
3983 };
3984
TEST_F_FORK(ftruncate,open_and_ftruncate)3985 TEST_F_FORK(ftruncate, open_and_ftruncate)
3986 {
3987 const char *const path = file1_s1d1;
3988 const struct rule rules[] = {
3989 {
3990 .path = path,
3991 .access = variant->allowed,
3992 },
3993 {},
3994 };
3995 int fd;
3996
3997 /* Enables Landlock. */
3998 enforce_fs(_metadata, variant->handled, rules);
3999
4000 fd = open(path, O_WRONLY);
4001 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
4002 if (fd >= 0) {
4003 EXPECT_EQ(variant->expected_ftruncate_result,
4004 test_ftruncate(fd));
4005 ASSERT_EQ(0, close(fd));
4006 }
4007 }
4008
TEST_F_FORK(ftruncate,open_and_ftruncate_in_different_processes)4009 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
4010 {
4011 int child, fd, status;
4012 int socket_fds[2];
4013
4014 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
4015 socket_fds));
4016
4017 child = fork();
4018 ASSERT_LE(0, child);
4019 if (child == 0) {
4020 /*
4021 * Enables Landlock in the child process, open a file descriptor
4022 * where truncation is forbidden and send it to the
4023 * non-landlocked parent process.
4024 */
4025 const char *const path = file1_s1d1;
4026 const struct rule rules[] = {
4027 {
4028 .path = path,
4029 .access = variant->allowed,
4030 },
4031 {},
4032 };
4033 int fd;
4034
4035 enforce_fs(_metadata, variant->handled, rules);
4036
4037 fd = open(path, O_WRONLY);
4038 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
4039
4040 if (fd >= 0) {
4041 ASSERT_EQ(0, send_fd(socket_fds[0], fd));
4042 ASSERT_EQ(0, close(fd));
4043 }
4044
4045 ASSERT_EQ(0, close(socket_fds[0]));
4046
4047 _exit(_metadata->exit_code);
4048 return;
4049 }
4050
4051 if (variant->expected_open_result == 0) {
4052 fd = recv_fd(socket_fds[1]);
4053 ASSERT_LE(0, fd);
4054
4055 EXPECT_EQ(variant->expected_ftruncate_result,
4056 test_ftruncate(fd));
4057 ASSERT_EQ(0, close(fd));
4058 }
4059
4060 ASSERT_EQ(child, waitpid(child, &status, 0));
4061 ASSERT_EQ(1, WIFEXITED(status));
4062 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
4063
4064 ASSERT_EQ(0, close(socket_fds[0]));
4065 ASSERT_EQ(0, close(socket_fds[1]));
4066 }
4067
4068 /* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */
test_fs_ioc_getflags_ioctl(int fd)4069 static int test_fs_ioc_getflags_ioctl(int fd)
4070 {
4071 uint32_t flags;
4072
4073 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)
4074 return errno;
4075 return 0;
4076 }
4077
TEST(memfd_ftruncate_and_ioctl)4078 TEST(memfd_ftruncate_and_ioctl)
4079 {
4080 int fd, i;
4081
4082 /*
4083 * We exercise the same test both with and without Landlock enabled, to
4084 * ensure that it behaves the same in both cases.
4085 */
4086 for (i = 0; i < 2; i++) {
4087 /* Creates a new memfd. */
4088 fd = memfd_create("name", MFD_CLOEXEC);
4089 ASSERT_LE(0, fd);
4090
4091 /*
4092 * Checks that operations associated with the opened file
4093 * (ftruncate, ioctl) are permitted on file descriptors that are
4094 * created in ways other than open(2).
4095 */
4096 EXPECT_EQ(0, test_ftruncate(fd));
4097 EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd));
4098
4099 ASSERT_EQ(0, close(fd));
4100
4101 /* Enables Landlock. */
4102 enforce_fs(_metadata, ACCESS_ALL, NULL);
4103 }
4104 }
4105
test_fionread_ioctl(int fd)4106 static int test_fionread_ioctl(int fd)
4107 {
4108 size_t sz = 0;
4109
4110 if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES)
4111 return errno;
4112 return 0;
4113 }
4114
TEST_F_FORK(layout1,o_path_ftruncate_and_ioctl)4115 TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)
4116 {
4117 int fd;
4118
4119 /*
4120 * Checks that for files opened with O_PATH, both ioctl(2) and
4121 * ftruncate(2) yield EBADF, as it is documented in open(2) for the
4122 * O_PATH flag.
4123 */
4124 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);
4125 ASSERT_LE(0, fd);
4126
4127 EXPECT_EQ(EBADF, test_ftruncate(fd));
4128 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));
4129
4130 ASSERT_EQ(0, close(fd));
4131
4132 /* Enables Landlock. */
4133 enforce_fs(_metadata, ACCESS_ALL, NULL);
4134
4135 /*
4136 * Checks that after enabling Landlock,
4137 * - the file can still be opened with O_PATH
4138 * - both ioctl and truncate still yield EBADF (not EACCES).
4139 */
4140 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);
4141 ASSERT_LE(0, fd);
4142
4143 EXPECT_EQ(EBADF, test_ftruncate(fd));
4144 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));
4145
4146 ASSERT_EQ(0, close(fd));
4147 }
4148
4149 /*
4150 * ioctl_error - generically call the given ioctl with a pointer to a
4151 * sufficiently large zeroed-out memory region.
4152 *
4153 * Returns the IOCTLs error, or 0.
4154 */
ioctl_error(struct __test_metadata * const _metadata,int fd,unsigned int cmd)4155 static int ioctl_error(struct __test_metadata *const _metadata, int fd,
4156 unsigned int cmd)
4157 {
4158 char buf[128]; /* sufficiently large */
4159 int res, stdinbak_fd, err;
4160
4161 /*
4162 * Depending on the IOCTL command, parts of the zeroed-out buffer might
4163 * be interpreted as file descriptor numbers. We do not want to
4164 * accidentally operate on file descriptor 0 (stdin), so we temporarily
4165 * move stdin to a different FD and close FD 0 for the IOCTL call.
4166 */
4167 stdinbak_fd = dup(0);
4168 ASSERT_LT(0, stdinbak_fd);
4169 ASSERT_EQ(0, close(0));
4170
4171 /* Invokes the IOCTL with a zeroed-out buffer. */
4172 bzero(&buf, sizeof(buf));
4173 res = ioctl(fd, cmd, &buf);
4174 err = errno;
4175
4176 /* Restores the old FD 0 and closes the backup FD. */
4177 ASSERT_EQ(0, dup2(stdinbak_fd, 0));
4178 ASSERT_EQ(0, close(stdinbak_fd));
4179
4180 if (res < 0)
4181 return err;
4182
4183 return 0;
4184 }
4185
4186 /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */
4187 struct space_resv {
4188 __s16 l_type;
4189 __s16 l_whence;
4190 __s64 l_start;
4191 __s64 l_len; /* len == 0 means until end of file */
4192 __s32 l_sysid;
4193 __u32 l_pid;
4194 __s32 l_pad[4]; /* reserved area */
4195 };
4196
4197 #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv)
4198 #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv)
4199 #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv)
4200 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv)
4201 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv)
4202
4203 /*
4204 * Tests a series of blanket-permitted and denied IOCTLs.
4205 */
TEST_F_FORK(layout1,blanket_permitted_ioctls)4206 TEST_F_FORK(layout1, blanket_permitted_ioctls)
4207 {
4208 int fd;
4209
4210 /* Enables Landlock. */
4211 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL);
4212
4213 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
4214 ASSERT_LE(0, fd);
4215
4216 /*
4217 * Checks permitted commands.
4218 * These ones may return errors, but should not be blocked by Landlock.
4219 */
4220 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX));
4221 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX));
4222 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO));
4223 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC));
4224 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE));
4225 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE));
4226 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW));
4227 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP));
4228 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ));
4229 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE));
4230 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE));
4231 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE));
4232 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID));
4233 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH));
4234
4235 /*
4236 * Checks blocked commands.
4237 * A call to a blocked IOCTL command always returns EACCES.
4238 */
4239 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));
4240 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS));
4241 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS));
4242 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR));
4243 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR));
4244 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP));
4245 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP));
4246 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64));
4247 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP));
4248 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64));
4249 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE));
4250
4251 /* Default case is also blocked. */
4252 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee));
4253
4254 ASSERT_EQ(0, close(fd));
4255 }
4256
4257 /*
4258 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right,
4259 * because they are not character or block devices.
4260 */
TEST_F_FORK(layout1,named_pipe_ioctl)4261 TEST_F_FORK(layout1, named_pipe_ioctl)
4262 {
4263 pid_t child_pid;
4264 int fd;
4265 const char *const path = file1_s1d1;
4266
4267 ASSERT_EQ(0, unlink(path));
4268 ASSERT_EQ(0, mkfifo(path, 0600));
4269
4270 /* Enables Landlock. */
4271 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL);
4272
4273 /* The child process opens the pipe for writing. */
4274 child_pid = fork();
4275 ASSERT_NE(-1, child_pid);
4276 if (child_pid == 0) {
4277 fd = open(path, O_WRONLY);
4278 close(fd);
4279 exit(0);
4280 }
4281
4282 fd = open(path, O_RDONLY);
4283 ASSERT_LE(0, fd);
4284
4285 /* FIONREAD is implemented by pipefifo_fops. */
4286 EXPECT_EQ(0, test_fionread_ioctl(fd));
4287
4288 ASSERT_EQ(0, close(fd));
4289 ASSERT_EQ(0, unlink(path));
4290
4291 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0));
4292 }
4293
4294 /*
4295 * set_up_named_unix_server - Create a pathname unix socket
4296 *
4297 * If the socket type is not SOCK_DGRAM, also invoke listen(2).
4298 *
4299 * Return: The listening FD - it is the caller responsibility to close it.
4300 */
set_up_named_unix_server(struct __test_metadata * const _metadata,int type,const char * const path)4301 static int set_up_named_unix_server(struct __test_metadata *const _metadata,
4302 int type, const char *const path)
4303 {
4304 int fd;
4305 struct sockaddr_un addr = {
4306 .sun_family = AF_UNIX,
4307 };
4308
4309 fd = socket(AF_UNIX, type, 0);
4310 ASSERT_LE(0, fd);
4311
4312 ASSERT_LT(strlen(path), sizeof(addr.sun_path));
4313 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
4314
4315 ASSERT_EQ(0, bind(fd, (struct sockaddr *)&addr, sizeof(addr)));
4316
4317 if (type != SOCK_DGRAM)
4318 ASSERT_EQ(0, listen(fd, 10 /* qlen */));
4319 return fd;
4320 }
4321
4322 /*
4323 * test_connect_named_unix - connect to the given named UNIX socket
4324 *
4325 * Return: The errno from connect(), or 0
4326 */
test_connect_named_unix(struct __test_metadata * const _metadata,int fd,const char * const path)4327 static int test_connect_named_unix(struct __test_metadata *const _metadata,
4328 int fd, const char *const path)
4329 {
4330 struct sockaddr_un addr = {
4331 .sun_family = AF_UNIX,
4332 };
4333
4334 ASSERT_LT(strlen(path), sizeof(addr.sun_path));
4335 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
4336
4337 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
4338 return errno;
4339 return 0;
4340 }
4341
4342 /* For named UNIX domain sockets, no IOCTL restrictions apply. */
TEST_F_FORK(layout1,named_unix_domain_socket_ioctl)4343 TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
4344 {
4345 const char *const path = file1_s1d1;
4346 int srv_fd, cli_fd;
4347
4348 /* Sets up a server */
4349 ASSERT_EQ(0, unlink(path));
4350 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path);
4351
4352 /* Enables Landlock. */
4353 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_IOCTL_DEV, NULL);
4354
4355 /* Sets up a client connection to it */
4356 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
4357 ASSERT_LE(0, cli_fd);
4358
4359 ASSERT_EQ(0, test_connect_named_unix(_metadata, cli_fd, path));
4360
4361 /* FIONREAD and other IOCTLs should not be forbidden. */
4362 EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
4363
4364 EXPECT_EQ(0, close(cli_fd));
4365 EXPECT_EQ(0, close(srv_fd));
4366 }
4367
4368 /* clang-format off */
FIXTURE(ioctl)4369 FIXTURE(ioctl) {};
4370
FIXTURE_SETUP(ioctl)4371 FIXTURE_SETUP(ioctl) {};
4372
FIXTURE_TEARDOWN(ioctl)4373 FIXTURE_TEARDOWN(ioctl) {};
4374 /* clang-format on */
4375
FIXTURE_VARIANT(ioctl)4376 FIXTURE_VARIANT(ioctl)
4377 {
4378 const __u64 handled;
4379 const __u64 allowed;
4380 const mode_t open_mode;
4381 /*
4382 * FIONREAD is used as a characteristic device-specific IOCTL command.
4383 * It is implemented in fs/ioctl.c for regular files,
4384 * but we do not blanket-permit it for devices.
4385 */
4386 const int expected_fionread_result;
4387 };
4388
4389 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,handled_i_allowed_none)4390 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) {
4391 /* clang-format on */
4392 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4393 .allowed = 0,
4394 .open_mode = O_RDWR,
4395 .expected_fionread_result = EACCES,
4396 };
4397
4398 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,handled_i_allowed_i)4399 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) {
4400 /* clang-format on */
4401 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4402 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4403 .open_mode = O_RDWR,
4404 .expected_fionread_result = 0,
4405 };
4406
4407 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,unhandled)4408 FIXTURE_VARIANT_ADD(ioctl, unhandled) {
4409 /* clang-format on */
4410 .handled = LANDLOCK_ACCESS_FS_EXECUTE,
4411 .allowed = LANDLOCK_ACCESS_FS_EXECUTE,
4412 .open_mode = O_RDWR,
4413 .expected_fionread_result = 0,
4414 };
4415
TEST_F_FORK(ioctl,handle_dir_access_file)4416 TEST_F_FORK(ioctl, handle_dir_access_file)
4417 {
4418 const int flag = 0;
4419 const struct rule rules[] = {
4420 {
4421 .path = "/dev",
4422 .access = variant->allowed,
4423 },
4424 {},
4425 };
4426 int fd;
4427
4428 /* Enables Landlock. */
4429 enforce_fs(_metadata, variant->handled, rules);
4430
4431 fd = open("/dev/zero", variant->open_mode);
4432 ASSERT_LE(0, fd);
4433
4434 /* Checks that IOCTL commands return the expected errors. */
4435 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd));
4436
4437 /* Checks that unrestrictable commands are unrestricted. */
4438 EXPECT_EQ(0, ioctl(fd, FIOCLEX));
4439 EXPECT_EQ(0, ioctl(fd, FIONCLEX));
4440 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag));
4441 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag));
4442 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag));
4443
4444 ASSERT_EQ(0, close(fd));
4445 }
4446
TEST_F_FORK(ioctl,handle_dir_access_dir)4447 TEST_F_FORK(ioctl, handle_dir_access_dir)
4448 {
4449 const int flag = 0;
4450 const struct rule rules[] = {
4451 {
4452 .path = "/dev",
4453 .access = variant->allowed,
4454 },
4455 {},
4456 };
4457 int dir_fd;
4458
4459 /* Enables Landlock. */
4460 enforce_fs(_metadata, variant->handled, rules);
4461
4462 /*
4463 * Ignore variant->open_mode for this test, as we intend to open a
4464 * directory. If the directory can not be opened, the variant is
4465 * infeasible to test with an opened directory.
4466 */
4467 dir_fd = open("/dev", O_RDONLY);
4468 if (dir_fd < 0)
4469 return;
4470
4471 /*
4472 * Checks that IOCTL commands return the expected errors.
4473 * We do not use the expected values from the fixture here.
4474 *
4475 * When using IOCTL on a directory, no Landlock restrictions apply.
4476 */
4477 EXPECT_EQ(0, test_fionread_ioctl(dir_fd));
4478
4479 /* Checks that unrestrictable commands are unrestricted. */
4480 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX));
4481 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX));
4482 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag));
4483 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag));
4484 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag));
4485
4486 ASSERT_EQ(0, close(dir_fd));
4487 }
4488
TEST_F_FORK(ioctl,handle_file_access_file)4489 TEST_F_FORK(ioctl, handle_file_access_file)
4490 {
4491 const int flag = 0;
4492 const struct rule rules[] = {
4493 {
4494 .path = "/dev/zero",
4495 .access = variant->allowed,
4496 },
4497 {},
4498 };
4499 int fd;
4500
4501 /* Enables Landlock. */
4502 enforce_fs(_metadata, variant->handled, rules);
4503
4504 fd = open("/dev/zero", variant->open_mode);
4505 ASSERT_LE(0, fd)
4506 {
4507 TH_LOG("Failed to open /dev/zero: %s", strerror(errno));
4508 }
4509
4510 /* Checks that IOCTL commands return the expected errors. */
4511 EXPECT_EQ(variant->expected_fionread_result, test_fionread_ioctl(fd));
4512
4513 /* Checks that unrestrictable commands are unrestricted. */
4514 EXPECT_EQ(0, ioctl(fd, FIOCLEX));
4515 EXPECT_EQ(0, ioctl(fd, FIONCLEX));
4516 EXPECT_EQ(0, ioctl(fd, FIONBIO, &flag));
4517 EXPECT_EQ(0, ioctl(fd, FIOASYNC, &flag));
4518 EXPECT_EQ(0, ioctl(fd, FIGETBSZ, &flag));
4519
4520 ASSERT_EQ(0, close(fd));
4521 }
4522
4523 /*
4524 * test_sendto_named_unix - sendto to the given named UNIX socket
4525 *
4526 * sendto() is equivalent to sendmsg() in this respect.
4527 *
4528 * Return: The errno from sendto(), or 0
4529 */
test_sendto_named_unix(struct __test_metadata * const _metadata,int fd,const char * const path)4530 static int test_sendto_named_unix(struct __test_metadata *const _metadata,
4531 int fd, const char *const path)
4532 {
4533 static const char buf[] = "dummy";
4534 struct sockaddr_un addr = {
4535 .sun_family = AF_UNIX,
4536 };
4537
4538 ASSERT_LT(strlen(path), sizeof(addr.sun_path));
4539 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
4540
4541 if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr,
4542 sizeof(addr)) == -1)
4543 return errno;
4544 return 0;
4545 }
4546
4547 /* clang-format off */
FIXTURE(scoped_domains)4548 FIXTURE(scoped_domains) {};
4549 /* clang-format on */
4550
4551 #include "scoped_base_variants.h"
4552
FIXTURE_SETUP(scoped_domains)4553 FIXTURE_SETUP(scoped_domains)
4554 {
4555 drop_caps(_metadata);
4556 };
4557
FIXTURE_TEARDOWN(scoped_domains)4558 FIXTURE_TEARDOWN(scoped_domains)
4559 {
4560 }
4561
4562 /*
4563 * Flags for test_connect_to_parent and test_connect_to_child:
4564 *
4565 * USE_SENDTO: Use sendto() instead of connect() (for SOCK_DGRAM only)
4566 * ENFORCE_ALL: Enforce a Landlock domain even when the variant says
4567 * we shouldn't. We enforce a domain where the path is allow-listed,
4568 * and expect the behavior to be the same as if none was used.
4569 */
4570 #define USE_SENDTO (1 << 0)
4571 #define ENFORCE_ALL (1 << 1)
4572
test_connect_to_parent(struct __test_metadata * const _metadata,const FIXTURE_VARIANT (scoped_domains)* variant,int sock_type,int flags)4573 static void test_connect_to_parent(struct __test_metadata *const _metadata,
4574 const FIXTURE_VARIANT(scoped_domains) *
4575 variant,
4576 int sock_type, int flags)
4577 {
4578 const char *const path = "sock";
4579 const struct rule rules[] = {
4580 {
4581 .path = ".",
4582 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
4583 },
4584 {},
4585 };
4586 int cli_fd, srv_fd, res, status;
4587 pid_t child_pid;
4588 int readiness_pipe[2];
4589 char buf[1];
4590
4591 if (variant->domain_both)
4592 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL);
4593 else if (flags & ENFORCE_ALL)
4594 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules);
4595
4596 unlink(path);
4597 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC));
4598
4599 child_pid = fork();
4600 ASSERT_LE(0, child_pid);
4601
4602 if (child_pid == 0) {
4603 if (variant->domain_child)
4604 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
4605 NULL);
4606 else if (flags & ENFORCE_ALL)
4607 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
4608 rules);
4609
4610 /* Wait for server to be available. */
4611 EXPECT_EQ(0, close(readiness_pipe[1]));
4612 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1));
4613 EXPECT_EQ(0, close(readiness_pipe[0]));
4614
4615 /* Talk to server. */
4616 cli_fd = socket(AF_UNIX, sock_type, 0);
4617 ASSERT_LE(0, cli_fd);
4618
4619 if (flags & USE_SENDTO)
4620 res = test_sendto_named_unix(_metadata, cli_fd, path);
4621 else
4622 res = test_connect_named_unix(_metadata, cli_fd, path);
4623
4624 EXPECT_EQ(variant->domain_child ? EACCES : 0, res);
4625
4626 /* Clean up. */
4627 EXPECT_EQ(0, close(cli_fd));
4628
4629 _exit(_metadata->exit_code);
4630 return;
4631 }
4632
4633 if (variant->domain_parent)
4634 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL);
4635 else if (flags & ENFORCE_ALL)
4636 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules);
4637
4638 srv_fd = set_up_named_unix_server(_metadata, sock_type, path);
4639
4640 /* Tell the child that it can connect. */
4641 EXPECT_EQ(0, close(readiness_pipe[0]));
4642 EXPECT_EQ(sizeof(buf), write(readiness_pipe[1], buf, sizeof(buf)));
4643 EXPECT_EQ(0, close(readiness_pipe[1]));
4644
4645 /* Wait for child. */
4646 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
4647 EXPECT_EQ(1, WIFEXITED(status));
4648 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
4649
4650 /* Clean up. */
4651 EXPECT_EQ(0, close(srv_fd));
4652 EXPECT_EQ(0, unlink(path));
4653 }
4654
test_connect_to_child(struct __test_metadata * const _metadata,const FIXTURE_VARIANT (scoped_domains)* variant,int sock_type,int flags)4655 static void test_connect_to_child(struct __test_metadata *const _metadata,
4656 const FIXTURE_VARIANT(scoped_domains) *
4657 variant,
4658 int sock_type, int flags)
4659 {
4660 const char *const path = "sock";
4661 const struct rule rules[] = {
4662 {
4663 .path = ".",
4664 .access = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
4665 },
4666 {},
4667 };
4668 int readiness_pipe[2];
4669 int shutdown_pipe[2];
4670 int cli_fd, srv_fd, res, status;
4671 pid_t child_pid;
4672 char buf[1];
4673
4674 if (variant->domain_both)
4675 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL);
4676 else if (flags & ENFORCE_ALL)
4677 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules);
4678
4679 unlink(path);
4680 ASSERT_EQ(0, pipe2(readiness_pipe, O_CLOEXEC));
4681 ASSERT_EQ(0, pipe2(shutdown_pipe, O_CLOEXEC));
4682
4683 child_pid = fork();
4684 ASSERT_LE(0, child_pid);
4685
4686 if (child_pid == 0) {
4687 if (variant->domain_child)
4688 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
4689 NULL);
4690 else if (flags & ENFORCE_ALL)
4691 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
4692 rules);
4693
4694 srv_fd = set_up_named_unix_server(_metadata, sock_type, path);
4695
4696 /* Tell the parent that it can connect. */
4697 EXPECT_EQ(0, close(readiness_pipe[0]));
4698 EXPECT_EQ(sizeof(buf),
4699 write(readiness_pipe[1], buf, sizeof(buf)));
4700 EXPECT_EQ(0, close(readiness_pipe[1]));
4701
4702 /* Wait until it is time to shut down. */
4703 EXPECT_EQ(0, close(shutdown_pipe[1]));
4704 EXPECT_EQ(1, read(shutdown_pipe[0], &buf, 1));
4705 EXPECT_EQ(0, close(shutdown_pipe[0]));
4706
4707 /* Cleanup */
4708 EXPECT_EQ(0, close(srv_fd));
4709 EXPECT_EQ(0, unlink(path));
4710
4711 _exit(_metadata->exit_code);
4712 return;
4713 }
4714
4715 if (variant->domain_parent)
4716 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL);
4717 else if (flags & ENFORCE_ALL)
4718 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, rules);
4719
4720 /* Wait for server to be available. */
4721 EXPECT_EQ(0, close(readiness_pipe[1]));
4722 EXPECT_EQ(1, read(readiness_pipe[0], &buf, 1));
4723 EXPECT_EQ(0, close(readiness_pipe[0]));
4724
4725 /* Talk to server. */
4726 cli_fd = socket(AF_UNIX, sock_type, 0);
4727 ASSERT_LE(0, cli_fd);
4728
4729 if (flags & USE_SENDTO)
4730 res = test_sendto_named_unix(_metadata, cli_fd, path);
4731 else
4732 res = test_connect_named_unix(_metadata, cli_fd, path);
4733
4734 EXPECT_EQ(variant->domain_parent ? EACCES : 0, res);
4735
4736 /* Clean up. */
4737 EXPECT_EQ(0, close(cli_fd));
4738
4739 /* Tell the server to shut down. */
4740 EXPECT_EQ(0, close(shutdown_pipe[0]));
4741 EXPECT_EQ(sizeof(buf), write(shutdown_pipe[1], buf, sizeof(buf)));
4742 EXPECT_EQ(0, close(shutdown_pipe[1]));
4743
4744 /* Wait for child. */
4745 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
4746 EXPECT_EQ(1, WIFEXITED(status));
4747 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
4748 }
4749
TEST_F(scoped_domains,unix_stream_connect_to_parent)4750 TEST_F(scoped_domains, unix_stream_connect_to_parent)
4751 {
4752 test_connect_to_parent(_metadata, variant, SOCK_STREAM, 0);
4753 }
4754
TEST_F(scoped_domains,unix_dgram_connect_to_parent)4755 TEST_F(scoped_domains, unix_dgram_connect_to_parent)
4756 {
4757 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, 0);
4758 }
4759
TEST_F(scoped_domains,unix_dgram_sendmsg_to_parent)4760 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent)
4761 {
4762 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, USE_SENDTO);
4763 }
4764
TEST_F(scoped_domains,unix_seqpacket_connect_to_parent)4765 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent)
4766 {
4767 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, 0);
4768 }
4769
TEST_F(scoped_domains,unix_stream_connect_to_parent_full)4770 TEST_F(scoped_domains, unix_stream_connect_to_parent_full)
4771 {
4772 test_connect_to_parent(_metadata, variant, SOCK_STREAM, ENFORCE_ALL);
4773 }
4774
TEST_F(scoped_domains,unix_dgram_connect_to_parent_full)4775 TEST_F(scoped_domains, unix_dgram_connect_to_parent_full)
4776 {
4777 test_connect_to_parent(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL);
4778 }
4779
TEST_F(scoped_domains,unix_dgram_sendmsg_to_parent_full)4780 TEST_F(scoped_domains, unix_dgram_sendmsg_to_parent_full)
4781 {
4782 test_connect_to_parent(_metadata, variant, SOCK_DGRAM,
4783 USE_SENDTO | ENFORCE_ALL);
4784 }
4785
TEST_F(scoped_domains,unix_seqpacket_connect_to_parent_full)4786 TEST_F(scoped_domains, unix_seqpacket_connect_to_parent_full)
4787 {
4788 test_connect_to_parent(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL);
4789 }
4790
TEST_F(scoped_domains,unix_stream_connect_to_child)4791 TEST_F(scoped_domains, unix_stream_connect_to_child)
4792 {
4793 test_connect_to_child(_metadata, variant, SOCK_STREAM, 0);
4794 }
4795
TEST_F(scoped_domains,unix_dgram_connect_to_child)4796 TEST_F(scoped_domains, unix_dgram_connect_to_child)
4797 {
4798 test_connect_to_child(_metadata, variant, SOCK_DGRAM, 0);
4799 }
4800
TEST_F(scoped_domains,unix_dgram_sendmsg_to_child)4801 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child)
4802 {
4803 test_connect_to_child(_metadata, variant, SOCK_DGRAM, USE_SENDTO);
4804 }
4805
TEST_F(scoped_domains,unix_seqpacket_connect_to_child)4806 TEST_F(scoped_domains, unix_seqpacket_connect_to_child)
4807 {
4808 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, 0);
4809 }
4810
TEST_F(scoped_domains,unix_stream_connect_to_child_full)4811 TEST_F(scoped_domains, unix_stream_connect_to_child_full)
4812 {
4813 test_connect_to_child(_metadata, variant, SOCK_STREAM, ENFORCE_ALL);
4814 }
4815
TEST_F(scoped_domains,unix_dgram_connect_to_child_full)4816 TEST_F(scoped_domains, unix_dgram_connect_to_child_full)
4817 {
4818 test_connect_to_child(_metadata, variant, SOCK_DGRAM, ENFORCE_ALL);
4819 }
4820
TEST_F(scoped_domains,unix_dgram_sendmsg_to_child_full)4821 TEST_F(scoped_domains, unix_dgram_sendmsg_to_child_full)
4822 {
4823 test_connect_to_child(_metadata, variant, SOCK_DGRAM,
4824 USE_SENDTO | ENFORCE_ALL);
4825 }
4826
TEST_F(scoped_domains,unix_seqpacket_connect_to_child_full)4827 TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full)
4828 {
4829 test_connect_to_child(_metadata, variant, SOCK_SEQPACKET, ENFORCE_ALL);
4830 }
4831
4832 #undef USE_SENDTO
4833 #undef ENFORCE_ALL
4834
read_core_pattern(struct __test_metadata * const _metadata,char * buf,size_t buf_size)4835 static void read_core_pattern(struct __test_metadata *const _metadata,
4836 char *buf, size_t buf_size)
4837 {
4838 int fd;
4839 ssize_t ret;
4840
4841 fd = open("/proc/sys/kernel/core_pattern", O_RDONLY | O_CLOEXEC);
4842 ASSERT_LE(0, fd);
4843
4844 ret = read(fd, buf, buf_size - 1);
4845 ASSERT_LE(0, ret);
4846 EXPECT_EQ(0, close(fd));
4847
4848 buf[ret] = '\0';
4849 }
4850
set_core_pattern(struct __test_metadata * const _metadata,const char * pattern)4851 static void set_core_pattern(struct __test_metadata *const _metadata,
4852 const char *pattern)
4853 {
4854 int fd;
4855 size_t len = strlen(pattern);
4856
4857 /*
4858 * Writing to /proc/sys/kernel/core_pattern requires EUID 0 because
4859 * sysctl_perm() checks that, ignoring capabilities like
4860 * CAP_SYS_ADMIN or CAP_DAC_OVERRIDE.
4861 *
4862 * Switching EUID clears the dumpable flag, which must be restored
4863 * afterwards to allow coredumps.
4864 */
4865 set_cap(_metadata, CAP_SETUID);
4866 ASSERT_EQ(0, seteuid(0));
4867 clear_cap(_metadata, CAP_SETUID);
4868
4869 fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC);
4870 ASSERT_LE(0, fd)
4871 {
4872 TH_LOG("Failed to open core_pattern for writing: %s",
4873 strerror(errno));
4874 }
4875
4876 ASSERT_EQ(len, write(fd, pattern, len));
4877 EXPECT_EQ(0, close(fd));
4878
4879 set_cap(_metadata, CAP_SETUID);
4880 ASSERT_EQ(0, seteuid(getuid()));
4881 clear_cap(_metadata, CAP_SETUID);
4882
4883 /* Restore dumpable flag cleared by seteuid(). */
4884 ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1, 0, 0, 0));
4885 }
4886
FIXTURE(coredump)4887 FIXTURE(coredump)
4888 {
4889 char original_core_pattern[256];
4890 };
4891
FIXTURE_SETUP(coredump)4892 FIXTURE_SETUP(coredump)
4893 {
4894 disable_caps(_metadata);
4895 read_core_pattern(_metadata, self->original_core_pattern,
4896 sizeof(self->original_core_pattern));
4897 }
4898
FIXTURE_TEARDOWN_PARENT(coredump)4899 FIXTURE_TEARDOWN_PARENT(coredump)
4900 {
4901 set_core_pattern(_metadata, self->original_core_pattern);
4902 }
4903
4904 /*
4905 * Test that even when a process is restricted with
4906 * LANDLOCK_ACCESS_FS_RESOLVE_UNIX, the kernel can still initiate a connection
4907 * to the coredump socket on the processes' behalf.
4908 */
TEST_F_FORK(coredump,socket_not_restricted)4909 TEST_F_FORK(coredump, socket_not_restricted)
4910 {
4911 static const char core_pattern[] = "@/tmp/landlock_coredump_test.sock";
4912 const char *const sock_path = core_pattern + 1;
4913 int srv_fd, conn_fd, status;
4914 pid_t child_pid;
4915 struct ucred cred;
4916 socklen_t cred_len = sizeof(cred);
4917 char buf[4096];
4918
4919 /* Set up the coredump server socket. */
4920 unlink(sock_path);
4921 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, sock_path);
4922
4923 /* Point coredumps at our socket. */
4924 set_core_pattern(_metadata, core_pattern);
4925
4926 /* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */
4927 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_RESOLVE_UNIX, NULL);
4928
4929 /* Fork a child that crashes. */
4930 child_pid = fork();
4931 ASSERT_LE(0, child_pid);
4932 if (child_pid == 0) {
4933 struct rlimit rl = {
4934 .rlim_cur = RLIM_INFINITY,
4935 .rlim_max = RLIM_INFINITY,
4936 };
4937
4938 ASSERT_EQ(0, setrlimit(RLIMIT_CORE, &rl));
4939
4940 /* Crash on purpose. */
4941 kill(getpid(), SIGSEGV);
4942 _exit(1);
4943 }
4944
4945 /*
4946 * Accept the coredump connection. If Landlock incorrectly denies the
4947 * kernel's coredump connect, accept() will block forever, so the test
4948 * would time out.
4949 */
4950 conn_fd = accept(srv_fd, NULL, NULL);
4951 ASSERT_LE(0, conn_fd);
4952
4953 /* Check that the connection came from the crashing child. */
4954 ASSERT_EQ(0, getsockopt(conn_fd, SOL_SOCKET, SO_PEERCRED, &cred,
4955 &cred_len));
4956 EXPECT_EQ(child_pid, cred.pid);
4957
4958 /* Drain the coredump data so the kernel can finish. */
4959 while (read(conn_fd, buf, sizeof(buf)) > 0)
4960 ;
4961
4962 EXPECT_EQ(0, close(conn_fd));
4963
4964 /* Wait for the child and verify it coredumped. */
4965 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
4966 ASSERT_TRUE(WIFSIGNALED(status));
4967 ASSERT_TRUE(WCOREDUMP(status));
4968
4969 EXPECT_EQ(0, close(srv_fd));
4970 EXPECT_EQ(0, unlink(sock_path));
4971 }
4972
4973 /* clang-format off */
FIXTURE(layout1_bind)4974 FIXTURE(layout1_bind) {};
4975 /* clang-format on */
4976
4977 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
4978 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";
4979 static const char bind_file2_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f2";
4980
4981 /* Move targets for disconnected path tests. */
4982 static const char dir_s4d1[] = TMP_DIR "/s4d1";
4983 static const char file1_s4d1[] = TMP_DIR "/s4d1/f1";
4984 static const char file2_s4d1[] = TMP_DIR "/s4d1/f2";
4985 static const char dir_s4d2[] = TMP_DIR "/s4d1/s4d2";
4986 static const char file1_s4d2[] = TMP_DIR "/s4d1/s4d2/f1";
4987 static const char file1_name[] = "f1";
4988 static const char file2_name[] = "f2";
4989
FIXTURE_SETUP(layout1_bind)4990 FIXTURE_SETUP(layout1_bind)
4991 {
4992 prepare_layout(_metadata);
4993
4994 create_layout1(_metadata);
4995
4996 set_cap(_metadata, CAP_SYS_ADMIN);
4997 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
4998 clear_cap(_metadata, CAP_SYS_ADMIN);
4999 }
5000
FIXTURE_TEARDOWN_PARENT(layout1_bind)5001 FIXTURE_TEARDOWN_PARENT(layout1_bind)
5002 {
5003 /* umount(dir_s2d2)) is handled by namespace lifetime. */
5004
5005 remove_path(file1_s4d1);
5006 remove_path(file2_s4d1);
5007
5008 remove_layout1(_metadata);
5009
5010 cleanup_layout(_metadata);
5011 }
5012
5013 /*
5014 * layout1_bind hierarchy:
5015 *
5016 * tmp
5017 * ├── s1d1
5018 * │ ├── f1
5019 * │ ├── f2
5020 * │ └── s1d2
5021 * │ ├── f1
5022 * │ ├── f2
5023 * │ └── s1d3 [disconnected by path_disconnected]
5024 * │ ├── f1
5025 * │ └── f2
5026 * ├── s2d1
5027 * │ ├── f1
5028 * │ └── s2d2 [bind mount from s1d2]
5029 * │ ├── f1
5030 * │ ├── f2
5031 * │ └── s1d3
5032 * │ ├── f1
5033 * │ └── f2
5034 * ├── s3d1
5035 * │ └── s3d2
5036 * │ └── s3d3
5037 * └── s4d1 [renamed from s1d3 by path_disconnected]
5038 * ├── f1
5039 * ├── f2
5040 * └── s4d2
5041 * └── f1
5042 */
5043
TEST_F_FORK(layout1_bind,no_restriction)5044 TEST_F_FORK(layout1_bind, no_restriction)
5045 {
5046 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
5047 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
5048 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
5049 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
5050 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
5051 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
5052
5053 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
5054 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
5055 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
5056 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
5057 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));
5058 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));
5059
5060 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));
5061 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
5062
5063 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
5064 }
5065
TEST_F_FORK(layout1_bind,same_content_same_file)5066 TEST_F_FORK(layout1_bind, same_content_same_file)
5067 {
5068 /*
5069 * Sets access right on parent directories of both source and
5070 * destination mount points.
5071 */
5072 const struct rule layer1_parent[] = {
5073 {
5074 .path = dir_s1d1,
5075 .access = ACCESS_RO,
5076 },
5077 {
5078 .path = dir_s2d1,
5079 .access = ACCESS_RW,
5080 },
5081 {},
5082 };
5083 /*
5084 * Sets access rights on the same bind-mounted directories. The result
5085 * should be ACCESS_RW for both directories, but not both hierarchies
5086 * because of the first layer.
5087 */
5088 const struct rule layer2_mount_point[] = {
5089 {
5090 .path = dir_s1d2,
5091 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5092 },
5093 {
5094 .path = dir_s2d2,
5095 .access = ACCESS_RW,
5096 },
5097 {},
5098 };
5099 /* Only allow read-access to the s1d3 hierarchies. */
5100 const struct rule layer3_source[] = {
5101 {
5102 .path = dir_s1d3,
5103 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5104 },
5105 {},
5106 };
5107 /* Removes all access rights. */
5108 const struct rule layer4_destination[] = {
5109 {
5110 .path = bind_file1_s1d3,
5111 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
5112 },
5113 {},
5114 };
5115
5116 /* Sets rules for the parent directories. */
5117 enforce_fs(_metadata, ACCESS_RW, layer1_parent);
5118
5119 /* Checks source hierarchy. */
5120 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
5121 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
5122 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
5123
5124 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
5125 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
5126 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
5127
5128 /* Checks destination hierarchy. */
5129 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));
5130 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
5131
5132 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
5133 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
5134
5135 /* Sets rules for the mount points. */
5136 enforce_fs(_metadata, ACCESS_RW, layer2_mount_point);
5137
5138 /* Checks source hierarchy. */
5139 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
5140 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
5141 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
5142
5143 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
5144 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
5145 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
5146
5147 /* Checks destination hierarchy. */
5148 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));
5149 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));
5150 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
5151
5152 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
5153 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
5154 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
5155
5156 /* Sets a (shared) rule only on the source. */
5157 enforce_fs(_metadata, ACCESS_RW, layer3_source);
5158
5159 /* Checks source hierarchy. */
5160 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
5161 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
5162 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
5163
5164 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
5165 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
5166 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
5167
5168 /* Checks destination hierarchy. */
5169 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));
5170 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));
5171 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
5172
5173 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
5174 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
5175 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
5176
5177 /* Sets a (shared) rule only on the destination. */
5178 enforce_fs(_metadata, ACCESS_RW, layer4_destination);
5179
5180 /* Checks source hierarchy. */
5181 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
5182 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
5183
5184 /* Checks destination hierarchy. */
5185 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));
5186 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
5187 }
5188
TEST_F_FORK(layout1_bind,reparent_cross_mount)5189 TEST_F_FORK(layout1_bind, reparent_cross_mount)
5190 {
5191 const struct rule layer1[] = {
5192 {
5193 /* dir_s2d1 is beneath the dir_s2d2 mount point. */
5194 .path = dir_s2d1,
5195 .access = LANDLOCK_ACCESS_FS_REFER,
5196 },
5197 {
5198 .path = bind_dir_s1d3,
5199 .access = LANDLOCK_ACCESS_FS_EXECUTE,
5200 },
5201 {},
5202 };
5203
5204 enforce_fs(_metadata,
5205 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE,
5206 layer1);
5207
5208 /* Checks basic denied move. */
5209 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));
5210 ASSERT_EQ(EXDEV, errno);
5211
5212 /* Checks real cross-mount move (Landlock is not involved). */
5213 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));
5214 ASSERT_EQ(EXDEV, errno);
5215
5216 /* Checks move that will give more accesses. */
5217 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));
5218 ASSERT_EQ(EXDEV, errno);
5219
5220 /* Checks legitimate downgrade move. */
5221 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));
5222 }
5223
5224 /*
5225 * Make sure access to file through a disconnected path works as expected.
5226 * This test moves s1d3 to s4d1.
5227 */
TEST_F_FORK(layout1_bind,path_disconnected)5228 TEST_F_FORK(layout1_bind, path_disconnected)
5229 {
5230 const struct rule layer1_allow_all[] = {
5231 {
5232 .path = TMP_DIR,
5233 .access = ACCESS_ALL,
5234 },
5235 {},
5236 };
5237 const struct rule layer2_allow_just_f1[] = {
5238 {
5239 .path = file1_s1d3,
5240 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5241 },
5242 {},
5243 };
5244 const struct rule layer3_only_s1d2[] = {
5245 {
5246 .path = dir_s1d2,
5247 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5248 },
5249 {},
5250 };
5251
5252 /* Landlock should not deny access just because it is disconnected. */
5253 int ruleset_fd_l1 =
5254 create_ruleset(_metadata, ACCESS_ALL, layer1_allow_all);
5255
5256 /* Creates the new ruleset now before we move the dir containing the file. */
5257 int ruleset_fd_l2 =
5258 create_ruleset(_metadata, ACCESS_RW, layer2_allow_just_f1);
5259 int ruleset_fd_l3 =
5260 create_ruleset(_metadata, ACCESS_RW, layer3_only_s1d2);
5261 int bind_s1d3_fd;
5262
5263 enforce_ruleset(_metadata, ruleset_fd_l1);
5264 EXPECT_EQ(0, close(ruleset_fd_l1));
5265
5266 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC);
5267 ASSERT_LE(0, bind_s1d3_fd);
5268
5269 /* Tests access is possible before we move. */
5270 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5271 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY));
5272 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY));
5273
5274 /* Makes it disconnected. */
5275 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1))
5276 {
5277 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1,
5278 strerror(errno));
5279 }
5280
5281 /* Tests that access is still possible. */
5282 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5283 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY));
5284
5285 /*
5286 * Tests that ".." is not possible (not because of Landlock, but just
5287 * because it's disconnected).
5288 */
5289 EXPECT_EQ(ENOENT,
5290 test_open_rel(bind_s1d3_fd, "..", O_RDONLY | O_DIRECTORY));
5291
5292 /* This should still work with a narrower rule. */
5293 enforce_ruleset(_metadata, ruleset_fd_l2);
5294 EXPECT_EQ(0, close(ruleset_fd_l2));
5295
5296 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY));
5297 /*
5298 * Accessing a file through a disconnected file descriptor can still be
5299 * allowed by a rule tied to this file, even if it is no longer visible in
5300 * its mount point.
5301 */
5302 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5303 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY));
5304
5305 enforce_ruleset(_metadata, ruleset_fd_l3);
5306 EXPECT_EQ(0, close(ruleset_fd_l3));
5307
5308 EXPECT_EQ(EACCES, test_open(file1_s4d1, O_RDONLY));
5309 /*
5310 * Accessing a file through a disconnected file descriptor can still be
5311 * allowed by a rule tied to the original mount point, even if it is no
5312 * longer visible in its mount point.
5313 */
5314 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5315 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY));
5316 }
5317
5318 /*
5319 * Test that renameat with disconnected paths works under Landlock. This test
5320 * moves s1d3 to s4d2, so that we can have a rule allowing refers on the move
5321 * target's immediate parent.
5322 */
TEST_F_FORK(layout1_bind,path_disconnected_rename)5323 TEST_F_FORK(layout1_bind, path_disconnected_rename)
5324 {
5325 const struct rule layer1[] = {
5326 {
5327 .path = dir_s1d2,
5328 .access = LANDLOCK_ACCESS_FS_REFER |
5329 LANDLOCK_ACCESS_FS_MAKE_DIR |
5330 LANDLOCK_ACCESS_FS_REMOVE_DIR |
5331 LANDLOCK_ACCESS_FS_MAKE_REG |
5332 LANDLOCK_ACCESS_FS_REMOVE_FILE |
5333 LANDLOCK_ACCESS_FS_READ_FILE,
5334 },
5335 {
5336 .path = dir_s4d1,
5337 .access = LANDLOCK_ACCESS_FS_REFER |
5338 LANDLOCK_ACCESS_FS_MAKE_DIR |
5339 LANDLOCK_ACCESS_FS_REMOVE_DIR |
5340 LANDLOCK_ACCESS_FS_MAKE_REG |
5341 LANDLOCK_ACCESS_FS_REMOVE_FILE |
5342 LANDLOCK_ACCESS_FS_READ_FILE,
5343 },
5344 {}
5345 };
5346
5347 /* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */
5348 const struct rule layer2_only_s1d2[] = {
5349 {
5350 .path = dir_s1d2,
5351 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5352 },
5353 {},
5354 };
5355 int ruleset_fd_l1, ruleset_fd_l2;
5356 pid_t child_pid;
5357 int bind_s1d3_fd, status;
5358
5359 ASSERT_EQ(0, mkdir(dir_s4d1, 0755))
5360 {
5361 TH_LOG("Failed to create %s: %s", dir_s4d1, strerror(errno));
5362 }
5363 ruleset_fd_l1 = create_ruleset(_metadata, ACCESS_ALL, layer1);
5364 ruleset_fd_l2 = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
5365 layer2_only_s1d2);
5366
5367 enforce_ruleset(_metadata, ruleset_fd_l1);
5368 EXPECT_EQ(0, close(ruleset_fd_l1));
5369
5370 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC);
5371 ASSERT_LE(0, bind_s1d3_fd);
5372 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5373
5374 /* Tests ENOENT priority over EACCES for disconnected directory. */
5375 EXPECT_EQ(EACCES, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY));
5376 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2))
5377 {
5378 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2,
5379 strerror(errno));
5380 }
5381 EXPECT_EQ(ENOENT, test_open_rel(bind_s1d3_fd, "..", O_DIRECTORY));
5382
5383 /*
5384 * The file is no longer under s1d2 but we should still be able to access it
5385 * with layer 2 because its mount point is evaluated as the first valid
5386 * directory because it was initially a parent. Do a fork to test this so
5387 * we don't prevent ourselves from renaming it back later.
5388 */
5389 child_pid = fork();
5390 ASSERT_LE(0, child_pid);
5391 if (child_pid == 0) {
5392 enforce_ruleset(_metadata, ruleset_fd_l2);
5393 EXPECT_EQ(0, close(ruleset_fd_l2));
5394 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5395 EXPECT_EQ(EACCES, test_open(file1_s4d2, O_RDONLY));
5396
5397 /*
5398 * Tests that access widening checks indeed prevents us from renaming it
5399 * back.
5400 */
5401 EXPECT_EQ(-1, rename(dir_s4d2, dir_s1d3));
5402 EXPECT_EQ(EXDEV, errno);
5403
5404 /*
5405 * Including through the now disconnected fd (but it should return
5406 * EXDEV).
5407 */
5408 EXPECT_EQ(-1, renameat(bind_s1d3_fd, file1_name, AT_FDCWD,
5409 file1_s2d2));
5410 EXPECT_EQ(EXDEV, errno);
5411 _exit(_metadata->exit_code);
5412 return;
5413 }
5414
5415 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0));
5416 EXPECT_EQ(1, WIFEXITED(status));
5417 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
5418
5419 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3))
5420 {
5421 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d1, dir_s1d3,
5422 strerror(errno));
5423 }
5424
5425 /* Now checks that we can access it under l2. */
5426 child_pid = fork();
5427 ASSERT_LE(0, child_pid);
5428 if (child_pid == 0) {
5429 enforce_ruleset(_metadata, ruleset_fd_l2);
5430 EXPECT_EQ(0, close(ruleset_fd_l2));
5431 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5432 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY));
5433 _exit(_metadata->exit_code);
5434 return;
5435 }
5436
5437 EXPECT_EQ(child_pid, waitpid(child_pid, &status, 0));
5438 EXPECT_EQ(1, WIFEXITED(status));
5439 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
5440
5441 /*
5442 * Also test that we can rename via a disconnected path. We move the
5443 * dir back to the disconnected place first, then we rename file1 to
5444 * file2 through our dir fd.
5445 */
5446 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2))
5447 {
5448 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d2,
5449 strerror(errno));
5450 }
5451 ASSERT_EQ(0,
5452 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name))
5453 {
5454 TH_LOG("Failed to rename %s to %s within disconnected %s: %s",
5455 file1_name, file2_name, bind_dir_s1d3, strerror(errno));
5456 }
5457 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file2_name, O_RDONLY));
5458 ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2))
5459 {
5460 TH_LOG("Failed to rename %s to %s through disconnected %s: %s",
5461 file2_name, file1_s2d2, bind_dir_s1d3, strerror(errno));
5462 }
5463 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY));
5464 EXPECT_EQ(0, test_open(file1_s1d2, O_RDONLY));
5465
5466 /* Move it back using the disconnected path as the target. */
5467 ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name))
5468 {
5469 TH_LOG("Failed to rename %s to %s through disconnected %s: %s",
5470 file1_s1d2, file1_name, bind_dir_s1d3, strerror(errno));
5471 }
5472
5473 /* Now make it connected again. */
5474 ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3))
5475 {
5476 TH_LOG("Failed to rename %s back to %s: %s", dir_s4d2, dir_s1d3,
5477 strerror(errno));
5478 }
5479
5480 /* Checks again that we can access it under l2. */
5481 enforce_ruleset(_metadata, ruleset_fd_l2);
5482 EXPECT_EQ(0, close(ruleset_fd_l2));
5483 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5484 EXPECT_EQ(0, test_open(file1_s1d3, O_RDONLY));
5485 }
5486
5487 /*
5488 * Test that linkat(2) with disconnected paths works under Landlock. This
5489 * test moves s1d3 to s4d1.
5490 */
TEST_F_FORK(layout1_bind,path_disconnected_link)5491 TEST_F_FORK(layout1_bind, path_disconnected_link)
5492 {
5493 /* Ruleset to be applied after renaming s1d3 to s4d1. */
5494 const struct rule layer1[] = {
5495 {
5496 .path = dir_s4d1,
5497 .access = LANDLOCK_ACCESS_FS_REFER |
5498 LANDLOCK_ACCESS_FS_READ_FILE |
5499 LANDLOCK_ACCESS_FS_MAKE_REG |
5500 LANDLOCK_ACCESS_FS_REMOVE_FILE,
5501 },
5502 {
5503 .path = dir_s2d2,
5504 .access = LANDLOCK_ACCESS_FS_REFER |
5505 LANDLOCK_ACCESS_FS_READ_FILE |
5506 LANDLOCK_ACCESS_FS_MAKE_REG |
5507 LANDLOCK_ACCESS_FS_REMOVE_FILE,
5508 },
5509 {}
5510 };
5511 int bind_s1d3_fd;
5512
5513 /* Removes unneeded files created by layout1, otherwise it will EEXIST. */
5514 ASSERT_EQ(0, unlink(file1_s1d2));
5515 ASSERT_EQ(0, unlink(file2_s1d3));
5516
5517 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_CLOEXEC);
5518 ASSERT_LE(0, bind_s1d3_fd);
5519 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY));
5520
5521 /* Disconnects bind_s1d3_fd. */
5522 ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1))
5523 {
5524 TH_LOG("Failed to rename %s to %s: %s", dir_s1d3, dir_s4d1,
5525 strerror(errno));
5526 }
5527
5528 /* Need this later to test different parent link. */
5529 ASSERT_EQ(0, mkdir(dir_s4d2, 0755))
5530 {
5531 TH_LOG("Failed to create %s: %s", dir_s4d2, strerror(errno));
5532 }
5533
5534 enforce_fs(_metadata, ACCESS_ALL, layer1);
5535
5536 /* From disconnected to connected. */
5537 ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0))
5538 {
5539 TH_LOG("Failed to link %s to %s via disconnected %s: %s",
5540 file1_name, file1_s2d2, bind_dir_s1d3, strerror(errno));
5541 }
5542
5543 /* Tests that we can access via the new link... */
5544 EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY))
5545 {
5546 TH_LOG("Failed to open newly linked %s: %s", file1_s2d2,
5547 strerror(errno));
5548 }
5549
5550 /* ...as well as the old one. */
5551 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY))
5552 {
5553 TH_LOG("Failed to open original %s: %s", file1_s4d1,
5554 strerror(errno));
5555 }
5556
5557 /* From connected to disconnected. */
5558 ASSERT_EQ(0, unlink(file1_s4d1));
5559 ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0))
5560 {
5561 TH_LOG("Failed to link %s to %s via disconnected %s: %s",
5562 file1_s2d2, file2_name, bind_dir_s1d3, strerror(errno));
5563 }
5564 EXPECT_EQ(0, test_open(file2_s4d1, O_RDONLY));
5565 ASSERT_EQ(0, unlink(file1_s2d2));
5566
5567 /* From disconnected to disconnected (same parent). */
5568 ASSERT_EQ(0,
5569 linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0))
5570 {
5571 TH_LOG("Failed to link %s to %s within disconnected %s: %s",
5572 file2_name, file1_name, bind_dir_s1d3, strerror(errno));
5573 }
5574 EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY))
5575 {
5576 TH_LOG("Failed to open newly linked %s: %s", file1_s4d1,
5577 strerror(errno));
5578 }
5579 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY))
5580 {
5581 TH_LOG("Failed to open %s through newly created link under disconnected path: %s",
5582 file1_name, strerror(errno));
5583 }
5584 ASSERT_EQ(0, unlink(file2_s4d1));
5585
5586 /* From disconnected to disconnected (different parent). */
5587 ASSERT_EQ(0,
5588 linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0))
5589 {
5590 TH_LOG("Failed to link %s to %s within disconnected %s: %s",
5591 file1_name, "s4d2/f1", bind_dir_s1d3, strerror(errno));
5592 }
5593 EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY))
5594 {
5595 TH_LOG("Failed to open %s after link: %s", file1_s4d2,
5596 strerror(errno));
5597 }
5598 EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY))
5599 {
5600 TH_LOG("Failed to open %s through disconnected path after link: %s",
5601 "s4d2/f1", strerror(errno));
5602 }
5603 }
5604
5605 /*
5606 * layout4_disconnected_leafs with bind mount and renames:
5607 *
5608 * tmp
5609 * ├── s1d1
5610 * │ └── s1d2 [source of the bind mount]
5611 * │ ├── s1d31
5612 * │ │ └── s1d41 [now renamed beneath s3d1]
5613 * │ │ ├── f1
5614 * │ │ └── f2
5615 * │ └── s1d32
5616 * │ └── s1d42 [now renamed beneath s4d1]
5617 * │ ├── f3
5618 * │ └── f4
5619 * ├── s2d1
5620 * │ └── s2d2 [bind mount of s1d2]
5621 * │ ├── s1d31
5622 * │ │ └── s1d41 [opened FD, now renamed beneath s3d1]
5623 * │ │ ├── f1
5624 * │ │ └── f2
5625 * │ └── s1d32
5626 * │ └── s1d42 [opened FD, now renamed beneath s4d1]
5627 * │ ├── f3
5628 * │ └── f4
5629 * ├── s3d1
5630 * │ └── s1d41 [renamed here]
5631 * │ ├── f1
5632 * │ └── f2
5633 * └── s4d1
5634 * └── s1d42 [renamed here]
5635 * ├── f3
5636 * └── f4
5637 */
5638 /* clang-format off */
FIXTURE(layout4_disconnected_leafs)5639 FIXTURE(layout4_disconnected_leafs) {
5640 int s2d2_fd;
5641 };
5642 /* clang-format on */
5643
FIXTURE_SETUP(layout4_disconnected_leafs)5644 FIXTURE_SETUP(layout4_disconnected_leafs)
5645 {
5646 prepare_layout(_metadata);
5647
5648 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1");
5649 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2");
5650 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3");
5651 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4");
5652 create_directory(_metadata, TMP_DIR "/s2d1/s2d2");
5653 create_directory(_metadata, TMP_DIR "/s3d1");
5654 create_directory(_metadata, TMP_DIR "/s4d1");
5655
5656 self->s2d2_fd =
5657 open(TMP_DIR "/s2d1/s2d2", O_DIRECTORY | O_PATH | O_CLOEXEC);
5658 ASSERT_LE(0, self->s2d2_fd);
5659
5660 set_cap(_metadata, CAP_SYS_ADMIN);
5661 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2", NULL,
5662 MS_BIND, NULL));
5663 clear_cap(_metadata, CAP_SYS_ADMIN);
5664 }
5665
FIXTURE_TEARDOWN_PARENT(layout4_disconnected_leafs)5666 FIXTURE_TEARDOWN_PARENT(layout4_disconnected_leafs)
5667 {
5668 /* umount(TMP_DIR "/s2d1") is handled by namespace lifetime. */
5669
5670 /* Removes files after renames. */
5671 remove_path(TMP_DIR "/s3d1/s1d41/f1");
5672 remove_path(TMP_DIR "/s3d1/s1d41/f2");
5673 remove_path(TMP_DIR "/s4d1/s1d42/f1");
5674 remove_path(TMP_DIR "/s4d1/s1d42/f3");
5675 remove_path(TMP_DIR "/s4d1/s1d42/f4");
5676 remove_path(TMP_DIR "/s4d1/s1d42/f5");
5677
5678 cleanup_layout(_metadata);
5679 }
5680
FIXTURE_VARIANT(layout4_disconnected_leafs)5681 FIXTURE_VARIANT(layout4_disconnected_leafs)
5682 {
5683 /*
5684 * Parent of the bind mount source. It should always be ignored when
5685 * testing against files under the s1d41 or s1d42 disconnected directories.
5686 */
5687 const __u64 allowed_s1d1;
5688 /*
5689 * Source of bind mount (to s2d2). It should always be enforced when
5690 * testing against files under the s1d41 or s1d42 disconnected directories.
5691 */
5692 const __u64 allowed_s1d2;
5693 /*
5694 * Original parent of s1d41. It should always be ignored when testing
5695 * against files under the s1d41 disconnected directory.
5696 */
5697 const __u64 allowed_s1d31;
5698 /*
5699 * Original parent of s1d42. It should always be ignored when testing
5700 * against files under the s1d42 disconnected directory.
5701 */
5702 const __u64 allowed_s1d32;
5703 /*
5704 * Opened and disconnected source directory. It should always be enforced
5705 * when testing against files under the s1d41 disconnected directory.
5706 */
5707 const __u64 allowed_s1d41;
5708 /*
5709 * Opened and disconnected source directory. It should always be enforced
5710 * when testing against files under the s1d42 disconnected directory.
5711 */
5712 const __u64 allowed_s1d42;
5713 /*
5714 * File in the s1d41 disconnected directory. It should always be enforced
5715 * when testing against itself under the s1d41 disconnected directory.
5716 */
5717 const __u64 allowed_f1;
5718 /*
5719 * File in the s1d41 disconnected directory. It should always be enforced
5720 * when testing against itself under the s1d41 disconnected directory.
5721 */
5722 const __u64 allowed_f2;
5723 /*
5724 * File in the s1d42 disconnected directory. It should always be enforced
5725 * when testing against itself under the s1d42 disconnected directory.
5726 */
5727 const __u64 allowed_f3;
5728 /*
5729 * Parent of the bind mount destination. It should always be enforced when
5730 * testing against files under the s1d41 or s1d42 disconnected directories.
5731 */
5732 const __u64 allowed_s2d1;
5733 /*
5734 * Directory covered by the bind mount. It should always be ignored when
5735 * testing against files under the s1d41 or s1d42 disconnected directories.
5736 */
5737 const __u64 allowed_s2d2;
5738 /*
5739 * New parent of the renamed s1d41. It should always be ignored when
5740 * testing against files under the s1d41 disconnected directory.
5741 */
5742 const __u64 allowed_s3d1;
5743 /*
5744 * New parent of the renamed s1d42. It should always be ignored when
5745 * testing against files under the s1d42 disconnected directory.
5746 */
5747 const __u64 allowed_s4d1;
5748
5749 /* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */
5750 const int expected_read_result;
5751 /* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */
5752 const int expected_rename_result;
5753 /*
5754 * Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3,
5755 * RENAME_EXCHANGE).
5756 */
5757 const int expected_exchange_result;
5758 /* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */
5759 const int expected_same_dir_rename_result;
5760 };
5761
5762 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d1_mount_src_parent)5763 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) {
5764 /* clang-format on */
5765 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER |
5766 LANDLOCK_ACCESS_FS_READ_FILE |
5767 LANDLOCK_ACCESS_FS_EXECUTE |
5768 LANDLOCK_ACCESS_FS_MAKE_REG,
5769 .expected_read_result = EACCES,
5770 .expected_same_dir_rename_result = EACCES,
5771 .expected_rename_result = EACCES,
5772 .expected_exchange_result = EACCES,
5773 };
5774
5775 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d2_mount_src_refer)5776 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) {
5777 /* clang-format on */
5778 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
5779 .expected_read_result = 0,
5780 .expected_same_dir_rename_result = EACCES,
5781 .expected_rename_result = EACCES,
5782 .expected_exchange_result = EACCES,
5783 };
5784
5785 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d2_mount_src_create)5786 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) {
5787 /* clang-format on */
5788 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE |
5789 LANDLOCK_ACCESS_FS_MAKE_REG,
5790 .expected_read_result = 0,
5791 .expected_same_dir_rename_result = 0,
5792 .expected_rename_result = EXDEV,
5793 .expected_exchange_result = EXDEV,
5794 };
5795
5796 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d2_mount_src_rename)5797 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) {
5798 /* clang-format on */
5799 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5800 .expected_read_result = EACCES,
5801 .expected_same_dir_rename_result = 0,
5802 .expected_rename_result = 0,
5803 .expected_exchange_result = 0,
5804 };
5805
5806 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d31_s1d32_old_parent)5807 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) {
5808 /* clang-format on */
5809 .allowed_s1d31 = LANDLOCK_ACCESS_FS_REFER |
5810 LANDLOCK_ACCESS_FS_READ_FILE |
5811 LANDLOCK_ACCESS_FS_EXECUTE |
5812 LANDLOCK_ACCESS_FS_MAKE_REG,
5813 .allowed_s1d32 = LANDLOCK_ACCESS_FS_REFER |
5814 LANDLOCK_ACCESS_FS_READ_FILE |
5815 LANDLOCK_ACCESS_FS_EXECUTE |
5816 LANDLOCK_ACCESS_FS_MAKE_REG,
5817 .expected_read_result = EACCES,
5818 .expected_same_dir_rename_result = EACCES,
5819 .expected_rename_result = EACCES,
5820 .expected_exchange_result = EACCES,
5821 };
5822
5823 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d41_s1d42_disconnected_refer)5824 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) {
5825 /* clang-format on */
5826 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER |
5827 LANDLOCK_ACCESS_FS_READ_FILE,
5828 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER |
5829 LANDLOCK_ACCESS_FS_READ_FILE,
5830 .expected_read_result = 0,
5831 .expected_same_dir_rename_result = EACCES,
5832 .expected_rename_result = EACCES,
5833 .expected_exchange_result = EACCES,
5834 };
5835
5836 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d41_s1d42_disconnected_create)5837 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) {
5838 /* clang-format on */
5839 .allowed_s1d41 = LANDLOCK_ACCESS_FS_READ_FILE |
5840 LANDLOCK_ACCESS_FS_MAKE_REG,
5841 .allowed_s1d42 = LANDLOCK_ACCESS_FS_READ_FILE |
5842 LANDLOCK_ACCESS_FS_MAKE_REG,
5843 .expected_read_result = 0,
5844 .expected_same_dir_rename_result = 0,
5845 .expected_rename_result = EXDEV,
5846 .expected_exchange_result = EXDEV,
5847 };
5848
5849 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d41_s1d42_disconnected_rename_even)5850 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) {
5851 /* clang-format on */
5852 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5853 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5854 .expected_read_result = EACCES,
5855 .expected_same_dir_rename_result = 0,
5856 .expected_rename_result = 0,
5857 .expected_exchange_result = 0,
5858 };
5859
5860 /* The destination directory has more access right. */
5861 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d41_s1d42_disconnected_rename_more)5862 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) {
5863 /* clang-format on */
5864 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5865 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER |
5866 LANDLOCK_ACCESS_FS_MAKE_REG |
5867 LANDLOCK_ACCESS_FS_EXECUTE,
5868 .expected_read_result = EACCES,
5869 .expected_same_dir_rename_result = 0,
5870 /* Access denied. */
5871 .expected_rename_result = EXDEV,
5872 .expected_exchange_result = EXDEV,
5873 };
5874
5875 /* The destination directory has less access right. */
5876 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s1d41_s1d42_disconnected_rename_less)5877 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) {
5878 /* clang-format on */
5879 .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER |
5880 LANDLOCK_ACCESS_FS_MAKE_REG |
5881 LANDLOCK_ACCESS_FS_EXECUTE,
5882 .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5883 .expected_read_result = EACCES,
5884 .expected_same_dir_rename_result = 0,
5885 /* Access allowed. */
5886 .expected_rename_result = 0,
5887 .expected_exchange_result = EXDEV,
5888 };
5889
5890 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s2d1_mount_dst_parent_create)5891 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) {
5892 /* clang-format on */
5893 .allowed_s2d1 = LANDLOCK_ACCESS_FS_READ_FILE |
5894 LANDLOCK_ACCESS_FS_MAKE_REG,
5895 .expected_read_result = 0,
5896 .expected_same_dir_rename_result = 0,
5897 .expected_rename_result = EXDEV,
5898 .expected_exchange_result = EXDEV,
5899 };
5900
5901 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s2d1_mount_dst_parent_refer)5902 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) {
5903 /* clang-format on */
5904 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
5905 .expected_read_result = 0,
5906 .expected_same_dir_rename_result = EACCES,
5907 .expected_rename_result = EACCES,
5908 .expected_exchange_result = EACCES,
5909 };
5910
5911 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s2d1_mount_dst_parent_mini)5912 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) {
5913 /* clang-format on */
5914 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER |
5915 LANDLOCK_ACCESS_FS_READ_FILE |
5916 LANDLOCK_ACCESS_FS_MAKE_REG,
5917 .expected_read_result = 0,
5918 .expected_same_dir_rename_result = 0,
5919 .expected_rename_result = 0,
5920 .expected_exchange_result = 0,
5921 };
5922
5923 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s2d2_covered_by_mount)5924 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) {
5925 /* clang-format on */
5926 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER |
5927 LANDLOCK_ACCESS_FS_READ_FILE |
5928 LANDLOCK_ACCESS_FS_EXECUTE |
5929 LANDLOCK_ACCESS_FS_MAKE_REG,
5930 .expected_read_result = EACCES,
5931 .expected_same_dir_rename_result = EACCES,
5932 .expected_rename_result = EACCES,
5933 .expected_exchange_result = EACCES,
5934 };
5935
5936 /* Tests collect_domain_accesses(). */
5937 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s3d1_s4d1_new_parent_refer)5938 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) {
5939 /* clang-format on */
5940 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
5941 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
5942 .expected_read_result = 0,
5943 .expected_same_dir_rename_result = EACCES,
5944 .expected_rename_result = EACCES,
5945 .expected_exchange_result = EACCES,
5946 };
5947
5948 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s3d1_s4d1_new_parent_create)5949 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) {
5950 /* clang-format on */
5951 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE |
5952 LANDLOCK_ACCESS_FS_MAKE_REG,
5953 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE |
5954 LANDLOCK_ACCESS_FS_MAKE_REG,
5955 .expected_read_result = 0,
5956 .expected_same_dir_rename_result = 0,
5957 .expected_rename_result = EXDEV,
5958 .expected_exchange_result = EXDEV,
5959 };
5960
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s3d1_s4d1_disconnected_rename_even)5961 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,
5962 s3d1_s4d1_disconnected_rename_even){
5963 /* clang-format on */
5964 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5965 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5966 .expected_read_result = EACCES,
5967 .expected_same_dir_rename_result = 0,
5968 .expected_rename_result = 0,
5969 .expected_exchange_result = 0,
5970 };
5971
5972 /* The destination directory has more access right. */
5973 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s3d1_s4d1_disconnected_rename_more)5974 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) {
5975 /* clang-format on */
5976 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5977 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG |
5978 LANDLOCK_ACCESS_FS_EXECUTE,
5979 .expected_read_result = EACCES,
5980 .expected_same_dir_rename_result = 0,
5981 /* Access denied. */
5982 .expected_rename_result = EXDEV,
5983 .expected_exchange_result = EXDEV,
5984 };
5985
5986 /* The destination directory has less access right. */
5987 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,s3d1_s4d1_disconnected_rename_less)5988 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) {
5989 /* clang-format on */
5990 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG |
5991 LANDLOCK_ACCESS_FS_EXECUTE,
5992 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
5993 .expected_read_result = EACCES,
5994 .expected_same_dir_rename_result = 0,
5995 /* Access allowed. */
5996 .expected_rename_result = 0,
5997 .expected_exchange_result = EXDEV,
5998 };
5999
6000 /* clang-format off */
FIXTURE_VARIANT_ADD(layout4_disconnected_leafs,f1_f2_f3)6001 FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) {
6002 /* clang-format on */
6003 .allowed_f1 = LANDLOCK_ACCESS_FS_READ_FILE,
6004 .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE,
6005 .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE,
6006 .expected_read_result = 0,
6007 .expected_same_dir_rename_result = EACCES,
6008 .expected_rename_result = EACCES,
6009 .expected_exchange_result = EACCES,
6010 };
6011
TEST_F_FORK(layout4_disconnected_leafs,read_rename_exchange)6012 TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange)
6013 {
6014 const __u64 handled_access =
6015 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE |
6016 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG;
6017 const struct rule rules[] = {
6018 {
6019 .path = TMP_DIR "/s1d1",
6020 .access = variant->allowed_s1d1,
6021 },
6022 {
6023 .path = TMP_DIR "/s1d1/s1d2",
6024 .access = variant->allowed_s1d2,
6025 },
6026 {
6027 .path = TMP_DIR "/s1d1/s1d2/s1d31",
6028 .access = variant->allowed_s1d31,
6029 },
6030 {
6031 .path = TMP_DIR "/s1d1/s1d2/s1d32",
6032 .access = variant->allowed_s1d32,
6033 },
6034 {
6035 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41",
6036 .access = variant->allowed_s1d41,
6037 },
6038 {
6039 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42",
6040 .access = variant->allowed_s1d42,
6041 },
6042 {
6043 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1",
6044 .access = variant->allowed_f1,
6045 },
6046 {
6047 .path = TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2",
6048 .access = variant->allowed_f2,
6049 },
6050 {
6051 .path = TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3",
6052 .access = variant->allowed_f3,
6053 },
6054 {
6055 .path = TMP_DIR "/s2d1",
6056 .access = variant->allowed_s2d1,
6057 },
6058 /* s2d2_fd */
6059 {
6060 .path = TMP_DIR "/s3d1",
6061 .access = variant->allowed_s3d1,
6062 },
6063 {
6064 .path = TMP_DIR "/s4d1",
6065 .access = variant->allowed_s4d1,
6066 },
6067 {},
6068 };
6069 int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd;
6070
6071 ruleset_fd = create_ruleset(_metadata, handled_access, rules);
6072
6073 /* Adds rule for the covered directory. */
6074 if (variant->allowed_s2d2) {
6075 ASSERT_EQ(0, landlock_add_rule(
6076 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
6077 &(struct landlock_path_beneath_attr){
6078 .parent_fd = self->s2d2_fd,
6079 .allowed_access =
6080 variant->allowed_s2d2,
6081 },
6082 0));
6083 }
6084 EXPECT_EQ(0, close(self->s2d2_fd));
6085
6086 s1d41_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d31/s1d41",
6087 O_DIRECTORY | O_PATH | O_CLOEXEC);
6088 ASSERT_LE(0, s1d41_bind_fd);
6089 s1d42_bind_fd = open(TMP_DIR "/s2d1/s2d2/s1d32/s1d42",
6090 O_DIRECTORY | O_PATH | O_CLOEXEC);
6091 ASSERT_LE(0, s1d42_bind_fd);
6092
6093 /* Disconnects and checks source and destination directories. */
6094 EXPECT_EQ(0, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY));
6095 EXPECT_EQ(0, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY));
6096 /* Renames to make it accessible through s3d1/s1d41 */
6097 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d31/s1d41",
6098 AT_FDCWD, TMP_DIR "/s3d1/s1d41"));
6099 /* Renames to make it accessible through s4d1/s1d42 */
6100 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s1d1/s1d2/s1d32/s1d42",
6101 AT_FDCWD, TMP_DIR "/s4d1/s1d42"));
6102 EXPECT_EQ(ENOENT, test_open_rel(s1d41_bind_fd, "..", O_DIRECTORY));
6103 EXPECT_EQ(ENOENT, test_open_rel(s1d42_bind_fd, "..", O_DIRECTORY));
6104
6105 enforce_ruleset(_metadata, ruleset_fd);
6106 EXPECT_EQ(0, close(ruleset_fd));
6107
6108 EXPECT_EQ(variant->expected_read_result,
6109 test_open_rel(s1d41_bind_fd, "f1", O_RDONLY));
6110
6111 EXPECT_EQ(variant->expected_rename_result,
6112 test_renameat(s1d41_bind_fd, "f1", s1d42_bind_fd, "f1"));
6113 EXPECT_EQ(variant->expected_exchange_result,
6114 test_exchangeat(s1d41_bind_fd, "f2", s1d42_bind_fd, "f3"));
6115
6116 EXPECT_EQ(variant->expected_same_dir_rename_result,
6117 test_renameat(s1d42_bind_fd, "f4", s1d42_bind_fd, "f5"));
6118 }
6119
6120 /*
6121 * layout5_disconnected_branch before rename:
6122 *
6123 * tmp
6124 * ├── s1d1
6125 * │ └── s1d2 [source of the first bind mount]
6126 * │ └── s1d3
6127 * │ ├── s1d41
6128 * │ │ ├── f1
6129 * │ │ └── f2
6130 * │ └── s1d42
6131 * │ ├── f3
6132 * │ └── f4
6133 * ├── s2d1
6134 * │ └── s2d2 [source of the second bind mount]
6135 * │ └── s2d3
6136 * │ └── s2d4 [first s1d2 bind mount]
6137 * │ └── s1d3
6138 * │ ├── s1d41
6139 * │ │ ├── f1
6140 * │ │ └── f2
6141 * │ └── s1d42
6142 * │ ├── f3
6143 * │ └── f4
6144 * ├── s3d1
6145 * │ └── s3d2 [second s2d2 bind mount]
6146 * │ └── s2d3
6147 * │ └── s2d4 [first s1d2 bind mount]
6148 * │ └── s1d3
6149 * │ ├── s1d41
6150 * │ │ ├── f1
6151 * │ │ └── f2
6152 * │ └── s1d42
6153 * │ ├── f3
6154 * │ └── f4
6155 * └── s4d1
6156 *
6157 * After rename:
6158 *
6159 * tmp
6160 * ├── s1d1
6161 * │ └── s1d2 [source of the first bind mount]
6162 * │ └── s1d3
6163 * │ ├── s1d41
6164 * │ │ ├── f1
6165 * │ │ └── f2
6166 * │ └── s1d42
6167 * │ ├── f3
6168 * │ └── f4
6169 * ├── s2d1
6170 * │ └── s2d2 [source of the second bind mount]
6171 * ├── s3d1
6172 * │ └── s3d2 [second s2d2 bind mount]
6173 * └── s4d1
6174 * └── s2d3 [renamed here]
6175 * └── s2d4 [first s1d2 bind mount]
6176 * └── s1d3
6177 * ├── s1d41
6178 * │ ├── f1
6179 * │ └── f2
6180 * └── s1d42
6181 * ├── f3
6182 * └── f4
6183 *
6184 * Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor:
6185 * 1. first bind mount: s1d3 -> s1d2
6186 * 2. second bind mount: s2d3
6187 * 3. tmp mount: s4d1 -> tmp [disconnected branch]
6188 * 4. second bind mount: s2d2
6189 * 5. tmp mount: s3d1 -> tmp
6190 * 6. parent mounts: [...] -> /
6191 *
6192 * The s4d1 directory is evaluated even if it is not in the s2d2 mount.
6193 */
6194
6195 /* clang-format off */
FIXTURE(layout5_disconnected_branch)6196 FIXTURE(layout5_disconnected_branch) {
6197 int s2d4_fd, s3d2_fd;
6198 };
6199 /* clang-format on */
6200
FIXTURE_SETUP(layout5_disconnected_branch)6201 FIXTURE_SETUP(layout5_disconnected_branch)
6202 {
6203 prepare_layout(_metadata);
6204
6205 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1");
6206 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2");
6207 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3");
6208 create_file(_metadata, TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4");
6209 create_directory(_metadata, TMP_DIR "/s2d1/s2d2/s2d3/s2d4");
6210 create_directory(_metadata, TMP_DIR "/s3d1/s3d2");
6211 create_directory(_metadata, TMP_DIR "/s4d1");
6212
6213 self->s2d4_fd = open(TMP_DIR "/s2d1/s2d2/s2d3/s2d4",
6214 O_DIRECTORY | O_PATH | O_CLOEXEC);
6215 ASSERT_LE(0, self->s2d4_fd);
6216
6217 self->s3d2_fd =
6218 open(TMP_DIR "/s3d1/s3d2", O_DIRECTORY | O_PATH | O_CLOEXEC);
6219 ASSERT_LE(0, self->s3d2_fd);
6220
6221 set_cap(_metadata, CAP_SYS_ADMIN);
6222 ASSERT_EQ(0, mount(TMP_DIR "/s1d1/s1d2", TMP_DIR "/s2d1/s2d2/s2d3/s2d4",
6223 NULL, MS_BIND, NULL));
6224 ASSERT_EQ(0, mount(TMP_DIR "/s2d1/s2d2", TMP_DIR "/s3d1/s3d2", NULL,
6225 MS_BIND | MS_REC, NULL));
6226 clear_cap(_metadata, CAP_SYS_ADMIN);
6227 }
6228
FIXTURE_TEARDOWN_PARENT(layout5_disconnected_branch)6229 FIXTURE_TEARDOWN_PARENT(layout5_disconnected_branch)
6230 {
6231 /* Bind mounts are handled by namespace lifetime. */
6232
6233 /* Removes files after renames. */
6234 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1");
6235 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2");
6236 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1");
6237 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3");
6238 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4");
6239 remove_path(TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5");
6240
6241 cleanup_layout(_metadata);
6242 }
6243
FIXTURE_VARIANT(layout5_disconnected_branch)6244 FIXTURE_VARIANT(layout5_disconnected_branch)
6245 {
6246 /*
6247 * Parent of all files. It should always be enforced when testing against
6248 * files under the s1d41 or s1d42 disconnected directories.
6249 */
6250 const __u64 allowed_base;
6251 /*
6252 * Parent of the first bind mount source. It should always be ignored when
6253 * testing against files under the s1d41 or s1d42 disconnected directories.
6254 */
6255 const __u64 allowed_s1d1;
6256 const __u64 allowed_s1d2;
6257 const __u64 allowed_s1d3;
6258 const __u64 allowed_s2d1;
6259 const __u64 allowed_s2d2;
6260 const __u64 allowed_s2d3;
6261 const __u64 allowed_s2d4;
6262 const __u64 allowed_s3d1;
6263 const __u64 allowed_s3d2;
6264 const __u64 allowed_s4d1;
6265
6266 /* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */
6267 const int expected_read_result;
6268 /*
6269 * Expected result of the call to renameat([fd:s1d3]/s1d41/f1,
6270 * [fd:s1d3]/s1d42/f1).
6271 */
6272 const int expected_rename_result;
6273 /*
6274 * Expected result of the call to renameat([fd:s1d3]/s1d41/f2,
6275 * [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE).
6276 */
6277 const int expected_exchange_result;
6278 /*
6279 * Expected result of the call to renameat([fd:s1d3]/s1d42/f4,
6280 * [fd:s1d3]/s1d42/f5).
6281 */
6282 const int expected_same_dir_rename_result;
6283 };
6284
6285 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d1_mount1_src_parent)6286 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) {
6287 /* clang-format on */
6288 .allowed_s1d1 = LANDLOCK_ACCESS_FS_REFER |
6289 LANDLOCK_ACCESS_FS_READ_FILE |
6290 LANDLOCK_ACCESS_FS_EXECUTE |
6291 LANDLOCK_ACCESS_FS_MAKE_REG,
6292 .expected_read_result = EACCES,
6293 .expected_same_dir_rename_result = EACCES,
6294 .expected_rename_result = EACCES,
6295 .expected_exchange_result = EACCES,
6296 };
6297
6298 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d2_mount1_src_refer)6299 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) {
6300 /* clang-format on */
6301 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
6302 .expected_read_result = 0,
6303 .expected_same_dir_rename_result = EACCES,
6304 .expected_rename_result = EACCES,
6305 .expected_exchange_result = EACCES,
6306 };
6307
6308 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d2_mount1_src_create)6309 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) {
6310 /* clang-format on */
6311 .allowed_s1d2 = LANDLOCK_ACCESS_FS_READ_FILE |
6312 LANDLOCK_ACCESS_FS_MAKE_REG,
6313 .expected_read_result = 0,
6314 .expected_same_dir_rename_result = 0,
6315 .expected_rename_result = EXDEV,
6316 .expected_exchange_result = EXDEV,
6317 };
6318
6319 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d2_mount1_src_rename)6320 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) {
6321 /* clang-format on */
6322 .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
6323 .expected_read_result = EACCES,
6324 .expected_same_dir_rename_result = 0,
6325 .expected_rename_result = 0,
6326 .expected_exchange_result = 0,
6327 };
6328
6329 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d3_fd_refer)6330 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) {
6331 /* clang-format on */
6332 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
6333 .expected_read_result = 0,
6334 .expected_same_dir_rename_result = EACCES,
6335 .expected_rename_result = EACCES,
6336 .expected_exchange_result = EACCES,
6337 };
6338
6339 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d3_fd_create)6340 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) {
6341 /* clang-format on */
6342 .allowed_s1d3 = LANDLOCK_ACCESS_FS_READ_FILE |
6343 LANDLOCK_ACCESS_FS_MAKE_REG,
6344 .expected_read_result = 0,
6345 .expected_same_dir_rename_result = 0,
6346 .expected_rename_result = EXDEV,
6347 .expected_exchange_result = EXDEV,
6348 };
6349
6350 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d3_fd_rename)6351 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) {
6352 /* clang-format on */
6353 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
6354 .expected_read_result = EACCES,
6355 .expected_same_dir_rename_result = 0,
6356 .expected_rename_result = 0,
6357 .expected_exchange_result = 0,
6358 };
6359
6360 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s1d3_fd_full)6361 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) {
6362 /* clang-format on */
6363 .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER |
6364 LANDLOCK_ACCESS_FS_READ_FILE |
6365 LANDLOCK_ACCESS_FS_EXECUTE |
6366 LANDLOCK_ACCESS_FS_MAKE_REG,
6367 .expected_read_result = 0,
6368 .expected_same_dir_rename_result = 0,
6369 .expected_rename_result = 0,
6370 .expected_exchange_result = 0,
6371 };
6372
6373 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d1_mount2_src_parent)6374 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) {
6375 /* clang-format on */
6376 .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER |
6377 LANDLOCK_ACCESS_FS_READ_FILE |
6378 LANDLOCK_ACCESS_FS_EXECUTE |
6379 LANDLOCK_ACCESS_FS_MAKE_REG,
6380 .expected_read_result = EACCES,
6381 .expected_same_dir_rename_result = EACCES,
6382 .expected_rename_result = EACCES,
6383 .expected_exchange_result = EACCES,
6384 };
6385
6386 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d2_mount2_src_refer)6387 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) {
6388 /* clang-format on */
6389 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
6390 .expected_read_result = 0,
6391 .expected_same_dir_rename_result = EACCES,
6392 .expected_rename_result = EACCES,
6393 .expected_exchange_result = EACCES,
6394 };
6395
6396 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d2_mount2_src_create)6397 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) {
6398 /* clang-format on */
6399 .allowed_s2d2 = LANDLOCK_ACCESS_FS_READ_FILE |
6400 LANDLOCK_ACCESS_FS_MAKE_REG,
6401 .expected_read_result = 0,
6402 .expected_same_dir_rename_result = 0,
6403 .expected_rename_result = EXDEV,
6404 .expected_exchange_result = EXDEV,
6405 };
6406
6407 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d2_mount2_src_rename)6408 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) {
6409 /* clang-format on */
6410 .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
6411 .expected_read_result = EACCES,
6412 .expected_same_dir_rename_result = 0,
6413 .expected_rename_result = 0,
6414 .expected_exchange_result = 0,
6415 };
6416
6417 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d3_mount1_dst_parent_refer)6418 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) {
6419 /* clang-format on */
6420 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
6421 .expected_read_result = 0,
6422 .expected_same_dir_rename_result = EACCES,
6423 .expected_rename_result = EACCES,
6424 .expected_exchange_result = EACCES,
6425 };
6426
6427 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d3_mount1_dst_parent_create)6428 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) {
6429 /* clang-format on */
6430 .allowed_s2d3 = LANDLOCK_ACCESS_FS_READ_FILE |
6431 LANDLOCK_ACCESS_FS_MAKE_REG,
6432 .expected_read_result = 0,
6433 .expected_same_dir_rename_result = 0,
6434 .expected_rename_result = EXDEV,
6435 .expected_exchange_result = EXDEV,
6436 };
6437
6438 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d3_mount1_dst_parent_rename)6439 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) {
6440 /* clang-format on */
6441 .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
6442 .expected_read_result = EACCES,
6443 .expected_same_dir_rename_result = 0,
6444 .expected_rename_result = 0,
6445 .expected_exchange_result = 0,
6446 };
6447
6448 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s2d4_mount1_dst)6449 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) {
6450 /* clang-format on */
6451 .allowed_s2d4 = LANDLOCK_ACCESS_FS_REFER |
6452 LANDLOCK_ACCESS_FS_READ_FILE |
6453 LANDLOCK_ACCESS_FS_EXECUTE |
6454 LANDLOCK_ACCESS_FS_MAKE_REG,
6455 .expected_read_result = EACCES,
6456 .expected_same_dir_rename_result = EACCES,
6457 .expected_rename_result = EACCES,
6458 .expected_exchange_result = EACCES,
6459 };
6460
6461 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s3d1_mount2_dst_parent_refer)6462 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) {
6463 /* clang-format on */
6464 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
6465 .expected_read_result = 0,
6466 .expected_same_dir_rename_result = EACCES,
6467 .expected_rename_result = EACCES,
6468 .expected_exchange_result = EACCES,
6469 };
6470
6471 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s3d1_mount2_dst_parent_create)6472 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) {
6473 /* clang-format on */
6474 .allowed_s3d1 = LANDLOCK_ACCESS_FS_READ_FILE |
6475 LANDLOCK_ACCESS_FS_MAKE_REG,
6476 .expected_read_result = 0,
6477 .expected_same_dir_rename_result = 0,
6478 .expected_rename_result = EXDEV,
6479 .expected_exchange_result = EXDEV,
6480 };
6481
6482 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s3d1_mount2_dst_parent_rename)6483 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) {
6484 /* clang-format on */
6485 .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
6486 .expected_read_result = EACCES,
6487 .expected_same_dir_rename_result = 0,
6488 .expected_rename_result = 0,
6489 .expected_exchange_result = 0,
6490 };
6491
6492 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s3d2_mount1_dst)6493 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) {
6494 /* clang-format on */
6495 .allowed_s3d2 = LANDLOCK_ACCESS_FS_REFER |
6496 LANDLOCK_ACCESS_FS_READ_FILE |
6497 LANDLOCK_ACCESS_FS_EXECUTE |
6498 LANDLOCK_ACCESS_FS_MAKE_REG,
6499 .expected_read_result = EACCES,
6500 .expected_same_dir_rename_result = EACCES,
6501 .expected_rename_result = EACCES,
6502 .expected_exchange_result = EACCES,
6503 };
6504
6505 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s4d1_rename_parent_refer)6506 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) {
6507 /* clang-format on */
6508 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE,
6509 .expected_read_result = 0,
6510 .expected_same_dir_rename_result = EACCES,
6511 .expected_rename_result = EACCES,
6512 .expected_exchange_result = EACCES,
6513 };
6514
6515 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s4d1_rename_parent_create)6516 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) {
6517 /* clang-format on */
6518 .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE |
6519 LANDLOCK_ACCESS_FS_MAKE_REG,
6520 .expected_read_result = 0,
6521 .expected_same_dir_rename_result = 0,
6522 .expected_rename_result = EXDEV,
6523 .expected_exchange_result = EXDEV,
6524 };
6525
6526 /* clang-format off */
FIXTURE_VARIANT_ADD(layout5_disconnected_branch,s4d1_rename_parent_rename)6527 FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) {
6528 /* clang-format on */
6529 .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG,
6530 .expected_read_result = EACCES,
6531 .expected_same_dir_rename_result = 0,
6532 .expected_rename_result = 0,
6533 .expected_exchange_result = 0,
6534 };
6535
TEST_F_FORK(layout5_disconnected_branch,read_rename_exchange)6536 TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange)
6537 {
6538 const __u64 handled_access =
6539 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE |
6540 LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_MAKE_REG;
6541 const struct rule rules[] = {
6542 {
6543 .path = TMP_DIR "/s1d1",
6544 .access = variant->allowed_s1d1,
6545 },
6546 {
6547 .path = TMP_DIR "/s1d1/s1d2",
6548 .access = variant->allowed_s1d2,
6549 },
6550 {
6551 .path = TMP_DIR "/s1d1/s1d2/s1d3",
6552 .access = variant->allowed_s1d3,
6553 },
6554 {
6555 .path = TMP_DIR "/s2d1",
6556 .access = variant->allowed_s2d1,
6557 },
6558 {
6559 .path = TMP_DIR "/s2d1/s2d2",
6560 .access = variant->allowed_s2d2,
6561 },
6562 {
6563 .path = TMP_DIR "/s2d1/s2d2/s2d3",
6564 .access = variant->allowed_s2d3,
6565 },
6566 /* s2d4_fd */
6567 {
6568 .path = TMP_DIR "/s3d1",
6569 .access = variant->allowed_s3d1,
6570 },
6571 /* s3d2_fd */
6572 {
6573 .path = TMP_DIR "/s4d1",
6574 .access = variant->allowed_s4d1,
6575 },
6576 {},
6577 };
6578 int ruleset_fd, s1d3_bind_fd;
6579
6580 ruleset_fd = create_ruleset(_metadata, handled_access, rules);
6581 ASSERT_LE(0, ruleset_fd);
6582
6583 /* Adds rules for the covered directories. */
6584 if (variant->allowed_s2d4) {
6585 ASSERT_EQ(0, landlock_add_rule(
6586 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
6587 &(struct landlock_path_beneath_attr){
6588 .parent_fd = self->s2d4_fd,
6589 .allowed_access =
6590 variant->allowed_s2d4,
6591 },
6592 0));
6593 }
6594 EXPECT_EQ(0, close(self->s2d4_fd));
6595
6596 if (variant->allowed_s3d2) {
6597 ASSERT_EQ(0, landlock_add_rule(
6598 ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
6599 &(struct landlock_path_beneath_attr){
6600 .parent_fd = self->s3d2_fd,
6601 .allowed_access =
6602 variant->allowed_s3d2,
6603 },
6604 0));
6605 }
6606 EXPECT_EQ(0, close(self->s3d2_fd));
6607
6608 s1d3_bind_fd = open(TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3",
6609 O_DIRECTORY | O_PATH | O_CLOEXEC);
6610 ASSERT_LE(0, s1d3_bind_fd);
6611
6612 /* Disconnects and checks source and destination directories. */
6613 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY));
6614 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY));
6615 /* Renames to make it accessible through s3d1/s1d41 */
6616 ASSERT_EQ(0, test_renameat(AT_FDCWD, TMP_DIR "/s2d1/s2d2/s2d3",
6617 AT_FDCWD, TMP_DIR "/s4d1/s2d3"));
6618 EXPECT_EQ(0, test_open_rel(s1d3_bind_fd, "..", O_DIRECTORY));
6619 EXPECT_EQ(ENOENT, test_open_rel(s1d3_bind_fd, "../..", O_DIRECTORY));
6620
6621 enforce_ruleset(_metadata, ruleset_fd);
6622 EXPECT_EQ(0, close(ruleset_fd));
6623
6624 EXPECT_EQ(variant->expected_read_result,
6625 test_open_rel(s1d3_bind_fd, "s1d41/f1", O_RDONLY));
6626
6627 EXPECT_EQ(variant->expected_rename_result,
6628 test_renameat(s1d3_bind_fd, "s1d41/f1", s1d3_bind_fd,
6629 "s1d42/f1"));
6630 EXPECT_EQ(variant->expected_exchange_result,
6631 test_exchangeat(s1d3_bind_fd, "s1d41/f2", s1d3_bind_fd,
6632 "s1d42/f3"));
6633
6634 EXPECT_EQ(variant->expected_same_dir_rename_result,
6635 test_renameat(s1d3_bind_fd, "s1d42/f4", s1d3_bind_fd,
6636 "s1d42/f5"));
6637 }
6638
6639 #define LOWER_BASE TMP_DIR "/lower"
6640 #define LOWER_DATA LOWER_BASE "/data"
6641 static const char lower_fl1[] = LOWER_DATA "/fl1";
6642 static const char lower_dl1[] = LOWER_DATA "/dl1";
6643 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
6644 static const char lower_fo1[] = LOWER_DATA "/fo1";
6645 static const char lower_do1[] = LOWER_DATA "/do1";
6646 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
6647 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
6648 /* lower_pl1 is a FIFO and is deliberately not in the lists below. */
6649 static const char lower_pl1[] = LOWER_DATA "/pl1";
6650
6651 static const char (*lower_base_files[])[] = {
6652 &lower_fl1,
6653 &lower_fo1,
6654 NULL,
6655 };
6656 static const char (*lower_base_directories[])[] = {
6657 &lower_dl1,
6658 &lower_do1,
6659 NULL,
6660 };
6661 static const char (*lower_sub_files[])[] = {
6662 &lower_dl1_fl2,
6663 &lower_do1_fo2,
6664 &lower_do1_fl3,
6665 NULL,
6666 };
6667
6668 #define UPPER_BASE TMP_DIR "/upper"
6669 #define UPPER_DATA UPPER_BASE "/data"
6670 #define UPPER_WORK UPPER_BASE "/work"
6671 static const char upper_fu1[] = UPPER_DATA "/fu1";
6672 static const char upper_du1[] = UPPER_DATA "/du1";
6673 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
6674 static const char upper_fo1[] = UPPER_DATA "/fo1";
6675 static const char upper_do1[] = UPPER_DATA "/do1";
6676 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
6677 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
6678
6679 static const char (*upper_base_files[])[] = {
6680 &upper_fu1,
6681 &upper_fo1,
6682 NULL,
6683 };
6684 static const char (*upper_base_directories[])[] = {
6685 &upper_du1,
6686 &upper_do1,
6687 NULL,
6688 };
6689 static const char (*upper_sub_files[])[] = {
6690 &upper_du1_fu2,
6691 &upper_do1_fo2,
6692 &upper_do1_fu3,
6693 NULL,
6694 };
6695
6696 #define MERGE_BASE TMP_DIR "/merge"
6697 #define MERGE_DATA MERGE_BASE "/data"
6698 static const char merge_fl1[] = MERGE_DATA "/fl1";
6699 /* merge_pl1 is a FIFO and is deliberately not in the lists below. */
6700 static const char merge_pl1[] = MERGE_DATA "/pl1";
6701 static const char merge_dl1[] = MERGE_DATA "/dl1";
6702 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
6703 static const char merge_fu1[] = MERGE_DATA "/fu1";
6704 static const char merge_du1[] = MERGE_DATA "/du1";
6705 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
6706 static const char merge_fo1[] = MERGE_DATA "/fo1";
6707 static const char merge_do1[] = MERGE_DATA "/do1";
6708 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
6709 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
6710 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
6711
6712 static const char (*merge_base_files[])[] = {
6713 &merge_fl1,
6714 &merge_fu1,
6715 &merge_fo1,
6716 NULL,
6717 };
6718 static const char (*merge_base_directories[])[] = {
6719 &merge_dl1,
6720 &merge_du1,
6721 &merge_do1,
6722 NULL,
6723 };
6724 static const char (*merge_sub_files[])[] = {
6725 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,
6726 &merge_do1_fl3, &merge_do1_fu3, NULL,
6727 };
6728
6729 /*
6730 * layout2_overlay hierarchy:
6731 *
6732 * tmp
6733 * ├── lower
6734 * │ └── data
6735 * │ ├── dl1
6736 * │ │ └── fl2
6737 * │ ├── do1
6738 * │ │ ├── fl3
6739 * │ │ └── fo2
6740 * │ ├── fl1
6741 * │ ├── fo1
6742 * │ └── pl1 [FIFO]
6743 * ├── merge
6744 * │ └── data
6745 * │ ├── dl1
6746 * │ │ └── fl2
6747 * │ ├── do1
6748 * │ │ ├── fl3
6749 * │ │ ├── fo2
6750 * │ │ └── fu3
6751 * │ ├── du1
6752 * │ │ └── fu2
6753 * │ ├── fl1
6754 * │ ├── fo1
6755 * │ ├── fu1
6756 * │ └── pl1 [FIFO]
6757 * └── upper
6758 * ├── data
6759 * │ ├── do1
6760 * │ │ ├── fo2
6761 * │ │ └── fu3
6762 * │ ├── du1
6763 * │ │ └── fu2
6764 * │ ├── fo1
6765 * │ └── fu1
6766 * └── work
6767 * └── work
6768 */
6769
FIXTURE(layout2_overlay)6770 FIXTURE(layout2_overlay)
6771 {
6772 bool skip_test;
6773 };
6774
FIXTURE_SETUP(layout2_overlay)6775 FIXTURE_SETUP(layout2_overlay)
6776 {
6777 if (!supports_filesystem("overlay")) {
6778 self->skip_test = true;
6779 SKIP(return, "overlayfs is not supported (setup)");
6780 }
6781
6782 prepare_layout(_metadata);
6783
6784 create_directory(_metadata, LOWER_BASE);
6785 set_cap(_metadata, CAP_SYS_ADMIN);
6786 /* Creates tmpfs mount points to get deterministic overlayfs. */
6787 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));
6788 clear_cap(_metadata, CAP_SYS_ADMIN);
6789 create_file(_metadata, lower_fl1);
6790 create_file(_metadata, lower_dl1_fl2);
6791 create_file(_metadata, lower_fo1);
6792 create_file(_metadata, lower_do1_fo2);
6793 create_file(_metadata, lower_do1_fl3);
6794 ASSERT_EQ(0, mknod(lower_pl1, S_IFIFO | 0600, 0));
6795
6796 create_directory(_metadata, UPPER_BASE);
6797 set_cap(_metadata, CAP_SYS_ADMIN);
6798 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));
6799 clear_cap(_metadata, CAP_SYS_ADMIN);
6800 create_file(_metadata, upper_fu1);
6801 create_file(_metadata, upper_du1_fu2);
6802 create_file(_metadata, upper_fo1);
6803 create_file(_metadata, upper_do1_fo2);
6804 create_file(_metadata, upper_do1_fu3);
6805 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));
6806
6807 create_directory(_metadata, MERGE_DATA);
6808 set_cap(_metadata, CAP_SYS_ADMIN);
6809 set_cap(_metadata, CAP_DAC_OVERRIDE);
6810 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,
6811 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA
6812 ",workdir=" UPPER_WORK));
6813 clear_cap(_metadata, CAP_DAC_OVERRIDE);
6814 clear_cap(_metadata, CAP_SYS_ADMIN);
6815 }
6816
FIXTURE_TEARDOWN_PARENT(layout2_overlay)6817 FIXTURE_TEARDOWN_PARENT(layout2_overlay)
6818 {
6819 if (self->skip_test)
6820 SKIP(return, "overlayfs is not supported (teardown)");
6821
6822 EXPECT_EQ(0, remove_path(lower_do1_fl3));
6823 EXPECT_EQ(0, remove_path(lower_dl1_fl2));
6824 EXPECT_EQ(0, remove_path(lower_fl1));
6825 EXPECT_EQ(0, remove_path(lower_do1_fo2));
6826 EXPECT_EQ(0, remove_path(lower_fo1));
6827 EXPECT_EQ(0, remove_path(lower_pl1));
6828
6829 /* umount(LOWER_BASE)) is handled by namespace lifetime. */
6830 EXPECT_EQ(0, remove_path(LOWER_BASE));
6831
6832 EXPECT_EQ(0, remove_path(upper_do1_fu3));
6833 EXPECT_EQ(0, remove_path(upper_du1_fu2));
6834 EXPECT_EQ(0, remove_path(upper_fu1));
6835 EXPECT_EQ(0, remove_path(upper_do1_fo2));
6836 EXPECT_EQ(0, remove_path(upper_fo1));
6837 EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));
6838
6839 /* umount(UPPER_BASE)) is handled by namespace lifetime. */
6840 EXPECT_EQ(0, remove_path(UPPER_BASE));
6841
6842 /* umount(MERGE_DATA)) is handled by namespace lifetime. */
6843 EXPECT_EQ(0, remove_path(MERGE_DATA));
6844
6845 cleanup_layout(_metadata);
6846 }
6847
TEST_F_FORK(layout2_overlay,no_restriction)6848 TEST_F_FORK(layout2_overlay, no_restriction)
6849 {
6850 if (self->skip_test)
6851 SKIP(return, "overlayfs is not supported (test)");
6852
6853 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
6854 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
6855 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
6856 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));
6857 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));
6858 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));
6859 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));
6860
6861 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));
6862 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));
6863 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));
6864 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));
6865 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));
6866 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));
6867 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));
6868
6869 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));
6870 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));
6871 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));
6872 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));
6873 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));
6874 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));
6875 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));
6876 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));
6877 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));
6878 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));
6879 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));
6880 }
6881
6882 #define for_each_path(path_list, path_entry, i) \
6883 for (i = 0, path_entry = *path_list[i]; path_list[i]; \
6884 path_entry = *path_list[++i])
6885
TEST_F_FORK(layout2_overlay,same_content_different_file)6886 TEST_F_FORK(layout2_overlay, same_content_different_file)
6887 {
6888 /* Sets access right on parent directories of both layers. */
6889 const struct rule layer1_base[] = {
6890 {
6891 .path = LOWER_BASE,
6892 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6893 },
6894 {
6895 .path = UPPER_BASE,
6896 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6897 },
6898 {
6899 .path = MERGE_BASE,
6900 .access = ACCESS_RW,
6901 },
6902 {},
6903 };
6904 const struct rule layer2_data[] = {
6905 {
6906 .path = LOWER_DATA,
6907 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6908 },
6909 {
6910 .path = UPPER_DATA,
6911 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6912 },
6913 {
6914 .path = MERGE_DATA,
6915 .access = ACCESS_RW,
6916 },
6917 {},
6918 };
6919 /* Sets access right on directories inside both layers. */
6920 const struct rule layer3_subdirs[] = {
6921 {
6922 .path = lower_dl1,
6923 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6924 },
6925 {
6926 .path = lower_do1,
6927 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6928 },
6929 {
6930 .path = upper_du1,
6931 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6932 },
6933 {
6934 .path = upper_do1,
6935 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6936 },
6937 {
6938 .path = merge_dl1,
6939 .access = ACCESS_RW,
6940 },
6941 {
6942 .path = merge_du1,
6943 .access = ACCESS_RW,
6944 },
6945 {
6946 .path = merge_do1,
6947 .access = ACCESS_RW,
6948 },
6949 {},
6950 };
6951 /* Tighten access rights to the files. */
6952 const struct rule layer4_files[] = {
6953 {
6954 .path = lower_dl1_fl2,
6955 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6956 },
6957 {
6958 .path = lower_do1_fo2,
6959 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6960 },
6961 {
6962 .path = lower_do1_fl3,
6963 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6964 },
6965 {
6966 .path = upper_du1_fu2,
6967 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6968 },
6969 {
6970 .path = upper_do1_fo2,
6971 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6972 },
6973 {
6974 .path = upper_do1_fu3,
6975 .access = LANDLOCK_ACCESS_FS_READ_FILE,
6976 },
6977 {
6978 .path = merge_dl1_fl2,
6979 .access = LANDLOCK_ACCESS_FS_READ_FILE |
6980 LANDLOCK_ACCESS_FS_WRITE_FILE,
6981 },
6982 {
6983 .path = merge_du1_fu2,
6984 .access = LANDLOCK_ACCESS_FS_READ_FILE |
6985 LANDLOCK_ACCESS_FS_WRITE_FILE,
6986 },
6987 {
6988 .path = merge_do1_fo2,
6989 .access = LANDLOCK_ACCESS_FS_READ_FILE |
6990 LANDLOCK_ACCESS_FS_WRITE_FILE,
6991 },
6992 {
6993 .path = merge_do1_fl3,
6994 .access = LANDLOCK_ACCESS_FS_READ_FILE |
6995 LANDLOCK_ACCESS_FS_WRITE_FILE,
6996 },
6997 {
6998 .path = merge_do1_fu3,
6999 .access = LANDLOCK_ACCESS_FS_READ_FILE |
7000 LANDLOCK_ACCESS_FS_WRITE_FILE,
7001 },
7002 {},
7003 };
7004 const struct rule layer5_merge_only[] = {
7005 {
7006 .path = MERGE_DATA,
7007 .access = LANDLOCK_ACCESS_FS_READ_FILE |
7008 LANDLOCK_ACCESS_FS_WRITE_FILE,
7009 },
7010 {},
7011 };
7012 size_t i;
7013 const char *path_entry;
7014
7015 if (self->skip_test)
7016 SKIP(return, "overlayfs is not supported (test)");
7017
7018 /* Sets rules on base directories (i.e. outside overlay scope). */
7019 enforce_fs(_metadata, ACCESS_RW, layer1_base);
7020
7021 /* Checks lower layer. */
7022 for_each_path(lower_base_files, path_entry, i) {
7023 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
7024 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
7025 }
7026 for_each_path(lower_base_directories, path_entry, i) {
7027 ASSERT_EQ(EACCES,
7028 test_open(path_entry, O_RDONLY | O_DIRECTORY));
7029 }
7030 for_each_path(lower_sub_files, path_entry, i) {
7031 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
7032 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
7033 }
7034 /* Checks upper layer. */
7035 for_each_path(upper_base_files, path_entry, i) {
7036 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
7037 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
7038 }
7039 for_each_path(upper_base_directories, path_entry, i) {
7040 ASSERT_EQ(EACCES,
7041 test_open(path_entry, O_RDONLY | O_DIRECTORY));
7042 }
7043 for_each_path(upper_sub_files, path_entry, i) {
7044 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
7045 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
7046 }
7047 /*
7048 * Checks that access rights are independent from the lower and upper
7049 * layers: write access to upper files viewed through the merge point
7050 * is still allowed, and write access to lower file viewed (and copied)
7051 * through the merge point is still allowed.
7052 */
7053 for_each_path(merge_base_files, path_entry, i) {
7054 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
7055 }
7056 for_each_path(merge_base_directories, path_entry, i) {
7057 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
7058 }
7059 for_each_path(merge_sub_files, path_entry, i) {
7060 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
7061 }
7062
7063 /* Sets rules on data directories (i.e. inside overlay scope). */
7064 enforce_fs(_metadata, ACCESS_RW, layer2_data);
7065
7066 /* Checks merge. */
7067 for_each_path(merge_base_files, path_entry, i) {
7068 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
7069 }
7070 for_each_path(merge_base_directories, path_entry, i) {
7071 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
7072 }
7073 for_each_path(merge_sub_files, path_entry, i) {
7074 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
7075 }
7076
7077 /* Same checks with tighter rules. */
7078 enforce_fs(_metadata, ACCESS_RW, layer3_subdirs);
7079
7080 /* Checks changes for lower layer. */
7081 for_each_path(lower_base_files, path_entry, i) {
7082 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
7083 }
7084 /* Checks changes for upper layer. */
7085 for_each_path(upper_base_files, path_entry, i) {
7086 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
7087 }
7088 /* Checks all merge accesses. */
7089 for_each_path(merge_base_files, path_entry, i) {
7090 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
7091 }
7092 for_each_path(merge_base_directories, path_entry, i) {
7093 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
7094 }
7095 for_each_path(merge_sub_files, path_entry, i) {
7096 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
7097 }
7098
7099 /* Sets rules directly on overlayed files. */
7100 enforce_fs(_metadata, ACCESS_RW, layer4_files);
7101
7102 /* Checks unchanged accesses on lower layer. */
7103 for_each_path(lower_sub_files, path_entry, i) {
7104 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
7105 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
7106 }
7107 /* Checks unchanged accesses on upper layer. */
7108 for_each_path(upper_sub_files, path_entry, i) {
7109 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
7110 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
7111 }
7112 /* Checks all merge accesses. */
7113 for_each_path(merge_base_files, path_entry, i) {
7114 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
7115 }
7116 for_each_path(merge_base_directories, path_entry, i) {
7117 ASSERT_EQ(EACCES,
7118 test_open(path_entry, O_RDONLY | O_DIRECTORY));
7119 }
7120 for_each_path(merge_sub_files, path_entry, i) {
7121 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
7122 }
7123
7124 /* Only allows access to the merge hierarchy. */
7125 enforce_fs(_metadata, ACCESS_RW, layer5_merge_only);
7126
7127 /* Checks new accesses on lower layer. */
7128 for_each_path(lower_sub_files, path_entry, i) {
7129 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
7130 }
7131 /* Checks new accesses on upper layer. */
7132 for_each_path(upper_sub_files, path_entry, i) {
7133 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
7134 }
7135 /* Checks all merge accesses. */
7136 for_each_path(merge_base_files, path_entry, i) {
7137 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
7138 }
7139 for_each_path(merge_base_directories, path_entry, i) {
7140 ASSERT_EQ(EACCES,
7141 test_open(path_entry, O_RDONLY | O_DIRECTORY));
7142 }
7143 for_each_path(merge_sub_files, path_entry, i) {
7144 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
7145 }
7146 }
7147
TEST_F_FORK(layout2_overlay,rename_in_overlay_without_make_reg)7148 TEST_F_FORK(layout2_overlay, rename_in_overlay_without_make_reg)
7149 {
7150 const char *const merge_pl1_renamed = MERGE_DATA "/pl1_renamed";
7151
7152 if (self->skip_test)
7153 SKIP(return, "overlayfs is not supported (test)");
7154
7155 /*
7156 * merge_pl1 is a FIFO which only exists in the lower layer. Before
7157 * the rename, the upper layer has no entry under this name.
7158 */
7159 ASSERT_TRUE(is_fifo(merge_pl1));
7160 ASSERT_TRUE(is_missing(UPPER_DATA "/pl1"));
7161
7162 /* MAKE_REG is restricted, but MAKE_FIFO is not. */
7163 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, NULL);
7164
7165 /*
7166 * Rename the FIFO through OverlayFS. merge_pl1 originates from the
7167 * lower layer, so this triggers a copy-up and creates the whiteout in
7168 * the upper layer to hide the lower layer FIFO file. Even though
7169 * MAKE_REG is restricted, the rename on the OverlayFS works.
7170 */
7171 EXPECT_EQ(0, rename(merge_pl1, merge_pl1_renamed));
7172
7173 /* Check that the rename worked. */
7174 EXPECT_TRUE(is_fifo(merge_pl1_renamed));
7175 EXPECT_TRUE(is_missing(merge_pl1));
7176
7177 /*
7178 * Check that the whiteout object was created on the underlying "upper"
7179 * filesystem during the rename. This is OK because the whiteout object
7180 * was created by OverlayFS, not by the calling task.
7181 */
7182 EXPECT_TRUE(is_whiteout(UPPER_DATA "/pl1"));
7183 }
7184
FIXTURE(layout3_fs)7185 FIXTURE(layout3_fs)
7186 {
7187 bool has_created_dir;
7188 bool has_created_file;
7189 bool skip_test;
7190 };
7191
FIXTURE_VARIANT(layout3_fs)7192 FIXTURE_VARIANT(layout3_fs)
7193 {
7194 const struct mnt_opt mnt;
7195 const char *const file_path;
7196 unsigned int cwd_fs_magic;
7197 };
7198
7199 /* clang-format off */
FIXTURE_VARIANT_ADD(layout3_fs,tmpfs)7200 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {
7201 /* clang-format on */
7202 .mnt = {
7203 .type = "tmpfs",
7204 .data = MNT_TMP_DATA,
7205 },
7206 .file_path = file1_s1d1,
7207 };
7208
FIXTURE_VARIANT_ADD(layout3_fs,ramfs)7209 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) {
7210 .mnt = {
7211 .type = "ramfs",
7212 .data = "mode=700",
7213 },
7214 .file_path = TMP_DIR "/dir/file",
7215 };
7216
FIXTURE_VARIANT_ADD(layout3_fs,cgroup2)7217 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {
7218 .mnt = {
7219 .type = "cgroup2",
7220 },
7221 .file_path = TMP_DIR "/test/cgroup.procs",
7222 };
7223
FIXTURE_VARIANT_ADD(layout3_fs,proc)7224 FIXTURE_VARIANT_ADD(layout3_fs, proc) {
7225 .mnt = {
7226 .type = "proc",
7227 },
7228 .file_path = TMP_DIR "/self/status",
7229 };
7230
FIXTURE_VARIANT_ADD(layout3_fs,sysfs)7231 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) {
7232 .mnt = {
7233 .type = "sysfs",
7234 },
7235 .file_path = TMP_DIR "/kernel/notes",
7236 };
7237
FIXTURE_VARIANT_ADD(layout3_fs,hostfs)7238 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) {
7239 .mnt = {
7240 .source = TMP_DIR,
7241 .flags = MS_BIND,
7242 },
7243 .file_path = TMP_DIR "/dir/file",
7244 .cwd_fs_magic = HOSTFS_SUPER_MAGIC,
7245 };
7246
dirname_alloc(const char * path)7247 static char *dirname_alloc(const char *path)
7248 {
7249 char *dup;
7250
7251 if (!path)
7252 return NULL;
7253
7254 dup = strdup(path);
7255 if (!dup)
7256 return NULL;
7257
7258 return dirname(dup);
7259 }
7260
FIXTURE_SETUP(layout3_fs)7261 FIXTURE_SETUP(layout3_fs)
7262 {
7263 struct stat statbuf;
7264 char *dir_path = dirname_alloc(variant->file_path);
7265
7266 if (!supports_filesystem(variant->mnt.type) ||
7267 !cwd_matches_fs(variant->cwd_fs_magic)) {
7268 self->skip_test = true;
7269 SKIP(return, "this filesystem is not supported (setup)");
7270 }
7271
7272 prepare_layout_opt(_metadata, &variant->mnt);
7273
7274 /* Creates directory when required. */
7275 if (stat(dir_path, &statbuf)) {
7276 set_cap(_metadata, CAP_DAC_OVERRIDE);
7277 EXPECT_EQ(0, mkdir(dir_path, 0700))
7278 {
7279 TH_LOG("Failed to create directory \"%s\": %s",
7280 dir_path, strerror(errno));
7281 }
7282 self->has_created_dir = true;
7283 clear_cap(_metadata, CAP_DAC_OVERRIDE);
7284 }
7285
7286 /* Creates file when required. */
7287 if (stat(variant->file_path, &statbuf)) {
7288 int fd;
7289
7290 set_cap(_metadata, CAP_DAC_OVERRIDE);
7291 fd = creat(variant->file_path, 0600);
7292 EXPECT_LE(0, fd)
7293 {
7294 TH_LOG("Failed to create file \"%s\": %s",
7295 variant->file_path, strerror(errno));
7296 }
7297 EXPECT_EQ(0, close(fd));
7298 self->has_created_file = true;
7299 clear_cap(_metadata, CAP_DAC_OVERRIDE);
7300 }
7301
7302 free(dir_path);
7303 }
7304
FIXTURE_TEARDOWN_PARENT(layout3_fs)7305 FIXTURE_TEARDOWN_PARENT(layout3_fs)
7306 {
7307 if (self->skip_test)
7308 SKIP(return, "this filesystem is not supported (teardown)");
7309
7310 if (self->has_created_file) {
7311 set_cap(_metadata, CAP_DAC_OVERRIDE);
7312 /*
7313 * Don't check for error because the file might already
7314 * have been removed (cf. release_inode test).
7315 */
7316 unlink(variant->file_path);
7317 clear_cap(_metadata, CAP_DAC_OVERRIDE);
7318 }
7319
7320 if (self->has_created_dir) {
7321 char *dir_path = dirname_alloc(variant->file_path);
7322
7323 set_cap(_metadata, CAP_DAC_OVERRIDE);
7324 /*
7325 * Don't check for error because the directory might already
7326 * have been removed (cf. release_inode test).
7327 */
7328 rmdir(dir_path);
7329 clear_cap(_metadata, CAP_DAC_OVERRIDE);
7330 free(dir_path);
7331 }
7332
7333 cleanup_layout(_metadata);
7334 }
7335
layer3_fs_tag_inode(struct __test_metadata * const _metadata,FIXTURE_DATA (layout3_fs)* self,const FIXTURE_VARIANT (layout3_fs)* variant,const char * const rule_path)7336 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,
7337 FIXTURE_DATA(layout3_fs) * self,
7338 const FIXTURE_VARIANT(layout3_fs) * variant,
7339 const char *const rule_path)
7340 {
7341 const struct rule layer1_allow_read_file[] = {
7342 {
7343 .path = rule_path,
7344 .access = LANDLOCK_ACCESS_FS_READ_FILE,
7345 },
7346 {},
7347 };
7348 const char *const dev_null_path = "/dev/null";
7349
7350 if (self->skip_test)
7351 SKIP(return, "this filesystem is not supported (test)");
7352
7353 /* Checks without Landlock. */
7354 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
7355 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
7356
7357 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
7358 layer1_allow_read_file);
7359
7360 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
7361 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
7362
7363 /* Forbids directory reading. */
7364 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, NULL);
7365
7366 /* Checks with Landlock and forbidden access. */
7367 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
7368 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
7369 }
7370
7371 /* Matrix of tests to check file hierarchy evaluation. */
7372
TEST_F_FORK(layout3_fs,tag_inode_dir_parent)7373 TEST_F_FORK(layout3_fs, tag_inode_dir_parent)
7374 {
7375 /* The current directory must not be the root for this test. */
7376 layer3_fs_tag_inode(_metadata, self, variant, ".");
7377 }
7378
TEST_F_FORK(layout3_fs,tag_inode_dir_mnt)7379 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt)
7380 {
7381 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);
7382 }
7383
TEST_F_FORK(layout3_fs,tag_inode_dir_child)7384 TEST_F_FORK(layout3_fs, tag_inode_dir_child)
7385 {
7386 char *dir_path = dirname_alloc(variant->file_path);
7387
7388 layer3_fs_tag_inode(_metadata, self, variant, dir_path);
7389 free(dir_path);
7390 }
7391
TEST_F_FORK(layout3_fs,tag_inode_file)7392 TEST_F_FORK(layout3_fs, tag_inode_file)
7393 {
7394 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);
7395 }
7396
7397 /* Light version of layout1.release_inodes */
TEST_F_FORK(layout3_fs,release_inodes)7398 TEST_F_FORK(layout3_fs, release_inodes)
7399 {
7400 const struct rule layer1[] = {
7401 {
7402 .path = TMP_DIR,
7403 .access = LANDLOCK_ACCESS_FS_READ_DIR,
7404 },
7405 {},
7406 };
7407 int ruleset_fd;
7408
7409 if (self->skip_test)
7410 SKIP(return, "this filesystem is not supported (test)");
7411
7412 /* Clean up for the teardown to not fail. */
7413 if (self->has_created_file)
7414 EXPECT_EQ(0, remove_path(variant->file_path));
7415
7416 if (self->has_created_dir) {
7417 char *dir_path = dirname_alloc(variant->file_path);
7418
7419 /* Don't check for error because of cgroup specificities. */
7420 remove_path(dir_path);
7421 free(dir_path);
7422 }
7423
7424 ruleset_fd =
7425 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
7426
7427 /* Unmount the filesystem while it is being used by a ruleset. */
7428 set_cap(_metadata, CAP_SYS_ADMIN);
7429 ASSERT_EQ(0, umount(TMP_DIR));
7430 clear_cap(_metadata, CAP_SYS_ADMIN);
7431
7432 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */
7433 set_cap(_metadata, CAP_SYS_ADMIN);
7434 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));
7435 clear_cap(_metadata, CAP_SYS_ADMIN);
7436
7437 enforce_ruleset(_metadata, ruleset_fd);
7438 ASSERT_EQ(0, close(ruleset_fd));
7439
7440 /* Checks that access to the new mount point is denied. */
7441 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));
7442 }
7443
matches_log_fs_extra(struct __test_metadata * const _metadata,int audit_fd,const char * const blockers,const char * const path,const char * const extra)7444 static int matches_log_fs_extra(struct __test_metadata *const _metadata,
7445 int audit_fd, const char *const blockers,
7446 const char *const path, const char *const extra)
7447 {
7448 static const char log_template[] = REGEX_LANDLOCK_PREFIX
7449 " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$";
7450 char *absolute_path = NULL;
7451 size_t log_match_remaining = sizeof(log_template) + strlen(blockers) +
7452 PATH_MAX * 2 +
7453 (extra ? strlen(extra) : 0) + 1;
7454 char log_match[log_match_remaining];
7455 char *log_match_cursor = log_match;
7456 size_t chunk_len;
7457
7458 chunk_len = snprintf(log_match_cursor, log_match_remaining,
7459 REGEX_LANDLOCK_PREFIX " blockers=%s path=\"",
7460 blockers);
7461 if (chunk_len < 0 || chunk_len >= log_match_remaining)
7462 return -E2BIG;
7463
7464 /*
7465 * It is assumed that absolute_path does not contain control
7466 * characters nor spaces, see audit_string_contains_control().
7467 */
7468 absolute_path = realpath(path, NULL);
7469 if (!absolute_path)
7470 return -errno;
7471
7472 log_match_remaining -= chunk_len;
7473 log_match_cursor += chunk_len;
7474 log_match_cursor = regex_escape(absolute_path, log_match_cursor,
7475 log_match_remaining);
7476 free(absolute_path);
7477 if (log_match_cursor < 0)
7478 return (long long)log_match_cursor;
7479
7480 log_match_remaining -= log_match_cursor - log_match;
7481 chunk_len = snprintf(log_match_cursor, log_match_remaining,
7482 "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$",
7483 extra ?: "");
7484 if (chunk_len < 0 || chunk_len >= log_match_remaining)
7485 return -E2BIG;
7486
7487 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
7488 NULL);
7489 }
7490
matches_log_fs(struct __test_metadata * const _metadata,int audit_fd,const char * const blockers,const char * const path)7491 static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd,
7492 const char *const blockers, const char *const path)
7493 {
7494 return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL);
7495 }
7496
FIXTURE(audit_layout1)7497 FIXTURE(audit_layout1)
7498 {
7499 struct audit_filter audit_filter;
7500 int audit_fd;
7501 };
7502
FIXTURE_SETUP(audit_layout1)7503 FIXTURE_SETUP(audit_layout1)
7504 {
7505 prepare_layout(_metadata);
7506
7507 create_layout1(_metadata);
7508
7509 set_cap(_metadata, CAP_AUDIT_CONTROL);
7510 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
7511 EXPECT_LE(0, self->audit_fd);
7512 disable_caps(_metadata);
7513 }
7514
FIXTURE_TEARDOWN_PARENT(audit_layout1)7515 FIXTURE_TEARDOWN_PARENT(audit_layout1)
7516 {
7517 remove_layout1(_metadata);
7518
7519 cleanup_layout(_metadata);
7520
7521 EXPECT_EQ(0, audit_cleanup(-1, NULL));
7522 }
7523
TEST_F(audit_layout1,execute_make)7524 TEST_F(audit_layout1, execute_make)
7525 {
7526 struct audit_records records;
7527
7528 copy_file(_metadata, bin_true, file1_s1d1);
7529 test_execute(_metadata, 0, file1_s1d1);
7530 test_check_exec(_metadata, 0, file1_s1d1);
7531
7532 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL);
7533
7534 test_execute(_metadata, EACCES, file1_s1d1);
7535 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute",
7536 file1_s1d1));
7537 test_check_exec(_metadata, EACCES, file1_s1d1);
7538 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute",
7539 file1_s1d1));
7540
7541 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7542 EXPECT_EQ(0, records.access);
7543 EXPECT_EQ(0, records.domain);
7544 }
7545
7546 /*
7547 * Using a set of handled/denied access rights make it possible to check that
7548 * only the blocked ones are logged.
7549 */
7550
TEST_F(audit_layout1,execute_read)7551 TEST_F(audit_layout1, execute_read)
7552 {
7553 struct audit_records records;
7554
7555 copy_file(_metadata, bin_true, file1_s1d1);
7556 test_execute(_metadata, 0, file1_s1d1);
7557 test_check_exec(_metadata, 0, file1_s1d1);
7558
7559 enforce_fs(_metadata, ACCESS_ALL, NULL);
7560
7561 /*
7562 * The only difference with the previous audit_layout1.execute_read test is
7563 * the extra ",fs\\.read_file" blocked by the executable file.
7564 */
7565 test_execute(_metadata, EACCES, file1_s1d1);
7566 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7567 "fs\\.execute,fs\\.read_file", file1_s1d1));
7568 test_check_exec(_metadata, EACCES, file1_s1d1);
7569 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7570 "fs\\.execute,fs\\.read_file", file1_s1d1));
7571
7572 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7573 EXPECT_EQ(0, records.access);
7574 EXPECT_EQ(0, records.domain);
7575 }
7576
TEST_F(audit_layout1,write_file)7577 TEST_F(audit_layout1, write_file)
7578 {
7579 struct audit_records records;
7580
7581 enforce_fs(_metadata, ACCESS_ALL, NULL);
7582
7583 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
7584 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7585 "fs\\.write_file", file1_s1d1));
7586
7587 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7588 EXPECT_EQ(0, records.access);
7589 EXPECT_EQ(1, records.domain);
7590 }
7591
TEST_F(audit_layout1,read_file)7592 TEST_F(audit_layout1, read_file)
7593 {
7594 struct audit_records records;
7595
7596 enforce_fs(_metadata, ACCESS_ALL, NULL);
7597
7598 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
7599 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file",
7600 file1_s1d1));
7601
7602 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7603 EXPECT_EQ(0, records.access);
7604 EXPECT_EQ(1, records.domain);
7605 }
7606
TEST_F(audit_layout1,read_dir)7607 TEST_F(audit_layout1, read_dir)
7608 {
7609 struct audit_records records;
7610
7611 enforce_fs(_metadata, ACCESS_ALL, NULL);
7612
7613 EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY));
7614 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir",
7615 dir_s1d1));
7616
7617 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7618 EXPECT_EQ(0, records.access);
7619 EXPECT_EQ(1, records.domain);
7620 }
7621
TEST_F(audit_layout1,remove_dir)7622 TEST_F(audit_layout1, remove_dir)
7623 {
7624 struct audit_records records;
7625
7626 EXPECT_EQ(0, unlink(file1_s1d3));
7627 EXPECT_EQ(0, unlink(file2_s1d3));
7628
7629 enforce_fs(_metadata, ACCESS_ALL, NULL);
7630
7631 EXPECT_EQ(-1, rmdir(dir_s1d3));
7632 EXPECT_EQ(EACCES, errno);
7633 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7634 "fs\\.remove_dir", dir_s1d2));
7635
7636 EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
7637 EXPECT_EQ(EACCES, errno);
7638 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7639 "fs\\.remove_dir", dir_s1d2));
7640
7641 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7642 EXPECT_EQ(0, records.access);
7643 EXPECT_EQ(0, records.domain);
7644 }
7645
TEST_F(audit_layout1,remove_file)7646 TEST_F(audit_layout1, remove_file)
7647 {
7648 struct audit_records records;
7649
7650 enforce_fs(_metadata, ACCESS_ALL, NULL);
7651
7652 EXPECT_EQ(-1, unlink(file1_s1d3));
7653 EXPECT_EQ(EACCES, errno);
7654 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7655 "fs\\.remove_file", dir_s1d3));
7656
7657 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7658 EXPECT_EQ(0, records.access);
7659 EXPECT_EQ(1, records.domain);
7660 }
7661
TEST_F(audit_layout1,make_char)7662 TEST_F(audit_layout1, make_char)
7663 {
7664 struct audit_records records;
7665
7666 EXPECT_EQ(0, unlink(file1_s1d3));
7667
7668 enforce_fs(_metadata, ACCESS_ALL, NULL);
7669
7670 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, makedev(7, 0)));
7671 EXPECT_EQ(EACCES, errno);
7672 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char",
7673 dir_s1d3));
7674
7675 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7676 EXPECT_EQ(0, records.access);
7677 EXPECT_EQ(1, records.domain);
7678 }
7679
TEST_F(audit_layout1,make_whiteout)7680 TEST_F(audit_layout1, make_whiteout)
7681 {
7682 struct audit_records records;
7683
7684 EXPECT_EQ(0, unlink(file1_s1d3));
7685
7686 enforce_fs(_metadata, ACCESS_ALL, NULL);
7687
7688 /* Whiteout creation is denied and logged as fs.make_reg. */
7689 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFCHR | 0644, makedev(0, 0)));
7690 EXPECT_EQ(EACCES, errno);
7691 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg",
7692 dir_s1d3));
7693
7694 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7695 EXPECT_EQ(0, records.access);
7696 EXPECT_EQ(1, records.domain);
7697 }
7698
TEST_F(audit_layout1,make_dir)7699 TEST_F(audit_layout1, make_dir)
7700 {
7701 struct audit_records records;
7702
7703 EXPECT_EQ(0, unlink(file1_s1d3));
7704
7705 enforce_fs(_metadata, ACCESS_ALL, NULL);
7706
7707 EXPECT_EQ(-1, mkdir(file1_s1d3, 0755));
7708 EXPECT_EQ(EACCES, errno);
7709 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir",
7710 dir_s1d3));
7711
7712 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7713 EXPECT_EQ(0, records.access);
7714 EXPECT_EQ(1, records.domain);
7715 }
7716
TEST_F(audit_layout1,make_reg)7717 TEST_F(audit_layout1, make_reg)
7718 {
7719 struct audit_records records;
7720
7721 EXPECT_EQ(0, unlink(file1_s1d3));
7722
7723 enforce_fs(_metadata, ACCESS_ALL, NULL);
7724
7725 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFREG | 0644, 0));
7726 EXPECT_EQ(EACCES, errno);
7727 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg",
7728 dir_s1d3));
7729
7730 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7731 EXPECT_EQ(0, records.access);
7732 EXPECT_EQ(1, records.domain);
7733 }
7734
TEST_F(audit_layout1,make_sock)7735 TEST_F(audit_layout1, make_sock)
7736 {
7737 struct audit_records records;
7738
7739 EXPECT_EQ(0, unlink(file1_s1d3));
7740
7741 enforce_fs(_metadata, ACCESS_ALL, NULL);
7742
7743 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFSOCK | 0644, 0));
7744 EXPECT_EQ(EACCES, errno);
7745 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock",
7746 dir_s1d3));
7747
7748 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7749 EXPECT_EQ(0, records.access);
7750 EXPECT_EQ(1, records.domain);
7751 }
7752
TEST_F(audit_layout1,make_fifo)7753 TEST_F(audit_layout1, make_fifo)
7754 {
7755 struct audit_records records;
7756
7757 EXPECT_EQ(0, unlink(file1_s1d3));
7758
7759 enforce_fs(_metadata, ACCESS_ALL, NULL);
7760
7761 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFIFO | 0644, 0));
7762 EXPECT_EQ(EACCES, errno);
7763 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo",
7764 dir_s1d3));
7765
7766 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7767 EXPECT_EQ(0, records.access);
7768 EXPECT_EQ(1, records.domain);
7769 }
7770
TEST_F(audit_layout1,make_block)7771 TEST_F(audit_layout1, make_block)
7772 {
7773 struct audit_records records;
7774
7775 EXPECT_EQ(0, unlink(file1_s1d3));
7776
7777 enforce_fs(_metadata, ACCESS_ALL, NULL);
7778
7779 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0));
7780 EXPECT_EQ(EACCES, errno);
7781 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7782 "fs\\.make_block", dir_s1d3));
7783
7784 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7785 EXPECT_EQ(0, records.access);
7786 EXPECT_EQ(1, records.domain);
7787 }
7788
TEST_F(audit_layout1,make_sym)7789 TEST_F(audit_layout1, make_sym)
7790 {
7791 struct audit_records records;
7792
7793 EXPECT_EQ(0, unlink(file1_s1d3));
7794
7795 enforce_fs(_metadata, ACCESS_ALL, NULL);
7796
7797 EXPECT_EQ(-1, symlink("target", file1_s1d3));
7798 EXPECT_EQ(EACCES, errno);
7799 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym",
7800 dir_s1d3));
7801
7802 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7803 EXPECT_EQ(0, records.access);
7804 EXPECT_EQ(1, records.domain);
7805 }
7806
TEST_F(audit_layout1,refer_handled)7807 TEST_F(audit_layout1, refer_handled)
7808 {
7809 struct audit_records records;
7810
7811 EXPECT_EQ(0, unlink(file1_s1d3));
7812
7813 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, NULL);
7814
7815 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3));
7816 EXPECT_EQ(EXDEV, errno);
7817 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
7818 dir_s1d1));
7819 EXPECT_EQ(0,
7820 matches_log_domain_allocated(self->audit_fd, getpid(), NULL));
7821 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
7822 dir_s1d3));
7823
7824 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7825 EXPECT_EQ(0, records.access);
7826 EXPECT_EQ(0, records.domain);
7827 }
7828
TEST_F(audit_layout1,refer_make)7829 TEST_F(audit_layout1, refer_make)
7830 {
7831 struct audit_records records;
7832
7833 EXPECT_EQ(0, unlink(file1_s1d3));
7834
7835 enforce_fs(_metadata,
7836 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER,
7837 NULL);
7838
7839 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3));
7840 EXPECT_EQ(EACCES, errno);
7841 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
7842 dir_s1d1));
7843 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7844 "fs\\.make_reg,fs\\.refer", dir_s1d3));
7845
7846 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7847 EXPECT_EQ(0, records.access);
7848 EXPECT_EQ(0, records.domain);
7849 }
7850
TEST_F(audit_layout1,refer_rename)7851 TEST_F(audit_layout1, refer_rename)
7852 {
7853 struct audit_records records;
7854
7855 EXPECT_EQ(0, unlink(file1_s1d3));
7856
7857 enforce_fs(_metadata, ACCESS_ALL, NULL);
7858
7859 EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3));
7860 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7861 "fs\\.remove_file,fs\\.refer", dir_s1d2));
7862 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7863 "fs\\.remove_file,fs\\.make_reg,fs\\.refer",
7864 dir_s2d3));
7865
7866 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7867 EXPECT_EQ(0, records.access);
7868 EXPECT_EQ(0, records.domain);
7869 }
7870
TEST_F(audit_layout1,refer_exchange)7871 TEST_F(audit_layout1, refer_exchange)
7872 {
7873 struct audit_records records;
7874
7875 EXPECT_EQ(0, unlink(file1_s1d3));
7876
7877 enforce_fs(_metadata, ACCESS_ALL, NULL);
7878
7879 /*
7880 * The only difference with the previous audit_layout1.refer_rename test is
7881 * the extra ",fs\\.make_reg" blocked by the source directory.
7882 */
7883 EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3));
7884 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7885 "fs\\.remove_file,fs\\.make_reg,fs\\.refer",
7886 dir_s1d2));
7887 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
7888 "fs\\.remove_file,fs\\.make_reg,fs\\.refer",
7889 dir_s2d3));
7890
7891 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7892 EXPECT_EQ(0, records.access);
7893 EXPECT_EQ(0, records.domain);
7894 }
7895
7896 /*
7897 * This test checks that the audit record is correctly generated when the
7898 * operation is only partially denied. This is the case for rename(2) when the
7899 * source file is allowed to be referenced but the destination directory is not.
7900 *
7901 * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize
7902 * file path walks and prepare for audit support") and commit 058518c20920
7903 * ("landlock: Align partial refer access checks with final ones").
7904 */
TEST_F(audit_layout1,refer_rename_half)7905 TEST_F(audit_layout1, refer_rename_half)
7906 {
7907 struct audit_records records;
7908 const struct rule layer1[] = {
7909 {
7910 .path = dir_s2d2,
7911 .access = LANDLOCK_ACCESS_FS_REFER,
7912 },
7913 {},
7914 };
7915
7916 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
7917
7918 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
7919 ASSERT_EQ(EXDEV, errno);
7920
7921 /* Only half of the request is denied. */
7922 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
7923 dir_s1d1));
7924
7925 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7926 EXPECT_EQ(0, records.access);
7927 EXPECT_EQ(1, records.domain);
7928 }
7929
TEST_F(audit_layout1,truncate)7930 TEST_F(audit_layout1, truncate)
7931 {
7932 struct audit_records records;
7933
7934 enforce_fs(_metadata, ACCESS_ALL, NULL);
7935
7936 EXPECT_EQ(-1, truncate(file1_s1d3, 0));
7937 EXPECT_EQ(EACCES, errno);
7938 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate",
7939 file1_s1d3));
7940
7941 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7942 EXPECT_EQ(0, records.access);
7943 EXPECT_EQ(1, records.domain);
7944 }
7945
TEST_F(audit_layout1,ioctl_dev)7946 TEST_F(audit_layout1, ioctl_dev)
7947 {
7948 struct audit_records records;
7949 int fd;
7950
7951 enforce_fs(_metadata, ACCESS_ALL & ~LANDLOCK_ACCESS_FS_READ_FILE, NULL);
7952
7953 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
7954 ASSERT_LE(0, fd);
7955 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));
7956 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd,
7957 "fs\\.ioctl_dev", "/dev/null",
7958 " ioctlcmd=0x541b"));
7959
7960 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7961 EXPECT_EQ(0, records.access);
7962 EXPECT_EQ(1, records.domain);
7963 }
7964
TEST_F(audit_layout1,resolve_unix)7965 TEST_F(audit_layout1, resolve_unix)
7966 {
7967 struct audit_records records;
7968 const char *const path = "sock";
7969 int srv_fd, cli_fd, status;
7970 pid_t child_pid;
7971
7972 srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, path);
7973
7974 child_pid = fork();
7975 ASSERT_LE(0, child_pid);
7976 if (!child_pid) {
7977 enforce_fs(_metadata, ACCESS_ALL, NULL);
7978
7979 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
7980 ASSERT_LE(0, cli_fd);
7981 EXPECT_EQ(EACCES,
7982 test_connect_named_unix(_metadata, cli_fd, path));
7983
7984 EXPECT_EQ(0, close(cli_fd));
7985 _exit(_metadata->exit_code);
7986 }
7987
7988 ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
7989 EXPECT_EQ(1, WIFEXITED(status));
7990 EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
7991
7992 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd,
7993 "fs\\.resolve_unix", path, NULL));
7994
7995 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
7996 EXPECT_EQ(0, records.access);
7997 EXPECT_EQ(1, records.domain);
7998
7999 EXPECT_EQ(0, close(srv_fd));
8000 }
8001
TEST_F(audit_layout1,mount)8002 TEST_F(audit_layout1, mount)
8003 {
8004 struct audit_records records;
8005
8006 enforce_fs(_metadata, LANDLOCK_ACCESS_FS_EXECUTE, NULL);
8007
8008 set_cap(_metadata, CAP_SYS_ADMIN);
8009 EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
8010 EXPECT_EQ(EPERM, errno);
8011 clear_cap(_metadata, CAP_SYS_ADMIN);
8012 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
8013 "fs\\.change_topology", dir_s3d2));
8014 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
8015 EXPECT_EQ(0, records.access);
8016 EXPECT_EQ(1, records.domain);
8017 }
8018
8019 static bool debug_quiet_tests;
8020
FIXTURE(audit_quiet_layout1)8021 FIXTURE(audit_quiet_layout1)
8022 {
8023 struct audit_filter audit_filter;
8024 int audit_fd;
8025 };
8026
FIXTURE_SETUP(audit_quiet_layout1)8027 FIXTURE_SETUP(audit_quiet_layout1)
8028 {
8029 prepare_layout(_metadata);
8030 create_layout1(_metadata);
8031
8032 set_cap(_metadata, CAP_AUDIT_CONTROL);
8033 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
8034 EXPECT_LE(0, self->audit_fd);
8035 clear_cap(_metadata, CAP_AUDIT_CONTROL);
8036
8037 if (getenv("DEBUG_QUIET_TESTS"))
8038 debug_quiet_tests = true;
8039 }
8040
FIXTURE_TEARDOWN_PARENT(audit_quiet_layout1)8041 FIXTURE_TEARDOWN_PARENT(audit_quiet_layout1)
8042 {
8043 remove_layout1(_metadata);
8044 cleanup_layout(_metadata);
8045
8046 set_cap(_metadata, CAP_AUDIT_CONTROL);
8047 EXPECT_EQ(0, audit_cleanup(-1, NULL));
8048 clear_cap(_metadata, CAP_AUDIT_CONTROL);
8049 }
8050
8051 struct a_rule {
8052 const char *path;
8053 __u64 access;
8054 bool quiet;
8055 };
8056
8057 struct a_layer {
8058 __u64 handled_access_fs;
8059 __u64 quiet_access_fs;
8060 struct a_rule rules[6];
8061 __u64 restrict_flags;
8062 };
8063
8064 struct a_target {
8065 /* File/dir to try open. */
8066 const char *target;
8067 /* Open mode (one of O_RDONLY, O_WRONLY, or O_RDWR). */
8068 int open_mode;
8069 /* Should open succeed? */
8070 bool expect_open_success;
8071 /* If open fails, whether to expect an audit log for read. */
8072 bool audit_read_blocked;
8073 /* If open fails, whether to expect an audit log for write. */
8074 bool audit_write_blocked;
8075 /* If ftruncate() is expected to be allowed. */
8076 bool expect_truncate_success;
8077 /* If ftruncate fails, whether to expect an audit log. */
8078 bool audit_truncate;
8079 /*
8080 * If ioctl() is expected to be allowed (ioctl not attempted if neither
8081 * this nor expect_ioctl_denied is set).
8082 */
8083 bool expect_ioctl_allowed;
8084 /* If ioctl() is expected to be denied. */
8085 bool expect_ioctl_denied;
8086 /* If ioctl fails, whether to expect an audit log. */
8087 bool audit_ioctl;
8088 };
8089
8090 #define AUDIT_QUIET_MAX_TARGETS 10
8091
FIXTURE_VARIANT(audit_quiet_layout1)8092 FIXTURE_VARIANT(audit_quiet_layout1)
8093 {
8094 struct a_layer layers[3];
8095 struct a_target targets[AUDIT_QUIET_MAX_TARGETS];
8096 };
8097
8098 #define FS_R LANDLOCK_ACCESS_FS_READ_FILE
8099 #define FS_W LANDLOCK_ACCESS_FS_WRITE_FILE
8100 #define FS_TRUNC LANDLOCK_ACCESS_FS_TRUNCATE
8101 #define FS_IOCTL LANDLOCK_ACCESS_FS_IOCTL_DEV
8102
sprint_access_bits(char * buf,size_t buflen,__u64 access)8103 static int sprint_access_bits(char *buf, size_t buflen, __u64 access)
8104 {
8105 size_t offset = 0;
8106
8107 if (buflen < strlen("rwti make_reg remove_file refer") + 1)
8108 abort();
8109
8110 buf[0] = '\0';
8111 if (access & FS_R)
8112 offset += snprintf(buf + offset, buflen - offset, "r");
8113 if (access & FS_W)
8114 offset += snprintf(buf + offset, buflen - offset, "w");
8115 if (access & FS_TRUNC)
8116 offset += snprintf(buf + offset, buflen - offset, "t");
8117 if (access & FS_IOCTL)
8118 offset += snprintf(buf + offset, buflen - offset, "i");
8119 if (access & LANDLOCK_ACCESS_FS_MAKE_REG)
8120 offset += snprintf(buf + offset, buflen - offset, ",make_reg");
8121 if (access & LANDLOCK_ACCESS_FS_REMOVE_FILE)
8122 offset +=
8123 snprintf(buf + offset, buflen - offset, ",remove_file");
8124 if (access & LANDLOCK_ACCESS_FS_REFER)
8125 offset += snprintf(buf + offset, buflen - offset, ",refer");
8126
8127 if (buf[0] == ',') {
8128 offset--;
8129 memmove(buf, buf + 1, offset);
8130 buf[offset] = '\0';
8131 }
8132
8133 return offset;
8134 }
8135
apply_a_layer(struct __test_metadata * const _metadata,const struct a_layer * l)8136 static int apply_a_layer(struct __test_metadata *const _metadata,
8137 const struct a_layer *l)
8138 {
8139 struct landlock_ruleset_attr rs_attr = {
8140 .handled_access_fs = l->handled_access_fs,
8141 .quiet_access_fs = l->quiet_access_fs,
8142 };
8143 int rs_fd;
8144 int i;
8145 const struct a_rule *r;
8146 char handled_access_s[33], quiet_access_s[33], rule_access_s[33];
8147
8148 if (!l->handled_access_fs)
8149 return 0;
8150
8151 rs_fd = landlock_create_ruleset(&rs_attr, sizeof(rs_attr), 0);
8152 ASSERT_LE(0, rs_fd);
8153
8154 for (i = 0; i < ARRAY_SIZE(l->rules); i++) {
8155 r = &l->rules[i];
8156 if (!r->path)
8157 continue;
8158
8159 add_path_beneath(_metadata, rs_fd, r->access, r->path,
8160 r->quiet ? LANDLOCK_ADD_RULE_QUIET : 0);
8161 }
8162
8163 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
8164 ASSERT_EQ(0, landlock_restrict_self(rs_fd, l->restrict_flags))
8165 {
8166 TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
8167 }
8168 ASSERT_EQ(0, close(rs_fd));
8169
8170 if (debug_quiet_tests) {
8171 sprint_access_bits(handled_access_s, sizeof(handled_access_s),
8172 l->handled_access_fs);
8173 sprint_access_bits(quiet_access_s, sizeof(quiet_access_s),
8174 l->quiet_access_fs);
8175 TH_LOG("applied layer: handled=%s quiet=%s restrict_flags=0x%llx",
8176 handled_access_s, quiet_access_s,
8177 (unsigned long long)l->restrict_flags);
8178 for (i = 0; i < ARRAY_SIZE(l->rules); i++) {
8179 r = &l->rules[i];
8180 if (!r->path)
8181 continue;
8182
8183 sprint_access_bits(rule_access_s, sizeof(rule_access_s),
8184 r->access);
8185 TH_LOG(" rule[%d]: path=%s access=%s quiet=%d", i,
8186 r->path, rule_access_s, r->quiet);
8187 }
8188 }
8189 return 0;
8190 }
8191
audit_quiet_layout1_test_body(struct __test_metadata * const _metadata,FIXTURE_DATA (audit_quiet_layout1)* self,const struct a_target * targets)8192 void audit_quiet_layout1_test_body(struct __test_metadata *const _metadata,
8193 FIXTURE_DATA(audit_quiet_layout1) * self,
8194 const struct a_target *targets)
8195 {
8196 struct audit_records records = {};
8197 int i;
8198 const struct a_target *target;
8199 int fd = -1;
8200 int open_mode;
8201 int ret;
8202 bool expect_audit;
8203 const char *blocker;
8204
8205 for (i = 0; i < AUDIT_QUIET_MAX_TARGETS; i++) {
8206 target = &targets[i];
8207 if (!target->target)
8208 continue;
8209
8210 open_mode = target->open_mode & (O_RDONLY | O_WRONLY | O_RDWR);
8211
8212 EXPECT_TRUE(open_mode == O_RDONLY || open_mode == O_WRONLY ||
8213 open_mode == O_RDWR);
8214
8215 if (target->expect_open_success) {
8216 EXPECT_FALSE(target->audit_read_blocked);
8217 EXPECT_FALSE(target->audit_write_blocked);
8218 }
8219 if (target->expect_truncate_success)
8220 EXPECT_TRUE(target->expect_open_success &&
8221 !target->audit_truncate);
8222
8223 if (debug_quiet_tests)
8224 TH_LOG("Try open \"%s\" with %s%s", target->target,
8225 open_mode != O_WRONLY ? "r" : "",
8226 open_mode != O_RDONLY ? "w" : "");
8227
8228 fd = openat(AT_FDCWD, target->target, open_mode | O_CLOEXEC);
8229 if (target->expect_open_success) {
8230 ASSERT_LE(0, fd)
8231 {
8232 TH_LOG("Failed to open \"%s\": %s",
8233 target->target, strerror(errno));
8234 };
8235 } else {
8236 ASSERT_EQ(-1, fd);
8237 ASSERT_EQ(EACCES, errno);
8238 }
8239
8240 expect_audit = true;
8241
8242 if (target->audit_read_blocked && target->audit_write_blocked)
8243 blocker = "fs\\.write_file,fs\\.read_file";
8244 else if (target->audit_read_blocked)
8245 blocker = "fs\\.read_file";
8246 else if (target->audit_write_blocked)
8247 blocker = "fs\\.write_file";
8248 else
8249 expect_audit = false;
8250
8251 if (expect_audit)
8252 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
8253 blocker, target->target));
8254
8255 /* Check that we see no (other) logs. */
8256 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
8257 ASSERT_EQ(0, records.access);
8258
8259 if (target->expect_open_success && fd >= 0) {
8260 if (debug_quiet_tests)
8261 TH_LOG("Try ftruncate \"%s\"", target->target);
8262
8263 ret = ftruncate(fd, 0);
8264 if (target->expect_truncate_success) {
8265 ASSERT_EQ(0, ret);
8266 } else {
8267 ASSERT_EQ(-1, ret);
8268 if (open_mode != O_RDONLY)
8269 ASSERT_EQ(EACCES, errno);
8270 }
8271
8272 if (target->audit_truncate)
8273 ASSERT_EQ(0, matches_log_fs(_metadata,
8274 self->audit_fd,
8275 "fs\\.truncate",
8276 target->target));
8277
8278 if (target->expect_ioctl_allowed ||
8279 target->expect_ioctl_denied) {
8280 if (debug_quiet_tests)
8281 TH_LOG("Try ioctl FIONREAD on \"%s\"",
8282 target->target);
8283
8284 ret = ioctl_error(_metadata, fd, FIONREAD);
8285 if (target->expect_ioctl_allowed) {
8286 ASSERT_NE(EACCES, ret);
8287 } else {
8288 ASSERT_EQ(EACCES, ret);
8289 }
8290 }
8291
8292 if (target->audit_ioctl)
8293 ASSERT_EQ(0, matches_log_fs_extra(
8294 _metadata, self->audit_fd,
8295 "fs\\.ioctl_dev",
8296 target->target,
8297 " ioctlcmd=0x541b\\+"));
8298
8299 /* Check that we see no other logs. */
8300 EXPECT_EQ(0, audit_count_records(self->audit_fd,
8301 &records));
8302 ASSERT_EQ(0, records.access);
8303 ASSERT_EQ(0, close(fd));
8304 }
8305 }
8306 }
8307
TEST_F(audit_quiet_layout1,base)8308 TEST_F(audit_quiet_layout1, base)
8309 {
8310 int i;
8311
8312 for (i = 0; i < ARRAY_SIZE(variant->layers); i++)
8313 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i]));
8314
8315 audit_quiet_layout1_test_body(_metadata, self, variant->targets);
8316 }
8317
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_simple)8318 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_simple) {
8319 .layers = {
8320 {
8321 .handled_access_fs = FS_R | FS_W | FS_TRUNC,
8322 .quiet_access_fs = FS_R,
8323 .rules = {
8324 { .path = dir_s1d1, .access = 0, .quiet = true },
8325 },
8326 },
8327 },
8328 .targets = {
8329 {
8330 .target = file1_s1d1,
8331 .open_mode = O_RDONLY,
8332 },
8333 /* Not covered by quiet */
8334 {
8335 .target = file1_s2d1,
8336 .open_mode = O_RDONLY,
8337 .audit_read_blocked = true,
8338 },
8339 /* Access not quieted */
8340 {
8341 .target = file1_s1d1,
8342 .open_mode = O_WRONLY,
8343 .audit_write_blocked = true,
8344 },
8345 /*
8346 * Quiet flag only takes effect if all blocked access bits are
8347 * quieted, otherwise audit log emitted as normal (with all
8348 * blockers)
8349 */
8350 {
8351 .target = file1_s1d1,
8352 .open_mode = O_RDWR,
8353 .audit_read_blocked = true,
8354 .audit_write_blocked = true,
8355 },
8356 },
8357 };
8358
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_allow_read)8359 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_read) {
8360 .layers = {
8361 {
8362 .handled_access_fs = FS_R | FS_W | FS_TRUNC,
8363 .quiet_access_fs = FS_W,
8364 .rules = {
8365 { .path = dir_s1d1, .access = FS_R, .quiet = true },
8366 /* Quiet flags inherit down and are not overridden */
8367 { .path = file1_s1d1, .access = FS_R, .quiet = false },
8368 { .path = file1_s2d3, .access = 0, .quiet = true },
8369 },
8370 },
8371 },
8372 .targets = {
8373 /* Read ok */
8374 {
8375 .target = file1_s1d1,
8376 .open_mode = O_RDONLY,
8377 .expect_open_success = true,
8378 },
8379 /* Write quieted */
8380 {
8381 .target = file1_s1d1,
8382 .open_mode = O_WRONLY,
8383 },
8384 /* Read allowed, write quieted so no audit */
8385 {
8386 .target = file1_s1d1,
8387 .open_mode = O_RDWR,
8388 },
8389 /* Not covered by quiet */
8390 {
8391 .target = file1_s2d2,
8392 .open_mode = O_WRONLY,
8393 .audit_write_blocked = true,
8394 },
8395 {
8396 .target = file1_s2d2,
8397 .open_mode = O_RDWR,
8398 .audit_read_blocked = true,
8399 .audit_write_blocked = true,
8400 },
8401 /* Single file quiet */
8402 {
8403 .target = file1_s2d3,
8404 .open_mode = O_WRONLY,
8405 },
8406 /* Wrong file */
8407 {
8408 .target = file2_s2d3,
8409 .open_mode = O_WRONLY,
8410 .audit_write_blocked = true,
8411 },
8412 /* Access not quieted */
8413 {
8414 .target = file1_s2d3,
8415 .open_mode = O_RDONLY,
8416 .audit_read_blocked = true,
8417 },
8418 /* Some access not quieted */
8419 {
8420 .target = file1_s2d3,
8421 .open_mode = O_RDWR,
8422 .audit_read_blocked = true,
8423 .audit_write_blocked = true,
8424 },
8425 },
8426 };
8427
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_allow_write)8428 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_allow_write) {
8429 .layers = {
8430 {
8431 .handled_access_fs = FS_R | FS_W | FS_TRUNC,
8432 .quiet_access_fs = FS_R,
8433 .rules = {
8434 { .path = dir_s1d1, .access = FS_W, .quiet = true },
8435 },
8436 },
8437 },
8438 .targets = {
8439 /* Read quieted */
8440 {
8441 .target = file1_s1d1,
8442 .open_mode = O_RDONLY,
8443 },
8444 /* Truncate not quieted */
8445 {
8446 .target = file1_s1d1,
8447 .open_mode = O_WRONLY,
8448 .expect_open_success = true,
8449 .audit_truncate = true,
8450 },
8451 /* Not covered by quiet */
8452 {
8453 .target = file1_s2d1,
8454 .open_mode = O_RDONLY,
8455 .audit_read_blocked = true,
8456 },
8457 /* Write allowed, read quieted so no audit */
8458 {
8459 .target = file1_s1d1,
8460 .open_mode = O_RDWR,
8461 },
8462 },
8463 };
8464
FIXTURE_VARIANT_ADD(audit_quiet_layout1,allow_write_quiet_trunc)8465 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_write_quiet_trunc) {
8466 .layers = {
8467 {
8468 .handled_access_fs = FS_R | FS_W | FS_TRUNC,
8469 .quiet_access_fs = FS_TRUNC,
8470 .rules = {
8471 { .path = dir_s1d1, .access = FS_W, .quiet = true },
8472 { .path = dir_s2d1, .access = FS_W, .quiet = false },
8473 },
8474 },
8475 },
8476 .targets = {
8477 /* Read not allowed and not quieted */
8478 {
8479 .target = file1_s1d1,
8480 .open_mode = O_RDONLY,
8481 .audit_read_blocked = true,
8482 },
8483 /* Truncate quieted */
8484 {
8485 .target = file1_s1d1,
8486 .open_mode = O_WRONLY,
8487 .expect_open_success = true,
8488 },
8489 /* Not covered by quiet (truncate) */
8490 {
8491 .target = file1_s2d1,
8492 .open_mode = O_WRONLY,
8493 .expect_open_success = true,
8494 .audit_truncate = true,
8495 },
8496 /* Not covered by quiet (read/write) */
8497 {
8498 .target = file1_s3d1,
8499 .open_mode = O_RDWR,
8500 .audit_read_blocked = true,
8501 .audit_write_blocked = true,
8502 },
8503 },
8504 };
8505
FIXTURE_VARIANT_ADD(audit_quiet_layout1,allow_rw_quiet_trunc)8506 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_rw_quiet_trunc) {
8507 .layers = {
8508 {
8509 .handled_access_fs = FS_R | FS_W | FS_TRUNC,
8510 .quiet_access_fs = FS_TRUNC,
8511 .rules = {
8512 { .path = dir_s1d1, .access = FS_R | FS_W, .quiet = true },
8513 { .path = dir_s2d1, .access = FS_R | FS_W, .quiet = false },
8514 },
8515 },
8516 },
8517 .targets = {
8518 {
8519 .target = file1_s1d1,
8520 .open_mode = O_RDWR,
8521 .expect_open_success = true,
8522 },
8523 {
8524 .target = file1_s2d1,
8525 .open_mode = O_RDWR,
8526 .expect_open_success = true,
8527 .audit_truncate = true,
8528 },
8529 },
8530 };
8531
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_all)8532 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_all) {
8533 .layers = {
8534 {
8535 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8536 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8537 .rules = {
8538 { .path = dir_s1d1, .access = 0, .quiet = true },
8539 { .path = file1_s2d1, .access = FS_R | FS_W, .quiet = true },
8540 { .path = file1_s2d3, .access = 0, .quiet = true },
8541 { .path = dir_s3d1, .access = FS_W, .quiet = false },
8542 { .path = "/dev/zero", .access = FS_R, .quiet = false },
8543 { .path = "/dev/null", .access = FS_R, .quiet = true },
8544 },
8545 },
8546 },
8547 .targets = {
8548 /* No logs */
8549 {
8550 .target = file1_s1d1,
8551 .open_mode = O_RDONLY,
8552 },
8553 {
8554 .target = file1_s1d1,
8555 .open_mode = O_WRONLY,
8556 },
8557 {
8558 .target = file1_s1d1,
8559 .open_mode = O_RDWR,
8560 },
8561 /* Truncate quieted - no log */
8562 {
8563 .target = file1_s2d1,
8564 .open_mode = O_RDWR,
8565 .expect_open_success = true,
8566 },
8567 /* Truncate not covered by quiet */
8568 {
8569 .target = file1_s3d1,
8570 .open_mode = O_WRONLY,
8571 .expect_open_success = true,
8572 .audit_truncate = true,
8573 },
8574 /* Not covered by quiet */
8575 {
8576 .target = file1_s3d1,
8577 .open_mode = O_RDONLY,
8578 .audit_read_blocked = true,
8579 },
8580 /* Single file quiet */
8581 {
8582 .target = file1_s2d3,
8583 .open_mode = O_RDWR,
8584 },
8585 /* Wrong file */
8586 {
8587 .target = file2_s2d3,
8588 .open_mode = O_RDWR,
8589 .audit_read_blocked = true,
8590 .audit_write_blocked = true,
8591 },
8592 /* Ioctl quieted */
8593 {
8594 .target = "/dev/null",
8595 .open_mode = O_RDONLY,
8596 .expect_open_success = true,
8597 .expect_ioctl_denied = true,
8598 },
8599 /* Ioctl not quieted */
8600 {
8601 .target = "/dev/zero",
8602 .open_mode = O_RDONLY,
8603 .expect_open_success = true,
8604 .expect_ioctl_denied = true,
8605 .audit_ioctl = true,
8606 },
8607 },
8608 };
8609
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_across_mountpoint)8610 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_across_mountpoint) {
8611 .layers = {
8612 {
8613 .handled_access_fs = FS_R | FS_W | FS_TRUNC,
8614 .quiet_access_fs = FS_R,
8615 .rules = {
8616 { .path = dir_s3d1, .access = 0, .quiet = true },
8617 },
8618 },
8619 },
8620 .targets = {
8621 {
8622 .target = file1_s3d3,
8623 .open_mode = O_RDONLY,
8624 },
8625 /* Not covered by quiet */
8626 {
8627 .target = file1_s1d1,
8628 .open_mode = O_RDONLY,
8629 .audit_read_blocked = true,
8630 },
8631 {
8632 .target = file1_s1d1,
8633 .open_mode = O_RDWR,
8634 .audit_read_blocked = true,
8635 .audit_write_blocked = true,
8636 },
8637 /* Access not quieted */
8638 {
8639 .target = file1_s3d3,
8640 .open_mode = O_WRONLY,
8641 .audit_write_blocked = true,
8642 },
8643 },
8644 };
8645
FIXTURE_VARIANT_ADD(audit_quiet_layout1,allow_all_quiet)8646 FIXTURE_VARIANT_ADD(audit_quiet_layout1, allow_all_quiet) {
8647 .layers = {
8648 {
8649 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8650 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8651 .rules = {
8652 {
8653 .path = dir_s1d1,
8654 .access = FS_R | FS_W | FS_TRUNC,
8655 .quiet = true
8656 },
8657 {
8658 .path = "/dev/null",
8659 .access = FS_R | FS_W | FS_IOCTL,
8660 .quiet = true
8661 },
8662 },
8663 },
8664 },
8665 .targets = {
8666 {
8667 .target = file1_s1d1,
8668 .open_mode = O_RDWR,
8669 .expect_open_success = true,
8670 .expect_truncate_success = true,
8671 },
8672 {
8673 .target = "/dev/null",
8674 .open_mode = O_RDONLY,
8675 .expect_open_success = true,
8676 .expect_ioctl_allowed = true,
8677 },
8678 },
8679 };
8680
8681 /*
8682 * With LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF, it doesn't matter what the
8683 * quiet flags below the layer say.
8684 */
FIXTURE_VARIANT_ADD(audit_quiet_layout1,subdomains_off)8685 FIXTURE_VARIANT_ADD(audit_quiet_layout1, subdomains_off) {
8686 .layers = {
8687 {
8688 .handled_access_fs = FS_R,
8689 .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
8690 .rules = {
8691 { .path = "/", .access = FS_R, .quiet = false },
8692 }
8693 },
8694 {
8695 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8696 .quiet_access_fs = FS_R,
8697 .rules = {
8698 { .path = dir_s1d1, .access = 0, .quiet = true },
8699 { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true },
8700 { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false },
8701 { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true },
8702 { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false },
8703 },
8704 },
8705 },
8706 .targets = {
8707 {
8708 .target = file1_s1d1,
8709 .open_mode = O_RDWR,
8710 },
8711 {
8712 .target = file1_s2d1,
8713 .open_mode = O_RDWR,
8714 },
8715 {
8716 .target = file1_s2d2,
8717 .open_mode = O_RDWR,
8718 .expect_open_success = true,
8719 /* No audit_truncate */
8720 },
8721 {
8722 .target = file1_s2d3,
8723 .open_mode = O_RDWR,
8724 .expect_open_success = true,
8725 /* No audit_truncate */
8726 },
8727 {
8728 .target = "/dev/null",
8729 .open_mode = O_RDONLY,
8730 .expect_open_success = true,
8731 .expect_ioctl_denied = true,
8732 /* No audit_ioctl */
8733 },
8734 {
8735 .target = "/dev/zero",
8736 .open_mode = O_RDONLY,
8737 .expect_open_success = true,
8738 .expect_ioctl_denied = true,
8739 /* No audit_ioctl */
8740 },
8741 },
8742 };
8743
8744 /*
8745 * With LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF, it doesn't matter what the
8746 * quiet flags on the layer say.
8747 */
FIXTURE_VARIANT_ADD(audit_quiet_layout1,same_exec_off)8748 FIXTURE_VARIANT_ADD(audit_quiet_layout1, same_exec_off) {
8749 .layers = {
8750 {
8751 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8752 .quiet_access_fs = FS_R,
8753 .restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
8754 .rules = {
8755 { .path = dir_s1d1, .access = 0, .quiet = true },
8756 { .path = file1_s2d2, .access = FS_R | FS_W, .quiet = true },
8757 { .path = file1_s2d3, .access = FS_R | FS_W, .quiet = false },
8758 { .path = "/dev/null", .access = FS_R | FS_W, .quiet = true },
8759 { .path = "/dev/zero", .access = FS_R | FS_W, .quiet = false },
8760 },
8761 },
8762 },
8763 .targets = {
8764 {
8765 .target = file1_s1d1,
8766 .open_mode = O_RDWR,
8767 },
8768 {
8769 .target = file1_s2d1,
8770 .open_mode = O_RDWR,
8771 },
8772 {
8773 .target = file1_s2d2,
8774 .open_mode = O_RDWR,
8775 .expect_open_success = true,
8776 /* No audit_truncate */
8777 },
8778 {
8779 .target = file1_s2d3,
8780 .open_mode = O_RDWR,
8781 .expect_open_success = true,
8782 /* No audit_truncate */
8783 },
8784 {
8785 .target = "/dev/null",
8786 .open_mode = O_RDONLY,
8787 .expect_open_success = true,
8788 .expect_ioctl_denied = true,
8789 /* No audit_ioctl */
8790 },
8791 {
8792 .target = "/dev/zero",
8793 .open_mode = O_RDONLY,
8794 .expect_open_success = true,
8795 .expect_ioctl_denied = true,
8796 /* No audit_ioctl */
8797 },
8798 },
8799 };
8800
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_two_layers_1)8801 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_1) {
8802 /* Here, rules that deny access are always quiet. */
8803 .layers = {
8804 {
8805 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8806 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8807 .rules = {
8808 {
8809 .path = dir_s1d1,
8810 .access = FS_W,
8811 .quiet = true,
8812 },
8813 {
8814 .path = dir_s2d1,
8815 .access = FS_R | FS_W | FS_TRUNC,
8816 .quiet = false,
8817 },
8818 {
8819 .path = "/dev/null",
8820 .access = FS_R,
8821 .quiet = true,
8822 },
8823 {
8824 .path = "/dev/zero",
8825 .access = FS_R | FS_W | FS_IOCTL,
8826 .quiet = false,
8827 },
8828 },
8829 },
8830 {
8831 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8832 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8833 .rules = {
8834 {
8835 .path = dir_s1d1,
8836 .access = FS_R | FS_W | FS_TRUNC,
8837 .quiet = false,
8838 },
8839 {
8840 .path = dir_s2d1,
8841 .access = FS_W,
8842 .quiet = true,
8843 },
8844 {
8845 .path = "/dev/null",
8846 .access = FS_R | FS_W | FS_IOCTL,
8847 .quiet = false,
8848 },
8849 {
8850 .path = "/dev/zero",
8851 .access = FS_R,
8852 .quiet = true,
8853 },
8854 },
8855 },
8856 },
8857 .targets = {
8858 {
8859 .target = file1_s1d1,
8860 .open_mode = O_RDONLY,
8861 },
8862 {
8863 .target = file1_s1d1,
8864 .open_mode = O_WRONLY,
8865 .expect_open_success = true,
8866 },
8867 {
8868 .target = file1_s2d1,
8869 .open_mode = O_RDONLY,
8870 },
8871 {
8872 .target = file1_s2d1,
8873 .open_mode = O_WRONLY,
8874 .expect_open_success = true,
8875 },
8876 {
8877 .target = "/dev/null",
8878 .open_mode = O_RDONLY,
8879 .expect_open_success = true,
8880 .expect_ioctl_denied = true,
8881 },
8882 {
8883 .target = "/dev/zero",
8884 .open_mode = O_RDONLY,
8885 .expect_open_success = true,
8886 .expect_ioctl_denied = true,
8887 },
8888 },
8889 };
8890
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_two_layers_2)8891 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_2) {
8892 /* Here, rules that deny access are never quiet. */
8893 .layers = {
8894 {
8895 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8896 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8897 .rules = {
8898 {
8899 .path = dir_s1d1,
8900 .access = FS_W,
8901 .quiet = false
8902 },
8903 {
8904 .path = dir_s2d1,
8905 .access = FS_R | FS_W | FS_TRUNC,
8906 .quiet = true
8907 },
8908 {
8909 .path = "/dev/null",
8910 .access = FS_R,
8911 .quiet = false
8912 },
8913 {
8914 .path = "/dev/zero",
8915 .access = FS_R | FS_W | FS_IOCTL,
8916 .quiet = true
8917 },
8918 },
8919 },
8920 {
8921 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8922 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8923 .rules = {
8924 {
8925 .path = dir_s1d1,
8926 .access = FS_R | FS_W | FS_TRUNC,
8927 .quiet = true
8928 },
8929 {
8930 .path = dir_s2d1,
8931 .access = FS_W,
8932 .quiet = false
8933 },
8934 {
8935 .path = "/dev/null",
8936 .access = FS_R | FS_W | FS_IOCTL,
8937 .quiet = true
8938 },
8939 {
8940 .path = "/dev/zero",
8941 .access = FS_R,
8942 .quiet = false
8943 },
8944 },
8945 },
8946 },
8947 .targets = {
8948 {
8949 .target = file1_s1d1,
8950 .open_mode = O_RDONLY,
8951 .audit_read_blocked = true,
8952 },
8953 {
8954 .target = file1_s1d1,
8955 .open_mode = O_WRONLY,
8956 .expect_open_success = true,
8957 .audit_truncate = true,
8958 },
8959 {
8960 .target = file1_s2d1,
8961 .open_mode = O_RDONLY,
8962 .audit_read_blocked = true,
8963 },
8964 {
8965 .target = file1_s2d1,
8966 .open_mode = O_WRONLY,
8967 .expect_open_success = true,
8968 .audit_truncate = true,
8969 },
8970 {
8971 .target = "/dev/null",
8972 .open_mode = O_RDONLY,
8973 .expect_open_success = true,
8974 .expect_ioctl_denied = true,
8975 .audit_ioctl = true,
8976 },
8977 {
8978 .target = "/dev/zero",
8979 .open_mode = O_RDONLY,
8980 .expect_open_success = true,
8981 .expect_ioctl_denied = true,
8982 .audit_ioctl = true,
8983 },
8984 },
8985 };
8986
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_two_layers_3)8987 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_3) {
8988 /* This time only the second layer quiets things. */
8989 .layers = {
8990 {
8991 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8992 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
8993 .rules = {
8994 {
8995 .path = dir_s1d1,
8996 .access = FS_W,
8997 .quiet = false,
8998 },
8999 {
9000 .path = dir_s2d1,
9001 .access = FS_R | FS_W | FS_TRUNC,
9002 .quiet = false,
9003 },
9004 {
9005 .path = "/dev/null",
9006 .access = FS_R,
9007 .quiet = false,
9008 },
9009 {
9010 .path = "/dev/zero",
9011 .access = FS_R | FS_W | FS_IOCTL,
9012 .quiet = false,
9013 },
9014 },
9015 },
9016 {
9017 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9018 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9019 .rules = {
9020 {
9021 .path = dir_s1d1,
9022 .access = FS_R | FS_W | FS_TRUNC,
9023 .quiet = false,
9024 },
9025 {
9026 .path = dir_s2d1,
9027 .access = FS_W,
9028 .quiet = true,
9029 },
9030 {
9031 .path = "/dev/null",
9032 .access = FS_R | FS_W | FS_IOCTL,
9033 .quiet = false,
9034 },
9035 {
9036 .path = "/dev/zero",
9037 .access = FS_R,
9038 .quiet = true,
9039 },
9040 },
9041 },
9042 },
9043 .targets = {
9044 {
9045 .target = file1_s1d1,
9046 .open_mode = O_RDONLY,
9047 .audit_read_blocked = true,
9048 },
9049 {
9050 .target = file1_s1d1,
9051 .open_mode = O_WRONLY,
9052 .expect_open_success = true,
9053 .audit_truncate = true,
9054 },
9055 {
9056 .target = file1_s2d1,
9057 .open_mode = O_RDONLY,
9058 },
9059 {
9060 .target = file1_s2d1,
9061 .open_mode = O_WRONLY,
9062 .expect_open_success = true,
9063 },
9064 {
9065 .target = "/dev/null",
9066 .open_mode = O_RDONLY,
9067 .expect_open_success = true,
9068 .expect_ioctl_denied = true,
9069 .audit_ioctl = true,
9070 },
9071 {
9072 .target = "/dev/zero",
9073 .open_mode = O_RDONLY,
9074 .expect_open_success = true,
9075 .expect_ioctl_denied = true,
9076 },
9077 },
9078 };
9079
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_two_layers_different_quiet_access)9080 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_quiet_access) {
9081 /* Here, rules that deny access are always quiet. */
9082 .layers = {
9083 {
9084 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9085 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9086 .rules = {
9087 {
9088 .path = dir_s1d1,
9089 .access = FS_W,
9090 .quiet = true,
9091 },
9092 {
9093 .path = dir_s2d1,
9094 .access = FS_R | FS_W | FS_TRUNC,
9095 .quiet = false,
9096 },
9097 {
9098 .path = "/dev/null",
9099 .access = FS_R,
9100 .quiet = true,
9101 },
9102 {
9103 .path = "/dev/zero",
9104 .access = FS_R | FS_W | FS_IOCTL,
9105 .quiet = false,
9106 },
9107 },
9108 },
9109 {
9110 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9111 .quiet_access_fs = FS_IOCTL,
9112 .rules = {
9113 {
9114 .path = dir_s1d1,
9115 .access = FS_R | FS_W | FS_TRUNC,
9116 .quiet = false,
9117 },
9118 {
9119 .path = dir_s2d1,
9120 .access = FS_W,
9121 .quiet = true,
9122 },
9123 {
9124 .path = "/dev/null",
9125 .access = FS_R | FS_W | FS_IOCTL,
9126 .quiet = false,
9127 },
9128 {
9129 .path = "/dev/zero",
9130 .access = FS_R,
9131 .quiet = true,
9132 },
9133 },
9134 },
9135 },
9136 .targets = {
9137 {
9138 .target = file1_s1d1,
9139 .open_mode = O_RDONLY,
9140 },
9141 {
9142 .target = file1_s1d1,
9143 .open_mode = O_WRONLY,
9144 .expect_open_success = true,
9145 },
9146 {
9147 .target = file1_s2d1,
9148 .open_mode = O_RDONLY,
9149 .audit_read_blocked = true,
9150 },
9151 {
9152 .target = file1_s2d1,
9153 .open_mode = O_WRONLY,
9154 .expect_open_success = true,
9155 .audit_truncate = true,
9156 },
9157 {
9158 .target = "/dev/null",
9159 .open_mode = O_RDONLY,
9160 .expect_open_success = true,
9161 .expect_ioctl_denied = true,
9162 },
9163 {
9164 .target = "/dev/zero",
9165 .open_mode = O_RDONLY,
9166 .expect_open_success = true,
9167 .expect_ioctl_denied = true,
9168 },
9169 },
9170 };
9171
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_two_layers_different_handled_1)9172 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_1) {
9173 /* Quiet from layer 1 */
9174 .layers = {
9175 {
9176 .handled_access_fs = FS_R,
9177 .quiet_access_fs = FS_R,
9178 .rules = {
9179 {
9180 .path = file1_s1d1,
9181 .access = FS_R,
9182 .quiet = true,
9183 },
9184 {
9185 .path = file2_s1d1,
9186 .access = 0,
9187 .quiet = true,
9188 },
9189 {
9190 .path = file1_s1d2,
9191 .access = 0,
9192 .quiet = true,
9193 },
9194 {
9195 .path = file2_s1d2,
9196 .access = FS_R,
9197 .quiet = true,
9198 },
9199 },
9200 },
9201 {
9202 .handled_access_fs = FS_W,
9203 .quiet_access_fs = FS_W,
9204 .rules = {
9205 {
9206 .path = file1_s1d1,
9207 .access = FS_W,
9208 .quiet = false,
9209 },
9210 /* Nothing for file2_s1d1 */
9211 {
9212 .path = file1_s1d2,
9213 .access = FS_W,
9214 .quiet = false,
9215 },
9216 /* Nothing for file2_s1d2 */
9217 },
9218 },
9219 },
9220 .targets = {
9221 {
9222 .target = file1_s1d1,
9223 .open_mode = O_RDWR,
9224 .expect_open_success = true,
9225 .expect_truncate_success = true,
9226 },
9227 /* Missing both, youngest layer denies write, not quiet */
9228 {
9229 .target = file2_s1d1,
9230 .open_mode = O_RDWR,
9231 .audit_write_blocked = true,
9232 },
9233 /* Missing read, denied and quieted by layer 1 */
9234 {
9235 .target = file1_s1d2,
9236 .open_mode = O_RDWR,
9237 },
9238 /* Missing write, denied and not quieted by layer 2 */
9239 {
9240 .target = file2_s1d2,
9241 .open_mode = O_RDWR,
9242 .audit_write_blocked = true,
9243 },
9244 },
9245 };
9246
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_two_layers_different_handled_2)9247 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_2) {
9248 /* Quiet from layer 2 */
9249 .layers = {
9250 {
9251 .handled_access_fs = FS_R,
9252 .quiet_access_fs = FS_R,
9253 .rules = {
9254 {
9255 .path = file1_s1d1,
9256 .access = FS_R,
9257 .quiet = false,
9258 },
9259 /* Nothing for file2_s1d1 and file1_s1d2 */
9260 {
9261 .path = file2_s1d2,
9262 .access = FS_R,
9263 .quiet = false,
9264 },
9265 },
9266 },
9267 {
9268 .handled_access_fs = FS_W,
9269 .quiet_access_fs = FS_W,
9270 .rules = {
9271 {
9272 .path = file1_s1d1,
9273 .access = FS_W,
9274 .quiet = true,
9275 },
9276 {
9277 .path = file2_s1d1,
9278 .access = 0,
9279 .quiet = true,
9280 },
9281 {
9282 .path = file1_s1d2,
9283 .access = FS_W,
9284 .quiet = true,
9285 },
9286 {
9287 .path = file2_s1d2,
9288 .access = 0,
9289 .quiet = true,
9290 },
9291 },
9292 },
9293 },
9294 .targets = {
9295 {
9296 .target = file1_s1d1,
9297 .open_mode = O_RDWR,
9298 .expect_open_success = true,
9299 .expect_truncate_success = true,
9300 },
9301 /* Missing both, youngest layer denies write, quiet */
9302 {
9303 .target = file2_s1d1,
9304 .open_mode = O_RDWR,
9305 },
9306 /* Missing read, denied and not quieted by layer 1 */
9307 {
9308 .target = file1_s1d2,
9309 .open_mode = O_RDWR,
9310 .audit_read_blocked = true,
9311 },
9312 /* Missing write, denied and quieted by layer 2 */
9313 {
9314 .target = file2_s1d2,
9315 .open_mode = O_RDWR,
9316 },
9317 },
9318 };
9319
FIXTURE_VARIANT_ADD(audit_quiet_layout1,quiet_two_layers_different_handled_3)9320 FIXTURE_VARIANT_ADD(audit_quiet_layout1, quiet_two_layers_different_handled_3) {
9321 /* Quiet from both layers */
9322 .layers = {
9323 {
9324 .handled_access_fs = FS_R,
9325 .quiet_access_fs = FS_R,
9326 .rules = {
9327 {
9328 .path = file1_s1d1,
9329 .access = FS_R,
9330 .quiet = true,
9331 },
9332 {
9333 .path = file2_s1d1,
9334 .access = 0,
9335 .quiet = true,
9336 },
9337 {
9338 .path = file1_s1d2,
9339 .access = 0,
9340 .quiet = true,
9341 },
9342 {
9343 .path = file2_s1d2,
9344 .access = FS_R,
9345 .quiet = true,
9346 },
9347 },
9348 },
9349 {
9350 .handled_access_fs = FS_W,
9351 .quiet_access_fs = FS_W,
9352 .rules = {
9353 {
9354 .path = file1_s1d1,
9355 .access = FS_W,
9356 .quiet = true,
9357 },
9358 {
9359 .path = file2_s1d1,
9360 .access = 0,
9361 .quiet = true,
9362 },
9363 {
9364 .path = file1_s1d2,
9365 .access = FS_W,
9366 .quiet = true,
9367 },
9368 {
9369 .path = file2_s1d2,
9370 .access = 0,
9371 .quiet = true,
9372 },
9373 },
9374 },
9375 },
9376 .targets = {
9377 {
9378 .target = file1_s1d1,
9379 .open_mode = O_RDWR,
9380 .expect_open_success = true,
9381 .expect_truncate_success = true,
9382 },
9383 {
9384 .target = file2_s1d1,
9385 .open_mode = O_RDWR,
9386 },
9387 {
9388 .target = file1_s1d2,
9389 .open_mode = O_RDWR,
9390 },
9391 {
9392 .target = file2_s1d2,
9393 .open_mode = O_RDWR,
9394 },
9395 },
9396 };
9397
FIXTURE_VARIANT_ADD(audit_quiet_layout1,without_quiet_then_with_quiet)9398 FIXTURE_VARIANT_ADD(audit_quiet_layout1, without_quiet_then_with_quiet) {
9399 .layers = {
9400 {
9401 .handled_access_fs = FS_R | FS_W,
9402 .quiet_access_fs = FS_R,
9403 .rules = {
9404 { .path = dir_s1d1, .access = FS_W, .quiet = false },
9405 { .path = dir_s1d1, .access = 0, .quiet = true },
9406 },
9407 },
9408 },
9409 .targets = {
9410 /* Read denied and quieted */
9411 {
9412 .target = file1_s1d1,
9413 .open_mode = O_RDONLY,
9414 },
9415 /* Write ok */
9416 {
9417 .target = file1_s1d1,
9418 .open_mode = O_WRONLY,
9419 .expect_open_success = true,
9420 .expect_truncate_success = true,
9421 },
9422 /* Write ok, read denied and quieted */
9423 {
9424 .target = file1_s1d1,
9425 .open_mode = O_RDWR,
9426 },
9427 /* Not covered by quiet */
9428 {
9429 .target = file1_s2d1,
9430 .open_mode = O_RDONLY,
9431 .audit_read_blocked = true,
9432 },
9433 },
9434 };
9435
9436 /*
9437 * The following TEST_F extend the above test cases to test more layers, with
9438 * the inserted layers having varying configurations.
9439 */
9440
9441 /* Extra allow all layers, quiet or not, does not change any behaviour. */
TEST_F(audit_quiet_layout1,allow_all_layer)9442 TEST_F(audit_quiet_layout1, allow_all_layer)
9443 {
9444 struct a_layer allow_all_layer = {
9445 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9446 .quiet_access_fs = 0,
9447 .rules = {
9448 {
9449 .path = "/",
9450 .access = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9451 .quiet = false,
9452 },
9453 },
9454 };
9455 int i;
9456
9457 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer));
9458 for (i = 0; i < ARRAY_SIZE(variant->layers); i++)
9459 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i]));
9460
9461 audit_quiet_layout1_test_body(_metadata, self, variant->targets);
9462
9463 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer));
9464
9465 audit_quiet_layout1_test_body(_metadata, self, variant->targets);
9466
9467 /*
9468 * SELF_LOG flags or quiet bits from inner allowing layers should not
9469 * affect behaviour.
9470 */
9471 allow_all_layer.quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL;
9472 allow_all_layer.rules[0].quiet = true;
9473 /*
9474 * Note: this only works because we're not checking counts of domain
9475 * alloc/dealloc logs
9476 */
9477 allow_all_layer.restrict_flags =
9478 LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
9479 LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF;
9480 ASSERT_EQ(0, apply_a_layer(_metadata, &allow_all_layer));
9481
9482 audit_quiet_layout1_test_body(_metadata, self, variant->targets);
9483 }
9484
9485 /*
9486 * Add useless outer layers until we reach the layer limit. Should not change
9487 * anything.
9488 */
TEST_F(audit_quiet_layout1,many_outer_layers)9489 TEST_F(audit_quiet_layout1, many_outer_layers)
9490 {
9491 struct a_layer useless_layer = {
9492 .handled_access_fs = FS_R | FS_W | FS_TRUNC,
9493 .quiet_access_fs = FS_R | FS_W | FS_TRUNC,
9494 .rules = {
9495 { .path = "/", .access = FS_R | FS_W | FS_TRUNC, .quiet = true },
9496 },
9497 };
9498 int i;
9499
9500 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) {
9501 if (variant->layers[i].handled_access_fs == 0)
9502 break;
9503 }
9504
9505 for (; i < LANDLOCK_MAX_NUM_LAYERS; i++)
9506 ASSERT_EQ(0, apply_a_layer(_metadata, &useless_layer));
9507
9508 for (i = 0; i < ARRAY_SIZE(variant->layers); i++)
9509 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i]));
9510
9511 audit_quiet_layout1_test_body(_metadata, self, variant->targets);
9512 }
9513
9514 /* An inner layer that denies and quiets everything should result in no logs. */
TEST_F(audit_quiet_layout1,deny_all_quiet_layer)9515 TEST_F(audit_quiet_layout1, deny_all_quiet_layer)
9516 {
9517 struct a_layer deny_all_layer = {
9518 .handled_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9519 .quiet_access_fs = FS_R | FS_W | FS_TRUNC | FS_IOCTL,
9520 .rules = {
9521 { .path = "/", .access = 0, .quiet = true },
9522 },
9523 };
9524 int i;
9525 FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {};
9526
9527 /* Any open should fail with no logs. */
9528 for (i = 0; i < ARRAY_SIZE(variant->targets); i++) {
9529 const struct a_target *target = &variant->targets[i];
9530
9531 variant_2.targets[i] = (struct a_target){
9532 .target = target->target,
9533 .open_mode = target->open_mode,
9534 /* We denied everything, open should always fail. */
9535 .expect_open_success = false,
9536 };
9537 }
9538
9539 for (i = 0; i < ARRAY_SIZE(variant->layers); i++)
9540 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i]));
9541 ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer));
9542
9543 audit_quiet_layout1_test_body(_metadata, self, variant_2.targets);
9544 }
9545
9546 /*
9547 * An inner layer that denies everything without quiet should produce logs for
9548 * all access.
9549 */
TEST_F(audit_quiet_layout1,deny_all_layer)9550 TEST_F(audit_quiet_layout1, deny_all_layer)
9551 {
9552 struct a_layer deny_all_layer = {
9553 .handled_access_fs = FS_R | FS_W,
9554 .quiet_access_fs = FS_R | FS_W,
9555 };
9556 int i;
9557 FIXTURE_VARIANT(audit_quiet_layout1) variant_2 = {};
9558 bool test_has_subdomains_off = false;
9559
9560 for (i = 0; i < ARRAY_SIZE(variant->layers); i++) {
9561 if (variant->layers[i].restrict_flags &
9562 LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF) {
9563 test_has_subdomains_off = true;
9564 break;
9565 }
9566 }
9567
9568 for (i = 0; i < ARRAY_SIZE(variant->targets); i++) {
9569 const struct a_target *target = &variant->targets[i];
9570
9571 variant_2.targets[i] = (struct a_target){
9572 .target = target->target,
9573 .open_mode = target->open_mode,
9574
9575 /* We denied everything, open should always fail. */
9576 .expect_open_success = false,
9577 /* Audit should always happen as long as open request contains read. */
9578 .audit_read_blocked = !test_has_subdomains_off &&
9579 target->open_mode != O_WRONLY,
9580 /* Audit should always happen as long as open request contains write. */
9581 .audit_write_blocked = !test_has_subdomains_off &&
9582 target->open_mode != O_RDONLY,
9583 };
9584 }
9585
9586 for (i = 0; i < ARRAY_SIZE(variant->layers); i++)
9587 ASSERT_EQ(0, apply_a_layer(_metadata, &variant->layers[i]));
9588 ASSERT_EQ(0, apply_a_layer(_metadata, &deny_all_layer));
9589
9590 audit_quiet_layout1_test_body(_metadata, self, variant_2.targets);
9591 }
9592
9593 /* Uses layout1_bind hierarchy */
FIXTURE(audit_quiet_rename)9594 FIXTURE(audit_quiet_rename)
9595 {
9596 struct audit_filter audit_filter;
9597 int audit_fd;
9598 };
9599
FIXTURE_SETUP(audit_quiet_rename)9600 FIXTURE_SETUP(audit_quiet_rename)
9601 {
9602 prepare_layout(_metadata);
9603 create_layout1(_metadata);
9604
9605 set_cap(_metadata, CAP_SYS_ADMIN);
9606 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
9607 clear_cap(_metadata, CAP_SYS_ADMIN);
9608
9609 set_cap(_metadata, CAP_AUDIT_CONTROL);
9610 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
9611 EXPECT_LE(0, self->audit_fd);
9612 clear_cap(_metadata, CAP_AUDIT_CONTROL);
9613
9614 if (getenv("DEBUG_QUIET_TESTS"))
9615 debug_quiet_tests = true;
9616 }
9617
FIXTURE_TEARDOWN_PARENT(audit_quiet_rename)9618 FIXTURE_TEARDOWN_PARENT(audit_quiet_rename)
9619 {
9620 remove_layout1(_metadata);
9621 cleanup_layout(_metadata);
9622
9623 /* umount(dir_s2d2)) is handled by namespace lifetime. */
9624
9625 remove_path(file1_s4d1);
9626 remove_path(file2_s4d1);
9627
9628 set_cap(_metadata, CAP_AUDIT_CONTROL);
9629 EXPECT_EQ(0, audit_cleanup(-1, NULL));
9630 clear_cap(_metadata, CAP_AUDIT_CONTROL);
9631 }
9632
simple_quiet_rename(struct __test_metadata * const _metadata,FIXTURE_DATA (audit_quiet_rename)* const self,__u64 handled_access,__u64 quiet_access,bool source_allow,bool dest_allow,bool source_quiet,bool dest_quiet,const char * source_blockers,const char * dest_blockers)9633 static void simple_quiet_rename(struct __test_metadata *const _metadata,
9634 FIXTURE_DATA(audit_quiet_rename) *const self,
9635 __u64 handled_access, __u64 quiet_access,
9636 bool source_allow, bool dest_allow,
9637 bool source_quiet, bool dest_quiet,
9638 const char *source_blockers,
9639 const char *dest_blockers)
9640 {
9641 /* We will move file1_s1d1 to file1_s2d1 */
9642 struct a_layer layer = {
9643 .handled_access_fs = handled_access,
9644 .quiet_access_fs = quiet_access,
9645 .rules = {
9646 {
9647 .path = dir_s1d1,
9648 .access = source_allow ? handled_access : 0,
9649 .quiet = source_quiet,
9650 },
9651 {
9652 .path = dir_s2d1,
9653 .access = dest_allow ? handled_access : 0,
9654 .quiet = dest_quiet,
9655 },
9656 },
9657 };
9658 struct audit_records records = {};
9659 int ret, err;
9660
9661 /* Skip landlock_add_rule for useless rules. */
9662 if (!source_allow && !source_quiet)
9663 layer.rules[0].path = NULL;
9664 if (!dest_allow && !dest_quiet)
9665 layer.rules[1].path = NULL;
9666
9667 EXPECT_EQ(0, unlink(file1_s2d1));
9668 EXPECT_EQ(0, apply_a_layer(_metadata, &layer));
9669
9670 if (debug_quiet_tests)
9671 TH_LOG("Try renameat \"%s\" to \"%s\"", file1_s1d1, file1_s2d1);
9672 ret = renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1);
9673 err = errno;
9674 if (ret != 0 && debug_quiet_tests) {
9675 TH_LOG("renameat error: %s", err == EXDEV ? "EXDEV" :
9676 err == EACCES ? "EACCES" :
9677 strerror(err));
9678 }
9679 if (source_allow && dest_allow) {
9680 ASSERT_EQ(0, ret);
9681 } else {
9682 ASSERT_EQ(-1, ret);
9683 if (handled_access & (LANDLOCK_ACCESS_FS_MAKE_REG |
9684 LANDLOCK_ACCESS_FS_REMOVE_FILE)) {
9685 ASSERT_EQ(EACCES, err);
9686 } else {
9687 ASSERT_EQ(EXDEV, err);
9688 }
9689
9690 if (source_blockers)
9691 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
9692 source_blockers, dir_s1d1));
9693 if (dest_blockers)
9694 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
9695 dest_blockers, dir_s2d1));
9696 }
9697 /*
9698 * No other logs. records.domain not checked per reasoning in
9699 * audit_quiet_layout1_test_body.
9700 */
9701 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
9702 ASSERT_EQ(0, records.access);
9703 }
9704
TEST_F(audit_quiet_rename,rename_ok)9705 TEST_F(audit_quiet_rename, rename_ok)
9706 {
9707 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9708 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9709 LANDLOCK_ACCESS_FS_REFER;
9710
9711 simple_quiet_rename(_metadata, self, access, access, true, true, false,
9712 false, NULL, NULL);
9713 }
9714
TEST_F(audit_quiet_rename,no_quiet)9715 TEST_F(audit_quiet_rename, no_quiet)
9716 {
9717 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9718 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9719 LANDLOCK_ACCESS_FS_REFER;
9720
9721 simple_quiet_rename(_metadata, self, access, access, false, false,
9722 false, false, "fs\\.remove_file,fs\\.refer",
9723 "fs\\.make_reg,fs\\.refer");
9724 }
9725
TEST_F(audit_quiet_rename,quiet)9726 TEST_F(audit_quiet_rename, quiet)
9727 {
9728 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9729 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9730 LANDLOCK_ACCESS_FS_REFER;
9731
9732 simple_quiet_rename(_metadata, self, access, access, false, false, true,
9733 true, NULL, NULL);
9734 }
9735
TEST_F(audit_quiet_rename,source_no_quiet_dest_quiet)9736 TEST_F(audit_quiet_rename, source_no_quiet_dest_quiet)
9737 {
9738 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9739 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9740 LANDLOCK_ACCESS_FS_REFER;
9741
9742 simple_quiet_rename(_metadata, self, access, access, false, false,
9743 false, true, "fs\\.remove_file,fs\\.refer", NULL);
9744 }
9745
TEST_F(audit_quiet_rename,source_quiet_dest_no_quiet)9746 TEST_F(audit_quiet_rename, source_quiet_dest_no_quiet)
9747 {
9748 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9749 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9750 LANDLOCK_ACCESS_FS_REFER;
9751
9752 simple_quiet_rename(_metadata, self, access, access, false, false, true,
9753 false, NULL, "fs\\.make_reg,fs\\.refer");
9754 }
9755
TEST_F(audit_quiet_rename,only_quiet_refer)9756 TEST_F(audit_quiet_rename, only_quiet_refer)
9757 {
9758 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9759 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9760 LANDLOCK_ACCESS_FS_REFER;
9761
9762 simple_quiet_rename(_metadata, self, access, LANDLOCK_ACCESS_FS_REFER,
9763 false, false, true, true,
9764 "fs\\.remove_file,fs\\.refer",
9765 "fs\\.make_reg,fs\\.refer");
9766 }
9767
TEST_F(audit_quiet_rename,source_allow_dest_quiet)9768 TEST_F(audit_quiet_rename, source_allow_dest_quiet)
9769 {
9770 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9771 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9772 LANDLOCK_ACCESS_FS_REFER;
9773
9774 simple_quiet_rename(_metadata, self, access, access, true, false, false,
9775 true, NULL, NULL);
9776 }
9777
TEST_F(audit_quiet_rename,source_quiet_dest_allow)9778 TEST_F(audit_quiet_rename, source_quiet_dest_allow)
9779 {
9780 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9781 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9782 LANDLOCK_ACCESS_FS_REFER;
9783
9784 simple_quiet_rename(_metadata, self, access, access, false, true, true,
9785 false, NULL, NULL);
9786 }
9787
TEST_F(audit_quiet_rename,handle_all_deny_quiet_refer)9788 TEST_F(audit_quiet_rename, handle_all_deny_quiet_refer)
9789 {
9790 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9791 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9792 LANDLOCK_ACCESS_FS_REFER;
9793 struct a_layer layer = {
9794 .handled_access_fs = access,
9795 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER,
9796 .rules = {
9797 {
9798 .path = dir_s1d1,
9799 .access = LANDLOCK_ACCESS_FS_MAKE_REG |
9800 LANDLOCK_ACCESS_FS_REMOVE_FILE,
9801 .quiet = true,
9802 },
9803 {
9804 .path = dir_s2d1,
9805 .access = LANDLOCK_ACCESS_FS_MAKE_REG |
9806 LANDLOCK_ACCESS_FS_REMOVE_FILE,
9807 .quiet = true,
9808 },
9809 },
9810 };
9811 struct audit_records records = {};
9812
9813 EXPECT_EQ(0, unlink(file1_s2d1));
9814 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
9815
9816 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
9817 ASSERT_EQ(EXDEV, errno);
9818
9819 /* No logs */
9820 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
9821 ASSERT_EQ(0, records.access);
9822 }
9823
TEST_F(audit_quiet_rename,handle_all_deny_not_quiet_refer)9824 TEST_F(audit_quiet_rename, handle_all_deny_not_quiet_refer)
9825 {
9826 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9827 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9828 LANDLOCK_ACCESS_FS_REFER;
9829 struct a_layer layer = {
9830 .handled_access_fs = access,
9831 .quiet_access_fs = 0,
9832 .rules = {
9833 {
9834 .path = dir_s1d1,
9835 .access = LANDLOCK_ACCESS_FS_MAKE_REG |
9836 LANDLOCK_ACCESS_FS_REMOVE_FILE,
9837 .quiet = false,
9838 },
9839 {
9840 .path = dir_s2d1,
9841 .access = LANDLOCK_ACCESS_FS_MAKE_REG |
9842 LANDLOCK_ACCESS_FS_REMOVE_FILE,
9843 .quiet = false,
9844 },
9845 },
9846 };
9847 struct audit_records records = {};
9848
9849 EXPECT_EQ(0, unlink(file1_s2d1));
9850 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
9851
9852 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
9853 ASSERT_EQ(EXDEV, errno);
9854
9855 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
9856 dir_s1d1));
9857 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
9858 dir_s2d1));
9859
9860 /* No other logs */
9861 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
9862 ASSERT_EQ(0, records.access);
9863 }
9864
TEST_F(audit_quiet_rename,handle_all_deny_refer_quiet_source_not_quiet_dest)9865 TEST_F(audit_quiet_rename, handle_all_deny_refer_quiet_source_not_quiet_dest)
9866 {
9867 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9868 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9869 LANDLOCK_ACCESS_FS_REFER;
9870 struct a_layer layer = {
9871 .handled_access_fs = access,
9872 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER,
9873 .rules = {
9874 {
9875 .path = dir_s1d1,
9876 .access = LANDLOCK_ACCESS_FS_MAKE_REG |
9877 LANDLOCK_ACCESS_FS_REMOVE_FILE,
9878 .quiet = true,
9879 },
9880 {
9881 .path = dir_s2d1,
9882 .access = LANDLOCK_ACCESS_FS_MAKE_REG |
9883 LANDLOCK_ACCESS_FS_REMOVE_FILE,
9884 .quiet = false,
9885 },
9886 },
9887 };
9888 struct audit_records records = {};
9889
9890 EXPECT_EQ(0, unlink(file1_s2d1));
9891 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
9892
9893 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
9894 ASSERT_EQ(EXDEV, errno);
9895
9896 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
9897 dir_s2d1));
9898
9899 /* No other logs */
9900 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
9901 ASSERT_EQ(0, records.access);
9902 }
9903
TEST_F(audit_quiet_rename,quiet_same_dir)9904 TEST_F(audit_quiet_rename, quiet_same_dir)
9905 {
9906 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9907 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9908 LANDLOCK_ACCESS_FS_REFER;
9909 struct a_layer layer = {
9910 .handled_access_fs = access,
9911 .quiet_access_fs = access,
9912 .rules = {
9913 {
9914 .path = dir_s1d1,
9915 .access = 0,
9916 .quiet = true,
9917 },
9918 },
9919 };
9920 struct audit_records records = {};
9921
9922 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
9923
9924 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1));
9925 ASSERT_EQ(EACCES, errno);
9926
9927 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
9928 ASSERT_EQ(0, records.access);
9929 }
9930
TEST_F(audit_quiet_rename,quiet_flag_on_file_ignored)9931 TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored)
9932 {
9933 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9934 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9935 LANDLOCK_ACCESS_FS_REFER;
9936 struct a_layer layer = {
9937 .handled_access_fs = access,
9938 .quiet_access_fs = access,
9939 .rules = {
9940 {
9941 .path = file1_s1d1,
9942 .access = 0,
9943 .quiet = true,
9944 },
9945 {
9946 .path = file1_s2d1,
9947 .access = 0,
9948 .quiet = true,
9949 },
9950 },
9951 };
9952 struct audit_records records = {};
9953
9954 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
9955
9956 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
9957 ASSERT_EQ(EACCES, errno);
9958
9959 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
9960 "fs\\.remove_file,fs\\.refer", dir_s1d1));
9961 /* We didn't unlink destination file */
9962 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
9963 "fs\\.remove_file,fs\\.make_reg,fs\\.refer",
9964 dir_s2d1));
9965
9966 /* No other logs */
9967 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
9968 ASSERT_EQ(0, records.access);
9969 }
9970
TEST_F(audit_quiet_rename,quiet_flag_on_file_ignored_same_dir)9971 TEST_F(audit_quiet_rename, quiet_flag_on_file_ignored_same_dir)
9972 {
9973 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
9974 LANDLOCK_ACCESS_FS_REMOVE_FILE |
9975 LANDLOCK_ACCESS_FS_REFER;
9976 struct a_layer layer = {
9977 .handled_access_fs = access,
9978 .quiet_access_fs = access,
9979 .rules = {
9980 {
9981 .path = file1_s1d1,
9982 .access = 0,
9983 .quiet = true,
9984 },
9985 {
9986 .path = file2_s1d1,
9987 .access = 0,
9988 .quiet = true,
9989 },
9990 },
9991 };
9992 struct audit_records records = {};
9993
9994 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
9995
9996 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1));
9997 ASSERT_EQ(EACCES, errno);
9998
9999 ASSERT_EQ(0,
10000 matches_log_fs(_metadata, self->audit_fd,
10001 "fs\\.remove_file,fs\\.make_reg", dir_s1d1));
10002
10003 /* No other logs */
10004 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10005 ASSERT_EQ(0, records.access);
10006 }
10007
TEST_F(audit_quiet_rename,two_layers_different_quiet1)10008 TEST_F(audit_quiet_rename, two_layers_different_quiet1)
10009 {
10010 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10011 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10012 LANDLOCK_ACCESS_FS_REFER;
10013 struct a_layer layer1 = {
10014 .handled_access_fs = access,
10015 .quiet_access_fs = access,
10016 .rules = {
10017 {
10018 .path = dir_s1d1,
10019 .access = access,
10020 .quiet = false,
10021 },
10022 {
10023 .path = dir_s2d1,
10024 .access = 0,
10025 .quiet = true,
10026 },
10027 },
10028 };
10029 struct a_layer layer2 = {
10030 .handled_access_fs = access,
10031 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER,
10032 .rules = {
10033 {
10034 .path = dir_s1d1,
10035 .access = 0,
10036 .quiet = true,
10037 },
10038 {
10039 .path = dir_s2d1,
10040 .access = access,
10041 .quiet = false,
10042 },
10043 },
10044 };
10045 struct audit_records records = {};
10046
10047 EXPECT_EQ(0, unlink(file1_s2d1));
10048
10049 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1));
10050 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2));
10051
10052 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
10053 ASSERT_EQ(EACCES, errno);
10054
10055 /*
10056 * The youngest denial will be layer 2. Refer is quieted but we are
10057 * also missing remove_file on source.
10058 */
10059 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
10060 "fs\\.remove_file,fs\\.refer", dir_s1d1));
10061 /* No other logs */
10062 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10063 ASSERT_EQ(0, records.access);
10064 }
10065
TEST_F(audit_quiet_rename,two_layers_different_quiet2)10066 TEST_F(audit_quiet_rename, two_layers_different_quiet2)
10067 {
10068 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10069 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10070 LANDLOCK_ACCESS_FS_REFER;
10071 struct a_layer layer1 = {
10072 .handled_access_fs = access,
10073 .quiet_access_fs = access,
10074 .rules = {
10075 {
10076 .path = dir_s1d1,
10077 .access = access,
10078 .quiet = false,
10079 },
10080 {
10081 .path = dir_s2d1,
10082 .access = 0,
10083 .quiet = true,
10084 },
10085 },
10086 };
10087 struct a_layer layer2 = {
10088 .handled_access_fs = LANDLOCK_ACCESS_FS_REFER,
10089 .quiet_access_fs = LANDLOCK_ACCESS_FS_REFER,
10090 .rules = {
10091 {
10092 .path = dir_s1d1,
10093 .access = 0,
10094 .quiet = true,
10095 },
10096 {
10097 .path = dir_s2d1,
10098 .access = LANDLOCK_ACCESS_FS_REFER,
10099 .quiet = false,
10100 },
10101 },
10102 };
10103 struct audit_records records = {};
10104
10105 EXPECT_EQ(0, unlink(file1_s2d1));
10106
10107 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1));
10108 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2));
10109
10110 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
10111 ASSERT_EQ(EACCES, errno);
10112
10113 /*
10114 * The youngest denial will be layer 2, but refer is quieted (and that
10115 * layer does not handle any other accesses).
10116 */
10117 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10118 ASSERT_EQ(0, records.access);
10119 }
10120
TEST_F(audit_quiet_rename,two_layers_different_quiet3)10121 TEST_F(audit_quiet_rename, two_layers_different_quiet3)
10122 {
10123 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10124 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10125 LANDLOCK_ACCESS_FS_REFER;
10126 struct a_layer layer1 = {
10127 .handled_access_fs = access,
10128 .quiet_access_fs = access,
10129 .rules = {
10130 {
10131 .path = dir_s1d1,
10132 .access = access,
10133 .quiet = false,
10134 },
10135 {
10136 .path = dir_s2d1,
10137 .access = 0,
10138 .quiet = true,
10139 },
10140 },
10141 };
10142 struct a_layer layer2 = {
10143 .handled_access_fs = access,
10144 .quiet_access_fs = access,
10145 .rules = {
10146 {
10147 .path = dir_s1d1,
10148 .access = 0,
10149 .quiet = true,
10150 },
10151 {
10152 .path = dir_s2d1,
10153 .access = access,
10154 .quiet = false,
10155 },
10156 },
10157 };
10158 struct audit_records records = {};
10159
10160 EXPECT_EQ(0, unlink(file1_s2d1));
10161
10162 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1));
10163 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2));
10164
10165 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
10166 ASSERT_EQ(EACCES, errno);
10167
10168 /*
10169 * The youngest denial will be layer 2, in which everything is quieted.
10170 */
10171 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10172 ASSERT_EQ(0, records.access);
10173 }
10174
TEST_F(audit_quiet_rename,first_layer_quiet_deny_all_second_layer_not_quiet_deny_all)10175 TEST_F(audit_quiet_rename,
10176 first_layer_quiet_deny_all_second_layer_not_quiet_deny_all)
10177 {
10178 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10179 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10180 LANDLOCK_ACCESS_FS_REFER;
10181 struct a_layer layer1 = {
10182 .handled_access_fs = access,
10183 .quiet_access_fs = access,
10184 .rules = {
10185 {
10186 .path = dir_s1d1,
10187 .access = 0,
10188 .quiet = true,
10189 },
10190 {
10191 .path = dir_s2d1,
10192 .access = 0,
10193 .quiet = true,
10194 },
10195 },
10196 };
10197 struct a_layer layer2 = {
10198 .handled_access_fs = access,
10199 .quiet_access_fs = access,
10200 .rules = {},
10201 };
10202 struct audit_records records = {};
10203
10204 EXPECT_EQ(0, unlink(file1_s2d1));
10205
10206 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1));
10207 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2));
10208
10209 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
10210 ASSERT_EQ(EACCES, errno);
10211
10212 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
10213 "fs\\.remove_file,fs\\.refer", dir_s1d1));
10214 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
10215 "fs\\.make_reg,fs\\.refer", dir_s2d1));
10216 /* No other logs. */
10217 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10218 ASSERT_EQ(0, records.access);
10219 }
10220
TEST_F(audit_quiet_rename,first_layer_quiet_deny_all_second_layer_dest_not_quiet)10221 TEST_F(audit_quiet_rename,
10222 first_layer_quiet_deny_all_second_layer_dest_not_quiet)
10223 {
10224 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10225 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10226 LANDLOCK_ACCESS_FS_REFER;
10227 struct a_layer layer1 = {
10228 .handled_access_fs = access,
10229 .quiet_access_fs = access,
10230 .rules = {
10231 {
10232 .path = dir_s1d1,
10233 .access = 0,
10234 .quiet = true,
10235 },
10236 {
10237 .path = dir_s2d1,
10238 .access = 0,
10239 .quiet = true,
10240 },
10241 },
10242 };
10243 struct a_layer layer2 = {
10244 .handled_access_fs = access,
10245 .quiet_access_fs = access,
10246 .rules = {
10247 {
10248 .path = dir_s1d1,
10249 .access = 0,
10250 .quiet = true,
10251 },
10252 },
10253 };
10254 struct audit_records records = {};
10255
10256 EXPECT_EQ(0, unlink(file1_s2d1));
10257
10258 ASSERT_EQ(0, apply_a_layer(_metadata, &layer1));
10259 ASSERT_EQ(0, apply_a_layer(_metadata, &layer2));
10260
10261 ASSERT_EQ(-1, renameat(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1));
10262 ASSERT_EQ(EACCES, errno);
10263
10264 /* Source is quieted but destination is not. */
10265 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
10266 "fs\\.make_reg,fs\\.refer", dir_s2d1));
10267 /* No other logs. */
10268 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10269 ASSERT_EQ(0, records.access);
10270 }
10271
TEST_F(audit_quiet_rename,rename_xchg)10272 TEST_F(audit_quiet_rename, rename_xchg)
10273 {
10274 struct a_layer layer = {
10275 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG |
10276 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10277 LANDLOCK_ACCESS_FS_REFER,
10278 .quiet_access_fs = LANDLOCK_ACCESS_FS_MAKE_REG,
10279 .rules = { {
10280 .path = dir_s1d1,
10281 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE |
10282 LANDLOCK_ACCESS_FS_REFER,
10283 .quiet = true,
10284 },
10285 {
10286 .path = dir_s2d1,
10287 .access = LANDLOCK_ACCESS_FS_MAKE_REG |
10288 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10289 LANDLOCK_ACCESS_FS_REFER,
10290 .quiet = false,
10291 } },
10292 };
10293 struct audit_records records = {};
10294
10295 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
10296
10297 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d1,
10298 RENAME_EXCHANGE));
10299 ASSERT_EQ(EACCES, errno);
10300
10301 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10302 ASSERT_EQ(0, records.access);
10303 }
10304
TEST_F(audit_quiet_rename,quiet_on_parent_mount)10305 TEST_F(audit_quiet_rename, quiet_on_parent_mount)
10306 {
10307 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10308 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10309 LANDLOCK_ACCESS_FS_REFER;
10310 struct a_layer layer = {
10311 .handled_access_fs = access,
10312 .quiet_access_fs = access,
10313 .rules = {
10314 {
10315 .path = dir_s2d1,
10316 .access = 0,
10317 .quiet = true,
10318 },
10319 },
10320 };
10321 struct audit_records records = {};
10322
10323 EXPECT_EQ(0, unlink(file2_s1d3));
10324 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
10325
10326 ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD,
10327 bind_file2_s1d3));
10328 ASSERT_EQ(EACCES, errno);
10329
10330 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10331 ASSERT_EQ(0, records.access);
10332 }
10333
TEST_F(audit_quiet_rename,quiet_behind_mountpoint_ignored)10334 TEST_F(audit_quiet_rename, quiet_behind_mountpoint_ignored)
10335 {
10336 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10337 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10338 LANDLOCK_ACCESS_FS_REFER;
10339 struct a_layer layer = {
10340 .handled_access_fs = access,
10341 .quiet_access_fs = access,
10342 .rules = {
10343 {
10344 .path = dir_s1d1,
10345 .access = 0,
10346 .quiet = true,
10347 },
10348 },
10349 };
10350 struct audit_records records = {};
10351
10352 EXPECT_EQ(0, unlink(file2_s1d3));
10353 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
10354
10355 ASSERT_EQ(-1, renameat(AT_FDCWD, bind_file1_s1d3, AT_FDCWD,
10356 bind_file2_s1d3));
10357 ASSERT_EQ(EACCES, errno);
10358 ASSERT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
10359 "fs\\.remove_file,fs\\.make_reg",
10360 bind_dir_s1d3));
10361
10362 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10363 ASSERT_EQ(0, records.access);
10364 }
10365
TEST_F(audit_quiet_rename,quiet_on_parent_mount_disconnected)10366 TEST_F(audit_quiet_rename, quiet_on_parent_mount_disconnected)
10367 {
10368 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10369 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10370 LANDLOCK_ACCESS_FS_REFER;
10371 struct a_layer layer = {
10372 .handled_access_fs = access,
10373 .quiet_access_fs = access,
10374 .rules = {
10375 {
10376 .path = dir_s2d1,
10377 .access = 0,
10378 .quiet = true,
10379 },
10380 },
10381 };
10382 struct audit_records records = {};
10383 int bind_s1d3_fd;
10384
10385 EXPECT_EQ(0, unlink(file2_s1d3));
10386
10387 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY);
10388 ASSERT_GE(bind_s1d3_fd, 0);
10389
10390 /* Make s1d3 disconnected. */
10391 create_directory(_metadata, dir_s4d1);
10392 ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2));
10393
10394 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
10395
10396 ASSERT_EQ(-1,
10397 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name));
10398 ASSERT_EQ(EACCES, errno);
10399
10400 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10401 ASSERT_EQ(0, records.access);
10402 }
10403
TEST_F(audit_quiet_rename,quiet_behind_mountpoint_disconnected)10404 TEST_F(audit_quiet_rename, quiet_behind_mountpoint_disconnected)
10405 {
10406 __u64 access = LANDLOCK_ACCESS_FS_MAKE_REG |
10407 LANDLOCK_ACCESS_FS_REMOVE_FILE |
10408 LANDLOCK_ACCESS_FS_REFER;
10409 struct a_layer layer = {
10410 .handled_access_fs = access,
10411 .quiet_access_fs = access,
10412 .rules = {
10413 {
10414 .path = dir_s4d1,
10415 .access = 0,
10416 .quiet = true,
10417 },
10418 },
10419 };
10420 struct audit_records records = {};
10421 int bind_s1d3_fd;
10422
10423 EXPECT_EQ(0, unlink(file2_s1d3));
10424
10425 bind_s1d3_fd = open(bind_dir_s1d3, O_PATH | O_DIRECTORY);
10426 ASSERT_GE(bind_s1d3_fd, 0);
10427
10428 /* Make s1d3 disconnected. */
10429 create_directory(_metadata, dir_s4d1);
10430 ASSERT_EQ(0, renameat(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s4d2));
10431
10432 ASSERT_EQ(0, apply_a_layer(_metadata, &layer));
10433
10434 ASSERT_EQ(-1,
10435 renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name));
10436 ASSERT_EQ(EACCES, errno);
10437
10438 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
10439 ASSERT_EQ(0, records.access);
10440 }
10441
10442 /* clang-format off */
FIXTURE(trace_layout1)10443 FIXTURE(trace_layout1) {
10444 /* clang-format on */
10445 int tracefs_ok;
10446 };
10447
FIXTURE_SETUP(trace_layout1)10448 FIXTURE_SETUP(trace_layout1)
10449 {
10450 struct stat st;
10451
10452 /*
10453 * Check tracefs availability before creating the layout, following the
10454 * layout3_fs pattern: skip before any layout creation to avoid leaving
10455 * stale TMP_DIR on skip.
10456 */
10457 if (stat(TRACEFS_LANDLOCK_DIR, &st)) {
10458 self->tracefs_ok = 0;
10459 SKIP(return, "tracefs not available");
10460 }
10461 self->tracefs_ok = 1;
10462
10463 /* Isolate tracefs state (PID filter, event enables). */
10464 set_cap(_metadata, CAP_SYS_ADMIN);
10465 ASSERT_EQ(0, unshare(CLONE_NEWNS));
10466 ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL));
10467 clear_cap(_metadata, CAP_SYS_ADMIN);
10468
10469 prepare_layout(_metadata);
10470 create_layout1(_metadata);
10471
10472 set_cap(_metadata, CAP_DAC_OVERRIDE);
10473 ASSERT_EQ(0, tracefs_fixture_setup());
10474 ASSERT_EQ(0, tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, true));
10475 ASSERT_EQ(0, tracefs_clear());
10476 ASSERT_EQ(0, tracefs_set_pid_filter(getpid()));
10477 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10478 }
10479
FIXTURE_TEARDOWN_PARENT(trace_layout1)10480 FIXTURE_TEARDOWN_PARENT(trace_layout1)
10481 {
10482 if (!self->tracefs_ok)
10483 return;
10484
10485 set_cap(_metadata, CAP_DAC_OVERRIDE);
10486 tracefs_enable_event(TRACEFS_CHECK_RULE_FS_ENABLE, false);
10487 tracefs_clear_pid_filter();
10488 tracefs_fixture_teardown();
10489 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10490
10491 remove_layout1(_metadata);
10492 cleanup_layout(_metadata);
10493 }
10494
10495 /*
10496 * Verifies that check_rule_inode events include correct field values: domain,
10497 * dev, ino, access_request, and grants. All values are verified against stat()
10498 * of the rule path on a deterministic tmpfs layout.
10499 */
TEST_F(trace_layout1,check_rule_fs_fields)10500 TEST_F(trace_layout1, check_rule_fs_fields)
10501 {
10502 struct stat dir_stat;
10503 char expected_dev[32];
10504 char expected_ino[32];
10505 char *buf;
10506 char field[64];
10507
10508 if (!self->tracefs_ok)
10509 SKIP(return, "tracefs not available");
10510
10511 ASSERT_EQ(0, stat(dir_s1d1, &dir_stat));
10512 snprintf(expected_dev, sizeof(expected_dev), "%u:%u",
10513 major(dir_stat.st_dev), minor(dir_stat.st_dev));
10514 snprintf(expected_ino, sizeof(expected_ino), "%lu", dir_stat.st_ino);
10515
10516 set_cap(_metadata, CAP_DAC_OVERRIDE);
10517 ASSERT_EQ(0, tracefs_clear());
10518 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10519
10520 sandbox_child_fs_access(_metadata, dir_s1d1,
10521 LANDLOCK_ACCESS_FS_READ_DIR,
10522 LANDLOCK_ACCESS_FS_READ_DIR, dir_s1d1);
10523
10524 set_cap(_metadata, CAP_DAC_OVERRIDE);
10525 buf = tracefs_read_trace();
10526 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10527 ASSERT_NE(NULL, buf);
10528
10529 EXPECT_EQ(1,
10530 tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK)))
10531 {
10532 TH_LOG("Expected 1 check_rule_inode event\n%s", buf);
10533 }
10534
10535 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10536 "dev", field, sizeof(field)));
10537 EXPECT_STREQ(expected_dev, field)
10538 {
10539 TH_LOG("Expected dev=%s, got %s", expected_dev, field);
10540 }
10541
10542 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10543 "ino", field, sizeof(field)));
10544 EXPECT_STREQ(expected_ino, field)
10545 {
10546 TH_LOG("Expected ino=%s, got %s", expected_ino, field);
10547 }
10548
10549 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10550 "access_request", field,
10551 sizeof(field)));
10552 EXPECT_STREQ("read_dir", field)
10553 {
10554 TH_LOG("Expected access_request=read_dir, got %s", field);
10555 }
10556
10557 /*
10558 * The domain handles only READ_DIR, so the rule carries the
10559 * unhandled-rights padding; intersecting with the request leaves just
10560 * the requested read_dir (no padding, no hex).
10561 */
10562 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10563 "grants", field, sizeof(field)));
10564 EXPECT_STREQ("{read_dir}", field)
10565 {
10566 TH_LOG("Expected grants={read_dir}, got %s", field);
10567 }
10568
10569 free(buf);
10570 }
10571
10572 /*
10573 * Verifies check_rule_inode behavior with multiple rules. With rules at s1d1
10574 * and s1d2 (a child of s1d1), accessing s1d2 produces only 1 event because the
10575 * pathwalk short-circuits after the first rule fully unmasks the single layer.
10576 */
TEST_F(trace_layout1,check_rule_fs_multiple_rules)10577 TEST_F(trace_layout1, check_rule_fs_multiple_rules)
10578 {
10579 pid_t pid;
10580 int status;
10581 char *buf;
10582 int count;
10583
10584 if (!self->tracefs_ok)
10585 SKIP(return, "tracefs not available");
10586
10587 set_cap(_metadata, CAP_DAC_OVERRIDE);
10588 ASSERT_EQ(0, tracefs_clear());
10589 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10590
10591 pid = fork();
10592 ASSERT_LE(0, pid);
10593
10594 if (pid == 0) {
10595 struct landlock_ruleset_attr attr = {
10596 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,
10597 };
10598 struct landlock_path_beneath_attr path_beneath = {
10599 .allowed_access = LANDLOCK_ACCESS_FS_READ_DIR,
10600 };
10601 int ruleset_fd, fd;
10602
10603 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
10604 if (ruleset_fd < 0)
10605 _exit(1);
10606
10607 path_beneath.parent_fd =
10608 open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
10609 if (path_beneath.parent_fd < 0)
10610 _exit(1);
10611 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
10612 &path_beneath, 0))
10613 _exit(1);
10614 close(path_beneath.parent_fd);
10615
10616 path_beneath.parent_fd =
10617 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
10618 if (path_beneath.parent_fd < 0)
10619 _exit(1);
10620 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
10621 &path_beneath, 0))
10622 _exit(1);
10623 close(path_beneath.parent_fd);
10624
10625 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
10626 if (landlock_restrict_self(ruleset_fd, 0))
10627 _exit(1);
10628 close(ruleset_fd);
10629
10630 fd = open(dir_s1d2, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
10631 if (fd >= 0)
10632 close(fd);
10633 _exit(0);
10634 }
10635
10636 ASSERT_EQ(pid, waitpid(pid, &status, 0));
10637 ASSERT_TRUE(WIFEXITED(status));
10638 EXPECT_EQ(0, WEXITSTATUS(status));
10639
10640 set_cap(_metadata, CAP_DAC_OVERRIDE);
10641 buf = tracefs_read_trace();
10642 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10643 ASSERT_NE(NULL, buf);
10644
10645 /*
10646 * Only one check_rule_inode event: the rule on dir_s1d2 fully unmasks
10647 * the single layer, so the pathwalk short-circuits before reaching the
10648 * dir_s1d1 rule.
10649 */
10650 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
10651 EXPECT_EQ(1, count)
10652 {
10653 TH_LOG("Expected 1 check_rule_inode event, got %d\n%s", count,
10654 buf);
10655 }
10656
10657 free(buf);
10658 }
10659
10660 /*
10661 * Verifies the grants array is intersected with the request: a handled,
10662 * granted, but unrequested right (execute) is filtered out, leaving only the
10663 * requested read_dir.
10664 */
TEST_F(trace_layout1,check_rule_fs_request_subset)10665 TEST_F(trace_layout1, check_rule_fs_request_subset)
10666 {
10667 char *buf;
10668 char field[64];
10669
10670 if (!self->tracefs_ok)
10671 SKIP(return, "tracefs not available");
10672
10673 set_cap(_metadata, CAP_DAC_OVERRIDE);
10674 ASSERT_EQ(0, tracefs_clear());
10675 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10676
10677 /*
10678 * Handle and grant READ_DIR|EXECUTE; the open only requests read_dir.
10679 */
10680 sandbox_child_fs_access(
10681 _metadata, dir_s1d1,
10682 LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE,
10683 LANDLOCK_ACCESS_FS_READ_DIR | LANDLOCK_ACCESS_FS_EXECUTE,
10684 dir_s1d1);
10685
10686 set_cap(_metadata, CAP_DAC_OVERRIDE);
10687 buf = tracefs_read_trace();
10688 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10689 ASSERT_NE(NULL, buf);
10690
10691 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10692 "access_request", field,
10693 sizeof(field)));
10694 EXPECT_STREQ("read_dir", field);
10695
10696 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10697 "grants", field, sizeof(field)));
10698 EXPECT_STREQ("{read_dir}", field);
10699
10700 free(buf);
10701 }
10702
10703 /*
10704 * Verifies that the optional TRUNCATE access right, which hook_file_open()
10705 * speculatively evaluates on every open, appears in the access_request= and
10706 * grants= fields. Opening file1_s1d1 read-only needs only read_file, but the
10707 * open hook also evaluates truncate; the domain handles and the rule grants
10708 * both, so the event reports access_request=read_file|truncate and
10709 * grants={read_file|truncate}, and the open is allowed.
10710 */
TEST_F(trace_layout1,check_rule_fs_optional_access)10711 TEST_F(trace_layout1, check_rule_fs_optional_access)
10712 {
10713 pid_t pid;
10714 int status;
10715 char *buf;
10716 char field[64];
10717 int count;
10718
10719 if (!self->tracefs_ok)
10720 SKIP(return, "tracefs not available");
10721
10722 set_cap(_metadata, CAP_DAC_OVERRIDE);
10723 ASSERT_EQ(0, tracefs_clear());
10724 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10725
10726 pid = fork();
10727 ASSERT_LE(0, pid);
10728
10729 if (pid == 0) {
10730 struct landlock_ruleset_attr attr = {
10731 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE |
10732 LANDLOCK_ACCESS_FS_TRUNCATE,
10733 };
10734 struct landlock_path_beneath_attr path_beneath = {
10735 .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE |
10736 LANDLOCK_ACCESS_FS_TRUNCATE,
10737 };
10738 int ruleset_fd, fd;
10739
10740 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
10741 if (ruleset_fd < 0)
10742 _exit(1);
10743
10744 path_beneath.parent_fd =
10745 open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
10746 if (path_beneath.parent_fd < 0)
10747 _exit(1);
10748 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
10749 &path_beneath, 0))
10750 _exit(1);
10751 close(path_beneath.parent_fd);
10752
10753 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
10754 if (landlock_restrict_self(ruleset_fd, 0))
10755 _exit(1);
10756 close(ruleset_fd);
10757
10758 /* Read-only open needs only read_file; truncate is optional. */
10759 fd = open(file1_s1d1, O_RDONLY | O_CLOEXEC);
10760 if (fd < 0)
10761 _exit(1);
10762 close(fd);
10763 _exit(0);
10764 }
10765
10766 ASSERT_EQ(pid, waitpid(pid, &status, 0));
10767 ASSERT_TRUE(WIFEXITED(status));
10768 /* The open is allowed: the required read_file is granted. */
10769 EXPECT_EQ(0, WEXITSTATUS(status));
10770
10771 set_cap(_metadata, CAP_DAC_OVERRIDE);
10772 buf = tracefs_read_trace();
10773 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10774 ASSERT_NE(NULL, buf);
10775
10776 /* The rule at dir_s1d1 matches when opening file1_s1d1. */
10777 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
10778 EXPECT_EQ(1, count)
10779 {
10780 TH_LOG("Expected 1 check_rule_inode event, got %d\n%s", count,
10781 buf);
10782 }
10783
10784 /* The open hook adds the optional truncate to the request. */
10785 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10786 "access_request", field,
10787 sizeof(field)));
10788 EXPECT_STREQ("read_file|truncate", field);
10789
10790 /* The rule grants both, so truncate appears in the grants array. */
10791 ASSERT_EQ(0, tracefs_extract_field(buf, REGEX_CHECK_RULE_FS(TRACE_TASK),
10792 "grants", field, sizeof(field)));
10793 EXPECT_STREQ("{read_file|truncate}", field);
10794
10795 free(buf);
10796 }
10797
10798 /*
10799 * Verifies that check_rule_inode fires for a rule matching the inode even when
10800 * it grants none of the requested rights, so the grants set is empty. Landlock
10801 * cannot know a rule ignores the request before reading it, so the event is
10802 * still emitted (grants={}), which lets a tracer see that the rule matched.
10803 * The domain handles READ_DIR|EXECUTE, dir_s1d2 grants only EXECUTE and its
10804 * parent dir_s1d1 grants only READ_DIR. Reading dir_s1d2 (requesting read_dir)
10805 * first matches the dir_s1d2 rule, which grants nothing requested (grants={});
10806 * walking up to dir_s1d1 then grants read_dir (grants={read_dir}) and allows
10807 * the access.
10808 */
TEST_F(trace_layout1,check_rule_fs_empty_grant)10809 TEST_F(trace_layout1, check_rule_fs_empty_grant)
10810 {
10811 pid_t pid;
10812 int status;
10813 char *buf;
10814 int count;
10815
10816 if (!self->tracefs_ok)
10817 SKIP(return, "tracefs not available");
10818
10819 set_cap(_metadata, CAP_DAC_OVERRIDE);
10820 ASSERT_EQ(0, tracefs_clear());
10821 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10822
10823 pid = fork();
10824 ASSERT_LE(0, pid);
10825
10826 if (pid == 0) {
10827 struct landlock_ruleset_attr attr = {
10828 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR |
10829 LANDLOCK_ACCESS_FS_EXECUTE,
10830 };
10831 struct landlock_path_beneath_attr path_beneath = {};
10832 int ruleset_fd, fd;
10833
10834 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
10835 if (ruleset_fd < 0)
10836 _exit(1);
10837
10838 /* Parent dir_s1d1 grants only READ_DIR. */
10839 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_READ_DIR;
10840 path_beneath.parent_fd =
10841 open(dir_s1d1, O_PATH | O_DIRECTORY | O_CLOEXEC);
10842 if (path_beneath.parent_fd < 0)
10843 _exit(1);
10844 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
10845 &path_beneath, 0))
10846 _exit(1);
10847 close(path_beneath.parent_fd);
10848
10849 /* Child dir_s1d2 grants only EXECUTE. */
10850 path_beneath.allowed_access = LANDLOCK_ACCESS_FS_EXECUTE;
10851 path_beneath.parent_fd =
10852 open(dir_s1d2, O_PATH | O_DIRECTORY | O_CLOEXEC);
10853 if (path_beneath.parent_fd < 0)
10854 _exit(1);
10855 if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
10856 &path_beneath, 0))
10857 _exit(1);
10858 close(path_beneath.parent_fd);
10859
10860 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
10861 if (landlock_restrict_self(ruleset_fd, 0))
10862 _exit(1);
10863 close(ruleset_fd);
10864
10865 fd = open(dir_s1d2, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
10866 if (fd < 0)
10867 _exit(1);
10868 close(fd);
10869 _exit(0);
10870 }
10871
10872 ASSERT_EQ(pid, waitpid(pid, &status, 0));
10873 ASSERT_TRUE(WIFEXITED(status));
10874 EXPECT_EQ(0, WEXITSTATUS(status));
10875
10876 set_cap(_metadata, CAP_DAC_OVERRIDE);
10877 buf = tracefs_read_trace();
10878 clear_cap(_metadata, CAP_DAC_OVERRIDE);
10879 ASSERT_NE(NULL, buf);
10880
10881 /*
10882 * dir_s1d2 (grants nothing requested) then dir_s1d1 (grants read_dir).
10883 */
10884 count = tracefs_count_matches(buf, REGEX_CHECK_RULE_FS(TRACE_TASK));
10885 EXPECT_EQ(2, count)
10886 {
10887 TH_LOG("Expected 2 check_rule_inode events, got %d\n%s", count,
10888 buf);
10889 }
10890
10891 /* The dir_s1d2 rule matches the inode but grants none of read_dir. */
10892 EXPECT_EQ(
10893 1,
10894 tracefs_count_matches(
10895 buf,
10896 TRACE_PREFIX(
10897 TRACE_TASK) "landlock_check_rule_inode: domain=[0-9a-f]\\+ "
10898 "access_request=read_dir "
10899 "dev=[0-9]\\+:[0-9]\\+ ino=[0-9]\\+ "
10900 "grants={}$"))
10901 {
10902 TH_LOG("Expected a grants={} event\n%s", buf);
10903 }
10904
10905 /* Walking up to dir_s1d1 grants the requested read_dir. */
10906 EXPECT_EQ(
10907 1,
10908 tracefs_count_matches(
10909 buf,
10910 TRACE_PREFIX(
10911 TRACE_TASK) "landlock_check_rule_inode: domain=[0-9a-f]\\+ "
10912 "access_request=read_dir "
10913 "dev=[0-9]\\+:[0-9]\\+ ino=[0-9]\\+ "
10914 "grants={read_dir}$"))
10915 {
10916 TH_LOG("Expected a grants={read_dir} event\n%s", buf);
10917 }
10918
10919 free(buf);
10920 }
10921
10922 TEST_HARNESS_MAIN
10923