]>
Commit | Line | Data |
---|---|---|
89c4ed63 A |
1 | /* |
2 | * validator/val_utils.c - validator utility functions. | |
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 helper functions for the validator module. | |
40 | */ | |
41 | #include "config.h" | |
42 | #include "validator/val_utils.h" | |
43 | #include "validator/validator.h" | |
44 | #include "validator/val_kentry.h" | |
45 | #include "validator/val_sigcrypt.h" | |
46 | #include "validator/val_anchor.h" | |
47 | #include "validator/val_nsec.h" | |
48 | #include "validator/val_neg.h" | |
49 | #include "services/cache/rrset.h" | |
50 | #include "services/cache/dns.h" | |
51 | #include "util/data/msgreply.h" | |
52 | #include "util/data/packed_rrset.h" | |
53 | #include "util/data/dname.h" | |
54 | #include "util/net_help.h" | |
55 | #include "util/module.h" | |
56 | #include "util/regional.h" | |
57 | ||
58 | enum val_classification | |
59 | val_classify_response(uint16_t query_flags, struct query_info* origqinf, | |
60 | struct query_info* qinf, struct reply_info* rep, size_t skip) | |
61 | { | |
62 | int rcode = (int)FLAGS_GET_RCODE(rep->flags); | |
63 | size_t i; | |
64 | ||
65 | /* Normal Name Error's are easy to detect -- but don't mistake a CNAME | |
66 | * chain ending in NXDOMAIN. */ | |
67 | if(rcode == LDNS_RCODE_NXDOMAIN && rep->an_numrrsets == 0) | |
68 | return VAL_CLASS_NAMEERROR; | |
69 | ||
70 | /* check for referral: nonRD query and it looks like a nodata */ | |
71 | if(!(query_flags&BIT_RD) && rep->an_numrrsets == 0 && | |
72 | rcode == LDNS_RCODE_NOERROR) { | |
73 | /* SOA record in auth indicates it is NODATA instead. | |
74 | * All validation requiring NODATA messages have SOA in | |
75 | * authority section. */ | |
76 | /* uses fact that answer section is empty */ | |
77 | int saw_ns = 0; | |
78 | for(i=0; i<rep->ns_numrrsets; i++) { | |
79 | if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA) | |
80 | return VAL_CLASS_NODATA; | |
81 | if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DS) | |
82 | return VAL_CLASS_REFERRAL; | |
83 | if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS) | |
84 | saw_ns = 1; | |
85 | } | |
86 | return saw_ns?VAL_CLASS_REFERRAL:VAL_CLASS_NODATA; | |
87 | } | |
88 | /* root referral where NS set is in the answer section */ | |
89 | if(!(query_flags&BIT_RD) && rep->ns_numrrsets == 0 && | |
90 | rep->an_numrrsets == 1 && rcode == LDNS_RCODE_NOERROR && | |
91 | ntohs(rep->rrsets[0]->rk.type) == LDNS_RR_TYPE_NS && | |
92 | query_dname_compare(rep->rrsets[0]->rk.dname, | |
93 | origqinf->qname) != 0) | |
94 | return VAL_CLASS_REFERRAL; | |
95 | ||
96 | /* dump bad messages */ | |
97 | if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) | |
98 | return VAL_CLASS_UNKNOWN; | |
99 | /* next check if the skip into the answer section shows no answer */ | |
100 | if(skip>0 && rep->an_numrrsets <= skip) | |
101 | return VAL_CLASS_CNAMENOANSWER; | |
102 | ||
103 | /* Next is NODATA */ | |
104 | if(rcode == LDNS_RCODE_NOERROR && rep->an_numrrsets == 0) | |
105 | return VAL_CLASS_NODATA; | |
106 | ||
107 | /* We distinguish between CNAME response and other positive/negative | |
108 | * responses because CNAME answers require extra processing. */ | |
109 | ||
110 | /* We distinguish between ANY and CNAME or POSITIVE because | |
111 | * ANY responses are validated differently. */ | |
112 | if(rcode == LDNS_RCODE_NOERROR && qinf->qtype == LDNS_RR_TYPE_ANY) | |
113 | return VAL_CLASS_ANY; | |
114 | ||
115 | /* Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless | |
116 | * qtype=CNAME, this will yield a CNAME response. */ | |
117 | for(i=skip; i<rep->an_numrrsets; i++) { | |
118 | if(rcode == LDNS_RCODE_NOERROR && | |
119 | ntohs(rep->rrsets[i]->rk.type) == qinf->qtype) | |
120 | return VAL_CLASS_POSITIVE; | |
121 | if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME) | |
122 | return VAL_CLASS_CNAME; | |
123 | } | |
124 | log_dns_msg("validator: error. failed to classify response message: ", | |
125 | qinf, rep); | |
126 | return VAL_CLASS_UNKNOWN; | |
127 | } | |
128 | ||
129 | /** Get signer name from RRSIG */ | |
130 | static void | |
131 | rrsig_get_signer(uint8_t* data, size_t len, uint8_t** sname, size_t* slen) | |
132 | { | |
133 | /* RRSIG rdata is not allowed to be compressed, it is stored | |
134 | * uncompressed in memory as well, so return a ptr to the name */ | |
135 | if(len < 21) { | |
136 | /* too short RRSig: | |
137 | * short, byte, byte, long, long, long, short, "." is | |
138 | * 2 1 1 4 4 4 2 1 = 19 | |
139 | * and a skip of 18 bytes to the name. | |
140 | * +2 for the rdatalen is 21 bytes len for root label */ | |
141 | *sname = NULL; | |
142 | *slen = 0; | |
143 | return; | |
144 | } | |
145 | data += 20; /* skip the fixed size bits */ | |
146 | len -= 20; | |
147 | *slen = dname_valid(data, len); | |
148 | if(!*slen) { | |
149 | /* bad dname in this rrsig. */ | |
150 | *sname = NULL; | |
151 | return; | |
152 | } | |
153 | *sname = data; | |
154 | } | |
155 | ||
156 | void | |
157 | val_find_rrset_signer(struct ub_packed_rrset_key* rrset, uint8_t** sname, | |
158 | size_t* slen) | |
159 | { | |
160 | struct packed_rrset_data* d = (struct packed_rrset_data*) | |
161 | rrset->entry.data; | |
162 | /* return signer for first signature, or NULL */ | |
163 | if(d->rrsig_count == 0) { | |
164 | *sname = NULL; | |
165 | *slen = 0; | |
166 | return; | |
167 | } | |
168 | /* get rrsig signer name out of the signature */ | |
169 | rrsig_get_signer(d->rr_data[d->count], d->rr_len[d->count], | |
170 | sname, slen); | |
171 | } | |
172 | ||
173 | /** | |
174 | * Find best signer name in this set of rrsigs. | |
175 | * @param rrset: which rrsigs to look through. | |
176 | * @param qinf: the query name that needs validation. | |
177 | * @param signer_name: the best signer_name. Updated if a better one is found. | |
178 | * @param signer_len: length of signer name. | |
179 | * @param matchcount: count of current best name (starts at 0 for no match). | |
180 | * Updated if match is improved. | |
181 | */ | |
182 | static void | |
183 | val_find_best_signer(struct ub_packed_rrset_key* rrset, | |
184 | struct query_info* qinf, uint8_t** signer_name, size_t* signer_len, | |
185 | int* matchcount) | |
186 | { | |
187 | struct packed_rrset_data* d = (struct packed_rrset_data*) | |
188 | rrset->entry.data; | |
189 | uint8_t* sign; | |
190 | size_t i; | |
191 | int m; | |
192 | for(i=d->count; i<d->count+d->rrsig_count; i++) { | |
193 | sign = d->rr_data[i]+2+18; | |
194 | /* look at signatures that are valid (long enough), | |
195 | * and have a signer name that is a superdomain of qname, | |
196 | * and then check the number of labels in the shared topdomain | |
197 | * improve the match if possible */ | |
198 | if(d->rr_len[i] > 2+19 && /* rdata, sig + root label*/ | |
199 | dname_subdomain_c(qinf->qname, sign)) { | |
200 | (void)dname_lab_cmp(qinf->qname, | |
201 | dname_count_labels(qinf->qname), | |
202 | sign, dname_count_labels(sign), &m); | |
203 | if(m > *matchcount) { | |
204 | *matchcount = m; | |
205 | *signer_name = sign; | |
206 | (void)dname_count_size_labels(*signer_name, | |
207 | signer_len); | |
208 | } | |
209 | } | |
210 | } | |
211 | } | |
212 | ||
213 | void | |
214 | val_find_signer(enum val_classification subtype, struct query_info* qinf, | |
215 | struct reply_info* rep, size_t skip, uint8_t** signer_name, | |
216 | size_t* signer_len) | |
217 | { | |
218 | size_t i; | |
219 | ||
220 | if(subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY) { | |
221 | /* check for the answer rrset */ | |
222 | for(i=skip; i<rep->an_numrrsets; i++) { | |
223 | if(query_dname_compare(qinf->qname, | |
224 | rep->rrsets[i]->rk.dname) == 0) { | |
225 | val_find_rrset_signer(rep->rrsets[i], | |
226 | signer_name, signer_len); | |
227 | return; | |
228 | } | |
229 | } | |
230 | *signer_name = NULL; | |
231 | *signer_len = 0; | |
232 | } else if(subtype == VAL_CLASS_CNAME) { | |
233 | /* check for the first signed cname/dname rrset */ | |
234 | for(i=skip; i<rep->an_numrrsets; i++) { | |
235 | val_find_rrset_signer(rep->rrsets[i], | |
236 | signer_name, signer_len); | |
237 | if(*signer_name) | |
238 | return; | |
239 | if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_DNAME) | |
240 | break; /* only check CNAME after a DNAME */ | |
241 | } | |
242 | *signer_name = NULL; | |
243 | *signer_len = 0; | |
244 | } else if(subtype == VAL_CLASS_NAMEERROR | |
245 | || subtype == VAL_CLASS_NODATA) { | |
246 | /*Check to see if the AUTH section NSEC record(s) have rrsigs*/ | |
247 | for(i=rep->an_numrrsets; i< | |
248 | rep->an_numrrsets+rep->ns_numrrsets; i++) { | |
249 | if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC | |
250 | || ntohs(rep->rrsets[i]->rk.type) == | |
251 | LDNS_RR_TYPE_NSEC3) { | |
252 | val_find_rrset_signer(rep->rrsets[i], | |
253 | signer_name, signer_len); | |
254 | return; | |
255 | } | |
256 | } | |
257 | } else if(subtype == VAL_CLASS_CNAMENOANSWER) { | |
258 | /* find closest superdomain signer name in authority section | |
259 | * NSEC and NSEC3s */ | |
260 | int matchcount = 0; | |
261 | *signer_name = NULL; | |
262 | *signer_len = 0; | |
263 | for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep-> | |
264 | ns_numrrsets; i++) { | |
265 | if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC | |
266 | || ntohs(rep->rrsets[i]->rk.type) == | |
267 | LDNS_RR_TYPE_NSEC3) { | |
268 | val_find_best_signer(rep->rrsets[i], qinf, | |
269 | signer_name, signer_len, &matchcount); | |
270 | } | |
271 | } | |
272 | } else if(subtype == VAL_CLASS_REFERRAL) { | |
273 | /* find keys for the item at skip */ | |
274 | if(skip < rep->rrset_count) { | |
275 | val_find_rrset_signer(rep->rrsets[skip], | |
276 | signer_name, signer_len); | |
277 | return; | |
278 | } | |
279 | *signer_name = NULL; | |
280 | *signer_len = 0; | |
281 | } else { | |
282 | verbose(VERB_QUERY, "find_signer: could not find signer name" | |
283 | " for unknown type response"); | |
284 | *signer_name = NULL; | |
285 | *signer_len = 0; | |
286 | } | |
287 | } | |
288 | ||
289 | /** return number of rrs in an rrset */ | |
290 | static size_t | |
291 | rrset_get_count(struct ub_packed_rrset_key* rrset) | |
292 | { | |
293 | struct packed_rrset_data* d = (struct packed_rrset_data*) | |
294 | rrset->entry.data; | |
295 | if(!d) return 0; | |
296 | return d->count; | |
297 | } | |
298 | ||
299 | /** return TTL of rrset */ | |
300 | static uint32_t | |
301 | rrset_get_ttl(struct ub_packed_rrset_key* rrset) | |
302 | { | |
303 | struct packed_rrset_data* d = (struct packed_rrset_data*) | |
304 | rrset->entry.data; | |
305 | if(!d) return 0; | |
306 | return d->ttl; | |
307 | } | |
308 | ||
309 | enum sec_status | |
310 | val_verify_rrset(struct module_env* env, struct val_env* ve, | |
311 | struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, | |
312 | uint8_t* sigalg, char** reason) | |
313 | { | |
314 | enum sec_status sec; | |
315 | struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> | |
316 | entry.data; | |
317 | if(d->security == sec_status_secure) { | |
318 | /* re-verify all other statuses, because keyset may change*/ | |
319 | log_nametypeclass(VERB_ALGO, "verify rrset cached", | |
320 | rrset->rk.dname, ntohs(rrset->rk.type), | |
321 | ntohs(rrset->rk.rrset_class)); | |
322 | return d->security; | |
323 | } | |
324 | /* check in the cache if verification has already been done */ | |
325 | rrset_check_sec_status(env->rrset_cache, rrset, *env->now); | |
326 | if(d->security == sec_status_secure) { | |
327 | log_nametypeclass(VERB_ALGO, "verify rrset from cache", | |
328 | rrset->rk.dname, ntohs(rrset->rk.type), | |
329 | ntohs(rrset->rk.rrset_class)); | |
330 | return d->security; | |
331 | } | |
332 | log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, | |
333 | ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); | |
334 | sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason); | |
335 | verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); | |
336 | regional_free_all(env->scratch); | |
337 | ||
338 | /* update rrset security status | |
339 | * only improves security status | |
340 | * and bogus is set only once, even if we rechecked the status */ | |
341 | if(sec > d->security) { | |
342 | d->security = sec; | |
343 | if(sec == sec_status_secure) | |
344 | d->trust = rrset_trust_validated; | |
345 | else if(sec == sec_status_bogus) { | |
346 | size_t i; | |
347 | /* update ttl for rrset to fixed value. */ | |
348 | d->ttl = ve->bogus_ttl; | |
349 | for(i=0; i<d->count+d->rrsig_count; i++) | |
350 | d->rr_ttl[i] = ve->bogus_ttl; | |
351 | /* leave RR specific TTL: not used for determine | |
352 | * if RRset timed out and clients see proper value. */ | |
353 | lock_basic_lock(&ve->bogus_lock); | |
354 | ve->num_rrset_bogus++; | |
355 | lock_basic_unlock(&ve->bogus_lock); | |
356 | } | |
357 | /* if status updated - store in cache for reuse */ | |
358 | rrset_update_sec_status(env->rrset_cache, rrset, *env->now); | |
359 | } | |
360 | ||
361 | return sec; | |
362 | } | |
363 | ||
364 | enum sec_status | |
365 | val_verify_rrset_entry(struct module_env* env, struct val_env* ve, | |
366 | struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, | |
367 | char** reason) | |
368 | { | |
369 | /* temporary dnskey rrset-key */ | |
370 | struct ub_packed_rrset_key dnskey; | |
371 | struct key_entry_data* kd = (struct key_entry_data*)kkey->entry.data; | |
372 | enum sec_status sec; | |
373 | dnskey.rk.type = htons(kd->rrset_type); | |
374 | dnskey.rk.rrset_class = htons(kkey->key_class); | |
375 | dnskey.rk.flags = 0; | |
376 | dnskey.rk.dname = kkey->name; | |
377 | dnskey.rk.dname_len = kkey->namelen; | |
378 | dnskey.entry.key = &dnskey; | |
379 | dnskey.entry.data = kd->rrset_data; | |
380 | sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason); | |
381 | return sec; | |
382 | } | |
383 | ||
384 | /** verify that a DS RR hashes to a key and that key signs the set */ | |
385 | static enum sec_status | |
386 | verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, | |
387 | struct ub_packed_rrset_key* dnskey_rrset, | |
388 | struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason) | |
389 | { | |
390 | enum sec_status sec = sec_status_bogus; | |
391 | size_t i, num, numchecked = 0, numhashok = 0; | |
392 | num = rrset_get_count(dnskey_rrset); | |
393 | for(i=0; i<num; i++) { | |
394 | /* Skip DNSKEYs that don't match the basic criteria. */ | |
395 | if(ds_get_key_algo(ds_rrset, ds_idx) | |
396 | != dnskey_get_algo(dnskey_rrset, i) | |
397 | || dnskey_calc_keytag(dnskey_rrset, i) | |
398 | != ds_get_keytag(ds_rrset, ds_idx)) { | |
399 | continue; | |
400 | } | |
401 | numchecked++; | |
402 | verbose(VERB_ALGO, "attempt DS match algo %d keytag %d", | |
403 | ds_get_key_algo(ds_rrset, ds_idx), | |
404 | ds_get_keytag(ds_rrset, ds_idx)); | |
405 | ||
406 | /* Convert the candidate DNSKEY into a hash using the | |
407 | * same DS hash algorithm. */ | |
408 | if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, | |
409 | ds_idx)) { | |
410 | verbose(VERB_ALGO, "DS match attempt failed"); | |
411 | continue; | |
412 | } | |
413 | numhashok++; | |
414 | verbose(VERB_ALGO, "DS match digest ok, trying signature"); | |
415 | ||
416 | /* Otherwise, we have a match! Make sure that the DNSKEY | |
417 | * verifies *with this key* */ | |
418 | sec = dnskey_verify_rrset(env, ve, dnskey_rrset, | |
419 | dnskey_rrset, i, reason); | |
420 | if(sec == sec_status_secure) { | |
421 | return sec; | |
422 | } | |
423 | /* If it didn't validate with the DNSKEY, try the next one! */ | |
424 | } | |
425 | if(numchecked == 0) | |
426 | algo_needs_reason(env, ds_get_key_algo(ds_rrset, ds_idx), | |
427 | reason, "no keys have a DS"); | |
428 | else if(numhashok == 0) | |
429 | *reason = "DS hash mismatches key"; | |
430 | else if(!*reason) | |
431 | *reason = "keyset not secured by DNSKEY that matches DS"; | |
432 | return sec_status_bogus; | |
433 | } | |
434 | ||
435 | int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset) | |
436 | { | |
437 | size_t i, num = rrset_get_count(ds_rrset); | |
438 | int d, digest_algo = 0; /* DS digest algo 0 is not used. */ | |
439 | /* find favorite algo, for now, highest number supported */ | |
440 | for(i=0; i<num; i++) { | |
441 | if(!ds_digest_algo_is_supported(ds_rrset, i) || | |
442 | !ds_key_algo_is_supported(ds_rrset, i)) { | |
443 | continue; | |
444 | } | |
445 | d = ds_get_digest_algo(ds_rrset, i); | |
446 | if(d > digest_algo) | |
447 | digest_algo = d; | |
448 | } | |
449 | return digest_algo; | |
450 | } | |
451 | ||
452 | enum sec_status | |
453 | val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, | |
454 | struct ub_packed_rrset_key* dnskey_rrset, | |
455 | struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason) | |
456 | { | |
457 | /* as long as this is false, we can consider this DS rrset to be | |
458 | * equivalent to no DS rrset. */ | |
459 | int has_useful_ds = 0, digest_algo, alg; | |
460 | struct algo_needs needs; | |
461 | size_t i, num; | |
462 | enum sec_status sec; | |
463 | ||
464 | if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len || | |
465 | query_dname_compare(dnskey_rrset->rk.dname, ds_rrset->rk.dname) | |
466 | != 0) { | |
467 | verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset " | |
468 | "by name"); | |
469 | *reason = "DNSKEY RRset did not match DS RRset by name"; | |
470 | return sec_status_bogus; | |
471 | } | |
472 | ||
473 | digest_algo = val_favorite_ds_algo(ds_rrset); | |
474 | if(sigalg) | |
475 | algo_needs_init_ds(&needs, ds_rrset, digest_algo, sigalg); | |
476 | num = rrset_get_count(ds_rrset); | |
477 | for(i=0; i<num; i++) { | |
478 | /* Check to see if we can understand this DS. | |
479 | * And check it is the strongest digest */ | |
480 | if(!ds_digest_algo_is_supported(ds_rrset, i) || | |
481 | !ds_key_algo_is_supported(ds_rrset, i) || | |
482 | ds_get_digest_algo(ds_rrset, i) != digest_algo) { | |
483 | continue; | |
484 | } | |
485 | ||
486 | /* Once we see a single DS with a known digestID and | |
487 | * algorithm, we cannot return INSECURE (with a | |
488 | * "null" KeyEntry). */ | |
489 | has_useful_ds = 1; | |
490 | ||
491 | sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, | |
492 | ds_rrset, i, reason); | |
493 | if(sec == sec_status_secure) { | |
494 | if(!sigalg || algo_needs_set_secure(&needs, | |
495 | (uint8_t)ds_get_key_algo(ds_rrset, i))) { | |
496 | verbose(VERB_ALGO, "DS matched DNSKEY."); | |
497 | return sec_status_secure; | |
498 | } | |
499 | } else if(sigalg && sec == sec_status_bogus) { | |
500 | algo_needs_set_bogus(&needs, | |
501 | (uint8_t)ds_get_key_algo(ds_rrset, i)); | |
502 | } | |
503 | } | |
504 | ||
505 | /* None of the DS's worked out. */ | |
506 | ||
507 | /* If no DSs were understandable, then this is OK. */ | |
508 | if(!has_useful_ds) { | |
509 | verbose(VERB_ALGO, "No usable DS records were found -- " | |
510 | "treating as insecure."); | |
511 | return sec_status_insecure; | |
512 | } | |
513 | /* If any were understandable, then it is bad. */ | |
514 | verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY."); | |
515 | if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { | |
516 | algo_needs_reason(env, alg, reason, "missing verification of " | |
517 | "DNSKEY signature"); | |
518 | } | |
519 | return sec_status_bogus; | |
520 | } | |
521 | ||
522 | struct key_entry_key* | |
523 | val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, | |
524 | struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, | |
525 | struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason) | |
526 | { | |
527 | uint8_t sigalg[ALGO_NEEDS_MAX+1]; | |
528 | enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, | |
529 | dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason); | |
530 | ||
531 | if(sec == sec_status_secure) { | |
532 | return key_entry_create_rrset(region, | |
533 | ds_rrset->rk.dname, ds_rrset->rk.dname_len, | |
534 | ntohs(ds_rrset->rk.rrset_class), dnskey_rrset, | |
535 | downprot?sigalg:NULL, *env->now); | |
536 | } else if(sec == sec_status_insecure) { | |
537 | return key_entry_create_null(region, ds_rrset->rk.dname, | |
538 | ds_rrset->rk.dname_len, | |
539 | ntohs(ds_rrset->rk.rrset_class), | |
540 | rrset_get_ttl(ds_rrset), *env->now); | |
541 | } | |
542 | return key_entry_create_bad(region, ds_rrset->rk.dname, | |
543 | ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class), | |
544 | BOGUS_KEY_TTL, *env->now); | |
545 | } | |
546 | ||
547 | enum sec_status | |
548 | val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, | |
549 | struct ub_packed_rrset_key* dnskey_rrset, | |
550 | struct ub_packed_rrset_key* ta_ds, | |
551 | struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason) | |
552 | { | |
553 | /* as long as this is false, we can consider this anchor to be | |
554 | * equivalent to no anchor. */ | |
555 | int has_useful_ta = 0, digest_algo = 0, alg; | |
556 | struct algo_needs needs; | |
557 | size_t i, num; | |
558 | enum sec_status sec; | |
559 | ||
560 | if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len || | |
561 | query_dname_compare(dnskey_rrset->rk.dname, ta_ds->rk.dname) | |
562 | != 0)) { | |
563 | verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset " | |
564 | "by name"); | |
565 | *reason = "DNSKEY RRset did not match DS RRset by name"; | |
566 | return sec_status_bogus; | |
567 | } | |
568 | if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len | |
569 | || query_dname_compare(dnskey_rrset->rk.dname, ta_dnskey->rk.dname) | |
570 | != 0)) { | |
571 | verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset " | |
572 | "by name"); | |
573 | *reason = "DNSKEY RRset did not match anchor RRset by name"; | |
574 | return sec_status_bogus; | |
575 | } | |
576 | ||
577 | if(ta_ds) | |
578 | digest_algo = val_favorite_ds_algo(ta_ds); | |
579 | if(sigalg) { | |
580 | if(ta_ds) | |
581 | algo_needs_init_ds(&needs, ta_ds, digest_algo, sigalg); | |
582 | else memset(&needs, 0, sizeof(needs)); | |
583 | if(ta_dnskey) | |
584 | algo_needs_init_dnskey_add(&needs, ta_dnskey, sigalg); | |
585 | } | |
586 | if(ta_ds) { | |
587 | num = rrset_get_count(ta_ds); | |
588 | for(i=0; i<num; i++) { | |
589 | /* Check to see if we can understand this DS. | |
590 | * And check it is the strongest digest */ | |
591 | if(!ds_digest_algo_is_supported(ta_ds, i) || | |
592 | !ds_key_algo_is_supported(ta_ds, i) || | |
593 | ds_get_digest_algo(ta_ds, i) != digest_algo) | |
594 | continue; | |
595 | ||
596 | /* Once we see a single DS with a known digestID and | |
597 | * algorithm, we cannot return INSECURE (with a | |
598 | * "null" KeyEntry). */ | |
599 | has_useful_ta = 1; | |
600 | ||
601 | sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, | |
602 | ta_ds, i, reason); | |
603 | if(sec == sec_status_secure) { | |
604 | if(!sigalg || algo_needs_set_secure(&needs, | |
605 | (uint8_t)ds_get_key_algo(ta_ds, i))) { | |
606 | verbose(VERB_ALGO, "DS matched DNSKEY."); | |
607 | return sec_status_secure; | |
608 | } | |
609 | } else if(sigalg && sec == sec_status_bogus) { | |
610 | algo_needs_set_bogus(&needs, | |
611 | (uint8_t)ds_get_key_algo(ta_ds, i)); | |
612 | } | |
613 | } | |
614 | } | |
615 | ||
616 | /* None of the DS's worked out: check the DNSKEYs. */ | |
617 | if(ta_dnskey) { | |
618 | num = rrset_get_count(ta_dnskey); | |
619 | for(i=0; i<num; i++) { | |
620 | /* Check to see if we can understand this DNSKEY */ | |
621 | if(!dnskey_algo_is_supported(ta_dnskey, i)) | |
622 | continue; | |
623 | ||
624 | /* we saw a useful TA */ | |
625 | has_useful_ta = 1; | |
626 | ||
627 | sec = dnskey_verify_rrset(env, ve, dnskey_rrset, | |
628 | ta_dnskey, i, reason); | |
629 | if(sec == sec_status_secure) { | |
630 | if(!sigalg || algo_needs_set_secure(&needs, | |
631 | (uint8_t)dnskey_get_algo(ta_dnskey, i))) { | |
632 | verbose(VERB_ALGO, "anchor matched DNSKEY."); | |
633 | return sec_status_secure; | |
634 | } | |
635 | } else if(sigalg && sec == sec_status_bogus) { | |
636 | algo_needs_set_bogus(&needs, | |
637 | (uint8_t)dnskey_get_algo(ta_dnskey, i)); | |
638 | } | |
639 | } | |
640 | } | |
641 | ||
642 | /* If no DSs were understandable, then this is OK. */ | |
643 | if(!has_useful_ta) { | |
644 | verbose(VERB_ALGO, "No usable trust anchors were found -- " | |
645 | "treating as insecure."); | |
646 | return sec_status_insecure; | |
647 | } | |
648 | /* If any were understandable, then it is bad. */ | |
649 | verbose(VERB_QUERY, "Failed to match any usable anchor to a DNSKEY."); | |
650 | if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { | |
651 | algo_needs_reason(env, alg, reason, "missing verification of " | |
652 | "DNSKEY signature"); | |
653 | } | |
654 | return sec_status_bogus; | |
655 | } | |
656 | ||
657 | struct key_entry_key* | |
658 | val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, | |
659 | struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, | |
660 | struct ub_packed_rrset_key* ta_ds_rrset, | |
661 | struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot, | |
662 | char** reason) | |
663 | { | |
664 | uint8_t sigalg[ALGO_NEEDS_MAX+1]; | |
665 | enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, | |
666 | dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset, | |
667 | downprot?sigalg:NULL, reason); | |
668 | ||
669 | if(sec == sec_status_secure) { | |
670 | return key_entry_create_rrset(region, | |
671 | dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len, | |
672 | ntohs(dnskey_rrset->rk.rrset_class), dnskey_rrset, | |
673 | downprot?sigalg:NULL, *env->now); | |
674 | } else if(sec == sec_status_insecure) { | |
675 | return key_entry_create_null(region, dnskey_rrset->rk.dname, | |
676 | dnskey_rrset->rk.dname_len, | |
677 | ntohs(dnskey_rrset->rk.rrset_class), | |
678 | rrset_get_ttl(dnskey_rrset), *env->now); | |
679 | } | |
680 | return key_entry_create_bad(region, dnskey_rrset->rk.dname, | |
681 | dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class), | |
682 | BOGUS_KEY_TTL, *env->now); | |
683 | } | |
684 | ||
685 | int | |
686 | val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset) | |
687 | { | |
688 | size_t i; | |
689 | for(i=0; i<rrset_get_count(ds_rrset); i++) { | |
690 | if(ds_digest_algo_is_supported(ds_rrset, i) && | |
691 | ds_key_algo_is_supported(ds_rrset, i)) | |
692 | return 1; | |
693 | } | |
694 | return 0; | |
695 | } | |
696 | ||
697 | /** get label count for a signature */ | |
698 | static uint8_t | |
699 | rrsig_get_labcount(struct packed_rrset_data* d, size_t sig) | |
700 | { | |
701 | if(d->rr_len[sig] < 2+4) | |
702 | return 0; /* bad sig length */ | |
703 | return d->rr_data[sig][2+3]; | |
704 | } | |
705 | ||
706 | int | |
707 | val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc) | |
708 | { | |
709 | struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> | |
710 | entry.data; | |
711 | uint8_t labcount; | |
712 | int labdiff; | |
713 | uint8_t* wn; | |
714 | size_t i, wl; | |
715 | if(d->rrsig_count == 0) { | |
716 | return 1; | |
717 | } | |
718 | labcount = rrsig_get_labcount(d, d->count + 0); | |
719 | /* check rest of signatures identical */ | |
720 | for(i=1; i<d->rrsig_count; i++) { | |
721 | if(labcount != rrsig_get_labcount(d, d->count + i)) { | |
722 | return 0; | |
723 | } | |
724 | } | |
725 | /* OK the rrsigs check out */ | |
726 | /* if the RRSIG label count is shorter than the number of actual | |
727 | * labels, then this rrset was synthesized from a wildcard. | |
728 | * Note that the RRSIG label count doesn't count the root label. */ | |
729 | wn = rrset->rk.dname; | |
730 | wl = rrset->rk.dname_len; | |
731 | /* skip a leading wildcard label in the dname (RFC4035 2.2) */ | |
732 | if(dname_is_wild(wn)) { | |
733 | wn += 2; | |
734 | wl -= 2; | |
735 | } | |
736 | labdiff = (dname_count_labels(wn) - 1) - (int)labcount; | |
737 | if(labdiff > 0) { | |
738 | *wc = wn; | |
739 | dname_remove_labels(wc, &wl, labdiff); | |
740 | return 1; | |
741 | } | |
742 | return 1; | |
743 | } | |
744 | ||
745 | int | |
746 | val_chase_cname(struct query_info* qchase, struct reply_info* rep, | |
747 | size_t* cname_skip) { | |
748 | size_t i; | |
749 | /* skip any DNAMEs, go to the CNAME for next part */ | |
750 | for(i = *cname_skip; i < rep->an_numrrsets; i++) { | |
751 | if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_CNAME && | |
752 | query_dname_compare(qchase->qname, rep->rrsets[i]-> | |
753 | rk.dname) == 0) { | |
754 | qchase->qname = NULL; | |
755 | get_cname_target(rep->rrsets[i], &qchase->qname, | |
756 | &qchase->qname_len); | |
757 | if(!qchase->qname) | |
758 | return 0; /* bad CNAME rdata */ | |
759 | (*cname_skip) = i+1; | |
760 | return 1; | |
761 | } | |
762 | } | |
763 | return 0; /* CNAME classified but no matching CNAME ?! */ | |
764 | } | |
765 | ||
766 | /** see if rrset has signer name as one of the rrsig signers */ | |
767 | static int | |
768 | rrset_has_signer(struct ub_packed_rrset_key* rrset, uint8_t* name, size_t len) | |
769 | { | |
770 | struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> | |
771 | entry.data; | |
772 | size_t i; | |
773 | for(i = d->count; i< d->count+d->rrsig_count; i++) { | |
774 | if(d->rr_len[i] > 2+18+len) { | |
775 | /* at least rdatalen + signature + signame (+1 sig)*/ | |
776 | if(!dname_valid(d->rr_data[i]+2+18, d->rr_len[i]-2-18)) | |
777 | continue; | |
778 | if(query_dname_compare(name, d->rr_data[i]+2+18) == 0) | |
779 | { | |
780 | return 1; | |
781 | } | |
782 | } | |
783 | } | |
784 | return 0; | |
785 | } | |
786 | ||
787 | void | |
788 | val_fill_reply(struct reply_info* chase, struct reply_info* orig, | |
789 | size_t skip, uint8_t* name, size_t len, uint8_t* signer) | |
790 | { | |
791 | size_t i; | |
792 | int seen_dname = 0; | |
793 | chase->rrset_count = 0; | |
794 | chase->an_numrrsets = 0; | |
795 | chase->ns_numrrsets = 0; | |
796 | chase->ar_numrrsets = 0; | |
797 | /* ANSWER section */ | |
798 | for(i=skip; i<orig->an_numrrsets; i++) { | |
799 | if(!signer) { | |
800 | if(query_dname_compare(name, | |
801 | orig->rrsets[i]->rk.dname) == 0) | |
802 | chase->rrsets[chase->an_numrrsets++] = | |
803 | orig->rrsets[i]; | |
804 | } else if(seen_dname && ntohs(orig->rrsets[i]->rk.type) == | |
805 | LDNS_RR_TYPE_CNAME) { | |
806 | chase->rrsets[chase->an_numrrsets++] = orig->rrsets[i]; | |
807 | seen_dname = 0; | |
808 | } else if(rrset_has_signer(orig->rrsets[i], name, len)) { | |
809 | chase->rrsets[chase->an_numrrsets++] = orig->rrsets[i]; | |
810 | if(ntohs(orig->rrsets[i]->rk.type) == | |
811 | LDNS_RR_TYPE_DNAME) { | |
812 | seen_dname = 1; | |
813 | } | |
814 | } | |
815 | } | |
816 | /* AUTHORITY section */ | |
817 | for(i = (skip > orig->an_numrrsets)?skip:orig->an_numrrsets; | |
818 | i<orig->an_numrrsets+orig->ns_numrrsets; | |
819 | i++) { | |
820 | if(!signer) { | |
821 | if(query_dname_compare(name, | |
822 | orig->rrsets[i]->rk.dname) == 0) | |
823 | chase->rrsets[chase->an_numrrsets+ | |
824 | chase->ns_numrrsets++] = orig->rrsets[i]; | |
825 | } else if(rrset_has_signer(orig->rrsets[i], name, len)) { | |
826 | chase->rrsets[chase->an_numrrsets+ | |
827 | chase->ns_numrrsets++] = orig->rrsets[i]; | |
828 | } | |
829 | } | |
830 | /* ADDITIONAL section */ | |
831 | for(i= (skip>orig->an_numrrsets+orig->ns_numrrsets)? | |
832 | skip:orig->an_numrrsets+orig->ns_numrrsets; | |
833 | i<orig->rrset_count; i++) { | |
834 | if(!signer) { | |
835 | if(query_dname_compare(name, | |
836 | orig->rrsets[i]->rk.dname) == 0) | |
837 | chase->rrsets[chase->an_numrrsets | |
838 | +orig->ns_numrrsets+chase->ar_numrrsets++] | |
839 | = orig->rrsets[i]; | |
840 | } else if(rrset_has_signer(orig->rrsets[i], name, len)) { | |
841 | chase->rrsets[chase->an_numrrsets+orig->ns_numrrsets+ | |
842 | chase->ar_numrrsets++] = orig->rrsets[i]; | |
843 | } | |
844 | } | |
845 | chase->rrset_count = chase->an_numrrsets + chase->ns_numrrsets + | |
846 | chase->ar_numrrsets; | |
847 | } | |
848 | ||
849 | void | |
850 | val_check_nonsecure(struct val_env* ve, struct reply_info* rep) | |
851 | { | |
852 | size_t i; | |
853 | /* authority */ | |
854 | for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { | |
855 | if(((struct packed_rrset_data*)rep->rrsets[i]->entry.data) | |
856 | ->security != sec_status_secure) { | |
857 | /* because we want to return the authentic original | |
858 | * message when presented with CD-flagged queries, | |
859 | * we need to preserve AUTHORITY section data. | |
860 | * However, this rrset is not signed or signed | |
861 | * with the wrong keys. Validation has tried to | |
862 | * verify this rrset with the keysets of import. | |
863 | * But this rrset did not verify. | |
864 | * Therefore the message is bogus. | |
865 | */ | |
866 | ||
867 | /* check if authority consists of only an NS record | |
868 | * which is bad, and there is an answer section with | |
869 | * data. In that case, delete NS and additional to | |
870 | * be lenient and make a minimal response */ | |
871 | if(rep->an_numrrsets != 0 && rep->ns_numrrsets == 1 && | |
872 | ntohs(rep->rrsets[i]->rk.type) | |
873 | == LDNS_RR_TYPE_NS) { | |
874 | verbose(VERB_ALGO, "truncate to minimal"); | |
875 | rep->ns_numrrsets = 0; | |
876 | rep->ar_numrrsets = 0; | |
877 | rep->rrset_count = rep->an_numrrsets; | |
878 | return; | |
879 | } | |
880 | ||
881 | log_nametypeclass(VERB_QUERY, "message is bogus, " | |
882 | "non secure rrset", | |
883 | rep->rrsets[i]->rk.dname, | |
884 | ntohs(rep->rrsets[i]->rk.type), | |
885 | ntohs(rep->rrsets[i]->rk.rrset_class)); | |
886 | rep->security = sec_status_bogus; | |
887 | return; | |
888 | } | |
889 | } | |
890 | /* additional */ | |
891 | if(!ve->clean_additional) | |
892 | return; | |
893 | for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) { | |
894 | if(((struct packed_rrset_data*)rep->rrsets[i]->entry.data) | |
895 | ->security != sec_status_secure) { | |
896 | /* This does not cause message invalidation. It was | |
897 | * simply unsigned data in the additional. The | |
898 | * RRSIG must have been truncated off the message. | |
899 | * | |
900 | * However, we do not want to return possible bogus | |
901 | * data to clients that rely on this service for | |
902 | * their authentication. | |
903 | */ | |
904 | /* remove this unneeded additional rrset */ | |
905 | memmove(rep->rrsets+i, rep->rrsets+i+1, | |
906 | sizeof(struct ub_packed_rrset_key*)* | |
907 | (rep->rrset_count - i - 1)); | |
908 | rep->ar_numrrsets--; | |
909 | rep->rrset_count--; | |
910 | i--; | |
911 | } | |
912 | } | |
913 | } | |
914 | ||
915 | /** check no anchor and unlock */ | |
916 | static int | |
917 | check_no_anchor(struct val_anchors* anchors, uint8_t* nm, size_t l, uint16_t c) | |
918 | { | |
919 | struct trust_anchor* ta; | |
920 | if((ta=anchors_lookup(anchors, nm, l, c))) { | |
921 | lock_basic_unlock(&ta->lock); | |
922 | } | |
923 | return !ta; | |
924 | } | |
925 | ||
926 | void | |
927 | val_mark_indeterminate(struct reply_info* rep, struct val_anchors* anchors, | |
928 | struct rrset_cache* r, struct module_env* env) | |
929 | { | |
930 | size_t i; | |
931 | struct packed_rrset_data* d; | |
932 | for(i=0; i<rep->rrset_count; i++) { | |
933 | d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; | |
934 | if(d->security == sec_status_unchecked && | |
935 | check_no_anchor(anchors, rep->rrsets[i]->rk.dname, | |
936 | rep->rrsets[i]->rk.dname_len, | |
937 | ntohs(rep->rrsets[i]->rk.rrset_class))) | |
938 | { | |
939 | /* mark as indeterminate */ | |
940 | d->security = sec_status_indeterminate; | |
941 | rrset_update_sec_status(r, rep->rrsets[i], *env->now); | |
942 | } | |
943 | } | |
944 | } | |
945 | ||
946 | void | |
947 | val_mark_insecure(struct reply_info* rep, uint8_t* kname, | |
948 | struct rrset_cache* r, struct module_env* env) | |
949 | { | |
950 | size_t i; | |
951 | struct packed_rrset_data* d; | |
952 | for(i=0; i<rep->rrset_count; i++) { | |
953 | d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; | |
954 | if(d->security == sec_status_unchecked && | |
955 | dname_subdomain_c(rep->rrsets[i]->rk.dname, kname)) { | |
956 | /* mark as insecure */ | |
957 | d->security = sec_status_insecure; | |
958 | rrset_update_sec_status(r, rep->rrsets[i], *env->now); | |
959 | } | |
960 | } | |
961 | } | |
962 | ||
963 | size_t | |
964 | val_next_unchecked(struct reply_info* rep, size_t skip) | |
965 | { | |
966 | size_t i; | |
967 | struct packed_rrset_data* d; | |
968 | for(i=skip+1; i<rep->rrset_count; i++) { | |
969 | d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; | |
970 | if(d->security == sec_status_unchecked) { | |
971 | return i; | |
972 | } | |
973 | } | |
974 | return rep->rrset_count; | |
975 | } | |
976 | ||
977 | const char* | |
978 | val_classification_to_string(enum val_classification subtype) | |
979 | { | |
980 | switch(subtype) { | |
981 | case VAL_CLASS_UNTYPED: return "untyped"; | |
982 | case VAL_CLASS_UNKNOWN: return "unknown"; | |
983 | case VAL_CLASS_POSITIVE: return "positive"; | |
984 | case VAL_CLASS_CNAME: return "cname"; | |
985 | case VAL_CLASS_NODATA: return "nodata"; | |
986 | case VAL_CLASS_NAMEERROR: return "nameerror"; | |
987 | case VAL_CLASS_CNAMENOANSWER: return "cnamenoanswer"; | |
988 | case VAL_CLASS_REFERRAL: return "referral"; | |
989 | case VAL_CLASS_ANY: return "qtype_any"; | |
990 | default: | |
991 | return "bad_val_classification"; | |
992 | } | |
993 | } | |
994 | ||
995 | /** log a sock_list entry */ | |
996 | static void | |
997 | sock_list_logentry(enum verbosity_value v, const char* s, struct sock_list* p) | |
998 | { | |
999 | if(p->len) | |
1000 | log_addr(v, s, &p->addr, p->len); | |
1001 | else verbose(v, "%s cache", s); | |
1002 | } | |
1003 | ||
1004 | void val_blacklist(struct sock_list** blacklist, struct regional* region, | |
1005 | struct sock_list* origin, int cross) | |
1006 | { | |
1007 | /* debug printout */ | |
1008 | if(verbosity >= VERB_ALGO) { | |
1009 | struct sock_list* p; | |
1010 | for(p=*blacklist; p; p=p->next) | |
1011 | sock_list_logentry(VERB_ALGO, "blacklist", p); | |
1012 | if(!origin) | |
1013 | verbose(VERB_ALGO, "blacklist add: cache"); | |
1014 | for(p=origin; p; p=p->next) | |
1015 | sock_list_logentry(VERB_ALGO, "blacklist add", p); | |
1016 | } | |
1017 | /* blacklist the IPs or the cache */ | |
1018 | if(!origin) { | |
1019 | /* only add if nothing there. anything else also stops cache*/ | |
1020 | if(!*blacklist) | |
1021 | sock_list_insert(blacklist, NULL, 0, region); | |
1022 | } else if(!cross) | |
1023 | sock_list_prepend(blacklist, origin); | |
1024 | else sock_list_merge(blacklist, region, origin); | |
1025 | } | |
1026 | ||
1027 | int val_has_signed_nsecs(struct reply_info* rep, char** reason) | |
1028 | { | |
1029 | size_t i, num_nsec = 0, num_nsec3 = 0; | |
1030 | struct packed_rrset_data* d; | |
1031 | for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { | |
1032 | if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC)) | |
1033 | num_nsec++; | |
1034 | else if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC3)) | |
1035 | num_nsec3++; | |
1036 | else continue; | |
1037 | d = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; | |
1038 | if(d && d->rrsig_count != 0) { | |
1039 | return 1; | |
1040 | } | |
1041 | } | |
1042 | if(num_nsec == 0 && num_nsec3 == 0) | |
1043 | *reason = "no DNSSEC records"; | |
1044 | else if(num_nsec != 0) | |
1045 | *reason = "no signatures over NSECs"; | |
1046 | else *reason = "no signatures over NSEC3s"; | |
1047 | return 0; | |
1048 | } | |
1049 | ||
1050 | struct dns_msg* | |
1051 | val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c, | |
1052 | struct regional* region, uint8_t* topname) | |
1053 | { | |
1054 | struct dns_msg* msg; | |
1055 | struct query_info qinfo; | |
1056 | struct ub_packed_rrset_key *rrset = rrset_cache_lookup( | |
1057 | env->rrset_cache, nm, nmlen, LDNS_RR_TYPE_DS, c, 0, | |
1058 | *env->now, 0); | |
1059 | if(rrset) { | |
1060 | /* DS rrset exists. Return it to the validator immediately*/ | |
1061 | struct ub_packed_rrset_key* copy = packed_rrset_copy_region( | |
1062 | rrset, region, *env->now); | |
1063 | lock_rw_unlock(&rrset->entry.lock); | |
1064 | if(!copy) | |
1065 | return NULL; | |
1066 | msg = dns_msg_create(nm, nmlen, LDNS_RR_TYPE_DS, c, region, 1); | |
1067 | if(!msg) | |
1068 | return NULL; | |
1069 | msg->rep->rrsets[0] = copy; | |
1070 | msg->rep->rrset_count++; | |
1071 | msg->rep->an_numrrsets++; | |
1072 | return msg; | |
1073 | } | |
1074 | /* lookup in rrset and negative cache for NSEC/NSEC3 */ | |
1075 | qinfo.qname = nm; | |
1076 | qinfo.qname_len = nmlen; | |
1077 | qinfo.qtype = LDNS_RR_TYPE_DS; | |
1078 | qinfo.qclass = c; | |
1079 | /* do not add SOA to reply message, it is going to be used internal */ | |
1080 | msg = val_neg_getmsg(env->neg_cache, &qinfo, region, env->rrset_cache, | |
1081 | env->scratch_buffer, *env->now, 0, topname); | |
1082 | return msg; | |
1083 | } |