OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <errno.h> | |
6 #include <fcntl.h> | |
7 #include <linux/unistd.h> | |
8 #include <netinet/in.h> | |
9 #include <netinet/tcp.h> | |
10 #include <netinet/udp.h> | |
11 #include <pthread.h> | |
12 #include <signal.h> | |
13 #include <stdarg.h> | |
14 #include <stdio.h> | |
15 #include <stdlib.h> | |
16 #include <string.h> | |
17 #include <sys/ioctl.h> | |
18 #include <sys/ipc.h> | |
19 #include <sys/mman.h> | |
20 #include <sys/prctl.h> | |
21 #include <sys/resource.h> | |
22 #include <sys/shm.h> | |
23 #include <sys/socket.h> | |
24 #include <sys/time.h> | |
25 #include <sys/types.h> | |
26 #include <time.h> | |
27 #include <unistd.h> | |
28 | |
29 #include "base/macros.h" | |
30 #include "base/posix/eintr_wrapper.h" | |
31 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | |
32 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" | |
33 #include "sandbox/linux/services/linux_syscalls.h" | |
34 | |
35 using sandbox::ErrorCode; | |
36 using sandbox::SandboxBPF; | |
37 using sandbox::SandboxBPFPolicy; | |
38 using sandbox::arch_seccomp_data; | |
39 | |
40 #define ERR EPERM | |
41 | |
42 // We don't expect our sandbox to do anything useful yet. So, we will fail | |
43 // almost immediately. For now, force the code to continue running. The | |
44 // following line should be removed as soon as the sandbox is starting to | |
45 // actually enforce restrictions in a meaningful way: | |
46 #define _exit(x) do { } while (0) | |
47 | |
48 namespace { | |
49 | |
50 bool SendFds(int transport, const void *buf, size_t len, ...) { | |
51 int count = 0; | |
52 va_list ap; | |
53 va_start(ap, len); | |
54 while (va_arg(ap, int) >= 0) { | |
55 ++count; | |
56 } | |
57 va_end(ap); | |
58 if (!count) { | |
59 return false; | |
60 } | |
61 char cmsg_buf[CMSG_SPACE(count*sizeof(int))]; | |
62 memset(cmsg_buf, 0, sizeof(cmsg_buf)); | |
63 struct iovec iov[2] = { { 0 } }; | |
64 struct msghdr msg = { 0 }; | |
65 int dummy = 0; | |
66 iov[0].iov_base = &dummy; | |
67 iov[0].iov_len = sizeof(dummy); | |
68 if (buf && len > 0) { | |
69 iov[1].iov_base = const_cast<void *>(buf); | |
70 iov[1].iov_len = len; | |
71 } | |
72 msg.msg_iov = iov; | |
73 msg.msg_iovlen = (buf && len > 0) ? 2 : 1; | |
74 msg.msg_control = cmsg_buf; | |
75 msg.msg_controllen = CMSG_LEN(count*sizeof(int)); | |
76 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); | |
77 cmsg->cmsg_level = SOL_SOCKET; | |
78 cmsg->cmsg_type = SCM_RIGHTS; | |
79 cmsg->cmsg_len = CMSG_LEN(count*sizeof(int)); | |
80 va_start(ap, len); | |
81 for (int i = 0, fd; (fd = va_arg(ap, int)) >= 0; ++i) { | |
82 (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i] = fd; | |
83 } | |
84 return sendmsg(transport, &msg, 0) == | |
85 static_cast<ssize_t>(sizeof(dummy) + ((buf && len > 0) ? len : 0)); | |
86 } | |
87 | |
88 bool GetFds(int transport, void *buf, size_t *len, ...) { | |
89 int count = 0; | |
90 va_list ap; | |
91 va_start(ap, len); | |
92 for (int *fd; (fd = va_arg(ap, int *)) != NULL; ++count) { | |
93 *fd = -1; | |
94 } | |
95 va_end(ap); | |
96 if (!count) { | |
97 return false; | |
98 } | |
99 char cmsg_buf[CMSG_SPACE(count*sizeof(int))]; | |
100 memset(cmsg_buf, 0, sizeof(cmsg_buf)); | |
101 struct iovec iov[2] = { { 0 } }; | |
102 struct msghdr msg = { 0 }; | |
103 int err; | |
104 iov[0].iov_base = &err; | |
105 iov[0].iov_len = sizeof(int); | |
106 if (buf && len && *len > 0) { | |
107 iov[1].iov_base = buf; | |
108 iov[1].iov_len = *len; | |
109 } | |
110 msg.msg_iov = iov; | |
111 msg.msg_iovlen = (buf && len && *len > 0) ? 2 : 1; | |
112 msg.msg_control = cmsg_buf; | |
113 msg.msg_controllen = CMSG_LEN(count*sizeof(int)); | |
114 ssize_t bytes = recvmsg(transport, &msg, 0); | |
115 if (len) { | |
116 *len = bytes > static_cast<int>(sizeof(int)) ? bytes - sizeof(int) : 0; | |
117 } | |
118 if (bytes != static_cast<ssize_t>(sizeof(int) + iov[1].iov_len)) { | |
119 if (bytes >= 0) { | |
120 errno = 0; | |
121 } | |
122 return false; | |
123 } | |
124 if (err) { | |
125 // "err" is the first four bytes of the payload. If these are non-zero, | |
126 // the sender on the other side of the socketpair sent us an errno value. | |
127 // We don't expect to get any file handles in this case. | |
128 errno = err; | |
129 return false; | |
130 } | |
131 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); | |
132 if ((msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) || | |
133 !cmsg || | |
134 cmsg->cmsg_level != SOL_SOCKET || | |
135 cmsg->cmsg_type != SCM_RIGHTS || | |
136 cmsg->cmsg_len != CMSG_LEN(count*sizeof(int))) { | |
137 errno = EBADF; | |
138 return false; | |
139 } | |
140 va_start(ap, len); | |
141 for (int *fd, i = 0; (fd = va_arg(ap, int *)) != NULL; ++i) { | |
142 *fd = (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i]; | |
143 } | |
144 va_end(ap); | |
145 return true; | |
146 } | |
147 | |
148 | |
149 // POSIX doesn't define any async-signal safe function for converting | |
150 // an integer to ASCII. We'll have to define our own version. | |
151 // itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the | |
152 // conversion was successful or NULL otherwise. It never writes more than "sz" | |
153 // bytes. Output will be truncated as needed, and a NUL character is always | |
154 // appended. | |
155 char *itoa_r(int i, char *buf, size_t sz) { | |
156 // Make sure we can write at least one NUL byte. | |
157 size_t n = 1; | |
158 if (n > sz) { | |
159 return NULL; | |
160 } | |
161 | |
162 // Handle negative numbers. | |
163 char *start = buf; | |
164 int minint = 0; | |
165 if (i < 0) { | |
166 // Make sure we can write the '-' character. | |
167 if (++n > sz) { | |
168 *start = '\000'; | |
169 return NULL; | |
170 } | |
171 *start++ = '-'; | |
172 | |
173 // Turn our number positive. | |
174 if (i == -i) { | |
175 // The lowest-most negative integer needs special treatment. | |
176 minint = 1; | |
177 i = -(i + 1); | |
178 } else { | |
179 // "Normal" negative numbers are easy. | |
180 i = -i; | |
181 } | |
182 } | |
183 | |
184 // Loop until we have converted the entire number. Output at least one | |
185 // character (i.e. '0'). | |
186 char *ptr = start; | |
187 do { | |
188 // Make sure there is still enough space left in our output buffer. | |
189 if (++n > sz) { | |
190 buf = NULL; | |
191 goto truncate; | |
192 } | |
193 | |
194 // Output the next digit and (if necessary) compensate for the lowest-most | |
195 // negative integer needing special treatment. This works because, no | |
196 // matter the bit width of the integer, the lowest-most integer always ends | |
197 // in 2, 4, 6, or 8. | |
198 *ptr++ = i%10 + '0' + minint; | |
199 minint = 0; | |
200 i /= 10; | |
201 } while (i); | |
202 truncate: // Terminate the output with a NUL character. | |
203 *ptr = '\000'; | |
204 | |
205 // Conversion to ASCII actually resulted in the digits being in reverse | |
206 // order. We can't easily generate them in forward order, as we can't tell | |
207 // the number of characters needed until we are done converting. | |
208 // So, now, we reverse the string (except for the possible "-" sign). | |
209 while (--ptr > start) { | |
210 char ch = *ptr; | |
211 *ptr = *start; | |
212 *start++ = ch; | |
213 } | |
214 return buf; | |
215 } | |
216 | |
217 // This handler gets called, whenever we encounter a system call that we | |
218 // don't recognize explicitly. For the purposes of this program, we just | |
219 // log the system call and then deny it. More elaborate sandbox policies | |
220 // might try to evaluate the system call in user-space, instead. | |
221 // The only notable complication is that this function must be async-signal | |
222 // safe. This restricts the libary functions that we can call. | |
223 intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) { | |
224 static const char msg0[] = "Disallowed system call #"; | |
225 static const char msg1[] = "\n"; | |
226 char buf[sizeof(msg0) - 1 + 25 + sizeof(msg1)]; | |
227 | |
228 *buf = '\000'; | |
229 strncat(buf, msg0, sizeof(buf) - 1); | |
230 | |
231 char *ptr = strrchr(buf, '\000'); | |
232 itoa_r(data.nr, ptr, sizeof(buf) - (ptr - buf)); | |
233 | |
234 ptr = strrchr(ptr, '\000'); | |
235 strncat(ptr, msg1, sizeof(buf) - (ptr - buf)); | |
236 | |
237 ptr = strrchr(ptr, '\000'); | |
238 if (HANDLE_EINTR(write(2, buf, ptr - buf))) { } | |
239 | |
240 return -ERR; | |
241 } | |
242 | |
243 class DemoPolicy : public SandboxBPFPolicy { | |
244 public: | |
245 DemoPolicy() {} | |
246 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox, | |
247 int sysno) const OVERRIDE; | |
248 | |
249 private: | |
250 DISALLOW_COPY_AND_ASSIGN(DemoPolicy); | |
251 }; | |
252 | |
253 ErrorCode DemoPolicy::EvaluateSyscall(SandboxBPF* sandbox, int sysno) const { | |
254 switch (sysno) { | |
255 #if defined(__NR_accept) | |
256 case __NR_accept: case __NR_accept4: | |
257 #endif | |
258 case __NR_alarm: | |
259 case __NR_brk: | |
260 case __NR_clock_gettime: | |
261 case __NR_close: | |
262 case __NR_dup: case __NR_dup2: | |
263 case __NR_epoll_create: case __NR_epoll_ctl: case __NR_epoll_wait: | |
264 case __NR_exit: case __NR_exit_group: | |
265 case __NR_fcntl: | |
266 #if defined(__NR_fcntl64) | |
267 case __NR_fcntl64: | |
268 #endif | |
269 case __NR_fdatasync: | |
270 case __NR_fstat: | |
271 #if defined(__NR_fstat64) | |
272 case __NR_fstat64: | |
273 #endif | |
274 case __NR_ftruncate: | |
275 case __NR_futex: | |
276 case __NR_getdents: case __NR_getdents64: | |
277 case __NR_getegid: | |
278 #if defined(__NR_getegid32) | |
279 case __NR_getegid32: | |
280 #endif | |
281 case __NR_geteuid: | |
282 #if defined(__NR_geteuid32) | |
283 case __NR_geteuid32: | |
284 #endif | |
285 case __NR_getgid: | |
286 #if defined(__NR_getgid32) | |
287 case __NR_getgid32: | |
288 #endif | |
289 case __NR_getitimer: case __NR_setitimer: | |
290 #if defined(__NR_getpeername) | |
291 case __NR_getpeername: | |
292 #endif | |
293 case __NR_getpid: case __NR_gettid: | |
294 #if defined(__NR_getsockname) | |
295 case __NR_getsockname: | |
296 #endif | |
297 case __NR_gettimeofday: | |
298 case __NR_getuid: | |
299 #if defined(__NR_getuid32) | |
300 case __NR_getuid32: | |
301 #endif | |
302 #if defined(__NR__llseek) | |
303 case __NR__llseek: | |
304 #endif | |
305 case __NR_lseek: | |
306 case __NR_nanosleep: | |
307 case __NR_pipe: case __NR_pipe2: | |
308 case __NR_poll: | |
309 case __NR_pread64: case __NR_preadv: | |
310 case __NR_pwrite64: case __NR_pwritev: | |
311 case __NR_read: case __NR_readv: | |
312 case __NR_restart_syscall: | |
313 case __NR_set_robust_list: | |
314 case __NR_rt_sigaction: | |
315 #if defined(__NR_sigaction) | |
316 case __NR_sigaction: | |
317 #endif | |
318 #if defined(__NR_signal) | |
319 case __NR_signal: | |
320 #endif | |
321 case __NR_rt_sigprocmask: | |
322 #if defined(__NR_sigprocmask) | |
323 case __NR_sigprocmask: | |
324 #endif | |
325 #if defined(__NR_shutdown) | |
326 case __NR_shutdown: | |
327 #endif | |
328 case __NR_rt_sigreturn: | |
329 #if defined(__NR_sigreturn) | |
330 case __NR_sigreturn: | |
331 #endif | |
332 #if defined(__NR_socketpair) | |
333 case __NR_socketpair: | |
334 #endif | |
335 case __NR_time: | |
336 case __NR_uname: | |
337 case __NR_write: case __NR_writev: | |
338 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
339 | |
340 case __NR_prctl: | |
341 // Allow PR_SET_DUMPABLE and PR_GET_DUMPABLE. Do not allow anything else. | |
342 return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, | |
343 PR_SET_DUMPABLE, | |
344 ErrorCode(ErrorCode::ERR_ALLOWED), | |
345 sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, | |
346 PR_GET_DUMPABLE, | |
347 ErrorCode(ErrorCode::ERR_ALLOWED), | |
348 sandbox->Trap(DefaultHandler, NULL))); | |
349 | |
350 // The following system calls are temporarily permitted. This must be | |
351 // tightened later. But we currently don't implement enough of the sandboxing | |
352 // API to do so. | |
353 // As is, this sandbox isn't exactly safe :-/ | |
354 #if defined(__NR_sendmsg) | |
355 case __NR_sendmsg: case __NR_sendto: | |
356 case __NR_recvmsg: case __NR_recvfrom: | |
357 case __NR_getsockopt: case __NR_setsockopt: | |
358 #elif defined(__NR_socketcall) | |
359 case __NR_socketcall: | |
360 #endif | |
361 #if defined(__NR_shmat) | |
362 case __NR_shmat: case __NR_shmctl: case __NR_shmdt: case __NR_shmget: | |
363 #elif defined(__NR_ipc) | |
364 case __NR_ipc: | |
365 #endif | |
366 #if defined(__NR_mmap2) | |
367 case __NR_mmap2: | |
368 #else | |
369 case __NR_mmap: | |
370 #endif | |
371 #if defined(__NR_ugetrlimit) | |
372 case __NR_ugetrlimit: | |
373 #endif | |
374 case __NR_getrlimit: | |
375 case __NR_ioctl: | |
376 case __NR_clone: | |
377 case __NR_munmap: case __NR_mprotect: case __NR_madvise: | |
378 case __NR_remap_file_pages: | |
379 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
380 | |
381 // Everything that isn't explicitly allowed is denied. | |
382 default: | |
383 return sandbox->Trap(DefaultHandler, NULL); | |
384 } | |
385 } | |
386 | |
387 void *ThreadFnc(void *arg) { | |
388 return arg; | |
389 } | |
390 | |
391 void *SendmsgStressThreadFnc(void *arg) { | |
392 if (arg) { } | |
393 static const int repetitions = 100; | |
394 static const int kNumFds = 3; | |
395 for (int rep = 0; rep < repetitions; ++rep) { | |
396 int fds[2 + kNumFds]; | |
397 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { | |
398 perror("socketpair()"); | |
399 _exit(1); | |
400 } | |
401 size_t len = 4; | |
402 char buf[4]; | |
403 if (!SendFds(fds[0], "test", 4, fds[1], fds[1], fds[1], -1) || | |
404 !GetFds(fds[1], buf, &len, fds+2, fds+3, fds+4, NULL) || | |
405 len != 4 || | |
406 memcmp(buf, "test", len) || | |
407 write(fds[2], "demo", 4) != 4 || | |
408 read(fds[0], buf, 4) != 4 || | |
409 memcmp(buf, "demo", 4)) { | |
410 perror("sending/receiving of fds"); | |
411 _exit(1); | |
412 } | |
413 for (int i = 0; i < 2+kNumFds; ++i) { | |
414 if (close(fds[i])) { | |
415 perror("close"); | |
416 _exit(1); | |
417 } | |
418 } | |
419 } | |
420 return NULL; | |
421 } | |
422 | |
423 } // namespace | |
424 | |
425 int main(int argc, char *argv[]) { | |
426 if (argc) { } | |
427 if (argv) { } | |
428 int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY); | |
429 if (SandboxBPF::SupportsSeccompSandbox(proc_fd) != | |
430 SandboxBPF::STATUS_AVAILABLE) { | |
431 perror("sandbox"); | |
432 _exit(1); | |
433 } | |
434 SandboxBPF sandbox; | |
435 sandbox.set_proc_fd(proc_fd); | |
436 sandbox.SetSandboxPolicy(new DemoPolicy()); | |
437 if (!sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED)) { | |
438 fprintf(stderr, "StartSandbox() failed"); | |
439 _exit(1); | |
440 } | |
441 | |
442 // Check that we can create threads | |
443 pthread_t thr; | |
444 if (!pthread_create(&thr, NULL, ThreadFnc, | |
445 reinterpret_cast<void *>(0x1234))) { | |
446 void *ret; | |
447 pthread_join(thr, &ret); | |
448 if (ret != reinterpret_cast<void *>(0x1234)) { | |
449 perror("clone() failed"); | |
450 _exit(1); | |
451 } | |
452 } else { | |
453 perror("clone() failed"); | |
454 _exit(1); | |
455 } | |
456 | |
457 // Check that we handle restart_syscall() without dieing. This is a little | |
458 // tricky to trigger. And I can't think of a good way to verify whether it | |
459 // actually executed. | |
460 signal(SIGALRM, SIG_IGN); | |
461 const struct itimerval tv = { { 0, 0 }, { 0, 5*1000 } }; | |
462 const struct timespec tmo = { 0, 100*1000*1000 }; | |
463 setitimer(ITIMER_REAL, &tv, NULL); | |
464 nanosleep(&tmo, NULL); | |
465 | |
466 // Check that we can query the size of the stack, but that all other | |
467 // calls to getrlimit() fail. | |
468 if (((errno = 0), !getrlimit(RLIMIT_STACK, NULL)) || errno != EFAULT || | |
469 ((errno = 0), !getrlimit(RLIMIT_CORE, NULL)) || errno != ERR) { | |
470 perror("getrlimit()"); | |
471 _exit(1); | |
472 } | |
473 | |
474 // Check that we can query TCGETS and TIOCGWINSZ, but no other ioctls(). | |
475 if (((errno = 0), !ioctl(2, TCGETS, NULL)) || errno != EFAULT || | |
476 ((errno = 0), !ioctl(2, TIOCGWINSZ, NULL)) || errno != EFAULT || | |
477 ((errno = 0), !ioctl(2, TCSETS, NULL)) || errno != ERR) { | |
478 perror("ioctl()"); | |
479 _exit(1); | |
480 } | |
481 | |
482 // Check that prctl() can manipulate the dumpable flag, but nothing else. | |
483 if (((errno = 0), !prctl(PR_GET_DUMPABLE)) || errno || | |
484 ((errno = 0), prctl(PR_SET_DUMPABLE, 1)) || errno || | |
485 ((errno = 0), !prctl(PR_SET_SECCOMP, 0)) || errno != ERR) { | |
486 perror("prctl()"); | |
487 _exit(1); | |
488 } | |
489 | |
490 // Check that we can send and receive file handles. | |
491 int fds[3]; | |
492 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { | |
493 perror("socketpair()"); | |
494 _exit(1); | |
495 } | |
496 size_t len = 4; | |
497 char buf[4]; | |
498 if (!SendFds(fds[0], "test", 4, fds[1], -1) || | |
499 !GetFds(fds[1], buf, &len, fds+2, NULL) || | |
500 len != 4 || | |
501 memcmp(buf, "test", len) || | |
502 write(fds[2], "demo", 4) != 4 || | |
503 read(fds[0], buf, 4) != 4 || | |
504 memcmp(buf, "demo", 4) || | |
505 close(fds[0]) || | |
506 close(fds[1]) || | |
507 close(fds[2])) { | |
508 perror("sending/receiving of fds"); | |
509 _exit(1); | |
510 } | |
511 | |
512 // Check whether SysV IPC works. | |
513 int shmid; | |
514 void *addr; | |
515 if ((shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT|0600)) < 0 || | |
516 (addr = shmat(shmid, NULL, 0)) == reinterpret_cast<void *>(-1) || | |
517 shmdt(addr) || | |
518 shmctl(shmid, IPC_RMID, NULL)) { | |
519 perror("sysv IPC"); | |
520 _exit(1); | |
521 } | |
522 | |
523 // Print a message so that the user can see the sandbox is activated. | |
524 time_t tm = time(NULL); | |
525 printf("Sandbox has been started at %s", ctime(&tm)); | |
526 | |
527 // Stress-test the sendmsg() code | |
528 static const int kSendmsgStressNumThreads = 10; | |
529 pthread_t sendmsgStressThreads[kSendmsgStressNumThreads]; | |
530 for (int i = 0; i < kSendmsgStressNumThreads; ++i) { | |
531 if (pthread_create(sendmsgStressThreads + i, NULL, | |
532 SendmsgStressThreadFnc, NULL)) { | |
533 perror("pthread_create"); | |
534 _exit(1); | |
535 } | |
536 } | |
537 for (int i = 0; i < kSendmsgStressNumThreads; ++i) { | |
538 pthread_join(sendmsgStressThreads[i], NULL); | |
539 } | |
540 | |
541 return 0; | |
542 } | |
OLD | NEW |