/src/tor/src/lib/sandbox/sandbox.c
Line | Count | Source |
1 | | /* Copyright (c) 2001 Matej Pfajfar. |
2 | | * Copyright (c) 2001-2004, Roger Dingledine. |
3 | | * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. |
4 | | * Copyright (c) 2007-2021, The Tor Project, Inc. */ |
5 | | /* See LICENSE for licensing information */ |
6 | | |
7 | | /** |
8 | | * \file sandbox.c |
9 | | * \brief Code to enable sandboxing. |
10 | | **/ |
11 | | |
12 | | #include "orconfig.h" |
13 | | |
14 | | #ifndef _LARGEFILE64_SOURCE |
15 | | /** |
16 | | * Temporarily required for O_LARGEFILE flag. Needs to be removed |
17 | | * with the libevent fix. |
18 | | */ |
19 | | #define _LARGEFILE64_SOURCE |
20 | | #endif /* !defined(_LARGEFILE64_SOURCE) */ |
21 | | |
22 | | /** Malloc mprotect limit in bytes. |
23 | | * |
24 | | * 28/06/2017: This value was increased from 16 MB to 20 MB after we introduced |
25 | | * LZMA support in Tor (0.3.1.1-alpha). We limit our LZMA coder to 16 MB, but |
26 | | * liblzma have a small overhead that we need to compensate for to avoid being |
27 | | * killed by the sandbox. |
28 | | */ |
29 | | #define MALLOC_MP_LIM (20*1024*1024) |
30 | | |
31 | | #include <stdio.h> |
32 | | #include <string.h> |
33 | | #include <stdlib.h> |
34 | | #include <errno.h> |
35 | | |
36 | | #include "lib/sandbox/sandbox.h" |
37 | | #include "lib/container/map.h" |
38 | | #include "lib/err/torerr.h" |
39 | | #include "lib/log/log.h" |
40 | | #include "lib/cc/torint.h" |
41 | | #include "lib/malloc/malloc.h" |
42 | | #include "lib/string/scanf.h" |
43 | | |
44 | | #include "ext/tor_queue.h" |
45 | | #include "ext/ht.h" |
46 | | #include "ext/siphash.h" |
47 | | |
48 | | #define DEBUGGING_CLOSE |
49 | | |
50 | | #if defined(USE_LIBSECCOMP) |
51 | | |
52 | | #include <sys/mman.h> |
53 | | #include <sys/syscall.h> |
54 | | #include <sys/types.h> |
55 | | #include <sys/stat.h> |
56 | | #include <sys/epoll.h> |
57 | | #include <sys/prctl.h> |
58 | | #include <linux/futex.h> |
59 | | #include <sys/file.h> |
60 | | |
61 | | #ifdef ENABLE_FRAGILE_HARDENING |
62 | | #include <sys/ptrace.h> |
63 | | #endif |
64 | | |
65 | | #include <stdarg.h> |
66 | | #include <seccomp.h> |
67 | | #include <signal.h> |
68 | | #include <unistd.h> |
69 | | #include <fcntl.h> |
70 | | #include <time.h> |
71 | | #include <poll.h> |
72 | | |
73 | | #ifdef HAVE_GNU_LIBC_VERSION_H |
74 | | #include <gnu/libc-version.h> |
75 | | #endif |
76 | | #ifdef HAVE_LINUX_NETFILTER_IPV4_H |
77 | | #include <linux/netfilter_ipv4.h> |
78 | | #endif |
79 | | #ifdef HAVE_LINUX_IF_H |
80 | | #include <linux/if.h> |
81 | | #endif |
82 | | #ifdef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H |
83 | | #include <linux/netfilter_ipv6/ip6_tables.h> |
84 | | #endif |
85 | | |
86 | | #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ |
87 | | defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) |
88 | | #define USE_BACKTRACE |
89 | | #define BACKTRACE_PRIVATE |
90 | | #include "lib/err/backtrace.h" |
91 | | #endif /* defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && ... */ |
92 | | |
93 | | #ifdef USE_BACKTRACE |
94 | | #include <execinfo.h> |
95 | | #endif |
96 | | |
97 | | /** |
98 | | * Linux 32 bit definitions |
99 | | */ |
100 | | #if defined(__i386__) |
101 | | |
102 | | #define REG_SYSCALL REG_EAX |
103 | | #define M_SYSCALL gregs[REG_SYSCALL] |
104 | | |
105 | | /** |
106 | | * Linux 64 bit definitions |
107 | | */ |
108 | | #elif defined(__x86_64__) |
109 | | |
110 | | #define REG_SYSCALL REG_RAX |
111 | | #define M_SYSCALL gregs[REG_SYSCALL] |
112 | | |
113 | | #elif defined(__arm__) |
114 | | |
115 | | #define M_SYSCALL arm_r7 |
116 | | |
117 | | #elif defined(__aarch64__) && defined(__LP64__) |
118 | | |
119 | | #define REG_SYSCALL 8 |
120 | | #define M_SYSCALL regs[REG_SYSCALL] |
121 | | |
122 | | #endif /* defined(__i386__) || ... */ |
123 | | |
124 | | #ifdef M_SYSCALL |
125 | | #define SYSCALL_NAME_DEBUGGING |
126 | | #endif |
127 | | |
128 | | /** |
129 | | * On newer architectures Linux provides a standardized, generic set of system |
130 | | * calls (defined in Linux's include/uapi/asm-generic/unistd.h), which omits a |
131 | | * number of legacy calls used by glibc on other platforms. |
132 | | */ |
133 | | #if defined(__aarch64__) || defined(__riscv) |
134 | | #define ARCH_USES_GENERIC_SYSCALLS |
135 | | #endif |
136 | | |
137 | | /**Determines if at least one sandbox is active.*/ |
138 | | static int sandbox_active = 0; |
139 | | /** Holds the parameter list configuration for the sandbox.*/ |
140 | | static sandbox_cfg_t *filter_dynamic = NULL; |
141 | | |
142 | | #undef SCMP_CMP |
143 | | #define SCMP_CMP(a,b,c) ((struct scmp_arg_cmp){(a),(b),(c),0}) |
144 | | #define SCMP_CMP_STR(a,b,c) \ |
145 | | ((struct scmp_arg_cmp) {(a),(b),(intptr_t)(void*)(c),0}) |
146 | | #define SCMP_CMP4(a,b,c,d) ((struct scmp_arg_cmp){(a),(b),(c),(d)}) |
147 | | /* We use a wrapper here because these masked comparisons seem to be pretty |
148 | | * verbose. Also, it's important to cast to scmp_datum_t before negating the |
149 | | * mask, since otherwise the negation might get applied to a 32 bit value, and |
150 | | * the high bits of the value might get masked out improperly. */ |
151 | | #define SCMP_CMP_MASKED(a,b,c) \ |
152 | | SCMP_CMP4((a), SCMP_CMP_MASKED_EQ, ~(scmp_datum_t)(b), (c)) |
153 | | /* Negative constants aren't consistently sign extended or zero extended. |
154 | | * Different compilers, libc, and architectures behave differently. For cases |
155 | | * where the kernel ABI uses a 32 bit integer, this macro can be used to |
156 | | * mask-compare only the lower 32 bits of the value. */ |
157 | | #define SCMP_CMP_LOWER32_EQ(a,b) \ |
158 | | SCMP_CMP4((a), SCMP_CMP_MASKED_EQ, 0xFFFFFFFF, (unsigned int)(b)) |
159 | | |
160 | | /** Variable used for storing all syscall numbers that will be allowed with the |
161 | | * stage 1 general Tor sandbox. |
162 | | */ |
163 | | static int filter_nopar_gen[] = { |
164 | | SCMP_SYS(access), |
165 | | SCMP_SYS(brk), |
166 | | #ifdef __NR_clock_gettime64 |
167 | | SCMP_SYS(clock_gettime64), |
168 | | #else |
169 | | SCMP_SYS(clock_gettime), |
170 | | #endif |
171 | | SCMP_SYS(close), |
172 | | SCMP_SYS(clone), |
173 | | SCMP_SYS(dup), |
174 | | #ifdef __NR_clone3 |
175 | | SCMP_SYS(clone3), |
176 | | #endif |
177 | | SCMP_SYS(epoll_create), |
178 | | SCMP_SYS(epoll_wait), |
179 | | #ifdef __NR_epoll_pwait |
180 | | SCMP_SYS(epoll_pwait), |
181 | | #endif |
182 | | #ifdef HAVE_EVENTFD |
183 | | SCMP_SYS(eventfd2), |
184 | | #endif |
185 | | #ifdef HAVE_PIPE2 |
186 | | SCMP_SYS(pipe2), |
187 | | #endif |
188 | | #ifdef HAVE_PIPE |
189 | | SCMP_SYS(pipe), |
190 | | #endif |
191 | | #ifdef __NR_fchmod |
192 | | SCMP_SYS(fchmod), |
193 | | #endif |
194 | | SCMP_SYS(fcntl), |
195 | | SCMP_SYS(fstat), |
196 | | #ifdef __NR_fstat64 |
197 | | SCMP_SYS(fstat64), |
198 | | #endif |
199 | | SCMP_SYS(fsync), |
200 | | SCMP_SYS(futex), |
201 | | SCMP_SYS(getdents), |
202 | | SCMP_SYS(getdents64), |
203 | | SCMP_SYS(getegid), |
204 | | #ifdef __NR_getegid32 |
205 | | SCMP_SYS(getegid32), |
206 | | #endif |
207 | | SCMP_SYS(geteuid), |
208 | | #ifdef __NR_geteuid32 |
209 | | SCMP_SYS(geteuid32), |
210 | | #endif |
211 | | SCMP_SYS(getgid), |
212 | | #ifdef __NR_getgid32 |
213 | | SCMP_SYS(getgid32), |
214 | | #endif |
215 | | SCMP_SYS(getpid), |
216 | | #ifdef ENABLE_FRAGILE_HARDENING |
217 | | SCMP_SYS(getppid), |
218 | | #endif |
219 | | #ifdef __NR_getrlimit |
220 | | SCMP_SYS(getrlimit), |
221 | | #endif |
222 | | SCMP_SYS(gettimeofday), |
223 | | SCMP_SYS(gettid), |
224 | | SCMP_SYS(getuid), |
225 | | #ifdef __NR_getuid32 |
226 | | SCMP_SYS(getuid32), |
227 | | #endif |
228 | | SCMP_SYS(lseek), |
229 | | #ifdef __NR__llseek |
230 | | SCMP_SYS(_llseek), |
231 | | #endif |
232 | | // glob uses this.. |
233 | | SCMP_SYS(lstat), |
234 | | #ifdef __NR_membarrier |
235 | | /* Inter-processor synchronization, needed for tracing support */ |
236 | | SCMP_SYS(membarrier), |
237 | | #endif |
238 | | SCMP_SYS(mkdir), |
239 | | SCMP_SYS(mlockall), |
240 | | #ifdef __NR_mmap |
241 | | /* XXXX restrict this in the same ways as mmap2 */ |
242 | | SCMP_SYS(mmap), |
243 | | #endif |
244 | | SCMP_SYS(munmap), |
245 | | #ifdef __NR_nanosleep |
246 | | SCMP_SYS(nanosleep), |
247 | | #endif |
248 | | #ifdef __NR_prlimit |
249 | | SCMP_SYS(prlimit), |
250 | | #endif |
251 | | #ifdef __NR_prlimit64 |
252 | | SCMP_SYS(prlimit64), |
253 | | #endif |
254 | | SCMP_SYS(read), |
255 | | SCMP_SYS(rt_sigreturn), |
256 | | #ifdef __NR_rseq |
257 | | SCMP_SYS(rseq), |
258 | | #endif |
259 | | SCMP_SYS(sched_getaffinity), |
260 | | #ifdef __NR_sched_yield |
261 | | SCMP_SYS(sched_yield), |
262 | | #endif |
263 | | SCMP_SYS(sendmsg), |
264 | | SCMP_SYS(set_robust_list), |
265 | | #ifdef __NR_setrlimit |
266 | | SCMP_SYS(setrlimit), |
267 | | #endif |
268 | | SCMP_SYS(shutdown), |
269 | | #ifdef __NR_sigaltstack |
270 | | SCMP_SYS(sigaltstack), |
271 | | #endif |
272 | | #ifdef __NR_sigreturn |
273 | | SCMP_SYS(sigreturn), |
274 | | #endif |
275 | | #if defined(__NR_stat) |
276 | | SCMP_SYS(stat), |
277 | | #elif defined(__i386__) && defined(__NR_statx) |
278 | | SCMP_SYS(statx), |
279 | | #endif |
280 | | SCMP_SYS(uname), |
281 | | SCMP_SYS(wait4), |
282 | | SCMP_SYS(write), |
283 | | SCMP_SYS(writev), |
284 | | SCMP_SYS(exit_group), |
285 | | SCMP_SYS(exit), |
286 | | |
287 | | SCMP_SYS(madvise), |
288 | | #ifdef __NR_stat64 |
289 | | // getaddrinfo uses this.. |
290 | | SCMP_SYS(stat64), |
291 | | #endif |
292 | | #ifdef __NR_lstat64 |
293 | | // glob uses this on i386 with glibc 2.36+ |
294 | | SCMP_SYS(lstat64), |
295 | | #endif |
296 | | |
297 | | #ifdef __NR_getrandom |
298 | | SCMP_SYS(getrandom), |
299 | | #endif |
300 | | |
301 | | #ifdef __NR_sysinfo |
302 | | // qsort uses this.. |
303 | | SCMP_SYS(sysinfo), |
304 | | #endif |
305 | | /* |
306 | | * These socket syscalls are not required on x86_64 and not supported with |
307 | | * some libseccomp versions (eg: 1.0.1) |
308 | | */ |
309 | | #if defined(__i386) |
310 | | SCMP_SYS(recv), |
311 | | SCMP_SYS(send), |
312 | | #endif |
313 | | |
314 | | // socket syscalls |
315 | | SCMP_SYS(bind), |
316 | | SCMP_SYS(listen), |
317 | | SCMP_SYS(connect), |
318 | | SCMP_SYS(getsockname), |
319 | | #ifdef ENABLE_NSS |
320 | | #ifdef __NR_getpeername |
321 | | SCMP_SYS(getpeername), |
322 | | #endif |
323 | | #endif |
324 | | SCMP_SYS(recvmsg), |
325 | | SCMP_SYS(recvfrom), |
326 | | SCMP_SYS(sendto), |
327 | | SCMP_SYS(unlink), |
328 | | #ifdef __NR_unlinkat |
329 | | SCMP_SYS(unlinkat), |
330 | | #endif |
331 | | SCMP_SYS(poll) |
332 | | }; |
333 | | |
334 | | /* opendir is not a syscall but it will use either open or openat. We do not |
335 | | * want the decision to allow open/openat to be the callers reponsability, so |
336 | | * we create a phony syscall number for opendir and sb_opendir will choose the |
337 | | * correct syscall. */ |
338 | | #define PHONY_OPENDIR_SYSCALL -2 |
339 | | |
340 | | /* These macros help avoid the error where the number of filters we add on a |
341 | | * single rule don't match the arg_cnt param. */ |
342 | | #define seccomp_rule_add_0(ctx,act,call) \ |
343 | | seccomp_rule_add((ctx),(act),(call),0) |
344 | | #define seccomp_rule_add_1(ctx,act,call,f1) \ |
345 | | seccomp_rule_add((ctx),(act),(call),1,(f1)) |
346 | | #define seccomp_rule_add_2(ctx,act,call,f1,f2) \ |
347 | | seccomp_rule_add((ctx),(act),(call),2,(f1),(f2)) |
348 | | #define seccomp_rule_add_3(ctx,act,call,f1,f2,f3) \ |
349 | | seccomp_rule_add((ctx),(act),(call),3,(f1),(f2),(f3)) |
350 | | #define seccomp_rule_add_4(ctx,act,call,f1,f2,f3,f4) \ |
351 | | seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4)) |
352 | | #define seccomp_rule_add_5(ctx,act,call,f1,f2,f3,f4,f5) \ |
353 | | seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4),(f5)) |
354 | | |
355 | | static const char *sandbox_get_interned_string(const char *str); |
356 | | |
357 | | /** |
358 | | * Function responsible for setting up the rt_sigaction syscall for |
359 | | * the seccomp filter sandbox. |
360 | | */ |
361 | | static int |
362 | | sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
363 | | { |
364 | | unsigned i; |
365 | | int rc; |
366 | | int param[] = { SIGINT, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, SIGHUP, SIGCHLD, |
367 | | SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS, SIGIO, |
368 | | #ifdef SIGXFSZ |
369 | | SIGXFSZ |
370 | | #endif |
371 | | }; |
372 | | (void) filter; |
373 | | |
374 | | for (i = 0; i < ARRAY_LENGTH(param); i++) { |
375 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), |
376 | | SCMP_CMP(0, SCMP_CMP_EQ, param[i])); |
377 | | if (rc) |
378 | | break; |
379 | | } |
380 | | |
381 | | return rc; |
382 | | } |
383 | | |
384 | | #ifdef __NR_time |
385 | | /** |
386 | | * Function responsible for setting up the time syscall for |
387 | | * the seccomp filter sandbox. |
388 | | */ |
389 | | static int |
390 | | sb_time(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
391 | | { |
392 | | (void) filter; |
393 | | |
394 | | return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(time), |
395 | | SCMP_CMP(0, SCMP_CMP_EQ, 0)); |
396 | | } |
397 | | #endif /* defined(__NR_time) */ |
398 | | |
399 | | /** |
400 | | * Function responsible for setting up the accept4 syscall for |
401 | | * the seccomp filter sandbox. |
402 | | */ |
403 | | static int |
404 | | sb_accept4(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
405 | | { |
406 | | int rc = 0; |
407 | | (void)filter; |
408 | | |
409 | | #ifdef __i386__ |
410 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall), |
411 | | SCMP_CMP(0, SCMP_CMP_EQ, 18)); |
412 | | if (rc) { |
413 | | return rc; |
414 | | } |
415 | | #endif /* defined(__i386__) */ |
416 | | |
417 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), |
418 | | SCMP_CMP_MASKED(3, SOCK_CLOEXEC|SOCK_NONBLOCK, 0)); |
419 | | if (rc) { |
420 | | return rc; |
421 | | } |
422 | | |
423 | | return 0; |
424 | | } |
425 | | |
426 | | #ifdef __NR_mmap2 |
427 | | /** |
428 | | * Function responsible for setting up the mmap2 syscall for |
429 | | * the seccomp filter sandbox. |
430 | | */ |
431 | | static int |
432 | | sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
433 | | { |
434 | | int rc = 0; |
435 | | (void)filter; |
436 | | |
437 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
438 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ), |
439 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE)); |
440 | | if (rc) { |
441 | | return rc; |
442 | | } |
443 | | |
444 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
445 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE), |
446 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE)); |
447 | | if (rc) { |
448 | | return rc; |
449 | | } |
450 | | |
451 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
452 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), |
453 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS)); |
454 | | if (rc) { |
455 | | return rc; |
456 | | } |
457 | | |
458 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
459 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), |
460 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK)); |
461 | | if (rc) { |
462 | | return rc; |
463 | | } |
464 | | |
465 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
466 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE), |
467 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK)); |
468 | | if (rc) { |
469 | | return rc; |
470 | | } |
471 | | |
472 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
473 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), |
474 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE)); |
475 | | if (rc) { |
476 | | return rc; |
477 | | } |
478 | | |
479 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
480 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE), |
481 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS)); |
482 | | if (rc) { |
483 | | return rc; |
484 | | } |
485 | | |
486 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), |
487 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_EXEC), |
488 | | SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_DENYWRITE)); |
489 | | if (rc) { |
490 | | return rc; |
491 | | } |
492 | | |
493 | | return 0; |
494 | | } |
495 | | #endif /* defined(__NR_mmap2) */ |
496 | | |
497 | | #ifdef HAVE_GNU_LIBC_VERSION_H |
498 | | #ifdef HAVE_GNU_GET_LIBC_VERSION |
499 | | #define CHECK_LIBC_VERSION |
500 | | #endif |
501 | | #endif |
502 | | |
503 | | /* Return true the libc version is greater or equal than |
504 | | * <b>major</b>.<b>minor</b>. Returns false otherwise. */ |
505 | | static int |
506 | | is_libc_at_least(int major, int minor) |
507 | | { |
508 | | #ifdef CHECK_LIBC_VERSION |
509 | | const char *version = gnu_get_libc_version(); |
510 | | if (version == NULL) |
511 | | return 0; |
512 | | |
513 | | int libc_major = -1; |
514 | | int libc_minor = -1; |
515 | | |
516 | | tor_sscanf(version, "%d.%d", &libc_major, &libc_minor); |
517 | | if (libc_major > major) |
518 | | return 1; |
519 | | else if (libc_major == major && libc_minor >= minor) |
520 | | return 1; |
521 | | else |
522 | | return 0; |
523 | | #else /* !defined(CHECK_LIBC_VERSION) */ |
524 | | (void)major; |
525 | | (void)minor; |
526 | | return 0; |
527 | | #endif /* defined(CHECK_LIBC_VERSION) */ |
528 | | } |
529 | | |
530 | | /* Return true if we think we're running with a libc that uses openat for the |
531 | | * open function on linux. */ |
532 | | static int |
533 | | libc_uses_openat_for_open(void) |
534 | | { |
535 | | #ifdef __NR_open |
536 | | return is_libc_at_least(2, 26); |
537 | | #else |
538 | | return 1; |
539 | | #endif /* defined(__NR_open) */ |
540 | | } |
541 | | |
542 | | /* Calls to opendir() cannot be filtered by the sandbox when built with fragile |
543 | | * hardening for an architecture that uses Linux's generic syscall interface, |
544 | | * so prevent a compiler warning by omitting this function along with |
545 | | * sb_opendir(). */ |
546 | | #if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) |
547 | | /* Return true if we think we're running with a libc that uses openat for the |
548 | | * opendir function on linux. */ |
549 | | static int |
550 | | libc_uses_openat_for_opendir(void) |
551 | | { |
552 | | #ifdef __NR_open |
553 | | // libc 2.27 and above or between 2.15 (inclusive) and 2.22 (exclusive) |
554 | | return is_libc_at_least(2, 27) || |
555 | | (is_libc_at_least(2, 15) && !is_libc_at_least(2, 22)); |
556 | | #else |
557 | | return 1; |
558 | | #endif /* defined(__NR_open) */ |
559 | | } |
560 | | #endif /* !(defined(ENABLE_FRAGILE_HARDENING) && |
561 | | defined(ARCH_USES_GENERIC_SYSCALLS)) */ |
562 | | |
563 | | /** Allow a single file to be opened. If <b>use_openat</b> is true, |
564 | | * we're using a libc that remaps all the opens into openats. */ |
565 | | static int |
566 | | allow_file_open(scmp_filter_ctx ctx, int use_openat, const char *file) |
567 | | { |
568 | | if (use_openat) { |
569 | | return seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), |
570 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), |
571 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, file)); |
572 | | } else { |
573 | | return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), |
574 | | SCMP_CMP_STR(0, SCMP_CMP_EQ, file)); |
575 | | } |
576 | | } |
577 | | |
578 | | /** |
579 | | * Function responsible for setting up the open syscall for |
580 | | * the seccomp filter sandbox. |
581 | | */ |
582 | | static int |
583 | | sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
584 | | { |
585 | | int rc; |
586 | | sandbox_cfg_t *elem = NULL; |
587 | | |
588 | | int use_openat = libc_uses_openat_for_open(); |
589 | | |
590 | | #ifdef ENABLE_FRAGILE_HARDENING |
591 | | /* AddressSanitizer uses either the "open" or the "openat" syscall (depending |
592 | | * on the architecture) to access information about the running process via |
593 | | * the filesystem, so the appropriate call must be allowed without |
594 | | * restriction or the sanitizer will be unable to execute normally when the |
595 | | * process terminates. */ |
596 | | #ifdef ARCH_USES_GENERIC_SYSCALLS |
597 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), |
598 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD)); |
599 | | if (rc != 0) { |
600 | | log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received " |
601 | | "libseccomp error %d", rc); |
602 | | return rc; |
603 | | } |
604 | | |
605 | | /* The "open" syscall is not defined on this architecture, so any other |
606 | | * requests to open files will necessarily use "openat" as well and there is |
607 | | * no need to consider any additional rules. */ |
608 | | return 0; |
609 | | #else |
610 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open)); |
611 | | if (rc != 0) { |
612 | | log_err(LD_BUG,"(Sandbox) failed to add open syscall, received " |
613 | | "libseccomp error %d", rc); |
614 | | return rc; |
615 | | } |
616 | | |
617 | | /* If glibc also uses only the "open" syscall to open files on this system |
618 | | * there is no need to consider any additional rules. */ |
619 | | if (!use_openat) |
620 | | return 0; |
621 | | #endif /* defined(ARCH_USES_GENERIC_SYSCALLS) */ |
622 | | #endif /* defined(ENABLE_FRAGILE_HARDENING) */ |
623 | | |
624 | | // for each dynamic parameter filters |
625 | | for (elem = filter; elem != NULL; elem = elem->next) { |
626 | | smp_param_t *param = elem->param; |
627 | | |
628 | | if (param != NULL && param->prot == 1 && param->syscall |
629 | | == SCMP_SYS(open)) { |
630 | | rc = allow_file_open(ctx, use_openat, param->value); |
631 | | if (rc != 0) { |
632 | | log_err(LD_BUG,"(Sandbox) failed to add open syscall, received " |
633 | | "libseccomp error %d", rc); |
634 | | return rc; |
635 | | } |
636 | | } |
637 | | } |
638 | | |
639 | | return 0; |
640 | | } |
641 | | |
642 | | #ifdef ARCH_USES_GENERIC_SYSCALLS |
643 | | static int |
644 | | sb_fchmodat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
645 | | { |
646 | | int rc; |
647 | | sandbox_cfg_t *elem = NULL; |
648 | | |
649 | | // for each dynamic parameter filters |
650 | | for (elem = filter; elem != NULL; elem = elem->next) { |
651 | | smp_param_t *param = elem->param; |
652 | | |
653 | | if (param != NULL && param->prot == 1 && param->syscall |
654 | | == SCMP_SYS(fchmodat)) { |
655 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmodat), |
656 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), |
657 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value)); |
658 | | if (rc != 0) { |
659 | | log_err(LD_BUG,"(Sandbox) failed to add fchmodat syscall, received " |
660 | | "libseccomp error %d", rc); |
661 | | return rc; |
662 | | } |
663 | | } |
664 | | } |
665 | | |
666 | | return 0; |
667 | | } |
668 | | #else |
669 | | static int |
670 | | sb_chmod(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
671 | | { |
672 | | int rc; |
673 | | sandbox_cfg_t *elem = NULL; |
674 | | |
675 | | // for each dynamic parameter filters |
676 | | for (elem = filter; elem != NULL; elem = elem->next) { |
677 | | smp_param_t *param = elem->param; |
678 | | |
679 | | if (param != NULL && param->prot == 1 && param->syscall |
680 | | == SCMP_SYS(chmod)) { |
681 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chmod), |
682 | | SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value)); |
683 | | if (rc != 0) { |
684 | | log_err(LD_BUG,"(Sandbox) failed to add chmod syscall, received " |
685 | | "libseccomp error %d", rc); |
686 | | return rc; |
687 | | } |
688 | | } |
689 | | } |
690 | | |
691 | | return 0; |
692 | | } |
693 | | #endif /* defined(ARCH_USES_GENERIC_SYSCALLS) */ |
694 | | |
695 | | #if defined(ARCH_USES_GENERIC_SYSCALLS) |
696 | | static int |
697 | | sb_fchownat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
698 | | { |
699 | | int rc; |
700 | | sandbox_cfg_t *elem = NULL; |
701 | | |
702 | | // for each dynamic parameter filters |
703 | | for (elem = filter; elem != NULL; elem = elem->next) { |
704 | | smp_param_t *param = elem->param; |
705 | | |
706 | | if (param != NULL && param->prot == 1 && param->syscall |
707 | | == SCMP_SYS(fchownat)) { |
708 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchownat), |
709 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), |
710 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value)); |
711 | | if (rc != 0) { |
712 | | log_err(LD_BUG,"(Sandbox) failed to add fchownat syscall, received " |
713 | | "libseccomp error %d", rc); |
714 | | return rc; |
715 | | } |
716 | | } |
717 | | } |
718 | | |
719 | | return 0; |
720 | | } |
721 | | #elif defined(__i386__) |
722 | | static int |
723 | | sb_chown32(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
724 | | { |
725 | | int rc; |
726 | | sandbox_cfg_t *elem = NULL; |
727 | | |
728 | | // for each dynamic parameter filters |
729 | | for (elem = filter; elem != NULL; elem = elem->next) { |
730 | | smp_param_t *param = elem->param; |
731 | | |
732 | | if (param != NULL && param->prot == 1 && param->syscall |
733 | | == SCMP_SYS(chown32)) { |
734 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chown32), |
735 | | SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value)); |
736 | | if (rc != 0) { |
737 | | log_err(LD_BUG,"(Sandbox) failed to add chown32 syscall, received " |
738 | | "libseccomp error %d", rc); |
739 | | return rc; |
740 | | } |
741 | | } |
742 | | } |
743 | | |
744 | | return 0; |
745 | | } |
746 | | #else |
747 | | static int |
748 | | sb_chown(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
749 | | { |
750 | | int rc; |
751 | | sandbox_cfg_t *elem = NULL; |
752 | | |
753 | | // for each dynamic parameter filters |
754 | | for (elem = filter; elem != NULL; elem = elem->next) { |
755 | | smp_param_t *param = elem->param; |
756 | | |
757 | | if (param != NULL && param->prot == 1 && param->syscall |
758 | | == SCMP_SYS(chown)) { |
759 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chown), |
760 | | SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value)); |
761 | | if (rc != 0) { |
762 | | log_err(LD_BUG,"(Sandbox) failed to add chown syscall, received " |
763 | | "libseccomp error %d", rc); |
764 | | return rc; |
765 | | } |
766 | | } |
767 | | } |
768 | | |
769 | | return 0; |
770 | | } |
771 | | #endif /* defined(ARCH_USES_GENERIC_SYSCALLS) || defined(__i386__) */ |
772 | | |
773 | | #if defined(__NR_rename) |
774 | | /** |
775 | | * Function responsible for setting up the rename syscall for |
776 | | * the seccomp filter sandbox. |
777 | | */ |
778 | | static int |
779 | | sb_rename(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
780 | | { |
781 | | int rc; |
782 | | sandbox_cfg_t *elem = NULL; |
783 | | |
784 | | // for each dynamic parameter filters |
785 | | for (elem = filter; elem != NULL; elem = elem->next) { |
786 | | smp_param_t *param = elem->param; |
787 | | |
788 | | if (param != NULL && param->prot == 1 && |
789 | | param->syscall == SCMP_SYS(rename)) { |
790 | | |
791 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rename), |
792 | | SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value), |
793 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value2)); |
794 | | if (rc != 0) { |
795 | | log_err(LD_BUG,"(Sandbox) failed to add rename syscall, received " |
796 | | "libseccomp error %d", rc); |
797 | | return rc; |
798 | | } |
799 | | } |
800 | | } |
801 | | |
802 | | return 0; |
803 | | } |
804 | | #elif defined(__NR_renameat) |
805 | | /** |
806 | | * Function responsible for setting up the renameat syscall for |
807 | | * the seccomp filter sandbox. |
808 | | */ |
809 | | static int |
810 | | sb_renameat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
811 | | { |
812 | | int rc; |
813 | | sandbox_cfg_t *elem = NULL; |
814 | | |
815 | | // for each dynamic parameter filters |
816 | | for (elem = filter; elem != NULL; elem = elem->next) { |
817 | | smp_param_t *param = elem->param; |
818 | | |
819 | | if (param != NULL && param->prot == 1 && |
820 | | param->syscall == SCMP_SYS(renameat)) { |
821 | | |
822 | | rc = seccomp_rule_add_4(ctx, SCMP_ACT_ALLOW, SCMP_SYS(renameat), |
823 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), |
824 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value), |
825 | | SCMP_CMP_LOWER32_EQ(2, AT_FDCWD), |
826 | | SCMP_CMP_STR(3, SCMP_CMP_EQ, param->value2)); |
827 | | if (rc != 0) { |
828 | | log_err(LD_BUG,"(Sandbox) failed to add renameat syscall, received " |
829 | | "libseccomp error %d", rc); |
830 | | return rc; |
831 | | } |
832 | | } |
833 | | } |
834 | | |
835 | | return 0; |
836 | | } |
837 | | #else |
838 | | /** |
839 | | * Function responsible for setting up the renameat2 syscall for |
840 | | * the seccomp filter sandbox. |
841 | | */ |
842 | | static int |
843 | | sb_renameat2(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
844 | | { |
845 | | int rc; |
846 | | sandbox_cfg_t *elem = NULL; |
847 | | |
848 | | // for each dynamic parameter filters |
849 | | for (elem = filter; elem != NULL; elem = elem->next) { |
850 | | smp_param_t *param = elem->param; |
851 | | |
852 | | if (param != NULL && param->prot == 1 && |
853 | | param->syscall == SCMP_SYS(renameat2)) { |
854 | | |
855 | | rc = seccomp_rule_add_5(ctx, SCMP_ACT_ALLOW, SCMP_SYS(renameat2), |
856 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), |
857 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value), |
858 | | SCMP_CMP_LOWER32_EQ(2, AT_FDCWD), |
859 | | SCMP_CMP_STR(3, SCMP_CMP_EQ, param->value2), |
860 | | SCMP_CMP(4, SCMP_CMP_EQ, 0)); |
861 | | if (rc != 0) { |
862 | | log_err(LD_BUG,"(Sandbox) failed to add renameat2 syscall, received " |
863 | | "libseccomp error %d", rc); |
864 | | return rc; |
865 | | } |
866 | | } |
867 | | } |
868 | | |
869 | | return 0; |
870 | | } |
871 | | #endif /* defined(__NR_rename) || defined(__NR_renameat) */ |
872 | | |
873 | | /* If Tor is built with fragile hardening for an architecture that uses Linux's |
874 | | * generic syscall interface a rule allowing the "openat" syscall without |
875 | | * restriction will have already been added by sb_open(), so there is no need |
876 | | * to consider adding additional, more restrictive rules here as they will |
877 | | * simply be ignored. |
878 | | * |
879 | | * Also, since the "open" syscall is not defined on these architectures, glibc |
880 | | * will necessarily use "openat" for its implementation of opendir() as well. |
881 | | * This means neither of the following two functions will have any effect and |
882 | | * both can be omitted. */ |
883 | | #if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) |
884 | | /** |
885 | | * Function responsible for setting up the openat syscall for |
886 | | * the seccomp filter sandbox. |
887 | | */ |
888 | | static int |
889 | | sb_openat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
890 | | { |
891 | | int rc; |
892 | | sandbox_cfg_t *elem = NULL; |
893 | | |
894 | | // for each dynamic parameter filters |
895 | | for (elem = filter; elem != NULL; elem = elem->next) { |
896 | | smp_param_t *param = elem->param; |
897 | | |
898 | | if (param != NULL && param->prot == 1 && param->syscall |
899 | | == SCMP_SYS(openat)) { |
900 | | rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), |
901 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), |
902 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value), |
903 | | SCMP_CMP(2, SCMP_CMP_EQ, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY| |
904 | | O_CLOEXEC)); |
905 | | if (rc != 0) { |
906 | | log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received " |
907 | | "libseccomp error %d", rc); |
908 | | return rc; |
909 | | } |
910 | | } |
911 | | } |
912 | | |
913 | | return 0; |
914 | | } |
915 | | |
916 | | static int |
917 | | sb_opendir(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
918 | | { |
919 | | int rc; |
920 | | sandbox_cfg_t *elem = NULL; |
921 | | |
922 | | // for each dynamic parameter filters |
923 | | for (elem = filter; elem != NULL; elem = elem->next) { |
924 | | smp_param_t *param = elem->param; |
925 | | |
926 | | if (param != NULL && param->prot == 1 && param->syscall |
927 | | == PHONY_OPENDIR_SYSCALL) { |
928 | | rc = allow_file_open(ctx, libc_uses_openat_for_opendir(), param->value); |
929 | | if (rc != 0) { |
930 | | log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received " |
931 | | "libseccomp error %d", rc); |
932 | | return rc; |
933 | | } |
934 | | } |
935 | | } |
936 | | |
937 | | return 0; |
938 | | } |
939 | | #endif /* !(defined(ENABLE_FRAGILE_HARDENING) && |
940 | | defined(ARCH_USES_GENERIC_SYSCALLS)) */ |
941 | | |
942 | | #ifdef ENABLE_FRAGILE_HARDENING |
943 | | /** |
944 | | * Function responsible for setting up the ptrace syscall for |
945 | | * the seccomp filter sandbox. |
946 | | */ |
947 | | static int |
948 | | sb_ptrace(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
949 | | { |
950 | | int rc; |
951 | | pid_t pid = getpid(); |
952 | | (void) filter; |
953 | | |
954 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), |
955 | | SCMP_CMP(0, SCMP_CMP_EQ, PTRACE_ATTACH), |
956 | | SCMP_CMP(1, SCMP_CMP_EQ, pid)); |
957 | | if (rc) |
958 | | return rc; |
959 | | |
960 | | /* AddressSanitizer uses "PTRACE_GETREGSET" on AArch64 (ARM64) and |
961 | | * System/390, "PTRACE_GETREGS" everywhere else. */ |
962 | | #if defined(__aarch64__) || defined(__s390__) |
963 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), |
964 | | SCMP_CMP(0, SCMP_CMP_EQ, PTRACE_GETREGSET), |
965 | | SCMP_CMP(1, SCMP_CMP_EQ, pid)); |
966 | | #else |
967 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), |
968 | | SCMP_CMP(0, SCMP_CMP_EQ, PTRACE_GETREGS), |
969 | | SCMP_CMP(1, SCMP_CMP_EQ, pid)); |
970 | | #endif /* defined(__aarch64__) || defined(__s390__) */ |
971 | | if (rc) |
972 | | return rc; |
973 | | |
974 | | return 0; |
975 | | } |
976 | | #endif |
977 | | |
978 | | /** |
979 | | * Function responsible for setting up the socket syscall for |
980 | | * the seccomp filter sandbox. |
981 | | */ |
982 | | static int |
983 | | sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
984 | | { |
985 | | int rc = 0; |
986 | | int i, j; |
987 | | (void) filter; |
988 | | |
989 | | #ifdef __i386__ |
990 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket)); |
991 | | if (rc) |
992 | | return rc; |
993 | | #endif |
994 | | |
995 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), |
996 | | SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE), |
997 | | SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM)); |
998 | | if (rc) |
999 | | return rc; |
1000 | | |
1001 | | for (i = 0; i < 2; ++i) { |
1002 | | const int pf = i ? PF_INET : PF_INET6; |
1003 | | for (j=0; j < 3; ++j) { |
1004 | | const int type = (j == 0) ? SOCK_STREAM : |
1005 | | SOCK_DGRAM; |
1006 | | const int protocol = (j == 0) ? IPPROTO_TCP : |
1007 | | (j == 1) ? IPPROTO_IP : |
1008 | | IPPROTO_UDP; |
1009 | | rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), |
1010 | | SCMP_CMP(0, SCMP_CMP_EQ, pf), |
1011 | | SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, type), |
1012 | | SCMP_CMP(2, SCMP_CMP_EQ, protocol)); |
1013 | | if (rc) |
1014 | | return rc; |
1015 | | } |
1016 | | } |
1017 | | |
1018 | | #ifdef ENABLE_NSS |
1019 | | rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), |
1020 | | SCMP_CMP(0, SCMP_CMP_EQ, PF_INET), |
1021 | | SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM), |
1022 | | SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP)); |
1023 | | if (rc) |
1024 | | return rc; |
1025 | | #endif /* defined(ENABLE_NSS) */ |
1026 | | |
1027 | | rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), |
1028 | | SCMP_CMP(0, SCMP_CMP_EQ, PF_UNIX), |
1029 | | SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM), |
1030 | | SCMP_CMP(2, SCMP_CMP_EQ, 0)); |
1031 | | if (rc) |
1032 | | return rc; |
1033 | | |
1034 | | rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), |
1035 | | SCMP_CMP(0, SCMP_CMP_EQ, PF_UNIX), |
1036 | | SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_DGRAM), |
1037 | | SCMP_CMP(2, SCMP_CMP_EQ, 0)); |
1038 | | if (rc) |
1039 | | return rc; |
1040 | | |
1041 | | rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), |
1042 | | SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK), |
1043 | | SCMP_CMP_MASKED(1, SOCK_CLOEXEC, SOCK_RAW), |
1044 | | SCMP_CMP(2, SCMP_CMP_EQ, 0)); |
1045 | | if (rc) |
1046 | | return rc; |
1047 | | |
1048 | | return 0; |
1049 | | } |
1050 | | |
1051 | | /** |
1052 | | * Function responsible for setting up the socketpair syscall for |
1053 | | * the seccomp filter sandbox. |
1054 | | */ |
1055 | | static int |
1056 | | sb_socketpair(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1057 | | { |
1058 | | int rc = 0; |
1059 | | (void) filter; |
1060 | | |
1061 | | #ifdef __i386__ |
1062 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair)); |
1063 | | if (rc) |
1064 | | return rc; |
1065 | | #endif |
1066 | | |
1067 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), |
1068 | | SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE), |
1069 | | SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC)); |
1070 | | if (rc) |
1071 | | return rc; |
1072 | | |
1073 | | return 0; |
1074 | | } |
1075 | | |
1076 | | #ifdef HAVE_KIST_SUPPORT |
1077 | | |
1078 | | #include <linux/sockios.h> |
1079 | | |
1080 | | static int |
1081 | | sb_ioctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1082 | | { |
1083 | | int rc; |
1084 | | (void) filter; |
1085 | | |
1086 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), |
1087 | | SCMP_CMP(1, SCMP_CMP_EQ, SIOCOUTQNSD)); |
1088 | | if (rc) |
1089 | | return rc; |
1090 | | return 0; |
1091 | | } |
1092 | | |
1093 | | #endif /* defined(HAVE_KIST_SUPPORT) */ |
1094 | | |
1095 | | /** |
1096 | | * Function responsible for setting up the setsockopt syscall for |
1097 | | * the seccomp filter sandbox. |
1098 | | */ |
1099 | | static int |
1100 | | sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1101 | | { |
1102 | | int rc = 0; |
1103 | | (void) filter; |
1104 | | |
1105 | | #ifdef __i386__ |
1106 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt)); |
1107 | | if (rc) |
1108 | | return rc; |
1109 | | #endif |
1110 | | |
1111 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), |
1112 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), |
1113 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_REUSEADDR)); |
1114 | | if (rc) |
1115 | | return rc; |
1116 | | |
1117 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), |
1118 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), |
1119 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF)); |
1120 | | if (rc) |
1121 | | return rc; |
1122 | | |
1123 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), |
1124 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), |
1125 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_RCVBUF)); |
1126 | | if (rc) |
1127 | | return rc; |
1128 | | |
1129 | | #ifdef HAVE_SYSTEMD |
1130 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), |
1131 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), |
1132 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUFFORCE)); |
1133 | | if (rc) |
1134 | | return rc; |
1135 | | #endif /* defined(HAVE_SYSTEMD) */ |
1136 | | |
1137 | | #ifdef IP_TRANSPARENT |
1138 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), |
1139 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), |
1140 | | SCMP_CMP(2, SCMP_CMP_EQ, IP_TRANSPARENT)); |
1141 | | if (rc) |
1142 | | return rc; |
1143 | | #endif /* defined(IP_TRANSPARENT) */ |
1144 | | |
1145 | | #ifdef IPV6_V6ONLY |
1146 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), |
1147 | | SCMP_CMP(1, SCMP_CMP_EQ, IPPROTO_IPV6), |
1148 | | SCMP_CMP(2, SCMP_CMP_EQ, IPV6_V6ONLY)); |
1149 | | if (rc) |
1150 | | return rc; |
1151 | | #endif /* defined(IPV6_V6ONLY) */ |
1152 | | |
1153 | | #ifdef IP_BIND_ADDRESS_NO_PORT |
1154 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), |
1155 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), |
1156 | | SCMP_CMP(2, SCMP_CMP_EQ, IP_BIND_ADDRESS_NO_PORT)); |
1157 | | if (rc) |
1158 | | return rc; |
1159 | | #endif |
1160 | | |
1161 | | return 0; |
1162 | | } |
1163 | | |
1164 | | /** |
1165 | | * Function responsible for setting up the getsockopt syscall for |
1166 | | * the seccomp filter sandbox. |
1167 | | */ |
1168 | | static int |
1169 | | sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1170 | | { |
1171 | | int rc = 0; |
1172 | | (void) filter; |
1173 | | |
1174 | | #ifdef __i386__ |
1175 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt)); |
1176 | | if (rc) |
1177 | | return rc; |
1178 | | #endif |
1179 | | |
1180 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), |
1181 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), |
1182 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_ERROR)); |
1183 | | if (rc) |
1184 | | return rc; |
1185 | | |
1186 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), |
1187 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), |
1188 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_ACCEPTCONN)); |
1189 | | if (rc) |
1190 | | return rc; |
1191 | | |
1192 | | #ifdef HAVE_SYSTEMD |
1193 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), |
1194 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET), |
1195 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF)); |
1196 | | if (rc) |
1197 | | return rc; |
1198 | | #endif /* defined(HAVE_SYSTEMD) */ |
1199 | | |
1200 | | #ifdef HAVE_LINUX_NETFILTER_IPV4_H |
1201 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), |
1202 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP), |
1203 | | SCMP_CMP(2, SCMP_CMP_EQ, SO_ORIGINAL_DST)); |
1204 | | if (rc) |
1205 | | return rc; |
1206 | | #endif /* defined(HAVE_LINUX_NETFILTER_IPV4_H) */ |
1207 | | |
1208 | | #ifdef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H |
1209 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), |
1210 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_IPV6), |
1211 | | SCMP_CMP(2, SCMP_CMP_EQ, IP6T_SO_ORIGINAL_DST)); |
1212 | | if (rc) |
1213 | | return rc; |
1214 | | #endif /* defined(HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H) */ |
1215 | | |
1216 | | #ifdef HAVE_KIST_SUPPORT |
1217 | | #include <netinet/tcp.h> |
1218 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), |
1219 | | SCMP_CMP(1, SCMP_CMP_EQ, SOL_TCP), |
1220 | | SCMP_CMP(2, SCMP_CMP_EQ, TCP_INFO)); |
1221 | | if (rc) |
1222 | | return rc; |
1223 | | #endif /* defined(HAVE_KIST_SUPPORT) */ |
1224 | | |
1225 | | return 0; |
1226 | | } |
1227 | | |
1228 | | #ifdef __NR_fcntl64 |
1229 | | /** |
1230 | | * Function responsible for setting up the fcntl64 syscall for |
1231 | | * the seccomp filter sandbox. |
1232 | | */ |
1233 | | static int |
1234 | | sb_fcntl64(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1235 | | { |
1236 | | int rc = 0; |
1237 | | (void) filter; |
1238 | | |
1239 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), |
1240 | | SCMP_CMP(1, SCMP_CMP_EQ, F_GETFL)); |
1241 | | if (rc) |
1242 | | return rc; |
1243 | | |
1244 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), |
1245 | | SCMP_CMP(1, SCMP_CMP_EQ, F_SETFL), |
1246 | | SCMP_CMP(2, SCMP_CMP_EQ, O_RDWR|O_NONBLOCK)); |
1247 | | if (rc) |
1248 | | return rc; |
1249 | | |
1250 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), |
1251 | | SCMP_CMP(1, SCMP_CMP_EQ, F_GETFD)); |
1252 | | if (rc) |
1253 | | return rc; |
1254 | | |
1255 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), |
1256 | | SCMP_CMP(1, SCMP_CMP_EQ, F_SETFD), |
1257 | | SCMP_CMP(2, SCMP_CMP_EQ, FD_CLOEXEC)); |
1258 | | if (rc) |
1259 | | return rc; |
1260 | | |
1261 | | return 0; |
1262 | | } |
1263 | | #endif /* defined(__NR_fcntl64) */ |
1264 | | |
1265 | | /** |
1266 | | * Function responsible for setting up the epoll_ctl syscall for |
1267 | | * the seccomp filter sandbox. |
1268 | | * |
1269 | | * Note: basically allows everything but will keep for now.. |
1270 | | */ |
1271 | | static int |
1272 | | sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1273 | | { |
1274 | | int rc = 0; |
1275 | | (void) filter; |
1276 | | |
1277 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), |
1278 | | SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_ADD)); |
1279 | | if (rc) |
1280 | | return rc; |
1281 | | |
1282 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), |
1283 | | SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_MOD)); |
1284 | | if (rc) |
1285 | | return rc; |
1286 | | |
1287 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), |
1288 | | SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_DEL)); |
1289 | | if (rc) |
1290 | | return rc; |
1291 | | |
1292 | | return 0; |
1293 | | } |
1294 | | |
1295 | | /** |
1296 | | * Function responsible for setting up the prctl syscall for |
1297 | | * the seccomp filter sandbox. |
1298 | | * |
1299 | | * NOTE: if multiple filters need to be added, the PR_SECCOMP parameter needs |
1300 | | * to be allowlisted in this function. |
1301 | | */ |
1302 | | static int |
1303 | | sb_prctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1304 | | { |
1305 | | int rc = 0; |
1306 | | (void) filter; |
1307 | | |
1308 | | #ifdef ENABLE_FRAGILE_HARDENING |
1309 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), |
1310 | | SCMP_CMP(0, SCMP_CMP_EQ, PR_GET_DUMPABLE)); |
1311 | | if (rc) |
1312 | | return rc; |
1313 | | |
1314 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), |
1315 | | SCMP_CMP(0, SCMP_CMP_EQ, PR_SET_PTRACER)); |
1316 | | if (rc) |
1317 | | return rc; |
1318 | | #endif |
1319 | | |
1320 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), |
1321 | | SCMP_CMP(0, SCMP_CMP_EQ, PR_SET_DUMPABLE)); |
1322 | | if (rc) |
1323 | | return rc; |
1324 | | |
1325 | | return 0; |
1326 | | } |
1327 | | |
1328 | | /** |
1329 | | * Function responsible for setting up the mprotect syscall for |
1330 | | * the seccomp filter sandbox. |
1331 | | * |
1332 | | * NOTE: does not NEED to be here.. currently only occurs before filter; will |
1333 | | * keep just in case for the future. |
1334 | | */ |
1335 | | static int |
1336 | | sb_mprotect(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1337 | | { |
1338 | | int rc = 0; |
1339 | | (void) filter; |
1340 | | |
1341 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), |
1342 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ)); |
1343 | | if (rc) |
1344 | | return rc; |
1345 | | |
1346 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), |
1347 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE)); |
1348 | | if (rc) |
1349 | | return rc; |
1350 | | |
1351 | | return 0; |
1352 | | } |
1353 | | |
1354 | | /** |
1355 | | * Function responsible for setting up the rt_sigprocmask syscall for |
1356 | | * the seccomp filter sandbox. |
1357 | | */ |
1358 | | static int |
1359 | | sb_rt_sigprocmask(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1360 | | { |
1361 | | int rc = 0; |
1362 | | (void) filter; |
1363 | | |
1364 | | #if defined(ENABLE_FRAGILE_HARDENING) || \ |
1365 | | defined(USE_TRACING_INSTRUMENTATION_LTTNG) |
1366 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), |
1367 | | SCMP_CMP(0, SCMP_CMP_EQ, SIG_BLOCK)); |
1368 | | if (rc) |
1369 | | return rc; |
1370 | | #endif |
1371 | | |
1372 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), |
1373 | | SCMP_CMP(0, SCMP_CMP_EQ, SIG_UNBLOCK)); |
1374 | | if (rc) |
1375 | | return rc; |
1376 | | |
1377 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), |
1378 | | SCMP_CMP(0, SCMP_CMP_EQ, SIG_SETMASK)); |
1379 | | if (rc) |
1380 | | return rc; |
1381 | | |
1382 | | return 0; |
1383 | | } |
1384 | | |
1385 | | /** |
1386 | | * Function responsible for setting up the flock syscall for |
1387 | | * the seccomp filter sandbox. |
1388 | | * |
1389 | | * NOTE: does not need to be here, occurs before filter is applied. |
1390 | | */ |
1391 | | static int |
1392 | | sb_flock(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1393 | | { |
1394 | | int rc = 0; |
1395 | | (void) filter; |
1396 | | |
1397 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), |
1398 | | SCMP_CMP(1, SCMP_CMP_EQ, LOCK_EX|LOCK_NB)); |
1399 | | if (rc) |
1400 | | return rc; |
1401 | | |
1402 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), |
1403 | | SCMP_CMP(1, SCMP_CMP_EQ, LOCK_UN)); |
1404 | | if (rc) |
1405 | | return rc; |
1406 | | |
1407 | | return 0; |
1408 | | } |
1409 | | |
1410 | | /** |
1411 | | * Function responsible for setting up the futex syscall for |
1412 | | * the seccomp filter sandbox. |
1413 | | */ |
1414 | | static int |
1415 | | sb_futex(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1416 | | { |
1417 | | int rc = 0; |
1418 | | (void) filter; |
1419 | | |
1420 | | // can remove |
1421 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), |
1422 | | SCMP_CMP(1, SCMP_CMP_EQ, |
1423 | | FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME)); |
1424 | | if (rc) |
1425 | | return rc; |
1426 | | |
1427 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), |
1428 | | SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAKE_PRIVATE)); |
1429 | | if (rc) |
1430 | | return rc; |
1431 | | |
1432 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), |
1433 | | SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAIT_PRIVATE)); |
1434 | | if (rc) |
1435 | | return rc; |
1436 | | |
1437 | | return 0; |
1438 | | } |
1439 | | |
1440 | | /** |
1441 | | * Function responsible for setting up the mremap syscall for |
1442 | | * the seccomp filter sandbox. |
1443 | | * |
1444 | | * NOTE: so far only occurs before filter is applied. |
1445 | | */ |
1446 | | static int |
1447 | | sb_mremap(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1448 | | { |
1449 | | int rc = 0; |
1450 | | (void) filter; |
1451 | | |
1452 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), |
1453 | | SCMP_CMP(3, SCMP_CMP_EQ, MREMAP_MAYMOVE)); |
1454 | | if (rc) |
1455 | | return rc; |
1456 | | |
1457 | | return 0; |
1458 | | } |
1459 | | |
1460 | | #ifdef ARCH_USES_GENERIC_SYSCALLS |
1461 | | /** |
1462 | | * Function responsible for setting up the newfstatat syscall for |
1463 | | * the seccomp filter sandbox. |
1464 | | */ |
1465 | | static int |
1466 | | sb_newfstatat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1467 | | { |
1468 | | int rc = 0; |
1469 | | |
1470 | | sandbox_cfg_t *elem = NULL; |
1471 | | |
1472 | | // for each dynamic parameter filters |
1473 | | for (elem = filter; elem != NULL; elem = elem->next) { |
1474 | | smp_param_t *param = elem->param; |
1475 | | |
1476 | | if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open) |
1477 | | || param->syscall == PHONY_OPENDIR_SYSCALL |
1478 | | || param->syscall == SCMP_SYS(newfstatat))) { |
1479 | | rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat), |
1480 | | SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), |
1481 | | SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value)); |
1482 | | if (rc != 0) { |
1483 | | log_err(LD_BUG,"(Sandbox) failed to add newfstatat syscall, received " |
1484 | | "libseccomp error %d", rc); |
1485 | | return rc; |
1486 | | } |
1487 | | } |
1488 | | } |
1489 | | |
1490 | | return 0; |
1491 | | } |
1492 | | #endif /* defined(ARCH_USES_GENERIC_SYSCALLS) */ |
1493 | | |
1494 | | #ifdef __NR_stat64 |
1495 | | /** |
1496 | | * Function responsible for setting up the stat64 syscall for |
1497 | | * the seccomp filter sandbox. |
1498 | | */ |
1499 | | static int |
1500 | | sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1501 | | { |
1502 | | int rc = 0; |
1503 | | sandbox_cfg_t *elem = NULL; |
1504 | | |
1505 | | // for each dynamic parameter filters |
1506 | | for (elem = filter; elem != NULL; elem = elem->next) { |
1507 | | smp_param_t *param = elem->param; |
1508 | | |
1509 | | if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open) |
1510 | | || param->syscall == SCMP_SYS(stat64))) { |
1511 | | rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64), |
1512 | | SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value)); |
1513 | | if (rc != 0) { |
1514 | | log_err(LD_BUG,"(Sandbox) failed to add stat64 syscall, received " |
1515 | | "libseccomp error %d", rc); |
1516 | | return rc; |
1517 | | } |
1518 | | } |
1519 | | } |
1520 | | |
1521 | | return 0; |
1522 | | } |
1523 | | #endif /* defined(__NR_stat64) */ |
1524 | | |
1525 | | static int |
1526 | | sb_kill(scmp_filter_ctx ctx, sandbox_cfg_t *filter) |
1527 | | { |
1528 | | (void) filter; |
1529 | | #ifdef __NR_kill |
1530 | | /* Allow killing anything with signal 0 -- it isn't really a kill. */ |
1531 | | return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kill), |
1532 | | SCMP_CMP(1, SCMP_CMP_EQ, 0)); |
1533 | | #else |
1534 | | return 0; |
1535 | | #endif /* defined(__NR_kill) */ |
1536 | | } |
1537 | | |
1538 | | /** |
1539 | | * Array of function pointers responsible for filtering different syscalls at |
1540 | | * a parameter level. |
1541 | | */ |
1542 | | static sandbox_filter_func_t filter_func[] = { |
1543 | | sb_rt_sigaction, |
1544 | | sb_rt_sigprocmask, |
1545 | | #ifdef __NR_time |
1546 | | sb_time, |
1547 | | #endif |
1548 | | sb_accept4, |
1549 | | #ifdef __NR_mmap2 |
1550 | | sb_mmap2, |
1551 | | #endif |
1552 | | #if defined(ARCH_USES_GENERIC_SYSCALLS) |
1553 | | sb_fchownat, |
1554 | | #elif defined(__i386__) |
1555 | | sb_chown32, |
1556 | | #else |
1557 | | sb_chown, |
1558 | | #endif |
1559 | | #if defined(ARCH_USES_GENERIC_SYSCALLS) |
1560 | | sb_fchmodat, |
1561 | | #else |
1562 | | sb_chmod, |
1563 | | #endif |
1564 | | sb_open, |
1565 | | #if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) |
1566 | | sb_openat, |
1567 | | sb_opendir, |
1568 | | #endif |
1569 | | #ifdef ENABLE_FRAGILE_HARDENING |
1570 | | sb_ptrace, |
1571 | | #endif |
1572 | | #if defined(__NR_rename) |
1573 | | sb_rename, |
1574 | | #elif defined(__NR_renameat) |
1575 | | sb_renameat, |
1576 | | #else |
1577 | | sb_renameat2, |
1578 | | #endif |
1579 | | #ifdef __NR_fcntl64 |
1580 | | sb_fcntl64, |
1581 | | #endif |
1582 | | sb_epoll_ctl, |
1583 | | sb_prctl, |
1584 | | sb_mprotect, |
1585 | | sb_flock, |
1586 | | sb_futex, |
1587 | | sb_mremap, |
1588 | | #if defined(ARCH_USES_GENERIC_SYSCALLS) |
1589 | | sb_newfstatat, |
1590 | | #elif defined(__NR_stat64) |
1591 | | sb_stat64, |
1592 | | #endif |
1593 | | |
1594 | | sb_socket, |
1595 | | sb_setsockopt, |
1596 | | sb_getsockopt, |
1597 | | sb_socketpair, |
1598 | | #ifdef HAVE_KIST_SUPPORT |
1599 | | sb_ioctl, |
1600 | | #endif |
1601 | | sb_kill |
1602 | | }; |
1603 | | |
1604 | | /** |
1605 | | * Return the interned (and hopefully sandbox-permitted) string equal |
1606 | | * to @a str. |
1607 | | * |
1608 | | * Return NULL if `str` is NULL, or `str` is not an interned string. |
1609 | | **/ |
1610 | | const char * |
1611 | | sandbox_intern_string(const char *str) |
1612 | | { |
1613 | | const char *interned = sandbox_get_interned_string(str); |
1614 | | |
1615 | | if (sandbox_active && str != NULL && interned == NULL) { |
1616 | | log_warn(LD_BUG, "No interned sandbox parameter found for %s", str); |
1617 | | } |
1618 | | |
1619 | | return interned ? interned : str; |
1620 | | } |
1621 | | |
1622 | | /** |
1623 | | * Return true if the sandbox is running and we are missing an interned string |
1624 | | * equal to @a str. |
1625 | | */ |
1626 | | bool |
1627 | | sandbox_interned_string_is_missing(const char *str) |
1628 | | { |
1629 | | return sandbox_active && sandbox_get_interned_string(str) == NULL; |
1630 | | } |
1631 | | |
1632 | | /** |
1633 | | * Try to find and return the interned string equal to @a str. |
1634 | | * |
1635 | | * If there is no such string, return NULL. |
1636 | | **/ |
1637 | | static const char * |
1638 | | sandbox_get_interned_string(const char *str) |
1639 | | { |
1640 | | sandbox_cfg_t *elem; |
1641 | | |
1642 | | if (str == NULL) |
1643 | | return NULL; |
1644 | | |
1645 | | for (elem = filter_dynamic; elem != NULL; elem = elem->next) { |
1646 | | smp_param_t *param = elem->param; |
1647 | | |
1648 | | if (param->prot) { |
1649 | | if (!strcmp(str, (char*)(param->value))) { |
1650 | | return (char*)param->value; |
1651 | | } |
1652 | | if (param->value2 && !strcmp(str, (char*)param->value2)) { |
1653 | | return (char*)param->value2; |
1654 | | } |
1655 | | } |
1656 | | } |
1657 | | |
1658 | | return NULL; |
1659 | | } |
1660 | | |
1661 | | /* DOCDOC */ |
1662 | | static int |
1663 | | prot_strings_helper(strmap_t *locations, |
1664 | | char **pr_mem_next_p, |
1665 | | size_t *pr_mem_left_p, |
1666 | | char **value_p) |
1667 | | { |
1668 | | char *param_val; |
1669 | | size_t param_size; |
1670 | | void *location; |
1671 | | |
1672 | | if (*value_p == 0) |
1673 | | return 0; |
1674 | | |
1675 | | param_val = (char*) *value_p; |
1676 | | param_size = strlen(param_val) + 1; |
1677 | | location = strmap_get(locations, param_val); |
1678 | | |
1679 | | if (location) { |
1680 | | // We already interned this string. |
1681 | | tor_free(param_val); |
1682 | | *value_p = location; |
1683 | | return 0; |
1684 | | } else if (*pr_mem_left_p >= param_size) { |
1685 | | // copy to protected |
1686 | | location = *pr_mem_next_p; |
1687 | | memcpy(location, param_val, param_size); |
1688 | | |
1689 | | // re-point el parameter to protected |
1690 | | tor_free(param_val); |
1691 | | *value_p = location; |
1692 | | |
1693 | | strmap_set(locations, location, location); /* good real estate advice */ |
1694 | | |
1695 | | // move next available protected memory |
1696 | | *pr_mem_next_p += param_size; |
1697 | | *pr_mem_left_p -= param_size; |
1698 | | return 0; |
1699 | | } else { |
1700 | | log_err(LD_BUG,"(Sandbox) insufficient protected memory!"); |
1701 | | return -1; |
1702 | | } |
1703 | | } |
1704 | | |
1705 | | /** |
1706 | | * Protects all the strings in the sandbox's parameter list configuration. It |
1707 | | * works by calculating the total amount of memory required by the parameter |
1708 | | * list, allocating the memory using mmap, and protecting it from writes with |
1709 | | * mprotect(). |
1710 | | */ |
1711 | | static int |
1712 | | prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg) |
1713 | | { |
1714 | | int ret = 0; |
1715 | | size_t pr_mem_size = 0, pr_mem_left = 0; |
1716 | | char *pr_mem_next = NULL, *pr_mem_base; |
1717 | | sandbox_cfg_t *el = NULL; |
1718 | | strmap_t *locations = NULL; |
1719 | | |
1720 | | // get total number of bytes required to mmap. (Overestimate.) |
1721 | | for (el = cfg; el != NULL; el = el->next) { |
1722 | | pr_mem_size += strlen((char*) el->param->value) + 1; |
1723 | | if (el->param->value2) |
1724 | | pr_mem_size += strlen((char*) el->param->value2) + 1; |
1725 | | } |
1726 | | |
1727 | | // allocate protected memory with MALLOC_MP_LIM canary |
1728 | | pr_mem_base = (char*) mmap(NULL, MALLOC_MP_LIM + pr_mem_size, |
1729 | | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
1730 | | if (pr_mem_base == MAP_FAILED) { |
1731 | | log_err(LD_BUG,"(Sandbox) failed allocate protected memory! mmap: %s", |
1732 | | strerror(errno)); |
1733 | | ret = -1; |
1734 | | goto out; |
1735 | | } |
1736 | | |
1737 | | pr_mem_next = pr_mem_base + MALLOC_MP_LIM; |
1738 | | pr_mem_left = pr_mem_size; |
1739 | | |
1740 | | locations = strmap_new(); |
1741 | | |
1742 | | // change el value pointer to protected |
1743 | | for (el = cfg; el != NULL; el = el->next) { |
1744 | | if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left, |
1745 | | &el->param->value) < 0) { |
1746 | | ret = -2; |
1747 | | goto out; |
1748 | | } |
1749 | | if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left, |
1750 | | &el->param->value2) < 0) { |
1751 | | ret = -2; |
1752 | | goto out; |
1753 | | } |
1754 | | el->param->prot = 1; |
1755 | | } |
1756 | | |
1757 | | // protecting from writes |
1758 | | if (mprotect(pr_mem_base, MALLOC_MP_LIM + pr_mem_size, PROT_READ)) { |
1759 | | log_err(LD_BUG,"(Sandbox) failed to protect memory! mprotect: %s", |
1760 | | strerror(errno)); |
1761 | | ret = -3; |
1762 | | goto out; |
1763 | | } |
1764 | | |
1765 | | /* |
1766 | | * Setting sandbox restrictions so the string memory cannot be tampered with |
1767 | | */ |
1768 | | // no mremap of the protected base address |
1769 | | ret = seccomp_rule_add_1(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap), |
1770 | | SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); |
1771 | | if (ret) { |
1772 | | log_err(LD_BUG,"(Sandbox) mremap protected memory filter fail!"); |
1773 | | goto out; |
1774 | | } |
1775 | | |
1776 | | // no munmap of the protected base address |
1777 | | ret = seccomp_rule_add_1(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap), |
1778 | | SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base)); |
1779 | | if (ret) { |
1780 | | log_err(LD_BUG,"(Sandbox) munmap protected memory filter fail!"); |
1781 | | goto out; |
1782 | | } |
1783 | | |
1784 | | /* |
1785 | | * Allow mprotect with PROT_READ|PROT_WRITE because openssl uses it, but |
1786 | | * never over the memory region used by the protected strings. |
1787 | | * |
1788 | | * PROT_READ|PROT_WRITE was originally fully allowed in sb_mprotect(), but |
1789 | | * had to be removed due to limitation of libseccomp regarding intervals. |
1790 | | * |
1791 | | * There is a restriction on how much you can mprotect with R|W up to the |
1792 | | * size of the canary. |
1793 | | */ |
1794 | | ret = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), |
1795 | | SCMP_CMP(0, SCMP_CMP_LT, (intptr_t) pr_mem_base), |
1796 | | SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), |
1797 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); |
1798 | | if (ret) { |
1799 | | log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (LT)!"); |
1800 | | goto out; |
1801 | | } |
1802 | | |
1803 | | ret = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), |
1804 | | SCMP_CMP(0, SCMP_CMP_GT, (intptr_t) pr_mem_base + pr_mem_size + |
1805 | | MALLOC_MP_LIM), |
1806 | | SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM), |
1807 | | SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE)); |
1808 | | if (ret) { |
1809 | | log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (GT)!"); |
1810 | | goto out; |
1811 | | } |
1812 | | |
1813 | | out: |
1814 | | strmap_free(locations, NULL); |
1815 | | return ret; |
1816 | | } |
1817 | | |
1818 | | /** |
1819 | | * Auxiliary function used in order to allocate a sandbox_cfg_t element and set |
1820 | | * its values according the parameter list. All elements are initialised |
1821 | | * with the 'prot' field set to false, as the pointer is not protected at this |
1822 | | * point. |
1823 | | */ |
1824 | | static sandbox_cfg_t* |
1825 | | new_element2(int syscall, char *value, char *value2) |
1826 | | { |
1827 | | smp_param_t *param = NULL; |
1828 | | |
1829 | | sandbox_cfg_t *elem = tor_malloc_zero(sizeof(sandbox_cfg_t)); |
1830 | | param = elem->param = tor_malloc_zero(sizeof(smp_param_t)); |
1831 | | |
1832 | | param->syscall = syscall; |
1833 | | param->value = value; |
1834 | | param->value2 = value2; |
1835 | | param->prot = 0; |
1836 | | |
1837 | | return elem; |
1838 | | } |
1839 | | |
1840 | | static sandbox_cfg_t* |
1841 | | new_element(int syscall, char *value) |
1842 | | { |
1843 | | return new_element2(syscall, value, NULL); |
1844 | | } |
1845 | | |
1846 | | #if defined(ARCH_USES_GENERIC_SYSCALLS) |
1847 | | #define SCMP_chown SCMP_SYS(fchownat) |
1848 | | #elif defined(__i386__) |
1849 | | #define SCMP_chown SCMP_SYS(chown32) |
1850 | | #else |
1851 | | #define SCMP_chown SCMP_SYS(chown) |
1852 | | #endif |
1853 | | |
1854 | | #if defined(ARCH_USES_GENERIC_SYSCALLS) |
1855 | | #define SCMP_chmod SCMP_SYS(fchmodat) |
1856 | | #else |
1857 | | #define SCMP_chmod SCMP_SYS(chmod) |
1858 | | #endif |
1859 | | |
1860 | | #if defined(__NR_rename) |
1861 | | #define SCMP_rename SCMP_SYS(rename) |
1862 | | #elif defined(__NR_renameat) |
1863 | | #define SCMP_rename SCMP_SYS(renameat) |
1864 | | #else |
1865 | | #define SCMP_rename SCMP_SYS(renameat2) |
1866 | | #endif |
1867 | | |
1868 | | #if defined(ARCH_USES_GENERIC_SYSCALLS) |
1869 | | #define SCMP_stat SCMP_SYS(newfstatat) |
1870 | | #elif defined(__NR_stat64) |
1871 | | #define SCMP_stat SCMP_SYS(stat64) |
1872 | | #else |
1873 | | #define SCMP_stat SCMP_SYS(stat) |
1874 | | #endif |
1875 | | |
1876 | | int |
1877 | | sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file) |
1878 | | { |
1879 | | sandbox_cfg_t *elem = NULL; |
1880 | | |
1881 | | elem = new_element(SCMP_stat, file); |
1882 | | |
1883 | | elem->next = *cfg; |
1884 | | *cfg = elem; |
1885 | | |
1886 | | return 0; |
1887 | | } |
1888 | | |
1889 | | int |
1890 | | sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file) |
1891 | | { |
1892 | | sandbox_cfg_t *elem = NULL; |
1893 | | |
1894 | | elem = new_element(SCMP_SYS(open), file); |
1895 | | |
1896 | | elem->next = *cfg; |
1897 | | *cfg = elem; |
1898 | | |
1899 | | return 0; |
1900 | | } |
1901 | | |
1902 | | int |
1903 | | sandbox_cfg_allow_chmod_filename(sandbox_cfg_t **cfg, char *file) |
1904 | | { |
1905 | | sandbox_cfg_t *elem = NULL; |
1906 | | |
1907 | | elem = new_element(SCMP_chmod, file); |
1908 | | |
1909 | | elem->next = *cfg; |
1910 | | *cfg = elem; |
1911 | | |
1912 | | return 0; |
1913 | | } |
1914 | | |
1915 | | int |
1916 | | sandbox_cfg_allow_chown_filename(sandbox_cfg_t **cfg, char *file) |
1917 | | { |
1918 | | sandbox_cfg_t *elem = NULL; |
1919 | | |
1920 | | elem = new_element(SCMP_chown, file); |
1921 | | |
1922 | | elem->next = *cfg; |
1923 | | *cfg = elem; |
1924 | | |
1925 | | return 0; |
1926 | | } |
1927 | | |
1928 | | int |
1929 | | sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2) |
1930 | | { |
1931 | | sandbox_cfg_t *elem = NULL; |
1932 | | |
1933 | | elem = new_element2(SCMP_rename, file1, file2); |
1934 | | |
1935 | | elem->next = *cfg; |
1936 | | *cfg = elem; |
1937 | | |
1938 | | return 0; |
1939 | | } |
1940 | | |
1941 | | int |
1942 | | sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file) |
1943 | | { |
1944 | | sandbox_cfg_t *elem = NULL; |
1945 | | |
1946 | | elem = new_element(SCMP_SYS(openat), file); |
1947 | | |
1948 | | elem->next = *cfg; |
1949 | | *cfg = elem; |
1950 | | |
1951 | | return 0; |
1952 | | } |
1953 | | |
1954 | | int |
1955 | | sandbox_cfg_allow_opendir_dirname(sandbox_cfg_t **cfg, char *dir) |
1956 | | { |
1957 | | sandbox_cfg_t *elem = NULL; |
1958 | | |
1959 | | elem = new_element(PHONY_OPENDIR_SYSCALL, dir); |
1960 | | |
1961 | | elem->next = *cfg; |
1962 | | *cfg = elem; |
1963 | | |
1964 | | return 0; |
1965 | | } |
1966 | | |
1967 | | /** |
1968 | | * Function responsible for going through the parameter syscall filters and |
1969 | | * call each function pointer in the list. |
1970 | | */ |
1971 | | static int |
1972 | | add_param_filter(scmp_filter_ctx ctx, sandbox_cfg_t* cfg) |
1973 | | { |
1974 | | unsigned i; |
1975 | | int rc = 0; |
1976 | | |
1977 | | // function pointer |
1978 | | for (i = 0; i < ARRAY_LENGTH(filter_func); i++) { |
1979 | | rc = filter_func[i](ctx, cfg); |
1980 | | if (rc) { |
1981 | | log_err(LD_BUG,"(Sandbox) failed to add syscall %d, received libseccomp " |
1982 | | "error %d", i, rc); |
1983 | | return rc; |
1984 | | } |
1985 | | } |
1986 | | |
1987 | | return 0; |
1988 | | } |
1989 | | |
1990 | | /** |
1991 | | * Function responsible of loading the libseccomp syscall filters which do not |
1992 | | * have parameter filtering. |
1993 | | */ |
1994 | | static int |
1995 | | add_noparam_filter(scmp_filter_ctx ctx) |
1996 | | { |
1997 | | unsigned i; |
1998 | | int rc = 0; |
1999 | | |
2000 | | // add general filters |
2001 | | for (i = 0; i < ARRAY_LENGTH(filter_nopar_gen); i++) { |
2002 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, filter_nopar_gen[i]); |
2003 | | if (rc != 0) { |
2004 | | log_err(LD_BUG,"(Sandbox) failed to add syscall index %d (NR=%d), " |
2005 | | "received libseccomp error %d", i, filter_nopar_gen[i], rc); |
2006 | | return rc; |
2007 | | } |
2008 | | } |
2009 | | |
2010 | | if (is_libc_at_least(2, 33)) { |
2011 | | #ifdef __NR_newfstatat |
2012 | | // Libc 2.33 uses this syscall to implement both fstat() and stat(). |
2013 | | // |
2014 | | // The trouble is that to implement fstat(fd, &st), it calls: |
2015 | | // newfstatat(fs, "", &st, AT_EMPTY_PATH) |
2016 | | // We can't detect this usage in particular, because "" is a pointer |
2017 | | // we don't control. And we can't just look for AT_EMPTY_PATH, since |
2018 | | // AT_EMPTY_PATH only has effect when the path string is empty. |
2019 | | // |
2020 | | // So our only solution seems to be allowing all fstatat calls, which |
2021 | | // means that an attacker can stat() anything on the filesystem. That's |
2022 | | // not a great solution, but I can't find a better one. |
2023 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat)); |
2024 | | if (rc != 0) { |
2025 | | log_err(LD_BUG,"(Sandbox) failed to add newfstatat() syscall; " |
2026 | | "received libseccomp error %d", rc); |
2027 | | return rc; |
2028 | | } |
2029 | | #elif defined(__NR_fstatat64) |
2030 | | // On i386, glibc uses fstatat64 instead of newfstatat. |
2031 | | // This is needed for glob() and stat() operations on 32-bit systems. |
2032 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstatat64)); |
2033 | | if (rc != 0) { |
2034 | | log_err(LD_BUG,"(Sandbox) failed to add fstatat64() syscall; " |
2035 | | "received libseccomp error %d", rc); |
2036 | | return rc; |
2037 | | } |
2038 | | #endif |
2039 | | #if defined(__i386__) && defined(__NR_statx) |
2040 | | // On i386 with glibc 2.33+, statx may be used for time64 support. |
2041 | | // glob() in glibc 2.36+ uses statx for directory traversal. |
2042 | | rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(statx)); |
2043 | | if (rc != 0) { |
2044 | | log_err(LD_BUG,"(Sandbox) failed to add statx() syscall; " |
2045 | | "received libseccomp error %d", rc); |
2046 | | return rc; |
2047 | | } |
2048 | | #endif |
2049 | | } |
2050 | | |
2051 | | return 0; |
2052 | | } |
2053 | | |
2054 | | /** |
2055 | | * Function responsible for setting up and enabling a global syscall filter. |
2056 | | * The function is a prototype developed for stage 1 of sandboxing Tor. |
2057 | | * Returns 0 on success. |
2058 | | */ |
2059 | | static int |
2060 | | install_syscall_filter(sandbox_cfg_t* cfg) |
2061 | | { |
2062 | | int rc = 0; |
2063 | | scmp_filter_ctx ctx; |
2064 | | |
2065 | | ctx = seccomp_init(SCMP_ACT_ERRNO(EPERM)); |
2066 | | if (ctx == NULL) { |
2067 | | log_err(LD_BUG,"(Sandbox) failed to initialise libseccomp context"); |
2068 | | rc = -1; |
2069 | | goto end; |
2070 | | } |
2071 | | |
2072 | | // protecting sandbox parameter strings |
2073 | | if ((rc = prot_strings(ctx, cfg))) { |
2074 | | goto end; |
2075 | | } |
2076 | | |
2077 | | // add parameter filters |
2078 | | if ((rc = add_param_filter(ctx, cfg))) { |
2079 | | log_err(LD_BUG, "(Sandbox) failed to add param filters!"); |
2080 | | goto end; |
2081 | | } |
2082 | | |
2083 | | // adding filters with no parameters |
2084 | | if ((rc = add_noparam_filter(ctx))) { |
2085 | | log_err(LD_BUG, "(Sandbox) failed to add param filters!"); |
2086 | | goto end; |
2087 | | } |
2088 | | |
2089 | | // loading the seccomp2 filter |
2090 | | if ((rc = seccomp_load(ctx))) { |
2091 | | log_err(LD_BUG, "(Sandbox) failed to load: %d (%s)! " |
2092 | | "Are you sure that your kernel has seccomp2 support? The " |
2093 | | "sandbox won't work without it.", rc, |
2094 | | strerror(-rc)); |
2095 | | goto end; |
2096 | | } |
2097 | | |
2098 | | // marking the sandbox as active |
2099 | | sandbox_active = 1; |
2100 | | |
2101 | | end: |
2102 | | seccomp_release(ctx); |
2103 | | return (rc < 0 ? -rc : rc); |
2104 | | } |
2105 | | |
2106 | | #ifdef SYSCALL_NAME_DEBUGGING |
2107 | | #include "lib/sandbox/linux_syscalls.inc" |
2108 | | |
2109 | | /** Return a string containing the name of a given syscall (if we know it) */ |
2110 | | static const char * |
2111 | | get_syscall_name(int syscall_num) |
2112 | | { |
2113 | | int i; |
2114 | | for (i = 0; SYSCALLS_BY_NUMBER[i].syscall_name; ++i) { |
2115 | | if (SYSCALLS_BY_NUMBER[i].syscall_num == syscall_num) |
2116 | | return SYSCALLS_BY_NUMBER[i].syscall_name; |
2117 | | } |
2118 | | |
2119 | | { |
2120 | | static char syscall_name_buf[64]; |
2121 | | format_dec_number_sigsafe(syscall_num, |
2122 | | syscall_name_buf, sizeof(syscall_name_buf)); |
2123 | | return syscall_name_buf; |
2124 | | } |
2125 | | } |
2126 | | |
2127 | | /** Return the syscall number from a ucontext_t that we got in a signal |
2128 | | * handler (if we know how to do that). */ |
2129 | | static int |
2130 | | get_syscall_from_ucontext(const ucontext_t *ctx) |
2131 | | { |
2132 | | return (int) ctx->uc_mcontext.M_SYSCALL; |
2133 | | } |
2134 | | #else /* !defined(SYSCALL_NAME_DEBUGGING) */ |
2135 | | static const char * |
2136 | | get_syscall_name(int syscall_num) |
2137 | | { |
2138 | | (void) syscall_num; |
2139 | | return "unknown"; |
2140 | | } |
2141 | | static int |
2142 | | get_syscall_from_ucontext(const ucontext_t *ctx) |
2143 | | { |
2144 | | (void) ctx; |
2145 | | return -1; |
2146 | | } |
2147 | | #endif /* defined(SYSCALL_NAME_DEBUGGING) */ |
2148 | | |
2149 | | #ifdef USE_BACKTRACE |
2150 | | #define MAX_DEPTH 256 |
2151 | | static void *syscall_cb_buf[MAX_DEPTH]; |
2152 | | #endif |
2153 | | |
2154 | | /** |
2155 | | * Function called when a SIGSYS is caught by the application. It notifies the |
2156 | | * user that an error has occurred and either terminates or allows the |
2157 | | * application to continue execution, based on the DEBUGGING_CLOSE symbol. |
2158 | | */ |
2159 | | static void |
2160 | | sigsys_debugging(int nr, siginfo_t *info, void *void_context) |
2161 | | { |
2162 | | ucontext_t *ctx = (ucontext_t *) (void_context); |
2163 | | const char *syscall_name; |
2164 | | #ifdef USE_BACKTRACE |
2165 | | size_t depth; |
2166 | | int n_fds, i; |
2167 | | const int *fds = NULL; |
2168 | | #endif |
2169 | | |
2170 | | (void) nr; |
2171 | | |
2172 | | if (info->si_code != SYS_SECCOMP) |
2173 | | return; |
2174 | | |
2175 | | if (!ctx) |
2176 | | return; |
2177 | | |
2178 | | int syscall = get_syscall_from_ucontext(ctx); |
2179 | | |
2180 | | #ifdef USE_BACKTRACE |
2181 | | depth = backtrace(syscall_cb_buf, MAX_DEPTH); |
2182 | | /* Clean up the top stack frame so we get the real function |
2183 | | * name for the most recently failing function. */ |
2184 | | clean_backtrace(syscall_cb_buf, depth, ctx); |
2185 | | #endif /* defined(USE_BACKTRACE) */ |
2186 | | |
2187 | | syscall_name = get_syscall_name(syscall); |
2188 | | |
2189 | | tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ", |
2190 | | syscall_name, |
2191 | | ")\n", |
2192 | | NULL); |
2193 | | |
2194 | | #ifdef USE_BACKTRACE |
2195 | | n_fds = tor_log_get_sigsafe_err_fds(&fds); |
2196 | | for (i=0; i < n_fds; ++i) |
2197 | | backtrace_symbols_fd(syscall_cb_buf, (int)depth, fds[i]); |
2198 | | #endif |
2199 | | |
2200 | | #if defined(DEBUGGING_CLOSE) |
2201 | | _exit(1); // exit ok: programming error has led to sandbox failure. |
2202 | | #endif // DEBUGGING_CLOSE |
2203 | | } |
2204 | | |
2205 | | /** |
2206 | | * Function that adds a handler for SIGSYS, which is the signal thrown |
2207 | | * when the application is issuing a syscall which is not allowed. The |
2208 | | * main purpose of this function is to help with debugging by identifying |
2209 | | * filtered syscalls. |
2210 | | */ |
2211 | | static int |
2212 | | install_sigsys_debugging(void) |
2213 | | { |
2214 | | struct sigaction act; |
2215 | | sigset_t mask; |
2216 | | |
2217 | | memset(&act, 0, sizeof(act)); |
2218 | | sigemptyset(&mask); |
2219 | | sigaddset(&mask, SIGSYS); |
2220 | | |
2221 | | act.sa_sigaction = &sigsys_debugging; |
2222 | | act.sa_flags = SA_SIGINFO; |
2223 | | if (sigaction(SIGSYS, &act, NULL) < 0) { |
2224 | | log_err(LD_BUG,"(Sandbox) Failed to register SIGSYS signal handler"); |
2225 | | return -1; |
2226 | | } |
2227 | | |
2228 | | if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { |
2229 | | log_err(LD_BUG,"(Sandbox) Failed call to sigprocmask()"); |
2230 | | return -2; |
2231 | | } |
2232 | | |
2233 | | return 0; |
2234 | | } |
2235 | | |
2236 | | /** |
2237 | | * Function responsible of registering the sandbox_cfg_t list of parameter |
2238 | | * syscall filters to the existing parameter list. This is used for incipient |
2239 | | * multiple-sandbox support. |
2240 | | */ |
2241 | | static int |
2242 | | register_cfg(sandbox_cfg_t* cfg) |
2243 | | { |
2244 | | sandbox_cfg_t *elem = NULL; |
2245 | | |
2246 | | if (filter_dynamic == NULL) { |
2247 | | filter_dynamic = cfg; |
2248 | | return 0; |
2249 | | } |
2250 | | |
2251 | | for (elem = filter_dynamic; elem->next != NULL; elem = elem->next) |
2252 | | ; |
2253 | | |
2254 | | elem->next = cfg; |
2255 | | |
2256 | | return 0; |
2257 | | } |
2258 | | |
2259 | | #endif /* defined(USE_LIBSECCOMP) */ |
2260 | | |
2261 | | #ifdef USE_LIBSECCOMP |
2262 | | /** |
2263 | | * Initialises the syscall sandbox filter for any linux architecture, taking |
2264 | | * into account various available features for different linux flavours. |
2265 | | */ |
2266 | | static int |
2267 | | initialise_libseccomp_sandbox(sandbox_cfg_t* cfg) |
2268 | | { |
2269 | | /* Prevent glibc from trying to open /dev/tty on fatal error */ |
2270 | | setenv("LIBC_FATAL_STDERR_", "1", 1); |
2271 | | |
2272 | | if (install_sigsys_debugging()) |
2273 | | return -1; |
2274 | | |
2275 | | if (install_syscall_filter(cfg)) |
2276 | | return -2; |
2277 | | |
2278 | | if (register_cfg(cfg)) |
2279 | | return -3; |
2280 | | |
2281 | | return 0; |
2282 | | } |
2283 | | |
2284 | | int |
2285 | | sandbox_is_active(void) |
2286 | | { |
2287 | | return sandbox_active != 0; |
2288 | | } |
2289 | | #endif /* defined(USE_LIBSECCOMP) */ |
2290 | | |
2291 | | sandbox_cfg_t* |
2292 | | sandbox_cfg_new(void) |
2293 | 0 | { |
2294 | 0 | return NULL; |
2295 | 0 | } |
2296 | | |
2297 | | int |
2298 | | sandbox_init(sandbox_cfg_t *cfg) |
2299 | 0 | { |
2300 | | #if defined(USE_LIBSECCOMP) |
2301 | | return initialise_libseccomp_sandbox(cfg); |
2302 | | |
2303 | | #elif defined(__linux__) |
2304 | | (void)cfg; |
2305 | 0 | log_warn(LD_GENERAL, |
2306 | 0 | "This version of Tor was built without support for sandboxing. To " |
2307 | 0 | "build with support for sandboxing on Linux, you must have " |
2308 | 0 | "libseccomp and its necessary header files (e.g. seccomp.h)."); |
2309 | 0 | return 0; |
2310 | |
|
2311 | | #else |
2312 | | (void)cfg; |
2313 | | log_warn(LD_GENERAL, |
2314 | | "Currently, sandboxing is only implemented on Linux. The feature " |
2315 | | "is disabled on your platform."); |
2316 | | return 0; |
2317 | | #endif /* defined(USE_LIBSECCOMP) || ... */ |
2318 | 0 | } |
2319 | | |
2320 | | #ifndef USE_LIBSECCOMP |
2321 | | int |
2322 | | sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file) |
2323 | 0 | { |
2324 | 0 | (void)cfg; (void)file; |
2325 | 0 | return 0; |
2326 | 0 | } |
2327 | | |
2328 | | int |
2329 | | sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file) |
2330 | 0 | { |
2331 | 0 | (void)cfg; (void)file; |
2332 | 0 | return 0; |
2333 | 0 | } |
2334 | | |
2335 | | int |
2336 | | sandbox_cfg_allow_opendir_dirname(sandbox_cfg_t **cfg, char *dir) |
2337 | 0 | { |
2338 | 0 | (void)cfg; (void)dir; |
2339 | 0 | return 0; |
2340 | 0 | } |
2341 | | |
2342 | | int |
2343 | | sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file) |
2344 | 0 | { |
2345 | 0 | (void)cfg; (void)file; |
2346 | 0 | return 0; |
2347 | 0 | } |
2348 | | |
2349 | | int |
2350 | | sandbox_cfg_allow_chown_filename(sandbox_cfg_t **cfg, char *file) |
2351 | 0 | { |
2352 | 0 | (void)cfg; (void)file; |
2353 | 0 | return 0; |
2354 | 0 | } |
2355 | | |
2356 | | int |
2357 | | sandbox_cfg_allow_chmod_filename(sandbox_cfg_t **cfg, char *file) |
2358 | 0 | { |
2359 | 0 | (void)cfg; (void)file; |
2360 | 0 | return 0; |
2361 | 0 | } |
2362 | | |
2363 | | int |
2364 | | sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2) |
2365 | 0 | { |
2366 | 0 | (void)cfg; (void)file1; (void)file2; |
2367 | 0 | return 0; |
2368 | 0 | } |
2369 | | |
2370 | | int |
2371 | | sandbox_is_active(void) |
2372 | 0 | { |
2373 | 0 | return 0; |
2374 | 0 | } |
2375 | | |
2376 | | #endif /* !defined(USE_LIBSECCOMP) */ |