/src/unbound/validator/autotrust.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * validator/autotrust.c - RFC5011 trust anchor management for unbound. |
3 | | * |
4 | | * Copyright (c) 2009, 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 | | * Contains autotrust implementation. The implementation was taken from |
40 | | * the autotrust daemon (BSD licensed), written by Matthijs Mekking. |
41 | | * It was modified to fit into unbound. The state table process is the same. |
42 | | */ |
43 | | #include "config.h" |
44 | | #include "validator/autotrust.h" |
45 | | #include "validator/val_anchor.h" |
46 | | #include "validator/val_utils.h" |
47 | | #include "validator/val_sigcrypt.h" |
48 | | #include "util/data/dname.h" |
49 | | #include "util/data/packed_rrset.h" |
50 | | #include "util/log.h" |
51 | | #include "util/module.h" |
52 | | #include "util/net_help.h" |
53 | | #include "util/config_file.h" |
54 | | #include "util/regional.h" |
55 | | #include "util/random.h" |
56 | | #include "util/data/msgparse.h" |
57 | | #include "services/mesh.h" |
58 | | #include "services/cache/rrset.h" |
59 | | #include "validator/val_kcache.h" |
60 | | #include "sldns/sbuffer.h" |
61 | | #include "sldns/wire2str.h" |
62 | | #include "sldns/str2wire.h" |
63 | | #include "sldns/keyraw.h" |
64 | | #include "sldns/rrdef.h" |
65 | | #include <stdarg.h> |
66 | | #include <ctype.h> |
67 | | |
68 | | /** number of times a key must be seen before it can become valid */ |
69 | 0 | #define MIN_PENDINGCOUNT 2 |
70 | | |
71 | | /** Event: Revoked */ |
72 | | static void do_revoked(struct module_env* env, struct autr_ta* anchor, int* c); |
73 | | |
74 | | struct autr_global_data* autr_global_create(void) |
75 | 0 | { |
76 | 0 | struct autr_global_data* global; |
77 | 0 | global = (struct autr_global_data*)malloc(sizeof(*global)); |
78 | 0 | if(!global) |
79 | 0 | return NULL; |
80 | 0 | rbtree_init(&global->probe, &probetree_cmp); |
81 | 0 | return global; |
82 | 0 | } |
83 | | |
84 | | void autr_global_delete(struct autr_global_data* global) |
85 | 0 | { |
86 | 0 | if(!global) |
87 | 0 | return; |
88 | | /* elements deleted by parent */ |
89 | 0 | free(global); |
90 | 0 | } |
91 | | |
92 | | int probetree_cmp(const void* x, const void* y) |
93 | 0 | { |
94 | 0 | struct trust_anchor* a = (struct trust_anchor*)x; |
95 | 0 | struct trust_anchor* b = (struct trust_anchor*)y; |
96 | 0 | log_assert(a->autr && b->autr); |
97 | 0 | if(a->autr->next_probe_time < b->autr->next_probe_time) |
98 | 0 | return -1; |
99 | 0 | if(a->autr->next_probe_time > b->autr->next_probe_time) |
100 | 0 | return 1; |
101 | | /* time is equal, sort on trust point identity */ |
102 | 0 | return anchor_cmp(x, y); |
103 | 0 | } |
104 | | |
105 | | size_t |
106 | | autr_get_num_anchors(struct val_anchors* anchors) |
107 | 0 | { |
108 | 0 | size_t res = 0; |
109 | 0 | if(!anchors) |
110 | 0 | return 0; |
111 | 0 | lock_basic_lock(&anchors->lock); |
112 | 0 | if(anchors->autr) |
113 | 0 | res = anchors->autr->probe.count; |
114 | 0 | lock_basic_unlock(&anchors->lock); |
115 | 0 | return res; |
116 | 0 | } |
117 | | |
118 | | /** Position in string */ |
119 | | static int |
120 | | position_in_string(char *str, const char* sub) |
121 | 0 | { |
122 | 0 | char* pos = strstr(str, sub); |
123 | 0 | if(pos) |
124 | 0 | return (int)(pos-str)+(int)strlen(sub); |
125 | 0 | return -1; |
126 | 0 | } |
127 | | |
128 | | /** Debug routine to print pretty key information */ |
129 | | static void |
130 | | verbose_key(struct autr_ta* ta, enum verbosity_value level, |
131 | | const char* format, ...) ATTR_FORMAT(printf, 3, 4); |
132 | | |
133 | | /** |
134 | | * Implementation of debug pretty key print |
135 | | * @param ta: trust anchor key with DNSKEY data. |
136 | | * @param level: verbosity level to print at. |
137 | | * @param format: printf style format string. |
138 | | */ |
139 | | static void |
140 | | verbose_key(struct autr_ta* ta, enum verbosity_value level, |
141 | | const char* format, ...) |
142 | 0 | { |
143 | 0 | va_list args; |
144 | 0 | va_start(args, format); |
145 | 0 | if(verbosity >= level) { |
146 | 0 | char* str = sldns_wire2str_dname(ta->rr, ta->dname_len); |
147 | 0 | int keytag = (int)sldns_calc_keytag_raw(sldns_wirerr_get_rdata( |
148 | 0 | ta->rr, ta->rr_len, ta->dname_len), |
149 | 0 | sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, |
150 | 0 | ta->dname_len)); |
151 | 0 | char msg[MAXSYSLOGMSGLEN]; |
152 | 0 | vsnprintf(msg, sizeof(msg), format, args); |
153 | 0 | verbose(level, "%s key %d %s", str?str:"??", keytag, msg); |
154 | 0 | free(str); |
155 | 0 | } |
156 | 0 | va_end(args); |
157 | 0 | } |
158 | | |
159 | | /** |
160 | | * Parse comments |
161 | | * @param str: to parse |
162 | | * @param ta: trust key autotrust metadata |
163 | | * @return false on failure. |
164 | | */ |
165 | | static int |
166 | | parse_comments(char* str, struct autr_ta* ta) |
167 | 0 | { |
168 | 0 | int len = (int)strlen(str), pos = 0, timestamp = 0; |
169 | 0 | char* comment = (char*) malloc(sizeof(char)*len+1); |
170 | 0 | char* comments = comment; |
171 | 0 | if(!comment) { |
172 | 0 | log_err("malloc failure in parse"); |
173 | 0 | return 0; |
174 | 0 | } |
175 | | /* skip over whitespace and data at start of line */ |
176 | 0 | while (*str != '\0' && *str != ';') |
177 | 0 | str++; |
178 | 0 | if (*str == ';') |
179 | 0 | str++; |
180 | | /* copy comments */ |
181 | 0 | while (*str != '\0') |
182 | 0 | { |
183 | 0 | *comments = *str; |
184 | 0 | comments++; |
185 | 0 | str++; |
186 | 0 | } |
187 | 0 | *comments = '\0'; |
188 | |
|
189 | 0 | comments = comment; |
190 | | |
191 | | /* read state */ |
192 | 0 | pos = position_in_string(comments, "state="); |
193 | 0 | if (pos >= (int) strlen(comments)) |
194 | 0 | { |
195 | 0 | log_err("parse error"); |
196 | 0 | free(comment); |
197 | 0 | return 0; |
198 | 0 | } |
199 | 0 | if (pos <= 0) |
200 | 0 | ta->s = AUTR_STATE_VALID; |
201 | 0 | else |
202 | 0 | { |
203 | 0 | int s = (int) comments[pos] - '0'; |
204 | 0 | switch(s) |
205 | 0 | { |
206 | 0 | case AUTR_STATE_START: |
207 | 0 | case AUTR_STATE_ADDPEND: |
208 | 0 | case AUTR_STATE_VALID: |
209 | 0 | case AUTR_STATE_MISSING: |
210 | 0 | case AUTR_STATE_REVOKED: |
211 | 0 | case AUTR_STATE_REMOVED: |
212 | 0 | ta->s = s; |
213 | 0 | break; |
214 | 0 | default: |
215 | 0 | verbose_key(ta, VERB_OPS, "has undefined " |
216 | 0 | "state, considered NewKey"); |
217 | 0 | ta->s = AUTR_STATE_START; |
218 | 0 | break; |
219 | 0 | } |
220 | 0 | } |
221 | | /* read pending count */ |
222 | 0 | pos = position_in_string(comments, "count="); |
223 | 0 | if (pos >= (int) strlen(comments)) |
224 | 0 | { |
225 | 0 | log_err("parse error"); |
226 | 0 | free(comment); |
227 | 0 | return 0; |
228 | 0 | } |
229 | 0 | if (pos <= 0) |
230 | 0 | ta->pending_count = 0; |
231 | 0 | else |
232 | 0 | { |
233 | 0 | comments += pos; |
234 | 0 | ta->pending_count = (uint8_t)atoi(comments); |
235 | 0 | } |
236 | | |
237 | | /* read last change */ |
238 | 0 | pos = position_in_string(comments, "lastchange="); |
239 | 0 | if (pos >= (int) strlen(comments)) |
240 | 0 | { |
241 | 0 | log_err("parse error"); |
242 | 0 | free(comment); |
243 | 0 | return 0; |
244 | 0 | } |
245 | 0 | if (pos >= 0) |
246 | 0 | { |
247 | 0 | comments += pos; |
248 | 0 | timestamp = atoi(comments); |
249 | 0 | } |
250 | 0 | if (pos < 0 || !timestamp) |
251 | 0 | ta->last_change = 0; |
252 | 0 | else |
253 | 0 | ta->last_change = (time_t)timestamp; |
254 | |
|
255 | 0 | free(comment); |
256 | 0 | return 1; |
257 | 0 | } |
258 | | |
259 | | /** Check if a line contains data (besides comments) */ |
260 | | static int |
261 | | str_contains_data(char* str, char comment) |
262 | 0 | { |
263 | 0 | while (*str != '\0') { |
264 | 0 | if (*str == comment || *str == '\n') |
265 | 0 | return 0; |
266 | 0 | if (*str != ' ' && *str != '\t') |
267 | 0 | return 1; |
268 | 0 | str++; |
269 | 0 | } |
270 | 0 | return 0; |
271 | 0 | } |
272 | | |
273 | | /** Get DNSKEY flags |
274 | | * rdata without rdatalen in front of it. */ |
275 | | static int |
276 | | dnskey_flags(uint16_t t, uint8_t* rdata, size_t len) |
277 | 0 | { |
278 | 0 | uint16_t f; |
279 | 0 | if(t != LDNS_RR_TYPE_DNSKEY) |
280 | 0 | return 0; |
281 | 0 | if(len < 2) |
282 | 0 | return 0; |
283 | 0 | memmove(&f, rdata, 2); |
284 | 0 | f = ntohs(f); |
285 | 0 | return (int)f; |
286 | 0 | } |
287 | | |
288 | | /** Check if KSK DNSKEY. |
289 | | * pass rdata without rdatalen in front of it */ |
290 | | static int |
291 | | rr_is_dnskey_sep(uint16_t t, uint8_t* rdata, size_t len) |
292 | 0 | { |
293 | 0 | return (dnskey_flags(t, rdata, len)&DNSKEY_BIT_SEP); |
294 | 0 | } |
295 | | |
296 | | /** Check if TA is KSK DNSKEY */ |
297 | | static int |
298 | | ta_is_dnskey_sep(struct autr_ta* ta) |
299 | 0 | { |
300 | 0 | return (dnskey_flags( |
301 | 0 | sldns_wirerr_get_type(ta->rr, ta->rr_len, ta->dname_len), |
302 | 0 | sldns_wirerr_get_rdata(ta->rr, ta->rr_len, ta->dname_len), |
303 | 0 | sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, ta->dname_len) |
304 | 0 | ) & DNSKEY_BIT_SEP); |
305 | 0 | } |
306 | | |
307 | | /** Check if REVOKED DNSKEY |
308 | | * pass rdata without rdatalen in front of it */ |
309 | | static int |
310 | | rr_is_dnskey_revoked(uint16_t t, uint8_t* rdata, size_t len) |
311 | 0 | { |
312 | 0 | return (dnskey_flags(t, rdata, len)&LDNS_KEY_REVOKE_KEY); |
313 | 0 | } |
314 | | |
315 | | /** create ta */ |
316 | | static struct autr_ta* |
317 | | autr_ta_create(uint8_t* rr, size_t rr_len, size_t dname_len) |
318 | 0 | { |
319 | 0 | struct autr_ta* ta = (struct autr_ta*)calloc(1, sizeof(*ta)); |
320 | 0 | if(!ta) { |
321 | 0 | free(rr); |
322 | 0 | return NULL; |
323 | 0 | } |
324 | 0 | ta->rr = rr; |
325 | 0 | ta->rr_len = rr_len; |
326 | 0 | ta->dname_len = dname_len; |
327 | 0 | return ta; |
328 | 0 | } |
329 | | |
330 | | /** create tp */ |
331 | | static struct trust_anchor* |
332 | | autr_tp_create(struct val_anchors* anchors, uint8_t* own, size_t own_len, |
333 | | uint16_t dc) |
334 | 0 | { |
335 | 0 | struct trust_anchor* tp = (struct trust_anchor*)calloc(1, sizeof(*tp)); |
336 | 0 | if(!tp) return NULL; |
337 | 0 | tp->name = memdup(own, own_len); |
338 | 0 | if(!tp->name) { |
339 | 0 | free(tp); |
340 | 0 | return NULL; |
341 | 0 | } |
342 | 0 | tp->namelen = own_len; |
343 | 0 | tp->namelabs = dname_count_labels(tp->name); |
344 | 0 | tp->node.key = tp; |
345 | 0 | tp->dclass = dc; |
346 | 0 | tp->autr = (struct autr_point_data*)calloc(1, sizeof(*tp->autr)); |
347 | 0 | if(!tp->autr) { |
348 | 0 | free(tp->name); |
349 | 0 | free(tp); |
350 | 0 | return NULL; |
351 | 0 | } |
352 | 0 | tp->autr->pnode.key = tp; |
353 | |
|
354 | 0 | lock_basic_lock(&anchors->lock); |
355 | 0 | if(!rbtree_insert(anchors->tree, &tp->node)) { |
356 | 0 | char buf[LDNS_MAX_DOMAINLEN]; |
357 | 0 | lock_basic_unlock(&anchors->lock); |
358 | 0 | dname_str(tp->name, buf); |
359 | 0 | log_err("trust anchor for '%s' presented twice", buf); |
360 | 0 | free(tp->name); |
361 | 0 | free(tp->autr); |
362 | 0 | free(tp); |
363 | 0 | return NULL; |
364 | 0 | } |
365 | 0 | if(!rbtree_insert(&anchors->autr->probe, &tp->autr->pnode)) { |
366 | 0 | char buf[LDNS_MAX_DOMAINLEN]; |
367 | 0 | (void)rbtree_delete(anchors->tree, tp); |
368 | 0 | lock_basic_unlock(&anchors->lock); |
369 | 0 | dname_str(tp->name, buf); |
370 | 0 | log_err("trust anchor for '%s' in probetree twice", buf); |
371 | 0 | free(tp->name); |
372 | 0 | free(tp->autr); |
373 | 0 | free(tp); |
374 | 0 | return NULL; |
375 | 0 | } |
376 | 0 | lock_basic_init(&tp->lock); |
377 | 0 | lock_protect(&tp->lock, tp, sizeof(*tp)); |
378 | 0 | lock_protect(&tp->lock, tp->autr, sizeof(*tp->autr)); |
379 | 0 | lock_basic_unlock(&anchors->lock); |
380 | 0 | return tp; |
381 | 0 | } |
382 | | |
383 | | /** delete assembled rrsets */ |
384 | | static void |
385 | | autr_rrset_delete(struct ub_packed_rrset_key* r) |
386 | 0 | { |
387 | 0 | if(r) { |
388 | 0 | free(r->rk.dname); |
389 | 0 | free(r->entry.data); |
390 | 0 | free(r); |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | void autr_point_delete(struct trust_anchor* tp) |
395 | 0 | { |
396 | 0 | if(!tp) |
397 | 0 | return; |
398 | 0 | lock_unprotect(&tp->lock, tp); |
399 | 0 | lock_unprotect(&tp->lock, tp->autr); |
400 | 0 | lock_basic_destroy(&tp->lock); |
401 | 0 | autr_rrset_delete(tp->ds_rrset); |
402 | 0 | autr_rrset_delete(tp->dnskey_rrset); |
403 | 0 | if(tp->autr) { |
404 | 0 | struct autr_ta* p = tp->autr->keys, *np; |
405 | 0 | while(p) { |
406 | 0 | np = p->next; |
407 | 0 | free(p->rr); |
408 | 0 | free(p); |
409 | 0 | p = np; |
410 | 0 | } |
411 | 0 | free(tp->autr->file); |
412 | 0 | free(tp->autr); |
413 | 0 | } |
414 | 0 | free(tp->name); |
415 | 0 | free(tp); |
416 | 0 | } |
417 | | |
418 | | /** find or add a new trust point for autotrust */ |
419 | | static struct trust_anchor* |
420 | | find_add_tp(struct val_anchors* anchors, uint8_t* rr, size_t rr_len, |
421 | | size_t dname_len) |
422 | 0 | { |
423 | 0 | struct trust_anchor* tp; |
424 | 0 | tp = anchor_find(anchors, rr, dname_count_labels(rr), dname_len, |
425 | 0 | sldns_wirerr_get_class(rr, rr_len, dname_len)); |
426 | 0 | if(tp) { |
427 | 0 | if(!tp->autr) { |
428 | 0 | log_err("anchor cannot be with and without autotrust"); |
429 | 0 | lock_basic_unlock(&tp->lock); |
430 | 0 | return NULL; |
431 | 0 | } |
432 | 0 | return tp; |
433 | 0 | } |
434 | 0 | tp = autr_tp_create(anchors, rr, dname_len, sldns_wirerr_get_class(rr, |
435 | 0 | rr_len, dname_len)); |
436 | 0 | if(!tp) |
437 | 0 | return NULL; |
438 | 0 | lock_basic_lock(&tp->lock); |
439 | 0 | return tp; |
440 | 0 | } |
441 | | |
442 | | /** Add trust anchor from RR */ |
443 | | static struct autr_ta* |
444 | | add_trustanchor_frm_rr(struct val_anchors* anchors, uint8_t* rr, size_t rr_len, |
445 | | size_t dname_len, struct trust_anchor** tp) |
446 | 0 | { |
447 | 0 | struct autr_ta* ta = autr_ta_create(rr, rr_len, dname_len); |
448 | 0 | if(!ta) |
449 | 0 | return NULL; |
450 | 0 | *tp = find_add_tp(anchors, rr, rr_len, dname_len); |
451 | 0 | if(!*tp) { |
452 | 0 | free(ta->rr); |
453 | 0 | free(ta); |
454 | 0 | return NULL; |
455 | 0 | } |
456 | | /* add ta to tp */ |
457 | 0 | ta->next = (*tp)->autr->keys; |
458 | 0 | (*tp)->autr->keys = ta; |
459 | 0 | lock_basic_unlock(&(*tp)->lock); |
460 | 0 | return ta; |
461 | 0 | } |
462 | | |
463 | | /** |
464 | | * Add new trust anchor from a string in file. |
465 | | * @param anchors: all anchors |
466 | | * @param str: string with anchor and comments, if any comments. |
467 | | * @param tp: trust point returned. |
468 | | * @param origin: what to use for @ |
469 | | * @param origin_len: length of origin |
470 | | * @param prev: previous rr name |
471 | | * @param prev_len: length of prev |
472 | | * @param skip: if true, the result is NULL, but not an error, skip it. |
473 | | * @return new key in trust point. |
474 | | */ |
475 | | static struct autr_ta* |
476 | | add_trustanchor_frm_str(struct val_anchors* anchors, char* str, |
477 | | struct trust_anchor** tp, uint8_t* origin, size_t origin_len, |
478 | | uint8_t** prev, size_t* prev_len, int* skip) |
479 | 0 | { |
480 | 0 | uint8_t rr[LDNS_RR_BUF_SIZE]; |
481 | 0 | size_t rr_len = sizeof(rr), dname_len; |
482 | 0 | uint8_t* drr; |
483 | 0 | int lstatus; |
484 | 0 | if (!str_contains_data(str, ';')) { |
485 | 0 | *skip = 1; |
486 | 0 | return NULL; /* empty line */ |
487 | 0 | } |
488 | 0 | if(0 != (lstatus = sldns_str2wire_rr_buf(str, rr, &rr_len, &dname_len, |
489 | 0 | 0, origin, origin_len, *prev, *prev_len))) |
490 | 0 | { |
491 | 0 | log_err("ldns error while converting string to RR at%d: %s: %s", |
492 | 0 | LDNS_WIREPARSE_OFFSET(lstatus), |
493 | 0 | sldns_get_errorstr_parse(lstatus), str); |
494 | 0 | return NULL; |
495 | 0 | } |
496 | 0 | free(*prev); |
497 | 0 | *prev = memdup(rr, dname_len); |
498 | 0 | *prev_len = dname_len; |
499 | 0 | if(!*prev) { |
500 | 0 | log_err("malloc failure in add_trustanchor"); |
501 | 0 | return NULL; |
502 | 0 | } |
503 | 0 | if(sldns_wirerr_get_type(rr, rr_len, dname_len)!=LDNS_RR_TYPE_DNSKEY && |
504 | 0 | sldns_wirerr_get_type(rr, rr_len, dname_len)!=LDNS_RR_TYPE_DS) { |
505 | 0 | *skip = 1; |
506 | 0 | return NULL; /* only DS and DNSKEY allowed */ |
507 | 0 | } |
508 | 0 | drr = memdup(rr, rr_len); |
509 | 0 | if(!drr) { |
510 | 0 | log_err("malloc failure in add trustanchor"); |
511 | 0 | return NULL; |
512 | 0 | } |
513 | 0 | return add_trustanchor_frm_rr(anchors, drr, rr_len, dname_len, tp); |
514 | 0 | } |
515 | | |
516 | | /** |
517 | | * Load single anchor |
518 | | * @param anchors: all points. |
519 | | * @param str: comments line |
520 | | * @param fname: filename |
521 | | * @param origin: the $ORIGIN. |
522 | | * @param origin_len: length of origin |
523 | | * @param prev: passed to ldns. |
524 | | * @param prev_len: length of prev |
525 | | * @param skip: if true, the result is NULL, but not an error, skip it. |
526 | | * @return false on failure, otherwise the tp read. |
527 | | */ |
528 | | static struct trust_anchor* |
529 | | load_trustanchor(struct val_anchors* anchors, char* str, const char* fname, |
530 | | uint8_t* origin, size_t origin_len, uint8_t** prev, size_t* prev_len, |
531 | | int* skip) |
532 | 0 | { |
533 | 0 | struct autr_ta* ta = NULL; |
534 | 0 | struct trust_anchor* tp = NULL; |
535 | |
|
536 | 0 | ta = add_trustanchor_frm_str(anchors, str, &tp, origin, origin_len, |
537 | 0 | prev, prev_len, skip); |
538 | 0 | if(!ta) |
539 | 0 | return NULL; |
540 | 0 | lock_basic_lock(&tp->lock); |
541 | 0 | if(!parse_comments(str, ta)) { |
542 | 0 | lock_basic_unlock(&tp->lock); |
543 | 0 | return NULL; |
544 | 0 | } |
545 | 0 | if(!tp->autr->file) { |
546 | 0 | tp->autr->file = strdup(fname); |
547 | 0 | if(!tp->autr->file) { |
548 | 0 | lock_basic_unlock(&tp->lock); |
549 | 0 | log_err("malloc failure"); |
550 | 0 | return NULL; |
551 | 0 | } |
552 | 0 | } |
553 | 0 | lock_basic_unlock(&tp->lock); |
554 | 0 | return tp; |
555 | 0 | } |
556 | | |
557 | | /** iterator for DSes from keylist. return true if a next element exists */ |
558 | | static int |
559 | | assemble_iterate_ds(struct autr_ta** list, uint8_t** rr, size_t* rr_len, |
560 | | size_t* dname_len) |
561 | 0 | { |
562 | 0 | while(*list) { |
563 | 0 | if(sldns_wirerr_get_type((*list)->rr, (*list)->rr_len, |
564 | 0 | (*list)->dname_len) == LDNS_RR_TYPE_DS) { |
565 | 0 | *rr = (*list)->rr; |
566 | 0 | *rr_len = (*list)->rr_len; |
567 | 0 | *dname_len = (*list)->dname_len; |
568 | 0 | *list = (*list)->next; |
569 | 0 | return 1; |
570 | 0 | } |
571 | 0 | *list = (*list)->next; |
572 | 0 | } |
573 | 0 | return 0; |
574 | 0 | } |
575 | | |
576 | | /** iterator for DNSKEYs from keylist. return true if a next element exists */ |
577 | | static int |
578 | | assemble_iterate_dnskey(struct autr_ta** list, uint8_t** rr, size_t* rr_len, |
579 | | size_t* dname_len) |
580 | 0 | { |
581 | 0 | while(*list) { |
582 | 0 | if(sldns_wirerr_get_type((*list)->rr, (*list)->rr_len, |
583 | 0 | (*list)->dname_len) != LDNS_RR_TYPE_DS && |
584 | 0 | ((*list)->s == AUTR_STATE_VALID || |
585 | 0 | (*list)->s == AUTR_STATE_MISSING)) { |
586 | 0 | *rr = (*list)->rr; |
587 | 0 | *rr_len = (*list)->rr_len; |
588 | 0 | *dname_len = (*list)->dname_len; |
589 | 0 | *list = (*list)->next; |
590 | 0 | return 1; |
591 | 0 | } |
592 | 0 | *list = (*list)->next; |
593 | 0 | } |
594 | 0 | return 0; |
595 | 0 | } |
596 | | |
597 | | /** see if iterator-list has any elements in it, or it is empty */ |
598 | | static int |
599 | | assemble_iterate_hasfirst(int iter(struct autr_ta**, uint8_t**, size_t*, |
600 | | size_t*), struct autr_ta* list) |
601 | 0 | { |
602 | 0 | uint8_t* rr = NULL; |
603 | 0 | size_t rr_len = 0, dname_len = 0; |
604 | 0 | return iter(&list, &rr, &rr_len, &dname_len); |
605 | 0 | } |
606 | | |
607 | | /** number of elements in iterator list */ |
608 | | static size_t |
609 | | assemble_iterate_count(int iter(struct autr_ta**, uint8_t**, size_t*, |
610 | | size_t*), struct autr_ta* list) |
611 | 0 | { |
612 | 0 | uint8_t* rr = NULL; |
613 | 0 | size_t i = 0, rr_len = 0, dname_len = 0; |
614 | 0 | while(iter(&list, &rr, &rr_len, &dname_len)) { |
615 | 0 | i++; |
616 | 0 | } |
617 | 0 | return i; |
618 | 0 | } |
619 | | |
620 | | /** |
621 | | * Create a ub_packed_rrset_key allocated on the heap. |
622 | | * It therefore does not have the correct ID value, and cannot be used |
623 | | * inside the cache. It can be used in storage outside of the cache. |
624 | | * Keys for the cache have to be obtained from alloc.h . |
625 | | * @param iter: iterator over the elements in the list. It filters elements. |
626 | | * @param list: the list. |
627 | | * @return key allocated or NULL on failure. |
628 | | */ |
629 | | static struct ub_packed_rrset_key* |
630 | | ub_packed_rrset_heap_key(int iter(struct autr_ta**, uint8_t**, size_t*, |
631 | | size_t*), struct autr_ta* list) |
632 | 0 | { |
633 | 0 | uint8_t* rr = NULL; |
634 | 0 | size_t rr_len = 0, dname_len = 0; |
635 | 0 | struct ub_packed_rrset_key* k; |
636 | 0 | if(!iter(&list, &rr, &rr_len, &dname_len)) |
637 | 0 | return NULL; |
638 | 0 | k = (struct ub_packed_rrset_key*)calloc(1, sizeof(*k)); |
639 | 0 | if(!k) |
640 | 0 | return NULL; |
641 | 0 | k->rk.type = htons(sldns_wirerr_get_type(rr, rr_len, dname_len)); |
642 | 0 | k->rk.rrset_class = htons(sldns_wirerr_get_class(rr, rr_len, dname_len)); |
643 | 0 | k->rk.dname_len = dname_len; |
644 | 0 | k->rk.dname = memdup(rr, dname_len); |
645 | 0 | if(!k->rk.dname) { |
646 | 0 | free(k); |
647 | 0 | return NULL; |
648 | 0 | } |
649 | 0 | return k; |
650 | 0 | } |
651 | | |
652 | | /** |
653 | | * Create packed_rrset data on the heap. |
654 | | * @param iter: iterator over the elements in the list. It filters elements. |
655 | | * @param list: the list. |
656 | | * @return data allocated or NULL on failure. |
657 | | */ |
658 | | static struct packed_rrset_data* |
659 | | packed_rrset_heap_data(int iter(struct autr_ta**, uint8_t**, size_t*, |
660 | | size_t*), struct autr_ta* list) |
661 | 0 | { |
662 | 0 | uint8_t* rr = NULL; |
663 | 0 | size_t rr_len = 0, dname_len = 0; |
664 | 0 | struct packed_rrset_data* data; |
665 | 0 | size_t count=0, rrsig_count=0, len=0, i, total; |
666 | 0 | uint8_t* nextrdata; |
667 | 0 | struct autr_ta* list_i; |
668 | 0 | time_t ttl = 0; |
669 | |
|
670 | 0 | list_i = list; |
671 | 0 | while(iter(&list_i, &rr, &rr_len, &dname_len)) { |
672 | 0 | if(sldns_wirerr_get_type(rr, rr_len, dname_len) == |
673 | 0 | LDNS_RR_TYPE_RRSIG) |
674 | 0 | rrsig_count++; |
675 | 0 | else count++; |
676 | | /* sizeof the rdlength + rdatalen */ |
677 | 0 | len += 2 + sldns_wirerr_get_rdatalen(rr, rr_len, dname_len); |
678 | 0 | ttl = (time_t)sldns_wirerr_get_ttl(rr, rr_len, dname_len); |
679 | 0 | } |
680 | 0 | if(count == 0 && rrsig_count == 0) |
681 | 0 | return NULL; |
682 | | |
683 | | /* allocate */ |
684 | 0 | total = count + rrsig_count; |
685 | 0 | len += sizeof(*data) + total*(sizeof(size_t) + sizeof(time_t) + |
686 | 0 | sizeof(uint8_t*)); |
687 | 0 | data = (struct packed_rrset_data*)calloc(1, len); |
688 | 0 | if(!data) |
689 | 0 | return NULL; |
690 | | |
691 | | /* fill it */ |
692 | 0 | data->ttl = ttl; |
693 | 0 | data->count = count; |
694 | 0 | data->rrsig_count = rrsig_count; |
695 | 0 | data->rr_len = (size_t*)((uint8_t*)data + |
696 | 0 | sizeof(struct packed_rrset_data)); |
697 | 0 | data->rr_data = (uint8_t**)&(data->rr_len[total]); |
698 | 0 | data->rr_ttl = (time_t*)&(data->rr_data[total]); |
699 | 0 | nextrdata = (uint8_t*)&(data->rr_ttl[total]); |
700 | | |
701 | | /* fill out len, ttl, fields */ |
702 | 0 | list_i = list; |
703 | 0 | i = 0; |
704 | 0 | while(iter(&list_i, &rr, &rr_len, &dname_len)) { |
705 | 0 | data->rr_ttl[i] = (time_t)sldns_wirerr_get_ttl(rr, rr_len, |
706 | 0 | dname_len); |
707 | 0 | if(data->rr_ttl[i] < data->ttl) |
708 | 0 | data->ttl = data->rr_ttl[i]; |
709 | 0 | data->rr_len[i] = 2 /* the rdlength */ + |
710 | 0 | sldns_wirerr_get_rdatalen(rr, rr_len, dname_len); |
711 | 0 | i++; |
712 | 0 | } |
713 | | |
714 | | /* fixup rest of ptrs */ |
715 | 0 | for(i=0; i<total; i++) { |
716 | 0 | data->rr_data[i] = nextrdata; |
717 | 0 | nextrdata += data->rr_len[i]; |
718 | 0 | } |
719 | | |
720 | | /* copy data in there */ |
721 | 0 | list_i = list; |
722 | 0 | i = 0; |
723 | 0 | while(iter(&list_i, &rr, &rr_len, &dname_len)) { |
724 | 0 | log_assert(data->rr_data[i]); |
725 | 0 | memmove(data->rr_data[i], |
726 | 0 | sldns_wirerr_get_rdatawl(rr, rr_len, dname_len), |
727 | 0 | data->rr_len[i]); |
728 | 0 | i++; |
729 | 0 | } |
730 | |
|
731 | 0 | if(data->rrsig_count && data->count == 0) { |
732 | 0 | data->count = data->rrsig_count; /* rrset type is RRSIG */ |
733 | 0 | data->rrsig_count = 0; |
734 | 0 | } |
735 | 0 | return data; |
736 | 0 | } |
737 | | |
738 | | /** |
739 | | * Assemble the trust anchors into DS and DNSKEY packed rrsets. |
740 | | * Uses only VALID and MISSING DNSKEYs. |
741 | | * Read the sldns_rrs and builds packed rrsets |
742 | | * @param tp: the trust point. Must be locked. |
743 | | * @return false on malloc failure. |
744 | | */ |
745 | | static int |
746 | | autr_assemble(struct trust_anchor* tp) |
747 | 0 | { |
748 | 0 | struct ub_packed_rrset_key* ubds=NULL, *ubdnskey=NULL; |
749 | | |
750 | | /* make packed rrset keys - malloced with no ID number, they |
751 | | * are not in the cache */ |
752 | | /* make packed rrset data (if there is a key) */ |
753 | 0 | if(assemble_iterate_hasfirst(assemble_iterate_ds, tp->autr->keys)) { |
754 | 0 | ubds = ub_packed_rrset_heap_key( |
755 | 0 | assemble_iterate_ds, tp->autr->keys); |
756 | 0 | if(!ubds) |
757 | 0 | goto error_cleanup; |
758 | 0 | ubds->entry.data = packed_rrset_heap_data( |
759 | 0 | assemble_iterate_ds, tp->autr->keys); |
760 | 0 | if(!ubds->entry.data) |
761 | 0 | goto error_cleanup; |
762 | 0 | } |
763 | | |
764 | | /* make packed DNSKEY data */ |
765 | 0 | if(assemble_iterate_hasfirst(assemble_iterate_dnskey, tp->autr->keys)) { |
766 | 0 | ubdnskey = ub_packed_rrset_heap_key( |
767 | 0 | assemble_iterate_dnskey, tp->autr->keys); |
768 | 0 | if(!ubdnskey) |
769 | 0 | goto error_cleanup; |
770 | 0 | ubdnskey->entry.data = packed_rrset_heap_data( |
771 | 0 | assemble_iterate_dnskey, tp->autr->keys); |
772 | 0 | if(!ubdnskey->entry.data) { |
773 | 0 | error_cleanup: |
774 | 0 | autr_rrset_delete(ubds); |
775 | 0 | autr_rrset_delete(ubdnskey); |
776 | 0 | return 0; |
777 | 0 | } |
778 | 0 | } |
779 | | |
780 | | /* we have prepared the new keys so nothing can go wrong any more. |
781 | | * And we are sure we cannot be left without trustanchor after |
782 | | * any errors. Put in the new keys and remove old ones. */ |
783 | | |
784 | | /* free the old data */ |
785 | 0 | autr_rrset_delete(tp->ds_rrset); |
786 | 0 | autr_rrset_delete(tp->dnskey_rrset); |
787 | | |
788 | | /* assign the data to replace the old */ |
789 | 0 | tp->ds_rrset = ubds; |
790 | 0 | tp->dnskey_rrset = ubdnskey; |
791 | 0 | tp->numDS = assemble_iterate_count(assemble_iterate_ds, |
792 | 0 | tp->autr->keys); |
793 | 0 | tp->numDNSKEY = assemble_iterate_count(assemble_iterate_dnskey, |
794 | 0 | tp->autr->keys); |
795 | 0 | return 1; |
796 | 0 | } |
797 | | |
798 | | /** parse integer */ |
799 | | static unsigned int |
800 | | parse_int(char* line, int* ret) |
801 | 0 | { |
802 | 0 | char *e; |
803 | 0 | unsigned int x = (unsigned int)strtol(line, &e, 10); |
804 | 0 | if(line == e) { |
805 | 0 | *ret = -1; /* parse error */ |
806 | 0 | return 0; |
807 | 0 | } |
808 | 0 | *ret = 1; /* matched */ |
809 | 0 | return x; |
810 | 0 | } |
811 | | |
812 | | /** parse id sequence for anchor */ |
813 | | static struct trust_anchor* |
814 | | parse_id(struct val_anchors* anchors, char* line) |
815 | 0 | { |
816 | 0 | struct trust_anchor *tp; |
817 | 0 | int r; |
818 | 0 | uint16_t dclass; |
819 | 0 | uint8_t* dname; |
820 | 0 | size_t dname_len; |
821 | | /* read the owner name */ |
822 | 0 | char* next = strchr(line, ' '); |
823 | 0 | if(!next) |
824 | 0 | return NULL; |
825 | 0 | next[0] = 0; |
826 | 0 | dname = sldns_str2wire_dname(line, &dname_len); |
827 | 0 | if(!dname) |
828 | 0 | return NULL; |
829 | | |
830 | | /* read the class */ |
831 | 0 | dclass = parse_int(next+1, &r); |
832 | 0 | if(r == -1) { |
833 | 0 | free(dname); |
834 | 0 | return NULL; |
835 | 0 | } |
836 | | |
837 | | /* find the trust point */ |
838 | 0 | tp = autr_tp_create(anchors, dname, dname_len, dclass); |
839 | 0 | free(dname); |
840 | 0 | return tp; |
841 | 0 | } |
842 | | |
843 | | /** |
844 | | * Parse variable from trustanchor header |
845 | | * @param line: to parse |
846 | | * @param anchors: the anchor is added to this, if "id:" is seen. |
847 | | * @param anchor: the anchor as result value or previously returned anchor |
848 | | * value to read the variable lines into. |
849 | | * @return: 0 no match, -1 failed syntax error, +1 success line read. |
850 | | * +2 revoked trust anchor file. |
851 | | */ |
852 | | static int |
853 | | parse_var_line(char* line, struct val_anchors* anchors, |
854 | | struct trust_anchor** anchor) |
855 | 0 | { |
856 | 0 | struct trust_anchor* tp = *anchor; |
857 | 0 | int r = 0; |
858 | 0 | if(strncmp(line, ";;id: ", 6) == 0) { |
859 | 0 | *anchor = parse_id(anchors, line+6); |
860 | 0 | if(!*anchor) return -1; |
861 | 0 | else return 1; |
862 | 0 | } else if(strncmp(line, ";;REVOKED", 9) == 0) { |
863 | 0 | if(tp) { |
864 | 0 | log_err("REVOKED statement must be at start of file"); |
865 | 0 | return -1; |
866 | 0 | } |
867 | 0 | return 2; |
868 | 0 | } else if(strncmp(line, ";;last_queried: ", 16) == 0) { |
869 | 0 | if(!tp) return -1; |
870 | 0 | lock_basic_lock(&tp->lock); |
871 | 0 | tp->autr->last_queried = (time_t)parse_int(line+16, &r); |
872 | 0 | lock_basic_unlock(&tp->lock); |
873 | 0 | } else if(strncmp(line, ";;last_success: ", 16) == 0) { |
874 | 0 | if(!tp) return -1; |
875 | 0 | lock_basic_lock(&tp->lock); |
876 | 0 | tp->autr->last_success = (time_t)parse_int(line+16, &r); |
877 | 0 | lock_basic_unlock(&tp->lock); |
878 | 0 | } else if(strncmp(line, ";;next_probe_time: ", 19) == 0) { |
879 | 0 | if(!tp) return -1; |
880 | 0 | lock_basic_lock(&anchors->lock); |
881 | 0 | lock_basic_lock(&tp->lock); |
882 | 0 | (void)rbtree_delete(&anchors->autr->probe, tp); |
883 | 0 | tp->autr->next_probe_time = (time_t)parse_int(line+19, &r); |
884 | 0 | (void)rbtree_insert(&anchors->autr->probe, &tp->autr->pnode); |
885 | 0 | lock_basic_unlock(&tp->lock); |
886 | 0 | lock_basic_unlock(&anchors->lock); |
887 | 0 | } else if(strncmp(line, ";;query_failed: ", 16) == 0) { |
888 | 0 | if(!tp) return -1; |
889 | 0 | lock_basic_lock(&tp->lock); |
890 | 0 | tp->autr->query_failed = (uint8_t)parse_int(line+16, &r); |
891 | 0 | lock_basic_unlock(&tp->lock); |
892 | 0 | } else if(strncmp(line, ";;query_interval: ", 18) == 0) { |
893 | 0 | if(!tp) return -1; |
894 | 0 | lock_basic_lock(&tp->lock); |
895 | 0 | tp->autr->query_interval = (time_t)parse_int(line+18, &r); |
896 | 0 | lock_basic_unlock(&tp->lock); |
897 | 0 | } else if(strncmp(line, ";;retry_time: ", 14) == 0) { |
898 | 0 | if(!tp) return -1; |
899 | 0 | lock_basic_lock(&tp->lock); |
900 | 0 | tp->autr->retry_time = (time_t)parse_int(line+14, &r); |
901 | 0 | lock_basic_unlock(&tp->lock); |
902 | 0 | } |
903 | 0 | return r; |
904 | 0 | } |
905 | | |
906 | | /** handle origin lines */ |
907 | | static int |
908 | | handle_origin(char* line, uint8_t** origin, size_t* origin_len) |
909 | 0 | { |
910 | 0 | size_t len = 0; |
911 | 0 | while(isspace((unsigned char)*line)) |
912 | 0 | line++; |
913 | 0 | if(strncmp(line, "$ORIGIN", 7) != 0) |
914 | 0 | return 0; |
915 | 0 | free(*origin); |
916 | 0 | line += 7; |
917 | 0 | while(isspace((unsigned char)*line)) |
918 | 0 | line++; |
919 | 0 | *origin = sldns_str2wire_dname(line, &len); |
920 | 0 | *origin_len = len; |
921 | 0 | if(!*origin) |
922 | 0 | log_warn("malloc failure or parse error in $ORIGIN"); |
923 | 0 | return 1; |
924 | 0 | } |
925 | | |
926 | | /** Read one line and put multiline RRs onto one line string */ |
927 | | static int |
928 | | read_multiline(char* buf, size_t len, FILE* in, int* linenr) |
929 | 0 | { |
930 | 0 | char* pos = buf; |
931 | 0 | size_t left = len; |
932 | 0 | int depth = 0; |
933 | 0 | buf[len-1] = 0; |
934 | 0 | while(left > 0 && fgets(pos, (int)left, in) != NULL) { |
935 | 0 | size_t i, poslen = strlen(pos); |
936 | 0 | (*linenr)++; |
937 | | |
938 | | /* check what the new depth is after the line */ |
939 | | /* this routine cannot handle braces inside quotes, |
940 | | say for TXT records, but this routine only has to read keys */ |
941 | 0 | for(i=0; i<poslen; i++) { |
942 | 0 | if(pos[i] == '(') { |
943 | 0 | depth++; |
944 | 0 | } else if(pos[i] == ')') { |
945 | 0 | if(depth == 0) { |
946 | 0 | log_err("mismatch: too many ')'"); |
947 | 0 | return -1; |
948 | 0 | } |
949 | 0 | depth--; |
950 | 0 | } else if(pos[i] == ';') { |
951 | 0 | break; |
952 | 0 | } |
953 | 0 | } |
954 | | |
955 | | /* normal oneline or last line: keeps newline and comments */ |
956 | 0 | if(depth == 0) { |
957 | 0 | return 1; |
958 | 0 | } |
959 | | |
960 | | /* more lines expected, snip off comments and newline */ |
961 | 0 | if(poslen>0) |
962 | 0 | pos[poslen-1] = 0; /* strip newline */ |
963 | 0 | if(strchr(pos, ';')) |
964 | 0 | strchr(pos, ';')[0] = 0; /* strip comments */ |
965 | | |
966 | | /* move to paste other lines behind this one */ |
967 | 0 | poslen = strlen(pos); |
968 | 0 | pos += poslen; |
969 | 0 | left -= poslen; |
970 | | /* the newline is changed into a space */ |
971 | 0 | if(left <= 2 /* space and eos */) { |
972 | 0 | log_err("line too long"); |
973 | 0 | return -1; |
974 | 0 | } |
975 | 0 | pos[0] = ' '; |
976 | 0 | pos[1] = 0; |
977 | 0 | pos += 1; |
978 | 0 | left -= 1; |
979 | 0 | } |
980 | 0 | if(depth != 0) { |
981 | 0 | log_err("mismatch: too many '('"); |
982 | 0 | return -1; |
983 | 0 | } |
984 | 0 | if(pos != buf) |
985 | 0 | return 1; |
986 | 0 | return 0; |
987 | 0 | } |
988 | | |
989 | | int autr_read_file(struct val_anchors* anchors, const char* nm) |
990 | 0 | { |
991 | | /* the file descriptor */ |
992 | 0 | FILE* fd; |
993 | | /* keep track of line numbers */ |
994 | 0 | int line_nr = 0; |
995 | | /* single line */ |
996 | 0 | char line[10240]; |
997 | | /* trust point being read */ |
998 | 0 | struct trust_anchor *tp = NULL, *tp2; |
999 | 0 | int r; |
1000 | | /* for $ORIGIN parsing */ |
1001 | 0 | uint8_t *origin=NULL, *prev=NULL; |
1002 | 0 | size_t origin_len=0, prev_len=0; |
1003 | |
|
1004 | 0 | if (!(fd = fopen(nm, "r"))) { |
1005 | 0 | log_err("unable to open %s for reading: %s", |
1006 | 0 | nm, strerror(errno)); |
1007 | 0 | return 0; |
1008 | 0 | } |
1009 | 0 | verbose(VERB_ALGO, "reading autotrust anchor file %s", nm); |
1010 | 0 | while ( (r=read_multiline(line, sizeof(line), fd, &line_nr)) != 0) { |
1011 | 0 | if(r == -1 || (r = parse_var_line(line, anchors, &tp)) == -1) { |
1012 | 0 | log_err("could not parse auto-trust-anchor-file " |
1013 | 0 | "%s line %d", nm, line_nr); |
1014 | 0 | fclose(fd); |
1015 | 0 | free(origin); |
1016 | 0 | free(prev); |
1017 | 0 | return 0; |
1018 | 0 | } else if(r == 1) { |
1019 | 0 | continue; |
1020 | 0 | } else if(r == 2) { |
1021 | 0 | log_warn("trust anchor %s has been revoked", nm); |
1022 | 0 | fclose(fd); |
1023 | 0 | free(origin); |
1024 | 0 | free(prev); |
1025 | 0 | return 1; |
1026 | 0 | } |
1027 | 0 | if (!str_contains_data(line, ';')) |
1028 | 0 | continue; /* empty lines allowed */ |
1029 | 0 | if(handle_origin(line, &origin, &origin_len)) |
1030 | 0 | continue; |
1031 | 0 | r = 0; |
1032 | 0 | if(!(tp2=load_trustanchor(anchors, line, nm, origin, |
1033 | 0 | origin_len, &prev, &prev_len, &r))) { |
1034 | 0 | if(!r) log_err("failed to load trust anchor from %s " |
1035 | 0 | "at line %i, skipping", nm, line_nr); |
1036 | | /* try to do the rest */ |
1037 | 0 | continue; |
1038 | 0 | } |
1039 | 0 | if(tp && tp != tp2) { |
1040 | 0 | log_err("file %s has mismatching data inside: " |
1041 | 0 | "the file may only contain keys for one name, " |
1042 | 0 | "remove keys for other domain names", nm); |
1043 | 0 | fclose(fd); |
1044 | 0 | free(origin); |
1045 | 0 | free(prev); |
1046 | 0 | return 0; |
1047 | 0 | } |
1048 | 0 | tp = tp2; |
1049 | 0 | } |
1050 | 0 | fclose(fd); |
1051 | 0 | free(origin); |
1052 | 0 | free(prev); |
1053 | 0 | if(!tp) { |
1054 | 0 | log_err("failed to read %s", nm); |
1055 | 0 | return 0; |
1056 | 0 | } |
1057 | | |
1058 | | /* now assemble the data into DNSKEY and DS packed rrsets */ |
1059 | 0 | lock_basic_lock(&tp->lock); |
1060 | 0 | if(!autr_assemble(tp)) { |
1061 | 0 | lock_basic_unlock(&tp->lock); |
1062 | 0 | log_err("malloc failure assembling %s", nm); |
1063 | 0 | return 0; |
1064 | 0 | } |
1065 | 0 | lock_basic_unlock(&tp->lock); |
1066 | 0 | return 1; |
1067 | 0 | } |
1068 | | |
1069 | | /** string for a trustanchor state */ |
1070 | | static const char* |
1071 | | trustanchor_state2str(autr_state_type s) |
1072 | 0 | { |
1073 | 0 | switch (s) { |
1074 | 0 | case AUTR_STATE_START: return " START "; |
1075 | 0 | case AUTR_STATE_ADDPEND: return " ADDPEND "; |
1076 | 0 | case AUTR_STATE_VALID: return " VALID "; |
1077 | 0 | case AUTR_STATE_MISSING: return " MISSING "; |
1078 | 0 | case AUTR_STATE_REVOKED: return " REVOKED "; |
1079 | 0 | case AUTR_STATE_REMOVED: return " REMOVED "; |
1080 | 0 | } |
1081 | 0 | return " UNKNOWN "; |
1082 | 0 | } |
1083 | | |
1084 | | /** ctime r for autotrust */ |
1085 | | static char* autr_ctime_r(time_t* t, char* s) |
1086 | 0 | { |
1087 | 0 | ctime_r(t, s); |
1088 | | #ifdef USE_WINSOCK |
1089 | | if(strlen(s) > 10 && s[7]==' ' && s[8]=='0') |
1090 | | s[8]=' '; /* fix error in windows ctime */ |
1091 | | #endif |
1092 | 0 | return s; |
1093 | 0 | } |
1094 | | |
1095 | | /** print ID to file */ |
1096 | | static int |
1097 | | print_id(FILE* out, char* fname, uint8_t* nm, size_t nmlen, uint16_t dclass) |
1098 | 0 | { |
1099 | 0 | char* s = sldns_wire2str_dname(nm, nmlen); |
1100 | 0 | if(!s) { |
1101 | 0 | log_err("malloc failure in write to %s", fname); |
1102 | 0 | return 0; |
1103 | 0 | } |
1104 | 0 | if(fprintf(out, ";;id: %s %d\n", s, (int)dclass) < 0) { |
1105 | 0 | log_err("could not write to %s: %s", fname, strerror(errno)); |
1106 | 0 | free(s); |
1107 | 0 | return 0; |
1108 | 0 | } |
1109 | 0 | free(s); |
1110 | 0 | return 1; |
1111 | 0 | } |
1112 | | |
1113 | | static int |
1114 | | autr_write_contents(FILE* out, char* fn, struct trust_anchor* tp) |
1115 | 0 | { |
1116 | 0 | char tmi[32]; |
1117 | 0 | struct autr_ta* ta; |
1118 | 0 | char* str; |
1119 | | |
1120 | | /* write pretty header */ |
1121 | 0 | if(fprintf(out, "; autotrust trust anchor file\n") < 0) { |
1122 | 0 | log_err("could not write to %s: %s", fn, strerror(errno)); |
1123 | 0 | return 0; |
1124 | 0 | } |
1125 | 0 | if(tp->autr->revoked) { |
1126 | 0 | if(fprintf(out, ";;REVOKED\n") < 0 || |
1127 | 0 | fprintf(out, "; The zone has all keys revoked, and is\n" |
1128 | 0 | "; considered as if it has no trust anchors.\n" |
1129 | 0 | "; the remainder of the file is the last probe.\n" |
1130 | 0 | "; to restart the trust anchor, overwrite this file.\n" |
1131 | 0 | "; with one containing valid DNSKEYs or DSes.\n") < 0) { |
1132 | 0 | log_err("could not write to %s: %s", fn, strerror(errno)); |
1133 | 0 | return 0; |
1134 | 0 | } |
1135 | 0 | } |
1136 | 0 | if(!print_id(out, fn, tp->name, tp->namelen, tp->dclass)) { |
1137 | 0 | return 0; |
1138 | 0 | } |
1139 | 0 | if(fprintf(out, ";;last_queried: %u ;;%s", |
1140 | 0 | (unsigned int)tp->autr->last_queried, |
1141 | 0 | autr_ctime_r(&(tp->autr->last_queried), tmi)) < 0 || |
1142 | 0 | fprintf(out, ";;last_success: %u ;;%s", |
1143 | 0 | (unsigned int)tp->autr->last_success, |
1144 | 0 | autr_ctime_r(&(tp->autr->last_success), tmi)) < 0 || |
1145 | 0 | fprintf(out, ";;next_probe_time: %u ;;%s", |
1146 | 0 | (unsigned int)tp->autr->next_probe_time, |
1147 | 0 | autr_ctime_r(&(tp->autr->next_probe_time), tmi)) < 0 || |
1148 | 0 | fprintf(out, ";;query_failed: %d\n", (int)tp->autr->query_failed)<0 |
1149 | 0 | || fprintf(out, ";;query_interval: %d\n", |
1150 | 0 | (int)tp->autr->query_interval) < 0 || |
1151 | 0 | fprintf(out, ";;retry_time: %d\n", (int)tp->autr->retry_time) < 0) { |
1152 | 0 | log_err("could not write to %s: %s", fn, strerror(errno)); |
1153 | 0 | return 0; |
1154 | 0 | } |
1155 | | |
1156 | | /* write anchors */ |
1157 | 0 | for(ta=tp->autr->keys; ta; ta=ta->next) { |
1158 | | /* by default do not store START and REMOVED keys */ |
1159 | 0 | if(ta->s == AUTR_STATE_START) |
1160 | 0 | continue; |
1161 | 0 | if(ta->s == AUTR_STATE_REMOVED) |
1162 | 0 | continue; |
1163 | | /* only store keys */ |
1164 | 0 | if(sldns_wirerr_get_type(ta->rr, ta->rr_len, ta->dname_len) |
1165 | 0 | != LDNS_RR_TYPE_DNSKEY) |
1166 | 0 | continue; |
1167 | 0 | str = sldns_wire2str_rr(ta->rr, ta->rr_len); |
1168 | 0 | if(!str || !str[0]) { |
1169 | 0 | free(str); |
1170 | 0 | log_err("malloc failure writing %s", fn); |
1171 | 0 | return 0; |
1172 | 0 | } |
1173 | 0 | str[strlen(str)-1] = 0; /* remove newline */ |
1174 | 0 | if(fprintf(out, "%s ;;state=%d [%s] ;;count=%d " |
1175 | 0 | ";;lastchange=%u ;;%s", str, (int)ta->s, |
1176 | 0 | trustanchor_state2str(ta->s), (int)ta->pending_count, |
1177 | 0 | (unsigned int)ta->last_change, |
1178 | 0 | autr_ctime_r(&(ta->last_change), tmi)) < 0) { |
1179 | 0 | log_err("could not write to %s: %s", fn, strerror(errno)); |
1180 | 0 | free(str); |
1181 | 0 | return 0; |
1182 | 0 | } |
1183 | 0 | free(str); |
1184 | 0 | } |
1185 | 0 | return 1; |
1186 | 0 | } |
1187 | | |
1188 | | void autr_write_file(struct module_env* env, struct trust_anchor* tp) |
1189 | 0 | { |
1190 | 0 | FILE* out; |
1191 | 0 | char* fname = tp->autr->file; |
1192 | 0 | #ifndef S_SPLINT_S |
1193 | 0 | long long llvalue; |
1194 | 0 | #endif |
1195 | 0 | char tempf[2048]; |
1196 | 0 | log_assert(tp->autr); |
1197 | 0 | if(!env) { |
1198 | 0 | log_err("autr_write_file: Module environment is NULL."); |
1199 | 0 | return; |
1200 | 0 | } |
1201 | | /* unique name with pid number, thread number, and struct pointer |
1202 | | * (the pointer uniquifies for multiple libunbound contexts) */ |
1203 | 0 | #ifndef S_SPLINT_S |
1204 | | #if defined(SIZE_MAX) && defined(UINT32_MAX) && (UINT32_MAX == SIZE_MAX || INT32_MAX == SIZE_MAX) |
1205 | | /* avoid warning about upcast on 32bit systems */ |
1206 | | llvalue = (unsigned long)tp; |
1207 | | #else |
1208 | 0 | llvalue = (unsigned long long)tp; |
1209 | 0 | #endif |
1210 | 0 | snprintf(tempf, sizeof(tempf), "%s.%d-%d-" ARG_LL "x", fname, (int)getpid(), |
1211 | 0 | env->worker?*(int*)env->worker:0, llvalue); |
1212 | 0 | #endif /* S_SPLINT_S */ |
1213 | 0 | verbose(VERB_ALGO, "autotrust: write to disk: %s", tempf); |
1214 | 0 | out = fopen(tempf, "w"); |
1215 | 0 | if(!out) { |
1216 | 0 | fatal_exit("could not open autotrust file for writing, %s: %s", |
1217 | 0 | tempf, strerror(errno)); |
1218 | 0 | return; |
1219 | 0 | } |
1220 | 0 | if(!autr_write_contents(out, tempf, tp)) { |
1221 | | /* failed to write contents (completely) */ |
1222 | 0 | fclose(out); |
1223 | 0 | unlink(tempf); |
1224 | 0 | fatal_exit("could not completely write: %s", fname); |
1225 | 0 | return; |
1226 | 0 | } |
1227 | 0 | if(fflush(out) != 0) |
1228 | 0 | log_err("could not fflush(%s): %s", fname, strerror(errno)); |
1229 | 0 | #ifdef HAVE_FSYNC |
1230 | 0 | if(fsync(fileno(out)) != 0) |
1231 | 0 | log_err("could not fsync(%s): %s", fname, strerror(errno)); |
1232 | | #else |
1233 | | FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); |
1234 | | #endif |
1235 | 0 | if(fclose(out) != 0) { |
1236 | 0 | fatal_exit("could not complete write: %s: %s", |
1237 | 0 | fname, strerror(errno)); |
1238 | 0 | unlink(tempf); |
1239 | 0 | return; |
1240 | 0 | } |
1241 | | /* success; overwrite actual file */ |
1242 | 0 | verbose(VERB_ALGO, "autotrust: replaced %s", fname); |
1243 | | #ifdef UB_ON_WINDOWS |
1244 | | (void)unlink(fname); /* windows does not replace file with rename() */ |
1245 | | #endif |
1246 | 0 | if(rename(tempf, fname) < 0) { |
1247 | 0 | fatal_exit("rename(%s to %s): %s", tempf, fname, strerror(errno)); |
1248 | 0 | } |
1249 | 0 | } |
1250 | | |
1251 | | /** |
1252 | | * Verify if dnskey works for trust point |
1253 | | * @param env: environment (with time) for verification |
1254 | | * @param ve: validator environment (with options) for verification. |
1255 | | * @param tp: trust point to verify with |
1256 | | * @param rrset: DNSKEY rrset to verify. |
1257 | | * @param qstate: qstate with region. |
1258 | | * @return false on failure, true if verification successful. |
1259 | | */ |
1260 | | static int |
1261 | | verify_dnskey(struct module_env* env, struct val_env* ve, |
1262 | | struct trust_anchor* tp, struct ub_packed_rrset_key* rrset, |
1263 | | struct module_qstate* qstate) |
1264 | 0 | { |
1265 | 0 | char reasonbuf[256]; |
1266 | 0 | char* reason = NULL; |
1267 | 0 | uint8_t sigalg[ALGO_NEEDS_MAX+1]; |
1268 | 0 | int downprot = env->cfg->harden_algo_downgrade; |
1269 | 0 | enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset, |
1270 | 0 | tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason, |
1271 | 0 | NULL, qstate, reasonbuf, sizeof(reasonbuf)); |
1272 | | /* sigalg is ignored, it returns algorithms signalled to exist, but |
1273 | | * in 5011 there are no other rrsets to check. if downprot is |
1274 | | * enabled, then it checks that the DNSKEY is signed with all |
1275 | | * algorithms available in the trust store. */ |
1276 | 0 | verbose(VERB_ALGO, "autotrust: validate DNSKEY with anchor: %s", |
1277 | 0 | sec_status_to_string(sec)); |
1278 | 0 | return sec == sec_status_secure; |
1279 | 0 | } |
1280 | | |
1281 | | static int32_t |
1282 | | rrsig_get_expiry(uint8_t* d, size_t len) |
1283 | 0 | { |
1284 | | /* rrsig: 2(rdlen), 2(type) 1(alg) 1(v) 4(origttl), then 4(expi), (4)incep) */ |
1285 | 0 | if(len < 2+8+4) |
1286 | 0 | return 0; |
1287 | 0 | return sldns_read_uint32(d+2+8); |
1288 | 0 | } |
1289 | | |
1290 | | /** Find minimum expiration interval from signatures */ |
1291 | | static time_t |
1292 | | min_expiry(struct module_env* env, struct packed_rrset_data* dd) |
1293 | 0 | { |
1294 | 0 | size_t i; |
1295 | 0 | int32_t t, r = 15 * 24 * 3600; /* 15 days max */ |
1296 | 0 | for(i=dd->count; i<dd->count+dd->rrsig_count; i++) { |
1297 | 0 | t = rrsig_get_expiry(dd->rr_data[i], dd->rr_len[i]); |
1298 | 0 | if((int32_t)t - (int32_t)*env->now > 0) { |
1299 | 0 | t -= (int32_t)*env->now; |
1300 | 0 | if(t < r) |
1301 | 0 | r = t; |
1302 | 0 | } |
1303 | 0 | } |
1304 | 0 | return (time_t)r; |
1305 | 0 | } |
1306 | | |
1307 | | /** Is rr self-signed revoked key */ |
1308 | | static int |
1309 | | rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, |
1310 | | struct ub_packed_rrset_key* dnskey_rrset, size_t i, |
1311 | | struct module_qstate* qstate) |
1312 | 0 | { |
1313 | 0 | enum sec_status sec; |
1314 | 0 | char* reason = NULL; |
1315 | 0 | verbose(VERB_ALGO, "seen REVOKE flag, check self-signed, rr %d", |
1316 | 0 | (int)i); |
1317 | | /* no algorithm downgrade protection necessary, if it is selfsigned |
1318 | | * revoked it can be removed. */ |
1319 | 0 | sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i, |
1320 | 0 | &reason, NULL, LDNS_SECTION_ANSWER, qstate); |
1321 | 0 | return (sec == sec_status_secure); |
1322 | 0 | } |
1323 | | |
1324 | | /** Set fetched value */ |
1325 | | static void |
1326 | | seen_trustanchor(struct autr_ta* ta, uint8_t seen) |
1327 | 0 | { |
1328 | 0 | ta->fetched = seen; |
1329 | 0 | if(ta->pending_count < 250) /* no numerical overflow, please */ |
1330 | 0 | ta->pending_count++; |
1331 | 0 | } |
1332 | | |
1333 | | /** set revoked value */ |
1334 | | static void |
1335 | | seen_revoked_trustanchor(struct autr_ta* ta, uint8_t revoked) |
1336 | 0 | { |
1337 | 0 | ta->revoked = revoked; |
1338 | 0 | } |
1339 | | |
1340 | | /** revoke a trust anchor */ |
1341 | | static void |
1342 | | revoke_dnskey(struct autr_ta* ta, int off) |
1343 | 0 | { |
1344 | 0 | uint16_t flags; |
1345 | 0 | uint8_t* data; |
1346 | 0 | if(sldns_wirerr_get_type(ta->rr, ta->rr_len, ta->dname_len) != |
1347 | 0 | LDNS_RR_TYPE_DNSKEY) |
1348 | 0 | return; |
1349 | 0 | if(sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, ta->dname_len) < 2) |
1350 | 0 | return; |
1351 | 0 | data = sldns_wirerr_get_rdata(ta->rr, ta->rr_len, ta->dname_len); |
1352 | 0 | flags = sldns_read_uint16(data); |
1353 | 0 | if (off && (flags&LDNS_KEY_REVOKE_KEY)) |
1354 | 0 | flags ^= LDNS_KEY_REVOKE_KEY; /* flip */ |
1355 | 0 | else |
1356 | 0 | flags |= LDNS_KEY_REVOKE_KEY; |
1357 | 0 | sldns_write_uint16(data, flags); |
1358 | 0 | } |
1359 | | |
1360 | | /** Compare two RRs skipping the REVOKED bit. Pass rdata(no len) */ |
1361 | | static int |
1362 | | dnskey_compare_skip_revbit(uint8_t* a, size_t a_len, uint8_t* b, size_t b_len) |
1363 | 0 | { |
1364 | 0 | size_t i; |
1365 | 0 | if(a_len != b_len) |
1366 | 0 | return -1; |
1367 | | /* compare RRs RDATA byte for byte. */ |
1368 | 0 | for(i = 0; i < a_len; i++) |
1369 | 0 | { |
1370 | 0 | uint8_t rdf1, rdf2; |
1371 | 0 | rdf1 = a[i]; |
1372 | 0 | rdf2 = b[i]; |
1373 | 0 | if(i==1) { |
1374 | | /* this is the second part of the flags field */ |
1375 | 0 | rdf1 |= LDNS_KEY_REVOKE_KEY; |
1376 | 0 | rdf2 |= LDNS_KEY_REVOKE_KEY; |
1377 | 0 | } |
1378 | 0 | if (rdf1 < rdf2) return -1; |
1379 | 0 | else if (rdf1 > rdf2) return 1; |
1380 | 0 | } |
1381 | 0 | return 0; |
1382 | 0 | } |
1383 | | |
1384 | | |
1385 | | /** compare trust anchor with rdata, 0 if equal. Pass rdata(no len) */ |
1386 | | static int |
1387 | | ta_compare(struct autr_ta* a, uint16_t t, uint8_t* b, size_t b_len) |
1388 | 0 | { |
1389 | 0 | if(!a) return -1; |
1390 | 0 | else if(!b) return -1; |
1391 | 0 | else if(sldns_wirerr_get_type(a->rr, a->rr_len, a->dname_len) != t) |
1392 | 0 | return (int)sldns_wirerr_get_type(a->rr, a->rr_len, |
1393 | 0 | a->dname_len) - (int)t; |
1394 | 0 | else if(t == LDNS_RR_TYPE_DNSKEY) { |
1395 | 0 | return dnskey_compare_skip_revbit( |
1396 | 0 | sldns_wirerr_get_rdata(a->rr, a->rr_len, a->dname_len), |
1397 | 0 | sldns_wirerr_get_rdatalen(a->rr, a->rr_len, |
1398 | 0 | a->dname_len), b, b_len); |
1399 | 0 | } |
1400 | 0 | else if(t == LDNS_RR_TYPE_DS) { |
1401 | 0 | if(sldns_wirerr_get_rdatalen(a->rr, a->rr_len, a->dname_len) != |
1402 | 0 | b_len) |
1403 | 0 | return -1; |
1404 | 0 | return memcmp(sldns_wirerr_get_rdata(a->rr, |
1405 | 0 | a->rr_len, a->dname_len), b, b_len); |
1406 | 0 | } |
1407 | 0 | return -1; |
1408 | 0 | } |
1409 | | |
1410 | | /** |
1411 | | * Find key |
1412 | | * @param tp: to search in |
1413 | | * @param t: rr type of the rdata. |
1414 | | * @param rdata: to look for (no rdatalen in it) |
1415 | | * @param rdata_len: length of rdata |
1416 | | * @param result: returns NULL or the ta key looked for. |
1417 | | * @return false on malloc failure during search. if true examine result. |
1418 | | */ |
1419 | | static int |
1420 | | find_key(struct trust_anchor* tp, uint16_t t, uint8_t* rdata, size_t rdata_len, |
1421 | | struct autr_ta** result) |
1422 | 0 | { |
1423 | 0 | struct autr_ta* ta; |
1424 | 0 | if(!tp || !rdata) { |
1425 | 0 | *result = NULL; |
1426 | 0 | return 0; |
1427 | 0 | } |
1428 | 0 | for(ta=tp->autr->keys; ta; ta=ta->next) { |
1429 | 0 | if(ta_compare(ta, t, rdata, rdata_len) == 0) { |
1430 | 0 | *result = ta; |
1431 | 0 | return 1; |
1432 | 0 | } |
1433 | 0 | } |
1434 | 0 | *result = NULL; |
1435 | 0 | return 1; |
1436 | 0 | } |
1437 | | |
1438 | | /** add key and clone RR and tp already locked. rdata without rdlen. */ |
1439 | | static struct autr_ta* |
1440 | | add_key(struct trust_anchor* tp, uint32_t ttl, uint8_t* rdata, size_t rdata_len) |
1441 | 0 | { |
1442 | 0 | struct autr_ta* ta; |
1443 | 0 | uint8_t* rr; |
1444 | 0 | size_t rr_len, dname_len; |
1445 | 0 | uint16_t rrtype = htons(LDNS_RR_TYPE_DNSKEY); |
1446 | 0 | uint16_t rrclass = htons(LDNS_RR_CLASS_IN); |
1447 | 0 | uint16_t rdlen = htons(rdata_len); |
1448 | 0 | dname_len = tp->namelen; |
1449 | 0 | ttl = htonl(ttl); |
1450 | 0 | rr_len = dname_len + 10 /* type,class,ttl,rdatalen */ + rdata_len; |
1451 | 0 | rr = (uint8_t*)malloc(rr_len); |
1452 | 0 | if(!rr) return NULL; |
1453 | 0 | memmove(rr, tp->name, tp->namelen); |
1454 | 0 | memmove(rr+dname_len, &rrtype, 2); |
1455 | 0 | memmove(rr+dname_len+2, &rrclass, 2); |
1456 | 0 | memmove(rr+dname_len+4, &ttl, 4); |
1457 | 0 | memmove(rr+dname_len+8, &rdlen, 2); |
1458 | 0 | memmove(rr+dname_len+10, rdata, rdata_len); |
1459 | 0 | ta = autr_ta_create(rr, rr_len, dname_len); |
1460 | 0 | if(!ta) { |
1461 | | /* rr freed in autr_ta_create */ |
1462 | 0 | return NULL; |
1463 | 0 | } |
1464 | | /* link in, tp already locked */ |
1465 | 0 | ta->next = tp->autr->keys; |
1466 | 0 | tp->autr->keys = ta; |
1467 | 0 | return ta; |
1468 | 0 | } |
1469 | | |
1470 | | /** get TTL from DNSKEY rrset */ |
1471 | | static time_t |
1472 | | key_ttl(struct ub_packed_rrset_key* k) |
1473 | 0 | { |
1474 | 0 | struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; |
1475 | 0 | return d->ttl; |
1476 | 0 | } |
1477 | | |
1478 | | /** update the time values for the trustpoint */ |
1479 | | static void |
1480 | | set_tp_times(struct trust_anchor* tp, time_t rrsig_exp_interval, |
1481 | | time_t origttl, int* changed) |
1482 | 0 | { |
1483 | 0 | time_t x, qi = tp->autr->query_interval, rt = tp->autr->retry_time; |
1484 | | |
1485 | | /* x = MIN(15days, ttl/2, expire/2) */ |
1486 | 0 | x = 15 * 24 * 3600; |
1487 | 0 | if(origttl/2 < x) |
1488 | 0 | x = origttl/2; |
1489 | 0 | if(rrsig_exp_interval/2 < x) |
1490 | 0 | x = rrsig_exp_interval/2; |
1491 | | /* MAX(1hr, x) */ |
1492 | 0 | if(!autr_permit_small_holddown) { |
1493 | 0 | if(x < 3600) |
1494 | 0 | tp->autr->query_interval = 3600; |
1495 | 0 | else tp->autr->query_interval = x; |
1496 | 0 | } else tp->autr->query_interval = x; |
1497 | | |
1498 | | /* x= MIN(1day, ttl/10, expire/10) */ |
1499 | 0 | x = 24 * 3600; |
1500 | 0 | if(origttl/10 < x) |
1501 | 0 | x = origttl/10; |
1502 | 0 | if(rrsig_exp_interval/10 < x) |
1503 | 0 | x = rrsig_exp_interval/10; |
1504 | | /* MAX(1hr, x) */ |
1505 | 0 | if(!autr_permit_small_holddown) { |
1506 | 0 | if(x < 3600) |
1507 | 0 | tp->autr->retry_time = 3600; |
1508 | 0 | else tp->autr->retry_time = x; |
1509 | 0 | } else tp->autr->retry_time = x; |
1510 | |
|
1511 | 0 | if(qi != tp->autr->query_interval || rt != tp->autr->retry_time) { |
1512 | 0 | *changed = 1; |
1513 | 0 | verbose(VERB_ALGO, "orig_ttl is %d", (int)origttl); |
1514 | 0 | verbose(VERB_ALGO, "rrsig_exp_interval is %d", |
1515 | 0 | (int)rrsig_exp_interval); |
1516 | 0 | verbose(VERB_ALGO, "query_interval: %d, retry_time: %d", |
1517 | 0 | (int)tp->autr->query_interval, |
1518 | 0 | (int)tp->autr->retry_time); |
1519 | 0 | } |
1520 | 0 | } |
1521 | | |
1522 | | /** init events to zero */ |
1523 | | static void |
1524 | | init_events(struct trust_anchor* tp) |
1525 | 0 | { |
1526 | 0 | struct autr_ta* ta; |
1527 | 0 | for(ta=tp->autr->keys; ta; ta=ta->next) { |
1528 | 0 | ta->fetched = 0; |
1529 | 0 | } |
1530 | 0 | } |
1531 | | |
1532 | | /** check for revoked keys without trusting any other information */ |
1533 | | static void |
1534 | | check_contains_revoked(struct module_env* env, struct val_env* ve, |
1535 | | struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, |
1536 | | int* changed, struct module_qstate* qstate) |
1537 | 0 | { |
1538 | 0 | struct packed_rrset_data* dd = (struct packed_rrset_data*) |
1539 | 0 | dnskey_rrset->entry.data; |
1540 | 0 | size_t i; |
1541 | 0 | log_assert(ntohs(dnskey_rrset->rk.type) == LDNS_RR_TYPE_DNSKEY); |
1542 | 0 | for(i=0; i<dd->count; i++) { |
1543 | 0 | struct autr_ta* ta = NULL; |
1544 | 0 | if(!rr_is_dnskey_sep(ntohs(dnskey_rrset->rk.type), |
1545 | 0 | dd->rr_data[i]+2, dd->rr_len[i]-2) || |
1546 | 0 | !rr_is_dnskey_revoked(ntohs(dnskey_rrset->rk.type), |
1547 | 0 | dd->rr_data[i]+2, dd->rr_len[i]-2)) |
1548 | 0 | continue; /* not a revoked KSK */ |
1549 | 0 | if(!find_key(tp, ntohs(dnskey_rrset->rk.type), |
1550 | 0 | dd->rr_data[i]+2, dd->rr_len[i]-2, &ta)) { |
1551 | 0 | log_err("malloc failure"); |
1552 | 0 | continue; /* malloc fail in compare*/ |
1553 | 0 | } |
1554 | 0 | if(!ta) |
1555 | 0 | continue; /* key not found */ |
1556 | 0 | if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i, qstate)) { |
1557 | | /* checked if there is an rrsig signed by this key. */ |
1558 | | /* same keytag, but stored can be revoked already, so |
1559 | | * compare keytags, with +0 or +128(REVOKE flag) */ |
1560 | 0 | log_assert(dnskey_calc_keytag(dnskey_rrset, i)-128 == |
1561 | 0 | sldns_calc_keytag_raw(sldns_wirerr_get_rdata( |
1562 | 0 | ta->rr, ta->rr_len, ta->dname_len), |
1563 | 0 | sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, |
1564 | 0 | ta->dname_len)) || |
1565 | 0 | dnskey_calc_keytag(dnskey_rrset, i) == |
1566 | 0 | sldns_calc_keytag_raw(sldns_wirerr_get_rdata( |
1567 | 0 | ta->rr, ta->rr_len, ta->dname_len), |
1568 | 0 | sldns_wirerr_get_rdatalen(ta->rr, ta->rr_len, |
1569 | 0 | ta->dname_len))); /* checks conversion*/ |
1570 | 0 | verbose_key(ta, VERB_ALGO, "is self-signed revoked"); |
1571 | 0 | if(!ta->revoked) |
1572 | 0 | *changed = 1; |
1573 | 0 | seen_revoked_trustanchor(ta, 1); |
1574 | 0 | do_revoked(env, ta, changed); |
1575 | 0 | } |
1576 | 0 | } |
1577 | 0 | } |
1578 | | |
1579 | | /** See if a DNSKEY is verified by one of the DSes */ |
1580 | | static int |
1581 | | key_matches_a_ds(struct module_env* env, struct val_env* ve, |
1582 | | struct ub_packed_rrset_key* dnskey_rrset, size_t key_idx, |
1583 | | struct ub_packed_rrset_key* ds_rrset) |
1584 | 0 | { |
1585 | 0 | struct packed_rrset_data* dd = (struct packed_rrset_data*) |
1586 | 0 | ds_rrset->entry.data; |
1587 | 0 | size_t ds_idx, num = dd->count; |
1588 | 0 | int d = val_favorite_ds_algo(ds_rrset); |
1589 | 0 | char* reason = ""; |
1590 | 0 | for(ds_idx=0; ds_idx<num; ds_idx++) { |
1591 | 0 | if(!ds_digest_algo_is_supported(ds_rrset, ds_idx) || |
1592 | 0 | !ds_key_algo_is_supported(ds_rrset, ds_idx) || |
1593 | 0 | !dnskey_size_is_supported(dnskey_rrset, key_idx) || |
1594 | 0 | ds_get_digest_algo(ds_rrset, ds_idx) != d) |
1595 | 0 | continue; |
1596 | 0 | if(ds_get_key_algo(ds_rrset, ds_idx) |
1597 | 0 | != dnskey_get_algo(dnskey_rrset, key_idx) |
1598 | 0 | || dnskey_calc_keytag(dnskey_rrset, key_idx) |
1599 | 0 | != ds_get_keytag(ds_rrset, ds_idx)) { |
1600 | 0 | continue; |
1601 | 0 | } |
1602 | 0 | if(!ds_digest_match_dnskey(env, dnskey_rrset, key_idx, |
1603 | 0 | ds_rrset, ds_idx)) { |
1604 | 0 | verbose(VERB_ALGO, "DS match attempt failed"); |
1605 | 0 | continue; |
1606 | 0 | } |
1607 | | /* match of hash is sufficient for bootstrap of trust point */ |
1608 | 0 | (void)reason; |
1609 | 0 | (void)ve; |
1610 | 0 | return 1; |
1611 | | /* no need to check RRSIG, DS hash already matched with source |
1612 | | if(dnskey_verify_rrset(env, ve, dnskey_rrset, |
1613 | | dnskey_rrset, key_idx, &reason) == sec_status_secure) { |
1614 | | return 1; |
1615 | | } else { |
1616 | | verbose(VERB_ALGO, "DS match failed because the key " |
1617 | | "does not verify the keyset: %s", reason); |
1618 | | } |
1619 | | */ |
1620 | 0 | } |
1621 | 0 | return 0; |
1622 | 0 | } |
1623 | | |
1624 | | /** Set update events */ |
1625 | | static int |
1626 | | update_events(struct module_env* env, struct val_env* ve, |
1627 | | struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, |
1628 | | int* changed) |
1629 | 0 | { |
1630 | 0 | struct packed_rrset_data* dd = (struct packed_rrset_data*) |
1631 | 0 | dnskey_rrset->entry.data; |
1632 | 0 | size_t i; |
1633 | 0 | log_assert(ntohs(dnskey_rrset->rk.type) == LDNS_RR_TYPE_DNSKEY); |
1634 | 0 | init_events(tp); |
1635 | 0 | for(i=0; i<dd->count; i++) { |
1636 | 0 | struct autr_ta* ta = NULL; |
1637 | 0 | if(!rr_is_dnskey_sep(ntohs(dnskey_rrset->rk.type), |
1638 | 0 | dd->rr_data[i]+2, dd->rr_len[i]-2)) |
1639 | 0 | continue; |
1640 | 0 | if(rr_is_dnskey_revoked(ntohs(dnskey_rrset->rk.type), |
1641 | 0 | dd->rr_data[i]+2, dd->rr_len[i]-2)) { |
1642 | | /* self-signed revoked keys already detected before, |
1643 | | * other revoked keys are not 'added' again */ |
1644 | 0 | continue; |
1645 | 0 | } |
1646 | | /* is a key of this type supported?. Note rr_list and |
1647 | | * packed_rrset are in the same order. */ |
1648 | 0 | if(!dnskey_algo_is_supported(dnskey_rrset, i) || |
1649 | 0 | !dnskey_size_is_supported(dnskey_rrset, i)) { |
1650 | | /* skip unknown algorithm key, it is useless to us */ |
1651 | 0 | log_nametypeclass(VERB_DETAIL, "trust point has " |
1652 | 0 | "unsupported algorithm at", |
1653 | 0 | tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass); |
1654 | 0 | continue; |
1655 | 0 | } |
1656 | | |
1657 | | /* is it new? if revocation bit set, find the unrevoked key */ |
1658 | 0 | if(!find_key(tp, ntohs(dnskey_rrset->rk.type), |
1659 | 0 | dd->rr_data[i]+2, dd->rr_len[i]-2, &ta)) { |
1660 | 0 | return 0; |
1661 | 0 | } |
1662 | 0 | if(!ta) { |
1663 | 0 | ta = add_key(tp, (uint32_t)dd->rr_ttl[i], |
1664 | 0 | dd->rr_data[i]+2, dd->rr_len[i]-2); |
1665 | 0 | *changed = 1; |
1666 | | /* first time seen, do we have DSes? if match: VALID */ |
1667 | 0 | if(ta && tp->ds_rrset && key_matches_a_ds(env, ve, |
1668 | 0 | dnskey_rrset, i, tp->ds_rrset)) { |
1669 | 0 | verbose_key(ta, VERB_ALGO, "verified by DS"); |
1670 | 0 | ta->s = AUTR_STATE_VALID; |
1671 | 0 | } |
1672 | 0 | } |
1673 | 0 | if(!ta) { |
1674 | 0 | return 0; |
1675 | 0 | } |
1676 | 0 | seen_trustanchor(ta, 1); |
1677 | 0 | verbose_key(ta, VERB_ALGO, "in DNS response"); |
1678 | 0 | } |
1679 | 0 | set_tp_times(tp, min_expiry(env, dd), key_ttl(dnskey_rrset), changed); |
1680 | 0 | return 1; |
1681 | 0 | } |
1682 | | |
1683 | | /** |
1684 | | * Check if the holddown time has already exceeded |
1685 | | * setting: add-holddown: add holddown timer |
1686 | | * setting: del-holddown: del holddown timer |
1687 | | * @param env: environment with current time |
1688 | | * @param ta: trust anchor to check for. |
1689 | | * @param holddown: the timer value |
1690 | | * @return number of seconds the holddown has passed. |
1691 | | */ |
1692 | | static time_t |
1693 | | check_holddown(struct module_env* env, struct autr_ta* ta, |
1694 | | unsigned int holddown) |
1695 | 0 | { |
1696 | 0 | time_t elapsed; |
1697 | 0 | if(*env->now < ta->last_change) { |
1698 | 0 | log_warn("time goes backwards. delaying key holddown"); |
1699 | 0 | return 0; |
1700 | 0 | } |
1701 | 0 | elapsed = *env->now - ta->last_change; |
1702 | 0 | if (elapsed > (time_t)holddown) { |
1703 | 0 | return elapsed-(time_t)holddown; |
1704 | 0 | } |
1705 | 0 | verbose_key(ta, VERB_ALGO, "holddown time " ARG_LL "d seconds to go", |
1706 | 0 | (long long) ((time_t)holddown-elapsed)); |
1707 | 0 | return 0; |
1708 | 0 | } |
1709 | | |
1710 | | |
1711 | | /** Set last_change to now */ |
1712 | | static void |
1713 | | reset_holddown(struct module_env* env, struct autr_ta* ta, int* changed) |
1714 | 0 | { |
1715 | 0 | ta->last_change = *env->now; |
1716 | 0 | *changed = 1; |
1717 | 0 | } |
1718 | | |
1719 | | /** Set the state for this trust anchor */ |
1720 | | static void |
1721 | | set_trustanchor_state(struct module_env* env, struct autr_ta* ta, int* changed, |
1722 | | autr_state_type s) |
1723 | 0 | { |
1724 | 0 | verbose_key(ta, VERB_ALGO, "update: %s to %s", |
1725 | 0 | trustanchor_state2str(ta->s), trustanchor_state2str(s)); |
1726 | 0 | ta->s = s; |
1727 | 0 | reset_holddown(env, ta, changed); |
1728 | 0 | } |
1729 | | |
1730 | | |
1731 | | /** Event: NewKey */ |
1732 | | static void |
1733 | | do_newkey(struct module_env* env, struct autr_ta* anchor, int* c) |
1734 | 0 | { |
1735 | 0 | if (anchor->s == AUTR_STATE_START) |
1736 | 0 | set_trustanchor_state(env, anchor, c, AUTR_STATE_ADDPEND); |
1737 | 0 | } |
1738 | | |
1739 | | /** Event: AddTime */ |
1740 | | static void |
1741 | | do_addtime(struct module_env* env, struct autr_ta* anchor, int* c) |
1742 | 0 | { |
1743 | | /* This not according to RFC, this is 30 days, but the RFC demands |
1744 | | * MAX(30days, TTL expire time of first DNSKEY set with this key), |
1745 | | * The value may be too small if a very large TTL was used. */ |
1746 | 0 | time_t exceeded = check_holddown(env, anchor, env->cfg->add_holddown); |
1747 | 0 | if (exceeded && anchor->s == AUTR_STATE_ADDPEND) { |
1748 | 0 | verbose_key(anchor, VERB_ALGO, "add-holddown time exceeded " |
1749 | 0 | ARG_LL "d seconds ago, and pending-count %d", |
1750 | 0 | (long long)exceeded, anchor->pending_count); |
1751 | 0 | if(anchor->pending_count >= MIN_PENDINGCOUNT) { |
1752 | 0 | set_trustanchor_state(env, anchor, c, AUTR_STATE_VALID); |
1753 | 0 | anchor->pending_count = 0; |
1754 | 0 | return; |
1755 | 0 | } |
1756 | 0 | verbose_key(anchor, VERB_ALGO, "add-holddown time sanity check " |
1757 | 0 | "failed (pending count: %d)", anchor->pending_count); |
1758 | 0 | } |
1759 | 0 | } |
1760 | | |
1761 | | /** Event: RemTime */ |
1762 | | static void |
1763 | | do_remtime(struct module_env* env, struct autr_ta* anchor, int* c) |
1764 | 0 | { |
1765 | 0 | time_t exceeded = check_holddown(env, anchor, env->cfg->del_holddown); |
1766 | 0 | if(exceeded && anchor->s == AUTR_STATE_REVOKED) { |
1767 | 0 | verbose_key(anchor, VERB_ALGO, "del-holddown time exceeded " |
1768 | 0 | ARG_LL "d seconds ago", (long long)exceeded); |
1769 | 0 | set_trustanchor_state(env, anchor, c, AUTR_STATE_REMOVED); |
1770 | 0 | } |
1771 | 0 | } |
1772 | | |
1773 | | /** Event: KeyRem */ |
1774 | | static void |
1775 | | do_keyrem(struct module_env* env, struct autr_ta* anchor, int* c) |
1776 | 0 | { |
1777 | 0 | if(anchor->s == AUTR_STATE_ADDPEND) { |
1778 | 0 | set_trustanchor_state(env, anchor, c, AUTR_STATE_START); |
1779 | 0 | anchor->pending_count = 0; |
1780 | 0 | } else if(anchor->s == AUTR_STATE_VALID) |
1781 | 0 | set_trustanchor_state(env, anchor, c, AUTR_STATE_MISSING); |
1782 | 0 | } |
1783 | | |
1784 | | /** Event: KeyPres */ |
1785 | | static void |
1786 | | do_keypres(struct module_env* env, struct autr_ta* anchor, int* c) |
1787 | 0 | { |
1788 | 0 | if(anchor->s == AUTR_STATE_MISSING) |
1789 | 0 | set_trustanchor_state(env, anchor, c, AUTR_STATE_VALID); |
1790 | 0 | } |
1791 | | |
1792 | | /* Event: Revoked */ |
1793 | | static void |
1794 | | do_revoked(struct module_env* env, struct autr_ta* anchor, int* c) |
1795 | 0 | { |
1796 | 0 | if(anchor->s == AUTR_STATE_VALID || anchor->s == AUTR_STATE_MISSING) { |
1797 | 0 | set_trustanchor_state(env, anchor, c, AUTR_STATE_REVOKED); |
1798 | 0 | verbose_key(anchor, VERB_ALGO, "old id, prior to revocation"); |
1799 | 0 | revoke_dnskey(anchor, 0); |
1800 | 0 | verbose_key(anchor, VERB_ALGO, "new id, after revocation"); |
1801 | 0 | } |
1802 | 0 | } |
1803 | | |
1804 | | /** Do statestable transition matrix for anchor */ |
1805 | | static void |
1806 | | anchor_state_update(struct module_env* env, struct autr_ta* anchor, int* c) |
1807 | 0 | { |
1808 | 0 | log_assert(anchor); |
1809 | 0 | switch(anchor->s) { |
1810 | | /* START */ |
1811 | 0 | case AUTR_STATE_START: |
1812 | | /* NewKey: ADDPEND */ |
1813 | 0 | if (anchor->fetched) |
1814 | 0 | do_newkey(env, anchor, c); |
1815 | 0 | break; |
1816 | | /* ADDPEND */ |
1817 | 0 | case AUTR_STATE_ADDPEND: |
1818 | | /* KeyRem: START */ |
1819 | 0 | if (!anchor->fetched) |
1820 | 0 | do_keyrem(env, anchor, c); |
1821 | | /* AddTime: VALID */ |
1822 | 0 | else do_addtime(env, anchor, c); |
1823 | 0 | break; |
1824 | | /* VALID */ |
1825 | 0 | case AUTR_STATE_VALID: |
1826 | | /* RevBit: REVOKED */ |
1827 | 0 | if (anchor->revoked) |
1828 | 0 | do_revoked(env, anchor, c); |
1829 | | /* KeyRem: MISSING */ |
1830 | 0 | else if (!anchor->fetched) |
1831 | 0 | do_keyrem(env, anchor, c); |
1832 | 0 | else if(!anchor->last_change) { |
1833 | 0 | verbose_key(anchor, VERB_ALGO, "first seen"); |
1834 | 0 | reset_holddown(env, anchor, c); |
1835 | 0 | } |
1836 | 0 | break; |
1837 | | /* MISSING */ |
1838 | 0 | case AUTR_STATE_MISSING: |
1839 | | /* RevBit: REVOKED */ |
1840 | 0 | if (anchor->revoked) |
1841 | 0 | do_revoked(env, anchor, c); |
1842 | | /* KeyPres */ |
1843 | 0 | else if (anchor->fetched) |
1844 | 0 | do_keypres(env, anchor, c); |
1845 | 0 | break; |
1846 | | /* REVOKED */ |
1847 | 0 | case AUTR_STATE_REVOKED: |
1848 | 0 | if (anchor->fetched) |
1849 | 0 | reset_holddown(env, anchor, c); |
1850 | | /* RemTime: REMOVED */ |
1851 | 0 | else do_remtime(env, anchor, c); |
1852 | 0 | break; |
1853 | | /* REMOVED */ |
1854 | 0 | case AUTR_STATE_REMOVED: |
1855 | 0 | default: |
1856 | 0 | break; |
1857 | 0 | } |
1858 | 0 | } |
1859 | | |
1860 | | /** if ZSK init then trust KSKs */ |
1861 | | static int |
1862 | | init_zsk_to_ksk(struct module_env* env, struct trust_anchor* tp, int* changed) |
1863 | 0 | { |
1864 | | /* search for VALID ZSKs */ |
1865 | 0 | struct autr_ta* anchor; |
1866 | 0 | int validzsk = 0; |
1867 | 0 | int validksk = 0; |
1868 | 0 | for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { |
1869 | | /* last_change test makes sure it was manually configured */ |
1870 | 0 | if(sldns_wirerr_get_type(anchor->rr, anchor->rr_len, |
1871 | 0 | anchor->dname_len) == LDNS_RR_TYPE_DNSKEY && |
1872 | 0 | anchor->last_change == 0 && |
1873 | 0 | !ta_is_dnskey_sep(anchor) && |
1874 | 0 | anchor->s == AUTR_STATE_VALID) |
1875 | 0 | validzsk++; |
1876 | 0 | } |
1877 | 0 | if(validzsk == 0) |
1878 | 0 | return 0; |
1879 | 0 | for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { |
1880 | 0 | if (ta_is_dnskey_sep(anchor) && |
1881 | 0 | anchor->s == AUTR_STATE_ADDPEND) { |
1882 | 0 | verbose_key(anchor, VERB_ALGO, "trust KSK from " |
1883 | 0 | "ZSK(config)"); |
1884 | 0 | set_trustanchor_state(env, anchor, changed, |
1885 | 0 | AUTR_STATE_VALID); |
1886 | 0 | validksk++; |
1887 | 0 | } |
1888 | 0 | } |
1889 | 0 | return validksk; |
1890 | 0 | } |
1891 | | |
1892 | | /** Remove missing trustanchors so the list does not grow forever */ |
1893 | | static void |
1894 | | remove_missing_trustanchors(struct module_env* env, struct trust_anchor* tp, |
1895 | | int* changed) |
1896 | 0 | { |
1897 | 0 | struct autr_ta* anchor; |
1898 | 0 | time_t exceeded; |
1899 | 0 | int valid = 0; |
1900 | | /* see if we have anchors that are valid */ |
1901 | 0 | for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { |
1902 | | /* Only do KSKs */ |
1903 | 0 | if (!ta_is_dnskey_sep(anchor)) |
1904 | 0 | continue; |
1905 | 0 | if (anchor->s == AUTR_STATE_VALID) |
1906 | 0 | valid++; |
1907 | 0 | } |
1908 | | /* if there are no SEP Valid anchors, see if we started out with |
1909 | | * a ZSK (last-change=0) anchor, which is VALID and there are KSKs |
1910 | | * now that can be made valid. Do this immediately because there |
1911 | | * is no guarantee that the ZSKs get announced long enough. Usually |
1912 | | * this is immediately after init with a ZSK trusted, unless the domain |
1913 | | * was not advertising any KSKs at all. In which case we perfectly |
1914 | | * track the zero number of KSKs. */ |
1915 | 0 | if(valid == 0) { |
1916 | 0 | valid = init_zsk_to_ksk(env, tp, changed); |
1917 | 0 | if(valid == 0) |
1918 | 0 | return; |
1919 | 0 | } |
1920 | | |
1921 | 0 | for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { |
1922 | | /* ignore ZSKs if newly added */ |
1923 | 0 | if(anchor->s == AUTR_STATE_START) |
1924 | 0 | continue; |
1925 | | /* remove ZSKs if a KSK is present */ |
1926 | 0 | if (!ta_is_dnskey_sep(anchor)) { |
1927 | 0 | if(valid > 0) { |
1928 | 0 | verbose_key(anchor, VERB_ALGO, "remove ZSK " |
1929 | 0 | "[%d key(s) VALID]", valid); |
1930 | 0 | set_trustanchor_state(env, anchor, changed, |
1931 | 0 | AUTR_STATE_REMOVED); |
1932 | 0 | } |
1933 | 0 | continue; |
1934 | 0 | } |
1935 | | /* Only do MISSING keys */ |
1936 | 0 | if (anchor->s != AUTR_STATE_MISSING) |
1937 | 0 | continue; |
1938 | 0 | if(env->cfg->keep_missing == 0) |
1939 | 0 | continue; /* keep forever */ |
1940 | | |
1941 | 0 | exceeded = check_holddown(env, anchor, env->cfg->keep_missing); |
1942 | | /* If keep_missing has exceeded and we still have more than |
1943 | | * one valid KSK: remove missing trust anchor */ |
1944 | 0 | if (exceeded && valid > 0) { |
1945 | 0 | verbose_key(anchor, VERB_ALGO, "keep-missing time " |
1946 | 0 | "exceeded " ARG_LL "d seconds ago, [%d key(s) VALID]", |
1947 | 0 | (long long)exceeded, valid); |
1948 | 0 | set_trustanchor_state(env, anchor, changed, |
1949 | 0 | AUTR_STATE_REMOVED); |
1950 | 0 | } |
1951 | 0 | } |
1952 | 0 | } |
1953 | | |
1954 | | /** Do the statetable from RFC5011 transition matrix */ |
1955 | | static int |
1956 | | do_statetable(struct module_env* env, struct trust_anchor* tp, int* changed) |
1957 | 0 | { |
1958 | 0 | struct autr_ta* anchor; |
1959 | 0 | for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { |
1960 | | /* Only do KSKs */ |
1961 | 0 | if(!ta_is_dnskey_sep(anchor)) |
1962 | 0 | continue; |
1963 | 0 | anchor_state_update(env, anchor, changed); |
1964 | 0 | } |
1965 | 0 | remove_missing_trustanchors(env, tp, changed); |
1966 | 0 | return 1; |
1967 | 0 | } |
1968 | | |
1969 | | /** See if time alone makes ADDPEND to VALID transition */ |
1970 | | static void |
1971 | | autr_holddown_exceed(struct module_env* env, struct trust_anchor* tp, int* c) |
1972 | 0 | { |
1973 | 0 | struct autr_ta* anchor; |
1974 | 0 | for(anchor = tp->autr->keys; anchor; anchor = anchor->next) { |
1975 | 0 | if(ta_is_dnskey_sep(anchor) && |
1976 | 0 | anchor->s == AUTR_STATE_ADDPEND) |
1977 | 0 | do_addtime(env, anchor, c); |
1978 | 0 | } |
1979 | 0 | } |
1980 | | |
1981 | | /** cleanup key list */ |
1982 | | static void |
1983 | | autr_cleanup_keys(struct trust_anchor* tp) |
1984 | 0 | { |
1985 | 0 | struct autr_ta* p, **prevp; |
1986 | 0 | prevp = &tp->autr->keys; |
1987 | 0 | p = tp->autr->keys; |
1988 | 0 | while(p) { |
1989 | | /* do we want to remove this key? */ |
1990 | 0 | if(p->s == AUTR_STATE_START || p->s == AUTR_STATE_REMOVED || |
1991 | 0 | sldns_wirerr_get_type(p->rr, p->rr_len, p->dname_len) |
1992 | 0 | != LDNS_RR_TYPE_DNSKEY) { |
1993 | 0 | struct autr_ta* np = p->next; |
1994 | | /* remove */ |
1995 | 0 | free(p->rr); |
1996 | 0 | free(p); |
1997 | | /* snip and go to next item */ |
1998 | 0 | *prevp = np; |
1999 | 0 | p = np; |
2000 | 0 | continue; |
2001 | 0 | } |
2002 | | /* remove pending counts if no longer pending */ |
2003 | 0 | if(p->s != AUTR_STATE_ADDPEND) |
2004 | 0 | p->pending_count = 0; |
2005 | 0 | prevp = &p->next; |
2006 | 0 | p = p->next; |
2007 | 0 | } |
2008 | 0 | } |
2009 | | |
2010 | | /** calculate next probe time */ |
2011 | | static time_t |
2012 | | calc_next_probe(struct module_env* env, time_t wait) |
2013 | 0 | { |
2014 | | /* make it random, 90-100% */ |
2015 | 0 | time_t rnd, rest; |
2016 | 0 | if(!autr_permit_small_holddown) { |
2017 | 0 | if(wait < 3600) |
2018 | 0 | wait = 3600; |
2019 | 0 | } else { |
2020 | 0 | if(wait == 0) wait = 1; |
2021 | 0 | } |
2022 | 0 | rnd = wait/10; |
2023 | 0 | rest = wait-rnd; |
2024 | 0 | rnd = (time_t)ub_random_max(env->rnd, (long int)rnd); |
2025 | 0 | return (time_t)(*env->now + rest + rnd); |
2026 | 0 | } |
2027 | | |
2028 | | /** what is first probe time (anchors must be locked) */ |
2029 | | static time_t |
2030 | | wait_probe_time(struct val_anchors* anchors) |
2031 | 0 | { |
2032 | 0 | rbnode_type* t = rbtree_first(&anchors->autr->probe); |
2033 | 0 | if(t != RBTREE_NULL) |
2034 | 0 | return ((struct trust_anchor*)t->key)->autr->next_probe_time; |
2035 | 0 | return 0; |
2036 | 0 | } |
2037 | | |
2038 | | /** reset worker timer, at the time from wait_probe_time. */ |
2039 | | static void |
2040 | | reset_worker_timer_at(struct module_env* env, time_t next) |
2041 | 0 | { |
2042 | 0 | struct timeval tv; |
2043 | 0 | #ifndef S_SPLINT_S |
2044 | | /* in case this is libunbound, no timer */ |
2045 | 0 | if(!env->probe_timer) |
2046 | 0 | return; |
2047 | 0 | if(next > *env->now) |
2048 | 0 | tv.tv_sec = (time_t)(next - *env->now); |
2049 | 0 | else tv.tv_sec = 0; |
2050 | | #else |
2051 | | (void)next; |
2052 | | #endif |
2053 | 0 | tv.tv_usec = 0; |
2054 | 0 | comm_timer_set(env->probe_timer, &tv); |
2055 | 0 | verbose(VERB_ALGO, "scheduled next probe in " ARG_LL "d sec", (long long)tv.tv_sec); |
2056 | 0 | } |
2057 | | |
2058 | | /** reset worker timer. This routine manages the locks on acquiring the |
2059 | | * next time for the timer. */ |
2060 | | static void |
2061 | | reset_worker_timer(struct module_env* env) |
2062 | 0 | { |
2063 | 0 | time_t next; |
2064 | 0 | if(!env->anchors) |
2065 | 0 | return; |
2066 | 0 | lock_basic_lock(&env->anchors->lock); |
2067 | 0 | next = wait_probe_time(env->anchors); |
2068 | 0 | lock_basic_unlock(&env->anchors->lock); |
2069 | 0 | reset_worker_timer_at(env, next); |
2070 | 0 | } |
2071 | | |
2072 | | /** set next probe for trust anchor */ |
2073 | | static int |
2074 | | set_next_probe(struct module_env* env, struct trust_anchor* tp, |
2075 | | struct ub_packed_rrset_key* dnskey_rrset) |
2076 | 0 | { |
2077 | 0 | struct trust_anchor key, *tp2; |
2078 | 0 | time_t mold, mnew; |
2079 | | /* use memory allocated in rrset for temporary name storage */ |
2080 | 0 | key.node.key = &key; |
2081 | 0 | key.name = dnskey_rrset->rk.dname; |
2082 | 0 | key.namelen = dnskey_rrset->rk.dname_len; |
2083 | 0 | key.namelabs = dname_count_labels(key.name); |
2084 | 0 | key.dclass = tp->dclass; |
2085 | 0 | lock_basic_unlock(&tp->lock); |
2086 | | |
2087 | | /* fetch tp again and lock anchors, so that we can modify the trees */ |
2088 | 0 | lock_basic_lock(&env->anchors->lock); |
2089 | 0 | tp2 = (struct trust_anchor*)rbtree_search(env->anchors->tree, &key); |
2090 | 0 | if(!tp2) { |
2091 | 0 | verbose(VERB_ALGO, "trustpoint was deleted in set_next_probe"); |
2092 | 0 | lock_basic_unlock(&env->anchors->lock); |
2093 | 0 | return 0; |
2094 | 0 | } |
2095 | 0 | log_assert(tp == tp2); |
2096 | 0 | lock_basic_lock(&tp->lock); |
2097 | | |
2098 | | /* schedule */ |
2099 | 0 | mold = wait_probe_time(env->anchors); |
2100 | 0 | (void)rbtree_delete(&env->anchors->autr->probe, tp); |
2101 | 0 | tp->autr->next_probe_time = calc_next_probe(env, |
2102 | 0 | tp->autr->query_interval); |
2103 | 0 | (void)rbtree_insert(&env->anchors->autr->probe, &tp->autr->pnode); |
2104 | 0 | mnew = wait_probe_time(env->anchors); |
2105 | |
|
2106 | 0 | lock_basic_unlock(&env->anchors->lock); |
2107 | 0 | verbose(VERB_ALGO, "next probe set in %d seconds", |
2108 | 0 | (int)tp->autr->next_probe_time - (int)*env->now); |
2109 | 0 | if(mold != mnew) { |
2110 | 0 | reset_worker_timer_at(env, mnew); |
2111 | 0 | } |
2112 | 0 | return 1; |
2113 | 0 | } |
2114 | | |
2115 | | /** Revoke and Delete a trust point */ |
2116 | | static void |
2117 | | autr_tp_remove(struct module_env* env, struct trust_anchor* tp, |
2118 | | struct ub_packed_rrset_key* dnskey_rrset) |
2119 | 0 | { |
2120 | 0 | struct trust_anchor* del_tp; |
2121 | 0 | struct trust_anchor key; |
2122 | 0 | struct autr_point_data pd; |
2123 | 0 | time_t mold, mnew; |
2124 | |
|
2125 | 0 | log_nametypeclass(VERB_OPS, "trust point was revoked", |
2126 | 0 | tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass); |
2127 | 0 | tp->autr->revoked = 1; |
2128 | | |
2129 | | /* use space allocated for dnskey_rrset to save name of anchor */ |
2130 | 0 | memset(&key, 0, sizeof(key)); |
2131 | 0 | memset(&pd, 0, sizeof(pd)); |
2132 | 0 | key.autr = &pd; |
2133 | 0 | key.node.key = &key; |
2134 | 0 | pd.pnode.key = &key; |
2135 | 0 | pd.next_probe_time = tp->autr->next_probe_time; |
2136 | 0 | key.name = dnskey_rrset->rk.dname; |
2137 | 0 | key.namelen = tp->namelen; |
2138 | 0 | key.namelabs = tp->namelabs; |
2139 | 0 | key.dclass = tp->dclass; |
2140 | | |
2141 | | /* unlock */ |
2142 | 0 | lock_basic_unlock(&tp->lock); |
2143 | | |
2144 | | /* take from tree. It could be deleted by someone else,hence (void). */ |
2145 | 0 | lock_basic_lock(&env->anchors->lock); |
2146 | 0 | del_tp = (struct trust_anchor*)rbtree_delete(env->anchors->tree, &key); |
2147 | 0 | mold = wait_probe_time(env->anchors); |
2148 | 0 | (void)rbtree_delete(&env->anchors->autr->probe, &key); |
2149 | 0 | mnew = wait_probe_time(env->anchors); |
2150 | 0 | anchors_init_parents_locked(env->anchors); |
2151 | 0 | lock_basic_unlock(&env->anchors->lock); |
2152 | | |
2153 | | /* if !del_tp then the trust point is no longer present in the tree, |
2154 | | * it was deleted by someone else, who will write the zonefile and |
2155 | | * clean up the structure */ |
2156 | 0 | if(del_tp) { |
2157 | | /* save on disk */ |
2158 | 0 | del_tp->autr->next_probe_time = 0; /* no more probing for it */ |
2159 | 0 | autr_write_file(env, del_tp); |
2160 | | |
2161 | | /* delete */ |
2162 | 0 | autr_point_delete(del_tp); |
2163 | 0 | } |
2164 | 0 | if(mold != mnew) { |
2165 | 0 | reset_worker_timer_at(env, mnew); |
2166 | 0 | } |
2167 | 0 | } |
2168 | | |
2169 | | int autr_process_prime(struct module_env* env, struct val_env* ve, |
2170 | | struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, |
2171 | | struct module_qstate* qstate) |
2172 | 0 | { |
2173 | 0 | int changed = 0; |
2174 | 0 | log_assert(tp && tp->autr); |
2175 | | /* autotrust update trust anchors */ |
2176 | | /* the tp is locked, and stays locked unless it is deleted */ |
2177 | | |
2178 | | /* we could just catch the anchor here while another thread |
2179 | | * is busy deleting it. Just unlock and let the other do its job */ |
2180 | 0 | if(tp->autr->revoked) { |
2181 | 0 | log_nametypeclass(VERB_ALGO, "autotrust not processed, " |
2182 | 0 | "trust point revoked", tp->name, |
2183 | 0 | LDNS_RR_TYPE_DNSKEY, tp->dclass); |
2184 | 0 | lock_basic_unlock(&tp->lock); |
2185 | 0 | return 0; /* it is revoked */ |
2186 | 0 | } |
2187 | | |
2188 | | /* query_dnskeys(): */ |
2189 | 0 | tp->autr->last_queried = *env->now; |
2190 | |
|
2191 | 0 | log_nametypeclass(VERB_ALGO, "autotrust process for", |
2192 | 0 | tp->name, LDNS_RR_TYPE_DNSKEY, tp->dclass); |
2193 | | /* see if time alone makes some keys valid */ |
2194 | 0 | autr_holddown_exceed(env, tp, &changed); |
2195 | 0 | if(changed) { |
2196 | 0 | verbose(VERB_ALGO, "autotrust: morekeys, reassemble"); |
2197 | 0 | if(!autr_assemble(tp)) { |
2198 | 0 | log_err("malloc failure assembling autotrust keys"); |
2199 | 0 | return 1; /* unchanged */ |
2200 | 0 | } |
2201 | 0 | } |
2202 | | /* did we get any data? */ |
2203 | 0 | if(!dnskey_rrset) { |
2204 | 0 | verbose(VERB_ALGO, "autotrust: no dnskey rrset"); |
2205 | | /* no update of query_failed, because then we would have |
2206 | | * to write to disk. But we cannot because we maybe are |
2207 | | * still 'initializing' with DS records, that we cannot write |
2208 | | * in the full format (which only contains KSKs). */ |
2209 | 0 | return 1; /* trust point exists */ |
2210 | 0 | } |
2211 | | /* check for revoked keys to remove immediately */ |
2212 | 0 | check_contains_revoked(env, ve, tp, dnskey_rrset, &changed, qstate); |
2213 | 0 | if(changed) { |
2214 | 0 | verbose(VERB_ALGO, "autotrust: revokedkeys, reassemble"); |
2215 | 0 | if(!autr_assemble(tp)) { |
2216 | 0 | log_err("malloc failure assembling autotrust keys"); |
2217 | 0 | return 1; /* unchanged */ |
2218 | 0 | } |
2219 | 0 | if(!tp->ds_rrset && !tp->dnskey_rrset) { |
2220 | | /* no more keys, all are revoked */ |
2221 | | /* this is a success for this probe attempt */ |
2222 | 0 | tp->autr->last_success = *env->now; |
2223 | 0 | autr_tp_remove(env, tp, dnskey_rrset); |
2224 | 0 | return 0; /* trust point removed */ |
2225 | 0 | } |
2226 | 0 | } |
2227 | | /* verify the dnskey rrset and see if it is valid. */ |
2228 | 0 | if(!verify_dnskey(env, ve, tp, dnskey_rrset, qstate)) { |
2229 | 0 | verbose(VERB_ALGO, "autotrust: dnskey did not verify."); |
2230 | | /* only increase failure count if this is not the first prime, |
2231 | | * this means there was a previous successful probe */ |
2232 | 0 | if(tp->autr->last_success) { |
2233 | 0 | tp->autr->query_failed += 1; |
2234 | 0 | autr_write_file(env, tp); |
2235 | 0 | } |
2236 | 0 | return 1; /* trust point exists */ |
2237 | 0 | } |
2238 | | |
2239 | 0 | tp->autr->last_success = *env->now; |
2240 | 0 | tp->autr->query_failed = 0; |
2241 | | |
2242 | | /* Add new trust anchors to the data structure |
2243 | | * - note which trust anchors are seen this probe. |
2244 | | * Set trustpoint query_interval and retry_time. |
2245 | | * - find minimum rrsig expiration interval |
2246 | | */ |
2247 | 0 | if(!update_events(env, ve, tp, dnskey_rrset, &changed)) { |
2248 | 0 | log_err("malloc failure in autotrust update_events. " |
2249 | 0 | "trust point unchanged."); |
2250 | 0 | return 1; /* trust point unchanged, so exists */ |
2251 | 0 | } |
2252 | | |
2253 | | /* - for every SEP key do the 5011 statetable. |
2254 | | * - remove missing trustanchors (if veryold and we have new anchors). |
2255 | | */ |
2256 | 0 | if(!do_statetable(env, tp, &changed)) { |
2257 | 0 | log_err("malloc failure in autotrust do_statetable. " |
2258 | 0 | "trust point unchanged."); |
2259 | 0 | return 1; /* trust point unchanged, so exists */ |
2260 | 0 | } |
2261 | | |
2262 | 0 | autr_cleanup_keys(tp); |
2263 | 0 | if(!set_next_probe(env, tp, dnskey_rrset)) |
2264 | 0 | return 0; /* trust point does not exist */ |
2265 | 0 | autr_write_file(env, tp); |
2266 | 0 | if(changed) { |
2267 | 0 | verbose(VERB_ALGO, "autotrust: changed, reassemble"); |
2268 | 0 | if(!autr_assemble(tp)) { |
2269 | 0 | log_err("malloc failure assembling autotrust keys"); |
2270 | 0 | return 1; /* unchanged */ |
2271 | 0 | } |
2272 | 0 | if(!tp->ds_rrset && !tp->dnskey_rrset) { |
2273 | | /* no more keys, all are revoked */ |
2274 | 0 | autr_tp_remove(env, tp, dnskey_rrset); |
2275 | 0 | return 0; /* trust point removed */ |
2276 | 0 | } |
2277 | 0 | } else verbose(VERB_ALGO, "autotrust: no changes"); |
2278 | | |
2279 | 0 | return 1; /* trust point exists */ |
2280 | 0 | } |
2281 | | |
2282 | | /** debug print a trust anchor key */ |
2283 | | static void |
2284 | | autr_debug_print_ta(struct autr_ta* ta) |
2285 | 0 | { |
2286 | 0 | char buf[32]; |
2287 | 0 | char* str = sldns_wire2str_rr(ta->rr, ta->rr_len); |
2288 | 0 | if(!str) { |
2289 | 0 | log_info("out of memory in debug_print_ta"); |
2290 | 0 | return; |
2291 | 0 | } |
2292 | 0 | if(str[0]) str[strlen(str)-1]=0; /* remove newline */ |
2293 | 0 | (void)autr_ctime_r(&ta->last_change, buf); |
2294 | 0 | if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ |
2295 | 0 | log_info("[%s] %s ;;state:%d ;;pending_count:%d%s%s last:%s", |
2296 | 0 | trustanchor_state2str(ta->s), str, ta->s, ta->pending_count, |
2297 | 0 | ta->fetched?" fetched":"", ta->revoked?" revoked":"", buf); |
2298 | 0 | free(str); |
2299 | 0 | } |
2300 | | |
2301 | | /** debug print a trust point */ |
2302 | | static void |
2303 | | autr_debug_print_tp(struct trust_anchor* tp) |
2304 | 0 | { |
2305 | 0 | struct autr_ta* ta; |
2306 | | /* Note: buf is also used for autr_ctime_r but that only needs a size |
2307 | | * of 26, so LDNS_MAX_DOMAINLEN is enough. */ |
2308 | 0 | char buf[LDNS_MAX_DOMAINLEN]; |
2309 | 0 | if(!tp->autr) |
2310 | 0 | return; |
2311 | 0 | dname_str(tp->name, buf); |
2312 | 0 | log_info("trust point %s : %d", buf, (int)tp->dclass); |
2313 | 0 | log_info("assembled %d DS and %d DNSKEYs", |
2314 | 0 | (int)tp->numDS, (int)tp->numDNSKEY); |
2315 | 0 | if(tp->ds_rrset) { |
2316 | 0 | log_packed_rrset(NO_VERBOSE, "DS:", tp->ds_rrset); |
2317 | 0 | } |
2318 | 0 | if(tp->dnskey_rrset) { |
2319 | 0 | log_packed_rrset(NO_VERBOSE, "DNSKEY:", tp->dnskey_rrset); |
2320 | 0 | } |
2321 | 0 | log_info("file %s", tp->autr->file); |
2322 | 0 | (void)autr_ctime_r(&tp->autr->last_queried, buf); |
2323 | 0 | if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ |
2324 | 0 | log_info("last_queried: %u %s", (unsigned)tp->autr->last_queried, buf); |
2325 | 0 | (void)autr_ctime_r(&tp->autr->last_success, buf); |
2326 | 0 | if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ |
2327 | 0 | log_info("last_success: %u %s", (unsigned)tp->autr->last_success, buf); |
2328 | 0 | (void)autr_ctime_r(&tp->autr->next_probe_time, buf); |
2329 | 0 | if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ |
2330 | 0 | log_info("next_probe_time: %u %s", (unsigned)tp->autr->next_probe_time, |
2331 | 0 | buf); |
2332 | 0 | log_info("query_interval: %u", (unsigned)tp->autr->query_interval); |
2333 | 0 | log_info("retry_time: %u", (unsigned)tp->autr->retry_time); |
2334 | 0 | log_info("query_failed: %u", (unsigned)tp->autr->query_failed); |
2335 | | |
2336 | 0 | for(ta=tp->autr->keys; ta; ta=ta->next) { |
2337 | 0 | autr_debug_print_ta(ta); |
2338 | 0 | } |
2339 | 0 | } |
2340 | | |
2341 | | void |
2342 | | autr_debug_print(struct val_anchors* anchors) |
2343 | 0 | { |
2344 | 0 | struct trust_anchor* tp; |
2345 | 0 | lock_basic_lock(&anchors->lock); |
2346 | 0 | RBTREE_FOR(tp, struct trust_anchor*, anchors->tree) { |
2347 | 0 | lock_basic_lock(&tp->lock); |
2348 | 0 | autr_debug_print_tp(tp); |
2349 | 0 | lock_basic_unlock(&tp->lock); |
2350 | 0 | } |
2351 | 0 | lock_basic_unlock(&anchors->lock); |
2352 | 0 | } |
2353 | | |
2354 | | void probe_answer_cb(void* arg, int ATTR_UNUSED(rcode), |
2355 | | sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec), |
2356 | | char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited)) |
2357 | 0 | { |
2358 | | /* retry was set before the query was done, |
2359 | | * re-querytime is set when query succeeded, but that may not |
2360 | | * have reset this timer because the query could have been |
2361 | | * handled by another thread. In that case, this callback would |
2362 | | * get called after the original timeout is done. |
2363 | | * By not resetting the timer, it may probe more often, but not |
2364 | | * less often. |
2365 | | * Unless the new lookup resulted in smaller TTLs and thus smaller |
2366 | | * timeout values. In that case one old TTL could be mistakenly done. |
2367 | | */ |
2368 | 0 | struct module_env* env = (struct module_env*)arg; |
2369 | 0 | verbose(VERB_ALGO, "autotrust probe answer cb"); |
2370 | 0 | reset_worker_timer(env); |
2371 | 0 | } |
2372 | | |
2373 | | /** probe a trust anchor DNSKEY and unlocks tp */ |
2374 | | static void |
2375 | | probe_anchor(struct module_env* env, struct trust_anchor* tp) |
2376 | 0 | { |
2377 | 0 | struct query_info qinfo; |
2378 | 0 | uint16_t qflags = BIT_RD; |
2379 | 0 | struct edns_data edns; |
2380 | 0 | sldns_buffer* buf = env->scratch_buffer; |
2381 | 0 | qinfo.qname = regional_alloc_init(env->scratch, tp->name, tp->namelen); |
2382 | 0 | if(!qinfo.qname) { |
2383 | 0 | log_err("out of memory making 5011 probe"); |
2384 | 0 | return; |
2385 | 0 | } |
2386 | 0 | qinfo.qname_len = tp->namelen; |
2387 | 0 | qinfo.qtype = LDNS_RR_TYPE_DNSKEY; |
2388 | 0 | qinfo.qclass = tp->dclass; |
2389 | 0 | qinfo.local_alias = NULL; |
2390 | 0 | log_query_info(VERB_ALGO, "autotrust probe", &qinfo); |
2391 | 0 | verbose(VERB_ALGO, "retry probe set in %d seconds", |
2392 | 0 | (int)tp->autr->next_probe_time - (int)*env->now); |
2393 | 0 | edns.edns_present = 1; |
2394 | 0 | edns.ext_rcode = 0; |
2395 | 0 | edns.edns_version = 0; |
2396 | 0 | edns.bits = EDNS_DO; |
2397 | 0 | edns.opt_list_in = NULL; |
2398 | 0 | edns.opt_list_out = NULL; |
2399 | 0 | edns.opt_list_inplace_cb_out = NULL; |
2400 | 0 | edns.padding_block_size = 0; |
2401 | 0 | edns.cookie_present = 0; |
2402 | 0 | edns.cookie_valid = 0; |
2403 | 0 | if(sldns_buffer_capacity(buf) < 65535) |
2404 | 0 | edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); |
2405 | 0 | else edns.udp_size = 65535; |
2406 | | |
2407 | | /* can't hold the lock while mesh_run is processing */ |
2408 | 0 | lock_basic_unlock(&tp->lock); |
2409 | | |
2410 | | /* delete the DNSKEY from rrset and key cache so an active probe |
2411 | | * is done. First the rrset so another thread does not use it |
2412 | | * to recreate the key entry in a race condition. */ |
2413 | 0 | rrset_cache_remove(env->rrset_cache, qinfo.qname, qinfo.qname_len, |
2414 | 0 | qinfo.qtype, qinfo.qclass, 0); |
2415 | 0 | key_cache_remove(env->key_cache, qinfo.qname, qinfo.qname_len, |
2416 | 0 | qinfo.qclass); |
2417 | |
|
2418 | 0 | if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, |
2419 | 0 | &probe_answer_cb, env, 0)) { |
2420 | 0 | log_err("out of memory making 5011 probe"); |
2421 | 0 | } |
2422 | 0 | } |
2423 | | |
2424 | | /** fetch first to-probe trust-anchor and lock it and set retrytime */ |
2425 | | static struct trust_anchor* |
2426 | | todo_probe(struct module_env* env, time_t* next) |
2427 | 0 | { |
2428 | 0 | struct trust_anchor* tp; |
2429 | 0 | rbnode_type* el; |
2430 | | /* get first one */ |
2431 | 0 | lock_basic_lock(&env->anchors->lock); |
2432 | 0 | if( (el=rbtree_first(&env->anchors->autr->probe)) == RBTREE_NULL) { |
2433 | | /* in case of revoked anchors */ |
2434 | 0 | lock_basic_unlock(&env->anchors->lock); |
2435 | | /* signal that there are no anchors to probe */ |
2436 | 0 | *next = 0; |
2437 | 0 | return NULL; |
2438 | 0 | } |
2439 | 0 | tp = (struct trust_anchor*)el->key; |
2440 | 0 | lock_basic_lock(&tp->lock); |
2441 | | |
2442 | | /* is it eligible? */ |
2443 | 0 | if((time_t)tp->autr->next_probe_time > *env->now) { |
2444 | | /* no more to probe */ |
2445 | 0 | *next = (time_t)tp->autr->next_probe_time - *env->now; |
2446 | 0 | lock_basic_unlock(&tp->lock); |
2447 | 0 | lock_basic_unlock(&env->anchors->lock); |
2448 | 0 | return NULL; |
2449 | 0 | } |
2450 | | |
2451 | | /* reset its next probe time */ |
2452 | 0 | (void)rbtree_delete(&env->anchors->autr->probe, tp); |
2453 | 0 | tp->autr->next_probe_time = calc_next_probe(env, tp->autr->retry_time); |
2454 | 0 | (void)rbtree_insert(&env->anchors->autr->probe, &tp->autr->pnode); |
2455 | 0 | lock_basic_unlock(&env->anchors->lock); |
2456 | |
|
2457 | 0 | return tp; |
2458 | 0 | } |
2459 | | |
2460 | | time_t |
2461 | | autr_probe_timer(struct module_env* env) |
2462 | 0 | { |
2463 | 0 | struct trust_anchor* tp; |
2464 | 0 | time_t next_probe = 3600; |
2465 | 0 | int num = 0; |
2466 | 0 | if(autr_permit_small_holddown) next_probe = 1; |
2467 | 0 | verbose(VERB_ALGO, "autotrust probe timer callback"); |
2468 | | /* while there are still anchors to probe */ |
2469 | 0 | while( (tp = todo_probe(env, &next_probe)) ) { |
2470 | | /* make a probe for this anchor */ |
2471 | 0 | probe_anchor(env, tp); |
2472 | 0 | num++; |
2473 | 0 | } |
2474 | 0 | regional_free_all(env->scratch); |
2475 | 0 | if(next_probe == 0) |
2476 | 0 | return 0; /* no trust points to probe */ |
2477 | 0 | verbose(VERB_ALGO, "autotrust probe timer %d callbacks done", num); |
2478 | 0 | return next_probe; |
2479 | 0 | } |