aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/circuitbuild.c9
-rw-r--r--src/or/circuitstats.c28
-rw-r--r--src/or/config.c160
-rw-r--r--src/or/config.h2
-rw-r--r--src/or/connection_edge.c29
-rw-r--r--src/or/directory.c12
-rw-r--r--src/or/directory.h3
-rw-r--r--src/or/main.c13
-rw-r--r--src/or/or.h21
-rw-r--r--src/or/rendclient.c49
-rw-r--r--src/or/rendclient.h3
-rw-r--r--src/or/rendcommon.c48
-rw-r--r--src/or/rendcommon.h6
-rw-r--r--src/or/rendservice.c299
-rw-r--r--src/or/rendservice.h9
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