tor  0.4.2.0-alpha-dev
parsecommon.c
Go to the documentation of this file.
1 /* Copyright (c) 2016-2019, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
10 #include "lib/log/log.h"
11 #include "lib/log/util_bug.h"
12 #include "lib/encoding/binascii.h"
14 #include "lib/string/util_string.h"
15 #include "lib/string/printf.h"
16 #include "lib/memarea/memarea.h"
18 #include "lib/ctime/di_ops.h"
19 
20 #include <string.h>
21 
22 #define MIN_ANNOTATION A_PURPOSE
23 #define MAX_ANNOTATION A_UNKNOWN_
24 
25 #define ALLOC_ZERO(sz) memarea_alloc_zero(area,sz)
26 #define ALLOC(sz) memarea_alloc(area,sz)
27 #define STRDUP(str) memarea_strdup(area,str)
28 #define STRNDUP(str,n) memarea_strndup(area,(str),(n))
29 
30 #define RET_ERR(msg) \
31  STMT_BEGIN \
32  if (tok) token_clear(tok); \
33  tok = ALLOC_ZERO(sizeof(directory_token_t)); \
34  tok->tp = ERR_; \
35  tok->error = STRDUP(msg); \
36  goto done_tokenizing; \
37  STMT_END
38 
40 void
42 {
43  if (tok->key)
44  crypto_pk_free(tok->key);
45 }
46 
52 int
54  const char *start, const char *end, smartlist_t *out,
55  const token_rule_t *table, int flags)
56 {
57  const char **s;
58  directory_token_t *tok = NULL;
59  int counts[NIL_];
60  int i;
61  int first_nonannotation;
62  int prev_len = smartlist_len(out);
63  tor_assert(area);
64 
65  s = &start;
66  if (!end) {
67  end = start+strlen(start);
68  } else {
69  /* it's only meaningful to check for nuls if we got an end-of-string ptr */
70  if (memchr(start, '\0', end-start)) {
71  log_warn(LD_DIR, "parse error: internal NUL character.");
72  return -1;
73  }
74  }
75  for (i = 0; i < NIL_; ++i)
76  counts[i] = 0;
77 
78  SMARTLIST_FOREACH(out, const directory_token_t *, t, ++counts[t->tp]);
79 
80  while (*s < end && (!tok || tok->tp != EOF_)) {
81  tok = get_next_token(area, s, end, table);
82  if (tok->tp == ERR_) {
83  log_warn(LD_DIR, "parse error: %s", tok->error);
84  token_clear(tok);
85  return -1;
86  }
87  ++counts[tok->tp];
88  smartlist_add(out, tok);
89  *s = eat_whitespace_eos(*s, end);
90  }
91 
92  if (flags & TS_NOCHECK)
93  return 0;
94 
95  if ((flags & TS_ANNOTATIONS_OK)) {
96  first_nonannotation = -1;
97  for (i = 0; i < smartlist_len(out); ++i) {
98  tok = smartlist_get(out, i);
99  if (tok->tp < MIN_ANNOTATION || tok->tp > MAX_ANNOTATION) {
100  first_nonannotation = i;
101  break;
102  }
103  }
104  if (first_nonannotation < 0) {
105  log_warn(LD_DIR, "parse error: item contains only annotations");
106  return -1;
107  }
108  for (i=first_nonannotation; i < smartlist_len(out); ++i) {
109  tok = smartlist_get(out, i);
110  if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
111  log_warn(LD_DIR, "parse error: Annotations mixed with keywords");
112  return -1;
113  }
114  }
115  if ((flags & TS_NO_NEW_ANNOTATIONS)) {
116  if (first_nonannotation != prev_len) {
117  log_warn(LD_DIR, "parse error: Unexpected annotations.");
118  return -1;
119  }
120  }
121  } else {
122  for (i=0; i < smartlist_len(out); ++i) {
123  tok = smartlist_get(out, i);
124  if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
125  log_warn(LD_DIR, "parse error: no annotations allowed.");
126  return -1;
127  }
128  }
129  first_nonannotation = 0;
130  }
131  for (i = 0; table[i].t; ++i) {
132  if (counts[table[i].v] < table[i].min_cnt) {
133  log_warn(LD_DIR, "Parse error: missing %s element.", table[i].t);
134  return -1;
135  }
136  if (counts[table[i].v] > table[i].max_cnt) {
137  log_warn(LD_DIR, "Parse error: too many %s elements.", table[i].t);
138  return -1;
139  }
140  if (table[i].pos & AT_START) {
141  if (smartlist_len(out) < 1 ||
142  (tok = smartlist_get(out, first_nonannotation))->tp != table[i].v) {
143  log_warn(LD_DIR, "Parse error: first item is not %s.", table[i].t);
144  return -1;
145  }
146  }
147  if (table[i].pos & AT_END) {
148  if (smartlist_len(out) < 1 ||
149  (tok = smartlist_get(out, smartlist_len(out)-1))->tp != table[i].v) {
150  log_warn(LD_DIR, "Parse error: last item is not %s.", table[i].t);
151  return -1;
152  }
153  }
154  }
155  return 0;
156 }
157 
163 static inline int
165  const char *s, const char *eol)
166 {
168 #define MAX_ARGS 512
169  char *mem = memarea_strndup(area, s, eol-s);
170  char *cp = mem;
171  int j = 0;
172  char *args[MAX_ARGS];
173  while (*cp) {
174  if (j == MAX_ARGS)
175  return -1;
176  args[j++] = cp;
177  cp = (char*)find_whitespace(cp);
178  if (!cp || !*cp)
179  break; /* End of the line. */
180  *cp++ = '\0';
181  cp = (char*)eat_whitespace(cp);
182  }
183  tok->n_args = j;
184  tok->args = memarea_memdup(area, args, j*sizeof(char*));
185  return j;
186 #undef MAX_ARGS
187 }
188 
194 static inline directory_token_t *
195 token_check_object(memarea_t *area, const char *kwd,
196  directory_token_t *tok, obj_syntax o_syn)
197 {
198  char ebuf[128];
199  switch (o_syn) {
200  case NO_OBJ:
201  /* No object is allowed for this token. */
202  if (tok->object_body) {
203  tor_snprintf(ebuf, sizeof(ebuf), "Unexpected object for %s", kwd);
204  RET_ERR(ebuf);
205  }
206  if (tok->key) {
207  tor_snprintf(ebuf, sizeof(ebuf), "Unexpected public key for %s", kwd);
208  RET_ERR(ebuf);
209  }
210  break;
211  case NEED_OBJ:
212  /* There must be a (non-key) object. */
213  if (!tok->object_body) {
214  tor_snprintf(ebuf, sizeof(ebuf), "Missing object for %s", kwd);
215  RET_ERR(ebuf);
216  }
217  break;
218  case NEED_KEY_1024: /* There must be a 1024-bit public key. */
219  case NEED_SKEY_1024: /* There must be a 1024-bit private key. */
220  if (tok->key && crypto_pk_num_bits(tok->key) != PK_BYTES*8) {
221  tor_snprintf(ebuf, sizeof(ebuf), "Wrong size on key for %s: %d bits",
222  kwd, crypto_pk_num_bits(tok->key));
223  RET_ERR(ebuf);
224  }
225  /* fall through */
226  case NEED_KEY: /* There must be some kind of key. */
227  if (!tok->key) {
228  tor_snprintf(ebuf, sizeof(ebuf), "Missing public key for %s", kwd);
229  RET_ERR(ebuf);
230  }
231  if (o_syn != NEED_SKEY_1024) {
232  if (crypto_pk_key_is_private(tok->key)) {
233  tor_snprintf(ebuf, sizeof(ebuf),
234  "Private key given for %s, which wants a public key", kwd);
235  RET_ERR(ebuf);
236  }
237  } else { /* o_syn == NEED_SKEY_1024 */
238  if (!crypto_pk_key_is_private(tok->key)) {
239  tor_snprintf(ebuf, sizeof(ebuf),
240  "Public key given for %s, which wants a private key", kwd);
241  RET_ERR(ebuf);
242  }
243  }
244  break;
245  case OBJ_OK:
246  /* Anything goes with this token. */
247  break;
248  }
249 
250  done_tokenizing:
251  return tok;
252 }
253 
257 static bool
258 mem_eq_token(const void *mem, size_t memlen, const char *token)
259 {
260  size_t len = strlen(token);
261  return memlen == len && fast_memeq(mem, token, len);
262 }
263 
270  const char **s, const char *eos, const token_rule_t *table)
271 {
274 #define MAX_UNPARSED_OBJECT_SIZE (128*1024)
275 
277 #define MAX_LINE_LENGTH (128*1024)
278 
279  const char *next, *eol;
280  size_t obname_len;
281  int i;
282  directory_token_t *tok;
283  obj_syntax o_syn = NO_OBJ;
284  char ebuf[128];
285  const char *kwd = "";
286 
287  tor_assert(area);
288  tok = ALLOC_ZERO(sizeof(directory_token_t));
289  tok->tp = ERR_;
290 
291  /* Set *s to first token, eol to end-of-line, next to after first token */
292  *s = eat_whitespace_eos(*s, eos); /* eat multi-line whitespace */
293  tor_assert(eos >= *s);
294  eol = memchr(*s, '\n', eos-*s);
295  if (!eol)
296  eol = eos;
297  if (eol - *s > MAX_LINE_LENGTH) {
298  RET_ERR("Line far too long");
299  }
300 
301  next = find_whitespace_eos(*s, eol);
302 
303  if (mem_eq_token(*s, next-*s, "opt")) {
304  /* Skip past an "opt" at the start of the line. */
305  *s = eat_whitespace_eos_no_nl(next, eol);
306  next = find_whitespace_eos(*s, eol);
307  } else if (*s == eos) { /* If no "opt", and end-of-line, line is invalid */
308  RET_ERR("Unexpected EOF");
309  }
310 
311  /* Search the table for the appropriate entry. (I tried a binary search
312  * instead, but it wasn't any faster.) */
313  for (i = 0; table[i].t ; ++i) {
314  if (mem_eq_token(*s, next-*s, table[i].t)) {
315  /* We've found the keyword. */
316  kwd = table[i].t;
317  tok->tp = table[i].v;
318  o_syn = table[i].os;
319  *s = eat_whitespace_eos_no_nl(next, eol);
320  /* We go ahead whether there are arguments or not, so that tok->args is
321  * always set if we want arguments. */
322  if (table[i].concat_args) {
323  /* The keyword takes the line as a single argument */
324  tok->args = ALLOC(sizeof(char*));
325  tok->args[0] = STRNDUP(*s,eol-*s); /* Grab everything on line */
326  tok->n_args = 1;
327  } else {
328  /* This keyword takes multiple arguments. */
329  if (get_token_arguments(area, tok, *s, eol)<0) {
330  tor_snprintf(ebuf, sizeof(ebuf),"Far too many arguments to %s", kwd);
331  RET_ERR(ebuf);
332  }
333  *s = eol;
334  }
335  if (tok->n_args < table[i].min_args) {
336  tor_snprintf(ebuf, sizeof(ebuf), "Too few arguments to %s", kwd);
337  RET_ERR(ebuf);
338  } else if (tok->n_args > table[i].max_args) {
339  tor_snprintf(ebuf, sizeof(ebuf), "Too many arguments to %s", kwd);
340  RET_ERR(ebuf);
341  }
342  break;
343  }
344  }
345 
346  if (tok->tp == ERR_) {
347  /* No keyword matched; call it an "K_opt" or "A_unrecognized" */
348  if (*s < eol && **s == '@')
349  tok->tp = A_UNKNOWN_;
350  else
351  tok->tp = K_OPT;
352  tok->args = ALLOC(sizeof(char*));
353  tok->args[0] = STRNDUP(*s, eol-*s);
354  tok->n_args = 1;
355  o_syn = OBJ_OK;
356  }
357 
358  /* Check whether there's an object present */
359  *s = eat_whitespace_eos(eol, eos); /* Scan from end of first line */
360  tor_assert(eos >= *s);
361  eol = memchr(*s, '\n', eos-*s);
362  if (!eol || eol-*s<11 || strcmpstart(*s, "-----BEGIN ")) /* No object. */
363  goto check_object;
364 
365  if (eol - *s <= 16 || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
366  !mem_eq_token(eol-5, 5, "-----") || /* nuls or invalid endings */
367  (eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */
368  RET_ERR("Malformed object: bad begin line");
369  }
370  tok->object_type = STRNDUP(*s+11, eol-*s-16);
371  obname_len = eol-*s-16; /* store objname length here to avoid a strlen() */
372  *s = eol+1; /* Set *s to possible start of object data (could be eos) */
373 
374  /* Go to the end of the object */
375  next = tor_memstr(*s, eos-*s, "-----END ");
376  if (!next) {
377  RET_ERR("Malformed object: missing object end line");
378  }
379  tor_assert(eos >= next);
380  eol = memchr(next, '\n', eos-next);
381  if (!eol) /* end-of-line marker, or eos if there's no '\n' */
382  eol = eos;
383  /* Validate the ending tag, which should be 9 + NAME + 5 + eol */
384  if ((size_t)(eol-next) != 9+obname_len+5 ||
385  !mem_eq_token(next+9, obname_len, tok->object_type) ||
386  !mem_eq_token(eol-5, 5, "-----")) {
387  tor_snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s",
388  tok->object_type);
389  ebuf[sizeof(ebuf)-1] = '\0';
390  RET_ERR(ebuf);
391  }
392  if (next - *s > MAX_UNPARSED_OBJECT_SIZE)
393  RET_ERR("Couldn't parse object: missing footer or object much too big.");
394 
395  {
396  int r;
397  size_t maxsize = base64_decode_maxsize(next-*s);
398  tok->object_body = ALLOC(maxsize);
399  r = base64_decode(tok->object_body, maxsize, *s, next-*s);
400  if (r<0)
401  RET_ERR("Malformed object: bad base64-encoded data");
402  tok->object_size = r;
403  }
404 
405  if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */
407  if (! tok->key)
408  RET_ERR("Couldn't parse public key.");
409  } else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */
411  tok->object_size);
412  if (! tok->key)
413  RET_ERR("Couldn't parse private key.");
414  }
415  *s = eol;
416 
417  check_object:
418  tok = token_check_object(area, kwd, tok, o_syn);
419 
420  done_tokenizing:
421  return tok;
422 
423 #undef RET_ERR
424 #undef ALLOC
425 #undef ALLOC_ZERO
426 #undef STRDUP
427 #undef STRNDUP
428 }
429 
435  const char *keyword_as_string)
436 {
437  directory_token_t *tok = find_opt_by_keyword(s, keyword);
438  if (PREDICT_UNLIKELY(!tok)) {
439  log_err(LD_BUG, "Missing %s [%d] in directory object that should have "
440  "been validated. Internal error.", keyword_as_string, (int)keyword);
441  tor_assert(tok);
442  }
443  return tok;
444 }
445 
451 {
452  SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t);
453  return NULL;
454 }
455 
460 smartlist_t *
462 {
463  smartlist_t *out = NULL;
465  if (t->tp == k) {
466  if (!out)
467  out = smartlist_new();
468  smartlist_add(out, t);
469  });
470  return out;
471 }
size_t base64_decode_maxsize(size_t srclen)
Definition: binascii.c:187
directory_token_t * find_by_keyword_(smartlist_t *s, directory_keyword keyword, const char *keyword_as_string)
Definition: parsecommon.c:434
struct crypto_pk_t * key
Definition: parsecommon.h:210
obj_syntax os
Definition: parsecommon.h:288
smartlist_t * find_all_by_keyword(const smartlist_t *s, directory_keyword k)
Definition: parsecommon.c:461
Header for printf.c.
Header for smartlist.c.
Headers for di_ops.c.
char * memarea_strndup(memarea_t *area, const char *s, size_t n)
Definition: memarea.c:273
directory_token_t * get_next_token(memarea_t *area, const char **s, const char *eos, const token_rule_t *table)
Definition: parsecommon.c:269
static directory_token_t * token_check_object(memarea_t *area, const char *kwd, directory_token_t *tok, obj_syntax o_syn)
Definition: parsecommon.c:195
void smartlist_add(smartlist_t *sl, void *element)
Headers for crypto_rsa.c.
const char * find_whitespace(const char *s)
Definition: util_string.c:344
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:206
Header for util_string.c.
crypto_pk_t * crypto_pk_asn1_decode_private(const char *str, size_t len)
const char * eat_whitespace_eos_no_nl(const char *s, const char *eos)
Definition: util_string.c:333
#define PK_BYTES
Definition: crypto_rsa.h:24
const char * t
Definition: parsecommon.h:277
void * memarea_memdup(memarea_t *area, const void *s, size_t n)
Definition: memarea.c:257
crypto_pk_t * crypto_pk_asn1_decode(const char *str, size_t len)
tor_assert(buffer)
int crypto_pk_key_is_private(const crypto_pk_t *key)
directory_keyword tp
Definition: parsecommon.h:202
directory_keyword v
Definition: parsecommon.h:279
const char * eat_whitespace(const char *s)
Definition: util_string.c:268
Header for binascii.c.
Header for memarea.c.
directory_token_t * find_opt_by_keyword(const smartlist_t *s, directory_keyword keyword)
Definition: parsecommon.c:450
const char * find_whitespace_eos(const char *s, const char *eos)
Definition: util_string.c:366
#define LD_DIR
Definition: log.h:85
Header file for parsecommon.c.
static bool mem_eq_token(const void *mem, size_t memlen, const char *token)
Definition: parsecommon.c:258
int tor_snprintf(char *str, size_t size, const char *format,...)
Definition: printf.c:27
int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
Definition: binascii.c:396
#define SMARTLIST_FOREACH(sl, type, var, cmd)
directory_keyword
Definition: parsecommon.h:23
obj_syntax
Definition: parsecommon.h:218
void token_clear(directory_token_t *tok)
Definition: parsecommon.c:41
static int get_token_arguments(memarea_t *area, directory_token_t *tok, const char *s, const char *eol)
Definition: parsecommon.c:164
Headers for log.c.
int crypto_pk_num_bits(crypto_pk_t *env)
Macros to manage assertions, fatal and non-fatal.
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 LD_BUG
Definition: log.h:83
const char * eat_whitespace_eos(const char *s, const char *eos)
Definition: util_string.c:295