LCOV - code coverage report
Current view: top level - feature/dirparse - authcert_parse.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 115 120 95.8 %
Date: 2021-11-24 03:28:48 Functions: 1 1 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 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         483 : 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         483 :   authority_cert_t *cert = NULL, *old_cert;
      43         483 :   smartlist_t *tokens = NULL;
      44         483 :   char digest[DIGEST_LEN];
      45         483 :   directory_token_t *tok;
      46         483 :   char fp_declared[DIGEST_LEN];
      47         483 :   const char *eos;
      48         483 :   size_t len;
      49         483 :   int found;
      50         483 :   memarea_t *area = NULL;
      51         483 :   const char *end_of_s = s + maxlen;
      52         483 :   const char *s_dup = s;
      53             : 
      54         483 :   s = eat_whitespace_eos(s, end_of_s);
      55         483 :   eos = tor_memstr(s, end_of_s - s, "\ndir-key-certification");
      56         483 :   if (! eos) {
      57           2 :     log_warn(LD_DIR, "No signature found on key certificate");
      58           2 :     return NULL;
      59             :   }
      60         481 :   eos = tor_memstr(eos, end_of_s - eos, "\n-----END SIGNATURE-----\n");
      61         481 :   if (! eos) {
      62           3 :     log_warn(LD_DIR, "No end-of-signature found on key certificate");
      63           3 :     return NULL;
      64             :   }
      65         478 :   eos = memchr(eos+2, '\n', end_of_s - (eos+2));
      66         478 :   tor_assert(eos);
      67         478 :   ++eos;
      68         478 :   len = eos - s;
      69             : 
      70         478 :   if (len > MAX_CERT_SIZE) {
      71           0 :     log_warn(LD_DIR, "Certificate is far too big (at %lu bytes long); "
      72             :              "rejecting", (unsigned long)len);
      73           0 :     return NULL;
      74             :   }
      75             : 
      76         478 :   tokens = smartlist_new();
      77         478 :   area = memarea_new();
      78         478 :   if (tokenize_string(area,s, eos, tokens, dir_key_certificate_table, 0) < 0) {
      79           5 :     log_warn(LD_DIR, "Error tokenizing key certificate");
      80           5 :     goto err;
      81             :   }
      82         473 :   if (router_get_hash_impl(s, eos - s, digest, "dir-key-certificate-version",
      83             :                            "\ndir-key-certification", '\n', DIGEST_SHA1) < 0)
      84           0 :     goto err;
      85         473 :   tok = smartlist_get(tokens, 0);
      86         473 :   if (tok->tp != K_DIR_KEY_CERTIFICATE_VERSION || strcmp(tok->args[0], "3")) {
      87           4 :     log_warn(LD_DIR,
      88             :              "Key certificate does not begin with a recognized version (3).");
      89           4 :     goto err;
      90             :   }
      91             : 
      92         469 :   cert = tor_malloc_zero(sizeof(authority_cert_t));
      93         469 :   memcpy(cert->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
      94             : 
      95         469 :   tok = find_by_keyword(tokens, K_DIR_SIGNING_KEY);
      96         469 :   tor_assert(tok->key);
      97         469 :   cert->signing_key = tok->key;
      98         469 :   tok->key = NULL;
      99         469 :   if (crypto_pk_get_digest(cert->signing_key, cert->signing_key_digest))
     100           1 :     goto err;
     101             : 
     102         468 :   tok = find_by_keyword(tokens, K_DIR_IDENTITY_KEY);
     103         468 :   tor_assert(tok->key);
     104         468 :   cert->identity_key = tok->key;
     105         468 :   tok->key = NULL;
     106             : 
     107         468 :   tok = find_by_keyword(tokens, K_FINGERPRINT);
     108         468 :   tor_assert(tok->n_args);
     109         468 :   if (base16_decode(fp_declared, DIGEST_LEN, tok->args[0],
     110         468 :                     strlen(tok->args[0])) != DIGEST_LEN) {
     111           5 :     log_warn(LD_DIR, "Couldn't decode key certificate fingerprint %s",
     112             :              escaped(tok->args[0]));
     113           5 :     goto err;
     114             :   }
     115             : 
     116         463 :   if (crypto_pk_get_digest(cert->identity_key,
     117         463 :                            cert->cache_info.identity_digest))
     118           1 :     goto err;
     119             : 
     120         462 :   if (tor_memneq(cert->cache_info.identity_digest, fp_declared, DIGEST_LEN)) {
     121           4 :     log_warn(LD_DIR, "Digest of certificate key didn't match declared "
     122             :              "fingerprint");
     123           4 :     goto err;
     124             :   }
     125             : 
     126         458 :   tok = find_opt_by_keyword(tokens, K_DIR_ADDRESS);
     127         458 :   if (tok) {
     128         167 :     struct in_addr in;
     129         167 :     char *address = NULL;
     130         167 :     tor_assert(tok->n_args);
     131             :     /* XXX++ use some tor_addr parse function below instead. -RD */
     132         167 :     if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
     133         166 :                             &cert->ipv4_dirport) < 0 ||
     134         166 :         tor_inet_aton(address, &in) == 0) {
     135           9 :       log_warn(LD_DIR, "Couldn't parse dir-address in certificate");
     136           9 :       tor_free(address);
     137           9 :       goto err;
     138             :     }
     139         158 :     tor_addr_from_in(&cert->ipv4_addr, &in);
     140         158 :     tor_free(address);
     141             :   }
     142             : 
     143         449 :   tok = find_by_keyword(tokens, K_DIR_KEY_PUBLISHED);
     144         449 :   if (parse_iso_time(tok->args[0], &cert->cache_info.published_on) < 0) {
     145           2 :      goto err;
     146             :   }
     147         447 :   tok = find_by_keyword(tokens, K_DIR_KEY_EXPIRES);
     148         447 :   if (parse_iso_time(tok->args[0], &cert->expires) < 0) {
     149           1 :      goto err;
     150             :   }
     151             : 
     152         446 :   tok = smartlist_get(tokens, smartlist_len(tokens)-1);
     153         446 :   if (tok->tp != K_DIR_KEY_CERTIFICATION) {
     154           1 :     log_warn(LD_DIR, "Certificate didn't end with dir-key-certification.");
     155           1 :     goto err;
     156             :   }
     157             : 
     158             :   /* If we already have this cert, don't bother checking the signature. */
     159         445 :   old_cert = authority_cert_get_by_digests(
     160             :                                      cert->cache_info.identity_digest,
     161             :                                      cert->signing_key_digest);
     162         445 :   found = 0;
     163         445 :   if (old_cert) {
     164             :     /* XXXX We could just compare signed_descriptor_digest, but that wouldn't
     165             :      * buy us much. */
     166          26 :     if (old_cert->cache_info.signed_descriptor_len == len &&
     167          52 :         old_cert->cache_info.signed_descriptor_body &&
     168          26 :         tor_memeq(s, old_cert->cache_info.signed_descriptor_body, len)) {
     169          26 :       log_debug(LD_DIR, "We already checked the signature on this "
     170             :                 "certificate; no need to do so again.");
     171          26 :       found = 1;
     172             :     }
     173             :   }
     174          26 :   if (!found) {
     175         419 :     if (check_signature_token(digest, DIGEST_LEN, tok, cert->identity_key, 0,
     176             :                               "key certificate")) {
     177           0 :       goto err;
     178             :     }
     179             : 
     180         419 :     tok = find_by_keyword(tokens, K_DIR_KEY_CROSSCERT);
     181         419 :     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           0 :       goto err;
     188             :     }
     189             :   }
     190             : 
     191         445 :   cert->cache_info.signed_descriptor_len = len;
     192         445 :   cert->cache_info.signed_descriptor_body = tor_malloc(len+1);
     193         445 :   memcpy(cert->cache_info.signed_descriptor_body, s, len);
     194         445 :   cert->cache_info.signed_descriptor_body[len] = 0;
     195         445 :   cert->cache_info.saved_location = SAVED_NOWHERE;
     196             : 
     197         445 :   if (end_of_string) {
     198         394 :     *end_of_string = eat_whitespace(eos);
     199             :   }
     200       11149 :   SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
     201         445 :   smartlist_free(tokens);
     202         445 :   if (area) {
     203         445 :     DUMP_AREA(area, "authority cert");
     204         445 :     memarea_drop_all(area);
     205             :   }
     206             :   return cert;
     207          33 :  err:
     208          33 :   dump_desc(s_dup, "authority cert");
     209          33 :   authority_cert_free(cert);
     210        2051 :   SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
     211          33 :   smartlist_free(tokens);
     212          33 :   if (area) {
     213          33 :     DUMP_AREA(area, "authority cert");
     214          33 :     memarea_drop_all(area);
     215             :   }
     216             :   return NULL;
     217             : }

Generated by: LCOV version 1.14