diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/circuitbuild.c | 9 | ||||
-rw-r--r-- | src/or/circuitstats.c | 28 | ||||
-rw-r--r-- | src/or/config.c | 160 | ||||
-rw-r--r-- | src/or/config.h | 2 | ||||
-rw-r--r-- | src/or/connection_edge.c | 29 | ||||
-rw-r--r-- | src/or/directory.c | 12 | ||||
-rw-r--r-- | src/or/directory.h | 3 | ||||
-rw-r--r-- | src/or/main.c | 13 | ||||
-rw-r--r-- | src/or/or.h | 21 | ||||
-rw-r--r-- | src/or/rendclient.c | 49 | ||||
-rw-r--r-- | src/or/rendclient.h | 3 | ||||
-rw-r--r-- | src/or/rendcommon.c | 48 | ||||
-rw-r--r-- | src/or/rendcommon.h | 6 | ||||
-rw-r--r-- | src/or/rendservice.c | 299 | ||||
-rw-r--r-- | src/or/rendservice.h | 9 |
15 files changed, 615 insertions, 76 deletions
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 2e7ea2f79a..69a8a9c5ec 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, @@ -1996,7 +1997,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/circuitstats.c b/src/or/circuitstats.c index f4db64ebca..fe8860e9c9 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 Tor2webMode/OnionServiceSingleHopMode 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. " diff --git a/src/or/config.c b/src/or/config.c index 136958c6aa..f7f1f571ce 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(OnionServiceSingleHopMode, BOOL, "0"), + V(OnionServiceNonAnonymousMode,BOOL, "0"), V(HTTPProxy, STRING, NULL), V(HTTPProxyAuthenticator, STRING, NULL), V(HTTPSProxy, STRING, NULL), @@ -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 the insecure OnionServiceSingleHopMode, 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_allow_non_anonymous_connection(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 Single " + "Onion."); + 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,88 @@ warn_about_relative_paths(or_options_t *options) } } +/* Validate options related to OnionServiceSingleHopMode. + * Modifies some options that are incompatible with OnionServiceSingleHopMode. + * 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) +{ + /* You must set OnionServiceNonAnonymousMode to 1 to use + * OnionServiceSingleHopMode */ + if (options->OnionServiceSingleHopMode && + !rend_service_non_anonymous_mode_enabled(options)) { + REJECT("OnionServiceSingleHopMode does not provide any server anonymity. " + "It must be used with OnionServiceNonAnonymousMode set to 1."); + } + + /* If you have OnionServiceNonAnonymousMode set, you must use + * OnionServiceSingleHopMode. */ + if (rend_service_non_anonymous_mode_enabled(options) && + !options->OnionServiceSingleHopMode) { + REJECT("OnionServiceNonAnonymousMode does not provide any server " + "anonymity. It must be used with OnionServiceSingleHopMode set to " + "1."); + } + + /* 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 (options->OnionServiceSingleHopMode && client_port_set && + !options->Tor2webMode) { + REJECT("OnionServiceSingleHopMode is incompatible with using Tor as an " + "anonymous client. Please set Socks/Trans/NATD/DNSPort to 0, or " + "OnionServiceSingleHopMode 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 (!options->OnionServiceSingleHopMode && 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 " + "the non-anonymous OnionServiceSingleHopMode."); + } + + if (options->OnionServiceSingleHopMode + && options->UseEntryGuards) { + /* Single Onion services do not (and should not) use entry guards + * in any meaningful way. 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 a piece of code which cannot possibly help Single Onions, + * compatible with OnionServiceSingleHopMode. + */ + log_notice(LD_CONFIG, + "OnionServiceSingleHopMode is enabled; disabling " + "UseEntryGuards."); + options->UseEntryGuards = 0; + } + + /* Check if existing hidden service keys were created with a different + * setting of OnionServiceNonAnonymousMode, 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 OnionServiceSingleHopMode " + "%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 @@ -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 @@ -3353,6 +3443,17 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } + /* OnionServiceSingleHopMode: one hop between the onion service server and + * intro and rendezvous points */ + if (options->OnionServiceSingleHopMode) { + log_warn(LD_CONFIG, + "OnionServiceSingleHopMode is set. Every hidden service on this " + "tor instance is NON-ANONYMOUS. If OnionServiceSingleHopMode is " + "disabled, Tor will refuse to launch hidden services from the " + "same directories, to protect 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, @@ -4295,6 +4396,19 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (old->OnionServiceSingleHopMode != new_val->OnionServiceSingleHopMode) { + *msg = tor_strdup("While Tor is running, changing " + "OnionServiceSingleHopMode is not allowed."); + return -1; + } + + if (old->OnionServiceNonAnonymousMode != + new_val->OnionServiceNonAnonymousMode) { + *msg = tor_strdup("While Tor is running, changing " + "OnionServiceNonAnonymousMode is not allowed."); + return -1; + } + if (old->DisableDebuggerAttachment && !new_val->DisableDebuggerAttachment) { *msg = tor_strdup("While Tor is running, disabling " 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 dc6b0930ca..8ad0f0c71d 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" @@ -2271,6 +2272,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); @@ -2314,10 +2316,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/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..76f9f38d3c 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,6 +2844,14 @@ tor_init(int argc, char *argv[]) return -1; } + /* The options are now initialised */ + const or_options_t *options = get_options(); + + 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."); + } + #ifndef _WIN32 if (geteuid()==0) log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, " diff --git a/src/or/or.h b/src/or/or.h index 574f184a13..dd3ab8a03a 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3701,6 +3701,27 @@ typedef struct { * they reach the normal circuit-build timeout. */ int CloseHSServiceRendCircuitsImmediatelyOnTimeout; + /** Onion Services in OnionServiceSingleHopMode 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. One-hop circuits make Single + * Onion servers easily locatable, but clients remain location-anonymous. + * OnionServiceSingleHopMode requires OnionServiceNonAnonymousMode 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 OnionServiceSingleHopMode; + /* Makes hidden service clients and servers non-anonymous on this tor + * instance. Allows the non-anonymous OnionServiceSingleHopMode. Enables + * direct connections in the hidden service protocol. + * Use rend_service_non_anonymous_mode() instead of using this option + * directly. + */ + int OnionServiceNonAnonymousMode; + 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 diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3a742fec0a..9d16e3b716 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 @@ -1552,3 +1549,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..12dd0f44e5 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -1067,3 +1067,51 @@ 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 Tor2webMode or OnionServiceSingleHopMode. + * + * Tor2webMode and OnionServiceSingleHopMode 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 fa147ba98e..0ba9205c6e 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" @@ -949,6 +950,208 @@ rend_service_update_descriptor(rend_service_t *service) } } +static const char *sos_poison_fname = "non_anonymous_hidden_service"; + +/* Allocate and return a string containing the path to the single onion + * poison file in service. + * The caller must free this path. + * Returns NULL if there is no directory for service. */ +static char * +sos_poison_path(const rend_service_t *service) +{ + char *poison_path; + + tor_assert(service->directory); + + tor_asprintf(&poison_path, "%s%s%s", + service->directory, PATH_SEPARATOR, sos_poison_fname); + + return poison_path; +} + +/** 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 = sos_poison_path(service); + tor_assert(poison_fname); + + 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 OnionServiceSingleHopMode, + * return 0, + * - If each service is not poisoned, and we are not in + * OnionServiceSingleHopMode, 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_allow_non_anonymous_connection(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(). When in single + * onion mode, add a file to this hidden service directory that marks it as a + * single onion hidden service. 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_allow_non_anonymous_connection(get_options())); + + int fd; + int retval = -1; + char *poison_fname = NULL; + + if (!service->directory) { + log_info(LD_REND, "Ephemeral HS started in OnionServiceSingleHopMode."); + 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 = 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 OnionServiceSingleHopMode. 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_allow_non_anonymous_connection(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. */ @@ -1372,6 +1575,19 @@ 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) +{ + /* The prefer_ipv6 argument to fascist_firewall_allows_address_addr is + * ignored, because pref_only is 0. */ + return (rend_service_allow_direct_connection(options) && + fascist_firewall_allows_address_addr(&ei->addr, ei->port, + FIREWALL_OR_CONNECTION, 0, 0)); +} + /****** * Handle cells ******/ @@ -1421,9 +1637,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 */ @@ -1627,6 +1841,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); @@ -1739,7 +1958,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, @@ -1764,6 +1986,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. */ @@ -2538,6 +2764,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); @@ -2566,6 +2796,11 @@ 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; + + if (rend_service_allow_direct_connection(get_options())) { + flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL; + } log_info(LD_REND, "Launching circuit to introduction point %s for service %s", @@ -2576,8 +2811,7 @@ rend_service_launch_establish_intro(rend_service_t *service, ++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); + intro->extend_info, flags); if (!launched) { log_info(LD_REND, @@ -2651,9 +2885,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); @@ -2720,6 +2952,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; @@ -2849,9 +3082,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 */ @@ -2871,6 +3102,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 @@ -3547,7 +3779,8 @@ rend_consider_services_intro_points(void) * pick it again in the next iteration. */ smartlist_add(exclude_nodes, (void*)node); intro = tor_malloc_zero(sizeof(rend_intro_point_t)); - intro->extend_info = extend_info_from_node(node, 0); + intro->extend_info = extend_info_from_node(node, + rend_service_allow_direct_connection(options)); intro->intro_key = crypto_pk_new(); const int fail = crypto_pk_generate_key(intro->intro_key); tor_assert(!fail); @@ -3592,8 +3825,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); @@ -3604,6 +3838,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 = @@ -3844,11 +4084,30 @@ 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. */ +/* Do the options allow onion services to make direct (non-anonymous) + * connections to introduction or rendezvous points? + * Returns true if tor is in OnionServiceSingleHopMode. */ 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; + return options->OnionServiceSingleHopMode ? 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. + * Returns true if tor is in OnionServiceSingleHopMode. */ +int +rend_service_reveal_startup_time(const or_options_t *options) +{ + return rend_service_allow_non_anonymous_connection(options); +} + +/* Is non-anonymous mode enabled using the OnionServiceNonAnonymousMode + * config option? */ +int +rend_service_non_anonymous_mode_enabled(const or_options_t *options) +{ + return options->OnionServiceNonAnonymousMode ? 1 : 0; } diff --git a/src/or/rendservice.h b/src/or/rendservice.h index d39d7bde5b..0cf448e207 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -164,6 +164,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 */ @@ -187,7 +192,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 |