diff options
Diffstat (limited to 'src/feature/dirparse/authcert_parse.c')
-rw-r--r-- | src/feature/dirparse/authcert_parse.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/src/feature/dirparse/authcert_parse.c b/src/feature/dirparse/authcert_parse.c new file mode 100644 index 0000000000..2ba46bb8fa --- /dev/null +++ b/src/feature/dirparse/authcert_parse.c @@ -0,0 +1,207 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "core/or/or.h" +#include "feature/dirparse/authcert_parse.h" +#include "feature/dirparse/parsecommon.h" +#include "feature/dirparse/sigcommon.h" +#include "feature/dirparse/unparseable.h" +#include "feature/nodelist/authcert.h" +#include "lib/memarea/memarea.h" + +#include "feature/nodelist/authority_cert_st.h" + +/** List of tokens recognized in V3 authority certificates. */ +static token_rule_t dir_key_certificate_table[] = { +#include "feature/dirparse/authcert_members.i" + T1("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ), + END_OF_TABLE +}; + +/** 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) +{ + /** Reject any certificate at least this big; it is probably an overflow, an + * attack, a bug, or some other nonsense. */ +#define MAX_CERT_SIZE (128*1024) + + authority_cert_t *cert = NULL, *old_cert; + smartlist_t *tokens = NULL; + char digest[DIGEST_LEN]; + directory_token_t *tok; + char fp_declared[DIGEST_LEN]; + char *eos; + size_t len; + int found; + memarea_t *area = NULL; + const char *s_dup = s; + + s = eat_whitespace(s); + eos = strstr(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"); + if (! eos) { + log_warn(LD_DIR, "No end-of-signature found on key certificate"); + return NULL; + } + eos = strchr(eos+2, '\n'); + tor_assert(eos); + ++eos; + len = eos - s; + + if (len > MAX_CERT_SIZE) { + log_warn(LD_DIR, "Certificate is far too big (at %lu bytes long); " + "rejecting", (unsigned long)len); + return NULL; + } + + tokens = smartlist_new(); + area = memarea_new(); + if (tokenize_string(area,s, eos, tokens, dir_key_certificate_table, 0) < 0) { + log_warn(LD_DIR, "Error tokenizing key certificate"); + goto err; + } + if (router_get_hash_impl(s, strlen(s), digest, "dir-key-certificate-version", + "\ndir-key-certification", '\n', DIGEST_SHA1) < 0) + goto err; + tok = smartlist_get(tokens, 0); + if (tok->tp != K_DIR_KEY_CERTIFICATE_VERSION || strcmp(tok->args[0], "3")) { + log_warn(LD_DIR, + "Key certificate does not begin with a recognized version (3)."); + goto err; + } + + cert = tor_malloc_zero(sizeof(authority_cert_t)); + memcpy(cert->cache_info.signed_descriptor_digest, digest, DIGEST_LEN); + + tok = find_by_keyword(tokens, K_DIR_SIGNING_KEY); + tor_assert(tok->key); + cert->signing_key = tok->key; + tok->key = NULL; + if (crypto_pk_get_digest(cert->signing_key, cert->signing_key_digest)) + goto err; + + tok = find_by_keyword(tokens, K_DIR_IDENTITY_KEY); + tor_assert(tok->key); + cert->identity_key = tok->key; + tok->key = NULL; + + tok = find_by_keyword(tokens, K_FINGERPRINT); + tor_assert(tok->n_args); + if (base16_decode(fp_declared, DIGEST_LEN, tok->args[0], + strlen(tok->args[0])) != DIGEST_LEN) { + log_warn(LD_DIR, "Couldn't decode key certificate fingerprint %s", + escaped(tok->args[0])); + goto err; + } + + if (crypto_pk_get_digest(cert->identity_key, + cert->cache_info.identity_digest)) + goto err; + + if (tor_memneq(cert->cache_info.identity_digest, fp_declared, DIGEST_LEN)) { + log_warn(LD_DIR, "Digest of certificate key didn't match declared " + "fingerprint"); + goto err; + } + + tok = find_opt_by_keyword(tokens, K_DIR_ADDRESS); + if (tok) { + struct in_addr in; + char *address = NULL; + 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 || + 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_free(address); + } + + tok = find_by_keyword(tokens, K_DIR_KEY_PUBLISHED); + if (parse_iso_time(tok->args[0], &cert->cache_info.published_on) < 0) { + goto err; + } + tok = find_by_keyword(tokens, K_DIR_KEY_EXPIRES); + if (parse_iso_time(tok->args[0], &cert->expires) < 0) { + goto err; + } + + tok = smartlist_get(tokens, smartlist_len(tokens)-1); + if (tok->tp != K_DIR_KEY_CERTIFICATION) { + log_warn(LD_DIR, "Certificate didn't end with dir-key-certification."); + goto err; + } + + /* If we already have this cert, don't bother checking the signature. */ + old_cert = authority_cert_get_by_digests( + cert->cache_info.identity_digest, + cert->signing_key_digest); + found = 0; + if (old_cert) { + /* XXXX We could just compare signed_descriptor_digest, but that wouldn't + * buy us much. */ + if (old_cert->cache_info.signed_descriptor_len == len && + old_cert->cache_info.signed_descriptor_body && + tor_memeq(s, old_cert->cache_info.signed_descriptor_body, len)) { + log_debug(LD_DIR, "We already checked the signature on this " + "certificate; no need to do so again."); + found = 1; + } + } + if (!found) { + if (check_signature_token(digest, DIGEST_LEN, tok, cert->identity_key, 0, + "key certificate")) { + goto err; + } + + tok = find_by_keyword(tokens, K_DIR_KEY_CROSSCERT); + if (check_signature_token(cert->cache_info.identity_digest, + DIGEST_LEN, + tok, + cert->signing_key, + CST_NO_CHECK_OBJTYPE, + "key cross-certification")) { + goto err; + } + } + + cert->cache_info.signed_descriptor_len = len; + cert->cache_info.signed_descriptor_body = tor_malloc(len+1); + memcpy(cert->cache_info.signed_descriptor_body, s, len); + cert->cache_info.signed_descriptor_body[len] = 0; + cert->cache_info.saved_location = SAVED_NOWHERE; + + if (end_of_string) { + *end_of_string = eat_whitespace(eos); + } + SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); + smartlist_free(tokens); + if (area) { + DUMP_AREA(area, "authority cert"); + memarea_drop_all(area); + } + return cert; + err: + dump_desc(s_dup, "authority cert"); + authority_cert_free(cert); + SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); + smartlist_free(tokens); + if (area) { + DUMP_AREA(area, "authority cert"); + memarea_drop_all(area); + } + return NULL; +} |