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.
This commit is contained in:
Cristian Toader 2013-06-17 13:07:14 +03:00 committed by Nick Mathewson
parent bcdc002269
commit f9c1ba6493
11 changed files with 438 additions and 0 deletions

12
changes/seccomp2_sandbox Normal file
View File

@ -0,0 +1,12 @@
o Major features (security):
- Use the seccomp2 syscall filtering facility on Linux to limit
which system calls Tor can invoke. This is an experimental,
Linux-only feature to provide defense-in-depth against unknown
attacks. To try turning it on, set "Sandbox 1" in your torrc
file. This is an experimental feature, however, and some things
may break, so please be ready to report bugs. We hope to add
support for better sandboxing in the future,
including more fine-grained filters, better division of
responsibility, and support for more platforms. This work has
been done by Cristian-Matei Toader for Google Summer of Code.

View File

@ -655,6 +655,12 @@ if test "$upnp" = "true"; then
fi
fi
dnl ============================================================
dnl Check for libseccomp
AC_CHECK_HEADERS([seccomp.h])
AC_SEARCH_LIBS(seccomp_init, [seccomp])
dnl ============================================================
dnl We need an implementation of curve25519.

View File

@ -423,6 +423,11 @@ GENERAL OPTIONS
proxy authentication that Tor supports; feel free to submit a patch if you
want it to support others.
**Sandbox** **0**|**1**::
If set to 1, Tor will run securely through the use of a syscall sandbox.
Otherwise the sandbox will be disabled. The option is currently an
experimental feature. (Default: 0)
**Socks4Proxy** __host__[:__port__]::
Tor will make all OR connections through the SOCKS 4 proxy at host:port
(or host:1080 if port is not specified).

View File

@ -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 = \

View File

@ -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)

328
src/common/sandbox.c Normal file
View File

@ -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
}

55
src/common/sandbox.h Normal file
View File

@ -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_ */

View File

@ -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);

View File

@ -40,6 +40,7 @@
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
#include "sandbox.h"
#include "util.h"
#include "routerlist.h"
#include "routerset.h"
@ -369,6 +370,7 @@ static config_var_t option_vars_[] = {
V(RunAsDaemon, BOOL, "0"),
// V(RunTesting, BOOL, "0"),
OBSOLETE("RunTesting"), // currently unused
V(Sandbox, BOOL, "0"),
V(SafeLogging, STRING, "1"),
V(SafeSocks, BOOL, "0"),
V(ServerDNSAllowBrokenConfig, BOOL, "1"),
@ -1140,6 +1142,8 @@ options_act_reversible(const or_options_t *old_options, char **msg)
goto rollback;
}
sandbox_set_debugging_fd(get_err_logging_fd());
commit:
r = 0;
if (logs_marked) {

View File

@ -57,6 +57,7 @@
#include <openssl/crypto.h>
#endif
#include "memarea.h"
#include "../common/sandbox.h"
#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
@ -2688,6 +2689,14 @@ tor_main(int argc, char *argv[])
#endif
if (tor_init(argc, argv)<0)
return -1;
if (get_options()->Sandbox) {
if (tor_global_sandbox()) {
log_err(LD_BUG,"Failed to create syscall sandbox filter");
return -1;
}
}
switch (get_options()->command) {
case CMD_RUN_TOR:
#ifdef NT_SERVICE

View File

@ -3727,6 +3727,7 @@ typedef struct {
SAFELOG_SCRUB_ALL, SAFELOG_SCRUB_RELAY, SAFELOG_SCRUB_NONE
} SafeLogging_;
int Sandbox; /** < Boolean: should sandboxing be enabled? */
int SafeSocks; /**< Boolean: should we outright refuse application
* connections that use socks4 or socks5-with-local-dns? */
#define LOG_PROTOCOL_WARN (get_options()->ProtocolWarnings ? \