summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/bridges.c23
-rw-r--r--src/or/bridges.h3
-rw-r--r--src/or/channel.c151
-rw-r--r--src/or/channel.h40
-rw-r--r--src/or/channeltls.c18
-rw-r--r--src/or/circuitbuild.c106
-rw-r--r--src/or/circuitbuild.h11
-rw-r--r--src/or/circuitlist.c179
-rw-r--r--src/or/circuitlist.h4
-rw-r--r--src/or/circuitstats.c2
-rw-r--r--src/or/circuituse.c201
-rw-r--r--src/or/circuituse.h20
-rw-r--r--src/or/config.c47
-rw-r--r--src/or/connection.c15
-rw-r--r--src/or/connection_edge.c2
-rw-r--r--src/or/connection_or.c299
-rw-r--r--src/or/connection_or.h5
-rw-r--r--src/or/control.c2
-rw-r--r--src/or/directory.c37
-rw-r--r--src/or/dirserv.c58
-rw-r--r--src/or/dirserv.h3
-rw-r--r--src/or/dnsserv.c2
-rw-r--r--src/or/entrynodes.c4
-rw-r--r--src/or/hibernate.c6
-rw-r--r--src/or/hs_circuitmap.c328
-rw-r--r--src/or/hs_circuitmap.h70
-rw-r--r--src/or/hs_common.h6
-rw-r--r--src/or/hs_descriptor.c46
-rw-r--r--src/or/hs_descriptor.h1
-rw-r--r--src/or/hs_intropoint.c288
-rw-r--r--src/or/hs_intropoint.h42
-rw-r--r--src/or/hs_service.c177
-rw-r--r--src/or/hs_service.h32
-rw-r--r--src/or/include.am8
-rw-r--r--src/or/main.c9
-rw-r--r--src/or/networkstatus.c63
-rw-r--r--src/or/networkstatus.h4
-rw-r--r--src/or/nodelist.c79
-rw-r--r--src/or/nodelist.h8
-rw-r--r--src/or/onion.c314
-rw-r--r--src/or/onion.h2
-rw-r--r--src/or/or.h45
-rw-r--r--src/or/protover.c2
-rw-r--r--src/or/rendcommon.c3
-rw-r--r--src/or/rendmid.c32
-rw-r--r--src/or/rendmid.h4
-rw-r--r--src/or/rendservice.c560
-rw-r--r--src/or/rendservice.h19
-rw-r--r--src/or/router.c13
-rw-r--r--src/or/routerkeys.c30
-rw-r--r--src/or/routerkeys.h2
-rw-r--r--src/or/routerlist.c49
-rw-r--r--src/or/routerlist.h1
-rw-r--r--src/or/routerparse.c41
-rw-r--r--src/or/shared_random.c6
-rw-r--r--src/or/torcert.c41
-rw-r--r--src/or/torcert.h2
57 files changed, 2598 insertions, 967 deletions
diff --git a/src/or/bridges.c b/src/or/bridges.c
index 4058979bfb..7d1acdfeaa 100644
--- a/src/or/bridges.c
+++ b/src/or/bridges.c
@@ -263,18 +263,35 @@ node_is_a_configured_bridge(const node_t *node)
*/
void
learned_router_identity(const tor_addr_t *addr, uint16_t port,
- const char *digest)
+ const char *digest,
+ const ed25519_public_key_t *ed_id)
{
+ // XXXX prop220 use ed_id here, once there is some way to specify
+ (void)ed_id;
+ int learned = 0;
bridge_info_t *bridge =
get_configured_bridge_by_addr_port_digest(addr, port, digest);
if (bridge && tor_digest_is_zero(bridge->identity)) {
+ memcpy(bridge->identity, digest, DIGEST_LEN);
+ learned = 1;
+ }
+ /* XXXX prop220 remember bridge ed25519 identities -- add a field */
+#if 0
+ if (bridge && ed_id &&
+ ed25519_public_key_is_zero(&bridge->ed25519_identity) &&
+ !ed25519_public_key_is_zero(ed_id)) {
+ memcpy(&bridge->ed25519_identity, ed_id, sizeof(*ed_id));
+ learned = 1;
+ }
+#endif
+ if (learned) {
char *transport_info = NULL;
const char *transport_name =
find_transport_name_by_bridge_addrport(addr, port);
if (transport_name)
tor_asprintf(&transport_info, " (with transport '%s')", transport_name);
- memcpy(bridge->identity, digest, DIGEST_LEN);
+ // XXXX prop220 log both fingerprints.
log_notice(LD_DIR, "Learned fingerprint %s for bridge %s%s.",
hex_str(digest, DIGEST_LEN), fmt_addrport(addr, port),
transport_info ? transport_info : "");
@@ -373,6 +390,8 @@ bridge_add_from_config(bridge_line_t *bridge_line)
{
bridge_info_t *b;
+ // XXXX prop220 add a way to specify ed25519 ID to bridge_line_t.
+
{ /* Log the bridge we are about to register: */
log_debug(LD_GENERAL, "Registering bridge at %s (transport: %s) (%s)",
fmt_addrport(&bridge_line->addr, bridge_line->port),
diff --git a/src/or/bridges.h b/src/or/bridges.h
index 74c5113231..de23fe6eeb 100644
--- a/src/or/bridges.h
+++ b/src/or/bridges.h
@@ -34,7 +34,8 @@ int extend_info_is_a_configured_bridge(const extend_info_t *ei);
int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
int node_is_a_configured_bridge(const node_t *node);
void learned_router_identity(const tor_addr_t *addr, uint16_t port,
- const char *digest);
+ const char *digest,
+ const ed25519_public_key_t *ed_id);
void bridge_add_from_config(struct bridge_line_t *bridge_line);
void retry_bridge_descriptor_fetch_directly(const char *digest);
diff --git a/src/or/channel.c b/src/or/channel.c
index 9898148dde..2951a1d665 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -733,27 +733,62 @@ channel_find_by_global_id(uint64_t global_identifier)
return rv;
}
+/** Return true iff <b>chan</b> matches <b>rsa_id_digest</b> and <b>ed_id</b>.
+ * as its identity keys. If either is NULL, do not check for a match. */
+static int
+channel_remote_identity_matches(const channel_t *chan,
+ const char *rsa_id_digest,
+ const ed25519_public_key_t *ed_id)
+{
+ if (BUG(!chan))
+ return 0;
+ if (rsa_id_digest) {
+ if (tor_memneq(rsa_id_digest, chan->identity_digest, DIGEST_LEN))
+ return 0;
+ }
+ if (ed_id) {
+ if (tor_memneq(ed_id->pubkey, chan->ed25519_identity.pubkey,
+ ED25519_PUBKEY_LEN))
+ return 0;
+ }
+ return 1;
+}
+
/**
- * Find channel by digest of the remote endpoint
+ * Find channel by RSA/Ed25519 identity of of the remote endpoint
+ *
+ * This function looks up a channel by the digest of its remote endpoint's RSA
+ * identity key. If <b>ed_id</b> is provided and nonzero, only a channel
+ * matching the <b>ed_id</b> will be returned.
*
- * This function looks up a channel by the digest of its remote endpoint in
- * the channel digest map. It's possible that more than one channel to a
- * given endpoint exists. Use channel_next_with_digest() to walk the list.
+ * It's possible that more than one channel to a given endpoint exists. Use
+ * channel_next_with_rsa_identity() to walk the list of channels; make sure
+ * to test for Ed25519 identity match too (as appropriate)
*/
-
channel_t *
-channel_find_by_remote_digest(const char *identity_digest)
+channel_find_by_remote_identity(const char *rsa_id_digest,
+ const ed25519_public_key_t *ed_id)
{
channel_t *rv = NULL;
channel_idmap_entry_t *ent, search;
- tor_assert(identity_digest);
+ tor_assert(rsa_id_digest); /* For now, we require that every channel have
+ * an RSA identity, and that every lookup
+ * contain an RSA identity */
+ if (ed_id && ed25519_public_key_is_zero(ed_id)) {
+ /* Treat zero as meaning "We don't care about the presence or absence of
+ * an Ed key", not "There must be no Ed key". */
+ ed_id = NULL;
+ }
- memcpy(search.digest, identity_digest, DIGEST_LEN);
+ memcpy(search.digest, rsa_id_digest, DIGEST_LEN);
ent = HT_FIND(channel_idmap, &channel_identity_map, &search);
if (ent) {
rv = TOR_LIST_FIRST(&ent->channel_list);
}
+ while (rv && ! channel_remote_identity_matches(rv, rsa_id_digest, ed_id)) {
+ rv = channel_next_with_rsa_identity(rv);
+ }
return rv;
}
@@ -766,7 +801,7 @@ channel_find_by_remote_digest(const char *identity_digest)
*/
channel_t *
-channel_next_with_digest(channel_t *chan)
+channel_next_with_rsa_identity(channel_t *chan)
{
tor_assert(chan);
@@ -1433,10 +1468,10 @@ channel_clear_identity_digest(channel_t *chan)
* This function sets the identity digest of the remote endpoint for a
* channel; this is intended for use by the lower layer.
*/
-
void
channel_set_identity_digest(channel_t *chan,
- const char *identity_digest)
+ const char *identity_digest,
+ const ed25519_public_key_t *ed_identity)
{
int was_in_digest_map, should_be_in_digest_map, state_not_in_map;
@@ -1475,6 +1510,11 @@ channel_set_identity_digest(channel_t *chan,
memset(chan->identity_digest, 0,
sizeof(chan->identity_digest));
}
+ if (ed_identity) {
+ memcpy(&chan->ed25519_identity, ed_identity, sizeof(*ed_identity));
+ } else {
+ memset(&chan->ed25519_identity, 0, sizeof(*ed_identity));
+ }
/* Put it in the digest map if we should */
if (should_be_in_digest_map)
@@ -3299,7 +3339,8 @@ channel_is_better(time_t now, channel_t *a, channel_t *b,
*/
channel_t *
-channel_get_for_extend(const char *digest,
+channel_get_for_extend(const char *rsa_id_digest,
+ const ed25519_public_key_t *ed_id,
const tor_addr_t *target_addr,
const char **msg_out,
int *launch_out)
@@ -3312,14 +3353,14 @@ channel_get_for_extend(const char *digest,
tor_assert(msg_out);
tor_assert(launch_out);
- chan = channel_find_by_remote_digest(digest);
+ chan = channel_find_by_remote_identity(rsa_id_digest, ed_id);
/* Walk the list, unrefing the old one and refing the new at each
* iteration.
*/
- for (; chan; chan = channel_next_with_digest(chan)) {
+ for (; chan; chan = channel_next_with_rsa_identity(chan)) {
tor_assert(tor_memeq(chan->identity_digest,
- digest, DIGEST_LEN));
+ rsa_id_digest, DIGEST_LEN));
if (CHANNEL_CONDEMNED(chan))
continue;
@@ -3330,6 +3371,11 @@ channel_get_for_extend(const char *digest,
continue;
}
+ /* The Ed25519 key has to match too */
+ if (!channel_remote_identity_matches(chan, rsa_id_digest, ed_id)) {
+ continue;
+ }
+
/* Never return a non-open connection. */
if (!CHANNEL_IS_OPEN(chan)) {
/* If the address matches, don't launch a new connection for this
@@ -4501,6 +4547,81 @@ channel_set_circid_type,(channel_t *chan,
}
}
+/** Helper for channel_update_bad_for_new_circs(): Perform the
+ * channel_update_bad_for_new_circs operation on all channels in <b>lst</b>,
+ * all of which MUST have the same RSA ID. (They MAY have different
+ * Ed25519 IDs.) */
+static void
+channel_rsa_id_group_set_badness(struct channel_list_s *lst, int force)
+{
+ /*XXXX This function should really be about channels. 15056 */
+ channel_t *chan;
+
+ /* First, get a minimal list of the ed25519 identites */
+ smartlist_t *ed_identities = smartlist_new();
+ TOR_LIST_FOREACH(chan, lst, next_with_same_id) {
+ uint8_t *id_copy =
+ tor_memdup(&chan->ed25519_identity.pubkey, DIGEST256_LEN);
+ smartlist_add(ed_identities, id_copy);
+ }
+ smartlist_sort_digests256(ed_identities);
+ smartlist_uniq_digests256(ed_identities);
+
+ /* Now, for each Ed identity, build a smartlist and find the best entry on
+ * it. */
+ smartlist_t *or_conns = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(ed_identities, const uint8_t *, ed_id) {
+ TOR_LIST_FOREACH(chan, lst, next_with_same_id) {
+ channel_tls_t *chantls = BASE_CHAN_TO_TLS(chan);
+ if (tor_memneq(ed_id, &chan->ed25519_identity.pubkey, DIGEST256_LEN))
+ continue;
+ or_connection_t *orconn = chantls->conn;
+ if (orconn) {
+ tor_assert(orconn->chan == chantls);
+ smartlist_add(or_conns, orconn);
+ }
+ }
+
+ connection_or_group_set_badness_(or_conns, force);
+ smartlist_clear(or_conns);
+ } SMARTLIST_FOREACH_END(ed_id);
+
+ /* XXXX 15056 we may want to do something special with connections that have
+ * no set Ed25519 identity! */
+
+ smartlist_free(or_conns);
+
+ SMARTLIST_FOREACH(ed_identities, uint8_t *, ed_id, tor_free(ed_id));
+ smartlist_free(ed_identities);
+}
+
+/** Go through all the channels (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
+channel_update_bad_for_new_circs(const char *digest, int force)
+{
+ if (digest) {
+ channel_idmap_entry_t *ent;
+ channel_idmap_entry_t search;
+ memset(&search, 0, sizeof(search));
+ memcpy(search.digest, digest, DIGEST_LEN);
+ ent = HT_FIND(channel_idmap, &channel_identity_map, &search);
+ if (ent) {
+ channel_rsa_id_group_set_badness(&ent->channel_list, force);
+ }
+ return;
+ }
+
+ /* no digest; just look at everything. */
+ channel_idmap_entry_t **iter;
+ HT_FOREACH(iter, channel_idmap, &channel_identity_map) {
+ channel_rsa_id_group_set_badness(&(*iter)->channel_list, force);
+ }
+}
+
/**
* Update the estimated number of bytes queued to transmit for this channel,
* and notify the scheduler. The estimate includes both the channel queue and
diff --git a/src/or/channel.h b/src/or/channel.h
index 7e7b2ec899..26aa93b5e2 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -153,16 +153,32 @@ struct channel_s {
int (*write_var_cell)(channel_t *, var_cell_t *);
/**
- * Hash of the public RSA key for the other side's identity key, or
- * zeroes if the other side hasn't shown us a valid identity key.
+ * Hash of the public RSA key for the other side's RSA identity key -- or
+ * zeroes if we don't have an RSA identity in mind for the other side, and
+ * it hasn't shown us one.
+ *
+ * Note that this is the RSA identity that we hope the other side has -- not
+ * necessarily its true identity. Don't believe this identity unless
+ * authentication has happened.
*/
char identity_digest[DIGEST_LEN];
+ /**
+ * Ed25519 key for the other side of this channel -- or zeroes if we don't
+ * have an Ed25519 identity in mind for the other side, and it hasn't shown
+ * us one.
+ *
+ * Note that this is the identity that we hope the other side has -- not
+ * necessarily its true identity. Don't believe this identity unless
+ * authentication has happened.
+ */
+ ed25519_public_key_t ed25519_identity;
+
/** Nickname of the OR on the other side, or NULL if none. */
char *nickname;
/**
- * Linked list of channels with the same identity digest, for the
- * digest->channel map
+ * Linked list of channels with the same RSA identity digest, for use with
+ * the digest->channel map
*/
TOR_LIST_ENTRY(channel_s) next_with_same_id;
@@ -427,7 +443,8 @@ void channel_mark_incoming(channel_t *chan);
void channel_mark_outgoing(channel_t *chan);
void channel_mark_remote(channel_t *chan);
void channel_set_identity_digest(channel_t *chan,
- const char *identity_digest);
+ const char *identity_digest,
+ const ed25519_public_key_t *ed_identity);
void channel_set_remote_end(channel_t *chan,
const char *identity_digest,
const char *nickname);
@@ -489,10 +506,11 @@ int channel_send_destroy(circid_t circ_id, channel_t *chan,
*/
channel_t * channel_connect(const tor_addr_t *addr, uint16_t port,
- const char *id_digest,
+ const char *rsa_id_digest,
const ed25519_public_key_t *ed_id);
-channel_t * channel_get_for_extend(const char *digest,
+channel_t * channel_get_for_extend(const char *rsa_id_digest,
+ const ed25519_public_key_t *ed_id,
const tor_addr_t *target_addr,
const char **msg_out,
int *launch_out);
@@ -506,11 +524,13 @@ int channel_is_better(time_t now,
*/
channel_t * channel_find_by_global_id(uint64_t global_identifier);
-channel_t * channel_find_by_remote_digest(const char *identity_digest);
+channel_t * channel_find_by_remote_identity(const char *rsa_id_digest,
+ const ed25519_public_key_t *ed_id);
/** For things returned by channel_find_by_remote_digest(), walk the list.
+ * The RSA key will match for all returned elements; the Ed25519 key might not.
*/
-channel_t * channel_next_with_digest(channel_t *chan);
+channel_t * channel_next_with_rsa_identity(channel_t *chan);
/*
* Helper macros to lookup state of given channel.
@@ -582,6 +602,8 @@ void channel_listener_dump_statistics(channel_listener_t *chan_l,
void channel_listener_dump_transport_statistics(channel_listener_t *chan_l,
int severity);
+void channel_update_bad_for_new_circs(const char *digest, int force);
+
/* Flow control queries */
uint64_t channel_get_global_queue_estimate(void);
int channel_num_cells_writeable(channel_t *chan);
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 02b783a8e3..dbed95fb43 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -175,7 +175,6 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port,
const char *id_digest,
const ed25519_public_key_t *ed_id)
{
- (void) ed_id; // XXXX not fully used yet
channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan));
channel_t *chan = &(tlschan->base_);
@@ -1661,9 +1660,10 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
connection_or_init_conn_from_address(chan->conn,
&(chan->conn->base_.addr),
chan->conn->base_.port,
+ /* zero, checked above */
(const char*)(chan->conn->handshake_state->
authenticated_rsa_peer_id),
- NULL, // XXXX Ed key
+ NULL, /* Ed25519 ID: Also checked as zero */
0);
}
}
@@ -2002,12 +2002,15 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
checked_ed_id, sizeof(ed25519_public_key_t));
}
+ log_debug(LD_HANDSHAKE, "calling client_learned_peer_id from "
+ "process_certs_cell");
+
if (connection_or_client_learned_peer_id(chan->conn,
chan->conn->handshake_state->authenticated_rsa_peer_id,
checked_ed_id) < 0)
ERR("Problem setting or checking peer id");
- log_info(LD_OR,
+ log_info(LD_HANDSHAKE,
"Got some good certificates from %s:%d: Authenticated it with "
"RSA%s",
safe_str(chan->conn->base_.address), chan->conn->base_.port,
@@ -2343,6 +2346,13 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS);
crypto_pk_free(identity_rcvd);
+ log_debug(LD_HANDSHAKE,
+ "Calling connection_or_init_conn_from_address for %s "
+ " from %s, with%s ed25519 id.",
+ safe_str(chan->conn->base_.address),
+ __func__,
+ ed_identity_received ? "" : "out");
+
connection_or_init_conn_from_address(chan->conn,
&(chan->conn->base_.addr),
chan->conn->base_.port,
@@ -2351,7 +2361,7 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
ed_identity_received,
0);
- log_info(LD_OR,
+ log_debug(LD_HANDSHAKE,
"Got an AUTHENTICATE cell from %s:%d, type %d: Looks good.",
safe_str(chan->conn->base_.address),
chan->conn->base_.port,
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index bf52b90730..a14a2b1482 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -64,8 +64,9 @@
#include "transports.h"
static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
- uint16_t port,
- const char *id_digest);
+ uint16_t port,
+ const char *id_digest,
+ const ed25519_public_key_t *ed_id);
static int circuit_deliver_create_cell(circuit_t *circ,
const create_cell_t *create_cell,
int relayed);
@@ -81,13 +82,12 @@ static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
*/
static channel_t *
channel_connect_for_circuit(const tor_addr_t *addr, uint16_t port,
- const char *id_digest)
+ const char *id_digest,
+ const ed25519_public_key_t *ed_id)
{
channel_t *chan;
- chan = channel_connect(addr, port, id_digest,
- NULL // XXXX Ed25519 id.
- );
+ chan = channel_connect(addr, port, id_digest, ed_id);
if (chan) command_setup_channel(chan);
return chan;
@@ -564,6 +564,7 @@ circuit_handle_first_hop(origin_circuit_t *circ)
firsthop->extend_info->port));
n_chan = channel_get_for_extend(firsthop->extend_info->identity_digest,
+ &firsthop->extend_info->ed_identity,
&firsthop->extend_info->addr,
&msg,
&should_launch);
@@ -581,7 +582,8 @@ circuit_handle_first_hop(origin_circuit_t *circ)
n_chan = channel_connect_for_circuit(
&firsthop->extend_info->addr,
firsthop->extend_info->port,
- firsthop->extend_info->identity_digest);
+ firsthop->extend_info->identity_digest,
+ &firsthop->extend_info->ed_identity);
if (!n_chan) { /* connect failed, forget the whole thing */
log_info(LD_CIRC,"connect to firsthop failed. Closing.");
return -END_CIRC_REASON_CONNECTFAILED;
@@ -1081,6 +1083,9 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
ec.orport_ipv4.port = hop->extend_info->port;
tor_addr_make_unspec(&ec.orport_ipv6.addr);
memcpy(ec.node_id, hop->extend_info->identity_digest, DIGEST_LEN);
+ /* Set the ED25519 identity too -- it will only get included
+ * in the extend2 cell if we're configured to use it, though. */
+ ed25519_pubkey_copy(&ec.ed_pubkey, &hop->extend_info->ed_identity);
len = onion_skin_create(ec.create_cell.handshake_type,
hop->extend_info,
@@ -1199,7 +1204,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
/* Check if they asked us for 0000..0000. We support using
* an empty fingerprint for the first hop (e.g. for a bridge relay),
- * but we don't want to let people send us extend cells for empty
+ * but we don't want to let clients send us extend cells for empty
* fingerprints -- a) because it opens the user up to a mitm attack,
* and b) because it lets an attacker force the relay to hold open a
* new TLS connection for each extend request. */
@@ -1209,6 +1214,18 @@ circuit_extend(cell_t *cell, circuit_t *circ)
return -1;
}
+ /* Fill in ed_pubkey if it was not provided and we can infer it from
+ * our networkstatus */
+ if (ed25519_public_key_is_zero(&ec.ed_pubkey)) {
+ const node_t *node = node_get_by_id((const char*)ec.node_id);
+ const ed25519_public_key_t *node_ed_id = NULL;
+ if (node &&
+ node_supports_ed25519_link_authentication(node) &&
+ (node_ed_id = node_get_ed25519_id(node))) {
+ ed25519_pubkey_copy(&ec.ed_pubkey, node_ed_id);
+ }
+ }
+
/* Next, check if we're being asked to connect to the hop that the
* extend cell came from. There isn't any reason for that, and it can
* assist circular-path attacks. */
@@ -1220,7 +1237,17 @@ circuit_extend(cell_t *cell, circuit_t *circ)
return -1;
}
+ /* Check the previous hop Ed25519 ID too */
+ if (! ed25519_public_key_is_zero(&ec.ed_pubkey) &&
+ ed25519_pubkey_eq(&ec.ed_pubkey,
+ &TO_OR_CIRCUIT(circ)->p_chan->ed25519_identity)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Client asked me to extend back to the previous hop "
+ "(by Ed25519 ID).");
+ }
+
n_chan = channel_get_for_extend((const char*)ec.node_id,
+ &ec.ed_pubkey,
&ec.orport_ipv4.addr,
&msg,
&should_launch);
@@ -1232,8 +1259,9 @@ circuit_extend(cell_t *cell, circuit_t *circ)
circ->n_hop = extend_info_new(NULL /*nickname*/,
(const char*)ec.node_id,
- NULL /*onion_key*/,
- NULL /*curve25519_key*/,
+ &ec.ed_pubkey,
+ NULL, /*onion_key*/
+ NULL, /*curve25519_key*/
&ec.orport_ipv4.addr,
ec.orport_ipv4.port);
@@ -1246,7 +1274,8 @@ circuit_extend(cell_t *cell, circuit_t *circ)
/* we should try to open a connection */
n_chan = channel_connect_for_circuit(&ec.orport_ipv4.addr,
ec.orport_ipv4.port,
- (const char*)ec.node_id);
+ (const char*)ec.node_id,
+ &ec.ed_pubkey);
if (!n_chan) {
log_info(LD_CIRC,"Launching n_chan failed. Closing circuit.");
circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
@@ -1548,9 +1577,9 @@ circuit_get_unhandled_ports(time_t now)
* If we're returning 0, set need_uptime and need_capacity to
* indicate any requirements that the unhandled ports have.
*/
-int
-circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
- int *need_capacity)
+MOCK_IMPL(int,
+circuit_all_predicted_ports_handled, (time_t now, int *need_uptime,
+ int *need_capacity))
{
int i, enough;
uint16_t *port;
@@ -2411,19 +2440,23 @@ onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
/** Allocate a new extend_info object based on the various arguments. */
extend_info_t *
-extend_info_new(const char *nickname, const char *digest,
+extend_info_new(const char *nickname,
+ const char *rsa_id_digest,
+ const ed25519_public_key_t *ed_id,
crypto_pk_t *onion_key,
- const curve25519_public_key_t *curve25519_key,
+ const curve25519_public_key_t *ntor_key,
const tor_addr_t *addr, uint16_t port)
{
extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t));
- memcpy(info->identity_digest, digest, DIGEST_LEN);
+ memcpy(info->identity_digest, rsa_id_digest, DIGEST_LEN);
+ if (ed_id && !ed25519_public_key_is_zero(ed_id))
+ memcpy(&info->ed_identity, ed_id, sizeof(ed25519_public_key_t));
if (nickname)
strlcpy(info->nickname, nickname, sizeof(info->nickname));
if (onion_key)
info->onion_key = crypto_pk_dup_key(onion_key);
- if (curve25519_key)
- memcpy(&info->curve25519_onion_key, curve25519_key,
+ if (ntor_key)
+ memcpy(&info->curve25519_onion_key, ntor_key,
sizeof(curve25519_public_key_t));
tor_addr_copy(&info->addr, addr);
info->port = port;
@@ -2473,20 +2506,35 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
return NULL;
}
+ const ed25519_public_key_t *ed_pubkey = NULL;
+
+ /* Don't send the ed25519 pubkey unless the target node actually supports
+ * authenticating with it. */
+ if (node_supports_ed25519_link_authentication(node)) {
+ log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node));
+ ed_pubkey = node_get_ed25519_id(node);
+ } else if (node_get_ed25519_id(node)) {
+ log_info(LD_CIRC, "Not including the ed25519 ID for %s, since it won't "
+ " be able to authenticate it.",
+ node_describe(node));
+ }
+
if (valid_addr && node->ri)
return extend_info_new(node->ri->nickname,
- node->identity,
- node->ri->onion_pkey,
- node->ri->onion_curve25519_pkey,
- &ap.addr,
- ap.port);
+ node->identity,
+ ed_pubkey,
+ node->ri->onion_pkey,
+ node->ri->onion_curve25519_pkey,
+ &ap.addr,
+ ap.port);
else if (valid_addr && node->rs && node->md)
return extend_info_new(node->rs->nickname,
- node->identity,
- node->md->onion_pkey,
- node->md->onion_curve25519_pkey,
- &ap.addr,
- ap.port);
+ node->identity,
+ ed_pubkey,
+ node->md->onion_pkey,
+ node->md->onion_curve25519_pkey,
+ &ap.addr,
+ ap.port);
else
return NULL;
}
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index b85dbecf7e..ddb070b427 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -42,15 +42,18 @@ int onionskin_answer(or_circuit_t *circ,
const struct created_cell_t *created_cell,
const char *keys,
const uint8_t *rend_circ_nonce);
-int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
- int *need_capacity);
+MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now,
+ int *need_uptime,
+ int *need_capacity));
int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info);
int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info);
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
-extend_info_t *extend_info_new(const char *nickname, const char *digest,
+extend_info_t *extend_info_new(const char *nickname,
+ const char *rsa_id_digest,
+ const ed25519_public_key_t *ed_id,
crypto_pk_t *onion_key,
- const curve25519_public_key_t *curve25519_key,
+ const curve25519_public_key_t *ntor_key,
const tor_addr_t *addr, uint16_t port);
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);
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index ab38b54985..5943e516ff 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -65,6 +65,7 @@
#include "control.h"
#include "entrynodes.h"
#include "main.h"
+#include "hs_circuitmap.h"
#include "hs_common.h"
#include "networkstatus.h"
#include "nodelist.h"
@@ -102,9 +103,6 @@ static smartlist_t *circuits_pending_close = NULL;
static void circuit_free_cpath_node(crypt_path_t *victim);
static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
-//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
-// const uint8_t *token);
-static void circuit_clear_rend_token(or_circuit_t *circ);
static void circuit_about_to_free_atexit(circuit_t *circ);
static void circuit_about_to_free(circuit_t *circ);
@@ -932,7 +930,9 @@ circuit_free(circuit_t *circ)
crypto_cipher_free(ocirc->n_crypto);
crypto_digest_free(ocirc->n_digest);
- circuit_clear_rend_token(ocirc);
+ if (ocirc->hs_token) {
+ hs_circuitmap_remove_circuit(ocirc);
+ }
if (ocirc->rend_splice) {
or_circuit_t *other = ocirc->rend_splice;
@@ -1481,177 +1481,6 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
return NULL;
}
-/** Map from rendezvous cookie to or_circuit_t */
-static digestmap_t *rend_cookie_map = NULL;
-
-/** Map from introduction point digest to or_circuit_t */
-static digestmap_t *intro_digest_map = NULL;
-
-/** Return the OR circuit whose purpose is <b>purpose</b>, and whose
- * rend_token is the REND_TOKEN_LEN-byte <b>token</b>. If <b>is_rend_circ</b>,
- * look for rendezvous point circuits; otherwise look for introduction point
- * circuits. */
-static or_circuit_t *
-circuit_get_by_rend_token_and_purpose(uint8_t purpose, int is_rend_circ,
- const char *token)
-{
- or_circuit_t *circ;
- digestmap_t *map = is_rend_circ ? rend_cookie_map : intro_digest_map;
-
- if (!map)
- return NULL;
-
- circ = digestmap_get(map, token);
- if (!circ ||
- circ->base_.purpose != purpose ||
- circ->base_.marked_for_close)
- return NULL;
-
- if (!circ->rendinfo) {
- char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN));
- log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned a "
- "circuit with no rendinfo set.",
- safe_str(t), is_rend_circ);
- tor_free(t);
- return NULL;
- }
-
- if (! bool_eq(circ->rendinfo->is_rend_circ, is_rend_circ) ||
- tor_memneq(circ->rendinfo->rend_token, token, REND_TOKEN_LEN)) {
- char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN));
- log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned %s:%d",
- safe_str(t), is_rend_circ,
- safe_str(hex_str(circ->rendinfo->rend_token, REND_TOKEN_LEN)),
- (int)circ->rendinfo->is_rend_circ);
- tor_free(t);
- return NULL;
- }
-
- return circ;
-}
-
-/** Clear the rendezvous cookie or introduction point key digest that's
- * configured on <b>circ</b>, if any, and remove it from any such maps. */
-static void
-circuit_clear_rend_token(or_circuit_t *circ)
-{
- or_circuit_t *found_circ;
- digestmap_t *map;
-
- if (!circ || !circ->rendinfo)
- return;
-
- map = circ->rendinfo->is_rend_circ ? rend_cookie_map : intro_digest_map;
-
- if (!map) {
- log_warn(LD_BUG, "Tried to clear rend token on circuit, but found no map");
- return;
- }
-
- found_circ = digestmap_get(map, circ->rendinfo->rend_token);
- if (found_circ == circ) {
- /* Great, this is the right one. */
- digestmap_remove(map, circ->rendinfo->rend_token);
- } else if (found_circ) {
- log_warn(LD_BUG, "Tried to clear rend token on circuit, but "
- "it was already replaced in the map.");
- } else {
- log_warn(LD_BUG, "Tried to clear rend token on circuit, but "
- "it not in the map at all.");
- }
-
- tor_free(circ->rendinfo); /* Sets it to NULL too */
-}
-
-/** Set the rendezvous cookie (if is_rend_circ), or the introduction point
- * digest (if ! is_rend_circ) of <b>circ</b> to the REND_TOKEN_LEN-byte value
- * in <b>token</b>, and add it to the appropriate map. If it previously had a
- * token, clear it. If another circuit previously had the same
- * cookie/intro-digest, mark that circuit and remove it from the map. */
-static void
-circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
- const uint8_t *token)
-{
- digestmap_t **map_p, *map;
- or_circuit_t *found_circ;
-
- /* Find the right map, creating it as needed */
- map_p = is_rend_circ ? &rend_cookie_map : &intro_digest_map;
-
- if (!*map_p)
- *map_p = digestmap_new();
-
- map = *map_p;
-
- /* If this circuit already has a token, we need to remove that. */
- if (circ->rendinfo)
- circuit_clear_rend_token(circ);
-
- if (token == NULL) {
- /* We were only trying to remove this token, not set a new one. */
- return;
- }
-
- found_circ = digestmap_get(map, (const char *)token);
- if (found_circ) {
- tor_assert(found_circ != circ);
- circuit_clear_rend_token(found_circ);
- if (! found_circ->base_.marked_for_close) {
- circuit_mark_for_close(TO_CIRCUIT(found_circ), END_CIRC_REASON_FINISHED);
- if (is_rend_circ) {
- log_fn(LOG_PROTOCOL_WARN, LD_REND,
- "Duplicate rendezvous cookie (%s...) used on two circuits",
- hex_str((const char*)token, 4)); /* only log first 4 chars */
- }
- }
- }
-
- /* Now set up the rendinfo */
- circ->rendinfo = tor_malloc(sizeof(*circ->rendinfo));
- memcpy(circ->rendinfo->rend_token, token, REND_TOKEN_LEN);
- circ->rendinfo->is_rend_circ = is_rend_circ ? 1 : 0;
-
- digestmap_set(map, (const char *)token, circ);
-}
-
-/** Return the circuit waiting for a rendezvous with the provided cookie.
- * Return NULL if no such circuit is found.
- */
-or_circuit_t *
-circuit_get_rendezvous(const uint8_t *cookie)
-{
- return circuit_get_by_rend_token_and_purpose(
- CIRCUIT_PURPOSE_REND_POINT_WAITING,
- 1, (const char*)cookie);
-}
-
-/** Return the circuit waiting for intro cells of the given digest.
- * Return NULL if no such circuit is found.
- */
-or_circuit_t *
-circuit_get_intro_point(const uint8_t *digest)
-{
- return circuit_get_by_rend_token_and_purpose(
- CIRCUIT_PURPOSE_INTRO_POINT, 0,
- (const char *)digest);
-}
-
-/** Set the rendezvous cookie of <b>circ</b> to <b>cookie</b>. If another
- * circuit previously had that cookie, mark it. */
-void
-circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie)
-{
- circuit_set_rend_token(circ, 1, cookie);
-}
-
-/** Set the intro point key digest of <b>circ</b> to <b>cookie</b>. If another
- * circuit previously had that intro point digest, mark it. */
-void
-circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest)
-{
- circuit_set_rend_token(circ, 0, digest);
-}
-
/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
* has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_*
* flags in <b>flags</b>, and if info is defined, does not already use info
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index e2102a118b..d83801a7a8 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -47,10 +47,6 @@ origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data(
const rend_data_t *rend_data);
origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
const uint8_t *digest, uint8_t purpose);
-or_circuit_t *circuit_get_rendezvous(const uint8_t *cookie);
-or_circuit_t *circuit_get_intro_point(const uint8_t *digest);
-void circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie);
-void circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest);
origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose,
extend_info_t *info, int flags);
void circuit_mark_all_unused_circs(void);
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 418acc0024..6cb99e4175 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -1431,7 +1431,7 @@ circuit_build_times_network_check_changed(circuit_build_times_t *cbt)
#define MAX_TIMEOUT ((int32_t) (INT32_MAX/2))
/* Check to see if this has happened before. If so, double the timeout
- * to give people on abysmally bad network connections a shot at access */
+ * to give clients on abysmally bad network connections a shot at access */
if (cbt->timeout_ms >= circuit_build_times_get_initial_timeout()) {
if (cbt->timeout_ms > MAX_TIMEOUT || cbt->close_ms > MAX_TIMEOUT) {
log_warn(LD_CIRC, "Insanely large circuit build timeout value. "
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 8e0fbd1d39..456a9c6712 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1039,8 +1039,117 @@ circuit_stream_is_being_handled(entry_connection_t *conn,
/** Don't keep more than this many unused open circuits around. */
#define MAX_UNUSED_OPEN_CIRCUITS 14
-/** Figure out how many circuits we have open that are clean. Make
- * sure it's enough for all the upcoming behaviors we predict we'll have.
+/* Return true if a circuit is available for use, meaning that it is open,
+ * clean, usable for new multi-hop connections, and a general purpose origin
+ * circuit.
+ * Accept any kind of circuit, return false if the above conditions are not
+ * met. */
+STATIC int
+circuit_is_available_for_use(const circuit_t *circ)
+{
+ const origin_circuit_t *origin_circ;
+ cpath_build_state_t *build_state;
+
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ return 0; /* We first filter out only origin circuits before doing the
+ following checks. */
+ if (circ->marked_for_close)
+ return 0; /* Don't mess with marked circs */
+ if (circ->timestamp_dirty)
+ return 0; /* Only count clean circs */
+ if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ return 0; /* We only pay attention to general purpose circuits.
+ General purpose circuits are always origin circuits. */
+
+ origin_circ = CONST_TO_ORIGIN_CIRCUIT(circ);
+ if (origin_circ->unusable_for_new_conns)
+ return 0;
+
+ build_state = origin_circ->build_state;
+ if (build_state->onehop_tunnel)
+ return 0;
+
+ return 1;
+}
+
+/* Return true if we need any more exit circuits.
+ * needs_uptime and needs_capacity are set only if we need more exit circuits.
+ * Check if we know of a port that's been requested recently and no circuit
+ * is currently available that can handle it. */
+STATIC int
+needs_exit_circuits(time_t now, int *needs_uptime, int *needs_capacity)
+{
+ return (!circuit_all_predicted_ports_handled(now, needs_uptime,
+ needs_capacity) &&
+ router_have_consensus_path() == CONSENSUS_PATH_EXIT);
+}
+
+/* Hidden services need at least this many internal circuits */
+#define SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS 3
+
+/* Return true if we need any more hidden service server circuits.
+ * HS servers only need an internal circuit. */
+STATIC int
+needs_hs_server_circuits(int num_uptime_internal)
+{
+ return (num_rend_services() &&
+ num_uptime_internal < SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS &&
+ router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN);
+}
+
+/* We need at least this many internal circuits for hidden service clients */
+#define SUFFICIENT_INTERNAL_HS_CLIENTS 3
+
+/* We need at least this much uptime for internal circuits for hidden service
+ * clients */
+#define SUFFICIENT_UPTIME_INTERNAL_HS_CLIENTS 2
+
+/* Return true if we need any more hidden service client circuits.
+ * HS clients only need an internal circuit. */
+STATIC int
+needs_hs_client_circuits(time_t now, int *needs_uptime, int *needs_capacity,
+ int num_internal, int num_uptime_internal)
+{
+ int used_internal_recently = rep_hist_get_predicted_internal(now,
+ needs_uptime,
+ needs_capacity);
+ int requires_uptime = num_uptime_internal <
+ SUFFICIENT_UPTIME_INTERNAL_HS_CLIENTS &&
+ needs_uptime;
+
+ return (used_internal_recently &&
+ (requires_uptime || num_internal < SUFFICIENT_INTERNAL_HS_CLIENTS) &&
+ router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN);
+}
+
+/* The minimum number of open slots we should keep in order to preemptively
+ * build circuits. */
+#define CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS 2
+
+/* Check to see if we need more circuits to have a good build timeout. However,
+ * leave a couple slots open so that we can still build circuits preemptively
+ * as needed. */
+#define CBT_MAX_UNUSED_OPEN_CIRCUITS (MAX_UNUSED_OPEN_CIRCUITS - \
+ CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS)
+
+/* Return true if we need more circuits for a good build timeout.
+ * XXXX make the assumption that build timeout streams should be
+ * created whenever we can build internal circuits. */
+STATIC int
+needs_circuits_for_build(int num)
+{
+ if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
+ if (num < CBT_MAX_UNUSED_OPEN_CIRCUITS &&
+ !circuit_build_times_disabled() &&
+ circuit_build_times_needs_circuits_now(get_circuit_build_times())) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Determine how many circuits we have open that are clean,
+ * Make sure it's enough for all the upcoming behaviors we predict we'll have.
* But put an upper bound on the total number of circuits.
*/
static void
@@ -1052,25 +1161,14 @@ circuit_predict_and_launch_new(void)
time_t now = time(NULL);
int flags = 0;
- /* First, count how many of each type of circuit we have already. */
+ /* Count how many of each type of circuit we currently have. */
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
- cpath_build_state_t *build_state;
- origin_circuit_t *origin_circ;
- if (!CIRCUIT_IS_ORIGIN(circ))
- continue;
- if (circ->marked_for_close)
- continue; /* don't mess with marked circs */
- if (circ->timestamp_dirty)
- continue; /* only count clean circs */
- if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL)
- continue; /* only pay attention to general-purpose circs */
- origin_circ = TO_ORIGIN_CIRCUIT(circ);
- if (origin_circ->unusable_for_new_conns)
- continue;
- build_state = origin_circ->build_state;
- if (build_state->onehop_tunnel)
+ if (!circuit_is_available_for_use(circ))
continue;
+
num++;
+
+ cpath_build_state_t *build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
if (build_state->is_internal)
num_internal++;
if (build_state->need_uptime && build_state->is_internal)
@@ -1080,19 +1178,14 @@ circuit_predict_and_launch_new(void)
/* If that's enough, then stop now. */
if (num >= MAX_UNUSED_OPEN_CIRCUITS)
- return; /* we already have many, making more probably will hurt */
-
- /* Second, see if we need any more exit circuits. */
- /* check if we know of a port that's been requested recently
- * and no circuit is currently available that can handle it.
- * Exits (obviously) require an exit circuit. */
- if (!circuit_all_predicted_ports_handled(now, &port_needs_uptime,
- &port_needs_capacity)
- && router_have_consensus_path() == CONSENSUS_PATH_EXIT) {
+ return;
+
+ if (needs_exit_circuits(now, &port_needs_uptime, &port_needs_capacity)) {
if (port_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME;
if (port_needs_capacity)
flags |= CIRCLAUNCH_NEED_CAPACITY;
+
log_info(LD_CIRC,
"Have %d clean circs (%d internal), need another exit circ.",
num, num_internal);
@@ -1100,12 +1193,10 @@ circuit_predict_and_launch_new(void)
return;
}
- /* Third, see if we need any more hidden service (server) circuits.
- * HS servers only need an internal circuit. */
- if (num_rend_services() && num_uptime_internal < 3
- && router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
+ if (needs_hs_server_circuits(num_uptime_internal)) {
flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME |
CIRCLAUNCH_IS_INTERNAL);
+
log_info(LD_CIRC,
"Have %d clean circs (%d internal), need another internal "
"circ for my hidden service.",
@@ -1114,18 +1205,16 @@ circuit_predict_and_launch_new(void)
return;
}
- /* Fourth, see if we need any more hidden service (client) circuits.
- * HS clients only need an internal circuit. */
- if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime,
- &hidserv_needs_capacity) &&
- ((num_uptime_internal<2 && hidserv_needs_uptime) ||
- num_internal<3)
- && router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
+ if (needs_hs_client_circuits(now, &hidserv_needs_uptime,
+ &hidserv_needs_capacity,
+ num_internal, num_uptime_internal))
+ {
if (hidserv_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME;
if (hidserv_needs_capacity)
flags |= CIRCLAUNCH_NEED_CAPACITY;
flags |= CIRCLAUNCH_IS_INTERNAL;
+
log_info(LD_CIRC,
"Have %d clean circs (%d uptime-internal, %d internal), need"
" another hidden service circ.",
@@ -1134,26 +1223,17 @@ circuit_predict_and_launch_new(void)
return;
}
- /* Finally, check to see if we still need more circuits to learn
- * a good build timeout. But if we're close to our max number we
- * want, don't do another -- we want to leave a few slots open so
- * we can still build circuits preemptively as needed.
- * XXXX make the assumption that build timeout streams should be
- * created whenever we can build internal circuits. */
- if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
- if (num < MAX_UNUSED_OPEN_CIRCUITS-2 &&
- ! circuit_build_times_disabled() &&
- circuit_build_times_needs_circuits_now(get_circuit_build_times())) {
- flags = CIRCLAUNCH_NEED_CAPACITY;
- /* if there are no exits in the consensus, make timeout
- * circuits internal */
- if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL)
- flags |= CIRCLAUNCH_IS_INTERNAL;
+ if (needs_circuits_for_build(num)) {
+ flags = CIRCLAUNCH_NEED_CAPACITY;
+ /* if there are no exits in the consensus, make timeout
+ * circuits internal */
+ if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL)
+ flags |= CIRCLAUNCH_IS_INTERNAL;
+
log_info(LD_CIRC,
"Have %d clean circs need another buildtime test circ.", num);
circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
return;
- }
}
}
@@ -2110,6 +2190,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (want_onehop && conn->chosen_exit_name[0] == '$') {
/* We're asking for a one-hop circuit to a router that
* we don't have a routerinfo about. Make up an extend_info. */
+ /* XXX prop220: we need to make chosen_exit_name able to
+ * encode both key formats. This is not absolutely critical
+ * since this is just for one-hop circuits, but we should
+ * still get it done */
char digest[DIGEST_LEN];
char *hexdigest = conn->chosen_exit_name+1;
tor_addr_t addr;
@@ -2124,9 +2208,12 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
escaped_safe_str_client(conn->socks_request->address));
return -1;
}
+ /* XXXX prop220 add a workaround for ed25519 ID below*/
extend_info = extend_info_new(conn->chosen_exit_name+1,
- digest, NULL, NULL, &addr,
- conn->socks_request->port);
+ digest,
+ NULL, /* Ed25519 ID */
+ NULL, NULL, /* onion keys */
+ &addr, conn->socks_request->port);
} else { /* ! (want_onehop && conn->chosen_exit_name[0] == '$') */
/* We will need an onion key for the router, and we
* don't have one. Refuse or relax requirements. */
@@ -2533,7 +2620,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
log_debug(LD_APP|LD_CIRC,
"Attaching apconn to circ %u (stream %d sec old).",
(unsigned)circ->base_.n_circ_id, conn_age);
- /* print the circ's path, so people can figure out which circs are
+ /* print the circ's path, so clients can figure out which circs are
* sucking. */
circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ);
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index 110bdda5b2..e5f8700ea2 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -60,5 +60,25 @@ int hostname_in_track_host_exits(const or_options_t *options,
const char *address);
void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ);
+#ifdef TOR_UNIT_TESTS
+/* Used only by circuituse.c and test_circuituse.c */
+
+STATIC int circuit_is_available_for_use(const circuit_t *circ);
+
+STATIC int needs_exit_circuits(time_t now,
+ int *port_needs_uptime,
+ int *port_needs_capacity);
+STATIC int needs_hs_server_circuits(int num_uptime_internal);
+
+STATIC int needs_hs_client_circuits(time_t now,
+ int *needs_uptime,
+ int *needs_capacity,
+ int num_internal,
+ int num_uptime_internal);
+
+STATIC int needs_circuits_for_build(int num);
+
+#endif
+
#endif
diff --git a/src/or/config.c b/src/or/config.c
index 2ec96d39e9..aba567a835 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -219,7 +219,7 @@ static config_var_t option_vars_[] = {
OBSOLETE("AuthDirListBadDirs"),
V(AuthDirListBadExits, BOOL, "0"),
V(AuthDirMaxServersPerAddr, UINT, "2"),
- V(AuthDirMaxServersPerAuthAddr,UINT, "5"),
+ OBSOLETE("AuthDirMaxServersPerAuthAddr"),
V(AuthDirHasIPv6Connectivity, BOOL, "0"),
VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"),
V(AutomapHostsOnResolve, BOOL, "0"),
@@ -306,6 +306,7 @@ static config_var_t option_vars_[] = {
V(ExtORPortCookieAuthFile, STRING, NULL),
V(ExtORPortCookieAuthFileGroupReadable, BOOL, "0"),
V(ExtraInfoStatistics, BOOL, "1"),
+ V(ExtendByEd25519ID, AUTOBOOL, "auto"),
V(FallbackDir, LINELIST, NULL),
/* XXXX prop271 -- this has an ugly name to remind us to remove it. */
VAR("UseDeprecatedGuardAlgorithm_", BOOL,
@@ -502,6 +503,7 @@ static config_var_t option_vars_[] = {
V(User, STRING, NULL),
OBSOLETE("UserspaceIOCPBuffers"),
V(AuthDirSharedRandomness, BOOL, "1"),
+ V(AuthDirTestEd25519LinkKeys, BOOL, "1"),
OBSOLETE("V1AuthoritativeDirectory"),
OBSOLETE("V2AuthoritativeDirectory"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
@@ -597,7 +599,6 @@ static const config_var_t testing_tor_network_defaults[] = {
V(EnforceDistinctSubnets, BOOL, "0"),
V(AssumeReachable, BOOL, "1"),
V(AuthDirMaxServersPerAddr, UINT, "0"),
- V(AuthDirMaxServersPerAuthAddr,UINT, "0"),
V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
"0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
@@ -1815,25 +1816,6 @@ options_act(const or_options_t *old_options)
monitor_owning_controller_process(options->OwningControllerProcess);
- /* We must create new keys after we poison the directories, because our
- * poisoning code checks for existing keys, and refuses to modify their
- * directories. */
-
- /* If we use non-anonymous single onion services, make sure we poison any
- new hidden service directories, so that we never accidentally launch the
- non-anonymous hidden services thinking they are anonymous. */
- if (running_tor && rend_service_non_anonymous_mode_enabled(options)) {
- if (options->RendConfigLines && !num_rend_services()) {
- log_warn(LD_BUG,"Error: hidden services configured, but not parsed.");
- return -1;
- }
- if (rend_service_poison_new_single_onion_dirs(NULL) < 0) {
- log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous"
- ".");
- return -1;
- }
- }
-
/* reload keys as needed for rendezvous services. */
if (rend_service_load_all_keys(NULL)<0) {
log_warn(LD_GENERAL,"Error loading rendezvous service keys");
@@ -1964,7 +1946,7 @@ options_act(const or_options_t *old_options)
addressmap_clear_invalid_automaps(options);
/* How long should we delay counting bridge stats after becoming a bridge?
- * We use this so we don't count people who used our bridge thinking it is
+ * We use this so we don't count clients who used our bridge thinking it is
* a relay. If you change this, don't forget to change the log message
* below. It's 4 hours (the time it takes to stop being used by clients)
* plus some extra time for clock skew. */
@@ -2990,21 +2972,6 @@ options_validate_single_onion(or_options_t *options, char **msg)
options->UseEntryGuards = 0;
}
- /* Check if existing hidden service keys were created in a different
- * single onion service mode, and refuse to launch if they
- * have. We'll poison new keys in options_act() just before we create them.
- */
- if (rend_service_list_verify_single_onion_poison(NULL, options) < 0) {
- log_warn(LD_GENERAL, "We are configured with "
- "HiddenServiceNonAnonymousMode %d, but one or more hidden "
- "service keys were created in %s mode. This is not allowed.",
- rend_service_non_anonymous_mode_enabled(options) ? 1 : 0,
- rend_service_non_anonymous_mode_enabled(options) ?
- "an anonymous" : "a non-anonymous"
- );
- return -1;
- }
-
return 0;
}
@@ -3144,7 +3111,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
} else if (!strcasecmp(options->TransProxyType, "ipfw")) {
#ifndef KERNEL_MAY_SUPPORT_IPFW
/* Earlier versions of OS X have ipfw */
- REJECT("ipfw is a FreeBSD-specific"
+ REJECT("ipfw is a FreeBSD-specific "
"and OS X/Darwin-specific feature.");
#else
options->TransProxyType_parsed = TPT_IPFW;
@@ -4978,12 +4945,12 @@ options_init_from_torrc(int argc, char **argv)
}
if (config_line_find(cmdline_only_options, "--version")) {
- printf("Tor version %s.\n",get_version());
+ printf("Tor %s\n", get_version());
exit(0);
}
if (config_line_find(cmdline_only_options, "--library-versions")) {
- printf("Tor version %s. \n", get_version());
+ printf("Tor %s\n", get_version());
printf("Library versions\tCompiled\t\tRuntime\n");
printf("Libevent\t\t%-15s\t\t%s\n",
tor_libevent_get_header_version_str(),
diff --git a/src/or/connection.c b/src/or/connection.c
index 87f0f91fd2..5b22594969 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -650,7 +650,7 @@ connection_free_(connection_t *conn)
if (conn->type == CONN_TYPE_OR &&
!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) {
log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
- connection_or_remove_from_identity_map(TO_OR_CONN(conn));
+ connection_or_clear_identity(TO_OR_CONN(conn));
}
if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) {
connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn));
@@ -681,7 +681,7 @@ connection_free,(connection_t *conn))
}
if (connection_speaks_cells(conn)) {
if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) {
- connection_or_remove_from_identity_map(TO_OR_CONN(conn));
+ connection_or_clear_identity(TO_OR_CONN(conn));
}
}
if (conn->type == CONN_TYPE_CONTROL) {
@@ -1601,16 +1601,19 @@ connection_handle_listener_read(connection_t *conn, int new_type)
/* remember the remote address */
tor_addr_copy(&newconn->addr, &addr);
- newconn->port = port;
- newconn->address = tor_addr_to_str_dup(&addr);
+ if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) {
+ newconn->port = 0;
+ newconn->address = tor_strdup(conn->address);
+ } else {
+ newconn->port = port;
+ newconn->address = tor_addr_to_str_dup(&addr);
+ }
if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) {
log_info(LD_NET, "New SOCKS connection opened from %s.",
fmt_and_decorate_addr(&addr));
}
if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) {
- newconn->port = 0;
- newconn->address = tor_strdup(conn->address);
log_info(LD_NET, "New SOCKS AF_UNIX connection opened");
}
if (new_type == CONN_TYPE_CONTROL) {
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 3874d52c23..fb077bb0c5 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -1300,7 +1300,7 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
* an internal address? If so, we should reject it if we're configured to
* do so. */
if (options->ClientDNSRejectInternalAddresses) {
- /* Don't let people try to do a reverse lookup on 10.0.0.1. */
+ /* Don't let clients try to do a reverse lookup on 10.0.0.1. */
tor_addr_t addr;
int ok;
ok = tor_addr_parse_PTR_name(
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 6233fb7669..b3ae291831 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -76,56 +76,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)
{
@@ -133,60 +102,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 *rsa_digest,
const ed25519_public_key_t *ed_id)
{
- (void) ed_id; // DOCDOC // XXXX not implemented yet.
- or_connection_t *tmp;
+ channel_t *chan = NULL;
tor_assert(conn);
tor_assert(rsa_digest);
- if (!orconn_identity_map)
- orconn_identity_map = digestmap_new();
- if (tor_memeq(conn->identity_digest, rsa_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, rsa_digest, DIGEST_LEN);
- /* If we're setting the ID to zero, don't add a mapping. */
- if (tor_digest_is_zero(rsa_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, rsa_digest, conn);
- conn->next_with_same_id = tmp;
-
/* Deal with channels */
- if (conn->chan)
- channel_set_identity_digest(TLS_CHAN_TO_BASE(conn->chan), rsa_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, rsa_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
@@ -889,14 +870,44 @@ connection_or_init_conn_from_address(or_connection_t *conn,
const ed25519_public_key_t *ed_id,
int started_here)
{
- (void) ed_id; // not fully used yet.
- const node_t *r = node_get_by_id(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) &&
+ ! 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);
@@ -918,10 +929,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,
@@ -967,7 +980,7 @@ 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
+/** 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().
*
@@ -984,16 +997,19 @@ 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) {
+ 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;
@@ -1017,11 +1033,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. */
@@ -1048,7 +1064,7 @@ connection_or_group_set_badness(or_connection_t *head, int force)
0)) {
best = or_conn;
}
- }
+ } SMARTLIST_FOREACH_END(or_conn);
if (!best)
return;
@@ -1067,7 +1083,7 @@ 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)
@@ -1101,24 +1117,7 @@ connection_or_group_set_badness(or_connection_t *head, int force)
connection_or_mark_bad_for_new_circs(or_conn);
}
}
- }
-}
-
-/** 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)
-{
- if (!orconn_identity_map)
- return;
-
- 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;
+ } SMARTLIST_FOREACH_END(or_conn);
}
/** <b>conn</b> is in the 'connecting' state, and it failed to complete
@@ -1188,7 +1187,6 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
const ed25519_public_key_t *ed_id,
channel_tls_t *chan))
{
- (void) ed_id; // XXXX not fully used yet.
or_connection_t *conn;
const or_options_t *options = get_options();
int socket_error = 0;
@@ -1207,6 +1205,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));
@@ -1576,20 +1579,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,
- NULL // Ed25519 ID
- );
+ 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.
@@ -1613,11 +1621,26 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
const uint8_t *rsa_peer_id,
const ed25519_public_key_t *ed_peer_id)
{
- (void) ed_peer_id; // not used yet.
-
const or_options_t *options = get_options();
-
- if (tor_digest_is_zero(conn->identity_digest)) {
+ 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);
@@ -1631,16 +1654,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*)rsa_peer_id /*, ed_peer_id XXXX */);
+ (const char*)rsa_peer_id, ed_peer_id);
+ changed_identity = 1;
}
- if (tor_memneq(rsa_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*)rsa_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());
@@ -1675,9 +1721,11 @@ 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);
+ "Tried connecting to router at %s:%d, but RSA identity key was 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));
#ifdef ENABLE_LEGACY_GUARD_ALGORITHM
@@ -1694,9 +1742,24 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
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*)rsa_peer_id /*, ed_id XXXX */);
+ (const char*)rsa_peer_id, ed_peer_id);
}
return 0;
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index da95718ac9..80a5bddb14 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -12,14 +12,13 @@
#ifndef TOR_CONNECTION_OR_H
#define TOR_CONNECTION_OR_H
-void connection_or_remove_from_identity_map(or_connection_t *conn);
+void connection_or_clear_identity(or_connection_t *conn);
void connection_or_clear_identity_map(void);
void clear_broken_connection_map(int disable);
or_connection_t *connection_or_get_for_extend(const char *digest,
const tor_addr_t *target_addr,
const char **msg_out,
int *launch_out);
-void connection_or_set_bad_connections(const char *digest, int force);
void connection_or_block_renegotiation(or_connection_t *conn);
int connection_or_reached_eof(or_connection_t *conn);
@@ -111,5 +110,7 @@ void var_cell_free(var_cell_t *cell);
/* DOCDOC */
#define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4
+void connection_or_group_set_badness_(smartlist_t *group, int force);
+
#endif
diff --git a/src/or/control.c b/src/or/control.c
index 9a8845a0d3..248c780cce 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -2030,7 +2030,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
} else if (!strcmpstart(question, "dir/status/")) {
*answer = tor_strdup("");
} else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */
- if (directory_caches_dir_info(get_options())) {
+ if (we_want_to_fetch_flavor(get_options(), FLAV_NS)) {
const cached_dir_t *consensus = dirserv_get_consensus("ns");
if (consensus)
*answer = tor_strdup(consensus->dir);
diff --git a/src/or/directory.c b/src/or/directory.c
index 9c039a006f..1c9ee96c43 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2994,6 +2994,28 @@ handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
return 0;
}
+/** Warn that the consensus <b>v</b> of type <b>flavor</b> is too old and will
+ * not be served to clients. Rate-limit the warning to avoid logging an entry
+ * on every request.
+ */
+static void
+warn_consensus_is_too_old(networkstatus_t *v, const char *flavor, time_t now)
+{
+#define TOO_OLD_WARNING_INTERVAL (60*60)
+ static ratelim_t warned = RATELIM_INIT(TOO_OLD_WARNING_INTERVAL);
+ char timestamp[ISO_TIME_LEN+1];
+ char *dupes;
+
+ if ((dupes = rate_limit_log(&warned, now))) {
+ format_local_iso_time(timestamp, v->valid_until);
+ log_warn(LD_DIRSERV, "Our %s%sconsensus is too old, so we will not "
+ "serve it to clients. It was valid until %s local time and we "
+ "continued to serve it for up to 24 hours after it expired.%s",
+ flavor ? flavor : "", flavor ? " " : "", timestamp, dupes);
+ tor_free(dupes);
+ }
+}
+
/** Helper function for GET /tor/status-vote/current/consensus
*/
static int
@@ -3038,6 +3060,15 @@ handle_get_current_consensus(dir_connection_t *conn,
v = networkstatus_get_latest_consensus_by_flavor(flav);
+ if (v && !networkstatus_consensus_reasonably_live(v, now)) {
+ write_http_status_line(conn, 404, "Consensus is too old");
+ warn_consensus_is_too_old(v, flavor, now);
+ smartlist_free(dir_fps);
+ geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
+ tor_free(flavor);
+ goto done;
+ }
+
if (v && want_fps &&
!client_likes_consensus(v, want_fps)) {
write_http_status_line(conn, 404, "Consensus not signed by sufficient "
@@ -4043,10 +4074,12 @@ STATIC int
next_random_exponential_delay(int delay, int max_delay)
{
/* Check preconditions */
+ if (BUG(max_delay < 0))
+ max_delay = 0;
if (BUG(delay > max_delay))
delay = max_delay;
- if (BUG(delay == INT_MAX))
- delay -= 1; /* prevent overflow */
+ if (delay == INT_MAX)
+ return INT_MAX; /* prevent overflow */
if (BUG(delay < 0))
delay = 0;
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 7ff37b4c62..4d349ddf16 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -1069,8 +1069,10 @@ directory_fetches_dir_info_later(const or_options_t *options)
return options->UseBridges != 0;
}
-/** Return true iff we want to fetch and keep certificates for authorities
+/** Return true iff we want to serve certificates for authorities
* that we don't acknowledge as authorities ourself.
+ * Use we_want_to_fetch_unknown_auth_certs to check if we want to fetch
+ * and keep these certificates.
*/
int
directory_caches_unknown_auth_certs(const or_options_t *options)
@@ -1078,11 +1080,14 @@ directory_caches_unknown_auth_certs(const or_options_t *options)
return dir_server_mode(options) || options->BridgeRelay;
}
-/** Return 1 if we want to keep descriptors, networkstatuses, etc around.
+/** Return 1 if we want to fetch and serve descriptors, networkstatuses, etc
* Else return 0.
* Check options->DirPort_set and directory_permits_begindir_requests()
* to see if we are willing to serve these directory documents to others via
* the DirPort and begindir-over-ORPort, respectively.
+ *
+ * To check if we should fetch documents, use we_want_to_fetch_flavor and
+ * we_want_to_fetch_unknown_auth_certs instead of this function.
*/
int
directory_caches_dir_info(const or_options_t *options)
@@ -1097,7 +1102,7 @@ directory_caches_dir_info(const or_options_t *options)
should_refuse_unknown_exits(options);
}
-/** Return 1 if we want to allow remote people to ask us directory
+/** Return 1 if we want to allow remote clients to ask us directory
* requests via the "begin_dir" interface, which doesn't require
* having any separate port open. */
int
@@ -2051,12 +2056,8 @@ get_possible_sybil_list(const smartlist_t *routers)
int addr_count;
/* Allow at most this number of Tor servers on a single IP address, ... */
int max_with_same_addr = options->AuthDirMaxServersPerAddr;
- /* ... unless it's a directory authority, in which case allow more. */
- int max_with_same_addr_on_authority = options->AuthDirMaxServersPerAuthAddr;
if (max_with_same_addr <= 0)
max_with_same_addr = INT_MAX;
- if (max_with_same_addr_on_authority <= 0)
- max_with_same_addr_on_authority = INT_MAX;
smartlist_add_all(routers_by_ip, routers);
smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_);
@@ -2069,9 +2070,7 @@ get_possible_sybil_list(const smartlist_t *routers)
last_addr = ri->addr;
addr_count = 1;
} else if (++addr_count > max_with_same_addr) {
- if (!router_addr_is_trusted_dir(ri->addr) ||
- addr_count > max_with_same_addr_on_authority)
- digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri);
+ digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri);
}
} SMARTLIST_FOREACH_END(ri);
@@ -2231,8 +2230,8 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs)
}
/** Routerstatus <b>rs</b> is part of a group of routers that are on
- * too narrow an IP-space. Clear out its flags: we don't want people
- * using it.
+ * too narrow an IP-space. Clear out its flags since we don't want it be used
+ * because of its Sybil-like appearance.
*/
static void
clear_status_flags_on_sybil(routerstatus_t *rs)
@@ -3171,7 +3170,8 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
void
dirserv_orconn_tls_done(const tor_addr_t *addr,
uint16_t or_port,
- const char *digest_rcvd)
+ const char *digest_rcvd,
+ const ed25519_public_key_t *ed_id_rcvd)
{
node_t *node = NULL;
tor_addr_port_t orport;
@@ -3183,8 +3183,25 @@ dirserv_orconn_tls_done(const tor_addr_t *addr,
node = node_get_mutable_by_id(digest_rcvd);
if (node == NULL || node->ri == NULL)
return;
+
ri = node->ri;
+ if (get_options()->AuthDirTestEd25519LinkKeys &&
+ ri->cache_info.signing_key_cert) {
+ /* We allow the node to have an ed25519 key if we haven't been told one in
+ * the routerinfo, but if we *HAVE* been told one in the routerinfo, it
+ * needs to match. */
+ const ed25519_public_key_t *expected_id =
+ &ri->cache_info.signing_key_cert->signing_key;
+ tor_assert(!ed25519_public_key_is_zero(expected_id));
+ if (! ed_id_rcvd || ! ed25519_pubkey_eq(ed_id_rcvd, expected_id)) {
+ log_info(LD_DIRSERV, "Router at %s:%d with RSA ID %s "
+ "did not present expected Ed25519 ID.",
+ fmt_addr(addr), or_port, hex_str(digest_rcvd, DIGEST_LEN));
+ return; /* Don't mark it as reachable. */
+ }
+ }
+
tor_addr_copy(&orport.addr, addr);
orport.port = or_port;
if (router_has_orport(ri, &orport)) {
@@ -3240,23 +3257,31 @@ dirserv_should_launch_reachability_test(const routerinfo_t *ri,
void
dirserv_single_reachability_test(time_t now, routerinfo_t *router)
{
+ const or_options_t *options = get_options();
channel_t *chan = NULL;
node_t *node = NULL;
tor_addr_t router_addr;
+ const ed25519_public_key_t *ed_id_key;
(void) now;
tor_assert(router);
node = node_get_mutable_by_id(router->cache_info.identity_digest);
tor_assert(node);
+ if (options->AuthDirTestEd25519LinkKeys &&
+ node_supports_ed25519_link_authentication(node)) {
+ ed_id_key = &router->cache_info.signing_key_cert->signing_key;
+ } else {
+ ed_id_key = NULL;
+ }
+
/* IPv4. */
log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
router->nickname, fmt_addr32(router->addr), router->or_port);
tor_addr_from_ipv4h(&router_addr, router->addr);
chan = channel_tls_connect(&router_addr, router->or_port,
router->cache_info.identity_digest,
- NULL // XXXX Ed25519 ID.
- );
+ ed_id_key);
if (chan) command_setup_channel(chan);
/* Possible IPv6. */
@@ -3269,8 +3294,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router)
router->ipv6_orport);
chan = channel_tls_connect(&router->ipv6_addr, router->ipv6_orport,
router->cache_info.identity_digest,
- NULL // XXXX Ed25519 ID.
- );
+ ed_id_key);
if (chan) command_setup_channel(chan);
}
}
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index 1e4f27e3d7..e83da5e5ac 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -73,7 +73,8 @@ int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
const char **msg);
void dirserv_orconn_tls_done(const tor_addr_t *addr,
uint16_t or_port,
- const char *digest_rcvd);
+ const char *digest_rcvd,
+ const ed25519_public_key_t *ed_id_rcvd);
int dirserv_should_launch_reachability_test(const routerinfo_t *ri,
const routerinfo_t *ri_old);
void dirserv_single_reachability_test(time_t now, routerinfo_t *router);
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index c5c0a88b09..8768b2a1d1 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -284,7 +284,7 @@ dnsserv_reject_request(entry_connection_t *conn)
}
/** Look up the original name that corresponds to 'addr' in req. We use this
- * to preserve case in order to facilitate people using 0x20-hacks to avoid
+ * to preserve case in order to facilitate clients using 0x20-hacks to avoid
* DNS poisoning. */
static const char *
evdns_get_orig_address(const struct evdns_server_request *req,
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index ac5398f5f8..4c6824760c 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -118,6 +118,7 @@
#define ENTRYNODES_PRIVATE
#include "or.h"
+#include "channel.h"
#include "bridges.h"
#include "circpathbias.h"
#include "circuitbuild.h"
@@ -126,7 +127,6 @@
#include "config.h"
#include "confparse.h"
#include "connection.h"
-#include "connection_or.h"
#include "control.h"
#include "directory.h"
#include "entrynodes.h"
@@ -4859,7 +4859,7 @@ entries_retry_helper(const or_options_t *options, int act)
* the node down and undermine the retry attempt. We mark even
* the established conns, since if the network just came back
* we'll want to attach circuits to fresh conns. */
- connection_or_set_bad_connections(node->identity, 1);
+ channel_update_bad_for_new_circs(node->identity, 1);
/* mark this entry node for retry */
router_set_status(node->identity, 1);
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index aaf5c4bdcd..c2b3bbb839 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -424,8 +424,8 @@ configure_accounting(time_t now)
if (-0.50 <= delta && delta <= 0.50) {
/* The start of the period is now a little later or earlier than we
* remembered. That's fine; we might lose some bytes we could otherwise
- * have written, but better to err on the side of obeying people's
- * accounting settings. */
+ * have written, but better to err on the side of obeying accounting
+ * settings. */
log_info(LD_ACCT, "Accounting interval moved by %.02f%%; "
"that's fine.", delta*100);
interval_end_time = start_of_accounting_period_after(now);
@@ -896,7 +896,7 @@ hibernate_go_dormant(time_t now)
log_notice(LD_ACCT,"Going dormant. Blowing away remaining connections.");
/* Close all OR/AP/exit conns. Leave dir conns because we still want
- * to be able to upload server descriptors so people know we're still
+ * to be able to upload server descriptors so clients know we're still
* running, and download directories so we can detect if we're obsolete.
* Leave control conns because we still want to be controllable.
*/
diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c
new file mode 100644
index 0000000000..5003b4b593
--- /dev/null
+++ b/src/or/hs_circuitmap.c
@@ -0,0 +1,328 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_circuitmap.c
+ *
+ * \brief Manage the hidden service circuitmap: A hash table that maps binary
+ * tokens to introduction and rendezvous circuits.
+ **/
+
+#define HS_CIRCUITMAP_PRIVATE
+
+#include "or.h"
+#include "config.h"
+#include "circuitlist.h"
+#include "hs_circuitmap.h"
+
+/************************** HS circuitmap code *******************************/
+
+/* This is the hidden service circuitmap. It's a hash table that maps
+ introduction and rendezvous tokens to specific circuits such that given a
+ token it's easy to find the corresponding circuit. */
+static struct hs_circuitmap_ht *the_hs_circuitmap = NULL;
+
+/* This is a helper function used by the hash table code (HT_). It returns 1 if
+ * two circuits have the same HS token. */
+static int
+hs_circuits_have_same_token(const or_circuit_t *first_circuit,
+ const or_circuit_t *second_circuit)
+{
+ const hs_token_t *first_token;
+ const hs_token_t *second_token;
+
+ tor_assert(first_circuit);
+ tor_assert(second_circuit);
+
+ first_token = first_circuit->hs_token;
+ second_token = second_circuit->hs_token;
+
+ /* Both circs must have a token */
+ if (BUG(!first_token) || BUG(!second_token)) {
+ return 0;
+ }
+
+ if (first_token->type != second_token->type) {
+ return 0;
+ }
+
+ if (first_token->token_len != second_token->token_len)
+ return 0;
+
+ return tor_memeq(first_token->token,
+ second_token->token,
+ first_token->token_len);
+}
+
+/* This is a helper function for the hash table code (HT_). It hashes a circuit
+ * HS token into an unsigned int for use as a key by the hash table routines.*/
+static inline unsigned int
+hs_circuit_hash_token(const or_circuit_t *circuit)
+{
+ tor_assert(circuit->hs_token);
+
+ return (unsigned) siphash24g(circuit->hs_token->token,
+ circuit->hs_token->token_len);
+}
+
+/* Register the circuitmap hash table */
+HT_PROTOTYPE(hs_circuitmap_ht, // The name of the hashtable struct
+ or_circuit_t, // The name of the element struct,
+ hs_circuitmap_node, // The name of HT_ENTRY member
+ hs_circuit_hash_token, hs_circuits_have_same_token)
+
+HT_GENERATE2(hs_circuitmap_ht, or_circuit_t, hs_circuitmap_node,
+ hs_circuit_hash_token, hs_circuits_have_same_token,
+ 0.6, tor_reallocarray, tor_free_)
+
+#ifdef TOR_UNIT_TESTS
+
+/* Return the global HS circuitmap. Used by unittests. */
+hs_circuitmap_ht *
+get_hs_circuitmap(void)
+{
+ return the_hs_circuitmap;
+}
+
+#endif
+
+/****************** HS circuitmap utility functions **************************/
+
+/** Return a new HS token of type <b>type</b> containing <b>token</b>. */
+static hs_token_t *
+hs_token_new(hs_token_type_t type, size_t token_len,
+ const uint8_t *token)
+{
+ tor_assert(token);
+
+ hs_token_t *hs_token = tor_malloc_zero(sizeof(hs_token_t));
+ hs_token->type = type;
+ hs_token->token_len = token_len;
+ hs_token->token = tor_memdup(token, token_len);
+
+ return hs_token;
+}
+
+/** Free memory allocated by this <b>hs_token</b>. */
+static void
+hs_token_free(hs_token_t *hs_token)
+{
+ if (!hs_token) {
+ return;
+ }
+
+ tor_free(hs_token->token);
+ tor_free(hs_token);
+}
+
+/** Return the circuit from the circuitmap with token <b>search_token</b>. */
+static or_circuit_t *
+get_circuit_with_token(hs_token_t *search_token)
+{
+ tor_assert(the_hs_circuitmap);
+
+ /* We use a dummy circuit object for the hash table search routine. */
+ or_circuit_t search_circ;
+ search_circ.hs_token = search_token;
+ return HT_FIND(hs_circuitmap_ht, the_hs_circuitmap, &search_circ);
+}
+
+/* Helper function that registers <b>circ</b> with <b>token</b> on the HS
+ circuitmap. This function steals reference of <b>token</b>. */
+static void
+hs_circuitmap_register_impl(or_circuit_t *circ, hs_token_t *token)
+{
+ tor_assert(circ);
+ tor_assert(token);
+ tor_assert(the_hs_circuitmap);
+
+ /* If this circuit already has a token, clear it. */
+ if (circ->hs_token) {
+ hs_circuitmap_remove_circuit(circ);
+ }
+
+ /* Kill old circuits with the same token. We want new intro/rend circuits to
+ take precedence over old ones, so that HSes and clients and reestablish
+ killed circuits without changing the HS token. */
+ {
+ or_circuit_t *found_circ;
+ found_circ = get_circuit_with_token(token);
+ if (found_circ) {
+ hs_circuitmap_remove_circuit(found_circ);
+ if (!found_circ->base_.marked_for_close) {
+ circuit_mark_for_close(TO_CIRCUIT(found_circ),
+ END_CIRC_REASON_FINISHED);
+ }
+ }
+ }
+
+ /* Register circuit and token to circuitmap. */
+ circ->hs_token = token;
+ HT_INSERT(hs_circuitmap_ht, the_hs_circuitmap, circ);
+}
+
+/** Helper function: Register <b>circ</b> of <b>type</b> on the HS
+ * circuitmap. Use the HS <b>token</b> as the key to the hash table. If
+ * <b>token</b> is not set, clear the circuit of any HS tokens. */
+static void
+hs_circuitmap_register_circuit(or_circuit_t *circ,
+ hs_token_type_t type, size_t token_len,
+ const uint8_t *token)
+{
+ hs_token_t *hs_token = NULL;
+
+ /* Create a new token and register it to the circuitmap */
+ tor_assert(token);
+ hs_token = hs_token_new(type, token_len, token);
+ tor_assert(hs_token);
+ hs_circuitmap_register_impl(circ, hs_token);
+}
+
+/* Query circuitmap for circuit with <b>token</b> of size <b>token_len</b>.
+ * Only returns a circuit with purpose equal to the <b>wanted_circ_purpose</b>
+ * parameter and if it is NOT marked for close. Return NULL if no such circuit
+ * is found. */
+static or_circuit_t *
+hs_circuitmap_get_circuit(hs_token_type_t type,
+ size_t token_len,
+ const uint8_t *token,
+ uint8_t wanted_circ_purpose)
+{
+ or_circuit_t *found_circ = NULL;
+
+ tor_assert(the_hs_circuitmap);
+
+ /* Check the circuitmap if we have a circuit with this token */
+ {
+ hs_token_t *search_hs_token = hs_token_new(type, token_len, token);
+ tor_assert(search_hs_token);
+ found_circ = get_circuit_with_token(search_hs_token);
+ hs_token_free(search_hs_token);
+ }
+
+ /* Check that the circuit is useful to us */
+ if (!found_circ ||
+ found_circ->base_.purpose != wanted_circ_purpose ||
+ found_circ->base_.marked_for_close) {
+ return NULL;
+ }
+
+ return found_circ;
+}
+
+/************** Public circuitmap API ****************************************/
+
+/* Public function: Return v3 introduction circuit with <b>auth_key</b>. Return
+ * NULL if no such circuit is found in the circuitmap. */
+or_circuit_t *
+hs_circuitmap_get_intro_circ_v3(const ed25519_public_key_t *auth_key)
+{
+ tor_assert(auth_key);
+
+ return hs_circuitmap_get_circuit(HS_TOKEN_INTRO_V3,
+ ED25519_PUBKEY_LEN, auth_key->pubkey,
+ CIRCUIT_PURPOSE_INTRO_POINT);
+}
+
+/* Public function: Return v2 introduction circuit with <b>digest</b>. Return
+ * NULL if no such circuit is found in the circuitmap. */
+or_circuit_t *
+hs_circuitmap_get_intro_circ_v2(const uint8_t *digest)
+{
+ tor_assert(digest);
+
+ return hs_circuitmap_get_circuit(HS_TOKEN_INTRO_V2,
+ REND_TOKEN_LEN, digest,
+ CIRCUIT_PURPOSE_INTRO_POINT);
+}
+
+/* Public function: Return rendezvous circuit with rendezvous
+ * <b>cookie</b>. Return NULL if no such circuit is found in the circuitmap. */
+or_circuit_t *
+hs_circuitmap_get_rend_circ(const uint8_t *cookie)
+{
+ tor_assert(cookie);
+
+ return hs_circuitmap_get_circuit(HS_TOKEN_REND,
+ REND_TOKEN_LEN, cookie,
+ CIRCUIT_PURPOSE_REND_POINT_WAITING);
+}
+
+/* Public function: Register rendezvous circuit with key <b>cookie</b> to the
+ * circuitmap. */
+void
+hs_circuitmap_register_rend_circ(or_circuit_t *circ, const uint8_t *cookie)
+{
+ hs_circuitmap_register_circuit(circ,
+ HS_TOKEN_REND,
+ REND_TOKEN_LEN, cookie);
+}
+
+/* Public function: Register v2 intro circuit with key <b>digest</b> to the
+ * circuitmap. */
+void
+hs_circuitmap_register_intro_circ_v2(or_circuit_t *circ, const uint8_t *digest)
+{
+ hs_circuitmap_register_circuit(circ,
+ HS_TOKEN_INTRO_V2,
+ REND_TOKEN_LEN, digest);
+}
+
+/* Public function: Register v3 intro circuit with key <b>auth_key</b> to the
+ * circuitmap. */
+void
+hs_circuitmap_register_intro_circ_v3(or_circuit_t *circ,
+ const ed25519_public_key_t *auth_key)
+{
+ hs_circuitmap_register_circuit(circ,
+ HS_TOKEN_INTRO_V3,
+ ED25519_PUBKEY_LEN, auth_key->pubkey);
+}
+
+/** Remove this circuit from the HS circuitmap. Clear its HS token, and remove
+ * it from the hashtable. */
+void
+hs_circuitmap_remove_circuit(or_circuit_t *circ)
+{
+ tor_assert(the_hs_circuitmap);
+
+ if (!circ || !circ->hs_token) {
+ return;
+ }
+
+ /* Remove circ from circuitmap */
+ or_circuit_t *tmp;
+ tmp = HT_REMOVE(hs_circuitmap_ht, the_hs_circuitmap, circ);
+ /* ... and ensure the removal was successful. */
+ if (tmp) {
+ tor_assert(tmp == circ);
+ } else {
+ log_warn(LD_BUG, "Could not find circuit (%u) in circuitmap.",
+ circ->p_circ_id);
+ }
+
+ /* Clear token from circ */
+ hs_token_free(circ->hs_token);
+ circ->hs_token = NULL;
+}
+
+/* Initialize the global HS circuitmap. */
+void
+hs_circuitmap_init(void)
+{
+ tor_assert(!the_hs_circuitmap);
+
+ the_hs_circuitmap = tor_malloc_zero(sizeof(struct hs_circuitmap_ht));
+ HT_INIT(hs_circuitmap_ht, the_hs_circuitmap);
+}
+
+/* Free all memory allocated by the global HS circuitmap. */
+void
+hs_circuitmap_free_all(void)
+{
+ if (the_hs_circuitmap) {
+ HT_CLEAR(hs_circuitmap_ht, the_hs_circuitmap);
+ tor_free(the_hs_circuitmap);
+ }
+}
+
diff --git a/src/or/hs_circuitmap.h b/src/or/hs_circuitmap.h
new file mode 100644
index 0000000000..b587039310
--- /dev/null
+++ b/src/or/hs_circuitmap.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_circuitmap.h
+ * \brief Header file for hs_circuitmap.c.
+ **/
+
+#ifndef TOR_HS_CIRCUITMAP_H
+#define TOR_HS_CIRCUITMAP_H
+
+typedef HT_HEAD(hs_circuitmap_ht, or_circuit_t) hs_circuitmap_ht;
+
+typedef struct hs_token_s hs_token_t;
+struct or_circuit_t;
+
+/** Public HS circuitmap API: */
+
+struct or_circuit_t *hs_circuitmap_get_rend_circ(const uint8_t *cookie);
+struct or_circuit_t *hs_circuitmap_get_intro_circ_v3(
+ const ed25519_public_key_t *auth_key);
+struct or_circuit_t *hs_circuitmap_get_intro_circ_v2(const uint8_t *digest);
+
+void hs_circuitmap_register_rend_circ(struct or_circuit_t *circ,
+ const uint8_t *cookie);
+void hs_circuitmap_register_intro_circ_v2(struct or_circuit_t *circ,
+ const uint8_t *digest);
+void hs_circuitmap_register_intro_circ_v3(struct or_circuit_t *circ,
+ const ed25519_public_key_t *auth_key);
+
+void hs_circuitmap_remove_circuit(struct or_circuit_t *circ);
+
+void hs_circuitmap_init(void);
+void hs_circuitmap_free_all(void);
+
+#ifdef HS_CIRCUITMAP_PRIVATE
+
+/** Represents the type of HS token. */
+typedef enum {
+ /** A rendezvous cookie (128bit)*/
+ HS_TOKEN_REND,
+ /** A v2 introduction point pubkey (160bit) */
+ HS_TOKEN_INTRO_V2,
+ /** A v3 introduction point pubkey (256bit) */
+ HS_TOKEN_INTRO_V3,
+} hs_token_type_t;
+
+/** Represents a token used in the HS protocol. Each such token maps to a
+ * specific introduction or rendezvous circuit. */
+struct hs_token_s {
+ /* Type of HS token. */
+ hs_token_type_t type;
+
+ /* The size of the token (depends on the type). */
+ size_t token_len;
+
+ /* The token itself. Memory allocated at runtime. */
+ uint8_t *token;
+};
+
+#endif /* HS_CIRCUITMAP_PRIVATE */
+
+#ifdef TOR_UNIT_TESTS
+
+hs_circuitmap_ht *get_hs_circuitmap(void);
+
+#endif /* TOR_UNIT_TESTS */
+
+#endif /* TOR_HS_CIRCUITMAP_H */
+
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 2502f35ad4..8158d278df 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -17,6 +17,12 @@
/* Version 3 of the protocol (prop224). */
#define HS_VERSION_THREE 3
+/* Denotes ed25519 authentication key on ESTABLISH_INTRO cell. */
+#define AUTH_KEY_ED25519 0x02
+
+/* String prefix for the signature of ESTABLISH_INTRO */
+#define ESTABLISH_INTRO_SIG_PREFIX "Tor establish-intro cell v1"
+
void rend_data_free(rend_data_t *data);
rend_data_t *rend_data_dup(const rend_data_t *data);
rend_data_t *rend_data_client_create(const char *onion_address,
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index d381732eff..37aa1d745e 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -15,6 +15,7 @@
#include "ed25519_cert.h" /* Trunnel interface. */
#include "parsecommon.h"
#include "rendcache.h"
+#include "torcert.h" /* tor_cert_encode_ed22519() */
/* Constant string value used for the descriptor format. */
#define str_hs_desc "hs-descriptor"
@@ -135,45 +136,6 @@ desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc)
/* === ENCODING === */
-/* Encode the ed25519 certificate <b>cert</b> and put the newly allocated
- * string in <b>cert_str_out</b>. Return 0 on success else a negative value. */
-STATIC int
-encode_cert(const tor_cert_t *cert, char **cert_str_out)
-{
- int ret = -1;
- char *ed_cert_b64 = NULL;
- size_t ed_cert_b64_len;
-
- tor_assert(cert);
- tor_assert(cert_str_out);
-
- /* Get the encoded size and add the NUL byte. */
- ed_cert_b64_len = base64_encode_size(cert->encoded_len,
- BASE64_ENCODE_MULTILINE) + 1;
- ed_cert_b64 = tor_malloc_zero(ed_cert_b64_len);
-
- /* Base64 encode the encoded certificate. */
- if (base64_encode(ed_cert_b64, ed_cert_b64_len,
- (const char *) cert->encoded, cert->encoded_len,
- BASE64_ENCODE_MULTILINE) < 0) {
- log_err(LD_BUG, "Couldn't base64-encode descriptor signing key cert!");
- goto err;
- }
-
- /* Put everything together in a NUL terminated string. */
- tor_asprintf(cert_str_out,
- "-----BEGIN ED25519 CERT-----\n"
- "%s"
- "-----END ED25519 CERT-----",
- ed_cert_b64);
- /* Success! */
- ret = 0;
-
- err:
- tor_free(ed_cert_b64);
- return ret;
-}
-
/* Encode the given link specifier objects into a newly allocated string.
* This can't fail so caller can always assume a valid string being
* returned. */
@@ -327,7 +289,7 @@ encode_enc_key(const ed25519_keypair_t *sig_key,
if (!cross_cert) {
goto err;
}
- ret = encode_cert(cross_cert, &encoded_cert);
+ ret = tor_cert_encode_ed22519(cross_cert, &encoded_cert);
tor_cert_free(cross_cert);
if (ret) {
goto err;
@@ -375,7 +337,7 @@ encode_intro_point(const ed25519_keypair_t *sig_key,
/* Authentication key encoding. */
{
char *encoded_cert;
- if (encode_cert(ip->auth_key_cert, &encoded_cert) < 0) {
+ if (tor_cert_encode_ed22519(ip->auth_key_cert, &encoded_cert) < 0) {
goto err;
}
smartlist_add_asprintf(lines, "%s\n%s", str_ip_auth_key, encoded_cert);
@@ -769,7 +731,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out)
"(%d)", (int) desc->plaintext_data.signing_key_cert->cert_type);
goto err;
}
- if (encode_cert(desc->plaintext_data.signing_key_cert,
+ if (tor_cert_encode_ed22519(desc->plaintext_data.signing_key_cert,
&encoded_cert) < 0) {
/* The function will print error logs. */
goto err;
diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h
index 895bed2485..083d353860 100644
--- a/src/or/hs_descriptor.h
+++ b/src/or/hs_descriptor.h
@@ -216,7 +216,6 @@ size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data);
#ifdef HS_DESCRIPTOR_PRIVATE
/* Encoding. */
-STATIC int encode_cert(const tor_cert_t *cert, char **cert_str_out);
STATIC char *encode_link_specifiers(const smartlist_t *specs);
STATIC size_t build_plaintext_padding(const char *plaintext,
size_t plaintext_len,
diff --git a/src/or/hs_intropoint.c b/src/or/hs_intropoint.c
new file mode 100644
index 0000000000..bfc7ec3876
--- /dev/null
+++ b/src/or/hs_intropoint.c
@@ -0,0 +1,288 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_intropoint.c
+ * \brief Implement next generation introductions point functionality
+ **/
+
+#define HS_INTROPOINT_PRIVATE
+
+#include "or.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "config.h"
+#include "relay.h"
+#include "rendmid.h"
+#include "rephist.h"
+
+#include "hs/cell_establish_intro.h"
+#include "hs/cell_common.h"
+#include "hs_circuitmap.h"
+#include "hs_intropoint.h"
+#include "hs_common.h"
+
+/** Extract the authentication key from an ESTABLISH_INTRO <b>cell</b> and
+ * place it in <b>auth_key_out</b>. */
+STATIC void
+get_auth_key_from_establish_intro_cell(ed25519_public_key_t *auth_key_out,
+ const hs_cell_establish_intro_t *cell)
+{
+ tor_assert(auth_key_out);
+
+ const uint8_t *key_array =
+ hs_cell_establish_intro_getconstarray_auth_key(cell);
+ tor_assert(key_array);
+ tor_assert(hs_cell_establish_intro_getlen_auth_key(cell) ==
+ sizeof(auth_key_out->pubkey));
+
+ memcpy(auth_key_out->pubkey, key_array, cell->auth_key_len);
+}
+
+/** We received an ESTABLISH_INTRO <b>cell</b>. Verify its signature and MAC,
+ * given <b>circuit_key_material</b>. Return 0 on success else -1 on error. */
+STATIC int
+verify_establish_intro_cell(const hs_cell_establish_intro_t *cell,
+ const uint8_t *circuit_key_material,
+ size_t circuit_key_material_len)
+{
+ /* We only reach this function if the first byte of the cell is 0x02 which
+ * means that auth_key_type is AUTH_KEY_ED25519, hence this check should
+ * always pass. See hs_intro_received_establish_intro(). */
+ if (BUG(cell->auth_key_type != AUTH_KEY_ED25519)) {
+ return -1;
+ }
+
+ /* Make sure the auth key length is of the right size for this type. For
+ * EXTRA safety, we check both the size of the array and the length which
+ * must be the same. Safety first!*/
+ if (hs_cell_establish_intro_getlen_auth_key(cell) != ED25519_PUBKEY_LEN ||
+ hs_cell_establish_intro_get_auth_key_len(cell) != ED25519_PUBKEY_LEN) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "ESTABLISH_INTRO auth key length is invalid");
+ return -1;
+ }
+
+ const uint8_t *msg = cell->start_cell;
+
+ /* Verify the sig */
+ {
+ ed25519_signature_t sig_struct;
+ const uint8_t *sig_array = hs_cell_establish_intro_getconstarray_sig(cell);
+
+ if (hs_cell_establish_intro_getlen_sig(cell) != sizeof(sig_struct.sig)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "ESTABLISH_INTRO sig len is invalid");
+ return -1;
+ }
+ /* We are now sure that sig_len is of the right size. */
+ memcpy(sig_struct.sig, sig_array, cell->sig_len);
+
+ ed25519_public_key_t auth_key;
+ get_auth_key_from_establish_intro_cell(&auth_key, cell);
+
+ const size_t sig_msg_len = cell->end_sig_fields - msg;
+ int sig_mismatch = ed25519_checksig_prefixed(&sig_struct,
+ (uint8_t*) msg, sig_msg_len,
+ ESTABLISH_INTRO_SIG_PREFIX,
+ &auth_key);
+ if (sig_mismatch) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "ESTABLISH_INTRO signature not as expected");
+ return -1;
+ }
+ }
+
+ /* Verify the MAC */
+ {
+ const size_t auth_msg_len = cell->end_mac_fields - msg;
+ uint8_t mac[DIGEST256_LEN];
+ crypto_mac_sha3_256(mac, sizeof(mac),
+ circuit_key_material, circuit_key_material_len,
+ msg, auth_msg_len);
+ if (tor_memneq(mac, cell->handshake_mac, sizeof(mac))) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "ESTABLISH_INTRO handshake_auth not as expected");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* Send an INTRO_ESTABLISHED cell to <b>circ</b>. */
+MOCK_IMPL(int,
+hs_intro_send_intro_established_cell,(or_circuit_t *circ))
+{
+ int ret;
+ uint8_t *encoded_cell = NULL;
+ ssize_t encoded_len, result_len;
+ hs_cell_intro_established_t *cell;
+ cell_extension_t *ext;
+
+ tor_assert(circ);
+
+ /* Build the cell payload. */
+ cell = hs_cell_intro_established_new();
+ ext = cell_extension_new();
+ cell_extension_set_num(ext, 0);
+ hs_cell_intro_established_set_extensions(cell, ext);
+ /* Encode the cell to binary format. */
+ encoded_len = hs_cell_intro_established_encoded_len(cell);
+ tor_assert(encoded_len > 0);
+ encoded_cell = tor_malloc_zero(encoded_len);
+ result_len = hs_cell_intro_established_encode(encoded_cell, encoded_len,
+ cell);
+ tor_assert(encoded_len == result_len);
+
+ ret = relay_send_command_from_edge(0, TO_CIRCUIT(circ),
+ RELAY_COMMAND_INTRO_ESTABLISHED,
+ (char *) encoded_cell, encoded_len,
+ NULL);
+ /* On failure, the above function will close the circuit. */
+ hs_cell_intro_established_free(cell);
+ tor_free(encoded_cell);
+ return ret;
+}
+
+/** We received an ESTABLISH_INTRO <b>parsed_cell</b> on <b>circ</b>. It's
+ * well-formed and passed our verifications. Perform appropriate actions to
+ * establish an intro point. */
+static int
+handle_verified_establish_intro_cell(or_circuit_t *circ,
+ const hs_cell_establish_intro_t *parsed_cell)
+{
+ /* Get the auth key of this intro point */
+ ed25519_public_key_t auth_key;
+ get_auth_key_from_establish_intro_cell(&auth_key, parsed_cell);
+
+ /* Then notify the hidden service that the intro point is established by
+ sending an INTRO_ESTABLISHED cell */
+ if (hs_intro_send_intro_established_cell(circ)) {
+ log_warn(LD_BUG, "Couldn't send INTRO_ESTABLISHED cell.");
+ return -1;
+ }
+
+ /* Associate intro point auth key with this circuit. */
+ hs_circuitmap_register_intro_circ_v3(circ, &auth_key);
+ /* Repurpose this circuit into an intro circuit. */
+ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
+
+ return 0;
+}
+
+/** We just received an ESTABLISH_INTRO cell in <b>circ</b> with payload in
+ * <b>request</b>. Handle it by making <b>circ</b> an intro circuit. Return 0
+ * if everything went well, or -1 if there were errors. */
+static int
+handle_establish_intro(or_circuit_t *circ, const uint8_t *request,
+ size_t request_len)
+{
+ int cell_ok, retval = -1;
+ hs_cell_establish_intro_t *parsed_cell = NULL;
+
+ tor_assert(circ);
+ tor_assert(request);
+
+ log_info(LD_REND, "Received an ESTABLISH_INTRO request on circuit %" PRIu32,
+ circ->p_circ_id);
+
+ /* Check that the circuit is in shape to become an intro point */
+ if (!hs_intro_circuit_is_suitable(circ)) {
+ goto err;
+ }
+
+ /* Parse the cell */
+ ssize_t parsing_result = hs_cell_establish_intro_parse(&parsed_cell,
+ request, request_len);
+ if (parsing_result < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Rejecting %s ESTABLISH_INTRO cell.",
+ parsing_result == -1 ? "invalid" : "truncated");
+ goto err;
+ }
+
+ cell_ok = verify_establish_intro_cell(parsed_cell,
+ (uint8_t *) circ->rend_circ_nonce,
+ sizeof(circ->rend_circ_nonce));
+ if (cell_ok < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Failed to verify ESTABLISH_INTRO cell.");
+ goto err;
+ }
+
+ /* This cell is legit. Take the appropriate actions. */
+ cell_ok = handle_verified_establish_intro_cell(circ, parsed_cell);
+ if (cell_ok < 0) {
+ goto err;
+ }
+
+ log_warn(LD_GENERAL, "Established prop224 intro point on circuit %" PRIu32,
+ circ->p_circ_id);
+
+ /* We are done! */
+ retval = 0;
+ goto done;
+
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+
+ done:
+ hs_cell_establish_intro_free(parsed_cell);
+ return retval;
+}
+
+/* Return True if circuit is suitable for becoming an intro circuit. */
+int
+hs_intro_circuit_is_suitable(const or_circuit_t *circ)
+{
+ /* Basic circuit state sanity checks. */
+ if (circ->base_.purpose != CIRCUIT_PURPOSE_OR) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Rejecting ESTABLISH_INTRO on non-OR circuit.");
+ return 0;
+ }
+
+ if (circ->base_.n_chan) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Rejecting ESTABLISH_INTRO on non-edge circuit.");
+ return 0;
+ }
+
+ return 1;
+}
+
+/* We just received an ESTABLISH_INTRO cell in <b>circ</b>. Figure out of it's
+ * a legacy or a next gen cell, and pass it to the appropriate handler. */
+int
+hs_intro_received_establish_intro(or_circuit_t *circ, const uint8_t *request,
+ size_t request_len)
+{
+ tor_assert(circ);
+ tor_assert(request);
+
+ if (request_len == 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Empty ESTABLISH_INTRO cell.");
+ goto err;
+ }
+
+ /* Using the first byte of the cell, figure out the version of
+ * ESTABLISH_INTRO and pass it to the appropriate cell handler */
+ const uint8_t first_byte = request[0];
+ switch (first_byte) {
+ case HS_INTRO_AUTH_KEY_TYPE_LEGACY0:
+ case HS_INTRO_AUTH_KEY_TYPE_LEGACY1:
+ return rend_mid_establish_intro_legacy(circ, request, request_len);
+ case HS_INTRO_AUTH_KEY_TYPE_ED25519:
+ return handle_establish_intro(circ, request, request_len);
+ default:
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unrecognized AUTH_KEY_TYPE %u.", first_byte);
+ goto err;
+ }
+
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ return -1;
+}
+
diff --git a/src/or/hs_intropoint.h b/src/or/hs_intropoint.h
new file mode 100644
index 0000000000..46d8d6f7de
--- /dev/null
+++ b/src/or/hs_intropoint.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_intropoint.h
+ * \brief Header file for hs_intropoint.c.
+ **/
+
+#ifndef TOR_HS_INTRO_H
+#define TOR_HS_INTRO_H
+
+/* Authentication key type in an ESTABLISH_INTRO cell. */
+enum hs_intro_auth_key_type {
+ HS_INTRO_AUTH_KEY_TYPE_LEGACY0 = 0x00,
+ HS_INTRO_AUTH_KEY_TYPE_LEGACY1 = 0x01,
+ HS_INTRO_AUTH_KEY_TYPE_ED25519 = 0x02,
+};
+
+int hs_intro_received_establish_intro(or_circuit_t *circ,
+ const uint8_t *request,
+ size_t request_len);
+
+MOCK_DECL(int, hs_intro_send_intro_established_cell,(or_circuit_t *circ));
+
+/* also used by rendservice.c */
+int hs_intro_circuit_is_suitable(const or_circuit_t *circ);
+
+#ifdef HS_INTROPOINT_PRIVATE
+
+STATIC int
+verify_establish_intro_cell(const hs_cell_establish_intro_t *out,
+ const uint8_t *circuit_key_material,
+ size_t circuit_key_material_len);
+
+STATIC void
+get_auth_key_from_establish_intro_cell(ed25519_public_key_t *auth_key_out,
+ const hs_cell_establish_intro_t *cell);
+
+#endif /* HS_INTROPOINT_PRIVATE */
+
+#endif /* TOR_HS_INTRO_H */
+
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
new file mode 100644
index 0000000000..3b5a3e7853
--- /dev/null
+++ b/src/or/hs_service.c
@@ -0,0 +1,177 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_service.c
+ * \brief Implement next generation hidden service functionality
+ **/
+
+#define HS_SERVICE_PRIVATE
+
+#include "or.h"
+#include "relay.h"
+#include "rendservice.h"
+#include "circuitlist.h"
+#include "circpathbias.h"
+
+#include "hs_service.h"
+#include "hs_common.h"
+
+#include "hs/cell_establish_intro.h"
+#include "hs/cell_common.h"
+
+/* XXX We don't currently use these functions, apart from generating unittest
+ data. When we start implementing the service-side support for prop224 we
+ should revisit these functions and use them. For now we mark them as
+ unittest-only code: */
+#ifdef TOR_UNIT_TESTS
+
+/** Given an ESTABLISH_INTRO <b>cell</b>, encode it and place its payload in
+ * <b>buf_out</b> which has size <b>buf_out_len</b>. Return the number of
+ * bytes written, or a negative integer if there was an error. */
+STATIC ssize_t
+get_establish_intro_payload(uint8_t *buf_out, size_t buf_out_len,
+ const hs_cell_establish_intro_t *cell)
+{
+ ssize_t bytes_used = 0;
+
+ if (buf_out_len < RELAY_PAYLOAD_SIZE) {
+ return -1;
+ }
+
+ bytes_used = hs_cell_establish_intro_encode(buf_out, buf_out_len,
+ cell);
+ return bytes_used;
+}
+
+/* Set the cell extensions of <b>cell</b>. */
+static void
+set_cell_extensions(hs_cell_establish_intro_t *cell)
+{
+ cell_extension_t *cell_extensions = cell_extension_new();
+
+ /* For now, we don't use extensions at all. */
+ cell_extensions->num = 0; /* It's already zeroed, but be explicit. */
+ hs_cell_establish_intro_set_extensions(cell, cell_extensions);
+}
+
+/** Given the circuit handshake info in <b>circuit_key_material</b>, create and
+ * return an ESTABLISH_INTRO cell. Return NULL if something went wrong. The
+ * returned cell is allocated on the heap and it's the responsibility of the
+ * caller to free it. */
+STATIC hs_cell_establish_intro_t *
+generate_establish_intro_cell(const uint8_t *circuit_key_material,
+ size_t circuit_key_material_len)
+{
+ hs_cell_establish_intro_t *cell = NULL;
+ ssize_t encoded_len;
+
+ log_warn(LD_GENERAL,
+ "Generating ESTABLISH_INTRO cell (key_material_len: %u)",
+ (unsigned) circuit_key_material_len);
+
+ /* Generate short-term keypair for use in ESTABLISH_INTRO */
+ ed25519_keypair_t key_struct;
+ if (ed25519_keypair_generate(&key_struct, 0) < 0) {
+ goto err;
+ }
+
+ cell = hs_cell_establish_intro_new();
+
+ /* Set AUTH_KEY_TYPE: 2 means ed25519 */
+ hs_cell_establish_intro_set_auth_key_type(cell, AUTH_KEY_ED25519);
+
+ /* Set AUTH_KEY_LEN field */
+ /* Must also set byte-length of AUTH_KEY to match */
+ int auth_key_len = ED25519_PUBKEY_LEN;
+ hs_cell_establish_intro_set_auth_key_len(cell, auth_key_len);
+ hs_cell_establish_intro_setlen_auth_key(cell, auth_key_len);
+
+ /* Set AUTH_KEY field */
+ uint8_t *auth_key_ptr = hs_cell_establish_intro_getarray_auth_key(cell);
+ memcpy(auth_key_ptr, key_struct.pubkey.pubkey, auth_key_len);
+
+ /* No cell extensions needed */
+ set_cell_extensions(cell);
+
+ /* Set signature size.
+ We need to do this up here, because _encode() needs it and we need to call
+ _encode() to calculate the MAC and signature.
+ */
+ int sig_len = ED25519_SIG_LEN;
+ hs_cell_establish_intro_set_sig_len(cell, sig_len);
+ hs_cell_establish_intro_setlen_sig(cell, sig_len);
+
+ /* XXX How to make this process easier and nicer? */
+
+ /* Calculate the cell MAC (aka HANDSHAKE_AUTH). */
+ {
+ /* To calculate HANDSHAKE_AUTH, we dump the cell in bytes, and then derive
+ the MAC from it. */
+ uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0};
+ uint8_t mac[TRUNNEL_SHA3_256_LEN];
+
+ encoded_len = hs_cell_establish_intro_encode(cell_bytes_tmp,
+ sizeof(cell_bytes_tmp),
+ cell);
+ if (encoded_len < 0) {
+ log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell.");
+ goto err;
+ }
+
+ /* sanity check */
+ tor_assert(encoded_len > ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN);
+
+ /* Calculate MAC of all fields before HANDSHAKE_AUTH */
+ crypto_mac_sha3_256(mac, sizeof(mac),
+ circuit_key_material, circuit_key_material_len,
+ cell_bytes_tmp,
+ encoded_len -
+ (ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN));
+ /* Write the MAC to the cell */
+ uint8_t *handshake_ptr =
+ hs_cell_establish_intro_getarray_handshake_mac(cell);
+ memcpy(handshake_ptr, mac, sizeof(mac));
+ }
+
+ /* Calculate the cell signature */
+ {
+ /* To calculate the sig we follow the same procedure as above. We first
+ dump the cell up to the sig, and then calculate the sig */
+ uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0};
+ ed25519_signature_t sig;
+
+ encoded_len = hs_cell_establish_intro_encode(cell_bytes_tmp,
+ sizeof(cell_bytes_tmp),
+ cell);
+ if (encoded_len < 0) {
+ log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell (2).");
+ goto err;
+ }
+
+ tor_assert(encoded_len > ED25519_SIG_LEN);
+
+ if (ed25519_sign_prefixed(&sig,
+ (uint8_t*) cell_bytes_tmp,
+ encoded_len - ED25519_SIG_LEN,
+ ESTABLISH_INTRO_SIG_PREFIX,
+ &key_struct)) {
+ log_warn(LD_BUG, "Unable to gen signature for ESTABLISH_INTRO cell.");
+ goto err;
+ }
+
+ /* And write the signature to the cell */
+ uint8_t *sig_ptr = hs_cell_establish_intro_getarray_sig(cell);
+ memcpy(sig_ptr, sig.sig, sig_len);
+ }
+
+ /* We are done! Return the cell! */
+ return cell;
+
+ err:
+ hs_cell_establish_intro_free(cell);
+ return NULL;
+}
+
+#endif /* TOR_UNIT_TESTS */
+
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
new file mode 100644
index 0000000000..994521fc50
--- /dev/null
+++ b/src/or/hs_service.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_service.h
+ * \brief Header file for hs_service.c.
+ **/
+
+#ifndef TOR_HS_SERVICE_H
+#define TOR_HS_SERVICE_H
+
+#include "or.h"
+#include "hs/cell_establish_intro.h"
+
+#ifdef HS_SERVICE_PRIVATE
+
+#ifdef TOR_UNIT_TESTS
+
+STATIC hs_cell_establish_intro_t *
+generate_establish_intro_cell(const uint8_t *circuit_key_material,
+ size_t circuit_key_material_len);
+
+STATIC ssize_t
+get_establish_intro_payload(uint8_t *buf, size_t buf_len,
+ const hs_cell_establish_intro_t *cell);
+
+#endif /* TOR_UNIT_TESTS */
+
+#endif /* HS_SERVICE_PRIVATE */
+
+#endif /* TOR_HS_SERVICE_H */
+
diff --git a/src/or/include.am b/src/or/include.am
index 278aa9baad..4e54deca55 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -46,6 +46,9 @@ LIBTOR_A_SOURCES = \
src/or/dnsserv.c \
src/or/fp_pair.c \
src/or/geoip.c \
+ src/or/hs_intropoint.c \
+ src/or/hs_circuitmap.c \
+ src/or/hs_service.c \
src/or/entrynodes.c \
src/or/ext_orport.c \
src/or/hibernate.c \
@@ -166,6 +169,9 @@ ORHEADERS = \
src/or/hs_cache.h \
src/or/hs_common.h \
src/or/hs_descriptor.h \
+ src/or/hs_intropoint.h \
+ src/or/hs_circuitmap.h \
+ src/or/hs_service.h \
src/or/keypin.h \
src/or/main.h \
src/or/microdesc.h \
@@ -208,7 +214,7 @@ noinst_HEADERS+= $(ORHEADERS) micro-revision.i
micro-revision.i: FORCE
$(AM_V_at)rm -f micro-revision.tmp; \
- if test -d "$(top_srcdir)/.git" && \
+ if test -r "$(top_srcdir)/.git" && \
test -x "`which git 2>&1;true`"; then \
HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
echo \"$$HASH\" > micro-revision.tmp; \
diff --git a/src/or/main.c b/src/or/main.c
index 25f8b1270c..0d4da65764 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -75,6 +75,7 @@
#include "geoip.h"
#include "hibernate.h"
#include "hs_cache.h"
+#include "hs_circuitmap.h"
#include "keypin.h"
#include "main.h"
#include "microdesc.h"
@@ -363,7 +364,7 @@ connection_unlink(connection_t *conn)
}
if (conn->type == CONN_TYPE_OR) {
if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest))
- connection_or_remove_from_identity_map(TO_OR_CONN(conn));
+ connection_or_clear_identity(TO_OR_CONN(conn));
/* connection_unlink() can only get called if the connection
* was already on the closeable list, and it got there by
* connection_mark_for_close(), which was called from
@@ -1437,7 +1438,7 @@ run_scheduled_events(time_t now)
}
/* 5. We do housekeeping for each connection... */
- connection_or_set_bad_connections(NULL, 0);
+ channel_update_bad_for_new_circs(NULL, 0);
int i;
for (i=0;i<smartlist_len(connection_array);i++) {
run_connection_housekeeping(i, now);
@@ -2411,6 +2412,9 @@ do_main_loop(void)
}
}
+ /* Initialize relay-side HS circuitmap */
+ hs_circuitmap_init();
+
/* set up once-a-second callback. */
if (! second_timer) {
struct timeval one_second;
@@ -3119,6 +3123,7 @@ tor_free_all(int postfork)
connection_edge_free_all();
scheduler_free_all();
nodelist_free_all();
+ hs_circuitmap_free_all();
microdesc_free_all();
routerparse_free_all();
ext_orport_free_all();
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index ce23d67979..49ff12bd6c 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -815,8 +815,11 @@ networkstatus_nickname_is_unnamed(const char *nickname)
#define NONAUTHORITY_NS_CACHE_INTERVAL (60*60)
/** Return true iff, given the options listed in <b>options</b>, <b>flavor</b>
- * is the flavor of a consensus networkstatus that we would like to fetch. */
-static int
+ * is the flavor of a consensus networkstatus that we would like to fetch.
+ *
+ * For certificate fetches, use we_want_to_fetch_unknown_auth_certs, and
+ * for serving fetched documents, use directory_caches_dir_info. */
+int
we_want_to_fetch_flavor(const or_options_t *options, int flavor)
{
if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) {
@@ -838,6 +841,29 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor)
return flavor == usable_consensus_flavor();
}
+/** Return true iff, given the options listed in <b>options</b>, we would like
+ * to fetch and store unknown authority certificates.
+ *
+ * For consensus and descriptor fetches, use we_want_to_fetch_flavor, and
+ * for serving fetched certificates, use directory_caches_unknown_auth_certs.
+ */
+int
+we_want_to_fetch_unknown_auth_certs(const or_options_t *options)
+{
+ if (authdir_mode_v3(options) ||
+ directory_caches_unknown_auth_certs((options))) {
+ /* We want to serve all certs to others, regardless if we would use
+ * them ourselves. */
+ return 1;
+ }
+ if (options->FetchUselessDescriptors) {
+ /* Unknown certificates are definitely useless. */
+ return 1;
+ }
+ /* Otherwise, don't fetch unknown certificates. */
+ return 0;
+}
+
/** How long will we hang onto a possibly live consensus for which we're
* fetching certs before we check whether there is a better one? */
#define DELAY_WHILE_FETCHING_CERTS (20*60)
@@ -1352,6 +1378,24 @@ networkstatus_get_live_consensus,(time_t now))
return NULL;
}
+/** Determine if <b>consensus</b> is valid or expired recently enough that
+ * we can still use it.
+ *
+ * Return 1 if the consensus is reasonably live, or 0 if it is too old.
+ */
+int
+networkstatus_consensus_reasonably_live(networkstatus_t *consensus, time_t now)
+{
+#define REASONABLY_LIVE_TIME (24*60*60)
+ if (BUG(!consensus))
+ return 0;
+
+ if (now <= consensus->valid_until + REASONABLY_LIVE_TIME)
+ return 1;
+
+ return 0;
+}
+
/* XXXX remove this in favor of get_live_consensus. But actually,
* leave something like it for bridge users, who need to not totally
* lose if they spend a while fetching a new consensus. */
@@ -1360,12 +1404,11 @@ networkstatus_get_live_consensus,(time_t now))
networkstatus_t *
networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
{
-#define REASONABLY_LIVE_TIME (24*60*60)
networkstatus_t *consensus =
networkstatus_get_latest_consensus_by_flavor(flavor);
if (consensus &&
consensus->valid_after <= now &&
- now <= consensus->valid_until+REASONABLY_LIVE_TIME)
+ networkstatus_consensus_reasonably_live(consensus, now))
return consensus;
else
return NULL;
@@ -1729,9 +1772,9 @@ networkstatus_set_current_consensus(const char *consensus,
}
if (flav != usable_consensus_flavor() &&
- !directory_caches_dir_info(options)) {
- /* This consensus is totally boring to us: we won't use it, and we won't
- * serve it. Drop it. */
+ !we_want_to_fetch_flavor(options, flav)) {
+ /* This consensus is totally boring to us: we won't use it, we didn't want
+ * it, and we won't serve it. Drop it. */
goto done;
}
@@ -1933,7 +1976,7 @@ networkstatus_set_current_consensus(const char *consensus,
download_status_failed(&consensus_dl_status[flav], 0);
}
- if (directory_caches_dir_info(options)) {
+ if (we_want_to_fetch_flavor(options, flav)) {
dirserv_set_cached_consensus_networkstatus(consensus,
flavor,
&c->digests,
@@ -2401,9 +2444,9 @@ int
client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options)
{
- if (!rs->is_flagged_running && !options->FetchUselessDescriptors) {
+ if (!rs->is_flagged_running) {
/* If we had this router descriptor, we wouldn't even bother using it.
- * But, if we want to have a complete list, fetch it anyway. */
+ * (Fetching and storing depends on by we_want_to_fetch_flavor().) */
return 0;
}
if (rs->published_on + options->TestingEstimatedDescriptorPropagationTime
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index 4b3854db0c..edf2dc7b7a 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -66,6 +66,8 @@ const routerstatus_t *router_get_consensus_status_by_nickname(
int warn_if_unnamed);
const char *networkstatus_get_router_digest_by_nickname(const char *nickname);
int networkstatus_nickname_is_unnamed(const char *nickname);
+int we_want_to_fetch_flavor(const or_options_t *options, int flavor);
+int we_want_to_fetch_unknown_auth_certs(const or_options_t *options);
void networkstatus_consensus_download_failed(int status_code,
const char *flavname);
void update_consensus_networkstatus_fetch_time(time_t now);
@@ -79,6 +81,8 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus,(void));
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
(consensus_flavor_t f));
MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now));
+int networkstatus_consensus_reasonably_live(networkstatus_t *consensus,
+ time_t now);
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor);
MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 2802d5b9e0..2bcedbfb0c 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -49,10 +49,12 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "protover.h"
#include "rendservice.h"
#include "router.h"
#include "routerlist.h"
#include "routerset.h"
+#include "torcert.h"
#include <string.h>
@@ -646,6 +648,73 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
}
}
+/** Return the Ed25519 identity key for the provided node, or NULL if it
+ * doesn't have one. */
+const ed25519_public_key_t *
+node_get_ed25519_id(const node_t *node)
+{
+ if (node->ri) {
+ if (node->ri->cache_info.signing_key_cert) {
+ const ed25519_public_key_t *pk =
+ &node->ri->cache_info.signing_key_cert->signing_key;
+ if (BUG(ed25519_public_key_is_zero(pk)))
+ goto try_the_md;
+ return pk;
+ }
+ }
+ try_the_md:
+ if (node->md) {
+ if (node->md->ed25519_identity_pkey) {
+ return node->md->ed25519_identity_pkey;
+ }
+ }
+ return NULL;
+}
+
+/** Return true iff this node's Ed25519 identity matches <b>id</b>.
+ * (An absent Ed25519 identity matches NULL or zero.) */
+int
+node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id)
+{
+ const ed25519_public_key_t *node_id = node_get_ed25519_id(node);
+ if (node_id == NULL || ed25519_public_key_is_zero(node_id)) {
+ return id == NULL || ed25519_public_key_is_zero(id);
+ } else {
+ return id && ed25519_pubkey_eq(node_id, id);
+ }
+}
+
+/** Return true iff <b>node</b> supports authenticating itself
+ * by ed25519 ID during the link handshake in a way that we can understand
+ * when we probe it. */
+int
+node_supports_ed25519_link_authentication(const node_t *node)
+{
+ /* XXXX Oh hm. What if some day in the future there are link handshake
+ * versions that aren't 3 but which are ed25519 */
+ if (! node_get_ed25519_id(node))
+ return 0;
+ if (node->ri) {
+ const char *protos = node->ri->protocol_list;
+ if (protos == NULL)
+ return 0;
+ return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3);
+ }
+ if (node->rs) {
+ return node->rs->supports_ed25519_link_handshake;
+ }
+ tor_assert_nonfatal_unreached_once();
+ return 0;
+}
+
+/** Return the RSA ID key's SHA1 digest for the provided node. */
+const uint8_t *
+node_get_rsa_id_digest(const node_t *node)
+{
+ tor_assert(node);
+ return (const uint8_t*)node->identity;
+}
+
/** Return the nickname of <b>node</b>, or NULL if we can't find one. */
const char *
node_get_nickname(const node_t *node)
@@ -1569,8 +1638,8 @@ router_have_minimum_dir_info(void)
* this can cause router_have_consensus_path() to be set to
* CONSENSUS_PATH_EXIT, even if there are no nodes with accept exit policies.
*/
-consensus_path_type_t
-router_have_consensus_path(void)
+MOCK_IMPL(consensus_path_type_t,
+router_have_consensus_path, (void))
{
return have_consensus_path;
}
@@ -1659,9 +1728,9 @@ count_usable_descriptors(int *num_present, int *num_usable,
* If **<b>status_out</b> is present, allocate a new string and print the
* available percentages of guard, middle, and exit nodes to it, noting
* whether there are exits in the consensus.
- * If there are no guards in the consensus,
- * we treat the exit fraction as 100%.
- */
+ * If there are no exits in the consensus, we treat the exit fraction as 100%,
+ * but set router_have_consensus_path() so that we can only build internal
+ * paths. */
static double
compute_frac_paths_available(const networkstatus_t *consensus,
const or_options_t *options, time_t now,
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 71a91e107f..8456d21c6c 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -55,6 +55,11 @@ void node_get_address_string(const node_t *node, char *cp, size_t len);
long node_get_declared_uptime(const node_t *node);
time_t node_get_published_on(const node_t *node);
const smartlist_t *node_get_declared_family(const node_t *node);
+const ed25519_public_key_t *node_get_ed25519_id(const node_t *node);
+int node_ed25519_id_matches(const node_t *node,
+ const ed25519_public_key_t *id);
+int node_supports_ed25519_link_authentication(const node_t *node);
+const uint8_t *node_get_rsa_id_digest(const node_t *node);
int node_has_ipv6_addr(const node_t *node);
int node_has_ipv6_orport(const node_t *node);
@@ -118,7 +123,8 @@ typedef enum {
* create exit and internal paths, circuits, streams, ... */
CONSENSUS_PATH_EXIT = 1
} consensus_path_type_t;
-consensus_path_type_t router_have_consensus_path(void);
+
+MOCK_DECL(consensus_path_type_t, router_have_consensus_path, (void));
void router_dir_info_changed(void);
const char *get_dir_info_status_string(void);
diff --git a/src/or/onion.c b/src/or/onion.c
index a987883802..42b9ca4b18 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -76,6 +76,9 @@
#include "rephist.h"
#include "router.h"
+// trunnel
+#include "ed25519_cert.h"
+
/** Type for a linked list of circuits that are waiting for a free CPU worker
* to process a waiting onion handshake. */
typedef struct onion_queue_t {
@@ -871,13 +874,114 @@ check_extend_cell(const extend_cell_t *cell)
return check_create_cell(&cell->create_cell, 1);
}
-/** Protocol constants for specifier types in EXTEND2
- * @{
- */
-#define SPECTYPE_IPV4 0
-#define SPECTYPE_IPV6 1
-#define SPECTYPE_LEGACY_ID 2
-/** @} */
+static int
+extend_cell_from_extend1_cell_body(extend_cell_t *cell_out,
+ const extend1_cell_body_t *cell)
+{
+ tor_assert(cell_out);
+ tor_assert(cell);
+ memset(cell_out, 0, sizeof(*cell_out));
+ tor_addr_make_unspec(&cell_out->orport_ipv4.addr);
+ tor_addr_make_unspec(&cell_out->orport_ipv6.addr);
+
+ cell_out->cell_type = RELAY_COMMAND_EXTEND;
+ tor_addr_from_ipv4h(&cell_out->orport_ipv4.addr, cell->ipv4addr);
+ cell_out->orport_ipv4.port = cell->port;
+ if (tor_memeq(cell->onionskin, NTOR_CREATE_MAGIC, 16)) {
+ cell_out->create_cell.cell_type = CELL_CREATE2;
+ cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR;
+ cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN;
+ memcpy(cell_out->create_cell.onionskin, cell->onionskin + 16,
+ NTOR_ONIONSKIN_LEN);
+ } else {
+ cell_out->create_cell.cell_type = CELL_CREATE;
+ cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP;
+ cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN;
+ memcpy(cell_out->create_cell.onionskin, cell->onionskin,
+ TAP_ONIONSKIN_CHALLENGE_LEN);
+ }
+ memcpy(cell_out->node_id, cell->identity, DIGEST_LEN);
+ return 0;
+}
+
+static int
+create_cell_from_create2_cell_body(create_cell_t *cell_out,
+ const create2_cell_body_t *cell)
+{
+ tor_assert(cell_out);
+ tor_assert(cell);
+ memset(cell_out, 0, sizeof(create_cell_t));
+ if (BUG(cell->handshake_len > sizeof(cell_out->onionskin))) {
+ /* This should be impossible because there just isn't enough room in the
+ * input cell to make the handshake_len this large and provide a
+ * handshake_data to match. */
+ return -1;
+ }
+
+ cell_out->cell_type = CELL_CREATE2;
+ cell_out->handshake_type = cell->handshake_type;
+ cell_out->handshake_len = cell->handshake_len;
+ memcpy(cell_out->onionskin,
+ create2_cell_body_getconstarray_handshake_data(cell),
+ cell->handshake_len);
+ return 0;
+}
+
+static int
+extend_cell_from_extend2_cell_body(extend_cell_t *cell_out,
+ const extend2_cell_body_t *cell)
+{
+ tor_assert(cell_out);
+ tor_assert(cell);
+ int found_ipv4 = 0, found_ipv6 = 0, found_rsa_id = 0, found_ed_id = 0;
+ memset(cell_out, 0, sizeof(*cell_out));
+ tor_addr_make_unspec(&cell_out->orport_ipv4.addr);
+ tor_addr_make_unspec(&cell_out->orport_ipv6.addr);
+ cell_out->cell_type = RELAY_COMMAND_EXTEND2;
+
+ unsigned i;
+ for (i = 0; i < cell->n_spec; ++i) {
+ const link_specifier_t *ls = extend2_cell_body_getconst_ls(cell, i);
+ switch (ls->ls_type) {
+ case LS_IPV4:
+ if (found_ipv4)
+ continue;
+ found_ipv4 = 1;
+ tor_addr_from_ipv4h(&cell_out->orport_ipv4.addr, ls->un_ipv4_addr);
+ cell_out->orport_ipv4.port = ls->un_ipv4_port;
+ break;
+ case LS_IPV6:
+ if (found_ipv6)
+ continue;
+ found_ipv6 = 1;
+ tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr,
+ (const char *)ls->un_ipv6_addr);
+ cell_out->orport_ipv6.port = ls->un_ipv6_port;
+ break;
+ case LS_LEGACY_ID:
+ if (found_rsa_id)
+ return -1;
+ found_rsa_id = 1;
+ memcpy(cell_out->node_id, ls->un_legacy_id, 20);
+ break;
+ case LS_ED25519_ID:
+ if (found_ed_id)
+ return -1;
+ found_ed_id = 1;
+ memcpy(cell_out->ed_pubkey.pubkey, ls->un_ed25519_id, 32);
+ break;
+ default:
+ /* Ignore this, whatever it is. */
+ break;
+ }
+ }
+
+ if (!found_rsa_id || !found_ipv4) /* These are mandatory */
+ return -1;
+
+ return create_cell_from_create2_cell_body(&cell_out->create_cell,
+ cell->create2);
+}
/** Parse an EXTEND or EXTEND2 cell (according to <b>command</b>) from the
* <b>payload_length</b> bytes of <b>payload</b> into <b>cell_out</b>. Return
@@ -886,101 +990,44 @@ int
extend_cell_parse(extend_cell_t *cell_out, const uint8_t command,
const uint8_t *payload, size_t payload_length)
{
- const uint8_t *eop;
- memset(cell_out, 0, sizeof(*cell_out));
+ tor_assert(cell_out);
+ tor_assert(payload);
+
if (payload_length > RELAY_PAYLOAD_SIZE)
return -1;
- eop = payload + payload_length;
switch (command) {
case RELAY_COMMAND_EXTEND:
{
- if (payload_length != 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN)
+ extend1_cell_body_t *cell = NULL;
+ if (extend1_cell_body_parse(&cell, payload, payload_length)<0 ||
+ cell == NULL) {
+ if (cell)
+ extend1_cell_body_free(cell);
return -1;
-
- cell_out->cell_type = RELAY_COMMAND_EXTEND;
- tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr, get_uint32(payload));
- cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4));
- tor_addr_make_unspec(&cell_out->orport_ipv6.addr);
- if (tor_memeq(payload + 6, NTOR_CREATE_MAGIC, 16)) {
- cell_out->create_cell.cell_type = CELL_CREATE2;
- cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_NTOR;
- cell_out->create_cell.handshake_len = NTOR_ONIONSKIN_LEN;
- memcpy(cell_out->create_cell.onionskin, payload + 22,
- NTOR_ONIONSKIN_LEN);
- } else {
- cell_out->create_cell.cell_type = CELL_CREATE;
- cell_out->create_cell.handshake_type = ONION_HANDSHAKE_TYPE_TAP;
- cell_out->create_cell.handshake_len = TAP_ONIONSKIN_CHALLENGE_LEN;
- memcpy(cell_out->create_cell.onionskin, payload + 6,
- TAP_ONIONSKIN_CHALLENGE_LEN);
}
- memcpy(cell_out->node_id, payload + 6 + TAP_ONIONSKIN_CHALLENGE_LEN,
- DIGEST_LEN);
- break;
+ int r = extend_cell_from_extend1_cell_body(cell_out, cell);
+ extend1_cell_body_free(cell);
+ if (r < 0)
+ return r;
}
+ break;
case RELAY_COMMAND_EXTEND2:
{
- uint8_t n_specs, spectype, speclen;
- int i;
- int found_ipv4 = 0, found_ipv6 = 0, found_id = 0;
- tor_addr_make_unspec(&cell_out->orport_ipv4.addr);
- tor_addr_make_unspec(&cell_out->orport_ipv6.addr);
-
- if (payload_length == 0)
+ extend2_cell_body_t *cell = NULL;
+ if (extend2_cell_body_parse(&cell, payload, payload_length) < 0 ||
+ cell == NULL) {
+ if (cell)
+ extend2_cell_body_free(cell);
return -1;
-
- cell_out->cell_type = RELAY_COMMAND_EXTEND2;
- n_specs = *payload++;
- /* Parse the specifiers. We'll only take the first IPv4 and first IPv6
- * address, and the node ID, and ignore everything else */
- for (i = 0; i < n_specs; ++i) {
- if (eop - payload < 2)
- return -1;
- spectype = payload[0];
- speclen = payload[1];
- payload += 2;
- if (eop - payload < speclen)
- return -1;
- switch (spectype) {
- case SPECTYPE_IPV4:
- if (speclen != 6)
- return -1;
- if (!found_ipv4) {
- tor_addr_from_ipv4n(&cell_out->orport_ipv4.addr,
- get_uint32(payload));
- cell_out->orport_ipv4.port = ntohs(get_uint16(payload+4));
- found_ipv4 = 1;
- }
- break;
- case SPECTYPE_IPV6:
- if (speclen != 18)
- return -1;
- if (!found_ipv6) {
- tor_addr_from_ipv6_bytes(&cell_out->orport_ipv6.addr,
- (const char*)payload);
- cell_out->orport_ipv6.port = ntohs(get_uint16(payload+16));
- found_ipv6 = 1;
- }
- break;
- case SPECTYPE_LEGACY_ID:
- if (speclen != 20)
- return -1;
- if (found_id)
- return -1;
- memcpy(cell_out->node_id, payload, 20);
- found_id = 1;
- break;
- }
- payload += speclen;
}
- if (!found_id || !found_ipv4)
- return -1;
- if (parse_create2_payload(&cell_out->create_cell,payload,eop-payload)<0)
- return -1;
- break;
+ int r = extend_cell_from_extend2_cell_body(cell_out, cell);
+ extend2_cell_body_free(cell);
+ if (r < 0)
+ return r;
}
+ break;
default:
return -1;
}
@@ -992,6 +1039,7 @@ extend_cell_parse(extend_cell_t *cell_out, const uint8_t command,
static int
check_extended_cell(const extended_cell_t *cell)
{
+ tor_assert(cell);
if (cell->created_cell.cell_type == CELL_CREATED) {
if (cell->cell_type != RELAY_COMMAND_EXTENDED)
return -1;
@@ -1013,6 +1061,9 @@ extended_cell_parse(extended_cell_t *cell_out,
const uint8_t command, const uint8_t *payload,
size_t payload_len)
{
+ tor_assert(cell_out);
+ tor_assert(payload);
+
memset(cell_out, 0, sizeof(*cell_out));
if (payload_len > RELAY_PAYLOAD_SIZE)
return -1;
@@ -1129,6 +1180,21 @@ created_cell_format(cell_t *cell_out, const created_cell_t *cell_in)
return 0;
}
+/** Return true iff we are configured (by torrc or by the networkstatus
+ * parameters) to use Ed25519 identities in our Extend2 cells. */
+static int
+should_include_ed25519_id_extend_cells(const networkstatus_t *ns,
+ const or_options_t *options)
+{
+ if (options->ExtendByEd25519ID != -1)
+ return options->ExtendByEd25519ID; /* The user has an opinion. */
+
+ return (int) networkstatus_get_param(ns, "ExtendByEd25519ID",
+ 0 /* default */,
+ 0 /* min */,
+ 1 /*max*/);
+}
+
/** Format the EXTEND{,2} cell in <b>cell_in</b>, storing its relay payload in
* <b>payload_out</b>, the number of bytes used in *<b>len_out</b>, and the
* relay command in *<b>command_out</b>. The <b>payload_out</b> must have
@@ -1137,12 +1203,11 @@ int
extend_cell_format(uint8_t *command_out, uint16_t *len_out,
uint8_t *payload_out, const extend_cell_t *cell_in)
{
- uint8_t *p, *eop;
+ uint8_t *p;
if (check_extend_cell(cell_in) < 0)
return -1;
p = payload_out;
- eop = payload_out + RELAY_PAYLOAD_SIZE;
memset(p, 0, RELAY_PAYLOAD_SIZE);
@@ -1165,33 +1230,56 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out,
break;
case RELAY_COMMAND_EXTEND2:
{
- uint8_t n = 2;
+ uint8_t n_specifiers = 2;
*command_out = RELAY_COMMAND_EXTEND2;
-
- *p++ = n; /* 2 identifiers */
- *p++ = SPECTYPE_IPV4; /* First is IPV4. */
- *p++ = 6; /* It's 6 bytes long. */
- set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr));
- set_uint16(p+4, htons(cell_in->orport_ipv4.port));
- p += 6;
- *p++ = SPECTYPE_LEGACY_ID; /* Next is an identity digest. */
- *p++ = 20; /* It's 20 bytes long */
- memcpy(p, cell_in->node_id, DIGEST_LEN);
- p += 20;
-
- /* Now we can send the handshake */
- set_uint16(p, htons(cell_in->create_cell.handshake_type));
- set_uint16(p+2, htons(cell_in->create_cell.handshake_len));
- p += 4;
-
- if (cell_in->create_cell.handshake_len > eop - p)
- return -1;
-
- memcpy(p, cell_in->create_cell.onionskin,
+ extend2_cell_body_t *cell = extend2_cell_body_new();
+ link_specifier_t *ls;
+ {
+ /* IPv4 specifier first. */
+ ls = link_specifier_new();
+ extend2_cell_body_add_ls(cell, ls);
+ ls->ls_type = LS_IPV4;
+ ls->ls_len = 6;
+ ls->un_ipv4_addr = tor_addr_to_ipv4h(&cell_in->orport_ipv4.addr);
+ ls->un_ipv4_port = cell_in->orport_ipv4.port;
+ }
+ {
+ /* Then RSA id */
+ ls = link_specifier_new();
+ extend2_cell_body_add_ls(cell, ls);
+ ls->ls_type = LS_LEGACY_ID;
+ ls->ls_len = DIGEST_LEN;
+ memcpy(ls->un_legacy_id, cell_in->node_id, DIGEST_LEN);
+ }
+ if (should_include_ed25519_id_extend_cells(NULL, get_options()) &&
+ !ed25519_public_key_is_zero(&cell_in->ed_pubkey)) {
+ /* Then, maybe, the ed25519 id! */
+ ++n_specifiers;
+ ls = link_specifier_new();
+ extend2_cell_body_add_ls(cell, ls);
+ ls->ls_type = LS_ED25519_ID;
+ ls->ls_len = 32;
+ memcpy(ls->un_ed25519_id, cell_in->ed_pubkey.pubkey, 32);
+ }
+ cell->n_spec = n_specifiers;
+
+ /* Now, the handshake */
+ cell->create2 = create2_cell_body_new();
+ cell->create2->handshake_type = cell_in->create_cell.handshake_type;
+ cell->create2->handshake_len = cell_in->create_cell.handshake_len;
+ create2_cell_body_setlen_handshake_data(cell->create2,
+ cell_in->create_cell.handshake_len);
+ memcpy(create2_cell_body_getarray_handshake_data(cell->create2),
+ cell_in->create_cell.onionskin,
cell_in->create_cell.handshake_len);
- p += cell_in->create_cell.handshake_len;
- *len_out = p - payload_out;
+ ssize_t len_encoded = extend2_cell_body_encode(
+ payload_out, RELAY_PAYLOAD_SIZE,
+ cell);
+ extend2_cell_body_free(cell);
+ if (len_encoded < 0 || len_encoded > UINT16_MAX)
+ return -1;
+ *len_out = (uint16_t) len_encoded;
}
break;
default:
diff --git a/src/or/onion.h b/src/or/onion.h
index 0275fa00d2..19e4a7c381 100644
--- a/src/or/onion.h
+++ b/src/or/onion.h
@@ -85,6 +85,8 @@ typedef struct extend_cell_t {
tor_addr_port_t orport_ipv6;
/** Identity fingerprint of the node we're conecting to.*/
uint8_t node_id[DIGEST_LEN];
+ /** Ed25519 public identity key. Zero if not set. */
+ ed25519_public_key_t ed_pubkey;
/** The "create cell" embedded in this extend cell. Note that unlike the
* create cells we generate ourself, this once can have a handshake type we
* don't recognize. */
diff --git a/src/or/or.h b/src/or/or.h
index 04ff548a78..d45dc687b4 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -80,6 +80,7 @@
#include "crypto_ed25519.h"
#include "tor_queue.h"
#include "util_format.h"
+#include "hs_circuitmap.h"
/* These signals are defined to help handle_control_signal work.
*/
@@ -1585,8 +1586,6 @@ typedef struct or_connection_t {
* bandwidthburst. (OPEN ORs only) */
int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */
- struct or_connection_t *next_with_same_id; /**< Next connection with same
- * identity digest as this one. */
/** Last emptied read token bucket in msec since midnight; only used if
* TB_EMPTY events are enabled. */
uint32_t read_emptied_time;
@@ -1664,6 +1663,8 @@ typedef struct entry_connection_t {
edge_connection_t edge_;
/** Nickname of planned exit node -- used with .exit support. */
+ /* XXX prop220: we need to make chosen_exit_name able to encode Ed IDs too.
+ * That's logically part of the UI parts for prop220 though. */
char *chosen_exit_name;
socks_request_t *socks_request; /**< SOCKS structure describing request (AP
@@ -2718,7 +2719,10 @@ typedef struct {
typedef struct extend_info_t {
char nickname[MAX_HEX_NICKNAME_LEN+1]; /**< This router's nickname for
* display. */
- char identity_digest[DIGEST_LEN]; /**< Hash of this router's identity key. */
+ /** Hash of this router's RSA identity key. */
+ char identity_digest[DIGEST_LEN];
+ /** Ed25519 identity for this router, if any. */
+ ed25519_public_key_t ed_identity;
uint16_t port; /**< OR port. */
tor_addr_t addr; /**< IP address. */
crypto_pk_t *onion_key; /**< Current onionskin key. */
@@ -3369,7 +3373,12 @@ typedef struct or_circuit_t {
* is not marked for close. */
struct or_circuit_t *rend_splice;
- struct or_circuit_rendinfo_s *rendinfo;
+ /** If set, points to an HS token that this circuit might be carrying.
+ * Used by the HS circuitmap. */
+ hs_token_t *hs_token;
+ /** Hashtable node: used to look up the circuit by its HS token using the HS
+ circuitmap. */
+ HT_ENTRY(or_circuit_t) hs_circuitmap_node;
/** Stores KH for the handshake. */
char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */
@@ -3404,25 +3413,11 @@ typedef struct or_circuit_t {
uint32_t max_middle_cells;
} or_circuit_t;
-typedef struct or_circuit_rendinfo_s {
-
#if REND_COOKIE_LEN != DIGEST_LEN
#error "The REND_TOKEN_LEN macro assumes REND_COOKIE_LEN == DIGEST_LEN"
#endif
#define REND_TOKEN_LEN DIGEST_LEN
- /** A hash of location-hidden service's PK if purpose is INTRO_POINT, or a
- * rendezvous cookie if purpose is REND_POINT_WAITING. Filled with zeroes
- * otherwise.
- */
- char rend_token[REND_TOKEN_LEN];
-
- /** True if this is a rendezvous point circuit; false if this is an
- * introduction point. */
- unsigned is_rend_circ;
-
-} or_circuit_rendinfo_t;
-
/** Convert a circuit subtype to a circuit_t. */
#define TO_CIRCUIT(x) (&((x)->base_))
@@ -3897,7 +3892,7 @@ typedef struct {
uint64_t BandwidthBurst; /**< How much bandwidth, at maximum, are we willing
* to use in a second? */
uint64_t MaxAdvertisedBandwidth; /**< How much bandwidth are we willing to
- * tell people we have? */
+ * tell other nodes we have? */
uint64_t RelayBandwidthRate; /**< How much bandwidth, on average, are we
* willing to use for all relayed conns? */
uint64_t RelayBandwidthBurst; /**< How much bandwidth, at maximum, will we
@@ -3983,9 +3978,6 @@ typedef struct {
* and vote for all other exits as good. */
int AuthDirMaxServersPerAddr; /**< Do not permit more than this
* number of servers per IP address. */
- int AuthDirMaxServersPerAuthAddr; /**< Do not permit more than this
- * number of servers per IP address shared
- * with an authority. */
int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */
int AuthDirPinKeys; /**< Boolean: Do we enforce key-pinning? */
@@ -4588,6 +4580,15 @@ typedef struct {
/** If 1, we skip all OOS checks. */
int DisableOOSCheck;
+ /** Autobool: Should we include Ed25519 identities in extend2 cells?
+ * If -1, we should do whatever the consensus parameter says. */
+ int ExtendByEd25519ID;
+
+ /** Bool (default: 1): When testing routerinfos as a directory authority,
+ * do we enforce Ed25519 identity match? */
+ /* NOTE: remove this option someday. */
+ int AuthDirTestEd25519LinkKeys;
+
/** If 1, we use the old (pre-prop271) guard selection algorithm.
*
* XXXX prop271 This option is only here as a stopgap while we're
diff --git a/src/or/protover.c b/src/or/protover.c
index 5972e61be7..ceaf2d5ccf 100644
--- a/src/or/protover.c
+++ b/src/or/protover.c
@@ -697,7 +697,7 @@ protover_compute_for_old_tor(const char *version)
if (tor_version_as_new_as(version,
FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS)) {
return "";
- } else if (tor_version_as_new_as(version, "0.2.7.5")) {
+ } else if (tor_version_as_new_as(version, "0.2.9.1-alpha")) {
/* 0.2.9.1-alpha HSRend=2 */
return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 "
"Link=1-4 LinkAuth=1 "
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index f2060e528c..def51b7986 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -16,6 +16,7 @@
#include "rendclient.h"
#include "rendcommon.h"
#include "rendmid.h"
+#include "hs_intropoint.h"
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
@@ -762,7 +763,7 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
switch (command) {
case RELAY_COMMAND_ESTABLISH_INTRO:
if (or_circ)
- r = rend_mid_establish_intro(or_circ,payload,length);
+ r = hs_intro_received_establish_intro(or_circ,payload,length);
break;
case RELAY_COMMAND_ESTABLISH_RENDEZVOUS:
if (or_circ)
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index f39c92afae..3319a639b9 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -11,16 +11,19 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
+#include "crypto.h"
#include "relay.h"
#include "rendmid.h"
#include "rephist.h"
+#include "hs_circuitmap.h"
+#include "hs_intropoint.h"
/** Respond to an ESTABLISH_INTRO cell by checking the signed data and
* setting the circuit's purpose and service pk digest.
*/
int
-rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
- size_t request_len)
+rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request,
+ size_t request_len)
{
crypto_pk_t *pk = NULL;
char buf[DIGEST_LEN+9];
@@ -32,15 +35,14 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
int reason = END_CIRC_REASON_INTERNAL;
log_info(LD_REND,
- "Received an ESTABLISH_INTRO request on circuit %u",
+ "Received a legacy ESTABLISH_INTRO request on circuit %u",
(unsigned) circ->p_circ_id);
- if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit.");
+ if (!hs_intro_circuit_is_suitable(circ)) {
reason = END_CIRC_REASON_TORPROTOCOL;
goto err;
}
+
if (request_len < 2+DIGEST_LEN)
goto truncated;
/* First 2 bytes: length of asn1-encoded key. */
@@ -94,7 +96,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
/* Close any other intro circuits with the same pk. */
c = NULL;
- while ((c = circuit_get_intro_point((const uint8_t *)pk_digest))) {
+ while ((c = hs_circuitmap_get_intro_circ_v2((const uint8_t *)pk_digest))) {
log_info(LD_REND, "Replacing old circuit for service %s",
safe_str(serviceid));
circuit_mark_for_close(TO_CIRCUIT(c), END_CIRC_REASON_FINISHED);
@@ -102,16 +104,14 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
}
/* Acknowledge the request. */
- if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
- RELAY_COMMAND_INTRO_ESTABLISHED,
- "", 0, NULL)<0) {
+ if (hs_intro_send_intro_established_cell(circ) < 0) {
log_info(LD_GENERAL, "Couldn't send INTRO_ESTABLISHED cell.");
goto err_no_close;
}
/* Now, set up this circuit. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
- circuit_set_intro_point_digest(circ, (uint8_t *)pk_digest);
+ hs_circuitmap_register_intro_circ_v2(circ, (uint8_t *)pk_digest);
log_info(LD_REND,
"Established introduction point on circuit %u for service %s",
@@ -181,7 +181,7 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request,
/* The first 20 bytes are all we look at: they have a hash of the service's
* PK. */
- intro_circ = circuit_get_intro_point((const uint8_t*)request);
+ intro_circ = hs_circuitmap_get_intro_circ_v2((const uint8_t*)request);
if (!intro_circ) {
log_info(LD_REND,
"No intro circ found for INTRODUCE1 cell (%s) from circuit %u; "
@@ -258,7 +258,7 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
goto err;
}
- if (circuit_get_rendezvous(request)) {
+ if (hs_circuitmap_get_rend_circ(request)) {
log_warn(LD_PROTOCOL,
"Duplicate rendezvous cookie in ESTABLISH_RENDEZVOUS.");
goto err;
@@ -274,7 +274,7 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
}
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING);
- circuit_set_rendezvous_cookie(circ, request);
+ hs_circuitmap_register_rend_circ(circ, request);
base16_encode(hexid,9,(char*)request,4);
@@ -323,7 +323,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
"Got request for rendezvous from circuit %u to cookie %s.",
(unsigned)circ->p_circ_id, hexid);
- rend_circ = circuit_get_rendezvous(request);
+ rend_circ = hs_circuitmap_get_rend_circ(request);
if (!rend_circ) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Rejecting RENDEZVOUS1 cell with unrecognized rendezvous cookie %s.",
@@ -358,7 +358,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_ESTABLISHED);
circuit_change_purpose(TO_CIRCUIT(rend_circ),
CIRCUIT_PURPOSE_REND_ESTABLISHED);
- circuit_set_rendezvous_cookie(circ, NULL);
+ hs_circuitmap_remove_circuit(circ);
rend_circ->rend_splice = circ;
circ->rend_splice = rend_circ;
diff --git a/src/or/rendmid.h b/src/or/rendmid.h
index 10d1287085..374f2b7d66 100644
--- a/src/or/rendmid.h
+++ b/src/or/rendmid.h
@@ -12,8 +12,8 @@
#ifndef TOR_RENDMID_H
#define TOR_RENDMID_H
-int rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
- size_t request_len);
+int rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request,
+ size_t request_len);
int rend_mid_introduce(or_circuit_t *circ, const uint8_t *request,
size_t request_len);
int rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index cf57c7d061..1713a84bbd 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -76,6 +76,13 @@ static ssize_t rend_service_parse_intro_for_v3(
static int rend_service_check_private_dir(const or_options_t *options,
const rend_service_t *s,
int create);
+static int rend_service_check_private_dir_impl(const or_options_t *options,
+ const rend_service_t *s,
+ int create);
+static const smartlist_t* rend_get_service_list(
+ const smartlist_t* substitute_service_list);
+static smartlist_t* rend_get_service_list_mutable(
+ smartlist_t* substitute_service_list);
/** Represents the mapping from a virtual port of a rendezvous service to
* a real port on some IP.
@@ -121,8 +128,44 @@ static const char *hostname_fname = "hostname";
static const char *client_keys_fname = "client_keys";
static const char *sos_poison_fname = "onion_service_non_anonymous";
+/** A list of rend_service_t's for services run on this OP.
+ */
+static smartlist_t *rend_service_list = NULL;
+
+/* Like rend_get_service_list_mutable, but returns a read-only list. */
+static const smartlist_t*
+rend_get_service_list(const smartlist_t* substitute_service_list)
+{
+ /* It is safe to cast away the const here, because
+ * rend_get_service_list_mutable does not actually modify the list */
+ return rend_get_service_list_mutable((smartlist_t*)substitute_service_list);
+}
+
+/* Return a mutable list of hidden services.
+ * If substitute_service_list is not NULL, return it.
+ * Otherwise, check if the global rend_service_list is non-NULL, and if so,
+ * return it.
+ * Otherwise, log a BUG message and return NULL.
+ * */
+static smartlist_t*
+rend_get_service_list_mutable(smartlist_t* substitute_service_list)
+{
+ if (substitute_service_list) {
+ return substitute_service_list;
+ }
+
+ /* If no special service list is provided, then just use the global one. */
+
+ if (BUG(!rend_service_list)) {
+ /* No global HS list, which is a programmer error. */
+ return NULL;
+ }
+
+ return rend_service_list;
+}
+
/** Tells if onion service <b>s</b> is ephemeral.
-*/
+ */
static unsigned int
rend_service_is_ephemeral(const struct rend_service_t *s)
{
@@ -137,10 +180,6 @@ rend_service_escaped_dir(const struct rend_service_t *s)
return rend_service_is_ephemeral(s) ? "[EPHEMERAL]" : escaped(s->directory);
}
-/** A list of rend_service_t's for services run on this OP.
- */
-static smartlist_t *rend_service_list = NULL;
-
/** Return the number of rendezvous services we have configured. */
int
num_rend_services(void)
@@ -225,21 +264,32 @@ rend_service_free_all(void)
rend_service_list = NULL;
}
-/** Validate <b>service</b> and add it to rend_service_list if possible.
+/** Validate <b>service</b> and add it to <b>service_list</b>, or to
+ * the global rend_service_list if <b>service_list</b> is NULL.
* Return 0 on success. On failure, free <b>service</b> and return -1.
+ * Takes ownership of <b>service</b>.
*/
static int
-rend_add_service(rend_service_t *service)
+rend_add_service(smartlist_t *service_list, rend_service_t *service)
{
int i;
rend_service_port_config_t *p;
+ tor_assert(service);
+
+ smartlist_t *s_list = rend_get_service_list_mutable(service_list);
+ /* We must have a service list, even if it's a temporary one, so we can
+ * check for duplicate services */
+ if (BUG(!s_list)) {
+ return -1;
+ }
+
service->intro_nodes = smartlist_new();
service->expiring_nodes = smartlist_new();
if (service->max_streams_per_circuit < 0) {
log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max "
- "streams per circuit; ignoring.",
+ "streams per circuit.",
rend_service_escaped_dir(service));
rend_service_free(service);
return -1;
@@ -248,24 +298,24 @@ rend_add_service(rend_service_t *service)
if (service->max_streams_close_circuit < 0 ||
service->max_streams_close_circuit > 1) {
log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid "
- "max streams handling; ignoring.",
+ "max streams handling.",
rend_service_escaped_dir(service));
rend_service_free(service);
return -1;
}
if (service->auth_type != REND_NO_AUTH &&
- smartlist_len(service->clients) == 0) {
+ (!service->clients ||
+ smartlist_len(service->clients) == 0)) {
log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no "
- "clients; ignoring.",
+ "clients.",
rend_service_escaped_dir(service));
rend_service_free(service);
return -1;
}
- if (!smartlist_len(service->ports)) {
- log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; "
- "ignoring.",
+ if (!service->ports || !smartlist_len(service->ports)) {
+ log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.",
rend_service_escaped_dir(service));
rend_service_free(service);
return -1;
@@ -286,20 +336,20 @@ rend_add_service(rend_service_t *service)
* lock file. But this is enough to detect a simple mistake that
* at least one person has actually made.
*/
+ tor_assert(s_list);
if (!rend_service_is_ephemeral(service)) {
/* Skip dupe for ephemeral services. */
- SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
+ SMARTLIST_FOREACH(s_list, rend_service_t*, ptr,
dupe = dupe ||
!strcmp(ptr->directory, service->directory));
if (dupe) {
log_warn(LD_REND, "Another hidden service is already configured for "
- "directory %s, ignoring.",
+ "directory %s.",
rend_service_escaped_dir(service));
rend_service_free(service);
return -1;
}
}
- smartlist_add(rend_service_list, service);
log_debug(LD_REND,"Configuring service with directory %s",
rend_service_escaped_dir(service));
for (i = 0; i < smartlist_len(service->ports); ++i) {
@@ -315,14 +365,19 @@ rend_add_service(rend_service_t *service)
"Service maps port %d to socket at \"%s\"",
p->virtual_port, p->unix_addr);
#else
- log_debug(LD_REND,
- "Service maps port %d to an AF_UNIX socket, but we "
- "have no AF_UNIX support on this platform. This is "
- "probably a bug.",
- p->virtual_port);
+ log_warn(LD_BUG,
+ "Service maps port %d to an AF_UNIX socket, but we "
+ "have no AF_UNIX support on this platform. This is "
+ "probably a bug.",
+ p->virtual_port);
+ rend_service_free(service);
+ return -1;
#endif /* defined(HAVE_SYS_UN_H) */
}
}
+ /* The service passed all the checks */
+ tor_assert(s_list);
+ smartlist_add(s_list, service);
return 0;
}
/* NOTREACHED */
@@ -452,6 +507,41 @@ rend_service_port_config_free(rend_service_port_config_t *p)
tor_free(p);
}
+/* Check the directory for <b>service</b>, and add the service to
+ * <b>service_list</b>, or to the global list if <b>service_list</b> is NULL.
+ * Only add the service to the list if <b>validate_only</b> is false.
+ * If <b>validate_only</b> is true, free the service.
+ * If <b>service</b> is NULL, ignore it, and return 0.
+ * Returns 0 on success, and -1 on failure.
+ * Takes ownership of <b>service</b>, either freeing it, or adding it to the
+ * global service list.
+ */
+STATIC int
+rend_service_check_dir_and_add(smartlist_t *service_list,
+ const or_options_t *options,
+ rend_service_t *service,
+ int validate_only)
+{
+ if (!service) {
+ /* It is ok for a service to be NULL, this means there are no services */
+ return 0;
+ }
+
+ if (rend_service_check_private_dir(options, service, !validate_only)
+ < 0) {
+ rend_service_free(service);
+ return -1;
+ }
+
+ smartlist_t *s_list = rend_get_service_list_mutable(service_list);
+ /* We must have a service list, even if it's a temporary one, so we can
+ * check for duplicate services */
+ if (BUG(!s_list)) {
+ return -1;
+ }
+ return rend_add_service(s_list, service);
+}
+
/** Set up rend_service_list, based on the values of HiddenServiceDir and
* HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
* failure. (If <b>validate_only</b> is set, parse, warn and return as
@@ -464,25 +554,21 @@ rend_config_services(const or_options_t *options, int validate_only)
rend_service_t *service = NULL;
rend_service_port_config_t *portcfg;
smartlist_t *old_service_list = NULL;
+ smartlist_t *temp_service_list = NULL;
int ok = 0;
- if (!validate_only) {
- old_service_list = rend_service_list;
- rend_service_list = smartlist_new();
- }
+ /* Use a temporary service list, so that we can check the new services'
+ * consistency with each other */
+ temp_service_list = smartlist_new();
for (line = options->RendConfigLines; line; line = line->next) {
if (!strcasecmp(line->key, "HiddenServiceDir")) {
- if (service) { /* register the one we just finished parsing */
- if (rend_service_check_private_dir(options, service, 0) < 0) {
- rend_service_free(service);
+ /* register the service we just finished parsing
+ * this code registers every service except the last one parsed,
+ * which is registered below the loop */
+ if (rend_service_check_dir_and_add(temp_service_list, options, service,
+ validate_only) < 0) {
return -1;
- }
-
- if (validate_only)
- rend_service_free(service);
- else
- rend_add_service(service);
}
service = tor_malloc_zero(sizeof(rend_service_t));
service->directory = tor_strdup(line->value);
@@ -693,19 +779,30 @@ rend_config_services(const or_options_t *options, int validate_only)
}
}
}
- if (service) {
- if (rend_service_check_private_dir(options, service, 0) < 0) {
- rend_service_free(service);
- return -1;
- }
+ /* register the final service after we have finished parsing all services
+ * this code only registers the last service, other services are registered
+ * within the loop. It is ok for this service to be NULL, it is ignored. */
+ if (rend_service_check_dir_and_add(temp_service_list, options, service,
+ validate_only) < 0) {
+ return -1;
+ }
- if (validate_only) {
- rend_service_free(service);
- } else {
- rend_add_service(service);
- }
+ /* Free the newly added services if validating */
+ if (validate_only) {
+ SMARTLIST_FOREACH(temp_service_list, rend_service_t *, ptr,
+ rend_service_free(ptr));
+ smartlist_free(temp_service_list);
+ temp_service_list = NULL;
+ return 0;
}
+ /* Otherwise, use the newly added services as the new service list
+ * Since we have now replaced the global service list, from this point on we
+ * must succeed, or die trying. */
+ old_service_list = rend_service_list;
+ rend_service_list = temp_service_list;
+ temp_service_list = NULL;
+
/* If this is a reload and there were hidden services configured before,
* keep the introduction points that are still needed and close the
* other ones. */
@@ -727,7 +824,7 @@ rend_config_services(const or_options_t *options, int validate_only)
* will NOT have their intro point closed.
*/
SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, {
- if (!old->directory) {
+ if (rend_service_is_ephemeral(old)) {
SMARTLIST_DEL_CURRENT(old_service_list, old);
smartlist_add(surviving_services, old);
smartlist_add(rend_service_list, old);
@@ -739,15 +836,20 @@ rend_config_services(const or_options_t *options, int validate_only)
* probably ok? */
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) {
SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) {
- if (new->directory && old->directory &&
- !strcmp(old->directory, new->directory)) {
- smartlist_add_all(new->intro_nodes, old->intro_nodes);
- smartlist_clear(old->intro_nodes);
- smartlist_add_all(new->expiring_nodes, old->expiring_nodes);
- smartlist_clear(old->expiring_nodes);
- smartlist_add(surviving_services, old);
- break;
+ if (BUG(rend_service_is_ephemeral(new)) ||
+ BUG(rend_service_is_ephemeral(old))) {
+ continue;
}
+ if (BUG(!new->directory) || BUG(!old->directory) ||
+ strcmp(old->directory, new->directory)) {
+ continue;
+ }
+ smartlist_add_all(new->intro_nodes, old->intro_nodes);
+ smartlist_clear(old->intro_nodes);
+ smartlist_add_all(new->expiring_nodes, old->expiring_nodes);
+ smartlist_clear(old->expiring_nodes);
+ smartlist_add(surviving_services, old);
+ break;
} SMARTLIST_FOREACH_END(old);
} SMARTLIST_FOREACH_END(new);
@@ -859,7 +961,7 @@ rend_service_add_ephemeral(crypto_pk_t *pk,
}
/* Initialize the service. */
- if (rend_add_service(s)) {
+ if (rend_add_service(NULL, s)) {
return RSAE_INTERNAL;
}
*service_id_out = tor_strdup(s->service_id);
@@ -1009,6 +1111,11 @@ service_is_single_onion_poisoned(const rend_service_t *service)
char *poison_fname = NULL;
file_status_t fstatus;
+ /* Passing a NULL service is a bug */
+ if (BUG(!service)) {
+ return 0;
+ }
+
if (rend_service_is_ephemeral(service)) {
return 0;
}
@@ -1042,58 +1149,69 @@ rend_service_private_key_exists(const rend_service_t *service)
return private_key_status == FN_FILE;
}
-/** Check the single onion service poison state of all existing hidden service
- * directories:
- * - If each service is poisoned, and we are in Single Onion Mode,
+/** Check the single onion service poison state of the directory for s:
+ * - If the service is poisoned, and we are in Single Onion Mode,
* return 0,
- * - If each service is not poisoned, and we are not in Single Onion Mode,
+ * - If the service is not poisoned, and we are not in Single Onion Mode,
* return 0,
- * - Otherwise, the poison state is invalid, and a service that was created in
- * one mode is being used in the other, return -1.
- * Hidden service directories without keys are not checked for consistency.
- * When their keys are created, they will be poisoned (if needed).
- * If a <b>service_list</b> is provided, treat it
- * as the list of hidden services (used in unittests). */
-int
-rend_service_list_verify_single_onion_poison(const smartlist_t *service_list,
- const or_options_t *options)
+ * - Otherwise, the poison state is invalid: the service was created in one
+ * mode, and is being used in the other, return -1.
+ * Hidden service directories without keys are always considered consistent.
+ * They will be poisoned after their directory is created (if needed). */
+STATIC int
+rend_service_verify_single_onion_poison(const rend_service_t* s,
+ const or_options_t* options)
{
- const smartlist_t *s_list;
- /* If no special service list is provided, then just use the global one. */
- if (!service_list) {
- if (!rend_service_list) { /* No global HS list. Nothing to see here. */
- return 0;
- }
+ /* Passing a NULL service is a bug */
+ if (BUG(!s)) {
+ return -1;
+ }
- s_list = rend_service_list;
- } else {
- s_list = service_list;
+ /* Ephemeral services are checked at ADD_ONION time */
+ if (BUG(rend_service_is_ephemeral(s))) {
+ return -1;
}
- int consistent = 1;
- SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) {
- if (service_is_single_onion_poisoned(s) !=
- rend_service_non_anonymous_mode_enabled(options) &&
- rend_service_private_key_exists(s)) {
- consistent = 0;
- }
- } SMARTLIST_FOREACH_END(s);
+ /* Service is expected to have a directory */
+ if (BUG(!s->directory)) {
+ return -1;
+ }
+
+ /* Services without keys are always ok - their keys will only ever be used
+ * in the current mode */
+ if (!rend_service_private_key_exists(s)) {
+ return 0;
+ }
+
+ /* The key has been used before in a different mode */
+ if (service_is_single_onion_poisoned(s) !=
+ rend_service_non_anonymous_mode_enabled(options)) {
+ return -1;
+ }
- return consistent ? 0 : -1;
+ /* The key exists and is consistent with the current mode */
+ return 0;
}
-/*** Helper for rend_service_poison_new_single_onion_dirs(). Add a file to
- * this hidden service directory that marks it as a single onion service.
- * Tor must be in single onion mode before calling this function.
+/*** Helper for rend_service_poison_new_single_onion_dir(). Add a file to
+ * the hidden service directory for s that marks it as a single onion service.
+ * Tor must be in single onion mode before calling this function, and the
+ * service directory must already have been created.
* Returns 0 when a directory is successfully poisoned, or if it is already
* poisoned. Returns -1 on a failure to read the directory or write the poison
* file, or if there is an existing private key file in the directory. (The
* service should have been poisoned when the key was created.) */
static int
-poison_new_single_onion_hidden_service_dir(const rend_service_t *service)
+poison_new_single_onion_hidden_service_dir_impl(const rend_service_t *service,
+ const or_options_t* options)
{
+ /* Passing a NULL service is a bug */
+ if (BUG(!service)) {
+ return -1;
+ }
+
/* We must only poison directories if we're in Single Onion mode */
- tor_assert(rend_service_non_anonymous_mode_enabled(get_options()));
+ tor_assert(rend_service_non_anonymous_mode_enabled(options));
int fd;
int retval = -1;
@@ -1111,8 +1229,8 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service)
return -1;
}
- /* Make sure the directory exists */
- if (rend_service_check_private_dir(get_options(), service, 1) < 0)
+ /* Make sure the directory was created before calling this function. */
+ if (BUG(rend_service_check_private_dir_impl(options, service, 0) < 0))
return -1;
poison_fname = rend_service_sos_poison_path(service);
@@ -1149,44 +1267,39 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service)
return retval;
}
-/** We just got launched in Single Onion Mode. That's a non-anoymous
- * mode for hidden services; hence we should mark all new hidden service
- * directories appropriately so that they are never launched as
- * location-private hidden services again. (New directories don't have private
- * key files.)
- * If a <b>service_list</b> is provided, treat it as the list of hidden
- * services (used in unittests).
+/** We just got launched in Single Onion Mode. That's a non-anonymous mode for
+ * hidden services. If s is new, we should mark its hidden service
+ * directory appropriately so that it is never launched as a location-private
+ * hidden service. (New directories don't have private key files.)
* Return 0 on success, -1 on fail. */
-int
-rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list)
+STATIC int
+rend_service_poison_new_single_onion_dir(const rend_service_t *s,
+ const or_options_t* options)
{
+ /* Passing a NULL service is a bug */
+ if (BUG(!s)) {
+ return -1;
+ }
+
/* We must only poison directories if we're in Single Onion mode */
- tor_assert(rend_service_non_anonymous_mode_enabled(get_options()));
+ tor_assert(rend_service_non_anonymous_mode_enabled(options));
- const smartlist_t *s_list;
- /* If no special service list is provided, then just use the global one. */
- if (!service_list) {
- if (!rend_service_list) { /* No global HS list. Nothing to see here. */
- return 0;
- }
+ /* Ephemeral services aren't allowed in non-anonymous mode */
+ if (BUG(rend_service_is_ephemeral(s))) {
+ return -1;
+ }
- s_list = rend_service_list;
- } else {
- s_list = service_list;
+ /* Service is expected to have a directory */
+ if (BUG(!s->directory)) {
+ return -1;
}
- SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) {
- if (!rend_service_private_key_exists(s)) {
- if (poison_new_single_onion_hidden_service_dir(s) < 0) {
- return -1;
- }
+ if (!rend_service_private_key_exists(s)) {
+ if (poison_new_single_onion_hidden_service_dir_impl(s, options)
+ < 0) {
+ return -1;
}
- } SMARTLIST_FOREACH_END(s);
-
- /* The keys for these services are linked to the server IP address */
- log_notice(LD_REND, "The configured onion service directories have been "
- "used in single onion mode. They can not be used for anonymous "
- "hidden services.");
+ }
return 0;
}
@@ -1200,13 +1313,10 @@ rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list)
int
rend_service_load_all_keys(const smartlist_t *service_list)
{
- const smartlist_t *s_list;
- /* If no special service list is provided, then just use the global one. */
- if (!service_list) {
- tor_assert(rend_service_list);
- s_list = rend_service_list;
- } else {
- s_list = service_list;
+ /* Use service_list for unit tests */
+ const smartlist_t *s_list = rend_get_service_list(service_list);
+ if (BUG(!s_list)) {
+ return -1;
}
SMARTLIST_FOREACH_BEGIN(s_list, rend_service_t *, s) {
@@ -1270,6 +1380,32 @@ rend_service_derive_key_digests(struct rend_service_t *s)
return 0;
}
+/* Implements the directory check from rend_service_check_private_dir,
+ * without doing the single onion poison checks. */
+static int
+rend_service_check_private_dir_impl(const or_options_t *options,
+ const rend_service_t *s,
+ int create)
+{
+ cpd_check_t check_opts = CPD_NONE;
+ if (create) {
+ check_opts |= CPD_CREATE;
+ } else {
+ check_opts |= CPD_CHECK_MODE_ONLY;
+ check_opts |= CPD_CHECK;
+ }
+ if (s->dir_group_readable) {
+ check_opts |= CPD_GROUP_READ;
+ }
+ /* Check/create directory */
+ if (check_private_dir(s->directory, check_opts, options->User) < 0) {
+ log_warn(LD_REND, "Checking service directory %s failed.", s->directory);
+ return -1;
+ }
+
+ return 0;
+}
+
/** Make sure that the directory for <b>s</b> is private, using the config in
* <b>options</b>.
* If <b>create</b> is true:
@@ -1284,20 +1420,58 @@ rend_service_check_private_dir(const or_options_t *options,
const rend_service_t *s,
int create)
{
- cpd_check_t check_opts = CPD_NONE;
- if (create) {
- check_opts |= CPD_CREATE;
- } else {
- check_opts |= CPD_CHECK_MODE_ONLY;
- check_opts |= CPD_CHECK;
- }
- if (s->dir_group_readable) {
- check_opts |= CPD_GROUP_READ;
+ /* Passing a NULL service is a bug */
+ if (BUG(!s)) {
+ return -1;
}
+
/* Check/create directory */
- if (check_private_dir(s->directory, check_opts, options->User) < 0) {
+ if (rend_service_check_private_dir_impl(options, s, create) < 0) {
+ return -1;
+ }
+
+ /* Check if the hidden service key exists, and was created in a different
+ * single onion service mode, and refuse to launch if it has.
+ * This is safe to call even when create is false, as it ignores missing
+ * keys and directories: they are always valid.
+ */
+ if (rend_service_verify_single_onion_poison(s, options) < 0) {
+ /* We can't use s->service_id here, as the key may not have been loaded */
+ log_warn(LD_GENERAL, "We are configured with "
+ "HiddenServiceNonAnonymousMode %d, but the hidden "
+ "service key in directory %s was created in %s mode. "
+ "This is not allowed.",
+ rend_service_non_anonymous_mode_enabled(options) ? 1 : 0,
+ rend_service_escaped_dir(s),
+ rend_service_non_anonymous_mode_enabled(options) ?
+ "an anonymous" : "a non-anonymous"
+ );
return -1;
}
+
+ /* Poison new single onion directories immediately after they are created,
+ * so that we never accidentally launch non-anonymous hidden services
+ * thinking they are anonymous. Any keys created later will end up with the
+ * correct poisoning state.
+ */
+ if (create && rend_service_non_anonymous_mode_enabled(options)) {
+ static int logged_warning = 0;
+
+ if (rend_service_poison_new_single_onion_dir(s, options) < 0) {
+ log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous"
+ ".");
+ return -1;
+ }
+
+ if (!logged_warning) {
+ /* The keys for these services are linked to the server IP address */
+ log_notice(LD_REND, "The configured onion service directories have been "
+ "used in single onion mode. They can not be used for "
+ "anonymous hidden services.");
+ logged_warning = 1;
+ }
+ }
+
return 0;
}
@@ -1310,7 +1484,9 @@ rend_service_load_keys(rend_service_t *s)
char *fname = NULL;
char buf[128];
- if (rend_service_check_private_dir(get_options(), s, 1) < 0)
+ /* Make sure the directory was created and single onion poisoning was
+ * checked before calling this function */
+ if (BUG(rend_service_check_private_dir(get_options(), s, 0) < 0))
goto err;
/* Load key */
@@ -2984,6 +3160,57 @@ count_intro_point_circuits(const rend_service_t *service)
return num_ipos;
}
+/* Given a buffer of at least RELAY_PAYLOAD_SIZE bytes in <b>cell_body_out</b>,
+ write the body of a legacy ESTABLISH_INTRO cell in it. Use <b>intro_key</b>
+ as the intro point auth key, and <b>rend_circ_nonce</b> as the circuit
+ crypto material. On success, fill <b>cell_body_out</b> and return the number
+ of bytes written. On fail, return -1.
+ */
+STATIC ssize_t
+encode_establish_intro_cell_legacy(char *cell_body_out, crypto_pk_t *intro_key,
+ char *rend_circ_nonce)
+{
+ int retval = -1;
+ int r;
+ int len = 0;
+ char auth[DIGEST_LEN + 9];
+
+ tor_assert(intro_key);
+ tor_assert(rend_circ_nonce);
+
+ /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
+ r = crypto_pk_asn1_encode(intro_key, cell_body_out+2,
+ RELAY_PAYLOAD_SIZE-2);
+ if (r < 0) {
+ log_warn(LD_BUG, "Internal error; failed to establish intro point.");
+ goto err;
+ }
+ len = r;
+ set_uint16(cell_body_out, htons((uint16_t)len));
+ len += 2;
+ memcpy(auth, rend_circ_nonce, DIGEST_LEN);
+ memcpy(auth+DIGEST_LEN, "INTRODUCE", 9);
+ if (crypto_digest(cell_body_out+len, auth, DIGEST_LEN+9))
+ goto err;
+ len += 20;
+ note_crypto_pk_op(REND_SERVER);
+ r = crypto_pk_private_sign_digest(intro_key, cell_body_out+len,
+ sizeof(cell_body_out)-len,
+ cell_body_out, len);
+ if (r<0) {
+ log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
+ goto err;
+ }
+ len += r;
+
+ retval = len;
+
+ err:
+ memwipe(auth, 0, sizeof(auth));
+
+ return retval;
+}
+
/** Called when we're done building a circuit to an introduction point:
* sends a RELAY_ESTABLISH_INTRO cell.
*/
@@ -2991,10 +3218,7 @@ void
rend_service_intro_has_opened(origin_circuit_t *circuit)
{
rend_service_t *service;
- size_t len;
- int r;
char buf[RELAY_PAYLOAD_SIZE];
- char auth[DIGEST_LEN + 9];
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
int reason = END_CIRC_REASON_TORPROTOCOL;
const char *rend_pk_digest;
@@ -3069,41 +3293,24 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
(unsigned)circuit->base_.n_circ_id, serviceid);
circuit_log_path(LOG_INFO, LD_REND, circuit);
- /* Use the intro key instead of the service key in ESTABLISH_INTRO. */
- crypto_pk_t *intro_key = circuit->intro_key;
- /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
- r = crypto_pk_asn1_encode(intro_key, buf+2,
- RELAY_PAYLOAD_SIZE-2);
- if (r < 0) {
- log_warn(LD_BUG, "Internal error; failed to establish intro point.");
- reason = END_CIRC_REASON_INTERNAL;
- goto err;
- }
- len = r;
- set_uint16(buf, htons((uint16_t)len));
- len += 2;
- memcpy(auth, circuit->cpath->prev->rend_circ_nonce, DIGEST_LEN);
- memcpy(auth+DIGEST_LEN, "INTRODUCE", 9);
- if (crypto_digest(buf+len, auth, DIGEST_LEN+9))
- goto err;
- len += 20;
- note_crypto_pk_op(REND_SERVER);
- r = crypto_pk_private_sign_digest(intro_key, buf+len, sizeof(buf)-len,
- buf, len);
- if (r<0) {
- log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
- reason = END_CIRC_REASON_INTERNAL;
- goto err;
- }
- len += r;
+ /* Send the ESTABLISH_INTRO cell */
+ {
+ ssize_t len;
+ len = encode_establish_intro_cell_legacy(buf, circuit->intro_key,
+ circuit->cpath->prev->rend_circ_nonce);
+ if (len < 0) {
+ reason = END_CIRC_REASON_INTERNAL;
+ goto err;
+ }
- if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
- RELAY_COMMAND_ESTABLISH_INTRO,
- buf, len, circuit->cpath->prev)<0) {
- log_info(LD_GENERAL,
+ if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
+ RELAY_COMMAND_ESTABLISH_INTRO,
+ buf, len, circuit->cpath->prev)<0) {
+ log_info(LD_GENERAL,
"Couldn't send introduction request for service %s on circuit %u",
serviceid, (unsigned)circuit->base_.n_circ_id);
- goto done;
+ goto done;
+ }
}
/* We've attempted to use this circuit */
@@ -3115,7 +3322,6 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
circuit_mark_for_close(TO_CIRCUIT(circuit), reason);
done:
memwipe(buf, 0, sizeof(buf));
- memwipe(auth, 0, sizeof(auth));
memwipe(serviceid, 0, sizeof(serviceid));
return;
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 630191e8b7..4e6b9a2536 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -119,7 +119,19 @@ typedef struct rend_service_t {
STATIC void rend_service_free(rend_service_t *service);
STATIC char *rend_service_sos_poison_path(const rend_service_t *service);
-
+STATIC int rend_service_check_dir_and_add(smartlist_t *service_list,
+ const or_options_t *options,
+ rend_service_t *service,
+ int validate_only);
+STATIC int rend_service_verify_single_onion_poison(
+ const rend_service_t *s,
+ const or_options_t *options);
+STATIC int rend_service_poison_new_single_onion_dir(
+ const rend_service_t *s,
+ const or_options_t* options);
+STATIC ssize_t encode_establish_intro_cell_legacy(char *cell_body_out,
+ crypto_pk_t *intro_key,
+ char *rend_circ_nonce);
#endif
int num_rend_services(void);
@@ -165,11 +177,6 @@ void rend_service_port_config_free(rend_service_port_config_t *p);
void rend_authorized_client_free(rend_authorized_client_t *client);
-int rend_service_list_verify_single_onion_poison(
- const smartlist_t *service_list,
- const or_options_t *options);
-int rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list);
-
/** Return value from rend_service_add_ephemeral. */
typedef enum {
RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
diff --git a/src/or/router.c b/src/or/router.c
index fd2942ec67..2d8208aa04 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -1178,9 +1178,9 @@ router_should_be_directory_server(const or_options_t *options, int dir_port)
if (accounting_is_enabled(options) &&
get_options()->AccountingRule != ACCT_IN) {
/* Don't spend bytes for directory traffic if we could end up hibernating,
- * but allow DirPort otherwise. Some people set AccountingMax because
- * they're confused or to get statistics. Directory traffic has a much
- * larger effect on output than input so there is no reason to turn it
+ * but allow DirPort otherwise. Some relay operators set AccountingMax
+ * because they're confused or to get statistics. Directory traffic has a
+ * much larger effect on output than input so there is no reason to turn it
* off if using AccountingRule in. */
int interval_length = accounting_get_interval_length();
uint32_t effective_bw = get_effective_bwrate(options);
@@ -1312,8 +1312,15 @@ extend_info_from_router(const routerinfo_t *r)
/* Make sure we don't need to check address reachability */
tor_assert_nonfatal(router_skip_or_reachability(get_options(), 0));
+ const ed25519_public_key_t *ed_id_key;
+ if (r->cache_info.signing_key_cert)
+ ed_id_key = &r->cache_info.signing_key_cert->signing_key;
+ else
+ ed_id_key = NULL;
+
router_get_prim_orport(r, &ap);
return extend_info_new(r->nickname, r->cache_info.identity_digest,
+ ed_id_key,
r->onion_pkey, r->onion_curve25519_pkey,
&ap.addr, ap.port);
}
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index 8d9a1328b8..51802b15e5 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -742,8 +742,12 @@ load_ed_keys(const or_options_t *options, time_t now)
if (need_new_signing_key) {
log_notice(LD_OR, "It looks like I need to generate and sign a new "
- "medium-term signing key, because %s. To do that, I need to "
- "load%s the permanent master identity key.",
+ "medium-term signing key, because %s. To do that, I "
+ "need to load%s the permanent master identity key. "
+ "If the master identity key was not moved or encrypted "
+ "with a passphrase, this will be done automatically and "
+ "no further action is required. Otherwise, provide the "
+ "necessary data using 'tor --keygen' to do it manually.",
(NULL == use_signing) ? "I don't have one" :
EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" :
"you asked me to make one with --keygen",
@@ -751,15 +755,19 @@ load_ed_keys(const or_options_t *options, time_t now)
} else if (want_new_signing_key && !offline_master) {
log_notice(LD_OR, "It looks like I should try to generate and sign a "
"new medium-term signing key, because the one I have is "
- "going to expire soon. To do that, I'm going to have to try to "
- "load the permanent master identity key.");
+ "going to expire soon. To do that, I'm going to have to "
+ "try to load the permanent master identity key. "
+ "If the master identity key was not moved or encrypted "
+ "with a passphrase, this will be done automatically and "
+ "no further action is required. Otherwise, provide the "
+ "necessary data using 'tor --keygen' to do it manually.");
} else if (want_new_signing_key) {
log_notice(LD_OR, "It looks like I should try to generate and sign a "
"new medium-term signing key, because the one I have is "
"going to expire soon. But OfflineMasterKey is set, so I "
- "won't try to load a permanent master identity key is set. "
- "You will need to use 'tor --keygen' make a new signing key "
- "and certificate.");
+ "won't try to load a permanent master identity key. You "
+ "will need to use 'tor --keygen' to make a new signing "
+ "key and certificate.");
}
{
@@ -1091,6 +1099,14 @@ get_master_identity_key(void)
return &master_identity_key->pubkey;
}
+/** Return true iff <b>id</b> is our Ed25519 master identity key. */
+int
+router_ed25519_id_is_me(const ed25519_public_key_t *id)
+{
+ return id && master_identity_key &&
+ ed25519_pubkey_eq(id, &master_identity_key->pubkey);
+}
+
#ifdef TOR_UNIT_TESTS
/* only exists for the unit tests, since otherwise the identity key
* should be used to sign nothing but the signing key. */
diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h
index 307a1cd234..98894cdc0b 100644
--- a/src/or/routerkeys.h
+++ b/src/or/routerkeys.h
@@ -45,6 +45,8 @@ const struct tor_cert_st *get_current_auth_key_cert(void);
void get_master_rsa_crosscert(const uint8_t **cert_out,
size_t *size_out);
+int router_ed25519_id_is_me(const ed25519_public_key_t *id);
+
struct tor_cert_st *make_ntor_onion_key_crosscert(
const curve25519_keypair_t *onion_key,
const ed25519_public_key_t *master_id_key,
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index cfa570c4a0..fb8056c67b 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -587,7 +587,7 @@ trusted_dirs_load_certs_from_string(const char *contents, int source,
"signing key %s", from_store ? "cached" : "downloaded",
ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN));
} else {
- int adding = directory_caches_unknown_auth_certs(get_options());
+ int adding = we_want_to_fetch_unknown_auth_certs(get_options());
log_info(LD_DIR, "%s %s certificate for unrecognized directory "
"authority with signing key %s",
adding ? "Adding" : "Not adding",
@@ -1013,7 +1013,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
char *resource = NULL;
cert_list_t *cl;
const or_options_t *options = get_options();
- const int cache = directory_caches_unknown_auth_certs(options);
+ const int keep_unknown = we_want_to_fetch_unknown_auth_certs(options);
fp_pair_t *fp_tmp = NULL;
char id_digest_str[2*DIGEST_LEN+1];
char sk_digest_str[2*DIGEST_LEN+1];
@@ -1085,9 +1085,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now,
if (!smartlist_len(voter->sigs))
continue; /* This authority never signed this consensus, so don't
* go looking for a cert with key digest 0000000000. */
- if (!cache &&
+ if (!keep_unknown &&
!trusteddirserver_get_by_v3_auth_digest(voter->identity_digest))
- continue; /* We are not a cache, and we don't know this authority.*/
+ continue; /* We don't want unknown certs, and we don't know this
+ * authority.*/
/*
* If we don't know *any* cert for this authority, and a download by ID
@@ -2997,20 +2998,6 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type)
return 0;
}
-/** Return true iff <b>addr</b> is the address of one of our trusted
- * directory authorities. */
-int
-router_addr_is_trusted_dir(uint32_t addr)
-{
- if (!trusted_dir_servers)
- return 0;
- SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ent,
- if (ent->addr == addr)
- return 1;
- );
- return 0;
-}
-
/** If hexdigest is correctly formed, base16_decode it into
* digest, which must have DIGEST_LEN space in it.
* Return 0 on success, -1 on failure.
@@ -3243,6 +3230,17 @@ signed_descriptor_free(signed_descriptor_t *sd)
tor_free(sd);
}
+/** Reset the given signed descriptor <b>sd</b> by freeing the allocated
+ * memory inside the object and by zeroing its content. */
+static void
+signed_descriptor_reset(signed_descriptor_t *sd)
+{
+ tor_assert(sd);
+ tor_free(sd->signed_descriptor_body);
+ tor_cert_free(sd->signing_key_cert);
+ memset(sd, 0, sizeof(*sd));
+}
+
/** Copy src into dest, and steal all references inside src so that when
* we free src, we don't mess up dest. */
static void
@@ -3250,6 +3248,8 @@ signed_descriptor_move(signed_descriptor_t *dest,
signed_descriptor_t *src)
{
tor_assert(dest != src);
+ /* Cleanup destination object before overwriting it.*/
+ signed_descriptor_reset(dest);
memcpy(dest, src, sizeof(signed_descriptor_t));
src->signed_descriptor_body = NULL;
src->signing_key_cert = NULL;
@@ -3902,7 +3902,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
router_describe(router));
*msg = "Router descriptor is not referenced by any network-status.";
- /* Only journal this desc if we'll be serving it. */
+ /* Only journal this desc if we want to keep old descriptors */
if (!from_cache && should_cache_old_descriptors())
signed_desc_append_to_journal(&router->cache_info,
&routerlist->desc_store);
@@ -4489,7 +4489,7 @@ router_load_extrainfo_from_string(const char *s, const char *eos,
ei->cache_info.identity_digest,
DIGEST_LEN);
smartlist_string_remove(requested_fingerprints, fp);
- /* We silently let people stuff us with extrainfos we didn't ask for,
+ /* We silently let relays stuff us with extrainfos we didn't ask for,
* so long as we would have wanted them anyway. Since we always fetch
* all the extrainfos we want, and we never actually act on them
* inside Tor, this should be harmless. */
@@ -4532,13 +4532,14 @@ router_load_extrainfo_from_string(const char *s, const char *eos,
smartlist_free(extrainfo_list);
}
-/** Return true iff any networkstatus includes a descriptor whose digest
- * is that of <b>desc</b>. */
+/** Return true iff the latest ns-flavored consensus includes a descriptor
+ * whose digest is that of <b>desc</b>. */
static int
signed_desc_digest_is_recognized(signed_descriptor_t *desc)
{
const routerstatus_t *rs;
- networkstatus_t *consensus = networkstatus_get_latest_consensus();
+ networkstatus_t *consensus = networkstatus_get_latest_consensus_by_flavor(
+ FLAV_NS);
if (consensus) {
rs = networkstatus_vote_find_entry(consensus, desc->identity_digest);
@@ -5161,7 +5162,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
++n_would_reject;
continue; /* We would throw it out immediately. */
}
- if (!directory_caches_dir_info(options) &&
+ if (!we_want_to_fetch_flavor(options, consensus->flavor) &&
!client_would_use_router(rs, now, options)) {
++n_wouldnt_use;
continue; /* We would never use it ourself. */
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 606e9085ce..8b68d69f28 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -86,7 +86,6 @@ int router_digest_is_trusted_dir_type(const char *digest,
#define router_digest_is_trusted_dir(d) \
router_digest_is_trusted_dir_type((d), NO_DIRINFO)
-int router_addr_is_trusted_dir(uint32_t addr);
int hexdigest_to_digest(const char *hexdigest, char *digest);
const routerinfo_t *router_get_by_id_digest(const char *digest);
routerinfo_t *router_get_mutable_by_digest(const char *digest);
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 60fdce0b64..068e226f61 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -370,8 +370,7 @@ static int router_get_hashes_impl(const char *s, size_t s_len,
char end_char);
static smartlist_t *find_all_exitpolicy(smartlist_t *s);
-#define CST_CHECK_AUTHORITY (1<<0)
-#define CST_NO_CHECK_OBJTYPE (1<<1)
+#define CST_NO_CHECK_OBJTYPE (1<<0)
static int check_signature_token(const char *digest,
ssize_t digest_len,
directory_token_t *tok,
@@ -715,7 +714,7 @@ dump_desc_populate_one_file, (const char *dirname, const char *f))
* filename.
*/
if (crypto_digest256((char *)content_digest, desc, (size_t) st.st_size,
- DIGEST_SHA256) != 0) {
+ DIGEST_SHA256) < 0) {
/* Weird, but okay */
log_info(LD_DIR,
"Unable to hash content of %s from unparseable descriptors "
@@ -879,7 +878,7 @@ dump_desc(const char *desc, const char *type)
/* Get the hash for logging purposes anyway */
len = strlen(desc);
if (crypto_digest256((char *)digest_sha256, desc, len,
- DIGEST_SHA256) != 0) {
+ DIGEST_SHA256) < 0) {
log_info(LD_DIR,
"Unable to parse descriptor of type %s, and unable to even hash"
" it!", type);
@@ -1173,28 +1172,9 @@ tor_version_is_obsolete(const char *myversion, const char *versionlist)
return ret;
}
-/** Return true iff <b>key</b> is allowed to sign directories.
- */
-static int
-dir_signing_key_is_trusted(crypto_pk_t *key)
-{
- char digest[DIGEST_LEN];
- if (!key) return 0;
- if (crypto_pk_get_digest(key, digest) < 0) {
- log_warn(LD_DIR, "Error computing dir-signing-key digest");
- return 0;
- }
- if (!router_digest_is_trusted_dir(digest)) {
- log_warn(LD_DIR, "Listed dir-signing-key is not trusted");
- return 0;
- }
- return 1;
-}
-
/** Check whether the object body of the token in <b>tok</b> has a good
- * signature for <b>digest</b> using key <b>pkey</b>. If
- * <b>CST_CHECK_AUTHORITY</b> is set, make sure that <b>pkey</b> is the key of
- * a directory authority. If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check
+ * signature for <b>digest</b> using key <b>pkey</b>.
+ * If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check
* the object type of the signature object. Use <b>doctype</b> as the type of
* the document when generating log messages. Return 0 on success, negative
* on failure.
@@ -1209,7 +1189,6 @@ check_signature_token(const char *digest,
{
char *signed_digest;
size_t keysize;
- const int check_authority = (flags & CST_CHECK_AUTHORITY);
const int check_objtype = ! (flags & CST_NO_CHECK_OBJTYPE);
tor_assert(pkey);
@@ -1217,12 +1196,6 @@ check_signature_token(const char *digest,
tor_assert(digest);
tor_assert(doctype);
- if (check_authority && !dir_signing_key_is_trusted(pkey)) {
- log_warn(LD_DIR, "Key on %s did not come from an authority; rejecting",
- doctype);
- return -1;
- }
-
if (check_objtype) {
if (strcmp(tok->object_type, "SIGNATURE")) {
log_warn(LD_DIR, "Bad object type on %s signature", doctype);
@@ -4536,12 +4509,12 @@ router_get_hash_impl(const char *s, size_t s_len, char *digest,
return -1;
if (alg == DIGEST_SHA1) {
- if (crypto_digest(digest, start, end-start)) {
+ if (crypto_digest(digest, start, end-start) < 0) {
log_warn(LD_BUG,"couldn't compute digest");
return -1;
}
} else {
- if (crypto_digest256(digest, start, end-start, alg)) {
+ if (crypto_digest256(digest, start, end-start, alg) < 0) {
log_warn(LD_BUG,"couldn't compute digest");
return -1;
}
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 5f6b03f1ba..0eb93382ca 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -192,7 +192,7 @@ verify_commit_and_reveal(const sr_commit_t *commit)
/* Use the invariant length since the encoded reveal variable has an
* extra byte for the NUL terminated byte. */
if (crypto_digest256(received_hashed_reveal, commit->encoded_reveal,
- SR_REVEAL_BASE64_LEN, commit->alg)) {
+ SR_REVEAL_BASE64_LEN, commit->alg) < 0) {
/* Unable to digest the reveal blob, this is unlikely. */
goto invalid;
}
@@ -932,7 +932,7 @@ sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert)
/* The invariant length is used here since the encoded reveal variable
* has an extra byte added for the NULL terminated byte. */
if (crypto_digest256(commit->hashed_reveal, commit->encoded_reveal,
- SR_REVEAL_BASE64_LEN, commit->alg)) {
+ SR_REVEAL_BASE64_LEN, commit->alg) < 0) {
goto error;
}
@@ -1012,7 +1012,7 @@ sr_compute_srv(void)
SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
smartlist_free(chunks);
if (crypto_digest256(hashed_reveals, reveals, strlen(reveals),
- SR_DIGEST_ALG)) {
+ SR_DIGEST_ALG) < 0) {
goto end;
}
current_srv = generate_srv(hashed_reveals, reveal_num,
diff --git a/src/or/torcert.c b/src/or/torcert.c
index 6bc880a89b..c58f3da2d3 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -648,3 +648,44 @@ or_handshake_certs_check_both(int severity,
}
}
+/* === ENCODING === */
+
+/* Encode the ed25519 certificate <b>cert</b> and put the newly allocated
+ * string in <b>cert_str_out</b>. Return 0 on success else a negative value. */
+int
+tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out)
+{
+ int ret = -1;
+ char *ed_cert_b64 = NULL;
+ size_t ed_cert_b64_len;
+
+ tor_assert(cert);
+ tor_assert(cert_str_out);
+
+ /* Get the encoded size and add the NUL byte. */
+ ed_cert_b64_len = base64_encode_size(cert->encoded_len,
+ BASE64_ENCODE_MULTILINE) + 1;
+ ed_cert_b64 = tor_malloc_zero(ed_cert_b64_len);
+
+ /* Base64 encode the encoded certificate. */
+ if (base64_encode(ed_cert_b64, ed_cert_b64_len,
+ (const char *) cert->encoded, cert->encoded_len,
+ BASE64_ENCODE_MULTILINE) < 0) {
+ log_err(LD_BUG, "Couldn't base64-encode ed22519 cert!");
+ goto err;
+ }
+
+ /* Put everything together in a NUL terminated string. */
+ tor_asprintf(cert_str_out,
+ "-----BEGIN ED25519 CERT-----\n"
+ "%s"
+ "-----END ED25519 CERT-----",
+ ed_cert_b64);
+ /* Success! */
+ ret = 0;
+
+ err:
+ tor_free(ed_cert_b64);
+ return ret;
+}
+
diff --git a/src/or/torcert.h b/src/or/torcert.h
index 4bd816f4a4..090f6b5811 100644
--- a/src/or/torcert.h
+++ b/src/or/torcert.h
@@ -98,5 +98,7 @@ void or_handshake_certs_check_both(int severity,
const ed25519_public_key_t **ed_id_out,
const common_digests_t **rsa_id_out);
+int tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out);
+
#endif