diff options
author | Nick Mathewson <nickm@torproject.org> | 2007-10-22 17:31:26 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2007-10-22 17:31:26 +0000 |
commit | 09bce19884d9b07a83979a6b6f0abb1789a3b9b3 (patch) | |
tree | e9d316e6aef1f6a95f0d98326aa32bcc88915c1d | |
parent | 4bab46d5d797c9a2f88fbf5dfb9ea0d91d384ee7 (diff) | |
download | tor-09bce19884d9b07a83979a6b6f0abb1789a3b9b3.tar.gz tor-09bce19884d9b07a83979a6b6f0abb1789a3b9b3.zip |
r16042@catbus: nickm | 2007-10-22 13:30:49 -0400
Move functions into and out of dirvote.c so that it contains all the v3 authority functionality, and no non-authority functionality.
svn:r12107
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | src/or/dirserv.c | 239 | ||||
-rw-r--r-- | src/or/dirvote.c | 420 | ||||
-rw-r--r-- | src/or/networkstatus.c | 208 | ||||
-rw-r--r-- | src/or/or.h | 34 | ||||
-rw-r--r-- | src/or/routerlist.c | 16 |
6 files changed, 443 insertions, 475 deletions
@@ -115,6 +115,7 @@ Changes in version 0.2.0.9-alpha - 2007-10-?? from 0.1.1.x or earlier, or if you try to downgrade to 0.1.1.x or earlier. - New convenience code to locate a file within the DataDirectory. + - Move non-authority functionality out of dirvote.c. o Utilities - Include the "tor-ctrl.sh" bash script by Stefan Behte to provide diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 420bf08d1d..7c702fa2dd 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1457,10 +1457,6 @@ dirserv_get_consensus(void) /** For authoritative directories: the current (v2) network status. */ static cached_dir_t *the_v2_networkstatus = NULL; -/** For authoritative directories: out most recent vote for the (v3) network - * status */ -static cached_dir_t *the_v3_networkstatus_vote = NULL; - /** Return true iff our opinion of the routers has been stale for long * enough that we should generate a new v2 network status doc. */ static int @@ -1870,10 +1866,9 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, /** Return a new networkstatus_vote_t* containing our current opinion. (For v3 * authorities */ -/* XXXX020 possibly rename and relocate to dirvote.c? */ -static networkstatus_vote_t * -generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, - authority_cert_t *cert) +networkstatus_vote_t * +dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, + authority_cert_t *cert) { or_options_t *options = get_options(); networkstatus_vote_t *v3_out = NULL; @@ -2029,224 +2024,11 @@ generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, return v3_out; } -/** Return a new string containing teh string representation of the vote in - * <b>v3_ns</b>, signed with our v3 signing key <b>private_signing_key</b>. - * For v3 authorities. */ -char * -format_networkstatus_vote(crypto_pk_env_t *private_signing_key, - networkstatus_vote_t *v3_ns) -{ -/** Longest status flag name that we generate. */ -#define LONGEST_STATUS_FLAG_NAME_LEN 9 -/** Maximum number of status flags we'll apply to one router. */ -#define N_STATUS_FLAGS 10 -/** Amount of space to allocate for each entry. (r line and s line.) */ -#define RS_ENTRY_LEN \ - ( /* first line */ \ - MAX_NICKNAME_LEN+BASE64_DIGEST_LEN*2+ISO_TIME_LEN+INET_NTOA_BUF_LEN+ \ - 5*2 /* ports */ + 10 /* punctuation */ + \ - /* second line */ \ - (LONGEST_STATUS_FLAG_NAME_LEN+1)*N_STATUS_FLAGS + 2) - - size_t len; - char *status = NULL; - const char *client_versions = NULL, *server_versions = NULL; - char *outp, *endp; - char fingerprint[FINGERPRINT_LEN+1]; - char ipaddr[INET_NTOA_BUF_LEN]; - char digest[DIGEST_LEN]; - struct in_addr in; - uint32_t addr; - routerlist_t *rl = router_get_routerlist(); - char *version_lines = NULL; - networkstatus_voter_info_t *voter; - - tor_assert(private_signing_key); - - voter = smartlist_get(v3_ns->voters, 0); - - addr = voter->addr; - in.s_addr = htonl(addr); - tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr)); - - base16_encode(fingerprint, sizeof(fingerprint), - v3_ns->cert->cache_info.identity_digest, DIGEST_LEN); - client_versions = v3_ns->client_versions; - server_versions = v3_ns->server_versions; - - if (client_versions || server_versions) { - size_t v_len = 64; - char *cp; - if (client_versions) - v_len += strlen(client_versions); - if (client_versions) - v_len += strlen(server_versions); - version_lines = tor_malloc(v_len); - cp = version_lines; - if (client_versions) { - tor_snprintf(cp, v_len-(cp-version_lines), - "client-versions %s\n", client_versions); - cp += strlen(cp); - } - if (server_versions) - tor_snprintf(cp, v_len-(cp-version_lines), - "server-versions %s\n", server_versions); - } else { - version_lines = tor_strdup(""); - } - - len = 8192; - len += strlen(version_lines); - len += (RS_ENTRY_LEN)*smartlist_len(rl->routers); - len += v3_ns->cert->cache_info.signed_descriptor_len; - - status = tor_malloc(len); - { - char published[ISO_TIME_LEN+1]; - char va[ISO_TIME_LEN+1]; - char fu[ISO_TIME_LEN+1]; - char vu[ISO_TIME_LEN+1]; - char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL); - authority_cert_t *cert = v3_ns->cert; - format_iso_time(published, v3_ns->published); - format_iso_time(va, v3_ns->valid_after); - format_iso_time(fu, v3_ns->fresh_until); - format_iso_time(vu, v3_ns->valid_until); - - tor_assert(cert); - tor_snprintf(status, len, - "network-status-version 3\n" - "vote-status vote\n" - "consensus-methods 1\n" - "published %s\n" - "valid-after %s\n" - "fresh-until %s\n" - "valid-until %s\n" - "voting-delay %d %d\n" - "%s" /* versions */ - "known-flags %s\n" - "dir-source %s %s %s %s %d %d\n" - "contact %s\n", - published, va, fu, vu, - v3_ns->vote_seconds, v3_ns->dist_seconds, - version_lines, - flags, - voter->nickname, fingerprint, voter->address, - ipaddr, voter->dir_port, voter->or_port, voter->contact); - - tor_free(flags); - outp = status + strlen(status); - endp = status + len; - tor_assert(outp + cert->cache_info.signed_descriptor_len < endp); - memcpy(outp, cert->cache_info.signed_descriptor_body, - cert->cache_info.signed_descriptor_len); - - outp += cert->cache_info.signed_descriptor_len; - } - - SMARTLIST_FOREACH(v3_ns->routerstatus_list, vote_routerstatus_t *, vrs, - { - if (routerstatus_format_entry(outp, endp-outp, &vrs->status, - vrs->version, 0) < 0) { - log_warn(LD_BUG, "Unable to print router status."); - goto err; - } - outp += strlen(outp); - }); - - { - char signing_key_fingerprint[FINGERPRINT_LEN+1]; - if (tor_snprintf(outp, endp-outp, "directory-signature ")<0) { - log_warn(LD_BUG, "Unable to start signature line."); - goto err; - } - outp += strlen(outp); - - if (crypto_pk_get_fingerprint(private_signing_key, - signing_key_fingerprint, 0)<0) { - log_warn(LD_BUG, "Unable to get fingerprint for signing key"); - goto err; - } - if (tor_snprintf(outp, endp-outp, "%s %s\n", fingerprint, - signing_key_fingerprint)<0) { - log_warn(LD_BUG, "Unable to end signature line."); - goto err; - } - outp += strlen(outp); - } - - if (router_get_networkstatus_v3_hash(status, digest)<0) - goto err; - note_crypto_pk_op(SIGN_DIR); - if (router_append_dirobj_signature(outp,endp-outp,digest, - private_signing_key)<0) { - log_warn(LD_BUG, "Unable to sign networkstatus vote."); - goto err; - } - - { - networkstatus_vote_t *v; - if (!(v = networkstatus_parse_vote_from_string(status, NULL, 1))) { - log_err(LD_BUG,"Generated a networkstatus vote we couldn't parse: " - "<<%s>>", status); - goto err; - } - networkstatus_vote_free(v); - } - - goto done; - - err: - tor_free(status); - done: - tor_free(version_lines); - return status; -} - -/** Replace the value of <b>the_v3_networkstatus_vote</b> with a - * new vote, and return that value. Returns NULL on failure. */ -/* XXXX020 possibly rename and relocate to dirvote.c? */ -cached_dir_t * -generate_v3_networkstatus(void) -{ - crypto_pk_env_t *key = get_my_v3_authority_signing_key(); - authority_cert_t *cert = get_my_v3_authority_cert(); - networkstatus_vote_t *ns; - char *status; - time_t now = time(NULL); - - if (!cert || !key) { - log_warn(LD_NET, "Didn't find key/certificate to generate v3 vote"); - return NULL; - } - - if (!(ns = generate_networkstatus_vote_obj(key, cert))) - return NULL; - - status = format_networkstatus_vote(key, ns); - networkstatus_vote_free(ns); - if (!status) - return NULL; - - { - cached_dir_t **ns_ptr = - &the_v3_networkstatus_vote; - if (*ns_ptr) - cached_dir_decref(*ns_ptr); - *ns_ptr = new_cached_dir(status, now); - status = NULL; /* So it doesn't get double-freed. */ - } - - return the_v3_networkstatus_vote; -} - -/** For v2 and v3 authoritative directories only: If <b>v2</b> is set, replace - * the contents of <b>the_v2_networkstatus</b> with a newly generated network - * status object. If <b>v2</b> is zero, replace the contents of - * <b>the_v3_networkstatus_vote</b> with a newly generated consensus vote - * object. */ +/** For v2 authoritative directories only: Replace the contents of + * <b>the_v2_networkstatus</b> with a newly generated network status + * object. */ static cached_dir_t * -generate_networkstatus_opinion(int v2) +generate_v2_networkstatus_opinion(void) { /** Longest status flag name that we generate. */ #define LONGEST_STATUS_FLAG_NAME_LEN 9 @@ -2285,9 +2067,6 @@ generate_networkstatus_opinion(int v2) smartlist_t *routers = NULL; digestmap_t *omit_as_sybil = NULL; - if (!v2) - return generate_v3_networkstatus(); - private_key = get_identity_key(); if (resolve_my_address(LOG_WARN, options, &addr, &hostname)<0) { @@ -2460,7 +2239,7 @@ dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result, cached_v2_networkstatus = digestmap_new(); if (should_generate_v2_networkstatus()) - generate_networkstatus_opinion(1); + generate_v2_networkstatus_opinion(); if (!strcmp(key,"authority")) { if (authdir_mode_v2(get_options())) { @@ -2515,7 +2294,7 @@ dirserv_get_networkstatus_v2(smartlist_t *result, SMARTLIST_FOREACH(fingerprints, const char *, fp, { if (router_digest_is_me(fp) && should_generate_v2_networkstatus()) - generate_networkstatus_opinion(1); + generate_v2_networkstatus_opinion(); cached = digestmap_get(cached_v2_networkstatus, fp); if (cached) { smartlist_add(result, cached); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 0a3088230d..81b046f65d 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -19,78 +19,193 @@ static int dirvote_add_signatures_to_pending_consensus( static char *list_v3_auth_ids(void); static void dirvote_fetch_missing_votes(void); static void dirvote_fetch_missing_signatures(void); -static void dirvote_perform_vote(void); +static int dirvote_perform_vote(void); static void dirvote_clear_votes(int all_votes); static int dirvote_compute_consensus(void); static int dirvote_publish_consensus(void); /* ===== - * Voting and consensus generation - * ===== */ + * Voting + * =====*/ -/*XXXX020 move to networkstaus.c */ -/** Clear all storage held in <b>ns</b>. */ -void -networkstatus_vote_free(networkstatus_vote_t *ns) +/** Return a new string containing teh string representation of the vote in + * <b>v3_ns</b>, signed with our v3 signing key <b>private_signing_key</b>. + * For v3 authorities. */ +char * +format_networkstatus_vote(crypto_pk_env_t *private_signing_key, + networkstatus_vote_t *v3_ns) { - if (!ns) - return; - - tor_free(ns->client_versions); - tor_free(ns->server_versions); - if (ns->known_flags) { - SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c)); - smartlist_free(ns->known_flags); +/** Longest status flag name that we generate. */ +#define LONGEST_STATUS_FLAG_NAME_LEN 9 +/** Maximum number of status flags we'll apply to one router. */ +#define N_STATUS_FLAGS 10 +/** Amount of space to allocate for each entry. (r line and s line.) */ +#define RS_ENTRY_LEN \ + ( /* first line */ \ + MAX_NICKNAME_LEN+BASE64_DIGEST_LEN*2+ISO_TIME_LEN+INET_NTOA_BUF_LEN+ \ + 5*2 /* ports */ + 10 /* punctuation */ + \ + /* second line */ \ + (LONGEST_STATUS_FLAG_NAME_LEN+1)*N_STATUS_FLAGS + 2) + + size_t len; + char *status = NULL; + const char *client_versions = NULL, *server_versions = NULL; + char *outp, *endp; + char fingerprint[FINGERPRINT_LEN+1]; + char ipaddr[INET_NTOA_BUF_LEN]; + char digest[DIGEST_LEN]; + struct in_addr in; + uint32_t addr; + routerlist_t *rl = router_get_routerlist(); + char *version_lines = NULL; + networkstatus_voter_info_t *voter; + + tor_assert(private_signing_key); + + voter = smartlist_get(v3_ns->voters, 0); + + addr = voter->addr; + in.s_addr = htonl(addr); + tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr)); + + base16_encode(fingerprint, sizeof(fingerprint), + v3_ns->cert->cache_info.identity_digest, DIGEST_LEN); + client_versions = v3_ns->client_versions; + server_versions = v3_ns->server_versions; + + if (client_versions || server_versions) { + size_t v_len = 64; + char *cp; + if (client_versions) + v_len += strlen(client_versions); + if (client_versions) + v_len += strlen(server_versions); + version_lines = tor_malloc(v_len); + cp = version_lines; + if (client_versions) { + tor_snprintf(cp, v_len-(cp-version_lines), + "client-versions %s\n", client_versions); + cp += strlen(cp); + } + if (server_versions) + tor_snprintf(cp, v_len-(cp-version_lines), + "server-versions %s\n", server_versions); + } else { + version_lines = tor_strdup(""); } - if (ns->voters) { - SMARTLIST_FOREACH(ns->voters, networkstatus_voter_info_t *, voter, - { - tor_free(voter->nickname); - tor_free(voter->address); - tor_free(voter->contact); - }); - smartlist_free(ns->voters); + + len = 8192; + len += strlen(version_lines); + len += (RS_ENTRY_LEN)*smartlist_len(rl->routers); + len += v3_ns->cert->cache_info.signed_descriptor_len; + + status = tor_malloc(len); + { + char published[ISO_TIME_LEN+1]; + char va[ISO_TIME_LEN+1]; + char fu[ISO_TIME_LEN+1]; + char vu[ISO_TIME_LEN+1]; + char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL); + authority_cert_t *cert = v3_ns->cert; + format_iso_time(published, v3_ns->published); + format_iso_time(va, v3_ns->valid_after); + format_iso_time(fu, v3_ns->fresh_until); + format_iso_time(vu, v3_ns->valid_until); + + tor_assert(cert); + tor_snprintf(status, len, + "network-status-version 3\n" + "vote-status vote\n" + "consensus-methods 1\n" + "published %s\n" + "valid-after %s\n" + "fresh-until %s\n" + "valid-until %s\n" + "voting-delay %d %d\n" + "%s" /* versions */ + "known-flags %s\n" + "dir-source %s %s %s %s %d %d\n" + "contact %s\n", + published, va, fu, vu, + v3_ns->vote_seconds, v3_ns->dist_seconds, + version_lines, + flags, + voter->nickname, fingerprint, voter->address, + ipaddr, voter->dir_port, voter->or_port, voter->contact); + + tor_free(flags); + outp = status + strlen(status); + endp = status + len; + tor_assert(outp + cert->cache_info.signed_descriptor_len < endp); + memcpy(outp, cert->cache_info.signed_descriptor_body, + cert->cache_info.signed_descriptor_len); + + outp += cert->cache_info.signed_descriptor_len; } - if (ns->cert) - authority_cert_free(ns->cert); - if (ns->routerstatus_list) { - if (ns->is_vote) { - SMARTLIST_FOREACH(ns->routerstatus_list, vote_routerstatus_t *, rs, - { - tor_free(rs->version); - tor_free(rs); - }); - } else { - SMARTLIST_FOREACH(ns->routerstatus_list, routerstatus_t *, rs, - tor_free(rs)); + SMARTLIST_FOREACH(v3_ns->routerstatus_list, vote_routerstatus_t *, vrs, + { + if (routerstatus_format_entry(outp, endp-outp, &vrs->status, + vrs->version, 0) < 0) { + log_warn(LD_BUG, "Unable to print router status."); + goto err; } + outp += strlen(outp); + }); + + { + char signing_key_fingerprint[FINGERPRINT_LEN+1]; + if (tor_snprintf(outp, endp-outp, "directory-signature ")<0) { + log_warn(LD_BUG, "Unable to start signature line."); + goto err; + } + outp += strlen(outp); - smartlist_free(ns->routerstatus_list); + if (crypto_pk_get_fingerprint(private_signing_key, + signing_key_fingerprint, 0)<0) { + log_warn(LD_BUG, "Unable to get fingerprint for signing key"); + goto err; + } + if (tor_snprintf(outp, endp-outp, "%s %s\n", fingerprint, + signing_key_fingerprint)<0) { + log_warn(LD_BUG, "Unable to end signature line."); + goto err; + } + outp += strlen(outp); } - if (ns->desc_digest_map) - digestmap_free(ns->desc_digest_map, NULL); - memset(ns, 11, sizeof(*ns)); - tor_free(ns); -} + if (router_get_networkstatus_v3_hash(status, digest)<0) + goto err; + note_crypto_pk_op(SIGN_DIR); + if (router_append_dirobj_signature(outp,endp-outp,digest, + private_signing_key)<0) { + log_warn(LD_BUG, "Unable to sign networkstatus vote."); + goto err; + } -/*XXXX020 move to networkstaus.c */ -/** Return the voter info from <b>vote</b> for the voter whose identity digest - * is <b>identity</b>, or NULL if no such voter is associated with - * <b>vote</b>. */ -networkstatus_voter_info_t * -networkstatus_get_voter_by_id(networkstatus_vote_t *vote, - const char *identity) -{ - if (!vote || !vote->voters) - return NULL; - SMARTLIST_FOREACH(vote->voters, networkstatus_voter_info_t *, voter, - if (!memcmp(voter->identity_digest, identity, DIGEST_LEN)) - return voter); - return NULL; + { + networkstatus_vote_t *v; + if (!(v = networkstatus_parse_vote_from_string(status, NULL, 1))) { + log_err(LD_BUG,"Generated a networkstatus vote we couldn't parse: " + "<<%s>>", status); + goto err; + } + networkstatus_vote_free(v); + } + + goto done; + + err: + tor_free(status); + done: + tor_free(version_lines); + return status; } +/* ===== + * Consensus generation + * ===== */ + /** Given a vote <b>vote</b> (not a consensus!), return its associated * networkstatus_voter_info_t.*/ static networkstatus_voter_info_t * @@ -818,155 +933,6 @@ networkstatus_compute_consensus(smartlist_t *votes, return result; } -/*XXXX020 move to networkstatus.c ? */ -/** Check whether the signature on <b>voter</b> is correctly signed by - * the signing key of <b>cert</b>. Return -1 if <b>cert</b> doesn't match the - * signing key; otherwise set the good_signature or bad_signature flag on - * <b>voter</b>, and return 0. */ -/* (private; exposed for testing.) */ -int -networkstatus_check_voter_signature(networkstatus_vote_t *consensus, - networkstatus_voter_info_t *voter, - authority_cert_t *cert) -{ - char d[DIGEST_LEN]; - char *signed_digest; - size_t signed_digest_len; - if (crypto_pk_get_digest(cert->signing_key, d)<0) - return -1; - if (memcmp(voter->signing_key_digest, d, DIGEST_LEN)) - return -1; - signed_digest_len = crypto_pk_keysize(cert->signing_key); - signed_digest = tor_malloc(signed_digest_len); - if (crypto_pk_public_checksig(cert->signing_key, - signed_digest, - voter->signature, - voter->signature_len) != DIGEST_LEN || - memcmp(signed_digest, consensus->networkstatus_digest, DIGEST_LEN)) { - log_warn(LD_DIR, "Got a bad signature on a networkstatus vote"); - voter->bad_signature = 1; - } else { - voter->good_signature = 1; - } - return 0; -} - -/*XXXX020 move to networkstatus.c ? */ -/** Given a v3 networkstatus consensus in <b>consensus</b>, check every - * as-yet-unchecked signature on <b>consensus</b>. Return 1 if there is a - * signature from every recognized authority on it, 0 if there are - * enough good signatures from recognized authorities on it, -1 if we might - * get enough good signatures by fetching missing certificates, and -2 - * otherwise. Log messages at INFO or WARN: if <b>warn</b> is over 1, warn - * about every problem; if warn is at least 1, warn only if we can't get - * enough signatures; if warn is negative, log nothing at all. */ -int -networkstatus_check_consensus_signature(networkstatus_vote_t *consensus, - int warn) -{ - int n_good = 0; - int n_missing_key = 0; - int n_bad = 0; - int n_unknown = 0; - int n_no_signature = 0; - int n_v3_authorities = get_n_authorities(V3_AUTHORITY); - int n_required = n_v3_authorities/2 + 1; - smartlist_t *need_certs_from = smartlist_create(); - smartlist_t *unrecognized = smartlist_create(); - smartlist_t *missing_authorities = smartlist_create(); - int severity; - - tor_assert(! consensus->is_vote); - - SMARTLIST_FOREACH(consensus->voters, networkstatus_voter_info_t *, voter, - { - if (!voter->good_signature && !voter->bad_signature && voter->signature) { - /* we can try to check the signature. */ - authority_cert_t *cert = - authority_cert_get_by_digests(voter->identity_digest, - voter->signing_key_digest); - if (! cert) { - if (!trusteddirserver_get_by_v3_auth_digest(voter->identity_digest)) { - smartlist_add(unrecognized, voter); - ++n_unknown; - } else { - smartlist_add(need_certs_from, voter); - ++n_missing_key; - } - continue; - } - if (networkstatus_check_voter_signature(consensus, voter, cert) < 0) { - smartlist_add(need_certs_from, voter); - ++n_missing_key; - continue; - } - } - if (voter->good_signature) - ++n_good; - else if (voter->bad_signature) - ++n_bad; - else - ++n_no_signature; - }); - - /* Now see whether we're missing any voters entirely. */ - SMARTLIST_FOREACH(router_get_trusted_dir_servers(), - trusted_dir_server_t *, ds, - { - if ((ds->type & V3_AUTHORITY) && - !networkstatus_get_voter_by_id(consensus, ds->v3_identity_digest)) - smartlist_add(missing_authorities, ds); - }); - - if (warn > 1 || (warn >= 0 && n_good < n_required)) - severity = LOG_WARN; - else - severity = LOG_INFO; - - if (warn >= 0) { - SMARTLIST_FOREACH(unrecognized, networkstatus_voter_info_t *, voter, - { - log(severity, LD_DIR, "Consensus includes unrecognized authority '%s' " - "at %s:%d (contact %s; identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, - voter->contact?voter->contact:"n/a", - hex_str(voter->identity_digest, DIGEST_LEN)); - }); - SMARTLIST_FOREACH(need_certs_from, networkstatus_voter_info_t *, voter, - { - log_info(LD_DIR, "Looks like we need to download a new certificate " - "from authority '%s' at %s:%d (contact %s; identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, - voter->contact?voter->contact:"n/a", - hex_str(voter->identity_digest, DIGEST_LEN)); - }); - SMARTLIST_FOREACH(missing_authorities, trusted_dir_server_t *, ds, - { - log(severity, LD_DIR, "Consensus does not include configured " - "authority '%s' at %s:%d (identity %s)", - ds->nickname, ds->address, (int)ds->dir_port, - hex_str(ds->v3_identity_digest, DIGEST_LEN)); - }); - log(severity, LD_DIR, - "%d unknown, %d missing key, %d good, %d bad, %d no signature, " - "%d required", n_unknown, n_missing_key, n_good, n_bad, - n_no_signature, n_required); - } - - smartlist_free(unrecognized); - smartlist_free(need_certs_from); - smartlist_free(missing_authorities); - - if (n_good == n_v3_authorities) - return 1; - else if (n_good >= n_required) - return 0; - else if (n_good + n_missing_key >= n_required) - return -1; - else - return -2; -} - /** Given a consensus vote <b>target</b> and a set of detached signatures in * <b>sigs</b> that correspond to the same consensus, check whether there are * any new signatures in <b>src_voter_list</b> that should be added to @@ -1127,23 +1093,7 @@ ns_detached_signatures_free(ns_detached_signatures_t *s) * Certificate functions * ===== */ -/*XXXX020 move to routerlist.c ? */ -/** Free storage held in <b>cert</b>. */ -void -authority_cert_free(authority_cert_t *cert) -{ - if (!cert) - return; - - tor_free(cert->cache_info.signed_descriptor_body); - if (cert->signing_key) - crypto_free_pk_env(cert->signing_key); - if (cert->identity_key) - crypto_free_pk_env(cert->identity_key); - - tor_free(cert); -} - +/*XXXX020 make this static? */ /** Allocate and return a new authority_cert_t with the same contents as * <b>cert</b>. */ authority_cert_t * @@ -1376,21 +1326,36 @@ static smartlist_t *pending_consensus_signature_list = NULL; /** Generate a networkstatus vote and post it to all the v3 authorities. * (V3 Authority only) */ -static void +static int dirvote_perform_vote(void) { - cached_dir_t *new_vote = generate_v3_networkstatus(); + crypto_pk_env_t *key = get_my_v3_authority_signing_key(); + authority_cert_t *cert = get_my_v3_authority_cert(); + networkstatus_vote_t *ns; + char *contents; pending_vote_t *pending_vote; + int status; const char *msg = ""; - if (!new_vote) - return; + if (!cert || !key) { + log_warn(LD_NET, "Didn't find key/certificate to generate v3 vote"); + return -1; + } + if (!(ns = dirserv_generate_networkstatus_vote_obj(key, cert))) + return -1; + + contents = format_networkstatus_vote(key, ns); + networkstatus_vote_free(ns); + if (!contents) + return -1; - if (!(pending_vote = dirvote_add_vote(new_vote->dir, &msg, &status))) { + pending_vote = dirvote_add_vote(contents, &msg, &status); + tor_free(contents); + if (!pending_vote) { log_warn(LD_DIR, "Couldn't store my own vote! (I told myself, '%s'.)", msg); - return; + return -1; } directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_VOTE, @@ -1399,6 +1364,7 @@ dirvote_perform_vote(void) pending_vote->vote_body->dir, pending_vote->vote_body->dir_len, 0); log_notice(LD_DIR, "Vote posted."); + return 0; } /** Send an HTTP request to every other v3 authority, for the votes of every diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index a9efd8d92e..657895915b 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -210,6 +210,214 @@ networkstatus_v2_free(networkstatus_v2_t *ns) tor_free(ns); } +/** Clear all storage held in <b>ns</b>. */ +void +networkstatus_vote_free(networkstatus_vote_t *ns) +{ + if (!ns) + return; + + tor_free(ns->client_versions); + tor_free(ns->server_versions); + if (ns->known_flags) { + SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c)); + smartlist_free(ns->known_flags); + } + if (ns->voters) { + SMARTLIST_FOREACH(ns->voters, networkstatus_voter_info_t *, voter, + { + tor_free(voter->nickname); + tor_free(voter->address); + tor_free(voter->contact); + }); + smartlist_free(ns->voters); + } + if (ns->cert) + authority_cert_free(ns->cert); + + if (ns->routerstatus_list) { + if (ns->is_vote) { + SMARTLIST_FOREACH(ns->routerstatus_list, vote_routerstatus_t *, rs, + { + tor_free(rs->version); + tor_free(rs); + }); + } else { + SMARTLIST_FOREACH(ns->routerstatus_list, routerstatus_t *, rs, + tor_free(rs)); + } + + smartlist_free(ns->routerstatus_list); + } + if (ns->desc_digest_map) + digestmap_free(ns->desc_digest_map, NULL); + + memset(ns, 11, sizeof(*ns)); + tor_free(ns); +} + +/** Return the voter info from <b>vote</b> for the voter whose identity digest + * is <b>identity</b>, or NULL if no such voter is associated with + * <b>vote</b>. */ +networkstatus_voter_info_t * +networkstatus_get_voter_by_id(networkstatus_vote_t *vote, + const char *identity) +{ + if (!vote || !vote->voters) + return NULL; + SMARTLIST_FOREACH(vote->voters, networkstatus_voter_info_t *, voter, + if (!memcmp(voter->identity_digest, identity, DIGEST_LEN)) + return voter); + return NULL; +} + +/** Check whether the signature on <b>voter</b> is correctly signed by + * the signing key of <b>cert</b>. Return -1 if <b>cert</b> doesn't match the + * signing key; otherwise set the good_signature or bad_signature flag on + * <b>voter</b>, and return 0. */ +/* (private; exposed for testing.) */ +int +networkstatus_check_voter_signature(networkstatus_vote_t *consensus, + networkstatus_voter_info_t *voter, + authority_cert_t *cert) +{ + char d[DIGEST_LEN]; + char *signed_digest; + size_t signed_digest_len; + if (crypto_pk_get_digest(cert->signing_key, d)<0) + return -1; + if (memcmp(voter->signing_key_digest, d, DIGEST_LEN)) + return -1; + signed_digest_len = crypto_pk_keysize(cert->signing_key); + signed_digest = tor_malloc(signed_digest_len); + if (crypto_pk_public_checksig(cert->signing_key, + signed_digest, + voter->signature, + voter->signature_len) != DIGEST_LEN || + memcmp(signed_digest, consensus->networkstatus_digest, DIGEST_LEN)) { + log_warn(LD_DIR, "Got a bad signature on a networkstatus vote"); + voter->bad_signature = 1; + } else { + voter->good_signature = 1; + } + return 0; +} + +/** Given a v3 networkstatus consensus in <b>consensus</b>, check every + * as-yet-unchecked signature on <b>consensus</b>. Return 1 if there is a + * signature from every recognized authority on it, 0 if there are + * enough good signatures from recognized authorities on it, -1 if we might + * get enough good signatures by fetching missing certificates, and -2 + * otherwise. Log messages at INFO or WARN: if <b>warn</b> is over 1, warn + * about every problem; if warn is at least 1, warn only if we can't get + * enough signatures; if warn is negative, log nothing at all. */ +int +networkstatus_check_consensus_signature(networkstatus_vote_t *consensus, + int warn) +{ + int n_good = 0; + int n_missing_key = 0; + int n_bad = 0; + int n_unknown = 0; + int n_no_signature = 0; + int n_v3_authorities = get_n_authorities(V3_AUTHORITY); + int n_required = n_v3_authorities/2 + 1; + smartlist_t *need_certs_from = smartlist_create(); + smartlist_t *unrecognized = smartlist_create(); + smartlist_t *missing_authorities = smartlist_create(); + int severity; + + tor_assert(! consensus->is_vote); + + SMARTLIST_FOREACH(consensus->voters, networkstatus_voter_info_t *, voter, + { + if (!voter->good_signature && !voter->bad_signature && voter->signature) { + /* we can try to check the signature. */ + authority_cert_t *cert = + authority_cert_get_by_digests(voter->identity_digest, + voter->signing_key_digest); + if (! cert) { + if (!trusteddirserver_get_by_v3_auth_digest(voter->identity_digest)) { + smartlist_add(unrecognized, voter); + ++n_unknown; + } else { + smartlist_add(need_certs_from, voter); + ++n_missing_key; + } + continue; + } + if (networkstatus_check_voter_signature(consensus, voter, cert) < 0) { + smartlist_add(need_certs_from, voter); + ++n_missing_key; + continue; + } + } + if (voter->good_signature) + ++n_good; + else if (voter->bad_signature) + ++n_bad; + else + ++n_no_signature; + }); + + /* Now see whether we're missing any voters entirely. */ + SMARTLIST_FOREACH(router_get_trusted_dir_servers(), + trusted_dir_server_t *, ds, + { + if ((ds->type & V3_AUTHORITY) && + !networkstatus_get_voter_by_id(consensus, ds->v3_identity_digest)) + smartlist_add(missing_authorities, ds); + }); + + if (warn > 1 || (warn >= 0 && n_good < n_required)) + severity = LOG_WARN; + else + severity = LOG_INFO; + + if (warn >= 0) { + SMARTLIST_FOREACH(unrecognized, networkstatus_voter_info_t *, voter, + { + log(severity, LD_DIR, "Consensus includes unrecognized authority '%s' " + "at %s:%d (contact %s; identity %s)", + voter->nickname, voter->address, (int)voter->dir_port, + voter->contact?voter->contact:"n/a", + hex_str(voter->identity_digest, DIGEST_LEN)); + }); + SMARTLIST_FOREACH(need_certs_from, networkstatus_voter_info_t *, voter, + { + log_info(LD_DIR, "Looks like we need to download a new certificate " + "from authority '%s' at %s:%d (contact %s; identity %s)", + voter->nickname, voter->address, (int)voter->dir_port, + voter->contact?voter->contact:"n/a", + hex_str(voter->identity_digest, DIGEST_LEN)); + }); + SMARTLIST_FOREACH(missing_authorities, trusted_dir_server_t *, ds, + { + log(severity, LD_DIR, "Consensus does not include configured " + "authority '%s' at %s:%d (identity %s)", + ds->nickname, ds->address, (int)ds->dir_port, + hex_str(ds->v3_identity_digest, DIGEST_LEN)); + }); + log(severity, LD_DIR, + "%d unknown, %d missing key, %d good, %d bad, %d no signature, " + "%d required", n_unknown, n_missing_key, n_good, n_bad, + n_no_signature, n_required); + } + + smartlist_free(unrecognized); + smartlist_free(need_certs_from); + smartlist_free(missing_authorities); + + if (n_good == n_v3_authorities) + return 1; + else if (n_good >= n_required) + return 0; + else if (n_good + n_missing_key >= n_required) + return -1; + else + return -2; +} + /** Helper: return a newly allocated string containing the name of the filename * where we plan to cache the network status with the given identity digest. */ char * diff --git a/src/or/or.h b/src/or/or.h index 677c30a391..d50e78c1ae 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2898,14 +2898,6 @@ void dirserv_free_all(void); void cached_dir_decref(cached_dir_t *d); cached_dir_t *new_cached_dir(char *s, time_t published); -cached_dir_t *generate_v3_networkstatus(void); - -#ifdef DIRSERV_PRIVATE -char * -format_networkstatus_vote(crypto_pk_env_t *private_key, - networkstatus_vote_t *v3_ns); -#endif - /********************************* dirvote.c ************************/ /** Lowest allowable value for VoteSeconds. */ @@ -2918,16 +2910,10 @@ format_networkstatus_vote(crypto_pk_env_t *private_key, void dirvote_free_all(void); /* vote manipulation */ -void networkstatus_vote_free(networkstatus_vote_t *ns); char *networkstatus_compute_consensus(smartlist_t *votes, int total_authorities, crypto_pk_env_t *identity_key, crypto_pk_env_t *signing_key); -networkstatus_voter_info_t *networkstatus_get_voter_by_id( - networkstatus_vote_t *vote, - const char *identity); -int networkstatus_check_consensus_signature(networkstatus_vote_t *consensus, - int warn); int networkstatus_add_detached_signatures(networkstatus_vote_t *target, ns_detached_signatures_t *sigs, const char **msg_out); @@ -2935,7 +2921,6 @@ char *networkstatus_get_detached_signatures(networkstatus_vote_t *consensus); void ns_detached_signatures_free(ns_detached_signatures_t *s); /* cert manipulation */ -void authority_cert_free(authority_cert_t *cert); authority_cert_t *authority_cert_dup(authority_cert_t *cert); /** Describes the schedule by which votes should be generated. */ @@ -2965,11 +2950,14 @@ const char *dirvote_get_pending_detached_signatures(void); #define DGV_INCLUDE_PENDING 2 #define DGV_INCLUDE_PREVIOUS 4 const cached_dir_t *dirvote_get_vote(const char *fp, int flags); +networkstatus_vote_t * +dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key, + authority_cert_t *cert); #ifdef DIRVOTE_PRIVATE -int networkstatus_check_voter_signature(networkstatus_vote_t *consensus, - networkstatus_voter_info_t *voter, - authority_cert_t *cert); +char * +format_networkstatus_vote(crypto_pk_env_t *private_key, + networkstatus_vote_t *v3_ns); #endif /********************************* dns.c ***************************/ @@ -3084,6 +3072,15 @@ int router_reload_v2_networkstatus(void); int router_reload_consensus_networkstatus(void); void routerstatus_free(routerstatus_t *rs); void networkstatus_v2_free(networkstatus_v2_t *ns); +void networkstatus_vote_free(networkstatus_vote_t *ns); +networkstatus_voter_info_t *networkstatus_get_voter_by_id( + networkstatus_vote_t *vote, + const char *identity); +int networkstatus_check_consensus_signature(networkstatus_vote_t *consensus, + int warn); +int networkstatus_check_voter_signature(networkstatus_vote_t *consensus, + networkstatus_voter_info_t *voter, + authority_cert_t *cert); char *networkstatus_get_cache_filename(const char *identity_digest); int router_set_networkstatus_v2(const char *s, time_t arrived_at, networkstatus_source_t source, @@ -3639,6 +3636,7 @@ void add_trusted_dir_server(const char *nickname, const char *address, uint16_t dir_port, uint16_t or_port, const char *digest, const char *v3_auth_digest, authority_type_t type); +void authority_cert_free(authority_cert_t *cert); void clear_trusted_dir_servers(void); int any_trusted_dir_is_v1_authority(void); void update_router_descriptor_downloads(time_t now); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index b62a48f041..95e63df3a9 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -3300,6 +3300,22 @@ add_trusted_dir_server(const char *nickname, const char *address, router_dir_info_changed(); } +/** Free storage held in <b>cert</b>. */ +void +authority_cert_free(authority_cert_t *cert) +{ + if (!cert) + return; + + tor_free(cert->cache_info.signed_descriptor_body); + if (cert->signing_key) + crypto_free_pk_env(cert->signing_key); + if (cert->identity_key) + crypto_free_pk_env(cert->identity_key); + + tor_free(cert); +} + /** Free storage held in <b>ds</b>. */ static void trusted_dir_server_free(trusted_dir_server_t *ds) |