mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-09-22 22:14:58 +02:00
208 lines
6.7 KiB
C
208 lines
6.7 KiB
C
/* 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;
|
|
}
|