/src/unbound/validator/validator.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * validator/validator.c - secure validator DNS query response module |
3 | | * |
4 | | * Copyright (c) 2007, NLnet Labs. All rights reserved. |
5 | | * |
6 | | * This software is open source. |
7 | | * |
8 | | * Redistribution and use in source and binary forms, with or without |
9 | | * modification, are permitted provided that the following conditions |
10 | | * are met: |
11 | | * |
12 | | * Redistributions of source code must retain the above copyright notice, |
13 | | * this list of conditions and the following disclaimer. |
14 | | * |
15 | | * Redistributions in binary form must reproduce the above copyright notice, |
16 | | * this list of conditions and the following disclaimer in the documentation |
17 | | * and/or other materials provided with the distribution. |
18 | | * |
19 | | * Neither the name of the NLNET LABS nor the names of its contributors may |
20 | | * be used to endorse or promote products derived from this software without |
21 | | * specific prior written permission. |
22 | | * |
23 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
27 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
29 | | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
30 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
31 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
32 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | | */ |
35 | | |
36 | | /** |
37 | | * \file |
38 | | * |
39 | | * This file contains a module that performs validation of DNS queries. |
40 | | * According to RFC 4034. |
41 | | */ |
42 | | #include "config.h" |
43 | | #include <ctype.h> |
44 | | #include "validator/validator.h" |
45 | | #include "validator/val_anchor.h" |
46 | | #include "validator/val_kcache.h" |
47 | | #include "validator/val_kentry.h" |
48 | | #include "validator/val_utils.h" |
49 | | #include "validator/val_nsec.h" |
50 | | #include "validator/val_nsec3.h" |
51 | | #include "validator/val_neg.h" |
52 | | #include "validator/val_sigcrypt.h" |
53 | | #include "validator/autotrust.h" |
54 | | #include "services/cache/dns.h" |
55 | | #include "services/cache/rrset.h" |
56 | | #include "util/data/dname.h" |
57 | | #include "util/module.h" |
58 | | #include "util/log.h" |
59 | | #include "util/net_help.h" |
60 | | #include "util/regional.h" |
61 | | #include "util/config_file.h" |
62 | | #include "util/fptr_wlist.h" |
63 | | #include "sldns/rrdef.h" |
64 | | #include "sldns/wire2str.h" |
65 | | #include "sldns/str2wire.h" |
66 | | |
67 | | /** Max number of RRSIGs to validate at once, suspend query for later. */ |
68 | 0 | #define MAX_VALIDATE_AT_ONCE 8 |
69 | | /** Max number of validation suspends allowed, error out otherwise. */ |
70 | 0 | #define MAX_VALIDATION_SUSPENDS 16 |
71 | | |
72 | | /* forward decl for cache response and normal super inform calls of a DS */ |
73 | | static void process_ds_response(struct module_qstate* qstate, |
74 | | struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, |
75 | | struct query_info* qinfo, struct sock_list* origin, int* suspend, |
76 | | struct module_qstate* sub_qstate); |
77 | | |
78 | | |
79 | | /* Updates the supplied EDE (RFC8914) code selectively so we don't lose |
80 | | * a more specific code */ |
81 | | static void |
82 | | update_reason_bogus(struct reply_info* rep, sldns_ede_code reason_bogus) |
83 | 0 | { |
84 | 0 | if(reason_bogus == LDNS_EDE_NONE) return; |
85 | 0 | if(reason_bogus == LDNS_EDE_DNSSEC_BOGUS |
86 | 0 | && rep->reason_bogus != LDNS_EDE_NONE |
87 | 0 | && rep->reason_bogus != LDNS_EDE_DNSSEC_BOGUS) return; |
88 | 0 | rep->reason_bogus = reason_bogus; |
89 | 0 | } |
90 | | |
91 | | |
92 | | /** fill up nsec3 key iterations config entry */ |
93 | | static int |
94 | | fill_nsec3_iter(size_t** keysize, size_t** maxiter, char* s, int c) |
95 | 0 | { |
96 | 0 | char* e; |
97 | 0 | int i; |
98 | 0 | *keysize = (size_t*)calloc((size_t)c, sizeof(size_t)); |
99 | 0 | *maxiter = (size_t*)calloc((size_t)c, sizeof(size_t)); |
100 | 0 | if(!*keysize || !*maxiter) { |
101 | 0 | free(*keysize); |
102 | 0 | *keysize = NULL; |
103 | 0 | free(*maxiter); |
104 | 0 | *maxiter = NULL; |
105 | 0 | log_err("out of memory"); |
106 | 0 | return 0; |
107 | 0 | } |
108 | 0 | for(i=0; i<c; i++) { |
109 | 0 | (*keysize)[i] = (size_t)strtol(s, &e, 10); |
110 | 0 | if(s == e) { |
111 | 0 | log_err("cannot parse: %s", s); |
112 | 0 | free(*keysize); |
113 | 0 | *keysize = NULL; |
114 | 0 | free(*maxiter); |
115 | 0 | *maxiter = NULL; |
116 | 0 | return 0; |
117 | 0 | } |
118 | 0 | s = e; |
119 | 0 | (*maxiter)[i] = (size_t)strtol(s, &e, 10); |
120 | 0 | if(s == e) { |
121 | 0 | log_err("cannot parse: %s", s); |
122 | 0 | free(*keysize); |
123 | 0 | *keysize = NULL; |
124 | 0 | free(*maxiter); |
125 | 0 | *maxiter = NULL; |
126 | 0 | return 0; |
127 | 0 | } |
128 | 0 | s = e; |
129 | 0 | if(i>0 && (*keysize)[i-1] >= (*keysize)[i]) { |
130 | 0 | log_err("nsec3 key iterations not ascending: %d %d", |
131 | 0 | (int)(*keysize)[i-1], (int)(*keysize)[i]); |
132 | 0 | free(*keysize); |
133 | 0 | *keysize = NULL; |
134 | 0 | free(*maxiter); |
135 | 0 | *maxiter = NULL; |
136 | 0 | return 0; |
137 | 0 | } |
138 | 0 | verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d", |
139 | 0 | (int)(*keysize)[i], (int)(*maxiter)[i]); |
140 | 0 | } |
141 | 0 | return 1; |
142 | 0 | } |
143 | | |
144 | | int |
145 | | val_env_parse_key_iter(char* val_nsec3_key_iterations, size_t** keysize, |
146 | | size_t** maxiter, int* keyiter_count) |
147 | 0 | { |
148 | 0 | int c; |
149 | 0 | c = cfg_count_numbers(val_nsec3_key_iterations); |
150 | 0 | if(c < 1 || (c&1)) { |
151 | 0 | log_err("validator: unparsable or odd nsec3 key " |
152 | 0 | "iterations: %s", val_nsec3_key_iterations); |
153 | 0 | return 0; |
154 | 0 | } |
155 | 0 | *keyiter_count = c/2; |
156 | 0 | if(!fill_nsec3_iter(keysize, maxiter, val_nsec3_key_iterations, c/2)) { |
157 | 0 | log_err("validator: cannot apply nsec3 key iterations"); |
158 | 0 | return 0; |
159 | 0 | } |
160 | 0 | return 1; |
161 | 0 | } |
162 | | |
163 | | void |
164 | | val_env_apply_cfg(struct val_env* val_env, struct config_file* cfg, |
165 | | size_t* keysize, size_t* maxiter, int keyiter_count) |
166 | 0 | { |
167 | 0 | free(val_env->nsec3_keysize); |
168 | 0 | free(val_env->nsec3_maxiter); |
169 | 0 | val_env->nsec3_keysize = keysize; |
170 | 0 | val_env->nsec3_maxiter = maxiter; |
171 | 0 | val_env->nsec3_keyiter_count = keyiter_count; |
172 | 0 | val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl; |
173 | 0 | val_env->date_override = cfg->val_date_override; |
174 | 0 | val_env->skew_min = cfg->val_sig_skew_min; |
175 | 0 | val_env->skew_max = cfg->val_sig_skew_max; |
176 | 0 | val_env->max_restart = cfg->val_max_restart; |
177 | 0 | } |
178 | | |
179 | | /** apply config settings to validator */ |
180 | | static int |
181 | | val_apply_cfg(struct module_env* env, struct val_env* val_env, |
182 | | struct config_file* cfg) |
183 | 0 | { |
184 | 0 | size_t* keysize=NULL, *maxiter=NULL; |
185 | 0 | int keyiter_count = 0; |
186 | 0 | if(!env->anchors) |
187 | 0 | env->anchors = anchors_create(); |
188 | 0 | if(!env->anchors) { |
189 | 0 | log_err("out of memory"); |
190 | 0 | return 0; |
191 | 0 | } |
192 | 0 | if (env->key_cache) |
193 | 0 | val_env->kcache = env->key_cache; |
194 | 0 | if(!val_env->kcache) |
195 | 0 | val_env->kcache = key_cache_create(cfg); |
196 | 0 | if(!val_env->kcache) { |
197 | 0 | log_err("out of memory"); |
198 | 0 | return 0; |
199 | 0 | } |
200 | 0 | env->key_cache = val_env->kcache; |
201 | 0 | if(!anchors_apply_cfg(env->anchors, cfg)) { |
202 | 0 | log_err("validator: error in trustanchors config"); |
203 | 0 | return 0; |
204 | 0 | } |
205 | 0 | if(!val_env_parse_key_iter(cfg->val_nsec3_key_iterations, |
206 | 0 | &keysize, &maxiter, &keyiter_count)) { |
207 | 0 | return 0; |
208 | 0 | } |
209 | 0 | val_env_apply_cfg(val_env, cfg, keysize, maxiter, keyiter_count); |
210 | 0 | if (env->neg_cache) |
211 | 0 | val_env->neg_cache = env->neg_cache; |
212 | 0 | if(!val_env->neg_cache) |
213 | 0 | val_env->neg_cache = val_neg_create(cfg, |
214 | 0 | val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]); |
215 | 0 | if(!val_env->neg_cache) { |
216 | 0 | log_err("out of memory"); |
217 | 0 | return 0; |
218 | 0 | } |
219 | 0 | env->neg_cache = val_env->neg_cache; |
220 | 0 | return 1; |
221 | 0 | } |
222 | | |
223 | | #ifdef USE_ECDSA_EVP_WORKAROUND |
224 | | void ecdsa_evp_workaround_init(void); |
225 | | #endif |
226 | | int |
227 | | val_init(struct module_env* env, int id) |
228 | 0 | { |
229 | 0 | struct val_env* val_env = (struct val_env*)calloc(1, |
230 | 0 | sizeof(struct val_env)); |
231 | 0 | if(!val_env) { |
232 | 0 | log_err("malloc failure"); |
233 | 0 | return 0; |
234 | 0 | } |
235 | 0 | env->modinfo[id] = (void*)val_env; |
236 | 0 | env->need_to_validate = 1; |
237 | 0 | lock_basic_init(&val_env->bogus_lock); |
238 | 0 | lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus, |
239 | 0 | sizeof(val_env->num_rrset_bogus)); |
240 | | #ifdef USE_ECDSA_EVP_WORKAROUND |
241 | | ecdsa_evp_workaround_init(); |
242 | | #endif |
243 | 0 | if(!val_apply_cfg(env, val_env, env->cfg)) { |
244 | 0 | log_err("validator: could not apply configuration settings."); |
245 | 0 | return 0; |
246 | 0 | } |
247 | 0 | if(env->cfg->disable_edns_do) { |
248 | 0 | struct trust_anchor* anchor = anchors_find_any_noninsecure( |
249 | 0 | env->anchors); |
250 | 0 | if(anchor) { |
251 | 0 | char b[LDNS_MAX_DOMAINLEN]; |
252 | 0 | dname_str(anchor->name, b); |
253 | 0 | log_warn("validator: disable-edns-do is enabled, but there is a trust anchor for '%s'. Since DNSSEC could not work, the disable-edns-do setting is turned off. Continuing without it.", b); |
254 | 0 | lock_basic_unlock(&anchor->lock); |
255 | 0 | env->cfg->disable_edns_do = 0; |
256 | 0 | } |
257 | 0 | } |
258 | |
|
259 | 0 | return 1; |
260 | 0 | } |
261 | | |
262 | | void |
263 | | val_deinit(struct module_env* env, int id) |
264 | 0 | { |
265 | 0 | struct val_env* val_env; |
266 | 0 | if(!env || !env->modinfo[id]) |
267 | 0 | return; |
268 | 0 | val_env = (struct val_env*)env->modinfo[id]; |
269 | 0 | lock_basic_destroy(&val_env->bogus_lock); |
270 | 0 | anchors_delete(env->anchors); |
271 | 0 | env->anchors = NULL; |
272 | 0 | key_cache_delete(val_env->kcache); |
273 | 0 | env->key_cache = NULL; |
274 | 0 | neg_cache_delete(val_env->neg_cache); |
275 | 0 | env->neg_cache = NULL; |
276 | 0 | free(val_env->nsec3_keysize); |
277 | 0 | free(val_env->nsec3_maxiter); |
278 | 0 | free(val_env); |
279 | 0 | env->modinfo[id] = NULL; |
280 | 0 | } |
281 | | |
282 | | /** fill in message structure */ |
283 | | static struct val_qstate* |
284 | | val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq) |
285 | 0 | { |
286 | 0 | if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) { |
287 | | /* create a message to verify */ |
288 | 0 | verbose(VERB_ALGO, "constructing reply for validation"); |
289 | 0 | vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region, |
290 | 0 | sizeof(struct dns_msg)); |
291 | 0 | if(!vq->orig_msg) |
292 | 0 | return NULL; |
293 | 0 | vq->orig_msg->qinfo = qstate->qinfo; |
294 | 0 | vq->orig_msg->rep = (struct reply_info*)regional_alloc( |
295 | 0 | qstate->region, sizeof(struct reply_info)); |
296 | 0 | if(!vq->orig_msg->rep) |
297 | 0 | return NULL; |
298 | 0 | memset(vq->orig_msg->rep, 0, sizeof(struct reply_info)); |
299 | 0 | vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf) |
300 | 0 | |BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD)); |
301 | 0 | vq->orig_msg->rep->qdcount = 1; |
302 | 0 | vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE; |
303 | 0 | } else { |
304 | 0 | vq->orig_msg = qstate->return_msg; |
305 | 0 | } |
306 | 0 | vq->qchase = qstate->qinfo; |
307 | | /* chase reply will be an edited (sub)set of the orig msg rrset ptrs */ |
308 | 0 | vq->chase_reply = regional_alloc_init(qstate->region, |
309 | 0 | vq->orig_msg->rep, |
310 | 0 | sizeof(struct reply_info) - sizeof(struct rrset_ref)); |
311 | 0 | if(!vq->chase_reply) |
312 | 0 | return NULL; |
313 | 0 | if(vq->orig_msg->rep->rrset_count > RR_COUNT_MAX) |
314 | 0 | return NULL; /* protect against integer overflow */ |
315 | | /* Over allocate (+an_numrrsets) in case we need to put extra DNAME |
316 | | * records for unsigned CNAME repetitions */ |
317 | 0 | vq->chase_reply->rrsets = regional_alloc(qstate->region, |
318 | 0 | sizeof(struct ub_packed_rrset_key*) * |
319 | 0 | (vq->orig_msg->rep->rrset_count |
320 | 0 | + vq->orig_msg->rep->an_numrrsets)); |
321 | 0 | if(!vq->chase_reply->rrsets) |
322 | 0 | return NULL; |
323 | 0 | memmove(vq->chase_reply->rrsets, vq->orig_msg->rep->rrsets, |
324 | 0 | sizeof(struct ub_packed_rrset_key*) * |
325 | 0 | vq->orig_msg->rep->rrset_count); |
326 | 0 | vq->rrset_skip = 0; |
327 | 0 | return vq; |
328 | 0 | } |
329 | | |
330 | | /** allocate new validator query state */ |
331 | | static struct val_qstate* |
332 | | val_new(struct module_qstate* qstate, int id) |
333 | 0 | { |
334 | 0 | struct val_qstate* vq = (struct val_qstate*)regional_alloc( |
335 | 0 | qstate->region, sizeof(*vq)); |
336 | 0 | log_assert(!qstate->minfo[id]); |
337 | 0 | if(!vq) |
338 | 0 | return NULL; |
339 | 0 | memset(vq, 0, sizeof(*vq)); |
340 | 0 | qstate->minfo[id] = vq; |
341 | 0 | vq->state = VAL_INIT_STATE; |
342 | 0 | return val_new_getmsg(qstate, vq); |
343 | 0 | } |
344 | | |
345 | | /** reset validator query state for query restart */ |
346 | | static void |
347 | | val_restart(struct val_qstate* vq) |
348 | 0 | { |
349 | 0 | struct comm_timer* temp_timer; |
350 | 0 | int restart_count; |
351 | 0 | if(!vq) return; |
352 | 0 | temp_timer = vq->suspend_timer; |
353 | 0 | restart_count = vq->restart_count+1; |
354 | 0 | memset(vq, 0, sizeof(*vq)); |
355 | 0 | vq->suspend_timer = temp_timer; |
356 | 0 | vq->restart_count = restart_count; |
357 | 0 | vq->state = VAL_INIT_STATE; |
358 | 0 | } |
359 | | |
360 | | /** |
361 | | * Exit validation with an error status |
362 | | * |
363 | | * @param qstate: query state |
364 | | * @param id: validator id. |
365 | | * @return false, for use by caller to return to stop processing. |
366 | | */ |
367 | | static int |
368 | | val_error(struct module_qstate* qstate, int id) |
369 | 0 | { |
370 | 0 | qstate->ext_state[id] = module_error; |
371 | 0 | qstate->return_rcode = LDNS_RCODE_SERVFAIL; |
372 | 0 | return 0; |
373 | 0 | } |
374 | | |
375 | | /** |
376 | | * Check to see if a given response needs to go through the validation |
377 | | * process. Typical reasons for this routine to return false are: CD bit was |
378 | | * on in the original request, or the response is a kind of message that |
379 | | * is unvalidatable (i.e., SERVFAIL, REFUSED, etc.) |
380 | | * |
381 | | * @param qstate: query state. |
382 | | * @param ret_rc: rcode for this message (if noerror - examine ret_msg). |
383 | | * @param ret_msg: return msg, can be NULL; look at rcode instead. |
384 | | * @return true if the response could use validation (although this does not |
385 | | * mean we can actually validate this response). |
386 | | */ |
387 | | static int |
388 | | needs_validation(struct module_qstate* qstate, int ret_rc, |
389 | | struct dns_msg* ret_msg) |
390 | 0 | { |
391 | 0 | int rcode; |
392 | | |
393 | | /* If the CD bit is on in the original request, then you could think |
394 | | * that we don't bother to validate anything. |
395 | | * But this is signalled internally with the valrec flag. |
396 | | * User queries are validated with BIT_CD to make our cache clean |
397 | | * so that bogus messages get retried by the upstream also for |
398 | | * downstream validators that set BIT_CD. |
399 | | * For DNS64 bit_cd signals no dns64 processing, but we want to |
400 | | * provide validation there too */ |
401 | | /* |
402 | | if((qstate->query_flags & BIT_CD)) { |
403 | | verbose(VERB_ALGO, "not validating response due to CD bit"); |
404 | | return 0; |
405 | | } |
406 | | */ |
407 | 0 | if(qstate->is_valrec) { |
408 | 0 | verbose(VERB_ALGO, "not validating response, is valrec" |
409 | 0 | "(validation recursion lookup)"); |
410 | 0 | return 0; |
411 | 0 | } |
412 | | |
413 | 0 | if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg) |
414 | 0 | rcode = ret_rc; |
415 | 0 | else rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags); |
416 | |
|
417 | 0 | if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) { |
418 | 0 | if(verbosity >= VERB_ALGO) { |
419 | 0 | char rc[16]; |
420 | 0 | rc[0]=0; |
421 | 0 | (void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc)); |
422 | 0 | verbose(VERB_ALGO, "cannot validate non-answer, rcode %s", rc); |
423 | 0 | } |
424 | 0 | return 0; |
425 | 0 | } |
426 | | |
427 | | /* cannot validate positive RRSIG response. (negatives can) */ |
428 | 0 | if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG && |
429 | 0 | rcode == LDNS_RCODE_NOERROR && ret_msg && |
430 | 0 | ret_msg->rep->an_numrrsets > 0) { |
431 | 0 | verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs."); |
432 | 0 | return 0; |
433 | 0 | } |
434 | 0 | return 1; |
435 | 0 | } |
436 | | |
437 | | /** |
438 | | * Check to see if the response has already been validated. |
439 | | * @param ret_msg: return msg, can be NULL |
440 | | * @return true if the response has already been validated |
441 | | */ |
442 | | static int |
443 | | already_validated(struct dns_msg* ret_msg) |
444 | 0 | { |
445 | | /* validate unchecked, and re-validate bogus messages */ |
446 | 0 | if (ret_msg && ret_msg->rep->security > sec_status_bogus) |
447 | 0 | { |
448 | 0 | verbose(VERB_ALGO, "response has already been validated: %s", |
449 | 0 | sec_status_to_string(ret_msg->rep->security)); |
450 | 0 | return 1; |
451 | 0 | } |
452 | 0 | return 0; |
453 | 0 | } |
454 | | |
455 | | /** |
456 | | * Generate a request for DNS data. |
457 | | * |
458 | | * @param qstate: query state that is the parent. |
459 | | * @param id: module id. |
460 | | * @param name: what name to query for. |
461 | | * @param namelen: length of name. |
462 | | * @param qtype: query type. |
463 | | * @param qclass: query class. |
464 | | * @param flags: additional flags, such as the CD bit (BIT_CD), or 0. |
465 | | * @param newq: If the subquery is newly created, it is returned, |
466 | | * otherwise NULL is returned |
467 | | * @param detached: true if this qstate should not attach to the subquery |
468 | | * @return false on alloc failure. |
469 | | */ |
470 | | static int |
471 | | generate_request(struct module_qstate* qstate, int id, uint8_t* name, |
472 | | size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags, |
473 | | struct module_qstate** newq, int detached) |
474 | 0 | { |
475 | 0 | struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id]; |
476 | 0 | struct query_info ask; |
477 | 0 | int valrec; |
478 | 0 | ask.qname = name; |
479 | 0 | ask.qname_len = namelen; |
480 | 0 | ask.qtype = qtype; |
481 | 0 | ask.qclass = qclass; |
482 | 0 | ask.local_alias = NULL; |
483 | 0 | log_query_info(VERB_ALGO, "generate request", &ask); |
484 | | /* enable valrec flag to avoid recursion to the same validation |
485 | | * routine, this lookup is simply a lookup. */ |
486 | 0 | valrec = 1; |
487 | |
|
488 | 0 | fptr_ok(fptr_whitelist_modenv_detect_cycle(qstate->env->detect_cycle)); |
489 | 0 | if((*qstate->env->detect_cycle)(qstate, &ask, |
490 | 0 | (uint16_t)(BIT_RD|flags), 0, valrec)) { |
491 | 0 | verbose(VERB_ALGO, "Could not generate request: cycle detected"); |
492 | 0 | return 0; |
493 | 0 | } |
494 | | |
495 | 0 | if(detached) { |
496 | 0 | struct mesh_state* sub = NULL; |
497 | 0 | fptr_ok(fptr_whitelist_modenv_add_sub( |
498 | 0 | qstate->env->add_sub)); |
499 | 0 | if(!(*qstate->env->add_sub)(qstate, &ask, |
500 | 0 | (uint16_t)(BIT_RD|flags), 0, valrec, newq, &sub)){ |
501 | 0 | log_err("Could not generate request: out of memory"); |
502 | 0 | return 0; |
503 | 0 | } |
504 | 0 | } |
505 | 0 | else { |
506 | 0 | fptr_ok(fptr_whitelist_modenv_attach_sub( |
507 | 0 | qstate->env->attach_sub)); |
508 | 0 | if(!(*qstate->env->attach_sub)(qstate, &ask, |
509 | 0 | (uint16_t)(BIT_RD|flags), 0, valrec, newq)){ |
510 | 0 | log_err("Could not generate request: out of memory"); |
511 | 0 | return 0; |
512 | 0 | } |
513 | 0 | } |
514 | | /* newq; validator does not need state created for that |
515 | | * query, and its a 'normal' for iterator as well */ |
516 | 0 | if(*newq) { |
517 | | /* add our blacklist to the query blacklist */ |
518 | 0 | sock_list_merge(&(*newq)->blacklist, (*newq)->region, |
519 | 0 | vq->chain_blacklist); |
520 | 0 | } |
521 | 0 | qstate->ext_state[id] = module_wait_subquery; |
522 | 0 | return 1; |
523 | 0 | } |
524 | | |
525 | | /** |
526 | | * Generate, send and detach key tag signaling query. |
527 | | * |
528 | | * @param qstate: query state. |
529 | | * @param id: module id. |
530 | | * @param ta: trust anchor, locked. |
531 | | * @return false on a processing error. |
532 | | */ |
533 | | static int |
534 | | generate_keytag_query(struct module_qstate* qstate, int id, |
535 | | struct trust_anchor* ta) |
536 | 0 | { |
537 | | /* 3 bytes for "_ta", 5 bytes per tag (4 bytes + "-") */ |
538 | 0 | #define MAX_LABEL_TAGS (LDNS_MAX_LABELLEN-3)/5 |
539 | 0 | size_t i, numtag; |
540 | 0 | uint16_t tags[MAX_LABEL_TAGS]; |
541 | 0 | char tagstr[LDNS_MAX_LABELLEN+1] = "_ta"; /* +1 for NULL byte */ |
542 | 0 | size_t tagstr_left = sizeof(tagstr) - strlen(tagstr); |
543 | 0 | char* tagstr_pos = tagstr + strlen(tagstr); |
544 | 0 | uint8_t dnamebuf[LDNS_MAX_DOMAINLEN+1]; /* +1 for label length byte */ |
545 | 0 | size_t dnamebuf_len = sizeof(dnamebuf); |
546 | 0 | uint8_t* keytagdname; |
547 | 0 | struct module_qstate* newq = NULL; |
548 | 0 | enum module_ext_state ext_state = qstate->ext_state[id]; |
549 | |
|
550 | 0 | numtag = anchor_list_keytags(ta, tags, MAX_LABEL_TAGS); |
551 | 0 | if(numtag == 0) |
552 | 0 | return 0; |
553 | | |
554 | 0 | for(i=0; i<numtag; i++) { |
555 | | /* Buffer can't overflow; numtag is limited to tags that fit in |
556 | | * the buffer. */ |
557 | 0 | snprintf(tagstr_pos, tagstr_left, "-%04x", (unsigned)tags[i]); |
558 | 0 | tagstr_left -= strlen(tagstr_pos); |
559 | 0 | tagstr_pos += strlen(tagstr_pos); |
560 | 0 | } |
561 | |
|
562 | 0 | sldns_str2wire_dname_buf_origin(tagstr, dnamebuf, &dnamebuf_len, |
563 | 0 | ta->name, ta->namelen); |
564 | 0 | if(!(keytagdname = (uint8_t*)regional_alloc_init(qstate->region, |
565 | 0 | dnamebuf, dnamebuf_len))) { |
566 | 0 | log_err("could not generate key tag query: out of memory"); |
567 | 0 | return 0; |
568 | 0 | } |
569 | | |
570 | 0 | log_nametypeclass(VERB_OPS, "generate keytag query", keytagdname, |
571 | 0 | LDNS_RR_TYPE_NULL, ta->dclass); |
572 | 0 | if(!generate_request(qstate, id, keytagdname, dnamebuf_len, |
573 | 0 | LDNS_RR_TYPE_NULL, ta->dclass, 0, &newq, 1)) { |
574 | 0 | verbose(VERB_ALGO, "failed to generate key tag signaling request"); |
575 | 0 | return 0; |
576 | 0 | } |
577 | | |
578 | | /* Not interested in subquery response. Restore the ext_state, |
579 | | * that might be changed by generate_request() */ |
580 | 0 | qstate->ext_state[id] = ext_state; |
581 | |
|
582 | 0 | return 1; |
583 | 0 | } |
584 | | |
585 | | /** |
586 | | * Get keytag as uint16_t from string |
587 | | * |
588 | | * @param start: start of string containing keytag |
589 | | * @param keytag: pointer where to store the extracted keytag |
590 | | * @return: 1 if keytag was extracted, else 0. |
591 | | */ |
592 | | static int |
593 | 0 | sentinel_get_keytag(char* start, uint16_t* keytag) { |
594 | 0 | char* keytag_str; |
595 | 0 | char* e = NULL; |
596 | 0 | keytag_str = calloc(1, SENTINEL_KEYTAG_LEN + 1 /* null byte */); |
597 | 0 | if(!keytag_str) |
598 | 0 | return 0; |
599 | 0 | memmove(keytag_str, start, SENTINEL_KEYTAG_LEN); |
600 | 0 | keytag_str[SENTINEL_KEYTAG_LEN] = '\0'; |
601 | 0 | *keytag = (uint16_t)strtol(keytag_str, &e, 10); |
602 | 0 | if(!e || *e != '\0') { |
603 | 0 | free(keytag_str); |
604 | 0 | return 0; |
605 | 0 | } |
606 | 0 | free(keytag_str); |
607 | 0 | return 1; |
608 | 0 | } |
609 | | |
610 | | /** |
611 | | * Prime trust anchor for use. |
612 | | * Generate and dispatch a priming query for the given trust anchor. |
613 | | * The trust anchor can be DNSKEY or DS and does not have to be signed. |
614 | | * |
615 | | * @param qstate: query state. |
616 | | * @param vq: validator query state. |
617 | | * @param id: module id. |
618 | | * @param toprime: what to prime. |
619 | | * @return false on a processing error. |
620 | | */ |
621 | | static int |
622 | | prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, |
623 | | int id, struct trust_anchor* toprime) |
624 | 0 | { |
625 | 0 | struct module_qstate* newq = NULL; |
626 | 0 | int ret = generate_request(qstate, id, toprime->name, toprime->namelen, |
627 | 0 | LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD, &newq, 0); |
628 | |
|
629 | 0 | if(newq && qstate->env->cfg->trust_anchor_signaling && |
630 | 0 | !generate_keytag_query(qstate, id, toprime)) { |
631 | 0 | verbose(VERB_ALGO, "keytag signaling query failed"); |
632 | 0 | return 0; |
633 | 0 | } |
634 | | |
635 | 0 | if(!ret) { |
636 | 0 | verbose(VERB_ALGO, "Could not prime trust anchor"); |
637 | 0 | return 0; |
638 | 0 | } |
639 | | /* ignore newq; validator does not need state created for that |
640 | | * query, and its a 'normal' for iterator as well */ |
641 | 0 | vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing |
642 | | from the validator inform_super() routine */ |
643 | | /* store trust anchor name for later lookup when prime returns */ |
644 | 0 | vq->trust_anchor_name = regional_alloc_init(qstate->region, |
645 | 0 | toprime->name, toprime->namelen); |
646 | 0 | vq->trust_anchor_len = toprime->namelen; |
647 | 0 | vq->trust_anchor_labs = toprime->namelabs; |
648 | 0 | if(!vq->trust_anchor_name) { |
649 | 0 | log_err("Could not prime trust anchor: out of memory"); |
650 | 0 | return 0; |
651 | 0 | } |
652 | 0 | return 1; |
653 | 0 | } |
654 | | |
655 | | /** |
656 | | * Validate if the ANSWER and AUTHORITY sections contain valid rrsets. |
657 | | * They must be validly signed with the given key. |
658 | | * Tries to validate ADDITIONAL rrsets as well, but only to check them. |
659 | | * Allows unsigned CNAME after a DNAME that expands the DNAME. |
660 | | * |
661 | | * Note that by the time this method is called, the process of finding the |
662 | | * trusted DNSKEY rrset that signs this response must already have been |
663 | | * completed. |
664 | | * |
665 | | * @param qstate: query state. |
666 | | * @param vq: validator query state. |
667 | | * @param env: module env for verify. |
668 | | * @param ve: validator env for verify. |
669 | | * @param chase_reply: answer to validate. |
670 | | * @param key_entry: the key entry, which is trusted, and which matches |
671 | | * the signer of the answer. The key entry isgood(). |
672 | | * @param suspend: returned true if the task takes too long and needs to |
673 | | * suspend to continue the effort later. |
674 | | * @return false if any of the rrsets in the an or ns sections of the message |
675 | | * fail to verify. The message is then set to bogus. |
676 | | */ |
677 | | static int |
678 | | validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, |
679 | | struct module_env* env, struct val_env* ve, |
680 | | struct reply_info* chase_reply, struct key_entry_key* key_entry, |
681 | | int* suspend) |
682 | 0 | { |
683 | 0 | uint8_t* sname; |
684 | 0 | size_t i, slen; |
685 | 0 | struct ub_packed_rrset_key* s; |
686 | 0 | enum sec_status sec; |
687 | 0 | int num_verifies = 0, verified, have_state = 0; |
688 | 0 | char reasonbuf[256]; |
689 | 0 | char* reason = NULL; |
690 | 0 | sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; |
691 | 0 | *suspend = 0; |
692 | 0 | if(vq->msg_signatures_state) { |
693 | | /* Pick up the state, and reset it, may not be needed now. */ |
694 | 0 | vq->msg_signatures_state = 0; |
695 | 0 | have_state = 1; |
696 | 0 | } |
697 | | |
698 | | /* validate the ANSWER section */ |
699 | 0 | for(i=0; i<chase_reply->an_numrrsets; i++) { |
700 | 0 | if(have_state && i <= vq->msg_signatures_index) |
701 | 0 | continue; |
702 | 0 | s = chase_reply->rrsets[i]; |
703 | | /* Skip the CNAME following a (validated) DNAME. |
704 | | * Because of the normalization routines in the iterator, |
705 | | * there will always be an unsigned CNAME following a DNAME |
706 | | * (unless qtype=DNAME in the answer part). */ |
707 | 0 | if(i>0 && ntohs(chase_reply->rrsets[i-1]->rk.type) == |
708 | 0 | LDNS_RR_TYPE_DNAME && |
709 | 0 | ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && |
710 | 0 | ((struct packed_rrset_data*)chase_reply->rrsets[i-1]->entry.data)->security == sec_status_secure && |
711 | 0 | dname_strict_subdomain_c(s->rk.dname, chase_reply->rrsets[i-1]->rk.dname) |
712 | 0 | ) { |
713 | | /* CNAME was synthesized by our own iterator */ |
714 | | /* since the DNAME verified, mark the CNAME as secure */ |
715 | 0 | ((struct packed_rrset_data*)s->entry.data)->security = |
716 | 0 | sec_status_secure; |
717 | 0 | ((struct packed_rrset_data*)s->entry.data)->trust = |
718 | 0 | rrset_trust_validated; |
719 | 0 | continue; |
720 | 0 | } |
721 | | |
722 | | /* Verify the answer rrset */ |
723 | 0 | sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, |
724 | 0 | &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified, |
725 | 0 | reasonbuf, sizeof(reasonbuf)); |
726 | | /* If the (answer) rrset failed to validate, then this |
727 | | * message is BAD. */ |
728 | 0 | if(sec != sec_status_secure) { |
729 | 0 | log_nametypeclass(VERB_QUERY, "validator: response " |
730 | 0 | "has failed ANSWER rrset:", s->rk.dname, |
731 | 0 | ntohs(s->rk.type), ntohs(s->rk.rrset_class)); |
732 | 0 | errinf_ede(qstate, reason, reason_bogus); |
733 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) |
734 | 0 | errinf(qstate, "for CNAME"); |
735 | 0 | else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) |
736 | 0 | errinf(qstate, "for DNAME"); |
737 | 0 | errinf_origin(qstate, qstate->reply_origin); |
738 | 0 | chase_reply->security = sec_status_bogus; |
739 | 0 | update_reason_bogus(chase_reply, reason_bogus); |
740 | |
|
741 | 0 | return 0; |
742 | 0 | } |
743 | | |
744 | 0 | num_verifies += verified; |
745 | 0 | if(num_verifies > MAX_VALIDATE_AT_ONCE && |
746 | 0 | i+1 < (env->cfg->val_clean_additional? |
747 | 0 | chase_reply->an_numrrsets+chase_reply->ns_numrrsets: |
748 | 0 | chase_reply->rrset_count)) { |
749 | | /* If the number of RRSIGs exceeds the maximum in |
750 | | * one go, suspend. Only suspend if there is a next |
751 | | * rrset to verify, i+1<loopmax. Store where to |
752 | | * continue later. */ |
753 | 0 | *suspend = 1; |
754 | 0 | vq->msg_signatures_state = 1; |
755 | 0 | vq->msg_signatures_index = i; |
756 | 0 | verbose(VERB_ALGO, "msg signature validation " |
757 | 0 | "suspended"); |
758 | 0 | return 0; |
759 | 0 | } |
760 | 0 | } |
761 | | |
762 | | /* validate the AUTHORITY section */ |
763 | 0 | for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ |
764 | 0 | chase_reply->ns_numrrsets; i++) { |
765 | 0 | if(have_state && i <= vq->msg_signatures_index) |
766 | 0 | continue; |
767 | 0 | s = chase_reply->rrsets[i]; |
768 | 0 | sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, |
769 | 0 | &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, |
770 | 0 | &verified, reasonbuf, sizeof(reasonbuf)); |
771 | | /* If anything in the authority section fails to be secure, |
772 | | * we have a bad message. */ |
773 | 0 | if(sec != sec_status_secure) { |
774 | 0 | log_nametypeclass(VERB_QUERY, "validator: response " |
775 | 0 | "has failed AUTHORITY rrset:", s->rk.dname, |
776 | 0 | ntohs(s->rk.type), ntohs(s->rk.rrset_class)); |
777 | 0 | errinf_ede(qstate, reason, reason_bogus); |
778 | 0 | errinf_origin(qstate, qstate->reply_origin); |
779 | 0 | errinf_rrset(qstate, s); |
780 | 0 | chase_reply->security = sec_status_bogus; |
781 | 0 | update_reason_bogus(chase_reply, reason_bogus); |
782 | 0 | return 0; |
783 | 0 | } |
784 | 0 | num_verifies += verified; |
785 | 0 | if(num_verifies > MAX_VALIDATE_AT_ONCE && |
786 | 0 | i+1 < (env->cfg->val_clean_additional? |
787 | 0 | chase_reply->an_numrrsets+chase_reply->ns_numrrsets: |
788 | 0 | chase_reply->rrset_count)) { |
789 | 0 | *suspend = 1; |
790 | 0 | vq->msg_signatures_state = 1; |
791 | 0 | vq->msg_signatures_index = i; |
792 | 0 | verbose(VERB_ALGO, "msg signature validation " |
793 | 0 | "suspended"); |
794 | 0 | return 0; |
795 | 0 | } |
796 | 0 | } |
797 | | |
798 | | /* If set, the validator should clean the additional section of |
799 | | * secure messages. */ |
800 | 0 | if(!env->cfg->val_clean_additional) |
801 | 0 | return 1; |
802 | | /* attempt to validate the ADDITIONAL section rrsets */ |
803 | 0 | for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; |
804 | 0 | i<chase_reply->rrset_count; i++) { |
805 | 0 | if(have_state && i <= vq->msg_signatures_index) |
806 | 0 | continue; |
807 | 0 | s = chase_reply->rrsets[i]; |
808 | | /* only validate rrs that have signatures with the key */ |
809 | | /* leave others unchecked, those get removed later on too */ |
810 | 0 | val_find_rrset_signer(s, &sname, &slen); |
811 | |
|
812 | 0 | verified = 0; |
813 | 0 | if(sname && query_dname_compare(sname, key_entry->name)==0) |
814 | 0 | (void)val_verify_rrset_entry(env, ve, s, key_entry, |
815 | 0 | &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, |
816 | 0 | &verified, reasonbuf, sizeof(reasonbuf)); |
817 | | /* the additional section can fail to be secure, |
818 | | * it is optional, check signature in case we need |
819 | | * to clean the additional section later. */ |
820 | 0 | num_verifies += verified; |
821 | 0 | if(num_verifies > MAX_VALIDATE_AT_ONCE && |
822 | 0 | i+1 < chase_reply->rrset_count) { |
823 | 0 | *suspend = 1; |
824 | 0 | vq->msg_signatures_state = 1; |
825 | 0 | vq->msg_signatures_index = i; |
826 | 0 | verbose(VERB_ALGO, "msg signature validation " |
827 | 0 | "suspended"); |
828 | 0 | return 0; |
829 | 0 | } |
830 | 0 | } |
831 | | |
832 | 0 | return 1; |
833 | 0 | } |
834 | | |
835 | | void |
836 | | validate_suspend_timer_cb(void* arg) |
837 | 0 | { |
838 | 0 | struct module_qstate* qstate = (struct module_qstate*)arg; |
839 | 0 | verbose(VERB_ALGO, "validate_suspend timer, continue"); |
840 | 0 | mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass, |
841 | 0 | NULL); |
842 | 0 | } |
843 | | |
844 | | /** Setup timer to continue validation of msg signatures later */ |
845 | | static int |
846 | | validate_suspend_setup_timer(struct module_qstate* qstate, |
847 | | struct val_qstate* vq, int id, enum val_state resume_state) |
848 | 0 | { |
849 | 0 | struct timeval tv; |
850 | 0 | int usec, slack, base; |
851 | 0 | if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) { |
852 | 0 | verbose(VERB_ALGO, "validate_suspend timer: " |
853 | 0 | "reached MAX_VALIDATION_SUSPENDS (%d); error out", |
854 | 0 | MAX_VALIDATION_SUSPENDS); |
855 | 0 | errinf(qstate, "max validation suspends reached, " |
856 | 0 | "too many RRSIG validations"); |
857 | 0 | return 0; |
858 | 0 | } |
859 | 0 | verbose(VERB_ALGO, "validate_suspend timer, set for suspend"); |
860 | 0 | vq->state = resume_state; |
861 | 0 | qstate->ext_state[id] = module_wait_reply; |
862 | 0 | if(!vq->suspend_timer) { |
863 | 0 | vq->suspend_timer = comm_timer_create( |
864 | 0 | qstate->env->worker_base, |
865 | 0 | validate_suspend_timer_cb, qstate); |
866 | 0 | if(!vq->suspend_timer) { |
867 | 0 | log_err("validate_suspend_setup_timer: " |
868 | 0 | "out of memory for comm_timer_create"); |
869 | 0 | return 0; |
870 | 0 | } |
871 | 0 | } |
872 | | /* The timer is activated later, after other events in the event |
873 | | * loop have been processed. The query state can also be deleted, |
874 | | * when the list is full and query states are dropped. */ |
875 | | /* Extend wait time if there are a lot of queries or if this one |
876 | | * is taking long, to keep around cpu time for ordinary queries. */ |
877 | 0 | usec = 50000; /* 50 msec */ |
878 | 0 | slack = 0; |
879 | 0 | if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states) |
880 | 0 | slack += 3; |
881 | 0 | else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2) |
882 | 0 | slack += 2; |
883 | 0 | else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4) |
884 | 0 | slack += 1; |
885 | 0 | if(vq->suspend_count > 3) |
886 | 0 | slack += 3; |
887 | 0 | else if(vq->suspend_count > 0) |
888 | 0 | slack += vq->suspend_count; |
889 | 0 | if(slack != 0 && slack <= 12 /* No numeric overflow. */) { |
890 | 0 | usec = usec << slack; |
891 | 0 | } |
892 | | /* Spread such timeouts within 90%-100% of the original timer. */ |
893 | 0 | base = usec * 9/10; |
894 | 0 | usec = base + ub_random_max(qstate->env->rnd, usec-base); |
895 | 0 | tv.tv_usec = (usec % 1000000); |
896 | 0 | tv.tv_sec = (usec / 1000000); |
897 | 0 | vq->suspend_count ++; |
898 | 0 | comm_timer_set(vq->suspend_timer, &tv); |
899 | 0 | return 1; |
900 | 0 | } |
901 | | |
902 | | /** |
903 | | * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding |
904 | | * and saw the NS record without signatures from a referral). |
905 | | * The positive response has a mangled authority section. |
906 | | * Remove that authority section and the additional section. |
907 | | * @param rep: reply |
908 | | * @return true if a wrongly truncated response. |
909 | | */ |
910 | | static int |
911 | | detect_wrongly_truncated(struct reply_info* rep) |
912 | 0 | { |
913 | 0 | size_t i; |
914 | | /* only NS in authority, and it is bogus */ |
915 | 0 | if(rep->ns_numrrsets != 1 || rep->an_numrrsets == 0) |
916 | 0 | return 0; |
917 | 0 | if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS) |
918 | 0 | return 0; |
919 | 0 | if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ] |
920 | 0 | ->entry.data)->security == sec_status_secure) |
921 | 0 | return 0; |
922 | | /* answer section is present and secure */ |
923 | 0 | for(i=0; i<rep->an_numrrsets; i++) { |
924 | 0 | if(((struct packed_rrset_data*)rep->rrsets[ i ] |
925 | 0 | ->entry.data)->security != sec_status_secure) |
926 | 0 | return 0; |
927 | 0 | } |
928 | 0 | verbose(VERB_ALGO, "truncating to minimal response"); |
929 | 0 | return 1; |
930 | 0 | } |
931 | | |
932 | | /** |
933 | | * For messages that are not referrals, if the chase reply contains an |
934 | | * unsigned NS record in the authority section it could have been |
935 | | * inserted by a (BIND) forwarder that thinks the zone is insecure, and |
936 | | * that has an NS record without signatures in cache. Remove the NS |
937 | | * record since the reply does not hinge on that record (in the authority |
938 | | * section), but do not remove it if it removes the last record from the |
939 | | * answer+authority sections. |
940 | | * @param chase_reply: the chased reply, we have a key for this contents, |
941 | | * so we should have signatures for these rrsets and not having |
942 | | * signatures means it will be bogus. |
943 | | * @param orig_reply: original reply, remove NS from there as well because |
944 | | * we cannot mark the NS record as DNSSEC valid because it is not |
945 | | * validated by signatures. |
946 | | */ |
947 | | static void |
948 | | remove_spurious_authority(struct reply_info* chase_reply, |
949 | | struct reply_info* orig_reply) |
950 | 0 | { |
951 | 0 | size_t i, found = 0; |
952 | 0 | int remove = 0; |
953 | | /* if no answer and only 1 auth RRset, do not remove that one */ |
954 | 0 | if(chase_reply->an_numrrsets == 0 && chase_reply->ns_numrrsets == 1) |
955 | 0 | return; |
956 | | /* search authority section for unsigned NS records */ |
957 | 0 | for(i = chase_reply->an_numrrsets; |
958 | 0 | i < chase_reply->an_numrrsets+chase_reply->ns_numrrsets; i++) { |
959 | 0 | struct packed_rrset_data* d = (struct packed_rrset_data*) |
960 | 0 | chase_reply->rrsets[i]->entry.data; |
961 | 0 | if(ntohs(chase_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS |
962 | 0 | && d->rrsig_count == 0) { |
963 | 0 | found = i; |
964 | 0 | remove = 1; |
965 | 0 | break; |
966 | 0 | } |
967 | 0 | } |
968 | | /* see if we found the entry */ |
969 | 0 | if(!remove) return; |
970 | 0 | log_rrset_key(VERB_ALGO, "Removing spurious unsigned NS record " |
971 | 0 | "(likely inserted by forwarder)", chase_reply->rrsets[found]); |
972 | | |
973 | | /* find rrset in orig_reply */ |
974 | 0 | for(i = orig_reply->an_numrrsets; |
975 | 0 | i < orig_reply->an_numrrsets+orig_reply->ns_numrrsets; i++) { |
976 | 0 | if(ntohs(orig_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS |
977 | 0 | && query_dname_compare(orig_reply->rrsets[i]->rk.dname, |
978 | 0 | chase_reply->rrsets[found]->rk.dname) == 0) { |
979 | | /* remove from orig_msg */ |
980 | 0 | val_reply_remove_auth(orig_reply, i); |
981 | 0 | break; |
982 | 0 | } |
983 | 0 | } |
984 | | /* remove rrset from chase_reply */ |
985 | 0 | val_reply_remove_auth(chase_reply, found); |
986 | 0 | } |
987 | | |
988 | | /** |
989 | | * Given a "positive" response -- a response that contains an answer to the |
990 | | * question, and no CNAME chain, validate this response. |
991 | | * |
992 | | * The answer and authority RRsets must already be verified as secure. |
993 | | * |
994 | | * @param env: module env for verify. |
995 | | * @param ve: validator env for verify. |
996 | | * @param qchase: query that was made. |
997 | | * @param chase_reply: answer to that query to validate. |
998 | | * @param kkey: the key entry, which is trusted, and which matches |
999 | | * the signer of the answer. The key entry isgood(). |
1000 | | * @param qstate: query state for the region. |
1001 | | * @param vq: validator state for the nsec3 cache table. |
1002 | | * @param nsec3_calculations: current nsec3 hash calculations. |
1003 | | * @param suspend: returned true if the task takes too long and needs to |
1004 | | * suspend to continue the effort later. |
1005 | | */ |
1006 | | static void |
1007 | | validate_positive_response(struct module_env* env, struct val_env* ve, |
1008 | | struct query_info* qchase, struct reply_info* chase_reply, |
1009 | | struct key_entry_key* kkey, struct module_qstate* qstate, |
1010 | | struct val_qstate* vq, int* nsec3_calculations, int* suspend) |
1011 | 0 | { |
1012 | 0 | uint8_t* wc = NULL; |
1013 | 0 | size_t wl; |
1014 | 0 | int wc_cached = 0; |
1015 | 0 | int wc_NSEC_ok = 0; |
1016 | 0 | int nsec3s_seen = 0; |
1017 | 0 | size_t i; |
1018 | 0 | struct ub_packed_rrset_key* s; |
1019 | 0 | *suspend = 0; |
1020 | | |
1021 | | /* validate the ANSWER section - this will be the answer itself */ |
1022 | 0 | for(i=0; i<chase_reply->an_numrrsets; i++) { |
1023 | 0 | s = chase_reply->rrsets[i]; |
1024 | | |
1025 | | /* Check to see if the rrset is the result of a wildcard |
1026 | | * expansion. If so, an additional check will need to be |
1027 | | * made in the authority section. */ |
1028 | 0 | if(!val_rrset_wildcard(s, &wc, &wl)) { |
1029 | 0 | log_nametypeclass(VERB_QUERY, "Positive response has " |
1030 | 0 | "inconsistent wildcard sigs:", s->rk.dname, |
1031 | 0 | ntohs(s->rk.type), ntohs(s->rk.rrset_class)); |
1032 | 0 | chase_reply->security = sec_status_bogus; |
1033 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1034 | 0 | return; |
1035 | 0 | } |
1036 | 0 | if(wc && !wc_cached && env->cfg->aggressive_nsec) { |
1037 | 0 | rrset_cache_update_wildcard(env->rrset_cache, s, wc, wl, |
1038 | 0 | env->alloc, *env->now); |
1039 | 0 | wc_cached = 1; |
1040 | 0 | } |
1041 | |
|
1042 | 0 | } |
1043 | | |
1044 | | /* validate the AUTHORITY section as well - this will generally be |
1045 | | * the NS rrset (which could be missing, no problem) */ |
1046 | 0 | for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ |
1047 | 0 | chase_reply->ns_numrrsets; i++) { |
1048 | 0 | s = chase_reply->rrsets[i]; |
1049 | | |
1050 | | /* If this is a positive wildcard response, and we have a |
1051 | | * (just verified) NSEC record, try to use it to 1) prove |
1052 | | * that qname doesn't exist and 2) that the correct wildcard |
1053 | | * was used. */ |
1054 | 0 | if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { |
1055 | 0 | if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { |
1056 | 0 | wc_NSEC_ok = 1; |
1057 | 0 | } |
1058 | | /* if not, continue looking for proof */ |
1059 | 0 | } |
1060 | | |
1061 | | /* Otherwise, if this is a positive wildcard response and |
1062 | | * we have NSEC3 records */ |
1063 | 0 | if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { |
1064 | 0 | nsec3s_seen = 1; |
1065 | 0 | } |
1066 | 0 | } |
1067 | | |
1068 | | /* If this was a positive wildcard response that we haven't already |
1069 | | * proven, and we have NSEC3 records, try to prove it using the NSEC3 |
1070 | | * records. */ |
1071 | 0 | if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && |
1072 | 0 | nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { |
1073 | 0 | enum sec_status sec = nsec3_prove_wildcard(env, ve, |
1074 | 0 | chase_reply->rrsets+chase_reply->an_numrrsets, |
1075 | 0 | chase_reply->ns_numrrsets, qchase, kkey, wc, |
1076 | 0 | &vq->nsec3_cache_table, nsec3_calculations); |
1077 | 0 | if(sec == sec_status_insecure) { |
1078 | 0 | verbose(VERB_ALGO, "Positive wildcard response is " |
1079 | 0 | "insecure"); |
1080 | 0 | chase_reply->security = sec_status_insecure; |
1081 | 0 | return; |
1082 | 0 | } else if(sec == sec_status_secure) { |
1083 | 0 | wc_NSEC_ok = 1; |
1084 | 0 | } else if(sec == sec_status_unchecked) { |
1085 | 0 | *suspend = 1; |
1086 | 0 | return; |
1087 | 0 | } |
1088 | 0 | } |
1089 | | |
1090 | | /* If after all this, we still haven't proven the positive wildcard |
1091 | | * response, fail. */ |
1092 | 0 | if(wc != NULL && !wc_NSEC_ok) { |
1093 | 0 | verbose(VERB_QUERY, "positive response was wildcard " |
1094 | 0 | "expansion and did not prove original data " |
1095 | 0 | "did not exist"); |
1096 | 0 | chase_reply->security = sec_status_bogus; |
1097 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1098 | 0 | return; |
1099 | 0 | } |
1100 | | |
1101 | 0 | verbose(VERB_ALGO, "Successfully validated positive response"); |
1102 | 0 | chase_reply->security = sec_status_secure; |
1103 | 0 | } |
1104 | | |
1105 | | /** |
1106 | | * Validate a NOERROR/NODATA signed response -- a response that has a |
1107 | | * NOERROR Rcode but no ANSWER section RRsets. This consists of making |
1108 | | * certain that the authority section NSEC/NSEC3s proves that the qname |
1109 | | * does exist and the qtype doesn't. |
1110 | | * |
1111 | | * The answer and authority RRsets must already be verified as secure. |
1112 | | * |
1113 | | * @param env: module env for verify. |
1114 | | * @param ve: validator env for verify. |
1115 | | * @param qchase: query that was made. |
1116 | | * @param chase_reply: answer to that query to validate. |
1117 | | * @param kkey: the key entry, which is trusted, and which matches |
1118 | | * the signer of the answer. The key entry isgood(). |
1119 | | * @param qstate: query state for the region. |
1120 | | * @param vq: validator state for the nsec3 cache table. |
1121 | | * @param nsec3_calculations: current nsec3 hash calculations. |
1122 | | * @param suspend: returned true if the task takes too long and needs to |
1123 | | * suspend to continue the effort later. |
1124 | | */ |
1125 | | static void |
1126 | | validate_nodata_response(struct module_env* env, struct val_env* ve, |
1127 | | struct query_info* qchase, struct reply_info* chase_reply, |
1128 | | struct key_entry_key* kkey, struct module_qstate* qstate, |
1129 | | struct val_qstate* vq, int* nsec3_calculations, int* suspend) |
1130 | 0 | { |
1131 | | /* Since we are here, there must be nothing in the ANSWER section to |
1132 | | * validate. */ |
1133 | | /* (Note: CNAME/DNAME responses will not directly get here -- |
1134 | | * instead, they are chased down into individual CNAME validations, |
1135 | | * and at the end of the cname chain a POSITIVE, or CNAME_NOANSWER |
1136 | | * validation.) */ |
1137 | | |
1138 | | /* validate the AUTHORITY section */ |
1139 | 0 | int has_valid_nsec = 0; /* If true, then the NODATA has been proven.*/ |
1140 | 0 | uint8_t* ce = NULL; /* for wildcard nodata responses. This is the |
1141 | | proven closest encloser. */ |
1142 | 0 | uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ |
1143 | 0 | int nsec3s_seen = 0; /* nsec3s seen */ |
1144 | 0 | struct ub_packed_rrset_key* s; |
1145 | 0 | size_t i; |
1146 | 0 | *suspend = 0; |
1147 | |
|
1148 | 0 | for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ |
1149 | 0 | chase_reply->ns_numrrsets; i++) { |
1150 | 0 | s = chase_reply->rrsets[i]; |
1151 | | /* If we encounter an NSEC record, try to use it to prove |
1152 | | * NODATA. |
1153 | | * This needs to handle the ENT NODATA case. */ |
1154 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { |
1155 | 0 | if(nsec_proves_nodata(s, qchase, &wc)) { |
1156 | 0 | has_valid_nsec = 1; |
1157 | | /* sets wc-encloser if wildcard applicable */ |
1158 | 0 | } |
1159 | 0 | if(val_nsec_proves_name_error(s, qchase->qname)) { |
1160 | 0 | ce = nsec_closest_encloser(qchase->qname, s); |
1161 | 0 | } |
1162 | 0 | if(val_nsec_proves_insecuredelegation(s, qchase)) { |
1163 | 0 | verbose(VERB_ALGO, "delegation is insecure"); |
1164 | 0 | chase_reply->security = sec_status_insecure; |
1165 | 0 | return; |
1166 | 0 | } |
1167 | 0 | } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { |
1168 | 0 | nsec3s_seen = 1; |
1169 | 0 | } |
1170 | 0 | } |
1171 | | |
1172 | | /* check to see if we have a wildcard NODATA proof. */ |
1173 | | |
1174 | | /* The wildcard NODATA is 1 NSEC proving that qname does not exist |
1175 | | * (and also proving what the closest encloser is), and 1 NSEC |
1176 | | * showing the matching wildcard, which must be *.closest_encloser. */ |
1177 | 0 | if(wc && !ce) |
1178 | 0 | has_valid_nsec = 0; |
1179 | 0 | else if(wc && ce) { |
1180 | 0 | if(query_dname_compare(wc, ce) != 0) { |
1181 | 0 | has_valid_nsec = 0; |
1182 | 0 | } |
1183 | 0 | } |
1184 | | |
1185 | 0 | if(!has_valid_nsec && nsec3s_seen && |
1186 | 0 | nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { |
1187 | 0 | enum sec_status sec = nsec3_prove_nodata(env, ve, |
1188 | 0 | chase_reply->rrsets+chase_reply->an_numrrsets, |
1189 | 0 | chase_reply->ns_numrrsets, qchase, kkey, |
1190 | 0 | &vq->nsec3_cache_table, nsec3_calculations); |
1191 | 0 | if(sec == sec_status_insecure) { |
1192 | 0 | verbose(VERB_ALGO, "NODATA response is insecure"); |
1193 | 0 | chase_reply->security = sec_status_insecure; |
1194 | 0 | return; |
1195 | 0 | } else if(sec == sec_status_secure) { |
1196 | 0 | has_valid_nsec = 1; |
1197 | 0 | } else if(sec == sec_status_unchecked) { |
1198 | | /* check is incomplete; suspend */ |
1199 | 0 | *suspend = 1; |
1200 | 0 | return; |
1201 | 0 | } |
1202 | 0 | } |
1203 | | |
1204 | 0 | if(!has_valid_nsec) { |
1205 | 0 | verbose(VERB_QUERY, "NODATA response failed to prove NODATA " |
1206 | 0 | "status with NSEC/NSEC3"); |
1207 | 0 | if(verbosity >= VERB_ALGO) |
1208 | 0 | log_dns_msg("Failed NODATA", qchase, chase_reply); |
1209 | 0 | chase_reply->security = sec_status_bogus; |
1210 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1211 | 0 | return; |
1212 | 0 | } |
1213 | | |
1214 | 0 | verbose(VERB_ALGO, "successfully validated NODATA response."); |
1215 | 0 | chase_reply->security = sec_status_secure; |
1216 | 0 | } |
1217 | | |
1218 | | /** |
1219 | | * Validate a NAMEERROR signed response -- a response that has a NXDOMAIN |
1220 | | * Rcode. |
1221 | | * This consists of making certain that the authority section NSEC proves |
1222 | | * that the qname doesn't exist and the covering wildcard also doesn't exist.. |
1223 | | * |
1224 | | * The answer and authority RRsets must have already been verified as secure. |
1225 | | * |
1226 | | * @param env: module env for verify. |
1227 | | * @param ve: validator env for verify. |
1228 | | * @param qchase: query that was made. |
1229 | | * @param chase_reply: answer to that query to validate. |
1230 | | * @param kkey: the key entry, which is trusted, and which matches |
1231 | | * the signer of the answer. The key entry isgood(). |
1232 | | * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. |
1233 | | * @param qstate: query state for the region. |
1234 | | * @param vq: validator state for the nsec3 cache table. |
1235 | | * @param nsec3_calculations: current nsec3 hash calculations. |
1236 | | * @param suspend: returned true if the task takes too long and needs to |
1237 | | * suspend to continue the effort later. |
1238 | | */ |
1239 | | static void |
1240 | | validate_nameerror_response(struct module_env* env, struct val_env* ve, |
1241 | | struct query_info* qchase, struct reply_info* chase_reply, |
1242 | | struct key_entry_key* kkey, int* rcode, |
1243 | | struct module_qstate* qstate, struct val_qstate* vq, |
1244 | | int* nsec3_calculations, int* suspend) |
1245 | 0 | { |
1246 | 0 | int has_valid_nsec = 0; |
1247 | 0 | int has_valid_wnsec = 0; |
1248 | 0 | int nsec3s_seen = 0; |
1249 | 0 | struct ub_packed_rrset_key* s; |
1250 | 0 | size_t i; |
1251 | 0 | uint8_t* ce; |
1252 | 0 | int ce_labs = 0; |
1253 | 0 | int prev_ce_labs = 0; |
1254 | 0 | *suspend = 0; |
1255 | |
|
1256 | 0 | for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ |
1257 | 0 | chase_reply->ns_numrrsets; i++) { |
1258 | 0 | s = chase_reply->rrsets[i]; |
1259 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { |
1260 | 0 | if(val_nsec_proves_name_error(s, qchase->qname)) |
1261 | 0 | has_valid_nsec = 1; |
1262 | 0 | ce = nsec_closest_encloser(qchase->qname, s); |
1263 | 0 | ce_labs = dname_count_labels(ce); |
1264 | | /* Use longest closest encloser to prove wildcard. */ |
1265 | 0 | if(ce_labs > prev_ce_labs || |
1266 | 0 | (ce_labs == prev_ce_labs && |
1267 | 0 | has_valid_wnsec == 0)) { |
1268 | 0 | if(val_nsec_proves_no_wc(s, qchase->qname, |
1269 | 0 | qchase->qname_len)) |
1270 | 0 | has_valid_wnsec = 1; |
1271 | 0 | else |
1272 | 0 | has_valid_wnsec = 0; |
1273 | 0 | } |
1274 | 0 | prev_ce_labs = ce_labs; |
1275 | 0 | if(val_nsec_proves_insecuredelegation(s, qchase)) { |
1276 | 0 | verbose(VERB_ALGO, "delegation is insecure"); |
1277 | 0 | chase_reply->security = sec_status_insecure; |
1278 | 0 | return; |
1279 | 0 | } |
1280 | 0 | } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) |
1281 | 0 | nsec3s_seen = 1; |
1282 | 0 | } |
1283 | | |
1284 | 0 | if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen && |
1285 | 0 | nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { |
1286 | | /* use NSEC3 proof, both answer and auth rrsets, in case |
1287 | | * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ |
1288 | 0 | chase_reply->security = nsec3_prove_nameerror(env, ve, |
1289 | 0 | chase_reply->rrsets, chase_reply->an_numrrsets+ |
1290 | 0 | chase_reply->ns_numrrsets, qchase, kkey, |
1291 | 0 | &vq->nsec3_cache_table, nsec3_calculations); |
1292 | 0 | if(chase_reply->security == sec_status_unchecked) { |
1293 | 0 | *suspend = 1; |
1294 | 0 | return; |
1295 | 0 | } else if(chase_reply->security != sec_status_secure) { |
1296 | 0 | verbose(VERB_QUERY, "NameError response failed nsec, " |
1297 | 0 | "nsec3 proof was %s", sec_status_to_string( |
1298 | 0 | chase_reply->security)); |
1299 | 0 | return; |
1300 | 0 | } |
1301 | 0 | has_valid_nsec = 1; |
1302 | 0 | has_valid_wnsec = 1; |
1303 | 0 | } |
1304 | | |
1305 | | /* If the message fails to prove either condition, it is bogus. */ |
1306 | 0 | if(!has_valid_nsec) { |
1307 | 0 | validate_nodata_response(env, ve, qchase, chase_reply, kkey, |
1308 | 0 | qstate, vq, nsec3_calculations, suspend); |
1309 | 0 | if(*suspend) return; |
1310 | 0 | verbose(VERB_QUERY, "NameError response has failed to prove: " |
1311 | 0 | "qname does not exist"); |
1312 | | /* Be lenient with RCODE in NSEC NameError responses */ |
1313 | 0 | if(chase_reply->security == sec_status_secure) { |
1314 | 0 | *rcode = LDNS_RCODE_NOERROR; |
1315 | 0 | } else { |
1316 | 0 | chase_reply->security = sec_status_bogus; |
1317 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1318 | 0 | } |
1319 | 0 | return; |
1320 | 0 | } |
1321 | | |
1322 | 0 | if(!has_valid_wnsec) { |
1323 | 0 | validate_nodata_response(env, ve, qchase, chase_reply, kkey, |
1324 | 0 | qstate, vq, nsec3_calculations, suspend); |
1325 | 0 | if(*suspend) return; |
1326 | 0 | verbose(VERB_QUERY, "NameError response has failed to prove: " |
1327 | 0 | "covering wildcard does not exist"); |
1328 | | /* Be lenient with RCODE in NSEC NameError responses */ |
1329 | 0 | if (chase_reply->security == sec_status_secure) { |
1330 | 0 | *rcode = LDNS_RCODE_NOERROR; |
1331 | 0 | } else { |
1332 | 0 | chase_reply->security = sec_status_bogus; |
1333 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1334 | 0 | } |
1335 | 0 | return; |
1336 | 0 | } |
1337 | | |
1338 | | /* Otherwise, we consider the message secure. */ |
1339 | 0 | verbose(VERB_ALGO, "successfully validated NAME ERROR response."); |
1340 | 0 | chase_reply->security = sec_status_secure; |
1341 | 0 | } |
1342 | | |
1343 | | /** |
1344 | | * Given a referral response, validate rrsets and take least trusted rrset |
1345 | | * as the current validation status. |
1346 | | * |
1347 | | * Note that by the time this method is called, the process of finding the |
1348 | | * trusted DNSKEY rrset that signs this response must already have been |
1349 | | * completed. |
1350 | | * |
1351 | | * @param chase_reply: answer to validate. |
1352 | | */ |
1353 | | static void |
1354 | | validate_referral_response(struct reply_info* chase_reply) |
1355 | 0 | { |
1356 | 0 | size_t i; |
1357 | 0 | enum sec_status s; |
1358 | | /* message security equals lowest rrset security */ |
1359 | 0 | chase_reply->security = sec_status_secure; |
1360 | 0 | for(i=0; i<chase_reply->rrset_count; i++) { |
1361 | 0 | s = ((struct packed_rrset_data*)chase_reply->rrsets[i] |
1362 | 0 | ->entry.data)->security; |
1363 | 0 | if(s < chase_reply->security) |
1364 | 0 | chase_reply->security = s; |
1365 | 0 | } |
1366 | 0 | verbose(VERB_ALGO, "validated part of referral response as %s", |
1367 | 0 | sec_status_to_string(chase_reply->security)); |
1368 | 0 | } |
1369 | | |
1370 | | /** |
1371 | | * Given an "ANY" response -- a response that contains an answer to a |
1372 | | * qtype==ANY question, with answers. This does no checking that all |
1373 | | * types are present. |
1374 | | * |
1375 | | * NOTE: it may be possible to get parent-side delegation point records |
1376 | | * here, which won't all be signed. Right now, this routine relies on the |
1377 | | * upstream iterative resolver to not return these responses -- instead |
1378 | | * treating them as referrals. |
1379 | | * |
1380 | | * NOTE: RFC 4035 is silent on this issue, so this may change upon |
1381 | | * clarification. Clarification draft -05 says to not check all types are |
1382 | | * present. |
1383 | | * |
1384 | | * Note that by the time this method is called, the process of finding the |
1385 | | * trusted DNSKEY rrset that signs this response must already have been |
1386 | | * completed. |
1387 | | * |
1388 | | * @param env: module env for verify. |
1389 | | * @param ve: validator env for verify. |
1390 | | * @param qchase: query that was made. |
1391 | | * @param chase_reply: answer to that query to validate. |
1392 | | * @param kkey: the key entry, which is trusted, and which matches |
1393 | | * the signer of the answer. The key entry isgood(). |
1394 | | * @param qstate: query state for the region. |
1395 | | * @param vq: validator state for the nsec3 cache table. |
1396 | | * @param nsec3_calculations: current nsec3 hash calculations. |
1397 | | * @param suspend: returned true if the task takes too long and needs to |
1398 | | * suspend to continue the effort later. |
1399 | | */ |
1400 | | static void |
1401 | | validate_any_response(struct module_env* env, struct val_env* ve, |
1402 | | struct query_info* qchase, struct reply_info* chase_reply, |
1403 | | struct key_entry_key* kkey, struct module_qstate* qstate, |
1404 | | struct val_qstate* vq, int* nsec3_calculations, int* suspend) |
1405 | 0 | { |
1406 | | /* all answer and auth rrsets already verified */ |
1407 | | /* but check if a wildcard response is given, then check NSEC/NSEC3 |
1408 | | * for qname denial to see if wildcard is applicable */ |
1409 | 0 | uint8_t* wc = NULL; |
1410 | 0 | size_t wl; |
1411 | 0 | int wc_NSEC_ok = 0; |
1412 | 0 | int nsec3s_seen = 0; |
1413 | 0 | size_t i; |
1414 | 0 | struct ub_packed_rrset_key* s; |
1415 | 0 | *suspend = 0; |
1416 | |
|
1417 | 0 | if(qchase->qtype != LDNS_RR_TYPE_ANY) { |
1418 | 0 | log_err("internal error: ANY validation called for non-ANY"); |
1419 | 0 | chase_reply->security = sec_status_bogus; |
1420 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1421 | 0 | return; |
1422 | 0 | } |
1423 | | |
1424 | | /* validate the ANSWER section - this will be the answer itself */ |
1425 | 0 | for(i=0; i<chase_reply->an_numrrsets; i++) { |
1426 | 0 | s = chase_reply->rrsets[i]; |
1427 | | |
1428 | | /* Check to see if the rrset is the result of a wildcard |
1429 | | * expansion. If so, an additional check will need to be |
1430 | | * made in the authority section. */ |
1431 | 0 | if(!val_rrset_wildcard(s, &wc, &wl)) { |
1432 | 0 | log_nametypeclass(VERB_QUERY, "Positive ANY response" |
1433 | 0 | " has inconsistent wildcard sigs:", |
1434 | 0 | s->rk.dname, ntohs(s->rk.type), |
1435 | 0 | ntohs(s->rk.rrset_class)); |
1436 | 0 | chase_reply->security = sec_status_bogus; |
1437 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1438 | 0 | return; |
1439 | 0 | } |
1440 | 0 | } |
1441 | | |
1442 | | /* if it was a wildcard, check for NSEC/NSEC3s in both answer |
1443 | | * and authority sections (NSEC may be moved to the ANSWER section) */ |
1444 | 0 | if(wc != NULL) |
1445 | 0 | for(i=0; i<chase_reply->an_numrrsets+chase_reply->ns_numrrsets; |
1446 | 0 | i++) { |
1447 | 0 | s = chase_reply->rrsets[i]; |
1448 | | |
1449 | | /* If this is a positive wildcard response, and we have a |
1450 | | * (just verified) NSEC record, try to use it to 1) prove |
1451 | | * that qname doesn't exist and 2) that the correct wildcard |
1452 | | * was used. */ |
1453 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { |
1454 | 0 | if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { |
1455 | 0 | wc_NSEC_ok = 1; |
1456 | 0 | } |
1457 | | /* if not, continue looking for proof */ |
1458 | 0 | } |
1459 | | |
1460 | | /* Otherwise, if this is a positive wildcard response and |
1461 | | * we have NSEC3 records */ |
1462 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { |
1463 | 0 | nsec3s_seen = 1; |
1464 | 0 | } |
1465 | 0 | } |
1466 | | |
1467 | | /* If this was a positive wildcard response that we haven't already |
1468 | | * proven, and we have NSEC3 records, try to prove it using the NSEC3 |
1469 | | * records. */ |
1470 | 0 | if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && |
1471 | 0 | nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { |
1472 | | /* look both in answer and auth section for NSEC3s */ |
1473 | 0 | enum sec_status sec = nsec3_prove_wildcard(env, ve, |
1474 | 0 | chase_reply->rrsets, |
1475 | 0 | chase_reply->an_numrrsets+chase_reply->ns_numrrsets, |
1476 | 0 | qchase, kkey, wc, &vq->nsec3_cache_table, |
1477 | 0 | nsec3_calculations); |
1478 | 0 | if(sec == sec_status_insecure) { |
1479 | 0 | verbose(VERB_ALGO, "Positive ANY wildcard response is " |
1480 | 0 | "insecure"); |
1481 | 0 | chase_reply->security = sec_status_insecure; |
1482 | 0 | return; |
1483 | 0 | } else if(sec == sec_status_secure) { |
1484 | 0 | wc_NSEC_ok = 1; |
1485 | 0 | } else if(sec == sec_status_unchecked) { |
1486 | 0 | *suspend = 1; |
1487 | 0 | return; |
1488 | 0 | } |
1489 | 0 | } |
1490 | | |
1491 | | /* If after all this, we still haven't proven the positive wildcard |
1492 | | * response, fail. */ |
1493 | 0 | if(wc != NULL && !wc_NSEC_ok) { |
1494 | 0 | verbose(VERB_QUERY, "positive ANY response was wildcard " |
1495 | 0 | "expansion and did not prove original data " |
1496 | 0 | "did not exist"); |
1497 | 0 | chase_reply->security = sec_status_bogus; |
1498 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1499 | 0 | return; |
1500 | 0 | } |
1501 | | |
1502 | 0 | verbose(VERB_ALGO, "Successfully validated positive ANY response"); |
1503 | 0 | chase_reply->security = sec_status_secure; |
1504 | 0 | } |
1505 | | |
1506 | | /** |
1507 | | * Validate CNAME response, or DNAME+CNAME. |
1508 | | * This is just like a positive proof, except that this is about a |
1509 | | * DNAME+CNAME. Possible wildcard proof. |
1510 | | * Difference with positive proof is that this routine refuses |
1511 | | * wildcarded DNAMEs. |
1512 | | * |
1513 | | * The answer and authority rrsets must already be verified as secure. |
1514 | | * |
1515 | | * @param env: module env for verify. |
1516 | | * @param ve: validator env for verify. |
1517 | | * @param qchase: query that was made. |
1518 | | * @param chase_reply: answer to that query to validate. |
1519 | | * @param kkey: the key entry, which is trusted, and which matches |
1520 | | * the signer of the answer. The key entry isgood(). |
1521 | | * @param qstate: query state for the region. |
1522 | | * @param vq: validator state for the nsec3 cache table. |
1523 | | * @param nsec3_calculations: current nsec3 hash calculations. |
1524 | | * @param suspend: returned true if the task takes too long and needs to |
1525 | | * suspend to continue the effort later. |
1526 | | */ |
1527 | | static void |
1528 | | validate_cname_response(struct module_env* env, struct val_env* ve, |
1529 | | struct query_info* qchase, struct reply_info* chase_reply, |
1530 | | struct key_entry_key* kkey, struct module_qstate* qstate, |
1531 | | struct val_qstate* vq, int* nsec3_calculations, int* suspend) |
1532 | 0 | { |
1533 | 0 | uint8_t* wc = NULL; |
1534 | 0 | size_t wl; |
1535 | 0 | int wc_NSEC_ok = 0; |
1536 | 0 | int nsec3s_seen = 0; |
1537 | 0 | size_t i; |
1538 | 0 | struct ub_packed_rrset_key* s; |
1539 | 0 | *suspend = 0; |
1540 | | |
1541 | | /* validate the ANSWER section - this will be the CNAME (+DNAME) */ |
1542 | 0 | for(i=0; i<chase_reply->an_numrrsets; i++) { |
1543 | 0 | s = chase_reply->rrsets[i]; |
1544 | | |
1545 | | /* Check to see if the rrset is the result of a wildcard |
1546 | | * expansion. If so, an additional check will need to be |
1547 | | * made in the authority section. */ |
1548 | 0 | if(!val_rrset_wildcard(s, &wc, &wl)) { |
1549 | 0 | log_nametypeclass(VERB_QUERY, "Cname response has " |
1550 | 0 | "inconsistent wildcard sigs:", s->rk.dname, |
1551 | 0 | ntohs(s->rk.type), ntohs(s->rk.rrset_class)); |
1552 | 0 | chase_reply->security = sec_status_bogus; |
1553 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1554 | 0 | return; |
1555 | 0 | } |
1556 | | |
1557 | | /* Refuse wildcarded DNAMEs rfc 4597. |
1558 | | * Do not follow a wildcarded DNAME because |
1559 | | * its synthesized CNAME expansion is underdefined */ |
1560 | 0 | if(qchase->qtype != LDNS_RR_TYPE_DNAME && |
1561 | 0 | ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME && wc) { |
1562 | 0 | log_nametypeclass(VERB_QUERY, "cannot validate a " |
1563 | 0 | "wildcarded DNAME:", s->rk.dname, |
1564 | 0 | ntohs(s->rk.type), ntohs(s->rk.rrset_class)); |
1565 | 0 | chase_reply->security = sec_status_bogus; |
1566 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1567 | 0 | return; |
1568 | 0 | } |
1569 | | |
1570 | | /* If we have found a CNAME, stop looking for one. |
1571 | | * The iterator has placed the CNAME chain in correct |
1572 | | * order. */ |
1573 | 0 | if (ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) { |
1574 | 0 | break; |
1575 | 0 | } |
1576 | 0 | } |
1577 | | |
1578 | | /* AUTHORITY section */ |
1579 | 0 | for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ |
1580 | 0 | chase_reply->ns_numrrsets; i++) { |
1581 | 0 | s = chase_reply->rrsets[i]; |
1582 | | |
1583 | | /* If this is a positive wildcard response, and we have a |
1584 | | * (just verified) NSEC record, try to use it to 1) prove |
1585 | | * that qname doesn't exist and 2) that the correct wildcard |
1586 | | * was used. */ |
1587 | 0 | if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { |
1588 | 0 | if(val_nsec_proves_positive_wildcard(s, qchase, wc)) { |
1589 | 0 | wc_NSEC_ok = 1; |
1590 | 0 | } |
1591 | | /* if not, continue looking for proof */ |
1592 | 0 | } |
1593 | | |
1594 | | /* Otherwise, if this is a positive wildcard response and |
1595 | | * we have NSEC3 records */ |
1596 | 0 | if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { |
1597 | 0 | nsec3s_seen = 1; |
1598 | 0 | } |
1599 | 0 | } |
1600 | | |
1601 | | /* If this was a positive wildcard response that we haven't already |
1602 | | * proven, and we have NSEC3 records, try to prove it using the NSEC3 |
1603 | | * records. */ |
1604 | 0 | if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && |
1605 | 0 | nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { |
1606 | 0 | enum sec_status sec = nsec3_prove_wildcard(env, ve, |
1607 | 0 | chase_reply->rrsets+chase_reply->an_numrrsets, |
1608 | 0 | chase_reply->ns_numrrsets, qchase, kkey, wc, |
1609 | 0 | &vq->nsec3_cache_table, nsec3_calculations); |
1610 | 0 | if(sec == sec_status_insecure) { |
1611 | 0 | verbose(VERB_ALGO, "wildcard CNAME response is " |
1612 | 0 | "insecure"); |
1613 | 0 | chase_reply->security = sec_status_insecure; |
1614 | 0 | return; |
1615 | 0 | } else if(sec == sec_status_secure) { |
1616 | 0 | wc_NSEC_ok = 1; |
1617 | 0 | } else if(sec == sec_status_unchecked) { |
1618 | 0 | *suspend = 1; |
1619 | 0 | return; |
1620 | 0 | } |
1621 | 0 | } |
1622 | | |
1623 | | /* If after all this, we still haven't proven the positive wildcard |
1624 | | * response, fail. */ |
1625 | 0 | if(wc != NULL && !wc_NSEC_ok) { |
1626 | 0 | verbose(VERB_QUERY, "CNAME response was wildcard " |
1627 | 0 | "expansion and did not prove original data " |
1628 | 0 | "did not exist"); |
1629 | 0 | chase_reply->security = sec_status_bogus; |
1630 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1631 | 0 | return; |
1632 | 0 | } |
1633 | | |
1634 | 0 | verbose(VERB_ALGO, "Successfully validated CNAME response"); |
1635 | 0 | chase_reply->security = sec_status_secure; |
1636 | 0 | } |
1637 | | |
1638 | | /** |
1639 | | * Validate CNAME NOANSWER response, no more data after a CNAME chain. |
1640 | | * This can be a NODATA or a NAME ERROR case, but not both at the same time. |
1641 | | * We don't know because the rcode has been set to NOERROR by the CNAME. |
1642 | | * |
1643 | | * The answer and authority rrsets must already be verified as secure. |
1644 | | * |
1645 | | * @param env: module env for verify. |
1646 | | * @param ve: validator env for verify. |
1647 | | * @param qchase: query that was made. |
1648 | | * @param chase_reply: answer to that query to validate. |
1649 | | * @param kkey: the key entry, which is trusted, and which matches |
1650 | | * the signer of the answer. The key entry isgood(). |
1651 | | * @param qstate: query state for the region. |
1652 | | * @param vq: validator state for the nsec3 cache table. |
1653 | | * @param nsec3_calculations: current nsec3 hash calculations. |
1654 | | * @param suspend: returned true if the task takes too long and needs to |
1655 | | * suspend to continue the effort later. |
1656 | | */ |
1657 | | static void |
1658 | | validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, |
1659 | | struct query_info* qchase, struct reply_info* chase_reply, |
1660 | | struct key_entry_key* kkey, struct module_qstate* qstate, |
1661 | | struct val_qstate* vq, int* nsec3_calculations, int* suspend) |
1662 | 0 | { |
1663 | 0 | int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ |
1664 | 0 | uint8_t* ce = NULL; /* for wildcard nodata responses. This is the |
1665 | | proven closest encloser. */ |
1666 | 0 | uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ |
1667 | 0 | int nxdomain_valid_nsec = 0; /* if true, nameerror has been proven */ |
1668 | 0 | int nxdomain_valid_wnsec = 0; |
1669 | 0 | int nsec3s_seen = 0; /* nsec3s seen */ |
1670 | 0 | struct ub_packed_rrset_key* s; |
1671 | 0 | size_t i; |
1672 | 0 | uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ |
1673 | 0 | int ce_labs = 0; |
1674 | 0 | int prev_ce_labs = 0; |
1675 | 0 | *suspend = 0; |
1676 | | |
1677 | | /* the AUTHORITY section */ |
1678 | 0 | for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+ |
1679 | 0 | chase_reply->ns_numrrsets; i++) { |
1680 | 0 | s = chase_reply->rrsets[i]; |
1681 | | |
1682 | | /* If we encounter an NSEC record, try to use it to prove |
1683 | | * NODATA. This needs to handle the ENT NODATA case. |
1684 | | * Also try to prove NAMEERROR, and absence of a wildcard */ |
1685 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { |
1686 | 0 | if(nsec_proves_nodata(s, qchase, &wc)) { |
1687 | 0 | nodata_valid_nsec = 1; |
1688 | | /* set wc encloser if wildcard applicable */ |
1689 | 0 | } |
1690 | 0 | if(val_nsec_proves_name_error(s, qchase->qname)) { |
1691 | 0 | ce = nsec_closest_encloser(qchase->qname, s); |
1692 | 0 | nxdomain_valid_nsec = 1; |
1693 | 0 | } |
1694 | 0 | nsec_ce = nsec_closest_encloser(qchase->qname, s); |
1695 | 0 | ce_labs = dname_count_labels(nsec_ce); |
1696 | | /* Use longest closest encloser to prove wildcard. */ |
1697 | 0 | if(ce_labs > prev_ce_labs || |
1698 | 0 | (ce_labs == prev_ce_labs && |
1699 | 0 | nxdomain_valid_wnsec == 0)) { |
1700 | 0 | if(val_nsec_proves_no_wc(s, qchase->qname, |
1701 | 0 | qchase->qname_len)) |
1702 | 0 | nxdomain_valid_wnsec = 1; |
1703 | 0 | else |
1704 | 0 | nxdomain_valid_wnsec = 0; |
1705 | 0 | } |
1706 | 0 | prev_ce_labs = ce_labs; |
1707 | 0 | if(val_nsec_proves_insecuredelegation(s, qchase)) { |
1708 | 0 | verbose(VERB_ALGO, "delegation is insecure"); |
1709 | 0 | chase_reply->security = sec_status_insecure; |
1710 | 0 | return; |
1711 | 0 | } |
1712 | 0 | } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) { |
1713 | 0 | nsec3s_seen = 1; |
1714 | 0 | } |
1715 | 0 | } |
1716 | | |
1717 | | /* check to see if we have a wildcard NODATA proof. */ |
1718 | | |
1719 | | /* The wildcard NODATA is 1 NSEC proving that qname does not exists |
1720 | | * (and also proving what the closest encloser is), and 1 NSEC |
1721 | | * showing the matching wildcard, which must be *.closest_encloser. */ |
1722 | 0 | if(wc && !ce) |
1723 | 0 | nodata_valid_nsec = 0; |
1724 | 0 | else if(wc && ce) { |
1725 | 0 | if(query_dname_compare(wc, ce) != 0) { |
1726 | 0 | nodata_valid_nsec = 0; |
1727 | 0 | } |
1728 | 0 | } |
1729 | 0 | if(nxdomain_valid_nsec && !nxdomain_valid_wnsec) { |
1730 | | /* name error is missing wildcard denial proof */ |
1731 | 0 | nxdomain_valid_nsec = 0; |
1732 | 0 | } |
1733 | | |
1734 | 0 | if(nodata_valid_nsec && nxdomain_valid_nsec) { |
1735 | 0 | verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name " |
1736 | 0 | "exists and not exists, bogus"); |
1737 | 0 | chase_reply->security = sec_status_bogus; |
1738 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1739 | 0 | return; |
1740 | 0 | } |
1741 | 0 | if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen && |
1742 | 0 | nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { |
1743 | 0 | int nodata; |
1744 | 0 | enum sec_status sec = nsec3_prove_nxornodata(env, ve, |
1745 | 0 | chase_reply->rrsets+chase_reply->an_numrrsets, |
1746 | 0 | chase_reply->ns_numrrsets, qchase, kkey, &nodata, |
1747 | 0 | &vq->nsec3_cache_table, nsec3_calculations); |
1748 | 0 | if(sec == sec_status_insecure) { |
1749 | 0 | verbose(VERB_ALGO, "CNAMEchain to noanswer response " |
1750 | 0 | "is insecure"); |
1751 | 0 | chase_reply->security = sec_status_insecure; |
1752 | 0 | return; |
1753 | 0 | } else if(sec == sec_status_secure) { |
1754 | 0 | if(nodata) |
1755 | 0 | nodata_valid_nsec = 1; |
1756 | 0 | else nxdomain_valid_nsec = 1; |
1757 | 0 | } else if(sec == sec_status_unchecked) { |
1758 | 0 | *suspend = 1; |
1759 | 0 | return; |
1760 | 0 | } |
1761 | 0 | } |
1762 | | |
1763 | 0 | if(!nodata_valid_nsec && !nxdomain_valid_nsec) { |
1764 | 0 | verbose(VERB_QUERY, "CNAMEchain to noanswer response failed " |
1765 | 0 | "to prove status with NSEC/NSEC3"); |
1766 | 0 | if(verbosity >= VERB_ALGO) |
1767 | 0 | log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply); |
1768 | 0 | chase_reply->security = sec_status_bogus; |
1769 | 0 | update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1770 | 0 | return; |
1771 | 0 | } |
1772 | | |
1773 | 0 | if(nodata_valid_nsec) |
1774 | 0 | verbose(VERB_ALGO, "successfully validated CNAME chain to a " |
1775 | 0 | "NODATA response."); |
1776 | 0 | else verbose(VERB_ALGO, "successfully validated CNAME chain to a " |
1777 | 0 | "NAMEERROR response."); |
1778 | 0 | chase_reply->security = sec_status_secure; |
1779 | 0 | } |
1780 | | |
1781 | | /** |
1782 | | * Process init state for validator. |
1783 | | * Process the INIT state. First tier responses start in the INIT state. |
1784 | | * This is where they are vetted for validation suitability, and the initial |
1785 | | * key search is done. |
1786 | | * |
1787 | | * Currently, events the come through this routine will be either promoted |
1788 | | * to FINISHED/CNAME_RESP (no validation needed), FINDKEY (next step to |
1789 | | * validation), or will be (temporarily) retired and a new priming request |
1790 | | * event will be generated. |
1791 | | * |
1792 | | * @param qstate: query state. |
1793 | | * @param vq: validator query state. |
1794 | | * @param ve: validator shared global environment. |
1795 | | * @param id: module id. |
1796 | | * @return true if the event should be processed further on return, false if |
1797 | | * not. |
1798 | | */ |
1799 | | static int |
1800 | | processInit(struct module_qstate* qstate, struct val_qstate* vq, |
1801 | | struct val_env* ve, int id) |
1802 | 0 | { |
1803 | 0 | uint8_t* lookup_name; |
1804 | 0 | size_t lookup_len; |
1805 | 0 | struct trust_anchor* anchor; |
1806 | 0 | enum val_classification subtype = val_classify_response( |
1807 | 0 | qstate->query_flags, &qstate->qinfo, &vq->qchase, |
1808 | 0 | vq->orig_msg->rep, vq->rrset_skip); |
1809 | 0 | if(vq->restart_count > ve->max_restart) { |
1810 | 0 | verbose(VERB_ALGO, "restart count exceeded"); |
1811 | 0 | return val_error(qstate, id); |
1812 | 0 | } |
1813 | | |
1814 | | /* correctly initialize reason_bogus */ |
1815 | 0 | update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_BOGUS); |
1816 | |
|
1817 | 0 | verbose(VERB_ALGO, "validator classification %s", |
1818 | 0 | val_classification_to_string(subtype)); |
1819 | 0 | if(subtype == VAL_CLASS_REFERRAL && |
1820 | 0 | vq->rrset_skip < vq->orig_msg->rep->rrset_count) { |
1821 | | /* referral uses the rrset name as qchase, to find keys for |
1822 | | * that rrset */ |
1823 | 0 | vq->qchase.qname = vq->orig_msg->rep-> |
1824 | 0 | rrsets[vq->rrset_skip]->rk.dname; |
1825 | 0 | vq->qchase.qname_len = vq->orig_msg->rep-> |
1826 | 0 | rrsets[vq->rrset_skip]->rk.dname_len; |
1827 | 0 | vq->qchase.qtype = ntohs(vq->orig_msg->rep-> |
1828 | 0 | rrsets[vq->rrset_skip]->rk.type); |
1829 | 0 | vq->qchase.qclass = ntohs(vq->orig_msg->rep-> |
1830 | 0 | rrsets[vq->rrset_skip]->rk.rrset_class); |
1831 | 0 | } |
1832 | 0 | lookup_name = vq->qchase.qname; |
1833 | 0 | lookup_len = vq->qchase.qname_len; |
1834 | | /* for type DS look at the parent side for keys/trustanchor */ |
1835 | | /* also for NSEC not at apex */ |
1836 | 0 | if(vq->qchase.qtype == LDNS_RR_TYPE_DS || |
1837 | 0 | (vq->qchase.qtype == LDNS_RR_TYPE_NSEC && |
1838 | 0 | vq->orig_msg->rep->rrset_count > vq->rrset_skip && |
1839 | 0 | ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) == |
1840 | 0 | LDNS_RR_TYPE_NSEC && |
1841 | 0 | !(vq->orig_msg->rep->rrsets[vq->rrset_skip]-> |
1842 | 0 | rk.flags&PACKED_RRSET_NSEC_AT_APEX))) { |
1843 | 0 | dname_remove_label(&lookup_name, &lookup_len); |
1844 | 0 | } |
1845 | |
|
1846 | 0 | val_mark_indeterminate(vq->chase_reply, qstate->env->anchors, |
1847 | 0 | qstate->env->rrset_cache, qstate->env); |
1848 | 0 | vq->key_entry = NULL; |
1849 | 0 | vq->empty_DS_name = NULL; |
1850 | 0 | vq->ds_rrset = 0; |
1851 | 0 | anchor = anchors_lookup(qstate->env->anchors, |
1852 | 0 | lookup_name, lookup_len, vq->qchase.qclass); |
1853 | | |
1854 | | /* Determine the signer/lookup name */ |
1855 | 0 | val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep, |
1856 | 0 | vq->rrset_skip, &vq->signer_name, &vq->signer_len); |
1857 | 0 | if(vq->signer_name != NULL && |
1858 | 0 | !dname_subdomain_c(lookup_name, vq->signer_name)) { |
1859 | 0 | log_nametypeclass(VERB_ALGO, "this signer name is not a parent " |
1860 | 0 | "of lookupname, omitted", vq->signer_name, 0, 0); |
1861 | 0 | vq->signer_name = NULL; |
1862 | 0 | } |
1863 | 0 | if(vq->signer_name == NULL) { |
1864 | 0 | log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name, |
1865 | 0 | 0, 0); |
1866 | 0 | } else { |
1867 | 0 | lookup_name = vq->signer_name; |
1868 | 0 | lookup_len = vq->signer_len; |
1869 | 0 | log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0); |
1870 | 0 | } |
1871 | | |
1872 | | /* for NXDOMAIN it could be signed by a parent of the trust anchor */ |
1873 | 0 | if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name && |
1874 | 0 | anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){ |
1875 | 0 | lock_basic_unlock(&anchor->lock); |
1876 | 0 | anchor = anchors_lookup(qstate->env->anchors, |
1877 | 0 | lookup_name, lookup_len, vq->qchase.qclass); |
1878 | 0 | if(!anchor) { /* unsigned parent denies anchor*/ |
1879 | 0 | verbose(VERB_QUERY, "unsigned parent zone denies" |
1880 | 0 | " trust anchor, indeterminate"); |
1881 | 0 | vq->chase_reply->security = sec_status_indeterminate; |
1882 | 0 | update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE); |
1883 | 0 | vq->state = VAL_FINISHED_STATE; |
1884 | 0 | return 1; |
1885 | 0 | } |
1886 | 0 | verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent"); |
1887 | 0 | } else if(subtype == VAL_CLASS_POSITIVE && |
1888 | 0 | qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY && |
1889 | 0 | query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) { |
1890 | | /* is a DNSKEY so lookup a bit higher since we want to |
1891 | | * get it from a parent or from trustanchor */ |
1892 | 0 | dname_remove_label(&lookup_name, &lookup_len); |
1893 | 0 | } |
1894 | | |
1895 | 0 | if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME || |
1896 | 0 | subtype == VAL_CLASS_REFERRAL) { |
1897 | | /* extract this part of orig_msg into chase_reply for |
1898 | | * the eventual VALIDATE stage */ |
1899 | 0 | val_fill_reply(vq->chase_reply, vq->orig_msg->rep, |
1900 | 0 | vq->rrset_skip, lookup_name, lookup_len, |
1901 | 0 | vq->signer_name); |
1902 | 0 | if(verbosity >= VERB_ALGO) |
1903 | 0 | log_dns_msg("chased extract", &vq->qchase, |
1904 | 0 | vq->chase_reply); |
1905 | 0 | } |
1906 | |
|
1907 | 0 | vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len, |
1908 | 0 | vq->qchase.qclass, qstate->region, *qstate->env->now); |
1909 | | |
1910 | | /* there is no key and no trust anchor */ |
1911 | 0 | if(vq->key_entry == NULL && anchor == NULL) { |
1912 | | /*response isn't under a trust anchor, so we cannot validate.*/ |
1913 | 0 | vq->chase_reply->security = sec_status_indeterminate; |
1914 | 0 | update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE); |
1915 | | /* go to finished state to cache this result */ |
1916 | 0 | vq->state = VAL_FINISHED_STATE; |
1917 | 0 | return 1; |
1918 | 0 | } |
1919 | | /* if not key, or if keyentry is *above* the trustanchor, i.e. |
1920 | | * the keyentry is based on another (higher) trustanchor */ |
1921 | 0 | else if(vq->key_entry == NULL || (anchor && |
1922 | 0 | dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) { |
1923 | | /* trust anchor is an 'unsigned' trust anchor */ |
1924 | 0 | if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) { |
1925 | 0 | vq->chase_reply->security = sec_status_insecure; |
1926 | 0 | val_mark_insecure(vq->chase_reply, anchor->name, |
1927 | 0 | qstate->env->rrset_cache, qstate->env); |
1928 | 0 | lock_basic_unlock(&anchor->lock); |
1929 | | /* go to finished state to cache this result */ |
1930 | 0 | vq->state = VAL_FINISHED_STATE; |
1931 | 0 | return 1; |
1932 | 0 | } |
1933 | | /* fire off a trust anchor priming query. */ |
1934 | 0 | verbose(VERB_DETAIL, "prime trust anchor"); |
1935 | 0 | if(!prime_trust_anchor(qstate, vq, id, anchor)) { |
1936 | 0 | lock_basic_unlock(&anchor->lock); |
1937 | 0 | return val_error(qstate, id); |
1938 | 0 | } |
1939 | 0 | lock_basic_unlock(&anchor->lock); |
1940 | | /* and otherwise, don't continue processing this event. |
1941 | | * (it will be reactivated when the priming query returns). */ |
1942 | 0 | vq->state = VAL_FINDKEY_STATE; |
1943 | 0 | return 0; |
1944 | 0 | } |
1945 | 0 | if(anchor) { |
1946 | 0 | lock_basic_unlock(&anchor->lock); |
1947 | 0 | } |
1948 | |
|
1949 | 0 | if(key_entry_isnull(vq->key_entry)) { |
1950 | | /* response is under a null key, so we cannot validate |
1951 | | * However, we do set the status to INSECURE, since it is |
1952 | | * essentially proven insecure. */ |
1953 | 0 | vq->chase_reply->security = sec_status_insecure; |
1954 | 0 | val_mark_insecure(vq->chase_reply, vq->key_entry->name, |
1955 | 0 | qstate->env->rrset_cache, qstate->env); |
1956 | | /* go to finished state to cache this result */ |
1957 | 0 | vq->state = VAL_FINISHED_STATE; |
1958 | 0 | return 1; |
1959 | 0 | } else if(key_entry_isbad(vq->key_entry)) { |
1960 | | /* Bad keys should have the relevant EDE code and text */ |
1961 | 0 | sldns_ede_code ede = key_entry_get_reason_bogus(vq->key_entry); |
1962 | | /* key is bad, chain is bad, reply is bogus */ |
1963 | 0 | errinf_dname(qstate, "key for validation", vq->key_entry->name); |
1964 | 0 | errinf_ede(qstate, "is marked as invalid", ede); |
1965 | 0 | errinf(qstate, "because of a previous"); |
1966 | 0 | errinf(qstate, key_entry_get_reason(vq->key_entry)); |
1967 | | |
1968 | | /* no retries, stop bothering the authority until timeout */ |
1969 | 0 | vq->restart_count = ve->max_restart; |
1970 | 0 | vq->chase_reply->security = sec_status_bogus; |
1971 | 0 | update_reason_bogus(vq->chase_reply, ede); |
1972 | 0 | vq->state = VAL_FINISHED_STATE; |
1973 | 0 | return 1; |
1974 | 0 | } |
1975 | | |
1976 | | /* otherwise, we have our "closest" cached key -- continue |
1977 | | * processing in the next state. */ |
1978 | 0 | vq->state = VAL_FINDKEY_STATE; |
1979 | 0 | return 1; |
1980 | 0 | } |
1981 | | |
1982 | | /** |
1983 | | * Process the FINDKEY state. Generally this just calculates the next name |
1984 | | * to query and either issues a DS or a DNSKEY query. It will check to see |
1985 | | * if the correct key has already been reached, in which case it will |
1986 | | * advance the event to the next state. |
1987 | | * |
1988 | | * @param qstate: query state. |
1989 | | * @param vq: validator query state. |
1990 | | * @param id: module id. |
1991 | | * @return true if the event should be processed further on return, false if |
1992 | | * not. |
1993 | | */ |
1994 | | static int |
1995 | | processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) |
1996 | 0 | { |
1997 | 0 | uint8_t* target_key_name, *current_key_name; |
1998 | 0 | size_t target_key_len; |
1999 | 0 | int strip_lab; |
2000 | 0 | struct module_qstate* newq = NULL; |
2001 | |
|
2002 | 0 | log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase); |
2003 | | /* We know that state.key_entry is not 0 or bad key -- if it were, |
2004 | | * then previous processing should have directed this event to |
2005 | | * a different state. |
2006 | | * It could be an isnull key, which signals the DNSKEY failed |
2007 | | * with retry and has to be looked up again. */ |
2008 | 0 | log_assert(vq->key_entry && !key_entry_isbad(vq->key_entry)); |
2009 | 0 | if(key_entry_isnull(vq->key_entry)) { |
2010 | 0 | if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, |
2011 | 0 | vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, |
2012 | 0 | vq->qchase.qclass, BIT_CD, &newq, 0)) { |
2013 | 0 | verbose(VERB_ALGO, "error generating DNSKEY request"); |
2014 | 0 | return val_error(qstate, id); |
2015 | 0 | } |
2016 | 0 | return 0; |
2017 | 0 | } |
2018 | | |
2019 | 0 | target_key_name = vq->signer_name; |
2020 | 0 | target_key_len = vq->signer_len; |
2021 | 0 | if(!target_key_name) { |
2022 | 0 | target_key_name = vq->qchase.qname; |
2023 | 0 | target_key_len = vq->qchase.qname_len; |
2024 | 0 | } |
2025 | |
|
2026 | 0 | current_key_name = vq->key_entry->name; |
2027 | | |
2028 | | /* If our current key entry matches our target, then we are done. */ |
2029 | 0 | if(query_dname_compare(target_key_name, current_key_name) == 0) { |
2030 | 0 | vq->state = VAL_VALIDATE_STATE; |
2031 | 0 | return 1; |
2032 | 0 | } |
2033 | | |
2034 | 0 | if(vq->empty_DS_name) { |
2035 | | /* if the last empty nonterminal/emptyDS name we detected is |
2036 | | * below the current key, use that name to make progress |
2037 | | * along the chain of trust */ |
2038 | 0 | if(query_dname_compare(target_key_name, |
2039 | 0 | vq->empty_DS_name) == 0) { |
2040 | | /* do not query for empty_DS_name again */ |
2041 | 0 | verbose(VERB_ALGO, "Cannot retrieve DS for signature"); |
2042 | 0 | errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING); |
2043 | 0 | errinf_origin(qstate, qstate->reply_origin); |
2044 | 0 | vq->chase_reply->security = sec_status_bogus; |
2045 | 0 | update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING); |
2046 | 0 | vq->state = VAL_FINISHED_STATE; |
2047 | 0 | return 1; |
2048 | 0 | } |
2049 | 0 | current_key_name = vq->empty_DS_name; |
2050 | 0 | } |
2051 | | |
2052 | 0 | log_nametypeclass(VERB_ALGO, "current keyname", current_key_name, |
2053 | 0 | LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); |
2054 | 0 | log_nametypeclass(VERB_ALGO, "target keyname", target_key_name, |
2055 | 0 | LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); |
2056 | | /* assert we are walking down the DNS tree */ |
2057 | 0 | if(!dname_subdomain_c(target_key_name, current_key_name)) { |
2058 | 0 | verbose(VERB_ALGO, "bad signer name"); |
2059 | 0 | vq->chase_reply->security = sec_status_bogus; |
2060 | 0 | vq->state = VAL_FINISHED_STATE; |
2061 | 0 | return 1; |
2062 | 0 | } |
2063 | | /* so this value is >= -1 */ |
2064 | 0 | strip_lab = dname_count_labels(target_key_name) - |
2065 | 0 | dname_count_labels(current_key_name) - 1; |
2066 | 0 | log_assert(strip_lab >= -1); |
2067 | 0 | verbose(VERB_ALGO, "striplab %d", strip_lab); |
2068 | 0 | if(strip_lab > 0) { |
2069 | 0 | dname_remove_labels(&target_key_name, &target_key_len, |
2070 | 0 | strip_lab); |
2071 | 0 | } |
2072 | 0 | log_nametypeclass(VERB_ALGO, "next keyname", target_key_name, |
2073 | 0 | LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN); |
2074 | | |
2075 | | /* The next step is either to query for the next DS, or to query |
2076 | | * for the next DNSKEY. */ |
2077 | 0 | if(vq->ds_rrset) |
2078 | 0 | log_nametypeclass(VERB_ALGO, "DS RRset", vq->ds_rrset->rk.dname, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN); |
2079 | 0 | else verbose(VERB_ALGO, "No DS RRset"); |
2080 | |
|
2081 | 0 | if(vq->ds_rrset && query_dname_compare(vq->ds_rrset->rk.dname, |
2082 | 0 | vq->key_entry->name) != 0) { |
2083 | 0 | if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, |
2084 | 0 | vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, |
2085 | 0 | vq->qchase.qclass, BIT_CD, &newq, 0)) { |
2086 | 0 | verbose(VERB_ALGO, "error generating DNSKEY request"); |
2087 | 0 | return val_error(qstate, id); |
2088 | 0 | } |
2089 | 0 | return 0; |
2090 | 0 | } |
2091 | | |
2092 | 0 | if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname, |
2093 | 0 | target_key_name) != 0) { |
2094 | | /* check if there is a cache entry : pick up an NSEC if |
2095 | | * there is no DS, check if that NSEC has DS-bit unset, and |
2096 | | * thus can disprove the secure delegation we seek. |
2097 | | * We can then use that NSEC even in the absence of a SOA |
2098 | | * record that would be required by the iterator to supply |
2099 | | * a completely protocol-correct response. |
2100 | | * Uses negative cache for NSEC3 lookup of DS responses. */ |
2101 | | /* only if cache not blacklisted, of course */ |
2102 | 0 | struct dns_msg* msg; |
2103 | 0 | int suspend; |
2104 | 0 | if(vq->sub_ds_msg) { |
2105 | | /* We have a suspended DS reply from a sub-query; |
2106 | | * process it. */ |
2107 | 0 | verbose(VERB_ALGO, "Process suspended sub DS response"); |
2108 | 0 | msg = vq->sub_ds_msg; |
2109 | 0 | process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, |
2110 | 0 | msg, &msg->qinfo, NULL, &suspend, NULL); |
2111 | 0 | if(suspend) { |
2112 | | /* we'll come back here later to continue */ |
2113 | 0 | if(!validate_suspend_setup_timer(qstate, vq, |
2114 | 0 | id, VAL_FINDKEY_STATE)) |
2115 | 0 | return val_error(qstate, id); |
2116 | 0 | return 0; |
2117 | 0 | } |
2118 | 0 | vq->sub_ds_msg = NULL; |
2119 | 0 | return 1; /* continue processing ds-response results */ |
2120 | 0 | } else if(!qstate->blacklist && !vq->chain_blacklist && |
2121 | 0 | (msg=val_find_DS(qstate->env, target_key_name, |
2122 | 0 | target_key_len, vq->qchase.qclass, qstate->region, |
2123 | 0 | vq->key_entry->name)) ) { |
2124 | 0 | verbose(VERB_ALGO, "Process cached DS response"); |
2125 | 0 | process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, |
2126 | 0 | msg, &msg->qinfo, NULL, &suspend, NULL); |
2127 | 0 | if(suspend) { |
2128 | | /* we'll come back here later to continue */ |
2129 | 0 | if(!validate_suspend_setup_timer(qstate, vq, |
2130 | 0 | id, VAL_FINDKEY_STATE)) |
2131 | 0 | return val_error(qstate, id); |
2132 | 0 | return 0; |
2133 | 0 | } |
2134 | 0 | return 1; /* continue processing ds-response results */ |
2135 | 0 | } |
2136 | 0 | if(!generate_request(qstate, id, target_key_name, |
2137 | 0 | target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass, |
2138 | 0 | BIT_CD, &newq, 0)) { |
2139 | 0 | verbose(VERB_ALGO, "error generating DS request"); |
2140 | 0 | return val_error(qstate, id); |
2141 | 0 | } |
2142 | 0 | return 0; |
2143 | 0 | } |
2144 | | |
2145 | | /* Otherwise, it is time to query for the DNSKEY */ |
2146 | 0 | if(!generate_request(qstate, id, vq->ds_rrset->rk.dname, |
2147 | 0 | vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY, |
2148 | 0 | vq->qchase.qclass, BIT_CD, &newq, 0)) { |
2149 | 0 | verbose(VERB_ALGO, "error generating DNSKEY request"); |
2150 | 0 | return val_error(qstate, id); |
2151 | 0 | } |
2152 | | |
2153 | 0 | return 0; |
2154 | 0 | } |
2155 | | |
2156 | | /** |
2157 | | * Process the VALIDATE stage, the init and findkey stages are finished, |
2158 | | * and the right keys are available to validate the response. |
2159 | | * Or, there are no keys available, in order to invalidate the response. |
2160 | | * |
2161 | | * After validation, the status is recorded in the message and rrsets, |
2162 | | * and finished state is started. |
2163 | | * |
2164 | | * @param qstate: query state. |
2165 | | * @param vq: validator query state. |
2166 | | * @param ve: validator shared global environment. |
2167 | | * @param id: module id. |
2168 | | * @return true if the event should be processed further on return, false if |
2169 | | * not. |
2170 | | */ |
2171 | | static int |
2172 | | processValidate(struct module_qstate* qstate, struct val_qstate* vq, |
2173 | | struct val_env* ve, int id) |
2174 | 0 | { |
2175 | 0 | enum val_classification subtype; |
2176 | 0 | int rcode, suspend, nsec3_calculations = 0; |
2177 | |
|
2178 | 0 | if(!vq->key_entry) { |
2179 | 0 | verbose(VERB_ALGO, "validate: no key entry, failed"); |
2180 | 0 | return val_error(qstate, id); |
2181 | 0 | } |
2182 | | |
2183 | | /* This is the default next state. */ |
2184 | 0 | vq->state = VAL_FINISHED_STATE; |
2185 | | |
2186 | | /* Unsigned responses must be underneath a "null" key entry.*/ |
2187 | 0 | if(key_entry_isnull(vq->key_entry)) { |
2188 | 0 | verbose(VERB_DETAIL, "Verified that %sresponse is INSECURE", |
2189 | 0 | vq->signer_name?"":"unsigned "); |
2190 | 0 | vq->chase_reply->security = sec_status_insecure; |
2191 | 0 | val_mark_insecure(vq->chase_reply, vq->key_entry->name, |
2192 | 0 | qstate->env->rrset_cache, qstate->env); |
2193 | 0 | key_cache_insert(ve->kcache, vq->key_entry, |
2194 | 0 | qstate->env->cfg->val_log_level >= 2); |
2195 | 0 | return 1; |
2196 | 0 | } |
2197 | | |
2198 | 0 | if(key_entry_isbad(vq->key_entry)) { |
2199 | 0 | log_nametypeclass(VERB_DETAIL, "Could not establish a chain " |
2200 | 0 | "of trust to keys for", vq->key_entry->name, |
2201 | 0 | LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class); |
2202 | 0 | vq->chase_reply->security = sec_status_bogus; |
2203 | 0 | update_reason_bogus(vq->chase_reply, |
2204 | 0 | key_entry_get_reason_bogus(vq->key_entry)); |
2205 | 0 | errinf_ede(qstate, "while building chain of trust", |
2206 | 0 | key_entry_get_reason_bogus(vq->key_entry)); |
2207 | 0 | if(vq->restart_count >= ve->max_restart) |
2208 | 0 | key_cache_insert(ve->kcache, vq->key_entry, |
2209 | 0 | qstate->env->cfg->val_log_level >= 2); |
2210 | 0 | return 1; |
2211 | 0 | } |
2212 | | |
2213 | | /* signerName being null is the indicator that this response was |
2214 | | * unsigned */ |
2215 | 0 | if(vq->signer_name == NULL) { |
2216 | 0 | log_query_info(VERB_ALGO, "processValidate: state has no " |
2217 | 0 | "signer name", &vq->qchase); |
2218 | 0 | verbose(VERB_DETAIL, "Could not establish validation of " |
2219 | 0 | "INSECURE status of unsigned response."); |
2220 | 0 | errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING); |
2221 | 0 | errinf_origin(qstate, qstate->reply_origin); |
2222 | 0 | vq->chase_reply->security = sec_status_bogus; |
2223 | 0 | update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING); |
2224 | 0 | return 1; |
2225 | 0 | } |
2226 | 0 | subtype = val_classify_response(qstate->query_flags, &qstate->qinfo, |
2227 | 0 | &vq->qchase, vq->orig_msg->rep, vq->rrset_skip); |
2228 | 0 | if(subtype != VAL_CLASS_REFERRAL) |
2229 | 0 | remove_spurious_authority(vq->chase_reply, vq->orig_msg->rep); |
2230 | | |
2231 | | /* check signatures in the message; |
2232 | | * answer and authority must be valid, additional is only checked. */ |
2233 | 0 | if(!validate_msg_signatures(qstate, vq, qstate->env, ve, |
2234 | 0 | vq->chase_reply, vq->key_entry, &suspend)) { |
2235 | 0 | if(suspend) { |
2236 | 0 | if(!validate_suspend_setup_timer(qstate, vq, |
2237 | 0 | id, VAL_VALIDATE_STATE)) |
2238 | 0 | return val_error(qstate, id); |
2239 | 0 | return 0; |
2240 | 0 | } |
2241 | | /* workaround bad recursor out there that truncates (even |
2242 | | * with EDNS4k) to 512 by removing RRSIG from auth section |
2243 | | * for positive replies*/ |
2244 | 0 | if((subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY |
2245 | 0 | || subtype == VAL_CLASS_CNAME) && |
2246 | 0 | detect_wrongly_truncated(vq->orig_msg->rep)) { |
2247 | | /* truncate the message some more */ |
2248 | 0 | vq->orig_msg->rep->ns_numrrsets = 0; |
2249 | 0 | vq->orig_msg->rep->ar_numrrsets = 0; |
2250 | 0 | vq->orig_msg->rep->rrset_count = |
2251 | 0 | vq->orig_msg->rep->an_numrrsets; |
2252 | 0 | vq->chase_reply->ns_numrrsets = 0; |
2253 | 0 | vq->chase_reply->ar_numrrsets = 0; |
2254 | 0 | vq->chase_reply->rrset_count = |
2255 | 0 | vq->chase_reply->an_numrrsets; |
2256 | 0 | qstate->errinf = NULL; |
2257 | 0 | } |
2258 | 0 | else { |
2259 | 0 | verbose(VERB_DETAIL, "Validate: message contains " |
2260 | 0 | "bad rrsets"); |
2261 | 0 | return 1; |
2262 | 0 | } |
2263 | 0 | } |
2264 | | |
2265 | 0 | switch(subtype) { |
2266 | 0 | case VAL_CLASS_POSITIVE: |
2267 | 0 | verbose(VERB_ALGO, "Validating a positive response"); |
2268 | 0 | validate_positive_response(qstate->env, ve, |
2269 | 0 | &vq->qchase, vq->chase_reply, vq->key_entry, |
2270 | 0 | qstate, vq, &nsec3_calculations, &suspend); |
2271 | 0 | if(suspend) { |
2272 | 0 | if(!validate_suspend_setup_timer(qstate, |
2273 | 0 | vq, id, VAL_VALIDATE_STATE)) |
2274 | 0 | return val_error(qstate, id); |
2275 | 0 | return 0; |
2276 | 0 | } |
2277 | 0 | verbose(VERB_DETAIL, "validate(positive): %s", |
2278 | 0 | sec_status_to_string( |
2279 | 0 | vq->chase_reply->security)); |
2280 | 0 | break; |
2281 | | |
2282 | 0 | case VAL_CLASS_NODATA: |
2283 | 0 | verbose(VERB_ALGO, "Validating a nodata response"); |
2284 | 0 | validate_nodata_response(qstate->env, ve, |
2285 | 0 | &vq->qchase, vq->chase_reply, vq->key_entry, |
2286 | 0 | qstate, vq, &nsec3_calculations, &suspend); |
2287 | 0 | if(suspend) { |
2288 | 0 | if(!validate_suspend_setup_timer(qstate, |
2289 | 0 | vq, id, VAL_VALIDATE_STATE)) |
2290 | 0 | return val_error(qstate, id); |
2291 | 0 | return 0; |
2292 | 0 | } |
2293 | 0 | verbose(VERB_DETAIL, "validate(nodata): %s", |
2294 | 0 | sec_status_to_string( |
2295 | 0 | vq->chase_reply->security)); |
2296 | 0 | break; |
2297 | | |
2298 | 0 | case VAL_CLASS_NAMEERROR: |
2299 | 0 | rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); |
2300 | 0 | verbose(VERB_ALGO, "Validating a nxdomain response"); |
2301 | 0 | validate_nameerror_response(qstate->env, ve, |
2302 | 0 | &vq->qchase, vq->chase_reply, vq->key_entry, &rcode, |
2303 | 0 | qstate, vq, &nsec3_calculations, &suspend); |
2304 | 0 | if(suspend) { |
2305 | 0 | if(!validate_suspend_setup_timer(qstate, |
2306 | 0 | vq, id, VAL_VALIDATE_STATE)) |
2307 | 0 | return val_error(qstate, id); |
2308 | 0 | return 0; |
2309 | 0 | } |
2310 | 0 | verbose(VERB_DETAIL, "validate(nxdomain): %s", |
2311 | 0 | sec_status_to_string( |
2312 | 0 | vq->chase_reply->security)); |
2313 | 0 | FLAGS_SET_RCODE(vq->orig_msg->rep->flags, rcode); |
2314 | 0 | FLAGS_SET_RCODE(vq->chase_reply->flags, rcode); |
2315 | 0 | break; |
2316 | | |
2317 | 0 | case VAL_CLASS_CNAME: |
2318 | 0 | verbose(VERB_ALGO, "Validating a cname response"); |
2319 | 0 | validate_cname_response(qstate->env, ve, |
2320 | 0 | &vq->qchase, vq->chase_reply, vq->key_entry, |
2321 | 0 | qstate, vq, &nsec3_calculations, &suspend); |
2322 | 0 | if(suspend) { |
2323 | 0 | if(!validate_suspend_setup_timer(qstate, |
2324 | 0 | vq, id, VAL_VALIDATE_STATE)) |
2325 | 0 | return val_error(qstate, id); |
2326 | 0 | return 0; |
2327 | 0 | } |
2328 | 0 | verbose(VERB_DETAIL, "validate(cname): %s", |
2329 | 0 | sec_status_to_string( |
2330 | 0 | vq->chase_reply->security)); |
2331 | 0 | break; |
2332 | | |
2333 | 0 | case VAL_CLASS_CNAMENOANSWER: |
2334 | 0 | verbose(VERB_ALGO, "Validating a cname noanswer " |
2335 | 0 | "response"); |
2336 | 0 | validate_cname_noanswer_response(qstate->env, ve, |
2337 | 0 | &vq->qchase, vq->chase_reply, vq->key_entry, |
2338 | 0 | qstate, vq, &nsec3_calculations, &suspend); |
2339 | 0 | if(suspend) { |
2340 | 0 | if(!validate_suspend_setup_timer(qstate, |
2341 | 0 | vq, id, VAL_VALIDATE_STATE)) |
2342 | 0 | return val_error(qstate, id); |
2343 | 0 | return 0; |
2344 | 0 | } |
2345 | 0 | verbose(VERB_DETAIL, "validate(cname_noanswer): %s", |
2346 | 0 | sec_status_to_string( |
2347 | 0 | vq->chase_reply->security)); |
2348 | 0 | break; |
2349 | | |
2350 | 0 | case VAL_CLASS_REFERRAL: |
2351 | 0 | verbose(VERB_ALGO, "Validating a referral response"); |
2352 | 0 | validate_referral_response(vq->chase_reply); |
2353 | 0 | verbose(VERB_DETAIL, "validate(referral): %s", |
2354 | 0 | sec_status_to_string( |
2355 | 0 | vq->chase_reply->security)); |
2356 | 0 | break; |
2357 | | |
2358 | 0 | case VAL_CLASS_ANY: |
2359 | 0 | verbose(VERB_ALGO, "Validating a positive ANY " |
2360 | 0 | "response"); |
2361 | 0 | validate_any_response(qstate->env, ve, &vq->qchase, |
2362 | 0 | vq->chase_reply, vq->key_entry, qstate, vq, |
2363 | 0 | &nsec3_calculations, &suspend); |
2364 | 0 | if(suspend) { |
2365 | 0 | if(!validate_suspend_setup_timer(qstate, |
2366 | 0 | vq, id, VAL_VALIDATE_STATE)) |
2367 | 0 | return val_error(qstate, id); |
2368 | 0 | return 0; |
2369 | 0 | } |
2370 | 0 | verbose(VERB_DETAIL, "validate(positive_any): %s", |
2371 | 0 | sec_status_to_string( |
2372 | 0 | vq->chase_reply->security)); |
2373 | 0 | break; |
2374 | | |
2375 | 0 | default: |
2376 | 0 | log_err("validate: unhandled response subtype: %d", |
2377 | 0 | subtype); |
2378 | 0 | } |
2379 | 0 | if(vq->chase_reply->security == sec_status_bogus) { |
2380 | 0 | if(subtype == VAL_CLASS_POSITIVE) |
2381 | 0 | errinf(qstate, "wildcard"); |
2382 | 0 | else errinf(qstate, val_classification_to_string(subtype)); |
2383 | 0 | errinf(qstate, "proof failed"); |
2384 | 0 | errinf_origin(qstate, qstate->reply_origin); |
2385 | 0 | } |
2386 | |
|
2387 | 0 | return 1; |
2388 | 0 | } |
2389 | | |
2390 | | /** |
2391 | | * The Finished state. The validation status (good or bad) has been determined. |
2392 | | * |
2393 | | * @param qstate: query state. |
2394 | | * @param vq: validator query state. |
2395 | | * @param ve: validator shared global environment. |
2396 | | * @param id: module id. |
2397 | | * @return true if the event should be processed further on return, false if |
2398 | | * not. |
2399 | | */ |
2400 | | static int |
2401 | | processFinished(struct module_qstate* qstate, struct val_qstate* vq, |
2402 | | struct val_env* ve, int id) |
2403 | 0 | { |
2404 | 0 | enum val_classification subtype = val_classify_response( |
2405 | 0 | qstate->query_flags, &qstate->qinfo, &vq->qchase, |
2406 | 0 | vq->orig_msg->rep, vq->rrset_skip); |
2407 | | |
2408 | | /* store overall validation result in orig_msg */ |
2409 | 0 | if(vq->rrset_skip == 0) { |
2410 | 0 | vq->orig_msg->rep->security = vq->chase_reply->security; |
2411 | 0 | update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus); |
2412 | 0 | } else if(subtype != VAL_CLASS_REFERRAL || |
2413 | 0 | vq->rrset_skip < vq->orig_msg->rep->an_numrrsets + |
2414 | 0 | vq->orig_msg->rep->ns_numrrsets) { |
2415 | | /* ignore sec status of additional section if a referral |
2416 | | * type message skips there and |
2417 | | * use the lowest security status as end result. */ |
2418 | 0 | if(vq->chase_reply->security < vq->orig_msg->rep->security) { |
2419 | 0 | vq->orig_msg->rep->security = |
2420 | 0 | vq->chase_reply->security; |
2421 | 0 | update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus); |
2422 | 0 | } |
2423 | 0 | } |
2424 | |
|
2425 | 0 | if(subtype == VAL_CLASS_REFERRAL) { |
2426 | | /* for a referral, move to next unchecked rrset and check it*/ |
2427 | 0 | vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep, |
2428 | 0 | vq->rrset_skip); |
2429 | 0 | if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) { |
2430 | | /* and restart for this rrset */ |
2431 | 0 | verbose(VERB_ALGO, "validator: go to next rrset"); |
2432 | 0 | vq->chase_reply->security = sec_status_unchecked; |
2433 | 0 | vq->state = VAL_INIT_STATE; |
2434 | 0 | return 1; |
2435 | 0 | } |
2436 | | /* referral chase is done */ |
2437 | 0 | } |
2438 | 0 | if(vq->chase_reply->security != sec_status_bogus && |
2439 | 0 | subtype == VAL_CLASS_CNAME) { |
2440 | | /* chase the CNAME; process next part of the message */ |
2441 | 0 | if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep, |
2442 | 0 | &vq->rrset_skip)) { |
2443 | 0 | verbose(VERB_ALGO, "validator: failed to chase CNAME"); |
2444 | 0 | vq->orig_msg->rep->security = sec_status_bogus; |
2445 | 0 | update_reason_bogus(vq->orig_msg->rep, LDNS_EDE_DNSSEC_BOGUS); |
2446 | 0 | } else { |
2447 | | /* restart process for new qchase at rrset_skip */ |
2448 | 0 | log_query_info(VERB_ALGO, "validator: chased to", |
2449 | 0 | &vq->qchase); |
2450 | 0 | vq->chase_reply->security = sec_status_unchecked; |
2451 | 0 | vq->state = VAL_INIT_STATE; |
2452 | 0 | return 1; |
2453 | 0 | } |
2454 | 0 | } |
2455 | | |
2456 | 0 | if(vq->orig_msg->rep->security == sec_status_secure) { |
2457 | | /* If the message is secure, check that all rrsets are |
2458 | | * secure (i.e. some inserted RRset for CNAME chain with |
2459 | | * a different signer name). And drop additional rrsets |
2460 | | * that are not secure (if clean-additional option is set) */ |
2461 | | /* this may cause the msg to be marked bogus */ |
2462 | 0 | val_check_nonsecure(qstate->env, vq->orig_msg->rep); |
2463 | 0 | if(vq->orig_msg->rep->security == sec_status_secure) { |
2464 | 0 | log_query_info(VERB_DETAIL, "validation success", |
2465 | 0 | &qstate->qinfo); |
2466 | 0 | if(!qstate->no_cache_store) { |
2467 | 0 | val_neg_addreply(qstate->env->neg_cache, |
2468 | 0 | vq->orig_msg->rep); |
2469 | 0 | } |
2470 | 0 | } |
2471 | 0 | } |
2472 | | |
2473 | | /* if the result is bogus - set message ttl to bogus ttl to avoid |
2474 | | * endless bogus revalidation */ |
2475 | 0 | if(vq->orig_msg->rep->security == sec_status_bogus) { |
2476 | 0 | struct msgreply_entry* e; |
2477 | | |
2478 | | /* see if we can try again to fetch data */ |
2479 | 0 | if(vq->restart_count < ve->max_restart) { |
2480 | 0 | verbose(VERB_ALGO, "validation failed, " |
2481 | 0 | "blacklist and retry to fetch data"); |
2482 | 0 | val_blacklist(&qstate->blacklist, qstate->region, |
2483 | 0 | qstate->reply_origin, 0); |
2484 | 0 | qstate->reply_origin = NULL; |
2485 | 0 | qstate->errinf = NULL; |
2486 | 0 | val_restart(vq); |
2487 | 0 | verbose(VERB_ALGO, "pass back to next module"); |
2488 | 0 | qstate->ext_state[id] = module_restart_next; |
2489 | 0 | return 0; |
2490 | 0 | } |
2491 | | |
2492 | 0 | if(qstate->env->cfg->serve_expired && |
2493 | 0 | (e=msg_cache_lookup(qstate->env, qstate->qinfo.qname, |
2494 | 0 | qstate->qinfo.qname_len, qstate->qinfo.qtype, |
2495 | 0 | qstate->qinfo.qclass, qstate->query_flags, |
2496 | 0 | 0 /*now; allow expired*/, |
2497 | 0 | 1 /*wr; we may update the data*/))) { |
2498 | 0 | struct reply_info* rep = (struct reply_info*)e->entry.data; |
2499 | 0 | if(rep && rep->security > sec_status_bogus && |
2500 | 0 | (!qstate->env->cfg->serve_expired_ttl || |
2501 | 0 | qstate->env->cfg->serve_expired_ttl_reset || |
2502 | 0 | *qstate->env->now <= rep->serve_expired_ttl)) { |
2503 | 0 | verbose(VERB_ALGO, "validation failed but " |
2504 | 0 | "previously cached valid response " |
2505 | 0 | "exists; set serve-expired-norec-ttl " |
2506 | 0 | "for response in cache"); |
2507 | 0 | rep->serve_expired_norec_ttl = NORR_TTL + |
2508 | 0 | *qstate->env->now; |
2509 | 0 | if(qstate->env->cfg->serve_expired_ttl_reset && |
2510 | 0 | *qstate->env->now + qstate->env->cfg->serve_expired_ttl |
2511 | 0 | > rep->serve_expired_ttl) { |
2512 | 0 | verbose(VERB_ALGO, "reset serve-expired-ttl for " |
2513 | 0 | "valid response in cache"); |
2514 | 0 | rep->serve_expired_ttl = *qstate->env->now + |
2515 | 0 | qstate->env->cfg->serve_expired_ttl; |
2516 | 0 | } |
2517 | | /* Return an error response. |
2518 | | * If serve-expired-client-timeout is enabled, |
2519 | | * the client-timeout logic will try to find an |
2520 | | * (expired) answer in the cache as last |
2521 | | * resort. If it is not enabled, expired |
2522 | | * answers are already used before the mesh |
2523 | | * activation. */ |
2524 | 0 | qstate->return_rcode = LDNS_RCODE_SERVFAIL; |
2525 | 0 | qstate->return_msg = NULL; |
2526 | 0 | qstate->ext_state[id] = module_finished; |
2527 | 0 | lock_rw_unlock(&e->entry.lock); |
2528 | 0 | return 0; |
2529 | 0 | } |
2530 | 0 | lock_rw_unlock(&e->entry.lock); |
2531 | 0 | } |
2532 | | |
2533 | 0 | vq->orig_msg->rep->ttl = ve->bogus_ttl; |
2534 | 0 | vq->orig_msg->rep->prefetch_ttl = |
2535 | 0 | PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl); |
2536 | 0 | vq->orig_msg->rep->serve_expired_ttl = |
2537 | 0 | vq->orig_msg->rep->ttl + qstate->env->cfg->serve_expired_ttl; |
2538 | 0 | if((qstate->env->cfg->val_log_level >= 1 || |
2539 | 0 | qstate->env->cfg->log_servfail) && |
2540 | 0 | !qstate->env->cfg->val_log_squelch) { |
2541 | 0 | if(qstate->env->cfg->val_log_level < 2 && |
2542 | 0 | !qstate->env->cfg->log_servfail) |
2543 | 0 | log_query_info(NO_VERBOSE, "validation failure", |
2544 | 0 | &qstate->qinfo); |
2545 | 0 | else { |
2546 | 0 | char* err_str = errinf_to_str_bogus(qstate, |
2547 | 0 | qstate->region); |
2548 | 0 | if(err_str) { |
2549 | 0 | log_info("%s", err_str); |
2550 | 0 | vq->orig_msg->rep->reason_bogus_str = err_str; |
2551 | 0 | } |
2552 | 0 | } |
2553 | 0 | } |
2554 | | /* |
2555 | | * If set, the validator will not make messages bogus, instead |
2556 | | * indeterminate is issued, so that no clients receive SERVFAIL. |
2557 | | * This allows an operator to run validation 'shadow' without |
2558 | | * hurting responses to clients. |
2559 | | */ |
2560 | | /* If we are in permissive mode, bogus gets indeterminate */ |
2561 | 0 | if(qstate->env->cfg->val_permissive_mode) |
2562 | 0 | vq->orig_msg->rep->security = sec_status_indeterminate; |
2563 | 0 | } |
2564 | | |
2565 | 0 | if(vq->orig_msg->rep->security == sec_status_secure && |
2566 | 0 | qstate->env->cfg->root_key_sentinel && |
2567 | 0 | (qstate->qinfo.qtype == LDNS_RR_TYPE_A || |
2568 | 0 | qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA)) { |
2569 | 0 | char* keytag_start; |
2570 | 0 | uint16_t keytag; |
2571 | 0 | if(*qstate->qinfo.qname == strlen(SENTINEL_IS) + |
2572 | 0 | SENTINEL_KEYTAG_LEN && |
2573 | 0 | dname_lab_startswith(qstate->qinfo.qname, SENTINEL_IS, |
2574 | 0 | &keytag_start)) { |
2575 | 0 | if(sentinel_get_keytag(keytag_start, &keytag) && |
2576 | 0 | !anchor_has_keytag(qstate->env->anchors, |
2577 | 0 | (uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) { |
2578 | 0 | vq->orig_msg->rep->security = |
2579 | 0 | sec_status_secure_sentinel_fail; |
2580 | 0 | } |
2581 | 0 | } else if(*qstate->qinfo.qname == strlen(SENTINEL_NOT) + |
2582 | 0 | SENTINEL_KEYTAG_LEN && |
2583 | 0 | dname_lab_startswith(qstate->qinfo.qname, SENTINEL_NOT, |
2584 | 0 | &keytag_start)) { |
2585 | 0 | if(sentinel_get_keytag(keytag_start, &keytag) && |
2586 | 0 | anchor_has_keytag(qstate->env->anchors, |
2587 | 0 | (uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) { |
2588 | 0 | vq->orig_msg->rep->security = |
2589 | 0 | sec_status_secure_sentinel_fail; |
2590 | 0 | } |
2591 | 0 | } |
2592 | 0 | } |
2593 | | |
2594 | | /* Update rep->reason_bogus as it is the one being cached */ |
2595 | 0 | update_reason_bogus(vq->orig_msg->rep, errinf_to_reason_bogus(qstate)); |
2596 | 0 | if(vq->orig_msg->rep->security != sec_status_bogus && |
2597 | 0 | vq->orig_msg->rep->security != sec_status_secure_sentinel_fail |
2598 | 0 | && vq->orig_msg->rep->reason_bogus == LDNS_EDE_DNSSEC_BOGUS) { |
2599 | | /* Not interested in any DNSSEC EDE here, validator by default |
2600 | | * uses LDNS_EDE_DNSSEC_BOGUS; |
2601 | | * TODO revisit default value for the module */ |
2602 | 0 | vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE; |
2603 | 0 | } |
2604 | | |
2605 | | /* store results in cache */ |
2606 | 0 | if((qstate->query_flags&BIT_RD)) { |
2607 | | /* if secure, this will override cache anyway, no need |
2608 | | * to check if from parentNS */ |
2609 | 0 | if(!qstate->no_cache_store) { |
2610 | 0 | if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, |
2611 | 0 | vq->orig_msg->rep, 0, qstate->prefetch_leeway, |
2612 | 0 | 0, qstate->region, qstate->query_flags, |
2613 | 0 | qstate->qstarttime, qstate->is_valrec)) { |
2614 | 0 | log_err("out of memory caching validator results"); |
2615 | 0 | } |
2616 | 0 | } |
2617 | 0 | } else { |
2618 | | /* for a referral, store the verified RRsets */ |
2619 | | /* and this does not get prefetched, so no leeway */ |
2620 | 0 | if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, |
2621 | 0 | vq->orig_msg->rep, 1, 0, 0, qstate->region, |
2622 | 0 | qstate->query_flags, qstate->qstarttime, |
2623 | 0 | qstate->is_valrec)) { |
2624 | 0 | log_err("out of memory caching validator results"); |
2625 | 0 | } |
2626 | 0 | } |
2627 | 0 | qstate->return_rcode = LDNS_RCODE_NOERROR; |
2628 | 0 | qstate->return_msg = vq->orig_msg; |
2629 | 0 | qstate->ext_state[id] = module_finished; |
2630 | 0 | return 0; |
2631 | 0 | } |
2632 | | |
2633 | | /** |
2634 | | * Handle validator state. |
2635 | | * If a method returns true, the next state is started. If false, then |
2636 | | * processing will stop. |
2637 | | * @param qstate: query state. |
2638 | | * @param vq: validator query state. |
2639 | | * @param ve: validator shared global environment. |
2640 | | * @param id: module id. |
2641 | | */ |
2642 | | static void |
2643 | | val_handle(struct module_qstate* qstate, struct val_qstate* vq, |
2644 | | struct val_env* ve, int id) |
2645 | 0 | { |
2646 | 0 | int cont = 1; |
2647 | 0 | while(cont) { |
2648 | 0 | verbose(VERB_ALGO, "val handle processing q with state %s", |
2649 | 0 | val_state_to_string(vq->state)); |
2650 | 0 | switch(vq->state) { |
2651 | 0 | case VAL_INIT_STATE: |
2652 | 0 | cont = processInit(qstate, vq, ve, id); |
2653 | 0 | break; |
2654 | 0 | case VAL_FINDKEY_STATE: |
2655 | 0 | cont = processFindKey(qstate, vq, id); |
2656 | 0 | break; |
2657 | 0 | case VAL_VALIDATE_STATE: |
2658 | 0 | cont = processValidate(qstate, vq, ve, id); |
2659 | 0 | break; |
2660 | 0 | case VAL_FINISHED_STATE: |
2661 | 0 | cont = processFinished(qstate, vq, ve, id); |
2662 | 0 | break; |
2663 | 0 | default: |
2664 | 0 | log_warn("validator: invalid state %d", |
2665 | 0 | vq->state); |
2666 | 0 | cont = 0; |
2667 | 0 | break; |
2668 | 0 | } |
2669 | 0 | } |
2670 | 0 | } |
2671 | | |
2672 | | void |
2673 | | val_operate(struct module_qstate* qstate, enum module_ev event, int id, |
2674 | | struct outbound_entry* outbound) |
2675 | 0 | { |
2676 | 0 | struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; |
2677 | 0 | struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id]; |
2678 | 0 | verbose(VERB_QUERY, "validator[module %d] operate: extstate:%s " |
2679 | 0 | "event:%s", id, strextstate(qstate->ext_state[id]), |
2680 | 0 | strmodulevent(event)); |
2681 | 0 | log_query_info(VERB_QUERY, "validator operate: query", |
2682 | 0 | &qstate->qinfo); |
2683 | 0 | if(vq && qstate->qinfo.qname != vq->qchase.qname) |
2684 | 0 | log_query_info(VERB_QUERY, "validator operate: chased to", |
2685 | 0 | &vq->qchase); |
2686 | 0 | (void)outbound; |
2687 | 0 | if(event == module_event_new || |
2688 | 0 | (event == module_event_pass && vq == NULL)) { |
2689 | | |
2690 | | /* pass request to next module, to get it */ |
2691 | 0 | verbose(VERB_ALGO, "validator: pass to next module"); |
2692 | 0 | qstate->ext_state[id] = module_wait_module; |
2693 | 0 | return; |
2694 | 0 | } |
2695 | 0 | if(event == module_event_moddone) { |
2696 | | /* check if validation is needed */ |
2697 | 0 | verbose(VERB_ALGO, "validator: nextmodule returned"); |
2698 | |
|
2699 | 0 | if(!needs_validation(qstate, qstate->return_rcode, |
2700 | 0 | qstate->return_msg)) { |
2701 | | /* no need to validate this */ |
2702 | 0 | if(qstate->return_msg) |
2703 | 0 | qstate->return_msg->rep->security = |
2704 | 0 | sec_status_indeterminate; |
2705 | 0 | qstate->ext_state[id] = module_finished; |
2706 | 0 | return; |
2707 | 0 | } |
2708 | 0 | if(already_validated(qstate->return_msg)) { |
2709 | 0 | qstate->ext_state[id] = module_finished; |
2710 | 0 | return; |
2711 | 0 | } |
2712 | 0 | if(qstate->rpz_applied) { |
2713 | 0 | verbose(VERB_ALGO, "rpz applied, mark it as insecure"); |
2714 | 0 | if(qstate->return_msg) |
2715 | 0 | qstate->return_msg->rep->security = |
2716 | 0 | sec_status_insecure; |
2717 | 0 | qstate->ext_state[id] = module_finished; |
2718 | 0 | return; |
2719 | 0 | } |
2720 | | /* qclass ANY should have validation result from spawned |
2721 | | * queries. If we get here, it is bogus or an internal error */ |
2722 | 0 | if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) { |
2723 | 0 | verbose(VERB_ALGO, "cannot validate classANY: bogus"); |
2724 | 0 | if(qstate->return_msg) { |
2725 | 0 | qstate->return_msg->rep->security = |
2726 | 0 | sec_status_bogus; |
2727 | 0 | update_reason_bogus(qstate->return_msg->rep, LDNS_EDE_DNSSEC_BOGUS); |
2728 | 0 | } |
2729 | 0 | qstate->ext_state[id] = module_finished; |
2730 | 0 | return; |
2731 | 0 | } |
2732 | | /* create state to start validation */ |
2733 | 0 | qstate->ext_state[id] = module_error; /* override this */ |
2734 | 0 | if(!vq) { |
2735 | 0 | vq = val_new(qstate, id); |
2736 | 0 | if(!vq) { |
2737 | 0 | log_err("validator: malloc failure"); |
2738 | 0 | qstate->ext_state[id] = module_error; |
2739 | 0 | return; |
2740 | 0 | } |
2741 | 0 | } else if(!vq->orig_msg) { |
2742 | 0 | if(!val_new_getmsg(qstate, vq)) { |
2743 | 0 | log_err("validator: malloc failure"); |
2744 | 0 | qstate->ext_state[id] = module_error; |
2745 | 0 | return; |
2746 | 0 | } |
2747 | 0 | } |
2748 | 0 | val_handle(qstate, vq, ve, id); |
2749 | 0 | return; |
2750 | 0 | } |
2751 | 0 | if(event == module_event_pass) { |
2752 | 0 | qstate->ext_state[id] = module_error; /* override this */ |
2753 | | /* continue processing, since val_env exists */ |
2754 | 0 | val_handle(qstate, vq, ve, id); |
2755 | 0 | return; |
2756 | 0 | } |
2757 | 0 | log_err("validator: bad event %s", strmodulevent(event)); |
2758 | 0 | qstate->ext_state[id] = module_error; |
2759 | 0 | return; |
2760 | 0 | } |
2761 | | |
2762 | | /** |
2763 | | * Evaluate the response to a priming request. |
2764 | | * |
2765 | | * @param dnskey_rrset: DNSKEY rrset (can be NULL if none) in prime reply. |
2766 | | * (this rrset is allocated in the wrong region, not the qstate). |
2767 | | * @param ta: trust anchor. |
2768 | | * @param qstate: qstate that needs key. |
2769 | | * @param id: module id. |
2770 | | * @param sub_qstate: the sub query state, that is the lookup that fetched |
2771 | | * the trust anchor data, it contains error information for the answer. |
2772 | | * @return new key entry or NULL on allocation failure. |
2773 | | * The key entry will either contain a validated DNSKEY rrset, or |
2774 | | * represent a Null key (query failed, but validation did not), or a |
2775 | | * Bad key (validation failed). |
2776 | | */ |
2777 | | static struct key_entry_key* |
2778 | | primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, |
2779 | | struct trust_anchor* ta, struct module_qstate* qstate, int id, |
2780 | | struct module_qstate* sub_qstate) |
2781 | 0 | { |
2782 | 0 | struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; |
2783 | 0 | struct key_entry_key* kkey = NULL; |
2784 | 0 | enum sec_status sec = sec_status_unchecked; |
2785 | 0 | char reasonbuf[256]; |
2786 | 0 | char* reason = NULL; |
2787 | 0 | sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; |
2788 | 0 | int downprot = qstate->env->cfg->harden_algo_downgrade; |
2789 | |
|
2790 | 0 | if(!dnskey_rrset) { |
2791 | 0 | char* err = errinf_to_str_misc(sub_qstate); |
2792 | 0 | char rstr[1024]; |
2793 | 0 | log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " |
2794 | 0 | "could not fetch DNSKEY rrset", |
2795 | 0 | ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); |
2796 | 0 | reason_bogus = LDNS_EDE_DNSKEY_MISSING; |
2797 | 0 | if(!err) { |
2798 | 0 | snprintf(rstr, sizeof(rstr), "no DNSKEY rrset"); |
2799 | 0 | } else { |
2800 | 0 | snprintf(rstr, sizeof(rstr), "no DNSKEY rrset " |
2801 | 0 | "[%s]", err); |
2802 | 0 | } |
2803 | 0 | if(qstate->env->cfg->harden_dnssec_stripped) { |
2804 | 0 | errinf_ede(qstate, rstr, reason_bogus); |
2805 | 0 | kkey = key_entry_create_bad(qstate->region, ta->name, |
2806 | 0 | ta->namelen, ta->dclass, BOGUS_KEY_TTL, |
2807 | 0 | reason_bogus, rstr, *qstate->env->now); |
2808 | 0 | } else kkey = key_entry_create_null(qstate->region, ta->name, |
2809 | 0 | ta->namelen, ta->dclass, NULL_KEY_TTL, |
2810 | 0 | reason_bogus, rstr, *qstate->env->now); |
2811 | 0 | if(!kkey) { |
2812 | 0 | log_err("out of memory: allocate fail prime key"); |
2813 | 0 | return NULL; |
2814 | 0 | } |
2815 | 0 | return kkey; |
2816 | 0 | } |
2817 | | /* attempt to verify with trust anchor DS and DNSKEY */ |
2818 | 0 | kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, |
2819 | 0 | dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot, |
2820 | 0 | &reason, &reason_bogus, qstate, reasonbuf, sizeof(reasonbuf)); |
2821 | 0 | if(!kkey) { |
2822 | 0 | log_err("out of memory: verifying prime TA"); |
2823 | 0 | return NULL; |
2824 | 0 | } |
2825 | 0 | if(key_entry_isgood(kkey)) |
2826 | 0 | sec = sec_status_secure; |
2827 | 0 | else |
2828 | 0 | sec = sec_status_bogus; |
2829 | 0 | verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", |
2830 | 0 | sec_status_to_string(sec)); |
2831 | |
|
2832 | 0 | if(sec != sec_status_secure) { |
2833 | 0 | log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " |
2834 | 0 | "DNSKEY rrset is not secure", |
2835 | 0 | ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); |
2836 | | /* NOTE: in this case, we should probably reject the trust |
2837 | | * anchor for longer, perhaps forever. */ |
2838 | 0 | if(qstate->env->cfg->harden_dnssec_stripped) { |
2839 | 0 | errinf_ede(qstate, reason, reason_bogus); |
2840 | 0 | kkey = key_entry_create_bad(qstate->region, ta->name, |
2841 | 0 | ta->namelen, ta->dclass, BOGUS_KEY_TTL, |
2842 | 0 | reason_bogus, reason, |
2843 | 0 | *qstate->env->now); |
2844 | 0 | } else kkey = key_entry_create_null(qstate->region, ta->name, |
2845 | 0 | ta->namelen, ta->dclass, NULL_KEY_TTL, |
2846 | 0 | reason_bogus, reason, |
2847 | 0 | *qstate->env->now); |
2848 | 0 | if(!kkey) { |
2849 | 0 | log_err("out of memory: allocate null prime key"); |
2850 | 0 | return NULL; |
2851 | 0 | } |
2852 | 0 | return kkey; |
2853 | 0 | } |
2854 | | |
2855 | 0 | log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor", |
2856 | 0 | ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); |
2857 | 0 | return kkey; |
2858 | 0 | } |
2859 | | |
2860 | | /** |
2861 | | * In inform supers, with the resulting message and rcode and the current |
2862 | | * keyset in the super state, validate the DS response, returning a KeyEntry. |
2863 | | * |
2864 | | * @param qstate: query state that is validating and asked for a DS. |
2865 | | * @param vq: validator query state |
2866 | | * @param id: module id. |
2867 | | * @param rcode: rcode result value. |
2868 | | * @param msg: result message (if rcode is OK). |
2869 | | * @param qinfo: from the sub query state, query info. |
2870 | | * @param ke: the key entry to return. It returns |
2871 | | * is_bad if the DS response fails to validate, is_null if the |
2872 | | * DS response indicated an end to secure space, is_good if the DS |
2873 | | * validated. It returns ke=NULL if the DS response indicated that the |
2874 | | * request wasn't a delegation point. |
2875 | | * @param sub_qstate: the sub query state, that is the lookup that fetched |
2876 | | * the trust anchor data, it contains error information for the answer. |
2877 | | * Can be NULL. |
2878 | | * @return |
2879 | | * 0 on success, |
2880 | | * 1 on servfail error (malloc failure), |
2881 | | * 2 on NSEC3 suspend. |
2882 | | */ |
2883 | | static int |
2884 | | ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, |
2885 | | int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, |
2886 | | struct key_entry_key** ke, struct module_qstate* sub_qstate) |
2887 | 0 | { |
2888 | 0 | struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; |
2889 | 0 | char reasonbuf[256]; |
2890 | 0 | char* reason = NULL; |
2891 | 0 | sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; |
2892 | 0 | enum val_classification subtype; |
2893 | 0 | int verified; |
2894 | 0 | if(rcode != LDNS_RCODE_NOERROR) { |
2895 | 0 | char rc[16]; |
2896 | 0 | rc[0]=0; |
2897 | 0 | (void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc)); |
2898 | | /* errors here pretty much break validation */ |
2899 | 0 | verbose(VERB_DETAIL, "DS response was error, thus bogus"); |
2900 | 0 | errinf(qstate, rc); |
2901 | 0 | reason = "no DS"; |
2902 | 0 | if(sub_qstate) { |
2903 | 0 | char* err = errinf_to_str_misc(sub_qstate); |
2904 | 0 | if(err) { |
2905 | 0 | char buf[1024]; |
2906 | 0 | snprintf(buf, sizeof(buf), "[%s]", err); |
2907 | 0 | errinf(qstate, buf); |
2908 | 0 | } |
2909 | 0 | } |
2910 | 0 | reason_bogus = LDNS_EDE_NETWORK_ERROR; |
2911 | 0 | errinf_ede(qstate, reason, reason_bogus); |
2912 | 0 | goto return_bogus; |
2913 | 0 | } |
2914 | | |
2915 | 0 | subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0); |
2916 | 0 | if(subtype == VAL_CLASS_POSITIVE) { |
2917 | 0 | struct ub_packed_rrset_key* ds; |
2918 | 0 | enum sec_status sec; |
2919 | 0 | ds = reply_find_answer_rrset(qinfo, msg->rep); |
2920 | | /* If there was no DS rrset, then we have misclassified |
2921 | | * this message. */ |
2922 | 0 | if(!ds) { |
2923 | 0 | log_warn("internal error: POSITIVE DS response was " |
2924 | 0 | "missing DS."); |
2925 | 0 | reason = "no DS record"; |
2926 | 0 | errinf_ede(qstate, reason, reason_bogus); |
2927 | 0 | goto return_bogus; |
2928 | 0 | } |
2929 | | /* Verify only returns BOGUS or SECURE. If the rrset is |
2930 | | * bogus, then we are done. */ |
2931 | 0 | sec = val_verify_rrset_entry(qstate->env, ve, ds, |
2932 | 0 | vq->key_entry, &reason, &reason_bogus, |
2933 | 0 | LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf, |
2934 | 0 | sizeof(reasonbuf)); |
2935 | 0 | if(sec != sec_status_secure) { |
2936 | 0 | verbose(VERB_DETAIL, "DS rrset in DS response did " |
2937 | 0 | "not verify"); |
2938 | 0 | errinf_ede(qstate, reason, reason_bogus); |
2939 | 0 | goto return_bogus; |
2940 | 0 | } |
2941 | | |
2942 | | /* If the DS rrset validates, we still have to make sure |
2943 | | * that they are usable. */ |
2944 | 0 | if(!val_dsset_isusable(ds)) { |
2945 | | /* If they aren't usable, then we treat it like |
2946 | | * there was no DS. */ |
2947 | 0 | *ke = key_entry_create_null(qstate->region, |
2948 | 0 | qinfo->qname, qinfo->qname_len, qinfo->qclass, |
2949 | 0 | ub_packed_rrset_ttl(ds), |
2950 | 0 | LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL, |
2951 | 0 | *qstate->env->now); |
2952 | 0 | return (*ke) == NULL; |
2953 | 0 | } |
2954 | | |
2955 | | /* Otherwise, we return the positive response. */ |
2956 | 0 | log_query_info(VERB_DETAIL, "validated DS", qinfo); |
2957 | 0 | *ke = key_entry_create_rrset(qstate->region, |
2958 | 0 | qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, |
2959 | 0 | NULL, LDNS_EDE_NONE, NULL, *qstate->env->now); |
2960 | 0 | return (*ke) == NULL; |
2961 | 0 | } else if(subtype == VAL_CLASS_NODATA || |
2962 | 0 | subtype == VAL_CLASS_NAMEERROR) { |
2963 | | /* NODATA means that the qname exists, but that there was |
2964 | | * no DS. This is a pretty normal case. */ |
2965 | 0 | time_t proof_ttl = 0; |
2966 | 0 | enum sec_status sec; |
2967 | | |
2968 | | /* make sure there are NSECs or NSEC3s with signatures */ |
2969 | 0 | if(!val_has_signed_nsecs(msg->rep, &reason)) { |
2970 | 0 | verbose(VERB_ALGO, "no NSECs: %s", reason); |
2971 | 0 | reason_bogus = LDNS_EDE_NSEC_MISSING; |
2972 | 0 | errinf_ede(qstate, reason, reason_bogus); |
2973 | 0 | goto return_bogus; |
2974 | 0 | } |
2975 | | |
2976 | | /* For subtype Name Error. |
2977 | | * attempt ANS 2.8.1.0 compatibility where it sets rcode |
2978 | | * to nxdomain, but really this is an Nodata/Noerror response. |
2979 | | * Find and prove the empty nonterminal in that case */ |
2980 | | |
2981 | | /* Try to prove absence of the DS with NSEC */ |
2982 | 0 | sec = val_nsec_prove_nodata_dsreply( |
2983 | 0 | qstate->env, ve, qinfo, msg->rep, vq->key_entry, |
2984 | 0 | &proof_ttl, &reason, &reason_bogus, qstate, |
2985 | 0 | reasonbuf, sizeof(reasonbuf)); |
2986 | 0 | switch(sec) { |
2987 | 0 | case sec_status_secure: |
2988 | 0 | verbose(VERB_DETAIL, "NSEC RRset for the " |
2989 | 0 | "referral proved no DS."); |
2990 | 0 | *ke = key_entry_create_null(qstate->region, |
2991 | 0 | qinfo->qname, qinfo->qname_len, |
2992 | 0 | qinfo->qclass, proof_ttl, |
2993 | 0 | LDNS_EDE_NONE, NULL, |
2994 | 0 | *qstate->env->now); |
2995 | 0 | return (*ke) == NULL; |
2996 | 0 | case sec_status_insecure: |
2997 | 0 | verbose(VERB_DETAIL, "NSEC RRset for the " |
2998 | 0 | "referral proved not a delegation point"); |
2999 | 0 | *ke = NULL; |
3000 | 0 | return 0; |
3001 | 0 | case sec_status_bogus: |
3002 | 0 | verbose(VERB_DETAIL, "NSEC RRset for the " |
3003 | 0 | "referral did not prove no DS."); |
3004 | 0 | errinf(qstate, reason); |
3005 | 0 | goto return_bogus; |
3006 | 0 | case sec_status_unchecked: |
3007 | 0 | default: |
3008 | | /* NSEC proof did not work, try next */ |
3009 | 0 | break; |
3010 | 0 | } |
3011 | | |
3012 | 0 | if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { |
3013 | 0 | log_err("malloc failure in ds_response_to_ke for " |
3014 | 0 | "NSEC3 cache"); |
3015 | 0 | reason = "malloc failure"; |
3016 | 0 | errinf_ede(qstate, reason, 0); |
3017 | 0 | goto return_bogus; |
3018 | 0 | } |
3019 | 0 | sec = nsec3_prove_nods(qstate->env, ve, |
3020 | 0 | msg->rep->rrsets + msg->rep->an_numrrsets, |
3021 | 0 | msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, |
3022 | 0 | &reason_bogus, qstate, &vq->nsec3_cache_table, |
3023 | 0 | reasonbuf, sizeof(reasonbuf)); |
3024 | 0 | switch(sec) { |
3025 | 0 | case sec_status_insecure: |
3026 | | /* case insecure also continues to unsigned |
3027 | | * space. If nsec3-iter-count too high or |
3028 | | * optout, then treat below as unsigned */ |
3029 | 0 | case sec_status_secure: |
3030 | 0 | verbose(VERB_DETAIL, "NSEC3s for the " |
3031 | 0 | "referral proved no DS."); |
3032 | 0 | *ke = key_entry_create_null(qstate->region, |
3033 | 0 | qinfo->qname, qinfo->qname_len, |
3034 | 0 | qinfo->qclass, proof_ttl, |
3035 | 0 | LDNS_EDE_NONE, NULL, |
3036 | 0 | *qstate->env->now); |
3037 | 0 | return (*ke) == NULL; |
3038 | 0 | case sec_status_indeterminate: |
3039 | 0 | verbose(VERB_DETAIL, "NSEC3s for the " |
3040 | 0 | "referral proved no delegation"); |
3041 | 0 | *ke = NULL; |
3042 | 0 | return 0; |
3043 | 0 | case sec_status_bogus: |
3044 | 0 | verbose(VERB_DETAIL, "NSEC3s for the " |
3045 | 0 | "referral did not prove no DS."); |
3046 | 0 | errinf_ede(qstate, reason, reason_bogus); |
3047 | 0 | goto return_bogus; |
3048 | 0 | case sec_status_unchecked: |
3049 | 0 | return 2; |
3050 | 0 | default: |
3051 | | /* NSEC3 proof did not work */ |
3052 | 0 | break; |
3053 | 0 | } |
3054 | | |
3055 | | /* Apparently, no available NSEC/NSEC3 proved NODATA, so |
3056 | | * this is BOGUS. */ |
3057 | 0 | verbose(VERB_DETAIL, "DS %s ran out of options, so return " |
3058 | 0 | "bogus", val_classification_to_string(subtype)); |
3059 | 0 | reason = "no DS but also no proof of that"; |
3060 | 0 | errinf_ede(qstate, reason, reason_bogus); |
3061 | 0 | goto return_bogus; |
3062 | 0 | } else if(subtype == VAL_CLASS_CNAME || |
3063 | 0 | subtype == VAL_CLASS_CNAMENOANSWER) { |
3064 | | /* if the CNAME matches the exact name we want and is signed |
3065 | | * properly, then also, we are sure that no DS exists there, |
3066 | | * much like a NODATA proof */ |
3067 | 0 | enum sec_status sec; |
3068 | 0 | struct ub_packed_rrset_key* cname; |
3069 | 0 | cname = reply_find_rrset_section_an(msg->rep, qinfo->qname, |
3070 | 0 | qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass); |
3071 | 0 | if(!cname) { |
3072 | 0 | reason = "validator classified CNAME but no " |
3073 | 0 | "CNAME of the queried name for DS"; |
3074 | 0 | errinf_ede(qstate, reason, reason_bogus); |
3075 | 0 | goto return_bogus; |
3076 | 0 | } |
3077 | 0 | if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count |
3078 | 0 | == 0) { |
3079 | 0 | if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep-> |
3080 | 0 | rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) { |
3081 | 0 | reason = "DS got DNAME answer"; |
3082 | 0 | } else { |
3083 | 0 | reason = "DS got unsigned CNAME answer"; |
3084 | 0 | } |
3085 | 0 | errinf_ede(qstate, reason, reason_bogus); |
3086 | 0 | goto return_bogus; |
3087 | 0 | } |
3088 | 0 | sec = val_verify_rrset_entry(qstate->env, ve, cname, |
3089 | 0 | vq->key_entry, &reason, &reason_bogus, |
3090 | 0 | LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf, |
3091 | 0 | sizeof(reasonbuf)); |
3092 | 0 | if(sec == sec_status_secure) { |
3093 | 0 | verbose(VERB_ALGO, "CNAME validated, " |
3094 | 0 | "proof that DS does not exist"); |
3095 | | /* and that it is not a referral point */ |
3096 | 0 | *ke = NULL; |
3097 | 0 | return 0; |
3098 | 0 | } |
3099 | 0 | errinf(qstate, "CNAME in DS response was not secure."); |
3100 | 0 | errinf_ede(qstate, reason, reason_bogus); |
3101 | 0 | goto return_bogus; |
3102 | 0 | } else { |
3103 | 0 | verbose(VERB_QUERY, "Encountered an unhandled type of " |
3104 | 0 | "DS response, thus bogus."); |
3105 | 0 | errinf(qstate, "no DS and"); |
3106 | 0 | reason = "no DS"; |
3107 | 0 | if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) { |
3108 | 0 | char rc[16]; |
3109 | 0 | rc[0]=0; |
3110 | 0 | (void)sldns_wire2str_rcode_buf((int)FLAGS_GET_RCODE( |
3111 | 0 | msg->rep->flags), rc, sizeof(rc)); |
3112 | 0 | errinf(qstate, rc); |
3113 | 0 | } else errinf(qstate, val_classification_to_string(subtype)); |
3114 | 0 | errinf(qstate, "message fails to prove that"); |
3115 | 0 | goto return_bogus; |
3116 | 0 | } |
3117 | 0 | return_bogus: |
3118 | 0 | *ke = key_entry_create_bad(qstate->region, qinfo->qname, |
3119 | 0 | qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL, |
3120 | 0 | reason_bogus, reason, *qstate->env->now); |
3121 | 0 | return (*ke) == NULL; |
3122 | 0 | } |
3123 | | |
3124 | | /** |
3125 | | * Process DS response. Called from inform_supers. |
3126 | | * Because it is in inform_supers, the mesh itself is busy doing callbacks |
3127 | | * for a state that is to be deleted soon; don't touch the mesh; instead |
3128 | | * set a state in the super, as the super will be reactivated soon. |
3129 | | * Perform processing to determine what state to set in the super. |
3130 | | * |
3131 | | * @param qstate: query state that is validating and asked for a DS. |
3132 | | * @param vq: validator query state |
3133 | | * @param id: module id. |
3134 | | * @param rcode: rcode result value. |
3135 | | * @param msg: result message (if rcode is OK). |
3136 | | * @param qinfo: from the sub query state, query info. |
3137 | | * @param origin: the origin of msg. |
3138 | | * @param suspend: returned true if the task takes too long and needs to |
3139 | | * suspend to continue the effort later. |
3140 | | * @param sub_qstate: the sub query state, that is the lookup that fetched |
3141 | | * the trust anchor data, it contains error information for the answer. |
3142 | | * Can be NULL. |
3143 | | */ |
3144 | | static void |
3145 | | process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, |
3146 | | int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, |
3147 | | struct sock_list* origin, int* suspend, |
3148 | | struct module_qstate* sub_qstate) |
3149 | 0 | { |
3150 | 0 | struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; |
3151 | 0 | struct key_entry_key* dske = NULL; |
3152 | 0 | uint8_t* olds = vq->empty_DS_name; |
3153 | 0 | int ret; |
3154 | 0 | *suspend = 0; |
3155 | 0 | vq->empty_DS_name = NULL; |
3156 | 0 | if(sub_qstate && sub_qstate->rpz_applied) { |
3157 | 0 | verbose(VERB_ALGO, "rpz was applied to the DS lookup, " |
3158 | 0 | "make it insecure"); |
3159 | 0 | vq->key_entry = NULL; |
3160 | 0 | vq->state = VAL_FINISHED_STATE; |
3161 | 0 | vq->chase_reply->security = sec_status_insecure; |
3162 | 0 | return; |
3163 | 0 | } |
3164 | 0 | ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske, |
3165 | 0 | sub_qstate); |
3166 | 0 | if(ret != 0) { |
3167 | 0 | switch(ret) { |
3168 | 0 | case 1: |
3169 | 0 | log_err("malloc failure in process_ds_response"); |
3170 | 0 | vq->key_entry = NULL; /* make it error */ |
3171 | 0 | vq->state = VAL_VALIDATE_STATE; |
3172 | 0 | return; |
3173 | 0 | case 2: |
3174 | 0 | *suspend = 1; |
3175 | 0 | return; |
3176 | 0 | default: |
3177 | 0 | log_err("unhandled error value for ds_response_to_ke"); |
3178 | 0 | vq->key_entry = NULL; /* make it error */ |
3179 | 0 | vq->state = VAL_VALIDATE_STATE; |
3180 | 0 | return; |
3181 | 0 | } |
3182 | 0 | } |
3183 | 0 | if(dske == NULL) { |
3184 | 0 | vq->empty_DS_name = regional_alloc_init(qstate->region, |
3185 | 0 | qinfo->qname, qinfo->qname_len); |
3186 | 0 | if(!vq->empty_DS_name) { |
3187 | 0 | log_err("malloc failure in empty_DS_name"); |
3188 | 0 | vq->key_entry = NULL; /* make it error */ |
3189 | 0 | vq->state = VAL_VALIDATE_STATE; |
3190 | 0 | return; |
3191 | 0 | } |
3192 | 0 | vq->empty_DS_len = qinfo->qname_len; |
3193 | 0 | vq->chain_blacklist = NULL; |
3194 | | /* ds response indicated that we aren't on a delegation point. |
3195 | | * Keep the forState.state on FINDKEY. */ |
3196 | 0 | } else if(key_entry_isgood(dske)) { |
3197 | 0 | vq->ds_rrset = key_entry_get_rrset(dske, qstate->region); |
3198 | 0 | if(!vq->ds_rrset) { |
3199 | 0 | log_err("malloc failure in process DS"); |
3200 | 0 | vq->key_entry = NULL; /* make it error */ |
3201 | 0 | vq->state = VAL_VALIDATE_STATE; |
3202 | 0 | return; |
3203 | 0 | } |
3204 | 0 | vq->chain_blacklist = NULL; /* fresh blacklist for next part*/ |
3205 | | /* Keep the forState.state on FINDKEY. */ |
3206 | 0 | } else if(key_entry_isbad(dske) |
3207 | 0 | && vq->restart_count < ve->max_restart) { |
3208 | 0 | vq->empty_DS_name = olds; |
3209 | 0 | val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1); |
3210 | 0 | qstate->errinf = NULL; |
3211 | 0 | vq->restart_count++; |
3212 | 0 | } else { |
3213 | 0 | if(key_entry_isbad(dske)) { |
3214 | 0 | errinf_origin(qstate, origin); |
3215 | 0 | errinf_dname(qstate, "for DS", qinfo->qname); |
3216 | 0 | } |
3217 | | /* NOTE: the reason for the DS to be not good (that is, |
3218 | | * either bad or null) should have been logged by |
3219 | | * dsResponseToKE. */ |
3220 | 0 | vq->key_entry = dske; |
3221 | | /* The FINDKEY phase has ended, so move on. */ |
3222 | 0 | vq->state = VAL_VALIDATE_STATE; |
3223 | 0 | } |
3224 | 0 | } |
3225 | | |
3226 | | /** |
3227 | | * Process DNSKEY response. Called from inform_supers. |
3228 | | * Sets the key entry in the state. |
3229 | | * Because it is in inform_supers, the mesh itself is busy doing callbacks |
3230 | | * for a state that is to be deleted soon; don't touch the mesh; instead |
3231 | | * set a state in the super, as the super will be reactivated soon. |
3232 | | * Perform processing to determine what state to set in the super. |
3233 | | * |
3234 | | * @param qstate: query state that is validating and asked for a DNSKEY. |
3235 | | * @param vq: validator query state |
3236 | | * @param id: module id. |
3237 | | * @param rcode: rcode result value. |
3238 | | * @param msg: result message (if rcode is OK). |
3239 | | * @param qinfo: from the sub query state, query info. |
3240 | | * @param origin: the origin of msg. |
3241 | | * @param sub_qstate: the sub query state, that is the lookup that fetched |
3242 | | * the trust anchor data, it contains error information for the answer. |
3243 | | */ |
3244 | | static void |
3245 | | process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, |
3246 | | int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, |
3247 | | struct sock_list* origin, struct module_qstate* sub_qstate) |
3248 | 0 | { |
3249 | 0 | struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; |
3250 | 0 | struct key_entry_key* old = vq->key_entry; |
3251 | 0 | struct ub_packed_rrset_key* dnskey = NULL; |
3252 | 0 | int downprot; |
3253 | 0 | char reasonbuf[256]; |
3254 | 0 | char* reason = NULL; |
3255 | 0 | sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; |
3256 | |
|
3257 | 0 | if(sub_qstate && sub_qstate->rpz_applied) { |
3258 | 0 | verbose(VERB_ALGO, "rpz was applied to the DNSKEY lookup, " |
3259 | 0 | "make it insecure"); |
3260 | 0 | vq->key_entry = NULL; |
3261 | 0 | vq->state = VAL_FINISHED_STATE; |
3262 | 0 | vq->chase_reply->security = sec_status_insecure; |
3263 | 0 | return; |
3264 | 0 | } |
3265 | | |
3266 | 0 | if(rcode == LDNS_RCODE_NOERROR) |
3267 | 0 | dnskey = reply_find_answer_rrset(qinfo, msg->rep); |
3268 | |
|
3269 | 0 | if(dnskey == NULL) { |
3270 | 0 | char* err; |
3271 | 0 | char rstr[1024]; |
3272 | | /* bad response */ |
3273 | 0 | verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to " |
3274 | 0 | "DNSKEY query."); |
3275 | |
|
3276 | 0 | if(vq->restart_count < ve->max_restart) { |
3277 | 0 | val_blacklist(&vq->chain_blacklist, qstate->region, |
3278 | 0 | origin, 1); |
3279 | 0 | qstate->errinf = NULL; |
3280 | 0 | vq->restart_count++; |
3281 | 0 | return; |
3282 | 0 | } |
3283 | 0 | err = errinf_to_str_misc(sub_qstate); |
3284 | 0 | if(!err) { |
3285 | 0 | snprintf(rstr, sizeof(rstr), "No DNSKEY record"); |
3286 | 0 | } else { |
3287 | 0 | snprintf(rstr, sizeof(rstr), "No DNSKEY record " |
3288 | 0 | "[%s]", err); |
3289 | 0 | } |
3290 | 0 | reason_bogus = LDNS_EDE_DNSKEY_MISSING; |
3291 | 0 | vq->key_entry = key_entry_create_bad(qstate->region, |
3292 | 0 | qinfo->qname, qinfo->qname_len, qinfo->qclass, |
3293 | 0 | BOGUS_KEY_TTL, reason_bogus, rstr, *qstate->env->now); |
3294 | 0 | if(!vq->key_entry) { |
3295 | 0 | log_err("alloc failure in missing dnskey response"); |
3296 | | /* key_entry is NULL for failure in Validate */ |
3297 | 0 | } |
3298 | 0 | errinf_ede(qstate, rstr, reason_bogus); |
3299 | 0 | errinf_origin(qstate, origin); |
3300 | 0 | errinf_dname(qstate, "for key", qinfo->qname); |
3301 | 0 | vq->state = VAL_VALIDATE_STATE; |
3302 | 0 | return; |
3303 | 0 | } |
3304 | 0 | if(!vq->ds_rrset) { |
3305 | 0 | log_err("internal error: no DS rrset for new DNSKEY response"); |
3306 | 0 | vq->key_entry = NULL; |
3307 | 0 | vq->state = VAL_VALIDATE_STATE; |
3308 | 0 | return; |
3309 | 0 | } |
3310 | 0 | downprot = qstate->env->cfg->harden_algo_downgrade; |
3311 | 0 | vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, |
3312 | 0 | ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, |
3313 | 0 | qstate, reasonbuf, sizeof(reasonbuf)); |
3314 | |
|
3315 | 0 | if(!vq->key_entry) { |
3316 | 0 | log_err("out of memory in verify new DNSKEYs"); |
3317 | 0 | vq->state = VAL_VALIDATE_STATE; |
3318 | 0 | return; |
3319 | 0 | } |
3320 | | /* If the key entry isBad or isNull, then we can move on to the next |
3321 | | * state. */ |
3322 | 0 | if(!key_entry_isgood(vq->key_entry)) { |
3323 | 0 | if(key_entry_isbad(vq->key_entry)) { |
3324 | 0 | if(vq->restart_count < ve->max_restart) { |
3325 | 0 | val_blacklist(&vq->chain_blacklist, |
3326 | 0 | qstate->region, origin, 1); |
3327 | 0 | qstate->errinf = NULL; |
3328 | 0 | vq->restart_count++; |
3329 | 0 | vq->key_entry = old; |
3330 | 0 | return; |
3331 | 0 | } |
3332 | 0 | verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, " |
3333 | 0 | "thus bogus."); |
3334 | 0 | errinf_ede(qstate, reason, reason_bogus); |
3335 | 0 | errinf_origin(qstate, origin); |
3336 | 0 | errinf_dname(qstate, "for key", qinfo->qname); |
3337 | 0 | } |
3338 | 0 | vq->chain_blacklist = NULL; |
3339 | 0 | vq->state = VAL_VALIDATE_STATE; |
3340 | 0 | return; |
3341 | 0 | } |
3342 | 0 | vq->chain_blacklist = NULL; |
3343 | 0 | qstate->errinf = NULL; |
3344 | | |
3345 | | /* The DNSKEY validated, so cache it as a trusted key rrset. */ |
3346 | 0 | key_cache_insert(ve->kcache, vq->key_entry, |
3347 | 0 | qstate->env->cfg->val_log_level >= 2); |
3348 | | |
3349 | | /* If good, we stay in the FINDKEY state. */ |
3350 | 0 | log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo); |
3351 | 0 | } |
3352 | | |
3353 | | /** |
3354 | | * Process prime response |
3355 | | * Sets the key entry in the state. |
3356 | | * |
3357 | | * @param qstate: query state that is validating and primed a trust anchor. |
3358 | | * @param vq: validator query state |
3359 | | * @param id: module id. |
3360 | | * @param rcode: rcode result value. |
3361 | | * @param msg: result message (if rcode is OK). |
3362 | | * @param origin: the origin of msg. |
3363 | | * @param sub_qstate: the sub query state, that is the lookup that fetched |
3364 | | * the trust anchor data, it contains error information for the answer. |
3365 | | */ |
3366 | | static void |
3367 | | process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, |
3368 | | int id, int rcode, struct dns_msg* msg, struct sock_list* origin, |
3369 | | struct module_qstate* sub_qstate) |
3370 | 0 | { |
3371 | 0 | struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; |
3372 | 0 | struct ub_packed_rrset_key* dnskey_rrset = NULL; |
3373 | 0 | struct trust_anchor* ta = anchor_find(qstate->env->anchors, |
3374 | 0 | vq->trust_anchor_name, vq->trust_anchor_labs, |
3375 | 0 | vq->trust_anchor_len, vq->qchase.qclass); |
3376 | 0 | if(!ta) { |
3377 | | /* trust anchor revoked, restart with less anchors */ |
3378 | 0 | vq->state = VAL_INIT_STATE; |
3379 | 0 | if(!vq->trust_anchor_name) |
3380 | 0 | vq->state = VAL_VALIDATE_STATE; /* break a loop */ |
3381 | 0 | vq->trust_anchor_name = NULL; |
3382 | 0 | return; |
3383 | 0 | } |
3384 | | /* Fetch and validate the keyEntry that corresponds to the |
3385 | | * current trust anchor. */ |
3386 | 0 | if(rcode == LDNS_RCODE_NOERROR) { |
3387 | 0 | dnskey_rrset = reply_find_rrset_section_an(msg->rep, |
3388 | 0 | ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY, |
3389 | 0 | ta->dclass); |
3390 | 0 | } |
3391 | |
|
3392 | 0 | if(ta->autr) { |
3393 | 0 | if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset, |
3394 | 0 | qstate)) { |
3395 | | /* trust anchor revoked, restart with less anchors */ |
3396 | 0 | vq->state = VAL_INIT_STATE; |
3397 | 0 | vq->trust_anchor_name = NULL; |
3398 | 0 | return; |
3399 | 0 | } |
3400 | 0 | } |
3401 | 0 | vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id, |
3402 | 0 | sub_qstate); |
3403 | 0 | lock_basic_unlock(&ta->lock); |
3404 | 0 | if(vq->key_entry) { |
3405 | 0 | if(key_entry_isbad(vq->key_entry) |
3406 | 0 | && vq->restart_count < ve->max_restart) { |
3407 | 0 | val_blacklist(&vq->chain_blacklist, qstate->region, |
3408 | 0 | origin, 1); |
3409 | 0 | qstate->errinf = NULL; |
3410 | 0 | vq->restart_count++; |
3411 | 0 | vq->key_entry = NULL; |
3412 | 0 | vq->state = VAL_INIT_STATE; |
3413 | 0 | return; |
3414 | 0 | } |
3415 | 0 | vq->chain_blacklist = NULL; |
3416 | 0 | errinf_origin(qstate, origin); |
3417 | 0 | errinf_dname(qstate, "for trust anchor", ta->name); |
3418 | | /* store the freshly primed entry in the cache */ |
3419 | 0 | key_cache_insert(ve->kcache, vq->key_entry, |
3420 | 0 | qstate->env->cfg->val_log_level >= 2); |
3421 | 0 | } |
3422 | | |
3423 | | /* If the result of the prime is a null key, skip the FINDKEY state.*/ |
3424 | 0 | if(!vq->key_entry || key_entry_isnull(vq->key_entry) || |
3425 | 0 | key_entry_isbad(vq->key_entry)) { |
3426 | 0 | vq->state = VAL_VALIDATE_STATE; |
3427 | 0 | } |
3428 | | /* the qstate will be reactivated after inform_super is done */ |
3429 | 0 | } |
3430 | | |
3431 | | /* |
3432 | | * inform validator super. |
3433 | | * |
3434 | | * @param qstate: query state that finished. |
3435 | | * @param id: module id. |
3436 | | * @param super: the qstate to inform. |
3437 | | */ |
3438 | | void |
3439 | | val_inform_super(struct module_qstate* qstate, int id, |
3440 | | struct module_qstate* super) |
3441 | 0 | { |
3442 | 0 | struct val_qstate* vq = (struct val_qstate*)super->minfo[id]; |
3443 | 0 | log_query_info(VERB_ALGO, "validator: inform_super, sub is", |
3444 | 0 | &qstate->qinfo); |
3445 | 0 | log_query_info(VERB_ALGO, "super is", &super->qinfo); |
3446 | 0 | if(!vq) { |
3447 | 0 | verbose(VERB_ALGO, "super: has no validator state"); |
3448 | 0 | return; |
3449 | 0 | } |
3450 | 0 | if(vq->wait_prime_ta) { |
3451 | 0 | vq->wait_prime_ta = 0; |
3452 | 0 | process_prime_response(super, vq, id, qstate->return_rcode, |
3453 | 0 | qstate->return_msg, qstate->reply_origin, qstate); |
3454 | 0 | return; |
3455 | 0 | } |
3456 | 0 | if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { |
3457 | 0 | int suspend; |
3458 | 0 | process_ds_response(super, vq, id, qstate->return_rcode, |
3459 | 0 | qstate->return_msg, &qstate->qinfo, |
3460 | 0 | qstate->reply_origin, &suspend, qstate); |
3461 | | /* If NSEC3 was needed during validation, NULL the NSEC3 cache; |
3462 | | * it will be re-initiated if needed later on. |
3463 | | * Validation (and the cache table) are happening/allocated in |
3464 | | * the super qstate whilst the RRs are allocated (and pointed |
3465 | | * to) in this sub qstate. */ |
3466 | 0 | if(vq->nsec3_cache_table.ct) { |
3467 | 0 | vq->nsec3_cache_table.ct = NULL; |
3468 | 0 | } |
3469 | 0 | if(suspend) { |
3470 | | /* deep copy the return_msg to vq->sub_ds_msg; it will |
3471 | | * be resumed later in the super state with the caveat |
3472 | | * that the initial calculations will be re-calculated |
3473 | | * and re-suspended there before continuing. */ |
3474 | 0 | vq->sub_ds_msg = dns_msg_deepcopy_region( |
3475 | 0 | qstate->return_msg, super->region); |
3476 | 0 | } |
3477 | 0 | return; |
3478 | 0 | } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { |
3479 | 0 | process_dnskey_response(super, vq, id, qstate->return_rcode, |
3480 | 0 | qstate->return_msg, &qstate->qinfo, |
3481 | 0 | qstate->reply_origin, qstate); |
3482 | 0 | return; |
3483 | 0 | } |
3484 | 0 | log_err("internal error in validator: no inform_supers possible"); |
3485 | 0 | } |
3486 | | |
3487 | | void |
3488 | | val_clear(struct module_qstate* qstate, int id) |
3489 | 0 | { |
3490 | 0 | struct val_qstate* vq; |
3491 | 0 | if(!qstate) |
3492 | 0 | return; |
3493 | 0 | vq = (struct val_qstate*)qstate->minfo[id]; |
3494 | 0 | if(vq) { |
3495 | 0 | if(vq->suspend_timer) { |
3496 | 0 | comm_timer_delete(vq->suspend_timer); |
3497 | 0 | } |
3498 | 0 | } |
3499 | | /* everything is allocated in the region, so assign NULL */ |
3500 | 0 | qstate->minfo[id] = NULL; |
3501 | 0 | } |
3502 | | |
3503 | | size_t |
3504 | | val_get_mem(struct module_env* env, int id) |
3505 | 0 | { |
3506 | 0 | struct val_env* ve = (struct val_env*)env->modinfo[id]; |
3507 | 0 | if(!ve) |
3508 | 0 | return 0; |
3509 | 0 | return sizeof(*ve) + key_cache_get_mem(ve->kcache) + |
3510 | 0 | val_neg_get_mem(ve->neg_cache) + |
3511 | 0 | sizeof(size_t)*2*ve->nsec3_keyiter_count; |
3512 | 0 | } |
3513 | | |
3514 | | /** |
3515 | | * The validator function block |
3516 | | */ |
3517 | | static struct module_func_block val_block = { |
3518 | | "validator", |
3519 | | NULL, NULL, &val_init, &val_deinit, &val_operate, &val_inform_super, |
3520 | | &val_clear, &val_get_mem |
3521 | | }; |
3522 | | |
3523 | | struct module_func_block* |
3524 | | val_get_funcblock(void) |
3525 | 0 | { |
3526 | 0 | return &val_block; |
3527 | 0 | } |
3528 | | |
3529 | | const char* |
3530 | | val_state_to_string(enum val_state state) |
3531 | 0 | { |
3532 | 0 | switch(state) { |
3533 | 0 | case VAL_INIT_STATE: return "VAL_INIT_STATE"; |
3534 | 0 | case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE"; |
3535 | 0 | case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE"; |
3536 | 0 | case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE"; |
3537 | 0 | } |
3538 | 0 | return "UNKNOWN VALIDATOR STATE"; |
3539 | 0 | } |
3540 | | |