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 routerparse.c
9 : * \brief Code to parse and validate consensus documents and votes.
10 : */
11 :
12 : #define NS_PARSE_PRIVATE
13 :
14 : #include "core/or/or.h"
15 : #include "app/config/config.h"
16 : #include "core/or/protover.h"
17 : #include "core/or/versions.h"
18 : #include "feature/client/entrynodes.h"
19 : #include "feature/dirauth/dirvote.h"
20 : #include "feature/dirparse/authcert_parse.h"
21 : #include "feature/dirparse/ns_parse.h"
22 : #include "feature/dirparse/parsecommon.h"
23 : #include "feature/dirparse/routerparse.h"
24 : #include "feature/dirparse/sigcommon.h"
25 : #include "feature/dirparse/unparseable.h"
26 : #include "feature/hs_common/shared_random_client.h"
27 : #include "feature/nodelist/authcert.h"
28 : #include "feature/nodelist/describe.h"
29 : #include "feature/nodelist/networkstatus.h"
30 : #include "feature/nodelist/nickname.h"
31 : #include "lib/crypt_ops/crypto_format.h"
32 : #include "lib/memarea/memarea.h"
33 :
34 : #include "feature/dirauth/vote_microdesc_hash_st.h"
35 : #include "feature/nodelist/authority_cert_st.h"
36 : #include "feature/nodelist/document_signature_st.h"
37 : #include "feature/nodelist/networkstatus_st.h"
38 : #include "feature/nodelist/networkstatus_voter_info_st.h"
39 : #include "feature/nodelist/vote_routerstatus_st.h"
40 : #include "feature/dirparse/authcert_members.h"
41 :
42 : #undef log
43 : #include <math.h>
44 :
45 : /** List of tokens recognized in the body part of v3 networkstatus
46 : * documents. */
47 : // clang-format off
48 : static token_rule_t rtrstatus_token_table[] = {
49 : T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
50 : T1( "r", K_R, GE(7), NO_OBJ ),
51 : T0N("a", K_A, GE(1), NO_OBJ ),
52 : T1( "s", K_S, ARGS, NO_OBJ ),
53 : T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
54 : T01("w", K_W, ARGS, NO_OBJ ),
55 : T0N("m", K_M, CONCAT_ARGS, NO_OBJ ),
56 : T0N("id", K_ID, GE(2), NO_OBJ ),
57 : T1("pr", K_PROTO, CONCAT_ARGS, NO_OBJ ),
58 : T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
59 : END_OF_TABLE
60 : };
61 : // clang-format on
62 :
63 : /** List of tokens recognized in V3 networkstatus votes. */
64 : // clang-format off
65 : static token_rule_t networkstatus_token_table[] = {
66 : T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
67 : GE(1), NO_OBJ ),
68 : T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
69 : T1("published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
70 : T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
71 : T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
72 : T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
73 : T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
74 : T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
75 : T01("params", K_PARAMS, ARGS, NO_OBJ ),
76 : T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
77 : T01("signing-ed25519", K_SIGNING_CERT_ED, NO_ARGS , NEED_OBJ ),
78 : T01("shared-rand-participate",K_SR_FLAG, NO_ARGS, NO_OBJ ),
79 : T0N("shared-rand-commit", K_COMMIT, GE(3), NO_OBJ ),
80 : T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2), NO_OBJ ),
81 : T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
82 : T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
83 : T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
84 : CONCAT_ARGS, NO_OBJ ),
85 : T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
86 : CONCAT_ARGS, NO_OBJ ),
87 : T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
88 : CONCAT_ARGS, NO_OBJ ),
89 : T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
90 : CONCAT_ARGS, NO_OBJ ),
91 :
92 : AUTHCERT_MEMBERS,
93 :
94 : T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
95 : T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
96 : T1( "dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
97 : T01("legacy-dir-key", K_LEGACY_DIR_KEY, GE(1), NO_OBJ ),
98 : T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
99 : T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
100 : T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
101 : T1( "consensus-methods", K_CONSENSUS_METHODS, GE(1), NO_OBJ ),
102 :
103 : END_OF_TABLE
104 : };
105 : // clang-format on
106 :
107 : /** List of tokens recognized in V3 networkstatus consensuses. */
108 : // clang-format off
109 : static token_rule_t networkstatus_consensus_token_table[] = {
110 : T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
111 : GE(1), NO_OBJ ),
112 : T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
113 : T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
114 : T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
115 : T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
116 : T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
117 :
118 : T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
119 :
120 : T1N("dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
121 : T1N("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
122 : T1N("vote-digest", K_VOTE_DIGEST, GE(1), NO_OBJ ),
123 :
124 : T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
125 :
126 : T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
127 : T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
128 : T01("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ),
129 : T01("params", K_PARAMS, ARGS, NO_OBJ ),
130 :
131 : T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2), NO_OBJ ),
132 : T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
133 :
134 : T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
135 : CONCAT_ARGS, NO_OBJ ),
136 : T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
137 : CONCAT_ARGS, NO_OBJ ),
138 : T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
139 : CONCAT_ARGS, NO_OBJ ),
140 : T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
141 : CONCAT_ARGS, NO_OBJ ),
142 :
143 : END_OF_TABLE
144 : };
145 : // clang-format on
146 :
147 : /** List of tokens recognized in the footer of v1 directory footers. */
148 : // clang-format off
149 : static token_rule_t networkstatus_vote_footer_token_table[] = {
150 : T01("directory-footer", K_DIRECTORY_FOOTER, NO_ARGS, NO_OBJ ),
151 : T01("bandwidth-weights", K_BW_WEIGHTS, ARGS, NO_OBJ ),
152 : T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
153 : END_OF_TABLE
154 : };
155 : // clang-format on
156 :
157 : /** Try to find the start and end of the signed portion of a networkstatus
158 : * document in <b>s</b>. On success, set <b>start_out</b> to the first
159 : * character of the document, and <b>end_out</b> to a position one after the
160 : * final character of the signed document, and return 0. On failure, return
161 : * -1. */
162 : int
163 2877 : router_get_networkstatus_v3_signed_boundaries(const char *s,
164 : size_t len,
165 : const char **start_out,
166 : const char **end_out)
167 : {
168 2877 : return router_get_hash_impl_helper(s, len,
169 : "network-status-version",
170 : "\ndirectory-signature",
171 : ' ', LOG_INFO,
172 : start_out, end_out);
173 : }
174 :
175 : /** Set <b>digest_out</b> to the SHA3-256 digest of the signed portion of the
176 : * networkstatus vote in <b>s</b> -- or of the entirety of <b>s</b> if no
177 : * signed portion can be identified. Return 0 on success, -1 on failure. */
178 : int
179 2849 : router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
180 : const char *s, size_t len)
181 : {
182 2849 : const char *start, *end;
183 2849 : if (router_get_networkstatus_v3_signed_boundaries(s, len,
184 : &start, &end) < 0) {
185 234 : start = s;
186 234 : end = s + len;
187 : }
188 2849 : tor_assert(start);
189 2849 : tor_assert(end);
190 2849 : return crypto_digest256((char*)digest_out, start, end-start,
191 : DIGEST_SHA3_256);
192 : }
193 :
194 : /** Set <b>digests</b> to all the digests of the consensus document in
195 : * <b>s</b> */
196 : int
197 2500 : router_get_networkstatus_v3_hashes(const char *s, size_t len,
198 : common_digests_t *digests)
199 : {
200 2500 : return router_get_hashes_impl(s, len, digests,
201 : "network-status-version",
202 : "\ndirectory-signature",
203 : ' ');
204 : }
205 :
206 : /** Helper: given a string <b>s</b>, return the start of the next router-status
207 : * object (starting with "r " at the start of a line). If none is found,
208 : * return the start of the directory footer, or the next directory signature.
209 : * If none is found, return the end of the string. */
210 : static inline const char *
211 4811 : find_start_of_next_routerstatus(const char *s, const char *s_eos)
212 : {
213 4811 : const char *eos, *footer, *sig;
214 4811 : if ((eos = tor_memstr(s, s_eos - s, "\nr ")))
215 3666 : ++eos;
216 : else
217 : eos = s_eos;
218 :
219 4811 : footer = tor_memstr(s, eos-s, "\ndirectory-footer");
220 4811 : sig = tor_memstr(s, eos-s, "\ndirectory-signature");
221 :
222 4811 : if (footer && sig)
223 136 : return MIN(footer, sig) + 1;
224 4675 : else if (footer)
225 5 : return footer+1;
226 4670 : else if (sig)
227 1009 : return sig+1;
228 : else
229 : return eos;
230 : }
231 :
232 : /** Parse the GuardFraction string from a consensus or vote.
233 : *
234 : * If <b>vote</b> or <b>vote_rs</b> are set the document getting
235 : * parsed is a vote routerstatus. Otherwise it's a consensus. This is
236 : * the same semantic as in routerstatus_parse_entry_from_string(). */
237 : STATIC int
238 4 : routerstatus_parse_guardfraction(const char *guardfraction_str,
239 : networkstatus_t *vote,
240 : vote_routerstatus_t *vote_rs,
241 : routerstatus_t *rs)
242 : {
243 4 : int ok;
244 4 : const char *end_of_header = NULL;
245 4 : int is_consensus = !vote_rs;
246 4 : uint32_t guardfraction;
247 :
248 4 : tor_assert(bool_eq(vote, vote_rs));
249 :
250 : /* If this info comes from a consensus, but we shouldn't apply
251 : guardfraction, just exit. */
252 4 : if (is_consensus && !should_apply_guardfraction(NULL)) {
253 : return 0;
254 : }
255 :
256 4 : end_of_header = strchr(guardfraction_str, '=');
257 4 : if (!end_of_header) {
258 : return -1;
259 : }
260 :
261 4 : guardfraction = (uint32_t)tor_parse_ulong(end_of_header+1,
262 : 10, 0, 100, &ok, NULL);
263 4 : if (!ok) {
264 2 : log_warn(LD_DIR, "Invalid GuardFraction %s", escaped(guardfraction_str));
265 2 : return -1;
266 : }
267 :
268 2 : log_debug(LD_GENERAL, "[*] Parsed %s guardfraction '%s' for '%s'.",
269 : is_consensus ? "consensus" : "vote",
270 : guardfraction_str, rs->nickname);
271 :
272 2 : if (!is_consensus) { /* We are parsing a vote */
273 0 : vote_rs->status.guardfraction_percentage = guardfraction;
274 0 : vote_rs->status.has_guardfraction = 1;
275 : } else {
276 : /* We are parsing a consensus. Only apply guardfraction to guards. */
277 2 : if (rs->is_possible_guard) {
278 1 : rs->guardfraction_percentage = guardfraction;
279 1 : rs->has_guardfraction = 1;
280 : } else {
281 1 : log_warn(LD_BUG, "Got GuardFraction for non-guard %s. "
282 : "This is not supposed to happen. Not applying. ", rs->nickname);
283 : }
284 : }
285 :
286 : return 0;
287 : }
288 :
289 : /** Given a string at *<b>s</b>, containing a routerstatus object, and an
290 : * empty smartlist at <b>tokens</b>, parse and return the first router status
291 : * object in the string, and advance *<b>s</b> to just after the end of the
292 : * router status. Return NULL and advance *<b>s</b> on error.
293 : *
294 : * If <b>vote</b> and <b>vote_rs</b> are provided, don't allocate a fresh
295 : * routerstatus but use <b>vote_rs</b> instead.
296 : *
297 : * If <b>consensus_method</b> is nonzero, this routerstatus is part of a
298 : * consensus, and we should parse it according to the method used to
299 : * make that consensus.
300 : *
301 : * Parse according to the syntax used by the consensus flavor <b>flav</b>.
302 : **/
303 : STATIC routerstatus_t *
304 2347 : routerstatus_parse_entry_from_string(memarea_t *area,
305 : const char **s, const char *s_eos,
306 : smartlist_t *tokens,
307 : networkstatus_t *vote,
308 : vote_routerstatus_t *vote_rs,
309 : int consensus_method,
310 : consensus_flavor_t flav)
311 : {
312 2347 : const char *eos, *s_dup = *s;
313 2347 : routerstatus_t *rs = NULL;
314 2347 : directory_token_t *tok;
315 2347 : char timebuf[ISO_TIME_LEN+1];
316 2347 : struct in_addr in;
317 2347 : int offset = 0;
318 2347 : tor_assert(tokens);
319 2347 : tor_assert(bool_eq(vote, vote_rs));
320 :
321 2347 : if (!consensus_method)
322 : flav = FLAV_NS;
323 1845 : tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
324 :
325 2347 : eos = find_start_of_next_routerstatus(*s, s_eos);
326 :
327 2347 : if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
328 1286 : log_warn(LD_DIR, "Error tokenizing router status");
329 1286 : goto err;
330 : }
331 1061 : if (smartlist_len(tokens) < 1) {
332 0 : log_warn(LD_DIR, "Impossibly short router status");
333 0 : goto err;
334 : }
335 1061 : tok = find_by_keyword(tokens, K_R);
336 1061 : tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
337 1061 : if (flav == FLAV_NS) {
338 392 : if (tok->n_args < 8) {
339 0 : log_warn(LD_DIR, "Too few arguments to r");
340 0 : goto err;
341 : }
342 : } else if (flav == FLAV_MICRODESC) {
343 : offset = -1; /* There is no descriptor digest in an md consensus r line */
344 : }
345 :
346 1061 : if (vote_rs) {
347 332 : rs = &vote_rs->status;
348 : } else {
349 729 : rs = tor_malloc_zero(sizeof(routerstatus_t));
350 : }
351 :
352 1061 : if (!is_legal_nickname(tok->args[0])) {
353 0 : log_warn(LD_DIR,
354 : "Invalid nickname %s in router status; skipping.",
355 : escaped(tok->args[0]));
356 0 : goto err;
357 : }
358 1061 : strlcpy(rs->nickname, tok->args[0], sizeof(rs->nickname));
359 :
360 1061 : if (digest_from_base64(rs->identity_digest, tok->args[1])) {
361 0 : log_warn(LD_DIR, "Error decoding identity digest %s",
362 : escaped(tok->args[1]));
363 0 : goto err;
364 : }
365 :
366 1061 : if (flav == FLAV_NS) {
367 392 : if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
368 0 : log_warn(LD_DIR, "Error decoding descriptor digest %s",
369 : escaped(tok->args[2]));
370 0 : goto err;
371 : }
372 : }
373 :
374 1061 : if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
375 2121 : tok->args[3+offset], tok->args[4+offset]) < 0 ||
376 1060 : parse_iso_time(timebuf, &rs->published_on)<0) {
377 10 : log_warn(LD_DIR, "Error parsing time '%s %s' [%d %d]",
378 : tok->args[3+offset], tok->args[4+offset],
379 : offset, (int)flav);
380 10 : goto err;
381 : }
382 :
383 1051 : if (tor_inet_aton(tok->args[5+offset], &in) == 0) {
384 10 : log_warn(LD_DIR, "Error parsing router address in network-status %s",
385 : escaped(tok->args[5+offset]));
386 10 : goto err;
387 : }
388 1041 : tor_addr_from_in(&rs->ipv4_addr, &in);
389 :
390 1041 : rs->ipv4_orport = (uint16_t) tor_parse_long(tok->args[6+offset],
391 : 10,0,65535,NULL,NULL);
392 1041 : rs->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[7+offset],
393 : 10,0,65535,NULL,NULL);
394 :
395 : {
396 1041 : smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
397 1041 : if (a_lines) {
398 132 : find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport);
399 132 : smartlist_free(a_lines);
400 : }
401 : }
402 :
403 1041 : tok = find_opt_by_keyword(tokens, K_S);
404 1041 : if (tok && vote) {
405 332 : int i;
406 332 : vote_rs->flags = 0;
407 1828 : for (i=0; i < tok->n_args; ++i) {
408 1496 : int p = smartlist_string_pos(vote->known_flags, tok->args[i]);
409 1496 : if (p >= 0) {
410 1496 : vote_rs->flags |= (UINT64_C(1)<<p);
411 : } else {
412 0 : log_warn(LD_DIR, "Flags line had a flag %s not listed in known_flags.",
413 : escaped(tok->args[i]));
414 0 : goto err;
415 : }
416 : }
417 709 : } else if (tok) {
418 : /* This is a consensus, not a vote. */
419 : int i;
420 2035 : for (i=0; i < tok->n_args; ++i) {
421 1326 : if (!strcmp(tok->args[i], "Exit"))
422 86 : rs->is_exit = 1;
423 1240 : else if (!strcmp(tok->args[i], "Stable"))
424 88 : rs->is_stable = 1;
425 1152 : else if (!strcmp(tok->args[i], "Fast"))
426 88 : rs->is_fast = 1;
427 1064 : else if (!strcmp(tok->args[i], "Running"))
428 158 : rs->is_flagged_running = 1;
429 906 : else if (!strcmp(tok->args[i], "Named"))
430 1 : rs->is_named = 1;
431 905 : else if (!strcmp(tok->args[i], "Valid"))
432 305 : rs->is_valid = 1;
433 600 : else if (!strcmp(tok->args[i], "Guard"))
434 88 : rs->is_possible_guard = 1;
435 512 : else if (!strcmp(tok->args[i], "BadExit"))
436 1 : rs->is_bad_exit = 1;
437 511 : else if (!strcmp(tok->args[i], "Authority"))
438 38 : rs->is_authority = 1;
439 473 : else if (!strcmp(tok->args[i], "Unnamed") &&
440 : consensus_method >= 2) {
441 : /* Unnamed is computed right by consensus method 2 and later. */
442 1 : rs->is_unnamed = 1;
443 472 : } else if (!strcmp(tok->args[i], "HSDir")) {
444 132 : rs->is_hs_dir = 1;
445 340 : } else if (!strcmp(tok->args[i], "V2Dir")) {
446 63 : rs->is_v2_dir = 1;
447 277 : } else if (!strcmp(tok->args[i], "StaleDesc")) {
448 1 : rs->is_staledesc = 1;
449 276 : } else if (!strcmp(tok->args[i], "Sybil")) {
450 0 : rs->is_sybil = 1;
451 : }
452 : }
453 : /* These are implied true by having been included in a consensus made
454 : * with a given method */
455 709 : rs->is_flagged_running = 1; /* Starting with consensus method 4. */
456 709 : rs->is_valid = 1; /* Starting with consensus method 24. */
457 : }
458 : {
459 1041 : const char *protocols = NULL, *version = NULL;
460 1041 : if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
461 1041 : tor_assert(tok->n_args == 1);
462 1041 : protocols = tok->args[0];
463 : }
464 1041 : if ((tok = find_opt_by_keyword(tokens, K_V))) {
465 547 : tor_assert(tok->n_args == 1);
466 547 : version = tok->args[0];
467 547 : if (vote_rs) {
468 331 : vote_rs->version = tor_strdup(tok->args[0]);
469 : }
470 : }
471 :
472 : // If the protover line is malformed, reject this routerstatus.
473 1041 : if (protocols && protover_list_is_invalid(protocols)) {
474 126 : goto err;
475 : }
476 915 : summarize_protover_flags(&rs->pv, protocols, version);
477 : }
478 :
479 : /* handle weighting/bandwidth info */
480 915 : if ((tok = find_opt_by_keyword(tokens, K_W))) {
481 : int i;
482 1151 : for (i=0; i < tok->n_args; ++i) {
483 669 : if (!strcmpstart(tok->args[i], "Bandwidth=")) {
484 481 : int ok;
485 962 : rs->bandwidth_kb =
486 481 : (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
487 : 10, 0, UINT32_MAX,
488 : &ok, NULL);
489 481 : if (!ok) {
490 0 : log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i]));
491 0 : goto err;
492 : }
493 481 : rs->has_bandwidth = 1;
494 188 : } else if (!strcmpstart(tok->args[i], "Measured=") && vote_rs) {
495 78 : int ok;
496 156 : vote_rs->measured_bw_kb =
497 78 : (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
498 : 10, 0, UINT32_MAX, &ok, NULL);
499 78 : if (!ok) {
500 0 : log_warn(LD_DIR, "Invalid Measured Bandwidth %s",
501 : escaped(tok->args[i]));
502 0 : goto err;
503 : }
504 78 : vote_rs->has_measured_bw = 1;
505 78 : vote->has_measured_bws = 1;
506 110 : } else if (!strcmpstart(tok->args[i], "Unmeasured=1")) {
507 108 : rs->bw_is_unmeasured = 1;
508 2 : } else if (!strcmpstart(tok->args[i], "GuardFraction=")) {
509 0 : if (routerstatus_parse_guardfraction(tok->args[i],
510 : vote, vote_rs, rs) < 0) {
511 0 : goto err;
512 : }
513 : }
514 : }
515 : }
516 :
517 : /* parse exit policy summaries */
518 915 : if ((tok = find_opt_by_keyword(tokens, K_P))) {
519 385 : tor_assert(tok->n_args == 1);
520 770 : if (strcmpstart(tok->args[0], "accept ") &&
521 385 : strcmpstart(tok->args[0], "reject ")) {
522 0 : log_warn(LD_DIR, "Unknown exit policy summary type %s.",
523 : escaped(tok->args[0]));
524 0 : goto err;
525 : }
526 : /* XXX weasel: parse this into ports and represent them somehow smart,
527 : * maybe not here but somewhere on if we need it for the client.
528 : * we should still parse it here to check it's valid tho.
529 : */
530 385 : rs->exitsummary = tor_strdup(tok->args[0]);
531 385 : rs->has_exitsummary = 1;
532 : }
533 :
534 915 : if (vote_rs) {
535 3221 : SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, t) {
536 2897 : if (t->tp == K_M && t->n_args) {
537 324 : vote_microdesc_hash_t *line =
538 324 : tor_malloc(sizeof(vote_microdesc_hash_t));
539 324 : line->next = vote_rs->microdesc;
540 324 : line->microdesc_hash_line = tor_strdup(t->args[0]);
541 324 : vote_rs->microdesc = line;
542 : }
543 2897 : if (t->tp == K_ID) {
544 325 : tor_assert(t->n_args >= 2);
545 325 : if (!strcmp(t->args[0], "ed25519")) {
546 325 : vote_rs->has_ed25519_listing = 1;
547 326 : if (strcmp(t->args[1], "none") &&
548 1 : digest256_from_base64((char*)vote_rs->ed25519_id,
549 : t->args[1])<0) {
550 1 : log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote");
551 1 : goto err;
552 : }
553 : }
554 : }
555 2896 : if (t->tp == K_PROTO) {
556 325 : tor_assert(t->n_args == 1);
557 325 : vote_rs->protocols = tor_strdup(t->args[0]);
558 : }
559 2896 : } SMARTLIST_FOREACH_END(t);
560 590 : } else if (flav == FLAV_MICRODESC) {
561 530 : tok = find_opt_by_keyword(tokens, K_M);
562 530 : if (tok) {
563 111 : tor_assert(tok->n_args);
564 111 : if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
565 0 : log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
566 : escaped(tok->args[0]));
567 0 : goto err;
568 : }
569 : } else {
570 419 : log_info(LD_BUG, "Found an entry in networkstatus with no "
571 : "microdescriptor digest. (Router %s ($%s) at %s:%d.)",
572 : rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN),
573 : fmt_addr(&rs->ipv4_addr), rs->ipv4_orport);
574 : }
575 : }
576 :
577 914 : if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
578 0 : rs->is_named = 0;
579 :
580 914 : goto done;
581 1433 : err:
582 1433 : dump_desc(s_dup, "routerstatus entry");
583 1433 : if (rs && !vote_rs)
584 139 : routerstatus_free(rs);
585 : rs = NULL;
586 2347 : done:
587 36088 : SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
588 2347 : smartlist_clear(tokens);
589 2347 : if (area) {
590 2347 : DUMP_AREA(area, "routerstatus entry");
591 2347 : memarea_clear(area);
592 : }
593 2347 : *s = eos;
594 :
595 2347 : return rs;
596 : }
597 :
598 : int
599 1 : compare_vote_routerstatus_entries(const void **_a, const void **_b)
600 : {
601 1 : const vote_routerstatus_t *a = *_a, *b = *_b;
602 1 : return fast_memcmp(a->status.identity_digest, b->status.identity_digest,
603 : DIGEST_LEN);
604 : }
605 :
606 : /** Verify the bandwidth weights of a network status document */
607 : int
608 24 : networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
609 : {
610 24 : int64_t G=0, M=0, E=0, D=0, T=0;
611 24 : double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
612 24 : double Gtotal=0, Mtotal=0, Etotal=0;
613 24 : const char *casename = NULL;
614 24 : int valid = 1;
615 24 : (void) consensus_method;
616 :
617 24 : const int64_t weight_scale = networkstatus_get_weight_scale_param(ns);
618 24 : tor_assert(weight_scale >= 1);
619 24 : Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
620 24 : Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
621 24 : Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
622 24 : Wmg = networkstatus_get_bw_weight(ns, "Wmg", -1);
623 24 : Wmm = networkstatus_get_bw_weight(ns, "Wmm", -1);
624 24 : Wme = networkstatus_get_bw_weight(ns, "Wme", -1);
625 24 : Wmd = networkstatus_get_bw_weight(ns, "Wmd", -1);
626 24 : Weg = networkstatus_get_bw_weight(ns, "Weg", -1);
627 24 : Wem = networkstatus_get_bw_weight(ns, "Wem", -1);
628 24 : Wee = networkstatus_get_bw_weight(ns, "Wee", -1);
629 24 : Wed = networkstatus_get_bw_weight(ns, "Wed", -1);
630 :
631 24 : if (Wgg<0 || Wgm<0 || Wgd<0 || Wmg<0 || Wmm<0 || Wme<0 || Wmd<0 || Weg<0
632 24 : || Wem<0 || Wee<0 || Wed<0) {
633 0 : log_warn(LD_BUG, "No bandwidth weights produced in consensus!");
634 0 : return 0;
635 : }
636 :
637 : // First, sanity check basic summing properties that hold for all cases
638 : // We use > 1 as the check for these because they are computed as integers.
639 : // Sometimes there are rounding errors.
640 24 : if (fabs(Wmm - weight_scale) > 1) {
641 0 : log_warn(LD_BUG, "Wmm=%f != %"PRId64,
642 : Wmm, (weight_scale));
643 0 : valid = 0;
644 : }
645 :
646 24 : if (fabs(Wem - Wee) > 1) {
647 0 : log_warn(LD_BUG, "Wem=%f != Wee=%f", Wem, Wee);
648 0 : valid = 0;
649 : }
650 :
651 24 : if (fabs(Wgm - Wgg) > 1) {
652 0 : log_warn(LD_BUG, "Wgm=%f != Wgg=%f", Wgm, Wgg);
653 0 : valid = 0;
654 : }
655 :
656 24 : if (fabs(Weg - Wed) > 1) {
657 0 : log_warn(LD_BUG, "Wed=%f != Weg=%f", Wed, Weg);
658 0 : valid = 0;
659 : }
660 :
661 24 : if (fabs(Wgg + Wmg - weight_scale) > 0.001*weight_scale) {
662 0 : log_warn(LD_BUG, "Wgg=%f != %"PRId64" - Wmg=%f", Wgg,
663 : (weight_scale), Wmg);
664 0 : valid = 0;
665 : }
666 :
667 24 : if (fabs(Wee + Wme - weight_scale) > 0.001*weight_scale) {
668 0 : log_warn(LD_BUG, "Wee=%f != %"PRId64" - Wme=%f", Wee,
669 : (weight_scale), Wme);
670 0 : valid = 0;
671 : }
672 :
673 24 : if (fabs(Wgd + Wmd + Wed - weight_scale) > 0.001*weight_scale) {
674 0 : log_warn(LD_BUG, "Wgd=%f + Wmd=%f + Wed=%f != %"PRId64,
675 : Wgd, Wmd, Wed, (weight_scale));
676 0 : valid = 0;
677 : }
678 :
679 24 : Wgg /= weight_scale;
680 24 : Wgm /= weight_scale; (void) Wgm; // unused from here on.
681 24 : Wgd /= weight_scale;
682 :
683 24 : Wmg /= weight_scale;
684 24 : Wmm /= weight_scale;
685 24 : Wme /= weight_scale;
686 24 : Wmd /= weight_scale;
687 :
688 24 : Weg /= weight_scale; (void) Weg; // unused from here on.
689 24 : Wem /= weight_scale; (void) Wem; // unused from here on.
690 24 : Wee /= weight_scale;
691 24 : Wed /= weight_scale;
692 :
693 : // Then, gather G, M, E, D, T to determine case
694 102 : SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
695 78 : int is_exit = 0;
696 : /* Bug #2203: Don't count bad exits as exits for balancing */
697 78 : is_exit = rs->is_exit && !rs->is_bad_exit;
698 78 : if (rs->has_bandwidth) {
699 78 : T += rs->bandwidth_kb;
700 78 : if (is_exit && rs->is_possible_guard) {
701 42 : D += rs->bandwidth_kb;
702 42 : Gtotal += Wgd*rs->bandwidth_kb;
703 42 : Mtotal += Wmd*rs->bandwidth_kb;
704 42 : Etotal += Wed*rs->bandwidth_kb;
705 36 : } else if (is_exit) {
706 0 : E += rs->bandwidth_kb;
707 0 : Mtotal += Wme*rs->bandwidth_kb;
708 0 : Etotal += Wee*rs->bandwidth_kb;
709 36 : } else if (rs->is_possible_guard) {
710 0 : G += rs->bandwidth_kb;
711 0 : Gtotal += Wgg*rs->bandwidth_kb;
712 0 : Mtotal += Wmg*rs->bandwidth_kb;
713 : } else {
714 36 : M += rs->bandwidth_kb;
715 36 : Mtotal += Wmm*rs->bandwidth_kb;
716 : }
717 : } else {
718 0 : log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
719 : routerstatus_describe(rs));
720 : }
721 78 : } SMARTLIST_FOREACH_END(rs);
722 :
723 : // Finally, check equality conditions depending upon case 1, 2 or 3
724 : // Full equality cases: 1, 3b
725 : // Partial equality cases: 2b (E=G), 3a (M=E)
726 : // Fully unknown: 2a
727 24 : if (3*E >= T && 3*G >= T) {
728 : // Case 1: Neither are scarce
729 12 : casename = "Case 1";
730 24 : if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
731 0 : log_warn(LD_DIR,
732 : "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
733 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
734 : " T=%"PRId64". "
735 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
736 : casename, Etotal, Mtotal,
737 : (G), (M), (E),
738 : (D), (T),
739 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
740 0 : valid = 0;
741 : }
742 24 : if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
743 0 : log_warn(LD_DIR,
744 : "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
745 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
746 : " T=%"PRId64". "
747 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
748 : casename, Etotal, Gtotal,
749 : (G), (M), (E),
750 : (D), (T),
751 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
752 0 : valid = 0;
753 : }
754 24 : if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
755 0 : log_warn(LD_DIR,
756 : "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
757 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
758 : " T=%"PRId64". "
759 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
760 : casename, Mtotal, Gtotal,
761 : (G), (M), (E),
762 : (D), (T),
763 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
764 0 : valid = 0;
765 : }
766 12 : } else if (3*E < T && 3*G < T) {
767 12 : int64_t R = MIN(E, G);
768 12 : int64_t S = MAX(E, G);
769 : /*
770 : * Case 2: Both Guards and Exits are scarce
771 : * Balance D between E and G, depending upon
772 : * D capacity and scarcity. Devote no extra
773 : * bandwidth to middle nodes.
774 : */
775 12 : if (R+D < S) { // Subcase a
776 0 : double Rtotal, Stotal;
777 0 : if (E < G) {
778 : Rtotal = Etotal;
779 : Stotal = Gtotal;
780 : } else {
781 0 : Rtotal = Gtotal;
782 0 : Stotal = Etotal;
783 : }
784 0 : casename = "Case 2a";
785 : // Rtotal < Stotal
786 0 : if (Rtotal > Stotal) {
787 0 : log_warn(LD_DIR,
788 : "Bw Weight Failure for %s: Rtotal %f > Stotal %f. "
789 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
790 : " T=%"PRId64". "
791 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
792 : casename, Rtotal, Stotal,
793 : (G), (M), (E),
794 : (D), (T),
795 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
796 0 : valid = 0;
797 : }
798 : // Rtotal < T/3
799 0 : if (3*Rtotal > T) {
800 0 : log_warn(LD_DIR,
801 : "Bw Weight Failure for %s: 3*Rtotal %f > T "
802 : "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
803 : " D=%"PRId64" T=%"PRId64". "
804 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
805 : casename, Rtotal*3, (T),
806 : (G), (M), (E),
807 : (D), (T),
808 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
809 0 : valid = 0;
810 : }
811 : // Stotal < T/3
812 0 : if (3*Stotal > T) {
813 0 : log_warn(LD_DIR,
814 : "Bw Weight Failure for %s: 3*Stotal %f > T "
815 : "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
816 : " D=%"PRId64" T=%"PRId64". "
817 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
818 : casename, Stotal*3, (T),
819 : (G), (M), (E),
820 : (D), (T),
821 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
822 0 : valid = 0;
823 : }
824 : // Mtotal > T/3
825 0 : if (3*Mtotal < T) {
826 0 : log_warn(LD_DIR,
827 : "Bw Weight Failure for %s: 3*Mtotal %f < T "
828 : "%"PRId64". "
829 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
830 : " T=%"PRId64". "
831 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
832 : casename, Mtotal*3, (T),
833 : (G), (M), (E),
834 : (D), (T),
835 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
836 0 : valid = 0;
837 : }
838 : } else { // Subcase b: R+D > S
839 12 : casename = "Case 2b";
840 :
841 : /* Check the rare-M redirect case. */
842 12 : if (D != 0 && 3*M < T) {
843 12 : casename = "Case 2b (balanced)";
844 24 : if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
845 6 : log_warn(LD_DIR,
846 : "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
847 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
848 : " T=%"PRId64". "
849 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
850 : casename, Etotal, Mtotal,
851 : (G), (M), (E),
852 : (D), (T),
853 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
854 6 : valid = 0;
855 : }
856 24 : if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
857 0 : log_warn(LD_DIR,
858 : "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
859 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
860 : " T=%"PRId64". "
861 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
862 : casename, Etotal, Gtotal,
863 : (G), (M), (E),
864 : (D), (T),
865 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
866 0 : valid = 0;
867 : }
868 24 : if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
869 6 : log_warn(LD_DIR,
870 : "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
871 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
872 : " T=%"PRId64". "
873 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
874 : casename, Mtotal, Gtotal,
875 : (G), (M), (E),
876 : (D), (T),
877 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
878 6 : valid = 0;
879 : }
880 : } else {
881 0 : if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
882 0 : log_warn(LD_DIR,
883 : "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
884 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
885 : " T=%"PRId64". "
886 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
887 : casename, Etotal, Gtotal,
888 : (G), (M), (E),
889 : (D), (T),
890 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
891 0 : valid = 0;
892 : }
893 : }
894 : }
895 : } else { // if (E < T/3 || G < T/3) {
896 0 : int64_t S = MIN(E, G);
897 0 : int64_t NS = MAX(E, G);
898 0 : if (3*(S+D) < T) { // Subcase a:
899 0 : double Stotal;
900 0 : double NStotal;
901 0 : if (G < E) {
902 : casename = "Case 3a (G scarce)";
903 : Stotal = Gtotal;
904 : NStotal = Etotal;
905 : } else { // if (G >= E) {
906 0 : casename = "Case 3a (E scarce)";
907 0 : NStotal = Gtotal;
908 0 : Stotal = Etotal;
909 : }
910 : // Stotal < T/3
911 0 : if (3*Stotal > T) {
912 0 : log_warn(LD_DIR,
913 : "Bw Weight Failure for %s: 3*Stotal %f > T "
914 : "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
915 : " D=%"PRId64" T=%"PRId64". "
916 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
917 : casename, Stotal*3, (T),
918 : (G), (M), (E),
919 : (D), (T),
920 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
921 0 : valid = 0;
922 : }
923 0 : if (NS >= M) {
924 0 : if (fabs(NStotal-Mtotal) > 0.01*MAX(NStotal,Mtotal)) {
925 0 : log_warn(LD_DIR,
926 : "Bw Weight Failure for %s: NStotal %f != Mtotal %f. "
927 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
928 : " T=%"PRId64". "
929 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
930 : casename, NStotal, Mtotal,
931 : (G), (M), (E),
932 : (D), (T),
933 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
934 0 : valid = 0;
935 : }
936 : } else {
937 : // if NS < M, NStotal > T/3 because only one of G or E is scarce
938 0 : if (3*NStotal < T) {
939 0 : log_warn(LD_DIR,
940 : "Bw Weight Failure for %s: 3*NStotal %f < T "
941 : "%"PRId64". G=%"PRId64" M=%"PRId64
942 : " E=%"PRId64" D=%"PRId64" T=%"PRId64". "
943 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
944 : casename, NStotal*3, (T),
945 : (G), (M), (E),
946 : (D), (T),
947 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
948 0 : valid = 0;
949 : }
950 : }
951 : } else { // Subcase b: S+D >= T/3
952 0 : casename = "Case 3b";
953 0 : if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
954 0 : log_warn(LD_DIR,
955 : "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
956 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
957 : " T=%"PRId64". "
958 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
959 : casename, Etotal, Mtotal,
960 : (G), (M), (E),
961 : (D), (T),
962 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
963 0 : valid = 0;
964 : }
965 0 : if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
966 0 : log_warn(LD_DIR,
967 : "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
968 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
969 : " T=%"PRId64". "
970 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
971 : casename, Etotal, Gtotal,
972 : (G), (M), (E),
973 : (D), (T),
974 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
975 0 : valid = 0;
976 : }
977 0 : if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
978 0 : log_warn(LD_DIR,
979 : "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
980 : "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
981 : " T=%"PRId64". "
982 : "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
983 : casename, Mtotal, Gtotal,
984 : (G), (M), (E),
985 : (D), (T),
986 : Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
987 0 : valid = 0;
988 : }
989 : }
990 : }
991 :
992 24 : if (valid)
993 18 : log_notice(LD_DIR, "Bandwidth-weight %s is verified and valid.",
994 : casename);
995 :
996 : return valid;
997 : }
998 :
999 : /** Check if a shared random value of type <b>srv_type</b> is in
1000 : * <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
1001 : * -1 on failure, 0 on success. The resulting srv is allocated on the heap and
1002 : * it's the responsibility of the caller to free it. */
1003 : static int
1004 3422 : extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
1005 : sr_srv_t **srv_out)
1006 : {
1007 3422 : int ret = -1;
1008 3422 : directory_token_t *tok;
1009 3422 : sr_srv_t *srv = NULL;
1010 3422 : smartlist_t *chunks;
1011 :
1012 3422 : tor_assert(tokens);
1013 :
1014 3422 : chunks = smartlist_new();
1015 3422 : tok = find_opt_by_keyword(tokens, srv_type);
1016 3422 : if (!tok) {
1017 : /* That's fine, no SRV is allowed. */
1018 3408 : ret = 0;
1019 3408 : goto end;
1020 : }
1021 42 : for (int i = 0; i < tok->n_args; i++) {
1022 28 : smartlist_add(chunks, tok->args[i]);
1023 : }
1024 14 : srv = sr_parse_srv(chunks);
1025 14 : if (srv == NULL) {
1026 13 : log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
1027 13 : goto end;
1028 : }
1029 : /* All is good. */
1030 1 : *srv_out = srv;
1031 1 : ret = 0;
1032 3422 : end:
1033 3422 : smartlist_free(chunks);
1034 3422 : return ret;
1035 : }
1036 :
1037 : /** Extract any shared random values found in <b>tokens</b> and place them in
1038 : * the networkstatus <b>ns</b>. */
1039 : static void
1040 1711 : extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
1041 : {
1042 1711 : const char *voter_identity;
1043 1711 : networkstatus_voter_info_t *voter;
1044 :
1045 1711 : tor_assert(ns);
1046 1711 : tor_assert(tokens);
1047 : /* Can be only one of them else code flow. */
1048 1711 : tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
1049 :
1050 1711 : if (ns->type == NS_TYPE_VOTE) {
1051 320 : voter = smartlist_get(ns->voters, 0);
1052 320 : tor_assert(voter);
1053 320 : voter_identity = hex_str(voter->identity_digest,
1054 : sizeof(voter->identity_digest));
1055 : } else {
1056 : /* Consensus has multiple voters so no specific voter. */
1057 : voter_identity = "consensus";
1058 : }
1059 :
1060 : /* We extract both, and on error everything is stopped because it means
1061 : * the vote is malformed for the shared random value(s). */
1062 1711 : if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
1063 8 : log_warn(LD_DIR, "SR: Unable to parse previous SRV from %s",
1064 : voter_identity);
1065 : /* Maybe we have a chance with the current SRV so let's try it anyway. */
1066 : }
1067 1711 : if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
1068 5 : log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
1069 : voter_identity);
1070 : }
1071 1711 : }
1072 :
1073 : /** Allocate a copy of a protover line, if present. If present but malformed,
1074 : * set *error to true. */
1075 : static char *
1076 8808 : dup_protocols_string(smartlist_t *tokens, bool *error, directory_keyword kw)
1077 : {
1078 8808 : directory_token_t *tok = find_opt_by_keyword(tokens, kw);
1079 8808 : if (!tok)
1080 : return NULL;
1081 642 : if (protover_list_is_invalid(tok->args[0]))
1082 47 : *error = true;
1083 642 : return tor_strdup(tok->args[0]);
1084 : }
1085 :
1086 : /** Parse a v3 networkstatus vote, opinion, or consensus (depending on
1087 : * ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
1088 : networkstatus_t *
1089 2500 : networkstatus_parse_vote_from_string(const char *s,
1090 : size_t s_len,
1091 : const char **eos_out,
1092 : networkstatus_type_t ns_type)
1093 : {
1094 2500 : smartlist_t *tokens = smartlist_new();
1095 2500 : smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
1096 2500 : networkstatus_voter_info_t *voter = NULL;
1097 2500 : networkstatus_t *ns = NULL;
1098 2500 : common_digests_t ns_digests;
1099 2500 : uint8_t sha3_as_signed[DIGEST256_LEN];
1100 2500 : const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
1101 2500 : directory_token_t *tok;
1102 2500 : struct in_addr in;
1103 2500 : int i, inorder, n_signatures = 0;
1104 2500 : memarea_t *area = NULL, *rs_area = NULL;
1105 2500 : consensus_flavor_t flav = FLAV_NS;
1106 2500 : char *last_kwd=NULL;
1107 2500 : const char *eos = s + s_len;
1108 :
1109 2500 : tor_assert(s);
1110 :
1111 2500 : if (eos_out)
1112 2398 : *eos_out = NULL;
1113 :
1114 4964 : if (router_get_networkstatus_v3_hashes(s, s_len, &ns_digests) ||
1115 2464 : router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed,
1116 : s, s_len)<0) {
1117 36 : log_warn(LD_DIR, "Unable to compute digest of network-status");
1118 36 : goto err;
1119 : }
1120 :
1121 2464 : area = memarea_new();
1122 2464 : end_of_header = find_start_of_next_routerstatus(s, eos);
1123 2914 : if (tokenize_string(area, s, end_of_header, tokens,
1124 : (ns_type == NS_TYPE_CONSENSUS) ?
1125 : networkstatus_consensus_token_table :
1126 : networkstatus_token_table, 0)) {
1127 192 : log_warn(LD_DIR, "Error tokenizing network-status header");
1128 192 : goto err;
1129 : }
1130 :
1131 2272 : ns = tor_malloc_zero(sizeof(networkstatus_t));
1132 2272 : memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
1133 2272 : memcpy(&ns->digest_sha3_as_signed, sha3_as_signed, sizeof(sha3_as_signed));
1134 :
1135 2272 : tok = find_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
1136 2272 : tor_assert(tok);
1137 2272 : if (tok->n_args > 1) {
1138 1586 : int flavor = networkstatus_parse_flavor_name(tok->args[1]);
1139 1586 : if (flavor < 0) {
1140 9 : log_warn(LD_DIR, "Can't parse document with unknown flavor %s",
1141 : escaped(tok->args[1]));
1142 9 : goto err;
1143 : }
1144 1577 : ns->flavor = flav = flavor;
1145 : }
1146 2263 : if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) {
1147 1 : log_warn(LD_DIR, "Flavor found on non-consensus networkstatus.");
1148 1 : goto err;
1149 : }
1150 :
1151 2262 : if (ns_type != NS_TYPE_CONSENSUS) {
1152 421 : const char *end_of_cert = NULL;
1153 421 : if (!(cert = tor_memstr(s, end_of_header - s,
1154 : "\ndir-key-certificate-version")))
1155 39 : goto err;
1156 420 : ++cert;
1157 420 : ns->cert = authority_cert_parse_from_string(cert, end_of_header - cert,
1158 : &end_of_cert);
1159 420 : if (!ns->cert || !end_of_cert || end_of_cert > end_of_header)
1160 38 : goto err;
1161 : }
1162 :
1163 2223 : tok = find_by_keyword(tokens, K_VOTE_STATUS);
1164 2223 : tor_assert(tok->n_args);
1165 2223 : if (!strcmp(tok->args[0], "vote")) {
1166 381 : ns->type = NS_TYPE_VOTE;
1167 1842 : } else if (!strcmp(tok->args[0], "consensus")) {
1168 1833 : ns->type = NS_TYPE_CONSENSUS;
1169 9 : } else if (!strcmp(tok->args[0], "opinion")) {
1170 1 : ns->type = NS_TYPE_OPINION;
1171 : } else {
1172 8 : log_warn(LD_DIR, "Unrecognized vote status %s in network-status",
1173 : escaped(tok->args[0]));
1174 8 : goto err;
1175 : }
1176 2215 : if (ns_type != ns->type) {
1177 2 : log_warn(LD_DIR, "Got the wrong kind of v3 networkstatus.");
1178 2 : goto err;
1179 : }
1180 :
1181 2213 : if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_OPINION) {
1182 380 : tok = find_by_keyword(tokens, K_PUBLISHED);
1183 380 : if (parse_iso_time(tok->args[0], &ns->published))
1184 9 : goto err;
1185 :
1186 371 : ns->supported_methods = smartlist_new();
1187 371 : tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHODS);
1188 371 : if (tok) {
1189 3216 : for (i=0; i < tok->n_args; ++i)
1190 2845 : smartlist_add_strdup(ns->supported_methods, tok->args[i]);
1191 : } else {
1192 0 : smartlist_add_strdup(ns->supported_methods, "1");
1193 : }
1194 : } else {
1195 1833 : tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHOD);
1196 1833 : if (tok) {
1197 228 : int num_ok;
1198 228 : ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX,
1199 : &num_ok, NULL);
1200 228 : if (!num_ok)
1201 2 : goto err;
1202 : } else {
1203 1605 : ns->consensus_method = 1;
1204 : }
1205 : }
1206 :
1207 : // Reject the vote if any of the protocols lines are malformed.
1208 2202 : bool unparseable = false;
1209 2202 : ns->recommended_client_protocols = dup_protocols_string(tokens, &unparseable,
1210 : K_RECOMMENDED_CLIENT_PROTOCOLS);
1211 2202 : ns->recommended_relay_protocols = dup_protocols_string(tokens, &unparseable,
1212 : K_RECOMMENDED_RELAY_PROTOCOLS);
1213 2202 : ns->required_client_protocols = dup_protocols_string(tokens, &unparseable,
1214 : K_REQUIRED_CLIENT_PROTOCOLS);
1215 2202 : ns->required_relay_protocols = dup_protocols_string(tokens, &unparseable,
1216 : K_REQUIRED_RELAY_PROTOCOLS);
1217 2202 : if (unparseable)
1218 39 : goto err;
1219 :
1220 2163 : tok = find_by_keyword(tokens, K_VALID_AFTER);
1221 2163 : if (parse_iso_time(tok->args[0], &ns->valid_after))
1222 20 : goto err;
1223 :
1224 2143 : tok = find_by_keyword(tokens, K_FRESH_UNTIL);
1225 2143 : if (parse_iso_time(tok->args[0], &ns->fresh_until))
1226 17 : goto err;
1227 :
1228 2126 : tok = find_by_keyword(tokens, K_VALID_UNTIL);
1229 2126 : if (parse_iso_time(tok->args[0], &ns->valid_until))
1230 11 : goto err;
1231 :
1232 2115 : tok = find_by_keyword(tokens, K_VOTING_DELAY);
1233 2115 : tor_assert(tok->n_args >= 2);
1234 : {
1235 2115 : int ok;
1236 4230 : ns->vote_seconds =
1237 2115 : (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
1238 2115 : if (!ok)
1239 6 : goto err;
1240 4224 : ns->dist_seconds =
1241 2112 : (int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
1242 2112 : if (!ok)
1243 3 : goto err;
1244 : }
1245 4218 : if (ns->valid_after +
1246 2109 : (get_options()->TestingTorNetwork ?
1247 4218 : MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL) > ns->fresh_until) {
1248 302 : log_warn(LD_DIR, "Vote/consensus freshness interval is too short");
1249 302 : goto err;
1250 : }
1251 3614 : if (ns->valid_after +
1252 1807 : (get_options()->TestingTorNetwork ?
1253 3614 : MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL)*2 > ns->valid_until) {
1254 7 : log_warn(LD_DIR, "Vote/consensus liveness interval is too short");
1255 7 : goto err;
1256 : }
1257 1800 : if (ns->vote_seconds < MIN_VOTE_SECONDS) {
1258 1 : log_warn(LD_DIR, "Vote seconds is too short");
1259 1 : goto err;
1260 : }
1261 1799 : if (ns->dist_seconds < MIN_DIST_SECONDS) {
1262 1 : log_warn(LD_DIR, "Dist seconds is too short");
1263 1 : goto err;
1264 : }
1265 :
1266 1798 : if ((tok = find_opt_by_keyword(tokens, K_CLIENT_VERSIONS))) {
1267 405 : ns->client_versions = tor_strdup(tok->args[0]);
1268 : }
1269 1798 : if ((tok = find_opt_by_keyword(tokens, K_SERVER_VERSIONS))) {
1270 310 : ns->server_versions = tor_strdup(tok->args[0]);
1271 : }
1272 :
1273 : {
1274 1798 : smartlist_t *package_lst = find_all_by_keyword(tokens, K_PACKAGE);
1275 1798 : ns->package_lines = smartlist_new();
1276 1798 : if (package_lst) {
1277 204 : SMARTLIST_FOREACH(package_lst, directory_token_t *, t,
1278 : smartlist_add_strdup(ns->package_lines, t->args[0]));
1279 : }
1280 1798 : smartlist_free(package_lst);
1281 : }
1282 :
1283 1798 : tok = find_by_keyword(tokens, K_KNOWN_FLAGS);
1284 1798 : ns->known_flags = smartlist_new();
1285 1798 : inorder = 1;
1286 5177 : for (i = 0; i < tok->n_args; ++i) {
1287 3379 : smartlist_add_strdup(ns->known_flags, tok->args[i]);
1288 3379 : if (i>0 && strcmp(tok->args[i-1], tok->args[i])>= 0) {
1289 302 : log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
1290 302 : inorder = 0;
1291 : }
1292 : }
1293 1798 : if (!inorder) {
1294 9 : log_warn(LD_DIR, "known-flags not in order");
1295 9 : goto err;
1296 : }
1297 1789 : if (ns->type != NS_TYPE_CONSENSUS &&
1298 326 : smartlist_len(ns->known_flags) > MAX_KNOWN_FLAGS_IN_VOTE) {
1299 : /* If we allowed more than 64 flags in votes, then parsing them would make
1300 : * us invoke undefined behavior whenever we used 1<<flagnum to do a
1301 : * bit-shift. This is only for votes and opinions: consensus users don't
1302 : * care about flags they don't recognize, and so don't build a bitfield
1303 : * for them. */
1304 0 : log_warn(LD_DIR, "Too many known-flags in consensus vote or opinion");
1305 0 : goto err;
1306 : }
1307 :
1308 1789 : tok = find_opt_by_keyword(tokens, K_PARAMS);
1309 1789 : if (tok) {
1310 361 : int any_dups = 0;
1311 361 : inorder = 1;
1312 361 : ns->net_params = smartlist_new();
1313 3009 : for (i = 0; i < tok->n_args; ++i) {
1314 2657 : int ok=0;
1315 2657 : char *eq = strchr(tok->args[i], '=');
1316 2657 : size_t eq_pos;
1317 2657 : if (!eq) {
1318 5 : log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
1319 9 : goto err;
1320 : }
1321 2652 : eq_pos = eq-tok->args[i];
1322 2652 : tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
1323 2652 : if (!ok) {
1324 4 : log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
1325 4 : goto err;
1326 : }
1327 2648 : if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) {
1328 437 : log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
1329 437 : inorder = 0;
1330 : }
1331 2648 : if (last_kwd && eq_pos == strlen(last_kwd) &&
1332 758 : fast_memeq(last_kwd, tok->args[i], eq_pos)) {
1333 278 : log_warn(LD_DIR, "Duplicate value for %s parameter",
1334 : escaped(tok->args[i]));
1335 278 : any_dups = 1;
1336 : }
1337 2648 : tor_free(last_kwd);
1338 2648 : last_kwd = tor_strndup(tok->args[i], eq_pos);
1339 2648 : smartlist_add_strdup(ns->net_params, tok->args[i]);
1340 : }
1341 352 : if (!inorder) {
1342 13 : log_warn(LD_DIR, "params not in order");
1343 13 : goto err;
1344 : }
1345 339 : if (any_dups) {
1346 2 : log_warn(LD_DIR, "Duplicate in parameters");
1347 2 : goto err;
1348 : }
1349 : }
1350 :
1351 1765 : ns->voters = smartlist_new();
1352 :
1353 42762 : SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
1354 41051 : tok = _tok;
1355 41051 : if (tok->tp == K_DIR_SOURCE) {
1356 3547 : tor_assert(tok->n_args >= 6);
1357 :
1358 3547 : if (voter)
1359 1789 : smartlist_add(ns->voters, voter);
1360 3547 : voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
1361 3547 : voter->sigs = smartlist_new();
1362 3547 : if (ns->type != NS_TYPE_CONSENSUS)
1363 326 : memcpy(voter->vote_digest, ns_digests.d[DIGEST_SHA1], DIGEST_LEN);
1364 :
1365 3547 : voter->nickname = tor_strdup(tok->args[0]);
1366 7085 : if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
1367 3538 : base16_decode(voter->identity_digest, sizeof(voter->identity_digest),
1368 : tok->args[1], HEX_DIGEST_LEN)
1369 : != sizeof(voter->identity_digest)) {
1370 16 : log_warn(LD_DIR, "Error decoding identity digest %s in "
1371 : "network-status document.", escaped(tok->args[1]));
1372 32 : goto err;
1373 : }
1374 3853 : if (ns->type != NS_TYPE_CONSENSUS &&
1375 322 : tor_memneq(ns->cert->cache_info.identity_digest,
1376 : voter->identity_digest, DIGEST_LEN)) {
1377 2 : log_warn(LD_DIR,"Mismatch between identities in certificate and vote");
1378 2 : goto err;
1379 : }
1380 3529 : if (ns->type != NS_TYPE_CONSENSUS) {
1381 320 : if (authority_cert_is_denylisted(ns->cert)) {
1382 0 : log_warn(LD_DIR, "Rejecting vote signature made with denylisted "
1383 : "signing key %s",
1384 : hex_str(ns->cert->signing_key_digest, DIGEST_LEN));
1385 0 : goto err;
1386 : }
1387 : }
1388 3529 : voter->address = tor_strdup(tok->args[2]);
1389 3529 : if (!tor_inet_aton(tok->args[3], &in)) {
1390 11 : log_warn(LD_DIR, "Error decoding IP address %s in network-status.",
1391 : escaped(tok->args[3]));
1392 11 : goto err;
1393 : }
1394 3518 : tor_addr_from_in(&voter->ipv4_addr, &in);
1395 3518 : int ok;
1396 7036 : voter->ipv4_dirport = (uint16_t)
1397 3518 : tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
1398 3518 : if (!ok)
1399 0 : goto err;
1400 7036 : voter->ipv4_orport = (uint16_t)
1401 3518 : tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL);
1402 3518 : if (!ok)
1403 3 : goto err;
1404 37504 : } else if (tok->tp == K_CONTACT) {
1405 2973 : if (!voter || voter->contact) {
1406 6 : log_warn(LD_DIR, "contact element is out of place.");
1407 6 : goto err;
1408 : }
1409 2967 : voter->contact = tor_strdup(tok->args[0]);
1410 34531 : } else if (tok->tp == K_VOTE_DIGEST) {
1411 2812 : tor_assert(ns->type == NS_TYPE_CONSENSUS);
1412 2812 : tor_assert(tok->n_args >= 1);
1413 2812 : if (!voter || ! tor_digest_is_zero(voter->vote_digest)) {
1414 7 : log_warn(LD_DIR, "vote-digest element is out of place.");
1415 7 : goto err;
1416 : }
1417 5602 : if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
1418 2797 : base16_decode(voter->vote_digest, sizeof(voter->vote_digest),
1419 : tok->args[0], HEX_DIGEST_LEN)
1420 : != sizeof(voter->vote_digest)) {
1421 9 : log_warn(LD_DIR, "Error decoding vote digest %s in "
1422 : "network-status consensus.", escaped(tok->args[0]));
1423 9 : goto err;
1424 : }
1425 : }
1426 40997 : } SMARTLIST_FOREACH_END(_tok);
1427 1711 : if (voter) {
1428 1711 : smartlist_add(ns->voters, voter);
1429 1711 : voter = NULL;
1430 : }
1431 1711 : if (smartlist_len(ns->voters) == 0) {
1432 0 : log_warn(LD_DIR, "Missing dir-source elements in a networkstatus.");
1433 0 : goto err;
1434 1711 : } else if (ns->type != NS_TYPE_CONSENSUS && smartlist_len(ns->voters) != 1) {
1435 0 : log_warn(LD_DIR, "Too many dir-source elements in a vote networkstatus.");
1436 0 : goto err;
1437 : }
1438 :
1439 1711 : if (ns->type != NS_TYPE_CONSENSUS &&
1440 320 : (tok = find_opt_by_keyword(tokens, K_LEGACY_DIR_KEY))) {
1441 48 : int bad = 1;
1442 48 : if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
1443 47 : networkstatus_voter_info_t *voter_0 = smartlist_get(ns->voters, 0);
1444 47 : if (base16_decode(voter_0->legacy_id_digest, DIGEST_LEN,
1445 : tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN)
1446 : bad = 1;
1447 : else
1448 : bad = 0;
1449 : }
1450 : if (bad) {
1451 2 : log_warn(LD_DIR, "Invalid legacy key digest %s on vote.",
1452 : escaped(tok->args[0]));
1453 : }
1454 : }
1455 :
1456 : /* If this is a vote document, check if information about the shared
1457 : randomness protocol is included, and extract it. */
1458 1711 : if (ns->type == NS_TYPE_VOTE) {
1459 320 : dirvote_parse_sr_commits(ns, tokens);
1460 : }
1461 : /* For both a vote and consensus, extract the shared random values. */
1462 1711 : if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
1463 1711 : extract_shared_random_srvs(ns, tokens);
1464 : }
1465 :
1466 : /* Parse routerstatus lines. */
1467 1711 : rs_tokens = smartlist_new();
1468 1711 : rs_area = memarea_new();
1469 1711 : s = end_of_header;
1470 1711 : ns->routerstatus_list = smartlist_new();
1471 :
1472 2610 : while (eos - s >= 2 && fast_memeq(s, "r ", 2)) {
1473 2332 : if (ns->type != NS_TYPE_CONSENSUS) {
1474 502 : vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
1475 502 : if (routerstatus_parse_entry_from_string(rs_area, &s, eos, rs_tokens, ns,
1476 : rs, 0, 0)) {
1477 324 : smartlist_add(ns->routerstatus_list, rs);
1478 : } else {
1479 178 : vote_routerstatus_free(rs);
1480 178 : goto err; // Malformed routerstatus, reject this vote.
1481 : }
1482 : } else {
1483 1830 : routerstatus_t *rs;
1484 1830 : if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, eos,
1485 : rs_tokens,
1486 : NULL, NULL,
1487 : ns->consensus_method,
1488 : flav))) {
1489 : /* Use exponential-backoff scheduling when downloading microdescs */
1490 575 : smartlist_add(ns->routerstatus_list, rs);
1491 : } else {
1492 1255 : goto err; // Malformed routerstatus, reject this vote.
1493 : }
1494 : }
1495 : }
1496 634 : for (i = 1; i < smartlist_len(ns->routerstatus_list); ++i) {
1497 368 : routerstatus_t *rs1, *rs2;
1498 368 : if (ns->type != NS_TYPE_CONSENSUS) {
1499 243 : vote_routerstatus_t *a = smartlist_get(ns->routerstatus_list, i-1);
1500 243 : vote_routerstatus_t *b = smartlist_get(ns->routerstatus_list, i);
1501 243 : rs1 = &a->status; rs2 = &b->status;
1502 : } else {
1503 125 : rs1 = smartlist_get(ns->routerstatus_list, i-1);
1504 125 : rs2 = smartlist_get(ns->routerstatus_list, i);
1505 : }
1506 368 : if (fast_memcmp(rs1->identity_digest, rs2->identity_digest, DIGEST_LEN)
1507 : >= 0) {
1508 12 : log_warn(LD_DIR, "Networkstatus entries not sorted by identity digest");
1509 12 : goto err;
1510 : }
1511 : }
1512 266 : if (ns_type != NS_TYPE_CONSENSUS) {
1513 142 : digest256map_t *ed_id_map = digest256map_new();
1514 466 : SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *,
1515 : vrs) {
1516 648 : if (! vrs->has_ed25519_listing ||
1517 324 : fast_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
1518 324 : continue;
1519 0 : if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
1520 0 : log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
1521 : "unique");
1522 0 : digest256map_free(ed_id_map, NULL);
1523 0 : goto err;
1524 : }
1525 0 : digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1);
1526 324 : } SMARTLIST_FOREACH_END(vrs);
1527 142 : digest256map_free(ed_id_map, NULL);
1528 : }
1529 :
1530 : /* Parse footer; check signature. */
1531 266 : footer_tokens = smartlist_new();
1532 266 : if ((end_of_footer = tor_memstr(s, eos-s, "\nnetwork-status-version ")))
1533 1 : ++end_of_footer;
1534 : else
1535 : end_of_footer = eos;
1536 266 : if (tokenize_string(area,s, end_of_footer, footer_tokens,
1537 : networkstatus_vote_footer_token_table, 0)) {
1538 35 : log_warn(LD_DIR, "Error tokenizing network-status vote footer.");
1539 35 : goto err;
1540 : }
1541 :
1542 : {
1543 231 : int found_sig = 0;
1544 1118 : SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
1545 887 : tok = _tok;
1546 887 : if (tok->tp == K_DIRECTORY_SIGNATURE)
1547 : found_sig = 1;
1548 469 : else if (found_sig) {
1549 0 : log_warn(LD_DIR, "Extraneous token after first directory-signature");
1550 0 : goto err;
1551 : }
1552 887 : } SMARTLIST_FOREACH_END(_tok);
1553 : }
1554 :
1555 231 : if ((tok = find_opt_by_keyword(footer_tokens, K_DIRECTORY_FOOTER))) {
1556 130 : if (tok != smartlist_get(footer_tokens, 0)) {
1557 0 : log_warn(LD_DIR, "Misplaced directory-footer token");
1558 0 : goto err;
1559 : }
1560 : }
1561 :
1562 231 : tok = find_opt_by_keyword(footer_tokens, K_BW_WEIGHTS);
1563 231 : if (tok) {
1564 56 : ns->weight_params = smartlist_new();
1565 1135 : for (i = 0; i < tok->n_args; ++i) {
1566 1081 : int ok=0;
1567 1081 : char *eq = strchr(tok->args[i], '=');
1568 1081 : if (!eq) {
1569 1 : log_warn(LD_DIR, "Bad element '%s' in weight params",
1570 : escaped(tok->args[i]));
1571 2 : goto err;
1572 : }
1573 1080 : tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
1574 1080 : if (!ok) {
1575 1 : log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
1576 1 : goto err;
1577 : }
1578 1079 : smartlist_add_strdup(ns->weight_params, tok->args[i]);
1579 : }
1580 : }
1581 :
1582 1038 : SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
1583 828 : char declared_identity[DIGEST_LEN];
1584 828 : networkstatus_voter_info_t *v;
1585 828 : document_signature_t *sig;
1586 828 : const char *id_hexdigest = NULL;
1587 828 : const char *sk_hexdigest = NULL;
1588 828 : digest_algorithm_t alg = DIGEST_SHA1;
1589 828 : tok = _tok;
1590 828 : if (tok->tp != K_DIRECTORY_SIGNATURE)
1591 593 : continue;
1592 378 : tor_assert(tok->n_args >= 2);
1593 378 : if (tok->n_args == 2) {
1594 160 : id_hexdigest = tok->args[0];
1595 160 : sk_hexdigest = tok->args[1];
1596 : } else {
1597 218 : const char *algname = tok->args[0];
1598 218 : int a;
1599 218 : id_hexdigest = tok->args[1];
1600 218 : sk_hexdigest = tok->args[2];
1601 218 : a = crypto_digest_algorithm_parse_name(algname);
1602 218 : if (a<0) {
1603 7 : log_warn(LD_DIR, "Unknown digest algorithm %s; skipping",
1604 : escaped(algname));
1605 7 : continue;
1606 : }
1607 211 : alg = a;
1608 : }
1609 :
1610 371 : if (!tok->object_type ||
1611 371 : strcmp(tok->object_type, "SIGNATURE") ||
1612 370 : tok->object_size < 128 || tok->object_size > 512) {
1613 8 : log_warn(LD_DIR, "Bad object type or length on directory-signature");
1614 19 : goto err;
1615 : }
1616 :
1617 724 : if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
1618 361 : base16_decode(declared_identity, sizeof(declared_identity),
1619 : id_hexdigest, HEX_DIGEST_LEN)
1620 : != sizeof(declared_identity)) {
1621 2 : log_warn(LD_DIR, "Error decoding declared identity %s in "
1622 : "network-status document.", escaped(id_hexdigest));
1623 2 : goto err;
1624 : }
1625 361 : if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
1626 5 : log_warn(LD_DIR, "ID on signature on network-status document does "
1627 : "not match any declared directory source.");
1628 5 : goto err;
1629 : }
1630 356 : sig = tor_malloc_zero(sizeof(document_signature_t));
1631 356 : memcpy(sig->identity_digest, v->identity_digest, DIGEST_LEN);
1632 356 : sig->alg = alg;
1633 711 : if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
1634 355 : base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
1635 : sk_hexdigest, HEX_DIGEST_LEN)
1636 : != sizeof(sig->signing_key_digest)) {
1637 4 : log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
1638 : "network-status document.", escaped(sk_hexdigest));
1639 4 : tor_free(sig);
1640 4 : goto err;
1641 : }
1642 :
1643 352 : if (ns->type != NS_TYPE_CONSENSUS) {
1644 115 : if (tor_memneq(declared_identity, ns->cert->cache_info.identity_digest,
1645 : DIGEST_LEN)) {
1646 0 : log_warn(LD_DIR, "Digest mismatch between declared and actual on "
1647 : "network-status vote.");
1648 0 : tor_free(sig);
1649 0 : goto err;
1650 : }
1651 : }
1652 :
1653 352 : if (networkstatus_get_voter_sig_by_alg(v, sig->alg)) {
1654 : /* We already parsed a vote with this algorithm from this voter. Use the
1655 : first one. */
1656 136 : log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
1657 : "that contains two signatures from the same voter with the same "
1658 : "algorithm. Ignoring the second signature.");
1659 136 : tor_free(sig);
1660 136 : continue;
1661 : }
1662 :
1663 216 : if (ns->type != NS_TYPE_CONSENSUS) {
1664 86 : if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
1665 86 : tok, ns->cert->signing_key, 0,
1666 : "network-status document")) {
1667 0 : tor_free(sig);
1668 0 : goto err;
1669 : }
1670 86 : sig->good_signature = 1;
1671 : } else {
1672 130 : if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
1673 0 : tor_free(sig);
1674 0 : goto err;
1675 : }
1676 130 : sig->signature = tor_memdup(tok->object_body, tok->object_size);
1677 130 : sig->signature_len = (int) tok->object_size;
1678 : }
1679 216 : smartlist_add(v->sigs, sig);
1680 :
1681 216 : ++n_signatures;
1682 809 : } SMARTLIST_FOREACH_END(_tok);
1683 :
1684 210 : if (! n_signatures) {
1685 70 : log_warn(LD_DIR, "No signatures on networkstatus document.");
1686 70 : goto err;
1687 140 : } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
1688 0 : log_warn(LD_DIR, "Received more than one signature on a "
1689 : "network-status vote.");
1690 0 : goto err;
1691 : }
1692 :
1693 140 : if (eos_out)
1694 38 : *eos_out = end_of_footer;
1695 :
1696 140 : goto done;
1697 2360 : err:
1698 2360 : dump_desc(s_dup, "v3 networkstatus");
1699 2360 : networkstatus_vote_free(ns);
1700 2360 : ns = NULL;
1701 2500 : done:
1702 2500 : if (tokens) {
1703 62869 : SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
1704 2500 : smartlist_free(tokens);
1705 : }
1706 2500 : if (voter) {
1707 47 : if (voter->sigs) {
1708 47 : SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
1709 : document_signature_free(sig));
1710 47 : smartlist_free(voter->sigs);
1711 : }
1712 47 : tor_free(voter->nickname);
1713 47 : tor_free(voter->address);
1714 47 : tor_free(voter->contact);
1715 47 : tor_free(voter);
1716 : }
1717 2500 : if (rs_tokens) {
1718 1711 : SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_clear(t));
1719 1711 : smartlist_free(rs_tokens);
1720 : }
1721 2500 : if (footer_tokens) {
1722 1578 : SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_clear(t));
1723 266 : smartlist_free(footer_tokens);
1724 : }
1725 2500 : if (area) {
1726 2464 : DUMP_AREA(area, "v3 networkstatus");
1727 2464 : memarea_drop_all(area);
1728 : }
1729 2500 : if (rs_area)
1730 1711 : memarea_drop_all(rs_area);
1731 2500 : tor_free(last_kwd);
1732 :
1733 2500 : return ns;
1734 : }
|