LCOV - code coverage report
Current view: top level - feature/dirauth - dsigs_parse.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 126 157 80.3 %
Date: 2021-11-24 03:28:48 Functions: 4 4 100.0 %

          Line data    Source code
       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          12 : detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
      40             : {
      41          12 :   common_digests_t *d = strmap_get(sigs->digests, flavor_name);
      42          12 :   if (!d) {
      43          12 :     d = tor_malloc_zero(sizeof(common_digests_t));
      44          12 :     strmap_set(sigs->digests, flavor_name, d);
      45             :   }
      46          12 :   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          18 : detached_get_signatures(ns_detached_signatures_t *sigs,
      54             :                         const char *flavor_name)
      55             : {
      56          18 :   smartlist_t *sl = strmap_get(sigs->signatures, flavor_name);
      57          18 :   if (!sl) {
      58          12 :     sl = smartlist_new();
      59          12 :     strmap_set(sigs->signatures, flavor_name, sl);
      60             :   }
      61          18 :   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           6 : 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           6 :   directory_token_t *tok;
      72           6 :   memarea_t *area = NULL;
      73           6 :   common_digests_t *digests;
      74             : 
      75           6 :   smartlist_t *tokens = smartlist_new();
      76          12 :   ns_detached_signatures_t *sigs =
      77           6 :     tor_malloc_zero(sizeof(ns_detached_signatures_t));
      78           6 :   sigs->digests = strmap_new();
      79           6 :   sigs->signatures = strmap_new();
      80             : 
      81           6 :   if (!eos)
      82           6 :     eos = s + strlen(s);
      83             : 
      84           6 :   area = memarea_new();
      85           6 :   if (tokenize_string(area,s, eos, tokens,
      86             :                       networkstatus_detached_signature_token_table, 0)) {
      87           0 :     log_warn(LD_DIR, "Error tokenizing detached networkstatus signatures");
      88           0 :     goto err;
      89             :   }
      90             : 
      91             :   /* Grab all the digest-like tokens. */
      92          54 :   SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
      93          48 :     const char *algname;
      94          48 :     digest_algorithm_t alg;
      95          48 :     const char *flavor;
      96          48 :     const char *hexdigest;
      97          48 :     size_t expected_length, digest_length;
      98             : 
      99          48 :     tok = _tok;
     100             : 
     101          48 :     if (tok->tp == K_CONSENSUS_DIGEST) {
     102           6 :       algname = "sha1";
     103           6 :       alg = DIGEST_SHA1;
     104           6 :       flavor = "ns";
     105           6 :       hexdigest = tok->args[0];
     106          42 :     } else if (tok->tp == K_ADDITIONAL_DIGEST) {
     107           6 :       int a = crypto_digest_algorithm_parse_name(tok->args[1]);
     108           6 :       if (a<0) {
     109           0 :         log_warn(LD_DIR, "Unrecognized algorithm name %s", tok->args[0]);
     110           0 :         continue;
     111             :       }
     112           6 :       alg = (digest_algorithm_t) a;
     113           6 :       flavor = tok->args[0];
     114           6 :       algname = tok->args[1];
     115           6 :       hexdigest = tok->args[2];
     116             :     } else {
     117          36 :       continue;
     118             :     }
     119             : 
     120          12 :     digest_length = crypto_digest_algorithm_get_length(alg);
     121          12 :     expected_length = digest_length * 2; /* hex encoding */
     122             : 
     123          12 :     if (strlen(hexdigest) != expected_length) {
     124           0 :       log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
     125             :                "networkstatus signatures");
     126           0 :       goto err;
     127             :     }
     128          12 :     digests = detached_get_digests(sigs, flavor);
     129          12 :     tor_assert(digests);
     130          12 :     if (!fast_mem_is_zero(digests->d[alg], digest_length)) {
     131           0 :       log_warn(LD_DIR, "Multiple digests for %s with %s on detached "
     132             :                "signatures document", flavor, algname);
     133           0 :       continue;
     134             :     }
     135          12 :     if (base16_decode(digests->d[alg], digest_length,
     136          12 :                       hexdigest, strlen(hexdigest)) != (int) digest_length) {
     137           0 :       log_warn(LD_DIR, "Bad encoding on consensus-digest in detached "
     138             :                "networkstatus signatures");
     139           0 :       goto err;
     140             :     }
     141          48 :   } SMARTLIST_FOREACH_END(_tok);
     142             : 
     143           6 :   tok = find_by_keyword(tokens, K_VALID_AFTER);
     144           6 :   if (parse_iso_time(tok->args[0], &sigs->valid_after)) {
     145           0 :     log_warn(LD_DIR, "Bad valid-after in detached networkstatus signatures");
     146           0 :     goto err;
     147             :   }
     148             : 
     149           6 :   tok = find_by_keyword(tokens, K_FRESH_UNTIL);
     150           6 :   if (parse_iso_time(tok->args[0], &sigs->fresh_until)) {
     151           0 :     log_warn(LD_DIR, "Bad fresh-until in detached networkstatus signatures");
     152           0 :     goto err;
     153             :   }
     154             : 
     155           6 :   tok = find_by_keyword(tokens, K_VALID_UNTIL);
     156           6 :   if (parse_iso_time(tok->args[0], &sigs->valid_until)) {
     157           0 :     log_warn(LD_DIR, "Bad valid-until in detached networkstatus signatures");
     158           0 :     goto err;
     159             :   }
     160             : 
     161          54 :   SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
     162          48 :     const char *id_hexdigest;
     163          48 :     const char *sk_hexdigest;
     164          48 :     const char *algname;
     165          48 :     const char *flavor;
     166          48 :     digest_algorithm_t alg;
     167             : 
     168          48 :     char id_digest[DIGEST_LEN];
     169          48 :     char sk_digest[DIGEST_LEN];
     170          48 :     smartlist_t *siglist;
     171          48 :     document_signature_t *sig;
     172          48 :     int is_duplicate;
     173             : 
     174          48 :     tok = _tok;
     175          48 :     if (tok->tp == K_DIRECTORY_SIGNATURE) {
     176           9 :       tor_assert(tok->n_args >= 2);
     177           9 :       flavor = "ns";
     178           9 :       algname = "sha1";
     179           9 :       id_hexdigest = tok->args[0];
     180           9 :       sk_hexdigest = tok->args[1];
     181          39 :     } else if (tok->tp == K_ADDITIONAL_SIGNATURE) {
     182           9 :       tor_assert(tok->n_args >= 4);
     183           9 :       flavor = tok->args[0];
     184           9 :       algname = tok->args[1];
     185           9 :       id_hexdigest = tok->args[2];
     186           9 :       sk_hexdigest = tok->args[3];
     187             :     } else {
     188          30 :       continue;
     189             :     }
     190             : 
     191             :     {
     192          18 :       int a = crypto_digest_algorithm_parse_name(algname);
     193          18 :       if (a<0) {
     194           0 :         log_warn(LD_DIR, "Unrecognized algorithm name %s", algname);
     195           0 :         continue;
     196             :       }
     197          18 :       alg = (digest_algorithm_t) a;
     198             :     }
     199             : 
     200          18 :     if (!tok->object_type ||
     201          18 :         strcmp(tok->object_type, "SIGNATURE") ||
     202          18 :         tok->object_size < 128 || tok->object_size > 512) {
     203           0 :       log_warn(LD_DIR, "Bad object type or length on directory-signature");
     204           0 :       goto err;
     205             :     }
     206             : 
     207          36 :     if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
     208          18 :         base16_decode(id_digest, sizeof(id_digest),
     209             :                       id_hexdigest, HEX_DIGEST_LEN) != sizeof(id_digest)) {
     210           0 :       log_warn(LD_DIR, "Error decoding declared identity %s in "
     211             :                "network-status vote.", escaped(id_hexdigest));
     212           0 :       goto err;
     213             :     }
     214          36 :     if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
     215          18 :         base16_decode(sk_digest, sizeof(sk_digest),
     216             :                       sk_hexdigest, HEX_DIGEST_LEN) != sizeof(sk_digest)) {
     217           0 :       log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
     218             :                "network-status vote.", escaped(sk_hexdigest));
     219           0 :       goto err;
     220             :     }
     221             : 
     222          18 :     siglist = detached_get_signatures(sigs, flavor);
     223          18 :     is_duplicate = 0;
     224          24 :     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          18 :     if (is_duplicate) {
     232           0 :       log_warn(LD_DIR, "Two signatures with identical keys and algorithm "
     233             :                "found.");
     234           0 :       continue;
     235             :     }
     236             : 
     237          18 :     sig = tor_malloc_zero(sizeof(document_signature_t));
     238          18 :     sig->alg = alg;
     239          18 :     memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
     240          18 :     memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
     241          18 :     if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
     242           0 :       tor_free(sig);
     243           0 :       goto err;
     244             :     }
     245          18 :     sig->signature = tor_memdup(tok->object_body, tok->object_size);
     246          18 :     sig->signature_len = (int) tok->object_size;
     247             : 
     248          18 :     smartlist_add(siglist, sig);
     249          48 :   } SMARTLIST_FOREACH_END(_tok);
     250             : 
     251           6 :   goto done;
     252           0 :  err:
     253           0 :   ns_detached_signatures_free(sigs);
     254           0 :   sigs = NULL;
     255           6 :  done:
     256          54 :   SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
     257           6 :   smartlist_free(tokens);
     258           6 :   if (area) {
     259           6 :     DUMP_AREA(area, "detached signatures");
     260           6 :     memarea_drop_all(area);
     261             :   }
     262           6 :   return sigs;
     263             : }
     264             : 
     265             : /** Release all storage held in <b>s</b>. */
     266             : void
     267           6 : ns_detached_signatures_free_(ns_detached_signatures_t *s)
     268             : {
     269           6 :   if (!s)
     270             :     return;
     271           6 :   if (s->signatures) {
     272          18 :     STRMAP_FOREACH(s->signatures, flavor, smartlist_t *, sigs) {
     273          30 :       SMARTLIST_FOREACH(sigs, document_signature_t *, sig,
     274             :                         document_signature_free(sig));
     275          12 :       smartlist_free(sigs);
     276           6 :     } STRMAP_FOREACH_END;
     277           6 :     strmap_free(s->signatures, NULL);
     278           6 :     strmap_free(s->digests, tor_free_);
     279             :   }
     280             : 
     281           6 :   tor_free(s);
     282             : }

Generated by: LCOV version 1.14