tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

authcert_parse.c (7165B)


      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 authcert_parse.c
      9 * @brief Authority certificate parsing.
     10 **/
     11 
     12 #include "core/or/or.h"
     13 #include "feature/dirparse/authcert_parse.h"
     14 #include "feature/dirparse/parsecommon.h"
     15 #include "feature/dirparse/sigcommon.h"
     16 #include "feature/dirparse/unparseable.h"
     17 #include "feature/nodelist/authcert.h"
     18 #include "lib/memarea/memarea.h"
     19 
     20 #include "feature/nodelist/authority_cert_st.h"
     21 #include "feature/dirparse/authcert_members.h"
     22 
     23 /** List of tokens recognized in V3 authority certificates. */
     24 // clang-format off
     25 static token_rule_t dir_key_certificate_table[] = {
     26  AUTHCERT_MEMBERS,
     27  T1("fingerprint",      K_FINGERPRINT,              CONCAT_ARGS, NO_OBJ ),
     28  END_OF_TABLE
     29 };
     30 // clang-format on
     31 
     32 /** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to
     33 * the first character after the certificate. */
     34 authority_cert_t *
     35 authority_cert_parse_from_string(const char *s, size_t maxlen,
     36                                 const char **end_of_string)
     37 {
     38  /** Reject any certificate at least this big; it is probably an overflow, an
     39   * attack, a bug, or some other nonsense. */
     40 #define MAX_CERT_SIZE (128*1024)
     41 
     42  authority_cert_t *cert = NULL, *old_cert;
     43  smartlist_t *tokens = NULL;
     44  char digest[DIGEST_LEN];
     45  directory_token_t *tok;
     46  char fp_declared[DIGEST_LEN];
     47  const char *eos;
     48  size_t len;
     49  int found;
     50  memarea_t *area = NULL;
     51  const char *end_of_s = s + maxlen;
     52  const char *s_dup = s;
     53 
     54  s = eat_whitespace_eos(s, end_of_s);
     55  eos = tor_memstr(s, end_of_s - s, "\ndir-key-certification");
     56  if (! eos) {
     57    log_warn(LD_DIR, "No signature found on key certificate");
     58    return NULL;
     59  }
     60  eos = tor_memstr(eos, end_of_s - eos, "\n-----END SIGNATURE-----\n");
     61  if (! eos) {
     62    log_warn(LD_DIR, "No end-of-signature found on key certificate");
     63    return NULL;
     64  }
     65  eos = memchr(eos+2, '\n', end_of_s - (eos+2));
     66  tor_assert(eos);
     67  ++eos;
     68  len = eos - s;
     69 
     70  if (len > MAX_CERT_SIZE) {
     71    log_warn(LD_DIR, "Certificate is far too big (at %lu bytes long); "
     72             "rejecting", (unsigned long)len);
     73    return NULL;
     74  }
     75 
     76  tokens = smartlist_new();
     77  area = memarea_new();
     78  if (tokenize_string(area,s, eos, tokens, dir_key_certificate_table, 0) < 0) {
     79    log_warn(LD_DIR, "Error tokenizing key certificate");
     80    goto err;
     81  }
     82  if (router_get_hash_impl(s, eos - s, digest, "dir-key-certificate-version",
     83                           "\ndir-key-certification", '\n', DIGEST_SHA1) < 0)
     84    goto err;
     85  tok = smartlist_get(tokens, 0);
     86  if (tok->tp != K_DIR_KEY_CERTIFICATE_VERSION || strcmp(tok->args[0], "3")) {
     87    log_warn(LD_DIR,
     88             "Key certificate does not begin with a recognized version (3).");
     89    goto err;
     90  }
     91 
     92  cert = tor_malloc_zero(sizeof(authority_cert_t));
     93  memcpy(cert->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
     94 
     95  tok = find_by_keyword(tokens, K_DIR_SIGNING_KEY);
     96  tor_assert(tok->key);
     97  cert->signing_key = tok->key;
     98  tok->key = NULL;
     99  if (crypto_pk_get_digest(cert->signing_key, cert->signing_key_digest))
    100    goto err;
    101 
    102  tok = find_by_keyword(tokens, K_DIR_IDENTITY_KEY);
    103  tor_assert(tok->key);
    104  cert->identity_key = tok->key;
    105  tok->key = NULL;
    106 
    107  tok = find_by_keyword(tokens, K_FINGERPRINT);
    108  tor_assert(tok->n_args);
    109  if (base16_decode(fp_declared, DIGEST_LEN, tok->args[0],
    110                    strlen(tok->args[0])) != DIGEST_LEN) {
    111    log_warn(LD_DIR, "Couldn't decode key certificate fingerprint %s",
    112             escaped(tok->args[0]));
    113    goto err;
    114  }
    115 
    116  if (crypto_pk_get_digest(cert->identity_key,
    117                           cert->cache_info.identity_digest))
    118    goto err;
    119 
    120  if (tor_memneq(cert->cache_info.identity_digest, fp_declared, DIGEST_LEN)) {
    121    log_warn(LD_DIR, "Digest of certificate key didn't match declared "
    122             "fingerprint");
    123    goto err;
    124  }
    125 
    126  tok = find_opt_by_keyword(tokens, K_DIR_ADDRESS);
    127  if (tok) {
    128    struct in_addr in;
    129    char *address = NULL;
    130    tor_assert(tok->n_args);
    131    /* XXX++ use some tor_addr parse function below instead. -RD */
    132    if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
    133                            &cert->ipv4_dirport) < 0 ||
    134        tor_inet_aton(address, &in) == 0) {
    135      log_warn(LD_DIR, "Couldn't parse dir-address in certificate");
    136      tor_free(address);
    137      goto err;
    138    }
    139    tor_addr_from_in(&cert->ipv4_addr, &in);
    140    tor_free(address);
    141  }
    142 
    143  tok = find_by_keyword(tokens, K_DIR_KEY_PUBLISHED);
    144  if (parse_iso_time(tok->args[0], &cert->cache_info.published_on) < 0) {
    145     goto err;
    146  }
    147  tok = find_by_keyword(tokens, K_DIR_KEY_EXPIRES);
    148  if (parse_iso_time(tok->args[0], &cert->expires) < 0) {
    149     goto err;
    150  }
    151 
    152  tok = smartlist_get(tokens, smartlist_len(tokens)-1);
    153  if (tok->tp != K_DIR_KEY_CERTIFICATION) {
    154    log_warn(LD_DIR, "Certificate didn't end with dir-key-certification.");
    155    goto err;
    156  }
    157 
    158  /* If we already have this cert, don't bother checking the signature. */
    159  old_cert = authority_cert_get_by_digests(
    160                                     cert->cache_info.identity_digest,
    161                                     cert->signing_key_digest);
    162  found = 0;
    163  if (old_cert) {
    164    /* XXXX We could just compare signed_descriptor_digest, but that wouldn't
    165     * buy us much. */
    166    if (old_cert->cache_info.signed_descriptor_len == len &&
    167        old_cert->cache_info.signed_descriptor_body &&
    168        tor_memeq(s, old_cert->cache_info.signed_descriptor_body, len)) {
    169      log_debug(LD_DIR, "We already checked the signature on this "
    170                "certificate; no need to do so again.");
    171      found = 1;
    172    }
    173  }
    174  if (!found) {
    175    if (check_signature_token(digest, DIGEST_LEN, tok, cert->identity_key, 0,
    176                              "key certificate")) {
    177      goto err;
    178    }
    179 
    180    tok = find_by_keyword(tokens, K_DIR_KEY_CROSSCERT);
    181    if (check_signature_token(cert->cache_info.identity_digest,
    182                              DIGEST_LEN,
    183                              tok,
    184                              cert->signing_key,
    185                              CST_NO_CHECK_OBJTYPE,
    186                              "key cross-certification")) {
    187      goto err;
    188    }
    189  }
    190 
    191  cert->cache_info.signed_descriptor_len = len;
    192  cert->cache_info.signed_descriptor_body = tor_malloc(len+1);
    193  memcpy(cert->cache_info.signed_descriptor_body, s, len);
    194  cert->cache_info.signed_descriptor_body[len] = 0;
    195  cert->cache_info.saved_location = SAVED_NOWHERE;
    196 
    197  if (end_of_string) {
    198    *end_of_string = eat_whitespace(eos);
    199  }
    200  SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
    201  smartlist_free(tokens);
    202  if (area) {
    203    DUMP_AREA(area, "authority cert");
    204    memarea_drop_all(area);
    205  }
    206  return cert;
    207 err:
    208  dump_desc(s_dup, "authority cert");
    209  authority_cert_free(cert);
    210  SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
    211  smartlist_free(tokens);
    212  if (area) {
    213    DUMP_AREA(area, "authority cert");
    214    memarea_drop_all(area);
    215  }
    216  return NULL;
    217 }