aboutsummaryrefslogtreecommitdiff
path: root/src/feature/dirparse/microdesc_parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/dirparse/microdesc_parse.c')
-rw-r--r--src/feature/dirparse/microdesc_parse.c320
1 files changed, 203 insertions, 117 deletions
diff --git a/src/feature/dirparse/microdesc_parse.c b/src/feature/dirparse/microdesc_parse.c
index 5a75af3994..9231080aaa 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 ),
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;
}