From e0dd0295045531750465047312651a30aab5d6a2 Mon Sep 17 00:00:00 2001 From: mdempsky Date: Tue, 9 Sep 2014 02:10:31 -0700 Subject: [PATCH] sandbox: remove seccomp-bpf demo program The demo program has served its purpose, and it's no longer necessary to demonstrate how to use seccomp-bpf. Review URL: https://codereview.chromium.org/554743006 Cr-Commit-Position: refs/heads/master@{#293901} --- sandbox/linux/BUILD.gn | 12 - sandbox/linux/sandbox_linux.gypi | 20 -- sandbox/linux/seccomp-bpf/demo.cc | 542 -------------------------------------- 3 files changed, 574 deletions(-) delete mode 100644 sandbox/linux/seccomp-bpf/demo.cc diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn index 3b941d72dfbb..754664de2e1a 100644 --- a/sandbox/linux/BUILD.gn +++ b/sandbox/linux/BUILD.gn @@ -186,18 +186,6 @@ component("seccomp_bpf_helpers") { ] } -if (compile_seccomp_bpf_demo) { - # A demonstration program for the seccomp-bpf sandbox. - executable("seccomp_bpf_demo") { - sources = [ - "seccomp-bpf/demo.cc", - ] - deps = [ - ":seccomp_bpf", - ] - } -} - # The setuid sandbox for Linux. executable("chrome_sandbox") { sources = [ diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi index 69b782326b00..5a1e65cc983a 100644 --- a/sandbox/linux/sandbox_linux.gypi +++ b/sandbox/linux/sandbox_linux.gypi @@ -181,26 +181,6 @@ ], }, { - # A demonstration program for the seccomp-bpf sandbox. - 'target_name': 'seccomp_bpf_demo', - 'conditions': [ - ['compile_seccomp_bpf_demo==1', { - 'type': 'executable', - 'sources': [ - 'seccomp-bpf/demo.cc', - ], - 'dependencies': [ - 'seccomp_bpf', - ], - }, { - 'type': 'none', - }], - ], - 'include_dirs': [ - '../../', - ], - }, - { # The setuid sandbox, for Linux 'target_name': 'chrome_sandbox', 'type': 'executable', diff --git a/sandbox/linux/seccomp-bpf/demo.cc b/sandbox/linux/seccomp-bpf/demo.cc deleted file mode 100644 index d9fd3423eeef..000000000000 --- a/sandbox/linux/seccomp-bpf/demo.cc +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base/macros.h" -#include "base/posix/eintr_wrapper.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" -#include "sandbox/linux/services/linux_syscalls.h" - -using sandbox::ErrorCode; -using sandbox::SandboxBPF; -using sandbox::SandboxBPFPolicy; -using sandbox::arch_seccomp_data; - -#define ERR EPERM - -// We don't expect our sandbox to do anything useful yet. So, we will fail -// almost immediately. For now, force the code to continue running. The -// following line should be removed as soon as the sandbox is starting to -// actually enforce restrictions in a meaningful way: -#define _exit(x) do { } while (0) - -namespace { - -bool SendFds(int transport, const void *buf, size_t len, ...) { - int count = 0; - va_list ap; - va_start(ap, len); - while (va_arg(ap, int) >= 0) { - ++count; - } - va_end(ap); - if (!count) { - return false; - } - char cmsg_buf[CMSG_SPACE(count*sizeof(int))]; - memset(cmsg_buf, 0, sizeof(cmsg_buf)); - struct iovec iov[2] = { { 0 } }; - struct msghdr msg = { 0 }; - int dummy = 0; - iov[0].iov_base = &dummy; - iov[0].iov_len = sizeof(dummy); - if (buf && len > 0) { - iov[1].iov_base = const_cast(buf); - iov[1].iov_len = len; - } - msg.msg_iov = iov; - msg.msg_iovlen = (buf && len > 0) ? 2 : 1; - msg.msg_control = cmsg_buf; - msg.msg_controllen = CMSG_LEN(count*sizeof(int)); - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(count*sizeof(int)); - va_start(ap, len); - for (int i = 0, fd; (fd = va_arg(ap, int)) >= 0; ++i) { - (reinterpret_cast(CMSG_DATA(cmsg)))[i] = fd; - } - return sendmsg(transport, &msg, 0) == - static_cast(sizeof(dummy) + ((buf && len > 0) ? len : 0)); -} - -bool GetFds(int transport, void *buf, size_t *len, ...) { - int count = 0; - va_list ap; - va_start(ap, len); - for (int *fd; (fd = va_arg(ap, int *)) != NULL; ++count) { - *fd = -1; - } - va_end(ap); - if (!count) { - return false; - } - char cmsg_buf[CMSG_SPACE(count*sizeof(int))]; - memset(cmsg_buf, 0, sizeof(cmsg_buf)); - struct iovec iov[2] = { { 0 } }; - struct msghdr msg = { 0 }; - int err; - iov[0].iov_base = &err; - iov[0].iov_len = sizeof(int); - if (buf && len && *len > 0) { - iov[1].iov_base = buf; - iov[1].iov_len = *len; - } - msg.msg_iov = iov; - msg.msg_iovlen = (buf && len && *len > 0) ? 2 : 1; - msg.msg_control = cmsg_buf; - msg.msg_controllen = CMSG_LEN(count*sizeof(int)); - ssize_t bytes = recvmsg(transport, &msg, 0); - if (len) { - *len = bytes > static_cast(sizeof(int)) ? bytes - sizeof(int) : 0; - } - if (bytes != static_cast(sizeof(int) + iov[1].iov_len)) { - if (bytes >= 0) { - errno = 0; - } - return false; - } - if (err) { - // "err" is the first four bytes of the payload. If these are non-zero, - // the sender on the other side of the socketpair sent us an errno value. - // We don't expect to get any file handles in this case. - errno = err; - return false; - } - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - if ((msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) || - !cmsg || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS || - cmsg->cmsg_len != CMSG_LEN(count*sizeof(int))) { - errno = EBADF; - return false; - } - va_start(ap, len); - for (int *fd, i = 0; (fd = va_arg(ap, int *)) != NULL; ++i) { - *fd = (reinterpret_cast(CMSG_DATA(cmsg)))[i]; - } - va_end(ap); - return true; -} - - -// POSIX doesn't define any async-signal safe function for converting -// an integer to ASCII. We'll have to define our own version. -// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the -// conversion was successful or NULL otherwise. It never writes more than "sz" -// bytes. Output will be truncated as needed, and a NUL character is always -// appended. -char *itoa_r(int i, char *buf, size_t sz) { - // Make sure we can write at least one NUL byte. - size_t n = 1; - if (n > sz) { - return NULL; - } - - // Handle negative numbers. - char *start = buf; - int minint = 0; - if (i < 0) { - // Make sure we can write the '-' character. - if (++n > sz) { - *start = '\000'; - return NULL; - } - *start++ = '-'; - - // Turn our number positive. - if (i == -i) { - // The lowest-most negative integer needs special treatment. - minint = 1; - i = -(i + 1); - } else { - // "Normal" negative numbers are easy. - i = -i; - } - } - - // Loop until we have converted the entire number. Output at least one - // character (i.e. '0'). - char *ptr = start; - do { - // Make sure there is still enough space left in our output buffer. - if (++n > sz) { - buf = NULL; - goto truncate; - } - - // Output the next digit and (if necessary) compensate for the lowest-most - // negative integer needing special treatment. This works because, no - // matter the bit width of the integer, the lowest-most integer always ends - // in 2, 4, 6, or 8. - *ptr++ = i%10 + '0' + minint; - minint = 0; - i /= 10; - } while (i); - truncate: // Terminate the output with a NUL character. - *ptr = '\000'; - - // Conversion to ASCII actually resulted in the digits being in reverse - // order. We can't easily generate them in forward order, as we can't tell - // the number of characters needed until we are done converting. - // So, now, we reverse the string (except for the possible "-" sign). - while (--ptr > start) { - char ch = *ptr; - *ptr = *start; - *start++ = ch; - } - return buf; -} - -// This handler gets called, whenever we encounter a system call that we -// don't recognize explicitly. For the purposes of this program, we just -// log the system call and then deny it. More elaborate sandbox policies -// might try to evaluate the system call in user-space, instead. -// The only notable complication is that this function must be async-signal -// safe. This restricts the libary functions that we can call. -intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) { - static const char msg0[] = "Disallowed system call #"; - static const char msg1[] = "\n"; - char buf[sizeof(msg0) - 1 + 25 + sizeof(msg1)]; - - *buf = '\000'; - strncat(buf, msg0, sizeof(buf) - 1); - - char *ptr = strrchr(buf, '\000'); - itoa_r(data.nr, ptr, sizeof(buf) - (ptr - buf)); - - ptr = strrchr(ptr, '\000'); - strncat(ptr, msg1, sizeof(buf) - (ptr - buf)); - - ptr = strrchr(ptr, '\000'); - if (HANDLE_EINTR(write(2, buf, ptr - buf))) { } - - return -ERR; -} - -class DemoPolicy : public SandboxBPFPolicy { - public: - DemoPolicy() {} - virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox, - int sysno) const OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(DemoPolicy); -}; - -ErrorCode DemoPolicy::EvaluateSyscall(SandboxBPF* sandbox, int sysno) const { - switch (sysno) { -#if defined(__NR_accept) - case __NR_accept: case __NR_accept4: -#endif - case __NR_alarm: - case __NR_brk: - case __NR_clock_gettime: - case __NR_close: - case __NR_dup: case __NR_dup2: - case __NR_epoll_create: case __NR_epoll_ctl: case __NR_epoll_wait: - case __NR_exit: case __NR_exit_group: - case __NR_fcntl: -#if defined(__NR_fcntl64) - case __NR_fcntl64: -#endif - case __NR_fdatasync: - case __NR_fstat: -#if defined(__NR_fstat64) - case __NR_fstat64: -#endif - case __NR_ftruncate: - case __NR_futex: - case __NR_getdents: case __NR_getdents64: - case __NR_getegid: -#if defined(__NR_getegid32) - case __NR_getegid32: -#endif - case __NR_geteuid: -#if defined(__NR_geteuid32) - case __NR_geteuid32: -#endif - case __NR_getgid: -#if defined(__NR_getgid32) - case __NR_getgid32: -#endif - case __NR_getitimer: case __NR_setitimer: -#if defined(__NR_getpeername) - case __NR_getpeername: -#endif - case __NR_getpid: case __NR_gettid: -#if defined(__NR_getsockname) - case __NR_getsockname: -#endif - case __NR_gettimeofday: - case __NR_getuid: -#if defined(__NR_getuid32) - case __NR_getuid32: -#endif -#if defined(__NR__llseek) - case __NR__llseek: -#endif - case __NR_lseek: - case __NR_nanosleep: - case __NR_pipe: case __NR_pipe2: - case __NR_poll: - case __NR_pread64: case __NR_preadv: - case __NR_pwrite64: case __NR_pwritev: - case __NR_read: case __NR_readv: - case __NR_restart_syscall: - case __NR_set_robust_list: - case __NR_rt_sigaction: -#if defined(__NR_sigaction) - case __NR_sigaction: -#endif -#if defined(__NR_signal) - case __NR_signal: -#endif - case __NR_rt_sigprocmask: -#if defined(__NR_sigprocmask) - case __NR_sigprocmask: -#endif -#if defined(__NR_shutdown) - case __NR_shutdown: -#endif - case __NR_rt_sigreturn: -#if defined(__NR_sigreturn) - case __NR_sigreturn: -#endif -#if defined(__NR_socketpair) - case __NR_socketpair: -#endif - case __NR_time: - case __NR_uname: - case __NR_write: case __NR_writev: - return ErrorCode(ErrorCode::ERR_ALLOWED); - - case __NR_prctl: - // Allow PR_SET_DUMPABLE and PR_GET_DUMPABLE. Do not allow anything else. - return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - PR_SET_DUMPABLE, - ErrorCode(ErrorCode::ERR_ALLOWED), - sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, - PR_GET_DUMPABLE, - ErrorCode(ErrorCode::ERR_ALLOWED), - sandbox->Trap(DefaultHandler, NULL))); - - // The following system calls are temporarily permitted. This must be - // tightened later. But we currently don't implement enough of the sandboxing - // API to do so. - // As is, this sandbox isn't exactly safe :-/ -#if defined(__NR_sendmsg) - case __NR_sendmsg: case __NR_sendto: - case __NR_recvmsg: case __NR_recvfrom: - case __NR_getsockopt: case __NR_setsockopt: -#elif defined(__NR_socketcall) - case __NR_socketcall: -#endif -#if defined(__NR_shmat) - case __NR_shmat: case __NR_shmctl: case __NR_shmdt: case __NR_shmget: -#elif defined(__NR_ipc) - case __NR_ipc: -#endif -#if defined(__NR_mmap2) - case __NR_mmap2: -#else - case __NR_mmap: -#endif -#if defined(__NR_ugetrlimit) - case __NR_ugetrlimit: -#endif - case __NR_getrlimit: - case __NR_ioctl: - case __NR_clone: - case __NR_munmap: case __NR_mprotect: case __NR_madvise: - case __NR_remap_file_pages: - return ErrorCode(ErrorCode::ERR_ALLOWED); - - // Everything that isn't explicitly allowed is denied. - default: - return sandbox->Trap(DefaultHandler, NULL); - } -} - -void *ThreadFnc(void *arg) { - return arg; -} - -void *SendmsgStressThreadFnc(void *arg) { - if (arg) { } - static const int repetitions = 100; - static const int kNumFds = 3; - for (int rep = 0; rep < repetitions; ++rep) { - int fds[2 + kNumFds]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { - perror("socketpair()"); - _exit(1); - } - size_t len = 4; - char buf[4]; - if (!SendFds(fds[0], "test", 4, fds[1], fds[1], fds[1], -1) || - !GetFds(fds[1], buf, &len, fds+2, fds+3, fds+4, NULL) || - len != 4 || - memcmp(buf, "test", len) || - write(fds[2], "demo", 4) != 4 || - read(fds[0], buf, 4) != 4 || - memcmp(buf, "demo", 4)) { - perror("sending/receiving of fds"); - _exit(1); - } - for (int i = 0; i < 2+kNumFds; ++i) { - if (close(fds[i])) { - perror("close"); - _exit(1); - } - } - } - return NULL; -} - -} // namespace - -int main(int argc, char *argv[]) { - if (argc) { } - if (argv) { } - int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY); - if (SandboxBPF::SupportsSeccompSandbox(proc_fd) != - SandboxBPF::STATUS_AVAILABLE) { - perror("sandbox"); - _exit(1); - } - SandboxBPF sandbox; - sandbox.set_proc_fd(proc_fd); - sandbox.SetSandboxPolicy(new DemoPolicy()); - if (!sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED)) { - fprintf(stderr, "StartSandbox() failed"); - _exit(1); - } - - // Check that we can create threads - pthread_t thr; - if (!pthread_create(&thr, NULL, ThreadFnc, - reinterpret_cast(0x1234))) { - void *ret; - pthread_join(thr, &ret); - if (ret != reinterpret_cast(0x1234)) { - perror("clone() failed"); - _exit(1); - } - } else { - perror("clone() failed"); - _exit(1); - } - - // Check that we handle restart_syscall() without dieing. This is a little - // tricky to trigger. And I can't think of a good way to verify whether it - // actually executed. - signal(SIGALRM, SIG_IGN); - const struct itimerval tv = { { 0, 0 }, { 0, 5*1000 } }; - const struct timespec tmo = { 0, 100*1000*1000 }; - setitimer(ITIMER_REAL, &tv, NULL); - nanosleep(&tmo, NULL); - - // Check that we can query the size of the stack, but that all other - // calls to getrlimit() fail. - if (((errno = 0), !getrlimit(RLIMIT_STACK, NULL)) || errno != EFAULT || - ((errno = 0), !getrlimit(RLIMIT_CORE, NULL)) || errno != ERR) { - perror("getrlimit()"); - _exit(1); - } - - // Check that we can query TCGETS and TIOCGWINSZ, but no other ioctls(). - if (((errno = 0), !ioctl(2, TCGETS, NULL)) || errno != EFAULT || - ((errno = 0), !ioctl(2, TIOCGWINSZ, NULL)) || errno != EFAULT || - ((errno = 0), !ioctl(2, TCSETS, NULL)) || errno != ERR) { - perror("ioctl()"); - _exit(1); - } - - // Check that prctl() can manipulate the dumpable flag, but nothing else. - if (((errno = 0), !prctl(PR_GET_DUMPABLE)) || errno || - ((errno = 0), prctl(PR_SET_DUMPABLE, 1)) || errno || - ((errno = 0), !prctl(PR_SET_SECCOMP, 0)) || errno != ERR) { - perror("prctl()"); - _exit(1); - } - - // Check that we can send and receive file handles. - int fds[3]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { - perror("socketpair()"); - _exit(1); - } - size_t len = 4; - char buf[4]; - if (!SendFds(fds[0], "test", 4, fds[1], -1) || - !GetFds(fds[1], buf, &len, fds+2, NULL) || - len != 4 || - memcmp(buf, "test", len) || - write(fds[2], "demo", 4) != 4 || - read(fds[0], buf, 4) != 4 || - memcmp(buf, "demo", 4) || - close(fds[0]) || - close(fds[1]) || - close(fds[2])) { - perror("sending/receiving of fds"); - _exit(1); - } - - // Check whether SysV IPC works. - int shmid; - void *addr; - if ((shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT|0600)) < 0 || - (addr = shmat(shmid, NULL, 0)) == reinterpret_cast(-1) || - shmdt(addr) || - shmctl(shmid, IPC_RMID, NULL)) { - perror("sysv IPC"); - _exit(1); - } - - // Print a message so that the user can see the sandbox is activated. - time_t tm = time(NULL); - printf("Sandbox has been started at %s", ctime(&tm)); - - // Stress-test the sendmsg() code - static const int kSendmsgStressNumThreads = 10; - pthread_t sendmsgStressThreads[kSendmsgStressNumThreads]; - for (int i = 0; i < kSendmsgStressNumThreads; ++i) { - if (pthread_create(sendmsgStressThreads + i, NULL, - SendmsgStressThreadFnc, NULL)) { - perror("pthread_create"); - _exit(1); - } - } - for (int i = 0; i < kSendmsgStressNumThreads; ++i) { - pthread_join(sendmsgStressThreads[i], NULL); - } - - return 0; -} -- 2.11.4.GIT