diff options
Diffstat (limited to 'src/feature/dirparse')
23 files changed, 501 insertions, 266 deletions
diff --git a/src/feature/dirparse/.may_include b/src/feature/dirparse/.may_include new file mode 100644 index 0000000000..424c745c12 --- /dev/null +++ b/src/feature/dirparse/.may_include @@ -0,0 +1 @@ +*.h diff --git a/src/feature/dirparse/authcert_members.h b/src/feature/dirparse/authcert_members.h new file mode 100644 index 0000000000..53eab175d6 --- /dev/null +++ b/src/feature/dirparse/authcert_members.h @@ -0,0 +1,31 @@ +/* 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 authcert_members.h + * + * @brief List of tokens common to V3 authority certificates and V3 + * consensuses. + **/ + +#ifndef TOR_AUTHCERT_MEMBERS_H +#define TOR_AUTHCERT_MEMBERS_H + +// clang-format off +#define AUTHCERT_MEMBERS \ + T1("dir-key-certificate-version", K_DIR_KEY_CERTIFICATE_VERSION, \ + GE(1), NO_OBJ ), \ + T1("dir-identity-key", K_DIR_IDENTITY_KEY, NO_ARGS, NEED_KEY ),\ + T1("dir-key-published",K_DIR_KEY_PUBLISHED, CONCAT_ARGS, NO_OBJ),\ + T1("dir-key-expires", K_DIR_KEY_EXPIRES, CONCAT_ARGS, NO_OBJ),\ + T1("dir-signing-key", K_DIR_SIGNING_KEY, NO_ARGS, NEED_KEY ),\ + T1("dir-key-crosscert", K_DIR_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ),\ + T1("dir-key-certification", K_DIR_KEY_CERTIFICATION,\ + NO_ARGS, NEED_OBJ),\ + T01("dir-address", K_DIR_ADDRESS, GE(1), NO_OBJ) +// clang-format on + +#endif /* !defined(TOR_AUTHCERT_MEMBERS_H) */ diff --git a/src/feature/dirparse/authcert_members.i b/src/feature/dirparse/authcert_members.i deleted file mode 100644 index 08cffca97a..0000000000 --- a/src/feature/dirparse/authcert_members.i +++ /dev/null @@ -1,13 +0,0 @@ -/* - * List of tokens common to V3 authority certificates and V3 consensuses. - */ - T1("dir-key-certificate-version", K_DIR_KEY_CERTIFICATE_VERSION, - GE(1), NO_OBJ ), - T1("dir-identity-key", K_DIR_IDENTITY_KEY, NO_ARGS, NEED_KEY ), - T1("dir-key-published",K_DIR_KEY_PUBLISHED, CONCAT_ARGS, NO_OBJ), - T1("dir-key-expires", K_DIR_KEY_EXPIRES, CONCAT_ARGS, NO_OBJ), - T1("dir-signing-key", K_DIR_SIGNING_KEY, NO_ARGS, NEED_KEY ), - T1("dir-key-crosscert", K_DIR_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ), - T1("dir-key-certification", K_DIR_KEY_CERTIFICATION, - NO_ARGS, NEED_OBJ), - T01("dir-address", K_DIR_ADDRESS, GE(1), NO_OBJ), diff --git a/src/feature/dirparse/authcert_parse.c b/src/feature/dirparse/authcert_parse.c index 1680bdbf30..b2460f6ace 100644 --- a/src/feature/dirparse/authcert_parse.c +++ b/src/feature/dirparse/authcert_parse.c @@ -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 authcert_parse.c + * @brief Authority certificate parsing. + **/ + #include "core/or/or.h" #include "feature/dirparse/authcert_parse.h" #include "feature/dirparse/parsecommon.h" @@ -13,18 +18,22 @@ #include "lib/memarea/memarea.h" #include "feature/nodelist/authority_cert_st.h" +#include "feature/dirparse/authcert_members.h" /** List of tokens recognized in V3 authority certificates. */ +// clang-format off static token_rule_t dir_key_certificate_table[] = { -#include "feature/dirparse/authcert_members.i" + AUTHCERT_MEMBERS, T1("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ), END_OF_TABLE }; +// clang-format on /** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to * the first character after the certificate. */ authority_cert_t * -authority_cert_parse_from_string(const char *s, const char **end_of_string) +authority_cert_parse_from_string(const char *s, size_t maxlen, + const char **end_of_string) { /** Reject any certificate at least this big; it is probably an overflow, an * attack, a bug, or some other nonsense. */ @@ -35,24 +44,25 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string) char digest[DIGEST_LEN]; directory_token_t *tok; char fp_declared[DIGEST_LEN]; - char *eos; + const char *eos; size_t len; int found; memarea_t *area = NULL; + const char *end_of_s = s + maxlen; const char *s_dup = s; - s = eat_whitespace(s); - eos = strstr(s, "\ndir-key-certification"); + s = eat_whitespace_eos(s, end_of_s); + eos = tor_memstr(s, end_of_s - s, "\ndir-key-certification"); if (! eos) { log_warn(LD_DIR, "No signature found on key certificate"); return NULL; } - eos = strstr(eos, "\n-----END SIGNATURE-----\n"); + eos = tor_memstr(eos, end_of_s - eos, "\n-----END SIGNATURE-----\n"); if (! eos) { log_warn(LD_DIR, "No end-of-signature found on key certificate"); return NULL; } - eos = strchr(eos+2, '\n'); + eos = memchr(eos+2, '\n', end_of_s - (eos+2)); tor_assert(eos); ++eos; len = eos - s; @@ -69,7 +79,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string) log_warn(LD_DIR, "Error tokenizing key certificate"); goto err; } - if (router_get_hash_impl(s, strlen(s), digest, "dir-key-certificate-version", + if (router_get_hash_impl(s, eos - s, digest, "dir-key-certificate-version", "\ndir-key-certification", '\n', DIGEST_SHA1) < 0) goto err; tok = smartlist_get(tokens, 0); @@ -120,13 +130,13 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string) tor_assert(tok->n_args); /* XXX++ use some tor_addr parse function below instead. -RD */ if (tor_addr_port_split(LOG_WARN, tok->args[0], &address, - &cert->dir_port) < 0 || + &cert->ipv4_dirport) < 0 || tor_inet_aton(address, &in) == 0) { log_warn(LD_DIR, "Couldn't parse dir-address in certificate"); tor_free(address); goto err; } - cert->addr = ntohl(in.s_addr); + tor_addr_from_in(&cert->ipv4_addr, &in); tor_free(address); } diff --git a/src/feature/dirparse/authcert_parse.h b/src/feature/dirparse/authcert_parse.h index ca475ad0e3..7f6dd1c02f 100644 --- a/src/feature/dirparse/authcert_parse.h +++ b/src/feature/dirparse/authcert_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 */ /** @@ -13,6 +13,7 @@ #define TOR_AUTHCERT_PARSE_H authority_cert_t *authority_cert_parse_from_string(const char *s, + size_t maxlen, const char **end_of_string); #endif /* !defined(TOR_AUTHCERT_PARSE_H) */ diff --git a/src/feature/dirparse/feature_dirparse.md b/src/feature/dirparse/feature_dirparse.md new file mode 100644 index 0000000000..e4b34668ba --- /dev/null +++ b/src/feature/dirparse/feature_dirparse.md @@ -0,0 +1,8 @@ +@dir /feature/dirparse +@brief feature/dirparse: Parsing Tor directory objects + +We define a number of "directory objects" in +[dir-spec.txt](https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt), +all of them using a common line-oriented meta-format. This module is used by +other parts of Tor to parse them. + diff --git a/src/feature/dirparse/include.am b/src/feature/dirparse/include.am new file mode 100644 index 0000000000..edca04f6f7 --- /dev/null +++ b/src/feature/dirparse/include.am @@ -0,0 +1,25 @@ + +# ADD_C_FILE: INSERT SOURCES HERE. +LIBTOR_APP_A_SOURCES += \ + src/feature/dirparse/authcert_parse.c \ + src/feature/dirparse/microdesc_parse.c \ + src/feature/dirparse/ns_parse.c \ + src/feature/dirparse/parsecommon.c \ + src/feature/dirparse/policy_parse.c \ + src/feature/dirparse/routerparse.c \ + src/feature/dirparse/sigcommon.c \ + src/feature/dirparse/signing.c \ + src/feature/dirparse/unparseable.c + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/feature/dirparse/authcert_members.h \ + src/feature/dirparse/authcert_parse.h \ + src/feature/dirparse/microdesc_parse.h \ + src/feature/dirparse/ns_parse.h \ + src/feature/dirparse/parsecommon.h \ + src/feature/dirparse/policy_parse.h \ + src/feature/dirparse/routerparse.h \ + src/feature/dirparse/sigcommon.h \ + src/feature/dirparse/signing.h \ + src/feature/dirparse/unparseable.h diff --git a/src/feature/dirparse/microdesc_parse.c b/src/feature/dirparse/microdesc_parse.c index 5a75af3994..31415f3fb7 100644 --- a/src/feature/dirparse/microdesc_parse.c +++ b/src/feature/dirparse/microdesc_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 */ /** @@ -18,6 +18,7 @@ #include "feature/dirparse/routerparse.h" #include "feature/nodelist/microdesc.h" #include "feature/nodelist/nickname.h" +#include "feature/nodelist/nodefamily.h" #include "feature/relay/router.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_ed25519.h" @@ -27,17 +28,19 @@ #include "feature/nodelist/microdesc_st.h" /** List of tokens recognized in microdescriptors */ +// clang-format off static token_rule_t microdesc_token_table[] = { T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024), - T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), + T1("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), T0N("id", K_ID, GE(2), NO_OBJ ), T0N("a", K_A, GE(1), NO_OBJ ), - T01("family", K_FAMILY, ARGS, NO_OBJ ), + T01("family", K_FAMILY, CONCAT_ARGS, NO_OBJ ), T01("p", K_P, CONCAT_ARGS, NO_OBJ ), T01("p6", K_P6, CONCAT_ARGS, NO_OBJ ), A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ), END_OF_TABLE }; +// clang-format on /** Assuming that s starts with a microdesc, return the start of the * *NEXT* one. Return NULL on "not found." */ @@ -91,6 +94,190 @@ find_start_of_next_microdesc(const char *s, const char *eos) #undef NEXT_LINE } +static inline int +policy_is_reject_star_or_null(struct short_policy_t *policy) +{ + return !policy || short_policy_is_reject_star(policy); +} + +/** + * Return a human-readable description of a given saved_location_t. + * Never returns NULL. + **/ +static const char * +saved_location_to_string(saved_location_t where) +{ + const char *location; + switch (where) { + case SAVED_NOWHERE: + location = "download or generated string"; + break; + case SAVED_IN_CACHE: + location = "cache"; + break; + case SAVED_IN_JOURNAL: + location = "journal"; + break; + default: + location = "unknown location"; + break; + } + return location; +} + +/** + * Given a microdescriptor stored in <b>where</b> which starts at <b>s</b>, + * which ends at <b>start_of_next_microdescriptor</b>, and which is located + * within a larger document beginning at <b>start</b>: Fill in the body, + * bodylen, bodylen, saved_location, off, and digest fields of <b>md</b> as + * appropriate. + * + * The body field will be an alias within <b>s</b> if <b>saved_location</b> + * is SAVED_IN_CACHE, and will be copied into body and nul-terminated + * otherwise. + **/ +static int +microdesc_extract_body(microdesc_t *md, + const char *start, + const char *s, const char *start_of_next_microdesc, + saved_location_t where) +{ + const bool copy_body = (where != SAVED_IN_CACHE); + + const char *cp = tor_memstr(s, start_of_next_microdesc-s, "onion-key"); + + const bool no_onion_key = (cp == NULL); + if (no_onion_key) { + cp = s; /* So that we have *some* junk to put in the body */ + } + + md->bodylen = start_of_next_microdesc - cp; + md->saved_location = where; + if (copy_body) + md->body = tor_memdup_nulterm(cp, md->bodylen); + else + md->body = (char*)cp; + md->off = cp - start; + + crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256); + + return no_onion_key ? -1 : 0; +} + +/** + * Parse a microdescriptor which begins at <b>s</b> and ends at + * <b>start_of_next_microdesc</b>. Store its fields into <b>md</b>. Use + * <b>where</b> for generating log information. If <b>allow_annotations</b> + * is true, then one or more annotations may precede the microdescriptor body + * proper. Use <b>area</b> for memory management, clearing it when done. + * + * On success, return 0; otherwise return -1. + **/ +static int +microdesc_parse_fields(microdesc_t *md, + memarea_t *area, + const char *s, const char *start_of_next_microdesc, + int allow_annotations, + saved_location_t where) +{ + smartlist_t *tokens = smartlist_new(); + int rv = -1; + int flags = allow_annotations ? TS_ANNOTATIONS_OK : 0; + directory_token_t *tok; + + if (tokenize_string(area, s, start_of_next_microdesc, tokens, + microdesc_token_table, flags)) { + log_warn(LD_DIR, "Unparseable microdescriptor found in %s", + saved_location_to_string(where)); + goto err; + } + + if ((tok = find_opt_by_keyword(tokens, A_LAST_LISTED))) { + if (parse_iso_time(tok->args[0], &md->last_listed)) { + log_warn(LD_DIR, "Bad last-listed time in microdescriptor"); + goto err; + } + } + + tok = find_by_keyword(tokens, K_ONION_KEY); + if (!crypto_pk_public_exponent_ok(tok->key)) { + log_warn(LD_DIR, + "Relay's onion key had invalid exponent."); + goto err; + } + md->onion_pkey = tor_memdup(tok->object_body, tok->object_size); + md->onion_pkey_len = tok->object_size; + crypto_pk_free(tok->key); + + if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) { + curve25519_public_key_t k; + tor_assert(tok->n_args >= 1); + if (curve25519_public_from_base64(&k, tok->args[0]) < 0) { + log_warn(LD_DIR, "Bogus ntor-onion-key in microdesc"); + goto err; + } + md->onion_curve25519_pkey = + tor_memdup(&k, sizeof(curve25519_public_key_t)); + } + + smartlist_t *id_lines = find_all_by_keyword(tokens, K_ID); + if (id_lines) { + SMARTLIST_FOREACH_BEGIN(id_lines, directory_token_t *, t) { + tor_assert(t->n_args >= 2); + if (!strcmp(t->args[0], "ed25519")) { + if (md->ed25519_identity_pkey) { + log_warn(LD_DIR, "Extra ed25519 key in microdesc"); + smartlist_free(id_lines); + goto err; + } + ed25519_public_key_t k; + if (ed25519_public_from_base64(&k, t->args[1])<0) { + log_warn(LD_DIR, "Bogus ed25519 key in microdesc"); + smartlist_free(id_lines); + goto err; + } + md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k)); + } + } SMARTLIST_FOREACH_END(t); + smartlist_free(id_lines); + } + + { + smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); + if (a_lines) { + find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport); + smartlist_free(a_lines); + } + } + + if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) { + md->family = nodefamily_parse(tok->args[0], + NULL, + NF_WARN_MALFORMED); + } + + if ((tok = find_opt_by_keyword(tokens, K_P))) { + md->exit_policy = parse_short_policy(tok->args[0]); + } + if ((tok = find_opt_by_keyword(tokens, K_P6))) { + md->ipv6_exit_policy = parse_short_policy(tok->args[0]); + } + + if (policy_is_reject_star_or_null(md->exit_policy) && + policy_is_reject_star_or_null(md->ipv6_exit_policy)) { + md->policy_is_reject_star = 1; + } + + rv = 0; + err: + + SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); + memarea_clear(area); + smartlist_free(tokens); + + return rv; +} + /** Parse as many microdescriptors as are found from the string starting at * <b>s</b> and ending at <b>eos</b>. If allow_annotations is set, read any * annotations we recognize and ignore ones we don't. @@ -108,16 +295,11 @@ microdescs_parse_from_string(const char *s, const char *eos, saved_location_t where, smartlist_t *invalid_digests_out) { - smartlist_t *tokens; smartlist_t *result; microdesc_t *md = NULL; memarea_t *area; const char *start = s; const char *start_of_next_microdesc; - int flags = allow_annotations ? TS_ANNOTATIONS_OK : 0; - const int copy_body = (where != SAVED_IN_CACHE); - - directory_token_t *tok; if (!eos) eos = s + strlen(s); @@ -125,143 +307,47 @@ microdescs_parse_from_string(const char *s, const char *eos, s = eat_whitespace_eos(s, eos); area = memarea_new(); result = smartlist_new(); - tokens = smartlist_new(); while (s < eos) { - int okay = 0; + bool okay = false; start_of_next_microdesc = find_start_of_next_microdesc(s, eos); if (!start_of_next_microdesc) start_of_next_microdesc = eos; md = tor_malloc_zero(sizeof(microdesc_t)); + uint8_t md_digest[DIGEST256_LEN]; { - const char *cp = tor_memstr(s, start_of_next_microdesc-s, - "onion-key"); - const int no_onion_key = (cp == NULL); - if (no_onion_key) { - cp = s; /* So that we have *some* junk to put in the body */ - } + const bool body_not_found = + microdesc_extract_body(md, start, s, + start_of_next_microdesc, + where) < 0; - md->bodylen = start_of_next_microdesc - cp; - md->saved_location = where; - if (copy_body) - md->body = tor_memdup_nulterm(cp, md->bodylen); - else - md->body = (char*)cp; - md->off = cp - start; - crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256); - if (no_onion_key) { + memcpy(md_digest, md->digest, DIGEST256_LEN); + if (body_not_found) { log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Malformed or truncated descriptor"); goto next; } } - if (tokenize_string(area, s, start_of_next_microdesc, tokens, - microdesc_token_table, flags)) { - log_warn(LD_DIR, "Unparseable microdescriptor"); - goto next; + if (microdesc_parse_fields(md, area, s, start_of_next_microdesc, + allow_annotations, where) == 0) { + smartlist_add(result, md); + md = NULL; // prevent free + okay = true; } - if ((tok = find_opt_by_keyword(tokens, A_LAST_LISTED))) { - if (parse_iso_time(tok->args[0], &md->last_listed)) { - log_warn(LD_DIR, "Bad last-listed time in microdescriptor"); - goto next; - } - } - - tok = find_by_keyword(tokens, K_ONION_KEY); - if (!crypto_pk_public_exponent_ok(tok->key)) { - log_warn(LD_DIR, - "Relay's onion key had invalid exponent."); - goto next; - } - router_set_rsa_onion_pkey(tok->key, &md->onion_pkey, - &md->onion_pkey_len); - crypto_pk_free(tok->key); - - if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) { - curve25519_public_key_t k; - tor_assert(tok->n_args >= 1); - if (curve25519_public_from_base64(&k, tok->args[0]) < 0) { - log_warn(LD_DIR, "Bogus ntor-onion-key in microdesc"); - goto next; - } - md->onion_curve25519_pkey = - tor_memdup(&k, sizeof(curve25519_public_key_t)); - } - - smartlist_t *id_lines = find_all_by_keyword(tokens, K_ID); - if (id_lines) { - SMARTLIST_FOREACH_BEGIN(id_lines, directory_token_t *, t) { - tor_assert(t->n_args >= 2); - if (!strcmp(t->args[0], "ed25519")) { - if (md->ed25519_identity_pkey) { - log_warn(LD_DIR, "Extra ed25519 key in microdesc"); - smartlist_free(id_lines); - goto next; - } - ed25519_public_key_t k; - if (ed25519_public_from_base64(&k, t->args[1])<0) { - log_warn(LD_DIR, "Bogus ed25519 key in microdesc"); - smartlist_free(id_lines); - goto next; - } - md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k)); - } - } SMARTLIST_FOREACH_END(t); - smartlist_free(id_lines); - } - - { - smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); - if (a_lines) { - find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport); - smartlist_free(a_lines); - } - } - - if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) { - int i; - md->family = smartlist_new(); - for (i=0;i<tok->n_args;++i) { - if (!is_legal_nickname_or_hexdigest(tok->args[i])) { - log_warn(LD_DIR, "Illegal nickname %s in family line", - escaped(tok->args[i])); - goto next; - } - smartlist_add_strdup(md->family, tok->args[i]); - } - } - - if ((tok = find_opt_by_keyword(tokens, K_P))) { - md->exit_policy = parse_short_policy(tok->args[0]); - } - if ((tok = find_opt_by_keyword(tokens, K_P6))) { - md->ipv6_exit_policy = parse_short_policy(tok->args[0]); - } - - smartlist_add(result, md); - okay = 1; - - md = NULL; next: if (! okay && invalid_digests_out) { smartlist_add(invalid_digests_out, - tor_memdup(md->digest, DIGEST256_LEN)); + tor_memdup(md_digest, DIGEST256_LEN)); } microdesc_free(md); md = NULL; - - SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); - memarea_clear(area); - smartlist_clear(tokens); s = start_of_next_microdesc; } - SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); memarea_drop_all(area); - smartlist_free(tokens); return result; } diff --git a/src/feature/dirparse/microdesc_parse.h b/src/feature/dirparse/microdesc_parse.h index 23a90084b1..e81126b8cd 100644 --- a/src/feature/dirparse/microdesc_parse.h +++ b/src/feature/dirparse/microdesc_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 */ /** @@ -17,4 +17,4 @@ smartlist_t *microdescs_parse_from_string(const char *s, const char *eos, saved_location_t where, smartlist_t *invalid_digests_out); -#endif +#endif /* !defined(TOR_MICRODESC_PARSE_H) */ diff --git a/src/feature/dirparse/ns_parse.c b/src/feature/dirparse/ns_parse.c index 109eebeb66..138d248b08 100644 --- a/src/feature/dirparse/ns_parse.c +++ b/src/feature/dirparse/ns_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 */ /** @@ -13,6 +13,7 @@ #include "core/or/or.h" #include "app/config/config.h" +#include "core/or/protover.h" #include "core/or/versions.h" #include "feature/client/entrynodes.h" #include "feature/dirauth/dirvote.h" @@ -36,12 +37,14 @@ #include "feature/nodelist/networkstatus_st.h" #include "feature/nodelist/networkstatus_voter_info_st.h" #include "feature/nodelist/vote_routerstatus_st.h" +#include "feature/dirparse/authcert_members.h" #undef log #include <math.h> /** List of tokens recognized in the body part of v3 networkstatus * documents. */ +// clang-format off static token_rule_t rtrstatus_token_table[] = { T01("p", K_P, CONCAT_ARGS, NO_OBJ ), T1( "r", K_R, GE(7), NO_OBJ ), @@ -51,12 +54,14 @@ static token_rule_t rtrstatus_token_table[] = { T01("w", K_W, ARGS, NO_OBJ ), T0N("m", K_M, CONCAT_ARGS, NO_OBJ ), T0N("id", K_ID, GE(2), NO_OBJ ), - T01("pr", K_PROTO, CONCAT_ARGS, NO_OBJ ), + T1("pr", K_PROTO, CONCAT_ARGS, NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), END_OF_TABLE }; +// clang-format on /** List of tokens recognized in V3 networkstatus votes. */ +// clang-format off static token_rule_t networkstatus_token_table[] = { T1_START("network-status-version", K_NETWORK_STATUS_VERSION, GE(1), NO_OBJ ), @@ -84,7 +89,7 @@ static token_rule_t networkstatus_token_table[] = { T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS, CONCAT_ARGS, NO_OBJ ), -#include "feature/dirparse/authcert_members.i" + AUTHCERT_MEMBERS, T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ), @@ -97,8 +102,10 @@ static token_rule_t networkstatus_token_table[] = { END_OF_TABLE }; +// clang-format on /** List of tokens recognized in V3 networkstatus consensuses. */ +// clang-format off static token_rule_t networkstatus_consensus_token_table[] = { T1_START("network-status-version", K_NETWORK_STATUS_VERSION, GE(1), NO_OBJ ), @@ -135,14 +142,17 @@ static token_rule_t networkstatus_consensus_token_table[] = { END_OF_TABLE }; +// clang-format on /** List of tokens recognized in the footer of v1 directory footers. */ +// clang-format off static token_rule_t networkstatus_vote_footer_token_table[] = { T01("directory-footer", K_DIRECTORY_FOOTER, NO_ARGS, NO_OBJ ), T01("bandwidth-weights", K_BW_WEIGHTS, ARGS, NO_OBJ ), T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ), END_OF_TABLE }; +// clang-format on /** Try to find the start and end of the signed portion of a networkstatus * document in <b>s</b>. On success, set <b>start_out</b> to the first @@ -151,10 +161,11 @@ static token_rule_t networkstatus_vote_footer_token_table[] = { * -1. */ int router_get_networkstatus_v3_signed_boundaries(const char *s, + size_t len, const char **start_out, const char **end_out) { - return router_get_hash_impl_helper(s, strlen(s), + return router_get_hash_impl_helper(s, len, "network-status-version", "\ndirectory-signature", ' ', LOG_INFO, @@ -166,12 +177,13 @@ router_get_networkstatus_v3_signed_boundaries(const char *s, * signed portion can be identified. Return 0 on success, -1 on failure. */ int router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out, - const char *s) + const char *s, size_t len) { const char *start, *end; - if (router_get_networkstatus_v3_signed_boundaries(s, &start, &end) < 0) { + if (router_get_networkstatus_v3_signed_boundaries(s, len, + &start, &end) < 0) { start = s; - end = s + strlen(s); + end = s + len; } tor_assert(start); tor_assert(end); @@ -182,9 +194,10 @@ router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out, /** Set <b>digests</b> to all the digests of the consensus document in * <b>s</b> */ int -router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests) +router_get_networkstatus_v3_hashes(const char *s, size_t len, + common_digests_t *digests) { - return router_get_hashes_impl(s,strlen(s),digests, + return router_get_hashes_impl(s, len, digests, "network-status-version", "\ndirectory-signature", ' '); @@ -195,13 +208,13 @@ router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests) * return the start of the directory footer, or the next directory signature. * If none is found, return the end of the string. */ static inline const char * -find_start_of_next_routerstatus(const char *s) +find_start_of_next_routerstatus(const char *s, const char *s_eos) { const char *eos, *footer, *sig; - if ((eos = strstr(s, "\nr "))) + if ((eos = tor_memstr(s, s_eos - s, "\nr "))) ++eos; else - eos = s + strlen(s); + eos = s_eos; footer = tor_memstr(s, eos-s, "\ndirectory-footer"); sig = tor_memstr(s, eos-s, "\ndirectory-signature"); @@ -234,7 +247,7 @@ routerstatus_parse_guardfraction(const char *guardfraction_str, tor_assert(bool_eq(vote, vote_rs)); - /* If this info comes from a consensus, but we should't apply + /* If this info comes from a consensus, but we shouldn't apply guardfraction, just exit. */ if (is_consensus && !should_apply_guardfraction(NULL)) { return 0; @@ -289,7 +302,8 @@ routerstatus_parse_guardfraction(const char *guardfraction_str, **/ STATIC routerstatus_t * routerstatus_parse_entry_from_string(memarea_t *area, - const char **s, smartlist_t *tokens, + const char **s, const char *s_eos, + smartlist_t *tokens, networkstatus_t *vote, vote_routerstatus_t *vote_rs, int consensus_method, @@ -308,7 +322,7 @@ routerstatus_parse_entry_from_string(memarea_t *area, flav = FLAV_NS; tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC); - eos = find_start_of_next_routerstatus(*s); + eos = find_start_of_next_routerstatus(*s, s_eos); if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) { log_warn(LD_DIR, "Error tokenizing router status"); @@ -371,12 +385,12 @@ routerstatus_parse_entry_from_string(memarea_t *area, escaped(tok->args[5+offset])); goto err; } - rs->addr = ntohl(in.s_addr); + tor_addr_from_in(&rs->ipv4_addr, &in); - rs->or_port = (uint16_t) tor_parse_long(tok->args[6+offset], - 10,0,65535,NULL,NULL); - rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset], - 10,0,65535,NULL,NULL); + rs->ipv4_orport = (uint16_t) tor_parse_long(tok->args[6+offset], + 10,0,65535,NULL,NULL); + rs->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[7+offset], + 10,0,65535,NULL,NULL); { smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); @@ -430,6 +444,8 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->is_hs_dir = 1; } else if (!strcmp(tok->args[i], "V2Dir")) { rs->is_v2_dir = 1; + } else if (!strcmp(tok->args[i], "StaleDesc")) { + rs->is_staledesc = 1; } } /* These are implied true by having been included in a consensus made @@ -451,6 +467,10 @@ routerstatus_parse_entry_from_string(memarea_t *area, } } + // If the protover line is malformed, reject this routerstatus. + if (protocols && protover_list_is_invalid(protocols)) { + goto err; + } summarize_protover_flags(&rs->pv, protocols, version); } @@ -548,7 +568,7 @@ routerstatus_parse_entry_from_string(memarea_t *area, log_info(LD_BUG, "Found an entry in networkstatus with no " "microdescriptor digest. (Router %s ($%s) at %s:%d.)", rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN), - fmt_addr32(rs->addr), rs->or_port); + fmt_addr(&rs->ipv4_addr), rs->ipv4_orport); } } @@ -1048,10 +1068,25 @@ extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens) } } +/** Allocate a copy of a protover line, if present. If present but malformed, + * set *error to true. */ +static char * +dup_protocols_string(smartlist_t *tokens, bool *error, directory_keyword kw) +{ + directory_token_t *tok = find_opt_by_keyword(tokens, kw); + if (!tok) + return NULL; + if (protover_list_is_invalid(tok->args[0])) + *error = true; + return tor_strdup(tok->args[0]); +} + /** Parse a v3 networkstatus vote, opinion, or consensus (depending on * ns_type), from <b>s</b>, and return the result. Return NULL on failure. */ networkstatus_t * -networkstatus_parse_vote_from_string(const char *s, const char **eos_out, +networkstatus_parse_vote_from_string(const char *s, + size_t s_len, + const char **eos_out, networkstatus_type_t ns_type) { smartlist_t *tokens = smartlist_new(); @@ -1067,20 +1102,22 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, memarea_t *area = NULL, *rs_area = NULL; consensus_flavor_t flav = FLAV_NS; char *last_kwd=NULL; + const char *eos = s + s_len; tor_assert(s); if (eos_out) *eos_out = NULL; - if (router_get_networkstatus_v3_hashes(s, &ns_digests) || - router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed, s)<0) { + if (router_get_networkstatus_v3_hashes(s, s_len, &ns_digests) || + router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed, + s, s_len)<0) { log_warn(LD_DIR, "Unable to compute digest of network-status"); goto err; } area = memarea_new(); - end_of_header = find_start_of_next_routerstatus(s); + end_of_header = find_start_of_next_routerstatus(s, eos); if (tokenize_string(area, s, end_of_header, tokens, (ns_type == NS_TYPE_CONSENSUS) ? networkstatus_consensus_token_table : @@ -1111,10 +1148,12 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, if (ns_type != NS_TYPE_CONSENSUS) { const char *end_of_cert = NULL; - if (!(cert = strstr(s, "\ndir-key-certificate-version"))) + if (!(cert = tor_memstr(s, end_of_header - s, + "\ndir-key-certificate-version"))) goto err; ++cert; - ns->cert = authority_cert_parse_from_string(cert, &end_of_cert); + ns->cert = authority_cert_parse_from_string(cert, end_of_header - cert, + &end_of_cert); if (!ns->cert || !end_of_cert || end_of_cert > end_of_header) goto err; } @@ -1163,14 +1202,18 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } } - if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_CLIENT_PROTOCOLS))) - ns->recommended_client_protocols = tor_strdup(tok->args[0]); - if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_RELAY_PROTOCOLS))) - ns->recommended_relay_protocols = tor_strdup(tok->args[0]); - if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_CLIENT_PROTOCOLS))) - ns->required_client_protocols = tor_strdup(tok->args[0]); - if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_RELAY_PROTOCOLS))) - ns->required_relay_protocols = tor_strdup(tok->args[0]); + // Reject the vote if any of the protocols lines are malformed. + bool unparseable = false; + ns->recommended_client_protocols = dup_protocols_string(tokens, &unparseable, + K_RECOMMENDED_CLIENT_PROTOCOLS); + ns->recommended_relay_protocols = dup_protocols_string(tokens, &unparseable, + K_RECOMMENDED_RELAY_PROTOCOLS); + ns->required_client_protocols = dup_protocols_string(tokens, &unparseable, + K_REQUIRED_CLIENT_PROTOCOLS); + ns->required_relay_protocols = dup_protocols_string(tokens, &unparseable, + K_REQUIRED_RELAY_PROTOCOLS); + if (unparseable) + goto err; tok = find_by_keyword(tokens, K_VALID_AFTER); if (parse_iso_time(tok->args[0], &ns->valid_after)) @@ -1333,8 +1376,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, goto err; } if (ns->type != NS_TYPE_CONSENSUS) { - if (authority_cert_is_blacklisted(ns->cert)) { - log_warn(LD_DIR, "Rejecting vote signature made with blacklisted " + if (authority_cert_is_denylisted(ns->cert)) { + log_warn(LD_DIR, "Rejecting vote signature made with denylisted " "signing key %s", hex_str(ns->cert->signing_key_digest, DIGEST_LEN)); goto err; @@ -1346,13 +1389,13 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, escaped(tok->args[3])); goto err; } - voter->addr = ntohl(in.s_addr); + tor_addr_from_in(&voter->ipv4_addr, &in); int ok; - voter->dir_port = (uint16_t) + voter->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL); if (!ok) goto err; - voter->or_port = (uint16_t) + voter->ipv4_orport = (uint16_t) tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL); if (!ok) goto err; @@ -1424,23 +1467,27 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, s = end_of_header; ns->routerstatus_list = smartlist_new(); - while (!strcmpstart(s, "r ")) { + while (eos - s >= 2 && fast_memeq(s, "r ", 2)) { if (ns->type != NS_TYPE_CONSENSUS) { vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t)); - if (routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, ns, + if (routerstatus_parse_entry_from_string(rs_area, &s, eos, rs_tokens, ns, rs, 0, 0)) { smartlist_add(ns->routerstatus_list, rs); } else { vote_routerstatus_free(rs); + goto err; // Malformed routerstatus, reject this vote. } } else { routerstatus_t *rs; - if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, + if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, eos, + rs_tokens, NULL, NULL, ns->consensus_method, flav))) { /* Use exponential-backoff scheduling when downloading microdescs */ smartlist_add(ns->routerstatus_list, rs); + } else { + goto err; // Malformed routerstatus, reject this vote. } } } @@ -1465,7 +1512,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *, vrs) { if (! vrs->has_ed25519_listing || - tor_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN)) + fast_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN)) continue; if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) { log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not " @@ -1480,10 +1527,10 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, /* Parse footer; check signature. */ footer_tokens = smartlist_new(); - if ((end_of_footer = strstr(s, "\nnetwork-status-version "))) + if ((end_of_footer = tor_memstr(s, eos-s, "\nnetwork-status-version "))) ++end_of_footer; else - end_of_footer = s + strlen(s); + end_of_footer = eos; if (tokenize_string(area,s, end_of_footer, footer_tokens, networkstatus_vote_footer_token_table, 0)) { log_warn(LD_DIR, "Error tokenizing network-status vote footer."); diff --git a/src/feature/dirparse/ns_parse.h b/src/feature/dirparse/ns_parse.h index 10a6f9cefc..6a1ea85c92 100644 --- a/src/feature/dirparse/ns_parse.h +++ b/src/feature/dirparse/ns_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 */ /** @@ -12,18 +12,19 @@ #ifndef TOR_NS_PARSE_H #define TOR_NS_PARSE_H -int router_get_networkstatus_v3_hashes(const char *s, +int router_get_networkstatus_v3_hashes(const char *s, size_t len, common_digests_t *digests); -int router_get_networkstatus_v3_signed_boundaries(const char *s, +int router_get_networkstatus_v3_signed_boundaries(const char *s, size_t len, const char **start_out, const char **end_out); int router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out, - const char *s); + const char *s, size_t len); int compare_vote_routerstatus_entries(const void **_a, const void **_b); int networkstatus_verify_bw_weights(networkstatus_t *ns, int); enum networkstatus_type_t; networkstatus_t *networkstatus_parse_vote_from_string(const char *s, + size_t len, const char **eos_out, enum networkstatus_type_t ns_type); @@ -35,11 +36,12 @@ STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str, struct memarea_t; STATIC routerstatus_t *routerstatus_parse_entry_from_string( struct memarea_t *area, - const char **s, smartlist_t *tokens, + const char **s, const char *eos, + smartlist_t *tokens, networkstatus_t *vote, vote_routerstatus_t *vote_rs, int consensus_method, consensus_flavor_t flav); -#endif +#endif /* defined(NS_PARSE_PRIVATE) */ -#endif +#endif /* !defined(TOR_NS_PARSE_H) */ diff --git a/src/feature/dirparse/parsecommon.c b/src/feature/dirparse/parsecommon.c index 1664a77bbe..ab465c4d7f 100644 --- a/src/feature/dirparse/parsecommon.c +++ b/src/feature/dirparse/parsecommon.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 */ /** @@ -15,6 +15,7 @@ #include "lib/string/printf.h" #include "lib/memarea/memarea.h" #include "lib/crypt_ops/crypto_rsa.h" +#include "lib/ctime/di_ops.h" #include <string.h> @@ -169,7 +170,6 @@ get_token_arguments(memarea_t *area, directory_token_t *tok, char *cp = mem; int j = 0; char *args[MAX_ARGS]; - memset(args, 0, sizeof(args)); while (*cp) { if (j == MAX_ARGS) return -1; @@ -251,6 +251,16 @@ token_check_object(memarea_t *area, const char *kwd, return tok; } +/** Return true iff the <b>memlen</b>-byte chunk of memory at + * <b>memlen</b> is the same length as <b>token</b>, and their + * contents are equal. */ +static bool +mem_eq_token(const void *mem, size_t memlen, const char *token) +{ + size_t len = strlen(token); + return memlen == len && fast_memeq(mem, token, len); +} + /** Helper function: read the next token from *s, advance *s to the end of the * token, and return the parsed token. Parse *<b>s</b> according to the list * of tokens in <b>table</b>. @@ -266,7 +276,7 @@ get_next_token(memarea_t *area, * attack, a bug, or some other nonsense. */ #define MAX_LINE_LENGTH (128*1024) - const char *next, *eol, *obstart; + const char *next, *eol; size_t obname_len; int i; directory_token_t *tok; @@ -290,7 +300,7 @@ get_next_token(memarea_t *area, next = find_whitespace_eos(*s, eol); - if (!strcmp_len(*s, "opt", next-*s)) { + if (mem_eq_token(*s, next-*s, "opt")) { /* Skip past an "opt" at the start of the line. */ *s = eat_whitespace_eos_no_nl(next, eol); next = find_whitespace_eos(*s, eol); @@ -301,7 +311,7 @@ get_next_token(memarea_t *area, /* Search the table for the appropriate entry. (I tried a binary search * instead, but it wasn't any faster.) */ for (i = 0; table[i].t ; ++i) { - if (!strcmp_len(*s, table[i].t, next-*s)) { + if (mem_eq_token(*s, next-*s, table[i].t)) { /* We've found the keyword. */ kwd = table[i].t; tok->tp = table[i].v; @@ -352,9 +362,8 @@ get_next_token(memarea_t *area, if (!eol || eol-*s<11 || strcmpstart(*s, "-----BEGIN ")) /* No object. */ goto check_object; - obstart = *s; /* Set obstart to start of object spec */ if (eol - *s <= 16 || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */ - strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */ + !mem_eq_token(eol-5, 5, "-----") || /* nuls or invalid endings */ (eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */ RET_ERR("Malformed object: bad begin line"); } @@ -373,8 +382,8 @@ get_next_token(memarea_t *area, eol = eos; /* Validate the ending tag, which should be 9 + NAME + 5 + eol */ if ((size_t)(eol-next) != 9+obname_len+5 || - strcmp_len(next+9, tok->object_type, obname_len) || - strcmp_len(eol-5, "-----", 5)) { + !mem_eq_token(next+9, obname_len, tok->object_type) || + !mem_eq_token(eol-5, 5, "-----")) { tor_snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s", tok->object_type); ebuf[sizeof(ebuf)-1] = '\0'; @@ -383,28 +392,32 @@ get_next_token(memarea_t *area, if (next - *s > MAX_UNPARSED_OBJECT_SIZE) RET_ERR("Couldn't parse object: missing footer or object much too big."); + { + int r; + size_t maxsize = base64_decode_maxsize(next-*s); + tok->object_body = ALLOC(maxsize); + r = base64_decode(tok->object_body, maxsize, *s, next-*s); + if (r<0) + RET_ERR("Malformed object: bad base64-encoded data"); + tok->object_size = r; + } + if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */ if (o_syn != NEED_KEY && o_syn != NEED_KEY_1024 && o_syn != OBJ_OK) { RET_ERR("Unexpected public key."); } - tok->key = crypto_pk_new(); - if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart)) + tok->key = crypto_pk_asn1_decode(tok->object_body, tok->object_size); + if (! tok->key) RET_ERR("Couldn't parse public key."); } else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */ if (o_syn != NEED_SKEY_1024 && o_syn != OBJ_OK) { RET_ERR("Unexpected private key."); } - tok->key = crypto_pk_new(); - if (crypto_pk_read_private_key1024_from_string(tok->key, - obstart, eol-obstart)) + tok->key = crypto_pk_asn1_decode_private(tok->object_body, + tok->object_size, + 1024); + if (! tok->key) RET_ERR("Couldn't parse private key."); - } else { /* If it's something else, try to base64-decode it */ - int r; - tok->object_body = ALLOC(next-*s); /* really, this is too much RAM. */ - r = base64_decode(tok->object_body, next-*s, *s, next-*s); - if (r<0) - RET_ERR("Malformed object: bad base64-encoded data"); - tok->object_size = r; } *s = eol; diff --git a/src/feature/dirparse/parsecommon.h b/src/feature/dirparse/parsecommon.h index ef74925b26..4db9a89f13 100644 --- a/src/feature/dirparse/parsecommon.h +++ b/src/feature/dirparse/parsecommon.h @@ -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 */ /** diff --git a/src/feature/dirparse/policy_parse.c b/src/feature/dirparse/policy_parse.c index 7562ae409b..28cd174686 100644 --- a/src/feature/dirparse/policy_parse.c +++ b/src/feature/dirparse/policy_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 */ /** @@ -9,7 +9,7 @@ * \brief Code to parse address policies. **/ -#define EXPOSE_ROUTERDESC_TOKEN_TABLE +#define ROUTERDESC_TOKEN_TABLE_PRIVATE #include "core/or/or.h" diff --git a/src/feature/dirparse/policy_parse.h b/src/feature/dirparse/policy_parse.h index e09ee5559f..7764069e66 100644 --- a/src/feature/dirparse/policy_parse.h +++ b/src/feature/dirparse/policy_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 */ /** diff --git a/src/feature/dirparse/routerparse.c b/src/feature/dirparse/routerparse.c index e44fbf77f9..3d90c1bc91 100644 --- a/src/feature/dirparse/routerparse.c +++ b/src/feature/dirparse/routerparse.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 */ /** @@ -50,7 +50,7 @@ * </ul> **/ -#define EXPOSE_ROUTERDESC_TOKEN_TABLE +#define ROUTERDESC_TOKEN_TABLE_PRIVATE #include "core/or/or.h" #include "app/config/config.h" @@ -81,6 +81,7 @@ /****************************************************************************/ /** List of tokens recognized in router descriptors */ +// clang-format off const token_rule_t routerdesc_token_table[] = { T0N("reject", K_REJECT, ARGS, NO_OBJ ), T0N("accept", K_ACCEPT, ARGS, NO_OBJ ), @@ -90,24 +91,24 @@ const token_rule_t routerdesc_token_table[] = { T01("ipv6-policy", K_IPV6_POLICY, CONCAT_ARGS, NO_OBJ), T1( "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY_1024 ), T1( "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024 ), - T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), + T1("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ), T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ), T01("uptime", K_UPTIME, GE(1), NO_OBJ ), T01("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ), T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ ), T01("platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ ), - T01("proto", K_PROTO, CONCAT_ARGS, NO_OBJ ), + T1("proto", K_PROTO, CONCAT_ARGS, NO_OBJ ), T01("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ), T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ), T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ), - T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), - T01("master-key-ed25519", K_MASTER_KEY_ED25519, GE(1), NO_OBJ ), - T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), - T01("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ), - T01("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT, + T1("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), + T1("master-key-ed25519", K_MASTER_KEY_ED25519, GE(1), NO_OBJ ), + T1("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), + T1("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ), + T1("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT, EQ(1), NEED_OBJ ), T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ), @@ -123,13 +124,15 @@ const token_rule_t routerdesc_token_table[] = { END_OF_TABLE }; +// clang-format on /** List of tokens recognized in extra-info documents. */ +// clang-format off static token_rule_t extrainfo_token_table[] = { T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ), T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ), - T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), - T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), + T1("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), + T1("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), @@ -162,6 +165,7 @@ static token_rule_t extrainfo_token_table[] = { END_OF_TABLE }; +// clang-format on #undef T @@ -452,6 +456,12 @@ router_parse_entry_from_string(const char *s, const char *end, } } + if (!tor_memstr(s, end-s, "\nproto ")) { + log_debug(LD_DIR, "Found an obsolete router descriptor. " + "Rejecting quietly."); + goto err; + } + if (router_get_router_hash(s, end - s, digest) < 0) { log_warn(LD_DIR, "Couldn't compute router hash."); goto err; @@ -515,15 +525,15 @@ router_parse_entry_from_string(const char *s, const char *end, log_warn(LD_DIR,"Router address is not an IP address."); goto err; } - router->addr = ntohl(in.s_addr); + tor_addr_from_in(&router->ipv4_addr, &in); - router->or_port = + router->ipv4_orport = (uint16_t) tor_parse_long(tok->args[2],10,0,65535,&ok,NULL); if (!ok) { log_warn(LD_DIR,"Invalid OR port %s", escaped(tok->args[2])); goto err; } - router->dir_port = + router->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[4],10,0,65535,&ok,NULL); if (!ok) { log_warn(LD_DIR,"Invalid dir port %s", escaped(tok->args[4])); @@ -591,8 +601,8 @@ router_parse_entry_from_string(const char *s, const char *end, "Relay's onion key had invalid exponent."); goto err; } - router_set_rsa_onion_pkey(tok->key, &router->onion_pkey, - &router->onion_pkey_len); + router->onion_pkey = tor_memdup(tok->object_body, tok->object_size); + router->onion_pkey_len = tok->object_size; crypto_pk_free(tok->key); if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) { @@ -649,17 +659,18 @@ router_parse_entry_from_string(const char *s, const char *end, goto err; } if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { - log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor"); + log_warn(LD_DIR, "Wrong object type on identity-ed25519 " + "in descriptor"); goto err; } if (strcmp(cc_ntor_tok->object_type, "ED25519 CERT")) { log_warn(LD_DIR, "Wrong object type on ntor-onion-key-crosscert " - "in decriptor"); + "in descriptor"); goto err; } if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) { log_warn(LD_DIR, "Wrong object type on onion-key-crosscert " - "in decriptor"); + "in descriptor"); goto err; } if (strcmp(cc_ntor_tok->args[0], "0") && @@ -903,13 +914,14 @@ router_parse_entry_from_string(const char *s, const char *end, /* This router accepts tunnelled directory requests via begindir if it has * an open dirport or it included "tunnelled-dir-server". */ - if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || router->dir_port > 0) { + if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || + router->ipv4_dirport > 0) { router->supports_tunnelled_dir_requests = 1; } tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE); - if (!router->or_port) { + if (!router->ipv4_orport) { log_warn(LD_DIR,"or_port unreadable or 0. Failing."); goto err; } @@ -985,6 +997,11 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n') --end; + if (!tor_memstr(s, end-s, "\nidentity-ed25519")) { + log_debug(LD_DIR, "Found an obsolete extrainfo. Rejecting quietly."); + goto err; + } + if (router_get_extrainfo_hash(s, end-s, digest) < 0) { log_warn(LD_DIR, "Couldn't compute router hash."); goto err; @@ -1060,7 +1077,8 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, goto err; } if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { - log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor"); + log_warn(LD_DIR, "Wrong object type on identity-ed25519 " + "in descriptor"); goto err; } diff --git a/src/feature/dirparse/routerparse.h b/src/feature/dirparse/routerparse.h index f9a13f2168..519044e9b0 100644 --- a/src/feature/dirparse/routerparse.h +++ b/src/feature/dirparse/routerparse.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 */ /** @@ -40,7 +40,8 @@ int find_single_ipv6_orport(const smartlist_t *list, void routerparse_init(void); void routerparse_free_all(void); -#ifdef EXPOSE_ROUTERDESC_TOKEN_TABLE +#ifdef ROUTERDESC_TOKEN_TABLE_PRIVATE +#include "feature/dirparse/parsecommon.h" extern const struct token_rule_t routerdesc_token_table[]; #endif diff --git a/src/feature/dirparse/sigcommon.c b/src/feature/dirparse/sigcommon.c index 2019e09918..fb81b2da6e 100644 --- a/src/feature/dirparse/sigcommon.c +++ b/src/feature/dirparse/sigcommon.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 */ /** @@ -139,13 +139,13 @@ signed_digest_equals, (const uint8_t *d1, const uint8_t *d2, size_t len)) * the document when generating log messages. Return 0 on success, negative * on failure. */ -int -check_signature_token(const char *digest, +MOCK_IMPL(int, +check_signature_token,(const char *digest, ssize_t digest_len, directory_token_t *tok, crypto_pk_t *pkey, int flags, - const char *doctype) + const char *doctype)) { char *signed_digest; size_t keysize; diff --git a/src/feature/dirparse/sigcommon.h b/src/feature/dirparse/sigcommon.h index fdd8e839a9..c7f370f8e8 100644 --- a/src/feature/dirparse/sigcommon.h +++ b/src/feature/dirparse/sigcommon.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 */ /** @@ -20,12 +20,12 @@ int router_get_hash_impl(const char *s, size_t s_len, char *digest, #define CST_NO_CHECK_OBJTYPE (1<<0) struct directory_token_t; -int check_signature_token(const char *digest, - ssize_t digest_len, - struct directory_token_t *tok, - crypto_pk_t *pkey, - int flags, - const char *doctype); +MOCK_DECL(int, check_signature_token,(const char *digest, + ssize_t digest_len, + struct directory_token_t *tok, + crypto_pk_t *pkey, + int flags, + const char *doctype)); int router_get_hash_impl_helper(const char *s, size_t s_len, const char *start_str, @@ -43,6 +43,6 @@ MOCK_DECL(STATIC int, signed_digest_equals, MOCK_DECL(STATIC int, router_compute_hash_final,(char *digest, const char *start, size_t len, digest_algorithm_t alg)); -#endif +#endif /* defined(SIGCOMMON_PRIVATE) */ #endif /* !defined(TOR_SIGCOMMON_H) */ diff --git a/src/feature/dirparse/signing.c b/src/feature/dirparse/signing.c index 3ab40c3807..e420e5b6b9 100644 --- a/src/feature/dirparse/signing.c +++ b/src/feature/dirparse/signing.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 */ /** diff --git a/src/feature/dirparse/signing.h b/src/feature/dirparse/signing.h index 2e3699baf8..7ca34bb14a 100644 --- a/src/feature/dirparse/signing.h +++ b/src/feature/dirparse/signing.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 */ /** @@ -20,4 +20,4 @@ int router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest, size_t digest_len, crypto_pk_t *private_key); -#endif +#endif /* !defined(TOR_SIGNING_H) */ diff --git a/src/feature/dirparse/unparseable.c b/src/feature/dirparse/unparseable.c index 1d623fe701..a91148a661 100644 --- a/src/feature/dirparse/unparseable.c +++ b/src/feature/dirparse/unparseable.c @@ -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 unparseable.c + * @brief Dump unparseable objects to disk. + **/ + #define UNPARSEABLE_PRIVATE #include "core/or/or.h" diff --git a/src/feature/dirparse/unparseable.h b/src/feature/dirparse/unparseable.h index 853fe8cb0f..cff91c82cc 100644 --- a/src/feature/dirparse/unparseable.h +++ b/src/feature/dirparse/unparseable.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 */ /** @@ -26,7 +26,7 @@ void dump_desc_init(void); log_debug(LD_MM, "Area for %s has %lu allocated; using %lu.", \ name, (unsigned long)alloc, (unsigned long)used); \ STMT_END -#else /* !(defined(DEBUG_AREA_ALLOC)) */ +#else /* !defined(DEBUG_AREA_ALLOC) */ #define DUMP_AREA(a,name) STMT_NIL #endif /* defined(DEBUG_AREA_ALLOC) */ @@ -51,6 +51,6 @@ EXTERN(struct smartlist_t *, descs_dumped) MOCK_DECL(STATIC dumped_desc_t *, dump_desc_populate_one_file, (const char *dirname, const char *f)); STATIC void dump_desc_populate_fifo_from_directory(const char *dirname); -#endif +#endif /* defined(UNPARSEABLE_PRIVATE) */ #endif /* !defined(TOR_UNPARSEABLE_H) */ |