diff options
49 files changed, 1640 insertions, 546 deletions
@@ -1,3 +1,80 @@ +Changes in version 0.2.3.9-alpha - 2011-1?-?? + o Major features: + - When using OpenSSL 1.0.0 or later, use OpenSSL's counter mode + implementation. It makes AES_CTR about 7% faster than our old one + (which was about 10% faster than the one OpenSSL used to provide). + Resolves ticket 4526. + - Tor clients and bridges can now be easily configured to use a + separate 'transport' proxy. This approach helps to resist + censorship by allowing bridges to use protocol obfuscation + plugins. It implements the 'managed proxy' part of proposal + 180. Implements ticket 3472. + - Block excess renegotiations even if they are RFC5746 compliant. + This security fix mitigates potential SSL Denial of Service attacks + that use SSL renegotiation as a way of forcing the server to perform + unneeded computationally expensive SSL handshakes. Implements + ticket 4312. + + o Major bugfixes: + - Teach Tor how to notice excess renegotiation attempts before it + receives the first data SSL record. Fixes part of ticket 4312. + - Only use the EVP interface when AES acceleration is enabled, + to avoid a 5-7% performance regression. Resolves issue 4525; + bugfix on 0.2.3.8-alpha. + + o Minor features: + - Experimental support for running on Windows with IOCP and no + kernel-space socket buffers. This feature is controlled by a new + UserspaceIOCPBuffers feature (off by default), which has no + effect unless Tor has been built with support for bufferevents, + is running on Windows, and has enabled IOCP. This may, in the + long run, help solve or mitigate bug 98. + - Try to make the introductory warning message that Tor prints on + startup more useful for actually finding help and information. + Resolves ticket 2474. + - Running "make version" now displays the version of Tor that + we're about to build. Idea from katmagic; resolves issue 4400. + - If set to 1, Tor will attempt to prevent basic debugging + attachment attempts by other processes. It has no impact for + users who wish to attach if they have CAP_SYS_PTRACE or if they + are root. We believe that this feature works on modern + Gnu/Linux distributions, and that it may also work on OSX and + some *BSD systems (untested). Some modern Gnu/Linux systems + such as Ubuntu have the kernel.yama.ptrace_scope sysctl and by + default enable it as an attempt to limit the PTRACE scope for + all user processes by default. This feature will attempt to + limit the PTRACE scope for Tor specifically - it will not + attempt to alter the system wide ptrace scope as it may not even + exist. If you wish to attach to Tor with a debugger such as gdb + or strace you will want to set this to 0 for the duration of + your debugging. Normal users should leave it on. (Default: 1) + + o Minor bugfixes: + - Resolve an integer overflow bug in smartlist_ensure_capacity(). + Fixes bug 4230; bugfix on Tor 0.1.0.1-rc. Based on a patch by + Mansour Moufid. + - Fix a compile warning in tor_inet_pton(). Bugfix on 0.2.3.8-alpha; + fixes bug 4554. + - Fix a minor formatting issue in one of tor-gencert's error messages. + Fixes bug 4574. + - Prevent a false positive from the check-spaces script, by disabling + the "whitespace between function name and (" check for functions + named 'op()'. + + o Build fixes: + - Properly handle the case where the build-tree is not the same + as the source tree when generating src/common/common_sha1.i, + src/or/micro-revision.i, and src/or/or_sha1.i. Fixes bug 3953; + bugfix on 0.2.0.1-alpha. + + o Code simplifications and refactorings: + - Remove the pure attribute from all functions that used it + previously. In many cases we assigned it incorrectly, because the + functions might assert or call impure functions, and we don't have + evidence that keeping the pure attribute is worthwhile. Implements + changes suggested in ticket 4421. + + Changes in version 0.2.3.8-alpha - 2011-11-22 Tor 0.2.3.8-alpha fixes some crash and assert bugs, including a socketpair-related bug that has been bothering Windows users. It adds diff --git a/Makefile.am b/Makefile.am index cd0d8833c6..b8d18d4c0b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,3 +70,10 @@ check-logs: ./contrib/checkLogs.pl \ src/*/*.[ch] | sort -n +version: + @echo "Tor @VERSION@" + @if test -d "$(top_srcdir)/.git" && test -x "`which git 2>&1;true`"; then \ + echo -n "git: " ;\ + (cd "$(top_srcdir)" && git rev-parse --short=16 HEAD); \ + fi + diff --git a/changes/bug2474 b/changes/bug2474 deleted file mode 100644 index 02d3eb7ba9..0000000000 --- a/changes/bug2474 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor features - - Try to make the introductory warning message that Tor prints on - startup more useful for actually finding help and information. - Resolves bug2474. - diff --git a/changes/bug4230 b/changes/bug4230 deleted file mode 100644 index c1ba5847fc..0000000000 --- a/changes/bug4230 +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes: - - Resolve an integer overflow bug in smartlist_ensure_capacity. - Fixes bug 4230; bugfix on Tor 0.1.0.1-rc. Based on a patch by - Mansour Moufid. - diff --git a/changes/bug4554 b/changes/bug4554 deleted file mode 100644 index e4754c29e9..0000000000 --- a/changes/bug4554 +++ /dev/null @@ -1,4 +0,0 @@ - o Minor bugfixes: - - Fix a compile warning in tor_inet_pton(). Bugfix on 0.2.3.8-alpha; - fixes bug 4554. - diff --git a/changes/bug4584 b/changes/bug4584 new file mode 100644 index 0000000000..38cf2d6da6 --- /dev/null +++ b/changes/bug4584 @@ -0,0 +1,4 @@ + o Privacy/anonymity features (bridge detection): + - Make bridge SSL certificates a bit more stealthy by using random + serial numbers, in the same fashion as OpenSSL when generating + self-signed certificates. Implements ticket 4584. diff --git a/changes/checkSpaces b/changes/checkSpaces deleted file mode 100644 index 91f79ed0fa..0000000000 --- a/changes/checkSpaces +++ /dev/null @@ -1,5 +0,0 @@ - o Minor bugfixes: - - Prevent a false positive from the check-spaces script by disabling - the "whitespace between function name and (" check for functions - named 'op()'. - diff --git a/changes/config b/changes/config new file mode 100644 index 0000000000..3a1c7d113e --- /dev/null +++ b/changes/config @@ -0,0 +1,26 @@ + o Minor features + - Slightly change behavior of "list" options (that is, options that + can appear more than once) when they appear both in torrc and on + the command line. Previously, the command-line options would be + appended to the ones from torrc. Now, the command-line options + override the torrc options entirely. This new behavior allows + the user to override list options (like exit policies and + ports to listen on) from the command line, rather than simply + appending to the list. + - You can get the old (appending) command-line behavior for "list" + "list" options, by prefixing the option name with a "+". + - You can remove all the values for a "list" option from the command + line without adding any new ones by prefixing the option name + with a "/". + - Add *experimental* support for a "defaults" torrc file to be parsed + before the regular torrc. Torrc options override the defaults file's + options in the same way that the command line overrides the torrc. + The SAVECONF controller command saves only those options which differ + between the current configuration and the defaults file. HUP reloads + both files. (Note: This is an experimental feature; its behavior will + probably be refined in future 0.2.3.x-alpha versions to better meet + packagers' needs.) + + o Minor bugfixes: + - Restore behavior of overriding SocksPort, ORPort, and similar + options from the command line. Bugfix on 0.2.3.3-alpha. diff --git a/changes/disable_network b/changes/disable_network new file mode 100644 index 0000000000..e6e7259ea4 --- /dev/null +++ b/changes/disable_network @@ -0,0 +1,9 @@ + o Minor features: + + - New "DisableNetwork" option to prevent Tor from launching any + connections or accepting any connections except on a control + port. Some bundles and controllers want to use this so they can + configure Tor before letting Tor talk to the rest of the + network--for example, to prevent any connections from being made + to a non-bridge address. + diff --git a/changes/proposal178 b/changes/proposal178 new file mode 100644 index 0000000000..ee706952ae --- /dev/null +++ b/changes/proposal178 @@ -0,0 +1,6 @@ + o Major features: + - Implement a more secure consensus parameter voting algorithm that + ensures that at least three directory authorities or a majority of + them voted on a given parameter before including it in the + consensus. Implements proposal 178. + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 8859c3776c..009ba412a6 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -125,6 +125,12 @@ Other options can be specified either on the command-line (--option You probably don't need to adjust this. It has no effect on Windows since that platform lacks getrlimit(). (Default: 1000) +**DisableNetwork** **0**|**1**:: + When this option is set, we don't listen for or accept any connections + other than controller connections, and we don't make any outbound + connections. Controllers sometimes use this option to avoid using + the network until Tor is fully configured. (Default: 0) + **ConstrainedSockets** **0**|**1**:: If set, Tor will tell the kernel to attempt to shrink the buffers for all sockets to the size specified in **ConstrainedSockSize**. This is useful for @@ -264,6 +270,20 @@ Other options can be specified either on the command-line (--option option requires that you start your Tor as root, and you should use the **User** option to properly reduce Tor's privileges. (Default: 0) +**DisableDebuggerAttachment** **0**|**1**:: + If set to 1, Tor will attempt to prevent basic debugging attachment attempts + by other processes. It has no impact for users who wish to attach if they + have CAP_SYS_PTRACE or if they are root. We believe that this feature + works on modern Gnu/Linux distributions, and that it may also work on *BSD + systems (untested). Some modern Gnu/Linux systems such as Ubuntu have the + kernel.yama.ptrace_scope sysctl and by default enable it as an attempt to + limit the PTRACE scope for all user processes by default. This feature will + attempt to limit the PTRACE scope for Tor specifically - it will not attempt + to alter the system wide ptrace scope as it may not even exist. If you wish + to attach to Tor with a debugger such as gdb or strace you will want to set + this to 0 for the duration of your debugging. Normal users should leave it + on. (Default: 1) + **FetchDirInfoEarly** **0**|**1**:: If set to 1, Tor will always fetch directory information like other directory caches, even if you don't meet the normal criteria for fetching diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 2244fe58d3..2920e73d2e 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -56,9 +56,9 @@ noinst_HEADERS = \ common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) if test "@SHA1SUM@" != none; then \ - @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ + (cd "$(srcdir)" && @SHA1SUM@ $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > common_sha1.i; \ elif test "@OPENSSL@" != none; then \ - @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ + (cd "$(srcdir)" && @OPENSSL@ sha1 $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > common_sha1.i; \ else \ rm common_sha1.i; \ touch common_sha1.i; \ diff --git a/src/common/address.h b/src/common/address.h index 359b0264d2..bc225a65ea 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -149,7 +149,7 @@ int tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2, unsigned int tor_addr_hash(const tor_addr_t *addr); int tor_addr_is_v4(const tor_addr_t *addr); -int tor_addr_is_internal(const tor_addr_t *ip, int for_listening) ATTR_PURE; +int tor_addr_is_internal(const tor_addr_t *ip, int for_listening); /** Longest length that can be required for a reverse lookup name. */ /* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */ @@ -185,7 +185,7 @@ int tor_addr_port_split(int severity, const char *addrport, char **address_out, uint16_t *port_out); /* IPv4 helpers */ -int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE; +int is_internal_IP(uint32_t ip, int for_listening); int addr_port_lookup(int severity, const char *addrport, char **address, uint32_t *addr, uint16_t *port_out); int parse_port_range(const char *port, uint16_t *port_min_out, diff --git a/src/common/aes.c b/src/common/aes.c index 9c03b8085c..cec6899817 100644 --- a/src/common/aes.c +++ b/src/common/aes.c @@ -14,34 +14,39 @@ #include <assert.h> #include <stdlib.h> #include <string.h> +#include <openssl/aes.h> +#include <openssl/evp.h> +#include <openssl/engine.h> +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +/* See comments about which counter mode implementation to use below. */ +#include <openssl/modes.h> +#define USE_OPENSSL_CTR +#endif #include "compat.h" #include "aes.h" #include "util.h" #include "torlog.h" -/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function, - * via OpenSSL's EVP_EncryptUpdate function. */ - -/** Defined iff we're using OpenSSL's AES functions for AES. */ -#undef USE_OPENSSL_AES -/** Defined iff we're using OpenSSL's EVP code for AES. */ -#undef USE_OPENSSL_EVP - -/* Here we pick which to use, if none is force-defined above */ -#if (!defined(USE_OPENSSL_AES) && \ - !defined(USE_OPENSSL_EVP)) - -#define USE_OPENSSL_EVP - +#ifdef ANDROID +/* Android's OpenSSL seems to have removed all of its Engine support. */ +#define DISABLE_ENGINES #endif -/* Include OpenSSL headers as needed. */ -#ifdef USE_OPENSSL_AES -# include <openssl/aes.h> -#endif -#ifdef USE_OPENSSL_EVP -# include <openssl/evp.h> -#endif +/* We have 2 strategies for getting AES: Via OpenSSL's AES_encrypt function, + * via OpenSSL's EVP_EncryptUpdate function. + * + * If there's any hardware acceleration in play, we want to be using EVP_* so + * we can get it. Otherwise, we'll want AES_*, which seems to be about 5% + * faster than indirecting through the EVP layer. + */ + +/* We have 2 strategies for counter mode: use our own, or use OpenSSL's. + * + * Here we have a counter mode that's faster than the one shipping with + * OpenSSL pre-1.0 (by about 10%!). But OpenSSL 1.0.0 added a counter mode + * implementation faster than the one here (by about 7%). So we pick which + * one to used based on the Openssl version above. + */ /*======================================================================*/ /* Interface to AES code, and counter implementation */ @@ -49,13 +54,12 @@ /** Implements an AES counter-mode cipher. */ struct aes_cnt_cipher { /** This next element (however it's defined) is the AES key. */ -#if defined(USE_OPENSSL_EVP) - EVP_CIPHER_CTX key; -#elif defined(USE_OPENSSL_AES) - AES_KEY key; -#endif + union { + EVP_CIPHER_CTX evp; + AES_KEY aes; + } key; -#if !defined(WORDS_BIGENDIAN) +#if !defined(WORDS_BIGENDIAN) && !defined(USE_OPENSSL_CTR) #define USING_COUNTER_VARS /** These four values, together, implement a 128-bit counter, with * counter0 as the low-order word and counter3 as the high-order word. */ @@ -77,9 +81,51 @@ struct aes_cnt_cipher { /** The encrypted value of ctr_buf. */ uint8_t buf[16]; /** Our current stream position within buf. */ +#ifdef USE_OPENSSL_CTR + unsigned int pos; +#else uint8_t pos; +#endif + + /** True iff we're using the evp implementation of this cipher. */ + uint8_t using_evp; }; +/** True if we should prefer the EVP implementation for AES, either because + * we're testing it or because we have hardware acceleration configured */ +static int should_use_EVP = 0; + +/** Check whether we should use the EVP interface for AES. If <b>force_val</b> + * is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP + * if there is an engine enabled for aes-ecb. */ +int +evaluate_evp_for_aes(int force_val) +{ + ENGINE *e; + + if (force_val >= 0) { + should_use_EVP = force_val; + return 0; + } +#ifdef DISABLE_ENGINES + should_use_EVP = 0; +#else + e = ENGINE_get_cipher_engine(NID_aes_128_ecb); + + if (e) { + log_notice(LD_CRYPTO, "AES engine \"%s\" found; using EVP_* functions.", + ENGINE_get_name(e)); + should_use_EVP = 1; + } else { + log_notice(LD_CRYPTO, "No AES engine found; using AES_* functions."); + should_use_EVP = 0; + } +#endif + + return 0; +} + +#ifndef USE_OPENSSL_CTR #if !defined(USING_COUNTER_VARS) #define COUNTER(c, n) ((c)->ctr_buf.buf32[3-(n)]) #else @@ -100,16 +146,15 @@ _aes_fill_buf(aes_cnt_cipher_t *cipher) * None of these issues are insurmountable in principle. */ -#if defined(USE_OPENSSL_EVP) - { + if (cipher->using_evp) { int outl=16, inl=16; - EVP_EncryptUpdate(&cipher->key, cipher->buf, &outl, + EVP_EncryptUpdate(&cipher->key.evp, cipher->buf, &outl, cipher->ctr_buf.buf, inl); + } else { + AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key.aes); } -#elif defined(USE_OPENSSL_AES) - AES_encrypt(cipher->ctr_buf.buf, cipher->buf, &cipher->key); -#endif } +#endif /** * Return a newly allocated counter-mode AES128 cipher implementation. @@ -129,18 +174,21 @@ aes_new_cipher(void) void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) { -#if defined(USE_OPENSSL_EVP) - const EVP_CIPHER *c; - switch (key_bits) { - case 128: c = EVP_aes_128_ecb(); break; - case 192: c = EVP_aes_192_ecb(); break; - case 256: c = EVP_aes_256_ecb(); break; - default: tor_assert(0); + if (should_use_EVP) { + const EVP_CIPHER *c; + switch (key_bits) { + case 128: c = EVP_aes_128_ecb(); break; + case 192: c = EVP_aes_192_ecb(); break; + case 256: c = EVP_aes_256_ecb(); break; + default: tor_assert(0); + } + EVP_EncryptInit(&cipher->key.evp, c, (const unsigned char*)key, NULL); + cipher->using_evp = 1; + } else { + AES_set_encrypt_key((const unsigned char *)key, key_bits, &cipher->key.aes); + cipher->using_evp = 0; } - EVP_EncryptInit(&cipher->key, c, (const unsigned char*)key, NULL); -#elif defined(USE_OPENSSL_AES) - AES_set_encrypt_key((const unsigned char *)key, key_bits, &(cipher->key)); -#endif + #ifdef USING_COUNTER_VARS cipher->counter0 = 0; cipher->counter1 = 0; @@ -151,7 +199,12 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits) memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf)); cipher->pos = 0; + +#ifdef USE_OPENSSL_CTR + memset(cipher->buf, 0, sizeof(cipher->buf)); +#else _aes_fill_buf(cipher); +#endif } /** Release storage held by <b>cipher</b> @@ -161,9 +214,9 @@ aes_free_cipher(aes_cnt_cipher_t *cipher) { if (!cipher) return; -#ifdef USE_OPENSSL_EVP - EVP_CIPHER_CTX_cleanup(&cipher->key); -#endif + if (cipher->using_evp) { + EVP_CIPHER_CTX_cleanup(&cipher->key.evp); + } memset(cipher, 0, sizeof(aes_cnt_cipher_t)); tor_free(cipher); } @@ -176,6 +229,18 @@ aes_free_cipher(aes_cnt_cipher_t *cipher) #define UPDATE_CTR_BUF(c, n) #endif +#ifdef USE_OPENSSL_CTR +/* Helper function to use EVP with openssl's counter-mode wrapper. */ +static void evp_block128_fn(const uint8_t in[16], + uint8_t out[16], + const void *key) +{ + EVP_CIPHER_CTX *ctx = (void*)key; + int inl=16, outl=16; + EVP_EncryptUpdate(ctx, out, &outl, in, inl); +} +#endif + /** Encrypt <b>len</b> bytes from <b>input</b>, storing the result in * <b>output</b>. Uses the key in <b>cipher</b>, and advances the counter * by <b>len</b> bytes as it encrypts. @@ -184,20 +249,29 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, char *output) { - /* This function alone is up to 5% of our runtime in some profiles; anything - * we could do to make it faster would be great. - * - * Experimenting suggests that unrolling the inner loop into a switch - * statement doesn't help. What does seem to help is making the input and - * output buffers word aligned, and never crypting anything besides an - * integer number of words at a time -- it shaves maybe 4-5% of the per-byte - * encryption time measured by bench_aes. We can't do that with the current - * Tor protocol, though: Tor really likes to crypt things in 509-byte - * chunks. - * - * If we were really ambitous, we'd force len to be a multiple of the block - * size, and shave maybe another 4-5% off. - */ +#ifdef USE_OPENSSL_CTR + if (cipher->using_evp) { + /* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If + * it weren't disabled, it might be better just to use that. + */ + CRYPTO_ctr128_encrypt((const unsigned char *)input, + (unsigned char *)output, + len, + &cipher->key.evp, + cipher->ctr_buf.buf, + cipher->buf, + &cipher->pos, + evp_block128_fn); + } else { + AES_ctr128_encrypt((const unsigned char *)input, + (unsigned char *)output, + len, + &cipher->key.aes, + cipher->ctr_buf.buf, + cipher->buf, + &cipher->pos); + } +#else int c = cipher->pos; if (PREDICT_UNLIKELY(!len)) return; @@ -220,6 +294,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, UPDATE_CTR_BUF(cipher, 0); _aes_fill_buf(cipher); } +#endif } /** Encrypt <b>len</b> bytes from <b>input</b>, storing the results in place. @@ -229,11 +304,9 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) { - - /* XXXX This function is up to 5% of our runtime in some profiles; - * we should look into unrolling some of the loops; taking advantage - * of alignment, using a bigger buffer, and so on. Not till after 0.1.2.x, - * though. */ +#ifdef USE_OPENSSL_CTR + aes_crypt(cipher, data, len, data); +#else int c = cipher->pos; if (PREDICT_UNLIKELY(!len)) return; @@ -256,6 +329,7 @@ aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len) UPDATE_CTR_BUF(cipher, 0); _aes_fill_buf(cipher); } +#endif } /** Reset the 128-bit counter of <b>cipher</b> to the 16-bit big-endian value @@ -272,6 +346,8 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv) cipher->pos = 0; memcpy(cipher->ctr_buf.buf, iv, 16); +#ifndef USE_OPENSSL_CTR _aes_fill_buf(cipher); +#endif } diff --git a/src/common/aes.h b/src/common/aes.h index b2591942bc..221e846155 100644 --- a/src/common/aes.h +++ b/src/common/aes.h @@ -24,5 +24,7 @@ void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len, void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len); void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv); +int evaluate_evp_for_aes(int force_value); + #endif diff --git a/src/common/compat.c b/src/common/compat.c index 9a2c9d764b..ea95f9f085 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -58,6 +58,14 @@ #endif #endif +/* Includes for the process attaching prevention */ +#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) +#include <sys/prctl.h> +#elif defined(__APPLE__) +#include <sys/types.h> +#include <sys/ptrace.h> +#endif + #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -1519,6 +1527,57 @@ switch_id(const char *user) #endif } +/* We only use the linux prctl for now. There is no Win32 support; this may + * also work on various BSD systems and Mac OS X - send testing feedback! + * + * On recent Gnu/Linux kernels it is possible to create a system-wide policy + * that will prevent non-root processes from attaching to other processes + * unless they are the parent process; thus gdb can attach to programs that + * they execute but they cannot attach to other processes running as the same + * user. The system wide policy may be set with the sysctl + * kernel.yama.ptrace_scope or by inspecting + * /proc/sys/kernel/yama/ptrace_scope and it is 1 by default on Ubuntu 11.04. + * + * This ptrace scope will be ignored on Gnu/Linux for users with + * CAP_SYS_PTRACE and so it is very likely that root will still be able to + * attach to the Tor process. + */ +/** Attempt to disable debugger attachment: return 0 on success, -1 on + * failure. */ +int +tor_disable_debugger_attach(void) +{ + int r, attempted; + r = -1; + attempted = 0; + log_debug(LD_CONFIG, + "Attemping to disable debugger attachment to Tor for " + "unprivileged users."); +#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL) +#ifdef PR_SET_DUMPABLE + attempted = 1; + r = prctl(PR_SET_DUMPABLE, 0); +#endif +#endif +#if defined(__APPLE__) && defined(PT_DENY_ATTACH) + if (r < 0) { + attempted = 1; + r = ptrace(PT_DENY_ATTACH, 0, 0, 0); + } +#endif + + // XXX: TODO - Mac OS X has dtrace and this may be disabled. + // XXX: TODO - Windows probably has something similar + if (r == 0) { + log_debug(LD_CONFIG,"Debugger attachment disabled for " + "unprivileged users."); + } else if (attempted) { + log_warn(LD_CONFIG, "Unable to disable ptrace attach: %s", + strerror(errno)); + } + return r; +} + #ifdef HAVE_PWD_H /** Allocate and return a string containing the home directory for the * user <b>username</b>. Only works on posix-like systems. */ diff --git a/src/common/compat.h b/src/common/compat.h index b005dd2974..db541623d3 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -135,7 +135,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) { /* GCC has several useful attributes. */ #if defined(__GNUC__) && __GNUC__ >= 3 #define ATTR_NORETURN __attribute__((noreturn)) -#define ATTR_PURE __attribute__((pure)) #define ATTR_CONST __attribute__((const)) #define ATTR_MALLOC __attribute__((malloc)) #define ATTR_NORETURN __attribute__((noreturn)) @@ -168,7 +167,6 @@ extern INLINE double U64_TO_DBL(uint64_t x) { #define PREDICT_UNLIKELY(exp) __builtin_expect(!!(exp), 0) #else #define ATTR_NORETURN -#define ATTR_PURE #define ATTR_CONST #define ATTR_MALLOC #define ATTR_NORETURN @@ -271,9 +269,9 @@ int tor_asprintf(char **strp, const char *fmt, ...) int tor_vasprintf(char **strp, const char *fmt, va_list args); const void *tor_memmem(const void *haystack, size_t hlen, const void *needle, - size_t nlen) ATTR_PURE ATTR_NONNULL((1,3)); + size_t nlen) ATTR_NONNULL((1,3)); static const void *tor_memstr(const void *haystack, size_t hlen, - const char *needle) ATTR_PURE ATTR_NONNULL((1,3)); + const char *needle) ATTR_NONNULL((1,3)); static INLINE const void * tor_memstr(const void *haystack, size_t hlen, const char *needle) { @@ -546,9 +544,9 @@ long tor_weak_random(void); /* ===== OS compatibility */ const char *get_uname(void); -uint16_t get_uint16(const void *cp) ATTR_PURE ATTR_NONNULL((1)); -uint32_t get_uint32(const void *cp) ATTR_PURE ATTR_NONNULL((1)); -uint64_t get_uint64(const void *cp) ATTR_PURE ATTR_NONNULL((1)); +uint16_t get_uint16(const void *cp) ATTR_NONNULL((1)); +uint32_t get_uint32(const void *cp) ATTR_NONNULL((1)); +uint64_t get_uint64(const void *cp) ATTR_NONNULL((1)); void set_uint16(void *cp, uint16_t v) ATTR_NONNULL((1)); void set_uint32(void *cp, uint32_t v) ATTR_NONNULL((1)); void set_uint64(void *cp, uint64_t v) ATTR_NONNULL((1)); @@ -566,6 +564,7 @@ set_uint8(void *cp, uint8_t v) typedef unsigned long rlim_t; #endif int set_max_file_descriptors(rlim_t limit, int *max); +int tor_disable_debugger_attach(void); int switch_id(const char *user); #ifdef HAVE_PWD_H char *get_user_homedir(const char *username); diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index 0cedef8d5e..67f465927c 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -558,6 +558,60 @@ tor_check_libevent_header_compatibility(void) #endif } +struct tor_libevent_action_t { + struct event *ev; + void (*cb)(void *arg); + void *arg; +}; + +/** Callback for tor_run_in_libevent_loop */ +static void +run_runnable_cb(evutil_socket_t s, short what, void *arg) +{ + tor_libevent_action_t *r = arg; + void (*cb)(void *) = r->cb; + void *cb_arg = r->arg; + (void)what; + (void)s; + tor_event_free(r->ev); + tor_free(r); + + cb(cb_arg); +} + +/** Cause cb(arg) to run later on this iteration of the libevent loop, or in + * the next iteration of the libevent loop. This is useful for when you're + * deep inside a no-reentrant code and there's some function you want to call + * without worrying about whether it might cause reeentrant invocation. + */ +tor_libevent_action_t * +tor_run_in_libevent_loop(void (*cb)(void *arg), void *arg) +{ + tor_libevent_action_t *r = tor_malloc(sizeof(tor_libevent_action_t)); + r->cb = cb; + r->arg = arg; + r->ev = tor_event_new(tor_libevent_get_base(), -1, EV_TIMEOUT, + run_runnable_cb, r); + if (!r->ev) { + tor_free(r); + return NULL; + } + /* Make the event active immediately. */ + event_active(r->ev, EV_TIMEOUT, 1); + + return r; +} + +/** + * Cancel <b>action</b> without running it. + */ +void +tor_cancel_libevent_action(tor_libevent_action_t *action) +{ + tor_event_free(action->ev); + tor_free(action); +} + /* If possible, we're going to try to use Libevent's periodic timer support, since it does a pretty good job of making sure that periodic events get diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 0247297177..4076cc0e08 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -44,8 +44,12 @@ void tor_event_free(struct event *ev); #define tor_evdns_add_server_port evdns_add_server_port #endif -typedef struct periodic_timer_t periodic_timer_t; +typedef struct tor_libevent_action_t tor_libevent_action_t; +tor_libevent_action_t *tor_run_in_libevent_loop(void (*cb)(void *arg), + void *arg); +void tor_cancel_libevent_action(tor_libevent_action_t *action); +typedef struct periodic_timer_t periodic_timer_t; periodic_timer_t *periodic_timer_new(struct event_base *base, const struct timeval *tv, void (*cb)(periodic_timer_t *timer, void *data), diff --git a/src/common/container.h b/src/common/container.h index 4a6eba789d..fe071cc1b3 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -35,19 +35,14 @@ void smartlist_remove(smartlist_t *sl, const void *element); void *smartlist_pop_last(smartlist_t *sl); void smartlist_reverse(smartlist_t *sl); void smartlist_string_remove(smartlist_t *sl, const char *element); -int smartlist_isin(const smartlist_t *sl, const void *element) ATTR_PURE; -int smartlist_string_isin(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_string_pos(const smartlist_t *, const char *elt) ATTR_PURE; -int smartlist_string_isin_case(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE; -int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) - ATTR_PURE; -int smartlist_digest_isin(const smartlist_t *sl, const char *element) - ATTR_PURE; -int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) - ATTR_PURE; +int smartlist_isin(const smartlist_t *sl, const void *element); +int smartlist_string_isin(const smartlist_t *sl, const char *element); +int smartlist_string_pos(const smartlist_t *, const char *elt); +int smartlist_string_isin_case(const smartlist_t *sl, const char *element); +int smartlist_string_num_isin(const smartlist_t *sl, int num); +int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2); +int smartlist_digest_isin(const smartlist_t *sl, const char *element); +int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); @@ -55,14 +50,14 @@ void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); #ifdef DEBUG_SMARTLIST /** Return the number of items in sl. */ -static INLINE int smartlist_len(const smartlist_t *sl) ATTR_PURE; +static INLINE int smartlist_len(const smartlist_t *sl); static INLINE int smartlist_len(const smartlist_t *sl) { tor_assert(sl); return (sl)->num_used; } /** Return the <b>idx</b>th element of sl. */ -static INLINE void *smartlist_get(const smartlist_t *sl, int idx) ATTR_PURE; +static INLINE void *smartlist_get(const smartlist_t *sl, int idx); static INLINE void *smartlist_get(const smartlist_t *sl, int idx) { tor_assert(sl); tor_assert(idx>=0); @@ -114,8 +109,7 @@ void smartlist_uniq_strings(smartlist_t *sl); void smartlist_uniq_digests(smartlist_t *sl); void smartlist_uniq_digests256(smartlist_t *sl); void *smartlist_bsearch(smartlist_t *sl, const void *key, - int (*compare)(const void *key, const void **member)) - ATTR_PURE; + int (*compare)(const void *key, const void **member)); int smartlist_bsearch_idx(const smartlist_t *sl, const void *key, int (*compare)(const void *key, const void **member), int *found_out); diff --git a/src/common/crypto.c b/src/common/crypto.c index a38956d3e8..ebaa0122fd 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -276,6 +276,9 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir) } else { log_info(LD_CRYPTO, "NOT using OpenSSL engine support."); } + + evaluate_evp_for_aes(-1); + return crypto_seed_rng(1); } return 0; diff --git a/src/common/tortls.c b/src/common/tortls.c index 554deb7bda..a6947c87d8 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -52,7 +52,6 @@ #include <event2/bufferevent_ssl.h> #include <event2/buffer.h> #include <event2/event.h> -#include "compat_libevent.h" #endif #define CRYPTO_PRIVATE /* to import prototypes from crypto.h */ @@ -64,6 +63,7 @@ #include "torlog.h" #include "container.h" #include <string.h> +#include "compat_libevent.h" /* Enable the "v2" TLS handshake. */ @@ -146,7 +146,7 @@ struct tor_tls_t { /** True iff we should call negotiated_callback when we're done reading. */ unsigned int got_renegotiate:1; /** Incremented every time we start the server side of a handshake. */ - uint8_t server_handshake_count; + unsigned int server_handshake_count:2; size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last * time. */ /** Last values retrieved from BIO_number_read()/write(); see @@ -157,6 +157,11 @@ struct tor_tls_t { /** If set, a callback to invoke whenever the client tries to renegotiate * the handshake. */ void (*negotiated_callback)(tor_tls_t *tls, void *arg); + + /** Callback to invoke whenever a client tries to renegotiate more + than once. */ + void (*excess_renegotiations_callback)(void *); + /** Argument to pass to negotiated_callback. */ void *callback_arg; }; @@ -580,7 +585,13 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, const char *cname_sign, unsigned int cert_lifetime) { + /* OpenSSL generates self-signed certificates with random 64-bit serial + * numbers, so let's do that too. */ +#define SERIAL_NUMBER_SIZE 8 + time_t start_time, end_time; + BIGNUM *serial_number = NULL; + unsigned char serial_tmp[SERIAL_NUMBER_SIZE]; EVP_PKEY *sign_pkey = NULL, *pkey=NULL; X509 *x509 = NULL; X509_NAME *name = NULL, *name_issuer=NULL; @@ -601,8 +612,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, goto error; if (!(X509_set_version(x509, 2))) goto error; - if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time))) - goto error; + + { /* our serial number is 8 random bytes. */ + if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0) + goto error; + if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL))) + goto error; + if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509)))) + goto error; + } if (!(name = tor_x509_name_new(cname))) goto error; @@ -635,11 +653,15 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, EVP_PKEY_free(sign_pkey); if (pkey) EVP_PKEY_free(pkey); + if (serial_number) + BN_free(serial_number); if (name) X509_NAME_free(name); if (name_issuer) X509_NAME_free(name_issuer); return x509; + +#undef SERIAL_NUMBER_SIZE } /** List of ciphers that servers should select from.*/ @@ -1297,55 +1319,42 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address) return 1; } +/** We got an SSL ClientHello message. This might mean that the + * client wants to initiate a renegotiation and appropriate actions + * must be taken. */ static void -tor_tls_debug_state_callback(const SSL *ssl, int type, int val) -{ - log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", - ssl, ssl_state_to_string(ssl->state), type, val); -} - -/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection - * changes state. We use this: - * <ul><li>To alter the state of the handshake partway through, so we - * do not send or request extra certificates in v2 handshakes.</li> - * <li>To detect renegotiation</li></ul> - */ -static void -tor_tls_server_info_callback(const SSL *ssl, int type, int val) +tor_tls_got_client_hello(tor_tls_t *tls) { - tor_tls_t *tls; - (void) val; + if (tls->server_handshake_count < 3) + ++tls->server_handshake_count; - tor_tls_debug_state_callback(ssl, type, val); + if (tls->server_handshake_count == 2) { + if (!tls->negotiated_callback) { + log_warn(LD_BUG, "Got a renegotiation request but we don't" + " have a renegotiation callback set!"); + } - if (type != SSL_CB_ACCEPT_LOOP) - return; - if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A) - return; + tls->got_renegotiate = 1; + } else if (tls->server_handshake_count > 2 && + tls->excess_renegotiations_callback) { + /* We got more than one renegotiation requests. The Tor protocol + needs just one renegotiation; more than that probably means + They are trying to DoS us and we have to stop them. */ - tls = tor_tls_get_by_ssl(ssl); - if (tls) { - /* Check whether we're watching for renegotiates. If so, this is one! */ - if (tls->negotiated_callback) - tls->got_renegotiate = 1; - if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/ - ++tls->server_handshake_count; - } else { - log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); - return; + tls->excess_renegotiations_callback(tls->callback_arg); } /* Now check the cipher list. */ - if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) { + if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) { /*XXXX_TLS keep this from happening more than once! */ /* Yes, we're casting away the const from ssl. This is very naughty of us. * Let's hope openssl doesn't notice! */ /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */ - SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN); + SSL_set_mode((SSL*) tls->ssl, SSL_MODE_NO_AUTO_CHAIN); /* Don't send a hello request. */ - SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL); + SSL_set_verify((SSL*) tls->ssl, SSL_VERIFY_NONE, NULL); if (tls) { tls->wasV2Handshake = 1; @@ -1360,6 +1369,34 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) } #endif +/** This is a callback function for SSL_set_info_callback() and it + * will be called in every SSL state change. + * It logs the SSL state change, and executes any actions that must be + * taken. */ +static void +tor_tls_state_changed_callback(const SSL *ssl, int type, int val) +{ + log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].", + ssl, ssl_state_to_string(ssl->state), type, val); + +#ifdef V2_HANDSHAKE_SERVER + if (type == SSL_CB_ACCEPT_LOOP && + ssl->state == SSL3_ST_SW_SRVR_HELLO_A) { + + /* Call tor_tls_got_client_hello() for every SSL ClientHello we + receive. */ + + tor_tls_t *tls = tor_tls_get_by_ssl(ssl); + if (!tls) { + log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!"); + return; + } + + tor_tls_got_client_hello(tls); + } +#endif +} + /** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically, * a list designed to mimic a common web browser. Some of the ciphers in the * list won't actually be implemented by OpenSSL: that's okay so long as the @@ -1501,14 +1538,8 @@ tor_tls_new(int sock, int isServer) log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu", result->last_read_count, result->last_write_count); } -#ifdef V2_HANDSHAKE_SERVER - if (isServer) { - SSL_set_info_callback(result->ssl, tor_tls_server_info_callback); - } else -#endif - { - SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback); - } + + SSL_set_info_callback(result->ssl, tor_tls_state_changed_callback); /* Not expected to get called. */ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object"); @@ -1526,25 +1557,23 @@ tor_tls_set_logged_address(tor_tls_t *tls, const char *address) tls->address = tor_strdup(address); } -/** Set <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b> - * next gets a client-side renegotiate in the middle of a read. Do not - * invoke this function until <em>after</em> initial handshaking is done! +/** Set <b>cb</b> to be called with argument <b>arg</b> whenever + * <b>tls</b> next gets a client-side renegotiate in the middle of a + * read. Set <b>cb2</b> to be called with argument <b>arg</b> whenever + * <b>tls</b> gets excess renegotiation requests. Do not invoke this + * function until <em>after</em> initial handshaking is done! */ void -tor_tls_set_renegotiate_callback(tor_tls_t *tls, +tor_tls_set_renegotiate_callbacks(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), + void (*cb2)(void *), void *arg) { tls->negotiated_callback = cb; + tls->excess_renegotiations_callback = cb2; tls->callback_arg = arg; tls->got_renegotiate = 0; -#ifdef V2_HANDSHAKE_SERVER - if (cb) { - SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback); - } else { - SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback); - } -#endif + SSL_set_info_callback(tls->ssl, tor_tls_state_changed_callback); } /** If this version of openssl requires it, turn on renegotiation on @@ -1564,16 +1593,6 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls) } } -/** If this version of openssl supports it, turn off renegotiation on - * <b>tls</b>. (Our protocol never requires this for security, but it's nice - * to use belt-and-suspenders here.) - */ -void -tor_tls_block_renegotiation(tor_tls_t *tls) -{ - tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; -} - void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls) { @@ -1611,6 +1630,7 @@ tor_tls_free(tor_tls_t *tls) SSL_free(tls->ssl); tls->ssl = NULL; tls->negotiated_callback = NULL; + tls->excess_renegotiations_callback = NULL; if (tls->context) tor_tls_context_decref(tls->context); tor_free(tls->address); @@ -1632,19 +1652,30 @@ tor_tls_read(tor_tls_t *tls, char *cp, size_t len) tor_assert(tls->state == TOR_TLS_ST_OPEN); tor_assert(len<INT_MAX); r = SSL_read(tls->ssl, cp, (int)len); - if (r > 0) { + if (r > 0) /* return the number of characters read */ + return r; + + /* If we got here, SSL_read() did not go as expected. */ + + err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); + #ifdef V2_HANDSHAKE_SERVER - if (tls->got_renegotiate) { - /* Renegotiation happened! */ - log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls)); - if (tls->negotiated_callback) - tls->negotiated_callback(tls, tls->callback_arg); - tls->got_renegotiate = 0; + if (tls->got_renegotiate) { + if (tls->server_handshake_count != 2) { + log_warn(LD_BUG, "We did not notice renegotiation in a timely " + "fashion (%u)!", tls->server_handshake_count); } -#endif + + /* Renegotiation happened! */ + log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls)); + if (tls->negotiated_callback) + tls->negotiated_callback(tls, tls->callback_arg); + tls->got_renegotiate = 0; + return r; } - err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET); +#endif + if (err == _TOR_TLS_ZERORETURN || err == TOR_TLS_CLOSE) { log_debug(LD_NET,"read returned r=%d; TLS is closed",r); tls->state = TOR_TLS_ST_CLOSED; @@ -1681,6 +1712,7 @@ tor_tls_write(tor_tls_t *tls, const char *cp, size_t n) } r = SSL_write(tls->ssl, cp, (int)n); err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET); + if (err == TOR_TLS_DONE) { return r; } diff --git a/src/common/tortls.h b/src/common/tortls.h index 673f18dfe8..9f86e37127 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -13,6 +13,7 @@ #include "crypto.h" #include "compat.h" +#include "compat_libevent.h" /* Opaque structure to hold a TLS connection. */ typedef struct tor_tls_t tor_tls_t; @@ -60,8 +61,9 @@ int tor_tls_context_init(int is_public_server, unsigned int key_lifetime); tor_tls_t *tor_tls_new(int sock, int is_server); void tor_tls_set_logged_address(tor_tls_t *tls, const char *address); -void tor_tls_set_renegotiate_callback(tor_tls_t *tls, +void tor_tls_set_renegotiate_callbacks(tor_tls_t *tls, void (*cb)(tor_tls_t *, void *arg), + void (*cb2)(void *), void *arg); int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free(tor_tls_t *tls); @@ -77,7 +79,6 @@ int tor_tls_handshake(tor_tls_t *tls); int tor_tls_finish_handshake(tor_tls_t *tls); int tor_tls_renegotiate(tor_tls_t *tls); void tor_tls_unblock_renegotiation(tor_tls_t *tls); -void tor_tls_block_renegotiation(tor_tls_t *tls); void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls); int tor_tls_shutdown(tor_tls_t *tls); int tor_tls_get_pending_bytes(tor_tls_t *tls); diff --git a/src/common/util.c b/src/common/util.c index c19e4d20ca..6d488d9963 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3173,28 +3173,72 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, /* Maximum number of file descriptors, if we cannot get it via sysconf() */ #define DEFAULT_MAX_FD 256 -/** Terminate process running at PID <b>pid</b>. +/** Terminate the process of <b>process_handle</b>. * Code borrowed from Python's os.kill. */ int -tor_terminate_process(pid_t pid) +tor_terminate_process(process_handle_t *process_handle) { #ifdef MS_WINDOWS - HANDLE handle; - /* If the signal is outside of what GenerateConsoleCtrlEvent can use, - attempt to open and terminate the process. */ - handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); - if (!handle) - return -1; + if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) { + HANDLE handle; + /* If the signal is outside of what GenerateConsoleCtrlEvent can use, + attempt to open and terminate the process. */ + handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, + process_handle->pid.dwProcessId); + if (!handle) + return -1; - if (!TerminateProcess(handle, 0)) - return -1; - else - return 0; + if (!TerminateProcess(handle, 0)) + return -1; + else + return 0; + } #else /* Unix */ - return kill(pid, SIGTERM); + return kill(process_handle->pid, SIGTERM); +#endif + + return -1; +} + +/** Return the Process ID of <b>process_handle</b>. */ +int +tor_process_get_pid(process_handle_t *process_handle) +{ +#ifdef MS_WINDOWS + return (int) process_handle->pid.dwProcessId; +#else + return (int) process_handle->pid; +#endif +} + +#ifdef MS_WINDOWS +HANDLE +tor_process_get_stdout_pipe(process_handle_t *process_handle) +{ + return process_handle->stdout_pipe; +} +#else +FILE * +tor_process_get_stdout_pipe(process_handle_t *process_handle) +{ + return process_handle->stdout_handle; +} +#endif + +static process_handle_t * +process_handle_new(void) +{ + process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t)); + +#ifndef MS_WINDOWS + out->stdout_pipe = -1; + out->stderr_pipe = -1; #endif + + return out; } +/*DOCDOC*/ #define CHILD_STATE_INIT 0 #define CHILD_STATE_PIPE 1 #define CHILD_STATE_MAXFD 2 @@ -3227,14 +3271,20 @@ tor_terminate_process(pid_t pid) */ int tor_spawn_background(const char *const filename, const char **argv, +#ifdef MS_WINDOWS + LPVOID envp, +#else const char **envp, - process_handle_t *process_handle) +#endif + process_handle_t **process_handle_out) { #ifdef MS_WINDOWS HANDLE stdout_pipe_read = NULL; HANDLE stdout_pipe_write = NULL; HANDLE stderr_pipe_read = NULL; HANDLE stderr_pipe_write = NULL; + process_handle_t *process_handle; + int status; STARTUPINFO siStartInfo; BOOL retval = FALSE; @@ -3244,30 +3294,26 @@ tor_spawn_background(const char *const filename, const char **argv, (void)envp; // Unused on Windows - /* process_handle must not be NULL */ - tor_assert(process_handle != NULL); - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; /* TODO: should we set explicit security attributes? (#2046, comment 5) */ saAttr.lpSecurityDescriptor = NULL; /* Assume failure to start process */ - memset(process_handle, 0, sizeof(process_handle_t)); - process_handle->status = PROCESS_STATUS_ERROR; + status = PROCESS_STATUS_ERROR; /* Set up pipe for stdout */ if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, &saAttr, 0)) { log_warn(LD_GENERAL, "Failed to create pipe for stdout communication with child process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } if (!SetHandleInformation(stdout_pipe_read, HANDLE_FLAG_INHERIT, 0)) { log_warn(LD_GENERAL, "Failed to configure pipe for stdout communication with child " "process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } /* Set up pipe for stderr */ @@ -3275,13 +3321,13 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to create pipe for stderr communication with child process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } if (!SetHandleInformation(stderr_pipe_read, HANDLE_FLAG_INHERIT, 0)) { log_warn(LD_GENERAL, "Failed to configure pipe for stderr communication with child " "process: %s", format_win32_error(GetLastError())); - return process_handle->status; + return status; } /* Create the child process */ @@ -3290,6 +3336,9 @@ tor_spawn_background(const char *const filename, const char **argv, */ joined_argv = tor_join_win_cmdline(argv); + process_handle = process_handle_new(); + process_handle->status = status; + ZeroMemory(&(process_handle->pid), sizeof(PROCESS_INFORMATION)); ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); @@ -3309,7 +3358,7 @@ tor_spawn_background(const char *const filename, const char **argv, /*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess() * work?) */ 0, // creation flags - NULL, // use parent's environment + envp, // use parent's environment NULL, // use parent's current directory &siStartInfo, // STARTUPINFO pointer &(process_handle->pid)); // receives PROCESS_INFORMATION @@ -3320,22 +3369,25 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to create child process %s: %s", filename?filename:argv[0], format_win32_error(GetLastError())); + tor_free(process_handle); } else { /* TODO: Close hProcess and hThread in process_handle->pid? */ process_handle->stdout_pipe = stdout_pipe_read; process_handle->stderr_pipe = stderr_pipe_read; - process_handle->status = PROCESS_STATUS_RUNNING; + status = process_handle->status = PROCESS_STATUS_RUNNING; } /* TODO: Close pipes on exit */ - - return process_handle->status; + *process_handle_out = process_handle; + return status; #else // MS_WINDOWS pid_t pid; int stdout_pipe[2]; int stderr_pipe[2]; int fd, retval; ssize_t nbytes; + process_handle_t *process_handle; + int status; const char *error_message = SPAWN_ERROR_MESSAGE; size_t error_message_length; @@ -3348,9 +3400,7 @@ tor_spawn_background(const char *const filename, const char **argv, static int max_fd = -1; - /* Assume failure to start */ - memset(process_handle, 0, sizeof(process_handle_t)); - process_handle->status = PROCESS_STATUS_ERROR; + status = PROCESS_STATUS_ERROR; /* We do the strlen here because strlen() is not signal handler safe, and we are not allowed to use unsafe functions between fork and exec */ @@ -3364,7 +3414,7 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to set up pipe for stdout communication with child process: %s", strerror(errno)); - return process_handle->status; + return status; } retval = pipe(stderr_pipe); @@ -3372,7 +3422,7 @@ tor_spawn_background(const char *const filename, const char **argv, log_warn(LD_GENERAL, "Failed to set up pipe for stderr communication with child process: %s", strerror(errno)); - return process_handle->status; + return status; } child_state = CHILD_STATE_MAXFD; @@ -3462,7 +3512,7 @@ tor_spawn_background(const char *const filename, const char **argv, _exit(255); /* Never reached, but avoids compiler warning */ - return process_handle->status; + return status; } /* In parent */ @@ -3473,9 +3523,11 @@ tor_spawn_background(const char *const filename, const char **argv, close(stdout_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); - return process_handle->status; + return status; } + process_handle = process_handle_new(); + process_handle->status = status; process_handle->pid = pid; /* TODO: If the child process forked but failed to exec, waitpid it */ @@ -3499,7 +3551,7 @@ tor_spawn_background(const char *const filename, const char **argv, strerror(errno)); } - process_handle->status = PROCESS_STATUS_RUNNING; + status = process_handle->status = PROCESS_STATUS_RUNNING; /* Set stdout/stderr pipes to be non-blocking */ fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK); fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK); @@ -3507,10 +3559,52 @@ tor_spawn_background(const char *const filename, const char **argv, process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r"); process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r"); + *process_handle_out = process_handle; return process_handle->status; #endif // MS_WINDOWS } +/** Destroy all resources allocated by the process handle in + * <b>process_handle</b>. + * If <b>also_terminate_process</b> is true, also terminate the + * process of the process handle. */ +void +tor_process_handle_destroy(process_handle_t *process_handle, + int also_terminate_process) +{ + if (!process_handle) + return; + + if (also_terminate_process) { + if (tor_terminate_process(process_handle) < 0) { + log_notice(LD_GENERAL, "Failed to terminate process with PID '%d'", + tor_process_get_pid(process_handle)); + } else { + log_info(LD_GENERAL, "Terminated process with PID '%d'", + tor_process_get_pid(process_handle)); + } + } + + process_handle->status = PROCESS_STATUS_NOTRUNNING; + +#ifdef MS_WINDOWS + if (process_handle->stdout_pipe) + CloseHandle(process_handle->stdout_pipe); + + if (process_handle->stderr_pipe) + CloseHandle(process_handle->stderr_pipe); +#else + if (process_handle->stdout_handle) + fclose(process_handle->stdout_handle); + + if (process_handle->stderr_handle) + fclose(process_handle->stderr_handle); +#endif + + memset(process_handle, 0x0f, sizeof(process_handle_t)); + tor_free(process_handle); +} + /** Get the exit code of a process specified by <b>process_handle</b> and store * it in <b>exit_code</b>, if set to a non-NULL value. If <b>block</b> is set * to true, the call will block until the process has exited. Otherwise if @@ -3522,7 +3616,7 @@ tor_spawn_background(const char *const filename, const char **argv, * probably not work in Tor, because waitpid() is called in main.c to reap any * terminated child processes.*/ int -tor_get_exit_code(const process_handle_t process_handle, +tor_get_exit_code(const process_handle_t *process_handle, int block, int *exit_code) { #ifdef MS_WINDOWS @@ -3531,14 +3625,14 @@ tor_get_exit_code(const process_handle_t process_handle, if (block) { /* Wait for the process to exit */ - retval = WaitForSingleObject(process_handle.pid.hProcess, INFINITE); + retval = WaitForSingleObject(process_handle->pid.hProcess, INFINITE); if (retval != WAIT_OBJECT_0) { log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s", (int)retval, format_win32_error(GetLastError())); return PROCESS_EXIT_ERROR; } } else { - retval = WaitForSingleObject(process_handle.pid.hProcess, 0); + retval = WaitForSingleObject(process_handle->pid.hProcess, 0); if (WAIT_TIMEOUT == retval) { /* Process has not exited */ return PROCESS_EXIT_RUNNING; @@ -3550,7 +3644,7 @@ tor_get_exit_code(const process_handle_t process_handle, } if (exit_code != NULL) { - success = GetExitCodeProcess(process_handle.pid.hProcess, + success = GetExitCodeProcess(process_handle->pid.hProcess, (PDWORD)exit_code); if (!success) { log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s", @@ -3562,19 +3656,19 @@ tor_get_exit_code(const process_handle_t process_handle, int stat_loc; int retval; - retval = waitpid(process_handle.pid, &stat_loc, block?0:WNOHANG); + retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG); if (!block && 0 == retval) { /* Process has not exited */ return PROCESS_EXIT_RUNNING; - } else if (retval != process_handle.pid) { - log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", process_handle.pid, - strerror(errno)); + } else if (retval != process_handle->pid) { + log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", + process_handle->pid, strerror(errno)); return PROCESS_EXIT_ERROR; } if (!WIFEXITED(stat_loc)) { log_warn(LD_GENERAL, "Process %d did not exit normally", - process_handle.pid); + process_handle->pid); return PROCESS_EXIT_ERROR; } @@ -3676,7 +3770,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, if (NULL == retval) { if (feof(h)) { log_debug(LD_GENERAL, "fgets() reached end of file"); - fclose(h); if (eof) *eof = 1; break; @@ -3689,7 +3782,6 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, } else { log_warn(LD_GENERAL, "fgets() from handle failed: %s", strerror(errno)); - fclose(h); return -1; } } @@ -3849,12 +3941,10 @@ log_from_pipe(FILE *stream, int severity, const char *executable, r = get_string_from_pipe(stream, buf, sizeof(buf) - 1); if (r == IO_STREAM_CLOSED) { - fclose(stream); return 1; } else if (r == IO_STREAM_EAGAIN) { return 0; } else if (r == IO_STREAM_TERM) { - fclose(stream); return -1; } @@ -3962,7 +4052,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* Static variables are initialized to zero, so child_handle.status=0 * which corresponds to it not running on startup */ - static process_handle_t child_handle; + static process_handle_t *child_handle=NULL; static time_t time_to_run_helper = 0; int stdout_status, stderr_status, retval; @@ -3988,46 +4078,50 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, argv[9] = NULL; /* Start the child, if it is not already running */ - if (child_handle.status != PROCESS_STATUS_RUNNING && + if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) && time_to_run_helper < now) { + int status; + /* Assume tor-fw-helper will succeed, start it later*/ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS; + if (child_handle) { + tor_process_handle_destroy(child_handle, 1); + child_handle = NULL; + } + #ifdef MS_WINDOWS /* Passing NULL as lpApplicationName makes Windows search for the .exe */ - tor_spawn_background(NULL, argv, NULL, &child_handle); + status = tor_spawn_background(NULL, argv, NULL, &child_handle); #else - tor_spawn_background(filename, argv, NULL, &child_handle); + status = tor_spawn_background(filename, argv, NULL, &child_handle); #endif - if (PROCESS_STATUS_ERROR == child_handle.status) { + + if (PROCESS_STATUS_ERROR == status) { log_warn(LD_GENERAL, "Failed to start port forwarding helper %s", filename); time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; return; } -#ifdef MS_WINDOWS - log_info(LD_GENERAL, - "Started port forwarding helper (%s)", filename); -#else + log_info(LD_GENERAL, - "Started port forwarding helper (%s) with pid %d", filename, - child_handle.pid); -#endif + "Started port forwarding helper (%s) with pid '%d'", + filename, tor_process_get_pid(child_handle)); } /* If child is running, read from its stdout and stderr) */ - if (PROCESS_STATUS_RUNNING == child_handle.status) { + if (child_handle && PROCESS_STATUS_RUNNING == child_handle->status) { /* Read from stdout/stderr and log result */ retval = 0; #ifdef MS_WINDOWS - stdout_status = log_from_handle(child_handle.stdout_pipe, LOG_INFO); - stderr_status = log_from_handle(child_handle.stderr_pipe, LOG_WARN); + stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO); + stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN); /* If we got this far (on Windows), the process started */ retval = 0; #else - stdout_status = log_from_pipe(child_handle.stdout_handle, + stdout_status = log_from_pipe(child_handle->stdout_handle, LOG_INFO, filename, &retval); - stderr_status = log_from_pipe(child_handle.stderr_handle, + stderr_status = log_from_pipe(child_handle->stderr_handle, LOG_WARN, filename, &retval); #endif if (retval) { @@ -4040,7 +4134,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* There was a failure */ retval = -1; #ifdef MS_WINDOWS - else if (tor_get_exit_code(child_handle, 0, NULL) != + else if (!child_handle || tor_get_exit_code(child_handle, 0, NULL) != PROCESS_EXIT_RUNNING) { /* process has exited or there was an error */ /* TODO: Do something with the process return value */ @@ -4063,10 +4157,10 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, if (0 != retval) { if (1 == retval) { log_info(LD_GENERAL, "Port forwarding helper terminated"); - child_handle.status = PROCESS_STATUS_NOTRUNNING; + child_handle->status = PROCESS_STATUS_NOTRUNNING; } else { log_warn(LD_GENERAL, "Failed to read from port forwarding helper"); - child_handle.status = PROCESS_STATUS_ERROR; + child_handle->status = PROCESS_STATUS_ERROR; } /* TODO: The child might not actually be finished (maybe it failed or diff --git a/src/common/util.h b/src/common/util.h index 79b641a014..3a68f3993d 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -173,19 +173,15 @@ int n_bits_set_u8(uint8_t v); #define HEX_CHARACTERS "0123456789ABCDEFabcdef" void tor_strlower(char *s) ATTR_NONNULL((1)); void tor_strupper(char *s) ATTR_NONNULL((1)); -int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1)); -int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1)); -int strcmp_opt(const char *s1, const char *s2) ATTR_PURE; -int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2)); -int strcmp_len(const char *s1, const char *s2, size_t len) - ATTR_PURE ATTR_NONNULL((1,2)); -int strcasecmpstart(const char *s1, const char *s2) - ATTR_PURE ATTR_NONNULL((1,2)); -int strcmpend(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2)); -int strcasecmpend(const char *s1, const char *s2) - ATTR_PURE ATTR_NONNULL((1,2)); -int fast_memcmpstart(const void *mem, size_t memlen, - const char *prefix) ATTR_PURE; +int tor_strisprint(const char *s) ATTR_NONNULL((1)); +int tor_strisnonupper(const char *s) ATTR_NONNULL((1)); +int strcmp_opt(const char *s1, const char *s2); +int strcmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcmp_len(const char *s1, const char *s2, size_t len) ATTR_NONNULL((1,2)); +int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2)); +int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix); void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2)); long tor_parse_long(const char *s, int base, long min, @@ -197,19 +193,19 @@ double tor_parse_double(const char *s, double min, double max, int *ok, uint64_t tor_parse_uint64(const char *s, int base, uint64_t min, uint64_t max, int *ok, char **next); const char *hex_str(const char *from, size_t fromlen) ATTR_NONNULL((1)); -const char *eat_whitespace(const char *s) ATTR_PURE; -const char *eat_whitespace_eos(const char *s, const char *eos) ATTR_PURE; -const char *eat_whitespace_no_nl(const char *s) ATTR_PURE; -const char *eat_whitespace_eos_no_nl(const char *s, const char *eos) ATTR_PURE; -const char *find_whitespace(const char *s) ATTR_PURE; -const char *find_whitespace_eos(const char *s, const char *eos) ATTR_PURE; -const char *find_str_at_start_of_line(const char *haystack, const char *needle) - ATTR_PURE; +const char *eat_whitespace(const char *s); +const char *eat_whitespace_eos(const char *s, const char *eos); +const char *eat_whitespace_no_nl(const char *s); +const char *eat_whitespace_eos_no_nl(const char *s, const char *eos); +const char *find_whitespace(const char *s); +const char *find_whitespace_eos(const char *s, const char *eos); +const char *find_str_at_start_of_line(const char *haystack, + const char *needle); int string_is_C_identifier(const char *string); -int tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE; -int tor_digest_is_zero(const char *digest) ATTR_PURE; -int tor_digest256_is_zero(const char *digest) ATTR_PURE; +int tor_mem_is_zero(const char *mem, size_t len); +int tor_digest_is_zero(const char *digest); +int tor_digest256_is_zero(const char *digest); char *esc_for_log(const char *string) ATTR_MALLOC; const char *escaped(const char *string); struct smartlist_t; @@ -352,7 +348,7 @@ const char *parse_config_line_from_str(const char *line, char **key_out, char **value_out); char *expand_filename(const char *filename); struct smartlist_t *tor_listdir(const char *dirname); -int path_is_relative(const char *filename) ATTR_PURE; +int path_is_relative(const char *filename); /* Process helpers */ void start_daemon(void); @@ -363,10 +359,14 @@ void write_pidfile(char *filename); void tor_check_port_forwarding(const char *filename, int dir_port, int or_port, time_t now); -int tor_terminate_process(pid_t pid); -typedef struct process_handle_s process_handle_t; +typedef struct process_handle_t process_handle_t; int tor_spawn_background(const char *const filename, const char **argv, - const char **envp, process_handle_t *process_handle); +#ifdef MS_WINDOWS + LPVOID envp, +#else + const char **envp, +#endif + process_handle_t **process_handle_out); #define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code " @@ -374,16 +374,16 @@ int tor_spawn_background(const char *const filename, const char **argv, HANDLE load_windows_system_library(const TCHAR *library_name); #endif -#ifdef UTIL_PRIVATE -/* Prototypes for private functions only used by util.c (and unit tests) */ - /* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be * 0 because tor_check_port_forwarding depends on this being the initial * statue of the static instance of process_handle_t */ #define PROCESS_STATUS_NOTRUNNING 0 #define PROCESS_STATUS_RUNNING 1 #define PROCESS_STATUS_ERROR -1 -struct process_handle_s { + +#ifdef UTIL_PRIVATE +/*DOCDOC*/ +struct process_handle_t { int status; #ifdef MS_WINDOWS HANDLE stdout_pipe; @@ -397,12 +397,13 @@ struct process_handle_s { pid_t pid; #endif // MS_WINDOWS }; +#endif /* Return values of tor_get_exit_code() */ #define PROCESS_EXIT_RUNNING 1 #define PROCESS_EXIT_EXITED 0 #define PROCESS_EXIT_ERROR -1 -int tor_get_exit_code(const process_handle_t process_handle, +int tor_get_exit_code(const process_handle_t *process_handle, int block, int *exit_code); int tor_split_lines(struct smartlist_t *sl, char *buf, int len); #ifdef MS_WINDOWS @@ -419,6 +420,20 @@ ssize_t tor_read_all_from_process_stderr( const process_handle_t *process_handle, char *buf, size_t count); char *tor_join_win_cmdline(const char *argv[]); +int tor_process_get_pid(process_handle_t *process_handle); +#ifdef MS_WINDOWS +HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle); +#else +FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); +#endif + +int tor_terminate_process(process_handle_t *process_handle); +void tor_process_handle_destroy(process_handle_t *process_handle, + int also_terminate_process); + +#ifdef UTIL_PRIVATE +/* Prototypes for private functions only used by util.c (and unit tests) */ + void format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno); diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 67adf504df..a5682081ae 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -126,8 +126,9 @@ tor_main.o: micro-revision.i micro-revision.i: FORCE @rm -f micro-revision.tmp; \ - if test -d ../../.git && test -x "`which git 2>&1;true`"; then \ - HASH="`git rev-parse --short=16 HEAD`"; \ + if test -d "$(top_srcdir)/.git" && \ + test -x "`which git 2>&1;true`"; then \ + HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ echo \"$$HASH\" > micro-revision.tmp; \ fi; \ if test ! -f micro-revision.tmp ; then \ @@ -141,10 +142,10 @@ micro-revision.i: FORCE or_sha1.i: $(tor_SOURCES) $(libtor_a_SOURCES) if test "@SHA1SUM@" != none; then \ - @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES) | \ + (cd "$(srcdir)" && @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES)) | \ @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \ elif test "@OPENSSL@" != none; then \ - @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES) | \ + (cd "$(srcdir)" && @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES)) | \ @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \ else \ rm or_sha1.i; \ diff --git a/src/or/config.c b/src/or/config.c index 06914b62af..ab991a4f6e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -231,6 +231,7 @@ static config_var_t _option_vars[] = { V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), OBSOLETE("DebugLogFile"), + V(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, NULL), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), V(DirListenAddress, LINELIST, NULL), @@ -246,6 +247,7 @@ static config_var_t _option_vars[] = { V(DirReqStatistics, BOOL, "1"), VAR("DirServer", LINELIST, DirServers, NULL), V(DisableAllSwap, BOOL, "0"), + V(DisableDebuggerAttachment, BOOL, "1"), V(DisableIOCP, BOOL, "1"), V(DynamicDHGroups, BOOL, "1"), V(DNSPort, LINELIST, NULL), @@ -406,6 +408,7 @@ static config_var_t _option_vars[] = { V(UseEntryGuards, BOOL, "1"), V(UseMicrodescriptors, AUTOBOOL, "auto"), V(User, STRING, NULL), + V(UserspaceIOCPBuffers, BOOL, "0"), VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"), VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"), VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), @@ -660,8 +663,12 @@ static const config_format_t state_format = { /** Command-line and config-file options. */ static or_options_t *global_options = NULL; +/** DOCDOC */ +static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; +/** DOCDOC */ +static char *torrc_defaults_fname; /** Persistent serialized state. */ static or_state_t *global_state = NULL; /** Configuration Options set by command line. */ @@ -805,6 +812,8 @@ config_free_all(void) { or_options_free(global_options); global_options = NULL; + or_options_free(global_default_options); + global_default_options = NULL; config_free(&state_format, global_state); global_state = NULL; @@ -820,6 +829,7 @@ config_free_all(void) } tor_free(torrc_fname); + tor_free(torrc_defaults_fname); tor_free(_version); tor_free(global_dirfrontpagecontents); } @@ -1092,13 +1102,22 @@ options_act_reversible(const or_options_t *old_options, char **msg) consider_hibernation(time(NULL)); /* Launch the listeners. (We do this before we setuid, so we can bind to - * ports under 1024.) We don't want to rebind if we're hibernating. */ + * ports under 1024.) We don't want to rebind if we're hibernating. If + * networking is disabled, this will close all but the control listeners, + * but disable those. */ if (!we_are_hibernating()) { if (retry_all_listeners(replaced_listeners, new_listeners) < 0) { *msg = tor_strdup("Failed to bind one of the listener ports."); goto rollback; } } + if (options->DisableNetwork) { + /* Aggressively close non-controller stuff, NOW */ + log_notice(LD_NET, "DisableNetwork is set. Tor will not make or accept " + "non-control network connections. Shutting down all existing " + "connections."); + connection_mark_all_noncontrol_connections(); + } } #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) @@ -1304,6 +1323,14 @@ options_act(const or_options_t *old_options) const int transition_affects_workers = old_options && options_transition_affects_workers(old_options, options); + /* disable ptrace and later, other basic debugging techniques */ + if (options->DisableDebuggerAttachment) { + tor_disable_debugger_attach(); + } else { + log_notice(LD_CONFIG,"Debugger attachment enabled " + "for unprivileged users."); + } + if (running_tor && !have_lockfile()) { if (try_locking(options, 1) < 0) return -1; @@ -1767,7 +1794,11 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) int i = 1; while (i < argc) { + unsigned command = CONFIG_LINE_NORMAL; + int want_arg = 1; + if (!strcmp(argv[i],"-f") || + !strcmp(argv[i],"--defaults-torrc") || !strcmp(argv[i],"--hash-password")) { i += 2; /* command-line option with argument. ignore them. */ continue; @@ -1784,13 +1815,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) continue; } - if (i == argc-1) { - log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", - argv[i]); - config_free_lines(front); - return -1; - } - *new = tor_malloc_zero(sizeof(config_line_t)); s = argv[i]; @@ -1799,15 +1823,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) s++; if (*s == '-') s++; + /* Figure out the command, if any. */ + if (*s == '+') { + s++; + command = CONFIG_LINE_APPEND; + } else if (*s == '/') { + s++; + command = CONFIG_LINE_CLEAR; + /* A 'clear' command has no argument. */ + want_arg = 0; + } + + if (want_arg && i == argc-1) { + log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", + argv[i]); + config_free_lines(front); + return -1; + } (*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1)); - (*new)->value = tor_strdup(argv[i+1]); + (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup(""); + (*new)->command = command; (*new)->next = NULL; log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'", (*new)->key, (*new)->value); new = &((*new)->next); - i += 2; + i += want_arg ? 2 : 1; } *result = front; return 0; @@ -1822,7 +1864,7 @@ config_line_append(config_line_t **lst, { config_line_t *newline; - newline = tor_malloc(sizeof(config_line_t)); + newline = tor_malloc_zero(sizeof(config_line_t)); newline->key = tor_strdup(key); newline->value = tor_strdup(val); newline->next = NULL; @@ -1835,9 +1877,12 @@ config_line_append(config_line_t **lst, /** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Return 0 on success, -1 on failure. Warn and ignore any - * misformatted lines. */ + * misformatted lines. + * + * If <b>extended</b> is set, then treat keys beginning with / and with + as + * indicating "clear" and "append" respectively. */ int -config_get_lines(const char *string, config_line_t **result) +config_get_lines(const char *string, config_line_t **result, int extended) { config_line_t *list = NULL, **next; char *k, *v; @@ -1853,13 +1898,30 @@ config_get_lines(const char *string, config_line_t **result) return -1; } if (k && v) { + unsigned command = CONFIG_LINE_NORMAL; + if (extended) { + if (k[0] == '+') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + command = CONFIG_LINE_APPEND; + } else if (k[0] == '/') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + tor_free(v); + v = tor_strdup(""); + command = CONFIG_LINE_CLEAR; + } + } /* This list can get long, so we keep a pointer to the end of it * rather than using config_line_append over and over and getting * n^2 performance. */ - *next = tor_malloc(sizeof(config_line_t)); + *next = tor_malloc_zero(sizeof(config_line_t)); (*next)->key = k; (*next)->value = v; (*next)->next = NULL; + (*next)->command = command; next = &((*next)->next); } else { tor_free(k); @@ -2087,7 +2149,19 @@ config_assign_value(const config_format_t *fmt, or_options_t *options, case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: - config_line_append((config_line_t**)lvalue, c->key, c->value); + { + config_line_t *lastval = *(config_line_t**)lvalue; + if (lastval && lastval->fragile) { + if (c->command != CONFIG_LINE_APPEND) { + config_free_lines(lastval); + *(config_line_t**)lvalue = NULL; + } else { + lastval->fragile = 0; + } + } + + config_line_append((config_line_t**)lvalue, c->key, c->value); + } break; case CONFIG_TYPE_OBSOLETE: log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); @@ -2103,6 +2177,28 @@ config_assign_value(const config_format_t *fmt, or_options_t *options, return 0; } +/** Mark every linelist in <b>options<b> "fragile", so that fresh assignments + * to it will replace old ones. */ +static void +config_mark_lists_fragile(const config_format_t *fmt, or_options_t *options) +{ + int i; + tor_assert(fmt); + tor_assert(options); + + for (i = 0; fmt->vars[i].name; ++i) { + const config_var_t *var = &fmt->vars[i]; + config_line_t *list; + if (var->type != CONFIG_TYPE_LINELIST && + var->type != CONFIG_TYPE_LINELIST_V) + continue; + + list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset); + if (list) + list->fragile = 1; + } +} + /** If <b>c</b> is a syntactically valid configuration line, update * <b>options</b> with its value and return 0. Otherwise return -1 for bad * key, -2 for bad value. @@ -2145,8 +2241,9 @@ config_assign_line(const config_format_t *fmt, or_options_t *options, if (!strlen(c->value)) { /* reset or clear it, then return */ if (!clear_first) { - if (var->type == CONFIG_TYPE_LINELIST || - var->type == CONFIG_TYPE_LINELIST_S) { + if ((var->type == CONFIG_TYPE_LINELIST || + var->type == CONFIG_TYPE_LINELIST_S) && + c->command != CONFIG_LINE_CLEAR) { /* We got an empty linelist from the torrc or command line. As a special case, call this an error. Warn and ignore. */ log_warn(LD_CONFIG, @@ -2156,6 +2253,8 @@ config_assign_line(const config_format_t *fmt, or_options_t *options, } } return 0; + } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { + option_reset(fmt, options, var, use_defaults); } if (options_seen && (var->type != CONFIG_TYPE_LINELIST && @@ -2250,7 +2349,7 @@ config_lines_dup(const config_line_t *inp) config_line_t *result = NULL; config_line_t **next_out = &result; while (inp) { - *next_out = tor_malloc(sizeof(config_line_t)); + *next_out = tor_malloc_zero(sizeof(config_line_t)); (*next_out)->key = tor_strdup(inp->key); (*next_out)->value = tor_strdup(inp->value); inp = inp->next; @@ -2487,6 +2586,12 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list, list = list->next; } bitarray_free(options_seen); + + /** Now we're done assigning a group of options to the configuration. + * Subsequent group assignments should _replace_ linelists, not extend + * them. */ + config_mark_lists_fragile(fmt, options); + return 0; } @@ -2982,24 +3087,30 @@ config_init(const config_format_t *fmt, void *options) * Else, if comment_defaults, write default values as comments. */ static char * -config_dump(const config_format_t *fmt, const void *options, int minimal, +config_dump(const config_format_t *fmt, const void *default_options, + const void *options, int minimal, int comment_defaults) { smartlist_t *elements; - or_options_t *defaults; + const or_options_t *defaults = default_options; + void *defaults_tmp = NULL; config_line_t *line, *assigned; char *result; int i; char *msg = NULL; - defaults = config_alloc(fmt); - config_init(fmt, defaults); + if (defaults == NULL) { + defaults = defaults_tmp = config_alloc(fmt); + config_init(fmt, defaults_tmp); + } /* XXX use a 1 here so we don't add a new log line while dumping */ - if (fmt->validate_fn(NULL,defaults, 1, &msg) < 0) { - log_err(LD_BUG, "Failed to validate default config."); - tor_free(msg); - tor_assert(0); + if (default_options == NULL) { + if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) { + log_err(LD_BUG, "Failed to validate default config."); + tor_free(msg); + tor_assert(0); + } } elements = smartlist_create(); @@ -3041,7 +3152,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal, result = smartlist_join_strings(elements, "", 0, NULL); SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); smartlist_free(elements); - config_free(fmt, defaults); + if (defaults_tmp) + config_free(fmt, defaults_tmp); return result; } @@ -3052,7 +3164,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal, char * options_dump(const or_options_t *options, int minimal) { - return config_dump(&options_format, options, minimal, 0); + return config_dump(&options_format, global_default_options, + options, minimal, 0); } /** Return 0 if every element of sl is a string holding a decimal @@ -4133,6 +4246,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, old_options->ORPort != new_options->ORPort || old_options->DirPort != new_options->DirPort || old_options->ClientOnly != new_options->ClientOnly || + old_options->DisableNetwork != new_options->DisableNetwork || old_options->_PublishServerDescriptor != new_options->_PublishServerDescriptor || get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || @@ -4206,17 +4320,25 @@ get_windows_conf_root(void) } #endif -/** Return the default location for our torrc file. */ +/** Return the default location for our torrc file. + * DOCDOC defaults_file */ static const char * -get_default_conf_file(void) +get_default_conf_file(int defaults_file) { #ifdef MS_WINDOWS - static char path[MAX_PATH+1]; - strlcpy(path, get_windows_conf_root(), MAX_PATH); - strlcat(path,"\\torrc",MAX_PATH); - return path; + if (defaults_file) { + static char defaults_path[MAX_PATH+1]; + tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults", + get_windows_conf_root()); + return defaults_path; + } else { + static char path[MAX_PATH+1]; + tor_snprintf(path, MAX_PATH, "%s\\torrc", + get_windows_conf_root()); + return path; + } #else - return (CONFDIR "/torrc"); + return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc"; #endif } @@ -4249,37 +4371,46 @@ check_nickname_list(const char *lst, const char *name, char **msg) return r; } -/** Learn config file name from command line arguments, or use the default */ +/** Learn config file name from command line arguments, or use the default, + * DOCDOC defaults_file */ static char * find_torrc_filename(int argc, char **argv, + int defaults_file, int *using_default_torrc, int *ignore_missing_torrc) { char *fname=NULL; int i; + const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f"; + const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc"; + + if (defaults_file) + *ignore_missing_torrc = 1; for (i = 1; i < argc; ++i) { - if (i < argc-1 && !strcmp(argv[i],"-f")) { + if (i < argc-1 && !strcmp(argv[i],fname_opt)) { if (fname) { - log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line."); + log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.", + fname_opt); tor_free(fname); } fname = expand_filename(argv[i+1]); *using_default_torrc = 0; ++i; - } else if (!strcmp(argv[i],"--ignore-missing-torrc")) { + } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) { *ignore_missing_torrc = 1; } } if (*using_default_torrc) { /* didn't find one, try CONFDIR */ - const char *dflt = get_default_conf_file(); + const char *dflt = get_default_conf_file(defaults_file); if (dflt && file_status(dflt) == FN_FILE) { fname = tor_strdup(dflt); } else { #ifndef MS_WINDOWS - char *fn; - fn = expand_filename("~/.torrc"); + char *fn = NULL; + if (!defaults_file) + fn = expand_filename("~/.torrc"); if (fn && file_status(fn) == FN_FILE) { fname = fn; } else { @@ -4294,31 +4425,34 @@ find_torrc_filename(int argc, char **argv, return fname; } -/** Load torrc from disk, setting torrc_fname if successful */ +/** Load torrc from disk, setting torrc_fname if successful. + * DOCDOC defaults_file */ static char * -load_torrc_from_disk(int argc, char **argv) +load_torrc_from_disk(int argc, char **argv, int defaults_file) { char *fname=NULL; char *cf = NULL; int using_default_torrc = 1; int ignore_missing_torrc = 0; + char **fname_var = defaults_file ? &torrc_fname : &torrc_defaults_fname; - fname = find_torrc_filename(argc, argv, + fname = find_torrc_filename(argc, argv, defaults_file, &using_default_torrc, &ignore_missing_torrc); tor_assert(fname); log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname); - tor_free(torrc_fname); - torrc_fname = fname; + tor_free(*fname_var); + *fname_var = fname; /* Open config file */ if (file_status(fname) != FN_FILE || !(cf = read_file_to_str(fname,0,NULL))) { - if (using_default_torrc == 1 || ignore_missing_torrc ) { - log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " - "using reasonable defaults.", fname); + if (using_default_torrc == 1 || ignore_missing_torrc) { + if (!defaults_file) + log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " + "using reasonable defaults.", fname); tor_free(fname); /* sets fname to NULL */ - torrc_fname = NULL; + *fname_var = NULL; cf = tor_strdup(""); } else { log(LOG_WARN, LD_CONFIG, @@ -4332,7 +4466,7 @@ load_torrc_from_disk(int argc, char **argv) return cf; err: tor_free(fname); - torrc_fname = NULL; + *fname_var = NULL; return NULL; } @@ -4343,7 +4477,7 @@ load_torrc_from_disk(int argc, char **argv) int options_init_from_torrc(int argc, char **argv) { - char *cf=NULL; + char *cf=NULL, *cf_defaults=NULL; int i, retval, command; static char **backup_argv; static int backup_argc; @@ -4403,13 +4537,15 @@ options_init_from_torrc(int argc, char **argv) if (command == CMD_HASH_PASSWORD) { cf = tor_strdup(""); } else { - cf = load_torrc_from_disk(argc, argv); + cf_defaults = load_torrc_from_disk(argc, argv, 1); + cf = load_torrc_from_disk(argc, argv, 0); if (!cf) goto err; } - retval = options_init_from_string(cf, command, command_arg, &errmsg); + retval = options_init_from_string(cf_defaults, cf, command, command_arg, &errmsg); tor_free(cf); + tor_free(cf_defaults); if (retval < 0) goto err; @@ -4433,13 +4569,13 @@ options_init_from_torrc(int argc, char **argv) * * -4 for error while setting the new options */ setopt_err_t -options_init_from_string(const char *cf, +options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg) { - or_options_t *oldoptions, *newoptions; + or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL; config_line_t *cl; - int retval; + int retval, i; setopt_err_t err = SETOPT_ERR_MISC; tor_assert(msg); @@ -4452,17 +4588,24 @@ options_init_from_string(const char *cf, newoptions->command = command; newoptions->command_arg = command_arg; - /* get config lines, assign them */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + /* get config lines, assign them */ + retval = config_get_lines(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } /* Go through command-line variables too */ @@ -4497,6 +4640,8 @@ options_init_from_string(const char *cf, /* Clear newoptions and re-initialize them with new defaults. */ config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); + newdefaultoptions = NULL; newoptions = tor_malloc_zero(sizeof(or_options_t)); newoptions->_magic = OR_OPTIONS_MAGIC; options_init(newoptions); @@ -4504,22 +4649,24 @@ options_init_from_string(const char *cf, newoptions->command_arg = command_arg; /* Assign all options a second time. */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, - global_cmdline_options, 0, 0, msg); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + /* get config lines, assign them */ + retval = config_get_lines(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } } @@ -4538,11 +4685,14 @@ options_init_from_string(const char *cf, err = SETOPT_ERR_SETTING; goto err; /* frees and replaces old options */ } + config_free(&options_format, global_default_options); + global_default_options = newdefaultoptions; return SETOPT_OK; err: config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); if (*msg) { char *old_msg = *msg; tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg); @@ -4554,12 +4704,14 @@ options_init_from_string(const char *cf, /** Return the location for our configuration file. */ const char * -get_torrc_fname(void) +get_torrc_fname(int defaults_fname) { - if (torrc_fname) - return torrc_fname; + const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname; + + if (fname) + return fname; else - return get_default_conf_file(); + return get_default_conf_file(defaults_fname); } /** Adjust the address map based on the MapAddress elements in the @@ -5164,7 +5316,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type, * clause once Tor 0.1.2.17 is obsolete. */ log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your " "torrc file (%s), or reinstall Tor and use the default torrc.", - get_torrc_fname()); + get_torrc_fname(0)); goto err; } if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) { @@ -5723,7 +5875,7 @@ options_save_current(void) * If we try falling back to datadirectory or something, we have a better * chance of saving the configuration, but a better chance of doing * something the user never expected. */ - return write_configuration_file(get_torrc_fname(), get_options()); + return write_configuration_file(get_torrc_fname(0), get_options()); } /** Mapping from a unit name to a multiplier for converting that unit into a @@ -6189,7 +6341,7 @@ or_state_load(void) if (contents) { config_line_t *lines=NULL; int assign_retval; - if (config_get_lines(contents, &lines)<0) + if (config_get_lines(contents, &lines, 0)<0) goto done; assign_retval = config_assign(&state_format, new_state, lines, 0, 0, &errmsg); @@ -6293,7 +6445,7 @@ or_state_save(time_t now) tor_free(global_state->TorVersion); tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); - state = config_dump(&state_format, global_state, 1, 0); + state = config_dump(&state_format, NULL, global_state, 1, 0); format_local_iso_time(tbuf, now); tor_asprintf(&contents, "# Tor state file last generated on %s local time\n" diff --git a/src/or/config.h b/src/or/config.h index 76f6841d70..e1fc5cfe9a 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -23,24 +23,24 @@ const char *escaped_safe_str_client(const char *address); const char *escaped_safe_str(const char *address); const char *get_version(void); -int config_get_lines(const char *string, config_line_t **result); +int config_get_lines(const char *string, config_line_t **result, int extended); void config_free_lines(config_line_t *front); setopt_err_t options_trial_assign(config_line_t *list, int use_defaults, int clear_first, char **msg); int resolve_my_address(int warn_severity, const or_options_t *options, uint32_t *addr, char **hostname_out); -int is_local_addr(const tor_addr_t *addr) ATTR_PURE; +int is_local_addr(const tor_addr_t *addr); void options_init(or_options_t *options); char *options_dump(const or_options_t *options, int minimal); int options_init_from_torrc(int argc, char **argv); -setopt_err_t options_init_from_string(const char *cf, +setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg); int option_is_recognized(const char *key); const char *option_get_canonical_name(const char *key); config_line_t *option_get_assignment(const or_options_t *options, const char *key); int options_save_current(void); -const char *get_torrc_fname(void); +const char *get_torrc_fname(int defaults_fname); char *options_get_datadir_fname2_suffix(const or_options_t *options, const char *sub1, const char *sub2, const char *suffix); diff --git a/src/or/connection.c b/src/or/connection.c index a52bf48078..b87f922175 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1318,6 +1318,24 @@ connection_connect(connection_t *conn, const char *address, else protocol_family = PF_INET; + if (get_options()->DisableNetwork) { + /* We should never even try to connect anyplace if DisableNetwork is set. + * Warn if we do, and refuse to make the connection. */ + static ratelim_t disablenet_violated = RATELIM_INIT(30*60); + char *m; +#ifdef MS_WINDOWS + *socket_error = WSAENETUNREACH; +#else + *socket_error = ENETUNREACH; +#endif + if ((m = rate_limit_log(&disablenet_violated, approx_time()))) { + log_warn(LD_BUG, "Tried to open a socket with DisableNetwork set.%s", m); + tor_free(m); + } + tor_fragile_assert(); + return -1; + } + s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP); if (s < 0) { *socket_error = tor_socket_errno(-1); @@ -1968,7 +1986,7 @@ retry_all_listeners(smartlist_t *replaced_conns, smartlist_add(listeners, conn); } SMARTLIST_FOREACH_END(conn); - if (! options->ClientOnly) { + if (! options->ClientOnly && ! options->DisableNetwork) { if (retry_listeners(listeners, CONN_TYPE_OR_LISTENER, options->ORListenAddress, options->ORPort, "0.0.0.0", @@ -1981,10 +1999,13 @@ retry_all_listeners(smartlist_t *replaced_conns, retval = -1; } - if (retry_listener_ports(listeners, - get_configured_client_ports(), - new_conns) < 0) - retval = -1; + if (!options->DisableNetwork) { + if (retry_listener_ports(listeners, + get_configured_client_ports(), + new_conns) < 0) + retval = -1; + } + if (retry_listeners(listeners, CONN_TYPE_CONTROL_LISTENER, options->ControlListenAddress, @@ -2025,6 +2046,43 @@ retry_all_listeners(smartlist_t *replaced_conns, return retval; } +/** Mark every listener of type other than CONTROL_LISTENER to be closed. */ +void +connection_mark_all_noncontrol_listeners(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + if (conn->type == CONN_TYPE_CONTROL_LISTENER) + continue; + if (connection_is_listener(conn)) + connection_mark_for_close(conn); + } SMARTLIST_FOREACH_END(conn); +} + +/** Mark every external conection not used for controllers for close. */ +void +connection_mark_all_noncontrol_connections(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + switch (conn->type) { + case CONN_TYPE_CPUWORKER: + case CONN_TYPE_CONTROL_LISTENER: + case CONN_TYPE_CONTROL: + break; + case CONN_TYPE_AP: + connection_mark_unattached_ap(TO_ENTRY_CONN(conn), + END_STREAM_REASON_HIBERNATING); + break; + default: + connection_mark_for_close(conn); + break; + } + } SMARTLIST_FOREACH_END(conn); +} + /** Return 1 if we should apply rate limiting to <b>conn</b>, and 0 * otherwise. * Right now this just checks if it's an internal IP address or an diff --git a/src/or/connection.h b/src/or/connection.h index 9f11489727..c4b8bf8abe 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -66,6 +66,9 @@ int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, int retry_all_listeners(smartlist_t *replaced_conns, smartlist_t *new_conns); +void connection_mark_all_noncontrol_listeners(void); +void connection_mark_all_noncontrol_connections(void); + ssize_t connection_bucket_write_limit(connection_t *conn, time_t now); int global_write_bucket_low(connection_t *conn, size_t attempt, int priority); void connection_bucket_init(void); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index cce99e4d65..246b08ad77 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -492,6 +492,9 @@ connection_or_about_to_close(or_connection_t *or_conn) time_t now = time(NULL); connection_t *conn = TO_CONN(or_conn); + if (or_conn->pending_action) + tor_cancel_libevent_action(or_conn->pending_action); + /* Remember why we're closing this connection. */ if (conn->state != OR_CONN_STATE_OPEN) { /* Inform any pending (not attached) circs that they should @@ -1146,10 +1149,6 @@ connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn) or_connection_t *conn = _conn; (void)tls; - /* Don't invoke this again. */ - tor_tls_set_renegotiate_callback(tls, NULL, NULL); - tor_tls_block_renegotiation(tls); - if (connection_tls_finish_handshake(conn) < 0) { /* XXXX_TLS double-check that it's ok to do this from inside read. */ /* XXXX_TLS double-check that this verifies certificates. */ @@ -1157,6 +1156,34 @@ connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn) } } +/*DOCDOC*/ +static void +close_connection_libevent_cb(void *_conn) +{ + or_connection_t *or_conn = _conn; + connection_t *conn = TO_CONN(or_conn); + + or_conn->pending_action = NULL; + + connection_stop_reading(conn); + if (!conn->marked_for_close) + connection_mark_for_close(conn); +} + +/* DOCDOC */ +static void +connection_or_close_connection_cb(void *_conn) +{ + /* We can't close their connection from in here since it's an OpenSSL + callback, so we set a libevent event that triggers in the next event + loop and closes the connection. */ + or_connection_t *or_conn = _conn; + if (or_conn->_base.marked_for_close || or_conn->pending_action) + return; + or_conn->pending_action = + tor_run_in_libevent_loop(close_connection_libevent_cb, or_conn); +} + /** Move forward with the tls handshake. If it finishes, hand * <b>conn</b> to connection_tls_finish_handshake(). * @@ -1203,8 +1230,9 @@ connection_tls_continue_handshake(or_connection_t *conn) /* v2/v3 handshake, but not a client. */ log_debug(LD_OR, "Done with initial SSL handshake (server-side). " "Expecting renegotiation or VERSIONS cell"); - tor_tls_set_renegotiate_callback(conn->tls, + tor_tls_set_renegotiate_callbacks(conn->tls, connection_or_tls_renegotiated_cb, + connection_or_close_connection_cb, conn); conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING; connection_stop_writing(TO_CONN(conn)); @@ -1266,8 +1294,9 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, } else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) { /* v2 or v3 handshake, as a server. Only got one handshake, so * wait for the next one. */ - tor_tls_set_renegotiate_callback(conn->tls, + tor_tls_set_renegotiate_callbacks(conn->tls, connection_or_tls_renegotiated_cb, + connection_or_close_connection_cb, conn); conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING; /* return 0; */ @@ -1536,7 +1565,6 @@ connection_tls_finish_handshake(or_connection_t *conn) connection_or_init_conn_from_address(conn, &conn->_base.addr, conn->_base.port, digest_rcvd, 0); } - tor_tls_block_renegotiation(conn->tls); return connection_or_set_state_open(conn); } else { conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V2; diff --git a/src/or/control.c b/src/or/control.c index 109eb8857b..8d924b44f1 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -737,7 +737,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body, SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp)); smartlist_free(entries); - if (config_get_lines(config, &lines) < 0) { + if (config_get_lines(config, &lines, 0) < 0) { log_warn(LD_CONTROL,"Controller gave us config lines we can't parse."); connection_write_str_to_buf("551 Couldn't parse configuration\r\n", conn); @@ -883,7 +883,7 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len, const char *msg = NULL; (void) len; - retval = options_init_from_string(body, CMD_RUN_TOR, NULL, &errstring); + retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring); if (retval != SETOPT_OK) log_warn(LD_CONTROL, @@ -1378,7 +1378,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, if (!strcmp(question, "version")) { *answer = tor_strdup(get_version()); } else if (!strcmp(question, "config-file")) { - *answer = tor_strdup(get_torrc_fname()); + *answer = tor_strdup(get_torrc_fname(0)); + } else if (!strcmp(question, "config-defaults-file")) { + *answer = tor_strdup(get_torrc_fname(1)); } else if (!strcmp(question, "config-text")) { *answer = options_dump(get_options(), 1); } else if (!strcmp(question, "info/names")) { diff --git a/src/or/dirserv.c b/src/or/dirserv.c index be62459b16..8fe1b18a35 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -232,7 +232,7 @@ dirserv_load_fingerprint_file(void) } tor_free(fname); - result = config_get_lines(cf, &front); + result = config_get_lines(cf, &front, 0); tor_free(cf); if (result < 0) { log_warn(LD_CONFIG, "Error reading from fingerprint file"); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index bf34c62af3..01e2358c44 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -50,7 +50,7 @@ static int dirvote_publish_consensus(void); static char *make_consensus_method_list(int low, int high, const char *sep); /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 11 +#define MAX_SUPPORTED_CONSENSUS_METHOD 12 /** Lowest consensus method that contains a 'directory-footer' marker */ #define MIN_METHOD_FOR_FOOTER 9 @@ -64,6 +64,10 @@ static char *make_consensus_method_list(int low, int high, const char *sep); /** Lowest consensus method that generates microdescriptors */ #define MIN_METHOD_FOR_MICRODESC 8 +/** Lowest consensus method that ensures a majority of authorities voted + * for a param. */ +#define MIN_METHOD_FOR_MAJORITY_PARAMS 12 + /* ===== * Voting * =====*/ @@ -608,11 +612,16 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning) return result; } +/** Minimum number of directory authorities voting for a parameter to + * include it in the consensus, if consensus method 12 or later is to be + * used. See proposal 178 for details. */ +#define MIN_VOTES_FOR_PARAM 3 + /** Helper: given a list of valid networkstatus_t, return a new string * containing the contents of the consensus network parameter set. */ /* private */ char * -dirvote_compute_params(smartlist_t *votes) +dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) { int i; int32_t *vals; @@ -669,11 +678,17 @@ dirvote_compute_params(smartlist_t *votes) next_param = smartlist_get(param_list, param_sl_idx+1); if (!next_param || strncmp(next_param, param, cur_param_len)) { /* We've reached the end of a series. */ - int32_t median = median_int32(vals, i); - char *out_string = tor_malloc(64+cur_param_len); - memcpy(out_string, param, cur_param_len); - tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); - smartlist_add(output, out_string); + /* Make sure enough authorities voted on this param, unless the + * the consensus method we use is too old for that. */ + if (method < MIN_METHOD_FOR_MAJORITY_PARAMS || + i > total_authorities/2 || + i >= MIN_VOTES_FOR_PARAM) { + int32_t median = median_int32(vals, i); + char *out_string = tor_malloc(64+cur_param_len); + memcpy(out_string, param, cur_param_len); + tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); + smartlist_add(output, out_string); + } i = 0; if (next_param) { @@ -1496,7 +1511,8 @@ networkstatus_compute_consensus(smartlist_t *votes, } if (consensus_method >= MIN_METHOD_FOR_PARAMS) { - params = dirvote_compute_params(votes); + params = dirvote_compute_params(votes, consensus_method, + total_authorities); if (params) { smartlist_add(chunks, tor_strdup("params ")); smartlist_add(chunks, params); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index d19635173f..1f4dc362b5 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -84,7 +84,8 @@ document_signature_t *voter_get_sig_by_algorithm( #ifdef DIRVOTE_PRIVATE char *format_networkstatus_vote(crypto_pk_env_t *private_key, networkstatus_t *v3_ns); -char *dirvote_compute_params(smartlist_t *votes); +char *dirvote_compute_params(smartlist_t *votes, int method, + int total_authorities); #endif #endif diff --git a/src/or/dns.c b/src/or/dns.c index 8ed9536903..beb110acb2 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -1395,6 +1395,10 @@ launch_resolve(edge_connection_t *exitconn) int r; int options = get_options()->ServerDNSSearchDomains ? 0 : DNS_QUERY_NO_SEARCH; + + if (get_options()->DisableNetwork) + return -1; + /* What? Nameservers not configured? Sounds like a bug. */ if (!nameservers_configured) { log_warn(LD_EXIT, "(Harmless.) Nameservers not configured, but resolve " @@ -1601,6 +1605,9 @@ launch_test_addresses(int fd, short event, void *args) (void)event; (void)args; + if (options->DisableNetwork) + return; + log_info(LD_EXIT, "Launching checks to see whether our nameservers like to " "hijack *everything*."); /* This situation is worse than the failure-hijacking situation. When this diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 6fd2b4f197..ce64581d1c 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -735,7 +735,6 @@ hibernate_soft_limit_reached(void) static void hibernate_begin(hibernate_state_t new_state, time_t now) { - connection_t *conn; const or_options_t *options = get_options(); if (new_state == HIBERNATE_STATE_EXITING && @@ -756,15 +755,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now) } /* close listeners. leave control listener(s). */ - while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) { - log_info(LD_NET,"Closing listener type %d", conn->type); - connection_mark_for_close(conn); - } + connection_mark_all_noncontrol_listeners(); /* XXX kill intro point circs */ /* XXX upload rendezvous service descriptors with no intro points */ diff --git a/src/or/main.c b/src/or/main.c index 95f9958aa8..da45f5a681 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -196,6 +196,26 @@ free_old_inbuf(connection_t *conn) } #endif +#ifdef MS_WINDOWS +/** Remove the kernel-space send and receive buffers for <b>s</b>. For use + * with IOCP only. */ +static int +set_buffer_lengths_to_zero(tor_socket_t s) +{ + int zero = 0; + int r = 0; + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) { + log_warn(LD_NET, "Unable to clear SO_SNDBUF"); + r = -1; + } + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) { + log_warn(LD_NET, "Unable to clear SO_RCVBUF"); + r = -1; + } + return r; +} +#endif + /** Add <b>conn</b> to the array of connections that we can poll on. The * connection's socket must be set; the connection starts out * non-reading and non-writing. @@ -216,6 +236,14 @@ connection_add_impl(connection_t *conn, int is_connecting) #ifdef USE_BUFFEREVENTS if (connection_type_uses_bufferevent(conn)) { if (SOCKET_OK(conn->s) && !conn->linked) { + +#ifdef MS_WINDOWS + if (tor_libevent_using_iocp_bufferevents() && + get_options()->UserspaceIOCPBuffers) { + set_buffer_lengths_to_zero(conn->s); + } +#endif + conn->bufev = bufferevent_socket_new( tor_libevent_get_base(), conn->s, @@ -906,7 +934,7 @@ directory_info_has_arrived(time_t now, int from_cache) update_extrainfo_downloads(now); } - if (server_mode(options) && !we_are_hibernating() && !from_cache && + if (server_mode(options) && !net_is_disabled() && !from_cache && (can_complete_circuit || !any_predicted_circuits(now))) consider_testing_reachability(1, 1); } @@ -1133,11 +1161,11 @@ run_scheduled_events(time_t now) if (router_rebuild_descriptor(1)<0) { log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); } - if (advertised_server_mode()) + if (advertised_server_mode() & !options->DisableNetwork) router_upload_dir_desc_to_dirservers(0); } - if (time_to_try_getting_descriptors < now) { + if (!options->DisableNetwork && time_to_try_getting_descriptors < now) { update_all_descriptor_downloads(now); update_extrainfo_downloads(now); if (router_have_minimum_dir_info()) @@ -1188,7 +1216,7 @@ run_scheduled_events(time_t now) if (time_to_launch_reachability_tests < now && (authdir_mode_tests_reachability(options)) && - !we_are_hibernating()) { + !net_is_disabled()) { time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; /* try to determine reachability of the other Tor relays */ dirserv_test_reachability(now); @@ -1324,7 +1352,7 @@ run_scheduled_events(time_t now) /* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (time_to_check_descriptor < now) { + if (time_to_check_descriptor < now && !options->DisableNetwork) { static int dirport_reachability_count = 0; time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; check_descriptor_bandwidth_changed(now); @@ -1399,7 +1427,7 @@ run_scheduled_events(time_t now) connection_expire_held_open(); /** 3d. And every 60 seconds, we relaunch listeners if any died. */ - if (!we_are_hibernating() && time_to_check_listeners < now) { + if (!net_is_disabled() && time_to_check_listeners < now) { retry_all_listeners(NULL, NULL); time_to_check_listeners = now+60; } @@ -1410,7 +1438,7 @@ run_scheduled_events(time_t now) * and we make a new circ if there are no clean circuits. */ have_dir_info = router_have_minimum_dir_info(); - if (have_dir_info && !we_are_hibernating()) + if (have_dir_info && !net_is_disabled()) circuit_build_needed_circs(now); /* every 10 seconds, but not at the same second as other such events */ @@ -1441,7 +1469,7 @@ run_scheduled_events(time_t now) circuit_close_all_marked(); /** 7. And upload service descriptors if necessary. */ - if (can_complete_circuit && !we_are_hibernating()) { + if (can_complete_circuit && !net_is_disabled()) { rend_consider_services_upload(now); rend_consider_descriptor_republication(); } @@ -1458,7 +1486,8 @@ run_scheduled_events(time_t now) /** 9. and if we're a server, check whether our DNS is telling stories to * us. */ - if (public_server_mode(options) && time_to_check_for_correct_dns < now) { + if (!net_is_disabled() && + public_server_mode(options) && time_to_check_for_correct_dns < now) { if (!time_to_check_for_correct_dns) { time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); } else { @@ -1477,7 +1506,8 @@ run_scheduled_events(time_t now) } /** 11. check the port forwarding app */ - if (time_to_check_port_forwarding < now && + if (!net_is_disabled() && + time_to_check_port_forwarding < now && options->PortForwarding && is_server) { #define PORT_FORWARDING_CHECK_INTERVAL 5 @@ -1489,7 +1519,7 @@ run_scheduled_events(time_t now) } /** 11b. check pending unconfigured managed proxies */ - if (pt_proxies_configuration_pending()) + if (!net_is_disabled() && pt_proxies_configuration_pending()) pt_configure_remaining_proxies(); /** 11c. validate pluggable transports configuration if we need to */ @@ -1561,7 +1591,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) control_event_stream_bandwidth_used(); if (server_mode(options) && - !we_are_hibernating() && + !net_is_disabled() && seconds_elapsed > 0 && can_complete_circuit && stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT != @@ -1762,7 +1792,8 @@ do_hup(void) /* retry appropriate downloads */ router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - update_networkstatus_downloads(time(NULL)); + if (!options->DisableNetwork) + update_networkstatus_downloads(time(NULL)); /* We'll retry routerstatus downloads in about 10 seconds; no need to * force a retry there. */ diff --git a/src/or/or.h b/src/or/or.h index ec49014d94..be7fb413e2 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1272,6 +1272,8 @@ typedef struct or_connection_t { unsigned active_circuit_pqueue_last_recalibrated; struct or_connection_t *next_with_same_id; /**< Next connection with same * identity digest as this one. */ + + tor_libevent_action_t *pending_action; } or_connection_t; /** Subtype of connection_t for an "edge connection" -- that is, an entry (ap) @@ -2836,11 +2838,25 @@ typedef struct port_cfg_t { char unix_addr[FLEXIBLE_ARRAY_MEMBER]; } port_cfg_t; +/** Ordinary configuration line. */ +#define CONFIG_LINE_NORMAL 0 +/** Appends to previous configuration for the same option, even if we + * would ordinary replace it. */ +#define CONFIG_LINE_APPEND 1 +/* Removes all previous configuration for an option. */ +#define CONFIG_LINE_CLEAR 2 + /** A linked list of lines in a config file. */ typedef struct config_line_t { char *key; char *value; struct config_line_t *next; + /** What special treatment (if any) does this line require? */ + unsigned int command:2; + /** If true, subsequent assignments to this linelist should replace + * it, not extend it. Set only on the first item in a linelist in an + * or_options_t. */ + unsigned int fragile:1; } config_line_t; typedef struct routerset_t routerset_t; @@ -3250,6 +3266,8 @@ typedef struct { disclaimer. This allows a server administrator to show that they're running Tor and anyone visiting their server will know this without any specialized knowledge. */ + int DisableDebuggerAttachment; /**< Currently Linux only specific attempt to + disable ptrace; needs BSD testing. */ /** Boolean: if set, we start even if our resolv.conf file is missing * or broken. */ int ServerDNSAllowBrokenConfig; @@ -3434,6 +3452,15 @@ typedef struct { * never use it. If -1, we do what the consensus says. */ int OptimisticData; + /** If 1, and we are using IOCP, we set the kernel socket SNDBUF and RCVBUF + * to 0 to try to save kernel memory and avoid the dread "Out of buffers" + * issue. */ + int UserspaceIOCPBuffers; + + /** If 1, we accept and launch no external network connections, except on + * control ports. */ + int DisableNetwork; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ diff --git a/src/or/router.c b/src/or/router.c index 5d36939a8b..8fe45dd6f8 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -785,7 +785,7 @@ check_whether_dirport_reachable(void) const or_options_t *options = get_options(); return !options->DirPort || options->AssumeReachable || - we_are_hibernating() || + net_is_disabled() || can_reach_dir_port; } @@ -811,7 +811,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) return 0; if (authdir_mode(options)) /* always publish */ return dir_port; - if (we_are_hibernating()) + if (net_is_disabled()) return 0; if (!check_whether_dirport_reachable()) return 0; @@ -979,6 +979,14 @@ router_perform_bandwidth_test(int num_circs, time_t now) } } +/** Return true iff our network is in some sense disabled: either we're + * hibernating, entering hibernation, or */ +int +net_is_disabled(void) +{ + return get_options()->DisableNetwork || we_are_hibernating(); +} + /** Return true iff we believe ourselves to be an authoritative * directory server. */ diff --git a/src/or/router.h b/src/or/router.h index 68eadbf4c2..6a9851cdbd 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -40,6 +40,8 @@ void router_orport_found_reachable(void); void router_dirport_found_reachable(void); void router_perform_bandwidth_test(int num_circs, time_t now); +int net_is_disabled(void); + int authdir_mode(const or_options_t *options); int authdir_mode_v1(const or_options_t *options); int authdir_mode_v2(const or_options_t *options); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index d97b978f43..689df99c57 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -3244,7 +3244,7 @@ router_set_status(const char *digest, int up) log_debug(LD_DIR,"Marking router %s as %s.", node_describe(node), up ? "up" : "down"); #endif - if (!up && node_is_me(node) && !we_are_hibernating()) + if (!up && node_is_me(node) && !net_is_disabled()) log_warn(LD_NET, "We just marked ourself as down. Are your external " "addresses reachable?"); node->is_running = up; @@ -4009,6 +4009,8 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc) void update_all_descriptor_downloads(time_t now) { + if (get_options()->DisableNetwork) + return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); launch_dummy_descriptor_download_as_needed(now, get_options()); @@ -4021,6 +4023,8 @@ routerlist_retry_directory_downloads(time_t now) { router_reset_status_download_failures(); router_reset_descriptor_download_failures(); + if (get_options()->DisableNetwork) + return; update_networkstatus_downloads(now); update_all_descriptor_downloads(now); } diff --git a/src/or/transports.c b/src/or/transports.c index 6e8200f407..10155c4475 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -13,11 +13,18 @@ #include "transports.h" #include "util.h" -static void set_managed_proxy_environment(char ***envp, +#ifdef MS_WINDOWS +static void set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp); +#else +static int set_managed_proxy_environment(char ***envp, + const managed_proxy_t *mp); +#endif + static INLINE int proxy_configuration_finished(const managed_proxy_t *mp); -static void managed_proxy_destroy(managed_proxy_t *mp); +static void managed_proxy_destroy(managed_proxy_t *mp, + int also_terminate_process); static void handle_finished_proxy(managed_proxy_t *mp); static void configure_proxy(managed_proxy_t *mp); @@ -229,12 +236,10 @@ proxy_prepare_for_restart(managed_proxy_t *mp) transport_t *t_tmp = NULL; tor_assert(mp->conf_state == PT_PROTO_COMPLETED); - tor_assert(mp->pid); - /* kill the old obfsproxy process */ - tor_terminate_process(mp->pid); - mp->pid = 0; - fclose(mp->_stdout); + /* destroy the process handle and terminate the process. */ + tor_process_handle_destroy(mp->process_handle, 1); + mp->process_handle = NULL; /* destroy all its old transports. we no longer use them. */ SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) { @@ -256,52 +261,52 @@ proxy_prepare_for_restart(managed_proxy_t *mp) static int launch_managed_proxy(managed_proxy_t *mp) { - (void) mp; - (void) set_managed_proxy_environment; - return -1; -#if 0 - /* XXXX023 we must reenable this code for managed proxies to work. - * "All it needs" is revision to work with the new tor_spawn_background - * API. */ - char **envp=NULL; - int pid; - process_handle_t proc; - FILE *stdout_read = NULL; - int stdout_pipe=-1, stderr_pipe=-1; + int retval; + +#ifdef MS_WINDOWS + + LPVOID envp=NULL; - /* prepare the environment variables for the managed proxy */ set_managed_proxy_environment(&envp, mp); + tor_assert(envp); - pid = tor_spawn_background(mp->argv[0], (const char **)mp->argv, - (const char **)envp, &proc); - if (pid < 0) { - log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.", - mp->argv[0]); + /* Passing NULL as lpApplicationName makes Windows search for the .exe */ + retval = tor_spawn_background(NULL, (const char **)mp->argv, envp, + &mp->process_handle); + + tor_free(envp); + +#else + + char **envp=NULL; + + /* prepare the environment variables for the managed proxy */ + if (set_managed_proxy_environment(&envp, mp) < 0) { + log_warn(LD_GENERAL, "Could not setup the environment of " + "the managed proxy at '%s'.", mp->argv[0]); + free_execve_args(envp); return -1; } + retval = tor_spawn_background(mp->argv[0], (const char **)mp->argv, + (const char **)envp, &mp->process_handle); + /* free the memory allocated by set_managed_proxy_environment(). */ free_execve_args(envp); - /* Set stdout/stderr pipes to be non-blocking */ -#ifdef _WIN32 - { - u_long nonblocking = 1; - ioctlsocket(stdout_pipe, FIONBIO, &nonblocking); - } -#else - fcntl(stdout_pipe, F_SETFL, O_NONBLOCK); #endif - /* Open the buffered IO streams */ - stdout_read = fdopen(stdout_pipe, "r"); + if (retval == PROCESS_STATUS_ERROR) { + log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.", + mp->argv[0]); + return -1; + } - log_info(LD_CONFIG, "Managed proxy has spawned at PID %d.", pid); + log_info(LD_CONFIG, "Managed proxy at '%s' has spawned with PID '%d'.", + mp->argv[0], tor_process_get_pid(mp->process_handle)); mp->conf_state = PT_PROTO_LAUNCHED; - mp->_stdout = stdout_read; - mp->pid = pid; -#endif + return 0; } @@ -314,7 +319,8 @@ pt_configure_remaining_proxies(void) log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!", unconfigured_proxies_n); SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { - tor_assert(mp->conf_state != PT_PROTO_BROKEN); + tor_assert(mp->conf_state != PT_PROTO_BROKEN || + mp->conf_state != PT_PROTO_FAILED_LAUNCH); if (mp->got_hup) { mp->got_hup = 0; @@ -343,6 +349,65 @@ pt_configure_remaining_proxies(void) } SMARTLIST_FOREACH_END(mp); } +#ifdef MS_WINDOWS + +/** Attempt to continue configuring managed proxy <b>mp</b>. */ +static void +configure_proxy(managed_proxy_t *mp) +{ + int pos; + char stdout_buf[200]; + smartlist_t *lines = NULL; + + /* if we haven't launched the proxy yet, do it now */ + if (mp->conf_state == PT_PROTO_INFANT) { + if (launch_managed_proxy(mp) < 0) { /* launch fail */ + mp->conf_state = PT_PROTO_FAILED_LAUNCH; + handle_finished_proxy(mp); + } + return; + } + + tor_assert(mp->conf_state != PT_PROTO_INFANT); + tor_assert(mp->process_handle); + + pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle), + stdout_buf, sizeof(stdout_buf) - 1, NULL); + if (pos < 0) { + log_notice(LD_GENERAL, "Failed to read data from managed proxy"); + mp->conf_state = PT_PROTO_BROKEN; + goto done; + } + + if (pos == 0) /* proxy has nothing interesting to say. */ + return; + + /* End with a null even if there isn't a \r\n at the end */ + /* TODO: What if this is a partial line? */ + stdout_buf[pos] = '\0'; + + /* Split up the buffer */ + lines = smartlist_create(); + tor_split_lines(lines, stdout_buf, pos); + + /* Handle lines. */ + SMARTLIST_FOREACH_BEGIN(lines, const char *, line) { + handle_proxy_line(line, mp); + if (proxy_configuration_finished(mp)) + goto done; + } SMARTLIST_FOREACH_END(line); + + done: + /* if the proxy finished configuring, exit the loop. */ + if (proxy_configuration_finished(mp)) + handle_finished_proxy(mp); + + if (lines) + smartlist_free(lines); +} + +#else /* MS_WINDOWS */ + /** Attempt to continue configuring managed proxy <b>mp</b>. */ static void configure_proxy(managed_proxy_t *mp) @@ -352,15 +417,19 @@ configure_proxy(managed_proxy_t *mp) /* if we haven't launched the proxy yet, do it now */ if (mp->conf_state == PT_PROTO_INFANT) { - launch_managed_proxy(mp); + if (launch_managed_proxy(mp) < 0) { /* launch fail */ + mp->conf_state = PT_PROTO_FAILED_LAUNCH; + handle_finished_proxy(mp); + } return; } tor_assert(mp->conf_state != PT_PROTO_INFANT); + tor_assert(mp->process_handle); while (1) { - r = get_string_from_pipe(mp->_stdout, stdout_buf, - sizeof(stdout_buf) - 1); + r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle), + stdout_buf, sizeof(stdout_buf) - 1); if (r == IO_STREAM_OKAY) { /* got a line; handle it! */ handle_proxy_line((const char *)stdout_buf, mp); @@ -382,6 +451,8 @@ configure_proxy(managed_proxy_t *mp) } } +#endif /* MS_WINDOWS */ + /** Register server managed proxy <b>mp</b> transports to state */ static void register_server_proxy(managed_proxy_t *mp) @@ -394,6 +465,10 @@ register_server_proxy(managed_proxy_t *mp) tor_assert(mp->conf_state != PT_PROTO_COMPLETED); SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { save_transport_to_state(t->name, &t->addr, t->port); + /* LOG_WARN so that the bridge operator can easily find the + transport's port in the log file and send it to the users. */ + log_warn(LD_GENERAL, "Registered server transport '%s' at '%s:%d'", + t->name, fmt_addr(&t->addr), (int)t->port); smartlist_add(sm_tmp, tor_strdup(t->name)); } SMARTLIST_FOREACH_END(t); @@ -453,7 +528,8 @@ register_proxy(managed_proxy_t *mp) /** Free memory allocated by managed proxy <b>mp</b>. */ static void -managed_proxy_destroy(managed_proxy_t *mp) +managed_proxy_destroy(managed_proxy_t *mp, + int also_terminate_process) { if (mp->conf_state != PT_PROTO_COMPLETED) SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); @@ -470,15 +546,11 @@ managed_proxy_destroy(managed_proxy_t *mp) /* remove it from the list of managed proxies */ smartlist_remove(managed_proxy_list, mp); - /* close its stdout stream */ - if (mp->_stdout) - fclose(mp->_stdout); - /* free the argv */ free_execve_args(mp->argv); - if (mp->pid) - tor_terminate_process(mp->pid); + tor_process_handle_destroy(mp->process_handle, also_terminate_process); + mp->process_handle = NULL; tor_free(mp); } @@ -489,7 +561,10 @@ handle_finished_proxy(managed_proxy_t *mp) { switch (mp->conf_state) { case PT_PROTO_BROKEN: /* if broken: */ - managed_proxy_destroy(mp); /* annihilate it. */ + managed_proxy_destroy(mp, 1); /* annihilate it. */ + break; + case PT_PROTO_FAILED_LAUNCH: /* if it failed before launching: */ + managed_proxy_destroy(mp, 0); /* destroy it but don't terminate */ break; case PT_PROTO_CONFIGURED: /* if configured correctly: */ register_proxy(mp); /* register its transports */ @@ -515,7 +590,8 @@ static INLINE int proxy_configuration_finished(const managed_proxy_t *mp) { return (mp->conf_state == PT_PROTO_CONFIGURED || - mp->conf_state == PT_PROTO_BROKEN); + mp->conf_state == PT_PROTO_BROKEN || + mp->conf_state == PT_PROTO_FAILED_LAUNCH); } /** This function is called when a proxy sends an {S,C}METHODS DONE message. */ @@ -537,7 +613,7 @@ handle_methods_done(const managed_proxy_t *mp) void handle_proxy_line(const char *line, managed_proxy_t *mp) { - log_debug(LD_GENERAL, "Got a line from managed proxy: %s\n", line); + log_debug(LD_GENERAL, "Got a line from managed proxy: %s", line); if (strlen(line) < SMALLEST_MANAGED_LINE_SIZE) { log_warn(LD_GENERAL, "Managed proxy configuration line is too small. " @@ -614,14 +690,16 @@ handle_proxy_line(const char *line, managed_proxy_t *mp) return; } else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) { log_warn(LD_GENERAL, "Could not launch managed proxy executable!"); - goto err; + mp->conf_state = PT_PROTO_FAILED_LAUNCH; + return; } log_warn(LD_CONFIG, "Unknown line received by managed proxy. (%s)", line); err: mp->conf_state = PT_PROTO_BROKEN; - return; + log_warn(LD_CONFIG, "Managed proxy at '%s' failed the configuration protocol" + " and will be destroyed.", mp->argv ? mp->argv[0] : ""); } /** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */ @@ -858,8 +936,115 @@ get_bindaddr_for_proxy(const managed_proxy_t *mp) return bindaddr; } -/** Prepare the <b>envp</b> of managed proxy <b>mp</b> */ +#ifdef MS_WINDOWS + +/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>. + * <b>envp</b> is allocated on the heap and should be freed by the + * caller after its use. */ static void +set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp) +{ + const or_options_t *options = get_options(); + extern char **environ; + + LPVOID tmp=NULL; + + char *state_tmp=NULL; + char *state_env=NULL; + char *transports_to_launch=NULL; + char *transports_env=NULL; + char *bindaddr_tmp=NULL; + char *bindaddr_env=NULL; + char *orport_env=NULL; + + char version_env[31]; /* XXX temp */ + char extended_env[43]; /* XXX temp */ + + int env_size = 0; + + /* A smartlist carrying all the env. variables that the managed + proxy should inherit. */ + smartlist_t *envs = smartlist_create(); + + /* Copy the whole environment of the Tor process. + It should also copy PATH and HOME of the Tor process.*/ + char **environ_tmp = environ; + while (*environ_tmp) + smartlist_add(envs, *environ_tmp++); + + /* Create the TOR_PT_* environment variables. */ + state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */ + tor_asprintf(&state_env, "TOR_PT_STATE_LOCATION=%s", state_tmp); + + strcpy(version_env, "TOR_PT_MANAGED_TRANSPORT_VER=1"); + + transports_to_launch = + smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL); + + tor_asprintf(&transports_env, + mp->is_server ? + "TOR_PT_SERVER_TRANSPORTS=%s" : "TOR_PT_CLIENT_TRANSPORTS=%s", + transports_to_launch); + + smartlist_add(envs, state_env); + smartlist_add(envs, version_env); + smartlist_add(envs, transports_env); + + if (mp->is_server) { + tor_asprintf(&orport_env, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort); + + bindaddr_tmp = get_bindaddr_for_proxy(mp); + tor_asprintf(&bindaddr_env, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp); + + strcpy(extended_env, "TOR_PT_EXTENDED_SERVER_PORT=127.0.0.1:4200"); + + smartlist_add(envs, orport_env); + smartlist_add(envs, extended_env); + smartlist_add(envs, bindaddr_env); + } + + /* It seems like some versions of Windows need a sorted lpEnvironment + block. */ + smartlist_sort_strings(envs); + + /* An environment block consists of a null-terminated block of + null-terminated strings: */ + + /* Calculate the block's size. */ + SMARTLIST_FOREACH(envs, const char *, s, + env_size += strlen(s) + 1); + env_size += 1; /* space for last NUL */ + + *envp = tor_malloc(env_size); + tmp = *envp; + + /* Create the block. */ + SMARTLIST_FOREACH_BEGIN(envs, const char *, s) { + memcpy(tmp, s, strlen(s)); /* copy the env. variable string */ + tmp += strlen(s); + memset(tmp, '\0', 1); /* append NUL at the end of the string */ + tmp += 1; + } SMARTLIST_FOREACH_END(s); + memset(tmp, '\0', 1); /* last NUL */ + + /* Finally, free the whole mess. */ + tor_free(state_tmp); + tor_free(state_env); + tor_free(transports_to_launch); + tor_free(transports_env); + tor_free(bindaddr_tmp); + tor_free(bindaddr_env); + tor_free(orport_env); + + smartlist_free(envs); +} + +#else /* MS_WINDOWS */ + +/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>. + * <b>envp</b> is allocated on the heap and should be freed by the + * caller after its use. */ +static int set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) { const or_options_t *options = get_options(); @@ -867,7 +1052,10 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) char *state_loc=NULL; char *transports_to_launch=NULL; char *bindaddr=NULL; + char *home_env=NULL; + char *path_env=NULL; + int r = -1; int n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT; /* allocate enough space for our env. vars and a NULL pointer */ @@ -878,8 +1066,13 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) transports_to_launch = smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL); - tor_asprintf(tmp++, "HOME=%s", getenv("HOME")); - tor_asprintf(tmp++, "PATH=%s", getenv("PATH")); + home_env = getenv("HOME"); + path_env = getenv("PATH"); + if (!home_env || !path_env) + goto done; + + tor_asprintf(tmp++, "HOME=%s", home_env); + tor_asprintf(tmp++, "PATH=%s", path_env); tor_asprintf(tmp++, "TOR_PT_STATE_LOCATION=%s", state_loc); tor_asprintf(tmp++, "TOR_PT_MANAGED_TRANSPORT_VER=1"); /* temp */ if (mp->is_server) { @@ -896,11 +1089,18 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) } *tmp = NULL; + r = 0; + + done: tor_free(state_loc); tor_free(transports_to_launch); tor_free(bindaddr); + + return r; } +#endif /* MS_WINDOWS */ + /** Create and return a new managed proxy for <b>transport</b> using * <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server * managed proxy. */ @@ -995,9 +1195,9 @@ pt_prepare_proxy_list_for_config_read(void) SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { /* Destroy unconfigured proxies. */ if (mp->conf_state != PT_PROTO_COMPLETED) { - managed_proxy_destroy(mp); - unconfigured_proxies_n--; - continue; + managed_proxy_destroy(mp, 1); + unconfigured_proxies_n--; + continue; } tor_assert(mp->conf_state == PT_PROTO_COMPLETED); @@ -1024,7 +1224,7 @@ sweep_proxy_list(void) SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { if (mp->marked_for_removal) { SMARTLIST_DEL_CURRENT(managed_proxy_list, mp); - managed_proxy_destroy(mp); + managed_proxy_destroy(mp, 1); } } SMARTLIST_FOREACH_END(mp); } @@ -1039,7 +1239,7 @@ pt_free_all(void) free them. Otherwise, it hasn't registered its transports yet and we should free them here. */ SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp, - managed_proxy_destroy(mp)); + managed_proxy_destroy(mp, 1)); smartlist_free(managed_proxy_list); managed_proxy_list=NULL; diff --git a/src/or/transports.h b/src/or/transports.h index 4a93387596..314af2b3a0 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -36,7 +36,8 @@ enum pt_proto_state { PT_PROTO_ACCEPTING_METHODS, /* accepting methods */ PT_PROTO_CONFIGURED, /* configured successfully */ PT_PROTO_COMPLETED, /* configure and registered its transports */ - PT_PROTO_BROKEN + PT_PROTO_BROKEN, /* broke during the protocol */ + PT_PROTO_FAILED_LAUNCH /* failed while launching */ }; /** Structure containing information of a managed proxy. */ @@ -47,8 +48,8 @@ typedef struct { int is_server; /* is it a server proxy? */ - FILE *_stdout; /* a stream to its stdout - (closed in managed_proxy_destroy()) */ + /* A pointer to the process handle of this managed proxy. */ + process_handle_t *process_handle; int pid; /* The Process ID this managed proxy is using. */ diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 1b338a29ab..cad8c2f556 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -7,6 +7,7 @@ #define CRYPTO_PRIVATE #include "or.h" #include "test.h" +#include "aes.h" /** Run unit tests for Diffie-Hellman functionality. */ static void @@ -95,13 +96,16 @@ test_crypto_rng(void) /** Run unit tests for our AES functionality */ static void -test_crypto_aes(void) +test_crypto_aes(void *arg) { char *data1 = NULL, *data2 = NULL, *data3 = NULL; crypto_cipher_env_t *env1 = NULL, *env2 = NULL; int i, j; char *mem_op_hex_tmp=NULL; + int use_evp = !strcmp(arg,"evp"); + evaluate_evp_for_aes(use_evp); + data1 = tor_malloc(1024); data2 = tor_malloc(1024); data3 = tor_malloc(1024); @@ -670,7 +674,7 @@ test_crypto_s2k(void) /** Test AES-CTR encryption and decryption with IV. */ static void -test_crypto_aes_iv(void) +test_crypto_aes_iv(void *arg) { crypto_cipher_env_t *cipher; char *plain, *encrypted1, *encrypted2, *decrypted1, *decrypted2; @@ -678,6 +682,9 @@ test_crypto_aes_iv(void) char key1[16], key2[16]; ssize_t encrypted_size, decrypted_size; + int use_evp = !strcmp(arg,"evp"); + evaluate_evp_for_aes(use_evp); + plain = tor_malloc(4095); encrypted1 = tor_malloc(4095 + 1 + 16); encrypted2 = tor_malloc(4095 + 1 + 16); @@ -851,18 +858,36 @@ test_crypto_base32_decode(void) ; } +static void * +pass_data_setup_fn(const struct testcase_t *testcase) +{ + return testcase->setup_data; +} +static int +pass_data_cleanup_fn(const struct testcase_t *testcase, void *ptr) +{ + (void)ptr; + (void)testcase; + return 1; +} +static const struct testcase_setup_t pass_data = { + pass_data_setup_fn, pass_data_cleanup_fn +}; + #define CRYPTO_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_crypto_ ## name } struct testcase_t crypto_tests[] = { CRYPTO_LEGACY(formats), CRYPTO_LEGACY(rng), - CRYPTO_LEGACY(aes), + { "aes_AES", test_crypto_aes, TT_FORK, &pass_data, (void*)"aes" }, + { "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(sha), CRYPTO_LEGACY(pk), CRYPTO_LEGACY(dh), CRYPTO_LEGACY(s2k), - CRYPTO_LEGACY(aes_iv), + { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" }, + { "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(base32_decode), END_OF_TESTCASES }; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 205d7b577c..5b7ce5cabe 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -620,13 +620,81 @@ test_dir_param_voting(void) test_eq(0, networkstatus_get_param(&vote4, "foobar", 0, -100, 8)); smartlist_add(votes, &vote1); + + /* Do the first tests without adding all the other votes, for + * networks without many dirauths. */ + + res = dirvote_compute_params(votes, 11, 6); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 2); + test_streq(res, ""); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 1); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-99"); + tor_free(res); + smartlist_add(votes, &vote2); + + res = dirvote_compute_params(votes, 11, 2); + test_streq(res, "ab=27 abcd=20 cw=5 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 2); + test_streq(res, "ab=27 cw=5 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 3); + test_streq(res, "ab=27 cw=5 x-yz=-99"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 6); + test_streq(res, ""); + tor_free(res); + smartlist_add(votes, &vote3); + + res = dirvote_compute_params(votes, 11, 3); + test_streq(res, "ab=27 abcd=20 c=60 cw=50 x-yz=-9 zzzzz=101"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 3); + test_streq(res, "ab=27 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 5); + test_streq(res, "cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 9); + test_streq(res, "cw=50 x-yz=-9"); + tor_free(res); + smartlist_add(votes, &vote4); - res = dirvote_compute_params(votes); - test_streq(res, - "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101"); + res = dirvote_compute_params(votes, 11, 4); + test_streq(res, "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 4); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 5); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + /* Test that the special-cased "at least three dirauths voted for + * this param" logic works as expected. */ + res = dirvote_compute_params(votes, 12, 6); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); + + res = dirvote_compute_params(votes, 12, 10); + test_streq(res, "ab=90 abcd=20 cw=50 x-yz=-9"); + tor_free(res); done: tor_free(res); @@ -1049,7 +1117,7 @@ test_dir_v3_networkstatus(void) "Running:Stable:V2Dir:Valid"); tor_free(cp); cp = smartlist_join_strings(con->net_params, ":", 0, NULL); - test_streq(cp, "bar=2000000000:circuitwindow=80:foo=660"); + test_streq(cp, "circuitwindow=80:foo=660"); tor_free(cp); test_eq(4, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/ diff --git a/src/test/test_pt.c b/src/test/test_pt.c index f97b21fa0d..45f441106e 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -91,7 +91,7 @@ test_pt_protocol(void) { char line[200]; - managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t)); + managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t)); mp->conf_state = PT_PROTO_LAUNCHED; mp->transports = smartlist_create(); diff --git a/src/test/test_util.c b/src/test/test_util.c index 64bf52e7b1..93f11cd208 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1388,27 +1388,29 @@ run_util_spawn_background(const char *argv[], const char *expected_out, { int retval, exit_code; ssize_t pos; - process_handle_t process_handle; + process_handle_t *process_handle=NULL; char stdout_buf[100], stderr_buf[100]; + int status; /* Start the program */ #ifdef MS_WINDOWS - tor_spawn_background(NULL, argv, NULL, &process_handle); + status = tor_spawn_background(NULL, argv, NULL, &process_handle); #else - tor_spawn_background(argv[0], argv, NULL, &process_handle); + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); #endif - tt_int_op(process_handle.status, ==, expected_status); - - /* If the process failed to start, don't bother continuing */ - if (process_handle.status == PROCESS_STATUS_ERROR) + tt_int_op(status, ==, expected_status); + if (status == PROCESS_STATUS_ERROR) return; - tt_int_op(process_handle.stdout_pipe, >, 0); - tt_int_op(process_handle.stderr_pipe, >, 0); + tt_assert(process_handle != NULL); + tt_int_op(process_handle->status, ==, expected_status); + + tt_int_op(process_handle->stdout_pipe, >, 0); + tt_int_op(process_handle->stderr_pipe, >, 0); /* Check stdout */ - pos = tor_read_all_from_process_stdout(&process_handle, stdout_buf, + pos = tor_read_all_from_process_stdout(process_handle, stdout_buf, sizeof(stdout_buf) - 1); tt_assert(pos >= 0); stdout_buf[pos] = '\0'; @@ -1422,7 +1424,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out, // TODO: Make test-child exit with something other than 0 /* Check stderr */ - pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf, + pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, sizeof(stderr_buf) - 1); tt_assert(pos >= 0); stderr_buf[pos] = '\0'; @@ -1430,7 +1432,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out, tt_int_op(pos, ==, strlen(expected_err)); done: - ; + if (process_handle) + tor_process_handle_destroy(process_handle, 1); } /** Check that we can launch a process and read the output */ @@ -1489,7 +1492,8 @@ test_util_spawn_background_partial_read(void *ptr) int retval, exit_code; ssize_t pos = -1; - process_handle_t process_handle; + process_handle_t *process_handle=NULL; + int status; char stdout_buf[100], stderr_buf[100]; #ifdef MS_WINDOWS const char *argv[] = {"test-child.exe", "--test", NULL}; @@ -1510,21 +1514,23 @@ test_util_spawn_background_partial_read(void *ptr) /* Start the program */ #ifdef MS_WINDOWS - tor_spawn_background(NULL, argv, NULL, &process_handle); + status = tor_spawn_background(NULL, argv, NULL, &process_handle); #else - tor_spawn_background(argv[0], argv, NULL, &process_handle); + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); #endif - tt_int_op(process_handle.status, ==, expected_status); + tt_int_op(status, ==, expected_status); + tt_assert(process_handle); + tt_int_op(process_handle->status, ==, expected_status); /* Check stdout */ - for (expected_out_ctr =0; expected_out[expected_out_ctr] != NULL;) { + for (expected_out_ctr = 0; expected_out[expected_out_ctr] != NULL;) { #ifdef MS_WINDOWS - pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, NULL); #else /* Check that we didn't read the end of file last time */ tt_assert(!eof); - pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, sizeof(stdout_buf) - 1, NULL, &eof); #endif log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos); @@ -1542,16 +1548,16 @@ test_util_spawn_background_partial_read(void *ptr) /* The process should have exited without writing more */ #ifdef MS_WINDOWS - pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, - &process_handle); + process_handle); tt_int_op(pos, ==, 0); #else if (!eof) { /* We should have got all the data, but maybe not the EOF flag */ - pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf, + pos = tor_read_all_handle(process_handle->stdout_handle, stdout_buf, sizeof(stdout_buf) - 1, - &process_handle, &eof); + process_handle, &eof); tt_int_op(pos, ==, 0); tt_assert(eof); } @@ -1566,7 +1572,7 @@ test_util_spawn_background_partial_read(void *ptr) // TODO: Make test-child exit with something other than 0 /* Check stderr */ - pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf, + pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, sizeof(stderr_buf) - 1); tt_assert(pos >= 0); stderr_buf[pos] = '\0'; @@ -1574,7 +1580,7 @@ test_util_spawn_background_partial_read(void *ptr) tt_int_op(pos, ==, strlen(expected_err)); done: - ; + tor_process_handle_destroy(process_handle, 1); } /** diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index 974a58becf..57f82b1bc7 100644 --- a/src/tools/tor-gencert.c +++ b/src/tools/tor-gencert.c @@ -153,7 +153,7 @@ parse_commandline(int argc, char **argv) } months_lifetime = atoi(argv[++i]); if (months_lifetime > 24 || months_lifetime < 0) { - fprintf(stderr, "Lifetime (in months) was out of range."); + fprintf(stderr, "Lifetime (in months) was out of range.\n"); return 1; } } else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--reuse")) { |