Line data Source code
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-2021, The Tor Project, Inc. */
5 : /* See LICENSE for licensing information */
6 :
7 : /**
8 : * @file authcert_parse.c
9 : * @brief Authority certificate parsing.
10 : **/
11 :
12 : #include "core/or/or.h"
13 : #include "feature/dirparse/authcert_parse.h"
14 : #include "feature/dirparse/parsecommon.h"
15 : #include "feature/dirparse/sigcommon.h"
16 : #include "feature/dirparse/unparseable.h"
17 : #include "feature/nodelist/authcert.h"
18 : #include "lib/memarea/memarea.h"
19 :
20 : #include "feature/nodelist/authority_cert_st.h"
21 : #include "feature/dirparse/authcert_members.h"
22 :
23 : /** List of tokens recognized in V3 authority certificates. */
24 : // clang-format off
25 : static token_rule_t dir_key_certificate_table[] = {
26 : AUTHCERT_MEMBERS,
27 : T1("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
28 : END_OF_TABLE
29 : };
30 : // clang-format on
31 :
32 : /** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to
33 : * the first character after the certificate. */
34 : authority_cert_t *
35 483 : authority_cert_parse_from_string(const char *s, size_t maxlen,
36 : const char **end_of_string)
37 : {
38 : /** Reject any certificate at least this big; it is probably an overflow, an
39 : * attack, a bug, or some other nonsense. */
40 : #define MAX_CERT_SIZE (128*1024)
41 :
42 483 : authority_cert_t *cert = NULL, *old_cert;
43 483 : smartlist_t *tokens = NULL;
44 483 : char digest[DIGEST_LEN];
45 483 : directory_token_t *tok;
46 483 : char fp_declared[DIGEST_LEN];
47 483 : const char *eos;
48 483 : size_t len;
49 483 : int found;
50 483 : memarea_t *area = NULL;
51 483 : const char *end_of_s = s + maxlen;
52 483 : const char *s_dup = s;
53 :
54 483 : s = eat_whitespace_eos(s, end_of_s);
55 483 : eos = tor_memstr(s, end_of_s - s, "\ndir-key-certification");
56 483 : if (! eos) {
57 2 : log_warn(LD_DIR, "No signature found on key certificate");
58 2 : return NULL;
59 : }
60 481 : eos = tor_memstr(eos, end_of_s - eos, "\n-----END SIGNATURE-----\n");
61 481 : if (! eos) {
62 3 : log_warn(LD_DIR, "No end-of-signature found on key certificate");
63 3 : return NULL;
64 : }
65 478 : eos = memchr(eos+2, '\n', end_of_s - (eos+2));
66 478 : tor_assert(eos);
67 478 : ++eos;
68 478 : len = eos - s;
69 :
70 478 : if (len > MAX_CERT_SIZE) {
71 0 : log_warn(LD_DIR, "Certificate is far too big (at %lu bytes long); "
72 : "rejecting", (unsigned long)len);
73 0 : return NULL;
74 : }
75 :
76 478 : tokens = smartlist_new();
77 478 : area = memarea_new();
78 478 : if (tokenize_string(area,s, eos, tokens, dir_key_certificate_table, 0) < 0) {
79 5 : log_warn(LD_DIR, "Error tokenizing key certificate");
80 5 : goto err;
81 : }
82 473 : if (router_get_hash_impl(s, eos - s, digest, "dir-key-certificate-version",
83 : "\ndir-key-certification", '\n', DIGEST_SHA1) < 0)
84 0 : goto err;
85 473 : tok = smartlist_get(tokens, 0);
86 473 : if (tok->tp != K_DIR_KEY_CERTIFICATE_VERSION || strcmp(tok->args[0], "3")) {
87 4 : log_warn(LD_DIR,
88 : "Key certificate does not begin with a recognized version (3).");
89 4 : goto err;
90 : }
91 :
92 469 : cert = tor_malloc_zero(sizeof(authority_cert_t));
93 469 : memcpy(cert->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
94 :
95 469 : tok = find_by_keyword(tokens, K_DIR_SIGNING_KEY);
96 469 : tor_assert(tok->key);
97 469 : cert->signing_key = tok->key;
98 469 : tok->key = NULL;
99 469 : if (crypto_pk_get_digest(cert->signing_key, cert->signing_key_digest))
100 1 : goto err;
101 :
102 468 : tok = find_by_keyword(tokens, K_DIR_IDENTITY_KEY);
103 468 : tor_assert(tok->key);
104 468 : cert->identity_key = tok->key;
105 468 : tok->key = NULL;
106 :
107 468 : tok = find_by_keyword(tokens, K_FINGERPRINT);
108 468 : tor_assert(tok->n_args);
109 468 : if (base16_decode(fp_declared, DIGEST_LEN, tok->args[0],
110 468 : strlen(tok->args[0])) != DIGEST_LEN) {
111 5 : log_warn(LD_DIR, "Couldn't decode key certificate fingerprint %s",
112 : escaped(tok->args[0]));
113 5 : goto err;
114 : }
115 :
116 463 : if (crypto_pk_get_digest(cert->identity_key,
117 463 : cert->cache_info.identity_digest))
118 1 : goto err;
119 :
120 462 : if (tor_memneq(cert->cache_info.identity_digest, fp_declared, DIGEST_LEN)) {
121 4 : log_warn(LD_DIR, "Digest of certificate key didn't match declared "
122 : "fingerprint");
123 4 : goto err;
124 : }
125 :
126 458 : tok = find_opt_by_keyword(tokens, K_DIR_ADDRESS);
127 458 : if (tok) {
128 167 : struct in_addr in;
129 167 : char *address = NULL;
130 167 : tor_assert(tok->n_args);
131 : /* XXX++ use some tor_addr parse function below instead. -RD */
132 167 : if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
133 166 : &cert->ipv4_dirport) < 0 ||
134 166 : tor_inet_aton(address, &in) == 0) {
135 9 : log_warn(LD_DIR, "Couldn't parse dir-address in certificate");
136 9 : tor_free(address);
137 9 : goto err;
138 : }
139 158 : tor_addr_from_in(&cert->ipv4_addr, &in);
140 158 : tor_free(address);
141 : }
142 :
143 449 : tok = find_by_keyword(tokens, K_DIR_KEY_PUBLISHED);
144 449 : if (parse_iso_time(tok->args[0], &cert->cache_info.published_on) < 0) {
145 2 : goto err;
146 : }
147 447 : tok = find_by_keyword(tokens, K_DIR_KEY_EXPIRES);
148 447 : if (parse_iso_time(tok->args[0], &cert->expires) < 0) {
149 1 : goto err;
150 : }
151 :
152 446 : tok = smartlist_get(tokens, smartlist_len(tokens)-1);
153 446 : if (tok->tp != K_DIR_KEY_CERTIFICATION) {
154 1 : log_warn(LD_DIR, "Certificate didn't end with dir-key-certification.");
155 1 : goto err;
156 : }
157 :
158 : /* If we already have this cert, don't bother checking the signature. */
159 445 : old_cert = authority_cert_get_by_digests(
160 : cert->cache_info.identity_digest,
161 : cert->signing_key_digest);
162 445 : found = 0;
163 445 : if (old_cert) {
164 : /* XXXX We could just compare signed_descriptor_digest, but that wouldn't
165 : * buy us much. */
166 26 : if (old_cert->cache_info.signed_descriptor_len == len &&
167 52 : old_cert->cache_info.signed_descriptor_body &&
168 26 : tor_memeq(s, old_cert->cache_info.signed_descriptor_body, len)) {
169 26 : log_debug(LD_DIR, "We already checked the signature on this "
170 : "certificate; no need to do so again.");
171 26 : found = 1;
172 : }
173 : }
174 26 : if (!found) {
175 419 : if (check_signature_token(digest, DIGEST_LEN, tok, cert->identity_key, 0,
176 : "key certificate")) {
177 0 : goto err;
178 : }
179 :
180 419 : tok = find_by_keyword(tokens, K_DIR_KEY_CROSSCERT);
181 419 : if (check_signature_token(cert->cache_info.identity_digest,
182 : DIGEST_LEN,
183 : tok,
184 : cert->signing_key,
185 : CST_NO_CHECK_OBJTYPE,
186 : "key cross-certification")) {
187 0 : goto err;
188 : }
189 : }
190 :
191 445 : cert->cache_info.signed_descriptor_len = len;
192 445 : cert->cache_info.signed_descriptor_body = tor_malloc(len+1);
193 445 : memcpy(cert->cache_info.signed_descriptor_body, s, len);
194 445 : cert->cache_info.signed_descriptor_body[len] = 0;
195 445 : cert->cache_info.saved_location = SAVED_NOWHERE;
196 :
197 445 : if (end_of_string) {
198 394 : *end_of_string = eat_whitespace(eos);
199 : }
200 11149 : SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
201 445 : smartlist_free(tokens);
202 445 : if (area) {
203 445 : DUMP_AREA(area, "authority cert");
204 445 : memarea_drop_all(area);
205 : }
206 : return cert;
207 33 : err:
208 33 : dump_desc(s_dup, "authority cert");
209 33 : authority_cert_free(cert);
210 2051 : SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
211 33 : smartlist_free(tokens);
212 33 : if (area) {
213 33 : DUMP_AREA(area, "authority cert");
214 33 : memarea_drop_all(area);
215 : }
216 : return NULL;
217 : }
|