diff options
author | Damien Miller <djm@mindrot.org> | 2012-04-04 11:27:54 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2012-04-04 11:27:54 +1000 |
commit | e0956e38349d4a32f3c4a726af45a3695ff2d3c2 (patch) | |
tree | 55f6b3c7251fa1512b38640fb23c809b4af285e3 /sandbox-seccomp-filter.c | |
parent | ce1ec9d4e27d4e08ef02e4e96818263d3ff2eecc (diff) |
- (djm) [Makefile.in configure.ac sandbox-seccomp-filter.c] Add sandbox
mode for Linux's new seccomp filter; patch from Will Drewry; feedback
and ok dtucker@
Diffstat (limited to 'sandbox-seccomp-filter.c')
-rw-r--r-- | sandbox-seccomp-filter.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c new file mode 100644 index 000000000..686812957 --- /dev/null +++ b/sandbox-seccomp-filter.c | |||
@@ -0,0 +1,222 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Will Drewry <wad@dataspill.org> | ||
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 | */ | ||
16 | |||
17 | /* | ||
18 | * Uncomment the SANDBOX_SECCOMP_FILTER_DEBUG macro below to help diagnose | ||
19 | * filter breakage during development. *Do not* use this in production, | ||
20 | * as it relies on making library calls that are unsafe in signal context. | ||
21 | * | ||
22 | * Instead, live systems the auditctl(8) may be used to monitor failures. | ||
23 | * E.g. | ||
24 | * auditctl -a task,always -F uid=<privsep uid> | ||
25 | */ | ||
26 | /* #define SANDBOX_SECCOMP_FILTER_DEBUG 1 */ | ||
27 | |||
28 | #ifdef SANDBOX_SECCOMP_FILTER_DEBUG | ||
29 | /* Use the kernel headers in case of an older toolchain. */ | ||
30 | # include <asm/siginfo.h> | ||
31 | # define __have_siginfo_t 1 | ||
32 | # define __have_sigval_t 1 | ||
33 | # define __have_sigevent_t 1 | ||
34 | #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ | ||
35 | |||
36 | #include "includes.h" | ||
37 | |||
38 | #ifdef SANDBOX_SECCOMP_FILTER | ||
39 | |||
40 | #include <sys/types.h> | ||
41 | #include <sys/resource.h> | ||
42 | #include <sys/prctl.h> | ||
43 | |||
44 | #include <linux/audit.h> | ||
45 | #include <linux/filter.h> | ||
46 | #include <linux/seccomp.h> | ||
47 | |||
48 | #include <asm/unistd.h> | ||
49 | |||
50 | #include <errno.h> | ||
51 | #include <signal.h> | ||
52 | #include <stdarg.h> | ||
53 | #include <stddef.h> /* for offsetof */ | ||
54 | #include <stdio.h> | ||
55 | #include <stdlib.h> | ||
56 | #include <string.h> | ||
57 | #include <unistd.h> | ||
58 | |||
59 | #include "log.h" | ||
60 | #include "ssh-sandbox.h" | ||
61 | #include "xmalloc.h" | ||
62 | |||
63 | /* Linux seccomp_filter sandbox */ | ||
64 | #define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL | ||
65 | |||
66 | /* Use a signal handler to emit violations when debugging */ | ||
67 | #ifdef SANDBOX_SECCOMP_FILTER_DEBUG | ||
68 | # undef SECCOMP_FILTER_FAIL | ||
69 | # define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP | ||
70 | #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ | ||
71 | |||
72 | /* Simple helpers to avoid manual errors (but larger BPF programs). */ | ||
73 | #define SC_DENY(_nr, _errno) \ | ||
74 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ | ||
75 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno)) | ||
76 | #define SC_ALLOW(_nr) \ | ||
77 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ | ||
78 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) | ||
79 | |||
80 | /* Syscall filtering set for preauth. */ | ||
81 | static const struct sock_filter preauth_insns[] = { | ||
82 | /* Ensure the syscall arch convention is as expected. */ | ||
83 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, | ||
84 | offsetof(struct seccomp_data, arch)), | ||
85 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0), | ||
86 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), | ||
87 | /* Load the syscall number for checking. */ | ||
88 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, | ||
89 | offsetof(struct seccomp_data, nr)), | ||
90 | SC_DENY(open, EACCES), | ||
91 | SC_ALLOW(getpid), | ||
92 | SC_ALLOW(gettimeofday), | ||
93 | SC_ALLOW(time), | ||
94 | SC_ALLOW(read), | ||
95 | SC_ALLOW(write), | ||
96 | SC_ALLOW(close), | ||
97 | SC_ALLOW(brk), | ||
98 | SC_ALLOW(poll), | ||
99 | #ifdef __NR__newselect | ||
100 | SC_ALLOW(_newselect), | ||
101 | #else | ||
102 | SC_ALLOW(select), | ||
103 | #endif | ||
104 | SC_ALLOW(madvise), | ||
105 | SC_ALLOW(mmap), | ||
106 | SC_ALLOW(munmap), | ||
107 | SC_ALLOW(exit_group), | ||
108 | #ifdef __NR_rt_sigprocmask | ||
109 | SC_ALLOW(rt_sigprocmask), | ||
110 | #else | ||
111 | SC_ALLOW(sigprocmask), | ||
112 | #endif | ||
113 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), | ||
114 | }; | ||
115 | |||
116 | static const struct sock_fprog preauth_program = { | ||
117 | .len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])), | ||
118 | .filter = (struct sock_filter *)preauth_insns, | ||
119 | }; | ||
120 | |||
121 | struct ssh_sandbox { | ||
122 | pid_t child_pid; | ||
123 | }; | ||
124 | |||
125 | struct ssh_sandbox * | ||
126 | ssh_sandbox_init(void) | ||
127 | { | ||
128 | struct ssh_sandbox *box; | ||
129 | |||
130 | /* | ||
131 | * Strictly, we don't need to maintain any state here but we need | ||
132 | * to return non-NULL to satisfy the API. | ||
133 | */ | ||
134 | debug3("%s: preparing seccomp filter sandbox", __func__); | ||
135 | box = xcalloc(1, sizeof(*box)); | ||
136 | box->child_pid = 0; | ||
137 | |||
138 | return box; | ||
139 | } | ||
140 | |||
141 | #ifdef SANDBOX_SECCOMP_FILTER_DEBUG | ||
142 | extern struct monitor *pmonitor; | ||
143 | void mm_log_handler(LogLevel level, const char *msg, void *ctx); | ||
144 | |||
145 | static void | ||
146 | ssh_sandbox_violation(int signum, siginfo_t *info, void *void_context) | ||
147 | { | ||
148 | char msg[256]; | ||
149 | |||
150 | snprintf(msg, sizeof(msg), | ||
151 | "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)", | ||
152 | __func__, info->si_arch, info->si_syscall, info->si_call_addr); | ||
153 | mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor); | ||
154 | _exit(1); | ||
155 | } | ||
156 | |||
157 | static void | ||
158 | ssh_sandbox_child_debugging(void) | ||
159 | { | ||
160 | struct sigaction act; | ||
161 | sigset_t mask; | ||
162 | |||
163 | debug3("%s: installing SIGSYS handler", __func__); | ||
164 | memset(&act, 0, sizeof(act)); | ||
165 | sigemptyset(&mask); | ||
166 | sigaddset(&mask, SIGSYS); | ||
167 | |||
168 | act.sa_sigaction = &ssh_sandbox_violation; | ||
169 | act.sa_flags = SA_SIGINFO; | ||
170 | if (sigaction(SIGSYS, &act, NULL) == -1) | ||
171 | fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno)); | ||
172 | if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) | ||
173 | fatal("%s: sigprocmask(SIGSYS): %s", | ||
174 | __func__, strerror(errno)); | ||
175 | } | ||
176 | #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ | ||
177 | |||
178 | void | ||
179 | ssh_sandbox_child(struct ssh_sandbox *box) | ||
180 | { | ||
181 | struct rlimit rl_zero; | ||
182 | |||
183 | /* Set rlimits for completeness if possible. */ | ||
184 | rl_zero.rlim_cur = rl_zero.rlim_max = 0; | ||
185 | if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) | ||
186 | fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", | ||
187 | __func__, strerror(errno)); | ||
188 | if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) | ||
189 | fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", | ||
190 | __func__, strerror(errno)); | ||
191 | if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) | ||
192 | fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", | ||
193 | __func__, strerror(errno)); | ||
194 | |||
195 | #ifdef SANDBOX_SECCOMP_FILTER_DEBUG | ||
196 | ssh_sandbox_child_debugging(); | ||
197 | #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ | ||
198 | |||
199 | debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__); | ||
200 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) | ||
201 | fatal("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", | ||
202 | __func__, strerror(errno)); | ||
203 | debug3("%s: attaching seccomp filter program", __func__); | ||
204 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) | ||
205 | fatal("%s: prctl(PR_SET_SECCOMP): %s", | ||
206 | __func__, strerror(errno)); | ||
207 | } | ||
208 | |||
209 | void | ||
210 | ssh_sandbox_parent_finish(struct ssh_sandbox *box) | ||
211 | { | ||
212 | free(box); | ||
213 | debug3("%s: finished", __func__); | ||
214 | } | ||
215 | |||
216 | void | ||
217 | ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) | ||
218 | { | ||
219 | box->child_pid = child_pid; | ||
220 | } | ||
221 | |||
222 | #endif /* SANDBOX_SECCOMP_FILTER */ | ||