|
8 | 8 | */
|
9 | 9 |
|
10 | 10 | #define _GNU_SOURCE
|
| 11 | +#include <asm/termbits.h> |
11 | 12 | #include <fcntl.h>
|
12 | 13 | #include <libgen.h>
|
13 | 14 | #include <linux/landlock.h>
|
|
16 | 17 | #include <stdio.h>
|
17 | 18 | #include <string.h>
|
18 | 19 | #include <sys/capability.h>
|
| 20 | +#include <sys/ioctl.h> |
19 | 21 | #include <sys/mount.h>
|
20 | 22 | #include <sys/prctl.h>
|
21 | 23 | #include <sys/sendfile.h>
|
|
24 | 26 | #include <sys/vfs.h>
|
25 | 27 | #include <unistd.h>
|
26 | 28 |
|
| 29 | +/* |
| 30 | + * Intentionally included last to work around header conflict. |
| 31 | + * See https://sourceware.org/glibc/wiki/Synchronizing_Headers. |
| 32 | + */ |
| 33 | +#include <linux/fs.h> |
| 34 | + |
27 | 35 | #include "common.h"
|
28 | 36 |
|
29 | 37 | #ifndef renameat2
|
@@ -744,6 +752,9 @@ static int create_ruleset(struct __test_metadata *const _metadata,
|
744 | 752 | }
|
745 | 753 |
|
746 | 754 | for (i = 0; rules[i].path; i++) {
|
| 755 | + if (!rules[i].access) |
| 756 | + continue; |
| 757 | + |
747 | 758 | add_path_beneath(_metadata, ruleset_fd, rules[i].access,
|
748 | 759 | rules[i].path);
|
749 | 760 | }
|
@@ -3452,7 +3463,7 @@ TEST_F_FORK(layout1, truncate_unhandled)
|
3452 | 3463 | LANDLOCK_ACCESS_FS_WRITE_FILE;
|
3453 | 3464 | int ruleset_fd;
|
3454 | 3465 |
|
3455 |
| - /* Enable Landlock. */ |
| 3466 | + /* Enables Landlock. */ |
3456 | 3467 | ruleset_fd = create_ruleset(_metadata, handled, rules);
|
3457 | 3468 |
|
3458 | 3469 | ASSERT_LE(0, ruleset_fd);
|
@@ -3535,7 +3546,7 @@ TEST_F_FORK(layout1, truncate)
|
3535 | 3546 | LANDLOCK_ACCESS_FS_TRUNCATE;
|
3536 | 3547 | int ruleset_fd;
|
3537 | 3548 |
|
3538 |
| - /* Enable Landlock. */ |
| 3549 | + /* Enables Landlock. */ |
3539 | 3550 | ruleset_fd = create_ruleset(_metadata, handled, rules);
|
3540 | 3551 |
|
3541 | 3552 | ASSERT_LE(0, ruleset_fd);
|
@@ -3761,7 +3772,7 @@ TEST_F_FORK(ftruncate, open_and_ftruncate)
|
3761 | 3772 | };
|
3762 | 3773 | int fd, ruleset_fd;
|
3763 | 3774 |
|
3764 |
| - /* Enable Landlock. */ |
| 3775 | + /* Enables Landlock. */ |
3765 | 3776 | ruleset_fd = create_ruleset(_metadata, variant->handled, rules);
|
3766 | 3777 | ASSERT_LE(0, ruleset_fd);
|
3767 | 3778 | enforce_ruleset(_metadata, ruleset_fd);
|
@@ -3854,6 +3865,181 @@ TEST(memfd_ftruncate)
|
3854 | 3865 | ASSERT_EQ(0, close(fd));
|
3855 | 3866 | }
|
3856 | 3867 |
|
| 3868 | +static int test_fionread_ioctl(int fd) |
| 3869 | +{ |
| 3870 | + size_t sz = 0; |
| 3871 | + |
| 3872 | + if (ioctl(fd, FIONREAD, &sz) < 0 && errno == EACCES) |
| 3873 | + return errno; |
| 3874 | + return 0; |
| 3875 | +} |
| 3876 | + |
| 3877 | +/* clang-format off */ |
| 3878 | +FIXTURE(ioctl) {}; |
| 3879 | + |
| 3880 | +FIXTURE_SETUP(ioctl) {}; |
| 3881 | + |
| 3882 | +FIXTURE_TEARDOWN(ioctl) {}; |
| 3883 | +/* clang-format on */ |
| 3884 | + |
| 3885 | +FIXTURE_VARIANT(ioctl) |
| 3886 | +{ |
| 3887 | + const __u64 handled; |
| 3888 | + const __u64 allowed; |
| 3889 | + const mode_t open_mode; |
| 3890 | + /* |
| 3891 | + * FIONREAD is used as a characteristic device-specific IOCTL command. |
| 3892 | + * It is implemented in fs/ioctl.c for regular files, |
| 3893 | + * but we do not blanket-permit it for devices. |
| 3894 | + */ |
| 3895 | + const int expected_fionread_result; |
| 3896 | +}; |
| 3897 | + |
| 3898 | +/* clang-format off */ |
| 3899 | +FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_none) { |
| 3900 | + /* clang-format on */ |
| 3901 | + .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, |
| 3902 | + .allowed = 0, |
| 3903 | + .open_mode = O_RDWR, |
| 3904 | + .expected_fionread_result = EACCES, |
| 3905 | +}; |
| 3906 | + |
| 3907 | +/* clang-format off */ |
| 3908 | +FIXTURE_VARIANT_ADD(ioctl, handled_i_allowed_i) { |
| 3909 | + /* clang-format on */ |
| 3910 | + .handled = LANDLOCK_ACCESS_FS_IOCTL_DEV, |
| 3911 | + .allowed = LANDLOCK_ACCESS_FS_IOCTL_DEV, |
| 3912 | + .open_mode = O_RDWR, |
| 3913 | + .expected_fionread_result = 0, |
| 3914 | +}; |
| 3915 | + |
| 3916 | +/* clang-format off */ |
| 3917 | +FIXTURE_VARIANT_ADD(ioctl, unhandled) { |
| 3918 | + /* clang-format on */ |
| 3919 | + .handled = LANDLOCK_ACCESS_FS_EXECUTE, |
| 3920 | + .allowed = LANDLOCK_ACCESS_FS_EXECUTE, |
| 3921 | + .open_mode = O_RDWR, |
| 3922 | + .expected_fionread_result = 0, |
| 3923 | +}; |
| 3924 | + |
| 3925 | +TEST_F_FORK(ioctl, handle_dir_access_file) |
| 3926 | +{ |
| 3927 | + const int flag = 0; |
| 3928 | + const struct rule rules[] = { |
| 3929 | + { |
| 3930 | + .path = "/dev", |
| 3931 | + .access = variant->allowed, |
| 3932 | + }, |
| 3933 | + {}, |
| 3934 | + }; |
| 3935 | + int file_fd, ruleset_fd; |
| 3936 | + |
| 3937 | + /* Enables Landlock. */ |
| 3938 | + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); |
| 3939 | + ASSERT_LE(0, ruleset_fd); |
| 3940 | + enforce_ruleset(_metadata, ruleset_fd); |
| 3941 | + ASSERT_EQ(0, close(ruleset_fd)); |
| 3942 | + |
| 3943 | + file_fd = open("/dev/zero", variant->open_mode); |
| 3944 | + ASSERT_LE(0, file_fd); |
| 3945 | + |
| 3946 | + /* Checks that IOCTL commands return the expected errors. */ |
| 3947 | + EXPECT_EQ(variant->expected_fionread_result, |
| 3948 | + test_fionread_ioctl(file_fd)); |
| 3949 | + |
| 3950 | + /* Checks that unrestrictable commands are unrestricted. */ |
| 3951 | + EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); |
| 3952 | + EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); |
| 3953 | + EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); |
| 3954 | + EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); |
| 3955 | + EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); |
| 3956 | + |
| 3957 | + ASSERT_EQ(0, close(file_fd)); |
| 3958 | +} |
| 3959 | + |
| 3960 | +TEST_F_FORK(ioctl, handle_dir_access_dir) |
| 3961 | +{ |
| 3962 | + const int flag = 0; |
| 3963 | + const struct rule rules[] = { |
| 3964 | + { |
| 3965 | + .path = "/dev", |
| 3966 | + .access = variant->allowed, |
| 3967 | + }, |
| 3968 | + {}, |
| 3969 | + }; |
| 3970 | + int dir_fd, ruleset_fd; |
| 3971 | + |
| 3972 | + /* Enables Landlock. */ |
| 3973 | + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); |
| 3974 | + ASSERT_LE(0, ruleset_fd); |
| 3975 | + enforce_ruleset(_metadata, ruleset_fd); |
| 3976 | + ASSERT_EQ(0, close(ruleset_fd)); |
| 3977 | + |
| 3978 | + /* |
| 3979 | + * Ignore variant->open_mode for this test, as we intend to open a |
| 3980 | + * directory. If the directory can not be opened, the variant is |
| 3981 | + * infeasible to test with an opened directory. |
| 3982 | + */ |
| 3983 | + dir_fd = open("/dev", O_RDONLY); |
| 3984 | + if (dir_fd < 0) |
| 3985 | + return; |
| 3986 | + |
| 3987 | + /* |
| 3988 | + * Checks that IOCTL commands return the expected errors. |
| 3989 | + * We do not use the expected values from the fixture here. |
| 3990 | + * |
| 3991 | + * When using IOCTL on a directory, no Landlock restrictions apply. |
| 3992 | + */ |
| 3993 | + EXPECT_EQ(0, test_fionread_ioctl(dir_fd)); |
| 3994 | + |
| 3995 | + /* Checks that unrestrictable commands are unrestricted. */ |
| 3996 | + EXPECT_EQ(0, ioctl(dir_fd, FIOCLEX)); |
| 3997 | + EXPECT_EQ(0, ioctl(dir_fd, FIONCLEX)); |
| 3998 | + EXPECT_EQ(0, ioctl(dir_fd, FIONBIO, &flag)); |
| 3999 | + EXPECT_EQ(0, ioctl(dir_fd, FIOASYNC, &flag)); |
| 4000 | + EXPECT_EQ(0, ioctl(dir_fd, FIGETBSZ, &flag)); |
| 4001 | + |
| 4002 | + ASSERT_EQ(0, close(dir_fd)); |
| 4003 | +} |
| 4004 | + |
| 4005 | +TEST_F_FORK(ioctl, handle_file_access_file) |
| 4006 | +{ |
| 4007 | + const int flag = 0; |
| 4008 | + const struct rule rules[] = { |
| 4009 | + { |
| 4010 | + .path = "/dev/zero", |
| 4011 | + .access = variant->allowed, |
| 4012 | + }, |
| 4013 | + {}, |
| 4014 | + }; |
| 4015 | + int file_fd, ruleset_fd; |
| 4016 | + |
| 4017 | + /* Enables Landlock. */ |
| 4018 | + ruleset_fd = create_ruleset(_metadata, variant->handled, rules); |
| 4019 | + ASSERT_LE(0, ruleset_fd); |
| 4020 | + enforce_ruleset(_metadata, ruleset_fd); |
| 4021 | + ASSERT_EQ(0, close(ruleset_fd)); |
| 4022 | + |
| 4023 | + file_fd = open("/dev/zero", variant->open_mode); |
| 4024 | + ASSERT_LE(0, file_fd) |
| 4025 | + { |
| 4026 | + TH_LOG("Failed to open /dev/zero: %s", strerror(errno)); |
| 4027 | + } |
| 4028 | + |
| 4029 | + /* Checks that IOCTL commands return the expected errors. */ |
| 4030 | + EXPECT_EQ(variant->expected_fionread_result, |
| 4031 | + test_fionread_ioctl(file_fd)); |
| 4032 | + |
| 4033 | + /* Checks that unrestrictable commands are unrestricted. */ |
| 4034 | + EXPECT_EQ(0, ioctl(file_fd, FIOCLEX)); |
| 4035 | + EXPECT_EQ(0, ioctl(file_fd, FIONCLEX)); |
| 4036 | + EXPECT_EQ(0, ioctl(file_fd, FIONBIO, &flag)); |
| 4037 | + EXPECT_EQ(0, ioctl(file_fd, FIOASYNC, &flag)); |
| 4038 | + EXPECT_EQ(0, ioctl(file_fd, FIGETBSZ, &flag)); |
| 4039 | + |
| 4040 | + ASSERT_EQ(0, close(file_fd)); |
| 4041 | +} |
| 4042 | + |
3857 | 4043 | /* clang-format off */
|
3858 | 4044 | FIXTURE(layout1_bind) {};
|
3859 | 4045 | /* clang-format on */
|
|
0 commit comments