aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/address.c91
-rw-r--r--src/common/address.h7
-rw-r--r--src/common/backtrace.c233
-rw-r--r--src/common/backtrace.h21
-rw-r--r--src/common/ciphers.inc169
-rw-r--r--src/common/compat.c543
-rw-r--r--src/common/compat.h41
-rw-r--r--src/common/compat_libevent.c70
-rw-r--r--src/common/compat_libevent.h7
-rw-r--r--src/common/container.c49
-rw-r--r--src/common/container.h23
-rw-r--r--src/common/crypto.c267
-rw-r--r--src/common/crypto.h21
-rw-r--r--src/common/crypto_curve25519.c2
-rw-r--r--src/common/crypto_curve25519.h10
-rw-r--r--src/common/crypto_format.c1
-rw-r--r--src/common/get_mozilla_ciphers.py192
-rw-r--r--src/common/include.am63
-rw-r--r--src/common/linux_syscalls.inc1153
-rw-r--r--src/common/log.c195
-rw-r--r--src/common/memarea.c50
-rw-r--r--src/common/sandbox.c1838
-rw-r--r--src/common/sandbox.h212
-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.h25
-rw-r--r--src/common/tortls.c231
-rw-r--r--src/common/tortls.h3
-rw-r--r--src/common/util.c343
-rw-r--r--src/common/util.h72
31 files changed, 5169 insertions, 865 deletions
diff --git a/src/common/address.c b/src/common/address.c
index 14a7b6bc9..29d4c0447 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>
@@ -181,7 +182,7 @@ tor_addr_make_unspec(tor_addr_t *a)
a->family = AF_UNSPEC;
}
-/** Set address <a>a</b> to the null address in address family <b>family</b>.
+/** Set address <b>a</b> to the null address in address family <b>family</b>.
* The null address for AF_INET is 0.0.0.0. The null address for AF_INET6 is
* [::]. AF_UNSPEC is all null. */
void
@@ -234,8 +235,10 @@ 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);
- if (!err) {
+ err = sandbox_getaddrinfo(name, NULL, &hints, &res);
+ /* The check for 'res' here shouldn't be necessary, but it makes static
+ * analysis tools happy. */
+ if (!err && res) {
best = NULL;
for (res_p = res; res_p; res_p = res_p->ai_next) {
if (family == AF_UNSPEC) {
@@ -261,7 +264,7 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr)
&((struct sockaddr_in6*)best->ai_addr)->sin6_addr);
result = 0;
}
- freeaddrinfo(res);
+ sandbox_freeaddrinfo(res);
return result;
}
return (err == EAI_AGAIN) ? 1 : -1;
@@ -873,6 +876,32 @@ tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src)
memcpy(dest, src, sizeof(tor_addr_t));
}
+/** Copy a tor_addr_t from <b>src</b> to <b>dest</b>, taking extra case to
+ * copy only the well-defined portions. Used for computing hashes of
+ * addresses.
+ */
+void
+tor_addr_copy_tight(tor_addr_t *dest, const tor_addr_t *src)
+{
+ tor_assert(src != dest);
+ tor_assert(src);
+ tor_assert(dest);
+ memset(dest, 0, sizeof(tor_addr_t));
+ dest->family = src->family;
+ switch (tor_addr_family(src))
+ {
+ case AF_INET:
+ dest->addr.in_addr.s_addr = src->addr.in_addr.s_addr;
+ break;
+ case AF_INET6:
+ memcpy(dest->addr.in6_addr.s6_addr, src->addr.in6_addr.s6_addr, 16);
+ case AF_UNSPEC:
+ break;
+ default:
+ tor_fragile_assert();
+ }
+}
+
/** Given two addresses <b>addr1</b> and <b>addr2</b>, return 0 if the two
* addresses are equivalent under the mask mbits, less than 0 if addr1
* precedes addr2, and greater than 0 otherwise.
@@ -994,19 +1023,17 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
}
}
-/** Return a hash code based on the address addr */
-unsigned int
+/** Return a hash code based on the address addr. DOCDOC extra */
+uint64_t
tor_addr_hash(const tor_addr_t *addr)
{
switch (tor_addr_family(addr)) {
case AF_INET:
- return tor_addr_to_ipv4h(addr);
+ return siphash24g(&addr->addr.in_addr.s_addr, 4);
case AF_UNSPEC:
return 0x4e4d5342;
- case AF_INET6: {
- const uint32_t *u = tor_addr_to_in6_addr32(addr);
- return u[0] + u[1] + u[2] + u[3];
- }
+ case AF_INET6:
+ return siphash24g(&addr->addr.in6_addr.s6_addr, 16);
default:
tor_fragile_assert();
return 0;
@@ -1420,31 +1447,22 @@ get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr)
* XXXX024 IPv6 deprecate some of these.
*/
-/** Return true iff <b>ip</b> (in host order) is an IP reserved to localhost,
- * or reserved for local networks by RFC 1918.
- */
-int
-is_internal_IP(uint32_t ip, int for_listening)
-{
- tor_addr_t myaddr;
- myaddr.family = AF_INET;
- myaddr.addr.in_addr.s_addr = htonl(ip);
-
- return tor_addr_is_internal(&myaddr, for_listening);
-}
-
/** Given an address of the form "ip:port", try to divide it into its
* ip and port portions, setting *<b>address_out</b> to a newly
* allocated string holding the address portion and *<b>port_out</b>
* to the port.
*
- * Don't do DNS lookups and don't allow domain names in the <ip> field.
- * Don't accept <b>addrport</b> of the form "<ip>" or "<ip>:0".
+ * Don't do DNS lookups and don't allow domain names in the "ip" field.
+ *
+ * If <b>default_port</b> is less than 0, don't accept <b>addrport</b> of the
+ * form "ip" or "ip:0". Otherwise, accept those forms, and set
+ * *<b>port_out</b> to <b>default_port</b>.
*
* Return 0 on success, -1 on failure. */
int
tor_addr_port_parse(int severity, const char *addrport,
- tor_addr_t *address_out, uint16_t *port_out)
+ tor_addr_t *address_out, uint16_t *port_out,
+ int default_port)
{
int retval = -1;
int r;
@@ -1458,8 +1476,12 @@ tor_addr_port_parse(int severity, const char *addrport,
if (r < 0)
goto done;
- if (!*port_out)
- goto done;
+ if (!*port_out) {
+ if (default_port >= 0)
+ *port_out = default_port;
+ else
+ goto done;
+ }
/* make sure that address_out is an IP address */
if (tor_addr_parse(address_out, addr_tmp) < 0)
@@ -1480,9 +1502,18 @@ int
tor_addr_port_split(int severity, const char *addrport,
char **address_out, uint16_t *port_out)
{
+ tor_addr_t a_tmp;
tor_assert(addrport);
tor_assert(address_out);
tor_assert(port_out);
+ /* We need to check for IPv6 manually because addr_port_lookup() doesn't
+ * do a good job on IPv6 addresses that lack a port. */
+ if (tor_addr_parse(&a_tmp, addrport) == AF_INET6) {
+ *port_out = 0;
+ *address_out = tor_strdup(addrport);
+ return 0;
+ }
+
return addr_port_lookup(severity, addrport, address_out, NULL, port_out);
}
@@ -1560,7 +1591,7 @@ addr_mask_get_bits(uint32_t mask)
return 0;
if (mask == 0xFFFFFFFFu)
return 32;
- for (i=0; i<=32; ++i) {
+ for (i=1; i<=32; ++i) {
if (mask == (uint32_t) ~((1u<<(32-i))-1)) {
return i;
}
diff --git a/src/common/address.h b/src/common/address.h
index 77e585534..8dc63b71c 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -167,7 +167,7 @@ int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
* "exactly". */
#define tor_addr_eq(a,b) (0==tor_addr_compare((a),(b),CMP_EXACT))
-unsigned int tor_addr_hash(const tor_addr_t *addr);
+uint64_t tor_addr_hash(const tor_addr_t *addr);
int tor_addr_is_v4(const tor_addr_t *addr);
int tor_addr_is_internal_(const tor_addr_t *ip, int for_listening,
const char *filename, int lineno);
@@ -192,6 +192,7 @@ const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len,
int decorate);
int tor_addr_parse(tor_addr_t *addr, const char *src);
void tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src);
+void tor_addr_copy_tight(tor_addr_t *dest, const tor_addr_t *src);
void tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr);
/** Set <b>dest</b> to the IPv4 address encoded in <b>v4addr</b> in host
* order. */
@@ -209,12 +210,12 @@ int tor_addr_port_split(int severity, const char *addrport,
char **address_out, uint16_t *port_out);
int tor_addr_port_parse(int severity, const char *addrport,
- tor_addr_t *address_out, uint16_t *port_out);
+ tor_addr_t *address_out, uint16_t *port_out,
+ int default_port);
int tor_addr_hostname_is_local(const char *name);
/* IPv4 helpers */
-int is_internal_IP(uint32_t ip, int for_listening);
int addr_port_lookup(int severity, const char *addrport, char **address,
uint32_t *addr, uint16_t *port_out);
int parse_port_range(const char *port, uint16_t *port_min_out,
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
new file mode 100644
index 000000000..3a073a8ff
--- /dev/null
+++ b/src/common/backtrace.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define __USE_GNU
+#define _GNU_SOURCE 1
+
+#include "orconfig.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
+
+#define EXPOSE_CLEAN_BACKTRACE
+#include "backtrace.h"
+
+#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];
+/** Protects cb_buf from concurrent access */
+static tor_mutex_t cb_buf_mutex;
+
+/** 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.
+ */
+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;
+ char **symbols;
+ int i;
+
+ tor_mutex_acquire(&cb_buf_mutex);
+
+ depth = backtrace(cb_buf, MAX_DEPTH);
+ symbols = backtrace_symbols(cb_buf, depth);
+
+ tor_log(severity, domain, "%s. Stack trace:", msg);
+ if (!symbols) {
+ tor_log(severity, domain, " Unable to generate backtrace.");
+ goto done;
+ }
+ for (i=0; i < depth; ++i) {
+ tor_log(severity, domain, " %s", symbols[i]);
+ }
+ free(symbols);
+
+ done:
+ tor_mutex_release(&cb_buf_mutex);
+}
+
+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;
+
+ tor_mutex_init(&cb_buf_mutex);
+
+ 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;
+ }
+ }
+
+ {
+ /* Now, generate (but do not log) a backtrace. This ensures that
+ * libc has pre-loaded the symbols we need to dump things, so that later
+ * reads won't be denied by the sandbox code */
+ char **symbols;
+ int depth = backtrace(cb_buf, MAX_DEPTH);
+ symbols = backtrace_symbols(cb_buf, depth);
+ if (symbols)
+ free(symbols);
+ }
+
+ return rv;
+}
+
+/** Uninstall crash handlers. */
+static void
+remove_bt_handler(void)
+{
+ tor_mutex_uninit(&cb_buf_mutex);
+}
+#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..1f4d73339
--- /dev/null
+++ b/src/common/backtrace.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_BACKTRACE_H
+#define TOR_BACKTRACE_H
+
+#include "orconfig.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);
+
+#ifdef EXPOSE_CLEAN_BACKTRACE
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
+ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
+void clean_backtrace(void **stack, int depth, const ucontext_t *ctx);
+#endif
+#endif
+
+#endif
+
diff --git a/src/common/ciphers.inc b/src/common/ciphers.inc
index 137d78b11..ab4ac4072 100644
--- a/src/common/ciphers.inc
+++ b/src/common/ciphers.inc
@@ -4,86 +4,51 @@
*
* This file was automatically generated by get_mozilla_ciphers.py.
*/
-#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
- CIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
-#else
- XCIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
-#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA
- CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
-#else
- XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
- CIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA)
-#else
- XCIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA)
-#endif
-#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
- CIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA)
+#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+ CIPHER(0xc02b, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
#else
- XCIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA)
+ XCIPHER(0xc02b, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA
- CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ CIPHER(0xc02f, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
#else
- XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
+ XCIPHER(0xc02f, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
#endif
-#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA
- CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
+#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+ CIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
#else
- XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
+ XCIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
#endif
-#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA
- CIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA)
+#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+ CIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
#else
- XCIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA)
+ XCIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
#endif
-#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA
- CIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ CIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
#else
- XCIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
+ XCIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
#endif
-#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA
- CIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA)
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA
+ CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
#else
- XCIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA)
+ XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
#endif
-#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA
- CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
+ CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
#else
- XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
+ XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
#endif
#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA
CIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA)
#else
XCIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA)
#endif
-#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- CIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
-#else
- XCIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
-#endif
#ifdef TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA
CIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA)
#else
XCIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA)
#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
- CIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
-#else
- XCIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
- CIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA)
-#else
- XCIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA)
-#endif
-#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
- CIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA)
-#else
- XCIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA)
-#endif
#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA
CIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)
#else
@@ -94,89 +59,63 @@
#else
XCIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA)
#endif
-#ifdef TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA
- CIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA)
-#else
- XCIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA)
-#endif
-#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA
- CIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA)
-#else
- XCIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA)
-#endif
-#ifdef TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA
- CIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA)
-#else
- XCIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA)
-#endif
-#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA
- CIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
+#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
+ CIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA)
#else
- XCIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
+ XCIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA)
#endif
-#ifdef TLS1_TXT_RSA_WITH_SEED_SHA
- CIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA)
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA
+ CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
#else
- XCIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA)
+ XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
#endif
-#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA
- CIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA)
+#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA
+ CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
#else
- XCIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA)
+ XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
#endif
-#ifdef SSL3_TXT_RSA_RC4_128_MD5
- CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
+#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
+ CIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA)
#else
- XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
+ XCIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA)
#endif
-#ifdef SSL3_TXT_RSA_RC4_128_SHA
- CIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
+#ifdef SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA
+ CIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#else
- XCIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
+ XCIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#endif
#ifdef TLS1_TXT_RSA_WITH_AES_128_SHA
CIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA)
#else
XCIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA)
#endif
-#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA
- CIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA)
-#else
- XCIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA)
-#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
- CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
-#else
- XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
-#endif
-#ifdef SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA
- CIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
+#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA
+ CIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA)
#else
- XCIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
+ XCIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA)
#endif
-#ifdef SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA
- CIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA)
+#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA
+ CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
#else
- XCIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA)
+ XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
#endif
-#ifdef TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA
- CIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA)
+#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA
+ CIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA)
#else
- XCIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA)
+ XCIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA)
#endif
-#ifdef TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA
- CIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA)
+#ifdef SSL3_TXT_RSA_DES_192_CBC3_SHA
+ CIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
#else
- XCIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA)
+ XCIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
#endif
-/* No openssl macro found for 0xfeff */
-#ifdef SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
- CIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA)
+#ifdef SSL3_TXT_RSA_RC4_128_SHA
+ CIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
#else
- XCIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA)
+ XCIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
#endif
-#ifdef SSL3_TXT_RSA_DES_192_CBC3_SHA
- CIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
+#ifdef SSL3_TXT_RSA_RC4_128_MD5
+ CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
#else
- XCIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
+ XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
#endif
diff --git a/src/common/compat.c b/src/common/compat.c
index d88c5f92d..111070cc1 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
@@ -34,6 +35,15 @@
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -109,6 +119,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 +136,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;
@@ -135,6 +147,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode)
return -1;
#endif
+ log_debug(LD_FS, "Opening %s with flags %x", path, flags);
fd = open(path, flags, mode);
#ifdef FD_CLOEXEC
if (fd >= 0) {
@@ -166,6 +179,15 @@ tor_fopen_cloexec(const char *path, const char *mode)
return result;
}
+/** As rename(), but work correctly with the sandbox. */
+int
+tor_rename(const char *path_old, const char *path_new)
+{
+ log_debug(LD_FS, "Renaming %s to %s", path_old, path_new);
+ return rename(sandbox_intern_string(path_old),
+ sandbox_intern_string(path_new));
+}
+
#if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN)
/** Try to create a memory mapping for <b>filename</b> and return it. On
* failure, return NULL. Sets errno properly, using ERANGE to mean
@@ -175,9 +197,10 @@ tor_mmap_file(const char *filename)
{
int fd; /* router file */
char *string;
- int page_size;
+ int page_size, result;
tor_mmap_t *res;
size_t size, filesize;
+ struct stat st;
tor_assert(filename);
@@ -191,9 +214,22 @@ tor_mmap_file(const char *filename)
return NULL;
}
- /* XXXX why not just do fstat here? */
- size = filesize = (size_t) lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
+ /* Get the size of the file */
+ result = fstat(fd, &st);
+ if (result != 0) {
+ int save_errno = errno;
+ log_warn(LD_FS,
+ "Couldn't fstat opened descriptor for \"%s\" during mmap: %s",
+ filename, strerror(errno));
+ close(fd);
+ errno = save_errno;
+ return NULL;
+ }
+ size = filesize = (size_t)(st.st_size);
+ /*
+ * Should we check for weird crap like mmapping a named pipe here,
+ * or just wait for if (!size) below to fail?
+ */
/* ensure page alignment */
page_size = getpagesize();
size += (size%page_size) ? page_size-(size%page_size) : 0;
@@ -224,12 +260,27 @@ tor_mmap_file(const char *filename)
return res;
}
-/** Release storage held for a memory mapping. */
-void
+/** Release storage held for a memory mapping; returns 0 on success,
+ * or -1 on failure (and logs a warning). */
+int
tor_munmap_file(tor_mmap_t *handle)
{
- munmap((char*)handle->data, handle->mapping_size);
- tor_free(handle);
+ int res;
+
+ if (handle == NULL)
+ return 0;
+
+ res = munmap((char*)handle->data, handle->mapping_size);
+ if (res == 0) {
+ /* munmap() succeeded */
+ tor_free(handle);
+ } else {
+ log_warn(LD_FS, "Failed to munmap() in tor_munmap_file(): %s",
+ strerror(errno));
+ res = -1;
+ }
+
+ return res;
}
#elif defined(_WIN32)
tor_mmap_t *
@@ -311,17 +362,29 @@ tor_mmap_file(const char *filename)
tor_munmap_file(res);
return NULL;
}
-void
+
+/* Unmap the file, and return 0 for success or -1 for failure */
+int
tor_munmap_file(tor_mmap_t *handle)
{
- if (handle->data)
+ if (handle == NULL)
+ return 0;
+
+ if (handle->data) {
/* This is an ugly cast, but without it, "data" in struct tor_mmap_t would
have to be redefined as non-const. */
- UnmapViewOfFile( (LPVOID) handle->data);
+ BOOL ok = UnmapViewOfFile( (LPVOID) handle->data);
+ if (!ok) {
+ log_warn(LD_FS, "Failed to UnmapViewOfFile() in tor_munmap_file(): %d",
+ (int)GetLastError());
+ }
+ }
if (handle->mmap_handle != NULL)
CloseHandle(handle->mmap_handle);
tor_free(handle);
+
+ return 0;
}
#else
tor_mmap_t *
@@ -337,13 +400,25 @@ tor_mmap_file(const char *filename)
handle->size = st.st_size;
return handle;
}
-void
+
+/** Unmap the file mapped with tor_mmap_file(), and return 0 for success
+ * or -1 for failure.
+ */
+
+int
tor_munmap_file(tor_mmap_t *handle)
{
- char *d = (char*)handle->data;
+ char *d = NULL;
+ if (handle == NULL)
+ return 0;
+
+ d = (char*)handle->data;
tor_free(d);
memwipe(handle, 0, sizeof(tor_mmap_t));
tor_free(handle);
+
+ /* Can't fail in this mmap()/munmap()-free case */
+ return 0;
}
#endif
@@ -498,21 +573,29 @@ tor_memmem(const void *_haystack, size_t hlen,
#else
/* This isn't as fast as the GLIBC implementation, but it doesn't need to
* be. */
- const char *p, *end;
+ const char *p, *last_possible_start;
const char *haystack = (const char*)_haystack;
const char *needle = (const char*)_needle;
char first;
tor_assert(nlen);
+ if (nlen > hlen)
+ return NULL;
+
p = haystack;
- end = haystack + hlen;
+ /* Last position at which the needle could start. */
+ last_possible_start = haystack + hlen - nlen;
first = *(const char*)needle;
- while ((p = memchr(p, first, end-p))) {
- if (p+nlen > end)
- return NULL;
+ while ((p = memchr(p, first, last_possible_start + 1 - p))) {
if (fast_memeq(p, needle, nlen))
return p;
- ++p;
+ if (++p > last_possible_start) {
+ /* This comparison shouldn't be necessary, since if p was previously
+ * equal to last_possible_start, the next memchr call would be
+ * "memchr(p, first, 0)", which will return NULL. But it clarifies the
+ * logic. */
+ return NULL;
+ }
}
return NULL;
#endif
@@ -729,7 +812,7 @@ int
replace_file(const char *from, const char *to)
{
#ifndef _WIN32
- return rename(from,to);
+ return tor_rename(from, to);
#else
switch (file_status(to))
{
@@ -744,7 +827,7 @@ replace_file(const char *from, const char *to)
errno = EISDIR;
return -1;
}
- return rename(from,to);
+ return tor_rename(from,to);
#endif
}
@@ -948,24 +1031,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 +1079,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 +1129,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 +1195,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 +1239,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 +1378,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 +1400,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 +1483,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. */
@@ -1532,6 +1702,106 @@ log_credential_status(void)
}
#endif
+#ifndef _WIN32
+/** Cached struct from the last getpwname() call we did successfully. */
+static struct passwd *passwd_cached = NULL;
+
+/** Helper: copy a struct passwd object.
+ *
+ * We only copy the fields pw_uid, pw_gid, pw_name, pw_dir. Tor doesn't use
+ * any others, and I don't want to run into incompatibilities.
+ */
+static struct passwd *
+tor_passwd_dup(const struct passwd *pw)
+{
+ struct passwd *new_pw = tor_malloc_zero(sizeof(struct passwd));
+ if (pw->pw_name)
+ new_pw->pw_name = tor_strdup(pw->pw_name);
+ if (pw->pw_dir)
+ new_pw->pw_dir = tor_strdup(pw->pw_dir);
+ new_pw->pw_uid = pw->pw_uid;
+ new_pw->pw_gid = pw->pw_gid;
+
+ return new_pw;
+}
+
+/** Helper: free one of our cached 'struct passwd' values. */
+static void
+tor_passwd_free(struct passwd *pw)
+{
+ if (!pw)
+ return;
+
+ tor_free(pw->pw_name);
+ tor_free(pw->pw_dir);
+ tor_free(pw);
+}
+
+/** Wrapper around getpwnam() that caches result. Used so that we don't need
+ * to give the sandbox access to /etc/passwd.
+ *
+ * The following fields alone will definitely be copied in the output: pw_uid,
+ * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
+ *
+ * When called with a NULL argument, this function clears storage associated
+ * with static variables it uses.
+ **/
+const struct passwd *
+tor_getpwnam(const char *username)
+{
+ struct passwd *pw;
+
+ if (username == NULL) {
+ tor_passwd_free(passwd_cached);
+ passwd_cached = NULL;
+ return NULL;
+ }
+
+ if ((pw = getpwnam(username))) {
+ tor_passwd_free(passwd_cached);
+ passwd_cached = tor_passwd_dup(pw);
+ log_notice(LD_GENERAL, "Caching new entry %s for %s",
+ passwd_cached->pw_name, username);
+ return pw;
+ }
+
+ /* Lookup failed */
+ if (! passwd_cached || ! passwd_cached->pw_name)
+ return NULL;
+
+ if (! strcmp(username, passwd_cached->pw_name))
+ return passwd_cached;
+
+ return NULL;
+}
+
+/** Wrapper around getpwnam() that can use cached result from
+ * tor_getpwnam(). Used so that we don't need to give the sandbox access to
+ * /etc/passwd.
+ *
+ * The following fields alone will definitely be copied in the output: pw_uid,
+ * pw_gid, pw_name, pw_dir. Other fields are not present in cached values.
+ */
+const struct passwd *
+tor_getpwuid(uid_t uid)
+{
+ struct passwd *pw;
+
+ if ((pw = getpwuid(uid))) {
+ return pw;
+ }
+
+ /* Lookup failed */
+ if (! passwd_cached)
+ return NULL;
+
+ if (uid == passwd_cached->pw_uid)
+ return passwd_cached;
+
+ return NULL;
+}
+#endif
+
/** Call setuid and setgid to run as <b>user</b> and switch to their
* primary group. Return 0 on success. On failure, log and return -1.
*/
@@ -1539,7 +1809,7 @@ int
switch_id(const char *user)
{
#ifndef _WIN32
- struct passwd *pw = NULL;
+ const struct passwd *pw = NULL;
uid_t old_uid;
gid_t old_gid;
static int have_already_switched_id = 0;
@@ -1560,7 +1830,7 @@ switch_id(const char *user)
old_gid = getgid();
/* Lookup the user and group information, if we have a problem, bail out. */
- pw = getpwnam(user);
+ pw = tor_getpwnam(user);
if (pw == NULL) {
log_warn(LD_CONFIG, "Error setting configured user: %s not found", user);
return -1;
@@ -1731,10 +2001,10 @@ tor_disable_debugger_attach(void)
char *
get_user_homedir(const char *username)
{
- struct passwd *pw;
+ const struct passwd *pw;
tor_assert(username);
- if (!(pw = getpwnam(username))) {
+ if (!(pw = tor_getpwnam(username))) {
log_err(LD_CONFIG,"User \"%s\" not found.", username);
return NULL;
}
@@ -1746,6 +2016,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)
@@ -2019,8 +2298,10 @@ tor_inet_pton(int af, const char *src, void *dst)
else {
unsigned byte1,byte2,byte3,byte4;
char more;
- for (eow = dot-1; eow >= src && TOR_ISDIGIT(*eow); --eow)
+ for (eow = dot-1; eow > src && TOR_ISDIGIT(*eow); --eow)
;
+ if (*eow != ':')
+ return 0;
++eow;
/* We use "scanf" because some platform inet_aton()s are too lax
@@ -2248,6 +2529,12 @@ tor_pthread_helper_fn(void *_data)
func(arg);
return NULL;
}
+/**
+ * A pthread attribute to make threads start detached.
+ */
+static pthread_attr_t attr_detached;
+/** True iff we've called tor_threads_init() */
+static int threads_initialized = 0;
#endif
/** Minimalist interface to run a void function in the background. On
@@ -2271,12 +2558,12 @@ spawn_func(void (*func)(void *), void *data)
#elif defined(USE_PTHREADS)
pthread_t thread;
tor_pthread_data_t *d;
+ if (PREDICT_UNLIKELY(!threads_initialized))
+ tor_threads_init();
d = tor_malloc(sizeof(tor_pthread_data_t));
d->data = data;
d->func = func;
- if (pthread_create(&thread,NULL,tor_pthread_helper_fn,d))
- return -1;
- if (pthread_detach(thread))
+ if (pthread_create(&thread,&attr_detached,tor_pthread_helper_fn,d))
return -1;
return 0;
#else
@@ -2633,8 +2920,6 @@ tor_get_thread_id(void)
* "reentrant" mutexes (i.e., once we can re-lock if we're already holding
* them.) */
static pthread_mutexattr_t attr_reentrant;
-/** True iff we've called tor_threads_init() */
-static int threads_initialized = 0;
/** Initialize <b>mutex</b> so it can be locked. Every mutex must be set
* up with tor_mutex_init() or tor_mutex_new(); not both. */
void
@@ -2778,6 +3063,8 @@ tor_threads_init(void)
if (!threads_initialized) {
pthread_mutexattr_init(&attr_reentrant);
pthread_mutexattr_settype(&attr_reentrant, PTHREAD_MUTEX_RECURSIVE);
+ tor_assert(0==pthread_attr_init(&attr_detached));
+ tor_assert(0==pthread_attr_setdetachstate(&attr_detached, 1));
threads_initialized = 1;
set_main_thread();
}
@@ -3153,3 +3440,119 @@ format_win32_error(DWORD err)
}
#endif
+#if defined(HW_PHYSMEM64)
+/* This appears to be an OpenBSD thing */
+#define INT64_HW_MEM HW_PHYSMEM64
+#elif defined(HW_MEMSIZE)
+/* OSX defines this one */
+#define INT64_HW_MEM HW_MEMSIZE
+#endif
+
+/**
+ * Helper: try to detect the total system memory, and return it. On failure,
+ * return 0.
+ */
+static uint64_t
+get_total_system_memory_impl(void)
+{
+#if defined(__linux__)
+ /* On linux, sysctl is deprecated. Because proc is so awesome that you
+ * shouldn't _want_ to write portable code, I guess? */
+ unsigned long long result=0;
+ int fd = -1;
+ char *s = NULL;
+ const char *cp;
+ size_t file_size=0;
+ if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0)))
+ return 0;
+ s = read_file_to_str_until_eof(fd, 65536, &file_size);
+ if (!s)
+ goto err;
+ cp = strstr(s, "MemTotal:");
+ if (!cp)
+ goto err;
+ /* Use the system sscanf so that space will match a wider number of space */
+ if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1)
+ goto err;
+
+ close(fd);
+ tor_free(s);
+ return result * 1024;
+
+ err:
+ tor_free(s);
+ close(fd);
+ return 0;
+#elif defined (_WIN32)
+ /* Windows has MEMORYSTATUSEX; pretty straightforward. */
+ MEMORYSTATUSEX ms;
+ memset(&ms, 0, sizeof(ms));
+ ms.dwLength = sizeof(ms);
+ if (! GlobalMemoryStatusEx(&ms))
+ return 0;
+
+ return ms.ullTotalPhys;
+
+#elif defined(HAVE_SYSCTL) && defined(INT64_HW_MEM)
+ /* On many systems, HW_PYHSMEM is clipped to 32 bits; let's use a better
+ * variant if we know about it. */
+ uint64_t memsize = 0;
+ size_t len = sizeof(memsize);
+ int mib[2] = {CTL_HW, INT64_HW_MEM};
+ if (sysctl(mib,2,&memsize,&len,NULL,0))
+ return 0;
+
+ return memsize;
+
+#elif defined(HAVE_SYSCTL) && defined(HW_PHYSMEM)
+ /* On some systems (like FreeBSD I hope) you can use a size_t with
+ * HW_PHYSMEM. */
+ size_t memsize=0;
+ size_t len = sizeof(memsize);
+ int mib[2] = {CTL_HW, HW_USERMEM};
+ if (sysctl(mib,2,&memsize,&len,NULL,0))
+ return -1;
+
+ return memsize;
+
+#else
+ /* I have no clue. */
+ return 0;
+#endif
+}
+
+/**
+ * Try to find out how much physical memory the system has. On success,
+ * return 0 and set *<b>mem_out</b> to that value. On failure, return -1.
+ */
+int
+get_total_system_memory(size_t *mem_out)
+{
+ static size_t mem_cached=0;
+ uint64_t m = get_total_system_memory_impl();
+ if (0 == m) {
+ /* We couldn't find our memory total */
+ if (0 == mem_cached) {
+ /* We have no cached value either */
+ *mem_out = 0;
+ return -1;
+ }
+
+ *mem_out = mem_cached;
+ return 0;
+ }
+
+#if SIZE_T_MAX != UINT64_MAX
+ if (m > SIZE_T_MAX) {
+ /* I think this could happen if we're a 32-bit Tor running on a 64-bit
+ * system: we could have more system memory than would fit in a
+ * size_t. */
+ m = SIZE_T_MAX;
+ }
+#endif
+
+ *mem_out = mem_cached = (size_t) m;
+
+ return 0;
+}
+
diff --git a/src/common/compat.h b/src/common/compat.h
index 51fb8c527..683c4d089 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
@@ -285,7 +292,7 @@ typedef struct tor_mmap_t {
} tor_mmap_t;
tor_mmap_t *tor_mmap_file(const char *filename) ATTR_NONNULL((1));
-void tor_munmap_file(tor_mmap_t *handle) ATTR_NONNULL((1));
+int tor_munmap_file(tor_mmap_t *handle) ATTR_NONNULL((1));
int tor_snprintf(char *str, size_t size, const char *format, ...)
CHECK_PRINTF(3,4) ATTR_NONNULL((1,3));
@@ -314,7 +321,7 @@ tor_memstr(const void *haystack, size_t hlen, const char *needle)
extern const uint32_t TOR_##name##_TABLE[]; \
static INLINE int TOR_##name(char c) { \
uint8_t u = c; \
- return !!(TOR_##name##_TABLE[(u >> 5) & 7] & (1 << (u & 31))); \
+ return !!(TOR_##name##_TABLE[(u >> 5) & 7] & (1u << (u & 31))); \
}
DECLARE_CTYPE_FN(ISALPHA)
DECLARE_CTYPE_FN(ISALNUM)
@@ -403,6 +410,7 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
/* ===== File compatibility */
int tor_open_cloexec(const char *path, int flags, unsigned mode);
FILE *tor_fopen_cloexec(const char *path, const char *mode);
+int tor_rename(const char *path_old, const char *path_new);
int replace_file(const char *from, const char *to);
int touch_file(const char *fname);
@@ -446,10 +454,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)
@@ -613,11 +633,18 @@ int switch_id(const char *user);
char *get_user_homedir(const char *username);
#endif
+#ifndef _WIN32
+const struct passwd *tor_getpwnam(const char *username);
+const struct passwd *tor_getpwuid(uid_t uid);
+#endif
+
int get_parent_directory(char *fname);
char *make_path_absolute(char *fname);
char **get_environment(void);
+int get_total_system_memory(size_t *mem_out);
+
int spawn_func(void (*func)(void *), void *data);
void spawn_exit(void) ATTR_NORETURN;
@@ -722,5 +749,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..74b54bb85 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -13,6 +13,8 @@
#include "compat.h"
#include "compat_libevent.h"
+#include "crypto.h"
+
#include "util.h"
#include "torlog.h"
@@ -415,6 +417,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. */
@@ -618,7 +628,25 @@ tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev,
}
#endif
-#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,1,1)
+int
+tor_init_libevent_rng(void)
+{
+ int rv = 0;
+#ifdef HAVE_EVUTIL_SECURE_RNG_INIT
+ char buf[256];
+ if (evutil_secure_rng_init() < 0) {
+ rv = -1;
+ }
+ /* Older libevent -- manually initialize the RNG */
+ crypto_rand(buf, 32);
+ evutil_secure_rng_add_bytes(buf, 32);
+ evutil_secure_rng_get_bytes(buf, sizeof(buf));
+#endif
+ return rv;
+}
+
+#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,1,1) \
+ && !defined(TOR_UNIT_TESTS)
void
tor_gettimeofday_cached(struct timeval *tv)
{
@@ -651,5 +679,45 @@ tor_gettimeofday_cache_clear(void)
{
cached_time_hires.tv_sec = 0;
}
+
+#ifdef TOR_UNIT_TESTS
+/** For testing: force-update the cached time to a given value. */
+void
+tor_gettimeofday_cache_set(const struct timeval *tv)
+{
+ tor_assert(tv);
+ memcpy(&cached_time_hires, tv, sizeof(*tv));
+}
+#endif
#endif
+/**
+ * As tor_gettimeofday_cached, but can never move backwards in time.
+ *
+ * The returned value may diverge from wall-clock time, since wall-clock time
+ * can trivially be adjusted backwards, and this can't. Don't mix wall-clock
+ * time with these values in the same calculation.
+ *
+ * Depending on implementation, this function may or may not "smooth out" huge
+ * jumps forward in wall-clock time. It may or may not keep its results
+ * advancing forward (as opposed to stalling) if the wall-clock time goes
+ * backwards. The current implementation does neither of of these.
+ *
+ * This function is not thread-safe; do not call it outside the main thread.
+ *
+ * In future versions of Tor, this may return a time does not have its
+ * origin at the Unix epoch.
+ */
+void
+tor_gettimeofday_cached_monotonic(struct timeval *tv)
+{
+ struct timeval last_tv = { 0, 0 };
+
+ tor_gettimeofday_cached(tv);
+ if (timercmp(tv, &last_tv, <)) {
+ memcpy(tv, &last_tv, sizeof(struct timeval));
+ } else {
+ memcpy(&last_tv, tv, sizeof(struct timeval));
+ }
+}
+
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 2472e2c49..9ee7b49cf 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);
@@ -88,8 +89,14 @@ int tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev,
struct bufferevent_rate_limit_group *g);
#endif
+int tor_init_libevent_rng(void);
+
void tor_gettimeofday_cached(struct timeval *tv);
void tor_gettimeofday_cache_clear(void);
+#ifdef TOR_UNIT_TESTS
+void tor_gettimeofday_cache_set(const struct timeval *tv);
+#endif
+void tor_gettimeofday_cached_monotonic(struct timeval *tv);
#endif
diff --git a/src/common/container.c b/src/common/container.c
index eec497a3e..b937d544f 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)
*/
@@ -708,6 +727,26 @@ smartlist_uniq_strings(smartlist_t *sl)
smartlist_uniq(sl, compare_string_ptrs_, tor_free_);
}
+/** Helper: compare two pointers. */
+static int
+compare_ptrs_(const void **_a, const void **_b)
+{
+ const void *a = *_a, *b = *_b;
+ if (a<b)
+ return -1;
+ else if (a==b)
+ return 0;
+ else
+ return 1;
+}
+
+/** Sort <b>sl</b> in ascending order of the pointers it contains. */
+void
+smartlist_sort_pointers(smartlist_t *sl)
+{
+ smartlist_sort(sl, compare_ptrs_);
+}
+
/* Heap-based priority queue implementation for O(lg N) insert and remove.
* Recall that the heap property is that, for every index I, h[I] <
* H[LEFT_CHILD[I]] and h[I] < H[RIGHT_CHILD[I]].
@@ -985,7 +1024,7 @@ strmap_entries_eq(const strmap_entry_t *a, const strmap_entry_t *b)
static INLINE unsigned int
strmap_entry_hash(const strmap_entry_t *a)
{
- return ht_string_hash(a->key);
+ return (unsigned) siphash24g(a->key, strlen(a->key));
}
/** Helper: compare digestmap_entry_t objects by key value. */
@@ -999,13 +1038,7 @@ digestmap_entries_eq(const digestmap_entry_t *a, const digestmap_entry_t *b)
static INLINE unsigned int
digestmap_entry_hash(const digestmap_entry_t *a)
{
-#if SIZEOF_INT != 8
- const uint32_t *p = (const uint32_t*)a->key;
- return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4];
-#else
- const uint64_t *p = (const uint64_t*)a->key;
- return p[0] ^ p[1];
-#endif
+ return (unsigned) siphash24g(a->key, DIGEST_LEN);
}
HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
diff --git a/src/common/container.h b/src/common/container.h
index fb9374794..0d31f2093 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -7,6 +7,7 @@
#define TOR_CONTAINER_H
#include "util.h"
+#include "siphash.h"
/** A resizeable list of pointers, with associated helpful functionality.
*
@@ -42,6 +43,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);
@@ -101,6 +103,7 @@ void smartlist_uniq(smartlist_t *sl,
void smartlist_sort_strings(smartlist_t *sl);
void smartlist_sort_digests(smartlist_t *sl);
void smartlist_sort_digests256(smartlist_t *sl);
+void smartlist_sort_pointers(smartlist_t *sl);
char *smartlist_get_most_frequent_string(smartlist_t *sl);
char *smartlist_get_most_frequent_digest256(smartlist_t *sl);
@@ -619,11 +622,11 @@ typedef struct {
static INLINE void
digestset_add(digestset_t *set, const char *digest)
{
- const uint32_t *p = (const uint32_t *)digest;
- const uint32_t d1 = p[0] + (p[1]>>16);
- const uint32_t d2 = p[1] + (p[2]>>16);
- const uint32_t d3 = p[2] + (p[3]>>16);
- const uint32_t d4 = p[3] + (p[0]>>16);
+ const uint64_t x = siphash24g(digest, 20);
+ const uint32_t d1 = (uint32_t) x;
+ const uint32_t d2 = (uint32_t)( (x>>16) + x);
+ const uint32_t d3 = (uint32_t)( (x>>32) + x);
+ const uint32_t d4 = (uint32_t)( (x>>48) + x);
bitarray_set(set->ba, BIT(d1));
bitarray_set(set->ba, BIT(d2));
bitarray_set(set->ba, BIT(d3));
@@ -635,11 +638,11 @@ digestset_add(digestset_t *set, const char *digest)
static INLINE int
digestset_contains(const digestset_t *set, const char *digest)
{
- const uint32_t *p = (const uint32_t *)digest;
- const uint32_t d1 = p[0] + (p[1]>>16);
- const uint32_t d2 = p[1] + (p[2]>>16);
- const uint32_t d3 = p[2] + (p[3]>>16);
- const uint32_t d4 = p[3] + (p[0]>>16);
+ const uint64_t x = siphash24g(digest, 20);
+ const uint32_t d1 = (uint32_t) x;
+ const uint32_t d2 = (uint32_t)( (x>>16) + x);
+ const uint32_t d3 = (uint32_t)( (x>>32) + x);
+ const uint32_t d4 = (uint32_t)( (x>>48) + x);
return bitarray_is_set(set->ba, BIT(d1)) &&
bitarray_is_set(set->ba, BIT(d2)) &&
bitarray_is_set(set->ba, BIT(d3)) &&
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 925beb352..a247a87d4 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,13 +126,15 @@ 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;
}
}
/** Boolean: has OpenSSL's crypto been initialized? */
+static int crypto_early_initialized_ = 0;
+
+/** Boolean: has OpenSSL's crypto been initialized? */
static int crypto_global_initialized_ = 0;
/** Log all pending crypto errors at level <b>severity</b>. Use
@@ -197,6 +199,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,32 +227,67 @@ 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;
+}
+
+/** Make sure that openssl is using its default PRNG. Return 1 if we had to
+ * adjust it; 0 otherwise. */
+static int
+crypto_force_rand_ssleay(void)
+{
+ if (RAND_get_rand_method() != RAND_SSLeay()) {
+ log_notice(LD_CRYPTO, "It appears that one of our engines has provided "
+ "a replacement the OpenSSL RNG. Resetting it to the default "
+ "implementation.");
+ RAND_set_rand_method(RAND_SSLeay());
+ return 1;
+ }
+ return 0;
+}
+
+/** Set up the siphash key if we haven't already done so. */
+int
+crypto_init_siphash_key(void)
+{
+ static int have_seeded_siphash = 0;
+ struct sipkey key;
+ if (have_seeded_siphash)
+ return 0;
+
+ if (crypto_rand((char*) &key, sizeof(key)) < 0)
+ return -1;
+ siphash_set_global_key(&key);
+ have_seeded_siphash = 1;
+ return 0;
+}
+
/** Initialize the crypto library. Return 0 on success, -1 on failure.
*/
int
-crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
+crypto_early_init(void)
{
- if (!crypto_global_initialized_) {
+ if (!crypto_early_initialized_) {
+
+ crypto_early_initialized_ = 1;
+
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
- crypto_global_initialized_ = 1;
+
setup_openssl_threading();
if (SSLeay() == OPENSSL_VERSION_NUMBER &&
@@ -251,6 +309,26 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
crypto_openssl_get_version_str());
}
+ crypto_force_rand_ssleay();
+
+ if (crypto_seed_rng(1) < 0)
+ return -1;
+ if (crypto_init_siphash_key() < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/** Initialize the crypto library. Return 0 on success, -1 on failure.
+ */
+int
+crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
+{
+ if (!crypto_global_initialized_) {
+ crypto_early_init();
+
+ crypto_global_initialized_ = 1;
+
if (useAccel > 0) {
#ifdef DISABLE_ENGINES
(void)accelName;
@@ -286,28 +364,41 @@ 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("RAND (which we will not use)", 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.");
}
- if (RAND_get_rand_method() != RAND_SSLeay()) {
- log_notice(LD_CRYPTO, "It appears that one of our engines has provided "
- "a replacement the OpenSSL RNG. Resetting it to the default "
- "implementation.");
- RAND_set_rand_method(RAND_SSLeay());
+ if (crypto_force_rand_ssleay()) {
+ if (crypto_seed_rng(1) < 0)
+ return -1;
}
evaluate_evp_for_aes(-1);
evaluate_ctr_for_aes();
-
- return crypto_seed_rng(1);
}
return 0;
}
@@ -1161,22 +1252,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;
}
@@ -1207,24 +1297,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;
}
@@ -1233,31 +1316,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)
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
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);
- 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;
@@ -1294,13 +1370,35 @@ 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);
}
return 0;
}
+/** Given a private or public key <b>pk</b>, put a hashed fingerprint of
+ * the public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1
+ * bytes of space). Return 0 on success, -1 on failure.
+ *
+ * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest
+ * of the ASN.1 encoding of the public key, converted to hexadecimal, in
+ * upper case.
+ */
+int
+crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out)
+{
+ char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN];
+ if (crypto_pk_get_digest(pk, digest)) {
+ return -1;
+ }
+ if (crypto_digest(hashed_digest, digest, DIGEST_LEN)) {
+ return -1;
+ }
+ base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN);
+ return 0;
+}
+
/* symmetric crypto */
/** Return a pointer to the key set for the cipher in <b>env</b>.
@@ -1496,7 +1594,7 @@ struct crypto_digest_t {
SHA256_CTX sha2; /**< state for SHA256 */
} d; /**< State for the digest we're using. Only one member of the
* union is usable, depending on the value of <b>algorithm</b>. */
- ENUM_BF(digest_algorithm_t) algorithm : 8; /**< Which algorithm is in use? */
+ digest_algorithm_bitfield_t algorithm : 8; /**< Which algorithm is in use? */
};
/** Allocate and return a new digest object to compute SHA1 digests.
@@ -1644,21 +1742,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>.
@@ -1727,7 +1810,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;
@@ -1751,15 +1834,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;
}
@@ -1786,7 +1862,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);
@@ -2394,7 +2471,8 @@ 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);
+ log_debug(LD_FS, "Opening %s for entropy", filenames[i]);
+ 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);
@@ -2449,8 +2527,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);
@@ -3026,7 +3104,7 @@ openssl_locking_cb_(int mode, int n, const char *file, int line)
(void)file;
(void)line;
if (!openssl_mutexes_)
- /* This is not a really good fix for the
+ /* This is not a really good fix for the
* "release-freed-lock-from-separate-thread-on-shutdown" problem, but
* it can't hurt. */
return;
@@ -3144,6 +3222,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..aa4271aa3 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
@@ -92,6 +89,7 @@ typedef enum {
DIGEST_SHA256 = 1,
} digest_algorithm_t;
#define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1)
+#define digest_algorithm_bitfield_t ENUM_BF(digest_algorithm_t)
/** A set of all the digests we know how to compute, taken on a single
* string. Any digests that are shorter than 256 bits are right-padded
@@ -112,6 +110,8 @@ 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_early_init(void);
int crypto_global_init(int hardwareAccel,
const char *accelName,
const char *accelPath);
@@ -183,6 +183,7 @@ crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
int 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);
int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
+int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
/* symmetric crypto */
const char *crypto_cipher_get_key(crypto_cipher_t *env);
@@ -221,9 +222,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,13 +252,14 @@ 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);
double crypto_rand_double(void);
struct tor_weak_rng_t;
void crypto_seed_weak_rng(struct tor_weak_rng_t *rng);
+int crypto_init_siphash_key(void);
char *crypto_random_hostname(int min_rand_len, int max_rand_len,
const char *prefix, const char *suffix);
@@ -290,7 +289,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 +299,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..57018ac2f 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. */
@@ -30,6 +31,11 @@ typedef struct curve25519_keypair_t {
} curve25519_keypair_t;
#ifdef CURVE25519_ENABLED
+/* These functions require that we actually know how to use curve25519 keys.
+ * The other data structures and functions in this header let us parse them,
+ * store them, and move them around.
+ */
+
int curve25519_public_key_is_ok(const curve25519_public_key_t *);
int curve25519_secret_key_generate(curve25519_secret_key_t *key_out,
@@ -52,8 +58,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/get_mozilla_ciphers.py b/src/common/get_mozilla_ciphers.py
deleted file mode 100644
index c7e9a84a0..000000000
--- a/src/common/get_mozilla_ciphers.py
+++ /dev/null
@@ -1,192 +0,0 @@
-#!/usr/bin/python
-# coding=utf-8
-# Copyright 2011, The Tor Project, Inc
-# original version by Arturo Filastò
-# See LICENSE for licensing information
-
-# This script parses Firefox and OpenSSL sources, and uses this information
-# to generate a ciphers.inc file.
-#
-# It takes two arguments: the location of a firefox source directory, and the
-# location of an openssl source directory.
-
-import os
-import re
-import sys
-
-if len(sys.argv) != 3:
- print >>sys.stderr, "Syntax: get_mozilla_ciphers.py <firefox-source-dir> <openssl-source-dir>"
- sys.exit(1)
-
-ff_root = sys.argv[1]
-ossl_root = sys.argv[2]
-
-def ff(s):
- return os.path.join(ff_root, s)
-def ossl(s):
- return os.path.join(ossl_root, s)
-
-#####
-# Read the cpp file to understand what Ciphers map to what name :
-# Make "ciphers" a map from name used in the javascript to a cipher macro name
-fileA = open(ff('security/manager/ssl/src/nsNSSComponent.cpp'),'r')
-
-# The input format is a file containing exactly one section of the form:
-# static CipherPref CipherPrefs[] = {
-# {"name", MACRO_NAME}, // comment
-# ...
-# {NULL, 0}
-# }
-
-inCipherSection = False
-cipherLines = []
-for line in fileA:
- if line.startswith('static CipherPref CipherPrefs'):
- # Get the starting boundary of the Cipher Preferences
- inCipherSection = True
- elif inCipherSection:
- line = line.strip()
- if line.startswith('{NULL, 0}'):
- # At the ending boundary of the Cipher Prefs
- break
- else:
- cipherLines.append(line)
-fileA.close()
-
-# Parse the lines and put them into a dict
-ciphers = {}
-cipher_pref = {}
-for line in cipherLines:
- m = re.search(r'^{\s*\"([^\"]+)\",\s*(\S*)\s*}', line)
- if m:
- key,value = m.groups()
- ciphers[key] = value
- cipher_pref[value] = key
-
-####
-# Now find the correct order for the ciphers
-fileC = open(ff('security/nss/lib/ssl/ssl3con.c'), 'r')
-firefox_ciphers = []
-inEnum=False
-for line in fileC:
- if not inEnum:
- if "ssl3CipherSuiteCfg cipherSuites[" in line:
- inEnum = True
- continue
-
- if line.startswith("};"):
- break
-
- m = re.match(r'^\s*\{\s*([A-Z_0-9]+),', line)
- if m:
- firefox_ciphers.append(m.group(1))
-
-fileC.close()
-
-#####
-# Read the JS file to understand what ciphers are enabled. The format is
-# pref("name", true/false);
-# Build a map enabled_ciphers from javascript name to "true" or "false",
-# and an (unordered!) list of the macro names for those ciphers that are
-# enabled.
-fileB = open(ff('netwerk/base/public/security-prefs.js'), 'r')
-
-enabled_ciphers = {}
-for line in fileB:
- m = re.match(r'pref\(\"([^\"]+)\"\s*,\s*(\S*)\s*\)', line)
- if not m:
- continue
- key, val = m.groups()
- if key.startswith("security.ssl3"):
- enabled_ciphers[key] = val
-fileB.close()
-
-used_ciphers = []
-for k, v in enabled_ciphers.items():
- if v == "true":
- used_ciphers.append(ciphers[k])
-
-#oSSLinclude = ('/usr/include/openssl/ssl3.h', '/usr/include/openssl/ssl.h',
-# '/usr/include/openssl/ssl2.h', '/usr/include/openssl/ssl23.h',
-# '/usr/include/openssl/tls1.h')
-oSSLinclude = ('ssl/ssl3.h', 'ssl/ssl.h',
- 'ssl/ssl2.h', 'ssl/ssl23.h',
- 'ssl/tls1.h')
-
-#####
-# This reads the hex code for the ciphers that are used by firefox.
-# sslProtoD is set to a map from macro name to macro value in sslproto.h;
-# cipher_codes is set to an (unordered!) list of these hex values.
-sslProto = open(ff('security/nss/lib/ssl/sslproto.h'), 'r')
-sslProtoD = {}
-
-for line in sslProto:
- m = re.match('#define\s+(\S+)\s+(\S+)', line)
- if m:
- key, value = m.groups()
- sslProtoD[key] = value
-sslProto.close()
-
-cipher_codes = []
-for x in used_ciphers:
- cipher_codes.append(sslProtoD[x].lower())
-
-####
-# Now read through all the openssl include files, and try to find the openssl
-# macro names for those files.
-openssl_macro_by_hex = {}
-all_openssl_macros = {}
-for fl in oSSLinclude:
- fp = open(ossl(fl), 'r')
- for line in fp.readlines():
- m = re.match('#define\s+(\S+)\s+(\S+)', line)
- if m:
- value,key = m.groups()
- if key.startswith('0x') and "_CK_" in value:
- key = key.replace('0x0300','0x').lower()
- #print "%s %s" % (key, value)
- openssl_macro_by_hex[key] = value
- all_openssl_macros[value]=key
- fp.close()
-
-# Now generate the output.
-print """\
-/* This is an include file used to define the list of ciphers clients should
- * advertise. Before including it, you should define the CIPHER and XCIPHER
- * macros.
- *
- * This file was automatically generated by get_mozilla_ciphers.py.
- */"""
-# Go in order by the order in CipherPrefs
-for firefox_macro in firefox_ciphers:
-
- try:
- js_cipher_name = cipher_pref[firefox_macro]
- except KeyError:
- # This one has no javascript preference.
- continue
-
- # The cipher needs to be enabled in security-prefs.js
- if enabled_ciphers.get(js_cipher_name, 'false') != 'true':
- continue
-
- hexval = sslProtoD[firefox_macro].lower()
-
- try:
- openssl_macro = openssl_macro_by_hex[hexval.lower()]
- openssl_macro = openssl_macro.replace("_CK_", "_TXT_")
- if openssl_macro not in all_openssl_macros:
- raise KeyError()
- format = {'hex':hexval, 'macro':openssl_macro, 'note':""}
- except KeyError:
- # openssl doesn't have a macro for this.
- format = {'hex':hexval, 'macro':firefox_macro,
- 'note':"/* No openssl macro found for "+hexval+" */\n"}
-
- res = """\
-%(note)s#ifdef %(macro)s
- CIPHER(%(hex)s, %(macro)s)
-#else
- XCIPHER(%(hex)s, %(macro)s)
-#endif""" % format
- print res
diff --git a/src/common/include.am b/src/common/include.am
index b796ebfae..61a90cd35 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,21 @@ else
libor_extra_source=
endif
+if USE_MEMPOOLS
+libor_mempool_source=src/common/mempool.c
+libor_mempool_header=src/common/mempool.h
+else
+libor_mempool_source=
+libor_mempool_header=
+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,26 +52,27 @@ 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 \
src/common/log.c \
src/common/memarea.c \
- src/common/mempool.c \
src/common/procmon.c \
src/common/util.c \
src/common/util_codedigest.c \
- $(libor_extra_source)
+ src/common/sandbox.c \
+ src/ext/csiphash.c \
+ $(libor_extra_source) \
+ $(libor_mempool_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 +80,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 \
@@ -70,13 +110,16 @@ COMMONHEADERS = \
src/common/crypto_curve25519.h \
src/common/di_ops.h \
src/common/memarea.h \
- src/common/mempool.h \
+ src/common/linux_syscalls.inc \
src/common/procmon.h \
+ src/common/sandbox.h \
+ src/common/testsupport.h \
src/common/torgzip.h \
src/common/torint.h \
src/common/torlog.h \
src/common/tortls.h \
- src/common/util.h
+ src/common/util.h \
+ $(libor_mempool_header)
noinst_HEADERS+= $(COMMONHEADERS)
diff --git a/src/common/linux_syscalls.inc b/src/common/linux_syscalls.inc
new file mode 100644
index 000000000..cf47c7380
--- /dev/null
+++ b/src/common/linux_syscalls.inc
@@ -0,0 +1,1153 @@
+/* Automatically generated with
+ gen_linux_syscalls.pl /usr/include/asm/unistd*.h
+ Do not edit.
+ */
+static const struct {
+ int syscall_num; const char *syscall_name;
+} SYSCALLS_BY_NUMBER[] = {
+#ifdef __NR__llseek
+ { __NR__llseek, "_llseek" },
+#endif
+#ifdef __NR__newselect
+ { __NR__newselect, "_newselect" },
+#endif
+#ifdef __NR__sysctl
+ { __NR__sysctl, "_sysctl" },
+#endif
+#ifdef __NR_accept
+ { __NR_accept, "accept" },
+#endif
+#ifdef __NR_accept4
+ { __NR_accept4, "accept4" },
+#endif
+#ifdef __NR_access
+ { __NR_access, "access" },
+#endif
+#ifdef __NR_acct
+ { __NR_acct, "acct" },
+#endif
+#ifdef __NR_add_key
+ { __NR_add_key, "add_key" },
+#endif
+#ifdef __NR_adjtimex
+ { __NR_adjtimex, "adjtimex" },
+#endif
+#ifdef __NR_afs_syscall
+ { __NR_afs_syscall, "afs_syscall" },
+#endif
+#ifdef __NR_alarm
+ { __NR_alarm, "alarm" },
+#endif
+#ifdef __NR_arch_prctl
+ { __NR_arch_prctl, "arch_prctl" },
+#endif
+#ifdef __NR_bdflush
+ { __NR_bdflush, "bdflush" },
+#endif
+#ifdef __NR_bind
+ { __NR_bind, "bind" },
+#endif
+#ifdef __NR_break
+ { __NR_break, "break" },
+#endif
+#ifdef __NR_brk
+ { __NR_brk, "brk" },
+#endif
+#ifdef __NR_capget
+ { __NR_capget, "capget" },
+#endif
+#ifdef __NR_capset
+ { __NR_capset, "capset" },
+#endif
+#ifdef __NR_chdir
+ { __NR_chdir, "chdir" },
+#endif
+#ifdef __NR_chmod
+ { __NR_chmod, "chmod" },
+#endif
+#ifdef __NR_chown
+ { __NR_chown, "chown" },
+#endif
+#ifdef __NR_chown32
+ { __NR_chown32, "chown32" },
+#endif
+#ifdef __NR_chroot
+ { __NR_chroot, "chroot" },
+#endif
+#ifdef __NR_clock_adjtime
+ { __NR_clock_adjtime, "clock_adjtime" },
+#endif
+#ifdef __NR_clock_getres
+ { __NR_clock_getres, "clock_getres" },
+#endif
+#ifdef __NR_clock_gettime
+ { __NR_clock_gettime, "clock_gettime" },
+#endif
+#ifdef __NR_clock_nanosleep
+ { __NR_clock_nanosleep, "clock_nanosleep" },
+#endif
+#ifdef __NR_clock_settime
+ { __NR_clock_settime, "clock_settime" },
+#endif
+#ifdef __NR_clone
+ { __NR_clone, "clone" },
+#endif
+#ifdef __NR_close
+ { __NR_close, "close" },
+#endif
+#ifdef __NR_connect
+ { __NR_connect, "connect" },
+#endif
+#ifdef __NR_creat
+ { __NR_creat, "creat" },
+#endif
+#ifdef __NR_create_module
+ { __NR_create_module, "create_module" },
+#endif
+#ifdef __NR_delete_module
+ { __NR_delete_module, "delete_module" },
+#endif
+#ifdef __NR_dup
+ { __NR_dup, "dup" },
+#endif
+#ifdef __NR_dup2
+ { __NR_dup2, "dup2" },
+#endif
+#ifdef __NR_dup3
+ { __NR_dup3, "dup3" },
+#endif
+#ifdef __NR_epoll_create
+ { __NR_epoll_create, "epoll_create" },
+#endif
+#ifdef __NR_epoll_create1
+ { __NR_epoll_create1, "epoll_create1" },
+#endif
+#ifdef __NR_epoll_ctl
+ { __NR_epoll_ctl, "epoll_ctl" },
+#endif
+#ifdef __NR_epoll_ctl_old
+ { __NR_epoll_ctl_old, "epoll_ctl_old" },
+#endif
+#ifdef __NR_epoll_pwait
+ { __NR_epoll_pwait, "epoll_pwait" },
+#endif
+#ifdef __NR_epoll_wait
+ { __NR_epoll_wait, "epoll_wait" },
+#endif
+#ifdef __NR_epoll_wait_old
+ { __NR_epoll_wait_old, "epoll_wait_old" },
+#endif
+#ifdef __NR_eventfd
+ { __NR_eventfd, "eventfd" },
+#endif
+#ifdef __NR_eventfd2
+ { __NR_eventfd2, "eventfd2" },
+#endif
+#ifdef __NR_execve
+ { __NR_execve, "execve" },
+#endif
+#ifdef __NR_exit
+ { __NR_exit, "exit" },
+#endif
+#ifdef __NR_exit_group
+ { __NR_exit_group, "exit_group" },
+#endif
+#ifdef __NR_faccessat
+ { __NR_faccessat, "faccessat" },
+#endif
+#ifdef __NR_fadvise64
+ { __NR_fadvise64, "fadvise64" },
+#endif
+#ifdef __NR_fadvise64_64
+ { __NR_fadvise64_64, "fadvise64_64" },
+#endif
+#ifdef __NR_fallocate
+ { __NR_fallocate, "fallocate" },
+#endif
+#ifdef __NR_fanotify_init
+ { __NR_fanotify_init, "fanotify_init" },
+#endif
+#ifdef __NR_fanotify_mark
+ { __NR_fanotify_mark, "fanotify_mark" },
+#endif
+#ifdef __NR_fchdir
+ { __NR_fchdir, "fchdir" },
+#endif
+#ifdef __NR_fchmod
+ { __NR_fchmod, "fchmod" },
+#endif
+#ifdef __NR_fchmodat
+ { __NR_fchmodat, "fchmodat" },
+#endif
+#ifdef __NR_fchown
+ { __NR_fchown, "fchown" },
+#endif
+#ifdef __NR_fchown32
+ { __NR_fchown32, "fchown32" },
+#endif
+#ifdef __NR_fchownat
+ { __NR_fchownat, "fchownat" },
+#endif
+#ifdef __NR_fcntl
+ { __NR_fcntl, "fcntl" },
+#endif
+#ifdef __NR_fcntl64
+ { __NR_fcntl64, "fcntl64" },
+#endif
+#ifdef __NR_fdatasync
+ { __NR_fdatasync, "fdatasync" },
+#endif
+#ifdef __NR_fgetxattr
+ { __NR_fgetxattr, "fgetxattr" },
+#endif
+#ifdef __NR_finit_module
+ { __NR_finit_module, "finit_module" },
+#endif
+#ifdef __NR_flistxattr
+ { __NR_flistxattr, "flistxattr" },
+#endif
+#ifdef __NR_flock
+ { __NR_flock, "flock" },
+#endif
+#ifdef __NR_fork
+ { __NR_fork, "fork" },
+#endif
+#ifdef __NR_fremovexattr
+ { __NR_fremovexattr, "fremovexattr" },
+#endif
+#ifdef __NR_fsetxattr
+ { __NR_fsetxattr, "fsetxattr" },
+#endif
+#ifdef __NR_fstat
+ { __NR_fstat, "fstat" },
+#endif
+#ifdef __NR_fstat64
+ { __NR_fstat64, "fstat64" },
+#endif
+#ifdef __NR_fstatat64
+ { __NR_fstatat64, "fstatat64" },
+#endif
+#ifdef __NR_fstatfs
+ { __NR_fstatfs, "fstatfs" },
+#endif
+#ifdef __NR_fstatfs64
+ { __NR_fstatfs64, "fstatfs64" },
+#endif
+#ifdef __NR_fsync
+ { __NR_fsync, "fsync" },
+#endif
+#ifdef __NR_ftime
+ { __NR_ftime, "ftime" },
+#endif
+#ifdef __NR_ftruncate
+ { __NR_ftruncate, "ftruncate" },
+#endif
+#ifdef __NR_ftruncate64
+ { __NR_ftruncate64, "ftruncate64" },
+#endif
+#ifdef __NR_futex
+ { __NR_futex, "futex" },
+#endif
+#ifdef __NR_futimesat
+ { __NR_futimesat, "futimesat" },
+#endif
+#ifdef __NR_get_kernel_syms
+ { __NR_get_kernel_syms, "get_kernel_syms" },
+#endif
+#ifdef __NR_get_mempolicy
+ { __NR_get_mempolicy, "get_mempolicy" },
+#endif
+#ifdef __NR_get_robust_list
+ { __NR_get_robust_list, "get_robust_list" },
+#endif
+#ifdef __NR_get_thread_area
+ { __NR_get_thread_area, "get_thread_area" },
+#endif
+#ifdef __NR_getcpu
+ { __NR_getcpu, "getcpu" },
+#endif
+#ifdef __NR_getcwd
+ { __NR_getcwd, "getcwd" },
+#endif
+#ifdef __NR_getdents
+ { __NR_getdents, "getdents" },
+#endif
+#ifdef __NR_getdents64
+ { __NR_getdents64, "getdents64" },
+#endif
+#ifdef __NR_getegid
+ { __NR_getegid, "getegid" },
+#endif
+#ifdef __NR_getegid32
+ { __NR_getegid32, "getegid32" },
+#endif
+#ifdef __NR_geteuid
+ { __NR_geteuid, "geteuid" },
+#endif
+#ifdef __NR_geteuid32
+ { __NR_geteuid32, "geteuid32" },
+#endif
+#ifdef __NR_getgid
+ { __NR_getgid, "getgid" },
+#endif
+#ifdef __NR_getgid32
+ { __NR_getgid32, "getgid32" },
+#endif
+#ifdef __NR_getgroups
+ { __NR_getgroups, "getgroups" },
+#endif
+#ifdef __NR_getgroups32
+ { __NR_getgroups32, "getgroups32" },
+#endif
+#ifdef __NR_getitimer
+ { __NR_getitimer, "getitimer" },
+#endif
+#ifdef __NR_getpeername
+ { __NR_getpeername, "getpeername" },
+#endif
+#ifdef __NR_getpgid
+ { __NR_getpgid, "getpgid" },
+#endif
+#ifdef __NR_getpgrp
+ { __NR_getpgrp, "getpgrp" },
+#endif
+#ifdef __NR_getpid
+ { __NR_getpid, "getpid" },
+#endif
+#ifdef __NR_getpmsg
+ { __NR_getpmsg, "getpmsg" },
+#endif
+#ifdef __NR_getppid
+ { __NR_getppid, "getppid" },
+#endif
+#ifdef __NR_getpriority
+ { __NR_getpriority, "getpriority" },
+#endif
+#ifdef __NR_getresgid
+ { __NR_getresgid, "getresgid" },
+#endif
+#ifdef __NR_getresgid32
+ { __NR_getresgid32, "getresgid32" },
+#endif
+#ifdef __NR_getresuid
+ { __NR_getresuid, "getresuid" },
+#endif
+#ifdef __NR_getresuid32
+ { __NR_getresuid32, "getresuid32" },
+#endif
+#ifdef __NR_getrlimit
+ { __NR_getrlimit, "getrlimit" },
+#endif
+#ifdef __NR_getrusage
+ { __NR_getrusage, "getrusage" },
+#endif
+#ifdef __NR_getsid
+ { __NR_getsid, "getsid" },
+#endif
+#ifdef __NR_getsockname
+ { __NR_getsockname, "getsockname" },
+#endif
+#ifdef __NR_getsockopt
+ { __NR_getsockopt, "getsockopt" },
+#endif
+#ifdef __NR_gettid
+ { __NR_gettid, "gettid" },
+#endif
+#ifdef __NR_gettimeofday
+ { __NR_gettimeofday, "gettimeofday" },
+#endif
+#ifdef __NR_getuid
+ { __NR_getuid, "getuid" },
+#endif
+#ifdef __NR_getuid32
+ { __NR_getuid32, "getuid32" },
+#endif
+#ifdef __NR_getxattr
+ { __NR_getxattr, "getxattr" },
+#endif
+#ifdef __NR_gtty
+ { __NR_gtty, "gtty" },
+#endif
+#ifdef __NR_idle
+ { __NR_idle, "idle" },
+#endif
+#ifdef __NR_init_module
+ { __NR_init_module, "init_module" },
+#endif
+#ifdef __NR_inotify_add_watch
+ { __NR_inotify_add_watch, "inotify_add_watch" },
+#endif
+#ifdef __NR_inotify_init
+ { __NR_inotify_init, "inotify_init" },
+#endif
+#ifdef __NR_inotify_init1
+ { __NR_inotify_init1, "inotify_init1" },
+#endif
+#ifdef __NR_inotify_rm_watch
+ { __NR_inotify_rm_watch, "inotify_rm_watch" },
+#endif
+#ifdef __NR_io_cancel
+ { __NR_io_cancel, "io_cancel" },
+#endif
+#ifdef __NR_io_destroy
+ { __NR_io_destroy, "io_destroy" },
+#endif
+#ifdef __NR_io_getevents
+ { __NR_io_getevents, "io_getevents" },
+#endif
+#ifdef __NR_io_setup
+ { __NR_io_setup, "io_setup" },
+#endif
+#ifdef __NR_io_submit
+ { __NR_io_submit, "io_submit" },
+#endif
+#ifdef __NR_ioctl
+ { __NR_ioctl, "ioctl" },
+#endif
+#ifdef __NR_ioperm
+ { __NR_ioperm, "ioperm" },
+#endif
+#ifdef __NR_iopl
+ { __NR_iopl, "iopl" },
+#endif
+#ifdef __NR_ioprio_get
+ { __NR_ioprio_get, "ioprio_get" },
+#endif
+#ifdef __NR_ioprio_set
+ { __NR_ioprio_set, "ioprio_set" },
+#endif
+#ifdef __NR_ipc
+ { __NR_ipc, "ipc" },
+#endif
+#ifdef __NR_kcmp
+ { __NR_kcmp, "kcmp" },
+#endif
+#ifdef __NR_kexec_load
+ { __NR_kexec_load, "kexec_load" },
+#endif
+#ifdef __NR_keyctl
+ { __NR_keyctl, "keyctl" },
+#endif
+#ifdef __NR_kill
+ { __NR_kill, "kill" },
+#endif
+#ifdef __NR_lchown
+ { __NR_lchown, "lchown" },
+#endif
+#ifdef __NR_lchown32
+ { __NR_lchown32, "lchown32" },
+#endif
+#ifdef __NR_lgetxattr
+ { __NR_lgetxattr, "lgetxattr" },
+#endif
+#ifdef __NR_link
+ { __NR_link, "link" },
+#endif
+#ifdef __NR_linkat
+ { __NR_linkat, "linkat" },
+#endif
+#ifdef __NR_listen
+ { __NR_listen, "listen" },
+#endif
+#ifdef __NR_listxattr
+ { __NR_listxattr, "listxattr" },
+#endif
+#ifdef __NR_llistxattr
+ { __NR_llistxattr, "llistxattr" },
+#endif
+#ifdef __NR_lock
+ { __NR_lock, "lock" },
+#endif
+#ifdef __NR_lookup_dcookie
+ { __NR_lookup_dcookie, "lookup_dcookie" },
+#endif
+#ifdef __NR_lremovexattr
+ { __NR_lremovexattr, "lremovexattr" },
+#endif
+#ifdef __NR_lseek
+ { __NR_lseek, "lseek" },
+#endif
+#ifdef __NR_lsetxattr
+ { __NR_lsetxattr, "lsetxattr" },
+#endif
+#ifdef __NR_lstat
+ { __NR_lstat, "lstat" },
+#endif
+#ifdef __NR_lstat64
+ { __NR_lstat64, "lstat64" },
+#endif
+#ifdef __NR_madvise
+ { __NR_madvise, "madvise" },
+#endif
+#ifdef __NR_mbind
+ { __NR_mbind, "mbind" },
+#endif
+#ifdef __NR_migrate_pages
+ { __NR_migrate_pages, "migrate_pages" },
+#endif
+#ifdef __NR_mincore
+ { __NR_mincore, "mincore" },
+#endif
+#ifdef __NR_mkdir
+ { __NR_mkdir, "mkdir" },
+#endif
+#ifdef __NR_mkdirat
+ { __NR_mkdirat, "mkdirat" },
+#endif
+#ifdef __NR_mknod
+ { __NR_mknod, "mknod" },
+#endif
+#ifdef __NR_mknodat
+ { __NR_mknodat, "mknodat" },
+#endif
+#ifdef __NR_mlock
+ { __NR_mlock, "mlock" },
+#endif
+#ifdef __NR_mlockall
+ { __NR_mlockall, "mlockall" },
+#endif
+#ifdef __NR_mmap
+ { __NR_mmap, "mmap" },
+#endif
+#ifdef __NR_mmap2
+ { __NR_mmap2, "mmap2" },
+#endif
+#ifdef __NR_modify_ldt
+ { __NR_modify_ldt, "modify_ldt" },
+#endif
+#ifdef __NR_mount
+ { __NR_mount, "mount" },
+#endif
+#ifdef __NR_move_pages
+ { __NR_move_pages, "move_pages" },
+#endif
+#ifdef __NR_mprotect
+ { __NR_mprotect, "mprotect" },
+#endif
+#ifdef __NR_mpx
+ { __NR_mpx, "mpx" },
+#endif
+#ifdef __NR_mq_getsetattr
+ { __NR_mq_getsetattr, "mq_getsetattr" },
+#endif
+#ifdef __NR_mq_notify
+ { __NR_mq_notify, "mq_notify" },
+#endif
+#ifdef __NR_mq_open
+ { __NR_mq_open, "mq_open" },
+#endif
+#ifdef __NR_mq_timedreceive
+ { __NR_mq_timedreceive, "mq_timedreceive" },
+#endif
+#ifdef __NR_mq_timedsend
+ { __NR_mq_timedsend, "mq_timedsend" },
+#endif
+#ifdef __NR_mq_unlink
+ { __NR_mq_unlink, "mq_unlink" },
+#endif
+#ifdef __NR_mremap
+ { __NR_mremap, "mremap" },
+#endif
+#ifdef __NR_msgctl
+ { __NR_msgctl, "msgctl" },
+#endif
+#ifdef __NR_msgget
+ { __NR_msgget, "msgget" },
+#endif
+#ifdef __NR_msgrcv
+ { __NR_msgrcv, "msgrcv" },
+#endif
+#ifdef __NR_msgsnd
+ { __NR_msgsnd, "msgsnd" },
+#endif
+#ifdef __NR_msync
+ { __NR_msync, "msync" },
+#endif
+#ifdef __NR_munlock
+ { __NR_munlock, "munlock" },
+#endif
+#ifdef __NR_munlockall
+ { __NR_munlockall, "munlockall" },
+#endif
+#ifdef __NR_munmap
+ { __NR_munmap, "munmap" },
+#endif
+#ifdef __NR_name_to_handle_at
+ { __NR_name_to_handle_at, "name_to_handle_at" },
+#endif
+#ifdef __NR_nanosleep
+ { __NR_nanosleep, "nanosleep" },
+#endif
+#ifdef __NR_newfstatat
+ { __NR_newfstatat, "newfstatat" },
+#endif
+#ifdef __NR_nfsservctl
+ { __NR_nfsservctl, "nfsservctl" },
+#endif
+#ifdef __NR_nice
+ { __NR_nice, "nice" },
+#endif
+#ifdef __NR_oldfstat
+ { __NR_oldfstat, "oldfstat" },
+#endif
+#ifdef __NR_oldlstat
+ { __NR_oldlstat, "oldlstat" },
+#endif
+#ifdef __NR_oldolduname
+ { __NR_oldolduname, "oldolduname" },
+#endif
+#ifdef __NR_oldstat
+ { __NR_oldstat, "oldstat" },
+#endif
+#ifdef __NR_olduname
+ { __NR_olduname, "olduname" },
+#endif
+#ifdef __NR_open
+ { __NR_open, "open" },
+#endif
+#ifdef __NR_open_by_handle_at
+ { __NR_open_by_handle_at, "open_by_handle_at" },
+#endif
+#ifdef __NR_openat
+ { __NR_openat, "openat" },
+#endif
+#ifdef __NR_pause
+ { __NR_pause, "pause" },
+#endif
+#ifdef __NR_perf_event_open
+ { __NR_perf_event_open, "perf_event_open" },
+#endif
+#ifdef __NR_personality
+ { __NR_personality, "personality" },
+#endif
+#ifdef __NR_pipe
+ { __NR_pipe, "pipe" },
+#endif
+#ifdef __NR_pipe2
+ { __NR_pipe2, "pipe2" },
+#endif
+#ifdef __NR_pivot_root
+ { __NR_pivot_root, "pivot_root" },
+#endif
+#ifdef __NR_poll
+ { __NR_poll, "poll" },
+#endif
+#ifdef __NR_ppoll
+ { __NR_ppoll, "ppoll" },
+#endif
+#ifdef __NR_prctl
+ { __NR_prctl, "prctl" },
+#endif
+#ifdef __NR_pread64
+ { __NR_pread64, "pread64" },
+#endif
+#ifdef __NR_preadv
+ { __NR_preadv, "preadv" },
+#endif
+#ifdef __NR_prlimit64
+ { __NR_prlimit64, "prlimit64" },
+#endif
+#ifdef __NR_process_vm_readv
+ { __NR_process_vm_readv, "process_vm_readv" },
+#endif
+#ifdef __NR_process_vm_writev
+ { __NR_process_vm_writev, "process_vm_writev" },
+#endif
+#ifdef __NR_prof
+ { __NR_prof, "prof" },
+#endif
+#ifdef __NR_profil
+ { __NR_profil, "profil" },
+#endif
+#ifdef __NR_pselect6
+ { __NR_pselect6, "pselect6" },
+#endif
+#ifdef __NR_ptrace
+ { __NR_ptrace, "ptrace" },
+#endif
+#ifdef __NR_putpmsg
+ { __NR_putpmsg, "putpmsg" },
+#endif
+#ifdef __NR_pwrite64
+ { __NR_pwrite64, "pwrite64" },
+#endif
+#ifdef __NR_pwritev
+ { __NR_pwritev, "pwritev" },
+#endif
+#ifdef __NR_query_module
+ { __NR_query_module, "query_module" },
+#endif
+#ifdef __NR_quotactl
+ { __NR_quotactl, "quotactl" },
+#endif
+#ifdef __NR_read
+ { __NR_read, "read" },
+#endif
+#ifdef __NR_readahead
+ { __NR_readahead, "readahead" },
+#endif
+#ifdef __NR_readdir
+ { __NR_readdir, "readdir" },
+#endif
+#ifdef __NR_readlink
+ { __NR_readlink, "readlink" },
+#endif
+#ifdef __NR_readlinkat
+ { __NR_readlinkat, "readlinkat" },
+#endif
+#ifdef __NR_readv
+ { __NR_readv, "readv" },
+#endif
+#ifdef __NR_reboot
+ { __NR_reboot, "reboot" },
+#endif
+#ifdef __NR_recvfrom
+ { __NR_recvfrom, "recvfrom" },
+#endif
+#ifdef __NR_recvmmsg
+ { __NR_recvmmsg, "recvmmsg" },
+#endif
+#ifdef __NR_recvmsg
+ { __NR_recvmsg, "recvmsg" },
+#endif
+#ifdef __NR_remap_file_pages
+ { __NR_remap_file_pages, "remap_file_pages" },
+#endif
+#ifdef __NR_removexattr
+ { __NR_removexattr, "removexattr" },
+#endif
+#ifdef __NR_rename
+ { __NR_rename, "rename" },
+#endif
+#ifdef __NR_renameat
+ { __NR_renameat, "renameat" },
+#endif
+#ifdef __NR_request_key
+ { __NR_request_key, "request_key" },
+#endif
+#ifdef __NR_restart_syscall
+ { __NR_restart_syscall, "restart_syscall" },
+#endif
+#ifdef __NR_rmdir
+ { __NR_rmdir, "rmdir" },
+#endif
+#ifdef __NR_rt_sigaction
+ { __NR_rt_sigaction, "rt_sigaction" },
+#endif
+#ifdef __NR_rt_sigpending
+ { __NR_rt_sigpending, "rt_sigpending" },
+#endif
+#ifdef __NR_rt_sigprocmask
+ { __NR_rt_sigprocmask, "rt_sigprocmask" },
+#endif
+#ifdef __NR_rt_sigqueueinfo
+ { __NR_rt_sigqueueinfo, "rt_sigqueueinfo" },
+#endif
+#ifdef __NR_rt_sigreturn
+ { __NR_rt_sigreturn, "rt_sigreturn" },
+#endif
+#ifdef __NR_rt_sigsuspend
+ { __NR_rt_sigsuspend, "rt_sigsuspend" },
+#endif
+#ifdef __NR_rt_sigtimedwait
+ { __NR_rt_sigtimedwait, "rt_sigtimedwait" },
+#endif
+#ifdef __NR_rt_tgsigqueueinfo
+ { __NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo" },
+#endif
+#ifdef __NR_sched_get_priority_max
+ { __NR_sched_get_priority_max, "sched_get_priority_max" },
+#endif
+#ifdef __NR_sched_get_priority_min
+ { __NR_sched_get_priority_min, "sched_get_priority_min" },
+#endif
+#ifdef __NR_sched_getaffinity
+ { __NR_sched_getaffinity, "sched_getaffinity" },
+#endif
+#ifdef __NR_sched_getparam
+ { __NR_sched_getparam, "sched_getparam" },
+#endif
+#ifdef __NR_sched_getscheduler
+ { __NR_sched_getscheduler, "sched_getscheduler" },
+#endif
+#ifdef __NR_sched_rr_get_interval
+ { __NR_sched_rr_get_interval, "sched_rr_get_interval" },
+#endif
+#ifdef __NR_sched_setaffinity
+ { __NR_sched_setaffinity, "sched_setaffinity" },
+#endif
+#ifdef __NR_sched_setparam
+ { __NR_sched_setparam, "sched_setparam" },
+#endif
+#ifdef __NR_sched_setscheduler
+ { __NR_sched_setscheduler, "sched_setscheduler" },
+#endif
+#ifdef __NR_sched_yield
+ { __NR_sched_yield, "sched_yield" },
+#endif
+#ifdef __NR_security
+ { __NR_security, "security" },
+#endif
+#ifdef __NR_select
+ { __NR_select, "select" },
+#endif
+#ifdef __NR_semctl
+ { __NR_semctl, "semctl" },
+#endif
+#ifdef __NR_semget
+ { __NR_semget, "semget" },
+#endif
+#ifdef __NR_semop
+ { __NR_semop, "semop" },
+#endif
+#ifdef __NR_semtimedop
+ { __NR_semtimedop, "semtimedop" },
+#endif
+#ifdef __NR_sendfile
+ { __NR_sendfile, "sendfile" },
+#endif
+#ifdef __NR_sendfile64
+ { __NR_sendfile64, "sendfile64" },
+#endif
+#ifdef __NR_sendmmsg
+ { __NR_sendmmsg, "sendmmsg" },
+#endif
+#ifdef __NR_sendmsg
+ { __NR_sendmsg, "sendmsg" },
+#endif
+#ifdef __NR_sendto
+ { __NR_sendto, "sendto" },
+#endif
+#ifdef __NR_set_mempolicy
+ { __NR_set_mempolicy, "set_mempolicy" },
+#endif
+#ifdef __NR_set_robust_list
+ { __NR_set_robust_list, "set_robust_list" },
+#endif
+#ifdef __NR_set_thread_area
+ { __NR_set_thread_area, "set_thread_area" },
+#endif
+#ifdef __NR_set_tid_address
+ { __NR_set_tid_address, "set_tid_address" },
+#endif
+#ifdef __NR_setdomainname
+ { __NR_setdomainname, "setdomainname" },
+#endif
+#ifdef __NR_setfsgid
+ { __NR_setfsgid, "setfsgid" },
+#endif
+#ifdef __NR_setfsgid32
+ { __NR_setfsgid32, "setfsgid32" },
+#endif
+#ifdef __NR_setfsuid
+ { __NR_setfsuid, "setfsuid" },
+#endif
+#ifdef __NR_setfsuid32
+ { __NR_setfsuid32, "setfsuid32" },
+#endif
+#ifdef __NR_setgid
+ { __NR_setgid, "setgid" },
+#endif
+#ifdef __NR_setgid32
+ { __NR_setgid32, "setgid32" },
+#endif
+#ifdef __NR_setgroups
+ { __NR_setgroups, "setgroups" },
+#endif
+#ifdef __NR_setgroups32
+ { __NR_setgroups32, "setgroups32" },
+#endif
+#ifdef __NR_sethostname
+ { __NR_sethostname, "sethostname" },
+#endif
+#ifdef __NR_setitimer
+ { __NR_setitimer, "setitimer" },
+#endif
+#ifdef __NR_setns
+ { __NR_setns, "setns" },
+#endif
+#ifdef __NR_setpgid
+ { __NR_setpgid, "setpgid" },
+#endif
+#ifdef __NR_setpriority
+ { __NR_setpriority, "setpriority" },
+#endif
+#ifdef __NR_setregid
+ { __NR_setregid, "setregid" },
+#endif
+#ifdef __NR_setregid32
+ { __NR_setregid32, "setregid32" },
+#endif
+#ifdef __NR_setresgid
+ { __NR_setresgid, "setresgid" },
+#endif
+#ifdef __NR_setresgid32
+ { __NR_setresgid32, "setresgid32" },
+#endif
+#ifdef __NR_setresuid
+ { __NR_setresuid, "setresuid" },
+#endif
+#ifdef __NR_setresuid32
+ { __NR_setresuid32, "setresuid32" },
+#endif
+#ifdef __NR_setreuid
+ { __NR_setreuid, "setreuid" },
+#endif
+#ifdef __NR_setreuid32
+ { __NR_setreuid32, "setreuid32" },
+#endif
+#ifdef __NR_setrlimit
+ { __NR_setrlimit, "setrlimit" },
+#endif
+#ifdef __NR_setsid
+ { __NR_setsid, "setsid" },
+#endif
+#ifdef __NR_setsockopt
+ { __NR_setsockopt, "setsockopt" },
+#endif
+#ifdef __NR_settimeofday
+ { __NR_settimeofday, "settimeofday" },
+#endif
+#ifdef __NR_setuid
+ { __NR_setuid, "setuid" },
+#endif
+#ifdef __NR_setuid32
+ { __NR_setuid32, "setuid32" },
+#endif
+#ifdef __NR_setxattr
+ { __NR_setxattr, "setxattr" },
+#endif
+#ifdef __NR_sgetmask
+ { __NR_sgetmask, "sgetmask" },
+#endif
+#ifdef __NR_shmat
+ { __NR_shmat, "shmat" },
+#endif
+#ifdef __NR_shmctl
+ { __NR_shmctl, "shmctl" },
+#endif
+#ifdef __NR_shmdt
+ { __NR_shmdt, "shmdt" },
+#endif
+#ifdef __NR_shmget
+ { __NR_shmget, "shmget" },
+#endif
+#ifdef __NR_shutdown
+ { __NR_shutdown, "shutdown" },
+#endif
+#ifdef __NR_sigaction
+ { __NR_sigaction, "sigaction" },
+#endif
+#ifdef __NR_sigaltstack
+ { __NR_sigaltstack, "sigaltstack" },
+#endif
+#ifdef __NR_signal
+ { __NR_signal, "signal" },
+#endif
+#ifdef __NR_signalfd
+ { __NR_signalfd, "signalfd" },
+#endif
+#ifdef __NR_signalfd4
+ { __NR_signalfd4, "signalfd4" },
+#endif
+#ifdef __NR_sigpending
+ { __NR_sigpending, "sigpending" },
+#endif
+#ifdef __NR_sigprocmask
+ { __NR_sigprocmask, "sigprocmask" },
+#endif
+#ifdef __NR_sigreturn
+ { __NR_sigreturn, "sigreturn" },
+#endif
+#ifdef __NR_sigsuspend
+ { __NR_sigsuspend, "sigsuspend" },
+#endif
+#ifdef __NR_socket
+ { __NR_socket, "socket" },
+#endif
+#ifdef __NR_socketcall
+ { __NR_socketcall, "socketcall" },
+#endif
+#ifdef __NR_socketpair
+ { __NR_socketpair, "socketpair" },
+#endif
+#ifdef __NR_splice
+ { __NR_splice, "splice" },
+#endif
+#ifdef __NR_ssetmask
+ { __NR_ssetmask, "ssetmask" },
+#endif
+#ifdef __NR_stat
+ { __NR_stat, "stat" },
+#endif
+#ifdef __NR_stat64
+ { __NR_stat64, "stat64" },
+#endif
+#ifdef __NR_statfs
+ { __NR_statfs, "statfs" },
+#endif
+#ifdef __NR_statfs64
+ { __NR_statfs64, "statfs64" },
+#endif
+#ifdef __NR_stime
+ { __NR_stime, "stime" },
+#endif
+#ifdef __NR_stty
+ { __NR_stty, "stty" },
+#endif
+#ifdef __NR_swapoff
+ { __NR_swapoff, "swapoff" },
+#endif
+#ifdef __NR_swapon
+ { __NR_swapon, "swapon" },
+#endif
+#ifdef __NR_symlink
+ { __NR_symlink, "symlink" },
+#endif
+#ifdef __NR_symlinkat
+ { __NR_symlinkat, "symlinkat" },
+#endif
+#ifdef __NR_sync
+ { __NR_sync, "sync" },
+#endif
+#ifdef __NR_sync_file_range
+ { __NR_sync_file_range, "sync_file_range" },
+#endif
+#ifdef __NR_syncfs
+ { __NR_syncfs, "syncfs" },
+#endif
+#ifdef __NR_sysfs
+ { __NR_sysfs, "sysfs" },
+#endif
+#ifdef __NR_sysinfo
+ { __NR_sysinfo, "sysinfo" },
+#endif
+#ifdef __NR_syslog
+ { __NR_syslog, "syslog" },
+#endif
+#ifdef __NR_tee
+ { __NR_tee, "tee" },
+#endif
+#ifdef __NR_tgkill
+ { __NR_tgkill, "tgkill" },
+#endif
+#ifdef __NR_time
+ { __NR_time, "time" },
+#endif
+#ifdef __NR_timer_create
+ { __NR_timer_create, "timer_create" },
+#endif
+#ifdef __NR_timer_delete
+ { __NR_timer_delete, "timer_delete" },
+#endif
+#ifdef __NR_timer_getoverrun
+ { __NR_timer_getoverrun, "timer_getoverrun" },
+#endif
+#ifdef __NR_timer_gettime
+ { __NR_timer_gettime, "timer_gettime" },
+#endif
+#ifdef __NR_timer_settime
+ { __NR_timer_settime, "timer_settime" },
+#endif
+#ifdef __NR_timerfd_create
+ { __NR_timerfd_create, "timerfd_create" },
+#endif
+#ifdef __NR_timerfd_gettime
+ { __NR_timerfd_gettime, "timerfd_gettime" },
+#endif
+#ifdef __NR_timerfd_settime
+ { __NR_timerfd_settime, "timerfd_settime" },
+#endif
+#ifdef __NR_times
+ { __NR_times, "times" },
+#endif
+#ifdef __NR_tkill
+ { __NR_tkill, "tkill" },
+#endif
+#ifdef __NR_truncate
+ { __NR_truncate, "truncate" },
+#endif
+#ifdef __NR_truncate64
+ { __NR_truncate64, "truncate64" },
+#endif
+#ifdef __NR_tuxcall
+ { __NR_tuxcall, "tuxcall" },
+#endif
+#ifdef __NR_ugetrlimit
+ { __NR_ugetrlimit, "ugetrlimit" },
+#endif
+#ifdef __NR_ulimit
+ { __NR_ulimit, "ulimit" },
+#endif
+#ifdef __NR_umask
+ { __NR_umask, "umask" },
+#endif
+#ifdef __NR_umount
+ { __NR_umount, "umount" },
+#endif
+#ifdef __NR_umount2
+ { __NR_umount2, "umount2" },
+#endif
+#ifdef __NR_uname
+ { __NR_uname, "uname" },
+#endif
+#ifdef __NR_unlink
+ { __NR_unlink, "unlink" },
+#endif
+#ifdef __NR_unlinkat
+ { __NR_unlinkat, "unlinkat" },
+#endif
+#ifdef __NR_unshare
+ { __NR_unshare, "unshare" },
+#endif
+#ifdef __NR_uselib
+ { __NR_uselib, "uselib" },
+#endif
+#ifdef __NR_ustat
+ { __NR_ustat, "ustat" },
+#endif
+#ifdef __NR_utime
+ { __NR_utime, "utime" },
+#endif
+#ifdef __NR_utimensat
+ { __NR_utimensat, "utimensat" },
+#endif
+#ifdef __NR_utimes
+ { __NR_utimes, "utimes" },
+#endif
+#ifdef __NR_vfork
+ { __NR_vfork, "vfork" },
+#endif
+#ifdef __NR_vhangup
+ { __NR_vhangup, "vhangup" },
+#endif
+#ifdef __NR_vm86
+ { __NR_vm86, "vm86" },
+#endif
+#ifdef __NR_vm86old
+ { __NR_vm86old, "vm86old" },
+#endif
+#ifdef __NR_vmsplice
+ { __NR_vmsplice, "vmsplice" },
+#endif
+#ifdef __NR_vserver
+ { __NR_vserver, "vserver" },
+#endif
+#ifdef __NR_wait4
+ { __NR_wait4, "wait4" },
+#endif
+#ifdef __NR_waitid
+ { __NR_waitid, "waitid" },
+#endif
+#ifdef __NR_waitpid
+ { __NR_waitpid, "waitpid" },
+#endif
+#ifdef __NR_write
+ { __NR_write, "write" },
+#endif
+#ifdef __NR_writev
+ { __NR_writev, "writev" },
+#endif
+ {0, NULL}
+};
+
diff --git a/src/common/log.c b/src/common/log.c
index e196a1128..517fa4faa 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;
@@ -143,9 +147,6 @@ static INLINE char *format_msg(char *buf, size_t buf_len,
const char *suffix,
const char *format, va_list ap, size_t *msg_len_out)
CHECK_PRINTF(7,0);
-static void logv(int severity, log_domain_mask_t domain, const char *funcname,
- const char *suffix, const char *format, va_list ap)
- CHECK_PRINTF(5,0);
/** Name of the application: used to generate the message we write at the
* start of each new log. */
@@ -332,9 +333,9 @@ format_msg(char *buf, size_t buf_len,
* <b>severity</b>. If provided, <b>funcname</b> is prepended to the
* message. The actual message is derived as from tor_snprintf(format,ap).
*/
-static void
-logv(int severity, log_domain_mask_t domain, const char *funcname,
- const char *suffix, const char *format, va_list ap)
+MOCK_IMPL(STATIC void,
+logv,(int severity, log_domain_mask_t domain, const char *funcname,
+ const char *suffix, const char *format, va_list ap))
{
char buf[10024];
size_t msg_len = 0;
@@ -439,6 +440,149 @@ 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();
+}
+
+/** Add to <b>out</b> a copy of every currently configured log file name. Used
+ * to enable access to these filenames with the sandbox code. */
+void
+tor_log_get_logfile_names(smartlist_t *out)
+{
+ logfile_t *lf;
+ tor_assert(out);
+
+ LOCK_LOGS();
+
+ for (lf = logfiles; lf; lf = lf->next) {
+ if (lf->is_temporary || lf->is_syslog || lf->callback)
+ continue;
+ if (lf->filename == NULL)
+ continue;
+ smartlist_add(out, tor_strdup(lf->filename));
+ }
+
+ 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
@@ -1153,38 +1297,3 @@ switch_logs_debug(void)
UNLOCK_LOGS();
}
-#if 0
-static void
-dump_log_info(logfile_t *lf)
-{
- const char *tp;
-
- if (lf->filename) {
- printf("=== log into \"%s\" (%s-%s) (%stemporary)\n", lf->filename,
- sev_to_string(lf->min_loglevel),
- sev_to_string(lf->max_loglevel),
- lf->is_temporary?"":"not ");
- } else if (lf->is_syslog) {
- printf("=== syslog (%s-%s) (%stemporary)\n",
- sev_to_string(lf->min_loglevel),
- sev_to_string(lf->max_loglevel),
- lf->is_temporary?"":"not ");
- } else {
- printf("=== log (%s-%s) (%stemporary)\n",
- sev_to_string(lf->min_loglevel),
- sev_to_string(lf->max_loglevel),
- lf->is_temporary?"":"not ");
- }
-}
-
-void
-describe_logs(void)
-{
- logfile_t *lf;
- printf("==== BEGIN LOGS ====\n");
- for (lf = logfiles; lf; lf = lf->next)
- dump_log_info(lf);
- printf("==== END LOGS ====\n");
-}
-#endif
-
diff --git a/src/common/memarea.c b/src/common/memarea.c
index 0ae0ccca1..bcaea0949 100644
--- a/src/common/memarea.c
+++ b/src/common/memarea.c
@@ -29,6 +29,13 @@
#error "void* is neither 4 nor 8 bytes long. I don't know how to align stuff."
#endif
+#if defined(__GNUC__) && defined(FLEXIBLE_ARRAY_MEMBER)
+#define USE_ALIGNED_ATTRIBUTE
+#define U_MEM mem
+#else
+#define U_MEM u.mem
+#endif
+
#ifdef USE_SENTINELS
/** Magic value that we stick at the end of a memarea so we can make sure
* there are no run-off-the-end bugs. */
@@ -39,12 +46,12 @@
* end, set those bytes. */
#define SET_SENTINEL(chunk) \
STMT_BEGIN \
- set_uint32( &(chunk)->u.mem[chunk->mem_size], SENTINEL_VAL ); \
+ set_uint32( &(chunk)->U_MEM[chunk->mem_size], SENTINEL_VAL ); \
STMT_END
/** Assert that the sentinel on a memarea is set correctly. */
#define CHECK_SENTINEL(chunk) \
STMT_BEGIN \
- uint32_t sent_val = get_uint32(&(chunk)->u.mem[chunk->mem_size]); \
+ uint32_t sent_val = get_uint32(&(chunk)->U_MEM[chunk->mem_size]); \
tor_assert(sent_val == SENTINEL_VAL); \
STMT_END
#else
@@ -71,19 +78,23 @@ realign_pointer(void *ptr)
typedef struct memarea_chunk_t {
/** Next chunk in this area. Only kept around so we can free it. */
struct memarea_chunk_t *next_chunk;
- size_t mem_size; /**< How much RAM is available in u.mem, total? */
- char *next_mem; /**< Next position in u.mem to allocate data at. If it's
+ size_t mem_size; /**< How much RAM is available in mem, total? */
+ char *next_mem; /**< Next position in mem to allocate data at. If it's
* greater than or equal to mem+mem_size, this chunk is
* full. */
+#ifdef USE_ALIGNED_ATTRIBUTE
+ char mem[FLEXIBLE_ARRAY_MEMBER] __attribute__((aligned(MEMAREA_ALIGN)));
+#else
union {
char mem[1]; /**< Memory space in this chunk. */
void *void_for_alignment_; /**< Dummy; used to make sure mem is aligned. */
} u;
+#endif
} memarea_chunk_t;
/** How many bytes are needed for overhead before we get to the memory part
* of a chunk? */
-#define CHUNK_HEADER_SIZE STRUCT_OFFSET(memarea_chunk_t, u)
+#define CHUNK_HEADER_SIZE STRUCT_OFFSET(memarea_chunk_t, U_MEM)
/** What's the smallest that we'll allocate a chunk? */
#define CHUNK_SIZE 4096
@@ -121,7 +132,7 @@ alloc_chunk(size_t sz, int freelist_ok)
res = tor_malloc(chunk_size);
res->next_chunk = NULL;
res->mem_size = chunk_size - CHUNK_HEADER_SIZE - SENTINEL_LEN;
- res->next_mem = res->u.mem;
+ res->next_mem = res->U_MEM;
tor_assert(res->next_mem+res->mem_size+SENTINEL_LEN ==
((char*)res)+chunk_size);
tor_assert(realign_pointer(res->next_mem) == res->next_mem);
@@ -140,7 +151,7 @@ chunk_free_unchecked(memarea_chunk_t *chunk)
++freelist_len;
chunk->next_chunk = freelist;
freelist = chunk;
- chunk->next_mem = chunk->u.mem;
+ chunk->next_mem = chunk->U_MEM;
} else {
tor_free(chunk);
}
@@ -183,7 +194,7 @@ memarea_clear(memarea_t *area)
}
area->first->next_chunk = NULL;
}
- area->first->next_mem = area->first->u.mem;
+ area->first->next_mem = area->first->U_MEM;
}
/** Remove all unused memarea chunks from the internal freelist. */
@@ -207,7 +218,7 @@ memarea_owns_ptr(const memarea_t *area, const void *p)
memarea_chunk_t *chunk;
const char *ptr = p;
for (chunk = area->first; chunk; chunk = chunk->next_chunk) {
- if (ptr >= chunk->u.mem && ptr < chunk->next_mem)
+ if (ptr >= chunk->U_MEM && ptr < chunk->next_mem)
return 1;
}
return 0;
@@ -226,7 +237,7 @@ memarea_alloc(memarea_t *area, size_t sz)
tor_assert(sz < SIZE_T_CEILING);
if (sz == 0)
sz = 1;
- if (chunk->next_mem+sz > chunk->u.mem+chunk->mem_size) {
+ if (chunk->next_mem+sz > chunk->U_MEM+chunk->mem_size) {
if (sz+CHUNK_HEADER_SIZE >= CHUNK_SIZE) {
/* This allocation is too big. Stick it in a special chunk, and put
* that chunk second in the list. */
@@ -244,8 +255,8 @@ memarea_alloc(memarea_t *area, size_t sz)
result = chunk->next_mem;
chunk->next_mem = chunk->next_mem + sz;
/* Reinstate these if bug 930 ever comes back
- tor_assert(chunk->next_mem >= chunk->u.mem);
- tor_assert(chunk->next_mem <= chunk->u.mem+chunk->mem_size);
+ tor_assert(chunk->next_mem >= chunk->U_MEM);
+ tor_assert(chunk->next_mem <= chunk->U_MEM+chunk->mem_size);
*/
chunk->next_mem = realign_pointer(chunk->next_mem);
return result;
@@ -280,14 +291,11 @@ memarea_strdup(memarea_t *area, const char *s)
char *
memarea_strndup(memarea_t *area, const char *s, size_t n)
{
- size_t ln;
+ size_t ln = 0;
char *result;
- const char *cp, *end = s+n;
tor_assert(n < SIZE_T_CEILING);
- for (cp = s; cp < end && *cp; ++cp)
+ for (ln = 0; ln < n && s[ln]; ++ln)
;
- /* cp now points to s+n, or to the 0 in the string. */
- ln = cp-s;
result = memarea_alloc(area, ln+1);
memcpy(result, s, ln);
result[ln]='\0';
@@ -304,8 +312,8 @@ memarea_get_stats(memarea_t *area, size_t *allocated_out, size_t *used_out)
for (chunk = area->first; chunk; chunk = chunk->next_chunk) {
CHECK_SENTINEL(chunk);
a += CHUNK_HEADER_SIZE + chunk->mem_size;
- tor_assert(chunk->next_mem >= chunk->u.mem);
- u += CHUNK_HEADER_SIZE + (chunk->next_mem - chunk->u.mem);
+ tor_assert(chunk->next_mem >= chunk->U_MEM);
+ u += CHUNK_HEADER_SIZE + (chunk->next_mem - chunk->U_MEM);
}
*allocated_out = a;
*used_out = u;
@@ -320,9 +328,9 @@ memarea_assert_ok(memarea_t *area)
for (chunk = area->first; chunk; chunk = chunk->next_chunk) {
CHECK_SENTINEL(chunk);
- tor_assert(chunk->next_mem >= chunk->u.mem);
+ tor_assert(chunk->next_mem >= chunk->U_MEM);
tor_assert(chunk->next_mem <=
- (char*) realign_pointer(chunk->u.mem+chunk->mem_size));
+ (char*) realign_pointer(chunk->U_MEM+chunk->mem_size));
}
}
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
new file mode 100644
index 000000000..05b91be7b
--- /dev/null
+++ b/src/common/sandbox.c
@@ -0,0 +1,1838 @@
+/* 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 "container.h"
+#include "torlog.h"
+#include "torint.h"
+#include "util.h"
+#include "tor_queue.h"
+
+#include "ht.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 <stdarg.h>
+#include <seccomp.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <poll.h>
+
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
+ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
+#define USE_BACKTRACE
+#define EXPOSE_CLEAN_BACKTRACE
+#include "backtrace.h"
+#endif
+
+#ifdef USE_BACKTRACE
+#include <execinfo.h>
+#endif
+
+/**
+ * Linux 32 bit definitions
+ */
+#if defined(__i386__)
+
+#define REG_SYSCALL REG_EAX
+#define M_SYSCALL gregs[REG_SYSCALL]
+
+/**
+ * Linux 64 bit definitions
+ */
+#elif defined(__x86_64__)
+
+#define REG_SYSCALL REG_RAX
+#define M_SYSCALL gregs[REG_SYSCALL]
+
+#elif defined(__arm__)
+
+#define M_SYSCALL arm_r7
+
+#endif
+
+/**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;
+
+#undef SCMP_CMP
+#define SCMP_CMP(a,b,c) ((struct scmp_arg_cmp){(a),(b),(c),0})
+#define SCMP_CMP4(a,b,c,d) ((struct scmp_arg_cmp){(a),(b),(c),(d)})
+/* We use a wrapper here because these masked comparisons seem to be pretty
+ * verbose. Also, it's important to cast to scmp_datum_t before negating the
+ * mask, since otherwise the negation might get applied to a 32 bit value, and
+ * the high bits of the value might get masked out improperly. */
+#define SCMP_CMP_MASKED(a,b,c) \
+ SCMP_CMP4((a), SCMP_CMP_MASKED_EQ, ~(scmp_datum_t)(b), (c))
+
+/** 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
+#ifdef __NR_getrlimit
+ SCMP_SYS(getrlimit),
+#endif
+ SCMP_SYS(gettimeofday),
+ SCMP_SYS(gettid),
+ 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),
+#ifdef __NR_mmap
+ /* XXXX restrict this in the same ways as mmap2 */
+ SCMP_SYS(mmap),
+#endif
+ SCMP_SYS(munmap),
+ SCMP_SYS(read),
+ SCMP_SYS(rt_sigreturn),
+ SCMP_SYS(sched_getaffinity),
+ SCMP_SYS(set_robust_list),
+#ifdef __NR_sigreturn
+ SCMP_SYS(sigreturn),
+#endif
+ SCMP_SYS(stat),
+ SCMP_SYS(uname),
+ SCMP_SYS(write),
+ SCMP_SYS(writev),
+ 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(listen),
+ SCMP_SYS(connect),
+ SCMP_SYS(getsockname),
+ SCMP_SYS(recvmsg),
+ SCMP_SYS(recvfrom),
+ SCMP_SYS(sendto),
+ SCMP_SYS(unlink)
+};
+
+/* These macros help avoid the error where the number of filters we add on a
+ * single rule don't match the arg_cnt param. */
+#define seccomp_rule_add_0(ctx,act,call) \
+ seccomp_rule_add((ctx),(act),(call),0)
+#define seccomp_rule_add_1(ctx,act,call,f1) \
+ seccomp_rule_add((ctx),(act),(call),1,(f1))
+#define seccomp_rule_add_2(ctx,act,call,f1,f2) \
+ seccomp_rule_add((ctx),(act),(call),2,(f1),(f2))
+#define seccomp_rule_add_3(ctx,act,call,f1,f2,f3) \
+ seccomp_rule_add((ctx),(act),(call),3,(f1),(f2),(f3))
+#define seccomp_rule_add_4(ctx,act,call,f1,f2,f3,f4) \
+ seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4))
+
+/**
+ * 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction),
+ SCMP_CMP(0, SCMP_CMP_EQ, param[i]));
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+#if 0
+/**
+ * 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 = elem->param;
+
+ if (param != NULL && param->prot == 1 && param->syscall
+ == SCMP_SYS(execve)) {
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve),
+ 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;
+}
+#endif
+
+/**
+ * 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;
+#ifdef __NR_time
+ return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(time),
+ SCMP_CMP(0, SCMP_CMP_EQ, 0));
+#else
+ return 0;
+#endif
+}
+
+/**
+ * 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall),
+ SCMP_CMP(0, SCMP_CMP_EQ, 18));
+ if (rc) {
+ return rc;
+ }
+#endif
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4),
+ SCMP_CMP_MASKED(3, SOCK_CLOEXEC|SOCK_NONBLOCK, 0));
+ 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;
+ (void)filter;
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2),
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ),
+ SCMP_CMP(3, SCMP_CMP_EQ, MAP_PRIVATE));
+ if (rc) {
+ return rc;
+ }
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2),
+ 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_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2),
+ 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_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2),
+ 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_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2),
+ 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_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2),
+ 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_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open),
+ 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_1(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open),
+ SCMP_CMP_MASKED(1, O_CLOEXEC|O_NONBLOCK|O_NOCTTY, O_RDONLY));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add open syscall, received libseccomp "
+ "error %d", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+sb__sysctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ (void) filter;
+ (void) ctx;
+
+ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(_sysctl));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add _sysctl syscall, "
+ "received libseccomp error %d", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Function responsible for setting up the rename syscall for
+ * the seccomp filter sandbox.
+ */
+static int
+sb_rename(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(rename)) {
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rename),
+ SCMP_CMP(0, SCMP_CMP_EQ, param->value),
+ SCMP_CMP(1, SCMP_CMP_EQ, param->value2));
+ if (rc != 0) {
+ log_err(LD_BUG,"(Sandbox) failed to add rename 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_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat),
+ 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;
+ int i;
+ (void) filter;
+
+#ifdef __i386__
+ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket));
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE),
+ SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM));
+ if (rc)
+ return rc;
+
+ for (i = 0; i < 2; ++i) {
+ const int pf = i ? PF_INET : PF_INET6;
+
+ rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
+ SCMP_CMP(0, SCMP_CMP_EQ, pf),
+ SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
+ SCMP_CMP(0, SCMP_CMP_EQ, pf),
+ SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_DGRAM),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP));
+ if (rc)
+ return rc;
+ }
+
+ rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
+ 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_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair));
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair),
+ 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_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt));
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET),
+ SCMP_CMP(2, SCMP_CMP_EQ, SO_REUSEADDR));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET),
+ SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_SOCKET),
+ SCMP_CMP(2, SCMP_CMP_EQ, SO_RCVBUF));
+ if (rc)
+ return rc;
+
+#ifdef IP_TRANSPARENT
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_IP),
+ SCMP_CMP(2, SCMP_CMP_EQ, IP_TRANSPARENT));
+ if (rc)
+ return rc;
+#endif
+
+ 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_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt));
+ if (rc)
+ return rc;
+#endif
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt),
+ 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;
+ (void) filter;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64),
+ SCMP_CMP(1, SCMP_CMP_EQ, F_GETFL));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64),
+ SCMP_CMP(1, SCMP_CMP_EQ, F_GETFD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl),
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_ADD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl),
+ SCMP_CMP(1, SCMP_CMP_EQ, EPOLL_CTL_MOD));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect),
+ SCMP_CMP(2, SCMP_CMP_EQ, PROT_READ));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask),
+ SCMP_CMP(0, SCMP_CMP_EQ, SIG_UNBLOCK));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock),
+ SCMP_CMP(1, SCMP_CMP_EQ, LOCK_EX|LOCK_NB));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex),
+ SCMP_CMP(1, SCMP_CMP_EQ,
+ FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex),
+ SCMP_CMP(1, SCMP_CMP_EQ, FUTEX_WAKE_PRIVATE));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap),
+ 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_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll),
+ 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_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64),
+ 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,
+#if 0
+ sb_execve,
+#endif
+ sb_time,
+ sb_accept4,
+#ifdef __NR_mmap2
+ sb_mmap2,
+#endif
+ sb_open,
+ sb_openat,
+ sb__sysctl,
+ sb_rename,
+#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) {
+ if (!strcmp(str, (char*)(param->value))) {
+ return (char*)param->value;
+ }
+ if (param->value2 && !strcmp(str, (char*)param->value2)) {
+ return (char*)param->value2;
+ }
+ }
+ }
+
+ if (sandbox_active)
+ log_warn(LD_BUG, "No interned sandbox parameter found for %s", str);
+ return str;
+}
+
+/** DOCDOC */
+static int
+prot_strings_helper(strmap_t *locations,
+ char **pr_mem_next_p,
+ size_t *pr_mem_left_p,
+ intptr_t *value_p)
+{
+ char *param_val;
+ size_t param_size;
+ void *location;
+
+ if (*value_p == 0)
+ return 0;
+
+ param_val = (char*) *value_p;
+ param_size = strlen(param_val) + 1;
+ location = strmap_get(locations, param_val);
+
+ if (location) {
+ // We already interned this string.
+ tor_free(param_val);
+ *value_p = (intptr_t) location;
+ return 0;
+ } else if (*pr_mem_left_p >= param_size) {
+ // copy to protected
+ location = *pr_mem_next_p;
+ memcpy(location, param_val, param_size);
+
+ // re-point el parameter to protected
+ tor_free(param_val);
+ *value_p = (intptr_t) location;
+
+ strmap_set(locations, location, location); /* good real estate advice */
+
+ // move next available protected memory
+ *pr_mem_next_p += param_size;
+ *pr_mem_left_p -= param_size;
+ return 0;
+ } else {
+ log_err(LD_BUG,"(Sandbox) insufficient protected memory!");
+ return -1;
+ }
+}
+
+/**
+ * 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;
+ strmap_t *locations = NULL;
+
+ // get total number of bytes required to mmap. (Overestimate.)
+ for (el = cfg; el != NULL; el = el->next) {
+ pr_mem_size += strlen((char*) el->param->value) + 1;
+ if (el->param->value2)
+ pr_mem_size += strlen((char*) el->param->value2) + 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;
+
+ locations = strmap_new();
+
+ // change el value pointer to protected
+ for (el = cfg; el != NULL; el = el->next) {
+ if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left,
+ &el->param->value) < 0) {
+ ret = -2;
+ goto out;
+ }
+ if (prot_strings_helper(locations, &pr_mem_next, &pr_mem_left,
+ &el->param->value2) < 0) {
+ ret = -2;
+ goto out;
+ }
+ el->param->prot = 1;
+ }
+
+ // 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_1(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap),
+ 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_1(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap),
+ 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_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect),
+ 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_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect),
+ 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:
+ strmap_free(locations, NULL);
+ 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_element2(int syscall, intptr_t value, intptr_t value2)
+{
+ smp_param_t *param = NULL;
+
+ sandbox_cfg_t *elem = tor_malloc_zero(sizeof(sandbox_cfg_t));
+ param = elem->param = tor_malloc_zero(sizeof(smp_param_t));
+
+ param->syscall = syscall;
+ param->value = value;
+ param->value2 = value2;
+ param->prot = 0;
+
+ return elem;
+}
+
+static sandbox_cfg_t*
+new_element(int syscall, intptr_t value)
+{
+ return new_element2(syscall, value, 0);
+}
+
+#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)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_stat, (intptr_t)(void*) file);
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ 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) {
+ rc = sandbox_cfg_allow_stat_filename(cfg, fn);
+ 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)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(open), (intptr_t)(void *) file);
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ return 0;
+}
+
+int
+sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element2(SCMP_SYS(rename),
+ (intptr_t)(void *) file1,
+ (intptr_t)(void *) file2);
+
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ 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) {
+ rc = sandbox_cfg_allow_open_filename(cfg, fn);
+ 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)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(openat), (intptr_t)(void *) file);
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
+ }
+
+ elem->next = *cfg;
+ *cfg = elem;
+
+ 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) {
+ rc = sandbox_cfg_allow_openat_filename(cfg, fn);
+ if (rc) {
+ log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_openat_filename_array fail");
+ goto end;
+ }
+ }
+
+ end:
+ va_end(ap);
+ return 0;
+}
+
+#if 0
+int
+sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
+{
+ sandbox_cfg_t *elem = NULL;
+
+ elem = new_element(SCMP_SYS(execve), (intptr_t)(void *) 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;
+}
+#endif
+
+/** Cache entry for getaddrinfo results; used when sandboxing is implemented
+ * so that we can consult the cache when the sandbox prevents us from doing
+ * getaddrinfo.
+ *
+ * We support only a limited range of getaddrinfo calls, where servname is null
+ * and hints contains only socktype=SOCK_STREAM, family in INET,INET6,UNSPEC.
+ */
+typedef struct cached_getaddrinfo_item_t {
+ HT_ENTRY(cached_getaddrinfo_item_t) node;
+ char *name;
+ int family;
+ /** set if no error; otherwise NULL */
+ struct addrinfo *res;
+ /** 0 for no error; otherwise an EAI_* value */
+ int err;
+} cached_getaddrinfo_item_t;
+
+static unsigned
+cached_getaddrinfo_item_hash(const cached_getaddrinfo_item_t *item)
+{
+ return (unsigned)siphash24g(item->name, strlen(item->name)) + item->family;
+}
+
+static unsigned
+cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a,
+ const cached_getaddrinfo_item_t *b)
+{
+ return (a->family == b->family) && 0 == strcmp(a->name, b->name);
+}
+
+static void
+cached_getaddrinfo_item_free(cached_getaddrinfo_item_t *item)
+{
+ if (item == NULL)
+ return;
+
+ tor_free(item->name);
+ if (item->res)
+ freeaddrinfo(item->res);
+ tor_free(item);
+}
+
+static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t)
+ getaddrinfo_cache = HT_INITIALIZER();
+
+HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node,
+ cached_getaddrinfo_item_hash,
+ cached_getaddrinfo_items_eq);
+HT_GENERATE(getaddrinfo_cache, cached_getaddrinfo_item_t, node,
+ cached_getaddrinfo_item_hash,
+ cached_getaddrinfo_items_eq,
+ 0.6, tor_malloc_, tor_realloc_, tor_free_);
+
+int
+sandbox_getaddrinfo(const char *name, const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ int err;
+ struct cached_getaddrinfo_item_t search, *item;
+
+ if (servname != NULL) {
+ log_warn(LD_BUG, "called with non-NULL servname");
+ return EAI_NONAME;
+ }
+ if (name == NULL) {
+ log_warn(LD_BUG, "called with NULL name");
+ return EAI_NONAME;
+ }
+
+ *res = NULL;
+
+ memset(&search, 0, sizeof(search));
+ search.name = (char *) name;
+ search.family = hints ? hints->ai_family : AF_UNSPEC;
+ item = HT_FIND(getaddrinfo_cache, &getaddrinfo_cache, &search);
+
+ if (! sandbox_is_active()) {
+ /* If the sandbox is not turned on yet, then getaddrinfo and store the
+ result. */
+
+ err = getaddrinfo(name, NULL, hints, res);
+ log_info(LD_NET,"(Sandbox) getaddrinfo %s.", err ? "failed" : "succeeded");
+
+ if (! item) {
+ item = tor_malloc_zero(sizeof(*item));
+ item->name = tor_strdup(name);
+ item->family = hints ? hints->ai_family : AF_UNSPEC;
+ HT_INSERT(getaddrinfo_cache, &getaddrinfo_cache, item);
+ }
+
+ if (item->res) {
+ freeaddrinfo(item->res);
+ item->res = NULL;
+ }
+ item->res = *res;
+ item->err = err;
+ return err;
+ }
+
+ /* Otherwise, the sanbox is on. If we have an item, yield its cached
+ result. */
+ if (item) {
+ *res = item->res;
+ return item->err;
+ }
+
+ /* getting here means something went wrong */
+ log_err(LD_BUG,"(Sandbox) failed to get address %s!", name);
+ return EAI_NONAME;
+}
+
+int
+sandbox_add_addrinfo(const char *name)
+{
+ struct addrinfo *res;
+ struct addrinfo hints;
+ int i;
+ static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC };
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ for (i = 0; i < 3; ++i) {
+ hints.ai_family = families[i];
+
+ res = NULL;
+ (void) sandbox_getaddrinfo(name, NULL, &hints, &res);
+ if (res)
+ sandbox_freeaddrinfo(res);
+ }
+
+ return 0;
+}
+
+void
+sandbox_free_getaddrinfo_cache(void)
+{
+ cached_getaddrinfo_item_t **next, **item;
+
+ for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache);
+ item;
+ item = next) {
+ next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item);
+ cached_getaddrinfo_item_free(*item);
+ }
+
+ HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache);
+}
+
+/**
+ * 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_0(ctx, SCMP_ACT_ALLOW, filter_nopar_gen[i]);
+ 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: %d (%s)!", rc,
+ strerror(-rc));
+ goto end;
+ }
+
+ // marking the sandbox as active
+ sandbox_active = 1;
+
+ end:
+ seccomp_release(ctx);
+ return (rc < 0 ? -rc : rc);
+}
+
+#include "linux_syscalls.inc"
+static const char *
+get_syscall_name(int syscall_num)
+{
+ int i;
+ for (i = 0; SYSCALLS_BY_NUMBER[i].syscall_name; ++i) {
+ if (SYSCALLS_BY_NUMBER[i].syscall_num == syscall_num)
+ return SYSCALLS_BY_NUMBER[i].syscall_name;
+ }
+
+ {
+ static char syscall_name_buf[64];
+ format_dec_number_sigsafe(syscall_num,
+ syscall_name_buf, sizeof(syscall_name_buf));
+ return syscall_name_buf;
+ }
+}
+
+#ifdef USE_BACKTRACE
+#define MAX_DEPTH 256
+static void *syscall_cb_buf[MAX_DEPTH];
+#endif
+
+/**
+ * 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);
+ const char *syscall_name;
+ int syscall;
+#ifdef USE_BACKTRACE
+ int depth;
+ int n_fds, i;
+ const int *fds = NULL;
+#endif
+
+ (void) nr;
+
+ if (info->si_code != SYS_SECCOMP)
+ return;
+
+ if (!ctx)
+ return;
+
+ syscall = (int) ctx->uc_mcontext.M_SYSCALL;
+
+#ifdef USE_BACKTRACE
+ depth = backtrace(syscall_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(syscall_cb_buf, depth, ctx);
+#endif
+
+ syscall_name = get_syscall_name(syscall);
+
+ tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ",
+ syscall_name,
+ ")\n",
+ NULL);
+
+#ifdef USE_BACKTRACE
+ n_fds = tor_log_get_sigsafe_err_fds(&fds);
+ for (i=0; i < n_fds; ++i)
+ backtrace_symbols_fd(syscall_cb_buf, depth, fds[i]);
+#endif
+
+#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;
+}
+
+int
+sandbox_is_active(void)
+{
+ return sandbox_active != 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(__linux__)
+ (void)cfg;
+ log_warn(LD_GENERAL,
+ "This version of Tor was built without support for sandboxing. To "
+ "build with support for sandboxing on Linux, you must have "
+ "libseccomp and its necessary header files (e.g. seccomp.h).");
+ return 0;
+
+#else
+ (void)cfg;
+ log_warn(LD_GENERAL,
+ "Currently, sandboxing is only implemented on Linux. The feature "
+ "is disabled on your platform.");
+ return 0;
+#endif
+}
+
+#ifndef USE_LIBSECCOMP
+int
+sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file)
+{
+ (void)cfg; (void)file;
+ 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)
+{
+ (void)cfg; (void)file;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+#if 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;
+}
+#endif
+
+int
+sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file)
+{
+ (void)cfg; (void)file;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...)
+{
+ (void)cfg;
+ return 0;
+}
+
+int
+sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2)
+{
+ (void)cfg; (void)file1; (void)file2;
+ return 0;
+}
+
+int
+sandbox_is_active(void)
+{
+ return 0;
+}
+#endif
+
diff --git a/src/common/sandbox.h b/src/common/sandbox.h
new file mode 100644
index 000000000..20d5d5080
--- /dev/null
+++ b/src/common/sandbox.h
@@ -0,0 +1,212 @@
+/* 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 value. */
+ intptr_t value;
+ /** parameter value, second argument. */
+ intptr_t value2;
+
+ /** 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. */
+ smp_param_t *param;
+
+ /** Next element of the configuration*/
+ struct sandbox_cfg_elem *next;
+};
+
+/** 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;
+
+#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);
+#define sandbox_freeaddrinfo(addrinfo) ((void)0)
+void sandbox_free_getaddrinfo_cache(void);
+#else
+#define sandbox_getaddrinfo(name, servname, hints, res) \
+ getaddrinfo((name),(servname), (hints),(res))
+#define sandbox_add_addrinfo(name) \
+ ((void)(name))
+#define sandbox_freeaddrinfo(addrinfo) \
+ freeaddrinfo((addrinfo))
+#define sandbox_free_getaddrinfo_cache()
+#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; we take ownership
+ * of the pointer.
+ */
+int sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file);
+
+/**DOCDOC*/
+int sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2);
+
+/** Function used to add a series of open allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... a list of stealable pointers to permitted files. The last
+ * one must be NULL.
+*/
+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; we steal the pointer to
+ * that file.
+ */
+int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file);
+
+/** Function used to add a series of openat allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... a list of stealable pointers to permitted files. The last
+ * one must be NULL.
+ */
+int sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...);
+
+#if 0
+/**
+ * Function used to add a execve allowed filename to a supplied configuration.
+ * The (char*) specifies the path to the allowed file; that pointer is stolen.
+ */
+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 ... an array of stealable pointers to permitted files. The last
+ * one must be NULL.
+ */
+int sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...);
+#endif
+
+/**
+ * Function used to add a stat/stat64 allowed filename to a configuration.
+ * The (char*) specifies the path to the allowed file; that pointer is stolen.
+ */
+int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file);
+
+/** Function used to add a series of stat64 allowed filenames to a supplied
+ * configuration.
+ * @param cfg sandbox configuration.
+ * @param ... an array of stealable pointers to permitted files. The last
+ * one must be NULL.
+ */
+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);
+
+/** Return true iff the sandbox is turned on. */
+int sandbox_is_active(void);
+
+#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..34f70f3c0 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -13,6 +13,7 @@
#ifndef TOR_TORLOG_H
#include "compat.h"
+#include "testsupport.h"
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
@@ -102,6 +103,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 +118,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,9 +152,16 @@ 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);
-#if defined(__GNUC__) || defined(RUNNING_DOXYGEN)
+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);
+
+struct smartlist_t;
+void tor_log_get_logfile_names(struct smartlist_t *out);
+
extern int log_global_min_severity_;
+#if defined(__GNUC__) || defined(RUNNING_DOXYGEN)
void log_fn_(int severity, log_domain_mask_t domain,
const char *funcname, const char *format, ...)
CHECK_PRINTF(4,5);
@@ -227,6 +232,12 @@ extern const char *log_fn_function_name_;
#endif /* !GNUC */
+#ifdef LOG_PRIVATE
+MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix, const char *format,
+ va_list ap) CHECK_PRINTF(5,0));
+#endif
+
# define TOR_TORLOG_H
#endif
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 886ee0dda..ea0f21cb2 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"
@@ -152,6 +149,7 @@ typedef enum {
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
TOR_TLS_ST_BUFFEREVENT
} tor_tls_state_t;
+#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
/** Holds a SSL object and its associated data. Members are only
* accessed from within tortls.c.
@@ -162,7 +160,7 @@ struct tor_tls_t {
SSL *ssl; /**< An OpenSSL SSL object. */
int socket; /**< The underlying file descriptor for this TLS connection. */
char *address; /**< An address to log when describing this connection. */
- ENUM_BF(tor_tls_state_t) state : 3; /**< The current SSL state,
+ tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
* depending on which operations
* have completed successfully. */
unsigned int isServer:1; /**< True iff this is a server-side connection */
@@ -714,31 +712,47 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
/** List of ciphers that servers should select from when we actually have
* our choice of what cipher to use. */
const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CHC_SHA
- TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":"
-#endif
+ /* This list is autogenerated with the gen_server_ciphers.py script;
+ * don't hand-edit it. */
#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":"
#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 ":"
+#endif
#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256
TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":"
#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":"
+#endif
#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":"
#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384
+ TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":"
#endif
-//#if TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA
-// TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA ":"
-//#endif
- /* These next two are mandatory. */
- TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256
+ TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256
+ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 ":"
+#endif
+ /* Required */
+ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
+ /* Required */
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"
#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":"
#endif
- SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA;
+ /* Required */
+ SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA;
/* Note: to set up your own private testing network with link crypto
* disabled, set your Tors' cipher list to
@@ -806,24 +820,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 +993,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 +1008,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)
@@ -1261,6 +1250,10 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
goto error;
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
+ /* Prefer the server's ordering of ciphers: the client's ordering has
+ * historically been chosen for fingerprinting resistance. */
+ SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
/* Disable TLS1.1 and TLS1.2 if they exist. We need to do this to
* workaround a bug present in all OpenSSL 1.0.1 versions (as of 1
* June 2012), wherein renegotiating while using one of these TLS
@@ -1418,6 +1411,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
@@ -1469,10 +1477,13 @@ prune_v2_cipher_list(void)
inp = outp = v2_cipher_list;
while (*inp) {
- unsigned char cipherid[2];
+ unsigned char cipherid[3];
const SSL_CIPHER *cipher;
/* Is there no better way to do this? */
set_uint16(cipherid, htons(*inp));
+ cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting
+ * with a two-byte 'cipherid', it may look for a v2
+ * cipher with the appropriate 3 bytes. */
cipher = m->get_cipher_by_char(cipherid);
if (cipher) {
tor_assert((cipher->id & 0xffff) == *inp);
@@ -1486,13 +1497,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 +1595,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 +1654,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)
@@ -2339,6 +2335,7 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem)
char mytime[33];
time_t now = time(NULL);
struct tm tm;
+ size_t n;
if (problem)
tor_log(severity, LD_GENERAL,
@@ -2364,11 +2361,17 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem)
BIO_get_mem_ptr(bio, &buf);
s2 = tor_strndup(buf->data, buf->length);
- strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm));
-
- tor_log(severity, LD_GENERAL,
- "(certificate lifetime runs from %s through %s. Your time is %s.)",
- s1,s2,mytime);
+ n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm));
+ if (n > 0) {
+ tor_log(severity, LD_GENERAL,
+ "(certificate lifetime runs from %s through %s. Your time is %s.)",
+ s1,s2,mytime);
+ } else {
+ tor_log(severity, LD_GENERAL,
+ "(certificate lifetime runs from %s through %s. "
+ "Couldn't get your time.)",
+ s1, s2);
+ }
end:
/* Not expected to get invoked */
@@ -2583,8 +2586,8 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written)
/** Return a ratio of the bytes that TLS has sent to the bytes that we've told
* it to send. Used to track whether our TLS records are getting too tiny. */
-double
-tls_get_write_overhead_ratio(void)
+MOCK_IMPL(double,
+tls_get_write_overhead_ratio,(void))
{
if (total_bytes_written_over_tls == 0)
return 1.0;
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 49c488b36..a76ba3bc7 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -13,6 +13,7 @@
#include "crypto.h"
#include "compat.h"
+#include "testsupport.h"
/* Opaque structure to hold a TLS connection. */
typedef struct tor_tls_t tor_tls_t;
@@ -95,7 +96,7 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls,
size_t *rbuf_capacity, size_t *rbuf_bytes,
size_t *wbuf_capacity, size_t *wbuf_bytes);
-double tls_get_write_overhead_ratio(void);
+MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
int tor_tls_used_v1_handshake(tor_tls_t *tls);
int tor_tls_received_v3_certificate(tor_tls_t *tls);
diff --git a/src/common/util.c b/src/common/util.c
index 5eb0f9a69..e27036a84 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
@@ -284,7 +303,7 @@ tor_memdup_(const void *mem, size_t len DMALLOC_PARAMS)
/** As tor_memdup(), but add an extra 0 byte at the end of the resulting
* memory. */
void *
-tor_memdup_nulterm(const void *mem, size_t len DMALLOC_PARAMS)
+tor_memdup_nulterm_(const void *mem, size_t len DMALLOC_PARAMS)
{
char *dup;
tor_assert(len < SIZE_T_CEILING+1);
@@ -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
* ===== */
@@ -1427,7 +1516,7 @@ void
format_iso_time_nospace_usec(char *buf, const struct timeval *tv)
{
tor_assert(tv);
- format_iso_time_nospace(buf, tv->tv_sec);
+ format_iso_time_nospace(buf, (time_t)tv->tv_sec);
tor_snprintf(buf+ISO_TIME_LEN, 8, ".%06d", (int)tv->tv_usec);
}
@@ -1741,7 +1830,8 @@ file_status(const char *fname)
int r;
f = tor_strdup(fname);
clean_name_for_stat(f);
- r = stat(f, &st);
+ log_debug(LD_FS, "stat()ing %s", f);
+ r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
if (errno == ENOENT) {
@@ -1781,7 +1871,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
char *f;
#ifndef _WIN32
int mask;
- struct passwd *pw = NULL;
+ const struct passwd *pw = NULL;
uid_t running_uid;
gid_t running_gid;
#else
@@ -1791,7 +1881,8 @@ 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);
+ log_debug(LD_FS, "stat()ing %s", f);
+ r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
if (errno != ENOENT) {
@@ -1827,7 +1918,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
if (effective_user) {
/* Look up the user and group information.
* If we have a problem, bail out. */
- pw = getpwnam(effective_user);
+ pw = tor_getpwnam(effective_user);
if (pw == NULL) {
log_warn(LD_CONFIG, "Error setting configured user: %s not found",
effective_user);
@@ -1841,13 +1932,13 @@ check_private_dir(const char *dirname, cpd_check_t check,
}
if (st.st_uid != running_uid) {
- struct passwd *pw = NULL;
+ const struct passwd *pw = NULL;
char *process_ownername = NULL;
- pw = getpwuid(running_uid);
+ pw = tor_getpwuid(running_uid);
process_ownername = pw ? tor_strdup(pw->pw_name) : tor_strdup("<unknown>");
- pw = getpwuid(st.st_uid);
+ pw = tor_getpwuid(st.st_uid);
log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by "
"%s (%d). Perhaps you are running Tor as the wrong user?",
@@ -1913,7 +2004,8 @@ write_str_to_file(const char *fname, const char *str, int bin)
#ifdef _WIN32
if (!bin && strchr(str, '\r')) {
log_warn(LD_BUG,
- "We're writing a text string that already contains a CR.");
+ "We're writing a text string that already contains a CR to %s",
+ escaped(fname));
}
#endif
return write_bytes_to_file(fname, str, strlen(str), bin);
@@ -1977,8 +2069,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) {
@@ -2050,6 +2144,7 @@ static int
finish_writing_to_file_impl(open_file_t *file_data, int abort_write)
{
int r = 0;
+
tor_assert(file_data && file_data->filename);
if (file_data->stdio_file) {
if (fclose(file_data->stdio_file)) {
@@ -2066,7 +2161,13 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write)
if (file_data->rename_on_close) {
tor_assert(file_data->tempname && file_data->filename);
if (abort_write) {
- unlink(file_data->tempname);
+ int res = unlink(file_data->tempname);
+ if (res != 0) {
+ /* We couldn't unlink and we'll leave a mess behind */
+ log_warn(LD_FS, "Failed to unlink %s: %s",
+ file_data->tempname, strerror(errno));
+ r = -1;
+ }
} else {
tor_assert(strcmp(file_data->filename, file_data->tempname));
if (replace_file(file_data->tempname, file_data->filename)) {
@@ -2132,12 +2233,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 +2267,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));
@@ -2927,7 +3036,7 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
/** Minimal sscanf replacement: parse <b>buf</b> according to <b>pattern</b>
* and store the results in the corresponding argument fields. Differs from
* sscanf in that:
- * <ul><li>It only handles %u, %lu, %x, %lx, %<NUM>s, %d, %ld, %lf, and %c.
+ * <ul><li>It only handles %u, %lu, %x, %lx, %[NUM]s, %d, %ld, %lf, and %c.
* <li>It only handles decimal inputs for %lf. (12.3, not 1.23e1)
* <li>It does not handle arbitrarily long widths.
* <li>Numbers do not consume any space characters.
@@ -3022,9 +3131,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 +3430,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 = (unsigned) (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 +3497,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 +3525,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 +3556,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 +3586,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 +3617,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 +3813,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 +4034,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;
@@ -4412,14 +4540,38 @@ stream_status_to_string(enum stream_status stream_status)
}
}
+/* DOCDOC */
+static void
+log_portfw_spawn_error_message(const char *buf,
+ const char *executable, int *child_status)
+{
+ /* Parse error message */
+ int retval, child_state, saved_errno;
+ retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
+ &child_state, &saved_errno);
+ if (retval == 2) {
+ log_warn(LD_GENERAL,
+ "Failed to start child process \"%s\" in state %d: %s",
+ executable, child_state, strerror(saved_errno));
+ if (child_status)
+ *child_status = 1;
+ } else {
+ /* Failed to parse message from child process, log it as a
+ warning */
+ log_warn(LD_GENERAL,
+ "Unexpected message from port forwarding helper \"%s\": %s",
+ executable, buf);
+ }
+}
+
#ifdef _WIN32
/** 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 +4659,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];
@@ -4558,23 +4711,7 @@ log_from_pipe(FILE *stream, int severity, const char *executable,
/* Check if buf starts with SPAWN_ERROR_MESSAGE */
if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) {
- /* Parse error message */
- int retval, child_state, saved_errno;
- retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
- &child_state, &saved_errno);
- if (retval == 2) {
- log_warn(LD_GENERAL,
- "Failed to start child process \"%s\" in state %d: %s",
- executable, child_state, strerror(saved_errno));
- if (child_status)
- *child_status = 1;
- } else {
- /* Failed to parse message from child process, log it as a
- warning */
- log_warn(LD_GENERAL,
- "Unexpected message from port forwarding helper \"%s\": %s",
- executable, buf);
- }
+ log_portfw_spawn_error_message(buf, executable, child_status);
} else {
log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf);
}
@@ -4652,7 +4789,7 @@ get_string_from_pipe(FILE *stream, char *buf_out, size_t count)
/** Parse a <b>line</b> from tor-fw-helper and issue an appropriate
* log message to our user. */
static void
-handle_fw_helper_line(const char *line)
+handle_fw_helper_line(const char *executable, const char *line)
{
smartlist_t *tokens = smartlist_new();
char *message = NULL;
@@ -4663,6 +4800,19 @@ handle_fw_helper_line(const char *line)
int port = 0;
int success = 0;
+ if (strcmpstart(line, SPAWN_ERROR_MESSAGE) == 0) {
+ /* We need to check for SPAWN_ERROR_MESSAGE again here, since it's
+ * possible that it got sent after we tried to read it in log_from_pipe.
+ *
+ * XXX Ideally, we should be using one of stdout/stderr for the real
+ * output, and one for the output of the startup code. We used to do that
+ * before cd05f35d2c.
+ */
+ int child_status;
+ log_portfw_spawn_error_message(line, executable, &child_status);
+ goto done;
+ }
+
smartlist_split_string(tokens, line, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
@@ -4742,7 +4892,8 @@ handle_fw_helper_line(const char *line)
/** Read what tor-fw-helper has to say in its stdout and handle it
* appropriately */
static int
-handle_fw_helper_output(process_handle_t *process_handle)
+handle_fw_helper_output(const char *executable,
+ process_handle_t *process_handle)
{
smartlist_t *fw_helper_output = NULL;
enum stream_status stream_status = 0;
@@ -4757,7 +4908,7 @@ handle_fw_helper_output(process_handle_t *process_handle)
/* Handle the lines we got: */
SMARTLIST_FOREACH_BEGIN(fw_helper_output, char *, line) {
- handle_fw_helper_line(line);
+ handle_fw_helper_line(executable, line);
tor_free(line);
} SMARTLIST_FOREACH_END(line);
@@ -4872,7 +5023,7 @@ tor_check_port_forwarding(const char *filename,
stderr_status = log_from_pipe(child_handle->stderr_handle,
LOG_INFO, filename, &retval);
#endif
- if (handle_fw_helper_output(child_handle) < 0) {
+ if (handle_fw_helper_output(filename, child_handle) < 0) {
log_warn(LD_GENERAL, "Failed to handle fw helper output.");
stdout_status = -1;
retval = -1;
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