tor

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

dsigs_parse.c (9304B)


      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 dsigs_parse.h
      9 * \brief Code to parse and validate detached-signature objects
     10 **/
     11 
     12 #include "core/or/or.h"
     13 #include "feature/dirparse/parsecommon.h"
     14 #include "feature/dirparse/unparseable.h"
     15 #include "feature/nodelist/networkstatus.h"
     16 #include "lib/memarea/memarea.h"
     17 
     18 #include "feature/dirauth/dsigs_parse.h"
     19 #include "feature/dirauth/ns_detached_signatures_st.h"
     20 #include "feature/nodelist/document_signature_st.h"
     21 
     22 /** List of tokens recognized in detached networkstatus signature documents. */
     23 static token_rule_t networkstatus_detached_signature_token_table[] = {
     24  T1_START("consensus-digest", K_CONSENSUS_DIGEST, GE(1),       NO_OBJ ),
     25  T("additional-digest",       K_ADDITIONAL_DIGEST,GE(3),       NO_OBJ ),
     26  T1("valid-after",            K_VALID_AFTER,      CONCAT_ARGS, NO_OBJ ),
     27  T1("fresh-until",            K_FRESH_UNTIL,      CONCAT_ARGS, NO_OBJ ),
     28  T1("valid-until",            K_VALID_UNTIL,      CONCAT_ARGS, NO_OBJ ),
     29  T("additional-signature",  K_ADDITIONAL_SIGNATURE, GE(4),   NEED_OBJ ),
     30  T1N("directory-signature", K_DIRECTORY_SIGNATURE,  GE(2),   NEED_OBJ ),
     31  END_OF_TABLE
     32 };
     33 
     34 /** Return the common_digests_t that holds the digests of the
     35 * <b>flavor_name</b>-flavored networkstatus according to the detached
     36 * signatures document <b>sigs</b>, allocating a new common_digests_t as
     37 * needed. */
     38 static common_digests_t *
     39 detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
     40 {
     41  common_digests_t *d = strmap_get(sigs->digests, flavor_name);
     42  if (!d) {
     43    d = tor_malloc_zero(sizeof(common_digests_t));
     44    strmap_set(sigs->digests, flavor_name, d);
     45  }
     46  return d;
     47 }
     48 
     49 /** Return the list of signatures of the <b>flavor_name</b>-flavored
     50 * networkstatus according to the detached signatures document <b>sigs</b>,
     51 * allocating a new common_digests_t as needed. */
     52 static smartlist_t *
     53 detached_get_signatures(ns_detached_signatures_t *sigs,
     54                        const char *flavor_name)
     55 {
     56  smartlist_t *sl = strmap_get(sigs->signatures, flavor_name);
     57  if (!sl) {
     58    sl = smartlist_new();
     59    strmap_set(sigs->signatures, flavor_name, sl);
     60  }
     61  return sl;
     62 }
     63 
     64 /** Parse a detached v3 networkstatus signature document between <b>s</b> and
     65 * <b>eos</b> and return the result.  Return -1 on failure. */
     66 ns_detached_signatures_t *
     67 networkstatus_parse_detached_signatures(const char *s, const char *eos)
     68 {
     69  /* XXXX there is too much duplicate shared between this function and
     70   * networkstatus_parse_vote_from_string(). */
     71  directory_token_t *tok;
     72  memarea_t *area = NULL;
     73  common_digests_t *digests;
     74 
     75  smartlist_t *tokens = smartlist_new();
     76  ns_detached_signatures_t *sigs =
     77    tor_malloc_zero(sizeof(ns_detached_signatures_t));
     78  sigs->digests = strmap_new();
     79  sigs->signatures = strmap_new();
     80 
     81  if (!eos)
     82    eos = s + strlen(s);
     83 
     84  area = memarea_new();
     85  if (tokenize_string(area,s, eos, tokens,
     86                      networkstatus_detached_signature_token_table, 0)) {
     87    log_warn(LD_DIR, "Error tokenizing detached networkstatus signatures");
     88    goto err;
     89  }
     90 
     91  /* Grab all the digest-like tokens. */
     92  SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
     93    const char *algname;
     94    digest_algorithm_t alg;
     95    const char *flavor;
     96    const char *hexdigest;
     97    size_t expected_length, digest_length;
     98 
     99    tok = _tok;
    100 
    101    if (tok->tp == K_CONSENSUS_DIGEST) {
    102      algname = "sha1";
    103      alg = DIGEST_SHA1;
    104      flavor = "ns";
    105      hexdigest = tok->args[0];
    106    } else if (tok->tp == K_ADDITIONAL_DIGEST) {
    107      int a = crypto_digest_algorithm_parse_name(tok->args[1]);
    108      if (a<0) {
    109        log_warn(LD_DIR, "Unrecognized algorithm name %s", tok->args[0]);
    110        continue;
    111      }
    112      alg = (digest_algorithm_t) a;
    113      flavor = tok->args[0];
    114      algname = tok->args[1];
    115      hexdigest = tok->args[2];
    116    } else {
    117      continue;
    118    }
    119 
    120    digest_length = crypto_digest_algorithm_get_length(alg);
    121    expected_length = digest_length * 2; /* hex encoding */
    122 
    123    if (strlen(hexdigest) != expected_length) {
    124      log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
    125               "networkstatus signatures");
    126      goto err;
    127    }
    128    digests = detached_get_digests(sigs, flavor);
    129    tor_assert(digests);
    130    if (!fast_mem_is_zero(digests->d[alg], digest_length)) {
    131      log_warn(LD_DIR, "Multiple digests for %s with %s on detached "
    132               "signatures document", flavor, algname);
    133      continue;
    134    }
    135    if (base16_decode(digests->d[alg], digest_length,
    136                      hexdigest, strlen(hexdigest)) != (int) digest_length) {
    137      log_warn(LD_DIR, "Bad encoding on consensus-digest in detached "
    138               "networkstatus signatures");
    139      goto err;
    140    }
    141  } SMARTLIST_FOREACH_END(_tok);
    142 
    143  tok = find_by_keyword(tokens, K_VALID_AFTER);
    144  if (parse_iso_time(tok->args[0], &sigs->valid_after)) {
    145    log_warn(LD_DIR, "Bad valid-after in detached networkstatus signatures");
    146    goto err;
    147  }
    148 
    149  tok = find_by_keyword(tokens, K_FRESH_UNTIL);
    150  if (parse_iso_time(tok->args[0], &sigs->fresh_until)) {
    151    log_warn(LD_DIR, "Bad fresh-until in detached networkstatus signatures");
    152    goto err;
    153  }
    154 
    155  tok = find_by_keyword(tokens, K_VALID_UNTIL);
    156  if (parse_iso_time(tok->args[0], &sigs->valid_until)) {
    157    log_warn(LD_DIR, "Bad valid-until in detached networkstatus signatures");
    158    goto err;
    159  }
    160 
    161  SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
    162    const char *id_hexdigest;
    163    const char *sk_hexdigest;
    164    const char *algname;
    165    const char *flavor;
    166    digest_algorithm_t alg;
    167 
    168    char id_digest[DIGEST_LEN];
    169    char sk_digest[DIGEST_LEN];
    170    smartlist_t *siglist;
    171    document_signature_t *sig;
    172    int is_duplicate;
    173 
    174    tok = _tok;
    175    if (tok->tp == K_DIRECTORY_SIGNATURE) {
    176      tor_assert(tok->n_args >= 2);
    177      flavor = "ns";
    178      algname = "sha1";
    179      id_hexdigest = tok->args[0];
    180      sk_hexdigest = tok->args[1];
    181    } else if (tok->tp == K_ADDITIONAL_SIGNATURE) {
    182      tor_assert(tok->n_args >= 4);
    183      flavor = tok->args[0];
    184      algname = tok->args[1];
    185      id_hexdigest = tok->args[2];
    186      sk_hexdigest = tok->args[3];
    187    } else {
    188      continue;
    189    }
    190 
    191    {
    192      int a = crypto_digest_algorithm_parse_name(algname);
    193      if (a<0) {
    194        log_warn(LD_DIR, "Unrecognized algorithm name %s", algname);
    195        continue;
    196      }
    197      alg = (digest_algorithm_t) a;
    198    }
    199 
    200    if (!tok->object_type ||
    201        strcmp(tok->object_type, "SIGNATURE") ||
    202        tok->object_size < 128 || tok->object_size > 512) {
    203      log_warn(LD_DIR, "Bad object type or length on directory-signature");
    204      goto err;
    205    }
    206 
    207    if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
    208        base16_decode(id_digest, sizeof(id_digest),
    209                      id_hexdigest, HEX_DIGEST_LEN) != sizeof(id_digest)) {
    210      log_warn(LD_DIR, "Error decoding declared identity %s in "
    211               "network-status vote.", escaped(id_hexdigest));
    212      goto err;
    213    }
    214    if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
    215        base16_decode(sk_digest, sizeof(sk_digest),
    216                      sk_hexdigest, HEX_DIGEST_LEN) != sizeof(sk_digest)) {
    217      log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
    218               "network-status vote.", escaped(sk_hexdigest));
    219      goto err;
    220    }
    221 
    222    siglist = detached_get_signatures(sigs, flavor);
    223    is_duplicate = 0;
    224    SMARTLIST_FOREACH(siglist, document_signature_t *, dsig, {
    225      if (dsig->alg == alg &&
    226          tor_memeq(id_digest, dsig->identity_digest, DIGEST_LEN) &&
    227          tor_memeq(sk_digest, dsig->signing_key_digest, DIGEST_LEN)) {
    228        is_duplicate = 1;
    229      }
    230    });
    231    if (is_duplicate) {
    232      log_warn(LD_DIR, "Two signatures with identical keys and algorithm "
    233               "found.");
    234      continue;
    235    }
    236 
    237    sig = tor_malloc_zero(sizeof(document_signature_t));
    238    sig->alg = alg;
    239    memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
    240    memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
    241    if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
    242      tor_free(sig);
    243      goto err;
    244    }
    245    sig->signature = tor_memdup(tok->object_body, tok->object_size);
    246    sig->signature_len = (int) tok->object_size;
    247 
    248    smartlist_add(siglist, sig);
    249  } SMARTLIST_FOREACH_END(_tok);
    250 
    251  goto done;
    252 err:
    253  ns_detached_signatures_free(sigs);
    254  sigs = NULL;
    255 done:
    256  SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
    257  smartlist_free(tokens);
    258  if (area) {
    259    DUMP_AREA(area, "detached signatures");
    260    memarea_drop_all(area);
    261  }
    262  return sigs;
    263 }
    264 
    265 /** Release all storage held in <b>s</b>. */
    266 void
    267 ns_detached_signatures_free_(ns_detached_signatures_t *s)
    268 {
    269  if (!s)
    270    return;
    271  if (s->signatures) {
    272    STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) {
    273      SMARTLIST_FOREACH(sigs, document_signature_t *, sig,
    274                        document_signature_free(sig));
    275      smartlist_free(sigs);
    276    } STRMAP_FOREACH_END;
    277    strmap_free(s->signatures, NULL);
    278    strmap_free(s->digests, tor_free_);
    279  }
    280 
    281  tor_free(s);
    282 }