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