/src/tor/src/feature/dirparse/routerparse.c
Line | Count | Source (jump to first uncovered line) |
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 routerparse.c |
9 | | * \brief Code to parse and validate router descriptors, consenus directories, |
10 | | * and similar objects. |
11 | | * |
12 | | * The objects parsed by this module use a common text-based metaformat, |
13 | | * documented in dir-spec.txt in torspec.git. This module is itself divided |
14 | | * into two major kinds of function: code to handle the metaformat, and code |
15 | | * to convert from particular instances of the metaformat into the |
16 | | * objects that Tor uses. |
17 | | * |
18 | | * The generic parsing code works by calling a table-based tokenizer on the |
19 | | * input string. Each token corresponds to a single line with a token, plus |
20 | | * optional arguments on that line, plus an optional base-64 encoded object |
21 | | * after that line. Each token has a definition in a table of token_rule_t |
22 | | * entries that describes how many arguments it can take, whether it takes an |
23 | | * object, how many times it may appear, whether it must appear first, and so |
24 | | * on. |
25 | | * |
26 | | * The tokenizer function tokenize_string() converts its string input into a |
27 | | * smartlist full of instances of directory_token_t, according to a provided |
28 | | * table of token_rule_t. |
29 | | * |
30 | | * The generic parts of this module additionally include functions for |
31 | | * finding the start and end of signed information inside a signed object, and |
32 | | * computing the digest that will be signed. |
33 | | * |
34 | | * There are also functions for saving objects to disk that have caused |
35 | | * parsing to fail. |
36 | | * |
37 | | * The specific parts of this module describe conversions between |
38 | | * particular lists of directory_token_t and particular objects. The |
39 | | * kinds of objects that can be parsed here are: |
40 | | * <ul> |
41 | | * <li>router descriptors (managed from routerlist.c) |
42 | | * <li>extra-info documents (managed from routerlist.c) |
43 | | * <li>microdescriptors (managed from microdesc.c) |
44 | | * <li>vote and consensus networkstatus documents, and the routerstatus_t |
45 | | * objects that they comprise (managed from networkstatus.c) |
46 | | * <li>detached-signature objects used by authorities for gathering |
47 | | * signatures on the networkstatus consensus (managed from dirvote.c) |
48 | | * <li>authority key certificates (managed from routerlist.c) |
49 | | * <li>hidden service descriptors (managed from rendcommon.c and rendcache.c) |
50 | | * </ul> |
51 | | **/ |
52 | | |
53 | | #define ROUTERDESC_TOKEN_TABLE_PRIVATE |
54 | | #define ROUTERPARSE_PRIVATE |
55 | | |
56 | | #include "core/or/or.h" |
57 | | #include "app/config/config.h" |
58 | | #include "core/or/policies.h" |
59 | | #include "core/or/versions.h" |
60 | | #include "feature/dirparse/parsecommon.h" |
61 | | #include "feature/dirparse/policy_parse.h" |
62 | | #include "feature/dirparse/routerparse.h" |
63 | | #include "feature/dirparse/sigcommon.h" |
64 | | #include "feature/dirparse/unparseable.h" |
65 | | #include "feature/nodelist/describe.h" |
66 | | #include "feature/nodelist/nickname.h" |
67 | | #include "feature/nodelist/routerinfo.h" |
68 | | #include "feature/nodelist/routerlist.h" |
69 | | #include "feature/nodelist/torcert.h" |
70 | | #include "feature/relay/router.h" |
71 | | #include "lib/crypt_ops/crypto_curve25519.h" |
72 | | #include "lib/crypt_ops/crypto_ed25519.h" |
73 | | #include "lib/crypt_ops/crypto_format.h" |
74 | | #include "lib/memarea/memarea.h" |
75 | | #include "lib/sandbox/sandbox.h" |
76 | | |
77 | | #include "core/or/addr_policy_st.h" |
78 | | #include "feature/nodelist/extrainfo_st.h" |
79 | | #include "feature/nodelist/routerinfo_st.h" |
80 | | #include "feature/nodelist/routerlist_st.h" |
81 | | |
82 | | /****************************************************************************/ |
83 | | |
84 | | /** List of tokens recognized in router descriptors */ |
85 | | // clang-format off |
86 | | const token_rule_t routerdesc_token_table[] = { |
87 | | T0N("reject", K_REJECT, ARGS, NO_OBJ ), |
88 | | T0N("accept", K_ACCEPT, ARGS, NO_OBJ ), |
89 | | T0N("reject6", K_REJECT6, ARGS, NO_OBJ ), |
90 | | T0N("accept6", K_ACCEPT6, ARGS, NO_OBJ ), |
91 | | T1_START( "router", K_ROUTER, GE(5), NO_OBJ ), |
92 | | T01("ipv6-policy", K_IPV6_POLICY, CONCAT_ARGS, NO_OBJ), |
93 | | T1( "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY_1024 ), |
94 | | T01("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024 ), |
95 | | T1("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), |
96 | | T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ), |
97 | | T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ), |
98 | | T01("uptime", K_UPTIME, GE(1), NO_OBJ ), |
99 | | T01("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ), |
100 | | T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ ), |
101 | | T01("platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ ), |
102 | | T1("proto", K_PROTO, CONCAT_ARGS, NO_OBJ ), |
103 | | T01("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ), |
104 | | T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), |
105 | | T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), |
106 | | T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ), |
107 | | T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ), |
108 | | T1("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), |
109 | | T1("master-key-ed25519", K_MASTER_KEY_ED25519, GE(1), NO_OBJ ), |
110 | | T1("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), |
111 | | T01("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ), |
112 | | T1("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT, |
113 | | EQ(1), NEED_OBJ ), |
114 | | |
115 | | T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ), |
116 | | |
117 | | T01("family", K_FAMILY, ARGS, NO_OBJ ), |
118 | | T0N("family-cert", K_FAMILY_CERT, ARGS, NEED_OBJ ), |
119 | | T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ), |
120 | | T0N("or-address", K_OR_ADDRESS, GE(1), NO_OBJ ), |
121 | | |
122 | | T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), |
123 | | T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ), |
124 | | A01("@purpose", A_PURPOSE, GE(1), NO_OBJ ), |
125 | | T01("tunnelled-dir-server",K_DIR_TUNNELLED, NO_ARGS, NO_OBJ ), |
126 | | |
127 | | END_OF_TABLE |
128 | | }; |
129 | | // clang-format on |
130 | | |
131 | | /** List of tokens recognized in extra-info documents. */ |
132 | | // clang-format off |
133 | | static token_rule_t extrainfo_token_table[] = { |
134 | | T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ), |
135 | | T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ), |
136 | | T1("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), |
137 | | T1("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), |
138 | | T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), |
139 | | T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), |
140 | | T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), |
141 | | T01("dirreq-stats-end", K_DIRREQ_END, ARGS, NO_OBJ ), |
142 | | T01("dirreq-v2-ips", K_DIRREQ_V2_IPS, ARGS, NO_OBJ ), |
143 | | T01("dirreq-v3-ips", K_DIRREQ_V3_IPS, ARGS, NO_OBJ ), |
144 | | T01("dirreq-v2-reqs", K_DIRREQ_V2_REQS, ARGS, NO_OBJ ), |
145 | | T01("dirreq-v3-reqs", K_DIRREQ_V3_REQS, ARGS, NO_OBJ ), |
146 | | T01("dirreq-v2-share", K_DIRREQ_V2_SHARE, ARGS, NO_OBJ ), |
147 | | T01("dirreq-v3-share", K_DIRREQ_V3_SHARE, ARGS, NO_OBJ ), |
148 | | T01("dirreq-v2-resp", K_DIRREQ_V2_RESP, ARGS, NO_OBJ ), |
149 | | T01("dirreq-v3-resp", K_DIRREQ_V3_RESP, ARGS, NO_OBJ ), |
150 | | T01("dirreq-v2-direct-dl", K_DIRREQ_V2_DIR, ARGS, NO_OBJ ), |
151 | | T01("dirreq-v3-direct-dl", K_DIRREQ_V3_DIR, ARGS, NO_OBJ ), |
152 | | T01("dirreq-v2-tunneled-dl", K_DIRREQ_V2_TUN, ARGS, NO_OBJ ), |
153 | | T01("dirreq-v3-tunneled-dl", K_DIRREQ_V3_TUN, ARGS, NO_OBJ ), |
154 | | T01("entry-stats-end", K_ENTRY_END, ARGS, NO_OBJ ), |
155 | | T01("entry-ips", K_ENTRY_IPS, ARGS, NO_OBJ ), |
156 | | T01("cell-stats-end", K_CELL_END, ARGS, NO_OBJ ), |
157 | | T01("cell-processed-cells", K_CELL_PROCESSED, ARGS, NO_OBJ ), |
158 | | T01("cell-queued-cells", K_CELL_QUEUED, ARGS, NO_OBJ ), |
159 | | T01("cell-time-in-queue", K_CELL_TIME, ARGS, NO_OBJ ), |
160 | | T01("cell-circuits-per-decile", K_CELL_CIRCS, ARGS, NO_OBJ ), |
161 | | T01("exit-stats-end", K_EXIT_END, ARGS, NO_OBJ ), |
162 | | T01("exit-kibibytes-written", K_EXIT_WRITTEN, ARGS, NO_OBJ ), |
163 | | T01("exit-kibibytes-read", K_EXIT_READ, ARGS, NO_OBJ ), |
164 | | T01("exit-streams-opened", K_EXIT_OPENED, ARGS, NO_OBJ ), |
165 | | |
166 | | T1_START( "extra-info", K_EXTRA_INFO, GE(2), NO_OBJ ), |
167 | | |
168 | | END_OF_TABLE |
169 | | }; |
170 | | // clang-format on |
171 | | |
172 | | #undef T |
173 | | |
174 | | /* static function prototypes */ |
175 | | static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok); |
176 | | static smartlist_t *find_all_exitpolicy(smartlist_t *s); |
177 | | static int check_family_certs(const smartlist_t *family_cert_tokens, |
178 | | const ed25519_public_key_t *identity_key, |
179 | | smartlist_t **family_ids_out, |
180 | | time_t *family_expiration_out); |
181 | | |
182 | | /** Set <b>digest</b> to the SHA-1 digest of the hash of the first router in |
183 | | * <b>s</b>. Return 0 on success, -1 on failure. |
184 | | */ |
185 | | int |
186 | | router_get_router_hash(const char *s, size_t s_len, char *digest) |
187 | 6.40k | { |
188 | 6.40k | return router_get_hash_impl(s, s_len, digest, |
189 | 6.40k | "router ","\nrouter-signature", '\n', |
190 | 6.40k | DIGEST_SHA1); |
191 | 6.40k | } |
192 | | |
193 | | /** Set <b>digest</b> to the SHA-1 digest of the hash of the <b>s_len</b>-byte |
194 | | * extrainfo string at <b>s</b>. Return 0 on success, -1 on failure. */ |
195 | | int |
196 | | router_get_extrainfo_hash(const char *s, size_t s_len, char *digest) |
197 | 3.11k | { |
198 | 3.11k | return router_get_hash_impl(s, s_len, digest, "extra-info", |
199 | 3.11k | "\nrouter-signature",'\n', DIGEST_SHA1); |
200 | 3.11k | } |
201 | | |
202 | | /** Helper: move *<b>s_ptr</b> ahead to the next router, the next extra-info, |
203 | | * or to the first of the annotations proceeding the next router or |
204 | | * extra-info---whichever comes first. Set <b>is_extrainfo_out</b> to true if |
205 | | * we found an extrainfo, or false if found a router. Do not scan beyond |
206 | | * <b>eos</b>. Return -1 if we found nothing; 0 if we found something. */ |
207 | | static int |
208 | | find_start_of_next_router_or_extrainfo(const char **s_ptr, |
209 | | const char *eos, |
210 | | int *is_extrainfo_out) |
211 | 0 | { |
212 | 0 | const char *annotations = NULL; |
213 | 0 | const char *s = *s_ptr; |
214 | |
|
215 | 0 | s = eat_whitespace_eos(s, eos); |
216 | |
|
217 | 0 | while (s < eos-32) { /* 32 gives enough room for a the first keyword. */ |
218 | | /* We're at the start of a line. */ |
219 | 0 | tor_assert(*s != '\n'); |
220 | | |
221 | 0 | if (*s == '@' && !annotations) { |
222 | 0 | annotations = s; |
223 | 0 | } else if (*s == 'r' && !strcmpstart(s, "router ")) { |
224 | 0 | *s_ptr = annotations ? annotations : s; |
225 | 0 | *is_extrainfo_out = 0; |
226 | 0 | return 0; |
227 | 0 | } else if (*s == 'e' && !strcmpstart(s, "extra-info ")) { |
228 | 0 | *s_ptr = annotations ? annotations : s; |
229 | 0 | *is_extrainfo_out = 1; |
230 | 0 | return 0; |
231 | 0 | } |
232 | | |
233 | 0 | if (!(s = memchr(s+1, '\n', eos-(s+1)))) |
234 | 0 | break; |
235 | 0 | s = eat_whitespace_eos(s, eos); |
236 | 0 | } |
237 | 0 | return -1; |
238 | 0 | } |
239 | | |
240 | | /** Given a string *<b>s</b> containing a concatenated sequence of router |
241 | | * descriptors (or extra-info documents if <b>want_extrainfo</b> is set), |
242 | | * parses them and stores the result in <b>dest</b>. All routers are marked |
243 | | * running and valid. Advances *s to a point immediately following the last |
244 | | * router entry. Ignore any trailing router entries that are not complete. |
245 | | * |
246 | | * If <b>saved_location</b> isn't SAVED_IN_CACHE, make a local copy of each |
247 | | * descriptor in the signed_descriptor_body field of each routerinfo_t. If it |
248 | | * isn't SAVED_NOWHERE, remember the offset of each descriptor. |
249 | | * |
250 | | * Returns 0 on success and -1 on failure. Adds a digest to |
251 | | * <b>invalid_digests_out</b> for every entry that was unparseable or |
252 | | * invalid. (This may cause duplicate entries.) |
253 | | */ |
254 | | int |
255 | | router_parse_list_from_string(const char **s, const char *eos, |
256 | | smartlist_t *dest, |
257 | | saved_location_t saved_location, |
258 | | int want_extrainfo, |
259 | | int allow_annotations, |
260 | | const char *prepend_annotations, |
261 | | smartlist_t *invalid_digests_out) |
262 | 0 | { |
263 | 0 | routerinfo_t *router; |
264 | 0 | extrainfo_t *extrainfo; |
265 | 0 | signed_descriptor_t *signed_desc = NULL; |
266 | 0 | void *elt; |
267 | 0 | const char *end, *start; |
268 | 0 | int have_extrainfo; |
269 | |
|
270 | 0 | tor_assert(s); |
271 | 0 | tor_assert(*s); |
272 | 0 | tor_assert(dest); |
273 | | |
274 | 0 | start = *s; |
275 | 0 | if (!eos) |
276 | 0 | eos = *s + strlen(*s); |
277 | |
|
278 | 0 | tor_assert(eos >= *s); |
279 | | |
280 | 0 | while (1) { |
281 | 0 | char raw_digest[DIGEST_LEN]; |
282 | 0 | int have_raw_digest = 0; |
283 | 0 | int dl_again = 0; |
284 | 0 | if (find_start_of_next_router_or_extrainfo(s, eos, &have_extrainfo) < 0) |
285 | 0 | break; |
286 | | |
287 | 0 | end = tor_memstr(*s, eos-*s, "\nrouter-signature"); |
288 | 0 | if (end) |
289 | 0 | end = tor_memstr(end, eos-end, "\n-----END SIGNATURE-----\n"); |
290 | 0 | if (end) |
291 | 0 | end += strlen("\n-----END SIGNATURE-----\n"); |
292 | |
|
293 | 0 | if (!end) |
294 | 0 | break; |
295 | | |
296 | 0 | elt = NULL; |
297 | |
|
298 | 0 | if (have_extrainfo && want_extrainfo) { |
299 | 0 | routerlist_t *rl = router_get_routerlist(); |
300 | 0 | have_raw_digest = router_get_extrainfo_hash(*s, end-*s, raw_digest) == 0; |
301 | 0 | extrainfo = extrainfo_parse_entry_from_string(*s, end, |
302 | 0 | saved_location != SAVED_IN_CACHE, |
303 | 0 | rl->identity_map, &dl_again); |
304 | 0 | if (extrainfo) { |
305 | 0 | signed_desc = &extrainfo->cache_info; |
306 | 0 | elt = extrainfo; |
307 | 0 | } |
308 | 0 | } else if (!have_extrainfo && !want_extrainfo) { |
309 | 0 | have_raw_digest = router_get_router_hash(*s, end-*s, raw_digest) == 0; |
310 | 0 | router = router_parse_entry_from_string(*s, end, |
311 | 0 | saved_location != SAVED_IN_CACHE, |
312 | 0 | allow_annotations, |
313 | 0 | prepend_annotations, &dl_again); |
314 | 0 | if (router) { |
315 | 0 | log_debug(LD_DIR, "Read router '%s', purpose '%s'", |
316 | 0 | router_describe(router), |
317 | 0 | router_purpose_to_string(router->purpose)); |
318 | 0 | signed_desc = &router->cache_info; |
319 | 0 | elt = router; |
320 | 0 | } |
321 | 0 | } |
322 | 0 | if (! elt && ! dl_again && have_raw_digest && invalid_digests_out) { |
323 | 0 | smartlist_add(invalid_digests_out, tor_memdup(raw_digest, DIGEST_LEN)); |
324 | 0 | } |
325 | 0 | if (!elt) { |
326 | 0 | *s = end; |
327 | 0 | continue; |
328 | 0 | } |
329 | 0 | if (saved_location != SAVED_NOWHERE) { |
330 | 0 | tor_assert(signed_desc); |
331 | 0 | signed_desc->saved_location = saved_location; |
332 | 0 | signed_desc->saved_offset = *s - start; |
333 | 0 | } |
334 | 0 | *s = end; |
335 | 0 | smartlist_add(dest, elt); |
336 | 0 | } |
337 | | |
338 | 0 | return 0; |
339 | 0 | } |
340 | | |
341 | | /** Try to find an IPv6 OR port in <b>list</b> of directory_token_t's |
342 | | * with at least one argument (use GE(1) in setup). If found, store |
343 | | * address and port number to <b>addr_out</b> and |
344 | | * <b>port_out</b>. Return number of OR ports found. */ |
345 | | int |
346 | | find_single_ipv6_orport(const smartlist_t *list, |
347 | | tor_addr_t *addr_out, |
348 | | uint16_t *port_out) |
349 | 7.82k | { |
350 | 7.82k | int ret = 0; |
351 | 7.82k | tor_assert(list != NULL); |
352 | 7.82k | tor_assert(addr_out != NULL); |
353 | 7.82k | tor_assert(port_out != NULL); |
354 | | |
355 | 493k | SMARTLIST_FOREACH_BEGIN(list, directory_token_t *, t) { |
356 | 493k | tor_addr_t a; |
357 | 493k | maskbits_t bits; |
358 | 493k | uint16_t port_min, port_max; |
359 | 493k | tor_assert(t->n_args >= 1); |
360 | | /* XXXX Prop186 the full spec allows much more than this. */ |
361 | 493k | if (tor_addr_parse_mask_ports(t->args[0], 0, |
362 | 493k | &a, &bits, &port_min, |
363 | 493k | &port_max) == AF_INET6 && |
364 | 493k | bits == 128 && |
365 | 493k | port_min == port_max) { |
366 | | /* Okay, this is one we can understand. Use it and ignore |
367 | | any potential more addresses in list. */ |
368 | 1.18k | tor_addr_copy(addr_out, &a); |
369 | 1.18k | *port_out = port_min; |
370 | 1.18k | ret = 1; |
371 | 1.18k | break; |
372 | 1.18k | } |
373 | 493k | } SMARTLIST_FOREACH_END(t); |
374 | | |
375 | 7.82k | return ret; |
376 | 7.82k | } |
377 | | |
378 | | /** Helper function: reads a single router entry from *<b>s</b> ... |
379 | | * *<b>end</b>. Mallocs a new router and returns it if all goes well, else |
380 | | * returns NULL. If <b>cache_copy</b> is true, duplicate the contents of |
381 | | * s through end into the signed_descriptor_body of the resulting |
382 | | * routerinfo_t. |
383 | | * |
384 | | * If <b>end</b> is NULL, <b>s</b> must be properly NUL-terminated. |
385 | | * |
386 | | * If <b>allow_annotations</b>, it's okay to encounter annotations in <b>s</b> |
387 | | * before the router; if it's false, reject the router if it's annotated. If |
388 | | * <b>prepend_annotations</b> is set, it should contain some annotations: |
389 | | * append them to the front of the router before parsing it, and keep them |
390 | | * around when caching the router. |
391 | | * |
392 | | * Only one of allow_annotations and prepend_annotations may be set. |
393 | | * |
394 | | * If <b>can_dl_again_out</b> is provided, set *<b>can_dl_again_out</b> to 1 |
395 | | * if it's okay to try to download a descriptor with this same digest again, |
396 | | * and 0 if it isn't. (It might not be okay to download it again if part of |
397 | | * the part covered by the digest is invalid.) |
398 | | */ |
399 | | routerinfo_t * |
400 | | router_parse_entry_from_string(const char *s, const char *end, |
401 | | int cache_copy, int allow_annotations, |
402 | | const char *prepend_annotations, |
403 | | int *can_dl_again_out) |
404 | 6.49k | { |
405 | 6.49k | routerinfo_t *router = NULL; |
406 | 6.49k | char digest[128]; |
407 | 6.49k | smartlist_t *tokens = NULL, *exit_policy_tokens = NULL; |
408 | 6.49k | directory_token_t *tok; |
409 | 6.49k | struct in_addr in; |
410 | 6.49k | const char *start_of_annotations, *cp, *s_dup = s; |
411 | 6.49k | size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0; |
412 | 6.49k | int ok = 1; |
413 | 6.49k | memarea_t *area = NULL; |
414 | 6.49k | tor_cert_t *ntor_cc_cert = NULL; |
415 | | /* Do not set this to '1' until we have parsed everything that we intend to |
416 | | * parse that's covered by the hash. */ |
417 | 6.49k | int can_dl_again = 0; |
418 | 6.49k | crypto_pk_t *rsa_pubkey = NULL; |
419 | | |
420 | 6.49k | tor_assert(!allow_annotations || !prepend_annotations); |
421 | | |
422 | 6.49k | if (!end) { |
423 | 0 | end = s + strlen(s); |
424 | 0 | } |
425 | | |
426 | | /* point 'end' to a point immediately after the final newline. */ |
427 | 6.81k | while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n') |
428 | 315 | --end; |
429 | | |
430 | 6.49k | area = memarea_new(); |
431 | 6.49k | tokens = smartlist_new(); |
432 | 6.49k | if (prepend_annotations) { |
433 | 0 | if (tokenize_string(area,prepend_annotations,NULL,tokens, |
434 | 0 | routerdesc_token_table,TS_NOCHECK)) { |
435 | 0 | log_warn(LD_DIR, "Error tokenizing router descriptor (annotations)."); |
436 | 0 | goto err; |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | 6.49k | start_of_annotations = s; |
441 | 6.49k | cp = tor_memstr(s, end-s, "\nrouter "); |
442 | 6.49k | if (!cp) { |
443 | 6.49k | if (end-s < 7 || strcmpstart(s, "router ")) { |
444 | 90 | log_warn(LD_DIR, "No router keyword found."); |
445 | 90 | goto err; |
446 | 90 | } |
447 | 6.49k | } else { |
448 | 1 | s = cp+1; |
449 | 1 | } |
450 | | |
451 | 6.40k | if (start_of_annotations != s) { /* We have annotations */ |
452 | 1 | if (allow_annotations) { |
453 | 0 | if (tokenize_string(area,start_of_annotations,s,tokens, |
454 | 0 | routerdesc_token_table,TS_NOCHECK)) { |
455 | 0 | log_warn(LD_DIR, "Error tokenizing router descriptor (annotations)."); |
456 | 0 | goto err; |
457 | 0 | } |
458 | 1 | } else { |
459 | 1 | log_warn(LD_DIR, "Found unexpected annotations on router descriptor not " |
460 | 1 | "loaded from disk. Dropping it."); |
461 | 1 | goto err; |
462 | 1 | } |
463 | 1 | } |
464 | | |
465 | 6.40k | if (!tor_memstr(s, end-s, "\nproto ")) { |
466 | 2 | log_debug(LD_DIR, "Found an obsolete router descriptor. " |
467 | 2 | "Rejecting quietly."); |
468 | 2 | goto err; |
469 | 2 | } |
470 | | |
471 | 6.40k | if (router_get_router_hash(s, end - s, digest) < 0) { |
472 | 5 | log_warn(LD_DIR, "Couldn't compute router hash."); |
473 | 5 | goto err; |
474 | 5 | } |
475 | 6.40k | { |
476 | 6.40k | int flags = 0; |
477 | 6.40k | if (allow_annotations) |
478 | 0 | flags |= TS_ANNOTATIONS_OK; |
479 | 6.40k | if (prepend_annotations) |
480 | 0 | flags |= TS_ANNOTATIONS_OK|TS_NO_NEW_ANNOTATIONS; |
481 | | |
482 | 6.40k | if (tokenize_string(area,s,end,tokens,routerdesc_token_table, flags)) { |
483 | 2.01k | log_warn(LD_DIR, "Error tokenizing router descriptor."); |
484 | 2.01k | goto err; |
485 | 2.01k | } |
486 | 6.40k | } |
487 | | |
488 | 4.39k | if (smartlist_len(tokens) < 2) { |
489 | 0 | log_warn(LD_DIR, "Impossibly short router descriptor."); |
490 | 0 | goto err; |
491 | 0 | } |
492 | | |
493 | 4.39k | tok = find_by_keyword(tokens, K_ROUTER); |
494 | 4.39k | const int router_token_pos = smartlist_pos(tokens, tok); |
495 | 4.39k | tor_assert(tok->n_args >= 5); |
496 | | |
497 | 4.39k | router = tor_malloc_zero(sizeof(routerinfo_t)); |
498 | 4.39k | router->cert_expiration_time = TIME_MAX; |
499 | 4.39k | router->cache_info.routerlist_index = -1; |
500 | 4.39k | router->cache_info.annotations_len = s-start_of_annotations + prepend_len; |
501 | 4.39k | router->cache_info.signed_descriptor_len = end-s; |
502 | 4.39k | if (cache_copy) { |
503 | 0 | size_t len = router->cache_info.signed_descriptor_len + |
504 | 0 | router->cache_info.annotations_len; |
505 | 0 | char *signed_body = |
506 | 0 | router->cache_info.signed_descriptor_body = tor_malloc(len+1); |
507 | 0 | if (prepend_annotations) { |
508 | 0 | memcpy(signed_body, prepend_annotations, prepend_len); |
509 | 0 | signed_body += prepend_len; |
510 | 0 | } |
511 | | /* This assertion will always succeed. |
512 | | * len == signed_desc_len + annotations_len |
513 | | * == end-s + s-start_of_annotations + prepend_len |
514 | | * == end-start_of_annotations + prepend_len |
515 | | * We already wrote prepend_len bytes into the buffer; now we're |
516 | | * writing end-start_of_annotations -NM. */ |
517 | 0 | tor_assert(signed_body+(end-start_of_annotations) == |
518 | 0 | router->cache_info.signed_descriptor_body+len); |
519 | 0 | memcpy(signed_body, start_of_annotations, end-start_of_annotations); |
520 | 0 | router->cache_info.signed_descriptor_body[len] = '\0'; |
521 | 0 | tor_assert(strlen(router->cache_info.signed_descriptor_body) == len); |
522 | 0 | } |
523 | 4.39k | memcpy(router->cache_info.signed_descriptor_digest, digest, DIGEST_LEN); |
524 | | |
525 | 4.39k | router->nickname = tor_strdup(tok->args[0]); |
526 | 4.39k | if (!is_legal_nickname(router->nickname)) { |
527 | 30 | log_warn(LD_DIR,"Router nickname is invalid"); |
528 | 30 | goto err; |
529 | 30 | } |
530 | 4.36k | if (!tor_inet_aton(tok->args[1], &in)) { |
531 | 27 | log_warn(LD_DIR,"Router address is not an IP address."); |
532 | 27 | goto err; |
533 | 27 | } |
534 | 4.33k | tor_addr_from_in(&router->ipv4_addr, &in); |
535 | | |
536 | 4.33k | router->ipv4_orport = |
537 | 4.33k | (uint16_t) tor_parse_long(tok->args[2],10,0,65535,&ok,NULL); |
538 | 4.33k | if (!ok) { |
539 | 110 | log_warn(LD_DIR,"Invalid OR port %s", escaped(tok->args[2])); |
540 | 110 | goto err; |
541 | 110 | } |
542 | 4.22k | router->ipv4_dirport = |
543 | 4.22k | (uint16_t) tor_parse_long(tok->args[4],10,0,65535,&ok,NULL); |
544 | 4.22k | if (!ok) { |
545 | 24 | log_warn(LD_DIR,"Invalid dir port %s", escaped(tok->args[4])); |
546 | 24 | goto err; |
547 | 24 | } |
548 | | |
549 | 4.19k | tok = find_by_keyword(tokens, K_BANDWIDTH); |
550 | 4.19k | tor_assert(tok->n_args >= 3); |
551 | 4.19k | router->bandwidthrate = (int) |
552 | 4.19k | tor_parse_long(tok->args[0],10,1,INT_MAX,&ok,NULL); |
553 | | |
554 | 4.19k | if (!ok) { |
555 | 49 | log_warn(LD_DIR, "bandwidthrate %s unreadable or 0. Failing.", |
556 | 49 | escaped(tok->args[0])); |
557 | 49 | goto err; |
558 | 49 | } |
559 | 4.15k | router->bandwidthburst = |
560 | 4.15k | (int) tor_parse_long(tok->args[1],10,0,INT_MAX,&ok,NULL); |
561 | 4.15k | if (!ok) { |
562 | 6 | log_warn(LD_DIR, "Invalid bandwidthburst %s", escaped(tok->args[1])); |
563 | 6 | goto err; |
564 | 6 | } |
565 | 4.14k | router->bandwidthcapacity = (int) |
566 | 4.14k | tor_parse_long(tok->args[2],10,0,INT_MAX,&ok,NULL); |
567 | 4.14k | if (!ok) { |
568 | 12 | log_warn(LD_DIR, "Invalid bandwidthcapacity %s", escaped(tok->args[1])); |
569 | 12 | goto err; |
570 | 12 | } |
571 | | |
572 | 4.13k | if ((tok = find_opt_by_keyword(tokens, A_PURPOSE))) { |
573 | 0 | tor_assert(tok->n_args); |
574 | 0 | router->purpose = router_purpose_from_string(tok->args[0]); |
575 | 0 | if (router->purpose == ROUTER_PURPOSE_UNKNOWN) { |
576 | 0 | goto err; |
577 | 0 | } |
578 | 4.13k | } else { |
579 | 4.13k | router->purpose = ROUTER_PURPOSE_GENERAL; |
580 | 4.13k | } |
581 | 4.13k | router->cache_info.send_unencrypted = |
582 | 4.13k | (router->purpose == ROUTER_PURPOSE_GENERAL) ? 1 : 0; |
583 | | |
584 | 4.13k | if ((tok = find_opt_by_keyword(tokens, K_UPTIME))) { |
585 | 80 | tor_assert(tok->n_args >= 1); |
586 | 80 | router->uptime = tor_parse_long(tok->args[0],10,0,LONG_MAX,&ok,NULL); |
587 | 80 | if (!ok) { |
588 | 1 | log_warn(LD_DIR, "Invalid uptime %s", escaped(tok->args[0])); |
589 | 1 | goto err; |
590 | 1 | } |
591 | 80 | } |
592 | | |
593 | 4.13k | if ((tok = find_opt_by_keyword(tokens, K_HIBERNATING))) { |
594 | 107 | tor_assert(tok->n_args >= 1); |
595 | 107 | router->is_hibernating |
596 | 107 | = (tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL) != 0); |
597 | 107 | } |
598 | | |
599 | 4.13k | tok = find_by_keyword(tokens, K_PUBLISHED); |
600 | 4.13k | tor_assert(tok->n_args == 1); |
601 | 4.13k | if (parse_iso_time(tok->args[0], &router->cache_info.published_on) < 0) |
602 | 389 | goto err; |
603 | | |
604 | 3.74k | tok = find_opt_by_keyword(tokens, K_ONION_KEY); |
605 | 3.74k | if (tok) { |
606 | 167 | if (!crypto_pk_public_exponent_ok(tok->key)) { |
607 | 2 | log_warn(LD_DIR, |
608 | 2 | "Relay's onion key had invalid exponent."); |
609 | 2 | goto err; |
610 | 2 | } |
611 | 165 | router->tap_onion_pkey = tor_memdup(tok->object_body, tok->object_size); |
612 | 165 | router->tap_onion_pkey_len = tok->object_size; |
613 | 165 | crypto_pk_free(tok->key); |
614 | 165 | } |
615 | | |
616 | 3.74k | if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) { |
617 | 3.74k | curve25519_public_key_t k; |
618 | 3.74k | tor_assert(tok->n_args >= 1); |
619 | 3.74k | if (curve25519_public_from_base64(&k, tok->args[0]) < 0) { |
620 | 162 | log_warn(LD_DIR, "Bogus ntor-onion-key in routerinfo"); |
621 | 162 | goto err; |
622 | 162 | } |
623 | 3.57k | router->onion_curve25519_pkey = |
624 | 3.57k | tor_memdup(&k, sizeof(curve25519_public_key_t)); |
625 | 3.57k | } |
626 | | |
627 | 3.57k | tok = find_by_keyword(tokens, K_SIGNING_KEY); |
628 | 3.57k | router->identity_pkey = tok->key; |
629 | 3.57k | tok->key = NULL; /* Prevent free */ |
630 | 3.57k | if (crypto_pk_get_digest(router->identity_pkey, |
631 | 3.57k | router->cache_info.identity_digest)) { |
632 | 0 | log_warn(LD_DIR, "Couldn't calculate key digest"); goto err; |
633 | 0 | } |
634 | | |
635 | 3.57k | { |
636 | 3.57k | directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok, |
637 | 3.57k | *master_key_tok; |
638 | 3.57k | ed_sig_tok = find_by_keyword(tokens, K_ROUTER_SIG_ED25519); |
639 | 3.57k | ed_cert_tok = find_by_keyword(tokens, K_IDENTITY_ED25519); |
640 | 3.57k | master_key_tok = find_by_keyword(tokens, K_MASTER_KEY_ED25519); |
641 | 3.57k | cc_ntor_tok = find_by_keyword(tokens, K_NTOR_ONION_KEY_CROSSCERT); |
642 | | /* This, and only this, is optional. */ |
643 | 3.57k | cc_tap_tok = find_opt_by_keyword(tokens, K_ONION_KEY_CROSSCERT); |
644 | | |
645 | 3.57k | if (bool_neq(cc_tap_tok==NULL, router->tap_onion_pkey==NULL)) { |
646 | 10 | log_warn(LD_DIR, "Router descriptor had only one of (onion-key, " |
647 | 10 | "onion-key-crosscert)."); |
648 | 10 | goto err; |
649 | 10 | } |
650 | | |
651 | 3.56k | IF_BUG_ONCE(! (ed_sig_tok && ed_cert_tok&& cc_ntor_tok &&master_key_tok)) { |
652 | 0 | goto err; |
653 | 0 | } |
654 | | |
655 | 3.56k | tor_cert_t *cert; |
656 | 3.56k | { |
657 | | /* Parse the identity certificate */ |
658 | 3.56k | cert = tor_cert_parse( |
659 | 3.56k | (const uint8_t*)ed_cert_tok->object_body, |
660 | 3.56k | ed_cert_tok->object_size); |
661 | 3.56k | if (! cert) { |
662 | 280 | log_warn(LD_DIR, "Couldn't parse ed25519 cert"); |
663 | 280 | goto err; |
664 | 280 | } |
665 | | /* makes sure it gets freed. */ |
666 | 3.28k | router->cache_info.signing_key_cert = cert; |
667 | | |
668 | 3.28k | if (cert->cert_type != CERT_TYPE_ID_SIGNING || |
669 | 3.28k | ! cert->signing_key_included) { |
670 | 10 | log_warn(LD_DIR, "Invalid form for ed25519 cert"); |
671 | 10 | goto err; |
672 | 10 | } |
673 | 3.28k | } |
674 | | |
675 | 3.27k | if (cc_tap_tok) { |
676 | 144 | rsa_pubkey = router_get_rsa_onion_pkey(router->tap_onion_pkey, |
677 | 144 | router->tap_onion_pkey_len); |
678 | 144 | if (rsa_pubkey == NULL) { |
679 | 0 | log_warn(LD_DIR, "No pubkey for TAP cross-verification."); |
680 | 0 | goto err; |
681 | 0 | } |
682 | 144 | if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) { |
683 | 30 | log_warn(LD_DIR, "Wrong object type on onion-key-crosscert " |
684 | 30 | "in descriptor"); |
685 | 30 | goto err; |
686 | 30 | } |
687 | 114 | if (check_tap_onion_key_crosscert( |
688 | 114 | (const uint8_t*)cc_tap_tok->object_body, |
689 | 114 | (int)cc_tap_tok->object_size, |
690 | 114 | rsa_pubkey, |
691 | 114 | &cert->signing_key, |
692 | 114 | (const uint8_t*)router->cache_info.identity_digest)<0) { |
693 | 0 | log_warn(LD_DIR, "Incorrect TAP cross-verification"); |
694 | 0 | goto err; |
695 | 0 | } |
696 | 114 | } |
697 | | |
698 | 3.24k | { |
699 | 3.24k | tor_assert(ed_sig_tok && ed_cert_tok && cc_ntor_tok); |
700 | 3.24k | const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok); |
701 | 3.24k | if (ed_cert_token_pos == -1 || router_token_pos == -1 || |
702 | 3.24k | (ed_cert_token_pos != router_token_pos + 1 && |
703 | 3.24k | ed_cert_token_pos != router_token_pos - 1)) { |
704 | 63 | log_warn(LD_DIR, "Ed25519 certificate in wrong position"); |
705 | 63 | goto err; |
706 | 63 | } |
707 | 3.18k | if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) { |
708 | 1 | log_warn(LD_DIR, "Ed25519 signature in wrong position"); |
709 | 1 | goto err; |
710 | 1 | } |
711 | 3.18k | if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { |
712 | 50 | log_warn(LD_DIR, "Wrong object type on identity-ed25519 " |
713 | 50 | "in descriptor"); |
714 | 50 | goto err; |
715 | 50 | } |
716 | 3.13k | if (strcmp(cc_ntor_tok->object_type, "ED25519 CERT")) { |
717 | 39 | log_warn(LD_DIR, "Wrong object type on ntor-onion-key-crosscert " |
718 | 39 | "in descriptor"); |
719 | 39 | goto err; |
720 | 39 | } |
721 | 3.09k | if (strcmp(cc_ntor_tok->args[0], "0") && |
722 | 3.09k | strcmp(cc_ntor_tok->args[0], "1")) { |
723 | 2 | log_warn(LD_DIR, "Bad sign bit on ntor-onion-key-crosscert"); |
724 | 2 | goto err; |
725 | 2 | } |
726 | 3.09k | int ntor_cc_sign_bit = !strcmp(cc_ntor_tok->args[0], "1"); |
727 | 3.09k | uint8_t d256[DIGEST256_LEN]; |
728 | 3.09k | const char *signed_start, *signed_end; |
729 | | |
730 | 3.09k | if (master_key_tok) { |
731 | | /* This token is optional, but if it's present, it must match |
732 | | * the signature in the signing cert, or supplant it. */ |
733 | 3.09k | tor_assert(master_key_tok->n_args >= 1); |
734 | 3.09k | ed25519_public_key_t pkey; |
735 | 3.09k | if (ed25519_public_from_base64(&pkey, master_key_tok->args[0])<0) { |
736 | 12 | log_warn(LD_DIR, "Can't parse ed25519 master key"); |
737 | 12 | goto err; |
738 | 12 | } |
739 | | |
740 | 3.08k | if (fast_memneq(&cert->signing_key.pubkey, |
741 | 3.08k | pkey.pubkey, ED25519_PUBKEY_LEN)) { |
742 | 11 | log_warn(LD_DIR, "Ed25519 master key does not match " |
743 | 11 | "key in certificate"); |
744 | 11 | goto err; |
745 | 11 | } |
746 | 3.08k | } |
747 | 3.07k | ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body, |
748 | 3.07k | cc_ntor_tok->object_size); |
749 | 3.07k | if (!ntor_cc_cert) { |
750 | 9 | log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert"); |
751 | 9 | goto err; |
752 | 9 | } |
753 | 3.06k | if (ntor_cc_cert->cert_type != CERT_TYPE_ONION_ID || |
754 | 3.06k | ! ed25519_pubkey_eq(&ntor_cc_cert->signed_key, &cert->signing_key)) { |
755 | 9 | log_warn(LD_DIR, "Invalid contents for ntor-onion-key-crosscert cert"); |
756 | 9 | goto err; |
757 | 9 | } |
758 | | |
759 | 3.05k | ed25519_public_key_t ntor_cc_pk; |
760 | 3.05k | if (ed25519_public_key_from_curve25519_public_key(&ntor_cc_pk, |
761 | 3.05k | router->onion_curve25519_pkey, |
762 | 3.05k | ntor_cc_sign_bit)<0) { |
763 | 0 | log_warn(LD_DIR, "Error converting onion key to ed25519"); |
764 | 0 | goto err; |
765 | 0 | } |
766 | | |
767 | 3.05k | if (router_get_hash_impl_helper(s, end-s, "router ", |
768 | 3.05k | "\nrouter-sig-ed25519", |
769 | 3.05k | ' ', LOG_WARN, |
770 | 3.05k | &signed_start, &signed_end) < 0) { |
771 | 1 | log_warn(LD_DIR, "Can't find ed25519-signed portion of descriptor"); |
772 | 1 | goto err; |
773 | 1 | } |
774 | 3.05k | crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); |
775 | 3.05k | crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX, |
776 | 3.05k | strlen(ED_DESC_SIGNATURE_PREFIX)); |
777 | 3.05k | crypto_digest_add_bytes(d, signed_start, signed_end-signed_start); |
778 | | |
779 | 3.05k | crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); |
780 | 3.05k | crypto_digest_free(d); |
781 | | |
782 | 3.05k | ed25519_checkable_t check[3]; |
783 | 3.05k | int check_ok[3]; |
784 | 3.05k | time_t expires = TIME_MAX; |
785 | 3.05k | if (tor_cert_get_checkable_sig(&check[0], cert, NULL, &expires) < 0) { |
786 | 0 | log_err(LD_BUG, "Couldn't create 'checkable' for cert."); |
787 | 0 | goto err; |
788 | 0 | } |
789 | 3.05k | if (tor_cert_get_checkable_sig(&check[1], |
790 | 3.05k | ntor_cc_cert, &ntor_cc_pk, &expires) < 0) { |
791 | 0 | log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert."); |
792 | 0 | goto err; |
793 | 0 | } |
794 | | |
795 | 3.05k | if (ed25519_signature_from_base64(&check[2].signature, |
796 | 3.05k | ed_sig_tok->args[0])<0) { |
797 | 86 | log_warn(LD_DIR, "Couldn't decode ed25519 signature"); |
798 | 86 | goto err; |
799 | 86 | } |
800 | 2.96k | check[2].pubkey = &cert->signed_key; |
801 | 2.96k | check[2].msg = d256; |
802 | 2.96k | check[2].len = DIGEST256_LEN; |
803 | | |
804 | 2.96k | if (ed25519_checksig_batch(check_ok, check, 3) < 0) { |
805 | 0 | log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); |
806 | 0 | goto err; |
807 | 0 | } |
808 | | |
809 | | /* We check this before adding it to the routerlist. */ |
810 | 2.96k | router->cert_expiration_time = expires; |
811 | 2.96k | } |
812 | 2.96k | } |
813 | | |
814 | 2.96k | if ((tok = find_opt_by_keyword(tokens, K_FINGERPRINT))) { |
815 | | /* If there's a fingerprint line, it must match the identity digest. */ |
816 | 226 | char d[DIGEST_LEN]; |
817 | 226 | tor_assert(tok->n_args == 1); |
818 | 226 | tor_strstrip(tok->args[0], " "); |
819 | 226 | if (base16_decode(d, DIGEST_LEN, |
820 | 226 | tok->args[0], strlen(tok->args[0])) != DIGEST_LEN) { |
821 | 189 | log_warn(LD_DIR, "Couldn't decode router fingerprint %s", |
822 | 189 | escaped(tok->args[0])); |
823 | 189 | goto err; |
824 | 189 | } |
825 | 37 | if (tor_memneq(d,router->cache_info.identity_digest, DIGEST_LEN)) { |
826 | 4 | log_warn(LD_DIR, "Fingerprint '%s' does not match identity digest.", |
827 | 4 | tok->args[0]); |
828 | 4 | goto err; |
829 | 4 | } |
830 | 37 | } |
831 | | |
832 | 2.77k | { |
833 | 2.77k | const char *version = NULL, *protocols = NULL; |
834 | 2.77k | if ((tok = find_opt_by_keyword(tokens, K_PLATFORM))) { |
835 | 642 | router->platform = tor_strdup(tok->args[0]); |
836 | 642 | version = tok->args[0]; |
837 | 642 | } |
838 | | |
839 | 2.77k | if ((tok = find_opt_by_keyword(tokens, K_PROTO))) { |
840 | 2.77k | router->protocol_list = tor_strdup(tok->args[0]); |
841 | 2.77k | protocols = tok->args[0]; |
842 | 2.77k | } |
843 | | |
844 | 2.77k | summarize_protover_flags(&router->pv, protocols, version); |
845 | 2.77k | } |
846 | | |
847 | 2.77k | if ((tok = find_opt_by_keyword(tokens, K_CONTACT))) { |
848 | 41 | router->contact_info = tor_strdup(tok->args[0]); |
849 | 41 | } |
850 | | |
851 | 2.77k | if (find_opt_by_keyword(tokens, K_REJECT6) || |
852 | 2.77k | find_opt_by_keyword(tokens, K_ACCEPT6)) { |
853 | 7 | log_warn(LD_DIR, "Rejecting router with reject6/accept6 line: they crash " |
854 | 7 | "older Tors."); |
855 | 7 | goto err; |
856 | 7 | } |
857 | 2.76k | { |
858 | 2.76k | smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS); |
859 | 2.76k | if (or_addresses) { |
860 | 750 | find_single_ipv6_orport(or_addresses, &router->ipv6_addr, |
861 | 750 | &router->ipv6_orport); |
862 | 750 | smartlist_free(or_addresses); |
863 | 750 | } |
864 | 2.76k | } |
865 | 2.76k | exit_policy_tokens = find_all_exitpolicy(tokens); |
866 | 2.76k | if (!smartlist_len(exit_policy_tokens)) { |
867 | 1.70k | log_warn(LD_DIR, "No exit policy tokens in descriptor."); |
868 | 1.70k | goto err; |
869 | 1.70k | } |
870 | 2.76k | SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t, |
871 | 735 | if (router_add_exit_policy(router,t)<0) { |
872 | 735 | log_warn(LD_DIR,"Error in exit policy"); |
873 | 735 | goto err; |
874 | 735 | }); |
875 | 735 | policy_expand_private(&router->exit_policy); |
876 | | |
877 | 735 | if ((tok = find_opt_by_keyword(tokens, K_IPV6_POLICY)) && tok->n_args) { |
878 | 167 | router->ipv6_exit_policy = parse_short_policy(tok->args[0]); |
879 | 167 | if (! router->ipv6_exit_policy) { |
880 | 117 | log_warn(LD_DIR , "Error in ipv6-policy %s", escaped(tok->args[0])); |
881 | 117 | goto err; |
882 | 117 | } |
883 | 167 | } |
884 | | |
885 | 618 | if (policy_is_reject_star(router->exit_policy, AF_INET, 1) && |
886 | 618 | (!router->ipv6_exit_policy || |
887 | 456 | short_policy_is_reject_star(router->ipv6_exit_policy))) |
888 | 409 | router->policy_is_reject_star = 1; |
889 | | |
890 | 618 | if ((tok = find_opt_by_keyword(tokens, K_FAMILY)) && tok->n_args) { |
891 | 132 | int i; |
892 | 132 | router->declared_family = smartlist_new(); |
893 | 903 | for (i=0;i<tok->n_args;++i) { |
894 | 883 | if (!is_legal_nickname_or_hexdigest(tok->args[i])) { |
895 | 112 | log_warn(LD_DIR, "Illegal nickname %s in family line", |
896 | 112 | escaped(tok->args[i])); |
897 | 112 | goto err; |
898 | 112 | } |
899 | 771 | smartlist_add_strdup(router->declared_family, tok->args[i]); |
900 | 771 | } |
901 | 132 | } |
902 | | |
903 | 506 | { |
904 | 506 | smartlist_t *family_cert_toks = find_all_by_keyword(tokens, K_FAMILY_CERT); |
905 | 506 | time_t family_expiration = TIME_MAX; |
906 | 506 | int r = 0; |
907 | 506 | if (family_cert_toks) { |
908 | 5 | r = check_family_certs(family_cert_toks, |
909 | 5 | &router->cache_info.signing_key_cert->signing_key, |
910 | 5 | &router->family_ids, |
911 | 5 | &family_expiration); |
912 | 5 | smartlist_free(family_cert_toks); |
913 | 5 | } |
914 | 506 | if (r<0) |
915 | 5 | goto err; |
916 | 506 | } |
917 | | |
918 | 501 | if (find_opt_by_keyword(tokens, K_CACHES_EXTRA_INFO)) |
919 | 4 | router->caches_extra_info = 1; |
920 | | |
921 | 501 | if (find_opt_by_keyword(tokens, K_ALLOW_SINGLE_HOP_EXITS)) |
922 | 3 | router->allow_single_hop_exits = 1; |
923 | | |
924 | 501 | if ((tok = find_opt_by_keyword(tokens, K_EXTRA_INFO_DIGEST))) { |
925 | 53 | tor_assert(tok->n_args >= 1); |
926 | 53 | if (strlen(tok->args[0]) == HEX_DIGEST_LEN) { |
927 | 20 | if (base16_decode(router->cache_info.extra_info_digest, DIGEST_LEN, |
928 | 20 | tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN) { |
929 | 10 | log_warn(LD_DIR,"Invalid extra info digest"); |
930 | 10 | } |
931 | 33 | } else { |
932 | 33 | log_warn(LD_DIR, "Invalid extra info digest %s", escaped(tok->args[0])); |
933 | 33 | } |
934 | | |
935 | 53 | if (tok->n_args >= 2) { |
936 | 36 | if (digest256_from_base64(router->cache_info.extra_info_digest256, |
937 | 36 | tok->args[1]) < 0) { |
938 | 29 | log_warn(LD_DIR, "Invalid extra info digest256 %s", |
939 | 29 | escaped(tok->args[1])); |
940 | 29 | } |
941 | 36 | } |
942 | 53 | } |
943 | | |
944 | 501 | if (find_opt_by_keyword(tokens, K_HIDDEN_SERVICE_DIR)) { |
945 | 17 | router->wants_to_be_hs_dir = 1; |
946 | 17 | } |
947 | | |
948 | | /* This router accepts tunnelled directory requests via begindir if it has |
949 | | * an open dirport or it included "tunnelled-dir-server". */ |
950 | 501 | if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || |
951 | 501 | router->ipv4_dirport > 0) { |
952 | 241 | router->supports_tunnelled_dir_requests = 1; |
953 | 241 | } |
954 | | |
955 | 501 | tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE); |
956 | | |
957 | 501 | if (!router->ipv4_orport) { |
958 | 140 | log_warn(LD_DIR,"or_port unreadable or 0. Failing."); |
959 | 140 | goto err; |
960 | 140 | } |
961 | | |
962 | | /* We've checked everything that's covered by the hash. */ |
963 | 361 | can_dl_again = 1; |
964 | 361 | if (check_signature_token(digest, DIGEST_LEN, tok, router->identity_pkey, 0, |
965 | 361 | "router descriptor") < 0) |
966 | 242 | goto err; |
967 | | |
968 | 119 | if (!router->platform) { |
969 | 97 | router->platform = tor_strdup("<unknown>"); |
970 | 97 | } |
971 | 119 | goto done; |
972 | | |
973 | 6.38k | err: |
974 | 6.38k | dump_desc(s_dup, "router descriptor"); |
975 | 6.38k | routerinfo_free(router); |
976 | 6.38k | router = NULL; |
977 | 6.49k | done: |
978 | 6.49k | crypto_pk_free(rsa_pubkey); |
979 | 6.49k | tor_cert_free(ntor_cc_cert); |
980 | 6.49k | if (tokens) { |
981 | 6.49k | SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); |
982 | 6.49k | smartlist_free(tokens); |
983 | 6.49k | } |
984 | 6.49k | smartlist_free(exit_policy_tokens); |
985 | 6.49k | if (area) { |
986 | 6.49k | DUMP_AREA(area, "routerinfo"); |
987 | 6.49k | memarea_drop_all(area); |
988 | 6.49k | } |
989 | 6.49k | if (can_dl_again_out) |
990 | 0 | *can_dl_again_out = can_dl_again; |
991 | 6.49k | return router; |
992 | 6.38k | } |
993 | | |
994 | | /** Parse a single extrainfo entry from the string <b>s</b>, ending at |
995 | | * <b>end</b>. (If <b>end</b> is NULL, parse up to the end of <b>s</b>.) If |
996 | | * <b>cache_copy</b> is true, make a copy of the extra-info document in the |
997 | | * cache_info fields of the result. If <b>routermap</b> is provided, use it |
998 | | * as a map from router identity to routerinfo_t when looking up signing keys. |
999 | | * |
1000 | | * If <b>can_dl_again_out</b> is provided, set *<b>can_dl_again_out</b> to 1 |
1001 | | * if it's okay to try to download an extrainfo with this same digest again, |
1002 | | * and 0 if it isn't. (It might not be okay to download it again if part of |
1003 | | * the part covered by the digest is invalid.) |
1004 | | */ |
1005 | | extrainfo_t * |
1006 | | extrainfo_parse_entry_from_string(const char *s, const char *end, |
1007 | | int cache_copy, struct digest_ri_map_t *routermap, |
1008 | | int *can_dl_again_out) |
1009 | 3.14k | { |
1010 | 3.14k | extrainfo_t *extrainfo = NULL; |
1011 | 3.14k | char digest[128]; |
1012 | 3.14k | smartlist_t *tokens = NULL; |
1013 | 3.14k | directory_token_t *tok; |
1014 | 3.14k | crypto_pk_t *key = NULL; |
1015 | 3.14k | routerinfo_t *router = NULL; |
1016 | 3.14k | memarea_t *area = NULL; |
1017 | 3.14k | const char *s_dup = s; |
1018 | | /* Do not set this to '1' until we have parsed everything that we intend to |
1019 | | * parse that's covered by the hash. */ |
1020 | 3.14k | int can_dl_again = 0; |
1021 | | |
1022 | 3.14k | if (BUG(s == NULL)) |
1023 | 0 | return NULL; |
1024 | | |
1025 | 3.14k | if (!end) { |
1026 | 0 | end = s + strlen(s); |
1027 | 0 | } |
1028 | | |
1029 | | /* point 'end' to a point immediately after the final newline. */ |
1030 | 3.34k | while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n') |
1031 | 200 | --end; |
1032 | | |
1033 | 3.14k | if (!tor_memstr(s, end-s, "\nidentity-ed25519")) { |
1034 | 32 | log_debug(LD_DIR, "Found an obsolete extrainfo. Rejecting quietly."); |
1035 | 32 | goto err; |
1036 | 32 | } |
1037 | | |
1038 | 3.11k | if (router_get_extrainfo_hash(s, end-s, digest) < 0) { |
1039 | 13 | log_warn(LD_DIR, "Couldn't compute router hash."); |
1040 | 13 | goto err; |
1041 | 13 | } |
1042 | 3.10k | tokens = smartlist_new(); |
1043 | 3.10k | area = memarea_new(); |
1044 | 3.10k | if (tokenize_string(area,s,end,tokens,extrainfo_token_table,0)) { |
1045 | 1.81k | log_warn(LD_DIR, "Error tokenizing extra-info document."); |
1046 | 1.81k | goto err; |
1047 | 1.81k | } |
1048 | | |
1049 | 1.28k | if (smartlist_len(tokens) < 2) { |
1050 | 0 | log_warn(LD_DIR, "Impossibly short extra-info document."); |
1051 | 0 | goto err; |
1052 | 0 | } |
1053 | | |
1054 | | /* XXXX Accept this in position 1 too, and ed identity in position 0. */ |
1055 | 1.28k | tok = smartlist_get(tokens,0); |
1056 | 1.28k | if (tok->tp != K_EXTRA_INFO) { |
1057 | 0 | log_warn(LD_DIR,"Entry does not start with \"extra-info\""); |
1058 | 0 | goto err; |
1059 | 0 | } |
1060 | | |
1061 | 1.28k | extrainfo = tor_malloc_zero(sizeof(extrainfo_t)); |
1062 | 1.28k | extrainfo->cache_info.is_extrainfo = 1; |
1063 | 1.28k | if (cache_copy) |
1064 | 0 | extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s,end-s); |
1065 | 1.28k | extrainfo->cache_info.signed_descriptor_len = end-s; |
1066 | 1.28k | memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN); |
1067 | 1.28k | crypto_digest256((char*)extrainfo->digest256, s, end-s, DIGEST_SHA256); |
1068 | | |
1069 | 1.28k | tor_assert(tok->n_args >= 2); |
1070 | 1.28k | if (!is_legal_nickname(tok->args[0])) { |
1071 | 98 | log_warn(LD_DIR,"Bad nickname %s on \"extra-info\"",escaped(tok->args[0])); |
1072 | 98 | goto err; |
1073 | 98 | } |
1074 | 1.18k | strlcpy(extrainfo->nickname, tok->args[0], sizeof(extrainfo->nickname)); |
1075 | 1.18k | if (strlen(tok->args[1]) != HEX_DIGEST_LEN || |
1076 | 1.18k | base16_decode(extrainfo->cache_info.identity_digest, DIGEST_LEN, |
1077 | 1.09k | tok->args[1], HEX_DIGEST_LEN) != DIGEST_LEN) { |
1078 | 146 | log_warn(LD_DIR,"Invalid fingerprint %s on \"extra-info\"", |
1079 | 146 | escaped(tok->args[1])); |
1080 | 146 | goto err; |
1081 | 146 | } |
1082 | | |
1083 | 1.04k | tok = find_by_keyword(tokens, K_PUBLISHED); |
1084 | 1.04k | if (parse_iso_time(tok->args[0], &extrainfo->cache_info.published_on)) { |
1085 | 349 | log_warn(LD_DIR,"Invalid published time %s on \"extra-info\"", |
1086 | 349 | escaped(tok->args[0])); |
1087 | 349 | goto err; |
1088 | 349 | } |
1089 | | |
1090 | 691 | { |
1091 | 691 | directory_token_t *ed_sig_tok, *ed_cert_tok; |
1092 | 691 | ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519); |
1093 | 691 | ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519); |
1094 | 691 | int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok; |
1095 | 691 | if (n_ed_toks != 0 && n_ed_toks != 2) { |
1096 | 0 | log_warn(LD_DIR, "Router descriptor with only partial ed25519/" |
1097 | 0 | "cross-certification support"); |
1098 | 0 | goto err; |
1099 | 0 | } |
1100 | 691 | if (ed_sig_tok) { |
1101 | 691 | tor_assert(ed_cert_tok); |
1102 | 691 | const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok); |
1103 | 691 | if (ed_cert_token_pos != 1) { |
1104 | | /* Accept this in position 0 XXXX */ |
1105 | 93 | log_warn(LD_DIR, "Ed25519 certificate in wrong position"); |
1106 | 93 | goto err; |
1107 | 93 | } |
1108 | 598 | if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) { |
1109 | 26 | log_warn(LD_DIR, "Ed25519 signature in wrong position"); |
1110 | 26 | goto err; |
1111 | 26 | } |
1112 | 572 | if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { |
1113 | 139 | log_warn(LD_DIR, "Wrong object type on identity-ed25519 " |
1114 | 139 | "in descriptor"); |
1115 | 139 | goto err; |
1116 | 139 | } |
1117 | | |
1118 | 433 | uint8_t d256[DIGEST256_LEN]; |
1119 | 433 | const char *signed_start, *signed_end; |
1120 | 433 | tor_cert_t *cert = tor_cert_parse( |
1121 | 433 | (const uint8_t*)ed_cert_tok->object_body, |
1122 | 433 | ed_cert_tok->object_size); |
1123 | 433 | if (! cert) { |
1124 | 301 | log_warn(LD_DIR, "Couldn't parse ed25519 cert"); |
1125 | 301 | goto err; |
1126 | 301 | } |
1127 | | /* makes sure it gets freed. */ |
1128 | 132 | extrainfo->cache_info.signing_key_cert = cert; |
1129 | | |
1130 | 132 | if (cert->cert_type != CERT_TYPE_ID_SIGNING || |
1131 | 132 | ! cert->signing_key_included) { |
1132 | 26 | log_warn(LD_DIR, "Invalid form for ed25519 cert"); |
1133 | 26 | goto err; |
1134 | 26 | } |
1135 | | |
1136 | 106 | if (router_get_hash_impl_helper(s, end-s, "extra-info ", |
1137 | 106 | "\nrouter-sig-ed25519", |
1138 | 106 | ' ', LOG_WARN, |
1139 | 106 | &signed_start, &signed_end) < 0) { |
1140 | 1 | log_warn(LD_DIR, "Can't find ed25519-signed portion of extrainfo"); |
1141 | 1 | goto err; |
1142 | 1 | } |
1143 | 105 | crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); |
1144 | 105 | crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX, |
1145 | 105 | strlen(ED_DESC_SIGNATURE_PREFIX)); |
1146 | 105 | crypto_digest_add_bytes(d, signed_start, signed_end-signed_start); |
1147 | 105 | crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); |
1148 | 105 | crypto_digest_free(d); |
1149 | | |
1150 | 105 | ed25519_checkable_t check[2]; |
1151 | 105 | int check_ok[2]; |
1152 | 105 | if (tor_cert_get_checkable_sig(&check[0], cert, NULL, NULL) < 0) { |
1153 | 0 | log_err(LD_BUG, "Couldn't create 'checkable' for cert."); |
1154 | 0 | goto err; |
1155 | 0 | } |
1156 | | |
1157 | 105 | if (ed25519_signature_from_base64(&check[1].signature, |
1158 | 105 | ed_sig_tok->args[0])<0) { |
1159 | 67 | log_warn(LD_DIR, "Couldn't decode ed25519 signature"); |
1160 | 67 | goto err; |
1161 | 67 | } |
1162 | 38 | check[1].pubkey = &cert->signed_key; |
1163 | 38 | check[1].msg = d256; |
1164 | 38 | check[1].len = DIGEST256_LEN; |
1165 | | |
1166 | 38 | if (ed25519_checksig_batch(check_ok, check, 2) < 0) { |
1167 | 0 | log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); |
1168 | 0 | goto err; |
1169 | 0 | } |
1170 | | /* We don't check the certificate expiration time: checking that it |
1171 | | * matches the cert in the router descriptor is adequate. */ |
1172 | 38 | } |
1173 | 691 | } |
1174 | | |
1175 | | /* We've checked everything that's covered by the hash. */ |
1176 | 38 | can_dl_again = 1; |
1177 | | |
1178 | 38 | if (routermap && |
1179 | 38 | (router = digestmap_get((digestmap_t*)routermap, |
1180 | 0 | extrainfo->cache_info.identity_digest))) { |
1181 | 0 | key = router->identity_pkey; |
1182 | 0 | } |
1183 | | |
1184 | 38 | tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE); |
1185 | 38 | if (strcmp(tok->object_type, "SIGNATURE") || |
1186 | 38 | tok->object_size < 128 || tok->object_size > 512) { |
1187 | 28 | log_warn(LD_DIR, "Bad object type or length on extra-info signature"); |
1188 | 28 | goto err; |
1189 | 28 | } |
1190 | | |
1191 | 10 | if (key) { |
1192 | 0 | if (check_signature_token(digest, DIGEST_LEN, tok, key, 0, |
1193 | 0 | "extra-info") < 0) |
1194 | 0 | goto err; |
1195 | | |
1196 | 0 | if (router) |
1197 | 0 | extrainfo->cache_info.send_unencrypted = |
1198 | 0 | router->cache_info.send_unencrypted; |
1199 | 10 | } else { |
1200 | 10 | extrainfo->pending_sig = tor_memdup(tok->object_body, |
1201 | 10 | tok->object_size); |
1202 | 10 | extrainfo->pending_sig_len = tok->object_size; |
1203 | 10 | } |
1204 | | |
1205 | 10 | goto done; |
1206 | 3.13k | err: |
1207 | 3.13k | dump_desc(s_dup, "extra-info descriptor"); |
1208 | 3.13k | extrainfo_free(extrainfo); |
1209 | 3.13k | extrainfo = NULL; |
1210 | 3.14k | done: |
1211 | 3.14k | if (tokens) { |
1212 | 3.10k | SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); |
1213 | 3.10k | smartlist_free(tokens); |
1214 | 3.10k | } |
1215 | 3.14k | if (area) { |
1216 | 3.10k | DUMP_AREA(area, "extrainfo"); |
1217 | 3.10k | memarea_drop_all(area); |
1218 | 3.10k | } |
1219 | 3.14k | if (can_dl_again_out) |
1220 | 3.14k | *can_dl_again_out = can_dl_again; |
1221 | 3.14k | return extrainfo; |
1222 | 3.13k | } |
1223 | | |
1224 | | /** Add an exit policy stored in the token <b>tok</b> to the router info in |
1225 | | * <b>router</b>. Return 0 on success, -1 on failure. */ |
1226 | | static int |
1227 | | router_add_exit_policy(routerinfo_t *router, directory_token_t *tok) |
1228 | 451k | { |
1229 | 451k | addr_policy_t *newe; |
1230 | | /* Use the standard interpretation of accept/reject *, an IPv4 wildcard. */ |
1231 | 451k | newe = router_parse_addr_policy(tok, 0); |
1232 | 451k | if (!newe) |
1233 | 313 | return -1; |
1234 | 450k | if (! router->exit_policy) |
1235 | 814 | router->exit_policy = smartlist_new(); |
1236 | | |
1237 | | /* Ensure that in descriptors, accept/reject fields are followed by |
1238 | | * IPv4 addresses, and accept6/reject6 fields are followed by |
1239 | | * IPv6 addresses. Unlike torrcs, descriptor exit policies do not permit |
1240 | | * accept/reject followed by IPv6. */ |
1241 | 450k | if (((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) && |
1242 | 450k | tor_addr_family(&newe->addr) == AF_INET) |
1243 | 450k | || |
1244 | 450k | ((tok->tp == K_ACCEPT || tok->tp == K_REJECT) && |
1245 | 450k | tor_addr_family(&newe->addr) == AF_INET6)) { |
1246 | | /* There's nothing the user can do about other relays' descriptors, |
1247 | | * so we don't provide usage advice here. */ |
1248 | 12 | log_warn(LD_DIR, "Mismatch between field type and address type in exit " |
1249 | 12 | "policy '%s'. Discarding entire router descriptor.", |
1250 | 12 | tok->n_args == 1 ? tok->args[0] : ""); |
1251 | 12 | addr_policy_free(newe); |
1252 | 12 | return -1; |
1253 | 12 | } |
1254 | | |
1255 | 450k | smartlist_add(router->exit_policy, newe); |
1256 | | |
1257 | 450k | return 0; |
1258 | 450k | } |
1259 | | |
1260 | | /** Return a newly allocated smartlist of all accept or reject tokens in |
1261 | | * <b>s</b>. |
1262 | | */ |
1263 | | static smartlist_t * |
1264 | | find_all_exitpolicy(smartlist_t *s) |
1265 | 2.76k | { |
1266 | 2.76k | smartlist_t *out = smartlist_new(); |
1267 | 2.76k | SMARTLIST_FOREACH(s, directory_token_t *, t, |
1268 | 2.76k | if (t->tp == K_ACCEPT || t->tp == K_ACCEPT6 || |
1269 | 2.76k | t->tp == K_REJECT || t->tp == K_REJECT6) |
1270 | 2.76k | smartlist_add(out,t)); |
1271 | 2.76k | return out; |
1272 | 2.76k | } |
1273 | | |
1274 | | /** |
1275 | | * Parse and validate a single `FAMILY_CERT` token's object. |
1276 | | * |
1277 | | * Arguments are as for `check_family_certs()`. |
1278 | | */ |
1279 | | STATIC int |
1280 | | check_one_family_cert(const uint8_t *cert_body, |
1281 | | size_t cert_body_size, |
1282 | | const ed25519_public_key_t *identity_key, |
1283 | | char **family_id_out, |
1284 | | time_t *family_expiration_out) |
1285 | 5 | { |
1286 | 5 | tor_cert_t *cert = NULL; |
1287 | 5 | int r = -1; |
1288 | | |
1289 | 5 | cert = tor_cert_parse(cert_body, cert_body_size); |
1290 | | |
1291 | 5 | if (! cert) |
1292 | 3 | goto done; |
1293 | 2 | if (cert->cert_type != CERT_TYPE_FAMILY_V_IDENTITY) { |
1294 | 1 | log_warn(LD_DIR, "Wrong cert type in family certificate."); |
1295 | 1 | goto done; |
1296 | 1 | } |
1297 | 1 | if (! cert->signing_key_included) { |
1298 | 1 | log_warn(LD_DIR, "Missing family key in family certificate."); |
1299 | 1 | goto done; |
1300 | 1 | } |
1301 | 0 | if (! ed25519_pubkey_eq(&cert->signed_key, identity_key)) { |
1302 | 0 | log_warn(LD_DIR, "Key mismatch in family certificate."); |
1303 | 0 | goto done; |
1304 | 0 | } |
1305 | | |
1306 | 0 | time_t valid_until = cert->valid_until; |
1307 | | |
1308 | | /* We're using NULL for the key, since the cert has the signing key included. |
1309 | | * We're using 0 for "now", since we're going to extract the expiration |
1310 | | * separately. |
1311 | | */ |
1312 | 0 | if (tor_cert_checksig(cert, NULL, 0) < 0) { |
1313 | 0 | log_warn(LD_DIR, "Invalid signature in family certificate"); |
1314 | 0 | goto done; |
1315 | 0 | } |
1316 | | |
1317 | | /* At this point we know that the cert is valid. |
1318 | | * We extract the expiration time and the signing key. */ |
1319 | 0 | *family_expiration_out = valid_until; |
1320 | |
|
1321 | 0 | char buf[ED25519_BASE64_LEN+1]; |
1322 | 0 | ed25519_public_to_base64(buf, &cert->signing_key); |
1323 | 0 | tor_asprintf(family_id_out, "ed25519:%s", buf); |
1324 | |
|
1325 | 0 | r = 0; |
1326 | 5 | done: |
1327 | 5 | tor_cert_free(cert); |
1328 | 5 | return r; |
1329 | 0 | } |
1330 | | |
1331 | | /** |
1332 | | * Given a list of `FAMILY_CERT` tokens, and a relay's ed25519 `identity_key`, |
1333 | | * validate the family certificates in all the tokens, and convert them into |
1334 | | * family IDs in a newly allocated `family_ids_out` list. |
1335 | | * Set `family_expiration_out` to the earliest time at which any certificate |
1336 | | * in the list expires. |
1337 | | * Return 0 on success, and -1 on failure. |
1338 | | */ |
1339 | | static int |
1340 | | check_family_certs(const smartlist_t *family_cert_tokens, |
1341 | | const ed25519_public_key_t *identity_key, |
1342 | | smartlist_t **family_ids_out, |
1343 | | time_t *family_expiration_out) |
1344 | 5 | { |
1345 | 5 | if (BUG(!identity_key) || |
1346 | 5 | BUG(!family_ids_out) || |
1347 | 5 | BUG(!family_expiration_out)) |
1348 | 0 | return -1; |
1349 | | |
1350 | 5 | *family_expiration_out = TIME_MAX; |
1351 | | |
1352 | 5 | if (family_cert_tokens == NULL || smartlist_len(family_cert_tokens) == 0) { |
1353 | 0 | *family_ids_out = NULL; |
1354 | 0 | return 0; |
1355 | 0 | } |
1356 | | |
1357 | 5 | *family_ids_out = smartlist_new(); |
1358 | 5 | SMARTLIST_FOREACH_BEGIN(family_cert_tokens, directory_token_t *, tok) { |
1359 | 5 | if (BUG(tok->object_body == NULL)) |
1360 | 0 | goto err; |
1361 | | |
1362 | 5 | char *this_id = NULL; |
1363 | 5 | time_t this_expiration = TIME_MAX; |
1364 | 5 | if (check_one_family_cert((const uint8_t*)tok->object_body, |
1365 | 5 | tok->object_size, |
1366 | 5 | identity_key, |
1367 | 5 | &this_id, &this_expiration) < 0) |
1368 | 5 | goto err; |
1369 | 0 | smartlist_add(*family_ids_out, this_id); |
1370 | 0 | *family_expiration_out = MIN(*family_expiration_out, this_expiration); |
1371 | 0 | } SMARTLIST_FOREACH_END(tok); |
1372 | | |
1373 | 0 | smartlist_sort_strings(*family_ids_out); |
1374 | 0 | smartlist_uniq_strings(*family_ids_out); |
1375 | |
|
1376 | 0 | return 0; |
1377 | 5 | err: |
1378 | 5 | SMARTLIST_FOREACH(*family_ids_out, char *, cp, tor_free(cp)); |
1379 | 5 | smartlist_free(*family_ids_out); |
1380 | 5 | return -1; |
1381 | 5 | } |
1382 | | |
1383 | | /** Called on startup; right now we just handle scanning the unparseable |
1384 | | * descriptor dumps, but hang anything else we might need to do in the |
1385 | | * future here as well. |
1386 | | */ |
1387 | | void |
1388 | | routerparse_init(void) |
1389 | 0 | { |
1390 | | /* |
1391 | | * Check both if the sandbox is active and whether it's configured; no |
1392 | | * point in loading all that if we won't be able to use it after the |
1393 | | * sandbox becomes active. |
1394 | | */ |
1395 | 0 | if (!(sandbox_is_active() || get_options()->Sandbox)) { |
1396 | 0 | dump_desc_init(); |
1397 | 0 | } |
1398 | 0 | } |
1399 | | |
1400 | | /** Clean up all data structures used by routerparse.c at exit */ |
1401 | | void |
1402 | | routerparse_free_all(void) |
1403 | 0 | { |
1404 | 0 | dump_desc_fifo_cleanup(); |
1405 | 0 | } |