diff options
Diffstat (limited to 'src/feature/relay/router.c')
-rw-r--r-- | src/feature/relay/router.c | 618 |
1 files changed, 453 insertions, 165 deletions
diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 283f7c326b..b8066c67e3 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -16,7 +16,7 @@ #include "core/or/policies.h" #include "core/or/protover.h" #include "feature/client/transports.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dirauth/process_descs.h" #include "feature/dircache/dirserv.h" #include "feature/dirclient/dirclient.h" @@ -30,6 +30,7 @@ #include "feature/nodelist/dirlist.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nickname.h" +#include "feature/nodelist/nodefamily.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" #include "feature/nodelist/torcert.h" @@ -49,6 +50,7 @@ #include "lib/encoding/confline.h" #include "lib/osinfo/uname.h" #include "lib/tls/tortls.h" +#include "lib/version/torversion.h" #include "feature/dirauth/authmode.h" @@ -58,6 +60,7 @@ #include "feature/dircommon/dir_connection_st.h" #include "feature/nodelist/authority_cert_st.h" #include "feature/nodelist/extrainfo_st.h" +#include "feature/nodelist/networkstatus_st.h" #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerstatus_st.h" @@ -149,6 +152,8 @@ routerinfo_err_to_string(int err) return "Cannot generate descriptor"; case TOR_ROUTERINFO_ERROR_DESC_REBUILDING: return "Descriptor still rebuilding - not ready yet"; + case TOR_ROUTERINFO_ERROR_INTERNAL_BUG: + return "Internal bug, see logs for details"; } log_warn(LD_BUG, "unknown routerinfo error %d - shouldn't happen", err); @@ -191,8 +196,8 @@ set_onion_key(crypto_pk_t *k) /** Return the current onion key. Requires that the onion key has been * loaded or generated. */ -crypto_pk_t * -get_onion_key(void) +MOCK_IMPL(crypto_pk_t *, +get_onion_key,(void)) { tor_assert(onionkey); return onionkey; @@ -239,7 +244,7 @@ expire_old_onion_keys(void) lastonionkey = NULL; } - /* We zero out the keypair. See the tor_mem_is_zero() check made in + /* We zero out the keypair. See the fast_mem_is_zero() check made in * construct_ntor_key_map() below. */ memset(&last_curve25519_onion_key, 0, sizeof(last_curve25519_onion_key)); @@ -266,11 +271,12 @@ expire_old_onion_keys(void) /** Return the current secret onion key for the ntor handshake. Must only * be called from the main thread. */ -static const curve25519_keypair_t * -get_current_curve25519_keypair(void) +MOCK_IMPL(STATIC const struct curve25519_keypair_t *, +get_current_curve25519_keypair,(void)) { return &curve25519_onion_key; } + /** Return a map from KEYID (the key itself) to keypairs for use in the ntor * handshake. Must only be called from the main thread. */ di_digest256_map_t * @@ -281,12 +287,12 @@ construct_ntor_key_map(void) const uint8_t *cur_pk = curve25519_onion_key.pubkey.public_key; const uint8_t *last_pk = last_curve25519_onion_key.pubkey.public_key; - if (!tor_mem_is_zero((const char *)cur_pk, CURVE25519_PUBKEY_LEN)) { + if (!fast_mem_is_zero((const char *)cur_pk, CURVE25519_PUBKEY_LEN)) { dimap_add_entry(&m, cur_pk, tor_memdup(&curve25519_onion_key, sizeof(curve25519_keypair_t))); } - if (!tor_mem_is_zero((const char*)last_pk, CURVE25519_PUBKEY_LEN) && + if (!fast_mem_is_zero((const char*)last_pk, CURVE25519_PUBKEY_LEN) && tor_memneq(cur_pk, last_pk, CURVE25519_PUBKEY_LEN)) { dimap_add_entry(&m, last_pk, tor_memdup(&last_curve25519_onion_key, @@ -337,6 +343,16 @@ set_server_identity_key(crypto_pk_t *k) } } +#ifdef TOR_UNIT_TESTS +/** Testing only -- set the server's RSA identity digest to + * be <b>digest</b> */ +void +set_server_identity_key_digest_testing(const uint8_t *digest) +{ + memcpy(server_identitykey_digest, digest, DIGEST_LEN); +} +#endif /* defined(TOR_UNIT_TESTS) */ + /** Make sure that we have set up our identity keys to match or not match as * appropriate, and die with an assertion if we have not. */ static void @@ -359,8 +375,8 @@ assert_identity_keys_ok(void) /** Returns the current server identity key; requires that the key has * been set, and that we are running as a Tor server. */ -crypto_pk_t * -get_server_identity_key(void) +MOCK_IMPL(crypto_pk_t *, +get_server_identity_key,(void)) { tor_assert(server_identitykey); tor_assert(server_mode(get_options())); @@ -634,7 +650,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, fname); goto done; } - parsed = authority_cert_parse_from_string(cert, &eos); + parsed = authority_cert_parse_from_string(cert, strlen(cert), &eos); if (!parsed) { log_warn(LD_DIR, "Unable to parse certificate in %s", fname); goto done; @@ -1031,7 +1047,7 @@ init_keys(void) return -1; keydir = get_keydir_fname("secret_onion_key_ntor.old"); - if (tor_mem_is_zero((const char *) + if (fast_mem_is_zero((const char *) last_curve25519_onion_key.pubkey.public_key, CURVE25519_PUBKEY_LEN) && file_status(keydir) == FN_FILE) { @@ -1511,9 +1527,9 @@ static extrainfo_t *desc_extrainfo = NULL; static const char *desc_gen_reason = "uninitialized reason"; /** Since when has our descriptor been "clean"? 0 if we need to regenerate it * now. */ -static time_t desc_clean_since = 0; +STATIC time_t desc_clean_since = 0; /** Why did we mark the descriptor dirty? */ -static const char *desc_dirty_reason = "Tor just started"; +STATIC const char *desc_dirty_reason = "Tor just started"; /** Boolean: do we need to regenerate the above? */ static int desc_needs_upload = 0; @@ -1732,10 +1748,6 @@ router_get_descriptor_gen_reason(void) return desc_gen_reason; } -/** A list of nicknames that we've warned about including in our family - * declaration verbatim rather than as digests. */ -static smartlist_t *warned_nonexistent_family = NULL; - static int router_guess_address_from_dir_headers(uint32_t *guess); /** Make a current best guess at our address, either because @@ -1848,26 +1860,159 @@ router_check_descriptor_address_consistency(uint32_t ipv4h_desc_addr) CONN_TYPE_DIR_LISTENER); } -/** Build a fresh routerinfo, signed server descriptor, and extra-info document - * for this OR. Set r to the generated routerinfo, e to the generated - * extra-info document. Return 0 on success, -1 on temporary error. Failure to - * generate an extra-info document is not an error and is indicated by setting - * e to NULL. Caller is responsible for freeing generated documents if 0 is - * returned. +/** A list of nicknames that we've warned about including in our family, + * for one reason or another. */ +static smartlist_t *warned_family = NULL; + +/** + * Return a new smartlist containing the family members configured in + * <b>options</b>. Warn about invalid or missing entries. Return NULL + * if this relay should not declare a family. + **/ +STATIC smartlist_t * +get_my_declared_family(const or_options_t *options) +{ + if (!options->MyFamily) + return NULL; + + if (options->BridgeRelay) + return NULL; + + if (!warned_family) + warned_family = smartlist_new(); + + smartlist_t *declared_family = smartlist_new(); + config_line_t *family; + + /* First we try to get the whole family in the form of hexdigests. */ + for (family = options->MyFamily; family; family = family->next) { + char *name = family->value; + const node_t *member; + if (options->Nickname && !strcasecmp(name, options->Nickname)) + continue; /* Don't list ourself by nickname, that's redundant */ + else + member = node_get_by_nickname(name, 0); + + if (!member) { + /* This node doesn't seem to exist, so warn about it if it is not + * a hexdigest. */ + int is_legal = is_legal_nickname_or_hexdigest(name); + if (!smartlist_contains_string(warned_family, name) && + !is_legal_hexdigest(name)) { + if (is_legal) + log_warn(LD_CONFIG, + "There is a router named %s in my declared family, but " + "I have no descriptor for it. I'll use the nickname " + "as is, but this may confuse clients. Please list it " + "by identity digest instead.", escaped(name)); + else + log_warn(LD_CONFIG, "There is a router named %s in my declared " + "family, but that isn't a legal digest or nickname. " + "Skipping it.", escaped(name)); + smartlist_add_strdup(warned_family, name); + } + if (is_legal) { + smartlist_add_strdup(declared_family, name); + } + } else { + /* List the node by digest. */ + char *fp = tor_malloc(HEX_DIGEST_LEN+2); + fp[0] = '$'; + base16_encode(fp+1,HEX_DIGEST_LEN+1, + member->identity, DIGEST_LEN); + smartlist_add(declared_family, fp); + + if (! is_legal_hexdigest(name) && + !smartlist_contains_string(warned_family, name)) { + /* Warn if this node was not specified by hexdigest. */ + log_warn(LD_CONFIG, "There is a router named %s in my declared " + "family, but it wasn't listed by digest. Please consider " + "saying %s instead, if that's what you meant.", + escaped(name), fp); + smartlist_add_strdup(warned_family, name); + } + } + } + + /* Now declared_family should have the closest we can come to the + * identities that the user wanted. + * + * Unlike older versions of Tor, we _do_ include our own identity: this + * helps microdescriptor compression, and helps in-memory compression + * on clients. */ + nodefamily_t *nf = nodefamily_from_members(declared_family, + router_get_my_id_digest(), + NF_WARN_MALFORMED, + NULL); + SMARTLIST_FOREACH(declared_family, char *, s, tor_free(s)); + smartlist_free(declared_family); + if (!nf) { + return NULL; + } + + char *s = nodefamily_format(nf); + nodefamily_free(nf); + + smartlist_t *result = smartlist_new(); + smartlist_split_string(result, s, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tor_free(s); + + if (smartlist_len(result) == 1) { + /* This is a one-element list containing only ourself; instead return + * nothing */ + const char *singleton = smartlist_get(result, 0); + bool is_me = false; + if (singleton[0] == '$') { + char d[DIGEST_LEN]; + int n = base16_decode(d, sizeof(d), singleton+1, strlen(singleton+1)); + if (n == DIGEST_LEN && + fast_memeq(d, router_get_my_id_digest(), DIGEST_LEN)) { + is_me = true; + } + } + if (!is_me) { + // LCOV_EXCL_START + log_warn(LD_BUG, "Found a singleton family list with an element " + "that wasn't us! Element was %s", escaped(singleton)); + // LCOV_EXCL_STOP + } else { + SMARTLIST_FOREACH(result, char *, cp, tor_free(cp)); + smartlist_free(result); + return NULL; + } + } + + return result; +} + +/** Allocate a fresh, unsigned routerinfo for this OR, without any of the + * fields that depend on the corresponding extrainfo. + * + * On success, set ri_out to the new routerinfo, and return 0. + * Caller is responsible for freeing the generated routerinfo. + * + * Returns a negative value and sets ri_out to NULL on temporary error. */ -int -router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) +MOCK_IMPL(STATIC int, +router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)) { - routerinfo_t *ri; - extrainfo_t *ei; + routerinfo_t *ri = NULL; uint32_t addr; char platform[256]; int hibernating = we_are_hibernating(); const or_options_t *options = get_options(); + int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG; + + if (BUG(!ri_out)) { + result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG; + goto err; + } if (router_pick_published_address(options, &addr, 0) < 0) { log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); - return TOR_ROUTERINFO_ERROR_NO_EXT_ADDR; + result = TOR_ROUTERINFO_ERROR_NO_EXT_ADDR; + goto err; } /* Log a message if the address in the descriptor doesn't match the ORPort @@ -1901,8 +2046,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key()); if (BUG(crypto_pk_get_digest(ri->identity_pkey, ri->cache_info.identity_digest) < 0)) { - routerinfo_free(ri); - return TOR_ROUTERINFO_ERROR_DIGEST_FAILED; + result = TOR_ROUTERINFO_ERROR_DIGEST_FAILED; + goto err; } ri->cache_info.signing_key_cert = tor_cert_dup(get_master_signing_key_cert()); @@ -1939,134 +2084,260 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) tor_free(p_tmp); } - if (options->MyFamily && ! options->BridgeRelay) { - if (!warned_nonexistent_family) - warned_nonexistent_family = smartlist_new(); - ri->declared_family = smartlist_new(); - config_line_t *family; - for (family = options->MyFamily; family; family = family->next) { - char *name = family->value; - const node_t *member; - if (!strcasecmp(name, options->Nickname)) - continue; /* Don't list ourself, that's redundant */ - else - member = node_get_by_nickname(name, 0); - if (!member) { - int is_legal = is_legal_nickname_or_hexdigest(name); - if (!smartlist_contains_string(warned_nonexistent_family, name) && - !is_legal_hexdigest(name)) { - if (is_legal) - log_warn(LD_CONFIG, - "I have no descriptor for the router named \"%s\" in my " - "declared family; I'll use the nickname as is, but " - "this may confuse clients.", name); - else - log_warn(LD_CONFIG, "There is a router named \"%s\" in my " - "declared family, but that isn't a legal nickname. " - "Skipping it.", escaped(name)); - smartlist_add_strdup(warned_nonexistent_family, name); - } - if (is_legal) { - smartlist_add_strdup(ri->declared_family, name); - } - } else if (router_digest_is_me(member->identity)) { - /* Don't list ourself in our own family; that's redundant */ - /* XXX shouldn't be possible */ - } else { - char *fp = tor_malloc(HEX_DIGEST_LEN+2); - fp[0] = '$'; - base16_encode(fp+1,HEX_DIGEST_LEN+1, - member->identity, DIGEST_LEN); - smartlist_add(ri->declared_family, fp); - if (smartlist_contains_string(warned_nonexistent_family, name)) - smartlist_string_remove(warned_nonexistent_family, name); - } - } + ri->declared_family = get_my_declared_family(options); - /* remove duplicates from the list */ - smartlist_sort_strings(ri->declared_family); - smartlist_uniq_strings(ri->declared_family); + if (options->BridgeRelay) { + ri->purpose = ROUTER_PURPOSE_BRIDGE; + /* Bridges shouldn't be able to send their descriptors unencrypted, + anyway, since they don't have a DirPort, and always connect to the + bridge authority anonymously. But just in case they somehow think of + sending them on an unencrypted connection, don't allow them to try. */ + ri->cache_info.send_unencrypted = 0; + } else { + ri->purpose = ROUTER_PURPOSE_GENERAL; + ri->cache_info.send_unencrypted = 1; } + goto done; + + err: + routerinfo_free(ri); + *ri_out = NULL; + return result; + + done: + *ri_out = ri; + return 0; +} + +/** Allocate and return a fresh, unsigned extrainfo for this OR, based on the + * routerinfo ri. + * + * Uses options->Nickname to set the nickname, and options->BridgeRelay to set + * ei->cache_info.send_unencrypted. + * + * If ri is NULL, logs a BUG() warning and returns NULL. + * Caller is responsible for freeing the generated extrainfo. + */ +static extrainfo_t * +router_build_fresh_unsigned_extrainfo(const routerinfo_t *ri) +{ + extrainfo_t *ei = NULL; + const or_options_t *options = get_options(); + + if (BUG(!ri)) + return NULL; + /* Now generate the extrainfo. */ ei = tor_malloc_zero(sizeof(extrainfo_t)); ei->cache_info.is_extrainfo = 1; - strlcpy(ei->nickname, get_options()->Nickname, sizeof(ei->nickname)); + strlcpy(ei->nickname, options->Nickname, sizeof(ei->nickname)); ei->cache_info.published_on = ri->cache_info.published_on; ei->cache_info.signing_key_cert = tor_cert_dup(get_master_signing_key_cert()); memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest, DIGEST_LEN); + + if (options->BridgeRelay) { + /* See note in router_build_fresh_routerinfo(). */ + ei->cache_info.send_unencrypted = 0; + } else { + ei->cache_info.send_unencrypted = 1; + } + + return ei; +} + +/** Dump the extrainfo descriptor body for ei, sign it, and add the body and + * signature to ei->cache_info. Note that the extrainfo body is determined by + * ei, and some additional config and statistics state: see + * extrainfo_dump_to_string() for details. + * + * Return 0 on success, -1 on temporary error. + * If ei is NULL, logs a BUG() warning and returns -1. + * On error, ei->cache_info is not modified. + */ +static int +router_dump_and_sign_extrainfo_descriptor_body(extrainfo_t *ei) +{ + if (BUG(!ei)) + return -1; + if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body, ei, get_server_identity_key(), get_master_signing_keypair()) < 0) { log_warn(LD_BUG, "Couldn't generate extra-info descriptor."); - extrainfo_free(ei); - ei = NULL; - } else { - ei->cache_info.signed_descriptor_len = - strlen(ei->cache_info.signed_descriptor_body); - router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body, - ei->cache_info.signed_descriptor_len, - ei->cache_info.signed_descriptor_digest); - crypto_digest256((char*) ei->digest256, - ei->cache_info.signed_descriptor_body, - ei->cache_info.signed_descriptor_len, - DIGEST_SHA256); + return -1; } - /* Now finish the router descriptor. */ - if (ei) { - memcpy(ri->cache_info.extra_info_digest, - ei->cache_info.signed_descriptor_digest, - DIGEST_LEN); - memcpy(ri->cache_info.extra_info_digest256, - ei->digest256, - DIGEST256_LEN); - } else { - /* ri was allocated with tor_malloc_zero, so there is no need to - * zero ri->cache_info.extra_info_digest here. */ + ei->cache_info.signed_descriptor_len = + strlen(ei->cache_info.signed_descriptor_body); + + router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body, + ei->cache_info.signed_descriptor_len, + ei->cache_info.signed_descriptor_digest); + crypto_digest256((char*) ei->digest256, + ei->cache_info.signed_descriptor_body, + ei->cache_info.signed_descriptor_len, + DIGEST_SHA256); + + return 0; +} + +/** Allocate and return a fresh, signed extrainfo for this OR, based on the + * routerinfo ri. + * + * If ri is NULL, logs a BUG() warning and returns NULL. + * Caller is responsible for freeing the generated extrainfo. + */ +STATIC extrainfo_t * +router_build_fresh_signed_extrainfo(const routerinfo_t *ri) +{ + int result = -1; + extrainfo_t *ei = NULL; + + if (BUG(!ri)) + return NULL; + + ei = router_build_fresh_unsigned_extrainfo(ri); + /* router_build_fresh_unsigned_extrainfo() should not fail. */ + if (BUG(!ei)) + goto err; + + result = router_dump_and_sign_extrainfo_descriptor_body(ei); + if (result < 0) + goto err; + + goto done; + + err: + extrainfo_free(ei); + return NULL; + + done: + return ei; +} + +/** Set the fields in ri that depend on ei. + * + * If ei is NULL, logs a BUG() warning and zeroes the relevant fields. + */ +STATIC void +router_update_routerinfo_from_extrainfo(routerinfo_t *ri, + const extrainfo_t *ei) +{ + if (BUG(!ei)) { + /* Just to be safe, zero ri->cache_info.extra_info_digest here. */ + memset(ri->cache_info.extra_info_digest, 0, DIGEST_LEN); + memset(ri->cache_info.extra_info_digest256, 0, DIGEST256_LEN); + return; } + + /* Now finish the router descriptor. */ + memcpy(ri->cache_info.extra_info_digest, + ei->cache_info.signed_descriptor_digest, + DIGEST_LEN); + memcpy(ri->cache_info.extra_info_digest256, + ei->digest256, + DIGEST256_LEN); +} + +/** Dump the descriptor body for ri, sign it, and add the body and signature to + * ri->cache_info. Note that the descriptor body is determined by ri, and some + * additional config and state: see router_dump_router_to_string() for details. + * + * Return 0 on success, and a negative value on temporary error. + * If ri is NULL, logs a BUG() warning and returns a negative value. + * On error, ri->cache_info is not modified. + */ +STATIC int +router_dump_and_sign_routerinfo_descriptor_body(routerinfo_t *ri) +{ + if (BUG(!ri)) + return TOR_ROUTERINFO_ERROR_INTERNAL_BUG; + if (! (ri->cache_info.signed_descriptor_body = router_dump_router_to_string(ri, get_server_identity_key(), get_onion_key(), get_current_curve25519_keypair(), get_master_signing_keypair())) ) { log_warn(LD_BUG, "Couldn't generate router descriptor."); - routerinfo_free(ri); - extrainfo_free(ei); return TOR_ROUTERINFO_ERROR_CANNOT_GENERATE; } + ri->cache_info.signed_descriptor_len = strlen(ri->cache_info.signed_descriptor_body); - ri->purpose = - options->BridgeRelay ? ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL; - if (options->BridgeRelay) { - /* Bridges shouldn't be able to send their descriptors unencrypted, - anyway, since they don't have a DirPort, and always connect to the - bridge authority anonymously. But just in case they somehow think of - sending them on an unencrypted connection, don't allow them to try. */ - ri->cache_info.send_unencrypted = 0; - if (ei) - ei->cache_info.send_unencrypted = 0; - } else { - ri->cache_info.send_unencrypted = 1; - if (ei) - ei->cache_info.send_unencrypted = 1; - } - router_get_router_hash(ri->cache_info.signed_descriptor_body, strlen(ri->cache_info.signed_descriptor_body), ri->cache_info.signed_descriptor_digest); + return 0; +} + +/** Build a fresh routerinfo, signed server descriptor, and signed extrainfo + * document for this OR. + * + * Set r to the generated routerinfo, e to the generated extrainfo document. + * Failure to generate an extra-info document is not an error and is indicated + * by setting e to NULL. + * Return 0 on success, and a negative value on temporary error. + * Caller is responsible for freeing generated documents on success. + */ +int +router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) +{ + int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG; + routerinfo_t *ri = NULL; + extrainfo_t *ei = NULL; + + if (BUG(!r)) + goto err; + + if (BUG(!e)) + goto err; + + result = router_build_fresh_unsigned_routerinfo(&ri); + if (result < 0) { + goto err; + } + /* If ri is NULL, then result should be negative. So this check should be + * unreachable. */ + if (BUG(!ri)) { + result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG; + goto err; + } + + ei = router_build_fresh_signed_extrainfo(ri); + + /* Failing to create an ei is not an error. */ + if (ei) { + router_update_routerinfo_from_extrainfo(ri, ei); + } + + result = router_dump_and_sign_routerinfo_descriptor_body(ri); + if (result < 0) + goto err; + if (ei) { - tor_assert(! - routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei, - &ri->cache_info, NULL)); + if (BUG(routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei, + &ri->cache_info, NULL))) { + result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG; + goto err; + } } + goto done; + + err: + routerinfo_free(ri); + extrainfo_free(ei); + *r = NULL; + *e = NULL; + return result; + + done: *r = ri; *e = ei; return 0; @@ -2152,7 +2423,9 @@ mark_my_descriptor_dirty_if_too_old(time_t now) /* Now we see whether we want to be retrying frequently or no. The * rule here is that we'll retry frequently if we aren't listed in the * live consensus we have, or if the publication time of the - * descriptor listed for us in the consensus is very old. */ + * descriptor listed for us in the consensus is very old, or if the + * consensus lists us as "stale" and we haven't regenerated since the + * consensus was published. */ ns = networkstatus_get_live_consensus(now); if (ns) { rs = networkstatus_vote_find_entry(ns, server_identitykey_digest); @@ -2160,6 +2433,8 @@ mark_my_descriptor_dirty_if_too_old(time_t now) retry_fast_reason = "not listed in consensus"; else if (rs->published_on < slow_cutoff) retry_fast_reason = "version listed in consensus is quite old"; + else if (rs->is_staledesc && ns->valid_after > desc_clean_since) + retry_fast_reason = "listed as stale in consensus"; } if (retry_fast_reason && desc_clean_since < fast_cutoff) @@ -2405,6 +2680,10 @@ get_platform_str(char *platform, size_t len) /** OR only: Given a routerinfo for this router, and an identity key to sign * with, encode the routerinfo as a signed server descriptor and return a new * string encoding the result, or NULL on failure. + * + * In addition to the fields in router, this function calls + * onion_key_lifetime(), get_options(), and we_are_hibernating(), and uses the + * results to populate some fields in the descriptor. */ char * router_dump_router_to_string(routerinfo_t *router, @@ -2468,11 +2747,8 @@ router_dump_router_to_string(routerinfo_t *router, log_err(LD_BUG,"Couldn't base64-encode signing key certificate!"); goto err; } - if (ed25519_public_to_base64(ed_fp_base64, - &router->cache_info.signing_key_cert->signing_key)<0) { - log_err(LD_BUG,"Couldn't base64-encode identity key\n"); - goto err; - } + ed25519_public_to_base64(ed_fp_base64, + &router->cache_info.signing_key_cert->signing_key); tor_asprintf(&ed_cert_line, "identity-ed25519\n" "-----BEGIN ED25519 CERT-----\n" "%s" @@ -2722,8 +2998,7 @@ router_dump_router_to_string(routerinfo_t *router, if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN, signing_keypair) < 0) goto err; - if (ed25519_signature_to_base64(buf, &sig) < 0) - goto err; + ed25519_signature_to_base64(buf, &sig); smartlist_add_asprintf(chunks, "%s\n", buf); } @@ -2862,9 +3137,14 @@ load_stats_file(const char *filename, const char *end_line, time_t now, return r; } -/** Write the contents of <b>extrainfo</b> and aggregated statistics to - * *<b>s_out</b>, signing them with <b>ident_key</b>. Return 0 on - * success, negative on failure. */ +/** Write the contents of <b>extrainfo</b>, to * *<b>s_out</b>, signing them + * with <b>ident_key</b>. + * + * If ExtraInfoStatistics is 1, also write aggregated statistics and related + * configuration data before signing. Most statistics also have an option that + * enables or disables that particular statistic. + * + * Return 0 on success, negative on failure. */ int extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, crypto_pk_t *ident_key, @@ -2874,7 +3154,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, char identity[HEX_DIGEST_LEN+1]; char published[ISO_TIME_LEN+1]; char digest[DIGEST_LEN]; - char *bandwidth_usage; int result; static int write_stats_to_extrainfo = 1; char sig[DIROBJ_MAX_SIG_LEN+1]; @@ -2889,7 +3168,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, base16_encode(identity, sizeof(identity), extrainfo->cache_info.identity_digest, DIGEST_LEN); format_iso_time(published, extrainfo->cache_info.published_on); - bandwidth_usage = rep_hist_get_bandwidth_lines(); if (emit_ed_sigs) { if (!extrainfo->cache_info.signing_key_cert->signing_key_included || !ed25519_pubkey_eq(&extrainfo->cache_info.signing_key_cert->signed_key, @@ -2915,21 +3193,35 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, ed_cert_line = tor_strdup(""); } - tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n%s", + tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n", extrainfo->nickname, identity, ed_cert_line, - published, bandwidth_usage); + published); smartlist_add(chunks, pre); - if (geoip_is_loaded(AF_INET)) - smartlist_add_asprintf(chunks, "geoip-db-digest %s\n", - geoip_db_digest(AF_INET)); - if (geoip_is_loaded(AF_INET6)) - smartlist_add_asprintf(chunks, "geoip6-db-digest %s\n", - geoip_db_digest(AF_INET6)); + /* Add information about the pluggable transports we support, even if we + * are not publishing statistics. This information is needed by BridgeDB + * to distribute bridges. */ + if (options->ServerTransportPlugin) { + char *pluggable_transports = pt_get_extra_info_descriptor_string(); + if (pluggable_transports) + smartlist_add(chunks, pluggable_transports); + } if (options->ExtraInfoStatistics && write_stats_to_extrainfo) { log_info(LD_GENERAL, "Adding stats to extra-info descriptor."); + /* Bandwidth usage stats don't have their own option */ + { + contents = rep_hist_get_bandwidth_lines(); + smartlist_add(chunks, contents); + } + /* geoip hashes aren't useful unless we are publishing other stats */ + if (geoip_is_loaded(AF_INET)) + smartlist_add_asprintf(chunks, "geoip-db-digest %s\n", + geoip_db_digest(AF_INET)); + if (geoip_is_loaded(AF_INET6)) + smartlist_add_asprintf(chunks, "geoip6-db-digest %s\n", + geoip_db_digest(AF_INET6)); if (options->DirReqStatistics && load_stats_file("stats"PATH_SEPARATOR"dirreq-stats", "dirreq-stats-end", now, &contents) > 0) { @@ -2965,19 +3257,12 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, if (contents) smartlist_add(chunks, contents); } - } - - /* Add information about the pluggable transports we support. */ - if (options->ServerTransportPlugin) { - char *pluggable_transports = pt_get_extra_info_descriptor_string(); - if (pluggable_transports) - smartlist_add(chunks, pluggable_transports); - } - - if (should_record_bridge_info(options) && write_stats_to_extrainfo) { - const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now); - if (bridge_stats) { - smartlist_add_strdup(chunks, bridge_stats); + /* bridge statistics */ + if (should_record_bridge_info(options)) { + const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now); + if (bridge_stats) { + smartlist_add_strdup(chunks, bridge_stats); + } } } @@ -2992,8 +3277,7 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN, signing_keypair) < 0) goto err; - if (ed25519_signature_to_base64(buf, &ed_sig) < 0) - goto err; + ed25519_signature_to_base64(buf, &ed_sig); smartlist_add_asprintf(chunks, "%s\n", buf); } @@ -3071,7 +3355,6 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, tor_free(s_dup); tor_free(ed_cert_line); extrainfo_free(ei_tmp); - tor_free(bandwidth_usage); return result; } @@ -3081,9 +3364,9 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, void router_reset_warnings(void) { - if (warned_nonexistent_family) { - SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp)); - smartlist_clear(warned_nonexistent_family); + if (warned_family) { + SMARTLIST_FOREACH(warned_family, char *, cp, tor_free(cp)); + smartlist_clear(warned_family); } } @@ -3096,6 +3379,10 @@ router_free_all(void) crypto_pk_free(server_identitykey); crypto_pk_free(client_identitykey); + /* Destroying a locked mutex is undefined behaviour. This mutex may be + * locked, because multiple threads can access it. But we need to destroy + * it, otherwise re-initialisation will trigger undefined behaviour. + * See #31735 for details. */ tor_mutex_free(key_lock); routerinfo_free(desc_routerinfo); extrainfo_free(desc_extrainfo); @@ -3107,11 +3394,12 @@ router_free_all(void) memwipe(&curve25519_onion_key, 0, sizeof(curve25519_onion_key)); memwipe(&last_curve25519_onion_key, 0, sizeof(last_curve25519_onion_key)); - if (warned_nonexistent_family) { - SMARTLIST_FOREACH(warned_nonexistent_family, char *, cp, tor_free(cp)); - smartlist_free(warned_nonexistent_family); + if (warned_family) { + SMARTLIST_FOREACH(warned_family, char *, cp, tor_free(cp)); + smartlist_free(warned_family); } } + /* From the given RSA key object, convert it to ASN-1 encoded format and set * the newly allocated object in onion_pkey_out. The length of the key is set * in onion_pkey_len_out. */ |