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 /* uncomment to enable debugging. ONLY FOR DEVELOPMENT */
63 /* #define SC_DEBUG */
65 #ifdef SC_DEBUG
66 # define SC_FAIL SECCOMP_RET_TRAP
67 #else
68 # define SC_FAIL SECCOMP_RET_KILL
69 #endif
71 #if (BYTE_ORDER == LITTLE_ENDIAN)
72 # define SC_ARG_LO 0
73 # define SC_ARG_HI sizeof(uint32_t)
74 #elif (BYTE_ORDER == BIG_ENDIAN)
75 # define SC_ARG_LO sizeof(uint32_t)
76 # define SC_ARG_HI 0
77 #else
78 # error "Uknown endian"
79 #endif
81 /* make the filter more readable */
82 #define SC_ALLOW(nr) \
83 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##nr, 0, 1), \
84 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
86 /*
87 * SC_ALLOW_ARG and the SECCOMP_AUDIT_ARCH below are courtesy of
88 * https://roy.marples.name/git/dhcpcd/blob/HEAD:/src/privsep-linux.c
89 */
90 #define SC_ALLOW_ARG(_nr, _arg, _val) \
91 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (_nr), 0, 6), \
92 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, \
93 offsetof(struct seccomp_data, args[(_arg)]) + SC_ARG_LO), \
94 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, \
95 ((_val) & 0xffffffff), 0, 3), \
96 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, \
97 offsetof(struct seccomp_data, args[(_arg)]) + SC_ARG_HI), \
98 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, \
99 (((uint32_t)((uint64_t)(_val) >> 32)) & 0xffffffff), 0, 1), \
100 BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), \
101 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, \
102 offsetof(struct seccomp_data, nr))
104 /*
105 * I personally find this quite nutty. Why can a system header not
106 * define a default for this?
107 */
108 #if defined(__i386__)
109 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386
110 #elif defined(__x86_64__)
111 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64
112 #elif defined(__arc__)
113 # if defined(__A7__)
114 # if (BYTE_ORDER == LITTLE_ENDIAN)
115 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCOMPACT
116 # else
117 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCOMPACTBE
118 # endif
119 # elif defined(__HS__)
120 # if (BYTE_ORDER == LITTLE_ENDIAN)
121 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV2
122 # else
123 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV2BE
124 # endif
125 # else
126 # error "Platform does not support seccomp filter yet"
127 # endif
128 #elif defined(__arm__)
129 # ifndef EM_ARM
130 # define EM_ARM 40
131 # endif
132 # if (BYTE_ORDER == LITTLE_ENDIAN)
133 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM
134 # else
135 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARMEB
136 # endif
137 #elif defined(__aarch64__)
138 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64
139 #elif defined(__alpha__)
140 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ALPHA
141 #elif defined(__hppa__)
142 # if defined(__LP64__)
143 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PARISC64
144 # else
145 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PARISC
146 # endif
147 #elif defined(__ia64__)
148 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_IA64
149 #elif defined(__microblaze__)
150 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MICROBLAZE
151 #elif defined(__m68k__)
152 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_M68K
153 #elif defined(__mips__)
154 # if defined(__MIPSEL__)
155 # if defined(__LP64__)
156 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64
157 # else
158 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL
159 # endif
160 # elif defined(__LP64__)
161 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64
162 # else
163 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS
164 # endif
165 #elif defined(__nds32__)
166 # if (BYTE_ORDER == LITTLE_ENDIAN)
167 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_NDS32
168 #else
169 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_NDS32BE
170 #endif
171 #elif defined(__nios2__)
172 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_NIOS2
173 #elif defined(__or1k__)
174 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_OPENRISC
175 #elif defined(__powerpc64__)
176 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64
177 #elif defined(__powerpc__)
178 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC
179 #elif defined(__riscv)
180 # if defined(__LP64__)
181 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_RISCV64
182 # else
183 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_RISCV32
184 # endif
185 #elif defined(__s390x__)
186 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390X
187 #elif defined(__s390__)
188 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390
189 #elif defined(__sh__)
190 # if defined(__LP64__)
191 # if (BYTE_ORDER == LITTLE_ENDIAN)
192 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SHEL64
193 # else
194 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SH64
195 # endif
196 # else
197 # if (BYTE_ORDER == LITTLE_ENDIAN)
198 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SHEL
199 # else
200 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SH
201 # endif
202 # endif
203 #elif defined(__sparc__)
204 # if defined(__arch64__)
205 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SPARC64
206 # else
207 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SPARC
208 # endif
209 #elif defined(__xtensa__)
210 # define SECCOMP_AUDIT_ARCH AUDIT_ARCH_XTENSA
211 #else
212 # error "Platform does not support seccomp filter yet"
213 #endif
215 static struct sock_filter filter[] = {
216 /* load the *current* architecture */
217 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
218 (offsetof(struct seccomp_data, arch))),
219 /* ensure it's the same that we've been compiled on */
220 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
221 SECCOMP_AUDIT_ARCH, 1, 0),
222 /* if not, kill the program */
223 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
225 /* load the syscall number */
226 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
227 (offsetof(struct seccomp_data, nr))),
229 #ifdef __NR_accept
230 SC_ALLOW(accept),
231 #endif
232 #ifdef __NR_accept4
233 SC_ALLOW(accept4),
234 #endif
235 #ifdef __NR_brk
236 SC_ALLOW(brk),
237 #endif
238 #ifdef __NR_clock_gettime
239 SC_ALLOW(clock_gettime),
240 #endif
241 #if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT)
242 SECCOMP_ALLOW(__NR_clock_gettime & ~__X32_SYSCALL_BIT),
243 #endif
244 #ifdef __NR_clock_gettime64
245 SC_ALLOW(clock_gettime64),
246 #endif
247 #ifdef __NR_close
248 SC_ALLOW(close),
249 #endif
250 #ifdef __NR_epoll_ctl
251 SC_ALLOW(epoll_ctl),
252 #endif
253 #ifdef __NR_epoll_pwait
254 SC_ALLOW(epoll_pwait),
255 #endif
256 #ifdef __NR_epoll_wait
257 SC_ALLOW(epoll_wait),
258 #endif
259 #ifdef __NR_exit
260 SC_ALLOW(exit),
261 #endif
262 #ifdef __NR_exit_group
263 SC_ALLOW(exit_group),
264 #endif
265 #ifdef __NR_fcntl
266 SC_ALLOW(fcntl),
267 #endif
268 #ifdef __NR_fcntl64
269 SC_ALLOW(fcntl64),
270 #endif
271 #ifdef __NR_fstat
272 SC_ALLOW(fstat),
273 #endif
274 #ifdef __NR_getdents64
275 SC_ALLOW(getdents64),
276 #endif
277 #ifdef __NR_getpid
278 SC_ALLOW(getpid),
279 #endif
280 #ifdef __NR_getrandom
281 SC_ALLOW(getrandom),
282 #endif
283 #ifdef __NR_gettimeofday
284 SC_ALLOW(gettimeofday),
285 #endif
286 #ifdef __NR_ioctl
287 /* allow ioctl only on fd 1, glibc doing stuff? */
288 SC_ALLOW_ARG(__NR_ioctl, 0, 1),
289 #endif
290 #ifdef __NR_lseek
291 SC_ALLOW(lseek),
292 #endif
293 #ifdef __NR_madvise
294 SC_ALLOW(madvise),
295 #endif
296 #ifdef __NR_mmap
297 SC_ALLOW(mmap),
298 #endif
299 #ifdef __NR_mmap2
300 SC_ALLOW(mmap2),
301 #endif
302 #ifdef __NR_munmap
303 SC_ALLOW(munmap),
304 #endif
305 #ifdef __NR_newfstatat
306 SC_ALLOW(newfstatat),
307 #endif
308 #ifdef __NR_oldfstat
309 SC_ALLOW(oldfstat),
310 #endif
311 #ifdef __NR_openat
312 SC_ALLOW(openat),
313 #endif
314 #ifdef __NR_prlimit64
315 SC_ALLOW(prlimit64),
316 #endif
317 #ifdef __NR_read
318 SC_ALLOW(read),
319 #endif
320 #ifdef __NR_recvmsg
321 SC_ALLOW(recvmsg),
322 #endif
323 #ifdef __NR_redav
324 SC_ALLOW(redav),
325 #endif
326 #ifdef __NR_rt_sigaction
327 SC_ALLOW(rt_sigaction),
328 #endif
329 #ifdef __NR_rt_sigreturn
330 SC_ALLOW(rt_sigreturn),
331 #endif
332 #ifdef __NR_sendmsg
333 SC_ALLOW(sendmsg),
334 #endif
335 #ifdef __NR_statx
336 SC_ALLOW(statx),
337 #endif
338 #ifdef __NR_write
339 SC_ALLOW(write),
340 #endif
341 #ifdef __NR_writev
342 SC_ALLOW(writev),
343 #endif
345 /* disallow enything else */
346 BPF_STMT(BPF_RET | BPF_K, SC_FAIL),
347 };
349 #ifdef SC_DEBUG
351 #include <signal.h>
352 #include <unistd.h>
354 static void
355 sandbox_seccomp_violation(int signum, siginfo_t *info, void *ctx)
357 (void)signum;
358 (void)ctx;
360 fprintf(stderr, "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)\n",
361 __func__, info->si_arch, info->si_syscall, info->si_call_addr);
362 _exit(1);
365 static void
366 sandbox_seccomp_catch_sigsys(void)
368 struct sigaction act;
369 sigset_t mask;
371 memset(&act, 0, sizeof(act));
372 sigemptyset(&mask);
373 sigaddset(&mask, SIGSYS);
375 act.sa_sigaction = &sandbox_seccomp_violation;
376 act.sa_flags = SA_SIGINFO;
377 if (sigaction(SIGSYS, &act, NULL) == -1)
378 fatal("%s: sigaction(SIGSYS): %s",
379 __func__, strerror(errno));
381 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
382 fatal("%s: sigprocmask(SIGSYS): %s\n",
383 __func__, strerror(errno));
385 #endif /* SC_DEBUG */
387 void
388 sandbox_server_process(void)
390 struct sock_fprog prog = {
391 .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])),
392 .filter = filter,
393 };
395 #ifdef SC_DEBUG
396 sandbox_seccomp_catch_sigsys();
397 #endif
399 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
400 fatal("%s: prctl(PR_SET_NO_NEW_PRIVS): %s",
401 __func__, strerror(errno));
403 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1)
404 fatal("%s: prctl(PR_SET_SECCOMP): %s\n",
405 __func__, strerror(errno));
408 void
409 sandbox_executor_process(void)
411 /* We cannot use seccomp for the executor process because we
412 * don't know what the child will do. Also, our filter will
413 * be inherited so the child cannot set its own seccomp
414 * policy. */
415 return;
418 void
419 sandbox_logger_process(void)
421 /* To be honest, here we could use a seccomp policy to only
422 * allow writev(2) and memory allocations. */
423 return;
426 #elif defined(__OpenBSD__)
428 #include <unistd.h>
430 void
431 sandbox_server_process(void)
433 struct vhost *h;
435 for (h = hosts; h->domain != NULL; ++h) {
436 if (unveil(h->dir, "r") == -1)
437 fatal("unveil %s for domain %s", h->dir, h->domain);
440 if (pledge("stdio recvfd rpath inet", NULL) == -1)
441 fatal("pledge");
444 void
445 sandbox_executor_process(void)
447 struct vhost *vhost;
449 for (vhost = hosts; vhost->domain != NULL; ++vhost) {
450 /* r so we can chdir into the correct directory */
451 if (unveil(vhost->dir, "rx") == -1)
452 err(1, "unveil %s for domain %s",
453 vhost->dir, vhost->domain);
456 /* rpath to chdir into the correct directory */
457 if (pledge("stdio rpath sendfd proc exec", NULL))
458 err(1, "pledge");
461 void
462 sandbox_logger_process(void)
464 if (pledge("stdio", NULL) == -1)
465 err(1, "pledge");
468 #else
470 #warning "No sandbox method known for this OS"
472 void
473 sandbox_server_process(void)
475 return;
478 void
479 sandbox_executor_process(void)
481 log_notice(NULL, "no sandbox method known for this OS");
484 void
485 sandbox_logger_process(void)
487 return;
490 #endif