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