ksgi
/
sandbox-seccomp-filter.c
350 строк · 9.1 Кб
1/* $Id$ */
2/*
3* Copyright (c) 2015 Kristaps Dzonsons <kristaps@bsd.lv>
4*
5* Permission to use, copy, modify, and distribute this software for any
6* purpose with or without fee is hereby granted, provided that the above
7* copyright notice and this permission notice appear in all copies.
8*
9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*/
17#include "config.h"
18
19#if HAVE_SECCOMP_FILTER
20
21/*
22* Copyright (c) 2012 Will Drewry <wad@dataspill.org>
23*
24* Permission to use, copy, modify, and distribute this software for any
25* purpose with or without fee is hereby granted, provided that the above
26* copyright notice and this permission notice appear in all copies.
27*
28* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
29* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
30* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
31* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
32* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
33* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
34* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35*/
36
37#include <sys/types.h>
38#include <sys/resource.h>
39#include <sys/prctl.h>
40
41#include <linux/audit.h>
42#include <linux/filter.h>
43#include <linux/seccomp.h>
44#include <elf.h>
45
46#ifdef AUDIT_ARCH_AARCH64
47#define __ARCH_WANT_SYSCALL_NO_AT
48#define __ARCH_WANT_SYSCALL_DEPRECATED
49#endif
50#include <asm/unistd.h>
51
52#include <errno.h>
53#include <signal.h>
54#include <stdarg.h>
55#include <stddef.h> /* for offsetof */
56#include <stdio.h>
57#include <stdlib.h>
58#include <string.h>
59#include <unistd.h>
60
61#include "kcgi.h"
62#include "extern.h"
63
64/* Linux seccomp_filter sandbox */
65#ifdef SANDBOX_SECCOMP_DEBUG
66# define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP
67#else
68# define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL
69#endif
70
71/* Simple helpers to avoid manual errors (but larger BPF programs). */
72#define SC_DENY(_nr, _errno) \
73BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \
74BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno))
75#define SC_ALLOW(_nr) \
76BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \
77BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
78
79static const struct sock_filter preauth_ctrl[] = {
80/* Ensure the syscall arch convention is as expected. */
81BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
82offsetof(struct seccomp_data, arch)),
83BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0),
84BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
85/* Load the syscall number for checking. */
86BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
87offsetof(struct seccomp_data, nr)),
88#ifdef __NR_open /* not defined on aarch64 */
89SC_DENY(open, EACCES),
90#endif
91#ifdef __NR_openat
92SC_DENY(openat, EACCES),
93#endif
94SC_ALLOW(getpid),
95#ifdef __NR_getrandom
96SC_ALLOW(getrandom),
97#endif
98SC_ALLOW(gettimeofday),
99SC_ALLOW(clock_gettime),
100#ifdef __NR_time /* not defined on EABI ARM */
101SC_ALLOW(time),
102#endif
103#ifdef __NR_accept /* not defined for __i386__ (linux) */
104SC_ALLOW(accept),
105#endif
106#ifdef __NR_socketcall /* used for accept on __i386__ (linux) */
107SC_ALLOW(socketcall),
108#endif
109SC_ALLOW(fcntl),
110#ifdef __NR_fcntl64 /* only noted on arm */
111SC_ALLOW(fcntl64),
112#endif
113#ifdef __NR_sendmsg /* not defined for __i386__ (linux) */
114SC_ALLOW(sendmsg),
115#endif
116#ifdef __NR_recvmsg /* XXX: untested: mirroring __NR_sendmsg */
117SC_ALLOW(recvmsg),
118#endif
119SC_ALLOW(read),
120SC_ALLOW(readv),
121SC_ALLOW(write),
122SC_ALLOW(writev),
123SC_ALLOW(close),
124#ifdef __NR_shutdown /* not defined on archs that go via socketcall(2) */
125SC_ALLOW(shutdown),
126#endif
127SC_ALLOW(brk),
128#ifdef __NR_ppoll
129SC_ALLOW(ppoll),
130#endif
131#ifdef __NR_poll /* not defined on aarch64 */
132SC_ALLOW(poll),
133#endif
134#ifdef __NR__newselect
135SC_ALLOW(_newselect),
136#endif
137#ifdef __NR_select
138SC_ALLOW(select),
139#endif
140#ifdef __NR_pselect6
141SC_ALLOW(pselect6),
142#endif
143SC_ALLOW(madvise),
144#ifdef __NR_mmap2 /* EABI ARM only has mmap2() */
145SC_ALLOW(mmap2),
146#endif
147#ifdef __NR_mmap
148SC_ALLOW(mmap),
149#endif
150SC_ALLOW(munmap),
151SC_ALLOW(exit_group),
152#ifdef __NR_rt_sigprocmask
153SC_ALLOW(rt_sigprocmask),
154#else
155SC_ALLOW(sigprocmask),
156#endif
157#ifdef __NR_rt_sigaction
158SC_ALLOW(rt_sigaction),
159#else
160SC_ALLOW(sigaction),
161#endif
162BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
163};
164
165/* Syscall filtering set for preauth. */
166static const struct sock_filter preauth_work[] = {
167/* Ensure the syscall arch convention is as expected. */
168BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
169offsetof(struct seccomp_data, arch)),
170BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0),
171BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
172/* Load the syscall number for checking. */
173BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
174offsetof(struct seccomp_data, nr)),
175#ifdef __NR_open /* not defined on aarch64 */
176SC_DENY(open, EACCES),
177#endif
178#ifdef __NR_openat
179SC_DENY(openat, EACCES),
180#endif
181SC_ALLOW(getpid),
182#ifdef __NR_getrandom
183SC_ALLOW(getrandom),
184#endif
185SC_ALLOW(gettimeofday),
186SC_ALLOW(clock_gettime),
187#ifdef __NR_time /* not defined on EABI ARM */
188SC_ALLOW(time),
189#endif
190SC_ALLOW(read),
191SC_ALLOW(readv),
192SC_ALLOW(lseek), /* for kutil_openlog logging */
193SC_ALLOW(fstat), /* for kutil_openlog logging */
194#ifdef __NR_newfstatat
195SC_ALLOW(newfstatat), /* for kutil_openlog logging */
196#endif
197#ifdef __NR_statx
198SC_ALLOW(statx), /* for kutil_openlog logging */
199#endif
200SC_ALLOW(write),
201SC_ALLOW(writev),
202SC_ALLOW(close),
203#ifdef __NR_fcntl64 /* only noted on arm */
204SC_ALLOW(fcntl64),
205#endif
206#ifdef __NR_shutdown /* not defined on archs that go via socketcall(2) */
207SC_ALLOW(shutdown),
208#endif
209SC_ALLOW(brk),
210#ifdef __NR_ppoll
211SC_ALLOW(ppoll),
212#endif
213#ifdef __NR_poll /* not defined on aarch64 */
214SC_ALLOW(poll),
215#endif
216#ifdef __NR__newselect
217SC_ALLOW(_newselect),
218#endif
219#ifdef __NR_select
220SC_ALLOW(select),
221#endif
222#ifdef __NR_pselect6
223SC_ALLOW(pselect6),
224#endif
225SC_ALLOW(madvise),
226#ifdef __NR_mmap2 /* EABI ARM only has mmap2() */
227SC_ALLOW(mmap2),
228#endif
229#ifdef __NR_mmap
230SC_ALLOW(mmap),
231#endif
232SC_ALLOW(mremap),
233SC_ALLOW(munmap),
234SC_ALLOW(exit_group),
235#ifdef __NR_rt_sigprocmask
236SC_ALLOW(rt_sigprocmask),
237#else
238SC_ALLOW(sigprocmask),
239#endif
240BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL),
241};
242
243static const struct sock_fprog preauth_prog_work = {
244.len = (unsigned short)(sizeof(preauth_work)/sizeof(preauth_work[0])),
245.filter = (struct sock_filter *)preauth_work,
246};
247
248static const struct sock_fprog preauth_prog_ctrl = {
249.len = (unsigned short)(sizeof(preauth_ctrl)/sizeof(preauth_ctrl[0])),
250.filter = (struct sock_filter *)preauth_ctrl,
251};
252
253#ifdef SANDBOX_SECCOMP_DEBUG
254static void
255ssh_sandbox_violation_control(int signum,
256siginfo_t *info, void *void_context)
257{
258
259fprintf(stderr,
260"%s: unexpected system call (control) "
261"(arch:0x%x,syscall:%d @ %p)\n",
262__func__, info->si_arch,
263info->si_syscall, info->si_call_addr);
264exit(1);
265}
266
267static void
268ssh_sandbox_violation_worker(int signum,
269siginfo_t *info, void *void_context)
270{
271
272fprintf(stderr,
273"%s: unexpected system call (worker) "
274"(arch:0x%x,syscall:%d @ %p)\n",
275__func__, info->si_arch,
276info->si_syscall, info->si_call_addr);
277exit(1);
278}
279
280static void
281ssh_sandbox_child_debugging(enum sandtype type)
282{
283struct sigaction act;
284sigset_t mask;
285
286memset(&act, 0, sizeof(act));
287sigemptyset(&mask);
288sigaddset(&mask, SIGSYS);
289
290act.sa_sigaction = SAND_WORKER == type ?
291&ssh_sandbox_violation_worker :
292&ssh_sandbox_violation_control;
293act.sa_flags = SA_SIGINFO;
294if (sigaction(SIGSYS, &act, NULL) == -1)
295fprintf(stderr, "%s: sigaction(SIGSYS): %s\n",
296__func__, strerror(errno));
297if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
298fprintf(stderr, "%s: sigprocmask(SIGSYS): %s\n",
299__func__, strerror(errno));
300}
301#endif /* SANDBOX_SECCOMP_DEBUG */
302
303int
304ksandbox_seccomp_init_child(enum sandtype type)
305{
306struct rlimit rl_zero;
307int nnp_failed = 0;
308
309rl_zero.rlim_cur = rl_zero.rlim_max = 0;
310#if 0
311/*
312* Don't do this: we might write into a kutil_openlog(3).
313*/
314if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1)
315kutil_warn(NULL, NULL, "setrlimit");
316
317/*
318* Don't do like OpenSSH: we need to pass stuff back and forth
319* over pipes, and this will prevent that from happening.
320*/
321if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1)
322kutil_warn(NULL, NULL, "setrlimit");
323#endif
324
325if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
326kutil_warn(NULL, NULL, "setrlimit");
327
328#ifdef SANDBOX_SECCOMP_DEBUG
329ssh_sandbox_child_debugging(type);
330#endif
331
332if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
333kutil_warn(NULL, NULL, "prctl");
334nnp_failed = 1;
335}
336
337if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER,
338SAND_WORKER != type ?
339&preauth_prog_ctrl : &preauth_prog_work) == -1)
340kutil_warn(NULL, NULL, "prctl");
341else if (nnp_failed) {
342kutil_warnx(NULL, NULL, "SECCOMP_MODE_FILTER "
343"activated but PR_SET_NO_NEW_PRIVS failed");
344_exit(EXIT_FAILURE);
345}
346
347return 1;
348}
349
350#endif
351