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 }