From cca1e0acffc7fc2814ed923cc86346175561c9a1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Aug 2016 17:31:49 -0400 Subject: Add necessary code to parse and handle required/recommended protocols --- src/or/networkstatus.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'src/or/networkstatus.c') diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 72af505d19..2eb3cd83cf 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -28,6 +28,7 @@ #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" +#include "protover.h" #include "relay.h" #include "router.h" #include "routerlist.h" @@ -275,6 +276,11 @@ networkstatus_vote_free(networkstatus_t *ns) tor_free(ns->client_versions); tor_free(ns->server_versions); + tor_free(ns->recommended_client_protocols); + tor_free(ns->recommended_relay_protocols); + tor_free(ns->required_client_protocols); + tor_free(ns->required_relay_protocols); + if (ns->known_flags) { SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c)); smartlist_free(ns->known_flags); @@ -2364,6 +2370,56 @@ getinfo_helper_networkstatus(control_connection_t *conn, return 0; } +/** Check whether the networkstatus ns lists any protocol + * versions as "required" or "recommended" that we do not support. If + * so, set *warning_out to a newly allocated string describing + * the problem. + * + * Return 1 if we should exit, 0 if we should not. */ +int +networkstatus_check_required_protocols(const networkstatus_t *ns, + int client_mode, + char **warning_out) +{ + const char *func = client_mode ? "client" : "relay"; + const char *required, *recommended; + char *missing = NULL; + + tor_assert(warning_out); + + if (client_mode) { + required = ns->required_client_protocols; + recommended = ns->recommended_client_protocols; + } else { + required = ns->required_relay_protocols; + recommended = ns->recommended_relay_protocols; + } + + if (!protover_all_supported(required, &missing)) { + tor_asprintf(warning_out, "At least one protocol listed as required in " + "the consensus is not supported by this version of Tor. " + "You should upgrade. This version of Tor will not work as a " + "%s on the Tor network. The missing protocols are: %s", + func, missing); + tor_free(missing); + return 1; + } + + if (! protover_all_supported(recommended, &missing)) { + tor_asprintf(warning_out, "At least one protocol listed as recommended in " + "the consensus is not supported by this version of Tor. " + "You should upgrade. This version of Tor will eventually " + "stop working as a %s on the Tor network. The missing " + "protocols are: %s", + func, missing); + tor_free(missing); + } + + tor_assert_nonfatal(missing == NULL); + + return 0; +} + /** Free all storage held locally in this module. */ void networkstatus_free_all(void) -- cgit v1.2.3-54-g00ecf From 7f718c46f355e194fb6ae8c8aabb72d2492c4d3f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 19 Aug 2016 17:45:08 -0400 Subject: Actually check for missing protocols and exit as appropriate. --- src/or/networkstatus.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'src/or/networkstatus.c') diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 2eb3cd83cf..6c92773a7a 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1552,6 +1552,31 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c, } #endif //TOR_UNIT_TESTS +/** Called when we have received a networkstatus c. If there are + * any _required_ protocols we are missing, log an error and exit + * immediately. If there are any _recommended_ protocols we are missing, + * warn. */ +static void +handle_missing_protocol_warning(const networkstatus_t *c, + const or_options_t *options) +{ + char *protocol_warning = NULL; + int should_exit = networkstatus_check_required_protocols(c, + !server_mode(options), + &protocol_warning); + if (protocol_warning) { + tor_log(should_exit ? LOG_ERR : LOG_WARN, + LD_GENERAL, + "%s", protocol_warning); + } + if (should_exit) { + tor_assert_nonfatal(protocol_warning); + } + tor_free(protocol_warning); + if (should_exit) + exit(1); +} + /** Try to replace the current cached v3 networkstatus with the one in * consensus. If we don't have enough certificates to validate it, * store it in consensus_waiting_for_certs and launch a certificate fetch. @@ -1595,6 +1620,7 @@ networkstatus_set_current_consensus(const char *consensus, time_t current_valid_after = 0; int free_consensus = 1; /* Free 'c' at the end of the function */ int old_ewma_enabled; + int checked_protocols_already = 0; if (flav < 0) { /* XXXX we don't handle unrecognized flavors yet. */ @@ -1610,6 +1636,16 @@ networkstatus_set_current_consensus(const char *consensus, goto done; } + if (from_cache && !was_waiting_for_certs) { + /* We previously stored this; check _now_ to make sure that version-kills + * really work. This happens even before we check signatures: we did so + * before when we stored this to disk. This does mean an attacker who can + * write to the datadir can make us not start: such an attacker could + * already harm us by replacing our guards, which would be worse. */ + checked_protocols_already = 1; + handle_missing_protocol_warning(c, options); + } + if ((int)c->flavor != flav) { /* This wasn't the flavor we thought we were getting. */ if (require_flavor) { @@ -1735,6 +1771,10 @@ networkstatus_set_current_consensus(const char *consensus, if (!from_cache && flav == usable_consensus_flavor()) control_event_client_status(LOG_NOTICE, "CONSENSUS_ARRIVED"); + if (!checked_protocols_already) { + handle_missing_protocol_warning(c, options); + } + /* Are we missing any certificates at all? */ if (r != 1 && dl_certs) authority_certs_fetch_missing(c, now, source_dir); -- cgit v1.2.3-54-g00ecf From f33b90324abe11724f59389e1aeaf8b3e021c3af Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 25 Aug 2016 14:55:41 -0400 Subject: Include protocol versions in votes. --- src/or/dirserv.c | 11 +++++++++++ src/or/dirserv.h | 4 +++- src/or/dirvote.c | 5 +++-- src/or/networkstatus.c | 3 ++- src/or/or.h | 2 ++ 5 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src/or/networkstatus.c') diff --git a/src/or/dirserv.c b/src/or/dirserv.c index a4eb738a30..03b05ac5d1 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -24,6 +24,7 @@ #include "networkstatus.h" #include "nodelist.h" #include "policies.h" +#include "protover.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@ -1795,6 +1796,7 @@ version_from_platform(const char *platform) */ char * routerstatus_format_entry(const routerstatus_t *rs, const char *version, + const char *protocols, routerstatus_format_type_t format, const vote_routerstatus_t *vrs) { @@ -1858,6 +1860,9 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) { smartlist_add_asprintf(chunks, "v %s\n", version); } + if (protocols) { + smartlist_add_asprintf(chunks, "proto %s\n", protocols); + } if (format != NS_V2) { const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest); @@ -2836,6 +2841,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, rs->is_flagged_running = 0; vrs->version = version_from_platform(ri->platform); + if (ri->protocol_list) { + vrs->protocols = tor_strdup(ri->protocol_list); + } else { + vrs->protocols = tor_strdup( + protover_compute_for_old_tor(vrs->version)); + } vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now, microdescriptors); diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 3c914e9311..1e4f27e3d7 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -96,7 +96,9 @@ size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs, size_t dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed); char *routerstatus_format_entry( - const routerstatus_t *rs, const char *platform, + const routerstatus_t *rs, + const char *version, + const char *protocols, routerstatus_format_type_t format, const vote_routerstatus_t *vrs); void dirserv_free_all(void); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 2d840a5988..67f0ba8cc7 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -244,7 +244,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, char *rsf; vote_microdesc_hash_t *h; rsf = routerstatus_format_entry(&vrs->status, - vrs->version, NS_V3_VOTE, vrs); + vrs->version, vrs->protocols, + NS_V3_VOTE, vrs); if (rsf) smartlist_add(chunks, rsf); @@ -2007,7 +2008,7 @@ networkstatus_compute_consensus(smartlist_t *votes, char *buf; /* Okay!! Now we can write the descriptor... */ /* First line goes into "buf". */ - buf = routerstatus_format_entry(&rs_out, NULL, rs_format, NULL); + buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL); if (buf) smartlist_add(chunks, buf); } diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 6c92773a7a..0fc22c9aa3 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -230,6 +230,7 @@ vote_routerstatus_free(vote_routerstatus_t *rs) if (!rs) return; tor_free(rs->version); + tor_free(rs->protocols); tor_free(rs->status.exitsummary); for (h = rs->microdesc; h; h = next) { tor_free(h->microdesc_hash_line); @@ -2095,7 +2096,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs) char * networkstatus_getinfo_helper_single(const routerstatus_t *rs) { - return routerstatus_format_entry(rs, NULL, NS_CONTROL_PORT, NULL); + return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL); } /** Alloc and return a string describing routerstatuses for the most diff --git a/src/or/or.h b/src/or/or.h index e9d41eeef8..5085139121 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2408,6 +2408,8 @@ typedef struct vote_routerstatus_t { * networkstatus_t.known_flags. */ char *version; /**< The version that the authority says this router is * running. */ + char *protocols; /**< The protocols that this authority says this router + * provides. */ unsigned int has_measured_bw:1; /**< The vote had a measured bw */ /** True iff the vote included an entry for ed25519 ID, or included * "id ed25519 none" to indicate that there was no ed25519 ID. */ -- cgit v1.2.3-54-g00ecf From 0a3da5ce79768c15da3897097f0cc7ba727a7d35 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 13 Sep 2016 11:02:44 -0400 Subject: prop264: sometimes check client _and_ server versions. As before, we check server protocols whenever server_mode(options) is true and we check client protocols whenever server_mode(options) is false. Additionally, we now _also_ check client protocols whenever any client port is set. --- src/or/networkstatus.c | 52 +++++++++++++++++++++++++++++++++++++++++++------- src/or/networkstatus.h | 3 --- 2 files changed, 45 insertions(+), 10 deletions(-) (limited to 'src/or/networkstatus.c') diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 0fc22c9aa3..3299a2c048 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -125,6 +125,9 @@ static void routerstatus_list_update_named_server_map(void); static void update_consensus_bootstrap_multiple_downloads( time_t now, const or_options_t *options); +static int networkstatus_check_required_protocols(const networkstatus_t *ns, + int client_mode, + char **warning_out); /** Forget that we've warned about anything networkstatus-related, so we will * give fresh warnings if the same behavior happens again. */ @@ -1553,17 +1556,35 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c, } #endif //TOR_UNIT_TESTS -/** Called when we have received a networkstatus c. If there are - * any _required_ protocols we are missing, log an error and exit - * immediately. If there are any _recommended_ protocols we are missing, - * warn. */ +/** + * Return true if any option is set in options to make us behave + * as a client. + * + * XXXX If we need this elsewhere at any point, we should make it nonstatic + * XXXX and move it into another file. + */ +static int +any_client_port_set(const or_options_t *options) +{ + return (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->ControlPort_set || + options->DNSPort_set); +} + +/** + * Helper for handle_missing_protocol_warning: handles either the + * client case (if is_client is set) or the server case otherwise. + */ static void -handle_missing_protocol_warning(const networkstatus_t *c, - const or_options_t *options) +handle_missing_protocol_warning_impl(const networkstatus_t *c, + int is_client) { char *protocol_warning = NULL; + int should_exit = networkstatus_check_required_protocols(c, - !server_mode(options), + is_client, &protocol_warning); if (protocol_warning) { tor_log(should_exit ? LOG_ERR : LOG_WARN, @@ -1578,6 +1599,23 @@ handle_missing_protocol_warning(const networkstatus_t *c, exit(1); } +/** Called when we have received a networkstatus c. If there are + * any _required_ protocols we are missing, log an error and exit + * immediately. If there are any _recommended_ protocols we are missing, + * warn. */ +static void +handle_missing_protocol_warning(const networkstatus_t *c, + const or_options_t *options) +{ + const int is_server = server_mode(options); + const int is_client = any_client_port_set(options) || !is_server; + + if (is_server) + handle_missing_protocol_warning_impl(c, 0); + if (is_client) + handle_missing_protocol_warning_impl(c, 1); +} + /** Try to replace the current cached v3 networkstatus with the one in * consensus. If we don't have enough certificates to validate it, * store it in consensus_waiting_for_certs and launch a certificate fetch. diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index da175ecca9..71f36b69ed 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -122,9 +122,6 @@ void document_signature_free(document_signature_t *sig); document_signature_t *document_signature_dup(const document_signature_t *sig); void networkstatus_free_all(void); int networkstatus_get_weight_scale_param(networkstatus_t *ns); -int networkstatus_check_required_protocols(const networkstatus_t *ns, - int client_mode, - char **warning_out); #ifdef NETWORKSTATUS_PRIVATE STATIC void vote_routerstatus_free(vote_routerstatus_t *rs); -- cgit v1.2.3-54-g00ecf From 1ab641a70e1f2cc2548abfd03e62e3f8b04825d2 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 22 Sep 2016 10:21:13 -0400 Subject: Remove version_known, and subtly change the meaning of protocols_known --- src/or/networkstatus.c | 5 +++-- src/or/or.h | 8 ++------ src/or/routerlist.c | 5 +++-- src/or/routerparse.c | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) (limited to 'src/or/networkstatus.c') diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 3299a2c048..f113777be1 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1456,8 +1456,9 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) a->is_valid != b->is_valid || a->is_possible_guard != b->is_possible_guard || a->is_bad_exit != b->is_bad_exit || - a->is_hs_dir != b->is_hs_dir || - a->version_known != b->version_known; + a->is_hs_dir != b->is_hs_dir; + // XXXX this function needs a huge refactoring; it has gotten out + // XXXX of sync with routerstatus_t, and it will do so again. } /** Notify controllers of any router status entries that changed between diff --git a/src/or/or.h b/src/or/or.h index befbf716af..7c7163f035 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2193,12 +2193,8 @@ typedef struct routerstatus_t { unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort * or it claims to accept tunnelled dir requests. */ - /** True iff we know version info for this router. (i.e., a "v" entry was - * included.) We'll replace all these with a big tor_version_t or a char[] - * if the number of traits we care about ever becomes incredibly big. */ - unsigned int version_known:1; - - /** True iff we have a proto line for this router.*/ + /** True iff we have a proto line for this router, or a versions line + * from which we could infer the protocols. */ unsigned int protocols_known:1; /** True iff this router has a version or protocol list that allows it to diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 0a03f13a56..0e637f4833 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -5526,7 +5526,8 @@ routerinfo_has_curve25519_onion_key(const routerinfo_t *ri) } /* Is rs running a tor version known to support ntor? - * If allow_unknown_versions is true, return true if the version is unknown. + * If allow_unknown_versions is true, return true if we can't tell + * (from a versions line or a protocols line) whether it supports ntor. * Otherwise, return false if the version is unknown. */ int routerstatus_version_supports_ntor(const routerstatus_t *rs, @@ -5536,7 +5537,7 @@ routerstatus_version_supports_ntor(const routerstatus_t *rs, return allow_unknown_versions; } - if (!rs->version_known && !rs->protocols_known) { + if (!rs->protocols_known) { return allow_unknown_versions; } diff --git a/src/or/routerparse.c b/src/or/routerparse.c index aecf6a2723..d5690c1101 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -2914,13 +2914,13 @@ routerstatus_parse_entry_from_string(memarea_t *area, } if ((tok = find_opt_by_keyword(tokens, K_V))) { tor_assert(tok->n_args == 1); - rs->version_known = 1; if (!strcmpstart(tok->args[0], "Tor ") && !found_protocol_list) { /* We only do version checks like this in the case where * the version is a "Tor" version, and where there is no * list of protocol versions that we should be looking at instead. */ rs->supports_extend2_cells = tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha"); + rs->protocols_known = 1; } if (vote_rs) { vote_rs->version = tor_strdup(tok->args[0]); -- cgit v1.2.3-54-g00ecf