2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #if defined(__FreeBSD__)
21 #include <sys/capsicum.h>
27 if (cap_enter() == -1)
31 #elif defined(__linux__)
33 #include <sys/prctl.h>
34 #include <sys/syscall.h>
35 #include <sys/syscall.h>
36 #include <sys/types.h>
38 #include <linux/audit.h>
39 #include <linux/filter.h>
40 #include <linux/seccomp.h>
48 /* thanks chromium' src/seccomp.c */
50 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386
51 #elif defined(__x86_64__)
52 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
53 #elif defined(__arm__)
54 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM
55 #elif defined(__aarch64__)
56 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64
57 #elif defined(__mips__)
58 # if defined(__mips64)
59 # if defined(__MIPSEB__)
60 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64
62 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64
65 # if defined(__MIPSEB__)
66 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS
68 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL
72 # error "Platform does not support seccomp filter yet"
75 /* uncomment to enable debugging. ONLY FOR DEVELOPMENT */
76 /* #define SC_DEBUG */
79 # define SC_FAIL SECCOMP_RET_TRAP
81 # define SC_FAIL SECCOMP_RET_KILL
84 /* make the filter more readable */
85 #define SC_ALLOW(nr) \
86 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##nr, 0, 1), \
87 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
95 sandbox_seccomp_violation(int signum, siginfo_t *info, void *ctx)
100 fprintf(stderr, "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)\n",
101 __func__, info->si_arch, info->si_syscall, info->si_call_addr);
106 sandbox_seccomp_catch_sigsys(void)
108 struct sigaction act;
111 memset(&act, 0, sizeof(act));
113 sigaddset(&mask, SIGSYS);
115 act.sa_sigaction = &sandbox_seccomp_violation;
116 act.sa_flags = SA_SIGINFO;
117 if (sigaction(SIGSYS, &act, NULL) == -1) {
118 fprintf(stderr, "%s: sigaction(SIGSYS): %s\n",
119 __func__, strerror(errno));
122 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
123 fprintf(stderr, "%s: sigprocmask(SIGSYS): %s\n",
124 __func__, strerror(errno));
128 #endif /* SC_DEBUG */
133 struct sock_filter filter[] = {
134 /* load the *current* architecture */
135 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
136 (offsetof(struct seccomp_data, arch))),
137 /* ensure it's the same that we've been compiled on */
138 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
139 SECCOMP_AUDIT_ARCH, 1, 0),
140 /* if not, kill the program */
141 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
143 /* load the syscall number */
144 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
145 (offsetof(struct seccomp_data, nr))),
147 /* allow logging on stdout */
152 /* these are used to serve the files. note how we
153 * allow openat but not open. */
156 /* it seems that on aarch64 there isn't a poll(2)
157 * syscall, but instead it's implemented on top of
173 /* we need recvmsg to receive fd */
179 /* alpine on amd64 does a clock_gettime(2) */
180 SC_ALLOW(clock_gettime),
182 /* for directory listing */
183 SC_ALLOW(getdents64),
186 SC_ALLOW(exit_group),
188 /* allow only F_GETFL and F_SETFL fcntl */
189 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 8),
190 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
191 (offsetof(struct seccomp_data, args[1]))),
192 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_GETFL, 0, 1),
193 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
194 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFL, 0, 1),
195 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
196 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFD, 0, 1),
197 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
198 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
200 /* re-load the syscall number */
201 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
202 (offsetof(struct seccomp_data, nr))),
204 /* allow ioctl but only on fd 1, glibc doing stuff? */
205 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_ioctl, 0, 3),
206 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
207 (offsetof(struct seccomp_data, args[0]))),
208 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 1),
209 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
211 /* disallow enything else */
212 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
215 struct sock_fprog prog = {
216 .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])),
221 sandbox_seccomp_catch_sigsys();
224 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
225 fprintf(stderr, "%s: prctl(PR_SET_NO_NEW_PRIVS): %s\n",
226 __func__, strerror(errno));
230 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
231 fprintf(stderr, "%s: prctl(PR_SET_SECCOMP): %s\n",
232 __func__, strerror(errno));
237 #elif defined(__OpenBSD__)
247 for (h = hosts; h->domain != NULL; ++h) {
248 if (unveil(h->dir, "rx") == -1)
249 err(1, "unveil %s for domain %s", h->dir, h->domain);
252 if (pledge("stdio recvfd rpath inet", NULL) == -1)
261 LOGN(NULL, "%s", "no sandbox method known for this OS");