summaryrefslogtreecommitdiff
path: root/src/or/routerparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/routerparse.c')
-rw-r--r--src/or/routerparse.c441
1 files changed, 416 insertions, 25 deletions
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 2d181e123f..40ce330f94 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -24,8 +24,11 @@
#include "microdesc.h"
#include "networkstatus.h"
#include "rephist.h"
+#include "routerkeys.h"
#include "routerparse.h"
#include "entrynodes.h"
+#include "torcert.h"
+
#undef log
#include <math.h>
@@ -69,6 +72,7 @@ typedef enum {
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
K_OR_ADDRESS,
+ K_ID,
K_P,
K_P6,
K_R,
@@ -83,6 +87,11 @@ typedef enum {
K_HIDDEN_SERVICE_DIR,
K_ALLOW_SINGLE_HOP_EXITS,
K_IPV6_POLICY,
+ K_ROUTER_SIG_ED25519,
+ K_IDENTITY_ED25519,
+ K_MASTER_KEY_ED25519,
+ K_ONION_KEY_CROSSCERT,
+ K_NTOR_ONION_KEY_CROSSCERT,
K_DIRREQ_END,
K_DIRREQ_V2_IPS,
@@ -293,6 +302,13 @@ static token_rule_t routerdesc_token_table[] = {
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,
+ EQ(1), NEED_OBJ ),
+
T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ),
T01("family", K_FAMILY, ARGS, NO_OBJ ),
@@ -310,6 +326,8 @@ static token_rule_t routerdesc_token_table[] = {
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 ),
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 ),
@@ -353,6 +371,7 @@ static token_rule_t rtrstatus_token_table[] = {
T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
T01("w", K_W, ARGS, NO_OBJ ),
T0N("m", K_M, CONCAT_ARGS, NO_OBJ ),
+ T0N("id", K_ID, GE(2), NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
END_OF_TABLE
};
@@ -490,6 +509,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = {
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("p", K_P, CONCAT_ARGS, NO_OBJ ),
@@ -506,6 +526,10 @@ static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
unsigned fmt_flags);
static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
+static int router_get_hash_impl_helper(const char *s, size_t s_len,
+ const char *start_str,
+ const char *end_str, char end_c,
+ const char **start_out, const char **end_out);
static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
const char *start_str, const char *end_str,
char end_char,
@@ -637,7 +661,7 @@ router_get_extrainfo_hash(const char *s, size_t s_len, char *digest)
char *
router_get_dirobj_signature(const char *digest,
size_t digest_len,
- crypto_pk_t *private_key)
+ const crypto_pk_t *private_key)
{
char *signature;
size_t i, keysize;
@@ -664,7 +688,8 @@ router_get_dirobj_signature(const char *digest,
goto truncated;
i = strlen(buf);
- if (base64_encode(buf+i, buf_len-i, signature, siglen) < 0) {
+ if (base64_encode(buf+i, buf_len-i, signature, siglen,
+ BASE64_ENCODE_MULTILINE) < 0) {
log_warn(LD_BUG,"couldn't base64-encode signature");
goto err;
}
@@ -857,8 +882,8 @@ check_signature_token(const char *digest,
tor_free(signed_digest);
return -1;
}
-// log_debug(LD_DIR,"Signed %s hash starts %s", doctype,
-// hex_str(signed_digest,4));
+ // log_debug(LD_DIR,"Signed %s hash starts %s", doctype,
+ // hex_str(signed_digest,4));
if (tor_memneq(digest, signed_digest, digest_len)) {
log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype);
tor_free(signed_digest);
@@ -1105,6 +1130,7 @@ router_parse_entry_from_string(const char *s, const char *end,
size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0;
int ok = 1;
memarea_t *area = NULL;
+ tor_cert_t *ntor_cc_cert = NULL;
/* Do not set this to '1' until we have parsed everything that we intend to
* parse that's covered by the hash. */
int can_dl_again = 0;
@@ -1177,9 +1203,11 @@ router_parse_entry_from_string(const char *s, const char *end,
}
tok = find_by_keyword(tokens, K_ROUTER);
+ const int router_token_pos = smartlist_pos(tokens, tok);
tor_assert(tok->n_args >= 5);
router = tor_malloc_zero(sizeof(routerinfo_t));
+ router->cert_expiration_time = TIME_MAX;
router->cache_info.routerlist_index = -1;
router->cache_info.annotations_len = s-start_of_annotations + prepend_len;
router->cache_info.signed_descriptor_len = end-s;
@@ -1310,6 +1338,172 @@ router_parse_entry_from_string(const char *s, const char *end,
log_warn(LD_DIR, "Couldn't calculate key digest"); goto err;
}
+ {
+ directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok,
+ *master_key_tok;
+ ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519);
+ ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519);
+ master_key_tok = find_opt_by_keyword(tokens, K_MASTER_KEY_ED25519);
+ cc_tap_tok = find_opt_by_keyword(tokens, K_ONION_KEY_CROSSCERT);
+ cc_ntor_tok = find_opt_by_keyword(tokens, K_NTOR_ONION_KEY_CROSSCERT);
+ int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok +
+ !!cc_tap_tok + !!cc_ntor_tok;
+ if ((n_ed_toks != 0 && n_ed_toks != 4) ||
+ (n_ed_toks == 4 && !router->onion_curve25519_pkey)) {
+ log_warn(LD_DIR, "Router descriptor with only partial ed25519/"
+ "cross-certification support");
+ goto err;
+ }
+ if (master_key_tok && !ed_sig_tok) {
+ log_warn(LD_DIR, "Router descriptor has ed25519 master key but no "
+ "certificate");
+ goto err;
+ }
+ if (ed_sig_tok) {
+ tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok);
+ const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok);
+ if (ed_cert_token_pos == -1 || router_token_pos == -1 ||
+ (ed_cert_token_pos != router_token_pos + 1 &&
+ ed_cert_token_pos != router_token_pos - 1)) {
+ log_warn(LD_DIR, "Ed25519 certificate in wrong position");
+ goto err;
+ }
+ if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) {
+ log_warn(LD_DIR, "Ed25519 signature in wrong position");
+ goto err;
+ }
+ if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) {
+ log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor");
+ 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");
+ goto err;
+ }
+ if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) {
+ log_warn(LD_DIR, "Wrong object type on onion-key-crosscert "
+ "in decriptor");
+ goto err;
+ }
+ if (strcmp(cc_ntor_tok->args[0], "0") &&
+ strcmp(cc_ntor_tok->args[0], "1")) {
+ log_warn(LD_DIR, "Bad sign bit on ntor-onion-key-crosscert");
+ goto err;
+ }
+ int ntor_cc_sign_bit = !strcmp(cc_ntor_tok->args[0], "1");
+
+ uint8_t d256[DIGEST256_LEN];
+ const char *signed_start, *signed_end;
+ tor_cert_t *cert = tor_cert_parse(
+ (const uint8_t*)ed_cert_tok->object_body,
+ ed_cert_tok->object_size);
+ if (! cert) {
+ log_warn(LD_DIR, "Couldn't parse ed25519 cert");
+ goto err;
+ }
+ router->signing_key_cert = cert; /* makes sure it gets freed. */
+
+ if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
+ ! cert->signing_key_included) {
+ log_warn(LD_DIR, "Invalid form for ed25519 cert");
+ goto err;
+ }
+
+ if (master_key_tok) {
+ /* This token is optional, but if it's present, it must match
+ * the signature in the signing cert, or supplant it. */
+ tor_assert(master_key_tok->n_args >= 1);
+ ed25519_public_key_t pkey;
+ if (ed25519_public_from_base64(&pkey, master_key_tok->args[0])<0) {
+ log_warn(LD_DIR, "Can't parse ed25519 master key");
+ goto err;
+ }
+
+ if (fast_memneq(&cert->signing_key.pubkey,
+ pkey.pubkey, ED25519_PUBKEY_LEN)) {
+ log_warn(LD_DIR, "Ed25519 master key does not match "
+ "key in certificate");
+ goto err;
+ }
+ }
+ ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body,
+ cc_ntor_tok->object_size);
+ if (!ntor_cc_cert) {
+ log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert");
+ goto err;
+ }
+ if (ntor_cc_cert->cert_type != CERT_TYPE_ONION_ID ||
+ ! ed25519_pubkey_eq(&ntor_cc_cert->signed_key, &cert->signing_key)) {
+ log_warn(LD_DIR, "Invalid contents for ntor-onion-key-crosscert cert");
+ goto err;
+ }
+
+ ed25519_public_key_t ntor_cc_pk;
+ if (ed25519_public_key_from_curve25519_public_key(&ntor_cc_pk,
+ router->onion_curve25519_pkey,
+ ntor_cc_sign_bit)<0) {
+ log_warn(LD_DIR, "Error converting onion key to ed25519");
+ goto err;
+ }
+
+ if (router_get_hash_impl_helper(s, end-s, "router ",
+ "\nrouter-sig-ed25519",
+ ' ', &signed_start, &signed_end) < 0) {
+ log_warn(LD_DIR, "Can't find ed25519-signed portion of descriptor");
+ goto err;
+ }
+ crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256);
+ crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX,
+ strlen(ED_DESC_SIGNATURE_PREFIX));
+ crypto_digest_add_bytes(d, signed_start, signed_end-signed_start);
+ crypto_digest_get_digest(d, (char*)d256, sizeof(d256));
+ crypto_digest_free(d);
+
+ ed25519_checkable_t check[3];
+ int check_ok[3];
+ if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) {
+ log_err(LD_BUG, "Couldn't create 'checkable' for cert.");
+ goto err;
+ }
+ if (tor_cert_get_checkable_sig(&check[1],
+ ntor_cc_cert, &ntor_cc_pk) < 0) {
+ log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert.");
+ goto err;
+ }
+
+ if (ed25519_signature_from_base64(&check[2].signature,
+ ed_sig_tok->args[0])<0) {
+ log_warn(LD_DIR, "Couldn't decode ed25519 signature");
+ goto err;
+ }
+ check[2].pubkey = &cert->signed_key;
+ check[2].msg = d256;
+ check[2].len = DIGEST256_LEN;
+
+ if (ed25519_checksig_batch(check_ok, check, 3) < 0) {
+ log_warn(LD_DIR, "Incorrect ed25519 signature(s)");
+ goto err;
+ }
+
+ if (check_tap_onion_key_crosscert(
+ (const uint8_t*)cc_tap_tok->object_body,
+ (int)cc_tap_tok->object_size,
+ router->onion_pkey,
+ &cert->signing_key,
+ (const uint8_t*)router->cache_info.identity_digest)<0) {
+ log_warn(LD_DIR, "Incorrect TAP cross-verification");
+ goto err;
+ }
+
+ /* We check this before adding it to the routerlist. */
+ if (cert->valid_until < ntor_cc_cert->valid_until)
+ router->cert_expiration_time = cert->valid_until;
+ else
+ router->cert_expiration_time = ntor_cc_cert->valid_until;
+ }
+ }
+
if ((tok = find_opt_by_keyword(tokens, K_FINGERPRINT))) {
/* If there's a fingerprint line, it must match the identity digest. */
char d[DIGEST_LEN];
@@ -1401,6 +1595,14 @@ router_parse_entry_from_string(const char *s, const char *end,
} else {
log_warn(LD_DIR, "Invalid extra info digest %s", escaped(tok->args[0]));
}
+
+ if (tok->n_args >= 2) {
+ if (digest256_from_base64(router->extra_info_digest256, tok->args[1])
+ < 0) {
+ log_warn(LD_DIR, "Invalid extra info digest256 %s",
+ escaped(tok->args[1]));
+ }
+ }
}
if (find_opt_by_keyword(tokens, K_HIDDEN_SERVICE_DIR)) {
@@ -1436,6 +1638,7 @@ router_parse_entry_from_string(const char *s, const char *end,
routerinfo_free(router);
router = NULL;
done:
+ tor_cert_free(ntor_cc_cert);
if (tokens) {
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
@@ -1502,6 +1705,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
goto err;
}
+ /* XXXX Accept this in position 1 too, and ed identity in position 0. */
tok = smartlist_get(tokens,0);
if (tok->tp != K_EXTRA_INFO) {
log_warn(LD_DIR,"Entry does not start with \"extra-info\"");
@@ -1514,6 +1718,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s,end-s);
extrainfo->cache_info.signed_descriptor_len = end-s;
memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
+ crypto_digest256((char*)extrainfo->digest256, s, end-s, DIGEST_SHA256);
tor_assert(tok->n_args >= 2);
if (!is_legal_nickname(tok->args[0])) {
@@ -1536,6 +1741,87 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
goto err;
}
+ {
+ directory_token_t *ed_sig_tok, *ed_cert_tok;
+ ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519);
+ ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519);
+ int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok;
+ if (n_ed_toks != 0 && n_ed_toks != 2) {
+ log_warn(LD_DIR, "Router descriptor with only partial ed25519/"
+ "cross-certification support");
+ goto err;
+ }
+ if (ed_sig_tok) {
+ tor_assert(ed_cert_tok);
+ const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok);
+ if (ed_cert_token_pos != 1) {
+ /* Accept this in position 0 XXXX */
+ log_warn(LD_DIR, "Ed25519 certificate in wrong position");
+ goto err;
+ }
+ if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) {
+ log_warn(LD_DIR, "Ed25519 signature in wrong position");
+ goto err;
+ }
+ if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) {
+ log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor");
+ goto err;
+ }
+
+ uint8_t d256[DIGEST256_LEN];
+ const char *signed_start, *signed_end;
+ tor_cert_t *cert = tor_cert_parse(
+ (const uint8_t*)ed_cert_tok->object_body,
+ ed_cert_tok->object_size);
+ if (! cert) {
+ log_warn(LD_DIR, "Couldn't parse ed25519 cert");
+ goto err;
+ }
+ extrainfo->signing_key_cert = cert; /* makes sure it gets freed. */
+ if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
+ ! cert->signing_key_included) {
+ log_warn(LD_DIR, "Invalid form for ed25519 cert");
+ goto err;
+ }
+
+ if (router_get_hash_impl_helper(s, end-s, "extra-info ",
+ "\nrouter-sig-ed25519",
+ ' ', &signed_start, &signed_end) < 0) {
+ log_warn(LD_DIR, "Can't find ed25519-signed portion of extrainfo");
+ goto err;
+ }
+ crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256);
+ crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX,
+ strlen(ED_DESC_SIGNATURE_PREFIX));
+ crypto_digest_add_bytes(d, signed_start, signed_end-signed_start);
+ crypto_digest_get_digest(d, (char*)d256, sizeof(d256));
+ crypto_digest_free(d);
+
+ ed25519_checkable_t check[2];
+ int check_ok[2];
+ if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) {
+ log_err(LD_BUG, "Couldn't create 'checkable' for cert.");
+ goto err;
+ }
+
+ if (ed25519_signature_from_base64(&check[1].signature,
+ ed_sig_tok->args[0])<0) {
+ log_warn(LD_DIR, "Couldn't decode ed25519 signature");
+ goto err;
+ }
+ check[1].pubkey = &cert->signed_key;
+ check[1].msg = d256;
+ check[1].len = DIGEST256_LEN;
+
+ if (ed25519_checksig_batch(check_ok, check, 2) < 0) {
+ log_warn(LD_DIR, "Incorrect ed25519 signature(s)");
+ goto err;
+ }
+ /* We don't check the certificate expiration time: checking that it
+ * matches the cert in the router descriptor is adequate. */
+ }
+ }
+
/* We've checked everything that's covered by the hash. */
can_dl_again = 1;
@@ -2015,10 +2301,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
tor_assert(tok->n_args == 1);
rs->version_known = 1;
if (strcmpstart(tok->args[0], "Tor ")) {
- rs->version_supports_microdesc_cache = 1;
} else {
- rs->version_supports_microdesc_cache =
- tor_version_supports_microdescriptors(tok->args[0]);
rs->version_supports_extend2_cells =
tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha");
}
@@ -2091,6 +2374,18 @@ routerstatus_parse_entry_from_string(memarea_t *area,
line->microdesc_hash_line = tor_strdup(t->args[0]);
vote_rs->microdesc = line;
}
+ if (t->tp == K_ID) {
+ tor_assert(t->n_args >= 2);
+ if (!strcmp(t->args[0], "ed25519")) {
+ vote_rs->has_ed25519_listing = 1;
+ if (strcmp(t->args[1], "none") &&
+ digest256_from_base64((char*)vote_rs->ed25519_id,
+ t->args[1])<0) {
+ log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote");
+ goto err;
+ }
+ }
+ }
} SMARTLIST_FOREACH_END(t);
} else if (flav == FLAV_MICRODESC) {
tok = find_opt_by_keyword(tokens, K_M);
@@ -2915,6 +3210,23 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
goto err;
}
}
+ if (ns_type != NS_TYPE_CONSENSUS) {
+ digest256map_t *ed_id_map = digest256map_new();
+ 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))
+ continue;
+ if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
+ log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
+ "unique");
+ digest256map_free(ed_id_map, NULL);
+ goto err;
+ }
+ digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1);
+ } SMARTLIST_FOREACH_END(vrs);
+ digest256map_free(ed_id_map, NULL);
+ }
/* Parse footer; check signature. */
footer_tokens = smartlist_new();
@@ -3354,22 +3666,38 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action.
*
+ * Returns NULL on policy errors.
+ *
+ * If there is a policy error, malformed_list is set to true if the entire
+ * policy list should be discarded. Otherwise, it is set to false, and only
+ * this item should be ignored - the rest of the policy list can continue to
+ * be processed and used.
+ *
* The addr_policy_t returned by this function can have its address set to
* AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair
* of AF_INET and AF_INET6 items.
*/
MOCK_IMPL(addr_policy_t *,
-router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
+router_parse_addr_policy_item_from_string,(const char *s, int assume_action,
+ int *malformed_list))
{
directory_token_t *tok = NULL;
const char *cp, *eos;
- /* Longest possible policy is "accept ffff:ffff:..255/ffff:...255:0-65535".
+ /* Longest possible policy is
+ * "accept6 ffff:ffff:..255/128:10000-65535",
+ * which contains a max-length IPv6 address, plus 24 characters.
* But note that there can be an arbitrary amount of space between the
- * accept and the address:mask/port element. */
+ * accept and the address:mask/port element.
+ * We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one
+ * IPv6 address. But making the buffer shorter might cause valid long lines,
+ * which parsed in previous versions, to fail to parse in new versions.
+ * (These lines would have to have excessive amounts of whitespace.) */
char line[TOR_ADDR_BUF_LEN*2 + 32];
addr_policy_t *r;
memarea_t *area = NULL;
+ tor_assert(malformed_list);
+
s = eat_whitespace(s);
if ((*s == '*' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
if (tor_snprintf(line, sizeof(line), "%s %s",
@@ -3396,9 +3724,34 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
goto err;
}
+ /* Use the extended interpretation of accept/reject *,
+ * expanding it into an IPv4 wildcard and an IPv6 wildcard.
+ * Also permit *4 and *6 for IPv4 and IPv6 only wildcards. */
r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
+ if (!r) {
+ goto err;
+ }
+
+ /* Ensure that accept6/reject6 fields are followed by IPv6 addresses.
+ * AF_UNSPEC addresses are only permitted on the accept/reject field type.
+ * Unlike descriptors, torrcs exit policy accept/reject can be followed by
+ * either an IPv4 or IPv6 address. */
+ if ((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
+ tor_addr_family(&r->addr) != AF_INET6) {
+ /* This is a non-fatal error, just ignore this one entry. */
+ *malformed_list = 0;
+ log_warn(LD_DIR, "IPv4 address '%s' with accept6/reject6 field type in "
+ "exit policy. Ignoring, but continuing to parse rules. (Use "
+ "accept/reject with IPv4 addresses.)",
+ tok->n_args == 1 ? tok->args[0] : "");
+ addr_policy_free(r);
+ r = NULL;
+ goto done;
+ }
+
goto done;
err:
+ *malformed_list = 1;
r = NULL;
done:
token_clear(tok);
@@ -3415,19 +3768,27 @@ static int
router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
{
addr_policy_t *newe;
+ /* Use the standard interpretation of accept/reject *, an IPv4 wildcard. */
newe = router_parse_addr_policy(tok, 0);
if (!newe)
return -1;
if (! router->exit_policy)
router->exit_policy = smartlist_new();
+ /* Ensure that in descriptors, accept/reject fields are followed by
+ * IPv4 addresses, and accept6/reject6 fields are followed by
+ * IPv6 addresses. Unlike torrcs, descriptor exit policies do not permit
+ * accept/reject followed by IPv6. */
if (((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
tor_addr_family(&newe->addr) == AF_INET)
||
((tok->tp == K_ACCEPT || tok->tp == K_REJECT) &&
tor_addr_family(&newe->addr) == AF_INET6)) {
+ /* There's nothing the user can do about other relays' descriptors,
+ * so we don't provide usage advice here. */
log_warn(LD_DIR, "Mismatch between field type and address type in exit "
- "policy");
+ "policy '%s'. Discarding entire router descriptor.",
+ tok->n_args == 1 ? tok->args[0] : "");
addr_policy_free(newe);
return -1;
}
@@ -3437,8 +3798,11 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
return 0;
}
-/** Given a K_ACCEPT or K_REJECT token and a router, create and return
- * a new exit_policy_t corresponding to the token. */
+/** Given a K_ACCEPT[6] or K_REJECT[6] token and a router, create and return
+ * a new exit_policy_t corresponding to the token. If TAPMP_EXTENDED_STAR
+ * is set in fmt_flags, K_ACCEPT6 and K_REJECT6 tokens followed by *
+ * expand to IPv6-only policies, otherwise they expand to IPv4 and IPv6
+ * policies */
static addr_policy_t *
router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
{
@@ -3462,6 +3826,13 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
else
newe.policy_type = ADDR_POLICY_ACCEPT;
+ /* accept6/reject6 * produces an IPv6 wildcard address only.
+ * (accept/reject * produces rules for IPv4 and IPv6 wildcard addresses.) */
+ if ((fmt_flags & TAPMP_EXTENDED_STAR)
+ && (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6)) {
+ fmt_flags |= TAPMP_STAR_IPV6_ONLY;
+ }
+
if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
&newe.prt_min, &newe.prt_max) < 0) {
log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
@@ -3471,9 +3842,12 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
return addr_policy_get_canonical_entry(&newe);
}
-/** Parse an exit policy line of the format "accept/reject private:...".
+/** Parse an exit policy line of the format "accept[6]/reject[6] private:...".
* This didn't exist until Tor 0.1.1.15, so nobody should generate it in
* router descriptors until earlier versions are obsolete.
+ *
+ * accept/reject and accept6/reject6 private all produce rules for both
+ * IPv4 and IPv6 addresses.
*/
static addr_policy_t *
router_parse_addr_policy_private(directory_token_t *tok)
@@ -3503,6 +3877,13 @@ router_parse_addr_policy_private(directory_token_t *tok)
result.prt_min = port_min;
result.prt_max = port_max;
+ if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) {
+ log_warn(LD_GENERAL,
+ "'%s' expands into rules which apply to all private IPv4 and "
+ "IPv6 addresses. (Use accept/reject private:* for IPv4 and "
+ "IPv6.)", tok->n_args == 1 ? tok->args[0] : "");
+ }
+
return addr_policy_get_canonical_entry(&result);
}
@@ -4184,7 +4565,6 @@ microdescs_parse_from_string(const char *s, const char *eos,
}
}
-
if (tokenize_string(area, s, start_of_next_microdesc, tokens,
microdesc_token_table, flags)) {
log_warn(LD_DIR, "Unparseable microdescriptor");
@@ -4218,6 +4598,26 @@ microdescs_parse_from_string(const char *s, const char *eos,
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");
+ 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");
+ 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) {
@@ -4271,14 +4671,6 @@ microdescs_parse_from_string(const char *s, const char *eos,
return result;
}
-/** Return true iff this Tor version can answer directory questions
- * about microdescriptors. */
-int
-tor_version_supports_microdescriptors(const char *platform)
-{
- return tor_version_as_new_as(platform, "0.2.3.1-alpha");
-}
-
/** Parse the Tor version of the platform string <b>platform</b>,
* and compare it to the version in <b>cutoff</b>. Return 1 if
* the router is at least as new as the cutoff, else return 0.
@@ -4602,8 +4994,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
tor_assert(tok == smartlist_get(tokens, 0));
tor_assert(tok->n_args == 1);
- if (strlen(tok->args[0]) != REND_DESC_ID_V2_LEN_BASE32 ||
- strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
+ if (!rend_valid_descriptor_id(tok->args[0])) {
log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
goto err;
}