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