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 /*
1836 * This test checks that a rule on a directory used as a mount point does not
1837 * grant access to the mount covering it. It is a generalization of the bind
1838 * mount case in layout3_fs.hostfs.release_inodes that tests hidden mount points.
1839 */
TEST_F_FORK(layout1,covered_rule)1840 TEST_F_FORK(layout1, covered_rule)
1841 {
1842 const struct rule layer1[] = {
1843 {
1844 .path = dir_s3d2,
1845 .access = LANDLOCK_ACCESS_FS_READ_DIR,
1846 },
1847 {},
1848 };
1849 int ruleset_fd;
1850
1851 /* Unmount to simplify FIXTURE_TEARDOWN. */
1852 set_cap(_metadata, CAP_SYS_ADMIN);
1853 ASSERT_EQ(0, umount(dir_s3d2));
1854 clear_cap(_metadata, CAP_SYS_ADMIN);
1855
1856 /* Creates a ruleset with the future hidden directory. */
1857 ruleset_fd =
1858 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
1859 ASSERT_LE(0, ruleset_fd);
1860
1861 /* Covers with a new mount point. */
1862 set_cap(_metadata, CAP_SYS_ADMIN);
1863 ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));
1864 clear_cap(_metadata, CAP_SYS_ADMIN);
1865
1866 ASSERT_EQ(0, test_open(dir_s3d2, O_RDONLY));
1867
1868 enforce_ruleset(_metadata, ruleset_fd);
1869 ASSERT_EQ(0, close(ruleset_fd));
1870
1871 /* Checks that access to the new mount point is denied. */
1872 ASSERT_EQ(EACCES, test_open(dir_s3d2, O_RDONLY));
1873 }
1874
1875 enum relative_access {
1876 REL_OPEN,
1877 REL_CHDIR,
1878 REL_CHROOT_ONLY,
1879 REL_CHROOT_CHDIR,
1880 };
1881
test_relative_path(struct __test_metadata * const _metadata,const enum relative_access rel)1882 static void test_relative_path(struct __test_metadata *const _metadata,
1883 const enum relative_access rel)
1884 {
1885 /*
1886 * Common layer to check that chroot doesn't ignore it (i.e. a chroot
1887 * is not a disconnected root directory).
1888 */
1889 const struct rule layer1_base[] = {
1890 {
1891 .path = TMP_DIR,
1892 .access = ACCESS_RO,
1893 },
1894 {},
1895 };
1896 const struct rule layer2_subs[] = {
1897 {
1898 .path = dir_s1d2,
1899 .access = ACCESS_RO,
1900 },
1901 {
1902 .path = dir_s2d2,
1903 .access = ACCESS_RO,
1904 },
1905 {},
1906 };
1907 int dirfd, ruleset_fd;
1908
1909 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
1910 ASSERT_LE(0, ruleset_fd);
1911 enforce_ruleset(_metadata, ruleset_fd);
1912 ASSERT_EQ(0, close(ruleset_fd));
1913
1914 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_subs);
1915
1916 ASSERT_LE(0, ruleset_fd);
1917 switch (rel) {
1918 case REL_OPEN:
1919 case REL_CHDIR:
1920 break;
1921 case REL_CHROOT_ONLY:
1922 ASSERT_EQ(0, chdir(dir_s2d2));
1923 break;
1924 case REL_CHROOT_CHDIR:
1925 ASSERT_EQ(0, chdir(dir_s1d2));
1926 break;
1927 default:
1928 ASSERT_TRUE(false);
1929 return;
1930 }
1931
1932 set_cap(_metadata, CAP_SYS_CHROOT);
1933 enforce_ruleset(_metadata, ruleset_fd);
1934
1935 switch (rel) {
1936 case REL_OPEN:
1937 dirfd = open(dir_s1d2, O_DIRECTORY);
1938 ASSERT_LE(0, dirfd);
1939 break;
1940 case REL_CHDIR:
1941 ASSERT_EQ(0, chdir(dir_s1d2));
1942 dirfd = AT_FDCWD;
1943 break;
1944 case REL_CHROOT_ONLY:
1945 /* Do chroot into dir_s1d2 (relative to dir_s2d2). */
1946 ASSERT_EQ(0, chroot("../../s1d1/s1d2"))
1947 {
1948 TH_LOG("Failed to chroot: %s", strerror(errno));
1949 }
1950 dirfd = AT_FDCWD;
1951 break;
1952 case REL_CHROOT_CHDIR:
1953 /* Do chroot into dir_s1d2. */
1954 ASSERT_EQ(0, chroot("."))
1955 {
1956 TH_LOG("Failed to chroot: %s", strerror(errno));
1957 }
1958 dirfd = AT_FDCWD;
1959 break;
1960 }
1961
1962 ASSERT_EQ((rel == REL_CHROOT_CHDIR) ? 0 : EACCES,
1963 test_open_rel(dirfd, "..", O_RDONLY));
1964 ASSERT_EQ(0, test_open_rel(dirfd, ".", O_RDONLY));
1965
1966 if (rel == REL_CHROOT_ONLY) {
1967 /* The current directory is dir_s2d2. */
1968 ASSERT_EQ(0, test_open_rel(dirfd, "./s2d3", O_RDONLY));
1969 } else {
1970 /* The current directory is dir_s1d2. */
1971 ASSERT_EQ(0, test_open_rel(dirfd, "./s1d3", O_RDONLY));
1972 }
1973
1974 if (rel == REL_CHROOT_ONLY || rel == REL_CHROOT_CHDIR) {
1975 /* Checks the root dir_s1d2. */
1976 ASSERT_EQ(0, test_open_rel(dirfd, "/..", O_RDONLY));
1977 ASSERT_EQ(0, test_open_rel(dirfd, "/", O_RDONLY));
1978 ASSERT_EQ(0, test_open_rel(dirfd, "/f1", O_RDONLY));
1979 ASSERT_EQ(0, test_open_rel(dirfd, "/s1d3", O_RDONLY));
1980 }
1981
1982 if (rel != REL_CHROOT_CHDIR) {
1983 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s1d1", O_RDONLY));
1984 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2", O_RDONLY));
1985 ASSERT_EQ(0, test_open_rel(dirfd, "../../s1d1/s1d2/s1d3",
1986 O_RDONLY));
1987
1988 ASSERT_EQ(EACCES, test_open_rel(dirfd, "../../s2d1", O_RDONLY));
1989 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2", O_RDONLY));
1990 ASSERT_EQ(0, test_open_rel(dirfd, "../../s2d1/s2d2/s2d3",
1991 O_RDONLY));
1992 }
1993
1994 if (rel == REL_OPEN)
1995 ASSERT_EQ(0, close(dirfd));
1996 ASSERT_EQ(0, close(ruleset_fd));
1997 }
1998
TEST_F_FORK(layout1,relative_open)1999 TEST_F_FORK(layout1, relative_open)
2000 {
2001 test_relative_path(_metadata, REL_OPEN);
2002 }
2003
TEST_F_FORK(layout1,relative_chdir)2004 TEST_F_FORK(layout1, relative_chdir)
2005 {
2006 test_relative_path(_metadata, REL_CHDIR);
2007 }
2008
TEST_F_FORK(layout1,relative_chroot_only)2009 TEST_F_FORK(layout1, relative_chroot_only)
2010 {
2011 test_relative_path(_metadata, REL_CHROOT_ONLY);
2012 }
2013
TEST_F_FORK(layout1,relative_chroot_chdir)2014 TEST_F_FORK(layout1, relative_chroot_chdir)
2015 {
2016 test_relative_path(_metadata, REL_CHROOT_CHDIR);
2017 }
2018
copy_file(struct __test_metadata * const _metadata,const char * const src_path,const char * const dst_path)2019 static void copy_file(struct __test_metadata *const _metadata,
2020 const char *const src_path, const char *const dst_path)
2021 {
2022 int dst_fd, src_fd;
2023 struct stat statbuf;
2024
2025 dst_fd = open(dst_path, O_WRONLY | O_TRUNC | O_CLOEXEC);
2026 ASSERT_LE(0, dst_fd)
2027 {
2028 TH_LOG("Failed to open \"%s\": %s", dst_path, strerror(errno));
2029 }
2030 src_fd = open(src_path, O_RDONLY | O_CLOEXEC);
2031 ASSERT_LE(0, src_fd)
2032 {
2033 TH_LOG("Failed to open \"%s\": %s", src_path, strerror(errno));
2034 }
2035 ASSERT_EQ(0, fstat(src_fd, &statbuf));
2036 ASSERT_EQ(statbuf.st_size,
2037 sendfile(dst_fd, src_fd, 0, statbuf.st_size));
2038 ASSERT_EQ(0, close(src_fd));
2039 ASSERT_EQ(0, close(dst_fd));
2040 }
2041
test_execute(struct __test_metadata * const _metadata,const int err,const char * const path)2042 static void test_execute(struct __test_metadata *const _metadata, const int err,
2043 const char *const path)
2044 {
2045 int status;
2046 char *const argv[] = { (char *)path, NULL };
2047 const pid_t child = fork();
2048
2049 ASSERT_LE(0, child);
2050 if (child == 0) {
2051 ASSERT_EQ(err ? -1 : 0, execve(path, argv, NULL))
2052 {
2053 TH_LOG("Failed to execute \"%s\": %s", path,
2054 strerror(errno));
2055 };
2056 ASSERT_EQ(err, errno);
2057 _exit(__test_passed(_metadata) ? 2 : 1);
2058 return;
2059 }
2060 ASSERT_EQ(child, waitpid(child, &status, 0));
2061 ASSERT_EQ(1, WIFEXITED(status));
2062 ASSERT_EQ(err ? 2 : 0, WEXITSTATUS(status))
2063 {
2064 TH_LOG("Unexpected return code for \"%s\"", path);
2065 };
2066 }
2067
test_check_exec(struct __test_metadata * const _metadata,const int err,const char * const path)2068 static void test_check_exec(struct __test_metadata *const _metadata,
2069 const int err, const char *const path)
2070 {
2071 int ret;
2072 char *const argv[] = { (char *)path, NULL };
2073
2074 ret = sys_execveat(AT_FDCWD, path, argv, NULL,
2075 AT_EMPTY_PATH | AT_EXECVE_CHECK);
2076 if (err) {
2077 EXPECT_EQ(-1, ret);
2078 EXPECT_EQ(errno, err);
2079 } else {
2080 EXPECT_EQ(0, ret);
2081 }
2082 }
2083
TEST_F_FORK(layout1,execute)2084 TEST_F_FORK(layout1, execute)
2085 {
2086 const struct rule rules[] = {
2087 {
2088 .path = dir_s1d2,
2089 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2090 },
2091 {},
2092 };
2093 const int ruleset_fd =
2094 create_ruleset(_metadata, rules[0].access, rules);
2095
2096 ASSERT_LE(0, ruleset_fd);
2097 copy_file(_metadata, bin_true, file1_s1d1);
2098 copy_file(_metadata, bin_true, file1_s1d2);
2099 copy_file(_metadata, bin_true, file1_s1d3);
2100
2101 /* Checks before file1_s1d1 being denied. */
2102 test_execute(_metadata, 0, file1_s1d1);
2103 test_check_exec(_metadata, 0, file1_s1d1);
2104
2105 enforce_ruleset(_metadata, ruleset_fd);
2106 ASSERT_EQ(0, close(ruleset_fd));
2107
2108 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
2109 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
2110 test_execute(_metadata, EACCES, file1_s1d1);
2111 test_check_exec(_metadata, EACCES, file1_s1d1);
2112
2113 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
2114 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
2115 test_execute(_metadata, 0, file1_s1d2);
2116 test_check_exec(_metadata, 0, file1_s1d2);
2117
2118 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
2119 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
2120 test_execute(_metadata, 0, file1_s1d3);
2121 test_check_exec(_metadata, 0, file1_s1d3);
2122 }
2123
TEST_F_FORK(layout1,umount_sandboxer)2124 TEST_F_FORK(layout1, umount_sandboxer)
2125 {
2126 int pipe_child[2], pipe_parent[2];
2127 char buf_parent;
2128 pid_t child;
2129 int status;
2130
2131 copy_file(_metadata, bin_sandbox_and_launch, file1_s3d3);
2132 ASSERT_EQ(0, pipe2(pipe_child, 0));
2133 ASSERT_EQ(0, pipe2(pipe_parent, 0));
2134
2135 child = fork();
2136 ASSERT_LE(0, child);
2137 if (child == 0) {
2138 char pipe_child_str[12], pipe_parent_str[12];
2139 char *const argv[] = { (char *)file1_s3d3,
2140 (char *)bin_wait_pipe, pipe_child_str,
2141 pipe_parent_str, NULL };
2142
2143 /* Passes the pipe FDs to the executed binary and its child. */
2144 EXPECT_EQ(0, close(pipe_child[0]));
2145 EXPECT_EQ(0, close(pipe_parent[1]));
2146 snprintf(pipe_child_str, sizeof(pipe_child_str), "%d",
2147 pipe_child[1]);
2148 snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d",
2149 pipe_parent[0]);
2150
2151 /*
2152 * We need bin_sandbox_and_launch (copied inside the mount as
2153 * file1_s3d3) to execute bin_wait_pipe (outside the mount) to
2154 * make sure the mount point will not be EBUSY because of
2155 * file1_s3d3 being in use. This avoids a potential race
2156 * condition between the following read() and umount() calls.
2157 */
2158 ASSERT_EQ(0, execve(argv[0], argv, NULL))
2159 {
2160 TH_LOG("Failed to execute \"%s\": %s", argv[0],
2161 strerror(errno));
2162 };
2163 _exit(1);
2164 return;
2165 }
2166
2167 EXPECT_EQ(0, close(pipe_child[1]));
2168 EXPECT_EQ(0, close(pipe_parent[0]));
2169
2170 /* Waits for the child to sandbox itself. */
2171 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1));
2172
2173 /* Tests that the sandboxer is tied to its mount point. */
2174 set_cap(_metadata, CAP_SYS_ADMIN);
2175 EXPECT_EQ(-1, umount(dir_s3d2));
2176 EXPECT_EQ(EBUSY, errno);
2177 clear_cap(_metadata, CAP_SYS_ADMIN);
2178
2179 /* Signals the child to launch a grandchild. */
2180 EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
2181
2182 /* Waits for the grandchild. */
2183 EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1));
2184
2185 /* Tests that the domain's sandboxer is not tied to its mount point. */
2186 set_cap(_metadata, CAP_SYS_ADMIN);
2187 EXPECT_EQ(0, umount(dir_s3d2))
2188 {
2189 TH_LOG("Failed to umount \"%s\": %s", dir_s3d2,
2190 strerror(errno));
2191 };
2192 clear_cap(_metadata, CAP_SYS_ADMIN);
2193
2194 /* Signals the grandchild to terminate. */
2195 EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
2196 ASSERT_EQ(child, waitpid(child, &status, 0));
2197 ASSERT_EQ(1, WIFEXITED(status));
2198 ASSERT_EQ(0, WEXITSTATUS(status));
2199 }
2200
TEST_F_FORK(layout1,link)2201 TEST_F_FORK(layout1, link)
2202 {
2203 const struct rule layer1[] = {
2204 {
2205 .path = dir_s1d2,
2206 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2207 },
2208 {},
2209 };
2210 const struct rule layer2[] = {
2211 {
2212 .path = dir_s1d3,
2213 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2214 },
2215 {},
2216 };
2217 int ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
2218
2219 ASSERT_LE(0, ruleset_fd);
2220
2221 ASSERT_EQ(0, unlink(file1_s1d1));
2222 ASSERT_EQ(0, unlink(file1_s1d2));
2223 ASSERT_EQ(0, unlink(file1_s1d3));
2224
2225 enforce_ruleset(_metadata, ruleset_fd);
2226 ASSERT_EQ(0, close(ruleset_fd));
2227
2228 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2229 ASSERT_EQ(EACCES, errno);
2230
2231 /* Denies linking because of reparenting. */
2232 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2233 ASSERT_EQ(EXDEV, errno);
2234 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2235 ASSERT_EQ(EXDEV, errno);
2236 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2237 ASSERT_EQ(EXDEV, errno);
2238
2239 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2240 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2241
2242 /* Prepares for next unlinks. */
2243 ASSERT_EQ(0, unlink(file2_s1d2));
2244 ASSERT_EQ(0, unlink(file2_s1d3));
2245
2246 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
2247 ASSERT_LE(0, ruleset_fd);
2248 enforce_ruleset(_metadata, ruleset_fd);
2249 ASSERT_EQ(0, close(ruleset_fd));
2250
2251 /* Checks that linkind doesn't require the ability to delete a file. */
2252 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
2253 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
2254 }
2255
test_rename(const char * const oldpath,const char * const newpath)2256 static int test_rename(const char *const oldpath, const char *const newpath)
2257 {
2258 if (rename(oldpath, newpath))
2259 return errno;
2260 return 0;
2261 }
2262
test_exchange(const char * const oldpath,const char * const newpath)2263 static int test_exchange(const char *const oldpath, const char *const newpath)
2264 {
2265 if (renameat2(AT_FDCWD, oldpath, AT_FDCWD, newpath, RENAME_EXCHANGE))
2266 return errno;
2267 return 0;
2268 }
2269
TEST_F_FORK(layout1,rename_file)2270 TEST_F_FORK(layout1, rename_file)
2271 {
2272 const struct rule rules[] = {
2273 {
2274 .path = dir_s1d3,
2275 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2276 },
2277 {
2278 .path = dir_s2d2,
2279 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
2280 },
2281 {},
2282 };
2283 const int ruleset_fd =
2284 create_ruleset(_metadata, rules[0].access, rules);
2285
2286 ASSERT_LE(0, ruleset_fd);
2287
2288 ASSERT_EQ(0, unlink(file1_s1d2));
2289
2290 enforce_ruleset(_metadata, ruleset_fd);
2291 ASSERT_EQ(0, close(ruleset_fd));
2292
2293 /*
2294 * Tries to replace a file, from a directory that allows file removal,
2295 * but to a different directory (which also allows file removal).
2296 */
2297 ASSERT_EQ(-1, rename(file1_s2d3, file1_s1d3));
2298 ASSERT_EQ(EXDEV, errno);
2299 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d3,
2300 RENAME_EXCHANGE));
2301 ASSERT_EQ(EXDEV, errno);
2302 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2303 RENAME_EXCHANGE));
2304 ASSERT_EQ(EXDEV, errno);
2305
2306 /*
2307 * Tries to replace a file, from a directory that denies file removal,
2308 * to a different directory (which allows file removal).
2309 */
2310 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2311 ASSERT_EQ(EACCES, errno);
2312 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file1_s1d3,
2313 RENAME_EXCHANGE));
2314 ASSERT_EQ(EACCES, errno);
2315 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s1d3,
2316 RENAME_EXCHANGE));
2317 ASSERT_EQ(EXDEV, errno);
2318
2319 /* Exchanges files and directories that partially allow removal. */
2320 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d2, AT_FDCWD, file1_s2d1,
2321 RENAME_EXCHANGE));
2322 ASSERT_EQ(EACCES, errno);
2323 /* Checks that file1_s2d1 cannot be removed (instead of ENOTDIR). */
2324 ASSERT_EQ(-1, rename(dir_s2d2, file1_s2d1));
2325 ASSERT_EQ(EACCES, errno);
2326 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, dir_s2d2,
2327 RENAME_EXCHANGE));
2328 ASSERT_EQ(EACCES, errno);
2329 /* Checks that file1_s1d1 cannot be removed (instead of EISDIR). */
2330 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
2331 ASSERT_EQ(EACCES, errno);
2332
2333 /* Renames files with different parents. */
2334 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2335 ASSERT_EQ(EXDEV, errno);
2336 ASSERT_EQ(0, unlink(file1_s1d3));
2337 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2338 ASSERT_EQ(EACCES, errno);
2339
2340 /* Exchanges and renames files with same parent. */
2341 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s2d3,
2342 RENAME_EXCHANGE));
2343 ASSERT_EQ(0, rename(file2_s2d3, file1_s2d3));
2344
2345 /* Exchanges files and directories with same parent, twice. */
2346 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2347 RENAME_EXCHANGE));
2348 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s2d3,
2349 RENAME_EXCHANGE));
2350 }
2351
TEST_F_FORK(layout1,rename_dir)2352 TEST_F_FORK(layout1, rename_dir)
2353 {
2354 const struct rule rules[] = {
2355 {
2356 .path = dir_s1d2,
2357 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2358 },
2359 {
2360 .path = dir_s2d1,
2361 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
2362 },
2363 {},
2364 };
2365 const int ruleset_fd =
2366 create_ruleset(_metadata, rules[0].access, rules);
2367
2368 ASSERT_LE(0, ruleset_fd);
2369
2370 /* Empties dir_s1d3 to allow renaming. */
2371 ASSERT_EQ(0, unlink(file1_s1d3));
2372 ASSERT_EQ(0, unlink(file2_s1d3));
2373
2374 enforce_ruleset(_metadata, ruleset_fd);
2375 ASSERT_EQ(0, close(ruleset_fd));
2376
2377 /* Exchanges and renames directory to a different parent. */
2378 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
2379 RENAME_EXCHANGE));
2380 ASSERT_EQ(EXDEV, errno);
2381 ASSERT_EQ(-1, rename(dir_s2d3, dir_s1d3));
2382 ASSERT_EQ(EXDEV, errno);
2383 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
2384 RENAME_EXCHANGE));
2385 ASSERT_EQ(EXDEV, errno);
2386
2387 /*
2388 * Exchanges directory to the same parent, which doesn't allow
2389 * directory removal.
2390 */
2391 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d1, AT_FDCWD, dir_s2d1,
2392 RENAME_EXCHANGE));
2393 ASSERT_EQ(EACCES, errno);
2394 /* Checks that dir_s1d2 cannot be removed (instead of ENOTDIR). */
2395 ASSERT_EQ(-1, rename(dir_s1d2, file1_s1d1));
2396 ASSERT_EQ(EACCES, errno);
2397 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s1d2,
2398 RENAME_EXCHANGE));
2399 ASSERT_EQ(EACCES, errno);
2400 /* Checks that dir_s1d2 cannot be removed (instead of EISDIR). */
2401 ASSERT_EQ(-1, rename(file1_s1d1, dir_s1d2));
2402 ASSERT_EQ(EACCES, errno);
2403
2404 /*
2405 * Exchanges and renames directory to the same parent, which allows
2406 * directory removal.
2407 */
2408 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s1d2,
2409 RENAME_EXCHANGE));
2410 ASSERT_EQ(0, unlink(dir_s1d3));
2411 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
2412 ASSERT_EQ(0, rename(file1_s1d2, dir_s1d3));
2413 ASSERT_EQ(0, rmdir(dir_s1d3));
2414 }
2415
TEST_F_FORK(layout1,reparent_refer)2416 TEST_F_FORK(layout1, reparent_refer)
2417 {
2418 const struct rule layer1[] = {
2419 {
2420 .path = dir_s1d2,
2421 .access = LANDLOCK_ACCESS_FS_REFER,
2422 },
2423 {
2424 .path = dir_s2d2,
2425 .access = LANDLOCK_ACCESS_FS_REFER,
2426 },
2427 {},
2428 };
2429 int ruleset_fd =
2430 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
2431
2432 ASSERT_LE(0, ruleset_fd);
2433 enforce_ruleset(_metadata, ruleset_fd);
2434 ASSERT_EQ(0, close(ruleset_fd));
2435
2436 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d1));
2437 ASSERT_EQ(EXDEV, errno);
2438 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d2));
2439 ASSERT_EQ(EXDEV, errno);
2440 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
2441 ASSERT_EQ(EXDEV, errno);
2442
2443 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d1));
2444 ASSERT_EQ(EXDEV, errno);
2445 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d2));
2446 ASSERT_EQ(EXDEV, errno);
2447 /*
2448 * Moving should only be allowed when the source and the destination
2449 * parent directory have REFER.
2450 */
2451 ASSERT_EQ(-1, rename(dir_s1d3, dir_s2d3));
2452 ASSERT_EQ(ENOTEMPTY, errno);
2453 ASSERT_EQ(0, unlink(file1_s2d3));
2454 ASSERT_EQ(0, unlink(file2_s2d3));
2455 ASSERT_EQ(0, rename(dir_s1d3, dir_s2d3));
2456 }
2457
2458 /* 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[])2459 static void refer_denied_by_default(struct __test_metadata *const _metadata,
2460 const struct rule layer1[],
2461 const int layer1_err,
2462 const struct rule layer2[])
2463 {
2464 int ruleset_fd;
2465
2466 ASSERT_EQ(0, unlink(file1_s1d2));
2467
2468 ruleset_fd = create_ruleset(_metadata, layer1[0].access, layer1);
2469 ASSERT_LE(0, ruleset_fd);
2470 enforce_ruleset(_metadata, ruleset_fd);
2471 ASSERT_EQ(0, close(ruleset_fd));
2472
2473 /*
2474 * If the first layer handles LANDLOCK_ACCESS_FS_REFER (according to
2475 * layer1_err), then it allows some different-parent renames and links.
2476 */
2477 ASSERT_EQ(layer1_err, test_rename(file1_s1d1, file1_s1d2));
2478 if (layer1_err == 0)
2479 ASSERT_EQ(layer1_err, test_rename(file1_s1d2, file1_s1d1));
2480 ASSERT_EQ(layer1_err, test_exchange(file2_s1d1, file2_s1d2));
2481 ASSERT_EQ(layer1_err, test_exchange(file2_s1d2, file2_s1d1));
2482
2483 ruleset_fd = create_ruleset(_metadata, layer2[0].access, layer2);
2484 ASSERT_LE(0, ruleset_fd);
2485 enforce_ruleset(_metadata, ruleset_fd);
2486 ASSERT_EQ(0, close(ruleset_fd));
2487
2488 /*
2489 * Now, either the first or the second layer does not handle
2490 * LANDLOCK_ACCESS_FS_REFER, which means that any different-parent
2491 * renames and links are denied, thus making the layer handling
2492 * LANDLOCK_ACCESS_FS_REFER null and void.
2493 */
2494 ASSERT_EQ(EXDEV, test_rename(file1_s1d1, file1_s1d2));
2495 ASSERT_EQ(EXDEV, test_exchange(file2_s1d1, file2_s1d2));
2496 ASSERT_EQ(EXDEV, test_exchange(file2_s1d2, file2_s1d1));
2497 }
2498
2499 const struct rule layer_dir_s1d1_refer[] = {
2500 {
2501 .path = dir_s1d1,
2502 .access = LANDLOCK_ACCESS_FS_REFER,
2503 },
2504 {},
2505 };
2506
2507 const struct rule layer_dir_s1d1_execute[] = {
2508 {
2509 /* Matches a parent directory. */
2510 .path = dir_s1d1,
2511 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2512 },
2513 {},
2514 };
2515
2516 const struct rule layer_dir_s2d1_execute[] = {
2517 {
2518 /* Does not match a parent directory. */
2519 .path = dir_s2d1,
2520 .access = LANDLOCK_ACCESS_FS_EXECUTE,
2521 },
2522 {},
2523 };
2524
2525 /*
2526 * Tests precedence over renames: denied by default for different parent
2527 * directories, *with* a rule matching a parent directory, but not directly
2528 * denying access (with MAKE_REG nor REMOVE).
2529 */
TEST_F_FORK(layout1,refer_denied_by_default1)2530 TEST_F_FORK(layout1, refer_denied_by_default1)
2531 {
2532 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
2533 layer_dir_s1d1_execute);
2534 }
2535
2536 /*
2537 * Same test but this time turning around the ABI version order: the first
2538 * layer does not handle LANDLOCK_ACCESS_FS_REFER.
2539 */
TEST_F_FORK(layout1,refer_denied_by_default2)2540 TEST_F_FORK(layout1, refer_denied_by_default2)
2541 {
2542 refer_denied_by_default(_metadata, layer_dir_s1d1_execute, EXDEV,
2543 layer_dir_s1d1_refer);
2544 }
2545
2546 /*
2547 * Tests precedence over renames: denied by default for different parent
2548 * directories, *without* a rule matching a parent directory, but not directly
2549 * denying access (with MAKE_REG nor REMOVE).
2550 */
TEST_F_FORK(layout1,refer_denied_by_default3)2551 TEST_F_FORK(layout1, refer_denied_by_default3)
2552 {
2553 refer_denied_by_default(_metadata, layer_dir_s1d1_refer, 0,
2554 layer_dir_s2d1_execute);
2555 }
2556
2557 /*
2558 * Same test but this time turning around the ABI version order: the first
2559 * layer does not handle LANDLOCK_ACCESS_FS_REFER.
2560 */
TEST_F_FORK(layout1,refer_denied_by_default4)2561 TEST_F_FORK(layout1, refer_denied_by_default4)
2562 {
2563 refer_denied_by_default(_metadata, layer_dir_s2d1_execute, EXDEV,
2564 layer_dir_s1d1_refer);
2565 }
2566
2567 /*
2568 * Tests walking through a denied root mount.
2569 */
TEST_F_FORK(layout1,refer_mount_root_deny)2570 TEST_F_FORK(layout1, refer_mount_root_deny)
2571 {
2572 const struct landlock_ruleset_attr ruleset_attr = {
2573 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_DIR,
2574 };
2575 int root_fd, ruleset_fd;
2576
2577 /* Creates a mount object from a non-mount point. */
2578 set_cap(_metadata, CAP_SYS_ADMIN);
2579 root_fd =
2580 open_tree(AT_FDCWD, dir_s1d1,
2581 AT_EMPTY_PATH | OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
2582 clear_cap(_metadata, CAP_SYS_ADMIN);
2583 ASSERT_LE(0, root_fd);
2584
2585 ruleset_fd =
2586 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
2587 ASSERT_LE(0, ruleset_fd);
2588
2589 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
2590 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
2591 EXPECT_EQ(0, close(ruleset_fd));
2592
2593 /* Link denied by Landlock: EACCES. */
2594 EXPECT_EQ(-1, linkat(root_fd, ".", root_fd, "does_not_exist", 0));
2595 EXPECT_EQ(EACCES, errno);
2596
2597 /* renameat2() always returns EBUSY. */
2598 EXPECT_EQ(-1, renameat2(root_fd, ".", root_fd, "does_not_exist", 0));
2599 EXPECT_EQ(EBUSY, errno);
2600
2601 EXPECT_EQ(0, close(root_fd));
2602 }
2603
TEST_F_FORK(layout1,refer_part_mount_tree_is_allowed)2604 TEST_F_FORK(layout1, refer_part_mount_tree_is_allowed)
2605 {
2606 const struct rule layer1[] = {
2607 {
2608 /* Parent mount point. */
2609 .path = dir_s3d1,
2610 .access = LANDLOCK_ACCESS_FS_REFER |
2611 LANDLOCK_ACCESS_FS_MAKE_REG,
2612 },
2613 {
2614 /*
2615 * Removing the source file is allowed because its
2616 * access rights are already a superset of the
2617 * destination.
2618 */
2619 .path = dir_s3d4,
2620 .access = LANDLOCK_ACCESS_FS_REFER |
2621 LANDLOCK_ACCESS_FS_MAKE_REG |
2622 LANDLOCK_ACCESS_FS_REMOVE_FILE,
2623 },
2624 {},
2625 };
2626 int ruleset_fd;
2627
2628 ASSERT_EQ(0, unlink(file1_s3d3));
2629 ruleset_fd = create_ruleset(_metadata,
2630 LANDLOCK_ACCESS_FS_REFER |
2631 LANDLOCK_ACCESS_FS_MAKE_REG |
2632 LANDLOCK_ACCESS_FS_REMOVE_FILE,
2633 layer1);
2634
2635 ASSERT_LE(0, ruleset_fd);
2636 enforce_ruleset(_metadata, ruleset_fd);
2637 ASSERT_EQ(0, close(ruleset_fd));
2638
2639 ASSERT_EQ(0, rename(file1_s3d4, file1_s3d3));
2640 }
2641
TEST_F_FORK(layout1,reparent_link)2642 TEST_F_FORK(layout1, reparent_link)
2643 {
2644 const struct rule layer1[] = {
2645 {
2646 .path = dir_s1d2,
2647 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2648 },
2649 {
2650 .path = dir_s1d3,
2651 .access = LANDLOCK_ACCESS_FS_REFER,
2652 },
2653 {
2654 .path = dir_s2d2,
2655 .access = LANDLOCK_ACCESS_FS_REFER,
2656 },
2657 {
2658 .path = dir_s2d3,
2659 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2660 },
2661 {},
2662 };
2663 const int ruleset_fd = create_ruleset(
2664 _metadata,
2665 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2666
2667 ASSERT_LE(0, ruleset_fd);
2668 enforce_ruleset(_metadata, ruleset_fd);
2669 ASSERT_EQ(0, close(ruleset_fd));
2670
2671 ASSERT_EQ(0, unlink(file1_s1d1));
2672 ASSERT_EQ(0, unlink(file1_s1d2));
2673 ASSERT_EQ(0, unlink(file1_s1d3));
2674
2675 /* Denies linking because of missing MAKE_REG. */
2676 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
2677 ASSERT_EQ(EACCES, errno);
2678 /* Denies linking because of missing source and destination REFER. */
2679 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d2));
2680 ASSERT_EQ(EXDEV, errno);
2681 /* Denies linking because of missing source REFER. */
2682 ASSERT_EQ(-1, link(file1_s2d1, file1_s1d3));
2683 ASSERT_EQ(EXDEV, errno);
2684
2685 /* Denies linking because of missing MAKE_REG. */
2686 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d1));
2687 ASSERT_EQ(EACCES, errno);
2688 /* Denies linking because of missing destination REFER. */
2689 ASSERT_EQ(-1, link(file1_s2d2, file1_s1d2));
2690 ASSERT_EQ(EXDEV, errno);
2691
2692 /* Allows linking because of REFER and MAKE_REG. */
2693 ASSERT_EQ(0, link(file1_s2d2, file1_s1d3));
2694 ASSERT_EQ(0, unlink(file1_s2d2));
2695 /* Reverse linking denied because of missing MAKE_REG. */
2696 ASSERT_EQ(-1, link(file1_s1d3, file1_s2d2));
2697 ASSERT_EQ(EACCES, errno);
2698 ASSERT_EQ(0, unlink(file1_s2d3));
2699 /* Checks reverse linking. */
2700 ASSERT_EQ(0, link(file1_s1d3, file1_s2d3));
2701 ASSERT_EQ(0, unlink(file1_s1d3));
2702
2703 /*
2704 * This is OK for a file link, but it should not be allowed for a
2705 * directory rename (because of the superset of access rights.
2706 */
2707 ASSERT_EQ(0, link(file1_s2d3, file1_s1d3));
2708 ASSERT_EQ(0, unlink(file1_s1d3));
2709
2710 ASSERT_EQ(-1, link(file2_s1d2, file1_s1d3));
2711 ASSERT_EQ(EXDEV, errno);
2712 ASSERT_EQ(-1, link(file2_s1d3, file1_s1d2));
2713 ASSERT_EQ(EXDEV, errno);
2714
2715 ASSERT_EQ(0, link(file2_s1d2, file1_s1d2));
2716 ASSERT_EQ(0, link(file2_s1d3, file1_s1d3));
2717 }
2718
TEST_F_FORK(layout1,reparent_rename)2719 TEST_F_FORK(layout1, reparent_rename)
2720 {
2721 /* Same rules as for reparent_link. */
2722 const struct rule layer1[] = {
2723 {
2724 .path = dir_s1d2,
2725 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2726 },
2727 {
2728 .path = dir_s1d3,
2729 .access = LANDLOCK_ACCESS_FS_REFER,
2730 },
2731 {
2732 .path = dir_s2d2,
2733 .access = LANDLOCK_ACCESS_FS_REFER,
2734 },
2735 {
2736 .path = dir_s2d3,
2737 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2738 },
2739 {},
2740 };
2741 const int ruleset_fd = create_ruleset(
2742 _metadata,
2743 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2744
2745 ASSERT_LE(0, ruleset_fd);
2746 enforce_ruleset(_metadata, ruleset_fd);
2747 ASSERT_EQ(0, close(ruleset_fd));
2748
2749 ASSERT_EQ(0, unlink(file1_s1d2));
2750 ASSERT_EQ(0, unlink(file1_s1d3));
2751
2752 /* Denies renaming because of missing MAKE_REG. */
2753 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s1d1,
2754 RENAME_EXCHANGE));
2755 ASSERT_EQ(EACCES, errno);
2756 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file2_s1d1,
2757 RENAME_EXCHANGE));
2758 ASSERT_EQ(EACCES, errno);
2759 ASSERT_EQ(0, unlink(file1_s1d1));
2760 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
2761 ASSERT_EQ(EACCES, errno);
2762 /* Even denies same file exchange. */
2763 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file2_s1d1,
2764 RENAME_EXCHANGE));
2765 ASSERT_EQ(EACCES, errno);
2766
2767 /* Denies renaming because of missing source and destination REFER. */
2768 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d2));
2769 ASSERT_EQ(EXDEV, errno);
2770 /*
2771 * Denies renaming because of missing MAKE_REG, source and destination
2772 * REFER.
2773 */
2774 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d1,
2775 RENAME_EXCHANGE));
2776 ASSERT_EQ(EACCES, errno);
2777 ASSERT_EQ(-1, renameat2(AT_FDCWD, file2_s1d1, AT_FDCWD, file1_s2d1,
2778 RENAME_EXCHANGE));
2779 ASSERT_EQ(EACCES, errno);
2780
2781 /* Denies renaming because of missing source REFER. */
2782 ASSERT_EQ(-1, rename(file1_s2d1, file1_s1d3));
2783 ASSERT_EQ(EXDEV, errno);
2784 /* Denies renaming because of missing MAKE_REG. */
2785 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d1, AT_FDCWD, file2_s1d3,
2786 RENAME_EXCHANGE));
2787 ASSERT_EQ(EACCES, errno);
2788
2789 /* Denies renaming because of missing MAKE_REG. */
2790 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d1));
2791 ASSERT_EQ(EACCES, errno);
2792 /* Denies renaming because of missing destination REFER*/
2793 ASSERT_EQ(-1, rename(file1_s2d2, file1_s1d2));
2794 ASSERT_EQ(EXDEV, errno);
2795
2796 /* Denies exchange because of one missing MAKE_REG. */
2797 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, file2_s1d3,
2798 RENAME_EXCHANGE));
2799 ASSERT_EQ(EACCES, errno);
2800 /* Allows renaming because of REFER and MAKE_REG. */
2801 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d3));
2802
2803 /* Reverse renaming denied because of missing MAKE_REG. */
2804 ASSERT_EQ(-1, rename(file1_s1d3, file1_s2d2));
2805 ASSERT_EQ(EACCES, errno);
2806 ASSERT_EQ(0, unlink(file1_s2d3));
2807 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2808
2809 /* Tests reverse renaming. */
2810 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2811 ASSERT_EQ(0, renameat2(AT_FDCWD, file2_s2d3, AT_FDCWD, file1_s1d3,
2812 RENAME_EXCHANGE));
2813 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2814
2815 /*
2816 * This is OK for a file rename, but it should not be allowed for a
2817 * directory rename (because of the superset of access rights).
2818 */
2819 ASSERT_EQ(0, rename(file1_s2d3, file1_s1d3));
2820 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2821
2822 /*
2823 * Tests superset restrictions applied to directories. Not only the
2824 * dir_s2d3's parent (dir_s2d2) should be taken into account but also
2825 * access rights tied to dir_s2d3. dir_s2d2 is missing one access right
2826 * compared to dir_s1d3/file1_s1d3 (MAKE_REG) but it is provided
2827 * directly by the moved dir_s2d3.
2828 */
2829 ASSERT_EQ(0, rename(dir_s2d3, file1_s1d3));
2830 ASSERT_EQ(0, rename(file1_s1d3, dir_s2d3));
2831 /*
2832 * The first rename is allowed but not the exchange because dir_s1d3's
2833 * parent (dir_s1d2) doesn't have REFER.
2834 */
2835 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, dir_s1d3,
2836 RENAME_EXCHANGE));
2837 ASSERT_EQ(EXDEV, errno);
2838 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, file1_s2d3,
2839 RENAME_EXCHANGE));
2840 ASSERT_EQ(EXDEV, errno);
2841 ASSERT_EQ(-1, rename(file1_s2d3, dir_s1d3));
2842 ASSERT_EQ(EXDEV, errno);
2843
2844 ASSERT_EQ(-1, rename(file2_s1d2, file1_s1d3));
2845 ASSERT_EQ(EXDEV, errno);
2846 ASSERT_EQ(-1, rename(file2_s1d3, file1_s1d2));
2847 ASSERT_EQ(EXDEV, errno);
2848
2849 /* Renaming in the same directory is always allowed. */
2850 ASSERT_EQ(0, rename(file2_s1d2, file1_s1d2));
2851 ASSERT_EQ(0, rename(file2_s1d3, file1_s1d3));
2852
2853 ASSERT_EQ(0, unlink(file1_s1d2));
2854 /* Denies because of missing source MAKE_REG and destination REFER. */
2855 ASSERT_EQ(-1, rename(dir_s2d3, file1_s1d2));
2856 ASSERT_EQ(EXDEV, errno);
2857
2858 ASSERT_EQ(0, unlink(file1_s1d3));
2859 /* Denies because of missing source MAKE_REG and REFER. */
2860 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d3));
2861 ASSERT_EQ(EXDEV, errno);
2862 }
2863
2864 static void
reparent_exdev_layers_enforce1(struct __test_metadata * const _metadata)2865 reparent_exdev_layers_enforce1(struct __test_metadata *const _metadata)
2866 {
2867 const struct rule layer1[] = {
2868 {
2869 .path = dir_s1d2,
2870 .access = LANDLOCK_ACCESS_FS_REFER,
2871 },
2872 {
2873 /* Interesting for the layer2 tests. */
2874 .path = dir_s1d3,
2875 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2876 },
2877 {
2878 .path = dir_s2d2,
2879 .access = LANDLOCK_ACCESS_FS_REFER,
2880 },
2881 {
2882 .path = dir_s2d3,
2883 .access = LANDLOCK_ACCESS_FS_MAKE_REG,
2884 },
2885 {},
2886 };
2887 const int ruleset_fd = create_ruleset(
2888 _metadata,
2889 LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REFER, layer1);
2890
2891 ASSERT_LE(0, ruleset_fd);
2892 enforce_ruleset(_metadata, ruleset_fd);
2893 ASSERT_EQ(0, close(ruleset_fd));
2894 }
2895
2896 static void
reparent_exdev_layers_enforce2(struct __test_metadata * const _metadata)2897 reparent_exdev_layers_enforce2(struct __test_metadata *const _metadata)
2898 {
2899 const struct rule layer2[] = {
2900 {
2901 .path = dir_s2d3,
2902 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,
2903 },
2904 {},
2905 };
2906 /*
2907 * Same checks as before but with a second layer and a new MAKE_DIR
2908 * rule (and no explicit handling of REFER).
2909 */
2910 const int ruleset_fd =
2911 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_MAKE_DIR, layer2);
2912
2913 ASSERT_LE(0, ruleset_fd);
2914 enforce_ruleset(_metadata, ruleset_fd);
2915 ASSERT_EQ(0, close(ruleset_fd));
2916 }
2917
TEST_F_FORK(layout1,reparent_exdev_layers_rename1)2918 TEST_F_FORK(layout1, reparent_exdev_layers_rename1)
2919 {
2920 ASSERT_EQ(0, unlink(file1_s2d2));
2921 ASSERT_EQ(0, unlink(file1_s2d3));
2922
2923 reparent_exdev_layers_enforce1(_metadata);
2924
2925 /*
2926 * Moving the dir_s1d3 directory below dir_s2d2 is allowed by Landlock
2927 * because it doesn't inherit new access rights.
2928 */
2929 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
2930 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
2931
2932 /*
2933 * Moving the dir_s1d3 directory below dir_s2d3 is allowed, even if it
2934 * gets a new inherited access rights (MAKE_REG), because MAKE_REG is
2935 * already allowed for dir_s1d3.
2936 */
2937 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d3));
2938 ASSERT_EQ(0, rename(file1_s2d3, dir_s1d3));
2939
2940 /*
2941 * However, moving the file1_s1d3 file below dir_s2d3 is allowed
2942 * because it cannot inherit MAKE_REG right (which is dedicated to
2943 * directories).
2944 */
2945 ASSERT_EQ(0, rename(file1_s1d3, file1_s2d3));
2946
2947 reparent_exdev_layers_enforce2(_metadata);
2948
2949 /*
2950 * Moving the dir_s1d3 directory below dir_s2d2 is now denied because
2951 * MAKE_DIR is not tied to dir_s2d2.
2952 */
2953 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d2));
2954 ASSERT_EQ(EACCES, errno);
2955
2956 /*
2957 * Moving the dir_s1d3 directory below dir_s2d3 is forbidden because it
2958 * would grants MAKE_REG and MAKE_DIR rights to it.
2959 */
2960 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
2961 ASSERT_EQ(EXDEV, errno);
2962
2963 /*
2964 * Moving the file2_s1d3 file below dir_s2d3 is denied because the
2965 * second layer does not handle REFER, which is always denied by
2966 * default.
2967 */
2968 ASSERT_EQ(-1, rename(file2_s1d3, file1_s2d3));
2969 ASSERT_EQ(EXDEV, errno);
2970 }
2971
TEST_F_FORK(layout1,reparent_exdev_layers_rename2)2972 TEST_F_FORK(layout1, reparent_exdev_layers_rename2)
2973 {
2974 reparent_exdev_layers_enforce1(_metadata);
2975
2976 /* Checks EACCES predominance over EXDEV. */
2977 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2978 ASSERT_EQ(EACCES, errno);
2979 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d2));
2980 ASSERT_EQ(EACCES, errno);
2981 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
2982 ASSERT_EQ(EXDEV, errno);
2983 /* Modify layout! */
2984 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d3));
2985
2986 /* Without REFER source. */
2987 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
2988 ASSERT_EQ(EXDEV, errno);
2989 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
2990 ASSERT_EQ(EXDEV, errno);
2991
2992 reparent_exdev_layers_enforce2(_metadata);
2993
2994 /* Checks EACCES predominance over EXDEV. */
2995 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d2));
2996 ASSERT_EQ(EACCES, errno);
2997 /* Checks with actual file2_s1d2. */
2998 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d2));
2999 ASSERT_EQ(EACCES, errno);
3000 ASSERT_EQ(-1, rename(file1_s1d1, file1_s2d3));
3001 ASSERT_EQ(EXDEV, errno);
3002 /*
3003 * Modifying the layout is now denied because the second layer does not
3004 * handle REFER, which is always denied by default.
3005 */
3006 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
3007 ASSERT_EQ(EXDEV, errno);
3008
3009 /* Without REFER source, EACCES wins over EXDEV. */
3010 ASSERT_EQ(-1, rename(dir_s1d1, file1_s2d2));
3011 ASSERT_EQ(EACCES, errno);
3012 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d2));
3013 ASSERT_EQ(EACCES, errno);
3014 }
3015
TEST_F_FORK(layout1,reparent_exdev_layers_exchange1)3016 TEST_F_FORK(layout1, reparent_exdev_layers_exchange1)
3017 {
3018 const char *const dir_file1_s1d2 = file1_s1d2, *const dir_file2_s2d3 =
3019 file2_s2d3;
3020
3021 ASSERT_EQ(0, unlink(file1_s1d2));
3022 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
3023 ASSERT_EQ(0, unlink(file2_s2d3));
3024 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
3025
3026 reparent_exdev_layers_enforce1(_metadata);
3027
3028 /* Error predominance with file exchange: returns EXDEV and EACCES. */
3029 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
3030 RENAME_EXCHANGE));
3031 ASSERT_EQ(EACCES, errno);
3032 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
3033 RENAME_EXCHANGE));
3034 ASSERT_EQ(EACCES, errno);
3035
3036 /*
3037 * Checks with directories which creation could be allowed, but denied
3038 * because of access rights that would be inherited.
3039 */
3040 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
3041 dir_file2_s2d3, RENAME_EXCHANGE));
3042 ASSERT_EQ(EXDEV, errno);
3043 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
3044 dir_file1_s1d2, RENAME_EXCHANGE));
3045 ASSERT_EQ(EXDEV, errno);
3046
3047 /* Checks with same access rights. */
3048 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
3049 RENAME_EXCHANGE));
3050 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
3051 RENAME_EXCHANGE));
3052
3053 /* Checks with different (child-only) access rights. */
3054 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
3055 RENAME_EXCHANGE));
3056 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
3057 RENAME_EXCHANGE));
3058
3059 /*
3060 * Checks that exchange between file and directory are consistent.
3061 *
3062 * Moving a file (file1_s2d2) to a directory which only grants more
3063 * directory-related access rights is allowed, and at the same time
3064 * moving a directory (dir_file2_s2d3) to another directory which
3065 * grants less access rights is allowed too.
3066 *
3067 * See layout1.reparent_exdev_layers_exchange3 for inverted arguments.
3068 */
3069 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3070 RENAME_EXCHANGE));
3071 /*
3072 * However, moving back the directory is denied because it would get
3073 * more access rights than the current state and because file creation
3074 * is forbidden (in dir_s2d2).
3075 */
3076 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3077 RENAME_EXCHANGE));
3078 ASSERT_EQ(EACCES, errno);
3079 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3080 RENAME_EXCHANGE));
3081 ASSERT_EQ(EACCES, errno);
3082
3083 reparent_exdev_layers_enforce2(_metadata);
3084
3085 /* Error predominance with file exchange: returns EXDEV and EACCES. */
3086 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, file1_s2d3,
3087 RENAME_EXCHANGE));
3088 ASSERT_EQ(EACCES, errno);
3089 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d3, AT_FDCWD, file1_s1d1,
3090 RENAME_EXCHANGE));
3091 ASSERT_EQ(EACCES, errno);
3092
3093 /* Checks with directories which creation is now denied. */
3094 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD,
3095 dir_file2_s2d3, RENAME_EXCHANGE));
3096 ASSERT_EQ(EACCES, errno);
3097 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD,
3098 dir_file1_s1d2, RENAME_EXCHANGE));
3099 ASSERT_EQ(EACCES, errno);
3100
3101 /* Checks with different (child-only) access rights. */
3102 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s1d3, AT_FDCWD, dir_s2d3,
3103 RENAME_EXCHANGE));
3104 /* Denied because of MAKE_DIR. */
3105 ASSERT_EQ(EACCES, errno);
3106 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_s1d3,
3107 RENAME_EXCHANGE));
3108 ASSERT_EQ(EACCES, errno);
3109
3110 /* Checks with different (child-only) access rights. */
3111 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_s2d3, AT_FDCWD, dir_file1_s1d2,
3112 RENAME_EXCHANGE));
3113 /* Denied because of MAKE_DIR. */
3114 ASSERT_EQ(EACCES, errno);
3115 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file1_s1d2, AT_FDCWD, dir_s2d3,
3116 RENAME_EXCHANGE));
3117 ASSERT_EQ(EACCES, errno);
3118
3119 /* See layout1.reparent_exdev_layers_exchange2 for complement. */
3120 }
3121
TEST_F_FORK(layout1,reparent_exdev_layers_exchange2)3122 TEST_F_FORK(layout1, reparent_exdev_layers_exchange2)
3123 {
3124 const char *const dir_file2_s2d3 = file2_s2d3;
3125
3126 ASSERT_EQ(0, unlink(file2_s2d3));
3127 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
3128
3129 reparent_exdev_layers_enforce1(_metadata);
3130 reparent_exdev_layers_enforce2(_metadata);
3131
3132 /* Checks that exchange between file and directory are consistent. */
3133 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3134 RENAME_EXCHANGE));
3135 ASSERT_EQ(EACCES, errno);
3136 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3137 RENAME_EXCHANGE));
3138 ASSERT_EQ(EACCES, errno);
3139 }
3140
TEST_F_FORK(layout1,reparent_exdev_layers_exchange3)3141 TEST_F_FORK(layout1, reparent_exdev_layers_exchange3)
3142 {
3143 const char *const dir_file2_s2d3 = file2_s2d3;
3144
3145 ASSERT_EQ(0, unlink(file2_s2d3));
3146 ASSERT_EQ(0, mkdir(file2_s2d3, 0700));
3147
3148 reparent_exdev_layers_enforce1(_metadata);
3149
3150 /*
3151 * Checks that exchange between file and directory are consistent,
3152 * including with inverted arguments (see
3153 * layout1.reparent_exdev_layers_exchange1).
3154 */
3155 ASSERT_EQ(0, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3156 RENAME_EXCHANGE));
3157 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_file2_s2d3,
3158 RENAME_EXCHANGE));
3159 ASSERT_EQ(EACCES, errno);
3160 ASSERT_EQ(-1, renameat2(AT_FDCWD, dir_file2_s2d3, AT_FDCWD, file1_s2d2,
3161 RENAME_EXCHANGE));
3162 ASSERT_EQ(EACCES, errno);
3163 }
3164
TEST_F_FORK(layout1,reparent_remove)3165 TEST_F_FORK(layout1, reparent_remove)
3166 {
3167 const struct rule layer1[] = {
3168 {
3169 .path = dir_s1d1,
3170 .access = LANDLOCK_ACCESS_FS_REFER |
3171 LANDLOCK_ACCESS_FS_REMOVE_DIR,
3172 },
3173 {
3174 .path = dir_s1d2,
3175 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
3176 },
3177 {
3178 .path = dir_s2d1,
3179 .access = LANDLOCK_ACCESS_FS_REFER |
3180 LANDLOCK_ACCESS_FS_REMOVE_FILE,
3181 },
3182 {},
3183 };
3184 const int ruleset_fd = create_ruleset(
3185 _metadata,
3186 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_REMOVE_DIR |
3187 LANDLOCK_ACCESS_FS_REMOVE_FILE,
3188 layer1);
3189
3190 ASSERT_LE(0, ruleset_fd);
3191 enforce_ruleset(_metadata, ruleset_fd);
3192 ASSERT_EQ(0, close(ruleset_fd));
3193
3194 /* Access denied because of wrong/swapped remove file/dir. */
3195 ASSERT_EQ(-1, rename(file1_s1d1, dir_s2d2));
3196 ASSERT_EQ(EACCES, errno);
3197 ASSERT_EQ(-1, rename(dir_s2d2, file1_s1d1));
3198 ASSERT_EQ(EACCES, errno);
3199 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d2,
3200 RENAME_EXCHANGE));
3201 ASSERT_EQ(EACCES, errno);
3202 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s1d1, AT_FDCWD, dir_s2d3,
3203 RENAME_EXCHANGE));
3204 ASSERT_EQ(EACCES, errno);
3205
3206 /* Access allowed thanks to the matching rights. */
3207 ASSERT_EQ(-1, rename(file1_s2d1, dir_s1d2));
3208 ASSERT_EQ(EISDIR, errno);
3209 ASSERT_EQ(-1, rename(dir_s1d2, file1_s2d1));
3210 ASSERT_EQ(ENOTDIR, errno);
3211 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
3212 ASSERT_EQ(ENOTDIR, errno);
3213 ASSERT_EQ(0, unlink(file1_s2d1));
3214 ASSERT_EQ(0, unlink(file1_s1d3));
3215 ASSERT_EQ(0, unlink(file2_s1d3));
3216 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d1));
3217
3218 /* Effectively removes a file and a directory by exchanging them. */
3219 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
3220 ASSERT_EQ(0, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
3221 RENAME_EXCHANGE));
3222 ASSERT_EQ(-1, renameat2(AT_FDCWD, file1_s2d2, AT_FDCWD, dir_s1d3,
3223 RENAME_EXCHANGE));
3224 ASSERT_EQ(EACCES, errno);
3225 }
3226
TEST_F_FORK(layout1,reparent_dom_superset)3227 TEST_F_FORK(layout1, reparent_dom_superset)
3228 {
3229 const struct rule layer1[] = {
3230 {
3231 .path = dir_s1d2,
3232 .access = LANDLOCK_ACCESS_FS_REFER,
3233 },
3234 {
3235 .path = file1_s1d2,
3236 .access = LANDLOCK_ACCESS_FS_EXECUTE,
3237 },
3238 {
3239 .path = dir_s1d3,
3240 .access = LANDLOCK_ACCESS_FS_MAKE_SOCK |
3241 LANDLOCK_ACCESS_FS_EXECUTE,
3242 },
3243 {
3244 .path = dir_s2d2,
3245 .access = LANDLOCK_ACCESS_FS_REFER |
3246 LANDLOCK_ACCESS_FS_EXECUTE |
3247 LANDLOCK_ACCESS_FS_MAKE_SOCK,
3248 },
3249 {
3250 .path = dir_s2d3,
3251 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3252 LANDLOCK_ACCESS_FS_MAKE_FIFO,
3253 },
3254 {},
3255 };
3256 int ruleset_fd = create_ruleset(_metadata,
3257 LANDLOCK_ACCESS_FS_REFER |
3258 LANDLOCK_ACCESS_FS_EXECUTE |
3259 LANDLOCK_ACCESS_FS_MAKE_SOCK |
3260 LANDLOCK_ACCESS_FS_READ_FILE |
3261 LANDLOCK_ACCESS_FS_MAKE_FIFO,
3262 layer1);
3263
3264 ASSERT_LE(0, ruleset_fd);
3265 enforce_ruleset(_metadata, ruleset_fd);
3266 ASSERT_EQ(0, close(ruleset_fd));
3267
3268 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d1));
3269 ASSERT_EQ(EXDEV, errno);
3270 /*
3271 * Moving file1_s1d2 beneath dir_s2d3 would grant it the READ_FILE
3272 * access right.
3273 */
3274 ASSERT_EQ(-1, rename(file1_s1d2, file1_s2d3));
3275 ASSERT_EQ(EXDEV, errno);
3276 /*
3277 * Moving file1_s1d2 should be allowed even if dir_s2d2 grants a
3278 * superset of access rights compared to dir_s1d2, because file1_s1d2
3279 * already has these access rights anyway.
3280 */
3281 ASSERT_EQ(0, rename(file1_s1d2, file1_s2d2));
3282 ASSERT_EQ(0, rename(file1_s2d2, file1_s1d2));
3283
3284 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d1));
3285 ASSERT_EQ(EXDEV, errno);
3286 /*
3287 * Moving dir_s1d3 beneath dir_s2d3 would grant it the MAKE_FIFO access
3288 * right.
3289 */
3290 ASSERT_EQ(-1, rename(dir_s1d3, file1_s2d3));
3291 ASSERT_EQ(EXDEV, errno);
3292 /*
3293 * Moving dir_s1d3 should be allowed even if dir_s2d2 grants a superset
3294 * of access rights compared to dir_s1d2, because dir_s1d3 already has
3295 * these access rights anyway.
3296 */
3297 ASSERT_EQ(0, rename(dir_s1d3, file1_s2d2));
3298 ASSERT_EQ(0, rename(file1_s2d2, dir_s1d3));
3299
3300 /*
3301 * Moving file1_s2d3 beneath dir_s1d2 is allowed, but moving it back
3302 * will be denied because the new inherited access rights from dir_s1d2
3303 * will be less than the destination (original) dir_s2d3. This is a
3304 * sinkhole scenario where we cannot move back files or directories.
3305 */
3306 ASSERT_EQ(0, rename(file1_s2d3, file2_s1d2));
3307 ASSERT_EQ(-1, rename(file2_s1d2, file1_s2d3));
3308 ASSERT_EQ(EXDEV, errno);
3309 ASSERT_EQ(0, unlink(file2_s1d2));
3310 ASSERT_EQ(0, unlink(file2_s2d3));
3311 /*
3312 * Checks similar directory one-way move: dir_s2d3 loses EXECUTE and
3313 * MAKE_SOCK which were inherited from dir_s1d3.
3314 */
3315 ASSERT_EQ(0, rename(dir_s2d3, file2_s1d2));
3316 ASSERT_EQ(-1, rename(file2_s1d2, dir_s2d3));
3317 ASSERT_EQ(EXDEV, errno);
3318 }
3319
TEST_F_FORK(layout1,remove_dir)3320 TEST_F_FORK(layout1, remove_dir)
3321 {
3322 const struct rule rules[] = {
3323 {
3324 .path = dir_s1d2,
3325 .access = LANDLOCK_ACCESS_FS_REMOVE_DIR,
3326 },
3327 {},
3328 };
3329 const int ruleset_fd =
3330 create_ruleset(_metadata, rules[0].access, rules);
3331
3332 ASSERT_LE(0, ruleset_fd);
3333
3334 ASSERT_EQ(0, unlink(file1_s1d1));
3335 ASSERT_EQ(0, unlink(file1_s1d2));
3336 ASSERT_EQ(0, unlink(file1_s1d3));
3337 ASSERT_EQ(0, unlink(file2_s1d3));
3338
3339 enforce_ruleset(_metadata, ruleset_fd);
3340 ASSERT_EQ(0, close(ruleset_fd));
3341
3342 ASSERT_EQ(0, rmdir(dir_s1d3));
3343 ASSERT_EQ(0, mkdir(dir_s1d3, 0700));
3344 ASSERT_EQ(0, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
3345
3346 /* dir_s1d2 itself cannot be removed. */
3347 ASSERT_EQ(-1, rmdir(dir_s1d2));
3348 ASSERT_EQ(EACCES, errno);
3349 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d2, AT_REMOVEDIR));
3350 ASSERT_EQ(EACCES, errno);
3351 ASSERT_EQ(-1, rmdir(dir_s1d1));
3352 ASSERT_EQ(EACCES, errno);
3353 ASSERT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d1, AT_REMOVEDIR));
3354 ASSERT_EQ(EACCES, errno);
3355 }
3356
TEST_F_FORK(layout1,remove_file)3357 TEST_F_FORK(layout1, remove_file)
3358 {
3359 const struct rule rules[] = {
3360 {
3361 .path = dir_s1d2,
3362 .access = LANDLOCK_ACCESS_FS_REMOVE_FILE,
3363 },
3364 {},
3365 };
3366 const int ruleset_fd =
3367 create_ruleset(_metadata, rules[0].access, rules);
3368
3369 ASSERT_LE(0, ruleset_fd);
3370 enforce_ruleset(_metadata, ruleset_fd);
3371 ASSERT_EQ(0, close(ruleset_fd));
3372
3373 ASSERT_EQ(-1, unlink(file1_s1d1));
3374 ASSERT_EQ(EACCES, errno);
3375 ASSERT_EQ(-1, unlinkat(AT_FDCWD, file1_s1d1, 0));
3376 ASSERT_EQ(EACCES, errno);
3377 ASSERT_EQ(0, unlink(file1_s1d2));
3378 ASSERT_EQ(0, unlinkat(AT_FDCWD, file1_s1d3, 0));
3379 }
3380
test_make_file(struct __test_metadata * const _metadata,const __u64 access,const mode_t mode,const dev_t dev)3381 static void test_make_file(struct __test_metadata *const _metadata,
3382 const __u64 access, const mode_t mode,
3383 const dev_t dev)
3384 {
3385 const struct rule rules[] = {
3386 {
3387 .path = dir_s1d2,
3388 .access = access,
3389 },
3390 {},
3391 };
3392 const int ruleset_fd = create_ruleset(_metadata, access, rules);
3393
3394 ASSERT_LE(0, ruleset_fd);
3395
3396 ASSERT_EQ(0, unlink(file1_s1d1));
3397 ASSERT_EQ(0, unlink(file2_s1d1));
3398 ASSERT_EQ(0, mknod(file2_s1d1, mode | 0400, dev))
3399 {
3400 TH_LOG("Failed to make file \"%s\": %s", file2_s1d1,
3401 strerror(errno));
3402 };
3403
3404 ASSERT_EQ(0, unlink(file1_s1d2));
3405 ASSERT_EQ(0, unlink(file2_s1d2));
3406
3407 ASSERT_EQ(0, unlink(file1_s1d3));
3408 ASSERT_EQ(0, unlink(file2_s1d3));
3409
3410 enforce_ruleset(_metadata, ruleset_fd);
3411 ASSERT_EQ(0, close(ruleset_fd));
3412
3413 ASSERT_EQ(-1, mknod(file1_s1d1, mode | 0400, dev));
3414 ASSERT_EQ(EACCES, errno);
3415 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3416 ASSERT_EQ(EACCES, errno);
3417 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3418 ASSERT_EQ(EACCES, errno);
3419
3420 ASSERT_EQ(0, mknod(file1_s1d2, mode | 0400, dev))
3421 {
3422 TH_LOG("Failed to make file \"%s\": %s", file1_s1d2,
3423 strerror(errno));
3424 };
3425 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3426 ASSERT_EQ(0, unlink(file2_s1d2));
3427 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3428
3429 ASSERT_EQ(0, mknod(file1_s1d3, mode | 0400, dev));
3430 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3431 ASSERT_EQ(0, unlink(file2_s1d3));
3432 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3433 }
3434
TEST_F_FORK(layout1,make_char)3435 TEST_F_FORK(layout1, make_char)
3436 {
3437 /* Creates a /dev/null device. */
3438 set_cap(_metadata, CAP_MKNOD);
3439 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_CHAR, S_IFCHR,
3440 makedev(1, 3));
3441 }
3442
TEST_F_FORK(layout1,make_block)3443 TEST_F_FORK(layout1, make_block)
3444 {
3445 /* Creates a /dev/loop0 device. */
3446 set_cap(_metadata, CAP_MKNOD);
3447 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_BLOCK, S_IFBLK,
3448 makedev(7, 0));
3449 }
3450
TEST_F_FORK(layout1,make_reg_1)3451 TEST_F_FORK(layout1, make_reg_1)
3452 {
3453 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, S_IFREG, 0);
3454 }
3455
TEST_F_FORK(layout1,make_reg_2)3456 TEST_F_FORK(layout1, make_reg_2)
3457 {
3458 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_REG, 0, 0);
3459 }
3460
TEST_F_FORK(layout1,make_sock)3461 TEST_F_FORK(layout1, make_sock)
3462 {
3463 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_SOCK, S_IFSOCK, 0);
3464 }
3465
TEST_F_FORK(layout1,make_fifo)3466 TEST_F_FORK(layout1, make_fifo)
3467 {
3468 test_make_file(_metadata, LANDLOCK_ACCESS_FS_MAKE_FIFO, S_IFIFO, 0);
3469 }
3470
TEST_F_FORK(layout1,make_sym)3471 TEST_F_FORK(layout1, make_sym)
3472 {
3473 const struct rule rules[] = {
3474 {
3475 .path = dir_s1d2,
3476 .access = LANDLOCK_ACCESS_FS_MAKE_SYM,
3477 },
3478 {},
3479 };
3480 const int ruleset_fd =
3481 create_ruleset(_metadata, rules[0].access, rules);
3482
3483 ASSERT_LE(0, ruleset_fd);
3484
3485 ASSERT_EQ(0, unlink(file1_s1d1));
3486 ASSERT_EQ(0, unlink(file2_s1d1));
3487 ASSERT_EQ(0, symlink("none", file2_s1d1));
3488
3489 ASSERT_EQ(0, unlink(file1_s1d2));
3490 ASSERT_EQ(0, unlink(file2_s1d2));
3491
3492 ASSERT_EQ(0, unlink(file1_s1d3));
3493 ASSERT_EQ(0, unlink(file2_s1d3));
3494
3495 enforce_ruleset(_metadata, ruleset_fd);
3496 ASSERT_EQ(0, close(ruleset_fd));
3497
3498 ASSERT_EQ(-1, symlink("none", file1_s1d1));
3499 ASSERT_EQ(EACCES, errno);
3500 ASSERT_EQ(-1, link(file2_s1d1, file1_s1d1));
3501 ASSERT_EQ(EACCES, errno);
3502 ASSERT_EQ(-1, rename(file2_s1d1, file1_s1d1));
3503 ASSERT_EQ(EACCES, errno);
3504
3505 ASSERT_EQ(0, symlink("none", file1_s1d2));
3506 ASSERT_EQ(0, link(file1_s1d2, file2_s1d2));
3507 ASSERT_EQ(0, unlink(file2_s1d2));
3508 ASSERT_EQ(0, rename(file1_s1d2, file2_s1d2));
3509
3510 ASSERT_EQ(0, symlink("none", file1_s1d3));
3511 ASSERT_EQ(0, link(file1_s1d3, file2_s1d3));
3512 ASSERT_EQ(0, unlink(file2_s1d3));
3513 ASSERT_EQ(0, rename(file1_s1d3, file2_s1d3));
3514 }
3515
TEST_F_FORK(layout1,make_dir)3516 TEST_F_FORK(layout1, make_dir)
3517 {
3518 const struct rule rules[] = {
3519 {
3520 .path = dir_s1d2,
3521 .access = LANDLOCK_ACCESS_FS_MAKE_DIR,
3522 },
3523 {},
3524 };
3525 const int ruleset_fd =
3526 create_ruleset(_metadata, rules[0].access, rules);
3527
3528 ASSERT_LE(0, ruleset_fd);
3529
3530 ASSERT_EQ(0, unlink(file1_s1d1));
3531 ASSERT_EQ(0, unlink(file1_s1d2));
3532 ASSERT_EQ(0, unlink(file1_s1d3));
3533
3534 enforce_ruleset(_metadata, ruleset_fd);
3535 ASSERT_EQ(0, close(ruleset_fd));
3536
3537 /* Uses file_* as directory names. */
3538 ASSERT_EQ(-1, mkdir(file1_s1d1, 0700));
3539 ASSERT_EQ(EACCES, errno);
3540 ASSERT_EQ(0, mkdir(file1_s1d2, 0700));
3541 ASSERT_EQ(0, mkdir(file1_s1d3, 0700));
3542 }
3543
open_proc_fd(struct __test_metadata * const _metadata,const int fd,const int open_flags)3544 static int open_proc_fd(struct __test_metadata *const _metadata, const int fd,
3545 const int open_flags)
3546 {
3547 static const char path_template[] = "/proc/self/fd/%d";
3548 char procfd_path[sizeof(path_template) + 10];
3549 const int procfd_path_size =
3550 snprintf(procfd_path, sizeof(procfd_path), path_template, fd);
3551
3552 ASSERT_LT(procfd_path_size, sizeof(procfd_path));
3553 return open(procfd_path, open_flags);
3554 }
3555
TEST_F_FORK(layout1,proc_unlinked_file)3556 TEST_F_FORK(layout1, proc_unlinked_file)
3557 {
3558 const struct rule rules[] = {
3559 {
3560 .path = file1_s1d2,
3561 .access = LANDLOCK_ACCESS_FS_READ_FILE,
3562 },
3563 {},
3564 };
3565 int reg_fd, proc_fd;
3566 const int ruleset_fd = create_ruleset(
3567 _metadata,
3568 LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE,
3569 rules);
3570
3571 ASSERT_LE(0, ruleset_fd);
3572 enforce_ruleset(_metadata, ruleset_fd);
3573 ASSERT_EQ(0, close(ruleset_fd));
3574
3575 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDWR));
3576 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
3577 reg_fd = open(file1_s1d2, O_RDONLY | O_CLOEXEC);
3578 ASSERT_LE(0, reg_fd);
3579 ASSERT_EQ(0, unlink(file1_s1d2));
3580
3581 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDONLY | O_CLOEXEC);
3582 ASSERT_LE(0, proc_fd);
3583 ASSERT_EQ(0, close(proc_fd));
3584
3585 proc_fd = open_proc_fd(_metadata, reg_fd, O_RDWR | O_CLOEXEC);
3586 ASSERT_EQ(-1, proc_fd)
3587 {
3588 TH_LOG("Successfully opened /proc/self/fd/%d: %s", reg_fd,
3589 strerror(errno));
3590 }
3591 ASSERT_EQ(EACCES, errno);
3592
3593 ASSERT_EQ(0, close(reg_fd));
3594 }
3595
TEST_F_FORK(layout1,proc_pipe)3596 TEST_F_FORK(layout1, proc_pipe)
3597 {
3598 int proc_fd;
3599 int pipe_fds[2];
3600 char buf = '\0';
3601 const struct rule rules[] = {
3602 {
3603 .path = dir_s1d2,
3604 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3605 LANDLOCK_ACCESS_FS_WRITE_FILE,
3606 },
3607 {},
3608 };
3609 /* Limits read and write access to files tied to the filesystem. */
3610 const int ruleset_fd =
3611 create_ruleset(_metadata, rules[0].access, rules);
3612
3613 ASSERT_LE(0, ruleset_fd);
3614 enforce_ruleset(_metadata, ruleset_fd);
3615 ASSERT_EQ(0, close(ruleset_fd));
3616
3617 /* Checks enforcement for normal files. */
3618 ASSERT_EQ(0, test_open(file1_s1d2, O_RDWR));
3619 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDWR));
3620
3621 /* Checks access to pipes through FD. */
3622 ASSERT_EQ(0, pipe2(pipe_fds, O_CLOEXEC));
3623 ASSERT_EQ(1, write(pipe_fds[1], ".", 1))
3624 {
3625 TH_LOG("Failed to write in pipe: %s", strerror(errno));
3626 }
3627 ASSERT_EQ(1, read(pipe_fds[0], &buf, 1));
3628 ASSERT_EQ('.', buf);
3629
3630 /* Checks write access to pipe through /proc/self/fd . */
3631 proc_fd = open_proc_fd(_metadata, pipe_fds[1], O_WRONLY | O_CLOEXEC);
3632 ASSERT_LE(0, proc_fd);
3633 ASSERT_EQ(1, write(proc_fd, ".", 1))
3634 {
3635 TH_LOG("Failed to write through /proc/self/fd/%d: %s",
3636 pipe_fds[1], strerror(errno));
3637 }
3638 ASSERT_EQ(0, close(proc_fd));
3639
3640 /* Checks read access to pipe through /proc/self/fd . */
3641 proc_fd = open_proc_fd(_metadata, pipe_fds[0], O_RDONLY | O_CLOEXEC);
3642 ASSERT_LE(0, proc_fd);
3643 buf = '\0';
3644 ASSERT_EQ(1, read(proc_fd, &buf, 1))
3645 {
3646 TH_LOG("Failed to read through /proc/self/fd/%d: %s",
3647 pipe_fds[1], strerror(errno));
3648 }
3649 ASSERT_EQ(0, close(proc_fd));
3650
3651 ASSERT_EQ(0, close(pipe_fds[0]));
3652 ASSERT_EQ(0, close(pipe_fds[1]));
3653 }
3654
3655 /* Invokes truncate(2) and returns its errno or 0. */
test_truncate(const char * const path)3656 static int test_truncate(const char *const path)
3657 {
3658 if (truncate(path, 10) < 0)
3659 return errno;
3660 return 0;
3661 }
3662
3663 /*
3664 * Invokes creat(2) and returns its errno or 0.
3665 * Closes the opened file descriptor on success.
3666 */
test_creat(const char * const path)3667 static int test_creat(const char *const path)
3668 {
3669 int fd = creat(path, 0600);
3670
3671 if (fd < 0)
3672 return errno;
3673
3674 /*
3675 * Mixing error codes from close(2) and creat(2) should not lead to any
3676 * (access type) confusion for this test.
3677 */
3678 if (close(fd) < 0)
3679 return errno;
3680 return 0;
3681 }
3682
3683 /*
3684 * Exercises file truncation when it's not restricted,
3685 * as it was the case before LANDLOCK_ACCESS_FS_TRUNCATE existed.
3686 */
TEST_F_FORK(layout1,truncate_unhandled)3687 TEST_F_FORK(layout1, truncate_unhandled)
3688 {
3689 const char *const file_r = file1_s1d1;
3690 const char *const file_w = file2_s1d1;
3691 const char *const file_none = file1_s1d2;
3692 const struct rule rules[] = {
3693 {
3694 .path = file_r,
3695 .access = LANDLOCK_ACCESS_FS_READ_FILE,
3696 },
3697 {
3698 .path = file_w,
3699 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3700 },
3701 /* Implicitly: No rights for file_none. */
3702 {},
3703 };
3704
3705 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3706 LANDLOCK_ACCESS_FS_WRITE_FILE;
3707 int ruleset_fd;
3708
3709 /* Enables Landlock. */
3710 ruleset_fd = create_ruleset(_metadata, handled, rules);
3711
3712 ASSERT_LE(0, ruleset_fd);
3713 enforce_ruleset(_metadata, ruleset_fd);
3714 ASSERT_EQ(0, close(ruleset_fd));
3715
3716 /*
3717 * Checks read right: truncate and open with O_TRUNC work, unless the
3718 * file is attempted to be opened for writing.
3719 */
3720 EXPECT_EQ(0, test_truncate(file_r));
3721 EXPECT_EQ(0, test_open(file_r, O_RDONLY | O_TRUNC));
3722 EXPECT_EQ(EACCES, test_open(file_r, O_WRONLY | O_TRUNC));
3723 EXPECT_EQ(EACCES, test_creat(file_r));
3724
3725 /*
3726 * Checks write right: truncate and open with O_TRUNC work, unless the
3727 * file is attempted to be opened for reading.
3728 */
3729 EXPECT_EQ(0, test_truncate(file_w));
3730 EXPECT_EQ(EACCES, test_open(file_w, O_RDONLY | O_TRUNC));
3731 EXPECT_EQ(0, test_open(file_w, O_WRONLY | O_TRUNC));
3732 EXPECT_EQ(0, test_creat(file_w));
3733
3734 /*
3735 * Checks "no rights" case: truncate works but all open attempts fail,
3736 * including creat.
3737 */
3738 EXPECT_EQ(0, test_truncate(file_none));
3739 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3740 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3741 EXPECT_EQ(EACCES, test_creat(file_none));
3742 }
3743
TEST_F_FORK(layout1,truncate)3744 TEST_F_FORK(layout1, truncate)
3745 {
3746 const char *const file_rwt = file1_s1d1;
3747 const char *const file_rw = file2_s1d1;
3748 const char *const file_rt = file1_s1d2;
3749 const char *const file_t = file2_s1d2;
3750 const char *const file_none = file1_s1d3;
3751 const char *const dir_t = dir_s2d1;
3752 const char *const file_in_dir_t = file1_s2d1;
3753 const char *const dir_w = dir_s3d1;
3754 const char *const file_in_dir_w = file1_s3d1;
3755 const struct rule rules[] = {
3756 {
3757 .path = file_rwt,
3758 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3759 LANDLOCK_ACCESS_FS_WRITE_FILE |
3760 LANDLOCK_ACCESS_FS_TRUNCATE,
3761 },
3762 {
3763 .path = file_rw,
3764 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3765 LANDLOCK_ACCESS_FS_WRITE_FILE,
3766 },
3767 {
3768 .path = file_rt,
3769 .access = LANDLOCK_ACCESS_FS_READ_FILE |
3770 LANDLOCK_ACCESS_FS_TRUNCATE,
3771 },
3772 {
3773 .path = file_t,
3774 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3775 },
3776 /* Implicitly: No access rights for file_none. */
3777 {
3778 .path = dir_t,
3779 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3780 },
3781 {
3782 .path = dir_w,
3783 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3784 },
3785 {},
3786 };
3787 const __u64 handled = LANDLOCK_ACCESS_FS_READ_FILE |
3788 LANDLOCK_ACCESS_FS_WRITE_FILE |
3789 LANDLOCK_ACCESS_FS_TRUNCATE;
3790 int ruleset_fd;
3791
3792 /* Enables Landlock. */
3793 ruleset_fd = create_ruleset(_metadata, handled, rules);
3794
3795 ASSERT_LE(0, ruleset_fd);
3796 enforce_ruleset(_metadata, ruleset_fd);
3797 ASSERT_EQ(0, close(ruleset_fd));
3798
3799 /* Checks read, write and truncate rights: truncation works. */
3800 EXPECT_EQ(0, test_truncate(file_rwt));
3801 EXPECT_EQ(0, test_open(file_rwt, O_RDONLY | O_TRUNC));
3802 EXPECT_EQ(0, test_open(file_rwt, O_WRONLY | O_TRUNC));
3803
3804 /* Checks read and write rights: no truncate variant works. */
3805 EXPECT_EQ(EACCES, test_truncate(file_rw));
3806 EXPECT_EQ(EACCES, test_open(file_rw, O_RDONLY | O_TRUNC));
3807 EXPECT_EQ(EACCES, test_open(file_rw, O_WRONLY | O_TRUNC));
3808
3809 /*
3810 * Checks read and truncate rights: truncation works.
3811 *
3812 * Note: Files can get truncated using open() even with O_RDONLY.
3813 */
3814 EXPECT_EQ(0, test_truncate(file_rt));
3815 EXPECT_EQ(0, test_open(file_rt, O_RDONLY | O_TRUNC));
3816 EXPECT_EQ(EACCES, test_open(file_rt, O_WRONLY | O_TRUNC));
3817
3818 /* Checks truncate right: truncate works, but can't open file. */
3819 EXPECT_EQ(0, test_truncate(file_t));
3820 EXPECT_EQ(EACCES, test_open(file_t, O_RDONLY | O_TRUNC));
3821 EXPECT_EQ(EACCES, test_open(file_t, O_WRONLY | O_TRUNC));
3822
3823 /* Checks "no rights" case: No form of truncation works. */
3824 EXPECT_EQ(EACCES, test_truncate(file_none));
3825 EXPECT_EQ(EACCES, test_open(file_none, O_RDONLY | O_TRUNC));
3826 EXPECT_EQ(EACCES, test_open(file_none, O_WRONLY | O_TRUNC));
3827
3828 /*
3829 * Checks truncate right on directory: truncate works on contained
3830 * files.
3831 */
3832 EXPECT_EQ(0, test_truncate(file_in_dir_t));
3833 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_RDONLY | O_TRUNC));
3834 EXPECT_EQ(EACCES, test_open(file_in_dir_t, O_WRONLY | O_TRUNC));
3835
3836 /*
3837 * Checks creat in dir_w: This requires the truncate right when
3838 * overwriting an existing file, but does not require it when the file
3839 * is new.
3840 */
3841 EXPECT_EQ(EACCES, test_creat(file_in_dir_w));
3842
3843 ASSERT_EQ(0, unlink(file_in_dir_w));
3844 EXPECT_EQ(0, test_creat(file_in_dir_w));
3845 }
3846
3847 /* Invokes ftruncate(2) and returns its errno or 0. */
test_ftruncate(int fd)3848 static int test_ftruncate(int fd)
3849 {
3850 if (ftruncate(fd, 10) < 0)
3851 return errno;
3852 return 0;
3853 }
3854
TEST_F_FORK(layout1,ftruncate)3855 TEST_F_FORK(layout1, ftruncate)
3856 {
3857 /*
3858 * This test opens a new file descriptor at different stages of
3859 * Landlock restriction:
3860 *
3861 * without restriction: ftruncate works
3862 * something else but truncate restricted: ftruncate works
3863 * truncate restricted and permitted: ftruncate works
3864 * truncate restricted and not permitted: ftruncate fails
3865 *
3866 * Whether this works or not is expected to depend on the time when the
3867 * FD was opened, not to depend on the time when ftruncate() was
3868 * called.
3869 */
3870 const char *const path = file1_s1d1;
3871 const __u64 handled1 = LANDLOCK_ACCESS_FS_READ_FILE |
3872 LANDLOCK_ACCESS_FS_WRITE_FILE;
3873 const struct rule layer1[] = {
3874 {
3875 .path = path,
3876 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3877 },
3878 {},
3879 };
3880 const __u64 handled2 = LANDLOCK_ACCESS_FS_TRUNCATE;
3881 const struct rule layer2[] = {
3882 {
3883 .path = path,
3884 .access = LANDLOCK_ACCESS_FS_TRUNCATE,
3885 },
3886 {},
3887 };
3888 const __u64 handled3 = LANDLOCK_ACCESS_FS_TRUNCATE |
3889 LANDLOCK_ACCESS_FS_WRITE_FILE;
3890 const struct rule layer3[] = {
3891 {
3892 .path = path,
3893 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
3894 },
3895 {},
3896 };
3897 int fd_layer0, fd_layer1, fd_layer2, fd_layer3, ruleset_fd;
3898
3899 fd_layer0 = open(path, O_WRONLY);
3900 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3901
3902 ruleset_fd = create_ruleset(_metadata, handled1, layer1);
3903 ASSERT_LE(0, ruleset_fd);
3904 enforce_ruleset(_metadata, ruleset_fd);
3905 ASSERT_EQ(0, close(ruleset_fd));
3906
3907 fd_layer1 = open(path, O_WRONLY);
3908 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3909 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3910
3911 ruleset_fd = create_ruleset(_metadata, handled2, layer2);
3912 ASSERT_LE(0, ruleset_fd);
3913 enforce_ruleset(_metadata, ruleset_fd);
3914 ASSERT_EQ(0, close(ruleset_fd));
3915
3916 fd_layer2 = open(path, O_WRONLY);
3917 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3918 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3919 EXPECT_EQ(0, test_ftruncate(fd_layer2));
3920
3921 ruleset_fd = create_ruleset(_metadata, handled3, layer3);
3922 ASSERT_LE(0, ruleset_fd);
3923 enforce_ruleset(_metadata, ruleset_fd);
3924 ASSERT_EQ(0, close(ruleset_fd));
3925
3926 fd_layer3 = open(path, O_WRONLY);
3927 EXPECT_EQ(0, test_ftruncate(fd_layer0));
3928 EXPECT_EQ(0, test_ftruncate(fd_layer1));
3929 EXPECT_EQ(0, test_ftruncate(fd_layer2));
3930 EXPECT_EQ(EACCES, test_ftruncate(fd_layer3));
3931
3932 ASSERT_EQ(0, close(fd_layer0));
3933 ASSERT_EQ(0, close(fd_layer1));
3934 ASSERT_EQ(0, close(fd_layer2));
3935 ASSERT_EQ(0, close(fd_layer3));
3936 }
3937
3938 /* clang-format off */
FIXTURE(ftruncate)3939 FIXTURE(ftruncate) {};
3940 /* clang-format on */
3941
FIXTURE_SETUP(ftruncate)3942 FIXTURE_SETUP(ftruncate)
3943 {
3944 prepare_layout(_metadata);
3945 create_file(_metadata, file1_s1d1);
3946 }
3947
FIXTURE_TEARDOWN_PARENT(ftruncate)3948 FIXTURE_TEARDOWN_PARENT(ftruncate)
3949 {
3950 EXPECT_EQ(0, remove_path(file1_s1d1));
3951 cleanup_layout(_metadata);
3952 }
3953
FIXTURE_VARIANT(ftruncate)3954 FIXTURE_VARIANT(ftruncate)
3955 {
3956 const __u64 handled;
3957 const __u64 allowed;
3958 const int expected_open_result;
3959 const int expected_ftruncate_result;
3960 };
3961
3962 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,w_w)3963 FIXTURE_VARIANT_ADD(ftruncate, w_w) {
3964 /* clang-format on */
3965 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE,
3966 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
3967 .expected_open_result = 0,
3968 .expected_ftruncate_result = 0,
3969 };
3970
3971 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,t_t)3972 FIXTURE_VARIANT_ADD(ftruncate, t_t) {
3973 /* clang-format on */
3974 .handled = LANDLOCK_ACCESS_FS_TRUNCATE,
3975 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,
3976 .expected_open_result = 0,
3977 .expected_ftruncate_result = 0,
3978 };
3979
3980 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_w)3981 FIXTURE_VARIANT_ADD(ftruncate, wt_w) {
3982 /* clang-format on */
3983 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3984 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE,
3985 .expected_open_result = 0,
3986 .expected_ftruncate_result = EACCES,
3987 };
3988
3989 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_wt)3990 FIXTURE_VARIANT_ADD(ftruncate, wt_wt) {
3991 /* clang-format on */
3992 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3993 .allowed = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
3994 .expected_open_result = 0,
3995 .expected_ftruncate_result = 0,
3996 };
3997
3998 /* clang-format off */
FIXTURE_VARIANT_ADD(ftruncate,wt_t)3999 FIXTURE_VARIANT_ADD(ftruncate, wt_t) {
4000 /* clang-format on */
4001 .handled = LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE,
4002 .allowed = LANDLOCK_ACCESS_FS_TRUNCATE,
4003 .expected_open_result = EACCES,
4004 };
4005
TEST_F_FORK(ftruncate,open_and_ftruncate)4006 TEST_F_FORK(ftruncate, open_and_ftruncate)
4007 {
4008 const char *const path = file1_s1d1;
4009 const struct rule rules[] = {
4010 {
4011 .path = path,
4012 .access = variant->allowed,
4013 },
4014 {},
4015 };
4016 int fd, ruleset_fd;
4017
4018 /* Enables Landlock. */
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 EXPECT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
4026 if (fd >= 0) {
4027 EXPECT_EQ(variant->expected_ftruncate_result,
4028 test_ftruncate(fd));
4029 ASSERT_EQ(0, close(fd));
4030 }
4031 }
4032
TEST_F_FORK(ftruncate,open_and_ftruncate_in_different_processes)4033 TEST_F_FORK(ftruncate, open_and_ftruncate_in_different_processes)
4034 {
4035 int child, fd, status;
4036 int socket_fds[2];
4037
4038 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
4039 socket_fds));
4040
4041 child = fork();
4042 ASSERT_LE(0, child);
4043 if (child == 0) {
4044 /*
4045 * Enables Landlock in the child process, open a file descriptor
4046 * where truncation is forbidden and send it to the
4047 * non-landlocked parent process.
4048 */
4049 const char *const path = file1_s1d1;
4050 const struct rule rules[] = {
4051 {
4052 .path = path,
4053 .access = variant->allowed,
4054 },
4055 {},
4056 };
4057 int fd, ruleset_fd;
4058
4059 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4060 ASSERT_LE(0, ruleset_fd);
4061 enforce_ruleset(_metadata, ruleset_fd);
4062 ASSERT_EQ(0, close(ruleset_fd));
4063
4064 fd = open(path, O_WRONLY);
4065 ASSERT_EQ(variant->expected_open_result, (fd < 0 ? errno : 0));
4066
4067 if (fd >= 0) {
4068 ASSERT_EQ(0, send_fd(socket_fds[0], fd));
4069 ASSERT_EQ(0, close(fd));
4070 }
4071
4072 ASSERT_EQ(0, close(socket_fds[0]));
4073
4074 _exit(_metadata->exit_code);
4075 return;
4076 }
4077
4078 if (variant->expected_open_result == 0) {
4079 fd = recv_fd(socket_fds[1]);
4080 ASSERT_LE(0, fd);
4081
4082 EXPECT_EQ(variant->expected_ftruncate_result,
4083 test_ftruncate(fd));
4084 ASSERT_EQ(0, close(fd));
4085 }
4086
4087 ASSERT_EQ(child, waitpid(child, &status, 0));
4088 ASSERT_EQ(1, WIFEXITED(status));
4089 ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
4090
4091 ASSERT_EQ(0, close(socket_fds[0]));
4092 ASSERT_EQ(0, close(socket_fds[1]));
4093 }
4094
4095 /* Invokes the FS_IOC_GETFLAGS IOCTL and returns its errno or 0. */
test_fs_ioc_getflags_ioctl(int fd)4096 static int test_fs_ioc_getflags_ioctl(int fd)
4097 {
4098 uint32_t flags;
4099
4100 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0)
4101 return errno;
4102 return 0;
4103 }
4104
TEST(memfd_ftruncate_and_ioctl)4105 TEST(memfd_ftruncate_and_ioctl)
4106 {
4107 const struct landlock_ruleset_attr attr = {
4108 .handled_access_fs = ACCESS_ALL,
4109 };
4110 int ruleset_fd, fd, i;
4111
4112 /*
4113 * We exercise the same test both with and without Landlock enabled, to
4114 * ensure that it behaves the same in both cases.
4115 */
4116 for (i = 0; i < 2; i++) {
4117 /* Creates a new memfd. */
4118 fd = memfd_create("name", MFD_CLOEXEC);
4119 ASSERT_LE(0, fd);
4120
4121 /*
4122 * Checks that operations associated with the opened file
4123 * (ftruncate, ioctl) are permitted on file descriptors that are
4124 * created in ways other than open(2).
4125 */
4126 EXPECT_EQ(0, test_ftruncate(fd));
4127 EXPECT_EQ(0, test_fs_ioc_getflags_ioctl(fd));
4128
4129 ASSERT_EQ(0, close(fd));
4130
4131 /* Enables Landlock. */
4132 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4133 ASSERT_LE(0, ruleset_fd);
4134 enforce_ruleset(_metadata, ruleset_fd);
4135 ASSERT_EQ(0, close(ruleset_fd));
4136 }
4137 }
4138
test_fionread_ioctl(int fd)4139 static int test_fionread_ioctl(int fd)
4140 {
4141 size_t sz = 0;
4142
4143 if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES)
4144 return errno;
4145 return 0;
4146 }
4147
TEST_F_FORK(layout1,o_path_ftruncate_and_ioctl)4148 TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)
4149 {
4150 const struct landlock_ruleset_attr attr = {
4151 .handled_access_fs = ACCESS_ALL,
4152 };
4153 int ruleset_fd, fd;
4154
4155 /*
4156 * Checks that for files opened with O_PATH, both ioctl(2) and
4157 * ftruncate(2) yield EBADF, as it is documented in open(2) for the
4158 * O_PATH flag.
4159 */
4160 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);
4161 ASSERT_LE(0, fd);
4162
4163 EXPECT_EQ(EBADF, test_ftruncate(fd));
4164 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));
4165
4166 ASSERT_EQ(0, close(fd));
4167
4168 /* Enables Landlock. */
4169 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4170 ASSERT_LE(0, ruleset_fd);
4171 enforce_ruleset(_metadata, ruleset_fd);
4172 ASSERT_EQ(0, close(ruleset_fd));
4173
4174 /*
4175 * Checks that after enabling Landlock,
4176 * - the file can still be opened with O_PATH
4177 * - both ioctl and truncate still yield EBADF (not EACCES).
4178 */
4179 fd = open(dir_s1d1, O_PATH | O_CLOEXEC);
4180 ASSERT_LE(0, fd);
4181
4182 EXPECT_EQ(EBADF, test_ftruncate(fd));
4183 EXPECT_EQ(EBADF, test_fs_ioc_getflags_ioctl(fd));
4184
4185 ASSERT_EQ(0, close(fd));
4186 }
4187
4188 /*
4189 * ioctl_error - generically call the given ioctl with a pointer to a
4190 * sufficiently large zeroed-out memory region.
4191 *
4192 * Returns the IOCTLs error, or 0.
4193 */
ioctl_error(struct __test_metadata * const _metadata,int fd,unsigned int cmd)4194 static int ioctl_error(struct __test_metadata *const _metadata, int fd,
4195 unsigned int cmd)
4196 {
4197 char buf[128]; /* sufficiently large */
4198 int res, stdinbak_fd;
4199
4200 /*
4201 * Depending on the IOCTL command, parts of the zeroed-out buffer might
4202 * be interpreted as file descriptor numbers. We do not want to
4203 * accidentally operate on file descriptor 0 (stdin), so we temporarily
4204 * move stdin to a different FD and close FD 0 for the IOCTL call.
4205 */
4206 stdinbak_fd = dup(0);
4207 ASSERT_LT(0, stdinbak_fd);
4208 ASSERT_EQ(0, close(0));
4209
4210 /* Invokes the IOCTL with a zeroed-out buffer. */
4211 bzero(&buf, sizeof(buf));
4212 res = ioctl(fd, cmd, &buf);
4213
4214 /* Restores the old FD 0 and closes the backup FD. */
4215 ASSERT_EQ(0, dup2(stdinbak_fd, 0));
4216 ASSERT_EQ(0, close(stdinbak_fd));
4217
4218 if (res < 0)
4219 return errno;
4220
4221 return 0;
4222 }
4223
4224 /* Define some linux/falloc.h IOCTL commands which are not available in uapi headers. */
4225 struct space_resv {
4226 __s16 l_type;
4227 __s16 l_whence;
4228 __s64 l_start;
4229 __s64 l_len; /* len == 0 means until end of file */
4230 __s32 l_sysid;
4231 __u32 l_pid;
4232 __s32 l_pad[4]; /* reserved area */
4233 };
4234
4235 #define FS_IOC_RESVSP _IOW('X', 40, struct space_resv)
4236 #define FS_IOC_UNRESVSP _IOW('X', 41, struct space_resv)
4237 #define FS_IOC_RESVSP64 _IOW('X', 42, struct space_resv)
4238 #define FS_IOC_UNRESVSP64 _IOW('X', 43, struct space_resv)
4239 #define FS_IOC_ZERO_RANGE _IOW('X', 57, struct space_resv)
4240
4241 /*
4242 * Tests a series of blanket-permitted and denied IOCTLs.
4243 */
TEST_F_FORK(layout1,blanket_permitted_ioctls)4244 TEST_F_FORK(layout1, blanket_permitted_ioctls)
4245 {
4246 const struct landlock_ruleset_attr attr = {
4247 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4248 };
4249 int ruleset_fd, fd;
4250
4251 /* Enables Landlock. */
4252 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4253 ASSERT_LE(0, ruleset_fd);
4254 enforce_ruleset(_metadata, ruleset_fd);
4255 ASSERT_EQ(0, close(ruleset_fd));
4256
4257 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
4258 ASSERT_LE(0, fd);
4259
4260 /*
4261 * Checks permitted commands.
4262 * These ones may return errors, but should not be blocked by Landlock.
4263 */
4264 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOCLEX));
4265 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONCLEX));
4266 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIONBIO));
4267 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOASYNC));
4268 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIOQSIZE));
4269 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIFREEZE));
4270 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FITHAW));
4271 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_FIEMAP));
4272 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIGETBSZ));
4273 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONE));
4274 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FICLONERANGE));
4275 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FIDEDUPERANGE));
4276 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSUUID));
4277 EXPECT_NE(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFSSYSFSPATH));
4278
4279 /*
4280 * Checks blocked commands.
4281 * A call to a blocked IOCTL command always returns EACCES.
4282 */
4283 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));
4284 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_GETFLAGS));
4285 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_SETFLAGS));
4286 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSGETXATTR));
4287 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_FSSETXATTR));
4288 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIBMAP));
4289 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP));
4290 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_RESVSP64));
4291 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP));
4292 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_UNRESVSP64));
4293 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE));
4294
4295 /* Default case is also blocked. */
4296 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee));
4297
4298 ASSERT_EQ(0, close(fd));
4299 }
4300
4301 /*
4302 * Named pipes are not governed by the LANDLOCK_ACCESS_FS_IOCTL_DEV right,
4303 * because they are not character or block devices.
4304 */
TEST_F_FORK(layout1,named_pipe_ioctl)4305 TEST_F_FORK(layout1, named_pipe_ioctl)
4306 {
4307 pid_t child_pid;
4308 int fd, ruleset_fd;
4309 const char *const path = file1_s1d1;
4310 const struct landlock_ruleset_attr attr = {
4311 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4312 };
4313
4314 ASSERT_EQ(0, unlink(path));
4315 ASSERT_EQ(0, mkfifo(path, 0600));
4316
4317 /* Enables Landlock. */
4318 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4319 ASSERT_LE(0, ruleset_fd);
4320 enforce_ruleset(_metadata, ruleset_fd);
4321 ASSERT_EQ(0, close(ruleset_fd));
4322
4323 /* The child process opens the pipe for writing. */
4324 child_pid = fork();
4325 ASSERT_NE(-1, child_pid);
4326 if (child_pid == 0) {
4327 fd = open(path, O_WRONLY);
4328 close(fd);
4329 exit(0);
4330 }
4331
4332 fd = open(path, O_RDONLY);
4333 ASSERT_LE(0, fd);
4334
4335 /* FIONREAD is implemented by pipefifo_fops. */
4336 EXPECT_EQ(0, test_fionread_ioctl(fd));
4337
4338 ASSERT_EQ(0, close(fd));
4339 ASSERT_EQ(0, unlink(path));
4340
4341 ASSERT_EQ(child_pid, waitpid(child_pid, NULL, 0));
4342 }
4343
4344 /* For named UNIX domain sockets, no IOCTL restrictions apply. */
TEST_F_FORK(layout1,named_unix_domain_socket_ioctl)4345 TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
4346 {
4347 const char *const path = file1_s1d1;
4348 int srv_fd, cli_fd, ruleset_fd;
4349 socklen_t size;
4350 struct sockaddr_un srv_un, cli_un;
4351 const struct landlock_ruleset_attr attr = {
4352 .handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4353 };
4354
4355 /* Sets up a server */
4356 srv_un.sun_family = AF_UNIX;
4357 strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));
4358
4359 ASSERT_EQ(0, unlink(path));
4360 srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
4361 ASSERT_LE(0, srv_fd);
4362
4363 size = offsetof(struct sockaddr_un, sun_path) + strlen(srv_un.sun_path);
4364 ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, size));
4365 ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */));
4366
4367 /* Enables Landlock. */
4368 ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
4369 ASSERT_LE(0, ruleset_fd);
4370 enforce_ruleset(_metadata, ruleset_fd);
4371 ASSERT_EQ(0, close(ruleset_fd));
4372
4373 /* Sets up a client connection to it */
4374 cli_un.sun_family = AF_UNIX;
4375 cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
4376 ASSERT_LE(0, cli_fd);
4377
4378 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);
4379 ASSERT_EQ(0, bind(cli_fd, (struct sockaddr *)&cli_un, size));
4380
4381 bzero(&cli_un, sizeof(cli_un));
4382 cli_un.sun_family = AF_UNIX;
4383 strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path));
4384 size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);
4385
4386 ASSERT_EQ(0, connect(cli_fd, (struct sockaddr *)&cli_un, size));
4387
4388 /* FIONREAD and other IOCTLs should not be forbidden. */
4389 EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
4390
4391 ASSERT_EQ(0, close(cli_fd));
4392 }
4393
4394 /* clang-format off */
FIXTURE(ioctl)4395 FIXTURE(ioctl) {};
4396
FIXTURE_SETUP(ioctl)4397 FIXTURE_SETUP(ioctl) {};
4398
FIXTURE_TEARDOWN(ioctl)4399 FIXTURE_TEARDOWN(ioctl) {};
4400 /* clang-format on */
4401
FIXTURE_VARIANT(ioctl)4402 FIXTURE_VARIANT(ioctl)
4403 {
4404 const __u64 handled;
4405 const __u64 allowed;
4406 const mode_t open_mode;
4407 /*
4408 * FIONREAD is used as a characteristic device-specific IOCTL command.
4409 * It is implemented in fs/ioctl.c for regular files,
4410 * but we do not blanket-permit it for devices.
4411 */
4412 const int expected_fionread_result;
4413 };
4414
4415 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,handled_i_allowed_none)4416 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) {
4417 /* clang-format on */
4418 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4419 .allowed = 0,
4420 .open_mode = O_RDWR,
4421 .expected_fionread_result = EACCES,
4422 };
4423
4424 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,handled_i_allowed_i)4425 FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) {
4426 /* clang-format on */
4427 .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4428 .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV,
4429 .open_mode = O_RDWR,
4430 .expected_fionread_result = 0,
4431 };
4432
4433 /* clang-format off */
FIXTURE_VARIANT_ADD(ioctl,unhandled)4434 FIXTURE_VARIANT_ADD(ioctl, unhandled) {
4435 /* clang-format on */
4436 .handled = LANDLOCK_ACCESS_FS_EXECUTE,
4437 .allowed = LANDLOCK_ACCESS_FS_EXECUTE,
4438 .open_mode = O_RDWR,
4439 .expected_fionread_result = 0,
4440 };
4441
TEST_F_FORK(ioctl,handle_dir_access_file)4442 TEST_F_FORK(ioctl, handle_dir_access_file)
4443 {
4444 const int flag = 0;
4445 const struct rule rules[] = {
4446 {
4447 .path = "/dev",
4448 .access = variant->allowed,
4449 },
4450 {},
4451 };
4452 int file_fd, ruleset_fd;
4453
4454 /* Enables Landlock. */
4455 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4456 ASSERT_LE(0, ruleset_fd);
4457 enforce_ruleset(_metadata, ruleset_fd);
4458 ASSERT_EQ(0, close(ruleset_fd));
4459
4460 file_fd = open("/dev/zero", variant->open_mode);
4461 ASSERT_LE(0, file_fd);
4462
4463 /* Checks that IOCTL commands return the expected errors. */
4464 EXPECT_EQ(variant->expected_fionread_result,
4465 test_fionread_ioctl(file_fd));
4466
4467 /* Checks that unrestrictable commands are unrestricted. */
4468 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
4469 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
4470 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
4471 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
4472 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));
4473
4474 ASSERT_EQ(0, close(file_fd));
4475 }
4476
TEST_F_FORK(ioctl,handle_dir_access_dir)4477 TEST_F_FORK(ioctl, handle_dir_access_dir)
4478 {
4479 const int flag = 0;
4480 const struct rule rules[] = {
4481 {
4482 .path = "/dev",
4483 .access = variant->allowed,
4484 },
4485 {},
4486 };
4487 int dir_fd, ruleset_fd;
4488
4489 /* Enables Landlock. */
4490 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4491 ASSERT_LE(0, ruleset_fd);
4492 enforce_ruleset(_metadata, ruleset_fd);
4493 ASSERT_EQ(0, close(ruleset_fd));
4494
4495 /*
4496 * Ignore variant->open_mode for this test, as we intend to open a
4497 * directory. If the directory can not be opened, the variant is
4498 * infeasible to test with an opened directory.
4499 */
4500 dir_fd = open("/dev", O_RDONLY);
4501 if (dir_fd < 0)
4502 return;
4503
4504 /*
4505 * Checks that IOCTL commands return the expected errors.
4506 * We do not use the expected values from the fixture here.
4507 *
4508 * When using IOCTL on a directory, no Landlock restrictions apply.
4509 */
4510 EXPECT_EQ(0, test_fionread_ioctl(dir_fd));
4511
4512 /* Checks that unrestrictable commands are unrestricted. */
4513 EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX));
4514 EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX));
4515 EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag));
4516 EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag));
4517 EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag));
4518
4519 ASSERT_EQ(0, close(dir_fd));
4520 }
4521
TEST_F_FORK(ioctl,handle_file_access_file)4522 TEST_F_FORK(ioctl, handle_file_access_file)
4523 {
4524 const int flag = 0;
4525 const struct rule rules[] = {
4526 {
4527 .path = "/dev/zero",
4528 .access = variant->allowed,
4529 },
4530 {},
4531 };
4532 int file_fd, ruleset_fd;
4533
4534 /* Enables Landlock. */
4535 ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
4536 ASSERT_LE(0, ruleset_fd);
4537 enforce_ruleset(_metadata, ruleset_fd);
4538 ASSERT_EQ(0, close(ruleset_fd));
4539
4540 file_fd = open("/dev/zero", variant->open_mode);
4541 ASSERT_LE(0, file_fd)
4542 {
4543 TH_LOG("Failed to open /dev/zero: %s", strerror(errno));
4544 }
4545
4546 /* Checks that IOCTL commands return the expected errors. */
4547 EXPECT_EQ(variant->expected_fionread_result,
4548 test_fionread_ioctl(file_fd));
4549
4550 /* Checks that unrestrictable commands are unrestricted. */
4551 EXPECT_EQ(0, ioctl(file_fd, FIOCLEX));
4552 EXPECT_EQ(0, ioctl(file_fd, FIONCLEX));
4553 EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag));
4554 EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag));
4555 EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag));
4556
4557 ASSERT_EQ(0, close(file_fd));
4558 }
4559
4560 /* clang-format off */
FIXTURE(layout1_bind)4561 FIXTURE(layout1_bind) {};
4562 /* clang-format on */
4563
FIXTURE_SETUP(layout1_bind)4564 FIXTURE_SETUP(layout1_bind)
4565 {
4566 prepare_layout(_metadata);
4567
4568 create_layout1(_metadata);
4569
4570 set_cap(_metadata, CAP_SYS_ADMIN);
4571 ASSERT_EQ(0, mount(dir_s1d2, dir_s2d2, NULL, MS_BIND, NULL));
4572 clear_cap(_metadata, CAP_SYS_ADMIN);
4573 }
4574
FIXTURE_TEARDOWN_PARENT(layout1_bind)4575 FIXTURE_TEARDOWN_PARENT(layout1_bind)
4576 {
4577 /* umount(dir_s2d2)) is handled by namespace lifetime. */
4578
4579 remove_layout1(_metadata);
4580
4581 cleanup_layout(_metadata);
4582 }
4583
4584 static const char bind_dir_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3";
4585 static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";
4586
4587 /*
4588 * layout1_bind hierarchy:
4589 *
4590 * tmp
4591 * ├── s1d1
4592 * │ ├── f1
4593 * │ ├── f2
4594 * │ └── s1d2
4595 * │ ├── f1
4596 * │ ├── f2
4597 * │ └── s1d3
4598 * │ ├── f1
4599 * │ └── f2
4600 * ├── s2d1
4601 * │ ├── f1
4602 * │ └── s2d2
4603 * │ ├── f1
4604 * │ ├── f2
4605 * │ └── s1d3
4606 * │ ├── f1
4607 * │ └── f2
4608 * └── s3d1
4609 * └── s3d2
4610 * └── s3d3
4611 */
4612
TEST_F_FORK(layout1_bind,no_restriction)4613 TEST_F_FORK(layout1_bind, no_restriction)
4614 {
4615 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY));
4616 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
4617 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
4618 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
4619 ASSERT_EQ(0, test_open(dir_s1d3, O_RDONLY));
4620 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
4621
4622 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY));
4623 ASSERT_EQ(0, test_open(file1_s2d1, O_RDONLY));
4624 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY));
4625 ASSERT_EQ(0, test_open(file1_s2d2, O_RDONLY));
4626 ASSERT_EQ(ENOENT, test_open(dir_s2d3, O_RDONLY));
4627 ASSERT_EQ(ENOENT, test_open(file1_s2d3, O_RDONLY));
4628
4629 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY));
4630 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
4631
4632 ASSERT_EQ(0, test_open(dir_s3d1, O_RDONLY));
4633 }
4634
TEST_F_FORK(layout1_bind,same_content_same_file)4635 TEST_F_FORK(layout1_bind, same_content_same_file)
4636 {
4637 /*
4638 * Sets access right on parent directories of both source and
4639 * destination mount points.
4640 */
4641 const struct rule layer1_parent[] = {
4642 {
4643 .path = dir_s1d1,
4644 .access = ACCESS_RO,
4645 },
4646 {
4647 .path = dir_s2d1,
4648 .access = ACCESS_RW,
4649 },
4650 {},
4651 };
4652 /*
4653 * Sets access rights on the same bind-mounted directories. The result
4654 * should be ACCESS_RW for both directories, but not both hierarchies
4655 * because of the first layer.
4656 */
4657 const struct rule layer2_mount_point[] = {
4658 {
4659 .path = dir_s1d2,
4660 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4661 },
4662 {
4663 .path = dir_s2d2,
4664 .access = ACCESS_RW,
4665 },
4666 {},
4667 };
4668 /* Only allow read-access to the s1d3 hierarchies. */
4669 const struct rule layer3_source[] = {
4670 {
4671 .path = dir_s1d3,
4672 .access = LANDLOCK_ACCESS_FS_READ_FILE,
4673 },
4674 {},
4675 };
4676 /* Removes all access rights. */
4677 const struct rule layer4_destination[] = {
4678 {
4679 .path = bind_file1_s1d3,
4680 .access = LANDLOCK_ACCESS_FS_WRITE_FILE,
4681 },
4682 {},
4683 };
4684 int ruleset_fd;
4685
4686 /* Sets rules for the parent directories. */
4687 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_parent);
4688 ASSERT_LE(0, ruleset_fd);
4689 enforce_ruleset(_metadata, ruleset_fd);
4690 ASSERT_EQ(0, close(ruleset_fd));
4691
4692 /* Checks source hierarchy. */
4693 ASSERT_EQ(0, test_open(file1_s1d1, O_RDONLY));
4694 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
4695 ASSERT_EQ(0, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
4696
4697 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
4698 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
4699 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
4700
4701 /* Checks destination hierarchy. */
4702 ASSERT_EQ(0, test_open(file1_s2d1, O_RDWR));
4703 ASSERT_EQ(0, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
4704
4705 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
4706 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
4707
4708 /* Sets rules for the mount points. */
4709 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_mount_point);
4710 ASSERT_LE(0, ruleset_fd);
4711 enforce_ruleset(_metadata, ruleset_fd);
4712 ASSERT_EQ(0, close(ruleset_fd));
4713
4714 /* Checks source hierarchy. */
4715 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
4716 ASSERT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
4717 ASSERT_EQ(EACCES, test_open(dir_s1d1, O_RDONLY | O_DIRECTORY));
4718
4719 ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
4720 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
4721 ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
4722
4723 /* Checks destination hierarchy. */
4724 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_RDONLY));
4725 ASSERT_EQ(EACCES, test_open(file1_s2d1, O_WRONLY));
4726 ASSERT_EQ(EACCES, test_open(dir_s2d1, O_RDONLY | O_DIRECTORY));
4727
4728 ASSERT_EQ(0, test_open(file1_s2d2, O_RDWR));
4729 ASSERT_EQ(0, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
4730 ASSERT_EQ(0, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
4731
4732 /* Sets a (shared) rule only on the source. */
4733 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_source);
4734 ASSERT_LE(0, ruleset_fd);
4735 enforce_ruleset(_metadata, ruleset_fd);
4736 ASSERT_EQ(0, close(ruleset_fd));
4737
4738 /* Checks source hierarchy. */
4739 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_RDONLY));
4740 ASSERT_EQ(EACCES, test_open(file1_s1d2, O_WRONLY));
4741 ASSERT_EQ(EACCES, test_open(dir_s1d2, O_RDONLY | O_DIRECTORY));
4742
4743 ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
4744 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
4745 ASSERT_EQ(EACCES, test_open(dir_s1d3, O_RDONLY | O_DIRECTORY));
4746
4747 /* Checks destination hierarchy. */
4748 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_RDONLY));
4749 ASSERT_EQ(EACCES, test_open(file1_s2d2, O_WRONLY));
4750 ASSERT_EQ(EACCES, test_open(dir_s2d2, O_RDONLY | O_DIRECTORY));
4751
4752 ASSERT_EQ(0, test_open(bind_file1_s1d3, O_RDONLY));
4753 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
4754 ASSERT_EQ(EACCES, test_open(bind_dir_s1d3, O_RDONLY | O_DIRECTORY));
4755
4756 /* Sets a (shared) rule only on the destination. */
4757 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_destination);
4758 ASSERT_LE(0, ruleset_fd);
4759 enforce_ruleset(_metadata, ruleset_fd);
4760 ASSERT_EQ(0, close(ruleset_fd));
4761
4762 /* Checks source hierarchy. */
4763 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_RDONLY));
4764 ASSERT_EQ(EACCES, test_open(file1_s1d3, O_WRONLY));
4765
4766 /* Checks destination hierarchy. */
4767 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_RDONLY));
4768 ASSERT_EQ(EACCES, test_open(bind_file1_s1d3, O_WRONLY));
4769 }
4770
TEST_F_FORK(layout1_bind,reparent_cross_mount)4771 TEST_F_FORK(layout1_bind, reparent_cross_mount)
4772 {
4773 const struct rule layer1[] = {
4774 {
4775 /* dir_s2d1 is beneath the dir_s2d2 mount point. */
4776 .path = dir_s2d1,
4777 .access = LANDLOCK_ACCESS_FS_REFER,
4778 },
4779 {
4780 .path = bind_dir_s1d3,
4781 .access = LANDLOCK_ACCESS_FS_EXECUTE,
4782 },
4783 {},
4784 };
4785 int ruleset_fd = create_ruleset(
4786 _metadata,
4787 LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_EXECUTE, layer1);
4788
4789 ASSERT_LE(0, ruleset_fd);
4790 enforce_ruleset(_metadata, ruleset_fd);
4791 ASSERT_EQ(0, close(ruleset_fd));
4792
4793 /* Checks basic denied move. */
4794 ASSERT_EQ(-1, rename(file1_s1d1, file1_s1d2));
4795 ASSERT_EQ(EXDEV, errno);
4796
4797 /* Checks real cross-mount move (Landlock is not involved). */
4798 ASSERT_EQ(-1, rename(file1_s2d1, file1_s2d2));
4799 ASSERT_EQ(EXDEV, errno);
4800
4801 /* Checks move that will give more accesses. */
4802 ASSERT_EQ(-1, rename(file1_s2d2, bind_file1_s1d3));
4803 ASSERT_EQ(EXDEV, errno);
4804
4805 /* Checks legitimate downgrade move. */
4806 ASSERT_EQ(0, rename(bind_file1_s1d3, file1_s2d2));
4807 }
4808
4809 #define LOWER_BASE TMP_DIR "/lower"
4810 #define LOWER_DATA LOWER_BASE "/data"
4811 static const char lower_fl1[] = LOWER_DATA "/fl1";
4812 static const char lower_dl1[] = LOWER_DATA "/dl1";
4813 static const char lower_dl1_fl2[] = LOWER_DATA "/dl1/fl2";
4814 static const char lower_fo1[] = LOWER_DATA "/fo1";
4815 static const char lower_do1[] = LOWER_DATA "/do1";
4816 static const char lower_do1_fo2[] = LOWER_DATA "/do1/fo2";
4817 static const char lower_do1_fl3[] = LOWER_DATA "/do1/fl3";
4818
4819 static const char (*lower_base_files[])[] = {
4820 &lower_fl1,
4821 &lower_fo1,
4822 NULL,
4823 };
4824 static const char (*lower_base_directories[])[] = {
4825 &lower_dl1,
4826 &lower_do1,
4827 NULL,
4828 };
4829 static const char (*lower_sub_files[])[] = {
4830 &lower_dl1_fl2,
4831 &lower_do1_fo2,
4832 &lower_do1_fl3,
4833 NULL,
4834 };
4835
4836 #define UPPER_BASE TMP_DIR "/upper"
4837 #define UPPER_DATA UPPER_BASE "/data"
4838 #define UPPER_WORK UPPER_BASE "/work"
4839 static const char upper_fu1[] = UPPER_DATA "/fu1";
4840 static const char upper_du1[] = UPPER_DATA "/du1";
4841 static const char upper_du1_fu2[] = UPPER_DATA "/du1/fu2";
4842 static const char upper_fo1[] = UPPER_DATA "/fo1";
4843 static const char upper_do1[] = UPPER_DATA "/do1";
4844 static const char upper_do1_fo2[] = UPPER_DATA "/do1/fo2";
4845 static const char upper_do1_fu3[] = UPPER_DATA "/do1/fu3";
4846
4847 static const char (*upper_base_files[])[] = {
4848 &upper_fu1,
4849 &upper_fo1,
4850 NULL,
4851 };
4852 static const char (*upper_base_directories[])[] = {
4853 &upper_du1,
4854 &upper_do1,
4855 NULL,
4856 };
4857 static const char (*upper_sub_files[])[] = {
4858 &upper_du1_fu2,
4859 &upper_do1_fo2,
4860 &upper_do1_fu3,
4861 NULL,
4862 };
4863
4864 #define MERGE_BASE TMP_DIR "/merge"
4865 #define MERGE_DATA MERGE_BASE "/data"
4866 static const char merge_fl1[] = MERGE_DATA "/fl1";
4867 static const char merge_dl1[] = MERGE_DATA "/dl1";
4868 static const char merge_dl1_fl2[] = MERGE_DATA "/dl1/fl2";
4869 static const char merge_fu1[] = MERGE_DATA "/fu1";
4870 static const char merge_du1[] = MERGE_DATA "/du1";
4871 static const char merge_du1_fu2[] = MERGE_DATA "/du1/fu2";
4872 static const char merge_fo1[] = MERGE_DATA "/fo1";
4873 static const char merge_do1[] = MERGE_DATA "/do1";
4874 static const char merge_do1_fo2[] = MERGE_DATA "/do1/fo2";
4875 static const char merge_do1_fl3[] = MERGE_DATA "/do1/fl3";
4876 static const char merge_do1_fu3[] = MERGE_DATA "/do1/fu3";
4877
4878 static const char (*merge_base_files[])[] = {
4879 &merge_fl1,
4880 &merge_fu1,
4881 &merge_fo1,
4882 NULL,
4883 };
4884 static const char (*merge_base_directories[])[] = {
4885 &merge_dl1,
4886 &merge_du1,
4887 &merge_do1,
4888 NULL,
4889 };
4890 static const char (*merge_sub_files[])[] = {
4891 &merge_dl1_fl2, &merge_du1_fu2, &merge_do1_fo2,
4892 &merge_do1_fl3, &merge_do1_fu3, NULL,
4893 };
4894
4895 /*
4896 * layout2_overlay hierarchy:
4897 *
4898 * tmp
4899 * ├── lower
4900 * │ └── data
4901 * │ ├── dl1
4902 * │ │ └── fl2
4903 * │ ├── do1
4904 * │ │ ├── fl3
4905 * │ │ └── fo2
4906 * │ ├── fl1
4907 * │ └── fo1
4908 * ├── merge
4909 * │ └── data
4910 * │ ├── dl1
4911 * │ │ └── fl2
4912 * │ ├── do1
4913 * │ │ ├── fl3
4914 * │ │ ├── fo2
4915 * │ │ └── fu3
4916 * │ ├── du1
4917 * │ │ └── fu2
4918 * │ ├── fl1
4919 * │ ├── fo1
4920 * │ └── fu1
4921 * └── upper
4922 * ├── data
4923 * │ ├── do1
4924 * │ │ ├── fo2
4925 * │ │ └── fu3
4926 * │ ├── du1
4927 * │ │ └── fu2
4928 * │ ├── fo1
4929 * │ └── fu1
4930 * └── work
4931 * └── work
4932 */
4933
FIXTURE(layout2_overlay)4934 FIXTURE(layout2_overlay)
4935 {
4936 bool skip_test;
4937 };
4938
FIXTURE_SETUP(layout2_overlay)4939 FIXTURE_SETUP(layout2_overlay)
4940 {
4941 if (!supports_filesystem("overlay")) {
4942 self->skip_test = true;
4943 SKIP(return, "overlayfs is not supported (setup)");
4944 }
4945
4946 prepare_layout(_metadata);
4947
4948 create_directory(_metadata, LOWER_BASE);
4949 set_cap(_metadata, CAP_SYS_ADMIN);
4950 /* Creates tmpfs mount points to get deterministic overlayfs. */
4951 ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));
4952 clear_cap(_metadata, CAP_SYS_ADMIN);
4953 create_file(_metadata, lower_fl1);
4954 create_file(_metadata, lower_dl1_fl2);
4955 create_file(_metadata, lower_fo1);
4956 create_file(_metadata, lower_do1_fo2);
4957 create_file(_metadata, lower_do1_fl3);
4958
4959 create_directory(_metadata, UPPER_BASE);
4960 set_cap(_metadata, CAP_SYS_ADMIN);
4961 ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));
4962 clear_cap(_metadata, CAP_SYS_ADMIN);
4963 create_file(_metadata, upper_fu1);
4964 create_file(_metadata, upper_du1_fu2);
4965 create_file(_metadata, upper_fo1);
4966 create_file(_metadata, upper_do1_fo2);
4967 create_file(_metadata, upper_do1_fu3);
4968 ASSERT_EQ(0, mkdir(UPPER_WORK, 0700));
4969
4970 create_directory(_metadata, MERGE_DATA);
4971 set_cap(_metadata, CAP_SYS_ADMIN);
4972 set_cap(_metadata, CAP_DAC_OVERRIDE);
4973 ASSERT_EQ(0, mount("overlay", MERGE_DATA, "overlay", 0,
4974 "lowerdir=" LOWER_DATA ",upperdir=" UPPER_DATA
4975 ",workdir=" UPPER_WORK));
4976 clear_cap(_metadata, CAP_DAC_OVERRIDE);
4977 clear_cap(_metadata, CAP_SYS_ADMIN);
4978 }
4979
FIXTURE_TEARDOWN_PARENT(layout2_overlay)4980 FIXTURE_TEARDOWN_PARENT(layout2_overlay)
4981 {
4982 if (self->skip_test)
4983 SKIP(return, "overlayfs is not supported (teardown)");
4984
4985 EXPECT_EQ(0, remove_path(lower_do1_fl3));
4986 EXPECT_EQ(0, remove_path(lower_dl1_fl2));
4987 EXPECT_EQ(0, remove_path(lower_fl1));
4988 EXPECT_EQ(0, remove_path(lower_do1_fo2));
4989 EXPECT_EQ(0, remove_path(lower_fo1));
4990
4991 /* umount(LOWER_BASE)) is handled by namespace lifetime. */
4992 EXPECT_EQ(0, remove_path(LOWER_BASE));
4993
4994 EXPECT_EQ(0, remove_path(upper_do1_fu3));
4995 EXPECT_EQ(0, remove_path(upper_du1_fu2));
4996 EXPECT_EQ(0, remove_path(upper_fu1));
4997 EXPECT_EQ(0, remove_path(upper_do1_fo2));
4998 EXPECT_EQ(0, remove_path(upper_fo1));
4999 EXPECT_EQ(0, remove_path(UPPER_WORK "/work"));
5000
5001 /* umount(UPPER_BASE)) is handled by namespace lifetime. */
5002 EXPECT_EQ(0, remove_path(UPPER_BASE));
5003
5004 /* umount(MERGE_DATA)) is handled by namespace lifetime. */
5005 EXPECT_EQ(0, remove_path(MERGE_DATA));
5006
5007 cleanup_layout(_metadata);
5008 }
5009
TEST_F_FORK(layout2_overlay,no_restriction)5010 TEST_F_FORK(layout2_overlay, no_restriction)
5011 {
5012 if (self->skip_test)
5013 SKIP(return, "overlayfs is not supported (test)");
5014
5015 ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
5016 ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
5017 ASSERT_EQ(0, test_open(lower_dl1_fl2, O_RDONLY));
5018 ASSERT_EQ(0, test_open(lower_fo1, O_RDONLY));
5019 ASSERT_EQ(0, test_open(lower_do1, O_RDONLY));
5020 ASSERT_EQ(0, test_open(lower_do1_fo2, O_RDONLY));
5021 ASSERT_EQ(0, test_open(lower_do1_fl3, O_RDONLY));
5022
5023 ASSERT_EQ(0, test_open(upper_fu1, O_RDONLY));
5024 ASSERT_EQ(0, test_open(upper_du1, O_RDONLY));
5025 ASSERT_EQ(0, test_open(upper_du1_fu2, O_RDONLY));
5026 ASSERT_EQ(0, test_open(upper_fo1, O_RDONLY));
5027 ASSERT_EQ(0, test_open(upper_do1, O_RDONLY));
5028 ASSERT_EQ(0, test_open(upper_do1_fo2, O_RDONLY));
5029 ASSERT_EQ(0, test_open(upper_do1_fu3, O_RDONLY));
5030
5031 ASSERT_EQ(0, test_open(merge_fl1, O_RDONLY));
5032 ASSERT_EQ(0, test_open(merge_dl1, O_RDONLY));
5033 ASSERT_EQ(0, test_open(merge_dl1_fl2, O_RDONLY));
5034 ASSERT_EQ(0, test_open(merge_fu1, O_RDONLY));
5035 ASSERT_EQ(0, test_open(merge_du1, O_RDONLY));
5036 ASSERT_EQ(0, test_open(merge_du1_fu2, O_RDONLY));
5037 ASSERT_EQ(0, test_open(merge_fo1, O_RDONLY));
5038 ASSERT_EQ(0, test_open(merge_do1, O_RDONLY));
5039 ASSERT_EQ(0, test_open(merge_do1_fo2, O_RDONLY));
5040 ASSERT_EQ(0, test_open(merge_do1_fl3, O_RDONLY));
5041 ASSERT_EQ(0, test_open(merge_do1_fu3, O_RDONLY));
5042 }
5043
5044 #define for_each_path(path_list, path_entry, i) \
5045 for (i = 0, path_entry = *path_list[i]; path_list[i]; \
5046 path_entry = *path_list[++i])
5047
TEST_F_FORK(layout2_overlay,same_content_different_file)5048 TEST_F_FORK(layout2_overlay, same_content_different_file)
5049 {
5050 /* Sets access right on parent directories of both layers. */
5051 const struct rule layer1_base[] = {
5052 {
5053 .path = LOWER_BASE,
5054 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5055 },
5056 {
5057 .path = UPPER_BASE,
5058 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5059 },
5060 {
5061 .path = MERGE_BASE,
5062 .access = ACCESS_RW,
5063 },
5064 {},
5065 };
5066 const struct rule layer2_data[] = {
5067 {
5068 .path = LOWER_DATA,
5069 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5070 },
5071 {
5072 .path = UPPER_DATA,
5073 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5074 },
5075 {
5076 .path = MERGE_DATA,
5077 .access = ACCESS_RW,
5078 },
5079 {},
5080 };
5081 /* Sets access right on directories inside both layers. */
5082 const struct rule layer3_subdirs[] = {
5083 {
5084 .path = lower_dl1,
5085 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5086 },
5087 {
5088 .path = lower_do1,
5089 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5090 },
5091 {
5092 .path = upper_du1,
5093 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5094 },
5095 {
5096 .path = upper_do1,
5097 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5098 },
5099 {
5100 .path = merge_dl1,
5101 .access = ACCESS_RW,
5102 },
5103 {
5104 .path = merge_du1,
5105 .access = ACCESS_RW,
5106 },
5107 {
5108 .path = merge_do1,
5109 .access = ACCESS_RW,
5110 },
5111 {},
5112 };
5113 /* Tighten access rights to the files. */
5114 const struct rule layer4_files[] = {
5115 {
5116 .path = lower_dl1_fl2,
5117 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5118 },
5119 {
5120 .path = lower_do1_fo2,
5121 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5122 },
5123 {
5124 .path = lower_do1_fl3,
5125 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5126 },
5127 {
5128 .path = upper_du1_fu2,
5129 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5130 },
5131 {
5132 .path = upper_do1_fo2,
5133 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5134 },
5135 {
5136 .path = upper_do1_fu3,
5137 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5138 },
5139 {
5140 .path = merge_dl1_fl2,
5141 .access = LANDLOCK_ACCESS_FS_READ_FILE |
5142 LANDLOCK_ACCESS_FS_WRITE_FILE,
5143 },
5144 {
5145 .path = merge_du1_fu2,
5146 .access = LANDLOCK_ACCESS_FS_READ_FILE |
5147 LANDLOCK_ACCESS_FS_WRITE_FILE,
5148 },
5149 {
5150 .path = merge_do1_fo2,
5151 .access = LANDLOCK_ACCESS_FS_READ_FILE |
5152 LANDLOCK_ACCESS_FS_WRITE_FILE,
5153 },
5154 {
5155 .path = merge_do1_fl3,
5156 .access = LANDLOCK_ACCESS_FS_READ_FILE |
5157 LANDLOCK_ACCESS_FS_WRITE_FILE,
5158 },
5159 {
5160 .path = merge_do1_fu3,
5161 .access = LANDLOCK_ACCESS_FS_READ_FILE |
5162 LANDLOCK_ACCESS_FS_WRITE_FILE,
5163 },
5164 {},
5165 };
5166 const struct rule layer5_merge_only[] = {
5167 {
5168 .path = MERGE_DATA,
5169 .access = LANDLOCK_ACCESS_FS_READ_FILE |
5170 LANDLOCK_ACCESS_FS_WRITE_FILE,
5171 },
5172 {},
5173 };
5174 int ruleset_fd;
5175 size_t i;
5176 const char *path_entry;
5177
5178 if (self->skip_test)
5179 SKIP(return, "overlayfs is not supported (test)");
5180
5181 /* Sets rules on base directories (i.e. outside overlay scope). */
5182 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
5183 ASSERT_LE(0, ruleset_fd);
5184 enforce_ruleset(_metadata, ruleset_fd);
5185 ASSERT_EQ(0, close(ruleset_fd));
5186
5187 /* Checks lower layer. */
5188 for_each_path(lower_base_files, path_entry, i) {
5189 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5190 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5191 }
5192 for_each_path(lower_base_directories, path_entry, i) {
5193 ASSERT_EQ(EACCES,
5194 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5195 }
5196 for_each_path(lower_sub_files, path_entry, i) {
5197 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5198 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5199 }
5200 /* Checks upper layer. */
5201 for_each_path(upper_base_files, path_entry, i) {
5202 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5203 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5204 }
5205 for_each_path(upper_base_directories, path_entry, i) {
5206 ASSERT_EQ(EACCES,
5207 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5208 }
5209 for_each_path(upper_sub_files, path_entry, i) {
5210 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5211 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5212 }
5213 /*
5214 * Checks that access rights are independent from the lower and upper
5215 * layers: write access to upper files viewed through the merge point
5216 * is still allowed, and write access to lower file viewed (and copied)
5217 * through the merge point is still allowed.
5218 */
5219 for_each_path(merge_base_files, path_entry, i) {
5220 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5221 }
5222 for_each_path(merge_base_directories, path_entry, i) {
5223 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
5224 }
5225 for_each_path(merge_sub_files, path_entry, i) {
5226 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5227 }
5228
5229 /* Sets rules on data directories (i.e. inside overlay scope). */
5230 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer2_data);
5231 ASSERT_LE(0, ruleset_fd);
5232 enforce_ruleset(_metadata, ruleset_fd);
5233 ASSERT_EQ(0, close(ruleset_fd));
5234
5235 /* Checks merge. */
5236 for_each_path(merge_base_files, path_entry, i) {
5237 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5238 }
5239 for_each_path(merge_base_directories, path_entry, i) {
5240 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
5241 }
5242 for_each_path(merge_sub_files, path_entry, i) {
5243 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5244 }
5245
5246 /* Same checks with tighter rules. */
5247 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer3_subdirs);
5248 ASSERT_LE(0, ruleset_fd);
5249 enforce_ruleset(_metadata, ruleset_fd);
5250 ASSERT_EQ(0, close(ruleset_fd));
5251
5252 /* Checks changes for lower layer. */
5253 for_each_path(lower_base_files, path_entry, i) {
5254 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5255 }
5256 /* Checks changes for upper layer. */
5257 for_each_path(upper_base_files, path_entry, i) {
5258 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5259 }
5260 /* Checks all merge accesses. */
5261 for_each_path(merge_base_files, path_entry, i) {
5262 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
5263 }
5264 for_each_path(merge_base_directories, path_entry, i) {
5265 ASSERT_EQ(0, test_open(path_entry, O_RDONLY | O_DIRECTORY));
5266 }
5267 for_each_path(merge_sub_files, path_entry, i) {
5268 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5269 }
5270
5271 /* Sets rules directly on overlayed files. */
5272 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer4_files);
5273 ASSERT_LE(0, ruleset_fd);
5274 enforce_ruleset(_metadata, ruleset_fd);
5275 ASSERT_EQ(0, close(ruleset_fd));
5276
5277 /* Checks unchanged accesses on lower layer. */
5278 for_each_path(lower_sub_files, path_entry, i) {
5279 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5280 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5281 }
5282 /* Checks unchanged accesses on upper layer. */
5283 for_each_path(upper_sub_files, path_entry, i) {
5284 ASSERT_EQ(0, test_open(path_entry, O_RDONLY));
5285 ASSERT_EQ(EACCES, test_open(path_entry, O_WRONLY));
5286 }
5287 /* Checks all merge accesses. */
5288 for_each_path(merge_base_files, path_entry, i) {
5289 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
5290 }
5291 for_each_path(merge_base_directories, path_entry, i) {
5292 ASSERT_EQ(EACCES,
5293 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5294 }
5295 for_each_path(merge_sub_files, path_entry, i) {
5296 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5297 }
5298
5299 /* Only allowes access to the merge hierarchy. */
5300 ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer5_merge_only);
5301 ASSERT_LE(0, ruleset_fd);
5302 enforce_ruleset(_metadata, ruleset_fd);
5303 ASSERT_EQ(0, close(ruleset_fd));
5304
5305 /* Checks new accesses on lower layer. */
5306 for_each_path(lower_sub_files, path_entry, i) {
5307 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5308 }
5309 /* Checks new accesses on upper layer. */
5310 for_each_path(upper_sub_files, path_entry, i) {
5311 ASSERT_EQ(EACCES, test_open(path_entry, O_RDONLY));
5312 }
5313 /* Checks all merge accesses. */
5314 for_each_path(merge_base_files, path_entry, i) {
5315 ASSERT_EQ(EACCES, test_open(path_entry, O_RDWR));
5316 }
5317 for_each_path(merge_base_directories, path_entry, i) {
5318 ASSERT_EQ(EACCES,
5319 test_open(path_entry, O_RDONLY | O_DIRECTORY));
5320 }
5321 for_each_path(merge_sub_files, path_entry, i) {
5322 ASSERT_EQ(0, test_open(path_entry, O_RDWR));
5323 }
5324 }
5325
FIXTURE(layout3_fs)5326 FIXTURE(layout3_fs)
5327 {
5328 bool has_created_dir;
5329 bool has_created_file;
5330 bool skip_test;
5331 };
5332
FIXTURE_VARIANT(layout3_fs)5333 FIXTURE_VARIANT(layout3_fs)
5334 {
5335 const struct mnt_opt mnt;
5336 const char *const file_path;
5337 unsigned int cwd_fs_magic;
5338 };
5339
5340 /* clang-format off */
FIXTURE_VARIANT_ADD(layout3_fs,tmpfs)5341 FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {
5342 /* clang-format on */
5343 .mnt = {
5344 .type = "tmpfs",
5345 .data = MNT_TMP_DATA,
5346 },
5347 .file_path = file1_s1d1,
5348 };
5349
FIXTURE_VARIANT_ADD(layout3_fs,ramfs)5350 FIXTURE_VARIANT_ADD(layout3_fs, ramfs) {
5351 .mnt = {
5352 .type = "ramfs",
5353 .data = "mode=700",
5354 },
5355 .file_path = TMP_DIR "/dir/file",
5356 };
5357
FIXTURE_VARIANT_ADD(layout3_fs,cgroup2)5358 FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {
5359 .mnt = {
5360 .type = "cgroup2",
5361 },
5362 .file_path = TMP_DIR "/test/cgroup.procs",
5363 };
5364
FIXTURE_VARIANT_ADD(layout3_fs,proc)5365 FIXTURE_VARIANT_ADD(layout3_fs, proc) {
5366 .mnt = {
5367 .type = "proc",
5368 },
5369 .file_path = TMP_DIR "/self/status",
5370 };
5371
FIXTURE_VARIANT_ADD(layout3_fs,sysfs)5372 FIXTURE_VARIANT_ADD(layout3_fs, sysfs) {
5373 .mnt = {
5374 .type = "sysfs",
5375 },
5376 .file_path = TMP_DIR "/kernel/notes",
5377 };
5378
FIXTURE_VARIANT_ADD(layout3_fs,hostfs)5379 FIXTURE_VARIANT_ADD(layout3_fs, hostfs) {
5380 .mnt = {
5381 .source = TMP_DIR,
5382 .flags = MS_BIND,
5383 },
5384 .file_path = TMP_DIR "/dir/file",
5385 .cwd_fs_magic = HOSTFS_SUPER_MAGIC,
5386 };
5387
dirname_alloc(const char * path)5388 static char *dirname_alloc(const char *path)
5389 {
5390 char *dup;
5391
5392 if (!path)
5393 return NULL;
5394
5395 dup = strdup(path);
5396 if (!dup)
5397 return NULL;
5398
5399 return dirname(dup);
5400 }
5401
FIXTURE_SETUP(layout3_fs)5402 FIXTURE_SETUP(layout3_fs)
5403 {
5404 struct stat statbuf;
5405 char *dir_path = dirname_alloc(variant->file_path);
5406
5407 if (!supports_filesystem(variant->mnt.type) ||
5408 !cwd_matches_fs(variant->cwd_fs_magic)) {
5409 self->skip_test = true;
5410 SKIP(return, "this filesystem is not supported (setup)");
5411 }
5412
5413 prepare_layout_opt(_metadata, &variant->mnt);
5414
5415 /* Creates directory when required. */
5416 if (stat(dir_path, &statbuf)) {
5417 set_cap(_metadata, CAP_DAC_OVERRIDE);
5418 EXPECT_EQ(0, mkdir(dir_path, 0700))
5419 {
5420 TH_LOG("Failed to create directory \"%s\": %s",
5421 dir_path, strerror(errno));
5422 }
5423 self->has_created_dir = true;
5424 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5425 }
5426
5427 /* Creates file when required. */
5428 if (stat(variant->file_path, &statbuf)) {
5429 int fd;
5430
5431 set_cap(_metadata, CAP_DAC_OVERRIDE);
5432 fd = creat(variant->file_path, 0600);
5433 EXPECT_LE(0, fd)
5434 {
5435 TH_LOG("Failed to create file \"%s\": %s",
5436 variant->file_path, strerror(errno));
5437 }
5438 EXPECT_EQ(0, close(fd));
5439 self->has_created_file = true;
5440 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5441 }
5442
5443 free(dir_path);
5444 }
5445
FIXTURE_TEARDOWN_PARENT(layout3_fs)5446 FIXTURE_TEARDOWN_PARENT(layout3_fs)
5447 {
5448 if (self->skip_test)
5449 SKIP(return, "this filesystem is not supported (teardown)");
5450
5451 if (self->has_created_file) {
5452 set_cap(_metadata, CAP_DAC_OVERRIDE);
5453 /*
5454 * Don't check for error because the file might already
5455 * have been removed (cf. release_inode test).
5456 */
5457 unlink(variant->file_path);
5458 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5459 }
5460
5461 if (self->has_created_dir) {
5462 char *dir_path = dirname_alloc(variant->file_path);
5463
5464 set_cap(_metadata, CAP_DAC_OVERRIDE);
5465 /*
5466 * Don't check for error because the directory might already
5467 * have been removed (cf. release_inode test).
5468 */
5469 rmdir(dir_path);
5470 clear_cap(_metadata, CAP_DAC_OVERRIDE);
5471 free(dir_path);
5472 }
5473
5474 cleanup_layout(_metadata);
5475 }
5476
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)5477 static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,
5478 FIXTURE_DATA(layout3_fs) * self,
5479 const FIXTURE_VARIANT(layout3_fs) * variant,
5480 const char *const rule_path)
5481 {
5482 const struct rule layer1_allow_read_file[] = {
5483 {
5484 .path = rule_path,
5485 .access = LANDLOCK_ACCESS_FS_READ_FILE,
5486 },
5487 {},
5488 };
5489 const struct landlock_ruleset_attr layer2_deny_everything_attr = {
5490 .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
5491 };
5492 const char *const dev_null_path = "/dev/null";
5493 int ruleset_fd;
5494
5495 if (self->skip_test)
5496 SKIP(return, "this filesystem is not supported (test)");
5497
5498 /* Checks without Landlock. */
5499 EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
5500 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
5501
5502 ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
5503 layer1_allow_read_file);
5504 EXPECT_LE(0, ruleset_fd);
5505 enforce_ruleset(_metadata, ruleset_fd);
5506 EXPECT_EQ(0, close(ruleset_fd));
5507
5508 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
5509 EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
5510
5511 /* Forbids directory reading. */
5512 ruleset_fd =
5513 landlock_create_ruleset(&layer2_deny_everything_attr,
5514 sizeof(layer2_deny_everything_attr), 0);
5515 EXPECT_LE(0, ruleset_fd);
5516 enforce_ruleset(_metadata, ruleset_fd);
5517 EXPECT_EQ(0, close(ruleset_fd));
5518
5519 /* Checks with Landlock and forbidden access. */
5520 EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
5521 EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
5522 }
5523
5524 /* Matrix of tests to check file hierarchy evaluation. */
5525
TEST_F_FORK(layout3_fs,tag_inode_dir_parent)5526 TEST_F_FORK(layout3_fs, tag_inode_dir_parent)
5527 {
5528 /* The current directory must not be the root for this test. */
5529 layer3_fs_tag_inode(_metadata, self, variant, ".");
5530 }
5531
TEST_F_FORK(layout3_fs,tag_inode_dir_mnt)5532 TEST_F_FORK(layout3_fs, tag_inode_dir_mnt)
5533 {
5534 layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);
5535 }
5536
TEST_F_FORK(layout3_fs,tag_inode_dir_child)5537 TEST_F_FORK(layout3_fs, tag_inode_dir_child)
5538 {
5539 char *dir_path = dirname_alloc(variant->file_path);
5540
5541 layer3_fs_tag_inode(_metadata, self, variant, dir_path);
5542 free(dir_path);
5543 }
5544
TEST_F_FORK(layout3_fs,tag_inode_file)5545 TEST_F_FORK(layout3_fs, tag_inode_file)
5546 {
5547 layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);
5548 }
5549
5550 /* Light version of layout1.release_inodes */
TEST_F_FORK(layout3_fs,release_inodes)5551 TEST_F_FORK(layout3_fs, release_inodes)
5552 {
5553 const struct rule layer1[] = {
5554 {
5555 .path = TMP_DIR,
5556 .access = LANDLOCK_ACCESS_FS_READ_DIR,
5557 },
5558 {},
5559 };
5560 int ruleset_fd;
5561
5562 if (self->skip_test)
5563 SKIP(return, "this filesystem is not supported (test)");
5564
5565 /* Clean up for the teardown to not fail. */
5566 if (self->has_created_file)
5567 EXPECT_EQ(0, remove_path(variant->file_path));
5568
5569 if (self->has_created_dir) {
5570 char *dir_path = dirname_alloc(variant->file_path);
5571
5572 /* Don't check for error because of cgroup specificities. */
5573 remove_path(dir_path);
5574 free(dir_path);
5575 }
5576
5577 ruleset_fd =
5578 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
5579 ASSERT_LE(0, ruleset_fd);
5580
5581 /* Unmount the filesystem while it is being used by a ruleset. */
5582 set_cap(_metadata, CAP_SYS_ADMIN);
5583 ASSERT_EQ(0, umount(TMP_DIR));
5584 clear_cap(_metadata, CAP_SYS_ADMIN);
5585
5586 /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */
5587 set_cap(_metadata, CAP_SYS_ADMIN);
5588 ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));
5589 clear_cap(_metadata, CAP_SYS_ADMIN);
5590
5591 enforce_ruleset(_metadata, ruleset_fd);
5592 ASSERT_EQ(0, close(ruleset_fd));
5593
5594 /* Checks that access to the new mount point is denied. */
5595 ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));
5596 }
5597
matches_log_fs_extra(struct __test_metadata * const _metadata,int audit_fd,const char * const blockers,const char * const path,const char * const extra)5598 static int matches_log_fs_extra(struct __test_metadata *const _metadata,
5599 int audit_fd, const char *const blockers,
5600 const char *const path, const char *const extra)
5601 {
5602 static const char log_template[] = REGEX_LANDLOCK_PREFIX
5603 " blockers=fs\\.%s path=\"%s\" dev=\"[^\"]\\+\" ino=[0-9]\\+$";
5604 char *absolute_path = NULL;
5605 size_t log_match_remaining = sizeof(log_template) + strlen(blockers) +
5606 PATH_MAX * 2 +
5607 (extra ? strlen(extra) : 0) + 1;
5608 char log_match[log_match_remaining];
5609 char *log_match_cursor = log_match;
5610 size_t chunk_len;
5611
5612 chunk_len = snprintf(log_match_cursor, log_match_remaining,
5613 REGEX_LANDLOCK_PREFIX " blockers=%s path=\"",
5614 blockers);
5615 if (chunk_len < 0 || chunk_len >= log_match_remaining)
5616 return -E2BIG;
5617
5618 /*
5619 * It is assume that absolute_path does not contain control characters nor
5620 * spaces, see audit_string_contains_control().
5621 */
5622 absolute_path = realpath(path, NULL);
5623 if (!absolute_path)
5624 return -errno;
5625
5626 log_match_remaining -= chunk_len;
5627 log_match_cursor += chunk_len;
5628 log_match_cursor = regex_escape(absolute_path, log_match_cursor,
5629 log_match_remaining);
5630 free(absolute_path);
5631 if (log_match_cursor < 0)
5632 return (long long)log_match_cursor;
5633
5634 log_match_remaining -= log_match_cursor - log_match;
5635 chunk_len = snprintf(log_match_cursor, log_match_remaining,
5636 "\" dev=\"[^\"]\\+\" ino=[0-9]\\+%s$",
5637 extra ?: "");
5638 if (chunk_len < 0 || chunk_len >= log_match_remaining)
5639 return -E2BIG;
5640
5641 return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
5642 NULL);
5643 }
5644
matches_log_fs(struct __test_metadata * const _metadata,int audit_fd,const char * const blockers,const char * const path)5645 static int matches_log_fs(struct __test_metadata *const _metadata, int audit_fd,
5646 const char *const blockers, const char *const path)
5647 {
5648 return matches_log_fs_extra(_metadata, audit_fd, blockers, path, NULL);
5649 }
5650
FIXTURE(audit_layout1)5651 FIXTURE(audit_layout1)
5652 {
5653 struct audit_filter audit_filter;
5654 int audit_fd;
5655 };
5656
FIXTURE_SETUP(audit_layout1)5657 FIXTURE_SETUP(audit_layout1)
5658 {
5659 prepare_layout(_metadata);
5660
5661 create_layout1(_metadata);
5662
5663 set_cap(_metadata, CAP_AUDIT_CONTROL);
5664 self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
5665 EXPECT_LE(0, self->audit_fd);
5666 disable_caps(_metadata);
5667 }
5668
FIXTURE_TEARDOWN_PARENT(audit_layout1)5669 FIXTURE_TEARDOWN_PARENT(audit_layout1)
5670 {
5671 remove_layout1(_metadata);
5672
5673 cleanup_layout(_metadata);
5674
5675 EXPECT_EQ(0, audit_cleanup(-1, NULL));
5676 }
5677
TEST_F(audit_layout1,execute_make)5678 TEST_F(audit_layout1, execute_make)
5679 {
5680 struct audit_records records;
5681
5682 copy_file(_metadata, bin_true, file1_s1d1);
5683 test_execute(_metadata, 0, file1_s1d1);
5684 test_check_exec(_metadata, 0, file1_s1d1);
5685
5686 drop_access_rights(_metadata,
5687 &(struct landlock_ruleset_attr){
5688 .handled_access_fs =
5689 LANDLOCK_ACCESS_FS_EXECUTE,
5690 });
5691
5692 test_execute(_metadata, EACCES, file1_s1d1);
5693 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute",
5694 file1_s1d1));
5695 test_check_exec(_metadata, EACCES, file1_s1d1);
5696 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.execute",
5697 file1_s1d1));
5698
5699 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5700 EXPECT_EQ(0, records.access);
5701 EXPECT_EQ(0, records.domain);
5702 }
5703
5704 /*
5705 * Using a set of handled/denied access rights make it possible to check that
5706 * only the blocked ones are logged.
5707 */
5708
5709 /* clang-format off */
5710 static const __u64 access_fs_16 =
5711 LANDLOCK_ACCESS_FS_EXECUTE |
5712 LANDLOCK_ACCESS_FS_WRITE_FILE |
5713 LANDLOCK_ACCESS_FS_READ_FILE |
5714 LANDLOCK_ACCESS_FS_READ_DIR |
5715 LANDLOCK_ACCESS_FS_REMOVE_DIR |
5716 LANDLOCK_ACCESS_FS_REMOVE_FILE |
5717 LANDLOCK_ACCESS_FS_MAKE_CHAR |
5718 LANDLOCK_ACCESS_FS_MAKE_DIR |
5719 LANDLOCK_ACCESS_FS_MAKE_REG |
5720 LANDLOCK_ACCESS_FS_MAKE_SOCK |
5721 LANDLOCK_ACCESS_FS_MAKE_FIFO |
5722 LANDLOCK_ACCESS_FS_MAKE_BLOCK |
5723 LANDLOCK_ACCESS_FS_MAKE_SYM |
5724 LANDLOCK_ACCESS_FS_REFER |
5725 LANDLOCK_ACCESS_FS_TRUNCATE |
5726 LANDLOCK_ACCESS_FS_IOCTL_DEV;
5727 /* clang-format on */
5728
TEST_F(audit_layout1,execute_read)5729 TEST_F(audit_layout1, execute_read)
5730 {
5731 struct audit_records records;
5732
5733 copy_file(_metadata, bin_true, file1_s1d1);
5734 test_execute(_metadata, 0, file1_s1d1);
5735 test_check_exec(_metadata, 0, file1_s1d1);
5736
5737 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5738 .handled_access_fs = access_fs_16,
5739 });
5740
5741 /*
5742 * The only difference with the previous audit_layout1.execute_read test is
5743 * the extra ",fs\\.read_file" blocked by the executable file.
5744 */
5745 test_execute(_metadata, EACCES, file1_s1d1);
5746 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
5747 "fs\\.execute,fs\\.read_file", file1_s1d1));
5748 test_check_exec(_metadata, EACCES, file1_s1d1);
5749 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
5750 "fs\\.execute,fs\\.read_file", file1_s1d1));
5751
5752 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5753 EXPECT_EQ(0, records.access);
5754 EXPECT_EQ(0, records.domain);
5755 }
5756
TEST_F(audit_layout1,write_file)5757 TEST_F(audit_layout1, write_file)
5758 {
5759 struct audit_records records;
5760
5761 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5762 .handled_access_fs = access_fs_16,
5763 });
5764
5765 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_WRONLY));
5766 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
5767 "fs\\.write_file", file1_s1d1));
5768
5769 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5770 EXPECT_EQ(0, records.access);
5771 EXPECT_EQ(1, records.domain);
5772 }
5773
TEST_F(audit_layout1,read_file)5774 TEST_F(audit_layout1, read_file)
5775 {
5776 struct audit_records records;
5777
5778 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5779 .handled_access_fs = access_fs_16,
5780 });
5781
5782 EXPECT_EQ(EACCES, test_open(file1_s1d1, O_RDONLY));
5783 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_file",
5784 file1_s1d1));
5785
5786 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5787 EXPECT_EQ(0, records.access);
5788 EXPECT_EQ(1, records.domain);
5789 }
5790
TEST_F(audit_layout1,read_dir)5791 TEST_F(audit_layout1, read_dir)
5792 {
5793 struct audit_records records;
5794
5795 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5796 .handled_access_fs = access_fs_16,
5797 });
5798
5799 EXPECT_EQ(EACCES, test_open(dir_s1d1, O_DIRECTORY));
5800 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.read_dir",
5801 dir_s1d1));
5802
5803 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5804 EXPECT_EQ(0, records.access);
5805 EXPECT_EQ(1, records.domain);
5806 }
5807
TEST_F(audit_layout1,remove_dir)5808 TEST_F(audit_layout1, remove_dir)
5809 {
5810 struct audit_records records;
5811
5812 EXPECT_EQ(0, unlink(file1_s1d3));
5813 EXPECT_EQ(0, unlink(file2_s1d3));
5814
5815 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5816 .handled_access_fs = access_fs_16,
5817 });
5818
5819 EXPECT_EQ(-1, rmdir(dir_s1d3));
5820 EXPECT_EQ(EACCES, errno);
5821 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
5822 "fs\\.remove_dir", dir_s1d2));
5823
5824 EXPECT_EQ(-1, unlinkat(AT_FDCWD, dir_s1d3, AT_REMOVEDIR));
5825 EXPECT_EQ(EACCES, errno);
5826 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
5827 "fs\\.remove_dir", dir_s1d2));
5828
5829 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5830 EXPECT_EQ(0, records.access);
5831 EXPECT_EQ(0, records.domain);
5832 }
5833
TEST_F(audit_layout1,remove_file)5834 TEST_F(audit_layout1, remove_file)
5835 {
5836 struct audit_records records;
5837
5838 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5839 .handled_access_fs = access_fs_16,
5840 });
5841
5842 EXPECT_EQ(-1, unlink(file1_s1d3));
5843 EXPECT_EQ(EACCES, errno);
5844 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
5845 "fs\\.remove_file", 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_char)5852 TEST_F(audit_layout1, make_char)
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_IFCHR | 0644, 0));
5863 EXPECT_EQ(EACCES, errno);
5864 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_char",
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_dir)5872 TEST_F(audit_layout1, make_dir)
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, mkdir(file1_s1d3, 0755));
5883 EXPECT_EQ(EACCES, errno);
5884 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_dir",
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_reg)5892 TEST_F(audit_layout1, make_reg)
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_IFREG | 0644, 0));
5903 EXPECT_EQ(EACCES, errno);
5904 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_reg",
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_sock)5912 TEST_F(audit_layout1, make_sock)
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_IFSOCK | 0644, 0));
5923 EXPECT_EQ(EACCES, errno);
5924 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sock",
5925 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_fifo)5932 TEST_F(audit_layout1, make_fifo)
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, mknod(file1_s1d3, S_IFIFO | 0644, 0));
5943 EXPECT_EQ(EACCES, errno);
5944 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_fifo",
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,make_block)5952 TEST_F(audit_layout1, make_block)
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 = access_fs_16,
5960 });
5961
5962 EXPECT_EQ(-1, mknod(file1_s1d3, S_IFBLK | 0644, 0));
5963 EXPECT_EQ(EACCES, errno);
5964 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
5965 "fs\\.make_block", dir_s1d3));
5966
5967 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5968 EXPECT_EQ(0, records.access);
5969 EXPECT_EQ(1, records.domain);
5970 }
5971
TEST_F(audit_layout1,make_sym)5972 TEST_F(audit_layout1, make_sym)
5973 {
5974 struct audit_records records;
5975
5976 EXPECT_EQ(0, unlink(file1_s1d3));
5977
5978 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5979 .handled_access_fs = access_fs_16,
5980 });
5981
5982 EXPECT_EQ(-1, symlink("target", file1_s1d3));
5983 EXPECT_EQ(EACCES, errno);
5984 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.make_sym",
5985 dir_s1d3));
5986
5987 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
5988 EXPECT_EQ(0, records.access);
5989 EXPECT_EQ(1, records.domain);
5990 }
5991
TEST_F(audit_layout1,refer_handled)5992 TEST_F(audit_layout1, refer_handled)
5993 {
5994 struct audit_records records;
5995
5996 EXPECT_EQ(0, unlink(file1_s1d3));
5997
5998 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
5999 .handled_access_fs =
6000 LANDLOCK_ACCESS_FS_REFER,
6001 });
6002
6003 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3));
6004 EXPECT_EQ(EXDEV, errno);
6005 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
6006 dir_s1d1));
6007 EXPECT_EQ(0,
6008 matches_log_domain_allocated(self->audit_fd, getpid(), NULL));
6009 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
6010 dir_s1d3));
6011
6012 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6013 EXPECT_EQ(0, records.access);
6014 EXPECT_EQ(0, records.domain);
6015 }
6016
TEST_F(audit_layout1,refer_make)6017 TEST_F(audit_layout1, refer_make)
6018 {
6019 struct audit_records records;
6020
6021 EXPECT_EQ(0, unlink(file1_s1d3));
6022
6023 drop_access_rights(_metadata,
6024 &(struct landlock_ruleset_attr){
6025 .handled_access_fs =
6026 LANDLOCK_ACCESS_FS_MAKE_REG |
6027 LANDLOCK_ACCESS_FS_REFER,
6028 });
6029
6030 EXPECT_EQ(-1, link(file1_s1d1, file1_s1d3));
6031 EXPECT_EQ(EACCES, errno);
6032 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
6033 dir_s1d1));
6034 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
6035 "fs\\.make_reg,fs\\.refer", dir_s1d3));
6036
6037 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6038 EXPECT_EQ(0, records.access);
6039 EXPECT_EQ(0, records.domain);
6040 }
6041
TEST_F(audit_layout1,refer_rename)6042 TEST_F(audit_layout1, refer_rename)
6043 {
6044 struct audit_records records;
6045
6046 EXPECT_EQ(0, unlink(file1_s1d3));
6047
6048 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
6049 .handled_access_fs = access_fs_16,
6050 });
6051
6052 EXPECT_EQ(EACCES, test_rename(file1_s1d2, file1_s2d3));
6053 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
6054 "fs\\.remove_file,fs\\.refer", dir_s1d2));
6055 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
6056 "fs\\.remove_file,fs\\.make_reg,fs\\.refer",
6057 dir_s2d3));
6058
6059 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6060 EXPECT_EQ(0, records.access);
6061 EXPECT_EQ(0, records.domain);
6062 }
6063
TEST_F(audit_layout1,refer_exchange)6064 TEST_F(audit_layout1, refer_exchange)
6065 {
6066 struct audit_records records;
6067
6068 EXPECT_EQ(0, unlink(file1_s1d3));
6069
6070 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
6071 .handled_access_fs = access_fs_16,
6072 });
6073
6074 /*
6075 * The only difference with the previous audit_layout1.refer_rename test is
6076 * the extra ",fs\\.make_reg" blocked by the source directory.
6077 */
6078 EXPECT_EQ(EACCES, test_exchange(file1_s1d2, file1_s2d3));
6079 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
6080 "fs\\.remove_file,fs\\.make_reg,fs\\.refer",
6081 dir_s1d2));
6082 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
6083 "fs\\.remove_file,fs\\.make_reg,fs\\.refer",
6084 dir_s2d3));
6085
6086 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6087 EXPECT_EQ(0, records.access);
6088 EXPECT_EQ(0, records.domain);
6089 }
6090
6091 /*
6092 * This test checks that the audit record is correctly generated when the
6093 * operation is only partially denied. This is the case for rename(2) when the
6094 * source file is allowed to be referenced but the destination directory is not.
6095 *
6096 * This is also a regression test for commit d617f0d72d80 ("landlock: Optimize
6097 * file path walks and prepare for audit support") and commit 058518c20920
6098 * ("landlock: Align partial refer access checks with final ones").
6099 */
TEST_F(audit_layout1,refer_rename_half)6100 TEST_F(audit_layout1, refer_rename_half)
6101 {
6102 struct audit_records records;
6103 const struct rule layer1[] = {
6104 {
6105 .path = dir_s2d2,
6106 .access = LANDLOCK_ACCESS_FS_REFER,
6107 },
6108 {},
6109 };
6110 int ruleset_fd =
6111 create_ruleset(_metadata, LANDLOCK_ACCESS_FS_REFER, layer1);
6112
6113 ASSERT_LE(0, ruleset_fd);
6114 enforce_ruleset(_metadata, ruleset_fd);
6115 ASSERT_EQ(0, close(ruleset_fd));
6116
6117 ASSERT_EQ(-1, rename(dir_s1d2, dir_s2d3));
6118 ASSERT_EQ(EXDEV, errno);
6119
6120 /* Only half of the request is denied. */
6121 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.refer",
6122 dir_s1d1));
6123
6124 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6125 EXPECT_EQ(0, records.access);
6126 EXPECT_EQ(1, records.domain);
6127 }
6128
TEST_F(audit_layout1,truncate)6129 TEST_F(audit_layout1, truncate)
6130 {
6131 struct audit_records records;
6132
6133 drop_access_rights(_metadata, &(struct landlock_ruleset_attr){
6134 .handled_access_fs = access_fs_16,
6135 });
6136
6137 EXPECT_EQ(-1, truncate(file1_s1d3, 0));
6138 EXPECT_EQ(EACCES, errno);
6139 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd, "fs\\.truncate",
6140 file1_s1d3));
6141
6142 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6143 EXPECT_EQ(0, records.access);
6144 EXPECT_EQ(1, records.domain);
6145 }
6146
TEST_F(audit_layout1,ioctl_dev)6147 TEST_F(audit_layout1, ioctl_dev)
6148 {
6149 struct audit_records records;
6150 int fd;
6151
6152 drop_access_rights(_metadata,
6153 &(struct landlock_ruleset_attr){
6154 .handled_access_fs =
6155 access_fs_16 &
6156 ~LANDLOCK_ACCESS_FS_READ_FILE,
6157 });
6158
6159 fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
6160 ASSERT_LE(0, fd);
6161 EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));
6162 EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd,
6163 "fs\\.ioctl_dev", "/dev/null",
6164 " ioctlcmd=0x541b"));
6165
6166 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6167 EXPECT_EQ(0, records.access);
6168 EXPECT_EQ(1, records.domain);
6169 }
6170
TEST_F(audit_layout1,mount)6171 TEST_F(audit_layout1, mount)
6172 {
6173 struct audit_records records;
6174
6175 drop_access_rights(_metadata,
6176 &(struct landlock_ruleset_attr){
6177 .handled_access_fs =
6178 LANDLOCK_ACCESS_FS_EXECUTE,
6179 });
6180
6181 set_cap(_metadata, CAP_SYS_ADMIN);
6182 EXPECT_EQ(-1, mount(NULL, dir_s3d2, NULL, MS_RDONLY, NULL));
6183 EXPECT_EQ(EPERM, errno);
6184 clear_cap(_metadata, CAP_SYS_ADMIN);
6185 EXPECT_EQ(0, matches_log_fs(_metadata, self->audit_fd,
6186 "fs\\.change_topology", dir_s3d2));
6187 EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
6188 EXPECT_EQ(0, records.access);
6189 EXPECT_EQ(1, records.domain);
6190 }
6191
6192 TEST_HARNESS_MAIN
6193