diff options
Diffstat (limited to 'src')
81 files changed, 6395 insertions, 2071 deletions
diff --git a/src/common/address.c b/src/common/address.c index 42a116a91e..6bd107889a 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1504,47 +1504,22 @@ tor_addr_is_multicast(const tor_addr_t *a) return 0; } -/** Set *<b>addr</b> to the IP address (if any) of whatever interface - * connects to the Internet. This address should only be used in checking - * whether our address has changed. Return 0 on success, -1 on failure. +/** Attempt to retrieve IP address of current host by utilizing some + * UDP socket trickery. Only look for address of given <b>family</b>. + * Set result to *<b>addr</b>. Return 0 on success, -1 on failure. */ -MOCK_IMPL(int, -get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) +STATIC int +get_interface_address6_via_udp_socket_hack(int severity, + sa_family_t family, + tor_addr_t *addr) { - /* XXX really, this function should yield a smartlist of addresses. */ - smartlist_t *addrs; - int sock=-1, r=-1; struct sockaddr_storage my_addr, target_addr; + int sock=-1, r=-1; socklen_t addr_len; - tor_assert(addr); - - /* Try to do this the smart way if possible. */ - if ((addrs = get_interface_addresses_raw(severity))) { - int rv = -1; - SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) { - if (family != AF_UNSPEC && family != tor_addr_family(a)) - continue; - if (tor_addr_is_loopback(a) || - tor_addr_is_multicast(a)) - continue; - tor_addr_copy(addr, a); - rv = 0; - - /* If we found a non-internal address, declare success. Otherwise, - * keep looking. */ - if (!tor_addr_is_internal(a, 0)) - break; - } SMARTLIST_FOREACH_END(a); - - SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); - smartlist_free(addrs); - return rv; - } - - /* Okay, the smart way is out. */ memset(addr, 0, sizeof(tor_addr_t)); memset(&target_addr, 0, sizeof(target_addr)); + /* Don't worry: no packets are sent. We just need to use a real address * on the actual Internet. */ if (family == AF_INET6) { @@ -1566,6 +1541,7 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) } else { return -1; } + if (sock < 0) { int e = tor_socket_errno(-1); log_fn(severity, LD_NET, "unable to create socket: %s", @@ -1573,27 +1549,74 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) goto err; } - if (connect(sock,(struct sockaddr *)&target_addr, addr_len) < 0) { + if (tor_connect_socket(sock,(struct sockaddr *)&target_addr, + addr_len) < 0) { int e = tor_socket_errno(sock); log_fn(severity, LD_NET, "connect() failed: %s", tor_socket_strerror(e)); goto err; } - if (getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) { + if (tor_getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) { int e = tor_socket_errno(sock); log_fn(severity, LD_NET, "getsockname() to determine interface failed: %s", tor_socket_strerror(e)); goto err; } - tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL); - r=0; + if (tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL) == 0) { + if (tor_addr_is_loopback(addr) || tor_addr_is_multicast(addr)) { + log_fn(severity, LD_NET, "Address that we determined via UDP socket" + " magic is unsuitable for public comms."); + } else { + r=0; + } + } + err: if (sock >= 0) tor_close_socket(sock); return r; } +/** Set *<b>addr</b> to the IP address (if any) of whatever interface + * connects to the Internet. This address should only be used in checking + * whether our address has changed. Return 0 on success, -1 on failure. + */ +MOCK_IMPL(int, +get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr)) +{ + /* XXX really, this function should yield a smartlist of addresses. */ + smartlist_t *addrs; + tor_assert(addr); + + /* Try to do this the smart way if possible. */ + if ((addrs = get_interface_addresses_raw(severity))) { + int rv = -1; + SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) { + if (family != AF_UNSPEC && family != tor_addr_family(a)) + continue; + if (tor_addr_is_loopback(a) || + tor_addr_is_multicast(a)) + continue; + + tor_addr_copy(addr, a); + rv = 0; + + /* If we found a non-internal address, declare success. Otherwise, + * keep looking. */ + if (!tor_addr_is_internal(a, 0)) + break; + } SMARTLIST_FOREACH_END(a); + + SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a)); + smartlist_free(addrs); + return rv; + } + + /* Okay, the smart way is out. */ + return get_interface_address6_via_udp_socket_hack(severity,family,addr); +} + /* ====== * IPv4 helpers * XXXX024 IPv6 deprecate some of these. diff --git a/src/common/address.h b/src/common/address.h index df835e917a..cd80615f93 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -274,6 +274,9 @@ tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port); #ifdef ADDRESS_PRIVATE STATIC smartlist_t *get_interface_addresses_raw(int severity); +STATIC int get_interface_address6_via_udp_socket_hack(int severity, + sa_family_t family, + tor_addr_t *addr); #ifdef HAVE_IFADDRS_TO_SMARTLIST STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa); diff --git a/src/common/aes.c b/src/common/aes.c index 7651f1d93a..dbc12dcd23 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -25,18 +25,19 @@ #endif #include <openssl/opensslv.h> +#include "crypto.h" + +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0) +#error "We require OpenSSL >= 1.0.0" +#endif + #include <assert.h> #include <stdlib.h> #include <string.h> #include <openssl/aes.h> #include <openssl/evp.h> #include <openssl/engine.h> -#include "crypto.h" -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) -/* See comments about which counter mode implementation to use below. */ #include <openssl/modes.h> -#define CAN_USE_OPENSSL_CTR -#endif #include "compat.h" #include "aes.h" #include "util.h" @@ -189,11 +190,9 @@ struct aes_cnt_cipher { * we're testing it or because we have hardware acceleration configured */ static int should_use_EVP = 0; -#ifdef CAN_USE_OPENSSL_CTR /** True iff we have tested the counter-mode implementation and found that it * doesn't have the counter-mode bug from OpenSSL 1.0.0. */ static int should_use_openssl_CTR = 0; -#endif /** 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 @@ -235,7 +234,6 @@ evaluate_evp_for_aes(int force_val) int evaluate_ctr_for_aes(void) { -#ifdef CAN_USE_OPENSSL_CTR /* Result of encrypting an all-zero block with an all-zero 128-bit AES key. * This should be the same as encrypting an all-zero block with an all-zero * 128-bit AES key in counter mode, starting at position 0 of the stream. @@ -268,10 +266,6 @@ evaluate_ctr_for_aes(void) "mode; using it."); should_use_openssl_CTR = 1; } -#else - log_info(LD_CRYPTO, "This version of OpenSSL has a slow implementation of " - "counter mode; not using it."); -#endif return 0; } @@ -331,7 +325,7 @@ static void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) { if (should_use_EVP) { - const EVP_CIPHER *c; + const EVP_CIPHER *c = 0; switch (key_bits) { case 128: c = EVP_aes_128_ecb(); break; case 192: c = EVP_aes_192_ecb(); break; @@ -356,11 +350,9 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) cipher->pos = 0; -#ifdef CAN_USE_OPENSSL_CTR if (should_use_openssl_CTR) memset(cipher->buf, 0, sizeof(cipher->buf)); else -#endif aes_fill_buf_(cipher); } @@ -386,7 +378,6 @@ aes_cipher_free(aes_cnt_cipher_t *cipher) #define UPDATE_CTR_BUF(c, n) #endif -#ifdef CAN_USE_OPENSSL_CTR /* Helper function to use EVP with openssl's counter-mode wrapper. */ static void evp_block128_fn(const uint8_t in[16], @@ -397,7 +388,6 @@ evp_block128_fn(const uint8_t in[16], 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 @@ -407,7 +397,6 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, char *output) { -#ifdef CAN_USE_OPENSSL_CTR if (should_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 @@ -431,9 +420,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, &cipher->pos); } return; - } else -#endif - { + } else { int c = cipher->pos; if (PREDICT_UNLIKELY(!len)) return; @@ -466,13 +453,10 @@ 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) { -#ifdef CAN_USE_OPENSSL_CTR if (should_use_openssl_CTR) { aes_crypt(cipher, data, len, data); return; - } else -#endif - { + } else { int c = cipher->pos; if (PREDICT_UNLIKELY(!len)) return; @@ -512,11 +496,8 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) cipher->pos = 0; memcpy(cipher->ctr_buf.buf, iv, 16); -#ifdef CAN_USE_OPENSSL_CTR if (!should_use_openssl_CTR) -#endif aes_fill_buf_(cipher); } #endif - diff --git a/src/common/compat.c b/src/common/compat.c index 1788e32ee3..8da7ef3f69 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1156,12 +1156,20 @@ mark_socket_open(tor_socket_t s) /** @} */ /** As socket(), but counts the number of open sockets. */ -tor_socket_t -tor_open_socket(int domain, int type, int protocol) +MOCK_IMPL(tor_socket_t, +tor_open_socket,(int domain, int type, int protocol)) { return tor_open_socket_with_extensions(domain, type, protocol, 1, 0); } +/** Mockable wrapper for connect(). */ +MOCK_IMPL(tor_socket_t, +tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address, + socklen_t address_len)) +{ + return connect(socket,address,address_len); +} + /** As socket(), but creates a nonblocking socket and * counts the number of open sockets. */ tor_socket_t @@ -1308,6 +1316,14 @@ get_n_open_sockets(void) return n; } +/** Mockable wrapper for getsockname(). */ +MOCK_IMPL(int, +tor_getsockname,(tor_socket_t socket, struct sockaddr *address, + socklen_t *address_len)) +{ + return getsockname(socket, address, address_len); +} + /** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1 * on failure. */ diff --git a/src/common/compat.h b/src/common/compat.h index 11b41cded9..5189b7e056 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -463,7 +463,8 @@ int tor_close_socket(tor_socket_t s); tor_socket_t tor_open_socket_with_extensions( int domain, int type, int protocol, int cloexec, int nonblock); -tor_socket_t tor_open_socket(int domain, int type, int protocol); +MOCK_DECL(tor_socket_t, +tor_open_socket,(int domain, int type, int protocol)); tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol); tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len); @@ -474,8 +475,15 @@ tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr, socklen_t *len, int cloexec, int nonblock); +MOCK_DECL(tor_socket_t, +tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address, + socklen_t address_len)); int get_n_open_sockets(void); +MOCK_DECL(int, +tor_getsockname,(tor_socket_t socket, struct sockaddr *address, + socklen_t *address_len)); + #define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags) #define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags) diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 15308dd4cb..a366b6c9c6 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -56,11 +56,6 @@ typedef uint32_t le_version_t; * it is. */ #define LE_OTHER V(0,0,99) -#if 0 -static le_version_t tor_get_libevent_version(const char **v_out); -#endif - -#if defined(HAVE_EVENT_SET_LOG_CALLBACK) || defined(RUNNING_DOXYGEN) /** A string which, if it appears in a libevent log, should be ignored. */ static const char *suppress_msg = NULL; /** Callback function passed to event_set_log() so we can intercept @@ -107,17 +102,6 @@ suppress_libevent_log_msg(const char *msg) { suppress_msg = msg; } -#else -void -configure_libevent_logging(void) -{ -} -void -suppress_libevent_log_msg(const char *msg) -{ - (void)msg; -} -#endif #ifndef HAVE_EVENT2_EVENT_H /** Work-alike replacement for event_new() on pre-Libevent-2.0 systems. */ @@ -275,19 +259,11 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg) exit(1); } -#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD) /* Making this a NOTICE for now so we can link bugs to a libevent versions * or methods better. */ log_info(LD_GENERAL, "Initialized libevent version %s using method %s. Good.", event_get_version(), tor_libevent_get_method()); -#else - log_notice(LD_GENERAL, - "Initialized old libevent (version 1.0b or earlier)."); - log_warn(LD_GENERAL, - "You have a *VERY* old version of libevent. It is likely to be buggy; " - "please build Tor with a more recent version."); -#endif #ifdef USE_BUFFEREVENTS tor_libevent_set_tick_timeout(torcfg->msec_per_tick); @@ -301,27 +277,14 @@ tor_libevent_get_base, (void)) return the_event_base; } -#ifndef HAVE_EVENT_BASE_LOOPEXIT -/** Replacement for event_base_loopexit on some very old versions of Libevent - * that we are not yet brave enough to deprecate. */ -int -tor_event_base_loopexit(struct event_base *base, struct timeval *tv) -{ - tor_assert(base == the_event_base); - return event_loopexit(tv); -} -#endif - /** Return the name of the Libevent backend we're using. */ const char * tor_libevent_get_method(void) { #ifdef HAVE_EVENT2_EVENT_H return event_base_get_method(the_event_base); -#elif defined(HAVE_EVENT_GET_METHOD) - return event_get_method(); #else - return "<unknown>"; + return event_get_method(); #endif } @@ -376,54 +339,12 @@ le_versions_compatibility(le_version_t v) return 5; } -#if 0 -/** Return the version number of the currently running version of Libevent. - * See le_version_t for info on the format. - */ -static le_version_t -tor_get_libevent_version(const char **v_out) -{ - const char *v; - le_version_t r; -#if defined(HAVE_EVENT_GET_VERSION_NUMBER) - v = event_get_version(); - r = event_get_version_number(); -#elif defined (HAVE_EVENT_GET_VERSION) - v = event_get_version(); - r = tor_decode_libevent_version(v); -#else - v = "pre-1.0c"; - r = LE_OLD; -#endif - if (v_out) - *v_out = v; - return r; -} -#endif - /** Return a string representation of the version of the currently running * version of Libevent. */ const char * tor_libevent_get_version_str(void) { -#ifdef HAVE_EVENT_GET_VERSION return event_get_version(); -#else - return "pre-1.0c"; -#endif -} - -/** - * Compare the current Libevent method and version to a list of versions - * which are known not to work. Warn the user as appropriate. - */ -void -tor_check_libevent_version(const char *m, int server, - const char **badness_out) -{ - (void) m; - (void) server; - *badness_out = NULL; } #if defined(LIBEVENT_VERSION) @@ -452,7 +373,7 @@ tor_check_libevent_header_compatibility(void) /* In libevent versions before 2.0, it's hard to keep binary compatibility * between upgrades, and unpleasant to detect when the version we compiled * against is unlike the version we have linked against. Here's how. */ -#if defined(HEADER_VERSION) && defined(HAVE_EVENT_GET_VERSION) +#if defined(HEADER_VERSION) /* We have a header-file version and a function-call version. Easy. */ if (strcmp(HEADER_VERSION, event_get_version())) { le_version_t v1, v2; @@ -474,7 +395,7 @@ tor_check_libevent_header_compatibility(void) else log_info(LD_GENERAL, "I think these versions are binary-compatible."); } -#elif defined(HAVE_EVENT_GET_VERSION) +#else /* event_get_version but no _EVENT_VERSION. We might be in 1.4.0-beta or earlier, where that's normal. To see whether we were compiled with an earlier version, let's see whether the struct event defines MIN_HEAP_IDX. @@ -504,9 +425,6 @@ tor_check_libevent_header_compatibility(void) } #endif -#elif defined(HEADER_VERSION) -#warn "_EVENT_VERSION is defined but not get_event_version(): Libevent is odd." -#else /* Your libevent is ancient. */ #endif } diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 6bbfae0056..39181efb7b 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -52,12 +52,7 @@ periodic_timer_t *periodic_timer_new(struct event_base *base, void *data); void periodic_timer_free(periodic_timer_t *); -#ifdef HAVE_EVENT_BASE_LOOPEXIT #define tor_event_base_loopexit event_base_loopexit -#else -struct timeval; -int tor_event_base_loopexit(struct event_base *base, struct timeval *tv); -#endif /** Defines a configuration for using libevent with Tor: passed as an argument * to tor_libevent_initialize() to describe how we want to set up. */ @@ -74,8 +69,6 @@ typedef struct tor_libevent_cfg { void tor_libevent_initialize(tor_libevent_cfg *cfg); MOCK_DECL(struct event_base *, tor_libevent_get_base, (void)); const char *tor_libevent_get_method(void); -void tor_check_libevent_version(const char *m, int server, - const char **badness_out); void tor_check_libevent_header_compatibility(void); const char *tor_libevent_get_version_str(void); const char *tor_libevent_get_header_version_str(void); diff --git a/src/common/compat_pthreads.c b/src/common/compat_pthreads.c index 246076b276..fdc504690b 100644 --- a/src/common/compat_pthreads.c +++ b/src/common/compat_pthreads.c @@ -91,10 +91,9 @@ static pthread_mutexattr_t attr_recursive; void tor_mutex_init(tor_mutex_t *mutex) { - int err; if (PREDICT_UNLIKELY(!threads_initialized)) tor_threads_init(); - err = pthread_mutex_init(&mutex->mutex, &attr_recursive); + const int err = pthread_mutex_init(&mutex->mutex, &attr_recursive); if (PREDICT_UNLIKELY(err)) { log_err(LD_GENERAL, "Error %d creating a mutex.", err); tor_fragile_assert(); @@ -278,12 +277,14 @@ tor_threads_init(void) if (!threads_initialized) { pthread_mutexattr_init(&attr_recursive); pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE); - tor_assert(0==pthread_attr_init(&attr_detached)); + const int ret1 = pthread_attr_init(&attr_detached); + tor_assert(ret1 == 0); #ifndef PTHREAD_CREATE_DETACHED #define PTHREAD_CREATE_DETACHED 1 #endif - tor_assert(0==pthread_attr_setdetachstate(&attr_detached, - PTHREAD_CREATE_DETACHED)); + const int ret2 = + pthread_attr_setdetachstate(&attr_detached, PTHREAD_CREATE_DETACHED); + tor_assert(ret2 == 0); threads_initialized = 1; set_main_thread(); } diff --git a/src/common/crypto.c b/src/common/crypto.c index 218c7bea1e..fcd862f045 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -24,13 +24,21 @@ #undef OCSP_RESPONSE #endif +#include <openssl/opensslv.h> + +#define CRYPTO_PRIVATE +#include "crypto.h" + +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0) +#error "We require OpenSSL >= 1.0.0" +#endif + #include <openssl/err.h> #include <openssl/rsa.h> #include <openssl/pem.h> #include <openssl/evp.h> #include <openssl/engine.h> #include <openssl/rand.h> -#include <openssl/opensslv.h> #include <openssl/bn.h> #include <openssl/dh.h> #include <openssl/conf.h> @@ -49,19 +57,13 @@ #include <sys/fcntl.h> #endif -#define CRYPTO_PRIVATE -#include "crypto.h" -#include "../common/torlog.h" +#include "torlog.h" #include "aes.h" -#include "../common/util.h" +#include "util.h" #include "container.h" #include "compat.h" #include "sandbox.h" -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) -#error "We require OpenSSL >= 0.9.8" -#endif - #ifdef ANDROID /* Android's OpenSSL seems to have removed all of its Engine support. */ #define DISABLE_ENGINES @@ -300,16 +302,9 @@ crypto_early_init(void) SSLeay(), SSLeay_version(SSLEAY_VERSION)); } - if (SSLeay() < OPENSSL_V_SERIES(1,0,0)) { - log_notice(LD_CRYPTO, - "Your OpenSSL version seems to be %s. We recommend 1.0.0 " - "or later.", - crypto_openssl_get_version_str()); - } - crypto_force_rand_ssleay(); - if (crypto_seed_rng(1) < 0) + if (crypto_seed_rng() < 0) return -1; if (crypto_init_siphash_key() < 0) return -1; @@ -391,7 +386,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) } if (crypto_force_rand_ssleay()) { - if (crypto_seed_rng(1) < 0) + if (crypto_seed_rng() < 0) return -1; } @@ -405,7 +400,11 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) void crypto_thread_cleanup(void) { +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + ERR_remove_thread_state(NULL); +#else ERR_remove_state(0); +#endif } /** used by tortls.c: wrap an RSA* in a crypto_pk_t. */ @@ -1397,6 +1396,78 @@ crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) return 0; } +/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the + * Base64 encoding of the DER representation of the private key as a NUL + * terminated string, and return it via <b>priv_out</b>. Return 0 on + * sucess, -1 on failure. + * + * It is the caller's responsibility to sanitize and free the resulting buffer. + */ +int +crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out) +{ + unsigned char *der = NULL; + int der_len; + int ret = -1; + + *priv_out = NULL; + + der_len = i2d_RSAPrivateKey(pk->key, &der); + if (der_len < 0 || der == NULL) + return ret; + + size_t priv_len = base64_encode_size(der_len, 0) + 1; + char *priv = tor_malloc_zero(priv_len); + if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) { + *priv_out = priv; + ret = 0; + } else { + tor_free(priv); + } + + memwipe(der, 0, der_len); + OPENSSL_free(der); + return ret; +} + +/** Given a string containing the Base64 encoded DER representation of the + * private key <b>str</b>, decode and return the result on success, or NULL + * on failure. + */ +crypto_pk_t * +crypto_pk_base64_decode(const char *str, size_t len) +{ + crypto_pk_t *pk = NULL; + + char *der = tor_malloc_zero(len + 1); + int der_len = base64_decode(der, len, str, len); + if (der_len <= 0) { + log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64)."); + goto out; + } + + const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */ + RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len); + if (!rsa) { + crypto_log_errors(LOG_WARN, "decoding private key"); + goto out; + } + + pk = crypto_new_pk_from_rsa_(rsa); + + /* Make sure it's valid. */ + if (crypto_pk_check_key(pk) <= 0) { + crypto_pk_free(pk); + pk = NULL; + goto out; + } + + out: + memwipe(der, 0, len + 1); + tor_free(der); + return pk; +} + /* symmetric crypto */ /** Return a pointer to the key set for the cipher in <b>env</b>. @@ -1768,235 +1839,12 @@ 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_new(); - tor_assert(dh_parameters); - - r = DH_generate_parameters_ex(dh_parameters, - DH_BYTES*8, DH_GENERATOR, NULL); - tor_assert(r == 0); - - 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; - char *base64_encoded_dh = NULL; - char *file_string = NULL; - int retval = -1; - static const char file_header[] = "# This file contains stored Diffie-" - "Hellman parameters for future use.\n# You *do not* need to edit this " - "file.\n\n"; - - 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, &dh_string_repr); - if ((len < 0) || (dh_string_repr == NULL)) { - log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2)."); - goto done; - } - - base64_encoded_dh = tor_calloc(len, 2); /* should be enough */ - new_len = base64_encode(base64_encoded_dh, len * 2, - (char *)dh_string_repr, len); - if (new_len < 0) { - log_warn(LD_CRYPTO, "Error occured while base64-encoding DH modulus."); - goto done; - } - - /* concatenate file header and the dh parameters blob */ - new_len = tor_asprintf(&file_string, "%s%s", file_header, base64_encoded_dh); - - /* write to file */ - if (write_bytes_to_new_file(fname, file_string, new_len, 0) < 0) { - log_info(LD_CRYPTO, "'%s' was already occupied.", fname); - goto done; - } - - retval = 0; - - done: - if (dh) - DH_free(dh); - if (dh_string_repr) - OPENSSL_free(dh_string_repr); - tor_free(base64_encoded_dh); - tor_free(file_string); - - 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; - const char *contents_tmp = NULL; - int dh_codes; - 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.*/ - } - - /* skip the file header */ - contents_tmp = eat_whitespace(contents); - if (!*contents_tmp) { - log_warn(LD_CRYPTO, "Stored dynamic DH modulus file " - "seems corrupted (eat_whitespace)."); - goto err; - } - - /* 'fname' contains the DH parameters stored in base64-ed DER - * format. We are only interested in the DH modulus. - * NOTE: We allocate more storage here than we need. Since we're already - * doing that, we can also add 1 byte extra to appease Coverity's - * scanner. */ - - cp = base64_decoded_dh = tor_malloc_zero(strlen(contents_tmp) + 1); - length = base64_decode((char *)base64_decoded_dh, strlen(contents_tmp), - contents_tmp, strlen(contents_tmp)); - 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 */ - char *fname_new=NULL; - tor_asprintf(&fname_new, "%s.broken", fname); - - 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 +/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH * modulus. */ void -crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname) +crypto_set_tls_dh_prime(void) { BIGNUM *tls_prime = NULL; - int store_dh_prime_afterwards = 0; int r; /* If the space is occupied, free the previous TLS DH prime */ @@ -2005,44 +1853,24 @@ crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname) 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); + 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); - } + /* 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 @@ -2079,10 +1907,8 @@ init_dh_param(void) dh_param_p = circuit_dh_prime; dh_param_g = generator; - /* Ensure that we have TLS DH parameters set up, too, even if we're - going to change them soon. */ if (!dh_param_p_tls) { - crypto_set_tls_dh_prime(NULL); + crypto_set_tls_dh_prime(); } } @@ -2419,15 +2245,6 @@ crypto_dh_free(crypto_dh_t *dh) * work for us too. */ #define ADD_ENTROPY 32 -/** True iff it's safe to use RAND_poll after setup. - * - * Versions of OpenSSL prior to 0.9.7k and 0.9.8c had a bug where RAND_poll - * would allocate an fd_set on the stack, open a new file, and try to FD_SET - * that fd without checking whether it fit in the fd_set. Thus, if the - * system has not just been started up, it is unsafe to call */ -#define RAND_POLL_IS_SAFE \ - (OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,8,'c')) - /** Set the seed of the weak RNG to a random value. */ void crypto_seed_weak_rng(tor_weak_rng_t *rng) @@ -2497,7 +2314,7 @@ crypto_strongest_rand(uint8_t *out, size_t out_len) * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure. */ int -crypto_seed_rng(int startup) +crypto_seed_rng(void) { int rand_poll_ok = 0, load_entropy_ok = 0; uint8_t buf[ADD_ENTROPY]; @@ -2505,11 +2322,9 @@ crypto_seed_rng(int startup) /* OpenSSL has a RAND_poll function that knows about more kinds of * entropy than we do. We'll try calling that, *and* calling our own entropy * functions. If one succeeds, we'll accept the RNG as seeded. */ - if (startup || RAND_POLL_IS_SAFE) { - rand_poll_ok = RAND_poll(); - if (rand_poll_ok == 0) - log_warn(LD_CRYPTO, "RAND_poll() failed."); - } + rand_poll_ok = RAND_poll(); + if (rand_poll_ok == 0) + log_warn(LD_CRYPTO, "RAND_poll() failed."); load_entropy_ok = !crypto_strongest_rand(buf, sizeof(buf)); if (load_entropy_ok) { @@ -2562,8 +2377,40 @@ crypto_rand_int(unsigned int max) } } +/** Return a pseudorandom integer, chosen uniformly from the values <i>i</i> + * such that <b>min</b> <= <i>i</i> < <b>max</b>. + * + * <b>min</b> MUST be in range [0, <b>max</b>). + * <b>max</b> MUST be in range (min, INT_MAX]. + */ +int +crypto_rand_int_range(unsigned int min, unsigned int max) +{ + tor_assert(min < max); + tor_assert(max <= INT_MAX); + + /* The overflow is avoided here because crypto_rand_int() returns a value + * between 0 and (max - min) inclusive. */ + return min + crypto_rand_int(max - min); +} + +/** As crypto_rand_int_range, but supports uint64_t. */ +uint64_t +crypto_rand_uint64_range(uint64_t min, uint64_t max) +{ + tor_assert(min < max); + return min + crypto_rand_uint64(max - min); +} + +/** As crypto_rand_int_range, but supports time_t. */ +time_t +crypto_rand_time_range(time_t min, time_t max) +{ + return (time_t) crypto_rand_uint64_range(min, max); +} + /** Return a pseudorandom 64-bit integer, chosen uniformly from the values - * between 0 and <b>max</b>-1. */ + * between 0 and <b>max</b>-1 inclusive. */ uint64_t crypto_rand_uint64(uint64_t max) { @@ -2624,7 +2471,7 @@ crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix, if (min_rand_len > max_rand_len) min_rand_len = max_rand_len; - randlen = min_rand_len + crypto_rand_int(max_rand_len - min_rand_len + 1); + randlen = crypto_rand_int_range(min_rand_len, max_rand_len+1); prefixlen = strlen(prefix); resultlen = prefixlen + strlen(suffix) + randlen + 16; @@ -2671,36 +2518,163 @@ smartlist_shuffle(smartlist_t *sl) } } +#define BASE64_OPENSSL_LINELEN 64 + +/** Return the Base64 encoded size of <b>srclen</b> bytes of data in + * bytes. + * + * If <b>flags</b>&BASE64_ENCODE_MULTILINE is true, return the size + * of the encoded output as multiline output (64 character, `\n' terminated + * lines). + */ +size_t +base64_encode_size(size_t srclen, int flags) +{ + size_t enclen; + tor_assert(srclen < INT_MAX); + + if (srclen == 0) + return 0; + + enclen = ((srclen - 1) / 3) * 4 + 4; + if (flags & BASE64_ENCODE_MULTILINE) { + size_t remainder = enclen % BASE64_OPENSSL_LINELEN; + enclen += enclen / BASE64_OPENSSL_LINELEN; + if (remainder) + enclen++; + } + tor_assert(enclen < INT_MAX && enclen > srclen); + return enclen; +} + +/** Internal table mapping 6 bit values to the Base64 alphabet. */ +static const char base64_encode_table[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' +}; + /** Base64 encode <b>srclen</b> bytes of data from <b>src</b>. Write * the result into <b>dest</b>, if it will fit within <b>destlen</b> - * bytes. Return the number of bytes written on success; -1 if + * bytes. Return the number of bytes written on success; -1 if * destlen is too short, or other failure. + * + * If <b>flags</b>&BASE64_ENCODE_MULTILINE is true, return encoded + * output in multiline format (64 character, `\n' terminated lines). */ int -base64_encode(char *dest, size_t destlen, const char *src, size_t srclen) -{ - /* FFFF we might want to rewrite this along the lines of base64_decode, if - * it ever shows up in the profile. */ - EVP_ENCODE_CTX ctx; - int len, ret; - tor_assert(srclen < INT_MAX); +base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, + int flags) +{ + const unsigned char *usrc = (unsigned char *)src; + const unsigned char *eous = usrc + srclen; + char *d = dest; + uint32_t n = 0; + size_t linelen = 0; + size_t enclen; + int n_idx = 0; + + if (!src || !dest) + return -1; - /* 48 bytes of input -> 64 bytes of output plus newline. - Plus one more byte, in case I'm wrong. - */ - if (destlen < ((srclen/48)+1)*66) + /* Ensure that there is sufficient space, including the NUL. */ + enclen = base64_encode_size(srclen, flags); + if (destlen < enclen + 1) return -1; if (destlen > SIZE_T_CEILING) return -1; + if (enclen > INT_MAX) + return -1; - EVP_EncodeInit(&ctx); - EVP_EncodeUpdate(&ctx, (unsigned char*)dest, &len, - (unsigned char*)src, (int)srclen); - EVP_EncodeFinal(&ctx, (unsigned char*)(dest+len), &ret); - ret += len; - return ret; + memset(dest, 0, enclen); + + /* XXX/Yawning: If this ends up being too slow, this can be sped up + * by separating the multiline format case and the normal case, and + * processing 48 bytes of input at a time when newlines are desired. + */ +#define ENCODE_CHAR(ch) \ + STMT_BEGIN \ + *d++ = ch; \ + if (flags & BASE64_ENCODE_MULTILINE) { \ + if (++linelen % BASE64_OPENSSL_LINELEN == 0) { \ + linelen = 0; \ + *d++ = '\n'; \ + } \ + } \ + STMT_END + +#define ENCODE_N(idx) \ + ENCODE_CHAR(base64_encode_table[(n >> ((3 - idx) * 6)) & 0x3f]) + +#define ENCODE_PAD() ENCODE_CHAR('=') + + /* Iterate over all the bytes in src. Each one will add 8 bits to the + * value we're encoding. Accumulate bits in <b>n</b>, and whenever we + * have 24 bits, batch them into 4 bytes and flush those bytes to dest. + */ + for ( ; usrc < eous; ++usrc) { + n = (n << 8) | *usrc; + if ((++n_idx) == 3) { + ENCODE_N(0); + ENCODE_N(1); + ENCODE_N(2); + ENCODE_N(3); + n_idx = 0; + n = 0; + } + } + switch (n_idx) { + case 0: + /* 0 leftover bits, no pading to add. */ + break; + case 1: + /* 8 leftover bits, pad to 12 bits, write the 2 6-bit values followed + * by 2 padding characters. + */ + n <<= 4; + ENCODE_N(2); + ENCODE_N(3); + ENCODE_PAD(); + ENCODE_PAD(); + break; + case 2: + /* 16 leftover bits, pad to 18 bits, write the 3 6-bit values followed + * by 1 padding character. + */ + n <<= 2; + ENCODE_N(1); + ENCODE_N(2); + ENCODE_N(3); + ENCODE_PAD(); + break; + default: + /* Something went catastrophically wrong. */ + tor_fragile_assert(); + return -1; + } + +#undef ENCODE_N +#undef ENCODE_PAD +#undef ENCODE_CHAR + + /* Multiline output always includes at least one newline. */ + if (flags & BASE64_ENCODE_MULTILINE && linelen != 0) + *d++ = '\n'; + + tor_assert(d - dest == (ptrdiff_t)enclen); + + *d++ = '\0'; /* NUL terminate the output. */ + + return (int) enclen; } +#undef BASE64_OPENSSL_LINELEN + /** @{ */ /** Special values used for the base64_decode_table */ #define X 255 @@ -2745,26 +2719,6 @@ static const uint8_t base64_decode_table[256] = { int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen) { -#ifdef USE_OPENSSL_BASE64 - EVP_ENCODE_CTX ctx; - int len, ret; - /* 64 bytes of input -> *up to* 48 bytes of output. - Plus one more byte, in case I'm wrong. - */ - if (destlen < ((srclen/64)+1)*49) - return -1; - if (destlen > SIZE_T_CEILING) - return -1; - - memset(dest, 0, destlen); - - EVP_DecodeInit(&ctx); - EVP_DecodeUpdate(&ctx, (unsigned char*)dest, &len, - (unsigned char*)src, srclen); - EVP_DecodeFinal(&ctx, (unsigned char*)dest, &ret); - ret += len; - return ret; -#else const char *eos = src+srclen; uint32_t n=0; int n_idx=0; @@ -2835,20 +2789,19 @@ base64_decode(char *dest, size_t destlen, const char *src, size_t srclen) tor_assert((dest-dest_orig) <= INT_MAX); return (int)(dest-dest_orig); -#endif } #undef X #undef SP #undef PAD /** Base64 encode DIGEST_LINE bytes from <b>digest</b>, remove the trailing = - * and newline characters, and store the nul-terminated result in the first + * characters, and store the nul-terminated result in the first * BASE64_DIGEST_LEN+1 bytes of <b>d64</b>. */ int digest_to_base64(char *d64, const char *digest) { char buf[256]; - base64_encode(buf, sizeof(buf), digest, DIGEST_LEN); + base64_encode(buf, sizeof(buf), digest, DIGEST_LEN, 0); buf[BASE64_DIGEST_LEN] = '\0'; memcpy(d64, buf, BASE64_DIGEST_LEN+1); return 0; @@ -2860,33 +2813,20 @@ digest_to_base64(char *d64, const char *digest) int digest_from_base64(char *digest, const char *d64) { -#ifdef USE_OPENSSL_BASE64 - char buf_in[BASE64_DIGEST_LEN+3]; - char buf[256]; - if (strlen(d64) != BASE64_DIGEST_LEN) - return -1; - memcpy(buf_in, d64, BASE64_DIGEST_LEN); - memcpy(buf_in+BASE64_DIGEST_LEN, "=\n\0", 3); - if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST_LEN) - return -1; - memcpy(digest, buf, DIGEST_LEN); - return 0; -#else if (base64_decode(digest, DIGEST_LEN, d64, strlen(d64)) == DIGEST_LEN) return 0; else return -1; -#endif } /** Base64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the - * trailing = and newline characters, and store the nul-terminated result in - * the first BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */ + * trailing = characters, and store the nul-terminated result in the first + * BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */ int digest256_to_base64(char *d64, const char *digest) { char buf[256]; - base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN); + base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN, 0); buf[BASE64_DIGEST256_LEN] = '\0'; memcpy(d64, buf, BASE64_DIGEST256_LEN+1); return 0; @@ -2898,23 +2838,10 @@ digest256_to_base64(char *d64, const char *digest) int digest256_from_base64(char *digest, const char *d64) { -#ifdef USE_OPENSSL_BASE64 - char buf_in[BASE64_DIGEST256_LEN+3]; - char buf[256]; - if (strlen(d64) != BASE64_DIGEST256_LEN) - return -1; - memcpy(buf_in, d64, BASE64_DIGEST256_LEN); - memcpy(buf_in+BASE64_DIGEST256_LEN, "=\n\0", 3); - if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST256_LEN) - return -1; - memcpy(digest, buf, DIGEST256_LEN); - return 0; -#else if (base64_decode(digest, DIGEST256_LEN, d64, strlen(d64)) == DIGEST256_LEN) return 0; else return -1; -#endif } /** Implements base32 encoding as in RFC 4648. Limitation: Requires @@ -3119,13 +3046,11 @@ openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v, tor_free(v); } -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) static void tor_set_openssl_thread_id(CRYPTO_THREADID *threadid) { CRYPTO_THREADID_set_numeric(threadid, tor_get_thread_id()); } -#endif /** @{ */ /** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being @@ -3140,11 +3065,7 @@ setup_openssl_threading(void) for (i=0; i < n; ++i) openssl_mutexes_[i] = tor_mutex_new(); CRYPTO_set_locking_callback(openssl_locking_cb_); -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0) - CRYPTO_set_id_callback(tor_get_thread_id); -#else CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id); -#endif CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_); CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_); CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_); @@ -3157,7 +3078,11 @@ int crypto_global_cleanup(void) { EVP_cleanup(); +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + ERR_remove_thread_state(NULL); +#else ERR_remove_state(0); +#endif ERR_free_strings(); if (dh_param_p) diff --git a/src/common/crypto.h b/src/common/crypto.h index d305bc17a0..05572f4565 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -122,8 +122,7 @@ int crypto_global_cleanup(void); crypto_pk_t *crypto_pk_new(void); void crypto_pk_free(crypto_pk_t *env); -void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname); - +void crypto_set_tls_dh_prime(void); crypto_cipher_t *crypto_cipher_new(const char *key); crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv); void crypto_cipher_free(crypto_cipher_t *env); @@ -185,6 +184,9 @@ int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out); int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space); int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out); +int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out); +crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len); + /* symmetric crypto */ const char *crypto_cipher_get_key(crypto_cipher_t *env); @@ -251,10 +253,13 @@ int crypto_expand_key_material_rfc5869_sha256( uint8_t *key_out, size_t key_out_len); /* random numbers */ -int crypto_seed_rng(int startup); +int crypto_seed_rng(void); MOCK_DECL(int,crypto_rand,(char *to, size_t n)); int crypto_strongest_rand(uint8_t *out, size_t out_len); int crypto_rand_int(unsigned int max); +int crypto_rand_int_range(unsigned int min, unsigned int max); +uint64_t crypto_rand_uint64_range(uint64_t min, uint64_t max); +time_t crypto_rand_time_range(time_t min, time_t max); uint64_t crypto_rand_uint64(uint64_t max); double crypto_rand_double(void); struct tor_weak_rng_t; @@ -268,7 +273,10 @@ struct smartlist_t; void *smartlist_choose(const struct smartlist_t *sl); void smartlist_shuffle(struct smartlist_t *sl); -int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen); +#define BASE64_ENCODE_MULTILINE 1 +size_t base64_encode_size(size_t srclen, int flags); +int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, + int flags); int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen); /** Characters that can appear (case-insensitively) in a base32 encoding. */ #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c index 00e0e9ea85..35a7854404 100644 --- a/src/common/crypto_format.c +++ b/src/common/crypto_format.c @@ -19,7 +19,7 @@ curve25519_public_to_base64(char *output, { char buf[128]; base64_encode(buf, sizeof(buf), - (const char*)pkey->public_key, CURVE25519_PUBKEY_LEN); + (const char*)pkey->public_key, CURVE25519_PUBKEY_LEN, 0); buf[CURVE25519_BASE64_PADDED_LEN] = '\0'; memcpy(output, buf, CURVE25519_BASE64_PADDED_LEN+1); return 0; diff --git a/src/common/include.am b/src/common/include.am index 5b63392541..b782310663 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -11,9 +11,7 @@ noinst_LIBRARIES += \ src/common/libor-event-testing.a endif -EXTRA_DIST+= \ - src/common/common_sha1.i \ - src/common/Makefile.nmake +EXTRA_DIST += src/common/Makefile.nmake #CFLAGS = -Wall -Wpointer-arith -O2 AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel @@ -63,7 +61,6 @@ LIBOR_A_SOURCES = \ src/common/log.c \ src/common/memarea.c \ src/common/util.c \ - src/common/util_codedigest.c \ src/common/util_process.c \ src/common/sandbox.c \ src/common/workqueue.c \ @@ -72,6 +69,8 @@ LIBOR_A_SOURCES = \ $(libor_extra_source) \ $(threads_impl_source) +src/common/log.o: micro-revision.i + LIBOR_CRYPTO_A_SOURCES = \ src/common/aes.c \ src/common/crypto.c \ @@ -96,9 +95,9 @@ src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES) src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES) src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES) -src_common_libor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) -src_common_libor_crypto_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) -src_common_libor_event_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_common_libor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_common_libor_crypto_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_common_libor_event_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_common_libor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_common_libor_crypto_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) @@ -133,17 +132,3 @@ COMMONHEADERS = \ noinst_HEADERS+= $(COMMONHEADERS) -DISTCLEANFILES+= src/common/common_sha1.i - -src/common/common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(COMMONHEADERS) - $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(src_common_libor_SOURCES) $(src_common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > $@; \ - elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_common_libor_SOURCES) $(src_Common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > $@; \ - else \ - rm $@; \ - touch $@; \ - fi - -src/common/util_codedigest.o: src/common/common_sha1.i - diff --git a/src/common/log.c b/src/common/log.c index e8cc30c312..4ad0bc3697 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -140,6 +140,9 @@ static size_t pending_startup_messages_len; * configured. */ static int queue_startup_messages = 1; +/** True iff __PRETTY_FUNCTION__ includes parenthesized arguments. */ +static int pretty_fn_has_parens = 0; + /** Don't store more than this many bytes of messages while waiting for the * logs to get configured. */ #define MAX_STARTUP_MSG_LEN (1<<16) @@ -263,6 +266,13 @@ log_tor_version(logfile_t *lf, int reset) return 0; } +const char bug_suffix[] = " (on Tor " VERSION +#ifndef _MSC_VER + " " +#include "micro-revision.i" +#endif + ")"; + /** Helper: Format a log message into a fixed-sized buffer. (This is * factored out of <b>logv</b> so that we never format a message more * than once.) Return a pointer to the first character of the message @@ -306,7 +316,9 @@ format_msg(char *buf, size_t buf_len, } if (funcname && should_log_function_name(domain, severity)) { - r = tor_snprintf(buf+n, buf_len-n, "%s(): ", funcname); + r = tor_snprintf(buf+n, buf_len-n, + pretty_fn_has_parens ? "%s: " : "%s(): ", + funcname); if (r<0) n = strlen(buf); else @@ -341,6 +353,13 @@ format_msg(char *buf, size_t buf_len, } } } + + if (domain == LD_BUG && + buf_len - n > strlen(bug_suffix)+1) { + memcpy(buf+n, bug_suffix, strlen(bug_suffix)); + n += strlen(bug_suffix); + } + buf[n]='\n'; buf[n+1]='\0'; *msg_len_out = n+1; @@ -925,6 +944,11 @@ init_logging(int disable_startup_queue) tor_mutex_init(&log_mutex); log_mutex_initialized = 1; } +#ifdef __GNUC__ + if (strchr(__PRETTY_FUNCTION__, '(')) { + pretty_fn_has_parens = 1; + } +#endif if (pending_cb_messages == NULL) pending_cb_messages = smartlist_new(); if (disable_startup_queue) diff --git a/src/common/tortls.c b/src/common/tortls.c index 97a82bf6e1..2b8daaaf06 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -43,13 +43,22 @@ #pragma GCC diagnostic ignored "-Wredundant-decls" #endif +#include <openssl/opensslv.h> +#include "crypto.h" + +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0) +#error "We require OpenSSL >= 1.0.0" +#endif +#ifdef OPENSSL_NO_EC +#error "We require OpenSSL with ECC support" +#endif + #include <openssl/ssl.h> #include <openssl/ssl3.h> #include <openssl/err.h> #include <openssl/tls1.h> #include <openssl/asn1.h> #include <openssl/bio.h> -#include <openssl/opensslv.h> #include <openssl/bn.h> #include <openssl/rsa.h> @@ -68,17 +77,12 @@ #include "compat_libevent.h" #endif -#include "crypto.h" #include "tortls.h" #include "util.h" #include "torlog.h" #include "container.h" #include <string.h> -#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8) -#error "We require OpenSSL >= 0.9.8" -#endif - /* Enable the "v2" TLS handshake. */ #define V2_HANDSHAKE_SERVER @@ -93,10 +97,8 @@ #define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer") -#if (OPENSSL_VERSION_NUMBER < OPENSSL_V(0,9,8,'s') || \ - (OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(0,9,9) && \ - OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f'))) -/* This is a version of OpenSSL before 0.9.8s/1.0.0f. It does not have +#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') +/* This is a version of OpenSSL before 1.0.0f. It does not have * the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and * SSL3 safely at the same time. */ @@ -114,13 +116,6 @@ #define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010 #endif -/** Does the run-time openssl version look like we need - * SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */ -static int use_unsafe_renegotiation_op = 0; -/** Does the run-time openssl version look like we need - * SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */ -static int use_unsafe_renegotiation_flag = 0; - /** Structure that we use for a single certificate. */ struct tor_cert_t { X509 *cert; @@ -479,65 +474,13 @@ tor_tls_init(void) check_no_tls_errors(); if (!tls_library_is_initialized) { - long version; SSL_library_init(); SSL_load_error_strings(); - version = SSLeay(); - - /* OpenSSL 0.9.8l introduced SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION - * here, but without thinking too hard about it: it turns out that the - * flag in question needed to be set at the last minute, and that it - * conflicted with an existing flag number that had already been added - * in the OpenSSL 1.0.0 betas. OpenSSL 0.9.8m thoughtfully replaced - * the flag with an option and (it seems) broke anything that used - * SSL3_FLAGS_* for the purpose. So we need to know how to do both, - * and we mustn't use the SSL3_FLAGS option with anything besides - * OpenSSL 0.9.8l. - * - * No, we can't just set flag 0x0010 everywhere. It breaks Tor with - * OpenSSL 1.0.0beta3 and later. On the other hand, we might be able to - * set option 0x00040000L everywhere. - * - * No, we can't simply detect whether the flag or the option is present - * in the headers at build-time: some vendors (notably Apple) like to - * leave their headers out of sync with their libraries. - * - * Yes, it _is_ almost as if the OpenSSL developers decided that no - * program should be allowed to use renegotiation unless it first passed - * a test of intelligence and determination. - */ - if (version > OPENSSL_V(0,9,8,'k') && version <= OPENSSL_V(0,9,8,'l')) { - log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l, but " - "some vendors have backported renegotiation code from " - "0.9.8m without updating the version number. " - "I will try SSL3_FLAGS and SSL_OP to enable renegotation.", - SSLeay_version(SSLEAY_VERSION)); - use_unsafe_renegotiation_flag = 1; - use_unsafe_renegotiation_op = 1; - } else if (version > OPENSSL_V(0,9,8,'l')) { - log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; " - "I will try SSL_OP to enable renegotiation", - SSLeay_version(SSLEAY_VERSION)); - use_unsafe_renegotiation_op = 1; - } else if (version <= OPENSSL_V(0,9,8,'k')) { - log_info(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than " - "0.9.8l, but some vendors have backported 0.9.8l's " - "renegotiation code to earlier versions, and some have " - "backported the code from 0.9.8m or 0.9.8n. I'll set both " - "SSL3_FLAGS and SSL_OP just to be safe.", - SSLeay_version(SSLEAY_VERSION), version); - use_unsafe_renegotiation_flag = 1; - use_unsafe_renegotiation_op = 1; - } else { - /* this is dead code, yes? */ - log_info(LD_GENERAL, "OpenSSL %s has version %lx", - SSLeay_version(SSLEAY_VERSION), version); - } - #if (SIZEOF_VOID_P >= 8 && \ - !defined(OPENSSL_NO_EC) && \ OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) + long version = SSLeay(); + if (version >= OPENSSL_V_SERIES(1,0,1)) { /* Warn if we could *almost* be running with much faster ECDH. If we're built for a 64-bit target, using OpenSSL 1.0.1, but we @@ -659,7 +602,8 @@ tor_tls_create_certificate(crypto_pk_t *rsa, * than having it start right now. Don't choose quite uniformly, since * then we might pick a time where we're about to expire. Lastly, be * sure to start on a day boundary. */ - start_time = time(NULL) - crypto_rand_int(cert_lifetime) + 2*24*3600; + time_t now = time(NULL); + start_time = crypto_rand_time_range(now - cert_lifetime, now) + 2*24*3600; start_time -= start_time % (24*3600); tor_assert(rsa); @@ -789,7 +733,7 @@ const char UNRESTRICTED_SERVER_CIPHER_LIST[] = /** List of ciphers that clients should advertise, omitting items that * our OpenSSL doesn't know about. */ static const char CLIENT_CIPHER_LIST[] = -#include "./ciphers.inc" +#include "ciphers.inc" /* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version * of any cipher we say. */ "!SSLv2" @@ -804,7 +748,7 @@ typedef struct cipher_info_t { unsigned id; const char *name; } cipher_info_t; static const cipher_info_t CLIENT_CIPHER_INFO_LIST[] = { #define CIPHER(id, name) { id, name }, #define XCIPHER(id, name) { id, #name }, -#include "./ciphers.inc" +#include "ciphers.inc" #undef CIPHER #undef XCIPHER }; @@ -1326,24 +1270,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, } #endif - /* XXX This block is now obsolete. */ - if ( -#ifdef DISABLE_SSL3_HANDSHAKE - 1 || -#endif - SSLeay() < OPENSSL_V(0,9,8,'s') || - (SSLeay() >= OPENSSL_V_SERIES(0,9,9) && - SSLeay() < OPENSSL_V(1,0,0,'f'))) { - /* And not SSL3 if it's subject to CVE-2011-4576. */ - log_info(LD_NET, "Disabling SSLv3 because this OpenSSL version " - "might otherwise be vulnerable to CVE-2011-4576 " - "(compile-time version %08lx (%s); " - "runtime version %08lx (%s))", - (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, - (unsigned long)SSLeay(), SSLeay_version(SSLEAY_VERSION)); - SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3); - } - SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE); SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE); @@ -1354,16 +1280,21 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, /* Yes, we know what we are doing here. No, we do not treat a renegotiation * as authenticating any earlier-received data. */ - if (use_unsafe_renegotiation_op) { + { SSL_CTX_set_options(result->ctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); } +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION); +#endif +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) #ifndef OPENSSL_NO_COMP /* Don't actually allow compression; it uses ram and time, but the data * we transmit is all encrypted anyway. */ if (result->ctx->comp_methods) result->ctx->comp_methods = NULL; #endif +#endif #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS); #endif @@ -1398,8 +1329,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh)); crypto_dh_free(dh); } -#if (!defined(OPENSSL_NO_EC) && \ - OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)) if (! is_client) { int nid; EC_KEY *ec_key; @@ -1415,9 +1344,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, SSL_CTX_set_tmp_ecdh(result->ctx, ec_key); EC_KEY_free(ec_key); } -#else - (void)flags; -#endif SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER, always_accept_verify_cb); /* let us realloc bufs that we're writing from */ @@ -1511,10 +1437,23 @@ static int v2_cipher_list_pruned = 0; /** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>; * return 1 if it does support it, or if we have no way to tell. */ static int -find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher) +find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher) { const SSL_CIPHER *c; -#ifdef HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,2) + { + unsigned char cipherid[3]; + tor_assert(ssl); + set_uint16(cipherid, htons(cipher)); + cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting + * with a two-byte 'cipherid', it may look for a v2 + * cipher with the appropriate 3 bytes. */ + c = SSL_CIPHER_find((SSL*)ssl, cipherid); + if (c) + tor_assert((c->id & 0xffff) == cipher); + return c != NULL; + } +#elif defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) if (m && m->get_cipher_by_char) { unsigned char cipherid[3]; set_uint16(cipherid, htons(cipher)); @@ -1527,6 +1466,7 @@ find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher) return c != NULL; } else #endif +#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) if (m && m->get_cipher && m->num_ciphers) { /* It would seem that some of the "let's-clean-up-openssl" forks have * removed the get_cipher_by_char function. Okay, so now you get a @@ -1540,23 +1480,24 @@ find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher) } } return 0; - } else { - return 1; /* No way to search */ } +#endif + (void) ssl; + return 1; /* No way to search */ } /** Remove from v2_cipher_list every cipher that we don't support, so that * comparing v2_cipher_list to a client's cipher list will give a sensible * result. */ static void -prune_v2_cipher_list(void) +prune_v2_cipher_list(const SSL *ssl) { uint16_t *inp, *outp; const SSL_METHOD *m = SSLv23_method(); inp = outp = v2_cipher_list; while (*inp) { - if (find_cipher_by_id(m, *inp)) { + if (find_cipher_by_id(ssl, m, *inp)) { *outp++ = *inp++; } else { inp++; @@ -1578,7 +1519,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl, int i, res; tor_tls_t *tor_tls; if (PREDICT_UNLIKELY(!v2_cipher_list_pruned)) - prune_v2_cipher_list(); + prune_v2_cipher_list(ssl); tor_tls = tor_tls_get_by_ssl(ssl); if (tor_tls && tor_tls->client_cipher_list_type) @@ -1612,7 +1553,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl, const uint16_t *v2_cipher = v2_cipher_list; for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) { SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i); - uint16_t id = cipher->id & 0xffff; + uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff; if (id == 0x00ff) /* extended renegotiation indicator. */ continue; if (!id || id != *v2_cipher) { @@ -1675,14 +1616,17 @@ static void tor_tls_server_info_callback(const SSL *ssl, int type, int val) { tor_tls_t *tls; + int ssl_state; (void) val; tor_tls_debug_state_callback(ssl, type, val); if (type != SSL_CB_ACCEPT_LOOP) return; - if ((ssl->state != SSL3_ST_SW_SRVR_HELLO_A) && - (ssl->state != SSL3_ST_SW_SRVR_HELLO_B)) + + ssl_state = SSL_state(ssl); + if ((ssl_state != SSL3_ST_SW_SRVR_HELLO_A) && + (ssl_state != SSL3_ST_SW_SRVR_HELLO_B)) return; tls = tor_tls_get_by_ssl(ssl); @@ -1713,10 +1657,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) if (tls) { tls->wasV2Handshake = 1; -#ifdef USE_BUFFEREVENTS - if (use_unsafe_renegotiation_flag) - tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; -#endif } else { log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); } @@ -1724,7 +1664,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) } #endif -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0) /** Callback to get invoked on a server after we've read the list of ciphers * the client supports, but before we pick our own ciphersuite. * @@ -1762,9 +1701,6 @@ tor_tls_setup_session_secret_cb(tor_tls_t *tls) { SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL); } -#else -#define tor_tls_setup_session_secret_cb(tls) STMT_NIL -#endif /** Explain which ciphers we're missing. */ static void @@ -1790,21 +1726,46 @@ log_unsupported_ciphers(smartlist_t *unsupported) tor_free(joined); } -/** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically, - * a list designed to mimic a common web browser. We might not be able to do - * that if OpenSSL doesn't support all the ciphers we want. Some of the - * ciphers in the list won't actually be implemented by OpenSSL: that's okay - * so long as the server doesn't select them. +static void +set_ssl_ciphers_to_list(SSL *ssl, STACK_OF(SSL_CIPHER) *stack) +{ + STACK_OF(SSL_CIPHER) *ciphers; + + int r, i; + /* #1: ensure that the ssl object has its own list of ciphers. Otherwise we + * might be about to stomp the SSL_CTX ciphers list. */ + r = SSL_set_cipher_list(ssl, "HIGH"); + tor_assert(r); + + /* #2: Grab ssl_ciphers and clear it. */ + ciphers = SSL_get_ciphers(ssl); + tor_assert(ciphers); + sk_SSL_CIPHER_zero(ciphers); + + /* #3: Copy the elements from stack. */ + for (i = 0; i < sk_SSL_CIPHER_num(stack); ++i) { + SSL_CIPHER *c = sk_SSL_CIPHER_value(stack, i); + sk_SSL_CIPHER_push(ciphers, c); + } +} + +/** Replace the ciphers on <b>ssl</b> with a new list of SSL ciphersuites: + * specifically, a list designed to mimic a common web browser. We might not + * be able to do that if OpenSSL doesn't support all the ciphers we want. + * Some of the ciphers in the list won't actually be implemented by OpenSSL: + * that's okay so long as the server doesn't select them. * * [If the server <b>does</b> select a bogus cipher, we won't crash or * anything; we'll just fail later when we try to look up the cipher in * ssl->cipher_list_by_id.] */ static void -rectify_client_ciphers(STACK_OF(SSL_CIPHER) **ciphers) +rectify_client_ciphers(SSL *ssl) { #ifdef V2_HANDSHAKE_CLIENT if (PREDICT_UNLIKELY(!CLIENT_CIPHER_STACK)) { + STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl); + /* We need to set CLIENT_CIPHER_STACK to an array of the ciphers * we want to use/advertise. */ int i = 0, j = 0; @@ -1825,32 +1786,33 @@ rectify_client_ciphers(STACK_OF(SSL_CIPHER) **ciphers) tor_assert(CLIENT_CIPHER_STACK); log_debug(LD_NET, "List was: %s", CLIENT_CIPHER_LIST); - for (j = 0; j < sk_SSL_CIPHER_num(*ciphers); ++j) { - SSL_CIPHER *cipher = sk_SSL_CIPHER_value(*ciphers, j); - log_debug(LD_NET, "Cipher %d: %lx %s", j, cipher->id, cipher->name); + for (j = 0; j < sk_SSL_CIPHER_num(ciphers); ++j) { + SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, j); + log_debug(LD_NET, "Cipher %d: %lx %s", j, + SSL_CIPHER_get_id(cipher), SSL_CIPHER_get_name(cipher)); } /* Then copy as many ciphers as we can from the good list, inserting * dummies as needed. Let j be an index into list of ciphers we have - * (*ciphers) and let i be an index into the ciphers we want + * (ciphers) and let i be an index into the ciphers we want * (CLIENT_INFO_CIPHER_LIST). We are building a list of ciphers in * CLIENT_CIPHER_STACK. */ for (i = j = 0; i < N_CLIENT_CIPHERS; ) { SSL_CIPHER *cipher = NULL; - if (j < sk_SSL_CIPHER_num(*ciphers)) - cipher = sk_SSL_CIPHER_value(*ciphers, j); - if (cipher && ((cipher->id >> 24) & 0xff) != 3) { + if (j < sk_SSL_CIPHER_num(ciphers)) + cipher = sk_SSL_CIPHER_value(ciphers, j); + if (cipher && ((SSL_CIPHER_get_id(cipher) >> 24) & 0xff) != 3) { /* Skip over non-v3 ciphers entirely. (This should no longer be * needed, thanks to saying !SSLv2 above.) */ log_debug(LD_NET, "Skipping v%d cipher %s", - (int)((cipher->id>>24) & 0xff), - cipher->name); + (int)((SSL_CIPHER_get_id(cipher)>>24) & 0xff), + SSL_CIPHER_get_name(cipher)); ++j; } else if (cipher && - (cipher->id & 0xffff) == CLIENT_CIPHER_INFO_LIST[i].id) { + (SSL_CIPHER_get_id(cipher) & 0xffff) == CLIENT_CIPHER_INFO_LIST[i].id) { /* "cipher" is the cipher we expect. Put it on the list. */ - log_debug(LD_NET, "Found cipher %s", cipher->name); + log_debug(LD_NET, "Found cipher %s", SSL_CIPHER_get_name(cipher)); sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, cipher); ++j; ++i; @@ -1876,12 +1838,10 @@ rectify_client_ciphers(STACK_OF(SSL_CIPHER) **ciphers) smartlist_free(unsupported); } - sk_SSL_CIPHER_free(*ciphers); - *ciphers = sk_SSL_CIPHER_dup(CLIENT_CIPHER_STACK); - tor_assert(*ciphers); + set_ssl_ciphers_to_list(ssl, CLIENT_CIPHER_STACK); #else - (void)ciphers; + (void)ciphers; #endif } @@ -1925,7 +1885,7 @@ tor_tls_new(int sock, int isServer) goto err; } if (!isServer) - rectify_client_ciphers(&result->ssl->cipher_list); + rectify_client_ciphers(result->ssl); result->socket = sock; bio = BIO_new_socket(sock, BIO_NOCLOSE); if (! bio) { @@ -2018,13 +1978,8 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls) { /* Yes, we know what we are doing here. No, we do not treat a renegotiation * as authenticating any earlier-received data. */ - if (use_unsafe_renegotiation_flag) { - tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; - } - if (use_unsafe_renegotiation_op) { - SSL_set_options(tls->ssl, - SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); - } + SSL_set_options(tls->ssl, + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); } /** If this version of openssl supports it, turn off renegotiation on @@ -2034,21 +1989,19 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls) void tor_tls_block_renegotiation(tor_tls_t *tls) { +#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; +#else + (void) tls; +#endif } /** Assert that the flags that allow legacy renegotiation are still set */ void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) { - if (use_unsafe_renegotiation_flag) { - tor_assert(0 != (tls->ssl->s3->flags & - SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); - } - if (use_unsafe_renegotiation_op) { - long options = SSL_get_options(tls->ssl); - tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); - } + long options = SSL_get_options(tls->ssl); + tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); } /** Return whether this tls initiated the connect (client) or @@ -2091,8 +2044,8 @@ tor_tls_free(tor_tls_t *tls) * number of characters read. On failure, returns TOR_TLS_ERROR, * TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE. */ -int -tor_tls_read(tor_tls_t *tls, char *cp, size_t len) +MOCK_IMPL(int, +tor_tls_read,(tor_tls_t *tls, char *cp, size_t len)) { int r, err; tor_assert(tls); @@ -2179,7 +2132,7 @@ tor_tls_handshake(tor_tls_t *tls) tor_assert(tls->ssl); tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE); check_no_tls_errors(); - oldstate = tls->ssl->state; + oldstate = SSL_state(tls->ssl); if (tls->isServer) { log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls, SSL_state_string_long(tls->ssl)); @@ -2189,7 +2142,7 @@ tor_tls_handshake(tor_tls_t *tls) SSL_state_string_long(tls->ssl)); r = SSL_connect(tls->ssl); } - if (oldstate != tls->ssl->state) + if (oldstate != SSL_state(tls->ssl)) log_debug(LD_HANDSHAKE, "After call, %p was in state %s", tls, SSL_state_string_long(tls->ssl)); /* We need to call this here and not earlier, since OpenSSL has a penchant @@ -2224,8 +2177,7 @@ tor_tls_finish_handshake(tor_tls_t *tls) 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; + SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN); #ifdef V2_HANDSHAKE_SERVER if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) { /* This check is redundant, but back when we did it in the callback, @@ -2850,12 +2802,23 @@ tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out) * Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read * buffer and *<b>rbuf_bytes</b> to the amount actually used. * Set *<b>wbuf_capacity</b> to the amount of storage allocated for the write - * buffer and *<b>wbuf_bytes</b> to the amount actually used. */ -void + * buffer and *<b>wbuf_bytes</b> to the amount actually used. + * + * Return 0 on success, -1 on failure.*/ +int tor_tls_get_buffer_sizes(tor_tls_t *tls, size_t *rbuf_capacity, size_t *rbuf_bytes, size_t *wbuf_capacity, size_t *wbuf_bytes) { +#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) + (void)tls; + (void)rbuf_capacity; + (void)rbuf_bytes; + (void)wbuf_capacity; + (void)wbuf_bytes; + + return -1; +#else if (tls->ssl->s3->rbuf.buf) *rbuf_capacity = tls->ssl->s3->rbuf.len; else @@ -2866,6 +2829,8 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls, *wbuf_capacity = 0; *rbuf_bytes = tls->ssl->s3->rbuf.left; *wbuf_bytes = tls->ssl->s3->wbuf.left; + return 0; +#endif } #ifdef USE_BUFFEREVENTS @@ -2940,3 +2905,29 @@ tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in, } #endif +/** Check whether the ECC group requested is supported by the current OpenSSL + * library instance. Return 1 if the group is supported, and 0 if not. + */ +int +evaluate_ecgroup_for_tls(const char *ecgroup) +{ + EC_KEY *ec_key; + int nid; + int ret; + + if (!ecgroup) + nid = NID_tor_default_ecdhe_group; + else if (!strcasecmp(ecgroup, "P256")) + nid = NID_X9_62_prime256v1; + else if (!strcasecmp(ecgroup, "P224")) + nid = NID_secp224r1; + else + return 0; + + ec_key = EC_KEY_new_by_curve_name(nid); + ret = (ec_key != NULL); + EC_KEY_free(ec_key); + + return ret; +} + diff --git a/src/common/tortls.h b/src/common/tortls.h index f8c6d5913b..9216e83150 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -77,7 +77,7 @@ int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity); int tor_tls_check_lifetime(int severity, tor_tls_t *tls, int past_tolerance, int future_tolerance); -int tor_tls_read(tor_tls_t *tls, char *cp, size_t len); +MOCK_DECL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len)); int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n); int tor_tls_handshake(tor_tls_t *tls); int tor_tls_finish_handshake(tor_tls_t *tls); @@ -92,7 +92,7 @@ size_t tor_tls_get_forced_write_size(tor_tls_t *tls); void tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written); -void tor_tls_get_buffer_sizes(tor_tls_t *tls, +int tor_tls_get_buffer_sizes(tor_tls_t *tls, size_t *rbuf_capacity, size_t *rbuf_bytes, size_t *wbuf_capacity, size_t *wbuf_bytes); @@ -139,5 +139,7 @@ int tor_tls_cert_is_valid(int severity, int check_rsa_1024); const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls); +int evaluate_ecgroup_for_tls(const char *ecgroup); + #endif diff --git a/src/common/util.c b/src/common/util.c index 442d57a2cf..942d0c290e 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -95,6 +95,9 @@ #ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> #endif +#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) +#include <sys/prctl.h> +#endif #ifdef __clang_analyzer__ #undef MALLOC_ZERO_WORKS @@ -771,16 +774,6 @@ fast_memcmpstart(const void *mem, size_t memlen, return fast_memcmp(mem, prefix, plen); } -/** Given a nul-terminated string s, set every character before the nul - * to zero. */ -void -tor_strclear(char *s) -{ - while (*s) { - *s++ = '\0'; - } -} - /** Return a pointer to the first char of s that is not whitespace and * not a comment, or to the terminating NUL if no such character exists. */ @@ -3562,7 +3555,7 @@ finish_daemon(const char *cp) /** Write the current process ID, followed by NL, into <b>filename</b>. */ void -write_pidfile(char *filename) +write_pidfile(const char *filename) { FILE *pidfile; @@ -3949,9 +3942,11 @@ process_handle_new(void) process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t)); #ifdef _WIN32 + out->stdin_pipe = INVALID_HANDLE_VALUE; out->stdout_pipe = INVALID_HANDLE_VALUE; out->stderr_pipe = INVALID_HANDLE_VALUE; #else + out->stdin_pipe = -1; out->stdout_pipe = -1; out->stderr_pipe = -1; #endif @@ -3991,7 +3986,7 @@ process_handle_waitpid_cb(int status, void *arg) #define CHILD_STATE_FORK 3 #define CHILD_STATE_DUPOUT 4 #define CHILD_STATE_DUPERR 5 -#define CHILD_STATE_REDIRECT 6 +#define CHILD_STATE_DUPIN 6 #define CHILD_STATE_CLOSEFD 7 #define CHILD_STATE_EXEC 8 #define CHILD_STATE_FAILEXEC 9 @@ -4025,6 +4020,8 @@ tor_spawn_background(const char *const filename, const char **argv, HANDLE stdout_pipe_write = NULL; HANDLE stderr_pipe_read = NULL; HANDLE stderr_pipe_write = NULL; + HANDLE stdin_pipe_read = NULL; + HANDLE stdin_pipe_write = NULL; process_handle_t *process_handle; int status; @@ -4070,6 +4067,20 @@ tor_spawn_background(const char *const filename, const char **argv, return status; } + /* Set up pipe for stdin */ + if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, &saAttr, 0)) { + log_warn(LD_GENERAL, + "Failed to create pipe for stdin communication with child process: %s", + format_win32_error(GetLastError())); + return status; + } + if (!SetHandleInformation(stdin_pipe_write, HANDLE_FLAG_INHERIT, 0)) { + log_warn(LD_GENERAL, + "Failed to configure pipe for stdin communication with child " + "process: %s", format_win32_error(GetLastError())); + return status; + } + /* Create the child process */ /* Windows expects argv to be a whitespace delimited string, so join argv up @@ -4084,7 +4095,7 @@ tor_spawn_background(const char *const filename, const char **argv, siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = stderr_pipe_write; siStartInfo.hStdOutput = stdout_pipe_write; - siStartInfo.hStdInput = NULL; + siStartInfo.hStdInput = stdin_pipe_read; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; /* Create the child process */ @@ -4114,6 +4125,7 @@ tor_spawn_background(const char *const filename, const char **argv, /* 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->stdin_pipe = stdin_pipe_write; status = process_handle->status = PROCESS_STATUS_RUNNING; } @@ -4124,6 +4136,7 @@ tor_spawn_background(const char *const filename, const char **argv, pid_t pid; int stdout_pipe[2]; int stderr_pipe[2]; + int stdin_pipe[2]; int fd, retval; ssize_t nbytes; process_handle_t *process_handle; @@ -4148,7 +4161,7 @@ tor_spawn_background(const char *const filename, const char **argv, child_state = CHILD_STATE_PIPE; - /* Set up pipe for redirecting stdout and stderr of child */ + /* Set up pipe for redirecting stdout, stderr, and stdin of child */ retval = pipe(stdout_pipe); if (-1 == retval) { log_warn(LD_GENERAL, @@ -4169,6 +4182,20 @@ tor_spawn_background(const char *const filename, const char **argv, return status; } + retval = pipe(stdin_pipe); + if (-1 == retval) { + log_warn(LD_GENERAL, + "Failed to set up pipe for stdin communication with child process: %s", + strerror(errno)); + + close(stdout_pipe[0]); + close(stdout_pipe[1]); + close(stderr_pipe[0]); + close(stderr_pipe[1]); + + return status; + } + child_state = CHILD_STATE_MAXFD; #ifdef _SC_OPEN_MAX @@ -4190,6 +4217,15 @@ tor_spawn_background(const char *const filename, const char **argv, if (0 == pid) { /* In child */ +#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) + /* Attempt to have the kernel issue a SIGTERM if the parent + * goes away. Certain attributes of the binary being execve()ed + * will clear this during the execve() call, but it's better + * than nothing. + */ + prctl(PR_SET_PDEATHSIG, SIGTERM); +#endif + child_state = CHILD_STATE_DUPOUT; /* Link child stdout to the write end of the pipe */ @@ -4204,13 +4240,11 @@ tor_spawn_background(const char *const filename, const char **argv, if (-1 == retval) goto error; - child_state = CHILD_STATE_REDIRECT; + child_state = CHILD_STATE_DUPIN; - /* Link stdin to /dev/null */ - fd = open("/dev/null", O_RDONLY); /* NOT cloexec, obviously. */ - if (fd != -1) - dup2(fd, STDIN_FILENO); - else + /* Link child stdin to the read end of the pipe */ + retval = dup2(stdin_pipe[0], STDIN_FILENO); + if (-1 == retval) goto error; child_state = CHILD_STATE_CLOSEFD; @@ -4219,7 +4253,8 @@ tor_spawn_background(const char *const filename, const char **argv, close(stderr_pipe[1]); close(stdout_pipe[0]); close(stdout_pipe[1]); - close(fd); + close(stdin_pipe[0]); + close(stdin_pipe[1]); /* Close all other fds, including the read end of the pipe */ /* XXX: We should now be doing enough FD_CLOEXEC setting to make @@ -4235,8 +4270,10 @@ tor_spawn_background(const char *const filename, const char **argv, does not modify the arguments */ if (env) execve(filename, (char *const *) argv, env->unixoid_environment_block); - else - execvp(filename, (char *const *) argv); + else { + static char *new_env[] = { NULL }; + execve(filename, (char *const *) argv, new_env); + } /* If we got here, the exec or open(/dev/null) failed */ @@ -4269,6 +4306,8 @@ tor_spawn_background(const char *const filename, const char **argv, if (-1 == pid) { log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno)); + close(stdin_pipe[0]); + close(stdin_pipe[1]); close(stdout_pipe[0]); close(stdout_pipe[1]); close(stderr_pipe[0]); @@ -4305,16 +4344,28 @@ tor_spawn_background(const char *const filename, const char **argv, strerror(errno)); } + /* Return write end of the stdin pipe to caller, and close the read end */ + process_handle->stdin_pipe = stdin_pipe[1]; + retval = close(stdin_pipe[0]); + + if (-1 == retval) { + log_warn(LD_GENERAL, + "Failed to close read end of stdin pipe in parent process: %s", + strerror(errno)); + } + status = process_handle->status = PROCESS_STATUS_RUNNING; - /* Set stdout/stderr pipes to be non-blocking */ + /* Set stdin/stdout/stderr pipes to be non-blocking */ if (fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK) < 0 || - fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0) { - log_warn(LD_GENERAL, "Failed to set stderror/stdout pipes nonblocking " - "in parent process: %s", strerror(errno)); + fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0 || + fcntl(process_handle->stdin_pipe, F_SETFL, O_NONBLOCK) < 0) { + log_warn(LD_GENERAL, "Failed to set stderror/stdout/stdin pipes " + "nonblocking in parent process: %s", strerror(errno)); } /* Open the buffered IO streams */ process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r"); process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r"); + process_handle->stdin_handle = fdopen(process_handle->stdin_pipe, "r"); *process_handle_out = process_handle; return process_handle->status; @@ -4357,6 +4408,9 @@ tor_process_handle_destroy,(process_handle_t *process_handle, if (process_handle->stderr_pipe) CloseHandle(process_handle->stderr_pipe); + + if (process_handle->stdin_pipe) + CloseHandle(process_handle->stdin_pipe); #else if (process_handle->stdout_handle) fclose(process_handle->stdout_handle); @@ -4364,6 +4418,9 @@ tor_process_handle_destroy,(process_handle_t *process_handle, if (process_handle->stderr_handle) fclose(process_handle->stderr_handle); + if (process_handle->stdin_handle) + fclose(process_handle->stdin_handle); + clear_waitpid_callback(process_handle->waitpid_cb); #endif diff --git a/src/common/util.h b/src/common/util.h index ea774bd9bd..ac4ebfc6f3 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -45,6 +45,13 @@ #error "Sorry; we don't support building with NDEBUG." #endif +/* Don't use assertions during coverage. It leads to tons of unreached + * branches which in reality are only assertions we didn't hit. */ +#ifdef TOR_COVERAGE +#define tor_assert(a) STMT_BEGIN \ + (void)(a); \ + STMT_END +#else /** Like assert(3), but send assertion failures to the log as well as to * stderr. */ #define tor_assert(expr) STMT_BEGIN \ @@ -52,6 +59,7 @@ tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \ abort(); \ } STMT_END +#endif void tor_assertion_failed_(const char *fname, unsigned int line, const char *func, const char *expr); @@ -209,7 +217,6 @@ 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_strclear(char *s); void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2)); long tor_parse_long(const char *s, int base, long min, @@ -411,7 +418,7 @@ int path_is_relative(const char *filename); /* Process helpers */ void start_daemon(void); void finish_daemon(const char *desired_cwd); -void write_pidfile(char *filename); +void write_pidfile(const char *filename); /* Port forwarding */ void tor_check_port_forwarding(const char *filename, @@ -467,12 +474,15 @@ struct process_handle_t { /** One of the PROCESS_STATUS_* values */ int status; #ifdef _WIN32 + HANDLE stdin_pipe; HANDLE stdout_pipe; HANDLE stderr_pipe; PROCESS_INFORMATION pid; #else + int stdin_pipe; int stdout_pipe; int stderr_pipe; + FILE *stdin_handle; FILE *stdout_handle; FILE *stderr_handle; pid_t pid; @@ -563,8 +573,6 @@ STATIC int format_helper_exit_status(unsigned char child_state, #endif -const char *libor_get_digests(void); - #define ARRAY_LENGTH(x) ((sizeof(x)) / sizeof(x[0])) #endif diff --git a/src/common/util_codedigest.c b/src/common/util_codedigest.c deleted file mode 100644 index 7384f7dc1a..0000000000 --- a/src/common/util_codedigest.c +++ /dev/null @@ -1,13 +0,0 @@ - -#include "util.h" - -/** Return a string describing the digest of the source files in src/common/ - */ -const char * -libor_get_digests(void) -{ - return "" -#include "common_sha1.i" - ; -} - diff --git a/src/ext/eventdns.c b/src/ext/eventdns.c index 2b2988f1ec..a0c7ff29fa 100644 --- a/src/ext/eventdns.c +++ b/src/ext/eventdns.c @@ -37,7 +37,7 @@ */ #include "eventdns_tor.h" -#include "../common/util.h" +#include "util.h" #include <sys/types.h> /* #define NDEBUG */ diff --git a/src/or/buffers.c b/src/or/buffers.c index be9974418d..2d7dd937d8 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -20,8 +20,8 @@ #include "control.h" #include "reasons.h" #include "ext_orport.h" -#include "../common/util.h" -#include "../common/torlog.h" +#include "util.h" +#include "torlog.h" #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -615,7 +615,7 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf) if (r < 0) return r; /* Error */ tor_assert(total_read+r < INT_MAX); - total_read += r; + total_read += r; if ((size_t)r < readlen) /* eof, block, or no more to read. */ break; } diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 946c002735..0688398f6d 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1053,6 +1053,10 @@ circuit_note_clock_jumped(int seconds_elapsed) "CLOCK_JUMPED"); circuit_mark_all_unused_circs(); circuit_mark_all_dirty_circs_as_unusable(); + if (seconds_elapsed < 0) { + /* Restart all the timers in case we jumped a long way into the past. */ + reset_all_main_loop_timers(); + } } /** Take the 'extend' <b>cell</b>, pull out addr/port plus the onion @@ -1396,9 +1400,12 @@ onionskin_answer(or_circuit_t *circ, log_debug(LD_CIRC,"Finished sending '%s' cell.", circ->is_first_hop ? "created_fast" : "created"); - /* Ignore the local bit when testing - many test networks run on local - * addresses */ - if ((!channel_is_local(circ->p_chan) || get_options()->TestingTorNetwork) + /* Ignore the local bit when ExtendAllowPrivateAddresses is set: + * it violates the assumption that private addresses are local. + * Also, many test networks run on local addresses, and + * TestingTorNetwork sets ExtendAllowPrivateAddresses. */ + if ((!channel_is_local(circ->p_chan) + || get_options()->ExtendAllowPrivateAddresses) && !channel_is_outgoing(circ->p_chan)) { /* record that we could process create cells from a non-local conn * that we didn't initiate; presumably this means that create cells diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index 7b3ad56537..3ced5afad5 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -1232,6 +1232,9 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) } cbt->liveness.network_last_live = now; cbt->liveness.nonlive_timeouts = 0; + + /* Tell control.c */ + control_event_network_liveness_update(1); } /** @@ -1316,6 +1319,9 @@ circuit_build_times_network_close(circuit_build_times_t *cbt, "Tor has not observed any network activity for the past %d " "seconds. Disabling circuit build timeout recording.", (int)(now - cbt->liveness.network_last_live)); + + /* Tell control.c */ + control_event_network_liveness_update(0); } else { log_info(LD_CIRC, "Got non-live timeout. Current count is: %d", diff --git a/src/or/circuituse.c b/src/or/circuituse.c index d0d31ad9cf..a429a7d053 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1189,17 +1189,28 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn) if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); + int removed = 0; if (conn == origin_circ->p_streams) { origin_circ->p_streams = conn->next_stream; - return; + removed = 1; + } else { + for (prevconn = origin_circ->p_streams; + prevconn && prevconn->next_stream && prevconn->next_stream != conn; + prevconn = prevconn->next_stream) + ; + if (prevconn && prevconn->next_stream) { + prevconn->next_stream = conn->next_stream; + removed = 1; + } } - - for (prevconn = origin_circ->p_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if (prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; + if (removed) { + /* If the stream was removed, and it was a rend stream, decrement the + * number of streams on the circuit associated with the rend service. + */ + if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) { + tor_assert(origin_circ->rend_data); + origin_circ->rend_data->nr_streams--; + } return; } } else { @@ -2149,7 +2160,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ, * that an attempt to connect to a hidden service just * succeeded. Tell rendclient.c. */ rend_client_note_connection_attempt_ended( - ENTRY_TO_EDGE_CONN(apconn)->rend_data->onion_address); + ENTRY_TO_EDGE_CONN(apconn)->rend_data); } if (cpath) { /* we were given one; use it */ diff --git a/src/or/config.c b/src/or/config.c index fca350c203..e4a2d1c5ae 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -225,7 +225,7 @@ static config_var_t option_vars_[] = { V(DisableDebuggerAttachment, BOOL, "1"), V(DisableIOCP, BOOL, "1"), OBSOLETE("DisableV2DirectoryInfo_"), - V(DynamicDHGroups, BOOL, "0"), + OBSOLETE("DynamicDHGroups"), VPORT(DNSPort, LINELIST, NULL), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), @@ -286,6 +286,8 @@ static config_var_t option_vars_[] = { VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL), VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL), V(HiddenServiceStatistics, BOOL, "0"), V(HidServAuth, LINELIST, NULL), V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), @@ -545,8 +547,6 @@ static char *get_bindaddr_from_transport_listen_line(const char *line, static int parse_dir_authority_line(const char *line, dirinfo_type_t required_type, int validate_only); -static int parse_dir_fallback_line(const char *line, - int validate_only); static void port_cfg_free(port_cfg_t *port); static int parse_ports(or_options_t *options, int validate_only, char **msg_out, int *n_ports_out); @@ -841,6 +841,41 @@ escaped_safe_str(const char *address) return escaped(address); } +/** List of default directory authorities */ + +static const char *default_authorities[] = { + "moria1 orport=9101 " + "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 " + "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31", + "tor26 orport=443 " + "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 " + "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D", + "dizum orport=443 " + "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 " + "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755", + "Tonga orport=443 bridge " + "82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D", + "gabelmoo orport=443 " + "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " + "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281", + "dannenberg orport=443 " + "v3ident=585769C78764D58426B8B52B6651A5A71137189A " + "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123", + "urras orport=80 " + "v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C " + "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417", + "maatuska orport=80 " + "v3ident=49015F787433103580E3B66A1707A00E60F2D15B " + "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810", + "Faravahar orport=443 " + "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 " + "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC", + "longclaw orport=443 " + "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 " + "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145", + NULL +}; + /** Add the default directory authorities directly into the trusted dir list, * but only add them insofar as they share bits with <b>type</b>. * Each authority's bits are restricted to the bits shared with <b>type</b>. @@ -849,50 +884,18 @@ static void add_default_trusted_dir_authorities(dirinfo_type_t type) { int i; - const char *authorities[] = { - "moria1 orport=9101 " - "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 " - "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31", - "tor26 orport=443 " - "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 " - "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D", - "dizum orport=443 " - "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 " - "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755", - "Tonga orport=443 bridge " - "82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D", - "gabelmoo orport=443 " - "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " - "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281", - "dannenberg orport=443 " - "v3ident=585769C78764D58426B8B52B6651A5A71137189A " - "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123", - "urras orport=80 " - "v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C " - "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417", - "maatuska orport=80 " - "v3ident=49015F787433103580E3B66A1707A00E60F2D15B " - "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810", - "Faravahar orport=443 " - "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 " - "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC", - "longclaw orport=443 " - "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 " - "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145", - NULL - }; - for (i=0; authorities[i]; i++) { - if (parse_dir_authority_line(authorities[i], type, 0)<0) { + for (i=0; default_authorities[i]; i++) { + if (parse_dir_authority_line(default_authorities[i], type, 0)<0) { log_err(LD_BUG, "Couldn't parse internal DirAuthority line %s", - authorities[i]); + default_authorities[i]); } } } /** Add the default fallback directory servers into the fallback directory * server list. */ -static void -add_default_fallback_dir_servers(void) +MOCK_IMPL(void, +add_default_fallback_dir_servers,(void)) { int i; const char *fallback[] = { @@ -961,7 +964,7 @@ validate_dir_servers(or_options_t *options, or_options_t *old_options) /** Look at all the config options and assign new dir authorities * as appropriate. */ -static int +int consider_adding_dir_servers(const or_options_t *options, const or_options_t *old_options) { @@ -979,23 +982,36 @@ consider_adding_dir_servers(const or_options_t *options, if (!need_to_update) return 0; /* all done */ + /* "You cannot set both DirAuthority and Alternate*Authority." + * Checking that this restriction holds allows us to simplify + * the unit tests. */ + tor_assert(!(options->DirAuthorities && + (options->AlternateDirAuthority + || options->AlternateBridgeAuthority))); + /* Start from a clean slate. */ clear_dir_servers(); if (!options->DirAuthorities) { /* then we may want some of the defaults */ dirinfo_type_t type = NO_DIRINFO; - if (!options->AlternateBridgeAuthority) + if (!options->AlternateBridgeAuthority) { type |= BRIDGE_DIRINFO; - if (!options->AlternateDirAuthority) + } + if (!options->AlternateDirAuthority) { type |= V3_DIRINFO | EXTRAINFO_DIRINFO | MICRODESC_DIRINFO; + /* Only add the default fallback directories when the DirAuthorities, + * AlternateDirAuthority, and FallbackDir directory config options + * are set to their defaults. */ + if (!options->FallbackDir) { + add_default_fallback_dir_servers(); + } + } /* if type == NO_DIRINFO, we don't want to add any of the * default authorities, because we've replaced them all */ if (type != NO_DIRINFO) add_default_trusted_dir_authorities(type); } - if (!options->FallbackDir) - add_default_fallback_dir_servers(); for (cl = options->DirAuthorities; cl; cl = cl->next) if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) @@ -1318,10 +1334,6 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options, if (!old_options) return 0; - if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) { - return 1; - } - if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup)) return 1; @@ -1503,24 +1515,6 @@ 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) && options->DynamicDHGroups) { - char *keydir = get_datadir_fname("keys"); - if (check_private_dir(keydir, CPD_CREATE, options->User)) { - tor_free(keydir); - return -1; - } - tor_free(keydir); - - if (!old_options || !old_options->DynamicDHGroups) { - char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); - crypto_set_tls_dh_prime(fname); - tor_free(fname); - } - } 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 || @@ -1758,6 +1752,7 @@ options_act(const or_options_t *old_options) if (!public_server_mode(options)) { options->CellStatistics = 0; options->EntryStatistics = 0; + options->ConnDirectionStatistics = 0; options->HiddenServiceStatistics = 0; options->ExitPortStatistics = 0; } @@ -1890,28 +1885,33 @@ options_act(const or_options_t *old_options) return 0; } +typedef enum { + TAKES_NO_ARGUMENT = 0, + ARGUMENT_NECESSARY = 1, + ARGUMENT_OPTIONAL = 2 +} takes_argument_t; + static const struct { const char *name; - int takes_argument; + takes_argument_t takes_argument; } CMDLINE_ONLY_OPTIONS[] = { - { "-f", 1 }, - { "--allow-missing-torrc", 0 }, - { "--defaults-torrc", 1 }, - { "--hash-password", 1 }, - { "--dump-config", 1 }, - { "--list-fingerprint", 0 }, - { "--verify-config", 0 }, - { "--ignore-missing-torrc", 0 }, - { "--quiet", 0 }, - { "--hush", 0 }, - { "--version", 0 }, - { "--library-versions", 0 }, - { "-h", 0 }, - { "--help", 0 }, - { "--list-torrc-options", 0 }, - { "--digests", 0 }, - { "--nt-service", 0 }, - { "-nt-service", 0 }, + { "-f", ARGUMENT_NECESSARY }, + { "--allow-missing-torrc", TAKES_NO_ARGUMENT }, + { "--defaults-torrc", ARGUMENT_NECESSARY }, + { "--hash-password", ARGUMENT_NECESSARY }, + { "--dump-config", ARGUMENT_OPTIONAL }, + { "--list-fingerprint", TAKES_NO_ARGUMENT }, + { "--verify-config", TAKES_NO_ARGUMENT }, + { "--ignore-missing-torrc", TAKES_NO_ARGUMENT }, + { "--quiet", TAKES_NO_ARGUMENT }, + { "--hush", TAKES_NO_ARGUMENT }, + { "--version", TAKES_NO_ARGUMENT }, + { "--library-versions", TAKES_NO_ARGUMENT }, + { "-h", TAKES_NO_ARGUMENT }, + { "--help", TAKES_NO_ARGUMENT }, + { "--list-torrc-options", TAKES_NO_ARGUMENT }, + { "--nt-service", TAKES_NO_ARGUMENT }, + { "-nt-service", TAKES_NO_ARGUMENT }, { NULL, 0 }, }; @@ -1938,7 +1938,7 @@ config_parse_commandline(int argc, char **argv, int ignore_errors, while (i < argc) { unsigned command = CONFIG_LINE_NORMAL; - int want_arg = 1; + takes_argument_t want_arg = ARGUMENT_NECESSARY; int is_cmdline = 0; int j; @@ -1968,7 +1968,9 @@ config_parse_commandline(int argc, char **argv, int ignore_errors, want_arg = 0; } - if (want_arg && i == argc-1) { + const int is_last = (i == argc-1); + + if (want_arg == ARGUMENT_NECESSARY && is_last) { if (ignore_errors) { arg = strdup(""); } else { @@ -1978,8 +1980,11 @@ config_parse_commandline(int argc, char **argv, int ignore_errors, config_free_lines(front_cmdline); return -1; } + } else if (want_arg == ARGUMENT_OPTIONAL && is_last) { + arg = tor_strdup(""); } else { - arg = want_arg ? tor_strdup(argv[i+1]) : strdup(""); + arg = (want_arg != TAKES_NO_ARGUMENT) ? tor_strdup(argv[i+1]) : + tor_strdup(""); } param = tor_malloc_zero(sizeof(config_line_t)); @@ -2566,6 +2571,61 @@ options_validate_cb(void *old_options, void *options, void *default_options, from_setconf, msg); } +#define REJECT(arg) \ + STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END +#define COMPLAIN(args...) \ + STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END + +/** Log a warning message iff <b>filepath</b> is not absolute. + * Warning message must contain option name <b>option</b> and + * an absolute path that <b>filepath<b> will resolve to. + * + * In case <b>filepath</b> is absolute, do nothing. + */ +static void +warn_if_option_path_is_relative(const char *option, + char *filepath) +{ + if (filepath && path_is_relative(filepath)) { + char *abs_path = make_path_absolute(filepath); + COMPLAIN("Path for %s (%s) is relative and will resolve to %s." + " Is this what you wanted?", option, filepath, abs_path); + tor_free(abs_path); + } +} + +/** Scan <b>options</b> for occurances of relative file/directory + * path and log a warning whenever it is found. + */ +static void +warn_about_relative_paths(or_options_t *options) +{ + tor_assert(options); + + warn_if_option_path_is_relative("CookieAuthFile", + options->CookieAuthFile); + warn_if_option_path_is_relative("ExtORPortCookieAuthFile", + options->ExtORPortCookieAuthFile); + warn_if_option_path_is_relative("DirPortFrontPage", + options->DirPortFrontPage); + warn_if_option_path_is_relative("V3BandwidthsFile", + options->V3BandwidthsFile); + warn_if_option_path_is_relative("ControlPortWriteToFile", + options->ControlPortWriteToFile); + warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile); + warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File); + warn_if_option_path_is_relative("Log",options->DebugLogFile); + warn_if_option_path_is_relative("AccelDir",options->AccelDir); + warn_if_option_path_is_relative("DataDirectory",options->DataDirectory); + warn_if_option_path_is_relative("PidFile",options->PidFile); + + for (config_line_t *hs_line = options->RendConfigLines; hs_line; + hs_line = hs_line->next) { + if (!strcasecmp(hs_line->key, "HiddenServiceDir")) + warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value); + } +} + /** Return 0 if every setting in <b>options</b> is reasonable, is a * permissible transition from <b>old_options</b>, and none of the * testing-only settings differ from <b>default_options</b> unless in @@ -2587,13 +2647,12 @@ options_validate(or_options_t *old_options, or_options_t *options, config_line_t *cl; const char *uname = get_uname(); int n_ports=0; -#define REJECT(arg) \ - STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END -#define COMPLAIN(arg) STMT_BEGIN log_warn(LD_CONFIG, arg); STMT_END tor_assert(msg); *msg = NULL; + warn_about_relative_paths(options); + if (server_mode(options) && (!strcmpstart(uname, "Windows 95") || !strcmpstart(uname, "Windows 98") || @@ -2747,6 +2806,9 @@ options_validate(or_options_t *old_options, or_options_t *options, COMPLAIN("Unrecognized TLSECGroup: Falling back to the default."); tor_free(options->TLSECGroup); } + if (!evaluate_ecgroup_for_tls(options->TLSECGroup)) { + REJECT("Unsupported TLSECGroup."); + } if (options->ExcludeNodes && options->StrictNodes) { COMPLAIN("You have asked to exclude certain relays from all positions " @@ -3755,9 +3817,10 @@ options_validate(or_options_t *old_options, or_options_t *options, "combination."); return 0; +} + #undef REJECT #undef COMPLAIN -} /* Given the value that the user has set for MaxMemInQueues, compute the * actual maximum value. We clip this value if it's too low, and autodetect @@ -4333,13 +4396,6 @@ options_init_from_torrc(int argc, char **argv) exit(0); } - if (config_line_find(cmdline_only_options, "--digests")) { - printf("Tor version %s.\n",get_version()); - printf("%s", libor_get_digests()); - printf("%s", tor_get_digests()); - exit(0); - } - if (config_line_find(cmdline_only_options, "--library-versions")) { printf("Tor version %s. \n", get_version()); printf("Library versions\tCompiled\t\tRuntime\n"); @@ -5458,7 +5514,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type, * <b>validate_only</b> is 0, and the line is well-formed, then add the * dirserver described in the line as a fallback directory. Return 0 on * success, or -1 if the line isn't well-formed or if we can't add it. */ -static int +int parse_dir_fallback_line(const char *line, int validate_only) { @@ -5934,7 +5990,8 @@ parse_port_config(smartlist_t *out, port = 1; } else if (!strcmp(addrport, "auto")) { port = CFG_AUTO_PORT; - tor_addr_parse(&addr, defaultaddr); + int af = tor_addr_parse(&addr, defaultaddr); + tor_assert(af >= 0); } else if (!strcasecmpend(addrport, ":auto")) { char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); port = CFG_AUTO_PORT; @@ -5949,7 +6006,8 @@ parse_port_config(smartlist_t *out, "9050" might be a valid address. */ port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL); if (ok) { - tor_addr_parse(&addr, defaultaddr); + int af = tor_addr_parse(&addr, defaultaddr); + tor_assert(af >= 0); } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) { if (ptmp == 0) { log_warn(LD_CONFIG, "%sPort line has address but no port", portname); @@ -6695,7 +6753,6 @@ get_num_cpus(const or_options_t *options) static void init_libevent(const or_options_t *options) { - const char *badness=NULL; tor_libevent_cfg cfg; tor_assert(options); @@ -6716,17 +6773,6 @@ init_libevent(const or_options_t *options) tor_libevent_initialize(&cfg); suppress_libevent_log_msg(NULL); - - tor_check_libevent_version(tor_libevent_get_method(), - server_mode(get_options()), - &badness); - if (badness) { - const char *v = tor_libevent_get_version_str(); - const char *m = tor_libevent_get_method(); - control_event_general_status(LOG_WARN, - "BAD_LIBEVENT VERSION=%s METHOD=%s BADNESS=%s RECOVERED=NO", - v, m, badness); - } } /** Return a newly allocated string holding a filename relative to the data @@ -6927,15 +6973,42 @@ getinfo_helper_config(control_connection_t *conn, smartlist_free(sl); } else if (!strcmp(question, "config/defaults")) { smartlist_t *sl = smartlist_new(); - int i; + int i, dirauth_lines_seen = 0; for (i = 0; option_vars_[i].name; ++i) { const config_var_t *var = &option_vars_[i]; if (var->initvalue != NULL) { - char *val = esc_for_log(var->initvalue); - smartlist_add_asprintf(sl, "%s %s\n",var->name,val); - tor_free(val); + if (strcmp(option_vars_[i].name, "DirAuthority") == 0) { + /* + * Count dirauth lines we have a default for; we'll use the + * count later to decide whether to add the defaults manually + */ + ++dirauth_lines_seen; + } + char *val = esc_for_log(var->initvalue); + smartlist_add_asprintf(sl, "%s %s\n",var->name,val); + tor_free(val); + } + } + + if (dirauth_lines_seen == 0) { + /* + * We didn't see any directory authorities with default values, + * so add the list of default authorities manually. + */ + const char **i; + + /* + * default_authorities is defined earlier in this file and + * is a const char ** NULL-terminated array of dirauth config + * lines. + */ + for (i = default_authorities; *i != NULL; ++i) { + char *val = esc_for_log(*i); + smartlist_add_asprintf(sl, "DirAuthority %s\n", val); + tor_free(val); } } + *answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); diff --git a/src/or/config.h b/src/or/config.h index b064f05321..74b28f45ca 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -91,7 +91,6 @@ int getinfo_helper_config(control_connection_t *conn, const char *question, char **answer, const char **errmsg); -const char *tor_get_digests(void); uint32_t get_effective_bwrate(const or_options_t *options); uint32_t get_effective_bwburst(const or_options_t *options); @@ -145,6 +144,12 @@ STATIC int options_validate(or_options_t *old_options, STATIC int parse_transport_line(const or_options_t *options, const char *line, int validate_only, int server); +STATIC int consider_adding_dir_servers(const or_options_t *options, + const or_options_t *old_options); +MOCK_DECL(STATIC void, add_default_fallback_dir_servers, (void)); +STATIC int +parse_dir_fallback_line(const char *line, + int validate_only); #endif #endif diff --git a/src/or/config_codedigest.c b/src/or/config_codedigest.c deleted file mode 100644 index 86d14bacef..0000000000 --- a/src/or/config_codedigest.c +++ /dev/null @@ -1,13 +0,0 @@ - -const char *tor_get_digests(void); - -/** Return a string describing the digest of the source files in src/or/ - */ -const char * -tor_get_digests(void) -{ - return "" -#include "or_sha1.i" - ; -} - diff --git a/src/or/connection.c b/src/or/connection.c index 7db0238b3d..24d47ccd18 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -586,6 +586,13 @@ connection_free_(connection_t *conn) control_connection_t *control_conn = TO_CONTROL_CONN(conn); tor_free(control_conn->safecookie_client_hash); tor_free(control_conn->incoming_cmd); + if (control_conn->ephemeral_onion_services) { + SMARTLIST_FOREACH(control_conn->ephemeral_onion_services, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(control_conn->ephemeral_onion_services); + } } /* Probably already freed by connection_free. */ @@ -1407,7 +1414,7 @@ static int connection_handle_listener_read(connection_t *conn, int new_type) { tor_socket_t news; /* the new socket */ - connection_t *newconn; + connection_t *newconn = 0; /* information about the remote peer when connecting to other routers */ struct sockaddr_storage addrbuf; struct sockaddr *remote = (struct sockaddr*)&addrbuf; @@ -3774,7 +3781,7 @@ connection_fetch_from_buf_line(connection_t *conn, char *data, } } -/** As fetch_from_buf_http, but fetches from a conncetion's input buffer_t or +/** As fetch_from_buf_http, but fetches from a connection's input buffer_t or * its bufferevent as appropriate. */ int connection_fetch_from_buf_http(connection_t *conn, @@ -4440,25 +4447,12 @@ alloc_http_authenticator(const char *authenticator) /* an authenticator in Basic authentication * is just the string "username:password" */ const size_t authenticator_length = strlen(authenticator); - /* The base64_encode function needs a minimum buffer length - * of 66 bytes. */ - const size_t base64_authenticator_length = (authenticator_length/48+1)*66; + const size_t base64_authenticator_length = + base64_encode_size(authenticator_length, 0) + 1; char *base64_authenticator = tor_malloc(base64_authenticator_length); if (base64_encode(base64_authenticator, base64_authenticator_length, - authenticator, authenticator_length) < 0) { + authenticator, authenticator_length, 0) < 0) { tor_free(base64_authenticator); /* free and set to null */ - } else { - int i = 0, j = 0; - ssize_t len = strlen(base64_authenticator); - - /* remove all newline occurrences within the string */ - for (i=0; i < len; ++i) { - if ('\n' != base64_authenticator[i]) { - base64_authenticator[j] = base64_authenticator[i]; - ++j; - } - } - base64_authenticator[j]='\0'; } return base64_authenticator; } diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 2a1a2f0fd2..c63c350fd8 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -102,8 +102,7 @@ connection_mark_unattached_ap_,(entry_connection_t *conn, int endreason, * but we should fix it someday anyway. */ if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) && connection_edge_is_rendezvous_stream(edge_conn)) { - rend_client_note_connection_attempt_ended( - edge_conn->rend_data->onion_address); + rend_client_note_connection_attempt_ended(edge_conn->rend_data); } if (base_conn->marked_for_close) { @@ -1499,61 +1498,76 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, return -1; } + /* Look up if we have client authorization configured for this hidden + * service. If we do, associate it with the rend_data. */ + rend_service_authorization_t *client_auth = + rend_client_lookup_service_authorization(socks->address); + + const char *cookie = NULL; + rend_auth_type_t auth_type = REND_NO_AUTH; + if (client_auth) { + log_info(LD_REND, "Using previously configured client authorization " + "for hidden service request."); + auth_type = client_auth->auth_type; + cookie = client_auth->descriptor_cookie; + } + /* Fill in the rend_data field so we can start doing a connection to * a hidden service. */ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data = - tor_malloc_zero(sizeof(rend_data_t)); - strlcpy(rend_data->onion_address, socks->address, - sizeof(rend_data->onion_address)); + rend_data_client_create(socks->address, NULL, cookie, auth_type); + if (rend_data == NULL) { + return -1; + } log_info(LD_REND,"Got a hidden service request for ID '%s'", safe_str_client(rend_data->onion_address)); - /* see if we already have a hidden service descriptor cached for this - * address. */ + /* Lookup the given onion address. If invalid, stop right now else we + * might have it in the cache or not, it will be tested later on. */ + unsigned int refetch_desc = 0; rend_cache_entry_t *entry = NULL; const int rend_cache_lookup_result = rend_cache_lookup_entry(rend_data->onion_address, -1, &entry); if (rend_cache_lookup_result < 0) { - /* We should already have rejected this address! */ - log_warn(LD_BUG,"Invalid service name '%s'", - safe_str_client(rend_data->onion_address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; + switch (-rend_cache_lookup_result) { + case EINVAL: + /* We should already have rejected this address! */ + log_warn(LD_BUG,"Invalid service name '%s'", + safe_str_client(rend_data->onion_address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); + return -1; + case ENOENT: + refetch_desc = 1; + break; + default: + log_warn(LD_BUG, "Unknown cache lookup error %d", + rend_cache_lookup_result); + return -1; + } } /* Help predict this next time. We're not sure if it will need * a stable circuit yet, but we know we'll need *something*. */ rep_hist_note_used_internal(now, 0, 1); - /* Look up if we have client authorization configured for this hidden - * service. If we do, associate it with the rend_data. */ - rend_service_authorization_t *client_auth = - rend_client_lookup_service_authorization( - rend_data->onion_address); - if (client_auth) { - log_info(LD_REND, "Using previously configured client authorization " - "for hidden service request."); - memcpy(rend_data->descriptor_cookie, - client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN); - rend_data->auth_type = client_auth->auth_type; - } - - /* Now, we either launch an attempt to connect to the hidden service, - * or we launch an attempt to look up its descriptor, depending on - * whether we had the descriptor. */ - if (rend_cache_lookup_result == 0) { + /* Now we have a descriptor but is it usable or not? If not, refetch. + * Also, a fetch could have been requested if the onion address was not + * found in the cache previously. */ + if (refetch_desc || !rend_client_any_intro_points_usable(entry)) { base_conn->state = AP_CONN_STATE_RENDDESC_WAIT; log_info(LD_REND, "Unknown descriptor %s. Fetching.", - safe_str_client(rend_data->onion_address)); + safe_str_client(rend_data->onion_address)); rend_client_refetch_v2_renddesc(rend_data); - } else { /* rend_cache_lookup_result > 0 */ - base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - log_info(LD_REND, "Descriptor is here. Great."); - if (connection_ap_handshake_attach_circuit(conn) < 0) { - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); - return -1; - } + return 0; + } + + /* We have the descriptor so launch a connection to the HS. */ + base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + log_info(LD_REND, "Descriptor is here. Great."); + if (connection_ap_handshake_attach_circuit(conn) < 0) { + if (!base_conn->marked_for_close) + connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); + return -1; } return 0; } @@ -2846,6 +2860,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) origin_circ->p_streams = n_stream; assert_circuit_ok(circ); + origin_circ->rend_data->nr_streams++; + connection_exit_connect(n_stream); /* For path bias: This circuit was used successfully */ diff --git a/src/or/control.c b/src/or/control.c index e25c3b2954..746dfff921 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -37,6 +37,9 @@ #include "nodelist.h" #include "policies.h" #include "reasons.h" +#include "rendclient.h" +#include "rendcommon.h" +#include "rendservice.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@ -72,7 +75,7 @@ static int disable_log_messages = 0; /** Macro: true if any control connection is interested in events of type * <b>e</b>. */ #define EVENT_IS_INTERESTING(e) \ - (!! (global_event_mask & (((uint64_t)1)<<(e)))) + (!! (global_event_mask & EVENT_MASK_(e))) /** If we're using cookie-type authentication, how long should our cookies be? */ @@ -92,6 +95,11 @@ static uint8_t *authentication_cookie = NULL; "Tor safe cookie authentication controller-to-server hash" #define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN +/** The list of onion services that have been added via ADD_ONION that do not + * belong to any particular control connection. + */ +static smartlist_t *detached_onion_services = NULL; + /** A sufficiently large size to record the last bootstrap phase string. */ #define BOOTSTRAP_MSG_LEN 1024 @@ -157,11 +165,22 @@ static int handle_control_resolve(control_connection_t *conn, uint32_t len, static int handle_control_usefeature(control_connection_t *conn, uint32_t len, const char *body); +static int handle_control_hsfetch(control_connection_t *conn, uint32_t len, + const char *body); +static int handle_control_hspost(control_connection_t *conn, uint32_t len, + const char *body); +static int handle_control_add_onion(control_connection_t *conn, uint32_t len, + const char *body); +static int handle_control_del_onion(control_connection_t *conn, uint32_t len, + const char *body); static int write_stream_target_to_buf(entry_connection_t *conn, char *buf, size_t len); static void orconn_target_get_name(char *buf, size_t len, or_connection_t *conn); +static int get_cached_network_liveness(void); +static void set_cached_network_liveness(int liveness); + /** Given a control event code for a message event, return the corresponding * log severity. */ static INLINE int @@ -941,6 +960,8 @@ static const struct control_event_t control_event_table[] = { { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" }, { EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" }, { EVENT_HS_DESC, "HS_DESC" }, + { EVENT_HS_DESC_CONTENT, "HS_DESC_CONTENT" }, + { EVENT_NETWORK_LIVENESS, "NETWORK_LIVENESS" }, { 0, NULL }, }; @@ -1713,6 +1734,22 @@ getinfo_helper_dir(control_connection_t *control_conn, *answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); + } else if (!strcmpstart(question, "hs/client/desc/id/")) { + rend_cache_entry_t *e = NULL; + + question += strlen("hs/client/desc/id/"); + if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + *errmsg = "Invalid address"; + return -1; + } + + if (!rend_cache_lookup_entry(question, -1, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else if (!strcmpstart(question, "md/id/")) { const node_t *node = node_get_by_hex_id(question+strlen("md/id/")); const microdesc_t *md = NULL; @@ -2100,6 +2137,46 @@ getinfo_helper_events(control_connection_t *control_conn, return -1; } *answer = bridge_stats; + } else if (!strcmp(question, "status/fresh-relay-descs")) { + if (!server_mode(get_options())) { + *errmsg = "Only relays have descriptors"; + return -1; + } + routerinfo_t *r; + extrainfo_t *e; + if (router_build_fresh_descriptor(&r, &e) < 0) { + *errmsg = "Error generating descriptor"; + return -1; + } + size_t size = r->cache_info.signed_descriptor_len + 1; + if (e) { + size += e->cache_info.signed_descriptor_len + 1; + } + tor_assert(r->cache_info.signed_descriptor_len); + char *descs = tor_malloc(size); + char *cp = descs; + memcpy(cp, signed_descriptor_get_body(&r->cache_info), + r->cache_info.signed_descriptor_len); + cp += r->cache_info.signed_descriptor_len - 1; + if (e) { + if (cp[0] == '\0') { + cp[0] = '\n'; + } else if (cp[0] != '\n') { + cp[1] = '\n'; + cp++; + } + memcpy(cp, signed_descriptor_get_body(&e->cache_info), + e->cache_info.signed_descriptor_len); + cp += e->cache_info.signed_descriptor_len - 1; + } + if (cp[0] == '\n') { + cp[0] = '\0'; + } else if (cp[0] != '\0') { + cp[1] = '\0'; + } + *answer = descs; + routerinfo_free(r); + extrainfo_free(e); } else { return 0; } @@ -2107,6 +2184,51 @@ getinfo_helper_events(control_connection_t *control_conn, return 0; } +/** Implementation helper for GETINFO: knows how to enumerate hidden services + * created via the control port. */ +static int +getinfo_helper_onions(control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg) +{ + smartlist_t *onion_list = NULL; + + if (!strcmp(question, "onions/current")) { + onion_list = control_conn->ephemeral_onion_services; + } else if (!strcmp(question, "onions/detached")) { + onion_list = detached_onion_services; + } else { + return 0; + } + if (!onion_list || smartlist_len(onion_list) == 0) { + *errmsg = "No onion services of the specified type."; + return -1; + } + *answer = smartlist_join_strings(onion_list, "\r\n", 0, NULL); + + return 0; +} + +/** Implementation helper for GETINFO: answers queries about network + * liveness. */ +static int +getinfo_helper_liveness(control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg) +{ + (void)control_conn; + (void)errmsg; + if (strcmp(question, "network-liveness") == 0) { + if (get_cached_network_liveness()) { + *answer = tor_strdup("up"); + } else { + *answer = tor_strdup("down"); + } + } + + return 0; +} + /** Callback function for GETINFO: on a given control connection, try to * answer the question <b>q</b> and store the newly-allocated answer in * *<b>a</b>. If an internal error occurs, return -1 and optionally set @@ -2176,6 +2298,8 @@ static const getinfo_item_t getinfo_items[] = { PREFIX("md/id/", dir, "Microdescriptors by ID"), PREFIX("md/name/", dir, "Microdescriptors by name"), PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."), + PREFIX("hs/client/desc/id", dir, + "Hidden Service descriptor in client's cache by onion."), PREFIX("net/listeners/", listeners, "Bound addresses by type"), ITEM("ns/all", networkstatus, "Brief summary of router status (v2 directory format)"), @@ -2189,6 +2313,8 @@ static const getinfo_item_t getinfo_items[] = { "Information about and from the ns consensus."), ITEM("network-status", dir, "Brief summary of router status (v1 directory format)"), + ITEM("network-liveness", liveness, + "Current opinion on whether the network is live"), ITEM("circuit-status", events, "List of current circuits originating here."), ITEM("stream-status", events,"List of current streams."), ITEM("orconn-status", events, "A list of current OR connections."), @@ -2210,6 +2336,8 @@ static const getinfo_item_t getinfo_items[] = { "The last bootstrap phase status event that Tor sent."), DOC("status/clients-seen", "Breakdown of client countries seen by a bridge."), + DOC("status/fresh-relay-descs", + "A fresh relay/ei descriptor pair for Tor's current state. Not stored."), DOC("status/version/recommended", "List of currently recommended versions."), DOC("status/version/current", "Status of the current version."), DOC("status/version/num-versioning", "Number of versioning authorities."), @@ -2239,6 +2367,10 @@ static const getinfo_item_t getinfo_items[] = { ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"), ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"), PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"), + ITEM("onions/current", onions, + "Onion services owned by the current control connection."), + ITEM("onions/detached", onions, + "Onion services detached from the control connection."), { NULL, NULL, NULL, 0 } }; @@ -2733,12 +2865,14 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len, uint8_t purpose = ROUTER_PURPOSE_GENERAL; int cache = 0; /* eventually, we may switch this to 1 */ - char *cp = memchr(body, '\n', len); + const char *cp = memchr(body, '\n', len); smartlist_t *args = smartlist_new(); tor_assert(cp); - *cp++ = '\0'; + ++cp; - smartlist_split_string(args, body, " ", + char *cmdline = tor_memdup_nulterm(body, cp-body); + + smartlist_split_string(args, cmdline, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); SMARTLIST_FOREACH_BEGIN(args, char *, option) { if (!strcasecmpstart(option, "purpose=")) { @@ -2787,6 +2921,7 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len, done: SMARTLIST_FOREACH(args, char *, arg, tor_free(arg)); smartlist_free(args); + tor_free(cmdline); return 0; } @@ -3102,8 +3237,8 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len, tor_free(client_nonce); return -1; } - - tor_assert(!crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN)); + const int fail = crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN); + tor_assert(!fail); /* Now compute and send the server-to-controller response, and the * server's nonce. */ @@ -3211,6 +3346,570 @@ handle_control_dropguards(control_connection_t *conn, return 0; } +/** Implementation for the HSFETCH command. */ +static int +handle_control_hsfetch(control_connection_t *conn, uint32_t len, + const char *body) +{ + int i; + char digest[DIGEST_LEN], *hsaddress = NULL, *arg1 = NULL, *desc_id = NULL; + smartlist_t *args = NULL, *hsdirs = NULL; + (void) len; /* body is nul-terminated; it's safe to ignore the length */ + static const char *hsfetch_command = "HSFETCH"; + static const char *v2_str = "v2-"; + const size_t v2_str_len = strlen(v2_str); + rend_data_t *rend_query = NULL; + + /* Make sure we have at least one argument, the HSAddress. */ + args = getargs_helper(hsfetch_command, conn, body, 1, -1); + if (!args) { + goto exit; + } + + /* Extract the first argument (either HSAddress or DescID). */ + arg1 = smartlist_get(args, 0); + /* Test if it's an HS address without the .onion part. */ + if (rend_valid_service_id(arg1)) { + hsaddress = arg1; + } else if (strcmpstart(arg1, v2_str) == 0 && + rend_valid_descriptor_id(arg1 + v2_str_len) && + base32_decode(digest, sizeof(digest), arg1 + v2_str_len, + REND_DESC_ID_V2_LEN_BASE32) == 0) { + /* We have a well formed version 2 descriptor ID. Keep the decoded value + * of the id. */ + desc_id = digest; + } else { + connection_printf_to_buf(conn, "513 Unrecognized \"%s\"\r\n", + arg1); + goto done; + } + + static const char *opt_server = "SERVER="; + + /* Skip first argument because it's the HSAddress or DescID. */ + for (i = 1; i < smartlist_len(args); ++i) { + const char *arg = smartlist_get(args, i); + const node_t *node; + + if (!strcasecmpstart(arg, opt_server)) { + const char *server; + + server = arg + strlen(opt_server); + node = node_get_by_hex_id(server); + if (!node) { + connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n", + server); + goto done; + } + if (!hsdirs) { + /* Stores routerstatus_t object for each specified server. */ + hsdirs = smartlist_new(); + } + /* Valid server, add it to our local list. */ + smartlist_add(hsdirs, node->rs); + } else { + connection_printf_to_buf(conn, "513 Unexpected argument \"%s\"\r\n", + arg); + goto done; + } + } + + rend_query = rend_data_client_create(hsaddress, desc_id, NULL, + REND_NO_AUTH); + if (rend_query == NULL) { + connection_printf_to_buf(conn, "551 Error creating the HS query\r\n"); + goto done; + } + + /* Using a descriptor ID, we force the user to provide at least one + * hsdir server using the SERVER= option. */ + if (desc_id && (!hsdirs || !smartlist_len(hsdirs))) { + connection_printf_to_buf(conn, "512 %s option is required\r\n", + opt_server); + goto done; + } + + /* We are about to trigger HSDir fetch so send the OK now because after + * that 650 event(s) are possible so better to have the 250 OK before them + * to avoid out of order replies. */ + send_control_done(conn); + + /* Trigger the fetch using the built rend query and possibly a list of HS + * directory to use. This function ignores the client cache thus this will + * always send a fetch command. */ + rend_client_fetch_v2_desc(rend_query, hsdirs); + + done: + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + /* Contains data pointer that we don't own thus no cleanup. */ + smartlist_free(hsdirs); + rend_data_free(rend_query); + exit: + return 0; +} + +/** Implementation for the HSPOST command. */ +static int +handle_control_hspost(control_connection_t *conn, + uint32_t len, + const char *body) +{ + static const char *opt_server = "SERVER="; + smartlist_t *args = smartlist_new(); + smartlist_t *hs_dirs = NULL; + const char *encoded_desc = body; + size_t encoded_desc_len = len; + + char *cp = memchr(body, '\n', len); + char *argline = tor_strndup(body, cp-body); + + /* If any SERVER= options were specified, try parse the options line */ + if (!strcasecmpstart(argline, opt_server)) { + /* encoded_desc begins after a newline character */ + cp = cp + 1; + encoded_desc = cp; + encoded_desc_len = len-(cp-body); + + smartlist_split_string(args, argline, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + SMARTLIST_FOREACH_BEGIN(args, const char *, arg) { + if (!strcasecmpstart(arg, opt_server)) { + const char *server = arg + strlen(opt_server); + const node_t *node = node_get_by_hex_id(server); + + if (!node || !node->rs) { + connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n", + server); + goto done; + } + if (!node->rs->is_hs_dir) { + connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir" + "\r\n", server); + goto done; + } + /* Valid server, add it to our local list. */ + if (!hs_dirs) + hs_dirs = smartlist_new(); + smartlist_add(hs_dirs, node->rs); + } else { + connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n", + arg); + goto done; + } + } SMARTLIST_FOREACH_END(arg); + } + + /* Read the dot encoded descriptor, and parse it. */ + rend_encoded_v2_service_descriptor_t *desc = + tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t)); + read_escaped_data(encoded_desc, encoded_desc_len, &desc->desc_str); + + rend_service_descriptor_t *parsed = NULL; + char *intro_content = NULL; + size_t intro_size; + size_t encoded_size; + const char *next_desc; + if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, desc->desc_str, 1)) { + /* Post the descriptor. */ + char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; + if (!rend_get_service_id(parsed->pk, serviceid)) { + smartlist_t *descs = smartlist_new(); + smartlist_add(descs, desc); + + /* We are about to trigger HS descriptor upload so send the OK now + * because after that 650 event(s) are possible so better to have the + * 250 OK before them to avoid out of order replies. */ + send_control_done(conn); + + /* Trigger the descriptor upload */ + directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0); + smartlist_free(descs); + } + + rend_service_descriptor_free(parsed); + } else { + connection_printf_to_buf(conn, "554 Invalid descriptor\r\n"); + } + + tor_free(intro_content); + rend_encoded_v2_service_descriptor_free(desc); + done: + tor_free(argline); + smartlist_free(hs_dirs); /* Contents belong to the rend service code. */ + SMARTLIST_FOREACH(args, char *, arg, tor_free(arg)); + smartlist_free(args); + return 0; +} + +/** Called when we get a ADD_ONION command; parse the body, and set up + * the new ephemeral Onion Service. */ +static int +handle_control_add_onion(control_connection_t *conn, + uint32_t len, + const char *body) +{ + smartlist_t *args; + size_t arg_len; + (void) len; /* body is nul-terminated; it's safe to ignore the length */ + args = getargs_helper("ADD_ONION", conn, body, 2, -1); + if (!args) + return 0; + arg_len = smartlist_len(args); + + /* Parse all of the arguments that do not involve handling cryptographic + * material first, since there's no reason to touch that at all if any of + * the other arguments are malformed. + */ + smartlist_t *port_cfgs = smartlist_new(); + int discard_pk = 0; + int detach = 0; + int max_streams = 0; + int max_streams_close_circuit = 0; + for (size_t i = 1; i < arg_len; i++) { + static const char *port_prefix = "Port="; + static const char *flags_prefix = "Flags="; + static const char *max_s_prefix = "MaxStreams="; + + const char *arg = smartlist_get(args, i); + if (!strcasecmpstart(arg, port_prefix)) { + /* "Port=VIRTPORT[,TARGET]". */ + const char *port_str = arg + strlen(port_prefix); + + rend_service_port_config_t *cfg = + rend_service_parse_port_config(port_str, ",", NULL); + if (!cfg) { + connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n"); + goto out; + } + smartlist_add(port_cfgs, cfg); + } else if (!strcasecmpstart(arg, max_s_prefix)) { + /* "MaxStreams=[0..65535]". */ + const char *max_s_str = arg + strlen(max_s_prefix); + int ok = 0; + max_streams = (int)tor_parse_long(max_s_str, 10, 0, 65535, &ok, NULL); + if (!ok) { + connection_printf_to_buf(conn, "512 Invalid MaxStreams\r\n"); + goto out; + } + } else if (!strcasecmpstart(arg, flags_prefix)) { + /* "Flags=Flag[,Flag]", where Flag can be: + * * 'DiscardPK' - If tor generates the keypair, do not include it in + * the response. + * * 'Detach' - Do not tie this onion service to any particular control + * connection. + * * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is + * exceeded. + */ + static const char *discard_flag = "DiscardPK"; + static const char *detach_flag = "Detach"; + static const char *max_s_close_flag = "MaxStreamsCloseCircuit"; + + smartlist_t *flags = smartlist_new(); + int bad = 0; + + smartlist_split_string(flags, arg + strlen(flags_prefix), ",", + SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(flags) < 1) { + connection_printf_to_buf(conn, "512 Invalid 'Flags' argument\r\n"); + bad = 1; + } + SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) + { + if (!strcasecmp(flag, discard_flag)) { + discard_pk = 1; + } else if (!strcasecmp(flag, detach_flag)) { + detach = 1; + } else if (!strcasecmp(flag, max_s_close_flag)) { + max_streams_close_circuit = 1; + } else { + connection_printf_to_buf(conn, + "512 Invalid 'Flags' argument: %s\r\n", + escaped(flag)); + bad = 1; + break; + } + } SMARTLIST_FOREACH_END(flag); + SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp)); + smartlist_free(flags); + if (bad) + goto out; + } else { + connection_printf_to_buf(conn, "513 Invalid argument\r\n"); + goto out; + } + } + if (smartlist_len(port_cfgs) == 0) { + connection_printf_to_buf(conn, "512 Missing 'Port' argument\r\n"); + goto out; + } + + /* Parse the "keytype:keyblob" argument. */ + crypto_pk_t *pk = NULL; + const char *key_new_alg = NULL; + char *key_new_blob = NULL; + char *err_msg = NULL; + + pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, + &key_new_alg, &key_new_blob, + &err_msg); + if (!pk) { + if (err_msg) { + connection_write_str_to_buf(err_msg, conn); + tor_free(err_msg); + } + goto out; + } + tor_assert(!err_msg); + + /* Create the HS, using private key pk, and port config port_cfg. + * rend_service_add_ephemeral() will take ownership of pk and port_cfg, + * regardless of success/failure. + */ + char *service_id = NULL; + int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, + max_streams_close_circuit, + &service_id); + port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */ + switch (ret) { + case RSAE_OKAY: + { + char *buf = NULL; + tor_assert(service_id); + if (key_new_alg) { + tor_assert(key_new_blob); + tor_asprintf(&buf, + "250-ServiceID=%s\r\n" + "250-PrivateKey=%s:%s\r\n" + "250 OK\r\n", + service_id, + key_new_alg, + key_new_blob); + } else { + tor_asprintf(&buf, + "250-ServiceID=%s\r\n" + "250 OK\r\n", + service_id); + } + if (detach) { + if (!detached_onion_services) + detached_onion_services = smartlist_new(); + smartlist_add(detached_onion_services, service_id); + } else { + if (!conn->ephemeral_onion_services) + conn->ephemeral_onion_services = smartlist_new(); + smartlist_add(conn->ephemeral_onion_services, service_id); + } + + connection_write_str_to_buf(buf, conn); + memwipe(buf, 0, strlen(buf)); + tor_free(buf); + break; + } + case RSAE_BADPRIVKEY: + connection_printf_to_buf(conn, "551 Failed to generate onion address\r\n"); + break; + case RSAE_ADDREXISTS: + connection_printf_to_buf(conn, "550 Onion address collision\r\n"); + break; + case RSAE_BADVIRTPORT: + connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n"); + break; + case RSAE_INTERNAL: /* FALLSTHROUGH */ + default: + connection_printf_to_buf(conn, "551 Failed to add Onion Service\r\n"); + } + if (key_new_blob) { + memwipe(key_new_blob, 0, strlen(key_new_blob)); + tor_free(key_new_blob); + } + + out: + if (port_cfgs) { + SMARTLIST_FOREACH(port_cfgs, rend_service_port_config_t*, p, + rend_service_port_config_free(p)); + smartlist_free(port_cfgs); + } + + SMARTLIST_FOREACH(args, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(args); + return 0; +} + +/** Helper function to handle parsing the KeyType:KeyBlob argument to the + * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated + * and the private key not discarded, the algorithm and serialized private key, + * or NULL and an optional control protocol error message on failure. The + * caller is responsible for freeing the returned key_new_blob and err_msg. + * + * Note: The error messages returned are deliberately vague to avoid echoing + * key material. + */ +STATIC crypto_pk_t * +add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, char **key_new_blob_out, + char **err_msg_out) +{ + smartlist_t *key_args = smartlist_new(); + crypto_pk_t *pk = NULL; + const char *key_new_alg = NULL; + char *key_new_blob = NULL; + char *err_msg = NULL; + int ok = 0; + + smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(key_args) != 2) { + err_msg = tor_strdup("512 Invalid key type/blob\r\n"); + goto err; + } + + /* The format is "KeyType:KeyBlob". */ + static const char *key_type_new = "NEW"; + static const char *key_type_best = "BEST"; + static const char *key_type_rsa1024 = "RSA1024"; + + const char *key_type = smartlist_get(key_args, 0); + const char *key_blob = smartlist_get(key_args, 1); + + if (!strcasecmp(key_type_rsa1024, key_type)) { + /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */ + pk = crypto_pk_base64_decode(key_blob, strlen(key_blob)); + if (!pk) { + err_msg = tor_strdup("512 Failed to decode RSA key\r\n"); + goto err; + } + if (crypto_pk_num_bits(pk) != PK_BYTES*8) { + err_msg = tor_strdup("512 Invalid RSA key size\r\n"); + goto err; + } + } else if (!strcasecmp(key_type_new, key_type)) { + /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */ + if (!strcasecmp(key_type_rsa1024, key_blob) || + !strcasecmp(key_type_best, key_blob)) { + /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */ + pk = crypto_pk_new(); + if (crypto_pk_generate_key(pk)) { + tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", + key_type_rsa1024); + goto err; + } + if (!discard_pk) { + if (crypto_pk_base64_encode(pk, &key_new_blob)) { + tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", + key_type_rsa1024); + goto err; + } + key_new_alg = key_type_rsa1024; + } + } else { + err_msg = tor_strdup("513 Invalid key type\r\n"); + goto err; + } + } else { + err_msg = tor_strdup("513 Invalid key type\r\n"); + goto err; + } + + /* Succeded in loading or generating a private key. */ + tor_assert(pk); + ok = 1; + + err: + SMARTLIST_FOREACH(key_args, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(key_args); + + if (!ok) { + crypto_pk_free(pk); + pk = NULL; + } + if (err_msg_out) { + *err_msg_out = err_msg; + } else { + tor_free(err_msg); + } + *key_new_alg_out = key_new_alg; + *key_new_blob_out = key_new_blob; + + return pk; +} + +/** Called when we get a DEL_ONION command; parse the body, and remove + * the existing ephemeral Onion Service. */ +static int +handle_control_del_onion(control_connection_t *conn, + uint32_t len, + const char *body) +{ + smartlist_t *args; + (void) len; /* body is nul-terminated; it's safe to ignore the length */ + args = getargs_helper("DEL_ONION", conn, body, 1, 1); + if (!args) + return 0; + + const char *service_id = smartlist_get(args, 0); + if (!rend_valid_service_id(service_id)) { + connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n"); + goto out; + } + + /* Determine if the onion service belongs to this particular control + * connection, or if it is in the global list of detached services. If it + * is in neither, either the service ID is invalid in some way, or it + * explicitly belongs to a different control connection, and an error + * should be returned. + */ + smartlist_t *services[2] = { + conn->ephemeral_onion_services, + detached_onion_services + }; + smartlist_t *onion_services = NULL; + int idx = -1; + for (size_t i = 0; i < ARRAY_LENGTH(services); i++) { + idx = smartlist_string_pos(services[i], service_id); + if (idx != -1) { + onion_services = services[i]; + break; + } + } + if (onion_services == NULL) { + connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n"); + } else { + int ret = rend_service_del_ephemeral(service_id); + if (ret) { + /* This should *NEVER* fail, since the service is on either the + * per-control connection list, or the global one. + */ + log_warn(LD_BUG, "Failed to remove Onion Service %s.", + escaped(service_id)); + tor_fragile_assert(); + } + + /* Remove/scrub the service_id from the appropriate list. */ + char *cp = smartlist_get(onion_services, idx); + smartlist_del(onion_services, idx); + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + + send_control_done(conn); + } + + out: + SMARTLIST_FOREACH(args, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(args); + return 0; +} + /** Called when <b>conn</b> has no more bytes left on its outbuf. */ int connection_control_finished_flushing(control_connection_t *conn) @@ -3257,6 +3956,15 @@ connection_control_closed(control_connection_t *conn) conn->event_mask = 0; control_update_global_event_mask(); + /* Close all ephemeral Onion Services if any. + * The list and it's contents are scrubbed/freed in connection_free_. + */ + if (conn->ephemeral_onion_services) { + SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, { + rend_service_del_ephemeral(cp); + }); + } + if (conn->is_owning_control_connection) { lost_owning_controller("connection", "closed"); } @@ -3508,6 +4216,22 @@ connection_control_process_inbuf(control_connection_t *conn) } else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) { if (handle_control_dropguards(conn, cmd_data_len, args)) return -1; + } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) { + if (handle_control_hsfetch(conn, cmd_data_len, args)) + return -1; + } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) { + if (handle_control_hspost(conn, cmd_data_len, args)) + return -1; + } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) { + int ret = handle_control_add_onion(conn, cmd_data_len, args); + memwipe(args, 0, cmd_data_len); /* Scrub the private key. */ + if (ret) + return -1; + } else if (!strcasecmp(conn->incoming_cmd, "DEL_ONION")) { + int ret = handle_control_del_onion(conn, cmd_data_len, args); + memwipe(args, 0, cmd_data_len); /* Scrub the service id/pk. */ + if (ret) + return -1; } else { connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n", conn->incoming_cmd); @@ -4415,6 +5139,52 @@ control_event_or_authdir_new_descriptor(const char *action, return 0; } +/** Cached liveness for network liveness events and GETINFO + */ + +static int network_is_live = 0; + +static int +get_cached_network_liveness(void) +{ + return network_is_live; +} + +static void +set_cached_network_liveness(int liveness) +{ + network_is_live = liveness; +} + +/** The network liveness has changed; this is called from circuitstats.c + * whenever we receive a cell, or when timeout expires and we assume the + * network is down. */ +int +control_event_network_liveness_update(int liveness) +{ + if (liveness > 0) { + if (get_cached_network_liveness() <= 0) { + /* Update cached liveness */ + set_cached_network_liveness(1); + log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS UP"); + send_control_event_string(EVENT_NETWORK_LIVENESS, ALL_FORMATS, + "650 NETWORK_LIVENESS UP\r\n"); + } + /* else was already live, no-op */ + } else { + if (get_cached_network_liveness() > 0) { + /* Update cached liveness */ + set_cached_network_liveness(0); + log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS DOWN"); + send_control_event_string(EVENT_NETWORK_LIVENESS, ALL_FORMATS, + "650 NETWORK_LIVENESS DOWN\r\n"); + } + /* else was already dead, no-op */ + } + + return 0; +} + /** Helper function for NS-style events. Constructs and sends an event * of type <b>event</b> with string <b>event_string</b> out of the set of * networkstatuses <b>statuses</b>. Currently it is used for NS events @@ -5176,6 +5946,29 @@ node_describe_longname_by_id,(const char *id_digest)) return longname; } +/** Return either the onion address if the given pointer is a non empty + * string else the unknown string. */ +static const char * +rend_hsaddress_str_or_unknown(const char *onion_address) +{ + static const char *str_unknown = "UNKNOWN"; + const char *str_ret = str_unknown; + + /* No valid pointer, unknown it is. */ + if (!onion_address) { + goto end; + } + /* Empty onion address thus we don't know, unknown it is. */ + if (onion_address[0] == '\0') { + goto end; + } + /* All checks are good so return the given onion address. */ + str_ret = onion_address; + + end: + return str_ret; +} + /** send HS_DESC requested event. * * <b>rend_query</b> is used to fetch requested onion address and auth type. @@ -5196,12 +5989,75 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query, send_control_event(EVENT_HS_DESC, ALL_FORMATS, "650 HS_DESC REQUESTED %s %s %s %s\r\n", - rend_query->onion_address, + rend_hsaddress_str_or_unknown(rend_query->onion_address), rend_auth_type_to_string(rend_query->auth_type), node_describe_longname_by_id(id_digest), desc_id_base32); } +/** For an HS descriptor query <b>rend_data</b>, using the + * <b>onion_address</b> and HSDir fingerprint <b>hsdir_fp</b>, find out + * which descriptor ID in the query is the right one. + * + * Return a pointer of the binary descriptor ID found in the query's object + * or NULL if not found. */ +static const char * +get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp) +{ + int replica; + const char *desc_id = NULL; + + /* Possible if the fetch was done using a descriptor ID. This means that + * the HSFETCH command was used. */ + if (!tor_digest_is_zero(rend_data->desc_id_fetch)) { + desc_id = rend_data->desc_id_fetch; + goto end; + } + + /* OK, we have an onion address so now let's find which descriptor ID + * is the one associated with the HSDir fingerprint. */ + for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; + replica++) { + const char *digest = rend_data->descriptor_id[replica]; + + SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) { + if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) { + /* Found it! This descriptor ID is the right one. */ + desc_id = digest; + goto end; + } + } SMARTLIST_FOREACH_END(fingerprint); + } + +end: + return desc_id; +} + +/** send HS_DESC upload event. + * + * <b>service_id</b> is the descriptor onion address. + * <b>hs_dir</b> is the description of contacting hs directory. + * <b>desc_id_base32</b> is the ID of requested hs descriptor. + */ +void +control_event_hs_descriptor_upload(const char *service_id, + const char *id_digest, + const char *desc_id_base32) +{ + if (!service_id || !id_digest || !desc_id_base32) { + log_warn(LD_BUG, "Called with service_digest==%p, " + "desc_id_base32==%p, id_digest==%p", service_id, + desc_id_base32, id_digest); + return; + } + + send_control_event(EVENT_HS_DESC, ALL_FORMATS, + "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n", + service_id, + node_describe_longname_by_id(id_digest), + desc_id_base32); +} + /** send HS_DESC event after got response from hs directory. * * NOTE: this is an internal function used by following functions: @@ -5212,27 +6068,77 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query, */ void control_event_hs_descriptor_receive_end(const char *action, - const rend_data_t *rend_query, + const char *onion_address, + const rend_data_t *rend_data, const char *id_digest, const char *reason) { + char *desc_id_field = NULL; char *reason_field = NULL; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + const char *desc_id = NULL; - if (!action || !rend_query || !id_digest) { - log_warn(LD_BUG, "Called with action==%p, rend_query==%p, " - "id_digest==%p", action, rend_query, id_digest); + if (!action || !id_digest || !rend_data || !onion_address) { + log_warn(LD_BUG, "Called with action==%p, id_digest==%p, " + "rend_data==%p, onion_address==%p", action, id_digest, + rend_data, onion_address); return; } + desc_id = get_desc_id_from_query(rend_data, id_digest); + if (desc_id != NULL) { + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + if (reason) { tor_asprintf(&reason_field, " REASON=%s", reason); } send_control_event(EVENT_HS_DESC, ALL_FORMATS, - "650 HS_DESC %s %s %s %s%s\r\n", + "650 HS_DESC %s %s %s %s%s%s\r\n", + action, + rend_hsaddress_str_or_unknown(onion_address), + rend_auth_type_to_string(rend_data->auth_type), + node_describe_longname_by_id(id_digest), + desc_id_field ? desc_id_field : "", + reason_field ? reason_field : ""); + + tor_free(desc_id_field); + tor_free(reason_field); +} + +/** send HS_DESC event after got response from hs directory. + * + * NOTE: this is an internal function used by following functions: + * control_event_hs_descriptor_uploaded + * control_event_hs_descriptor_upload_failed + * + * So do not call this function directly. + */ +void +control_event_hs_descriptor_upload_end(const char *action, + const char *id_digest, + const char *reason) +{ + char *reason_field = NULL; + + if (!action || !id_digest) { + log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action, + id_digest); + return; + } + + if (reason) { + tor_asprintf(&reason_field, " REASON=%s", reason); + } + + send_control_event(EVENT_HS_DESC, ALL_FORMATS, + "650 HS_DESC %s UNKNOWN UNKNOWN %s%s\r\n", action, - rend_query->onion_address, - rend_auth_type_to_string(rend_query->auth_type), node_describe_longname_by_id(id_digest), reason_field ? reason_field : ""); @@ -5241,19 +6147,35 @@ control_event_hs_descriptor_receive_end(const char *action, /** send HS_DESC RECEIVED event * - * called when a we successfully received a hidden service descriptor. + * called when we successfully received a hidden service descriptor. */ void -control_event_hs_descriptor_received(const rend_data_t *rend_query, +control_event_hs_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, const char *id_digest) { - if (!rend_query || !id_digest) { - log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p", - rend_query, id_digest); + if (!rend_data || !id_digest || !onion_address) { + log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, " + "onion_address==%p", rend_data, id_digest, onion_address); + return; + } + control_event_hs_descriptor_receive_end("RECEIVED", onion_address, + rend_data, id_digest, NULL); +} + +/** send HS_DESC UPLOADED event + * + * called when we successfully uploaded a hidden service descriptor. + */ +void +control_event_hs_descriptor_uploaded(const char *id_digest) +{ + if (!id_digest) { + log_warn(LD_BUG, "Called with id_digest==%p", + id_digest); return; } - control_event_hs_descriptor_receive_end("RECEIVED", rend_query, - id_digest, NULL); + control_event_hs_descriptor_upload_end("UPLOADED", id_digest, NULL); } /** Send HS_DESC event to inform controller that query <b>rend_query</b> @@ -5262,17 +6184,68 @@ control_event_hs_descriptor_received(const rend_data_t *rend_query, * field. */ void -control_event_hs_descriptor_failed(const rend_data_t *rend_query, +control_event_hs_descriptor_failed(const rend_data_t *rend_data, const char *id_digest, const char *reason) { - if (!rend_query || !id_digest) { - log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p", - rend_query, id_digest); + if (!rend_data || !id_digest) { + log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p", + rend_data, id_digest); return; } - control_event_hs_descriptor_receive_end("FAILED", rend_query, - id_digest, reason); + control_event_hs_descriptor_receive_end("FAILED", + rend_data->onion_address, + rend_data, id_digest, reason); +} + +/** send HS_DESC_CONTENT event after completion of a successful fetch from + * hs directory. */ +void +control_event_hs_descriptor_content(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *content) +{ + static const char *event_name = "HS_DESC_CONTENT"; + char *esc_content = NULL; + + if (!onion_address || !desc_id || !hsdir_id_digest) { + log_warn(LD_BUG, "Called with onion_address==%p, desc_id==%p, " + "hsdir_id_digest==%p", onion_address, desc_id, hsdir_id_digest); + return; + } + + if (content == NULL) { + /* Point it to empty content so it can still be escaped. */ + content = ""; + } + write_escaped_data(content, strlen(content), &esc_content); + + send_control_event(EVENT_HS_DESC_CONTENT, ALL_FORMATS, + "650+%s %s %s %s\r\n%s650 OK\r\n", + event_name, + rend_hsaddress_str_or_unknown(onion_address), + desc_id, + node_describe_longname_by_id(hsdir_id_digest), + esc_content); + tor_free(esc_content); +} + +/** Send HS_DESC event to inform controller upload of hidden service + * descriptor identified by <b>id_digest</b> failed. If <b>reason</b> + * is not NULL, add it to REASON= field. + */ +void +control_event_hs_descriptor_upload_failed(const char *id_digest, + const char *reason) +{ + if (!id_digest) { + log_warn(LD_BUG, "Called with id_digest==%p", + id_digest); + return; + } + control_event_hs_descriptor_upload_end("UPLOAD_FAILED", + id_digest, reason); } /** Free any leftover allocated memory of the control.c subsystem. */ @@ -5281,6 +6254,10 @@ control_free_all(void) { if (authentication_cookie) /* Free the auth cookie */ tor_free(authentication_cookie); + if (detached_onion_services) { /* Free the detached onion services */ + SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp)); + smartlist_free(detached_onion_services); + } } #ifdef TOR_UNIT_TESTS diff --git a/src/or/control.h b/src/or/control.h index 47a601817a..2d02443834 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -67,6 +67,7 @@ int control_event_or_authdir_new_descriptor(const char *action, size_t desclen, const char *msg); int control_event_my_descriptor_changed(void); +int control_event_network_liveness_update(int liveness); int control_event_networkstatus_changed(smartlist_t *statuses); int control_event_newconsensus(const networkstatus_t *consensus); @@ -106,15 +107,30 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest)); void control_event_hs_descriptor_requested(const rend_data_t *rend_query, const char *desc_id_base32, const char *hs_dir); +void control_event_hs_descriptor_upload(const char *service_id, + const char *desc_id_base32, + const char *hs_dir); void control_event_hs_descriptor_receive_end(const char *action, - const rend_data_t *rend_query, - const char *hs_dir, - const char *reason); -void control_event_hs_descriptor_received(const rend_data_t *rend_query, - const char *hs_dir); -void control_event_hs_descriptor_failed(const rend_data_t *rend_query, - const char *hs_dir, + const char *onion_address, + const rend_data_t *rend_data, + const char *id_digest, + const char *reason); +void control_event_hs_descriptor_upload_end(const char *action, + const char *hs_dir, + const char *reason); +void control_event_hs_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *id_digest); +void control_event_hs_descriptor_uploaded(const char *hs_dir); +void control_event_hs_descriptor_failed(const rend_data_t *rend_data, + const char *id_digest, const char *reason); +void control_event_hs_descriptor_upload_failed(const char *hs_dir, + const char *reason); +void control_event_hs_descriptor_content(const char *onion_address, + const char *desc_id, + const char *hsdir_fp, + const char *content); void control_free_all(void); @@ -123,6 +139,7 @@ void control_free_all(void); * because it is used both as a list of v0 event types, and as indices * into the bitfield to determine which controllers want which events. */ +/* This bitfield has no event zero 0x0000 */ #define EVENT_MIN_ 0x0001 #define EVENT_CIRCUIT_STATUS 0x0001 #define EVENT_STREAM_STATUS 0x0002 @@ -157,10 +174,32 @@ void control_free_all(void); #define EVENT_CIRC_BANDWIDTH_USED 0x001D #define EVENT_TRANSPORT_LAUNCHED 0x0020 #define EVENT_HS_DESC 0x0021 -#define EVENT_MAX_ 0x0021 -/* If EVENT_MAX_ ever hits 0x003F, we need to make the mask into a +#define EVENT_HS_DESC_CONTENT 0x0022 +#define EVENT_NETWORK_LIVENESS 0x0023 +#define EVENT_MAX_ 0x0023 + +/* sizeof(control_connection_t.event_mask) in bits, currently a uint64_t */ +#define EVENT_CAPACITY_ 0x0040 + +/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a * different structure, as it can only handle a maximum left shift of 1<<63. */ +#if EVENT_MAX_ >= EVENT_CAPACITY_ +#error control_connection_t.event_mask has an event greater than its capacity +#endif + +#define EVENT_MASK_(e) (((uint64_t)1)<<(e)) + +#define EVENT_MASK_NONE_ ((uint64_t)0x0) + +#define EVENT_MASK_ABOVE_MIN_ ((~((uint64_t)0x0)) << EVENT_MIN_) +#define EVENT_MASK_BELOW_MAX_ ((~((uint64_t)0x0)) \ + >> (EVENT_CAPACITY_ - EVENT_MAX_ \ + - EVENT_MIN_)) + +#define EVENT_MASK_ALL_ (EVENT_MASK_ABOVE_MIN_ \ + & EVENT_MASK_BELOW_MAX_) + /* Used only by control.c and test.c */ STATIC size_t write_escaped_data(const char *data, size_t len, char **out); STATIC size_t read_escaped_data(const char *data, size_t len, char **out); @@ -204,6 +243,11 @@ void append_cell_stats_by_command(smartlist_t *event_parts, void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); STATIC char *get_bw_samples(void); + +STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, + char **key_new_blob_out, + char **err_msg_out); #endif #endif diff --git a/src/or/directory.c b/src/or/directory.c index d2b6b86f6d..9fe3052774 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -2102,14 +2102,23 @@ connection_dir_client_reached_eof(dir_connection_t *conn) control_event_hs_descriptor_failed(conn->rend_data, \ conn->identity_digest, \ reason) ) + #define SEND_HS_DESC_FAILED_CONTENT() ( \ + control_event_hs_descriptor_content(conn->rend_data->onion_address, \ + conn->requested_resource, \ + conn->identity_digest, \ + NULL) ) tor_assert(conn->rend_data); log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d " "(%s))", (int)body_len, status_code, escaped(reason)); switch (status_code) { case 200: + { + rend_cache_entry_t *entry = NULL; + switch (rend_cache_store_v2_desc_as_client(body, - conn->requested_resource, conn->rend_data)) { + conn->requested_resource, conn->rend_data, + &entry)) { case RCS_BADDESC: case RCS_NOTDIR: /* Impossible */ log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. " @@ -2117,25 +2126,41 @@ connection_dir_client_reached_eof(dir_connection_t *conn) /* We'll retry when connection_about_to_close_connection() * cleans this dir conn up. */ SEND_HS_DESC_FAILED_EVENT("BAD_DESC"); + SEND_HS_DESC_FAILED_CONTENT(); break; case RCS_OKAY: default: + { + char service_id[REND_SERVICE_ID_LEN_BASE32 + 1]; + /* Should never be NULL here for an OKAY returned code. */ + tor_assert(entry); + rend_get_service_id(entry->parsed->pk, service_id); + /* success. notify pending connections about this. */ log_info(LD_REND, "Successfully fetched v2 rendezvous " "descriptor."); - control_event_hs_descriptor_received(conn->rend_data, + control_event_hs_descriptor_received(service_id, + conn->rend_data, conn->identity_digest); + control_event_hs_descriptor_content(service_id, + conn->requested_resource, + conn->identity_digest, + body); conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2; - rend_client_desc_trynow(conn->rend_data->onion_address); + rend_client_desc_trynow(service_id); + memwipe(service_id, 0, sizeof(service_id)); break; + } } break; + } case 404: /* Not there. We'll retry when * connection_about_to_close_connection() cleans this conn up. */ log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: " "Retrying at another directory."); SEND_HS_DESC_FAILED_EVENT("NOT_FOUND"); + SEND_HS_DESC_FAILED_CONTENT(); break; case 400: log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " @@ -2143,6 +2168,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) "v2 rendezvous query? Retrying at another directory.", escaped(reason)); SEND_HS_DESC_FAILED_EVENT("QUERY_REJECTED"); + SEND_HS_DESC_FAILED_CONTENT(); break; default: log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " @@ -2152,11 +2178,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn) status_code, escaped(reason), conn->base_.address, conn->base_.port); SEND_HS_DESC_FAILED_EVENT("UNEXPECTED"); + SEND_HS_DESC_FAILED_CONTENT(); break; } } if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) { + #define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \ + control_event_hs_descriptor_upload_failed(conn->identity_digest, \ + reason) ) log_info(LD_REND,"Uploaded rendezvous descriptor (status %d " "(%s))", status_code, escaped(reason)); @@ -2165,17 +2195,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn) log_info(LD_REND, "Uploading rendezvous descriptor: finished with status " "200 (%s)", escaped(reason)); + control_event_hs_descriptor_uploaded(conn->identity_digest); break; case 400: log_warn(LD_REND,"http status 400 (%s) response from dirserver " "'%s:%d'. Malformed rendezvous descriptor?", escaped(reason), conn->base_.address, conn->base_.port); + SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED"); break; default: log_warn(LD_REND,"http status %d (%s) response unexpected (server " "'%s:%d').", status_code, escaped(reason), conn->base_.address, conn->base_.port); + SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED"); break; } } @@ -3066,7 +3099,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, /* Handle v2 rendezvous descriptor fetch request. */ const char *descp; const char *query = url + strlen("/tor/rendezvous2/"); - if (strlen(query) == REND_DESC_ID_V2_LEN_BASE32) { + if (rend_valid_descriptor_id(query)) { log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'", safe_str(escaped(query))); switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) { diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 7a5154dae5..3009026ee7 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -2244,7 +2244,8 @@ networkstatus_format_signatures(networkstatus_t *consensus, for_detached_signatures ? flavor_name : "", digest_name, id, sk); } - base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len); + base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len, + BASE64_ENCODE_MULTILINE); strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf)); smartlist_add(elements, tor_strdup(buf)); } SMARTLIST_FOREACH_END(sig); @@ -3459,7 +3460,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) char kbuf[128]; base64_encode(kbuf, sizeof(kbuf), (const char*)ri->onion_curve25519_pkey->public_key, - CURVE25519_PUBKEY_LEN); + CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); } diff --git a/src/or/dns.c b/src/or/dns.c index cc4a169422..db77d20783 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -24,7 +24,7 @@ #include "relay.h" #include "router.h" #include "ht.h" -#include "../common/sandbox.h" +#include "sandbox.h" #ifdef HAVE_EVENT2_DNS_H #include <event2/event.h> #include <event2/dns.h> diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 30108b6041..ebf675166b 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -141,8 +141,7 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node, } if (node) { - int is_dir = node_is_dir(node) && node->rs && - node->rs->version_supports_microdesc_cache; + int is_dir = node_is_dir(node); if (options->UseBridges && node_is_a_configured_bridge(node)) is_dir = 1; if (e->is_dir_cache != is_dir) { @@ -398,10 +397,10 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, entry->bad_since = 0; entry->can_retry = 1; } - entry->is_dir_cache = node->rs && - node->rs->version_supports_microdesc_cache; + entry->is_dir_cache = node_is_dir(node); if (get_options()->UseBridges && node_is_a_configured_bridge(node)) entry->is_dir_cache = 1; + return NULL; } } else if (!for_directory) { @@ -432,8 +431,7 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, node_describe(node)); strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname)); memcpy(entry->identity, node->identity, DIGEST_LEN); - entry->is_dir_cache = node_is_dir(node) && node->rs && - node->rs->version_supports_microdesc_cache; + entry->is_dir_cache = node_is_dir(node); if (get_options()->UseBridges && node_is_a_configured_bridge(node)) entry->is_dir_cache = 1; @@ -442,7 +440,8 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, * don't all select them on the same day, and b) avoid leaving a * precise timestamp in the state file about when we first picked * this guard. For details, see the Jan 2010 or-dev thread. */ - entry->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30); + time_t now = time(NULL); + entry->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now); entry->chosen_by_version = tor_strdup(VERSION); /* Are we picking this guard because all of our current guards are @@ -571,22 +570,6 @@ remove_obsolete_entry_guards(time_t now) } else if (tor_version_parse(ver, &v)) { msg = "does not seem to be from any recognized version of Tor"; version_is_bad = 1; - } else { - char *tor_ver = NULL; - tor_asprintf(&tor_ver, "Tor %s", ver); - if ((tor_version_as_new_as(tor_ver, "0.1.0.10-alpha") && - !tor_version_as_new_as(tor_ver, "0.1.2.16-dev")) || - (tor_version_as_new_as(tor_ver, "0.2.0.0-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.0.6-alpha")) || - /* above are bug 440; below are bug 1217 */ - (tor_version_as_new_as(tor_ver, "0.2.1.3-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.1.23")) || - (tor_version_as_new_as(tor_ver, "0.2.2.0-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.2.7-alpha"))) { - msg = "was selected without regard for guard bandwidth"; - version_is_bad = 1; - } - tor_free(tor_ver); } if (!version_is_bad && entry->chosen_on_date + guard_lifetime < now) { /* It's been too long since the date listed in our state file. */ @@ -989,39 +972,6 @@ entry_list_is_constrained(const or_options_t *options) return 0; } -/** Return true iff this node can answer directory questions about - * microdescriptors. */ -static int -node_understands_microdescriptors(const node_t *node) -{ - tor_assert(node); - if (node->rs && node->rs->version_supports_microdesc_cache) - return 1; - if (node->ri && tor_version_supports_microdescriptors(node->ri->platform)) - return 1; - return 0; -} - -/** Return true iff <b>node</b> is able to answer directory questions - * of type <b>dirinfo</b>. Always returns true if <b>dirinfo</b> is - * NO_DIRINFO (zero). */ -static int -node_can_handle_dirinfo(const node_t *node, dirinfo_type_t dirinfo) -{ - /* Checking dirinfo for any type other than microdescriptors isn't required - yet, since we only choose directory guards that can support microdescs, - routerinfos, and networkstatuses, AND we don't use directory guards if - we're configured to do direct downloads of anything else. The only case - where we might have a guard that doesn't know about a type of directory - information is when we're retrieving directory information from a - bridge. */ - - if ((dirinfo & MICRODESC_DIRINFO) && - !node_understands_microdescriptors(node)) - return 0; - return 1; -} - /** Pick a live (up and listed) entry guard from entry_guards. If * <b>state</b> is non-NULL, this is for a specific circuit -- * make sure not to pick this circuit's exit or any node in the @@ -1077,6 +1027,8 @@ populate_live_entry_guards(smartlist_t *live_entry_guards, int retval = 0; entry_is_live_flags_t entry_flags = 0; + (void) dirinfo_type; + { /* Set the flags we want our entry node to have */ if (need_uptime) { entry_flags |= ENTRY_NEED_UPTIME; @@ -1108,9 +1060,6 @@ populate_live_entry_guards(smartlist_t *live_entry_guards, continue; /* don't pick the same node for entry and exit */ if (smartlist_contains(exit_family, node)) continue; /* avoid relays that are family members of our exit */ - if (dirinfo_type != NO_DIRINFO && - !node_can_handle_dirinfo(node, dirinfo_type)) - continue; /* this node won't be able to answer our dir questions */ smartlist_add(live_entry_guards, (void*)node); if (!entry->made_contact) { /* Always start with the first not-yet-contacted entry @@ -1491,8 +1440,9 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) } } else { if (state_version) { + time_t now = time(NULL); + e->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now); e->chosen_by_version = tor_strdup(state_version); - e->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30); } } if (e->path_bias_disabled && !e->bad_since) @@ -2484,11 +2434,9 @@ any_bridge_supports_microdescriptors(void) SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { node = node_get_by_id(e->identity); if (node && node->is_running && - node_is_bridge(node) && node_is_a_configured_bridge(node) && - node_understands_microdescriptors(node)) { + node_is_bridge(node) && node_is_a_configured_bridge(node)) { /* This is one of our current bridges, and we know enough about - * it to know that it will be able to answer our microdescriptor - * questions. */ + * it to know that it will be able to answer our questions. */ return 1; } } SMARTLIST_FOREACH_END(e); diff --git a/src/or/include.am b/src/or/include.am index b44e1099dc..0ce3cdc5c3 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -15,7 +15,7 @@ else tor_platform_source= endif -EXTRA_DIST+= src/or/ntmain.c src/or/or_sha1.i src/or/Makefile.nmake +EXTRA_DIST+= src/or/ntmain.c src/or/Makefile.nmake if USE_EXTERNAL_EVDNS evdns_source= @@ -79,14 +79,14 @@ LIBTOR_A_SOURCES = \ src/or/status.c \ src/or/onion_ntor.c \ $(evdns_source) \ - $(tor_platform_source) \ - src/or/config_codedigest.c + $(tor_platform_source) src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES) src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) -#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \ -# ../common/libor-event.a +#libtor_a_LIBADD = $(top_builddir)/common/libor.a \ +# $(top_builddir)/common/libor-crypto.a \ +# $(top_builddir)/common/libor-event.a src_or_tor_SOURCES = src/or/tor_main.c @@ -98,7 +98,7 @@ AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ -DBINDIR="\"$(bindir)\"" -src_or_libtor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_or_libtor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) # -L flags need to go in LDFLAGS. -l flags need to go in LDADD. @@ -115,7 +115,7 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \ if COVERAGE_ENABLED src_or_tor_cov_SOURCES = src/or/tor_main.c -src_or_tor_cov_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_or_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ @@ -123,9 +123,9 @@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ src/common/libor-event-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ -TESTING_TOR_BINARY = ./src/or/tor-cov +TESTING_TOR_BINARY = $(top_builddir)/src/or/tor-cov else -TESTING_TOR_BINARY = ./src/or/tor +TESTING_TOR_BINARY = $(top_builddir)/src/or/tor endif ORHEADERS = \ @@ -189,36 +189,24 @@ ORHEADERS = \ noinst_HEADERS+= $(ORHEADERS) micro-revision.i -src/or/config_codedigest.o: src/or/or_sha1.i - micro-revision.i: FORCE - @rm -f micro-revision.tmp; \ - 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 \ - if test ! -f micro-revision.i ; then \ - echo '""' > micro-revision.i; \ - fi; \ - elif test ! -f micro-revision.i || \ - test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \ - mv micro-revision.tmp micro-revision.i; \ - fi; true - -src/or/or_sha1.i: $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) - $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) ) | \ - "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > src/or/or_sha1.i; \ - elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)) | \ - "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > src/or/or_sha1.i; \ - else \ - rm src/or/or_sha1.i; \ - touch src/or/or_sha1.i; \ - fi - -CLEANFILES+= micro-revision.i src/or/micro-revision.i + $(AM_V_at)rm -f micro-revision.tmp; \ + 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 \ + if test ! -f micro-revision.i; then \ + echo '""' > micro-revision.i; \ + fi; \ + elif test ! -f micro-revision.i || \ + test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \ + mv micro-revision.tmp micro-revision.i; \ + fi; \ + rm -f micro-revision.tmp; \ + true + +CLEANFILES+= micro-revision.i src/or/micro-revision.i micro-revision.tmp FORCE: diff --git a/src/or/main.c b/src/or/main.c index d0fe8cbc00..74e6b33397 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -63,7 +63,7 @@ #include <openssl/crypto.h> #endif #include "memarea.h" -#include "../common/sandbox.h" +#include "sandbox.h" #ifdef HAVE_EVENT2_EVENT_H #include <event2/event.h> @@ -131,8 +131,6 @@ static uint64_t stats_n_bytes_written = 0; time_t time_of_process_start = 0; /** How many seconds have we been running? */ long stats_n_seconds_working = 0; -/** When do we next launch DNS wildcarding checks? */ -static time_t time_to_check_for_correct_dns = 0; /** How often will we honor SIGNEWNYM requests? */ #define MAX_SIGNEWNYM_RATE 10 @@ -1202,7 +1200,46 @@ get_signewnym_epoch(void) return newnym_epoch; } -static time_t time_to_check_descriptor = 0; +typedef struct { + time_t last_rotated_x509_certificate; + time_t check_v3_certificate; + time_t check_listeners; + time_t download_networkstatus; + time_t try_getting_descriptors; + time_t reset_descriptor_failures; + time_t add_entropy; + time_t write_bridge_status_file; + time_t downrate_stability; + time_t save_stability; + time_t clean_caches; + time_t recheck_bandwidth; + time_t check_for_expired_networkstatus; + time_t write_stats_files; + time_t write_bridge_stats; + time_t check_port_forwarding; + time_t launch_reachability_tests; + time_t retry_dns_init; + time_t next_heartbeat; + time_t check_descriptor; + /** When do we next launch DNS wildcarding checks? */ + time_t check_for_correct_dns; +} time_to_t; + +static time_to_t time_to = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/** Reset all the time_to's so we'll do all our actions again as if we + * just started up. + * Useful if our clock just moved back a long time from the future, + * so we don't wait until that future arrives again before acting. + */ +void +reset_all_main_loop_timers(void) +{ + memset(&time_to, 0, sizeof(time_to)); +} + /** * Update our schedule so that we'll check whether we need to update our * descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL @@ -1211,7 +1248,7 @@ static time_t time_to_check_descriptor = 0; void reschedule_descriptor_update_check(void) { - time_to_check_descriptor = 0; + time_to.check_descriptor = 0; } /** Perform regular maintenance tasks. This function gets run once per @@ -1220,26 +1257,7 @@ reschedule_descriptor_update_check(void) static void run_scheduled_events(time_t now) { - static time_t last_rotated_x509_certificate = 0; - static time_t time_to_check_v3_certificate = 0; - static time_t time_to_check_listeners = 0; - static time_t time_to_download_networkstatus = 0; - static time_t time_to_try_getting_descriptors = 0; - static time_t time_to_reset_descriptor_failures = 0; - static time_t time_to_add_entropy = 0; - static time_t time_to_write_bridge_status_file = 0; - static time_t time_to_downrate_stability = 0; - static time_t time_to_save_stability = 0; - static time_t time_to_clean_caches = 0; - static time_t time_to_recheck_bandwidth = 0; - static time_t time_to_check_for_expired_networkstatus = 0; - static time_t time_to_write_stats_files = 0; - static time_t time_to_write_bridge_stats = 0; - static time_t time_to_check_port_forwarding = 0; - static time_t time_to_launch_reachability_tests = 0; static int should_init_bridge_stats = 1; - static time_t time_to_retry_dns_init = 0; - static time_t time_to_next_heartbeat = 0; const or_options_t *options = get_options(); int is_server = server_mode(options); @@ -1280,18 +1298,18 @@ run_scheduled_events(time_t now) } if (!should_delay_dir_fetches(options, NULL) && - time_to_try_getting_descriptors < now) { + time_to.try_getting_descriptors < now) { update_all_descriptor_downloads(now); update_extrainfo_downloads(now); if (router_have_minimum_dir_info()) - time_to_try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL; + time_to.try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL; else - time_to_try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL; + time_to.try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL; } - if (time_to_reset_descriptor_failures < now) { + if (time_to.reset_descriptor_failures < now) { router_reset_descriptor_download_failures(); - time_to_reset_descriptor_failures = + time_to.reset_descriptor_failures = now + DESCRIPTOR_FAILURE_RESET_INTERVAL; } @@ -1300,28 +1318,29 @@ run_scheduled_events(time_t now) /* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our * TLS context. */ - if (!last_rotated_x509_certificate) - last_rotated_x509_certificate = now; - if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) { + if (!time_to.last_rotated_x509_certificate) + time_to.last_rotated_x509_certificate = now; + if (time_to.last_rotated_x509_certificate + + MAX_SSL_KEY_LIFETIME_INTERNAL < now) { log_info(LD_GENERAL,"Rotating tls context."); 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 */ } - last_rotated_x509_certificate = now; + time_to.last_rotated_x509_certificate = now; /* We also make sure to rotate the TLS connections themselves if they've * been up for too long -- but that's done via is_bad_for_new_circs in * connection_run_housekeeping() above. */ } - if (time_to_add_entropy < now) { - if (time_to_add_entropy) { + if (time_to.add_entropy < now) { + if (time_to.add_entropy) { /* We already seeded once, so don't die on failure. */ - crypto_seed_rng(0); + crypto_seed_rng(); } /** How often do we add more entropy to OpenSSL's RNG pool? */ #define ENTROPY_INTERVAL (60*60) - time_to_add_entropy = now + ENTROPY_INTERVAL; + time_to.add_entropy = now + ENTROPY_INTERVAL; } /* 1c. If we have to change the accounting interval or record @@ -1329,10 +1348,10 @@ run_scheduled_events(time_t now) if (accounting_is_enabled(options)) accounting_run_housekeeping(now); - if (time_to_launch_reachability_tests < now && + if (time_to.launch_reachability_tests < now && (authdir_mode_tests_reachability(options)) && !net_is_disabled()) { - time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; + time_to.launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; /* try to determine reachability of the other Tor relays */ dirserv_test_reachability(now); } @@ -1340,29 +1359,29 @@ run_scheduled_events(time_t now) /* 1d. Periodically, we discount older stability information so that new * stability info counts more, and save the stability information to disk as * appropriate. */ - if (time_to_downrate_stability < now) - time_to_downrate_stability = rep_hist_downrate_old_runs(now); + if (time_to.downrate_stability < now) + time_to.downrate_stability = rep_hist_downrate_old_runs(now); if (authdir_mode_tests_reachability(options)) { - if (time_to_save_stability < now) { - if (time_to_save_stability && rep_hist_record_mtbf_data(now, 1)<0) { + if (time_to.save_stability < now) { + if (time_to.save_stability && rep_hist_record_mtbf_data(now, 1)<0) { log_warn(LD_GENERAL, "Couldn't store mtbf data."); } #define SAVE_STABILITY_INTERVAL (30*60) - time_to_save_stability = now + SAVE_STABILITY_INTERVAL; + time_to.save_stability = now + SAVE_STABILITY_INTERVAL; } } /* 1e. Periodically, if we're a v3 authority, we check whether our cert is * close to expiring and warn the admin if it is. */ - if (time_to_check_v3_certificate < now) { + if (time_to.check_v3_certificate < now) { v3_authority_check_key_expiry(); #define CHECK_V3_CERTIFICATE_INTERVAL (5*60) - time_to_check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL; + time_to.check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL; } /* 1f. Check whether our networkstatus has expired. */ - if (time_to_check_for_expired_networkstatus < now) { + if (time_to.check_for_expired_networkstatus < now) { networkstatus_t *ns = networkstatus_get_latest_consensus(); /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in * networkstatus_get_reasonably_live_consensus(), but that value is way @@ -1373,68 +1392,68 @@ run_scheduled_events(time_t now) router_dir_info_changed(); } #define CHECK_EXPIRED_NS_INTERVAL (2*60) - time_to_check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL; + time_to.check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL; } /* 1g. Check whether we should write statistics to disk. */ - if (time_to_write_stats_files < now) { + if (time_to.write_stats_files < now) { #define CHECK_WRITE_STATS_INTERVAL (60*60) - time_t next_time_to_write_stats_files = (time_to_write_stats_files > 0 ? - time_to_write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL; + time_t next_time_to_write_stats_files = (time_to.write_stats_files > 0 ? + time_to.write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL; if (options->CellStatistics) { time_t next_write = - rep_hist_buffer_stats_write(time_to_write_stats_files); + rep_hist_buffer_stats_write(time_to.write_stats_files); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } if (options->DirReqStatistics) { - time_t next_write = geoip_dirreq_stats_write(time_to_write_stats_files); + time_t next_write = geoip_dirreq_stats_write(time_to.write_stats_files); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } if (options->EntryStatistics) { - time_t next_write = geoip_entry_stats_write(time_to_write_stats_files); + time_t next_write = geoip_entry_stats_write(time_to.write_stats_files); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } if (options->HiddenServiceStatistics) { - time_t next_write = rep_hist_hs_stats_write(time_to_write_stats_files); + time_t next_write = rep_hist_hs_stats_write(time_to.write_stats_files); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } if (options->ExitPortStatistics) { - time_t next_write = rep_hist_exit_stats_write(time_to_write_stats_files); + time_t next_write = rep_hist_exit_stats_write(time_to.write_stats_files); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } if (options->ConnDirectionStatistics) { - time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files); + time_t next_write = rep_hist_conn_stats_write(time_to.write_stats_files); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } if (options->BridgeAuthoritativeDir) { - time_t next_write = rep_hist_desc_stats_write(time_to_write_stats_files); + time_t next_write = rep_hist_desc_stats_write(time_to.write_stats_files); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } - time_to_write_stats_files = next_time_to_write_stats_files; + time_to.write_stats_files = next_time_to_write_stats_files; } /* 1h. Check whether we should write bridge statistics to disk. */ if (should_record_bridge_info(options)) { - if (time_to_write_bridge_stats < now) { + if (time_to.write_bridge_stats < now) { if (should_init_bridge_stats) { /* (Re-)initialize bridge statistics. */ geoip_bridge_stats_init(now); - time_to_write_bridge_stats = now + WRITE_STATS_INTERVAL; + time_to.write_bridge_stats = now + WRITE_STATS_INTERVAL; should_init_bridge_stats = 0; } else { /* Possibly write bridge statistics to disk and ask when to write * them next time. */ - time_to_write_bridge_stats = geoip_bridge_stats_write( - time_to_write_bridge_stats); + time_to.write_bridge_stats = geoip_bridge_stats_write( + time_to.write_bridge_stats); } } } else if (!should_init_bridge_stats) { @@ -1444,19 +1463,19 @@ run_scheduled_events(time_t now) } /* Remove old information from rephist and the rend cache. */ - if (time_to_clean_caches < now) { + if (time_to.clean_caches < now) { rep_history_clean(now - options->RephistTrackTime); rend_cache_clean(now); rend_cache_clean_v2_descs_as_dir(now, 0); microdesc_cache_rebuild(NULL, 0); #define CLEAN_CACHES_INTERVAL (30*60) - time_to_clean_caches = now + CLEAN_CACHES_INTERVAL; + time_to.clean_caches = now + CLEAN_CACHES_INTERVAL; } #define RETRY_DNS_INTERVAL (10*60) /* If we're a server and initializing dns failed, retry periodically. */ - if (time_to_retry_dns_init < now) { - time_to_retry_dns_init = now + RETRY_DNS_INTERVAL; + if (time_to.retry_dns_init < now) { + time_to.retry_dns_init = now + RETRY_DNS_INTERVAL; if (is_server && has_dns_init_failed()) dns_init(); } @@ -1471,9 +1490,9 @@ 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 && !options->DisableNetwork) { + if (time_to.check_descriptor < now && !options->DisableNetwork) { static int dirport_reachability_count = 0; - time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; + time_to.check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; check_descriptor_bandwidth_changed(now); check_descriptor_ipaddress_changed(now); mark_my_descriptor_dirty_if_too_old(now); @@ -1487,18 +1506,18 @@ run_scheduled_events(time_t now) consider_testing_reachability(1, dirport_reachability_count==0); if (++dirport_reachability_count > 5) dirport_reachability_count = 0; - } else if (time_to_recheck_bandwidth < now) { + } else if (time_to.recheck_bandwidth < now) { /* If we haven't checked for 12 hours and our bandwidth estimate is * low, do another bandwidth test. This is especially important for * bridges, since they might go long periods without much use. */ const routerinfo_t *me = router_get_my_routerinfo(); - if (time_to_recheck_bandwidth && me && + if (time_to.recheck_bandwidth && me && me->bandwidthcapacity < me->bandwidthrate && me->bandwidthcapacity < 51200) { reset_bandwidth_test(); } #define BANDWIDTH_RECHECK_INTERVAL (12*60*60) - time_to_recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL; + time_to.recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL; } } @@ -1516,8 +1535,8 @@ run_scheduled_events(time_t now) #define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60) if (!should_delay_dir_fetches(options, NULL) && - time_to_download_networkstatus < now) { - time_to_download_networkstatus = + time_to.download_networkstatus < now) { + time_to.download_networkstatus = now + networkstatus_dl_check_interval(options); update_networkstatus_downloads(now); } @@ -1547,9 +1566,9 @@ run_scheduled_events(time_t now) connection_expire_held_open(); /* 3d. And every 60 seconds, we relaunch listeners if any died. */ - if (!net_is_disabled() && time_to_check_listeners < now) { + if (!net_is_disabled() && time_to.check_listeners < now) { retry_all_listeners(NULL, NULL, 0); - time_to_check_listeners = now+60; + time_to.check_listeners = now+60; } /* 4. Every second, we try a new circuit if there are no valid @@ -1601,28 +1620,29 @@ run_scheduled_events(time_t now) * to us. */ if (!net_is_disabled() && public_server_mode(options) && - time_to_check_for_correct_dns < now && + time_to.check_for_correct_dns < now && ! router_my_exit_policy_is_reject_star()) { - if (!time_to_check_for_correct_dns) { - time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); + if (!time_to.check_for_correct_dns) { + time_to.check_for_correct_dns = + crypto_rand_time_range(now + 60, now + 180); } else { dns_launch_correctness_checks(); - time_to_check_for_correct_dns = now + 12*3600 + + time_to.check_for_correct_dns = now + 12*3600 + crypto_rand_int(12*3600); } } /* 10. write bridge networkstatus file to disk */ if (options->BridgeAuthoritativeDir && - time_to_write_bridge_status_file < now) { + time_to.write_bridge_status_file < now) { networkstatus_dump_bridge_status_to_file(now); #define BRIDGE_STATUSFILE_INTERVAL (30*60) - time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL; + time_to.write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL; } /* 11. check the port forwarding app */ if (!net_is_disabled() && - time_to_check_port_forwarding < now && + time_to.check_port_forwarding < now && options->PortForwarding && is_server) { #define PORT_FORWARDING_CHECK_INTERVAL 5 @@ -1635,7 +1655,7 @@ run_scheduled_events(time_t now) SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp)); smartlist_free(ports_to_forward); } - time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; + time_to.check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; } /* 11b. check pending unconfigured managed proxies */ @@ -1644,10 +1664,10 @@ run_scheduled_events(time_t now) /* 12. write the heartbeat message */ if (options->HeartbeatPeriod && - time_to_next_heartbeat <= now) { - if (time_to_next_heartbeat) /* don't log the first heartbeat */ + time_to.next_heartbeat <= now) { + if (time_to.next_heartbeat) /* don't log the first heartbeat */ log_heartbeat(now); - time_to_next_heartbeat = now+options->HeartbeatPeriod; + time_to.next_heartbeat = now+options->HeartbeatPeriod; } } @@ -1747,8 +1767,6 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN || seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) { circuit_note_clock_jumped(seconds_elapsed); - /* XXX if the time jumps *back* many months, do our events in - * run_scheduled_events() recover? I don't think they do. -RD */ } else if (seconds_elapsed > 0) stats_n_seconds_working += seconds_elapsed; @@ -1872,7 +1890,7 @@ dns_servers_relaunch_checks(void) { if (server_mode(get_options())) { dns_reset_correctness_checks(); - time_to_check_for_correct_dns = 0; + time_to.check_for_correct_dns = 0; } } @@ -2315,12 +2333,13 @@ dumpstats(int severity) if (conn->type == CONN_TYPE_OR) { or_connection_t *or_conn = TO_OR_CONN(conn); if (or_conn->tls) { - tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len, - &wbuf_cap, &wbuf_len); - tor_log(severity, LD_GENERAL, - "Conn %d: %d/%d bytes used on OpenSSL read buffer; " - "%d/%d bytes used on write buffer.", - i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap); + if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len, + &wbuf_cap, &wbuf_len) == 0) { + tor_log(severity, LD_GENERAL, + "Conn %d: %d/%d bytes used on OpenSSL read buffer; " + "%d/%d bytes used on write buffer.", + i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap); + } } } } @@ -2484,10 +2503,11 @@ tor_init(int argc, char *argv[]) if (!strcmp(cl->key, "--quiet") || !strcmp(cl->key, "--dump-config")) quiet = 2; - /* --version, --digests, and --help imply --hush */ + /* The following options imply --hush */ if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") || !strcmp(cl->key, "--list-torrc-options") || !strcmp(cl->key, "--library-versions") || + !strcmp(cl->key, "--hash-password") || !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) { if (quiet < 1) quiet = 1; @@ -2803,6 +2823,7 @@ do_dump_config(void) const char *arg = options->command_arg; int how; char *opts; + if (!strcmp(arg, "short")) { how = OPTIONS_DUMP_MINIMAL; } else if (!strcmp(arg, "non-builtin")) { @@ -2810,8 +2831,9 @@ do_dump_config(void) } else if (!strcmp(arg, "full")) { how = OPTIONS_DUMP_ALL; } else { - printf("%s is not a recognized argument to --dump-config. " - "Please select 'short', 'non-builtin', or 'full'", arg); + fprintf(stderr, "No valid argument to --dump-config found!\n"); + fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n"); + return -1; } @@ -3112,7 +3134,8 @@ tor_main(int argc, char *argv[]) result = 0; break; case CMD_VERIFY_CONFIG: - printf("Configuration was valid\n"); + if (quiet_level == 0) + printf("Configuration was valid\n"); result = 0; break; case CMD_DUMP_CONFIG: diff --git a/src/or/main.h b/src/or/main.h index f77b4711c5..542eab6565 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -52,6 +52,7 @@ void directory_info_has_arrived(time_t now, int from_cache); void ip_address_changed(int at_interface); void dns_servers_relaunch_checks(void); +void reset_all_main_loop_timers(void); void reschedule_descriptor_update_check(void); MOCK_DECL(long,get_uptime,(void)); diff --git a/src/or/or.h b/src/or/or.h index 0d81b54d94..af3496765e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -88,7 +88,7 @@ #include "crypto.h" #include "tortls.h" -#include "../common/torlog.h" +#include "torlog.h" #include "container.h" #include "torgzip.h" #include "address.h" @@ -793,17 +793,34 @@ typedef struct rend_data_t { /** Onion address (without the .onion part) that a client requests. */ char onion_address[REND_SERVICE_ID_LEN_BASE32+1]; + /** Descriptor ID for each replicas computed from the onion address. If + * the onion address is empty, this array MUST be empty. We keep them so + * we know when to purge our entry in the last hsdir request table. */ + char descriptor_id[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS][DIGEST_LEN]; + /** (Optional) descriptor cookie that is used by a client. */ char descriptor_cookie[REND_DESC_COOKIE_LEN]; /** Authorization type for accessing a service used by a client. */ rend_auth_type_t auth_type; + /** Descriptor ID for a client request. The control port command HSFETCH + * uses this. It's set if the descriptor query should only use this + * descriptor ID. */ + char desc_id_fetch[DIGEST_LEN]; + /** Hash of the hidden service's PK used by a service. */ char rend_pk_digest[DIGEST_LEN]; /** Rendezvous cookie used by both, client and service. */ char rend_cookie[REND_COOKIE_LEN]; + + /** List of HSDir fingerprints on which this request has been sent to. + * This contains binary identity digest of the directory. */ + smartlist_t *hsdirs_fp; + + /** Number of streams associated with this rendezvous circuit. */ + int nr_streams; } rend_data_t; /** Time interval for tracking replays of DH public keys received in @@ -1731,6 +1748,9 @@ typedef struct control_connection_t { * connection. */ unsigned int is_owning_control_connection:1; + /** List of ephemeral onion services belonging to this connection. */ + smartlist_t *ephemeral_onion_services; + /** If we have sent an AUTHCHALLENGE reply on this connection and * have not received a successful AUTHENTICATE command, points to * the value which the client must send to authenticate itself; @@ -2131,9 +2151,6 @@ typedef struct routerstatus_t { * if the number of traits we care about ever becomes incredibly big. */ unsigned int version_known:1; - /** True iff this router is a version that, if it caches directory info, - * we can get microdescriptors from. */ - unsigned int version_supports_microdesc_cache:1; /** True iff this router has a version that allows it to accept EXTEND2 * cells */ unsigned int version_supports_extend2_cells:1; @@ -3387,8 +3404,6 @@ 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. */ @@ -4810,12 +4825,13 @@ 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 and maximum number of distinct INTRODUCE2 cells which a + * hidden service's introduction point will receive before it begins to + * expire. */ +#define INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS 16384 +/* Double the minimum value so the interval is [min, min * 2]. */ +#define INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS \ + (INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS * 2) /** The minimum number of seconds that an introduction point will last * before expiring due to old age. (If it receives @@ -4869,6 +4885,12 @@ typedef struct rend_intro_point_t { */ int accepted_introduce2_count; + /** (Service side only) Number of maximum INTRODUCE2 cells that this IP + * will accept. This is a random value between + * INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and + * INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */ + int max_introductions; + /** (Service side only) The time at which this intro point was first * published, or -1 if this intro point has not yet been * published. */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 162e0ac53e..59e938e89c 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -141,7 +141,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, int r, v3_shift = 0; char payload[RELAY_PAYLOAD_SIZE]; char tmp[RELAY_PAYLOAD_SIZE]; - rend_cache_entry_t *entry; + rend_cache_entry_t *entry = NULL; crypt_path_t *cpath; off_t dh_offset; crypto_pk_t *intro_key = NULL; @@ -158,8 +158,13 @@ rend_client_send_introduction(origin_circuit_t *introcirc, tor_assert(!(rendcirc->build_state->onehop_tunnel)); #endif - if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, - &entry) < 1) { + r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, + &entry); + /* An invalid onion address is not possible else we have a big issue. */ + tor_assert(r != -EINVAL); + if (r < 0 || !rend_client_any_intro_points_usable(entry)) { + /* If the descriptor is not found or the intro points are not usable + * anymore, trigger a fetch. */ log_info(LD_REND, "query %s didn't have valid rend desc in cache. " "Refetching descriptor.", @@ -469,9 +474,8 @@ rend_client_introduction_acked(origin_circuit_t *circ, /** Contains the last request times to hidden service directories for * certain queries; each key is a string consisting of the - * concatenation of a base32-encoded HS directory identity digest, a - * base32-encoded HS descriptor ID, and a hidden service address - * (without the ".onion" part); each value is a pointer to a time_t + * concatenation of a base32-encoded HS directory identity digest and + * base32-encoded HS descriptor ID; each value is a pointer to a time_t * holding the time of the last request for that descriptor ID to that * HS directory. */ static strmap_t *last_hid_serv_requests_ = NULL; @@ -487,19 +491,16 @@ get_last_hid_serv_requests(void) } #define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \ - REND_DESC_ID_V2_LEN_BASE32 + \ - REND_SERVICE_ID_LEN_BASE32) + REND_DESC_ID_V2_LEN_BASE32) /** Look up the last request time to hidden service directory <b>hs_dir</b> - * for descriptor ID <b>desc_id_base32</b> for the service specified in - * <b>rend_query</b>. If <b>set</b> is non-zero, - * assign the current time <b>now</b> and return that. Otherwise, return - * the most recent request time, or 0 if no such request has been sent - * before. */ + * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero, + * assign the current time <b>now</b> and return that. Otherwise, return the + * most recent request time, or 0 if no such request has been sent before. + */ static time_t lookup_last_hid_serv_request(routerstatus_t *hs_dir, const char *desc_id_base32, - const rend_data_t *rend_query, time_t now, int set) { char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; @@ -508,10 +509,9 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir, strmap_t *last_hid_serv_requests = get_last_hid_serv_requests(); base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32), hs_dir->identity_digest, DIGEST_LEN); - tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s%s", + tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s", hsdir_id_base32, - desc_id_base32, - rend_query->onion_address); + desc_id_base32); /* XXX023 tor_assert(strlen(hsdir_desc_comb_id) == LAST_HID_SERV_REQUEST_KEY_LEN); */ if (set) { @@ -552,20 +552,23 @@ directory_clean_last_hid_serv_requests(time_t now) } } -/** Remove all requests related to the hidden service named - * <b>onion_address</b> from the history of times of requests to - * hidden service directories. +/** Remove all requests related to the descriptor ID <b>desc_id</b> from the + * history of times of requests to hidden service directories. + * <b>desc_id</b> is an unencoded descriptor ID of size DIGEST_LEN. * * This is called from rend_client_note_connection_attempt_ended(), which - * must be idempotent, so any future changes to this function must leave - * it idempotent too. - */ + * must be idempotent, so any future changes to this function must leave it + * idempotent too. */ static void -purge_hid_serv_from_last_hid_serv_requests(const char *onion_address) +purge_hid_serv_from_last_hid_serv_requests(const char *desc_id) { strmap_iter_t *iter; strmap_t *last_hid_serv_requests = get_last_hid_serv_requests(); - /* XXX023 tor_assert(strlen(onion_address) == REND_SERVICE_ID_LEN_BASE32); */ + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + + /* Key is stored with the base32 encoded desc_id. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); for (iter = strmap_iter_init(last_hid_serv_requests); !strmap_iter_done(iter); ) { const char *key; @@ -573,9 +576,9 @@ purge_hid_serv_from_last_hid_serv_requests(const char *onion_address) strmap_iter_get(iter, &key, &val); /* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */ if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN - - REND_SERVICE_ID_LEN_BASE32, - onion_address, - REND_SERVICE_ID_LEN_BASE32)) { + REND_DESC_ID_V2_LEN_BASE32, + desc_id_base32, + REND_DESC_ID_V2_LEN_BASE32)) { iter = strmap_iter_next_rmv(last_hid_serv_requests, iter); tor_free(val); } else { @@ -604,64 +607,53 @@ rend_client_purge_last_hid_serv_requests(void) } } -/** Determine the responsible hidden service directories for <b>desc_id</b> - * and fetch the descriptor with that ID from one of them. Only - * send a request to a hidden service directory that we have not yet tried - * during this attempt to connect to this hidden service; on success, return 1, - * in the case that no hidden service directory is left to ask for the - * descriptor, return 0, and in case of a failure -1. */ -static int -directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) +/** This returns a good valid hs dir that should be used for the given + * descriptor id. + * + * Return NULL on error else the hsdir node pointer. */ +static routerstatus_t * +pick_hsdir(const char *desc_id, const char *desc_id_base32) { smartlist_t *responsible_dirs = smartlist_new(); smartlist_t *usable_responsible_dirs = smartlist_new(); const or_options_t *options = get_options(); routerstatus_t *hs_dir; - char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; time_t now = time(NULL); - char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; -#ifdef ENABLE_TOR2WEB_MODE - const int tor2web_mode = options->Tor2webMode; - const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; -#else - const int how_to_fetch = DIRIND_ANONYMOUS; -#endif int excluded_some; - tor_assert(desc_id); - tor_assert(rend_query); - /* Determine responsible dirs. Even if we can't get all we want, - * work with the ones we have. If it's empty, we'll notice below. */ - hid_serv_get_responsible_directories(responsible_dirs, desc_id); - base32_encode(desc_id_base32, sizeof(desc_id_base32), - desc_id, DIGEST_LEN); + tor_assert(desc_id); + tor_assert(desc_id_base32); - /* Only select those hidden service directories to which we did not send - * a request recently and for which we have a router descriptor here. */ + /* Determine responsible dirs. Even if we can't get all we want, work with + * the ones we have. If it's empty, we'll notice below. */ + hid_serv_get_responsible_directories(responsible_dirs, desc_id); /* Clean request history first. */ directory_clean_last_hid_serv_requests(now); - SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, { - time_t last = lookup_last_hid_serv_request( - dir, desc_id_base32, rend_query, 0, 0); - const node_t *node = node_get_by_id(dir->identity_digest); - if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || - !node || !node_has_descriptor(node)) { - SMARTLIST_DEL_CURRENT(responsible_dirs, dir); - continue; - } - if (! routerset_contains_node(options->ExcludeNodes, node)) { - smartlist_add(usable_responsible_dirs, dir); - } - }); + /* Only select those hidden service directories to which we did not send a + * request recently and for which we have a router descriptor here. */ + SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) { + time_t last = lookup_last_hid_serv_request(dir, desc_id_base32, + 0, 0); + const node_t *node = node_get_by_id(dir->identity_digest); + if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || + !node || !node_has_descriptor(node)) { + SMARTLIST_DEL_CURRENT(responsible_dirs, dir); + continue; + } + if (!routerset_contains_node(options->ExcludeNodes, node)) { + smartlist_add(usable_responsible_dirs, dir); + } + } SMARTLIST_FOREACH_END(dir); excluded_some = smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs); hs_dir = smartlist_choose(usable_responsible_dirs); - if (! hs_dir && ! options->StrictNodes) + if (!hs_dir && !options->StrictNodes) { hs_dir = smartlist_choose(responsible_dirs); + } smartlist_free(responsible_dirs); smartlist_free(usable_responsible_dirs); @@ -674,23 +666,69 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) "requested hidden service: they are all either down or " "excluded, and StrictNodes is set."); } - return 0; + } else { + /* Remember that we are requesting a descriptor from this hidden service + * directory now. */ + lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1); + } + + return hs_dir; +} + +/** Determine the responsible hidden service directories for <b>desc_id</b> + * and fetch the descriptor with that ID from one of them. Only + * send a request to a hidden service directory that we have not yet tried + * during this attempt to connect to this hidden service; on success, return 1, + * in the case that no hidden service directory is left to ask for the + * descriptor, return 0, and in case of a failure -1. */ +static int +directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query, + routerstatus_t *rs_hsdir) +{ + routerstatus_t *hs_dir = rs_hsdir; + char *hsdir_fp; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; +#ifdef ENABLE_TOR2WEB_MODE + const int tor2web_mode = get_options()->Tor2webMode; + const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; +#else + const int how_to_fetch = DIRIND_ANONYMOUS; +#endif + + tor_assert(desc_id); + + base32_encode(desc_id_base32, sizeof(desc_id_base32), + desc_id, DIGEST_LEN); + + /* Automatically pick an hs dir if none given. */ + if (!rs_hsdir) { + hs_dir = pick_hsdir(desc_id, desc_id_base32); + if (!hs_dir) { + /* No suitable hs dir can be found, stop right now. */ + return 0; + } } - /* Remember that we are requesting a descriptor from this hidden service - * directory now. */ - lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1); + /* Add a copy of the HSDir identity digest to the query so we can track it + * on the control port. */ + hsdir_fp = tor_memdup(hs_dir->identity_digest, + sizeof(hs_dir->identity_digest)); + smartlist_add(rend_query->hsdirs_fp, hsdir_fp); - /* Encode descriptor cookie for logging purposes. */ + /* Encode descriptor cookie for logging purposes. Also, if the cookie is + * malformed, no fetch is triggered thus this needs to be done before the + * fetch request. */ if (rend_query->auth_type != REND_NO_AUTH) { if (base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64), - rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN)<0) { + rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN, + 0)<0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); return 0; } - /* Remove == signs and newline. */ - descriptor_cookie_base64[strlen(descriptor_cookie_base64)-3] = '\0'; + /* Remove == signs. */ + descriptor_cookie_base64[strlen(descriptor_cookie_base64)-2] = '\0'; } else { strlcpy(descriptor_cookie_base64, "(none)", sizeof(descriptor_cookie_base64)); @@ -721,16 +759,144 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) return 1; } +/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are + * given, they will be used instead. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query, + smartlist_t *hsdirs) +{ + int ret; + + tor_assert(rend_query); + + if (!hsdirs) { + ret = directory_get_from_hs_dir(desc_id, rend_query, NULL); + goto end; /* either success or failure, but we're done */ + } + + /* Using the given hsdir list, trigger a fetch on each of them. */ + SMARTLIST_FOREACH_BEGIN(hsdirs, routerstatus_t *, hs_dir) { + /* This should always be a success. */ + ret = directory_get_from_hs_dir(desc_id, rend_query, hs_dir); + tor_assert(ret); + } SMARTLIST_FOREACH_END(hs_dir); + + /* Everything went well. */ + ret = 0; + + end: + return ret; +} + +/** Fetch a v2 descriptor using the onion address in the given query object. + * This will compute the descriptor id for each replicas and fetch it on the + * given hsdir(s) if any or the responsible ones that are choosen + * automatically. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs) +{ + char descriptor_id[DIGEST_LEN]; + int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; + int i, tries_left, ret; + + tor_assert(query); + + /* Randomly iterate over the replicas until a descriptor can be fetched + * from one of the consecutive nodes, or no options are left. */ + for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) { + replicas_left_to_try[i] = i; + } + + tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; + while (tries_left > 0) { + int rand = crypto_rand_int(tries_left); + int chosen_replica = replicas_left_to_try[rand]; + replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; + + ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address, + query->auth_type == REND_STEALTH_AUTH ? + query->descriptor_cookie : NULL, + time(NULL), chosen_replica); + if (ret < 0) { + /* Normally, on failure the descriptor_id is untouched but let's be + * safe in general in case the function changes at some point. */ + goto end; + } + + if (tor_memcmp(descriptor_id, query->descriptor_id[chosen_replica], + sizeof(descriptor_id)) != 0) { + /* Not equal from what we currently have so purge the last hid serv + * request cache and update the descriptor ID with the new value. */ + purge_hid_serv_from_last_hid_serv_requests( + query->descriptor_id[chosen_replica]); + memcpy(query->descriptor_id[chosen_replica], descriptor_id, + sizeof(query->descriptor_id[chosen_replica])); + } + + /* Trigger the fetch with the computed descriptor ID. */ + ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs); + if (ret != 0) { + /* Either on success or failure, as long as we tried a fetch we are + * done here. */ + goto end; + } + } + + /* If we come here, there are no hidden service directories left. */ + log_info(LD_REND, "Could not pick one of the responsible hidden " + "service directories to fetch descriptors, because " + "we already tried them all unsuccessfully."); + ret = 0; + + end: + memwipe(descriptor_id, 0, sizeof(descriptor_id)); + return ret; +} + +/** Fetch a v2 descriptor using the given query. If any hsdir are specified, + * use them for the fetch. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +int +rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs) +{ + int ret; + + tor_assert(query); + + /* Depending on what's available in the rend data query object, we will + * trigger a fetch by HS address or using a descriptor ID. */ + + if (query->onion_address[0] != '\0') { + ret = fetch_v2_desc_by_addr(query, hsdirs); + } else if (!tor_digest_is_zero(query->desc_id_fetch)) { + ret = fetch_v2_desc_by_descid(query->desc_id_fetch, query, hsdirs); + } else { + /* Query data is invalid. */ + ret = -1; + goto error; + } + + error: + return ret; +} + /** Unless we already have a descriptor for <b>rend_query</b> with at least * one (possibly) working introduction point in it, start a connection to a * hidden service directory to fetch a v2 rendezvous service descriptor. */ void -rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) +rend_client_refetch_v2_renddesc(rend_data_t *rend_query) { - char descriptor_id[DIGEST_LEN]; - int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; - int i, tries_left; + int ret; rend_cache_entry_t *e = NULL; + tor_assert(rend_query); /* Are we configured to fetch descriptors? */ if (!get_options()->FetchHidServDescriptors) { @@ -739,7 +905,7 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) return; } /* Before fetching, check if we already have a usable descriptor here. */ - if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 && + if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) == 0 && rend_client_any_intro_points_usable(e)) { log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we " "already have a usable descriptor here. Not fetching."); @@ -747,44 +913,12 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) } log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s", safe_str_client(rend_query->onion_address)); - /* Randomly iterate over the replicas until a descriptor can be fetched - * from one of the consecutive nodes, or no options are left. */ - tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; - for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) - replicas_left_to_try[i] = i; - while (tries_left > 0) { - int rand = crypto_rand_int(tries_left); - int chosen_replica = replicas_left_to_try[rand]; - replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; - if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address, - rend_query->auth_type == REND_STEALTH_AUTH ? - rend_query->descriptor_cookie : NULL, - time(NULL), chosen_replica) < 0) { - log_warn(LD_REND, "Internal error: Computing v2 rendezvous " - "descriptor ID did not succeed."); - /* - * Hmm, can this write anything to descriptor_id and still fail? - * Let's clear it just to be safe. - * - * From here on, any returns should goto done which clears - * descriptor_id so we don't leave key-derived material on the stack. - */ - goto done; - } - if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0) - goto done; /* either success or failure, but we're done */ + ret = rend_client_fetch_v2_desc(rend_query, NULL); + if (ret <= 0) { + /* Close pending connections on error or if no hsdir can be found. */ + rend_client_desc_trynow(rend_query->onion_address); } - /* If we come here, there are no hidden service directories left. */ - log_info(LD_REND, "Could not pick one of the responsible hidden " - "service directories to fetch descriptors, because " - "we already tried them all unsuccessfully."); - /* Close pending connections. */ - rend_client_desc_trynow(rend_query->onion_address); - - done: - memwipe(descriptor_id, 0, sizeof(descriptor_id)); - return; } @@ -845,7 +979,7 @@ rend_client_cancel_descriptor_fetches(void) */ int rend_client_report_intro_point_failure(extend_info_t *failed_intro, - const rend_data_t *rend_query, + rend_data_t *rend_query, unsigned int failure_type) { int i, r; @@ -853,17 +987,26 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, connection_t *conn; r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent); - if (r<0) { - log_warn(LD_BUG, "Malformed service ID %s.", - escaped_safe_str_client(rend_query->onion_address)); - return -1; - } - if (r==0) { - log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.", - escaped_safe_str_client(rend_query->onion_address)); - rend_client_refetch_v2_renddesc(rend_query); - return 0; + if (r < 0) { + /* Either invalid onion address or cache entry not found. */ + switch (-r) { + case EINVAL: + log_warn(LD_BUG, "Malformed service ID %s.", + escaped_safe_str_client(rend_query->onion_address)); + return -1; + case ENOENT: + log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.", + escaped_safe_str_client(rend_query->onion_address)); + rend_client_refetch_v2_renddesc(rend_query); + return 0; + default: + log_warn(LD_BUG, "Unknown cache lookup returned code: %d", r); + return -1; + } } + /* The intro points are not checked here if they are usable or not because + * this is called when an intro point circuit is closed thus there must be + * at least one intro point that is usable and is about to be flagged. */ for (i = 0; i < smartlist_len(ent->parsed->intro_nodes); i++) { rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i); @@ -1062,7 +1205,7 @@ rend_client_desc_trynow(const char *query) continue; assert_connection_ok(base_conn, now); if (rend_cache_lookup_entry(rend_data->onion_address, -1, - &entry) == 1 && + &entry) == 0 && rend_client_any_intro_points_usable(entry)) { /* either this fetch worked, or it failed but there was a * valid entry from before which we should reuse */ @@ -1086,27 +1229,28 @@ rend_client_desc_trynow(const char *query) "unavailable (try again later).", safe_str_client(query)); connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED); - rend_client_note_connection_attempt_ended(query); + rend_client_note_connection_attempt_ended(rend_data); } } SMARTLIST_FOREACH_END(base_conn); } -/** Clear temporary state used only during an attempt to connect to - * the hidden service named <b>onion_address</b>. Called when a - * connection attempt has ended; it is possible for this to be called - * multiple times while handling an ended connection attempt, and - * any future changes to this function must ensure it remains - * idempotent. - */ +/** Clear temporary state used only during an attempt to connect to the + * hidden service with <b>rend_data</b>. Called when a connection attempt + * has ended; it is possible for this to be called multiple times while + * handling an ended connection attempt, and any future changes to this + * function must ensure it remains idempotent. */ void -rend_client_note_connection_attempt_ended(const char *onion_address) +rend_client_note_connection_attempt_ended(const rend_data_t *rend_data) { + unsigned int have_onion = 0; rend_cache_entry_t *cache_entry = NULL; - rend_cache_lookup_entry(onion_address, -1, &cache_entry); - log_info(LD_REND, "Connection attempt for %s has ended; " - "cleaning up temporary state.", - safe_str_client(onion_address)); + if (*rend_data->onion_address != '\0') { + /* Ignore return value; we find an entry, or we don't. */ + (void) rend_cache_lookup_entry(rend_data->onion_address, -1, + &cache_entry); + have_onion = 1; + } /* Clear the timed_out flag on all remaining intro points for this HS. */ if (cache_entry != NULL) { @@ -1116,7 +1260,20 @@ rend_client_note_connection_attempt_ended(const char *onion_address) } /* Remove the HS's entries in last_hid_serv_requests. */ - purge_hid_serv_from_last_hid_serv_requests(onion_address); + if (have_onion) { + unsigned int replica; + for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id); + replica++) { + const char *desc_id = rend_data->descriptor_id[replica]; + purge_hid_serv_from_last_hid_serv_requests(desc_id); + } + log_info(LD_REND, "Connection attempt for %s has ended; " + "cleaning up temporary state.", + safe_str_client(rend_data->onion_address)); + } else { + /* We only have an ID for a fetch. Probably used by HSFETCH. */ + purge_hid_serv_from_last_hid_serv_requests(rend_data->desc_id_fetch); + } } /** Return a newly allocated extend_info_t* for a randomly chosen introduction @@ -1126,13 +1283,17 @@ rend_client_note_connection_attempt_ended(const char *onion_address) extend_info_t * rend_client_get_random_intro(const rend_data_t *rend_query) { + int ret; extend_info_t *result; rend_cache_entry_t *entry; - if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) { - log_warn(LD_REND, - "Query '%s' didn't have valid rend desc in cache. Failing.", - safe_str_client(rend_query->onion_address)); + ret = rend_cache_lookup_entry(rend_query->onion_address, -1, &entry); + if (ret < 0 || !rend_client_any_intro_points_usable(entry)) { + log_warn(LD_REND, + "Query '%s' didn't have valid rend desc in cache. Failing.", + safe_str_client(rend_query->onion_address)); + /* XXX: Should we refetch the descriptor here if the IPs are not usable + * anymore ?. */ return NULL; } diff --git a/src/or/rendclient.h b/src/or/rendclient.h index 098c61d0a1..6118924e1d 100644 --- a/src/or/rendclient.h +++ b/src/or/rendclient.h @@ -19,7 +19,8 @@ void rend_client_rendcirc_has_opened(origin_circuit_t *circ); int rend_client_introduction_acked(origin_circuit_t *circ, const uint8_t *request, size_t request_len); -void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query); +void rend_client_refetch_v2_renddesc(rend_data_t *rend_query); +int rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs); void rend_client_cancel_descriptor_fetches(void); void rend_client_purge_last_hid_serv_requests(void); @@ -28,7 +29,7 @@ void rend_client_purge_last_hid_serv_requests(void); #define INTRO_POINT_FAILURE_UNREACHABLE 2 int rend_client_report_intro_point_failure(extend_info_t *failed_intro, - const rend_data_t *rend_query, + rend_data_t *rend_query, unsigned int failure_type); int rend_client_rendezvous_acked(origin_circuit_t *circ, @@ -39,7 +40,7 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ, size_t request_len); void rend_client_desc_trynow(const char *query); -void rend_client_note_connection_attempt_ended(const char *onion_address); +void rend_client_note_connection_attempt_ended(const rend_data_t *rend_data); extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query); int rend_client_any_intro_points_usable(const rend_cache_entry_t *entry); @@ -51,7 +52,6 @@ int rend_parse_service_authorization(const or_options_t *options, rend_service_authorization_t *rend_client_lookup_service_authorization( const char *onion_address); void rend_service_authorization_free_all(void); -rend_data_t *rend_data_dup(const rend_data_t *request); #endif diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 5fdd13efce..ec4353c409 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -155,10 +155,10 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id, } /* Calculate current time-period. */ time_period = get_time_period(now, 0, service_id_binary); - /* Calculate secret-id-part = h(time-period | replica). */ + /* Calculate secret-id-part = h(time-period | desc-cookie | replica). */ get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie, replica); - /* Calculate descriptor ID. */ + /* Calculate descriptor ID: H(permanent-id | secret-id-part) */ rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part); return 0; } @@ -529,7 +529,8 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, } /* Base64-encode introduction points. */ ipos_base64 = tor_calloc(ipos_len, 2); - if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) { + if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len, + BASE64_ENCODE_MULTILINE)<0) { log_warn(LD_REND, "Could not encode introduction point string to " "base64. length=%d", (int)ipos_len); tor_free(ipos_base64); @@ -646,7 +647,6 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, rend_encoded_v2_service_descriptor_free(enc); goto err; } - desc_str[written++] = '\n'; desc_str[written++] = 0; /* Check if we can parse our own descriptor. */ if (!rend_desc_v2_is_parsable(enc)) { @@ -920,36 +920,70 @@ rend_valid_service_id(const char *query) return 1; } -/** If we have a cached rend_cache_entry_t for the service ID <b>query</b> - * with <b>version</b>, set *<b>e</b> to that entry and return 1. - * Else return 0. If <b>version</b> is nonnegative, only return an entry - * in that descriptor format version. Otherwise (if <b>version</b> is - * negative), return the most recent format we have. - */ +/** Return true iff <b>query</b> is a syntactically valid descriptor ID. + * (as generated by rend_get_descriptor_id_bytes). */ +int +rend_valid_descriptor_id(const char *query) +{ + if (strlen(query) != REND_DESC_ID_V2_LEN_BASE32) { + goto invalid; + } + if (strspn(query, BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) { + goto invalid; + } + + return 1; + + invalid: + return 0; +} + +/** Lookup in the client cache the given service ID <b>query</b> for + * <b>version</b>. + * + * Return 0 if found and if <b>e</b> is non NULL, set it with the entry + * found. Else, a negative value is returned and <b>e</b> is untouched. + * -EINVAL means that <b>query</b> is not a valid service id. + * -ENOENT means that no entry in the cache was found. */ int rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e) { - char key[REND_SERVICE_ID_LEN_BASE32+2]; /* <version><query>\0 */ + int ret = 0; + char key[REND_SERVICE_ID_LEN_BASE32 + 2]; /* <version><query>\0 */ + rend_cache_entry_t *entry = NULL; + static const int default_version = 2; + tor_assert(rend_cache); - if (!rend_valid_service_id(query)) - return -1; - *e = NULL; - if (version != 0) { - tor_snprintf(key, sizeof(key), "2%s", query); - *e = strmap_get_lc(rend_cache, key); + tor_assert(query); + + if (!rend_valid_service_id(query)) { + ret = -EINVAL; + goto end; + } + + switch (version) { + case 0: + log_warn(LD_REND, "Cache lookup of a v0 renddesc is deprecated."); + break; + case 2: + /* Default is version 2. */ + default: + tor_snprintf(key, sizeof(key), "%d%s", default_version, query); + entry = strmap_get_lc(rend_cache, key); + break; } - if (!*e && version != 2) { - tor_snprintf(key, sizeof(key), "0%s", query); - *e = strmap_get_lc(rend_cache, key); + if (!entry) { + ret = -ENOENT; + goto end; } - if (!*e) - return 0; - tor_assert((*e)->parsed && (*e)->parsed->intro_nodes); - /* XXX023 hack for now, to return "not found" if there are no intro - * points remaining. See bug 997. */ - if (! rend_client_any_intro_points_usable(*e)) - return 0; - return 1; + tor_assert(entry->parsed && entry->parsed->intro_nodes); + + if (e) { + *e = entry; + } + + end: + return ret; } /** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and @@ -1121,12 +1155,14 @@ rend_cache_store_v2_desc_as_dir(const char *desc) * If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>, * reject it. * - * Return an appropriate rend_cache_store_status_t. + * Return an appropriate rend_cache_store_status_t. If entry is not NULL, + * set it with the cache entry pointer of the descriptor. */ rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc, const char *desc_id_base32, - const rend_data_t *rend_query) + const rend_data_t *rend_query, + rend_cache_entry_t **entry) { /*XXXX this seems to have a bit of duplicate code with * rend_cache_store_v2_desc_as_dir(). Fix that. */ @@ -1159,6 +1195,9 @@ rend_cache_store_v2_desc_as_client(const char *desc, tor_assert(desc); tor_assert(desc_id_base32); memset(want_desc_id, 0, sizeof(want_desc_id)); + if (entry) { + *entry = NULL; + } if (base32_decode(want_desc_id, sizeof(want_desc_id), desc_id_base32, strlen(desc_id_base32)) != 0) { log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.", @@ -1177,7 +1216,8 @@ rend_cache_store_v2_desc_as_client(const char *desc, log_warn(LD_REND, "Couldn't compute service ID."); goto err; } - if (strcmp(rend_query->onion_address, service_id)) { + if (rend_query->onion_address[0] != '\0' && + strcmp(rend_query->onion_address, service_id)) { log_warn(LD_REND, "Received service descriptor for service ID %s; " "expected descriptor for service ID %s.", service_id, safe_str(rend_query->onion_address)); @@ -1224,7 +1264,7 @@ rend_cache_store_v2_desc_as_client(const char *desc, "service descriptor for %s. This is probably a (misguided) " "attempt to improve reliability, but it could also be an " "attempt to do a guard enumeration attack. Rejecting.", - safe_str_client(rend_query->onion_address)); + safe_str_client(service_id)); goto err; } @@ -1270,9 +1310,15 @@ rend_cache_store_v2_desc_as_client(const char *desc, rend_cache_increment_allocation(rend_cache_entry_allocation(e)); log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", safe_str_client(service_id), (int)encoded_size); + if (entry) { + *entry = e; + } return RCS_OKAY; okay: + if (entry) { + *entry = e; + } retval = RCS_OKAY; err: @@ -1354,7 +1400,116 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, rend_data_t * rend_data_dup(const rend_data_t *data) { + rend_data_t *data_dup; tor_assert(data); - return tor_memdup(data, sizeof(rend_data_t)); + data_dup = tor_memdup(data, sizeof(rend_data_t)); + data_dup->hsdirs_fp = smartlist_new(); + SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp, + smartlist_add(data_dup->hsdirs_fp, + tor_memdup(fp, DIGEST_LEN))); + return data_dup; +} + +/** Compute descriptor ID for each replicas and save them. A valid onion + * address must be present in the <b>rend_data</b>. + * + * Return 0 on success else -1. */ +static int +compute_desc_id(rend_data_t *rend_data) +{ + int ret; + unsigned replica; + time_t now = time(NULL); + + tor_assert(rend_data); + + /* Compute descriptor ID for each replicas. */ + for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id); + replica++) { + ret = rend_compute_v2_desc_id(rend_data->descriptor_id[replica], + rend_data->onion_address, + rend_data->descriptor_cookie, + now, replica); + if (ret < 0) { + goto end; + } + } + + end: + return ret; +} + +/** Allocate and initialize a rend_data_t object for a service using the + * given arguments. Only the <b>onion_address</b> is not optional. + * + * Return a valid rend_data_t pointer. */ +rend_data_t * +rend_data_service_create(const char *onion_address, const char *pk_digest, + const uint8_t *cookie, rend_auth_type_t auth_type) +{ + rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data)); + + /* We need at least one else the call is wrong. */ + tor_assert(onion_address != NULL); + + if (pk_digest) { + memcpy(rend_data->rend_pk_digest, pk_digest, + sizeof(rend_data->rend_pk_digest)); + } + if (cookie) { + memcpy(rend_data->rend_cookie, cookie, + sizeof(rend_data->rend_cookie)); + } + + strlcpy(rend_data->onion_address, onion_address, + sizeof(rend_data->onion_address)); + rend_data->auth_type = auth_type; + /* Won't be used but still need to initialize it for rend_data dup and + * free. */ + rend_data->hsdirs_fp = smartlist_new(); + + return rend_data; +} + +/** Allocate and initialize a rend_data_t object for a client request using + * the given arguments. Either an onion address or a descriptor ID is + * needed. Both can be given but only the onion address will be used to make + * the descriptor fetch. + * + * Return a valid rend_data_t pointer or NULL on error meaning the + * descriptor IDs couldn't be computed from the given data. */ +rend_data_t * +rend_data_client_create(const char *onion_address, const char *desc_id, + const char *cookie, rend_auth_type_t auth_type) +{ + rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data)); + + /* We need at least one else the call is wrong. */ + tor_assert(onion_address != NULL || desc_id != NULL); + + if (cookie) { + memcpy(rend_data->descriptor_cookie, cookie, + sizeof(rend_data->descriptor_cookie)); + } + if (desc_id) { + memcpy(rend_data->desc_id_fetch, desc_id, + sizeof(rend_data->desc_id_fetch)); + } + if (onion_address) { + strlcpy(rend_data->onion_address, onion_address, + sizeof(rend_data->onion_address)); + if (compute_desc_id(rend_data) < 0) { + goto error; + } + } + + rend_data->auth_type = auth_type; + rend_data->hsdirs_fp = smartlist_new(); + + return rend_data; + + error: + rend_data_free(rend_data); + return NULL; } diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index 8396cc3551..0ed7adc710 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -16,6 +16,12 @@ static INLINE void rend_data_free(rend_data_t *data) { + if (!data) { + return; + } + /* Cleanup the HSDir identity digest. */ + SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d)); + smartlist_free(data->hsdirs_fp); tor_free(data); } @@ -37,6 +43,7 @@ void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove); void rend_cache_purge(void); void rend_cache_free_all(void); int rend_valid_service_id(const char *query); +int rend_valid_descriptor_id(const char *query); int rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **entry_out); int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc); @@ -50,7 +57,8 @@ typedef enum { rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc); rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc, const char *desc_id_base32, - const rend_data_t *rend_query); + const rend_data_t *rend_query, + rend_cache_entry_t **entry); int rend_encode_v2_descriptors(smartlist_t *descs_out, rend_service_descriptor_t *desc, time_t now, uint8_t period, rend_auth_type_t auth_type, @@ -65,5 +73,14 @@ void rend_get_descriptor_id_bytes(char *descriptor_id_out, const char *secret_id_part); size_t rend_cache_get_total_allocation(void); +rend_data_t *rend_data_dup(const rend_data_t *data); +rend_data_t *rend_data_client_create(const char *onion_address, + const char *desc_id, + const char *cookie, + rend_auth_type_t auth_type); +rend_data_t *rend_data_service_create(const char *onion_address, + const char *pk_digest, + const uint8_t *cookie, + rend_auth_type_t auth_type); #endif diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 111b369b1c..0329d70924 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -15,6 +15,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "control.h" #include "directory.h" #include "main.h" #include "networkstatus.h" @@ -42,9 +43,15 @@ static int intro_point_accepted_intro_count(rend_intro_point_t *intro); static int intro_point_should_expire_now(rend_intro_point_t *intro, time_t now); struct rend_service_t; +static int rend_service_derive_key_digests(struct rend_service_t *s); static int rend_service_load_keys(struct rend_service_t *s); static int rend_service_load_auth_keys(struct rend_service_t *s, const char *hfname); +static struct rend_service_t *rend_service_get_by_pk_digest( + const char* digest); +static struct rend_service_t *rend_service_get_by_service_id(const char *id); +static const char *rend_service_escaped_dir( + const struct rend_service_t *s); static ssize_t rend_service_parse_intro_for_v0_or_v1( rend_intro_cell_t *intro, @@ -65,7 +72,7 @@ static ssize_t rend_service_parse_intro_for_v3( /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. */ -typedef struct rend_service_port_config_t { +struct rend_service_port_config_s { /* The incoming HS virtual port we're mapping */ uint16_t virtual_port; /* Is this an AF_UNIX port? */ @@ -76,7 +83,7 @@ typedef struct rend_service_port_config_t { tor_addr_t real_addr; /* The socket path to connect to, if is_unix_addr */ char unix_addr[FLEXIBLE_ARRAY_MEMBER]; -} rend_service_port_config_t; +}; /** Try to maintain this many intro points per service by default. */ #define NUM_INTRO_POINTS_DEFAULT 3 @@ -102,7 +109,8 @@ typedef struct rend_service_port_config_t { /** Represents a single hidden service running at this OP. */ typedef struct rend_service_t { /* Fields specified in config file */ - char *directory; /**< where in the filesystem it stores it */ + char *directory; /**< where in the filesystem it stores it. Will be NULL if + * this service is ephemeral. */ int dir_group_readable; /**< if 1, allow group read permissions on directory */ smartlist_t *ports; /**< List of rend_service_port_config_t */ @@ -139,8 +147,23 @@ typedef struct rend_service_t { /** If true, we don't close circuits for making requests to unsupported * ports. */ int allow_unknown_ports; + /** The maximum number of simultanious streams-per-circuit that are allowed + * to be established, or 0 if no limit is set. + */ + int max_streams_per_circuit; + /** If true, we close circuits that exceed the max_streams_per_circuit + * limit. */ + int max_streams_close_circuit; } rend_service_t; +/** Returns a escaped string representation of the service, <b>s</b>. + */ +static const char * +rend_service_escaped_dir(const struct rend_service_t *s) +{ + return (s->directory) ? escaped(s->directory) : "[EPHEMERAL]"; +} + /** A list of rend_service_t's for services run on this OP. */ static smartlist_t *rend_service_list = NULL; @@ -173,7 +196,7 @@ rend_authorized_client_free(rend_authorized_client_t *client) return; if (client->client_key) crypto_pk_free(client->client_key); - tor_strclear(client->client_name); + memwipe(client->client_name, 0, strlen(client->client_name)); tor_free(client->client_name); memwipe(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie)); tor_free(client); @@ -195,7 +218,8 @@ rend_service_free(rend_service_t *service) return; tor_free(service->directory); - SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p)); + SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p, + rend_service_port_config_free(p)); smartlist_free(service->ports); if (service->private_key) crypto_pk_free(service->private_key); @@ -232,8 +256,9 @@ rend_service_free_all(void) } /** Validate <b>service</b> and add it to rend_service_list if possible. + * Return 0 on success and -1 on failure. */ -static void +static int rend_add_service(rend_service_t *service) { int i; @@ -241,20 +266,38 @@ rend_add_service(rend_service_t *service) service->intro_nodes = smartlist_new(); + if (service->max_streams_per_circuit < 0) { + log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max " + "streams per circuit; ignoring.", + rend_service_escaped_dir(service)); + rend_service_free(service); + return -1; + } + + if (service->max_streams_close_circuit < 0 || + service->max_streams_close_circuit > 1) { + log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid " + "max streams handling; ignoring.", + rend_service_escaped_dir(service)); + rend_service_free(service); + return -1; + } + if (service->auth_type != REND_NO_AUTH && smartlist_len(service->clients) == 0) { log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no " "clients; ignoring.", - escaped(service->directory)); + rend_service_escaped_dir(service)); rend_service_free(service); - return; + return -1; } if (!smartlist_len(service->ports)) { log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; " "ignoring.", - escaped(service->directory)); + rend_service_escaped_dir(service)); rend_service_free(service); + return -1; } else { int dupe = 0; /* XXX This duplicate check has two problems: @@ -272,14 +315,17 @@ rend_add_service(rend_service_t *service) * lock file. But this is enough to detect a simple mistake that * at least one person has actually made. */ - SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, - dupe = dupe || - !strcmp(ptr->directory, service->directory)); - if (dupe) { - log_warn(LD_REND, "Another hidden service is already configured for " - "directory %s, ignoring.", service->directory); - rend_service_free(service); - return; + if (service->directory != NULL) { /* Skip dupe for ephemeral services. */ + SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, + dupe = dupe || + !strcmp(ptr->directory, service->directory)); + if (dupe) { + log_warn(LD_REND, "Another hidden service is already configured for " + "directory %s, ignoring.", + rend_service_escaped_dir(service)); + rend_service_free(service); + return -1; + } } smartlist_add(rend_service_list, service); log_debug(LD_REND,"Configuring service with directory \"%s\"", @@ -305,7 +351,9 @@ rend_add_service(rend_service_t *service) #endif /* defined(HAVE_SYS_UN_H) */ } } + return 0; } + /* NOTREACHED */ } /** Return a new rend_service_port_config_t with its path set to @@ -324,15 +372,17 @@ rend_service_port_config_new(const char *socket_path) return conf; } -/** Parses a real-port to virtual-port mapping and returns a new - * rend_service_port_config_t. +/** Parses a real-port to virtual-port mapping separated by the provided + * separator and returns a new rend_service_port_config_t, or NULL and an + * optional error string on failure. * - * The format is: VirtualPort (IP|RealPort|IP:RealPort|'socket':path)? + * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)? * * IP defaults to 127.0.0.1; RealPort defaults to VirtualPort. */ -static rend_service_port_config_t * -parse_port_config(const char *string) +rend_service_port_config_t * +rend_service_parse_port_config(const char *string, const char *sep, + char **err_msg_out) { smartlist_t *sl; int virtport; @@ -343,19 +393,24 @@ parse_port_config(const char *string) rend_service_port_config_t *result = NULL; unsigned int is_unix_addr = 0; char *socket_path = NULL; + char *err_msg = NULL; sl = smartlist_new(); - smartlist_split_string(sl, string, " ", + smartlist_split_string(sl, string, sep, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) { - log_warn(LD_CONFIG, "Bad syntax in hidden service port configuration."); + if (err_msg_out) + err_msg = tor_strdup("Bad syntax in hidden service port configuration."); + goto err; } virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL); if (!virtport) { - log_warn(LD_CONFIG, "Missing or invalid port %s in hidden service port " - "configuration", escaped(smartlist_get(sl,0))); + if (err_msg_out) + tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service " + "port configuration", escaped(smartlist_get(sl,0))); + goto err; } @@ -369,10 +424,11 @@ parse_port_config(const char *string) addrport = smartlist_get(sl,1); ret = config_parse_unix_port(addrport, &socket_path); if (ret < 0 && ret != -ENOENT) { - if (ret == -EINVAL) { - log_warn(LD_CONFIG, - "Empty socket path in hidden service port configuration."); - } + if (ret == -EINVAL) + if (err_msg_out) + err_msg = tor_strdup("Empty socket path in hidden service port " + "configuration."); + goto err; } if (socket_path) { @@ -380,8 +436,10 @@ parse_port_config(const char *string) } else if (strchr(addrport, ':') || strchr(addrport, '.')) { /* else try it as an IP:port pair if it has a : or . in it */ if (tor_addr_port_lookup(addrport, &addr, &p)<0) { - log_warn(LD_CONFIG,"Unparseable address in hidden service port " - "configuration."); + if (err_msg_out) + err_msg = tor_strdup("Unparseable address in hidden service port " + "configuration."); + goto err; } realport = p?p:virtport; @@ -389,8 +447,11 @@ parse_port_config(const char *string) /* No addr:port, no addr -- must be port. */ realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL); if (!realport) { - log_warn(LD_CONFIG,"Unparseable or out-of-range port %s in hidden " - "service port configuration.", escaped(addrport)); + if (err_msg_out) + tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in " + "hidden service port configuration.", + escaped(addrport)); + goto err; } tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */ @@ -408,6 +469,7 @@ parse_port_config(const char *string) } err: + if (err_msg_out) *err_msg_out = err_msg; SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); if (socket_path) tor_free(socket_path); @@ -415,6 +477,13 @@ parse_port_config(const char *string) return result; } +/** Release all storage held in a rend_service_port_config_t. */ +void +rend_service_port_config_free(rend_service_port_config_t *p) +{ + tor_free(p); +} + /** Set up rend_service_list, based on the values of HiddenServiceDir and * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on * failure. (If <b>validate_only</b> is set, parse, warn and return as @@ -439,113 +508,145 @@ rend_config_services(const or_options_t *options, int validate_only) if (service) { /* register the one we just finished parsing */ if (validate_only) rend_service_free(service); - else - rend_add_service(service); - } - service = tor_malloc_zero(sizeof(rend_service_t)); - service->directory = tor_strdup(line->value); - service->ports = smartlist_new(); - service->intro_period_started = time(NULL); - service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; - continue; - } - if (!service) { - log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive", - line->key); - rend_service_free(service); - return -1; - } - if (!strcasecmp(line->key, "HiddenServicePort")) { - portcfg = parse_port_config(line->value); - if (!portcfg) { - rend_service_free(service); - return -1; - } - smartlist_add(service->ports, portcfg); - } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) { - service->allow_unknown_ports = (int)tor_parse_long(line->value, - 10, 0, 1, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, - "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s", - line->value); - rend_service_free(service); - return -1; - } - log_info(LD_CONFIG, - "HiddenServiceAllowUnknownPorts=%d for %s", - (int)service->allow_unknown_ports, service->directory); - } else if (!strcasecmp(line->key, - "HiddenServiceDirGroupReadable")) { - service->dir_group_readable = (int)tor_parse_long(line->value, - 10, 0, 1, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, - "HiddenServiceDirGroupReadable should be 0 or 1, not %s", - line->value); - rend_service_free(service); - return -1; - } - log_info(LD_CONFIG, - "HiddenServiceDirGroupReadable=%d for %s", - service->dir_group_readable, service->directory); - } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) { - /* Parse auth type and comma-separated list of client names and add a - * rend_authorized_client_t for each client to the service's list - * of authorized clients. */ - smartlist_t *type_names_split, *clients; - const char *authname; - int num_clients; - if (service->auth_type != REND_NO_AUTH) { - log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient " - "lines for a single service."); - rend_service_free(service); - return -1; - } - type_names_split = smartlist_new(); - smartlist_split_string(type_names_split, line->value, " ", 0, 2); - if (smartlist_len(type_names_split) < 1) { - log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This " - "should have been prevented when parsing the " - "configuration."); - smartlist_free(type_names_split); - rend_service_free(service); - return -1; - } - authname = smartlist_get(type_names_split, 0); - if (!strcasecmp(authname, "basic")) { - service->auth_type = REND_BASIC_AUTH; - } else if (!strcasecmp(authname, "stealth")) { - service->auth_type = REND_STEALTH_AUTH; - } else { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " - "unrecognized auth-type '%s'. Only 'basic' or 'stealth' " - "are recognized.", - (char *) smartlist_get(type_names_split, 0)); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - rend_service_free(service); - return -1; - } - service->clients = smartlist_new(); - if (smartlist_len(type_names_split) < 2) { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " - "auth-type '%s', but no client names.", - service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - continue; - } - clients = smartlist_new(); - smartlist_split_string(clients, smartlist_get(type_names_split, 1), - ",", SPLIT_SKIP_SPACE, 0); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - /* Remove duplicate client names. */ - num_clients = smartlist_len(clients); - smartlist_sort_strings(clients); - smartlist_uniq_strings(clients); - if (smartlist_len(clients) < num_clients) { + else + rend_add_service(service); + } + service = tor_malloc_zero(sizeof(rend_service_t)); + service->directory = tor_strdup(line->value); + service->ports = smartlist_new(); + service->intro_period_started = time(NULL); + service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; + continue; + } + if (!service) { + log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive", + line->key); + rend_service_free(service); + return -1; + } + if (!strcasecmp(line->key, "HiddenServicePort")) { + char *err_msg = NULL; + portcfg = rend_service_parse_port_config(line->value, " ", &err_msg); + if (!portcfg) { + if (err_msg) + log_warn(LD_CONFIG, "%s", err_msg); + tor_free(err_msg); + rend_service_free(service); + return -1; + } + tor_assert(!err_msg); + smartlist_add(service->ports, portcfg); + } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) { + service->allow_unknown_ports = (int)tor_parse_long(line->value, + 10, 0, 1, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s", + line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceAllowUnknownPorts=%d for %s", + (int)service->allow_unknown_ports, service->directory); + } else if (!strcasecmp(line->key, + "HiddenServiceDirGroupReadable")) { + service->dir_group_readable = (int)tor_parse_long(line->value, + 10, 0, 1, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceDirGroupReadable should be 0 or 1, not %s", + line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceDirGroupReadable=%d for %s", + service->dir_group_readable, service->directory); + } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) { + service->max_streams_per_circuit = (int)tor_parse_long(line->value, + 10, 0, 65535, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceMaxStreams should be between 0 and %d, not %s", + 65535, line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceMaxStreams=%d for %s", + service->max_streams_per_circuit, service->directory); + } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) { + service->max_streams_close_circuit = (int)tor_parse_long(line->value, + 10, 0, 1, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, not %s", + line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceMaxStreamsCloseCircuit=%d for %s", + (int)service->max_streams_close_circuit, service->directory); + + } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) { + /* Parse auth type and comma-separated list of client names and add a + * rend_authorized_client_t for each client to the service's list + * of authorized clients. */ + smartlist_t *type_names_split, *clients; + const char *authname; + int num_clients; + if (service->auth_type != REND_NO_AUTH) { + log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient " + "lines for a single service."); + rend_service_free(service); + return -1; + } + type_names_split = smartlist_new(); + smartlist_split_string(type_names_split, line->value, " ", 0, 2); + if (smartlist_len(type_names_split) < 1) { + log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This " + "should have been prevented when parsing the " + "configuration."); + smartlist_free(type_names_split); + rend_service_free(service); + return -1; + } + authname = smartlist_get(type_names_split, 0); + if (!strcasecmp(authname, "basic")) { + service->auth_type = REND_BASIC_AUTH; + } else if (!strcasecmp(authname, "stealth")) { + service->auth_type = REND_STEALTH_AUTH; + } else { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " + "unrecognized auth-type '%s'. Only 'basic' or 'stealth' " + "are recognized.", + (char *) smartlist_get(type_names_split, 0)); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + rend_service_free(service); + return -1; + } + service->clients = smartlist_new(); + if (smartlist_len(type_names_split) < 2) { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " + "auth-type '%s', but no client names.", + service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + continue; + } + clients = smartlist_new(); + smartlist_split_string(clients, smartlist_get(type_names_split, 1), + ",", SPLIT_SKIP_SPACE, 0); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + /* Remove duplicate client names. */ + num_clients = smartlist_len(clients); + smartlist_sort_strings(clients); + smartlist_uniq_strings(clients); + if (smartlist_len(clients) < num_clients) { log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d " "duplicate client name(s); removing.", num_clients - smartlist_len(clients)); @@ -632,12 +733,35 @@ rend_config_services(const or_options_t *options, int validate_only) if (old_service_list && !validate_only) { smartlist_t *surviving_services = smartlist_new(); + /* Preserve the existing ephemeral services. + * + * This is the ephemeral service equivalent of the "Copy introduction + * points to new services" block, except there's no copy required since + * the service structure isn't regenerated. + * + * After this is done, all ephemeral services will be: + * * Removed from old_service_list, so the equivalent non-ephemeral code + * will not attempt to preserve them. + * * Added to the new rend_service_list (that previously only had the + * services listed in the configuration). + * * Added to surviving_services, which is the list of services that + * will NOT have their intro point closed. + */ + SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, { + if (!old->directory) { + SMARTLIST_DEL_CURRENT(old_service_list, old); + smartlist_add(surviving_services, old); + smartlist_add(rend_service_list, old); + } + }); + /* Copy introduction points to new services. */ /* XXXX This is O(n^2), but it's only called on reconfigure, so it's * probably ok? */ SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) { SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) { - if (!strcmp(old->directory, new->directory)) { + if (new->directory && old->directory && + !strcmp(old->directory, new->directory)) { smartlist_add_all(new->intro_nodes, old->intro_nodes); smartlist_clear(old->intro_nodes); smartlist_add(surviving_services, old); @@ -685,6 +809,125 @@ rend_config_services(const or_options_t *options, int validate_only) return 0; } +/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, with + * <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit, + * and circuit closure on max streams being exceeded set by + * <b>max_streams_close_circuit</b>. + * + * Regardless of sucess/failure, callers should not touch pk/ports after + * calling this routine, and may assume that correct cleanup has been done + * on failure. + * + * Return an appropriate rend_service_add_ephemeral_status_t. + */ +rend_service_add_ephemeral_status_t +rend_service_add_ephemeral(crypto_pk_t *pk, + smartlist_t *ports, + int max_streams_per_circuit, + int max_streams_close_circuit, + char **service_id_out) +{ + *service_id_out = NULL; + /* Allocate the service structure, and initialize the key, and key derived + * parameters. + */ + rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t)); + s->directory = NULL; /* This indicates the service is ephemeral. */ + s->private_key = pk; + s->auth_type = REND_NO_AUTH; + s->ports = ports; + s->intro_period_started = time(NULL); + s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; + s->max_streams_per_circuit = max_streams_per_circuit; + s->max_streams_close_circuit = max_streams_close_circuit; + if (rend_service_derive_key_digests(s) < 0) { + rend_service_free(s); + return RSAE_BADPRIVKEY; + } + + if (!s->ports || smartlist_len(s->ports) == 0) { + log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified."); + rend_service_free(s); + return RSAE_BADVIRTPORT; + } + + /* Enforcing pk/id uniqueness should be done by rend_service_load_keys(), but + * it's not, see #14828. + */ + if (rend_service_get_by_pk_digest(s->pk_digest)) { + log_warn(LD_CONFIG, "Onion Service private key collides with an " + "existing service."); + rend_service_free(s); + return RSAE_ADDREXISTS; + } + if (rend_service_get_by_service_id(s->service_id)) { + log_warn(LD_CONFIG, "Onion Service id collides with an existing service."); + rend_service_free(s); + return RSAE_ADDREXISTS; + } + + /* Initialize the service. */ + if (rend_add_service(s)) { + rend_service_free(s); + return RSAE_INTERNAL; + } + *service_id_out = tor_strdup(s->service_id); + + log_debug(LD_CONFIG, "Added ephemeral Onion Service: %s", s->service_id); + return RSAE_OKAY; +} + +/** Remove the ephemeral service <b>service_id</b> if possible. Returns 0 on + * success, and -1 on failure. + */ +int +rend_service_del_ephemeral(const char *service_id) +{ + rend_service_t *s; + if (!rend_valid_service_id(service_id)) { + log_warn(LD_CONFIG, "Requested malformed Onion Service id for removal."); + return -1; + } + if ((s = rend_service_get_by_service_id(service_id)) == NULL) { + log_warn(LD_CONFIG, "Requested non-existent Onion Service id for " + "removal."); + return -1; + } + if (s->directory) { + log_warn(LD_CONFIG, "Requested non-ephemeral Onion Service for removal."); + return -1; + } + + /* Kill the intro point circuit for the Onion Service, and remove it from + * the list. Closing existing connections is the application's problem. + * + * XXX: As with the comment in rend_config_services(), a nice abstraction + * would be ideal here, but for now just duplicate the code. + */ + SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { + if (!circ->marked_for_close && + circ->state == CIRCUIT_STATE_OPEN && + (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || + circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) { + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + tor_assert(oc->rend_data); + if (!tor_memeq(s->pk_digest, oc->rend_data->rend_pk_digest, DIGEST_LEN)) + continue; + log_debug(LD_REND, "Closing intro point %s for service %s.", + safe_str_client(extend_info_describe( + oc->build_state->chosen_exit)), + oc->rend_data->onion_address); + circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); + } + } SMARTLIST_FOREACH_END(circ); + smartlist_remove(rend_service_list, s); + rend_service_free(s); + + log_debug(LD_CONFIG, "Removed ephemeral Onion Service: %s", service_id); + + return 0; +} + /** Replace the old value of <b>service</b>-\>desc with one that reflects * the other fields in service. */ @@ -769,6 +1012,7 @@ rend_service_add_filenames_to_list(smartlist_t *lst, const rend_service_t *s) { tor_assert(lst); tor_assert(s); + tor_assert(s->directory); smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"private_key", s->directory); smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"hostname", @@ -787,11 +1031,31 @@ rend_services_add_filenames_to_lists(smartlist_t *open_lst, if (!rend_service_list) return; SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) { - rend_service_add_filenames_to_list(open_lst, s); - smartlist_add(stat_lst, tor_strdup(s->directory)); + if (s->directory) { + rend_service_add_filenames_to_list(open_lst, s); + smartlist_add(stat_lst, tor_strdup(s->directory)); + } } SMARTLIST_FOREACH_END(s); } +/** Derive all rend_service_t internal material based on the service's key. + * Returns 0 on sucess, -1 on failure. + */ +static int +rend_service_derive_key_digests(struct rend_service_t *s) +{ + if (rend_get_service_id(s->private_key, s->service_id)<0) { + log_warn(LD_BUG, "Internal error: couldn't encode service ID."); + return -1; + } + if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) { + log_warn(LD_BUG, "Couldn't compute hash of public key."); + return -1; + } + + return 0; +} + /** Load and/or generate private keys for the hidden service <b>s</b>, * possibly including keys for client authorization. Return 0 on success, -1 * on failure. */ @@ -830,15 +1094,10 @@ rend_service_load_keys(rend_service_t *s) if (!s->private_key) return -1; - /* Create service file */ - if (rend_get_service_id(s->private_key, s->service_id)<0) { - log_warn(LD_BUG, "Internal error: couldn't encode service ID."); - return -1; - } - if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) { - log_warn(LD_BUG, "Couldn't compute hash of public key."); + if (rend_service_derive_key_digests(s) < 0) return -1; - } + + /* Create service file */ if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname)) >= sizeof(fname)) { @@ -941,7 +1200,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) } if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, client->descriptor_cookie, - REND_DESC_COOKIE_LEN) < 0) { + REND_DESC_COOKIE_LEN, 0) < 0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); goto err; } @@ -968,7 +1227,6 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) client->client_key = prkey; } /* Add entry to client_keys file. */ - desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */ written = tor_snprintf(buf, sizeof(buf), "client-name %s\ndescriptor-cookie %s\n", client->client_name, desc_cook_out); @@ -1023,12 +1281,11 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) ((int)s->auth_type - 1) << 4; if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, extended_desc_cookie, - REND_DESC_COOKIE_LEN+1) < 0) { + REND_DESC_COOKIE_LEN+1, 0) < 0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); goto err; } - desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and - newline. */ + desc_cook_out[strlen(desc_cook_out)-2] = '\0'; /* Remove A=. */ tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n", service_id, desc_cook_out, client->client_name); } @@ -1052,7 +1309,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) abort_writing_to_file(open_hfile); done: if (client_keys_str) { - tor_strclear(client_keys_str); + memwipe(client_keys_str, 0, strlen(client_keys_str)); tor_free(client_keys_str); } strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); @@ -1080,6 +1337,20 @@ rend_service_get_by_pk_digest(const char* digest) return NULL; } +/** Return the service whose service id is <b>id</b>, or NULL if no such + * service exists. + */ +static struct rend_service_t * +rend_service_get_by_service_id(const char *id) +{ + tor_assert(strlen(id) == REND_SERVICE_ID_LEN_BASE32); + SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s, { + if (tor_memeq(s->service_id, id, REND_SERVICE_ID_LEN_BASE32)) + return s; + }); + return NULL; +} + /** Return 1 if any virtual port in <b>service</b> wants a circuit * to have good uptime. Else return 0. */ @@ -1133,7 +1404,7 @@ rend_check_authorization(rend_service_t *service, if (!auth_client) { char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64), - descriptor_cookie, REND_DESC_COOKIE_LEN); + descriptor_cookie, REND_DESC_COOKIE_LEN, 0); log_info(LD_REND, "No authorization found for descriptor cookie '%s'! " "Dropping cell!", descriptor_cookie_base64); @@ -1167,16 +1438,17 @@ rend_service_note_removing_intro_point(rend_service_t *service, /* This intro point was never used. Don't change * n_intro_points_wanted. */ } else { + /* We want to increase the number of introduction points service * operates if intro was heavily used, or decrease the number of * intro points if intro was lightly used. * * We consider an intro point's target 'usage' to be - * INTRO_POINT_LIFETIME_INTRODUCTIONS introductions in + * maximum of INTRODUCE2 cells divided by * INTRO_POINT_LIFETIME_MIN_SECONDS seconds. To calculate intro's - * fraction of target usage, we divide the fraction of - * _LIFETIME_INTRODUCTIONS introductions that it has handled by - * the fraction of _LIFETIME_MIN_SECONDS for which it existed. + * fraction of target usage, we divide the amount of INTRODUCE2 cells + * that it has handled by the fraction of _LIFETIME_MIN_SECONDS for + * which it existed. * * Then we multiply that fraction of desired usage by a fudge * factor of 1.5, to decide how many new introduction points @@ -1198,7 +1470,7 @@ rend_service_note_removing_intro_point(rend_service_t *service, intro_point_accepted_intro_count(intro) / (double)(now - intro->time_published); const double intro_point_target_usage = - INTRO_POINT_LIFETIME_INTRODUCTIONS / + intro->max_introductions / (double)INTRO_POINT_LIFETIME_MIN_SECONDS; const double fractional_n_intro_points_wanted_to_replace_this_one = (1.5 * (intro_point_usage / intro_point_target_usage)); @@ -1533,13 +1805,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, hexcookie, serviceid); tor_assert(launched->build_state); /* Fill in the circuit's state. */ - launched->rend_data = tor_malloc_zero(sizeof(rend_data_t)); - memcpy(launched->rend_data->rend_pk_digest, - circuit->rend_data->rend_pk_digest, - DIGEST_LEN); - memcpy(launched->rend_data->rend_cookie, parsed_req->rc, REND_COOKIE_LEN); - strlcpy(launched->rend_data->onion_address, service->service_id, - sizeof(launched->rend_data->onion_address)); + + launched->rend_data = + rend_data_service_create(service->service_id, + circuit->rend_data->rend_pk_digest, + parsed_req->rc, service->auth_type); launched->build_state->service_pending_final_cpath_ref = tor_malloc_zero(sizeof(crypt_path_reference_t)); @@ -2511,10 +2781,9 @@ rend_service_launch_establish_intro(rend_service_t *service, intro->extend_info = extend_info_dup(launched->build_state->chosen_exit); } - launched->rend_data = tor_malloc_zero(sizeof(rend_data_t)); - strlcpy(launched->rend_data->onion_address, service->service_id, - sizeof(launched->rend_data->onion_address)); - memcpy(launched->rend_data->rend_pk_digest, service->pk_digest, DIGEST_LEN); + launched->rend_data = rend_data_service_create(service->service_id, + service->pk_digest, NULL, + service->auth_type); launched->intro_key = crypto_pk_dup_key(intro->intro_key); if (launched->base_.state == CIRCUIT_STATE_OPEN) rend_service_intro_has_opened(launched); @@ -2899,14 +3168,16 @@ find_intro_point(origin_circuit_t *circ) 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 - * purposes. */ -static void +/** Upload the rend_encoded_v2_service_descriptor_t's in <b>descs</b> + * associated with the rend_service_descriptor_t <b>renddesc</b> to + * the responsible hidden service directories OR the hidden service + * directories specified by <b>hs_dirs</b>; <b>service_id</b> and + * <b>seconds_valid</b> are only passed for logging purposes. + */ +void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, - smartlist_t *descs, const char *service_id, - int seconds_valid) + smartlist_t *descs, smartlist_t *hs_dirs, + const char *service_id, int seconds_valid) { int i, j, failed_upload = 0; smartlist_t *responsible_dirs = smartlist_new(); @@ -2914,14 +3185,21 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, routerstatus_t *hs_dir; for (i = 0; i < smartlist_len(descs); i++) { rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i); - /* Determine responsible dirs. */ - if (hid_serv_get_responsible_directories(responsible_dirs, - desc->desc_id) < 0) { - log_warn(LD_REND, "Could not determine the responsible hidden service " - "directories to post descriptors to."); - smartlist_free(responsible_dirs); - smartlist_free(successful_uploads); - return; + /** If any HSDirs are specified, they should be used instead of + * the responsible directories */ + if (hs_dirs && smartlist_len(hs_dirs) > 0) { + smartlist_add_all(responsible_dirs, hs_dirs); + } else { + /* Determine responsible dirs. */ + if (hid_serv_get_responsible_directories(responsible_dirs, + desc->desc_id) < 0) { + log_warn(LD_REND, "Could not determine the responsible hidden service " + "directories to post descriptors to."); + control_event_hs_descriptor_upload(service_id, + "UNKNOWN", + "UNKNOWN"); + goto done; + } } for (j = 0; j < smartlist_len(responsible_dirs); j++) { char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; @@ -2961,6 +3239,9 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, hs_dir->nickname, hs_dir_ip, hs_dir->or_port); + control_event_hs_descriptor_upload(service_id, + hs_dir->identity_digest, + desc_id_base32); tor_free(hs_dir_ip); /* Remember successful upload to this router for next time. */ if (!smartlist_contains_digest(successful_uploads, @@ -2988,6 +3269,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, } }); } + done: smartlist_free(responsible_dirs); smartlist_free(successful_uploads); } @@ -3052,7 +3334,7 @@ upload_service_descriptor(rend_service_t *service) rend_get_service_id(service->desc->pk, serviceid); log_info(LD_REND, "Launching upload for hidden service %s", serviceid); - directory_post_to_hs_dir(service->desc, descs, serviceid, + directory_post_to_hs_dir(service->desc, descs, NULL, serviceid, seconds_valid); /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) @@ -3081,7 +3363,7 @@ upload_service_descriptor(rend_service_t *service) smartlist_free(client_cookies); return; } - directory_post_to_hs_dir(service->desc, descs, serviceid, + directory_post_to_hs_dir(service->desc, descs, NULL, serviceid, seconds_valid); /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) @@ -3133,7 +3415,7 @@ intro_point_should_expire_now(rend_intro_point_t *intro, } if (intro_point_accepted_intro_count(intro) >= - INTRO_POINT_LIFETIME_INTRODUCTIONS) { + intro->max_introductions) { /* This intro point has been used too many times. Expire it now. */ return 1; } @@ -3142,9 +3424,8 @@ intro_point_should_expire_now(rend_intro_point_t *intro, /* 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); + crypto_rand_int_range(INTRO_POINT_LIFETIME_MIN_SECONDS, + INTRO_POINT_LIFETIME_MAX_SECONDS); /* Start the expiration timer now, rather than when the intro * point was first published. There shouldn't be much of a time @@ -3329,7 +3610,8 @@ rend_services_introduce(void) log_warn(LD_REND, "Could only establish %d introduction points for %s; " "wanted %u.", - smartlist_len(service->intro_nodes), service->service_id, + smartlist_len(service->intro_nodes), + safe_str_client(service->service_id), n_intro_points_to_open); break; } @@ -3340,10 +3622,14 @@ rend_services_introduce(void) intro = tor_malloc_zero(sizeof(rend_intro_point_t)); intro->extend_info = extend_info_from_node(node, 0); intro->intro_key = crypto_pk_new(); - tor_assert(!crypto_pk_generate_key(intro->intro_key)); + const int fail = crypto_pk_generate_key(intro->intro_key); + tor_assert(!fail); intro->time_published = -1; intro->time_to_expire = -1; intro->time_expiring = -1; + intro->max_introductions = + crypto_rand_int_range(INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS, + INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS); 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)), @@ -3567,6 +3853,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn, serviceid, (unsigned)circ->base_.n_circ_id); return -2; } + if (service->max_streams_per_circuit > 0) { + /* Enforce the streams-per-circuit limit, and refuse to provide a + * mapping if this circuit will exceed the limit. */ +#define MAX_STREAM_WARN_INTERVAL 600 + static struct ratelim_t stream_ratelim = + RATELIM_INIT(MAX_STREAM_WARN_INTERVAL); + if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) { + log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND, + "Maximum streams per circuit limit reached on rendezvous " + "circuit %u; %s. Circuit has %d out of %d streams.", + (unsigned)circ->base_.n_circ_id, + service->max_streams_close_circuit ? + "closing circuit" : + "ignoring open stream request", + circ->rend_data->nr_streams, + service->max_streams_per_circuit); + return service->max_streams_close_circuit ? -2 : -1; + } + } matching_ports = smartlist_new(); SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p, { diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 754f7c358c..b540d2c8ad 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -15,6 +15,7 @@ #include "or.h" typedef struct rend_intro_cell_s rend_intro_cell_t; +typedef struct rend_service_port_config_s rend_service_port_config_t; #ifdef RENDSERVICE_PRIVATE @@ -101,5 +102,29 @@ int rend_service_set_connection_addr_port(edge_connection_t *conn, void rend_service_dump_stats(int severity); void rend_service_free_all(void); +rend_service_port_config_t *rend_service_parse_port_config(const char *string, + const char *sep, + char **err_msg_out); +void rend_service_port_config_free(rend_service_port_config_t *p); + +/** Return value from rend_service_add_ephemeral. */ +typedef enum { + RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ + RSAE_ADDREXISTS = -3, /**< Onion address collision */ + RSAE_BADPRIVKEY = -2, /**< Invalid public key */ + RSAE_INTERNAL = -1, /**< Internal error */ + RSAE_OKAY = 0 /**< Service added as expected */ +} rend_service_add_ephemeral_status_t; +rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, + smartlist_t *ports, + int max_streams_per_circuit, + int max_streams_close_circuit, + char **service_id_out); +int rend_service_del_ephemeral(const char *service_id); + +void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, + smartlist_t *descs, smartlist_t *hs_dirs, + const char *service_id, int seconds_valid); + #endif diff --git a/src/or/router.c b/src/or/router.c index 2ddaa895fc..24b7c750f6 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -683,7 +683,9 @@ router_initialize_tls_context(void) if (!lifetime) { /* we should guess a good ssl cert lifetime */ /* choose between 5 and 365 days, and round to the day */ - lifetime = 5*24*3600 + crypto_rand_int(361*24*3600); + unsigned int five_days = 5*24*3600; + unsigned int one_year = 365*24*3600; + lifetime = crypto_rand_int_range(five_days, one_year); lifetime -= lifetime % (24*3600); if (crypto_rand_int(2)) { @@ -1802,12 +1804,15 @@ router_pick_published_address(const or_options_t *options, uint32_t *addr) return 0; } -/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh - * routerinfo, signed server descriptor, and extra-info document for this OR. - * Return 0 on success, -1 on temporary error. +/** Build a fresh routerinfo, signed server descriptor, and extra-info document + * for this OR. Set r to the generated routerinfo, e to the generated + * extra-info document. Return 0 on success, -1 on temporary error. Failure to + * generate an extra-info document is not an error and is indicated by setting + * e to NULL. Caller is responsible for freeing generated documents if 0 is + * returned. */ int -router_rebuild_descriptor(int force) +router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) { routerinfo_t *ri; extrainfo_t *ei; @@ -1816,20 +1821,11 @@ router_rebuild_descriptor(int force) int hibernating = we_are_hibernating(); const or_options_t *options = get_options(); - if (desc_clean_since && !force) - return 0; - - if (router_pick_published_address(options, &addr) < 0 || - router_get_advertised_or_port(options) == 0) { - /* Stop trying to rebuild our descriptor every second. We'll - * learn that it's time to try again when ip_address_changed() - * marks it dirty. */ - desc_clean_since = time(NULL); + if (router_pick_published_address(options, &addr) < 0) { + log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); return -1; } - log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : ""); - ri = tor_malloc_zero(sizeof(routerinfo_t)); ri->cache_info.routerlist_index = -1; ri->nickname = tor_strdup(options->Nickname); @@ -2024,6 +2020,41 @@ router_rebuild_descriptor(int force) tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL)); } + *r = ri; + *e = ei; + return 0; +} + +/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh + * routerinfo, signed server descriptor, and extra-info document for this OR. + * Return 0 on success, -1 on temporary error. + */ +int +router_rebuild_descriptor(int force) +{ + routerinfo_t *ri; + extrainfo_t *ei; + uint32_t addr; + const or_options_t *options = get_options(); + + if (desc_clean_since && !force) + return 0; + + if (router_pick_published_address(options, &addr) < 0 || + router_get_advertised_or_port(options) == 0) { + /* Stop trying to rebuild our descriptor every second. We'll + * learn that it's time to try again when ip_address_changed() + * marks it dirty. */ + desc_clean_since = time(NULL); + return -1; + } + + log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : ""); + + if (router_build_fresh_descriptor(&ri, &ei) < 0) { + return -1; + } + routerinfo_free(desc_routerinfo); desc_routerinfo = ri; extrainfo_free(desc_extrainfo); @@ -2424,7 +2455,7 @@ router_dump_router_to_string(routerinfo_t *router, char kbuf[128]; base64_encode(kbuf, sizeof(kbuf), (const char *)router->onion_curve25519_pkey->public_key, - CURVE25519_PUBKEY_LEN); + CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); } diff --git a/src/or/router.h b/src/or/router.h index 8108ffb22f..73b43bc751 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -89,6 +89,7 @@ const uint8_t *router_get_my_id_digest(void); int router_extrainfo_digest_is_me(const char *digest); int router_is_me(const routerinfo_t *router); int router_pick_published_address(const or_options_t *options, uint32_t *addr); +int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, crypto_pk_t *ident_key); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index f4f6200bbc..fd096799de 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -37,7 +37,7 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "../common/sandbox.h" +#include "sandbox.h" // #define DEBUG_ROUTERLIST /****************************************************************************/ @@ -1498,9 +1498,6 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, if ((type & EXTRAINFO_DIRINFO) && !router_supports_extrainfo(node->identity, is_trusted_extrainfo)) continue; - if ((type & MICRODESC_DIRINFO) && !is_trusted && - !node->rs->version_supports_microdesc_cache) - continue; if (for_guard && node->using_as_guard) continue; /* Don't make the same node a guard twice. */ if (try_excluding && diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 9c6651292c..f15aeeb0cf 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -664,7 +664,8 @@ router_get_dirobj_signature(const char *digest, goto truncated; i = strlen(buf); - if (base64_encode(buf+i, buf_len-i, signature, siglen) < 0) { + if (base64_encode(buf+i, buf_len-i, signature, siglen, + BASE64_ENCODE_MULTILINE) < 0) { log_warn(LD_BUG,"couldn't base64-encode signature"); goto err; } @@ -2015,10 +2016,7 @@ routerstatus_parse_entry_from_string(memarea_t *area, tor_assert(tok->n_args == 1); rs->version_known = 1; if (strcmpstart(tok->args[0], "Tor ")) { - rs->version_supports_microdesc_cache = 1; } else { - rs->version_supports_microdesc_cache = - tor_version_supports_microdescriptors(tok->args[0]); rs->version_supports_extend2_cells = tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha"); } @@ -3363,7 +3361,9 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action)) { directory_token_t *tok = NULL; const char *cp, *eos; - /* Longest possible policy is "accept ffff:ffff:..255/ffff:...255:0-65535". + /* Longest possible policy is + * "accept6 ffff:ffff:..255/ffff:...255:10000-65535", + * which contains 2 max-length IPv6 addresses, plus 21 characters. * But note that there can be an arbitrary amount of space between the * accept and the address:mask/port element. */ char line[TOR_ADDR_BUF_LEN*2 + 32]; @@ -4263,14 +4263,6 @@ microdescs_parse_from_string(const char *s, const char *eos, return result; } -/** Return true iff this Tor version can answer directory questions - * about microdescriptors. */ -int -tor_version_supports_microdescriptors(const char *platform) -{ - return tor_version_as_new_as(platform, "0.2.3.1-alpha"); -} - /** Parse the Tor version of the platform string <b>platform</b>, * and compare it to the version in <b>cutoff</b>. Return 1 if * the router is at least as new as the cutoff, else return 0. @@ -4583,8 +4575,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR); tor_assert(tok == smartlist_get(tokens, 0)); tor_assert(tok->n_args == 1); - if (strlen(tok->args[0]) != REND_DESC_ID_V2_LEN_BASE32 || - strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) { + if (!rend_valid_descriptor_id(tok->args[0])) { log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]); goto err; } diff --git a/src/or/routerparse.h b/src/or/routerparse.h index fc21cb1041..e294d95391 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -44,7 +44,6 @@ MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string, (const char *s, int assume_action)); version_status_t tor_version_is_obsolete(const char *myversion, const char *versionlist); -int tor_version_supports_microdescriptors(const char *platform); int tor_version_as_new_as(const char *platform, const char *cutoff); int tor_version_parse(const char *s, tor_version_t *out); int tor_version_compare(tor_version_t *a, tor_version_t *b); diff --git a/src/or/transports.c b/src/or/transports.c index 6f07054ea8..ba2c784c2c 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -1388,6 +1388,11 @@ create_managed_proxy_environment(const managed_proxy_t *mp) } else { smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT="); } + + /* All new versions of tor will keep stdin open, so PTs can use it + * as a reliable termination detection mechanism. + */ + smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1"); } else { /* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the * TOR_PT_PROXY line. diff --git a/src/test/bench.c b/src/test/bench.c index 5cbc072700..bc2b1f04d8 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -19,11 +19,9 @@ const char tor_git_revision[] = ""; #include "relay.h" #include <openssl/opensslv.h> #include <openssl/evp.h> -#ifndef OPENSSL_NO_EC #include <openssl/ec.h> #include <openssl/ecdh.h> #include <openssl/obj_mac.h> -#endif #include "config.h" #include "crypto_curve25519.h" @@ -502,9 +500,6 @@ bench_dh(void) " %f millisec each.\n", NANOCOUNT(start, end, iters)/1e6); } -#if (!defined(OPENSSL_NO_EC) \ - && OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)) -#define HAVE_EC_BENCHMARKS static void bench_ecdh_impl(int nid, const char *name) { @@ -554,7 +549,6 @@ bench_ecdh_p224(void) { bench_ecdh_impl(NID_secp224r1, "P-224"); } -#endif typedef void (*bench_fn)(void); @@ -577,10 +571,8 @@ static struct benchmark_t benchmarks[] = { ENT(cell_aes), ENT(cell_ops), ENT(dh), -#ifdef HAVE_EC_BENCHMARKS ENT(ecdh_p256), ENT(ecdh_p224), -#endif {NULL,NULL,0} }; @@ -625,7 +617,7 @@ main(int argc, const char **argv) reset_perftime(); - crypto_seed_rng(1); + crypto_seed_rng(); crypto_init_siphash_key(); options = options_new(); init_logging(1); diff --git a/src/test/bt_test.py b/src/test/bt_test.py index 0afe797a6d..e694361703 100755 --- a/src/test/bt_test.py +++ b/src/test/bt_test.py @@ -36,7 +36,7 @@ LINES = sys.stdin.readlines() for I in range(len(LINES)): if matches(LINES[I:], FUNCNAMES): print("OK") - break + sys.exit(0) else: print("BAD") - + sys.exit(1) diff --git a/src/test/include.am b/src/test/include.am index d20d2f66b9..c672b3fedc 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -1,10 +1,32 @@ -TESTS += src/test/test src/test/test-slow + +TESTSCRIPTS = src/test/test_zero_length_keys.sh + +if USEPYTHON +TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh +endif + +TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ + $(TESTSCRIPTS) + +### This is a lovely feature, but it requires automake >= 1.12, and Tor +### doesn't require that yet. Below is a kludge to work around. +### +# TEST_EXTENSIONS = .sh +# SH_LOG_COMPILER = $(SHELL) + +check-am: set-test-permissions + +.PHONY: set-test-permissions +set-test-permissions: $(TESTSCRIPTS) + $(AM_V_at)chmod u+x $(TESTSCRIPTS) + noinst_PROGRAMS+= src/test/bench if UNITTESTS_ENABLED noinst_PROGRAMS+= \ src/test/test \ src/test/test-slow \ + src/test/test-memwipe \ src/test/test-child \ src/test/test_workqueue endif @@ -34,6 +56,7 @@ src_test_test_SOURCES = \ src/test/test_circuitmux.c \ src/test/test_config.c \ src/test/test_containers.c \ + src/test/test_controller.c \ src/test/test_controller_events.c \ src/test/test_crypto.c \ src/test/test_data.c \ @@ -73,10 +96,13 @@ src_test_test_slow_SOURCES = \ src/test/testing_common.c \ src/ext/tinytest.c +src_test_test_memwipe_SOURCES = \ + src/test/test-memwipe.c + src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) -src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS) +src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) src_test_bench_SOURCES = \ src/test/bench.c @@ -89,7 +115,7 @@ src_test_test_workqueue_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ - src/common/libor-crypto-testing.a $(LIBDONNA) \ + src/common/libor-crypto-testing.a $(LIBDONNA) src/common/libor.a \ src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ @@ -100,6 +126,11 @@ src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS) src_test_test_slow_LDADD = $(src_test_test_LDADD) src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS) +src_test_test_memwipe_CPPFLAGS = $(src_test_test_CPPFLAGS) +src_test_test_memwipe_CFLAGS = $(src_test_test_CFLAGS) +src_test_test_memwipe_LDADD = $(src_test_test_LDADD) +src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS) + src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ @@ -137,13 +168,6 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ src_test_test_ntor_cl_AM_CPPFLAGS = \ -I"$(top_srcdir)/src/or" -NTOR_TEST_DEPS=src/test/test-ntor-cl - -if COVERAGE_ENABLED -CMDLINE_TEST_TOR = ./src/or/tor-cov -else -CMDLINE_TEST_TOR = ./src/or/tor -endif noinst_PROGRAMS += src/test/test-bt-cl src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c @@ -151,22 +175,10 @@ src_test_test_bt_cl_LDADD = src/common/libor-testing.a \ @TOR_LIB_MATH@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) -src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) - - -check-local: $(NTOR_TEST_DEPS) $(CMDLINE_TEST_TOR) -if USEPYTHON - $(PYTHON) $(top_srcdir)/src/test/test_cmdline_args.py $(CMDLINE_TEST_TOR) "${top_srcdir}" - $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py test-tor - $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py self-test - ./src/test/test-bt-cl assert | $(PYTHON) $(top_srcdir)/src/test/bt_test.py - ./src/test/test-bt-cl crash | $(PYTHON) $(top_srcdir)/src/test/bt_test.py -endif - $(top_srcdir)/src/test/zero_length_keys.sh +src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) EXTRA_DIST += \ src/test/bt_test.py \ src/test/ntor_ref.py \ src/test/slownacl_curve25519.py \ - src/test/test_cmdline_args.py \ src/test/zero_length_keys.sh diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py index e37637d92a..767da57a9c 100755 --- a/src/test/ntor_ref.py +++ b/src/test/ntor_ref.py @@ -283,7 +283,7 @@ def client_part2(seckey_x, msg, node_id, pubkey_B, keyBytes=72): my_auth = H_mac(auth_input) badness = my_auth != their_auth - badness = bad_result(yx) + bad_result(bx) + badness |= bad_result(yx) + bad_result(bx) if badness: return None diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c new file mode 100644 index 0000000000..cd3900e277 --- /dev/null +++ b/src/test/test-memwipe.c @@ -0,0 +1,207 @@ +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <stdlib.h> + +#include "crypto.h" +#include "compat.h" + +#undef MIN +#define MIN(a,b) ( ((a)<(b)) ? (a) : (b) ) + +static unsigned fill_a_buffer_memset(void) __attribute__((noinline)); +static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline)); +static unsigned fill_a_buffer_nothing(void) __attribute__((noinline)); +static unsigned fill_heap_buffer_memset(void) __attribute__((noinline)); +static unsigned fill_heap_buffer_memwipe(void) __attribute__((noinline)); +static unsigned fill_heap_buffer_nothing(void) __attribute__((noinline)); +static unsigned check_a_buffer(void) __attribute__((noinline)); + +const char *s = NULL; + +#define FILL_BUFFER_IMPL() \ + unsigned int i; \ + unsigned sum = 0; \ + \ + /* Fill up a 1k buffer with a recognizable pattern. */ \ + for (i = 0; i < 2048; i += strlen(s)) { \ + memcpy(buf+i, s, MIN(strlen(s), 2048-i)); \ + } \ + \ + /* Use the buffer as input to a computation so the above can't get */ \ + /* optimized away. */ \ + for (i = 0; i < 2048; ++i) { \ + sum += (unsigned char)buf[i]; \ + } + +static unsigned +fill_a_buffer_memset(void) +{ + char buf[2048]; + FILL_BUFFER_IMPL() + memset(buf, 0, sizeof(buf)); + return sum; +} + +static unsigned +fill_a_buffer_memwipe(void) +{ + char buf[2048]; + FILL_BUFFER_IMPL() + memwipe(buf, 0, sizeof(buf)); + return sum; +} + +static unsigned +fill_a_buffer_nothing(void) +{ + char buf[2048]; + FILL_BUFFER_IMPL() + return sum; +} + +static INLINE int +vmemeq(volatile char *a, const char *b, size_t n) +{ + while (n--) { + if (*a++ != *b++) + return 0; + } + return 1; +} + +static unsigned +check_a_buffer(void) +{ + unsigned int i; + volatile char buf[1024]; + unsigned sum = 0; + + /* See if this buffer has the string in it. + + YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM AN UNINITIALIZED + BUFFER. + + If you know a better way to figure out whether the compiler eliminated + the memset/memwipe calls or not, please let me know. + */ + for (i = 0; i < sizeof(buf); ++i) { + if (vmemeq(buf+i, s, strlen(s))) + ++sum; + } + + return sum; +} + +static char *heap_buf = NULL; + +static unsigned +fill_heap_buffer_memset(void) +{ + char *buf = heap_buf = malloc(2048); + FILL_BUFFER_IMPL() + memset(buf, 0, 2048); + free(buf); + return sum; +} + +static unsigned +fill_heap_buffer_memwipe(void) +{ + char *buf = heap_buf = malloc(2048); + FILL_BUFFER_IMPL() + memwipe(buf, 0, 2048); + free(buf); + return sum; +} + +static unsigned +fill_heap_buffer_nothing(void) +{ + char *buf = heap_buf = malloc(2048); + FILL_BUFFER_IMPL() + free(buf); + return sum; +} + +static unsigned +check_heap_buffer(void) +{ + unsigned int i; + unsigned sum = 0; + volatile char *buf = heap_buf; + + /* See if this buffer has the string in it. + + YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM A FREED BUFFER. + + If you know a better way to figure out whether the compiler eliminated + the memset/memwipe calls or not, please let me know. + */ + for (i = 0; i < sizeof(buf); ++i) { + if (vmemeq(buf+i, s, strlen(s))) + ++sum; + } + + return sum; +} + +static struct testcase { + const char *name; + /* this spacing satisfies make check-spaces */ + unsigned + (*fill_fn)(void); + unsigned + (*check_fn)(void); +} testcases[] = { + { "nil", fill_a_buffer_nothing, check_a_buffer }, + { "nil-heap", fill_heap_buffer_nothing, check_heap_buffer }, + { "memset", fill_a_buffer_memset, check_a_buffer }, + { "memset-heap", fill_heap_buffer_memset, check_heap_buffer }, + { "memwipe", fill_a_buffer_memwipe, check_a_buffer }, + { "memwipe-heap", fill_heap_buffer_memwipe, check_heap_buffer }, + { NULL, NULL, NULL } +}; + +int +main(int argc, char **argv) +{ + unsigned x, x2; + int i; + int working = 1; + unsigned found[6]; + (void) argc; (void) argv; + + s = "squamous haberdasher gallimaufry"; + + memset(found, 0, sizeof(found)); + + for (i = 0; testcases[i].name; ++i) { + x = testcases[i].fill_fn(); + found[i] = testcases[i].check_fn(); + + x2 = fill_a_buffer_nothing(); + + if (x != x2) { + working = 0; + } + } + + if (!working || !found[0] || !found[1]) { + printf("It appears that this test case may not give you reliable " + "information. Sorry.\n"); + } + + if (!found[2] && !found[3]) { + printf("It appears that memset is good enough on this platform. Good.\n"); + } + + if (found[4] || found[5]) { + printf("ERROR: memwipe does not wipe data!\n"); + return 1; + } else { + printf("OKAY: memwipe seems to work.\n"); + return 0; + } +} + diff --git a/src/test/test-network.sh b/src/test/test-network.sh index be57cafb7f..ccfb5df424 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -13,7 +13,7 @@ do export TOR_DIR="$2" shift ;; - --flavo?r|--network-flavo?r) + --flavor|--flavour|--network-flavor|--network-flavour) export NETWORK_FLAVOUR="$2" shift ;; diff --git a/src/test/test.c b/src/test/test.c index 0524a6978f..f30b8ae1af 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1127,6 +1127,7 @@ extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t container_tests[]; +extern struct testcase_t controller_tests[]; extern struct testcase_t controller_event_tests[]; extern struct testcase_t crypto_tests[]; extern struct testcase_t dir_tests[]; @@ -1171,7 +1172,8 @@ struct testgroup_t testgroups[] = { { "circuitmux/", circuitmux_tests }, { "config/", config_tests }, { "container/", container_tests }, - { "control/", controller_event_tests }, + { "control/", controller_tests }, + { "control/event/", controller_event_tests }, { "crypto/", crypto_tests }, { "dir/", dir_tests }, { "dir/md/", microdesc_tests }, diff --git a/src/test/test_address.c b/src/test/test_address.c index 424a6352b0..d13d678f3d 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -130,8 +130,8 @@ test_address_ifaddrs_to_smartlist(void *arg) ipv6_sockaddr = tor_malloc(sizeof(struct sockaddr_in6)); ipv6_sockaddr->sin6_family = AF_INET6; ipv6_sockaddr->sin6_port = 0; - inet_pton(AF_INET6, "2001:db8:8714:3a90::12", - &(ipv6_sockaddr->sin6_addr)); + tor_inet_pton(AF_INET6, "2001:db8:8714:3a90::12", + &(ipv6_sockaddr->sin6_addr)); ifa = tor_malloc(sizeof(struct ifaddrs)); ifa_ipv4 = tor_malloc(sizeof(struct ifaddrs)); @@ -222,7 +222,7 @@ test_address_get_if_addrs_ifaddrs(void *arg) (void)arg; - results = get_interface_addresses_ifaddrs(0); + results = get_interface_addresses_ifaddrs(LOG_ERR); tt_int_op(smartlist_len(results),>=,1); tt_assert(smartlist_contains_localhost_tor_addr(results)); @@ -245,7 +245,7 @@ test_address_get_if_addrs_win32(void *arg) (void)arg; - results = get_interface_addresses_win32(0); + results = get_interface_addresses_win32(LOG_ERR); tt_int_op(smartlist_len(results),>=,1); tt_assert(smartlist_contains_localhost_tor_addr(results)); @@ -452,10 +452,194 @@ test_address_get_if_addrs_ioctl(void *arg) #endif +#define FAKE_SOCKET_FD (42) + +static tor_socket_t +fake_open_socket(int domain, int type, int protocol) +{ + (void)domain; + (void)type; + (void)protocol; + + return FAKE_SOCKET_FD; +} + +static int last_connected_socket_fd = 0; + +static int connect_retval = 0; + +static tor_socket_t +pretend_to_connect(tor_socket_t socket, const struct sockaddr *address, + socklen_t address_len) +{ + (void)address; + (void)address_len; + + last_connected_socket_fd = socket; + + return connect_retval; +} + +static struct sockaddr *mock_addr = NULL; + +static int +fake_getsockname(tor_socket_t socket, struct sockaddr *address, + socklen_t *address_len) +{ + socklen_t bytes_to_copy = 0; + (void) socket; + + if (!mock_addr) + return -1; + + if (mock_addr->sa_family == AF_INET) { + bytes_to_copy = sizeof(struct sockaddr_in); + } else if (mock_addr->sa_family == AF_INET6) { + bytes_to_copy = sizeof(struct sockaddr_in6); + } else { + return -1; + } + + if (*address_len < bytes_to_copy) { + return -1; + } + + memcpy(address,mock_addr,bytes_to_copy); + *address_len = bytes_to_copy; + + return 0; +} + +static void +test_address_udp_socket_trick_whitebox(void *arg) +{ + int hack_retval; + tor_addr_t *addr_from_hack = tor_malloc_zero(sizeof(tor_addr_t)); + struct sockaddr_in6 *mock_addr6; + struct sockaddr_in6 *ipv6_to_check = + tor_malloc_zero(sizeof(struct sockaddr_in6)); + + (void)arg; + + MOCK(tor_open_socket,fake_open_socket); + MOCK(tor_connect_socket,pretend_to_connect); + MOCK(tor_getsockname,fake_getsockname); + + mock_addr = tor_malloc_zero(sizeof(struct sockaddr_storage)); + sockaddr_in_from_string("23.32.246.118",(struct sockaddr_in *)mock_addr); + + hack_retval = + get_interface_address6_via_udp_socket_hack(LOG_DEBUG, + AF_INET, addr_from_hack); + + tt_int_op(hack_retval,==,0); + tt_assert(tor_addr_eq_ipv4h(addr_from_hack, 0x1720f676)); + + /* Now, lets do an IPv6 case. */ + memset(mock_addr,0,sizeof(struct sockaddr_storage)); + + mock_addr6 = (struct sockaddr_in6 *)mock_addr; + mock_addr6->sin6_family = AF_INET6; + mock_addr6->sin6_port = 0; + tor_inet_pton(AF_INET6,"2001:cdba::3257:9652",&(mock_addr6->sin6_addr)); + + hack_retval = + get_interface_address6_via_udp_socket_hack(LOG_DEBUG, + AF_INET6, addr_from_hack); + + tt_int_op(hack_retval,==,0); + + tor_addr_to_sockaddr(addr_from_hack,0,(struct sockaddr *)ipv6_to_check, + sizeof(struct sockaddr_in6)); + + tt_assert(sockaddr_in6_are_equal(mock_addr6,ipv6_to_check)); + + UNMOCK(tor_open_socket); + UNMOCK(tor_connect_socket); + UNMOCK(tor_getsockname); + + done: + tor_free(ipv6_to_check); + tor_free(mock_addr); + tor_free(addr_from_hack); + return; +} + +static void +test_address_udp_socket_trick_blackbox(void *arg) +{ + /* We want get_interface_address6_via_udp_socket_hack() to yield + * the same valid address that get_interface_address6() returns. + * If the latter is unable to find a valid address, we want + * _hack() to fail and return-1. + * + * Furthermore, we want _hack() never to crash, even if + * get_interface_addresses_raw() is returning NULL. + */ + + tor_addr_t addr4; + tor_addr_t addr4_to_check; + tor_addr_t addr6; + tor_addr_t addr6_to_check; + int retval, retval_reference; + + (void)arg; + +#if 0 + retval_reference = get_interface_address6(LOG_DEBUG,AF_INET,&addr4); + retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG, + AF_INET, + &addr4_to_check); + + tt_int_op(retval,==,retval_reference); + tt_assert( (retval == -1 && retval_reference == -1) || + (tor_addr_compare(&addr4,&addr4_to_check,CMP_EXACT) == 0) ); + + retval_reference = get_interface_address6(LOG_DEBUG,AF_INET6,&addr6); + retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG, + AF_INET6, + &addr6_to_check); + + tt_int_op(retval,==,retval_reference); + tt_assert( (retval == -1 && retval_reference == -1) || + (tor_addr_compare(&addr6,&addr6_to_check,CMP_EXACT) == 0) ); + +#else + /* Both of the blackbox test cases fail horribly if: + * * The host has no external addreses. + * * There are multiple interfaces with either AF_INET or AF_INET6. + * * The last address isn't the one associated with the default route. + * + * The tests SHOULD be re-enabled when #12377 is fixed correctly, but till + * then this fails a lot, in situations we expect failures due to knowing + * about the code being broken. + */ + + (void)addr4_to_check; + (void)addr6_to_check; + (void)addr6; + (void) retval_reference; +#endif + + /* When family is neither AF_INET nor AF_INET6, we want _hack to + * fail and return -1. + */ + + retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG, + AF_INET+AF_INET6,&addr4); + + tt_assert(retval == -1); + + done: + return; +} + #define ADDRESS_TEST(name, flags) \ { #name, test_address_ ## name, flags, NULL, NULL } struct testcase_t address_tests[] = { + ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK), + ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK), #ifdef HAVE_IFADDRS_TO_SMARTLIST ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK), ADDRESS_TEST(ifaddrs_to_smartlist, 0), diff --git a/src/test/test_bt.sh.in b/src/test/test_bt.sh.in new file mode 100644 index 0000000000..ca8be965d4 --- /dev/null +++ b/src/test/test_bt.sh.in @@ -0,0 +1,9 @@ +#!@SHELL@ +# Test backtrace functionality. + +exitcode=0 + +@builddir@/src/test/test-bt-cl assert | @PYTHON@ @abs_top_srcdir@/src/test/bt_test.py || exitcode=1 +@builddir@/src/test/test-bt-cl crash | @PYTHON@ @abs_top_srcdir@/src/test/bt_test.py || exitcode=1 + +exit ${exitcode} diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c index 0fa0cd5c0a..01c621eb0e 100644 --- a/src/test/test_bt_cl.c +++ b/src/test/test_bt_cl.c @@ -5,6 +5,8 @@ #include <stdio.h> #include <stdlib.h> +/* To prevent 'assert' from going away. */ +#undef TOR_COVERAGE #include "or.h" #include "util.h" #include "backtrace.h" diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 0ede8081d8..22ee6aa8e3 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -698,6 +698,59 @@ test_buffers_zlib_fin_at_chunk_end(void *arg) tor_free(msg); } +const uint8_t *tls_read_ptr; +int n_remaining; +int next_reply_val[16]; + +static int +mock_tls_read(tor_tls_t *tls, char *cp, size_t len) +{ + (void)tls; + int rv = next_reply_val[0]; + if (rv > 0) { + int max = rv > (int)len ? (int)len : rv; + if (max > n_remaining) + max = n_remaining; + memcpy(cp, tls_read_ptr, max); + rv = max; + n_remaining -= max; + tls_read_ptr += max; + } + + memmove(next_reply_val, next_reply_val + 1, 15*sizeof(int)); + return rv; +} + +static void +test_buffers_tls_read_mocked(void *arg) +{ + uint8_t *mem; + buf_t *buf; + (void)arg; + + mem = tor_malloc(64*1024); + crypto_rand((char*)mem, 64*1024); + tls_read_ptr = mem; + n_remaining = 64*1024; + + MOCK(tor_tls_read, mock_tls_read); + + buf = buf_new(); + + next_reply_val[0] = 1024; + tt_int_op(128, ==, read_to_buf_tls(NULL, 128, buf)); + + next_reply_val[0] = 5000; + next_reply_val[1] = 5000; + tt_int_op(6000, ==, read_to_buf_tls(NULL, 6000, buf)); + + + done: + UNMOCK(tor_tls_read); + tor_free(mem); + buf_free(buf); +} + struct testcase_t buffer_tests[] = { { "basic", test_buffers_basic, TT_FORK, NULL, NULL }, { "copy", test_buffer_copy, TT_FORK, NULL, NULL }, @@ -710,6 +763,8 @@ struct testcase_t buffer_tests[] = { { "zlib_fin_with_nil", test_buffers_zlib_fin_with_nil, TT_FORK, NULL, NULL }, { "zlib_fin_at_chunk_end", test_buffers_zlib_fin_at_chunk_end, TT_FORK, NULL, NULL}, + { "tls_read_mocked", test_buffers_tls_read_mocked, 0, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_cmdline_args.py b/src/test/test_cmdline_args.py deleted file mode 100755 index 57641974db..0000000000 --- a/src/test/test_cmdline_args.py +++ /dev/null @@ -1,311 +0,0 @@ -#!/usr/bin/python - -import binascii -import hashlib -import os -import re -import shutil -import subprocess -import sys -import tempfile -import unittest - -TOR = "./src/or/tor" -TOP_SRCDIR = "." - -if len(sys.argv) > 1: - TOR = sys.argv[1] - del sys.argv[1] - -if len(sys.argv) > 1: - TOP_SRCDIR = sys.argv[1] - del sys.argv[1] - -class UnexpectedSuccess(Exception): - pass - -class UnexpectedFailure(Exception): - pass - -if sys.version < '3': - def b2s(b): - return b - def s2b(s): - return s - def NamedTemporaryFile(): - return tempfile.NamedTemporaryFile(delete=False) -else: - def b2s(b): - return str(b, 'ascii') - def s2b(s): - return s.encode('ascii') - def NamedTemporaryFile(): - return tempfile.NamedTemporaryFile(mode="w",delete=False,encoding="ascii") - -def contents(fn): - f = open(fn) - try: - return f.read() - finally: - f.close() - -def run_tor(args, failure=False, stdin=None): - kwargs = {} - if stdin != None: - kwargs['stdin'] = subprocess.PIPE - p = subprocess.Popen([TOR] + args, stdout=subprocess.PIPE, **kwargs) - output, _ = p.communicate(input=stdin) - result = p.poll() - if result and not failure: - raise UnexpectedFailure() - elif not result and failure: - raise UnexpectedSuccess() - return b2s(output.replace('\r\n','\n')) - -def spaceify_fp(fp): - for i in range(0, len(fp), 4): - yield fp[i:i+4] - -def lines(s): - out = s.splitlines() - if out and out[-1] == '': - del out[-1] - return out - -def strip_log_junk(line): - m = re.match(r'([^\[]+\[[a-z]*\] *)(.*)', line) - if not m: - return ""+line - return m.group(2).strip() - -def randstring(entropy_bytes): - s = os.urandom(entropy_bytes) - return b2s(binascii.b2a_hex(s)) - -def findLineContaining(lines, s): - for ln in lines: - if s in ln: - return True - return False - -class CmdlineTests(unittest.TestCase): - - def test_version(self): - out = run_tor(["--version"]) - self.assertTrue(out.startswith("Tor version ")) - self.assertEqual(len(lines(out)), 1) - - def test_quiet(self): - out = run_tor(["--quiet", "--quumblebluffin", "1"], failure=True) - self.assertEqual(out, "") - - def test_help(self): - out = run_tor(["--help"], failure=False) - out2 = run_tor(["-h"], failure=False) - self.assertTrue(out.startswith("Copyright (c) 2001")) - self.assertTrue(out.endswith( - "tor -f <torrc> [args]\n" - "See man page for options, or https://www.torproject.org/ for documentation.\n")) - self.assertTrue(out == out2) - - def test_hush(self): - torrc = NamedTemporaryFile() - torrc.close() - try: - out = run_tor(["--hush", "-f", torrc.name, - "--quumblebluffin", "1"], failure=True) - finally: - os.unlink(torrc.name) - self.assertEqual(len(lines(out)), 2) - ln = [ strip_log_junk(l) for l in lines(out) ] - self.assertEqual(ln[0], "Failed to parse/validate config: Unknown option 'quumblebluffin'. Failing.") - self.assertEqual(ln[1], "Reading config failed--see warnings above.") - - def test_missing_argument(self): - out = run_tor(["--hush", "--hash-password"], failure=True) - self.assertEqual(len(lines(out)), 2) - ln = [ strip_log_junk(l) for l in lines(out) ] - self.assertEqual(ln[0], "Command-line option '--hash-password' with no value. Failing.") - - def test_hash_password(self): - out = run_tor(["--hash-password", "woodwose"]) - result = lines(out)[-1] - self.assertEqual(result[:3], "16:") - self.assertEqual(len(result), 61) - r = binascii.a2b_hex(result[3:]) - self.assertEqual(len(r), 29) - - salt, how, hashed = r[:8], r[8], r[9:] - self.assertEqual(len(hashed), 20) - if type(how) == type("A"): - how = ord(how) - - count = (16 + (how & 15)) << ((how >> 4) + 6) - stuff = salt + s2b("woodwose") - repetitions = count // len(stuff) + 1 - inp = stuff * repetitions - inp = inp[:count] - - self.assertEqual(hashlib.sha1(inp).digest(), hashed) - - def test_digests(self): - main_c = os.path.join(TOP_SRCDIR, "src", "or", "main.c") - - if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime: - self.skipTest(TOR+" not up to date") - out = run_tor(["--digests"]) - main_line = [ l for l in lines(out) if l.endswith("/main.c") or l.endswith(" main.c") ] - digest, name = main_line[0].split() - f = open(main_c, 'rb') - actual = hashlib.sha1(f.read()).hexdigest() - f.close() - self.assertEqual(digest, actual) - - def test_dump_options(self): - default_torrc = NamedTemporaryFile() - torrc = NamedTemporaryFile() - torrc.write("SocksPort 9999") - torrc.close() - default_torrc.write("SafeLogging 0") - default_torrc.close() - out_sh = out_nb = out_fl = None - opts = [ "-f", torrc.name, - "--defaults-torrc", default_torrc.name ] - try: - out_sh = run_tor(["--dump-config", "short"]+opts) - out_nb = run_tor(["--dump-config", "non-builtin"]+opts) - out_fl = run_tor(["--dump-config", "full"]+opts) - out_nr = run_tor(["--dump-config", "bliznert"]+opts, - failure=True) - - out_verif = run_tor(["--verify-config"]+opts) - finally: - os.unlink(torrc.name) - os.unlink(default_torrc.name) - - self.assertEqual(len(lines(out_sh)), 2) - self.assertTrue(lines(out_sh)[0].startswith("DataDirectory ")) - self.assertEqual(lines(out_sh)[1:], - [ "SocksPort 9999" ]) - - self.assertEqual(len(lines(out_nb)), 2) - self.assertEqual(lines(out_nb), - [ "SafeLogging 0", - "SocksPort 9999" ]) - - out_fl = lines(out_fl) - self.assertTrue(len(out_fl) > 100) - self.assertTrue("SocksPort 9999" in out_fl) - self.assertTrue("SafeLogging 0" in out_fl) - self.assertTrue("ClientOnly 0" in out_fl) - - self.assertTrue(out_verif.endswith("Configuration was valid\n")) - - def test_list_fingerprint(self): - tmpdir = tempfile.mkdtemp(prefix='ttca_') - torrc = NamedTemporaryFile() - torrc.write("ORPort 9999\n") - torrc.write("DataDirectory %s\n"%tmpdir) - torrc.write("Nickname tippi") - torrc.close() - opts = ["-f", torrc.name] - try: - out = run_tor(["--list-fingerprint"]+opts) - fp = contents(os.path.join(tmpdir, "fingerprint")) - finally: - os.unlink(torrc.name) - shutil.rmtree(tmpdir) - - out = lines(out) - lastlog = strip_log_junk(out[-2]) - lastline = out[-1] - fp = fp.strip() - nn_fp = fp.split()[0] - space_fp = " ".join(spaceify_fp(fp.split()[1])) - self.assertEqual(lastlog, - "Your Tor server's identity key fingerprint is '%s'"%fp) - self.assertEqual(lastline, "tippi %s"%space_fp) - self.assertEqual(nn_fp, "tippi") - - def test_list_options(self): - out = lines(run_tor(["--list-torrc-options"])) - self.assertTrue(len(out)>100) - self.assertTrue(out[0] <= 'AccountingMax') - self.assertTrue("UseBridges" in out) - self.assertTrue("SocksPort" in out) - - def test_cmdline_args(self): - default_torrc = NamedTemporaryFile() - torrc = NamedTemporaryFile() - contents = ("SocksPort 9999\n" - "SocksPort 9998\n" - "ORPort 9000\n" - "ORPort 9001\n" - "Nickname eleventeen\n" - "ControlPort 9500\n") - torrc.write(contents) - default_torrc.write("") - default_torrc.close() - torrc.close() - out_sh = out_nb = out_fl = None - - opts_stdin = [ "-f", "-", - "--defaults-torrc", default_torrc.name, - "--dump-config", "short" ] - opts = [ "-f", torrc.name, - "--defaults-torrc", default_torrc.name, - "--dump-config", "short" ] - try: - out_0 = run_tor(opts_stdin,stdin=contents) - out_1 = run_tor(opts) - out_2 = run_tor(opts+["+ORPort", "9003", - "SocksPort", "9090", - "/ControlPort", - "/TransPort", - "+ExtORPort", "9005"]) - finally: - os.unlink(torrc.name) - os.unlink(default_torrc.name) - - out_0 = [ l for l in lines(out_0) if not l.startswith("DataDir") ] - out_1 = [ l for l in lines(out_1) if not l.startswith("DataDir") ] - out_2 = [ l for l in lines(out_2) if not l.startswith("DataDir") ] - - self.assertEqual(out_0, - ["ControlPort 9500", - "Nickname eleventeen", - "ORPort 9000", - "ORPort 9001", - "SocksPort 9999", - "SocksPort 9998"]) - - self.assertEqual(out_1, - ["ControlPort 9500", - "Nickname eleventeen", - "ORPort 9000", - "ORPort 9001", - "SocksPort 9999", - "SocksPort 9998"]) - - self.assertEqual(out_2, - ["ExtORPort 9005", - "Nickname eleventeen", - "ORPort 9000", - "ORPort 9001", - "ORPort 9003", - "SocksPort 9090"]) - - def test_missing_torrc(self): - fname = "nonexistent_file_"+randstring(8) - out = run_tor(["-f", fname, "--verify-config"], failure=True) - ln = [ strip_log_junk(l) for l in lines(out) ] - self.assertTrue("Unable to open configuration file" in ln[-2]) - self.assertTrue("Reading config failed" in ln[-1]) - - out = run_tor(["-f", fname, "--verify-config", "--ignore-missing-torrc"]) - ln = [ strip_log_junk(l) for l in lines(out) ] - self.assertTrue(findLineContaining(ln, ", using reasonable defaults")) - self.assertTrue("Configuration was valid" in ln[-1]) - -if __name__ == '__main__': - unittest.main() diff --git a/src/test/test_config.c b/src/test/test_config.c index 0444062722..28e9fa0f32 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -17,6 +17,7 @@ #include "address.h" #include "entrynodes.h" #include "transports.h" +#include "routerlist.h" static void test_config_addressmap(void *arg) @@ -1036,7 +1037,7 @@ static int n_get_interface_address6_failure = 0; /** * This mock function is meant to replace get_interface_addres6(). - * It will pretent to fail by return -1. + * It will pretend to fail by return -1. * <b>n_get_interface_address6_failure</b> is incremented by one * every time this function is called and <b>last_address6_family</b> * is assigned the value of <b>family</b> argument. @@ -1405,7 +1406,7 @@ test_config_resolve_my_address(void *arg) /* CASE 12: * Suppose the following happens: - * 1. options->Address is NULL AND options->DirAuthorities is 1. + * 1. options->Address is NULL AND options->DirAuthorities is non-NULL * 2. tor_gethostname() succeeds in getting hostname of a machine ... * 3. ... which is successfully parsed by tor_inet_aton() ... * 4. into IPv4 address that tor_addr_is_inernal() considers to be @@ -1443,10 +1444,1776 @@ test_config_resolve_my_address(void *arg) UNMOCK(tor_gethostname); } +static int n_add_default_fallback_dir_servers_known_default = 0; + +/** + * This mock function is meant to replace add_default_fallback_dir_servers(). + * It will parse and add one known default fallback dir server, + * which has a dir_port of 99. + * <b>n_add_default_fallback_dir_servers_known_default</b> is incremented by + * one every time this function is called. + */ +static void +add_default_fallback_dir_servers_known_default(void) +{ + int i; + const char *fallback[] = { + "127.0.0.1:60099 orport=9009 " + "id=0923456789012345678901234567890123456789", + NULL + }; + for (i=0; fallback[i]; i++) { + if (parse_dir_fallback_line(fallback[i], 0)<0) { + log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s", + fallback[i]); + } + } + n_add_default_fallback_dir_servers_known_default++; +} + +static void +test_config_adding_dir_servers(void *arg) +{ + (void)arg; + + /* allocate options */ + or_options_t *options = tor_malloc(sizeof(or_options_t)); + + /* Allocate and populate configuration lines: + * + * Use the same format as the hard-coded directories in + * add_default_trusted_dir_authorities(). + * Zeroing the structure has the same effect as initialising to: + * { NULL, NULL, NULL, CONFIG_LINE_NORMAL, 0}; + */ + config_line_t *test_dir_authority = tor_malloc(sizeof(config_line_t)); + memset(test_dir_authority, 0, sizeof(config_line_t)); + test_dir_authority->key = tor_strdup("DirAuthority"); + test_dir_authority->value = tor_strdup( + "D0 orport=9000 " + "v3ident=0023456789012345678901234567890123456789 " + "127.0.0.1:60090 0123 4567 8901 2345 6789 0123 4567 8901 2345 6789" + ); + + config_line_t *test_alt_bridge_authority = tor_malloc(sizeof(config_line_t)); + memset(test_alt_bridge_authority, 0, sizeof(config_line_t)); + test_alt_bridge_authority->key = tor_strdup("AlternateBridgeAuthority"); + test_alt_bridge_authority->value = tor_strdup( + "B1 orport=9001 bridge " + "127.0.0.1:60091 1123 4567 8901 2345 6789 0123 4567 8901 2345 6789" + ); + + config_line_t *test_alt_dir_authority = tor_malloc(sizeof(config_line_t)); + memset(test_alt_dir_authority, 0, sizeof(config_line_t)); + test_alt_dir_authority->key = tor_strdup("AlternateDirAuthority"); + test_alt_dir_authority->value = tor_strdup( + "A2 orport=9002 " + "v3ident=0223456789012345678901234567890123456789 " + "127.0.0.1:60092 2123 4567 8901 2345 6789 0123 4567 8901 2345 6789" + ); + + /* Use the format specified in the manual page */ + config_line_t *test_fallback_directory = tor_malloc(sizeof(config_line_t)); + memset(test_fallback_directory, 0, sizeof(config_line_t)); + test_fallback_directory->key = tor_strdup("FallbackDir"); + test_fallback_directory->value = tor_strdup( + "127.0.0.1:60093 orport=9003 id=0323456789012345678901234567890123456789" + ); + + /* We need to know if add_default_fallback_dir_servers is called, + * so we use a version of add_default_fallback_dir_servers that adds + * one known default fallback directory. + * There doesn't appear to be any need to test it unmocked. */ + MOCK(add_default_fallback_dir_servers, + add_default_fallback_dir_servers_known_default); + + /* There are 16 different cases, covering each combination of set/NULL for: + * DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority & + * FallbackDir. + * But validate_dir_servers() ensures that: + * "You cannot set both DirAuthority and Alternate*Authority." + * This reduces the number of cases to 10. + * + * Let's count these cases using binary, with 1 meaning set & 0 meaning NULL + * So 1001 or case 9 is: + * DirAuthorities set, + * AlternateBridgeAuthority NULL, + * AlternateDirAuthority NULL + * FallbackDir set + * The valid cases are cases 0-9 counting using this method, as every case + * greater than or equal to 10 = 1010 is invalid. + * + * After #15642 - Disable default fallback dirs when any custom dirs set + * + * 1. Outcome: Use Set Directory Authorities + * - No Default Authorities + * - Use AlternateBridgeAuthority, AlternateDirAuthority, and FallbackDir + * if they are set + * Cases expected to yield this outcome: + * 8 & 9 (the 2 valid cases where DirAuthorities is set) + * 6 & 7 (the 2 cases where DirAuthorities is NULL, and + * AlternateBridgeAuthority and AlternateDirAuthority are both set) + * + * 2. Outcome: Use Set Bridge Authority + * - Use Default Non-Bridge Directory Authorities + * - Use FallbackDir if it is set, otherwise use default FallbackDir + * Cases expected to yield this outcome: + * 4 & 5 (the 2 cases where DirAuthorities is NULL, + * AlternateBridgeAuthority is set, and + * AlternateDirAuthority is NULL) + * + * 3. Outcome: Use Set Alternate Directory Authority + * - Use Default Bridge Authorities + * - Use FallbackDir if it is set, otherwise No Default Fallback Directories + * Cases expected to yield this outcome: + * 2 & 3 (the 2 cases where DirAuthorities and AlternateBridgeAuthority + * are both NULL, but AlternateDirAuthority is set) + * + * 4. Outcome: Use Set Custom Fallback Directory + * - Use Default Bridge & Directory Authorities + * Cases expected to yield this outcome: + * 1 (DirAuthorities, AlternateBridgeAuthority and AlternateDirAuthority + * are all NULL, but FallbackDir is set) + * + * 5. Outcome: Use All Defaults + * - Use Default Bridge & Directory Authorities, and + * Default Fallback Directories + * Cases expected to yield this outcome: + * 0 (DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority + * and FallbackDir are all NULL) + * + * Before #15642 but after #13163 - Stop using default authorities when both + * Alternate Dir and Bridge Authority are set + * (#13163 was committed in 0.2.6 as c1dd43d823c7) + * + * The behaviour is different in the following cases + * where FallbackDir is NULL: + * 2, 6, 8 + * + * In these cases, the Default Fallback Directories are applied, even when + * DirAuthorities or AlternateDirAuthority are set. + * + * However, as the list of default fallback directories is currently empty, + * this change doesn't modify any user-visible behaviour. + */ + + /* + * Find out how many default Bridge, Non-Bridge and Fallback Directories + * are hard-coded into this build. + * This code makes some assumptions about the implementation. + * If they are wrong, one or more of cases 0-5 could fail. + */ + int n_default_alt_bridge_authority = 0; + int n_default_alt_dir_authority = 0; + int n_default_fallback_dir = 0; +#define n_default_authorities ((n_default_alt_bridge_authority) \ + + (n_default_alt_dir_authority)) + + /* Pre-Count Number of Authorities of Each Type + * Use 0000: No Directory Authorities or Fallback Directories Set + */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0000 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = NULL; + options->FallbackDir = NULL; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 1); + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + + /* Count Bridge Authorities */ + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if it's a bridge auth */ + n_default_alt_bridge_authority += + ((ds->is_authority && (ds->type & BRIDGE_DIRINFO)) ? + 1 : 0) + ); + /* If we have no default bridge authority, something has gone wrong */ + tt_assert(n_default_alt_bridge_authority >= 1); + + /* Count v3 Authorities */ + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment found counter if it's a v3 auth */ + n_default_alt_dir_authority += + ((ds->is_authority && (ds->type & V3_DIRINFO)) ? + 1 : 0) + ); + /* If we have no default authorities, something has gone really wrong */ + tt_assert(n_default_alt_dir_authority >= 1); + + /* Calculate Fallback Directory Count */ + n_default_fallback_dir = (smartlist_len(fallback_servers) - + n_default_alt_bridge_authority - + n_default_alt_dir_authority); + /* If we have a negative count, something has gone really wrong */ + tt_assert(n_default_fallback_dir >= 0); + } + } + + /* + * 1. Outcome: Use Set Directory Authorities + * - No Default Authorities + * - Use AlternateBridgeAuthority, AlternateDirAuthority, and FallbackDir + * if they are set + * Cases expected to yield this outcome: + * 8 & 9 (the 2 valid cases where DirAuthorities is set) + * 6 & 7 (the 2 cases where DirAuthorities is NULL, and + * AlternateBridgeAuthority and AlternateDirAuthority are both set) + */ + + /* Case 9: 1001 - DirAuthorities Set, AlternateBridgeAuthority Not Set, + AlternateDirAuthority Not Set, FallbackDir Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 1001 */ + options->DirAuthorities = test_dir_authority; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = NULL; + options->FallbackDir = test_fallback_directory; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* D0, (No B1), (No A2) */ + tt_assert(smartlist_len(dir_servers) == 1); + + /* DirAuthority - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 1); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* D0, (No B1), (No A2), Custom Fallback */ + tt_assert(smartlist_len(fallback_servers) == 2); + + /* DirAuthority - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 1); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* Custom FallbackDir - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 1); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + } + } + + /* Case 8: 1000 - DirAuthorities Set, Others Not Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 1000 */ + options->DirAuthorities = test_dir_authority; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = NULL; + options->FallbackDir = NULL; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* D0, (No B1), (No A2) */ + tt_assert(smartlist_len(dir_servers) == 1); + + /* DirAuthority - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 1); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* D0, (No B1), (No A2), (No Fallback) */ + tt_assert(smartlist_len(fallback_servers) == 1); + + /* DirAuthority - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 1); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 0); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + } + } + + /* Case 7: 0111 - DirAuthorities Not Set, Others Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0111 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = test_alt_bridge_authority; + options->AlternateDirAuthority = test_alt_dir_authority; + options->FallbackDir = test_fallback_directory; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), B1, A2 */ + tt_assert(smartlist_len(dir_servers) == 2); + + /* (No DirAuthority) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), B1, A2, Custom Fallback */ + tt_assert(smartlist_len(fallback_servers) == 3); + + /* (No DirAuthority) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + + /* Custom FallbackDir - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 1); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + } + } + + /* Case 6: 0110 - DirAuthorities Not Set, AlternateBridgeAuthority & + AlternateDirAuthority Set, FallbackDir Not Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0110 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = test_alt_bridge_authority; + options->AlternateDirAuthority = test_alt_dir_authority; + options->FallbackDir = NULL; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), B1, A2 */ + tt_assert(smartlist_len(dir_servers) == 2); + + /* (No DirAuthority) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), B1, A2, (No Fallback) */ + tt_assert(smartlist_len(fallback_servers) == 2); + + /* (No DirAuthority) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + + /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 0); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + } + } + + /* + 2. Outcome: Use Set Bridge Authority + - Use Default Non-Bridge Directory Authorities + - Use FallbackDir if it is set, otherwise use default FallbackDir + Cases expected to yield this outcome: + 4 & 5 (the 2 cases where DirAuthorities is NULL, + AlternateBridgeAuthority is set, and + AlternateDirAuthority is NULL) + */ + + /* Case 5: 0101 - DirAuthorities Not Set, AlternateBridgeAuthority Set, + AlternateDirAuthority Not Set, FallbackDir Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0101 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = test_alt_bridge_authority; + options->AlternateDirAuthority = NULL; + options->FallbackDir = test_fallback_directory; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities */ + tt_assert(smartlist_len(dir_servers) == 1 + n_default_alt_dir_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* There's no easy way of checking that we have included all the + * default v3 non-Bridge directory authorities, so let's assume that + * if the total count above is correct, we have the right ones. + */ + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities, + * Custom Fallback */ + tt_assert(smartlist_len(fallback_servers) == + 2 + n_default_alt_dir_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* Custom FallbackDir - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 1); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + + /* There's no easy way of checking that we have included all the + * default v3 non-Bridge directory authorities, so let's assume that + * if the total count above is correct, we have the right ones. + */ + } + } + + /* Case 4: 0100 - DirAuthorities Not Set, AlternateBridgeAuthority Set, + AlternateDirAuthority & FallbackDir Not Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0100 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = test_alt_bridge_authority; + options->AlternateDirAuthority = NULL; + options->FallbackDir = NULL; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 1); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities */ + tt_assert(smartlist_len(dir_servers) == 1 + n_default_alt_dir_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* There's no easy way of checking that we have included all the + * default v3 non-Bridge directory authorities, so let's assume that + * if the total count above is correct, we have the right ones. + */ + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities, + * Default Fallback */ + tt_assert(smartlist_len(fallback_servers) == + 2 + n_default_alt_dir_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* AlternateBridgeAuthority - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 1); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 0); + + /* Default FallbackDir - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 1); + + /* There's no easy way of checking that we have included all the + * default v3 non-Bridge directory authorities, so let's assume that + * if the total count above is correct, we have the right ones. + */ + } + } + + /* + 3. Outcome: Use Set Alternate Directory Authority + - Use Default Bridge Authorities + - Use FallbackDir if it is set, otherwise No Default Fallback Directories + Cases expected to yield this outcome: + 2 & 3 (the 2 cases where DirAuthorities and AlternateBridgeAuthority + are both NULL, but AlternateDirAuthority is set) + */ + + /* Case 3: 0011 - DirAuthorities & AlternateBridgeAuthority Not Set, + AlternateDirAuthority & FallbackDir Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0011 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = test_alt_dir_authority; + options->FallbackDir = test_fallback_directory; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, A2 */ + tt_assert(smartlist_len(dir_servers) == + 1 + n_default_alt_bridge_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + + /* There's no easy way of checking that we have included all the + * default Bridge authorities (except for hard-coding tonga's details), + * so let's assume that if the total count above is correct, + * we have the right ones. + */ + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, A2, + * Custom Fallback Directory, (No Default Fallback Directories) */ + tt_assert(smartlist_len(fallback_servers) == + 2 + n_default_alt_bridge_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + + /* Custom FallbackDir - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 1); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + + /* There's no easy way of checking that we have included all the + * default Bridge authorities (except for hard-coding tonga's details), + * so let's assume that if the total count above is correct, + * we have the right ones. + */ + } + } + + /* Case 2: 0010 - DirAuthorities & AlternateBridgeAuthority Not Set, + AlternateDirAuthority Set, FallbackDir Not Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0010 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = test_alt_dir_authority; + options->FallbackDir = NULL; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, A2, + * No Default or Custom Fallback Directories */ + tt_assert(smartlist_len(dir_servers) == + 1 + n_default_alt_bridge_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + + /* There's no easy way of checking that we have included all the + * default Bridge authorities (except for hard-coding tonga's details), + * so let's assume that if the total count above is correct, + * we have the right ones. + */ + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, A2, + * No Custom or Default Fallback Directories */ + tt_assert(smartlist_len(fallback_servers) == + 1 + n_default_alt_bridge_authority); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* AlternateDirAuthority - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 1); + + /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 0); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + + /* There's no easy way of checking that we have included all the + * default Bridge authorities (except for hard-coding tonga's details), + * so let's assume that if the total count above is correct, + * we have the right ones. + */ + } + } + + /* + 4. Outcome: Use Set Custom Fallback Directory + - Use Default Bridge & Directory Authorities + Cases expected to yield this outcome: + 1 (DirAuthorities, AlternateBridgeAuthority and AlternateDirAuthority + are all NULL, but FallbackDir is set) + */ + + /* Case 1: 0001 - DirAuthorities, AlternateBridgeAuthority + & AlternateDirAuthority Not Set, FallbackDir Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0001 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = NULL; + options->FallbackDir = test_fallback_directory; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must not have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 0); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, + * (No A2), Default v3 Directory Authorities */ + tt_assert(smartlist_len(dir_servers) == n_default_authorities); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* There's no easy way of checking that we have included all the + * default Bridge & V3 Directory authorities, so let's assume that + * if the total count above is correct, we have the right ones. + */ + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, + * (No A2), Default v3 Directory Authorities, + * Custom Fallback Directory, (No Default Fallback Directories) */ + tt_assert(smartlist_len(fallback_servers) == + 1 + n_default_authorities); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* Custom FallbackDir - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 1); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 0); + + /* There's no easy way of checking that we have included all the + * default Bridge & V3 Directory authorities, so let's assume that + * if the total count above is correct, we have the right ones. + */ + } + } + + /* + 5. Outcome: Use All Defaults + - Use Default Bridge & Directory Authorities, Default Fallback Directories + Cases expected to yield this outcome: + 0 (DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority + and FallbackDir are all NULL) + */ + + /* Case 0: 0000 - All Not Set */ + { + /* clear fallback dirs counter */ + n_add_default_fallback_dir_servers_known_default = 0; + + /* clear options*/ + memset(options, 0, sizeof(or_options_t)); + + /* clear any previous dir servers: + consider_adding_dir_servers() should do this anyway */ + clear_dir_servers(); + + /* assign options: 0001 */ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = NULL; + options->FallbackDir = NULL; + + /* parse options - ensure we always update by passing NULL old_options */ + consider_adding_dir_servers(options, NULL); + + /* check outcome */ + + /* we must have added the default fallback dirs */ + tt_assert(n_add_default_fallback_dir_servers_known_default == 1); + + { + /* trusted_dir_servers */ + const smartlist_t *dir_servers = router_get_trusted_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, + * (No A2), Default v3 Directory Authorities */ + tt_assert(smartlist_len(dir_servers) == n_default_authorities); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(dir_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* There's no easy way of checking that we have included all the + * default Bridge & V3 Directory authorities, so let's assume that + * if the total count above is correct, we have the right ones. + */ + } + + { + /* fallback_dir_servers */ + const smartlist_t *fallback_servers = router_get_fallback_dir_servers(); + /* (No D0), (No B1), Default Bridge Authorities, + * (No A2), Default v3 Directory Authorities, + * (No Custom Fallback Directory), Default Fallback Directories */ + tt_assert(smartlist_len(fallback_servers) == + n_default_authorities + n_default_fallback_dir); + + /* (No DirAuthorities) - D0 - dir_port: 60090 */ + int found_D0 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_D0 += + (ds->dir_port == 60090 ? + 1 : 0) + ); + tt_assert(found_D0 == 0); + + /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */ + int found_B1 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_B1 += + (ds->dir_port == 60091 ? + 1 : 0) + ); + tt_assert(found_B1 == 0); + + /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */ + int found_A2 = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_A2 += + (ds->dir_port == 60092 ? + 1 : 0) + ); + tt_assert(found_A2 == 0); + + /* Custom FallbackDir - No Nickname - dir_port: 60093 */ + int found_non_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_non_default_fallback += + (ds->dir_port == 60093 ? + 1 : 0) + ); + tt_assert(found_non_default_fallback == 0); + + /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */ + int found_default_fallback = 0; + SMARTLIST_FOREACH(fallback_servers, + dir_server_t *, + ds, + /* increment the found counter if dir_port matches */ + found_default_fallback += + (ds->dir_port == 60099 ? + 1 : 0) + ); + tt_assert(found_default_fallback == 1); + + /* There's no easy way of checking that we have included all the + * default Bridge & V3 Directory authorities, and the default + * Fallback Directories, so let's assume that if the total count + * above is correct, we have the right ones. + */ + } + } + + done: + clear_dir_servers(); + + tor_free(test_dir_authority->key); + tor_free(test_dir_authority->value); + tor_free(test_dir_authority); + + tor_free(test_alt_dir_authority->key); + tor_free(test_alt_dir_authority->value); + tor_free(test_alt_dir_authority); + + tor_free(test_alt_bridge_authority->key); + tor_free(test_alt_bridge_authority->value); + tor_free(test_alt_bridge_authority); + + tor_free(test_fallback_directory->key); + tor_free(test_fallback_directory->value); + tor_free(test_fallback_directory); + + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = NULL; + options->FallbackDir = NULL; + or_options_free(options); + + UNMOCK(add_default_fallback_dir_servers); +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } struct testcase_t config_tests[] = { + CONFIG_TEST(adding_dir_servers, TT_FORK), CONFIG_TEST(resolve_my_address, TT_FORK), CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), diff --git a/src/test/test_controller.c b/src/test/test_controller.c new file mode 100644 index 0000000000..b40825bb5d --- /dev/null +++ b/src/test/test_controller.c @@ -0,0 +1,163 @@ +/* Copyright (c) 2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CONTROL_PRIVATE +#include "or.h" +#include "control.h" +#include "rendservice.h" +#include "test.h" + +static void +test_add_onion_helper_keyarg(void *arg) +{ + crypto_pk_t *pk = NULL; + crypto_pk_t *pk2 = NULL; + const char *key_new_alg = NULL; + char *key_new_blob = NULL; + char *err_msg = NULL; + char *encoded = NULL; + char *arg_str = NULL; + + (void) arg; + + /* Test explicit RSA1024 key generation. */ + pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, + &err_msg); + tt_assert(pk); + tt_str_op(key_new_alg, OP_EQ, "RSA1024"); + tt_assert(key_new_blob); + tt_assert(!err_msg); + + /* Test "BEST" key generation (Assumes BEST = RSA1024). */ + crypto_pk_free(pk); + tor_free(key_new_blob); + pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, + &err_msg); + tt_assert(pk); + tt_str_op(key_new_alg, OP_EQ, "RSA1024"); + tt_assert(key_new_blob); + tt_assert(!err_msg); + + /* Test discarding the private key. */ + crypto_pk_free(pk); + tor_free(key_new_blob); + pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, + &err_msg); + tt_assert(pk); + tt_assert(!key_new_alg); + tt_assert(!key_new_blob); + tt_assert(!err_msg); + + /* Test generating a invalid key type. */ + crypto_pk_free(pk); + pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, + &err_msg); + tt_assert(!pk); + tt_assert(!key_new_alg); + tt_assert(!key_new_blob); + tt_assert(err_msg); + + /* Test loading a RSA1024 key. */ + tor_free(err_msg); + pk = pk_generate(0); + tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded)); + tor_asprintf(&arg_str, "RSA1024:%s", encoded); + pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &err_msg); + tt_assert(pk2); + tt_assert(!key_new_alg); + tt_assert(!key_new_blob); + tt_assert(!err_msg); + tt_assert(crypto_pk_cmp_keys(pk, pk2) == 0); + + /* Test loading a invalid key type. */ + tor_free(arg_str); + crypto_pk_free(pk); pk = NULL; + tor_asprintf(&arg_str, "RSA512:%s", encoded); + pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &err_msg); + tt_assert(!pk); + tt_assert(!key_new_alg); + tt_assert(!key_new_blob); + tt_assert(err_msg); + + /* Test loading a invalid key. */ + tor_free(arg_str); + crypto_pk_free(pk); pk = NULL; + tor_free(err_msg); + encoded[strlen(encoded)/2] = '\0'; + tor_asprintf(&arg_str, "RSA1024:%s", encoded); + pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &err_msg); + tt_assert(!pk); + tt_assert(!key_new_alg); + tt_assert(!key_new_blob); + tt_assert(err_msg); + + done: + crypto_pk_free(pk); + crypto_pk_free(pk2); + tor_free(key_new_blob); + tor_free(err_msg); + tor_free(encoded); + tor_free(arg_str); +} + +static void +test_rend_service_parse_port_config(void *arg) +{ + const char *sep = ","; + rend_service_port_config_t *cfg = NULL; + char *err_msg = NULL; + + (void)arg; + + /* Test "VIRTPORT" only. */ + cfg = rend_service_parse_port_config("80", sep, &err_msg); + tt_assert(cfg); + tt_assert(!err_msg); + + /* Test "VIRTPORT,TARGET" (Target is port). */ + rend_service_port_config_free(cfg); + cfg = rend_service_parse_port_config("80,8080", sep, &err_msg); + tt_assert(cfg); + tt_assert(!err_msg); + + /* Test "VIRTPORT,TARGET" (Target is IPv4:port). */ + rend_service_port_config_free(cfg); + cfg = rend_service_parse_port_config("80,192.0.2.1:8080", sep, &err_msg); + tt_assert(cfg); + tt_assert(!err_msg); + + /* Test "VIRTPORT,TARGET" (Target is IPv6:port). */ + rend_service_port_config_free(cfg); + cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg); + tt_assert(cfg); + tt_assert(!err_msg); + + /* XXX: Someone should add tests for AF_UNIX targets if supported. */ + + /* Test empty config. */ + rend_service_port_config_free(cfg); + cfg = rend_service_parse_port_config("", sep, &err_msg); + tt_assert(!cfg); + tt_assert(err_msg); + + /* Test invalid port. */ + tor_free(err_msg); + cfg = rend_service_parse_port_config("90001", sep, &err_msg); + tt_assert(!cfg); + tt_assert(err_msg); + + done: + rend_service_port_config_free(cfg); + tor_free(err_msg); +} + +struct testcase_t controller_tests[] = { + { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL }, + { "rend_service_parse_port_config", test_rend_service_parse_port_config, 0, + NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index e36314da45..bd91aecd94 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -293,6 +293,104 @@ test_cntev_format_cell_stats(void *arg) tor_free(n_chan); } +static void +test_cntev_event_mask(void *arg) +{ + unsigned int test_event, selected_event; + (void)arg; + + /* Check that nothing is interesting when no events are set */ + control_testing_set_global_event_mask(EVENT_MASK_NONE_); + + /* Check that nothing is interesting between EVENT_MIN_ and EVENT_MAX_ */ + for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++) + tt_assert(!control_event_is_interesting(test_event)); + + /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_ + * This will break if control_event_is_interesting() checks its arguments */ + for (test_event = 0; test_event < EVENT_MIN_; test_event++) + tt_assert(!control_event_is_interesting(test_event)); + for (test_event = EVENT_MAX_ + 1; + test_event < EVENT_CAPACITY_; + test_event++) + tt_assert(!control_event_is_interesting(test_event)); + + /* Check that all valid events are interesting when all events are set */ + control_testing_set_global_event_mask(EVENT_MASK_ALL_); + + /* Check that everything is interesting between EVENT_MIN_ and EVENT_MAX_ */ + for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++) + tt_assert(control_event_is_interesting(test_event)); + + /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_ + * This will break if control_event_is_interesting() checks its arguments */ + for (test_event = 0; test_event < EVENT_MIN_; test_event++) + tt_assert(!control_event_is_interesting(test_event)); + for (test_event = EVENT_MAX_ + 1; + test_event < EVENT_CAPACITY_; + test_event++) + tt_assert(!control_event_is_interesting(test_event)); + + /* Check that only that event is interesting when a single event is set */ + for (selected_event = EVENT_MIN_; + selected_event <= EVENT_MAX_; + selected_event++) { + control_testing_set_global_event_mask(EVENT_MASK_(selected_event)); + + /* Check that only this event is interesting + * between EVENT_MIN_ and EVENT_MAX_ */ + for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++) { + if (test_event == selected_event) { + tt_assert(control_event_is_interesting(test_event)); + } else { + tt_assert(!control_event_is_interesting(test_event)); + } + } + + /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_ + * This will break if control_event_is_interesting checks its arguments */ + for (test_event = 0; test_event < EVENT_MIN_; test_event++) + tt_assert(!control_event_is_interesting(test_event)); + for (test_event = EVENT_MAX_ + 1; + test_event < EVENT_CAPACITY_; + test_event++) + tt_assert(!control_event_is_interesting(test_event)); + } + + /* Check that only that event is not-interesting + * when a single event is un-set */ + for (selected_event = EVENT_MIN_; + selected_event <= EVENT_MAX_; + selected_event++) { + control_testing_set_global_event_mask( + EVENT_MASK_ALL_ + & ~(EVENT_MASK_(selected_event)) + ); + + /* Check that only this event is not-interesting + * between EVENT_MIN_ and EVENT_MAX_ */ + for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++) { + if (test_event == selected_event) { + tt_assert(!control_event_is_interesting(test_event)); + } else { + tt_assert(control_event_is_interesting(test_event)); + } + } + + /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_ + * This will break if control_event_is_interesting checks its arguments */ + for (test_event = 0; test_event < EVENT_MIN_; test_event++) + tt_assert(!control_event_is_interesting(test_event)); + for (test_event = EVENT_MAX_ + 1; + test_event < EVENT_CAPACITY_; + test_event++) + tt_assert(!control_event_is_interesting(test_event)); + } + + done: + ; +} + #define TEST(name, flags) \ { #name, test_cntev_ ## name, flags, 0, NULL } @@ -302,6 +400,7 @@ struct testcase_t controller_event_tests[] = { TEST(sum_up_cell_stats, 0), TEST(append_cell_stats, 0), TEST(format_cell_stats, 0), + TEST(event_mask, 0), END_OF_TESTCASES }; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index e9fb8bf084..7944864bd7 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -14,6 +14,8 @@ #include "crypto_ed25519.h" #include "ed25519_vectors.inc" +#include <openssl/evp.h> + extern const char AUTHORITY_SIGNKEY_3[]; extern const char AUTHORITY_SIGNKEY_A_DIGEST[]; extern const char AUTHORITY_SIGNKEY_A_DIGEST256[]; @@ -72,7 +74,7 @@ test_crypto_rng(void *arg) /* Try out RNG. */ (void)arg; - tt_assert(! crypto_seed_rng(0)); + tt_assert(! crypto_seed_rng()); crypto_rand(data1, 100); crypto_rand(data2, 100); tt_mem_op(data1,OP_NE, data2,100); @@ -105,6 +107,30 @@ test_crypto_rng(void *arg) ; } +static void +test_crypto_rng_range(void *arg) +{ + int got_smallest = 0, got_largest = 0; + int i; + + (void)arg; + for (i = 0; i < 1000; ++i) { + int x = crypto_rand_int_range(5,9); + tt_int_op(x, OP_GE, 5); + tt_int_op(x, OP_LT, 9); + if (x == 5) + got_smallest = 1; + if (x == 8) + got_largest = 1; + } + + /* These fail with probability 1/10^603. */ + tt_assert(got_smallest); + tt_assert(got_largest); + done: + ; +} + /** Run unit tests for our AES functionality */ static void test_crypto_aes(void *arg) @@ -571,6 +597,42 @@ test_crypto_pk_fingerprints(void *arg) tor_free(mem_op_hex_tmp); } +static void +test_crypto_pk_base64(void *arg) +{ + crypto_pk_t *pk1 = NULL; + crypto_pk_t *pk2 = NULL; + char *encoded = NULL; + + (void)arg; + + /* Test Base64 encoding a key. */ + pk1 = pk_generate(0); + tt_assert(pk1); + tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded)); + tt_assert(encoded); + + /* Test decoding a valid key. */ + pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)); + tt_assert(pk2); + tt_assert(crypto_pk_cmp_keys(pk1,pk2) == 0); + crypto_pk_free(pk2); + + /* Test decoding a invalid key (not Base64). */ + static const char *invalid_b64 = "The key is in another castle!"; + pk2 = crypto_pk_base64_decode(invalid_b64, strlen(invalid_b64)); + tt_assert(!pk2); + + /* Test decoding a truncated Base64 blob. */ + pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)/2); + tt_assert(!pk2); + + done: + crypto_pk_free(pk1); + crypto_pk_free(pk2); + tor_free(encoded); +} + /** Sanity check for crypto pk digests */ static void test_crypto_digests(void *arg) @@ -601,6 +663,22 @@ test_crypto_digests(void *arg) crypto_pk_free(k); } +/** Encode src into dest with OpenSSL's EVP Encode interface, returning the + * length of the encoded data in bytes. + */ +static int +base64_encode_evp(char *dest, char *src, size_t srclen) +{ + const unsigned char *s = (unsigned char*)src; + EVP_ENCODE_CTX ctx; + int len, ret; + + EVP_EncodeInit(&ctx); + EVP_EncodeUpdate(&ctx, (unsigned char *)dest, &len, s, (int)srclen); + EVP_EncodeFinal(&ctx, (unsigned char *)(dest + len), &ret); + return ret+ len; +} + /** Run unit tests for misc crypto formatting functionality (base64, base32, * fingerprints, etc) */ static void @@ -618,7 +696,7 @@ test_crypto_formats(void *arg) /* Base64 tests */ memset(data1, 6, 1024); for (idx = 0; idx < 10; ++idx) { - i = base64_encode(data2, 1024, data1, idx); + i = base64_encode(data2, 1024, data1, idx, 0); tt_int_op(i, OP_GE, 0); j = base64_decode(data3, 1024, data2, i); tt_int_op(j,OP_EQ, idx); @@ -628,7 +706,7 @@ test_crypto_formats(void *arg) strlcpy(data1, "Test string that contains 35 chars.", 1024); strlcat(data1, " 2nd string that contains 35 chars.", 1024); - i = base64_encode(data2, 1024, data1, 71); + i = base64_encode(data2, 1024, data1, 71, 0); tt_int_op(i, OP_GE, 0); j = base64_decode(data3, 1024, data2, i); tt_int_op(j,OP_EQ, 71); @@ -647,6 +725,20 @@ test_crypto_formats(void *arg) tt_assert(digest_from_base64(data3, "###") < 0); + for (i = 0; i < 256; i++) { + /* Test the multiline format Base64 encoder with 0 .. 256 bytes of + * output against OpenSSL. + */ + const size_t enclen = base64_encode_size(i, BASE64_ENCODE_MULTILINE); + data1[i] = i; + j = base64_encode(data2, 1024, data1, i, BASE64_ENCODE_MULTILINE); + tt_int_op(j, OP_EQ, enclen); + j = base64_encode_evp(data3, data1, i); + tt_int_op(j, OP_EQ, enclen); + tt_mem_op(data2, OP_EQ, data3, enclen); + tt_int_op(j, OP_EQ, strlen(data2)); + } + /* Encoding SHA256 */ crypto_rand(data2, DIGEST256_LEN); memset(data2, 100, 1024); @@ -1605,11 +1697,13 @@ test_crypto_siphash(void *arg) struct testcase_t crypto_tests[] = { CRYPTO_LEGACY(formats), CRYPTO_LEGACY(rng), + { "rng_range", test_crypto_rng_range, 0, NULL, NULL }, { "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" }, { "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" }, CRYPTO_LEGACY(sha), CRYPTO_LEGACY(pk), { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL }, + { "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL }, CRYPTO_LEGACY(digests), CRYPTO_LEGACY(dh), { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup, diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 17cb9d9329..0011d3698a 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -162,9 +162,6 @@ populate_live_entry_guards_test_helper(int num_needed) False, all guards should have made_contact enabled. */ tt_int_op(entry->made_contact, OP_EQ, 1); - /* Since we don't have a routerstatus, all of the entry guards are - not directory servers. */ - tt_int_op(entry->is_dir_cache, OP_EQ, 0); } SMARTLIST_FOREACH_END(entry); /* First, try to get some fast guards. This should fail. */ diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 67ef646aab..dd49cd3e35 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -13,6 +13,7 @@ #include "test.h" #include "control.h" #include "config.h" +#include "rendcommon.h" #include "routerset.h" #include "circuitbuild.h" #include "test_helpers.h" @@ -28,6 +29,68 @@ #define STR_HSDIR_NONE_EXIST_LONGNAME \ "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" +/* DuckDuckGo descriptor as an example. */ +static const char *hs_desc_content = "\ +rendezvous-service-descriptor g5ojobzupf275beh5ra72uyhb3dkpxwg\r\n\ +version 2\r\n\ +permanent-key\r\n\ +-----BEGIN RSA PUBLIC KEY-----\r\n\ +MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE\r\n\ +aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg\r\n\ +I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE=\r\n\ +-----END RSA PUBLIC KEY-----\r\n\ +secret-id-part anmjoxxwiupreyajjt5yasimfmwcnxlf\r\n\ +publication-time 2015-03-11 19:00:00\r\n\ +protocol-versions 2,3\r\n\ +introduction-points\r\n\ +-----BEGIN MESSAGE-----\r\n\ +aW50cm9kdWN0aW9uLXBvaW50IDd1bnd4cmg2dG5kNGh6eWt1Z3EzaGZzdHduc2ll\r\n\ +cmhyCmlwLWFkZHJlc3MgMTg4LjEzOC4xMjEuMTE4Cm9uaW9uLXBvcnQgOTAwMQpv\r\n\ +bmlvbi1rZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dC\r\n\ +QUxGRVVyeVpDbk9ROEhURmV5cDVjMTRObWVqL1BhekFLTTBxRENTNElKUWh0Y3g1\r\n\ +NXpRSFdOVWIKQ2hHZ0JqR1RjV3ZGRnA0N3FkdGF6WUZhVXE2c0lQKzVqeWZ5b0Q4\r\n\ +UmJ1bzBwQmFWclJjMmNhYUptWWM0RDh6Vgpuby9sZnhzOVVaQnZ1cWY4eHIrMDB2\r\n\ +S0JJNmFSMlA2OE1WeDhrMExqcUpUU2RKOE9idm9yQWdNQkFBRT0KLS0tLS1FTkQg\r\n\ +UlNBIFBVQkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQ\r\n\ +VUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTnJHb0ozeTlHNXQzN2F2ekI1cTlwN1hG\r\n\ +VUplRUVYMUNOaExnWmJXWGJhVk5OcXpoZFhyL0xTUQppM1Z6dW5OaUs3cndUVnE2\r\n\ +K2QyZ1lRckhMMmIvMXBBY3ZKWjJiNSs0bTRRc0NibFpjRENXTktRbHJnRWN5WXRJ\r\n\ +CkdscXJTbFFEaXA0ZnNrUFMvNDVkWTI0QmJsQ3NGU1k3RzVLVkxJck4zZFpGbmJr\r\n\ +NEZIS1hBZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJv\r\n\ +ZHVjdGlvbi1wb2ludCBiNGM3enlxNXNheGZzN2prNXFibG1wN3I1b3pwdHRvagpp\r\n\ +cC1hZGRyZXNzIDEwOS4xNjkuNDUuMjI2Cm9uaW9uLXBvcnQgOTAwMQpvbmlvbi1r\r\n\ +ZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQU8xSXpw\r\n\ +WFFUTUY3RXZUb1NEUXpzVnZiRVFRQUQrcGZ6NzczMVRXZzVaUEJZY1EyUkRaeVp4\r\n\ +OEQKNUVQSU1FeUE1RE83cGd0ak5LaXJvYXJGMC8yempjMkRXTUlSaXZyU29YUWVZ\r\n\ +ZXlMM1pzKzFIajJhMDlCdkYxZAp6MEswblRFdVhoNVR5V3lyMHdsbGI1SFBnTlI0\r\n\ +MS9oYkprZzkwZitPVCtIeGhKL1duUml2QWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBV\r\n\ +QkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQVUJMSUMg\r\n\ +S0VZLS0tLS0KTUlHSkFvR0JBSzNWZEJ2ajFtQllLL3JrcHNwcm9Ub0llNUtHVmth\r\n\ +QkxvMW1tK1I2YUVJek1VZFE1SjkwNGtyRwpCd3k5NC8rV0lGNFpGYXh5Z2phejl1\r\n\ +N2pKY1k3ZGJhd1pFeG1hYXFCRlRwL2h2ZG9rcHQ4a1ByRVk4OTJPRHJ1CmJORUox\r\n\ +N1FPSmVMTVZZZk5Kcjl4TWZCQ3JQai8zOGh2RUdrbWVRNmRVWElvbVFNaUJGOVRB\r\n\ +Z01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJvZHVjdGlv\r\n\ +bi1wb2ludCBhdjVtcWl0Y2Q3cjJkandsYmN0c2Jlc2R3eGt0ZWtvegppcC1hZGRy\r\n\ +ZXNzIDE0NC43Ni44LjczCm9uaW9uLXBvcnQgNDQzCm9uaW9uLWtleQotLS0tLUJF\r\n\ +R0lOIFJTQSBQVUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTzVweVZzQmpZQmNmMXBE\r\n\ +dklHUlpmWXUzQ05nNldka0ZLMGlvdTBXTGZtejZRVDN0NWhzd3cyVwpjejlHMXhx\r\n\ +MmN0Nkd6VWkrNnVkTDlITTRVOUdHTi9BbW8wRG9GV1hKWHpBQkFXd2YyMVdsd1lW\r\n\ +eFJQMHRydi9WCkN6UDkzcHc5OG5vSmdGUGRUZ05iMjdKYmVUZENLVFBrTEtscXFt\r\n\ +b3NveUN2RitRa25vUS9BZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0t\r\n\ +LS0tCnNlcnZpY2Uta2V5Ci0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLQpN\r\n\ +SUdKQW9HQkFMVjNKSmtWN3lTNU9jc1lHMHNFYzFQOTVRclFRR3ZzbGJ6Wi9zRGxl\r\n\ +RlpKYXFSOUYvYjRUVERNClNGcFMxcU1GbldkZDgxVmRGMEdYRmN2WVpLamRJdHU2\r\n\ +SndBaTRJeEhxeXZtdTRKdUxrcXNaTEFLaXRLVkx4eGsKeERlMjlDNzRWMmJrOTRJ\r\n\ +MEgybTNKS2tzTHVwc3VxWWRVUmhOVXN0SElKZmgyZmNIalF0bEFnTUJBQUU9Ci0t\r\n\ +LS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0KCg==\r\n\ +-----END MESSAGE-----\r\n\ +signature\r\n\ +-----BEGIN SIGNATURE-----\r\n\ +d4OuCE5OLAOnRB6cQN6WyMEmg/BHem144Vec+eYgeWoKwx3MxXFplUjFxgnMlmwN\r\n\ +PcftsZf2ztN0sbNCtPgDL3d0PqvxY3iHTQAI8EbaGq/IAJUZ8U4y963dD5+Bn6JQ\r\n\ +myE3ctmh0vy5+QxSiRjmQBkuEpCyks7LvWvHYrhnmcg=\r\n\ +-----END SIGNATURE-----"; + /* Helper global variable for hidden service descriptor event test. * It's used as a pointer to dynamically created message buffer in * send_control_event_string_replacement function, which mocks @@ -69,10 +132,13 @@ static void test_hs_desc_event(void *arg) { #define STR_HS_ADDR "ajhb7kljbiru65qo" - #define STR_HS_ID "b3oeducbhjmbqmgw2i3jtz4fekkrinwj" + #define STR_HS_CONTENT_DESC_ID "g5ojobzupf275beh5ra72uyhb3dkpxwg" + #define STR_DESC_ID_BASE32 "hba3gmcgpfivzfhx5rtfqkfdhv65yrj3" + int ret; rend_data_t rend_query; const char *expected_msg; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; (void) arg; MOCK(send_control_event_string, @@ -81,31 +147,48 @@ test_hs_desc_event(void *arg) node_describe_longname_by_id_replacement); /* setup rend_query struct */ + memset(&rend_query, 0, sizeof(rend_query)); strncpy(rend_query.onion_address, STR_HS_ADDR, REND_SERVICE_ID_LEN_BASE32+1); - rend_query.auth_type = 0; + rend_query.auth_type = REND_NO_AUTH; + rend_query.hsdirs_fp = smartlist_new(); + smartlist_add(rend_query.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID, + DIGEST_LEN)); + + /* Compute descriptor ID for replica 0, should be STR_DESC_ID_BASE32. */ + ret = rend_compute_v2_desc_id(rend_query.descriptor_id[0], + rend_query.onion_address, + NULL, 0, 0); + tt_int_op(ret, ==, 0); + base32_encode(desc_id_base32, sizeof(desc_id_base32), + rend_query.descriptor_id[0], DIGEST_LEN); + /* Make sure rend_compute_v2_desc_id works properly. */ + tt_mem_op(desc_id_base32, OP_EQ, STR_DESC_ID_BASE32, + sizeof(desc_id_base32)); /* test request event */ control_event_hs_descriptor_requested(&rend_query, HSDIR_EXIST_ID, - STR_HS_ID); + STR_DESC_ID_BASE32); expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ - STR_HSDIR_EXIST_LONGNAME" "STR_HS_ID"\r\n"; + STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n"; tt_assert(received_msg); tt_str_op(received_msg,OP_EQ, expected_msg); tor_free(received_msg); /* test received event */ - rend_query.auth_type = 1; - control_event_hs_descriptor_received(&rend_query, HSDIR_EXIST_ID); + rend_query.auth_type = REND_BASIC_AUTH; + control_event_hs_descriptor_received(rend_query.onion_address, + &rend_query, HSDIR_EXIST_ID); expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\ - STR_HSDIR_EXIST_LONGNAME"\r\n"; + STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n"; tt_assert(received_msg); tt_str_op(received_msg,OP_EQ, expected_msg); tor_free(received_msg); /* test failed event */ - rend_query.auth_type = 2; - control_event_hs_descriptor_failed(&rend_query, HSDIR_NONE_EXIST_ID, + rend_query.auth_type = REND_STEALTH_AUTH; + control_event_hs_descriptor_failed(&rend_query, + HSDIR_NONE_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ STR_HSDIR_NONE_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n"; @@ -115,14 +198,31 @@ test_hs_desc_event(void *arg) /* test invalid auth type */ rend_query.auth_type = 999; - control_event_hs_descriptor_failed(&rend_query, HSDIR_EXIST_ID, + control_event_hs_descriptor_failed(&rend_query, + HSDIR_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ - STR_HSDIR_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n"; + STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32\ + " REASON=QUERY_REJECTED\r\n"; tt_assert(received_msg); tt_str_op(received_msg,OP_EQ, expected_msg); tor_free(received_msg); + /* test valid content. */ + char *exp_msg; + control_event_hs_descriptor_content(rend_query.onion_address, STR_HS_CONTENT_DESC_ID, + HSDIR_EXIST_ID, hs_desc_content); + tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " "\ + STR_HS_CONTENT_DESC_ID " " STR_HSDIR_EXIST_LONGNAME\ + "\r\n%s\r\n.\r\n650 OK\r\n", hs_desc_content); + + tt_assert(received_msg); + tt_str_op(received_msg, OP_EQ, exp_msg); + tor_free(received_msg); + tor_free(exp_msg); + SMARTLIST_FOREACH(rend_query.hsdirs_fp, char *, d, tor_free(d)); + smartlist_free(rend_query.hsdirs_fp); + done: UNMOCK(send_control_event_string); UNMOCK(node_describe_longname_by_id); @@ -198,7 +298,142 @@ test_pick_bad_tor2web_rendezvous_node(void *arg) routerset_free(options->Tor2webRendezvousPoints); } +/* Make sure rend_data_t is valid at creation, destruction and when + * duplicated. */ +static void +test_hs_rend_data(void *arg) +{ + int rep; + rend_data_t *client; + /* Binary format of a descriptor ID. */ + char desc_id[DIGEST_LEN]; + char client_cookie[REND_DESC_COOKIE_LEN]; + time_t now = time(NULL); + + (void)arg; + + base32_decode(desc_id, sizeof(desc_id), STR_DESC_ID_BASE32, + REND_DESC_ID_V2_LEN_BASE32); + memset(client_cookie, 'e', sizeof(client_cookie)); + + client = rend_data_client_create(STR_HS_ADDR, desc_id, client_cookie, + REND_NO_AUTH); + tt_assert(client); + tt_int_op(client->auth_type, ==, REND_NO_AUTH); + tt_str_op(client->onion_address, OP_EQ, STR_HS_ADDR); + tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); + tt_mem_op(client->descriptor_cookie, OP_EQ, client_cookie, + sizeof(client_cookie)); + tt_assert(client->hsdirs_fp); + tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); + for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { + int ret = rend_compute_v2_desc_id(desc_id, client->onion_address, + client->descriptor_cookie, now, rep); + /* That shouldn't never fail. */ + tt_int_op(ret, ==, 0); + tt_mem_op(client->descriptor_id[rep], OP_EQ, desc_id, sizeof(desc_id)); + } + /* The rest should be zeroed because this is a client request. */ + tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); + + /* Test dup(). */ + rend_data_t *client_dup = rend_data_dup(client); + tt_assert(client_dup); + tt_int_op(client_dup->auth_type, ==, client->auth_type); + tt_str_op(client_dup->onion_address, OP_EQ, client->onion_address); + tt_mem_op(client_dup->desc_id_fetch, OP_EQ, client->desc_id_fetch, + sizeof(client_dup->desc_id_fetch)); + tt_mem_op(client_dup->descriptor_cookie, OP_EQ, client->descriptor_cookie, + sizeof(client_dup->descriptor_cookie)); + + tt_assert(client_dup->hsdirs_fp); + tt_int_op(smartlist_len(client_dup->hsdirs_fp), ==, 0); + for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { + tt_mem_op(client_dup->descriptor_id[rep], OP_EQ, + client->descriptor_id[rep], DIGEST_LEN); + } + /* The rest should be zeroed because this is a client request. */ + tt_int_op(tor_digest_is_zero(client_dup->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), ==, 1); + rend_data_free(client); + rend_data_free(client_dup); + + /* Reset state. */ + base32_decode(desc_id, sizeof(desc_id), STR_DESC_ID_BASE32, + REND_DESC_ID_V2_LEN_BASE32); + memset(client_cookie, 'e', sizeof(client_cookie)); + + /* Try with different parameters here for which some content should be + * zeroed out. */ + client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH); + tt_assert(client); + tt_int_op(client->auth_type, ==, REND_BASIC_AUTH); + tt_int_op(strlen(client->onion_address), ==, 0); + tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); + tt_int_op(tor_mem_is_zero(client->descriptor_cookie, + sizeof(client->descriptor_cookie)), ==, 1); + tt_assert(client->hsdirs_fp); + tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); + for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { + tt_int_op(tor_digest_is_zero(client->descriptor_id[rep]), ==, 1); + } + /* The rest should be zeroed because this is a client request. */ + tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); + rend_data_free(client); + + /* Let's test the service object now. */ + rend_data_t *service; + char rend_pk_digest[DIGEST_LEN]; + uint8_t rend_cookie[DIGEST_LEN]; + memset(rend_pk_digest, 'f', sizeof(rend_pk_digest)); + memset(rend_cookie, 'g', sizeof(rend_cookie)); + + service = rend_data_service_create(STR_HS_ADDR, rend_pk_digest, + rend_cookie, REND_NO_AUTH); + tt_assert(service); + tt_int_op(service->auth_type, ==, REND_NO_AUTH); + tt_str_op(service->onion_address, OP_EQ, STR_HS_ADDR); + tt_mem_op(service->rend_pk_digest, OP_EQ, rend_pk_digest, + sizeof(rend_pk_digest)); + tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie)); + tt_assert(service->hsdirs_fp); + tt_int_op(smartlist_len(service->hsdirs_fp), ==, 0); + for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { + tt_int_op(tor_digest_is_zero(service->descriptor_id[rep]), ==, 1); + } + /* The rest should be zeroed because this is a service request. */ + tt_int_op(tor_digest_is_zero(service->descriptor_cookie), ==, 1); + tt_int_op(tor_digest_is_zero(service->desc_id_fetch), ==, 1); + + /* Test dup(). */ + rend_data_t *service_dup = rend_data_dup(service); + tt_assert(service_dup); + tt_int_op(service_dup->auth_type, ==, service->auth_type); + tt_str_op(service_dup->onion_address, OP_EQ, service->onion_address); + tt_mem_op(service_dup->rend_pk_digest, OP_EQ, service->rend_pk_digest, + sizeof(service_dup->rend_pk_digest)); + tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie, + sizeof(service_dup->rend_cookie)); + tt_assert(service_dup->hsdirs_fp); + tt_int_op(smartlist_len(service_dup->hsdirs_fp), ==, 0); + for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { + tt_int_op(tor_digest_is_zero(service_dup->descriptor_id[rep]), ==, 1); + } + /* The rest should be zeroed because this is a service request. */ + tt_int_op(tor_digest_is_zero(service_dup->descriptor_cookie), ==, 1); + tt_int_op(tor_digest_is_zero(service_dup->desc_id_fetch), ==, 1); + rend_data_free(service); + rend_data_free(service_dup); + + done: + return; +} + struct testcase_t hs_tests[] = { + { "hs_rend_data", test_hs_rend_data, TT_FORK, + NULL, NULL }, { "hs_desc_event", test_hs_desc_event, TT_FORK, NULL, NULL }, { "pick_tor2web_rendezvous_node", test_pick_tor2web_rendezvous_node, TT_FORK, diff --git a/src/test/test_ntor.sh.in b/src/test/test_ntor.sh.in new file mode 100644 index 0000000000..be35384ddf --- /dev/null +++ b/src/test/test_ntor.sh.in @@ -0,0 +1,9 @@ +#!@SHELL@ +# Validate Tor's ntor implementation. + +exitcode=0 + +@PYTHON@ @abs_top_srcdir@/src/test/ntor_ref.py test-tor || exitcode=1 +@PYTHON@ @abs_top_srcdir@/src/test/ntor_ref.py self-test || exitcode=1 + +exit ${exitcode} diff --git a/src/test/test_util.c b/src/test/test_util.c index 51e9e761ab..30dc59844a 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -4111,26 +4111,6 @@ test_util_laplace(void *arg) ; } -static void -test_util_strclear(void *arg) -{ - static const char *vals[] = { "", "a", "abcdef", "abcdefgh", NULL }; - int i; - char *v = NULL; - (void)arg; - - for (i = 0; vals[i]; ++i) { - size_t n; - v = tor_strdup(vals[i]); - n = strlen(v); - tor_strclear(v); - tt_assert(tor_mem_is_zero(v, n+1)); - tor_free(v); - } - done: - tor_free(v); -} - #define UTIL_LEGACY(name) \ { #name, test_util_ ## name , 0, NULL, NULL } @@ -4322,6 +4302,35 @@ test_util_ipv4_validation(void *arg) return; } +static void +test_util_writepid(void *arg) +{ + (void) arg; + + char *contents = NULL; + const char *fname = get_fname("tmp_pid"); + unsigned long pid; + char c; + + write_pidfile(fname); + + contents = read_file_to_str(fname, 0, NULL); + tt_assert(contents); + + int n = sscanf(contents, "%lu\n%c", &pid, &c); + tt_int_op(n, OP_EQ, 1); + tt_uint_op(pid, OP_EQ, +#ifdef _WIN32 + _getpid() +#else + getpid() +#endif + ); + + done: + tor_free(contents); +} + struct testcase_t util_tests[] = { UTIL_LEGACY(time), UTIL_TEST(parse_http_time, 0), @@ -4348,7 +4357,6 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(di_ops), UTIL_TEST(round_to_next_multiple_of, 0), UTIL_TEST(laplace, 0), - UTIL_TEST(strclear, 0), UTIL_TEST(find_str_at_start_of_line, 0), UTIL_TEST(string_is_C_identifier, 0), UTIL_TEST(asprintf, 0), @@ -4389,6 +4397,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(max_mem, 0), UTIL_TEST(hostname_validation, 0), UTIL_TEST(ipv4_validation, 0), + UTIL_TEST(writepid, 0), END_OF_TESTCASES }; diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c index a597ef3cbc..dcd0c9af36 100644 --- a/src/test/test_util_slow.c +++ b/src/test/test_util_slow.c @@ -107,9 +107,11 @@ run_util_spawn_background(const char *argv[], const char *expected_out, #ifdef _WIN32 tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE); tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE); + tt_assert(process_handle->stdin_pipe != INVALID_HANDLE_VALUE); #else tt_assert(process_handle->stdout_pipe >= 0); tt_assert(process_handle->stderr_pipe >= 0); + tt_assert(process_handle->stdin_pipe >= 0); #endif /* Check stdout */ diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index aaff5069be..77053b0760 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -18,6 +18,8 @@ #include <event.h> #endif +#define MAX_INFLIGHT (1<<16) + static int opt_verbose = 0; static int opt_n_threads = 8; static int opt_n_items = 10000; @@ -348,7 +350,7 @@ main(int argc, char **argv) } if (opt_n_threads < 1 || opt_n_items < 1 || opt_n_inflight < 1 || opt_n_lowwater < 0 || - opt_n_cancel > opt_n_inflight || + opt_n_cancel > opt_n_inflight || opt_n_inflight > MAX_INFLIGHT || opt_ratio_rsa < 0) { help(); return 1; @@ -356,7 +358,7 @@ main(int argc, char **argv) init_logging(1); crypto_global_init(1, NULL, NULL); - crypto_seed_rng(1); + crypto_seed_rng(); rq = replyqueue_new(as_flags); tor_assert(rq); diff --git a/src/test/test_zero_length_keys.sh.in b/src/test/test_zero_length_keys.sh.in new file mode 100644 index 0000000000..d1492d5e20 --- /dev/null +++ b/src/test/test_zero_length_keys.sh.in @@ -0,0 +1,10 @@ +#!@SHELL@ +# Check that tor regenerates keys when key files are zero-length + +exitcode=0 + +@SHELL@ @abs_top_srcdir@/src/test/zero_length_keys.sh "@builddir@/src/or/tor" -z || exitcode=1 +@SHELL@ @abs_top_srcdir@/src/test/zero_length_keys.sh "@builddir@/src/or/tor" -d || exitcode=1 +@SHELL@ @abs_top_srcdir@/src/test/zero_length_keys.sh "@builddir@/src/or/tor" -e || exitcode=1 + +exit ${exitcode} diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 403c83bdd2..7f387c0b3d 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -269,8 +269,8 @@ main(int c, const char **v) printf("Can't initialize crypto subsystem; exiting.\n"); return 1; } - crypto_set_tls_dh_prime(NULL); - crypto_seed_rng(1); + crypto_set_tls_dh_prime(); + crypto_seed_rng(); rep_hist_init(); network_init(); setup_directory(); diff --git a/src/test/zero_length_keys.sh b/src/test/zero_length_keys.sh index 2fd11d38bd..3c61f8d465 100755 --- a/src/test/zero_length_keys.sh +++ b/src/test/zero_length_keys.sh @@ -3,13 +3,13 @@ # Test for bug #13111 - Tor fails to start if onion keys are zero length # # Usage: -# ./zero_length_keys.sh +# ./zero_length_keys.sh PATH_TO_TOR # Run all the tests below -# ./zero_length_keys.sh -z +# ./zero_length_keys.sh PATH_TO_TOR -z # Check tor will launch and regenerate zero-length keys -# ./zero_length_keys.sh -d +# ./zero_length_keys.sh PATH_TO_TOR -d # Check tor regenerates deleted keys (existing behaviour) -# ./zero_length_keys.sh -e +# ./zero_length_keys.sh PATH_TO_TOR -e # Check tor does not overwrite existing keys (existing behaviour) # # Exit Statuses: @@ -19,10 +19,16 @@ # 3: a command failed - the test could not be completed # -if [ $# -lt 1 ]; then +if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then + echo "Usage: ${0} PATH_TO_TOR [-z|-d|-e]" + exit 1 +elif [ $# -eq 1 ]; then echo "Testing that tor correctly handles zero-length keys" - "$0" -z && "$0" -d && "$0" -e + "$0" "${1}" -z && "$0" "${1}" -d && "$0" "${1}" -e exit $? +else #[$# -gt 1 ]; then + TOR_BINARY="${1}" + shift fi DATA_DIR=`mktemp -d -t tor_zero_length_keys.XXXXXX` @@ -40,7 +46,7 @@ touch "$DATA_DIR"/empty_torrc # DisableNetwork means that the ORPort won't actually be opened. # 'ExitRelay 0' suppresses a warning. -TOR="./src/or/tor --hush --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f $DATA_DIR/empty_torrc" +TOR="${TOR_BINARY} --hush --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f $DATA_DIR/empty_torrc" if [ -s "$DATA_DIR"/keys/secret_id_key ] && [ -s "$DATA_DIR"/keys/secret_onion_key ] && [ -s "$DATA_DIR"/keys/secret_onion_key_ntor ]; then diff --git a/src/tools/include.am b/src/tools/include.am index 54b150a80c..5d778c1143 100644 --- a/src/tools/include.am +++ b/src/tools/include.am @@ -1,21 +1,45 @@ bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-gencert noinst_PROGRAMS+= src/tools/tor-checkkey +if COVERAGE_ENABLED +noinst_PROGRAMS+= src/tools/tor-cov-resolve src/tools/tor-cov-gencert +endif + src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c src_tools_tor_resolve_LDFLAGS = src_tools_tor_resolve_LDADD = src/common/libor.a @TOR_LIB_MATH@ @TOR_LIB_WS32@ +if COVERAGE_ENABLED +src_tools_tor_cov_resolve_SOURCES = src/tools/tor-resolve.c +src_tools_tor_cov_resolve_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_tools_tor_cov_resolve_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_tools_tor_cov_resolve_LDADD = src/common/libor-testing.a \ + @TOR_LIB_MATH@ @TOR_LIB_WS32@ +endif + src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ src_tools_tor_gencert_LDADD = src/common/libor.a src/common/libor-crypto.a \ - $(LIBDONNA) \ + $(LIBDONNA) \ + @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + +if COVERAGE_ENABLED +src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c +src_tools_tor_cov_gencert_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \ + src/common/libor-crypto-testing.a \ + $(LIBDONNA) \ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ +endif src_tools_tor_checkkey_SOURCES = src/tools/tor-checkkey.c src_tools_tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ src_tools_tor_checkkey_LDADD = src/common/libor.a src/common/libor-crypto.a \ - $(LIBDONNA) \ + $(LIBDONNA) \ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ diff --git a/src/tools/tor-checkkey.c b/src/tools/tor-checkkey.c index e404b682cf..ed68bdf52c 100644 --- a/src/tools/tor-checkkey.c +++ b/src/tools/tor-checkkey.c @@ -7,7 +7,7 @@ #include <stdlib.h> #include "crypto.h" #include "torlog.h" -#include "../common/util.h" +#include "util.h" #include "compat.h" #include <openssl/bn.h> #include <openssl/rsa.h> diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index c599822e07..7660be6230 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -28,8 +28,8 @@ #endif #include "compat.h" -#include "../common/util.h" -#include "../common/torlog.h" +#include "util.h" +#include "torlog.h" #include "crypto.h" #include "address.h" @@ -186,8 +186,7 @@ parse_commandline(int argc, char **argv) return 1; in.s_addr = htonl(addr); tor_inet_ntoa(&in, b, sizeof(b)); - address = tor_malloc(INET_NTOA_BUF_LEN+32); - tor_snprintf(address, INET_NTOA_BUF_LEN+32, "%s:%d", b, (int)port); + tor_asprintf(&address, "%s:%d", b, (int)port); } else if (!strcmp(argv[i], "--create-identity-key")) { make_new_id = 1; } else if (!strcmp(argv[i], "--passphrase-fd")) { @@ -486,7 +485,8 @@ generate_certificate(void) EVP_PKEY_get1_RSA(signing_key), RSA_PKCS1_PADDING); signed_len = strlen(buf); - base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r); + base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r, + BASE64_ENCODE_MULTILINE); strlcat(buf, "-----END ID SIGNATURE-----\n" @@ -501,7 +501,8 @@ generate_certificate(void) RSA_PKCS1_PADDING); strlcat(buf, "-----BEGIN SIGNATURE-----\n", sizeof(buf)); signed_len = strlen(buf); - base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r); + base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r, + BASE64_ENCODE_MULTILINE); strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf)); if (!(f = fopen(certificate_file, "w"))) { @@ -532,7 +533,7 @@ main(int argc, char **argv) fprintf(stderr, "Couldn't initialize crypto library.\n"); return 1; } - if (crypto_seed_rng(1)) { + if (crypto_seed_rng()) { fprintf(stderr, "Couldn't seed RNG.\n"); goto done; } @@ -564,6 +565,7 @@ main(int argc, char **argv) tor_free(identity_key_file); tor_free(signing_key_file); tor_free(certificate_file); + tor_free(address); crypto_global_cleanup(); return r; diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c index 04815a63f7..19e3a554fa 100644 --- a/src/tools/tor-resolve.c +++ b/src/tools/tor-resolve.c @@ -5,9 +5,9 @@ #include "orconfig.h" #include "compat.h" -#include "../common/util.h" +#include "util.h" #include "address.h" -#include "../common/torlog.h" +#include "torlog.h" #include "sandbox.h" #include <stdio.h> diff --git a/src/trunnel/include.am b/src/trunnel/include.am index c7ac1679d0..54e3db287e 100644 --- a/src/trunnel/include.am +++ b/src/trunnel/include.am @@ -23,7 +23,7 @@ src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES) src_trunnel_libor_trunnel_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) src_trunnel_libor_trunnel_testing_a_SOURCES = $(TRUNNELSOURCES) -src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) +src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_trunnel_libor_trunnel_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) noinst_HEADERS+= $(TRUNNELHEADERS) diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index c0ab2de390..9c780e85d7 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -232,7 +232,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.2.6.8" +#define VERSION "0.2.7.1-alpha-dev" |