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