Split the authority-cert and signature/hash code from routerparse
[tor.git] / src / feature / dirparse / authcert_parse.c
blob2ba46bb8fa8335752b5ecccfffbee46dce0d684e
1 /* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2018, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
7 #include "core/or/or.h"
8 #include "feature/dirparse/authcert_parse.h"
9 #include "feature/dirparse/parsecommon.h"
10 #include "feature/dirparse/sigcommon.h"
11 #include "feature/dirparse/unparseable.h"
12 #include "feature/nodelist/authcert.h"
13 #include "lib/memarea/memarea.h"
15 #include "feature/nodelist/authority_cert_st.h"
17 /** List of tokens recognized in V3 authority certificates. */
18 static token_rule_t dir_key_certificate_table[] = {
19 #include "feature/dirparse/authcert_members.i"
20 T1("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
21 END_OF_TABLE
24 /** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to
25 * the first character after the certificate. */
26 authority_cert_t *
27 authority_cert_parse_from_string(const char *s, const char **end_of_string)
29 /** Reject any certificate at least this big; it is probably an overflow, an
30 * attack, a bug, or some other nonsense. */
31 #define MAX_CERT_SIZE (128*1024)
33 authority_cert_t *cert = NULL, *old_cert;
34 smartlist_t *tokens = NULL;
35 char digest[DIGEST_LEN];
36 directory_token_t *tok;
37 char fp_declared[DIGEST_LEN];
38 char *eos;
39 size_t len;
40 int found;
41 memarea_t *area = NULL;
42 const char *s_dup = s;
44 s = eat_whitespace(s);
45 eos = strstr(s, "\ndir-key-certification");
46 if (! eos) {
47 log_warn(LD_DIR, "No signature found on key certificate");
48 return NULL;
50 eos = strstr(eos, "\n-----END SIGNATURE-----\n");
51 if (! eos) {
52 log_warn(LD_DIR, "No end-of-signature found on key certificate");
53 return NULL;
55 eos = strchr(eos+2, '\n');
56 tor_assert(eos);
57 ++eos;
58 len = eos - s;
60 if (len > MAX_CERT_SIZE) {
61 log_warn(LD_DIR, "Certificate is far too big (at %lu bytes long); "
62 "rejecting", (unsigned long)len);
63 return NULL;
66 tokens = smartlist_new();
67 area = memarea_new();
68 if (tokenize_string(area,s, eos, tokens, dir_key_certificate_table, 0) < 0) {
69 log_warn(LD_DIR, "Error tokenizing key certificate");
70 goto err;
72 if (router_get_hash_impl(s, strlen(s), digest, "dir-key-certificate-version",
73 "\ndir-key-certification", '\n', DIGEST_SHA1) < 0)
74 goto err;
75 tok = smartlist_get(tokens, 0);
76 if (tok->tp != K_DIR_KEY_CERTIFICATE_VERSION || strcmp(tok->args[0], "3")) {
77 log_warn(LD_DIR,
78 "Key certificate does not begin with a recognized version (3).");
79 goto err;
82 cert = tor_malloc_zero(sizeof(authority_cert_t));
83 memcpy(cert->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
85 tok = find_by_keyword(tokens, K_DIR_SIGNING_KEY);
86 tor_assert(tok->key);
87 cert->signing_key = tok->key;
88 tok->key = NULL;
89 if (crypto_pk_get_digest(cert->signing_key, cert->signing_key_digest))
90 goto err;
92 tok = find_by_keyword(tokens, K_DIR_IDENTITY_KEY);
93 tor_assert(tok->key);
94 cert->identity_key = tok->key;
95 tok->key = NULL;
97 tok = find_by_keyword(tokens, K_FINGERPRINT);
98 tor_assert(tok->n_args);
99 if (base16_decode(fp_declared, DIGEST_LEN, tok->args[0],
100 strlen(tok->args[0])) != DIGEST_LEN) {
101 log_warn(LD_DIR, "Couldn't decode key certificate fingerprint %s",
102 escaped(tok->args[0]));
103 goto err;
106 if (crypto_pk_get_digest(cert->identity_key,
107 cert->cache_info.identity_digest))
108 goto err;
110 if (tor_memneq(cert->cache_info.identity_digest, fp_declared, DIGEST_LEN)) {
111 log_warn(LD_DIR, "Digest of certificate key didn't match declared "
112 "fingerprint");
113 goto err;
116 tok = find_opt_by_keyword(tokens, K_DIR_ADDRESS);
117 if (tok) {
118 struct in_addr in;
119 char *address = NULL;
120 tor_assert(tok->n_args);
121 /* XXX++ use some tor_addr parse function below instead. -RD */
122 if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
123 &cert->dir_port) < 0 ||
124 tor_inet_aton(address, &in) == 0) {
125 log_warn(LD_DIR, "Couldn't parse dir-address in certificate");
126 tor_free(address);
127 goto err;
129 cert->addr = ntohl(in.s_addr);
130 tor_free(address);
133 tok = find_by_keyword(tokens, K_DIR_KEY_PUBLISHED);
134 if (parse_iso_time(tok->args[0], &cert->cache_info.published_on) < 0) {
135 goto err;
137 tok = find_by_keyword(tokens, K_DIR_KEY_EXPIRES);
138 if (parse_iso_time(tok->args[0], &cert->expires) < 0) {
139 goto err;
142 tok = smartlist_get(tokens, smartlist_len(tokens)-1);
143 if (tok->tp != K_DIR_KEY_CERTIFICATION) {
144 log_warn(LD_DIR, "Certificate didn't end with dir-key-certification.");
145 goto err;
148 /* If we already have this cert, don't bother checking the signature. */
149 old_cert = authority_cert_get_by_digests(
150 cert->cache_info.identity_digest,
151 cert->signing_key_digest);
152 found = 0;
153 if (old_cert) {
154 /* XXXX We could just compare signed_descriptor_digest, but that wouldn't
155 * buy us much. */
156 if (old_cert->cache_info.signed_descriptor_len == len &&
157 old_cert->cache_info.signed_descriptor_body &&
158 tor_memeq(s, old_cert->cache_info.signed_descriptor_body, len)) {
159 log_debug(LD_DIR, "We already checked the signature on this "
160 "certificate; no need to do so again.");
161 found = 1;
164 if (!found) {
165 if (check_signature_token(digest, DIGEST_LEN, tok, cert->identity_key, 0,
166 "key certificate")) {
167 goto err;
170 tok = find_by_keyword(tokens, K_DIR_KEY_CROSSCERT);
171 if (check_signature_token(cert->cache_info.identity_digest,
172 DIGEST_LEN,
173 tok,
174 cert->signing_key,
175 CST_NO_CHECK_OBJTYPE,
176 "key cross-certification")) {
177 goto err;
181 cert->cache_info.signed_descriptor_len = len;
182 cert->cache_info.signed_descriptor_body = tor_malloc(len+1);
183 memcpy(cert->cache_info.signed_descriptor_body, s, len);
184 cert->cache_info.signed_descriptor_body[len] = 0;
185 cert->cache_info.saved_location = SAVED_NOWHERE;
187 if (end_of_string) {
188 *end_of_string = eat_whitespace(eos);
190 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
191 smartlist_free(tokens);
192 if (area) {
193 DUMP_AREA(area, "authority cert");
194 memarea_drop_all(area);
196 return cert;
197 err:
198 dump_desc(s_dup, "authority cert");
199 authority_cert_free(cert);
200 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
201 smartlist_free(tokens);
202 if (area) {
203 DUMP_AREA(area, "authority cert");
204 memarea_drop_all(area);
206 return NULL;