diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/channel.c | 8 | ||||
-rw-r--r-- | src/or/channel.h | 4 | ||||
-rw-r--r-- | src/or/channeltls.c | 2 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 227 | ||||
-rw-r--r-- | src/or/circuitbuild.h | 5 | ||||
-rw-r--r-- | src/or/circuitlist.c | 6 | ||||
-rw-r--r-- | src/or/config.c | 33 | ||||
-rw-r--r-- | src/or/connection.c | 304 | ||||
-rw-r--r-- | src/or/connection.h | 23 | ||||
-rw-r--r-- | src/or/connection_or.c | 4 | ||||
-rw-r--r-- | src/or/connection_or.h | 2 | ||||
-rw-r--r-- | src/or/dirserv.c | 14 | ||||
-rw-r--r-- | src/or/dirvote.c | 10 | ||||
-rw-r--r-- | src/or/dirvote.h | 6 | ||||
-rw-r--r-- | src/or/hibernate.c | 2 | ||||
-rw-r--r-- | src/or/main.c | 21 | ||||
-rw-r--r-- | src/or/main.h | 4 | ||||
-rw-r--r-- | src/or/networkstatus.c | 6 | ||||
-rw-r--r-- | src/or/nodelist.c | 28 | ||||
-rw-r--r-- | src/or/onion.c | 4 | ||||
-rw-r--r-- | src/or/or.h | 10 | ||||
-rw-r--r-- | src/or/policies.c | 4 | ||||
-rw-r--r-- | src/or/rendclient.c | 48 | ||||
-rw-r--r-- | src/or/rendservice.c | 11 | ||||
-rw-r--r-- | src/or/rendservice.h | 2 | ||||
-rw-r--r-- | src/or/router.c | 9 | ||||
-rw-r--r-- | src/or/routerlist.c | 51 | ||||
-rw-r--r-- | src/or/routerlist.h | 3 | ||||
-rw-r--r-- | src/or/shared_random.c | 4 |
29 files changed, 719 insertions, 136 deletions
diff --git a/src/or/channel.c b/src/or/channel.c index 87fa721089..6a78b21988 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -838,7 +838,7 @@ channel_free(channel_t *chan) } /* Call a free method if there is one */ - if (chan->free) chan->free(chan); + if (chan->free_fn) chan->free_fn(chan); channel_clear_remote_end(chan); @@ -878,7 +878,7 @@ channel_listener_free(channel_listener_t *chan_l) tor_assert(!(chan_l->registered)); /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); + if (chan_l->free_fn) chan_l->free_fn(chan_l); /* * We're in CLOSED or ERROR, so the incoming channel queue is already @@ -916,7 +916,7 @@ channel_force_free(channel_t *chan) } /* Call a free method if there is one */ - if (chan->free) chan->free(chan); + if (chan->free_fn) chan->free_fn(chan); channel_clear_remote_end(chan); @@ -958,7 +958,7 @@ channel_listener_force_free(channel_listener_t *chan_l) chan_l); /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); + if (chan_l->free_fn) chan_l->free_fn(chan_l); /* * The incoming list just gets emptied and freed; we request close on diff --git a/src/or/channel.h b/src/or/channel.h index 78e1b71014..a711b56d44 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -90,7 +90,7 @@ struct channel_s { /* Methods implemented by the lower layer */ /** Free a channel */ - void (*free)(channel_t *); + void (*free_fn)(channel_t *); /** Close an open channel */ void (*close)(channel_t *); /** Describe the transport subclass for this channel */ @@ -273,7 +273,7 @@ struct channel_listener_s { /* Methods implemented by the lower layer */ /** Free a channel */ - void (*free)(channel_listener_t *); + void (*free_fn)(channel_listener_t *); /** Close an open channel */ void (*close)(channel_listener_t *); /** Describe the transport subclass for this channel */ diff --git a/src/or/channeltls.c b/src/or/channeltls.c index a62f80ef91..9c2411ede8 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -117,7 +117,7 @@ channel_tls_common_init(channel_tls_t *tlschan) chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; chan->describe_transport = channel_tls_describe_transport_method; - chan->free = channel_tls_free_method; + chan->free_fn = channel_tls_free_method; chan->get_overhead_estimate = channel_tls_get_overhead_estimate_method; chan->get_remote_addr = channel_tls_get_remote_addr_method; chan->get_remote_descr = channel_tls_get_remote_descr_method; diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 14d40150db..12c75530e2 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -58,7 +58,6 @@ static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); static int count_acceptable_nodes(smartlist_t *routers); static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); -static int circuits_can_use_ntor(void); /** This function tries to get a channel to the specified endpoint, * and then calls command_setup_channel() to give it the right @@ -365,7 +364,7 @@ circuit_rep_hist_note_result(origin_circuit_t *circ) } while (hop!=circ->cpath); } -/** Return 1 iff at least one node in circ's cpath supports ntor. */ +/** Return 1 iff every node in circ's cpath definitely supports ntor. */ static int circuit_cpath_supports_ntor(const origin_circuit_t *circ) { @@ -373,16 +372,19 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ) cpath = head = circ->cpath; do { - if (cpath->extend_info && - !tor_mem_is_zero( - (const char*)cpath->extend_info->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) - return 1; + /* if the extend_info is missing, we can't tell if it supports ntor */ + if (!cpath->extend_info) { + return 0; + } + /* if the key is blank, it definitely doesn't support ntor */ + if (!extend_info_supports_ntor(cpath->extend_info)) { + return 0; + } cpath = cpath->next; } while (cpath != head); - return 0; + return 1; } /** Pick all the entries in our cpath. Stop and return 0 when we're @@ -390,41 +392,61 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ) static int onion_populate_cpath(origin_circuit_t *circ) { - int n_tries = 0; - const int using_ntor = circuits_can_use_ntor(); + int r = 0; -#define MAX_POPULATE_ATTEMPTS 32 + /* onion_extend_cpath assumes these are non-NULL */ + tor_assert(circ); + tor_assert(circ->build_state); - while (1) { - int r = onion_extend_cpath(circ); + while (r == 0) { + r = onion_extend_cpath(circ); if (r < 0) { log_info(LD_CIRC,"Generating cpath hop failed."); return -1; } - if (r == 1) { - /* This circuit doesn't need/shouldn't be forced to have an ntor hop */ - if (circ->build_state->desired_path_len <= 1 || ! using_ntor) - return 0; + } - /* This circuit has an ntor hop. great! */ - if (circuit_cpath_supports_ntor(circ)) - return 0; + /* The path is complete */ + tor_assert(r == 1); - /* No node in the circuit supports ntor. Have we already tried too many - * times? */ - if (++n_tries >= MAX_POPULATE_ATTEMPTS) - break; + /* Does every node in this path support ntor? */ + int path_supports_ntor = circuit_cpath_supports_ntor(circ); - /* Clear the path and retry */ - circuit_clear_cpath(circ); + /* We would like every path to support ntor, but we have to allow for some + * edge cases. */ + tor_assert(circuit_get_cpath_len(circ)); + if (circuit_can_use_tap(circ)) { + /* Circuits from clients to intro points, and hidden services to + * rend points do not support ntor, because the hidden service protocol + * does not include ntor onion keys. This is also true for Tor2web clients + * and Single Onion Services. */ + return 0; + } + + if (circuit_get_cpath_len(circ) == 1) { + /* Allow for bootstrapping: when we're fetching directly from a fallback, + * authority, or bridge, we have no way of knowing its ntor onion key + * before we connect to it. So instead, we try connecting, and end up using + * CREATE_FAST. */ + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + const node_t *node = node_get_by_id( + circ->cpath->extend_info->identity_digest); + /* If we don't know the node and its descriptor, we must be bootstrapping. + */ + if (!node || !node_has_descriptor(node)) { + return 0; } } - log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop " - "circuit with at least one node that supports ntor.", - MAX_POPULATE_ATTEMPTS, - circ->build_state->desired_path_len); - return -1; + if (BUG(!path_supports_ntor)) { + /* If we're building a multi-hop path, and it's not one of the HS or + * bootstrapping exceptions, and it doesn't support ntor, something has + * gone wrong. */ + return -1; + } + + return 0; } /** Create and return a new origin circuit. Initialize its purpose and @@ -757,10 +779,13 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ) tor_assert(circ->cpath); tor_assert(circ->cpath->extend_info); - if (!circ->cpath->extend_info->onion_key) - return 1; /* our hand is forced: only a create_fast will work. */ + if (!circuit_has_usable_onion_key(circ)) { + /* We don't have ntor, and we don't have or can't use TAP, + * so our hand is forced: only a create_fast will work. */ + return 1; + } if (public_server_mode(options)) { - /* We're a server, and we know an onion key. We can choose. + /* We're a server, and we have a usable onion key. We can choose. * Prefer to blend our circuit into the other circuits we are * creating on behalf of others. */ return 0; @@ -785,30 +810,20 @@ circuit_timeout_want_to_count_circ(origin_circuit_t *circ) && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN; } -/** Return true if the ntor handshake is enabled in the configuration, or if - * it's been set to "auto" in the configuration and it's enabled in the - * consensus. */ -static int -circuits_can_use_ntor(void) -{ - const or_options_t *options = get_options(); - if (options->UseNTorHandshake != -1) - return options->UseNTorHandshake; - return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1); -} - /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b> * directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b> - * accordingly. */ + * accordingly. + * Note that TAP handshakes are only used for direct connections: + * - from Tor2web to intro points not in the client's consensus, and + * - from Single Onions to rend points not in the service's consensus. + * This is checked in onion_populate_cpath. */ static void circuit_pick_create_handshake(uint8_t *cell_type_out, uint16_t *handshake_type_out, const extend_info_t *ei) { - /* XXXX029 Remove support for deciding to use TAP. */ - if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN) && - circuits_can_use_ntor()) { + /* XXXX030 Remove support for deciding to use TAP. */ + if (extend_info_supports_ntor(ei)) { *cell_type_out = CELL_CREATE2; *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR; return; @@ -822,7 +837,11 @@ circuit_pick_create_handshake(uint8_t *cell_type_out, * directly, and set *<b>handshake_type_out</b> accordingly. Decide whether, * in extending through <b>node</b> to do so, we should use an EXTEND2 or an * EXTEND cell to do so, and set *<b>cell_type_out</b> and - * *<b>create_cell_type_out</b> accordingly. */ + * *<b>create_cell_type_out</b> accordingly. + * Note that TAP handshakes are only used for extend handshakes: + * - from clients to intro points, and + * - from hidden services to rend points. + * This is checked in onion_populate_cpath. */ static void circuit_pick_extend_handshake(uint8_t *cell_type_out, uint8_t *create_cell_type_out, @@ -833,11 +852,25 @@ circuit_pick_extend_handshake(uint8_t *cell_type_out, uint8_t t; circuit_pick_create_handshake(&t, handshake_type_out, ei); - /* XXXX029 Remove support for deciding to use TAP. */ - if (node_prev && - *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP && + /* XXXX030 Remove support for deciding to use TAP. */ + + /* It is an error to extend if there is no previous node. */ + if (BUG(node_prev == NULL)) { + *cell_type_out = RELAY_COMMAND_EXTEND; + *create_cell_type_out = CELL_CREATE; + return; + } + + /* It is an error for a node with a known version to be so old it does not + * support ntor. */ + tor_assert_nonfatal(routerstatus_version_supports_ntor(node_prev->rs, 1)); + + /* Assume relays without tor versions or routerstatuses support ntor. + * The authorities enforce ntor support, and assuming and failing is better + * than allowing a malicious node to perform a protocol downgrade to TAP. */ + if (*handshake_type_out != ONION_HANDSHAKE_TYPE_TAP && (node_has_curve25519_onion_key(node_prev) || - (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) { + (routerstatus_version_supports_ntor(node_prev->rs, 1)))) { *cell_type_out = RELAY_COMMAND_EXTEND2; *create_cell_type_out = CELL_CREATE2; } else { @@ -2058,15 +2091,18 @@ count_acceptable_nodes(smartlist_t *nodes) if (! node->is_running) // log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i); continue; + /* XXX This clause makes us count incorrectly: if AllowInvalidRouters + * allows this node in some places, then we're getting an inaccurate + * count. For now, be conservative and don't count it. But later we + * should try to be smarter. */ if (! node->is_valid) // log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i); continue; if (! node_has_descriptor(node)) continue; - /* XXX This clause makes us count incorrectly: if AllowInvalidRouters - * allows this node in some places, then we're getting an inaccurate - * count. For now, be conservative and don't count it. But later we - * should try to be smarter. */ + /* The node has a descriptor, so we can just check the ntor key directly */ + if (!node_has_curve25519_onion_key(node)) + continue; ++num; } SMARTLIST_FOREACH_END(node); @@ -2356,6 +2392,14 @@ extend_info_from_node(const node_t *node, int for_direct_connect) log_warn(LD_CIRC, "Could not choose valid address for %s", node->ri ? node->ri->nickname : node->rs->nickname); + /* Every node we connect or extend to must support ntor */ + if (!node_has_curve25519_onion_key(node)) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Attempted to create extend_info for a node that does not support " + "ntor: %s", node_describe(node)); + return NULL; + } + if (valid_addr && node->ri) return extend_info_new(node->ri->nickname, node->identity, @@ -2441,3 +2485,66 @@ extend_info_addr_is_allowed(const tor_addr_t *addr) return 0; } +/* Does ei have a valid TAP key? */ +int +extend_info_supports_tap(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid TAP keys are not NULL */ + return ei->onion_key != NULL; +} + +/* Does ei have a valid ntor key? */ +int +extend_info_supports_ntor(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid ntor keys have at least one non-zero byte */ + return !tor_mem_is_zero( + (const char*)ei->curve25519_onion_key.public_key, + CURVE25519_PUBKEY_LEN); +} + +/* Is circuit purpose allowed to use the deprecated TAP encryption protocol? + * The hidden service protocol still uses TAP for some connections, because + * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ +static int +circuit_purpose_can_use_tap_impl(uint8_t purpose) +{ + return (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND || + purpose == CIRCUIT_PURPOSE_C_INTRODUCING); +} + +/* Is circ allowed to use the deprecated TAP encryption protocol? + * The hidden service protocol still uses TAP for some connections, because + * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ +int +circuit_can_use_tap(const origin_circuit_t *circ) +{ + tor_assert(circ); + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + return (circuit_purpose_can_use_tap_impl(circ->base_.purpose) && + extend_info_supports_tap(circ->cpath->extend_info)); +} + +/* Does circ have an onion key which it's allowed to use? */ +int +circuit_has_usable_onion_key(const origin_circuit_t *circ) +{ + tor_assert(circ); + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + return (extend_info_supports_ntor(circ->cpath->extend_info) || + circuit_can_use_tap(circ)); +} + +/* Does ei have an onion key which it would prefer to use? + * Currently, we prefer ntor keys*/ +int +extend_info_has_preferred_onion_key(const extend_info_t* ei) +{ + tor_assert(ei); + return extend_info_supports_ntor(ei); +} + diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 7f5fd511a9..1244601f71 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -54,6 +54,11 @@ extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); void extend_info_free(extend_info_t *info); int extend_info_addr_is_allowed(const tor_addr_t *addr); +int extend_info_supports_tap(const extend_info_t* ei); +int extend_info_supports_ntor(const extend_info_t* ei); +int circuit_can_use_tap(const origin_circuit_t *circ); +int circuit_has_usable_onion_key(const origin_circuit_t *circ); +int extend_info_has_preferred_onion_key(const extend_info_t* ei); const node_t *build_state_get_exit_node(cpath_build_state_t *state); const char *build_state_get_exit_nickname(cpath_build_state_t *state); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 5c691644a4..3c92baa274 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -1613,7 +1613,8 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, return best; } -/** Return the number of hops in circuit's path. */ +/** Return the number of hops in circuit's path. If circ has no entries, + * or is NULL, returns 0. */ int circuit_get_cpath_len(origin_circuit_t *circ) { @@ -1629,7 +1630,8 @@ circuit_get_cpath_len(origin_circuit_t *circ) } /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there - * aren't that many hops in the list. */ + * aren't that many hops in the list. <b>hopnum</b> starts at 1. + * Returns NULL if <b>hopnum</b> is 0 or negative. */ crypt_path_t * circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum) { diff --git a/src/or/config.c b/src/or/config.c index 10002ff620..9c5514f1da 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -211,6 +211,7 @@ static config_var_t option_vars_[] = { V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), V(DataDirectoryGroupReadable, BOOL, "0"), + V(DisableOOSCheck, BOOL, "1"), V(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, "0"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), @@ -437,7 +438,7 @@ static config_var_t option_vars_[] = { V(UseEntryGuardsAsDirGuards, BOOL, "1"), V(UseGuardFraction, AUTOBOOL, "auto"), V(UseMicrodescriptors, AUTOBOOL, "auto"), - V(UseNTorHandshake, AUTOBOOL, "1"), + OBSOLETE("UseNTorHandshake"), V(User, STRING, NULL), OBSOLETE("UserspaceIOCPBuffers"), V(AuthDirSharedRandomness, BOOL, "1"), @@ -612,7 +613,6 @@ static const config_deprecation_t option_deprecation_notes_[] = { "to accidentally lose your anonymity by leaking DNS information" }, { "TLSECGroup", "The default is a nice secure choice; the other option " "is less secure." }, - { "UseNTorHandshake", "The ntor handshake should always be used." }, { "ControlListenAddress", "Use ControlPort instead." }, { "DirListenAddress", "Use DirPort instead, possibly with the " "NoAdvertise sub-option" }, @@ -1374,6 +1374,35 @@ options_act_reversible(const or_options_t *old_options, char **msg) connection_mark_for_close(conn); } }); + + if (set_conn_limit) { + /* + * If we adjusted the conn limit, recompute the OOS threshold too + * + * How many possible sockets to keep in reserve? If we have lots of + * possible sockets, keep this below a limit and set ConnLimit_high_thresh + * very close to ConnLimit_, but if ConnLimit_ is low, shrink it in + * proportion. + * + * Somewhat arbitrarily, set socks_in_reserve to 5% of ConnLimit_, but + * cap it at 64. + */ + int socks_in_reserve = options->ConnLimit_ / 20; + if (socks_in_reserve > 64) socks_in_reserve = 64; + + options->ConnLimit_high_thresh = options->ConnLimit_ - socks_in_reserve; + options->ConnLimit_low_thresh = (options->ConnLimit_ / 4) * 3; + log_info(LD_GENERAL, + "Recomputed OOS thresholds: ConnLimit %d, ConnLimit_ %d, " + "ConnLimit_high_thresh %d, ConnLimit_low_thresh %d", + options->ConnLimit, options->ConnLimit_, + options->ConnLimit_high_thresh, + options->ConnLimit_low_thresh); + + /* Give the OOS handler a chance with the new thresholds */ + connection_check_oos(get_n_open_sockets(), 0); + } + goto done; rollback: diff --git a/src/or/connection.c b/src/or/connection.c index 68e442df54..5ecd1ad7bf 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -754,9 +754,9 @@ connection_mark_for_close_(connection_t *conn, int line, const char *file) * For all other cases, use connection_mark_and_flush() instead, which * checks for or_connection_t properly, instead. See below. */ -void -connection_mark_for_close_internal_(connection_t *conn, - int line, const char *file) +MOCK_IMPL(void, +connection_mark_for_close_internal_, (connection_t *conn, + int line, const char *file)) { assert_connection_ok(conn,0); tor_assert(line); @@ -1090,6 +1090,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, int start_reading = 0; static int global_next_session_group = SESSION_GROUP_FIRST_AUTO; tor_addr_t addr; + int exhaustion = 0; if (listensockaddr->sa_family == AF_INET || listensockaddr->sa_family == AF_INET6) { @@ -1108,6 +1109,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, int e = tor_socket_errno(s); if (ERRNO_IS_RESOURCE_LIMIT(e)) { warn_too_many_conns(); + /* + * We'll call the OOS handler at the error exit, so set the + * exhaustion flag for it. + */ + exhaustion = 1; } else { log_warn(LD_NET, "Socket creation failed: %s", tor_socket_strerror(e)); @@ -1226,6 +1232,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, int e = tor_socket_errno(s); if (ERRNO_IS_RESOURCE_LIMIT(e)) { warn_too_many_conns(); + /* + * We'll call the OOS handler at the error exit, so set the + * exhaustion flag for it. + */ + exhaustion = 1; } else { log_warn(LD_NET,"Socket creation failed: %s.", strerror(e)); } @@ -1344,6 +1355,12 @@ connection_listener_new(const struct sockaddr *listensockaddr, dnsserv_configure_listener(conn); } + /* + * Normal exit; call the OOS handler since connection count just changed; + * the exhaustion flag will always be zero here though. + */ + connection_check_oos(get_n_open_sockets(), 0); + return conn; err: @@ -1352,6 +1369,9 @@ connection_listener_new(const struct sockaddr *listensockaddr, if (conn) connection_free(conn); + /* Call the OOS handler, indicate if we saw an exhaustion-related error */ + connection_check_oos(get_n_open_sockets(), exhaustion); + return NULL; } @@ -1442,21 +1462,34 @@ connection_handle_listener_read(connection_t *conn, int new_type) if (!SOCKET_OK(news)) { /* accept() error */ int e = tor_socket_errno(conn->s); if (ERRNO_IS_ACCEPT_EAGAIN(e)) { - return 0; /* they hung up before we could accept(). that's fine. */ + /* + * they hung up before we could accept(). that's fine. + * + * give the OOS handler a chance to run though + */ + connection_check_oos(get_n_open_sockets(), 0); + return 0; } else if (ERRNO_IS_RESOURCE_LIMIT(e)) { warn_too_many_conns(); + /* Exhaustion; tell the OOS handler */ + connection_check_oos(get_n_open_sockets(), 1); return 0; } /* else there was a real error. */ log_warn(LD_NET,"accept() failed: %s. Closing listener.", tor_socket_strerror(e)); connection_mark_for_close(conn); + /* Tell the OOS handler about this too */ + connection_check_oos(get_n_open_sockets(), 0); return -1; } log_debug(LD_NET, "Connection accepted on socket %d (child of fd %d).", (int)news,(int)conn->s); + /* We accepted a new conn; run OOS handler */ + connection_check_oos(get_n_open_sockets(), 0); + if (make_socket_reuseable(news) < 0) { if (tor_socket_errno(news) == EINVAL) { /* This can happen on OSX if we get a badly timed shutdown. */ @@ -1661,12 +1694,18 @@ connection_connect_sockaddr,(connection_t *conn, s = tor_open_socket_nonblocking(protocol_family, SOCK_STREAM, proto); if (! SOCKET_OK(s)) { + /* + * Early OOS handler calls; it matters if it's an exhaustion-related + * error or not. + */ *socket_error = tor_socket_errno(s); if (ERRNO_IS_RESOURCE_LIMIT(*socket_error)) { warn_too_many_conns(); + connection_check_oos(get_n_open_sockets(), 1); } else { log_warn(LD_NET,"Error creating network socket: %s", tor_socket_strerror(*socket_error)); + connection_check_oos(get_n_open_sockets(), 0); } return -1; } @@ -1676,6 +1715,13 @@ connection_connect_sockaddr,(connection_t *conn, tor_socket_strerror(errno)); } + /* + * We've got the socket open; give the OOS handler a chance to check + * against configuured maximum socket number, but tell it no exhaustion + * failure. + */ + connection_check_oos(get_n_open_sockets(), 0); + if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) { *socket_error = tor_socket_errno(s); log_warn(LD_NET,"Error binding network socket: %s", @@ -4454,6 +4500,256 @@ connection_reached_eof(connection_t *conn) } } +/** Comparator for the two-orconn case in OOS victim sort */ +static int +oos_victim_comparator_for_orconns(or_connection_t *a, or_connection_t *b) +{ + int a_circs, b_circs; + /* Fewer circuits == higher priority for OOS kill, sort earlier */ + + a_circs = connection_or_get_num_circuits(a); + b_circs = connection_or_get_num_circuits(b); + + if (a_circs < b_circs) return 1; + else if (a_circs > b_circs) return -1; + else return 0; +} + +/** Sort comparator for OOS victims; better targets sort before worse + * ones. */ +static int +oos_victim_comparator(const void **a_v, const void **b_v) +{ + connection_t *a = NULL, *b = NULL; + + /* Get connection pointers out */ + + a = (connection_t *)(*a_v); + b = (connection_t *)(*b_v); + + tor_assert(a != NULL); + tor_assert(b != NULL); + + /* + * We always prefer orconns as victims currently; we won't even see + * these non-orconn cases, but if we do, sort them after orconns. + */ + if (a->type == CONN_TYPE_OR && b->type == CONN_TYPE_OR) { + return oos_victim_comparator_for_orconns(TO_OR_CONN(a), TO_OR_CONN(b)); + } else { + /* + * One isn't an orconn; if one is, it goes first. We currently have no + * opinions about cases where neither is an orconn. + */ + if (a->type == CONN_TYPE_OR) return -1; + else if (b->type == CONN_TYPE_OR) return 1; + else return 0; + } +} + +/** Pick n victim connections for the OOS handler and return them in a + * smartlist. + */ +MOCK_IMPL(STATIC smartlist_t *, +pick_oos_victims, (int n)) +{ + smartlist_t *eligible = NULL, *victims = NULL; + smartlist_t *conns; + int conn_counts_by_type[CONN_TYPE_MAX_ + 1], i; + + /* + * Big damn assumption (someone improve this someday!): + * + * Socket exhaustion normally happens on high-volume relays, and so + * most of the connections involved are orconns. We should pick victims + * by assembling a list of all orconns, and sorting them in order of + * how much 'damage' by some metric we'd be doing by dropping them. + * + * If we move on from orconns, we should probably think about incoming + * directory connections next, or exit connections. Things we should + * probably never kill are controller connections and listeners. + * + * This function will count how many connections of different types + * exist and log it for purposes of gathering data on typical OOS + * situations to guide future improvements. + */ + + /* First, get the connection array */ + conns = get_connection_array(); + /* + * Iterate it and pick out eligible connection types, and log some stats + * along the way. + */ + eligible = smartlist_new(); + memset(conn_counts_by_type, 0, sizeof(conn_counts_by_type)); + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { + /* Bump the counter */ + tor_assert(c->type <= CONN_TYPE_MAX_); + ++(conn_counts_by_type[c->type]); + + /* Skip anything without a socket we can free */ + if (!(SOCKET_OK(c->s))) { + continue; + } + + /* Skip anything we would count as moribund */ + if (connection_is_moribund(c)) { + continue; + } + + switch (c->type) { + case CONN_TYPE_OR: + /* We've got an orconn, it's eligible to be OOSed */ + smartlist_add(eligible, c); + break; + default: + /* We don't know what to do with it, ignore it */ + break; + } + } SMARTLIST_FOREACH_END(c); + + /* Log some stats */ + if (smartlist_len(conns) > 0) { + /* At least one counter must be non-zero */ + log_info(LD_NET, "Some stats on conn types seen during OOS follow"); + for (i = CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) { + /* Did we see any? */ + if (conn_counts_by_type[i] > 0) { + log_info(LD_NET, "%s: %d conns", + conn_type_to_string(i), + conn_counts_by_type[i]); + } + } + log_info(LD_NET, "Done with OOS conn type stats"); + } + + /* Did we find more eligible targets than we want to kill? */ + if (smartlist_len(eligible) > n) { + /* Sort the list in order of target preference */ + smartlist_sort(eligible, oos_victim_comparator); + /* Pick first n as victims */ + victims = smartlist_new(); + for (i = 0; i < n; ++i) { + smartlist_add(victims, smartlist_get(eligible, i)); + } + /* Free the original list */ + smartlist_free(eligible); + } else { + /* No, we can just call them all victims */ + victims = eligible; + } + + return victims; +} + +/** Kill a list of connections for the OOS handler. */ +MOCK_IMPL(STATIC void, +kill_conn_list_for_oos, (smartlist_t *conns)) +{ + if (!conns) return; + + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { + /* Make sure the channel layer gets told about orconns */ + if (c->type == CONN_TYPE_OR) { + connection_or_close_for_error(TO_OR_CONN(c), 1); + } else { + connection_mark_for_close(c); + } + } SMARTLIST_FOREACH_END(c); + + log_notice(LD_NET, + "OOS handler marked %d connections", + smartlist_len(conns)); +} + +/** Out-of-Sockets handler; n_socks is the current number of open + * sockets, and failed is non-zero if a socket exhaustion related + * error immediately preceded this call. This is where to do + * circuit-killing heuristics as needed. + */ +void +connection_check_oos(int n_socks, int failed) +{ + int target_n_socks = 0, moribund_socks, socks_to_kill; + smartlist_t *conns; + + /* Early exit: is OOS checking disabled? */ + if (get_options()->DisableOOSCheck) { + return; + } + + /* Sanity-check args */ + tor_assert(n_socks >= 0); + + /* + * Make some log noise; keep it at debug level since this gets a chance + * to run on every connection attempt. + */ + log_debug(LD_NET, + "Running the OOS handler (%d open sockets, %s)", + n_socks, (failed != 0) ? "exhaustion seen" : "no exhaustion"); + + /* + * Check if we're really handling an OOS condition, and if so decide how + * many sockets we want to get down to. Be sure we check if the threshold + * is distinct from zero first; it's possible for this to be called a few + * times before we've finished reading the config. + */ + if (n_socks >= get_options()->ConnLimit_high_thresh && + get_options()->ConnLimit_high_thresh != 0 && + get_options()->ConnLimit_ != 0) { + /* Try to get down to the low threshold */ + target_n_socks = get_options()->ConnLimit_low_thresh; + log_notice(LD_NET, + "Current number of sockets %d is greater than configured " + "limit %d; OOS handler trying to get down to %d", + n_socks, get_options()->ConnLimit_high_thresh, + target_n_socks); + } else if (failed) { + /* + * If we're not at the limit but we hit a socket exhaustion error, try to + * drop some (but not as aggressively as ConnLimit_low_threshold, which is + * 3/4 of ConnLimit_) + */ + target_n_socks = (n_socks * 9) / 10; + log_notice(LD_NET, + "We saw socket exhaustion at %d open sockets; OOS handler " + "trying to get down to %d", + n_socks, target_n_socks); + } + + if (target_n_socks > 0) { + /* + * It's an OOS! + * + * Count moribund sockets; it's be important that anything we decide + * to get rid of here but don't immediately close get counted as moribund + * on subsequent invocations so we don't try to kill too many things if + * connection_check_oos() gets called multiple times. + */ + moribund_socks = connection_count_moribund(); + + if (moribund_socks < n_socks - target_n_socks) { + socks_to_kill = n_socks - target_n_socks - moribund_socks; + + conns = pick_oos_victims(socks_to_kill); + if (conns) { + kill_conn_list_for_oos(conns); + log_notice(LD_NET, + "OOS handler killed %d conns", smartlist_len(conns)); + smartlist_free(conns); + } else { + log_notice(LD_NET, "OOS handler failed to pick any victim conns"); + } + } else { + log_notice(LD_NET, + "Not killing any sockets for OOS because there are %d " + "already moribund, and we only want to eliminate %d", + moribund_socks, n_socks - target_n_socks); + } + } +} + /** Log how many bytes are used by buffers of different kinds and sizes. */ void connection_dump_buffer_mem_stats(int severity) diff --git a/src/or/connection.h b/src/or/connection.h index f8e0f73246..d25e002fa4 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -34,8 +34,8 @@ void connection_about_to_close_connection(connection_t *conn); void connection_close_immediate(connection_t *conn); void connection_mark_for_close_(connection_t *conn, int line, const char *file); -void connection_mark_for_close_internal_(connection_t *conn, - int line, const char *file); +MOCK_DECL(void, connection_mark_for_close_internal_, + (connection_t *conn, int line, const char *file)); #define connection_mark_for_close(c) \ connection_mark_for_close_((c), __LINE__, SHORT_FILE__) @@ -247,6 +247,22 @@ void clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted, log_domain_mask_t domain, const char *received, const char *source); +/** Check if a connection is on the way out so the OOS handler doesn't try + * to kill more than it needs. */ +static inline int +connection_is_moribund(connection_t *conn) +{ + if (conn != NULL && + (conn->conn_array_index < 0 || + conn->marked_for_close)) { + return 1; + } else { + return 0; + } +} + +void connection_check_oos(int n_socks, int failed); + #ifdef CONNECTION_PRIVATE STATIC void connection_free_(connection_t *conn); @@ -265,6 +281,9 @@ MOCK_DECL(STATIC int,connection_connect_sockaddr, const struct sockaddr *bindaddr, socklen_t bindaddr_len, int *socket_error)); +MOCK_DECL(STATIC void, kill_conn_list_for_oos, (smartlist_t *conns)); +MOCK_DECL(STATIC smartlist_t *, pick_oos_victims, (int n)); + #endif #endif diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 1f0c4bdef5..72d8e13e90 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -394,8 +394,8 @@ connection_or_change_state(or_connection_t *conn, uint8_t state) * be an or_connection_t field, but it got moved to channel_t and we * shouldn't maintain two copies. */ -int -connection_or_get_num_circuits(or_connection_t *conn) +MOCK_IMPL(int, +connection_or_get_num_circuits, (or_connection_t *conn)) { tor_assert(conn); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index e2ec47a4f2..2e8c6066cc 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -64,7 +64,7 @@ void connection_or_init_conn_from_address(or_connection_t *conn, int connection_or_client_learned_peer_id(or_connection_t *conn, const uint8_t *peer_id); time_t connection_or_client_used(or_connection_t *conn); -int connection_or_get_num_circuits(or_connection_t *conn); +MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn)); void or_handshake_state_free(or_handshake_state_t *state); void or_handshake_state_record_cell(or_connection_t *conn, or_handshake_state_t *state, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 64ebde6fdd..ff50ca4417 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -255,6 +255,20 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, return FP_REJECT; } + /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc, + * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha. + * But just in case a relay doesn't provide or lies about its version, or + * doesn't include an ntor key in its descriptor, check that it exists, + * and is non-zero (clients check that it's non-zero before using it). */ + if (!routerinfo_has_curve25519_onion_key(router)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s is missing an ntor curve25519 onion " + "key.", router_describe(router)); + if (msg) + *msg = "Missing ntor curve25519 onion key. Please upgrade!"; + return FP_REJECT; + } + if (router->cache_info.signing_key_cert) { /* This has an ed25519 identity key. */ if (KEYPIN_MISMATCH == diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 9748f4ae4d..ae869c9064 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -1582,7 +1582,7 @@ networkstatus_compute_consensus(smartlist_t *votes, const char *chosen_version; const char *chosen_name = NULL; int exitsummary_disagreement = 0; - int is_named = 0, is_unnamed = 0, is_running = 0; + int is_named = 0, is_unnamed = 0, is_running = 0, is_valid = 0; int is_guard = 0, is_exit = 0, is_bad_exit = 0; int naming_conflict = 0; int n_listing = 0; @@ -1733,6 +1733,8 @@ networkstatus_compute_consensus(smartlist_t *votes, is_running = 1; else if (!strcmp(fl, "BadExit")) is_bad_exit = 1; + else if (!strcmp(fl, "Valid")) + is_valid = 1; } } } SMARTLIST_FOREACH_END(fl); @@ -1742,6 +1744,12 @@ networkstatus_compute_consensus(smartlist_t *votes, if (!is_running) continue; + /* Starting with consensus method 24, we don't list servers + * that are not valid in a consensus. See Proposal 272 */ + if (!is_valid && + consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES) + continue; + /* Pick the version. */ if (smartlist_len(versions)) { sort_version_list(versions, 0); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index a1f71ce4bb..06bfe671bd 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -55,7 +55,7 @@ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 23 +#define MAX_SUPPORTED_CONSENSUS_METHOD 24 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -99,6 +99,10 @@ * value(s). */ #define MIN_METHOD_FOR_SHARED_RANDOM 23 +/** Lowest consensus method where authorities drop all nodes that don't get + * the Valid flag. */ +#define MIN_METHOD_FOR_EXCLUDING_INVALID_NODES 24 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 209aae01cf..7e25306234 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -692,7 +692,7 @@ read_bandwidth_usage(void) int res; res = unlink(fname); - if (res != 0) { + if (res != 0 && errno != ENOENT) { log_warn(LD_FS, "Failed to unlink %s: %s", fname, strerror(errno)); diff --git a/src/or/main.c b/src/or/main.c index 4dbd9a005b..03c2b7ed58 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -381,8 +381,8 @@ connection_in_array(connection_t *conn) /** Set <b>*array</b> to an array of all connections. <b>*array</b> must not * be modified. */ -smartlist_t * -get_connection_array(void) +MOCK_IMPL(smartlist_t *, +get_connection_array, (void)) { if (!connection_array) connection_array = smartlist_new(); @@ -651,6 +651,23 @@ close_closeable_connections(void) } } +/** Count moribund connections for the OOS handler */ +MOCK_IMPL(int, +connection_count_moribund, (void)) +{ + int moribund = 0; + + /* + * Count things we'll try to kill when close_closeable_connections() + * runs next. + */ + SMARTLIST_FOREACH_BEGIN(closeable_connection_lst, connection_t *, conn) { + if (SOCKET_OK(conn->s) && connection_is_moribund(conn)) ++moribund; + } SMARTLIST_FOREACH_END(conn); + + return moribund; +} + /** Libevent callback: this gets invoked when (connection_t*)<b>conn</b> has * some data to read. */ static void diff --git a/src/or/main.h b/src/or/main.h index 31a22de424..0220ae3c57 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -25,7 +25,7 @@ int connection_in_array(connection_t *conn); void add_connection_to_closeable_list(connection_t *conn); int connection_is_on_closeable_list(connection_t *conn); -smartlist_t *get_connection_array(void); +MOCK_DECL(smartlist_t *, get_connection_array, (void)); MOCK_DECL(uint64_t,get_bytes_read,(void)); MOCK_DECL(uint64_t,get_bytes_written,(void)); @@ -47,6 +47,8 @@ MOCK_DECL(void,connection_start_writing,(connection_t *conn)); void connection_stop_reading_from_linked_conn(connection_t *conn); +MOCK_DECL(int, connection_count_moribund, (void)); + void directory_all_unreachable(time_t now); void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index fe4b4562ff..72af505d19 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -2275,6 +2275,12 @@ client_would_use_router(const routerstatus_t *rs, time_t now, /* We'd drop it immediately for being too old. */ return 0; } + if (!routerstatus_version_supports_ntor(rs, 1)) { + /* We'd ignore it because it doesn't support ntor. + * If we don't know the version, download the descriptor so we can + * check if it supports ntor. */ + return 0; + } return 1; } diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 7b64cafd79..070e2e9e0d 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -1173,14 +1173,38 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out) } } +/** Return true iff <b>md</b> has a curve25519 onion key. + * Use node_has_curve25519_onion_key() instead of calling this directly. */ +static int +microdesc_has_curve25519_onion_key(const microdesc_t *md) +{ + if (!md) { + return 0; + } + + if (!md->onion_curve25519_pkey) { + return 0; + } + + if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key, + CURVE25519_PUBKEY_LEN)) { + return 0; + } + + return 1; +} + /** Return true iff <b>node</b> has a curve25519 onion key. */ int node_has_curve25519_onion_key(const node_t *node) { + if (!node) + return 0; + if (node->ri) - return node->ri->onion_curve25519_pkey != NULL; + return routerinfo_has_curve25519_onion_key(node->ri); else if (node->md) - return node->md->onion_curve25519_pkey != NULL; + return microdesc_has_curve25519_onion_key(node->md); else return 0; } diff --git a/src/or/onion.c b/src/or/onion.c index 5495074a83..8a566af766 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -11,6 +11,7 @@ **/ #include "or.h" +#include "circuitbuild.h" #include "circuitlist.h" #include "config.h" #include "cpuworker.h" @@ -438,8 +439,7 @@ onion_skin_create(int type, r = CREATE_FAST_LEN; break; case ONION_HANDSHAKE_TYPE_NTOR: - if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) + if (!extend_info_supports_ntor(node)) return -1; if (onion_skin_ntor_create((const uint8_t*)node->identity_digest, &node->curve25519_onion_key, diff --git a/src/or/or.h b/src/or/or.h index d83a921ae9..5b9b007ac1 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3701,6 +3701,10 @@ typedef struct { 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 + * have this many. */ + int ConnLimit_low_thresh; /**< try to get down to here after socket + * exhaustion. */ int RunAsDaemon; /**< If true, run in the background. (Unix only) */ int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */ smartlist_t *FirewallPorts; /**< Which ports our firewall allows @@ -4382,9 +4386,6 @@ typedef struct { char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */ - /** Autobool: should we use the ntor handshake if we can? */ - int UseNTorHandshake; - /** Fraction: */ double PathsNeededToBuildCircuits; @@ -4456,6 +4457,9 @@ typedef struct { * participate in the protocol. If on (default), a flag is added to the * vote indicating participation. */ int AuthDirSharedRandomness; + + /** If 1, we skip all OOS checks. */ + int DisableOOSCheck; } or_options_t; /** Persistent state for an onion router, as saved to disk. */ diff --git a/src/or/policies.c b/src/or/policies.c index 07f256f5cc..44a46d2fe2 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -2119,8 +2119,10 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port) if (subnet_status[i] != 0) continue; /* We already reject some part of this /8 */ tor_addr_from_ipv4h(&addr, i<<24); - if (tor_addr_is_internal(&addr, 0)) + if (tor_addr_is_internal(&addr, 0) && + !get_options()->DirAllowPrivateAddresses) { continue; /* Local or non-routable addresses */ + } if (p->policy_type == ADDR_POLICY_ACCEPT) { if (p->maskbits > 8) continue; /* Narrower than a /8. */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3468b07561..263dd3d876 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1368,40 +1368,20 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, i = crypto_rand_int(smartlist_len(usable_nodes)); intro = smartlist_get(usable_nodes, i); - /* Do we need to look up the router or is the extend info complete? */ - if (!intro->extend_info->onion_key) { - const node_t *node; - extend_info_t *new_extend_info; - if (tor_digest_is_zero(intro->extend_info->identity_digest)) - node = node_get_by_hex_id(intro->extend_info->nickname); - else - node = node_get_by_id(intro->extend_info->identity_digest); - if (!node) { - log_info(LD_REND, "Unknown router with nickname '%s'; trying another.", - intro->extend_info->nickname); - smartlist_del(usable_nodes, i); - goto again; - } -#ifdef ENABLE_TOR2WEB_MODE - new_extend_info = extend_info_from_node(node, options->Tor2webMode); -#else - new_extend_info = extend_info_from_node(node, 0); -#endif - if (!new_extend_info) { - const char *alternate_reason = ""; -#ifdef ENABLE_TOR2WEB_MODE - alternate_reason = ", or we cannot connect directly to it"; -#endif - log_info(LD_REND, "We don't have a descriptor for the intro-point relay " - "'%s'%s; trying another.", - extend_info_describe(intro->extend_info), alternate_reason); - smartlist_del(usable_nodes, i); - goto again; - } else { - extend_info_free(intro->extend_info); - intro->extend_info = new_extend_info; - } - tor_assert(intro->extend_info != NULL); + if (BUG(!intro->extend_info)) { + /* This should never happen, but it isn't fatal, just try another */ + smartlist_del(usable_nodes, i); + goto again; + } + /* All version 2 HS descriptors come with a TAP onion key. + * Clients used to try to get the TAP onion key from the consensus, but this + * meant that hidden services could discover which consensus clients have. */ + if (!extend_info_supports_tap(intro->extend_info)) { + log_info(LD_REND, "The HS descriptor is missing a TAP onion key for the " + "intro-point relay '%s'; trying another.", + safe_str_client(extend_info_describe(intro->extend_info))); + smartlist_del(usable_nodes, i); + goto again; } /* Check if we should refuse to talk to this router. */ if (strict && diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 4c88f1fa5f..8d3a7d704c 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -508,7 +508,7 @@ rend_config_services(const or_options_t *options, int validate_only) if (!strcasecmp(line->key, "HiddenServiceDir")) { if (service) { /* register the one we just finished parsing */ if (validate_only) - rend_service_free(service); + rend_service_free(service); else rend_add_service(service); } @@ -3896,3 +3896,12 @@ 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. */ +int +rend_service_allow_direct_connection(const or_options_t *options) +{ + (void)options; + return 0; +} + diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 4966cb0302..1622086a99 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -131,5 +131,7 @@ 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); + #endif diff --git a/src/or/router.c b/src/or/router.c index e9961d4594..8fa5799896 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -452,7 +452,8 @@ init_key_from_file(const char *fname, int generate, int severity, goto error; } } else { - log_info(LD_GENERAL, "No key found in \"%s\"", fname); + tor_log(severity, LD_GENERAL, "No key found in \"%s\"", fname); + goto error; } return prkey; case FN_FILE: @@ -560,7 +561,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, fname = get_datadir_fname2("keys", legacy ? "legacy_signing_key" : "authority_signing_key"); - signing_key = init_key_from_file(fname, 0, LOG_INFO, 0); + signing_key = init_key_from_file(fname, 0, LOG_ERR, 0); if (!signing_key) { log_warn(LD_DIR, "No version 3 directory key found in %s", fname); goto done; @@ -2836,6 +2837,10 @@ router_dump_router_to_string(routerinfo_t *router, (const char *)router->onion_curve25519_pkey->public_key, CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); + } else { + /* Authorities will start rejecting relays without ntor keys in 0.2.9 */ + log_err(LD_BUG, "A relay must have an ntor onion key"); + goto err; } /* Write the exit policy to the end of 's'. */ diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 1773f1d05c..74b8d1b1d3 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -2260,10 +2260,16 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid, continue; if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) continue; - /* Choose a node with an OR address that matches the firewall rules, - * if we are making a direct connection */ + /* Don't choose nodes if we are certain they can't do ntor */ + if (node->rs && !routerstatus_version_supports_ntor(node->rs, 1)) + continue; + if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) + continue; + /* Choose a node with an OR address that matches the firewall rules */ if (direct_conn && check_reach && - !fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr)) + !fascist_firewall_allows_node(node, + FIREWALL_OR_CONNECTION, + pref_addr)) continue; smartlist_add(sl, (void *)node); @@ -5497,6 +5503,45 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey, return r; } +/* Does ri have a valid ntor onion key? + * Valid ntor onion keys exist and have at least one non-zero byte. */ +int +routerinfo_has_curve25519_onion_key(const routerinfo_t *ri) +{ + if (!ri) { + return 0; + } + + if (!ri->onion_curve25519_pkey) { + return 0; + } + + if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key, + CURVE25519_PUBKEY_LEN)) { + return 0; + } + + return 1; +} + +/* Is rs running a tor version known to support ntor? + * If allow_unknown_versions is true, return true if the version is unknown. + * Otherwise, return false if the version is unknown. */ +int +routerstatus_version_supports_ntor(const routerstatus_t *rs, + int allow_unknown_versions) +{ + if (!rs) { + return allow_unknown_versions; + } + + if (!rs->version_known) { + return allow_unknown_versions; + } + + return rs->version_supports_extend2_cells; +} + /** Assert that the internal representation of <b>rl</b> is * self-consistent. */ void diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 72ab6d9bf3..47e5445e57 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -206,6 +206,9 @@ int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri, extrainfo_t *ei, signed_descriptor_t *sd, const char **msg); +int routerinfo_has_curve25519_onion_key(const routerinfo_t *ri); +int routerstatus_version_supports_ntor(const routerstatus_t *rs, + int allow_unknown_versions); void routerlist_assert_ok(const routerlist_t *rl); const char *esc_router_info(const routerinfo_t *router); diff --git a/src/or/shared_random.c b/src/or/shared_random.c index 19564f5924..e672a416be 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -578,8 +578,8 @@ commit_is_authoritative(const sr_commit_t *commit, tor_assert(commit); tor_assert(voter_key); - return !memcmp(commit->rsa_identity, voter_key, - sizeof(commit->rsa_identity)); + return fast_memeq(commit->rsa_identity, voter_key, + sizeof(commit->rsa_identity)); } /* Decide if the newly received <b>commit</b> should be kept depending on |