diff options
Diffstat (limited to 'src/feature/dirauth/process_descs.c')
-rw-r--r-- | src/feature/dirauth/process_descs.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c new file mode 100644 index 0000000000..2c2fefca77 --- /dev/null +++ b/src/feature/dirauth/process_descs.c @@ -0,0 +1,835 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file process_descs.c + * \brief Make decisions about uploaded descriptors + * + * Authorities use the code in this module to decide what to do with just- + * uploaded descriptors, and to manage the fingerprint file that helps + * them make those decisions. + **/ + +#include "core/or/or.h" +#include "feature/dirauth/process_descs.h" + +#include "app/config/config.h" +#include "core/or/policies.h" +#include "feature/dirauth/keypin.h" +#include "feature/dirauth/reachability.h" +#include "feature/dircache/directory.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerlist.h" +#include "feature/nodelist/routerparse.h" +#include "feature/nodelist/torcert.h" +#include "feature/relay/router.h" + +#include "core/or/tor_version_st.h" +#include "feature/nodelist/extrainfo_st.h" +#include "feature/nodelist/node_st.h" +#include "feature/nodelist/routerinfo_st.h" +#include "feature/nodelist/routerstatus_st.h" + +#include "lib/encoding/confline.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; + +/** Should be static; exposed for testing. */ +static authdir_config_t *fingerprint_list = NULL; + +/** Allocate and return a new, empty, authdir_config_t. */ +static authdir_config_t * +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(); + return list; +} + +/** 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) +{ + char *fingerprint; + char d[DIGEST_LEN]; + router_status_t *status; + tor_assert(fp); + tor_assert(list); + + fingerprint = tor_strdup(fp); + tor_strstrip(fingerprint, " "); + if (base16_decode(d, DIGEST_LEN, + fingerprint, strlen(fingerprint)) != DIGEST_LEN) { + log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"", + escaped(fp)); + tor_free(fingerprint); + return; + } + + status = digestmap_get(list->status_by_digest, d); + if (!status) { + status = tor_malloc_zero(sizeof(router_status_t)); + digestmap_set(list->status_by_digest, d, status); + } + + tor_free(fingerprint); + *status |= add_status; + return; +} + +/** Add the fingerprint for this OR to the global list of recognized + * identity key fingerprints. */ +int +dirserv_add_own_fingerprint(crypto_pk_t *pk) +{ + char fp[FINGERPRINT_LEN+1]; + if (crypto_pk_get_fingerprint(pk, fp, 0)<0) { + log_err(LD_BUG, "Error computing fingerprint"); + return -1; + } + if (!fingerprint_list) + fingerprint_list = authdir_config_new(); + add_fingerprint_to_dir(fp, fingerprint_list, 0); + return 0; +} + +/** Load the nickname-\>fingerprint mappings stored in the approved-routers + * file. The file format is line-based, with each non-blank holding one + * nickname, some space, and a fingerprint for that nickname. On success, + * replace the current fingerprint list with the new list and return 0. On + * failure, leave the current fingerprint list untouched, and return -1. */ +int +dirserv_load_fingerprint_file(void) +{ + char *fname; + char *cf; + char *nickname, *fingerprint; + authdir_config_t *fingerprint_list_new; + int result; + config_line_t *front=NULL, *list; + + fname = get_datadir_fname("approved-routers"); + log_info(LD_GENERAL, + "Reloading approved fingerprints from \"%s\"...", fname); + + cf = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); + if (!cf) { + log_warn(LD_FS, "Cannot open fingerprint file '%s'. That's ok.", fname); + tor_free(fname); + return 0; + } + tor_free(fname); + + result = config_get_lines(cf, &front, 0); + tor_free(cf); + if (result < 0) { + log_warn(LD_CONFIG, "Error reading from fingerprint file"); + return -1; + } + + fingerprint_list_new = authdir_config_new(); + + for (list=front; list; list=list->next) { + char digest_tmp[DIGEST_LEN]; + router_status_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; + } + if (!strcasecmp(nickname, "!reject")) { + add_status = FP_REJECT; + } else if (!strcasecmp(nickname, "!badexit")) { + add_status = FP_BADEXIT; + } else if (!strcasecmp(nickname, "!invalid")) { + add_status = FP_INVALID; + } + add_fingerprint_to_dir(fingerprint, fingerprint_list_new, add_status); + } + + config_free_lines(front); + dirserv_free_fingerprint_list(); + fingerprint_list = fingerprint_list_new; + /* Delete any routers whose fingerprints we no longer recognize */ + directory_remove_invalid(); + return 0; +} + +/* If this is set, then we don't allow routers that have advertised an Ed25519 + * identity to stop doing so. This is going to be essential for good identity + * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could + * just sign fake descriptors missing the Ed25519 key. But we won't actually + * be able to prevent that kind of thing until we're confident that there isn't + * actually a legit reason to downgrade to 0.2.5. Now we are not recommending + * 0.2.5 anymore so there is no reason to keep the #undef. + */ + +#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. + * + * If the status is 'FP_REJECT' and <b>msg</b> is provided, set + * *<b>msg</b> to an explanation of 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; + + 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; + } + + /* 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); + if (r) + return r; + + /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc, + * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha. + * But just in case a relay doesn't provide or lies about its version, or + * doesn't include an ntor key in its descriptor, check that it exists, + * and is non-zero (clients check that it's non-zero before using it). */ + if (!routerinfo_has_curve25519_onion_key(router)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s is missing an ntor curve25519 onion " + "key.", router_describe(router)); + if (msg) + *msg = "Missing ntor curve25519 onion key. Please upgrade!"; + return FP_REJECT; + } + + if (router->cache_info.signing_key_cert) { + /* This has an ed25519 identity key. */ + if (KEYPIN_MISMATCH == + keypin_check((const uint8_t*)router->cache_info.identity_digest, + router->cache_info.signing_key_cert->signing_key.pubkey)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s has an Ed25519 key, " + "but the <rsa,ed25519> keys don't match what they were before.", + router_describe(router)); + if (key_pinning) { + if (msg) { + *msg = "Ed25519 identity key or RSA identity key has changed."; + } + return FP_REJECT; + } + } + } else { + /* No ed25519 key */ + if (KEYPIN_MISMATCH == keypin_check_lone_rsa( + (const uint8_t*)router->cache_info.identity_digest)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s has no Ed25519 key, " + "when we previously knew an Ed25519 for it. Ignoring for now, " + "since Ed25519 keys are fairly new.", + router_describe(router)); +#ifdef DISABLE_DISABLING_ED25519 + if (key_pinning) { + if (msg) { + *msg = "Ed25519 identity key has disappeared."; + } + return FP_REJECT; + } +#endif /* defined(DISABLE_DISABLING_ED25519) */ + } + } + + return 0; +} + +/** 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) +{ + uint32_t res; + + res = dirserv_get_status_impl(rs->identity_digest, rs->nickname, + rs->addr, rs->or_port, + NULL, NULL, LOG_DEBUG); + + return (res & FP_REJECT) != 0; +} + +/** 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. + * + * 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, + const char *platform, const char **msg, int severity) +{ + uint32_t result = 0; + router_status_t *status_by_digest; + + if (!fingerprint_list) + fingerprint_list = authdir_config_new(); + + log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.", + strmap_size(fingerprint_list->fp_by_name), + digestmap_size(fingerprint_list->status_by_digest)); + + if (platform) { + tor_version_t ver_tmp; + if (tor_version_parse_platform(platform, &ver_tmp, 1) < 0) { + if (msg) { + *msg = "Malformed platform string."; + } + return FP_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; + } + + status_by_digest = digestmap_get(fingerprint_list->status_by_digest, + id_digest); + if (status_by_digest) + result |= *status_by_digest; + + if (result & FP_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) { + if (msg) + *msg = "Fingerprint 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; + } + + if (!authdir_policy_permits_address(addr, or_port)) { + log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'", + nickname, fmt_addr32(addr)); + if (msg) + *msg = "Suspicious relay address range -- 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 address(es) and fingerprint(s)?"; + return FP_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; + } + + return result; +} + +/** Clear the current fingerprint list. */ +void +dirserv_free_fingerprint_list(void) +{ + if (!fingerprint_list) + return; + + strmap_free(fingerprint_list->fp_by_name, tor_free_); + digestmap_free(fingerprint_list->status_by_digest, tor_free_); + tor_free(fingerprint_list); +} + +/* + * Descriptor list + */ + +/** 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 +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_internal(&addr, 0)) { + log_info(LD_DIRSERV, + "Router %s published internal IP 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 <b>complain</b> then explain at log-level 'notice' why we refused + * a descriptor; else explain at log-level 'info'. + */ +int +authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, + int complain, int *valid_out) +{ + /* Okay. Now check whether the fingerprint is recognized. */ + time_t now; + 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) + return -1; /* msg is already set. */ + + /* Is there too much clock skew? */ + now = time(NULL); + if (ri->cache_info.published_on > now+ROUTER_ALLOW_SKEW) { + log_fn(severity, LD_DIRSERV, "Publication time for %s is too " + "far (%d minutes) in the future; possible clock skew. Not adding " + "(%s)", + router_describe(ri), + (int)((ri->cache_info.published_on-now)/60), + esc_router_info(ri)); + *msg = "Rejected: Your clock is set too far in the future, or your " + "timezone is not correct."; + return -1; + } + if (ri->cache_info.published_on < now-ROUTER_MAX_AGE_TO_PUBLISH) { + log_fn(severity, LD_DIRSERV, + "Publication time for %s is too far " + "(%d minutes) in the past. Not adding (%s)", + router_describe(ri), + (int)((now-ri->cache_info.published_on)/60), + esc_router_info(ri)); + *msg = "Rejected: Server is expired, or your clock is too far in the past," + " or your timezone is not correct."; + return -1; + } + if (dirserv_router_has_valid_address(ri) < 0) { + log_fn(severity, LD_DIRSERV, + "Router %s has invalid address. Not adding (%s).", + router_describe(ri), + esc_router_info(ri)); + *msg = "Rejected: Address is a private address."; + return -1; + } + + *valid_out = ! (status & FP_INVALID); + + return 0; +} + +/** Update the relevant flags of <b>node</b> based on our opinion as a + * directory authority in <b>authstatus</b>, as returned by + * dirserv_router_get_status or equivalent. */ +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; +} + +/** True iff <b>a</b> is more severe than <b>b</b>. */ +static int +WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b) +{ + return a < 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, + const char *source, + const char **msg) +{ + was_router_added_t r, r_tmp; + const char *msg_out; + smartlist_t *list; + const char *s; + int n_parsed = 0; + time_t now = time(NULL); + char annotation_buf[ROUTER_ANNOTATION_BUF_LEN]; + char time_buf[ISO_TIME_LEN+1]; + int general = purpose == ROUTER_PURPOSE_GENERAL; + tor_assert(msg); + + r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */ + + format_iso_time(time_buf, now); + if (tor_snprintf(annotation_buf, sizeof(annotation_buf), + "@uploaded-at %s\n" + "@source %s\n" + "%s%s%s", time_buf, escaped(source), + !general ? "@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; + } + + s = desc; + list = smartlist_new(); + if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0, + annotation_buf, NULL)) { + SMARTLIST_FOREACH(list, routerinfo_t *, ri, { + msg_out = NULL; + tor_assert(ri->purpose == purpose); + r_tmp = dirserv_add_descriptor(ri, &msg_out, source); + if (WRA_MORE_SEVERE(r_tmp, r)) { + r = r_tmp; + *msg = msg_out; + } + }); + } + n_parsed += smartlist_len(list); + smartlist_clear(list); + + s = desc; + if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0, + NULL, NULL)) { + SMARTLIST_FOREACH(list, extrainfo_t *, ei, { + msg_out = NULL; + + r_tmp = dirserv_add_extrainfo(ei, &msg_out); + if (WRA_MORE_SEVERE(r_tmp, r)) { + r = r_tmp; + *msg = msg_out; + } + }); + } + n_parsed += smartlist_len(list); + smartlist_free(list); + + if (! *msg) { + if (!n_parsed) { + *msg = "No descriptors found in your POST."; + if (WRA_WAS_ADDED(r)) + r = ROUTER_IS_ALREADY_KNOWN; + } else { + *msg = "(no message)"; + } + } + + return r; +} + +/** Examine the parsed server descriptor in <b>ri</b> and maybe insert it into + * the list of server descriptors. Set *<b>msg</b> to a message that should be + * passed back to the origin of this descriptor, or NULL if there is no such + * message. Use <b>source</b> to produce better log messages. + * + * If <b>ri</b> is not added to the list of server descriptors, free it. + * 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. + * + * This function is only called when fresh descriptors are posted, not when + * we re-load the cache. + */ +was_router_added_t +dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) +{ + was_router_added_t r; + routerinfo_t *ri_old; + 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; + *msg = NULL; + + /* If it's too big, refuse it now. Otherwise we'll cache it all over the + * network and it'll clog everything up. */ + if (ri->cache_info.signed_descriptor_len > MAX_DESCRIPTOR_UPLOAD_SIZE) { + log_notice(LD_DIR, "Somebody attempted to publish a router descriptor '%s'" + " (source: %s) with size %d. Either this is an attack, or the " + "MAX_DESCRIPTOR_UPLOAD_SIZE (%d) constant is too low.", + ri->nickname, source, (int)ri->cache_info.signed_descriptor_len, + MAX_DESCRIPTOR_UPLOAD_SIZE); + *msg = "Router descriptor was too large."; + r = ROUTER_AUTHDIR_REJECTS; + goto fail; + } + + /* Check whether this descriptor is semantically identical to the last one + * from this server. (We do this here and not in router_add_to_routerlist + * because we want to be able to accept the newest router descriptor that + * another authority has, so we all converge on the same one.) */ + ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest); + if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on + && router_differences_are_cosmetic(ri_old, ri) + && !router_is_me(ri)) { + log_info(LD_DIRSERV, + "Not replacing descriptor from %s (source: %s); " + "differences are cosmetic.", + router_describe(ri), source); + *msg = "Not replacing router descriptor; no information has changed since " + "the last one with this identity."; + r = ROUTER_IS_ALREADY_KNOWN; + goto fail; + } + + /* Do keypinning again ... this time, to add the pin if appropriate */ + int keypin_status; + if (ri->cache_info.signing_key_cert) { + ed25519_public_key_t *pkey = &ri->cache_info.signing_key_cert->signing_key; + /* First let's validate this pubkey before pinning it */ + if (ed25519_validate_pubkey(pkey) < 0) { + log_warn(LD_DIRSERV, "Received bad key from %s (source %s)", + router_describe(ri), source); + routerinfo_free(ri); + return ROUTER_AUTHDIR_REJECTS; + } + + /* Now pin it! */ + keypin_status = keypin_check_and_add( + (const uint8_t*)ri->cache_info.identity_digest, + pkey->pubkey, ! key_pinning); + } else { + keypin_status = keypin_check_lone_rsa( + (const uint8_t*)ri->cache_info.identity_digest); +#ifndef DISABLE_DISABLING_ED25519 + if (keypin_status == KEYPIN_MISMATCH) + keypin_status = KEYPIN_NOT_FOUND; +#endif + } + if (keypin_status == KEYPIN_MISMATCH && key_pinning) { + log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because " + "its key did not match an older RSA/Ed25519 keypair", + router_describe(ri), source); + *msg = "Looks like your keypair has changed? This authority previously " + "recorded a different RSA identity for this Ed25519 identity (or vice " + "versa.) Did you replace or copy some of your key files, but not " + "the others? You should either restore the expected keypair, or " + "delete your keys and restart Tor to start your relay with a new " + "identity."; + r = ROUTER_AUTHDIR_REJECTS; + goto fail; + } + + /* Make a copy of desc, since router_add_to_routerlist might free + * ri and its associated signed_descriptor_t. */ + desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen); + nickname = tor_strdup(ri->nickname); + + /* Tell if we're about to need to launch a test if we add this. */ + ri->needs_retest_if_added = + dirserv_should_launch_reachability_test(ri, ri_old); + + r = router_add_to_routerlist(ri, msg, 0, 0); + if (!WRA_WAS_ADDED(r)) { + /* unless the routerinfo was fine, just out-of-date */ + log_info(LD_DIRSERV, + "Did not add descriptor from '%s' (source: %s): %s.", + nickname, source, *msg ? *msg : "(no message)"); + } else { + smartlist_t *changed; + + changed = smartlist_new(); + smartlist_add(changed, ri); + routerlist_descriptors_added(changed, 0); + smartlist_free(changed); + if (!*msg) { + *msg = "Descriptor accepted"; + } + log_info(LD_DIRSERV, + "Added descriptor from '%s' (source: %s): %s.", + nickname, source, *msg); + } + tor_free(desc); + tor_free(nickname); + return r; + fail: + { + const char *desc_digest = ri->cache_info.signed_descriptor_digest; + download_status_t *dls = + router_get_dl_status_by_descriptor_digest(desc_digest); + if (dls) { + log_info(LD_GENERAL, "Marking router with descriptor %s as rejected, " + "and therefore undownloadable", + hex_str(desc_digest, DIGEST_LEN)); + download_status_mark_impossible(dls); + } + routerinfo_free(ri); + } + return r; +} + +/** As dirserv_add_descriptor, but for an extrainfo_t <b>ei</b>. */ +static was_router_added_t +dirserv_add_extrainfo(extrainfo_t *ei, const char **msg) +{ + routerinfo_t *ri; + int r; + was_router_added_t rv; + tor_assert(msg); + *msg = NULL; + + /* Needs to be mutable so routerinfo_incompatible_with_extrainfo + * can mess with some of the flags in ri->cache_info. */ + ri = router_get_mutable_by_digest(ei->cache_info.identity_digest); + if (!ri) { + *msg = "No corresponding router descriptor for extra-info descriptor"; + rv = ROUTER_BAD_EI; + goto fail; + } + + /* If it's too big, refuse it now. Otherwise we'll cache it all over the + * network and it'll clog everything up. */ + if (ei->cache_info.signed_descriptor_len > MAX_EXTRAINFO_UPLOAD_SIZE) { + log_notice(LD_DIR, "Somebody attempted to publish an extrainfo " + "with size %d. Either this is an attack, or the " + "MAX_EXTRAINFO_UPLOAD_SIZE (%d) constant is too low.", + (int)ei->cache_info.signed_descriptor_len, + MAX_EXTRAINFO_UPLOAD_SIZE); + *msg = "Extrainfo document was too large"; + rv = ROUTER_BAD_EI; + goto fail; + } + + if ((r = routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei, + &ri->cache_info, msg))) { + if (r<0) { + extrainfo_free(ei); + return ROUTER_IS_ALREADY_KNOWN; + } + rv = ROUTER_BAD_EI; + goto fail; + } + router_add_extrainfo_to_routerlist(ei, msg, 0, 0); + return ROUTER_ADDED_SUCCESSFULLY; + fail: + { + const char *d = ei->cache_info.signed_descriptor_digest; + signed_descriptor_t *sd = router_get_by_extrainfo_digest((char*)d); + if (sd) { + log_info(LD_GENERAL, "Marking extrainfo with descriptor %s as " + "rejected, and therefore undownloadable", + hex_str((char*)d,DIGEST_LEN)); + download_status_mark_impossible(&sd->ei_dl_status); + } + extrainfo_free(ei); + } + return rv; +} + +/** Remove all descriptors whose nicknames or fingerprints no longer + * are allowed by our fingerprint list. (Descriptors that used to be + * good can become bad when we reload the fingerprint list.) + */ +static void +directory_remove_invalid(void) +{ + routerlist_t *rl = router_get_routerlist(); + smartlist_t *nodes = smartlist_new(); + smartlist_add_all(nodes, nodelist_get_list()); + + SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) { + const char *msg = NULL; + const char *description; + routerinfo_t *ent = node->ri; + uint32_t r; + if (!ent) + continue; + r = dirserv_router_get_status(ent, &msg, LOG_INFO); + description = router_describe(ent); + if (r & FP_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)) { + log_info(LD_DIRSERV, "Router '%s' is now %svalid.", description, + (r&FP_INVALID) ? "in" : ""); + node->is_valid = (r&FP_INVALID)?0:1; + } + if (bool_neq((r & FP_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; + } + } SMARTLIST_FOREACH_END(node); + + routerlist_assert_ok(rl); + smartlist_free(nodes); +} |