1 /*
2 * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <stddef.h>
20 #include <stdbool.h>
21 #include <getopt.h>
22
23 #include <linux/unistd.h>
24 #include <linux/filter.h>
25 #include <linux/seccomp.h>
26 #include <linux/audit.h>
27 #include <sys/prctl.h>
28
29 #include "c.h"
30 #include "exitcodes.h"
31
32 #if __x86_64__
33 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_X86_64
34 #elif __i386__
35 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_I386
36 #elif __arm__
37 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ARM
38 #elif __aarch64__
39 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_AARCH64
40 #elif __riscv
41 # if __riscv_xlen == 32
42 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_RISCV32
43 # elif __riscv_xlen == 64
44 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_RISCV64
45 # endif
46 #elif __s390__
47 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390
48 #elif __s390x__
49 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390X
50 #elif __PPC64__
51 # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
52 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PPC64
53 # else
54 # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PPC64LE
55 # endif
56 #else
57 # error Unknown target architecture
58 #endif
59
60 #define syscall_nr (offsetof(struct seccomp_data, nr))
61
62 struct syscall {
63 const char *const name;
64 int number;
65 };
66
67 const struct syscall syscalls[] = {
68 { "move_mount", __NR_move_mount },
69 { "open_tree", __NR_open_tree },
70 { "fsopen", __NR_fsopen },
71 };
72
73 int main(int argc, char **argv)
74 {
75 int c;
76 size_t i;
77 bool found;
78 static const struct option longopts[] = {
79 { "syscall", required_argument, NULL, 's' },
80 { 0 }
81 };
82
83 bool blocked_syscalls[ARRAY_SIZE(syscalls)] = {};
84
85 while ((c = getopt_long (argc, argv, "s:", longopts, NULL)) != -1) {
86 switch (c) {
87 case 's':
88 found = 0;
89 for (i = 0; i < ARRAY_SIZE(syscalls); i++) {
90 if (strcmp(optarg, syscalls[i].name) == 0) {
91 blocked_syscalls[i] = true;
92 found = 1;
93 break;
94 }
95 }
96 if (!found)
97 errx(EXIT_FAILURE, "Unknown syscall '%s'", optarg);
98 break;
99 default:
100 errx(EXIT_FAILURE, "Unknown option");
101 }
102 }
103
104 if (optind >= argc)
105 errx(EXIT_FAILURE, "No executable specified");
106
107 #define N_FILTERS (ARRAY_SIZE(syscalls) + 3)
108
109 struct sock_filter filter[N_FILTERS] = {
110 [0] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr),
111
112 [N_FILTERS - 2] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
113 [N_FILTERS - 1] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS),
114 };
115
116 const struct sock_filter nop = BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0);
117
118 for (i = 0; i < ARRAY_SIZE(syscalls); i++) {
119 if (blocked_syscalls[i]) {
120 const struct sock_filter block = BPF_JUMP(
121 BPF_JMP | BPF_JEQ | BPF_K,
122 syscalls[i].number,
123 N_FILTERS - 3 - i, 0);
124 filter[i + 1] = block;
125 } else {
126 filter[i + 1] = nop;
127 }
128 }
129
130 struct sock_fprog prog = {
131 .len = ARRAY_SIZE(filter),
132 .filter = filter,
133 };
134
135 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
136 err(EXIT_NOTSUPP, "prctl(PR_SET_NO_NEW_PRIVS)");
137
138 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
139 err(EXIT_NOTSUPP, "prctl(PR_SET_SECCOMP)");
140
141 if (execvp(argv[optind], argv + optind))
142 err(EXIT_NOTSUPP, "Could not exec");
143 }