aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/address.c3
-rw-r--r--src/common/backtrace.c205
-rw-r--r--src/common/backtrace.h12
-rw-r--r--src/common/compat.c177
-rw-r--r--src/common/compat.h29
-rw-r--r--src/common/compat_libevent.c8
-rw-r--r--src/common/compat_libevent.h1
-rw-r--r--src/common/container.c19
-rw-r--r--src/common/container.h1
-rw-r--r--src/common/crypto.c166
-rw-r--r--src/common/crypto.h17
-rw-r--r--src/common/crypto_curve25519.c2
-rw-r--r--src/common/crypto_curve25519.h5
-rw-r--r--src/common/crypto_format.c1
-rw-r--r--src/common/include.am45
-rw-r--r--src/common/log.c130
-rw-r--r--src/common/sandbox.c1493
-rw-r--r--src/common/sandbox.h247
-rw-r--r--src/common/testsupport.h80
-rw-r--r--src/common/torgzip.c16
-rw-r--r--src/common/torgzip.h6
-rw-r--r--src/common/torlog.h13
-rw-r--r--src/common/tortls.c158
-rw-r--r--src/common/util.c249
-rw-r--r--src/common/util.h72
25 files changed, 2813 insertions, 342 deletions
diff --git a/src/common/address.c b/src/common/address.c
index 14a7b6bc9..b9f2d9315 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -14,6 +14,7 @@
#include "address.h"
#include "torlog.h"
#include "container.h"
+#include "sandbox.h"
#ifdef _WIN32
#include <process.h>
@@ -234,7 +235,7 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr)
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
- err = getaddrinfo(name, NULL, &hints, &res);
+ err = sandbox_getaddrinfo(name, NULL, &hints, &res);
if (!err) {
best = NULL;
for (res_p = res; res_p; res_p = res_p->ai_next) {
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
new file mode 100644
index 000000000..5049298a1
--- /dev/null
+++ b/src/common/backtrace.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define __USE_GNU
+#define _GNU_SOURCE 1
+
+#include "orconfig.h"
+#include "backtrace.h"
+#include "compat.h"
+#include "util.h"
+#include "torlog.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_CYGWIN_SIGNAL_H
+#include <cygwin/signal.h>
+#elif defined(HAVE_SYS_UCONTEXT_H)
+#include <sys/ucontext.h>
+#elif defined(HAVE_UCONTEXT_H)
+#include <ucontext.h>
+#endif
+
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
+ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
+#define USE_BACKTRACE
+#endif
+
+#if !defined(USE_BACKTRACE)
+#define NO_BACKTRACE_IMPL
+#endif
+
+/** Version of Tor to report in backtrace messages. */
+static char *bt_version = NULL;
+
+#ifdef USE_BACKTRACE
+/** Largest stack depth to try to dump. */
+#define MAX_DEPTH 256
+/** Static allocation of stack to dump. This is static so we avoid stack
+ * pressure. */
+static void *cb_buf[MAX_DEPTH];
+
+/** Change a stacktrace in <b>stack</b> of depth <b>depth</b> so that it will
+ * log the correct function from which a signal was received with context
+ * <b>ctx</b>. (When we get a signal, the current function will not have
+ * called any other function, and will therefore have not pushed its address
+ * onto the stack. Fortunately, we usually have the program counter in the
+ * ucontext_t structure.
+ */
+static void
+clean_backtrace(void **stack, int depth, const ucontext_t *ctx)
+{
+#ifdef PC_FROM_UCONTEXT
+#if defined(__linux__)
+ const int n = 1;
+#elif defined(__darwin__) || defined(__APPLE__) || defined(__OpenBSD__) \
+ || defined(__FreeBSD__)
+ const int n = 2;
+#else
+ const int n = 1;
+#endif
+ if (depth <= n)
+ return;
+
+ stack[n] = (void*) ctx->PC_FROM_UCONTEXT;
+#else
+ (void) depth;
+ (void) ctx;
+#endif
+}
+
+/** Log a message <b>msg</b> at <b>severity</b> in <b>domain</b>, and follow
+ * that with a backtrace log. */
+void
+log_backtrace(int severity, int domain, const char *msg)
+{
+ int depth = backtrace(cb_buf, MAX_DEPTH);
+ char **symbols = backtrace_symbols(cb_buf, depth);
+ int i;
+ tor_log(severity, domain, "%s. Stack trace:", msg);
+ if (!symbols) {
+ tor_log(severity, domain, " Unable to generate backtrace.");
+ return;
+ }
+ for (i=0; i < depth; ++i) {
+ tor_log(severity, domain, " %s", symbols[i]);
+ }
+ free(symbols);
+}
+
+static void crash_handler(int sig, siginfo_t *si, void *ctx_)
+ __attribute__((noreturn));
+
+/** Signal handler: write a crash message with a stack trace, and die. */
+static void
+crash_handler(int sig, siginfo_t *si, void *ctx_)
+{
+ char buf[40];
+ int depth;
+ ucontext_t *ctx = (ucontext_t *) ctx_;
+ int n_fds, i;
+ const int *fds = NULL;
+
+ (void) si;
+
+ depth = backtrace(cb_buf, MAX_DEPTH);
+ /* Clean up the top stack frame so we get the real function
+ * name for the most recently failing function. */
+ clean_backtrace(cb_buf, depth, ctx);
+
+ format_dec_number_sigsafe((unsigned)sig, buf, sizeof(buf));
+
+ tor_log_err_sigsafe(bt_version, " died: Caught signal ", buf, "\n",
+ NULL);
+
+ n_fds = tor_log_get_sigsafe_err_fds(&fds);
+ for (i=0; i < n_fds; ++i)
+ backtrace_symbols_fd(cb_buf, depth, fds[i]);
+
+ abort();
+}
+
+/** Install signal handlers as needed so that when we crash, we produce a
+ * useful stack trace. Return 0 on success, -1 on failure. */
+static int
+install_bt_handler(void)
+{
+ int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS,
+ SIGIO, -1 };
+ int i, rv=0;
+
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = crash_handler;
+ sa.sa_flags = SA_SIGINFO;
+ sigfillset(&sa.sa_mask);
+
+ for (i = 0; trap_signals[i] >= 0; ++i) {
+ if (sigaction(trap_signals[i], &sa, NULL) == -1) {
+ log_warn(LD_BUG, "Sigaction failed: %s", strerror(errno));
+ rv = -1;
+ }
+ }
+ return rv;
+}
+
+/** Uninstall crash handlers. */
+static void
+remove_bt_handler(void)
+{
+ /* We don't need to actually free anything at exit here. */
+}
+#endif
+
+#ifdef NO_BACKTRACE_IMPL
+void
+log_backtrace(int severity, int domain, const char *msg)
+{
+ tor_log(severity, domain, "%s. (Stack trace not available)", msg);
+}
+
+static int
+install_bt_handler(void)
+{
+ return 0;
+}
+
+static void
+remove_bt_handler(void)
+{
+}
+#endif
+
+/** Set up code to handle generating error messages on crashes. */
+int
+configure_backtrace_handler(const char *tor_version)
+{
+ tor_free(bt_version);
+ if (!tor_version)
+ tor_version = "";
+ tor_asprintf(&bt_version, "Tor %s", tor_version);
+
+ return install_bt_handler();
+}
+
+/** Perform end-of-process cleanup for code that generates error messages on
+ * crashes. */
+void
+clean_up_backtrace_handler(void)
+{
+ remove_bt_handler();
+
+ tor_free(bt_version);
+}
+
diff --git a/src/common/backtrace.h b/src/common/backtrace.h
new file mode 100644
index 000000000..765436fee
--- /dev/null
+++ b/src/common/backtrace.h
@@ -0,0 +1,12 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_BACKTRACE_H
+#define TOR_BACKTRACE_H
+
+void log_backtrace(int severity, int domain, const char *msg);
+int configure_backtrace_handler(const char *tor_version);
+void clean_up_backtrace_handler(void);
+
+#endif
+
diff --git a/src/common/compat.c b/src/common/compat.c
index d88c5f92d..8e2619f84 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -23,6 +23,7 @@
* we can also take out the configure check. */
#define _GNU_SOURCE
+#define COMPAT_PRIVATE
#include "compat.h"
#ifdef _WIN32
@@ -109,6 +110,7 @@
#include "util.h"
#include "container.h"
#include "address.h"
+#include "sandbox.h"
/* Inline the strl functions if the platform doesn't have them. */
#ifndef HAVE_STRLCPY
@@ -125,6 +127,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode)
{
int fd;
#ifdef O_CLOEXEC
+ path = sandbox_intern_string(path);
fd = open(path, flags|O_CLOEXEC, mode);
if (fd >= 0)
return fd;
@@ -948,24 +951,40 @@ socket_accounting_unlock(void)
}
/** As close(), but guaranteed to work for sockets across platforms (including
- * Windows, where close()ing a socket doesn't work. Returns 0 on success, -1
- * on failure. */
+ * Windows, where close()ing a socket doesn't work. Returns 0 on success and
+ * the socket error code on failure. */
int
-tor_close_socket(tor_socket_t s)
+tor_close_socket_simple(tor_socket_t s)
{
int r = 0;
/* On Windows, you have to call close() on fds returned by open(),
- * and closesocket() on fds returned by socket(). On Unix, everything
- * gets close()'d. We abstract this difference by always using
- * tor_close_socket to close sockets, and always using close() on
- * files.
- */
-#if defined(_WIN32)
- r = closesocket(s);
-#else
- r = close(s);
-#endif
+ * and closesocket() on fds returned by socket(). On Unix, everything
+ * gets close()'d. We abstract this difference by always using
+ * tor_close_socket to close sockets, and always using close() on
+ * files.
+ */
+ #if defined(_WIN32)
+ r = closesocket(s);
+ #else
+ r = close(s);
+ #endif
+
+ if (r != 0) {
+ int err = tor_socket_errno(-1);
+ log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err));
+ return err;
+ }
+
+ return r;
+}
+
+/** As tor_close_socket_simple(), but keeps track of the number
+ * of open sockets. Returns 0 on success, -1 on failure. */
+int
+tor_close_socket(tor_socket_t s)
+{
+ int r = tor_close_socket_simple(s);
socket_accounting_lock();
#ifdef DEBUG_SOCKET_COUNTING
@@ -980,13 +999,11 @@ tor_close_socket(tor_socket_t s)
if (r == 0) {
--n_sockets_open;
} else {
- int err = tor_socket_errno(-1);
- log_info(LD_NET, "Close returned an error: %s", tor_socket_strerror(err));
#ifdef _WIN32
- if (err != WSAENOTSOCK)
+ if (r != WSAENOTSOCK)
--n_sockets_open;
#else
- if (err != EBADF)
+ if (r != EBADF)
--n_sockets_open;
#endif
r = -1;
@@ -1032,33 +1049,61 @@ mark_socket_open(tor_socket_t s)
tor_socket_t
tor_open_socket(int domain, int type, int protocol)
{
+ return tor_open_socket_with_extensions(domain, type, protocol, 1, 0);
+}
+
+/** As socket(), but creates a nonblocking socket and
+ * counts the number of open sockets. */
+tor_socket_t
+tor_open_socket_nonblocking(int domain, int type, int protocol)
+{
+ return tor_open_socket_with_extensions(domain, type, protocol, 1, 1);
+}
+
+/** As socket(), but counts the number of open sockets and handles
+ * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified.
+ * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate
+ * if the corresponding extension should be used.*/
+tor_socket_t
+tor_open_socket_with_extensions(int domain, int type, int protocol,
+ int cloexec, int nonblock)
+{
tor_socket_t s;
-#ifdef SOCK_CLOEXEC
- s = socket(domain, type|SOCK_CLOEXEC, protocol);
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) |
+ (nonblock ? SOCK_NONBLOCK : 0);
+ s = socket(domain, type|ext_flags, protocol);
if (SOCKET_OK(s))
goto socket_ok;
/* If we got an error, see if it is EINVAL. EINVAL might indicate that,
- * even though we were built on a system with SOCK_CLOEXEC support, we
- * are running on one without. */
+ * even though we were built on a system with SOCK_CLOEXEC and SOCK_NONBLOCK
+ * support, we are running on one without. */
if (errno != EINVAL)
return s;
-#endif /* SOCK_CLOEXEC */
+#endif /* SOCK_CLOEXEC && SOCK_NONBLOCK */
s = socket(domain, type, protocol);
if (! SOCKET_OK(s))
return s;
#if defined(FD_CLOEXEC)
- if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
-#if defined(_WIN32)
- closesocket(s);
+ if (cloexec) {
+ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_FS,"Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
+ }
#else
- close(s);
+ (void)cloexec;
#endif
- return -1;
+
+ if (nonblock) {
+ if (set_socket_nonblocking(s) == -1) {
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
}
-#endif
goto socket_ok; /* So that socket_ok will not be unused. */
@@ -1070,19 +1115,41 @@ tor_open_socket(int domain, int type, int protocol)
return s;
}
-/** As socket(), but counts the number of open sockets. */
+/** As accept(), but counts the number of open sockets. */
tor_socket_t
tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
{
+ return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 0);
+}
+
+/** As accept(), but returns a nonblocking socket and
+ * counts the number of open sockets. */
+tor_socket_t
+tor_accept_socket_nonblocking(tor_socket_t sockfd, struct sockaddr *addr,
+ socklen_t *len)
+{
+ return tor_accept_socket_with_extensions(sockfd, addr, len, 1, 1);
+}
+
+/** As accept(), but counts the number of open sockets and handles
+ * socket creation with either of SOCK_CLOEXEC and SOCK_NONBLOCK specified.
+ * <b>cloexec</b> and <b>nonblock</b> should be either 0 or 1 to indicate
+ * if the corresponding extension should be used.*/
+tor_socket_t
+tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
+ socklen_t *len, int cloexec, int nonblock)
+{
tor_socket_t s;
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- s = accept4(sockfd, addr, len, SOCK_CLOEXEC);
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) |
+ (nonblock ? SOCK_NONBLOCK : 0);
+ s = accept4(sockfd, addr, len, ext_flags);
if (SOCKET_OK(s))
goto socket_ok;
/* If we got an error, see if it is ENOSYS. ENOSYS indicates that,
* even though we were built on a system with accept4 support, we
* are running on one without. Also, check for EINVAL, which indicates that
- * we are missing SOCK_CLOEXEC support. */
+ * we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */
if (errno != EINVAL && errno != ENOSYS)
return s;
#endif
@@ -1092,13 +1159,24 @@ tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len)
return s;
#if defined(FD_CLOEXEC)
- if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
- log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno));
- close(s);
- return TOR_INVALID_SOCKET;
+ if (cloexec) {
+ if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
+ log_warn(LD_NET, "Couldn't set FD_CLOEXEC: %s", strerror(errno));
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
}
+#else
+ (void)cloexec;
#endif
+ if (nonblock) {
+ if (set_socket_nonblocking(s) == -1) {
+ tor_close_socket_simple(s);
+ return TOR_INVALID_SOCKET;
+ }
+ }
+
goto socket_ok; /* So that socket_ok will not be unused. */
socket_ok:
@@ -1220,6 +1298,18 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return 0;
#else
+ return tor_ersatz_socketpair(family, type, protocol, fd);
+#endif
+}
+
+#ifdef NEED_ERSATZ_SOCKETPAIR
+/**
+ * Helper used to implement socketpair on systems that lack it, by
+ * making a direct connection to localhost.
+ */
+STATIC int
+tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
+{
/* This socketpair does not work when localhost is down. So
* it's really not the same thing at all. But it's close enough
* for now, and really, when localhost is down sometimes, we
@@ -1230,7 +1320,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_socket_t acceptor = TOR_INVALID_SOCKET;
struct sockaddr_in listen_addr;
struct sockaddr_in connect_addr;
- int size;
+ socklen_t size;
int saved_errno = -1;
if (protocol
@@ -1313,8 +1403,8 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
if (SOCKET_OK(acceptor))
tor_close_socket(acceptor);
return -saved_errno;
-#endif
}
+#endif
/** Number of extra file descriptors to keep in reserve beyond those that we
* tell Tor it's allowed to use. */
@@ -1746,6 +1836,15 @@ get_user_homedir(const char *username)
* actually examine the filesystem; does a purely syntactic modification.
*
* The parent of the root director is considered to be iteself.
+ *
+ * Path separators are the forward slash (/) everywhere and additionally
+ * the backslash (\) on Win32.
+ *
+ * Cuts off any number of trailing path separators but otherwise ignores
+ * them for purposes of finding the parent directory.
+ *
+ * Returns 0 if a parent directory was successfully found, -1 otherwise (fname
+ * did not have any path separators or only had them at the end).
* */
int
get_parent_directory(char *fname)
diff --git a/src/common/compat.h b/src/common/compat.h
index 8ab719052..8e700a9a1 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -8,6 +8,7 @@
#include "orconfig.h"
#include "torint.h"
+#include "testsupport.h"
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
@@ -84,13 +85,19 @@
/* ===== Compiler compatibility */
-/* GCC can check printf types on arbitrary functions. */
+/* GCC can check printf and scanf types on arbitrary functions. */
#ifdef __GNUC__
#define CHECK_PRINTF(formatIdx, firstArg) \
__attribute__ ((format(printf, formatIdx, firstArg)))
#else
#define CHECK_PRINTF(formatIdx, firstArg)
#endif
+#ifdef __GNUC__
+#define CHECK_SCANF(formatIdx, firstArg) \
+ __attribute__ ((format(scanf, formatIdx, firstArg)))
+#else
+#define CHECK_SCANF(formatIdx, firstArg)
+#endif
/* inline is __inline on windows. */
#ifdef _WIN32
@@ -444,10 +451,22 @@ typedef int socklen_t;
#define TOR_INVALID_SOCKET (-1)
#endif
+int tor_close_socket_simple(tor_socket_t s);
int tor_close_socket(tor_socket_t s);
+tor_socket_t tor_open_socket_with_extensions(
+ int domain, int type, int protocol,
+ int cloexec, int nonblock);
tor_socket_t tor_open_socket(int domain, int type, int protocol);
+tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol);
tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr,
socklen_t *len);
+tor_socket_t tor_accept_socket_nonblocking(tor_socket_t sockfd,
+ struct sockaddr *addr,
+ socklen_t *len);
+tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd,
+ struct sockaddr *addr,
+ socklen_t *len,
+ int cloexec, int nonblock);
int get_n_open_sockets(void);
#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
@@ -720,5 +739,13 @@ char *format_win32_error(DWORD err);
#endif
+#ifdef COMPAT_PRIVATE
+#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
+#define NEED_ERSATZ_SOCKETPAIR
+STATIC int tor_ersatz_socketpair(int family, int type, int protocol,
+ tor_socket_t fd[2]);
+#endif
+#endif
+
#endif
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 200a7c65f..4d0fff833 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -415,6 +415,14 @@ tor_check_libevent_version(const char *m, int server,
#define HEADER_VERSION _EVENT_VERSION
#endif
+/** Return a string representation of the version of Libevent that was used
+* at compilation time. */
+const char *
+tor_libevent_get_header_version_str(void)
+{
+ return HEADER_VERSION;
+}
+
/** See whether the headers we were built against differ from the library we
* linked against so much that we're likely to crash. If so, warn the
* user. */
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 2472e2c49..fda873359 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -78,6 +78,7 @@ void tor_check_libevent_version(const char *m, int server,
const char **badness_out);
void tor_check_libevent_header_compatibility(void);
const char *tor_libevent_get_version_str(void);
+const char *tor_libevent_get_header_version_str(void);
#ifdef USE_BUFFEREVENTS
const struct timeval *tor_libevent_get_one_tick_timeout(void);
diff --git a/src/common/container.c b/src/common/container.c
index eec497a3e..476dc8291 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -243,6 +243,25 @@ smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2)
return 1;
}
+/** Return true iff the two lists contain the same int pointer values in
+ * the same order, or if they are both NULL. */
+int
+smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2)
+{
+ if (sl1 == NULL)
+ return sl2 == NULL;
+ if (sl2 == NULL)
+ return 0;
+ if (smartlist_len(sl1) != smartlist_len(sl2))
+ return 0;
+ SMARTLIST_FOREACH(sl1, int *, cp1, {
+ int *cp2 = smartlist_get(sl2, cp1_sl_idx);
+ if (*cp1 != *cp2)
+ return 0;
+ });
+ return 1;
+}
+
/** Return true iff <b>sl</b> has some element E such that
* tor_memeq(E,<b>element</b>,DIGEST_LEN)
*/
diff --git a/src/common/container.h b/src/common/container.h
index 1a68b8f67..1bcc54066 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -42,6 +42,7 @@ int smartlist_contains_string_case(const smartlist_t *sl, const char *element);
int smartlist_contains_int_as_string(const smartlist_t *sl, int num);
int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2);
int smartlist_contains_digest(const smartlist_t *sl, const char *element);
+int smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2);
int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2);
void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2);
void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2);
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 0ababeaea..5afb98e2c 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -56,6 +56,7 @@
#include "../common/util.h"
#include "container.h"
#include "compat.h"
+#include "sandbox.h"
#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)
#error "We require OpenSSL >= 0.9.8"
@@ -114,7 +115,6 @@ crypto_get_rsa_padding_overhead(int padding)
switch (padding)
{
case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
- case RSA_PKCS1_PADDING: return PKCS1_PADDING_OVERHEAD;
default: tor_assert(0); return -1;
}
}
@@ -126,7 +126,6 @@ crypto_get_rsa_padding(int padding)
{
switch (padding)
{
- case PK_PKCS1_PADDING: return RSA_PKCS1_PADDING;
case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING;
default: tor_assert(0); return -1;
}
@@ -197,6 +196,27 @@ try_load_engine(const char *path, const char *engine)
}
#endif
+/* Returns a trimmed and human-readable version of an openssl version string
+* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10
+* May 2012' and this will parse them into a form similar to '1.0.0b' */
+static char *
+parse_openssl_version_str(const char *raw_version)
+{
+ const char *end_of_version = NULL;
+ /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's
+ trim that down. */
+ if (!strcmpstart(raw_version, "OpenSSL ")) {
+ raw_version += strlen("OpenSSL ");
+ end_of_version = strchr(raw_version, ' ');
+ }
+
+ if (end_of_version)
+ return tor_strndup(raw_version,
+ end_of_version-raw_version);
+ else
+ return tor_strdup(raw_version);
+}
+
static char *crypto_openssl_version_str = NULL;
/* Return a human-readable version of the run-time openssl version number. */
const char *
@@ -204,23 +224,24 @@ crypto_openssl_get_version_str(void)
{
if (crypto_openssl_version_str == NULL) {
const char *raw_version = SSLeay_version(SSLEAY_VERSION);
- const char *end_of_version = NULL;
- /* The output should be something like "OpenSSL 1.0.0b 10 May 2012. Let's
- trim that down. */
- if (!strcmpstart(raw_version, "OpenSSL ")) {
- raw_version += strlen("OpenSSL ");
- end_of_version = strchr(raw_version, ' ');
- }
-
- if (end_of_version)
- crypto_openssl_version_str = tor_strndup(raw_version,
- end_of_version-raw_version);
- else
- crypto_openssl_version_str = tor_strdup(raw_version);
+ crypto_openssl_version_str = parse_openssl_version_str(raw_version);
}
return crypto_openssl_version_str;
}
+static char *crypto_openssl_header_version_str = NULL;
+/* Return a human-readable version of the compile-time openssl version
+* number. */
+const char *
+crypto_openssl_get_header_version_str(void)
+{
+ if (crypto_openssl_header_version_str == NULL) {
+ crypto_openssl_header_version_str =
+ parse_openssl_version_str(OPENSSL_VERSION_TEXT);
+ }
+ return crypto_openssl_header_version_str;
+}
+
/** Initialize the crypto library. Return 0 on success, -1 on failure.
*/
int
@@ -286,12 +307,28 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
" setting default ciphers.");
ENGINE_set_default(e, ENGINE_METHOD_ALL);
}
+ /* Log, if available, the intersection of the set of algorithms
+ used by Tor and the set of algorithms available in the engine */
log_engine("RSA", ENGINE_get_default_RSA());
log_engine("DH", ENGINE_get_default_DH());
+ log_engine("ECDH", ENGINE_get_default_ECDH());
+ log_engine("ECDSA", ENGINE_get_default_ECDSA());
log_engine("RAND", ENGINE_get_default_RAND());
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
- log_engine("3DES", ENGINE_get_cipher_engine(NID_des_ede3_ecb));
- log_engine("AES", ENGINE_get_cipher_engine(NID_aes_128_ecb));
+ log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc));
+ log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb));
+ log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc));
+#ifdef NID_aes_128_ctr
+ log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr));
+#endif
+#ifdef NID_aes_128_gcm
+ log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm));
+#endif
+ log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc));
+#ifdef NID_aes_256_gcm
+ log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm));
+#endif
+
#endif
} else {
log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
@@ -1154,22 +1191,21 @@ int
crypto_pk_asn1_encode(crypto_pk_t *pk, char *dest, size_t dest_len)
{
int len;
- unsigned char *buf, *cp;
- len = i2d_RSAPublicKey(pk->key, NULL);
- if (len < 0 || (size_t)len > dest_len || dest_len > SIZE_T_CEILING)
+ unsigned char *buf = NULL;
+
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
return -1;
- cp = buf = tor_malloc(len+1);
- len = i2d_RSAPublicKey(pk->key, &cp);
- if (len < 0) {
- crypto_log_errors(LOG_WARN,"encoding public key");
- tor_free(buf);
+
+ if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
+ OPENSSL_free(buf);
return -1;
}
/* We don't encode directly into 'dest', because that would be illegal
* type-punning. (C99 is smarter than me, C99 is smarter than me...)
*/
memcpy(dest,buf,len);
- tor_free(buf);
+ OPENSSL_free(buf);
return len;
}
@@ -1200,24 +1236,17 @@ crypto_pk_asn1_decode(const char *str, size_t len)
int
crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out)
{
- unsigned char *buf, *bufp;
+ unsigned char *buf = NULL;
int len;
- len = i2d_RSAPublicKey(pk->key, NULL);
- if (len < 0)
- return -1;
- buf = bufp = tor_malloc(len+1);
- len = i2d_RSAPublicKey(pk->key, &bufp);
- if (len < 0) {
- crypto_log_errors(LOG_WARN,"encoding public key");
- tor_free(buf);
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
return -1;
- }
if (crypto_digest(digest_out, (char*)buf, len) < 0) {
- tor_free(buf);
+ OPENSSL_free(buf);
return -1;
}
- tor_free(buf);
+ OPENSSL_free(buf);
return 0;
}
@@ -1226,31 +1255,24 @@ crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out)
int
crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out)
{
- unsigned char *buf, *bufp;
+ unsigned char *buf = NULL;
int len;
- len = i2d_RSAPublicKey(pk->key, NULL);
- if (len < 0)
- return -1;
- buf = bufp = tor_malloc(len+1);
- len = i2d_RSAPublicKey(pk->key, &bufp);
- if (len < 0) {
- crypto_log_errors(LOG_WARN,"encoding public key");
- tor_free(buf);
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
return -1;
- }
if (crypto_digest_all(digests_out, (char*)buf, len) < 0) {
- tor_free(buf);
+ OPENSSL_free(buf);
return -1;
}
- tor_free(buf);
+ OPENSSL_free(buf);
return 0;
}
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
* every four spaces. */
-/* static */ void
-add_spaces_to_fp(char *out, size_t outlen, const char *in)
+void
+crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
{
int n = 0;
char *end = out+outlen;
@@ -1287,7 +1309,7 @@ crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space)
}
base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN);
if (add_space) {
- add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest);
+ crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest);
} else {
strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1);
}
@@ -1637,21 +1659,6 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
crypto_digest_free(d);
}
-/** Compute the HMAC-SHA-1 of the <b>msg_len</b> bytes in <b>msg</b>, using
- * the <b>key</b> of length <b>key_len</b>. Store the DIGEST_LEN-byte result
- * in <b>hmac_out</b>.
- */
-void
-crypto_hmac_sha1(char *hmac_out,
- const char *key, size_t key_len,
- const char *msg, size_t msg_len)
-{
- tor_assert(key_len < INT_MAX);
- tor_assert(msg_len < INT_MAX);
- HMAC(EVP_sha1(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
- (unsigned char*)hmac_out, NULL);
-}
-
/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
* the <b>key</b> of length <b>key_len</b>. Store the DIGEST256_LEN-byte
* result in <b>hmac_out</b>.
@@ -1720,7 +1727,7 @@ crypto_store_dynamic_dh_modulus(const char *fname)
{
int len, new_len;
DH *dh = NULL;
- unsigned char *dh_string_repr = NULL, *cp = NULL;
+ unsigned char *dh_string_repr = NULL;
char *base64_encoded_dh = NULL;
char *file_string = NULL;
int retval = -1;
@@ -1744,15 +1751,8 @@ crypto_store_dynamic_dh_modulus(const char *fname)
if (!BN_set_word(dh->g, DH_GENERATOR))
goto done;
- len = i2d_DHparams(dh, NULL);
- if (len < 0) {
- log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (1).");
- goto done;
- }
-
- cp = dh_string_repr = tor_malloc_zero(len+1);
- len = i2d_DHparams(dh, &cp);
- if ((len < 0) || ((cp - dh_string_repr) != len)) {
+ len = i2d_DHparams(dh, &dh_string_repr);
+ if ((len < 0) || (dh_string_repr == NULL)) {
log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2).");
goto done;
}
@@ -1779,7 +1779,8 @@ crypto_store_dynamic_dh_modulus(const char *fname)
done:
if (dh)
DH_free(dh);
- tor_free(dh_string_repr);
+ if (dh_string_repr)
+ OPENSSL_free(dh_string_repr);
tor_free(base64_encoded_dh);
tor_free(file_string);
@@ -2387,7 +2388,7 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
return 0;
#else
for (i = 0; filenames[i]; ++i) {
- fd = open(filenames[i], O_RDONLY, 0);
+ fd = open(sandbox_intern_string(filenames[i]), O_RDONLY, 0);
if (fd<0) continue;
log_info(LD_CRYPTO, "Reading entropy from \"%s\"", filenames[i]);
n = read_all(fd, (char*)out, out_len, 0);
@@ -2442,8 +2443,8 @@ crypto_seed_rng(int startup)
/** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
* success, -1 on failure.
*/
-int
-crypto_rand(char *to, size_t n)
+MOCK_IMPL(int,
+crypto_rand, (char *to, size_t n))
{
int r;
tor_assert(n < INT_MAX);
@@ -3137,6 +3138,7 @@ crypto_global_cleanup(void)
}
#endif
tor_free(crypto_openssl_version_str);
+ tor_free(crypto_openssl_header_version_str);
return 0;
}
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 2fbca4c26..6ce3697c9 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -15,6 +15,7 @@
#include <stdio.h>
#include "torint.h"
+#include "testsupport.h"
/*
Macro to create an arbitrary OpenSSL version number as used by
@@ -69,13 +70,9 @@
* signs removed. */
#define BASE64_DIGEST256_LEN 43
-/** Constant used to indicate PKCS1 padding for public-key encryption */
-#define PK_PKCS1_PADDING 60001
/** Constant used to indicate OAEP padding for public-key encryption */
#define PK_PKCS1_OAEP_PADDING 60002
-/** Number of bytes added for PKCS1 padding. */
-#define PKCS1_PADDING_OVERHEAD 11
/** Number of bytes added for PKCS1-OAEP padding. */
#define PKCS1_OAEP_PADDING_OVERHEAD 42
@@ -112,6 +109,7 @@ typedef struct crypto_dh_t crypto_dh_t;
/* global state */
const char * crypto_openssl_get_version_str(void);
+const char * crypto_openssl_get_header_version_str(void);
int crypto_global_init(int hardwareAccel,
const char *accelName,
const char *accelPath);
@@ -221,9 +219,6 @@ void crypto_digest_get_digest(crypto_digest_t *digest,
crypto_digest_t *crypto_digest_dup(const crypto_digest_t *digest);
void crypto_digest_assign(crypto_digest_t *into,
const crypto_digest_t *from);
-void crypto_hmac_sha1(char *hmac_out,
- const char *key, size_t key_len,
- const char *msg, size_t msg_len);
void crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len);
@@ -254,7 +249,7 @@ int crypto_expand_key_material_rfc5869_sha256(
/* random numbers */
int crypto_seed_rng(int startup);
-int crypto_rand(char *to, size_t n);
+MOCK_DECL(int,crypto_rand,(char *to, size_t n));
int crypto_strongest_rand(uint8_t *out, size_t out_len);
int crypto_rand_int(unsigned int max);
uint64_t crypto_rand_uint64(uint64_t max);
@@ -290,7 +285,6 @@ void secret_to_key(char *key_out, size_t key_out_len, const char *secret,
/** OpenSSL-based utility functions. */
void memwipe(void *mem, uint8_t byte, size_t sz);
-#ifdef CRYPTO_PRIVATE
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
* unit tests. */
struct rsa_st;
@@ -301,9 +295,8 @@ crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
struct evp_pkey_st *crypto_pk_get_evp_pkey_(crypto_pk_t *env,
int private);
struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
-/* Prototypes for private functions only used by crypto.c and test.c*/
-void add_spaces_to_fp(char *out, size_t outlen, const char *in);
-#endif
+
+void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
#endif
diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c
index 88c723f37..9e83440e1 100644
--- a/src/common/crypto_curve25519.c
+++ b/src/common/crypto_curve25519.c
@@ -29,7 +29,7 @@ int curve25519_donna(uint8_t *mypublic,
#endif
#endif
-int
+STATIC int
curve25519_impl(uint8_t *output, const uint8_t *secret,
const uint8_t *basepoint)
{
diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h
index 652f1883c..f9d533ba2 100644
--- a/src/common/crypto_curve25519.h
+++ b/src/common/crypto_curve25519.h
@@ -4,6 +4,7 @@
#ifndef TOR_CRYPTO_CURVE25519_H
#define TOR_CRYPTO_CURVE25519_H
+#include "testsupport.h"
#include "torint.h"
/** Length of a curve25519 public key when encoded. */
@@ -52,8 +53,8 @@ int curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out,
const char *fname);
#ifdef CRYPTO_CURVE25519_PRIVATE
-int curve25519_impl(uint8_t *output, const uint8_t *secret,
- const uint8_t *basepoint);
+STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
+ const uint8_t *basepoint);
#endif
#endif
diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c
index 93932f839..be669c8d2 100644
--- a/src/common/crypto_format.c
+++ b/src/common/crypto_format.c
@@ -3,7 +3,6 @@
/* Formatting and parsing code for crypto-related data structures. */
-#define CRYPTO_CURVE25519_PRIVATE
#include "orconfig.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
diff --git a/src/common/include.am b/src/common/include.am
index b796ebfae..814786b77 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -1,5 +1,15 @@
-noinst_LIBRARIES+= src/common/libor.a src/common/libor-crypto.a src/common/libor-event.a
+noinst_LIBRARIES += \
+ src/common/libor.a \
+ src/common/libor-crypto.a \
+ src/common/libor-event.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += \
+ src/common/libor-testing.a \
+ src/common/libor-crypto-testing.a \
+ src/common/libor-event-testing.a
+endif
EXTRA_DIST+= \
src/common/common_sha1.i \
@@ -14,9 +24,13 @@ else
libor_extra_source=
endif
+src_common_libcurve25519_donna_a_CFLAGS=
+
if BUILD_CURVE25519_DONNA
src_common_libcurve25519_donna_a_SOURCES=\
src/ext/curve25519_donna/curve25519-donna.c
+src_common_libcurve25519_donna_a_CFLAGS+=\
+ @F_OMIT_FRAME_POINTER@
noinst_LIBRARIES+=src/common/libcurve25519_donna.a
LIBDONNA=src/common/libcurve25519_donna.a
else
@@ -30,14 +44,13 @@ LIBDONNA=
endif
endif
-src_common_libcurve25519_donna_a_CFLAGS =
-
if CURVE25519_ENABLED
libcrypto_extra_source=src/common/crypto_curve25519.c
endif
-src_common_libor_a_SOURCES = \
+LIBOR_A_SOURCES = \
src/common/address.c \
+ src/common/backtrace.c \
src/common/compat.c \
src/common/container.c \
src/common/di_ops.c \
@@ -47,9 +60,10 @@ 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 = \
+LIBOR_CRYPTO_A_SOURCES = \
src/common/aes.c \
src/common/crypto.c \
src/common/crypto_format.c \
@@ -57,10 +71,27 @@ src_common_libor_crypto_a_SOURCES = \
src/common/tortls.c \
$(libcrypto_extra_source)
-src_common_libor_event_a_SOURCES = src/common/compat_libevent.c
+LIBOR_EVENT_A_SOURCES = src/common/compat_libevent.c
+
+src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES)
+src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
+src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SOURCES)
+
+src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES)
+src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
+src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES)
+
+src_common_libor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_common_libor_crypto_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_common_libor_event_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_common_libor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_common_libor_crypto_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
COMMONHEADERS = \
src/common/address.h \
+ src/common/backtrace.h \
src/common/aes.h \
src/common/ciphers.inc \
src/common/compat.h \
@@ -72,6 +103,8 @@ COMMONHEADERS = \
src/common/memarea.h \
src/common/mempool.h \
src/common/procmon.h \
+ src/common/sandbox.h \
+ src/common/testsupport.h \
src/common/torgzip.h \
src/common/torint.h \
src/common/torlog.h \
diff --git a/src/common/log.c b/src/common/log.c
index e196a1128..9c67de320 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -36,6 +36,10 @@
#include "torlog.h"
#include "container.h"
+/** Given a severity, yields an index into log_severity_list_t.masks to use
+ * for that severity. */
+#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR)
+
/** @{ */
/** The string we stick at the end of a log message when it is too long,
* and its length. */
@@ -83,12 +87,12 @@ should_log_function_name(log_domain_mask_t domain, int severity)
case LOG_DEBUG:
case LOG_INFO:
/* All debugging messages occur in interesting places. */
- return 1;
+ return (domain & LD_NOFUNCNAME) == 0;
case LOG_NOTICE:
case LOG_WARN:
case LOG_ERR:
/* We care about places where bugs occur. */
- return (domain == LD_BUG);
+ return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG;
default:
/* Call assert, not tor_assert, since tor_assert calls log on failure. */
assert(0); return 0;
@@ -439,6 +443,128 @@ tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
va_end(ap);
}
+/** Maximum number of fds that will get notifications if we crash */
+#define MAX_SIGSAFE_FDS 8
+/** Array of fds to log crash-style warnings to. */
+static int sigsafe_log_fds[MAX_SIGSAFE_FDS] = { STDERR_FILENO };
+/** The number of elements used in sigsafe_log_fds */
+static int n_sigsafe_log_fds = 1;
+
+/** Write <b>s</b> to each element of sigsafe_log_fds. Return 0 on success, -1
+ * on failure. */
+static int
+tor_log_err_sigsafe_write(const char *s)
+{
+ int i;
+ ssize_t r;
+ size_t len = strlen(s);
+ int err = 0;
+ for (i=0; i < n_sigsafe_log_fds; ++i) {
+ r = write(sigsafe_log_fds[i], s, len);
+ err += (r != (ssize_t)len);
+ }
+ return err ? -1 : 0;
+}
+
+/** Given a list of string arguments ending with a NULL, writes them
+ * to our logs and to stderr (if possible). This function is safe to call
+ * from within a signal handler. */
+void
+tor_log_err_sigsafe(const char *m, ...)
+{
+ va_list ap;
+ const char *x;
+ char timebuf[33];
+ time_t now = time(NULL);
+
+ if (!m)
+ return;
+ if (log_time_granularity >= 2000) {
+ int g = log_time_granularity / 1000;
+ now -= now % g;
+ }
+ timebuf[0] = now < 0 ? '-' : ' ';
+ if (now < 0) now = -now;
+ timebuf[1] = '\0';
+ format_dec_number_sigsafe(now, timebuf+1, sizeof(timebuf)-1);
+ tor_log_err_sigsafe_write("\n=========================================="
+ "================== T=");
+ tor_log_err_sigsafe_write(timebuf);
+ tor_log_err_sigsafe_write("\n");
+ tor_log_err_sigsafe_write(m);
+ va_start(ap, m);
+ while ((x = va_arg(ap, const char*))) {
+ tor_log_err_sigsafe_write(x);
+ }
+ va_end(ap);
+}
+
+/** Set *<b>out</b> to a pointer to an array of the fds to log errors to from
+ * inside a signal handler. Return the number of elements in the array. */
+int
+tor_log_get_sigsafe_err_fds(const int **out)
+{
+ *out = sigsafe_log_fds;
+ return n_sigsafe_log_fds;
+}
+
+/** Helper function; return true iff the <b>n</b>-element array <b>array</b>
+ * contains <b>item</b>. */
+static int
+int_array_contains(const int *array, int n, int item)
+{
+ int j;
+ for (j = 0; j < n; ++j) {
+ if (array[j] == item)
+ return 1;
+ }
+ return 0;
+}
+
+/** Function to call whenever the list of logs changes to get ready to log
+ * from signal handlers. */
+void
+tor_log_update_sigsafe_err_fds(void)
+{
+ const logfile_t *lf;
+ int found_real_stderr = 0;
+
+ LOCK_LOGS();
+ /* Reserve the first one for stderr. This is safe because when we daemonize,
+ * we dup2 /dev/null to stderr, */
+ sigsafe_log_fds[0] = STDERR_FILENO;
+ n_sigsafe_log_fds = 1;
+
+ for (lf = logfiles; lf; lf = lf->next) {
+ /* Don't try callback to the control port, or syslogs: We can't
+ * do them from a signal handler. Don't try stdout: we always do stderr.
+ */
+ if (lf->is_temporary || lf->is_syslog ||
+ lf->callback || lf->seems_dead || lf->fd < 0)
+ continue;
+ if (lf->severities->masks[SEVERITY_MASK_IDX(LOG_ERR)] &
+ (LD_BUG|LD_GENERAL)) {
+ if (lf->fd == STDERR_FILENO)
+ found_real_stderr = 1;
+ /* Avoid duplicates */
+ if (int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, lf->fd))
+ continue;
+ sigsafe_log_fds[n_sigsafe_log_fds++] = lf->fd;
+ if (n_sigsafe_log_fds == MAX_SIGSAFE_FDS)
+ break;
+ }
+ }
+
+ if (!found_real_stderr &&
+ int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) {
+ /* Don't use a virtual stderr when we're also logging to stdout. */
+ assert(n_sigsafe_log_fds >= 2); /* Don't use assert inside log functions*/
+ sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds];
+ }
+
+ UNLOCK_LOGS();
+}
+
/** Output a message to the log, prefixed with a function name <b>fn</b>. */
#ifdef __GNUC__
/** GCC-based implementation of the log_fn backend, used when we have
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
new file mode 100644
index 000000000..7ef577dbe
--- /dev/null
+++ b/src/common/sandbox.c
@@ -0,0 +1,1493 @@
+/* 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 "orconfig.h"
+
+#ifndef _LARGEFILE64_SOURCE
+/**
+ * Temporarily required for O_LARGEFILE flag. Needs to be removed
+ * with the libevent fix.
+ */
+#define _LARGEFILE64_SOURCE
+#endif
+
+/** Malloc mprotect limit in bytes. */
+#define MALLOC_MP_LIM 1048576
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "sandbox.h"
+#include "torlog.h"
+#include "torint.h"
+#include "util.h"
+#include "tor_queue.h"
+
+#define DEBUGGING_CLOSE
+
+#if defined(USE_LIBSECCOMP)
+
+#define _GNU_SOURCE
+
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <linux/futex.h>
+#include <bits/signum.h>
+#include <event2/event.h>
+
+#include <stdarg.h>
+#include <seccomp.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <poll.h>
+
+/**Determines if at least one sandbox is active.*/
+static int sandbox_active = 0;
+/** Holds the parameter list configuration for the sandbox.*/
+static sandbox_cfg_t *filter_dynamic = NULL;
+/** Holds a list of pre-recorded results from getaddrinfo().*/
+static sb_addr_info_t *sb_addr_info = NULL;
+
+#undef SCMP_CMP
+#define SCMP_CMP(a,b,c) ((struct scmp_arg_cmp){(a),(b),(c),0})
+
+/** Variable used for storing all syscall numbers that will be allowed with the
+ * stage 1 general Tor sandbox.
+ */
+static int filter_nopar_gen[] = {
+ SCMP_SYS(access),
+ SCMP_SYS(brk),
+ SCMP_SYS(clock_gettime),
+ SCMP_SYS(close),
+ SCMP_SYS(clone),
+ SCMP_SYS(epoll_create),
+ SCMP_SYS(epoll_wait),
+ SCMP_SYS(fcntl),
+ SCMP_SYS(fstat),
+#ifdef __NR_fstat64
+ SCMP_SYS(fstat64),
+#endif
+ 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),
+ SCMP_SYS(munmap),
+ SCMP_SYS(read),
+ SCMP_SYS(rename),
+ SCMP_SYS(rt_sigreturn),
+ SCMP_SYS(set_robust_list),
+#ifdef __NR_sigreturn
+ SCMP_SYS(sigreturn),
+#endif
+ SCMP_SYS(stat),
+ SCMP_SYS(uname),
+ SCMP_SYS(write),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(exit),
+
+ SCMP_SYS(madvise),
+#ifdef __NR_stat64
+ // getaddrinfo uses this..
+ SCMP_SYS(stat64),
+#endif
+
+ /*
+ * These socket syscalls are not required on x86_64 and not supported with
+ * some libseccomp versions (eg: 1.0.1)
+ */
+#if defined(__i386)
+ SCMP_SYS(recv),
+ SCMP_SYS(send),
+#endif
+
+ // socket syscalls
+ SCMP_SYS(bind),
+ SCMP_SYS(connect),
+ SCMP_SYS(getsockname),
+ SCMP_SYS(recvmsg),
+ SCMP_SYS(recvfrom),
+ SCMP_SYS(sendto),
+ SCMP_SYS(unlink)
+};
+
+/**
+ * Function responsible for setting up the rt_sigaction syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ unsigned i;
+ int rc;
+ int param[] = { SIGINT, SIGTERM, SIGPIPE, SIGUSR1, SIGUSR2, SIGHUP, SIGCHLD,
+#ifdef SIGXFSZ
+ SIGXFSZ
+#endif
+ };
+ (void) filter;
+
+ for (i = 0; i < ARRAY_LENGTH(param); i++) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param[i]));
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Function responsible for setting up the execve syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_execve(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = (smp_param_t*) elem->param;
+
+ if (param != NULL && param->prot == 1 && param->syscall
+ == SCMP_SYS(execve)) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add execve syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the time syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_time(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ (void) filter;
+ return seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(time), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, 0));
+}
+
+/**
+ * Function responsible for setting up the accept4 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_accept4(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void)filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, 18));
+ if (rc) {
+ return rc;
+ }
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 1,
+ SCMP_CMP(3, SCMP_CMP_EQ, SOCK_CLOEXEC));
+ if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
+
+#ifdef __NR_mmap2
+/**
+ * Function responsible for setting up the mmap2 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_ANONYMOUS));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ,MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_EXEC),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE|MAP_DENYWRITE));
+ if (rc) {
+ return rc;
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * Function responsible for setting up the open syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param != NULL && param->prot == 1 && param->syscall
+ == SCMP_SYS(open)) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(-1), SCMP_SYS(open), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, O_RDONLY|O_CLOEXEC));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add open syscall, received libseccomp "
+ "error %d", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the openat syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_openat(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param != NULL && param->prot == 1 && param->syscall
+ == SCMP_SYS(openat)) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, AT_FDCWD),
+ SCMP_CMP(1, SCMP_CMP_EQ, param->value),
+ SCMP_CMP(2, SCMP_CMP_EQ, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|
+ O_CLOEXEC));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the socket syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_INET),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_INET),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_RAW),
+ SCMP_CMP(2, SCMP_CMP_EQ, 0));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the socketpair syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_socketpair(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 2,
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the setsockopt syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET),
+ SCMP_CMP(2, SCMP_CMP_EQ, SO_REUSEADDR));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the getsockopt syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), 0);
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET),
+ SCMP_CMP(2, SCMP_CMP_EQ, SO_ERROR));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+#ifdef __NR_fcntl64
+/**
+ * Function responsible for setting up the fcntl64 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_fcntl64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_GETFL));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_SETFL),
+ SCMP_CMP(2, SCMP_CMP_EQ, O_RDWR|O_NONBLOCK));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_GETFD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, F_SETFD),
+ SCMP_CMP(2, SCMP_CMP_EQ, FD_CLOEXEC));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+#endif
+
+/**
+ * Function responsible for setting up the epoll_ctl syscall for
+ * the seccomp filter sandbox.
+ *
+ * Note: basically allows everything but will keep for now..
+ */
+static int
+sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_ADD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_MOD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_DEL));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the fcntl64 syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: if multiple filters need to be added, the PR_SECCOMP parameter needs
+ * to be whitelisted in this function.
+ */
+static int
+sb_prctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, PR_SET_DUMPABLE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the fcntl64 syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: does not NEED to be here.. currently only occurs before filter; will
+ * keep just in case for the future.
+ */
+static int
+sb_mprotect(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 1,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 1,
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the rt_sigprocmask syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_rt_sigprocmask(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, SIG_UNBLOCK));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, SIG_SETMASK));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the flock syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: does not need to be here, occurs before filter is applied.
+ */
+static int
+sb_flock(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, LOCK_EX|LOCK_NB));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, LOCK_UN));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the futex syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_futex(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ // can remove
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ,
+ FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAKE_PRIVATE));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAIT_PRIVATE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the mremap syscall for
+ * the seccomp filter sandbox.
+ *
+ * NOTE: so far only occurs before filter is applied.
+ */
+static int
+sb_mremap(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 1,
+ SCMP_CMP(3, SCMP_CMP_EQ, MREMAP_MAYMOVE));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the poll syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_poll(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ (void) filter;
+
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), 2,
+ SCMP_CMP(1, SCMP_CMP_EQ, 1),
+ SCMP_CMP(2, SCMP_CMP_EQ, 10));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+#ifdef __NR_stat64
+/**
+ * Function responsible for setting up the stat64 syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc = 0;
+ sandbox_cfg_t *elem = NULL;
+
+ // for each dynamic parameter filters
+ for (elem = filter; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open)
+ || param->syscall == SCMP_SYS(stat64))) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
+ "libseccomp error %d", rc);
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * Array of function pointers responsible for filtering different syscalls at
+ * a parameter level.
+ */
+static sandbox_filter_func_t filter_func[] = {
+ sb_rt_sigaction,
+ sb_rt_sigprocmask,
+ sb_execve,
+ sb_time,
+ sb_accept4,
+#ifdef __NR_mmap2
+ sb_mmap2,
+#endif
+ sb_open,
+ sb_openat,
+#ifdef __NR_fcntl64
+ sb_fcntl64,
+#endif
+ sb_epoll_ctl,
+ sb_prctl,
+ sb_mprotect,
+ sb_flock,
+ sb_futex,
+ sb_mremap,
+ sb_poll,
+#ifdef __NR_stat64
+ sb_stat64,
+#endif
+
+ sb_socket,
+ sb_setsockopt,
+ sb_getsockopt,
+ sb_socketpair
+};
+
+const char*
+sandbox_intern_string(const char *str)
+{
+ sandbox_cfg_t *elem;
+
+ if (str == NULL)
+ return NULL;
+
+ for (elem = filter_dynamic; elem != NULL; elem = elem->next) {
+ smp_param_t *param = elem->param;
+
+ if (param->prot && !strcmp(str, (char*)(param->value))) {
+ return (char*)(param->value);
+ }
+ }
+
+ log_info(LD_GENERAL, "(Sandbox) Parameter %s not found", str);
+ return str;
+}
+
+/**
+ * Protects all the strings in the sandbox's parameter list configuration. It
+ * works by calculating the total amount of memory required by the parameter
+ * list, allocating the memory using mmap, and protecting it from writes with
+ * mprotect().
+ */
+static int
+prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
+{
+ int ret = 0;
+ size_t pr_mem_size = 0, pr_mem_left = 0;
+ char *pr_mem_next = NULL, *pr_mem_base;
+ sandbox_cfg_t *el = NULL;
+
+ // get total number of bytes required to mmap
+ for (el = cfg; el != NULL; el = el->next) {
+ pr_mem_size += strlen((char*) ((smp_param_t*)el->param)->value) + 1;
+ }
+
+ // allocate protected memory with MALLOC_MP_LIM canary
+ pr_mem_base = (char*) mmap(NULL, MALLOC_MP_LIM + pr_mem_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (pr_mem_base == MAP_FAILED) {
+ log_err(LD_BUG,"(Sandbox) failed allocate protected memory! mmap: %s",
+ strerror(errno));
+ ret = -1;
+ goto out;
+ }
+
+ pr_mem_next = pr_mem_base + MALLOC_MP_LIM;
+ pr_mem_left = pr_mem_size;
+
+ // change el value pointer to protected
+ for (el = cfg; el != NULL; el = el->next) {
+ char *param_val = (char*)((smp_param_t *)el->param)->value;
+ size_t param_size = strlen(param_val) + 1;
+
+ if (pr_mem_left >= param_size) {
+ // copy to protected
+ memcpy(pr_mem_next, param_val, param_size);
+
+ // re-point el parameter to protected
+ {
+ void *old_val = (void *) ((smp_param_t*)el->param)->value;
+ tor_free(old_val);
+ }
+ ((smp_param_t*)el->param)->value = (intptr_t) pr_mem_next;
+ ((smp_param_t*)el->param)->prot = 1;
+
+ // move next available protected memory
+ pr_mem_next += param_size;
+ pr_mem_left -= param_size;
+ } else {
+ log_err(LD_BUG,"(Sandbox) insufficient protected memory!");
+ ret = -2;
+ goto out;
+ }
+ }
+
+ // protecting from writes
+ if (mprotect(pr_mem_base, MALLOC_MP_LIM + pr_mem_size, PROT_READ)) {
+ log_err(LD_BUG,"(Sandbox) failed to protect memory! mprotect: %s",
+ strerror(errno));
+ ret = -3;
+ goto out;
+ }
+
+ /*
+ * Setting sandbox restrictions so the string memory cannot be tampered with
+ */
+ // no mremap of the protected base address
+ ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) mremap protected memory filter fail!");
+ return ret;
+ }
+
+ // no munmap of the protected base address
+ ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, (intptr_t) pr_mem_base));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) munmap protected memory filter fail!");
+ return ret;
+ }
+
+ /*
+ * Allow mprotect with PROT_READ|PROT_WRITE because openssl uses it, but
+ * never over the memory region used by the protected strings.
+ *
+ * PROT_READ|PROT_WRITE was originally fully allowed in sb_mprotect(), but
+ * had to be removed due to limitation of libseccomp regarding intervals.
+ *
+ * There is a restriction on how much you can mprotect with R|W up to the
+ * size of the canary.
+ */
+ ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2,
+ SCMP_CMP(0, SCMP_CMP_LT, (intptr_t) pr_mem_base),
+ SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM),
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (LT)!");
+ return ret;
+ }
+
+ ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2,
+ SCMP_CMP(0, SCMP_CMP_GT, (intptr_t) pr_mem_base + pr_mem_size +
+ MALLOC_MP_LIM),
+ SCMP_CMP(1, SCMP_CMP_LE, MALLOC_MP_LIM),
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ|PROT_WRITE));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) mprotect protected memory filter fail (GT)!");
+ return ret;
+ }
+
+ out:
+ return ret;
+}
+
+/**
+ * Auxiliary function used in order to allocate a sandbox_cfg_t element and set
+ * it's values according the the parameter list. All elements are initialised
+ * with the 'prot' field set to false, as the pointer is not protected at this
+ * point.
+ */
+static sandbox_cfg_t*
+new_element(int syscall, int index, intptr_t value)
+{
+ smp_param_t *param = NULL;
+
+ sandbox_cfg_t *elem = tor_malloc(sizeof(sandbox_cfg_t));
+ elem->param = tor_malloc(sizeof(smp_param_t));
+
+ param = elem->param;
+ param->syscall = syscall;
+ param->pindex = index;
+ param->value = value;
+ param->prot = 0;
+
+ return elem;
+}
+
+#ifdef __NR_stat64
+#define SCMP_stat SCMP_SYS(stat64)
+#else
+#define SCMP_stat SCMP_SYS(stat)
+#endif
+
+int
+sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file, int fr)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_stat, 0, (intptr_t)(void*) tor_strdup(file));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ if (fr) tor_free(file);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+ int fr = va_arg(ap, int);
+
+ rc = sandbox_cfg_allow_stat_filename(cfg, fn, fr);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_stat_filename_array fail");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file, int fr)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(open), 0, (intptr_t)(void *)tor_strdup(file));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ if (fr) tor_free(file);
+
+ return 0;
+}
+
+int
+sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+ int fr = va_arg(ap, int);
+
+ rc = sandbox_cfg_allow_open_filename(cfg, fn, fr);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_open_filename_array fail");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file, int fr)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(openat), 1, (intptr_t)(void *)tor_strdup(file));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ if (fr) tor_free(file);
+
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+ int fr = va_arg(ap, int);
+
+ rc = sandbox_cfg_allow_openat_filename(cfg, fn, fr);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_openat_filename_array fail");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(execve), 1, (intptr_t)(void *)tor_strdup(com));
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...)
+{
+ int rc = 0;
+ char *fn = NULL;
+
+ va_list ap;
+ va_start(ap, cfg);
+
+ while ((fn = va_arg(ap, char*)) != NULL) {
+
+ rc = sandbox_cfg_allow_execve(cfg, fn);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_execve_array failed");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+int
+sandbox_getaddrinfo(const char *name, const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ sb_addr_info_t *el;
+
+ if (servname != NULL)
+ return -1;
+
+ *res = NULL;
+
+ for (el = sb_addr_info; el; el = el->next) {
+ if (!strcmp(el->name, name)) {
+ *res = tor_malloc(sizeof(struct addrinfo));
+
+ memcpy(*res, el->info, sizeof(struct addrinfo));
+ /* XXXX What if there are multiple items in the list? */
+ return 0;
+ }
+ }
+
+ if (!sandbox_active) {
+ if (getaddrinfo(name, NULL, hints, res)) {
+ log_err(LD_BUG,"(Sandbox) getaddrinfo failed!");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ // getting here means something went wrong
+ log_err(LD_BUG,"(Sandbox) failed to get address %s!", name);
+ if (*res) {
+ tor_free(*res);
+ res = NULL;
+ }
+ return -1;
+}
+
+int
+sandbox_add_addrinfo(const char* name)
+{
+ int ret;
+ struct addrinfo hints;
+ sb_addr_info_t *el = NULL;
+
+ el = tor_malloc(sizeof(sb_addr_info_t));
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+
+ ret = getaddrinfo(name, NULL, &hints, &(el->info));
+ if (ret) {
+ log_err(LD_BUG,"(Sandbox) failed to getaddrinfo");
+ ret = -2;
+ tor_free(el);
+ goto out;
+ }
+
+ el->name = tor_strdup(name);
+ el->next = sb_addr_info;
+ sb_addr_info = el;
+
+ out:
+ return ret;
+}
+
+/**
+ * Function responsible for going through the parameter syscall filters and
+ * call each function pointer in the list.
+ */
+static int
+add_param_filter(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
+{
+ unsigned i;
+ int rc = 0;
+
+ // function pointer
+ for (i = 0; i < ARRAY_LENGTH(filter_func); i++) {
+ if ((filter_func[i])(ctx, cfg)) {
+ log_err(LD_BUG,"(Sandbox) failed to add syscall %d, received libseccomp "
+ "error %d", i, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible of loading the libseccomp syscall filters which do not
+ * have parameter filtering.
+ */
+static int
+add_noparam_filter(scmp_filter_ctx ctx)
+{
+ unsigned i;
+ int rc = 0;
+
+ // add general filters
+ for (i = 0; i < ARRAY_LENGTH(filter_nopar_gen); i++) {
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, filter_nopar_gen[i], 0);
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add syscall index %d (NR=%d), "
+ "received libseccomp error %d", i, filter_nopar_gen[i], rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * 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_syscall_filter(sandbox_cfg_t* cfg)
+{
+ int rc = 0;
+ 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;
+ }
+
+ // protectign sandbox parameter strings
+ if ((rc = prot_strings(ctx, cfg))) {
+ goto end;
+ }
+
+ // add parameter filters
+ if ((rc = add_param_filter(ctx, cfg))) {
+ log_err(LD_BUG, "(Sandbox) failed to add param filters!");
+ goto end;
+ }
+
+ // adding filters with no parameters
+ if ((rc = add_noparam_filter(ctx))) {
+ log_err(LD_BUG, "(Sandbox) failed to add param filters!");
+ goto end;
+ }
+
+ // loading the seccomp2 filter
+ if ((rc = seccomp_load(ctx))) {
+ log_err(LD_BUG, "(Sandbox) failed to load!");
+ goto end;
+ }
+
+ // marking the sandbox as active
+ sandbox_active = 1;
+
+ end:
+ seccomp_release(ctx);
+ return (rc < 0 ? -rc : rc);
+}
+
+/**
+ * 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 number[32];
+ int syscall;
+ (void) nr;
+
+ if (info->si_code != SYS_SECCOMP)
+ return;
+
+ if (!ctx)
+ return;
+
+ syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
+
+ format_dec_number_sigsafe(syscall, number, sizeof(number));
+ tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ",
+ number,
+ ")\n",
+ NULL);
+
+#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;
+}
+
+/**
+ * Function responsible of registering the sandbox_cfg_t list of parameter
+ * syscall filters to the existing parameter list. This is used for incipient
+ * multiple-sandbox support.
+ */
+static int
+register_cfg(sandbox_cfg_t* cfg)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ if (filter_dynamic == NULL) {
+ filter_dynamic = cfg;
+ return 0;
+ }
+
+ for (elem = filter_dynamic; elem->next != NULL; elem = elem->next);
+
+ elem->next = cfg;
+
+ 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(sandbox_cfg_t* cfg)
+{
+ if (install_sigsys_debugging())
+ return -1;
+
+ if (install_syscall_filter(cfg))
+ return -2;
+
+ if (register_cfg(cfg))
+ return -3;
+
+ return 0;
+}
+
+#endif // USE_LIBSECCOMP
+
+sandbox_cfg_t*
+sandbox_cfg_new(void)
+{
+ return NULL;
+}
+
+int
+sandbox_init(sandbox_cfg_t *cfg)
+{
+#if defined(USE_LIBSECCOMP)
+ return initialise_libseccomp_sandbox(cfg);
+
+#elif defined(_WIN32)
+ (void)cfg;
+ log_warn(LD_BUG,"Windows sandboxing is not implemented. The feature is "
+ "currently disabled.");
+ return 0;
+
+#elif defined(TARGET_OS_MAC)
+ (void)cfg;
+ log_warn(LD_BUG,"Mac OSX sandboxing is not implemented. The feature is "
+ "currently disabled");
+ return 0;
+#else
+ (void)cfg;
+ log_warn(LD_BUG,"Sandboxing is not implemented for your platform. The "
+ "feature is currently disabled");
+ return 0;
+#endif
+}
+
+#ifndef USE_LIBSECCOMP
+int
+sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file,
+ int fr)
+{
+ (void)cfg; (void)file; (void)fr;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr)
+{
+ (void)cfg; (void)file; (void)fr;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
+{
+ (void)cfg; (void)com;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr)
+{
+ (void)cfg; (void)file; (void)fr;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+#endif
+
diff --git a/src/common/sandbox.h b/src/common/sandbox.h
new file mode 100644
index 000000000..d64d427d3
--- /dev/null
+++ b/src/common/sandbox.h
@@ -0,0 +1,247 @@
+/* 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_
+
+#include "orconfig.h"
+#include "torint.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
+
+#if defined(HAVE_SECCOMP_H) && defined(__linux__)
+#define USE_LIBSECCOMP
+#endif
+
+struct sandbox_cfg_elem;
+
+/** Typedef to structure used to manage a sandbox configuration. */
+typedef struct sandbox_cfg_elem sandbox_cfg_t;
+
+/**
+ * Linux definitions
+ */
+#ifdef USE_LIBSECCOMP
+
+#ifndef __USE_GNU
+#define __USE_GNU
+#endif
+#include <sys/ucontext.h>
+#include <seccomp.h>
+#include <netdb.h>
+
+#define PARAM_PTR 0
+#define PARAM_NUM 1
+
+/**
+ * Enum used to manage the type of the implementation for general purpose.
+ */
+typedef enum {
+ /** Libseccomp implementation based on seccomp2*/
+ LIBSECCOMP2 = 0
+} SB_IMPL;
+
+/**
+ * Configuration parameter structure associated with the LIBSECCOMP2
+ * implementation.
+ */
+typedef struct smp_param {
+ /** syscall associated with parameter. */
+ int syscall;
+
+ /** parameter index. */
+ int pindex;
+ /** parameter value. */
+ intptr_t value;
+
+ /** parameter flag (0 = not protected, 1 = protected). */
+ int prot;
+} smp_param_t;
+
+/**
+ * Structure used to manage a sandbox configuration.
+ *
+ * It is implemented as a linked list of parameters. Currently only controls
+ * parameters for open, openat, execve, stat64.
+ */
+struct sandbox_cfg_elem {
+ /** Sandbox implementation which dictates the parameter type. */
+ SB_IMPL implem;
+
+ /** Configuration parameter. */
+ void *param;
+
+ /** Next element of the configuration*/
+ struct sandbox_cfg_elem *next;
+};
+
+/**
+ * Structure used for keeping a linked list of getaddrinfo pre-recorded
+ * results.
+ */
+struct sb_addr_info_el {
+ /** Name of the address info result. */
+ char *name;
+ /** Pre-recorded getaddrinfo result. */
+ struct addrinfo *info;
+ /** Next element in the list. */
+ struct sb_addr_info_el *next;
+};
+/** Typedef to structure used to manage an addrinfo list. */
+typedef struct sb_addr_info_el sb_addr_info_t;
+
+/** Function pointer defining the prototype of a filter function.*/
+typedef int (*sandbox_filter_func_t)(scmp_filter_ctx ctx,
+ sandbox_cfg_t *filter);
+
+/** Type that will be used in step 3 in order to manage multiple sandboxes.*/
+typedef struct {
+ /** function pointers associated with the filter */
+ sandbox_filter_func_t *filter_func;
+
+ /** filter function pointer parameters */
+ sandbox_cfg_t *filter_dynamic;
+} sandbox_t;
+
+/**
+ * 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 // USE_LIBSECCOMP
+
+#ifdef USE_LIBSECCOMP
+/** Pre-calls getaddrinfo in order to pre-record result. */
+int sandbox_add_addrinfo(const char *addr);
+
+struct addrinfo;
+/** Replacement for getaddrinfo(), using pre-recorded results. */
+int sandbox_getaddrinfo(const char *name, const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+#else
+#define sandbox_getaddrinfo(name, servname, hints, res) \
+ getaddrinfo((name),(servname), (hints),(res))
+#define sandbox_add_addrinfo(name) \
+ ((void)(name))
+#endif
+
+#ifdef USE_LIBSECCOMP
+/** Returns a registered protected string used with the sandbox, given that
+ * it matches the parameter.
+ */
+const char* sandbox_intern_string(const char *param);
+#else
+#define sandbox_intern_string(s) (s)
+#endif
+
+/** Creates an empty sandbox configuration file.*/
+sandbox_cfg_t * sandbox_cfg_new(void);
+
+/**
+ * Function used to add a open allowed filename to a supplied configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file,
+ int fr);
+
+/** Function used to add a series of open allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...);
+
+/**
+ * Function used to add a openat allowed filename to a supplied configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr);
+
+/** Function used to add a series of openat allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...);
+
+/**
+ * Function used to add a execve allowed filename to a supplied configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com);
+
+/** Function used to add a series of execve allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...);
+
+/**
+ * Function used to add a stat/stat64 allowed filename to a configuration.
+ * The (char*) specifies the path to the allowed file, fr = 1 tells the
+ * function that the char* needs to be free-ed, 0 means the pointer does not
+ * need to be free-ed.
+ */
+int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file,
+ int fr);
+
+/** Function used to add a series of stat64 allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
+ * the char* specifies the path to the allowed file, 1 tells the function
+ * that the char* needs to be free-ed, 0 means the pointer does not need to
+ * be free-ed; the final parameter needs to be <NULL, 0>.
+ */
+int sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...);
+
+/** Function used to initialise a sandbox configuration.*/
+int sandbox_init(sandbox_cfg_t* cfg);
+
+#endif /* SANDBOX_H_ */
+
diff --git a/src/common/testsupport.h b/src/common/testsupport.h
new file mode 100644
index 000000000..4a4f50b69
--- /dev/null
+++ b/src/common/testsupport.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_TESTSUPPORT_H
+#define TOR_TESTSUPPORT_H
+
+#ifdef TOR_UNIT_TESTS
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+/** Quick and dirty macros to implement test mocking.
+ *
+ * To use them, suppose that you have a function you'd like to mock
+ * with the signature "void writebuf(size_t n, char *buf)". You can then
+ * declare the function as:
+ *
+ * MOCK_DECL(void, writebuf, (size_t n, char *buf));
+ *
+ * and implement it as:
+ *
+ * MOCK_IMPL(void
+ * writebuf,(size_t n, char *buf)
+ * {
+ * ...
+ * }
+ *
+ * For the non-testing build, this will expand simply into:
+ *
+ * void writebuf(size_t n, char *buf);
+ * void
+ * writebuf(size_t n, char *buf)
+ * {
+ * ...
+ * }
+ *
+ * But for the testing case, it will expand into:
+ *
+ * void writebuf__real(size_t n, char *buf);
+ * extern void (*writebuf)(size_t n, char *buf);
+ *
+ * void (*writebuf)(size_t n, char *buf) = writebuf__real;
+ * void
+ * writebuf__real(size_t n, char *buf)
+ * {
+ * ...
+ * }
+ *
+ * This is not a great mocking system! It is deliberately "the simplest
+ * thing that could work", and pays for its simplicity in its lack of
+ * features, and in its uglification of the Tor code. Replacing it with
+ * something clever would be a fine thing.
+ *
+ * @{ */
+#ifdef TOR_UNIT_TESTS
+#define MOCK_DECL(rv, funcname, arglist) \
+ rv funcname ##__real arglist; \
+ extern rv(*funcname) arglist
+#define MOCK_IMPL(rv, funcname, arglist) \
+ rv(*funcname) arglist = funcname ##__real; \
+ rv funcname ##__real arglist
+#define MOCK(func, replacement) \
+ do { \
+ (func) = (replacement); \
+ } while (0)
+#define UNMOCK(func) \
+ do { \
+ func = func ##__real; \
+ } while (0)
+#else
+#define MOCK_DECL(rv, funcname, arglist) \
+ rv funcname arglist
+#define MOCK_IMPL(rv, funcname, arglist) \
+ rv funcname arglist
+#endif
+/** @} */
+
+#endif
+
diff --git a/src/common/torgzip.c b/src/common/torgzip.c
index 4328c63c8..15451ee30 100644
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@ -68,6 +68,22 @@ is_gzip_supported(void)
return gzip_is_supported;
}
+/** Return a string representation of the version of the currently running
+ * version of zlib. */
+const char *
+tor_zlib_get_version_str(void)
+{
+ return zlibVersion();
+}
+
+/** Return a string representation of the version of the version of zlib
+* used at compilation. */
+const char *
+tor_zlib_get_header_version_str(void)
+{
+ return ZLIB_VERSION;
+}
+
/** Return the 'bits' value to tell zlib to use <b>method</b>.*/
static INLINE int
method_bits(compress_method_t method)
diff --git a/src/common/torgzip.h b/src/common/torgzip.h
index be1016445..5db03fe6e 100644
--- a/src/common/torgzip.h
+++ b/src/common/torgzip.h
@@ -32,6 +32,12 @@ tor_gzip_uncompress(char **out, size_t *out_len,
int is_gzip_supported(void);
+const char *
+tor_zlib_get_version_str(void);
+
+const char *
+tor_zlib_get_header_version_str(void);
+
compress_method_t detect_compression_method(const char *in, size_t in_len);
/** Return values from tor_zlib_process; see that function's documentation for
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 8675d7b6e..d210c8b24 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -102,6 +102,9 @@
/** This log message is not safe to send to a callback-based logger
* immediately. Used as a flag, not a log domain. */
#define LD_NOCB (1u<<31)
+/** This log message should not include a function name, even if it otherwise
+ * would. Used as a flag, not a log domain. */
+#define LD_NOFUNCNAME (1u<<30)
/** Mask of zero or more log domains, OR'd together. */
typedef uint32_t log_domain_mask_t;
@@ -114,12 +117,6 @@ typedef struct log_severity_list_t {
log_domain_mask_t masks[LOG_DEBUG-LOG_ERR+1];
} log_severity_list_t;
-#ifdef LOG_PRIVATE
-/** Given a severity, yields an index into log_severity_list_t.masks to use
- * for that severity. */
-#define SEVERITY_MASK_IDX(sev) ((sev) - LOG_ERR)
-#endif
-
/** Callback type used for add_callback_log. */
typedef void (*log_callback)(int severity, uint32_t domain, const char *msg);
@@ -154,6 +151,10 @@ void set_log_time_granularity(int granularity_msec);
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
CHECK_PRINTF(3,4);
+void tor_log_err_sigsafe(const char *m, ...);
+int tor_log_get_sigsafe_err_fds(const int **out);
+void tor_log_update_sigsafe_err_fds(void);
+
#if defined(__GNUC__) || defined(RUNNING_DOXYGEN)
extern int log_global_min_severity_;
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 60444f1b8..3d6efeba0 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -48,9 +48,6 @@
#include "compat_libevent.h"
#endif
-#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */
-#define TORTLS_PRIVATE
-
#include "crypto.h"
#include "tortls.h"
#include "util.h"
@@ -806,24 +803,24 @@ tor_cert_new(X509 *x509_cert)
tor_cert_t *cert;
EVP_PKEY *pkey;
RSA *rsa;
- int length, length2;
- unsigned char *cp;
+ int length;
+ unsigned char *buf = NULL;
if (!x509_cert)
return NULL;
- length = i2d_X509(x509_cert, NULL);
+ length = i2d_X509(x509_cert, &buf);
cert = tor_malloc_zero(sizeof(tor_cert_t));
- if (length <= 0) {
+ if (length <= 0 || buf == NULL) {
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
X509_free(x509_cert);
return NULL;
}
cert->encoded_len = (size_t) length;
- cp = cert->encoded = tor_malloc(length);
- length2 = i2d_X509(x509_cert, &cp);
- tor_assert(length2 == length);
+ cert->encoded = tor_malloc(length);
+ memcpy(cert->encoded, buf, length);
+ OPENSSL_free(buf);
cert->cert = x509_cert;
@@ -979,31 +976,6 @@ tor_tls_cert_get_key(tor_cert_t *cert)
return result;
}
-/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */
-static int
-pkey_eq(EVP_PKEY *a, EVP_PKEY *b)
-{
- /* We'd like to do this, but openssl 0.9.7 doesn't have it:
- return EVP_PKEY_cmp(a,b) == 1;
- */
- unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr;
- int a_len1, b_len1, a_len2, b_len2, result;
- a_len1 = i2d_PublicKey(a, NULL);
- b_len1 = i2d_PublicKey(b, NULL);
- if (a_len1 != b_len1)
- return 0;
- a_ptr = a_enc = tor_malloc(a_len1);
- b_ptr = b_enc = tor_malloc(b_len1);
- a_len2 = i2d_PublicKey(a, &a_ptr);
- b_len2 = i2d_PublicKey(b, &b_ptr);
- tor_assert(a_len2 == a_len1);
- tor_assert(b_len2 == b_len1);
- result = tor_memeq(a_enc, b_enc, a_len1);
- tor_free(a_enc);
- tor_free(b_enc);
- return result;
-}
-
/** Return true iff the other side of <b>tls</b> has authenticated to us, and
* the key certified in <b>cert</b> is the same as the key they used to do it.
*/
@@ -1019,7 +991,7 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
link_key = X509_get_pubkey(peercert);
cert_key = X509_get_pubkey(cert->cert);
- result = link_key && cert_key && pkey_eq(cert_key, link_key);
+ result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1;
X509_free(peercert);
if (link_key)
@@ -1418,6 +1390,21 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
return NULL;
}
+/** Invoked when a TLS state changes: log the change at severity 'debug' */
+static void
+tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
+{
+ log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
+ ssl, SSL_state_string_long(ssl), type, val);
+}
+
+/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
+const char *
+tor_tls_get_ciphersuite_name(tor_tls_t *tls)
+{
+ return SSL_get_cipher(tls->ssl);
+}
+
#ifdef V2_HANDSHAKE_SERVER
/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
@@ -1486,13 +1473,6 @@ prune_v2_cipher_list(void)
v2_cipher_list_pruned = 1;
}
-/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
-const char *
-tor_tls_get_ciphersuite_name(tor_tls_t *tls)
-{
- return SSL_get_cipher(tls->ssl);
-}
-
/** Examine the client cipher list in <b>ssl</b>, and determine what kind of
* client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
* CIPHERS_UNRESTRICTED.
@@ -1591,56 +1571,6 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2;
}
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
-/** Callback to get invoked on a server after we've read the list of ciphers
- * the client supports, but before we pick our own ciphersuite.
- *
- * We can't abuse an info_cb for this, since by the time one of the
- * client_hello info_cbs is called, we've already picked which ciphersuite to
- * use.
- *
- * Technically, this function is an abuse of this callback, since the point of
- * a session_secret_cb is to try to set up and/or verify a shared-secret for
- * authentication on the fly. But as long as we return 0, we won't actually be
- * setting up a shared secret, and all will be fine.
- */
-static int
-tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
- STACK_OF(SSL_CIPHER) *peer_ciphers,
- SSL_CIPHER **cipher, void *arg)
-{
- (void) secret;
- (void) secret_len;
- (void) peer_ciphers;
- (void) cipher;
- (void) arg;
-
- if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) ==
- CIPHERS_UNRESTRICTED) {
- SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST);
- }
-
- SSL_set_session_secret_cb(ssl, NULL, NULL);
-
- return 0;
-}
-static void
-tor_tls_setup_session_secret_cb(tor_tls_t *tls)
-{
- SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
-}
-#else
-#define tor_tls_setup_session_secret_cb(tls) STMT_NIL
-#endif
-
-/** Invoked when a TLS state changes: log the change at severity 'debug' */
-static void
-tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
-{
- log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
- ssl, SSL_state_string_long(ssl), type, val);
-}
-
/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
* changes state. We use this:
* <ul><li>To alter the state of the handshake partway through, so we
@@ -1700,6 +1630,48 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
}
#endif
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
+/** Callback to get invoked on a server after we've read the list of ciphers
+ * the client supports, but before we pick our own ciphersuite.
+ *
+ * We can't abuse an info_cb for this, since by the time one of the
+ * client_hello info_cbs is called, we've already picked which ciphersuite to
+ * use.
+ *
+ * Technically, this function is an abuse of this callback, since the point of
+ * a session_secret_cb is to try to set up and/or verify a shared-secret for
+ * authentication on the fly. But as long as we return 0, we won't actually be
+ * setting up a shared secret, and all will be fine.
+ */
+static int
+tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ SSL_CIPHER **cipher, void *arg)
+{
+ (void) secret;
+ (void) secret_len;
+ (void) peer_ciphers;
+ (void) cipher;
+ (void) arg;
+
+ if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) ==
+ CIPHERS_UNRESTRICTED) {
+ SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST);
+ }
+
+ SSL_set_session_secret_cb(ssl, NULL, NULL);
+
+ return 0;
+}
+static void
+tor_tls_setup_session_secret_cb(tor_tls_t *tls)
+{
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
+}
+#else
+#define tor_tls_setup_session_secret_cb(tls) STMT_NIL
+#endif
+
/** Explain which ciphers we're missing. */
static void
log_unsupported_ciphers(smartlist_t *unsupported)
diff --git a/src/common/util.c b/src/common/util.c
index 5eb0f9a69..054de3dbe 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -24,6 +24,8 @@
#include "torint.h"
#include "container.h"
#include "address.h"
+#include "sandbox.h"
+#include "backtrace.h"
#ifdef _WIN32
#include <io.h>
@@ -94,6 +96,23 @@
#endif
/* =====
+ * Assertion helper.
+ * ===== */
+/** Helper for tor_assert: report the assertion failure. */
+void
+tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr)
+{
+ char buf[256];
+ log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.",
+ fname, line, func, expr);
+ tor_snprintf(buf, sizeof(buf),
+ "Assertion %s failed in %s at %s:%u",
+ expr, func, fname, line);
+ log_backtrace(LOG_ERR, LD_BUG, buf);
+}
+
+/* =====
* Memory management
* ===== */
#ifdef USE_DMALLOC
@@ -879,6 +898,39 @@ tor_digest_is_zero(const char *digest)
return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN);
}
+/** Return true if <b>string</b> is a valid '<key>=[<value>]' string.
+ * <value> is optional, to indicate the empty string. Log at logging
+ * <b>severity</b> if something ugly happens. */
+int
+string_is_key_value(int severity, const char *string)
+{
+ /* position of equal sign in string */
+ const char *equal_sign_pos = NULL;
+
+ tor_assert(string);
+
+ if (strlen(string) < 2) { /* "x=" is shortest args string */
+ tor_log(severity, LD_GENERAL, "'%s' is too short to be a k=v value.",
+ escaped(string));
+ return 0;
+ }
+
+ equal_sign_pos = strchr(string, '=');
+ if (!equal_sign_pos) {
+ tor_log(severity, LD_GENERAL, "'%s' is not a k=v value.", escaped(string));
+ return 0;
+ }
+
+ /* validate that the '=' is not in the beginning of the string. */
+ if (equal_sign_pos == string) {
+ tor_log(severity, LD_GENERAL, "'%s' is not a valid k=v value.",
+ escaped(string));
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return true iff the DIGEST256_LEN bytes in digest are all zero. */
int
tor_digest256_is_zero(const char *digest)
@@ -1190,6 +1242,43 @@ escaped(const char *s)
return escaped_val_;
}
+/** Return a newly allocated string equal to <b>string</b>, except that every
+ * character in <b>chars_to_escape</b> is preceded by a backslash. */
+char *
+tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape)
+{
+ char *new_string = NULL;
+ char *new_cp = NULL;
+ size_t length, new_length;
+
+ tor_assert(string);
+
+ length = strlen(string);
+
+ if (!length) /* If we were given the empty string, return the same. */
+ return tor_strdup("");
+ /* (new_length > SIZE_MAX) => ((length * 2) + 1 > SIZE_MAX) =>
+ (length*2 > SIZE_MAX - 1) => (length > (SIZE_MAX - 1)/2) */
+ if (length > (SIZE_MAX - 1)/2) /* check for overflow */
+ return NULL;
+
+ /* this should be enough even if all characters must be escaped */
+ new_length = (length * 2) + 1;
+
+ new_string = new_cp = tor_malloc(new_length);
+
+ while (*string) {
+ if (strchr(chars_to_escape, *string))
+ *new_cp++ = '\\';
+
+ *new_cp++ = *string++;
+ }
+
+ *new_cp = '\0'; /* NUL-terminate the new string */
+
+ return new_string;
+}
+
/* =====
* Time
* ===== */
@@ -1741,7 +1830,7 @@ file_status(const char *fname)
int r;
f = tor_strdup(fname);
clean_name_for_stat(f);
- r = stat(f, &st);
+ r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
if (errno == ENOENT) {
@@ -1791,7 +1880,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
tor_assert(dirname);
f = tor_strdup(dirname);
clean_name_for_stat(f);
- r = stat(f, &st);
+ r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
if (errno != ENOENT) {
@@ -1977,8 +2066,10 @@ start_writing_to_file(const char *fname, int open_flags, int mode,
open_flags &= ~O_EXCL;
new_file->rename_on_close = 1;
}
+#if O_BINARY != 0
if (open_flags & O_BINARY)
new_file->binary = 1;
+#endif
new_file->fd = tor_open_cloexec(open_name, open_flags, mode);
if (new_file->fd < 0) {
@@ -2132,12 +2223,20 @@ write_chunks_to_file_impl(const char *fname, const smartlist_t *chunks,
return -1;
}
-/** Given a smartlist of sized_chunk_t, write them atomically to a file
- * <b>fname</b>, overwriting or creating the file as necessary. */
+/** Given a smartlist of sized_chunk_t, write them to a file
+ * <b>fname</b>, overwriting or creating the file as necessary.
+ * If <b>no_tempfile</b> is 0 then the file will be written
+ * atomically. */
int
-write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin)
+write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin,
+ int no_tempfile)
{
int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT);
+
+ if (no_tempfile) {
+ /* O_APPEND stops write_chunks_to_file from using tempfiles */
+ flags |= O_APPEND;
+ }
return write_chunks_to_file_impl(fname, chunks, flags);
}
@@ -2158,9 +2257,9 @@ write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
/** As write_str_to_file, but does not assume a NUL-terminated
* string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
-int
-write_bytes_to_file(const char *fname, const char *str, size_t len,
- int bin)
+MOCK_IMPL(int,
+write_bytes_to_file,(const char *fname, const char *str, size_t len,
+ int bin))
{
return write_bytes_to_file_impl(fname, str, len,
OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));
@@ -3022,9 +3121,10 @@ tor_listdir(const char *dirname)
FindClose(handle);
tor_free(pattern);
#else
+ const char *prot_dname = sandbox_intern_string(dirname);
DIR *d;
struct dirent *de;
- if (!(d = opendir(dirname)))
+ if (!(d = opendir(prot_dname)))
return NULL;
result = smartlist_new();
@@ -3320,14 +3420,59 @@ tor_join_win_cmdline(const char *argv[])
return joined_argv;
}
+/* As format_{hex,dex}_number_sigsafe, but takes a <b>radix</b> argument
+ * in range 2..16 inclusive. */
+static int
+format_number_sigsafe(unsigned long x, char *buf, int buf_len,
+ unsigned int radix)
+{
+ unsigned long tmp;
+ int len;
+ char *cp;
+
+ /* NOT tor_assert. This needs to be safe to run from within a signal handler,
+ * and from within the 'tor_assert() has failed' code. */
+ if (radix < 2 || radix > 16)
+ return 0;
+
+ /* Count how many digits we need. */
+ tmp = x;
+ len = 1;
+ while (tmp >= radix) {
+ tmp /= radix;
+ ++len;
+ }
+
+ /* Not long enough */
+ if (!buf || len >= buf_len)
+ return 0;
+
+ cp = buf + len;
+ *cp = '\0';
+ do {
+ unsigned digit = x % radix;
+ tor_assert(cp > buf);
+ --cp;
+ *cp = "0123456789ABCDEF"[digit];
+ x /= radix;
+ } while (x);
+
+ /* NOT tor_assert; see above. */
+ if (cp != buf) {
+ abort();
+ }
+
+ return len;
+}
+
/**
- * Helper function to output hex numbers, called by
- * format_helper_exit_status(). This writes the hexadecimal digits of x into
- * buf, up to max_len digits, and returns the actual number of digits written.
- * If there is insufficient space, it will write nothing and return 0.
+ * Helper function to output hex numbers from within a signal handler.
+ *
+ * Writes the nul-terminated hexadecimal digits of <b>x</b> into a buffer
+ * <b>buf</b> of size <b>buf_len</b>, and return the actual number of digits
+ * written, not counting the terminal NUL.
*
- * This function DOES NOT add a terminating NUL character to its output: be
- * careful!
+ * If there is insufficient space, write nothing and return 0.
*
* This accepts an unsigned int because format_helper_exit_status() needs to
* call it with a signed int and an unsigned char, and since the C standard
@@ -3342,46 +3487,19 @@ tor_join_win_cmdline(const char *argv[])
* arbitrary C functions.
*/
int
-format_hex_number_for_helper_exit_status(unsigned int x, char *buf,
- int max_len)
+format_hex_number_sigsafe(unsigned long x, char *buf, int buf_len)
{
- int len;
- unsigned int tmp;
- char *cur;
-
- /* Sanity check */
- if (!buf || max_len <= 0)
- return 0;
-
- /* How many chars do we need for x? */
- if (x > 0) {
- len = 0;
- tmp = x;
- while (tmp > 0) {
- tmp >>= 4;
- ++len;
- }
- } else {
- len = 1;
- }
-
- /* Bail if we would go past the end of the buffer */
- if (len > max_len)
- return 0;
-
- /* Point to last one */
- cur = buf + len - 1;
-
- /* Convert x to hex */
- do {
- *cur-- = "0123456789ABCDEF"[x & 0xf];
- x >>= 4;
- } while (x != 0 && cur >= buf);
+ return format_number_sigsafe(x, buf, buf_len, 16);
+}
- /* Return len */
- return len;
+/** As format_hex_number_sigsafe, but format the number in base 10. */
+int
+format_dec_number_sigsafe(unsigned long x, char *buf, int buf_len)
+{
+ return format_number_sigsafe(x, buf, buf_len, 10);
}
+#ifndef _WIN32
/** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in
* <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler
* safe.
@@ -3397,7 +3515,7 @@ format_hex_number_for_helper_exit_status(unsigned int x, char *buf,
* On success return the number of characters added to hex_errno, not counting
* the terminating NUL; return -1 on error.
*/
-int
+STATIC int
format_helper_exit_status(unsigned char child_state, int saved_errno,
char *hex_errno)
{
@@ -3428,8 +3546,8 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
cur = hex_errno;
/* Emit child_state */
- written = format_hex_number_for_helper_exit_status(child_state,
- cur, left);
+ written = format_hex_number_sigsafe(child_state, cur, left);
+
if (written <= 0)
goto err;
@@ -3458,8 +3576,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
}
/* Emit unsigned_errno */
- written = format_hex_number_for_helper_exit_status(unsigned_errno,
- cur, left);
+ written = format_hex_number_sigsafe(unsigned_errno, cur, left);
if (written <= 0)
goto err;
@@ -3490,6 +3607,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
done:
return res;
}
+#endif
/* Maximum number of file descriptors, if we cannot get it via sysconf() */
#define DEFAULT_MAX_FD 256
@@ -3685,7 +3803,7 @@ tor_spawn_background(const char *const filename, const char **argv,
TRUE, // handles are inherited
/*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
* work?) */
- 0, // creation flags
+ CREATE_NO_WINDOW, // creation flags
(env==NULL) ? NULL : env->windows_environment_block,
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
@@ -3906,9 +4024,9 @@ tor_spawn_background(const char *const filename, const char **argv,
* <b>process_handle</b>.
* If <b>also_terminate_process</b> is true, also terminate the
* process of the process handle. */
-void
-tor_process_handle_destroy(process_handle_t *process_handle,
- int also_terminate_process)
+MOCK_IMPL(void,
+tor_process_handle_destroy,(process_handle_t *process_handle,
+ int also_terminate_process))
{
if (!process_handle)
return;
@@ -4417,9 +4535,9 @@ stream_status_to_string(enum stream_status stream_status)
/** Return a smartlist containing lines outputted from
* <b>handle</b>. Return NULL on error, and set
* <b>stream_status_out</b> appropriately. */
-smartlist_t *
-tor_get_lines_from_handle(HANDLE *handle,
- enum stream_status *stream_status_out)
+MOCK_IMPL(smartlist_t *,
+tor_get_lines_from_handle, (HANDLE *handle,
+ enum stream_status *stream_status_out))
{
int pos;
char stdout_buf[600] = {0};
@@ -4507,8 +4625,9 @@ log_from_handle(HANDLE *pipe, int severity)
/** Return a smartlist containing lines outputted from
* <b>handle</b>. Return NULL on error, and set
* <b>stream_status_out</b> appropriately. */
-smartlist_t *
-tor_get_lines_from_handle(FILE *handle, enum stream_status *stream_status_out)
+MOCK_IMPL(smartlist_t *,
+tor_get_lines_from_handle, (FILE *handle,
+ enum stream_status *stream_status_out))
{
enum stream_status stream_status;
char stdout_buf[400];
diff --git a/src/common/util.h b/src/common/util.h
index 73daa6e2a..18dc20639 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -15,6 +15,7 @@
#include "torint.h"
#include "compat.h"
#include "di_ops.h"
+#include "testsupport.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
@@ -47,13 +48,13 @@
/** Like assert(3), but send assertion failures to the log as well as to
* stderr. */
#define tor_assert(expr) STMT_BEGIN \
- if (PREDICT_UNLIKELY(!(expr))) { \
- log_err(LD_BUG, "%s:%d: %s: Assertion %s failed; aborting.", \
- SHORT_FILE__, __LINE__, __func__, #expr); \
- fprintf(stderr,"%s:%d %s: Assertion %s failed; aborting.\n", \
- SHORT_FILE__, __LINE__, __func__, #expr); \
- abort(); \
- } STMT_END
+ if (PREDICT_UNLIKELY(!(expr))) { \
+ tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
+ abort(); \
+ } STMT_END
+
+void tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr);
/* If we're building with dmalloc, we want all of our memory allocation
* functions to take an extra file/line pair of arguments. If not, not.
@@ -222,23 +223,22 @@ const char *find_whitespace_eos(const char *s, const char *eos);
const char *find_str_at_start_of_line(const char *haystack,
const char *needle);
int string_is_C_identifier(const char *string);
+int string_is_key_value(int severity, const char *string);
int tor_mem_is_zero(const char *mem, size_t len);
int tor_digest_is_zero(const char *digest);
int tor_digest256_is_zero(const char *digest);
char *esc_for_log(const char *string) ATTR_MALLOC;
const char *escaped(const char *string);
+
+char *tor_escape_str_for_pt_args(const char *string,
+ const char *chars_to_escape);
+
struct smartlist_t;
-int tor_vsscanf(const char *buf, const char *pattern, va_list ap)
-#ifdef __GNUC__
- __attribute__((format(scanf, 2, 0)))
-#endif
- ;
+int tor_vsscanf(const char *buf, const char *pattern, va_list ap) \
+ CHECK_SCANF(2, 0);
int tor_sscanf(const char *buf, const char *pattern, ...)
-#ifdef __GNUC__
- __attribute__((format(scanf, 2, 3)))
-#endif
- ;
+ CHECK_SCANF(2, 3);
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern, ...)
CHECK_PRINTF(2, 3);
@@ -356,8 +356,9 @@ FILE *fdopen_file(open_file_t *file_data);
int finish_writing_to_file(open_file_t *file_data);
int abort_writing_to_file(open_file_t *file_data);
int write_str_to_file(const char *fname, const char *str, int bin);
-int write_bytes_to_file(const char *fname, const char *str, size_t len,
- int bin);
+MOCK_DECL(int,
+write_bytes_to_file,(const char *fname, const char *str, size_t len,
+ int bin));
/** An ad-hoc type to hold a string of characters and a count; used by
* write_chunks_to_file. */
typedef struct sized_chunk_t {
@@ -365,7 +366,7 @@ typedef struct sized_chunk_t {
size_t len;
} sized_chunk_t;
int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks,
- int bin);
+ int bin, int no_tempfile);
int append_bytes_to_file(const char *fname, const char *str, size_t len,
int bin);
int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
@@ -493,18 +494,21 @@ FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle);
#endif
#ifdef _WIN32
-struct smartlist_t *
-tor_get_lines_from_handle(HANDLE *handle,
- enum stream_status *stream_status);
+MOCK_DECL(struct smartlist_t *,
+tor_get_lines_from_handle,(HANDLE *handle,
+ enum stream_status *stream_status));
#else
-struct smartlist_t *
-tor_get_lines_from_handle(FILE *handle,
- enum stream_status *stream_status);
+MOCK_DECL(struct smartlist_t *,
+tor_get_lines_from_handle,(FILE *handle,
+ enum stream_status *stream_status));
#endif
-int tor_terminate_process(process_handle_t *process_handle);
-void tor_process_handle_destroy(process_handle_t *process_handle,
- int also_terminate_process);
+int
+tor_terminate_process(process_handle_t *process_handle);
+
+MOCK_DECL(void,
+tor_process_handle_destroy,(process_handle_t *process_handle,
+ int also_terminate_process));
/* ===== Insecure rng */
typedef struct tor_weak_rng_t {
@@ -520,12 +524,14 @@ int32_t tor_weak_random_range(tor_weak_rng_t *rng, int32_t top);
* <b>n</b> */
#define tor_weak_random_one_in_n(rng, n) (0==tor_weak_random_range((rng),(n)))
+int format_hex_number_sigsafe(unsigned long x, char *buf, int max_len);
+int format_dec_number_sigsafe(unsigned long x, char *buf, int max_len);
+
#ifdef UTIL_PRIVATE
/* Prototypes for private functions only used by util.c (and unit tests) */
-int format_hex_number_for_helper_exit_status(unsigned int x, char *buf,
- int max_len);
-int format_helper_exit_status(unsigned char child_state,
+#ifndef _WIN32
+STATIC int format_helper_exit_status(unsigned char child_state,
int saved_errno, char *hex_errno);
/* Space for hex values of child state, a slash, saved_errno (with
@@ -534,7 +540,11 @@ int format_helper_exit_status(unsigned char child_state,
1 + sizeof(int) * 2 + 1)
#endif
+#endif
+
const char *libor_get_digests(void);
+#define ARRAY_LENGTH(x) (sizeof(x)) / sizeof(x[0])
+
#endif