diff options
Diffstat (limited to 'src/or/connection_or.c')
-rw-r--r-- | src/or/connection_or.c | 1099 |
1 files changed, 814 insertions, 285 deletions
diff --git a/src/or/connection_or.c b/src/or/connection_or.c index a01d086279..99c0ac077f 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -21,12 +21,14 @@ * This module also implements the client side of the v3 Tor link handshake, **/ #include "or.h" +#include "bridges.h" #include "buffers.h" /* * Define this so we get channel internal functions, since we're implementing * part of a subclass (channel_tls_t). */ #define TOR_CHANNEL_INTERNAL_ +#define CONNECTION_OR_PRIVATE #include "channel.h" #include "channeltls.h" #include "circuitbuild.h" @@ -37,6 +39,8 @@ #include "connection.h" #include "connection_or.h" #include "control.h" +#include "crypto_rand.h" +#include "crypto_util.h" #include "dirserv.h" #include "entrynodes.h" #include "geoip.h" @@ -45,14 +49,18 @@ #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" +#include "proto_cell.h" #include "reasons.h" #include "relay.h" #include "rendcommon.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "ext_orport.h" #include "scheduler.h" +#include "torcert.h" +#include "channelpadding.h" static int connection_tls_finish_handshake(or_connection_t *conn); static int connection_or_launch_v3_or_handshake(or_connection_t *conn); @@ -74,56 +82,25 @@ static void connection_or_mark_bad_for_new_circs(or_connection_t *or_conn); static void connection_or_change_state(or_connection_t *conn, uint8_t state); -/**************************************************************/ +static void connection_or_check_canonicity(or_connection_t *conn, + int started_here); -/** Map from identity digest of connected OR or desired OR to a connection_t - * with that identity digest. If there is more than one such connection_t, - * they form a linked list, with next_with_same_id as the next pointer. */ -static digestmap_t *orconn_identity_map = NULL; +/**************************************************************/ /** Global map between Extended ORPort identifiers and OR * connections. */ static digestmap_t *orconn_ext_or_id_map = NULL; -/** If conn is listed in orconn_identity_map, remove it, and clear - * conn->identity_digest. Otherwise do nothing. */ +/** Clear clear conn->identity_digest and update other data + * structures as appropriate.*/ void -connection_or_remove_from_identity_map(or_connection_t *conn) +connection_or_clear_identity(or_connection_t *conn) { - or_connection_t *tmp; tor_assert(conn); - if (!orconn_identity_map) - return; - tmp = digestmap_get(orconn_identity_map, conn->identity_digest); - if (!tmp) { - if (!tor_digest_is_zero(conn->identity_digest)) { - log_warn(LD_BUG, "Didn't find connection '%s' on identity map when " - "trying to remove it.", - conn->nickname ? conn->nickname : "NULL"); - } - return; - } - if (conn == tmp) { - if (conn->next_with_same_id) - digestmap_set(orconn_identity_map, conn->identity_digest, - conn->next_with_same_id); - else - digestmap_remove(orconn_identity_map, conn->identity_digest); - } else { - while (tmp->next_with_same_id) { - if (tmp->next_with_same_id == conn) { - tmp->next_with_same_id = conn->next_with_same_id; - break; - } - tmp = tmp->next_with_same_id; - } - } memset(conn->identity_digest, 0, DIGEST_LEN); - conn->next_with_same_id = NULL; } -/** Remove all entries from the identity-to-orconn map, and clear - * all identities in OR conns.*/ +/** Clear all identities in OR conns.*/ void connection_or_clear_identity_map(void) { @@ -131,57 +108,72 @@ connection_or_clear_identity_map(void) SMARTLIST_FOREACH(conns, connection_t *, conn, { if (conn->type == CONN_TYPE_OR) { - or_connection_t *or_conn = TO_OR_CONN(conn); - memset(or_conn->identity_digest, 0, DIGEST_LEN); - or_conn->next_with_same_id = NULL; + connection_or_clear_identity(TO_OR_CONN(conn)); } }); - - digestmap_free(orconn_identity_map, NULL); - orconn_identity_map = NULL; } /** Change conn->identity_digest to digest, and add conn into - * orconn_digest_map. */ + * the appropriate digest maps. + * + * NOTE that this function only allows two kinds of transitions: from + * unset identity to set identity, and from idempotent re-settings + * of the same identity. It's not allowed to clear an identity or to + * change an identity. Return 0 on success, and -1 if the transition + * is not allowed. + **/ static void -connection_or_set_identity_digest(or_connection_t *conn, const char *digest) +connection_or_set_identity_digest(or_connection_t *conn, + const char *rsa_digest, + const ed25519_public_key_t *ed_id) { - or_connection_t *tmp; + channel_t *chan = NULL; tor_assert(conn); - tor_assert(digest); + tor_assert(rsa_digest); - if (!orconn_identity_map) - orconn_identity_map = digestmap_new(); - if (tor_memeq(conn->identity_digest, digest, DIGEST_LEN)) + if (conn->chan) + chan = TLS_CHAN_TO_BASE(conn->chan); + + log_info(LD_HANDSHAKE, "Set identity digest for %p (%s): %s %s.", + conn, + escaped_safe_str(conn->base_.address), + hex_str(rsa_digest, DIGEST_LEN), + ed25519_fmt(ed_id)); + log_info(LD_HANDSHAKE, " (Previously: %s %s)", + hex_str(conn->identity_digest, DIGEST_LEN), + chan ? ed25519_fmt(&chan->ed25519_identity) : "<null>"); + + const int rsa_id_was_set = ! tor_digest_is_zero(conn->identity_digest); + const int ed_id_was_set = + chan && !ed25519_public_key_is_zero(&chan->ed25519_identity); + const int rsa_changed = + tor_memneq(conn->identity_digest, rsa_digest, DIGEST_LEN); + const int ed_changed = ed_id_was_set && + (!ed_id || !ed25519_pubkey_eq(ed_id, &chan->ed25519_identity)); + + tor_assert(!rsa_changed || !rsa_id_was_set); + tor_assert(!ed_changed || !ed_id_was_set); + + if (!rsa_changed && !ed_changed) return; /* If the identity was set previously, remove the old mapping. */ - if (! tor_digest_is_zero(conn->identity_digest)) { - connection_or_remove_from_identity_map(conn); - if (conn->chan) - channel_clear_identity_digest(TLS_CHAN_TO_BASE(conn->chan)); + if (rsa_id_was_set) { + connection_or_clear_identity(conn); + if (chan) + channel_clear_identity_digest(chan); } - memcpy(conn->identity_digest, digest, DIGEST_LEN); + memcpy(conn->identity_digest, rsa_digest, DIGEST_LEN); - /* If we're setting the ID to zero, don't add a mapping. */ - if (tor_digest_is_zero(digest)) + /* If we're initializing the IDs to zero, don't add a mapping yet. */ + if (tor_digest_is_zero(rsa_digest) && + (!ed_id || ed25519_public_key_is_zero(ed_id))) return; - tmp = digestmap_set(orconn_identity_map, digest, conn); - conn->next_with_same_id = tmp; - /* Deal with channels */ - if (conn->chan) - channel_set_identity_digest(TLS_CHAN_TO_BASE(conn->chan), digest); - -#if 1 - /* Testing code to check for bugs in representation. */ - for (; tmp; tmp = tmp->next_with_same_id) { - tor_assert(tor_memeq(tmp->identity_digest, digest, DIGEST_LEN)); - tor_assert(tmp != conn); - } -#endif + if (chan) + channel_set_identity_digest(chan, rsa_digest, ed_id); } /** Remove the Extended ORPort identifier of <b>conn</b> from the @@ -485,7 +477,7 @@ var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids) var_cell_t * var_cell_new(uint16_t payload_len) { - size_t size = STRUCT_OFFSET(var_cell_t, payload) + payload_len; + size_t size = offsetof(var_cell_t, payload) + payload_len; var_cell_t *cell = tor_malloc_zero(size); cell->payload_len = payload_len; cell->command = 0; @@ -504,7 +496,7 @@ var_cell_copy(const var_cell_t *src) size_t size = 0; if (src != NULL) { - size = STRUCT_OFFSET(var_cell_t, payload) + src->payload_len; + size = offsetof(var_cell_t, payload) + src->payload_len; copy = tor_malloc_zero(size); copy->payload_len = src->payload_len; copy->command = src->command; @@ -517,7 +509,7 @@ var_cell_copy(const var_cell_t *src) /** Release all space held by <b>cell</b>. */ void -var_cell_free(var_cell_t *cell) +var_cell_free_(var_cell_t *cell) { tor_free(cell); } @@ -604,8 +596,9 @@ connection_or_flushed_some(or_connection_t *conn) { size_t datalen; - /* The channel will want to update its estimated queue size */ - channel_update_xmit_queue_size(TLS_CHAN_TO_BASE(conn->chan)); + /* Update the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); /* If we're under the low water mark, add cells until we're just over the * high water mark. */ @@ -667,6 +660,11 @@ connection_or_finished_flushing(or_connection_t *conn) tor_fragile_assert(); return -1; } + + /* Update the channel's active timestamp if there is one */ + if (conn->chan) + channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + return 0; } @@ -711,7 +709,6 @@ connection_or_finished_connecting(or_connection_t *or_conn) void connection_or_about_to_close(or_connection_t *or_conn) { - time_t now = time(NULL); connection_t *conn = TO_CONN(or_conn); /* Tell the controlling channel we're closed */ @@ -731,15 +728,14 @@ connection_or_about_to_close(or_connection_t *or_conn) if (connection_or_nonopen_was_started_here(or_conn)) { const or_options_t *options = get_options(); connection_or_note_state_when_broken(or_conn); - rep_hist_note_connect_failed(or_conn->identity_digest, now); - entry_guard_register_connect_status(or_conn->identity_digest,0, - !options->HTTPSProxy, now); + /* Tell the new guard API about the channel failure */ + entry_guard_chan_failed(TLS_CHAN_TO_BASE(or_conn->chan)); if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) { int reason = tls_error_to_orconn_end_reason(or_conn->tls_error); control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED, reason); if (!authdir_mode_tests_reachability(options)) - control_event_bootstrap_problem( + control_event_bootstrap_prob_or( orconn_end_reason_to_control_string(reason), reason, or_conn); } @@ -747,11 +743,9 @@ connection_or_about_to_close(or_connection_t *or_conn) } else if (conn->hold_open_until_flushed) { /* We only set hold_open_until_flushed when we're intentionally * closing a connection. */ - rep_hist_note_disconnect(or_conn->identity_digest, now); control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, tls_error_to_orconn_end_reason(or_conn->tls_error)); } else if (!tor_digest_is_zero(or_conn->identity_digest)) { - rep_hist_note_connection_died(or_conn->identity_digest, now); control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, tls_error_to_orconn_end_reason(or_conn->tls_error)); } @@ -802,18 +796,10 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset, (int)options->BandwidthBurst, 1, INT32_MAX); } - conn->bandwidthrate = rate; - conn->bandwidthburst = burst; - if (reset) { /* set up the token buckets to be full */ - conn->read_bucket = conn->write_bucket = burst; - return; + token_bucket_rw_adjust(&conn->bucket, rate, burst); + if (reset) { + token_bucket_rw_reset(&conn->bucket, monotime_coarse_get_stamp()); } - /* If the new token bucket is smaller, take out the extra tokens. - * (If it's larger, don't -- the buckets can grow to reach the cap.) */ - if (conn->read_bucket > burst) - conn->read_bucket = burst; - if (conn->write_bucket > burst) - conn->write_bucket = burst; } /** Either our set of relays or our per-conn rate limits have changed. @@ -830,24 +816,6 @@ connection_or_update_token_buckets(smartlist_t *conns, }); } -/** How long do we wait before killing non-canonical OR connections with no - * circuits? In Tor versions up to 0.2.1.25 and 0.2.2.12-alpha, we waited 15 - * minutes before cancelling these connections, which caused fast relays to - * accrue many many idle connections. Hopefully 3-4.5 minutes is low enough - * that it kills most idle connections, without being so low that we cause - * clients to bounce on and off. - * - * For canonical connections, the limit is higher, at 15-22.5 minutes. - * - * For each OR connection, we randomly add up to 50% extra to its idle_timeout - * field, to avoid exposing when exactly the last circuit closed. Since we're - * storing idle_timeout in a uint16_t, don't let these values get higher than - * 12 hours or so without revising connection_or_set_canonical and/or expanding - * idle_timeout. - */ -#define IDLE_OR_CONN_TIMEOUT_NONCANONICAL 180 -#define IDLE_OR_CONN_TIMEOUT_CANONICAL 900 - /* Mark <b>or_conn</b> as canonical if <b>is_canonical</b> is set, and * non-canonical otherwise. Adjust idle_timeout accordingly. */ @@ -855,9 +823,6 @@ void connection_or_set_canonical(or_connection_t *or_conn, int is_canonical) { - const unsigned int timeout_base = is_canonical ? - IDLE_OR_CONN_TIMEOUT_CANONICAL : IDLE_OR_CONN_TIMEOUT_NONCANONICAL; - if (bool_eq(is_canonical, or_conn->is_canonical) && or_conn->idle_timeout != 0) { /* Don't recalculate an existing idle_timeout unless the canonical @@ -866,7 +831,14 @@ connection_or_set_canonical(or_connection_t *or_conn, } or_conn->is_canonical = !! is_canonical; /* force to a 1-bit boolean */ - or_conn->idle_timeout = timeout_base + crypto_rand_int(timeout_base / 2); + or_conn->idle_timeout = channelpadding_get_channel_idle_timeout( + TLS_CHAN_TO_BASE(or_conn->chan), is_canonical); + + log_info(LD_CIRC, + "Channel " U64_FORMAT " chose an idle timeout of %d.", + or_conn->chan ? + U64_PRINTF_ARG(TLS_CHAN_TO_BASE(or_conn->chan)->global_identifier):0, + or_conn->idle_timeout); } /** If we don't necessarily know the router we're connecting to, but we @@ -878,15 +850,47 @@ void connection_or_init_conn_from_address(or_connection_t *conn, const tor_addr_t *addr, uint16_t port, const char *id_digest, + const ed25519_public_key_t *ed_id, int started_here) { - const node_t *r = node_get_by_id(id_digest); - connection_or_set_identity_digest(conn, id_digest); + log_debug(LD_HANDSHAKE, "init conn from address %s: %s, %s (%d)", + fmt_addr(addr), + hex_str((const char*)id_digest, DIGEST_LEN), + ed25519_fmt(ed_id), + started_here); + + connection_or_set_identity_digest(conn, id_digest, ed_id); connection_or_update_token_buckets_helper(conn, 1, get_options()); conn->base_.port = port; tor_addr_copy(&conn->base_.addr, addr); tor_addr_copy(&conn->real_addr, addr); + + connection_or_check_canonicity(conn, started_here); +} + +/** Check whether the identity of <b>conn</b> matches a known node. If it + * does, check whether the address of conn matches the expected address, and + * update the connection's is_canonical flag, nickname, and address fields as + * appropriate. */ +static void +connection_or_check_canonicity(or_connection_t *conn, int started_here) +{ + const char *id_digest = conn->identity_digest; + const ed25519_public_key_t *ed_id = NULL; + const tor_addr_t *addr = &conn->real_addr; + if (conn->chan) + ed_id = & TLS_CHAN_TO_BASE(conn->chan)->ed25519_identity; + + const node_t *r = node_get_by_id(id_digest); + if (r && + node_supports_ed25519_link_authentication(r, 1) && + ! node_ed25519_id_matches(r, ed_id)) { + /* If this node is capable of proving an ed25519 ID, + * we can't call this a canonical connection unless both IDs match. */ + r = NULL; + } + if (r) { tor_addr_port_t node_ap; node_get_pref_orport(r, &node_ap); @@ -908,10 +912,12 @@ connection_or_init_conn_from_address(or_connection_t *conn, tor_addr_copy(&conn->base_.addr, &node_ap.addr); conn->base_.port = node_ap.port; } + tor_free(conn->nickname); conn->nickname = tor_strdup(node_get_nickname(r)); tor_free(conn->base_.address); conn->base_.address = tor_addr_to_str_dup(&node_ap.addr); } else { + tor_free(conn->nickname); conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); conn->nickname[0] = '$'; base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1, @@ -957,7 +963,37 @@ connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) * too old for new circuits? */ #define TIME_BEFORE_OR_CONN_IS_TOO_OLD (60*60*24*7) -/** Given the head of the linked list for all the or_connections with a given +/** Expire an or_connection if it is too old. Helper for + * connection_or_group_set_badness_ and fast path for + * channel_rsa_id_group_set_badness. + * + * Returns 1 if the connection was already expired, else 0. + */ +int +connection_or_single_set_badness_(time_t now, + or_connection_t *or_conn, + int force) +{ + /* XXXX this function should also be about channels? */ + if (or_conn->base_.marked_for_close || + connection_or_is_bad_for_new_circs(or_conn)) + return 1; + + if (force || + or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD + < now) { + log_info(LD_OR, + "Marking OR conn to %s:%d as too old for new circuits " + "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", + or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + (int)(now - or_conn->base_.timestamp_created)); + connection_or_mark_bad_for_new_circs(or_conn); + } + + return 0; +} + +/** Given a list of all the or_connections with a given * identity, set elements of that list as is_bad_for_new_circs as * appropriate. Helper for connection_or_set_bad_connections(). * @@ -974,29 +1010,21 @@ connection_or_mark_bad_for_new_circs(or_connection_t *or_conn) * See channel_is_better() in channel.c for our idea of what makes one OR * connection better than another. */ -static void -connection_or_group_set_badness(or_connection_t *head, int force) +void +connection_or_group_set_badness_(smartlist_t *group, int force) { - or_connection_t *or_conn = NULL, *best = NULL; + /* XXXX this function should be entirely about channels, not OR + * XXXX connections. */ + + or_connection_t *best = NULL; int n_old = 0, n_inprogress = 0, n_canonical = 0, n_other = 0; time_t now = time(NULL); /* Pass 1: expire everything that's old, and see what the status of * everything else is. */ - for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { - if (or_conn->base_.marked_for_close || - connection_or_is_bad_for_new_circs(or_conn)) + SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { + if (connection_or_single_set_badness_(now, or_conn, force)) continue; - if (force || - or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD - < now) { - log_info(LD_OR, - "Marking OR conn to %s:%d as too old for new circuits " - "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, - (int)(now - or_conn->base_.timestamp_created)); - connection_or_mark_bad_for_new_circs(or_conn); - } if (connection_or_is_bad_for_new_circs(or_conn)) { ++n_old; @@ -1007,11 +1035,11 @@ connection_or_group_set_badness(or_connection_t *head, int force) } else { ++n_other; } - } + } SMARTLIST_FOREACH_END(or_conn); /* Pass 2: We know how about how good the best connection is. * expire everything that's worse, and find the very best if we can. */ - for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { + SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { if (or_conn->base_.marked_for_close || connection_or_is_bad_for_new_circs(or_conn)) continue; /* This one doesn't need to be marked bad. */ @@ -1032,13 +1060,11 @@ connection_or_group_set_badness(or_connection_t *head, int force) } if (!best || - channel_is_better(now, - TLS_CHAN_TO_BASE(or_conn->chan), - TLS_CHAN_TO_BASE(best->chan), - 0)) { + channel_is_better(TLS_CHAN_TO_BASE(or_conn->chan), + TLS_CHAN_TO_BASE(best->chan))) { best = or_conn; } - } + } SMARTLIST_FOREACH_END(or_conn); if (!best) return; @@ -1057,17 +1083,15 @@ connection_or_group_set_badness(or_connection_t *head, int force) * 0.1.2.x dies out, the first case will go away, and the second one is * "mostly harmless", so a fix can wait until somebody is bored. */ - for (or_conn = head; or_conn; or_conn = or_conn->next_with_same_id) { + SMARTLIST_FOREACH_BEGIN(group, or_connection_t *, or_conn) { if (or_conn->base_.marked_for_close || connection_or_is_bad_for_new_circs(or_conn) || or_conn->base_.state != OR_CONN_STATE_OPEN) continue; if (or_conn != best && - channel_is_better(now, - TLS_CHAN_TO_BASE(best->chan), - TLS_CHAN_TO_BASE(or_conn->chan), 1)) { - /* This isn't the best conn, _and_ the best conn is better than it, - even when we're being forgiving. */ + channel_is_better(TLS_CHAN_TO_BASE(best->chan), + TLS_CHAN_TO_BASE(or_conn->chan))) { + /* This isn't the best conn, _and_ the best conn is better than it */ if (best->is_canonical) { log_info(LD_OR, "Marking OR conn to %s:%d as unsuitable for new circuits: " @@ -1091,24 +1115,217 @@ connection_or_group_set_badness(or_connection_t *head, int force) connection_or_mark_bad_for_new_circs(or_conn); } } + } SMARTLIST_FOREACH_END(or_conn); +} + +/* Lifetime of a connection failure. After that, we'll retry. This is in + * seconds. */ +#define OR_CONNECT_FAILURE_LIFETIME 60 +/* The interval to use with when to clean up the failure cache. */ +#define OR_CONNECT_FAILURE_CLEANUP_INTERVAL 60 + +/* When is the next time we have to cleanup the failure map. We keep this + * because we clean it opportunistically. */ +static time_t or_connect_failure_map_next_cleanup_ts = 0; + +/* OR connection failure entry data structure. It is kept in the connection + * failure map defined below and indexed by OR identity digest, address and + * port. + * + * We need to identify a connection failure with these three values because we + * want to avoid to wrongfully blacklist a relay if someone is trying to + * extend to a known identity digest but with the wrong IP/port. For instance, + * it can happen if a relay changed its port but the client still has an old + * descriptor with the old port. We want to stop connecting to that + * IP/port/identity all together, not only the relay identity. */ +typedef struct or_connect_failure_entry_t { + HT_ENTRY(or_connect_failure_entry_t) node; + /* Identity digest of the connection where it is connecting to. */ + uint8_t identity_digest[DIGEST_LEN]; + /* This is the connection address from the base connection_t. After the + * connection is checked for canonicity, the base address should represent + * what we know instead of where we are connecting to. This is what we need + * so we can correlate known relays within the consensus. */ + tor_addr_t addr; + uint16_t port; + /* Last time we were unable to connect. */ + time_t last_failed_connect_ts; +} or_connect_failure_entry_t; + +/* Map where we keep connection failure entries. They are indexed by addr, + * port and identity digest. */ +static HT_HEAD(or_connect_failure_ht, or_connect_failure_entry_t) + or_connect_failures_map = HT_INITIALIZER(); + +/* Helper: Hashtable equal function. Return 1 if equal else 0. */ +static int +or_connect_failure_ht_eq(const or_connect_failure_entry_t *a, + const or_connect_failure_entry_t *b) +{ + return fast_memeq(a->identity_digest, b->identity_digest, DIGEST_LEN) && + tor_addr_eq(&a->addr, &b->addr) && + a->port == b->port; +} + +/* Helper: Return the hash for the hashtable of the given entry. For this + * table, it is a combination of address, port and identity digest. */ +static unsigned int +or_connect_failure_ht_hash(const or_connect_failure_entry_t *entry) +{ + size_t offset = 0, addr_size; + const void *addr_ptr; + /* Largest size is IPv6 and IPv4 is smaller so it is fine. */ + uint8_t data[16 + sizeof(uint16_t) + DIGEST_LEN]; + + /* Get the right address bytes depending on the family. */ + switch (tor_addr_family(&entry->addr)) { + case AF_INET: + addr_size = 4; + addr_ptr = &entry->addr.addr.in_addr.s_addr; + break; + case AF_INET6: + addr_size = 16; + addr_ptr = &entry->addr.addr.in6_addr.s6_addr; + break; + default: + tor_assert_nonfatal_unreached(); + return 0; + } + + memcpy(data, addr_ptr, addr_size); + offset += addr_size; + memcpy(data + offset, entry->identity_digest, DIGEST_LEN); + offset += DIGEST_LEN; + set_uint16(data + offset, entry->port); + offset += sizeof(uint16_t); + + return (unsigned int) siphash24g(data, offset); +} + +HT_PROTOTYPE(or_connect_failure_ht, or_connect_failure_entry_t, node, + or_connect_failure_ht_hash, or_connect_failure_ht_eq) + +HT_GENERATE2(or_connect_failure_ht, or_connect_failure_entry_t, node, + or_connect_failure_ht_hash, or_connect_failure_ht_eq, + 0.6, tor_reallocarray_, tor_free_) + +/* Initialize a given connect failure entry with the given identity_digest, + * addr and port. All field are optional except ocf. */ +static void +or_connect_failure_init(const char *identity_digest, const tor_addr_t *addr, + uint16_t port, or_connect_failure_entry_t *ocf) +{ + tor_assert(ocf); + if (identity_digest) { + memcpy(ocf->identity_digest, identity_digest, + sizeof(ocf->identity_digest)); + } + if (addr) { + tor_addr_copy(&ocf->addr, addr); } + ocf->port = port; } -/** Go through all the OR connections (or if <b>digest</b> is non-NULL, just - * the OR connections with that digest), and set the is_bad_for_new_circs - * flag based on the rules in connection_or_group_set_badness() (or just - * always set it if <b>force</b> is true). - */ -void -connection_or_set_bad_connections(const char *digest, int force) +/* Return a newly allocated connection failure entry. It is initialized with + * the given or_conn data. This can't fail. */ +static or_connect_failure_entry_t * +or_connect_failure_new(const or_connection_t *or_conn) { - if (!orconn_identity_map) - return; + or_connect_failure_entry_t *ocf = tor_malloc_zero(sizeof(*ocf)); + or_connect_failure_init(or_conn->identity_digest, &or_conn->real_addr, + TO_CONN(or_conn)->port, ocf); + return ocf; +} + +/* Return a connection failure entry matching the given or_conn. NULL is + * returned if not found. */ +static or_connect_failure_entry_t * +or_connect_failure_find(const or_connection_t *or_conn) +{ + or_connect_failure_entry_t lookup; + tor_assert(or_conn); + or_connect_failure_init(or_conn->identity_digest, &TO_CONN(or_conn)->addr, + TO_CONN(or_conn)->port, &lookup); + return HT_FIND(or_connect_failure_ht, &or_connect_failures_map, &lookup); +} + +/* Note down in the connection failure cache that a failure occurred on the + * given or_conn. */ +STATIC void +note_or_connect_failed(const or_connection_t *or_conn) +{ + or_connect_failure_entry_t *ocf = NULL; + + tor_assert(or_conn); + + ocf = or_connect_failure_find(or_conn); + if (ocf == NULL) { + ocf = or_connect_failure_new(or_conn); + HT_INSERT(or_connect_failure_ht, &or_connect_failures_map, ocf); + } + ocf->last_failed_connect_ts = approx_time(); +} + +/* Cleanup the connection failure cache and remove all entries below the + * given cutoff. */ +static void +or_connect_failure_map_cleanup(time_t cutoff) +{ + or_connect_failure_entry_t **ptr, **next, *entry; + + for (ptr = HT_START(or_connect_failure_ht, &or_connect_failures_map); + ptr != NULL; ptr = next) { + entry = *ptr; + if (entry->last_failed_connect_ts <= cutoff) { + next = HT_NEXT_RMV(or_connect_failure_ht, &or_connect_failures_map, ptr); + tor_free(entry); + } else { + next = HT_NEXT(or_connect_failure_ht, &or_connect_failures_map, ptr); + } + } +} + +/* Return true iff the given OR connection can connect to its destination that + * is the triplet identity_digest, address and port. + * + * The or_conn MUST have gone through connection_or_check_canonicity() so the + * base address is properly set to what we know or doesn't know. */ +STATIC int +should_connect_to_relay(const or_connection_t *or_conn) +{ + time_t now, cutoff; + time_t connect_failed_since_ts = 0; + or_connect_failure_entry_t *ocf; + + tor_assert(or_conn); + + now = approx_time(); + cutoff = now - OR_CONNECT_FAILURE_LIFETIME; + + /* Opportunistically try to cleanup the failure cache. We do that at regular + * interval so it doesn't grow too big. */ + if (or_connect_failure_map_next_cleanup_ts <= now) { + or_connect_failure_map_cleanup(cutoff); + or_connect_failure_map_next_cleanup_ts = + now + OR_CONNECT_FAILURE_CLEANUP_INTERVAL; + } + + /* Look if we have failed previously to the same destination as this + * OR connection. */ + ocf = or_connect_failure_find(or_conn); + if (ocf) { + connect_failed_since_ts = ocf->last_failed_connect_ts; + } + /* If we do have an unable to connect timestamp and it is below cutoff, we + * can connect. Or we have never failed before so let it connect. */ + if (connect_failed_since_ts > cutoff) { + goto no_connect; + } - DIGESTMAP_FOREACH(orconn_identity_map, identity, or_connection_t *, conn) { - if (!digest || tor_memeq(digest, conn->identity_digest, DIGEST_LEN)) - connection_or_group_set_badness(conn, force); - } DIGESTMAP_FOREACH_END; + /* Ok we can connect! */ + return 1; + no_connect: + return 0; } /** <b>conn</b> is in the 'connecting' state, and it failed to complete @@ -1123,7 +1340,8 @@ connection_or_connect_failed(or_connection_t *conn, { control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason); if (!authdir_mode_tests_reachability(get_options())) - control_event_bootstrap_problem(msg, reason, conn); + control_event_bootstrap_prob_or(msg, reason, conn); + note_or_connect_failed(conn); } /** <b>conn</b> got an error in connection_handle_read_impl() or @@ -1174,7 +1392,9 @@ connection_or_notify_error(or_connection_t *conn, MOCK_IMPL(or_connection_t *, connection_or_connect, (const tor_addr_t *_addr, uint16_t port, - const char *id_digest, channel_tls_t *chan)) + const char *id_digest, + const ed25519_public_key_t *ed_id, + channel_tls_t *chan)) { or_connection_t *conn; const or_options_t *options = get_options(); @@ -1194,6 +1414,11 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, log_info(LD_PROTOCOL,"Client asked me to connect to myself. Refusing."); return NULL; } + if (server_mode(options) && router_ed25519_id_is_me(ed_id)) { + log_info(LD_PROTOCOL,"Client asked me to connect to myself by Ed25519 " + "identity. Refusing."); + return NULL; + } conn = or_connection_new(CONN_TYPE_OR, tor_addr_family(&addr)); @@ -1206,7 +1431,20 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, */ conn->chan = chan; chan->conn = conn; - connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1); + connection_or_init_conn_from_address(conn, &addr, port, id_digest, ed_id, 1); + + /* We have a proper OR connection setup, now check if we can connect to it + * that is we haven't had a failure earlier. This is to avoid to try to + * constantly connect to relays that we think are not reachable. */ + if (!should_connect_to_relay(conn)) { + log_info(LD_GENERAL, "Can't connect to identity %s at %s:%u because we " + "failed earlier. Refusing.", + hex_str(id_digest, DIGEST_LEN), fmt_addr(&TO_CONN(conn)->addr), + TO_CONN(conn)->port); + connection_free_(TO_CONN(conn)); + return NULL; + } + connection_or_change_state(conn, OR_CONN_STATE_CONNECTING); control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0); @@ -1239,7 +1477,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port), transport_name, transport_name); - control_event_bootstrap_problem( + control_event_bootstrap_prob_or( "Can't connect to bridge", END_OR_CONN_REASON_PT_MISSING, conn); @@ -1250,7 +1488,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port)); } - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; } @@ -1263,7 +1501,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, connection_or_connect_failed(conn, errno_to_orconn_end_reason(socket_error), tor_socket_strerror(socket_error)); - connection_free(TO_CONN(conn)); + connection_free_(TO_CONN(conn)); return NULL; case 0: connection_watch_events(TO_CONN(conn), READ_EVENT | WRITE_EVENT); @@ -1373,7 +1611,6 @@ connection_tls_start_handshake,(or_connection_t *conn, int receiving)) connection_start_reading(TO_CONN(conn)); log_debug(LD_HANDSHAKE,"starting TLS handshake on fd "TOR_SOCKET_T_FORMAT, conn->base_.s); - note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C); if (connection_tls_continue_handshake(conn) < 0) return -1; @@ -1553,7 +1790,10 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, } if (identity_rcvd) { - crypto_pk_get_digest(identity_rcvd, digest_rcvd_out); + if (crypto_pk_get_digest(identity_rcvd, digest_rcvd_out) < 0) { + crypto_pk_free(identity_rcvd); + return -1; + } } else { memset(digest_rcvd_out, 0, DIGEST_LEN); } @@ -1563,18 +1803,25 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, crypto_pk_free(identity_rcvd); - if (started_here) + if (started_here) { + /* A TLS handshake can't teach us an Ed25519 ID, so we set it to NULL + * here. */ + log_debug(LD_HANDSHAKE, "Calling client_learned_peer_id from " + "check_valid_tls_handshake"); return connection_or_client_learned_peer_id(conn, - (const uint8_t*)digest_rcvd_out); + (const uint8_t*)digest_rcvd_out, + NULL); + } return 0; } /** Called when we (as a connection initiator) have definitively, * authenticatedly, learned that ID of the Tor instance on the other - * side of <b>conn</b> is <b>peer_id</b>. For v1 and v2 handshakes, + * side of <b>conn</b> is <b>rsa_peer_id</b> and optionally <b>ed_peer_id</b>. + * For v1 and v2 handshakes, * this is right after we get a certificate chain in a TLS handshake - * or renegotiation. For v3 handshakes, this is right after we get a + * or renegotiation. For v3+ handshakes, this is right after we get a * certificate chain in a CERTS cell. * * If we did not know the ID before, record the one we got. @@ -1595,12 +1842,31 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, */ int connection_or_client_learned_peer_id(or_connection_t *conn, - const uint8_t *peer_id) + const uint8_t *rsa_peer_id, + const ed25519_public_key_t *ed_peer_id) { const or_options_t *options = get_options(); - - if (tor_digest_is_zero(conn->identity_digest)) { - connection_or_set_identity_digest(conn, (const char*)peer_id); + channel_tls_t *chan_tls = conn->chan; + channel_t *chan = channel_tls_to_base(chan_tls); + int changed_identity = 0; + tor_assert(chan); + + const int expected_rsa_key = + ! tor_digest_is_zero(conn->identity_digest); + const int expected_ed_key = + ! ed25519_public_key_is_zero(&chan->ed25519_identity); + + log_info(LD_HANDSHAKE, "learned peer id for %p (%s): %s, %s", + conn, + safe_str_client(conn->base_.address), + hex_str((const char*)rsa_peer_id, DIGEST_LEN), + ed25519_fmt(ed_peer_id)); + + if (! expected_rsa_key && ! expected_ed_key) { + log_info(LD_HANDSHAKE, "(we had no ID in mind when we made this " + "connection."); + connection_or_set_identity_digest(conn, + (const char*)rsa_peer_id, ed_peer_id); tor_free(conn->nickname); conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); conn->nickname[0] = '$'; @@ -1612,16 +1878,39 @@ connection_or_client_learned_peer_id(or_connection_t *conn, /* if it's a bridge and we didn't know its identity fingerprint, now * we do -- remember it for future attempts. */ learned_router_identity(&conn->base_.addr, conn->base_.port, - (const char*)peer_id); + (const char*)rsa_peer_id, ed_peer_id); + changed_identity = 1; } - if (tor_memneq(peer_id, conn->identity_digest, DIGEST_LEN)) { + const int rsa_mismatch = expected_rsa_key && + tor_memneq(rsa_peer_id, conn->identity_digest, DIGEST_LEN); + /* It only counts as an ed25519 mismatch if we wanted an ed25519 identity + * and didn't get it. It's okay if we get one that we didn't ask for. */ + const int ed25519_mismatch = + expected_ed_key && + (ed_peer_id == NULL || + ! ed25519_pubkey_eq(&chan->ed25519_identity, ed_peer_id)); + + if (rsa_mismatch || ed25519_mismatch) { /* I was aiming for a particular digest. I didn't get it! */ - char seen[HEX_DIGEST_LEN+1]; - char expected[HEX_DIGEST_LEN+1]; - base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN); - base16_encode(expected, sizeof(expected), conn->identity_digest, + char seen_rsa[HEX_DIGEST_LEN+1]; + char expected_rsa[HEX_DIGEST_LEN+1]; + char seen_ed[ED25519_BASE64_LEN+1]; + char expected_ed[ED25519_BASE64_LEN+1]; + base16_encode(seen_rsa, sizeof(seen_rsa), + (const char*)rsa_peer_id, DIGEST_LEN); + base16_encode(expected_rsa, sizeof(expected_rsa), conn->identity_digest, DIGEST_LEN); + if (ed_peer_id) { + ed25519_public_to_base64(seen_ed, ed_peer_id); + } else { + strlcpy(seen_ed, "no ed25519 key", sizeof(seen_ed)); + } + if (! ed25519_public_key_is_zero(&chan->ed25519_identity)) { + ed25519_public_to_base64(expected_ed, &chan->ed25519_identity); + } else { + strlcpy(expected_ed, "no ed25519 key", sizeof(expected_ed)); + } const int using_hardcoded_fingerprints = !networkstatus_get_reasonably_live_consensus(time(NULL), usable_consensus_flavor()); @@ -1659,30 +1948,48 @@ connection_or_client_learned_peer_id(or_connection_t *conn, } log_fn(severity, LD_HANDSHAKE, - "Tried connecting to router at %s:%d, but identity key was not " - "as expected: wanted %s but got %s.%s", - conn->base_.address, conn->base_.port, expected, seen, extra_log); - entry_guard_register_connect_status(conn->identity_digest, 0, 1, - time(NULL)); + "Tried connecting to router at %s:%d, but RSA + ed25519 identity " + "keys were not as expected: wanted %s + %s but got %s + %s.%s", + conn->base_.address, conn->base_.port, + expected_rsa, expected_ed, seen_rsa, seen_ed, extra_log); + + /* Tell the new guard API about the channel failure */ + entry_guard_chan_failed(TLS_CHAN_TO_BASE(conn->chan)); control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, END_OR_CONN_REASON_OR_IDENTITY); if (!authdir_mode_tests_reachability(options)) - control_event_bootstrap_problem( + control_event_bootstrap_prob_or( "Unexpected identity in router certificate", END_OR_CONN_REASON_OR_IDENTITY, conn); return -1; } + + if (!expected_ed_key && ed_peer_id) { + log_info(LD_HANDSHAKE, "(We had no Ed25519 ID in mind when we made this " + "connection.)"); + connection_or_set_identity_digest(conn, + (const char*)rsa_peer_id, ed_peer_id); + changed_identity = 1; + } + + if (changed_identity) { + /* If we learned an identity for this connection, then we might have + * just discovered it to be canonical. */ + connection_or_check_canonicity(conn, conn->handshake_state->started_here); + } + if (authdir_mode_tests_reachability(options)) { dirserv_orconn_tls_done(&conn->base_.addr, conn->base_.port, - (const char*)peer_id); + (const char*)rsa_peer_id, ed_peer_id); } return 0; } -/** Return when a client used this, for connection.c, since client_used - * is now one of the timestamps of channel_t */ +/** Return when we last used this channel for client activity (origin + * circuits). This is called from connection.c, since client_used is now one + * of the timestamps in channel_t */ time_t connection_or_client_used(or_connection_t *conn) @@ -1696,7 +2003,7 @@ connection_or_client_used(or_connection_t *conn) /** The v1/v2 TLS handshake is finished. * - * Make sure we are happy with the person we just handshaked with. + * Make sure we are happy with the peer we just handshaked with. * * If they initiated the connection, make sure they're not already connected, * then initialize conn from the information in router. @@ -1731,7 +2038,8 @@ connection_tls_finish_handshake(or_connection_t *conn) if (tor_tls_used_v1_handshake(conn->tls)) { conn->link_proto = 1; connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); + conn->base_.port, digest_rcvd, + NULL, 0); tor_tls_block_renegotiation(conn->tls); rep_hist_note_negotiated_link_proto(1, started_here); return connection_or_set_state_open(conn); @@ -1740,7 +2048,8 @@ connection_tls_finish_handshake(or_connection_t *conn) if (connection_init_or_handshake_state(conn, started_here) < 0) return -1; connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); + conn->base_.port, digest_rcvd, + NULL, 0); return connection_or_send_versions(conn, 0); } } @@ -1779,19 +2088,24 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here) s->started_here = started_here ? 1 : 0; s->digest_sent_data = 1; s->digest_received_data = 1; + if (! started_here && get_current_link_cert_cert()) { + s->own_link_cert = tor_cert_dup(get_current_link_cert_cert()); + } + s->certs = or_handshake_certs_new(); + s->certs->started_here = s->started_here; return 0; } /** Free all storage held by <b>state</b>. */ void -or_handshake_state_free(or_handshake_state_t *state) +or_handshake_state_free_(or_handshake_state_t *state) { if (!state) return; crypto_digest_free(state->digest_sent); crypto_digest_free(state->digest_received); - tor_x509_cert_free(state->auth_cert); - tor_x509_cert_free(state->id_cert); + or_handshake_certs_free(state->certs); + tor_cert_free(state->own_link_cert); memwipe(state, 0xBE, sizeof(or_handshake_state_t)); tor_free(state); } @@ -1912,12 +2226,23 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) cell_pack(&networkcell, cell, conn->wide_circ_ids); - connection_write_to_buf(networkcell.body, cell_network_size, TO_CONN(conn)); + rep_hist_padding_count_write(PADDING_TYPE_TOTAL); + if (cell->command == CELL_PADDING) + rep_hist_padding_count_write(PADDING_TYPE_CELL); + + connection_buf_add(networkcell.body, cell_network_size, TO_CONN(conn)); /* Touch the channel's active timestamp if there is one */ - if (conn->chan) + if (conn->chan) { channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); + if (TLS_CHAN_TO_BASE(conn->chan)->currently_padding) { + rep_hist_padding_count_write(PADDING_TYPE_ENABLED_TOTAL); + if (cell->command == CELL_PADDING) + rep_hist_padding_count_write(PADDING_TYPE_ENABLED_CELL); + } + } + if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) or_handshake_state_record_cell(conn, conn->handshake_state, cell, 0); } @@ -1935,8 +2260,8 @@ connection_or_write_var_cell_to_buf,(const var_cell_t *cell, tor_assert(cell); tor_assert(conn); n = var_cell_pack_header(cell, hdr, conn->wide_circ_ids); - connection_write_to_buf(hdr, n, TO_CONN(conn)); - connection_write_to_buf((char*)cell->payload, + connection_buf_add(hdr, n, TO_CONN(conn)); + connection_buf_add((char*)cell->payload, cell->payload_len, TO_CONN(conn)); if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) or_handshake_state_record_var_cell(conn, conn->handshake_state, cell, 0); @@ -2011,7 +2336,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan)); circuit_build_times_network_is_live(get_circuit_build_times_mutable()); - connection_fetch_from_buf(buf, cell_network_size, TO_CONN(conn)); + connection_buf_get_bytes(buf, cell_network_size, TO_CONN(conn)); /* retrieve cell info from buf (create the host-order struct from the * network-order string) */ @@ -2023,7 +2348,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) } /** Array of recognized link protocol versions. */ -static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4 }; +static const uint16_t or_protocol_versions[] = { 1, 2, 3, 4, 5 }; /** Number of versions in <b>or_protocol_versions</b>. */ static const int n_or_protocol_versions = (int)( sizeof(or_protocol_versions)/sizeof(uint16_t) ); @@ -2144,66 +2469,187 @@ connection_or_send_netinfo,(or_connection_t *conn)) return 0; } +/** Helper used to add an encoded certs to a cert cell */ +static void +add_certs_cell_cert_helper(certs_cell_t *certs_cell, + uint8_t cert_type, + const uint8_t *cert_encoded, + size_t cert_len) +{ + tor_assert(cert_len <= UINT16_MAX); + certs_cell_cert_t *ccc = certs_cell_cert_new(); + ccc->cert_type = cert_type; + ccc->cert_len = cert_len; + certs_cell_cert_setlen_body(ccc, cert_len); + memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len); + + certs_cell_add_certs(certs_cell, ccc); +} + +/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at + * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are + * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>. + * (If <b>cert</b> is NULL, take no action.) */ +static void +add_x509_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_x509_cert_t *cert) +{ + if (NULL == cert) + return; + + const uint8_t *cert_encoded = NULL; + size_t cert_len; + tor_x509_cert_get_der(cert, &cert_encoded, &cert_len); + + add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len); +} + +/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object + * that we are building in <b>certs_cell</b>. Set its type field to + * <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */ +static void +add_ed25519_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_cert_t *cert) +{ + if (NULL == cert) + return; + + add_certs_cell_cert_helper(certs_cell, cert_type, + cert->encoded, cert->encoded_len); +} + +#ifdef TOR_UNIT_TESTS +int certs_cell_ed25519_disabled_for_testing = 0; +#else +#define certs_cell_ed25519_disabled_for_testing 0 +#endif + /** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1 * on failure. */ int connection_or_send_certs_cell(or_connection_t *conn) { - const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL, - *using_link_cert = NULL; + const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL; tor_x509_cert_t *own_link_cert = NULL; - const uint8_t *link_encoded = NULL, *id_encoded = NULL; - size_t link_len, id_len; var_cell_t *cell; - size_t cell_len; - ssize_t pos; + + certs_cell_t *certs_cell = NULL; tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); if (! conn->handshake_state) return -1; + const int conn_in_server_mode = ! conn->handshake_state->started_here; + + /* Get the encoded values of the X509 certificates */ if (tor_tls_get_my_certs(conn_in_server_mode, &global_link_cert, &id_cert) < 0) return -1; + + if (conn_in_server_mode) { + own_link_cert = tor_tls_get_own_cert(conn->tls); + } + tor_assert(id_cert); + + certs_cell = certs_cell_new(); + + /* Start adding certs. First the link cert or auth1024 cert. */ if (conn_in_server_mode) { - using_link_cert = own_link_cert = tor_tls_get_own_cert(conn->tls); + tor_assert_nonfatal(own_link_cert); + add_x509_cert(certs_cell, + OR_CERT_TYPE_TLS_LINK, own_link_cert); } else { - using_link_cert = global_link_cert; + tor_assert(global_link_cert); + add_x509_cert(certs_cell, + OR_CERT_TYPE_AUTH_1024, global_link_cert); } - tor_x509_cert_get_der(using_link_cert, &link_encoded, &link_len); - tor_x509_cert_get_der(id_cert, &id_encoded, &id_len); - cell_len = 1 /* 1 byte: num certs in cell */ + - 2 * ( 1 + 2 ) /* For each cert: 1 byte for type, 2 for length */ + - link_len + id_len; - cell = var_cell_new(cell_len); - cell->command = CELL_CERTS; - cell->payload[0] = 2; - pos = 1; + /* Next the RSA->RSA ID cert */ + add_x509_cert(certs_cell, + OR_CERT_TYPE_ID_1024, id_cert); - if (conn_in_server_mode) - cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */ - else - cell->payload[pos] = OR_CERT_TYPE_AUTH_1024; /* client authentication */ - set_uint16(&cell->payload[pos+1], htons(link_len)); - memcpy(&cell->payload[pos+3], link_encoded, link_len); - pos += 3 + link_len; + /* Next the Ed25519 certs */ + add_ed25519_cert(certs_cell, + CERTTYPE_ED_ID_SIGN, + get_master_signing_key_cert()); + if (conn_in_server_mode) { + tor_assert_nonfatal(conn->handshake_state->own_link_cert || + certs_cell_ed25519_disabled_for_testing); + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_LINK, + conn->handshake_state->own_link_cert); + } else { + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_AUTH, + get_current_auth_key_cert()); + } + + /* And finally the crosscert. */ + { + const uint8_t *crosscert=NULL; + size_t crosscert_len; + get_master_rsa_crosscert(&crosscert, &crosscert_len); + if (crosscert) { + add_certs_cell_cert_helper(certs_cell, + CERTTYPE_RSA1024_ID_EDID, + crosscert, crosscert_len); + } + } - cell->payload[pos] = OR_CERT_TYPE_ID_1024; /* ID cert */ - set_uint16(&cell->payload[pos+1], htons(id_len)); - memcpy(&cell->payload[pos+3], id_encoded, id_len); - pos += 3 + id_len; + /* We've added all the certs; make the cell. */ + certs_cell->n_certs = certs_cell_getlen_certs(certs_cell); - tor_assert(pos == (int)cell_len); /* Otherwise we just smashed the heap */ + ssize_t alloc_len = certs_cell_encoded_len(certs_cell); + tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX); + cell = var_cell_new(alloc_len); + cell->command = CELL_CERTS; + ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell); + tor_assert(enc_len > 0 && enc_len <= alloc_len); + cell->payload_len = enc_len; connection_or_write_var_cell_to_buf(cell, conn); var_cell_free(cell); + certs_cell_free(certs_cell); tor_x509_cert_free(own_link_cert); return 0; } +/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that + * we can send and receive. */ +int +authchallenge_type_is_supported(uint16_t challenge_type) +{ + switch (challenge_type) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: + case AUTHTYPE_ED25519_SHA256_RFC5705: + return 1; + case AUTHTYPE_RSA_SHA256_RFC5705: + default: + return 0; + } +} + +/** Return true iff <b>challenge_type_a</b> is one that we would rather + * use than <b>challenge_type_b</b>. */ +int +authchallenge_type_is_better(uint16_t challenge_type_a, + uint16_t challenge_type_b) +{ + /* Any supported type is better than an unsupported one; + * all unsupported types are equally bad. */ + if (!authchallenge_type_is_supported(challenge_type_a)) + return 0; + if (!authchallenge_type_is_supported(challenge_type_b)) + return 1; + /* It happens that types are superior in numerically ascending order. + * If that ever changes, this must change too. */ + return (challenge_type_a > challenge_type_b); +} + /** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0 * on success, -1 on failure. */ int @@ -2218,17 +2664,26 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) auth_challenge_cell_t *ac = auth_challenge_cell_new(); + tor_assert(sizeof(ac->challenge) == 32); crypto_rand((char*)ac->challenge, sizeof(ac->challenge)); auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET); + /* Disabled, because everything that supports this method also supports + * the much-superior ED25519_SHA256_RFC5705 */ + /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */ + auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705); auth_challenge_cell_set_n_methods(ac, auth_challenge_cell_getlen_methods(ac)); cell = var_cell_new(auth_challenge_cell_encoded_len(ac)); ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len, ac); - if (len != cell->payload_len) + if (len != cell->payload_len) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Encoded auth challenge cell length not as expected"); goto done; + /* LCOV_EXCL_STOP */ + } cell->command = CELL_AUTH_CHALLENGE; connection_or_write_var_cell_to_buf(cell, conn); @@ -2242,8 +2697,8 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) } /** Compute the main body of an AUTHENTICATE cell that a client can use - * to authenticate itself on a v3 handshake for <b>conn</b>. Write it to the - * <b>outlen</b>-byte buffer at <b>out</b>. + * to authenticate itself on a v3 handshake for <b>conn</b>. Return it + * in a var_cell_t. * * If <b>server</b> is true, only calculate the first * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's @@ -2259,24 +2714,44 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) * * Return the length of the cell body on success, and -1 on failure. */ -int +var_cell_t * connection_or_compute_authenticate_cell_body(or_connection_t *conn, - uint8_t *out, size_t outlen, + const int authtype, crypto_pk_t *signing_key, - int server) + const ed25519_keypair_t *ed_signing_key, + int server) { auth1_t *auth = NULL; auth_ctx_t *ctx = auth_ctx_new(); - int result; + var_cell_t *result = NULL; + int old_tlssecrets_algorithm = 0; + const char *authtype_str = NULL; - /* assert state is reasonable XXXX */ + int is_ed = 0; - ctx->is_ed = 0; + /* assert state is reasonable XXXX */ + switch (authtype) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: + authtype_str = "AUTH0001"; + old_tlssecrets_algorithm = 1; + break; + case AUTHTYPE_RSA_SHA256_RFC5705: + authtype_str = "AUTH0002"; + break; + case AUTHTYPE_ED25519_SHA256_RFC5705: + authtype_str = "AUTH0003"; + is_ed = 1; + break; + default: + tor_assert(0); + break; + } auth = auth1_new(); + ctx->is_ed = is_ed; /* Type: 8 bytes. */ - memcpy(auth1_getarray_type(auth), "AUTH0001", 8); + memcpy(auth1_getarray_type(auth), authtype_str, 8); { const tor_x509_cert_t *id_cert=NULL; @@ -2286,7 +2761,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, goto err; my_digests = tor_x509_cert_get_id_digests(id_cert); their_digests = - tor_x509_cert_get_id_digests(conn->handshake_state->id_cert); + tor_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert); tor_assert(my_digests); tor_assert(their_digests); my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; @@ -2302,6 +2777,22 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, memcpy(auth->sid, server_id, 32); } + if (is_ed) { + const ed25519_public_key_t *my_ed_id, *their_ed_id; + if (!conn->handshake_state->certs->ed_id_sign) { + log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer."); + goto err; + } + my_ed_id = get_master_identity_key(); + their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key; + + const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey; + const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey; + + memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN); + memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN); + } + { crypto_digest_t *server_d, *client_d; if (server) { @@ -2328,7 +2819,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, cert = tor_tls_get_peer_cert(conn->tls); } if (!cert) { - log_warn(LD_OR, "Unable to find cert when making AUTH1 data."); + log_warn(LD_OR, "Unable to find cert when making %s data.", + authtype_str); goto err; } @@ -2339,36 +2831,85 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, } /* HMAC of clientrandom and serverrandom using master key : 32 octets */ - tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets); + if (old_tlssecrets_algorithm) { + tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets); + } else { + char label[128]; + tor_snprintf(label, sizeof(label), + "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str); + int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets, + auth->cid, sizeof(auth->cid), + label); + if (r < 0) { + if (r != -2) + log_warn(LD_BUG, "TLS key export failed for unknown reason."); + // If r == -2, this was openssl bug 7712. + goto err; + } + } /* 8 octets were reserved for the current time, but we're trying to get out * of the habit of sending time around willynilly. Fortunately, nothing * checks it. That's followed by 16 bytes of nonce. */ crypto_rand((char*)auth->rand, 24); + ssize_t maxlen = auth1_encoded_len(auth, ctx); + if (ed_signing_key && is_ed) { + maxlen += ED25519_SIG_LEN; + } else if (signing_key && !is_ed) { + maxlen += crypto_pk_keysize(signing_key); + } + + const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */ + result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen); + uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN; + const size_t outlen = maxlen; ssize_t len; + + result->command = CELL_AUTHENTICATE; + set_uint16(result->payload, htons(authtype)); + if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) { - log_warn(LD_OR, "Unable to encode signed part of AUTH1 data."); + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data."); goto err; + /* LCOV_EXCL_STOP */ } if (server) { auth1_t *tmp = NULL; ssize_t len2 = auth1_parse(&tmp, out, len, ctx); if (!tmp) { - log_warn(LD_OR, "Unable to parse signed part of AUTH1 data."); + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that " + "we just encoded"); goto err; + /* LCOV_EXCL_STOP */ } - result = (int) (tmp->end_of_fixed_part - out); + result->payload_len = (tmp->end_of_signed - result->payload); + auth1_free(tmp); if (len2 != len) { - log_warn(LD_OR, "Mismatched length when re-parsing AUTH1 data."); + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data."); goto err; + /* LCOV_EXCL_STOP */ } goto done; } - if (signing_key) { + if (ed_signing_key && is_ed) { + ed25519_signature_t sig; + if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to sign ed25519 authentication data"); + goto err; + /* LCOV_EXCL_STOP */ + } + auth1_setlen_sig(auth, ED25519_SIG_LEN); + memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN); + + } else if (signing_key && !is_ed) { auth1_setlen_sig(auth, crypto_pk_keysize(signing_key)); char d[32]; @@ -2383,18 +2924,24 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, } auth1_setlen_sig(auth, siglen); + } - len = auth1_encode(out, outlen, auth, ctx); - if (len < 0) { - log_warn(LD_OR, "Unable to encode signed AUTH1 data."); - goto err; - } + len = auth1_encode(out, outlen, auth, ctx); + if (len < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ } - result = (int) len; + tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len); + result->payload_len = len + AUTH_CELL_HEADER_LEN; + set_uint16(result->payload+2, htons(len)); + goto done; err: - result = -1; + var_cell_free(result); + result = NULL; done: auth1_free(auth); auth_ctx_free(ctx); @@ -2408,47 +2955,29 @@ connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype)) { var_cell_t *cell; crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); - int authlen; - size_t cell_maxlen; /* XXXX make sure we're actually supposed to send this! */ if (!pk) { log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key"); return -1; } - if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) { + if (! authchallenge_type_is_supported(authtype)) { log_warn(LD_BUG, "Tried to send authenticate cell with unknown " "authentication type %d", authtype); return -1; } - cell_maxlen = 4 + /* overhead */ - V3_AUTH_BODY_LEN + /* Authentication body */ - crypto_pk_keysize(pk) + /* Max signature length */ - 16 /* add a few extra bytes just in case. */; - - cell = var_cell_new(cell_maxlen); - cell->command = CELL_AUTHENTICATE; - set_uint16(cell->payload, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); - /* skip over length ; we don't know that yet. */ - - authlen = connection_or_compute_authenticate_cell_body(conn, - cell->payload+4, - cell_maxlen-4, - pk, - 0 /* not server */); - if (authlen < 0) { - log_warn(LD_BUG, "Unable to compute authenticate cell!"); - var_cell_free(cell); + cell = connection_or_compute_authenticate_cell_body(conn, + authtype, + pk, + get_current_auth_keypair(), + 0 /* not server */); + if (! cell) { + log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!"); return -1; } - tor_assert(authlen + 4 <= cell->payload_len); - set_uint16(cell->payload+2, htons(authlen)); - cell->payload_len = authlen + 4; - connection_or_write_var_cell_to_buf(cell, conn); var_cell_free(cell); return 0; } - |