Tor  0.4.3.0-alpha-dev
microdesc_parse.c
Go to the documentation of this file.
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-2020, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
6 
7 /**
8  * \file microdesc_parse.c
9  * \brief Code to parse and validate microdescriptors.
10  **/
11 
12 #include "core/or/or.h"
13 
14 #include "app/config/config.h"
15 #include "core/or/policies.h"
22 #include "feature/relay/router.h"
26 #include "lib/memarea/memarea.h"
27 
29 
30 /** List of tokens recognized in microdescriptors */
32  T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024),
33  T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
34  T0N("id", K_ID, GE(2), NO_OBJ ),
35  T0N("a", K_A, GE(1), NO_OBJ ),
36  T01("family", K_FAMILY, CONCAT_ARGS, NO_OBJ ),
37  T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
38  T01("p6", K_P6, CONCAT_ARGS, NO_OBJ ),
39  A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ),
41 };
42 
43 /** Assuming that s starts with a microdesc, return the start of the
44  * *NEXT* one. Return NULL on "not found." */
45 static const char *
46 find_start_of_next_microdesc(const char *s, const char *eos)
47 {
48  int started_with_annotations;
49  s = eat_whitespace_eos(s, eos);
50  if (!s)
51  return NULL;
52 
53 #define CHECK_LENGTH() STMT_BEGIN \
54  if (eos - s < 32) \
55  return NULL; \
56  STMT_END
57 
58 #define NEXT_LINE() STMT_BEGIN \
59  s = memchr(s, '\n', eos-s); \
60  if (!s || eos - s <= 1) \
61  return NULL; \
62  s++; \
63  STMT_END
64 
65  CHECK_LENGTH();
66 
67  started_with_annotations = (*s == '@');
68 
69  if (started_with_annotations) {
70  /* Start by advancing to the first non-annotation line. */
71  while (*s == '@')
72  NEXT_LINE();
73  }
74  CHECK_LENGTH();
75 
76  /* Now we should be pointed at an onion-key line. If we are, then skip
77  * it. */
78  if (!strcmpstart(s, "onion-key"))
79  NEXT_LINE();
80 
81  /* Okay, now we're pointed at the first line of the microdescriptor which is
82  not an annotation or onion-key. The next line that _is_ an annotation or
83  onion-key is the start of the next microdescriptor. */
84  while (eos - s > 32) {
85  if (*s == '@' || !strcmpstart(s, "onion-key"))
86  return s;
87  NEXT_LINE();
88  }
89  return NULL;
90 
91 #undef CHECK_LENGTH
92 #undef NEXT_LINE
93 }
94 
95 static inline int
96 policy_is_reject_star_or_null(struct short_policy_t *policy)
97 {
98  return !policy || short_policy_is_reject_star(policy);
99 }
100 
101 /**
102  * Return a human-readable description of a given saved_location_t.
103  * Never returns NULL.
104  **/
105 static const char *
107 {
108  const char *location;
109  switch (where) {
110  case SAVED_NOWHERE:
111  location = "download or generated string";
112  break;
113  case SAVED_IN_CACHE:
114  location = "cache";
115  break;
116  case SAVED_IN_JOURNAL:
117  location = "journal";
118  break;
119  default:
120  location = "unknown location";
121  break;
122  }
123  return location;
124 }
125 
126 /**
127  * Given a microdescriptor stored in <b>where</b> which starts at <b>s</b>,
128  * which ends at <b>start_of_next_microdescriptor</b>, and which is located
129  * within a larger document beginning at <b>start</b>: Fill in the body,
130  * bodylen, bodylen, saved_location, off, and digest fields of <b>md</b> as
131  * appropriate.
132  *
133  * The body field will be an alias within <b>s</b> if <b>saved_location</b>
134  * is SAVED_IN_CACHE, and will be copied into body and nul-terminated
135  * otherwise.
136  **/
137 static int
139  const char *start,
140  const char *s, const char *start_of_next_microdesc,
141  saved_location_t where)
142 {
143  const bool copy_body = (where != SAVED_IN_CACHE);
144 
145  const char *cp = tor_memstr(s, start_of_next_microdesc-s, "onion-key");
146 
147  const bool no_onion_key = (cp == NULL);
148  if (no_onion_key) {
149  cp = s; /* So that we have *some* junk to put in the body */
150  }
151 
152  md->bodylen = start_of_next_microdesc - cp;
153  md->saved_location = where;
154  if (copy_body)
155  md->body = tor_memdup_nulterm(cp, md->bodylen);
156  else
157  md->body = (char*)cp;
158  md->off = cp - start;
159 
160  crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
161 
162  return no_onion_key ? -1 : 0;
163 }
164 
165 /**
166  * Parse a microdescriptor which begins at <b>s</b> and ends at
167  * <b>start_of_next_microdesc</b>. Store its fields into <b>md</b>. Use
168  * <b>where</b> for generating log information. If <b>allow_annotations</b>
169  * is true, then one or more annotations may precede the microdescriptor body
170  * proper. Use <b>area</b> for memory management, clearing it when done.
171  *
172  * On success, return 0; otherwise return -1.
173  **/
174 static int
176  memarea_t *area,
177  const char *s, const char *start_of_next_microdesc,
178  int allow_annotations,
179  saved_location_t where)
180 {
181  smartlist_t *tokens = smartlist_new();
182  int rv = -1;
183  int flags = allow_annotations ? TS_ANNOTATIONS_OK : 0;
184  directory_token_t *tok;
185 
186  if (tokenize_string(area, s, start_of_next_microdesc, tokens,
187  microdesc_token_table, flags)) {
188  log_warn(LD_DIR, "Unparseable microdescriptor found in %s",
189  saved_location_to_string(where));
190  goto err;
191  }
192 
193  if ((tok = find_opt_by_keyword(tokens, A_LAST_LISTED))) {
194  if (parse_iso_time(tok->args[0], &md->last_listed)) {
195  log_warn(LD_DIR, "Bad last-listed time in microdescriptor");
196  goto err;
197  }
198  }
199 
200  tok = find_by_keyword(tokens, K_ONION_KEY);
201  if (!crypto_pk_public_exponent_ok(tok->key)) {
202  log_warn(LD_DIR,
203  "Relay's onion key had invalid exponent.");
204  goto err;
205  }
206  md->onion_pkey = tor_memdup(tok->object_body, tok->object_size);
207  md->onion_pkey_len = tok->object_size;
208  crypto_pk_free(tok->key);
209 
210  if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
212  tor_assert(tok->n_args >= 1);
213  if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
214  log_warn(LD_DIR, "Bogus ntor-onion-key in microdesc");
215  goto err;
216  }
218  tor_memdup(&k, sizeof(curve25519_public_key_t));
219  }
220 
221  smartlist_t *id_lines = find_all_by_keyword(tokens, K_ID);
222  if (id_lines) {
224  tor_assert(t->n_args >= 2);
225  if (!strcmp(t->args[0], "ed25519")) {
226  if (md->ed25519_identity_pkey) {
227  log_warn(LD_DIR, "Extra ed25519 key in microdesc");
228  smartlist_free(id_lines);
229  goto err;
230  }
232  if (ed25519_public_from_base64(&k, t->args[1])<0) {
233  log_warn(LD_DIR, "Bogus ed25519 key in microdesc");
234  smartlist_free(id_lines);
235  goto err;
236  }
237  md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k));
238  }
239  } SMARTLIST_FOREACH_END(t);
240  smartlist_free(id_lines);
241  }
242 
243  {
244  smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
245  if (a_lines) {
246  find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport);
247  smartlist_free(a_lines);
248  }
249  }
250 
251  if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) {
252  md->family = nodefamily_parse(tok->args[0],
253  NULL,
254  NF_WARN_MALFORMED);
255  }
256 
257  if ((tok = find_opt_by_keyword(tokens, K_P))) {
258  md->exit_policy = parse_short_policy(tok->args[0]);
259  }
260  if ((tok = find_opt_by_keyword(tokens, K_P6))) {
262  }
263 
264  if (policy_is_reject_star_or_null(md->exit_policy) &&
265  policy_is_reject_star_or_null(md->ipv6_exit_policy)) {
266  md->policy_is_reject_star = 1;
267  }
268 
269  rv = 0;
270  err:
271 
273  memarea_clear(area);
274  smartlist_free(tokens);
275 
276  return rv;
277 }
278 
279 /** Parse as many microdescriptors as are found from the string starting at
280  * <b>s</b> and ending at <b>eos</b>. If allow_annotations is set, read any
281  * annotations we recognize and ignore ones we don't.
282  *
283  * If <b>saved_location</b> isn't SAVED_IN_CACHE, make a local copy of each
284  * descriptor in the body field of each microdesc_t.
285  *
286  * Return all newly parsed microdescriptors in a newly allocated
287  * smartlist_t. If <b>invalid_disgests_out</b> is provided, add a SHA256
288  * microdesc digest to it for every microdesc that we found to be badly
289  * formed. (This may cause duplicates) */
290 smartlist_t *
291 microdescs_parse_from_string(const char *s, const char *eos,
292  int allow_annotations,
293  saved_location_t where,
294  smartlist_t *invalid_digests_out)
295 {
296  smartlist_t *result;
297  microdesc_t *md = NULL;
298  memarea_t *area;
299  const char *start = s;
300  const char *start_of_next_microdesc;
301 
302  if (!eos)
303  eos = s + strlen(s);
304 
305  s = eat_whitespace_eos(s, eos);
306  area = memarea_new();
307  result = smartlist_new();
308 
309  while (s < eos) {
310  bool okay = false;
311 
312  start_of_next_microdesc = find_start_of_next_microdesc(s, eos);
313  if (!start_of_next_microdesc)
314  start_of_next_microdesc = eos;
315 
316  md = tor_malloc_zero(sizeof(microdesc_t));
317  uint8_t md_digest[DIGEST256_LEN];
318  {
319  const bool body_not_found =
320  microdesc_extract_body(md, start, s,
321  start_of_next_microdesc,
322  where) < 0;
323 
324  memcpy(md_digest, md->digest, DIGEST256_LEN);
325  if (body_not_found) {
326  log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Malformed or truncated descriptor");
327  goto next;
328  }
329  }
330 
331  if (microdesc_parse_fields(md, area, s, start_of_next_microdesc,
332  allow_annotations, where) == 0) {
333  smartlist_add(result, md);
334  md = NULL; // prevent free
335  okay = true;
336  }
337 
338  next:
339  if (! okay && invalid_digests_out) {
340  smartlist_add(invalid_digests_out,
341  tor_memdup(md_digest, DIGEST256_LEN));
342  }
343  microdesc_free(md);
344  md = NULL;
345  s = start_of_next_microdesc;
346  }
347 
348  memarea_drop_all(area);
349 
350  return result;
351 }
char * body
Definition: microdesc_st.h:58
struct curve25519_public_key_t * onion_curve25519_pkey
Definition: microdesc_st.h:75
smartlist_t * find_all_by_keyword(const smartlist_t *s, directory_keyword k)
Definition: parsecommon.c:461
#define END_OF_TABLE
Definition: parsecommon.h:244
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
time_t last_listed
Definition: microdesc_st.h:36
size_t bodylen
Definition: microdesc_st.h:60
Header file for nodefamily.c.
#define T0N(s, t, a, o)
Definition: parsecommon.h:248
int curve25519_public_from_base64(curve25519_public_key_t *pkey, const char *input)
#define A01(s, t, a, o)
Definition: parsecommon.h:260
struct nodefamily_t * family
Definition: microdesc_st.h:83
Microdescriptor structure.
void smartlist_add(smartlist_t *sl, void *element)
#define memarea_drop_all(area)
Definition: memarea.h:22
Header file for config.c.
Header file for nickname.c.
#define tor_assert(expr)
Definition: util_bug.h:102
Header file for microdesc.c.
int short_policy_is_reject_star(const short_policy_t *policy)
Definition: policies.c:2933
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:206
#define T1_START(s, t, a, o)
Definition: parsecommon.h:252
Header file for microdesc_parse.c.
smartlist_t * smartlist_new(void)
void memarea_clear(memarea_t *area)
Definition: memarea.c:178
memarea_t * memarea_new(void)
Definition: memarea.c:153
saved_location_t
Definition: or.h:746
int find_single_ipv6_orport(const smartlist_t *list, tor_addr_t *addr_out, uint16_t *port_out)
Definition: routerparse.c:336
short_policy_t * parse_short_policy(const char *summary)
Definition: policies.c:2748
struct ed25519_public_key_t * ed25519_identity_pkey
Definition: microdesc_st.h:77
#define DIGEST256_LEN
Definition: digest_sizes.h:23
Header file for policies.c.
#define NO_ARGS
Definition: parsecommon.h:265
smartlist_t * microdescs_parse_from_string(const char *s, const char *eos, int allow_annotations, saved_location_t where, smartlist_t *invalid_digests_out)
Header for crypto_format.c.
size_t onion_pkey_len
Definition: microdesc_st.h:72
struct short_policy_t * exit_policy
Definition: microdesc_st.h:85
#define GE(n)
Definition: parsecommon.h:269
Master header file for Tor-specific functionality.
tor_addr_t ipv6_addr
Definition: microdesc_st.h:79
Header for memarea.c.
Header for crypto_ed25519.c.
#define CONCAT_ARGS
Definition: parsecommon.h:267
static const char * find_start_of_next_microdesc(const char *s, const char *eos)
char digest[DIGEST256_LEN]
Definition: microdesc_st.h:62
saved_location_bitfield_t saved_location
Definition: microdesc_st.h:38
directory_token_t * find_opt_by_keyword(const smartlist_t *s, directory_keyword keyword)
Definition: parsecommon.c:450
static int microdesc_parse_fields(microdesc_t *md, memarea_t *area, const char *s, const char *start_of_next_microdesc, int allow_annotations, saved_location_t where)
int crypto_pk_public_exponent_ok(const crypto_pk_t *env)
#define LD_DIR
Definition: log.h:88
Header file for parsecommon.c.
#define SMARTLIST_FOREACH(sl, type, var, cmd)
Header file for router.c.
char * onion_pkey
Definition: microdesc_st.h:70
#define log_fn(severity, domain, args,...)
Definition: log.h:287
int ed25519_public_from_base64(ed25519_public_key_t *pkey, const char *input)
void token_clear(directory_token_t *tok)
Definition: parsecommon.c:41
struct crypto_pk_t * key
Definition: parsecommon.h:210
static token_rule_t microdesc_token_table[]
struct short_policy_t * ipv6_exit_policy
Definition: microdesc_st.h:87
int crypto_digest256(char *digest, const char *m, size_t len, digest_algorithm_t algorithm)
int parse_iso_time(const char *cp, time_t *t)
Definition: time_fmt.c:392
uint16_t ipv6_orport
Definition: microdesc_st.h:81
static int microdesc_extract_body(microdesc_t *md, const char *start, const char *s, const char *start_of_next_microdesc, saved_location_t where)
unsigned int policy_is_reject_star
Definition: microdesc_st.h:44
int tokenize_string(memarea_t *area, const char *start, const char *end, smartlist_t *out, const token_rule_t *table, int flags)
Definition: parsecommon.c:53
#define T01(s, t, a, o)
Definition: parsecommon.h:258
Header for crypto_curve25519.c.
const char * eat_whitespace_eos(const char *s, const char *eos)
Definition: util_string.c:295
static const char * saved_location_to_string(saved_location_t where)
Header file for routerparse.c.