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