diff options
Diffstat (limited to 'src/or/protover.c')
-rw-r--r-- | src/or/protover.c | 177 |
1 files changed, 161 insertions, 16 deletions
diff --git a/src/or/protover.c b/src/or/protover.c index 2c5d5ab1fc..c973660980 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Tor Project, Inc. */ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -28,11 +28,14 @@ #include "protover.h" #include "routerparse.h" +#ifndef HAVE_RUST + static const smartlist_t *get_supported_protocol_list(void); static int protocol_list_contains(const smartlist_t *protos, protocol_type_t pr, uint32_t ver); /** Mapping between protocol type string and protocol type. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `PROTOCOL_NAMES` static const struct { protocol_type_t protover_type; const char *name; @@ -51,6 +54,11 @@ static const struct { #define N_PROTOCOL_NAMES ARRAY_LENGTH(PROTOCOL_NAMES) +/* Maximum allowed length of any single subprotocol name. */ +// C_RUST_COUPLED: src/rust/protover/protover.rs +// `MAX_PROTOCOL_NAME_LENGTH` +static const unsigned MAX_PROTOCOL_NAME_LENGTH = 100; + /** * Given a protocol_type_t, return the corresponding string used in * descriptors. @@ -94,7 +102,7 @@ str_to_protocol_type(const char *s, protocol_type_t *pr_out) * Release all space held by a single proto_entry_t structure */ STATIC void -proto_entry_free(proto_entry_t *entry) +proto_entry_free_(proto_entry_t *entry) { if (!entry) return; @@ -206,6 +214,16 @@ parse_single_entry(const char *s, const char *end_of_entry) if (equals == s) goto error; + /* The name must not be longer than MAX_PROTOCOL_NAME_LENGTH. */ + if (equals - s > (int)MAX_PROTOCOL_NAME_LENGTH) { + log_warn(LD_NET, "When parsing a protocol entry, I got a very large " + "protocol name. This is possibly an attack or a bug, unless " + "the Tor network truly supports protocol names larger than " + "%ud characters. The offending string was: %s", + MAX_PROTOCOL_NAME_LENGTH, escaped(out->name)); + goto error; + } + /* The name must contain only alphanumeric characters and hyphens. */ if (!is_valid_keyword(s, equals-s)) goto error; @@ -275,6 +293,21 @@ parse_protocol_list(const char *s) } /** + * Return true if the unparsed protover in <b>s</b> would contain a protocol + * name longer than MAX_PROTOCOL_NAME_LENGTH, and false otherwise. + */ +bool +protover_contains_long_protocol_names(const char *s) +{ + smartlist_t *list = parse_protocol_list(s); + if (!list) + return true; /* yes, has a dangerous name */ + SMARTLIST_FOREACH(list, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(list); + return false; /* no, looks fine */ +} + +/** * Given a protocol type and version number, return true iff we know * how to speak that protocol. */ @@ -307,20 +340,57 @@ protocol_list_supports_protocol(const char *list, protocol_type_t tp, return contains; } +/** + * Return true iff "list" encodes a protocol list that includes support for + * the indicated protocol and version, or some later version. + */ +int +protocol_list_supports_protocol_or_later(const char *list, + protocol_type_t tp, + uint32_t version) +{ + /* NOTE: This is a pretty inefficient implementation. If it ever shows + * up in profiles, we should memoize it. + */ + smartlist_t *protocols = parse_protocol_list(list); + if (!protocols) { + return 0; + } + const char *pr_name = protocol_type_to_str(tp); + + int contains = 0; + SMARTLIST_FOREACH_BEGIN(protocols, proto_entry_t *, proto) { + if (strcasecmp(proto->name, pr_name)) + continue; + SMARTLIST_FOREACH_BEGIN(proto->ranges, const proto_range_t *, range) { + if (range->high >= version) { + contains = 1; + goto found; + } + } SMARTLIST_FOREACH_END(range); + } SMARTLIST_FOREACH_END(proto); + + found: + SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(protocols); + return contains; +} + /** Return the canonical string containing the list of protocols * that we support. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `SUPPORTED_PROTOCOLS` const char * protover_get_supported_protocols(void) { return "Cons=1-2 " "Desc=1-2 " - "DirCache=1 " - "HSDir=1 " - "HSIntro=3 " + "DirCache=1-2 " + "HSDir=1-2 " + "HSIntro=3-4 " "HSRend=1-2 " - "Link=1-4 " - "LinkAuth=1 " + "Link=1-5 " + "LinkAuth=1,3 " "Microdesc=1-2 " "Relay=1-2"; } @@ -375,7 +445,7 @@ encode_protocol_list(const smartlist_t *sl) const char *separator = ""; smartlist_t *chunks = smartlist_new(); SMARTLIST_FOREACH_BEGIN(sl, const proto_entry_t *, ent) { - smartlist_add(chunks, tor_strdup(separator)); + smartlist_add_strdup(chunks, separator); proto_entry_encode_into(chunks, ent); @@ -392,6 +462,8 @@ encode_protocol_list(const smartlist_t *sl) /* We treat any protocol list with more than this many subprotocols in it * as a DoS attempt. */ +/// C_RUST_COUPLED: src/rust/protover/protover.rs +/// `MAX_PROTOCOLS_TO_EXPAND` static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16); /** Voting helper: Given a list of proto_entry_t, return a newly allocated @@ -412,6 +484,14 @@ expand_protocol_list(const smartlist_t *protos) SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) { const char *name = ent->name; + if (strlen(name) > MAX_PROTOCOL_NAME_LENGTH) { + log_warn(LD_NET, "When expanding a protocol entry, I got a very large " + "protocol name. This is possibly an attack or a bug, unless " + "the Tor network truly supports protocol names larger than " + "%ud characters. The offending string was: %s", + MAX_PROTOCOL_NAME_LENGTH, escaped(name)); + continue; + } SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) { uint32_t u; for (u = range->low; u <= range->high; ++u) { @@ -508,7 +588,7 @@ contract_protocol_list(const smartlist_t *proto_strings) smartlist_sort(lst, cmp_single_ent_by_version); if (! first_entry) - smartlist_add(chunks, tor_strdup(" ")); + smartlist_add_strdup(chunks, " "); /* We're going to construct this entry from the ranges. */ proto_entry_t *entry = tor_malloc_zero(sizeof(proto_entry_t)); @@ -657,7 +737,9 @@ int protover_all_supported(const char *s, char **missing_out) { int all_supported = 1; - smartlist_t *missing; + smartlist_t *missing_some; + smartlist_t *missing_completely; + smartlist_t *missing_all; if (!s) { return 1; @@ -670,7 +752,8 @@ protover_all_supported(const char *s, char **missing_out) return 1; } - missing = smartlist_new(); + missing_some = smartlist_new(); + missing_completely = smartlist_new(); SMARTLIST_FOREACH_BEGIN(entries, const proto_entry_t *, ent) { protocol_type_t tp; @@ -682,26 +765,86 @@ protover_all_supported(const char *s, char **missing_out) } SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) { + proto_entry_t *unsupported = tor_malloc_zero(sizeof(proto_entry_t)); + proto_range_t *versions = tor_malloc_zero(sizeof(proto_range_t)); uint32_t i; + + unsupported->name = tor_strdup(ent->name); + unsupported->ranges = smartlist_new(); + for (i = range->low; i <= range->high; ++i) { if (!protover_is_supported_here(tp, i)) { - goto unsupported; + if (versions->low == 0 && versions->high == 0) { + versions->low = i; + /* Pre-emptively add the high now, just in case we're in a single + * version range (e.g. "Link=999"). */ + versions->high = i; + } + /* If the last one to be unsupported is one less than the current + * one, we're in a continuous range, so set the high field. */ + if ((versions->high && versions->high == i - 1) || + /* Similarly, if the last high wasn't set and we're currently + * one higher than the low, add current index as the highest + * known high. */ + (!versions->high && versions->low == i - 1)) { + versions->high = i; + continue; + } + } else { + /* If we hit a supported version, and we previously had a range, + * we've hit a non-continuity. Copy the previous range and add it to + * the unsupported->ranges list and zero-out the previous range for + * the next iteration. */ + if (versions->low != 0 && versions->high != 0) { + proto_range_t *versions_to_add = tor_malloc(sizeof(proto_range_t)); + + versions_to_add->low = versions->low; + versions_to_add->high = versions->high; + smartlist_add(unsupported->ranges, versions_to_add); + + versions->low = 0; + versions->high = 0; + } } } + /* Once we've run out of versions to check, see if we had any unsupported + * ones and, if so, add them to unsupported->ranges. */ + if (versions->low != 0 && versions->high != 0) { + smartlist_add(unsupported->ranges, versions); + } + /* Finally, if we had something unsupported, add it to the list of + * missing_some things and mark that there was something missing. */ + if (smartlist_len(unsupported->ranges) != 0) { + smartlist_add(missing_some, (void*) unsupported); + all_supported = 0; + } else { + proto_entry_free(unsupported); + tor_free(versions); + } } SMARTLIST_FOREACH_END(range); continue; unsupported: all_supported = 0; - smartlist_add(missing, (void*) ent); + smartlist_add(missing_completely, (void*) ent); } SMARTLIST_FOREACH_END(ent); + /* We keep the two smartlists separate so that we can free the proto_entry_t + * we created and put in missing_some, so here we add them together to build + * the string. */ + missing_all = smartlist_new(); + smartlist_add_all(missing_all, missing_some); + smartlist_add_all(missing_all, missing_completely); + if (missing_out && !all_supported) { - tor_assert(0 != smartlist_len(missing)); - *missing_out = encode_protocol_list(missing); + tor_assert(smartlist_len(missing_all) != 0); + *missing_out = encode_protocol_list(missing_all); } - smartlist_free(missing); + SMARTLIST_FOREACH(missing_some, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(missing_some); + smartlist_free(missing_completely); + smartlist_free(missing_all); SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent)); smartlist_free(entries); @@ -742,6 +885,7 @@ protocol_list_contains(const smartlist_t *protos, * Note that this is only used to infer protocols for Tor versions that * can't declare their own. **/ +/// C_RUST_COUPLED: src/rust/protover/protover.rs `compute_for_old_tor` const char * protover_compute_for_old_tor(const char *version) { @@ -791,3 +935,4 @@ protover_free_all(void) } } +#endif /* !defined(HAVE_RUST) */ |