diff options
71 files changed, 1745 insertions, 418 deletions
diff --git a/changes/bug19677 b/changes/bug19677 new file mode 100644 index 0000000000..e8ba3dd61a --- /dev/null +++ b/changes/bug19677 @@ -0,0 +1,6 @@ + o Minor bug fixes (option parsing): + - Count unix sockets when counting client listeners (SOCKS, Trans, + NATD, and DNS). This has no user-visible behaviour changes: these + options are set once, and never read. + Required for correct behaviour in ticket #17178. + Fixes bug #19677, patch by teor. diff --git a/changes/bug19767 b/changes/bug19767 new file mode 100644 index 0000000000..f0a010be0c --- /dev/null +++ b/changes/bug19767 @@ -0,0 +1,4 @@ + o Minor bugfixes (compilation): + - Always include orconfig.h before including any other C headers. + Sometimes, it includes macros that affect the behavior of the + standard headers. Closes bug 19767. diff --git a/changes/bug19998 b/changes/bug19998 new file mode 100644 index 0000000000..d01589da03 --- /dev/null +++ b/changes/bug19998 @@ -0,0 +1,6 @@ + o Minor features (security, TLS): + - Servers no longer support clients that do not provide AES + ciphersuites. (3DES is no longer considered an acceptable + cipher.) We believe that no such clients currently exist, + since we have required OpenSSL 0.9.7 or later since 2009. + Closes ticket 19998. diff --git a/changes/bug20063 b/changes/bug20063 new file mode 100644 index 0000000000..aa9ec23d03 --- /dev/null +++ b/changes/bug20063 @@ -0,0 +1,6 @@ + o Minor bugfixes (Linux seccomp2 sandbox): + - Add permission to run the sched_yield() and sigaltstack() system + calls, in order to support versions of Tor compiled with + asan or ubsan code that use these calls. Now "sandbox 1" and + "--enable-expensive-hardening" should be compatible. + Fixes bug 20063; bugfix on 0.2.5.1-alpha. diff --git a/changes/bug20081 b/changes/bug20081 new file mode 100644 index 0000000000..a95161cdc3 --- /dev/null +++ b/changes/bug20081 @@ -0,0 +1,5 @@ + o Minor bugfixes (allocation): + - Change how we allocate memory for large chunks on buffers, to avoid + a (currently impossible) integer overflow, and to waste less space + when allocating unusually large chunks. Fixes bug 20081; bugfix on + 0.2.0.16-alpha. Issue identified by Guido Vranken. diff --git a/changes/bug20141 b/changes/bug20141 new file mode 100644 index 0000000000..99ecf0ad62 --- /dev/null +++ b/changes/bug20141 @@ -0,0 +1,4 @@ + o Minor bugfixes (compilation): + - Fix a syntax error in the IF_BUG_ONCE__() macro in non- + GCC-compatible compilers. Fixes bug 20141; bugfix on + 0.2.9.1-alpha. Patch from Gisle Vanem. diff --git a/changes/feature17178 b/changes/feature17178 new file mode 100644 index 0000000000..172097cd62 --- /dev/null +++ b/changes/feature17178 @@ -0,0 +1,30 @@ + o Major features (onion services): + - Add experimental HiddenServiceSingleHopMode and + HiddenServiceNonAnonymousMode options. When both are set to 1, every + hidden service on a tor instance becomes a non-anonymous Single Onion + Service. Single Onions make one-hop (direct) connections to their + introduction and renzedvous points. One-hop circuits make Single Onion + servers easily locatable, but clients remain location-anonymous. + This is compatible with the existing hidden service implementation, and + works on the current tor network without any changes to older relays or + clients. + Implements proposal #260, completes ticket #17178. Patch by teor & asn. + o Minor features (Tor2web): + - Make Tor2web clients respect ReachableAddresses. + This feature was inadvertently enabled in 0.2.8.6, then removed + by bugfix #19973 on 0.2.8.7. + Implements feature #20034. Patch by teor. + o Minor bug fixes (Tor2web): + - Prevent Tor2web clients running hidden services, these services are + not anonymous due to the one-hop client paths. + Fixes bug #19678. Patch by teor. + o Minor bug fixes (circuits): + - Use CircuitBuildTimeout whenever LearnCircuitBuildTimeout is disabled. + Fixes bug #19678 in commit 5b0b51ca3 in 0.2.4.12-alpha. Patch by teor. + o Minor bug fixes (options): + - Stop changing the configured value of UseEntryGuards on authorities + and Tor2web clients. + Fixes bug #20074 in commits 51fc6799 in tor-0.1.1.16-rc and + acda1735 in tor-0.2.4.3-alpha. Patch by teor. + - Check the consistency of UseEntryGuards and EntryNodes more reliably. + Fixes bug #20074 in commit 686aaa5c in tor-0.2.4.12-alpha. Patch by teor. diff --git a/changes/feature20072 b/changes/feature20072 new file mode 100644 index 0000000000..1afabca5a3 --- /dev/null +++ b/changes/feature20072 @@ -0,0 +1,5 @@ + o Minor feature (testing, hidden services, ipv6): + - Add the single-onion and single-onion-ipv6 chutney targets to + make test-network-all. This requires a recent chutney version + with the single onion network flavours (git c72a652 or later). + Closes ticket 20072; patch by teor. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index f53ff9499f..2e73b2784c 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1199,7 +1199,9 @@ The following options are useful only for clients (that is, if If this option is set to 1, we pick a few long-term entry servers, and try to stick with them. This is desirable because constantly changing servers increases the odds that an adversary who owns some servers will observe a - fraction of your paths. (Default: 1) + fraction of your paths. Entry Guards can not be used by Directory + Authorities, Single Onion Services, and Tor2web clients. In these cases, + the this option is ignored. (Default: 1) [[UseEntryGuardsAsDirGuards]] **UseEntryGuardsAsDirGuards** **0**|**1**:: If this option is set to 1, and UseEntryGuards is also set to 1, @@ -1440,7 +1442,9 @@ The following options are useful only for clients (that is, if non-hidden-service hostnames through Tor. It **must only** be used when running a tor2web Hidden Service web proxy. To enable this option the compile time flag --enable-tor2web-mode must be - specified. (Default: 0) + specified. Since Tor2webMode is non-anonymous, you can not run an + anonymous Hidden Service on a tor version compiled with Tor2webMode. + (Default: 0) [[Tor2webRendezvousPoints]] **Tor2webRendezvousPoints** __node__,__node__,__...__:: A list of identity fingerprints, nicknames, country codes and @@ -2393,6 +2397,37 @@ The following options are used to configure a hidden service. Number of introduction points the hidden service will have. You can't have more than 10. (Default: 3) +[[HiddenServiceSingleHopMode]] **HiddenServiceSingleHopMode** **0**|**1**:: + **Experimental - Non Anonymous** Hidden Services on a tor instance in + HiddenServiceSingleHopMode make one-hop (direct) circuits between the onion + service server, and the introduction and rendezvous points. (Onion service + descriptors are still posted using 3-hop paths, to avoid onion service + directories blocking the service.) + This option makes every hidden service instance hosted by a tor instance a + Single Onion Service. One-hop circuits make Single Onion servers easily + locatable, but clients remain location-anonymous. However, the fact that a + client is accessing a Single Onion rather than a Hidden Service may be + statistically distinguishable. + + **WARNING:** Once a hidden service directory has been used by a tor + instance in HiddenServiceSingleHopMode, it can **NEVER** be used again for + a hidden service. It is best practice to create a new hidden service + directory, key, and address for each new Single Onion Service and Hidden + Service. It is not possible to run Single Onion Services and Hidden + Services from the same tor instance: they should be run on different + servers with different IP addresses. + + HiddenServiceSingleHopMode requires HiddenServiceNonAnonymousMode to be set + to 1. Since a Single Onion is non-anonymous, you can not to run an + anonymous SOCKSPort on the same tor instance as a Single Onion service. + (Default: 0) + +[[HiddenServiceNonAnonymousMode]] **HiddenServiceNonAnonymousMode** **0**|**1**:: + Makes hidden services non-anonymous on this tor instance. Allows the + non-anonymous HiddenServiceSingleHopMode. Enables direct connections in the + server-side hidden service protocol. + (Default: 0) + TESTING NETWORK OPTIONS ----------------------- diff --git a/src/common/address.c b/src/common/address.c index 5cd7a8df67..6799dd6c56 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1603,6 +1603,7 @@ get_interface_addresses_raw,(int severity, sa_family_t family)) return result; #endif (void) severity; + (void) result; return NULL; } diff --git a/src/common/compat.c b/src/common/compat.c index 5385bd871c..3fde2b3089 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1110,8 +1110,8 @@ tor_close_socket_simple(tor_socket_t s) /** As tor_close_socket_simple(), but keeps track of the number * of open sockets. Returns 0 on success, -1 on failure. */ -int -tor_close_socket(tor_socket_t s) +MOCK_IMPL(int, +tor_close_socket,(tor_socket_t s)) { int r = tor_close_socket_simple(s); diff --git a/src/common/compat.h b/src/common/compat.h index 12f280d2e9..ee1c9454de 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -478,7 +478,7 @@ typedef int socklen_t; #endif int tor_close_socket_simple(tor_socket_t s); -int tor_close_socket(tor_socket_t s); +MOCK_DECL(int, tor_close_socket, (tor_socket_t s)); tor_socket_t tor_open_socket_with_extensions( int domain, int type, int protocol, int cloexec, int nonblock); diff --git a/src/common/crypto.c b/src/common/crypto.c index b87023f071..bf682ff330 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -984,6 +984,20 @@ crypto_pk_dup_key(crypto_pk_t *env) return env; } +#ifdef TOR_UNIT_TESTS +/** For testing: replace dest with src. (Dest must have a refcount + * of 1) */ +void +crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src) +{ + tor_assert(dest); + tor_assert(dest->refs == 1); + tor_assert(src); + RSA_free(dest->key); + dest->key = RSAPrivateKey_dup(src->key); +} +#endif + /** Make a real honest-to-goodness copy of <b>env</b>, and return it. * Returns NULL on failure. */ crypto_pk_t * diff --git a/src/common/crypto.h b/src/common/crypto.h index f8fb0daa81..2d1155e205 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -13,6 +13,8 @@ #ifndef TOR_CRYPTO_H #define TOR_CRYPTO_H +#include "orconfig.h" + #include <stdio.h> #include "torint.h" #include "testsupport.h" @@ -326,5 +328,9 @@ extern int break_strongest_rng_fallback; #endif #endif +#ifdef TOR_UNIT_TESTS +void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src); +#endif + #endif diff --git a/src/common/sandbox.c b/src/common/sandbox.c index 37c1a9428f..24ba8a2997 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -184,11 +184,17 @@ static int filter_nopar_gen[] = { SCMP_SYS(read), SCMP_SYS(rt_sigreturn), SCMP_SYS(sched_getaffinity), +#ifdef __NR_sched_yield + SCMP_SYS(sched_yield), +#endif SCMP_SYS(sendmsg), SCMP_SYS(set_robust_list), #ifdef __NR_setrlimit SCMP_SYS(setrlimit), #endif +#ifdef __NR_sigaltstack + SCMP_SYS(sigaltstack), +#endif #ifdef __NR_sigreturn SCMP_SYS(sigreturn), #endif diff --git a/src/common/tortls.c b/src/common/tortls.c index 23889be259..cf3c8ab548 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -552,8 +552,7 @@ MOCK_IMPL(STATIC X509 *, * claiming extra unsupported ciphers in order to avoid fingerprinting. */ #define SERVER_CIPHER_LIST \ (TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \ - TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \ - SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) /** List of ciphers that servers should select from when we actually have * our choice of what cipher to use. */ @@ -593,12 +592,8 @@ static const char UNRESTRICTED_SERVER_CIPHER_LIST[] = /* Required */ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" /* Required */ - TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" -#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA - TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":" -#endif - /* Required */ - SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA; + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA + ; /* Note: to set up your own private testing network with link crypto * disabled, set your Tors' cipher list to diff --git a/src/common/util.c b/src/common/util.c index 211ed7f8d2..259ff8756f 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -4702,13 +4702,13 @@ tor_get_exit_code(process_handle_t *process_handle, 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)); + (int)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); + (int)process_handle->pid); return PROCESS_EXIT_ERROR; } diff --git a/src/common/util_bug.h b/src/common/util_bug.h index 8b69a47607..e02778110b 100644 --- a/src/common/util_bug.h +++ b/src/common/util_bug.h @@ -117,7 +117,7 @@ #else #define IF_BUG_ONCE__(cond,var) \ static int var = 0; \ - if (PREDICT_UNLIKELY(cond)) ? \ + if (PREDICT_UNLIKELY(cond) ? \ (var ? 1 : \ (var=1, \ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \ diff --git a/src/config/torrc.minimal.in-staging b/src/config/torrc.minimal.in-staging index d4dfd5f6bb..5fa37c18f0 100644 --- a/src/config/torrc.minimal.in-staging +++ b/src/config/torrc.minimal.in-staging @@ -99,7 +99,8 @@ ## A handle for your relay, so people don't have to refer to it by key. ## Nicknames must be between 1 and 19 characters inclusive, and must -## contain only the characters [a-zA-Z0-9]. +## contain only the alphanumeric characters (a-z, A-Z, 0-9). No unicode, +## no emoji. #Nickname ididnteditheconfig ## Define these to limit how much relayed traffic you will allow. Your @@ -130,9 +131,12 @@ ## descriptors containing these lines and that Google indexes them, so ## spammers might also collect them. You may want to obscure the fact that ## it's an email address and/or generate a new address for this purpose. +## Notice that "<" and ">" are recommended. #ContactInfo Random Person <nobody AT example dot com> -## You might also include your PGP or GPG fingerprint if you have one: -#ContactInfo 0xFFFFFFFF Random Person <nobody AT example dot com> +## You might also include your PGP or GPG fingerprint if you have one. +## Use the full fingerprint, not just a (short) KeyID: KeyIDs are easy +## to forge. +#ContactInfo FFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFF Random Person <nobody AT example dot com> ## Uncomment this to mirror directory information for others. Please do ## if you have enough bandwidth. @@ -149,11 +153,12 @@ ## distribution for a sample. #DirPortFrontPage @CONFDIR@/tor-exit-notice.html -## Uncomment this if you run more than one Tor relay, and add the identity -## key fingerprint of each Tor relay you control, even if they're on -## different networks. You declare it here so Tor clients can avoid -## using more than one of your relays in a single circuit. See -## https://www.torproject.org/docs/faq#MultipleRelays +## Uncomment this if you run more than one Tor relay, and add the +## identity key fingerprint of each Tor relay you control, even if +## they're on different networks. Include "$" with each key id. You +## declare it here so Tor clients can avoid using more than one of +## your relays in a single circuit. +## See https://www.torproject.org/docs/faq#MultipleRelays ## However, you should never include a bridge's fingerprint here, as it would ## break its concealability and potentially reveal its IP/TCP address. #MyFamily $keyid,$keyid,... @@ -203,4 +208,3 @@ ## a private bridge, for example because you'll give out your bridge ## address manually to your friends, uncomment this line: #PublishServerDescriptor 0 - diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c index 07f6a0f23a..9537ae66a1 100644 --- a/src/ext/ed25519/donna/ed25519_tor.c +++ b/src/ext/ed25519/donna/ed25519_tor.c @@ -34,7 +34,7 @@ #define ED25519_FN2(fn,suffix) ED25519_FN3(fn,suffix) #define ED25519_FN(fn) ED25519_FN2(fn,ED25519_SUFFIX) - +#include "orconfig.h" #include "ed25519-donna.h" #include "ed25519_donna_tor.h" #include "ed25519-randombytes.h" diff --git a/src/ext/ed25519/ref10/keypair.c b/src/ext/ed25519/ref10/keypair.c index 7ddbaa971e..68a88f9adc 100644 --- a/src/ext/ed25519/ref10/keypair.c +++ b/src/ext/ed25519/ref10/keypair.c @@ -1,6 +1,7 @@ /* Modified for Tor: new API, 64-byte secret keys. */ -#include <string.h> + #include "randombytes.h" +#include <string.h> #include "crypto_sign.h" #include "crypto_hash_sha512.h" #include "ge.h" diff --git a/src/ext/ed25519/ref10/open.c b/src/ext/ed25519/ref10/open.c index 9dbeb4cdd0..3ab7b7d6e7 100644 --- a/src/ext/ed25519/ref10/open.c +++ b/src/ext/ed25519/ref10/open.c @@ -1,6 +1,7 @@ /* (Modified by Tor to verify signature separately from message) */ -#include <string.h> + #include "crypto_sign.h" +#include <string.h> #include "crypto_hash_sha512.h" #include "crypto_verify_32.h" #include "ge.h" diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h index dfe5f89e1a..3ffde6e09b 100644 --- a/src/ext/trunnel/trunnel-impl.h +++ b/src/ext/trunnel/trunnel-impl.h @@ -1,4 +1,4 @@ -/* trunnel-impl.h -- copied from Trunnel v1.4.4 +/* trunnel-impl.h -- copied from Trunnel v1.4.6 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ @@ -11,12 +11,12 @@ #ifndef TRUNNEL_IMPL_H_INCLUDED_ #define TRUNNEL_IMPL_H_INCLUDED_ -#include "trunnel.h" -#include <assert.h> -#include <string.h> #ifdef TRUNNEL_LOCAL_H #include "trunnel-local.h" #endif +#include "trunnel.h" +#include <assert.h> +#include <string.h> #if defined(_MSC_VER) && (_MSC_VER < 1600) #define uint8_t unsigned char diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c index 0ed75aa9a4..3994422643 100644 --- a/src/ext/trunnel/trunnel.c +++ b/src/ext/trunnel/trunnel.c @@ -1,4 +1,4 @@ -/* trunnel.c -- copied from Trunnel v1.4.4 +/* trunnel.c -- copied from Trunnel v1.4.6 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ @@ -10,9 +10,9 @@ * See trunnel-impl.h for documentation of these functions. */ +#include "trunnel-impl.h" #include <stdlib.h> #include <string.h> -#include "trunnel-impl.h" #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h index 62e87ee50c..41068b8fb3 100644 --- a/src/ext/trunnel/trunnel.h +++ b/src/ext/trunnel/trunnel.h @@ -1,4 +1,4 @@ -/* trunnel.h -- copied from Trunnel v1.4.4 +/* trunnel.h -- copied from Trunnel v1.4.6 * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/or/buffers.c b/src/or/buffers.c index 3198572392..c08da63a0d 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -166,9 +166,12 @@ chunk_grow(chunk_t *chunk, size_t sz) /** Return the allocation size we'd like to use to hold <b>target</b> * bytes. */ -static inline size_t +STATIC size_t preferred_chunk_size(size_t target) { + tor_assert(target <= SIZE_T_CEILING - CHUNK_HEADER_LEN); + if (CHUNK_ALLOC_SIZE(target) >= MAX_CHUNK_ALLOC) + return CHUNK_ALLOC_SIZE(target); size_t sz = MIN_CHUNK_ALLOC; while (CHUNK_SIZE_WITH_ALLOC(sz) < target) { sz <<= 1; diff --git a/src/or/buffers.h b/src/or/buffers.h index 275867c70a..52b21d5885 100644 --- a/src/or/buffers.h +++ b/src/or/buffers.h @@ -65,6 +65,7 @@ void assert_buf_ok(buf_t *buf); STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); STATIC void buf_pullup(buf_t *buf, size_t bytes); void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz); +STATIC size_t preferred_chunk_size(size_t target); #define DEBUG_CHUNK_ALLOC /** A single chunk on a buffer. */ diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 12c75530e2..cc9b184698 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -28,6 +28,7 @@ #include "connection_edge.h" #include "connection_or.h" #include "control.h" +#include "crypto.h" #include "directory.h" #include "entrynodes.h" #include "main.h" @@ -38,14 +39,14 @@ #include "onion_tap.h" #include "onion_fast.h" #include "policies.h" -#include "transports.h" #include "relay.h" +#include "rendcommon.h" #include "rephist.h" #include "router.h" #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "crypto.h" +#include "transports.h" static channel_t * channel_connect_for_circuit(const tor_addr_t *addr, uint16_t port, @@ -1859,13 +1860,32 @@ pick_rendezvous_node(router_crn_flags_t flags) flags |= CRN_ALLOW_INVALID; #ifdef ENABLE_TOR2WEB_MODE + /* We want to connect directly to the node if we can */ + router_crn_flags_t direct_flags = flags; + direct_flags |= CRN_PREF_ADDR; + direct_flags |= CRN_DIRECT_CONN; + /* The user wants us to pick specific RPs. */ if (options->Tor2webRendezvousPoints) { - const node_t *tor2web_rp = pick_tor2web_rendezvous_node(flags, options); + const node_t *tor2web_rp = pick_tor2web_rendezvous_node(direct_flags, + options); if (tor2web_rp) { return tor2web_rp; } - /* Else, if no tor2web RP was found, fall back to choosing a random node */ + } + + /* Else, if no direct, preferred tor2web RP was found, fall back to choosing + * a random direct node */ + const node_t *node = router_choose_random_node(NULL, options->ExcludeNodes, + direct_flags); + /* Return the direct node (if found), or log a message and fall back to an + * indirect connection. */ + if (node) { + return node; + } else { + log_info(LD_REND, + "Unable to find a random rendezvous point that is reachable via " + "a direct connection, falling back to a 3-hop path."); } #endif @@ -2000,7 +2020,9 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei) cpath_build_state_t *state = circ->build_state; if (state->onehop_tunnel) { - log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel."); + log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel%s.", + (rend_allow_non_anonymous_connection(get_options()) ? + ", or intro or rendezvous connection" : "")); state->desired_path_len = 1; } else { int r = new_route_len(circ->base_.purpose, exit_ei, nodelist_get_list()); diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index b784a140ac..13836cdcfa 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -8,6 +8,8 @@ #define TOR_CIRCUITMUX_EWMA_C_ +#include "orconfig.h" + #include <math.h> #include "or.h" diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index f4db64ebca..3d64113521 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -21,6 +21,8 @@ #include "control.h" #include "main.h" #include "networkstatus.h" +#include "rendclient.h" +#include "rendservice.h" #include "statefile.h" #undef log @@ -81,12 +83,14 @@ get_circuit_build_timeout_ms(void) /** * This function decides if CBT learning should be disabled. It returns - * true if one or more of the following four conditions are met: + * true if one or more of the following conditions are met: * * 1. If the cbtdisabled consensus parameter is set. * 2. If the torrc option LearnCircuitBuildTimeout is false. * 3. If we are a directory authority * 4. If we fail to write circuit build time history to our state file. + * 5. If we are compiled or configured in Tor2web mode + * 6. If we are configured in Single Onion mode */ int circuit_build_times_disabled(void) @@ -94,14 +98,30 @@ circuit_build_times_disabled(void) if (unit_tests) { return 0; } else { + const or_options_t *options = get_options(); int consensus_disabled = networkstatus_get_param(NULL, "cbtdisabled", 0, 0, 1); - int config_disabled = !get_options()->LearnCircuitBuildTimeout; - int dirauth_disabled = get_options()->AuthoritativeDir; + int config_disabled = !options->LearnCircuitBuildTimeout; + int dirauth_disabled = options->AuthoritativeDir; int state_disabled = did_last_state_file_write_fail() ? 1 : 0; + /* LearnCircuitBuildTimeout and Tor2web/Single Onion Services are + * incompatible in two ways: + * + * - LearnCircuitBuildTimeout results in a low CBT, which + * Single Onion use of one-hop intro and rendezvous circuits lowers + * much further, producing *far* too many timeouts. + * + * - The adaptive CBT code does not update its timeout estimate + * using build times for single-hop circuits. + * + * If we fix both of these issues someday, we should test + * these modes with LearnCircuitBuildTimeout on again. */ + int tor2web_disabled = rend_client_allow_non_anonymous_connection(options); + int single_onion_disabled = rend_service_allow_non_anonymous_connection( + options); if (consensus_disabled || config_disabled || dirauth_disabled || - state_disabled) { + state_disabled || tor2web_disabled || single_onion_disabled) { #if 0 log_debug(LD_CIRC, "CircuitBuildTime learning is disabled. " @@ -469,7 +489,7 @@ circuit_build_times_get_initial_timeout(void) */ if (!unit_tests && get_options()->CircuitBuildTimeout) { timeout = get_options()->CircuitBuildTimeout*1000; - if (get_options()->LearnCircuitBuildTimeout && + if (!circuit_build_times_disabled() && timeout < circuit_build_times_min_timeout()) { log_warn(LD_CIRC, "Config CircuitBuildTimeout too low. Setting to %ds", circuit_build_times_min_timeout()/1000); diff --git a/src/or/config.c b/src/or/config.c index 9c5514f1da..b30832d1c4 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -18,6 +18,7 @@ #include "circuitlist.h" #include "circuitmux.h" #include "circuitmux_ewma.h" +#include "circuitstats.h" #include "config.h" #include "connection.h" #include "connection_edge.h" @@ -297,6 +298,8 @@ static config_var_t option_vars_[] = { V(HidServAuth, LINELIST, NULL), V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"), + V(HiddenServiceSingleHopMode, BOOL, "0"), + V(HiddenServiceNonAnonymousMode,BOOL, "0"), V(HTTPProxy, STRING, NULL), V(HTTPProxyAuthenticator, STRING, NULL), V(HTTPSProxy, STRING, NULL), @@ -434,7 +437,7 @@ static config_var_t option_vars_[] = { OBSOLETE("TunnelDirConns"), V(UpdateBridgesFromAuthority, BOOL, "0"), V(UseBridges, BOOL, "0"), - V(UseEntryGuards, BOOL, "1"), + VAR("UseEntryGuards", BOOL, UseEntryGuards_option, "1"), V(UseEntryGuardsAsDirGuards, BOOL, "1"), V(UseGuardFraction, AUTOBOOL, "auto"), V(UseMicrodescriptors, AUTOBOOL, "auto"), @@ -1558,10 +1561,10 @@ options_act(const or_options_t *old_options) if (consider_adding_dir_servers(options, old_options) < 0) return -1; -#ifdef NON_ANONYMOUS_MODE_ENABLED - log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a " - "non-anonymous mode. It will provide NO ANONYMITY."); -#endif + if (rend_non_anonymous_mode_enabled(options)) { + log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run " + "in a non-anonymous mode. It will provide NO ANONYMITY."); + } #ifdef ENABLE_TOR2WEB_MODE /* LCOV_EXCL_START */ @@ -1723,8 +1726,27 @@ options_act(const or_options_t *old_options) monitor_owning_controller_process(options->OwningControllerProcess); + /* We must create new keys after we poison the directories, because our + * poisoning code checks for existing keys, and refuses to modify their + * directories. */ + + /* If we use non-anonymous single onion services, make sure we poison any + new hidden service directories, so that we never accidentally launch the + non-anonymous hidden services thinking they are anonymous. */ + if (running_tor && rend_service_non_anonymous_mode_enabled(options)) { + if (options->RendConfigLines && !num_rend_services()) { + log_warn(LD_BUG,"Error: hidden services configured, but not parsed."); + return -1; + } + if (rend_service_poison_new_single_onion_dirs(NULL) < 0) { + log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous" + "."); + return -1; + } + } + /* reload keys as needed for rendezvous services. */ - if (rend_service_load_all_keys()<0) { + if (rend_service_load_all_keys(NULL)<0) { log_warn(LD_GENERAL,"Error loading rendezvous service keys"); return -1; } @@ -2796,6 +2818,86 @@ warn_about_relative_paths(or_options_t *options) } } +/* Validate options related to single onion services. + * Modifies some options that are incompatible with single onion services. + * On failure returns -1, and sets *msg to an error string. + * Returns 0 on success. */ +STATIC int +options_validate_single_onion(or_options_t *options, char **msg) +{ + /* The two single onion service options must have matching values. */ + if (options->HiddenServiceSingleHopMode && + !options->HiddenServiceNonAnonymousMode) { + REJECT("HiddenServiceSingleHopMode does not provide any server anonymity. " + "It must be used with HiddenServiceNonAnonymousMode set to 1."); + } + if (options->HiddenServiceNonAnonymousMode && + !options->HiddenServiceSingleHopMode) { + REJECT("HiddenServiceNonAnonymousMode does not provide any server " + "anonymity. It must be used with HiddenServiceSingleHopMode set to " + "1."); + } + + /* Now that we've checked that the two options are consistent, we can safely + * call the rend_service_* functions that abstract these options. */ + + /* If you run an anonymous client with an active Single Onion service, the + * client loses anonymity. */ + const int client_port_set = (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->DNSPort_set); + if (rend_service_non_anonymous_mode_enabled(options) && client_port_set && + !options->Tor2webMode) { + REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as " + "an anonymous client. Please set Socks/Trans/NATD/DNSPort to 0, or " + "HiddenServiceNonAnonymousMode to 0, or use the non-anonymous " + "Tor2webMode."); + } + + /* If you run a hidden service in non-anonymous mode, the hidden service + * loses anonymity, even if SOCKSPort / Tor2web mode isn't used. */ + if (!rend_service_non_anonymous_mode_enabled(options) && + options->RendConfigLines && options->Tor2webMode) { + REJECT("Non-anonymous (Tor2web) mode is incompatible with using Tor as a " + "hidden service. Please remove all HiddenServiceDir lines, or use " + "a version of tor compiled without --enable-tor2web-mode, or use " + " HiddenServiceNonAnonymousMode."); + } + + if (rend_service_allow_non_anonymous_connection(options) + && options->UseEntryGuards) { + /* Single Onion services only use entry guards when uploading descriptors, + * all other connections are one-hop. Further, Single Onions causes the + * hidden service code to do things which break the path bias + * detector, and it's far easier to turn off entry guards (and + * thus the path bias detector with it) than to figure out how to + * make path bias compatible with single onions. + */ + log_notice(LD_CONFIG, + "HiddenServiceSingleHopMode is enabled; disabling " + "UseEntryGuards."); + options->UseEntryGuards = 0; + } + + /* Check if existing hidden service keys were created in a different + * single onion service mode, and refuse to launch if they + * have. We'll poison new keys in options_act() just before we create them. + */ + if (rend_service_list_verify_single_onion_poison(NULL, options) < 0) { + log_warn(LD_GENERAL, "We are configured with " + "HiddenServiceNonAnonymousMode %d, but one or more hidden " + "service keys were created in %s mode. This is not allowed.", + rend_service_non_anonymous_mode_enabled(options) ? 1 : 0, + rend_service_non_anonymous_mode_enabled(options) ? + "an anonymous" : "a non-anonymous" + ); + return -1; + } + + return 0; +} + /** Return 0 if every setting in <b>options</b> is reasonable, is a * permissible transition from <b>old_options</b>, and none of the * testing-only settings differ from <b>default_options</b> unless in @@ -2822,6 +2924,12 @@ options_validate(or_options_t *old_options, or_options_t *options, tor_assert(msg); *msg = NULL; + /* Set UseEntryGuards from the configured value, before we check it below. + * We change UseEntryGuards whenn it's incompatible with other options, + * but leave UseEntryGuards_option with the original value. + * Always use the value of UseEntryGuards, not UseEntryGuards_option. */ + options->UseEntryGuards = options->UseEntryGuards_option; + warn_about_relative_paths(options); if (server_mode(options) && @@ -3197,10 +3305,6 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->UseBridges && options->EntryNodes) REJECT("You cannot set both UseBridges and EntryNodes."); - if (options->EntryNodes && !options->UseEntryGuards) { - REJECT("If EntryNodes is set, UseEntryGuards must be enabled."); - } - options->MaxMemInQueues = compute_real_max_mem_in_queues(options->MaxMemInQueues_raw, server_mode(options)); @@ -3291,25 +3395,11 @@ options_validate(or_options_t *old_options, or_options_t *options, options->PredictedPortsRelevanceTime = MAX_PREDICTED_CIRCS_RELEVANCE; } -#ifdef ENABLE_TOR2WEB_MODE - if (options->Tor2webMode && options->LearnCircuitBuildTimeout) { - /* LearnCircuitBuildTimeout and Tor2webMode are incompatible in - * two ways: - * - * - LearnCircuitBuildTimeout results in a low CBT, which - * Tor2webMode's use of one-hop rendezvous circuits lowers - * much further, producing *far* too many timeouts. - * - * - The adaptive CBT code does not update its timeout estimate - * using build times for single-hop circuits. - * - * If we fix both of these issues someday, we should test - * Tor2webMode with LearnCircuitBuildTimeout on again. */ - log_notice(LD_CONFIG,"Tor2webMode is enabled; turning " - "LearnCircuitBuildTimeout off."); - options->LearnCircuitBuildTimeout = 0; - } + /* Check the Single Onion Service options */ + if (options_validate_single_onion(options, msg) < 0) + return -1; +#ifdef ENABLE_TOR2WEB_MODE if (options->Tor2webMode && options->UseEntryGuards) { /* tor2web mode clients do not (and should not) use entry guards * in any meaningful way. Further, tor2web mode causes the hidden @@ -3329,8 +3419,13 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Tor2webRendezvousPoints cannot be set without Tor2webMode."); } + if (options->EntryNodes && !options->UseEntryGuards) { + REJECT("If EntryNodes is set, UseEntryGuards must be enabled."); + } + if (!(options->UseEntryGuards) && - (options->RendConfigLines != NULL)) { + (options->RendConfigLines != NULL) && + !rend_service_allow_non_anonymous_connection(options)) { log_warn(LD_CONFIG, "UseEntryGuards is disabled, but you have configured one or more " "hidden services on this Tor instance. Your hidden services " @@ -3353,6 +3448,17 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } + /* Single Onion Services: non-anonymous hidden services */ + if (rend_service_non_anonymous_mode_enabled(options)) { + log_warn(LD_CONFIG, + "HiddenServiceNonAnonymousMode is set. Every hidden service on " + "this tor instance is NON-ANONYMOUS. If " + "the HiddenServiceNonAnonymousMode option is changed, Tor will " + "refuse to launch hidden services from the same directories, to " + "protect your anonymity against config errors. This setting is " + "for experimental use only."); + } + if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout && options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) { log_warn(LD_CONFIG, @@ -3364,8 +3470,15 @@ options_validate(or_options_t *old_options, or_options_t *options, RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT ); } else if (!options->LearnCircuitBuildTimeout && !options->CircuitBuildTimeout) { - log_notice(LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but didn't " - "a CircuitBuildTimeout. I'll pick a plausible default."); + int severity = LOG_NOTICE; + /* Be a little quieter if we've deliberately disabled + * LearnCircuitBuildTimeout. */ + if (circuit_build_times_disabled()) { + severity = LOG_INFO; + } + log_fn(severity, LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but " + "didn't specify a CircuitBuildTimeout. I'll pick a plausible " + "default."); } if (options->PathBiasNoticeRate > 1.0) { @@ -4295,6 +4408,19 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (old->HiddenServiceSingleHopMode != new_val->HiddenServiceSingleHopMode) { + *msg = tor_strdup("While Tor is running, changing " + "HiddenServiceSingleHopMode is not allowed."); + return -1; + } + + if (old->HiddenServiceNonAnonymousMode != + new_val->HiddenServiceNonAnonymousMode) { + *msg = tor_strdup("While Tor is running, changing " + "HiddenServiceNonAnonymousMode is not allowed."); + return -1; + } + if (old->DisableDebuggerAttachment && !new_val->DisableDebuggerAttachment) { *msg = tor_strdup("While Tor is running, disabling " @@ -6777,14 +6903,17 @@ parse_port_config(smartlist_t *out, } /** Return the number of ports which are actually going to listen with type - * <b>listenertype</b>. Do not count no_listen ports. Do not count unix - * sockets. */ + * <b>listenertype</b>. Do not count no_listen ports. Only count unix + * sockets if count_sockets is true. */ static int -count_real_listeners(const smartlist_t *ports, int listenertype) +count_real_listeners(const smartlist_t *ports, int listenertype, + int count_sockets) { int n = 0; SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { - if (port->server_cfg.no_listen || port->is_unix_addr) + if (port->server_cfg.no_listen) + continue; + if (!count_sockets && port->is_unix_addr) continue; if (port->type != listenertype) continue; @@ -6793,9 +6922,8 @@ count_real_listeners(const smartlist_t *ports, int listenertype) return n; } -/** Parse all client port types (Socks, DNS, Trans, NATD) from - * <b>options</b>. On success, set *<b>n_ports_out</b> to the number - * of ports that are listed, update the *Port_set values in +/** Parse all ports from <b>options</b>. On success, set *<b>n_ports_out</b> + * to the number of ports that are listed, update the *Port_set values in * <b>options</b>, and return 0. On failure, set *<b>msg</b> to a * description of the problem and return -1. * @@ -6921,21 +7049,22 @@ parse_ports(or_options_t *options, int validate_only, /* Update the *Port_set options. The !! here is to force a boolean out of an integer. */ options->ORPort_set = - !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER, 0); options->SocksPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER, 1); options->TransPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1); options->NATDPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1); + /* Use options->ControlSocket to test if a control socket is set */ options->ControlPort_set = - !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0); options->DirPort_set = - !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER, 0); options->DNSPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER, 1); options->ExtORPort_set = - !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER, 0); if (world_writable_control_socket) { SMARTLIST_FOREACH(ports, port_cfg_t *, p, diff --git a/src/or/config.h b/src/or/config.h index 7db66a31b9..208659acb7 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -166,6 +166,8 @@ extern struct config_format_t options_format; STATIC port_cfg_t *port_cfg_new(size_t namelen); STATIC void port_cfg_free(port_cfg_t *port); STATIC void or_options_free(or_options_t *options); +STATIC int options_validate_single_onion(or_options_t *options, + char **msg); STATIC int options_validate(or_options_t *old_options, or_options_t *options, or_options_t *default_options, diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 4d615e8e2b..a41dcd5360 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -27,6 +27,7 @@ #include "control.h" #include "dns.h" #include "dnsserv.h" +#include "directory.h" #include "dirserv.h" #include "hibernate.h" #include "main.h" @@ -2326,6 +2327,7 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn) char payload[CELL_PAYLOAD_SIZE]; int payload_len; int begin_type; + const or_options_t *options = get_options(); origin_circuit_t *circ; edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn); connection_t *base_conn = TO_CONN(edge_conn); @@ -2369,10 +2371,31 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn) begin_type = ap_conn->use_begindir ? RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN; + + /* Check that circuits are anonymised, based on their type. */ if (begin_type == RELAY_COMMAND_BEGIN) { -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(circ->build_state->onehop_tunnel == 0); -#endif + /* This connection is a standard OR connection. + * Make sure its path length is anonymous, or that we're in a + * non-anonymous mode. */ + assert_circ_anonymity_ok(circ, options); + } else if (begin_type == RELAY_COMMAND_BEGIN_DIR) { + /* This connection is a begindir directory connection. + * Look at the linked directory connection to access the directory purpose. + * (This must be non-NULL, because we're doing begindir.) */ + tor_assert(base_conn->linked); + connection_t *linked_dir_conn_base = base_conn->linked_conn; + tor_assert(linked_dir_conn_base); + /* Sensitive directory connections must have an anonymous path length. + * Otherwise, directory connections are typically one-hop. + * This matches the earlier check for directory connection path anonymity + * in directory_initiate_command_rend(). */ + if (is_sensitive_dir_purpose(linked_dir_conn_base->purpose)) { + assert_circ_anonymity_ok(circ, options); + } + } else { + /* This code was written for the two connection types BEGIN and BEGIN_DIR + */ + tor_assert_unreached(); } if (connection_edge_send_command(edge_conn, begin_type, diff --git a/src/or/control.c b/src/or/control.c index 1337af4201..8962075e1e 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4249,6 +4249,8 @@ handle_control_add_onion(control_connection_t *conn, int max_streams = 0; int max_streams_close_circuit = 0; rend_auth_type_t auth_type = REND_NO_AUTH; + /* Default to adding an anonymous hidden service if no flag is given */ + int non_anonymous = 0; for (size_t i = 1; i < arg_len; i++) { static const char *port_prefix = "Port="; static const char *flags_prefix = "Flags="; @@ -4285,11 +4287,16 @@ handle_control_add_onion(control_connection_t *conn, * * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is * exceeded. * * 'BasicAuth' - Client authorization using the 'basic' method. + * * 'NonAnonymous' - Add a non-anonymous Single Onion Service. If this + * flag is present, tor must be in non-anonymous + * hidden service mode. If this flag is absent, + * tor must be in anonymous hidden service mode. */ static const char *discard_flag = "DiscardPK"; static const char *detach_flag = "Detach"; static const char *max_s_close_flag = "MaxStreamsCloseCircuit"; static const char *basicauth_flag = "BasicAuth"; + static const char *non_anonymous_flag = "NonAnonymous"; smartlist_t *flags = smartlist_new(); int bad = 0; @@ -4310,6 +4317,8 @@ handle_control_add_onion(control_connection_t *conn, max_streams_close_circuit = 1; } else if (!strcasecmp(flag, basicauth_flag)) { auth_type = REND_BASIC_AUTH; + } else if (!strcasecmp(flag, non_anonymous_flag)) { + non_anonymous = 1; } else { connection_printf_to_buf(conn, "512 Invalid 'Flags' argument: %s\r\n", @@ -4378,6 +4387,19 @@ handle_control_add_onion(control_connection_t *conn, smartlist_len(auth_clients) > 16)) { connection_printf_to_buf(conn, "512 Too many auth clients\r\n"); goto out; + } else if (non_anonymous != rend_service_non_anonymous_mode_enabled( + get_options())) { + /* If we failed, and the non-anonymous flag is set, Tor must be in + * anonymous hidden service mode. + * The error message changes based on the current Tor config: + * 512 Tor is in anonymous hidden service mode + * 512 Tor is in non-anonymous hidden service mode + * (I've deliberately written them out in full here to aid searchability.) + */ + connection_printf_to_buf(conn, "512 Tor is in %sanonymous hidden service " + "mode\r\n", + non_anonymous ? "" : "non-"); + goto out; } /* Parse the "keytype:keyblob" argument. */ diff --git a/src/or/directory.c b/src/or/directory.c index d37b5c2e0f..52b14b9bae 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -1085,7 +1085,7 @@ directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port, * <b>dir_purpose</b> reveals sensitive information about a Tor * instance's client activities. (Such connections must be performed * through normal three-hop Tor circuits.) */ -static int +int is_sensitive_dir_purpose(uint8_t dir_purpose) { return ((dir_purpose == DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2) || @@ -1140,12 +1140,10 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port, log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose)); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(is_sensitive_dir_purpose(dir_purpose) && - !anonymized_connection)); -#else - (void)is_sensitive_dir_purpose; -#endif + if (is_sensitive_dir_purpose(dir_purpose)) { + tor_assert(anonymized_connection || + rend_non_anonymous_mode_enabled(options)); + } /* use encrypted begindir connections for everything except relays * this provides better protection for directory fetches */ diff --git a/src/or/directory.h b/src/or/directory.h index f04e7ab315..9477948aa0 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -132,7 +132,10 @@ int download_status_get_n_failures(const download_status_t *dls); int download_status_get_n_attempts(const download_status_t *dls); time_t download_status_get_next_attempt_at(const download_status_t *dls); +/* Yes, these two functions are confusingly similar. + * Let's sort that out in #20077. */ int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose); +int is_sensitive_dir_purpose(uint8_t dir_purpose); #ifdef TOR_UNIT_TESTS /* Used only by directory.c and test_dir.c */ diff --git a/src/or/main.c b/src/or/main.c index 03c2b7ed58..7ac1c1c4f2 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2833,11 +2833,6 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } -#ifdef NON_ANONYMOUS_MODE_ENABLED - log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a " - "non-anonymous mode. It will provide NO ANONYMITY."); -#endif - if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); return -1; @@ -2849,15 +2844,18 @@ tor_init(int argc, char *argv[]) return -1; } + /* The options are now initialised */ + const or_options_t *options = get_options(); + #ifndef _WIN32 if (geteuid()==0) log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, " "and you probably shouldn't."); #endif - if (crypto_global_init(get_options()->HardwareAccel, - get_options()->AccelName, - get_options()->AccelDir)) { + if (crypto_global_init(options->HardwareAccel, + options->AccelName, + options->AccelDir)) { log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting."); return -1; } diff --git a/src/or/or.h b/src/or/or.h index 5b9b007ac1..88e06fcaaf 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3606,9 +3606,13 @@ typedef struct { /** @name port booleans * - * Derived booleans: True iff there is a non-listener port on an AF_INET or - * AF_INET6 address of the given type configured in one of the _lines - * options above. + * Derived booleans: For server ports and ControlPort, true iff there is a + * non-listener port on an AF_INET or AF_INET6 address of the given type + * configured in one of the _lines options above. + * For client ports, also true if there is a unix socket configured. + * If you are checking for client ports, you may want to use: + * SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set + * rather than SocksPort_set. * * @{ */ @@ -3699,6 +3703,26 @@ typedef struct { * they reach the normal circuit-build timeout. */ int CloseHSServiceRendCircuitsImmediatelyOnTimeout; + /** Onion Services in HiddenServiceSingleHopMode make one-hop (direct) + * circuits between the onion service server, and the introduction and + * rendezvous points. (Onion service descriptors are still posted using + * 3-hop paths, to avoid onion service directories blocking the service.) + * This option makes every hidden service instance hosted by + * this tor instance a Single Onion Service. + * HiddenServiceSingleHopMode requires HiddenServiceNonAnonymousMode to be + * set to 1. + * Use rend_service_allow_non_anonymous_connection() or + * rend_service_reveal_startup_time() instead of using this option directly. + */ + int HiddenServiceSingleHopMode; + /* Makes hidden service clients and servers non-anonymous on this tor + * instance. Allows the non-anonymous HiddenServiceSingleHopMode. Enables + * non-anonymous behaviour in the hidden service protocol. + * Use rend_service_non_anonymous_mode_enabled() instead of using this option + * directly. + */ + int HiddenServiceNonAnonymousMode; + int ConnLimit; /**< Demanded minimum number of simultaneous connections. */ int ConnLimit_; /**< Maximum allowed number of simultaneous connections. */ int ConnLimit_high_thresh; /**< start trying to lower socket usage if we @@ -3754,7 +3778,8 @@ typedef struct { * unattached before we fail it? */ int LearnCircuitBuildTimeout; /**< If non-zero, we attempt to learn a value * for CircuitBuildTimeout based on timeout - * history */ + * history. Use circuit_build_times_disabled() + * rather than checking this value directly. */ int CircuitBuildTimeout; /**< Cull non-open circuits that were born at * least this many seconds ago. Used until * adaptive algorithm learns a new value. */ @@ -3940,8 +3965,16 @@ typedef struct { int TokenBucketRefillInterval; char *AccelName; /**< Optional hardware acceleration engine name. */ char *AccelDir; /**< Optional hardware acceleration engine search dir. */ - int UseEntryGuards; /**< Boolean: Do we try to enter from a smallish number - * of fixed nodes? */ + + /** Boolean: Do we try to enter from a smallish number + * of fixed nodes? */ + int UseEntryGuards_option; + /** Internal variable to remember whether we're actually acting on + * UseEntryGuards_option -- when we're a non-anonymous Tor2web client or + * Single Onion Service, it is alwasy false, otherwise we use the value of + * UseEntryGuards_option. */ + int UseEntryGuards; + int NumEntryGuards; /**< How many entry guards do we try to establish? */ int UseEntryGuardsAsDirGuards; /** Boolean: Do we try to get directory info * from a smallish number of fixed nodes? */ @@ -5054,7 +5087,8 @@ typedef struct rend_encoded_v2_service_descriptor_t { * the service side) and in rend_service_descriptor_t (on both the * client and service side). */ typedef struct rend_intro_point_t { - extend_info_t *extend_info; /**< Extend info of this introduction point. */ + extend_info_t *extend_info; /**< Extend info for connecting to this + * introduction point via a multi-hop path. */ crypto_pk_t *intro_key; /**< Introduction key that replaces the service * key, if this descriptor is V2. */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 263dd3d876..a93bc94a9c 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -134,6 +134,7 @@ int rend_client_send_introduction(origin_circuit_t *introcirc, origin_circuit_t *rendcirc) { + const or_options_t *options = get_options(); size_t payload_len; int r, v3_shift = 0; char payload[RELAY_PAYLOAD_SIZE]; @@ -150,10 +151,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc, tor_assert(rendcirc->rend_data); tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address, rendcirc->rend_data->onion_address)); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(introcirc->build_state->onehop_tunnel)); - tor_assert(!(rendcirc->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(introcirc, options); + assert_circ_anonymity_ok(rendcirc, options); r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, &entry); @@ -387,6 +386,7 @@ int rend_client_introduction_acked(origin_circuit_t *circ, const uint8_t *request, size_t request_len) { + const or_options_t *options = get_options(); origin_circuit_t *rendcirc; (void) request; // XXXX Use this. @@ -398,10 +398,9 @@ rend_client_introduction_acked(origin_circuit_t *circ, return -1; } + tor_assert(circ->build_state); tor_assert(circ->build_state->chosen_exit); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circ->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circ, options); tor_assert(circ->rend_data); /* For path bias: This circuit was used successfully. Valid @@ -416,9 +415,7 @@ rend_client_introduction_acked(origin_circuit_t *circ, log_info(LD_REND,"Received ack. Telling rend circ..."); rendcirc = circuit_get_ready_rend_circ_by_rend_data(circ->rend_data); if (rendcirc) { /* remember the ack */ -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(rendcirc->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(rendcirc, options); circuit_change_purpose(TO_CIRCUIT(rendcirc), CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); /* Set timestamp_dirty, because circuit_expire_building expects @@ -1527,3 +1524,35 @@ rend_parse_service_authorization(const or_options_t *options, return res; } +/* Can Tor client code make direct (non-anonymous) connections to introduction + * or rendezvous points? + * Returns true if tor was compiled with NON_ANONYMOUS_MODE_ENABLED, and is + * configured in Tor2web mode. */ +int +rend_client_allow_non_anonymous_connection(const or_options_t *options) +{ + /* Tor2web support needs to be compiled in to a tor binary. */ +#ifdef NON_ANONYMOUS_MODE_ENABLED + /* Tor2web */ + return options->Tor2webMode ? 1 : 0; +#else + (void)options; + return 0; +#endif +} + +/* At compile-time, was non-anonymous mode enabled via + * NON_ANONYMOUS_MODE_ENABLED ? */ +int +rend_client_non_anonymous_mode_enabled(const or_options_t *options) +{ + (void)options; + /* Tor2web support needs to be compiled in to a tor binary. */ +#ifdef NON_ANONYMOUS_MODE_ENABLED + /* Tor2web */ + return 1; +#else + return 0; +#endif +} + diff --git a/src/or/rendclient.h b/src/or/rendclient.h index e90dac07ab..b8f8c2f871 100644 --- a/src/or/rendclient.h +++ b/src/or/rendclient.h @@ -51,5 +51,8 @@ rend_service_authorization_t *rend_client_lookup_service_authorization( const char *onion_address); void rend_service_authorization_free_all(void); +int rend_client_allow_non_anonymous_connection(const or_options_t *options); +int rend_client_non_anonymous_mode_enabled(const or_options_t *options); + #endif diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 01b0766cf0..d9d39b1f19 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -1067,3 +1067,52 @@ rend_auth_decode_cookie(const char *cookie_in, uint8_t *cookie_out, return res; } +/* Is this a rend client or server that allows direct (non-anonymous) + * connections? + * Clients must be specifically compiled and configured in this mode. + * Onion services can be configured to start in this mode. + * Prefer rend_client_allow_non_anonymous_connection() or + * rend_service_allow_non_anonymous_connection() whenever possible, so that + * checks are specific to Single Onion Services or Tor2web. */ +int +rend_allow_non_anonymous_connection(const or_options_t* options) +{ + return (rend_client_allow_non_anonymous_connection(options) + || rend_service_allow_non_anonymous_connection(options)); +} + +/* Is this a rend client or server in non-anonymous mode? + * Clients must be specifically compiled in this mode. + * Onion services can be configured to start in this mode. + * Prefer rend_client_non_anonymous_mode_enabled() or + * rend_service_non_anonymous_mode_enabled() whenever possible, so that checks + * are specific to Single Onion Services or Tor2web. */ +int +rend_non_anonymous_mode_enabled(const or_options_t *options) +{ + return (rend_client_non_anonymous_mode_enabled(options) + || rend_service_non_anonymous_mode_enabled(options)); +} + +/* Make sure that tor only builds one-hop circuits when they would not + * compromise user anonymity. + * + * One-hop circuits are permitted in Tor2web or Single Onion modes. + * + * Tor2web or Single Onion modes are also allowed to make multi-hop circuits. + * For example, single onion HSDir circuits are 3-hop to prevent denial of + * service. + */ +void +assert_circ_anonymity_ok(origin_circuit_t *circ, + const or_options_t *options) +{ + tor_assert(options); + tor_assert(circ); + tor_assert(circ->build_state); + + if (circ->build_state->onehop_tunnel) { + tor_assert(rend_allow_non_anonymous_connection(options)); + } +} + diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index 88cf512f4a..090e6f25e0 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -77,5 +77,11 @@ int rend_auth_decode_cookie(const char *cookie_in, rend_auth_type_t *auth_type_out, char **err_msg_out); +int rend_allow_non_anonymous_connection(const or_options_t* options); +int rend_non_anonymous_mode_enabled(const or_options_t *options); + +void assert_circ_anonymity_ok(origin_circuit_t *circ, + const or_options_t *options); + #endif diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 8d3a7d704c..114a56b74e 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -20,6 +20,7 @@ #include "main.h" #include "networkstatus.h" #include "nodelist.h" +#include "policies.h" #include "rendclient.h" #include "rendcommon.h" #include "rendservice.h" @@ -107,59 +108,13 @@ struct rend_service_port_config_s { * rendezvous point before giving up? */ #define MAX_REND_TIMEOUT 30 -/** Represents a single hidden service running at this OP. */ -typedef struct rend_service_t { - /* Fields specified in config file */ - char *directory; /**< where in the filesystem it stores it. Will be NULL if - * this service is ephemeral. */ - int dir_group_readable; /**< if 1, allow group read - permissions on directory */ - smartlist_t *ports; /**< List of rend_service_port_config_t */ - rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client - * authorization is performed. */ - smartlist_t *clients; /**< List of rend_authorized_client_t's of - * clients that may access our service. Can be NULL - * if no client authorization is performed. */ - /* Other fields */ - crypto_pk_t *private_key; /**< Permanent hidden-service key. */ - char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without - * '.onion' */ - char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */ - smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have, - * or are trying to establish. */ - /** List of rend_intro_point_t that are expiring. They are removed once - * the new descriptor is successfully uploaded. A node in this list CAN - * NOT appear in the intro_nodes list. */ - smartlist_t *expiring_nodes; - time_t intro_period_started; /**< Start of the current period to build - * introduction points. */ - int n_intro_circuits_launched; /**< Count of intro circuits we have - * established in this period. */ - unsigned int n_intro_points_wanted; /**< Number of intro points this - * service wants to have open. */ - rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */ - time_t desc_is_dirty; /**< Time at which changes to the hidden service - * descriptor content occurred, or 0 if it's - * up-to-date. */ - time_t next_upload_time; /**< Scheduled next hidden service descriptor - * upload time. */ - /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to - * detect repeats. Clients may send INTRODUCE1 cells for the same - * rendezvous point through two or more different introduction points; - * when they do, this keeps us from launching multiple simultaneous attempts - * to connect to the same rend point. */ - replaycache_t *accepted_intro_dh_parts; - /** If true, we don't close circuits for making requests to unsupported - * ports. */ - int allow_unknown_ports; - /** The maximum number of simultanious streams-per-circuit that are allowed - * to be established, or 0 if no limit is set. - */ - int max_streams_per_circuit; - /** If true, we close circuits that exceed the max_streams_per_circuit - * limit. */ - int max_streams_close_circuit; -} rend_service_t; +/* Hidden service directory file names: + * new file names should be added to rend_service_add_filenames_to_list() + * for sandboxing purposes. */ +static const char *private_key_fname = "private_key"; +static const char *hostname_fname = "hostname"; +static const char *client_keys_fname = "client_keys"; +static const char *sos_poison_fname = "onion_service_non_anonymous"; /** Returns a escaped string representation of the service, <b>s</b>. */ @@ -206,16 +161,18 @@ rend_authorized_client_strmap_item_free(void *authorized_client) /** Release the storage held by <b>service</b>. */ -static void +STATIC void rend_service_free(rend_service_t *service) { if (!service) return; tor_free(service->directory); - SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p, - rend_service_port_config_free(p)); - smartlist_free(service->ports); + if (service->ports) { + SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p, + rend_service_port_config_free(p)); + smartlist_free(service->ports); + } if (service->private_key) crypto_pk_free(service->private_key); if (service->intro_nodes) { @@ -1001,13 +958,235 @@ rend_service_update_descriptor(rend_service_t *service) } } +/* Allocate and return a string containing the path to file_name in + * service->directory. Asserts that service has a directory. + * This function will never return NULL. + * The caller must free this path. */ +static char * +rend_service_path(const rend_service_t *service, const char *file_name) +{ + char *file_path = NULL; + + tor_assert(service->directory); + + /* Can never fail: asserts rather than leaving file_path NULL. */ + tor_asprintf(&file_path, "%s%s%s", + service->directory, PATH_SEPARATOR, file_name); + + return file_path; +} + +/* Allocate and return a string containing the path to the single onion + * service poison file in service->directory. Asserts that service has a + * directory. + * The caller must free this path. */ +STATIC char * +rend_service_sos_poison_path(const rend_service_t *service) +{ + return rend_service_path(service, sos_poison_fname); +} + +/** Return True if hidden services <b>service> has been poisoned by single + * onion mode. */ +static int +service_is_single_onion_poisoned(const rend_service_t *service) +{ + char *poison_fname = NULL; + file_status_t fstatus; + + if (!service->directory) { + return 0; + } + + poison_fname = rend_service_sos_poison_path(service); + + fstatus = file_status(poison_fname); + tor_free(poison_fname); + + /* If this fname is occupied, the hidden service has been poisoned. */ + if (fstatus == FN_FILE || fstatus == FN_EMPTY) { + return 1; + } + + return 0; +} + +/* Return 1 if the private key file for service exists and has a non-zero size, + * and 0 otherwise. */ +static int +rend_service_private_key_exists(const rend_service_t *service) +{ + char *private_key_path = rend_service_path(service, private_key_fname); + const file_status_t private_key_status = file_status(private_key_path); + tor_free(private_key_path); + /* Only non-empty regular private key files could have been used before. */ + return private_key_status == FN_FILE; +} + +/** Check the single onion service poison state of all existing hidden service + * directories: + * - If each service is poisoned, and we are in Single Onion Mode, + * return 0, + * - If each service is not poisoned, and we are not in Single Onion Mode, + * return 0, + * - Otherwise, the poison state is invalid, and a service that was created in + * one mode is being used in the other, return -1. + * Hidden service directories without keys are not checked for consistency. + * When their keys are created, they will be poisoned (if needed). + * If a <b>service_list</b> is provided, treat it + * as the list of hidden services (used in unittests). */ +int +rend_service_list_verify_single_onion_poison(const smartlist_t *service_list, + const or_options_t *options) +{ + const smartlist_t *s_list; + /* If no special service list is provided, then just use the global one. */ + if (!service_list) { + if (!rend_service_list) { /* No global HS list. Nothing to see here. */ + return 0; + } + + s_list = rend_service_list; + } else { + s_list = service_list; + } + + int consistent = 1; + SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) { + if (service_is_single_onion_poisoned(s) != + rend_service_non_anonymous_mode_enabled(options) && + rend_service_private_key_exists(s)) { + consistent = 0; + } + } SMARTLIST_FOREACH_END(s); + + return consistent ? 0 : -1; +} + +/*** Helper for rend_service_poison_new_single_onion_dirs(). Add a file to + * this hidden service directory that marks it as a single onion service. + * Tor must be in single onion mode before calling this function. + * Returns 0 when a directory is successfully poisoned, or if it is already + * poisoned. Returns -1 on a failure to read the directory or write the poison + * file, or if there is an existing private key file in the directory. (The + * service should have been poisoned when the key was created.) */ +static int +poison_new_single_onion_hidden_service_dir(const rend_service_t *service) +{ + /* We must only poison directories if we're in Single Onion mode */ + tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + + int fd; + int retval = -1; + char *poison_fname = NULL; + + if (!service->directory) { + log_info(LD_REND, "Ephemeral HS started in non-anonymous mode."); + return 0; + } + + /* Make sure we're only poisoning new hidden service directories */ + if (rend_service_private_key_exists(service)) { + log_warn(LD_BUG, "Tried to single onion poison a service directory after " + "the private key was created."); + return -1; + } + + poison_fname = rend_service_sos_poison_path(service); + + switch (file_status(poison_fname)) { + case FN_DIR: + case FN_ERROR: + log_warn(LD_FS, "Can't read single onion poison file \"%s\"", + poison_fname); + goto done; + case FN_FILE: /* single onion poison file already exists. NOP. */ + case FN_EMPTY: /* single onion poison file already exists. NOP. */ + log_debug(LD_FS, "Tried to re-poison a single onion poisoned file \"%s\"", + poison_fname); + break; + case FN_NOENT: + fd = tor_open_cloexec(poison_fname, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd < 0) { + log_warn(LD_FS, "Could not create single onion poison file %s", + poison_fname); + goto done; + } + close(fd); + break; + default: + tor_assert(0); + } + + retval = 0; + + done: + tor_free(poison_fname); + + return retval; +} + +/** We just got launched in Single Onion Mode. That's a non-anoymous + * mode for hidden services; hence we should mark all new hidden service + * directories appropriately so that they are never launched as + * location-private hidden services again. (New directories don't have private + * key files.) + * If a <b>service_list</b> is provided, treat it as the list of hidden + * services (used in unittests). + * Return 0 on success, -1 on fail. */ +int +rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list) +{ + /* We must only poison directories if we're in Single Onion mode */ + tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + + const smartlist_t *s_list; + /* If no special service list is provided, then just use the global one. */ + if (!service_list) { + if (!rend_service_list) { /* No global HS list. Nothing to see here. */ + return 0; + } + + s_list = rend_service_list; + } else { + s_list = service_list; + } + + SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) { + if (!rend_service_private_key_exists(s)) { + if (poison_new_single_onion_hidden_service_dir(s) < 0) { + return -1; + } + } + } SMARTLIST_FOREACH_END(s); + + /* The keys for these services are linked to the server IP address */ + log_notice(LD_REND, "The configured onion service directories have been " + "used in single onion mode. They can not be used for anonymous " + "hidden services."); + + return 0; +} + /** Load and/or generate private keys for all hidden services, possibly - * including keys for client authorization. Return 0 on success, -1 on - * failure. */ + * including keys for client authorization. + * If a <b>service_list</b> is provided, treat it as the list of hidden + * services (used in unittests). Otherwise, require that rend_service_list is + * not NULL. + * Return 0 on success, -1 on failure. */ int -rend_service_load_all_keys(void) +rend_service_load_all_keys(const smartlist_t *service_list) { - SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) { + const smartlist_t *s_list; + /* If no special service list is provided, then just use the global one. */ + if (!service_list) { + tor_assert(rend_service_list); + s_list = rend_service_list; + } else { + s_list = service_list; + } + + SMARTLIST_FOREACH_BEGIN(s_list, rend_service_t *, s) { if (s->private_key) continue; log_info(LD_REND, "Loading hidden-service keys from \"%s\"", @@ -1027,12 +1206,10 @@ rend_service_add_filenames_to_list(smartlist_t *lst, const rend_service_t *s) tor_assert(lst); tor_assert(s); tor_assert(s->directory); - smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"private_key", - s->directory); - smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"hostname", - s->directory); - smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"client_keys", - s->directory); + smartlist_add(lst, rend_service_path(s, private_key_fname)); + smartlist_add(lst, rend_service_path(s, hostname_fname)); + smartlist_add(lst, rend_service_path(s, client_keys_fname)); + smartlist_add(lst, rend_service_sos_poison_path(s)); } /** Add to <b>open_lst</b> every filename used by a configured hidden service, @@ -1076,7 +1253,7 @@ rend_service_derive_key_digests(struct rend_service_t *s) static int rend_service_load_keys(rend_service_t *s) { - char fname[512]; + char *fname = NULL; char buf[128]; cpd_check_t check_opts = CPD_CREATE; @@ -1085,7 +1262,7 @@ rend_service_load_keys(rend_service_t *s) } /* Check/create directory */ if (check_private_dir(s->directory, check_opts, get_options()->User) < 0) { - return -1; + goto err; } #ifndef _WIN32 if (s->dir_group_readable) { @@ -1097,34 +1274,23 @@ rend_service_load_keys(rend_service_t *s) #endif /* Load key */ - if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || - strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname)) - >= sizeof(fname)) { - log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".", - s->directory); - return -1; - } + fname = rend_service_path(s, private_key_fname); s->private_key = init_key_from_file(fname, 1, LOG_ERR, 0); + if (!s->private_key) - return -1; + goto err; if (rend_service_derive_key_digests(s) < 0) - return -1; + goto err; + tor_free(fname); /* Create service file */ - if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || - strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname)) - >= sizeof(fname)) { - log_warn(LD_CONFIG, "Directory name too long to store hostname file:" - " \"%s\".", s->directory); - return -1; - } + fname = rend_service_path(s, hostname_fname); tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id); if (write_str_to_file(fname,buf,0)<0) { log_warn(LD_CONFIG, "Could not write onion address to hostname file."); - memwipe(buf, 0, sizeof(buf)); - return -1; + goto err; } #ifndef _WIN32 if (s->dir_group_readable) { @@ -1135,15 +1301,21 @@ rend_service_load_keys(rend_service_t *s) } #endif - memwipe(buf, 0, sizeof(buf)); - /* If client authorization is configured, load or generate keys. */ if (s->auth_type != REND_NO_AUTH) { - if (rend_service_load_auth_keys(s, fname) < 0) - return -1; + if (rend_service_load_auth_keys(s, fname) < 0) { + goto err; + } } - return 0; + int r = 0; + goto done; + err: + r = -1; + done: + memwipe(buf, 0, sizeof(buf)); + tor_free(fname); + return r; } /** Load and/or generate client authorization keys for the hidden service @@ -1153,7 +1325,7 @@ static int rend_service_load_auth_keys(rend_service_t *s, const char *hfname) { int r = 0; - char cfname[512]; + char *cfname = NULL; char *client_keys_str = NULL; strmap_t *parsed_clients = strmap_new(); FILE *cfile, *hfile; @@ -1163,12 +1335,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) char buf[1500]; /* Load client keys and descriptor cookies, if available. */ - if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys", - s->directory)<0) { - log_warn(LD_CONFIG, "Directory name too long to store client keys " - "file: \"%s\".", s->directory); - goto err; - } + cfname = rend_service_path(s, client_keys_fname); client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL); if (client_keys_str) { if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) { @@ -1322,7 +1489,10 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) } strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); - memwipe(cfname, 0, sizeof(cfname)); + if (cfname) { + memwipe(cfname, 0, strlen(cfname)); + tor_free(cfname); + } /* Clear stack buffers that held key-derived material. */ memwipe(buf, 0, sizeof(buf)); @@ -1424,6 +1594,31 @@ rend_check_authorization(rend_service_t *service, return 1; } +/* Can this service make a direct connection to ei? + * It must be a single onion service, and the firewall rules must allow ei. */ +static int +rend_service_use_direct_connection(const or_options_t* options, + const extend_info_t* ei) +{ + /* We'll connect directly all reachable addresses, whether preferred or not. + * The prefer_ipv6 argument to fascist_firewall_allows_address_addr is + * ignored, because pref_only is 0. */ + return (rend_service_allow_non_anonymous_connection(options) && + fascist_firewall_allows_address_addr(&ei->addr, ei->port, + FIREWALL_OR_CONNECTION, 0, 0)); +} + +/* Like rend_service_use_direct_connection, but to a node. */ +static int +rend_service_use_direct_connection_node(const or_options_t* options, + const node_t* node) +{ + /* We'll connect directly all reachable addresses, whether preferred or not. + */ + return (rend_service_allow_non_anonymous_connection(options) && + fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)); +} + /****** * Handle cells ******/ @@ -1473,9 +1668,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit, goto err; } -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, options); tor_assert(circuit->rend_data); /* We'll use this in a bazillion log messages */ @@ -1679,6 +1872,11 @@ rend_service_receive_introduction(origin_circuit_t *circuit, for (i=0;i<MAX_REND_FAILURES;i++) { int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL; if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; + /* A Single Onion Service only uses a direct connection if its + * firewall rules permit direct connections to the address. */ + if (rend_service_use_direct_connection(options, rp)) { + flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL; + } launched = circuit_launch_by_extend_info( CIRCUIT_PURPOSE_S_CONNECT_REND, rp, flags); @@ -1791,7 +1989,10 @@ find_rp_for_intro(const rend_intro_cell_t *intro, goto err; } - rp = extend_info_from_node(node, 0); + /* Are we in single onion mode? */ + const int allow_direct = rend_service_allow_non_anonymous_connection( + get_options()); + rp = extend_info_from_node(node, allow_direct); if (!rp) { if (err_msg_out) { tor_asprintf(&err_msg, @@ -1816,6 +2017,10 @@ find_rp_for_intro(const rend_intro_cell_t *intro, goto err; } + /* rp is always set here: extend_info_dup guarantees a non-NULL result, and + * the other cases goto err. */ + tor_assert(rp); + /* Make sure the RP we are being asked to connect to is _not_ a private * address unless it's allowed. Let's avoid to build a circuit to our * second middle node and fail right after when extending to the RP. */ @@ -2590,6 +2795,10 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc) log_info(LD_REND,"Reattempting rendezvous circuit to '%s'", safe_str(extend_info_describe(oldstate->chosen_exit))); + /* You'd think Single Onion Services would want to retry the rendezvous + * using a direct connection. But if it's blocked by a firewall, or the + * service is IPv6-only, or the rend point avoiding becoming a one-hop + * proxy, we need a 3-hop connection. */ newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND, oldstate->chosen_exit, CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); @@ -2618,26 +2827,72 @@ rend_service_launch_establish_intro(rend_service_t *service, rend_intro_point_t *intro) { origin_circuit_t *launched; + int flags = CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL; + const or_options_t *options = get_options(); + extend_info_t *launch_ei = intro->extend_info; + extend_info_t *direct_ei = NULL; + + /* Are we in single onion mode? */ + if (rend_service_allow_non_anonymous_connection(options)) { + /* Do we have a descriptor for the node? + * We've either just chosen it from the consensus, or we've just reviewed + * our intro points to see which ones are still valid, and deleted the ones + * that aren't in the consensus any more. */ + const node_t *node = node_get_by_id(launch_ei->identity_digest); + if (BUG(!node)) { + /* The service has kept an intro point after it went missing from the + * consensus. If we did anything else here, it would be a consensus + * distinguisher. Which are less of an issue for single onion services, + * but still a bug. */ + return -1; + } + /* Can we connect to the node directly? If so, replace launch_ei + * (a multi-hop extend_info) with one suitable for direct connection. */ + if (rend_service_use_direct_connection_node(options, node)) { + direct_ei = extend_info_from_node(node, 1); + if (BUG(!direct_ei)) { + /* rend_service_use_direct_connection_node and extend_info_from_node + * disagree about which addresses on this node are permitted. This + * should never happen. Avoiding the connection is a safe response. */ + return -1; + } + flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL; + launch_ei = direct_ei; + } + } + /* launch_ei is either intro->extend_info, or has been replaced with a valid + * extend_info for single onion service direct connection. */ + tor_assert(launch_ei); + /* We must have the same intro when making a direct connection. */ + tor_assert(tor_memeq(intro->extend_info->identity_digest, + launch_ei->identity_digest, + DIGEST_LEN)); log_info(LD_REND, - "Launching circuit to introduction point %s for service %s", + "Launching circuit to introduction point %s%s%s for service %s", safe_str_client(extend_info_describe(intro->extend_info)), + direct_ei ? " via direct address " : "", + direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : "", service->service_id); rep_hist_note_used_internal(time(NULL), 1, 0); ++service->n_intro_circuits_launched; launched = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, - intro->extend_info, - CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL); + launch_ei, flags); if (!launched) { log_info(LD_REND, - "Can't launch circuit to establish introduction at %s.", - safe_str_client(extend_info_describe(intro->extend_info))); + "Can't launch circuit to establish introduction at %s%s%s.", + safe_str_client(extend_info_describe(intro->extend_info)), + direct_ei ? " via direct address " : "", + direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : "" + ); + extend_info_free(direct_ei); return -1; } - /* We must have the same exit node even if cannibalized. */ + /* We must have the same exit node even if cannibalized or direct connection. + */ tor_assert(tor_memeq(intro->extend_info->identity_digest, launched->build_state->chosen_exit->identity_digest, DIGEST_LEN)); @@ -2648,6 +2903,7 @@ rend_service_launch_establish_intro(rend_service_t *service, launched->intro_key = crypto_pk_dup_key(intro->intro_key); if (launched->base_.state == CIRCUIT_STATE_OPEN) rend_service_intro_has_opened(launched); + extend_info_free(direct_ei); return 0; } @@ -2703,9 +2959,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) int reason = END_CIRC_REASON_TORPROTOCOL; tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, get_options()); tor_assert(circuit->cpath); tor_assert(circuit->rend_data); @@ -2772,6 +3026,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) log_info(LD_REND, "Established circuit %u as introduction point for service %s", (unsigned)circuit->base_.n_circ_id, serviceid); + circuit_log_path(LOG_INFO, LD_REND, circuit); /* Use the intro key instead of the service key in ESTABLISH_INTRO. */ crypto_pk_t *intro_key = circuit->intro_key; @@ -2901,9 +3156,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); tor_assert(circuit->cpath); tor_assert(circuit->build_state); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, get_options()); tor_assert(circuit->rend_data); /* Declare the circuit dirty to avoid reuse, and for path-bias */ @@ -2923,6 +3176,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) "Done building circuit %u to rendezvous with " "cookie %s for service %s", (unsigned)circuit->base_.n_circ_id, hexcookie, serviceid); + circuit_log_path(LOG_INFO, LD_REND, circuit); /* Clear the 'in-progress HS circ has timed out' flag for * consistency with what happens on the client side; this line has @@ -3489,6 +3743,9 @@ rend_consider_services_intro_points(void) int i; time_t now; const or_options_t *options = get_options(); + /* Are we in single onion mode? */ + const int allow_direct = rend_service_allow_non_anonymous_connection( + get_options()); /* List of nodes we need to _exclude_ when choosing a new node to * establish an intro point to. */ smartlist_t *exclude_nodes; @@ -3584,8 +3841,24 @@ rend_consider_services_intro_points(void) router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC; if (get_options()->AllowInvalid_ & ALLOW_INVALID_INTRODUCTION) flags |= CRN_ALLOW_INVALID; + router_crn_flags_t direct_flags = flags; + direct_flags |= CRN_PREF_ADDR; + direct_flags |= CRN_DIRECT_CONN; + node = router_choose_random_node(exclude_nodes, - options->ExcludeNodes, flags); + options->ExcludeNodes, + allow_direct ? direct_flags : flags); + /* If we are in single onion mode, retry node selection for a 3-hop + * path */ + if (allow_direct && !node) { + log_info(LD_REND, + "Unable to find an intro point that we can connect to " + "directly for %s, falling back to a 3-hop path.", + safe_str_client(service->service_id)); + node = router_choose_random_node(exclude_nodes, + options->ExcludeNodes, flags); + } + if (!node) { log_warn(LD_REND, "We only have %d introduction points established for %s; " @@ -3595,10 +3868,13 @@ rend_consider_services_intro_points(void) n_intro_points_to_open); break; } - /* Add the choosen node to the exclusion list in order to avoid to - * pick it again in the next iteration. */ + /* Add the choosen node to the exclusion list in order to avoid picking + * it again in the next iteration. */ smartlist_add(exclude_nodes, (void*)node); intro = tor_malloc_zero(sizeof(rend_intro_point_t)); + /* extend_info is for clients, so we want the multi-hop primary ORPort, + * even if we are a single onion service and intend to connect to it + * directly ourselves. */ intro->extend_info = extend_info_from_node(node, 0); intro->intro_key = crypto_pk_new(); const int fail = crypto_pk_generate_key(intro->intro_key); @@ -3644,8 +3920,9 @@ rend_consider_services_upload(time_t now) { int i; rend_service_t *service; - int rendpostperiod = get_options()->RendPostPeriod; - int rendinitialpostdelay = (get_options()->TestingTorNetwork ? + const or_options_t *options = get_options(); + int rendpostperiod = options->RendPostPeriod; + int rendinitialpostdelay = (options->TestingTorNetwork ? MIN_REND_INITIAL_POST_DELAY_TESTING : MIN_REND_INITIAL_POST_DELAY); @@ -3656,6 +3933,12 @@ rend_consider_services_upload(time_t now) * the descriptor is stable before being published. See comment below. */ service->next_upload_time = now + rendinitialpostdelay + crypto_rand_int(2*rendpostperiod); + /* Single Onion Services prioritise availability over hiding their + * startup time, as their IP address is publicly discoverable anyway. + */ + if (rend_service_reveal_startup_time(options)) { + service->next_upload_time = now + rendinitialpostdelay; + } } /* Does every introduction points have been established? */ unsigned int intro_points_ready = @@ -3896,12 +4179,51 @@ rend_service_set_connection_addr_port(edge_connection_t *conn, return -2; } -/* Stub that should be replaced with the #17178 version of the function - * when merging. */ +/* Are HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode consistent? + */ +static int +rend_service_non_anonymous_mode_consistent(const or_options_t *options) +{ + /* !! is used to make these options boolean */ + return (!! options->HiddenServiceSingleHopMode == + !! options->HiddenServiceNonAnonymousMode); +} + +/* Do the options allow onion services to make direct (non-anonymous) + * connections to introduction or rendezvous points? + * Must only be called after options_validate_single_onion() has successfully + * checked onion service option consistency. + * Returns true if tor is in HiddenServiceSingleHopMode. */ int -rend_service_allow_direct_connection(const or_options_t *options) +rend_service_allow_non_anonymous_connection(const or_options_t *options) { - (void)options; - return 0; + tor_assert(rend_service_non_anonymous_mode_consistent(options)); + return options->HiddenServiceSingleHopMode ? 1 : 0; +} + +/* Do the options allow us to reveal the exact startup time of the onion + * service? + * Single Onion Services prioritise availability over hiding their + * startup time, as their IP address is publicly discoverable anyway. + * Must only be called after options_validate_single_onion() has successfully + * checked onion service option consistency. + * Returns true if tor is in non-anonymous hidden service mode. */ +int +rend_service_reveal_startup_time(const or_options_t *options) +{ + tor_assert(rend_service_non_anonymous_mode_consistent(options)); + return rend_service_non_anonymous_mode_enabled(options); +} + +/* Is non-anonymous mode enabled using the HiddenServiceNonAnonymousMode + * config option? + * Must only be called after options_validate_single_onion() has successfully + * checked onion service option consistency. + */ +int +rend_service_non_anonymous_mode_enabled(const or_options_t *options) +{ + tor_assert(rend_service_non_anonymous_mode_consistent(options)); + return options->HiddenServiceNonAnonymousMode ? 1 : 0; } diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 1622086a99..630191e8b7 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -63,11 +63,68 @@ struct rend_intro_cell_s { uint8_t dh[DH_KEY_LEN]; }; +/** Represents a single hidden service running at this OP. */ +typedef struct rend_service_t { + /* Fields specified in config file */ + char *directory; /**< where in the filesystem it stores it. Will be NULL if + * this service is ephemeral. */ + int dir_group_readable; /**< if 1, allow group read + permissions on directory */ + smartlist_t *ports; /**< List of rend_service_port_config_t */ + rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client + * authorization is performed. */ + smartlist_t *clients; /**< List of rend_authorized_client_t's of + * clients that may access our service. Can be NULL + * if no client authorization is performed. */ + /* Other fields */ + crypto_pk_t *private_key; /**< Permanent hidden-service key. */ + char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without + * '.onion' */ + char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */ + smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have, + * or are trying to establish. */ + /** List of rend_intro_point_t that are expiring. They are removed once + * the new descriptor is successfully uploaded. A node in this list CAN + * NOT appear in the intro_nodes list. */ + smartlist_t *expiring_nodes; + time_t intro_period_started; /**< Start of the current period to build + * introduction points. */ + int n_intro_circuits_launched; /**< Count of intro circuits we have + * established in this period. */ + unsigned int n_intro_points_wanted; /**< Number of intro points this + * service wants to have open. */ + rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */ + time_t desc_is_dirty; /**< Time at which changes to the hidden service + * descriptor content occurred, or 0 if it's + * up-to-date. */ + time_t next_upload_time; /**< Scheduled next hidden service descriptor + * upload time. */ + /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to + * detect repeats. Clients may send INTRODUCE1 cells for the same + * rendezvous point through two or more different introduction points; + * when they do, this keeps us from launching multiple simultaneous attempts + * to connect to the same rend point. */ + replaycache_t *accepted_intro_dh_parts; + /** If true, we don't close circuits for making requests to unsupported + * ports. */ + int allow_unknown_ports; + /** The maximum number of simultanious streams-per-circuit that are allowed + * to be established, or 0 if no limit is set. + */ + int max_streams_per_circuit; + /** If true, we close circuits that exceed the max_streams_per_circuit + * limit. */ + int max_streams_close_circuit; +} rend_service_t; + +STATIC void rend_service_free(rend_service_t *service); +STATIC char *rend_service_sos_poison_path(const rend_service_t *service); + #endif int num_rend_services(void); int rend_config_services(const or_options_t *options, int validate_only); -int rend_service_load_all_keys(void); +int rend_service_load_all_keys(const smartlist_t *service_list); void rend_services_add_filenames_to_lists(smartlist_t *open_lst, smartlist_t *stat_lst); void rend_consider_services_intro_points(void); @@ -108,6 +165,11 @@ void rend_service_port_config_free(rend_service_port_config_t *p); void rend_authorized_client_free(rend_authorized_client_t *client); +int rend_service_list_verify_single_onion_poison( + const smartlist_t *service_list, + const or_options_t *options); +int rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list); + /** Return value from rend_service_add_ephemeral. */ typedef enum { RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ @@ -131,7 +193,9 @@ void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, const char *service_id, int seconds_valid); void rend_service_desc_has_uploaded(const rend_data_t *rend_data); -int rend_service_allow_direct_connection(const or_options_t *options); +int rend_service_allow_non_anonymous_connection(const or_options_t *options); +int rend_service_reveal_startup_time(const or_options_t *options); +int rend_service_non_anonymous_mode_enabled(const or_options_t *options); #endif diff --git a/src/test/include.am b/src/test/include.am index 0aff395091..5bcc969cf0 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -28,9 +28,10 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ $(TESTSCRIPTS) # These flavors are run using automake's test-driver and test-network.sh -TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min +TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min single-onion # only run if we can ping6 ::1 (localhost) -TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-ipv6 +TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-ipv6 \ + single-onion-ipv6 # only run if we can find a stable (or simply another) version of tor TEST_CHUTNEY_FLAVORS_MIXED = mixed diff --git a/src/test/test-child.c b/src/test/test-child.c index e2552a499d..fdf3ccec0a 100644 --- a/src/test/test-child.c +++ b/src/test/test-child.c @@ -1,8 +1,8 @@ /* Copyright (c) 2011-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#include <stdio.h> #include "orconfig.h" +#include <stdio.h> #ifdef _WIN32 #define WINDOWS_LEAN_AND_MEAN #include <windows.h> diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index 8187c45461..21882448c3 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -1,3 +1,4 @@ +#include "orconfig.h" #include <string.h> #include <stdio.h> #include <sys/types.h> diff --git a/src/test/test.h b/src/test/test.h index c0643e154d..bf1f53d207 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -169,6 +169,7 @@ struct crypto_pk_t *pk_generate(int idx); #define NS_UNMOCK(name) UNMOCK(name) extern const struct testcase_setup_t passthrough_setup; +extern const struct testcase_setup_t ed25519_test_setup; extern struct testcase_t accounting_tests[]; extern struct testcase_t addr_tests[]; diff --git a/src/test/test_address.c b/src/test/test_address.c index 9b4c52fdfc..e52779cb64 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -557,6 +557,13 @@ fake_open_socket(int domain, int type, int protocol) return FAKE_SOCKET_FD; } +static int +fake_close_socket(tor_socket_t s) +{ + (void)s; + return 0; +} + static int last_connected_socket_fd = 0; static int connect_retval = 0; @@ -617,6 +624,7 @@ test_address_udp_socket_trick_whitebox(void *arg) MOCK(tor_open_socket,fake_open_socket); MOCK(tor_connect_socket,pretend_to_connect); MOCK(tor_getsockname,fake_getsockname); + MOCK(tor_close_socket,fake_close_socket); mock_addr = tor_malloc_zero(sizeof(struct sockaddr_storage)); sockaddr_in_from_string("23.32.246.118",(struct sockaddr_in *)mock_addr); @@ -647,11 +655,12 @@ test_address_udp_socket_trick_whitebox(void *arg) tt_assert(sockaddr_in6_are_equal(mock_addr6,ipv6_to_check)); + done: UNMOCK(tor_open_socket); UNMOCK(tor_connect_socket); UNMOCK(tor_getsockname); + UNMOCK(tor_close_socket); - done: tor_free(ipv6_to_check); tor_free(mock_addr); tor_free(addr_from_hack); diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 971dd1d889..3408da3aa9 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -744,6 +744,27 @@ test_buffers_tls_read_mocked(void *arg) buf_free(buf); } +static void +test_buffers_chunk_size(void *arg) +{ + (void)arg; + const int min = 256; + const int max = 65536; + tt_uint_op(preferred_chunk_size(3), OP_EQ, min); + tt_uint_op(preferred_chunk_size(25), OP_EQ, min); + tt_uint_op(preferred_chunk_size(0), OP_EQ, min); + tt_uint_op(preferred_chunk_size(256), OP_EQ, 512); + tt_uint_op(preferred_chunk_size(65400), OP_EQ, max); + /* Here, we're implicitly saying that the chunk header overhead is + * between 1 and 100 bytes. 24..48 would probably be more accurate. */ + tt_uint_op(preferred_chunk_size(65536), OP_GT, 65536); + tt_uint_op(preferred_chunk_size(65536), OP_LT, 65536+100); + tt_uint_op(preferred_chunk_size(165536), OP_GT, 165536); + tt_uint_op(preferred_chunk_size(165536), OP_LT, 165536+100); + done: + ; +} + struct testcase_t buffer_tests[] = { { "basic", test_buffers_basic, TT_FORK, NULL, NULL }, { "copy", test_buffer_copy, TT_FORK, NULL, NULL }, @@ -758,6 +779,7 @@ struct testcase_t buffer_tests[] = { NULL, NULL}, { "tls_read_mocked", test_buffers_tls_read_mocked, 0, NULL, NULL }, + { "chunk_size", test_buffers_chunk_size, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index bde46a2998..08442e01b6 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -1,6 +1,8 @@ /* Copyright (c) 2014-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +#include "orconfig.h" + #include <math.h> #define TOR_CHANNEL_INTERNAL_ diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index b233e99328..e996c42115 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -355,10 +355,8 @@ test_pick_circid(void *arg) } done: - if (chan1) - circuitmux_free(chan1->cmux); - if (chan2) - circuitmux_free(chan2->cmux); + circuitmux_free(chan1->cmux); + circuitmux_free(chan2->cmux); tor_free(chan1); tor_free(chan2); bitarray_free(ba); diff --git a/src/test/test_connection.c b/src/test/test_connection.c index bf2cf18b96..d394fc9852 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -95,6 +95,13 @@ mock_connection_connect_sockaddr(connection_t *conn, return 1; } +static int +fake_close_socket(evutil_socket_t sock) +{ + (void)sock; + return 0; +} + static void test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr) { @@ -124,6 +131,7 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose) MOCK(connection_connect_sockaddr, mock_connection_connect_sockaddr); + MOCK(tor_close_socket, fake_close_socket); init_connection_lists(); @@ -148,12 +156,13 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose) assert_connection_ok(conn, time(NULL)); UNMOCK(connection_connect_sockaddr); - + UNMOCK(tor_close_socket); return conn; /* On failure */ done: UNMOCK(connection_connect_sockaddr); + UNMOCK(tor_close_socket); return NULL; } diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 9cae1e8dd8..d83b93be58 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -999,11 +999,11 @@ test_crypto_sha3(void *arg) crypto_digest_free(d1); /* Attempt to exercise the incremental hashing code by creating a randomized - * 100 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512 + * 30 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512 * is used because it has a lowest rate of the family (the code is common, * but the slower rate exercises more of it). */ - const size_t bufsz = 100 * 1024; + const size_t bufsz = 30 * 1024; size_t j = 0; large = tor_malloc(bufsz); crypto_rand(large, bufsz); @@ -2139,24 +2139,6 @@ test_crypto_curve25519_persist(void *arg) tor_free(tag); } -static void * -ed25519_testcase_setup(const struct testcase_t *testcase) -{ - crypto_ed25519_testing_force_impl(testcase->setup_data); - return testcase->setup_data; -} -static int -ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr) -{ - (void)testcase; - (void)ptr; - crypto_ed25519_testing_restore_impl(); - return 1; -} -static const struct testcase_setup_t ed25519_test_setup = { - ed25519_testcase_setup, ed25519_testcase_cleanup -}; - static void test_crypto_ed25519_simple(void *arg) { @@ -2619,77 +2601,6 @@ test_crypto_ed25519_testvectors(void *arg) } static void -test_crypto_ed25519_fuzz_donna(void *arg) -{ - const unsigned iters = 1024; - uint8_t msg[1024]; - unsigned i; - (void)arg; - - tt_assert(sizeof(msg) == iters); - crypto_rand((char*) msg, sizeof(msg)); - - /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to - * generate keys/sign per iteration. - */ - for (i = 0; i < iters; ++i) { - const int use_donna = i & 1; - uint8_t blinding[32]; - curve25519_keypair_t ckp; - ed25519_keypair_t kp, kp_blind, kp_curve25519; - ed25519_public_key_t pk, pk_blind, pk_curve25519; - ed25519_signature_t sig, sig_blind; - int bit = 0; - - crypto_rand((char*) blinding, sizeof(blinding)); - - /* Impl. A: - * 1. Generate a keypair. - * 2. Blinded the keypair. - * 3. Sign a message (unblinded). - * 4. Sign a message (blinded). - * 5. Generate a curve25519 keypair, and convert it to Ed25519. - */ - ed25519_set_impl_params(use_donna); - tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1)); - tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding)); - tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp)); - tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind)); - - tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1)); - tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair( - &kp_curve25519, &bit, &ckp)); - - /* Impl. B: - * 1. Validate the public key by rederiving it. - * 2. Validate the blinded public key by rederiving it. - * 3. Validate the unblinded signature (and test a invalid signature). - * 4. Validate the blinded signature. - * 5. Validate the public key (from Curve25519) by rederiving it. - */ - ed25519_set_impl_params(!use_donna); - tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey)); - tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32); - - tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding)); - tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32); - - tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk)); - sig.sig[0] ^= 15; - tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk)); - - tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind)); - - tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key( - &pk_curve25519, &ckp.pubkey, bit)); - tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32); - } - - done: - ; -} - -static void test_crypto_ed25519_storage(void *arg) { (void)arg; @@ -2995,7 +2906,6 @@ struct testcase_t crypto_tests[] = { ED25519_TEST(convert, 0), ED25519_TEST(blinding, 0), ED25519_TEST(testvectors, 0), - ED25519_TEST(fuzz_donna, TT_FORK), { "ed25519_storage", test_crypto_ed25519_storage, 0, NULL, NULL }, { "siphash", test_crypto_siphash, 0, NULL, NULL }, { "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL }, diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c index 6f3e40e0ab..0d7d65ac73 100644 --- a/src/test/test_crypto_slow.c +++ b/src/test/test_crypto_slow.c @@ -421,12 +421,16 @@ test_crypto_pbkdf2_vectors(void *arg) secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1"); + /* This is the very slow one here. When enabled, it accounts for roughly + * half the time spent in test-slow. */ + /* base16_decode((char*)spec, sizeof(spec), "73616c74" "18" , 10); memset(out, 0x00, sizeof(out)); tt_int_op(20, OP_EQ, secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1)); test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"); + */ base16_decode((char*)spec, sizeof(spec), "73616c7453414c5473616c7453414c5473616c745" @@ -503,9 +507,88 @@ test_crypto_pwbox(void *arg) tor_free(decoded); } +static void +test_crypto_ed25519_fuzz_donna(void *arg) +{ + const unsigned iters = 1024; + uint8_t msg[1024]; + unsigned i; + (void)arg; + + tt_assert(sizeof(msg) == iters); + crypto_rand((char*) msg, sizeof(msg)); + + /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to + * generate keys/sign per iteration. + */ + for (i = 0; i < iters; ++i) { + const int use_donna = i & 1; + uint8_t blinding[32]; + curve25519_keypair_t ckp; + ed25519_keypair_t kp, kp_blind, kp_curve25519; + ed25519_public_key_t pk, pk_blind, pk_curve25519; + ed25519_signature_t sig, sig_blind; + int bit = 0; + + crypto_rand((char*) blinding, sizeof(blinding)); + + /* Impl. A: + * 1. Generate a keypair. + * 2. Blinded the keypair. + * 3. Sign a message (unblinded). + * 4. Sign a message (blinded). + * 5. Generate a curve25519 keypair, and convert it to Ed25519. + */ + ed25519_set_impl_params(use_donna); + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1)); + tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding)); + tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp)); + tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind)); + + tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1)); + tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair( + &kp_curve25519, &bit, &ckp)); + + /* Impl. B: + * 1. Validate the public key by rederiving it. + * 2. Validate the blinded public key by rederiving it. + * 3. Validate the unblinded signature (and test a invalid signature). + * 4. Validate the blinded signature. + * 5. Validate the public key (from Curve25519) by rederiving it. + */ + ed25519_set_impl_params(!use_donna); + tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey)); + tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32); + + tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding)); + tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32); + + tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk)); + sig.sig[0] ^= 15; + tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk)); + + tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind)); + + tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key( + &pk_curve25519, &ckp.pubkey, bit)); + tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32); + } + + done: + ; +} + #define CRYPTO_LEGACY(name) \ { #name, test_crypto_ ## name , 0, NULL, NULL } +#define ED25519_TEST_ONE(name, fl, which) \ + { #name "/ed25519_" which, test_crypto_ed25519_ ## name, (fl), \ + &ed25519_test_setup, (void*)which } + +#define ED25519_TEST(name, fl) \ + ED25519_TEST_ONE(name, (fl), "donna"), \ + ED25519_TEST_ONE(name, (fl), "ref10") + struct testcase_t slow_crypto_tests[] = { CRYPTO_LEGACY(s2k_rfc2440), #ifdef HAVE_LIBSCRYPT @@ -527,6 +610,7 @@ struct testcase_t slow_crypto_tests[] = { { "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL }, { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL }, { "pwbox", test_crypto_pwbox, 0, NULL, NULL }, + ED25519_TEST(fuzz_donna, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 1daa1552e9..fd5ab15643 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -8,12 +8,14 @@ #define CONTROL_PRIVATE #define CIRCUITBUILD_PRIVATE +#define RENDSERVICE_PRIVATE #include "or.h" #include "test.h" #include "control.h" #include "config.h" #include "rendcommon.h" +#include "rendservice.h" #include "routerset.h" #include "circuitbuild.h" #include "test_helpers.h" @@ -496,6 +498,212 @@ test_hs_auth_cookies(void *arg) return; } +static int mock_get_options_calls = 0; +static or_options_t *mock_options = NULL; + +static void +reset_options(or_options_t *options, int *get_options_calls) +{ + memset(options, 0, sizeof(or_options_t)); + options->TestingTorNetwork = 1; + + *get_options_calls = 0; +} + +static const or_options_t * +mock_get_options(void) +{ + ++mock_get_options_calls; + tor_assert(mock_options); + return mock_options; +} + +/* Test that single onion poisoning works. */ +static void +test_single_onion_poisoning(void *arg) +{ + or_options_t opt; + mock_options = &opt; + reset_options(mock_options, &mock_get_options_calls); + MOCK(get_options, mock_get_options); + + int ret = -1; + mock_options->DataDirectory = tor_strdup(get_fname("test_data_dir")); + rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t)); + char *dir1 = tor_strdup(get_fname("test_hs_dir1")); + rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t)); + char *dir2 = tor_strdup(get_fname("test_hs_dir2")); + smartlist_t *services = smartlist_new(); + + (void) arg; + + /* No services, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Create directories for both services */ + +#ifdef _WIN32 + ret = mkdir(mock_options->DataDirectory); + tt_assert(ret == 0); + ret = mkdir(dir1); + tt_assert(ret == 0); + ret = mkdir(dir2); +#else + ret = mkdir(mock_options->DataDirectory, 0700); + tt_assert(ret == 0); + ret = mkdir(dir1, 0700); + tt_assert(ret == 0); + ret = mkdir(dir2, 0700); +#endif + tt_assert(ret == 0); + + service_1->directory = dir1; + service_2->directory = dir2; + dir1 = dir2 = NULL; + smartlist_add(services, service_1); + /* But don't add the second service yet. */ + + /* Service directories, but no previous keys, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Poison! Poison! Poison! + * This can only be done in HiddenServiceSingleHopMode. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dirs(services); + tt_assert(ret == 0); + /* Poisoning twice is a no-op. */ + ret = rend_service_poison_new_single_onion_dirs(services); + tt_assert(ret == 0); + + /* Poisoned service directories, but no previous keys, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Now add some keys, and we'll have a problem. */ + ret = rend_service_load_all_keys(services); + tt_assert(ret == 0); + + /* Poisoned service directories with previous keys are not allowed. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret < 0); + + /* But they are allowed if we're in non-anonymous mode. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Re-poisoning directories with existing keys is a no-op, because + * directories with existing keys are ignored. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dirs(services); + tt_assert(ret == 0); + /* And it keeps the poison. */ + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Now add the second service: it has no key and no poison file */ + smartlist_add(services, service_2); + + /* A new service, and an existing poisoned service. Not ok. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret < 0); + + /* But ok to add in non-anonymous mode. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Now remove the poisoning from the first service, and we have the opposite + * problem. */ + char *poison_path = rend_service_sos_poison_path(service_1); + ret = unlink(poison_path); + tor_free(poison_path); + tt_assert(ret == 0); + + /* Unpoisoned service directories with previous keys are ok, as are empty + * directories. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* But the existing unpoisoned key is not ok in non-anonymous mode, even if + * there is an empty service. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret < 0); + + /* Poisoning directories with existing keys is a no-op, because directories + * with existing keys are ignored. But the new directory should poison. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dirs(services); + tt_assert(ret == 0); + /* And the old directory remains unpoisoned. */ + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret < 0); + + /* And the new directory should be ignored, because it has no key. */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret == 0); + + /* Re-poisoning directories without existing keys is a no-op. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_poison_new_single_onion_dirs(services); + tt_assert(ret == 0); + /* And the old directory remains unpoisoned. */ + ret = rend_service_list_verify_single_onion_poison(services, mock_options); + tt_assert(ret < 0); + + done: + /* TODO: should we delete the directories here? */ + rend_service_free(service_1); + rend_service_free(service_2); + smartlist_free(services); + UNMOCK(get_options); + tor_free(mock_options->DataDirectory); + tor_free(dir1); + tor_free(dir2); +} + struct testcase_t hs_tests[] = { { "hs_rend_data", test_hs_rend_data, TT_FORK, NULL, NULL }, @@ -508,6 +716,8 @@ struct testcase_t hs_tests[] = { NULL, NULL }, { "hs_auth_cookies", test_hs_auth_cookies, TT_FORK, NULL, NULL }, + { "single_onion_poisoning", test_single_onion_poisoning, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_options.c b/src/test/test_options.c index 0451b93738..31a916f9ac 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -2750,6 +2750,155 @@ test_options_validate__rend(void *ignored) } static void +test_options_validate__single_onion(void *ignored) +{ + (void)ignored; + int ret; + char *msg; + options_test_data_t *tdata = NULL; + setup_capture_of_logs(LOG_WARN); + + /* Test that HiddenServiceSingleHopMode must come with + * HiddenServiceNonAnonymousMode */ + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + tt_str_op(msg, OP_EQ, "HiddenServiceSingleHopMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceNonAnonymousMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + tt_str_op(msg, OP_EQ, "HiddenServiceSingleHopMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceNonAnonymousMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + /* Test that SOCKSPort must come with Tor2webMode if + * HiddenServiceSingleHopMode is 1 */ + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 5000\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + "Tor2webMode 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode is incompatible with " + "using Tor as an anonymous client. Please set " + "Socks/Trans/NATD/DNSPort to 0, or HiddenServiceNonAnonymousMode " + "to 0, or use the non-anonymous Tor2webMode."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 0\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + "Tor2webMode 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 5000\n" + "HiddenServiceSingleHopMode 0\n" + "Tor2webMode 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "SOCKSPort 5000\n" + "HiddenServiceSingleHopMode 1\n" + "HiddenServiceNonAnonymousMode 1\n" + "Tor2webMode 1\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + /* Test that a hidden service can't be run with Tor2web + * Use HiddenServiceNonAnonymousMode instead of Tor2webMode, because + * Tor2webMode requires a compilation #define */ + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceNonAnonymousMode 1\n" + "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n" + "HiddenServicePort 80 127.0.0.1:8080\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceSingleHopMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceNonAnonymousMode 1\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode does not provide any " + "server anonymity. It must be used with " + "HiddenServiceSingleHopMode set to 1."); + tor_free(msg); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n" + "HiddenServicePort 80 127.0.0.1:8080\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + free_options_test_data(tdata); + + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "HiddenServiceNonAnonymousMode 1\n" + "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n" + "HiddenServicePort 80 127.0.0.1:8080\n" + "HiddenServiceSingleHopMode 1\n" + "SOCKSPort 0\n" + ); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + done: + policies_free_all(); + teardown_capture_of_logs(); + free_options_test_data(tdata); + tor_free(msg); +} + +static void test_options_validate__accounting(void *ignored) { (void)ignored; @@ -4371,6 +4520,7 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(port_forwarding), LOCAL_VALIDATE_TEST(tor2web), LOCAL_VALIDATE_TEST(rend), + LOCAL_VALIDATE_TEST(single_onion), LOCAL_VALIDATE_TEST(accounting), LOCAL_VALIDATE_TEST(proxy), LOCAL_VALIDATE_TEST(control), diff --git a/src/test/test_relay.c b/src/test/test_relay.c index 29016e444b..4713c79ea5 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -113,11 +113,11 @@ test_relay_append_cell_to_circuit_queue(void *arg) done: tor_free(cell); - cell_queue_clear(&orcirc->base_.n_chan_cells); - cell_queue_clear(&orcirc->p_chan_cells); if (orcirc) { circuitmux_detach_circuit(nchan->cmux, TO_CIRCUIT(orcirc)); circuitmux_detach_circuit(pchan->cmux, TO_CIRCUIT(orcirc)); + cell_queue_clear(&orcirc->base_.n_chan_cells); + cell_queue_clear(&orcirc->p_chan_cells); } tor_free(orcirc); free_fake_channel(nchan); diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index 2e0736f99b..05ea8e86e8 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -1,10 +1,9 @@ /* Copyright (c) 2014-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -#include <math.h> - #include "orconfig.h" +#include <math.h> #include <event2/event.h> #define TOR_CHANNEL_INTERNAL_ diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index 19f2e2d95e..056f199b94 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -1110,11 +1110,9 @@ test_keep_commit(void *arg) trusteddirserver_get_by_v3_auth_digest_m); { - k = crypto_pk_new(); + k = pk_generate(1); /* Setup a minimal dirauth environment for this test */ /* Have a key that is not the one from our commit. */ - tt_int_op(0, ==, crypto_pk_generate_key(k)); - tt_int_op(0, ==, crypto_pk_get_fingerprint(k, fp, 0)); init_authority_state(); state = get_sr_state(); } diff --git a/src/test/test_status.c b/src/test/test_status.c index b4438aabe9..a3b1a2af87 100644 --- a/src/test/test_status.c +++ b/src/test/test_status.c @@ -3,6 +3,8 @@ #define LOG_PRIVATE #define REPHIST_PRIVATE +#include "orconfig.h" + #include <float.h> #include <math.h> diff --git a/src/test/test_threads.c b/src/test/test_threads.c index 1bbe6f5508..ebbc95c7ca 100644 --- a/src/test/test_threads.c +++ b/src/test/test_threads.c @@ -87,11 +87,6 @@ test_threads_basic(void *arg) char *s1 = NULL, *s2 = NULL; int done = 0, timedout = 0; time_t started; -#ifndef _WIN32 - struct timeval tv; - tv.tv_sec=0; - tv.tv_usec=100*1000; -#endif (void) arg; tt_int_op(tor_threadlocal_init(&count), OP_EQ, 0); @@ -120,10 +115,8 @@ test_threads_basic(void *arg) timedout = done = 1; } tor_mutex_release(thread_test_mutex_); -#ifndef _WIN32 /* Prevent the main thread from starving the worker threads. */ - select(0, NULL, NULL, NULL, &tv); -#endif + tor_sleep_msec(10); } tor_mutex_acquire(thread_test_start1_); tor_mutex_release(thread_test_start1_); @@ -286,16 +279,7 @@ test_threads_conditionvar(void *arg) if (!timeout) { tt_int_op(ti->n_shutdown, ==, 4); } else { -#ifdef _WIN32 - Sleep(500); /* msec */ -#elif defined(HAVE_USLEEP) - usleep(500*1000); /* usec */ -#else - { - struct tv = { 0, 500*1000 }; - select(0, NULL, NULL, NULL, &tv); - } -#endif + tor_sleep_msec(200); tor_mutex_acquire(ti->mutex); tt_int_op(ti->n_shutdown, ==, 2); tt_int_op(ti->n_timeouts, ==, 2); diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 8502e8aa3f..8efcac242f 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -2422,6 +2422,8 @@ test_tortls_context_new(void *ignored) ret = tor_tls_context_new(NULL, 0, 0, 0); tt_assert(!ret); + /* note: we already override this in testing_common.c, so we + * run this unit test in a subprocess. */ MOCK(crypto_pk_generate_key_with_bits, fixed_crypto_pk_generate_key_with_bits); fixed_crypto_pk_new_result_index = 0; @@ -2808,7 +2810,7 @@ struct testcase_t tortls_tests[] = { INTRUSIVE_TEST_CASE(find_cipher_by_id, 0), INTRUSIVE_TEST_CASE(session_secret_cb, 0), INTRUSIVE_TEST_CASE(debug_state_callback, 0), - INTRUSIVE_TEST_CASE(context_new, 0), + INTRUSIVE_TEST_CASE(context_new, TT_FORK /* redundant */), LOCAL_TEST_CASE(create_certificate, 0), LOCAL_TEST_CASE(cert_new, 0), LOCAL_TEST_CASE(cert_is_valid, 0), diff --git a/src/test/test_util.c b/src/test/test_util.c index 224ec7bd55..1edfaa9ead 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -877,9 +877,15 @@ test_util_time(void *arg) t_res = INT64_MIN; CAPTURE(); tor_gmtime_r(&t_res, &b_time); - CHECK_TIMEGM_WARNING("Rounding up to "); - tt_assert(b_time.tm_year == (1970-1900) || - b_time.tm_year == (1-1900)); + if (! (b_time.tm_year == (1970-1900) || + b_time.tm_year == (1-1900))) { + tt_int_op(b_time.tm_year, OP_EQ, 1970-1900); + } + if (b_time.tm_year != 1970-1900) { + CHECK_TIMEGM_WARNING("Rounding up to "); + } else { + teardown_capture_of_logs(); + } } #endif @@ -5088,6 +5094,44 @@ test_util_socket(void *arg) tor_close_socket(fd4); } +static int +is_there_a_localhost(void) +{ + tor_socket_t s; + s = tor_open_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + tor_assert(SOCKET_OK(s)); + + struct sockaddr_in s_in; + memset(&s_in, 0, sizeof(s_in)); + s_in.sin_family = AF_INET; + s_in.sin_addr.s_addr = htonl(0x7f000001); + s_in.sin_port = 0; + + int result = 0; + if (bind(s, (void*)&s_in, sizeof(s_in)) == 0) { + result = 1; + } + tor_close_socket(s); + if (result) + return result; + + s = tor_open_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + tor_assert(SOCKET_OK(s)); + + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr.s6_addr[15] = 1; + sin6.sin6_port = 0; + + if (bind(s, (void*)&sin6, sizeof(sin6)) == 0) { + result = 1; + } + tor_close_socket(s); + + return result; +} + /* Test for socketpair and ersatz_socketpair(). We test them both, since * the latter is a tolerably good way to exersize tor_accept_socket(). */ static void @@ -5106,10 +5150,10 @@ test_util_socketpair(void *arg) * Otherwise, we risk exposing a socketpair on a routable IP address. (Some * BSD jails use a routable address for localhost. Fortunately, they have * the real AF_UNIX socketpair.) */ - if (ersatz && ERRNO_IS_EPROTO(-socketpair_result)) { + if (ersatz && socketpair_result < 0 && !is_there_a_localhost()) { /* In my testing, an IPv6-only FreeBSD jail without ::1 returned EINVAL. * Assume we're on a machine without 127.0.0.1 or ::1 and give up now. */ - goto done; + tt_skip(); } tt_int_op(0, OP_EQ, socketpair_result); diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 29f36a3da6..fd7c4e7074 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -156,37 +156,51 @@ remove_directory(void) } /** Define this if unit tests spend too much time generating public keys*/ -#undef CACHE_GENERATED_KEYS +#define CACHE_GENERATED_KEYS -static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL}; -#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys) +#define N_PREGEN_KEYS 11 +static crypto_pk_t *pregen_keys[N_PREGEN_KEYS]; +static int next_key_idx; /** Generate and return a new keypair for use in unit tests. If we're using - * the key cache optimization, we might reuse keys: we only guarantee that - * keys made with distinct values for <b>idx</b> are different. The value of - * <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */ + * the key cache optimization, we might reuse keys. "idx" is ignored. + * Our only guarantee is that we won't reuse a key till this function has been + * called several times. The order in which keys are returned is slightly + * randomized, so that tests that depend on a particular order will not be + * reliable. */ crypto_pk_t * pk_generate(int idx) { - int res; + (void) idx; #ifdef CACHE_GENERATED_KEYS - tor_assert(idx < N_PREGEN_KEYS); - if (! pregen_keys[idx]) { - pregen_keys[idx] = crypto_pk_new(); - res = crypto_pk_generate_key(pregen_keys[idx]); - tor_assert(!res); - } - return crypto_pk_dup_key(pregen_keys[idx]); + /* Either skip 1 or 2 keys. */ + next_key_idx += crypto_rand_int_range(1,3); + next_key_idx %= N_PREGEN_KEYS; + return crypto_pk_dup_key(pregen_keys[next_key_idx]); #else crypto_pk_t *result; - (void) idx; + int res; result = crypto_pk_new(); - res = crypto_pk_generate_key(result); + res = crypto_pk_generate_key__real(result); tor_assert(!res); return result; #endif } +#ifdef CACHE_GENERATED_KEYS +static int +crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits) +{ + if (bits != 1024) + return crypto_pk_generate_key_with_bits__real(env, bits); + + crypto_pk_t *newkey = pk_generate(0); + crypto_pk_assign_(env, newkey); + crypto_pk_free(newkey); + return 0; +} +#endif + /** Free all storage used for the cached key optimization. */ static void free_pregenerated_keys(void) @@ -213,6 +227,24 @@ passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr) return 1; } +static void * +ed25519_testcase_setup(const struct testcase_t *testcase) +{ + crypto_ed25519_testing_force_impl(testcase->setup_data); + return testcase->setup_data; +} +static int +ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr) +{ + (void)testcase; + (void)ptr; + crypto_ed25519_testing_restore_impl(); + return 1; +} +const struct testcase_setup_t ed25519_test_setup = { + ed25519_testcase_setup, ed25519_testcase_cleanup +}; + const struct testcase_setup_t passthrough_setup = { passthrough_test_setup, passthrough_test_cleanup }; @@ -307,6 +339,16 @@ main(int c, const char **v) } tor_set_failed_assertion_callback(an_assertion_failed); +#ifdef CACHE_GENERATED_KEYS + for (i = 0; i < N_PREGEN_KEYS; ++i) { + pregen_keys[i] = crypto_pk_new(); + int r = crypto_pk_generate_key(pregen_keys[i]); + tor_assert(r == 0); + } + MOCK(crypto_pk_generate_key_with_bits, + crypto_pk_generate_key_with_bits__get_cached); +#endif + atexit(remove_directory); int have_failed = (tinytest_main(c, v, testgroups) != 0); diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c index f495743667..24988d510b 100644 --- a/src/trunnel/ed25519_cert.c +++ b/src/trunnel/ed25519_cert.c @@ -1,4 +1,4 @@ -/* ed25519_cert.c -- generated by Trunnel v1.4.4. +/* ed25519_cert.c -- generated by Trunnel v1.4.6. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ @@ -157,7 +157,7 @@ ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t &inp->un_unparsed.n_, inp->un_unparsed.elts_, newlen, sizeof(inp->un_unparsed.elts_[0]), (trunnel_free_fn_t) NULL, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->un_unparsed.elts_ = newptr; return 0; @@ -589,7 +589,7 @@ ed25519_cert_setlen_ext(ed25519_cert_t *inp, size_t newlen) &inp->ext.n_, inp->ext.elts_, newlen, sizeof(inp->ext.elts_[0]), (trunnel_free_fn_t) ed25519_cert_extension_free, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->ext.elts_ = newptr; return 0; diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h index 75a82d8aff..28f6feef31 100644 --- a/src/trunnel/ed25519_cert.h +++ b/src/trunnel/ed25519_cert.h @@ -1,4 +1,4 @@ -/* ed25519_cert.h -- generated by by Trunnel v1.4.4. +/* ed25519_cert.h -- generated by by Trunnel v1.4.6. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c index 3ef7341ae9..c2717f36bf 100644 --- a/src/trunnel/link_handshake.c +++ b/src/trunnel/link_handshake.c @@ -1,4 +1,4 @@ -/* link_handshake.c -- generated by Trunnel v1.4.4. +/* link_handshake.c -- generated by Trunnel v1.4.6. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ @@ -143,7 +143,7 @@ auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen) &inp->methods.n_, inp->methods.elts_, newlen, sizeof(inp->methods.elts_[0]), (trunnel_free_fn_t) NULL, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->methods.elts_ = newptr; return 0; @@ -452,7 +452,7 @@ certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen) &inp->body.n_, inp->body.elts_, newlen, sizeof(inp->body.elts_[0]), (trunnel_free_fn_t) NULL, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->body.elts_ = newptr; return 0; @@ -747,7 +747,7 @@ rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_t *inp, size_t newlen) &inp->sig.n_, inp->sig.elts_, newlen, sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->sig.elts_ = newptr; return 0; @@ -1268,7 +1268,7 @@ auth1_setlen_sig(auth1_t *inp, size_t newlen) &inp->sig.n_, inp->sig.elts_, newlen, sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->sig.elts_ = newptr; return 0; @@ -1714,7 +1714,7 @@ certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen) &inp->certs.n_, inp->certs.elts_, newlen, sizeof(inp->certs.elts_[0]), (trunnel_free_fn_t) certs_cell_cert_free, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->certs.elts_ = newptr; return 0; diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h index 2749ec7dd4..54611b96e8 100644 --- a/src/trunnel/link_handshake.h +++ b/src/trunnel/link_handshake.h @@ -1,4 +1,4 @@ -/* link_handshake.h -- generated by by Trunnel v1.4.4. +/* link_handshake.h -- generated by by Trunnel v1.4.6. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ diff --git a/src/trunnel/pwbox.c b/src/trunnel/pwbox.c index 9b348a9b30..62662a9369 100644 --- a/src/trunnel/pwbox.c +++ b/src/trunnel/pwbox.c @@ -1,4 +1,4 @@ -/* pwbox.c -- generated by Trunnel v1.4.4. +/* pwbox.c -- generated by Trunnel v1.4.6. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ @@ -151,7 +151,7 @@ pwbox_encoded_setlen_skey_header(pwbox_encoded_t *inp, size_t newlen) &inp->skey_header.n_, inp->skey_header.elts_, newlen, sizeof(inp->skey_header.elts_[0]), (trunnel_free_fn_t) NULL, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->skey_header.elts_ = newptr; return 0; @@ -226,7 +226,7 @@ pwbox_encoded_setlen_data(pwbox_encoded_t *inp, size_t newlen) &inp->data.n_, inp->data.elts_, newlen, sizeof(inp->data.elts_[0]), (trunnel_free_fn_t) NULL, &inp->trunnel_error_code_); - if (newptr == NULL) + if (newlen != 0 && newptr == NULL) goto trunnel_alloc_failed; inp->data.elts_ = newptr; return 0; diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h index e69e2c1a0e..77a813d123 100644 --- a/src/trunnel/pwbox.h +++ b/src/trunnel/pwbox.h @@ -1,4 +1,4 @@ -/* pwbox.h -- generated by by Trunnel v1.4.4. +/* pwbox.h -- generated by by Trunnel v1.4.6. * https://gitweb.torproject.org/trunnel.git * You probably shouldn't edit this file. */ |