aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/Makefile.nmake9
-rw-r--r--src/common/address.c111
-rw-r--r--src/common/address.h22
-rw-r--r--src/common/aes.c2
-rw-r--r--src/common/aes.h2
-rw-r--r--src/common/backtrace.c42
-rw-r--r--src/common/backtrace.h11
-rw-r--r--src/common/ciphers.inc169
-rw-r--r--src/common/compat.c474
-rw-r--r--src/common/compat.h76
-rw-r--r--src/common/compat_libevent.c67
-rw-r--r--src/common/compat_libevent.h8
-rw-r--r--src/common/container.c734
-rw-r--r--src/common/container.h100
-rw-r--r--src/common/crypto.c175
-rw-r--r--src/common/crypto.h12
-rw-r--r--src/common/crypto_curve25519.c178
-rw-r--r--src/common/crypto_curve25519.h18
-rw-r--r--src/common/crypto_ed25519.c353
-rw-r--r--src/common/crypto_ed25519.h113
-rw-r--r--src/common/crypto_format.c24
-rw-r--r--src/common/crypto_pwbox.c187
-rw-r--r--src/common/crypto_pwbox.h20
-rw-r--r--src/common/crypto_s2k.c460
-rw-r--r--src/common/crypto_s2k.h73
-rw-r--r--src/common/di_ops.c4
-rw-r--r--src/common/di_ops.h2
-rw-r--r--src/common/get_mozilla_ciphers.py192
-rw-r--r--src/common/include.am41
-rw-r--r--src/common/linux_syscalls.inc1153
-rw-r--r--src/common/log.c330
-rw-r--r--src/common/memarea.c52
-rw-r--r--src/common/memarea.h2
-rw-r--r--src/common/mempool.c2
-rw-r--r--src/common/mempool.h2
-rw-r--r--src/common/procmon.c3
-rw-r--r--src/common/procmon.h2
-rw-r--r--src/common/sandbox.c807
-rw-r--r--src/common/sandbox.h123
-rw-r--r--src/common/testsupport.h2
-rw-r--r--src/common/torgzip.c72
-rw-r--r--src/common/torgzip.h5
-rw-r--r--src/common/torint.h22
-rw-r--r--src/common/torlog.h21
-rw-r--r--src/common/tortls.c145
-rw-r--r--src/common/tortls.h5
-rw-r--r--src/common/util.c459
-rw-r--r--src/common/util.h21
-rw-r--r--src/common/util_process.c158
-rw-r--r--src/common/util_process.h26
50 files changed, 5334 insertions, 1757 deletions
diff --git a/src/common/Makefile.nmake b/src/common/Makefile.nmake
index 0ebeaaaf71..b8c5dd4fea 100644
--- a/src/common/Makefile.nmake
+++ b/src/common/Makefile.nmake
@@ -1,12 +1,13 @@
all: libor.lib libor-crypto.lib libor-event.lib
-CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\ext
+CFLAGS = /O2 /MT /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common \
+ /I ..\ext
-LIBOR_OBJECTS = address.obj compat.obj container.obj di_ops.obj \
- log.obj memarea.obj mempool.obj procmon.obj util.obj \
+LIBOR_OBJECTS = address.obj backtrace.obj compat.obj container.obj di_ops.obj \
+ log.obj memarea.obj mempool.obj procmon.obj sandbox.obj util.obj \
util_codedigest.obj
-LIBOR_CRYPTO_OBJECTS = aes.obj crypto.obj torgzip.obj tortls.obj \
+LIBOR_CRYPTO_OBJECTS = aes.obj crypto.obj crypto_format.obj torgzip.obj tortls.obj \
crypto_curve25519.obj curve25519-donna.obj
LIBOR_EVENT_OBJECTS = compat_libevent.obj
diff --git a/src/common/address.c b/src/common/address.c
index be41cc73ac..a3b5df66bc 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -182,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
@@ -236,7 +236,9 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr)
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
err = sandbox_getaddrinfo(name, NULL, &hints, &res);
- if (!err) {
+ /* 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) {
@@ -262,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;
@@ -321,15 +323,23 @@ tor_addr_is_internal_(const tor_addr_t *addr, int for_listening,
{
uint32_t iph4 = 0;
uint32_t iph6[4];
- sa_family_t v_family;
- v_family = tor_addr_family(addr);
+
+ tor_assert(addr);
+ sa_family_t v_family = tor_addr_family(addr);
if (v_family == AF_INET) {
iph4 = tor_addr_to_ipv4h(addr);
} else if (v_family == AF_INET6) {
if (tor_addr_is_v4(addr)) { /* v4-mapped */
+ uint32_t *addr32 = NULL;
v_family = AF_INET;
- iph4 = ntohl(tor_addr_to_in6_addr32(addr)[3]);
+ // Work around an incorrect NULL pointer dereference warning in
+ // "clang --analyze" due to limited analysis depth
+ addr32 = tor_addr_to_in6_addr32(addr);
+ // To improve performance, wrap this assertion in:
+ // #if !defined(__clang_analyzer__) || PARANOIA
+ tor_assert(addr32);
+ iph4 = ntohl(addr32[3]);
}
}
@@ -461,7 +471,6 @@ tor_addr_parse_PTR_name(tor_addr_t *result, const char *address,
if (!strcasecmpend(address, ".ip6.arpa")) {
const char *cp;
- int i;
int n0, n1;
struct in6_addr in6;
@@ -469,7 +478,7 @@ tor_addr_parse_PTR_name(tor_addr_t *result, const char *address,
return -1;
cp = address;
- for (i = 0; i < 16; ++i) {
+ for (int i = 0; i < 16; ++i) {
n0 = hex_decode_digit(*cp++); /* The low-order nybble appears first. */
if (*cp++ != '.') return -1; /* Then a dot. */
n1 = hex_decode_digit(*cp++); /* The high-order nybble appears first. */
@@ -594,7 +603,7 @@ tor_addr_parse_mask_ports(const char *s,
int any_flag=0, v4map=0;
sa_family_t family;
struct in6_addr in6_tmp;
- struct in_addr in_tmp;
+ struct in_addr in_tmp = { .s_addr = 0 };
tor_assert(s);
tor_assert(addr_out);
@@ -655,7 +664,7 @@ tor_addr_parse_mask_ports(const char *s,
tor_addr_from_ipv4h(addr_out, 0);
any_flag = 1;
} else if (!strcmp(address, "*6") && (flags & TAPMP_EXTENDED_STAR)) {
- static char nil_bytes[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+ static char nil_bytes[16] = { [0]=0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
family = AF_INET6;
tor_addr_from_ipv6_bytes(addr_out, nil_bytes);
any_flag = 1;
@@ -879,6 +888,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 care 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.
@@ -1000,19 +1035,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;
@@ -1426,31 +1459,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;
@@ -1464,8 +1488,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)
@@ -1486,9 +1514,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);
}
@@ -1566,7 +1603,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 77e5855346..e8bab223a7 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -103,7 +103,18 @@ tor_addr_to_ipv4h(const tor_addr_t *a)
static INLINE uint32_t
tor_addr_to_mapped_ipv4h(const tor_addr_t *a)
{
- return a->family == AF_INET6 ? ntohl(tor_addr_to_in6_addr32(a)[3]) : 0;
+ if (a->family == AF_INET6) {
+ uint32_t *addr32 = NULL;
+ // Work around an incorrect NULL pointer dereference warning in
+ // "clang --analyze" due to limited analysis depth
+ addr32 = tor_addr_to_in6_addr32(a);
+ // To improve performance, wrap this assertion in:
+ // #if !defined(__clang_analyzer__) || PARANOIA
+ tor_assert(addr32);
+ return ntohl(addr32[3]);
+ } else {
+ return 0;
+ }
}
/** Return the address family of <b>a</b>. Possible values are:
* AF_INET6, AF_INET, AF_UNSPEC. */
@@ -167,7 +178,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 +203,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 +221,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/aes.c b/src/common/aes.c
index f454a7f7b2..877dce625c 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/common/aes.h b/src/common/aes.h
index 8ff28a7622..f014e3a424 100644
--- a/src/common/aes.h
+++ b/src/common/aes.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* Implements a minimal interface to counter-mode AES. */
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
index 5049298a12..e6fb8938ac 100644
--- a/src/common/backtrace.c
+++ b/src/common/backtrace.c
@@ -1,11 +1,10 @@
-/* Copyright (c) 2013, The Tor Project, Inc. */
+/* Copyright (c) 2013-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define __USE_GNU
#define _GNU_SOURCE 1
#include "orconfig.h"
-#include "backtrace.h"
#include "compat.h"
#include "util.h"
#include "torlog.h"
@@ -31,6 +30,9 @@
#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
@@ -49,6 +51,8 @@ static char *bt_version = NULL;
/** 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
@@ -57,7 +61,7 @@ static void *cb_buf[MAX_DEPTH];
* onto the stack. Fortunately, we usually have the program counter in the
* ucontext_t structure.
*/
-static void
+void
clean_backtrace(void **stack, int depth, const ucontext_t *ctx)
{
#ifdef PC_FROM_UCONTEXT
@@ -84,18 +88,27 @@ clean_backtrace(void **stack, int depth, const ucontext_t *ctx)
void
log_backtrace(int severity, int domain, const char *msg)
{
- int depth = backtrace(cb_buf, MAX_DEPTH);
- char **symbols = backtrace_symbols(cb_buf, depth);
+ int 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.");
- return;
+ 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_)
@@ -140,6 +153,9 @@ install_bt_handler(void)
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;
@@ -151,6 +167,18 @@ install_bt_handler(void)
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;
}
@@ -158,7 +186,7 @@ install_bt_handler(void)
static void
remove_bt_handler(void)
{
- /* We don't need to actually free anything at exit here. */
+ tor_mutex_uninit(&cb_buf_mutex);
}
#endif
diff --git a/src/common/backtrace.h b/src/common/backtrace.h
index 765436fee3..4938745b3d 100644
--- a/src/common/backtrace.h
+++ b/src/common/backtrace.h
@@ -1,12 +1,21 @@
-/* Copyright (c) 2013, The Tor Project, Inc. */
+/* Copyright (c) 2013-2014, 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 137d78b117..ab4ac40724 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 8e2619f846..8574bd04c9 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -35,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
@@ -68,6 +77,7 @@
/* Includes for the process attaching prevention */
#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
+/* Only use the linux prctl; the IRIX prctl is totally different */
#include <sys/prctl.h>
#elif defined(__APPLE__)
#include <sys/types.h>
@@ -101,9 +111,11 @@
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
-#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
-/* Only use the linux prctl; the IRIX prctl is totally different */
-#include <sys/prctl.h>
+#ifdef TOR_UNIT_TESTS
+#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H)
+/* as fallback implementation for tor_sleep_msec */
+#include <sys/select.h>
+#endif
#endif
#include "torlog.h"
@@ -126,9 +138,10 @@ int
tor_open_cloexec(const char *path, int flags, unsigned mode)
{
int fd;
+ const char *p = path;
#ifdef O_CLOEXEC
- path = sandbox_intern_string(path);
- fd = open(path, flags|O_CLOEXEC, mode);
+ p = sandbox_intern_string(path);
+ fd = open(p, flags|O_CLOEXEC, mode);
if (fd >= 0)
return fd;
/* If we got an error, see if it is EINVAL. EINVAL might indicate that,
@@ -138,7 +151,8 @@ tor_open_cloexec(const char *path, int flags, unsigned mode)
return -1;
#endif
- fd = open(path, flags, mode);
+ log_debug(LD_FS, "Opening %s with flags %x", p, flags);
+ fd = open(p, flags, mode);
#ifdef FD_CLOEXEC
if (fd >= 0) {
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
@@ -169,6 +183,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
@@ -178,9 +201,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);
@@ -194,9 +218,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;
@@ -227,12 +264,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 *
@@ -314,17 +366,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 *
@@ -340,13 +404,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
@@ -501,21 +577,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
@@ -732,7 +816,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))
{
@@ -747,7 +831,7 @@ replace_file(const char *from, const char *to)
errno = EISDIR;
return -1;
}
- return rename(from,to);
+ return tor_rename(from,to);
#endif
}
@@ -895,14 +979,23 @@ tor_fd_getpos(int fd)
#endif
}
-/** Move <b>fd</b> to the end of the file. Return -1 on error, 0 on success. */
+/** Move <b>fd</b> to the end of the file. Return -1 on error, 0 on success.
+ * If the file is a pipe, do nothing and succeed.
+ **/
int
tor_fd_seekend(int fd)
{
#ifdef _WIN32
return _lseek(fd, 0, SEEK_END) < 0 ? -1 : 0;
#else
- return lseek(fd, 0, SEEK_END) < 0 ? -1 : 0;
+ off_t rc = lseek(fd, 0, SEEK_END) < 0 ? -1 : 0;
+#ifdef ESPIPE
+ /* If we get an error and ESPIPE, then it's a pipe or a socket of a fifo:
+ * no need to worry. */
+ if (rc < 0 && errno == ESPIPE)
+ rc = 0;
+#endif
+ return (rc < 0) ? -1 : 0;
#endif
}
@@ -918,6 +1011,23 @@ tor_fd_setpos(int fd, off_t pos)
#endif
}
+/** Replacement for ftruncate(fd, 0): move to the front of the file and remove
+ * all the rest of the file. Return -1 on error, 0 on success. */
+int
+tor_ftruncate(int fd)
+{
+ /* Rumor has it that some versions of ftruncate do not move the file pointer.
+ */
+ if (tor_fd_setpos(fd, 0) < 0)
+ return -1;
+
+#ifdef _WIN32
+ return _chsize(fd, 0);
+#else
+ return ftruncate(fd, 0);
+#endif
+}
+
#undef DEBUG_SOCKET_COUNTING
#ifdef DEBUG_SOCKET_COUNTING
/** A bitarray of all fds that should be passed to tor_socket_close(). Only
@@ -1323,6 +1433,9 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
socklen_t size;
int saved_errno = -1;
+ memset(&connect_addr, 0, sizeof(connect_addr));
+ memset(&listen_addr, 0, sizeof(listen_addr));
+
if (protocol
#ifdef AF_UNIX
|| family != AF_UNIX
@@ -1584,12 +1697,12 @@ log_credential_status(void)
/* log supplementary groups */
sup_gids_size = 64;
- sup_gids = tor_malloc(sizeof(gid_t) * 64);
+ sup_gids = tor_calloc(64, sizeof(gid_t));
while ((ngids = getgroups(sup_gids_size, sup_gids)) < 0 &&
errno == EINVAL &&
sup_gids_size < NGROUPS_MAX) {
sup_gids_size *= 2;
- sup_gids = tor_realloc(sup_gids, sizeof(gid_t) * sup_gids_size);
+ sup_gids = tor_reallocarray(sup_gids, sizeof(gid_t), sup_gids_size);
}
if (ngids < 0) {
@@ -1622,6 +1735,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.
*/
@@ -1629,7 +1842,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;
@@ -1650,7 +1863,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;
@@ -1821,10 +2034,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;
}
@@ -2118,8 +2331,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
@@ -2297,14 +2512,12 @@ get_uname(void)
"Unrecognized version of Windows [major=%d,minor=%d]",
(int)info.dwMajorVersion,(int)info.dwMinorVersion);
}
-#if !defined (WINCE)
#ifdef VER_NT_SERVER
if (info.wProductType == VER_NT_SERVER ||
info.wProductType == VER_NT_DOMAIN_CONTROLLER) {
strlcat(uname_result, " [server]", sizeof(uname_result));
}
#endif
-#endif
#else
strlcpy(uname_result, "Unknown platform", sizeof(uname_result));
#endif
@@ -2347,6 +2560,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
@@ -2370,12 +2589,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
@@ -2503,15 +2722,8 @@ tor_gettimeofday(struct timeval *timeval)
uint64_t ft_64;
FILETIME ft_ft;
} ft;
-#if defined (WINCE)
- /* wince do not have GetSystemTimeAsFileTime */
- SYSTEMTIME stime;
- GetSystemTime(&stime);
- SystemTimeToFileTime(&stime,&ft.ft_ft);
-#else
/* number of 100-nsec units since Jan 1, 1601 */
GetSystemTimeAsFileTime(&ft.ft_ft);
-#endif
if (ft.ft_64 < EPOCH_BIAS) {
log_err(LD_GENERAL,"System time is before 1970; failing.");
exit(1);
@@ -2537,7 +2749,7 @@ tor_gettimeofday(struct timeval *timeval)
return;
}
-#if defined(TOR_IS_MULTITHREADED) && !defined(_WIN32)
+#if !defined(_WIN32)
/** Defined iff we need to add locks when defining fake versions of reentrant
* versions of time-related functions. */
#define TIME_FNS_NEED_LOCKS
@@ -2556,14 +2768,24 @@ correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
const char *outcome;
if (PREDICT_LIKELY(r)) {
- if (r->tm_year > 8099) { /* We can't strftime dates after 9999 CE. */
+ /* We can't strftime dates after 9999 CE, and we want to avoid dates
+ * before 1 CE (avoiding the year 0 issue and negative years). */
+ if (r->tm_year > 8099) {
r->tm_year = 8099;
r->tm_mon = 11;
r->tm_mday = 31;
- r->tm_yday = 365;
+ r->tm_yday = 364;
r->tm_hour = 23;
r->tm_min = 59;
r->tm_sec = 59;
+ } else if (r->tm_year < (1-1900)) {
+ r->tm_year = (1-1900);
+ r->tm_mon = 0;
+ r->tm_mday = 1;
+ r->tm_yday = 0;
+ r->tm_hour = 0;
+ r->tm_min = 0;
+ r->tm_sec = 0;
}
return r;
}
@@ -2577,7 +2799,7 @@ correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
r->tm_year = 70; /* 1970 CE */
r->tm_mon = 0;
r->tm_mday = 1;
- r->tm_yday = 1;
+ r->tm_yday = 0;
r->tm_hour = 0;
r->tm_min = 0 ;
r->tm_sec = 0;
@@ -2590,7 +2812,7 @@ correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
r->tm_year = 137; /* 2037 CE */
r->tm_mon = 11;
r->tm_mday = 31;
- r->tm_yday = 365;
+ r->tm_yday = 364;
r->tm_hour = 23;
r->tm_min = 59;
r->tm_sec = 59;
@@ -2659,7 +2881,7 @@ tor_localtime_r(const time_t *timep, struct tm *result)
/** @} */
/** @{ */
-/** As gmtimee_r, but defined for platforms that don't have it:
+/** As gmtime_r, but defined for platforms that don't have it:
*
* Convert *<b>timep</b> to a struct tm in UTC, and store the value in
* *<b>result</b>. Return the result on success, or NULL on failure.
@@ -2732,8 +2954,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
@@ -2799,7 +3019,6 @@ tor_get_thread_id(void)
}
#endif
-#ifdef TOR_IS_MULTITHREADED
/** Return a newly allocated, ready-for-use mutex. */
tor_mutex_t *
tor_mutex_new(void)
@@ -2817,7 +3036,6 @@ tor_mutex_free(tor_mutex_t *m)
tor_mutex_uninit(m);
tor_free(m);
}
-#endif
/* Conditions. */
#ifdef USE_PTHREADS
@@ -2877,6 +3095,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();
}
@@ -3252,3 +3472,139 @@ 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_MAX != UINT64_MAX
+ if (m > SIZE_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_MAX;
+ }
+#endif
+
+ *mem_out = mem_cached = (size_t) m;
+
+ return 0;
+}
+
+#ifdef TOR_UNIT_TESTS
+/** Delay for <b>msec</b> milliseconds. Only used in tests. */
+void
+tor_sleep_msec(int msec)
+{
+#ifdef _WIN32
+ Sleep(msec);
+#elif defined(HAVE_USLEEP)
+ sleep(msec / 1000);
+ /* Some usleep()s hate sleeping more than 1 sec */
+ usleep((msec % 1000) * 1000);
+#elif defined(HAVE_SYS_SELECT_H)
+ struct timeval tv = { msec / 1000, (msec % 1000) * 1000};
+ select(0, NULL, NULL, NULL, &tv);
+#else
+ sleep(CEIL_DIV(msec, 1000));
+#endif
+}
+#endif
+
diff --git a/src/common/compat.h b/src/common/compat.h
index 8e700a9a13..7001361af3 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_COMPAT_H
@@ -56,21 +56,6 @@
#include <stdio.h>
#include <errno.h>
-#if defined (WINCE)
-#include <fcntl.h>
-#include <io.h>
-#include <math.h>
-#include <projects.h>
-/* this is not exported as W .... */
-#define SHGetPathFromIDListW SHGetPathFromIDList
-/* wcecompat has vasprintf */
-#define HAVE_VASPRINTF
-/* no service here */
-#ifdef NT_SERVICE
-#undef NT_SERVICE
-#endif
-#endif // WINCE
-
#ifndef NULL_REP_IS_ZERO_BYTES
#error "It seems your platform does not represent NULL as zero. We can't cope."
#endif
@@ -169,6 +154,7 @@ extern INLINE double U64_TO_DBL(uint64_t x) {
*
* #define ATTR_NONNULL(x) __attribute__((nonnull x)) */
#define ATTR_NONNULL(x)
+#define ATTR_UNUSED __attribute__ ((unused))
/** Macro: Evaluates to <b>exp</b> and hints the compiler that the value
* of <b>exp</b> will probably be true.
@@ -192,6 +178,7 @@ extern INLINE double U64_TO_DBL(uint64_t x) {
#define ATTR_MALLOC
#define ATTR_NORETURN
#define ATTR_NONNULL(x)
+#define ATTR_UNUSED
#define PREDICT_LIKELY(exp) (exp)
#define PREDICT_UNLIKELY(exp) (exp)
#endif
@@ -216,6 +203,15 @@ extern INLINE double U64_TO_DBL(uint64_t x) {
#define STMT_END } while (0)
#endif
+/* Some tools (like coccinelle) don't like to see operators as macro
+ * arguments. */
+#define OP_LT <
+#define OP_GT >
+#define OP_GE >=
+#define OP_LE <=
+#define OP_EQ ==
+#define OP_NE !=
+
/* ===== String compatibility */
#ifdef _WIN32
/* Windows names string functions differently from most other platforms. */
@@ -290,7 +286,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));
@@ -319,7 +315,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)
@@ -408,6 +404,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);
@@ -420,6 +417,7 @@ void tor_lockfile_unlock(tor_lockfile_t *lockfile);
off_t tor_fd_getpos(int fd);
int tor_fd_setpos(int fd, off_t pos);
int tor_fd_seekend(int fd);
+int tor_ftruncate(int fd);
#ifdef _WIN32
#define PATH_SEPARATOR "\\"
@@ -573,17 +571,18 @@ const char *tor_socket_strerror(int e);
#else
#define SOCK_ERRNO(e) e
#if EAGAIN == EWOULDBLOCK
-#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN)
+/* || 0 is for -Wparentheses-equality (-Wall?) appeasement under clang */
+#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || 0)
#else
#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == EWOULDBLOCK)
#endif
-#define ERRNO_IS_EINPROGRESS(e) ((e) == EINPROGRESS)
-#define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS)
+#define ERRNO_IS_EINPROGRESS(e) ((e) == EINPROGRESS || 0)
+#define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS || 0)
#define ERRNO_IS_ACCEPT_EAGAIN(e) \
(ERRNO_IS_EAGAIN(e) || (e) == ECONNABORTED)
#define ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e) \
((e) == EMFILE || (e) == ENFILE || (e) == ENOBUFS || (e) == ENOMEM)
-#define ERRNO_IS_EADDRINUSE(e) ((e) == EADDRINUSE)
+#define ERRNO_IS_EADDRINUSE(e) (((e) == EADDRINUSE) || 0)
#define tor_socket_errno(sock) (errno)
#define tor_socket_strerror(e) strerror(e)
#endif
@@ -630,23 +629,27 @@ 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;
-#if defined(ENABLE_THREADS) && defined(_WIN32)
+#if defined(_WIN32)
#define USE_WIN32_THREADS
-#define TOR_IS_MULTITHREADED 1
-#elif (defined(ENABLE_THREADS) && defined(HAVE_PTHREAD_H) && \
- defined(HAVE_PTHREAD_CREATE))
+#elif defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_CREATE)
#define USE_PTHREADS
-#define TOR_IS_MULTITHREADED 1
#else
-#undef TOR_IS_MULTITHREADED
+#error "No threading system was found"
#endif
int compute_num_cpus(void);
@@ -672,7 +675,6 @@ typedef struct tor_mutex_t {
int tor_mlockall(void);
-#ifdef TOR_IS_MULTITHREADED
tor_mutex_t *tor_mutex_new(void);
void tor_mutex_init(tor_mutex_t *m);
void tor_mutex_acquire(tor_mutex_t *m);
@@ -681,21 +683,10 @@ void tor_mutex_free(tor_mutex_t *m);
void tor_mutex_uninit(tor_mutex_t *m);
unsigned long tor_get_thread_id(void);
void tor_threads_init(void);
-#else
-#define tor_mutex_new() ((tor_mutex_t*)tor_malloc(sizeof(int)))
-#define tor_mutex_init(m) STMT_NIL
-#define tor_mutex_acquire(m) STMT_VOID(m)
-#define tor_mutex_release(m) STMT_NIL
-#define tor_mutex_free(m) STMT_BEGIN tor_free(m); STMT_END
-#define tor_mutex_uninit(m) STMT_NIL
-#define tor_get_thread_id() (1UL)
-#define tor_threads_init() STMT_NIL
-#endif
void set_main_thread(void);
int in_main_thread(void);
-#ifdef TOR_IS_MULTITHREADED
#if 0
typedef struct tor_cond_t tor_cond_t;
tor_cond_t *tor_cond_new(void);
@@ -704,7 +695,6 @@ int tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex);
void tor_cond_signal_one(tor_cond_t *cond);
void tor_cond_signal_all(tor_cond_t *cond);
#endif
-#endif
/** Macros for MIN/MAX. Never use these when the arguments could have
* side-effects.
@@ -739,6 +729,10 @@ char *format_win32_error(DWORD err);
#endif
+#ifdef TOR_UNIT_TESTS
+void tor_sleep_msec(int msec);
+#endif
+
#ifdef COMPAT_PRIVATE
#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
#define NEED_ERSATZ_SOCKETPAIR
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 4d0fff833b..4cfe5cc93b 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2013, The Tor Project, Inc. */
+/* Copyright (c) 2009-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -13,6 +13,8 @@
#include "compat.h"
#include "compat_libevent.h"
+#include "crypto.h"
+
#include "util.h"
#include "torlog.h"
@@ -208,6 +210,9 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
} else {
using_iocp_bufferevents = 0;
}
+#elif defined(__COVERITY__)
+ /* Avoid a 'dead code' warning below. */
+ using_threads = ! torcfg->disable_iocp;
#endif
if (!using_threads) {
@@ -626,7 +631,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)
{
@@ -659,5 +682,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, OP_LT)) {
+ 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 fda8733592..c5c78b822d 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2013, The Tor Project, Inc. */
+/* Copyright (c) 2009-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_COMPAT_LIBEVENT_H
@@ -89,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 476dc82913..ab4e22de52 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -28,21 +28,21 @@
/** Allocate and return an empty smartlist.
*/
-smartlist_t *
-smartlist_new(void)
+MOCK_IMPL(smartlist_t *,
+smartlist_new,(void))
{
smartlist_t *sl = tor_malloc(sizeof(smartlist_t));
sl->num_used = 0;
sl->capacity = SMARTLIST_DEFAULT_CAPACITY;
- sl->list = tor_malloc(sizeof(void *) * sl->capacity);
+ sl->list = tor_calloc(sizeof(void *), sl->capacity);
return sl;
}
/** Deallocate a smartlist. Does not release storage associated with the
* list's elements.
*/
-void
-smartlist_free(smartlist_t *sl)
+MOCK_IMPL(void,
+smartlist_free,(smartlist_t *sl))
{
if (!sl)
return;
@@ -66,19 +66,28 @@ smartlist_ensure_capacity(smartlist_t *sl, int size)
#define MAX_CAPACITY (INT_MAX)
#else
#define MAX_CAPACITY (int)((SIZE_MAX / (sizeof(void*))))
+#define ASSERT_CAPACITY
#endif
if (size > sl->capacity) {
int higher = sl->capacity;
if (PREDICT_UNLIKELY(size > MAX_CAPACITY/2)) {
+#ifdef ASSERT_CAPACITY
+ /* We don't include this assertion when MAX_CAPACITY == INT_MAX,
+ * since int size; (size <= INT_MAX) makes analysis tools think we're
+ * doing something stupid. */
tor_assert(size <= MAX_CAPACITY);
+#endif
higher = MAX_CAPACITY;
} else {
while (size > higher)
higher *= 2;
}
sl->capacity = higher;
- sl->list = tor_realloc(sl->list, sizeof(void*)*((size_t)sl->capacity));
+ sl->list = tor_reallocarray(sl->list, sizeof(void *),
+ ((size_t)sl->capacity));
}
+#undef ASSERT_CAPACITY
+#undef MAX_CAPACITY
}
/** Append element to the end of the list. */
@@ -727,6 +736,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]].
@@ -992,6 +1021,7 @@ smartlist_uniq_digests256(smartlist_t *sl)
DEFINE_MAP_STRUCTS(strmap_t, char *key, strmap_);
DEFINE_MAP_STRUCTS(digestmap_t, char key[DIGEST_LEN], digestmap_);
+DEFINE_MAP_STRUCTS(digest256map_t, uint8_t key[DIGEST256_LEN], digest256map_);
/** Helper: compare strmap_entry_t objects by key value. */
static INLINE int
@@ -1004,7 +1034,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. */
@@ -1018,213 +1048,306 @@ 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);
+}
+
+/** Helper: compare digestmap_entry_t objects by key value. */
+static INLINE int
+digest256map_entries_eq(const digest256map_entry_t *a,
+ const digest256map_entry_t *b)
+{
+ return tor_memeq(a->key, b->key, DIGEST256_LEN);
+}
+
+/** Helper: return a hash value for a digest_map_t. */
+static INLINE unsigned int
+digest256map_entry_hash(const digest256map_entry_t *a)
+{
+ return (unsigned) siphash24g(a->key, DIGEST256_LEN);
}
HT_PROTOTYPE(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
strmap_entries_eq)
-HT_GENERATE(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
- strmap_entries_eq, 0.6, malloc, realloc, free)
+HT_GENERATE2(strmap_impl, strmap_entry_t, node, strmap_entry_hash,
+ strmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
HT_PROTOTYPE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash,
digestmap_entries_eq)
-HT_GENERATE(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash,
- digestmap_entries_eq, 0.6, malloc, realloc, free)
+HT_GENERATE2(digestmap_impl, digestmap_entry_t, node, digestmap_entry_hash,
+ digestmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
-/** Constructor to create a new empty map from strings to void*'s.
- */
-strmap_t *
-strmap_new(void)
+HT_PROTOTYPE(digest256map_impl, digest256map_entry_t, node,
+ digest256map_entry_hash,
+ digest256map_entries_eq)
+HT_GENERATE2(digest256map_impl, digest256map_entry_t, node,
+ digest256map_entry_hash,
+ digest256map_entries_eq, 0.6, tor_reallocarray_, tor_free_)
+
+static INLINE void
+strmap_entry_free(strmap_entry_t *ent)
{
- strmap_t *result;
- result = tor_malloc(sizeof(strmap_t));
- HT_INIT(strmap_impl, &result->head);
- return result;
+ tor_free(ent->key);
+ tor_free(ent);
}
-
-/** Constructor to create a new empty map from digests to void*'s.
- */
-digestmap_t *
-digestmap_new(void)
+static INLINE void
+digestmap_entry_free(digestmap_entry_t *ent)
{
- digestmap_t *result;
- result = tor_malloc(sizeof(digestmap_t));
- HT_INIT(digestmap_impl, &result->head);
- return result;
+ tor_free(ent);
}
-
-/** Set the current value for <b>key</b> to <b>val</b>. Returns the previous
- * value for <b>key</b> if one was set, or NULL if one was not.
- *
- * This function makes a copy of <b>key</b> if necessary, but not of
- * <b>val</b>.
- */
-void *
-strmap_set(strmap_t *map, const char *key, void *val)
-{
- strmap_entry_t *resolve;
- strmap_entry_t search;
- void *oldval;
- tor_assert(map);
- tor_assert(key);
- tor_assert(val);
- search.key = (char*)key;
- resolve = HT_FIND(strmap_impl, &map->head, &search);
- if (resolve) {
- oldval = resolve->val;
- resolve->val = val;
- return oldval;
- } else {
- resolve = tor_malloc_zero(sizeof(strmap_entry_t));
- resolve->key = tor_strdup(key);
- resolve->val = val;
- tor_assert(!HT_FIND(strmap_impl, &map->head, resolve));
- HT_INSERT(strmap_impl, &map->head, resolve);
- return NULL;
- }
+static INLINE void
+digest256map_entry_free(digest256map_entry_t *ent)
+{
+ tor_free(ent);
}
-#define OPTIMIZED_DIGESTMAP_SET
-
-/** Like strmap_set() above but for digestmaps. */
-void *
-digestmap_set(digestmap_t *map, const char *key, void *val)
+static INLINE void
+strmap_assign_tmp_key(strmap_entry_t *ent, const char *key)
{
-#ifndef OPTIMIZED_DIGESTMAP_SET
- digestmap_entry_t *resolve;
-#endif
- digestmap_entry_t search;
- void *oldval;
- tor_assert(map);
- tor_assert(key);
- tor_assert(val);
- memcpy(&search.key, key, DIGEST_LEN);
-#ifndef OPTIMIZED_DIGESTMAP_SET
- resolve = HT_FIND(digestmap_impl, &map->head, &search);
- if (resolve) {
- oldval = resolve->val;
- resolve->val = val;
- return oldval;
- } else {
- resolve = tor_malloc_zero(sizeof(digestmap_entry_t));
- memcpy(resolve->key, key, DIGEST_LEN);
- resolve->val = val;
- HT_INSERT(digestmap_impl, &map->head, resolve);
- return NULL;
- }
-#else
- /* We spend up to 5% of our time in this function, so the code below is
- * meant to optimize the check/alloc/set cycle by avoiding the two trips to
- * the hash table that we do in the unoptimized code above. (Each of
- * HT_INSERT and HT_FIND calls HT_SET_HASH and HT_FIND_P.)
- */
- HT_FIND_OR_INSERT_(digestmap_impl, node, digestmap_entry_hash, &(map->head),
- digestmap_entry_t, &search, ptr,
- {
- /* we found an entry. */
- oldval = (*ptr)->val;
- (*ptr)->val = val;
- return oldval;
- },
- {
- /* We didn't find the entry. */
- digestmap_entry_t *newent =
- tor_malloc_zero(sizeof(digestmap_entry_t));
- memcpy(newent->key, key, DIGEST_LEN);
- newent->val = val;
- HT_FOI_INSERT_(node, &(map->head), &search, newent, ptr);
- return NULL;
- });
-#endif
+ ent->key = (char*)key;
}
-
-/** Return the current value associated with <b>key</b>, or NULL if no
- * value is set.
- */
-void *
-strmap_get(const strmap_t *map, const char *key)
-{
- strmap_entry_t *resolve;
- strmap_entry_t search;
- tor_assert(map);
- tor_assert(key);
- search.key = (char*)key;
- resolve = HT_FIND(strmap_impl, &map->head, &search);
- if (resolve) {
- return resolve->val;
- } else {
- return NULL;
- }
+static INLINE void
+digestmap_assign_tmp_key(digestmap_entry_t *ent, const char *key)
+{
+ memcpy(ent->key, key, DIGEST_LEN);
}
-
-/** Like strmap_get() above but for digestmaps. */
-void *
-digestmap_get(const digestmap_t *map, const char *key)
-{
- digestmap_entry_t *resolve;
- digestmap_entry_t search;
- tor_assert(map);
- tor_assert(key);
- memcpy(&search.key, key, DIGEST_LEN);
- resolve = HT_FIND(digestmap_impl, &map->head, &search);
- if (resolve) {
- return resolve->val;
- } else {
- return NULL;
- }
+static INLINE void
+digest256map_assign_tmp_key(digest256map_entry_t *ent, const uint8_t *key)
+{
+ memcpy(ent->key, key, DIGEST256_LEN);
+}
+static INLINE void
+strmap_assign_key(strmap_entry_t *ent, const char *key)
+{
+ ent->key = tor_strdup(key);
+}
+static INLINE void
+digestmap_assign_key(digestmap_entry_t *ent, const char *key)
+{
+ memcpy(ent->key, key, DIGEST_LEN);
+}
+static INLINE void
+digest256map_assign_key(digest256map_entry_t *ent, const uint8_t *key)
+{
+ memcpy(ent->key, key, DIGEST256_LEN);
}
-/** Remove the value currently associated with <b>key</b> from the map.
- * Return the value if one was set, or NULL if there was no entry for
- * <b>key</b>.
- *
- * Note: you must free any storage associated with the returned value.
+/**
+ * Macro: implement all the functions for a map that are declared in
+ * container.h by the DECLARE_MAP_FNS() macro. You must additionally define a
+ * prefix_entry_free_() function to free entries (and their keys), a
+ * prefix_assign_tmp_key() function to temporarily set a stack-allocated
+ * entry to hold a key, and a prefix_assign_key() function to set a
+ * heap-allocated entry to hold a key.
*/
-void *
-strmap_remove(strmap_t *map, const char *key)
-{
- strmap_entry_t *resolve;
- strmap_entry_t search;
- void *oldval;
- tor_assert(map);
- tor_assert(key);
- search.key = (char*)key;
- resolve = HT_REMOVE(strmap_impl, &map->head, &search);
- if (resolve) {
- oldval = resolve->val;
- tor_free(resolve->key);
- tor_free(resolve);
- return oldval;
- } else {
- return NULL;
+#define IMPLEMENT_MAP_FNS(maptype, keytype, prefix) \
+ /** Create and return a new empty map. */ \
+ MOCK_IMPL(maptype *, \
+ prefix##_new,(void)) \
+ { \
+ maptype *result; \
+ result = tor_malloc(sizeof(maptype)); \
+ HT_INIT(prefix##_impl, &result->head); \
+ return result; \
+ } \
+ \
+ /** Return the item from <b>map</b> whose key matches <b>key</b>, or \
+ * NULL if no such value exists. */ \
+ void * \
+ prefix##_get(const maptype *map, const keytype key) \
+ { \
+ prefix ##_entry_t *resolve; \
+ prefix ##_entry_t search; \
+ tor_assert(map); \
+ tor_assert(key); \
+ prefix ##_assign_tmp_key(&search, key); \
+ resolve = HT_FIND(prefix ##_impl, &map->head, &search); \
+ if (resolve) { \
+ return resolve->val; \
+ } else { \
+ return NULL; \
+ } \
+ } \
+ \
+ /** Add an entry to <b>map</b> mapping <b>key</b> to <b>val</b>; \
+ * return the previous value, or NULL if no such value existed. */ \
+ void * \
+ prefix##_set(maptype *map, const keytype key, void *val) \
+ { \
+ prefix##_entry_t search; \
+ void *oldval; \
+ tor_assert(map); \
+ tor_assert(key); \
+ tor_assert(val); \
+ prefix##_assign_tmp_key(&search, key); \
+ /* We a lot of our time in this function, so the code below is */ \
+ /* meant to optimize the check/alloc/set cycle by avoiding the two */\
+ /* trips to the hash table that we would do in the unoptimized */ \
+ /* version of this code. (Each of HT_INSERT and HT_FIND calls */ \
+ /* HT_SET_HASH and HT_FIND_P.) */ \
+ HT_FIND_OR_INSERT_(prefix##_impl, node, prefix##_entry_hash, \
+ &(map->head), \
+ prefix##_entry_t, &search, ptr, \
+ { \
+ /* we found an entry. */ \
+ oldval = (*ptr)->val; \
+ (*ptr)->val = val; \
+ return oldval; \
+ }, \
+ { \
+ /* We didn't find the entry. */ \
+ prefix##_entry_t *newent = \
+ tor_malloc_zero(sizeof(prefix##_entry_t)); \
+ prefix##_assign_key(newent, key); \
+ newent->val = val; \
+ HT_FOI_INSERT_(node, &(map->head), \
+ &search, newent, ptr); \
+ return NULL; \
+ }); \
+ } \
+ \
+ /** Remove the value currently associated with <b>key</b> from the map. \
+ * Return the value if one was set, or NULL if there was no entry for \
+ * <b>key</b>. \
+ * \
+ * Note: you must free any storage associated with the returned value. \
+ */ \
+ void * \
+ prefix##_remove(maptype *map, const keytype key) \
+ { \
+ prefix##_entry_t *resolve; \
+ prefix##_entry_t search; \
+ void *oldval; \
+ tor_assert(map); \
+ tor_assert(key); \
+ prefix##_assign_tmp_key(&search, key); \
+ resolve = HT_REMOVE(prefix##_impl, &map->head, &search); \
+ if (resolve) { \
+ oldval = resolve->val; \
+ prefix##_entry_free(resolve); \
+ return oldval; \
+ } else { \
+ return NULL; \
+ } \
+ } \
+ \
+ /** Return the number of elements in <b>map</b>. */ \
+ int \
+ prefix##_size(const maptype *map) \
+ { \
+ return HT_SIZE(&map->head); \
+ } \
+ \
+ /** Return true iff <b>map</b> has no entries. */ \
+ int \
+ prefix##_isempty(const maptype *map) \
+ { \
+ return HT_EMPTY(&map->head); \
+ } \
+ \
+ /** Assert that <b>map</b> is not corrupt. */ \
+ void \
+ prefix##_assert_ok(const maptype *map) \
+ { \
+ tor_assert(!prefix##_impl_HT_REP_IS_BAD_(&map->head)); \
+ } \
+ \
+ /** Remove all entries from <b>map</b>, and deallocate storage for \
+ * those entries. If free_val is provided, invoked it every value in \
+ * <b>map</b>. */ \
+ MOCK_IMPL(void, \
+ prefix##_free, (maptype *map, void (*free_val)(void*))) \
+ { \
+ prefix##_entry_t **ent, **next, *this; \
+ if (!map) \
+ return; \
+ for (ent = HT_START(prefix##_impl, &map->head); ent != NULL; \
+ ent = next) { \
+ this = *ent; \
+ next = HT_NEXT_RMV(prefix##_impl, &map->head, ent); \
+ if (free_val) \
+ free_val(this->val); \
+ prefix##_entry_free(this); \
+ } \
+ tor_assert(HT_EMPTY(&map->head)); \
+ HT_CLEAR(prefix##_impl, &map->head); \
+ tor_free(map); \
+ } \
+ \
+ /** return an <b>iterator</b> pointer to the front of a map. \
+ * \
+ * Iterator example: \
+ * \
+ * \code \
+ * // uppercase values in "map", removing empty values. \
+ * \
+ * strmap_iter_t *iter; \
+ * const char *key; \
+ * void *val; \
+ * char *cp; \
+ * \
+ * for (iter = strmap_iter_init(map); !strmap_iter_done(iter); ) { \
+ * strmap_iter_get(iter, &key, &val); \
+ * cp = (char*)val; \
+ * if (!*cp) { \
+ * iter = strmap_iter_next_rmv(map,iter); \
+ * free(val); \
+ * } else { \
+ * for (;*cp;cp++) *cp = TOR_TOUPPER(*cp); \
+ */ \
+ prefix##_iter_t * \
+ prefix##_iter_init(maptype *map) \
+ { \
+ tor_assert(map); \
+ return HT_START(prefix##_impl, &map->head); \
+ } \
+ \
+ /** Advance <b>iter</b> a single step to the next entry, and return \
+ * its new value. */ \
+ prefix##_iter_t * \
+ prefix##_iter_next(maptype *map, prefix##_iter_t *iter) \
+ { \
+ tor_assert(map); \
+ tor_assert(iter); \
+ return HT_NEXT(prefix##_impl, &map->head, iter); \
+ } \
+ /** Advance <b>iter</b> a single step to the next entry, removing the \
+ * current entry, and return its new value. */ \
+ prefix##_iter_t * \
+ prefix##_iter_next_rmv(maptype *map, prefix##_iter_t *iter) \
+ { \
+ prefix##_entry_t *rmv; \
+ tor_assert(map); \
+ tor_assert(iter); \
+ tor_assert(*iter); \
+ rmv = *iter; \
+ iter = HT_NEXT_RMV(prefix##_impl, &map->head, iter); \
+ prefix##_entry_free(rmv); \
+ return iter; \
+ } \
+ /** Set *<b>keyp</b> and *<b>valp</b> to the current entry pointed \
+ * to by iter. */ \
+ void \
+ prefix##_iter_get(prefix##_iter_t *iter, const keytype *keyp, \
+ void **valp) \
+ { \
+ tor_assert(iter); \
+ tor_assert(*iter); \
+ tor_assert(keyp); \
+ tor_assert(valp); \
+ *keyp = (*iter)->key; \
+ *valp = (*iter)->val; \
+ } \
+ /** Return true iff <b>iter</b> has advanced past the last entry of \
+ * <b>map</b>. */ \
+ int \
+ prefix##_iter_done(prefix##_iter_t *iter) \
+ { \
+ return iter == NULL; \
}
-}
-/** Like strmap_remove() above but for digestmaps. */
-void *
-digestmap_remove(digestmap_t *map, const char *key)
-{
- digestmap_entry_t *resolve;
- digestmap_entry_t search;
- void *oldval;
- tor_assert(map);
- tor_assert(key);
- memcpy(&search.key, key, DIGEST_LEN);
- resolve = HT_REMOVE(digestmap_impl, &map->head, &search);
- if (resolve) {
- oldval = resolve->val;
- tor_free(resolve);
- return oldval;
- } else {
- return NULL;
- }
-}
+IMPLEMENT_MAP_FNS(strmap_t, char *, strmap)
+IMPLEMENT_MAP_FNS(digestmap_t, char *, digestmap)
+IMPLEMENT_MAP_FNS(digest256map_t, uint8_t *, digest256map)
/** Same as strmap_set, but first converts <b>key</b> to lowercase. */
void *
@@ -1264,231 +1387,6 @@ strmap_remove_lc(strmap_t *map, const char *key)
return v;
}
-/** return an <b>iterator</b> pointer to the front of a map.
- *
- * Iterator example:
- *
- * \code
- * // uppercase values in "map", removing empty values.
- *
- * strmap_iter_t *iter;
- * const char *key;
- * void *val;
- * char *cp;
- *
- * for (iter = strmap_iter_init(map); !strmap_iter_done(iter); ) {
- * strmap_iter_get(iter, &key, &val);
- * cp = (char*)val;
- * if (!*cp) {
- * iter = strmap_iter_next_rmv(map,iter);
- * free(val);
- * } else {
- * for (;*cp;cp++) *cp = TOR_TOUPPER(*cp);
- * iter = strmap_iter_next(map,iter);
- * }
- * }
- * \endcode
- *
- */
-strmap_iter_t *
-strmap_iter_init(strmap_t *map)
-{
- tor_assert(map);
- return HT_START(strmap_impl, &map->head);
-}
-
-/** Start iterating through <b>map</b>. See strmap_iter_init() for example. */
-digestmap_iter_t *
-digestmap_iter_init(digestmap_t *map)
-{
- tor_assert(map);
- return HT_START(digestmap_impl, &map->head);
-}
-
-/** Advance the iterator <b>iter</b> for <b>map</b> a single step to the next
- * entry, and return its new value. */
-strmap_iter_t *
-strmap_iter_next(strmap_t *map, strmap_iter_t *iter)
-{
- tor_assert(map);
- tor_assert(iter);
- return HT_NEXT(strmap_impl, &map->head, iter);
-}
-
-/** Advance the iterator <b>iter</b> for map a single step to the next entry,
- * and return its new value. */
-digestmap_iter_t *
-digestmap_iter_next(digestmap_t *map, digestmap_iter_t *iter)
-{
- tor_assert(map);
- tor_assert(iter);
- return HT_NEXT(digestmap_impl, &map->head, iter);
-}
-
-/** Advance the iterator <b>iter</b> a single step to the next entry, removing
- * the current entry, and return its new value.
- */
-strmap_iter_t *
-strmap_iter_next_rmv(strmap_t *map, strmap_iter_t *iter)
-{
- strmap_entry_t *rmv;
- tor_assert(map);
- tor_assert(iter);
- tor_assert(*iter);
- rmv = *iter;
- iter = HT_NEXT_RMV(strmap_impl, &map->head, iter);
- tor_free(rmv->key);
- tor_free(rmv);
- return iter;
-}
-
-/** Advance the iterator <b>iter</b> a single step to the next entry, removing
- * the current entry, and return its new value.
- */
-digestmap_iter_t *
-digestmap_iter_next_rmv(digestmap_t *map, digestmap_iter_t *iter)
-{
- digestmap_entry_t *rmv;
- tor_assert(map);
- tor_assert(iter);
- tor_assert(*iter);
- rmv = *iter;
- iter = HT_NEXT_RMV(digestmap_impl, &map->head, iter);
- tor_free(rmv);
- return iter;
-}
-
-/** Set *<b>keyp</b> and *<b>valp</b> to the current entry pointed to by
- * iter. */
-void
-strmap_iter_get(strmap_iter_t *iter, const char **keyp, void **valp)
-{
- tor_assert(iter);
- tor_assert(*iter);
- tor_assert(keyp);
- tor_assert(valp);
- *keyp = (*iter)->key;
- *valp = (*iter)->val;
-}
-
-/** Set *<b>keyp</b> and *<b>valp</b> to the current entry pointed to by
- * iter. */
-void
-digestmap_iter_get(digestmap_iter_t *iter, const char **keyp, void **valp)
-{
- tor_assert(iter);
- tor_assert(*iter);
- tor_assert(keyp);
- tor_assert(valp);
- *keyp = (*iter)->key;
- *valp = (*iter)->val;
-}
-
-/** Return true iff <b>iter</b> has advanced past the last entry of
- * <b>map</b>. */
-int
-strmap_iter_done(strmap_iter_t *iter)
-{
- return iter == NULL;
-}
-
-/** Return true iff <b>iter</b> has advanced past the last entry of
- * <b>map</b>. */
-int
-digestmap_iter_done(digestmap_iter_t *iter)
-{
- return iter == NULL;
-}
-
-/** Remove all entries from <b>map</b>, and deallocate storage for those
- * entries. If free_val is provided, it is invoked on every value in
- * <b>map</b>.
- */
-void
-strmap_free(strmap_t *map, void (*free_val)(void*))
-{
- strmap_entry_t **ent, **next, *this;
- if (!map)
- return;
-
- for (ent = HT_START(strmap_impl, &map->head); ent != NULL; ent = next) {
- this = *ent;
- next = HT_NEXT_RMV(strmap_impl, &map->head, ent);
- tor_free(this->key);
- if (free_val)
- free_val(this->val);
- tor_free(this);
- }
- tor_assert(HT_EMPTY(&map->head));
- HT_CLEAR(strmap_impl, &map->head);
- tor_free(map);
-}
-
-/** Remove all entries from <b>map</b>, and deallocate storage for those
- * entries. If free_val is provided, it is invoked on every value in
- * <b>map</b>.
- */
-void
-digestmap_free(digestmap_t *map, void (*free_val)(void*))
-{
- digestmap_entry_t **ent, **next, *this;
- if (!map)
- return;
- for (ent = HT_START(digestmap_impl, &map->head); ent != NULL; ent = next) {
- this = *ent;
- next = HT_NEXT_RMV(digestmap_impl, &map->head, ent);
- if (free_val)
- free_val(this->val);
- tor_free(this);
- }
- tor_assert(HT_EMPTY(&map->head));
- HT_CLEAR(digestmap_impl, &map->head);
- tor_free(map);
-}
-
-/** Fail with an assertion error if anything has gone wrong with the internal
- * representation of <b>map</b>. */
-void
-strmap_assert_ok(const strmap_t *map)
-{
- tor_assert(!strmap_impl_HT_REP_IS_BAD_(&map->head));
-}
-/** Fail with an assertion error if anything has gone wrong with the internal
- * representation of <b>map</b>. */
-void
-digestmap_assert_ok(const digestmap_t *map)
-{
- tor_assert(!digestmap_impl_HT_REP_IS_BAD_(&map->head));
-}
-
-/** Return true iff <b>map</b> has no entries. */
-int
-strmap_isempty(const strmap_t *map)
-{
- return HT_EMPTY(&map->head);
-}
-
-/** Return true iff <b>map</b> has no entries. */
-int
-digestmap_isempty(const digestmap_t *map)
-{
- return HT_EMPTY(&map->head);
-}
-
-/** Return the number of items in <b>map</b>. */
-int
-strmap_size(const strmap_t *map)
-{
- return HT_SIZE(&map->head);
-}
-
-/** Return the number of items in <b>map</b>. */
-int
-digestmap_size(const digestmap_t *map)
-{
- return HT_SIZE(&map->head);
-}
-
/** Declare a function called <b>funcname</b> that acts as a find_nth_FOO
* function for an array of type <b>elt_t</b>*.
*
diff --git a/src/common/container.h b/src/common/container.h
index 1bcc540665..d3d20af5b2 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -1,12 +1,13 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_CONTAINER_H
#define TOR_CONTAINER_H
#include "util.h"
+#include "siphash.h"
/** A resizeable list of pointers, with associated helpful functionality.
*
@@ -26,8 +27,8 @@ typedef struct smartlist_t {
/** @} */
} smartlist_t;
-smartlist_t *smartlist_new(void);
-void smartlist_free(smartlist_t *sl);
+MOCK_DECL(smartlist_t *, smartlist_new, (void));
+MOCK_DECL(void, smartlist_free, (smartlist_t *sl));
void smartlist_clear(smartlist_t *sl);
void smartlist_add(smartlist_t *sl, void *element);
void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2);
@@ -102,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);
@@ -326,11 +328,11 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join,
#define DECLARE_MAP_FNS(maptype, keytype, prefix) \
typedef struct maptype maptype; \
typedef struct prefix##entry_t *prefix##iter_t; \
- maptype* prefix##new(void); \
+ MOCK_DECL(maptype*, prefix##new, (void)); \
void* prefix##set(maptype *map, keytype key, void *val); \
void* prefix##get(const maptype *map, keytype key); \
void* prefix##remove(maptype *map, keytype key); \
- void prefix##free(maptype *map, void (*free_val)(void*)); \
+ MOCK_DECL(void, prefix##free, (maptype *map, void (*free_val)(void*))); \
int prefix##isempty(const maptype *map); \
int prefix##size(const maptype *map); \
prefix##iter_t *prefix##iter_init(maptype *map); \
@@ -344,6 +346,9 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join,
DECLARE_MAP_FNS(strmap_t, const char *, strmap_);
/* Map from const char[DIGEST_LEN] to void *. Implemented with a hash table. */
DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_);
+/* Map from const uint8_t[DIGEST_LEN] to void *. Implemented with a hash
+ * table. */
+DECLARE_MAP_FNS(digest256map_t, const uint8_t *, digest256map_);
#undef DECLARE_MAP_FNS
@@ -459,6 +464,13 @@ DECLARE_MAP_FNS(digestmap_t, const char *, digestmap_);
/** Used to end a DIGESTMAP_FOREACH() block. */
#define DIGESTMAP_FOREACH_END MAP_FOREACH_END
+#define DIGEST256MAP_FOREACH(map, keyvar, valtype, valvar) \
+ MAP_FOREACH(digest256map_, map, const uint8_t *, keyvar, valtype, valvar)
+#define DIGEST256MAP_FOREACH_MODIFY(map, keyvar, valtype, valvar) \
+ MAP_FOREACH_MODIFY(digest256map_, map, const uint8_t *, \
+ keyvar, valtype, valvar)
+#define DIGEST256MAP_FOREACH_END MAP_FOREACH_END
+
#define STRMAP_FOREACH(map, keyvar, valtype, valvar) \
MAP_FOREACH(strmap_, map, const char *, keyvar, valtype, valvar)
#define STRMAP_FOREACH_MODIFY(map, keyvar, valtype, valvar) \
@@ -471,65 +483,75 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
#define DECLARE_TYPED_DIGESTMAP_FNS(prefix, maptype, valtype) \
typedef struct maptype maptype; \
- typedef struct prefix##iter_t prefix##iter_t; \
- static INLINE maptype* prefix##new(void) \
+ typedef struct prefix##iter_t *prefix##iter_t; \
+ ATTR_UNUSED static INLINE maptype* \
+ prefix##new(void) \
{ \
return (maptype*)digestmap_new(); \
} \
- static INLINE digestmap_t* prefix##to_digestmap(maptype *map) \
+ ATTR_UNUSED static INLINE digestmap_t* \
+ prefix##to_digestmap(maptype *map) \
{ \
return (digestmap_t*)map; \
} \
- static INLINE valtype* prefix##get(maptype *map, const char *key) \
+ ATTR_UNUSED static INLINE valtype* \
+ prefix##get(maptype *map, const char *key) \
{ \
return (valtype*)digestmap_get((digestmap_t*)map, key); \
} \
- static INLINE valtype* prefix##set(maptype *map, const char *key, \
- valtype *val) \
+ ATTR_UNUSED static INLINE valtype* \
+ prefix##set(maptype *map, const char *key, valtype *val) \
{ \
return (valtype*)digestmap_set((digestmap_t*)map, key, val); \
} \
- static INLINE valtype* prefix##remove(maptype *map, const char *key) \
+ ATTR_UNUSED static INLINE valtype* \
+ prefix##remove(maptype *map, const char *key) \
{ \
return (valtype*)digestmap_remove((digestmap_t*)map, key); \
} \
- static INLINE void prefix##free(maptype *map, void (*free_val)(void*)) \
+ ATTR_UNUSED static INLINE void \
+ prefix##free(maptype *map, void (*free_val)(void*)) \
{ \
digestmap_free((digestmap_t*)map, free_val); \
} \
- static INLINE int prefix##isempty(maptype *map) \
+ ATTR_UNUSED static INLINE int \
+ prefix##isempty(maptype *map) \
{ \
return digestmap_isempty((digestmap_t*)map); \
} \
- static INLINE int prefix##size(maptype *map) \
+ ATTR_UNUSED static INLINE int \
+ prefix##size(maptype *map) \
{ \
return digestmap_size((digestmap_t*)map); \
} \
- static INLINE prefix##iter_t *prefix##iter_init(maptype *map) \
+ ATTR_UNUSED static INLINE \
+ prefix##iter_t *prefix##iter_init(maptype *map) \
{ \
return (prefix##iter_t*) digestmap_iter_init((digestmap_t*)map); \
} \
- static INLINE prefix##iter_t *prefix##iter_next(maptype *map, \
- prefix##iter_t *iter) \
+ ATTR_UNUSED static INLINE \
+ prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter) \
{ \
return (prefix##iter_t*) digestmap_iter_next( \
(digestmap_t*)map, (digestmap_iter_t*)iter); \
} \
- static INLINE prefix##iter_t *prefix##iter_next_rmv(maptype *map, \
- prefix##iter_t *iter) \
+ ATTR_UNUSED static INLINE prefix##iter_t* \
+ prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter) \
{ \
return (prefix##iter_t*) digestmap_iter_next_rmv( \
(digestmap_t*)map, (digestmap_iter_t*)iter); \
} \
- static INLINE void prefix##iter_get(prefix##iter_t *iter, \
- const char **keyp, \
- valtype **valp) \
+ ATTR_UNUSED static INLINE void \
+ prefix##iter_get(prefix##iter_t *iter, \
+ const char **keyp, \
+ valtype **valp) \
{ \
void *v; \
digestmap_iter_get((digestmap_iter_t*) iter, keyp, &v); \
*valp = v; \
} \
- static INLINE int prefix##iter_done(prefix##iter_t *iter) \
+ ATTR_UNUSED static INLINE int \
+ prefix##iter_done(prefix##iter_t *iter) \
{ \
return digestmap_iter_done((digestmap_iter_t*)iter); \
}
@@ -551,7 +573,7 @@ bitarray_init_zero(unsigned int n_bits)
{
/* round up to the next int. */
size_t sz = (n_bits+BITARRAY_MASK) >> BITARRAY_SHIFT;
- return tor_malloc_zero(sz*sizeof(unsigned int));
+ return tor_calloc(sz, sizeof(unsigned int));
}
/** Expand <b>ba</b> from holding <b>n_bits_old</b> to <b>n_bits_new</b>,
* clearing all new bits. Returns a possibly changed pointer to the
@@ -565,7 +587,7 @@ bitarray_expand(bitarray_t *ba,
char *ptr;
if (sz_new <= sz_old)
return ba;
- ptr = tor_realloc(ba, sz_new*sizeof(unsigned int));
+ ptr = tor_reallocarray(ba, sz_new, sizeof(unsigned int));
/* This memset does nothing to the older excess bytes. But they were
* already set to 0 by bitarry_init_zero. */
memset(ptr+sz_old*sizeof(unsigned int), 0,
@@ -610,11 +632,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));
@@ -626,11 +648,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)) &&
@@ -677,5 +699,11 @@ median_int32(int32_t *array, int n_elements)
return find_nth_int32(array, n_elements, (n_elements-1)/2);
}
+static INLINE uint32_t
+third_quartile_uint32(uint32_t *array, int n_elements)
+{
+ return find_nth_uint32(array, n_elements, (n_elements*3)/4);
+}
+
#endif
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 9bdb1f41fa..7138ba003e 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -75,12 +75,10 @@
/** Macro: is k a valid RSA private key? */
#define PRIVATE_KEY_OK(k) ((k) && (k)->key && (k)->key->p)
-#ifdef TOR_IS_MULTITHREADED
/** A number of preallocated mutexes for use by OpenSSL. */
static tor_mutex_t **openssl_mutexes_ = NULL;
/** How many mutexes have we allocated for use by OpenSSL? */
static int n_openssl_mutexes_ = 0;
-#endif
/** A public key, or a public/private key-pair. */
struct crypto_pk_t
@@ -132,6 +130,9 @@ crypto_get_rsa_padding(int padding)
}
/** 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
@@ -242,15 +243,49 @@ crypto_openssl_get_header_version_str(void)
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 &&
@@ -272,6 +307,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;
@@ -335,17 +390,13 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
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;
}
@@ -961,7 +1012,7 @@ crypto_pk_public_checksig(crypto_pk_t *env, char *to,
env->key, RSA_PKCS1_PADDING);
if (r<0) {
- crypto_log_errors(LOG_WARN, "checking RSA signature");
+ crypto_log_errors(LOG_INFO, "checking RSA signature");
return -1;
}
return r;
@@ -1324,6 +1375,28 @@ crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space)
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>.
@@ -1519,7 +1592,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.
@@ -1611,7 +1684,7 @@ crypto_digest_get_digest(crypto_digest_t *digest,
log_warn(LD_BUG, "Called with unknown algorithm %d", digest->algorithm);
/* If fragile_assert is not enabled, then we should at least not
* leak anything. */
- memset(r, 0xff, sizeof(r));
+ memwipe(r, 0xff, sizeof(r));
tor_fragile_assert();
break;
}
@@ -1765,7 +1838,7 @@ crypto_store_dynamic_dh_modulus(const char *fname)
goto done;
}
- base64_encoded_dh = tor_malloc_zero(len * 2); /* should be enough */
+ base64_encoded_dh = tor_calloc(len, 2); /* should be enough */
new_len = base64_encode(base64_encoded_dh, len * 2,
(char *)dh_string_repr, len);
if (new_len < 0) {
@@ -2381,10 +2454,8 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
if (!provider_set) {
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
- if ((unsigned long)GetLastError() != (unsigned long)NTE_BAD_KEYSET) {
- log_warn(LD_CRYPTO, "Can't get CryptoAPI provider [1]");
- return -1;
- }
+ log_warn(LD_CRYPTO, "Can't get CryptoAPI provider [1]");
+ return -1;
}
provider_set = 1;
}
@@ -2396,6 +2467,7 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
return 0;
#else
for (i = 0; filenames[i]; ++i) {
+ 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]);
@@ -2927,50 +2999,6 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
return 0;
}
-/** Implement RFC2440-style iterated-salted S2K conversion: convert the
- * <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
- * <b>key_out</b>. As in RFC2440, the first 8 bytes of s2k_specifier
- * are a salt; the 9th byte describes how much iteration to do.
- * Does not support <b>key_out_len</b> &gt; DIGEST_LEN.
- */
-void
-secret_to_key(char *key_out, size_t key_out_len, const char *secret,
- size_t secret_len, const char *s2k_specifier)
-{
- crypto_digest_t *d;
- uint8_t c;
- size_t count, tmplen;
- char *tmp;
- tor_assert(key_out_len < SIZE_T_CEILING);
-
-#define EXPBIAS 6
- c = s2k_specifier[8];
- count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS);
-#undef EXPBIAS
-
- tor_assert(key_out_len <= DIGEST_LEN);
-
- d = crypto_digest_new();
- tmplen = 8+secret_len;
- tmp = tor_malloc(tmplen);
- memcpy(tmp,s2k_specifier,8);
- memcpy(tmp+8,secret,secret_len);
- secret_len += 8;
- while (count) {
- if (count >= secret_len) {
- crypto_digest_add_bytes(d, tmp, secret_len);
- count -= secret_len;
- } else {
- crypto_digest_add_bytes(d, tmp, count);
- count = 0;
- }
- }
- crypto_digest_get_digest(d, key_out, key_out_len);
- memwipe(tmp, 0, tmplen);
- tor_free(tmp);
- crypto_digest_free(d);
-}
-
/**
* Destroy the <b>sz</b> bytes of data stored at <b>mem</b>, setting them to
* the value <b>byte</b>.
@@ -3014,8 +3042,6 @@ memwipe(void *mem, uint8_t byte, size_t sz)
memset(mem, byte, sz);
}
-#ifdef TOR_IS_MULTITHREADED
-
#ifndef OPENSSL_THREADS
#error OpenSSL has been built without thread support. Tor requires an \
OpenSSL library with thread support enabled.
@@ -3028,7 +3054,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;
@@ -3092,7 +3118,7 @@ setup_openssl_threading(void)
int i;
int n = CRYPTO_num_locks();
n_openssl_mutexes_ = n;
- openssl_mutexes_ = tor_malloc(n*sizeof(tor_mutex_t *));
+ openssl_mutexes_ = tor_calloc(n, sizeof(tor_mutex_t *));
for (i=0; i < n; ++i)
openssl_mutexes_[i] = tor_mutex_new();
CRYPTO_set_locking_callback(openssl_locking_cb_);
@@ -3102,13 +3128,6 @@ setup_openssl_threading(void)
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_);
return 0;
}
-#else
-static int
-setup_openssl_threading(void)
-{
- return 0;
-}
-#endif
/** Uninitialize the crypto library. Return 0 on success, -1 on failure.
*/
@@ -3132,7 +3151,7 @@ crypto_global_cleanup(void)
CONF_modules_unload(1);
CRYPTO_cleanup_all_ex_data();
-#ifdef TOR_IS_MULTITHREADED
+
if (n_openssl_mutexes_) {
int n = n_openssl_mutexes_;
tor_mutex_t **ms = openssl_mutexes_;
@@ -3144,7 +3163,7 @@ crypto_global_cleanup(void)
}
tor_free(ms);
}
-#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 6ce3697c92..d496521849 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -89,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
@@ -110,6 +111,7 @@ typedef struct crypto_dh_t crypto_dh_t;
/* global state */
const char * crypto_openssl_get_version_str(void);
const char * crypto_openssl_get_header_version_str(void);
+int crypto_early_init(void);
int crypto_global_init(int hardwareAccel,
const char *accelName,
const char *accelPath);
@@ -181,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);
@@ -256,6 +259,7 @@ 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);
@@ -276,12 +280,6 @@ int digest_from_base64(char *digest, const char *d64);
int digest256_to_base64(char *d64, const char *digest);
int digest256_from_base64(char *digest, const char *d64);
-/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the
- * 9th describes how much iteration to do. */
-#define S2K_SPECIFIER_LEN 9
-void secret_to_key(char *key_out, size_t key_out_len, const char *secret,
- size_t secret_len, const char *s2k_specifier);
-
/** OpenSSL-based utility functions. */
void memwipe(void *mem, uint8_t byte, size_t sz);
diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c
index 9e83440e16..c04b715abd 100644
--- a/src/common/crypto_curve25519.c
+++ b/src/common/crypto_curve25519.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Tor Project, Inc. */
+/* Copyright (c) 2012-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* Wrapper code for a curve25519 implementation. */
@@ -8,6 +8,7 @@
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
+#include "container.h"
#include "crypto.h"
#include "crypto_curve25519.h"
#include "util.h"
@@ -63,26 +64,44 @@ curve25519_public_key_is_ok(const curve25519_public_key_t *key)
return !safe_mem_is_zero(key->public_key, CURVE25519_PUBKEY_LEN);
}
-/** Generate a new keypair and return the secret key. If <b>extra_strong</b>
- * is true, this key is possibly going to get used more than once, so
- * use a better-than-usual RNG. Return 0 on success, -1 on failure. */
+/**
+ * Generate CURVE25519_SECKEY_LEN random bytes in <b>out</b>. If
+ * <b>extra_strong</b> is true, this key is possibly going to get used more
+ * than once, so use a better-than-usual RNG. Return 0 on success, -1 on
+ * failure.
+ *
+ * This function does not adjust the output of the RNG at all; the will caller
+ * will need to clear or set the appropriate bits to make curve25519 work.
+ */
int
-curve25519_secret_key_generate(curve25519_secret_key_t *key_out,
- int extra_strong)
+curve25519_rand_seckey_bytes(uint8_t *out, int extra_strong)
{
uint8_t k_tmp[CURVE25519_SECKEY_LEN];
- if (crypto_rand((char*)key_out->secret_key, CURVE25519_SECKEY_LEN) < 0)
+ if (crypto_rand((char*)out, CURVE25519_SECKEY_LEN) < 0)
return -1;
if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) {
/* If they asked for extra-strong entropy and we have some, use it as an
* HMAC key to improve not-so-good entropy rather than using it directly,
* just in case the extra-strong entropy is less amazing than we hoped. */
- crypto_hmac_sha256((char *)key_out->secret_key,
- (const char *)k_tmp, sizeof(k_tmp),
- (const char *)key_out->secret_key, CURVE25519_SECKEY_LEN);
+ crypto_hmac_sha256((char*) out,
+ (const char *)k_tmp, sizeof(k_tmp),
+ (const char *)out, CURVE25519_SECKEY_LEN);
}
memwipe(k_tmp, 0, sizeof(k_tmp));
+ return 0;
+}
+
+/** Generate a new keypair and return the secret key. If <b>extra_strong</b>
+ * is true, this key is possibly going to get used more than once, so
+ * use a better-than-usual RNG. Return 0 on success, -1 on failure. */
+int
+curve25519_secret_key_generate(curve25519_secret_key_t *key_out,
+ int extra_strong)
+{
+ if (curve25519_rand_seckey_bytes(key_out->secret_key, extra_strong) < 0)
+ return -1;
+
key_out->secret_key[0] &= 248;
key_out->secret_key[31] &= 127;
key_out->secret_key[31] |= 64;
@@ -109,69 +128,144 @@ curve25519_keypair_generate(curve25519_keypair_t *keypair_out,
return 0;
}
+/** Write the <b>datalen</b> bytes from <b>data</b> to the file named
+ * <b>fname</b> in the tagged-data format. This format contains a
+ * 32-byte header, followed by the data itself. The header is the
+ * NUL-padded string "== <b>typestring</b>: <b>tag</b> ==". The length
+ * of <b>typestring</b> and <b>tag</b> must therefore be no more than
+ * 24.
+ **/
int
-curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair,
- const char *fname,
- const char *tag)
+crypto_write_tagged_contents_to_file(const char *fname,
+ const char *typestring,
+ const char *tag,
+ const uint8_t *data,
+ size_t datalen)
{
- char contents[32 + CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN];
- int r;
+ char header[32];
+ smartlist_t *chunks = smartlist_new();
+ sized_chunk_t ch0, ch1;
+ int r = -1;
- memset(contents, 0, sizeof(contents));
- tor_snprintf(contents, sizeof(contents), "== c25519v1: %s ==", tag);
- tor_assert(strlen(contents) <= 32);
- memcpy(contents+32, keypair->seckey.secret_key, CURVE25519_SECKEY_LEN);
- memcpy(contents+32+CURVE25519_SECKEY_LEN,
- keypair->pubkey.public_key, CURVE25519_PUBKEY_LEN);
+ memset(header, 0, sizeof(header));
+ if (tor_snprintf(header, sizeof(header),
+ "== %s: %s ==", typestring, tag) < 0)
+ goto end;
+ ch0.bytes = header;
+ ch0.len = 32;
+ ch1.bytes = (const char*) data;
+ ch1.len = datalen;
+ smartlist_add(chunks, &ch0);
+ smartlist_add(chunks, &ch1);
- r = write_bytes_to_file(fname, contents, sizeof(contents), 1);
+ r = write_chunks_to_file(fname, chunks, 1, 0);
- memwipe(contents, 0, sizeof(contents));
+ end:
+ smartlist_free(chunks);
return r;
}
-int
-curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out,
- char **tag_out,
- const char *fname)
+/** Read a tagged-data file from <b>fname</b> into the
+ * <b>data_out_len</b>-byte buffer in <b>data_out</b>. Check that the
+ * typestring matches <b>typestring</b>; store the tag into a newly allocated
+ * string in <b>tag_out</b>. Return -1 on failure, and the number of bytes of
+ * data on success. */
+ssize_t
+crypto_read_tagged_contents_from_file(const char *fname,
+ const char *typestring,
+ char **tag_out,
+ uint8_t *data_out,
+ ssize_t data_out_len)
{
char prefix[33];
- char *content;
+ char *content = NULL;
struct stat st;
- int r = -1;
+ ssize_t r = -1;
+ size_t st_size = 0;
*tag_out = NULL;
-
st.st_size = 0;
content = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st);
if (! content)
goto end;
- if (st.st_size != 32 + CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN)
+ if (st.st_size < 32 || st.st_size > 32 + data_out_len)
goto end;
+ st_size = (size_t)st.st_size;
memcpy(prefix, content, 32);
- prefix[32] = '\0';
- if (strcmpstart(prefix, "== c25519v1: ") ||
- strcmpend(prefix, " =="))
+ prefix[32] = 0;
+ /* Check type, extract tag. */
+ if (strcmpstart(prefix, "== ") || strcmpend(prefix, " ==") ||
+ ! tor_mem_is_zero(prefix+strlen(prefix), 32-strlen(prefix)))
+ goto end;
+
+ if (strcmpstart(prefix+3, typestring) ||
+ 3+strlen(typestring) >= 32 ||
+ strcmpstart(prefix+3+strlen(typestring), ": "))
goto end;
- *tag_out = tor_strndup(prefix+strlen("== c25519v1: "),
- strlen(prefix) - strlen("== c25519v1: =="));
+ *tag_out = tor_strndup(prefix+5+strlen(typestring),
+ strlen(prefix)-8-strlen(typestring));
+
+ memcpy(data_out, content+32, st_size-32);
+ r = st_size - 32;
+
+ end:
+ if (content)
+ memwipe(content, 0, st_size);
+ tor_free(content);
+ return r;
+}
+
+/** DOCDOC */
+int
+curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair,
+ const char *fname,
+ const char *tag)
+{
+ uint8_t contents[CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN];
+ int r;
+
+ memcpy(contents, keypair->seckey.secret_key, CURVE25519_SECKEY_LEN);
+ memcpy(contents+CURVE25519_SECKEY_LEN,
+ keypair->pubkey.public_key, CURVE25519_PUBKEY_LEN);
+
+ r = crypto_write_tagged_contents_to_file(fname,
+ "c25519v1",
+ tag,
+ contents,
+ sizeof(contents));
+
+ memwipe(contents, 0, sizeof(contents));
+ return r;
+}
+
+/** DOCDOC */
+int
+curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out,
+ char **tag_out,
+ const char *fname)
+{
+ uint8_t content[CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN];
+ ssize_t len;
+ int r = -1;
+
+ len = crypto_read_tagged_contents_from_file(fname, "c25519v1", tag_out,
+ content, sizeof(content));
+ if (len != sizeof(content))
+ goto end;
- memcpy(keypair_out->seckey.secret_key, content+32, CURVE25519_SECKEY_LEN);
+ memcpy(keypair_out->seckey.secret_key, content, CURVE25519_SECKEY_LEN);
curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey);
if (tor_memneq(keypair_out->pubkey.public_key,
- content + 32 + CURVE25519_SECKEY_LEN,
+ content + CURVE25519_SECKEY_LEN,
CURVE25519_PUBKEY_LEN))
goto end;
r = 0;
end:
- if (content) {
- memwipe(content, 0, (size_t) st.st_size);
- tor_free(content);
- }
+ memwipe(content, 0, sizeof(content));
if (r != 0) {
memset(keypair_out, 0, sizeof(*keypair_out));
tor_free(*tag_out);
diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h
index 57018ac2f5..e8f885227e 100644
--- a/src/common/crypto_curve25519.h
+++ b/src/common/crypto_curve25519.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Tor Project, Inc. */
+/* Copyright (c) 2012-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_CRYPTO_CURVE25519_H
@@ -30,7 +30,6 @@ typedef struct curve25519_keypair_t {
curve25519_secret_key_t seckey;
} 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.
@@ -57,11 +56,12 @@ int curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out,
char **tag_out,
const char *fname);
+int curve25519_rand_seckey_bytes(uint8_t *out, int extra_strong);
+
#ifdef CRYPTO_CURVE25519_PRIVATE
STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
const uint8_t *basepoint);
#endif
-#endif
#define CURVE25519_BASE64_PADDED_LEN 44
@@ -70,5 +70,17 @@ int curve25519_public_from_base64(curve25519_public_key_t *pkey,
int curve25519_public_to_base64(char *output,
const curve25519_public_key_t *pkey);
+int crypto_write_tagged_contents_to_file(const char *fname,
+ const char *typestring,
+ const char *tag,
+ const uint8_t *data,
+ size_t datalen);
+
+ssize_t crypto_read_tagged_contents_from_file(const char *fname,
+ const char *typestring,
+ char **tag_out,
+ uint8_t *data_out,
+ ssize_t data_out_len);
+
#endif
diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c
new file mode 100644
index 0000000000..340fb4956f
--- /dev/null
+++ b/src/common/crypto_ed25519.c
@@ -0,0 +1,353 @@
+/* Copyright (c) 2013-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* Wrapper code for an ed25519 implementation. */
+
+#include "orconfig.h"
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "crypto.h"
+
+#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
+#include "torlog.h"
+#include "util.h"
+
+#include "ed25519/ref10/ed25519_ref10.h"
+
+#include <openssl/sha.h>
+
+/**
+ * Initialize a new ed25519 secret key in <b>seckey_out</b>. If
+ * <b>extra_strong</b>, take the RNG inputs directly from the operating
+ * system. Return 0 on success, -1 on failure.
+ */
+int
+ed25519_secret_key_generate(ed25519_secret_key_t *seckey_out,
+ int extra_strong)
+{
+ int r;
+ uint8_t seed[32];
+ if (! extra_strong || crypto_strongest_rand(seed, sizeof(seed)) < 0)
+ crypto_rand((char*)seed, sizeof(seed));
+
+ r = ed25519_ref10_seckey_expand(seckey_out->seckey, seed);
+ memwipe(seed, 0, sizeof(seed));
+
+ return r < 0 ? -1 : 0;
+}
+
+/**
+ * Given a 32-byte random seed in <b>seed</b>, expand it into an ed25519
+ * secret key in <b>seckey_out</b>. Return 0 on success, -1 on failure.
+ */
+int
+ed25519_secret_key_from_seed(ed25519_secret_key_t *seckey_out,
+ const uint8_t *seed)
+{
+ if (ed25519_ref10_seckey_expand(seckey_out->seckey, seed) < 0)
+ return -1;
+ return 0;
+}
+
+/**
+ * Given a secret key in <b>seckey</b>, expand it into an
+ * ed25519 public key. Return 0 on success, -1 on failure.
+ */
+int
+ed25519_public_key_generate(ed25519_public_key_t *pubkey_out,
+ const ed25519_secret_key_t *seckey)
+{
+ if (ed25519_ref10_pubkey(pubkey_out->pubkey, seckey->seckey) < 0)
+ return -1;
+ return 0;
+}
+
+/** Generate a new ed25519 keypair in <b>keypair_out</b>. If
+ * <b>extra_strong</b> is set, try to mix some system entropy into the key
+ * generation process. Return 0 on success, -1 on failure. */
+int
+ed25519_keypair_generate(ed25519_keypair_t *keypair_out, int extra_strong)
+{
+ if (ed25519_secret_key_generate(&keypair_out->seckey, extra_strong) < 0)
+ return -1;
+ if (ed25519_public_key_generate(&keypair_out->pubkey,
+ &keypair_out->seckey)<0)
+ return -1;
+ return 0;
+}
+
+/**
+ * Set <b>signature_out</b> to a signature of the <b>len</b>-byte message
+ * <b>msg</b>, using the secret and public key in <b>keypair</b>.
+ */
+int
+ed25519_sign(ed25519_signature_t *signature_out,
+ const uint8_t *msg, size_t len,
+ const ed25519_keypair_t *keypair)
+{
+
+ if (ed25519_ref10_sign(signature_out->sig, msg, len,
+ keypair->seckey.seckey,
+ keypair->pubkey.pubkey) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Check whether if <b>signature</b> is a valid signature for the
+ * <b>len</b>-byte message in <b>msg</b> made with the key <b>pubkey</b>.
+ *
+ * Return 0 if the signature is valid; -1 if it isn't.
+ */
+int
+ed25519_checksig(const ed25519_signature_t *signature,
+ const uint8_t *msg, size_t len,
+ const ed25519_public_key_t *pubkey)
+{
+ return
+ ed25519_ref10_open(signature->sig, msg, len, pubkey->pubkey) < 0 ? -1 : 0;
+}
+
+/** Validate every signature among those in <b>checkable</b>, which contains
+ * exactly <b>n_checkable</b> elements. If <b>okay_out</b> is non-NULL, set
+ * the i'th element of <b>okay_out</b> to 1 if the i'th element of
+ * <b>checkable</b> is valid, and to 0 otherwise. Return 0 if every signature
+ * was valid. Otherwise return -N, where N is the number of invalid
+ * signatures.
+ */
+int
+ed25519_checksig_batch(int *okay_out,
+ const ed25519_checkable_t *checkable,
+ int n_checkable)
+{
+ int res, i;
+
+ res = 0;
+ for (i = 0; i < n_checkable; ++i) {
+ const ed25519_checkable_t *ch = &checkable[i];
+ int r = ed25519_checksig(&ch->signature, ch->msg, ch->len, ch->pubkey);
+ if (r < 0)
+ --res;
+ if (okay_out)
+ okay_out[i] = (r == 0);
+ }
+
+#if 0
+ /* This is how we'd do it if we were using ed25519_donna. I'll keep this
+ * code around here in case we ever do that. */
+ const uint8_t **ms;
+ size_t *lens;
+ const uint8_t **pks;
+ const uint8_t **sigs;
+ int *oks;
+
+ ms = tor_malloc(sizeof(uint8_t*)*n_checkable);
+ lens = tor_malloc(sizeof(size_t)*n_checkable);
+ pks = tor_malloc(sizeof(uint8_t*)*n_checkable);
+ sigs = tor_malloc(sizeof(uint8_t*)*n_checkable);
+ oks = okay_out ? okay_out : tor_malloc(sizeof(int)*n_checkable);
+
+ for (i = 0; i < n_checkable; ++i) {
+ ms[i] = checkable[i].msg;
+ lens[i] = checkable[i].len;
+ pks[i] = checkable[i].pubkey->pubkey;
+ sigs[i] = checkable[i].signature.sig;
+ oks[i] = 0;
+ }
+
+ ed25519_sign_open_batch_donna_fb(ms, lens, pks, sigs, n_checkable, oks);
+
+ res = 0;
+ for (i = 0; i < n_checkable; ++i) {
+ if (!oks[i])
+ --res;
+ }
+
+ tor_free(ms);
+ tor_free(lens);
+ tor_free(pks);
+ if (! okay_out)
+ tor_free(oks);
+#endif
+
+ return res;
+}
+
+/**
+ * Given a curve25519 keypair in <b>inp</b>, generate a corresponding
+ * ed25519 keypair in <b>out</b>, and set <b>signbit_out</b> to the
+ * sign bit of the X coordinate of the ed25519 key.
+ *
+ * NOTE THAT IT IS PROBABLY NOT SAFE TO USE THE GENERATED KEY FOR ANYTHING
+ * OUTSIDE OF WHAT'S PRESENTED IN PROPOSAL 228. In particular, it's probably
+ * not a great idea to use it to sign attacker-supplied anything.
+ */
+int
+ed25519_keypair_from_curve25519_keypair(ed25519_keypair_t *out,
+ int *signbit_out,
+ const curve25519_keypair_t *inp)
+{
+ const char string[] = "Derive high part of ed25519 key from curve25519 key";
+ ed25519_public_key_t pubkey_check;
+ SHA512_CTX ctx;
+ uint8_t sha512_output[64];
+
+ memcpy(out->seckey.seckey, inp->seckey.secret_key, 32);
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx, out->seckey.seckey, 32);
+ SHA512_Update(&ctx, string, sizeof(string));
+ SHA512_Final(sha512_output, &ctx);
+ memcpy(out->seckey.seckey + 32, sha512_output, 32);
+
+ ed25519_public_key_generate(&out->pubkey, &out->seckey);
+
+ *signbit_out = out->pubkey.pubkey[31] >> 7;
+
+ ed25519_public_key_from_curve25519_public_key(&pubkey_check, &inp->pubkey,
+ *signbit_out);
+
+ tor_assert(fast_memeq(pubkey_check.pubkey, out->pubkey.pubkey, 32));
+
+ memwipe(&pubkey_check, 0, sizeof(pubkey_check));
+ memwipe(&ctx, 0, sizeof(ctx));
+ memwipe(sha512_output, 0, sizeof(sha512_output));
+
+ return 0;
+}
+
+/**
+ * Given a curve25519 public key and sign bit of X coordinate of the ed25519
+ * public key, generate the corresponding ed25519 public key.
+ */
+int
+ed25519_public_key_from_curve25519_public_key(ed25519_public_key_t *pubkey,
+ const curve25519_public_key_t *pubkey_in,
+ int signbit)
+{
+ return ed25519_ref10_pubkey_from_curve25519_pubkey(pubkey->pubkey,
+ pubkey_in->public_key,
+ signbit);
+}
+
+/**
+ * Given an ed25519 keypair in <b>inp</b>, generate a corresponding
+ * ed25519 keypair in <b>out</b>, blinded by the corresponding 32-byte input
+ * in 'param'.
+ *
+ * Tor uses key blinding for the "next-generation" hidden services design:
+ * service descriptors are encrypted with a key derived from the service's
+ * long-term public key, and then signed with (and stored at a position
+ * indexed by) a short-term key derived by blinding the long-term keys.
+ */
+int
+ed25519_keypair_blind(ed25519_keypair_t *out,
+ const ed25519_keypair_t *inp,
+ const uint8_t *param)
+{
+ ed25519_public_key_t pubkey_check;
+
+ ed25519_ref10_blind_secret_key(out->seckey.seckey,
+ inp->seckey.seckey, param);
+
+ ed25519_public_blind(&pubkey_check, &inp->pubkey, param);
+ ed25519_public_key_generate(&out->pubkey, &out->seckey);
+
+ tor_assert(fast_memeq(pubkey_check.pubkey, out->pubkey.pubkey, 32));
+
+ memwipe(&pubkey_check, 0, sizeof(pubkey_check));
+
+ return 0;
+}
+
+/**
+ * Given an ed25519 public key in <b>inp</b>, generate a corresponding blinded
+ * public key in <b>out</b>, blinded with the 32-byte parameter in
+ * <b>param</b>. Return 0 on sucess, -1 on railure.
+ */
+int
+ed25519_public_blind(ed25519_public_key_t *out,
+ const ed25519_public_key_t *inp,
+ const uint8_t *param)
+{
+ ed25519_ref10_blind_public_key(out->pubkey, inp->pubkey, param);
+ return 0;
+}
+
+/**
+ * Store seckey unencrypted to <b>filename</b>, marking it with <b>tag</b>.
+ * Return 0 on success, -1 on failure.
+ */
+int
+ed25519_seckey_write_to_file(const ed25519_secret_key_t *seckey,
+ const char *filename,
+ const char *tag)
+{
+ return crypto_write_tagged_contents_to_file(filename,
+ "ed25519v1-secret",
+ tag,
+ seckey->seckey,
+ sizeof(seckey->seckey));
+}
+
+/**
+ * Read seckey unencrypted from <b>filename</b>, storing it into
+ * <b>seckey_out</b>. Set *<b>tag_out</> to the tag it was marked with.
+ * Return 0 on success, -1 on failure.
+ */
+int
+ed25519_seckey_read_from_file(ed25519_secret_key_t *seckey_out,
+ char **tag_out,
+ const char *filename)
+{
+ ssize_t len;
+
+ len = crypto_read_tagged_contents_from_file(filename, "ed25519v1-secret",
+ tag_out, seckey_out->seckey,
+ sizeof(seckey_out->seckey));
+ if (len != sizeof(seckey_out->seckey))
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Store pubkey unencrypted to <b>filename</b>, marking it with <b>tag</b>.
+ * Return 0 on success, -1 on failure.
+ */
+int
+ed25519_pubkey_write_to_file(const ed25519_public_key_t *pubkey,
+ const char *filename,
+ const char *tag)
+{
+ return crypto_write_tagged_contents_to_file(filename,
+ "ed25519v1-public",
+ tag,
+ pubkey->pubkey,
+ sizeof(pubkey->pubkey));
+}
+
+/**
+ * Store pubkey unencrypted to <b>filename</b>, marking it with <b>tag</b>.
+ * Return 0 on success, -1 on failure.
+ */
+int
+ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
+ char **tag_out,
+ const char *filename)
+{
+ ssize_t len;
+
+ len = crypto_read_tagged_contents_from_file(filename, "ed25519v1-public",
+ tag_out, pubkey_out->pubkey,
+ sizeof(pubkey_out->pubkey));
+ if (len != sizeof(pubkey_out->pubkey))
+ return -1;
+
+ return 0;
+}
+
diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h
new file mode 100644
index 0000000000..8c3663e0dd
--- /dev/null
+++ b/src/common/crypto_ed25519.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2012-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_CRYPTO_ED25519_H
+#define TOR_CRYPTO_ED25519_H
+
+#include "testsupport.h"
+#include "torint.h"
+
+#define ED25519_PUBKEY_LEN 32
+#define ED25519_SECKEY_LEN 64
+#define ED25519_SECKEY_SEED_LEN 32
+#define ED25519_SIG_LEN 64
+
+/** An Ed25519 signature. */
+typedef struct {
+ uint8_t sig[ED25519_SIG_LEN];
+} ed25519_signature_t;
+
+/** An Ed25519 public key */
+typedef struct {
+ uint8_t pubkey[ED25519_PUBKEY_LEN];
+} ed25519_public_key_t;
+
+/** An Ed25519 secret key */
+typedef struct {
+ /** Note that we store secret keys in an expanded format that doesn't match
+ * the format from standard ed25519. Ed25519 stores a 32-byte value k and
+ * expands it into a 64-byte H(k), using the first 32 bytes for a multiplier
+ * of the base point, and second 32 bytes as an input to a hash function
+ * for deriving r. But because we implement key blinding, we need to store
+ * keys in the 64-byte expanded form. */
+ uint8_t seckey[ED25519_SECKEY_LEN];
+} ed25519_secret_key_t;
+
+/** An Ed25519 keypair. */
+typedef struct {
+ ed25519_public_key_t pubkey;
+ ed25519_secret_key_t seckey;
+} ed25519_keypair_t;
+
+int ed25519_secret_key_generate(ed25519_secret_key_t *seckey_out,
+ int extra_strong);
+int ed25519_secret_key_from_seed(ed25519_secret_key_t *seckey_out,
+ const uint8_t *seed);
+
+int ed25519_public_key_generate(ed25519_public_key_t *pubkey_out,
+ const ed25519_secret_key_t *seckey);
+int ed25519_keypair_generate(ed25519_keypair_t *keypair_out, int extra_strong);
+int ed25519_sign(ed25519_signature_t *signature_out,
+ const uint8_t *msg, size_t len,
+ const ed25519_keypair_t *key);
+int ed25519_checksig(const ed25519_signature_t *signature,
+ const uint8_t *msg, size_t len,
+ const ed25519_public_key_t *pubkey);
+
+/**
+ * A collection of information necessary to check an Ed25519 signature. Used
+ * for batch verification.
+ */
+typedef struct {
+ /** The public key that supposedly generated the signature. */
+ ed25519_public_key_t *pubkey;
+ /** The signature to check. */
+ ed25519_signature_t signature;
+ /** The message that the signature is supposed to have been applied to. */
+ const uint8_t *msg;
+ /** The length of the message. */
+ size_t len;
+} ed25519_checkable_t;
+
+int ed25519_checksig_batch(int *okay_out,
+ const ed25519_checkable_t *checkable,
+ int n_checkable);
+
+int ed25519_keypair_from_curve25519_keypair(ed25519_keypair_t *out,
+ int *signbit_out,
+ const curve25519_keypair_t *inp);
+
+int ed25519_public_key_from_curve25519_public_key(ed25519_public_key_t *pubkey,
+ const curve25519_public_key_t *pubkey_in,
+ int signbit);
+int ed25519_keypair_blind(ed25519_keypair_t *out,
+ const ed25519_keypair_t *inp,
+ const uint8_t *param);
+int ed25519_public_blind(ed25519_public_key_t *out,
+ const ed25519_public_key_t *inp,
+ const uint8_t *param);
+
+#define ED25519_BASE64_LEN 43
+
+int ed25519_public_from_base64(ed25519_public_key_t *pkey,
+ const char *input);
+int ed25519_public_to_base64(char *output,
+ const ed25519_public_key_t *pkey);
+
+/* XXXX read encrypted, write encrypted. */
+
+int ed25519_seckey_write_to_file(const ed25519_secret_key_t *seckey,
+ const char *filename,
+ const char *tag);
+int ed25519_seckey_read_from_file(ed25519_secret_key_t *seckey_out,
+ char **tag_out,
+ const char *filename);
+int ed25519_pubkey_write_to_file(const ed25519_public_key_t *pubkey,
+ const char *filename,
+ const char *tag);
+int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
+ char **tag_out,
+ const char *filename);
+
+#endif
+
diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c
index be669c8d2b..63dd391914 100644
--- a/src/common/crypto_format.c
+++ b/src/common/crypto_format.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, The Tor Project, Inc. */
+/* Copyright (c) 2012-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* Formatting and parsing code for crypto-related data structures. */
@@ -9,6 +9,7 @@
#endif
#include "crypto.h"
#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
#include "util.h"
#include "torlog.h"
@@ -43,3 +44,24 @@ curve25519_public_from_base64(curve25519_public_key_t *pkey,
}
}
+/** Try to decode the string <b>input</b> into an ed25519 public key. On
+ * success, store the value in <b>pkey</b> and return 0. Otherwise return
+ * -1. */
+int
+ed25519_public_from_base64(ed25519_public_key_t *pkey,
+ const char *input)
+{
+ return digest256_from_base64((char*)pkey->pubkey, input);
+}
+
+/** Encode the public key <b>pkey</b> into the buffer at <b>output</b>,
+ * which must have space for ED25519_BASE64_LEN bytes of encoded key,
+ * plus one byte for a terminating NUL. Return 0 on success, -1 on failure.
+ */
+int
+ed25519_public_to_base64(char *output,
+ const ed25519_public_key_t *pkey)
+{
+ return digest256_to_base64(output, (const char *)pkey->pubkey);
+}
+
diff --git a/src/common/crypto_pwbox.c b/src/common/crypto_pwbox.c
new file mode 100644
index 0000000000..b866c7ef39
--- /dev/null
+++ b/src/common/crypto_pwbox.c
@@ -0,0 +1,187 @@
+
+#include "crypto.h"
+#include "crypto_s2k.h"
+#include "crypto_pwbox.h"
+#include "di_ops.h"
+#include "util.h"
+#include "pwbox.h"
+
+/* 8 bytes "TORBOX00"
+ 1 byte: header len (H)
+ H bytes: header, denoting secret key algorithm.
+ 16 bytes: IV
+ Round up to multiple of 128 bytes, then encrypt:
+ 4 bytes: data len
+ data
+ zeros
+ 32 bytes: HMAC-SHA256 of all previous bytes.
+*/
+
+#define MAX_OVERHEAD (S2K_MAXLEN + 8 + 1 + 32 + CIPHER_IV_LEN)
+
+/**
+ * Make an authenticated passphrase-encrypted blob to encode the
+ * <b>input_len</b> bytes in <b>input</b> using the passphrase
+ * <b>secret</b> of <b>secret_len</b> bytes. Allocate a new chunk of memory
+ * to hold the encrypted data, and store a pointer to that memory in
+ * *<b>out</b>, and its size in <b>outlen_out</b>. Use <b>s2k_flags</b> as an
+ * argument to the passphrase-hashing function.
+ */
+int
+crypto_pwbox(uint8_t **out, size_t *outlen_out,
+ const uint8_t *input, size_t input_len,
+ const char *secret, size_t secret_len,
+ unsigned s2k_flags)
+{
+ uint8_t *result = NULL, *encrypted_portion;
+ size_t encrypted_len = 128 * CEIL_DIV(input_len+4, 128);
+ ssize_t result_len;
+ int spec_len;
+ uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
+ pwbox_encoded_t *enc = NULL;
+ ssize_t enc_len;
+
+ crypto_cipher_t *cipher;
+ int rv;
+
+ enc = pwbox_encoded_new();
+
+ pwbox_encoded_setlen_skey_header(enc, S2K_MAXLEN);
+
+ spec_len = secret_to_key_make_specifier(
+ pwbox_encoded_getarray_skey_header(enc),
+ S2K_MAXLEN,
+ s2k_flags);
+ if (spec_len < 0 || spec_len > S2K_MAXLEN)
+ goto err;
+ pwbox_encoded_setlen_skey_header(enc, spec_len);
+ enc->header_len = spec_len;
+
+ crypto_rand((char*)enc->iv, sizeof(enc->iv));
+
+ pwbox_encoded_setlen_data(enc, encrypted_len);
+ encrypted_portion = pwbox_encoded_getarray_data(enc);
+
+ set_uint32(encrypted_portion, htonl((uint32_t)input_len));
+ memcpy(encrypted_portion+4, input, input_len);
+
+ /* Now that all the data is in position, derive some keys, encrypt, and
+ * digest */
+ if (secret_to_key_derivekey(keys, sizeof(keys),
+ pwbox_encoded_getarray_skey_header(enc),
+ spec_len,
+ secret, secret_len) < 0)
+ goto err;
+
+ cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
+ crypto_cipher_crypt_inplace(cipher, (char*)encrypted_portion, encrypted_len);
+ crypto_cipher_free(cipher);
+
+ result_len = pwbox_encoded_encoded_len(enc);
+ if (result_len < 0)
+ goto err;
+ result = tor_malloc(result_len);
+ enc_len = pwbox_encoded_encode(result, result_len, enc);
+ if (enc_len < 0)
+ goto err;
+ tor_assert(enc_len == result_len);
+
+ crypto_hmac_sha256((char*) result + result_len - 32,
+ (const char*)keys + CIPHER_KEY_LEN,
+ DIGEST256_LEN,
+ (const char*)result,
+ result_len - 32);
+
+ *out = result;
+ *outlen_out = result_len;
+ rv = 0;
+ goto out;
+
+ err:
+ tor_free(result);
+ rv = -1;
+
+ out:
+ pwbox_encoded_free(enc);
+ memwipe(keys, 0, sizeof(keys));
+ return rv;
+}
+
+/**
+ * Try to decrypt the passphrase-encrypted blob of <b>input_len</b> bytes in
+ * <b>input</b> using the passphrase <b>secret</b> of <b>secret_len</b> bytes.
+ * On success, return 0 and allocate a new chunk of memory to hold the
+ * decrypted data, and store a pointer to that memory in *<b>out</b>, and its
+ * size in <b>outlen_out</b>. On failure, return UNPWBOX_BAD_SECRET if
+ * the passphrase might have been wrong, and UNPWBOX_CORRUPT if the object is
+ * definitely corrupt.
+ */
+int
+crypto_unpwbox(uint8_t **out, size_t *outlen_out,
+ const uint8_t *inp, size_t input_len,
+ const char *secret, size_t secret_len)
+{
+ uint8_t *result = NULL;
+ const uint8_t *encrypted;
+ uint8_t keys[CIPHER_KEY_LEN + DIGEST256_LEN];
+ uint8_t hmac[DIGEST256_LEN];
+ uint32_t result_len;
+ size_t encrypted_len;
+ crypto_cipher_t *cipher = NULL;
+ int rv = UNPWBOX_CORRUPTED;
+ ssize_t got_len;
+
+ pwbox_encoded_t *enc = NULL;
+
+ got_len = pwbox_encoded_parse(&enc, inp, input_len);
+ if (got_len < 0 || (size_t)got_len != input_len)
+ goto err;
+
+ /* Now derive the keys and check the hmac. */
+ if (secret_to_key_derivekey(keys, sizeof(keys),
+ pwbox_encoded_getarray_skey_header(enc),
+ pwbox_encoded_getlen_skey_header(enc),
+ secret, secret_len) < 0)
+ goto err;
+
+ crypto_hmac_sha256((char *)hmac,
+ (const char*)keys + CIPHER_KEY_LEN, DIGEST256_LEN,
+ (const char*)inp, input_len - DIGEST256_LEN);
+
+ if (tor_memneq(hmac, enc->hmac, DIGEST256_LEN)) {
+ rv = UNPWBOX_BAD_SECRET;
+ goto err;
+ }
+
+ /* How long is the plaintext? */
+ encrypted = pwbox_encoded_getarray_data(enc);
+ encrypted_len = pwbox_encoded_getlen_data(enc);
+ if (encrypted_len < 4)
+ goto err;
+
+ cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
+ crypto_cipher_decrypt(cipher, (char*)&result_len, (char*)encrypted, 4);
+ result_len = ntohl(result_len);
+ if (encrypted_len < result_len + 4)
+ goto err;
+
+ /* Allocate a buffer and decrypt */
+ result = tor_malloc_zero(result_len);
+ crypto_cipher_decrypt(cipher, (char*)result, (char*)encrypted+4, result_len);
+
+ *out = result;
+ *outlen_out = result_len;
+
+ rv = UNPWBOX_OKAY;
+ goto out;
+
+ err:
+ tor_free(result);
+
+ out:
+ crypto_cipher_free(cipher);
+ pwbox_encoded_free(enc);
+ memwipe(keys, 0, sizeof(keys));
+ return rv;
+}
+
diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h
new file mode 100644
index 0000000000..aadd477078
--- /dev/null
+++ b/src/common/crypto_pwbox.h
@@ -0,0 +1,20 @@
+#ifndef CRYPTO_PWBOX_H_INCLUDED_
+#define CRYPTO_PWBOX_H_INCLUDED_
+
+#include "torint.h"
+
+#define UNPWBOX_OKAY 0
+#define UNPWBOX_BAD_SECRET -1
+#define UNPWBOX_CORRUPTED -2
+
+int crypto_pwbox(uint8_t **out, size_t *outlen_out,
+ const uint8_t *inp, size_t input_len,
+ const char *secret, size_t secret_len,
+ unsigned s2k_flags);
+
+int crypto_unpwbox(uint8_t **out, size_t *outlen_out,
+ const uint8_t *inp, size_t input_len,
+ const char *secret, size_t secret_len);
+
+#endif
+
diff --git a/src/common/crypto_s2k.c b/src/common/crypto_s2k.c
new file mode 100644
index 0000000000..6d9ee497ab
--- /dev/null
+++ b/src/common/crypto_s2k.c
@@ -0,0 +1,460 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CRYPTO_S2K_PRIVATE
+
+#include "crypto.h"
+#include "util.h"
+#include "compat.h"
+#include "crypto_s2k.h"
+
+#include <openssl/evp.h>
+
+#ifdef HAVE_LIBSCRYPT_H
+#define HAVE_SCRYPT
+#include <libscrypt.h>
+#endif
+
+/* Encoded secrets take the form:
+
+ u8 type;
+ u8 salt_and_parameters[depends on type];
+ u8 key[depends on type];
+
+ As a special case, if the encoded secret is exactly 29 bytes long,
+ type 0 is understood.
+
+ Recognized types are:
+ 00 -- RFC2440. salt_and_parameters is 9 bytes. key is 20 bytes.
+ salt_and_parameters is 8 bytes random salt,
+ 1 byte iteration info.
+ 01 -- PKBDF2_SHA1. salt_and_parameters is 17 bytes. key is 20 bytes.
+ salt_and_parameters is 16 bytes random salt,
+ 1 byte iteration info.
+ 02 -- SCRYPT_SALSA208_SHA256. salt_and_parameters is 18 bytes. key is
+ 32 bytes.
+ salt_and_parameters is 18 bytes random salt, 2 bytes iteration
+ info.
+*/
+
+#define S2K_TYPE_RFC2440 0
+#define S2K_TYPE_PBKDF2 1
+#define S2K_TYPE_SCRYPT 2
+
+#define PBKDF2_SPEC_LEN 17
+#define PBKDF2_KEY_LEN 20
+
+#define SCRYPT_SPEC_LEN 18
+#define SCRYPT_KEY_LEN 32
+
+/** Given an algorithm ID (one of S2K_TYPE_*), return the length of the
+ * specifier part of it, without the prefix type byte. */
+static int
+secret_to_key_spec_len(uint8_t type)
+{
+ switch (type) {
+ case S2K_TYPE_RFC2440:
+ return S2K_RFC2440_SPECIFIER_LEN;
+ case S2K_TYPE_PBKDF2:
+ return PBKDF2_SPEC_LEN;
+ case S2K_TYPE_SCRYPT:
+ return SCRYPT_SPEC_LEN;
+ default:
+ return -1;
+ }
+}
+
+/** Given an algorithm ID (one of S2K_TYPE_*), return the length of the
+ * its preferred output. */
+static int
+secret_to_key_key_len(uint8_t type)
+{
+ switch (type) {
+ case S2K_TYPE_RFC2440:
+ return DIGEST_LEN;
+ case S2K_TYPE_PBKDF2:
+ return DIGEST_LEN;
+ case S2K_TYPE_SCRYPT:
+ return DIGEST256_LEN;
+ default:
+ return -1;
+ }
+}
+
+/** Given a specifier in <b>spec_and_key</b> of length
+ * <b>spec_and_key_len</b>, along with its prefix algorithm ID byte, and along
+ * with a key if <b>key_included</b> is true, check whether the whole
+ * specifier-and-key is of valid length, and return the algorithm type if it
+ * is. Set *<b>legacy_out</b> to 1 iff this is a legacy password hash or
+ * legacy specifier. Return an error code on failure.
+ */
+static int
+secret_to_key_get_type(const uint8_t *spec_and_key, size_t spec_and_key_len,
+ int key_included, int *legacy_out)
+{
+ size_t legacy_len = S2K_RFC2440_SPECIFIER_LEN;
+ uint8_t type;
+ int total_len;
+
+ if (key_included)
+ legacy_len += DIGEST_LEN;
+
+ if (spec_and_key_len == legacy_len) {
+ *legacy_out = 1;
+ return S2K_TYPE_RFC2440;
+ }
+
+ *legacy_out = 0;
+ if (spec_and_key_len == 0)
+ return S2K_BAD_LEN;
+
+ type = spec_and_key[0];
+ total_len = secret_to_key_spec_len(type);
+ if (total_len < 0)
+ return S2K_BAD_ALGORITHM;
+ if (key_included) {
+ int keylen = secret_to_key_key_len(type);
+ if (keylen < 0)
+ return S2K_BAD_ALGORITHM;
+ total_len += keylen;
+ }
+
+ if ((size_t)total_len + 1 == spec_and_key_len)
+ return type;
+ else
+ return S2K_BAD_LEN;
+}
+
+/**
+ * Write a new random s2k specifier of type <b>type</b>, without prefixing
+ * type byte, to <b>spec_out</b>, which must have enough room. May adjust
+ * parameter choice based on <b>flags</b>.
+ */
+static int
+make_specifier(uint8_t *spec_out, uint8_t type, unsigned flags)
+{
+ int speclen = secret_to_key_spec_len(type);
+ if (speclen < 0)
+ return S2K_BAD_ALGORITHM;
+
+ crypto_rand((char*)spec_out, speclen);
+ switch (type) {
+ case S2K_TYPE_RFC2440:
+ /* Hash 64 k of data. */
+ spec_out[S2K_RFC2440_SPECIFIER_LEN-1] = 96;
+ break;
+ case S2K_TYPE_PBKDF2:
+ /* 131 K iterations */
+ spec_out[PBKDF2_SPEC_LEN-1] = 17;
+ break;
+ case S2K_TYPE_SCRYPT:
+ if (flags & S2K_FLAG_LOW_MEM) {
+ /* N = 1<<12 */
+ spec_out[SCRYPT_SPEC_LEN-2] = 12;
+ } else {
+ /* N = 1<<15 */
+ spec_out[SCRYPT_SPEC_LEN-2] = 15;
+ }
+ /* r = 8; p = 2. */
+ spec_out[SCRYPT_SPEC_LEN-1] = (3u << 4) | (1u << 0);
+ break;
+ default:
+ tor_fragile_assert();
+ return S2K_BAD_ALGORITHM;
+ }
+
+ return speclen;
+}
+
+/** Implement RFC2440-style iterated-salted S2K conversion: convert the
+ * <b>secret_len</b>-byte <b>secret</b> into a <b>key_out_len</b> byte
+ * <b>key_out</b>. As in RFC2440, the first 8 bytes of s2k_specifier
+ * are a salt; the 9th byte describes how much iteration to do.
+ * If <b>key_out_len</b> &gt; DIGEST_LEN, use HDKF to expand the result.
+ */
+void
+secret_to_key_rfc2440(char *key_out, size_t key_out_len, const char *secret,
+ size_t secret_len, const char *s2k_specifier)
+{
+ crypto_digest_t *d;
+ uint8_t c;
+ size_t count, tmplen;
+ char *tmp;
+ uint8_t buf[DIGEST_LEN];
+ tor_assert(key_out_len < SIZE_T_CEILING);
+
+#define EXPBIAS 6
+ c = s2k_specifier[8];
+ count = ((uint32_t)16 + (c & 15)) << ((c >> 4) + EXPBIAS);
+#undef EXPBIAS
+
+ d = crypto_digest_new();
+ tmplen = 8+secret_len;
+ tmp = tor_malloc(tmplen);
+ memcpy(tmp,s2k_specifier,8);
+ memcpy(tmp+8,secret,secret_len);
+ secret_len += 8;
+ while (count) {
+ if (count >= secret_len) {
+ crypto_digest_add_bytes(d, tmp, secret_len);
+ count -= secret_len;
+ } else {
+ crypto_digest_add_bytes(d, tmp, count);
+ count = 0;
+ }
+ }
+ crypto_digest_get_digest(d, (char*)buf, sizeof(buf));
+
+ if (key_out_len <= sizeof(buf)) {
+ memcpy(key_out, buf, key_out_len);
+ } else {
+ crypto_expand_key_material_rfc5869_sha256(buf, DIGEST_LEN,
+ (const uint8_t*)s2k_specifier, 8,
+ (const uint8_t*)"EXPAND", 6,
+ (uint8_t*)key_out, key_out_len);
+ }
+ memwipe(tmp, 0, tmplen);
+ memwipe(buf, 0, sizeof(buf));
+ tor_free(tmp);
+ crypto_digest_free(d);
+}
+
+/**
+ * Helper: given a valid specifier without prefix type byte in <b>spec</b>,
+ * whose length must be correct, and given a secret passphrase <b>secret</b>
+ * of length <b>secret_len</b>, compute the key and store it into
+ * <b>key_out</b>, which must have enough room for secret_to_key_key_len(type)
+ * bytes. Return the number of bytes written on success and an error code
+ * on failure.
+ */
+STATIC int
+secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
+ const uint8_t *spec, size_t spec_len,
+ const char *secret, size_t secret_len,
+ int type)
+{
+ int rv;
+ if (key_out_len > INT_MAX)
+ return S2K_BAD_LEN;
+
+ switch (type) {
+ case S2K_TYPE_RFC2440:
+ secret_to_key_rfc2440((char*)key_out, key_out_len, secret, secret_len,
+ (const char*)spec);
+ return (int)key_out_len;
+
+ case S2K_TYPE_PBKDF2: {
+ uint8_t log_iters;
+ if (spec_len < 1 || secret_len > INT_MAX || spec_len > INT_MAX)
+ return S2K_BAD_LEN;
+ log_iters = spec[spec_len-1];
+ if (log_iters > 31)
+ return S2K_BAD_PARAMS;
+ rv = PKCS5_PBKDF2_HMAC_SHA1(secret, (int)secret_len,
+ spec, (int)spec_len-1,
+ (1<<log_iters),
+ (int)key_out_len, key_out);
+ if (rv < 0)
+ return S2K_FAILED;
+ return (int)key_out_len;
+ }
+
+ case S2K_TYPE_SCRYPT: {
+#ifdef HAVE_SCRYPT
+ uint8_t log_N, log_r, log_p;
+ uint64_t N;
+ uint32_t r, p;
+ if (spec_len < 2)
+ return S2K_BAD_LEN;
+ log_N = spec[spec_len-2];
+ log_r = (spec[spec_len-1]) >> 4;
+ log_p = (spec[spec_len-1]) & 15;
+ if (log_N > 63)
+ return S2K_BAD_PARAMS;
+ N = ((uint64_t)1) << log_N;
+ r = 1u << log_r;
+ p = 1u << log_p;
+ rv = libscrypt_scrypt((const uint8_t*)secret, secret_len,
+ spec, spec_len-2, N, r, p, key_out, key_out_len);
+ if (rv != 0)
+ return S2K_FAILED;
+ return (int)key_out_len;
+#else
+ return S2K_NO_SCRYPT_SUPPORT;
+#endif
+ }
+ default:
+ return S2K_BAD_ALGORITHM;
+ }
+}
+
+/**
+ * Given a specifier previously constructed with secret_to_key_make_specifier
+ * in <b>spec</b> of length <b>spec_len</b>, and a secret password in
+ * <b>secret</b> of length <b>secret_len</b>, generate <b>key_out_len</b>
+ * bytes of cryptographic material in <b>key_out</b>. The native output of
+ * the secret-to-key function will be truncated if key_out_len is short, and
+ * expanded with HKDF if key_out_len is long. Returns S2K_OKAY on success,
+ * and an error code on failure.
+ */
+int
+secret_to_key_derivekey(uint8_t *key_out, size_t key_out_len,
+ const uint8_t *spec, size_t spec_len,
+ const char *secret, size_t secret_len)
+{
+ int legacy_format = 0;
+ int type = secret_to_key_get_type(spec, spec_len, 0, &legacy_format);
+ int r;
+
+ if (type < 0)
+ return type;
+#ifndef HAVE_SCRYPT
+ if (type == S2K_TYPE_SCRYPT)
+ return S2K_NO_SCRYPT_SUPPORT;
+ #endif
+
+ if (! legacy_format) {
+ ++spec;
+ --spec_len;
+ }
+
+ r = secret_to_key_compute_key(key_out, key_out_len, spec, spec_len,
+ secret, secret_len, type);
+ if (r < 0)
+ return r;
+ else
+ return S2K_OKAY;
+}
+
+/**
+ * Construct a new s2k algorithm specifier and salt in <b>buf</b>, according
+ * to the bitwise-or of some S2K_FLAG_* options in <b>flags</b>. Up to
+ * <b>buf_len</b> bytes of storage may be used in <b>buf</b>. Return the
+ * number of bytes used on success and an error code on failure.
+ */
+int
+secret_to_key_make_specifier(uint8_t *buf, size_t buf_len, unsigned flags)
+{
+ int rv;
+ int spec_len;
+#ifdef HAVE_SCRYPT
+ uint8_t type = S2K_TYPE_SCRYPT;
+#else
+ uint8_t type = S2K_TYPE_RFC2440;
+#endif
+
+ if (flags & S2K_FLAG_NO_SCRYPT)
+ type = S2K_TYPE_RFC2440;
+ if (flags & S2K_FLAG_USE_PBKDF2)
+ type = S2K_TYPE_PBKDF2;
+
+ spec_len = secret_to_key_spec_len(type);
+
+ if ((int)buf_len < spec_len + 1)
+ return S2K_TRUNCATED;
+
+ buf[0] = type;
+ rv = make_specifier(buf+1, type, flags);
+ if (rv < 0)
+ return rv;
+ else
+ return rv + 1;
+}
+
+/**
+ * Hash a passphrase from <b>secret</b> of length <b>secret_len</b>, according
+ * to the bitwise-or of some S2K_FLAG_* options in <b>flags</b>, and store the
+ * hash along with salt and hashing parameters into <b>buf</b>. Up to
+ * <b>buf_len</b> bytes of storage may be used in <b>buf</b>. Set
+ * *<b>len_out</b> to the number of bytes used and return S2K_OKAY on success;
+ * and return an error code on failure.
+ */
+int
+secret_to_key_new(uint8_t *buf,
+ size_t buf_len,
+ size_t *len_out,
+ const char *secret, size_t secret_len,
+ unsigned flags)
+{
+ int key_len;
+ int spec_len;
+ int type;
+ int rv;
+
+ spec_len = secret_to_key_make_specifier(buf, buf_len, flags);
+
+ if (spec_len < 0)
+ return spec_len;
+
+ type = buf[0];
+ key_len = secret_to_key_key_len(type);
+
+ if (key_len < 0)
+ return key_len;
+
+ if ((int)buf_len < key_len + spec_len)
+ return S2K_TRUNCATED;
+
+ rv = secret_to_key_compute_key(buf + spec_len, key_len,
+ buf + 1, spec_len-1,
+ secret, secret_len, type);
+ if (rv < 0)
+ return rv;
+
+ *len_out = spec_len + key_len;
+
+ return S2K_OKAY;
+}
+
+/**
+ * Given a hashed passphrase in <b>spec_and_key</b> of length
+ * <b>spec_and_key_len</b> as generated by secret_to_key_new(), verify whether
+ * it is a hash of the passphrase <b>secret</b> of length <b>secret_len</b>.
+ * Return S2K_OKAY on a match, S2K_BAD_SECRET on a well-formed hash that
+ * doesn't match this secret, and another error code on other errors.
+ */
+int
+secret_to_key_check(const uint8_t *spec_and_key, size_t spec_and_key_len,
+ const char *secret, size_t secret_len)
+{
+ int is_legacy = 0;
+ int type = secret_to_key_get_type(spec_and_key, spec_and_key_len,
+ 1, &is_legacy);
+ uint8_t buf[32];
+ int spec_len;
+ int key_len;
+ int rv;
+
+ if (type < 0)
+ return type;
+
+ if (! is_legacy) {
+ spec_and_key++;
+ spec_and_key_len--;
+ }
+
+ spec_len = secret_to_key_spec_len(type);
+ key_len = secret_to_key_key_len(type);
+ tor_assert(spec_len > 0);
+ tor_assert(key_len > 0);
+ tor_assert(key_len <= (int) sizeof(buf));
+ tor_assert((int)spec_and_key_len == spec_len + key_len);
+ rv = secret_to_key_compute_key(buf, key_len,
+ spec_and_key, spec_len,
+ secret, secret_len, type);
+ if (rv < 0)
+ goto done;
+
+ if (tor_memeq(buf, spec_and_key + spec_len, key_len))
+ rv = S2K_OKAY;
+ else
+ rv = S2K_BAD_SECRET;
+
+ done:
+ memwipe(buf, 0, sizeof(buf));
+ return rv;
+}
+
diff --git a/src/common/crypto_s2k.h b/src/common/crypto_s2k.h
new file mode 100644
index 0000000000..a33dc96e46
--- /dev/null
+++ b/src/common/crypto_s2k.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_CRYPTO_S2K_H_INCLUDED
+#define TOR_CRYPTO_S2K_H_INCLUDED
+
+#include <stdio.h>
+#include "torint.h"
+
+/** Length of RFC2440-style S2K specifier: the first 8 bytes are a salt, the
+ * 9th describes how much iteration to do. */
+#define S2K_RFC2440_SPECIFIER_LEN 9
+void secret_to_key_rfc2440(
+ char *key_out, size_t key_out_len, const char *secret,
+ size_t secret_len, const char *s2k_specifier);
+
+/** Flag for secret-to-key function: do not use scrypt. */
+#define S2K_FLAG_NO_SCRYPT (1u<<0)
+/** Flag for secret-to-key functions: if using a memory-tuned s2k function,
+ * assume that we have limited memory. */
+#define S2K_FLAG_LOW_MEM (1u<<1)
+/** Flag for secret-to-key functions: force use of pbkdf2. Without this, we
+ * default to scrypt, then RFC2440. */
+#define S2K_FLAG_USE_PBKDF2 (1u<<2)
+
+/** Maximum possible output length from secret_to_key_new. */
+#define S2K_MAXLEN 64
+
+/** Error code from secret-to-key functions: all is well */
+#define S2K_OKAY 0
+/** Error code from secret-to-key functions: generic failure */
+#define S2K_FAILED -1
+/** Error code from secret-to-key functions: provided secret didn't match */
+#define S2K_BAD_SECRET -2
+/** Error code from secret-to-key functions: didn't recognize the algorithm */
+#define S2K_BAD_ALGORITHM -3
+/** Error code from secret-to-key functions: specifier wasn't valid */
+#define S2K_BAD_PARAMS -4
+/** Error code from secret-to-key functions: compiled without scrypt */
+#define S2K_NO_SCRYPT_SUPPORT -5
+/** Error code from secret-to-key functions: not enough space to write output.
+ */
+#define S2K_TRUNCATED -6
+/** Error code from secret-to-key functions: Wrong length for specifier. */
+#define S2K_BAD_LEN -7
+
+int secret_to_key_new(uint8_t *buf,
+ size_t buf_len,
+ size_t *len_out,
+ const char *secret, size_t secret_len,
+ unsigned flags);
+
+int secret_to_key_make_specifier(uint8_t *buf, size_t buf_len, unsigned flags);
+
+int secret_to_key_check(const uint8_t *spec_and_key, size_t spec_and_key_len,
+ const char *secret, size_t secret_len);
+
+int secret_to_key_derivekey(uint8_t *key_out, size_t key_out_len,
+ const uint8_t *spec, size_t spec_len,
+ const char *secret, size_t secret_len);
+
+#ifdef CRYPTO_S2K_PRIVATE
+STATIC int secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
+ const uint8_t *spec, size_t spec_len,
+ const char *secret, size_t secret_len,
+ int type);
+#endif
+
+#endif
+
diff --git a/src/common/di_ops.c b/src/common/di_ops.c
index 14a1443400..0dcd6924e7 100644
--- a/src/common/di_ops.c
+++ b/src/common/di_ops.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Tor Project, Inc. */
+/* Copyright (c) 2011-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -130,6 +130,7 @@ tor_memeq(const void *a, const void *b, size_t sz)
* 1 & ((any_difference - 1) >> 8) == 0
*/
+ /*coverity[overflow]*/
return 1 & ((any_difference - 1) >> 8);
}
@@ -217,6 +218,7 @@ safe_mem_is_zero(const void *mem, size_t sz)
total |= *ptr++;
}
+ /*coverity[overflow]*/
return 1 & ((total - 1) >> 8);
}
diff --git a/src/common/di_ops.h b/src/common/di_ops.h
index d93534b69b..935f93fc1a 100644
--- a/src/common/di_ops.h
+++ b/src/common/di_ops.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/common/get_mozilla_ciphers.py b/src/common/get_mozilla_ciphers.py
deleted file mode 100644
index c7e9a84a0e..0000000000
--- 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 814786b776..6441596199 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -16,7 +16,7 @@ EXTRA_DIST+= \
src/common/Makefile.nmake
#CFLAGS = -Wall -Wpointer-arith -O2
-AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common
+AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel
if USE_OPENBSD_MALLOC
libor_extra_source=src/ext/OpenBSD_malloc_Linux.c
@@ -24,6 +24,14 @@ 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
@@ -44,9 +52,7 @@ LIBDONNA=
endif
endif
-if CURVE25519_ENABLED
-libcrypto_extra_source=src/common/crypto_curve25519.c
-endif
+LIBDONNA += $(LIBED25519_REF10)
LIBOR_A_SOURCES = \
src/common/address.c \
@@ -56,22 +62,30 @@ LIBOR_A_SOURCES = \
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 \
+ src/common/util_process.c \
src/common/sandbox.c \
- $(libor_extra_source)
+ src/ext/csiphash.c \
+ src/ext/trunnel/trunnel.c \
+ $(libor_extra_source) \
+ $(libor_mempool_source)
LIBOR_CRYPTO_A_SOURCES = \
src/common/aes.c \
src/common/crypto.c \
+ src/common/crypto_pwbox.c \
+ src/common/crypto_s2k.c \
src/common/crypto_format.c \
src/common/torgzip.c \
src/common/tortls.c \
- $(libcrypto_extra_source)
+ src/trunnel/pwbox.c \
+ src/common/crypto_curve25519.c \
+ src/common/crypto_ed25519.c
-LIBOR_EVENT_A_SOURCES = src/common/compat_libevent.c
+LIBOR_EVENT_A_SOURCES = \
+ src/common/compat_libevent.c \
+ src/common/procmon.c
src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES)
src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
@@ -99,9 +113,12 @@ COMMONHEADERS = \
src/common/container.h \
src/common/crypto.h \
src/common/crypto_curve25519.h \
+ src/common/crypto_ed25519.h \
+ src/common/crypto_pwbox.h \
+ src/common/crypto_s2k.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 \
@@ -109,7 +126,9 @@ COMMONHEADERS = \
src/common/torint.h \
src/common/torlog.h \
src/common/tortls.h \
- src/common/util.h
+ src/common/util.h \
+ src/common/util_process.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 0000000000..cf47c73809
--- /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 9c67de320b..ad0da7da6b 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -117,15 +117,33 @@ static int syslog_count = 0;
/** Represents a log message that we are going to send to callback-driven
* loggers once we can do so in a non-reentrant way. */
-typedef struct pending_cb_message_t {
+typedef struct pending_log_message_t {
int severity; /**< The severity of the message */
log_domain_mask_t domain; /**< The domain of the message */
+ char *fullmsg; /**< The message, with all decorations */
char *msg; /**< The content of the message */
-} pending_cb_message_t;
+} pending_log_message_t;
/** Log messages waiting to be replayed onto callback-based logs */
static smartlist_t *pending_cb_messages = NULL;
+/** Log messages waiting to be replayed once the logging system is initialized.
+ */
+static smartlist_t *pending_startup_messages = NULL;
+
+/** Number of bytes of messages queued in pending_startup_messages. (This is
+ * the length of the messages, not the number of bytes used to store
+ * them.) */
+static size_t pending_startup_messages_len;
+
+/** True iff we should store messages while waiting for the logs to get
+ * configured. */
+static int queue_startup_messages = 1;
+
+/** Don't store more than this many bytes of messages while waiting for the
+ * logs to get configured. */
+#define MAX_STARTUP_MSG_LEN (1<<16)
+
/** Lock the log_mutex to prevent others from changing the logfile_t list */
#define LOCK_LOGS() STMT_BEGIN \
tor_mutex_acquire(&log_mutex); \
@@ -147,9 +165,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,13 +347,109 @@ format_msg(char *buf, size_t buf_len,
return end_of_prefix;
}
+/* Create a new pending_log_message_t with appropriate values */
+static pending_log_message_t *
+pending_log_message_new(int severity, log_domain_mask_t domain,
+ const char *fullmsg, const char *shortmsg)
+{
+ pending_log_message_t *m = tor_malloc(sizeof(pending_log_message_t));
+ m->severity = severity;
+ m->domain = domain;
+ m->fullmsg = fullmsg ? tor_strdup(fullmsg) : NULL;
+ m->msg = tor_strdup(shortmsg);
+ return m;
+}
+
+/** Release all storage held by <b>msg</b>. */
+static void
+pending_log_message_free(pending_log_message_t *msg)
+{
+ if (!msg)
+ return;
+ tor_free(msg->msg);
+ tor_free(msg->fullmsg);
+ tor_free(msg);
+}
+
+/** Return true iff <b>lf</b> would like to receive a message with the
+ * specified <b>severity</b> in the specified <b>domain</b>.
+ */
+static INLINE int
+logfile_wants_message(const logfile_t *lf, int severity,
+ log_domain_mask_t domain)
+{
+ if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
+ return 0;
+ }
+ if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) {
+ return 0;
+ }
+ if (lf->seems_dead) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/** Send a message to <b>lf</b>. The full message, with time prefix and
+ * severity, is in <b>buf</b>. The message itself is in
+ * <b>msg_after_prefix</b>. If <b>callbacks_deferred</b> points to true, then
+ * we already deferred this message for pending callbacks and don't need to do
+ * it again. Otherwise, if we need to do it, do it, and set
+ * <b>callbacks_deferred</b> to 1. */
+static INLINE void
+logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len,
+ const char *msg_after_prefix, log_domain_mask_t domain,
+ int severity, int *callbacks_deferred)
+{
+
+ if (lf->is_syslog) {
+#ifdef HAVE_SYSLOG_H
+#ifdef MAXLINE
+ /* Some syslog implementations have limits on the length of what you can
+ * pass them, and some very old ones do not detect overflow so well.
+ * Regrettably, they call their maximum line length MAXLINE. */
+#if MAXLINE < 64
+#warn "MAXLINE is a very low number; it might not be from syslog.h after all"
+#endif
+ char *m = msg_after_prefix;
+ if (msg_len >= MAXLINE)
+ m = tor_strndup(msg_after_prefix, MAXLINE-1);
+ syslog(severity, "%s", m);
+ if (m != msg_after_prefix) {
+ tor_free(m);
+ }
+#else
+ /* We have syslog but not MAXLINE. That's promising! */
+ syslog(severity, "%s", msg_after_prefix);
+#endif
+#endif
+ } else if (lf->callback) {
+ if (domain & LD_NOCB) {
+ if (!*callbacks_deferred && pending_cb_messages) {
+ smartlist_add(pending_cb_messages,
+ pending_log_message_new(severity,domain,NULL,msg_after_prefix));
+ *callbacks_deferred = 1;
+ }
+ } else {
+ lf->callback(severity, domain, msg_after_prefix);
+ }
+ } else {
+ if (write_all(lf->fd, buf, msg_len, 0) < 0) { /* error */
+ /* don't log the error! mark this log entry to be blown away, and
+ * continue. */
+ lf->seems_dead = 1;
+ }
+ }
+}
+
/** Helper: sends a message to the appropriate logfiles, at loglevel
* <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;
@@ -357,20 +468,21 @@ logv(int severity, log_domain_mask_t domain, const char *funcname,
if ((! (domain & LD_NOCB)) && smartlist_len(pending_cb_messages))
flush_pending_log_callbacks();
- lf = logfiles;
- while (lf) {
- if (! (lf->severities->masks[SEVERITY_MASK_IDX(severity)] & domain)) {
- lf = lf->next;
- continue;
- }
- if (! (lf->fd >= 0 || lf->is_syslog || lf->callback)) {
- lf = lf->next;
- continue;
- }
- if (lf->seems_dead) {
- lf = lf->next;
+ if (queue_startup_messages &&
+ pending_startup_messages_len < MAX_STARTUP_MSG_LEN) {
+ end_of_prefix =
+ format_msg(buf, sizeof(buf), domain, severity, funcname, suffix,
+ format, ap, &msg_len);
+ formatted = 1;
+
+ smartlist_add(pending_startup_messages,
+ pending_log_message_new(severity,domain,buf,end_of_prefix));
+ pending_startup_messages_len += msg_len;
+ }
+
+ for (lf = logfiles; lf; lf = lf->next) {
+ if (! logfile_wants_message(lf, severity, domain))
continue;
- }
if (!formatted) {
end_of_prefix =
@@ -379,51 +491,8 @@ logv(int severity, log_domain_mask_t domain, const char *funcname,
formatted = 1;
}
- if (lf->is_syslog) {
-#ifdef HAVE_SYSLOG_H
- char *m = end_of_prefix;
-#ifdef MAXLINE
- /* Some syslog implementations have limits on the length of what you can
- * pass them, and some very old ones do not detect overflow so well.
- * Regrettably, they call their maximum line length MAXLINE. */
-#if MAXLINE < 64
-#warn "MAXLINE is a very low number; it might not be from syslog.h after all"
-#endif
- if (msg_len >= MAXLINE)
- m = tor_strndup(end_of_prefix, MAXLINE-1);
-#endif
- syslog(severity, "%s", m);
-#ifdef MAXLINE
- if (m != end_of_prefix) {
- tor_free(m);
- }
-#endif
-#endif
- lf = lf->next;
- continue;
- } else if (lf->callback) {
- if (domain & LD_NOCB) {
- if (!callbacks_deferred && pending_cb_messages) {
- pending_cb_message_t *msg = tor_malloc(sizeof(pending_cb_message_t));
- msg->severity = severity;
- msg->domain = domain;
- msg->msg = tor_strdup(end_of_prefix);
- smartlist_add(pending_cb_messages, msg);
-
- callbacks_deferred = 1;
- }
- } else {
- lf->callback(severity, domain, end_of_prefix);
- }
- lf = lf->next;
- continue;
- }
- if (write_all(lf->fd, buf, msg_len, 0) < 0) { /* error */
- /* don't log the error! mark this log entry to be blown away, and
- * continue. */
- lf->seems_dead = 1;
- }
- lf = lf->next;
+ logfile_deliver(lf, buf, msg_len, end_of_prefix, domain, severity,
+ &callbacks_deferred);
}
UNLOCK_LOGS();
}
@@ -565,6 +634,27 @@ tor_log_update_sigsafe_err_fds(void)
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
@@ -706,12 +796,14 @@ void
logs_free_all(void)
{
logfile_t *victim, *next;
- smartlist_t *messages;
+ smartlist_t *messages, *messages2;
LOCK_LOGS();
next = logfiles;
logfiles = NULL;
messages = pending_cb_messages;
pending_cb_messages = NULL;
+ messages2 = pending_startup_messages;
+ pending_startup_messages = NULL;
UNLOCK_LOGS();
while (next) {
victim = next;
@@ -721,12 +813,18 @@ logs_free_all(void)
}
tor_free(appname);
- SMARTLIST_FOREACH(messages, pending_cb_message_t *, msg, {
- tor_free(msg->msg);
- tor_free(msg);
+ SMARTLIST_FOREACH(messages, pending_log_message_t *, msg, {
+ pending_log_message_free(msg);
});
smartlist_free(messages);
+ if (messages2) {
+ SMARTLIST_FOREACH(messages2, pending_log_message_t *, msg, {
+ pending_log_message_free(msg);
+ });
+ smartlist_free(messages2);
+ }
+
/* We _could_ destroy the log mutex here, but that would screw up any logs
* that happened between here and the end of execution. */
}
@@ -821,7 +919,7 @@ add_stream_log(const log_severity_list_t *severity, const char *name, int fd)
/** Initialize the global logging facility */
void
-init_logging(void)
+init_logging(int disable_startup_queue)
{
if (!log_mutex_initialized) {
tor_mutex_init(&log_mutex);
@@ -829,6 +927,11 @@ init_logging(void)
}
if (pending_cb_messages == NULL)
pending_cb_messages = smartlist_new();
+ if (disable_startup_queue)
+ queue_startup_messages = 0;
+ if (pending_startup_messages == NULL && queue_startup_messages) {
+ pending_startup_messages = smartlist_new();
+ }
}
/** Set whether we report logging domains as a part of our log messages.
@@ -914,7 +1017,7 @@ flush_pending_log_callbacks(void)
messages = pending_cb_messages;
pending_cb_messages = smartlist_new();
do {
- SMARTLIST_FOREACH_BEGIN(messages, pending_cb_message_t *, msg) {
+ SMARTLIST_FOREACH_BEGIN(messages, pending_log_message_t *, msg) {
const int severity = msg->severity;
const int domain = msg->domain;
for (lf = logfiles; lf; lf = lf->next) {
@@ -924,8 +1027,7 @@ flush_pending_log_callbacks(void)
}
lf->callback(severity, domain, msg->msg);
}
- tor_free(msg->msg);
- tor_free(msg);
+ pending_log_message_free(msg);
} SMARTLIST_FOREACH_END(msg);
smartlist_clear(messages);
@@ -939,6 +1041,39 @@ flush_pending_log_callbacks(void)
UNLOCK_LOGS();
}
+/** Flush all the messages we stored from startup while waiting for log
+ * initialization.
+ */
+void
+flush_log_messages_from_startup(void)
+{
+ logfile_t *lf;
+
+ LOCK_LOGS();
+ queue_startup_messages = 0;
+ pending_startup_messages_len = 0;
+ if (! pending_startup_messages)
+ goto out;
+
+ SMARTLIST_FOREACH_BEGIN(pending_startup_messages, pending_log_message_t *,
+ msg) {
+ int callbacks_deferred = 0;
+ for (lf = logfiles; lf; lf = lf->next) {
+ if (! logfile_wants_message(lf, msg->severity, msg->domain))
+ continue;
+
+ logfile_deliver(lf, msg->fullmsg, strlen(msg->fullmsg), msg->msg,
+ msg->domain, msg->severity, &callbacks_deferred);
+ }
+ pending_log_message_free(msg);
+ } SMARTLIST_FOREACH_END(msg);
+ smartlist_free(pending_startup_messages);
+ pending_startup_messages = NULL;
+
+ out:
+ UNLOCK_LOGS();
+}
+
/** Close any log handlers added by add_temp_log() or marked by
* mark_logs_temp(). */
void
@@ -992,12 +1127,16 @@ mark_logs_temp(void)
* logfile fails, -1 is returned and errno is set appropriately (by open(2)).
*/
int
-add_file_log(const log_severity_list_t *severity, const char *filename)
+add_file_log(const log_severity_list_t *severity, const char *filename,
+ const int truncate)
{
int fd;
logfile_t *lf;
- fd = tor_open_cloexec(filename, O_WRONLY|O_CREAT|O_APPEND, 0644);
+ int open_flags = O_WRONLY|O_CREAT;
+ open_flags |= truncate ? O_TRUNC : O_APPEND;
+
+ fd = tor_open_cloexec(filename, open_flags, 0644);
if (fd<0)
return -1;
if (tor_fd_seekend(fd)<0) {
@@ -1279,38 +1418,15 @@ 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 ");
- }
-}
-
+/** Truncate all the log files. */
void
-describe_logs(void)
+truncate_logs(void)
{
logfile_t *lf;
- printf("==== BEGIN LOGS ====\n");
- for (lf = logfiles; lf; lf = lf->next)
- dump_log_info(lf);
- printf("==== END LOGS ====\n");
+ for (lf = logfiles; lf; lf = lf->next) {
+ if (lf->fd >= 0) {
+ tor_ftruncate(lf->fd);
+ }
+ }
}
-#endif
diff --git a/src/common/memarea.c b/src/common/memarea.c
index 0ae0ccca1d..40c09bd0e6 100644
--- a/src/common/memarea.c
+++ b/src/common/memarea.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Tor Project, Inc. */
+/* Copyright (c) 2008-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/** \file 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/memarea.h b/src/common/memarea.h
index 8b88585d35..fb261d11fa 100644
--- a/src/common/memarea.h
+++ b/src/common/memarea.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Tor Project, Inc. */
+/* Copyright (c) 2008-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* Tor dependencies */
diff --git a/src/common/mempool.c b/src/common/mempool.c
index 4389888760..695a110d3d 100644
--- a/src/common/mempool.c
+++ b/src/common/mempool.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#if 1
/* Tor dependencies */
diff --git a/src/common/mempool.h b/src/common/mempool.h
index 0fc1e4c676..1e7a3121de 100644
--- a/src/common/mempool.h
+++ b/src/common/mempool.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 0a49689e3a..ee27e97f79 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Tor Project, Inc. */
+/* Copyright (c) 2011-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -162,6 +162,7 @@ tor_validate_process_specifier(const char *process_spec,
return parse_process_specifier(process_spec, &ppspec, msg);
}
+/* XXXX we should use periodic_timer_new() for this stuff */
#ifdef HAVE_EVENT2_EVENT_H
#define PERIODIC_TIMER_FLAGS EV_PERSIST
#else
diff --git a/src/common/procmon.h b/src/common/procmon.h
index b9388e2e90..6c487648bb 100644
--- a/src/common/procmon.h
+++ b/src/common/procmon.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2013, The Tor Project, Inc. */
+/* Copyright (c) 2011-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index 6b78748834..ece56df81f 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -27,11 +27,14 @@
#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)
@@ -46,7 +49,6 @@
#include <sys/prctl.h>
#include <linux/futex.h>
#include <bits/signum.h>
-#include <event2/event.h>
#include <stdarg.h>
#include <seccomp.h>
@@ -56,15 +58,55 @@
#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;
-/** Holds a list of pre-recorded results from getaddrinfo().*/
-static sb_addr_info_t *sb_addr_info = NULL;
#undef SCMP_CMP
#define SCMP_CMP(a,b,c) ((struct scmp_arg_cmp){(a),(b),(c),0})
+#define SCMP_CMP_STR(a,b,c) \
+ ((struct scmp_arg_cmp) {(a),(b),(intptr_t)(void*)(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.
@@ -95,8 +137,11 @@ static int filter_nopar_gen[] = {
#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),
@@ -107,11 +152,14 @@ static int filter_nopar_gen[] = {
#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(rename),
SCMP_SYS(rt_sigreturn),
+ SCMP_SYS(sched_getaffinity),
SCMP_SYS(set_robust_list),
#ifdef __NR_sigreturn
SCMP_SYS(sigreturn),
@@ -119,6 +167,7 @@ static int filter_nopar_gen[] = {
SCMP_SYS(stat),
SCMP_SYS(uname),
SCMP_SYS(write),
+ SCMP_SYS(writev),
SCMP_SYS(exit_group),
SCMP_SYS(exit),
@@ -139,6 +188,7 @@ static int filter_nopar_gen[] = {
// socket syscalls
SCMP_SYS(bind),
+ SCMP_SYS(listen),
SCMP_SYS(connect),
SCMP_SYS(getsockname),
SCMP_SYS(recvmsg),
@@ -147,6 +197,19 @@ static int filter_nopar_gen[] = {
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.
@@ -164,7 +227,7 @@ sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
(void) filter;
for (i = 0; i < ARRAY_LENGTH(param); i++) {
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 1,
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction),
SCMP_CMP(0, SCMP_CMP_EQ, param[i]));
if (rc)
break;
@@ -173,6 +236,7 @@ sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return rc;
}
+#if 0
/**
* Function responsible for setting up the execve syscall for
* the seccomp filter sandbox.
@@ -185,12 +249,12 @@ sb_execve(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
// for each dynamic parameter filters
for (elem = filter; elem != NULL; elem = elem->next) {
- smp_param_t *param = (smp_param_t*) elem->param;
+ smp_param_t *param = elem->param;
if (param != NULL && param->prot == 1 && param->syscall
== SCMP_SYS(execve)) {
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 1,
- SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve),
+ SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
if (rc != 0) {
log_err(LD_BUG,"(Sandbox) failed to add execve syscall, received "
"libseccomp error %d", rc);
@@ -201,6 +265,7 @@ sb_execve(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return 0;
}
+#endif
/**
* Function responsible for setting up the time syscall for
@@ -210,8 +275,12 @@ static int
sb_time(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
{
(void) filter;
- return seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(time), 1,
+#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
}
/**
@@ -225,15 +294,15 @@ sb_accept4(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
(void)filter;
#ifdef __i386__
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketcall), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 1,
- SCMP_CMP(3, SCMP_CMP_EQ, SOCK_CLOEXEC));
+ 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;
}
@@ -252,49 +321,49 @@ sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void)filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap2), 2,
+ 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) {
@@ -321,8 +390,8 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
if (param != NULL && param->prot == 1 && param->syscall
== SCMP_SYS(open)) {
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
- SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open),
+ SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
if (rc != 0) {
log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
"libseccomp error %d", rc);
@@ -331,8 +400,8 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
}
}
- rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(-1), SCMP_SYS(open), 1,
- SCMP_CMP(1, SCMP_CMP_EQ, O_RDONLY|O_CLOEXEC));
+ 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);
@@ -342,6 +411,54 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
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_STR(0, SCMP_CMP_EQ, param->value),
+ SCMP_CMP_STR(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.
@@ -358,9 +475,9 @@ sb_openat(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
if (param != NULL && param->prot == 1 && param->syscall
== SCMP_SYS(openat)) {
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 1,
+ 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_STR(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) {
@@ -382,43 +499,40 @@ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0);
+ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket));
if (rc)
return rc;
#endif
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
SCMP_CMP(0, SCMP_CMP_EQ, PF_FILE),
- SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK),
- SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_IP));
+ SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM));
if (rc)
return rc;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
- SCMP_CMP(0, SCMP_CMP_EQ, PF_INET),
- SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC),
- SCMP_CMP(2, SCMP_CMP_EQ, IPPROTO_TCP));
- if (rc)
- return rc;
+ for (i = 0; i < 2; ++i) {
+ const int pf = i ? PF_INET : PF_INET6;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
- SCMP_CMP(0, SCMP_CMP_EQ, PF_INET),
- SCMP_CMP(1, SCMP_CMP_EQ, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK),
+ 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;
+ if (rc)
+ return rc;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
- SCMP_CMP(0, SCMP_CMP_EQ, PF_INET),
- SCMP_CMP(1, SCMP_CMP_EQ, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK),
+ 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;
+ if (rc)
+ return rc;
+ }
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 3,
+ 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));
@@ -439,12 +553,12 @@ sb_socketpair(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
(void) filter;
#ifdef __i386__
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 0);
+ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair));
if (rc)
return rc;
#endif
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socketpair), 2,
+ 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)
@@ -464,19 +578,31 @@ sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
(void) filter;
#ifdef __i386__
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 0);
+ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt));
if (rc)
return rc;
#endif
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 2,
+ 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)
@@ -497,12 +623,12 @@ sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
(void) filter;
#ifdef __i386__
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), 0);
+ rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt));
if (rc)
return rc;
#endif
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt), 2,
+ 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)
@@ -522,23 +648,23 @@ sb_fcntl64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 2,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 2,
+ 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)
@@ -560,17 +686,17 @@ sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 1,
+ 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;
@@ -591,7 +717,7 @@ sb_prctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), 1,
+ 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;
@@ -612,12 +738,12 @@ sb_mprotect(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 1,
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect),
SCMP_CMP(2, SCMP_CMP_EQ, PROT_NONE));
if (rc)
return rc;
@@ -635,12 +761,12 @@ sb_rt_sigprocmask(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 1,
+ 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;
@@ -660,12 +786,12 @@ sb_flock(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock), 1,
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(flock),
SCMP_CMP(1, SCMP_CMP_EQ, LOCK_UN));
if (rc)
return rc;
@@ -684,18 +810,18 @@ sb_futex(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
(void) filter;
// can remove
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ 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(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 1,
+ 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;
@@ -715,7 +841,7 @@ sb_mremap(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 1,
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap),
SCMP_CMP(3, SCMP_CMP_EQ, MREMAP_MAYMOVE));
if (rc)
return rc;
@@ -733,7 +859,7 @@ sb_poll(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
int rc = 0;
(void) filter;
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(poll), 2,
+ 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)
@@ -759,8 +885,8 @@ sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open)
|| param->syscall == SCMP_SYS(stat64))) {
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64), 1,
- SCMP_CMP(0, SCMP_CMP_EQ, param->value));
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64),
+ SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
if (rc != 0) {
log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
"libseccomp error %d", rc);
@@ -780,7 +906,9 @@ sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
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
@@ -788,6 +916,8 @@ static sandbox_filter_func_t filter_func[] = {
#endif
sb_open,
sb_openat,
+ sb__sysctl,
+ sb_rename,
#ifdef __NR_fcntl64
sb_fcntl64,
#endif
@@ -808,7 +938,7 @@ static sandbox_filter_func_t filter_func[] = {
sb_socketpair
};
-const char*
+const char *
sandbox_intern_string(const char *str)
{
sandbox_cfg_t *elem;
@@ -819,15 +949,65 @@ sandbox_intern_string(const char *str)
for (elem = filter_dynamic; elem != NULL; elem = elem->next) {
smp_param_t *param = elem->param;
- if (param->prot && !strcmp(str, (char*)(param->value))) {
- return (char*)(param->value);
+ 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;
+ }
}
}
- log_info(LD_GENERAL, "(Sandbox) Parameter %s not found", str);
+ 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,
+ char **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 = 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 = 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
@@ -841,10 +1021,13 @@ prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
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
+ // get total number of bytes required to mmap. (Overestimate.)
for (el = cfg; el != NULL; el = el->next) {
- pr_mem_size += strlen((char*) ((smp_param_t*)el->param)->value) + 1;
+ 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
@@ -860,31 +1043,21 @@ prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
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) {
- char *param_val = (char*)((smp_param_t *)el->param)->value;
- size_t param_size = strlen(param_val) + 1;
-
- if (pr_mem_left >= param_size) {
- // copy to protected
- memcpy(pr_mem_next, param_val, param_size);
-
- // re-point el parameter to protected
- {
- void *old_val = (void *) ((smp_param_t*)el->param)->value;
- tor_free(old_val);
- }
- ((smp_param_t*)el->param)->value = (intptr_t) pr_mem_next;
- ((smp_param_t*)el->param)->prot = 1;
-
- // move next available protected memory
- pr_mem_next += param_size;
- pr_mem_left -= param_size;
- } else {
- log_err(LD_BUG,"(Sandbox) insufficient protected memory!");
+ 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
@@ -899,19 +1072,19 @@ prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
* Setting sandbox restrictions so the string memory cannot be tampered with
*/
// no mremap of the protected base address
- ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(mremap), 1,
+ 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;
+ goto out;
}
// no munmap of the protected base address
- ret = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(munmap), 1,
+ 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;
+ goto out;
}
/*
@@ -924,27 +1097,28 @@ prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
* There is a restriction on how much you can mprotect with R|W up to the
* size of the canary.
*/
- ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2,
+ 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;
+ goto out;
}
- ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 2,
+ 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;
+ goto out;
}
out:
- return ret;
+ strmap_free(locations, NULL);
+ return ret;
}
/**
@@ -954,22 +1128,27 @@ prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
* point.
*/
static sandbox_cfg_t*
-new_element(int syscall, int index, intptr_t value)
+new_element2(int syscall, char *value, char *value2)
{
smp_param_t *param = NULL;
- sandbox_cfg_t *elem = tor_malloc(sizeof(sandbox_cfg_t));
- elem->param = tor_malloc(sizeof(smp_param_t));
+ sandbox_cfg_t *elem = tor_malloc_zero(sizeof(sandbox_cfg_t));
+ param = elem->param = tor_malloc_zero(sizeof(smp_param_t));
- param = elem->param;
param->syscall = syscall;
- param->pindex = index;
param->value = value;
+ param->value2 = value2;
param->prot = 0;
return elem;
}
+static sandbox_cfg_t*
+new_element(int syscall, char *value)
+{
+ return new_element2(syscall, value, NULL);
+}
+
#ifdef __NR_stat64
#define SCMP_stat SCMP_SYS(stat64)
#else
@@ -977,11 +1156,11 @@ new_element(int syscall, int index, intptr_t value)
#endif
int
-sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file, int fr)
+sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file)
{
sandbox_cfg_t *elem = NULL;
- elem = new_element(SCMP_stat, 0, (intptr_t)(void*) tor_strdup(file));
+ elem = new_element(SCMP_stat, file);
if (!elem) {
log_err(LD_BUG,"(Sandbox) failed to register parameter!");
return -1;
@@ -990,40 +1169,15 @@ sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file, int fr)
elem->next = *cfg;
*cfg = elem;
- if (fr) tor_free(file);
- return 0;
-}
-
-int
-sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...)
-{
- int rc = 0;
- char *fn = NULL;
-
- va_list ap;
- va_start(ap, cfg);
-
- while ((fn = va_arg(ap, char*)) != NULL) {
- int fr = va_arg(ap, int);
-
- rc = sandbox_cfg_allow_stat_filename(cfg, fn, fr);
- if (rc) {
- log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_stat_filename_array fail");
- goto end;
- }
- }
-
- end:
- va_end(ap);
return 0;
}
int
-sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file, int fr)
+sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file)
{
sandbox_cfg_t *elem = NULL;
- elem = new_element(SCMP_SYS(open), 0, (intptr_t)(void *)tor_strdup(file));
+ elem = new_element(SCMP_SYS(open), file);
if (!elem) {
log_err(LD_BUG,"(Sandbox) failed to register parameter!");
return -1;
@@ -1032,41 +1186,33 @@ sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file, int fr)
elem->next = *cfg;
*cfg = elem;
- if (fr) tor_free(file);
-
return 0;
}
int
-sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...)
+sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2)
{
- int rc = 0;
- char *fn = NULL;
-
- va_list ap;
- va_start(ap, cfg);
+ sandbox_cfg_t *elem = NULL;
- while ((fn = va_arg(ap, char*)) != NULL) {
- int fr = va_arg(ap, int);
+ elem = new_element2(SCMP_SYS(rename), file1, file2);
- rc = sandbox_cfg_allow_open_filename(cfg, fn, fr);
- if (rc) {
- log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_open_filename_array fail");
- goto end;
- }
+ if (!elem) {
+ log_err(LD_BUG,"(Sandbox) failed to register parameter!");
+ return -1;
}
- end:
- va_end(ap);
+ elem->next = *cfg;
+ *cfg = elem;
+
return 0;
}
int
-sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file, int fr)
+sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file)
{
sandbox_cfg_t *elem = NULL;
- elem = new_element(SCMP_SYS(openat), 1, (intptr_t)(void *)tor_strdup(file));
+ elem = new_element(SCMP_SYS(openat), file);
if (!elem) {
log_err(LD_BUG,"(Sandbox) failed to register parameter!");
return -1;
@@ -1075,41 +1221,16 @@ sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file, int fr)
elem->next = *cfg;
*cfg = elem;
- if (fr) tor_free(file);
-
- return 0;
-}
-
-int
-sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...)
-{
- int rc = 0;
- char *fn = NULL;
-
- va_list ap;
- va_start(ap, cfg);
-
- while ((fn = va_arg(ap, char*)) != NULL) {
- int fr = va_arg(ap, int);
-
- rc = sandbox_cfg_allow_openat_filename(cfg, fn, fr);
- if (rc) {
- log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_openat_filename_array fail");
- goto end;
- }
- }
-
- end:
- va_end(ap);
return 0;
}
+#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), 1, (intptr_t)(void *)tor_strdup(com));
+ elem = new_element(SCMP_SYS(execve), com);
if (!elem) {
log_err(LD_BUG,"(Sandbox) failed to register parameter!");
return -1;
@@ -1121,27 +1242,71 @@ sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
return 0;
}
-int
-sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...)
+#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)
{
- int rc = 0;
- char *fn = NULL;
+ return (unsigned)siphash24g(item->name, strlen(item->name)) + item->family;
+}
- va_list ap;
- va_start(ap, cfg);
+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);
+}
- while ((fn = va_arg(ap, char*)) != NULL) {
+static void
+cached_getaddrinfo_item_free(cached_getaddrinfo_item_t *item)
+{
+ if (item == NULL)
+ return;
- rc = sandbox_cfg_allow_execve(cfg, fn);
- if (rc) {
- log_err(LD_BUG,"(Sandbox) sandbox_cfg_allow_execve_array failed");
- goto end;
- }
- }
+ tor_free(item->name);
+ if (item->res)
+ freeaddrinfo(item->res);
+ tor_free(item);
+}
- end:
- va_end(ap);
- return 0;
+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_GENERATE2(getaddrinfo_cache, cached_getaddrinfo_item_t, node,
+ cached_getaddrinfo_item_hash,
+ cached_getaddrinfo_items_eq,
+ 0.6, tor_reallocarray_, tor_free_)
+
+/** If true, don't try to cache getaddrinfo results. */
+static int sandbox_getaddrinfo_cache_disabled = 0;
+
+/** Tell the sandbox layer not to try to cache getaddrinfo results. Used as in
+ * tor-resolve, when we have no intention of initializing crypto or of
+ * installing the sandbox.*/
+void
+sandbox_disable_getaddrinfo_cache(void)
+{
+ sandbox_getaddrinfo_cache_disabled = 1;
}
int
@@ -1149,68 +1314,99 @@ sandbox_getaddrinfo(const char *name, const char *servname,
const struct addrinfo *hints,
struct addrinfo **res)
{
- sb_addr_info_t *el;
+ int err;
+ struct cached_getaddrinfo_item_t search, *item;
- if (servname != NULL)
- return -1;
+ if (sandbox_getaddrinfo_cache_disabled) {
+ return getaddrinfo(name, NULL, hints, res);
+ }
+
+ 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;
- for (el = sb_addr_info; el; el = el->next) {
- if (!strcmp(el->name, name)) {
- *res = tor_malloc(sizeof(struct addrinfo));
+ 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. */
- memcpy(*res, el->info, sizeof(struct addrinfo));
- /* XXXX What if there are multiple items in the list? */
- return 0;
+ 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 (!sandbox_active) {
- if (getaddrinfo(name, NULL, hints, res)) {
- log_err(LD_BUG,"(Sandbox) getaddrinfo failed!");
- return -1;
+ if (item->res) {
+ freeaddrinfo(item->res);
+ item->res = NULL;
}
+ item->res = *res;
+ item->err = err;
+ return err;
+ }
- return 0;
+ /* 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
+ /* getting here means something went wrong */
log_err(LD_BUG,"(Sandbox) failed to get address %s!", name);
- if (*res) {
- tor_free(*res);
- res = NULL;
- }
- return -1;
+ return EAI_NONAME;
}
int
-sandbox_add_addrinfo(const char* name)
+sandbox_add_addrinfo(const char *name)
{
- int ret;
+ struct addrinfo *res;
struct addrinfo hints;
- sb_addr_info_t *el = NULL;
-
- el = tor_malloc(sizeof(sb_addr_info_t));
+ int i;
+ static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC };
memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
+ for (i = 0; i < 3; ++i) {
+ hints.ai_family = families[i];
- ret = getaddrinfo(name, NULL, &hints, &(el->info));
- if (ret) {
- log_err(LD_BUG,"(Sandbox) failed to getaddrinfo");
- ret = -2;
- tor_free(el);
- goto out;
+ res = NULL;
+ (void) sandbox_getaddrinfo(name, NULL, &hints, &res);
+ if (res)
+ sandbox_freeaddrinfo(res);
}
- el->name = tor_strdup(name);
- el->next = sb_addr_info;
- sb_addr_info = el;
+ return 0;
+}
- out:
- return ret;
+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);
}
/**
@@ -1247,7 +1443,7 @@ add_noparam_filter(scmp_filter_ctx ctx)
// add general filters
for (i = 0; i < ARRAY_LENGTH(filter_nopar_gen); i++) {
- rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, filter_nopar_gen[i], 0);
+ 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);
@@ -1295,7 +1491,8 @@ install_syscall_filter(sandbox_cfg_t* cfg)
// loading the seccomp2 filter
if ((rc = seccomp_load(ctx))) {
- log_err(LD_BUG, "(Sandbox) failed to load!");
+ log_err(LD_BUG, "(Sandbox) failed to load: %d (%s)!", rc,
+ strerror(-rc));
goto end;
}
@@ -1307,6 +1504,29 @@ install_syscall_filter(sandbox_cfg_t* cfg)
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
@@ -1316,8 +1536,14 @@ static void
sigsys_debugging(int nr, siginfo_t *info, void *void_context)
{
ucontext_t *ctx = (ucontext_t *) (void_context);
- char number[32];
+ 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)
@@ -1326,14 +1552,28 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context)
if (!ctx)
return;
- syscall = ctx->uc_mcontext.gregs[REG_SYSCALL];
+ 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);
- format_dec_number_sigsafe(syscall, number, sizeof(number));
tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ",
- number,
+ 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
@@ -1385,7 +1625,8 @@ register_cfg(sandbox_cfg_t* cfg)
return 0;
}
- for (elem = filter_dynamic; elem->next != NULL; elem = elem->next);
+ for (elem = filter_dynamic; elem->next != NULL; elem = elem->next)
+ ;
elem->next = cfg;
@@ -1414,6 +1655,11 @@ initialise_libseccomp_sandbox(sandbox_cfg_t* cfg)
return 0;
}
+int
+sandbox_is_active(void)
+{
+ return sandbox_active != 0;
+}
#endif // USE_LIBSECCOMP
sandbox_cfg_t*
@@ -1428,83 +1674,70 @@ sandbox_init(sandbox_cfg_t *cfg)
#if defined(USE_LIBSECCOMP)
return initialise_libseccomp_sandbox(cfg);
-#elif defined(_WIN32)
+#elif defined(__linux__)
(void)cfg;
- log_warn(LD_BUG,"Windows sandboxing is not implemented. The feature is "
- "currently disabled.");
+ 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;
-#elif defined(TARGET_OS_MAC)
- (void)cfg;
- log_warn(LD_BUG,"Mac OSX sandboxing is not implemented. The feature is "
- "currently disabled");
- return 0;
#else
(void)cfg;
- log_warn(LD_BUG,"Sandboxing is not implemented for your platform. The "
- "feature is currently disabled");
+ 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,
- int fr)
+sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file)
{
- (void)cfg; (void)file; (void)fr;
+ (void)cfg; (void)file;
return 0;
}
int
-sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...)
+sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file)
{
- (void)cfg;
+ (void)cfg; (void)file;
return 0;
}
+#if 0
int
-sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file,
- int fr)
-{
- (void)cfg; (void)file; (void)fr;
- return 0;
-}
-
-int
-sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...)
+sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
{
- (void)cfg;
+ (void)cfg; (void)com;
return 0;
}
+#endif
int
-sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
+sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file)
{
- (void)cfg; (void)com;
+ (void)cfg; (void)file;
return 0;
}
int
-sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...)
+sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2)
{
- (void)cfg;
+ (void)cfg; (void)file1; (void)file2;
return 0;
}
int
-sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file,
- int fr)
+sandbox_is_active(void)
{
- (void)cfg; (void)file; (void)fr;
return 0;
}
-int
-sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...)
+void
+sandbox_disable_getaddrinfo_cache(void)
{
- (void)cfg;
- return 0;
}
#endif
diff --git a/src/common/sandbox.h b/src/common/sandbox.h
index d64d427d3e..ad001865a7 100644
--- a/src/common/sandbox.h
+++ b/src/common/sandbox.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -65,10 +65,10 @@ typedef struct smp_param {
/** syscall associated with parameter. */
int syscall;
- /** parameter index. */
- int pindex;
/** parameter value. */
- intptr_t value;
+ char *value;
+ /** parameter value, second argument. */
+ char *value2;
/** parameter flag (0 = not protected, 1 = protected). */
int prot;
@@ -85,27 +85,12 @@ struct sandbox_cfg_elem {
SB_IMPL implem;
/** Configuration parameter. */
- void *param;
+ smp_param_t *param;
/** Next element of the configuration*/
struct sandbox_cfg_elem *next;
};
-/**
- * Structure used for keeping a linked list of getaddrinfo pre-recorded
- * results.
- */
-struct sb_addr_info_el {
- /** Name of the address info result. */
- char *name;
- /** Pre-recorded getaddrinfo result. */
- struct addrinfo *info;
- /** Next element in the list. */
- struct sb_addr_info_el *next;
-};
-/** Typedef to structure used to manage an addrinfo list. */
-typedef struct sb_addr_info_el sb_addr_info_t;
-
/** Function pointer defining the prototype of a filter function.*/
typedef int (*sandbox_filter_func_t)(scmp_filter_ctx ctx,
sandbox_cfg_t *filter);
@@ -119,22 +104,6 @@ typedef struct {
sandbox_cfg_t *filter_dynamic;
} sandbox_t;
-/**
- * Linux 32 bit definitions
- */
-#if defined(__i386__)
-
-#define REG_SYSCALL REG_EAX
-
-/**
- * Linux 64 bit definitions
- */
-#elif defined(__x86_64__)
-
-#define REG_SYSCALL REG_RAX
-
-#endif
-
#endif // USE_LIBSECCOMP
#ifdef USE_LIBSECCOMP
@@ -146,11 +115,16 @@ struct addrinfo;
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
@@ -167,81 +141,42 @@ sandbox_cfg_t * sandbox_cfg_new(void);
/**
* Function used to add a open allowed filename to a supplied configuration.
- * The (char*) specifies the path to the allowed file, fr = 1 tells the
- * function that the char* needs to be free-ed, 0 means the pointer does not
- * need to be free-ed.
- */
-int sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file,
- int fr);
-
-/** Function used to add a series of open allowed filenames to a supplied
- * configuration.
- * @param cfg sandbox configuration.
- * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
- * the char* specifies the path to the allowed file, 1 tells the function
- * that the char* needs to be free-ed, 0 means the pointer does not need to
- * be free-ed; the final parameter needs to be <NULL, 0>.
+ * The (char*) specifies the path to the allowed file; we take ownership
+ * of the pointer.
*/
-int sandbox_cfg_allow_open_filename_array(sandbox_cfg_t **cfg, ...);
+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 openat allowed filename to a supplied configuration.
- * The (char*) specifies the path to the allowed file, fr = 1 tells the
- * function that the char* needs to be free-ed, 0 means the pointer does not
- * need to be free-ed.
- */
-int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file,
- int fr);
-
-/** Function used to add a series of openat allowed filenames to a supplied
- * configuration.
- * @param cfg sandbox configuration.
- * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
- * the char* specifies the path to the allowed file, 1 tells the function
- * that the char* needs to be free-ed, 0 means the pointer does not need to
- * be free-ed; the final parameter needs to be <NULL, 0>.
+ * The (char*) specifies the path to the allowed file; we steal the pointer to
+ * that file.
*/
-int sandbox_cfg_allow_openat_filename_array(sandbox_cfg_t **cfg, ...);
+int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file);
+#if 0
/**
* Function used to add a execve allowed filename to a supplied configuration.
- * The (char*) specifies the path to the allowed file, fr = 1 tells the
- * function that the char* needs to be free-ed, 0 means the pointer does not
- * need to be free-ed.
+ * 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 ... all future parameters are specified as pairs of <(char*), 1 / 0>
- * the char* specifies the path to the allowed file, 1 tells the function
- * that the char* needs to be free-ed, 0 means the pointer does not need to
- * be free-ed; the final parameter needs to be <NULL, 0>.
- */
-int sandbox_cfg_allow_execve_array(sandbox_cfg_t **cfg, ...);
+#endif
/**
* Function used to add a stat/stat64 allowed filename to a configuration.
- * The (char*) specifies the path to the allowed file, fr = 1 tells the
- * function that the char* needs to be free-ed, 0 means the pointer does not
- * need to be free-ed.
- */
-int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file,
- int fr);
-
-/** Function used to add a series of stat64 allowed filenames to a supplied
- * configuration.
- * @param cfg sandbox configuration.
- * @param ... all future parameters are specified as pairs of <(char*), 1 / 0>
- * the char* specifies the path to the allowed file, 1 tells the function
- * that the char* needs to be free-ed, 0 means the pointer does not need to
- * be free-ed; the final parameter needs to be <NULL, 0>.
+ * The (char*) specifies the path to the allowed file; that pointer is stolen.
*/
-int sandbox_cfg_allow_stat_filename_array(sandbox_cfg_t **cfg, ...);
+int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file);
/** 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);
+
+void sandbox_disable_getaddrinfo_cache(void);
+
#endif /* SANDBOX_H_ */
diff --git a/src/common/testsupport.h b/src/common/testsupport.h
index 4a4f50b69b..2610086700 100644
--- a/src/common/testsupport.h
+++ b/src/common/testsupport.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, The Tor Project, Inc. */
+/* Copyright (c) 2013-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_TESTSUPPORT_H
diff --git a/src/common/torgzip.c b/src/common/torgzip.c
index 15451ee30d..4480e4b747 100644
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -46,6 +46,12 @@
#include <zlib.h>
+static size_t tor_zlib_state_size_precalc(int inflate,
+ int windowbits, int memlevel);
+
+/** Total number of bytes allocated for zlib state */
+static size_t total_zlib_allocation = 0;
+
/** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
* set to -1 if we haven't checked yet. */
static int gzip_is_supported = -1;
@@ -411,6 +417,9 @@ struct tor_zlib_state_t {
size_t input_so_far;
/** Number of bytes written so far. Used to detect zlib bombs. */
size_t output_so_far;
+
+ /** Approximate number of bytes allocated for this object. */
+ size_t allocation;
};
/** Construct and return a tor_zlib_state_t object using <b>method</b>. If
@@ -420,6 +429,7 @@ tor_zlib_state_t *
tor_zlib_new(int compress, compress_method_t method)
{
tor_zlib_state_t *out;
+ int bits;
if (method == GZIP_METHOD && !is_gzip_supported()) {
/* Old zlib version don't support gzip in inflateInit2 */
@@ -432,14 +442,19 @@ tor_zlib_new(int compress, compress_method_t method)
out->stream.zfree = Z_NULL;
out->stream.opaque = NULL;
out->compress = compress;
+ bits = method_bits(method);
if (compress) {
if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED,
- method_bits(method), 8, Z_DEFAULT_STRATEGY) != Z_OK)
+ bits, 8, Z_DEFAULT_STRATEGY) != Z_OK)
goto err;
} else {
- if (inflateInit2(&out->stream, method_bits(method)) != Z_OK)
+ if (inflateInit2(&out->stream, bits) != Z_OK)
goto err;
}
+ out->allocation = tor_zlib_state_size_precalc(!compress, bits, 8);
+
+ total_zlib_allocation += out->allocation;
+
return out;
err:
@@ -472,7 +487,7 @@ tor_zlib_process(tor_zlib_state_t *state,
state->stream.avail_out = (unsigned int)*out_len;
if (state->compress) {
- err = deflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH);
+ err = deflate(&state->stream, finish ? Z_FINISH : Z_NO_FLUSH);
} else {
err = inflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH);
}
@@ -496,7 +511,7 @@ tor_zlib_process(tor_zlib_state_t *state,
case Z_STREAM_END:
return TOR_ZLIB_DONE;
case Z_BUF_ERROR:
- if (state->stream.avail_in == 0)
+ if (state->stream.avail_in == 0 && !finish)
return TOR_ZLIB_OK;
return TOR_ZLIB_BUF_FULL;
case Z_OK:
@@ -517,6 +532,8 @@ tor_zlib_free(tor_zlib_state_t *state)
if (!state)
return;
+ total_zlib_allocation -= state->allocation;
+
if (state->compress)
deflateEnd(&state->stream);
else
@@ -525,3 +542,48 @@ tor_zlib_free(tor_zlib_state_t *state)
tor_free(state);
}
+/** Return an approximate number of bytes used in RAM to hold a state with
+ * window bits <b>windowBits</b> and compression level 'memlevel' */
+static size_t
+tor_zlib_state_size_precalc(int inflate, int windowbits, int memlevel)
+{
+ windowbits &= 15;
+
+#define A_FEW_KILOBYTES 2048
+
+ if (inflate) {
+ /* From zconf.h:
+
+ "The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects."
+ */
+ return sizeof(tor_zlib_state_t) + sizeof(struct z_stream_s) +
+ (1 << 15) + A_FEW_KILOBYTES;
+ } else {
+ /* Also from zconf.h:
+
+ "The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ ... plus a few kilobytes for small objects."
+ */
+ return sizeof(tor_zlib_state_t) + sizeof(struct z_stream_s) +
+ (1 << (windowbits + 2)) + (1 << (memlevel + 9)) + A_FEW_KILOBYTES;
+ }
+#undef A_FEW_KILOBYTES
+}
+
+/** Return the approximate number of bytes allocated for <b>state</b>. */
+size_t
+tor_zlib_state_size(const tor_zlib_state_t *state)
+{
+ return state->allocation;
+}
+
+/** Return the approximate number of bytes allocated for all zlib states. */
+size_t
+tor_zlib_get_total_allocation(void)
+{
+ return total_zlib_allocation;
+}
+
diff --git a/src/common/torgzip.h b/src/common/torgzip.h
index 5db03fe6e0..1378d55b76 100644
--- a/src/common/torgzip.h
+++ b/src/common/torgzip.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -55,5 +55,8 @@ tor_zlib_output_t tor_zlib_process(tor_zlib_state_t *state,
int finish);
void tor_zlib_free(tor_zlib_state_t *state);
+size_t tor_zlib_state_size(const tor_zlib_state_t *state);
+size_t tor_zlib_get_total_allocation(void);
+
#endif
diff --git a/src/common/torint.h b/src/common/torint.h
index a993d7649a..d0b0ac14a0 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -332,30 +332,30 @@ typedef uint32_t uintptr_t;
#endif /* time_t_is_signed */
#endif /* ifndef(TIME_MAX) */
-#ifndef SIZE_T_MAX
+#ifndef SIZE_MAX
#if (SIZEOF_SIZE_T == 4)
-#define SIZE_T_MAX UINT32_MAX
+#define SIZE_MAX UINT32_MAX
#elif (SIZEOF_SIZE_T == 8)
-#define SIZE_T_MAX UINT64_MAX
+#define SIZE_MAX UINT64_MAX
#else
-#error "Can't define SIZE_T_MAX"
+#error "Can't define SIZE_MAX"
#endif
#endif
-#ifndef SSIZE_T_MAX
+#ifndef SSIZE_MAX
#if (SIZEOF_SIZE_T == 4)
-#define SSIZE_T_MAX INT32_MAX
+#define SSIZE_MAX INT32_MAX
#elif (SIZEOF_SIZE_T == 8)
-#define SSIZE_T_MAX INT64_MAX
+#define SSIZE_MAX INT64_MAX
#else
-#error "Can't define SSIZE_T_MAX"
+#error "Can't define SSIZE_MAX"
#endif
#endif
/** Any ssize_t larger than this amount is likely to be an underflow. */
-#define SSIZE_T_CEILING ((ssize_t)(SSIZE_T_MAX-16))
+#define SSIZE_T_CEILING ((ssize_t)(SSIZE_MAX-16))
/** Any size_t larger than this amount is likely to be an underflow. */
-#define SIZE_T_CEILING ((size_t)(SSIZE_T_MAX-16))
+#define SIZE_T_CEILING ((size_t)(SSIZE_MAX-16))
#endif /* __TORINT_H */
diff --git a/src/common/torlog.h b/src/common/torlog.h
index d210c8b249..fa7266c199 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -1,7 +1,7 @@
/* 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. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -13,6 +13,7 @@
#ifndef TOR_TORLOG_H
#include "compat.h"
+#include "testsupport.h"
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
@@ -120,7 +121,7 @@ typedef struct log_severity_list_t {
/** Callback type used for add_callback_log. */
typedef void (*log_callback)(int severity, uint32_t domain, const char *msg);
-void init_logging(void);
+void init_logging(int disable_startup_queue);
int parse_log_level(const char *level);
const char *log_level_to_string(int level);
int parse_log_severity_config(const char **cfg,
@@ -129,7 +130,8 @@ void set_log_severity_config(int minSeverity, int maxSeverity,
log_severity_list_t *severity_out);
void add_stream_log(const log_severity_list_t *severity, const char *name,
int fd);
-int add_file_log(const log_severity_list_t *severity, const char *filename);
+int add_file_log(const log_severity_list_t *severity, const char *filename,
+ const int truncate);
#ifdef HAVE_SYSLOG_H
int add_syslog_log(const log_severity_list_t *severity);
#endif
@@ -145,8 +147,10 @@ void mark_logs_temp(void);
void change_callback_log_severity(int loglevelMin, int loglevelMax,
log_callback cb);
void flush_pending_log_callbacks(void);
+void flush_log_messages_from_startup(void);
void log_set_application_name(const char *name);
void set_log_time_granularity(int granularity_msec);
+void truncate_logs(void);
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
CHECK_PRINTF(3,4);
@@ -155,9 +159,12 @@ void tor_log_err_sigsafe(const char *m, ...);
int tor_log_get_sigsafe_err_fds(const int **out);
void tor_log_update_sigsafe_err_fds(void);
-#if defined(__GNUC__) || defined(RUNNING_DOXYGEN)
+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);
@@ -228,6 +235,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 315a767e9e..cca2d420b6 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -16,10 +16,6 @@
#include "orconfig.h"
-#if defined (WINCE)
-#include <WinSock2.h>
-#endif
-
#include <assert.h>
#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
#ifndef _WIN32_WINNT
@@ -149,6 +145,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.
@@ -159,7 +156,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 */
@@ -711,31 +708,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
@@ -769,8 +782,7 @@ static const cipher_info_t CLIENT_CIPHER_INFO_LIST[] = {
};
/** The length of CLIENT_CIPHER_INFO_LIST and CLIENT_CIPHER_DUMMIES. */
-static const int N_CLIENT_CIPHERS =
- sizeof(CLIENT_CIPHER_INFO_LIST)/sizeof(CLIENT_CIPHER_INFO_LIST[0]);
+static const int N_CLIENT_CIPHERS = ARRAY_LENGTH(CLIENT_CIPHER_INFO_LIST);
#endif
#ifndef V2_HANDSHAKE_CLIENT
@@ -1158,6 +1170,9 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
return ((new_ctx != NULL) ? 0 : -1);
}
+/** The group we should use for ecdhe when none was selected. */
+#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1
+
/** Create a new TLS context for use with Tor TLS handshakes.
* <b>identity</b> should be set to the identity key used to sign the
* certificate.
@@ -1228,10 +1243,15 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
goto error;
#endif
- /* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */
+ /* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */
if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
goto error;
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
+
+ /* 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
@@ -1266,6 +1286,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
#endif
+ /* XXX This block is now obsolete. */
if (
#ifdef DISABLE_SSL3_HANDSHAKE
1 ||
@@ -1297,10 +1318,12 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_options(result->ctx,
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
+#ifndef OPENSSL_NO_COMP
/* Don't actually allow compression; it uses ram and time, but the data
* we transmit is all encrypted anyway. */
if (result->ctx->comp_methods)
result->ctx->comp_methods = NULL;
+#endif
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
@@ -1345,7 +1368,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
else if (flags & TOR_TLS_CTX_USE_ECDHE_P256)
nid = NID_X9_62_prime256v1;
else
- nid = NID_X9_62_prime256v1;
+ nid = NID_tor_default_ecdhe_group;
/* Use P-256 for ECDHE. */
ec_key = EC_KEY_new_by_curve_name(nid);
if (ec_key != NULL) /*XXXX Handle errors? */
@@ -1445,6 +1468,43 @@ static uint16_t v2_cipher_list[] = {
/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */
static int v2_cipher_list_pruned = 0;
+/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>;
+ * return 1 if it does support it, or if we have no way to tell. */
+static int
+find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher)
+{
+ const SSL_CIPHER *c;
+#ifdef HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR
+ if (m && m->get_cipher_by_char) {
+ unsigned char cipherid[3];
+ set_uint16(cipherid, htons(cipher));
+ 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. */
+ c = m->get_cipher_by_char(cipherid);
+ if (c)
+ tor_assert((c->id & 0xffff) == cipher);
+ return c != NULL;
+ } else
+#endif
+ if (m && m->get_cipher && m->num_ciphers) {
+ /* It would seem that some of the "let's-clean-up-openssl" forks have
+ * removed the get_cipher_by_char function. Okay, so now you get a
+ * quadratic search.
+ */
+ int i;
+ for (i = 0; i < m->num_ciphers(); ++i) {
+ c = m->get_cipher(i);
+ if (c && (c->id & 0xffff) == cipher) {
+ return 1;
+ }
+ }
+ return 0;
+ } else {
+ return 1; /* No way to search */
+ }
+}
+
/** Remove from v2_cipher_list every cipher that we don't support, so that
* comparing v2_cipher_list to a client's cipher list will give a sensible
* result. */
@@ -1456,13 +1516,7 @@ prune_v2_cipher_list(void)
inp = outp = v2_cipher_list;
while (*inp) {
- unsigned char cipherid[2];
- const SSL_CIPHER *cipher;
- /* Is there no better way to do this? */
- set_uint16(cipherid, htons(*inp));
- cipher = m->get_cipher_by_char(cipherid);
- if (cipher) {
- tor_assert((cipher->id & 0xffff) == *inp);
+ if (find_cipher_by_id(m, *inp)) {
*outp++ = *inp++;
} else {
inp++;
@@ -2311,6 +2365,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,
@@ -2336,11 +2391,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 */
@@ -2555,8 +2616,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;
@@ -2582,16 +2643,20 @@ check_no_tls_errors_(const char *fname, int line)
int
tor_tls_used_v1_handshake(tor_tls_t *tls)
{
+#if defined(V2_HANDSHAKE_SERVER) && defined(V2_HANDSHAKE_CLIENT)
+ return ! tls->wasV2Handshake;
+#else
if (tls->isServer) {
-#ifdef V2_HANDSHAKE_SERVER
+# ifdef V2_HANDSHAKE_SERVER
return ! tls->wasV2Handshake;
-#endif
+# endif
} else {
-#ifdef V2_HANDSHAKE_CLIENT
+# ifdef V2_HANDSHAKE_CLIENT
return ! tls->wasV2Handshake;
-#endif
+# endif
}
return 1;
+#endif
}
/** Return true iff <b>name</b> is a DN of a kind that could only
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 49c488b365..235d801202 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_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 054de3dbe9..50097dac93 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2003, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -26,6 +26,7 @@
#include "address.h"
#include "sandbox.h"
#include "backtrace.h"
+#include "util_process.h"
#ifdef _WIN32
#include <io.h>
@@ -95,6 +96,10 @@
#include <sys/wait.h>
#endif
+#ifdef __clang_analyzer__
+#undef MALLOC_ZERO_WORKS
+#endif
+
/* =====
* Assertion helper.
* ===== */
@@ -190,33 +195,40 @@ tor_malloc_zero_(size_t size DMALLOC_PARAMS)
return result;
}
+/* The square root of SIZE_MAX + 1. If a is less than this, and b is less
+ * than this, then a*b is less than SIZE_MAX. (For example, if size_t is
+ * 32 bits, then SIZE_MAX is 0xffffffff and this value is 0x10000. If a and
+ * b are less than this, then their product is at most (65535*65535) ==
+ * 0xfffe0001. */
+#define SQRT_SIZE_MAX_P1 (((size_t)1) << (sizeof(size_t)*4))
+
+/** Return non-zero if and only if the product of the arguments is exact. */
+static INLINE int
+size_mul_check(const size_t x, const size_t y)
+{
+ /* This first check is equivalent to
+ (x < SQRT_SIZE_MAX_P1 && y < SQRT_SIZE_MAX_P1)
+
+ Rationale: if either one of x or y is >= SQRT_SIZE_MAX_P1, then it
+ will have some bit set in its most significant half.
+ */
+ return ((x|y) < SQRT_SIZE_MAX_P1 ||
+ y == 0 ||
+ x <= SIZE_MAX / y);
+}
+
/** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill
* the memory with zero bytes, and return a pointer to the result.
* Log and terminate the process on error. (Same as
* calloc(<b>nmemb</b>,<b>size</b>), but never returns NULL.)
- *
- * XXXX This implementation probably asserts in cases where it could
- * work, because it only tries dividing SIZE_MAX by size (according to
- * the calloc(3) man page, the size of an element of the nmemb-element
- * array to be allocated), not by nmemb (which could in theory be
- * smaller than size). Don't do that then.
+ * The second argument (<b>size</b>) should preferably be non-zero
+ * and a compile-time constant.
*/
void *
tor_calloc_(size_t nmemb, size_t size DMALLOC_PARAMS)
{
- /* You may ask yourself, "wouldn't it be smart to use calloc instead of
- * malloc+memset? Perhaps libc's calloc knows some nifty optimization trick
- * we don't!" Indeed it does, but its optimizations are only a big win when
- * we're allocating something very big (it knows if it just got the memory
- * from the OS in a pre-zeroed state). We don't want to use tor_malloc_zero
- * for big stuff, so we don't bother with calloc. */
- void *result;
- size_t max_nmemb = (size == 0) ? SIZE_MAX : SIZE_MAX/size;
-
- tor_assert(nmemb < max_nmemb);
-
- result = tor_malloc_zero_((nmemb * size) DMALLOC_FN_ARGS);
- return result;
+ tor_assert(size_mul_check(nmemb, size));
+ return tor_malloc_zero_((nmemb * size) DMALLOC_FN_ARGS);
}
/** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b>
@@ -230,6 +242,13 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS)
tor_assert(size < SIZE_T_CEILING);
+#ifndef MALLOC_ZERO_WORKS
+ /* Some libc mallocs don't work when size==0. Override them. */
+ if (size==0) {
+ size=1;
+ }
+#endif
+
#ifdef USE_DMALLOC
result = dmalloc_realloc(file, line, ptr, size, DMALLOC_FUNC_REALLOC, 0);
#else
@@ -243,6 +262,20 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS)
return result;
}
+/**
+ * Try to realloc <b>ptr</b> so that it takes up sz1 * sz2 bytes. Check for
+ * overflow. Unlike other allocation functions, return NULL on overflow.
+ */
+void *
+tor_reallocarray_(void *ptr, size_t sz1, size_t sz2 DMALLOC_PARAMS)
+{
+ /* XXXX we can make this return 0, but we would need to check all the
+ * reallocarray users. */
+ tor_assert(size_mul_check(sz1, sz2));
+
+ return tor_realloc(ptr, (sz1 * sz2) DMALLOC_FN_ARGS);
+}
+
/** Return a newly allocated copy of the NUL-terminated string s. On
* error, log and terminate. (Like strdup(s), but never returns
* NULL.)
@@ -303,7 +336,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);
@@ -898,8 +931,8 @@ 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
+/** 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)
@@ -931,6 +964,68 @@ string_is_key_value(int severity, const char *string)
return 1;
}
+/** Return true if <b>string</b> represents a valid IPv4 adddress in
+ * 'a.b.c.d' form.
+ */
+int
+string_is_valid_ipv4_address(const char *string)
+{
+ struct in_addr addr;
+
+ return (tor_inet_pton(AF_INET,string,&addr) == 1);
+}
+
+/** Return true if <b>string</b> represents a valid IPv6 address in
+ * a form that inet_pton() can parse.
+ */
+int
+string_is_valid_ipv6_address(const char *string)
+{
+ struct in6_addr addr;
+
+ return (tor_inet_pton(AF_INET6,string,&addr) == 1);
+}
+
+/** Return true iff <b>string</b> matches a pattern of DNS names
+ * that we allow Tor clients to connect to.
+ */
+int
+string_is_valid_hostname(const char *string)
+{
+ int result = 1;
+ smartlist_t *components;
+
+ components = smartlist_new();
+
+ smartlist_split_string(components,string,".",0,0);
+
+ SMARTLIST_FOREACH_BEGIN(components, char *, c) {
+ if (c[0] == '-') {
+ result = 0;
+ break;
+ }
+
+ do {
+ if ((*c >= 'a' && *c <= 'z') ||
+ (*c >= 'A' && *c <= 'Z') ||
+ (*c >= '0' && *c <= '9') ||
+ (*c == '-'))
+ c++;
+ else
+ result = 0;
+ } while (result && *c);
+
+ } SMARTLIST_FOREACH_END(c);
+
+ SMARTLIST_FOREACH_BEGIN(components, char *, c) {
+ tor_free(c);
+ } SMARTLIST_FOREACH_END(c);
+
+ smartlist_free(components);
+
+ return result;
+}
+
/** Return true iff the DIGEST256_LEN bytes in digest are all zero. */
int
tor_digest256_is_zero(const char *digest)
@@ -1182,9 +1277,14 @@ esc_for_log(const char *s)
}
}
+ tor_assert(len <= SSIZE_MAX);
+
result = outp = tor_malloc(len);
*outp++ = '\"';
for (cp = s; *cp; ++cp) {
+ /* This assertion should always succeed, since we will write at least
+ * one char here, and two chars for closing quote and nul later */
+ tor_assert((outp-result) < (ssize_t)len-2);
switch (*cp) {
case '\\':
case '\"':
@@ -1208,6 +1308,7 @@ esc_for_log(const char *s)
if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) {
*outp++ = *cp;
} else {
+ tor_assert((outp-result) < (ssize_t)len-4);
tor_snprintf(outp, 5, "\\%03o", (int)(uint8_t) *cp);
outp += 4;
}
@@ -1215,6 +1316,7 @@ esc_for_log(const char *s)
}
}
+ tor_assert((outp-result) <= (ssize_t)len-2);
*outp++ = '\"';
*outp++ = 0;
@@ -1343,7 +1445,8 @@ n_leapdays(int y1, int y2)
--y2;
return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
}
-/** Number of days per month in non-leap year; used by tor_timegm. */
+/** Number of days per month in non-leap year; used by tor_timegm and
+ * parse_rfc1123_time. */
static const int days_per_month[] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
@@ -1357,10 +1460,32 @@ tor_timegm(const struct tm *tm, time_t *time_out)
* It's way more brute-force than fiddling with tzset().
*/
time_t year, days, hours, minutes, seconds;
- int i;
- year = tm->tm_year + 1900;
- if (year < 1970 || tm->tm_mon < 0 || tm->tm_mon > 11 ||
- tm->tm_year >= INT32_MAX-1900) {
+ int i, invalid_year, dpm;
+ /* avoid int overflow on addition */
+ if (tm->tm_year < INT32_MAX-1900) {
+ year = tm->tm_year + 1900;
+ } else {
+ /* clamp year */
+ year = INT32_MAX;
+ }
+ invalid_year = (year < 1970 || tm->tm_year >= INT32_MAX-1900);
+
+ if (tm->tm_mon >= 0 && tm->tm_mon <= 11) {
+ dpm = days_per_month[tm->tm_mon];
+ if (tm->tm_mon == 1 && !invalid_year && IS_LEAPYEAR(tm->tm_year)) {
+ dpm = 29;
+ }
+ } else {
+ /* invalid month - default to 0 days per month */
+ dpm = 0;
+ }
+
+ if (invalid_year ||
+ tm->tm_mon < 0 || tm->tm_mon > 11 ||
+ tm->tm_mday < 1 || tm->tm_mday > dpm ||
+ tm->tm_hour < 0 || tm->tm_hour > 23 ||
+ tm->tm_min < 0 || tm->tm_min > 59 ||
+ tm->tm_sec < 0 || tm->tm_sec > 60) {
log_warn(LD_BUG, "Out-of-range argument to tor_timegm");
return -1;
}
@@ -1424,8 +1549,9 @@ parse_rfc1123_time(const char *buf, time_t *t)
struct tm tm;
char month[4];
char weekday[4];
- int i, m;
+ int i, m, invalid_year;
unsigned tm_mday, tm_year, tm_hour, tm_min, tm_sec;
+ unsigned dpm;
if (strlen(buf) != RFC1123_TIME_LEN)
return -1;
@@ -1438,18 +1564,6 @@ parse_rfc1123_time(const char *buf, time_t *t)
tor_free(esc);
return -1;
}
- if (tm_mday < 1 || tm_mday > 31 || tm_hour > 23 || tm_min > 59 ||
- tm_sec > 60 || tm_year >= INT32_MAX || tm_year < 1970) {
- char *esc = esc_for_log(buf);
- log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc);
- tor_free(esc);
- return -1;
- }
- tm.tm_mday = (int)tm_mday;
- tm.tm_year = (int)tm_year;
- tm.tm_hour = (int)tm_hour;
- tm.tm_min = (int)tm_min;
- tm.tm_sec = (int)tm_sec;
m = -1;
for (i = 0; i < 12; ++i) {
@@ -1466,6 +1580,26 @@ parse_rfc1123_time(const char *buf, time_t *t)
}
tm.tm_mon = m;
+ invalid_year = (tm_year >= INT32_MAX || tm_year < 1970);
+ tor_assert(m >= 0 && m <= 11);
+ dpm = days_per_month[m];
+ if (m == 1 && !invalid_year && IS_LEAPYEAR(tm_year)) {
+ dpm = 29;
+ }
+
+ if (invalid_year || tm_mday < 1 || tm_mday > dpm ||
+ tm_hour > 23 || tm_min > 59 || tm_sec > 60) {
+ char *esc = esc_for_log(buf);
+ log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc);
+ tor_free(esc);
+ return -1;
+ }
+ tm.tm_mday = (int)tm_mday;
+ tm.tm_year = (int)tm_year;
+ tm.tm_hour = (int)tm_hour;
+ tm.tm_min = (int)tm_min;
+ tm.tm_sec = (int)tm_sec;
+
if (tm.tm_year < 1970) {
char *esc = esc_for_log(buf);
log_warn(LD_GENERAL,
@@ -1516,7 +1650,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);
}
@@ -1637,7 +1771,11 @@ format_time_interval(char *out, size_t out_len, long interval)
{
/* We only report seconds if there's no hours. */
long sec = 0, min = 0, hour = 0, day = 0;
- if (interval < 0)
+
+ /* -LONG_MIN is LONG_MAX + 1, which causes signed overflow */
+ if (interval < -LONG_MAX)
+ interval = LONG_MAX;
+ else if (interval < 0)
interval = -interval;
if (interval >= 86400) {
@@ -1753,7 +1891,7 @@ write_all(tor_socket_t fd, const char *buf, size_t count, int isSocket)
{
size_t written = 0;
ssize_t result;
- tor_assert(count < SSIZE_T_MAX);
+ tor_assert(count < SSIZE_MAX);
while (written != count) {
if (isSocket)
@@ -1778,7 +1916,7 @@ read_all(tor_socket_t fd, char *buf, size_t count, int isSocket)
size_t numread = 0;
ssize_t result;
- if (count > SIZE_T_CEILING || count > SSIZE_T_MAX)
+ if (count > SIZE_T_CEILING || count > SSIZE_MAX)
return -1;
while (numread != count) {
@@ -1830,6 +1968,7 @@ file_status(const char *fname)
int r;
f = tor_strdup(fname);
clean_name_for_stat(f);
+ log_debug(LD_FS, "stat()ing %s", f);
r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
@@ -1856,8 +1995,12 @@ file_status(const char *fname)
* <b>check</b>&CPD_CHECK, and we think we can create it, return 0. Else
* return -1. If CPD_GROUP_OK is set, then it's okay if the directory
* is group-readable, but in all cases we create the directory mode 0700.
- * If CPD_CHECK_MODE_ONLY is set, then we don't alter the directory permissions
- * if they are too permissive: we just return -1.
+ * If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and
+ * if the directory is created it will use mode 0750 with group read
+ * permission. Group read privileges also assume execute permission
+ * as norm for directories. If CPD_CHECK_MODE_ONLY is set, then we don't
+ * alter the directory permissions if they are too permissive:
+ * we just return -1.
* When effective_user is not NULL, check permissions against the given user
* and its primary group.
*/
@@ -1869,8 +2012,8 @@ check_private_dir(const char *dirname, cpd_check_t check,
struct stat st;
char *f;
#ifndef _WIN32
- int mask;
- struct passwd *pw = NULL;
+ unsigned unwanted_bits = 0;
+ const struct passwd *pw = NULL;
uid_t running_uid;
gid_t running_gid;
#else
@@ -1880,6 +2023,7 @@ check_private_dir(const char *dirname, cpd_check_t check,
tor_assert(dirname);
f = tor_strdup(dirname);
clean_name_for_stat(f);
+ log_debug(LD_FS, "stat()ing %s", f);
r = stat(sandbox_intern_string(f), &st);
tor_free(f);
if (r) {
@@ -1890,10 +2034,14 @@ check_private_dir(const char *dirname, cpd_check_t check,
}
if (check & CPD_CREATE) {
log_info(LD_GENERAL, "Creating directory %s", dirname);
-#if defined (_WIN32) && !defined (WINCE)
+#if defined (_WIN32)
r = mkdir(dirname);
#else
- r = mkdir(dirname, 0700);
+ if (check & CPD_GROUP_READ) {
+ r = mkdir(dirname, 0750);
+ } else {
+ r = mkdir(dirname, 0700);
+ }
#endif
if (r) {
log_warn(LD_FS, "Error creating directory %s: %s", dirname,
@@ -1916,7 +2064,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);
@@ -1930,13 +2078,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?",
@@ -1946,7 +2094,8 @@ check_private_dir(const char *dirname, cpd_check_t check,
tor_free(process_ownername);
return -1;
}
- if ((check & CPD_GROUP_OK) && st.st_gid != running_gid) {
+ if ( (check & (CPD_GROUP_OK|CPD_GROUP_READ))
+ && (st.st_gid != running_gid) ) {
struct group *gr;
char *process_groupname = NULL;
gr = getgrgid(running_gid);
@@ -1961,12 +2110,12 @@ check_private_dir(const char *dirname, cpd_check_t check,
tor_free(process_groupname);
return -1;
}
- if (check & CPD_GROUP_OK) {
- mask = 0027;
+ if (check & (CPD_GROUP_OK|CPD_GROUP_READ)) {
+ unwanted_bits = 0027;
} else {
- mask = 0077;
+ unwanted_bits = 0077;
}
- if (st.st_mode & mask) {
+ if ((st.st_mode & unwanted_bits) != 0) {
unsigned new_mode;
if (check & CPD_CHECK_MODE_ONLY) {
log_warn(LD_FS, "Permissions on directory %s are too permissive.",
@@ -1976,10 +2125,13 @@ check_private_dir(const char *dirname, cpd_check_t check,
log_warn(LD_FS, "Fixing permissions on directory %s", dirname);
new_mode = st.st_mode;
new_mode |= 0700; /* Owner should have rwx */
- new_mode &= ~mask; /* Clear the other bits that we didn't want set...*/
+ if (check & CPD_GROUP_READ) {
+ new_mode |= 0050; /* Group should have rx */
+ }
+ new_mode &= ~unwanted_bits; /* Clear the bits that we didn't want set...*/
if (chmod(dirname, new_mode)) {
log_warn(LD_FS, "Could not chmod directory %s: %s", dirname,
- strerror(errno));
+ strerror(errno));
return -1;
} else {
return 0;
@@ -2002,7 +2154,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);
@@ -2141,6 +2294,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)) {
@@ -2157,7 +2311,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)) {
@@ -2321,6 +2481,7 @@ read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out)
pos += r;
} while (r > 0 && pos < max_bytes_to_read);
+ tor_assert(pos < string_max);
*sz_out = pos;
string[pos] = '\0';
return string;
@@ -2793,10 +2954,14 @@ scan_unsigned(const char **bufp, unsigned long *out, int width, int base)
while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp))
&& scanned_so_far < width) {
int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++);
- unsigned long new_result = result * base + digit;
- if (new_result < result)
- return -1; /* over/underflow. */
- result = new_result;
+ // Check for overflow beforehand, without actually causing any overflow
+ // This preserves functionality on compilers that don't wrap overflow
+ // (i.e. that trap or optimise away overflow)
+ // result * base + digit > ULONG_MAX
+ // result * base > ULONG_MAX - digit
+ if (result > (ULONG_MAX - digit)/base)
+ return -1; /* Processing this digit would overflow */
+ result = result * base + digit;
++scanned_so_far;
}
@@ -2831,10 +2996,17 @@ scan_signed(const char **bufp, long *out, int width)
if (scan_unsigned(bufp, &result, width, 10) < 0)
return -1;
- if (neg) {
+ if (neg && result > 0) {
if (result > ((unsigned long)LONG_MAX) + 1)
return -1; /* Underflow */
- *out = -(long)result;
+ // Avoid overflow on the cast to signed long when result is LONG_MIN
+ // by subtracting 1 from the unsigned long positive value,
+ // then, after it has been cast to signed and negated,
+ // subtracting the original 1 (the double-subtraction is intentional).
+ // Otherwise, the cast to signed could cause a temporary long
+ // to equal LONG_MAX + 1, which is undefined.
+ // We avoid underflow on the subtraction by treating -0 as positive.
+ *out = (-(long)(result - 1)) - 1;
} else {
if (result > LONG_MAX)
return -1; /* Overflow */
@@ -3026,7 +3198,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.
@@ -3367,8 +3539,9 @@ format_win_cmdline_argument(const char *arg)
smartlist_add(arg_chars, (void*)&backslash);
/* Allocate space for argument, quotes (if needed), and terminator */
- formatted_arg = tor_malloc(sizeof(char) *
- (smartlist_len(arg_chars) + (need_quotes?2:0) + 1));
+ const size_t formatted_arg_len = smartlist_len(arg_chars) +
+ (need_quotes ? 2 : 0) + 1;
+ formatted_arg = tor_malloc_zero(formatted_arg_len);
/* Add leading quote */
i=0;
@@ -3450,7 +3623,7 @@ format_number_sigsafe(unsigned long x, char *buf, int buf_len,
cp = buf + len;
*cp = '\0';
do {
- unsigned digit = x % radix;
+ unsigned digit = (unsigned) (x % radix);
tor_assert(cp > buf);
--cp;
*cp = "0123456789ABCDEF"[digit];
@@ -3533,7 +3706,13 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
/* Convert errno to be unsigned for hex conversion */
if (saved_errno < 0) {
- unsigned_errno = (unsigned int) -saved_errno;
+ // Avoid overflow on the cast to unsigned int when result is INT_MIN
+ // by adding 1 to the signed int negative value,
+ // then, after it has been negated and cast to unsigned,
+ // adding the original 1 back (the double-addition is intentional).
+ // Otherwise, the cast to signed could cause a temporary int
+ // to equal INT_MAX + 1, which is undefined.
+ unsigned_errno = ((unsigned int) -(saved_errno + 1)) + 1;
} else {
unsigned_errno = (unsigned int) saved_errno;
}
@@ -3619,13 +3798,7 @@ tor_terminate_process(process_handle_t *process_handle)
{
#ifdef _WIN32
if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) {
- HANDLE handle;
- /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
- attempt to open and terminate the process. */
- handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
- process_handle->pid.dwProcessId);
- if (!handle)
- return -1;
+ HANDLE handle = process_handle->pid.hProcess;
if (!TerminateProcess(handle, 0))
return -1;
@@ -3633,7 +3806,10 @@ tor_terminate_process(process_handle_t *process_handle)
return 0;
}
#else /* Unix */
- return kill(process_handle->pid, SIGTERM);
+ if (process_handle->waitpid_cb) {
+ /* We haven't got a waitpid yet, so we can just kill off the process. */
+ return kill(process_handle->pid, SIGTERM);
+ }
#endif
return -1;
@@ -3682,6 +3858,23 @@ process_handle_new(void)
return out;
}
+#ifndef _WIN32
+/** Invoked when a process that we've launched via tor_spawn_background() has
+ * been found to have terminated.
+ */
+static void
+process_handle_waitpid_cb(int status, void *arg)
+{
+ process_handle_t *process_handle = arg;
+
+ process_handle->waitpid_exit_status = status;
+ clear_waitpid_callback(process_handle->waitpid_cb);
+ if (process_handle->status == PROCESS_STATUS_RUNNING)
+ process_handle->status = PROCESS_STATUS_NOTRUNNING;
+ process_handle->waitpid_cb = 0;
+}
+#endif
+
/**
* @name child-process states
*
@@ -3998,6 +4191,10 @@ tor_spawn_background(const char *const filename, const char **argv,
strerror(errno));
}
+ process_handle->waitpid_cb = set_waitpid_callback(pid,
+ process_handle_waitpid_cb,
+ process_handle);
+
process_handle->stderr_pipe = stderr_pipe[0];
retval = close(stderr_pipe[1]);
@@ -4009,8 +4206,11 @@ tor_spawn_background(const char *const filename, const char **argv,
status = process_handle->status = PROCESS_STATUS_RUNNING;
/* Set stdout/stderr pipes to be non-blocking */
- fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK);
- fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK);
+ if (fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK) < 0 ||
+ fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0) {
+ log_warn(LD_GENERAL, "Failed to set stderror/stdout pipes nonblocking "
+ "in parent process: %s", strerror(errno));
+ }
/* Open the buffered IO streams */
process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r");
process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r");
@@ -4062,6 +4262,8 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
if (process_handle->stderr_handle)
fclose(process_handle->stderr_handle);
+
+ clear_waitpid_callback(process_handle->waitpid_cb);
#endif
memset(process_handle, 0x0f, sizeof(process_handle_t));
@@ -4079,7 +4281,7 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
* probably not work in Tor, because waitpid() is called in main.c to reap any
* terminated child processes.*/
int
-tor_get_exit_code(const process_handle_t *process_handle,
+tor_get_exit_code(process_handle_t *process_handle,
int block, int *exit_code)
{
#ifdef _WIN32
@@ -4119,7 +4321,20 @@ tor_get_exit_code(const process_handle_t *process_handle,
int stat_loc;
int retval;
- retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG);
+ if (process_handle->waitpid_cb) {
+ /* We haven't processed a SIGCHLD yet. */
+ retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG);
+ if (retval == process_handle->pid) {
+ clear_waitpid_callback(process_handle->waitpid_cb);
+ process_handle->waitpid_cb = NULL;
+ process_handle->waitpid_exit_status = stat_loc;
+ }
+ } else {
+ /* We already got a SIGCHLD for this process, and handled it. */
+ retval = process_handle->pid;
+ stat_loc = process_handle->waitpid_exit_status;
+ }
+
if (!block && 0 == retval) {
/* Process has not exited */
return PROCESS_EXIT_RUNNING;
@@ -4329,7 +4544,7 @@ tor_read_all_handle(HANDLE h, char *buf, size_t count,
DWORD byte_count;
BOOL process_exited = FALSE;
- if (count > SIZE_T_CEILING || count > SSIZE_T_MAX)
+ if (count > SIZE_T_CEILING || count > SSIZE_MAX)
return -1;
while (numread != count) {
@@ -4395,7 +4610,7 @@ tor_read_all_handle(FILE *h, char *buf, size_t count,
if (eof)
*eof = 0;
- if (count > SIZE_T_CEILING || count > SSIZE_T_MAX)
+ if (count > SIZE_T_CEILING || count > SSIZE_MAX)
return -1;
while (numread != count) {
@@ -4530,6 +4745,30 @@ 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
@@ -4677,23 +4916,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);
}
@@ -4771,7 +4994,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;
@@ -4782,6 +5005,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);
@@ -4861,7 +5097,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;
@@ -4876,7 +5113,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);
@@ -4942,7 +5179,7 @@ tor_check_port_forwarding(const char *filename,
for each smartlist element (one for "-p" and one for the
ports), and one for the final NULL. */
args_n = 1 + 2*smartlist_len(ports_to_forward) + 1;
- argv = tor_malloc_zero(sizeof(char*)*args_n);
+ argv = tor_calloc(args_n, sizeof(char *));
argv[argv_index++] = filename;
SMARTLIST_FOREACH_BEGIN(ports_to_forward, const char *, port) {
@@ -4991,7 +5228,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 18dc20639f..921dd79da0 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -1,6 +1,6 @@
/* Copyright (c) 2003-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -79,6 +79,7 @@ void *tor_malloc_(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
void *tor_malloc_zero_(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
void *tor_calloc_(size_t nmemb, size_t size DMALLOC_PARAMS) ATTR_MALLOC;
void *tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS);
+void *tor_reallocarray_(void *ptr, size_t size1, size_t size2 DMALLOC_PARAMS);
char *tor_strdup_(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1));
char *tor_strndup_(const char *s, size_t n DMALLOC_PARAMS)
ATTR_MALLOC ATTR_NONNULL((1));
@@ -116,6 +117,8 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
#define tor_malloc_zero(size) tor_malloc_zero_(size DMALLOC_ARGS)
#define tor_calloc(nmemb,size) tor_calloc_(nmemb, size DMALLOC_ARGS)
#define tor_realloc(ptr, size) tor_realloc_(ptr, size DMALLOC_ARGS)
+#define tor_reallocarray(ptr, sz1, sz2) \
+ tor_reallocarray_((ptr), (sz1), (sz2) DMALLOC_ARGS)
#define tor_strdup(s) tor_strdup_(s DMALLOC_ARGS)
#define tor_strndup(s, n) tor_strndup_(s, n DMALLOC_ARGS)
#define tor_memdup(s, n) tor_memdup_(s, n DMALLOC_ARGS)
@@ -224,6 +227,9 @@ 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 string_is_valid_hostname(const char *string);
+int string_is_valid_ipv4_address(const char *string);
+int string_is_valid_ipv6_address(const char *string);
int tor_mem_is_zero(const char *mem, size_t len);
int tor_digest_is_zero(const char *digest);
@@ -341,9 +347,11 @@ typedef unsigned int cpd_check_t;
#define CPD_CREATE 1
#define CPD_CHECK 2
#define CPD_GROUP_OK 4
-#define CPD_CHECK_MODE_ONLY 8
+#define CPD_GROUP_READ 8
+#define CPD_CHECK_MODE_ONLY 16
int check_private_dir(const char *dirname, cpd_check_t check,
const char *effective_user);
+
#define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC)
#define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND)
#define OPEN_FLAGS_DONT_REPLACE (O_CREAT|O_EXCL|O_APPEND|O_WRONLY)
@@ -446,6 +454,7 @@ void set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
#define PROCESS_STATUS_ERROR -1
#ifdef UTIL_PRIVATE
+struct waitpid_callback_t;
/** Structure to represent the state of a process with which Tor is
* communicating. The contents of this structure are private to util.c */
struct process_handle_t {
@@ -461,6 +470,12 @@ struct process_handle_t {
FILE *stdout_handle;
FILE *stderr_handle;
pid_t pid;
+ /** If the process has not given us a SIGCHLD yet, this has the
+ * waitpid_callback_t that gets invoked once it has. Otherwise this
+ * contains NULL. */
+ struct waitpid_callback_t *waitpid_cb;
+ /** The exit status reported by waitpid. */
+ int waitpid_exit_status;
#endif // _WIN32
};
#endif
@@ -469,7 +484,7 @@ struct process_handle_t {
#define PROCESS_EXIT_RUNNING 1
#define PROCESS_EXIT_EXITED 0
#define PROCESS_EXIT_ERROR -1
-int tor_get_exit_code(const process_handle_t *process_handle,
+int tor_get_exit_code(process_handle_t *process_handle,
int block, int *exit_code);
int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
#ifdef _WIN32
diff --git a/src/common/util_process.c b/src/common/util_process.c
new file mode 100644
index 0000000000..1924c19509
--- /dev/null
+++ b/src/common/util_process.c
@@ -0,0 +1,158 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_process.c
+ * \brief utility functions for launching processes and checking their
+ * status. These functions are kept separately from procmon so that they
+ * won't require linking against libevent.
+ **/
+
+#include "orconfig.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include "compat.h"
+#include "util.h"
+#include "torlog.h"
+#include "util_process.h"
+#include "ht.h"
+
+/* ================================================== */
+/* Convenience structures for handlers for waitpid().
+ *
+ * The tor_process_monitor*() code above doesn't use them, since it is for
+ * monitoring a non-child process.
+ */
+
+#ifndef _WIN32
+
+/** Mapping from a PID to a userfn/userdata pair. */
+struct waitpid_callback_t {
+ HT_ENTRY(waitpid_callback_t) node;
+ pid_t pid;
+
+ void (*userfn)(int, void *userdata);
+ void *userdata;
+
+ unsigned running;
+};
+
+static INLINE unsigned int
+process_map_entry_hash_(const waitpid_callback_t *ent)
+{
+ return (unsigned) ent->pid;
+}
+
+static INLINE unsigned int
+process_map_entries_eq_(const waitpid_callback_t *a,
+ const waitpid_callback_t *b)
+{
+ return a->pid == b->pid;
+}
+
+static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
+
+HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
+ process_map_entries_eq_);
+HT_GENERATE2(process_map, waitpid_callback_t, node, process_map_entry_hash_,
+ process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_);
+
+/**
+ * Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for
+ * it. If we eventually do, call <b>fn</b>, passing it the exit status (as
+ * yielded by waitpid) and the pointer <b>arg</b>.
+ *
+ * To cancel this, or clean up after it has triggered, call
+ * clear_waitpid_callback().
+ */
+waitpid_callback_t *
+set_waitpid_callback(pid_t pid, void (*fn)(int, void *), void *arg)
+{
+ waitpid_callback_t *old_ent;
+ waitpid_callback_t *ent = tor_malloc_zero(sizeof(waitpid_callback_t));
+ ent->pid = pid;
+ ent->userfn = fn;
+ ent->userdata = arg;
+ ent->running = 1;
+
+ old_ent = HT_REPLACE(process_map, &process_map, ent);
+ if (old_ent) {
+ log_warn(LD_BUG, "Replaced a waitpid monitor on pid %u. That should be "
+ "impossible.", (unsigned) pid);
+ old_ent->running = 0;
+ }
+
+ return ent;
+}
+
+/**
+ * Cancel a waitpid_callback_t, or clean up after one has triggered. Releases
+ * all storage held by <b>ent</b>.
+ */
+void
+clear_waitpid_callback(waitpid_callback_t *ent)
+{
+ waitpid_callback_t *old_ent;
+ if (ent == NULL)
+ return;
+
+ if (ent->running) {
+ old_ent = HT_REMOVE(process_map, &process_map, ent);
+ if (old_ent != ent) {
+ log_warn(LD_BUG, "Couldn't remove waitpid monitor for pid %u.",
+ (unsigned) ent->pid);
+ return;
+ }
+ }
+
+ tor_free(ent);
+}
+
+/** Helper: find the callack for <b>pid</b>; if there is one, run it,
+ * reporting the exit status as <b>status</b>. */
+static void
+notify_waitpid_callback_by_pid(pid_t pid, int status)
+{
+ waitpid_callback_t search, *ent;
+
+ search.pid = pid;
+ ent = HT_REMOVE(process_map, &process_map, &search);
+ if (!ent || !ent->running) {
+ log_info(LD_GENERAL, "Child process %u has exited; no callback was "
+ "registered", (unsigned)pid);
+ return;
+ }
+
+ log_info(LD_GENERAL, "Child process %u has exited; running callback.",
+ (unsigned)pid);
+
+ ent->running = 0;
+ ent->userfn(status, ent->userdata);
+}
+
+/** Use waitpid() to wait for all children that have exited, and invoke any
+ * callbacks registered for them. */
+void
+notify_pending_waitpid_callbacks(void)
+{
+ /* I was going to call this function reap_zombie_children(), but
+ * that makes it sound way more exciting than it really is. */
+ pid_t child;
+ int status = 0;
+
+ while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
+ notify_waitpid_callback_by_pid(child, status);
+ status = 0; /* should be needless */
+ }
+}
+
+#endif
+
diff --git a/src/common/util_process.h b/src/common/util_process.h
new file mode 100644
index 0000000000..e7c55ed33d
--- /dev/null
+++ b/src/common/util_process.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2011-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_process.h
+ * \brief Headers for util_process.c
+ **/
+
+#ifndef TOR_UTIL_PROCESS_H
+#define TOR_UTIL_PROCESS_H
+
+#ifndef _WIN32
+/** A callback structure waiting for us to get a SIGCHLD informing us that a
+ * PID has been closed. Created by set_waitpid_callback. Cancelled or cleaned-
+ * up from clear_waitpid_callback(). Do not access outside of the main thread;
+ * do not access from inside a signal handler. */
+typedef struct waitpid_callback_t waitpid_callback_t;
+
+waitpid_callback_t *set_waitpid_callback(pid_t pid,
+ void (*fn)(int, void *), void *arg);
+void clear_waitpid_callback(waitpid_callback_t *ent);
+void notify_pending_waitpid_callbacks(void);
+#endif
+
+#endif
+