mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-09-20 12:22:14 +02:00
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:
parent
bcdc002269
commit
f9c1ba6493
12
changes/seccomp2_sandbox
Normal file
12
changes/seccomp2_sandbox
Normal 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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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 = \
|
||||
|
@ -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
328
src/common/sandbox.c
Normal 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
55
src/common/sandbox.h
Normal 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_ */
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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 ? \
|
||||
|
Loading…
Reference in New Issue
Block a user