diff options
Diffstat (limited to 'src/feature/dirauth/dirvote.c')
-rw-r--r-- | src/feature/dirauth/dirvote.c | 610 |
1 files changed, 405 insertions, 205 deletions
diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index d6a99d3ef8..f2032d71f6 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -1,11 +1,13 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define DIRVOTE_PRIVATE + #include "core/or/or.h" #include "app/config/config.h" +#include "app/config/resolve_addr.h" #include "core/or/policies.h" #include "core/or/protover.h" #include "core/or/tor_version_st.h" @@ -28,6 +30,7 @@ #include "feature/nodelist/fmt_routerstatus.h" #include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/nodefamily.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" #include "feature/relay/router.h" @@ -35,15 +38,17 @@ #include "feature/stats/rephist.h" #include "feature/client/entrynodes.h" /* needed for guardfraction methods */ #include "feature/nodelist/torcert.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/dirauth/dirvote.h" #include "feature/dirauth/authmode.h" #include "feature/dirauth/shared_random_state.h" +#include "feature/dirauth/dirauth_sys.h" #include "feature/nodelist/authority_cert_st.h" #include "feature/dircache/cached_dir_st.h" #include "feature/dirclient/dir_server_st.h" +#include "feature/dirauth/dirauth_options_st.h" #include "feature/nodelist/document_signature_st.h" #include "feature/nodelist/microdesc_st.h" #include "feature/nodelist/networkstatus_st.h" @@ -60,6 +65,9 @@ #include "lib/encoding/confline.h" #include "lib/crypt_ops/crypto_format.h" +/* Algorithm to use for the bandwidth file digest. */ +#define DIGEST_ALG_BW_FILE DIGEST_SHA256 + /** * \file dirvote.c * \brief Functions to compute directory consensus, and schedule voting. @@ -216,10 +224,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, networkstatus_t *v3_ns) { smartlist_t *chunks = smartlist_new(); - char *packages = NULL; char fingerprint[FINGERPRINT_LEN+1]; char digest[DIGEST_LEN]; - uint32_t addr; char *protocols_lines = NULL; char *client_versions_line = NULL, *server_versions_line = NULL; char *shared_random_vote_str = NULL; @@ -231,8 +237,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, voter = smartlist_get(v3_ns->voters, 0); - addr = voter->addr; - base16_encode(fingerprint, sizeof(fingerprint), v3_ns->cert->cache_info.identity_digest, DIGEST_LEN); @@ -242,19 +246,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, v3_ns->server_versions); protocols_lines = format_protocols_lines_for_vote(v3_ns); - if (v3_ns->package_lines) { - smartlist_t *tmp = smartlist_new(); - SMARTLIST_FOREACH(v3_ns->package_lines, const char *, p, - if (validate_recommended_package_line(p)) - smartlist_add_asprintf(tmp, "package %s\n", p)); - smartlist_sort_strings(tmp); - packages = smartlist_join_strings(tmp, "", 0, NULL); - SMARTLIST_FOREACH(tmp, char *, cp, tor_free(cp)); - smartlist_free(tmp); - } else { - packages = tor_strdup(""); - } - /* Get shared random commitments/reveals line(s). */ shared_random_vote_str = sr_get_string_for_vote(); @@ -268,6 +259,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, char *flag_thresholds = dirserv_get_flag_thresholds_line(); char *params; char *bw_headers_line = NULL; + char *bw_file_digest = NULL; authority_cert_t *cert = v3_ns->cert; char *methods = make_consensus_method_list(MIN_SUPPORTED_CONSENSUS_METHOD, @@ -307,43 +299,68 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, tor_free(bw_file_headers); } - smartlist_add_asprintf(chunks, - "network-status-version 3\n" - "vote-status %s\n" - "consensus-methods %s\n" - "published %s\n" - "valid-after %s\n" - "fresh-until %s\n" - "valid-until %s\n" - "voting-delay %d %d\n" - "%s%s" /* versions */ - "%s" /* protocols */ - "%s" /* packages */ - "known-flags %s\n" - "flag-thresholds %s\n" - "params %s\n" - "%s" /* bandwidth file headers */ - "dir-source %s %s %s %s %d %d\n" - "contact %s\n" - "%s" /* shared randomness information */ - , - v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion", - methods, - published, va, fu, vu, - v3_ns->vote_seconds, v3_ns->dist_seconds, - client_versions_line, - server_versions_line, - protocols_lines, - packages, - flags, - flag_thresholds, - params, - bw_headers_line ? bw_headers_line : "", - voter->nickname, fingerprint, voter->address, - fmt_addr32(addr), voter->dir_port, voter->or_port, - voter->contact, - shared_random_vote_str ? - shared_random_vote_str : ""); + /* Create bandwidth-file-digest if applicable. + * v3_ns->b64_digest_bw_file will contain the digest when V3BandwidthsFile + * is configured and the bandwidth file could be read, even if it was not + * parseable. + */ + if (!tor_digest256_is_zero((const char *)v3_ns->bw_file_digest256)) { + /* Encode the digest. */ + char b64_digest_bw_file[BASE64_DIGEST256_LEN+1] = {0}; + digest256_to_base64(b64_digest_bw_file, + (const char *)v3_ns->bw_file_digest256); + /* "bandwidth-file-digest" 1*(SP algorithm "=" digest) NL */ + char *digest_algo_b64_digest_bw_file = NULL; + tor_asprintf(&digest_algo_b64_digest_bw_file, "%s=%s", + crypto_digest_algorithm_get_name(DIGEST_ALG_BW_FILE), + b64_digest_bw_file); + /* No need for tor_strdup(""), format_line_if_present does it. */ + bw_file_digest = format_line_if_present( + "bandwidth-file-digest", digest_algo_b64_digest_bw_file); + tor_free(digest_algo_b64_digest_bw_file); + } + + const char *ip_str = fmt_addr(&voter->ipv4_addr); + + if (ip_str[0]) { + smartlist_add_asprintf(chunks, + "network-status-version 3\n" + "vote-status %s\n" + "consensus-methods %s\n" + "published %s\n" + "valid-after %s\n" + "fresh-until %s\n" + "valid-until %s\n" + "voting-delay %d %d\n" + "%s%s" /* versions */ + "%s" /* protocols */ + "known-flags %s\n" + "flag-thresholds %s\n" + "params %s\n" + "%s" /* bandwidth file headers */ + "%s" /* bandwidth file digest */ + "dir-source %s %s %s %s %d %d\n" + "contact %s\n" + "%s" /* shared randomness information */ + , + v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion", + methods, + published, va, fu, vu, + v3_ns->vote_seconds, v3_ns->dist_seconds, + client_versions_line, + server_versions_line, + protocols_lines, + flags, + flag_thresholds, + params, + bw_headers_line ? bw_headers_line : "", + bw_file_digest ? bw_file_digest: "", + voter->nickname, fingerprint, voter->address, + ip_str, voter->ipv4_dirport, voter->ipv4_orport, + voter->contact, + shared_random_vote_str ? + shared_random_vote_str : ""); + } tor_free(params); tor_free(flags); @@ -351,6 +368,10 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, tor_free(methods); tor_free(shared_random_vote_str); tor_free(bw_headers_line); + tor_free(bw_file_digest); + + if (ip_str[0] == '\0') + goto err; if (!tor_digest_is_zero(voter->legacy_id_digest)) { char fpbuf[HEX_DIGEST_LEN+1]; @@ -369,7 +390,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, rsf = routerstatus_format_entry(&vrs->status, vrs->version, vrs->protocols, NS_V3_VOTE, - ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, vrs); if (rsf) smartlist_add(chunks, rsf); @@ -412,7 +432,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, { networkstatus_t *v; - if (!(v = networkstatus_parse_vote_from_string(status, NULL, + if (!(v = networkstatus_parse_vote_from_string(status, strlen(status), + NULL, v3_ns->type))) { log_err(LD_BUG,"Generated a networkstatus %s we couldn't parse: " "<<%s>>", @@ -430,7 +451,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, tor_free(client_versions_line); tor_free(server_versions_line); tor_free(protocols_lines); - tor_free(packages); SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); smartlist_free(chunks); @@ -614,9 +634,12 @@ compare_vote_rs(const vote_routerstatus_t *a, const vote_routerstatus_t *b) if ((r = strcmp(b->status.nickname, a->status.nickname))) return r; - CMP_FIELD(unsigned, int, addr); - CMP_FIELD(unsigned, int, or_port); - CMP_FIELD(unsigned, int, dir_port); + if ((r = tor_addr_compare(&a->status.ipv4_addr, &b->status.ipv4_addr, + CMP_EXACT))) { + return r; + } + CMP_FIELD(unsigned, int, ipv4_orport); + CMP_FIELD(unsigned, int, ipv4_dirport); return 0; } @@ -872,7 +895,7 @@ dirvote_get_intermediate_param_value(const smartlist_t *param_list, int ok; value = (int32_t) tor_parse_long(integer_str, 10, INT32_MIN, INT32_MAX, &ok, NULL); - if (BUG(! ok)) + if (BUG(!ok)) return default_val; ++n_found; } @@ -1525,14 +1548,11 @@ networkstatus_compute_consensus(smartlist_t *votes, consensus_method = MAX_SUPPORTED_CONSENSUS_METHOD; } - if (consensus_method >= MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE) { + { /* It's smarter to initialize these weights to 1, so that later on, * we can't accidentally divide by zero. */ G = M = E = D = 1; T = 4; - } else { - /* ...but originally, they were set to zero. */ - G = M = E = D = T = 0; } /* Compute medians of time-related things, and figure out how many @@ -1721,9 +1741,9 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_add_asprintf(chunks, "dir-source %s%s %s %s %s %d %d\n", voter->nickname, e->is_legacy ? "-legacy" : "", - fingerprint, voter->address, fmt_addr32(voter->addr), - voter->dir_port, - voter->or_port); + fingerprint, voter->address, fmt_addr(&voter->ipv4_addr), + voter->ipv4_dirport, + voter->ipv4_orport); if (! e->is_legacy) { smartlist_add_asprintf(chunks, "contact %s\n" @@ -2020,10 +2040,10 @@ networkstatus_compute_consensus(smartlist_t *votes, memcpy(rs_out.identity_digest, current_rsa_id, DIGEST_LEN); memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest, DIGEST_LEN); - rs_out.addr = rs->status.addr; + tor_addr_copy(&rs_out.ipv4_addr, &rs->status.ipv4_addr); rs_out.published_on = rs->status.published_on; - rs_out.dir_port = rs->status.dir_port; - rs_out.or_port = rs->status.or_port; + rs_out.ipv4_dirport = rs->status.ipv4_dirport; + rs_out.ipv4_orport = rs->status.ipv4_orport; tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr); rs_out.ipv6_orport = alt_orport.port; rs_out.has_bandwidth = 0; @@ -2233,7 +2253,7 @@ networkstatus_compute_consensus(smartlist_t *votes, /* Okay!! Now we can write the descriptor... */ /* First line goes into "buf". */ buf = routerstatus_format_entry(&rs_out, NULL, NULL, - rs_format, consensus_method, NULL); + rs_format, NULL); if (buf) smartlist_add(chunks, buf); } @@ -2253,8 +2273,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_add_strdup(chunks, chosen_version); } smartlist_add_strdup(chunks, "\n"); - if (chosen_protocol_list && - consensus_method >= MIN_METHOD_FOR_RS_PROTOCOLS) { + if (chosen_protocol_list) { smartlist_add_asprintf(chunks, "pr %s\n", chosen_protocol_list); } /* Now the weight line. */ @@ -2409,7 +2428,8 @@ networkstatus_compute_consensus(smartlist_t *votes, { networkstatus_t *c; - if (!(c = networkstatus_parse_vote_from_string(result, NULL, + if (!(c = networkstatus_parse_vote_from_string(result, strlen(result), + NULL, NS_TYPE_CONSENSUS))) { log_err(LD_BUG, "Generated a networkstatus consensus we couldn't " "parse."); @@ -2516,9 +2536,12 @@ compute_consensus_package_lines(smartlist_t *votes) * any new signatures in <b>src_voter_list</b> that should be added to * <b>target</b>. (A signature should be added if we have no signature for that * voter in <b>target</b> yet, or if we have no verifiable signature and the - * new signature is verifiable.) Return the number of signatures added or - * changed, or -1 if the document signed by <b>sigs</b> isn't the same - * document as <b>target</b>. */ + * new signature is verifiable.) + * + * Return the number of signatures added or changed, or -1 if the document + * signatures are invalid. Sets *<b>msg_out</b> to a string constant + * describing the signature status. + */ STATIC int networkstatus_add_detached_signatures(networkstatus_t *target, ns_detached_signatures_t *sigs, @@ -2567,7 +2590,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target, return -1; } for (alg = DIGEST_SHA1; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) { - if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) { + if (!fast_mem_is_zero(digests->d[alg], DIGEST256_LEN)) { if (fast_memeq(target->digests.d[alg], digests->d[alg], DIGEST256_LEN)) { ++n_matches; @@ -2763,7 +2786,7 @@ networkstatus_get_detached_signatures(smartlist_t *consensuses) char d[HEX_DIGEST256_LEN+1]; const char *alg_name = crypto_digest_algorithm_get_name(alg); - if (tor_mem_is_zero(ns->digests.d[alg], DIGEST256_LEN)) + if (fast_mem_is_zero(ns->digests.d[alg], DIGEST256_LEN)) continue; base16_encode(d, sizeof(d), ns->digests.d[alg], DIGEST256_LEN); smartlist_add_asprintf(elements, "additional-digest %s %s %s\n", @@ -2839,7 +2862,7 @@ dirvote_act(const or_options_t *options, time_t now) "Mine is %s.", keys, hex_str(c->cache_info.identity_digest, DIGEST_LEN)); tor_free(keys); - voting_schedule_recalculate_timing(options, now); + dirauth_sched_recalculate_timing(options, now); } #define IF_TIME_FOR_NEXT_ACTION(when_field, done_field) \ @@ -2885,7 +2908,7 @@ dirvote_act(const or_options_t *options, time_t now) networkstatus_get_latest_consensus_by_flavor(FLAV_NS)); /* XXXX We will want to try again later if we haven't got enough * signatures yet. Implement this if it turns out to ever happen. */ - voting_schedule_recalculate_timing(options, now); + dirauth_sched_recalculate_timing(options, now); return voting_schedule.voting_starts; } ENDIF @@ -2952,7 +2975,7 @@ dirvote_perform_vote(void) if (!contents) return -1; - pending_vote = dirvote_add_vote(contents, &msg, &status); + pending_vote = dirvote_add_vote(contents, 0, "self", &msg, &status); tor_free(contents); if (!pending_vote) { log_warn(LD_DIR, "Couldn't store my own vote! (I told myself, '%s'.)", @@ -3108,13 +3131,46 @@ list_v3_auth_ids(void) return keys; } +/* Check the voter information <b>vi</b>, and assert that at least one + * signature is good. Asserts on failure. */ +static void +assert_any_sig_good(const networkstatus_voter_info_t *vi) +{ + int any_sig_good = 0; + SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig, + if (sig->good_signature) + any_sig_good = 1); + tor_assert(any_sig_good); +} + +/* Add <b>cert</b> to our list of known authority certificates. */ +static void +add_new_cert_if_needed(const struct authority_cert_t *cert) +{ + tor_assert(cert); + if (!authority_cert_get_by_digests(cert->cache_info.identity_digest, + cert->signing_key_digest)) { + /* Hey, it's a new cert! */ + trusted_dirs_load_certs_from_string( + cert->cache_info.signed_descriptor_body, + TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/, + NULL); + if (!authority_cert_get_by_digests(cert->cache_info.identity_digest, + cert->signing_key_digest)) { + log_warn(LD_BUG, "We added a cert, but still couldn't find it."); + } + } +} + /** Called when we have received a networkstatus vote in <b>vote_body</b>. * Parse and validate it, and on success store it as a pending vote (which we * then return). Return NULL on failure. Sets *<b>msg_out</b> and * *<b>status_out</b> to an HTTP response and status code. (V3 authority * only) */ pending_vote_t * -dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) +dirvote_add_vote(const char *vote_body, time_t time_posted, + const char *where_from, + const char **msg_out, int *status_out) { networkstatus_t *vote; networkstatus_voter_info_t *vi; @@ -3132,7 +3188,8 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) *msg_out = NULL; again: - vote = networkstatus_parse_vote_from_string(vote_body, &end_of_vote, + vote = networkstatus_parse_vote_from_string(vote_body, strlen(vote_body), + &end_of_vote, NS_TYPE_VOTE); if (!end_of_vote) end_of_vote = vote_body + strlen(vote_body); @@ -3144,13 +3201,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) } tor_assert(smartlist_len(vote->voters) == 1); vi = get_voter(vote); - { - int any_sig_good = 0; - SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig, - if (sig->good_signature) - any_sig_good = 1); - tor_assert(any_sig_good); - } + assert_any_sig_good(vi); ds = trusteddirserver_get_by_v3_auth_digest(vi->identity_digest); if (!ds) { char *keys = list_v3_auth_ids(); @@ -3163,19 +3214,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) *msg_out = "Vote not from a recognized v3 authority"; goto err; } - tor_assert(vote->cert); - if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, - vote->cert->signing_key_digest)) { - /* Hey, it's a new cert! */ - trusted_dirs_load_certs_from_string( - vote->cert->cache_info.signed_descriptor_body, - TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/, - NULL); - if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, - vote->cert->signing_key_digest)) { - log_warn(LD_BUG, "We added a cert, but still couldn't find it."); - } - } + add_new_cert_if_needed(vote->cert); /* Is it for the right period? */ if (vote->valid_after != voting_schedule.interval_starts) { @@ -3188,6 +3227,31 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) goto err; } + if (time_posted) { /* they sent it to me via a POST */ + log_notice(LD_DIR, "%s posted a vote to me from %s.", + vi->nickname, where_from); + } else { /* I imported this one myself */ + log_notice(LD_DIR, "Retrieved %s's vote from %s.", + vi->nickname, where_from); + } + + /* Check if we received it, as a post, after the cutoff when we + * start asking other dir auths for it. If we do, the best plan + * is to discard it, because using it greatly increases the chances + * of a split vote for this round (some dir auths got it in time, + * some didn't). */ + if (time_posted && time_posted > voting_schedule.fetch_missing_votes) { + char tbuf1[ISO_TIME_LEN+1], tbuf2[ISO_TIME_LEN+1]; + format_iso_time(tbuf1, time_posted); + format_iso_time(tbuf2, voting_schedule.fetch_missing_votes); + log_warn(LD_DIR, "Rejecting %s's posted vote from %s received at %s; " + "our cutoff for received votes is %s. Check your clock, " + "CPU load, and network load. Also check the authority that " + "posted the vote.", vi->nickname, vi->address, tbuf1, tbuf2); + *msg_out = "Posted vote received too late, would be dangerous to count it"; + goto err; + } + /* Fetch any new router descriptors we just learned about */ update_consensus_router_descriptor_downloads(time(NULL), 1, vote); @@ -3199,8 +3263,8 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) networkstatus_voter_info_t *vi_old = get_voter(v->vote); if (fast_memeq(vi_old->vote_digest, vi->vote_digest, DIGEST_LEN)) { /* Ah, it's the same vote. Not a problem. */ - log_info(LD_DIR, "Discarding a vote we already have (from %s).", - vi->address); + log_notice(LD_DIR, "Discarding a vote we already have (from %s).", + vi->address); if (*status_out < 200) *status_out = 200; goto discard; @@ -3223,6 +3287,8 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) *msg_out = "OK"; return v; } else { + log_notice(LD_DIR, "Discarding vote from %s because we have " + "a newer one already.", vi->address); *msg_out = "Already have a newer pending vote"; goto err; } @@ -3390,7 +3456,9 @@ dirvote_compute_consensuses(void) flavor_name); continue; } - consensus = networkstatus_parse_vote_from_string(consensus_body, NULL, + consensus = networkstatus_parse_vote_from_string(consensus_body, + strlen(consensus_body), + NULL, NS_TYPE_CONSENSUS); if (!consensus) { log_warn(LD_DIR, "Couldn't parse %s consensus we generated!", @@ -3405,6 +3473,15 @@ dirvote_compute_consensuses(void) pending[flav].body = consensus_body; pending[flav].consensus = consensus; n_generated++; + + /* Write it out to disk too, for dir auth debugging purposes */ + { + char *filename; + tor_asprintf(&filename, "my-consensus-%s", flavor_name); + write_str_to_file(get_datadir_fname(filename), consensus_body, 0); + tor_free(filename); + } + consensus_body = NULL; consensus = NULL; } @@ -3529,7 +3606,7 @@ dirvote_add_signatures_to_pending_consensus( * just in case we break detached signature processing at some point. */ { networkstatus_t *v = networkstatus_parse_vote_from_string( - pc->body, NULL, + pc->body, strlen(pc->body), NULL, NS_TYPE_CONSENSUS); tor_assert(v); networkstatus_vote_free(v); @@ -3550,6 +3627,14 @@ dirvote_add_signatures_to_pending_consensus( return r; } +/** Helper: we just got the <b>detached_signatures_body</b> sent to us as + * signatures on the currently pending consensus. Add them to the pending + * consensus (if we have one). + * + * Set *<b>msg</b> to a string constant describing the status, regardless of + * success or failure. + * + * Return negative on failure, nonnegative on success. */ static int dirvote_add_signatures_to_all_pending_consensuses( const char *detached_signatures_body, @@ -3612,7 +3697,12 @@ dirvote_add_signatures_to_all_pending_consensuses( /** Helper: we just got the <b>detached_signatures_body</b> sent to us as * signatures on the currently pending consensus. Add them to the pending * consensus (if we have one); otherwise queue them until we have a - * consensus. Return negative on failure, nonnegative on success. */ + * consensus. + * + * Set *<b>msg</b> to a string constant describing the status, regardless of + * success or failure. + * + * Return negative on failure, nonnegative on success. */ int dirvote_add_signatures(const char *detached_signatures_body, const char *source, @@ -3654,7 +3744,9 @@ dirvote_publish_consensus(void) continue; } - if (networkstatus_set_current_consensus(pending->body, name, 0, NULL)) + if (networkstatus_set_current_consensus(pending->body, + strlen(pending->body), + name, 0, NULL)) log_warn(LD_DIR, "Error publishing %s consensus", name); else log_notice(LD_DIR, "Published %s consensus", name); @@ -3777,22 +3869,22 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) smartlist_add_asprintf(chunks, "onion-key\n%s", key); if (ri->onion_curve25519_pkey) { - char kbuf[128]; - base64_encode(kbuf, sizeof(kbuf), - (const char*)ri->onion_curve25519_pkey->public_key, - CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); - smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); + char kbuf[CURVE25519_BASE64_PADDED_LEN + 1]; + bool add_padding = (consensus_method < MIN_METHOD_FOR_UNPADDED_NTOR_KEY); + curve25519_public_to_base64(kbuf, ri->onion_curve25519_pkey, add_padding); + smartlist_add_asprintf(chunks, "ntor-onion-key %s\n", kbuf); } - /* We originally put a lines in the micrdescriptors, but then we worked out - * that we needed them in the microdesc consensus. See #20916. */ - if (consensus_method < MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC && - !tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport) - smartlist_add_asprintf(chunks, "a %s\n", - fmt_addrport(&ri->ipv6_addr, ri->ipv6_orport)); - - if (family) - smartlist_add_asprintf(chunks, "family %s\n", family); + if (family) { + if (consensus_method < MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS) { + smartlist_add_asprintf(chunks, "family %s\n", family); + } else { + const uint8_t *id = (const uint8_t *)ri->cache_info.identity_digest; + char *canonical_family = nodefamily_canonicalize(family, id, 0); + smartlist_add_asprintf(chunks, "family %s\n", canonical_family); + tor_free(canonical_family); + } + } if (summary && strcmp(summary, "reject 1-65535")) smartlist_add_asprintf(chunks, "p %s\n", summary); @@ -3869,8 +3961,7 @@ dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len, ","); tor_assert(microdesc_consensus_methods); - if (digest256_to_base64(d64, md->digest)<0) - goto out; + digest256_to_base64(d64, md->digest); if (tor_snprintf(out_buf, out_buf_len, "m %s sha256=%s\n", microdesc_consensus_methods, d64)<0) @@ -3889,8 +3980,12 @@ static const struct consensus_method_range_t { int low; int high; } microdesc_consensus_methods[] = { - {MIN_SUPPORTED_CONSENSUS_METHOD, MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC - 1}, - {MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC, MAX_SUPPORTED_CONSENSUS_METHOD}, + {MIN_SUPPORTED_CONSENSUS_METHOD, + MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS - 1}, + {MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS, + MIN_METHOD_FOR_UNPADDED_NTOR_KEY - 1}, + {MIN_METHOD_FOR_UNPADDED_NTOR_KEY, + MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; @@ -4103,8 +4198,8 @@ dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, /** Get the best estimate of a router's bandwidth for dirauth purposes, * preferring measured to advertised values if available. */ -static uint32_t -dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) +MOCK_IMPL(uint32_t,dirserv_get_bandwidth_for_router_kb, + (const routerinfo_t *ri)) { uint32_t bw_kb = 0; /* @@ -4133,31 +4228,72 @@ dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) return bw_kb; } -/** Helper for sorting: compares two routerinfos first by address, and then by - * descending order of "usefulness". (An authority is more useful than a - * non-authority; a running router is more useful than a non-running router; - * and a router with more bandwidth is more useful than one with less.) +/** + * Helper: compare the address of family `family` in `a` with the address in + * `b`. The family must be one of `AF_INET` and `AF_INET6`. **/ static int -compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) +compare_routerinfo_addrs_by_family(const routerinfo_t *a, + const routerinfo_t *b, + int family) +{ + const tor_addr_t *addr1 = (family==AF_INET) ? &a->ipv4_addr : &a->ipv6_addr; + const tor_addr_t *addr2 = (family==AF_INET) ? &b->ipv4_addr : &b->ipv6_addr; + return tor_addr_compare(addr1, addr2, CMP_EXACT); +} + +/** Helper for sorting: compares two ipv4 routerinfos first by ipv4 address, + * and then by descending order of "usefulness" + * (see compare_routerinfo_usefulness) + **/ +STATIC int +compare_routerinfo_by_ipv4(const void **a, const void **b) +{ + const routerinfo_t *first = *(const routerinfo_t **)a; + const routerinfo_t *second = *(const routerinfo_t **)b; + int comparison = compare_routerinfo_addrs_by_family(first, second, AF_INET); + if (comparison == 0) { + // If addresses are equal, use other comparison criteria + return compare_routerinfo_usefulness(first, second); + } else { + return comparison; + } +} + +/** Helper for sorting: compares two ipv6 routerinfos first by ipv6 address, + * and then by descending order of "usefulness" + * (see compare_routerinfo_usefulness) + **/ +STATIC int +compare_routerinfo_by_ipv6(const void **a, const void **b) +{ + const routerinfo_t *first = *(const routerinfo_t **)a; + const routerinfo_t *second = *(const routerinfo_t **)b; + int comparison = compare_routerinfo_addrs_by_family(first, second, AF_INET6); + // If addresses are equal, use other comparison criteria + if (comparison == 0) + return compare_routerinfo_usefulness(first, second); + else + return comparison; +} + +/** +* Compare routerinfos by descending order of "usefulness" : +* An authority is more useful than a non-authority; a running router is +* more useful than a non-running router; and a router with more bandwidth +* is more useful than one with less. +**/ +STATIC int +compare_routerinfo_usefulness(const routerinfo_t *first, + const routerinfo_t *second) { - routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b; int first_is_auth, second_is_auth; - uint32_t bw_kb_first, bw_kb_second; const node_t *node_first, *node_second; int first_is_running, second_is_running; - - /* we return -1 if first should appear before second... that is, - * if first is a better router. */ - if (first->addr < second->addr) - return -1; - else if (first->addr > second->addr) - return 1; - + uint32_t bw_kb_first, bw_kb_second; /* Potentially, this next bit could cause k n lg n memeq calls. But in * reality, we will almost never get here, since addresses will usually be * different. */ - first_is_auth = router_digest_is_trusted_dir(first->cache_info.identity_digest); second_is_auth = @@ -4172,7 +4308,6 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) node_second = node_get_by_id(second->cache_info.identity_digest); first_is_running = node_first && node_first->is_running; second_is_running = node_second && node_second->is_running; - if (first_is_running && !second_is_running) return -1; else if (!first_is_running && second_is_running) @@ -4193,41 +4328,89 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) DIGEST_LEN); } -/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t - * whose keys are the identity digests of those routers that we're going to - * exclude for Sybil-like appearance. */ -static digestmap_t * -get_possible_sybil_list(const smartlist_t *routers) +/** Given a list of routerinfo_t in <b>routers</b> that all use the same + * IP version, specified in <b>family</b>, return a new digestmap_t whose keys + * are the identity digests of those routers that we're going to exclude for + * Sybil-like appearance. + */ +STATIC digestmap_t * +get_sybil_list_by_ip_version(const smartlist_t *routers, sa_family_t family) { - const or_options_t *options = get_options(); - digestmap_t *omit_as_sybil; + const dirauth_options_t *options = dirauth_get_options(); + digestmap_t *omit_as_sybil = digestmap_new(); smartlist_t *routers_by_ip = smartlist_new(); - uint32_t last_addr; - int addr_count; + int addr_count = 0; + routerinfo_t *last_ri = NULL; /* Allow at most this number of Tor servers on a single IP address, ... */ int max_with_same_addr = options->AuthDirMaxServersPerAddr; if (max_with_same_addr <= 0) max_with_same_addr = INT_MAX; smartlist_add_all(routers_by_ip, routers); - smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); - omit_as_sybil = digestmap_new(); + if (family == AF_INET6) + smartlist_sort(routers_by_ip, compare_routerinfo_by_ipv6); + else + smartlist_sort(routers_by_ip, compare_routerinfo_by_ipv4); - last_addr = 0; - addr_count = 0; SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { - if (last_addr != ri->addr) { - last_addr = ri->addr; + bool addrs_equal; + if (last_ri) + addrs_equal = !compare_routerinfo_addrs_by_family(last_ri, ri, family); + else + addrs_equal = false; + + if (! addrs_equal) { + last_ri = ri; addr_count = 1; } else if (++addr_count > max_with_same_addr) { digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); } } SMARTLIST_FOREACH_END(ri); - smartlist_free(routers_by_ip); return omit_as_sybil; } +/** Given a list of routerinfo_t in <b>routers</b>, return a new digestmap_t + * whose keys are the identity digests of those routers that we're going to + * exclude for Sybil-like appearance. */ +STATIC digestmap_t * +get_all_possible_sybil(const smartlist_t *routers) +{ + smartlist_t *routers_ipv6, *routers_ipv4; + routers_ipv6 = smartlist_new(); + routers_ipv4 = smartlist_new(); + digestmap_t *omit_as_sybil_ipv4; + digestmap_t *omit_as_sybil_ipv6; + digestmap_t *omit_as_sybil = digestmap_new(); + // Sort the routers in two lists depending on their IP version + SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + // If the router has an IPv6 address + if (tor_addr_family(&(ri->ipv6_addr)) == AF_INET6) { + smartlist_add(routers_ipv6, ri); + } + // If the router has an IPv4 address + if (tor_addr_family(&(ri->ipv4_addr)) == AF_INET) { + smartlist_add(routers_ipv4, ri); + } + } SMARTLIST_FOREACH_END(ri); + omit_as_sybil_ipv4 = get_sybil_list_by_ip_version(routers_ipv4, AF_INET); + omit_as_sybil_ipv6 = get_sybil_list_by_ip_version(routers_ipv6, AF_INET6); + + // Add all possible sybils to the common digestmap + DIGESTMAP_FOREACH (omit_as_sybil_ipv4, sybil_id, routerinfo_t *, ri) { + digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); + } DIGESTMAP_FOREACH_END; + DIGESTMAP_FOREACH (omit_as_sybil_ipv6, sybil_id, routerinfo_t *, ri) { + digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); + } DIGESTMAP_FOREACH_END; + // Clean the temp variables + smartlist_free(routers_ipv4); + smartlist_free(routers_ipv6); + digestmap_free(omit_as_sybil_ipv4, NULL); + digestmap_free(omit_as_sybil_ipv6, NULL); + // Return the digestmap: it now contains all the possible sybils + return omit_as_sybil; +} /** Given a platform string as in a routerinfo_t (possibly null), return a * newly allocated version string for a networkstatus document, or NULL if the * platform doesn't give a Tor version. */ @@ -4364,6 +4547,23 @@ clear_status_flags_on_sybil(routerstatus_t *rs) * forget to add it to this clause. */ } +/** Space-separated list of all the flags that we will always vote on. */ +const char DIRVOTE_UNIVERSAL_FLAGS[] = + "Authority " + "Exit " + "Fast " + "Guard " + "HSDir " + "Stable " + "StaleDesc " + "V2Dir " + "Valid"; +/** Space-separated list of all flags that we may or may not vote on, + * depending on our configuration. */ +const char DIRVOTE_OPTIONAL_FLAGS[] = + "BadExit " + "Running"; + /** Return a new networkstatus_t* containing our current opinion. (For v3 * authorities) */ networkstatus_t * @@ -4371,23 +4571,24 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, authority_cert_t *cert) { const or_options_t *options = get_options(); + const dirauth_options_t *d_options = dirauth_get_options(); networkstatus_t *v3_out = NULL; - uint32_t addr; + tor_addr_t addr; char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; const char *contact; smartlist_t *routers, *routerstatuses; char identity_digest[DIGEST_LEN]; char signing_key_digest[DIGEST_LEN]; - int listbadexits = options->AuthDirListBadExits; + const int listbadexits = d_options->AuthDirListBadExits; routerlist_t *rl = router_get_routerlist(); time_t now = time(NULL); time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; networkstatus_voter_info_t *voter = NULL; vote_timing_t timing; - digestmap_t *omit_as_sybil = NULL; const int vote_on_reachability = running_long_enough_to_decide_unreachable(); smartlist_t *microdescriptors = NULL; smartlist_t *bw_file_headers = NULL; + uint8_t bw_file_digest256[DIGEST256_LEN] = {0}; tor_assert(private_key); tor_assert(cert); @@ -4400,20 +4601,25 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, log_err(LD_BUG, "Error computing identity key digest"); return NULL; } - if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { + if (!find_my_address(options, AF_INET, LOG_WARN, &addr, NULL, &hostname)) { log_warn(LD_NET, "Couldn't resolve my hostname"); return NULL; } if (!hostname || !strchr(hostname, '.')) { tor_free(hostname); - hostname = tor_dup_ip(addr); + hostname = tor_addr_to_str_dup(&addr); + } + + if (!hostname) { + log_err(LD_BUG, "Failed to determine hostname AND duplicate address"); + return NULL; } - if (options->VersioningAuthoritativeDir) { + if (d_options->VersioningAuthoritativeDirectory) { client_versions = - format_recommended_version_list(options->RecommendedClientVersions, 0); + format_recommended_version_list(d_options->RecommendedClientVersions, 0); server_versions = - format_recommended_version_list(options->RecommendedServerVersions, 0); + format_recommended_version_list(d_options->RecommendedServerVersions, 0); } contact = get_options()->ContactInfo; @@ -4425,7 +4631,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, * set_routerstatus_from_routerinfo() see up-to-date bandwidth info. */ if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL); + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL, + NULL); } else { /* * No bandwidths file; clear the measured bandwidth cache in case we had @@ -4447,19 +4654,16 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, routers_make_ed_keys_unique(routers); /* After this point, don't use rl->routers; use 'routers' instead. */ routers_sort_by_identity(routers); - omit_as_sybil = get_possible_sybil_list(routers); - - DIGESTMAP_FOREACH(omit_as_sybil, sybil_id, void *, ignore) { - (void) ignore; + /* Get a digestmap of possible sybil routers, IPv4 or IPv6 */ + digestmap_t *omit_as_sybil = get_all_possible_sybil(routers); + DIGESTMAP_FOREACH (omit_as_sybil, sybil_id, void *, ignore) { + (void)ignore; rep_hist_make_router_pessimal(sybil_id, now); - } DIGESTMAP_FOREACH_END; - + } DIGESTMAP_FOREACH_END /* Count how many have measured bandwidths so we know how to assign flags; * this must come before dirserv_compute_performance_thresholds() */ dirserv_count_measured_bws(routers); - dirserv_compute_performance_thresholds(omit_as_sybil); - routerstatuses = smartlist_new(); microdescriptors = smartlist_new(); @@ -4467,7 +4671,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /* If it has a protover list and contains a protocol name greater than * MAX_PROTOCOL_NAME_LENGTH, skip it. */ if (ri->protocol_list && - protover_contains_long_protocol_names(ri->protocol_list)) { + protover_list_is_invalid(ri->protocol_list)) { continue; } if (ri->cache_info.published_on >= cutoff) { @@ -4479,15 +4683,14 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; - set_routerstatus_from_routerinfo(rs, node, ri, now, - listbadexits); + dirauth_set_routerstatus_from_routerinfo(rs, node, ri, now, + listbadexits); if (ri->cache_info.signing_key_cert) { memcpy(vrs->ed25519_id, ri->cache_info.signing_key_cert->signing_key.pubkey, ED25519_PUBKEY_LEN); } - if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) clear_status_flags_on_sybil(rs); @@ -4530,7 +4733,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /* Only set bw_file_headers when V3BandwidthsFile is configured */ bw_file_headers = smartlist_new(); dirserv_read_measured_bandwidths(options->V3BandwidthsFile, - routerstatuses, bw_file_headers); + routerstatuses, bw_file_headers, + bw_file_digest256); + } else { /* * No bandwidths file; clear the measured bandwidth cache in case we had @@ -4557,7 +4762,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, else last_consensus_interval = options->TestingV3AuthInitialVotingInterval; v3_out->valid_after = - voting_schedule_get_start_of_next_interval(now, + voting_sched_get_start_of_interval_after(now, (int)last_consensus_interval, options->TestingV3AuthVotingStartOffset); format_iso_time(tbuf, v3_out->valid_after); @@ -4596,18 +4801,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, tor_assert_nonfatal(protover_all_supported( v3_out->recommended_client_protocols, NULL)); - v3_out->package_lines = smartlist_new(); - { - config_line_t *cl; - for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { - if (validate_recommended_package_line(cl->value)) - smartlist_add_strdup(v3_out->package_lines, cl->value); - } - } - v3_out->known_flags = smartlist_new(); smartlist_split_string(v3_out->known_flags, - "Authority Exit Fast Guard Stable V2Dir Valid HSDir", + DIRVOTE_UNIVERSAL_FLAGS, 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (vote_on_reachability) smartlist_add_strdup(v3_out->known_flags, "Running"); @@ -4615,22 +4811,26 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, smartlist_add_strdup(v3_out->known_flags, "BadExit"); smartlist_sort_strings(v3_out->known_flags); - if (options->ConsensusParams) { + if (d_options->ConsensusParams) { + config_line_t *paramline = d_options->ConsensusParams; v3_out->net_params = smartlist_new(); - smartlist_split_string(v3_out->net_params, - options->ConsensusParams, NULL, 0, 0); + for ( ; paramline; paramline = paramline->next) { + smartlist_split_string(v3_out->net_params, + paramline->value, NULL, 0, 0); + } smartlist_sort_strings(v3_out->net_params); } v3_out->bw_file_headers = bw_file_headers; + memcpy(v3_out->bw_file_digest256, bw_file_digest256, DIGEST256_LEN); voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter->nickname = tor_strdup(options->Nickname); memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); voter->sigs = smartlist_new(); voter->address = hostname; - voter->addr = addr; - voter->dir_port = router_get_advertised_dir_port(options, 0); - voter->or_port = router_get_advertised_or_port(options); + tor_addr_copy(&voter->ipv4_addr, &addr); + voter->ipv4_dirport = routerconf_find_dir_port(options, 0); + voter->ipv4_orport = routerconf_find_or_port(options, AF_INET); voter->contact = tor_strdup(contact); if (options->V3AuthUseLegacyKey) { authority_cert_t *c = get_my_v3_legacy_cert(); |