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