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