diff options
Diffstat (limited to 'src')
57 files changed, 3401 insertions, 1124 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 2244fe58d..2920e73d2 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -56,9 +56,9 @@ noinst_HEADERS = \ common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) if test "@SHA1SUM@" != none; then \ - @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ + (cd "$(srcdir)" && @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ elif test "@OPENSSL@" != none; then \ - @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ + (cd "$(srcdir)" && @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ else \ rm common_sha1.i; \ touch common_sha1.i; \ diff --git a/src/common/address.c b/src/common/address.c index f40e428cc..ab056f427 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -962,8 +962,11 @@ char * tor_dup_addr(const tor_addr_t *addr) { char buf[TOR_ADDR_BUF_LEN]; - tor_addr_to_str(buf, addr, sizeof(buf), 0); - return tor_strdup(buf); + if (tor_addr_to_str(buf, addr, sizeof(buf), 0)) { + return tor_strdup(buf); + } else { + return tor_strdup("<unknown address type>"); + } } /** Return a string representing the address <b>addr</b>. This string is diff --git a/src/common/address.h b/src/common/address.h index 359b0264d..4568c32bf 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -31,6 +31,13 @@ typedef struct tor_addr_t } addr; } tor_addr_t; +/** Holds an IP address and a TCP/UDP port. */ +typedef struct tor_addr_port_t +{ + tor_addr_t addr; + uint16_t port; +} tor_addr_port_t; + static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a); @@ -149,7 +156,7 @@ int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, unsigned int 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) ATTR_PURE; +int tor_addr_is_internal(const tor_addr_t *ip, int for_listening); /** Longest length that can be required for a reverse lookup name. */ /* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */ @@ -185,7 +192,7 @@ int tor_addr_port_split(int severity, const char *addrport, char **address_out, uint16_t *port_out); /* IPv4 helpers */ -int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE; +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 9c03b8085..cec689981 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -14,34 +14,39 @@ #include <assert.h> #include <stdlib.h> #include <string.h> +#include <openssl/aes.h> +#include <openssl/evp.h> +#include <openssl/engine.h> +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +/* See comments about which counter mode implementation to use below. */ +#include <openssl/modes.h> +#define USE_OPENSSL_CTR +#endif #include "compat.h" #include "aes.h" #include "util.h" #include "torlog.h" -/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function, - * via OpenSSL's EVP_EncryptUpdate function. */ - -/** Defined iff we're using OpenSSL's AES functions for AES. */ -#undef USE_OPENSSL_AES -/** Defined iff we're using OpenSSL's EVP code for AES. */ -#undef USE_OPENSSL_EVP - -/* Here we pick which to use, if none is force-defined above */ -#if (!defined(USE_OPENSSL_AES) && \ - !defined(USE_OPENSSL_EVP)) - -#define USE_OPENSSL_EVP - +#ifdef ANDROID +/* Android's OpenSSL seems to have removed all of its Engine support. */ +#define DISABLE_ENGINES #endif -/* Include OpenSSL headers as needed. */ -#ifdef USE_OPENSSL_AES -# include <openssl/aes.h> -#endif -#ifdef USE_OPENSSL_EVP -# include <openssl/evp.h> -#endif +/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function, + * via OpenSSL's EVP_EncryptUpdate function. + * + * If there's any hardware acceleration in play, we want to be using EVP_* so + * we can get it. Otherwise, we'll want AES_*, which seems to be about 5% + * faster than indirecting through the EVP layer. + */ + +/* We have 2 strategies for counter mode: use our own, or use OpenSSL's. + * + * Here we have a counter mode that's faster than the one shipping with + * OpenSSL pre-1.0 (by about 10%!). But OpenSSL 1.0.0 added a counter mode + * implementation faster than the one here (by about 7%). So we pick which + * one to used based on the Openssl version above. + */ /*======================================================================*/ /* Interface to AES code, and counter implementation */ @@ -49,13 +54,12 @@ /** Implements an AES counter-mode cipher. */ struct aes_cnt_cipher { /** This next element (however it's defined) is the AES key. */ -#if defined(USE_OPENSSL_EVP) - EVP_CIPHER_CTX key; -#elif defined(USE_OPENSSL_AES) - AES_KEY key; -#endif + union { + EVP_CIPHER_CTX evp; + AES_KEY aes; + } key; -#if !defined(WORDS_BIGENDIAN) +#if !defined(WORDS_BIGENDIAN) && !defined(USE_OPENSSL_CTR) #define USING_COUNTER_VARS /** These four values, together, implement a 128-bit counter, with * counter0 as the low-order word and counter3 as the high-order word. */ @@ -77,9 +81,51 @@ struct aes_cnt_cipher { /** The encrypted value of ctr_buf. */ uint8_t buf[16]; /** Our current stream position within buf. */ +#ifdef USE_OPENSSL_CTR + unsigned int pos; +#else uint8_t pos; +#endif + + /** True iff we're using the evp implementation of this cipher. */ + uint8_t using_evp; }; +/** True if we should prefer the EVP implementation for AES, either because + * we're testing it or because we have hardware acceleration configured */ +static int should_use_EVP = 0; + +/** Check whether we should use the EVP interface for AES. If <b>force_val</b> + * is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP + * if there is an engine enabled for aes-ecb. */ +int +evaluate_evp_for_aes(int force_val) +{ + ENGINE *e; + + if (force_val >= 0) { + should_use_EVP = force_val; + return 0; + } +#ifdef DISABLE_ENGINES + should_use_EVP = 0; +#else + e = ENGINE_get_cipher_engine(NID_aes_128_ecb); + + if (e) { + log_notice(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", + ENGINE_get_name(e)); + should_use_EVP = 1; + } else { + log_notice(LD_CRYPTO, "No AES engine found; using AES_* functions."); + should_use_EVP = 0; + } +#endif + + return 0; +} + +#ifndef USE_OPENSSL_CTR #if !defined(USING_COUNTER_VARS) #define COUNTER(c, n) ((c)->ctr_buf.buf32[3-(n)]) #else @@ -100,16 +146,15 @@ _aes_fill_buf(aes_cnt_cipher_t *cipher) * None of these issues are insurmountable in principle. */ -#if defined(USE_OPENSSL_EVP) - { + if (cipher->using_evp) { int outl=16, inl=16; - EVP_EncryptUpdate(&cipher->key, cipher->buf, &outl, + EVP_EncryptUpdate(&cipher->key.evp, cipher->buf, &outl, cipher->ctr_buf.buf, inl); + } else { + AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key.aes); } -#elif defined(USE_OPENSSL_AES) - AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key); -#endif } +#endif /** * Return a newly allocated counter-mode AES128 cipher implementation. @@ -129,18 +174,21 @@ aes_new_cipher(void) void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) { -#if defined(USE_OPENSSL_EVP) - const EVP_CIPHER *c; - switch (key_bits) { - case 128: c = EVP_aes_128_ecb(); break; - case 192: c = EVP_aes_192_ecb(); break; - case 256: c = EVP_aes_256_ecb(); break; - default: tor_assert(0); + if (should_use_EVP) { + const EVP_CIPHER *c; + switch (key_bits) { + case 128: c = EVP_aes_128_ecb(); break; + case 192: c = EVP_aes_192_ecb(); break; + case 256: c = EVP_aes_256_ecb(); break; + default: tor_assert(0); + } + EVP_EncryptInit(&cipher->key.evp, c, (const unsigned char*)key, NULL); + cipher->using_evp = 1; + } else { + AES_set_encrypt_key((const unsigned char *)key, key_bits, &cipher->key.aes); + cipher->using_evp = 0; } - EVP_EncryptInit(&cipher->key, c, (const unsigned char*)key, NULL); -#elif defined(USE_OPENSSL_AES) - AES_set_encrypt_key((const unsigned char *)key, key_bits, &(cipher->key)); -#endif + #ifdef USING_COUNTER_VARS cipher->counter0 = 0; cipher->counter1 = 0; @@ -151,7 +199,12 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf)); cipher->pos = 0; + +#ifdef USE_OPENSSL_CTR + memset(cipher->buf, 0, sizeof(cipher->buf)); +#else _aes_fill_buf(cipher); +#endif } /** Release storage held by <b>cipher</b> @@ -161,9 +214,9 @@ aes_free_cipher(aes_cnt_cipher_t *cipher) { if (!cipher) return; -#ifdef USE_OPENSSL_EVP - EVP_CIPHER_CTX_cleanup(&cipher->key); -#endif + if (cipher->using_evp) { + EVP_CIPHER_CTX_cleanup(&cipher->key.evp); + } memset(cipher, 0, sizeof(aes_cnt_cipher_t)); tor_free(cipher); } @@ -176,6 +229,18 @@ aes_free_cipher(aes_cnt_cipher_t *cipher) #define UPDATE_CTR_BUF(c, n) #endif +#ifdef USE_OPENSSL_CTR +/* Helper function to use EVP with openssl's counter-mode wrapper. */ +static void evp_block128_fn(const uint8_t in[16], + uint8_t out[16], + const void *key) +{ + EVP_CIPHER_CTX *ctx = (void*)key; + int inl=16, outl=16; + EVP_EncryptUpdate(ctx, out, &outl, in, inl); +} +#endif + /** Encrypt <b>len</b> bytes from <b>input</b>, storing the result in * <b>output</b>. Uses the key in <b>cipher</b>, and advances the counter * by <b>len</b> bytes as it encrypts. @@ -184,20 +249,29 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, char *output) { - /* This function alone is up to 5% of our runtime in some profiles; anything - * we could do to make it faster would be great. - * - * Experimenting suggests that unrolling the inner loop into a switch - * statement doesn't help. What does seem to help is making the input and - * output buffers word aligned, and never crypting anything besides an - * integer number of words at a time -- it shaves maybe 4-5% of the per-byte - * encryption time measured by bench_aes. We can't do that with the current - * Tor protocol, though: Tor really likes to crypt things in 509-byte - * chunks. - * - * If we were really ambitous, we'd force len to be a multiple of the block - * size, and shave maybe another 4-5% off. - */ +#ifdef USE_OPENSSL_CTR + if (cipher->using_evp) { + /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If + * it weren't disabled, it might be better just to use that. + */ + CRYPTO_ctr128_encrypt((const unsigned char *)input, + (unsigned char *)output, + len, + &cipher->key.evp, + cipher->ctr_buf.buf, + cipher->buf, + &cipher->pos, + evp_block128_fn); + } else { + AES_ctr128_encrypt((const unsigned char *)input, + (unsigned char *)output, + len, + &cipher->key.aes, + cipher->ctr_buf.buf, + cipher->buf, + &cipher->pos); + } +#else int c = cipher->pos; if (PREDICT_UNLIKELY(!len)) return; @@ -220,6 +294,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, UPDATE_CTR_BUF(cipher, 0); _aes_fill_buf(cipher); } +#endif } /** Encrypt <b>len</b> bytes from <b>input</b>, storing the results in place. @@ -229,11 +304,9 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) { - - /* XXXX This function is up to 5% of our runtime in some profiles; - * we should look into unrolling some of the loops; taking advantage - * of alignment, using a bigger buffer, and so on. Not till after 0.1.2.x, - * though. */ +#ifdef USE_OPENSSL_CTR + aes_crypt(cipher, data, len, data); +#else int c = cipher->pos; if (PREDICT_UNLIKELY(!len)) return; @@ -256,6 +329,7 @@ aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) UPDATE_CTR_BUF(cipher, 0); _aes_fill_buf(cipher); } +#endif } /** Reset the 128-bit counter of <b>cipher</b> to the 16-bit big-endian value @@ -272,6 +346,8 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) cipher->pos = 0; memcpy(cipher->ctr_buf.buf, iv, 16); +#ifndef USE_OPENSSL_CTR _aes_fill_buf(cipher); +#endif } diff --git a/src/common/aes.h b/src/common/aes.h index b2591942b..221e84615 100644 --- a/src/common/aes.h +++ b/src/common/aes.h @@ -24,5 +24,7 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len); void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv); +int evaluate_evp_for_aes(int force_value); + #endif diff --git a/src/common/compat.c b/src/common/compat.c index 9a2c9d764..ea95f9f08 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -58,6 +58,14 @@ #endif #endif +/* Includes for the process attaching prevention */ +#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) +#include <sys/prctl.h> +#elif defined(__APPLE__) +#include <sys/types.h> +#include <sys/ptrace.h> +#endif + #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -1519,6 +1527,57 @@ switch_id(const char *user) #endif } +/* We only use the linux prctl for now. There is no Win32 support; this may + * also work on various BSD systems and Mac OS X - send testing feedback! + * + * On recent Gnu/Linux kernels it is possible to create a system-wide policy + * that will prevent non-root processes from attaching to other processes + * unless they are the parent process; thus gdb can attach to programs that + * they execute but they cannot attach to other processes running as the same + * user. The system wide policy may be set with the sysctl + * kernel.yama.ptrace_scope or by inspecting + * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04. + * + * This ptrace scope will be ignored on Gnu/Linux for users with + * CAP_SYS_PTRACE and so it is very likely that root will still be able to + * attach to the Tor process. + */ +/** Attempt to disable debugger attachment: return 0 on success, -1 on + * failure. */ +int +tor_disable_debugger_attach(void) +{ + int r, attempted; + r = -1; + attempted = 0; + log_debug(LD_CONFIG, + "Attemping to disable debugger attachment to Tor for " + "unprivileged users."); +#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL) +#ifdef PR_SET_DUMPABLE + attempted = 1; + r = prctl(PR_SET_DUMPABLE, 0); +#endif +#endif +#if defined(__APPLE__) && defined(PT_DENY_ATTACH) + if (r < 0) { + attempted = 1; + r = ptrace(PT_DENY_ATTACH, 0, 0, 0); + } +#endif + + // XXX: TODO - Mac OS X has dtrace and this may be disabled. + // XXX: TODO - Windows probably has something similar + if (r == 0) { + log_debug(LD_CONFIG,"Debugger attachment disabled for " + "unprivileged users."); + } else if (attempted) { + log_warn(LD_CONFIG, "Unable to disable ptrace attach: %s", + strerror(errno)); + } + return r; +} + #ifdef HAVE_PWD_H /** Allocate and return a string containing the home directory for the * user <b>username</b>. Only works on posix-like systems. */ diff --git a/src/common/compat.h b/src/common/compat.h index b005dd297..db541623d 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -135,7 +135,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) { /* GCC has several useful attributes. */ #if defined(__GNUC__) && __GNUC__ >= 3 #define ATTR_NORETURN __attribute__((noreturn)) -#define ATTR_PURE __attribute__((pure)) #define ATTR_CONST __attribute__((const)) #define ATTR_MALLOC __attribute__((malloc)) #define ATTR_NORETURN __attribute__((noreturn)) @@ -168,7 +167,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) { #define PREDICT_UNLIKELY(exp) __builtin_expect(!!(exp), 0) #else #define ATTR_NORETURN -#define ATTR_PURE #define ATTR_CONST #define ATTR_MALLOC #define ATTR_NORETURN @@ -271,9 +269,9 @@ int tor_asprintf(char **strp, const char *fmt, ...) int tor_vasprintf(char **strp, const char *fmt, va_list args); const void *tor_memmem(const void *haystack, size_t hlen, const void *needle, - size_t nlen) ATTR_PURE ATTR_NONNULL((1,3)); + size_t nlen) ATTR_NONNULL((1,3)); static const void *tor_memstr(const void *haystack, size_t hlen, - const char *needle) ATTR_PURE ATTR_NONNULL((1,3)); + const char *needle) ATTR_NONNULL((1,3)); static INLINE const void * tor_memstr(const void *haystack, size_t hlen, const char *needle) { @@ -546,9 +544,9 @@ long tor_weak_random(void); /* ===== OS compatibility */ const char *get_uname(void); -uint16_t get_uint16(const void *cp) ATTR_PURE ATTR_NONNULL((1)); -uint32_t get_uint32(const void *cp) ATTR_PURE ATTR_NONNULL((1)); -uint64_t get_uint64(const void *cp) ATTR_PURE ATTR_NONNULL((1)); +uint16_t get_uint16(const void *cp) ATTR_NONNULL((1)); +uint32_t get_uint32(const void *cp) ATTR_NONNULL((1)); +uint64_t get_uint64(const void *cp) ATTR_NONNULL((1)); void set_uint16(void *cp, uint16_t v) ATTR_NONNULL((1)); void set_uint32(void *cp, uint32_t v) ATTR_NONNULL((1)); void set_uint64(void *cp, uint64_t v) ATTR_NONNULL((1)); @@ -566,6 +564,7 @@ set_uint8(void *cp, uint8_t v) typedef unsigned long rlim_t; #endif int set_max_file_descriptors(rlim_t limit, int *max); +int tor_disable_debugger_attach(void); int switch_id(const char *user); #ifdef HAVE_PWD_H char *get_user_homedir(const char *username); diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 0cedef8d5..67f465927 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -558,6 +558,60 @@ tor_check_libevent_header_compatibility(void) #endif } +struct tor_libevent_action_t { + struct event *ev; + void (*cb)(void *arg); + void *arg; +}; + +/** Callback for tor_run_in_libevent_loop */ +static void +run_runnable_cb(evutil_socket_t s, short what, void *arg) +{ + tor_libevent_action_t *r = arg; + void (*cb)(void *) = r->cb; + void *cb_arg = r->arg; + (void)what; + (void)s; + tor_event_free(r->ev); + tor_free(r); + + cb(cb_arg); +} + +/** Cause cb(arg) to run later on this iteration of the libevent loop, or in + * the next iteration of the libevent loop. This is useful for when you're + * deep inside a no-reentrant code and there's some function you want to call + * without worrying about whether it might cause reeentrant invocation. + */ +tor_libevent_action_t * +tor_run_in_libevent_loop(void (*cb)(void *arg), void *arg) +{ + tor_libevent_action_t *r = tor_malloc(sizeof(tor_libevent_action_t)); + r->cb = cb; + r->arg = arg; + r->ev = tor_event_new(tor_libevent_get_base(), -1, EV_TIMEOUT, + run_runnable_cb, r); + if (!r->ev) { + tor_free(r); + return NULL; + } + /* Make the event active immediately. */ + event_active(r->ev, EV_TIMEOUT, 1); + + return r; +} + +/** + * Cancel <b>action</b> without running it. + */ +void +tor_cancel_libevent_action(tor_libevent_action_t *action) +{ + tor_event_free(action->ev); + tor_free(action); +} + /* If possible, we're going to try to use Libevent's periodic timer support, since it does a pretty good job of making sure that periodic events get diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 024729717..4076cc0e0 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -44,8 +44,12 @@ void tor_event_free(struct event *ev); #define tor_evdns_add_server_port evdns_add_server_port #endif -typedef struct periodic_timer_t periodic_timer_t; +typedef struct tor_libevent_action_t tor_libevent_action_t; +tor_libevent_action_t *tor_run_in_libevent_loop(void (*cb)(void *arg), + void *arg); +void tor_cancel_libevent_action(tor_libevent_action_t *action); +typedef struct periodic_timer_t periodic_timer_t; periodic_timer_t *periodic_timer_new(struct event_base *base, const struct timeval *tv, void (*cb)(periodic_timer_t *timer, void *data), diff --git a/src/common/container.h b/src/common/container.h index 4a6eba789..fe071cc1b 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -35,19 +35,14 @@ void smartlist_remove(smartlist_t *sl, const void *element); void *smartlist_pop_last(smartlist_t *sl); void smartlist_reverse(smartlist_t *sl); void smartlist_string_remove(smartlist_t *sl, const char *element); -int smartlist_isin(const smartlist_t *sl, const void *element) ATTR_PURE; -int smartlist_string_isin(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_string_pos(const smartlist_t *, const char *elt) ATTR_PURE; -int smartlist_string_isin_case(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE; -int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) - ATTR_PURE; -int smartlist_digest_isin(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) - ATTR_PURE; +int smartlist_isin(const smartlist_t *sl, const void *element); +int smartlist_string_isin(const smartlist_t *sl, const char *element); +int smartlist_string_pos(const smartlist_t *, const char *elt); +int smartlist_string_isin_case(const smartlist_t *sl, const char *element); +int smartlist_string_num_isin(const smartlist_t *sl, int num); +int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2); +int smartlist_digest_isin(const smartlist_t *sl, const char *element); +int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); @@ -55,14 +50,14 @@ void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); #ifdef DEBUG_SMARTLIST /** Return the number of items in sl. */ -static INLINE int smartlist_len(const smartlist_t *sl) ATTR_PURE; +static INLINE int smartlist_len(const smartlist_t *sl); static INLINE int smartlist_len(const smartlist_t *sl) { tor_assert(sl); return (sl)->num_used; } /** Return the <b>idx</b>th element of sl. */ -static INLINE void *smartlist_get(const smartlist_t *sl, int idx) ATTR_PURE; +static INLINE void *smartlist_get(const smartlist_t *sl, int idx); static INLINE void *smartlist_get(const smartlist_t *sl, int idx) { tor_assert(sl); tor_assert(idx>=0); @@ -114,8 +109,7 @@ void smartlist_uniq_strings(smartlist_t *sl); void smartlist_uniq_digests(smartlist_t *sl); void smartlist_uniq_digests256(smartlist_t *sl); void *smartlist_bsearch(smartlist_t *sl, const void *key, - int (*compare)(const void *key, const void **member)) - ATTR_PURE; + int (*compare)(const void *key, const void **member)); int smartlist_bsearch_idx(const smartlist_t *sl, const void *key, int (*compare)(const void *key, const void **member), int *found_out); diff --git a/src/common/crypto.c b/src/common/crypto.c index f2ef83352..62b0bcec6 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -276,6 +276,9 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) } else { log_info(LD_CRYPTO, "NOT using OpenSSL engine support."); } + + evaluate_evp_for_aes(-1); + return crypto_seed_rng(1); } return 0; @@ -288,37 +291,6 @@ crypto_thread_cleanup(void) ERR_remove_state(0); } -/** Uninitialize the crypto library. Return 0 on success, -1 on failure. - */ -int -crypto_global_cleanup(void) -{ - EVP_cleanup(); - ERR_remove_state(0); - ERR_free_strings(); - -#ifndef DISABLE_ENGINES - ENGINE_cleanup(); -#endif - - 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; - int i; - _openssl_mutexes = NULL; - _n_openssl_mutexes = 0; - for (i=0;i<n;++i) { - tor_mutex_free(ms[i]); - } - tor_free(ms); - } -#endif - return 0; -} - /** used by tortls.c: wrap an RSA* in a crypto_pk_env_t. */ crypto_pk_env_t * _crypto_new_pk_env_rsa(RSA *rsa) @@ -1809,6 +1781,9 @@ crypto_hmac_sha256(char *hmac_out, /* DH */ +/** Our DH 'g' parameter */ +#define DH_GENERATOR 2 + /** Shared P parameter for our circuit-crypto DH key exchanges. */ static BIGNUM *dh_param_p = NULL; /** Shared P parameter for our TLS DH key exchanges. */ @@ -1816,49 +1791,303 @@ static BIGNUM *dh_param_p_tls = NULL; /** Shared G parameter for our DH key exchanges. */ static BIGNUM *dh_param_g = NULL; +/** Generate and return a reasonable and safe DH parameter p. */ +static BIGNUM * +crypto_generate_dynamic_dh_modulus(void) +{ + BIGNUM *dynamic_dh_modulus; + DH *dh_parameters; + int r, dh_codes; + char *s; + + dynamic_dh_modulus = BN_new(); + tor_assert(dynamic_dh_modulus); + + dh_parameters = DH_generate_parameters(DH_BYTES*8, DH_GENERATOR, NULL, NULL); + tor_assert(dh_parameters); + + r = DH_check(dh_parameters, &dh_codes); + tor_assert(r && !dh_codes); + + BN_copy(dynamic_dh_modulus, dh_parameters->p); + tor_assert(dynamic_dh_modulus); + + DH_free(dh_parameters); + + { /* log the dynamic DH modulus: */ + s = BN_bn2hex(dynamic_dh_modulus); + tor_assert(s); + log_info(LD_OR, "Dynamic DH modulus generated: [%s]", s); + OPENSSL_free(s); + } + + return dynamic_dh_modulus; +} + +/** Store our dynamic DH modulus (and its group parameters) to + <b>fname</b> for future use. */ +static int +crypto_store_dynamic_dh_modulus(const char *fname) +{ + int len, new_len; + DH *dh = NULL; + unsigned char *dh_string_repr = NULL, *cp = NULL; + char *base64_encoded_dh = NULL; + int retval = -1; + + tor_assert(fname); + + if (!dh_param_p_tls) { + log_info(LD_CRYPTO, "Tried to store a DH modulus that does not exist."); + goto done; + } + + if (!(dh = DH_new())) + goto done; + if (!(dh->p = BN_dup(dh_param_p_tls))) + goto done; + if (!(dh->g = BN_new())) + goto done; + if (!BN_set_word(dh->g, DH_GENERATOR)) + goto done; + + len = i2d_DHparams(dh, NULL); + if (len < 0) { + log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (1)."); + goto done; + } + + cp = dh_string_repr = tor_malloc_zero(len+1); + len = i2d_DHparams(dh, &cp); + if ((len < 0) || ((cp - dh_string_repr) != len)) { + log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2)."); + goto done; + } + + base64_encoded_dh = tor_malloc_zero(len * 2); /* should be enough */ + new_len = base64_encode(base64_encoded_dh, len * 2, + (char *)dh_string_repr, len); + if (new_len < 0) { + log_warn(LD_CRYPTO, "Error occured while base64-encoding DH modulus."); + goto done; + } + + if (write_bytes_to_new_file(fname, base64_encoded_dh, new_len, 0) < 0) { + log_info(LD_CRYPTO, "'%s' was already occupied.", fname); + goto done; + } + + retval = 0; + + done: + if (dh) + DH_free(dh); + tor_free(dh_string_repr); + tor_free(base64_encoded_dh); + + return retval; +} + +/** Return the dynamic DH modulus stored in <b>fname</b>. If there is no + dynamic DH modulus stored in <b>fname</b>, return NULL. */ +static BIGNUM * +crypto_get_stored_dynamic_dh_modulus(const char *fname) +{ + int retval; + char *contents = NULL; + int dh_codes; + char *fname_new = NULL; + DH *stored_dh = NULL; + BIGNUM *dynamic_dh_modulus = NULL; + int length = 0; + unsigned char *base64_decoded_dh = NULL; + const unsigned char *cp = NULL; + + tor_assert(fname); + + contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); + if (!contents) { + log_info(LD_CRYPTO, "Could not open file '%s'", fname); + goto done; /*usually means that ENOENT. don't try to move file to broken.*/ + } + + /* 'fname' contains the DH parameters stored in base64-ed DER + format. We are only interested in the DH modulus. */ + + cp = base64_decoded_dh = tor_malloc_zero(strlen(contents)); + length = base64_decode((char *)base64_decoded_dh, strlen(contents), + contents, strlen(contents)); + if (length < 0) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (base64)."); + goto err; + } + + stored_dh = d2i_DHparams(NULL, &cp, length); + if ((!stored_dh) || (cp - base64_decoded_dh != length)) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (d2i)."); + goto err; + } + + { /* check the cryptographic qualities of the stored dynamic DH modulus: */ + retval = DH_check(stored_dh, &dh_codes); + if (!retval || dh_codes) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus is not a safe prime."); + goto err; + } + + retval = DH_size(stored_dh); + if (retval < DH_BYTES) { + log_warn(LD_CRYPTO, "Stored dynamic DH modulus is smaller " + "than '%d' bits.", DH_BYTES*8); + goto err; + } + + if (!BN_is_word(stored_dh->g, 2)) { + log_warn(LD_CRYPTO, "Stored dynamic DH parameters do not use '2' " + "as the group generator."); + goto err; + } + } + + { /* log the dynamic DH modulus: */ + char *s = BN_bn2hex(stored_dh->p); + tor_assert(s); + log_info(LD_OR, "Found stored dynamic DH modulus: [%s]", s); + OPENSSL_free(s); + } + + goto done; + + err: + + { /* move broken prime to $filename.broken */ + fname_new = tor_malloc(strlen(fname) + 8); + + /* no can do if these functions return error */ + strlcpy(fname_new, fname, strlen(fname) + 8); + strlcat(fname_new, ".broken", strlen(fname) + 8); + + log_warn(LD_CRYPTO, "Moving broken dynamic DH prime to '%s'.", fname_new); + + if (replace_file(fname, fname_new)) + log_notice(LD_CRYPTO, "Error while moving '%s' to '%s'.", + fname, fname_new); + + tor_free(fname_new); + } + + if (stored_dh) { + DH_free(stored_dh); + stored_dh = NULL; + } + + done: + tor_free(contents); + tor_free(base64_decoded_dh); + + if (stored_dh) { + dynamic_dh_modulus = BN_dup(stored_dh->p); + DH_free(stored_dh); + } + + return dynamic_dh_modulus; +} + +/** Set the global TLS Diffie-Hellman modulus. + * If <b>dynamic_dh_modulus_fname</b> is set, try to read a dynamic DH modulus + * off it and use it as the DH modulus. If that's not possible, + * generate a new dynamic DH modulus. + * If <b>dynamic_dh_modulus_fname</b> is NULL, use the Apache mod_ssl DH + * modulus. */ +void +crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname) +{ + BIGNUM *tls_prime = NULL; + int store_dh_prime_afterwards = 0; + int r; + + /* If the space is occupied, free the previous TLS DH prime */ + if (dh_param_p_tls) { + BN_free(dh_param_p_tls); + dh_param_p_tls = NULL; + } + + if (dynamic_dh_modulus_fname) { /* use dynamic DH modulus: */ + log_info(LD_OR, "Using stored dynamic DH modulus."); + tls_prime = crypto_get_stored_dynamic_dh_modulus(dynamic_dh_modulus_fname); + + if (!tls_prime) { + log_notice(LD_OR, "Generating fresh dynamic DH modulus. " + "This might take a while..."); + tls_prime = crypto_generate_dynamic_dh_modulus(); + + store_dh_prime_afterwards++; + } + } else { /* use the static DH prime modulus used by Apache in mod_ssl: */ + tls_prime = BN_new(); + tor_assert(tls_prime); + + /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see + * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this + * prime. + */ + r =BN_hex2bn(&tls_prime, + "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98" + "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A" + "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7" + "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68" + "B0E7393E0F24218EB3"); + tor_assert(r); + } + + tor_assert(tls_prime); + + dh_param_p_tls = tls_prime; + + if (store_dh_prime_afterwards) + /* save the new dynamic DH modulus to disk. */ + if (crypto_store_dynamic_dh_modulus(dynamic_dh_modulus_fname)) { + log_notice(LD_CRYPTO, "Failed while storing dynamic DH modulus. " + "Make sure your data directory is sane."); + } +} + /** Initialize dh_param_p and dh_param_g if they are not already * set. */ static void init_dh_param(void) { - BIGNUM *p, *p2, *g; + BIGNUM *circuit_dh_prime, *generator; int r; - if (dh_param_p && dh_param_g && dh_param_p_tls) + if (dh_param_p && dh_param_g) return; - p = BN_new(); - p2 = BN_new(); - g = BN_new(); - tor_assert(p); - tor_assert(p2); - tor_assert(g); + circuit_dh_prime = BN_new(); + generator = BN_new(); + tor_assert(circuit_dh_prime && generator); + + /* Set our generator for all DH parameters */ + r = BN_set_word(generator, DH_GENERATOR); + tor_assert(r); /* This is from rfc2409, section 6.2. It's a safe prime, and supposedly it equals: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }. */ - r = BN_hex2bn(&p, + r = BN_hex2bn(&circuit_dh_prime, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" "49286651ECE65381FFFFFFFFFFFFFFFF"); tor_assert(r); - /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see - * modules/ssl/ssl_engine_dh.c */ - r = BN_hex2bn(&p2, - "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98" - "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A" - "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7" - "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68" - "B0E7393E0F24218EB3"); - tor_assert(r); - r = BN_set_word(g, 2); - tor_assert(r); - dh_param_p = p; - dh_param_p_tls = p2; - dh_param_g = g; + /* Set the new values as the global DH parameters. */ + dh_param_p = circuit_dh_prime; + dh_param_g = generator; + + /* Should be already set by config.c. */ + tor_assert(dh_param_p_tls); } /** Number of bits to use when choosing the x or y value in a Diffie-Hellman @@ -2830,5 +3059,44 @@ setup_openssl_threading(void) return 0; } #endif + +/** Uninitialize the crypto library. Return 0 on success, -1 on failure. + */ +int +crypto_global_cleanup(void) +{ + EVP_cleanup(); + ERR_remove_state(0); + ERR_free_strings(); + + if (dh_param_p) + BN_free(dh_param_p); + if (dh_param_p_tls) + BN_free(dh_param_p_tls); + if (dh_param_g) + BN_free(dh_param_g); + +#ifndef DISABLE_ENGINES + ENGINE_cleanup(); +#endif + + 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; + int i; + _openssl_mutexes = NULL; + _n_openssl_mutexes = 0; + for (i=0;i<n;++i) { + tor_mutex_free(ms[i]); + } + tor_free(ms); + } +#endif + return 0; +} + /** @} */ diff --git a/src/common/crypto.h b/src/common/crypto.h index 80c10296a..771c49c2d 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -91,6 +91,8 @@ int crypto_global_cleanup(void); crypto_pk_env_t *crypto_new_pk_env(void); void crypto_free_pk_env(crypto_pk_env_t *env); +void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname); + /* convenience function: wraps crypto_create_crypto_env, set_key, and init. */ crypto_cipher_env_t *crypto_create_init_cipher(const char *key, int encrypt_mode); diff --git a/src/common/tortls.c b/src/common/tortls.c index 554deb7bd..18f268470 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -52,7 +52,6 @@ #include <event2/bufferevent_ssl.h> #include <event2/buffer.h> #include <event2/event.h> -#include "compat_libevent.h" #endif #define CRYPTO_PRIVATE /* to import prototypes from crypto.h */ @@ -64,6 +63,7 @@ #include "torlog.h" #include "container.h" #include <string.h> +#include "compat_libevent.h" /* Enable the "v2" TLS handshake. */ @@ -146,7 +146,7 @@ struct tor_tls_t { /** True iff we should call negotiated_callback when we're done reading. */ unsigned int got_renegotiate:1; /** Incremented every time we start the server side of a handshake. */ - uint8_t server_handshake_count; + unsigned int server_handshake_count:2; size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last * time. */ /** Last values retrieved from BIO_number_read()/write(); see @@ -157,6 +157,11 @@ struct tor_tls_t { /** If set, a callback to invoke whenever the client tries to renegotiate * the handshake. */ void (*negotiated_callback)(tor_tls_t *tls, void *arg); + + /** Callback to invoke whenever a client tries to renegotiate more + than once. */ + void (*excess_renegotiations_callback)(void *); + /** Argument to pass to negotiated_callback. */ void *callback_arg; }; @@ -580,7 +585,13 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, const char *cname_sign, unsigned int cert_lifetime) { + /* OpenSSL generates self-signed certificates with random 64-bit serial + * numbers, so let's do that too. */ +#define SERIAL_NUMBER_SIZE 8 + time_t start_time, end_time; + BIGNUM *serial_number = NULL; + unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; EVP_PKEY *sign_pkey = NULL, *pkey=NULL; X509 *x509 = NULL; X509_NAME *name = NULL, *name_issuer=NULL; @@ -601,8 +612,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, goto error; if (!(X509_set_version(x509, 2))) goto error; - if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time))) - goto error; + + { /* our serial number is 8 random bytes. */ + if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0) + goto error; + if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) + goto error; + if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) + goto error; + } if (!(name = tor_x509_name_new(cname))) goto error; @@ -635,11 +653,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, EVP_PKEY_free(sign_pkey); if (pkey) EVP_PKEY_free(pkey); + if (serial_number) + BN_free(serial_number); if (name) X509_NAME_free(name); if (name_issuer) X509_NAME_free(name_issuer); return x509; + +#undef SERIAL_NUMBER_SIZE } /** List of ciphers that servers should select from.*/ @@ -1297,55 +1319,42 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address) return 1; } +/** We got an SSL ClientHello message. This might mean that the + * client wants to initiate a renegotiation and appropriate actions + * must be taken. */ static void -tor_tls_debug_state_callback(const SSL *ssl, int type, int val) -{ - log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", - ssl, ssl_state_to_string(ssl->state), type, val); -} - -/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection - * changes state. We use this: - * <ul><li>To alter the state of the handshake partway through, so we - * do not send or request extra certificates in v2 handshakes.</li> - * <li>To detect renegotiation</li></ul> - */ -static void -tor_tls_server_info_callback(const SSL *ssl, int type, int val) +tor_tls_got_client_hello(tor_tls_t *tls) { - tor_tls_t *tls; - (void) val; + if (tls->server_handshake_count < 3) + ++tls->server_handshake_count; - tor_tls_debug_state_callback(ssl, type, val); + if (tls->server_handshake_count == 2) { + if (!tls->negotiated_callback) { + log_warn(LD_BUG, "Got a renegotiation request but we don't" + " have a renegotiation callback set!"); + } - if (type != SSL_CB_ACCEPT_LOOP) - return; - if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A) - return; + tls->got_renegotiate = 1; + } else if (tls->server_handshake_count > 2 && + tls->excess_renegotiations_callback) { + /* We got more than one renegotiation requests. The Tor protocol + needs just one renegotiation; more than that probably means + They are trying to DoS us and we have to stop them. */ - tls = tor_tls_get_by_ssl(ssl); - if (tls) { - /* Check whether we're watching for renegotiates. If so, this is one! */ - if (tls->negotiated_callback) - tls->got_renegotiate = 1; - if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/ - ++tls->server_handshake_count; - } else { - log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); - return; + tls->excess_renegotiations_callback(tls->callback_arg); } /* Now check the cipher list. */ - if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) { + if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) { /*XXXX_TLS keep this from happening more than once! */ /* Yes, we're casting away the const from ssl. This is very naughty of us. * Let's hope openssl doesn't notice! */ /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */ - SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN); + SSL_set_mode((SSL*) tls->ssl, SSL_MODE_NO_AUTO_CHAIN); /* Don't send a hello request. */ - SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL); + SSL_set_verify((SSL*) tls->ssl, SSL_VERIFY_NONE, NULL); if (tls) { tls->wasV2Handshake = 1; @@ -1360,6 +1369,34 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) } #endif +/** This is a callback function for SSL_set_info_callback() and it + * will be called in every SSL state change. + * It logs the SSL state change, and executes any actions that must be + * taken. */ +static void +tor_tls_state_changed_callback(const SSL *ssl, int type, int val) +{ + log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", + ssl, ssl_state_to_string(ssl->state), type, val); + +#ifdef V2_HANDSHAKE_SERVER + if (type == SSL_CB_ACCEPT_LOOP && + ssl->state == SSL3_ST_SW_SRVR_HELLO_A) { + + /* Call tor_tls_got_client_hello() for every SSL ClientHello we + receive. */ + + tor_tls_t *tls = tor_tls_get_by_ssl(ssl); + if (!tls) { + log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); + return; + } + + tor_tls_got_client_hello(tls); + } +#endif +} + /** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically, * a list designed to mimic a common web browser. Some of the ciphers in the * list won't actually be implemented by OpenSSL: that's okay so long as the @@ -1501,14 +1538,8 @@ tor_tls_new(int sock, int isServer) log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu", result->last_read_count, result->last_write_count); } -#ifdef V2_HANDSHAKE_SERVER - if (isServer) { - SSL_set_info_callback(result->ssl, tor_tls_server_info_callback); - } else -#endif - { - SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); - } + + SSL_set_info_callback(result->ssl, tor_tls_state_changed_callback); /* Not expected to get called. */ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); @@ -1526,25 +1557,22 @@ tor_tls_set_logged_address(tor_tls_t *tls, const char *address) tls->address = tor_strdup(address); } -/** Set <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b> - * next gets a client-side renegotiate in the middle of a read. Do not - * invoke this function until <em>after</em> initial handshaking is done! +/** Set <b>cb</b> to be called with argument <b>arg</b> whenever + * <b>tls</b> next gets a client-side renegotiate in the middle of a + * read. Set <b>cb2</b> to be called with argument <b>arg</b> whenever + * <b>tls</b> gets excess renegotiation requests. Do not invoke this + * function until <em>after</em> initial handshaking is done! */ void -tor_tls_set_renegotiate_callback(tor_tls_t *tls, +tor_tls_set_renegotiate_callbacks(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), + void (*cb2)(void *), void *arg) { tls->negotiated_callback = cb; + tls->excess_renegotiations_callback = cb2; tls->callback_arg = arg; tls->got_renegotiate = 0; -#ifdef V2_HANDSHAKE_SERVER - if (cb) { - SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback); - } else { - SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback); - } -#endif } /** If this version of openssl requires it, turn on renegotiation on @@ -1564,16 +1592,6 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls) } } -/** If this version of openssl supports it, turn off renegotiation on - * <b>tls</b>. (Our protocol never requires this for security, but it's nice - * to use belt-and-suspenders here.) - */ -void -tor_tls_block_renegotiation(tor_tls_t *tls) -{ - tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; -} - void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) { @@ -1611,6 +1629,7 @@ tor_tls_free(tor_tls_t *tls) SSL_free(tls->ssl); tls->ssl = NULL; tls->negotiated_callback = NULL; + tls->excess_renegotiations_callback = NULL; if (tls->context) tor_tls_context_decref(tls->context); tor_free(tls->address); @@ -1632,19 +1651,30 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len) tor_assert(tls->state == TOR_TLS_ST_OPEN); tor_assert(len<INT_MAX); r = SSL_read(tls->ssl, cp, (int)len); - if (r > 0) { + if (r > 0) /* return the number of characters read */ + return r; + + /* If we got here, SSL_read() did not go as expected. */ + + err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); + #ifdef V2_HANDSHAKE_SERVER - if (tls->got_renegotiate) { - /* Renegotiation happened! */ - log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls)); - if (tls->negotiated_callback) - tls->negotiated_callback(tls, tls->callback_arg); - tls->got_renegotiate = 0; + if (tls->got_renegotiate) { + if (tls->server_handshake_count != 2) { + log_warn(LD_BUG, "We did not notice renegotiation in a timely " + "fashion (%u)!", tls->server_handshake_count); } -#endif + + /* Renegotiation happened! */ + log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls)); + if (tls->negotiated_callback) + tls->negotiated_callback(tls, tls->callback_arg); + tls->got_renegotiate = 0; + return r; } - err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); +#endif + if (err == _TOR_TLS_ZERORETURN || err == TOR_TLS_CLOSE) { log_debug(LD_NET,"read returned r=%d; TLS is closed",r); tls->state = TOR_TLS_ST_CLOSED; @@ -1681,6 +1711,7 @@ tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) } r = SSL_write(tls->ssl, cp, (int)n); err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET); + if (err == TOR_TLS_DONE) { return r; } @@ -1745,7 +1776,6 @@ tor_tls_finish_handshake(tor_tls_t *tls) { int r = TOR_TLS_DONE; if (tls->isServer) { - SSL_set_info_callback(tls->ssl, NULL); SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb); /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */ tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN; diff --git a/src/common/tortls.h b/src/common/tortls.h index 673f18dfe..9f86e3712 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -13,6 +13,7 @@ #include "crypto.h" #include "compat.h" +#include "compat_libevent.h" /* Opaque structure to hold a TLS connection. */ typedef struct tor_tls_t tor_tls_t; @@ -60,8 +61,9 @@ int tor_tls_context_init(int is_public_server, unsigned int key_lifetime); tor_tls_t *tor_tls_new(int sock, int is_server); void tor_tls_set_logged_address(tor_tls_t *tls, const char *address); -void tor_tls_set_renegotiate_callback(tor_tls_t *tls, +void tor_tls_set_renegotiate_callbacks(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), + void (*cb2)(void *), void *arg); int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free(tor_tls_t *tls); @@ -77,7 +79,6 @@ int tor_tls_handshake(tor_tls_t *tls); int tor_tls_finish_handshake(tor_tls_t *tls); int tor_tls_renegotiate(tor_tls_t *tls); void tor_tls_unblock_renegotiation(tor_tls_t *tls); -void tor_tls_block_renegotiation(tor_tls_t *tls); void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls); int tor_tls_shutdown(tor_tls_t *tls); int tor_tls_get_pending_bytes(tor_tls_t *tls); diff --git a/src/common/util.c b/src/common/util.c index c44a4aa3b..6d488d996 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2137,13 +2137,12 @@ write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin) return write_chunks_to_file_impl(fname, chunks, flags); } -/** As write_str_to_file, but does not assume a NUL-terminated - * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */ -int -write_bytes_to_file(const char *fname, const char *str, size_t len, - int bin) +/** Write <b>len</b> bytes, starting at <b>str</b>, to <b>fname</b> + using the open() flags passed in <b>flags</b>. */ +static int +write_bytes_to_file_impl(const char *fname, const char *str, size_t len, + int flags) { - int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT); int r; sized_chunk_t c = { str, len }; smartlist_t *chunks = smartlist_create(); @@ -2153,20 +2152,35 @@ write_bytes_to_file(const char *fname, const char *str, size_t len, return r; } +/** As write_str_to_file, but does not assume a NUL-terminated + * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */ +int +write_bytes_to_file(const char *fname, const char *str, size_t len, + int bin) +{ + return write_bytes_to_file_impl(fname, str, len, + OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT)); +} + /** As write_bytes_to_file, but if the file already exists, append the bytes * to the end of the file instead of overwriting it. */ int append_bytes_to_file(const char *fname, const char *str, size_t len, int bin) { - int flags = OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT); - int r; - sized_chunk_t c = { str, len }; - smartlist_t *chunks = smartlist_create(); - smartlist_add(chunks, &c); - r = write_chunks_to_file_impl(fname, chunks, flags); - smartlist_free(chunks); - return r; + return write_bytes_to_file_impl(fname, str, len, + OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT)); +} + +/** Like write_str_to_file(), but also return -1 if there was a file + already residing in <b>fname</b>. */ +int +write_bytes_to_new_file(const char *fname, const char *str, size_t len, + int bin) +{ + return write_bytes_to_file_impl(fname, str, len, + OPEN_FLAGS_DONT_REPLACE| + (bin?O_BINARY:O_TEXT)); } /** Read the contents of <b>filename</b> into a newly allocated @@ -3159,28 +3173,72 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, /* Maximum number of file descriptors, if we cannot get it via sysconf() */ #define DEFAULT_MAX_FD 256 -/** Terminate process running at PID <b>pid</b>. +/** Terminate the process of <b>process_handle</b>. * Code borrowed from Python's os.kill. */ int -tor_terminate_process(pid_t pid) +tor_terminate_process(process_handle_t *process_handle) { #ifdef MS_WINDOWS - 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, pid); - if (!handle) - return -1; + 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; - if (!TerminateProcess(handle, 0)) - return -1; - else - return 0; + if (!TerminateProcess(handle, 0)) + return -1; + else + return 0; + } #else /* Unix */ - return kill(pid, SIGTERM); + return kill(process_handle->pid, SIGTERM); +#endif + + return -1; +} + +/** Return the Process ID of <b>process_handle</b>. */ +int +tor_process_get_pid(process_handle_t *process_handle) +{ +#ifdef MS_WINDOWS + return (int) process_handle->pid.dwProcessId; +#else + return (int) process_handle->pid; +#endif +} + +#ifdef MS_WINDOWS +HANDLE +tor_process_get_stdout_pipe(process_handle_t *process_handle) +{ + return process_handle->stdout_pipe; +} +#else +FILE * +tor_process_get_stdout_pipe(process_handle_t *process_handle) +{ + return process_handle->stdout_handle; +} +#endif + +static process_handle_t * +process_handle_new(void) +{ + process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t)); + +#ifndef MS_WINDOWS + out->stdout_pipe = -1; + out->stderr_pipe = -1; #endif + + return out; } +/*DOCDOC*/ #define CHILD_STATE_INIT 0 #define CHILD_STATE_PIPE 1 #define CHILD_STATE_MAXFD 2 @@ -3213,14 +3271,20 @@ tor_terminate_process(pid_t pid) */ int tor_spawn_background(const char *const filename, const char **argv, +#ifdef MS_WINDOWS + LPVOID envp, +#else const char **envp, - process_handle_t *process_handle) +#endif + process_handle_t **process_handle_out) { #ifdef MS_WINDOWS HANDLE stdout_pipe_read = NULL; HANDLE stdout_pipe_write = NULL; HANDLE stderr_pipe_read = NULL; HANDLE stderr_pipe_write = NULL; + process_handle_t *process_handle; + int status; STARTUPINFO siStartInfo; BOOL retval = FALSE; @@ -3230,30 +3294,26 @@ tor_spawn_background(const char *const filename, const char **argv, (void)envp; // Unused on Windows - /* process_handle must not be NULL */ - tor_assert(process_handle != NULL); - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; /* TODO: should we set explicit security attributes? (#2046, comment 5) */ saAttr.lpSecurityDescriptor = NULL; /* Assume failure to start process */ - memset(process_handle, 0, sizeof(process_handle_t)); - process_handle->status = PROCESS_STATUS_ERROR; + status = PROCESS_STATUS_ERROR; /* Set up pipe for stdout */ if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, &saAttr, 0)) { log_warn(LD_GENERAL, "Failed to create pipe for stdout communication with child process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } if (!SetHandleInformation(stdout_pipe_read, HANDLE_FLAG_INHERIT, 0)) { log_warn(LD_GENERAL, "Failed to configure pipe for stdout communication with child " "process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } /* Set up pipe for stderr */ @@ -3261,13 +3321,13 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to create pipe for stderr communication with child process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } if (!SetHandleInformation(stderr_pipe_read, HANDLE_FLAG_INHERIT, 0)) { log_warn(LD_GENERAL, "Failed to configure pipe for stderr communication with child " "process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } /* Create the child process */ @@ -3276,6 +3336,9 @@ tor_spawn_background(const char *const filename, const char **argv, */ joined_argv = tor_join_win_cmdline(argv); + process_handle = process_handle_new(); + process_handle->status = status; + ZeroMemory(&(process_handle->pid), sizeof(PROCESS_INFORMATION)); ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); @@ -3295,7 +3358,7 @@ tor_spawn_background(const char *const filename, const char **argv, /*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess() * work?) */ 0, // creation flags - NULL, // use parent's environment + envp, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &(process_handle->pid)); // receives PROCESS_INFORMATION @@ -3306,22 +3369,25 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to create child process %s: %s", filename?filename:argv[0], format_win32_error(GetLastError())); + tor_free(process_handle); } else { /* TODO: Close hProcess and hThread in process_handle->pid? */ process_handle->stdout_pipe = stdout_pipe_read; process_handle->stderr_pipe = stderr_pipe_read; - process_handle->status = PROCESS_STATUS_RUNNING; + status = process_handle->status = PROCESS_STATUS_RUNNING; } /* TODO: Close pipes on exit */ - - return process_handle->status; + *process_handle_out = process_handle; + return status; #else // MS_WINDOWS pid_t pid; int stdout_pipe[2]; int stderr_pipe[2]; int fd, retval; ssize_t nbytes; + process_handle_t *process_handle; + int status; const char *error_message = SPAWN_ERROR_MESSAGE; size_t error_message_length; @@ -3334,9 +3400,7 @@ tor_spawn_background(const char *const filename, const char **argv, static int max_fd = -1; - /* Assume failure to start */ - memset(process_handle, 0, sizeof(process_handle_t)); - process_handle->status = PROCESS_STATUS_ERROR; + status = PROCESS_STATUS_ERROR; /* We do the strlen here because strlen() is not signal handler safe, and we are not allowed to use unsafe functions between fork and exec */ @@ -3350,7 +3414,7 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to set up pipe for stdout communication with child process: %s", strerror(errno)); - return process_handle->status; + return status; } retval = pipe(stderr_pipe); @@ -3358,7 +3422,7 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to set up pipe for stderr communication with child process: %s", strerror(errno)); - return process_handle->status; + return status; } child_state = CHILD_STATE_MAXFD; @@ -3448,7 +3512,7 @@ tor_spawn_background(const char *const filename, const char **argv, _exit(255); /* Never reached, but avoids compiler warning */ - return process_handle->status; + return status; } /* In parent */ @@ -3459,9 +3523,11 @@ tor_spawn_background(const char *const filename, const char **argv, close(stdout_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); - return process_handle->status; + return status; } + process_handle = process_handle_new(); + process_handle->status = status; process_handle->pid = pid; /* TODO: If the child process forked but failed to exec, waitpid it */ @@ -3485,7 +3551,7 @@ tor_spawn_background(const char *const filename, const char **argv, strerror(errno)); } - process_handle->status = PROCESS_STATUS_RUNNING; + 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); @@ -3493,10 +3559,52 @@ tor_spawn_background(const char *const filename, const char **argv, process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r"); process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r"); + *process_handle_out = process_handle; return process_handle->status; #endif // MS_WINDOWS } +/** Destroy all resources allocated by the process handle in + * <b>process_handle</b>. + * If <b>also_terminate_process</b> is true, also terminate the + * process of the process handle. */ +void +tor_process_handle_destroy(process_handle_t *process_handle, + int also_terminate_process) +{ + if (!process_handle) + return; + + if (also_terminate_process) { + if (tor_terminate_process(process_handle) < 0) { + log_notice(LD_GENERAL, "Failed to terminate process with PID '%d'", + tor_process_get_pid(process_handle)); + } else { + log_info(LD_GENERAL, "Terminated process with PID '%d'", + tor_process_get_pid(process_handle)); + } + } + + process_handle->status = PROCESS_STATUS_NOTRUNNING; + +#ifdef MS_WINDOWS + if (process_handle->stdout_pipe) + CloseHandle(process_handle->stdout_pipe); + + if (process_handle->stderr_pipe) + CloseHandle(process_handle->stderr_pipe); +#else + if (process_handle->stdout_handle) + fclose(process_handle->stdout_handle); + + if (process_handle->stderr_handle) + fclose(process_handle->stderr_handle); +#endif + + memset(process_handle, 0x0f, sizeof(process_handle_t)); + tor_free(process_handle); +} + /** Get the exit code of a process specified by <b>process_handle</b> and store * it in <b>exit_code</b>, if set to a non-NULL value. If <b>block</b> is set * to true, the call will block until the process has exited. Otherwise if @@ -3508,7 +3616,7 @@ tor_spawn_background(const char *const filename, const char **argv, * 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(const process_handle_t *process_handle, int block, int *exit_code) { #ifdef MS_WINDOWS @@ -3517,14 +3625,14 @@ tor_get_exit_code(const process_handle_t process_handle, if (block) { /* Wait for the process to exit */ - retval = WaitForSingleObject(process_handle.pid.hProcess, INFINITE); + retval = WaitForSingleObject(process_handle->pid.hProcess, INFINITE); if (retval != WAIT_OBJECT_0) { log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s", (int)retval, format_win32_error(GetLastError())); return PROCESS_EXIT_ERROR; } } else { - retval = WaitForSingleObject(process_handle.pid.hProcess, 0); + retval = WaitForSingleObject(process_handle->pid.hProcess, 0); if (WAIT_TIMEOUT == retval) { /* Process has not exited */ return PROCESS_EXIT_RUNNING; @@ -3536,7 +3644,7 @@ tor_get_exit_code(const process_handle_t process_handle, } if (exit_code != NULL) { - success = GetExitCodeProcess(process_handle.pid.hProcess, + success = GetExitCodeProcess(process_handle->pid.hProcess, (PDWORD)exit_code); if (!success) { log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s", @@ -3548,19 +3656,19 @@ 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); + retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG); if (!block && 0 == retval) { /* Process has not exited */ return PROCESS_EXIT_RUNNING; - } else if (retval != process_handle.pid) { - log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", process_handle.pid, - strerror(errno)); + } else if (retval != process_handle->pid) { + log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", + process_handle->pid, strerror(errno)); return PROCESS_EXIT_ERROR; } if (!WIFEXITED(stat_loc)) { log_warn(LD_GENERAL, "Process %d did not exit normally", - process_handle.pid); + process_handle->pid); return PROCESS_EXIT_ERROR; } @@ -3662,7 +3770,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, if (NULL == retval) { if (feof(h)) { log_debug(LD_GENERAL, "fgets() reached end of file"); - fclose(h); if (eof) *eof = 1; break; @@ -3675,7 +3782,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, } else { log_warn(LD_GENERAL, "fgets() from handle failed: %s", strerror(errno)); - fclose(h); return -1; } } @@ -3835,12 +3941,10 @@ log_from_pipe(FILE *stream, int severity, const char *executable, r = get_string_from_pipe(stream, buf, sizeof(buf) - 1); if (r == IO_STREAM_CLOSED) { - fclose(stream); return 1; } else if (r == IO_STREAM_EAGAIN) { return 0; } else if (r == IO_STREAM_TERM) { - fclose(stream); return -1; } @@ -3948,7 +4052,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* Static variables are initialized to zero, so child_handle.status=0 * which corresponds to it not running on startup */ - static process_handle_t child_handle; + static process_handle_t *child_handle=NULL; static time_t time_to_run_helper = 0; int stdout_status, stderr_status, retval; @@ -3974,46 +4078,50 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, argv[9] = NULL; /* Start the child, if it is not already running */ - if (child_handle.status != PROCESS_STATUS_RUNNING && + if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) && time_to_run_helper < now) { + int status; + /* Assume tor-fw-helper will succeed, start it later*/ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS; + if (child_handle) { + tor_process_handle_destroy(child_handle, 1); + child_handle = NULL; + } + #ifdef MS_WINDOWS /* Passing NULL as lpApplicationName makes Windows search for the .exe */ - tor_spawn_background(NULL, argv, NULL, &child_handle); + status = tor_spawn_background(NULL, argv, NULL, &child_handle); #else - tor_spawn_background(filename, argv, NULL, &child_handle); + status = tor_spawn_background(filename, argv, NULL, &child_handle); #endif - if (PROCESS_STATUS_ERROR == child_handle.status) { + + if (PROCESS_STATUS_ERROR == status) { log_warn(LD_GENERAL, "Failed to start port forwarding helper %s", filename); time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; return; } -#ifdef MS_WINDOWS - log_info(LD_GENERAL, - "Started port forwarding helper (%s)", filename); -#else + log_info(LD_GENERAL, - "Started port forwarding helper (%s) with pid %d", filename, - child_handle.pid); -#endif + "Started port forwarding helper (%s) with pid '%d'", + filename, tor_process_get_pid(child_handle)); } /* If child is running, read from its stdout and stderr) */ - if (PROCESS_STATUS_RUNNING == child_handle.status) { + if (child_handle && PROCESS_STATUS_RUNNING == child_handle->status) { /* Read from stdout/stderr and log result */ retval = 0; #ifdef MS_WINDOWS - stdout_status = log_from_handle(child_handle.stdout_pipe, LOG_INFO); - stderr_status = log_from_handle(child_handle.stderr_pipe, LOG_WARN); + stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO); + stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN); /* If we got this far (on Windows), the process started */ retval = 0; #else - stdout_status = log_from_pipe(child_handle.stdout_handle, + stdout_status = log_from_pipe(child_handle->stdout_handle, LOG_INFO, filename, &retval); - stderr_status = log_from_pipe(child_handle.stderr_handle, + stderr_status = log_from_pipe(child_handle->stderr_handle, LOG_WARN, filename, &retval); #endif if (retval) { @@ -4026,7 +4134,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* There was a failure */ retval = -1; #ifdef MS_WINDOWS - else if (tor_get_exit_code(child_handle, 0, NULL) != + else if (!child_handle || tor_get_exit_code(child_handle, 0, NULL) != PROCESS_EXIT_RUNNING) { /* process has exited or there was an error */ /* TODO: Do something with the process return value */ @@ -4049,10 +4157,10 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, if (0 != retval) { if (1 == retval) { log_info(LD_GENERAL, "Port forwarding helper terminated"); - child_handle.status = PROCESS_STATUS_NOTRUNNING; + child_handle->status = PROCESS_STATUS_NOTRUNNING; } else { log_warn(LD_GENERAL, "Failed to read from port forwarding helper"); - child_handle.status = PROCESS_STATUS_ERROR; + child_handle->status = PROCESS_STATUS_ERROR; } /* TODO: The child might not actually be finished (maybe it failed or diff --git a/src/common/util.h b/src/common/util.h index 77ed1ca5e..3a68f3993 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -173,19 +173,15 @@ int n_bits_set_u8(uint8_t v); #define HEX_CHARACTERS "0123456789ABCDEFabcdef" void tor_strlower(char *s) ATTR_NONNULL((1)); void tor_strupper(char *s) ATTR_NONNULL((1)); -int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1)); -int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1)); -int strcmp_opt(const char *s1, const char *s2) ATTR_PURE; -int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2)); -int strcmp_len(const char *s1, const char *s2, size_t len) - ATTR_PURE ATTR_NONNULL((1,2)); -int strcasecmpstart(const char *s1, const char *s2) - ATTR_PURE ATTR_NONNULL((1,2)); -int strcmpend(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2)); -int strcasecmpend(const char *s1, const char *s2) - ATTR_PURE ATTR_NONNULL((1,2)); -int fast_memcmpstart(const void *mem, size_t memlen, - const char *prefix) ATTR_PURE; +int tor_strisprint(const char *s) ATTR_NONNULL((1)); +int tor_strisnonupper(const char *s) ATTR_NONNULL((1)); +int strcmp_opt(const char *s1, const char *s2); +int strcmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcmp_len(const char *s1, const char *s2, size_t len) ATTR_NONNULL((1,2)); +int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix); void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2)); long tor_parse_long(const char *s, int base, long min, @@ -197,19 +193,19 @@ double tor_parse_double(const char *s, double min, double max, int *ok, uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next); const char *hex_str(const char *from, size_t fromlen) ATTR_NONNULL((1)); -const char *eat_whitespace(const char *s) ATTR_PURE; -const char *eat_whitespace_eos(const char *s, const char *eos) ATTR_PURE; -const char *eat_whitespace_no_nl(const char *s) ATTR_PURE; -const char *eat_whitespace_eos_no_nl(const char *s, const char *eos) ATTR_PURE; -const char *find_whitespace(const char *s) ATTR_PURE; -const char *find_whitespace_eos(const char *s, const char *eos) ATTR_PURE; -const char *find_str_at_start_of_line(const char *haystack, const char *needle) - ATTR_PURE; +const char *eat_whitespace(const char *s); +const char *eat_whitespace_eos(const char *s, const char *eos); +const char *eat_whitespace_no_nl(const char *s); +const char *eat_whitespace_eos_no_nl(const char *s, const char *eos); +const char *find_whitespace(const char *s); +const char *find_whitespace_eos(const char *s, const char *eos); +const char *find_str_at_start_of_line(const char *haystack, + const char *needle); int string_is_C_identifier(const char *string); -int tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE; -int tor_digest_is_zero(const char *digest) ATTR_PURE; -int tor_digest256_is_zero(const char *digest) ATTR_PURE; +int tor_mem_is_zero(const char *mem, size_t len); +int tor_digest_is_zero(const char *digest); +int tor_digest256_is_zero(const char *digest); char *esc_for_log(const char *string) ATTR_MALLOC; const char *escaped(const char *string); struct smartlist_t; @@ -315,6 +311,7 @@ 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) typedef struct open_file_t open_file_t; int start_writing_to_file(const char *fname, int open_flags, int mode, open_file_t **data_out); @@ -336,6 +333,8 @@ int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks, int bin); int append_bytes_to_file(const char *fname, const char *str, size_t len, int bin); +int write_bytes_to_new_file(const char *fname, const char *str, size_t len, + int bin); /** Flag for read_file_to_str: open the file in binary mode. */ #define RFTS_BIN 1 @@ -349,7 +348,7 @@ const char *parse_config_line_from_str(const char *line, char **key_out, char **value_out); char *expand_filename(const char *filename); struct smartlist_t *tor_listdir(const char *dirname); -int path_is_relative(const char *filename) ATTR_PURE; +int path_is_relative(const char *filename); /* Process helpers */ void start_daemon(void); @@ -360,10 +359,14 @@ void write_pidfile(char *filename); void tor_check_port_forwarding(const char *filename, int dir_port, int or_port, time_t now); -int tor_terminate_process(pid_t pid); -typedef struct process_handle_s process_handle_t; +typedef struct process_handle_t process_handle_t; int tor_spawn_background(const char *const filename, const char **argv, - const char **envp, process_handle_t *process_handle); +#ifdef MS_WINDOWS + LPVOID envp, +#else + const char **envp, +#endif + process_handle_t **process_handle_out); #define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code " @@ -371,16 +374,16 @@ int tor_spawn_background(const char *const filename, const char **argv, HANDLE load_windows_system_library(const TCHAR *library_name); #endif -#ifdef UTIL_PRIVATE -/* Prototypes for private functions only used by util.c (and unit tests) */ - /* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be * 0 because tor_check_port_forwarding depends on this being the initial * statue of the static instance of process_handle_t */ #define PROCESS_STATUS_NOTRUNNING 0 #define PROCESS_STATUS_RUNNING 1 #define PROCESS_STATUS_ERROR -1 -struct process_handle_s { + +#ifdef UTIL_PRIVATE +/*DOCDOC*/ +struct process_handle_t { int status; #ifdef MS_WINDOWS HANDLE stdout_pipe; @@ -394,12 +397,13 @@ struct process_handle_s { pid_t pid; #endif // MS_WINDOWS }; +#endif /* Return values of tor_get_exit_code() */ #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(const process_handle_t *process_handle, int block, int *exit_code); int tor_split_lines(struct smartlist_t *sl, char *buf, int len); #ifdef MS_WINDOWS @@ -416,6 +420,20 @@ ssize_t tor_read_all_from_process_stderr( const process_handle_t *process_handle, char *buf, size_t count); char *tor_join_win_cmdline(const char *argv[]); +int tor_process_get_pid(process_handle_t *process_handle); +#ifdef MS_WINDOWS +HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle); +#else +FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); +#endif + +int tor_terminate_process(process_handle_t *process_handle); +void tor_process_handle_destroy(process_handle_t *process_handle, + int also_terminate_process); + +#ifdef UTIL_PRIVATE +/* Prototypes for private functions only used by util.c (and unit tests) */ + void format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno); diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 67adf504d..a5682081a 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -126,8 +126,9 @@ tor_main.o: micro-revision.i micro-revision.i: FORCE @rm -f micro-revision.tmp; \ - if test -d ../../.git && test -x "`which git 2>&1;true`"; then \ - HASH="`git rev-parse --short=16 HEAD`"; \ + if test -d "$(top_srcdir)/.git" && \ + test -x "`which git 2>&1;true`"; then \ + HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ echo \"$$HASH\" > micro-revision.tmp; \ fi; \ if test ! -f micro-revision.tmp ; then \ @@ -141,10 +142,10 @@ micro-revision.i: FORCE or_sha1.i: $(tor_SOURCES) $(libtor_a_SOURCES) if test "@SHA1SUM@" != none; then \ - @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES) | \ + (cd "$(srcdir)" && @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES)) | \ @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \ elif test "@OPENSSL@" != none; then \ - @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES) | \ + (cd "$(srcdir)" && @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES)) | \ @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \ else \ rm or_sha1.i; \ diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b04bd1012..7934a2e7f 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -3012,7 +3012,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit) log_warn(LD_CIRC,"failed to choose an exit server"); return -1; } - exit = extend_info_from_node(node); + exit = extend_info_from_node(node, 0); tor_assert(exit); } state->chosen_exit = exit; @@ -3254,14 +3254,19 @@ onion_extend_cpath(origin_circuit_t *circ) } else if (cur_len == 0) { /* picking first node */ const node_t *r = choose_good_entry_server(purpose, state); if (r) { - info = extend_info_from_node(r); + /* If we're extending to a bridge, use the preferred address + rather than the primary, for potentially extending to an IPv6 + bridge. */ + int use_pref_addr = (r->ri != NULL && + r->ri->purpose == ROUTER_PURPOSE_BRIDGE); + info = extend_info_from_node(r, use_pref_addr); tor_assert(info); } } else { const node_t *r = choose_good_middle_server(purpose, state, circ->cpath, cur_len); if (r) { - info = extend_info_from_node(r); + info = extend_info_from_node(r, 0); tor_assert(info); } } @@ -3320,28 +3325,36 @@ extend_info_alloc(const char *nickname, const char *digest, return info; } -/** Allocate and return a new extend_info_t that can be used to build a - * circuit to or through the router <b>r</b>. */ +/** Allocate and return a new extend_info_t that can be used to build + * a circuit to or through the router <b>r</b>. Use the primary + * address of the router unless <b>for_direct_connect</b> is true, in + * which case the preferred address is used instead. */ extend_info_t * -extend_info_from_router(const routerinfo_t *r) +extend_info_from_router(const routerinfo_t *r, int for_direct_connect) { - tor_addr_t addr; + tor_addr_port_t ap; tor_assert(r); - tor_addr_from_ipv4h(&addr, r->addr); + + if (for_direct_connect) + router_get_pref_orport(r, &ap); + else + router_get_prim_orport(r, &ap); return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &addr, r->or_port); + r->onion_pkey, &ap.addr, ap.port); } -/** Allocate and return a new extend_info that can be used to build a ircuit - * to or through the node <b>node</b>. May return NULL if there is not - * enough info about <b>node</b> to extend to it--for example, if there - * is no routerinfo_t or microdesc_t. +/** Allocate and return a new extend_info that can be used to build a + * ircuit to or through the node <b>node</b>. Use the primary address + * of the node unless <b>for_direct_connect</b> is true, in which case + * the preferred address is used instead. May return NULL if there is + * not enough info about <b>node</b> to extend to it--for example, if + * there is no routerinfo_t or microdesc_t. **/ extend_info_t * -extend_info_from_node(const node_t *node) +extend_info_from_node(const node_t *node, int for_direct_connect) { if (node->ri) { - return extend_info_from_router(node->ri); + return extend_info_from_router(node->ri, for_direct_connect); } else if (node->rs && node->md) { tor_addr_t addr; tor_addr_from_ipv4h(&addr, node->rs->addr); @@ -4841,10 +4854,11 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, static bridge_info_t * get_configured_bridge_by_routerinfo(const routerinfo_t *ri) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - return get_configured_bridge_by_addr_port_digest(&addr, - ri->or_port, ri->cache_info.identity_digest); + tor_addr_port_t ap; + + router_get_pref_orport(ri, &ap); + return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port, + ri->cache_info.identity_digest); } /** Return 1 if <b>ri</b> is one of our known bridges, else 0. */ @@ -4858,18 +4872,31 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri) int node_is_a_configured_bridge(const node_t *node) { - tor_addr_t addr; - uint16_t orport; + int retval = 0; /* Negative. */ + smartlist_t *orports = NULL; + if (!node) - return 0; - if (node_get_addr(node, &addr) < 0) - return 0; - orport = node_get_orport(node); - if (orport == 0) - return 0; + goto out; + + orports = node_get_all_orports(node); + if (orports == NULL) + goto out; - return get_configured_bridge_by_addr_port_digest( - &addr, orport, node->identity) != NULL; + SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) { + if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port, + node->identity) != NULL) { + retval = 1; + goto out; + } + } SMARTLIST_FOREACH_END(orport); + + out: + if (orports != NULL) { + SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); + smartlist_free(orports); + orports = NULL; + } + return retval; } /** We made a connection to a router at <b>addr</b>:<b>port</b> @@ -5123,18 +5150,52 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) routerinfo_t *ri = node->ri; tor_addr_from_ipv4h(&addr, ri->addr); - if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == ri->or_port) { + if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == ri->or_port) || + (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && + bridge->port == ri->ipv6_orport)) { /* they match, so no need to do anything */ } else { - ri->addr = tor_addr_to_ipv4h(&bridge->addr); - tor_free(ri->address); - ri->address = tor_dup_ip(ri->addr); - ri->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerinfo for '%s' to match configured " - "address %s:%d.", - ri->nickname, ri->address, ri->or_port); + if (tor_addr_family(&bridge->addr) == AF_INET) { + ri->addr = tor_addr_to_ipv4h(&bridge->addr); + tor_free(ri->address); + ri->address = tor_dup_ip(ri->addr); + ri->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s:%d.", + ri->nickname, ri->address, ri->or_port); + } else if (tor_addr_family(&bridge->addr) == AF_INET6) { + tor_addr_copy(&ri->ipv6_addr, &bridge->addr); + ri->ipv6_orport = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s:%d.", + ri->nickname, fmt_addr(&ri->ipv6_addr), ri->ipv6_orport); + } else { + log_err(LD_BUG, "Address family not supported: %d.", + tor_addr_family(&bridge->addr)); + return; + } + } + + /* Indicate that we prefer connecting to this bridge over the + protocol that the bridge address indicates. Last bridge + descriptor handled wins. */ + ri->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6; + + /* XXXipv6 we lack support for falling back to another address for + the same relay, warn the user */ + if (!tor_addr_is_null(&ri->ipv6_addr)) + { + tor_addr_port_t ap; + router_get_pref_orport(ri, &ap); + log_notice(LD_CONFIG, + "Bridge '%s' has both an IPv4 and an IPv6 address. " + "Will prefer using its %s address (%s:%d).", + ri->nickname, + ri->ipv6_preferred ? "IPv6" : "IPv4", + fmt_addr(&ap.addr), ap.port); } } if (node->rs) { @@ -5179,8 +5240,8 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) rewrite_node_address_for_bridge(bridge, node); add_an_entry_guard(node, 1, 1); - log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname, - from_cache ? "cached" : "fresh"); + log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname, + from_cache ? "cached" : "fresh", router_describe(ri)); /* set entry->made_contact so if it goes down we don't drop it from * our entry node list */ entry_guard_register_connect_status(ri->cache_info.identity_digest, diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 1052db615..8df274870 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -59,8 +59,10 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); extend_info_t *extend_info_alloc(const char *nickname, const char *digest, crypto_pk_env_t *onion_key, const tor_addr_t *addr, uint16_t port); -extend_info_t *extend_info_from_router(const routerinfo_t *r); -extend_info_t *extend_info_from_node(const node_t *node); +extend_info_t *extend_info_from_router(const routerinfo_t *r, + int for_direct_connect); +extend_info_t *extend_info_from_node(const node_t *node, + int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); void extend_info_free(extend_info_t *info); const node_t *build_state_get_exit_node(cpath_build_state_t *state); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index f9931d88d..fbcfe2445 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1439,7 +1439,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, int opt = conn->chosen_exit_optional; r = node_get_by_nickname(conn->chosen_exit_name, 1); if (r && node_has_descriptor(r)) { - extend_info = extend_info_from_node(r); + /* We might want to connect to an IPv6 bridge for loading + descriptors so we use the preferred address rather than + the primary. */ + extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0); } else { log_debug(LD_DIR, "considering %d, %s", want_onehop, conn->chosen_exit_name); @@ -1690,7 +1693,7 @@ consider_recording_trackhost(const entry_connection_t *conn, addressmap_register(conn->socks_request->address, new_address, time(NULL) + options->TrackHostExitsExpire, - ADDRMAPSRC_TRACKEXIT); + ADDRMAPSRC_TRACKEXIT, 0, 0); } /** Attempt to attach the connection <b>conn</b> to <b>circ</b>, and send a diff --git a/src/or/config.c b/src/or/config.c index adfde8138..525ff16b7 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -220,7 +220,7 @@ static config_var_t _option_vars[] = { V(ConstrainedSockSize, MEMUNIT, "8192"), V(ContactInfo, STRING, NULL), V(ControlListenAddress, LINELIST, NULL), - V(ControlPort, PORT, "0"), + V(ControlPort, LINELIST, NULL), V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), @@ -231,12 +231,13 @@ static config_var_t _option_vars[] = { V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), OBSOLETE("DebugLogFile"), + V(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, NULL), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), V(DirListenAddress, LINELIST, NULL), OBSOLETE("DirFetchPeriod"), V(DirPolicy, LINELIST, NULL), - V(DirPort, PORT, "0"), + V(DirPort, LINELIST, NULL), V(DirPortFrontPage, FILENAME, NULL), OBSOLETE("DirPostPeriod"), OBSOLETE("DirRecordUsageByCountry"), @@ -246,7 +247,9 @@ static config_var_t _option_vars[] = { V(DirReqStatistics, BOOL, "1"), VAR("DirServer", LINELIST, DirServers, NULL), V(DisableAllSwap, BOOL, "0"), + V(DisableDebuggerAttachment, BOOL, "1"), V(DisableIOCP, BOOL, "1"), + V(DynamicDHGroups, BOOL, "1"), V(DNSPort, LINELIST, NULL), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), @@ -340,7 +343,7 @@ static config_var_t _option_vars[] = { V(NumCPUs, UINT, "0"), V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), - V(ORPort, PORT, "0"), + V(ORPort, LINELIST, NULL), V(OutboundBindAddress, STRING, NULL), OBSOLETE("PathlenCoinWeight"), V(PerConnBWBurst, MEMUNIT, "0"), @@ -406,6 +409,7 @@ static config_var_t _option_vars[] = { V(UseEntryGuards, BOOL, "1"), V(UseMicrodescriptors, AUTOBOOL, "auto"), V(User, STRING, NULL), + V(UserspaceIOCPBuffers, BOOL, "0"), VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"), VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"), VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), @@ -597,8 +601,11 @@ static int parse_dir_server_line(const char *line, dirinfo_type_t required_type, int validate_only); static void port_cfg_free(port_cfg_t *port); -static int parse_client_ports(const or_options_t *options, int validate_only, +static int parse_ports(const or_options_t *options, int validate_only, char **msg_out, int *n_ports_out); +static int check_server_ports(const smartlist_t *ports, + const or_options_t *options); + static int validate_data_directory(or_options_t *options); static int write_configuration_file(const char *fname, const or_options_t *options); @@ -611,9 +618,6 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options, static int or_state_load(void); static int options_init_logs(or_options_t *options, int validate_only); -static int is_listening_on_low_port(int port_option, - const config_line_t *listen_options); - static uint64_t config_parse_memunit(const char *s, int *ok); static int config_parse_msec_interval(const char *s, int *ok); static int config_parse_interval(const char *s, int *ok); @@ -660,16 +664,20 @@ static const config_format_t state_format = { /** Command-line and config-file options. */ static or_options_t *global_options = NULL; +/** DOCDOC */ +static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; +/** DOCDOC */ +static char *torrc_defaults_fname; /** Persistent serialized state. */ static or_state_t *global_state = NULL; /** Configuration Options set by command line. */ static config_line_t *global_cmdline_options = NULL; /** Contents of most recently read DirPortFrontPage file. */ static char *global_dirfrontpagecontents = NULL; -/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */ -static smartlist_t *configured_client_ports = NULL; +/** List of port_cfg_t for all configured ports. */ +static smartlist_t *configured_ports = NULL; /** Return the contents of our frontpage string, or NULL if not configured. */ const char * @@ -805,6 +813,8 @@ config_free_all(void) { or_options_free(global_options); global_options = NULL; + or_options_free(global_default_options); + global_default_options = NULL; config_free(&state_format, global_state); global_state = NULL; @@ -812,14 +822,15 @@ config_free_all(void) config_free_lines(global_cmdline_options); global_cmdline_options = NULL; - if (configured_client_ports) { - SMARTLIST_FOREACH(configured_client_ports, + if (configured_ports) { + SMARTLIST_FOREACH(configured_ports, port_cfg_t *, p, tor_free(p)); - smartlist_free(configured_client_ports); - configured_client_ports = NULL; + smartlist_free(configured_ports); + configured_ports = NULL; } tor_free(torrc_fname); + tor_free(torrc_defaults_fname); tor_free(_version); tor_free(global_dirfrontpagecontents); } @@ -1065,7 +1076,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) #endif if (running_tor) { - int n_client_ports=0; + int n_ports=0; /* We need to set the connection limit before we can open the listeners. */ if (set_max_file_descriptors((unsigned)options->ConnLimit, &options->_ConnLimit) < 0) { @@ -1081,10 +1092,10 @@ options_act_reversible(const or_options_t *old_options, char **msg) libevent_initialized = 1; } - /* Adjust the client port configuration so we can launch listeners. */ - if (parse_client_ports(options, 0, msg, &n_client_ports)) { + /* Adjust the port configuration so we can launch listeners. */ + if (parse_ports(options, 0, msg, &n_ports)) { if (!*msg) - *msg = tor_strdup("Unexpected problem parsing client port config"); + *msg = tor_strdup("Unexpected problem parsing port config"); goto rollback; } @@ -1092,13 +1103,22 @@ options_act_reversible(const or_options_t *old_options, char **msg) consider_hibernation(time(NULL)); /* Launch the listeners. (We do this before we setuid, so we can bind to - * ports under 1024.) We don't want to rebind if we're hibernating. */ + * ports under 1024.) We don't want to rebind if we're hibernating. If + * networking is disabled, this will close all but the control listeners, + * but disable those. */ if (!we_are_hibernating()) { if (retry_all_listeners(replaced_listeners, new_listeners) < 0) { *msg = tor_strdup("Failed to bind one of the listener ports."); goto rollback; } } + if (options->DisableNetwork) { + /* Aggressively close non-controller stuff, NOW */ + log_notice(LD_NET, "DisableNetwork is set. Tor will not make or accept " + "non-control network connections. Shutting down all existing " + "connections."); + connection_mark_all_noncontrol_connections(); + } } #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) @@ -1267,6 +1287,24 @@ get_effective_bwburst(const or_options_t *options) return (uint32_t)bw; } +/** Return True if any changes from <b>old_options</b> to + * <b>new_options</b> needs us to refresh our TLS context. */ +static int +options_transition_requires_fresh_tls_context(const or_options_t *old_options, + const or_options_t *new_options) +{ + tor_assert(new_options); + + if (!old_options) + return 0; + + if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) { + return 1; + } + + return 0; +} + /** Fetch the active option list, and take actions based on it. All of the * things we do should survive being done repeatedly. If present, * <b>old_options</b> contains the previous value of the options. @@ -1286,6 +1324,14 @@ options_act(const or_options_t *old_options) const int transition_affects_workers = old_options && options_transition_affects_workers(old_options, options); + /* disable ptrace and later, other basic debugging techniques */ + if (options->DisableDebuggerAttachment) { + tor_disable_debugger_attach(); + } else { + log_notice(LD_CONFIG,"Debugger attachment enabled " + "for unprivileged users."); + } + if (running_tor && !have_lockfile()) { if (try_locking(options, 1) < 0) return -1; @@ -1384,6 +1430,29 @@ options_act(const or_options_t *old_options) finish_daemon(options->DataDirectory); } + /* If needed, generate a new TLS DH prime according to the current torrc. */ + if (server_mode(options)) { + if (!old_options) { + if (options->DynamicDHGroups) { + char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); + crypto_set_tls_dh_prime(fname); + tor_free(fname); + } else { + crypto_set_tls_dh_prime(NULL); + } + } else { + if (options->DynamicDHGroups && !old_options->DynamicDHGroups) { + char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); + crypto_set_tls_dh_prime(fname); + tor_free(fname); + } else if (!options->DynamicDHGroups && old_options->DynamicDHGroups) { + crypto_set_tls_dh_prime(NULL); + } + } + } else { /* clients don't need a dynamic DH prime. */ + crypto_set_tls_dh_prime(NULL); + } + /* We want to reinit keys as needed before we do much of anything else: keys are important, and other things can depend on them. */ if (transition_affects_workers || @@ -1393,6 +1462,13 @@ options_act(const or_options_t *old_options) log_warn(LD_BUG,"Error initializing keys; exiting"); return -1; } + } else if (old_options && + options_transition_requires_fresh_tls_context(old_options, + options)) { + if (router_initialize_tls_context() < 0) { + log_warn(LD_BUG,"Error initializing TLS context."); + return -1; + } } /* Write our PID to the PID file. If we do not have write permissions we @@ -1502,7 +1578,7 @@ options_act(const or_options_t *old_options) int was_relay = 0; if (options->BridgeRelay) { time_t int_start = time(NULL); - if (old_options->ORPort == options->ORPort) { + if (config_lines_eq(old_options->ORPort, options->ORPort)) { int_start += RELAY_BRIDGE_STATS_DELAY; was_relay = 1; } @@ -1741,7 +1817,11 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) int i = 1; while (i < argc) { + unsigned command = CONFIG_LINE_NORMAL; + int want_arg = 1; + if (!strcmp(argv[i],"-f") || + !strcmp(argv[i],"--defaults-torrc") || !strcmp(argv[i],"--hash-password")) { i += 2; /* command-line option with argument. ignore them. */ continue; @@ -1758,13 +1838,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) continue; } - if (i == argc-1) { - log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", - argv[i]); - config_free_lines(front); - return -1; - } - *new = tor_malloc_zero(sizeof(config_line_t)); s = argv[i]; @@ -1773,15 +1846,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) s++; if (*s == '-') s++; + /* Figure out the command, if any. */ + if (*s == '+') { + s++; + command = CONFIG_LINE_APPEND; + } else if (*s == '/') { + s++; + command = CONFIG_LINE_CLEAR; + /* A 'clear' command has no argument. */ + want_arg = 0; + } + + if (want_arg && i == argc-1) { + log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", + argv[i]); + config_free_lines(front); + return -1; + } (*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1)); - (*new)->value = tor_strdup(argv[i+1]); + (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup(""); + (*new)->command = command; (*new)->next = NULL; log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'", (*new)->key, (*new)->value); new = &((*new)->next); - i += 2; + i += want_arg ? 2 : 1; } *result = front; return 0; @@ -1796,7 +1887,7 @@ config_line_append(config_line_t **lst, { config_line_t *newline; - newline = tor_malloc(sizeof(config_line_t)); + newline = tor_malloc_zero(sizeof(config_line_t)); newline->key = tor_strdup(key); newline->value = tor_strdup(val); newline->next = NULL; @@ -1809,9 +1900,12 @@ config_line_append(config_line_t **lst, /** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Return 0 on success, -1 on failure. Warn and ignore any - * misformatted lines. */ + * misformatted lines. + * + * If <b>extended</b> is set, then treat keys beginning with / and with + as + * indicating "clear" and "append" respectively. */ int -config_get_lines(const char *string, config_line_t **result) +config_get_lines(const char *string, config_line_t **result, int extended) { config_line_t *list = NULL, **next; char *k, *v; @@ -1827,13 +1921,30 @@ config_get_lines(const char *string, config_line_t **result) return -1; } if (k && v) { + unsigned command = CONFIG_LINE_NORMAL; + if (extended) { + if (k[0] == '+') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + command = CONFIG_LINE_APPEND; + } else if (k[0] == '/') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + tor_free(v); + v = tor_strdup(""); + command = CONFIG_LINE_CLEAR; + } + } /* This list can get long, so we keep a pointer to the end of it * rather than using config_line_append over and over and getting * n^2 performance. */ - *next = tor_malloc(sizeof(config_line_t)); + *next = tor_malloc_zero(sizeof(config_line_t)); (*next)->key = k; (*next)->value = v; (*next)->next = NULL; + (*next)->command = command; next = &((*next)->next); } else { tor_free(k); @@ -2061,7 +2172,19 @@ config_assign_value(const config_format_t *fmt, or_options_t *options, case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: - config_line_append((config_line_t**)lvalue, c->key, c->value); + { + config_line_t *lastval = *(config_line_t**)lvalue; + if (lastval && lastval->fragile) { + if (c->command != CONFIG_LINE_APPEND) { + config_free_lines(lastval); + *(config_line_t**)lvalue = NULL; + } else { + lastval->fragile = 0; + } + } + + config_line_append((config_line_t**)lvalue, c->key, c->value); + } break; case CONFIG_TYPE_OBSOLETE: log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); @@ -2077,6 +2200,28 @@ config_assign_value(const config_format_t *fmt, or_options_t *options, return 0; } +/** Mark every linelist in <b>options<b> "fragile", so that fresh assignments + * to it will replace old ones. */ +static void +config_mark_lists_fragile(const config_format_t *fmt, or_options_t *options) +{ + int i; + tor_assert(fmt); + tor_assert(options); + + for (i = 0; fmt->vars[i].name; ++i) { + const config_var_t *var = &fmt->vars[i]; + config_line_t *list; + if (var->type != CONFIG_TYPE_LINELIST && + var->type != CONFIG_TYPE_LINELIST_V) + continue; + + list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset); + if (list) + list->fragile = 1; + } +} + /** If <b>c</b> is a syntactically valid configuration line, update * <b>options</b> with its value and return 0. Otherwise return -1 for bad * key, -2 for bad value. @@ -2119,8 +2264,9 @@ config_assign_line(const config_format_t *fmt, or_options_t *options, if (!strlen(c->value)) { /* reset or clear it, then return */ if (!clear_first) { - if (var->type == CONFIG_TYPE_LINELIST || - var->type == CONFIG_TYPE_LINELIST_S) { + if ((var->type == CONFIG_TYPE_LINELIST || + var->type == CONFIG_TYPE_LINELIST_S) && + c->command != CONFIG_LINE_CLEAR) { /* We got an empty linelist from the torrc or command line. As a special case, call this an error. Warn and ignore. */ log_warn(LD_CONFIG, @@ -2130,6 +2276,8 @@ config_assign_line(const config_format_t *fmt, or_options_t *options, } } return 0; + } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { + option_reset(fmt, options, var, use_defaults); } if (options_seen && (var->type != CONFIG_TYPE_LINELIST && @@ -2224,7 +2372,7 @@ config_lines_dup(const config_line_t *inp) config_line_t *result = NULL; config_line_t **next_out = &result; while (inp) { - *next_out = tor_malloc(sizeof(config_line_t)); + *next_out = tor_malloc_zero(sizeof(config_line_t)); (*next_out)->key = tor_strdup(inp->key); (*next_out)->value = tor_strdup(inp->value); inp = inp->next; @@ -2461,6 +2609,12 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list, list = list->next; } bitarray_free(options_seen); + + /** Now we're done assigning a group of options to the configuration. + * Subsequent group assignments should _replace_ linelists, not extend + * them. */ + config_mark_lists_fragile(fmt, options); + return 0; } @@ -2903,37 +3057,6 @@ options_init(or_options_t *options) config_init(&options_format, options); } -/* Check if the port number given in <b>port_option</b> in combination with - * the specified port in <b>listen_options</b> will result in Tor actually - * opening a low port (meaning a port lower than 1024). Return 1 if - * it is, or 0 if it isn't or the concept of a low port isn't applicable for - * the platform we're on. */ -static int -is_listening_on_low_port(int port_option, - const config_line_t *listen_options) -{ -#ifdef MS_WINDOWS - (void) port_option; - (void) listen_options; - return 0; /* No port is too low for windows. */ -#else - const config_line_t *l; - uint16_t p; - if (port_option == 0) - return 0; /* We're not listening */ - if (listen_options == NULL) - return (port_option < 1024); - - for (l = listen_options; l; l = l->next) { - addr_port_lookup(LOG_WARN, l->value, NULL, NULL, &p); - if (p<1024) { - return 1; - } - } - return 0; -#endif -} - /** Set all vars in the configuration object <b>options</b> to their default * values. */ static void @@ -2956,24 +3079,30 @@ config_init(const config_format_t *fmt, void *options) * Else, if comment_defaults, write default values as comments. */ static char * -config_dump(const config_format_t *fmt, const void *options, int minimal, +config_dump(const config_format_t *fmt, const void *default_options, + const void *options, int minimal, int comment_defaults) { smartlist_t *elements; - or_options_t *defaults; + const or_options_t *defaults = default_options; + void *defaults_tmp = NULL; config_line_t *line, *assigned; char *result; int i; char *msg = NULL; - defaults = config_alloc(fmt); - config_init(fmt, defaults); + if (defaults == NULL) { + defaults = defaults_tmp = config_alloc(fmt); + config_init(fmt, defaults_tmp); + } /* XXX use a 1 here so we don't add a new log line while dumping */ - if (fmt->validate_fn(NULL,defaults, 1, &msg) < 0) { - log_err(LD_BUG, "Failed to validate default config."); - tor_free(msg); - tor_assert(0); + if (default_options == NULL) { + if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) { + log_err(LD_BUG, "Failed to validate default config."); + tor_free(msg); + tor_assert(0); + } } elements = smartlist_create(); @@ -3015,7 +3144,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal, result = smartlist_join_strings(elements, "", 0, NULL); SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); smartlist_free(elements); - config_free(fmt, defaults); + if (defaults_tmp) + config_free(fmt, defaults_tmp); return result; } @@ -3026,7 +3156,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal, char * options_dump(const or_options_t *options, int minimal) { - return config_dump(&options_format, options, minimal, 0); + return config_dump(&options_format, global_default_options, + options, minimal, 0); } /** Return 0 if every element of sl is a string holding a decimal @@ -3151,7 +3282,7 @@ options_validate(or_options_t *old_options, or_options_t *options, int i; config_line_t *cl; const char *uname = get_uname(); - int n_client_ports=0; + int n_ports=0; #define REJECT(arg) \ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END @@ -3169,13 +3300,7 @@ options_validate(or_options_t *old_options, or_options_t *options, "for details.", uname); } - if (options->ORPort == 0 && options->ORListenAddress != NULL) - REJECT("ORPort must be defined if ORListenAddress is defined."); - - if (options->DirPort == 0 && options->DirListenAddress != NULL) - REJECT("DirPort must be defined if DirListenAddress is defined."); - - if (parse_client_ports(options, 1, msg, &n_client_ports) < 0) + if (parse_ports(options, 1, msg, &n_ports) < 0) return -1; if (validate_data_directory(options)<0) @@ -3222,7 +3347,9 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Can't use a relative path to torrc when RunAsDaemon is set."); #endif - if (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines) + /* XXXX require that the only port not be DirPort? */ + /* XXXX require that at least one port be listened-upon. */ + if (n_ports == 0 && !options->RendConfigLines) log(LOG_WARN, LD_CONFIG, "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all " "undefined, and there aren't any hidden services configured. " @@ -3238,19 +3365,6 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); } - if (options->AccountingMax && - (is_listening_on_low_port(options->ORPort, options->ORListenAddress) || - is_listening_on_low_port(options->DirPort, options->DirListenAddress))) - { - log(LOG_WARN, LD_CONFIG, - "You have set AccountingMax to use hibernation. You have also " - "chosen a low DirPort or OrPort. This combination can make Tor stop " - "working when it tries to re-attach the port after a period of " - "hibernation. Please choose a different port or turn off " - "hibernation unless you know this combination will work on your " - "platform."); - } - if (options->ExcludeExitNodes || options->ExcludeNodes) { options->_ExcludeExitNodesUnion = routerset_new(); routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeExitNodes); @@ -3504,7 +3618,8 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->BridgeRelay && options->DirPort) { log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling " "DirPort"); - options->DirPort = 0; + config_free_lines(options->DirPort); + options->DirPort = NULL; } if (options->MinUptimeHidServDirectoryV2 < 0) { @@ -3745,39 +3860,6 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->ControlListenAddress) { - int all_are_local = 1; - config_line_t *ln; - for (ln = options->ControlListenAddress; ln; ln = ln->next) { - if (strcmpstart(ln->value, "127.")) - all_are_local = 0; - } - if (!all_are_local) { - if (!options->HashedControlPassword && - !options->HashedControlSessionPassword && - !options->CookieAuthentication) { - log_warn(LD_CONFIG, - "You have a ControlListenAddress set to accept " - "unauthenticated connections from a non-local address. " - "This means that programs not running on your computer " - "can reconfigure your Tor, without even having to guess a " - "password. That's so bad that I'm closing your ControlPort " - "for you. If you need to control your Tor remotely, try " - "enabling authentication and using a tool like stunnel or " - "ssh to encrypt remote access."); - options->ControlPort = 0; - } else { - log_warn(LD_CONFIG, "You have a ControlListenAddress set to accept " - "connections from a non-local address. This means that " - "programs not running on your computer can reconfigure your " - "Tor. That's pretty bad, since the controller " - "protocol isn't encrypted! Maybe you should just listen on " - "127.0.0.1 and use a tool like stunnel or ssh to encrypt " - "remote connections to your control port."); - } - } - } - if (options->ControlPort && !options->HashedControlPassword && !options->HashedControlSessionPassword && !options->CookieAuthentication) { @@ -4000,8 +4082,9 @@ options_validate(or_options_t *old_options, or_options_t *options, } }); - if (options->BridgeRelay == 1 && options->ORPort == 0) - REJECT("BridgeRelay is 1, ORPort is 0. This is an invalid combination."); + if (options->BridgeRelay == 1 && ! options->ORPort) + REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " + "combination."); return 0; #undef REJECT @@ -4092,7 +4175,7 @@ options_transition_affects_workers(const or_options_t *old_options, { if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || old_options->NumCPUs != new_options->NumCPUs || - old_options->ORPort != new_options->ORPort || + !config_lines_eq(old_options->ORPort, new_options->ORPort) || old_options->ServerDNSSearchDomains != new_options->ServerDNSSearchDomains || old_options->_SafeLogging != new_options->_SafeLogging || @@ -4122,9 +4205,10 @@ options_transition_affects_descriptor(const or_options_t *old_options, !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || old_options->ExitPolicyRejectPrivate != new_options->ExitPolicyRejectPrivate || - old_options->ORPort != new_options->ORPort || - old_options->DirPort != new_options->DirPort || + !config_lines_eq(old_options->ORPort, new_options->ORPort) || + !config_lines_eq(old_options->DirPort, new_options->DirPort) || old_options->ClientOnly != new_options->ClientOnly || + old_options->DisableNetwork != new_options->DisableNetwork || old_options->_PublishServerDescriptor != new_options->_PublishServerDescriptor || get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || @@ -4198,17 +4282,25 @@ get_windows_conf_root(void) } #endif -/** Return the default location for our torrc file. */ +/** Return the default location for our torrc file. + * DOCDOC defaults_file */ static const char * -get_default_conf_file(void) +get_default_conf_file(int defaults_file) { #ifdef MS_WINDOWS - static char path[MAX_PATH+1]; - strlcpy(path, get_windows_conf_root(), MAX_PATH); - strlcat(path,"\\torrc",MAX_PATH); - return path; + if (defaults_file) { + static char defaults_path[MAX_PATH+1]; + tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults", + get_windows_conf_root()); + return defaults_path; + } else { + static char path[MAX_PATH+1]; + tor_snprintf(path, MAX_PATH, "%s\\torrc", + get_windows_conf_root()); + return path; + } #else - return (CONFDIR "/torrc"); + return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc"; #endif } @@ -4241,37 +4333,46 @@ check_nickname_list(const char *lst, const char *name, char **msg) return r; } -/** Learn config file name from command line arguments, or use the default */ +/** Learn config file name from command line arguments, or use the default, + * DOCDOC defaults_file */ static char * find_torrc_filename(int argc, char **argv, + int defaults_file, int *using_default_torrc, int *ignore_missing_torrc) { char *fname=NULL; int i; + const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f"; + const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc"; + + if (defaults_file) + *ignore_missing_torrc = 1; for (i = 1; i < argc; ++i) { - if (i < argc-1 && !strcmp(argv[i],"-f")) { + if (i < argc-1 && !strcmp(argv[i],fname_opt)) { if (fname) { - log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line."); + log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.", + fname_opt); tor_free(fname); } fname = expand_filename(argv[i+1]); *using_default_torrc = 0; ++i; - } else if (!strcmp(argv[i],"--ignore-missing-torrc")) { + } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) { *ignore_missing_torrc = 1; } } if (*using_default_torrc) { /* didn't find one, try CONFDIR */ - const char *dflt = get_default_conf_file(); + const char *dflt = get_default_conf_file(defaults_file); if (dflt && file_status(dflt) == FN_FILE) { fname = tor_strdup(dflt); } else { #ifndef MS_WINDOWS - char *fn; - fn = expand_filename("~/.torrc"); + char *fn = NULL; + if (!defaults_file) + fn = expand_filename("~/.torrc"); if (fn && file_status(fn) == FN_FILE) { fname = fn; } else { @@ -4286,31 +4387,34 @@ find_torrc_filename(int argc, char **argv, return fname; } -/** Load torrc from disk, setting torrc_fname if successful */ +/** Load torrc from disk, setting torrc_fname if successful. + * DOCDOC defaults_file */ static char * -load_torrc_from_disk(int argc, char **argv) +load_torrc_from_disk(int argc, char **argv, int defaults_file) { char *fname=NULL; char *cf = NULL; int using_default_torrc = 1; int ignore_missing_torrc = 0; + char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname; - fname = find_torrc_filename(argc, argv, + fname = find_torrc_filename(argc, argv, defaults_file, &using_default_torrc, &ignore_missing_torrc); tor_assert(fname); log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname); - tor_free(torrc_fname); - torrc_fname = fname; + tor_free(*fname_var); + *fname_var = fname; /* Open config file */ if (file_status(fname) != FN_FILE || !(cf = read_file_to_str(fname,0,NULL))) { - if (using_default_torrc == 1 || ignore_missing_torrc ) { - log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " - "using reasonable defaults.", fname); + if (using_default_torrc == 1 || ignore_missing_torrc) { + if (!defaults_file) + log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " + "using reasonable defaults.", fname); tor_free(fname); /* sets fname to NULL */ - torrc_fname = NULL; + *fname_var = NULL; cf = tor_strdup(""); } else { log(LOG_WARN, LD_CONFIG, @@ -4324,7 +4428,7 @@ load_torrc_from_disk(int argc, char **argv) return cf; err: tor_free(fname); - torrc_fname = NULL; + *fname_var = NULL; return NULL; } @@ -4335,8 +4439,9 @@ load_torrc_from_disk(int argc, char **argv) int options_init_from_torrc(int argc, char **argv) { - char *cf=NULL; - int i, retval, command; + char *cf=NULL, *cf_defaults=NULL; + int i, command; + int retval = -1; static char **backup_argv; static int backup_argc; char *command_arg = NULL; @@ -4395,24 +4500,24 @@ options_init_from_torrc(int argc, char **argv) if (command == CMD_HASH_PASSWORD) { cf = tor_strdup(""); } else { - cf = load_torrc_from_disk(argc, argv); + cf_defaults = load_torrc_from_disk(argc, argv, 1); + cf = load_torrc_from_disk(argc, argv, 0); if (!cf) goto err; } - retval = options_init_from_string(cf, command, command_arg, &errmsg); - tor_free(cf); - if (retval < 0) - goto err; - - return 0; + retval = options_init_from_string(cf_defaults, cf, command, command_arg, + &errmsg); err: + + tor_free(cf); + tor_free(cf_defaults); if (errmsg) { log(LOG_WARN,LD_CONFIG,"%s", errmsg); tor_free(errmsg); } - return -1; + return retval < 0 ? -1 : 0; } /** Load the options from the configuration in <b>cf</b>, validate @@ -4425,13 +4530,13 @@ options_init_from_torrc(int argc, char **argv) * * -4 for error while setting the new options */ setopt_err_t -options_init_from_string(const char *cf, +options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg) { - or_options_t *oldoptions, *newoptions; + or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL; config_line_t *cl; - int retval; + int retval, i; setopt_err_t err = SETOPT_ERR_MISC; tor_assert(msg); @@ -4444,17 +4549,24 @@ options_init_from_string(const char *cf, newoptions->command = command; newoptions->command_arg = command_arg; - /* get config lines, assign them */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + /* get config lines, assign them */ + retval = config_get_lines(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } /* Go through command-line variables too */ @@ -4489,6 +4601,8 @@ options_init_from_string(const char *cf, /* Clear newoptions and re-initialize them with new defaults. */ config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); + newdefaultoptions = NULL; newoptions = tor_malloc_zero(sizeof(or_options_t)); newoptions->_magic = OR_OPTIONS_MAGIC; options_init(newoptions); @@ -4496,22 +4610,24 @@ options_init_from_string(const char *cf, newoptions->command_arg = command_arg; /* Assign all options a second time. */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, - global_cmdline_options, 0, 0, msg); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + /* get config lines, assign them */ + retval = config_get_lines(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } } @@ -4530,11 +4646,14 @@ options_init_from_string(const char *cf, err = SETOPT_ERR_SETTING; goto err; /* frees and replaces old options */ } + config_free(&options_format, global_default_options); + global_default_options = newdefaultoptions; return SETOPT_OK; err: config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); if (*msg) { char *old_msg = *msg; tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg); @@ -4546,12 +4665,14 @@ options_init_from_string(const char *cf, /** Return the location for our configuration file. */ const char * -get_torrc_fname(void) +get_torrc_fname(int defaults_fname) { - if (torrc_fname) - return torrc_fname; + const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname; + + if (fname) + return fname; else - return get_default_conf_file(); + return get_default_conf_file(defaults_fname); } /** Adjust the address map based on the MapAddress elements in the @@ -4567,24 +4688,60 @@ config_register_addressmaps(const or_options_t *options) addressmap_clear_configured(); elts = smartlist_create(); for (opt = options->AddressMap; opt; opt = opt->next) { + int from_wildcard = 0, to_wildcard = 0; smartlist_split_string(elts, opt->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); - if (smartlist_len(elts) >= 2) { - from = smartlist_get(elts,0); - to = smartlist_get(elts,1); - if (address_is_invalid_destination(to, 1)) { - log_warn(LD_CONFIG, - "Skipping invalid argument '%s' to MapAddress", to); - } else { - addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC); - if (smartlist_len(elts)>2) { - log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); - } - } - } else { + if (smartlist_len(elts) < 2) { log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.", opt->value); + goto cleanup; + } + + from = smartlist_get(elts,0); + to = smartlist_get(elts,1); + + if (to[0] == '.' || from[0] == '.') { + log_warn(LD_CONFIG,"MapAddress '%s' is ambiguous - address starts with a" + "'.'. Ignoring.",opt->value); + goto cleanup; + } + + if (!strcmp(to, "*") || !strcmp(from, "*")) { + log_warn(LD_CONFIG,"MapAddress '%s' is unsupported - can't remap from " + "or to *. Ignoring.",opt->value); + goto cleanup; + } + /* Detect asterisks in expressions of type: '*.example.com' */ + if (!strncmp(from,"*.",2)) { + from += 2; + from_wildcard = 1; + } + if (!strncmp(to,"*.",2)) { + to += 2; + to_wildcard = 1; + } + + if (to_wildcard && !from_wildcard) { + log_warn(LD_CONFIG, + "Skipping invalid argument '%s' to MapAddress: " + "can only use wildcard (i.e. '*.') if 'from' address " + "uses wildcard also", opt->value); + goto cleanup; + } + + if (address_is_invalid_destination(to, 1)) { + log_warn(LD_CONFIG, + "Skipping invalid argument '%s' to MapAddress", opt->value); + goto cleanup; } + + addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC, + from_wildcard, to_wildcard); + + if (smartlist_len(elts) > 2) + log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); + + cleanup: SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); smartlist_clear(elts); } @@ -5156,7 +5313,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type, * clause once Tor 0.1.2.17 is obsolete. */ log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your " "torrc file (%s), or reinstall Tor and use the default torrc.", - get_torrc_fname()); + get_torrc_fname(0)); goto err; } if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) { @@ -5220,12 +5377,53 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname) } SMARTLIST_FOREACH_END(port); } -#define CL_PORT_NO_OPTIONS (1u<<0) +/** DOCDOC */ +static void +warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) +{ + int warned = 0; + SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { + if (port->type != CONN_TYPE_CONTROL_LISTENER) + continue; + if (port->is_unix_addr) + continue; + if (!tor_addr_is_loopback(&port->addr)) { + if (forbid) { + if (!warned) + log_warn(LD_CONFIG, + "You have a ControlPort set to accept " + "unauthenticated connections from a non-local address. " + "This means that programs not running on your computer " + "can reconfigure your Tor, without even having to guess a " + "password. That's so bad that I'm closing your ControlPort " + "for you. If you need to control your Tor remotely, try " + "enabling authentication and using a tool like stunnel or " + "ssh to encrypt remote access."); + warned = 1; + port_cfg_free(port); + SMARTLIST_DEL_CURRENT(ports, port); + } else { + log_warn(LD_CONFIG, "You have a ControlPort set to accept " + "connections from a non-local address. This means that " + "programs not running on your computer can reconfigure your " + "Tor. That's pretty bad, since the controller " + "protocol isn't encrypted! Maybe you should just listen on " + "127.0.0.1 and use a tool like stunnel or ssh to encrypt " + "remote connections to your control port."); + return; /* No point in checking the rest */ + } + } + } SMARTLIST_FOREACH_END(port); +} + +#define CL_PORT_NO_OPTIONS (1u<<0) #define CL_PORT_WARN_NONLOCAL (1u<<1) #define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) +#define CL_PORT_SERVER_OPTIONS (1u<<3) +#define CL_PORT_FORBID_NONLOCAL (1u<<4) /** - * Parse port configuration for a single client port type. + * Parse port configuration for a single port type. * * Read entries of the "FooPort" type from the list <b>ports</b>, and * entries of the "FooListenAddress" type from the list @@ -5245,17 +5443,22 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname) * isolation options in the FooPort entries. * * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the - * ports are not on a local address. + * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set, + * this is a contrl port with no password set: don't even allow it. * * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn * if FooListenAddress is set but FooPort is 0. * + * If CL_PORT_SERVER_OPTIONS is set in <b>flags</b>, do not allow stream + * isolation options in the FooPort entries; instead allow the + * server-port option set. + * * On success, if <b>out</b> is given, add a new port_cfg_t entry to * <b>out</b> for every port that the client should listen on. Return 0 * on success, -1 on failure. */ static int -parse_client_port_config(smartlist_t *out, +parse_port_config(smartlist_t *out, const config_line_t *ports, const config_line_t *listenaddrs, const char *portname, @@ -5266,8 +5469,11 @@ parse_client_port_config(smartlist_t *out, { smartlist_t *elts; int retval = -1; - const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS); + const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER); + const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS; + const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS; const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL; + const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; const unsigned allow_spurious_listenaddr = flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; @@ -5303,6 +5509,17 @@ parse_client_port_config(smartlist_t *out, return -1; } + if (use_server_options && out) { + /* Add a no_listen port. */ + port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); + cfg->type = listener_type; + cfg->port = mainport; + tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */ + cfg->no_listen = 1; + cfg->ipv4_only = 1; + smartlist_add(out, cfg); + } + for (; listenaddrs; listenaddrs = listenaddrs->next) { tor_addr_t addr; uint16_t port = 0; @@ -5318,12 +5535,17 @@ parse_client_port_config(smartlist_t *out, tor_addr_copy(&cfg->addr, &addr); cfg->session_group = SESSION_GROUP_UNSET; cfg->isolation_flags = ISO_DEFAULT; + cfg->no_advertise = 1; smartlist_add(out, cfg); } } - if (warn_nonlocal && out) - warn_nonlocal_client_ports(out, portname); + if (warn_nonlocal && out) { + if (is_control) + warn_nonlocal_controller_ports(out, forbid_nonlocal); + else + warn_nonlocal_client_ports(out, portname); + } return 0; } /* end if (listenaddrs) */ @@ -5355,6 +5577,8 @@ parse_client_port_config(smartlist_t *out, char *addrport; uint16_t ptmp=0; int ok; + int no_listen = 0, no_advertise = 0, all_addrs = 0, + ipv4_only = 0, ipv6_only = 0; smartlist_split_string(elts, ports->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); @@ -5363,7 +5587,7 @@ parse_client_port_config(smartlist_t *out, goto err; } - if (!allow_client_options && smartlist_len(elts) > 1) { + if (allow_no_options && smartlist_len(elts) > 1) { log_warn(LD_CONFIG, "Too many options on %sPort line", portname); goto err; } @@ -5402,56 +5626,107 @@ parse_client_port_config(smartlist_t *out, } /* Now parse the rest of the options, if any. */ - SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { - int no = 0, isoflag = 0; - const char *elt_orig = elt; - if (elt_sl_idx == 0) - continue; /* Skip addr:port */ - if (!strcasecmpstart(elt, "SessionGroup=")) { - int group = (int)tor_parse_long(elt+strlen("SessionGroup="), - 10, 0, INT_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "Invalid %sPort option '%s'", + if (use_server_options) { + /* This is a server port; parse advertising options */ + SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { + if (elt_sl_idx == 0) + continue; /* Skip addr:port */ + + if (!strcasecmp(elt, "NoAdvertise")) { + no_advertise = 1; + } else if (!strcasecmp(elt, "NoListen")) { + no_listen = 1; +#if 0 + /* not implemented yet. */ + } else if (!strcasecmp(elt, "AllAddrs")) { + + all_addrs = 1; +#endif + } else if (!strcasecmp(elt, "IPv4Only")) { + ipv4_only = 1; + } else if (!strcasecmp(elt, "IPv6Only")) { + ipv6_only = 1; + } else { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", portname, escaped(elt)); - goto err; - } - if (sessiongroup >= 0) { - log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort", - portname); - goto err; } - sessiongroup = group; - continue; - } + } SMARTLIST_FOREACH_END(elt); - if (!strcasecmpstart(elt, "No")) { - no = 1; - elt += 2; + if (no_advertise && no_listen) { + log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise " + "on %sPort line '%s'", + portname, escaped(ports->value)); + goto err; } - if (!strcasecmpend(elt, "s")) - elt[strlen(elt)-1] = '\0'; /* kill plurals. */ - - if (!strcasecmp(elt, "IsolateDestPort")) { - isoflag = ISO_DESTPORT; - } else if (!strcasecmp(elt, "IsolateDestAddr")) { - isoflag = ISO_DESTADDR; - } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) { - isoflag = ISO_SOCKSAUTH; - } else if (!strcasecmp(elt, "IsolateClientProtocol")) { - isoflag = ISO_CLIENTPROTO; - } else if (!strcasecmp(elt, "IsolateClientAddr")) { - isoflag = ISO_CLIENTADDR; - } else { - log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", - portname, escaped(elt_orig)); + if (ipv4_only && ipv6_only) { + log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only " + "on %sPort line '%s'", + portname, escaped(ports->value)); + goto err; } - - if (no) { - isolation &= ~isoflag; - } else { - isolation |= isoflag; + if (ipv4_only && tor_addr_family(&addr) == AF_INET6) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", + portname); + goto err; + } + if (ipv6_only && tor_addr_family(&addr) == AF_INET) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", + portname); + goto err; } - } SMARTLIST_FOREACH_END(elt); + } else { + /* This is a client port; parse isolation options */ + SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { + int no = 0, isoflag = 0; + const char *elt_orig = elt; + if (elt_sl_idx == 0) + continue; /* Skip addr:port */ + if (!strcasecmpstart(elt, "SessionGroup=")) { + int group = (int)tor_parse_long(elt+strlen("SessionGroup="), + 10, 0, INT_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, "Invalid %sPort option '%s'", + portname, escaped(elt)); + goto err; + } + if (sessiongroup >= 0) { + log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort", + portname); + goto err; + } + sessiongroup = group; + continue; + } + + if (!strcasecmpstart(elt, "No")) { + no = 1; + elt += 2; + } + if (!strcasecmpend(elt, "s")) + elt[strlen(elt)-1] = '\0'; /* kill plurals. */ + + if (!strcasecmp(elt, "IsolateDestPort")) { + isoflag = ISO_DESTPORT; + } else if (!strcasecmp(elt, "IsolateDestAddr")) { + isoflag = ISO_DESTADDR; + } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) { + isoflag = ISO_SOCKSAUTH; + } else if (!strcasecmp(elt, "IsolateClientProtocol")) { + isoflag = ISO_CLIENTPROTO; + } else if (!strcasecmp(elt, "IsolateClientAddr")) { + isoflag = ISO_CLIENTADDR; + } else { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", + portname, escaped(elt_orig)); + } + + if (no) { + isolation &= ~isoflag; + } else { + isolation |= isoflag; + } + } SMARTLIST_FOREACH_END(elt); + } if (out && port) { port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); @@ -5460,14 +5735,24 @@ parse_client_port_config(smartlist_t *out, tor_addr_copy(&cfg->addr, &addr); cfg->session_group = sessiongroup; cfg->isolation_flags = isolation; + cfg->no_listen = no_listen; + cfg->no_listen = no_advertise; + cfg->all_addrs = all_addrs; + cfg->ipv4_only = ipv4_only; + cfg->ipv6_only = ipv6_only; + smartlist_add(out, cfg); } SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_clear(elts); } - if (warn_nonlocal && out) - warn_nonlocal_client_ports(out, portname); + if (warn_nonlocal && out) { + if (is_control) + warn_nonlocal_controller_ports(out, forbid_nonlocal); + else + warn_nonlocal_client_ports(out, portname); + } retval = 0; err: @@ -5476,6 +5761,27 @@ parse_client_port_config(smartlist_t *out, return retval; } +/** DOCDOC */ +static int +parse_socket_config(smartlist_t *out, const config_line_t *cfg, + int listener_type) +{ + + if (!out) + return 0; + + for ( ; cfg; cfg = cfg->next) { + size_t len = strlen(cfg->value); + port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); + port->is_unix_addr = 1; + memcpy(port->unix_addr, cfg->value, len+1); + port->type = listener_type; + smartlist_add(out, port); + } + + return 0; +} + /** Parse all client port types (Socks, DNS, Trans, NATD) from * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of * ports that are listed and return 0. On failure, set *<b>msg</b> to a @@ -5485,8 +5791,8 @@ parse_client_port_config(smartlist_t *out, * new list of ports parsed from <b>options</b>. **/ static int -parse_client_ports(const or_options_t *options, int validate_only, - char **msg, int *n_ports_out) +parse_ports(const or_options_t *options, int validate_only, + char **msg, int *n_ports_out) { smartlist_t *ports; int retval = -1; @@ -5495,7 +5801,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *n_ports_out = 0; - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->SocksPort, options->SocksListenAddress, "Socks", CONN_TYPE_AP_LISTENER, "127.0.0.1", 9050, @@ -5503,7 +5809,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration"); goto err; } - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->DNSPort, options->DNSListenAddress, "DNS", CONN_TYPE_AP_DNS_LISTENER, "127.0.0.1", 0, @@ -5511,7 +5817,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration"); goto err; } - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->TransPort, options->TransListenAddress, "Trans", CONN_TYPE_AP_TRANS_LISTENER, "127.0.0.1", 0, @@ -5519,7 +5825,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration"); goto err; } - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->NATDPort, options->NATDListenAddress, "NATD", CONN_TYPE_AP_NATD_LISTENER, "127.0.0.1", 0, @@ -5527,16 +5833,63 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration"); goto err; } + { + unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL; + const int any_passwords = (options->HashedControlPassword || + options->HashedControlSessionPassword || + options->CookieAuthentication); + if (! any_passwords) + control_port_flags |= CL_PORT_FORBID_NONLOCAL; + + if (parse_port_config(ports, + options->ControlPort, options->ControlListenAddress, + "Control", CONN_TYPE_CONTROL_LISTENER, + "127.0.0.1", 0, + control_port_flags) < 0) { + *msg = tor_strdup("Invalid ControlPort/ControlListenAddress " + "configuration"); + goto err; + } + if (parse_socket_config(ports, + options->ControlSocket, + CONN_TYPE_CONTROL_LISTENER) < 0) { + *msg = tor_strdup("Invalid ControlSocket configuration"); + goto err; + } + } + if (! options->ClientOnly) { + if (parse_port_config(ports, + options->ORPort, options->ORListenAddress, + "OR", CONN_TYPE_OR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration"); + goto err; + } + if (parse_port_config(ports, + options->DirPort, options->DirListenAddress, + "Dir", CONN_TYPE_DIR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration"); + goto err; + } + } + + if (check_server_ports(ports, options) < 0) { + *msg = tor_strdup("Misconfigured server ports"); + goto err; + } *n_ports_out = smartlist_len(ports); if (!validate_only) { - if (configured_client_ports) { - SMARTLIST_FOREACH(configured_client_ports, + if (configured_ports) { + SMARTLIST_FOREACH(configured_ports, port_cfg_t *, p, port_cfg_free(p)); - smartlist_free(configured_client_ports); + smartlist_free(configured_ports); } - configured_client_ports = ports; + configured_ports = ports; ports = NULL; /* prevent free below. */ } @@ -5549,14 +5902,107 @@ parse_client_ports(const or_options_t *options, int validate_only, return retval; } +/** DOCDOC */ +static int +check_server_ports(const smartlist_t *ports, + const or_options_t *options) +{ + int n_orport_advertised = 0; + int n_orport_advertised_ipv4 = 0; + int n_orport_listeners = 0; + int n_dirport_advertised = 0; + int n_dirport_listeners = 0; + int n_low_port = 0; + int r = 0; + + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + if (port->type == CONN_TYPE_DIR_LISTENER) { + if (! port->no_advertise) + ++n_dirport_advertised; + if (! port->no_listen) + ++n_dirport_listeners; + } else if (port->type == CONN_TYPE_OR_LISTENER) { + if (! port->no_advertise) { + ++n_orport_advertised; + if (tor_addr_family(&port->addr) == AF_INET || + (tor_addr_family(&port->addr) == AF_UNSPEC && !port->ipv6_only)) + ++n_orport_advertised_ipv4; + } + if (! port->no_listen) + ++n_orport_listeners; + } else { + continue; + } +#ifndef MS_WINDOWS + if (!port->no_advertise && port->port < 1024) + ++n_low_port; +#endif + } SMARTLIST_FOREACH_END(port); + + if (n_orport_advertised && !n_orport_listeners) { + log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually " + "listening on one."); + r = -1; + } + if (n_dirport_advertised && !n_dirport_listeners) { + log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually " + "listening on one."); + r = -1; + } + if (n_dirport_advertised > 1) { + log_warn(LD_CONFIG, "Can't advertise more than one DirPort."); + r = -1; + } + if (n_orport_advertised && !n_orport_advertised_ipv4 && + !options->BridgeRelay) { + log_warn(LD_CONFIG, "Configured non-bridge only to listen on an IPv6 " + "address."); + r = -1; + } + + if (n_low_port && options->AccountingMax) { + log(LOG_WARN, LD_CONFIG, + "You have set AccountingMax to use hibernation. You have also " + "chosen a low DirPort or OrPort. This combination can make Tor stop " + "working when it tries to re-attach the port after a period of " + "hibernation. Please choose a different port or turn off " + "hibernation unless you know this combination will work on your " + "platform."); + } + + return r; +} + /** Return a list of port_cfg_t for client ports parsed from the * options. */ const smartlist_t * -get_configured_client_ports(void) +get_configured_ports(void) +{ + if (!configured_ports) + configured_ports = smartlist_create(); + return configured_ports; +} + +/** Return the first advertised port of type <b>listener_type</b> in + <b>address_family</b>. */ +int +get_first_advertised_port_by_type_af(int listener_type, int address_family) { - if (!configured_client_ports) - configured_client_ports = smartlist_create(); - return configured_client_ports; + if (!configured_ports) + return 0; + SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { + if (cfg->type == listener_type && + !cfg->no_advertise && + (tor_addr_family(&cfg->addr) == address_family || + tor_addr_family(&cfg->addr) == AF_UNSPEC)) { + if (tor_addr_family(&cfg->addr) != AF_UNSPEC || + (address_family == AF_INET && !cfg->ipv6_only) || + (address_family == AF_INET6 && !cfg->ipv4_only)) { + return cfg->port; + } + } + } SMARTLIST_FOREACH_END(cfg); + return 0; } /** Adjust the value of options->DataDirectory, or fill it in if it's @@ -5715,7 +6161,7 @@ options_save_current(void) * If we try falling back to datadirectory or something, we have a better * chance of saving the configuration, but a better chance of doing * something the user never expected. */ - return write_configuration_file(get_torrc_fname(), get_options()); + return write_configuration_file(get_torrc_fname(0), get_options()); } /** Mapping from a unit name to a multiplier for converting that unit into a @@ -5934,7 +6380,7 @@ init_libevent(const or_options_t *options) suppress_libevent_log_msg(NULL); tor_check_libevent_version(tor_libevent_get_method(), - get_options()->ORPort != 0, + get_options()->ORPort != NULL, &badness); if (badness) { const char *v = tor_libevent_get_version_str(); @@ -6181,7 +6627,7 @@ or_state_load(void) if (contents) { config_line_t *lines=NULL; int assign_retval; - if (config_get_lines(contents, &lines)<0) + if (config_get_lines(contents, &lines, 0)<0) goto done; assign_retval = config_assign(&state_format, new_state, lines, 0, 0, &errmsg); @@ -6285,7 +6731,7 @@ or_state_save(time_t now) tor_free(global_state->TorVersion); tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); - state = config_dump(&state_format, global_state, 1, 0); + state = config_dump(&state_format, NULL, global_state, 1, 0); format_local_iso_time(tbuf, now); tor_asprintf(&contents, "# Tor state file last generated on %s local time\n" diff --git a/src/or/config.h b/src/or/config.h index 76f6841d7..2d94192d3 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -23,24 +23,24 @@ const char *escaped_safe_str_client(const char *address); const char *escaped_safe_str(const char *address); const char *get_version(void); -int config_get_lines(const char *string, config_line_t **result); +int config_get_lines(const char *string, config_line_t **result, int extended); void config_free_lines(config_line_t *front); setopt_err_t options_trial_assign(config_line_t *list, int use_defaults, int clear_first, char **msg); int resolve_my_address(int warn_severity, const or_options_t *options, uint32_t *addr, char **hostname_out); -int is_local_addr(const tor_addr_t *addr) ATTR_PURE; +int is_local_addr(const tor_addr_t *addr); void options_init(or_options_t *options); char *options_dump(const or_options_t *options, int minimal); int options_init_from_torrc(int argc, char **argv); -setopt_err_t options_init_from_string(const char *cf, +setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg); int option_is_recognized(const char *key); const char *option_get_canonical_name(const char *key); config_line_t *option_get_assignment(const or_options_t *options, const char *key); int options_save_current(void); -const char *get_torrc_fname(void); +const char *get_torrc_fname(int defaults_fname); char *options_get_datadir_fname2_suffix(const or_options_t *options, const char *sub1, const char *sub2, const char *suffix); @@ -64,7 +64,13 @@ or_state_t *get_or_state(void); int did_last_state_file_write_fail(void); int or_state_save(time_t now); -const smartlist_t *get_configured_client_ports(void); +const smartlist_t *get_configured_ports(void); +int get_first_advertised_port_by_type_af(int listener_type, + int address_family); +#define get_primary_or_port() \ + (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET)) +#define get_primary_dir_port() \ + (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET)) int options_need_geoip_info(const or_options_t *options, const char **reason_out); diff --git a/src/or/connection.c b/src/or/connection.c index a52bf4807..7b95acac6 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -880,7 +880,8 @@ connection_create_listener(const struct sockaddr *listensockaddr, return NULL; } - if (listensockaddr->sa_family == AF_INET) { + if (listensockaddr->sa_family == AF_INET || + listensockaddr->sa_family == AF_INET6) { int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); if (is_tcp) start_reading = 1; @@ -890,7 +891,7 @@ connection_create_listener(const struct sockaddr *listensockaddr, log_notice(LD_NET, "Opening %s on %s:%d", conn_type_to_string(type), fmt_addr(&addr), usePort); - s = tor_open_socket(PF_INET, + s = tor_open_socket(tor_addr_family(&addr), is_tcp ? SOCK_STREAM : SOCK_DGRAM, is_tcp ? IPPROTO_TCP: IPPROTO_UDP); if (!SOCKET_OK(s)) { @@ -1318,6 +1319,24 @@ connection_connect(connection_t *conn, const char *address, else protocol_family = PF_INET; + if (get_options()->DisableNetwork) { + /* We should never even try to connect anyplace if DisableNetwork is set. + * Warn if we do, and refuse to make the connection. */ + static ratelim_t disablenet_violated = RATELIM_INIT(30*60); + char *m; +#ifdef MS_WINDOWS + *socket_error = WSAENETUNREACH; +#else + *socket_error = ENETUNREACH; +#endif + if ((m = rate_limit_log(&disablenet_violated, approx_time()))) { + log_warn(LD_BUG, "Tried to open a socket with DisableNetwork set.%s", m); + tor_free(m); + } + tor_fragile_assert(); + return -1; + } + s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP); if (s < 0) { *socket_error = tor_socket_errno(-1); @@ -1796,6 +1815,9 @@ retry_listener_ports(smartlist_t *old_conns, (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr)) continue; + if (wanted->no_listen) + continue; /* We don't want to open a listener for this one */ + if (wanted->is_unix_addr) { if (conn->socket_family == AF_UNIX && !strcmp(wanted->unix_addr, conn->address)) { @@ -1834,6 +1856,8 @@ retry_listener_ports(smartlist_t *old_conns, connection_t *conn; int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port; tor_assert(real_port <= UINT16_MAX); + if (port->no_listen) + continue; if (port->is_unix_addr) { listensockaddr = (struct sockaddr *) @@ -1870,82 +1894,6 @@ retry_listener_ports(smartlist_t *old_conns, return r; } -/** - * Launch any configured listener connections of type <b>type</b>. (A - * listener is configured if <b>port_option</b> is non-zero. If any - * ListenAddress configuration options are given in <b>cfg</b>, create a - * connection binding to each one. Otherwise, create a single - * connection binding to the address <b>default_addr</b>.) - * - * We assume that we're starting with a list of existing listener connection_t - * pointers in <b>old_conns</b>: we do not launch listeners that are already - * in that list. Instead, we just remove them from the list. - * - * All new connections we launch are added to <b>new_conns</b>. - */ -static int -retry_listeners(smartlist_t *old_conns, - int type, const config_line_t *cfg, - int port_option, const char *default_addr, - smartlist_t *new_conns, - int is_sockaddr_un) -{ - smartlist_t *ports = smartlist_create(); - tor_addr_t dflt_addr; - int retval = 0; - - if (default_addr) { - tor_addr_parse(&dflt_addr, default_addr); - } else { - tor_addr_make_unspec(&dflt_addr); - } - - if (port_option) { - if (!cfg) { - port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t)); - tor_addr_copy(&port->addr, &dflt_addr); - port->port = port_option; - port->type = type; - smartlist_add(ports, port); - } else { - const config_line_t *c; - for (c = cfg; c; c = c->next) { - port_cfg_t *port; - tor_addr_t addr; - uint16_t portval = 0; - if (is_sockaddr_un) { - size_t len = strlen(c->value); - port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); - port->is_unix_addr = 1; - memcpy(port->unix_addr, c->value, len+1); - } else { - if (tor_addr_port_lookup(c->value, &addr, &portval) < 0) { - log_warn(LD_CONFIG, "Can't parse/resolve %s %s", - c->key, c->value); - retval = -1; - continue; - } - port = tor_malloc_zero(sizeof(port_cfg_t)); - tor_addr_copy(&port->addr, &addr); - } - port->type = type; - port->port = portval ? portval : port_option; - smartlist_add(ports, port); - } - } - } - - if (retval == -1) - goto cleanup; - - retval = retry_listener_ports(old_conns, ports, new_conns); - - cleanup: - SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p)); - smartlist_free(ports); - return retval; -} - /** Launch listeners for each port you should have open. Only launch * listeners who are not already open, and only close listeners we no longer * want. @@ -1968,35 +1916,10 @@ retry_all_listeners(smartlist_t *replaced_conns, smartlist_add(listeners, conn); } SMARTLIST_FOREACH_END(conn); - if (! options->ClientOnly) { - if (retry_listeners(listeners, - CONN_TYPE_OR_LISTENER, options->ORListenAddress, - options->ORPort, "0.0.0.0", - new_conns, 0) < 0) - retval = -1; - if (retry_listeners(listeners, - CONN_TYPE_DIR_LISTENER, options->DirListenAddress, - options->DirPort, "0.0.0.0", - new_conns, 0) < 0) - retval = -1; - } - if (retry_listener_ports(listeners, - get_configured_client_ports(), + get_configured_ports(), new_conns) < 0) retval = -1; - if (retry_listeners(listeners, - CONN_TYPE_CONTROL_LISTENER, - options->ControlListenAddress, - options->ControlPort, "127.0.0.1", - new_conns, 0) < 0) - retval = -1; - if (retry_listeners(listeners, - CONN_TYPE_CONTROL_LISTENER, - options->ControlSocket, - options->ControlSocket ? 1 : 0, NULL, - new_conns, 1) < 0) - retval = -1; /* Any members that were still in 'listeners' don't correspond to * any configured port. Kill 'em. */ @@ -2013,6 +1936,7 @@ retry_all_listeners(smartlist_t *replaced_conns, smartlist_free(listeners); + /* XXXprop186 should take all advertised ports into account */ if (old_or_port != router_get_advertised_or_port(options) || old_dir_port != router_get_advertised_dir_port(options, 0)) { /* Our chosen ORPort or DirPort is not what it used to be: the @@ -2025,6 +1949,43 @@ retry_all_listeners(smartlist_t *replaced_conns, return retval; } +/** Mark every listener of type other than CONTROL_LISTENER to be closed. */ +void +connection_mark_all_noncontrol_listeners(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + if (conn->type == CONN_TYPE_CONTROL_LISTENER) + continue; + if (connection_is_listener(conn)) + connection_mark_for_close(conn); + } SMARTLIST_FOREACH_END(conn); +} + +/** Mark every external conection not used for controllers for close. */ +void +connection_mark_all_noncontrol_connections(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + switch (conn->type) { + case CONN_TYPE_CPUWORKER: + case CONN_TYPE_CONTROL_LISTENER: + case CONN_TYPE_CONTROL: + break; + case CONN_TYPE_AP: + connection_mark_unattached_ap(TO_ENTRY_CONN(conn), + END_STREAM_REASON_HIBERNATING); + break; + default: + connection_mark_for_close(conn); + break; + } + } SMARTLIST_FOREACH_END(conn); +} + /** Return 1 if we should apply rate limiting to <b>conn</b>, and 0 * otherwise. * Right now this just checks if it's an internal IP address or an diff --git a/src/or/connection.h b/src/or/connection.h index 9f1148972..c4b8bf8ab 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -66,6 +66,9 @@ int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, int retry_all_listeners(smartlist_t *replaced_conns, smartlist_t *new_conns); +void connection_mark_all_noncontrol_listeners(void); +void connection_mark_all_noncontrol_connections(void); + ssize_t connection_bucket_write_limit(connection_t *conn, time_t now); int global_write_bucket_low(connection_t *conn, size_t attempt, int priority); void connection_bucket_init(void); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index dc15721b6..aba9ba272 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -803,11 +803,18 @@ connection_ap_detach_retriable(entry_connection_t *conn, * the configuration file, "1" for mappings set from the control * interface, and other values for DNS and TrackHostExit mappings that can * expire.) + * + * A mapping may be 'wildcarded'. If "src_wildcard" is true, then + * any address that ends with a . followed by the key for this entry will + * get remapped by it. If "dst_wildcard" is also true, then only the + * matching suffix of such addresses will get replaced by new_address. */ typedef struct { char *new_address; time_t expires; addressmap_entry_source_t source:3; + unsigned src_wildcard:1; + unsigned dst_wildcard:1; short num_resolve_failures; } addressmap_entry_t; @@ -1054,6 +1061,37 @@ addressmap_free_all(void) virtaddress_reversemap = NULL; } +/** Try to find a match for AddressMap expressions that use + * wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or + * '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c). + * Return the matching entry in AddressMap or NULL if no match is found. + * For expressions such as '*.c.d *.e.f', truncate <b>address</b> 'a.c.d' + * to 'a' before we return the matching AddressMap entry. + * + * This function does not handle the case where a pattern of the form "*.c.d" + * matches the address c.d -- that's done by the main addressmap_rewrite + * function. + */ +static addressmap_entry_t * +addressmap_match_superdomains(char *address) +{ + addressmap_entry_t *val; + char *cp; + + cp = address; + while ((cp = strchr(cp, '.'))) { + /* cp now points to a suffix of address that begins with a . */ + val = strmap_get_lc(addressmap, cp+1); + if (val && val->src_wildcard) { + if (val->dst_wildcard) + *cp = '\0'; + return val; + } + ++cp; + } + return NULL; +} + /** Look at address, and rewrite it until it doesn't want any * more rewrites; but don't get into an infinite loop. * Don't write more than maxlen chars into address. Return true if the @@ -1066,25 +1104,49 @@ addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out) { addressmap_entry_t *ent; int rewrites; - char *cp; time_t expires = TIME_MAX; for (rewrites = 0; rewrites < 16; rewrites++) { + int exact_match = 0; + char *addr_orig = tor_strdup(escaped_safe_str_client(address)); + ent = strmap_get(addressmap, address); if (!ent || !ent->new_address) { + ent = addressmap_match_superdomains(address); + } else { + if (ent->src_wildcard && !ent->dst_wildcard && + !strcasecmp(address, ent->new_address)) { + /* This is a rule like *.example.com example.com, and we just got + * "example.com" */ + tor_free(addr_orig); + if (expires_out) + *expires_out = expires; + return rewrites > 0; + } + + exact_match = 1; + } + + if (!ent || !ent->new_address) { + tor_free(addr_orig); if (expires_out) *expires_out = expires; return (rewrites > 0); /* done, no rewrite needed */ } - cp = tor_strdup(escaped_safe_str_client(ent->new_address)); + if (ent->dst_wildcard && !exact_match) { + strlcat(address, ".", maxlen); + strlcat(address, ent->new_address, maxlen); + } else { + strlcpy(address, ent->new_address, maxlen); + } + log_info(LD_APP, "Addressmap: rewriting %s to %s", - escaped_safe_str_client(address), cp); + addr_orig, escaped_safe_str_client(address)); if (ent->expires > 1 && ent->expires < expires) expires = ent->expires; - tor_free(cp); - strlcpy(address, ent->new_address, maxlen); + tor_free(addr_orig); } log_warn(LD_CONFIG, "Loop detected: we've rewritten %s 16 times! Using it as-is.", @@ -1148,17 +1210,34 @@ addressmap_have_mapping(const char *address, int update_expiry) * <b>new_address</b> should be a newly dup'ed string, which we'll use or * free as appropriate. We will leave address alone. * - * If <b>new_address</b> is NULL, or equal to <b>address</b>, remove - * any mappings that exist from <b>address</b>. - */ + * If <b>wildcard_addr</b> is true, then the mapping will match any address + * equal to <b>address</b>, or any address ending with a period followed by + * <b>address</b>. If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are + * both true, the mapping will rewrite addresses that end with + * ".<b>address</b>" into ones that end with ".<b>new_address</b>." + * + * If <b>new_address</b> is NULL, or <b>new_address</b> is equal to + * <b>address</b> and <b>wildcard_addr</b> is equal to + * <b>wildcard_new_addr</b>, remove any mappings that exist from + * <b>address</b>. + * + * + * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is + * not set. */ void addressmap_register(const char *address, char *new_address, time_t expires, - addressmap_entry_source_t source) + addressmap_entry_source_t source, + const int wildcard_addr, + const int wildcard_new_addr) { addressmap_entry_t *ent; + if (wildcard_new_addr) + tor_assert(wildcard_addr); + ent = strmap_get(addressmap, address); - if (!new_address || !strcasecmp(address,new_address)) { + if (!new_address || (!strcasecmp(address,new_address) && + wildcard_addr == wildcard_new_addr)) { /* Remove the mapping, if any. */ tor_free(new_address); if (ent) { @@ -1193,6 +1272,8 @@ addressmap_register(const char *address, char *new_address, time_t expires, ent->expires = expires==2 ? 1 : expires; ent->num_resolve_failures = 0; ent->source = source; + ent->src_wildcard = wildcard_addr ? 1 : 0; + ent->dst_wildcard = wildcard_new_addr ? 1 : 0; log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'", safe_str_client(address), @@ -1277,7 +1358,7 @@ client_dns_set_addressmap_impl(const char *address, const char *name, "%s", name); } addressmap_register(extendedaddress, tor_strdup(extendedval), - time(NULL) + ttl, ADDRMAPSRC_DNS); + time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0); } /** Record the fact that <b>address</b> resolved to <b>val</b>. @@ -1529,7 +1610,7 @@ addressmap_register_virtual_address(int type, char *new_address) log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address); if (vent_needs_to_be_added) strmap_set(virtaddress_reversemap, new_address, vent); - addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP); + addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0); #if 0 { @@ -2603,7 +2684,7 @@ connection_ap_make_link(connection_t *partner, want_onehop ? "direct" : "anonymized", safe_str_client(address), port); - conn = entry_connection_new(CONN_TYPE_AP, AF_INET); + conn = entry_connection_new(CONN_TYPE_AP, tor_addr_family(&partner->addr)); base_conn = ENTRY_TO_CONN(conn); base_conn->linked = 1; /* so that we can add it safely below. */ @@ -3209,7 +3290,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn) exitconn->_base.state = EXIT_CONN_STATE_OPEN; - dirconn = dir_connection_new(AF_INET); + dirconn = dir_connection_new(tor_addr_family(&exitconn->_base.addr)); tor_addr_copy(&dirconn->_base.addr, &exitconn->_base.addr); dirconn->_base.port = 0; diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 830667e60..47c9c45b1 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -78,7 +78,8 @@ int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out); int addressmap_have_mapping(const char *address, int update_timeout); void addressmap_register(const char *address, char *new_address, - time_t expires, addressmap_entry_source_t source); + time_t expires, addressmap_entry_source_t source, + int address_wildcard, int new_address_wildcard); int parse_virtual_addr_network(const char *val, int validate_only, char **msg); int client_dns_incr_failures(const char *address); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index cce99e4d6..470f6d2a3 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -42,6 +42,7 @@ static int connection_or_check_valid_tls_handshake(or_connection_t *conn, char *digest_rcvd_out); static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn); +static void connection_or_close_connection_cb(void *_conn); #ifdef USE_BUFFEREVENTS static void connection_or_handle_event_cb(struct bufferevent *bufev, @@ -492,6 +493,9 @@ connection_or_about_to_close(or_connection_t *or_conn) time_t now = time(NULL); connection_t *conn = TO_CONN(or_conn); + if (or_conn->pending_action) + tor_cancel_libevent_action(or_conn->pending_action); + /* Remember why we're closing this connection. */ if (conn->state != OR_CONN_STATE_OPEN) { /* Inform any pending (not attached) circs that they should @@ -626,7 +630,9 @@ connection_or_update_token_buckets(smartlist_t *conns, /** If we don't necessarily know the router we're connecting to, but we * have an addr/port/id_digest, then fill in as much as we can. Start - * by checking to see if this describes a router we know. */ + * by checking to see if this describes a router we know. + * <b>started_here</b> is 1 if we are the initiator of <b>conn</b> and + * 0 if it's an incoming connection. */ void connection_or_init_conn_from_address(or_connection_t *conn, const tor_addr_t *addr, uint16_t port, @@ -641,10 +647,11 @@ connection_or_init_conn_from_address(or_connection_t *conn, tor_addr_copy(&conn->_base.addr, addr); tor_addr_copy(&conn->real_addr, addr); if (r) { - tor_addr_t node_addr; - node_get_addr(r, &node_addr); - /* XXXX proposal 118 will make this more complex. */ - if (tor_addr_eq(&conn->_base.addr, &node_addr)) + tor_addr_port_t node_ap; + node_get_pref_orport(r, &node_ap); + /* XXXX proposal 186 is making this more complex. For now, a conn + is canonical when it uses the _preferred_ address. */ + if (tor_addr_eq(&conn->_base.addr, &node_ap.addr)) conn->is_canonical = 1; if (!started_here) { /* Override the addr/port, so our log messages will make sense. @@ -657,12 +664,12 @@ connection_or_init_conn_from_address(or_connection_t *conn, * right IP address and port 56244, that wouldn't be as helpful. now we * log the "right" port too, so we know if it's moria1 or moria2. */ - tor_addr_copy(&conn->_base.addr, &node_addr); - conn->_base.port = node_get_orport(r); + tor_addr_copy(&conn->_base.addr, &node_ap.addr); + conn->_base.port = node_ap.port; } conn->nickname = tor_strdup(node_get_nickname(r)); tor_free(conn->_base.address); - conn->_base.address = tor_dup_addr(&node_addr); + conn->_base.address = tor_dup_addr(&node_ap.addr); } else { const char *n; /* If we're an authoritative directory server, we may know a @@ -1029,7 +1036,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port, return NULL; } - conn = or_connection_new(AF_INET); + conn = or_connection_new(tor_addr_family(&addr)); /* set up conn so it's got all the data we need to remember */ connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1); @@ -1096,12 +1103,16 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving) conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING; tor_assert(!conn->tls); conn->tls = tor_tls_new(conn->_base.s, receiving); - tor_tls_set_logged_address(conn->tls, // XXX client and relay? - escaped_safe_str(conn->_base.address)); if (!conn->tls) { log_warn(LD_BUG,"tor_tls_new failed. Closing."); return -1; } + tor_tls_set_logged_address(conn->tls, // XXX client and relay? + escaped_safe_str(conn->_base.address)); + tor_tls_set_renegotiate_callbacks(conn->tls, + connection_or_tls_renegotiated_cb, + connection_or_close_connection_cb, + conn); #ifdef USE_BUFFEREVENTS if (connection_type_uses_bufferevent(TO_CONN(conn))) { const int filtering = get_options()->_UseFilteringSSLBufferevents; @@ -1146,10 +1157,6 @@ connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn) or_connection_t *conn = _conn; (void)tls; - /* Don't invoke this again. */ - tor_tls_set_renegotiate_callback(tls, NULL, NULL); - tor_tls_block_renegotiation(tls); - if (connection_tls_finish_handshake(conn) < 0) { /* XXXX_TLS double-check that it's ok to do this from inside read. */ /* XXXX_TLS double-check that this verifies certificates. */ @@ -1157,6 +1164,34 @@ connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn) } } +/*DOCDOC*/ +static void +close_connection_libevent_cb(void *_conn) +{ + or_connection_t *or_conn = _conn; + connection_t *conn = TO_CONN(or_conn); + + or_conn->pending_action = NULL; + + connection_stop_reading(conn); + if (!conn->marked_for_close) + connection_mark_for_close(conn); +} + +/* DOCDOC */ +static void +connection_or_close_connection_cb(void *_conn) +{ + /* We can't close their connection from in here since it's an OpenSSL + callback, so we set a libevent event that triggers in the next event + loop and closes the connection. */ + or_connection_t *or_conn = _conn; + if (or_conn->_base.marked_for_close || or_conn->pending_action) + return; + or_conn->pending_action = + tor_run_in_libevent_loop(close_connection_libevent_cb, or_conn); +} + /** Move forward with the tls handshake. If it finishes, hand * <b>conn</b> to connection_tls_finish_handshake(). * @@ -1203,9 +1238,6 @@ connection_tls_continue_handshake(or_connection_t *conn) /* v2/v3 handshake, but not a client. */ log_debug(LD_OR, "Done with initial SSL handshake (server-side). " "Expecting renegotiation or VERSIONS cell"); - tor_tls_set_renegotiate_callback(conn->tls, - connection_or_tls_renegotiated_cb, - conn); conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING; connection_stop_writing(TO_CONN(conn)); connection_start_reading(TO_CONN(conn)); @@ -1266,9 +1298,6 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, } else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) { /* v2 or v3 handshake, as a server. Only got one handshake, so * wait for the next one. */ - tor_tls_set_renegotiate_callback(conn->tls, - connection_or_tls_renegotiated_cb, - conn); conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING; /* return 0; */ return; /* ???? */ @@ -1536,7 +1565,6 @@ connection_tls_finish_handshake(or_connection_t *conn) connection_or_init_conn_from_address(conn, &conn->_base.addr, conn->_base.port, digest_rcvd, 0); } - tor_tls_block_renegotiation(conn->tls); return connection_or_set_state_open(conn); } else { conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V2; diff --git a/src/or/control.c b/src/or/control.c index 109eb8857..20caabf0c 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -737,7 +737,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body, SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp)); smartlist_free(entries); - if (config_get_lines(config, &lines) < 0) { + if (config_get_lines(config, &lines, 0) < 0) { log_warn(LD_CONTROL,"Controller gave us config lines we can't parse."); connection_write_str_to_buf("551 Couldn't parse configuration\r\n", conn); @@ -883,7 +883,7 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len, const char *msg = NULL; (void) len; - retval = options_init_from_string(body, CMD_RUN_TOR, NULL, &errstring); + retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring); if (retval != SETOPT_OK) log_warn(LD_CONTROL, @@ -1331,7 +1331,8 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len, smartlist_add(reply, ans); } } else { - addressmap_register(from, tor_strdup(to), 1, ADDRMAPSRC_CONTROLLER); + addressmap_register(from, tor_strdup(to), 1, + ADDRMAPSRC_CONTROLLER, 0, 0); tor_snprintf(ans, anslen, "250-%s", line); smartlist_add(reply, ans); } @@ -1378,7 +1379,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, if (!strcmp(question, "version")) { *answer = tor_strdup(get_version()); } else if (!strcmp(question, "config-file")) { - *answer = tor_strdup(get_torrc_fname()); + *answer = tor_strdup(get_torrc_fname(0)); + } else if (!strcmp(question, "config-defaults-file")) { + *answer = tor_strdup(get_torrc_fname(1)); } else if (!strcmp(question, "config-text")) { *answer = options_dump(get_options(), 1); } else if (!strcmp(question, "info/names")) { @@ -2024,6 +2027,7 @@ typedef struct getinfo_item_t { static const getinfo_item_t getinfo_items[] = { ITEM("version", misc, "The current version of Tor."), ITEM("config-file", misc, "Current location of the \"torrc\" file."), + ITEM("config-defaults-file", misc, "Current location of the defaults file."), ITEM("config-text", misc, "Return the string that would be written by a saveconf command."), ITEM("accounting/bytes", accounting, @@ -2399,7 +2403,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len, /* now circ refers to something that is ready to be extended */ SMARTLIST_FOREACH(nodes, const node_t *, node, { - extend_info_t *info = extend_info_from_node(node); + extend_info_t *info = extend_info_from_node(node, 0); tor_assert(info); /* True, since node_has_descriptor(node) == true */ circuit_append_new_exit(circ, info); extend_info_free(info); diff --git a/src/or/directory.c b/src/or/directory.c index 073b88717..d4abe0243 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -923,7 +923,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, return; } - conn = dir_connection_new(AF_INET); + conn = dir_connection_new(tor_addr_family(&addr)); /* set up conn so it's got all the data we need to remember */ tor_addr_copy(&conn->_base.addr, &addr); @@ -1623,9 +1623,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (!reason) reason = tor_strdup("[no reason given]"); log_debug(LD_DIR, - "Received response from directory server '%s:%d': %d %s", + "Received response from directory server '%s:%d': %d %s " + "(purpose: %d)", conn->_base.address, conn->_base.port, status_code, - escaped(reason)); + escaped(reason), + conn->_base.purpose); /* now check if it's got any hints for us about our IP address. */ if (conn->dirconn_direct) { @@ -3733,7 +3735,7 @@ download_status_reset(download_status_t *dls) const int *schedule; size_t schedule_len; - find_dl_schedule_and_len(dls, get_options()->DirPort, + find_dl_schedule_and_len(dls, get_options()->DirPort != NULL, &schedule, &schedule_len); dls->n_download_failures = 0; diff --git a/src/or/directory.h b/src/or/directory.h index 8c63bb5df..5050f700d 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -80,7 +80,7 @@ time_t download_status_increment_failure(download_status_t *dls, * the optional status code <b>sc</b>. */ #define download_status_failed(dls, sc) \ download_status_increment_failure((dls), (sc), NULL, \ - get_options()->DirPort, time(NULL)) + get_options()->DirPort!=NULL, time(NULL)) void download_status_reset(download_status_t *dls); static int download_status_is_ready(download_status_t *dls, time_t now, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index be62459b1..f4bbca850 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -232,7 +232,7 @@ dirserv_load_fingerprint_file(void) } tor_free(fname); - result = config_get_lines(cf, &front); + result = config_get_lines(cf, &front, 0); tor_free(cf); if (result < 0) { log_warn(LD_CONFIG, "Error reading from fingerprint file"); @@ -1212,7 +1212,7 @@ directory_fetches_from_authorities(const or_options_t *options) return 1; /* we don't know our IP address; ask an authority. */ refuseunknown = ! router_my_exit_policy_is_reject_star() && should_refuse_unknown_exits(options); - if (options->DirPort == 0 && !refuseunknown) + if (options->DirPort == NULL && !refuseunknown) return 0; if (!server_mode(options) || !advertised_server_mode()) return 0; @@ -1248,7 +1248,7 @@ directory_fetches_dir_info_later(const or_options_t *options) int directory_caches_v2_dir_info(const or_options_t *options) { - return options->DirPort != 0; + return options->DirPort != NULL; } /** Return 1 if we want to keep descriptors, networkstatuses, etc around @@ -1273,7 +1273,7 @@ directory_caches_dir_info(const or_options_t *options) int directory_permits_begindir_requests(const or_options_t *options) { - return options->BridgeRelay != 0 || options->DirPort != 0; + return options->BridgeRelay != 0 || options->DirPort != NULL; } /** Return 1 if we want to allow controllers to ask us directory @@ -1282,7 +1282,7 @@ directory_permits_begindir_requests(const or_options_t *options) int directory_permits_controller_requests(const or_options_t *options) { - return options->DirPort != 0; + return options->DirPort != NULL; } /** Return 1 if we have no need to fetch new descriptors. This generally diff --git a/src/or/dirvote.c b/src/or/dirvote.c index bf34c62af..01e2358c4 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -50,7 +50,7 @@ static int dirvote_publish_consensus(void); static char *make_consensus_method_list(int low, int high, const char *sep); /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 11 +#define MAX_SUPPORTED_CONSENSUS_METHOD 12 /** Lowest consensus method that contains a 'directory-footer' marker */ #define MIN_METHOD_FOR_FOOTER 9 @@ -64,6 +64,10 @@ static char *make_consensus_method_list(int low, int high, const char *sep); /** Lowest consensus method that generates microdescriptors */ #define MIN_METHOD_FOR_MICRODESC 8 +/** Lowest consensus method that ensures a majority of authorities voted + * for a param. */ +#define MIN_METHOD_FOR_MAJORITY_PARAMS 12 + /* ===== * Voting * =====*/ @@ -608,11 +612,16 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning) return result; } +/** Minimum number of directory authorities voting for a parameter to + * include it in the consensus, if consensus method 12 or later is to be + * used. See proposal 178 for details. */ +#define MIN_VOTES_FOR_PARAM 3 + /** Helper: given a list of valid networkstatus_t, return a new string * containing the contents of the consensus network parameter set. */ /* private */ char * -dirvote_compute_params(smartlist_t *votes) +dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) { int i; int32_t *vals; @@ -669,11 +678,17 @@ dirvote_compute_params(smartlist_t *votes) next_param = smartlist_get(param_list, param_sl_idx+1); if (!next_param || strncmp(next_param, param, cur_param_len)) { /* We've reached the end of a series. */ - int32_t median = median_int32(vals, i); - char *out_string = tor_malloc(64+cur_param_len); - memcpy(out_string, param, cur_param_len); - tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); - smartlist_add(output, out_string); + /* Make sure enough authorities voted on this param, unless the + * the consensus method we use is too old for that. */ + if (method < MIN_METHOD_FOR_MAJORITY_PARAMS || + i > total_authorities/2 || + i >= MIN_VOTES_FOR_PARAM) { + int32_t median = median_int32(vals, i); + char *out_string = tor_malloc(64+cur_param_len); + memcpy(out_string, param, cur_param_len); + tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); + smartlist_add(output, out_string); + } i = 0; if (next_param) { @@ -1496,7 +1511,8 @@ networkstatus_compute_consensus(smartlist_t *votes, } if (consensus_method >= MIN_METHOD_FOR_PARAMS) { - params = dirvote_compute_params(votes); + params = dirvote_compute_params(votes, consensus_method, + total_authorities); if (params) { smartlist_add(chunks, tor_strdup("params ")); smartlist_add(chunks, params); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index d19635173..1f4dc362b 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -84,7 +84,8 @@ document_signature_t *voter_get_sig_by_algorithm( #ifdef DIRVOTE_PRIVATE char *format_networkstatus_vote(crypto_pk_env_t *private_key, networkstatus_t *v3_ns); -char *dirvote_compute_params(smartlist_t *votes); +char *dirvote_compute_params(smartlist_t *votes, int method, + int total_authorities); #endif #endif diff --git a/src/or/dns.c b/src/or/dns.c index 8ed953690..beb110acb 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -1395,6 +1395,10 @@ launch_resolve(edge_connection_t *exitconn) int r; int options = get_options()->ServerDNSSearchDomains ? 0 : DNS_QUERY_NO_SEARCH; + + if (get_options()->DisableNetwork) + return -1; + /* What? Nameservers not configured? Sounds like a bug. */ if (!nameservers_configured) { log_warn(LD_EXIT, "(Harmless.) Nameservers not configured, but resolve " @@ -1601,6 +1605,9 @@ launch_test_addresses(int fd, short event, void *args) (void)event; (void)args; + if (options->DisableNetwork) + return; + log_info(LD_EXIT, "Launching checks to see whether our nameservers like to " "hijack *everything*."); /* This situation is worse than the failure-hijacking situation. When this diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 6fd2b4f19..ce64581d1 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -735,7 +735,6 @@ hibernate_soft_limit_reached(void) static void hibernate_begin(hibernate_state_t new_state, time_t now) { - connection_t *conn; const or_options_t *options = get_options(); if (new_state == HIBERNATE_STATE_EXITING && @@ -756,15 +755,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now) } /* close listeners. leave control listener(s). */ - while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) { - log_info(LD_NET,"Closing listener type %d", conn->type); - connection_mark_for_close(conn); - } + connection_mark_all_noncontrol_listeners(); /* XXX kill intro point circs */ /* XXX upload rendezvous service descriptors with no intro points */ diff --git a/src/or/main.c b/src/or/main.c index 266356a96..d3eb721d8 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -196,6 +196,26 @@ free_old_inbuf(connection_t *conn) } #endif +#ifdef MS_WINDOWS +/** Remove the kernel-space send and receive buffers for <b>s</b>. For use + * with IOCP only. */ +static int +set_buffer_lengths_to_zero(tor_socket_t s) +{ + int zero = 0; + int r = 0; + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) { + log_warn(LD_NET, "Unable to clear SO_SNDBUF"); + r = -1; + } + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) { + log_warn(LD_NET, "Unable to clear SO_RCVBUF"); + r = -1; + } + return r; +} +#endif + /** Add <b>conn</b> to the array of connections that we can poll on. The * connection's socket must be set; the connection starts out * non-reading and non-writing. @@ -216,6 +236,14 @@ connection_add_impl(connection_t *conn, int is_connecting) #ifdef USE_BUFFEREVENTS if (connection_type_uses_bufferevent(conn)) { if (SOCKET_OK(conn->s) && !conn->linked) { + +#ifdef MS_WINDOWS + if (tor_libevent_using_iocp_bufferevents() && + get_options()->UserspaceIOCPBuffers) { + set_buffer_lengths_to_zero(conn->s); + } +#endif + conn->bufev = bufferevent_socket_new( tor_libevent_get_base(), conn->s, @@ -906,7 +934,7 @@ directory_info_has_arrived(time_t now, int from_cache) update_extrainfo_downloads(now); } - if (server_mode(options) && !we_are_hibernating() && !from_cache && + if (server_mode(options) && !net_is_disabled() && !from_cache && (can_complete_circuit || !any_predicted_circuits(now))) consider_testing_reachability(1, 1); } @@ -1133,11 +1161,11 @@ run_scheduled_events(time_t now) if (router_rebuild_descriptor(1)<0) { log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); } - if (advertised_server_mode()) + if (advertised_server_mode() & !options->DisableNetwork) router_upload_dir_desc_to_dirservers(0); } - if (time_to_try_getting_descriptors < now) { + if (!options->DisableNetwork && time_to_try_getting_descriptors < now) { update_all_descriptor_downloads(now); update_extrainfo_downloads(now); if (router_have_minimum_dir_info()) @@ -1161,10 +1189,7 @@ run_scheduled_events(time_t now) last_rotated_x509_certificate = now; if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) { log_info(LD_GENERAL,"Rotating tls context."); - if (tor_tls_context_init(public_server_mode(options), - get_tlsclient_identity_key(), - is_server ? get_server_identity_key() : NULL, - MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) { + if (router_initialize_tls_context() < 0) { log_warn(LD_BUG, "Error reinitializing TLS context"); /* XXX is it a bug here, that we just keep going? -RD */ } @@ -1191,7 +1216,7 @@ run_scheduled_events(time_t now) if (time_to_launch_reachability_tests < now && (authdir_mode_tests_reachability(options)) && - !we_are_hibernating()) { + !net_is_disabled()) { time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; /* try to determine reachability of the other Tor relays */ dirserv_test_reachability(now); @@ -1327,7 +1352,7 @@ run_scheduled_events(time_t now) /* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (time_to_check_descriptor < now) { + if (time_to_check_descriptor < now && !options->DisableNetwork) { static int dirport_reachability_count = 0; time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; check_descriptor_bandwidth_changed(now); @@ -1402,7 +1427,7 @@ run_scheduled_events(time_t now) connection_expire_held_open(); /** 3d. And every 60 seconds, we relaunch listeners if any died. */ - if (!we_are_hibernating() && time_to_check_listeners < now) { + if (!net_is_disabled() && time_to_check_listeners < now) { retry_all_listeners(NULL, NULL); time_to_check_listeners = now+60; } @@ -1413,7 +1438,7 @@ run_scheduled_events(time_t now) * and we make a new circ if there are no clean circuits. */ have_dir_info = router_have_minimum_dir_info(); - if (have_dir_info && !we_are_hibernating()) + if (have_dir_info && !net_is_disabled()) circuit_build_needed_circs(now); /* every 10 seconds, but not at the same second as other such events */ @@ -1444,7 +1469,7 @@ run_scheduled_events(time_t now) circuit_close_all_marked(); /** 7. And upload service descriptors if necessary. */ - if (can_complete_circuit && !we_are_hibernating()) { + if (can_complete_circuit && !net_is_disabled()) { rend_consider_services_upload(now); rend_consider_descriptor_republication(); } @@ -1461,7 +1486,8 @@ run_scheduled_events(time_t now) /** 9. and if we're a server, check whether our DNS is telling stories to * us. */ - if (public_server_mode(options) && time_to_check_for_correct_dns < now) { + if (!net_is_disabled() && + public_server_mode(options) && time_to_check_for_correct_dns < now) { if (!time_to_check_for_correct_dns) { time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); } else { @@ -1480,19 +1506,21 @@ run_scheduled_events(time_t now) } /** 11. check the port forwarding app */ - if (time_to_check_port_forwarding < now && + if (!net_is_disabled() && + time_to_check_port_forwarding < now && options->PortForwarding && is_server) { #define PORT_FORWARDING_CHECK_INTERVAL 5 + /* XXXXX this should take a list of ports, not just two! */ tor_check_port_forwarding(options->PortForwardingHelper, - options->DirPort, - options->ORPort, + get_primary_dir_port(), + get_primary_or_port(), now); time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; } /** 11b. check pending unconfigured managed proxies */ - if (pt_proxies_configuration_pending()) + if (!net_is_disabled() && pt_proxies_configuration_pending()) pt_configure_remaining_proxies(); /** 11c. validate pluggable transports configuration if we need to */ @@ -1564,7 +1592,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) control_event_stream_bandwidth_used(); if (server_mode(options) && - !we_are_hibernating() && + !net_is_disabled() && seconds_elapsed > 0 && can_complete_circuit && stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT != @@ -1765,7 +1793,8 @@ do_hup(void) /* retry appropriate downloads */ router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - update_networkstatus_downloads(time(NULL)); + if (!options->DisableNetwork) + update_networkstatus_downloads(time(NULL)); /* We'll retry routerstatus downloads in about 10 seconds; no need to * force a retry there. */ diff --git a/src/or/nodelist.c b/src/or/nodelist.c index b93b919c1..eafc9b8b7 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -646,24 +646,70 @@ node_exit_policy_rejects_all(const node_t *node) return 1; } -/** Copy the address for <b>node</b> into *<b>addr_out</b>. */ -int -node_get_addr(const node_t *node, tor_addr_t *addr_out) +/** Return list of tor_addr_port_t with all OR ports (in the sense IP + * addr + TCP port) for <b>node</b>. Caller must free all elements + * using tor_free() and free the list using smartlist_free(). + * + * XXX this is potentially a memory fragmentation hog -- if on + * critical path consider the option of having the caller allocate the + * memory + */ +smartlist_t * +node_get_all_orports(const node_t *node) +{ + smartlist_t *sl = smartlist_create(); + + if (node->ri != NULL) { + if (node->ri->addr != 0) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_from_ipv4h(&ap->addr, node->ri->addr); + ap->port = node->ri->or_port; + smartlist_add(sl, ap); + } + if (!tor_addr_is_null(&node->ri->ipv6_addr)) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_copy(&ap->addr, &node->ri->ipv6_addr); + ap->port = node->ri->or_port; + smartlist_add(sl, ap); + } + } else if (node->rs != NULL) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_from_ipv4h(&ap->addr, node->rs->addr); + ap->port = node->rs->or_port; + smartlist_add(sl, ap); + } + + return sl; +} + +/** Copy the primary (IPv4) OR port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - tor_addr_from_ipv4h(addr_out, node->ri->addr); - return 0; - } else if (node->rs) { - tor_addr_from_ipv4h(addr_out, node->rs->addr); - return 0; + router_get_prim_orport(node->ri, ap_out); } - return -1; + else if (node->rs) { + tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); + ap_out->port = node->rs->or_port; + } +} + +/** Wrapper around node_get_prim_orport for backward + compatibility. */ +void +node_get_addr(const node_t *node, tor_addr_t *addr_out) +{ + tor_addr_port_t ap; + node_get_prim_orport(node, &ap); + tor_addr_copy(addr_out, &ap.addr); } /** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't * seem to have one. */ uint32_t -node_get_addr_ipv4h(const node_t *node) +node_get_prim_addr_ipv4h(const node_t *node) { if (node->ri) { return node->ri->addr; @@ -673,9 +719,38 @@ node_get_addr_ipv4h(const node_t *node) return 0; } -/** Copy a string representation of the IP address for <b>node</b> into the - * <b>len</b>-byte buffer at <b>buf</b>. - */ +/** Copy the preferred OR port (IP address and TCP port) for + * <b>node</b> into <b>ap_out</b>. */ +void +node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) +{ + if (node->ri) { + router_get_pref_orport(node->ri, ap_out); + } else if (node->rs) { + /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private + bridges but needs fixing */ + tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); + ap_out->port = node->rs->or_port; + } +} + +/** Copy the preferred IPv6 OR port (address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) +{ + if (node->ri) { + router_get_pref_ipv6_orport(node->ri, ap_out); + } else if (node->rs) { + /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private + bridges but needs fixing */ + tor_addr_make_unspec(&ap_out->addr); + ap_out->port = 0; + } +} + +/** Copy a string representation of an IP address for <b>node</b> into + * the <b>len</b>-byte buffer at <b>buf</b>. */ void node_get_address_string(const node_t *node, char *buf, size_t len) { @@ -701,18 +776,6 @@ node_get_declared_uptime(const node_t *node) return -1; } -/** Return <b>node</b>'s declared or_port */ -uint16_t -node_get_orport(const node_t *node) -{ - if (node->ri) - return node->ri->or_port; - else if (node->rs) - return node->rs->or_port; - else - return 0; -} - /** Return <b>node</b>'s platform string, or NULL if we don't know it. */ const char * node_get_platform(const node_t *node) diff --git a/src/or/nodelist.h b/src/or/nodelist.h index bd2e63953..1b7549dad 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -37,10 +37,13 @@ int node_get_purpose(const node_t *node); (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE) int node_is_me(const node_t *node); int node_exit_policy_rejects_all(const node_t *node); -int node_get_addr(const node_t *node, tor_addr_t *addr_out); -uint32_t node_get_addr_ipv4h(const node_t *node); +smartlist_t *node_get_all_orports(const node_t *node); +void node_get_prim_orport(const node_t *node, tor_addr_port_t *addr_port_out); +void node_get_pref_orport(const node_t *node, tor_addr_port_t *addr_port_out); +void node_get_pref_ipv6_orport(const node_t *node, + tor_addr_port_t *addr_port_out); +uint32_t node_get_prim_addr_ipv4h(const node_t *node); int node_allows_single_hop_exits(const node_t *node); -uint16_t node_get_orport(const node_t *node); const char *node_get_nickname(const node_t *node); const char *node_get_platform(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); @@ -50,6 +53,10 @@ const smartlist_t *node_get_declared_family(const node_t *node); smartlist_t *nodelist_get_list(void); +/* Temporary during transition to multiple addresses. */ +void node_get_addr(const node_t *node, tor_addr_t *addr_out); +#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n)) + /* XXXX These need to move out of routerlist.c */ void nodelist_refresh_countries(void); void node_set_country(node_t *node); diff --git a/src/or/or.h b/src/or/or.h index 8117ee1f8..eb9f060e5 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -793,10 +793,10 @@ typedef struct rend_data_t { char rend_cookie[REND_COOKIE_LEN]; } rend_data_t; -/** Time interval for tracking possible replays of INTRODUCE2 cells. - * Incoming cells with timestamps half of this interval in the past or - * future are dropped immediately. */ -#define REND_REPLAY_TIME_INTERVAL (60 * 60) +/** Time interval for tracking replays of DH public keys received in + * INTRODUCE2 cells. Used only to avoid launching multiple + * simultaneous attempts to connect to the same rendezvous point. */ +#define REND_REPLAY_TIME_INTERVAL (5 * 60) /** Used to indicate which way a cell is going on a circuit. */ typedef enum { @@ -1276,6 +1276,8 @@ typedef struct or_connection_t { unsigned active_circuit_pqueue_last_recalibrated; struct or_connection_t *next_with_same_id; /**< Next connection with same * identity digest as this one. */ + + tor_libevent_action_t *pending_action; } or_connection_t; /** Subtype of connection_t for an "edge connection" -- that is, an entry (ap) @@ -1724,6 +1726,13 @@ typedef struct { uint16_t or_port; /**< Port for TLS connections. */ uint16_t dir_port; /**< Port for HTTP directory connections. */ + /* DOCDOC */ + /* XXXXX187 Actually these should probably be part of a list of addresses, + * not just a special case. Use abstractions to access these; don't do it + * directly. */ + tor_addr_t ipv6_addr; + uint16_t ipv6_orport; + crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */ crypto_pk_env_t *identity_pkey; /**< Public RSA key for signing. */ @@ -1755,6 +1764,8 @@ typedef struct { /** True if, after we have added this router, we should re-launch * tests for it. */ unsigned int needs_retest_if_added:1; + /** True if ipv6_addr:ipv6_orport is preferred. */ + unsigned int ipv6_preferred:1; /** Tor can use this router for general positions in circuits; we got it * from a directory server as usual, or we're an authority and a server @@ -2835,16 +2846,37 @@ typedef struct port_cfg_t { int session_group; /**< A session group, or -1 if this port is not in a * session group. */ + /* Server port types (or, dir) only: */ + unsigned int no_advertise : 1; + unsigned int no_listen : 1; + unsigned int all_addrs : 1; + unsigned int ipv4_only : 1; + unsigned int ipv6_only : 1; + /* Unix sockets only: */ /** Path for an AF_UNIX address */ char unix_addr[FLEXIBLE_ARRAY_MEMBER]; } port_cfg_t; +/** Ordinary configuration line. */ +#define CONFIG_LINE_NORMAL 0 +/** Appends to previous configuration for the same option, even if we + * would ordinary replace it. */ +#define CONFIG_LINE_APPEND 1 +/* Removes all previous configuration for an option. */ +#define CONFIG_LINE_CLEAR 2 + /** A linked list of lines in a config file. */ typedef struct config_line_t { char *key; char *value; struct config_line_t *next; + /** What special treatment (if any) does this line require? */ + unsigned int command:2; + /** If true, subsequent assignments to this linelist should replace + * it, not extend it. Set only on the first item in a linelist in an + * or_options_t. */ + unsigned int fragile:1; } config_line_t; typedef struct routerset_t routerset_t; @@ -2877,6 +2909,8 @@ typedef struct { char *Address; /**< OR only: configured address for this onion router. */ char *PidFile; /**< Where to store PID of Tor process. */ + int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/ + routerset_t *ExitNodes; /**< Structure containing nicknames, digests, * country codes and IP address patterns of ORs to * consider as exits. */ @@ -2934,17 +2968,18 @@ typedef struct { int DirAllowPrivateAddresses; char *User; /**< Name of user to run Tor as. */ char *Group; /**< Name of group to run Tor as. */ - int ORPort; /**< Port to listen on for OR connections. */ + config_line_t *ORPort; /**< Ports to listen on for OR connections. */ config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */ /** Ports to listen on for transparent pf/netfilter connections. */ config_line_t *TransPort; config_line_t *NATDPort; /**< Ports to listen on for transparent natd * connections. */ - int ControlPort; /**< Port to listen on for control connections. */ + config_line_t *ControlPort; /**< Port to listen on for control + * connections. */ config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on * for control connections. */ int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ - int DirPort; /**< Port to listen on for directory connections. */ + config_line_t *DirPort; /**< Port to listen on for directory connections. */ config_line_t *DNSPort; /**< Port to listen on for DNS requests. */ int AssumeReachable; /**< Whether to publish our descriptor regardless. */ int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ @@ -3257,6 +3292,8 @@ typedef struct { disclaimer. This allows a server administrator to show that they're running Tor and anyone visiting their server will know this without any specialized knowledge. */ + int DisableDebuggerAttachment; /**< Currently Linux only specific attempt to + disable ptrace; needs BSD testing. */ /** Boolean: if set, we start even if our resolv.conf file is missing * or broken. */ int ServerDNSAllowBrokenConfig; @@ -3441,6 +3478,15 @@ typedef struct { * never use it. If -1, we do what the consensus says. */ int OptimisticData; + /** If 1, and we are using IOCP, we set the kernel socket SNDBUF and RCVBUF + * to 0 to try to save kernel memory and avoid the dread "Out of buffers" + * issue. */ + int UserspaceIOCPBuffers; + + /** If 1, we accept and launch no external network connections, except on + * control ports. */ + int DisableNetwork; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ @@ -4026,6 +4072,26 @@ typedef struct rend_encoded_v2_service_descriptor_t { * introduction point. See also rend_intro_point_t.unreachable_count. */ #define MAX_INTRO_POINT_REACHABILITY_FAILURES 5 +/** The maximum number of distinct INTRODUCE2 cells which a hidden + * service's introduction point will receive before it begins to + * expire. + * + * XXX023 Is this number at all sane? */ +#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384 + +/** The minimum number of seconds that an introduction point will last + * before expiring due to old age. (If it receives + * INTRO_POINT_LIFETIME_INTRODUCTIONS INTRODUCE2 cells, it may expire + * sooner.) + * + * XXX023 Should this be configurable? */ +#define INTRO_POINT_LIFETIME_MIN_SECONDS 18*60*60 +/** The maximum number of seconds that an introduction point will last + * before expiring due to old age. + * + * XXX023 Should this be configurable? */ +#define INTRO_POINT_LIFETIME_MAX_SECONDS 24*60*60 + /** Introduction point information. Used both in rend_service_t (on * the service side) and in rend_service_descriptor_t (on both the * client and service side). */ @@ -4045,6 +4111,37 @@ typedef struct rend_intro_point_t { * circuit to this intro point for some reason other than our * circuit-build timeout. See also MAX_INTRO_POINT_REACHABILITY_FAILURES. */ unsigned int unreachable_count : 3; + + /** (Service side only) Flag indicating that this intro point was + * included in the last HS descriptor we generated. */ + unsigned int listed_in_last_desc : 1; + + /** (Service side only) A digestmap recording the INTRODUCE2 cells + * this intro point's circuit has received. Each key is the digest + * of the RSA-encrypted part of a received INTRODUCE2 cell; each + * value is a pointer to the time_t at which the cell was received. + * This digestmap is used to prevent replay attacks. */ + digestmap_t *accepted_intro_rsa_parts; + + /** (Service side only) The time at which this intro point was first + * published, or -1 if this intro point has not yet been + * published. */ + time_t time_published; + + /** (Service side only) The time at which this intro point should + * (start to) expire, or -1 if we haven't decided when this intro + * point should expire. */ + time_t time_to_expire; + + /** (Service side only) The time at which we decided that this intro + * point should start expiring, or -1 if this intro point is not yet + * expiring. + * + * This field also serves as a flag to indicate that we have decided + * to expire this intro point, in case intro_point_should_expire_now + * flaps (perhaps due to a clock jump; perhaps due to other + * weirdness, or even a (present or future) bug). */ + time_t time_expiring; } rend_intro_point_t; /** Information used to connect to a hidden service. Used on both the diff --git a/src/or/relay.c b/src/or/relay.c index ac3114bda..6cf4b73a5 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -924,6 +924,7 @@ connection_edge_process_relay_cell_not_open( } circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ)); /* don't send a socks reply to transparent conns */ + tor_assert(entry_conn->socks_request != NULL); if (!entry_conn->socks_request->has_finished) connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0); diff --git a/src/or/rendclient.c b/src/or/rendclient.c index e7ef313ab..5429b6c7e 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1067,7 +1067,7 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, smartlist_del(usable_nodes, i); goto again; } - new_extend_info = extend_info_from_node(node); + new_extend_info = extend_info_from_node(node, 0); if (!new_extend_info) { log_info(LD_REND, "We don't have a descriptor for the intro-point relay " "'%s'; trying another.", diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 94bb00221..d09e6566c 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -440,6 +440,11 @@ rend_intro_point_free(rend_intro_point_t *intro) extend_info_free(intro->extend_info); crypto_free_pk_env(intro->intro_key); + + if (intro->accepted_intro_rsa_parts != NULL) { + digestmap_free(intro->accepted_intro_rsa_parts, _tor_free); + } + tor_free(intro); } diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 34d255ac1..a360d5ce5 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -26,6 +26,10 @@ static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest); +static rend_intro_point_t *find_intro_point(origin_circuit_t *circ); + +static int intro_point_should_expire_now(rend_intro_point_t *intro, + time_t now); /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. @@ -36,8 +40,10 @@ typedef struct rend_service_port_config_t { tor_addr_t real_addr; } rend_service_port_config_t; -/** Try to maintain this many intro points per service if possible. */ -#define NUM_INTRO_POINTS 3 +/** Try to maintain this many intro points per service by default. */ +#define NUM_INTRO_POINTS_DEFAULT 3 +/** Maintain no more than this many intro points per hidden service. */ +#define NUM_INTRO_POINTS_MAX 10 /** If we can't build our intro circuits, don't retry for this long. */ #define INTRO_CIRC_RETRY_PERIOD (60*5) @@ -51,6 +57,10 @@ typedef struct rend_service_port_config_t { * rendezvous point before giving up? */ #define MAX_REND_TIMEOUT 30 +/** How many seconds should we wait for new HS descriptors to reach + * our clients before we close an expiring intro point? */ +#define INTRO_POINT_EXPIRATION_GRACE_PERIOD 5*60 + /** Represents a single hidden service running at this OP. */ typedef struct rend_service_t { /* Fields specified in config file */ @@ -72,17 +82,24 @@ typedef struct rend_service_t { * introduction points. */ int n_intro_circuits_launched; /**< Count of intro circuits we have * established in this period. */ + unsigned int n_intro_points_wanted; /**< Number of intro points this + * service wants to have open. */ rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */ time_t desc_is_dirty; /**< Time at which changes to the hidden service * descriptor content occurred, or 0 if it's * up-to-date. */ time_t next_upload_time; /**< Scheduled next hidden service descriptor * upload time. */ - /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t of when - * they were received; used to prevent replays. */ - digestmap_t *accepted_intros; - /** Time at which we last removed expired values from accepted_intros. */ - time_t last_cleaned_accepted_intros; + /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t + * of when they were received. Clients may send INTRODUCE1 cells + * for the same rendezvous point through two or more different + * introduction points; when they do, this digestmap keeps us from + * launching multiple simultaneous attempts to connect to the same + * rend point. */ + digestmap_t *accepted_intro_dh_parts; + /** Time at which we last removed expired values from + * accepted_intro_dh_parts. */ + time_t last_cleaned_accepted_intro_dh_parts; } rend_service_t; /** A list of rend_service_t's for services run on this OP. @@ -142,7 +159,7 @@ rend_service_free(rend_service_t *service) rend_authorized_client_free(c);); smartlist_free(service->clients); } - digestmap_free(service->accepted_intros, _tor_free); + digestmap_free(service->accepted_intro_dh_parts, _tor_free); tor_free(service); } @@ -319,6 +336,7 @@ rend_config_services(const or_options_t *options, int validate_only) service->directory = tor_strdup(line->value); service->ports = smartlist_create(); service->intro_period_started = time(NULL); + service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; continue; } if (!service) { @@ -542,16 +560,38 @@ rend_service_update_descriptor(rend_service_t *service) for (i = 0; i < smartlist_len(service->intro_nodes); ++i) { rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i); rend_intro_point_t *intro_desc; + + /* This intro point won't be listed in the descriptor... */ + intro_svc->listed_in_last_desc = 0; + + if (intro_svc->time_expiring != -1) { + /* This intro point is expiring. Don't list it. */ + continue; + } + circ = find_intro_circuit(intro_svc, service->pk_digest); - if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) + if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) { + /* This intro point's circuit isn't finished yet. Don't list it. */ continue; + } + + /* ...unless this intro point is listed in the descriptor. */ + intro_svc->listed_in_last_desc = 1; - /* We have an entirely established intro circuit. */ + /* We have an entirely established intro circuit. Publish it in + * our descriptor. */ intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t)); intro_desc->extend_info = extend_info_dup(intro_svc->extend_info); if (intro_svc->intro_key) intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key); smartlist_add(d->intro_nodes, intro_desc); + + if (intro_svc->time_published == -1) { + /* We are publishing this intro point in a descriptor for the + * first time -- note the current time in the service's copy of + * the intro point. */ + intro_svc->time_published = time(NULL); + } } } @@ -857,15 +897,16 @@ rend_check_authorization(rend_service_t *service, /** Remove elements from <b>service</b>'s replay cache that are old enough to * be noticed by timestamp checking. */ static void -clean_accepted_intros(rend_service_t *service, time_t now) +clean_accepted_intro_dh_parts(rend_service_t *service, time_t now) { const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL; - service->last_cleaned_accepted_intros = now; - if (!service->accepted_intros) + service->last_cleaned_accepted_intro_dh_parts = now; + if (!service->accepted_intro_dh_parts) return; - DIGESTMAP_FOREACH_MODIFY(service->accepted_intros, digest, time_t *, t) { + DIGESTMAP_FOREACH_MODIFY(service->accepted_intro_dh_parts, digest, + time_t *, t) { if (*t < cutoff) { tor_free(t); MAP_DEL_CURRENT(digest); @@ -890,6 +931,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, char buf[RELAY_PAYLOAD_SIZE]; char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */ rend_service_t *service; + rend_intro_point_t *intro_point; int r, i, v3_shift = 0; size_t len, keylen; crypto_dh_env_t *dh = NULL; @@ -939,7 +981,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, service = rend_service_get_by_pk_digest( circuit->rend_data->rend_pk_digest); if (!service) { - log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.", + log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro " + "circ for an unrecognized service %s.", escaped(serviceid)); return -1; } @@ -964,17 +1007,26 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, return -1; } - if (!service->accepted_intros) - service->accepted_intros = digestmap_new(); + intro_point = find_intro_point(circuit); + if (intro_point == NULL) { + log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro circ " + "(for service %s) with no corresponding rend_intro_point_t.", + escaped(serviceid)); + return -1; + } + + if (!service->accepted_intro_dh_parts) + service->accepted_intro_dh_parts = digestmap_new(); + + if (!intro_point->accepted_intro_rsa_parts) + intro_point->accepted_intro_rsa_parts = digestmap_new(); { char pkpart_digest[DIGEST_LEN]; - /* Check for replay of PK-encrypted portion. It is slightly naughty to - use the same digestmap to check for this and for g^x replays, but - collisions are tremendously unlikely. - */ + /* Check for replay of PK-encrypted portion. */ crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen); - access_time = digestmap_get(service->accepted_intros, pkpart_digest); + access_time = digestmap_get(intro_point->accepted_intro_rsa_parts, + pkpart_digest); if (access_time != NULL) { log_warn(LD_REND, "Possible replay detected! We received an " "INTRODUCE2 cell with same PK-encrypted part %d seconds ago. " @@ -983,7 +1035,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, } access_time = tor_malloc(sizeof(time_t)); *access_time = now; - digestmap_set(service->accepted_intros, pkpart_digest, access_time); + digestmap_set(intro_point->accepted_intro_rsa_parts, + pkpart_digest, access_time); } /* Next N bytes is encrypted with service key */ @@ -999,7 +1052,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, len = r; if (*buf == 3) { /* Version 3 INTRODUCE2 cell. */ - time_t ts = 0; v3_shift = 1; auth_type = buf[1]; switch (auth_type) { @@ -1021,17 +1073,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, log_info(LD_REND, "Unknown authorization type '%d'", auth_type); } - /* Check timestamp. */ - ts = ntohl(get_uint32(buf+1+v3_shift)); + /* Skip the timestamp field. We no longer use it. */ v3_shift += 4; - if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 || - (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) { - /* This is far more likely to mean that a client's clock is - * skewed than that a replay attack is in progress. */ - log_info(LD_REND, "INTRODUCE2 cell is too %s. Discarding.", - (now - ts) < 0 ? "old" : "new"); - return -1; - } } if (*buf == 2 || *buf == 3) { /* Version 2 INTRODUCE2 cell. */ @@ -1101,7 +1144,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, goto err; } - extend_info = extend_info_from_node(node); + extend_info = extend_info_from_node(node, 0); } if (len != REND_COOKIE_LEN+DH_KEY_LEN) { @@ -1130,7 +1173,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, /* Check whether there is a past request with the same Diffie-Hellman, * part 1. */ - access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash); + access_time = digestmap_get(service->accepted_intro_dh_parts, + diffie_hellman_hash); if (access_time != NULL) { /* A Tor client will send a new INTRODUCE1 cell with the same rend * cookie and DH public key as its previous one if its intro circ @@ -1152,9 +1196,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, * one hour. */ access_time = tor_malloc(sizeof(time_t)); *access_time = now; - digestmap_set(service->accepted_intros, diffie_hellman_hash, access_time); - if (service->last_cleaned_accepted_intros + REND_REPLAY_TIME_INTERVAL < now) - clean_accepted_intros(service, now); + digestmap_set(service->accepted_intro_dh_parts, + diffie_hellman_hash, access_time); + if (service->last_cleaned_accepted_intro_dh_parts + REND_REPLAY_TIME_INTERVAL + < now) + clean_accepted_intro_dh_parts(service, now); /* If the service performs client authorization, check included auth data. */ if (service->clients) { @@ -1416,7 +1462,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) /* If we already have enough introduction circuits for this service, * redefine this one as a general circuit or close it, depending. */ - if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) { + if (count_established_intro_points(serviceid) > + (int)service->n_intro_points_wanted) { /* XXX023 remove cast */ const or_options_t *options = get_options(); if (options->ExcludeNodes) { /* XXXX in some future version, we can test whether the transition is @@ -1658,6 +1705,35 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest) return NULL; } +/** Return a pointer to the rend_intro_point_t corresponding to the + * service-side introduction circuit <b>circ</b>. */ +static rend_intro_point_t * +find_intro_point(origin_circuit_t *circ) +{ + const char *serviceid; + rend_service_t *service = NULL; + + tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || + TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO); + tor_assert(circ->rend_data); + serviceid = circ->rend_data->onion_address; + + SMARTLIST_FOREACH(rend_service_list, rend_service_t *, s, + if (tor_memeq(s->service_id, serviceid, REND_SERVICE_ID_LEN_BASE32)) { + service = s; + break; + }); + + if (service == NULL) return NULL; + + SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro_point, + if (crypto_pk_cmp_keys(intro_point->intro_key, circ->intro_key) == 0) { + return intro_point; + }); + + return NULL; +} + /** Determine the responsible hidden service directories for the * rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them; * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging @@ -1861,6 +1937,52 @@ upload_service_descriptor(rend_service_t *service) service->desc_is_dirty = 0; } +/** Return non-zero iff <b>intro</b> should 'expire' now (i.e. we + * should stop publishing it in new descriptors and eventually close + * it). */ +static int +intro_point_should_expire_now(rend_intro_point_t *intro, + time_t now) +{ + tor_assert(intro != NULL); + + if (intro->time_published == -1) { + /* Don't expire an intro point if we haven't even published it yet. */ + return 0; + } + + if (intro->time_expiring != -1) { + /* We've already started expiring this intro point. *Don't* let + * this function's result 'flap'. */ + return 1; + } + + if (digestmap_size(intro->accepted_intro_rsa_parts) >= + INTRO_POINT_LIFETIME_INTRODUCTIONS) { + /* This intro point has been used too many times. Expire it now. */ + return 1; + } + + if (intro->time_to_expire == -1) { + /* This intro point has been published, but we haven't picked an + * expiration time for it. Pick one now. */ + int intro_point_lifetime_seconds = + INTRO_POINT_LIFETIME_MIN_SECONDS + + crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS - + INTRO_POINT_LIFETIME_MIN_SECONDS); + + /* Start the expiration timer now, rather than when the intro + * point was first published. There shouldn't be much of a time + * difference. */ + intro->time_to_expire = now + intro_point_lifetime_seconds; + + return 0; + } + + /* This intro point has a time to expire set already. Use it. */ + return (now >= intro->time_to_expire); +} + /** For every service, check how many intro points it currently has, and: * - Pick new intro points as necessary. * - Launch circuits to any new intro points. @@ -1872,7 +1994,9 @@ rend_services_introduce(void) const node_t *node; rend_service_t *service; rend_intro_point_t *intro; - int changed, prev_intro_nodes; + int intro_point_set_changed, prev_intro_nodes; + unsigned int n_intro_points_unexpired; + unsigned int n_intro_points_to_open; smartlist_t *intro_nodes; time_t now; const or_options_t *options = get_options(); @@ -1885,7 +2009,16 @@ rend_services_introduce(void) service = smartlist_get(rend_service_list, i); tor_assert(service); - changed = 0; + + /* intro_point_set_changed becomes non-zero iff the set of intro + * points to be published in service's descriptor has changed. */ + intro_point_set_changed = 0; + + /* n_intro_points_unexpired collects the number of non-expiring + * intro points we have, so that we know how many new intro + * circuits we need to launch for this service. */ + n_intro_points_unexpired = 0; + if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) { /* One period has elapsed; we can try building circuits again. */ service->intro_period_started = now; @@ -1899,59 +2032,116 @@ rend_services_introduce(void) /* Find out which introduction points we have in progress for this service. */ - for (j=0; j < smartlist_len(service->intro_nodes); ++j) { - intro = smartlist_get(service->intro_nodes, j); + SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *, + intro) { + origin_circuit_t *intro_circ = + find_intro_circuit(intro, service->pk_digest); + + if (intro->time_expiring + INTRO_POINT_EXPIRATION_GRACE_PERIOD > now) { + /* This intro point has completely expired. Remove it, and + * mark the circuit for close if it's still alive. */ + if (intro_circ != NULL) { + circuit_mark_for_close(TO_CIRCUIT(intro_circ), + END_CIRC_REASON_FINISHED); + } + rend_intro_point_free(intro); + intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */ + SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); + /* We don't need to set intro_point_set_changed here, because + * this intro point wouldn't have been published in a current + * descriptor anyway. */ + continue; + } + node = node_get_by_id(intro->extend_info->identity_digest); - if (!node || !find_intro_circuit(intro, service->pk_digest)) { - log_info(LD_REND,"Giving up on %s as intro point for %s.", + if (!node || !intro_circ) { + int removing_this_intro_point_changes_the_intro_point_set = 1; + log_info(LD_REND, "Giving up on %s as intro point for %s" + " (circuit disappeared).", safe_str_client(extend_info_describe(intro->extend_info)), safe_str_client(service->service_id)); - if (service->desc) { - SMARTLIST_FOREACH(service->desc->intro_nodes, rend_intro_point_t *, - dintro, { - if (tor_memeq(dintro->extend_info->identity_digest, - intro->extend_info->identity_digest, DIGEST_LEN)) { - log_info(LD_REND, "The intro point we are giving up on was " - "included in the last published descriptor. " - "Marking current descriptor as dirty."); - service->desc_is_dirty = now; - } - }); + if (intro->time_expiring != -1) { + log_info(LD_REND, "We were already expiring the intro point; " + "no need to mark the HS descriptor as dirty over this."); + removing_this_intro_point_changes_the_intro_point_set = 0; + } else if (intro->listed_in_last_desc) { + log_info(LD_REND, "The intro point we are giving up on was " + "included in the last published descriptor. " + "Marking current descriptor as dirty."); + service->desc_is_dirty = now; } rend_intro_point_free(intro); - smartlist_del(service->intro_nodes,j--); - changed = 1; + intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */ + SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); + if (removing_this_intro_point_changes_the_intro_point_set) + intro_point_set_changed = 1; } + + if (intro != NULL && intro_point_should_expire_now(intro, now)) { + log_info(LD_REND, "Expiring %s as intro point for %s.", + safe_str_client(extend_info_describe(intro->extend_info)), + safe_str_client(service->service_id)); + + /* The polite (and generally Right) way to expire an intro + * point is to establish a new one to replace it, publish a + * new descriptor that doesn't list any expiring intro points, + * and *then*, once our upload attempts for the new descriptor + * have ended (whether in success or failure), close the + * expiring intro points. + * + * Unfortunately, we can't find out when the new descriptor + * has actually been uploaded, so we'll have to settle for a + * five-minute timer. Start it. XXX023 This sucks. */ + intro->time_expiring = now; + + intro_point_set_changed = 1; + } + + if (intro != NULL && intro->time_expiring == -1) + ++n_intro_points_unexpired; + if (node) smartlist_add(intro_nodes, (void*)node); - } - - /* We have enough intro points, and the intro points we thought we had were - * all connected. - */ - if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) { - /* We have all our intro points! Start a fresh period and reset the - * circuit count. */ + } SMARTLIST_FOREACH_END(intro); + + if (!intro_point_set_changed && + (n_intro_points_unexpired >= service->n_intro_points_wanted)) { + /* We have enough intro circuits in progress, and none of our + * intro circuits have died since the last call to + * rend_services_introduce! Start a fresh period and reset the + * circuit count. + * + * XXXX WTF? */ service->intro_period_started = now; service->n_intro_circuits_launched = 0; continue; } - /* Remember how many introduction circuits we started with. */ + /* Remember how many introduction circuits we started with. + * + * prev_intro_nodes serves a different purpose than + * n_intro_points_unexpired -- this variable tells us where our + * previously-created intro points end and our new ones begin in + * the intro-point list, so we don't have to launch the circuits + * at the same time as we create the intro points they correspond + * to. XXXX This is daft. */ prev_intro_nodes = smartlist_len(service->intro_nodes); + /* We have enough directory information to start establishing our - * intro points. We want to end up with three intro points, but if - * we're just starting, we launch five and pick the first three that - * complete. + * intro points. We want to end up with n_intro_points_wanted + * intro points, but if we're just starting, we launch two extra + * circuits and use the first n_intro_points_wanted that complete. * * The ones after the first three will be converted to 'general' * internal circuits in rend_service_intro_has_opened(), and then * we'll drop them from the list of intro points next time we * go through the above "find out which introduction points we have * in progress" loop. */ -#define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2) - for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ? - NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) { + n_intro_points_to_open = (service->n_intro_points_wanted + + (prev_intro_nodes == 0 ? 2 : 0)); + for (j = (int)n_intro_points_unexpired; + j < (int)n_intro_points_to_open; + ++j) { /* XXXX remove casts */ router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC; if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION) flags |= CRN_ALLOW_INVALID; @@ -1959,16 +2149,21 @@ rend_services_introduce(void) options->ExcludeNodes, flags); if (!node) { log_warn(LD_REND, - "Could only establish %d introduction points for %s.", - smartlist_len(service->intro_nodes), service->service_id); + "Could only establish %d introduction points for %s; " + "wanted %u.", + smartlist_len(service->intro_nodes), service->service_id, + n_intro_points_to_open); break; } - changed = 1; + intro_point_set_changed = 1; smartlist_add(intro_nodes, (void*)node); intro = tor_malloc_zero(sizeof(rend_intro_point_t)); - intro->extend_info = extend_info_from_node(node); + intro->extend_info = extend_info_from_node(node, 0); intro->intro_key = crypto_new_pk_env(); tor_assert(!crypto_pk_generate_key(intro->intro_key)); + intro->time_published = -1; + intro->time_to_expire = -1; + intro->time_expiring = -1; smartlist_add(service->intro_nodes, intro); log_info(LD_REND, "Picked router %s as an intro point for %s.", safe_str_client(node_describe(node)), @@ -1976,7 +2171,7 @@ rend_services_introduce(void) } /* If there's no need to launch new circuits, stop here. */ - if (!changed) + if (!intro_point_set_changed) continue; /* Establish new introduction points. */ diff --git a/src/or/router.c b/src/or/router.c index b6b96a5ff..a08a2009a 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -484,6 +484,16 @@ v3_authority_check_key_expiry(void) last_warned = now; } +int +router_initialize_tls_context(void) +{ + return tor_tls_context_init(public_server_mode(get_options()), + get_tlsclient_identity_key(), + server_mode(get_options()) ? + get_server_identity_key() : NULL, + MAX_SSL_KEY_LIFETIME_ADVERTISED); +} + /** Initialize all OR private keys, and the TLS context, as necessary. * On OPs, this only initializes the tls context. Return 0 on success, * or -1 if Tor should die. @@ -530,10 +540,7 @@ init_keys(void) } set_client_identity_key(prkey); /* Create a TLS context. */ - if (tor_tls_context_init(0, - get_tlsclient_identity_key(), - NULL, - MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) { + if (router_initialize_tls_context() < 0) { log_err(LD_GENERAL,"Error creating TLS context for Tor client."); return -1; } @@ -626,13 +633,11 @@ init_keys(void) tor_free(keydir); /* 3. Initialize link key and TLS context. */ - if (tor_tls_context_init(public_server_mode(options), - get_tlsclient_identity_key(), - get_server_identity_key(), - MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) { + if (router_initialize_tls_context() < 0) { log_err(LD_GENERAL,"Error initializing TLS context"); return -1; } + /* 4. Build our router descriptor. */ /* Must be called after keys are initialized. */ mydesc = router_get_my_descriptor(); @@ -780,7 +785,7 @@ check_whether_dirport_reachable(void) const or_options_t *options = get_options(); return !options->DirPort || options->AssumeReachable || - we_are_hibernating() || + net_is_disabled() || can_reach_dir_port; } @@ -806,7 +811,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) return 0; if (authdir_mode(options)) /* always publish */ return dir_port; - if (we_are_hibernating()) + if (net_is_disabled()) return 0; if (!check_whether_dirport_reachable()) return 0; @@ -888,7 +893,8 @@ consider_testing_reachability(int test_or, int test_dir) log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.", !orport_reachable ? "reachability" : "bandwidth", me->address, me->or_port); - ei = extend_info_from_router(me); + /* XXX IPv6 self testing IPv6 orports will need pref_addr */ + ei = extend_info_from_router(me, 0); circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); extend_info_free(ei); @@ -974,6 +980,14 @@ router_perform_bandwidth_test(int num_circs, time_t now) } } +/** Return true iff our network is in some sense disabled: either we're + * hibernating, entering hibernation, or */ +int +net_is_disabled(void) +{ + return get_options()->DisableNetwork || we_are_hibernating(); +} + /** Return true iff we believe ourselves to be an authoritative * directory server. */ @@ -1070,7 +1084,7 @@ int server_mode(const or_options_t *options) { if (options->ClientOnly) return 0; - return (options->ORPort != 0 || options->ORListenAddress); + return (options->ORPort || options->ORListenAddress); } /** Return true iff we are trying to be a non-bridge server. @@ -1121,7 +1135,14 @@ int proxy_mode(const or_options_t *options) { (void)options; - return smartlist_len(get_configured_client_ports()) > 0; + SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) { + if (p->type == CONN_TYPE_AP_LISTENER || + p->type == CONN_TYPE_AP_TRANS_LISTENER || + p->type == CONN_TYPE_AP_DNS_LISTENER || + p->type == CONN_TYPE_AP_NATD_LISTENER) + return 1; + } SMARTLIST_FOREACH_END(p); + return 0; } /** Decide if we're a publishable server. We are a publishable server if: @@ -1180,17 +1201,21 @@ consider_publishable_server(int force) /** Return the port that we should advertise as our ORPort; this is either * the one configured in the ORPort option, or the one we actually bound to - * if ORPort is "auto". */ + * if ORPort is "auto". + */ uint16_t router_get_advertised_or_port(const or_options_t *options) { - if (options->ORPort == CFG_AUTO_PORT) { + int port = get_primary_or_port(); + (void)options; + + if (port == CFG_AUTO_PORT) { connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER); if (c) return c->port; return 0; } - return options->ORPort; + return port; } /** Return the port that we should advertise as our DirPort; @@ -1201,15 +1226,18 @@ router_get_advertised_or_port(const or_options_t *options) uint16_t router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport) { - if (!options->DirPort) + int dirport_configured = get_primary_dir_port(); + (void)options; + + if (!dirport_configured) return dirport; - if (options->DirPort == CFG_AUTO_PORT) { + if (dirport_configured == CFG_AUTO_PORT) { connection_t *c = connection_get_by_type(CONN_TYPE_DIR_LISTENER); if (c) return c->port; return 0; } - return options->DirPort; + return dirport_configured; } /* @@ -1466,6 +1494,24 @@ router_rebuild_descriptor(int force) ri->cache_info.published_on = time(NULL); ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from * main thread */ + if (options->BridgeRelay) { + /* For now, only bridges advertise an ipv6 or-address. And only one. */ + const port_cfg_t *ipv6_orport = NULL; + SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) { + if (p->type == CONN_TYPE_OR_LISTENER && + ! p->no_advertise && + ! p->ipv4_only && + tor_addr_family(&p->addr) == AF_INET6 && + ! tor_addr_is_internal(&p->addr, 1)) { + ipv6_orport = p; + break; + } + } SMARTLIST_FOREACH_END(p); + if (ipv6_orport) { + tor_addr_copy(&ri->ipv6_addr, &ipv6_orport->addr); + ri->ipv6_orport = ipv6_orport->port; + } + } ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key()); if (crypto_pk_get_digest(ri->identity_pkey, ri->cache_info.identity_digest)<0) { @@ -1875,6 +1921,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, int result=0; addr_policy_t *tmpe; char *family_line; + char *extra_or_address = NULL; const or_options_t *options = get_options(); /* Make sure the identity key matches the one in the routerinfo. */ @@ -1927,9 +1974,22 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, router->cache_info.extra_info_digest, DIGEST_LEN); } + if (router->ipv6_orport && + tor_addr_family(&router->ipv6_addr) == AF_INET6) { + char addr[TOR_ADDR_BUF_LEN]; + const char *a; + a = tor_addr_to_str(addr, &router->ipv6_addr, sizeof(addr), 1); + if (a) { + tor_asprintf(&extra_or_address, + "or-address %s:%d\n", a, router->ipv6_orport); + log_notice(LD_OR, "My line is <%s>", extra_or_address); + } + } + /* Generate the easy portion of the router descriptor. */ result = tor_snprintf(s, maxlen, "router %s %s %d 0 %d\n" + "%s" "platform %s\n" "opt protocols Link 1 2 Circuit 1\n" "published %s\n" @@ -1944,6 +2004,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, router->address, router->or_port, decide_to_advertise_dirport(options, router->dir_port), + extra_or_address ? extra_or_address : "", router->platform, published, fingerprint, @@ -1964,6 +2025,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, tor_free(family_line); tor_free(onion_pkey); tor_free(identity_pkey); + tor_free(extra_or_address); if (result < 0) { log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!"); @@ -2059,6 +2121,52 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, return (int)written+1; } +/** Copy the primary (IPv4) OR port (IP address and TCP port) for + * <b>router</b> into *<b>ap_out</b>. */ +void +router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +{ + tor_assert(ap_out != NULL); + tor_addr_from_ipv4h(&ap_out->addr, router->addr); + ap_out->port = router->or_port; +} + +/** Return 1 if we prefer the IPv6 address and OR TCP port of + * <b>router</b>, else 0. + * + * We prefer the IPv6 address if the router has one and + * i) the routerinfo_t says so + * or + * ii) the router has no IPv4 address. */ +int +router_ipv6_preferred(const routerinfo_t *router) +{ + return (!tor_addr_is_null(&router->ipv6_addr) + && (router->ipv6_preferred || router->addr == 0)); +} + +/** Copy the preferred OR port (IP address and TCP port) for + * <b>router</b> into *<b>addr_out</b>. */ +void +router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +{ + if (router_ipv6_preferred(router)) + router_get_pref_ipv6_orport(router, ap_out); + else + router_get_prim_orport(router, ap_out); +} + +/** Copy the preferred IPv6 OR port (IP address and TCP port) for + * <b>router</b> into *<b>ap_out</b>. */ +void +router_get_pref_ipv6_orport(const routerinfo_t *router, + tor_addr_port_t *ap_out) +{ + tor_assert(ap_out != NULL); + tor_addr_copy(&ap_out->addr, &router->ipv6_addr); + ap_out->port = router->ipv6_orport; +} + /** Load the contents of <b>filename</b>, find the last line starting with * <b>end_line</b>, ensure that its timestamp is not more than 25 hours in * the past or more than 1 hour in the future with respect to <b>now</b>, diff --git a/src/or/router.h b/src/or/router.h index f9d156cb0..d426b25da 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -30,6 +30,7 @@ crypto_pk_env_t *init_key_from_file(const char *fname, int generate, int severity); void v3_authority_check_key_expiry(void); +int router_initialize_tls_context(void); int init_keys(void); int check_whether_orport_reachable(void); @@ -39,6 +40,8 @@ void router_orport_found_reachable(void); void router_dirport_found_reachable(void); void router_perform_bandwidth_test(int num_circs, time_t now); +int net_is_disabled(void); + int authdir_mode(const or_options_t *options); int authdir_mode_v1(const or_options_t *options); int authdir_mode_v2(const or_options_t *options); @@ -82,6 +85,13 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr); int router_rebuild_descriptor(int force); int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, crypto_pk_env_t *ident_key); +void router_get_prim_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +void router_get_pref_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +void router_get_pref_ipv6_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +int router_ipv6_preferred(const routerinfo_t *router); int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, crypto_pk_env_t *ident_key); int is_legal_nickname(const char *s); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index d97b978f4..689df99c5 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -3244,7 +3244,7 @@ router_set_status(const char *digest, int up) log_debug(LD_DIR,"Marking router %s as %s.", node_describe(node), up ? "up" : "down"); #endif - if (!up && node_is_me(node) && !we_are_hibernating()) + if (!up && node_is_me(node) && !net_is_disabled()) log_warn(LD_NET, "We just marked ourself as down. Are your external " "addresses reachable?"); node->is_running = up; @@ -4009,6 +4009,8 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc) void update_all_descriptor_downloads(time_t now) { + if (get_options()->DisableNetwork) + return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); launch_dummy_descriptor_download_as_needed(now, get_options()); @@ -4021,6 +4023,8 @@ routerlist_retry_directory_downloads(time_t now) { router_reset_status_download_failures(); router_reset_descriptor_download_failures(); + if (get_options()->DisableNetwork) + return; update_networkstatus_downloads(now); update_all_descriptor_downloads(now); } diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 4ea7b964c..678b11971 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -64,6 +64,7 @@ typedef enum { K_DIR_OPTIONS, K_CLIENT_VERSIONS, K_SERVER_VERSIONS, + K_OR_ADDRESS, K_P, K_R, K_S, @@ -286,6 +287,7 @@ static token_rule_t routerdesc_token_table[] = { T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ), + T0N("or-address", K_OR_ADDRESS, GE(1), NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ), @@ -541,6 +543,7 @@ static int router_get_hashes_impl(const char *s, size_t s_len, const char *start_str, const char *end_str, char end_char); static void token_clear(directory_token_t *tok); +static smartlist_t *find_all_by_keyword(smartlist_t *s, directory_keyword k); static smartlist_t *find_all_exitpolicy(smartlist_t *s); static directory_token_t *_find_by_keyword(smartlist_t *s, directory_keyword keyword, @@ -1506,6 +1509,27 @@ router_parse_entry_from_string(const char *s, const char *end, "older Tors."); goto err; } + { + smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS); + if (or_addresses) { + SMARTLIST_FOREACH_BEGIN(or_addresses, directory_token_t *, t) { + tor_addr_t a; + maskbits_t bits; + uint16_t port_min, port_max; + /* XXXX Prop186 the full spec allows much more than this. */ + if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min, + &port_max) == AF_INET6 && + bits == 128 && + port_min == port_max) { + /* Okay, this is one we can understand. */ + tor_addr_copy(&router->ipv6_addr, &a); + router->ipv6_orport = port_min; + break; + } + } SMARTLIST_FOREACH_END(t); + smartlist_free(or_addresses); + } + } exit_policy_tokens = find_all_exitpolicy(tokens); if (!smartlist_len(exit_policy_tokens)) { log_warn(LD_DIR, "No exit policy tokens in descriptor."); @@ -4134,6 +4158,20 @@ _find_by_keyword(smartlist_t *s, directory_keyword keyword, return tok; } +/** DOCDOC */ +static smartlist_t * +find_all_by_keyword(smartlist_t *s, directory_keyword k) +{ + smartlist_t *out = NULL; + SMARTLIST_FOREACH(s, directory_token_t *, t, + if (t->tp == k) { + if (!out) + out = smartlist_create(); + smartlist_add(out, t); + }); + return out; +} + /** Return a newly allocated smartlist of all accept or reject tokens in * <b>s</b>. */ diff --git a/src/or/transports.c b/src/or/transports.c index 6e8200f40..eaaf1a3bf 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -12,12 +12,20 @@ #include "circuitbuild.h" #include "transports.h" #include "util.h" +#include "router.h" -static void set_managed_proxy_environment(char ***envp, +#ifdef MS_WINDOWS +static void set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp); +#else +static int set_managed_proxy_environment(char ***envp, + const managed_proxy_t *mp); +#endif + static INLINE int proxy_configuration_finished(const managed_proxy_t *mp); -static void managed_proxy_destroy(managed_proxy_t *mp); +static void managed_proxy_destroy(managed_proxy_t *mp, + int also_terminate_process); static void handle_finished_proxy(managed_proxy_t *mp); static void configure_proxy(managed_proxy_t *mp); @@ -229,12 +237,10 @@ proxy_prepare_for_restart(managed_proxy_t *mp) transport_t *t_tmp = NULL; tor_assert(mp->conf_state == PT_PROTO_COMPLETED); - tor_assert(mp->pid); - /* kill the old obfsproxy process */ - tor_terminate_process(mp->pid); - mp->pid = 0; - fclose(mp->_stdout); + /* destroy the process handle and terminate the process. */ + tor_process_handle_destroy(mp->process_handle, 1); + mp->process_handle = NULL; /* destroy all its old transports. we no longer use them. */ SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) { @@ -256,52 +262,52 @@ proxy_prepare_for_restart(managed_proxy_t *mp) static int launch_managed_proxy(managed_proxy_t *mp) { - (void) mp; - (void) set_managed_proxy_environment; - return -1; -#if 0 - /* XXXX023 we must reenable this code for managed proxies to work. - * "All it needs" is revision to work with the new tor_spawn_background - * API. */ - char **envp=NULL; - int pid; - process_handle_t proc; - FILE *stdout_read = NULL; - int stdout_pipe=-1, stderr_pipe=-1; + int retval; + +#ifdef MS_WINDOWS + + LPVOID envp=NULL; - /* prepare the environment variables for the managed proxy */ set_managed_proxy_environment(&envp, mp); + tor_assert(envp); - pid = tor_spawn_background(mp->argv[0], (const char **)mp->argv, - (const char **)envp, &proc); - if (pid < 0) { - log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.", - mp->argv[0]); + /* Passing NULL as lpApplicationName makes Windows search for the .exe */ + retval = tor_spawn_background(NULL, (const char **)mp->argv, envp, + &mp->process_handle); + + tor_free(envp); + +#else + + char **envp=NULL; + + /* prepare the environment variables for the managed proxy */ + if (set_managed_proxy_environment(&envp, mp) < 0) { + log_warn(LD_GENERAL, "Could not setup the environment of " + "the managed proxy at '%s'.", mp->argv[0]); + free_execve_args(envp); return -1; } + retval = tor_spawn_background(mp->argv[0], (const char **)mp->argv, + (const char **)envp, &mp->process_handle); + /* free the memory allocated by set_managed_proxy_environment(). */ free_execve_args(envp); - /* Set stdout/stderr pipes to be non-blocking */ -#ifdef _WIN32 - { - u_long nonblocking = 1; - ioctlsocket(stdout_pipe, FIONBIO, &nonblocking); - } -#else - fcntl(stdout_pipe, F_SETFL, O_NONBLOCK); #endif - /* Open the buffered IO streams */ - stdout_read = fdopen(stdout_pipe, "r"); + if (retval == PROCESS_STATUS_ERROR) { + log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.", + mp->argv[0]); + return -1; + } - log_info(LD_CONFIG, "Managed proxy has spawned at PID %d.", pid); + log_info(LD_CONFIG, "Managed proxy at '%s' has spawned with PID '%d'.", + mp->argv[0], tor_process_get_pid(mp->process_handle)); mp->conf_state = PT_PROTO_LAUNCHED; - mp->_stdout = stdout_read; - mp->pid = pid; -#endif + return 0; } @@ -314,7 +320,8 @@ pt_configure_remaining_proxies(void) log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!", unconfigured_proxies_n); SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { - tor_assert(mp->conf_state != PT_PROTO_BROKEN); + tor_assert(mp->conf_state != PT_PROTO_BROKEN || + mp->conf_state != PT_PROTO_FAILED_LAUNCH); if (mp->got_hup) { mp->got_hup = 0; @@ -343,6 +350,65 @@ pt_configure_remaining_proxies(void) } SMARTLIST_FOREACH_END(mp); } +#ifdef MS_WINDOWS + +/** Attempt to continue configuring managed proxy <b>mp</b>. */ +static void +configure_proxy(managed_proxy_t *mp) +{ + int pos; + char stdout_buf[200]; + smartlist_t *lines = NULL; + + /* if we haven't launched the proxy yet, do it now */ + if (mp->conf_state == PT_PROTO_INFANT) { + if (launch_managed_proxy(mp) < 0) { /* launch fail */ + mp->conf_state = PT_PROTO_FAILED_LAUNCH; + handle_finished_proxy(mp); + } + return; + } + + tor_assert(mp->conf_state != PT_PROTO_INFANT); + tor_assert(mp->process_handle); + + pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle), + stdout_buf, sizeof(stdout_buf) - 1, NULL); + if (pos < 0) { + log_notice(LD_GENERAL, "Failed to read data from managed proxy"); + mp->conf_state = PT_PROTO_BROKEN; + goto done; + } + + if (pos == 0) /* proxy has nothing interesting to say. */ + return; + + /* End with a null even if there isn't a \r\n at the end */ + /* TODO: What if this is a partial line? */ + stdout_buf[pos] = '\0'; + + /* Split up the buffer */ + lines = smartlist_create(); + tor_split_lines(lines, stdout_buf, pos); + + /* Handle lines. */ + SMARTLIST_FOREACH_BEGIN(lines, const char *, line) { + handle_proxy_line(line, mp); + if (proxy_configuration_finished(mp)) + goto done; + } SMARTLIST_FOREACH_END(line); + + done: + /* if the proxy finished configuring, exit the loop. */ + if (proxy_configuration_finished(mp)) + handle_finished_proxy(mp); + + if (lines) + smartlist_free(lines); +} + +#else /* MS_WINDOWS */ + /** Attempt to continue configuring managed proxy <b>mp</b>. */ static void configure_proxy(managed_proxy_t *mp) @@ -352,15 +418,19 @@ configure_proxy(managed_proxy_t *mp) /* if we haven't launched the proxy yet, do it now */ if (mp->conf_state == PT_PROTO_INFANT) { - launch_managed_proxy(mp); + if (launch_managed_proxy(mp) < 0) { /* launch fail */ + mp->conf_state = PT_PROTO_FAILED_LAUNCH; + handle_finished_proxy(mp); + } return; } tor_assert(mp->conf_state != PT_PROTO_INFANT); + tor_assert(mp->process_handle); while (1) { - r = get_string_from_pipe(mp->_stdout, stdout_buf, - sizeof(stdout_buf) - 1); + r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle), + stdout_buf, sizeof(stdout_buf) - 1); if (r == IO_STREAM_OKAY) { /* got a line; handle it! */ handle_proxy_line((const char *)stdout_buf, mp); @@ -382,6 +452,8 @@ configure_proxy(managed_proxy_t *mp) } } +#endif /* MS_WINDOWS */ + /** Register server managed proxy <b>mp</b> transports to state */ static void register_server_proxy(managed_proxy_t *mp) @@ -394,6 +466,10 @@ register_server_proxy(managed_proxy_t *mp) tor_assert(mp->conf_state != PT_PROTO_COMPLETED); SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { save_transport_to_state(t->name, &t->addr, t->port); + /* LOG_WARN so that the bridge operator can easily find the + transport's port in the log file and send it to the users. */ + log_warn(LD_GENERAL, "Registered server transport '%s' at '%s:%d'", + t->name, fmt_addr(&t->addr), (int)t->port); smartlist_add(sm_tmp, tor_strdup(t->name)); } SMARTLIST_FOREACH_END(t); @@ -453,7 +529,8 @@ register_proxy(managed_proxy_t *mp) /** Free memory allocated by managed proxy <b>mp</b>. */ static void -managed_proxy_destroy(managed_proxy_t *mp) +managed_proxy_destroy(managed_proxy_t *mp, + int also_terminate_process) { if (mp->conf_state != PT_PROTO_COMPLETED) SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); @@ -470,15 +547,11 @@ managed_proxy_destroy(managed_proxy_t *mp) /* remove it from the list of managed proxies */ smartlist_remove(managed_proxy_list, mp); - /* close its stdout stream */ - if (mp->_stdout) - fclose(mp->_stdout); - /* free the argv */ free_execve_args(mp->argv); - if (mp->pid) - tor_terminate_process(mp->pid); + tor_process_handle_destroy(mp->process_handle, also_terminate_process); + mp->process_handle = NULL; tor_free(mp); } @@ -489,7 +562,10 @@ handle_finished_proxy(managed_proxy_t *mp) { switch (mp->conf_state) { case PT_PROTO_BROKEN: /* if broken: */ - managed_proxy_destroy(mp); /* annihilate it. */ + managed_proxy_destroy(mp, 1); /* annihilate it. */ + break; + case PT_PROTO_FAILED_LAUNCH: /* if it failed before launching: */ + managed_proxy_destroy(mp, 0); /* destroy it but don't terminate */ break; case PT_PROTO_CONFIGURED: /* if configured correctly: */ register_proxy(mp); /* register its transports */ @@ -515,7 +591,8 @@ static INLINE int proxy_configuration_finished(const managed_proxy_t *mp) { return (mp->conf_state == PT_PROTO_CONFIGURED || - mp->conf_state == PT_PROTO_BROKEN); + mp->conf_state == PT_PROTO_BROKEN || + mp->conf_state == PT_PROTO_FAILED_LAUNCH); } /** This function is called when a proxy sends an {S,C}METHODS DONE message. */ @@ -537,7 +614,7 @@ handle_methods_done(const managed_proxy_t *mp) void handle_proxy_line(const char *line, managed_proxy_t *mp) { - log_debug(LD_GENERAL, "Got a line from managed proxy: %s\n", line); + log_debug(LD_GENERAL, "Got a line from managed proxy: %s", line); if (strlen(line) < SMALLEST_MANAGED_LINE_SIZE) { log_warn(LD_GENERAL, "Managed proxy configuration line is too small. " @@ -614,14 +691,16 @@ handle_proxy_line(const char *line, managed_proxy_t *mp) return; } else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) { log_warn(LD_GENERAL, "Could not launch managed proxy executable!"); - goto err; + mp->conf_state = PT_PROTO_FAILED_LAUNCH; + return; } log_warn(LD_CONFIG, "Unknown line received by managed proxy. (%s)", line); err: mp->conf_state = PT_PROTO_BROKEN; - return; + log_warn(LD_CONFIG, "Managed proxy at '%s' failed the configuration protocol" + " and will be destroyed.", mp->argv ? mp->argv[0] : ""); } /** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */ @@ -858,8 +937,115 @@ get_bindaddr_for_proxy(const managed_proxy_t *mp) return bindaddr; } -/** Prepare the <b>envp</b> of managed proxy <b>mp</b> */ +#ifdef MS_WINDOWS + +/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>. + * <b>envp</b> is allocated on the heap and should be freed by the + * caller after its use. */ static void +set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp) +{ + const or_options_t *options = get_options(); + extern char **environ; + + LPVOID tmp=NULL; + + char *state_tmp=NULL; + char *state_env=NULL; + char *transports_to_launch=NULL; + char *transports_env=NULL; + char *bindaddr_tmp=NULL; + char *bindaddr_env=NULL; + char *orport_env=NULL; + + char version_env[31]; /* XXX temp */ + char extended_env[43]; /* XXX temp */ + + int env_size = 0; + + /* A smartlist carrying all the env. variables that the managed + proxy should inherit. */ + smartlist_t *envs = smartlist_create(); + + /* Copy the whole environment of the Tor process. + It should also copy PATH and HOME of the Tor process.*/ + char **environ_tmp = environ; + while (*environ_tmp) + smartlist_add(envs, *environ_tmp++); + + /* Create the TOR_PT_* environment variables. */ + state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */ + tor_asprintf(&state_env, "TOR_PT_STATE_LOCATION=%s", state_tmp); + + strcpy(version_env, "TOR_PT_MANAGED_TRANSPORT_VER=1"); + + transports_to_launch = + smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL); + + tor_asprintf(&transports_env, + mp->is_server ? + "TOR_PT_SERVER_TRANSPORTS=%s" : "TOR_PT_CLIENT_TRANSPORTS=%s", + transports_to_launch); + + smartlist_add(envs, state_env); + smartlist_add(envs, version_env); + smartlist_add(envs, transports_env); + + if (mp->is_server) { + tor_asprintf(&orport_env, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort); + + bindaddr_tmp = get_bindaddr_for_proxy(mp); + tor_asprintf(&bindaddr_env, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp); + + strcpy(extended_env, "TOR_PT_EXTENDED_SERVER_PORT=127.0.0.1:4200"); + + smartlist_add(envs, orport_env); + smartlist_add(envs, extended_env); + smartlist_add(envs, bindaddr_env); + } + + /* It seems like some versions of Windows need a sorted lpEnvironment + block. */ + smartlist_sort_strings(envs); + + /* An environment block consists of a null-terminated block of + null-terminated strings: */ + + /* Calculate the block's size. */ + SMARTLIST_FOREACH(envs, const char *, s, + env_size += strlen(s) + 1); + env_size += 1; /* space for last NUL */ + + *envp = tor_malloc(env_size); + tmp = *envp; + + /* Create the block. */ + SMARTLIST_FOREACH_BEGIN(envs, const char *, s) { + memcpy(tmp, s, strlen(s)); /* copy the env. variable string */ + tmp += strlen(s); + memset(tmp, '\0', 1); /* append NUL at the end of the string */ + tmp += 1; + } SMARTLIST_FOREACH_END(s); + memset(tmp, '\0', 1); /* last NUL */ + + /* Finally, free the whole mess. */ + tor_free(state_tmp); + tor_free(state_env); + tor_free(transports_to_launch); + tor_free(transports_env); + tor_free(bindaddr_tmp); + tor_free(bindaddr_env); + tor_free(orport_env); + + smartlist_free(envs); +} + +#else /* MS_WINDOWS */ + +/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>. + * <b>envp</b> is allocated on the heap and should be freed by the + * caller after its use. */ +static int set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) { const or_options_t *options = get_options(); @@ -867,7 +1053,10 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) char *state_loc=NULL; char *transports_to_launch=NULL; char *bindaddr=NULL; + char *home_env=NULL; + char *path_env=NULL; + int r = -1; int n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT; /* allocate enough space for our env. vars and a NULL pointer */ @@ -878,15 +1067,21 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) transports_to_launch = smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL); - tor_asprintf(tmp++, "HOME=%s", getenv("HOME")); - tor_asprintf(tmp++, "PATH=%s", getenv("PATH")); + home_env = getenv("HOME"); + path_env = getenv("PATH"); + if (!home_env || !path_env) + goto done; + + tor_asprintf(tmp++, "HOME=%s", home_env); + tor_asprintf(tmp++, "PATH=%s", path_env); tor_asprintf(tmp++, "TOR_PT_STATE_LOCATION=%s", state_loc); tor_asprintf(tmp++, "TOR_PT_MANAGED_TRANSPORT_VER=1"); /* temp */ if (mp->is_server) { bindaddr = get_bindaddr_for_proxy(mp); /* XXX temp */ - tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort); + tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", + router_get_advertised_or_port(options)); tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr); tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch); /* XXX temp*/ @@ -896,11 +1091,18 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) } *tmp = NULL; + r = 0; + + done: tor_free(state_loc); tor_free(transports_to_launch); tor_free(bindaddr); + + return r; } +#endif /* MS_WINDOWS */ + /** Create and return a new managed proxy for <b>transport</b> using * <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server * managed proxy. */ @@ -995,9 +1197,9 @@ pt_prepare_proxy_list_for_config_read(void) SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { /* Destroy unconfigured proxies. */ if (mp->conf_state != PT_PROTO_COMPLETED) { - managed_proxy_destroy(mp); - unconfigured_proxies_n--; - continue; + managed_proxy_destroy(mp, 1); + unconfigured_proxies_n--; + continue; } tor_assert(mp->conf_state == PT_PROTO_COMPLETED); @@ -1024,7 +1226,7 @@ sweep_proxy_list(void) SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { if (mp->marked_for_removal) { SMARTLIST_DEL_CURRENT(managed_proxy_list, mp); - managed_proxy_destroy(mp); + managed_proxy_destroy(mp, 1); } } SMARTLIST_FOREACH_END(mp); } @@ -1039,7 +1241,7 @@ pt_free_all(void) free them. Otherwise, it hasn't registered its transports yet and we should free them here. */ SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp, - managed_proxy_destroy(mp)); + managed_proxy_destroy(mp, 1)); smartlist_free(managed_proxy_list); managed_proxy_list=NULL; diff --git a/src/or/transports.h b/src/or/transports.h index 4a9338759..314af2b3a 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -36,7 +36,8 @@ enum pt_proto_state { PT_PROTO_ACCEPTING_METHODS, /* accepting methods */ PT_PROTO_CONFIGURED, /* configured successfully */ PT_PROTO_COMPLETED, /* configure and registered its transports */ - PT_PROTO_BROKEN + PT_PROTO_BROKEN, /* broke during the protocol */ + PT_PROTO_FAILED_LAUNCH /* failed while launching */ }; /** Structure containing information of a managed proxy. */ @@ -47,8 +48,8 @@ typedef struct { int is_server; /* is it a server proxy? */ - FILE *_stdout; /* a stream to its stdout - (closed in managed_proxy_destroy()) */ + /* A pointer to the process handle of this managed proxy. */ + process_handle_t *process_handle; int pid; /* The Process ID this managed proxy is using. */ diff --git a/src/test/Makefile.am b/src/test/Makefile.am index ffe1f942e..73de30071 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -21,6 +21,7 @@ test_SOURCES = \ test_microdesc.c \ test_pt.c \ test_util.c \ + test_config.c \ tinytest.c bench_SOURCES = \ diff --git a/src/test/test.c b/src/test/test.c index d4edf1484..5fcc31c47 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1844,6 +1844,7 @@ extern struct testcase_t util_tests[]; extern struct testcase_t dir_tests[]; extern struct testcase_t microdesc_tests[]; extern struct testcase_t pt_tests[]; +extern struct testcase_t config_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -1855,6 +1856,7 @@ static struct testgroup_t testgroups[] = { { "dir/", dir_tests }, { "dir/md/", microdesc_tests }, { "pt/", pt_tests }, + { "config/", config_tests }, END_OF_GROUPS }; @@ -1907,6 +1909,7 @@ main(int c, const char **v) printf("Can't initialize crypto subsystem; exiting.\n"); return 1; } + crypto_set_tls_dh_prime(NULL); rep_hist_init(); network_init(); setup_directory(); diff --git a/src/test/test_config.c b/src/test/test_config.c new file mode 100644 index 000000000..4ce0fa845 --- /dev/null +++ b/src/test/test_config.c @@ -0,0 +1,170 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2010, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "or.h" +#include "config.h" +#include "connection_edge.h" +#include "test.h" + +static void +test_config_addressmap(void *arg) +{ + char buf[1024]; + char address[256]; + time_t expires = TIME_MAX; + (void)arg; + + strlcpy(buf, "MapAddress .invalidwildcard.com *.torserver.exit\n" // invalid + "MapAddress *invalidasterisk.com *.torserver.exit\n" // invalid + "MapAddress *.google.com *.torserver.exit\n" + "MapAddress *.yahoo.com *.google.com.torserver.exit\n" + "MapAddress *.cn.com www.cnn.com\n" + "MapAddress *.cnn.com www.cnn.com\n" + "MapAddress ex.com www.cnn.com\n" + "MapAddress ey.com *.cnn.com\n" + "MapAddress www.torproject.org 1.1.1.1\n" + "MapAddress other.torproject.org " + "this.torproject.org.otherserver.exit\n" + "MapAddress test.torproject.org 2.2.2.2\n" + "MapAddress www.google.com 3.3.3.3\n" + "MapAddress www.example.org 4.4.4.4\n" + "MapAddress 4.4.4.4 7.7.7.7\n" + "MapAddress 4.4.4.4 5.5.5.5\n" + "MapAddress www.infiniteloop.org 6.6.6.6\n" + "MapAddress 6.6.6.6 www.infiniteloop.org\n" + , sizeof(buf)); + + config_get_lines(buf, &(get_options_mutable()->AddressMap), 0); + config_register_addressmaps(get_options()); + + /* MapAddress .invalidwildcard.com .torserver.exit - no match */ + strlcpy(address, "www.invalidwildcard.com", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + + /* MapAddress *invalidasterisk.com .torserver.exit - no match */ + strlcpy(address, "www.invalidasterisk.com", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + + /* Where no mapping for FQDN match on top-level domain */ + /* MapAddress .google.com .torserver.exit */ + strlcpy(address, "reader.google.com", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "reader.torserver.exit"); + + /* MapAddress *.yahoo.com *.google.com.torserver.exit */ + strlcpy(address, "reader.yahoo.com", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "reader.google.com.torserver.exit"); + + /*MapAddress *.cnn.com www.cnn.com */ + strlcpy(address, "cnn.com", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "www.cnn.com"); + + /* MapAddress .cn.com www.cnn.com */ + strlcpy(address, "www.cn.com", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "www.cnn.com"); + + /* MapAddress ex.com www.cnn.com - no match */ + strlcpy(address, "www.ex.com", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + + /* MapAddress ey.com *.cnn.com - invalid expression */ + strlcpy(address, "ey.com", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + + /* Where mapping for FQDN match on FQDN */ + strlcpy(address, "www.google.com", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "3.3.3.3"); + + strlcpy(address, "www.torproject.org", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "1.1.1.1"); + + strlcpy(address, "other.torproject.org", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "this.torproject.org.otherserver.exit"); + + strlcpy(address, "test.torproject.org", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "2.2.2.2"); + + /* Test a chain of address mappings and the order in which they were added: + "MapAddress www.example.org 4.4.4.4" + "MapAddress 4.4.4.4 7.7.7.7" + "MapAddress 4.4.4.4 5.5.5.5" + */ + strlcpy(address, "www.example.org", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "5.5.5.5"); + + /* Test infinite address mapping results in no change */ + strlcpy(address, "www.infiniteloop.org", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "www.infiniteloop.org"); + + /* Test we don't find false positives */ + strlcpy(address, "www.example.com", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + + /* Test top-level-domain matching a bit harder */ + addressmap_clear_configured(); + strlcpy(buf, "MapAddress *.com *.torserver.exit\n" + "MapAddress *.torproject.org 1.1.1.1\n" + "MapAddress *.net 2.2.2.2\n" + , sizeof(buf)); + config_get_lines(buf, &(get_options_mutable()->AddressMap), 0); + config_register_addressmaps(get_options()); + + strlcpy(address, "www.abc.com", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "www.abc.torserver.exit"); + + strlcpy(address, "www.def.com", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "www.def.torserver.exit"); + + strlcpy(address, "www.torproject.org", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "1.1.1.1"); + + strlcpy(address, "test.torproject.org", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "1.1.1.1"); + + strlcpy(address, "torproject.net", sizeof(address)); + test_assert(addressmap_rewrite(address, sizeof(address), &expires)); + test_streq(address, "2.2.2.2"); + + /* We don't support '*' as a mapping directive */ + addressmap_clear_configured(); + strlcpy(buf, "MapAddress * *.torserver.exit\n", sizeof(buf)); + config_get_lines(buf, &(get_options_mutable()->AddressMap), 0); + config_register_addressmaps(get_options()); + + strlcpy(address, "www.abc.com", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + + strlcpy(address, "www.def.net", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + + strlcpy(address, "www.torproject.org", sizeof(address)); + test_assert(!addressmap_rewrite(address, sizeof(address), &expires)); + +done: + ; +} + +#define CONFIG_TEST(name, flags) \ + { #name, test_config_ ## name, flags, NULL, NULL } + +struct testcase_t config_tests[] = { + CONFIG_TEST(addressmap, 0), + END_OF_TESTCASES +}; + diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 1b338a29a..cad8c2f55 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -7,6 +7,7 @@ #define CRYPTO_PRIVATE #include "or.h" #include "test.h" +#include "aes.h" /** Run unit tests for Diffie-Hellman functionality. */ static void @@ -95,13 +96,16 @@ test_crypto_rng(void) /** Run unit tests for our AES functionality */ static void -test_crypto_aes(void) +test_crypto_aes(void *arg) { char *data1 = NULL, *data2 = NULL, *data3 = NULL; crypto_cipher_env_t *env1 = NULL, *env2 = NULL; int i, j; char *mem_op_hex_tmp=NULL; + int use_evp = !strcmp(arg,"evp"); + evaluate_evp_for_aes(use_evp); + data1 = tor_malloc(1024); data2 = tor_malloc(1024); data3 = tor_malloc(1024); @@ -670,7 +674,7 @@ test_crypto_s2k(void) /** Test AES-CTR encryption and decryption with IV. */ static void -test_crypto_aes_iv(void) +test_crypto_aes_iv(void *arg) { crypto_cipher_env_t *cipher; char *plain, *encrypted1, *encrypted2, *decrypted1, *decrypted2; @@ -678,6 +682,9 @@ test_crypto_aes_iv(void) char key1[16], key2[16]; ssize_t encrypted_size, decrypted_size; + int use_evp = !strcmp(arg,"evp"); + evaluate_evp_for_aes(use_evp); + plain = tor_malloc(4095); encrypted1 = tor_malloc(4095 + 1 + 16); encrypted2 = tor_malloc(4095 + 1 + 16); @@ -851,18 +858,36 @@ test_crypto_base32_decode(void) ; } +static void * +pass_data_setup_fn(const struct testcase_t *testcase) +{ + return testcase->setup_data; +} +static int +pass_data_cleanup_fn(const struct testcase_t *testcase, void *ptr) +{ + (void)ptr; + (void)testcase; + return 1; +} +static const struct testcase_setup_t pass_data = { + pass_data_setup_fn, pass_data_cleanup_fn +}; + #define CRYPTO_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_crypto_ ## name } struct testcase_t crypto_tests[] = { CRYPTO_LEGACY(formats), CRYPTO_LEGACY(rng), - CRYPTO_LEGACY(aes), + { "aes_AES", test_crypto_aes, TT_FORK, &pass_data, (void*)"aes" }, + { "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(sha), CRYPTO_LEGACY(pk), CRYPTO_LEGACY(dh), CRYPTO_LEGACY(s2k), - CRYPTO_LEGACY(aes_iv), + { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" }, + { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(base32_decode), END_OF_TESTCASES }; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 205d7b577..046a1f25b 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -96,6 +96,8 @@ test_dir_formats(void) r1->cache_info.published_on = 0; r1->or_port = 9000; r1->dir_port = 9003; + tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::"); + r1->ipv6_orport = 9999; r1->onion_pkey = crypto_pk_dup_key(pk1); r1->identity_pkey = crypto_pk_dup_key(pk2); r1->bandwidthrate = 1000; @@ -141,6 +143,7 @@ test_dir_formats(void) test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0); strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n" + "or-address [1:2:3:4::]:9999\n" "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" @@ -620,13 +623,81 @@ test_dir_param_voting(void) test_eq(0, networkstatus_get_param(&vote4, "foobar", 0, -100, 8)); smartlist_add(votes, &vote1); + + /* Do the first tests without adding all the other votes, for + * networks without many dirauths. */ + + res = dirvote_compute_params(votes, 11, 6); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 2); + test_streq(res, ""); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 1); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-99"); + tor_free(res); + smartlist_add(votes, &vote2); + + res = dirvote_compute_params(votes, 11, 2); + test_streq(res, "ab=27 abcd=20 cw=5 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 2); + test_streq(res, "ab=27 cw=5 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 3); + test_streq(res, "ab=27 cw=5 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 6); + test_streq(res, ""); + tor_free(res); + smartlist_add(votes, &vote3); + + res = dirvote_compute_params(votes, 11, 3); + test_streq(res, "ab=27 abcd=20 c=60 cw=50 x-yz=-9 zzzzz=101"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 3); + test_streq(res, "ab=27 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 5); + test_streq(res, "cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 9); + test_streq(res, "cw=50 x-yz=-9"); + tor_free(res); + smartlist_add(votes, &vote4); - res = dirvote_compute_params(votes); - test_streq(res, - "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101"); + res = dirvote_compute_params(votes, 11, 4); + test_streq(res, "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 4); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 5); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + /* Test that the special-cased "at least three dirauths voted for + * this param" logic works as expected. */ + res = dirvote_compute_params(votes, 12, 6); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 10); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); done: tor_free(res); @@ -1049,7 +1120,7 @@ test_dir_v3_networkstatus(void) "Running:Stable:V2Dir:Valid"); tor_free(cp); cp = smartlist_join_strings(con->net_params, ":", 0, NULL); - test_streq(cp, "bar=2000000000:circuitwindow=80:foo=660"); + test_streq(cp, "circuitwindow=80:foo=660"); tor_free(cp); test_eq(4, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/ diff --git a/src/test/test_pt.c b/src/test/test_pt.c index f97b21fa0..45f441106 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -91,7 +91,7 @@ test_pt_protocol(void) { char line[200]; - managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t)); + managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t)); mp->conf_state = PT_PROTO_LAUNCHED; mp->transports = smartlist_create(); diff --git a/src/test/test_util.c b/src/test/test_util.c index 64bf52e7b..c762a8d7b 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1388,27 +1388,29 @@ run_util_spawn_background(const char *argv[], const char *expected_out, { int retval, exit_code; ssize_t pos; - process_handle_t process_handle; + process_handle_t *process_handle=NULL; char stdout_buf[100], stderr_buf[100]; + int status; /* Start the program */ #ifdef MS_WINDOWS - tor_spawn_background(NULL, argv, NULL, &process_handle); + status = tor_spawn_background(NULL, argv, NULL, &process_handle); #else - tor_spawn_background(argv[0], argv, NULL, &process_handle); + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); #endif - tt_int_op(process_handle.status, ==, expected_status); - - /* If the process failed to start, don't bother continuing */ - if (process_handle.status == PROCESS_STATUS_ERROR) + tt_int_op(status, ==, expected_status); + if (status == PROCESS_STATUS_ERROR) return; - tt_int_op(process_handle.stdout_pipe, >, 0); - tt_int_op(process_handle.stderr_pipe, >, 0); + tt_assert(process_handle != NULL); + tt_int_op(process_handle->status, ==, expected_status); + + tt_int_op(process_handle->stdout_pipe, >, 0); + tt_int_op(process_handle->stderr_pipe, >, 0); /* Check stdout */ - pos = tor_read_all_from_process_stdout(&process_handle, stdout_buf, + pos = tor_read_all_from_process_stdout(process_handle, stdout_buf, sizeof(stdout_buf) - 1); tt_assert(pos >= 0); stdout_buf[pos] = '\0'; @@ -1422,7 +1424,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out, // TODO: Make test-child exit with something other than 0 /* Check stderr */ - pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf, + pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, sizeof(stderr_buf) - 1); tt_assert(pos >= 0); stderr_buf[pos] = '\0'; @@ -1430,7 +1432,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out, tt_int_op(pos, ==, strlen(expected_err)); done: - ; + if (process_handle) + tor_process_handle_destroy(process_handle, 1); } /** Check that we can launch a process and read the output */ @@ -1489,7 +1492,8 @@ test_util_spawn_background_partial_read(void *ptr) int retval, exit_code; ssize_t pos = -1; - process_handle_t process_handle; + process_handle_t *process_handle=NULL; + int status; char stdout_buf[100], stderr_buf[100]; #ifdef MS_WINDOWS const char *argv[] = {"test-child.exe", "--test", NULL}; @@ -1510,21 +1514,23 @@ test_util_spawn_background_partial_read(void *ptr) /* Start the program */ #ifdef MS_WINDOWS - tor_spawn_background(NULL, argv, NULL, &process_handle); + status = tor_spawn_background(NULL, argv, NULL, &process_handle); #else - tor_spawn_background(argv[0], argv, NULL, &process_handle); + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); #endif - tt_int_op(process_handle.status, ==, expected_status); + tt_int_op(status, ==, expected_status); + tt_assert(process_handle); + tt_int_op(process_handle->status, ==, expected_status); /* Check stdout */ - for (expected_out_ctr =0; expected_out[expected_out_ctr] != NULL;) { + for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) { #ifdef MS_WINDOWS - pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, NULL); #else /* Check that we didn't read the end of file last time */ tt_assert(!eof); - pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, sizeof(stdout_buf) - 1, NULL, &eof); #endif log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos); @@ -1542,16 +1548,16 @@ test_util_spawn_background_partial_read(void *ptr) /* The process should have exited without writing more */ #ifdef MS_WINDOWS - pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, - &process_handle); + process_handle); tt_int_op(pos, ==, 0); #else if (!eof) { /* We should have got all the data, but maybe not the EOF flag */ - pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, sizeof(stdout_buf) - 1, - &process_handle, &eof); + process_handle, &eof); tt_int_op(pos, ==, 0); tt_assert(eof); } @@ -1566,7 +1572,7 @@ test_util_spawn_background_partial_read(void *ptr) // TODO: Make test-child exit with something other than 0 /* Check stderr */ - pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf, + pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, sizeof(stderr_buf) - 1); tt_assert(pos >= 0); stderr_buf[pos] = '\0'; @@ -1574,7 +1580,7 @@ test_util_spawn_background_partial_read(void *ptr) tt_int_op(pos, ==, strlen(expected_err)); done: - ; + tor_process_handle_destroy(process_handle, 1); } /** @@ -1654,7 +1660,7 @@ test_util_split_lines(void *ptr) int i, j; char *orig_line; - smartlist_t *sl; + smartlist_t *sl=NULL; (void)ptr; @@ -1683,10 +1689,12 @@ test_util_split_lines(void *ptr) tt_assert(tests[i].split_line[j] == NULL); tor_free(orig_line); smartlist_free(sl); + sl = NULL; } done: - ; + tor_free(orig_line); + smartlist_free(sl); } static void diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index 974a58bec..57f82b1bc 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -153,7 +153,7 @@ parse_commandline(int argc, char **argv) } months_lifetime = atoi(argv[++i]); if (months_lifetime > 24 || months_lifetime < 0) { - fprintf(stderr, "Lifetime (in months) was out of range."); + fprintf(stderr, "Lifetime (in months) was out of range.\n"); return 1; } } else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--reuse")) { |