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
router_get_networkstatus_v3_signed_boundaries(const char * s,size_t len,const char ** start_out,const char ** end_out)163 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   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
router_get_networkstatus_v3_sha3_as_signed(uint8_t * digest_out,const char * s,size_t len)179 router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
180                                            const char *s, size_t len)
181 {
182   const char *start, *end;
183   if (router_get_networkstatus_v3_signed_boundaries(s, len,
184                                                     &start, &end) < 0) {
185     start = s;
186     end = s + len;
187   }
188   tor_assert(start);
189   tor_assert(end);
190   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
router_get_networkstatus_v3_hashes(const char * s,size_t len,common_digests_t * digests)197 router_get_networkstatus_v3_hashes(const char *s, size_t len,
198                                    common_digests_t *digests)
199 {
200   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 *
find_start_of_next_routerstatus(const char * s,const char * s_eos)211 find_start_of_next_routerstatus(const char *s, const char *s_eos)
212 {
213   const char *eos, *footer, *sig;
214   if ((eos = tor_memstr(s, s_eos - s, "\nr ")))
215     ++eos;
216   else
217     eos = s_eos;
218 
219   footer = tor_memstr(s, eos-s, "\ndirectory-footer");
220   sig = tor_memstr(s, eos-s, "\ndirectory-signature");
221 
222   if (footer && sig)
223     return MIN(footer, sig) + 1;
224   else if (footer)
225     return footer+1;
226   else if (sig)
227     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
routerstatus_parse_guardfraction(const char * guardfraction_str,networkstatus_t * vote,vote_routerstatus_t * vote_rs,routerstatus_t * rs)238 routerstatus_parse_guardfraction(const char *guardfraction_str,
239                                  networkstatus_t *vote,
240                                  vote_routerstatus_t *vote_rs,
241                                  routerstatus_t *rs)
242 {
243   int ok;
244   const char *end_of_header = NULL;
245   int is_consensus = !vote_rs;
246   uint32_t guardfraction;
247 
248   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   if (is_consensus && !should_apply_guardfraction(NULL)) {
253     return 0;
254   }
255 
256   end_of_header = strchr(guardfraction_str, '=');
257   if (!end_of_header) {
258     return -1;
259   }
260 
261   guardfraction = (uint32_t)tor_parse_ulong(end_of_header+1,
262                                             10, 0, 100, &ok, NULL);
263   if (!ok) {
264     log_warn(LD_DIR, "Invalid GuardFraction %s", escaped(guardfraction_str));
265     return -1;
266   }
267 
268   log_debug(LD_GENERAL, "[*] Parsed %s guardfraction '%s' for '%s'.",
269             is_consensus ? "consensus" : "vote",
270             guardfraction_str, rs->nickname);
271 
272   if (!is_consensus) { /* We are parsing a vote */
273     vote_rs->status.guardfraction_percentage = guardfraction;
274     vote_rs->status.has_guardfraction = 1;
275   } else {
276     /* We are parsing a consensus. Only apply guardfraction to guards. */
277     if (rs->is_possible_guard) {
278       rs->guardfraction_percentage = guardfraction;
279       rs->has_guardfraction = 1;
280     } else {
281       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 *
routerstatus_parse_entry_from_string(memarea_t * area,const char ** s,const char * s_eos,smartlist_t * tokens,networkstatus_t * vote,vote_routerstatus_t * vote_rs,int consensus_method,consensus_flavor_t flav)304 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   const char *eos, *s_dup = *s;
313   routerstatus_t *rs = NULL;
314   directory_token_t *tok;
315   char timebuf[ISO_TIME_LEN+1];
316   struct in_addr in;
317   int offset = 0;
318   tor_assert(tokens);
319   tor_assert(bool_eq(vote, vote_rs));
320 
321   if (!consensus_method)
322     flav = FLAV_NS;
323   tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
324 
325   eos = find_start_of_next_routerstatus(*s, s_eos);
326 
327   if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
328     log_warn(LD_DIR, "Error tokenizing router status");
329     goto err;
330   }
331   if (smartlist_len(tokens) < 1) {
332     log_warn(LD_DIR, "Impossibly short router status");
333     goto err;
334   }
335   tok = find_by_keyword(tokens, K_R);
336   tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
337   if (flav == FLAV_NS) {
338     if (tok->n_args < 8) {
339       log_warn(LD_DIR, "Too few arguments to r");
340       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   if (vote_rs) {
347     rs = &vote_rs->status;
348   } else {
349     rs = tor_malloc_zero(sizeof(routerstatus_t));
350   }
351 
352   if (!is_legal_nickname(tok->args[0])) {
353     log_warn(LD_DIR,
354              "Invalid nickname %s in router status; skipping.",
355              escaped(tok->args[0]));
356     goto err;
357   }
358   strlcpy(rs->nickname, tok->args[0], sizeof(rs->nickname));
359 
360   if (digest_from_base64(rs->identity_digest, tok->args[1])) {
361     log_warn(LD_DIR, "Error decoding identity digest %s",
362              escaped(tok->args[1]));
363     goto err;
364   }
365 
366   if (flav == FLAV_NS) {
367     if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
368       log_warn(LD_DIR, "Error decoding descriptor digest %s",
369                escaped(tok->args[2]));
370       goto err;
371     }
372   }
373 
374   if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
375                    tok->args[3+offset], tok->args[4+offset]) < 0 ||
376       parse_iso_time(timebuf, &rs->published_on)<0) {
377     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     goto err;
381   }
382 
383   if (tor_inet_aton(tok->args[5+offset], &in) == 0) {
384     log_warn(LD_DIR, "Error parsing router address in network-status %s",
385              escaped(tok->args[5+offset]));
386     goto err;
387   }
388   tor_addr_from_in(&rs->ipv4_addr, &in);
389 
390   rs->ipv4_orport = (uint16_t) tor_parse_long(tok->args[6+offset],
391                                               10,0,65535,NULL,NULL);
392   rs->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[7+offset],
393                                                10,0,65535,NULL,NULL);
394 
395   {
396     smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
397     if (a_lines) {
398       find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport);
399       smartlist_free(a_lines);
400     }
401   }
402 
403   tok = find_opt_by_keyword(tokens, K_S);
404   if (tok && vote) {
405     int i;
406     vote_rs->flags = 0;
407     for (i=0; i < tok->n_args; ++i) {
408       int p = smartlist_string_pos(vote->known_flags, tok->args[i]);
409       if (p >= 0) {
410         vote_rs->flags |= (UINT64_C(1)<<p);
411       } else {
412         log_warn(LD_DIR, "Flags line had a flag %s not listed in known_flags.",
413                  escaped(tok->args[i]));
414         goto err;
415       }
416     }
417   } else if (tok) {
418     /* This is a consensus, not a vote. */
419     int i;
420     for (i=0; i < tok->n_args; ++i) {
421       if (!strcmp(tok->args[i], "Exit"))
422         rs->is_exit = 1;
423       else if (!strcmp(tok->args[i], "Stable"))
424         rs->is_stable = 1;
425       else if (!strcmp(tok->args[i], "Fast"))
426         rs->is_fast = 1;
427       else if (!strcmp(tok->args[i], "Running"))
428         rs->is_flagged_running = 1;
429       else if (!strcmp(tok->args[i], "Named"))
430         rs->is_named = 1;
431       else if (!strcmp(tok->args[i], "Valid"))
432         rs->is_valid = 1;
433       else if (!strcmp(tok->args[i], "Guard"))
434         rs->is_possible_guard = 1;
435       else if (!strcmp(tok->args[i], "BadExit"))
436         rs->is_bad_exit = 1;
437       else if (!strcmp(tok->args[i], "Authority"))
438         rs->is_authority = 1;
439       else if (!strcmp(tok->args[i], "Unnamed") &&
440                consensus_method >= 2) {
441         /* Unnamed is computed right by consensus method 2 and later. */
442         rs->is_unnamed = 1;
443       } else if (!strcmp(tok->args[i], "HSDir")) {
444         rs->is_hs_dir = 1;
445       } else if (!strcmp(tok->args[i], "V2Dir")) {
446         rs->is_v2_dir = 1;
447       } else if (!strcmp(tok->args[i], "StaleDesc")) {
448         rs->is_staledesc = 1;
449       } else if (!strcmp(tok->args[i], "Sybil")) {
450         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     rs->is_flagged_running = 1; /* Starting with consensus method 4. */
456     rs->is_valid = 1; /* Starting with consensus method 24. */
457   }
458   {
459     const char *protocols = NULL, *version = NULL;
460     if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
461       tor_assert(tok->n_args == 1);
462       protocols = tok->args[0];
463     }
464     if ((tok = find_opt_by_keyword(tokens, K_V))) {
465       tor_assert(tok->n_args == 1);
466       version = tok->args[0];
467       if (vote_rs) {
468         vote_rs->version = tor_strdup(tok->args[0]);
469       }
470     }
471 
472     // If the protover line is malformed, reject this routerstatus.
473     if (protocols && protover_list_is_invalid(protocols)) {
474       goto err;
475     }
476     summarize_protover_flags(&rs->pv, protocols, version);
477   }
478 
479   /* handle weighting/bandwidth info */
480   if ((tok = find_opt_by_keyword(tokens, K_W))) {
481     int i;
482     for (i=0; i < tok->n_args; ++i) {
483       if (!strcmpstart(tok->args[i], "Bandwidth=")) {
484         int ok;
485         rs->bandwidth_kb =
486           (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
487                                     10, 0, UINT32_MAX,
488                                     &ok, NULL);
489         if (!ok) {
490           log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i]));
491           goto err;
492         }
493         rs->has_bandwidth = 1;
494       } else if (!strcmpstart(tok->args[i], "Measured=") && vote_rs) {
495         int ok;
496         vote_rs->measured_bw_kb =
497             (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
498                                       10, 0, UINT32_MAX, &ok, NULL);
499         if (!ok) {
500           log_warn(LD_DIR, "Invalid Measured Bandwidth %s",
501                    escaped(tok->args[i]));
502           goto err;
503         }
504         vote_rs->has_measured_bw = 1;
505         vote->has_measured_bws = 1;
506       } else if (!strcmpstart(tok->args[i], "Unmeasured=1")) {
507         rs->bw_is_unmeasured = 1;
508       } else if (!strcmpstart(tok->args[i], "GuardFraction=")) {
509         if (routerstatus_parse_guardfraction(tok->args[i],
510                                              vote, vote_rs, rs) < 0) {
511           goto err;
512         }
513       }
514     }
515   }
516 
517   /* parse exit policy summaries */
518   if ((tok = find_opt_by_keyword(tokens, K_P))) {
519     tor_assert(tok->n_args == 1);
520     if (strcmpstart(tok->args[0], "accept ") &&
521         strcmpstart(tok->args[0], "reject ")) {
522       log_warn(LD_DIR, "Unknown exit policy summary type %s.",
523                escaped(tok->args[0]));
524       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     rs->exitsummary = tor_strdup(tok->args[0]);
531     rs->has_exitsummary = 1;
532   }
533 
534   if (vote_rs) {
535     SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, t) {
536       if (t->tp == K_M && t->n_args) {
537         vote_microdesc_hash_t *line =
538           tor_malloc(sizeof(vote_microdesc_hash_t));
539         line->next = vote_rs->microdesc;
540         line->microdesc_hash_line = tor_strdup(t->args[0]);
541         vote_rs->microdesc = line;
542       }
543       if (t->tp == K_ID) {
544         tor_assert(t->n_args >= 2);
545         if (!strcmp(t->args[0], "ed25519")) {
546           vote_rs->has_ed25519_listing = 1;
547           if (strcmp(t->args[1], "none") &&
548               digest256_from_base64((char*)vote_rs->ed25519_id,
549                                     t->args[1])<0) {
550             log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote");
551             goto err;
552           }
553         }
554       }
555       if (t->tp == K_PROTO) {
556         tor_assert(t->n_args == 1);
557         vote_rs->protocols = tor_strdup(t->args[0]);
558       }
559     } SMARTLIST_FOREACH_END(t);
560   } else if (flav == FLAV_MICRODESC) {
561     tok = find_opt_by_keyword(tokens, K_M);
562     if (tok) {
563       tor_assert(tok->n_args);
564       if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
565         log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
566                  escaped(tok->args[0]));
567         goto err;
568       }
569     } else {
570       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   if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
578     rs->is_named = 0;
579 
580   goto done;
581  err:
582   dump_desc(s_dup, "routerstatus entry");
583   if (rs && !vote_rs)
584     routerstatus_free(rs);
585   rs = NULL;
586  done:
587   SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
588   smartlist_clear(tokens);
589   if (area) {
590     DUMP_AREA(area, "routerstatus entry");
591     memarea_clear(area);
592   }
593   *s = eos;
594 
595   return rs;
596 }
597 
598 int
compare_vote_routerstatus_entries(const void ** _a,const void ** _b)599 compare_vote_routerstatus_entries(const void **_a, const void **_b)
600 {
601   const vote_routerstatus_t *a = *_a, *b = *_b;
602   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
networkstatus_verify_bw_weights(networkstatus_t * ns,int consensus_method)608 networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
609 {
610   int64_t G=0, M=0, E=0, D=0, T=0;
611   double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
612   double Gtotal=0, Mtotal=0, Etotal=0;
613   const char *casename = NULL;
614   int valid = 1;
615   (void) consensus_method;
616 
617   const int64_t weight_scale = networkstatus_get_weight_scale_param(ns);
618   tor_assert(weight_scale >= 1);
619   Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
620   Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
621   Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
622   Wmg = networkstatus_get_bw_weight(ns, "Wmg", -1);
623   Wmm = networkstatus_get_bw_weight(ns, "Wmm", -1);
624   Wme = networkstatus_get_bw_weight(ns, "Wme", -1);
625   Wmd = networkstatus_get_bw_weight(ns, "Wmd", -1);
626   Weg = networkstatus_get_bw_weight(ns, "Weg", -1);
627   Wem = networkstatus_get_bw_weight(ns, "Wem", -1);
628   Wee = networkstatus_get_bw_weight(ns, "Wee", -1);
629   Wed = networkstatus_get_bw_weight(ns, "Wed", -1);
630 
631   if (Wgg<0 || Wgm<0 || Wgd<0 || Wmg<0 || Wmm<0 || Wme<0 || Wmd<0 || Weg<0
632           || Wem<0 || Wee<0 || Wed<0) {
633     log_warn(LD_BUG, "No bandwidth weights produced in consensus!");
634     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   if (fabs(Wmm - weight_scale) > 1) {
641     log_warn(LD_BUG, "Wmm=%f != %"PRId64,
642              Wmm, (weight_scale));
643     valid = 0;
644   }
645 
646   if (fabs(Wem - Wee) > 1) {
647     log_warn(LD_BUG, "Wem=%f != Wee=%f", Wem, Wee);
648     valid = 0;
649   }
650 
651   if (fabs(Wgm - Wgg) > 1) {
652     log_warn(LD_BUG, "Wgm=%f != Wgg=%f", Wgm, Wgg);
653     valid = 0;
654   }
655 
656   if (fabs(Weg - Wed) > 1) {
657     log_warn(LD_BUG, "Wed=%f != Weg=%f", Wed, Weg);
658     valid = 0;
659   }
660 
661   if (fabs(Wgg + Wmg - weight_scale) > 0.001*weight_scale) {
662     log_warn(LD_BUG, "Wgg=%f != %"PRId64" - Wmg=%f", Wgg,
663              (weight_scale), Wmg);
664     valid = 0;
665   }
666 
667   if (fabs(Wee + Wme - weight_scale) > 0.001*weight_scale) {
668     log_warn(LD_BUG, "Wee=%f != %"PRId64" - Wme=%f", Wee,
669              (weight_scale), Wme);
670     valid = 0;
671   }
672 
673   if (fabs(Wgd + Wmd + Wed - weight_scale) > 0.001*weight_scale) {
674     log_warn(LD_BUG, "Wgd=%f + Wmd=%f + Wed=%f != %"PRId64,
675              Wgd, Wmd, Wed, (weight_scale));
676     valid = 0;
677   }
678 
679   Wgg /= weight_scale;
680   Wgm /= weight_scale; (void) Wgm; // unused from here on.
681   Wgd /= weight_scale;
682 
683   Wmg /= weight_scale;
684   Wmm /= weight_scale;
685   Wme /= weight_scale;
686   Wmd /= weight_scale;
687 
688   Weg /= weight_scale; (void) Weg; // unused from here on.
689   Wem /= weight_scale; (void) Wem; // unused from here on.
690   Wee /= weight_scale;
691   Wed /= weight_scale;
692 
693   // Then, gather G, M, E, D, T to determine case
694   SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
695     int is_exit = 0;
696     /* Bug #2203: Don't count bad exits as exits for balancing */
697     is_exit = rs->is_exit && !rs->is_bad_exit;
698     if (rs->has_bandwidth) {
699       T += rs->bandwidth_kb;
700       if (is_exit && rs->is_possible_guard) {
701         D += rs->bandwidth_kb;
702         Gtotal += Wgd*rs->bandwidth_kb;
703         Mtotal += Wmd*rs->bandwidth_kb;
704         Etotal += Wed*rs->bandwidth_kb;
705       } else if (is_exit) {
706         E += rs->bandwidth_kb;
707         Mtotal += Wme*rs->bandwidth_kb;
708         Etotal += Wee*rs->bandwidth_kb;
709       } else if (rs->is_possible_guard) {
710         G += rs->bandwidth_kb;
711         Gtotal += Wgg*rs->bandwidth_kb;
712         Mtotal += Wmg*rs->bandwidth_kb;
713       } else {
714         M += rs->bandwidth_kb;
715         Mtotal += Wmm*rs->bandwidth_kb;
716       }
717     } else {
718       log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
719                routerstatus_describe(rs));
720     }
721   } 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   if (3*E >= T && 3*G >= T) {
728     // Case 1: Neither are scarce
729     casename = "Case 1";
730     if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
731       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       valid = 0;
741     }
742     if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
743       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       valid = 0;
753     }
754     if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
755       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       valid = 0;
765     }
766   } else if (3*E < T && 3*G < T) {
767     int64_t R = MIN(E, G);
768     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     if (R+D < S) { // Subcase a
776       double Rtotal, Stotal;
777       if (E < G) {
778         Rtotal = Etotal;
779         Stotal = Gtotal;
780       } else {
781         Rtotal = Gtotal;
782         Stotal = Etotal;
783       }
784       casename = "Case 2a";
785       // Rtotal < Stotal
786       if (Rtotal > Stotal) {
787         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         valid = 0;
797       }
798       // Rtotal < T/3
799       if (3*Rtotal > T) {
800         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         valid = 0;
810       }
811       // Stotal < T/3
812       if (3*Stotal > T) {
813         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         valid = 0;
823       }
824       // Mtotal > T/3
825       if (3*Mtotal < T) {
826         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         valid = 0;
837       }
838     } else { // Subcase b: R+D > S
839       casename = "Case 2b";
840 
841       /* Check the rare-M redirect case. */
842       if (D != 0 && 3*M < T) {
843         casename = "Case 2b (balanced)";
844         if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
845           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           valid = 0;
855         }
856         if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
857           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           valid = 0;
867         }
868         if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
869           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           valid = 0;
879         }
880       } else {
881         if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
882           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           valid = 0;
892         }
893       }
894     }
895   } else { // if (E < T/3 || G < T/3) {
896     int64_t S = MIN(E, G);
897     int64_t NS = MAX(E, G);
898     if (3*(S+D) < T) { // Subcase a:
899       double Stotal;
900       double NStotal;
901       if (G < E) {
902         casename = "Case 3a (G scarce)";
903         Stotal = Gtotal;
904         NStotal = Etotal;
905       } else { // if (G >= E) {
906         casename = "Case 3a (E scarce)";
907         NStotal = Gtotal;
908         Stotal = Etotal;
909       }
910       // Stotal < T/3
911       if (3*Stotal > T) {
912         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         valid = 0;
922       }
923       if (NS >= M) {
924         if (fabs(NStotal-Mtotal) > 0.01*MAX(NStotal,Mtotal)) {
925           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           valid = 0;
935         }
936       } else {
937         // if NS < M, NStotal > T/3 because only one of G or E is scarce
938         if (3*NStotal < T) {
939           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           valid = 0;
949         }
950       }
951     } else { // Subcase b: S+D >= T/3
952       casename = "Case 3b";
953       if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
954         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         valid = 0;
964       }
965       if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
966         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         valid = 0;
976       }
977       if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
978         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         valid = 0;
988       }
989     }
990   }
991 
992   if (valid)
993     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
extract_one_srv(smartlist_t * tokens,directory_keyword srv_type,sr_srv_t ** srv_out)1004 extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
1005                 sr_srv_t **srv_out)
1006 {
1007   int ret = -1;
1008   directory_token_t *tok;
1009   sr_srv_t *srv = NULL;
1010   smartlist_t *chunks;
1011 
1012   tor_assert(tokens);
1013 
1014   chunks = smartlist_new();
1015   tok = find_opt_by_keyword(tokens, srv_type);
1016   if (!tok) {
1017     /* That's fine, no SRV is allowed. */
1018     ret = 0;
1019     goto end;
1020   }
1021   for (int i = 0; i < tok->n_args; i++) {
1022     smartlist_add(chunks, tok->args[i]);
1023   }
1024   srv = sr_parse_srv(chunks);
1025   if (srv == NULL) {
1026     log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
1027     goto end;
1028   }
1029   /* All is good. */
1030   *srv_out = srv;
1031   ret = 0;
1032  end:
1033   smartlist_free(chunks);
1034   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
extract_shared_random_srvs(networkstatus_t * ns,smartlist_t * tokens)1040 extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
1041 {
1042   const char *voter_identity;
1043   networkstatus_voter_info_t *voter;
1044 
1045   tor_assert(ns);
1046   tor_assert(tokens);
1047   /* Can be only one of them else code flow. */
1048   tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
1049 
1050   if (ns->type == NS_TYPE_VOTE) {
1051     voter = smartlist_get(ns->voters, 0);
1052     tor_assert(voter);
1053     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   if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
1063     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   if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
1068     log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
1069              voter_identity);
1070   }
1071 }
1072 
1073 /** Allocate a copy of a protover line, if present. If present but malformed,
1074  * set *error to true. */
1075 static char *
dup_protocols_string(smartlist_t * tokens,bool * error,directory_keyword kw)1076 dup_protocols_string(smartlist_t *tokens, bool *error, directory_keyword kw)
1077 {
1078   directory_token_t *tok = find_opt_by_keyword(tokens, kw);
1079   if (!tok)
1080     return NULL;
1081   if (protover_list_is_invalid(tok->args[0]))
1082     *error = true;
1083   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 *
networkstatus_parse_vote_from_string(const char * s,size_t s_len,const char ** eos_out,networkstatus_type_t ns_type)1089 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   smartlist_t *tokens = smartlist_new();
1095   smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
1096   networkstatus_voter_info_t *voter = NULL;
1097   networkstatus_t *ns = NULL;
1098   common_digests_t ns_digests;
1099   uint8_t sha3_as_signed[DIGEST256_LEN];
1100   const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
1101   directory_token_t *tok;
1102   struct in_addr in;
1103   int i, inorder, n_signatures = 0;
1104   memarea_t *area = NULL, *rs_area = NULL;
1105   consensus_flavor_t flav = FLAV_NS;
1106   char *last_kwd=NULL;
1107   const char *eos = s + s_len;
1108 
1109   tor_assert(s);
1110 
1111   if (eos_out)
1112     *eos_out = NULL;
1113 
1114   if (router_get_networkstatus_v3_hashes(s, s_len, &ns_digests) ||
1115       router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed,
1116                                                  s, s_len)<0) {
1117     log_warn(LD_DIR, "Unable to compute digest of network-status");
1118     goto err;
1119   }
1120 
1121   area = memarea_new();
1122   end_of_header = find_start_of_next_routerstatus(s, eos);
1123   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     log_warn(LD_DIR, "Error tokenizing network-status header");
1128     goto err;
1129   }
1130 
1131   ns = tor_malloc_zero(sizeof(networkstatus_t));
1132   memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
1133   memcpy(&ns->digest_sha3_as_signed, sha3_as_signed, sizeof(sha3_as_signed));
1134 
1135   tok = find_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
1136   tor_assert(tok);
1137   if (tok->n_args > 1) {
1138     int flavor = networkstatus_parse_flavor_name(tok->args[1]);
1139     if (flavor < 0) {
1140       log_warn(LD_DIR, "Can't parse document with unknown flavor %s",
1141                escaped(tok->args[1]));
1142       goto err;
1143     }
1144     ns->flavor = flav = flavor;
1145   }
1146   if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) {
1147     log_warn(LD_DIR, "Flavor found on non-consensus networkstatus.");
1148     goto err;
1149   }
1150 
1151   if (ns_type != NS_TYPE_CONSENSUS) {
1152     const char *end_of_cert = NULL;
1153     if (!(cert = tor_memstr(s, end_of_header - s,
1154                             "\ndir-key-certificate-version")))
1155       goto err;
1156     ++cert;
1157     ns->cert = authority_cert_parse_from_string(cert, end_of_header - cert,
1158                                                 &end_of_cert);
1159     if (!ns->cert || !end_of_cert || end_of_cert > end_of_header)
1160       goto err;
1161   }
1162 
1163   tok = find_by_keyword(tokens, K_VOTE_STATUS);
1164   tor_assert(tok->n_args);
1165   if (!strcmp(tok->args[0], "vote")) {
1166     ns->type = NS_TYPE_VOTE;
1167   } else if (!strcmp(tok->args[0], "consensus")) {
1168     ns->type = NS_TYPE_CONSENSUS;
1169   } else if (!strcmp(tok->args[0], "opinion")) {
1170     ns->type = NS_TYPE_OPINION;
1171   } else {
1172     log_warn(LD_DIR, "Unrecognized vote status %s in network-status",
1173              escaped(tok->args[0]));
1174     goto err;
1175   }
1176   if (ns_type != ns->type) {
1177     log_warn(LD_DIR, "Got the wrong kind of v3 networkstatus.");
1178     goto err;
1179   }
1180 
1181   if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_OPINION) {
1182     tok = find_by_keyword(tokens, K_PUBLISHED);
1183     if (parse_iso_time(tok->args[0], &ns->published))
1184       goto err;
1185 
1186     ns->supported_methods = smartlist_new();
1187     tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHODS);
1188     if (tok) {
1189       for (i=0; i < tok->n_args; ++i)
1190         smartlist_add_strdup(ns->supported_methods, tok->args[i]);
1191     } else {
1192       smartlist_add_strdup(ns->supported_methods, "1");
1193     }
1194   } else {
1195     tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHOD);
1196     if (tok) {
1197       int num_ok;
1198       ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX,
1199                                                  &num_ok, NULL);
1200       if (!num_ok)
1201         goto err;
1202     } else {
1203       ns->consensus_method = 1;
1204     }
1205   }
1206 
1207   // Reject the vote if any of the protocols lines are malformed.
1208   bool unparseable = false;
1209   ns->recommended_client_protocols = dup_protocols_string(tokens, &unparseable,
1210                                          K_RECOMMENDED_CLIENT_PROTOCOLS);
1211   ns->recommended_relay_protocols = dup_protocols_string(tokens, &unparseable,
1212                                          K_RECOMMENDED_RELAY_PROTOCOLS);
1213   ns->required_client_protocols = dup_protocols_string(tokens, &unparseable,
1214                                          K_REQUIRED_CLIENT_PROTOCOLS);
1215   ns->required_relay_protocols = dup_protocols_string(tokens, &unparseable,
1216                                          K_REQUIRED_RELAY_PROTOCOLS);
1217   if (unparseable)
1218     goto err;
1219 
1220   tok = find_by_keyword(tokens, K_VALID_AFTER);
1221   if (parse_iso_time(tok->args[0], &ns->valid_after))
1222     goto err;
1223 
1224   tok = find_by_keyword(tokens, K_FRESH_UNTIL);
1225   if (parse_iso_time(tok->args[0], &ns->fresh_until))
1226     goto err;
1227 
1228   tok = find_by_keyword(tokens, K_VALID_UNTIL);
1229   if (parse_iso_time(tok->args[0], &ns->valid_until))
1230     goto err;
1231 
1232   tok = find_by_keyword(tokens, K_VOTING_DELAY);
1233   tor_assert(tok->n_args >= 2);
1234   {
1235     int ok;
1236     ns->vote_seconds =
1237       (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
1238     if (!ok)
1239       goto err;
1240     ns->dist_seconds =
1241       (int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
1242     if (!ok)
1243       goto err;
1244   }
1245   if (ns->valid_after +
1246       (get_options()->TestingTorNetwork ?
1247        MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL) > ns->fresh_until) {
1248     log_warn(LD_DIR, "Vote/consensus freshness interval is too short");
1249     goto err;
1250   }
1251   if (ns->valid_after +
1252       (get_options()->TestingTorNetwork ?
1253        MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL)*2 > ns->valid_until) {
1254     log_warn(LD_DIR, "Vote/consensus liveness interval is too short");
1255     goto err;
1256   }
1257   if (ns->vote_seconds < MIN_VOTE_SECONDS) {
1258     log_warn(LD_DIR, "Vote seconds is too short");
1259     goto err;
1260   }
1261   if (ns->dist_seconds < MIN_DIST_SECONDS) {
1262     log_warn(LD_DIR, "Dist seconds is too short");
1263     goto err;
1264   }
1265 
1266   if ((tok = find_opt_by_keyword(tokens, K_CLIENT_VERSIONS))) {
1267     ns->client_versions = tor_strdup(tok->args[0]);
1268   }
1269   if ((tok = find_opt_by_keyword(tokens, K_SERVER_VERSIONS))) {
1270     ns->server_versions = tor_strdup(tok->args[0]);
1271   }
1272 
1273   {
1274     smartlist_t *package_lst = find_all_by_keyword(tokens, K_PACKAGE);
1275     ns->package_lines = smartlist_new();
1276     if (package_lst) {
1277       SMARTLIST_FOREACH(package_lst, directory_token_t *, t,
1278                     smartlist_add_strdup(ns->package_lines, t->args[0]));
1279     }
1280     smartlist_free(package_lst);
1281   }
1282 
1283   tok = find_by_keyword(tokens, K_KNOWN_FLAGS);
1284   ns->known_flags = smartlist_new();
1285   inorder = 1;
1286   for (i = 0; i < tok->n_args; ++i) {
1287     smartlist_add_strdup(ns->known_flags, tok->args[i]);
1288     if (i>0 && strcmp(tok->args[i-1], tok->args[i])>= 0) {
1289       log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
1290       inorder = 0;
1291     }
1292   }
1293   if (!inorder) {
1294     log_warn(LD_DIR, "known-flags not in order");
1295     goto err;
1296   }
1297   if (ns->type != NS_TYPE_CONSENSUS &&
1298       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     log_warn(LD_DIR, "Too many known-flags in consensus vote or opinion");
1305     goto err;
1306   }
1307 
1308   tok = find_opt_by_keyword(tokens, K_PARAMS);
1309   if (tok) {
1310     int any_dups = 0;
1311     inorder = 1;
1312     ns->net_params = smartlist_new();
1313     for (i = 0; i < tok->n_args; ++i) {
1314       int ok=0;
1315       char *eq = strchr(tok->args[i], '=');
1316       size_t eq_pos;
1317       if (!eq) {
1318         log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
1319         goto err;
1320       }
1321       eq_pos = eq-tok->args[i];
1322       tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
1323       if (!ok) {
1324         log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
1325         goto err;
1326       }
1327       if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) {
1328         log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
1329         inorder = 0;
1330       }
1331       if (last_kwd && eq_pos == strlen(last_kwd) &&
1332           fast_memeq(last_kwd, tok->args[i], eq_pos)) {
1333         log_warn(LD_DIR, "Duplicate value for %s parameter",
1334                  escaped(tok->args[i]));
1335         any_dups = 1;
1336       }
1337       tor_free(last_kwd);
1338       last_kwd = tor_strndup(tok->args[i], eq_pos);
1339       smartlist_add_strdup(ns->net_params, tok->args[i]);
1340     }
1341     if (!inorder) {
1342       log_warn(LD_DIR, "params not in order");
1343       goto err;
1344     }
1345     if (any_dups) {
1346       log_warn(LD_DIR, "Duplicate in parameters");
1347       goto err;
1348     }
1349   }
1350 
1351   ns->voters = smartlist_new();
1352 
1353   SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
1354     tok = _tok;
1355     if (tok->tp == K_DIR_SOURCE) {
1356       tor_assert(tok->n_args >= 6);
1357 
1358       if (voter)
1359         smartlist_add(ns->voters, voter);
1360       voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
1361       voter->sigs = smartlist_new();
1362       if (ns->type != NS_TYPE_CONSENSUS)
1363         memcpy(voter->vote_digest, ns_digests.d[DIGEST_SHA1], DIGEST_LEN);
1364 
1365       voter->nickname = tor_strdup(tok->args[0]);
1366       if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
1367           base16_decode(voter->identity_digest, sizeof(voter->identity_digest),
1368                         tok->args[1], HEX_DIGEST_LEN)
1369                         != sizeof(voter->identity_digest)) {
1370         log_warn(LD_DIR, "Error decoding identity digest %s in "
1371                  "network-status document.", escaped(tok->args[1]));
1372         goto err;
1373       }
1374       if (ns->type != NS_TYPE_CONSENSUS &&
1375           tor_memneq(ns->cert->cache_info.identity_digest,
1376                  voter->identity_digest, DIGEST_LEN)) {
1377         log_warn(LD_DIR,"Mismatch between identities in certificate and vote");
1378         goto err;
1379       }
1380       if (ns->type != NS_TYPE_CONSENSUS) {
1381         if (authority_cert_is_denylisted(ns->cert)) {
1382           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           goto err;
1386         }
1387       }
1388       voter->address = tor_strdup(tok->args[2]);
1389       if (!tor_inet_aton(tok->args[3], &in)) {
1390         log_warn(LD_DIR, "Error decoding IP address %s in network-status.",
1391                  escaped(tok->args[3]));
1392         goto err;
1393       }
1394       tor_addr_from_in(&voter->ipv4_addr, &in);
1395       int ok;
1396       voter->ipv4_dirport = (uint16_t)
1397         tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
1398       if (!ok)
1399         goto err;
1400       voter->ipv4_orport = (uint16_t)
1401         tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL);
1402       if (!ok)
1403         goto err;
1404     } else if (tok->tp == K_CONTACT) {
1405       if (!voter || voter->contact) {
1406         log_warn(LD_DIR, "contact element is out of place.");
1407         goto err;
1408       }
1409       voter->contact = tor_strdup(tok->args[0]);
1410     } else if (tok->tp == K_VOTE_DIGEST) {
1411       tor_assert(ns->type == NS_TYPE_CONSENSUS);
1412       tor_assert(tok->n_args >= 1);
1413       if (!voter || ! tor_digest_is_zero(voter->vote_digest)) {
1414         log_warn(LD_DIR, "vote-digest element is out of place.");
1415         goto err;
1416       }
1417       if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
1418         base16_decode(voter->vote_digest, sizeof(voter->vote_digest),
1419                       tok->args[0], HEX_DIGEST_LEN)
1420                       != sizeof(voter->vote_digest)) {
1421         log_warn(LD_DIR, "Error decoding vote digest %s in "
1422                  "network-status consensus.", escaped(tok->args[0]));
1423         goto err;
1424       }
1425     }
1426   } SMARTLIST_FOREACH_END(_tok);
1427   if (voter) {
1428     smartlist_add(ns->voters, voter);
1429     voter = NULL;
1430   }
1431   if (smartlist_len(ns->voters) == 0) {
1432     log_warn(LD_DIR, "Missing dir-source elements in a networkstatus.");
1433     goto err;
1434   } else if (ns->type != NS_TYPE_CONSENSUS && smartlist_len(ns->voters) != 1) {
1435     log_warn(LD_DIR, "Too many dir-source elements in a vote networkstatus.");
1436     goto err;
1437   }
1438 
1439   if (ns->type != NS_TYPE_CONSENSUS &&
1440       (tok = find_opt_by_keyword(tokens, K_LEGACY_DIR_KEY))) {
1441     int bad = 1;
1442     if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
1443       networkstatus_voter_info_t *voter_0 = smartlist_get(ns->voters, 0);
1444       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       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   if (ns->type == NS_TYPE_VOTE) {
1459     dirvote_parse_sr_commits(ns, tokens);
1460   }
1461   /* For both a vote and consensus, extract the shared random values. */
1462   if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
1463     extract_shared_random_srvs(ns, tokens);
1464   }
1465 
1466   /* Parse routerstatus lines. */
1467   rs_tokens = smartlist_new();
1468   rs_area = memarea_new();
1469   s = end_of_header;
1470   ns->routerstatus_list = smartlist_new();
1471 
1472   while (eos - s >= 2 && fast_memeq(s, "r ", 2)) {
1473     if (ns->type != NS_TYPE_CONSENSUS) {
1474       vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
1475       if (routerstatus_parse_entry_from_string(rs_area, &s, eos, rs_tokens, ns,
1476                                                rs, 0, 0)) {
1477         smartlist_add(ns->routerstatus_list, rs);
1478       } else {
1479         vote_routerstatus_free(rs);
1480         goto err; // Malformed routerstatus, reject this vote.
1481       }
1482     } else {
1483       routerstatus_t *rs;
1484       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         smartlist_add(ns->routerstatus_list, rs);
1491       } else {
1492         goto err; // Malformed routerstatus, reject this vote.
1493       }
1494     }
1495   }
1496   for (i = 1; i < smartlist_len(ns->routerstatus_list); ++i) {
1497     routerstatus_t *rs1, *rs2;
1498     if (ns->type != NS_TYPE_CONSENSUS) {
1499       vote_routerstatus_t *a = smartlist_get(ns->routerstatus_list, i-1);
1500       vote_routerstatus_t *b = smartlist_get(ns->routerstatus_list, i);
1501       rs1 = &a->status; rs2 = &b->status;
1502     } else {
1503       rs1 = smartlist_get(ns->routerstatus_list, i-1);
1504       rs2 = smartlist_get(ns->routerstatus_list, i);
1505     }
1506     if (fast_memcmp(rs1->identity_digest, rs2->identity_digest, DIGEST_LEN)
1507         >= 0) {
1508       log_warn(LD_DIR, "Networkstatus entries not sorted by identity digest");
1509       goto err;
1510     }
1511   }
1512   if (ns_type != NS_TYPE_CONSENSUS) {
1513     digest256map_t *ed_id_map = digest256map_new();
1514     SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *,
1515                             vrs) {
1516       if (! vrs->has_ed25519_listing ||
1517           fast_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
1518         continue;
1519       if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
1520         log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
1521                  "unique");
1522         digest256map_free(ed_id_map, NULL);
1523         goto err;
1524       }
1525       digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1);
1526     } SMARTLIST_FOREACH_END(vrs);
1527     digest256map_free(ed_id_map, NULL);
1528   }
1529 
1530   /* Parse footer; check signature. */
1531   footer_tokens = smartlist_new();
1532   if ((end_of_footer = tor_memstr(s, eos-s, "\nnetwork-status-version ")))
1533     ++end_of_footer;
1534   else
1535     end_of_footer = eos;
1536   if (tokenize_string(area,s, end_of_footer, footer_tokens,
1537                       networkstatus_vote_footer_token_table, 0)) {
1538     log_warn(LD_DIR, "Error tokenizing network-status vote footer.");
1539     goto err;
1540   }
1541 
1542   {
1543     int found_sig = 0;
1544     SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
1545       tok = _tok;
1546       if (tok->tp == K_DIRECTORY_SIGNATURE)
1547         found_sig = 1;
1548       else if (found_sig) {
1549         log_warn(LD_DIR, "Extraneous token after first directory-signature");
1550         goto err;
1551       }
1552     } SMARTLIST_FOREACH_END(_tok);
1553   }
1554 
1555   if ((tok = find_opt_by_keyword(footer_tokens, K_DIRECTORY_FOOTER))) {
1556     if (tok != smartlist_get(footer_tokens, 0)) {
1557       log_warn(LD_DIR, "Misplaced directory-footer token");
1558       goto err;
1559     }
1560   }
1561 
1562   tok = find_opt_by_keyword(footer_tokens, K_BW_WEIGHTS);
1563   if (tok) {
1564     ns->weight_params = smartlist_new();
1565     for (i = 0; i < tok->n_args; ++i) {
1566       int ok=0;
1567       char *eq = strchr(tok->args[i], '=');
1568       if (!eq) {
1569         log_warn(LD_DIR, "Bad element '%s' in weight params",
1570                  escaped(tok->args[i]));
1571         goto err;
1572       }
1573       tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
1574       if (!ok) {
1575         log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
1576         goto err;
1577       }
1578       smartlist_add_strdup(ns->weight_params, tok->args[i]);
1579     }
1580   }
1581 
1582   SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
1583     char declared_identity[DIGEST_LEN];
1584     networkstatus_voter_info_t *v;
1585     document_signature_t *sig;
1586     const char *id_hexdigest = NULL;
1587     const char *sk_hexdigest = NULL;
1588     digest_algorithm_t alg = DIGEST_SHA1;
1589     tok = _tok;
1590     if (tok->tp != K_DIRECTORY_SIGNATURE)
1591       continue;
1592     tor_assert(tok->n_args >= 2);
1593     if (tok->n_args == 2) {
1594       id_hexdigest = tok->args[0];
1595       sk_hexdigest = tok->args[1];
1596     } else {
1597       const char *algname = tok->args[0];
1598       int a;
1599       id_hexdigest = tok->args[1];
1600       sk_hexdigest = tok->args[2];
1601       a = crypto_digest_algorithm_parse_name(algname);
1602       if (a<0) {
1603         log_warn(LD_DIR, "Unknown digest algorithm %s; skipping",
1604                  escaped(algname));
1605         continue;
1606       }
1607       alg = a;
1608     }
1609 
1610     if (!tok->object_type ||
1611         strcmp(tok->object_type, "SIGNATURE") ||
1612         tok->object_size < 128 || tok->object_size > 512) {
1613       log_warn(LD_DIR, "Bad object type or length on directory-signature");
1614       goto err;
1615     }
1616 
1617     if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
1618         base16_decode(declared_identity, sizeof(declared_identity),
1619                       id_hexdigest, HEX_DIGEST_LEN)
1620                       != sizeof(declared_identity)) {
1621       log_warn(LD_DIR, "Error decoding declared identity %s in "
1622                "network-status document.", escaped(id_hexdigest));
1623       goto err;
1624     }
1625     if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
1626       log_warn(LD_DIR, "ID on signature on network-status document does "
1627                "not match any declared directory source.");
1628       goto err;
1629     }
1630     sig = tor_malloc_zero(sizeof(document_signature_t));
1631     memcpy(sig->identity_digest, v->identity_digest, DIGEST_LEN);
1632     sig->alg = alg;
1633     if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
1634         base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
1635                       sk_hexdigest, HEX_DIGEST_LEN)
1636                       != sizeof(sig->signing_key_digest)) {
1637       log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
1638                "network-status document.", escaped(sk_hexdigest));
1639       tor_free(sig);
1640       goto err;
1641     }
1642 
1643     if (ns->type != NS_TYPE_CONSENSUS) {
1644       if (tor_memneq(declared_identity, ns->cert->cache_info.identity_digest,
1645                  DIGEST_LEN)) {
1646         log_warn(LD_DIR, "Digest mismatch between declared and actual on "
1647                  "network-status vote.");
1648         tor_free(sig);
1649         goto err;
1650       }
1651     }
1652 
1653     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       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       tor_free(sig);
1660       continue;
1661     }
1662 
1663     if (ns->type != NS_TYPE_CONSENSUS) {
1664       if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
1665                                 tok, ns->cert->signing_key, 0,
1666                                 "network-status document")) {
1667         tor_free(sig);
1668         goto err;
1669       }
1670       sig->good_signature = 1;
1671     } else {
1672       if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
1673         tor_free(sig);
1674         goto err;
1675       }
1676       sig->signature = tor_memdup(tok->object_body, tok->object_size);
1677       sig->signature_len = (int) tok->object_size;
1678     }
1679     smartlist_add(v->sigs, sig);
1680 
1681     ++n_signatures;
1682   } SMARTLIST_FOREACH_END(_tok);
1683 
1684   if (! n_signatures) {
1685     log_warn(LD_DIR, "No signatures on networkstatus document.");
1686     goto err;
1687   } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
1688     log_warn(LD_DIR, "Received more than one signature on a "
1689              "network-status vote.");
1690     goto err;
1691   }
1692 
1693   if (eos_out)
1694     *eos_out = end_of_footer;
1695 
1696   goto done;
1697  err:
1698   dump_desc(s_dup, "v3 networkstatus");
1699   networkstatus_vote_free(ns);
1700   ns = NULL;
1701  done:
1702   if (tokens) {
1703     SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
1704     smartlist_free(tokens);
1705   }
1706   if (voter) {
1707     if (voter->sigs) {
1708       SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
1709                         document_signature_free(sig));
1710       smartlist_free(voter->sigs);
1711     }
1712     tor_free(voter->nickname);
1713     tor_free(voter->address);
1714     tor_free(voter->contact);
1715     tor_free(voter);
1716   }
1717   if (rs_tokens) {
1718     SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_clear(t));
1719     smartlist_free(rs_tokens);
1720   }
1721   if (footer_tokens) {
1722     SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_clear(t));
1723     smartlist_free(footer_tokens);
1724   }
1725   if (area) {
1726     DUMP_AREA(area, "v3 networkstatus");
1727     memarea_drop_all(area);
1728   }
1729   if (rs_area)
1730     memarea_drop_all(rs_area);
1731   tor_free(last_kwd);
1732 
1733   return ns;
1734 }
1735