aboutsummaryrefslogtreecommitdiff
path: root/src/or/protover.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/protover.c')
-rw-r--r--src/or/protover.c177
1 files changed, 161 insertions, 16 deletions
diff --git a/src/or/protover.c b/src/or/protover.c
index 2c5d5ab1fc..a63c2eb02d 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 continous 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) */