summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-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/circuitstats.c2
-rw-r--r--src/or/circuituse.c201
-rw-r--r--src/or/circuituse.h20
-rw-r--r--src/or/config.c43
-rw-r--r--src/or/connection.c15
-rw-r--r--src/or/connection_edge.c10
-rw-r--r--src/or/connection_or.c299
-rw-r--r--src/or/connection_or.h5
-rw-r--r--src/or/control.c4
-rw-r--r--src/or/directory.c37
-rw-r--r--src/or/dirserv.c61
-rw-r--r--src/or/dirserv.h3
-rw-r--r--src/or/dnsserv.c2
-rw-r--r--src/or/entrynodes.c33
-rw-r--r--src/or/entrynodes.h3
-rw-r--r--src/or/hibernate.c6
-rw-r--r--src/or/hs_descriptor.c59
-rw-r--r--src/or/hs_descriptor.h1
-rw-r--r--src/or/include.am2
-rw-r--r--src/or/main.c4
-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.h23
-rw-r--r--src/or/protover.c2
-rw-r--r--src/or/rendservice.c490
-rw-r--r--src/or/rendservice.h15
-rw-r--r--src/or/router.c52
-rw-r--r--src/or/router.h3
-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.c50
-rw-r--r--src/or/torcert.h2
-rw-r--r--src/or/transports.c2
46 files changed, 1604 insertions, 770 deletions
diff --git a/src/or/channel.c b/src/or/channel.c
index af5810788c..7984558b40 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)
@@ -3296,7 +3336,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)
@@ -3309,14 +3350,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;
@@ -3327,6 +3368,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
@@ -4498,6 +4544,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 9fb309d0fd..aef0143c9d 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -174,7 +174,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_);
@@ -1652,9 +1651,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);
}
}
@@ -1993,12 +1993,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,
@@ -2334,6 +2337,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,
@@ -2342,7 +2352,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 0881f231aa..9bd0125972 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -63,8 +63,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);
@@ -80,13 +81,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;
@@ -556,6 +556,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);
@@ -573,7 +574,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;
@@ -1041,6 +1043,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,
@@ -1159,7 +1164,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. */
@@ -1169,6 +1174,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. */
@@ -1180,7 +1197,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);
@@ -1192,8 +1219,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);
@@ -1206,7 +1234,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);
@@ -1508,9 +1537,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;
@@ -2356,19 +2385,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;
@@ -2418,20 +2451,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 1244601f71..54d14bbc7f 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -40,15 +40,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/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 ba7b75ff25..e109cef5dd 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1022,8 +1022,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
@@ -1035,25 +1144,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)
@@ -1063,19 +1161,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);
@@ -1083,12 +1176,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.",
@@ -1097,18 +1188,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.",
@@ -1117,26 +1206,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;
- }
}
}
@@ -2088,6 +2168,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;
@@ -2102,9 +2186,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. */
@@ -2511,7 +2598,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 5973978c45..d484be1986 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -59,5 +59,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 9553822ba3..c68f83ab69 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -218,7 +218,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"),
@@ -305,6 +305,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),
V(UseDefaultFallbackDirs, BOOL, "1"),
@@ -497,6 +498,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"),
@@ -592,7 +594,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,
@@ -1778,25 +1779,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");
@@ -1916,7 +1898,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. */
@@ -2940,21 +2922,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;
}
@@ -3087,7 +3054,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;
diff --git a/src/or/connection.c b/src/or/connection.c
index 2e3df34a5a..ac3408a72e 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -644,7 +644,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));
@@ -675,7 +675,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) {
@@ -1595,16 +1595,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 875c911f01..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(
@@ -1627,11 +1627,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
tor_assert(!automap);
rep_hist_note_used_resolve(now); /* help predict this next time */
- }
+ } else if (socks->command == SOCKS_COMMAND_CONNECT) {
+ /* Now see if this is a connect request that we can reject immediately */
- /* Now see if this is a connect request that we can reject immediately */
- if (socks->command == SOCKS_COMMAND_CONNECT) {
- /* Special handling for attempts to connect */
tor_assert(!automap);
/* Don't allow connections to port 0. */
if (socks->port == 0) {
@@ -1771,7 +1769,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
rep_hist_note_used_resolve(now); /* help predict this next time */
/* no extra processing needed */
} else {
- /* We should only be doing CONNECT or RESOLVE! */
+ /* We should only be doing CONNECT, RESOLVE, or RESOLVE_PTR! */
tor_fragile_assert();
}
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index eb67f0653f..635d3e416a 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -75,56 +75,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)
{
@@ -132,60 +101,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
@@ -883,14 +864,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);
@@ -912,10 +923,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,
@@ -961,7 +974,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().
*
@@ -978,16 +991,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;
@@ -1011,11 +1027,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. */
@@ -1042,7 +1058,7 @@ connection_or_group_set_badness(or_connection_t *head, int force)
0)) {
best = or_conn;
}
- }
+ } SMARTLIST_FOREACH_END(or_conn);
if (!best)
return;
@@ -1061,7 +1077,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)
@@ -1095,24 +1111,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
@@ -1182,7 +1181,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;
@@ -1201,6 +1199,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));
@@ -1570,20 +1573,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.
@@ -1607,11 +1615,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);
@@ -1625,16 +1648,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());
@@ -1669,9 +1715,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);
+
entry_guard_register_connect_status(conn->identity_digest, 0, 1,
time(NULL));
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
@@ -1683,9 +1731,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 96cc41bc4b..a22113174a 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1705,7 +1705,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
*answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
} else if (!strcmp(question, "address")) {
uint32_t addr;
- if (router_pick_published_address(get_options(), &addr) < 0) {
+ if (router_pick_published_address(get_options(), &addr, 0) < 0) {
*errmsg = "Address unknown";
return -1;
}
@@ -2029,7 +2029,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 65ddd7d583..84623593ab 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2939,6 +2939,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
@@ -2983,6 +3005,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 "
@@ -3988,10 +4019,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 c797c9aa0e..4d349ddf16 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -1033,7 +1033,8 @@ directory_fetches_from_authorities(const or_options_t *options)
return 1;
if (options->BridgeRelay == 1)
return 0;
- if (server_mode(options) && router_pick_published_address(options, &addr)<0)
+ if (server_mode(options) &&
+ router_pick_published_address(options, &addr, 1) < 0)
return 1; /* we don't know our IP address; ask an authority. */
refuseunknown = ! router_my_exit_policy_is_reject_star() &&
should_refuse_unknown_exits(options);
@@ -1068,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)
@@ -1077,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)
@@ -1096,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
@@ -2050,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_);
@@ -2068,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);
@@ -2230,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)
@@ -3170,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;
@@ -3182,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)) {
@@ -3239,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. */
@@ -3268,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 b3fa31df7b..af1b1a39ab 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -15,13 +15,13 @@
#define ENTRYNODES_PRIVATE
#include "or.h"
+#include "channel.h"
#include "circpathbias.h"
#include "circuitbuild.h"
#include "circuitstats.h"
#include "config.h"
#include "confparse.h"
#include "connection.h"
-#include "connection_or.h"
#include "control.h"
#include "directory.h"
#include "entrynodes.h"
@@ -2108,18 +2108,34 @@ 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 : "");
@@ -2216,6 +2232,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),
@@ -2306,7 +2324,10 @@ routerset_contains_bridge(const routerset_t *routerset,
return 0;
extinfo = extend_info_new(
- NULL, bridge->identity, NULL, NULL, &bridge->addr, bridge->port);
+ NULL, bridge->identity,
+ NULL, /* Ed25519 ID */
+ NULL, NULL, /* onion keys */
+ &bridge->addr, bridge->port);
result = routerset_contains_extendinfo(routerset, extinfo);
extend_info_free(extinfo);
return result;
@@ -2746,7 +2767,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/entrynodes.h b/src/or/entrynodes.h
index 00f96916b6..f8aaedf171 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -167,7 +167,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);
struct bridge_line_t;
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/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_descriptor.c b/src/or/hs_descriptor.c
index 1517ccb12e..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;
@@ -1394,15 +1356,10 @@ decode_intro_points(const hs_descriptor_t *desc,
retval = 0;
err:
- if (chunked_desc) {
- SMARTLIST_FOREACH(chunked_desc, char *, a, tor_free(a));
- smartlist_free(chunked_desc);
- }
- if (intro_points) {
- SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a));
- smartlist_free(intro_points);
- }
-
+ SMARTLIST_FOREACH(chunked_desc, char *, a, tor_free(a));
+ smartlist_free(chunked_desc);
+ SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a));
+ smartlist_free(intro_points);
return retval;
}
/* Return 1 iff the given base64 encoded signature in b64_sig from the encoded
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/include.am b/src/or/include.am
index 38777ccc6d..c0ab0bc3e1 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -212,7 +212,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 ff477dba5b..327b768842 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -363,7 +363,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
@@ -1427,7 +1427,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);
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 316ce48387..fa548305f9 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -814,8 +814,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) {
@@ -837,6 +840,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)
@@ -1351,6 +1377,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. */
@@ -1359,12 +1403,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;
@@ -1728,9 +1771,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;
}
@@ -1932,7 +1975,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,
@@ -2381,9 +2424,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 71f36b69ed..d584c2ecfc 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 10cfc76856..f63fe06a40 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1582,8 +1582,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;
@@ -1661,6 +1659,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
@@ -2711,7 +2711,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. */
@@ -3872,7 +3875,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
@@ -3958,9 +3961,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? */
@@ -4562,6 +4562,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;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
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/rendservice.c b/src/or/rendservice.c
index e483a498e4..9a39090ac2 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 */
@@ -344,9 +399,9 @@ rend_service_port_config_new(const char *socket_path)
return conf;
}
-/** Parses a real-port to virtual-port mapping separated by the provided
- * separator and returns a new rend_service_port_config_t, or NULL and an
- * optional error string on failure.
+/** Parses a virtual-port to real-port/socket mapping separated by
+ * the provided separator and returns a new rend_service_port_config_t,
+ * or NULL and an optional error string on failure.
*
* The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)?
*
@@ -371,14 +426,12 @@ rend_service_parse_port_config(const char *string, const char *sep,
smartlist_split_string(sl, string, sep,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
if (smartlist_len(sl) < 1 || BUG(smartlist_len(sl) > 2)) {
- if (err_msg_out)
- err_msg = tor_strdup("Bad syntax in hidden service port configuration.");
+ err_msg = tor_strdup("Bad syntax in hidden service port configuration.");
goto err;
}
virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL);
if (!virtport) {
- if (err_msg_out)
- tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service "
+ tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service "
"port configuration", escaped(smartlist_get(sl,0)));
goto err;
@@ -406,10 +459,8 @@ rend_service_parse_port_config(const char *string, const char *sep,
} else if (strchr(addrport, ':') || strchr(addrport, '.')) {
/* else try it as an IP:port pair if it has a : or . in it */
if (tor_addr_port_lookup(addrport, &addr, &p)<0) {
- if (err_msg_out)
- err_msg = tor_strdup("Unparseable address in hidden service port "
- "configuration.");
-
+ err_msg = tor_strdup("Unparseable address in hidden service port "
+ "configuration.");
goto err;
}
realport = p?p:virtport;
@@ -417,11 +468,9 @@ rend_service_parse_port_config(const char *string, const char *sep,
/* No addr:port, no addr -- must be port. */
realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL);
if (!realport) {
- if (err_msg_out)
- tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in "
- "hidden service port configuration.",
- escaped(addrport));
-
+ tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in "
+ "hidden service port configuration.",
+ escaped(addrport));
goto err;
}
tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */
@@ -440,7 +489,11 @@ rend_service_parse_port_config(const char *string, const char *sep,
err:
tor_free(addrport);
- if (err_msg_out) *err_msg_out = err_msg;
+ if (err_msg_out != NULL) {
+ *err_msg_out = err_msg;
+ } else {
+ tor_free(err_msg);
+ }
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
@@ -454,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
@@ -466,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);
@@ -695,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. */
@@ -729,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);
@@ -741,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);
@@ -861,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);
@@ -1011,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;
}
@@ -1044,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;
+ }
- return consistent ? 0 : -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;
+ }
+
+ /* 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;
@@ -1113,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);
@@ -1151,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;
}
@@ -1202,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) {
@@ -1272,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:
@@ -1286,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;
}
@@ -1312,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 */
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 8fb3e8f97a..4e6b9a2536 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -119,6 +119,16 @@ 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);
@@ -167,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 79caf42a2a..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);
}
@@ -1961,23 +1968,34 @@ static int router_guess_address_from_dir_headers(uint32_t *guess);
/** Make a current best guess at our address, either because
* it's configured in torrc, or because we've learned it from
* dirserver headers. Place the answer in *<b>addr</b> and return
- * 0 on success, else return -1 if we have no guess. */
+ * 0 on success, else return -1 if we have no guess.
+ *
+ * If <b>cache_only</b> is true, just return any cached answers, and
+ * don't try to get any new answers.
+ */
MOCK_IMPL(int,
-router_pick_published_address,(const or_options_t *options, uint32_t *addr))
+router_pick_published_address,(const or_options_t *options, uint32_t *addr,
+ int cache_only))
{
+ /* First, check the cached output from resolve_my_address(). */
*addr = get_last_resolved_addr();
- if (!*addr &&
- resolve_my_address(LOG_INFO, options, addr, NULL, NULL) < 0) {
- log_info(LD_CONFIG, "Could not determine our address locally. "
- "Checking if directory headers provide any hints.");
- if (router_guess_address_from_dir_headers(addr) < 0) {
- log_info(LD_CONFIG, "No hints from directory headers either. "
- "Will try again later.");
- return -1;
+ if (*addr)
+ return 0;
+
+ /* Second, consider doing a resolve attempt right here. */
+ if (!cache_only) {
+ if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) {
+ log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
+ return 0;
}
}
- log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
- return 0;
+
+ /* Third, check the cached output from router_new_address_suggestion(). */
+ if (router_guess_address_from_dir_headers(addr) >= 0)
+ return 0;
+
+ /* We have no useful cached answers. Return failure. */
+ return -1;
}
/* Like router_check_descriptor_address_consistency, but specifically for the
@@ -2074,7 +2092,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
int hibernating = we_are_hibernating();
const or_options_t *options = get_options();
- if (router_pick_published_address(options, &addr) < 0) {
+ if (router_pick_published_address(options, &addr, 0) < 0) {
log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
return -1;
}
@@ -2323,7 +2341,7 @@ router_rebuild_descriptor(int force)
if (desc_clean_since && !force)
return 0;
- if (router_pick_published_address(options, &addr) < 0 ||
+ if (router_pick_published_address(options, &addr, 0) < 0 ||
router_get_advertised_or_port(options) == 0) {
/* Stop trying to rebuild our descriptor every second. We'll
* learn that it's time to try again when ip_address_changed()
diff --git a/src/or/router.h b/src/or/router.h
index 73bfea1faa..c30a0301b7 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -91,7 +91,8 @@ const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
MOCK_DECL(int,router_pick_published_address,(const or_options_t *options,
- uint32_t *addr));
+ uint32_t *addr,
+ int cache_only));
int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
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 c99d22ed41..69ae51ad4a 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -586,7 +586,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",
@@ -1012,7 +1012,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];
@@ -1084,9 +1084,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
@@ -2990,20 +2991,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.
@@ -3236,6 +3223,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
@@ -3243,6 +3241,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;
@@ -3895,7 +3895,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);
@@ -4482,7 +4482,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. */
@@ -4525,13 +4525,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);
@@ -5154,7 +5155,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 2cfd3fc58a..e3f2186c32 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 852def9ef6..c58f3da2d3 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -156,11 +156,12 @@ tor_cert_parse(const uint8_t *encoded, const size_t len)
cert->encoded_len = len;
memcpy(cert->signed_key.pubkey, parsed->certified_key, 32);
- const int64_t valid_until_64 = ((int64_t)parsed->exp_field) * 3600;
+ int64_t valid_until_64 = ((int64_t)parsed->exp_field) * 3600;
+#if SIZEOF_TIME_T < SIZEOF_INT64_T
if (valid_until_64 > TIME_MAX)
- cert->valid_until = TIME_MAX - 1;
- else
- cert->valid_until = (time_t) valid_until_64;
+ valid_until_64 = TIME_MAX - 1;
+#endif
+ cert->valid_until = (time_t) valid_until_64;
cert->cert_type = parsed->cert_type;
for (unsigned i = 0; i < ed25519_cert_getlen_ext(parsed); ++i) {
@@ -647,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
diff --git a/src/or/transports.c b/src/or/transports.c
index 614b28c168..f755882c16 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -1611,7 +1611,7 @@ pt_get_extra_info_descriptor_string(void)
uint32_t external_ip_address = 0;
if (tor_addr_is_null(&t->addr) &&
router_pick_published_address(get_options(),
- &external_ip_address) >= 0) {
+ &external_ip_address, 0) >= 0) {
tor_addr_t addr;
tor_addr_from_ipv4h(&addr, external_ip_address);
addrport = fmt_addrport(&addr, t->port);