Message ID | 20240711-landlock-v3-9-c7b0e9edf9b0@suse.com |
---|---|
State | Accepted |
Headers | show |
Series | landlock testing suite | expand |
Nice work! Reviewed-by: Li Wang <liwang@redhat.com> On Thu, Jul 11, 2024 at 7:21 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/landlock04.c | 214 +++++++++++++ > testcases/kernel/syscalls/landlock/landlock_exec.c | 9 + > .../kernel/syscalls/landlock/landlock_tester.h | 350 > +++++++++++++++++++++ > 5 files changed, 576 insertions(+) > > diff --git a/runtest/syscalls b/runtest/syscalls > index f2b64c0df..3c7cd66e2 100644 > --- a/runtest/syscalls > +++ b/runtest/syscalls > @@ -688,6 +688,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/landlock04.c > b/testcases/kernel/syscalls/landlock/landlock04.c > new file mode 100644 > index 000000000..30fd9400f > --- /dev/null > +++ b/testcases/kernel/syscalls/landlock/landlock04.c > @@ -0,0 +1,214 @@ > +// 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" > +#include "tst_safe_stdio.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 enable_exec_libs(const int ruleset_fd) > +{ > + FILE *fp; > + char line[1024]; > + char path[PATH_MAX]; > + char dependency[8][PATH_MAX]; > + int count = 0; > + int duplicate = 0; > + > + fp = SAFE_FOPEN("/proc/self/maps", "r"); > + > + while (fgets(line, sizeof(line), fp)) { > + if (strstr(line, ".so") == NULL) > + continue; > + > + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path); > + > + for (int i = 0; i < count; i++) { > + if (strcmp(path, dependency[i]) == 0) { > + duplicate = 1; > + break; > + } > + } > + > + if (duplicate) { > + duplicate = 0; > + continue; > + } > + > + strncpy(dependency[count], path, PATH_MAX); > + count++; > + > + tst_res(TINFO, "Enable read/exec permissions for %s", > path); > + > + path_beneath_attr->allowed_access = > + LANDLOCK_ACCESS_FS_READ_FILE | > + LANDLOCK_ACCESS_FS_EXECUTE; > + path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | > O_CLOEXEC); > + > + SAFE_LANDLOCK_ADD_RULE( > + ruleset_fd, > + LANDLOCK_RULE_PATH_BENEATH, > + path_beneath_attr, > + 0); > + > + SAFE_CLOSE(path_beneath_attr->parent_fd); > + } > + > + SAFE_FCLOSE(fp); > +} > + > +static void setup(void) > +{ > + struct tvariant variant = tvariants[tst_variant]; > + int ruleset_fd; > + > + verify_landlock_is_enabled(); > + tester_create_tree(); > + > + tst_res(TINFO, "Testing %s", variant.desc); > + > + ruleset_attr->handled_access_fs = tester_get_all_rules(); > + > + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET( > + ruleset_attr, sizeof(struct landlock_ruleset_attr), 0); > + > + /* since our binary is dynamically linked, we need to enable > dependences > + * to be read and executed > + */ > + enable_exec_libs(ruleset_fd); > + > + path_beneath_attr->allowed_access = variant.access; > + path_beneath_attr->parent_fd = SAFE_OPEN( > + SANDBOX_FOLDER, O_PATH | O_CLOEXEC); > + > + SAFE_LANDLOCK_ADD_RULE( > + ruleset_fd, > + LANDLOCK_RULE_PATH_BENEATH, > + path_beneath_attr, > + 0); > + > + SAFE_CLOSE(path_beneath_attr->parent_fd); > + > + enforce_ruleset(ruleset_fd); > + SAFE_CLOSE(ruleset_fd); > +} > + > +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", > + "exfat", > + 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 > > -- > 2.43.0 > > > -- > Mailing list info: https://lists.linux.it/listinfo/ltp > >
Hi Andrea, ... > +static void enable_exec_libs(const int ruleset_fd) > +{ > + FILE *fp; > + char line[1024]; > + char path[PATH_MAX]; > + char dependency[8][PATH_MAX]; > + int count = 0; > + int duplicate = 0; > + > + fp = SAFE_FOPEN("/proc/self/maps", "r"); > + > + while (fgets(line, sizeof(line), fp)) { > + if (strstr(line, ".so") == NULL) > + continue; > + > + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path); > + > + for (int i = 0; i < count; i++) { > + if (strcmp(path, dependency[i]) == 0) { > + duplicate = 1; > + break; > + } > + } > + > + if (duplicate) { > + duplicate = 0; > + continue; > + } > + > + strncpy(dependency[count], path, PATH_MAX); > + count++; > + > + tst_res(TINFO, "Enable read/exec permissions for %s", path); > + > + path_beneath_attr->allowed_access = > + LANDLOCK_ACCESS_FS_READ_FILE | > + LANDLOCK_ACCESS_FS_EXECUTE; > + path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC); > + > + SAFE_LANDLOCK_ADD_RULE( > + ruleset_fd, > + LANDLOCK_RULE_PATH_BENEATH, > + path_beneath_attr, > + 0); Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it fails (after fresh boot) with: ... tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs tst_test.c:1746: TINFO: === Testing on ext2 === tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra opts='' mke2fs 1.47.0 (5-Feb-2023) tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0 landlock_common.h:30: TINFO: Landlock ABI v3 landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE landlock04.c:123: TINFO: Enable read/exec permissions for /usr/lib/i386-linux-gnu/libc.so.6 landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL (22) tst_test.c:1746: TINFO: === Testing on ext3 === It works on 6.10.0-rc7-3.g92abc10-default (Tumbleweed), 6.9.8-amd64 (Debian). Any hint, what could be wrong? Kind regards, Petr > + > + SAFE_CLOSE(path_beneath_attr->parent_fd); > + } > + > + SAFE_FCLOSE(fp); > +}
Hi Petr, I honestly have no idea why it fails on that machine. All the landlock features we are using are there since version 1 and this test should work from kernel 5.13, so EINVAL is not justified to that. The exable_exec_libs is correctly guessing the path of the libc and that is also correct. Andrea On 7/16/24 19:27, Petr Vorel wrote: > Hi Andrea, > > ... >> +static void enable_exec_libs(const int ruleset_fd) >> +{ >> + FILE *fp; >> + char line[1024]; >> + char path[PATH_MAX]; >> + char dependency[8][PATH_MAX]; >> + int count = 0; >> + int duplicate = 0; >> + >> + fp = SAFE_FOPEN("/proc/self/maps", "r"); >> + >> + while (fgets(line, sizeof(line), fp)) { >> + if (strstr(line, ".so") == NULL) >> + continue; >> + >> + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path); >> + >> + for (int i = 0; i < count; i++) { >> + if (strcmp(path, dependency[i]) == 0) { >> + duplicate = 1; >> + break; >> + } >> + } >> + >> + if (duplicate) { >> + duplicate = 0; >> + continue; >> + } >> + >> + strncpy(dependency[count], path, PATH_MAX); >> + count++; >> + >> + tst_res(TINFO, "Enable read/exec permissions for %s", path); >> + >> + path_beneath_attr->allowed_access = >> + LANDLOCK_ACCESS_FS_READ_FILE | >> + LANDLOCK_ACCESS_FS_EXECUTE; >> + path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC); >> + >> + SAFE_LANDLOCK_ADD_RULE( >> + ruleset_fd, >> + LANDLOCK_RULE_PATH_BENEATH, >> + path_beneath_attr, >> + 0); > Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it fails (after > fresh boot) with: > > ... > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs > tst_test.c:1746: TINFO: === Testing on ext2 === > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra opts='' > mke2fs 1.47.0 (5-Feb-2023) > tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0 > landlock_common.h:30: TINFO: Landlock ABI v3 > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE > landlock04.c:123: TINFO: Enable read/exec permissions for /usr/lib/i386-linux-gnu/libc.so.6 > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL (22) > tst_test.c:1746: TINFO: === Testing on ext3 === > > It works on 6.10.0-rc7-3.g92abc10-default (Tumbleweed), 6.9.8-amd64 (Debian). > > Any hint, what could be wrong? > > Kind regards, > Petr > >> + >> + SAFE_CLOSE(path_beneath_attr->parent_fd); >> + } >> + >> + SAFE_FCLOSE(fp); >> +}
Hi Petr, Andrea, On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote: > Hi Andrea, > > ... > > +static void enable_exec_libs(const int ruleset_fd) > > +{ > > + FILE *fp; > > + char line[1024]; > > + char path[PATH_MAX]; > > + char dependency[8][PATH_MAX]; > > + int count = 0; > > + int duplicate = 0; > > + > > + fp = SAFE_FOPEN("/proc/self/maps", "r"); > > + > > + while (fgets(line, sizeof(line), fp)) { > > + if (strstr(line, ".so") == NULL) > > + continue; > > + > > + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path); > > + > > + for (int i = 0; i < count; i++) { > > + if (strcmp(path, dependency[i]) == 0) { > > + duplicate = 1; > > + break; > > + } > > + } > > + > > + if (duplicate) { > > + duplicate = 0; > > + continue; > > + } > > + > > + strncpy(dependency[count], path, PATH_MAX); > > + count++; > > + > > + tst_res(TINFO, "Enable read/exec permissions for %s", > path); > > + > > + path_beneath_attr->allowed_access = > > + LANDLOCK_ACCESS_FS_READ_FILE | > > + LANDLOCK_ACCESS_FS_EXECUTE; > > + path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | > O_CLOEXEC); > > + > > + SAFE_LANDLOCK_ADD_RULE( > > + ruleset_fd, > > + LANDLOCK_RULE_PATH_BENEATH, > > + path_beneath_attr, > > + 0); > > Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it fails > (after > fresh boot) with: > > ... > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs > tst_test.c:1746: TINFO: === Testing on ext2 === > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra > opts='' > mke2fs 1.47.0 (5-Feb-2023) > tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox > fstyp=ext2 flags=0 > landlock_common.h:30: TINFO: Landlock ABI v3 > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE > landlock04.c:123: TINFO: Enable read/exec permissions for > /usr/lib/i386-linux-gnu/libc.so.6 > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL > (22) > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was refactored from the v6.7 mainline kernel, so it can't add the rule correctly with older kernels. commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> Date: Thu Oct 26 09:47:46 2023 +0800 landlock: Refactor landlock_add_rule() syscall But this is my guess (through reading the code), I didn't do more to verify that by installing such a kernel.
Hi Li, Andrea, > Hi Petr, Andrea, ... > > ... > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs > > tst_test.c:1746: TINFO: === Testing on ext2 === > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra > > opts='' > > mke2fs 1.47.0 (5-Feb-2023) > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to /tmp/LTP_lant6WbKJ/sandbox > > fstyp=ext2 flags=0 > > landlock_common.h:30: TINFO: Landlock ABI v3 > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE > > landlock04.c:123: TINFO: Enable read/exec permissions for > > /usr/lib/i386-linux-gnu/libc.so.6 > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL > > (22) > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was > refactored from the v6.7 mainline kernel, so it can't add the rule correctly > with older kernels. > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > Date: Thu Oct 26 09:47:46 2023 +0800 > landlock: Refactor landlock_add_rule() syscall > But this is my guess (through reading the code), I didn't do more to > verify that by installing such a kernel. Thanks, Li, for a hint. Quick test shows that it's working on 6.9.9, but broken with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify this specific commit, I suppose we should require 6.7, right? Kind regards, Petr
On Wed, Jul 24, 2024 at 9:30 PM Petr Vorel <pvorel@suse.cz> wrote: > Hi Li, Andrea, > > > Hi Petr, Andrea, > ... > > > ... > > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs > > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs > > > tst_test.c:1746: TINFO: === Testing on ext2 === > > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra > > > opts='' > > > mke2fs 1.47.0 (5-Feb-2023) > > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to > /tmp/LTP_lant6WbKJ/sandbox > > > fstyp=ext2 flags=0 > > > landlock_common.h:30: TINFO: Landlock ABI v3 > > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE > > > landlock04.c:123: TINFO: Enable read/exec permissions for > > > /usr/lib/i386-linux-gnu/libc.so.6 > > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL > > > (22) > > > > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was > > refactored from the v6.7 mainline kernel, so it can't add the rule > correctly > > with older kernels. > > > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 > > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > > Date: Thu Oct 26 09:47:46 2023 +0800 > > > landlock: Refactor landlock_add_rule() syscall > > > But this is my guess (through reading the code), I didn't do more to > > verify that by installing such a kernel. > > Thanks, Li, for a hint. Quick test shows that it's working on 6.9.9, but > broken > with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify this > specific commit, I suppose we should require 6.7, right? > I think YES, 6.7 includes some vital improvement on the landlock syscall.
> On Wed, Jul 24, 2024 at 9:30 PM Petr Vorel <pvorel@suse.cz> wrote: > > Hi Li, Andrea, > > > Hi Petr, Andrea, > > ... > > > > ... > > > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs > > > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs > > > > tst_test.c:1746: TINFO: === Testing on ext2 === > > > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra > > > > opts='' > > > > mke2fs 1.47.0 (5-Feb-2023) > > > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to > > /tmp/LTP_lant6WbKJ/sandbox > > > > fstyp=ext2 flags=0 > > > > landlock_common.h:30: TINFO: Landlock ABI v3 > > > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE > > > > landlock04.c:123: TINFO: Enable read/exec permissions for > > > > /usr/lib/i386-linux-gnu/libc.so.6 > > > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): EINVAL > > > > (22) > > > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was > > > refactored from the v6.7 mainline kernel, so it can't add the rule > > correctly > > > with older kernels. > > > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 > > > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > > > Date: Thu Oct 26 09:47:46 2023 +0800 > > > landlock: Refactor landlock_add_rule() syscall > > > But this is my guess (through reading the code), I didn't do more to > > > verify that by installing such a kernel. > > Thanks, Li, for a hint. Quick test shows that it's working on 6.9.9, but > > broken > > with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify this > > specific commit, I suppose we should require 6.7, right? > I think YES, 6.7 includes some vital improvement on the landlock syscall. OK, I'm for merging with .min_kver = "6.7". What blocking me to merge is the fact landlock03 needs to fix for -i2. But let's discuss it there. Kind regards, Petr
On Wed, Jul 24, 2024 at 9:37 PM Li Wang <liwang@redhat.com> wrote: > > > On Wed, Jul 24, 2024 at 9:30 PM Petr Vorel <pvorel@suse.cz> wrote: > >> Hi Li, Andrea, >> >> > Hi Petr, Andrea, >> ... >> > > ... >> > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs >> > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs >> > > tst_test.c:1746: TINFO: === Testing on ext2 === >> > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' extra >> > > opts='' >> > > mke2fs 1.47.0 (5-Feb-2023) >> > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to >> /tmp/LTP_lant6WbKJ/sandbox >> > > fstyp=ext2 flags=0 >> > > landlock_common.h:30: TINFO: Landlock ABI v3 >> > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE >> > > landlock04.c:123: TINFO: Enable read/exec permissions for >> > > /usr/lib/i386-linux-gnu/libc.so.6 >> > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): >> EINVAL >> > > (22) >> >> >> > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was >> > refactored from the v6.7 mainline kernel, so it can't add the rule >> correctly >> > with older kernels. >> >> > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 >> > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> >> > Date: Thu Oct 26 09:47:46 2023 +0800 >> >> > landlock: Refactor landlock_add_rule() syscall >> >> > But this is my guess (through reading the code), I didn't do more to >> > verify that by installing such a kernel. >> >> Thanks, Li, for a hint. Quick test shows that it's working on 6.9.9, but >> broken >> with with 6.6.x or 6.5.x, also reproduced on more distros. I'll verify >> this >> specific commit, I suppose we should require 6.7, right? >> > And BTW, the below commit is another (on my suspicion list) that needs check. commit 13fc6455fa19b0859e1b9640bf09903bec8df4f4 Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> Date: Thu Oct 26 09:47:40 2023 +0800 landlock: Make ruleset's access masks more generic > I think YES, 6.7 includes some vital improvement on the landlock syscall. > > > -- > Regards, > Li Wang >
Hi Li, thanks for checking. Mmmh I don't know if it's because they added LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would break all the other features. Andrea On 7/24/24 14:12, Li Wang wrote: > Hi Petr, Andrea, > > On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote: > > Hi Andrea, > > ... > > +static void enable_exec_libs(const int ruleset_fd) > > +{ > > + FILE *fp; > > + char line[1024]; > > + char path[PATH_MAX]; > > + char dependency[8][PATH_MAX]; > > + int count = 0; > > + int duplicate = 0; > > + > > + fp = SAFE_FOPEN("/proc/self/maps", "r"); > > + > > + while (fgets(line, sizeof(line), fp)) { > > + if (strstr(line, ".so") == NULL) > > + continue; > > + > > + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path); > > + > > + for (int i = 0; i < count; i++) { > > + if (strcmp(path, dependency[i]) == 0) { > > + duplicate = 1; > > + break; > > + } > > + } > > + > > + if (duplicate) { > > + duplicate = 0; > > + continue; > > + } > > + > > + strncpy(dependency[count], path, PATH_MAX); > > + count++; > > + > > + tst_res(TINFO, "Enable read/exec permissions for > %s", path); > > + > > + path_beneath_attr->allowed_access = > > + LANDLOCK_ACCESS_FS_READ_FILE | > > + LANDLOCK_ACCESS_FS_EXECUTE; > > + path_beneath_attr->parent_fd = SAFE_OPEN(path, > O_PATH | O_CLOEXEC); > > + > > + SAFE_LANDLOCK_ADD_RULE( > > + ruleset_fd, > > + LANDLOCK_RULE_PATH_BENEATH, > > + path_beneath_attr, > > + 0); > > Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it > fails (after > fresh boot) with: > > ... > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs > tst_test.c:1746: TINFO: === Testing on ext2 === > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' > extra opts='' > mke2fs 1.47.0 (5-Feb-2023) > tst_test.c:1123: TINFO: Mounting /dev/loop1 to > /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0 > landlock_common.h:30: TINFO: Landlock ABI v3 > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE > landlock04.c:123: TINFO: Enable read/exec permissions for > /usr/lib/i386-linux-gnu/libc.so.6 > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): > EINVAL (22) > > > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was > refactored from the v6.7 mainline kernel, so it can't add the rule > correctly > with older kernels. > > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > Date: Thu Oct 26 09:47:46 2023 +0800 > > landlock: Refactor landlock_add_rule() syscall > > But this is my guess (through reading the code), I didn't do more to > verify that by installing such a kernel. > > > -- > Regards, > Li Wang
Hi! it seems like the landlock() support in kernel 6.6 is different than the one in 6.7. The reason why we see that error in kernel <=6.6 is related to how landlock is handling the rules set according to the rule we want to enable. Let's suppose we want to enable the execution for a file. What we should be able to do, is to consider __ALL__ the rules available for landlock, then to enable EXEC only for a specific file. Then, if we make any other operation that is not EXEC, we should have a permission error. This translates to: - set ruleset_attr->handled_access_fs for all available LANDLOCK_ACCESS_FS_* rules - set path_beneath_attr->allowed_access to LANDLOCK_ACCESS_FS_EXEC | LANDLOCK_ACCESS_FS_READ (we need to read in order to execute) for a binary - enforce the rules inside a sandbox containing the binary - execute the binary will work - do any other operation inside the sandbox and obtain a permissions error - at this point, any new rule that is added, will update the list of landlock rules, enabling the sandbox permissions For some reasons that I don't know (and this is evident from kselftests as well), if the initial rules set (ruleset_attr->handled_access_fs) is not identical to the rules we are going to enable (path_beneath_attr->allowed_access), landlock_add_rule() will fail with EINVAL. And this is our case for all kernels <=6.6. I really have no idea why this happens and maybe we need to contact the landlock developers. Andrea On 7/24/24 15:47, Andrea Cervesato wrote: > Hi Li, > > thanks for checking. Mmmh I don't know if it's because they added > LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would > break all the other features. > > Andrea > > On 7/24/24 14:12, Li Wang wrote: >> Hi Petr, Andrea, >> >> On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote: >> >> Hi Andrea, >> >> ... >> > +static void enable_exec_libs(const int ruleset_fd) >> > +{ >> > + FILE *fp; >> > + char line[1024]; >> > + char path[PATH_MAX]; >> > + char dependency[8][PATH_MAX]; >> > + int count = 0; >> > + int duplicate = 0; >> > + >> > + fp = SAFE_FOPEN("/proc/self/maps", "r"); >> > + >> > + while (fgets(line, sizeof(line), fp)) { >> > + if (strstr(line, ".so") == NULL) >> > + continue; >> > + >> > + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", >> path); >> > + >> > + for (int i = 0; i < count; i++) { >> > + if (strcmp(path, dependency[i]) == 0) { >> > + duplicate = 1; >> > + break; >> > + } >> > + } >> > + >> > + if (duplicate) { >> > + duplicate = 0; >> > + continue; >> > + } >> > + >> > + strncpy(dependency[count], path, PATH_MAX); >> > + count++; >> > + >> > + tst_res(TINFO, "Enable read/exec permissions for >> %s", path); >> > + >> > + path_beneath_attr->allowed_access = >> > + LANDLOCK_ACCESS_FS_READ_FILE | >> > + LANDLOCK_ACCESS_FS_EXECUTE; >> > + path_beneath_attr->parent_fd = SAFE_OPEN(path, >> O_PATH | O_CLOEXEC); >> > + >> > + SAFE_LANDLOCK_ADD_RULE( >> > + ruleset_fd, >> > + LANDLOCK_RULE_PATH_BENEATH, >> > + path_beneath_attr, >> > + 0); >> >> Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it >> fails (after >> fresh boot) with: >> >> ... >> tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs >> tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs >> tst_test.c:1746: TINFO: === Testing on ext2 === >> tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' >> extra opts='' >> mke2fs 1.47.0 (5-Feb-2023) >> tst_test.c:1123: TINFO: Mounting /dev/loop1 to >> /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0 >> landlock_common.h:30: TINFO: Landlock ABI v3 >> landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE >> landlock04.c:123: TINFO: Enable read/exec permissions for >> /usr/lib/i386-linux-gnu/libc.so.6 >> landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): >> EINVAL (22) >> >> >> Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was >> refactored from the v6.7 mainline kernel, so it can't add the rule >> correctly >> with older kernels. >> >> commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 >> Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> >> Date: Thu Oct 26 09:47:46 2023 +0800 >> >> landlock: Refactor landlock_add_rule() syscall >> >> But this is my guess (through reading the code), I didn't do more to >> verify that by installing such a kernel. >> >> >> -- >> Regards, >> Li Wang > >
Hi Andrea, On Thu, Jul 25, 2024 at 09:50:36AM +0200, Andrea Cervesato wrote: > Hi all, > > we are facing an issue with landlock support in kernels <=6.6. We have a > test that takes in consideration all possible rules set and enable only one > of them, checking that all the others are raising a permission error. > The test can be found here: > https://github.com/acerv/ltp/commit/9b1d6838592cebe3c89282a7339db543be2a00e7 There is definitely an issue in this commit. ALL_RULES contains all access right, mixing filesystem and network ones, which doesn't make sense because they don't have the same "namespace". LANDLOCK_ACCESS_NET_* and LANDLOCK_ACCESS_FS_* bits overlaps. The tester_get_all_rules() function does the ABI checks and removes the LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP bits if they are not supported (in the handled_access_net namespace), which is the same as removing LANDLOCK_ACCESS_FS_{EXECUTE,WRITE_FILE} whereas they are supported (in the handled_access_fs namespace). > It work fine for all kernels >= 6.7. LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP were introduced with this kernel (Landlock ABI 4), so I guess that's the issue. > > Below you will find the discussion in the LTP mailing list. Can you please > give any help with this? > > Regards, > Andrea > > > > -------- Forwarded Message -------- > Subject: Re: [LTP] [PATCH v3 09/11] Add landlock04 test > Date: Thu, 25 Jul 2024 09:12:39 +0200 > From: Andrea Cervesato <andrea.cervesato@suse.com> > To: Li Wang <liwang@redhat.com>, Petr Vorel <pvorel@suse.cz> > CC: Andrea Cervesato <andrea.cervesato@suse.de>, ltp@lists.linux.it, > Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > > > > Hi! > > it seems like the landlock() support in kernel 6.6 is different than the one > in 6.7. The reason why we see that error in kernel <=6.6 is related to how > landlock is handling the rules set according to the rule we want to enable. > > Let's suppose we want to enable the execution for a file. What we should be > able to do, is to consider __ALL__ the rules available for landlock, then to > enable EXEC only for a specific file. Then, if we make any other operation > that is not EXEC, we should have a permission error. This translates to: > > - set ruleset_attr->handled_access_fs for all available LANDLOCK_ACCESS_FS_* > rules > - set path_beneath_attr->allowed_access to LANDLOCK_ACCESS_FS_EXEC | > LANDLOCK_ACCESS_FS_READ (we need to read in order to execute) for a binary > - enforce the rules inside a sandbox containing the binary > - execute the binary will work > - do any other operation inside the sandbox and obtain a permissions error > - at this point, any new rule that is added, will update the list of > landlock rules, enabling the sandbox permissions > > For some reasons that I don't know (and this is evident from kselftests as > well), if the initial rules set (ruleset_attr->handled_access_fs) is not > identical to the rules we are going to enable > (path_beneath_attr->allowed_access), landlock_add_rule() will fail with > EINVAL. And this is our case for all kernels <=6.6. path_beneath_attr->allowed_access needs to be a subset of ruleset_attr->handled_access_fs, otherwise it doesn't make sense to allow something which is not handled/denied. > > I really have no idea why this happens and maybe we need to contact the > landlock developers. Feel free to Cc me and Günther for all Landlock-related patches. > > Andrea > > On 7/24/24 15:47, Andrea Cervesato wrote: > > Hi Li, > > > > thanks for checking. Mmmh I don't know if it's because they added > > LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would break > > all the other features. > > > > Andrea > > > > On 7/24/24 14:12, Li Wang wrote: > > > Hi Petr, Andrea, > > > > > > On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote: > > > > > > Hi Andrea, > > > > > > ... > > > > +static void enable_exec_libs(const int ruleset_fd) > > > > +{ > > > > + FILE *fp; > > > > + char line[1024]; > > > > + char path[PATH_MAX]; > > > > + char dependency[8][PATH_MAX]; > > > > + int count = 0; > > > > + int duplicate = 0; > > > > + > > > > + fp = SAFE_FOPEN("/proc/self/maps", "r"); > > > > + > > > > + while (fgets(line, sizeof(line), fp)) { > > > > + if (strstr(line, ".so") == NULL) > > > > + continue; > > > > + > > > > + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", > > > path); > > > > + > > > > + for (int i = 0; i < count; i++) { > > > > + if (strcmp(path, dependency[i]) == 0) { > > > > + duplicate = 1; > > > > + break; > > > > + } > > > > + } > > > > + > > > > + if (duplicate) { > > > > + duplicate = 0; > > > > + continue; > > > > + } > > > > + > > > > + strncpy(dependency[count], path, PATH_MAX); > > > > + count++; > > > > + > > > > + tst_res(TINFO, "Enable read/exec permissions for > > > %s", path); > > > > + > > > > + path_beneath_attr->allowed_access = > > > > + LANDLOCK_ACCESS_FS_READ_FILE | > > > > + LANDLOCK_ACCESS_FS_EXECUTE; > > > > + path_beneath_attr->parent_fd = SAFE_OPEN(path, > > > O_PATH | O_CLOEXEC); > > > > + > > > > + SAFE_LANDLOCK_ADD_RULE( > > > > + ruleset_fd, > > > > + LANDLOCK_RULE_PATH_BENEATH, > > > > + path_beneath_attr, > > > > + 0); > > > > > > Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it > > > fails (after > > > fresh boot) with: > > > > > > ... > > > tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs > > > tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs > > > tst_test.c:1746: TINFO: === Testing on ext2 === > > > tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' > > > extra opts='' > > > mke2fs 1.47.0 (5-Feb-2023) > > > tst_test.c:1123: TINFO: Mounting /dev/loop1 to > > > /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0 > > > landlock_common.h:30: TINFO: Landlock ABI v3 > > > landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE > > > landlock04.c:123: TINFO: Enable read/exec permissions for > > > /usr/lib/i386-linux-gnu/libc.so.6 > > > landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): > > > EINVAL (22) > > > > > > > > > Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was > > > refactored from the v6.7 mainline kernel, so it can't add the rule > > > correctly > > > with older kernels. > > > > > > commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 > > > Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > > > Date: Thu Oct 26 09:47:46 2023 +0800 > > > > > > landlock: Refactor landlock_add_rule() syscall > > > > > > But this is my guess (through reading the code), I didn't do more to > > > verify that by installing such a kernel. > > > > > > > > > -- > > > Regards, > > > Li Wang > > > > > >
On 7/25/24 11:06, Mickaël Salaün wrote: > Hi Andrea, > > On Thu, Jul 25, 2024 at 09:50:36AM +0200, Andrea Cervesato wrote: >> Hi all, >> >> we are facing an issue with landlock support in kernels <=6.6. We have a >> test that takes in consideration all possible rules set and enable only one >> of them, checking that all the others are raising a permission error. >> The test can be found here: >> https://github.com/acerv/ltp/commit/9b1d6838592cebe3c89282a7339db543be2a00e7 > There is definitely an issue in this commit. ALL_RULES contains all > access right, mixing filesystem and network ones, which doesn't make > sense because they don't have the same "namespace". > LANDLOCK_ACCESS_NET_* and LANDLOCK_ACCESS_FS_* bits overlaps. > The tester_get_all_rules() function does the ABI checks and removes the > LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP bits if they are not supported > (in the handled_access_net namespace), which is the same as removing > LANDLOCK_ACCESS_FS_{EXECUTE,WRITE_FILE} whereas they are supported (in > the handled_access_fs namespace). Thank you very much. Indeed, there was an error in the test itself that was mixing namespaces. Now problem seems to be solved. >> It work fine for all kernels >= 6.7. > LANDLOCK_ACCESS_NET_{BIND,CONNECT}_TCP were introduced with this kernel > (Landlock ABI 4), so I guess that's the issue. > >> Below you will find the discussion in the LTP mailing list. Can you please >> give any help with this? >> >> Regards, >> Andrea >> >> >> >> -------- Forwarded Message -------- >> Subject: Re: [LTP] [PATCH v3 09/11] Add landlock04 test >> Date: Thu, 25 Jul 2024 09:12:39 +0200 >> From: Andrea Cervesato <andrea.cervesato@suse.com> >> To: Li Wang <liwang@redhat.com>, Petr Vorel <pvorel@suse.cz> >> CC: Andrea Cervesato <andrea.cervesato@suse.de>, ltp@lists.linux.it, >> Konstantin Meskhidze <konstantin.meskhidze@huawei.com> >> >> >> >> Hi! >> >> it seems like the landlock() support in kernel 6.6 is different than the one >> in 6.7. The reason why we see that error in kernel <=6.6 is related to how >> landlock is handling the rules set according to the rule we want to enable. >> >> Let's suppose we want to enable the execution for a file. What we should be >> able to do, is to consider __ALL__ the rules available for landlock, then to >> enable EXEC only for a specific file. Then, if we make any other operation >> that is not EXEC, we should have a permission error. This translates to: >> >> - set ruleset_attr->handled_access_fs for all available LANDLOCK_ACCESS_FS_* >> rules >> - set path_beneath_attr->allowed_access to LANDLOCK_ACCESS_FS_EXEC | >> LANDLOCK_ACCESS_FS_READ (we need to read in order to execute) for a binary >> - enforce the rules inside a sandbox containing the binary >> - execute the binary will work >> - do any other operation inside the sandbox and obtain a permissions error >> - at this point, any new rule that is added, will update the list of >> landlock rules, enabling the sandbox permissions >> >> For some reasons that I don't know (and this is evident from kselftests as >> well), if the initial rules set (ruleset_attr->handled_access_fs) is not >> identical to the rules we are going to enable >> (path_beneath_attr->allowed_access), landlock_add_rule() will fail with >> EINVAL. And this is our case for all kernels <=6.6. > path_beneath_attr->allowed_access needs to be a subset of > ruleset_attr->handled_access_fs, otherwise it doesn't make sense to > allow something which is not handled/denied. > >> I really have no idea why this happens and maybe we need to contact the >> landlock developers. > Feel free to Cc me and Günther for all Landlock-related patches. Sure. >> Andrea >> >> On 7/24/24 15:47, Andrea Cervesato wrote: >>> Hi Li, >>> >>> thanks for checking. Mmmh I don't know if it's because they added >>> LANDLOCK_RULE_NET_PORT. It sounds strange to me, since that would break >>> all the other features. >>> >>> Andrea >>> >>> On 7/24/24 14:12, Li Wang wrote: >>>> Hi Petr, Andrea, >>>> >>>> On Wed, Jul 17, 2024 at 1:27 AM Petr Vorel <pvorel@suse.cz> wrote: >>>> >>>> Hi Andrea, >>>> >>>> ... >>>> > +static void enable_exec_libs(const int ruleset_fd) >>>> > +{ >>>> > + FILE *fp; >>>> > + char line[1024]; >>>> > + char path[PATH_MAX]; >>>> > + char dependency[8][PATH_MAX]; >>>> > + int count = 0; >>>> > + int duplicate = 0; >>>> > + >>>> > + fp = SAFE_FOPEN("/proc/self/maps", "r"); >>>> > + >>>> > + while (fgets(line, sizeof(line), fp)) { >>>> > + if (strstr(line, ".so") == NULL) >>>> > + continue; >>>> > + >>>> > + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", >>>> path); >>>> > + >>>> > + for (int i = 0; i < count; i++) { >>>> > + if (strcmp(path, dependency[i]) == 0) { >>>> > + duplicate = 1; >>>> > + break; >>>> > + } >>>> > + } >>>> > + >>>> > + if (duplicate) { >>>> > + duplicate = 0; >>>> > + continue; >>>> > + } >>>> > + >>>> > + strncpy(dependency[count], path, PATH_MAX); >>>> > + count++; >>>> > + >>>> > + tst_res(TINFO, "Enable read/exec permissions for >>>> %s", path); >>>> > + >>>> > + path_beneath_attr->allowed_access = >>>> > + LANDLOCK_ACCESS_FS_READ_FILE | >>>> > + LANDLOCK_ACCESS_FS_EXECUTE; >>>> > + path_beneath_attr->parent_fd = SAFE_OPEN(path, >>>> O_PATH | O_CLOEXEC); >>>> > + >>>> > + SAFE_LANDLOCK_ADD_RULE( >>>> > + ruleset_fd, >>>> > + LANDLOCK_RULE_PATH_BENEATH, >>>> > + path_beneath_attr, >>>> > + 0); >>>> >>>> Unfortunately, on 6.6.15-amd64 kernel (random Debian machine) it >>>> fails (after >>>> fresh boot) with: >>>> >>>> ... >>>> tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs >>>> tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs >>>> tst_test.c:1746: TINFO: === Testing on ext2 === >>>> tst_test.c:1111: TINFO: Formatting /dev/loop1 with ext2 opts='' >>>> extra opts='' >>>> mke2fs 1.47.0 (5-Feb-2023) >>>> tst_test.c:1123: TINFO: Mounting /dev/loop1 to >>>> /tmp/LTP_lant6WbKJ/sandbox fstyp=ext2 flags=0 >>>> landlock_common.h:30: TINFO: Landlock ABI v3 >>>> landlock04.c:151: TINFO: Testing LANDLOCK_ACCESS_FS_EXECUTE >>>> landlock04.c:123: TINFO: Enable read/exec permissions for >>>> /usr/lib/i386-linux-gnu/libc.so.6 >>>> landlock04.c:131: TBROK: landlock_add_rule(3, 1, 0xf7f13ff4, 0): >>>> EINVAL (22) >>>> >>>> >>>> Possibly that's because the 'LANDLOCK_RULE_PATH_BENEATH' was >>>> refactored from the v6.7 mainline kernel, so it can't add the rule >>>> correctly >>>> with older kernels. >>>> >>>> commit 0e0fc7e8eb4a11bd9f89a9c74bc7c0e144c56203 >>>> Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> >>>> Date: Thu Oct 26 09:47:46 2023 +0800 >>>> >>>> landlock: Refactor landlock_add_rule() syscall >>>> >>>> But this is my guess (through reading the code), I didn't do more to >>>> verify that by installing such a kernel. >>>> >>>> >>>> -- >>>> Regards, >>>> Li Wang >>> >> Regards, Andrea
diff --git a/runtest/syscalls b/runtest/syscalls index f2b64c0df..3c7cd66e2 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -688,6 +688,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/landlock04.c b/testcases/kernel/syscalls/landlock/landlock04.c new file mode 100644 index 000000000..30fd9400f --- /dev/null +++ b/testcases/kernel/syscalls/landlock/landlock04.c @@ -0,0 +1,214 @@ +// 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" +#include "tst_safe_stdio.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 enable_exec_libs(const int ruleset_fd) +{ + FILE *fp; + char line[1024]; + char path[PATH_MAX]; + char dependency[8][PATH_MAX]; + int count = 0; + int duplicate = 0; + + fp = SAFE_FOPEN("/proc/self/maps", "r"); + + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, ".so") == NULL) + continue; + + SAFE_SSCANF(line, "%*x-%*x %*s %*x %*s %*d %s", path); + + for (int i = 0; i < count; i++) { + if (strcmp(path, dependency[i]) == 0) { + duplicate = 1; + break; + } + } + + if (duplicate) { + duplicate = 0; + continue; + } + + strncpy(dependency[count], path, PATH_MAX); + count++; + + tst_res(TINFO, "Enable read/exec permissions for %s", path); + + path_beneath_attr->allowed_access = + LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_EXECUTE; + path_beneath_attr->parent_fd = SAFE_OPEN(path, O_PATH | O_CLOEXEC); + + SAFE_LANDLOCK_ADD_RULE( + ruleset_fd, + LANDLOCK_RULE_PATH_BENEATH, + path_beneath_attr, + 0); + + SAFE_CLOSE(path_beneath_attr->parent_fd); + } + + SAFE_FCLOSE(fp); +} + +static void setup(void) +{ + struct tvariant variant = tvariants[tst_variant]; + int ruleset_fd; + + verify_landlock_is_enabled(); + tester_create_tree(); + + tst_res(TINFO, "Testing %s", variant.desc); + + ruleset_attr->handled_access_fs = tester_get_all_rules(); + + ruleset_fd = SAFE_LANDLOCK_CREATE_RULESET( + ruleset_attr, sizeof(struct landlock_ruleset_attr), 0); + + /* since our binary is dynamically linked, we need to enable dependences + * to be read and executed + */ + enable_exec_libs(ruleset_fd); + + path_beneath_attr->allowed_access = variant.access; + path_beneath_attr->parent_fd = SAFE_OPEN( + SANDBOX_FOLDER, O_PATH | O_CLOEXEC); + + SAFE_LANDLOCK_ADD_RULE( + ruleset_fd, + LANDLOCK_RULE_PATH_BENEATH, + path_beneath_attr, + 0); + + SAFE_CLOSE(path_beneath_attr->parent_fd); + + enforce_ruleset(ruleset_fd); + SAFE_CLOSE(ruleset_fd); +} + +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", + "exfat", + 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