diff options
Diffstat (limited to 'tests/helpers/test_enosys.c')
-rw-r--r-- | tests/helpers/test_enosys.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/tests/helpers/test_enosys.c b/tests/helpers/test_enosys.c new file mode 100644 index 0000000..9e93cc2 --- /dev/null +++ b/tests/helpers/test_enosys.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stddef.h> +#include <stdbool.h> +#include <getopt.h> + +#include <linux/unistd.h> +#include <linux/filter.h> +#include <linux/seccomp.h> +#include <linux/audit.h> +#include <sys/prctl.h> + +#include "c.h" +#include "audit-arch.h" +#include "exitcodes.h" + +#define syscall_nr (offsetof(struct seccomp_data, nr)) + +struct syscall { + const char *const name; + int number; +}; + +const struct syscall syscalls[] = { +#ifdef __NR_move_mount + { "move_mount", __NR_move_mount }, +#endif +#ifdef __NR_open_tree + { "open_tree", __NR_open_tree }, +#endif +#ifdef __NR_fsopen + { "fsopen", __NR_fsopen }, +#endif +#ifdef __NR_mount_setattr + { "mount_setattr", __NR_mount_setattr }, +#endif + +}; + +int main(int argc, char **argv) +{ + int c; + size_t i; + bool found; + static const struct option longopts[] = { + { "syscall", required_argument, NULL, 's' }, + { 0 } + }; + + bool blocked_syscalls[ARRAY_SIZE(syscalls)] = {}; + + while ((c = getopt_long (argc, argv, "s:", longopts, NULL)) != -1) { + switch (c) { + case 's': + found = 0; + for (i = 0; i < ARRAY_SIZE(syscalls); i++) { + if (strcmp(optarg, syscalls[i].name) == 0) { + blocked_syscalls[i] = true; + found = 1; + break; + } + } + if (!found) + errx(EXIT_FAILURE, "Unknown syscall '%s'", optarg); + break; + default: + errx(EXIT_FAILURE, "Unknown option"); + } + } + + if (optind >= argc) + errx(EXIT_FAILURE, "No executable specified"); + +#define N_FILTERS (ARRAY_SIZE(syscalls) + 3) + + struct sock_filter filter[N_FILTERS] = { + [0] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr), + + [N_FILTERS - 2] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + [N_FILTERS - 1] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS), + }; + + const struct sock_filter nop = BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0); + + for (i = 0; i < ARRAY_SIZE(syscalls); i++) { + if (blocked_syscalls[i]) { + const struct sock_filter block = BPF_JUMP( + BPF_JMP | BPF_JEQ | BPF_K, + syscalls[i].number, + N_FILTERS - 3 - i, 0); + filter[i + 1] = block; + } else { + filter[i + 1] = nop; + } + } + + struct sock_fprog prog = { + .len = ARRAY_SIZE(filter), + .filter = filter, + }; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + err(EXIT_NOTSUPP, "prctl(PR_SET_NO_NEW_PRIVS)"); + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) + err(EXIT_NOTSUPP, "prctl(PR_SET_SECCOMP)"); + + if (execvp(argv[optind], argv + optind)) + err(EXIT_NOTSUPP, "Could not exec"); +} |