]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/nsec.c
mDNSResponder-544.tar.gz
[apple/mdnsresponder.git] / mDNSCore / nsec.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 // ***************************************************************************
19 // nsec.c: This file contains support functions to validate NSEC records for
20 // NODATA and NXDOMAIN error.
21 // ***************************************************************************
22
23 #include "mDNSEmbeddedAPI.h"
24 #include "DNSCommon.h"
25 #include "nsec.h"
26 #include "nsec3.h"
27
28 // Define DNSSEC_DISABLED to remove all the DNSSEC functionality
29 // and use the stub functions implemented later in this file.
30
31 #ifndef DNSSEC_DISABLED
32
33 // Implementation Notes
34 //
35 // NSEC records in DNSSEC are used for authenticated denial of existence i.e., if the response to a query
36 // results in NXDOMAIN or NODATA error, the response also contains NSEC records in the additional section
37 // to prove the non-existence of the original name. In most of the cases, NSEC records don't have any
38 // relationship to the original name queried i.e, if they are cached based on the name like other records,
39 // it can't be located to prove the non-existence of the original name. Hence, we create a negative cache
40 // record like we do for the NXDOMAIN/NODATA error and then cache the NSEC records as part of that. Sometimes,
41 // NSEC records are also used for wildcard expanded answer in which case they are cached with the cache record
42 // that is created for the original name. NSEC records are freed when the parent cache (the record that they
43 // are attached to is expired).
44 //
45 // NSEC records also can be queried like any other record and hence can exist independent of the negative
46 // cache record. It exists as part of negative cache record only when we get a NXDOMAIN/NODATA error with
47 // NSEC records. When a query results in NXDOMAIN/NODATA error and needs to be validated, the NSEC
48 // records (and its RRSIGS) are cached as part of the negative cache record. The NSEC records that
49 // exist separately from the negative cache record should not be used to answer ValidationRequired/
50 // ValidatingResponse questions as it may not be sufficient to prove the non-existence of the name.
51 // The exception is when the NSEC record is looked up explicitly. See DNSSECRecordAnswersQuestion
52 // for more details.
53 //
54
55 mDNSlocal CacheRecord *NSECParentForQuestion(mDNS *const m, DNSQuestion *q)
56 {
57 CacheGroup *cg;
58 CacheRecord *cr;
59 mDNSu32 slot;
60 mDNSu32 namehash;
61
62 slot = HashSlot(&q->qname);
63 namehash = DomainNameHashValue(&q->qname);
64 cg = CacheGroupForName(m, slot, namehash, &q->qname);
65 if (!cg)
66 {
67 LogDNSSEC("NSECParentForQuestion: Cannot find cg for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
68 return mDNSNULL;
69 }
70 for (cr = cg->members; cr; cr = cr->next)
71 if (SameNameRecordAnswersQuestion(&cr->resrec, q))
72 return cr;
73 return mDNSNULL;
74 }
75
76 mDNSlocal void UpdateParent(DNSSECVerifier *dv)
77 {
78 AuthChainLink(dv->parent, dv->ac);
79 ResetAuthChain(dv);
80 dv->parent->NumPackets += dv->NumPackets;
81 }
82
83 // Note: This should just call the parent callback which will free the DNSSECVerifier.
84 mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
85 {
86 if (!dv->parent)
87 {
88 LogMsg("VerifyNSECCCallback: ERROR!! no parent DV\n");
89 FreeDNSSECVerifier(m, dv);
90 return;
91 }
92 if (dv->ac)
93 {
94 // Before we free the "dv", we need to update the
95 // parent with our AuthChain information
96 UpdateParent(dv);
97 }
98 // "status" indicates whether we are able to successfully verify
99 // the NSEC/NSEC3 signatures. For NSEC3, the OptOut flag may be set
100 // for which we need to deliver insecure result.
101 if ((dv->parent->flags & NSEC3_OPT_OUT) && (status == DNSSEC_Secure))
102 {
103 dv->parent->DVCallback(m, dv->parent, DNSSEC_Insecure);
104 }
105 else
106 {
107 dv->parent->DVCallback(m, dv->parent, status);
108 }
109 // The callback we called in the previous line should recursively
110 // free all the DNSSECVerifiers starting from dv->parent and above.
111 // So, set that to NULL and free the "dv" itself here.
112 dv->parent = mDNSNULL;
113 FreeDNSSECVerifier(m, dv);
114 }
115
116 // If the caller provides a callback, it takes the responsibility of calling the original callback
117 // in "pdv" when it is done.
118 //
119 // INPUT:
120 //
121 // rr: The NSEC record that should be verified
122 // rv: The NSEC record can also be provided like this
123 // pdv: Parent DNSSECVerifier which will be called when the verification is done.
124 // callback: As part of the proof, we need multiple NSEC verifications before we call the "pdv" callback in
125 // which case a intermediate "callback" is provided which can be used to do multiple verifications.
126 // ncr: The cache record where the RRSIGS are cached
127 //
128 // NSEC records and signatures are cached along with the cache record so that we can expire them all together. We can't cache
129 // them based on the name hash like other records as in most cases the returned NSECs has a different name than we asked for
130 // (except for NODATA error where the name exists but type does not exist).
131 //
132 mDNSexport void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, DNSSECVerifierCallback callback)
133 {
134 DNSSECVerifier *dv = mDNSNULL;
135 CacheRecord **rp;
136 const domainname *name;
137 mDNSu16 rrtype;
138
139 if (!rv && !rr)
140 {
141 LogDNSSEC("VerifyNSEC: Both rr and rv are NULL");
142 goto error;
143 }
144 if (!pdv)
145 {
146 LogDNSSEC("VerifyNSEC: ERROR!! pdv is NULL");
147 return;
148 }
149 // Remember the name and type for which we are verifying, so that when we are done processing all
150 // the verifications, we can trace it back.
151 //
152 // Note: Currently it is not used because when the verification completes as we just
153 // call the "pdv" callback which has its origName and origType.
154 if (rr)
155 {
156 name = rr->name;
157 rrtype = rr->rrtype;
158 }
159 else
160 {
161 name = &rv->name;
162 rrtype = rv->rrtype;
163 }
164
165 dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, DNSSEC_VALIDATION_SECURE,
166 (callback ? callback : VerifyNSECCallback), mDNSNULL);
167 if (!dv)
168 {
169 LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed");
170 return;
171 }
172
173 dv->parent = pdv;
174
175 if (AddRRSetToVerifier(dv, rr, rv, RRVS_rr) != mStatus_NoError)
176 {
177 LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier failed to add NSEC");
178 goto error;
179 }
180
181 // Add the signatures after validating them
182 rp = &(ncr->nsec);
183 while (*rp)
184 {
185 if ((*rp)->resrec.rrtype == kDNSType_RRSIG)
186 {
187 ValidateRRSIG(dv, RRVS_rrsig, &(*rp)->resrec);
188 }
189 rp=&(*rp)->next;
190 }
191
192 if (!dv->rrset)
193 {
194 LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset");
195 goto error;
196 }
197 // Expired signatures.
198 if (!dv->rrsig)
199 goto error;
200
201 // Next step is to fetch the keys
202 dv->next = RRVS_key;
203
204 StartDNSSECVerification(m, dv);
205 return;
206 error:
207 pdv->DVCallback(m, pdv, DNSSEC_Bogus);
208 if (dv)
209 {
210 dv->parent = mDNSNULL;
211 FreeDNSSECVerifier(m, dv);
212 }
213 return;
214 }
215
216 mDNSlocal void DeleteCachedNSECS(mDNS *const m, CacheRecord *cr)
217 {
218 CacheRecord *rp, *next;
219
220 if (cr->nsec) LogDNSSEC("DeleteCachedNSECS: Deleting NSEC Records\n");
221 for (rp = cr->nsec; rp; rp = next)
222 {
223 next = rp->next;
224 ReleaseCacheRecord(m, rp);
225 }
226 cr->nsec = mDNSNULL;
227 }
228
229 // Returns success if it adds the nsecs and the rrsigs to the cache record. Otherwise, it returns
230 // failure (mDNSfalse)
231 mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
232 {
233 CacheRecord *cr;
234 mDNSBool nsecs_seen = mDNSfalse;
235 mDNSBool nsec3s_seen = mDNSfalse;
236
237 if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain)
238 {
239 LogMsg("AddNSECSForCacheRecord: Addings nsecs for rcode %d", rcode);
240 return mDNSfalse;
241 }
242
243 // Sanity check the list to see if we have anything else other than
244 // NSECs and its RRSIGs
245 for (cr = crlist; cr; cr = cr->next)
246 {
247 if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_NSEC3 &&
248 cr->resrec.rrtype != kDNSType_SOA && cr->resrec.rrtype != kDNSType_RRSIG)
249 {
250 LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr));
251 return mDNSfalse;
252 }
253 if (cr->resrec.rrtype == kDNSType_RRSIG)
254 {
255 RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data;
256 rdataRRSig *rrsig = &rdb->rrsig;
257 mDNSu16 tc = swap16(rrsig->typeCovered);
258 if (tc != kDNSType_NSEC && tc != kDNSType_NSEC3 && tc != kDNSType_SOA)
259 {
260 LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr));
261 return mDNSfalse;
262 }
263 }
264 else if (cr->resrec.rrtype == kDNSType_NSEC)
265 {
266 nsecs_seen = mDNStrue;
267 }
268 else if (cr->resrec.rrtype == kDNSType_NSEC3)
269 {
270 nsec3s_seen = mDNStrue;
271 }
272 LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr));
273 }
274 if ((nsecs_seen && nsec3s_seen) || (!nsecs_seen && !nsec3s_seen))
275 {
276 LogDNSSEC("AddNSECSForCacheRecord:ERROR nsecs_seen %d, nsec3s_seen %d", nsecs_seen, nsec3s_seen);
277 return mDNSfalse;
278 }
279 DeleteCachedNSECS(m, negcr);
280 LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr));
281 negcr->nsec = crlist;
282 return mDNStrue;
283 }
284
285 // Return the number of labels that matches starting from the right (excluding the
286 // root label)
287 mDNSexport int CountLabelsMatch(const domainname *const d1, const domainname *const d2)
288 {
289 int count, c1, c2;
290 int match, i, skip1, skip2;
291
292 c1 = CountLabels(d1);
293 skip1 = c1 - 1;
294 c2 = CountLabels(d2);
295 skip2 = c2 - 1;
296
297 // Root label always matches. And we don't include it here to
298 // match CountLabels
299 match = 0;
300
301 // Compare as many labels as possible starting from the rightmost
302 count = c1 < c2 ? c1 : c2;
303 for (i = count; i > 0; i--)
304 {
305 const domainname *da, *db;
306
307 da = SkipLeadingLabels(d1, skip1);
308 db = SkipLeadingLabels(d2, skip2);
309 if (!SameDomainName(da, db)) return match;
310 skip1--;
311 skip2--;
312 match++;
313 }
314 return match;
315 }
316
317 // Empty Non-Terminal (ENT): if the qname is bigger than nsec owner's name and a
318 // subdomain of the nsec's nxt field, then the qname is a empty non-terminal. For
319 // example, if you are looking for (in RFC 4035 example zone) "y.w.example A"
320 // record, if it is a ENT, then it would return
321 //
322 // x.w.example. 3600 NSEC x.y.w.example. MX RRSIG NSEC
323 //
324 // This function is normally called before checking for wildcard matches. If you
325 // find this NSEC, there is no need to look for a wildcard record
326 // that could possibly answer the question.
327 mDNSlocal mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname)
328 {
329 const domainname *oname = rr->name;
330 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
331 const domainname *nxt = (const domainname *)&rdb->data;
332 int ret;
333 int subdomain;
334
335 // Is the owner name smaller than qname?
336 ret = DNSSECCanonicalOrder(oname, qname, mDNSNULL);
337 if (ret < 0)
338 {
339 // Is the next domain field a subdomain of qname ?
340 ret = DNSSECCanonicalOrder(nxt, qname, &subdomain);
341 if (subdomain)
342 {
343 if (ret <= 0)
344 {
345 LogMsg("NSECAnswersENT: ERROR!! DNSSECCanonicalOrder subdomain set "
346 " qname %##s, NSEC %##s", qname->c, rr->name->c);
347 }
348 return mDNStrue;
349 }
350 }
351 return mDNSfalse;
352 }
353
354 mDNSlocal const domainname *NSECClosestEncloser(ResourceRecord *rr, domainname *qname)
355 {
356 const domainname *oname = rr->name;
357 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
358 const domainname *nxt = (const domainname *)&rdb->data;
359 int match1, match2;
360
361 match1 = CountLabelsMatch(oname, qname);
362 match2 = CountLabelsMatch(nxt, qname);
363 // Return the closest i.e the one that matches more labels
364 if (match1 > match2)
365 return SkipLeadingLabels(oname, CountLabels(oname) - match1);
366 else
367 return SkipLeadingLabels(nxt, CountLabels(nxt) - match2);
368 }
369
370 // Assumption: NSEC has been validated outside of this function
371 //
372 // Does the name exist given the name and NSEC rr ?
373 //
374 // Returns -1 if it is an inappropriate nsec
375 // Returns 1 if the name exists
376 // Returns 0 if the name does not exist
377 //
378 mDNSlocal int NSECNameExists(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype)
379 {
380 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
381 const domainname *nxt = (const domainname *)&rdb->data;
382 const domainname *oname = rr->name; // owner name
383 int ret1, subdomain1;
384 int ret2, subdomain2;
385 int ret3, subdomain3;
386
387 ret1 = DNSSECCanonicalOrder(oname, name, &subdomain1);
388 if (ret1 > 0)
389 {
390 LogDNSSEC("NSECNameExists: owner name %##s is bigger than name %##s", oname->c, name->c);
391 return -1;
392 }
393
394 // Section 4.1 of draft-ietf-dnsext-dnssec-bis-updates-14:
395 //
396 // Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume non-
397 // existence of any RRs below that zone cut, which include all RRs at
398 // that (original) owner name other than DS RRs, and all RRs below that
399 // owner name regardless of type.
400 //
401 // This also implies that we can't use the child side NSEC for DS question.
402
403 if (!ret1)
404 {
405 mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA);
406 mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS);
407
408 // We are here because the owner name is the same as "name". Make sure the
409 // NSEC has the right NS and SOA bits set.
410 if (qtype != kDNSType_DS && ns && !soa)
411 {
412 LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)",
413 RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
414 return -1;
415 }
416 else if (qtype == kDNSType_DS && soa)
417 {
418 LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)",
419 RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
420 return -1;
421 }
422 LogDNSSEC("NSECNameExists: owner name %##s is same as name %##s", oname->c, name->c);
423 return 1;
424 }
425
426 // If the name is a.b.com and NSEC's owner name is b.com i.e., a subdomain
427 // and nsec comes from the parent (NS is set and SOA is not set), then this
428 // NSEC can't be used for names below the owner name.
429 //
430 // Similarly if DNAME is set, we can't use it here. See RFC2672-bis-dname
431 // appendix.
432 if (subdomain1 && (RRAssertsExistence(rr, kDNSType_DNAME) ||
433 (RRAssertsNonexistence(rr, kDNSType_SOA) && RRAssertsExistence(rr, kDNSType_NS))))
434 {
435 LogDNSSEC("NSECNameExists: NSEC %s comes from the parent, can't use it here",
436 RRDisplayString(m, rr));
437 return -1;
438 }
439
440 // At this stage, we know that name is greater than the owner name and
441 // the nsec is not from the parent side.
442 //
443 // Compare with the next field in the nsec.
444 //
445 ret2 = DNSSECCanonicalOrder(name, nxt, &subdomain2);
446
447 // Exact match with the nsec next name
448 if (!ret2)
449 {
450 LogDNSSEC("NSECNameExists: name %##s is same as nxt name %##s", name->c, nxt->c);
451 return 1;
452 }
453
454 ret3 = DNSSECCanonicalOrder(oname, nxt, &subdomain3);
455
456 if (!ret3)
457 {
458 // Pathological case of a single name in the domain. This means only the
459 // apex of the zone itself exists. Nothing below it. "subdomain2" indicates
460 // that name is a subdmain of "next" and hence below the zone.
461 if (subdomain2)
462 {
463 LogDNSSEC("NSECNameExists: owner name %##s subdomain of nxt name %##s", oname->c, nxt->c);
464 return 0;
465 }
466 else
467 {
468 LogDNSSEC("NSECNameExists: Single name in zone, owner name %##s is same as nxt name %##s", oname->c, nxt->c);
469 return -1;
470 }
471 }
472
473 if (ret3 < 0)
474 {
475 // Regular NSEC in the zone. Make sure that the "name" lies within
476 // oname and next. oname < name and name < next
477 if (ret1 < 0 && ret2 < 0)
478 {
479 LogDNSSEC("NSECNameExists: Normal NSEC name %##s lies within owner %##s and nxt name %##s",
480 name->c, oname->c, nxt->c);
481 return 0;
482 }
483 else
484 {
485 LogDNSSEC("NSECNameExists: Normal NSEC name %##s does not lie within owner %##s and nxt name %##s",
486 name->c, oname->c, nxt->c);
487 return -1;
488 }
489 }
490 else
491 {
492 // Last NSEC in the zone. The "next" is pointing to the apex. All names
493 // should be a subdomain of that and the name should be bigger than
494 // oname
495 if (ret1 < 0 && subdomain2)
496 {
497 LogDNSSEC("NSECNameExists: Last NSEC name %##s lies within owner %##s and nxt name %##s",
498 name->c, oname->c, nxt->c);
499 return 0;
500 }
501 else
502 {
503 LogDNSSEC("NSECNameExists: Last NSEC name %##s does not lie within owner %##s and nxt name %##s",
504 name->c, oname->c, nxt->c);
505 return -1;
506 }
507 }
508
509 LogDNSSEC("NSECNameExists: NSEC %s did not match any case", RRDisplayString(m, rr));
510 return -1;
511 }
512
513 // If the answer was result of a wildcard match, then this function proves
514 // that a proper wildcard was used to answer the question and that the
515 // original name does not exist
516 mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv)
517 {
518 CacheRecord *ncr;
519 CacheRecord **rp;
520 const domainname *ce;
521 DNSQuestion q;
522 CacheRecord **nsec3 = mDNSNULL;
523
524 LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType));
525 //
526 // RFC 4035: Section 3.1.3.3
527 //
528 // 1) We used a wildcard because the qname does not exist, so verify
529 // that the qname does not exist
530 //
531 // 2) Is the wildcard the right one ?
532 //
533 // Unfortunately, this is not well explained in that section. Refer to
534 // RFC 5155 section 7.2.6.
535
536 // Walk the list of nsecs we received and see if they prove that
537 // the name does not exist
538
539 mDNSPlatformMemZero(&q, sizeof(DNSQuestion));
540 q.ThisQInterval = -1;
541 InitializeQuestion(m, &q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL);
542
543 ncr = NSECParentForQuestion(m, &q);
544 if (!ncr)
545 {
546 LogMsg("WildcardAnswerProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
547 goto error;
548 }
549 else
550 {
551 LogDNSSEC("WildcardAnswerProof: found %s", CRDisplayString(m, ncr));
552 }
553 rp = &(ncr->nsec);
554 while (*rp)
555 {
556 if ((*rp)->resrec.rrtype == kDNSType_NSEC)
557 {
558 CacheRecord *cr = *rp;
559 if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType))
560 break;
561 }
562 else if ((*rp)->resrec.rrtype == kDNSType_NSEC3)
563 {
564 nsec3 = rp;
565 }
566 rp=&(*rp)->next;
567 }
568 if (!(*rp))
569 {
570 mDNSBool ret = mDNSfalse;
571 if (nsec3)
572 {
573 ret = NSEC3WildcardAnswerProof(m, ncr, dv);
574 }
575 if (!ret)
576 {
577 LogDNSSEC("WildcardAnswerProof: NSEC3 wildcard proof failed for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
578 goto error;
579 }
580 rp = nsec3;
581 }
582 else
583 {
584 ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName);
585 if (!ce)
586 {
587 LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
588 goto error;
589 }
590 if (!SameDomainName(ce, dv->wildcardName))
591 {
592 LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c);
593 goto error;
594 }
595 }
596
597 VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL);
598 return;
599 error:
600 dv->DVCallback(m, dv, DNSSEC_Bogus);
601 }
602
603 // We have a NSEC. Need to see if it proves that NODATA exists for the given name. Note that this
604 // function does not prove anything as proof may require more than one NSEC and this function
605 // processes only one NSEC at a time.
606 //
607 // Returns mDNSfalse if the NSEC does not prove the NODATA error
608 // Returns mDNStrue if the NSEC proves the NODATA error
609 //
610 mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname *name, mDNSu16 qtype, domainname **wildcard)
611 {
612 const domainname *oname = rr->name; // owner name
613
614 if (wildcard) *wildcard = mDNSNULL;
615 // RFC 4035
616 //
617 // section 3.1.3.1 : Name matches. Prove that the type does not exist and also CNAME is
618 // not set as in that case CNAME should have been returned ( CNAME part is mentioned in
619 // section 4.3 of dnssec-bis-updates.) Without the CNAME check, a positive response can
620 // be converted to a NODATA/NOERROR response.
621 //
622 // section 3.1.3.4 : No exact match for the name but there is a wildcard that could match
623 // the name but not the type. There are two NSECs in this case. One of them is a wildcard
624 // NSEC and another NSEC proving that the qname does not exist. We are called with one
625 // NSEC at a time. We return what we matched and the caller should decide whether all
626 // conditions are met for the proof.
627 if (SameDomainName(oname, name))
628 {
629 mDNSBool soa = RRAssertsExistence(rr, kDNSType_SOA);
630 mDNSBool ns = RRAssertsExistence(rr, kDNSType_NS);
631 if (qtype != kDNSType_DS)
632 {
633 // For non-DS type questions, we don't want to use the parent side records to
634 // answer it
635 if (ns && !soa)
636 {
637 LogDNSSEC("NSECNoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
638 RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
639 return mDNSfalse;
640 }
641 }
642 else
643 {
644 if (soa)
645 {
646 LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
647 RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
648 return mDNSfalse;
649 }
650 }
651 if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME))
652 {
653 LogMsg("NSECNoDataError: ERROR!! qtype %s exists in %s", DNSTypeName(qtype), RRDisplayString(m, rr));
654 return mDNSfalse;
655 }
656 LogDNSSEC("NSECNoDataError: qype %s does not exist in %s", DNSTypeName(qtype), RRDisplayString(m, rr));
657 return mDNStrue;
658 }
659 else
660 {
661 // Name does not exist. Before we check for a wildcard match, make sure that
662 // this is not an ENT.
663 if (NSECAnswersENT(rr, name))
664 {
665 LogDNSSEC("NSECNoDataError: name %##s exists %s", name->c, RRDisplayString(m, rr));
666 return mDNSfalse;
667 }
668
669 // Wildcard check. If this is a wildcard NSEC, then check to see if we could
670 // have answered the question using this wildcard and it should not have the
671 // "qtype" passed in with its bitmap.
672 //
673 // See RFC 4592, on how wildcards are used to synthesize answers. Find the
674 // closest encloser and the qname should be a subdomain i.e if the wildcard
675 // is *.x.example, x.example is the closest encloser and the qname should be
676 // a subdomain e.g., y.x.example or z.y.x.example and so on.
677 if (oname->c[0] == 1 && oname->c[1] == '*')
678 {
679 int r, s;
680 const domainname *ce = SkipLeadingLabels(oname, 1);
681
682 r = DNSSECCanonicalOrder(name, ce, &s);
683 if (s)
684 {
685 if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME))
686 {
687 LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
688 return mDNSfalse;
689 }
690 if (qtype == kDNSType_DS && RRAssertsExistence(rr, kDNSType_SOA))
691 {
692 LogDNSSEC("NSECNoDataError: Child side wildcard NSEC %s, can't use for parent qname %##s (%s)",
693 RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
694 return mDNSfalse;
695 }
696 else if (qtype != kDNSType_DS && RRAssertsNonexistence(rr, kDNSType_SOA) &&
697 RRAssertsExistence(rr, kDNSType_NS))
698 {
699 // Don't use the parent side record for this
700 LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)",
701 RRDisplayString(m, rr), name->c, DNSTypeName(qtype));
702 return mDNSfalse;
703 }
704 *wildcard = (domainname *)ce;
705 LogDNSSEC("NSECNoDataError: qtype %s does not exist in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr));
706 return mDNStrue;
707 }
708 }
709 return mDNSfalse;
710 }
711 }
712
713 mDNSexport void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
714 {
715 RRVerifier *rv;
716 DNSSECVerifier *pdv;
717 CacheRecord *ncr;
718
719 LogDNSSEC("NoDataNSECCallback: called");
720 if (!dv->parent)
721 {
722 LogMsg("NoDataNSECCCallback: no parent DV");
723 FreeDNSSECVerifier(m, dv);
724 return;
725 }
726
727 if (dv->ac)
728 {
729 // Before we free the "dv", we need to update the
730 // parent with our AuthChain information
731 UpdateParent(dv);
732 }
733
734 pdv = dv->parent;
735
736 // We don't care about the "dv" that was allocated in VerifyNSEC
737 // as it just verifies one of the nsecs. Get the original verifier and
738 // verify the other NSEC like we did the first time.
739 dv->parent = mDNSNULL;
740 FreeDNSSECVerifier(m, dv);
741
742 if (status != DNSSEC_Secure)
743 {
744 goto error;
745 }
746
747 ncr = NSECParentForQuestion(m, &pdv->q);
748 if (!ncr)
749 {
750 LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
751 goto error;
752 }
753 rv = pdv->pendingNSEC;
754 pdv->pendingNSEC = rv->next;
755 // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one,
756 // we don't need to come back here; let the regular NSECCallback call the original callback.
757 rv->next = mDNSNULL;
758 LogDNSSEC("NoDataNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype));
759 if (!pdv->pendingNSEC)
760 VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
761 else
762 VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NoDataNSECCallback);
763 return;
764
765 error:
766 pdv->DVCallback(m, pdv, status);
767 }
768
769 mDNSexport void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status)
770 {
771 RRVerifier *rv;
772 DNSSECVerifier *pdv;
773 CacheRecord *ncr;
774
775 LogDNSSEC("NameErrorNSECCallback: called");
776 if (!dv->parent)
777 {
778 LogMsg("NameErrorNSECCCallback: no parent DV");
779 FreeDNSSECVerifier(m, dv);
780 return;
781 }
782
783 if (dv->ac)
784 {
785 // Before we free the "dv", we need to update the
786 // parent with our AuthChain information
787 UpdateParent(dv);
788 }
789
790 pdv = dv->parent;
791 // We don't care about the "dv" that was allocated in VerifyNSEC
792 // as it just verifies one of the nsecs. Get the original verifier and
793 // verify the other NSEC like we did the first time.
794 dv->parent = mDNSNULL;
795 FreeDNSSECVerifier(m, dv);
796
797 if (status != DNSSEC_Secure)
798 {
799 goto error;
800 }
801
802 ncr = NSECParentForQuestion(m, &pdv->q);
803 if (!ncr)
804 {
805 LogMsg("NameErrorNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype));
806 goto error;
807 }
808 rv = pdv->pendingNSEC;
809 pdv->pendingNSEC = rv->next;
810 // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one,
811 // we don't need to come back here; let the regular NSECCallback call the original callback.
812 rv->next = mDNSNULL;
813 LogDNSSEC("NameErrorNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype));
814 if (!pdv->pendingNSEC)
815 VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL);
816 else
817 VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NameErrorNSECCallback);
818
819 return;
820
821 error:
822 pdv->DVCallback(m, pdv, status);
823 }
824
825 // We get a NODATA error with no records in answer section. This proves
826 // that qname does not exist.
827 mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
828 {
829 CacheRecord **rp;
830 domainname *wildcard = mDNSNULL;
831 const domainname *ce = mDNSNULL;
832 ResourceRecord *nsec_wild = mDNSNULL;
833 ResourceRecord *nsec_noname = mDNSNULL;
834
835 // NODATA Error could mean two things. The name exists with no type or there is a
836 // wildcard that matches the name but no type. This is done by NSECNoDataError.
837 //
838 // If it is the case of wildcard, there are two NSECs. One is the wildcard NSEC and
839 // the other NSEC to prove that there is no other closer match.
840
841 wildcard = mDNSNULL;
842 rp = &(ncr->nsec);
843 while (*rp)
844 {
845 if ((*rp)->resrec.rrtype == kDNSType_NSEC)
846 {
847 CacheRecord *cr = *rp;
848 if (NSECNoDataError(m, &cr->resrec, &dv->q.qname, dv->q.qtype, &wildcard))
849 {
850 if (wildcard)
851 {
852 dv->flags |= WILDCARD_PROVES_NONAME_EXISTS;
853 LogDNSSEC("NoDataProof: NSEC %s proves NODATA error for %##s (%s)",
854 RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
855 }
856 else
857 {
858 dv->flags |= NSEC_PROVES_NOTYPE_EXISTS;
859 LogDNSSEC("NoDataProof: NSEC %s proves NOTYPE error for %##s (%s)",
860 RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
861 }
862 nsec_wild = &cr->resrec;
863 }
864 if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
865 {
866 LogDNSSEC("NoDataProof: NSEC %s proves that name %##s (%s) does not exist",
867 RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
868 // If we have a wildcard, then we should check to see if the closest
869 // encloser is the same as the wildcard.
870 ce = NSECClosestEncloser(&cr->resrec, &dv->q.qname);
871 dv->flags |= NSEC_PROVES_NONAME_EXISTS;
872 nsec_noname = &cr->resrec;
873 }
874 }
875 rp=&(*rp)->next;
876 }
877 if (!nsec_noname && !nsec_wild)
878 {
879 LogDNSSEC("NoDataProof: No valid NSECs for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
880 goto error;
881 }
882 // If the type exists, then we have to verify just that NSEC
883 if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
884 {
885 // If we have a wildcard, then we should have a "ce" which matches the wildcard
886 // If we don't have a wildcard, then we should have proven that the name does not
887 // exist which means we would have set the "ce".
888 if (wildcard && !ce)
889 {
890 LogMsg("NoDataProof: Cannot prove that the name %##s (%s) does not exist", dv->q.qname.c, DNSTypeName(dv->q.qtype));
891 goto error;
892 }
893 if (wildcard && !SameDomainName(wildcard, ce))
894 {
895 LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c);
896 goto error;
897 }
898 // If a single NSEC can prove both, then we just have validate that one NSEC.
899 if (nsec_wild == nsec_noname)
900 {
901 nsec_noname = mDNSNULL;
902 dv->flags &= ~NSEC_PROVES_NONAME_EXISTS;
903 }
904 }
905
906 if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) ==
907 (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS))
908 {
909 mStatus status;
910 RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
911 if (!r) goto error;
912 // First verify wildcard NSEC and then when we are done, we
913 // will verify the noname nsec
914 dv->pendingNSEC = r;
915 LogDNSSEC("NoDataProof: Verifying wild and noname %s", RRDisplayString(m, nsec_wild));
916 VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback);
917 }
918 else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) ||
919 (dv->flags & NSEC_PROVES_NOTYPE_EXISTS))
920 {
921 LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild));
922 VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
923 }
924 else if (dv->flags & NSEC_PROVES_NONAME_EXISTS)
925 {
926 LogDNSSEC("NoDataProof: Verifying noname %s", RRDisplayString(m, nsec_noname));
927 VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL);
928 }
929 return;
930 error:
931 LogDNSSEC("NoDataProof: Error return");
932 dv->DVCallback(m, dv, DNSSEC_Bogus);
933 }
934
935 mDNSlocal mDNSBool NSECNoWildcard(mDNS *const m, ResourceRecord *rr, domainname *qname, mDNSu16 qtype)
936 {
937 const domainname *ce;
938 domainname wild;
939
940 // If the query name is c.x.w.example and if the name does not exist, we should get
941 // get a nsec back that looks something like this:
942 //
943 // w.example NSEC a.w.example
944 //
945 // First, we need to get the closest encloser which in this case is w.example. Wild
946 // card synthesis works by finding the closest encloser first and then look for
947 // a "*" label (assuming * label does not appear in the question). If it does not
948 // exists, it would return the NSEC at that name. And the wildcard name at the
949 // closest encloser "*.w.example" would be covered by such an NSEC. (Appending "*"
950 // makes it bigger than w.example and "* is smaller than "a" for the above NSEC)
951 //
952 ce = NSECClosestEncloser(rr, qname);
953 if (!ce) { LogMsg("NSECNoWildcard: No closest encloser for rr %s, qname %##s (%s)", qname->c, DNSTypeName(qtype)); return mDNSfalse; }
954
955 wild.c[0] = 1;
956 wild.c[1] = '*';
957 wild.c[2] = 0;
958 if (!AppendDomainName(&wild, ce))
959 {
960 LogMsg("NSECNoWildcard: ERROR!! Can't append domainname closest encloser name %##s, qname %##s (%s)", ce->c, qname->c, DNSTypeName(qtype));
961 return mDNSfalse;
962 }
963 if (NSECNameExists(m, rr, &wild, qtype) != 0)
964 {
965 LogDNSSEC("NSECNoWildcard: Wildcard name %##s exists or not valid qname %##s (%s)", wild.c, qname->c, DNSTypeName(qtype));
966 return mDNSfalse;
967 }
968 LogDNSSEC("NSECNoWildcard: Wildcard name %##s does not exist for record %s, qname %##s (%s)", wild.c,
969 RRDisplayString(m, rr), qname->c, DNSTypeName(qtype));
970 return mDNStrue;
971 }
972
973 // We get a NXDOMAIN error with no records in answer section. This proves
974 // that qname does not exist.
975 mDNSlocal void NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr)
976 {
977 CacheRecord **rp;
978 ResourceRecord *nsec_wild = mDNSNULL;
979 ResourceRecord *nsec_noname = mDNSNULL;
980 mStatus status;
981
982 // NXDOMAIN Error. We need to prove that the qname does not exist and there
983 // is no wildcard that can be used to answer the question.
984
985 rp = &(ncr->nsec);
986 while (*rp)
987 {
988 if ((*rp)->resrec.rrtype == kDNSType_NSEC)
989 {
990 CacheRecord *cr = *rp;
991 if (!NSECNameExists(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
992 {
993 LogDNSSEC("NameErrorProof: NSEC %s proves name does not exist for %##s (%s)",
994 RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
995 // If we have a wildcard, then we should check to see if the closest
996 // encloser is the same as the wildcard.
997 dv->flags |= NSEC_PROVES_NONAME_EXISTS;
998 nsec_noname = &cr->resrec;
999 }
1000 if (NSECNoWildcard(m, &cr->resrec, &dv->q.qname, dv->q.qtype))
1001 {
1002 dv->flags |= WILDCARD_PROVES_NONAME_EXISTS;
1003 nsec_wild = &cr->resrec;
1004 LogDNSSEC("NameErrorProof: NSEC %s proves wildcard cannot answer question for %##s (%s)",
1005 RRDisplayString(m, &(*rp)->resrec), dv->q.qname.c, DNSTypeName(dv->q.qtype));
1006 }
1007 }
1008 rp=&(*rp)->next;
1009 }
1010 if (!nsec_noname || !nsec_wild)
1011 {
1012 LogMsg("NameErrorProof: Proof failed for %##s (%s) noname %p, wild %p", dv->q.qname.c, DNSTypeName(dv->q.qtype), nsec_noname, nsec_wild);
1013 goto error;
1014 }
1015
1016 // First verify wildcard NSEC and then when we are done, we will verify the noname nsec.
1017 // Sometimes a single NSEC can prove both that the "qname" does not exist and a wildcard
1018 // could not have produced qname. These are a few examples where this can happen.
1019 //
1020 // 1. If the zone is example.com and you look up *.example.com and if there are no wildcards,
1021 // you will get a NSEC back "example.com NSEC a.example.com". This proves that both the
1022 // name does not exist and *.example.com also does not exist
1023 //
1024 // 2. If the zone is example.com and it has a record like this:
1025 //
1026 // example.com NSEC d.example.com
1027 //
1028 // any name you lookup in between like a.example.com,b.example.com etc. you will get a single
1029 // NSEC back. In that case we just have to verify only once.
1030 //
1031 if (nsec_wild != nsec_noname)
1032 {
1033 RRVerifier *r = AllocateRRVerifier(nsec_noname, &status);
1034 if (!r) goto error;
1035 dv->pendingNSEC = r;
1036 LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild));
1037 VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback);
1038 }
1039 else
1040 {
1041 LogDNSSEC("NoDataProof: Verifying only one %s", RRDisplayString(m, nsec_wild));
1042 VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL);
1043 }
1044 return;
1045 error:
1046 dv->DVCallback(m, dv, DNSSEC_Bogus);
1047 }
1048
1049 mDNSexport CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype)
1050 {
1051 CacheGroup *cg;
1052 CacheRecord *cr;
1053 mDNSu32 slot, namehash;
1054
1055 slot = HashSlot(name);
1056 namehash = DomainNameHashValue(name);
1057
1058 cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name);
1059 if (!cg)
1060 {
1061 LogDNSSEC("NSECRecordForName: cg NULL for %##s", name);
1062 return mDNSNULL;
1063 }
1064 for (cr = cg->members; cr; cr = cr->next)
1065 {
1066 if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && cr->resrec.rrtype == qtype)
1067 {
1068 CacheRecord *ncr;
1069 for (ncr = cr->nsec; ncr; ncr = ncr->next)
1070 {
1071 if (ncr->resrec.rrtype == kDNSType_NSEC &&
1072 SameDomainName(ncr->resrec.name, name))
1073 {
1074 // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
1075 // should be absent
1076 if (RRAssertsExistence(&ncr->resrec, kDNSType_SOA) ||
1077 RRAssertsExistence(&ncr->resrec, kDNSType_DS))
1078 {
1079 LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but DS or SOA bit set", CRDisplayString(m, ncr), name,
1080 DNSTypeName(qtype));
1081 return mDNSNULL;
1082 }
1083 // Section 2.3 of RFC 4035 states that:
1084 //
1085 // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
1086 // have an NSEC resource record.
1087 //
1088 // So, if we have an NSEC record matching the question name with the NS bit set,
1089 // then this is a delegation.
1090 //
1091 if (RRAssertsExistence(&ncr->resrec, kDNSType_NS))
1092 {
1093 LogDNSSEC("NSECRecordForName: found record %s for %##s (%s)", CRDisplayString(m, ncr), name, DNSTypeName(qtype));
1094 return ncr;
1095 }
1096 else
1097 {
1098 LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but NS bit is not set", CRDisplayString(m, ncr), name,
1099 DNSTypeName(qtype));
1100 return mDNSNULL;
1101 }
1102 }
1103 }
1104 }
1105 }
1106 return mDNSNULL;
1107 }
1108
1109 mDNSlocal void StartInsecureProof(mDNS * const m, DNSSECVerifier *dv)
1110 {
1111 domainname trigger;
1112 DNSSECVerifier *prevdv = mDNSNULL;
1113
1114 // Remember the name that triggered the insecure proof
1115 AssignDomainName(&trigger, &dv->q.qname);
1116 while (dv->parent)
1117 {
1118 prevdv = dv;
1119 dv = dv->parent;
1120 }
1121 if (prevdv)
1122 {
1123 prevdv->parent = mDNSNULL;
1124 FreeDNSSECVerifier(m, prevdv);
1125 }
1126 // For Optional DNSSEC, we are opportunistically verifying dnssec. We don't care
1127 // if something results in bogus as we still want to deliver results to the
1128 // application e.g., CNAME processing results in bogus because the path is broken,
1129 // but we still want to follow CNAMEs so that we can deliver the final results to
1130 // the application.
1131 if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL)
1132 {
1133 LogDNSSEC("StartInsecureProof: Aborting insecure proof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
1134 dv->DVCallback(m, dv, DNSSEC_Bogus);
1135 return;
1136 }
1137
1138 LogDNSSEC("StartInsecureProof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
1139 // Don't start the insecure proof again after we finish the one that we start here by
1140 // setting InsecureProofDone.
1141 dv->InsecureProofDone = 1;
1142 ProveInsecure(m, dv, mDNSNULL, &trigger);
1143 return;
1144 }
1145
1146 mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr)
1147 {
1148 LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr));
1149
1150 // If we are encountering a break in the chain of trust i.e., NSEC/NSEC3s for
1151 // DS query, then do the insecure proof. This is important because if we
1152 // validate these NSECs normally and prove that they are "secure", we will
1153 // end up delivering the secure result to the original question where as
1154 // these NSEC/NSEC3s actually prove that DS does not exist and hence insecure.
1155 //
1156 // This break in the chain can happen after we have partially validated the
1157 // path (dv->ac is non-NULL) or the first time (dv->ac is NULL) after we
1158 // fetched the DNSKEY (dv->key is non-NULL). We don't want to do this
1159 // if we have just started the non-existence proof (dv->key is NULL) as
1160 // it does not indicate a break in the chain of trust.
1161 //
1162 // If we are already doing a insecurity proof, don't start another one. In
1163 // the case of NSECs, it is possible that insecurity proof starts and it
1164 // gets NSECs and as part of validating that we receive more NSECS in which
1165 // case we don't want to start another insecurity proof.
1166 if (dv->ValidationRequired != DNSSEC_VALIDATION_INSECURE &&
1167 (!dv->parent || dv->parent->ValidationRequired != DNSSEC_VALIDATION_INSECURE))
1168 {
1169 if ((dv->ac && dv->q.qtype == kDNSType_DS) ||
1170 (!dv->ac && dv->key && dv->q.qtype == kDNSType_DS))
1171 {
1172 LogDNSSEC("ValidateWithNSECS: Starting insecure proof: name %##s ac %p, key %p, parent %p", dv->q.qname.c,
1173 dv->ac, dv->key, dv->parent);
1174 StartInsecureProof(m, dv);
1175 return;
1176 }
1177 }
1178 // "parent" is set when we are validating a NSEC and we should not be here in
1179 // the normal case when parent is set. For example, we are looking up the A
1180 // record for www.example.com and following can happen.
1181 //
1182 // a) Record does not exist and we get a NSEC
1183 // b) While validating (a), we get an NSEC for the first DS record that we look up
1184 // c) Record exists but we get NSECs for the first DS record
1185 // d) We are able to partially validate (a) or (b), but we get NSECs somewhere in
1186 // the chain
1187 //
1188 // For (a), parent is not set as we are not validating the NSEC yet. Hence we would
1189 // start the validation now.
1190 //
1191 // For (b), the parent is set, but should be caught by the above "if" block because we
1192 // should have gotten the DNSKEY at least. In the case of nested insecurity proof,
1193 // we would end up here and fail with bogus.
1194 //
1195 // For (c), the parent is not set and should be caught by the above "if" block because we
1196 // should have gotten the DNSKEY at least.
1197 //
1198 // For (d), the above "if" block would catch it as "dv->ac" is non-NULL.
1199 //
1200 // Hence, we should not come here in the normal case. Possible pathological cases are:
1201 // Insecure proof getting NSECs while validating NSECs, getting NSECs for DNSKEY for (c)
1202 // above etc.
1203 if (dv->parent)
1204 {
1205 LogDNSSEC("ValidateWithNSECS: dv parent set for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype));
1206 dv->DVCallback(m, dv, DNSSEC_Bogus);
1207 return;
1208 }
1209 if (cr->resrec.RecordType == kDNSRecordTypePacketNegative)
1210 {
1211 mDNSu8 rcode;
1212 CacheRecord *neg = cr->nsec;
1213 mDNSBool nsecs_seen = mDNSfalse;
1214
1215 while (neg)
1216 {
1217 // The list can only have NSEC or NSEC3s. This was checked when we added the
1218 // NSECs to the cache record.
1219 if (neg->resrec.rrtype == kDNSType_NSEC)
1220 nsecs_seen = mDNStrue;
1221 LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg));
1222 neg = neg->next;
1223 }
1224
1225 rcode = (mDNSu8)(cr->responseFlags.b[1] & kDNSFlag1_RC_Mask);
1226 if (rcode == kDNSFlag1_RC_NoErr)
1227 {
1228 if (nsecs_seen)
1229 NoDataProof(m, dv, cr);
1230 else
1231 NSEC3NoDataProof(m, dv, cr);
1232 }
1233 else if (rcode == kDNSFlag1_RC_NXDomain)
1234 {
1235 if (nsecs_seen)
1236 NameErrorProof(m, dv, cr);
1237 else
1238 NSEC3NameErrorProof(m, dv, cr);
1239 }
1240 else
1241 {
1242 LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", rcode);
1243 dv->DVCallback(m, dv, DNSSEC_Bogus);
1244 }
1245 }
1246 else
1247 {
1248 LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr));
1249 dv->DVCallback(m, dv, DNSSEC_Bogus);
1250 return;
1251 }
1252 }
1253
1254 #else // !DNSSEC_DISABLED
1255
1256 mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode)
1257 {
1258 (void)m;
1259 (void)crlist;
1260 (void)negcr;
1261 (void)rcode;
1262
1263 return mDNSfalse;
1264 }
1265
1266 #endif // !DNSSEC_DISABLED