Message ID | 20240701-landlock-v1-8-58e9af649a72@suse.com |
---|---|
State | Superseded |
Headers | show |
Series | landlock testing suite | expand |
On Mon, Jul 1, 2024 at 11:44 PM Andrea Cervesato <andrea.cervesato@suse.de> wrote: > From: Andrea Cervesato <andrea.cervesato@suse.com> > > This test verifies that all landlock rules are working properly. > The way we do it is to verify that all disabled syscalls are not > working but the one we enabled via specifc landlock rules. > > Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com> > --- > runtest/syscalls | 1 + > testcases/kernel/syscalls/landlock/.gitignore | 2 + > testcases/kernel/syscalls/landlock/Makefile | 5 + > testcases/kernel/syscalls/landlock/landlock04.c | 143 +++++++++ > testcases/kernel/syscalls/landlock/landlock_exec.c | 9 + > .../kernel/syscalls/landlock/landlock_tester.h | 350 > +++++++++++++++++++++ > 6 files changed, 510 insertions(+) > > diff --git a/runtest/syscalls b/runtest/syscalls > index 1e2d682e3..9acdaf760 100644 > --- a/runtest/syscalls > +++ b/runtest/syscalls > @@ -687,6 +687,7 @@ kill13 kill13 > landlock01 landlock01 > landlock02 landlock02 > landlock03 landlock03 > +landlock04 landlock04 > > lchown01 lchown01 > lchown01_16 lchown01_16 > diff --git a/testcases/kernel/syscalls/landlock/.gitignore > b/testcases/kernel/syscalls/landlock/.gitignore > index f79cd090b..4fe8d7cba 100644 > --- a/testcases/kernel/syscalls/landlock/.gitignore > +++ b/testcases/kernel/syscalls/landlock/.gitignore > @@ -1,3 +1,5 @@ > +landlock_exec > landlock01 > landlock02 > landlock03 > +landlock04 > diff --git a/testcases/kernel/syscalls/landlock/Makefile > b/testcases/kernel/syscalls/landlock/Makefile > index 4b3e3fd8f..bdc6bd2d4 100644 > --- a/testcases/kernel/syscalls/landlock/Makefile > +++ b/testcases/kernel/syscalls/landlock/Makefile > @@ -8,3 +8,8 @@ include $(top_srcdir)/include/mk/testcases.mk > LDLIBS += -lc > > include $(top_srcdir)/include/mk/generic_leaf_target.mk > + > +# the reason why landlock_exec test binary is statically linked, is that > +# we can't read libc out of the sandboxed folder once > LANDLOCK_ACCESS_FS_EXECUTE > +# has been activated > +landlock_exec: LDLIBS += -static -fPIC > diff --git a/testcases/kernel/syscalls/landlock/landlock04.c > b/testcases/kernel/syscalls/landlock/landlock04.c > new file mode 100644 > index 000000000..1e7c6f3d1 > --- /dev/null > +++ b/testcases/kernel/syscalls/landlock/landlock04.c > @@ -0,0 +1,143 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (C) 2024 SUSE LLC Andrea Cervesato < > andrea.cervesato@suse.com> > + */ > + > +/*\ > + * [Description] > + * > + * This test verifies that all landlock rules are working properly. The > way we > + * do it is to verify that all disabled syscalls are not working but the > one we > + * enabled via specifc landlock rules. > + */ > + > +#include "landlock_common.h" > +#include "landlock_tester.h" > + > +#define ACCESS_NAME(x) #x > + > +static struct landlock_ruleset_attr *ruleset_attr; > +static struct landlock_path_beneath_attr *path_beneath_attr; > + > +static struct tvariant { > + int access; > + char *desc; > +} tvariants[] = { > + { > + LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_EXECUTE, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_EXECUTE) > + }, > + { > + LANDLOCK_ACCESS_FS_WRITE_FILE, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_WRITE_FILE) > + }, > + { > + LANDLOCK_ACCESS_FS_READ_FILE, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_FILE) > + }, > + { > + LANDLOCK_ACCESS_FS_READ_DIR, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_DIR) > + }, > + { > + LANDLOCK_ACCESS_FS_REMOVE_DIR, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_DIR) > + }, > + { > + LANDLOCK_ACCESS_FS_REMOVE_FILE, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_FILE) > + }, > + { > + LANDLOCK_ACCESS_FS_MAKE_CHAR, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_CHAR) > + }, > + { > + LANDLOCK_ACCESS_FS_MAKE_BLOCK, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_BLOCK) > + }, > + { > + LANDLOCK_ACCESS_FS_MAKE_REG, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_REG) > + }, > + { > + LANDLOCK_ACCESS_FS_MAKE_SOCK, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SOCK) > + }, > + { > + LANDLOCK_ACCESS_FS_MAKE_FIFO, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_FIFO) > + }, > + { > + LANDLOCK_ACCESS_FS_MAKE_SYM, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SYM) > + }, > + { > + LANDLOCK_ACCESS_FS_WRITE_FILE | > LANDLOCK_ACCESS_FS_TRUNCATE, > + ACCESS_NAME(LANDLOCK_ACCESS_FS_TRUNCATE) > + }, > +}; > + > +static void run(void) > +{ > + if (!SAFE_FORK()) { > + struct tvariant variant = tvariants[tst_variant]; > + > + tester_run_all_rules(variant.access); > + _exit(0); > + } > +} > + > +static void setup(void) > +{ > + struct tvariant variant = tvariants[tst_variant]; > + > + verify_landlock_is_enabled(); > + tester_create_tree(); > + > + tst_res(TINFO, "Testing %s", variant.desc); > + > + ruleset_attr->handled_access_fs = tester_get_all_rules(); > + > + apply_landlock_layer( > + ruleset_attr, > + path_beneath_attr, > + SANDBOX_FOLDER, > + variant.access); > +} > + > +static struct tst_test test = { > + .test_all = run, > + .setup = setup, > + .min_kver = "5.13", > + .forks_child = 1, > + .needs_tmpdir = 1, > + .needs_root = 1, > + .test_variants = ARRAY_SIZE(tvariants), > + .resource_files = (const char *[]) { > + TESTAPP, > + NULL, > + }, > + .needs_kconfigs = (const char *[]) { > + "CONFIG_SECURITY_LANDLOCK=y", > + NULL > + }, > + .bufs = (struct tst_buffers []) { > + {&ruleset_attr, .size = sizeof(struct > landlock_ruleset_attr)}, > + {&path_beneath_attr, .size = sizeof(struct > landlock_path_beneath_attr)}, > + {}, > + }, > + .caps = (struct tst_cap []) { > + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), > + TST_CAP(TST_CAP_REQ, CAP_MKNOD), > We have to define CAP_MKNOD in the include/lapi/capability.h, otherwise it can't be built on some platform. landlock04.c:131:38: error: ‘CAP_MKNOD’ undeclared here (not in a function); did you mean ‘SAFE_MKNOD’? 131 | TST_CAP(TST_CAP_REQ, CAP_MKNOD),
On Tue, Jul 2, 2024 at 4:00 PM Li Wang <liwang@redhat.com> wrote: > > > On Mon, Jul 1, 2024 at 11:44 PM Andrea Cervesato <andrea.cervesato@suse.de> > wrote: > >> From: Andrea Cervesato <andrea.cervesato@suse.com> >> >> This test verifies that all landlock rules are working properly. >> The way we do it is to verify that all disabled syscalls are not >> working but the one we enabled via specifc landlock rules. >> >> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com> >> --- >> runtest/syscalls | 1 + >> testcases/kernel/syscalls/landlock/.gitignore | 2 + >> testcases/kernel/syscalls/landlock/Makefile | 5 + >> testcases/kernel/syscalls/landlock/landlock04.c | 143 +++++++++ >> testcases/kernel/syscalls/landlock/landlock_exec.c | 9 + >> .../kernel/syscalls/landlock/landlock_tester.h | 350 >> +++++++++++++++++++++ >> 6 files changed, 510 insertions(+) >> >> diff --git a/runtest/syscalls b/runtest/syscalls >> index 1e2d682e3..9acdaf760 100644 >> --- a/runtest/syscalls >> +++ b/runtest/syscalls >> @@ -687,6 +687,7 @@ kill13 kill13 >> landlock01 landlock01 >> landlock02 landlock02 >> landlock03 landlock03 >> +landlock04 landlock04 >> >> lchown01 lchown01 >> lchown01_16 lchown01_16 >> diff --git a/testcases/kernel/syscalls/landlock/.gitignore >> b/testcases/kernel/syscalls/landlock/.gitignore >> index f79cd090b..4fe8d7cba 100644 >> --- a/testcases/kernel/syscalls/landlock/.gitignore >> +++ b/testcases/kernel/syscalls/landlock/.gitignore >> @@ -1,3 +1,5 @@ >> +landlock_exec >> landlock01 >> landlock02 >> landlock03 >> +landlock04 >> diff --git a/testcases/kernel/syscalls/landlock/Makefile >> b/testcases/kernel/syscalls/landlock/Makefile >> index 4b3e3fd8f..bdc6bd2d4 100644 >> --- a/testcases/kernel/syscalls/landlock/Makefile >> +++ b/testcases/kernel/syscalls/landlock/Makefile >> @@ -8,3 +8,8 @@ include $(top_srcdir)/include/mk/testcases.mk >> LDLIBS += -lc >> >> include $(top_srcdir)/include/mk/generic_leaf_target.mk >> + >> +# the reason why landlock_exec test binary is statically linked, is that >> +# we can't read libc out of the sandboxed folder once >> LANDLOCK_ACCESS_FS_EXECUTE >> +# has been activated >> +landlock_exec: LDLIBS += -static -fPIC >> > And, if we decided to build the program as static, we'd better adding glibc-static into the dependency list, otherwise it always failed to link the binary on RHEL distributions. --- a/ci/fedora.sh +++ b/ci/fedora.sh @@ -17,6 +17,7 @@ $yum \ numactl-devel \ libtirpc \ libtirpc-devel \ + libc-static \ perl-JSON \ perl-libwww-perl \ pkg-config \
I got some failures when this test was performed with exfat FS, but haven't figured out the reason, FYI: # uname -r 6.9.6-200.fc40.aarch64 # LTP_SINGLE_FS_TYPE=exfat ./landlock04 ... tst_test.c:1694: TINFO: === Testing on exfat === tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra opts='' tst_test.c:1120: TINFO: Mounting /dev/loop0 to /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0 landlock_common.h:30: TINFO: Landlock ABI v4 landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_CHAR landlock_tester.h:237: TINFO: Test normal or special files creation landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: EPERM (1) landlock_tester.h:120: TINFO: Test binary execution landlock_tester.h:131: TPASS: execve(FILE_EXEC, args, NULL) : EACCES (13) landlock_tester.h:146: TINFO: Test writing file landlock_tester.h:151: TPASS: open(FILE_WRITE, O_WRONLY, PERM_MODE) : EACCES (13) landlock_tester.h:159: TINFO: Test reading file ... tst_test.c:1694: TINFO: === Testing on exfat === tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra opts='' tst_test.c:1120: TINFO: Mounting /dev/loop0 to /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0 landlock_common.h:30: TINFO: Landlock ABI v4 landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_BLOCK landlock_tester.h:237: TINFO: Test normal or special files creation landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: EPERM (1) ... tst_test.c:1694: TINFO: === Testing on exfat === tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra opts='' tst_test.c:1120: TINFO: Mounting /dev/loop0 to /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0 landlock_common.h:30: TINFO: Landlock ABI v4 landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_SOCK landlock_tester.h:237: TINFO: Test normal or special files creation landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: EPERM (1) ... Regard, Li Wang
Hi Li, thanks for catching it. I had to install exfat in my VM in order to test it. Indeed, I think exfat doesn't support char/block/sock devices, since its focus is mainly SD cards and USB drives. We can easily skip that specific FS from the test, as I already done for vfat. Andrea On 7/3/24 10:20, Li Wang wrote: > > I got some failures when this test was performed with exfat FS, but > haven't figured out the reason, FYI: > > # uname -r > 6.9.6-200.fc40.aarch64 > > # LTP_SINGLE_FS_TYPE=exfat ./landlock04 > ... > tst_test.c:1694: TINFO: === Testing on exfat === > tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra > opts='' > tst_test.c:1120: TINFO: Mounting /dev/loop0 to > /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0 > landlock_common.h:30: TINFO: Landlock ABI v4 > landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_CHAR > landlock_tester.h:237: TINFO: Test normal or special files creation > landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: > EPERM (1) > landlock_tester.h:120: TINFO: Test binary execution > landlock_tester.h:131: TPASS: execve(FILE_EXEC, args, NULL) : EACCES (13) > landlock_tester.h:146: TINFO: Test writing file > landlock_tester.h:151: TPASS: open(FILE_WRITE, O_WRONLY, PERM_MODE) : > EACCES (13) > landlock_tester.h:159: TINFO: Test reading file > ... > tst_test.c:1694: TINFO: === Testing on exfat === > tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra > opts='' > tst_test.c:1120: TINFO: Mounting /dev/loop0 to > /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0 > landlock_common.h:30: TINFO: Landlock ABI v4 > landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_BLOCK > landlock_tester.h:237: TINFO: Test normal or special files creation > landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: > EPERM (1) > ... > tst_test.c:1694: TINFO: === Testing on exfat === > tst_test.c:1106: TINFO: Formatting /dev/loop0 with exfat opts='' extra > opts='' > tst_test.c:1120: TINFO: Mounting /dev/loop0 to > /tmp/LTP_lantEvE2G/sandbox fstyp=exfat flags=0 > landlock_common.h:30: TINFO: Landlock ABI v4 > landlock04.c:98: TINFO: Testing LANDLOCK_ACCESS_FS_MAKE_SOCK > landlock_tester.h:237: TINFO: Test normal or special files creation > landlock_tester.h:240: TFAIL: mknod(path, type | 0400, dev) failed: > EPERM (1) > ... > > > Regard, > Li Wang
Comments below. On 7/2/24 14:22, Li Wang wrote: > > > On Tue, Jul 2, 2024 at 4:00 PM Li Wang <liwang@redhat.com> wrote: > > > > On Mon, Jul 1, 2024 at 11:44 PM Andrea Cervesato > <andrea.cervesato@suse.de> wrote: > > From: Andrea Cervesato <andrea.cervesato@suse.com> > > This test verifies that all landlock rules are working properly. > The way we do it is to verify that all disabled syscalls are not > working but the one we enabled via specifc landlock rules. > > Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com> > --- > runtest/syscalls | 1 + > testcases/kernel/syscalls/landlock/.gitignore | 2 + > testcases/kernel/syscalls/landlock/Makefile | 5 + > testcases/kernel/syscalls/landlock/landlock04.c | 143 > +++++++++ > testcases/kernel/syscalls/landlock/landlock_exec.c | 9 + > .../kernel/syscalls/landlock/landlock_tester.h | 350 > +++++++++++++++++++++ > 6 files changed, 510 insertions(+) > > diff --git a/runtest/syscalls b/runtest/syscalls > index 1e2d682e3..9acdaf760 100644 > --- a/runtest/syscalls > +++ b/runtest/syscalls > @@ -687,6 +687,7 @@ kill13 kill13 > landlock01 landlock01 > landlock02 landlock02 > landlock03 landlock03 > +landlock04 landlock04 > > lchown01 lchown01 > lchown01_16 lchown01_16 > diff --git a/testcases/kernel/syscalls/landlock/.gitignore > b/testcases/kernel/syscalls/landlock/.gitignore > index f79cd090b..4fe8d7cba 100644 > --- a/testcases/kernel/syscalls/landlock/.gitignore > +++ b/testcases/kernel/syscalls/landlock/.gitignore > @@ -1,3 +1,5 @@ > +landlock_exec > landlock01 > landlock02 > landlock03 > +landlock04 > diff --git a/testcases/kernel/syscalls/landlock/Makefile > b/testcases/kernel/syscalls/landlock/Makefile > index 4b3e3fd8f..bdc6bd2d4 100644 > --- a/testcases/kernel/syscalls/landlock/Makefile > +++ b/testcases/kernel/syscalls/landlock/Makefile > @@ -8,3 +8,8 @@ include $(top_srcdir)/include/mk/testcases.mk > <http://testcases.mk> > LDLIBS += -lc > > include $(top_srcdir)/include/mk/generic_leaf_target.mk > <http://generic_leaf_target.mk> > + > +# the reason why landlock_exec test binary is statically > linked, is that > +# we can't read libc out of the sandboxed folder once > LANDLOCK_ACCESS_FS_EXECUTE > +# has been activated > +landlock_exec: LDLIBS += -static -fPIC > > > And, if we decided to build the program as static, we'd better > adding glibc-static into the dependency list, otherwise it always > failed to link the binary on RHEL distributions. > We found a solution for this. We compile landlock_exec with dynamic linking, then we apply READ + EXEC rules to /lib and /lib64. In this way we can easily execute the binary and we don't need more LTP dependences. > --- a/ci/fedora.sh > +++ b/ci/fedora.sh > @@ -17,6 +17,7 @@ $yum \ > numactl-devel \ > libtirpc \ > libtirpc-devel \ > + libc-static \ > perl-JSON \ > perl-libwww-perl \ > pkg-config \ > > > > -- > Regards, > Li Wang Andrea
Andrea Cervesato <andrea.cervesato@suse.com> wrote: > +# the reason why landlock_exec test binary is statically linked, is that >>> +# we can't read libc out of the sandboxed folder once >>> LANDLOCK_ACCESS_FS_EXECUTE >>> +# has been activated >>> +landlock_exec: LDLIBS += -static -fPIC >>> >> > And, if we decided to build the program as static, we'd better > adding glibc-static into the dependency list, otherwise it always > failed to link the binary on RHEL distributions. > > We found a solution for this. We compile landlock_exec with dynamic > linking, then we apply READ + EXEC rules to /lib and /lib64. > In this way we can easily execute the binary and we don't need more LTP > dependences. > Sounds good.
diff --git a/runtest/syscalls b/runtest/syscalls index 1e2d682e3..9acdaf760 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -687,6 +687,7 @@ kill13 kill13 landlock01 landlock01 landlock02 landlock02 landlock03 landlock03 +landlock04 landlock04 lchown01 lchown01 lchown01_16 lchown01_16 diff --git a/testcases/kernel/syscalls/landlock/.gitignore b/testcases/kernel/syscalls/landlock/.gitignore index f79cd090b..4fe8d7cba 100644 --- a/testcases/kernel/syscalls/landlock/.gitignore +++ b/testcases/kernel/syscalls/landlock/.gitignore @@ -1,3 +1,5 @@ +landlock_exec landlock01 landlock02 landlock03 +landlock04 diff --git a/testcases/kernel/syscalls/landlock/Makefile b/testcases/kernel/syscalls/landlock/Makefile index 4b3e3fd8f..bdc6bd2d4 100644 --- a/testcases/kernel/syscalls/landlock/Makefile +++ b/testcases/kernel/syscalls/landlock/Makefile @@ -8,3 +8,8 @@ include $(top_srcdir)/include/mk/testcases.mk LDLIBS += -lc include $(top_srcdir)/include/mk/generic_leaf_target.mk + +# the reason why landlock_exec test binary is statically linked, is that +# we can't read libc out of the sandboxed folder once LANDLOCK_ACCESS_FS_EXECUTE +# has been activated +landlock_exec: LDLIBS += -static -fPIC diff --git a/testcases/kernel/syscalls/landlock/landlock04.c b/testcases/kernel/syscalls/landlock/landlock04.c new file mode 100644 index 000000000..1e7c6f3d1 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock04.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com> + */ + +/*\ + * [Description] + * + * This test verifies that all landlock rules are working properly. The way we + * do it is to verify that all disabled syscalls are not working but the one we + * enabled via specifc landlock rules. + */ + +#include "landlock_common.h" +#include "landlock_tester.h" + +#define ACCESS_NAME(x) #x + +static struct landlock_ruleset_attr *ruleset_attr; +static struct landlock_path_beneath_attr *path_beneath_attr; + +static struct tvariant { + int access; + char *desc; +} tvariants[] = { + { + LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_EXECUTE, + ACCESS_NAME(LANDLOCK_ACCESS_FS_EXECUTE) + }, + { + LANDLOCK_ACCESS_FS_WRITE_FILE, + ACCESS_NAME(LANDLOCK_ACCESS_FS_WRITE_FILE) + }, + { + LANDLOCK_ACCESS_FS_READ_FILE, + ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_FILE) + }, + { + LANDLOCK_ACCESS_FS_READ_DIR, + ACCESS_NAME(LANDLOCK_ACCESS_FS_READ_DIR) + }, + { + LANDLOCK_ACCESS_FS_REMOVE_DIR, + ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_DIR) + }, + { + LANDLOCK_ACCESS_FS_REMOVE_FILE, + ACCESS_NAME(LANDLOCK_ACCESS_FS_REMOVE_FILE) + }, + { + LANDLOCK_ACCESS_FS_MAKE_CHAR, + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_CHAR) + }, + { + LANDLOCK_ACCESS_FS_MAKE_BLOCK, + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_BLOCK) + }, + { + LANDLOCK_ACCESS_FS_MAKE_REG, + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_REG) + }, + { + LANDLOCK_ACCESS_FS_MAKE_SOCK, + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SOCK) + }, + { + LANDLOCK_ACCESS_FS_MAKE_FIFO, + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_FIFO) + }, + { + LANDLOCK_ACCESS_FS_MAKE_SYM, + ACCESS_NAME(LANDLOCK_ACCESS_FS_MAKE_SYM) + }, + { + LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_TRUNCATE, + ACCESS_NAME(LANDLOCK_ACCESS_FS_TRUNCATE) + }, +}; + +static void run(void) +{ + if (!SAFE_FORK()) { + struct tvariant variant = tvariants[tst_variant]; + + tester_run_all_rules(variant.access); + _exit(0); + } +} + +static void setup(void) +{ + struct tvariant variant = tvariants[tst_variant]; + + verify_landlock_is_enabled(); + tester_create_tree(); + + tst_res(TINFO, "Testing %s", variant.desc); + + ruleset_attr->handled_access_fs = tester_get_all_rules(); + + apply_landlock_layer( + ruleset_attr, + path_beneath_attr, + SANDBOX_FOLDER, + variant.access); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .min_kver = "5.13", + .forks_child = 1, + .needs_tmpdir = 1, + .needs_root = 1, + .test_variants = ARRAY_SIZE(tvariants), + .resource_files = (const char *[]) { + TESTAPP, + NULL, + }, + .needs_kconfigs = (const char *[]) { + "CONFIG_SECURITY_LANDLOCK=y", + NULL + }, + .bufs = (struct tst_buffers []) { + {&ruleset_attr, .size = sizeof(struct landlock_ruleset_attr)}, + {&path_beneath_attr, .size = sizeof(struct landlock_path_beneath_attr)}, + {}, + }, + .caps = (struct tst_cap []) { + TST_CAP(TST_CAP_REQ, CAP_SYS_ADMIN), + TST_CAP(TST_CAP_REQ, CAP_MKNOD), + {} + }, + .format_device = 1, + .mount_device = 1, + .mntpoint = SANDBOX_FOLDER, + .all_filesystems = 1, + .skip_filesystems = (const char *[]) { + "vfat", + NULL + }, + .max_runtime = 3600, +}; diff --git a/testcases/kernel/syscalls/landlock/landlock_exec.c b/testcases/kernel/syscalls/landlock/landlock_exec.c new file mode 100644 index 000000000..aae5c76b2 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock_exec.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com> + */ + +int main(void) +{ + return 0; +} diff --git a/testcases/kernel/syscalls/landlock/landlock_tester.h b/testcases/kernel/syscalls/landlock/landlock_tester.h new file mode 100644 index 000000000..89ca085d7 --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock_tester.h @@ -0,0 +1,350 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com> + */ + +#ifndef LANDLOCK_TESTER_H + +#include "tst_test.h" +#include "lapi/landlock.h" +#include <sys/sysmacros.h> + +#define PERM_MODE 0700 + +#define SANDBOX_FOLDER "sandbox" +#define TESTAPP "landlock_exec" + +#define FILE_EXEC SANDBOX_FOLDER"/"TESTAPP +#define FILE_READ SANDBOX_FOLDER"/file_read" +#define FILE_WRITE SANDBOX_FOLDER"/file_write" +#define FILE_REMOVE SANDBOX_FOLDER"/file_remove" +#define FILE_UNLINK SANDBOX_FOLDER"/file_unlink" +#define FILE_UNLINKAT SANDBOX_FOLDER"/file_unlinkat" +#define FILE_TRUNCATE SANDBOX_FOLDER"/file_truncate" +#define FILE_REGULAR SANDBOX_FOLDER"/regular0" +#define FILE_SOCKET SANDBOX_FOLDER"/socket0" +#define FILE_FIFO SANDBOX_FOLDER"/fifo0" +#define FILE_SYM0 SANDBOX_FOLDER"/symbolic0" +#define FILE_SYM1 SANDBOX_FOLDER"/symbolic1" +#define DIR_READDIR SANDBOX_FOLDER"/dir_readdir" +#define DIR_RMDIR SANDBOX_FOLDER"/dir_rmdir" +#define DEV_CHAR0 SANDBOX_FOLDER"/chardev0" +#define DEV_BLK0 SANDBOX_FOLDER"/blkdev0" + +#define ALL_RULES (\ + LANDLOCK_ACCESS_FS_EXECUTE | \ + LANDLOCK_ACCESS_FS_WRITE_FILE | \ + LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_READ_DIR | \ + LANDLOCK_ACCESS_FS_REMOVE_DIR | \ + LANDLOCK_ACCESS_FS_REMOVE_FILE | \ + LANDLOCK_ACCESS_FS_MAKE_CHAR | \ + LANDLOCK_ACCESS_FS_MAKE_DIR | \ + LANDLOCK_ACCESS_FS_MAKE_REG | \ + LANDLOCK_ACCESS_FS_MAKE_SOCK | \ + LANDLOCK_ACCESS_FS_MAKE_FIFO | \ + LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ + LANDLOCK_ACCESS_FS_MAKE_SYM | \ + LANDLOCK_ACCESS_FS_REFER | \ + LANDLOCK_ACCESS_FS_TRUNCATE | \ + LANDLOCK_ACCESS_NET_BIND_TCP | \ + LANDLOCK_ACCESS_NET_CONNECT_TCP | \ + LANDLOCK_ACCESS_FS_IOCTL_DEV) + +static char *readdir_files[] = { + DIR_READDIR"/file0", + DIR_READDIR"/file1", + DIR_READDIR"/file2", +}; + +static int dev_chr; +static int dev_blk; + +static int tester_get_all_rules(void) +{ + int abi; + int all_rules = ALL_RULES; + + abi = SAFE_LANDLOCK_CREATE_RULESET( + NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + + if (abi < 2) + all_rules &= ~LANDLOCK_ACCESS_FS_REFER; + + if (abi < 3) + all_rules &= ~LANDLOCK_ACCESS_FS_TRUNCATE; + + if (abi < 4) { + all_rules &= ~(LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP); + } + + if (abi < 5) + all_rules &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV; + + return all_rules; +} + +static void tester_create_tree(void) +{ + if (access(SANDBOX_FOLDER, F_OK) == -1) + SAFE_MKDIR(SANDBOX_FOLDER, PERM_MODE); + + /* folders */ + SAFE_MKDIR(DIR_RMDIR, PERM_MODE); + SAFE_MKDIR(DIR_READDIR, PERM_MODE); + for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++) + SAFE_TOUCH(readdir_files[i], PERM_MODE, NULL); + + /* files */ + tst_fill_file(FILE_READ, 'a', getpagesize(), 1); + SAFE_TOUCH(FILE_WRITE, PERM_MODE, NULL); + SAFE_TOUCH(FILE_REMOVE, PERM_MODE, NULL); + SAFE_TOUCH(FILE_UNLINK, PERM_MODE, NULL); + SAFE_TOUCH(FILE_UNLINKAT, PERM_MODE, NULL); + SAFE_TOUCH(FILE_TRUNCATE, PERM_MODE, NULL); + SAFE_TOUCH(FILE_SYM0, PERM_MODE, NULL); + SAFE_CP(TESTAPP, FILE_EXEC); + + /* devices */ + dev_chr = makedev(1, 3); + dev_blk = makedev(7, 0); +} + +static void _test_exec(const int result) +{ + int status; + pid_t pid; + char *const args[] = {(char *)FILE_EXEC, NULL}; + + tst_res(TINFO, "Test binary execution"); + + pid = SAFE_FORK(); + if (!pid) { + int rval; + + if (result == TPASS) { + rval = execve(FILE_EXEC, args, NULL); + if (rval == -1) + tst_res(TFAIL | TERRNO, "Failed to execute test binary"); + } else { + TST_EXP_FAIL(execve(FILE_EXEC, args, NULL), EACCES); + } + + _exit(1); + } + + SAFE_WAITPID(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return; + + tst_res(result, "Test binary has been executed"); +} + +static void _test_write(const int result) +{ + tst_res(TINFO, "Test writing file"); + + if (result == TPASS) + TST_EXP_FD(open(FILE_WRITE, O_WRONLY, PERM_MODE)); + else + TST_EXP_FAIL(open(FILE_WRITE, O_WRONLY, PERM_MODE), EACCES); + + if (TST_RET != -1) + SAFE_CLOSE(TST_RET); +} + +static void _test_read(const int result) +{ + tst_res(TINFO, "Test reading file"); + + if (result == TPASS) + TST_EXP_FD(open(FILE_READ, O_RDONLY, PERM_MODE)); + else + TST_EXP_FAIL(open(FILE_READ, O_RDONLY, PERM_MODE), EACCES); + + if (TST_RET != -1) + SAFE_CLOSE(TST_RET); +} + +static void _test_readdir(const int result) +{ + tst_res(TINFO, "Test reading directory"); + + DIR *dir; + struct dirent *de; + int files_counted = 0; + + dir = opendir(DIR_READDIR); + if (!dir) { + tst_res(result == TPASS ? TFAIL : TPASS, + "Can't read '%s' directory", DIR_READDIR); + + return; + } + + tst_res(result, "Can read '%s' directory", DIR_READDIR); + if (result == TFAIL) + return; + + while ((de = readdir(dir)) != NULL) { + if (de->d_type != DT_REG) + continue; + + for (size_t i = 0; i < ARRAY_SIZE(readdir_files); i++) { + if (readdir_files[i] == NULL) + continue; + + if (strstr(readdir_files[i], de->d_name) != NULL) + files_counted++; + } + } + + SAFE_CLOSEDIR(dir); + + TST_EXP_EQ_LI(files_counted, ARRAY_SIZE(readdir_files)); +} + +static void _test_rmdir(const int result) +{ + tst_res(TINFO, "Test removing directory"); + + if (result == TPASS) + TST_EXP_PASS(rmdir(DIR_RMDIR)); + else + TST_EXP_FAIL(rmdir(DIR_RMDIR), EACCES); +} + +static void _test_rmfile(const int result) +{ + tst_res(TINFO, "Test removing file"); + + if (result == TPASS) { + TST_EXP_PASS(unlink(FILE_UNLINK)); + TST_EXP_PASS(remove(FILE_REMOVE)); + } else { + TST_EXP_FAIL(unlink(FILE_UNLINK), EACCES); + TST_EXP_FAIL(remove(FILE_REMOVE), EACCES); + } +} + +static void _test_make( + const char *path, + const int type, + const int dev, + const int result) +{ + tst_res(TINFO, "Test normal or special files creation"); + + if (result == TPASS) + TST_EXP_PASS(mknod(path, type | 0400, dev)); + else + TST_EXP_FAIL(mknod(path, type | 0400, dev), EACCES); +} + +static void _test_symbolic(const int result) +{ + tst_res(TINFO, "Test symbolic links"); + + if (result == TPASS) + TST_EXP_PASS(symlink(FILE_SYM0, FILE_SYM1)); + else + TST_EXP_FAIL(symlink(FILE_SYM0, FILE_SYM1), EACCES); +} + +static void _test_truncate(const int result) +{ + int fd; + + tst_res(TINFO, "Test truncating file"); + + if (result == TPASS) { + TST_EXP_PASS(truncate(FILE_TRUNCATE, 10)); + + fd = TST_EXP_FD(open(FILE_TRUNCATE, O_WRONLY, PERM_MODE)); + if (fd != -1) { + TST_EXP_PASS(ftruncate(fd, 10)); + SAFE_CLOSE(fd); + } + + fd = TST_EXP_FD(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC, PERM_MODE)); + if (fd != -1) + SAFE_CLOSE(fd); + } else { + TST_EXP_FAIL(truncate(FILE_TRUNCATE, 10), EACCES); + + fd = open(FILE_TRUNCATE, O_WRONLY, PERM_MODE); + if (fd != -1) { + TST_EXP_FAIL(ftruncate(fd, 10), EACCES); + SAFE_CLOSE(fd); + } + + TST_EXP_FAIL(open(FILE_TRUNCATE, O_WRONLY | O_TRUNC, PERM_MODE), + EACCES); + + if (TST_RET != -1) + SAFE_CLOSE(TST_RET); + } +} + +static void tester_run_rules(const int rules, const int result) +{ + if (rules & LANDLOCK_ACCESS_FS_EXECUTE) + _test_exec(result); + + if (rules & LANDLOCK_ACCESS_FS_WRITE_FILE) + _test_write(result); + + if (rules & LANDLOCK_ACCESS_FS_READ_FILE) + _test_read(result); + + if (rules & LANDLOCK_ACCESS_FS_READ_DIR) + _test_readdir(result); + + if (rules & LANDLOCK_ACCESS_FS_REMOVE_DIR) + _test_rmdir(result); + + if (rules & LANDLOCK_ACCESS_FS_REMOVE_FILE) + _test_rmfile(result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_CHAR) + _test_make(DEV_CHAR0, S_IFCHR, dev_chr, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_BLOCK) + _test_make(DEV_BLK0, S_IFBLK, dev_blk, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_REG) + _test_make(FILE_REGULAR, S_IFREG, 0, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_SOCK) + _test_make(FILE_SOCKET, S_IFSOCK, 0, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_FIFO) + _test_make(FILE_FIFO, S_IFIFO, 0, result); + + if (rules & LANDLOCK_ACCESS_FS_MAKE_SYM) + _test_symbolic(result); + + if (rules & LANDLOCK_ACCESS_FS_TRUNCATE) { + if ((tst_kvercmp(6, 2, 0)) < 0) { + tst_res(TINFO, "Skip truncate test. Minimum kernel version is 6.2"); + return; + } + + _test_truncate(result); + } +} + +static inline void tester_run_all_rules(const int pass_rules) +{ + int fail_rules; + int all_rules; + + all_rules = tester_get_all_rules(); + fail_rules = all_rules & ~pass_rules; + + tester_run_rules(pass_rules, TPASS); + tester_run_rules(fail_rules, TFAIL); +} + +#endif