diff options
author | Cristian Toader <cristian.matei.toader@gmail.com> | 2013-06-17 13:07:14 +0300 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2013-07-11 09:13:13 -0400 |
commit | f9c1ba6493478d227c202e4d3444283b2c840a6a (patch) | |
tree | c79191a86be32416dd1de5ead221b15e776e6114 /src/common | |
parent | bcdc0022693c75ea1523468e783bf03832e0a358 (diff) | |
download | tor-f9c1ba6493478d227c202e4d3444283b2c840a6a.tar.gz tor-f9c1ba6493478d227c202e4d3444283b2c840a6a.zip |
Add a basic seccomp2 syscall filter on Linux
It's controlled by the new Sandbox argument. Right now, it's rather
coarse-grained, it's Linux-only, and it may break some features.
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/include.am | 1 | ||||
-rw-r--r-- | src/common/log.c | 16 | ||||
-rw-r--r-- | src/common/sandbox.c | 328 | ||||
-rw-r--r-- | src/common/sandbox.h | 55 | ||||
-rw-r--r-- | src/common/torlog.h | 1 |
5 files changed, 401 insertions, 0 deletions
diff --git a/src/common/include.am b/src/common/include.am index 68275cbcf7..8c32a21778 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -49,6 +49,7 @@ src_common_libor_a_SOURCES = \ src/common/procmon.c \ src/common/util.c \ src/common/util_codedigest.c \ + src/common/sandbox.c \ $(libor_extra_source) src_common_libor_crypto_a_SOURCES = \ diff --git a/src/common/log.c b/src/common/log.c index e196a11287..6f95e518cb 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -1138,6 +1138,22 @@ get_min_log_level(void) return min; } +/** Return the fd of a file log that is receiving ERR messages, or -1 if + * no such log exists. */ +int +get_err_logging_fd(void) +{ + const logfile_t *lf; + for (lf = logfiles; lf; lf = lf->next) { + if (lf->is_temporary || lf->is_syslog || !lf->filename || + lf->callback || lf->seems_dead || lf->fd < 0) + continue; + if (lf->severities->masks[LOG_ERR] & LD_GENERAL) + return lf->fd; + } + return -1; +} + /** Switch all logs to output at most verbose level. */ void switch_logs_debug(void) diff --git a/src/common/sandbox.c b/src/common/sandbox.c new file mode 100644 index 0000000000..48fc6661d5 --- /dev/null +++ b/src/common/sandbox.c @@ -0,0 +1,328 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file sandbox.c + * \brief Code to enable sandboxing. + **/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "sandbox.h" +#include "torlog.h" +#include "orconfig.h" + +#if defined(HAVE_SECCOMP_H) && defined(__linux__) +#define USE_LIBSECCOMP +#endif + +#define DEBUGGING_CLOSE + +#if defined(USE_LIBSECCOMP) + +#include <sys/syscall.h> +#include <seccomp.h> +#include <signal.h> +#include <unistd.h> + +/** Variable used for storing all syscall numbers that will be allowed with the + * stage 1 general Tor sandbox. + */ +static int general_filter[] = { + SCMP_SYS(access), + SCMP_SYS(brk), + SCMP_SYS(clock_gettime), + SCMP_SYS(close), + SCMP_SYS(clone), + SCMP_SYS(epoll_create), + SCMP_SYS(epoll_ctl), + SCMP_SYS(epoll_wait), + SCMP_SYS(execve), + SCMP_SYS(fcntl), +#ifdef __NR_fcntl64 + /* Older libseccomp versions don't define PNR entries for all of these, + * so we need to ifdef them here.*/ + SCMP_SYS(fcntl64), +#endif + SCMP_SYS(flock), + SCMP_SYS(fstat), +#ifdef __NR_fstat64 + SCMP_SYS(fstat64), +#endif + SCMP_SYS(futex), + SCMP_SYS(getdents64), + SCMP_SYS(getegid), +#ifdef __NR_getegid32 + SCMP_SYS(getegid32), +#endif + SCMP_SYS(geteuid), +#ifdef __NR_geteuid32 + SCMP_SYS(geteuid32), +#endif + SCMP_SYS(getgid), +#ifdef __NR_getgid32 + SCMP_SYS(getgid32), +#endif + SCMP_SYS(getrlimit), + SCMP_SYS(gettimeofday), + SCMP_SYS(getuid), +#ifdef __NR_getuid32 + SCMP_SYS(getuid32), +#endif + SCMP_SYS(lseek), +#ifdef __NR__llseek + SCMP_SYS(_llseek), +#endif + SCMP_SYS(mkdir), + SCMP_SYS(mlockall), + SCMP_SYS(mmap), +#ifdef __NR_mmap2 + SCMP_SYS(mmap2), +#endif + SCMP_SYS(mprotect), + SCMP_SYS(mremap), + SCMP_SYS(munmap), + SCMP_SYS(open), + SCMP_SYS(openat), + SCMP_SYS(poll), + SCMP_SYS(prctl), + SCMP_SYS(read), + SCMP_SYS(rename), + SCMP_SYS(rt_sigaction), + SCMP_SYS(rt_sigprocmask), + SCMP_SYS(rt_sigreturn), +#ifdef __NR_sigreturn + SCMP_SYS(sigreturn), +#endif + SCMP_SYS(set_robust_list), + SCMP_SYS(set_thread_area), + SCMP_SYS(set_tid_address), + SCMP_SYS(stat), +#ifdef __NR_stat64 + SCMP_SYS(stat64), +#endif + SCMP_SYS(time), + SCMP_SYS(uname), + SCMP_SYS(write), + SCMP_SYS(exit_group), + SCMP_SYS(exit), + + // socket syscalls + SCMP_SYS(accept4), + SCMP_SYS(bind), + SCMP_SYS(connect), + SCMP_SYS(getsockname), + SCMP_SYS(getsockopt), + SCMP_SYS(listen), +#if __NR_recv >= 0 + /* This is a kludge; It's necessary on 64-bit with libseccomp 1.0.0; I + * don't know if other 64-bit or other versions require it. */ + SCMP_SYS(recv), +#endif + SCMP_SYS(recvmsg), +#if __NR_send >= 0 + SCMP_SYS(send), +#endif + SCMP_SYS(sendto), + SCMP_SYS(setsockopt), + SCMP_SYS(socket), + SCMP_SYS(socketpair), + + // TODO: remove when accept4 is fixed +#ifdef __NR_socketcall + SCMP_SYS(socketcall), +#endif + + SCMP_SYS(recvfrom), + SCMP_SYS(unlink) +}; + +/** + * Function responsible for setting up and enabling a global syscall filter. + * The function is a prototype developed for stage 1 of sandboxing Tor. + * Returns 0 on success. + */ +static int +install_glob_syscall_filter(void) +{ + int rc = 0, i, filter_size; + scmp_filter_ctx ctx; + + ctx = seccomp_init(SCMP_ACT_TRAP); + if (ctx == NULL) { + log_err(LD_BUG,"(Sandbox) failed to initialise libseccomp context"); + rc = -1; + goto end; + } + + if (general_filter != NULL) { + filter_size = sizeof(general_filter) / sizeof(general_filter[0]); + } else { + filter_size = 0; + } + + // add general filters + for (i = 0; i < filter_size; i++) { + rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, general_filter[i], 0); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add syscall index %d, " + "received libseccomp error %d", i, rc); + goto end; + } + } + + rc = seccomp_load(ctx); + + end: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} + +/** Additional file descriptor to use when logging seccomp2 failures */ +static int sigsys_debugging_fd = -1; + +/** Use the file descriptor <b>fd</b> to log seccomp2 failures. */ +static void +sigsys_set_debugging_fd(int fd) +{ + sigsys_debugging_fd = fd; +} + +/** + * Function called when a SIGSYS is caught by the application. It notifies the + * user that an error has occurred and either terminates or allows the + * application to continue execution, based on the DEBUGGING_CLOSE symbol. + */ +static void +sigsys_debugging(int nr, siginfo_t *info, void *void_context) +{ + ucontext_t *ctx = (ucontext_t *) (void_context); + char message[64]; + int rv = 0, syscall, length, err; + (void) nr; + + if (info->si_code != SYS_SECCOMP) + return; + + if (!ctx) + return; + + syscall = ctx->uc_mcontext.gregs[REG_SYSCALL]; + + /* XXXX Avoid use of snprintf; it isn't on the list of Stuff You're Allowed + * To Do In A Signal Handler. */ + length = snprintf(message, sizeof(message), + "\n\n(Sandbox) bad syscall (%d) was caught.\n", + syscall); + + err = 0; + if (sigsys_debugging_fd >= 0) { + rv = write(sigsys_debugging_fd, message, length); + err += rv != length; + } + + rv = write(STDOUT_FILENO, message, length); + err += rv != length; + + if (err) + _exit(2); + +#if defined(DEBUGGING_CLOSE) + _exit(1); +#endif // DEBUGGING_CLOSE +} + +/** + * Function that adds a handler for SIGSYS, which is the signal thrown + * when the application is issuing a syscall which is not allowed. The + * main purpose of this function is to help with debugging by identifying + * filtered syscalls. + */ +static int +install_sigsys_debugging(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0, sizeof(act)); + sigemptyset(&mask); + sigaddset(&mask, SIGSYS); + + act.sa_sigaction = &sigsys_debugging; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGSYS, &act, NULL) < 0) { + log_err(LD_BUG,"(Sandbox) Failed to register SIGSYS signal handler"); + return -1; + } + + if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { + log_err(LD_BUG,"(Sandbox) Failed call to sigprocmask()"); + return -2; + } + + return 0; +} +#endif // USE_LIBSECCOMP + +#ifdef USE_LIBSECCOMP +/** + * Initialises the syscall sandbox filter for any linux architecture, taking + * into account various available features for different linux flavours. + */ +static int +initialise_libseccomp_sandbox(void) +{ + if (install_sigsys_debugging()) + return -1; + + if (install_glob_syscall_filter()) + return -2; + + return 0; +} + +#endif // USE_LIBSECCOMP + +/** + * Enables the stage 1 general sandbox. It applies a syscall filter which does + * not restrict any Tor features. The filter is representative for the whole + * application. + */ +int +tor_global_sandbox(void) +{ + +#if defined(USE_LIBSECCOMP) + return initialise_libseccomp_sandbox(); + +#elif defined(_WIN32) + log_warn(LD_BUG,"Windows sandboxing is not implemented. The feature is " + "currently disabled."); + return 0; + +#elif defined(TARGET_OS_MAC) + log_warn(LD_BUG,"Mac OSX sandboxing is not implemented. The feature is " + "currently disabled"); + return 0; +#else + log_warn(LD_BUG,"Sandboxing is not implemented for your platform. The " + "feature is currently disabled"); + return 0; +#endif +} + +/** Use <b>fd</b> to log non-survivable sandbox violations */ +void +sandbox_set_debugging_fd(int fd) +{ +#ifdef USE_LIBSECCOMP + sigsys_set_debugging_fd(fd); +#else + (void)fd; +#endif +} diff --git a/src/common/sandbox.h b/src/common/sandbox.h new file mode 100644 index 0000000000..bd6f0cfb47 --- /dev/null +++ b/src/common/sandbox.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file sandbox.h + * \brief Header file for sandbox.c. + **/ + +#ifndef SANDBOX_H_ +#define SANDBOX_H_ + +#ifndef SYS_SECCOMP + +/** + * Used by SIGSYS signal handler to check if the signal was issued due to a + * seccomp2 filter violation. + */ +#define SYS_SECCOMP 1 + +#endif + +/** + * Linux definitions + */ +#ifdef __linux__ + +#define __USE_GNU +#include <sys/ucontext.h> + +/** + * Linux 32 bit definitions + */ +#if defined(__i386__) + +#define REG_SYSCALL REG_EAX + +/** + * Linux 64 bit definitions + */ +#elif defined(__x86_64__) + +#define REG_SYSCALL REG_RAX + +#endif + +#endif // __linux__ + +void sandbox_set_debugging_fd(int fd); +int tor_global_sandbox(void); + +#endif /* SANDBOX_H_ */ + diff --git a/src/common/torlog.h b/src/common/torlog.h index 8675d7b6e7..9b2ff2c9ff 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -142,6 +142,7 @@ int get_min_log_level(void); void switch_logs_debug(void); void logs_free_all(void); void add_temp_log(int min_severity); +int get_err_logging_fd(void); void close_temp_logs(void); void rollback_log_changes(void); void mark_logs_temp(void); |