/src/unbound/validator/val_anchor.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * validator/val_anchor.c - validator trust anchor storage. |
3 | | * |
4 | | * Copyright (c) 2007, NLnet Labs. All rights reserved. |
5 | | * |
6 | | * This software is open source. |
7 | | * |
8 | | * Redistribution and use in source and binary forms, with or without |
9 | | * modification, are permitted provided that the following conditions |
10 | | * are met: |
11 | | * |
12 | | * Redistributions of source code must retain the above copyright notice, |
13 | | * this list of conditions and the following disclaimer. |
14 | | * |
15 | | * Redistributions in binary form must reproduce the above copyright notice, |
16 | | * this list of conditions and the following disclaimer in the documentation |
17 | | * and/or other materials provided with the distribution. |
18 | | * |
19 | | * Neither the name of the NLNET LABS nor the names of its contributors may |
20 | | * be used to endorse or promote products derived from this software without |
21 | | * specific prior written permission. |
22 | | * |
23 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
27 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
29 | | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
30 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
31 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
32 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | | */ |
35 | | |
36 | | /** |
37 | | * \file |
38 | | * |
39 | | * This file contains storage for the trust anchors for the validator. |
40 | | */ |
41 | | #include "config.h" |
42 | | #include <ctype.h> |
43 | | #include "validator/val_anchor.h" |
44 | | #include "validator/val_sigcrypt.h" |
45 | | #include "validator/autotrust.h" |
46 | | #include "util/data/packed_rrset.h" |
47 | | #include "util/data/dname.h" |
48 | | #include "util/log.h" |
49 | | #include "util/net_help.h" |
50 | | #include "util/config_file.h" |
51 | | #include "util/as112.h" |
52 | | #include "sldns/sbuffer.h" |
53 | | #include "sldns/rrdef.h" |
54 | | #include "sldns/str2wire.h" |
55 | | #ifdef HAVE_GLOB_H |
56 | | #include <glob.h> |
57 | | #endif |
58 | | |
59 | | int |
60 | | anchor_cmp(const void* k1, const void* k2) |
61 | 0 | { |
62 | 0 | int m; |
63 | 0 | struct trust_anchor* n1 = (struct trust_anchor*)k1; |
64 | 0 | struct trust_anchor* n2 = (struct trust_anchor*)k2; |
65 | | /* no need to ntohs(class) because sort order is irrelevant */ |
66 | 0 | if(n1->dclass != n2->dclass) { |
67 | 0 | if(n1->dclass < n2->dclass) |
68 | 0 | return -1; |
69 | 0 | return 1; |
70 | 0 | } |
71 | 0 | return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs, |
72 | 0 | &m); |
73 | 0 | } |
74 | | |
75 | | struct val_anchors* |
76 | | anchors_create(void) |
77 | 0 | { |
78 | 0 | struct val_anchors* a = (struct val_anchors*)calloc(1, sizeof(*a)); |
79 | 0 | if(!a) |
80 | 0 | return NULL; |
81 | 0 | a->tree = rbtree_create(anchor_cmp); |
82 | 0 | if(!a->tree) { |
83 | 0 | anchors_delete(a); |
84 | 0 | return NULL; |
85 | 0 | } |
86 | 0 | a->autr = autr_global_create(); |
87 | 0 | if(!a->autr) { |
88 | 0 | anchors_delete(a); |
89 | 0 | return NULL; |
90 | 0 | } |
91 | 0 | lock_basic_init(&a->lock); |
92 | 0 | lock_protect(&a->lock, a, sizeof(*a)); |
93 | 0 | lock_protect(&a->lock, a->autr, sizeof(*a->autr)); |
94 | 0 | return a; |
95 | 0 | } |
96 | | |
97 | | /** delete assembled rrset */ |
98 | | static void |
99 | | assembled_rrset_delete(struct ub_packed_rrset_key* pkey) |
100 | 0 | { |
101 | 0 | if(!pkey) return; |
102 | 0 | if(pkey->entry.data) { |
103 | 0 | struct packed_rrset_data* pd = (struct packed_rrset_data*) |
104 | 0 | pkey->entry.data; |
105 | 0 | free(pd->rr_data); |
106 | 0 | free(pd->rr_ttl); |
107 | 0 | free(pd->rr_len); |
108 | 0 | free(pd); |
109 | 0 | } |
110 | 0 | free(pkey->rk.dname); |
111 | 0 | free(pkey); |
112 | 0 | } |
113 | | |
114 | | /** destroy locks in tree and delete autotrust anchors */ |
115 | | static void |
116 | | anchors_delfunc(rbnode_type* elem, void* ATTR_UNUSED(arg)) |
117 | 0 | { |
118 | 0 | struct trust_anchor* ta = (struct trust_anchor*)elem; |
119 | 0 | if(!ta) return; |
120 | 0 | if(ta->autr) { |
121 | 0 | autr_point_delete(ta); |
122 | 0 | } else { |
123 | 0 | struct ta_key* p, *np; |
124 | 0 | lock_basic_destroy(&ta->lock); |
125 | 0 | free(ta->name); |
126 | 0 | p = ta->keylist; |
127 | 0 | while(p) { |
128 | 0 | np = p->next; |
129 | 0 | free(p->data); |
130 | 0 | free(p); |
131 | 0 | p = np; |
132 | 0 | } |
133 | 0 | assembled_rrset_delete(ta->ds_rrset); |
134 | 0 | assembled_rrset_delete(ta->dnskey_rrset); |
135 | 0 | free(ta); |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | | void |
140 | | anchors_delete(struct val_anchors* anchors) |
141 | 0 | { |
142 | 0 | if(!anchors) |
143 | 0 | return; |
144 | 0 | lock_unprotect(&anchors->lock, anchors->autr); |
145 | 0 | lock_unprotect(&anchors->lock, anchors); |
146 | 0 | lock_basic_destroy(&anchors->lock); |
147 | 0 | if(anchors->tree) |
148 | 0 | traverse_postorder(anchors->tree, anchors_delfunc, NULL); |
149 | 0 | free(anchors->tree); |
150 | 0 | autr_global_delete(anchors->autr); |
151 | 0 | free(anchors); |
152 | 0 | } |
153 | | |
154 | | void |
155 | | anchors_init_parents_locked(struct val_anchors* anchors) |
156 | 0 | { |
157 | 0 | struct trust_anchor* node, *prev = NULL, *p; |
158 | 0 | int m; |
159 | | /* nobody else can grab locks because we hold the main lock. |
160 | | * Thus the previous items, after unlocked, are not deleted */ |
161 | 0 | RBTREE_FOR(node, struct trust_anchor*, anchors->tree) { |
162 | 0 | lock_basic_lock(&node->lock); |
163 | 0 | node->parent = NULL; |
164 | 0 | if(!prev || prev->dclass != node->dclass) { |
165 | 0 | prev = node; |
166 | 0 | lock_basic_unlock(&node->lock); |
167 | 0 | continue; |
168 | 0 | } |
169 | 0 | (void)dname_lab_cmp(prev->name, prev->namelabs, node->name, |
170 | 0 | node->namelabs, &m); /* we know prev is smaller */ |
171 | | /* sort order like: . com. bla.com. zwb.com. net. */ |
172 | | /* find the previous, or parent-parent-parent */ |
173 | 0 | for(p = prev; p; p = p->parent) |
174 | | /* looking for name with few labels, a parent */ |
175 | 0 | if(p->namelabs <= m) { |
176 | | /* ==: since prev matched m, this is closest*/ |
177 | | /* <: prev matches more, but is not a parent, |
178 | | * this one is a (grand)parent */ |
179 | 0 | node->parent = p; |
180 | 0 | break; |
181 | 0 | } |
182 | 0 | lock_basic_unlock(&node->lock); |
183 | 0 | prev = node; |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | /** initialise parent pointers in the tree */ |
188 | | static void |
189 | | init_parents(struct val_anchors* anchors) |
190 | 0 | { |
191 | 0 | lock_basic_lock(&anchors->lock); |
192 | 0 | anchors_init_parents_locked(anchors); |
193 | 0 | lock_basic_unlock(&anchors->lock); |
194 | 0 | } |
195 | | |
196 | | struct trust_anchor* |
197 | | anchor_find(struct val_anchors* anchors, uint8_t* name, int namelabs, |
198 | | size_t namelen, uint16_t dclass) |
199 | 0 | { |
200 | 0 | struct trust_anchor key; |
201 | 0 | rbnode_type* n; |
202 | 0 | if(!name) return NULL; |
203 | 0 | key.node.key = &key; |
204 | 0 | key.name = name; |
205 | 0 | key.namelabs = namelabs; |
206 | 0 | key.namelen = namelen; |
207 | 0 | key.dclass = dclass; |
208 | 0 | lock_basic_lock(&anchors->lock); |
209 | 0 | n = rbtree_search(anchors->tree, &key); |
210 | 0 | if(n) { |
211 | 0 | lock_basic_lock(&((struct trust_anchor*)n->key)->lock); |
212 | 0 | } |
213 | 0 | lock_basic_unlock(&anchors->lock); |
214 | 0 | if(!n) |
215 | 0 | return NULL; |
216 | 0 | return (struct trust_anchor*)n->key; |
217 | 0 | } |
218 | | |
219 | | /** create new trust anchor object */ |
220 | | static struct trust_anchor* |
221 | | anchor_new_ta(struct val_anchors* anchors, uint8_t* name, int namelabs, |
222 | | size_t namelen, uint16_t dclass, int lockit) |
223 | 0 | { |
224 | | #ifdef UNBOUND_DEBUG |
225 | | rbnode_type* r; |
226 | | #endif |
227 | 0 | struct trust_anchor* ta = (struct trust_anchor*)malloc( |
228 | 0 | sizeof(struct trust_anchor)); |
229 | 0 | if(!ta) |
230 | 0 | return NULL; |
231 | 0 | memset(ta, 0, sizeof(*ta)); |
232 | 0 | ta->node.key = ta; |
233 | 0 | ta->name = memdup(name, namelen); |
234 | 0 | if(!ta->name) { |
235 | 0 | free(ta); |
236 | 0 | return NULL; |
237 | 0 | } |
238 | 0 | ta->namelabs = namelabs; |
239 | 0 | ta->namelen = namelen; |
240 | 0 | ta->dclass = dclass; |
241 | 0 | lock_basic_init(&ta->lock); |
242 | 0 | if(lockit) { |
243 | 0 | lock_basic_lock(&anchors->lock); |
244 | 0 | } |
245 | | #ifdef UNBOUND_DEBUG |
246 | | r = |
247 | | #else |
248 | 0 | (void) |
249 | 0 | #endif |
250 | 0 | rbtree_insert(anchors->tree, &ta->node); |
251 | 0 | if(lockit) { |
252 | 0 | lock_basic_unlock(&anchors->lock); |
253 | 0 | } |
254 | 0 | log_assert(r != NULL); |
255 | 0 | return ta; |
256 | 0 | } |
257 | | |
258 | | /** find trustanchor key by exact data match */ |
259 | | static struct ta_key* |
260 | | anchor_find_key(struct trust_anchor* ta, uint8_t* rdata, size_t rdata_len, |
261 | | uint16_t type) |
262 | 0 | { |
263 | 0 | struct ta_key* k; |
264 | 0 | for(k = ta->keylist; k; k = k->next) { |
265 | 0 | if(k->type == type && k->len == rdata_len && |
266 | 0 | memcmp(k->data, rdata, rdata_len) == 0) |
267 | 0 | return k; |
268 | 0 | } |
269 | 0 | return NULL; |
270 | 0 | } |
271 | | |
272 | | /** create new trustanchor key */ |
273 | | static struct ta_key* |
274 | | anchor_new_ta_key(uint8_t* rdata, size_t rdata_len, uint16_t type) |
275 | 0 | { |
276 | 0 | struct ta_key* k = (struct ta_key*)malloc(sizeof(*k)); |
277 | 0 | if(!k) |
278 | 0 | return NULL; |
279 | 0 | memset(k, 0, sizeof(*k)); |
280 | 0 | k->data = memdup(rdata, rdata_len); |
281 | 0 | if(!k->data) { |
282 | 0 | free(k); |
283 | 0 | return NULL; |
284 | 0 | } |
285 | 0 | k->len = rdata_len; |
286 | 0 | k->type = type; |
287 | 0 | return k; |
288 | 0 | } |
289 | | |
290 | | /** |
291 | | * This routine adds a new RR to a trust anchor. The trust anchor may not |
292 | | * exist yet, and is created if not. The RR can be DS or DNSKEY. |
293 | | * This routine will also remove duplicates; storing them only once. |
294 | | * @param anchors: anchor storage. |
295 | | * @param name: name of trust anchor (wireformat) |
296 | | * @param type: type or RR |
297 | | * @param dclass: class of RR |
298 | | * @param rdata: rdata wireformat, starting with rdlength. |
299 | | * If NULL, nothing is stored, but an entry is created. |
300 | | * @param rdata_len: length of rdata including rdlength. |
301 | | * @return: NULL on error, else the trust anchor. |
302 | | */ |
303 | | static struct trust_anchor* |
304 | | anchor_store_new_key(struct val_anchors* anchors, uint8_t* name, uint16_t type, |
305 | | uint16_t dclass, uint8_t* rdata, size_t rdata_len) |
306 | 0 | { |
307 | 0 | struct ta_key* k; |
308 | 0 | struct trust_anchor* ta; |
309 | 0 | int namelabs; |
310 | 0 | size_t namelen; |
311 | 0 | namelabs = dname_count_size_labels(name, &namelen); |
312 | 0 | if(type != LDNS_RR_TYPE_DS && type != LDNS_RR_TYPE_DNSKEY) { |
313 | 0 | log_err("Bad type for trust anchor"); |
314 | 0 | return 0; |
315 | 0 | } |
316 | | /* lookup or create trustanchor */ |
317 | 0 | ta = anchor_find(anchors, name, namelabs, namelen, dclass); |
318 | 0 | if(!ta) { |
319 | 0 | ta = anchor_new_ta(anchors, name, namelabs, namelen, dclass, 1); |
320 | 0 | if(!ta) |
321 | 0 | return NULL; |
322 | 0 | lock_basic_lock(&ta->lock); |
323 | 0 | } |
324 | 0 | if(!rdata) { |
325 | 0 | lock_basic_unlock(&ta->lock); |
326 | 0 | return ta; |
327 | 0 | } |
328 | | /* look for duplicates */ |
329 | 0 | if(anchor_find_key(ta, rdata, rdata_len, type)) { |
330 | 0 | lock_basic_unlock(&ta->lock); |
331 | 0 | return ta; |
332 | 0 | } |
333 | 0 | k = anchor_new_ta_key(rdata, rdata_len, type); |
334 | 0 | if(!k) { |
335 | 0 | lock_basic_unlock(&ta->lock); |
336 | 0 | return NULL; |
337 | 0 | } |
338 | | /* add new key */ |
339 | 0 | if(type == LDNS_RR_TYPE_DS) |
340 | 0 | ta->numDS++; |
341 | 0 | else ta->numDNSKEY++; |
342 | 0 | k->next = ta->keylist; |
343 | 0 | ta->keylist = k; |
344 | 0 | lock_basic_unlock(&ta->lock); |
345 | 0 | return ta; |
346 | 0 | } |
347 | | |
348 | | /** |
349 | | * Add new RR. It converts ldns RR to wire format. |
350 | | * @param anchors: anchor storage. |
351 | | * @param rr: the wirerr. |
352 | | * @param rl: length of rr. |
353 | | * @param dl: length of dname. |
354 | | * @return NULL on error, else the trust anchor. |
355 | | */ |
356 | | static struct trust_anchor* |
357 | | anchor_store_new_rr(struct val_anchors* anchors, uint8_t* rr, size_t rl, |
358 | | size_t dl) |
359 | 0 | { |
360 | 0 | struct trust_anchor* ta; |
361 | 0 | if(!(ta=anchor_store_new_key(anchors, rr, |
362 | 0 | sldns_wirerr_get_type(rr, rl, dl), |
363 | 0 | sldns_wirerr_get_class(rr, rl, dl), |
364 | 0 | sldns_wirerr_get_rdatawl(rr, rl, dl), |
365 | 0 | sldns_wirerr_get_rdatalen(rr, rl, dl)+2))) { |
366 | 0 | return NULL; |
367 | 0 | } |
368 | 0 | log_nametypeclass(VERB_QUERY, "adding trusted key", |
369 | 0 | rr, sldns_wirerr_get_type(rr, rl, dl), |
370 | 0 | sldns_wirerr_get_class(rr, rl, dl)); |
371 | 0 | return ta; |
372 | 0 | } |
373 | | |
374 | | /** |
375 | | * Insert insecure anchor |
376 | | * @param anchors: anchor storage. |
377 | | * @param str: the domain name. |
378 | | * @return NULL on error, Else last trust anchor point |
379 | | */ |
380 | | static struct trust_anchor* |
381 | | anchor_insert_insecure(struct val_anchors* anchors, const char* str) |
382 | 0 | { |
383 | 0 | struct trust_anchor* ta; |
384 | 0 | size_t dname_len = 0; |
385 | 0 | uint8_t* nm = sldns_str2wire_dname(str, &dname_len); |
386 | 0 | if(!nm) { |
387 | 0 | log_err("parse error in domain name '%s'", str); |
388 | 0 | return NULL; |
389 | 0 | } |
390 | 0 | ta = anchor_store_new_key(anchors, nm, LDNS_RR_TYPE_DS, |
391 | 0 | LDNS_RR_CLASS_IN, NULL, 0); |
392 | 0 | free(nm); |
393 | 0 | return ta; |
394 | 0 | } |
395 | | |
396 | | struct trust_anchor* |
397 | | anchor_store_str(struct val_anchors* anchors, sldns_buffer* buffer, |
398 | | const char* str) |
399 | 0 | { |
400 | 0 | struct trust_anchor* ta; |
401 | 0 | uint8_t* rr = sldns_buffer_begin(buffer); |
402 | 0 | size_t len = sldns_buffer_capacity(buffer), dname_len = 0; |
403 | 0 | int status = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, |
404 | 0 | 0, NULL, 0, NULL, 0); |
405 | 0 | if(status != 0) { |
406 | 0 | log_err("error parsing trust anchor %s: at %d: %s", |
407 | 0 | str, LDNS_WIREPARSE_OFFSET(status), |
408 | 0 | sldns_get_errorstr_parse(status)); |
409 | 0 | return NULL; |
410 | 0 | } |
411 | 0 | if(!(ta=anchor_store_new_rr(anchors, rr, len, dname_len))) { |
412 | 0 | log_err("out of memory"); |
413 | 0 | return NULL; |
414 | 0 | } |
415 | 0 | return ta; |
416 | 0 | } |
417 | | |
418 | | /** |
419 | | * Read a file with trust anchors |
420 | | * @param anchors: anchor storage. |
421 | | * @param buffer: parsing buffer. |
422 | | * @param fname: string. |
423 | | * @param onlyone: only one trust anchor allowed in file. |
424 | | * @return NULL on error. Else last trust-anchor point. |
425 | | */ |
426 | | static struct trust_anchor* |
427 | | anchor_read_file(struct val_anchors* anchors, sldns_buffer* buffer, |
428 | | const char* fname, int onlyone) |
429 | 0 | { |
430 | 0 | struct trust_anchor* ta = NULL, *tanew; |
431 | 0 | struct sldns_file_parse_state pst; |
432 | 0 | int status; |
433 | 0 | size_t len, dname_len; |
434 | 0 | uint8_t* rr = sldns_buffer_begin(buffer); |
435 | 0 | int ok = 1; |
436 | 0 | FILE* in = fopen(fname, "r"); |
437 | 0 | if(!in) { |
438 | 0 | log_err("error opening file %s: %s", fname, strerror(errno)); |
439 | 0 | return 0; |
440 | 0 | } |
441 | 0 | memset(&pst, 0, sizeof(pst)); |
442 | 0 | pst.default_ttl = 3600; |
443 | 0 | pst.lineno = 1; |
444 | 0 | while(!feof(in)) { |
445 | 0 | len = sldns_buffer_capacity(buffer); |
446 | 0 | dname_len = 0; |
447 | 0 | status = sldns_fp2wire_rr_buf(in, rr, &len, &dname_len, &pst); |
448 | 0 | if(len == 0) /* empty, $TTL, $ORIGIN */ |
449 | 0 | continue; |
450 | 0 | if(status != 0) { |
451 | 0 | log_err("parse error in %s:%d:%d : %s", fname, |
452 | 0 | pst.lineno, LDNS_WIREPARSE_OFFSET(status), |
453 | 0 | sldns_get_errorstr_parse(status)); |
454 | 0 | ok = 0; |
455 | 0 | break; |
456 | 0 | } |
457 | 0 | if(sldns_wirerr_get_type(rr, len, dname_len) != |
458 | 0 | LDNS_RR_TYPE_DS && sldns_wirerr_get_type(rr, len, |
459 | 0 | dname_len) != LDNS_RR_TYPE_DNSKEY) { |
460 | 0 | continue; |
461 | 0 | } |
462 | 0 | if(!(tanew=anchor_store_new_rr(anchors, rr, len, dname_len))) { |
463 | 0 | log_err("mem error at %s line %d", fname, pst.lineno); |
464 | 0 | ok = 0; |
465 | 0 | break; |
466 | 0 | } |
467 | 0 | if(onlyone && ta && ta != tanew) { |
468 | 0 | log_err("error at %s line %d: no multiple anchor " |
469 | 0 | "domains allowed (you can have multiple " |
470 | 0 | "keys, but they must have the same name).", |
471 | 0 | fname, pst.lineno); |
472 | 0 | ok = 0; |
473 | 0 | break; |
474 | 0 | } |
475 | 0 | ta = tanew; |
476 | 0 | } |
477 | 0 | fclose(in); |
478 | 0 | if(!ok) return NULL; |
479 | | /* empty file is OK when multiple anchors are allowed */ |
480 | 0 | if(!onlyone && !ta) return (struct trust_anchor*)1; |
481 | 0 | return ta; |
482 | 0 | } |
483 | | |
484 | | /** skip file to end of line */ |
485 | | static void |
486 | | skip_to_eol(FILE* in) |
487 | 0 | { |
488 | 0 | int c; |
489 | 0 | while((c = getc(in)) != EOF ) { |
490 | 0 | if(c == '\n') |
491 | 0 | return; |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | /** true for special characters in bind configs */ |
496 | | static int |
497 | | is_bind_special(int c) |
498 | 0 | { |
499 | 0 | switch(c) { |
500 | 0 | case '{': |
501 | 0 | case '}': |
502 | 0 | case '"': |
503 | 0 | case ';': |
504 | 0 | return 1; |
505 | 0 | } |
506 | 0 | return 0; |
507 | 0 | } |
508 | | |
509 | | /** |
510 | | * Read a keyword skipping bind comments; spaces, specials, restkeywords. |
511 | | * The file is split into the following tokens: |
512 | | * * special characters, on their own, rdlen=1, { } doublequote ; |
513 | | * * whitespace becomes a single ' ' or tab. Newlines become spaces. |
514 | | * * other words ('keywords') |
515 | | * * comments are skipped if desired |
516 | | * / / C++ style comment to end of line |
517 | | * # to end of line |
518 | | * / * C style comment * / |
519 | | * @param in: file to read from. |
520 | | * @param buf: buffer, what is read is stored after current buffer position. |
521 | | * Space is left in the buffer to write a terminating 0. |
522 | | * @param line: line number is increased per line, for error reports. |
523 | | * @param comments: if 0, comments are not possible and become text. |
524 | | * if 1, comments are skipped entirely. |
525 | | * In BIND files, this is when reading quoted strings, for example |
526 | | * " base 64 text with / / in there " |
527 | | * @return the number of character written to the buffer. |
528 | | * 0 on end of file. |
529 | | */ |
530 | | static int |
531 | | readkeyword_bindfile(FILE* in, sldns_buffer* buf, int* line, int comments) |
532 | 0 | { |
533 | 0 | int c; |
534 | 0 | int numdone = 0; |
535 | 0 | while((c = getc(in)) != EOF ) { |
536 | 0 | if(comments && c == '#') { /* # blabla */ |
537 | 0 | skip_to_eol(in); |
538 | 0 | (*line)++; |
539 | 0 | continue; |
540 | 0 | } else if(comments && c=='/' && numdone>0 && /* /_/ bla*/ |
541 | 0 | sldns_buffer_read_u8_at(buf, |
542 | 0 | sldns_buffer_position(buf)-1) == '/') { |
543 | 0 | sldns_buffer_skip(buf, -1); |
544 | 0 | numdone--; |
545 | 0 | skip_to_eol(in); |
546 | 0 | (*line)++; |
547 | 0 | continue; |
548 | 0 | } else if(comments && c=='*' && numdone>0 && /* /_* bla *_/ */ |
549 | 0 | sldns_buffer_read_u8_at(buf, |
550 | 0 | sldns_buffer_position(buf)-1) == '/') { |
551 | 0 | sldns_buffer_skip(buf, -1); |
552 | 0 | numdone--; |
553 | | /* skip to end of comment */ |
554 | 0 | while(c != EOF && (c=getc(in)) != EOF ) { |
555 | 0 | if(c == '*') { |
556 | 0 | if((c=getc(in)) == '/') |
557 | 0 | break; |
558 | 0 | } |
559 | 0 | if(c == '\n') |
560 | 0 | (*line)++; |
561 | 0 | } |
562 | 0 | continue; |
563 | 0 | } |
564 | | /* not a comment, complete the keyword */ |
565 | 0 | if(numdone > 0) { |
566 | | /* check same type */ |
567 | 0 | if(isspace((unsigned char)c)) { |
568 | 0 | ungetc(c, in); |
569 | 0 | return numdone; |
570 | 0 | } |
571 | 0 | if(is_bind_special(c)) { |
572 | 0 | ungetc(c, in); |
573 | 0 | return numdone; |
574 | 0 | } |
575 | 0 | } |
576 | 0 | if(c == '\n') { |
577 | 0 | c = ' '; |
578 | 0 | (*line)++; |
579 | 0 | } |
580 | | /* space for 1 char + 0 string terminator */ |
581 | 0 | if(sldns_buffer_remaining(buf) < 2) { |
582 | 0 | fatal_exit("trusted-keys, %d, string too long", *line); |
583 | 0 | } |
584 | 0 | sldns_buffer_write_u8(buf, (uint8_t)c); |
585 | 0 | numdone++; |
586 | 0 | if(isspace((unsigned char)c)) { |
587 | | /* collate whitespace into ' ' */ |
588 | 0 | while((c = getc(in)) != EOF ) { |
589 | 0 | if(c == '\n') |
590 | 0 | (*line)++; |
591 | 0 | if(!isspace((unsigned char)c)) { |
592 | 0 | ungetc(c, in); |
593 | 0 | break; |
594 | 0 | } |
595 | 0 | } |
596 | 0 | return numdone; |
597 | 0 | } |
598 | 0 | if(is_bind_special(c)) |
599 | 0 | return numdone; |
600 | 0 | } |
601 | 0 | return numdone; |
602 | 0 | } |
603 | | |
604 | | /** skip through file to { or ; */ |
605 | | static int |
606 | | skip_to_special(FILE* in, sldns_buffer* buf, int* line, int spec) |
607 | 0 | { |
608 | 0 | int rdlen; |
609 | 0 | sldns_buffer_clear(buf); |
610 | 0 | while((rdlen=readkeyword_bindfile(in, buf, line, 1))) { |
611 | 0 | if(rdlen == 1 && isspace((unsigned char)*sldns_buffer_begin(buf))) { |
612 | 0 | sldns_buffer_clear(buf); |
613 | 0 | continue; |
614 | 0 | } |
615 | 0 | if(rdlen != 1 || *sldns_buffer_begin(buf) != (uint8_t)spec) { |
616 | 0 | sldns_buffer_write_u8(buf, 0); |
617 | 0 | log_err("trusted-keys, line %d, expected %c", |
618 | 0 | *line, spec); |
619 | 0 | return 0; |
620 | 0 | } |
621 | 0 | return 1; |
622 | 0 | } |
623 | 0 | log_err("trusted-keys, line %d, expected %c got EOF", *line, spec); |
624 | 0 | return 0; |
625 | 0 | } |
626 | | |
627 | | /** |
628 | | * read contents of trusted-keys{ ... ; clauses and insert keys into storage. |
629 | | * @param anchors: where to store keys |
630 | | * @param buf: buffer to use |
631 | | * @param line: line number in file |
632 | | * @param in: file to read from. |
633 | | * @return 0 on error. |
634 | | */ |
635 | | static int |
636 | | process_bind_contents(struct val_anchors* anchors, sldns_buffer* buf, |
637 | | int* line, FILE* in) |
638 | 0 | { |
639 | | /* loop over contents, collate strings before ; */ |
640 | | /* contents is (numbered): 0 1 2 3 4 5 6 7 8 */ |
641 | | /* name. 257 3 5 base64 base64 */ |
642 | | /* quoted value: 0 "111" 0 0 0 0 0 0 0 */ |
643 | | /* comments value: 1 "000" 1 1 1 "0 0 0 0" 1 */ |
644 | 0 | int contnum = 0; |
645 | 0 | int quoted = 0; |
646 | 0 | int comments = 1; |
647 | 0 | int rdlen; |
648 | 0 | char* str = 0; |
649 | 0 | sldns_buffer_clear(buf); |
650 | 0 | while((rdlen=readkeyword_bindfile(in, buf, line, comments))) { |
651 | 0 | if(rdlen == 1 && sldns_buffer_position(buf) == 1 |
652 | 0 | && isspace((unsigned char)*sldns_buffer_begin(buf))) { |
653 | | /* starting whitespace is removed */ |
654 | 0 | sldns_buffer_clear(buf); |
655 | 0 | continue; |
656 | 0 | } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '"') { |
657 | | /* remove " from the string */ |
658 | 0 | if(contnum == 0) { |
659 | 0 | quoted = 1; |
660 | 0 | comments = 0; |
661 | 0 | } |
662 | 0 | sldns_buffer_skip(buf, -1); |
663 | 0 | if(contnum > 0 && quoted) { |
664 | 0 | if(sldns_buffer_remaining(buf) < 8+1) { |
665 | 0 | log_err("line %d, too long", *line); |
666 | 0 | return 0; |
667 | 0 | } |
668 | 0 | sldns_buffer_write(buf, " DNSKEY ", 8); |
669 | 0 | quoted = 0; |
670 | 0 | comments = 1; |
671 | 0 | } else if(contnum > 0) |
672 | 0 | comments = !comments; |
673 | 0 | continue; |
674 | 0 | } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == ';') { |
675 | |
|
676 | 0 | if(contnum < 5) { |
677 | 0 | sldns_buffer_write_u8(buf, 0); |
678 | 0 | log_err("line %d, bad key", *line); |
679 | 0 | return 0; |
680 | 0 | } |
681 | 0 | sldns_buffer_skip(buf, -1); |
682 | 0 | sldns_buffer_write_u8(buf, 0); |
683 | 0 | str = strdup((char*)sldns_buffer_begin(buf)); |
684 | 0 | if(!str) { |
685 | 0 | log_err("line %d, allocation failure", *line); |
686 | 0 | return 0; |
687 | 0 | } |
688 | 0 | if(!anchor_store_str(anchors, buf, str)) { |
689 | 0 | log_err("line %d, bad key", *line); |
690 | 0 | free(str); |
691 | 0 | return 0; |
692 | 0 | } |
693 | 0 | free(str); |
694 | 0 | sldns_buffer_clear(buf); |
695 | 0 | contnum = 0; |
696 | 0 | quoted = 0; |
697 | 0 | comments = 1; |
698 | 0 | continue; |
699 | 0 | } else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '}') { |
700 | 0 | if(contnum > 0) { |
701 | 0 | sldns_buffer_write_u8(buf, 0); |
702 | 0 | log_err("line %d, bad key before }", *line); |
703 | 0 | return 0; |
704 | 0 | } |
705 | 0 | return 1; |
706 | 0 | } else if(rdlen == 1 && |
707 | 0 | isspace((unsigned char)sldns_buffer_current(buf)[-1])) { |
708 | | /* leave whitespace here */ |
709 | 0 | } else { |
710 | | /* not space or whatnot, so actual content */ |
711 | 0 | contnum ++; |
712 | 0 | if(contnum == 1 && !quoted) { |
713 | 0 | if(sldns_buffer_remaining(buf) < 8+1) { |
714 | 0 | log_err("line %d, too long", *line); |
715 | 0 | return 0; |
716 | 0 | } |
717 | 0 | sldns_buffer_write(buf, " DNSKEY ", 8); |
718 | 0 | } |
719 | 0 | } |
720 | 0 | } |
721 | | |
722 | 0 | log_err("line %d, EOF before }", *line); |
723 | 0 | return 0; |
724 | 0 | } |
725 | | |
726 | | /** |
727 | | * Read a BIND9 like file with trust anchors in named.conf format. |
728 | | * @param anchors: anchor storage. |
729 | | * @param buffer: parsing buffer. |
730 | | * @param fname: string. |
731 | | * @return false on error. |
732 | | */ |
733 | | static int |
734 | | anchor_read_bind_file(struct val_anchors* anchors, sldns_buffer* buffer, |
735 | | const char* fname) |
736 | 0 | { |
737 | 0 | int line_nr = 1; |
738 | 0 | FILE* in = fopen(fname, "r"); |
739 | 0 | int rdlen = 0; |
740 | 0 | if(!in) { |
741 | 0 | log_err("error opening file %s: %s", fname, strerror(errno)); |
742 | 0 | return 0; |
743 | 0 | } |
744 | 0 | verbose(VERB_QUERY, "reading in bind-compat-mode: '%s'", fname); |
745 | | /* scan for trusted-keys keyword, ignore everything else */ |
746 | 0 | sldns_buffer_clear(buffer); |
747 | 0 | while((rdlen=readkeyword_bindfile(in, buffer, &line_nr, 1)) != 0) { |
748 | 0 | if(rdlen != 12 || strncmp((char*)sldns_buffer_begin(buffer), |
749 | 0 | "trusted-keys", 12) != 0) { |
750 | 0 | sldns_buffer_clear(buffer); |
751 | | /* ignore everything but trusted-keys */ |
752 | 0 | continue; |
753 | 0 | } |
754 | 0 | if(!skip_to_special(in, buffer, &line_nr, '{')) { |
755 | 0 | log_err("error in trusted key: \"%s\"", fname); |
756 | 0 | fclose(in); |
757 | 0 | return 0; |
758 | 0 | } |
759 | | /* process contents */ |
760 | 0 | if(!process_bind_contents(anchors, buffer, &line_nr, in)) { |
761 | 0 | log_err("error in trusted key: \"%s\"", fname); |
762 | 0 | fclose(in); |
763 | 0 | return 0; |
764 | 0 | } |
765 | 0 | if(!skip_to_special(in, buffer, &line_nr, ';')) { |
766 | 0 | log_err("error in trusted key: \"%s\"", fname); |
767 | 0 | fclose(in); |
768 | 0 | return 0; |
769 | 0 | } |
770 | 0 | sldns_buffer_clear(buffer); |
771 | 0 | } |
772 | 0 | fclose(in); |
773 | 0 | return 1; |
774 | 0 | } |
775 | | |
776 | | /** |
777 | | * Read a BIND9 like files with trust anchors in named.conf format. |
778 | | * Performs wildcard processing of name. |
779 | | * @param anchors: anchor storage. |
780 | | * @param buffer: parsing buffer. |
781 | | * @param pat: pattern string. (can be wildcarded) |
782 | | * @return false on error. |
783 | | */ |
784 | | static int |
785 | | anchor_read_bind_file_wild(struct val_anchors* anchors, sldns_buffer* buffer, |
786 | | const char* pat) |
787 | 0 | { |
788 | 0 | #ifdef HAVE_GLOB |
789 | 0 | glob_t g; |
790 | 0 | size_t i; |
791 | 0 | int r, flags; |
792 | 0 | if(!strchr(pat, '*') && !strchr(pat, '?') && !strchr(pat, '[') && |
793 | 0 | !strchr(pat, '{') && !strchr(pat, '~')) { |
794 | 0 | return anchor_read_bind_file(anchors, buffer, pat); |
795 | 0 | } |
796 | 0 | verbose(VERB_QUERY, "wildcard found, processing %s", pat); |
797 | 0 | flags = 0 |
798 | 0 | #ifdef GLOB_ERR |
799 | 0 | | GLOB_ERR |
800 | 0 | #endif |
801 | 0 | #ifdef GLOB_NOSORT |
802 | 0 | | GLOB_NOSORT |
803 | 0 | #endif |
804 | 0 | #ifdef GLOB_BRACE |
805 | 0 | | GLOB_BRACE |
806 | 0 | #endif |
807 | 0 | #ifdef GLOB_TILDE |
808 | 0 | | GLOB_TILDE |
809 | 0 | #endif |
810 | 0 | ; |
811 | 0 | memset(&g, 0, sizeof(g)); |
812 | 0 | r = glob(pat, flags, NULL, &g); |
813 | 0 | if(r) { |
814 | | /* some error */ |
815 | 0 | if(r == GLOB_NOMATCH) { |
816 | 0 | verbose(VERB_QUERY, "trusted-keys-file: " |
817 | 0 | "no matches for %s", pat); |
818 | 0 | return 1; |
819 | 0 | } else if(r == GLOB_NOSPACE) { |
820 | 0 | log_err("wildcard trusted-keys-file %s: " |
821 | 0 | "pattern out of memory", pat); |
822 | 0 | } else if(r == GLOB_ABORTED) { |
823 | 0 | log_err("wildcard trusted-keys-file %s: expansion " |
824 | 0 | "aborted (%s)", pat, strerror(errno)); |
825 | 0 | } else { |
826 | 0 | log_err("wildcard trusted-keys-file %s: expansion " |
827 | 0 | "failed (%s)", pat, strerror(errno)); |
828 | 0 | } |
829 | | /* ignore globs that yield no files */ |
830 | 0 | return 1; |
831 | 0 | } |
832 | | /* process files found, if any */ |
833 | 0 | for(i=0; i<(size_t)g.gl_pathc; i++) { |
834 | 0 | if(!anchor_read_bind_file(anchors, buffer, g.gl_pathv[i])) { |
835 | 0 | log_err("error reading wildcard " |
836 | 0 | "trusted-keys-file: %s", g.gl_pathv[i]); |
837 | 0 | globfree(&g); |
838 | 0 | return 0; |
839 | 0 | } |
840 | 0 | } |
841 | 0 | globfree(&g); |
842 | 0 | return 1; |
843 | | #else /* not HAVE_GLOB */ |
844 | | return anchor_read_bind_file(anchors, buffer, pat); |
845 | | #endif /* HAVE_GLOB */ |
846 | 0 | } |
847 | | |
848 | | /** |
849 | | * Assemble an rrset structure for the type |
850 | | * @param ta: trust anchor. |
851 | | * @param num: number of items to fetch from list. |
852 | | * @param type: fetch only items of this type. |
853 | | * @return rrset or NULL on error. |
854 | | */ |
855 | | static struct ub_packed_rrset_key* |
856 | | assemble_it(struct trust_anchor* ta, size_t num, uint16_t type) |
857 | 0 | { |
858 | 0 | struct ub_packed_rrset_key* pkey = (struct ub_packed_rrset_key*) |
859 | 0 | malloc(sizeof(*pkey)); |
860 | 0 | struct packed_rrset_data* pd; |
861 | 0 | struct ta_key* tk; |
862 | 0 | size_t i; |
863 | 0 | if(!pkey) |
864 | 0 | return NULL; |
865 | 0 | memset(pkey, 0, sizeof(*pkey)); |
866 | 0 | pkey->rk.dname = memdup(ta->name, ta->namelen); |
867 | 0 | if(!pkey->rk.dname) { |
868 | 0 | free(pkey); |
869 | 0 | return NULL; |
870 | 0 | } |
871 | | |
872 | 0 | pkey->rk.dname_len = ta->namelen; |
873 | 0 | pkey->rk.type = htons(type); |
874 | 0 | pkey->rk.rrset_class = htons(ta->dclass); |
875 | | /* The rrset is build in an uncompressed way. This means it |
876 | | * cannot be copied in the normal way. */ |
877 | 0 | pd = (struct packed_rrset_data*)malloc(sizeof(*pd)); |
878 | 0 | if(!pd) { |
879 | 0 | free(pkey->rk.dname); |
880 | 0 | free(pkey); |
881 | 0 | return NULL; |
882 | 0 | } |
883 | 0 | memset(pd, 0, sizeof(*pd)); |
884 | 0 | pd->count = num; |
885 | 0 | pd->trust = rrset_trust_ultimate; |
886 | 0 | pd->rr_len = (size_t*)reallocarray(NULL, num, sizeof(size_t)); |
887 | 0 | if(!pd->rr_len) { |
888 | 0 | free(pd); |
889 | 0 | free(pkey->rk.dname); |
890 | 0 | free(pkey); |
891 | 0 | return NULL; |
892 | 0 | } |
893 | 0 | pd->rr_ttl = (time_t*)reallocarray(NULL, num, sizeof(time_t)); |
894 | 0 | if(!pd->rr_ttl) { |
895 | 0 | free(pd->rr_len); |
896 | 0 | free(pd); |
897 | 0 | free(pkey->rk.dname); |
898 | 0 | free(pkey); |
899 | 0 | return NULL; |
900 | 0 | } |
901 | 0 | pd->rr_data = (uint8_t**)reallocarray(NULL, num, sizeof(uint8_t*)); |
902 | 0 | if(!pd->rr_data) { |
903 | 0 | free(pd->rr_ttl); |
904 | 0 | free(pd->rr_len); |
905 | 0 | free(pd); |
906 | 0 | free(pkey->rk.dname); |
907 | 0 | free(pkey); |
908 | 0 | return NULL; |
909 | 0 | } |
910 | | /* fill in rrs */ |
911 | 0 | i=0; |
912 | 0 | for(tk = ta->keylist; tk; tk = tk->next) { |
913 | 0 | if(tk->type != type) |
914 | 0 | continue; |
915 | 0 | pd->rr_len[i] = tk->len; |
916 | | /* reuse data ptr to allocation in talist */ |
917 | 0 | pd->rr_data[i] = tk->data; |
918 | 0 | pd->rr_ttl[i] = 0; |
919 | 0 | i++; |
920 | 0 | } |
921 | 0 | pkey->entry.data = (void*)pd; |
922 | 0 | return pkey; |
923 | 0 | } |
924 | | |
925 | | /** |
926 | | * Assemble structures for the trust DS and DNSKEY rrsets. |
927 | | * @param ta: trust anchor |
928 | | * @return: false on error. |
929 | | */ |
930 | | static int |
931 | | anchors_assemble(struct trust_anchor* ta) |
932 | 0 | { |
933 | 0 | if(ta->numDS > 0) { |
934 | 0 | ta->ds_rrset = assemble_it(ta, ta->numDS, LDNS_RR_TYPE_DS); |
935 | 0 | if(!ta->ds_rrset) |
936 | 0 | return 0; |
937 | 0 | } |
938 | 0 | if(ta->numDNSKEY > 0) { |
939 | 0 | ta->dnskey_rrset = assemble_it(ta, ta->numDNSKEY, |
940 | 0 | LDNS_RR_TYPE_DNSKEY); |
941 | 0 | if(!ta->dnskey_rrset) |
942 | 0 | return 0; |
943 | 0 | } |
944 | 0 | return 1; |
945 | 0 | } |
946 | | |
947 | | /** |
948 | | * Check DS algos for support, warn if not. |
949 | | * @param ta: trust anchor |
950 | | * @return number of DS anchors with unsupported algorithms. |
951 | | */ |
952 | | static size_t |
953 | | anchors_ds_unsupported(struct trust_anchor* ta) |
954 | 0 | { |
955 | 0 | size_t i, num = 0; |
956 | 0 | for(i=0; i<ta->numDS; i++) { |
957 | 0 | if(!ds_digest_algo_is_supported(ta->ds_rrset, i) || |
958 | 0 | !ds_key_algo_is_supported(ta->ds_rrset, i)) |
959 | 0 | num++; |
960 | 0 | } |
961 | 0 | return num; |
962 | 0 | } |
963 | | |
964 | | /** |
965 | | * Check DNSKEY algos for support, warn if not. |
966 | | * @param ta: trust anchor |
967 | | * @return number of DNSKEY anchors with unsupported algorithms. |
968 | | */ |
969 | | static size_t |
970 | | anchors_dnskey_unsupported(struct trust_anchor* ta) |
971 | 0 | { |
972 | 0 | size_t i, num = 0; |
973 | 0 | for(i=0; i<ta->numDNSKEY; i++) { |
974 | 0 | if(!dnskey_algo_is_supported(ta->dnskey_rrset, i) || |
975 | 0 | !dnskey_size_is_supported(ta->dnskey_rrset, i)) |
976 | 0 | num++; |
977 | 0 | } |
978 | 0 | return num; |
979 | 0 | } |
980 | | |
981 | | /** |
982 | | * Assemble the rrsets in the anchors, ready for use by validator. |
983 | | * @param anchors: trust anchor storage. |
984 | | * @return: false on error. |
985 | | */ |
986 | | static int |
987 | | anchors_assemble_rrsets(struct val_anchors* anchors) |
988 | 0 | { |
989 | 0 | struct trust_anchor* ta; |
990 | 0 | struct trust_anchor* next; |
991 | 0 | size_t nods, nokey; |
992 | 0 | lock_basic_lock(&anchors->lock); |
993 | 0 | ta=(struct trust_anchor*)rbtree_first(anchors->tree); |
994 | 0 | while((rbnode_type*)ta != RBTREE_NULL) { |
995 | 0 | next = (struct trust_anchor*)rbtree_next(&ta->node); |
996 | 0 | lock_basic_lock(&ta->lock); |
997 | 0 | if(ta->autr || (ta->numDS == 0 && ta->numDNSKEY == 0)) { |
998 | 0 | lock_basic_unlock(&ta->lock); |
999 | 0 | ta = next; /* skip */ |
1000 | 0 | continue; |
1001 | 0 | } |
1002 | 0 | if(!anchors_assemble(ta)) { |
1003 | 0 | log_err("out of memory"); |
1004 | 0 | lock_basic_unlock(&ta->lock); |
1005 | 0 | lock_basic_unlock(&anchors->lock); |
1006 | 0 | return 0; |
1007 | 0 | } |
1008 | 0 | nods = anchors_ds_unsupported(ta); |
1009 | 0 | nokey = anchors_dnskey_unsupported(ta); |
1010 | 0 | if(nods) { |
1011 | 0 | log_nametypeclass(NO_VERBOSE, "warning: unsupported " |
1012 | 0 | "algorithm for trust anchor", |
1013 | 0 | ta->name, LDNS_RR_TYPE_DS, ta->dclass); |
1014 | 0 | } |
1015 | 0 | if(nokey) { |
1016 | 0 | log_nametypeclass(NO_VERBOSE, "warning: unsupported " |
1017 | 0 | "algorithm for trust anchor", |
1018 | 0 | ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); |
1019 | 0 | } |
1020 | 0 | if(nods == ta->numDS && nokey == ta->numDNSKEY) { |
1021 | 0 | char b[257]; |
1022 | 0 | dname_str(ta->name, b); |
1023 | 0 | log_warn("trust anchor %s has no supported algorithms," |
1024 | 0 | " the anchor is ignored (check if you need to" |
1025 | 0 | " upgrade unbound and " |
1026 | | #ifdef HAVE_LIBRESSL |
1027 | | "libressl" |
1028 | | #else |
1029 | 0 | "openssl" |
1030 | 0 | #endif |
1031 | 0 | ")", b); |
1032 | 0 | (void)rbtree_delete(anchors->tree, &ta->node); |
1033 | 0 | lock_basic_unlock(&ta->lock); |
1034 | 0 | anchors_delfunc(&ta->node, NULL); |
1035 | 0 | ta = next; |
1036 | 0 | continue; |
1037 | 0 | } |
1038 | 0 | lock_basic_unlock(&ta->lock); |
1039 | 0 | ta = next; |
1040 | 0 | } |
1041 | 0 | lock_basic_unlock(&anchors->lock); |
1042 | 0 | return 1; |
1043 | 0 | } |
1044 | | |
1045 | | int |
1046 | | anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg) |
1047 | 0 | { |
1048 | 0 | struct config_strlist* f; |
1049 | 0 | const char** zstr; |
1050 | 0 | char* nm; |
1051 | 0 | sldns_buffer* parsebuf = sldns_buffer_new(65535); |
1052 | 0 | if(!parsebuf) { |
1053 | 0 | log_err("malloc error in anchors_apply_cfg."); |
1054 | 0 | return 0; |
1055 | 0 | } |
1056 | 0 | if(cfg->insecure_lan_zones) { |
1057 | 0 | for(zstr = as112_zones; *zstr; zstr++) { |
1058 | 0 | if(!anchor_insert_insecure(anchors, *zstr)) { |
1059 | 0 | log_err("error in insecure-lan-zones: %s", *zstr); |
1060 | 0 | sldns_buffer_free(parsebuf); |
1061 | 0 | return 0; |
1062 | 0 | } |
1063 | 0 | } |
1064 | 0 | } |
1065 | 0 | for(f = cfg->domain_insecure; f; f = f->next) { |
1066 | 0 | if(!f->str || f->str[0] == 0) /* empty "" */ |
1067 | 0 | continue; |
1068 | 0 | if(!anchor_insert_insecure(anchors, f->str)) { |
1069 | 0 | log_err("error in domain-insecure: %s", f->str); |
1070 | 0 | sldns_buffer_free(parsebuf); |
1071 | 0 | return 0; |
1072 | 0 | } |
1073 | 0 | } |
1074 | 0 | for(f = cfg->trust_anchor_file_list; f; f = f->next) { |
1075 | 0 | if(!f->str || f->str[0] == 0) /* empty "" */ |
1076 | 0 | continue; |
1077 | 0 | nm = f->str; |
1078 | 0 | if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, |
1079 | 0 | cfg->chrootdir, strlen(cfg->chrootdir)) == 0) |
1080 | 0 | nm += strlen(cfg->chrootdir); |
1081 | 0 | if(!anchor_read_file(anchors, parsebuf, nm, 0)) { |
1082 | 0 | log_err("error reading trust-anchor-file: %s", f->str); |
1083 | 0 | sldns_buffer_free(parsebuf); |
1084 | 0 | return 0; |
1085 | 0 | } |
1086 | 0 | } |
1087 | 0 | for(f = cfg->trusted_keys_file_list; f; f = f->next) { |
1088 | 0 | if(!f->str || f->str[0] == 0) /* empty "" */ |
1089 | 0 | continue; |
1090 | 0 | nm = f->str; |
1091 | 0 | if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, |
1092 | 0 | cfg->chrootdir, strlen(cfg->chrootdir)) == 0) |
1093 | 0 | nm += strlen(cfg->chrootdir); |
1094 | 0 | if(!anchor_read_bind_file_wild(anchors, parsebuf, nm)) { |
1095 | 0 | log_err("error reading trusted-keys-file: %s", f->str); |
1096 | 0 | sldns_buffer_free(parsebuf); |
1097 | 0 | return 0; |
1098 | 0 | } |
1099 | 0 | } |
1100 | 0 | for(f = cfg->trust_anchor_list; f; f = f->next) { |
1101 | 0 | if(!f->str || f->str[0] == 0) /* empty "" */ |
1102 | 0 | continue; |
1103 | 0 | if(!anchor_store_str(anchors, parsebuf, f->str)) { |
1104 | 0 | log_err("error in trust-anchor: \"%s\"", f->str); |
1105 | 0 | sldns_buffer_free(parsebuf); |
1106 | 0 | return 0; |
1107 | 0 | } |
1108 | 0 | } |
1109 | | /* do autr last, so that it sees what anchors are filled by other |
1110 | | * means can can print errors about double config for the name */ |
1111 | 0 | for(f = cfg->auto_trust_anchor_file_list; f; f = f->next) { |
1112 | 0 | if(!f->str || f->str[0] == 0) /* empty "" */ |
1113 | 0 | continue; |
1114 | 0 | nm = f->str; |
1115 | 0 | if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, |
1116 | 0 | cfg->chrootdir, strlen(cfg->chrootdir)) == 0) |
1117 | 0 | nm += strlen(cfg->chrootdir); |
1118 | 0 | if(!autr_read_file(anchors, nm)) { |
1119 | 0 | log_err("error reading auto-trust-anchor-file: %s", |
1120 | 0 | f->str); |
1121 | 0 | sldns_buffer_free(parsebuf); |
1122 | 0 | return 0; |
1123 | 0 | } |
1124 | 0 | } |
1125 | | /* first assemble, since it may delete useless anchors */ |
1126 | 0 | anchors_assemble_rrsets(anchors); |
1127 | 0 | init_parents(anchors); |
1128 | 0 | sldns_buffer_free(parsebuf); |
1129 | 0 | if(verbosity >= VERB_ALGO) autr_debug_print(anchors); |
1130 | 0 | return 1; |
1131 | 0 | } |
1132 | | |
1133 | | struct trust_anchor* |
1134 | | anchors_lookup(struct val_anchors* anchors, |
1135 | | uint8_t* qname, size_t qname_len, uint16_t qclass) |
1136 | 0 | { |
1137 | 0 | struct trust_anchor key; |
1138 | 0 | struct trust_anchor* result; |
1139 | 0 | rbnode_type* res = NULL; |
1140 | 0 | key.node.key = &key; |
1141 | 0 | key.name = qname; |
1142 | 0 | key.namelabs = dname_count_labels(qname); |
1143 | 0 | key.namelen = qname_len; |
1144 | 0 | key.dclass = qclass; |
1145 | 0 | lock_basic_lock(&anchors->lock); |
1146 | 0 | if(rbtree_find_less_equal(anchors->tree, &key, &res)) { |
1147 | | /* exact */ |
1148 | 0 | result = (struct trust_anchor*)res; |
1149 | 0 | } else { |
1150 | | /* smaller element (or no element) */ |
1151 | 0 | int m; |
1152 | 0 | result = (struct trust_anchor*)res; |
1153 | 0 | if(!result || result->dclass != qclass) { |
1154 | 0 | lock_basic_unlock(&anchors->lock); |
1155 | 0 | return NULL; |
1156 | 0 | } |
1157 | | /* count number of labels matched */ |
1158 | 0 | (void)dname_lab_cmp(result->name, result->namelabs, key.name, |
1159 | 0 | key.namelabs, &m); |
1160 | 0 | while(result) { /* go up until qname is subdomain of stub */ |
1161 | 0 | if(result->namelabs <= m) |
1162 | 0 | break; |
1163 | 0 | result = result->parent; |
1164 | 0 | } |
1165 | 0 | } |
1166 | 0 | if(result) { |
1167 | 0 | lock_basic_lock(&result->lock); |
1168 | 0 | } |
1169 | 0 | lock_basic_unlock(&anchors->lock); |
1170 | 0 | return result; |
1171 | 0 | } |
1172 | | |
1173 | | size_t |
1174 | | anchors_get_mem(struct val_anchors* anchors) |
1175 | 0 | { |
1176 | 0 | struct trust_anchor *ta; |
1177 | 0 | size_t s = sizeof(*anchors); |
1178 | 0 | if(!anchors) |
1179 | 0 | return 0; |
1180 | 0 | RBTREE_FOR(ta, struct trust_anchor*, anchors->tree) { |
1181 | 0 | s += sizeof(*ta) + ta->namelen; |
1182 | | /* keys and so on */ |
1183 | 0 | } |
1184 | 0 | return s; |
1185 | 0 | } |
1186 | | |
1187 | | int |
1188 | | anchors_add_insecure(struct val_anchors* anchors, uint16_t c, uint8_t* nm) |
1189 | 0 | { |
1190 | 0 | struct trust_anchor key; |
1191 | 0 | key.node.key = &key; |
1192 | 0 | key.name = nm; |
1193 | 0 | key.namelabs = dname_count_size_labels(nm, &key.namelen); |
1194 | 0 | key.dclass = c; |
1195 | 0 | lock_basic_lock(&anchors->lock); |
1196 | 0 | if(rbtree_search(anchors->tree, &key)) { |
1197 | 0 | lock_basic_unlock(&anchors->lock); |
1198 | | /* nothing to do, already an anchor or insecure point */ |
1199 | 0 | return 1; |
1200 | 0 | } |
1201 | 0 | if(!anchor_new_ta(anchors, nm, key.namelabs, key.namelen, c, 0)) { |
1202 | 0 | log_err("out of memory"); |
1203 | 0 | lock_basic_unlock(&anchors->lock); |
1204 | 0 | return 0; |
1205 | 0 | } |
1206 | | /* no other contents in new ta, because it is insecure point */ |
1207 | 0 | anchors_init_parents_locked(anchors); |
1208 | 0 | lock_basic_unlock(&anchors->lock); |
1209 | 0 | return 1; |
1210 | 0 | } |
1211 | | |
1212 | | void |
1213 | | anchors_delete_insecure(struct val_anchors* anchors, uint16_t c, |
1214 | | uint8_t* nm) |
1215 | 0 | { |
1216 | 0 | struct trust_anchor key; |
1217 | 0 | struct trust_anchor* ta; |
1218 | 0 | key.node.key = &key; |
1219 | 0 | key.name = nm; |
1220 | 0 | key.namelabs = dname_count_size_labels(nm, &key.namelen); |
1221 | 0 | key.dclass = c; |
1222 | 0 | lock_basic_lock(&anchors->lock); |
1223 | 0 | if(!(ta=(struct trust_anchor*)rbtree_search(anchors->tree, &key))) { |
1224 | 0 | lock_basic_unlock(&anchors->lock); |
1225 | | /* nothing there */ |
1226 | 0 | return; |
1227 | 0 | } |
1228 | | /* lock it to drive away other threads that use it */ |
1229 | 0 | lock_basic_lock(&ta->lock); |
1230 | | /* see if its really an insecure point */ |
1231 | 0 | if(ta->keylist || ta->autr || ta->numDS || ta->numDNSKEY) { |
1232 | 0 | lock_basic_unlock(&anchors->lock); |
1233 | 0 | lock_basic_unlock(&ta->lock); |
1234 | | /* its not an insecure point, do not remove it */ |
1235 | 0 | return; |
1236 | 0 | } |
1237 | | |
1238 | | /* remove from tree */ |
1239 | 0 | (void)rbtree_delete(anchors->tree, &ta->node); |
1240 | 0 | anchors_init_parents_locked(anchors); |
1241 | 0 | lock_basic_unlock(&anchors->lock); |
1242 | | |
1243 | | /* actual free of data */ |
1244 | 0 | lock_basic_unlock(&ta->lock); |
1245 | 0 | anchors_delfunc(&ta->node, NULL); |
1246 | 0 | } |
1247 | | |
1248 | | /** compare two keytags, return -1, 0 or 1 */ |
1249 | | static int |
1250 | | keytag_compare(const void* x, const void* y) |
1251 | 0 | { |
1252 | 0 | if(*(uint16_t*)x == *(uint16_t*)y) |
1253 | 0 | return 0; |
1254 | 0 | if(*(uint16_t*)x > *(uint16_t*)y) |
1255 | 0 | return 1; |
1256 | 0 | return -1; |
1257 | 0 | } |
1258 | | |
1259 | | size_t |
1260 | | anchor_list_keytags(struct trust_anchor* ta, uint16_t* list, size_t num) |
1261 | 0 | { |
1262 | 0 | size_t i, ret = 0; |
1263 | 0 | if(ta->numDS == 0 && ta->numDNSKEY == 0) |
1264 | 0 | return 0; /* insecure point */ |
1265 | 0 | if(ta->numDS != 0 && ta->ds_rrset) { |
1266 | 0 | struct packed_rrset_data* d=(struct packed_rrset_data*) |
1267 | 0 | ta->ds_rrset->entry.data; |
1268 | 0 | for(i=0; i<d->count; i++) { |
1269 | 0 | if(ret == num) continue; |
1270 | 0 | list[ret++] = ds_get_keytag(ta->ds_rrset, i); |
1271 | 0 | } |
1272 | 0 | } |
1273 | 0 | if(ta->numDNSKEY != 0 && ta->dnskey_rrset) { |
1274 | 0 | struct packed_rrset_data* d=(struct packed_rrset_data*) |
1275 | 0 | ta->dnskey_rrset->entry.data; |
1276 | 0 | for(i=0; i<d->count; i++) { |
1277 | 0 | if(ret == num) continue; |
1278 | 0 | list[ret++] = dnskey_calc_keytag(ta->dnskey_rrset, i); |
1279 | 0 | } |
1280 | 0 | } |
1281 | 0 | qsort(list, ret, sizeof(*list), keytag_compare); |
1282 | 0 | return ret; |
1283 | 0 | } |
1284 | | |
1285 | | int |
1286 | | anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs, |
1287 | | size_t namelen, uint16_t dclass, uint16_t keytag) |
1288 | 0 | { |
1289 | 0 | uint16_t* taglist; |
1290 | 0 | uint16_t* tl; |
1291 | 0 | size_t numtag, i; |
1292 | 0 | struct trust_anchor* anchor = anchor_find(anchors, |
1293 | 0 | name, namelabs, namelen, dclass); |
1294 | 0 | if(!anchor) |
1295 | 0 | return 0; |
1296 | 0 | if(!anchor->numDS && !anchor->numDNSKEY) { |
1297 | 0 | lock_basic_unlock(&anchor->lock); |
1298 | 0 | return 0; |
1299 | 0 | } |
1300 | | |
1301 | 0 | taglist = calloc(anchor->numDS + anchor->numDNSKEY, sizeof(*taglist)); |
1302 | 0 | if(!taglist) { |
1303 | 0 | lock_basic_unlock(&anchor->lock); |
1304 | 0 | return 0; |
1305 | 0 | } |
1306 | | |
1307 | 0 | numtag = anchor_list_keytags(anchor, taglist, |
1308 | 0 | anchor->numDS+anchor->numDNSKEY); |
1309 | 0 | lock_basic_unlock(&anchor->lock); |
1310 | 0 | if(!numtag) { |
1311 | 0 | free(taglist); |
1312 | 0 | return 0; |
1313 | 0 | } |
1314 | 0 | tl = taglist; |
1315 | 0 | for(i=0; i<numtag; i++) { |
1316 | 0 | if(*tl == keytag) { |
1317 | 0 | free(taglist); |
1318 | 0 | return 1; |
1319 | 0 | } |
1320 | 0 | tl++; |
1321 | 0 | } |
1322 | 0 | free(taglist); |
1323 | 0 | return 0; |
1324 | 0 | } |