diff options
Diffstat (limited to 'src/common')
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> > 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> > 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 + |