Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 *
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.
7 *
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.
15 */
17 #include "gmid.h"
19 #if defined(__FreeBSD__)
21 #include <sys/capsicum.h>
23 void
24 sandbox()
25 {
26 if (cap_enter() == -1)
27 fatal("cap_enter");
28 }
30 #elif defined(__linux__)
32 #include <sys/prctl.h>
33 #include <sys/syscall.h>
34 #include <sys/syscall.h>
35 #include <sys/types.h>
37 #include <linux/audit.h>
38 #include <linux/filter.h>
39 #include <linux/seccomp.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stddef.h>
44 #include <stdio.h>
45 #include <string.h>
47 /* thanks chromium' src/seccomp.c */
48 #if defined(__i386__)
49 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386
50 #elif defined(__x86_64__)
51 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
52 #elif defined(__arm__)
53 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM
54 #elif defined(__aarch64__)
55 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64
56 #elif defined(__mips__)
57 # if defined(__mips64)
58 # if defined(__MIPSEB__)
59 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64
60 # else
61 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64
62 # endif
63 # else
64 # if defined(__MIPSEB__)
65 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS
66 # else
67 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL
68 # endif
69 # endif
70 #else
71 # error "Platform does not support seccomp filter yet"
72 #endif
74 /* uncomment to enable debugging. ONLY FOR DEVELOPMENT */
75 /* #define SC_DEBUG */
77 #ifdef SC_DEBUG
78 # define SC_FAIL SECCOMP_RET_TRAP
79 #else
80 # define SC_FAIL SECCOMP_RET_KILL
81 #endif
83 /* make the filter more readable */
84 #define SC_ALLOW(nr) \
85 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##nr, 0, 1), \
86 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
88 #ifdef SC_DEBUG
90 #include <signal.h>
91 #include <unistd.h>
93 static void
94 sandbox_seccomp_violation(int signum, siginfo_t *info, void *ctx)
95 {
96 (void)signum;
97 (void)ctx;
99 fprintf(stderr, "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)\n",
100 __func__, info->si_arch, info->si_syscall, info->si_call_addr);
101 _exit(1);
104 static void
105 sandbox_seccomp_catch_sigsys(void)
107 struct sigaction act;
108 sigset_t mask;
110 memset(&act, 0, sizeof(act));
111 sigemptyset(&mask);
112 sigaddset(&mask, SIGSYS);
114 act.sa_sigaction = &sandbox_seccomp_violation;
115 act.sa_flags = SA_SIGINFO;
116 if (sigaction(SIGSYS, &act, NULL) == -1)
117 fatal("%s: sigaction(SIGSYS): %s",
118 __func__, strerror(errno));
120 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
121 fatal("%s: sigprocmask(SIGSYS): %s\n",
122 __func__, strerror(errno));
124 #endif /* SC_DEBUG */
126 void
127 sandbox()
129 struct sock_filter filter[] = {
130 /* load the *current* architecture */
131 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
132 (offsetof(struct seccomp_data, arch))),
133 /* ensure it's the same that we've been compiled on */
134 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
135 SECCOMP_AUDIT_ARCH, 1, 0),
136 /* if not, kill the program */
137 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
139 /* load the syscall number */
140 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
141 (offsetof(struct seccomp_data, nr))),
143 /* allow logging on stdout */
144 SC_ALLOW(write),
145 SC_ALLOW(writev),
146 SC_ALLOW(readv),
148 /* these are used to serve the files. note how we
149 * allow openat but not open. */
150 #ifdef __NR_epoll_wait
151 /* epoll_wait(2) isn't present on aarch64, at least */
152 SC_ALLOW(epoll_wait),
153 #endif
154 SC_ALLOW(epoll_pwait),
155 SC_ALLOW(epoll_ctl),
156 SC_ALLOW(accept),
157 SC_ALLOW(accept4),
158 SC_ALLOW(read),
159 SC_ALLOW(openat),
160 SC_ALLOW(fstat),
161 SC_ALLOW(newfstatat),
162 SC_ALLOW(close),
163 SC_ALLOW(lseek),
164 SC_ALLOW(brk),
165 SC_ALLOW(mmap),
166 SC_ALLOW(munmap),
168 /* needed for signal handling */
169 SC_ALLOW(rt_sigreturn),
170 SC_ALLOW(rt_sigaction),
172 /* we need recvmsg to receive fd */
173 SC_ALLOW(recvmsg),
175 /* XXX: ??? */
176 SC_ALLOW(getpid),
178 /* alpine on amd64 */
179 SC_ALLOW(clock_gettime),
180 SC_ALLOW(madvise),
182 /* void on aarch64 does a gettrandom */
183 SC_ALLOW(getrandom),
185 /* arch on amd64 */
186 SC_ALLOW(gettimeofday),
188 /* for directory listing */
189 SC_ALLOW(getdents64),
191 SC_ALLOW(exit),
192 SC_ALLOW(exit_group),
194 /* stuff used by syslog. revisit once we move
195 * logging in its own process */
196 SC_ALLOW(socket),
197 SC_ALLOW(sendto),
198 SC_ALLOW(connect),
200 /* allow only F_GETFL, F_SETFL & F_SETFD fcntl */
201 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 8),
202 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
203 (offsetof(struct seccomp_data, args[1]))),
204 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_GETFL, 0, 1),
205 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
206 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFL, 0, 1),
207 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
208 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFD, 0, 1),
209 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
210 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
212 /* re-load the syscall number */
213 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
214 (offsetof(struct seccomp_data, nr))),
216 /* allow ioctl but only on fd 1, glibc doing stuff? */
217 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_ioctl, 0, 3),
218 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
219 (offsetof(struct seccomp_data, args[0]))),
220 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 1),
221 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
223 /* disallow enything else */
224 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
225 };
227 struct sock_fprog prog = {
228 .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])),
229 .filter = filter,
230 };
232 #ifdef SC_DEBUG
233 sandbox_seccomp_catch_sigsys();
234 #endif
236 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
237 fatal("%s: prctl(PR_SET_NO_NEW_PRIVS): %s",
238 __func__, strerror(errno));
240 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1)
241 fatal("%s: prctl(PR_SET_SECCOMP): %s\n",
242 __func__, strerror(errno));
245 #elif defined(__OpenBSD__)
247 #include <unistd.h>
249 void
250 sandbox()
252 struct vhost *h;
254 for (h = hosts; h->domain != NULL; ++h) {
255 if (unveil(h->dir, "r") == -1)
256 fatal("unveil %s for domain %s", h->dir, h->domain);
259 if (pledge("stdio recvfd rpath inet", NULL) == -1)
260 fatal("pledge");
263 #else
265 void
266 sandbox()
268 log_notice(NULL, "no sandbox method known for this OS");
271 #endif