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 : }
|