diff options
Diffstat (limited to 'src/feature/dirauth')
44 files changed, 2720 insertions, 716 deletions
diff --git a/src/feature/dirauth/.may_include b/src/feature/dirauth/.may_include new file mode 100644 index 0000000000..a9bb274699 --- /dev/null +++ b/src/feature/dirauth/.may_include @@ -0,0 +1,2 @@ +*.h +feature/dirauth/*.inc diff --git a/src/feature/dirauth/authmode.c b/src/feature/dirauth/authmode.c index 29fcc6d1a9..0fde7bc679 100644 --- a/src/feature/dirauth/authmode.c +++ b/src/feature/dirauth/authmode.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -26,6 +26,15 @@ authdir_mode(const or_options_t *options) { return options->AuthoritativeDir != 0; } + +/* Return true iff we believe ourselves to be a v3 authoritative directory + * server. */ +int +authdir_mode_v3(const or_options_t *options) +{ + return authdir_mode(options) && options->V3AuthoritativeDir != 0; +} + /** Return true iff we are an authoritative directory server that is * authoritative about receiving and serving descriptors of type * <b>purpose</b> on its dirport. diff --git a/src/feature/dirauth/authmode.h b/src/feature/dirauth/authmode.h index 876a1f947b..6e6ba7f8ae 100644 --- a/src/feature/dirauth/authmode.h +++ b/src/feature/dirauth/authmode.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018-2019, The Tor Project, Inc. */ +/* Copyright (c) 2018-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,22 +14,16 @@ #ifdef HAVE_MODULE_DIRAUTH int authdir_mode(const or_options_t *options); +int authdir_mode_v3(const or_options_t *options); int authdir_mode_handles_descs(const or_options_t *options, int purpose); int authdir_mode_publishes_statuses(const or_options_t *options); int authdir_mode_tests_reachability(const or_options_t *options); int authdir_mode_bridge(const or_options_t *options); -/* Return true iff we believe ourselves to be a v3 authoritative directory - * server. */ -static inline int -authdir_mode_v3(const or_options_t *options) -{ - return authdir_mode(options) && options->V3AuthoritativeDir != 0; -} - +/* Is the dirauth module enabled? */ #define have_module_dirauth() (1) -#else /* HAVE_MODULE_DIRAUTH */ +#else /* !defined(HAVE_MODULE_DIRAUTH) */ #define authdir_mode(options) (((void)(options)),0) #define authdir_mode_handles_descs(options,purpose) \ @@ -41,6 +35,6 @@ authdir_mode_v3(const or_options_t *options) #define have_module_dirauth() (0) -#endif /* HAVE_MODULE_DIRAUTH */ +#endif /* defined(HAVE_MODULE_DIRAUTH) */ -#endif /* TOR_MODE_H */ +#endif /* !defined(TOR_DIRAUTH_MODE_H) */ diff --git a/src/feature/dirauth/bridgeauth.c b/src/feature/dirauth/bridgeauth.c new file mode 100644 index 0000000000..b7bf3e4e04 --- /dev/null +++ b/src/feature/dirauth/bridgeauth.c @@ -0,0 +1,60 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file bridgeauth.c + * @brief Bridge authority code + **/ + +#include "core/or/or.h" +#include "feature/dirauth/bridgeauth.h" +#include "feature/dirauth/voteflags.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/relay/router.h" +#include "app/config/config.h" + +#include "feature/nodelist/routerinfo_st.h" + +/** Write out router status entries for all our bridge descriptors. Here, we + * also mark routers as running. */ +void +bridgeauth_dump_bridge_status_to_file(time_t now) +{ + char *status; + char *fname = NULL; + char *thresholds = NULL; + char *published_thresholds_and_status = NULL; + char published[ISO_TIME_LEN+1]; + const routerinfo_t *me = router_get_my_routerinfo(); + char fingerprint[FINGERPRINT_LEN+1]; + char *fingerprint_line = NULL; + + dirserv_set_bridges_running(now); + status = networkstatus_getinfo_by_purpose("bridge", now); + + if (me && crypto_pk_get_fingerprint(me->identity_pkey, + fingerprint, 0) >= 0) { + tor_asprintf(&fingerprint_line, "fingerprint %s\n", fingerprint); + } else { + log_warn(LD_BUG, "Error computing fingerprint for bridge status."); + } + format_iso_time(published, now); + dirserv_compute_bridge_flag_thresholds(); + thresholds = dirserv_get_flag_thresholds_line(); + tor_asprintf(&published_thresholds_and_status, + "published %s\nflag-thresholds %s\n%s%s", + published, thresholds, fingerprint_line ? fingerprint_line : "", + status); + fname = get_datadir_fname("networkstatus-bridges"); + if (write_str_to_file(fname,published_thresholds_and_status,0)<0) { + log_warn(LD_DIRSERV, "Unable to write networkstatus-bridges file."); + } + tor_free(thresholds); + tor_free(published_thresholds_and_status); + tor_free(fname); + tor_free(status); + tor_free(fingerprint_line); +} diff --git a/src/feature/dirauth/bridgeauth.h b/src/feature/dirauth/bridgeauth.h new file mode 100644 index 0000000000..382d1cfcb8 --- /dev/null +++ b/src/feature/dirauth/bridgeauth.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file bridgeauth.h + * @brief Header for bridgeauth.c + **/ + +#ifndef TOR_DIRAUTH_BRIDGEAUTH_H +#define TOR_DIRAUTH_BRIDGEAUTH_H + +void bridgeauth_dump_bridge_status_to_file(time_t now); + +#endif /* !defined(TOR_DIRAUTH_BRIDGEAUTH_H) */ diff --git a/src/feature/dirauth/bwauth.c b/src/feature/dirauth/bwauth.c index 12f9399e9f..ff0c78f018 100644 --- a/src/feature/dirauth/bwauth.c +++ b/src/feature/dirauth/bwauth.c @@ -1,6 +1,6 @@ /* 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 */ /** @@ -13,13 +13,16 @@ #include "feature/dirauth/bwauth.h" #include "app/config/config.h" +#include "feature/dirauth/dirauth_sys.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/routerlist.h" #include "feature/dirparse/ns_parse.h" +#include "feature/dirauth/dirauth_options_st.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/vote_routerstatus_st.h" +#include "lib/crypt_ops/crypto_format.h" #include "lib/encoding/keyval.h" /** Total number of routers with measured bandwidth; this is set by @@ -55,7 +58,7 @@ dirserv_get_last_n_measured_bws(void) } /** Measured bandwidth cache entry */ -typedef struct mbw_cache_entry_s { +typedef struct mbw_cache_entry_t { long mbw_kb; time_t as_of; } mbw_cache_entry_t; @@ -181,7 +184,7 @@ dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri) /* Check if we have a measured bandwidth, and check the threshold if not */ if (!(dirserv_query_measured_bw_cache_kb(ri->cache_info.identity_digest, &mbw_kb, NULL))) { - threshold = get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised; + threshold = dirauth_get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised; if (routers_with_measured_bw > threshold) { /* Return zero for unmeasured bandwidth if we are above threshold */ bw_kb = 0; @@ -198,14 +201,38 @@ dirserv_get_credible_bandwidth_kb(const routerinfo_t *ri) } /** - * Read the measured bandwidth list file, apply it to the list of - * vote_routerstatus_t and store all the headers in <b>bw_file_headers</b>. + * Read the measured bandwidth list <b>from_file</b>: + * - store all the headers in <b>bw_file_headers</b>, + * - apply bandwidth lines to the list of vote_routerstatus_t in + * <b>routerstatuses</b>, + * - cache bandwidth lines for dirserv_get_bandwidth_for_router(), + * - expire old entries in the measured bandwidth cache, and + * - store the DIGEST_SHA256 of the contents of the file in <b>digest_out</b>. + * * Returns -1 on error, 0 otherwise. + * + * If the file can't be read, or is empty: + * - <b>bw_file_headers</b> is empty, + * - <b>routerstatuses</b> is not modified, + * - the measured bandwidth cache is not modified, and + * - <b>digest_out</b> is the zero-byte digest. + * + * Otherwise, if there is an error later in the file: + * - <b>bw_file_headers</b> contains all the headers up to the error, + * - <b>routerstatuses</b> is updated with all the relay lines up to the error, + * - the measured bandwidth cache is updated with all the relay lines up to + * the error, + * - if the timestamp is valid and recent, old entries in the measured + * bandwidth cache are expired, and + * - <b>digest_out</b> is the digest up to the first read error (if any). + * The digest is taken over all the readable file contents, even if the + * file is outdated or unparseable. */ int dirserv_read_measured_bandwidths(const char *from_file, smartlist_t *routerstatuses, - smartlist_t *bw_file_headers) + smartlist_t *bw_file_headers, + uint8_t *digest_out) { FILE *fp = tor_fopen_cloexec(from_file, "r"); int applied_lines = 0; @@ -219,8 +246,7 @@ dirserv_read_measured_bandwidths(const char *from_file, int rv = -1; char *line = NULL; size_t n = 0; - - /* Initialise line, so that we can't possibly run off the end. */ + crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA256); if (fp == NULL) { log_warn(LD_CONFIG, "Can't open bandwidth file at configured location: %s", @@ -228,16 +254,18 @@ dirserv_read_measured_bandwidths(const char *from_file, goto err; } - /* If fgets fails, line is either unmodified, or indeterminate. */ if (tor_getline(&line,&n,fp) <= 0) { log_warn(LD_DIRSERV, "Empty bandwidth file"); goto err; } + /* If the line could be gotten, add it to the digest */ + crypto_digest_add_bytes(digest, (const char *) line, strlen(line)); if (!strlen(line) || line[strlen(line)-1] != '\n') { log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s", escaped(line)); - goto err; + /* Continue adding lines to the digest. */ + goto continue_digest; } line[strlen(line)-1] = '\0'; @@ -245,14 +273,14 @@ dirserv_read_measured_bandwidths(const char *from_file, if (!ok) { log_warn(LD_DIRSERV, "Non-integer time in bandwidth file: %s", escaped(line)); - goto err; + goto continue_digest; } - now = time(NULL); + now = approx_time(); if ((now - file_time) > MAX_MEASUREMENT_AGE) { log_warn(LD_DIRSERV, "Bandwidth measurement file stale. Age: %u", (unsigned)(time(NULL) - file_time)); - goto err; + goto continue_digest; } /* If timestamp was correct and bw_file_headers is not NULL, @@ -267,6 +295,7 @@ dirserv_read_measured_bandwidths(const char *from_file, while (!feof(fp)) { measured_bw_line_t parsed_line; if (tor_getline(&line, &n, fp) >= 0) { + crypto_digest_add_bytes(digest, (const char *) line, strlen(line)); if (measured_bw_line_parse(&parsed_line, line, line_is_after_headers) != -1) { /* This condition will be true when the first complete valid bw line @@ -305,6 +334,14 @@ dirserv_read_measured_bandwidths(const char *from_file, "Applied %d measurements.", applied_lines); rv = 0; + continue_digest: + /* Continue parsing lines to return the digest of the Bandwidth File. */ + while (!feof(fp)) { + if (tor_getline(&line, &n, fp) >= 0) { + crypto_digest_add_bytes(digest, (const char *) line, strlen(line)); + } + } + err: if (line) { // we need to raw_free this buffer because we got it from tor_getdelim() @@ -312,6 +349,9 @@ dirserv_read_measured_bandwidths(const char *from_file, } if (fp) fclose(fp); + if (digest_out) + crypto_digest_get_digest(digest, (char *) digest_out, DIGEST256_LEN); + crypto_digest_free(digest); return rv; } @@ -327,6 +367,9 @@ dirserv_read_measured_bandwidths(const char *from_file, * the header block yet. If we encounter an incomplete bw line, return -1 but * don't warn since there could be additional header lines coming. If we * encounter a proper bw line, return 0 (and we got past the headers). + * + * If the line contains "vote=0", stop parsing it, and return -1, so that the + * line is ignored during voting. */ STATIC int measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line, diff --git a/src/feature/dirauth/bwauth.h b/src/feature/dirauth/bwauth.h index 4507728458..849c58e2fc 100644 --- a/src/feature/dirauth/bwauth.h +++ b/src/feature/dirauth/bwauth.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -21,8 +21,8 @@ int dirserv_read_measured_bandwidths(const char *from_file, smartlist_t *routerstatuses, - smartlist_t *bw_file_headers); - + smartlist_t *bw_file_headers, + uint8_t *digest_out); int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_out, time_t *as_of_out); @@ -55,4 +55,4 @@ STATIC void dirserv_cache_measured_bw(const measured_bw_line_t *parsed_line, STATIC void dirserv_expire_measured_bw_cache(time_t now); #endif /* defined(BWAUTH_PRIVATE) */ -#endif +#endif /* !defined(TOR_BWAUTH_H) */ diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c new file mode 100644 index 0000000000..a0b6de7eca --- /dev/null +++ b/src/feature/dirauth/dirauth_config.c @@ -0,0 +1,471 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_config.c + * @brief Code to interpret the user's configuration of Tor's directory + * authority module. + **/ + +#include "orconfig.h" +#include "feature/dirauth/dirauth_config.h" + +#include "lib/encoding/confline.h" +#include "lib/confmgt/confmgt.h" +#include "lib/conf/confdecl.h" + +/* Required for dirinfo_type_t in or_options_t */ +#include "core/or/or.h" +#include "app/config/config.h" +#include "app/config/resolve_addr.h" + +#include "feature/dirauth/voting_schedule.h" +#include "feature/stats/rephist.h" + +#include "feature/dirauth/authmode.h" +#include "feature/dirauth/bwauth.h" +#include "feature/dirauth/dirauth_periodic.h" +#include "feature/dirauth/dirauth_sys.h" +#include "feature/dirauth/dirvote.h" +#include "feature/dirauth/guardfraction.h" +#include "feature/dirauth/dirauth_options_st.h" + +/* Copied from config.c, we will refactor later in 29211. */ +#define REJECT(arg) \ + STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END +#if defined(__GNUC__) && __GNUC__ <= 3 +#define COMPLAIN(args...) \ + STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END +#else +#define COMPLAIN(args, ...) \ + STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END +#endif /* defined(__GNUC__) && __GNUC__ <= 3 */ + +#define YES_IF_CHANGED_INT(opt) \ + if (!CFG_EQ_INT(old_options, new_options, opt)) return 1; + +/** Return true iff we are configured to reject request under load for non + * relay connections. */ +bool +dirauth_should_reject_requests_under_load(void) +{ + return !!dirauth_get_options()->AuthDirRejectRequestsUnderLoad; +} + +/** + * Legacy validation/normalization function for the dirauth mode options in + * options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_dirauth_mode(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!authdir_mode(options)) + return 0; + + /* confirm that our address isn't broken, so we can complain now */ + uint32_t tmp; + if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0) + REJECT("Failed to resolve/guess local address. See logs for details."); + + if (!options->ContactInfo && !options->TestingTorNetwork) + REJECT("Authoritative directory servers must set ContactInfo"); + + if (options->UseEntryGuards) { + log_info(LD_CONFIG, "Authoritative directory servers can't set " + "UseEntryGuards. Disabling."); + options->UseEntryGuards = 0; + } + if (!options->DownloadExtraInfo && authdir_mode_v3(options)) { + log_info(LD_CONFIG, "Authoritative directories always try to download " + "extra-info documents. Setting DownloadExtraInfo."); + options->DownloadExtraInfo = 1; + } + if (!(options->BridgeAuthoritativeDir || + options->V3AuthoritativeDir)) + REJECT("AuthoritativeDir is set, but none of " + "(Bridge/V3)AuthoritativeDir is set."); + + /* If we have a v3bandwidthsfile and it's broken, complain on startup */ + if (options->V3BandwidthsFile && !old_options) { + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL, + NULL); + } + /* same for guardfraction file */ + if (options->GuardfractionFile && !old_options) { + dirserv_read_guardfraction_file(options->GuardfractionFile, NULL); + } + + if (!options->DirPort_set) + REJECT("Running as authoritative directory, but no DirPort set."); + + if (!options->ORPort_set) + REJECT("Running as authoritative directory, but no ORPort set."); + + if (options->ClientOnly) + REJECT("Running as authoritative directory, but ClientOnly also set."); + + return 0; +} + +/** + * Legacy validation/normalization function for the dirauth schedule options + * in options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_dirauth_schedule(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!authdir_mode_v3(options)) + return 0; + + if (options->V3AuthVoteDelay + options->V3AuthDistDelay >= + options->V3AuthVotingInterval/2) { + REJECT("V3AuthVoteDelay plus V3AuthDistDelay must be less than half " + "V3AuthVotingInterval"); + } + + if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS) { + if (options->TestingTorNetwork) { + if (options->V3AuthVoteDelay < MIN_VOTE_SECONDS_TESTING) { + REJECT("V3AuthVoteDelay is way too low."); + } else { + COMPLAIN("V3AuthVoteDelay is very low. " + "This may lead to failure to vote for a consensus."); + } + } else { + REJECT("V3AuthVoteDelay is way too low."); + } + } + + if (options->V3AuthDistDelay < MIN_DIST_SECONDS) { + if (options->TestingTorNetwork) { + if (options->V3AuthDistDelay < MIN_DIST_SECONDS_TESTING) { + REJECT("V3AuthDistDelay is way too low."); + } else { + COMPLAIN("V3AuthDistDelay is very low. " + "This may lead to missing votes in a consensus."); + } + } else { + REJECT("V3AuthDistDelay is way too low."); + } + } + + if (options->V3AuthNIntervalsValid < 2) + REJECT("V3AuthNIntervalsValid must be at least 2."); + + if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL) { + if (options->TestingTorNetwork) { + if (options->V3AuthVotingInterval < MIN_VOTE_INTERVAL_TESTING) { + /* Unreachable, covered by earlier checks */ + REJECT("V3AuthVotingInterval is insanely low."); /* LCOV_EXCL_LINE */ + } else { + COMPLAIN("V3AuthVotingInterval is very low. " + "This may lead to failure to synchronise for a consensus."); + } + } else { + REJECT("V3AuthVotingInterval is insanely low."); + } + } else if (options->V3AuthVotingInterval > 24*60*60) { + REJECT("V3AuthVotingInterval is insanely high."); + } else if (((24*60*60) % options->V3AuthVotingInterval) != 0) { + COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours."); + } + + return 0; +} + +/** + * Legacy validation/normalization function for the dirauth testing options + * in options. Uses old_options as the previous options. + * + * Returns 0 on success, returns -1 and sets *msg to a newly allocated string + * on error. + */ +int +options_validate_dirauth_testing(const or_options_t *old_options, + or_options_t *options, + char **msg) +{ + (void)old_options; + + if (BUG(!options)) + return -1; + + if (BUG(!msg)) + return -1; + + if (!authdir_mode(options)) + return 0; + + if (!authdir_mode_v3(options)) + return 0; + + if (options->TestingV3AuthInitialVotingInterval + < MIN_VOTE_INTERVAL_TESTING_INITIAL) { + REJECT("TestingV3AuthInitialVotingInterval is insanely low."); + } else if (((30*60) % options->TestingV3AuthInitialVotingInterval) != 0) { + REJECT("TestingV3AuthInitialVotingInterval does not divide evenly into " + "30 minutes."); + } + + if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS_TESTING) { + REJECT("TestingV3AuthInitialVoteDelay is way too low."); + } + + if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS_TESTING) { + REJECT("TestingV3AuthInitialDistDelay is way too low."); + } + + if (options->TestingV3AuthInitialVoteDelay + + options->TestingV3AuthInitialDistDelay >= + options->TestingV3AuthInitialVotingInterval) { + REJECT("TestingV3AuthInitialVoteDelay plus TestingV3AuthInitialDistDelay " + "must be less than TestingV3AuthInitialVotingInterval"); + } + + if (options->TestingV3AuthVotingStartOffset > + MIN(options->TestingV3AuthInitialVotingInterval, + options->V3AuthVotingInterval)) { + REJECT("TestingV3AuthVotingStartOffset is higher than the voting " + "interval."); + } else if (options->TestingV3AuthVotingStartOffset < 0) { + REJECT("TestingV3AuthVotingStartOffset must be non-negative."); + } + + return 0; +} + +/** + * Return true if changing the configuration from <b>old</b> to <b>new</b> + * affects the timing of the voting subsystem + */ +static int +options_transition_affects_dirauth_timing(const or_options_t *old_options, + const or_options_t *new_options) +{ + tor_assert(old_options); + tor_assert(new_options); + + if (authdir_mode_v3(old_options) != authdir_mode_v3(new_options)) + return 1; + if (! authdir_mode_v3(new_options)) + return 0; + + YES_IF_CHANGED_INT(V3AuthVotingInterval); + YES_IF_CHANGED_INT(V3AuthVoteDelay); + YES_IF_CHANGED_INT(V3AuthDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialVotingInterval); + YES_IF_CHANGED_INT(TestingV3AuthInitialVoteDelay); + YES_IF_CHANGED_INT(TestingV3AuthInitialDistDelay); + YES_IF_CHANGED_INT(TestingV3AuthVotingStartOffset); + + return 0; +} + +/** Fetch the active option list, and take dirauth actions based on it. All of + * the things we do should survive being done repeatedly. If present, + * <b>old_options</b> contains the previous value of the options. + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_dirauth(const or_options_t *old_options) +{ + const or_options_t *options = get_options(); + + /* We may need to reschedule some dirauth stuff if our status changed. */ + if (old_options) { + if (options_transition_affects_dirauth_timing(old_options, options)) { + dirauth_sched_recalculate_timing(options, time(NULL)); + reschedule_dirvote(options); + } + } + + return 0; +} + +/** Fetch the active option list, and take dirauth mtbf actions based on it. + * All of the things we do should survive being done repeatedly. If present, + * <b>old_options</b> contains the previous value of the options. + * + * Must be called immediately after a successful or_state_load(). + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_dirauth_mtbf(const or_options_t *old_options) +{ + (void)old_options; + + const or_options_t *options = get_options(); + int running_tor = options->command == CMD_RUN_TOR; + + if (!authdir_mode(options)) + return 0; + + /* Load dirauth state */ + if (running_tor) { + rep_hist_load_mtbf_data(time(NULL)); + } + + return 0; +} + +/** Fetch the active option list, and take dirauth statistics actions based + * on it. All of the things we do should survive being done repeatedly. If + * present, <b>old_options</b> contains the previous value of the options. + * + * Sets <b>*print_notice_out</b> if we enabled stats, and need to print + * a stats log using options_act_relay_stats_msg(). + * + * Return 0 if all goes well, return -1 if it's time to die. + * + * Note: We haven't moved all the "act on new configuration" logic + * into the options_act* functions yet. Some is still in do_hup() and other + * places. + */ +int +options_act_dirauth_stats(const or_options_t *old_options, + bool *print_notice_out) +{ + if (BUG(!print_notice_out)) + return -1; + + const or_options_t *options = get_options(); + + if (authdir_mode_bridge(options)) { + time_t now = time(NULL); + int print_notice = 0; + + if (!old_options || !authdir_mode_bridge(old_options)) { + rep_hist_desc_stats_init(now); + print_notice = 1; + } + if (print_notice) + *print_notice_out = 1; + } + + /* If we used to have statistics enabled but we just disabled them, + stop gathering them. */ + if (old_options && authdir_mode_bridge(old_options) && + !authdir_mode_bridge(options)) + rep_hist_desc_stats_term(); + + return 0; +} + +/** + * Make any necessary modifications to a dirauth_options_t that occur + * before validation. On success return 0; on failure return -1 and + * set *<b>msg_out</b> to a newly allocated error string. + **/ +static int +dirauth_options_pre_normalize(void *arg, char **msg_out) +{ + dirauth_options_t *options = arg; + (void)msg_out; + + if (!options->RecommendedClientVersions) + options->RecommendedClientVersions = + config_lines_dup(options->RecommendedVersions); + if (!options->RecommendedServerVersions) + options->RecommendedServerVersions = + config_lines_dup(options->RecommendedVersions); + + if (config_ensure_bandwidth_cap(&options->AuthDirFastGuarantee, + "AuthDirFastGuarantee", msg_out) < 0) + return -1; + if (config_ensure_bandwidth_cap(&options->AuthDirGuardBWGuarantee, + "AuthDirGuardBWGuarantee", msg_out) < 0) + return -1; + + return 0; +} + +/** + * Check whether a dirauth_options_t is correct. + * + * On success return 0; on failure return -1 and set *<b>msg_out</b> to a + * newly allocated error string. + **/ +static int +dirauth_options_validate(const void *arg, char **msg) +{ + const dirauth_options_t *options = arg; + + if (options->VersioningAuthoritativeDirectory && + (!options->RecommendedClientVersions || + !options->RecommendedServerVersions)) { + REJECT("Versioning authoritative dir servers must set " + "Recommended*Versions."); + } + + char *t; + /* Call these functions to produce warnings only. */ + t = format_recommended_version_list(options->RecommendedClientVersions, 1); + tor_free(t); + t = format_recommended_version_list(options->RecommendedServerVersions, 1); + tor_free(t); + + if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) { + COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high."); + } + + return 0; +} + +/* Declare the options field table for dirauth_options */ +#define CONF_CONTEXT TABLE +#include "feature/dirauth/dirauth_options.inc" +#undef CONF_CONTEXT + +/** Magic number for dirauth_options_t. */ +#define DIRAUTH_OPTIONS_MAGIC 0x41757448 + +/** + * Declare the configuration options for the dirauth module. + **/ +const config_format_t dirauth_options_fmt = { + .size = sizeof(dirauth_options_t), + .magic = { "dirauth_options_t", + DIRAUTH_OPTIONS_MAGIC, + offsetof(dirauth_options_t, magic) }, + .vars = dirauth_options_t_vars, + + .pre_normalize_fn = dirauth_options_pre_normalize, + .validate_fn = dirauth_options_validate +}; diff --git a/src/feature/dirauth/dirauth_config.h b/src/feature/dirauth/dirauth_config.h new file mode 100644 index 0000000000..9042ff8779 --- /dev/null +++ b/src/feature/dirauth/dirauth_config.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_config.h + * @brief Header for feature/dirauth/dirauth_config.c + **/ + +#ifndef TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H +#define TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H + +struct or_options_t; + +#ifdef HAVE_MODULE_DIRAUTH + +#include "lib/cc/torint.h" + +int options_validate_dirauth_mode(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_dirauth_schedule(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_validate_dirauth_testing(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg); + +int options_act_dirauth(const struct or_options_t *old_options); +int options_act_dirauth_mtbf(const struct or_options_t *old_options); +int options_act_dirauth_stats(const struct or_options_t *old_options, + bool *print_notice_out); + +bool dirauth_should_reject_requests_under_load(void); + +extern const struct config_format_t dirauth_options_fmt; + +#else /* !defined(HAVE_MODULE_DIRAUTH) */ + +/** When tor is compiled with the dirauth module disabled, it can't be + * configured as a directory authority. + * + * Returns -1 and sets msg to a newly allocated string, if AuthoritativeDir + * is set in options. Otherwise returns 0. */ +static inline int +options_validate_dirauth_mode(const struct or_options_t *old_options, + struct or_options_t *options, + char **msg) +{ + (void)old_options; + + /* Only check the primary option for now, #29211 will disable more + * options. */ + if (options->AuthoritativeDir) { + /* REJECT() this configuration */ + *msg = tor_strdup("This tor was built with dirauth mode disabled. " + "It can not be configured with AuthoritativeDir 1."); + return -1; + } + + return 0; +} + +#define options_validate_dirauth_schedule(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) +#define options_validate_dirauth_testing(old_options, options, msg) \ + (((void)(old_options)),((void)(options)),((void)(msg)),0) + +#define options_act_dirauth(old_options) \ + (((void)(old_options)),0) +#define options_act_dirauth_mtbf(old_options) \ + (((void)(old_options)),0) + +static inline int +options_act_dirauth_stats(const struct or_options_t *old_options, + bool *print_notice_out) +{ + (void)old_options; + *print_notice_out = 0; + return 0; +} + +#define dirauth_should_reject_requests_under_load() (false) + +#endif /* defined(HAVE_MODULE_DIRAUTH) */ + +#endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H) */ diff --git a/src/feature/dirauth/dirauth_options.inc b/src/feature/dirauth/dirauth_options.inc new file mode 100644 index 0000000000..2aa07a6c88 --- /dev/null +++ b/src/feature/dirauth/dirauth_options.inc @@ -0,0 +1,105 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_options.inc + * @brief Declare configuration options for the crypto_ops module. + **/ + +/** Holds configuration about our directory authority options. */ +BEGIN_CONF_STRUCT(dirauth_options_t) + +/** If non-zero, always vote the Fast flag for any relay advertising + * this amount of capacity or more. */ +CONF_VAR(AuthDirFastGuarantee, MEMUNIT, 0, "100 KB") + +/** If non-zero, this advertised capacity or more is always sufficient + * to satisfy the bandwidth requirement for the Guard flag. */ +CONF_VAR(AuthDirGuardBWGuarantee, MEMUNIT, 0, "2 MB") + +/** Boolean: are we on IPv6? */ +CONF_VAR(AuthDirHasIPv6Connectivity, BOOL, 0, "0") + +/** True iff we should list bad exits, and vote for all other exits as + * good. */ +CONF_VAR(AuthDirListBadExits, BOOL, 0, "0") + +/** Do not permit more than this number of servers per IP address. */ +CONF_VAR(AuthDirMaxServersPerAddr, POSINT, 0, "2") + +/** Boolean: Do we enforce key-pinning? */ +CONF_VAR(AuthDirPinKeys, BOOL, 0, "1") + +/** Bool (default: 1): Switch for the shared random protocol. Only + * relevant to a directory authority. If off, the authority won't + * participate in the protocol. If on (default), a flag is added to the + * vote indicating participation. */ +CONF_VAR(AuthDirSharedRandomness, BOOL, 0, "1") + +/** Bool (default: 1): When testing routerinfos as a directory authority, + * do we enforce Ed25519 identity match? */ +/* NOTE: remove this option someday. */ +CONF_VAR(AuthDirTestEd25519LinkKeys, BOOL, 0, "1") + +/** Authority only: key=value pairs that we add to our networkstatus + * consensus vote on the 'params' line. */ +CONF_VAR(ConsensusParams, LINELIST, 0, NULL) + +/** Authority only: minimum number of measured bandwidths we must see + * before we only believe measured bandwidths to assign flags. */ +CONF_VAR(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, 0, "500") + +/** As directory authority, accept hidden service directories after what + * time? */ +CONF_VAR(MinUptimeHidServDirectoryV2, INTERVAL, 0, "96 hours") + +/** Which versions of tor should we tell users to run? */ +CONF_VAR(RecommendedVersions, LINELIST, 0, NULL) + +/** Which versions of tor should we tell users to run on clients? */ +CONF_VAR(RecommendedClientVersions, LINELIST, 0, NULL) + +/** Which versions of tor should we tell users to run on relays? */ +CONF_VAR(RecommendedServerVersions, LINELIST, 0, NULL) + +/** If an authority has been around for less than this amount of time, it + * does not believe its reachability information is accurate. Only + * altered on testing networks. */ +CONF_VAR(TestingAuthDirTimeToLearnReachability, INTERVAL, 0, "30 minutes") + + /** Relays in a testing network which should be voted Exit + * regardless of exit policy. */ +CONF_VAR(TestingDirAuthVoteExit, ROUTERSET, 0, NULL) +CONF_VAR(TestingDirAuthVoteExitIsStrict, BOOL, 0, "0") + +/** Relays in a testing network which should be voted Guard + * regardless of uptime and bandwidth. */ +CONF_VAR(TestingDirAuthVoteGuard, ROUTERSET, 0, NULL) +CONF_VAR(TestingDirAuthVoteGuardIsStrict, BOOL, 0, "0") + +/** Relays in a testing network which should be voted HSDir + * regardless of uptime and DirPort. */ +CONF_VAR(TestingDirAuthVoteHSDir, ROUTERSET, 0, NULL) +CONF_VAR(TestingDirAuthVoteHSDirIsStrict, BOOL, 0, "0") + +/** Minimum value for the Exit flag threshold on testing networks. */ +CONF_VAR(TestingMinExitFlagThreshold, MEMUNIT, 0, "0") + +/** Minimum value for the Fast flag threshold on testing networks. */ +CONF_VAR(TestingMinFastFlagThreshold, MEMUNIT, 0, "0") + +/** Boolean: is this an authoritative directory that's willing to recommend + * versions? */ +CONF_VAR(VersioningAuthoritativeDirectory, BOOL, 0, "0") + +/** Boolean: Under bandwidth pressure, if set to 1, the authority will always + * answer directory requests from relays but will start sending 503 error code + * for the other connections. If set to 0, all connections are considered the + * same and the authority will try to answer them all regardless of bandwidth + * pressure or not. */ +CONF_VAR(AuthDirRejectRequestsUnderLoad, BOOL, 0, "1") + +END_CONF_STRUCT(dirauth_options_t) diff --git a/src/feature/dirauth/dirauth_options_st.h b/src/feature/dirauth/dirauth_options_st.h new file mode 100644 index 0000000000..02a498c054 --- /dev/null +++ b/src/feature/dirauth/dirauth_options_st.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_options_st.h + * @brief Structure dirauth_options_t to hold directory authority options. + **/ + +#ifndef TOR_FEATURE_DIRAUTH_DIRAUTH_OPTIONS_ST_H +#define TOR_FEATURE_DIRAUTH_DIRAUTH_OPTIONS_ST_H + +#include "lib/conf/confdecl.h" +#include "feature/nodelist/routerset.h" + +#define CONF_CONTEXT STRUCT +#include "feature/dirauth/dirauth_options.inc" +#undef CONF_CONTEXT + +typedef struct dirauth_options_t dirauth_options_t; + +#endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_OPTIONS_ST_H) */ diff --git a/src/feature/dirauth/dirauth_periodic.c b/src/feature/dirauth/dirauth_periodic.c new file mode 100644 index 0000000000..19e51c5a05 --- /dev/null +++ b/src/feature/dirauth/dirauth_periodic.c @@ -0,0 +1,168 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_periodic.c + * @brief Peridoic events for directory authorities. + **/ + +#include "core/or/or.h" + +#include "app/config/or_options_st.h" +#include "core/mainloop/netstatus.h" +#include "feature/dirauth/reachability.h" +#include "feature/stats/rephist.h" + +#include "feature/dirauth/bridgeauth.h" +#include "feature/dirauth/dirvote.h" +#include "feature/dirauth/dirauth_periodic.h" +#include "feature/dirauth/authmode.h" + +#include "core/mainloop/periodic.h" + +#ifndef COCCI +#define DECLARE_EVENT(name, roles, flags) \ + static periodic_event_item_t name ## _event = \ + PERIODIC_EVENT(name, \ + PERIODIC_EVENT_ROLE_##roles, \ + flags) +#endif /* !defined(COCCI) */ + +#define FL(name) (PERIODIC_EVENT_FLAG_##name) + +/** + * Periodic callback: if we're an authority, check on our authority + * certificate (the one that authenticates our authority signing key). + */ +static int +check_authority_cert_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; + /* 1e. Periodically, if we're a v3 authority, we check whether our cert is + * close to expiring and warn the admin if it is. */ + v3_authority_check_key_expiry(); +#define CHECK_V3_CERTIFICATE_INTERVAL (5*60) + return CHECK_V3_CERTIFICATE_INTERVAL; +} + +DECLARE_EVENT(check_authority_cert, DIRAUTH, 0); + +/** + * Scheduled callback: Run directory-authority voting functionality. + * + * The schedule is a bit complicated here, so dirvote_act() manages the + * schedule itself. + **/ +static int +dirvote_callback(time_t now, const or_options_t *options) +{ + if (!authdir_mode_v3(options)) { + tor_assert_nonfatal_unreached(); + return 3600; + } + + time_t next = dirvote_act(options, now); + if (BUG(next == TIME_MAX)) { + /* This shouldn't be returned unless we called dirvote_act() without + * being an authority. If it happens, maybe our configuration will + * fix itself in an hour or so? */ + return 3600; + } + return safe_timer_diff(now, next); +} + +DECLARE_EVENT(dirvote, DIRAUTH, FL(NEED_NET)); + +/** Reschedule the directory-authority voting event. Run this whenever the + * schedule has changed. */ +void +reschedule_dirvote(const or_options_t *options) +{ + if (authdir_mode_v3(options)) { + periodic_event_reschedule(&dirvote_event); + } +} + +/** + * Periodic callback: if we're an authority, record our measured stability + * information from rephist in an mtbf file. + */ +static int +save_stability_callback(time_t now, const or_options_t *options) +{ + if (authdir_mode_tests_reachability(options)) { + if (rep_hist_record_mtbf_data(now, 1)<0) { + log_warn(LD_GENERAL, "Couldn't store mtbf data."); + } + } +#define SAVE_STABILITY_INTERVAL (30*60) + return SAVE_STABILITY_INTERVAL; +} + +DECLARE_EVENT(save_stability, AUTHORITIES, 0); + +/** + * Periodic callback: if we're an authority, make sure we test + * the routers on the network for reachability. + */ +static int +launch_reachability_tests_callback(time_t now, const or_options_t *options) +{ + if (authdir_mode_tests_reachability(options) && + !net_is_disabled()) { + /* try to determine reachability of the other Tor relays */ + dirserv_test_reachability(now); + } + return REACHABILITY_TEST_INTERVAL; +} + +DECLARE_EVENT(launch_reachability_tests, AUTHORITIES, FL(NEED_NET)); + +/** + * Periodic callback: if we're an authority, discount the stability + * information (and other rephist information) that's older. + */ +static int +downrate_stability_callback(time_t now, const or_options_t *options) +{ + (void)options; + /* 1d. Periodically, we discount older stability information so that new + * stability info counts more, and save the stability information to disk as + * appropriate. */ + time_t next = rep_hist_downrate_old_runs(now); + return safe_timer_diff(now, next); +} + +DECLARE_EVENT(downrate_stability, AUTHORITIES, 0); + +/** + * Periodic callback: if we're the bridge authority, write a networkstatus + * file to disk. + */ +static int +write_bridge_ns_callback(time_t now, const or_options_t *options) +{ + if (options->BridgeAuthoritativeDir) { + bridgeauth_dump_bridge_status_to_file(now); +#define BRIDGE_STATUSFILE_INTERVAL (30*60) + return BRIDGE_STATUSFILE_INTERVAL; + } + return PERIODIC_EVENT_NO_UPDATE; +} + +DECLARE_EVENT(write_bridge_ns, BRIDGEAUTH, 0); + +void +dirauth_register_periodic_events(void) +{ + periodic_events_register(&downrate_stability_event); + periodic_events_register(&launch_reachability_tests_event); + periodic_events_register(&save_stability_event); + periodic_events_register(&check_authority_cert_event); + periodic_events_register(&dirvote_event); + periodic_events_register(&write_bridge_ns_event); +} diff --git a/src/feature/dirauth/dirauth_periodic.h b/src/feature/dirauth/dirauth_periodic.h new file mode 100644 index 0000000000..ccdda92a77 --- /dev/null +++ b/src/feature/dirauth/dirauth_periodic.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_periodic.h + * @brief Header for dirauth_periodic.c + **/ + +#ifndef DIRVOTE_PERIODIC_H +#define DIRVOTE_PERIODIC_H + +#ifdef HAVE_MODULE_DIRAUTH + +void dirauth_register_periodic_events(void); +void reschedule_dirvote(const or_options_t *options); + +#else /* !defined(HAVE_MODULE_DIRAUTH) */ + +static inline void +reschedule_dirvote(const or_options_t *options) +{ + (void)options; +} + +#endif /* defined(HAVE_MODULE_DIRAUTH) */ + +#endif /* !defined(DIRVOTE_PERIODIC_H) */ diff --git a/src/feature/dirauth/dirauth_stub.c b/src/feature/dirauth/dirauth_stub.c new file mode 100644 index 0000000000..9f48ce14fd --- /dev/null +++ b/src/feature/dirauth/dirauth_stub.c @@ -0,0 +1,34 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_stub.c + * @brief Stub declarations for use when dirauth module is disabled. + **/ + +#include "orconfig.h" +#include "feature/dirauth/dirauth_sys.h" +#include "lib/conf/conftypes.h" +#include "lib/conf/confdecl.h" +#include "lib/subsys/subsys.h" + +/* Declare the options field table for dirauth_options */ +#define CONF_CONTEXT STUB_TABLE +#include "feature/dirauth/dirauth_options.inc" +#undef CONF_CONTEXT + +static const config_format_t dirauth_options_stub_fmt = { + .vars = dirauth_options_t_vars, +}; + +const struct subsys_fns_t sys_dirauth = { + .name = "dirauth", + SUBSYS_DECLARE_LOCATION(), + .supported = false, + .level = DIRAUTH_SUBSYS_LEVEL, + + .options_format = &dirauth_options_stub_fmt +}; diff --git a/src/feature/dirauth/dirauth_sys.c b/src/feature/dirauth/dirauth_sys.c new file mode 100644 index 0000000000..07c5743877 --- /dev/null +++ b/src/feature/dirauth/dirauth_sys.c @@ -0,0 +1,71 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_sys.c + * @brief Directory authority subsystem declarations + **/ + +#include "core/or/or.h" + +#define DIRAUTH_SYS_PRIVATE +#include "feature/dirauth/bwauth.h" +#include "feature/dirauth/dirauth_sys.h" +#include "feature/dirauth/dirvote.h" +#include "feature/dirauth/dirauth_periodic.h" +#include "feature/dirauth/keypin.h" +#include "feature/dirauth/process_descs.h" +#include "feature/dirauth/dirauth_config.h" + +#include "feature/dirauth/dirauth_options_st.h" + +#include "lib/subsys/subsys.h" + +static const dirauth_options_t *global_dirauth_options; + +static int +subsys_dirauth_initialize(void) +{ + dirauth_register_periodic_events(); + return 0; +} + +static void +subsys_dirauth_shutdown(void) +{ + dirserv_free_fingerprint_list(); + dirvote_free_all(); + dirserv_clear_measured_bw_cache(); + keypin_close_journal(); + global_dirauth_options = NULL; +} + +const dirauth_options_t * +dirauth_get_options(void) +{ + tor_assert(global_dirauth_options); + return global_dirauth_options; +} + +STATIC int +dirauth_set_options(void *arg) +{ + dirauth_options_t *opts = arg; + global_dirauth_options = opts; + return 0; +} + +const struct subsys_fns_t sys_dirauth = { + .name = "dirauth", + SUBSYS_DECLARE_LOCATION(), + .supported = true, + .level = DIRAUTH_SUBSYS_LEVEL, + .initialize = subsys_dirauth_initialize, + .shutdown = subsys_dirauth_shutdown, + + .options_format = &dirauth_options_fmt, + .set_options = dirauth_set_options, +}; diff --git a/src/feature/dirauth/dirauth_sys.h b/src/feature/dirauth/dirauth_sys.h new file mode 100644 index 0000000000..c512b91b33 --- /dev/null +++ b/src/feature/dirauth/dirauth_sys.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file dirauth_sys.h + * @brief Header for dirauth_sys.c + **/ + +#ifndef DIRAUTH_SYS_H +#define DIRAUTH_SYS_H + +struct dirauth_options_t; +const struct dirauth_options_t *dirauth_get_options(void); + +extern const struct subsys_fns_t sys_dirauth; + +/** + * Subsystem level for the directory-authority system. + * + * Defined here so that it can be shared between the real and stub + * definitions. + **/ +#define DIRAUTH_SUBSYS_LEVEL 70 + +#ifdef DIRAUTH_SYS_PRIVATE +STATIC int dirauth_set_options(void *arg); +#endif + +#endif /* !defined(DIRAUTH_SYS_H) */ diff --git a/src/feature/dirauth/dircollate.c b/src/feature/dirauth/dircollate.c index 7992e3a85f..2657f53853 100644 --- a/src/feature/dirauth/dircollate.c +++ b/src/feature/dirauth/dircollate.c @@ -1,6 +1,6 @@ /* 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 */ /** @@ -32,8 +32,8 @@ static void dircollator_collate_by_ed25519(dircollator_t *dc); /** Hashtable entry mapping a pair of digests (actually an ed25519 key and an * RSA SHA1 digest) to an array of vote_routerstatus_t. */ -typedef struct ddmap_entry_s { - HT_ENTRY(ddmap_entry_s) node; +typedef struct ddmap_entry_t { + HT_ENTRY(ddmap_entry_t) node; /** A SHA1-RSA1024 identity digest and Ed25519 identity key, * concatenated. (If there is no ed25519 identity key, there is no * entry in this table.) */ @@ -89,10 +89,10 @@ ddmap_entry_set_digests(ddmap_entry_t *ent, memcpy(ent->d + DIGEST_LEN, ed25519, DIGEST256_LEN); } -HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, - ddmap_entry_eq) -HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, - ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_) +HT_PROTOTYPE(double_digest_map, ddmap_entry_t, node, ddmap_entry_hash, + ddmap_entry_eq); +HT_GENERATE2(double_digest_map, ddmap_entry_t, node, ddmap_entry_hash, + ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_); /** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of its RSA @@ -324,4 +324,3 @@ dircollator_get_votes_for_router(dircollator_t *dc, int idx) return digestmap_get(dc->by_collated_rsa_sha1, smartlist_get(dc->all_rsa_sha1_lst, idx)); } - diff --git a/src/feature/dirauth/dircollate.h b/src/feature/dirauth/dircollate.h index 754a094817..90c6bddad5 100644 --- a/src/feature/dirauth/dircollate.h +++ b/src/feature/dirauth/dircollate.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -15,7 +15,7 @@ #include "lib/testsupport/testsupport.h" #include "core/or/or.h" -typedef struct dircollator_s dircollator_t; +typedef struct dircollator_t dircollator_t; dircollator_t *dircollator_new(int n_votes, int n_authorities); void dircollator_free_(dircollator_t *obj); @@ -30,11 +30,11 @@ vote_routerstatus_t **dircollator_get_votes_for_router(dircollator_t *dc, int idx); #ifdef DIRCOLLATE_PRIVATE -struct ddmap_entry_s; -typedef HT_HEAD(double_digest_map, ddmap_entry_s) double_digest_map_t; +struct ddmap_entry_t; +typedef HT_HEAD(double_digest_map, ddmap_entry_t) double_digest_map_t; /** A dircollator keeps track of all the routerstatus entries in a * set of networkstatus votes, and matches them by an appropriate rule. */ -struct dircollator_s { +struct dircollator_t { /** True iff we have run the collation algorithm. */ int is_collated; /** The total number of votes that we received. */ diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index 9e01cee42a..d9fbd2a7ce 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -1,11 +1,12 @@ /* 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 +29,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 +37,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 +64,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,7 +223,6 @@ 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; @@ -242,19 +248,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 +261,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 +301,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_addr32(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->dir_port, voter->or_port, + voter->contact, + shared_random_vote_str ? + shared_random_vote_str : ""); + } tor_free(params); tor_free(flags); @@ -351,6 +370,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 +392,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 +434,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 +453,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); @@ -872,7 +894,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 +1547,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 @@ -2233,7 +2252,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 +2272,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 +2427,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 +2535,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 +2589,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 +2785,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 +2861,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 +2907,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 +2974,7 @@ dirvote_perform_vote(void) if (!contents) return -1; - pending_vote = dirvote_add_vote(contents, &msg, &status); + pending_vote = dirvote_add_vote(contents, 0, &msg, &status); tor_free(contents); if (!pending_vote) { log_warn(LD_DIR, "Couldn't store my own vote! (I told myself, '%s'.)", @@ -3108,13 +3130,45 @@ 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 **msg_out, int *status_out) { networkstatus_t *vote; networkstatus_voter_info_t *vi; @@ -3132,7 +3186,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 +3199,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 +3212,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 +3225,23 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) goto err; } + /* 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 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->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); @@ -3390,7 +3444,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!", @@ -3529,7 +3585,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 +3606,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 +3676,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 +3723,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); @@ -3784,15 +3855,16 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) smartlist_add_asprintf(chunks, "ntor-onion-key %s", 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 +3941,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 +3960,10 @@ 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, + MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; @@ -4199,7 +4272,7 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) static digestmap_t * get_possible_sybil_list(const smartlist_t *routers) { - const or_options_t *options = get_options(); + const dirauth_options_t *options = dirauth_get_options(); digestmap_t *omit_as_sybil; smartlist_t *routers_by_ip = smartlist_new(); uint32_t last_addr; @@ -4364,6 +4437,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,6 +4461,7 @@ 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; char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; @@ -4378,7 +4469,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, 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; @@ -4388,6 +4479,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, 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); @@ -4409,11 +4501,16 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, hostname = tor_dup_ip(addr); } - if (options->VersioningAuthoritativeDir) { + if (!hostname) { + log_err(LD_BUG, "Failed to determine hostname AND duplicate address"); + return NULL; + } + + 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 +4522,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 @@ -4479,8 +4577,8 @@ 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, @@ -4530,7 +4628,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 +4657,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); @@ -4579,17 +4679,14 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /* These are hardwired, to avoid disaster. */ v3_out->recommended_relay_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 Microdesc=1-2 Relay=2"); + tor_strdup(DIRVOTE_RECOMMEND_RELAY_PROTO); v3_out->recommended_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 Microdesc=1-2 Relay=2"); - v3_out->required_client_protocols = - tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=4 Microdesc=1-2 Relay=2"); + tor_strdup(DIRVOTE_RECOMMEND_CLIENT_PROTO); + v3_out->required_relay_protocols = - tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " - "Link=3-4 Microdesc=1 Relay=1-2"); + tor_strdup(DIRVOTE_REQUIRE_RELAY_PROTO); + v3_out->required_client_protocols = + tor_strdup(DIRVOTE_REQUIRE_CLIENT_PROTO); /* We are not allowed to vote to require anything we don't have. */ tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL)); @@ -4601,18 +4698,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"); @@ -4620,13 +4708,17 @@ 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); diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index 02d88d19d1..a9b356b387 100644 --- a/src/feature/dirauth/dirvote.h +++ b/src/feature/dirauth/dirvote.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -49,35 +49,17 @@ #define MIN_VOTE_INTERVAL_TESTING_INITIAL \ ((MIN_VOTE_SECONDS_TESTING)+(MIN_DIST_SECONDS_TESTING)+1) -/* A placeholder for routerstatus_format_entry() when the consensus method - * argument is not applicable. */ -#define ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD 0 - /** The lowest consensus method that we currently support. */ -#define MIN_SUPPORTED_CONSENSUS_METHOD 25 +#define MIN_SUPPORTED_CONSENSUS_METHOD 28 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 28 - -/** Lowest consensus method where authorities vote on required/recommended - * protocols. */ -#define MIN_METHOD_FOR_RECOMMENDED_PROTOCOLS 25 - -/** Lowest consensus method where authorities add protocols to routerstatus - * entries. */ -#define MIN_METHOD_FOR_RS_PROTOCOLS 25 - -/** Lowest consensus method where authorities initialize bandwidth weights to 1 - * instead of 0. See #14881 */ -#define MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE 26 +#define MAX_SUPPORTED_CONSENSUS_METHOD 29 -/** Lowest consensus method where the microdesc consensus contains relay IPv6 - * addresses. See #23826 and #20916. */ -#define MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS 27 - -/** Lowest consensus method where microdescriptors do not contain relay IPv6 - * addresses. See #23828 and #20916. */ -#define MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC 28 +/** + * Lowest consensus method where microdescriptor lines are put in canonical + * form for improved compressibility and ease of storage. See proposal 298. + **/ +#define MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS 29 /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not @@ -92,6 +74,9 @@ /** Maximum size of a line in a vote. */ #define MAX_BW_FILE_HEADERS_LINE_LEN 1024 +extern const char DIRVOTE_UNIVERSAL_FLAGS[]; +extern const char DIRVOTE_OPTIONAL_FLAGS[]; + /* * Public API. Used outside of the dirauth subsystem. * @@ -109,6 +94,7 @@ void dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, /* Storing signatures and votes functions */ struct pending_vote_t * dirvote_add_vote(const char *vote_body, + time_t time_posted, const char **msg_out, int *status_out); int dirvote_add_signatures(const char *detached_signatures_body, @@ -119,7 +105,7 @@ struct config_line_t; char *format_recommended_version_list(const struct config_line_t *line, int warn); -#else /* HAVE_MODULE_DIRAUTH */ +#else /* !defined(HAVE_MODULE_DIRAUTH) */ static inline time_t dirvote_act(const or_options_t *options, time_t now) @@ -157,9 +143,13 @@ dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items, } static inline struct 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 **msg_out, + int *status_out) { (void) vote_body; + (void) time_posted; /* If the dirauth module is disabled, this should NEVER be called else we * failed to safeguard the dirauth module. */ tor_assert_nonfatal_unreached(); @@ -177,14 +167,14 @@ dirvote_add_signatures(const char *detached_signatures_body, { (void) detached_signatures_body; (void) source; - (void) msg_out; + *msg_out = "No directory authority support"; /* If the dirauth module is disabled, this should NEVER be called else we * failed to safeguard the dirauth module. */ tor_assert_nonfatal_unreached(); return 0; } -#endif /* HAVE_MODULE_DIRAUTH */ +#endif /* defined(HAVE_MODULE_DIRAUTH) */ /* Item access */ MOCK_DECL(const char*, dirvote_get_pending_consensus, @@ -245,6 +235,64 @@ char *networkstatus_get_detached_signatures(smartlist_t *consensuses); STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method); +/** The recommended relay protocols for this authority's votes. + * Recommending a new protocol causes old tor versions to log a warning. + */ +#define DIRVOTE_RECOMMEND_RELAY_PROTO \ + "Cons=2 " \ + "Desc=2 " \ + "DirCache=2 " \ + "HSDir=2 " \ + "HSIntro=4 " \ + "HSRend=2 " \ + "Link=4-5 " \ + "LinkAuth=3 " \ + "Microdesc=2 " \ + "Relay=2" + +/** The recommended client protocols for this authority's votes. + * Recommending a new protocol causes old tor versions to log a warning. + */ +#define DIRVOTE_RECOMMEND_CLIENT_PROTO \ + "Cons=2 " \ + "Desc=2 " \ + "DirCache=2 " \ + "HSDir=2 " \ + "HSIntro=4 " \ + "HSRend=2 " \ + "Link=4-5 " \ + "Microdesc=2 " \ + "Relay=2" + +/** The required relay protocols for this authority's votes. + * WARNING: Requiring a new protocol causes old tor versions to shut down. + * Requiring the wrong protocols can break the tor network. + * See Proposal 303: When and how to remove support for protocol versions. + */ +#define DIRVOTE_REQUIRE_RELAY_PROTO \ + "Cons=2 " \ + "Desc=2 " \ + "DirCache=2 " \ + "HSDir=2 " \ + "HSIntro=4 " \ + "HSRend=2 " \ + "Link=4-5 " \ + "LinkAuth=3 " \ + "Microdesc=2 " \ + "Relay=2" + +/** The required relay protocols for this authority's votes. + * WARNING: Requiring a new protocol causes old tor versions to shut down. + * Requiring the wrong protocols can break the tor network. + * See Proposal 303: When and how to remove support for protocol versions. + */ +#define DIRVOTE_REQUIRE_CLIENT_PROTO \ + "Cons=2 " \ + "Desc=2 " \ + "Link=4 " \ + "Microdesc=2 " \ + "Relay=2" + #endif /* defined(DIRVOTE_PRIVATE) */ #endif /* !defined(TOR_DIRVOTE_H) */ diff --git a/src/feature/dirauth/dsigs_parse.c b/src/feature/dirauth/dsigs_parse.c index d88176fee9..d0bb931814 100644 --- a/src/feature/dirauth/dsigs_parse.c +++ b/src/feature/dirauth/dsigs_parse.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -127,7 +127,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos) } digests = detached_get_digests(sigs, flavor); tor_assert(digests); - if (!tor_mem_is_zero(digests->d[alg], digest_length)) { + if (!fast_mem_is_zero(digests->d[alg], digest_length)) { log_warn(LD_DIR, "Multiple digests for %s with %s on detached " "signatures document", flavor, algname); continue; diff --git a/src/feature/dirauth/dsigs_parse.h b/src/feature/dirauth/dsigs_parse.h index fec51ba488..b25e3e0b28 100644 --- a/src/feature/dirauth/dsigs_parse.h +++ b/src/feature/dirauth/dsigs_parse.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -19,4 +19,4 @@ void ns_detached_signatures_free_(ns_detached_signatures_t *s); #define ns_detached_signatures_free(s) \ FREE_AND_NULL(ns_detached_signatures_t, ns_detached_signatures_free_, (s)) -#endif +#endif /* !defined(TOR_DSIGS_PARSE_H) */ diff --git a/src/feature/dirauth/feature_dirauth.md b/src/feature/dirauth/feature_dirauth.md new file mode 100644 index 0000000000..b152b94894 --- /dev/null +++ b/src/feature/dirauth/feature_dirauth.md @@ -0,0 +1,9 @@ +@dir /feature/dirauth +@brief feature/dirauth: Directory authority implementation. + +This module handles running Tor as a directory authority. + +The directory protocol is specified in +[dir-spec.txt](https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt). + + diff --git a/src/feature/dirauth/guardfraction.c b/src/feature/dirauth/guardfraction.c index d1a7f194d4..b84f804f5f 100644 --- a/src/feature/dirauth/guardfraction.c +++ b/src/feature/dirauth/guardfraction.c @@ -1,6 +1,6 @@ /* 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 */ /** @@ -188,7 +188,7 @@ guardfraction_file_parse_inputs_line(const char *inputs_line, * * guardfraction-file-version 1 * written-at <date and time> - * n-inputs <number of consesuses parsed> <number of days considered> + * n-inputs <number of consensuses parsed> <number of days considered> * * guard-seen <fpr 1> <guardfraction percentage> <consensus appearances> * guard-seen <fpr 2> <guardfraction percentage> <consensus appearances> diff --git a/src/feature/dirauth/guardfraction.h b/src/feature/dirauth/guardfraction.h index 72404907a4..c10fd9b7bb 100644 --- a/src/feature/dirauth/guardfraction.h +++ b/src/feature/dirauth/guardfraction.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -16,9 +16,9 @@ STATIC int dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str, smartlist_t *vote_routerstatuses); -#endif /* defined(DIRSERV_PRIVATE) */ +#endif int dirserv_read_guardfraction_file(const char *fname, smartlist_t *vote_routerstatuses); -#endif +#endif /* !defined(TOR_GUARDFRACTION_H) */ diff --git a/src/feature/dirauth/include.am b/src/feature/dirauth/include.am new file mode 100644 index 0000000000..e26f120d4e --- /dev/null +++ b/src/feature/dirauth/include.am @@ -0,0 +1,54 @@ + +# The Directory Authority module. + +# ADD_C_FILE: INSERT SOURCES HERE. +MODULE_DIRAUTH_SOURCES = \ + src/feature/dirauth/authmode.c \ + src/feature/dirauth/bridgeauth.c \ + src/feature/dirauth/bwauth.c \ + src/feature/dirauth/dirauth_config.c \ + src/feature/dirauth/dirauth_periodic.c \ + src/feature/dirauth/dirauth_sys.c \ + src/feature/dirauth/dircollate.c \ + src/feature/dirauth/dirvote.c \ + src/feature/dirauth/dsigs_parse.c \ + src/feature/dirauth/guardfraction.c \ + src/feature/dirauth/keypin.c \ + src/feature/dirauth/process_descs.c \ + src/feature/dirauth/reachability.c \ + src/feature/dirauth/recommend_pkg.c \ + src/feature/dirauth/shared_random.c \ + src/feature/dirauth/shared_random_state.c \ + src/feature/dirauth/voteflags.c \ + src/feature/dirauth/voting_schedule.c + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/feature/dirauth/authmode.h \ + src/feature/dirauth/bridgeauth.h \ + src/feature/dirauth/bwauth.h \ + src/feature/dirauth/dirauth_config.h \ + src/feature/dirauth/dirauth_options.inc \ + src/feature/dirauth/dirauth_options_st.h \ + src/feature/dirauth/dirauth_periodic.h \ + src/feature/dirauth/dirauth_sys.h \ + src/feature/dirauth/dircollate.h \ + src/feature/dirauth/dirvote.h \ + src/feature/dirauth/dsigs_parse.h \ + src/feature/dirauth/guardfraction.h \ + src/feature/dirauth/keypin.h \ + src/feature/dirauth/ns_detached_signatures_st.h \ + src/feature/dirauth/reachability.h \ + src/feature/dirauth/recommend_pkg.h \ + src/feature/dirauth/process_descs.h \ + src/feature/dirauth/shared_random.h \ + src/feature/dirauth/shared_random_state.h \ + src/feature/dirauth/vote_microdesc_hash_st.h \ + src/feature/dirauth/voteflags.h \ + src/feature/dirauth/voting_schedule.h + +if BUILD_MODULE_DIRAUTH +LIBTOR_APP_A_SOURCES += $(MODULE_DIRAUTH_SOURCES) +else +LIBTOR_APP_A_STUB_SOURCES += src/feature/dirauth/dirauth_stub.c +endif diff --git a/src/feature/dirauth/keypin.c b/src/feature/dirauth/keypin.c index 06cb9ba1ff..5072a58573 100644 --- a/src/feature/dirauth/keypin.c +++ b/src/feature/dirauth/keypin.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2019, The Tor Project, Inc. */ +/* Copyright (c) 2014-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -15,8 +15,6 @@ #include "lib/cc/torint.h" #include "lib/crypt_ops/crypto_digest.h" #include "lib/crypt_ops/crypto_format.h" -#include "lib/crypt_ops/crypto_format.h" -#include "lib/ctime/di_ops.h" #include "lib/ctime/di_ops.h" #include "lib/encoding/binascii.h" #include "lib/encoding/time_fmt.h" @@ -120,14 +118,14 @@ return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key)); } HT_PROTOTYPE(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, - keypin_ents_eq_rsa) + keypin_ents_eq_rsa); HT_GENERATE2(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, - keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_) + keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_); HT_PROTOTYPE(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, - keypin_ents_eq_ed) + keypin_ents_eq_ed); HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, - keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_) + keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_); /** * Check whether we already have an entry in the key pinning table for a @@ -438,7 +436,7 @@ keypin_load_journal_impl(const char *data, size_t size) tor_log(severity, LD_DIRSERV, "Loaded %d entries from keypin journal. " "Found %d corrupt lines (ignored), %d duplicates (harmless), " - "and %d conflicts (resolved in favor or more recent entry).", + "and %d conflicts (resolved in favor of more recent entry).", n_entries, n_corrupt_lines, n_duplicates, n_conflicts); return 0; diff --git a/src/feature/dirauth/keypin.h b/src/feature/dirauth/keypin.h index 722b6ca5fc..881f010f0e 100644 --- a/src/feature/dirauth/keypin.h +++ b/src/feature/dirauth/keypin.h @@ -1,6 +1,11 @@ -/* Copyright (c) 2014-2019, The Tor Project, Inc. */ +/* Copyright (c) 2014-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file keypin.h + * @brief Header for keypin.c + **/ + #ifndef TOR_KEYPIN_H #define TOR_KEYPIN_H @@ -11,10 +16,25 @@ int keypin_check_and_add(const uint8_t *rsa_id_digest, const int replace_existing_entry); int keypin_check(const uint8_t *rsa_id_digest, const uint8_t *ed25519_id_key); +int keypin_close_journal(void); +#ifdef HAVE_MODULE_DIRAUTH int keypin_open_journal(const char *fname); -int keypin_close_journal(void); int keypin_load_journal(const char *fname); +#else +static inline int +keypin_open_journal(const char *fname) +{ + (void)fname; + return 0; +} +static inline int +keypin_load_journal(const char *fname) +{ + (void)fname; + return 0; +} +#endif /* defined(HAVE_MODULE_DIRAUTH) */ void keypin_clear(void); int keypin_check_lone_rsa(const uint8_t *rsa_id_digest); @@ -25,6 +45,8 @@ int keypin_check_lone_rsa(const uint8_t *rsa_id_digest); #ifdef KEYPIN_PRIVATE +#include "ext/ht.h" + /** * In-memory representation of a key-pinning table entry. */ @@ -44,4 +66,3 @@ MOCK_DECL(STATIC void, keypin_add_entry_to_map, (keypin_ent_t *ent)); #endif /* defined(KEYPIN_PRIVATE) */ #endif /* !defined(TOR_KEYPIN_H) */ - diff --git a/src/feature/dirauth/ns_detached_signatures_st.h b/src/feature/dirauth/ns_detached_signatures_st.h index 0f92be2f0d..f409431ec1 100644 --- a/src/feature/dirauth/ns_detached_signatures_st.h +++ b/src/feature/dirauth/ns_detached_signatures_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ +/** + * @file ns_detached_signatures_st.h + * @brief Detached consensus signatures structure. + **/ + #ifndef NS_DETACHED_SIGNATURES_ST_H #define NS_DETACHED_SIGNATURES_ST_H @@ -18,5 +23,4 @@ struct ns_detached_signatures_t { * document_signature_t */ }; -#endif - +#endif /* !defined(NS_DETACHED_SIGNATURES_ST_H) */ diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c index 21b8e239ec..5025d0ae39 100644 --- a/src/feature/dirauth/process_descs.c +++ b/src/feature/dirauth/process_descs.c @@ -1,6 +1,6 @@ /* 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 */ /** @@ -12,17 +12,21 @@ * them make those decisions. **/ +#define PROCESS_DESCS_PRIVATE + #include "core/or/or.h" #include "feature/dirauth/process_descs.h" #include "app/config/config.h" #include "core/or/policies.h" #include "core/or/versions.h" +#include "feature/dirauth/dirauth_sys.h" #include "feature/dirauth/keypin.h" #include "feature/dirauth/reachability.h" #include "feature/dirclient/dlstatus.h" #include "feature/dircommon/directory.h" #include "feature/nodelist/describe.h" +#include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerinfo.h" @@ -32,46 +36,28 @@ #include "feature/relay/router.h" #include "core/or/tor_version_st.h" +#include "feature/dirauth/dirauth_options_st.h" #include "feature/nodelist/extrainfo_st.h" #include "feature/nodelist/node_st.h" +#include "feature/nodelist/microdesc_st.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerstatus_st.h" +#include "feature/nodelist/vote_routerstatus_st.h" #include "lib/encoding/confline.h" +#include "lib/crypt_ops/crypto_format.h" /** How far in the future do we allow a router to get? (seconds) */ #define ROUTER_ALLOW_SKEW (60*60*12) static void directory_remove_invalid(void); -struct authdir_config_t; static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei, const char **msg); static uint32_t -dirserv_get_status_impl(const char *fp, const char *nickname, - uint32_t addr, uint16_t or_port, - const char *platform, const char **msg, - int severity); - -/* 1 Historically used to indicate Named */ -#define FP_INVALID 2 /**< Believed invalid. */ -#define FP_REJECT 4 /**< We will not publish this router. */ -/* 8 Historically used to avoid using this as a dir. */ -#define FP_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */ -/* 32 Historically used to indicade Unnamed */ - -/** Target of status_by_digest map. */ -typedef uint32_t router_status_t; - -static void add_fingerprint_to_dir(const char *fp, - struct authdir_config_t *list, - router_status_t add_status); - -/** List of nickname-\>identity fingerprint mappings for all the routers - * that we name. Used to prevent router impersonation. */ -typedef struct authdir_config_t { - strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */ - digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */ -} authdir_config_t; +dirserv_get_status_impl(const char *id_digest, + const ed25519_public_key_t *ed25519_public_key, + const char *nickname, uint32_t addr, uint16_t or_port, + const char *platform, const char **msg, int severity); /** Should be static; exposed for testing. */ static authdir_config_t *fingerprint_list = NULL; @@ -83,20 +69,39 @@ authdir_config_new(void) authdir_config_t *list = tor_malloc_zero(sizeof(authdir_config_t)); list->fp_by_name = strmap_new(); list->status_by_digest = digestmap_new(); + list->status_by_digest256 = digest256map_new(); return list; } +#ifdef TOR_UNIT_TESTS + +/** Initialize fingerprint_list to a new authdir_config_t. Used for tests. */ +void +authdir_init_fingerprint_list(void) +{ + fingerprint_list = authdir_config_new(); +} + +/* Return the current fingerprint_list. Used for tests. */ +authdir_config_t * +authdir_return_fingerprint_list(void) +{ + return fingerprint_list; +} + +#endif /* defined(TOR_UNIT_TESTS) */ + /** Add the fingerprint <b>fp</b> to the smartlist of fingerprint_entry_t's * <b>list</b>, or-ing the currently set status flags with * <b>add_status</b>. */ -/* static */ void -add_fingerprint_to_dir(const char *fp, authdir_config_t *list, - router_status_t add_status) +int +add_rsa_fingerprint_to_dir(const char *fp, authdir_config_t *list, + rtr_flags_t add_status) { char *fingerprint; char d[DIGEST_LEN]; - router_status_t *status; + rtr_flags_t *status; tor_assert(fp); tor_assert(list); @@ -107,24 +112,52 @@ add_fingerprint_to_dir(const char *fp, authdir_config_t *list, log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"", escaped(fp)); tor_free(fingerprint); - return; + return -1; } status = digestmap_get(list->status_by_digest, d); if (!status) { - status = tor_malloc_zero(sizeof(router_status_t)); + status = tor_malloc_zero(sizeof(rtr_flags_t)); digestmap_set(list->status_by_digest, d, status); } tor_free(fingerprint); *status |= add_status; - return; + return 0; +} + +/** Add the ed25519 key <b>edkey</b> to the smartlist of fingerprint_entry_t's + * <b>list</b>, or-ing the currently set status flags with <b>add_status</b>. + * Return -1 if we were unable to decode the key, else return 0. + */ +int +add_ed25519_to_dir(const ed25519_public_key_t *edkey, authdir_config_t *list, + rtr_flags_t add_status) +{ + rtr_flags_t *status; + + tor_assert(edkey); + tor_assert(list); + + if (ed25519_validate_pubkey(edkey) < 0) { + log_warn(LD_DIRSERV, "Invalid ed25519 key \"%s\"", ed25519_fmt(edkey)); + return -1; + } + + status = digest256map_get(list->status_by_digest256, edkey->pubkey); + if (!status) { + status = tor_malloc_zero(sizeof(rtr_flags_t)); + digest256map_set(list->status_by_digest256, edkey->pubkey, status); + } + + *status |= add_status; + return 0; } /** Add the fingerprint for this OR to the global list of recognized * identity key fingerprints. */ int -dirserv_add_own_fingerprint(crypto_pk_t *pk) +dirserv_add_own_fingerprint(crypto_pk_t *pk, const ed25519_public_key_t *edkey) { char fp[FINGERPRINT_LEN+1]; if (crypto_pk_get_fingerprint(pk, fp, 0)<0) { @@ -133,7 +166,14 @@ dirserv_add_own_fingerprint(crypto_pk_t *pk) } if (!fingerprint_list) fingerprint_list = authdir_config_new(); - add_fingerprint_to_dir(fp, fingerprint_list, 0); + if (add_rsa_fingerprint_to_dir(fp, fingerprint_list, 0) < 0) { + log_err(LD_BUG, "Error adding RSA fingerprint"); + return -1; + } + if (add_ed25519_to_dir(edkey, fingerprint_list, 0) < 0) { + log_err(LD_BUG, "Error adding ed25519 key"); + return -1; + } return 0; } @@ -174,27 +214,46 @@ dirserv_load_fingerprint_file(void) fingerprint_list_new = authdir_config_new(); for (list=front; list; list=list->next) { - char digest_tmp[DIGEST_LEN]; - router_status_t add_status = 0; + rtr_flags_t add_status = 0; nickname = list->key; fingerprint = list->value; tor_strstrip(fingerprint, " "); /* remove spaces */ - if (strlen(fingerprint) != HEX_DIGEST_LEN || - base16_decode(digest_tmp, sizeof(digest_tmp), - fingerprint, HEX_DIGEST_LEN) != sizeof(digest_tmp)) { - log_notice(LD_CONFIG, - "Invalid fingerprint (nickname '%s', " - "fingerprint %s). Skipping.", - nickname, fingerprint); - continue; - } + + /* Determine what we should do with the relay with the nickname field. */ if (!strcasecmp(nickname, "!reject")) { - add_status = FP_REJECT; + add_status = RTR_REJECT; } else if (!strcasecmp(nickname, "!badexit")) { - add_status = FP_BADEXIT; + add_status = RTR_BADEXIT; } else if (!strcasecmp(nickname, "!invalid")) { - add_status = FP_INVALID; + add_status = RTR_INVALID; + } + + /* Check if fingerprint is RSA or ed25519 by verifying it. */ + int ed25519_not_ok = -1, rsa_not_ok = -1; + + /* Attempt to add the RSA key. */ + if (strlen(fingerprint) == HEX_DIGEST_LEN) { + rsa_not_ok = add_rsa_fingerprint_to_dir(fingerprint, + fingerprint_list_new, + add_status); + } + + /* Check ed25519 key. We check the size to prevent buffer overflows. + * If valid, attempt to add it, */ + ed25519_public_key_t ed25519_pubkey_tmp; + if (strlen(fingerprint) == BASE64_DIGEST256_LEN) { + if (!digest256_from_base64((char *) ed25519_pubkey_tmp.pubkey, + fingerprint)) { + ed25519_not_ok = add_ed25519_to_dir(&ed25519_pubkey_tmp, + fingerprint_list_new, add_status); + } + } + + /* If both keys are invalid (or missing), log and skip. */ + if (ed25519_not_ok && rsa_not_ok) { + log_warn(LD_CONFIG, "Invalid fingerprint (nickname '%s', " + "fingerprint %s). Skipping.", nickname, fingerprint); + continue; } - add_fingerprint_to_dir(fingerprint, fingerprint_list_new, add_status); } config_free_lines(front); @@ -216,30 +275,42 @@ dirserv_load_fingerprint_file(void) #define DISABLE_DISABLING_ED25519 -/** Check whether <b>router</b> has a nickname/identity key combination that - * we recognize from the fingerprint list, or an IP we automatically act on - * according to our configuration. Return the appropriate router status. +/** Check whether <b>router</b> has: + * - a nickname/identity key combination that we recognize from the fingerprint + * list, + * - an IP we automatically act on according to our configuration, + * - an appropriate version, and + * - matching pinned keys. + * + * Return the appropriate router status. * - * If the status is 'FP_REJECT' and <b>msg</b> is provided, set - * *<b>msg</b> to an explanation of why. */ + * If the status is 'RTR_REJECT' and <b>msg</b> is provided, set + * *<b>msg</b> to a string constant explaining why. */ uint32_t dirserv_router_get_status(const routerinfo_t *router, const char **msg, int severity) { char d[DIGEST_LEN]; - const int key_pinning = get_options()->AuthDirPinKeys; + const int key_pinning = dirauth_get_options()->AuthDirPinKeys; + uint32_t r; + ed25519_public_key_t *signing_key = NULL; if (crypto_pk_get_digest(router->identity_pkey, d)) { log_warn(LD_BUG,"Error computing fingerprint"); if (msg) *msg = "Bug: Error computing fingerprint"; - return FP_REJECT; + return RTR_REJECT; } - /* Check for the more usual versions to reject a router first. */ - const uint32_t r = dirserv_get_status_impl(d, router->nickname, - router->addr, router->or_port, - router->platform, msg, severity); + /* First, check for the more common reasons to reject a router. */ + if (router->cache_info.signing_key_cert) { + /* This has an ed25519 identity key. */ + signing_key = &router->cache_info.signing_key_cert->signing_key; + } + r = dirserv_get_status_impl(d, signing_key, router->nickname, router->addr, + router->or_port, router->platform, msg, + severity); + if (r) return r; @@ -254,7 +325,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, "key.", router_describe(router)); if (msg) *msg = "Missing ntor curve25519 onion key. Please upgrade!"; - return FP_REJECT; + return RTR_REJECT; } if (router->cache_info.signing_key_cert) { @@ -270,7 +341,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, if (msg) { *msg = "Ed25519 identity key or RSA identity key has changed."; } - return FP_REJECT; + return RTR_REJECT; } } } else { @@ -287,7 +358,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, if (msg) { *msg = "Ed25519 identity key has disappeared."; } - return FP_REJECT; + return RTR_REJECT; } #endif /* defined(DISABLE_DISABLING_ED25519) */ } @@ -299,31 +370,74 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, /** Return true if there is no point in downloading the router described by * <b>rs</b> because this directory would reject it. */ int -dirserv_would_reject_router(const routerstatus_t *rs) +dirserv_would_reject_router(const routerstatus_t *rs, + const vote_routerstatus_t *vrs) { uint32_t res; + struct ed25519_public_key_t pk; + memcpy(&pk.pubkey, vrs->ed25519_id, ED25519_PUBKEY_LEN); - res = dirserv_get_status_impl(rs->identity_digest, rs->nickname, - rs->addr, rs->or_port, - NULL, NULL, LOG_DEBUG); + res = dirserv_get_status_impl(rs->identity_digest, &pk, rs->nickname, + rs->addr, rs->or_port, NULL, NULL, LOG_DEBUG); - return (res & FP_REJECT) != 0; + return (res & RTR_REJECT) != 0; +} + +/** + * Check whether the platform string in <b>platform</b> describes a platform + * that, as a directory authority, we want to reject. If it does, return + * true, and set *<b>msg</b> (if present) to a rejection message. Otherwise + * return false. + */ +STATIC bool +dirserv_rejects_tor_version(const char *platform, + const char **msg) +{ + if (!platform) + return false; + + static const char please_upgrade_string[] = + "Tor version is insecure or unsupported. Please upgrade!"; + + /* Versions before Tor 0.3.5 are unsupported. + * + * Also, reject unstable versions of 0.3.5, since (as of this writing) + * they are almost none of the network. */ + if (!tor_version_as_new_as(platform,"0.3.5.7")) { + if (msg) + *msg = please_upgrade_string; + return true; + } + + /* Series between Tor 0.3.6 and 0.4.1.4-rc inclusive are unsupported. + * Reject them. 0.3.6.0-alpha-dev only existed for a short time, before + * it was renamed to 0.4.0.0-alpha-dev. */ + if (tor_version_as_new_as(platform,"0.3.6.0-alpha-dev") && + !tor_version_as_new_as(platform,"0.4.1.5")) { + if (msg) { + *msg = please_upgrade_string; + } + return true; + } + + return false; } /** Helper: As dirserv_router_get_status, but takes the router fingerprint - * (hex, no spaces), nickname, address (used for logging only), IP address, OR - * port and platform (logging only) as arguments. + * (hex, no spaces), ed25519 key, nickname, address (used for logging only), + * IP address, OR port and platform (logging only) as arguments. * * Log messages at 'severity'. (There's not much point in * logging that we're rejecting servers we'll not download.) */ static uint32_t -dirserv_get_status_impl(const char *id_digest, const char *nickname, - uint32_t addr, uint16_t or_port, +dirserv_get_status_impl(const char *id_digest, + const ed25519_public_key_t *ed25519_public_key, + const char *nickname, uint32_t addr, uint16_t or_port, const char *platform, const char **msg, int severity) { uint32_t result = 0; - router_status_t *status_by_digest; + rtr_flags_t *status_by_digest; if (!fingerprint_list) fingerprint_list = authdir_config_new(); @@ -338,27 +452,13 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname, if (msg) { *msg = "Malformed platform string."; } - return FP_REJECT; + return RTR_REJECT; } } - /* Versions before Tor 0.2.4.18-rc are too old to support, and are - * missing some important security fixes too. Disable them. */ - if (platform && !tor_version_as_new_as(platform,"0.2.4.18-rc")) { - if (msg) - *msg = "Tor version is insecure or unsupported. Please upgrade!"; - return FP_REJECT; - } - - /* Tor 0.2.9.x where x<5 suffers from bug #20499, where relays don't - * keep their consensus up to date so they make bad guards. - * The simple fix is to just drop them from the network. */ - if (platform && - tor_version_as_new_as(platform,"0.2.9.0-alpha") && - !tor_version_as_new_as(platform,"0.2.9.5-alpha")) { - if (msg) - *msg = "Tor version contains bug 20499. Please upgrade!"; - return FP_REJECT; + /* Check whether the version is obsolete, broken, insecure, etc... */ + if (platform && dirserv_rejects_tor_version(platform, msg)) { + return RTR_REJECT; } status_by_digest = digestmap_get(fingerprint_list->status_by_digest, @@ -366,23 +466,30 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname, if (status_by_digest) result |= *status_by_digest; - if (result & FP_REJECT) { + if (ed25519_public_key) { + status_by_digest = digest256map_get(fingerprint_list->status_by_digest256, + ed25519_public_key->pubkey); + if (status_by_digest) + result |= *status_by_digest; + } + + if (result & RTR_REJECT) { if (msg) - *msg = "Fingerprint is marked rejected -- if you think this is a " - "mistake please set a valid email address in ContactInfo and " - "send an email to bad-relays@lists.torproject.org mentioning " - "your fingerprint(s)?"; - return FP_REJECT; - } else if (result & FP_INVALID) { + *msg = "Fingerprint and/or ed25519 identity is marked rejected -- if " + "you think this is a mistake please set a valid email address " + "in ContactInfo and send an email to " + "bad-relays@lists.torproject.org mentioning your fingerprint(s)?"; + return RTR_REJECT; + } else if (result & RTR_INVALID) { if (msg) - *msg = "Fingerprint is marked invalid"; + *msg = "Fingerprint and/or ed25519 identity is marked invalid"; } if (authdir_policy_badexit_address(addr, or_port)) { log_fn(severity, LD_DIRSERV, "Marking '%s' as bad exit because of address '%s'", nickname, fmt_addr32(addr)); - result |= FP_BADEXIT; + result |= RTR_BADEXIT; } if (!authdir_policy_permits_address(addr, or_port)) { @@ -393,13 +500,13 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname, "mistake please set a valid email address in ContactInfo and " "send an email to bad-relays@lists.torproject.org mentioning " "your address(es) and fingerprint(s)?"; - return FP_REJECT; + return RTR_REJECT; } if (!authdir_policy_valid_address(addr, or_port)) { log_fn(severity, LD_DIRSERV, "Not marking '%s' valid because of address '%s'", nickname, fmt_addr32(addr)); - result |= FP_INVALID; + result |= RTR_INVALID; } return result; @@ -414,6 +521,7 @@ dirserv_free_fingerprint_list(void) strmap_free(fingerprint_list->fp_by_name, tor_free_); digestmap_free(fingerprint_list->status_by_digest, tor_free_); + digest256map_free(fingerprint_list->status_by_digest256, tor_free_); tor_free(fingerprint_list); } @@ -423,27 +531,40 @@ dirserv_free_fingerprint_list(void) /** Return -1 if <b>ri</b> has a private or otherwise bad address, * unless we're configured to not care. Return 0 if all ok. */ -static int +STATIC int dirserv_router_has_valid_address(routerinfo_t *ri) { tor_addr_t addr; + if (get_options()->DirAllowPrivateAddresses) return 0; /* whatever it is, we're fine with it */ + tor_addr_from_ipv4h(&addr, ri->addr); + if (tor_addr_is_null(&addr) || tor_addr_is_internal(&addr, 0)) { + log_info(LD_DIRSERV, + "Router %s published internal IPv4 address. Refusing.", + router_describe(ri)); + return -1; /* it's a private IP, we should reject it */ + } - if (tor_addr_is_internal(&addr, 0)) { + /* We only check internal v6 on non-null addresses because we do not require + * IPv6 and null IPv6 is normal. */ + if (!tor_addr_is_null(&ri->ipv6_addr) && + tor_addr_is_internal(&ri->ipv6_addr, 0)) { log_info(LD_DIRSERV, - "Router %s published internal IP address. Refusing.", + "Router %s published internal IPv6 address. Refusing.", router_describe(ri)); return -1; /* it's a private IP, we should reject it */ } + return 0; } /** Check whether we, as a directory server, want to accept <b>ri</b>. If so, * set its is_valid,running fields and return 0. Otherwise, return -1. * - * If the router is rejected, set *<b>msg</b> to an explanation of why. + * If the router is rejected, set *<b>msg</b> to a string constant explining + * why. * * If <b>complain</b> then explain at log-level 'notice' why we refused * a descriptor; else explain at log-level 'info'. @@ -457,7 +578,7 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, int severity = (complain && ri->contact_info) ? LOG_NOTICE : LOG_INFO; uint32_t status = dirserv_router_get_status(ri, msg, severity); tor_assert(msg); - if (status & FP_REJECT) + if (status & RTR_REJECT) return -1; /* msg is already set. */ /* Is there too much clock skew? */ @@ -493,7 +614,7 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, return -1; } - *valid_out = ! (status & FP_INVALID); + *valid_out = ! (status & RTR_INVALID); return 0; } @@ -505,8 +626,8 @@ void dirserv_set_node_flags_from_authoritative_status(node_t *node, uint32_t authstatus) { - node->is_valid = (authstatus & FP_INVALID) ? 0 : 1; - node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0; + node->is_valid = (authstatus & RTR_INVALID) ? 0 : 1; + node->is_bad_exit = (authstatus & RTR_BADEXIT) ? 1 : 0; } /** True iff <b>a</b> is more severe than <b>b</b>. */ @@ -519,7 +640,8 @@ WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b) /** As for dirserv_add_descriptor(), but accepts multiple documents, and * returns the most severe error that occurred for any one of them. */ was_router_added_t -dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose, +dirserv_add_multiple_descriptors(const char *desc, size_t desclen, + uint8_t purpose, const char *source, const char **msg) { @@ -534,7 +656,12 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose, int general = purpose == ROUTER_PURPOSE_GENERAL; tor_assert(msg); - r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */ + r=ROUTER_ADDED_SUCCESSFULLY; /* Least severe return value. */ + + if (!string_is_utf8_no_bom(desc, desclen)) { + *msg = "descriptor(s) or extrainfo(s) not valid UTF-8 or had BOM."; + return ROUTER_AUTHDIR_REJECTS; + } format_iso_time(time_buf, now); if (tor_snprintf(annotation_buf, sizeof(annotation_buf), @@ -545,14 +672,12 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose, !general ? router_purpose_to_string(purpose) : "", !general ? "\n" : "")<0) { *msg = "Couldn't format annotations"; - /* XXX Not cool: we return -1 below, but (was_router_added_t)-1 is - * ROUTER_BAD_EI, which isn't what's gone wrong here. :( */ - return -1; + return ROUTER_AUTHDIR_BUG_ANNOTATIONS; } s = desc; list = smartlist_new(); - if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0, + if (!router_parse_list_from_string(&s, s+desclen, list, SAVED_NOWHERE, 0, 0, annotation_buf, NULL)) { SMARTLIST_FOREACH(list, routerinfo_t *, ri, { msg_out = NULL; @@ -568,7 +693,7 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose, smartlist_clear(list); s = desc; - if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0, + if (!router_parse_list_from_string(&s, s+desclen, list, SAVED_NOWHERE, 1, 0, NULL, NULL)) { SMARTLIST_FOREACH(list, extrainfo_t *, ei, { msg_out = NULL; @@ -605,7 +730,8 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose, * That means the caller must not access <b>ri</b> after this function * returns, since it might have been freed. * - * Return the status of the operation. + * Return the status of the operation, and set *<b>msg</b> to a string + * constant describing the status. * * This function is only called when fresh descriptors are posted, not when * we re-load the cache. @@ -618,7 +744,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) char *desc, *nickname; const size_t desclen = ri->cache_info.signed_descriptor_len + ri->cache_info.annotations_len; - const int key_pinning = get_options()->AuthDirPinKeys; + const int key_pinning = dirauth_get_options()->AuthDirPinKeys; *msg = NULL; /* If it's too big, refuse it now. Otherwise we'll cache it all over the @@ -816,21 +942,21 @@ directory_remove_invalid(void) continue; r = dirserv_router_get_status(ent, &msg, LOG_INFO); description = router_describe(ent); - if (r & FP_REJECT) { + if (r & RTR_REJECT) { log_info(LD_DIRSERV, "Router %s is now rejected: %s", description, msg?msg:""); routerlist_remove(rl, ent, 0, time(NULL)); continue; } - if (bool_neq((r & FP_INVALID), !node->is_valid)) { + if (bool_neq((r & RTR_INVALID), !node->is_valid)) { log_info(LD_DIRSERV, "Router '%s' is now %svalid.", description, - (r&FP_INVALID) ? "in" : ""); - node->is_valid = (r&FP_INVALID)?0:1; + (r&RTR_INVALID) ? "in" : ""); + node->is_valid = (r&RTR_INVALID)?0:1; } - if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) { + if (bool_neq((r & RTR_BADEXIT), node->is_bad_exit)) { log_info(LD_DIRSERV, "Router '%s' is now a %s exit", description, - (r & FP_BADEXIT) ? "bad" : "good"); - node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0; + (r & RTR_BADEXIT) ? "bad" : "good"); + node->is_bad_exit = (r&RTR_BADEXIT) ? 1: 0; } } SMARTLIST_FOREACH_END(node); diff --git a/src/feature/dirauth/process_descs.h b/src/feature/dirauth/process_descs.h index ae2d6ad25d..1461ab697d 100644 --- a/src/feature/dirauth/process_descs.h +++ b/src/feature/dirauth/process_descs.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -12,27 +12,155 @@ #ifndef TOR_RECV_UPLOADS_H #define TOR_RECV_UPLOADS_H -int dirserv_load_fingerprint_file(void); +// for was_router_added_t. +#include "feature/nodelist/routerlist.h" + +#include "lib/crypt_ops/crypto_ed25519.h" + +struct authdir_config_t; + +/** Target of status_by_digest map. */ +typedef uint32_t rtr_flags_t; + +int add_rsa_fingerprint_to_dir(const char *fp, struct authdir_config_t *list, + rtr_flags_t add_status); + +int add_ed25519_to_dir(const ed25519_public_key_t *edkey, + struct authdir_config_t *list, + rtr_flags_t add_status); + +/** List of nickname-\>identity fingerprint mappings for all the routers + * that we name. Used to prevent router impersonation. */ +typedef struct authdir_config_t { + strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */ + digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */ + digest256map_t *status_by_digest256; /**< Map from digest256 to + * router_status_t. */ +} authdir_config_t; + +#if defined(PROCESS_DESCS_PRIVATE) || defined(TOR_UNIT_TESTS) + +/* 1 Historically used to indicate Named */ +#define RTR_INVALID 2 /**< Believed invalid. */ +#define RTR_REJECT 4 /**< We will not publish this router. */ +/* 8 Historically used to avoid using this as a dir. */ +#define RTR_BADEXIT 16 /**< We'll tell clients not to use this as an exit. */ +/* 32 Historically used to indicade Unnamed */ + +#endif /* defined(PROCESS_DESCS_PRIVATE) || defined(TOR_UNIT_TESTS) */ + +#ifdef TOR_UNIT_TESTS + +void authdir_init_fingerprint_list(void); + +authdir_config_t *authdir_return_fingerprint_list(void); + +#endif /* defined(TOR_UNIT_TESTS) */ + void dirserv_free_fingerprint_list(void); -int dirserv_add_own_fingerprint(crypto_pk_t *pk); +#ifdef HAVE_MODULE_DIRAUTH +int dirserv_load_fingerprint_file(void); enum was_router_added_t dirserv_add_multiple_descriptors( - const char *desc, uint8_t purpose, + const char *desc, size_t desclen, + uint8_t purpose, const char *source, const char **msg); enum was_router_added_t dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source); +int dirserv_would_reject_router(const routerstatus_t *rs, + const vote_routerstatus_t *vrs); int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, int complain, int *valid_out); +int dirserv_add_own_fingerprint(crypto_pk_t *pk, + const ed25519_public_key_t *edkey); uint32_t dirserv_router_get_status(const routerinfo_t *router, const char **msg, int severity); void dirserv_set_node_flags_from_authoritative_status(node_t *node, uint32_t authstatus); +#else /* !defined(HAVE_MODULE_DIRAUTH) */ +static inline int +dirserv_load_fingerprint_file(void) +{ + return 0; +} +static inline enum was_router_added_t +dirserv_add_multiple_descriptors(const char *desc, size_t desclen, + uint8_t purpose, + const char *source, + const char **msg) +{ + (void)desc; + (void)desclen; + (void)purpose; + (void)source; + *msg = "No directory authority support"; + return (enum was_router_added_t)0; +} +static inline enum was_router_added_t +dirserv_add_descriptor(routerinfo_t *ri, + const char **msg, + const char *source) +{ + (void)ri; + (void)source; + *msg = "No directory authority support"; + return (enum was_router_added_t)0; +} +static inline int +dirserv_would_reject_router(const routerstatus_t *rs, + const vote_routerstatus_t *vrs) +{ + (void)rs; + (void)vrs; + return 0; +} +static inline int +authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, + int complain, + int *valid_out) +{ + (void)ri; + (void)complain; + *msg = "No directory authority support"; + *valid_out = 0; + return 0; +} +static inline int +dirserv_add_own_fingerprint(crypto_pk_t *pk, const ed25519_public_key_t *edkey) +{ + (void)pk; + (void)edkey; + return 0; +} +static inline uint32_t +dirserv_router_get_status(const routerinfo_t *router, + const char **msg, + int severity) +{ + (void)router; + (void)severity; + if (msg) + *msg = "No directory authority support"; + return 0; +} +static inline void +dirserv_set_node_flags_from_authoritative_status(node_t *node, + uint32_t authstatus) +{ + (void)node; + (void)authstatus; +} +#endif /* defined(HAVE_MODULE_DIRAUTH) */ -int dirserv_would_reject_router(const routerstatus_t *rs); +#ifdef TOR_UNIT_TESTS +STATIC int dirserv_router_has_valid_address(routerinfo_t *ri); +STATIC bool dirserv_rejects_tor_version(const char *platform, + const char **msg); +#endif /* defined(TOR_UNIT_TESTS) */ -#endif +#endif /* !defined(TOR_RECV_UPLOADS_H) */ diff --git a/src/feature/dirauth/reachability.c b/src/feature/dirauth/reachability.c index 883b692cbb..65fa27ed80 100644 --- a/src/feature/dirauth/reachability.c +++ b/src/feature/dirauth/reachability.c @@ -1,6 +1,6 @@ /* 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 */ /** @@ -17,6 +17,7 @@ #include "core/or/channeltls.h" #include "core/or/command.h" #include "feature/dirauth/authmode.h" +#include "feature/dirauth/dirauth_sys.h" #include "feature/nodelist/describe.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerinfo.h" @@ -24,6 +25,7 @@ #include "feature/nodelist/torcert.h" #include "feature/stats/rephist.h" +#include "feature/dirauth/dirauth_options_st.h" #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerlist_st.h" @@ -53,7 +55,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr, ri = node->ri; - if (get_options()->AuthDirTestEd25519LinkKeys && + if (dirauth_get_options()->AuthDirTestEd25519LinkKeys && node_supports_ed25519_link_authentication(node, 1) && ri->cache_info.signing_key_cert) { /* We allow the node to have an ed25519 key if we haven't been told one in @@ -125,7 +127,7 @@ dirserv_should_launch_reachability_test(const routerinfo_t *ri, void dirserv_single_reachability_test(time_t now, routerinfo_t *router) { - const or_options_t *options = get_options(); + const dirauth_options_t *dirauth_options = dirauth_get_options(); channel_t *chan = NULL; const node_t *node = NULL; tor_addr_t router_addr; @@ -136,7 +138,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) node = node_get_by_id(router->cache_info.identity_digest); tor_assert(node); - if (options->AuthDirTestEd25519LinkKeys && + if (dirauth_options->AuthDirTestEd25519LinkKeys && node_supports_ed25519_link_authentication(node, 1) && router->cache_info.signing_key_cert) { ed_id_key = &router->cache_info.signing_key_cert->signing_key; @@ -154,7 +156,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) if (chan) command_setup_channel(chan); /* Possible IPv6. */ - if (get_options()->AuthDirHasIPv6Connectivity == 1 && + if (dirauth_get_options()->AuthDirHasIPv6Connectivity == 1 && !tor_addr_is_null(&router->ipv6_addr)) { char addrstr[TOR_ADDR_BUF_LEN]; log_debug(LD_OR, "Testing reachability of %s at %s:%u.", diff --git a/src/feature/dirauth/reachability.h b/src/feature/dirauth/reachability.h index 5a938673ff..19448a67f3 100644 --- a/src/feature/dirauth/reachability.h +++ b/src/feature/dirauth/reachability.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -24,13 +24,27 @@ #define REACHABILITY_TEST_CYCLE_PERIOD \ (REACHABILITY_TEST_INTERVAL*REACHABILITY_MODULO_PER_TEST) +#ifdef HAVE_MODULE_DIRAUTH +void dirserv_single_reachability_test(time_t now, routerinfo_t *router); +void dirserv_test_reachability(time_t now); + +int dirserv_should_launch_reachability_test(const routerinfo_t *ri, + const routerinfo_t *ri_old); void dirserv_orconn_tls_done(const tor_addr_t *addr, uint16_t or_port, const char *digest_rcvd, const struct ed25519_public_key_t *ed_id_rcvd); -int dirserv_should_launch_reachability_test(const routerinfo_t *ri, - const routerinfo_t *ri_old); -void dirserv_single_reachability_test(time_t now, routerinfo_t *router); -void dirserv_test_reachability(time_t now); +#else /* !defined(HAVE_MODULE_DIRAUTH) */ +#define dirserv_single_reachability_test(now, router) \ + (((void)(now)),((void)(router))) +#define dirserv_test_reachability(now) \ + (((void)(now))) + +#define dirserv_should_launch_reachability_test(ri, ri_old) \ + (((void)(ri)),((void)(ri_old)),0) +#define dirserv_orconn_tls_done(addr, or_port, digest_rcvd, ed_id_rcvd) \ + (((void)(addr)),((void)(or_port)),((void)(digest_rcvd)), \ + ((void)(ed_id_rcvd))) +#endif /* defined(HAVE_MODULE_DIRAUTH) */ -#endif +#endif /* !defined(TOR_REACHABILITY_H) */ diff --git a/src/feature/dirauth/recommend_pkg.c b/src/feature/dirauth/recommend_pkg.c index 0456ff8463..84254566c6 100644 --- a/src/feature/dirauth/recommend_pkg.c +++ b/src/feature/dirauth/recommend_pkg.c @@ -1,6 +1,6 @@ /* 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 */ /** diff --git a/src/feature/dirauth/recommend_pkg.h b/src/feature/dirauth/recommend_pkg.h index 8200d78f72..dcd9f8be8a 100644 --- a/src/feature/dirauth/recommend_pkg.h +++ b/src/feature/dirauth/recommend_pkg.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -12,6 +12,18 @@ #ifndef TOR_RECOMMEND_PKG_H #define TOR_RECOMMEND_PKG_H +#ifdef HAVE_MODULE_DIRAUTH int validate_recommended_package_line(const char *line); -#endif +#else + +static inline int +validate_recommended_package_line(const char *line) +{ + (void) line; + return 0; +} + +#endif /* defined(HAVE_MODULE_DIRAUTH) */ + +#endif /* !defined(TOR_RECOMMEND_PKG_H) */ diff --git a/src/feature/dirauth/shared_random.c b/src/feature/dirauth/shared_random.c index 34b2283250..fd55008242 100644 --- a/src/feature/dirauth/shared_random.c +++ b/src/feature/dirauth/shared_random.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2019, The Tor Project, Inc. */ +/* Copyright (c) 2016-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -90,7 +90,7 @@ #include "core/or/or.h" #include "feature/dirauth/shared_random.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confmgt.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" #include "feature/nodelist/networkstatus.h" @@ -99,29 +99,31 @@ #include "feature/nodelist/dirlist.h" #include "feature/hs_common/shared_random_client.h" #include "feature/dirauth/shared_random_state.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/dirauth_sys.h" +#include "feature/dirauth/dirauth_options_st.h" #include "feature/nodelist/authority_cert_st.h" #include "feature/nodelist/networkstatus_st.h" -/* String prefix of shared random values in votes/consensuses. */ +/** String prefix of shared random values in votes/consensuses. */ static const char previous_srv_str[] = "shared-rand-previous-value"; static const char current_srv_str[] = "shared-rand-current-value"; static const char commit_ns_str[] = "shared-rand-commit"; static const char sr_flag_ns_str[] = "shared-rand-participate"; -/* The value of the consensus param AuthDirNumSRVAgreements found in the +/** The value of the consensus param AuthDirNumSRVAgreements found in the * vote. This is set once the consensus creation subsystem requests the * SRV(s) that should be put in the consensus. We use this value to decide * if we keep or not an SRV. */ static int32_t num_srv_agreements_from_vote; -/* Return a heap allocated copy of the SRV <b>orig</b>. */ -STATIC sr_srv_t * -srv_dup(const sr_srv_t *orig) +/** Return a heap allocated copy of the SRV <b>orig</b>. */ +sr_srv_t * +sr_srv_dup(const sr_srv_t *orig) { sr_srv_t *duplicate = NULL; @@ -135,7 +137,7 @@ srv_dup(const sr_srv_t *orig) return duplicate; } -/* Allocate a new commit object and initializing it with <b>rsa_identity</b> +/** Allocate a new commit object and initializing it with <b>rsa_identity</b> * that MUST be provided. The digest algorithm is set to the default one * that is supported. The rest is uninitialized. This never returns NULL. */ static sr_commit_t * @@ -153,7 +155,7 @@ commit_new(const char *rsa_identity) return commit; } -/* Issue a log message describing <b>commit</b>. */ +/** Issue a log message describing <b>commit</b>. */ static void commit_log(const sr_commit_t *commit) { @@ -166,7 +168,7 @@ commit_log(const sr_commit_t *commit) commit->reveal_ts, safe_str(commit->encoded_reveal)); } -/* Make sure that the commitment and reveal information in <b>commit</b> +/** Make sure that the commitment and reveal information in <b>commit</b> * match. If they match return 0, return -1 otherwise. This function MUST be * used everytime we receive a new reveal value. Furthermore, the commit * object MUST have a reveal value and the hash of the reveal value. */ @@ -220,15 +222,15 @@ verify_commit_and_reveal(const sr_commit_t *commit) return -1; } -/* Return true iff the commit contains an encoded reveal value. */ +/** Return true iff the commit contains an encoded reveal value. */ STATIC int commit_has_reveal_value(const sr_commit_t *commit) { - return !tor_mem_is_zero(commit->encoded_reveal, + return !fast_mem_is_zero(commit->encoded_reveal, sizeof(commit->encoded_reveal)); } -/* Parse the encoded commit. The format is: +/** Parse the encoded commit. The format is: * base64-encode( TIMESTAMP || H(REVEAL) ) * * If successfully decoded and parsed, commit is updated and 0 is returned. @@ -283,7 +285,7 @@ commit_decode(const char *encoded, sr_commit_t *commit) return -1; } -/* Parse the b64 blob at <b>encoded</b> containing reveal information and +/** Parse the b64 blob at <b>encoded</b> containing reveal information and * store the information in-place in <b>commit</b>. Return 0 on success else * a negative value. */ STATIC int @@ -333,7 +335,7 @@ reveal_decode(const char *encoded, sr_commit_t *commit) return -1; } -/* Encode a reveal element using a given commit object to dst which is a +/** Encode a reveal element using a given commit object to dst which is a * buffer large enough to put the base64-encoded reveal construction. The * format is as follow: * REVEAL = base64-encode( TIMESTAMP || H(RN) ) @@ -362,7 +364,7 @@ reveal_encode(const sr_commit_t *commit, char *dst, size_t len) return ret; } -/* Encode the given commit object to dst which is a buffer large enough to +/** Encode the given commit object to dst which is a buffer large enough to * put the base64-encoded commit. The format is as follow: * COMMIT = base64-encode( TIMESTAMP || H(H(RN)) ) * Return base64 encoded length on success else a negative value. @@ -388,14 +390,14 @@ commit_encode(const sr_commit_t *commit, char *dst, size_t len) return base64_encode(dst, len, buf, sizeof(buf), 0); } -/* Cleanup both our global state and disk state. */ +/** Cleanup both our global state and disk state. */ static void sr_cleanup(void) { sr_state_free_all(); } -/* Using <b>commit</b>, return a newly allocated string containing the commit +/** Using <b>commit</b>, return a newly allocated string containing the commit * information that should be used during SRV calculation. It's the caller * responsibility to free the memory. Return NULL if this is not a commit to be * used for SRV calculation. */ @@ -414,7 +416,7 @@ get_srv_element_from_commit(const sr_commit_t *commit) return element; } -/* Return a srv object that is built with the construction: +/** Return a srv object that is built with the construction: * SRV = SHA3-256("shared-random" | INT_8(reveal_num) | * INT_4(version) | HASHED_REVEALS | previous_SRV) * This function cannot fail. */ @@ -456,7 +458,7 @@ generate_srv(const char *hashed_reveals, uint64_t reveal_num, return srv; } -/* Compare reveal values and return the result. This should exclusively be +/** Compare reveal values and return the result. This should exclusively be * used by smartlist_sort(). */ static int compare_reveal_(const void **_a, const void **_b) @@ -466,7 +468,7 @@ compare_reveal_(const void **_a, const void **_b) sizeof(a->hashed_reveal)); } -/* Given <b>commit</b> give the line that we should place in our votes. +/** Given <b>commit</b> give the line that we should place in our votes. * It's the responsibility of the caller to free the string. */ static char * get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase) @@ -486,7 +488,7 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase) { /* Send a reveal value for this commit if we have one. */ const char *reveal_str = commit->encoded_reveal; - if (tor_mem_is_zero(commit->encoded_reveal, + if (fast_mem_is_zero(commit->encoded_reveal, sizeof(commit->encoded_reveal))) { reveal_str = ""; } @@ -506,7 +508,7 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase) return vote_line; } -/* Return a heap allocated string that contains the given <b>srv</b> string +/** Return a heap allocated string that contains the given <b>srv</b> string * representation formatted for a networkstatus document using the * <b>key</b> as the start of the line. This doesn't return NULL. */ static char * @@ -524,7 +526,7 @@ srv_to_ns_string(const sr_srv_t *srv, const char *key) return srv_str; } -/* Given the previous SRV and the current SRV, return a heap allocated +/** Given the previous SRV and the current SRV, return a heap allocated * string with their data that could be put in a vote or a consensus. Caller * must free the returned string. Return NULL if no SRVs were provided. */ static char * @@ -557,7 +559,7 @@ get_ns_str_from_sr_values(const sr_srv_t *prev_srv, const sr_srv_t *cur_srv) return srv_str; } -/* Return 1 iff the two commits have the same commitment values. This +/** Return 1 iff the two commits have the same commitment values. This * function does not care about reveal values. */ STATIC int commitments_are_the_same(const sr_commit_t *commit_one, @@ -572,7 +574,7 @@ commitments_are_the_same(const sr_commit_t *commit_one, return 1; } -/* We just received a commit from the vote of authority with +/** We just received a commit from the vote of authority with * <b>identity_digest</b>. Return 1 if this commit is authorititative that * is, it belongs to the authority that voted it. Else return 0 if not. */ STATIC int @@ -586,7 +588,7 @@ commit_is_authoritative(const sr_commit_t *commit, sizeof(commit->rsa_identity)); } -/* Decide if the newly received <b>commit</b> should be kept depending on +/** Decide if the newly received <b>commit</b> should be kept depending on * the current phase and state of the protocol. The <b>voter_key</b> is the * RSA identity key fingerprint of the authority's vote from which the * commit comes from. The <b>phase</b> is the phase we should be validating @@ -705,7 +707,7 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key, return 0; } -/* We are in reveal phase and we found a valid and verified <b>commit</b> in +/** We are in reveal phase and we found a valid and verified <b>commit</b> in * a vote that contains reveal values that we could use. Update the commit * we have in our state. Never call this with an unverified commit. */ STATIC void @@ -726,7 +728,7 @@ save_commit_during_reveal_phase(const sr_commit_t *commit) sr_state_copy_reveal_info(saved_commit, commit); } -/* Save <b>commit</b> to our persistent state. Depending on the current +/** Save <b>commit</b> to our persistent state. Depending on the current * phase, different actions are taken. Steals reference of <b>commit</b>. * The commit object MUST be valid and verified before adding it to the * state. */ @@ -751,7 +753,7 @@ save_commit_to_state(sr_commit_t *commit) } } -/* Return 1 if we should we keep an SRV voted by <b>n_agreements</b> auths. +/** Return 1 if we should we keep an SRV voted by <b>n_agreements</b> auths. * Return 0 if we should ignore it. */ static int should_keep_srv(int n_agreements) @@ -781,7 +783,7 @@ should_keep_srv(int n_agreements) return 1; } -/* Helper: compare two DIGEST256_LEN digests. */ +/** Helper: compare two DIGEST256_LEN digests. */ static int compare_srvs_(const void **_a, const void **_b) { @@ -789,7 +791,7 @@ compare_srvs_(const void **_a, const void **_b) return tor_memcmp(a->value, b->value, sizeof(a->value)); } -/* Return the most frequent member of the sorted list of DIGEST256_LEN +/** Return the most frequent member of the sorted list of DIGEST256_LEN * digests in <b>sl</b> with the count of that most frequent element. */ static sr_srv_t * smartlist_get_most_frequent_srv(const smartlist_t *sl, int *count_out) @@ -806,7 +808,7 @@ compare_srv_(const void **_a, const void **_b) sizeof(a->value)); } -/* Using a list of <b>votes</b>, return the SRV object from them that has +/** Using a list of <b>votes</b>, return the SRV object from them that has * been voted by the majority of dirauths. If <b>current</b> is set, we look * for the current SRV value else the previous one. The returned pointer is * an object located inside a vote. NULL is returned if no appropriate value @@ -868,7 +870,7 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current) return the_srv; } -/* Free a commit object. */ +/** Free a commit object. */ void sr_commit_free_(sr_commit_t *commit) { @@ -880,7 +882,7 @@ sr_commit_free_(sr_commit_t *commit) tor_free(commit); } -/* Generate the commitment/reveal value for the protocol run starting at +/** Generate the commitment/reveal value for the protocol run starting at * <b>timestamp</b>. <b>my_rsa_cert</b> is our authority RSA certificate. */ sr_commit_t * sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert) @@ -937,7 +939,8 @@ sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert) return NULL; } -/* Compute the shared random value based on the active commits in our state. */ +/** Compute the shared random value based on the active commits in our + * state. */ void sr_compute_srv(void) { @@ -1010,7 +1013,7 @@ sr_compute_srv(void) tor_free(reveals); } -/* Parse a commit from a vote or from our disk state and return a newly +/** Parse a commit from a vote or from our disk state and return a newly * allocated commit object. NULL is returned on error. * * The commit's data is in <b>args</b> and the order matters very much: @@ -1082,7 +1085,7 @@ sr_parse_commit(const smartlist_t *args) return NULL; } -/* Called when we are done parsing a vote by <b>voter_key</b> that might +/** Called when we are done parsing a vote by <b>voter_key</b> that might * contain some useful <b>commits</b>. Find if any of them should be kept * and update our state accordingly. Once done, the list of commitments will * be empty. */ @@ -1120,7 +1123,7 @@ sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key) } SMARTLIST_FOREACH_END(commit); } -/* Return a heap-allocated string containing commits that should be put in +/** Return a heap-allocated string containing commits that should be put in * the votes. It's the responsibility of the caller to free the string. * This always return a valid string, either empty or with line(s). */ char * @@ -1129,7 +1132,7 @@ sr_get_string_for_vote(void) char *vote_str = NULL; digestmap_t *state_commits; smartlist_t *chunks = smartlist_new(); - const or_options_t *options = get_options(); + const dirauth_options_t *options = dirauth_get_options(); /* Are we participating in the protocol? */ if (!options->AuthDirSharedRandomness) { @@ -1178,7 +1181,7 @@ sr_get_string_for_vote(void) return vote_str; } -/* Return a heap-allocated string that should be put in the consensus and +/** Return a heap-allocated string that should be put in the consensus and * contains the shared randomness values. It's the responsibility of the * caller to free the string. NULL is returned if no SRV(s) available. * @@ -1194,7 +1197,7 @@ sr_get_string_for_consensus(const smartlist_t *votes, int32_t num_srv_agreements) { char *srv_str; - const or_options_t *options = get_options(); + const dirauth_options_t *options = dirauth_get_options(); tor_assert(votes); @@ -1222,7 +1225,7 @@ sr_get_string_for_consensus(const smartlist_t *votes, return NULL; } -/* We just computed a new <b>consensus</b>. Update our state with the SRVs +/** We just computed a new <b>consensus</b>. Update our state with the SRVs * from the consensus (might be NULL as well). Register the SRVs in our SR * state and prepare for the upcoming protocol round. */ void @@ -1253,15 +1256,15 @@ sr_act_post_consensus(const networkstatus_t *consensus) * decided by the majority. */ sr_state_unset_fresh_srv(); /* Set the SR values from the given consensus. */ - sr_state_set_previous_srv(srv_dup(consensus->sr_info.previous_srv)); - sr_state_set_current_srv(srv_dup(consensus->sr_info.current_srv)); + sr_state_set_previous_srv(sr_srv_dup(consensus->sr_info.previous_srv)); + sr_state_set_current_srv(sr_srv_dup(consensus->sr_info.current_srv)); } /* Prepare our state so that it's ready for the next voting period. */ - sr_state_update(voting_schedule_get_next_valid_after_time()); + sr_state_update(dirauth_sched_get_next_valid_after_time()); } -/* Initialize shared random subsystem. This MUST be called early in the boot +/** Initialize shared random subsystem. This MUST be called early in the boot * process of tor. Return 0 on success else -1 on error. */ int sr_init(int save_to_disk) @@ -1269,7 +1272,7 @@ sr_init(int save_to_disk) return sr_state_init(save_to_disk, 1); } -/* Save our state to disk and cleanup everything. */ +/** Save our state to disk and cleanup everything. */ void sr_save_and_cleanup(void) { @@ -1279,7 +1282,7 @@ sr_save_and_cleanup(void) #ifdef TOR_UNIT_TESTS -/* Set the global value of number of SRV agreements so the test can play +/** Set the global value of number of SRV agreements so the test can play * along by calling specific functions that don't parse the votes prior for * the AuthDirNumSRVAgreements value. */ void diff --git a/src/feature/dirauth/shared_random.h b/src/feature/dirauth/shared_random.h index 25d95ebbc7..c4e259dcdb 100644 --- a/src/feature/dirauth/shared_random.h +++ b/src/feature/dirauth/shared_random.h @@ -1,86 +1,88 @@ -/* Copyright (c) 2016-2019, The Tor Project, Inc. */ +/* Copyright (c) 2016-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_SHARED_RANDOM_H #define TOR_SHARED_RANDOM_H -/* - * This file contains ABI/API of the shared random protocol defined in +/** + * \file shared_random.h + * + * \brief This file contains ABI/API of the shared random protocol defined in * proposal #250. Every public functions and data structure are namespaced * with "sr_" which stands for shared random. */ #include "core/or/or.h" -/* Protocol version */ +/** Protocol version */ #define SR_PROTO_VERSION 1 -/* Default digest algorithm. */ +/** Default digest algorithm. */ #define SR_DIGEST_ALG DIGEST_SHA3_256 -/* Invariant token in the SRV calculation. */ +/** Invariant token in the SRV calculation. */ #define SR_SRV_TOKEN "shared-random" -/* Don't count the NUL terminated byte even though the TOKEN has it. */ +/** Don't count the NUL terminated byte even though the TOKEN has it. */ #define SR_SRV_TOKEN_LEN (sizeof(SR_SRV_TOKEN) - 1) -/* Length of the random number (in bytes). */ +/** Length of the random number (in bytes). */ #define SR_RANDOM_NUMBER_LEN 32 -/* Size of a decoded commit value in a vote or state. It's a hash and a +/** Size of a decoded commit value in a vote or state. It's a hash and a * timestamp. It adds up to 40 bytes. */ #define SR_COMMIT_LEN (sizeof(uint64_t) + DIGEST256_LEN) -/* Size of a decoded reveal value from a vote or state. It's a 64 bit +/** Size of a decoded reveal value from a vote or state. It's a 64 bit * timestamp and the hashed random number. This adds up to 40 bytes. */ #define SR_REVEAL_LEN (sizeof(uint64_t) + DIGEST256_LEN) -/* Size of SRV message length. The construction is has follow: +/** Size of SRV message length. The construction is has follow: * "shared-random" | INT_8(reveal_num) | INT_4(version) | PREV_SRV */ #define SR_SRV_MSG_LEN \ (SR_SRV_TOKEN_LEN + sizeof(uint64_t) + sizeof(uint32_t) + DIGEST256_LEN) -/* Length of base64 encoded commit NOT including the NUL terminated byte. +/** Length of base64 encoded commit NOT including the NUL terminated byte. * Formula is taken from base64_encode_size. This adds up to 56 bytes. */ #define SR_COMMIT_BASE64_LEN (BASE64_LEN(SR_COMMIT_LEN)) -/* Length of base64 encoded reveal NOT including the NUL terminated byte. +/** Length of base64 encoded reveal NOT including the NUL terminated byte. * Formula is taken from base64_encode_size. This adds up to 56 bytes. */ #define SR_REVEAL_BASE64_LEN (BASE64_LEN(SR_REVEAL_LEN)) -/* Length of base64 encoded shared random value. It's 32 bytes long so 44 +/** Length of base64 encoded shared random value. It's 32 bytes long so 44 * bytes from the base64_encode_size formula. That includes the '=' * character at the end. */ #define SR_SRV_VALUE_BASE64_LEN (BASE64_LEN(DIGEST256_LEN)) -/* Assert if commit valid flag is not set. */ +/** Assert if commit valid flag is not set. */ #define ASSERT_COMMIT_VALID(c) tor_assert((c)->valid) -/* Protocol phase. */ +/** Protocol phase. */ typedef enum { - /* Commitment phase */ + /** Commitment phase */ SR_PHASE_COMMIT = 1, - /* Reveal phase */ + /** Reveal phase */ SR_PHASE_REVEAL = 2, } sr_phase_t; -/* A shared random value (SRV). */ +/** A shared random value (SRV). */ typedef struct sr_srv_t { - /* The number of reveal values used to derive this SRV. */ + /** The number of reveal values used to derive this SRV. */ uint64_t num_reveals; - /* The actual value. This is the stored result of SHA3-256. */ + /** The actual value. This is the stored result of SHA3-256. */ uint8_t value[DIGEST256_LEN]; } sr_srv_t; -/* A commit (either ours or from another authority). */ +/** A commit (either ours or from another authority). */ typedef struct sr_commit_t { - /* Hashing algorithm used. */ + /** Hashing algorithm used. */ digest_algorithm_t alg; - /* Indicate if this commit has been verified thus valid. */ + /** Indicate if this commit has been verified thus valid. */ unsigned int valid:1; /* Commit owner info */ - /* The RSA identity key of the authority and its base16 representation, + /** The RSA identity key of the authority and its base16 representation, * which includes the NUL terminated byte. */ char rsa_identity[DIGEST_LEN]; char rsa_identity_hex[HEX_DIGEST_LEN + 1]; /* Commitment information */ - /* Timestamp of reveal. Correspond to TIMESTAMP. */ + /** Timestamp of reveal. Correspond to TIMESTAMP. */ uint64_t reveal_ts; /* H(REVEAL) as found in COMMIT message. */ char hashed_reveal[DIGEST256_LEN]; @@ -89,13 +91,13 @@ typedef struct sr_commit_t { /* Reveal information */ - /* H(RN) which is what we used as the random value for this commit. We + /** H(RN) which is what we used as the random value for this commit. We * don't use the raw bytes since those are sent on the network thus * avoiding possible information leaks of our PRNG. */ uint8_t random_number[SR_RANDOM_NUMBER_LEN]; - /* Timestamp of commit. Correspond to TIMESTAMP. */ + /** Timestamp of commit. Correspond to TIMESTAMP. */ uint64_t commit_ts; - /* This is the whole reveal message. We use it during verification */ + /** This is the whole reveal message. We use it during verification */ char encoded_reveal[SR_REVEAL_BASE64_LEN + 1]; } sr_commit_t; @@ -110,7 +112,7 @@ int sr_init(int save_to_disk); void sr_save_and_cleanup(void); void sr_act_post_consensus(const networkstatus_t *consensus); -#else /* HAVE_MODULE_DIRAUTH */ +#else /* !defined(HAVE_MODULE_DIRAUTH) */ static inline int sr_init(int save_to_disk) @@ -131,7 +133,7 @@ sr_act_post_consensus(const networkstatus_t *consensus) (void) consensus; } -#endif /* HAVE_MODULE_DIRAUTH */ +#endif /* defined(HAVE_MODULE_DIRAUTH) */ /* Public methods used only by dirauth code. */ @@ -154,6 +156,7 @@ const char *sr_commit_get_rsa_fpr(const sr_commit_t *commit) void sr_compute_srv(void); sr_commit_t *sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert); +sr_srv_t *sr_srv_dup(const sr_srv_t *orig); #ifdef SHARED_RANDOM_PRIVATE @@ -172,7 +175,6 @@ STATIC sr_srv_t *get_majority_srv_from_votes(const smartlist_t *votes, int current); STATIC void save_commit_to_state(sr_commit_t *commit); -STATIC sr_srv_t *srv_dup(const sr_srv_t *orig); STATIC int commitments_are_the_same(const sr_commit_t *commit_one, const sr_commit_t *commit_two); STATIC int commit_is_authoritative(const sr_commit_t *commit, @@ -191,4 +193,3 @@ void set_num_srv_agreements(int32_t value); #endif /* TOR_UNIT_TESTS */ #endif /* !defined(TOR_SHARED_RANDOM_H) */ - diff --git a/src/feature/dirauth/shared_random_state.c b/src/feature/dirauth/shared_random_state.c index b3e4a4ef92..07bc757506 100644 --- a/src/feature/dirauth/shared_random_state.c +++ b/src/feature/dirauth/shared_random_state.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2019, The Tor Project, Inc. */ +/* Copyright (c) 2016-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -12,7 +12,7 @@ #include "core/or/or.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confmgt.h" #include "lib/crypt_ops/crypto_util.h" #include "feature/dirauth/dirvote.h" #include "feature/nodelist/networkstatus.h" @@ -20,23 +20,24 @@ #include "feature/dirauth/shared_random.h" #include "feature/hs_common/shared_random_client.h" #include "feature/dirauth/shared_random_state.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "lib/encoding/confline.h" +#include "lib/version/torversion.h" #include "app/config/or_state_st.h" -/* Default filename of the shared random state on disk. */ +/** Default filename of the shared random state on disk. */ static const char default_fname[] = "sr-state"; -/* String representation of a protocol phase. */ +/** String representation of a protocol phase. */ static const char *phase_str[] = { "unknown", "commit", "reveal" }; -/* Our shared random protocol state. There is only one possible state per +/** Our shared random protocol state. There is only one possible state per * protocol run so this is the global state which is reset at every run once * the shared random value has been computed. */ static sr_state_t *sr_state = NULL; -/* Representation of our persistent state on disk. The sr_state above +/** Representation of our persistent state on disk. The sr_state above * contains the data parsed from this state. When we save to disk, we * translate the sr_state to this sr_disk_state. */ static sr_disk_state_t *sr_disk_state = NULL; @@ -50,24 +51,18 @@ static const char dstate_cur_srv_key[] = "SharedRandCurrentValue"; * members with CONF_CHECK_VAR_TYPE. */ DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t); -/* These next two are duplicates or near-duplicates from config.c */ -#define VAR(name, conftype, member, initvalue) \ - { name, CONFIG_TYPE_ ## conftype, offsetof(sr_disk_state_t, member), \ - initvalue CONF_TEST_MEMBERS(sr_disk_state_t, conftype, member) } -/* As VAR, but the option name and member name are the same. */ -#define V(member, conftype, initvalue) \ +#define VAR(varname,conftype,member,initvalue) \ + CONFIG_VAR_ETYPE(sr_disk_state_t, varname, conftype, member, 0, initvalue) +#define V(member,conftype,initvalue) \ VAR(#member, conftype, member, initvalue) -/* Our persistent state magic number. */ -#define SR_DISK_STATE_MAGIC 0x98AB1254 -static int -disk_state_validate_cb(void *old_state, void *state, void *default_state, - int from_setconf, char **msg); -static void disk_state_free_cb(void *); +/** Our persistent state magic number. */ +#define SR_DISK_STATE_MAGIC 0x98AB1254 -/* Array of variables that are saved to disk as a persistent state. */ -static config_var_t state_vars[] = { - V(Version, UINT, "0"), +/** Array of variables that are saved to disk as a persistent state. */ +// clang-format off +static const config_var_t state_vars[] = { + V(Version, POSINT, "0"), V(TorVersion, STRING, NULL), V(ValidAfter, ISOTIME, NULL), V(ValidUntil, ISOTIME, NULL), @@ -79,29 +74,45 @@ static config_var_t state_vars[] = { VAR("SharedRandCurrentValue", LINELIST_S, SharedRandValues, NULL), END_OF_CONFIG_VARS }; +// clang-format on -/* "Extra" variable in the state that receives lines we can't parse. This +/** "Extra" variable in the state that receives lines we can't parse. This * lets us preserve options from versions of Tor newer than us. */ -static config_var_t state_extra_var = { - "__extra", CONFIG_TYPE_LINELIST, - offsetof(sr_disk_state_t, ExtraLines), NULL - CONF_TEST_MEMBERS(sr_disk_state_t, LINELIST, ExtraLines) +static const struct_member_t state_extra_var = { + .name = "__extra", + .type = CONFIG_TYPE_LINELIST, + .offset = offsetof(sr_disk_state_t, ExtraLines), }; -/* Configuration format of sr_disk_state_t. */ +/** Configuration format of sr_disk_state_t. */ static const config_format_t state_format = { - sizeof(sr_disk_state_t), - SR_DISK_STATE_MAGIC, - offsetof(sr_disk_state_t, magic_), - NULL, - NULL, - state_vars, - disk_state_validate_cb, - disk_state_free_cb, - &state_extra_var, + .size = sizeof(sr_disk_state_t), + .magic = { + "sr_disk_state_t", + SR_DISK_STATE_MAGIC, + offsetof(sr_disk_state_t, magic_), + }, + .vars = state_vars, + .extra = &state_extra_var, }; -/* Return a string representation of a protocol phase. */ +/** Global configuration manager for the shared-random state file */ +static config_mgr_t *shared_random_state_mgr = NULL; + +/** Return the configuration manager for the shared-random state file. */ +static const config_mgr_t * +get_srs_mgr(void) +{ + if (PREDICT_UNLIKELY(shared_random_state_mgr == NULL)) { + shared_random_state_mgr = config_mgr_new(&state_format); + config_mgr_freeze(shared_random_state_mgr); + } + return shared_random_state_mgr; +} + +static void state_query_del_(sr_state_object_t obj_type, void *data); + +/** Return a string representation of a protocol phase. */ STATIC const char * get_phase_str(sr_phase_t phase) { @@ -119,7 +130,7 @@ get_phase_str(sr_phase_t phase) return the_string; } -/* Return the time we should expire the state file created at <b>now</b>. +/** Return the time we should expire the state file created at <b>now</b>. * We expire the state file in the beginning of the next protocol run. */ STATIC time_t get_state_valid_until_time(time_t now) @@ -130,7 +141,7 @@ get_state_valid_until_time(time_t now) voting_interval = get_voting_interval(); /* Find the time the current round started. */ - beginning_of_current_round = get_start_time_of_current_round(); + beginning_of_current_round = dirauth_sched_get_cur_valid_after_time(); /* Find how many rounds are left till the end of the protocol run */ current_round = (now / voting_interval) % total_rounds; @@ -150,7 +161,7 @@ get_state_valid_until_time(time_t now) return valid_until; } -/* Given the consensus 'valid-after' time, return the protocol phase we should +/** Given the consensus 'valid-after' time, return the protocol phase we should * be in. */ STATIC sr_phase_t get_sr_protocol_phase(time_t valid_after) @@ -170,7 +181,7 @@ get_sr_protocol_phase(time_t valid_after) } } -/* Add the given <b>commit</b> to <b>state</b>. It MUST be a valid commit +/** Add the given <b>commit</b> to <b>state</b>. It MUST be a valid commit * and there shouldn't be a commit from the same authority in the state * already else verification hasn't been done prior. This takes ownership of * the commit once in our state. */ @@ -195,7 +206,7 @@ commit_add_to_state(sr_commit_t *commit, sr_state_t *state) } } -/* Helper: deallocate a commit object. (Used with digestmap_free(), which +/** Helper: deallocate a commit object. (Used with digestmap_free(), which * requires a function pointer whose argument is void *). */ static void commit_free_(void *p) @@ -206,7 +217,7 @@ commit_free_(void *p) #define state_free(val) \ FREE_AND_NULL(sr_state_t, state_free_, (val)) -/* Free a state that was allocated with state_new(). */ +/** Free a state that was allocated with state_new(). */ static void state_free_(sr_state_t *state) { @@ -220,7 +231,7 @@ state_free_(sr_state_t *state) tor_free(state); } -/* Allocate an sr_state_t object and returns it. If no <b>fname</b>, the +/** Allocate an sr_state_t object and returns it. If no <b>fname</b>, the * default file name is used. This function does NOT initialize the state * timestamp, phase or shared random value. NULL is never returned. */ static sr_state_t * @@ -239,7 +250,7 @@ state_new(const char *fname, time_t now) return new_state; } -/* Set our global state pointer with the one given. */ +/** Set our global state pointer with the one given. */ static void state_set(sr_state_t *state) { @@ -253,34 +264,33 @@ state_set(sr_state_t *state) #define disk_state_free(val) \ FREE_AND_NULL(sr_disk_state_t, disk_state_free_, (val)) -/* Free an allocated disk state. */ +/** Free an allocated disk state. */ static void disk_state_free_(sr_disk_state_t *state) { if (state == NULL) { return; } - config_free(&state_format, state); + config_free(get_srs_mgr(), state); } -/* Allocate a new disk state, initialize it and return it. */ +/** Allocate a new disk state, initialize it and return it. */ static sr_disk_state_t * disk_state_new(time_t now) { - sr_disk_state_t *new_state = tor_malloc_zero(sizeof(*new_state)); + sr_disk_state_t *new_state = config_new(get_srs_mgr()); - new_state->magic_ = SR_DISK_STATE_MAGIC; new_state->Version = SR_PROTO_VERSION; new_state->TorVersion = tor_strdup(get_version()); new_state->ValidUntil = get_state_valid_until_time(now); new_state->ValidAfter = now; /* Init config format. */ - config_init(&state_format, new_state); + config_init(get_srs_mgr(), new_state); return new_state; } -/* Set our global disk state with the given state. */ +/** Set our global disk state with the given state. */ static void disk_state_set(sr_disk_state_t *state) { @@ -291,7 +301,7 @@ disk_state_set(sr_disk_state_t *state) sr_disk_state = state; } -/* Return -1 if the disk state is invalid (something in there that we can't or +/** Return -1 if the disk state is invalid (something in there that we can't or * shouldn't use). Return 0 if everything checks out. */ static int disk_state_validate(const sr_disk_state_t *state) @@ -326,31 +336,7 @@ disk_state_validate(const sr_disk_state_t *state) return -1; } -/* Validate the disk state (NOP for now). */ -static int -disk_state_validate_cb(void *old_state, void *state, void *default_state, - int from_setconf, char **msg) -{ - /* We don't use these; only options do. */ - (void) from_setconf; - (void) default_state; - (void) old_state; - - /* This is called by config_dump which is just before we are about to - * write it to disk. At that point, our global memory state has been - * copied to the disk state so it's fair to assume it's trustable. */ - (void) state; - (void) msg; - return 0; -} - -static void -disk_state_free_cb(void *state) -{ - disk_state_free_(state); -} - -/* Parse the Commit line(s) in the disk state and translate them to the +/** Parse the Commit line(s) in the disk state and translate them to the * the memory state. Return 0 on success else -1 on error. */ static int disk_state_parse_commits(sr_state_t *state, @@ -405,7 +391,7 @@ disk_state_parse_commits(sr_state_t *state, return -1; } -/* Parse a share random value line from the disk state and save it to dst +/** Parse a share random value line from the disk state and save it to dst * which is an allocated srv object. Return 0 on success else -1. */ static int disk_state_parse_srv(const char *value, sr_srv_t *dst) @@ -440,7 +426,7 @@ disk_state_parse_srv(const char *value, sr_srv_t *dst) return ret; } -/* Parse both SharedRandCurrentValue and SharedRandPreviousValue line from +/** Parse both SharedRandCurrentValue and SharedRandPreviousValue line from * the state. Return 0 on success else -1. */ static int disk_state_parse_sr_values(sr_state_t *state, @@ -491,7 +477,7 @@ disk_state_parse_sr_values(sr_state_t *state, return -1; } -/* Parse the given disk state and set a newly allocated state. On success, +/** Parse the given disk state and set a newly allocated state. On success, * return that state else NULL. */ static sr_state_t * disk_state_parse(const sr_disk_state_t *new_disk_state) @@ -525,7 +511,7 @@ disk_state_parse(const sr_disk_state_t *new_disk_state) return NULL; } -/* From a valid commit object and an allocated config line, set the line's +/** From a valid commit object and an allocated config line, set the line's * value to the state string representation of a commit. */ static void disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line) @@ -535,7 +521,7 @@ disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line) tor_assert(commit); tor_assert(line); - if (!tor_mem_is_zero(commit->encoded_reveal, + if (!fast_mem_is_zero(commit->encoded_reveal, sizeof(commit->encoded_reveal))) { /* Add extra whitespace so we can format the line correctly. */ tor_asprintf(&reveal_str, " %s", commit->encoded_reveal); @@ -552,7 +538,7 @@ disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line) } } -/* From a valid srv object and an allocated config line, set the line's +/** From a valid srv object and an allocated config line, set the line's * value to the state string representation of a shared random value. */ static void disk_state_put_srv_line(const sr_srv_t *srv, config_line_t *line) @@ -570,7 +556,7 @@ disk_state_put_srv_line(const sr_srv_t *srv, config_line_t *line) tor_asprintf(&line->value, "%" PRIu64 " %s", srv->num_reveals, encoded); } -/* Reset disk state that is free allocated memory and zeroed the object. */ +/** Reset disk state that is free allocated memory and zeroed the object. */ static void disk_state_reset(void) { @@ -580,15 +566,16 @@ disk_state_reset(void) config_free_lines(sr_disk_state->ExtraLines); tor_free(sr_disk_state->TorVersion); - /* Clean up the struct */ - memset(sr_disk_state, 0, sizeof(*sr_disk_state)); + /* Clear other fields. */ + sr_disk_state->ValidAfter = 0; + sr_disk_state->ValidUntil = 0; + sr_disk_state->Version = 0; /* Reset it with useful data */ - sr_disk_state->magic_ = SR_DISK_STATE_MAGIC; sr_disk_state->TorVersion = tor_strdup(get_version()); } -/* Update our disk state based on our global SR state. */ +/** Update our disk state based on our global SR state. */ static void disk_state_update(void) { @@ -632,7 +619,7 @@ disk_state_update(void) } DIGESTMAP_FOREACH_END; } -/* Load state from disk and put it into our disk state. If the state passes +/** Load state from disk and put it into our disk state. If the state passes * validation, our global state will be updated with it. Return 0 on * success. On error, -EINVAL is returned if the state on disk did contained * something malformed or is unreadable. -ENOENT is returned indicating that @@ -650,7 +637,7 @@ disk_state_load_from_disk(void) return ret; } -/* Helper for disk_state_load_from_disk(). */ +/** Helper for disk_state_load_from_disk(). */ STATIC int disk_state_load_from_disk_impl(const char *fname) { @@ -679,7 +666,7 @@ disk_state_load_from_disk_impl(const char *fname) } disk_state = disk_state_new(time(NULL)); - config_assign(&state_format, disk_state, lines, 0, &errmsg); + config_assign(get_srs_mgr(), disk_state, lines, 0, &errmsg); config_free_lines(lines); if (errmsg) { log_warn(LD_DIR, "SR: Reading state error: %s", errmsg); @@ -712,7 +699,7 @@ disk_state_load_from_disk_impl(const char *fname) return ret; } -/* Save the disk state to disk but before that update it from the current +/** Save the disk state to disk but before that update it from the current * state so we always have the latest. Return 0 on success else -1. */ static int disk_state_save_to_disk(void) @@ -732,7 +719,7 @@ disk_state_save_to_disk(void) /* Make sure that our disk state is up to date with our memory state * before saving it to disk. */ disk_state_update(); - state = config_dump(&state_format, NULL, sr_disk_state, 0, 0); + state = config_dump(get_srs_mgr(), NULL, sr_disk_state, 0, 0); format_local_iso_time(tbuf, now); tor_asprintf(&content, "# Tor shared random state file last generated on %s " @@ -756,7 +743,7 @@ disk_state_save_to_disk(void) return ret; } -/* Reset our state to prepare for a new protocol run. Once this returns, all +/** Reset our state to prepare for a new protocol run. Once this returns, all * commits in the state will be removed and freed. */ STATIC void reset_state_for_new_protocol_run(time_t valid_after) @@ -777,7 +764,7 @@ reset_state_for_new_protocol_run(time_t valid_after) sr_state_delete_commits(); } -/* This is the first round of the new protocol run starting at +/** This is the first round of the new protocol run starting at * <b>valid_after</b>. Do the necessary housekeeping. */ STATIC void new_protocol_run(time_t valid_after) @@ -811,7 +798,7 @@ new_protocol_run(time_t valid_after) } } -/* Return 1 iff the <b>next_phase</b> is a phase transition from the current +/** Return 1 iff the <b>next_phase</b> is a phase transition from the current * phase that is it's different. */ STATIC int is_phase_transition(sr_phase_t next_phase) @@ -819,7 +806,7 @@ is_phase_transition(sr_phase_t next_phase) return sr_state->phase != next_phase; } -/* Helper function: return a commit using the RSA fingerprint of the +/** Helper function: return a commit using the RSA fingerprint of the * authority or NULL if no such commit is known. */ static sr_commit_t * state_query_get_commit(const char *rsa_fpr) @@ -828,11 +815,14 @@ state_query_get_commit(const char *rsa_fpr) return digestmap_get(sr_state->commits, rsa_fpr); } -/* Helper function: This handles the GET state action using an +/** Helper function: This handles the GET state action using an * <b>obj_type</b> and <b>data</b> needed for the action. */ static void * state_query_get_(sr_state_object_t obj_type, const void *data) { + if (BUG(!sr_state)) + return NULL; + void *obj = NULL; switch (obj_type) { @@ -860,24 +850,45 @@ state_query_get_(sr_state_object_t obj_type, const void *data) return obj; } -/* Helper function: This handles the PUT state action using an - * <b>obj_type</b> and <b>data</b> needed for the action. */ +/** Helper function: This handles the PUT state action using an + * <b>obj_type</b> and <b>data</b> needed for the action. + * PUT frees the previous data before replacing it, if needed. */ static void state_query_put_(sr_state_object_t obj_type, void *data) { + if (BUG(!sr_state)) + return; + switch (obj_type) { case SR_STATE_OBJ_COMMIT: { sr_commit_t *commit = data; tor_assert(commit); + /* commit_add_to_state() frees the old commit, if there is one */ commit_add_to_state(commit, sr_state); break; } case SR_STATE_OBJ_CURSRV: - sr_state->current_srv = (sr_srv_t *) data; + /* Check if the new pointer is the same as the old one: if it is, it's + * probably a bug. The caller may have confused current and previous, + * or they may have forgotten to sr_srv_dup(). + * Putting NULL multiple times is allowed. */ + if (!BUG(data && sr_state->current_srv == (sr_srv_t *) data)) { + /* We own the old SRV, so we need to free it. */ + state_query_del_(SR_STATE_OBJ_CURSRV, NULL); + sr_state->current_srv = (sr_srv_t *) data; + } break; case SR_STATE_OBJ_PREVSRV: - sr_state->previous_srv = (sr_srv_t *) data; + /* Check if the new pointer is the same as the old one: if it is, it's + * probably a bug. The caller may have confused current and previous, + * or they may have forgotten to sr_srv_dup(). + * Putting NULL multiple times is allowed. */ + if (!BUG(data && sr_state->previous_srv == (sr_srv_t *) data)) { + /* We own the old SRV, so we need to free it. */ + state_query_del_(SR_STATE_OBJ_PREVSRV, NULL); + sr_state->previous_srv = (sr_srv_t *) data; + } break; case SR_STATE_OBJ_VALID_AFTER: sr_state->valid_after = *((time_t *) data); @@ -892,11 +903,14 @@ state_query_put_(sr_state_object_t obj_type, void *data) } } -/* Helper function: This handles the DEL_ALL state action using an +/** Helper function: This handles the DEL_ALL state action using an * <b>obj_type</b> and <b>data</b> needed for the action. */ static void state_query_del_all_(sr_state_object_t obj_type) { + if (BUG(!sr_state)) + return; + switch (obj_type) { case SR_STATE_OBJ_COMMIT: { @@ -907,7 +921,7 @@ state_query_del_all_(sr_state_object_t obj_type) } DIGESTMAP_FOREACH_END; break; } - /* The following object are _NOT_ suppose to be removed. */ + /* The following objects are _NOT_ supposed to be removed. */ case SR_STATE_OBJ_CURSRV: case SR_STATE_OBJ_PREVSRV: case SR_STATE_OBJ_PHASE: @@ -918,13 +932,16 @@ state_query_del_all_(sr_state_object_t obj_type) } } -/* Helper function: This handles the DEL state action using an +/** Helper function: This handles the DEL state action using an * <b>obj_type</b> and <b>data</b> needed for the action. */ static void state_query_del_(sr_state_object_t obj_type, void *data) { (void) data; + if (BUG(!sr_state)) + return; + switch (obj_type) { case SR_STATE_OBJ_PREVSRV: tor_free(sr_state->previous_srv); @@ -941,7 +958,7 @@ state_query_del_(sr_state_object_t obj_type, void *data) } } -/* Query state using an <b>action</b> for an object type <b>obj_type</b>. +/** Query state using an <b>action</b> for an object type <b>obj_type</b>. * The <b>data</b> pointer needs to point to an object that the action needs * to use and if anything is required to be returned, it is stored in * <b>out</b>. @@ -983,7 +1000,7 @@ state_query(sr_state_action_t action, sr_state_object_t obj_type, } } -/* Delete the current SRV value from the state freeing it and the value is set +/** Delete the current SRV value from the state freeing it and the value is set * to NULL meaning empty. */ STATIC void state_del_current_srv(void) @@ -991,7 +1008,7 @@ state_del_current_srv(void) state_query(SR_STATE_ACTION_DEL, SR_STATE_OBJ_CURSRV, NULL, NULL); } -/* Delete the previous SRV value from the state freeing it and the value is +/** Delete the previous SRV value from the state freeing it and the value is * set to NULL meaning empty. */ STATIC void state_del_previous_srv(void) @@ -999,20 +1016,20 @@ state_del_previous_srv(void) state_query(SR_STATE_ACTION_DEL, SR_STATE_OBJ_PREVSRV, NULL, NULL); } -/* Rotate SRV value by freeing the previous value, assigning the current - * value to the previous one and nullifying the current one. */ +/** Rotate SRV value by setting the previous SRV to the current SRV, and + * clearing the current SRV. */ STATIC void state_rotate_srv(void) { /* First delete previous SRV from the state. Object will be freed. */ state_del_previous_srv(); - /* Set previous SRV with the current one. */ - sr_state_set_previous_srv(sr_state_get_current_srv()); - /* Nullify the current srv. */ + /* Set previous SRV to a copy of the current one. */ + sr_state_set_previous_srv(sr_srv_dup(sr_state_get_current_srv())); + /* Free and NULL the current srv. */ sr_state_set_current_srv(NULL); } -/* Set valid after time in the our state. */ +/** Set valid after time in the our state. */ void sr_state_set_valid_after(time_t valid_after) { @@ -1020,16 +1037,19 @@ sr_state_set_valid_after(time_t valid_after) (void *) &valid_after, NULL); } -/* Return the phase we are currently in according to our state. */ +/** Return the phase we are currently in according to our state. */ sr_phase_t sr_state_get_phase(void) { - void *ptr; + void *ptr=NULL; state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_PHASE, NULL, &ptr); + tor_assert(ptr); return *(sr_phase_t *) ptr; } -/* Return the previous SRV value from our state. Value CAN be NULL. */ +/** Return the previous SRV value from our state. Value CAN be NULL. + * The state object owns the SRV, so the calling code should not free the SRV. + * Use sr_srv_dup() if you want to keep a copy of the SRV. */ const sr_srv_t * sr_state_get_previous_srv(void) { @@ -1039,7 +1059,7 @@ sr_state_get_previous_srv(void) return srv; } -/* Set the current SRV value from our state. Value CAN be NULL. The srv +/** Set the current SRV value from our state. Value CAN be NULL. The srv * object ownership is transferred to the state object. */ void sr_state_set_previous_srv(const sr_srv_t *srv) @@ -1048,7 +1068,9 @@ sr_state_set_previous_srv(const sr_srv_t *srv) NULL); } -/* Return the current SRV value from our state. Value CAN be NULL. */ +/** Return the current SRV value from our state. Value CAN be NULL. + * The state object owns the SRV, so the calling code should not free the SRV. + * Use sr_srv_dup() if you want to keep a copy of the SRV. */ const sr_srv_t * sr_state_get_current_srv(void) { @@ -1058,7 +1080,7 @@ sr_state_get_current_srv(void) return srv; } -/* Set the current SRV value from our state. Value CAN be NULL. The srv +/** Set the current SRV value from our state. Value CAN be NULL. The srv * object ownership is transferred to the state object. */ void sr_state_set_current_srv(const sr_srv_t *srv) @@ -1067,7 +1089,7 @@ sr_state_set_current_srv(const sr_srv_t *srv) NULL); } -/* Clean all the SRVs in our state. */ +/** Clean all the SRVs in our state. */ void sr_state_clean_srvs(void) { @@ -1076,7 +1098,7 @@ sr_state_clean_srvs(void) state_del_current_srv(); } -/* Return a pointer to the commits map from our state. CANNOT be NULL. */ +/** Return a pointer to the commits map from our state. CANNOT be NULL. */ digestmap_t * sr_state_get_commits(void) { @@ -1087,7 +1109,7 @@ sr_state_get_commits(void) return commits; } -/* Update the current SR state as needed for the upcoming voting round at +/** Update the current SR state as needed for the upcoming voting round at * <b>valid_after</b>. */ void sr_state_update(time_t valid_after) @@ -1151,7 +1173,7 @@ sr_state_update(time_t valid_after) } } -/* Return commit object from the given authority digest <b>rsa_identity</b>. +/** Return commit object from the given authority digest <b>rsa_identity</b>. * Return NULL if not found. */ sr_commit_t * sr_state_get_commit(const char *rsa_identity) @@ -1165,7 +1187,7 @@ sr_state_get_commit(const char *rsa_identity) return commit; } -/* Add <b>commit</b> to the permanent state. The commit object ownership is +/** Add <b>commit</b> to the permanent state. The commit object ownership is * transferred to the state so the caller MUST not free it. */ void sr_state_add_commit(sr_commit_t *commit) @@ -1180,14 +1202,14 @@ sr_state_add_commit(sr_commit_t *commit) sr_commit_get_rsa_fpr(commit)); } -/* Remove all commits from our state. */ +/** Remove all commits from our state. */ void sr_state_delete_commits(void) { state_query(SR_STATE_ACTION_DEL_ALL, SR_STATE_OBJ_COMMIT, NULL, NULL); } -/* Copy the reveal information from <b>commit</b> into <b>saved_commit</b>. +/** Copy the reveal information from <b>commit</b> into <b>saved_commit</b>. * This <b>saved_commit</b> MUST come from our current SR state. Once modified, * the disk state is updated. */ void @@ -1208,7 +1230,7 @@ sr_state_copy_reveal_info(sr_commit_t *saved_commit, const sr_commit_t *commit) sr_commit_get_rsa_fpr(saved_commit)); } -/* Set the fresh SRV flag from our state. This doesn't need to trigger a +/** Set the fresh SRV flag from our state. This doesn't need to trigger a * disk state synchronization so we directly change the state. */ void sr_state_set_fresh_srv(void) @@ -1216,7 +1238,7 @@ sr_state_set_fresh_srv(void) sr_state->is_srv_fresh = 1; } -/* Unset the fresh SRV flag from our state. This doesn't need to trigger a +/** Unset the fresh SRV flag from our state. This doesn't need to trigger a * disk state synchronization so we directly change the state. */ void sr_state_unset_fresh_srv(void) @@ -1224,14 +1246,14 @@ sr_state_unset_fresh_srv(void) sr_state->is_srv_fresh = 0; } -/* Return the value of the fresh SRV flag. */ +/** Return the value of the fresh SRV flag. */ unsigned int sr_state_srv_is_fresh(void) { return sr_state->is_srv_fresh; } -/* Cleanup and free our disk and memory state. */ +/** Cleanup and free our disk and memory state. */ void sr_state_free_all(void) { @@ -1240,9 +1262,10 @@ sr_state_free_all(void) /* Nullify our global state. */ sr_state = NULL; sr_disk_state = NULL; + config_mgr_free(shared_random_state_mgr); } -/* Save our current state in memory to disk. */ +/** Save our current state in memory to disk. */ void sr_state_save(void) { @@ -1250,7 +1273,7 @@ sr_state_save(void) state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL); } -/* Return 1 iff the state has been initialized that is it exists in memory. +/** Return 1 iff the state has been initialized that is it exists in memory. * Return 0 otherwise. */ int sr_state_is_initialized(void) @@ -1258,7 +1281,7 @@ sr_state_is_initialized(void) return sr_state == NULL ? 0 : 1; } -/* Initialize the disk and memory state. +/** Initialize the disk and memory state. * * If save_to_disk is set to 1, the state is immediately saved to disk after * creation else it's not thus only kept in memory. @@ -1310,7 +1333,7 @@ sr_state_init(int save_to_disk, int read_from_disk) /* We have a state in memory, let's make sure it's updated for the current * and next voting round. */ { - time_t valid_after = voting_schedule_get_next_valid_after_time(); + time_t valid_after = dirauth_sched_get_next_valid_after_time(); sr_state_update(valid_after); } return 0; @@ -1321,7 +1344,7 @@ sr_state_init(int save_to_disk, int read_from_disk) #ifdef TOR_UNIT_TESTS -/* Set the current phase of the protocol. Used only by unit tests. */ +/** Set the current phase of the protocol. Used only by unit tests. */ void set_sr_phase(sr_phase_t phase) { @@ -1330,7 +1353,7 @@ set_sr_phase(sr_phase_t phase) sr_state->phase = phase; } -/* Get the SR state. Used only by unit tests */ +/** Get the SR state. Used only by unit tests */ sr_state_t * get_sr_state(void) { diff --git a/src/feature/dirauth/shared_random_state.h b/src/feature/dirauth/shared_random_state.h index 08f999f9d4..3a34bcc3e7 100644 --- a/src/feature/dirauth/shared_random_state.h +++ b/src/feature/dirauth/shared_random_state.h @@ -1,12 +1,17 @@ -/* Copyright (c) 2016-2019, The Tor Project, Inc. */ +/* Copyright (c) 2016-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file shared_random_state.h + * @brief Header for shared_random_state.c + **/ + #ifndef TOR_SHARED_RANDOM_STATE_H #define TOR_SHARED_RANDOM_STATE_H #include "feature/dirauth/shared_random.h" -/* Action that can be performed on the state for any objects. */ +/** Action that can be performed on the state for any objects. */ typedef enum { SR_STATE_ACTION_GET = 1, SR_STATE_ACTION_PUT = 2, @@ -15,52 +20,53 @@ typedef enum { SR_STATE_ACTION_SAVE = 5, } sr_state_action_t; -/* Object in the state that can be queried through the state API. */ +/** Object in the state that can be queried through the state API. */ typedef enum { - /* Will return a single commit using an authority identity key. */ + /** Will return a single commit using an authority identity key. */ SR_STATE_OBJ_COMMIT, - /* Returns the entire list of commits from the state. */ + /** Returns the entire list of commits from the state. */ SR_STATE_OBJ_COMMITS, - /* Return the current SRV object pointer. */ + /** Return the current SRV object pointer. */ SR_STATE_OBJ_CURSRV, - /* Return the previous SRV object pointer. */ + /** Return the previous SRV object pointer. */ SR_STATE_OBJ_PREVSRV, - /* Return the phase. */ + /** Return the phase. */ SR_STATE_OBJ_PHASE, - /* Get or Put the valid after time. */ + /** Get or Put the valid after time. */ SR_STATE_OBJ_VALID_AFTER, } sr_state_object_t; -/* State of the protocol. It's also saved on disk in fname. This data +/** State of the protocol. It's also saved on disk in fname. This data * structure MUST be synchronized at all time with the one on disk. */ typedef struct sr_state_t { - /* Filename of the state file on disk. */ + /** Filename of the state file on disk. */ char *fname; - /* Version of the protocol. */ + /** Version of the protocol. */ uint32_t version; - /* The valid-after of the voting period we have prepared the state for. */ + /** The valid-after of the voting period we have prepared the state for. */ time_t valid_after; - /* Until when is this state valid? */ + /** Until when is this state valid? */ time_t valid_until; - /* Protocol phase. */ + /** Protocol phase. */ sr_phase_t phase; - /* Number of runs completed. */ + /** Number of runs completed. */ uint64_t n_protocol_runs; - /* The number of commitment rounds we've performed in this protocol run. */ + /** The number of commitment rounds we've performed in this protocol run. */ unsigned int n_commit_rounds; - /* The number of reveal rounds we've performed in this protocol run. */ + /** The number of reveal rounds we've performed in this protocol run. */ unsigned int n_reveal_rounds; - /* A map of all the received commitments for this protocol run. This is + /** A map of all the received commitments for this protocol run. This is * indexed by authority RSA identity digest. */ digestmap_t *commits; - /* Current and previous shared random value. */ + /** Current shared random value. */ sr_srv_t *previous_srv; + /** Previous shared random value. */ sr_srv_t *current_srv; - /* Indicate if the state contains an SRV that was _just_ generated. This is + /** Indicate if the state contains an SRV that was _just_ generated. This is * used during voting so that we know whether to use the super majority rule * or not when deciding on keeping it for the consensus. It is _always_ set * to 0 post consensus. @@ -73,22 +79,22 @@ typedef struct sr_state_t { unsigned int is_srv_fresh:1; } sr_state_t; -/* Persistent state of the protocol, as saved to disk. */ +/** Persistent state of the protocol, as saved to disk. */ typedef struct sr_disk_state_t { uint32_t magic_; - /* Version of the protocol. */ + /** Version of the protocol. */ int Version; - /* Version of our running tor. */ + /** Version of our running tor. */ char *TorVersion; - /* Creation time of this state */ + /** Creation time of this state */ time_t ValidAfter; - /* State valid until? */ + /** State valid until? */ time_t ValidUntil; - /* All commits seen that are valid. */ + /** All commits seen that are valid. */ struct config_line_t *Commit; - /* Previous and current shared random value. */ + /** Previous and current shared random value. */ struct config_line_t *SharedRandValues; - /* Extra Lines for configuration we might not know. */ + /** Extra Lines for configuration we might not know. */ struct config_line_t *ExtraLines; } sr_disk_state_t; diff --git a/src/feature/dirauth/vote_microdesc_hash_st.h b/src/feature/dirauth/vote_microdesc_hash_st.h index 92acdf1157..7f8ebf7fd7 100644 --- a/src/feature/dirauth/vote_microdesc_hash_st.h +++ b/src/feature/dirauth/vote_microdesc_hash_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ +/** + * @file vote_microdesc_hash_st.h + * @brief Microdescriptor-hash voting strcture. + **/ + #ifndef VOTE_MICRODESC_HASH_ST_H #define VOTE_MICRODESC_HASH_ST_H @@ -18,5 +23,4 @@ struct vote_microdesc_hash_t { char *microdesc_hash_line; }; -#endif - +#endif /* !defined(VOTE_MICRODESC_HASH_ST_H) */ diff --git a/src/feature/dirauth/voteflags.c b/src/feature/dirauth/voteflags.c index 54c70b989a..477eb6f0b7 100644 --- a/src/feature/dirauth/voteflags.c +++ b/src/feature/dirauth/voteflags.c @@ -1,6 +1,6 @@ /* 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 */ /** @@ -18,6 +18,7 @@ #include "core/or/policies.h" #include "feature/dirauth/bwauth.h" #include "feature/dirauth/reachability.h" +#include "feature/dirauth/dirauth_sys.h" #include "feature/hibernate/hibernate.h" #include "feature/nodelist/dirlist.h" #include "feature/nodelist/networkstatus.h" @@ -27,8 +28,10 @@ #include "feature/relay/router.h" #include "feature/stats/rephist.h" +#include "feature/dirauth/dirauth_options_st.h" #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" +#include "feature/nodelist/routerlist_st.h" #include "feature/nodelist/vote_routerstatus_st.h" #include "lib/container/order.h" @@ -95,7 +98,7 @@ real_uptime(const routerinfo_t *router, time_t now) */ static int dirserv_thinks_router_is_unreliable(time_t now, - routerinfo_t *router, + const routerinfo_t *router, int need_uptime, int need_capacity) { if (need_uptime) { @@ -144,7 +147,7 @@ router_is_active(const routerinfo_t *ri, const node_t *node, time_t now) * if TestingTorNetwork, and TestingMinExitFlagThreshold is non-zero */ if (!ri->bandwidthcapacity) { if (get_options()->TestingTorNetwork) { - if (get_options()->TestingMinExitFlagThreshold > 0) { + if (dirauth_get_options()->TestingMinExitFlagThreshold > 0) { /* If we're in a TestingTorNetwork, and TestingMinExitFlagThreshold is, * then require bandwidthcapacity */ return 0; @@ -174,14 +177,14 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router, long uptime; /* If we haven't been running for at least - * get_options()->MinUptimeHidServDirectoryV2 seconds, we can't + * MinUptimeHidServDirectoryV2 seconds, we can't * have accurate data telling us a relay has been up for at least * that long. We also want to allow a bit of slack: Reachability * tests aren't instant. If we haven't been running long enough, * trust the relay. */ if (get_uptime() > - get_options()->MinUptimeHidServDirectoryV2 * 1.1) + dirauth_get_options()->MinUptimeHidServDirectoryV2 * 1.1) uptime = MIN(rep_hist_get_uptime(router->cache_info.identity_digest, now), real_uptime(router, now)); else @@ -190,7 +193,7 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router, return (router->wants_to_be_hs_dir && router->supports_tunnelled_dir_requests && node->is_stable && node->is_fast && - uptime >= get_options()->MinUptimeHidServDirectoryV2 && + uptime >= dirauth_get_options()->MinUptimeHidServDirectoryV2 && router_is_active(router, node, now)); } @@ -213,9 +216,10 @@ router_counts_toward_thresholds(const node_t *node, time_t now, dirserv_has_measured_bw(node->identity); uint64_t min_bw_kb = ABSOLUTE_MIN_BW_VALUE_TO_CONSIDER_KB; const or_options_t *options = get_options(); + const dirauth_options_t *dirauth_options = dirauth_get_options(); if (options->TestingTorNetwork) { - min_bw_kb = (int64_t)options->TestingMinExitFlagThreshold / 1000; + min_bw_kb = (int64_t)dirauth_options->TestingMinExitFlagThreshold / 1000; } return node->ri && router_is_active(node->ri, node, now) && @@ -238,14 +242,15 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) uint32_t *uptimes, *bandwidths_kb, *bandwidths_excluding_exits_kb; long *tks; double *mtbfs, *wfus; - smartlist_t *nodelist; + const smartlist_t *nodelist; time_t now = time(NULL); const or_options_t *options = get_options(); + const dirauth_options_t *dirauth_options = dirauth_get_options(); /* Require mbw? */ int require_mbw = (dirserv_get_last_n_measured_bws() > - options->MinMeasuredBWsForAuthToIgnoreAdvertised) ? 1 : 0; + dirauth_options->MinMeasuredBWsForAuthToIgnoreAdvertised) ? 1 : 0; /* initialize these all here, in case there are no routers */ stable_uptime = 0; @@ -337,7 +342,7 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) ABSOLUTE_MIN_VALUE_FOR_FAST_FLAG, INT32_MAX); if (options->TestingTorNetwork) { - min_fast = (int32_t)options->TestingMinFastFlagThreshold; + min_fast = (int32_t)dirauth_options->TestingMinFastFlagThreshold; } max_fast = networkstatus_get_param(NULL, "FastFlagMaxThreshold", INT32_MAX, min_fast, INT32_MAX); @@ -351,9 +356,11 @@ dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) } /* Protect sufficiently fast nodes from being pushed out of the set * of Fast nodes. */ - if (options->AuthDirFastGuarantee && - fast_bandwidth_kb > options->AuthDirFastGuarantee/1000) - fast_bandwidth_kb = (uint32_t)options->AuthDirFastGuarantee/1000; + { + const uint64_t fast_opt = dirauth_get_options()->AuthDirFastGuarantee; + if (fast_opt && fast_bandwidth_kb > fast_opt / 1000) + fast_bandwidth_kb = (uint32_t)(fast_opt / 1000); + } /* Now that we have a time-known that 7/8 routers are known longer than, * fill wfus with the wfu of every such "familiar" router. */ @@ -427,7 +434,7 @@ dirserv_get_flag_thresholds_line(void) { char *result=NULL; const int measured_threshold = - get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised; + dirauth_get_options()->MinMeasuredBWsForAuthToIgnoreAdvertised; const int enough_measured_bw = dirserv_get_last_n_measured_bws() > measured_threshold; @@ -454,8 +461,9 @@ dirserv_get_flag_thresholds_line(void) int running_long_enough_to_decide_unreachable(void) { - return time_of_process_start - + get_options()->TestingAuthDirTimeToLearnReachability < approx_time(); + const dirauth_options_t *opts = dirauth_get_options(); + return time_of_process_start + + opts->TestingAuthDirTimeToLearnReachability < approx_time(); } /** Each server needs to have passed a reachability test no more @@ -480,6 +488,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) */ int answer; const or_options_t *options = get_options(); + const dirauth_options_t *dirauth_options = dirauth_get_options(); node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest); tor_assert(node); @@ -506,7 +515,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) IPv6 OR port since that'd kill all dual stack relays until a majority of the dir auths have IPv6 connectivity. */ answer = (now < node->last_reachable + REACHABLE_TIMEOUT && - (options->AuthDirHasIPv6Connectivity != 1 || + (dirauth_options->AuthDirHasIPv6Connectivity != 1 || tor_addr_is_null(&router->ipv6_addr) || now < node->last_reachable6 + REACHABLE_TIMEOUT)); } @@ -531,42 +540,49 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) node->is_running = answer; } -/** Extract status information from <b>ri</b> and from other authority - * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is - * set. - * - * We assume that ri-\>is_running has already been set, e.g. by - * dirserv_set_router_is_running(ri, now); +/* Check <b>node</b> and <b>ri</b> on whether or not we should publish a + * relay's IPv6 addresses. */ +static int +should_publish_node_ipv6(const node_t *node, const routerinfo_t *ri, + time_t now) +{ + const dirauth_options_t *options = dirauth_get_options(); + + return options->AuthDirHasIPv6Connectivity == 1 && + !tor_addr_is_null(&ri->ipv6_addr) && + ((node->last_reachable6 >= now - REACHABLE_TIMEOUT) || + router_is_me(ri)); +} + +/** + * Extract status information from <b>ri</b> and from other authority + * functions and store it in <b>rs</b>, as per + * <b>set_routerstatus_from_routerinfo</b>. Additionally, sets information + * in from the authority subsystem. */ void -set_routerstatus_from_routerinfo(routerstatus_t *rs, - node_t *node, - routerinfo_t *ri, - time_t now, - int listbadexits) +dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs, + node_t *node, + const routerinfo_t *ri, + time_t now, + int listbadexits) { const or_options_t *options = get_options(); uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri); - memset(rs, 0, sizeof(routerstatus_t)); - - rs->is_authority = - router_digest_is_trusted_dir(ri->cache_info.identity_digest); - - /* Already set by compute_performance_thresholds. */ - rs->is_exit = node->is_exit; - rs->is_stable = node->is_stable = - !dirserv_thinks_router_is_unreliable(now, ri, 1, 0); - rs->is_fast = node->is_fast = - !dirserv_thinks_router_is_unreliable(now, ri, 0, 1); - rs->is_flagged_running = node->is_running; /* computed above */ + /* Set these flags so that set_routerstatus_from_routerinfo can copy them. + */ + node->is_stable = !dirserv_thinks_router_is_unreliable(now, ri, 1, 0); + node->is_fast = !dirserv_thinks_router_is_unreliable(now, ri, 0, 1); + node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now); - rs->is_valid = node->is_valid; + set_routerstatus_from_routerinfo(rs, node, ri); + /* Override rs->is_possible_guard. */ + const uint64_t bw_opt = dirauth_get_options()->AuthDirGuardBWGuarantee; if (node->is_fast && node->is_stable && ri->supports_tunnelled_dir_requests && - ((options->AuthDirGuardBWGuarantee && - routerbw_kb >= options->AuthDirGuardBWGuarantee/1000) || + ((bw_opt && routerbw_kb >= bw_opt / 1000) || routerbw_kb >= MIN(guard_bandwidth_including_exits_kb, guard_bandwidth_excluding_exits_kb))) { long tk = rep_hist_get_weighted_time_known( @@ -578,29 +594,16 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, rs->is_possible_guard = 0; } + /* Override rs->is_bad_exit */ rs->is_bad_exit = listbadexits && node->is_bad_exit; - rs->is_hs_dir = node->is_hs_dir = - dirserv_thinks_router_is_hs_dir(ri, node, now); - - rs->is_named = rs->is_unnamed = 0; - - rs->published_on = ri->cache_info.published_on; - memcpy(rs->identity_digest, node->identity, DIGEST_LEN); - memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest, - DIGEST_LEN); - rs->addr = ri->addr; - strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname)); - rs->or_port = ri->or_port; - rs->dir_port = ri->dir_port; - rs->is_v2_dir = ri->supports_tunnelled_dir_requests; - if (options->AuthDirHasIPv6Connectivity == 1 && - !tor_addr_is_null(&ri->ipv6_addr) && - node->last_reachable6 >= now - REACHABLE_TIMEOUT) { - /* We're configured as having IPv6 connectivity. There's an IPv6 - OR port and it's reachable so copy it to the routerstatus. */ - tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr); - rs->ipv6_orport = ri->ipv6_orport; - } else { + + /* Set rs->is_staledesc. */ + rs->is_staledesc = + (ri->cache_info.published_on + DESC_IS_STALE_INTERVAL) < now; + + if (! should_publish_node_ipv6(node, ri, now)) { + /* We're not configured as having IPv6 connectivity or the node isn't: + * zero its IPv6 information. */ tor_addr_make_null(&rs->ipv6_addr, AF_INET6); rs->ipv6_orport = 0; } @@ -617,9 +620,9 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs) { - const or_options_t *options = get_options(); + const dirauth_options_t *options = dirauth_get_options(); - tor_assert(options->TestingTorNetwork); + tor_assert(get_options()->TestingTorNetwork); if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit, rs, 0)) { @@ -642,3 +645,20 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs) rs->is_hs_dir = 0; } } + +/** Use dirserv_set_router_is_running() to set bridges as running if they're + * reachable. + * + * This function is called from set_bridge_running_callback() when running as + * a bridge authority. + */ +void +dirserv_set_bridges_running(time_t now) +{ + routerlist_t *rl = router_get_routerlist(); + + SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) { + if (ri->purpose == ROUTER_PURPOSE_BRIDGE) + dirserv_set_router_is_running(ri, now); + } SMARTLIST_FOREACH_END(ri); +} diff --git a/src/feature/dirauth/voteflags.h b/src/feature/dirauth/voteflags.h index aa7b6ed082..91f3854573 100644 --- a/src/feature/dirauth/voteflags.h +++ b/src/feature/dirauth/voteflags.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * 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 */ /** @@ -12,20 +12,28 @@ #ifndef TOR_VOTEFLAGS_H #define TOR_VOTEFLAGS_H +#ifdef HAVE_MODULE_DIRAUTH void dirserv_set_router_is_running(routerinfo_t *router, time_t now); char *dirserv_get_flag_thresholds_line(void); void dirserv_compute_bridge_flag_thresholds(void); int running_long_enough_to_decide_unreachable(void); -void set_routerstatus_from_routerinfo(routerstatus_t *rs, - node_t *node, - routerinfo_t *ri, time_t now, - int listbadexits); +void dirauth_set_routerstatus_from_routerinfo(routerstatus_t *rs, + node_t *node, + const routerinfo_t *ri, + time_t now, + int listbadexits); void dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil); +#endif /* defined(HAVE_MODULE_DIRAUTH) */ + +void dirserv_set_bridges_running(time_t now); #ifdef VOTEFLAGS_PRIVATE +/** Any descriptor older than this age causes the authorities to set the + * StaleDesc flag. */ +#define DESC_IS_STALE_INTERVAL (18*60*60) STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs); -#endif +#endif /* defined(VOTEFLAGS_PRIVATE) */ -#endif +#endif /* !defined(TOR_VOTEFLAGS_H) */ diff --git a/src/feature/dirauth/voting_schedule.c b/src/feature/dirauth/voting_schedule.c new file mode 100644 index 0000000000..efc4a0b316 --- /dev/null +++ b/src/feature/dirauth/voting_schedule.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2018-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file voting_schedule.c + * \brief Compute information about our voting schedule as a directory + * authority. + **/ + +#include "feature/dirauth/voting_schedule.h" + +#include "core/or/or.h" +#include "app/config/config.h" +#include "feature/nodelist/networkstatus.h" + +#include "feature/nodelist/networkstatus_st.h" + +/* ===== + * Vote scheduling + * ===== */ + +/* Populate and return a new voting_schedule_t that can be used to schedule + * voting. The object is allocated on the heap and it's the responsibility of + * the caller to free it. Can't fail. */ +static voting_schedule_t * +create_voting_schedule(const or_options_t *options, time_t now, int severity) +{ + int interval, vote_delay, dist_delay; + time_t start; + time_t end; + networkstatus_t *consensus; + voting_schedule_t *new_voting_schedule; + + new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t)); + + consensus = networkstatus_get_live_consensus(now); + + if (consensus) { + interval = (int)( consensus->fresh_until - consensus->valid_after ); + vote_delay = consensus->vote_seconds; + dist_delay = consensus->dist_seconds; + + /* Note down the consensus valid after, so that we detect outdated voting + * schedules in case of skewed clocks etc. */ + new_voting_schedule->live_consensus_valid_after = consensus->valid_after; + } else { + interval = options->TestingV3AuthInitialVotingInterval; + vote_delay = options->TestingV3AuthInitialVoteDelay; + dist_delay = options->TestingV3AuthInitialDistDelay; + } + + tor_assert(interval > 0); + new_voting_schedule->interval = interval; + + if (vote_delay + dist_delay > interval/2) + vote_delay = dist_delay = interval / 4; + + start = new_voting_schedule->interval_starts = + voting_sched_get_start_of_interval_after(now,interval, + options->TestingV3AuthVotingStartOffset); + end = voting_sched_get_start_of_interval_after(start+1, interval, + options->TestingV3AuthVotingStartOffset); + + tor_assert(end > start); + + new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2); + new_voting_schedule->voting_ends = start - dist_delay; + new_voting_schedule->fetch_missing_votes = + start - dist_delay - (vote_delay/2); + new_voting_schedule->voting_starts = start - dist_delay - vote_delay; + + { + char tbuf[ISO_TIME_LEN+1]; + format_iso_time(tbuf, new_voting_schedule->interval_starts); + tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: " + "consensus_set=%d, interval=%d", + tbuf, consensus?1:0, interval); + } + + return new_voting_schedule; +} + +#define voting_schedule_free(s) \ + FREE_AND_NULL(voting_schedule_t, voting_schedule_free_, (s)) + +/** Frees a voting_schedule_t. This should be used instead of the generic + * tor_free. */ +static void +voting_schedule_free_(voting_schedule_t *voting_schedule_to_free) +{ + if (!voting_schedule_to_free) + return; + tor_free(voting_schedule_to_free); +} + +voting_schedule_t voting_schedule; + +/** + * Return the current voting schedule, recreating it if necessary. + * + * Dirauth only. + **/ +static const voting_schedule_t * +dirauth_get_voting_schedule(void) +{ + time_t now = approx_time(); + bool need_to_recalculate_voting_schedule = false; + + /* This is a safe guard in order to make sure that the voting schedule + * static object is at least initialized. Using this function with a zeroed + * voting schedule can lead to bugs. */ + if (fast_mem_is_zero((const char *) &voting_schedule, + sizeof(voting_schedule))) { + need_to_recalculate_voting_schedule = true; + goto done; /* no need for next check if we have to recalculate anyway */ + } + + /* Also make sure we are not using an outdated voting schedule. If we have a + * newer consensus, make sure we recalculate the voting schedule. */ + const networkstatus_t *ns = networkstatus_get_live_consensus(now); + if (ns && ns->valid_after != voting_schedule.live_consensus_valid_after) { + log_info(LD_DIR, "Voting schedule is outdated: recalculating (%d/%d)", + (int) ns->valid_after, + (int) voting_schedule.live_consensus_valid_after); + need_to_recalculate_voting_schedule = true; + } + + done: + if (need_to_recalculate_voting_schedule) { + dirauth_sched_recalculate_timing(get_options(), approx_time()); + voting_schedule.created_on_demand = 1; + } + + return &voting_schedule; +} + +/** Return the next voting valid-after time. + * + * Dirauth only. */ +time_t +dirauth_sched_get_next_valid_after_time(void) +{ + return dirauth_get_voting_schedule()->interval_starts; +} + +/** + * Return our best idea of what the valid-after time for the _current_ + * consensus, whether we have one or not. + * + * Dirauth only. + **/ +time_t +dirauth_sched_get_cur_valid_after_time(void) +{ + const voting_schedule_t *sched = dirauth_get_voting_schedule(); + time_t next_start = sched->interval_starts; + int interval = sched->interval; + int offset = get_options()->TestingV3AuthVotingStartOffset; + return voting_sched_get_start_of_interval_after(next_start - interval - 1, + interval, + offset); +} + +/** Return the voting interval that we are configured to use. + * + * Dirauth only. */ +int +dirauth_sched_get_configured_interval(void) +{ + return get_options()->V3AuthVotingInterval; +} + +/** Set voting_schedule to hold the timing for the next vote we should be + * doing. All type of tor do that because HS subsystem needs the timing as + * well to function properly. */ +void +dirauth_sched_recalculate_timing(const or_options_t *options, time_t now) +{ + voting_schedule_t *new_voting_schedule; + + /* get the new voting schedule */ + new_voting_schedule = create_voting_schedule(options, now, LOG_INFO); + tor_assert(new_voting_schedule); + + /* Fill in the global static struct now */ + memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule)); + voting_schedule_free(new_voting_schedule); +} diff --git a/src/feature/dirauth/voting_schedule.h b/src/feature/dirauth/voting_schedule.h new file mode 100644 index 0000000000..9e2ac29c75 --- /dev/null +++ b/src/feature/dirauth/voting_schedule.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2018-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file voting_schedule.h + * \brief Header file for voting_schedule.c. + **/ + +#ifndef TOR_VOTING_SCHEDULE_H +#define TOR_VOTING_SCHEDULE_H + +#include "core/or/or.h" + +#ifdef HAVE_MODULE_DIRAUTH + +/** Scheduling information for a voting interval. */ +typedef struct { + /** When do we generate and distribute our vote for this interval? */ + time_t voting_starts; + /** When do we send an HTTP request for any votes that we haven't + * been posted yet?*/ + time_t fetch_missing_votes; + /** When do we give up on getting more votes and generate a consensus? */ + time_t voting_ends; + /** When do we send an HTTP request for any signatures we're expecting to + * see on the consensus? */ + time_t fetch_missing_signatures; + /** When do we publish the consensus? */ + time_t interval_starts; + + /** Our computed dirauth interval */ + int interval; + + /** True iff we have generated and distributed our vote. */ + int have_voted; + /** True iff we've requested missing votes. */ + int have_fetched_missing_votes; + /** True iff we have built a consensus and sent the signatures around. */ + int have_built_consensus; + /** True iff we've fetched missing signatures. */ + int have_fetched_missing_signatures; + /** True iff we have published our consensus. */ + int have_published_consensus; + + /* True iff this voting schedule was set on demand meaning not through the + * normal vote operation of a dirauth or when a consensus is set. This only + * applies to a directory authority that needs to recalculate the voting + * timings only for the first vote even though this object was initilized + * prior to voting. */ + int created_on_demand; + + /** The valid-after time of the last live consensus that filled this voting + * schedule. It's used to detect outdated voting schedules. */ + time_t live_consensus_valid_after; +} voting_schedule_t; + +/* Public API. */ + +extern voting_schedule_t voting_schedule; + +void dirauth_sched_recalculate_timing(const or_options_t *options, + time_t now); + +time_t dirauth_sched_get_next_valid_after_time(void); +time_t dirauth_sched_get_cur_valid_after_time(void); +int dirauth_sched_get_configured_interval(void); + +#else /* !defined(HAVE_MODULE_DIRAUTH) */ + +#define dirauth_sched_recalculate_timing(opt,now) \ + ((void)(opt), (void)(now)) + +static inline time_t +dirauth_sched_get_next_valid_after_time(void) +{ + tor_assert_unreached(); + return 0; +} +static inline time_t +dirauth_sched_get_cur_valid_after_time(void) +{ + tor_assert_unreached(); + return 0; +} +static inline int +dirauth_sched_get_configured_interval(void) +{ + tor_assert_unreached(); + return 1; +} +#endif /* defined(HAVE_MODULE_DIRAUTH) */ + +#endif /* !defined(TOR_VOTING_SCHEDULE_H) */ |