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_server_process(void)
25 {
26 if (cap_enter() == -1)
27 fatal("cap_enter");
28 }
30 void
31 sandbox_executor_process(void)
32 {
33 /* We cannot capsicum the executor process because it needs
34 * to fork(2)+execve(2) cgi scripts */
35 return;
36 }
38 void
39 sandbox_logger_process(void)
40 {
41 if (cap_enter() == -1)
42 fatal("cap_enter");
43 }
45 #elif defined(__linux__)
47 #include <sys/prctl.h>
48 #include <sys/syscall.h>
49 #include <sys/syscall.h>
50 #include <sys/types.h>
52 #include <linux/audit.h>
53 #include <linux/filter.h>
54 #include <linux/seccomp.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <stddef.h>
59 #include <stdio.h>
60 #include <string.h>
62 /* thanks chromium' src/seccomp.c */
63 #if defined(__i386__)
64 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386
65 #elif defined(__x86_64__)
66 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
67 #elif defined(__arm__)
68 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM
69 #elif defined(__aarch64__)
70 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64
71 #elif defined(__mips__)
72 # if defined(__mips64)
73 # if defined(__MIPSEB__)
74 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64
75 # else
76 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64
77 # endif
78 # else
79 # if defined(__MIPSEB__)
80 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS
81 # else
82 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL
83 # endif
84 # endif
85 #else
86 # error "Platform does not support seccomp filter yet"
87 #endif
89 /* uncomment to enable debugging. ONLY FOR DEVELOPMENT */
90 /* #define SC_DEBUG */
92 #ifdef SC_DEBUG
93 # define SC_FAIL SECCOMP_RET_TRAP
94 #else
95 # define SC_FAIL SECCOMP_RET_KILL
96 #endif
98 /* make the filter more readable */
99 #define SC_ALLOW(nr) \
100 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##nr, 0, 1), \
101 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
103 #ifdef SC_DEBUG
105 #include <signal.h>
106 #include <unistd.h>
108 static void
109 sandbox_seccomp_violation(int signum, siginfo_t *info, void *ctx)
111 (void)signum;
112 (void)ctx;
114 fprintf(stderr, "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)\n",
115 __func__, info->si_arch, info->si_syscall, info->si_call_addr);
116 _exit(1);
119 static void
120 sandbox_seccomp_catch_sigsys(void)
122 struct sigaction act;
123 sigset_t mask;
125 memset(&act, 0, sizeof(act));
126 sigemptyset(&mask);
127 sigaddset(&mask, SIGSYS);
129 act.sa_sigaction = &sandbox_seccomp_violation;
130 act.sa_flags = SA_SIGINFO;
131 if (sigaction(SIGSYS, &act, NULL) == -1)
132 fatal("%s: sigaction(SIGSYS): %s",
133 __func__, strerror(errno));
135 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
136 fatal("%s: sigprocmask(SIGSYS): %s\n",
137 __func__, strerror(errno));
139 #endif /* SC_DEBUG */
141 void
142 sandbox_server_process(void)
144 struct sock_filter filter[] = {
145 /* load the *current* architecture */
146 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
147 (offsetof(struct seccomp_data, arch))),
148 /* ensure it's the same that we've been compiled on */
149 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
150 SECCOMP_AUDIT_ARCH, 1, 0),
151 /* if not, kill the program */
152 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
154 /* load the syscall number */
155 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
156 (offsetof(struct seccomp_data, nr))),
158 /* allow logging on stdout */
159 SC_ALLOW(write),
160 SC_ALLOW(writev),
161 SC_ALLOW(readv),
163 /* these are used to serve the files. note how we
164 * allow openat but not open. */
165 #ifdef __NR_epoll_wait
166 /* epoll_wait(2) isn't present on aarch64, at least */
167 SC_ALLOW(epoll_wait),
168 #endif
169 SC_ALLOW(epoll_pwait),
170 SC_ALLOW(epoll_ctl),
171 SC_ALLOW(accept),
172 SC_ALLOW(accept4),
173 SC_ALLOW(read),
174 SC_ALLOW(openat),
175 SC_ALLOW(fstat),
176 SC_ALLOW(newfstatat),
177 SC_ALLOW(close),
178 SC_ALLOW(lseek),
179 SC_ALLOW(brk),
180 SC_ALLOW(mmap),
181 SC_ALLOW(munmap),
183 /* for imsg */
184 SC_ALLOW(sendmsg),
185 SC_ALLOW(prlimit64),
187 /* needed for signal handling */
188 SC_ALLOW(rt_sigreturn),
189 SC_ALLOW(rt_sigaction),
191 /* we need recvmsg to receive fd */
192 SC_ALLOW(recvmsg),
194 /* XXX: ??? */
195 SC_ALLOW(getpid),
197 /* alpine on amd64 */
198 SC_ALLOW(clock_gettime),
199 SC_ALLOW(madvise),
201 /* void on aarch64 does a gettrandom */
202 SC_ALLOW(getrandom),
204 /* arch on amd64 */
205 SC_ALLOW(gettimeofday),
207 /* for directory listing */
208 SC_ALLOW(getdents64),
210 SC_ALLOW(exit),
211 SC_ALLOW(exit_group),
213 /* allow only F_GETFL, F_SETFL & F_SETFD fcntl */
214 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fcntl, 0, 8),
215 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
216 (offsetof(struct seccomp_data, args[1]))),
217 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_GETFL, 0, 1),
218 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
219 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFL, 0, 1),
220 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
221 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, F_SETFD, 0, 1),
222 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
223 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
225 /* re-load the syscall number */
226 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
227 (offsetof(struct seccomp_data, nr))),
229 /* allow ioctl but only on fd 1, glibc doing stuff? */
230 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_ioctl, 0, 3),
231 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
232 (offsetof(struct seccomp_data, args[0]))),
233 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 1),
234 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
236 /* disallow enything else */
237 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
238 };
240 struct sock_fprog prog = {
241 .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])),
242 .filter = filter,
243 };
245 #ifdef SC_DEBUG
246 sandbox_seccomp_catch_sigsys();
247 #endif
249 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
250 fatal("%s: prctl(PR_SET_NO_NEW_PRIVS): %s",
251 __func__, strerror(errno));
253 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1)
254 fatal("%s: prctl(PR_SET_SECCOMP): %s\n",
255 __func__, strerror(errno));
258 void
259 sandbox_executor_process(void)
261 /* We cannot use seccomp for the executor process because we
262 * don't know what the child will do. Also, our filter will
263 * be inherited so the child cannot set its own seccomp
264 * policy. */
265 return;
268 void
269 sandbox_logger_process(void)
271 /* To be honest, here we could use a seccomp policy to only
272 * allow writev(2) and memory allocations. */
273 return;
276 #elif defined(__OpenBSD__)
278 #include <unistd.h>
280 void
281 sandbox_server_process(void)
283 struct vhost *h;
285 TAILQ_FOREACH(h, &hosts, vhosts) {
286 if (unveil(h->dir, "r") == -1)
287 fatal("unveil %s for domain %s", h->dir, h->domain);
290 if (pledge("stdio recvfd rpath inet", NULL) == -1)
291 fatal("pledge");
294 void
295 sandbox_executor_process(void)
297 struct vhost *h;
299 TAILQ_FOREACH(h, &hosts, vhosts) {
300 /* r so we can chdir into the correct directory */
301 if (unveil(h->dir, "rx") == -1)
302 err(1, "unveil %s for domain %s",
303 h->dir, h->domain);
306 /* rpath to chdir into the correct directory */
307 if (pledge("stdio rpath sendfd proc exec", NULL))
308 err(1, "pledge");
311 void
312 sandbox_logger_process(void)
314 if (pledge("stdio", NULL) == -1)
315 err(1, "pledge");
318 #else
320 #warning "No sandbox method known for this OS"
322 void
323 sandbox_server_process(void)
325 return;
328 void
329 sandbox_executor_process(void)
331 log_notice(NULL, "no sandbox method known for this OS");
334 void
335 sandbox_logger_process(void)
337 return;
340 #endif