1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 // ***************************************************************************
19 // nsec.c: This file contains support functions to validate NSEC records for
20 // NODATA and NXDOMAIN error.
21 // ***************************************************************************
23 #include "mDNSEmbeddedAPI.h"
24 #include "DNSCommon.h"
27 // Implementation Notes
29 // NSEC records in DNSSEC are used for authenticated denial of existence i.e., if the response to a query
30 // results in NXDOMAIN or NODATA error, the response also contains NSEC records in the additional section
31 // to prove the non-existence of the original name. In most of the cases, NSEC records don't have any
32 // relationship to the original name queried i.e, if they are cached based on the name like other records,
33 // it can't be located to prove the non-existence of the original name. Hence, we create a negative cache
34 // record like we do for the NXDOMAIN/NODATA error and then cache the NSEC records as part of that. Sometimes,
35 // NSEC records are also used for wildcard expanded answer in which case they are cached with the cache record
36 // that is created for the original name. NSEC records are freed when the parent cache (the record that they
37 // are attached to is expired).
39 // NSEC records also can be queried like any other record and hence can exist independent of the negative
40 // cache record. It exists as part of negative cache record only when we get a NXDOMAIN/NODATA error with
41 // NSEC records. When a query results in NXDOMAIN/NODATA error and needs to be validated, the NSEC
42 // records (and its RRSIGS) are cached as part of the negative cache record. The NSEC records that
43 // exist separately from the negative cache record should not be used to answer ValidationRequired/
44 // ValidatingResponse questions as it may not be sufficient to prove the non-existence of the name.
45 // The exception is when the NSEC record is looked up explicitly. See DNSSECRecordAnswersQuestion
49 mDNSlocal CacheRecord
*NSECParentForQuestion(mDNS
*const m
, DNSQuestion
*q
)
56 slot
= HashSlot(&q
->qname
);
57 namehash
= DomainNameHashValue(&q
->qname
);
58 cg
= CacheGroupForName(m
, slot
, namehash
, &q
->qname
);
61 LogDNSSEC("NSECParentForQuestion: Cannot find cg for %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
64 for (cr
= cg
->members
; cr
; cr
= cr
->next
)
65 if (SameNameRecordAnswersQuestion(&cr
->resrec
, q
))
70 // Note: This should just call the parent callback which will free the DNSSECVerifier.
71 mDNSlocal
void VerifyNSECCallback(mDNS
*const m
, DNSSECVerifier
*dv
, DNSSECStatus status
)
75 LogMsg("VerifyNSECCCallback: ERROR!! no parent DV\n");
76 FreeDNSSECVerifier(m
, dv
);
81 // Before we call the callback, we need to update the
82 // parent with our AuthChain information
83 AuthChainLink(dv
->parent
, dv
->ac
);
87 dv
->parent
->DVCallback(m
, dv
->parent
, status
);
88 // The callback we called in the previous line should recursively
89 // free all the DNSSECVerifiers starting from dv->parent and above.
90 // So, set that to NULL and free the "dv" itself here.
91 dv
->parent
= mDNSNULL
;
92 FreeDNSSECVerifier(m
, dv
);
95 // If the caller provides a callback, it takes the responsibility of calling the original callback
96 // in "pdv" when it is done.
100 // rr: The NSEC record that should be verified
101 // rv: The NSEC record can also be provided like this
102 // pdv: Parent DNSSECVerifier which will be called when the verification is done.
103 // callback: As part of the proof, we need multiple NSEC verifications before we call the "pdv" callback in
104 // which case a intermediate "callback" is provided which can be used to do multiple verifications.
105 // ncr: The cache record where the RRSIGS are cached
107 // NSEC records and signatures are cached along with the cache record so that we can expire them all together. We can't cache
108 // them based on the name hash like other records as in most cases the returned NSECs has a different name than we asked for
109 // (except for NODATA error where the name exists but type does not exist).
111 mDNSlocal
void VerifyNSEC(mDNS
*const m
, ResourceRecord
*rr
, RRVerifier
*rv
, DNSSECVerifier
*pdv
, CacheRecord
*ncr
,
112 DNSSECVerifierCallback callback
)
114 DNSSECVerifier
*dv
= mDNSNULL
;
116 const domainname
*name
;
121 LogDNSSEC("VerifyNSEC: Both rr and rv are NULL");
126 LogDNSSEC("VerifyNSEC: ERROR!! pdv is NULL");
129 // Remember the name and type for which we are verifying, so that when we are done processing all
130 // the verifications, we can trace it back.
132 // Note: Currently it is not used because when the verification completes as we just
133 // call the "pdv" callback which has its origName and origType.
145 dv
= AllocateDNSSECVerifier(m
, name
, rrtype
, pdv
->q
.InterfaceID
, (callback
? callback
: VerifyNSECCallback
), mDNSNULL
);
146 if (!dv
) { LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); return; }
150 if (AddRRSetToVerifier(dv
, rr
, rv
, RRVS_rr
) != mStatus_NoError
)
152 LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier failed to add NSEC");
156 // Add the signatures after validating them
160 if ((*rp
)->resrec
.rrtype
== kDNSType_RRSIG
)
162 ValidateRRSIG(dv
, RRVS_rrsig
, &(*rp
)->resrec
);
167 if (!dv
->rrset
|| !dv
->rrsig
)
169 LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset %p, rrsig %p", dv
->rrset
, dv
->rrsig
);
173 // Next step is to fetch the keys
176 StartDNSSECVerification(m
, dv
);
179 pdv
->DVCallback(m
, pdv
, DNSSEC_Indeterminate
);
182 dv
->parent
= mDNSNULL
;
183 FreeDNSSECVerifier(m
, dv
);
188 mDNSlocal
void DeleteCachedNSECS(mDNS
*const m
, CacheRecord
*cr
)
190 CacheRecord
*rp
, *next
;
192 if (cr
->nsec
) LogDNSSEC("DeleteCachedNSECS: Deleting NSEC Records\n");
193 for (rp
= cr
->nsec
; rp
; rp
= next
)
196 ReleaseCacheRecord(m
, rp
);
201 // Returns success if it adds the nsecs and the rrsigs to the cache record. Otherwise, it returns
202 // failure (mDNSfalse)
203 mDNSexport mDNSBool
AddNSECSForCacheRecord(mDNS
*const m
, CacheRecord
*crlist
, CacheRecord
*negcr
, mDNSu8 rcode
)
205 CacheRecord
*cr
, *next
;
207 if (rcode
!= kDNSFlag1_RC_NoErr
&& rcode
!= kDNSFlag1_RC_NXDomain
)
209 LogMsg("AddNSECSForCacheRecord: Addings nsecs for rcode %d", rcode
);
213 // Sanity check the list to see if we have anything else other than
214 // NSECs and its RRSIGs
215 for (cr
= crlist
; cr
; cr
= cr
->next
)
218 if (cr
->resrec
.rrtype
!= kDNSType_NSEC
&& cr
->resrec
.rrtype
!= kDNSType_RRSIG
)
220 LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m
, cr
));
223 if (cr
->resrec
.rrtype
== kDNSType_RRSIG
)
225 RDataBody2
*const rdb
= (RDataBody2
*)cr
->smallrdatastorage
.data
;
226 rdataRRSig
*rrsig
= &rdb
->rrsig
;
227 if (swap16(rrsig
->typeCovered
) != kDNSType_NSEC
)
229 LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m
, cr
));
233 LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m
, cr
));
235 DeleteCachedNSECS(m
, negcr
);
236 LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m
, negcr
));
237 negcr
->nsec
= crlist
;
238 negcr
->rcode
= rcode
;
242 // Return the number of labels that matches starting from the right (excluding the
244 mDNSlocal
int CountLabelsMatch(const domainname
*const d1
, const domainname
*const d2
)
247 int match
, i
, skip1
, skip2
;
249 c1
= CountLabels(d1
);
251 c2
= CountLabels(d2
);
254 // Root label always matches. And we don't include it here to
258 // Compare as many labels as possible starting from the rightmost
259 count
= c1
< c2
? c1
: c2
;
260 for (i
= count
; i
> 0; i
--)
262 const domainname
*da
, *db
;
264 da
= SkipLeadingLabels(d1
, skip1
);
265 db
= SkipLeadingLabels(d2
, skip2
);
266 if (!SameDomainName(da
, db
)) return match
;
278 // For the purposes of DNS security, owner names are ordered by treating
279 // individual labels as unsigned left-justified octet strings. The
280 // absence of a octet sorts before a zero value octet, and uppercase
281 // US-ASCII letters are treated as if they were lowercase US-ASCII
284 // To compute the canonical ordering of a set of DNS names, start by
285 // sorting the names according to their most significant (rightmost)
286 // labels. For names in which the most significant label is identical,
287 // continue sorting according to their next most significant label, and
290 // Returns 0 if the names are same
291 // Returns -1 if d1 < d2
292 // Returns 1 if d1 > d2
294 // subdomain is set if there is at least one label match (starting from the end)
295 // and d1 has more labels than d2 e.g., a.b.com is a subdomain of b.com
297 mDNSlocal
int DNSSECCanonicalOrder(const domainname
*const d1
, const domainname
*const d2
, int *subdomain
)
302 c1
= CountLabels(d1
);
304 c2
= CountLabels(d2
);
307 if (subdomain
) *subdomain
= 0;
309 // Compare as many labels as possible starting from the rightmost
310 count
= c1
< c2
? c1
: c2
;
311 for (i
= count
; i
> 0; i
--)
314 int j
, len
, lena
, lenb
;
316 a
= (mDNSu8
*)SkipLeadingLabels(d1
, skip1
);
317 b
= (mDNSu8
*)SkipLeadingLabels(d2
, skip2
);
320 // Compare label by label. Note that "z" > "yak" because z > y, but z < za
321 // (lena - lenb check below) because 'za' has two characters. Hence compare the
322 // letters first and then compare the length of the label at the end.
323 len
= lena
< lenb
? lena
: lenb
;
325 for (j
= 0; j
< len
; j
++)
329 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
330 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
333 verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac
, bc
);
334 return ((ac
< bc
) ? -1 : 1);
337 if ((lena
- lenb
) != 0)
339 verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena
, lenb
);
340 return ((lena
< lenb
) ? -1 : 1);
343 // Continue with the next label
347 // We have compared label by label. Both of them are same if we are here.
349 // Two possibilities.
351 // 1) Both names have same number of labels. In that case, return zero.
352 // 2) The number of labels is not same. As zero label sorts before, names
353 // with more number of labels is greater.
355 // a.b.com is a subdomain of b.com
356 if ((c1
> c2
) && subdomain
)
359 verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1
, c2
);
361 return ((c1
< c2
) ? -1 : 1);
366 // Empty Non-Terminal (ENT): if the qname is bigger than nsec owner's name and a
367 // subdomain of the nsec's nxt field, then the qname is a empty non-terminal. For
368 // example, if you are looking for (in RFC 4035 example zone) "y.w.example A"
369 // record, if it is a ENT, then it would return
371 // x.w.example. 3600 NSEC x.y.w.example. MX RRSIG NSEC
373 // This function is normally called before checking for wildcard matches. If you
374 // find this NSEC, there is no need to look for a wildcard record
375 // that could possibly answer the question.
376 mDNSexport mDNSBool
NSECAnswersENT(const ResourceRecord
*const rr
, domainname
*qname
)
378 const domainname
*oname
= rr
->name
;
379 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
380 const domainname
*nxt
= (const domainname
*)&rdb
->data
;
384 // Is the owner name smaller than qname?
385 ret
= DNSSECCanonicalOrder(oname
, qname
, mDNSNULL
);
388 // Is the next domain field a subdomain of qname ?
389 ret
= DNSSECCanonicalOrder(nxt
, qname
, &subdomain
);
394 LogMsg("NSECAnswersENT: ERROR!! DNSSECCanonicalOrder subdomain set "
395 " qname %##s, NSEC %##s", qname
->c
, rr
->name
->c
);
403 mDNSlocal
const domainname
*NSECClosestEncloser(ResourceRecord
*rr
, domainname
*qname
)
405 const domainname
*oname
= rr
->name
;
406 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
407 const domainname
*nxt
= (const domainname
*)&rdb
->data
;
410 match1
= CountLabelsMatch(oname
, qname
);
411 match2
= CountLabelsMatch(nxt
, qname
);
412 // Return the closest i.e the one that matches more labels
414 return SkipLeadingLabels(oname
, CountLabels(oname
) - match1
);
416 return SkipLeadingLabels(nxt
, CountLabels(nxt
) - match2
);
419 // Assumption: NSEC has been validated outside of this function
421 // Does the name exist given the name and NSEC rr ?
423 // Returns -1 if it is an inappropriate nsec
424 // Returns 1 if the name exists
425 // Returns 0 if the name does not exist
427 mDNSlocal
int NSECNameExists(mDNS
*const m
, ResourceRecord
*rr
, domainname
*name
, mDNSu16 qtype
)
429 const RDataBody2
*const rdb
= (RDataBody2
*)rr
->rdata
->u
.data
;
430 const domainname
*nxt
= (const domainname
*)&rdb
->data
;
431 const domainname
*oname
= rr
->name
; // owner name
432 int ret1
, subdomain1
;
433 int ret2
, subdomain2
;
434 int ret3
, subdomain3
;
436 ret1
= DNSSECCanonicalOrder(oname
, name
, &subdomain1
);
439 LogDNSSEC("NSECNameExists: owner name %##s is bigger than name %##s", oname
->c
, name
->c
);
443 // Section 4.1 of draft-ietf-dnsext-dnssec-bis-updates-14:
445 // Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume non-
446 // existence of any RRs below that zone cut, which include all RRs at
447 // that (original) owner name other than DS RRs, and all RRs below that
448 // owner name regardless of type.
450 // This also implies that we can't use the child side NSEC for DS question.
454 mDNSBool soa
= RRAssertsExistence(rr
, kDNSType_SOA
);
455 mDNSBool ns
= RRAssertsExistence(rr
, kDNSType_NS
);
457 // We are here because the owner name is the same as "name". Make sure the
458 // NSEC has the right NS and SOA bits set.
459 if (ns
&& !soa
&& qtype
!= kDNSType_DS
)
461 LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)",
462 RRDisplayString(m
, rr
), name
->c
, DNSTypeName(qtype
));
465 else if (ns
&& soa
&& qtype
== kDNSType_DS
)
467 LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)",
468 RRDisplayString(m
, rr
), name
->c
, DNSTypeName(qtype
));
471 LogDNSSEC("NSECNameExists: owner name %##s is same as name %##s", oname
->c
, name
->c
);
475 // If the name is a.b.com and NSEC's owner name is b.com i.e., a subdomain
476 // and nsec comes from the parent (NS is set and SOA is not set), then this
477 // NSEC can't be used for names below the owner name.
479 // Similarly if DNAME is set, we can't use it here. See RFC2672-bis-dname
481 if (subdomain1
&& (RRAssertsExistence(rr
, kDNSType_DNAME
) ||
482 (RRAssertsNonexistence(rr
, kDNSType_SOA
) && RRAssertsExistence(rr
, kDNSType_NS
))))
484 LogDNSSEC("NSECNameExists: NSEC %s comes from the parent, can't use it here",
485 RRDisplayString(m
, rr
));
489 // At this stage, we know that name is greater than the owner name and
490 // the nsec is not from the parent side.
492 // Compare with the next field in the nsec.
494 ret2
= DNSSECCanonicalOrder(name
, nxt
, &subdomain2
);
496 // Exact match with the nsec next name
499 LogDNSSEC("NSECNameExists: name %##s is same as nxt name %##s", name
->c
, nxt
->c
);
503 ret3
= DNSSECCanonicalOrder(oname
, nxt
, &subdomain3
);
507 // Pathological case of a single name in the domain. This means only the
508 // apex of the zone itself exists. Nothing below it. "subdomain2" indicates
509 // that name is a subdmain of "next" and hence below the zone.
512 LogDNSSEC("NSECNameExists: owner name %##s subdomain of nxt name %##s", oname
->c
, nxt
->c
);
517 LogDNSSEC("NSECNameExists: Single name in zone, owner name %##s is same as nxt name %##s", oname
->c
, nxt
->c
);
524 // Regular NSEC in the zone. Make sure that the "name" lies within
525 // oname and next. oname < name and name < next
526 if (ret1
< 0 && ret2
< 0)
528 LogDNSSEC("NSECNameExists: Normal NSEC name %##s lies within owner %##s and nxt name %##s",
529 name
->c
, oname
->c
, nxt
->c
);
534 LogDNSSEC("NSECNameExists: Normal NSEC name %##s does not lie within owner %##s and nxt name %##s",
535 name
->c
, oname
->c
, nxt
->c
);
541 // Last NSEC in the zone. The "next" is pointing to the apex. All names
542 // should be a subdomain of that and the name should be bigger than
544 if (ret1
< 0 && subdomain2
)
546 LogDNSSEC("NSECNameExists: Last NSEC name %##s lies within owner %##s and nxt name %##s",
547 name
->c
, oname
->c
, nxt
->c
);
552 LogDNSSEC("NSECNameExists: Last NSEC name %##s does not lie within owner %##s and nxt name %##s",
553 name
->c
, oname
->c
, nxt
->c
);
558 LogDNSSEC("NSECNameExists: NSEC %s did not match any case", RRDisplayString(m
, rr
));
562 // If the answer was result of a wildcard match, then this function proves
563 // that a proper wildcard was used to answer the question and that the
564 // original name does not exist
565 mDNSexport
void WildcardAnswerProof(mDNS
*const m
, DNSSECVerifier
*dv
)
569 const domainname
*ce
;
572 LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv
->origName
.c
, DNSTypeName(dv
->origType
));
574 // RFC 4035: Section 3.1.3.3
576 // 1) We used a wildcard because the qname does not exist, so verify
577 // that the qname does not exist
579 // 2) Is the wildcard the right one ?
581 // Unfortunately, this is not well explained in that section. Refer to
582 // RFC 5155 section 7.2.6.
584 // Walk the list of nsecs we received and see if they prove that
585 // the name does not exist
587 mDNSPlatformMemZero(&q
, sizeof(DNSQuestion
));
588 q
.ThisQInterval
= -1;
589 InitializeQuestion(m
, &q
, dv
->InterfaceID
, &dv
->origName
, dv
->origType
, mDNSNULL
, mDNSNULL
);
591 ncr
= NSECParentForQuestion(m
, &q
);
594 LogMsg("NSECWildCardProof: Can't find NSEC Parent for %##s (%s)", q
.qname
.c
, DNSTypeName(q
.qtype
));
600 if ((*rp
)->resrec
.rrtype
== kDNSType_NSEC
)
602 CacheRecord
*cr
= *rp
;
603 if (!NSECNameExists(m
, &cr
->resrec
, &dv
->origName
, dv
->origType
))
610 LogMsg("NSECWildCardProof: ERROR!! No NSECs found for %##s (%s)", q
.qname
.c
, DNSTypeName(q
.qtype
));
613 ce
= NSECClosestEncloser(&((*rp
)->resrec
), &dv
->origName
);
616 LogMsg("NSECWildCardProof: ERROR!! Closest Encloser NULL for %##s (%s)", q
.qname
.c
, DNSTypeName(q
.qtype
));
619 if (!SameDomainName(ce
, dv
->wildcardName
))
621 LogMsg("NSECWildCardProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q
.qname
.c
, dv
->wildcardName
->c
);
625 VerifyNSEC(m
, &((*rp
)->resrec
), mDNSNULL
, dv
, ncr
, mDNSNULL
);
628 dv
->DVCallback(m
, dv
, DNSSEC_Insecure
);
631 // We have a NSEC. Need to see if it proves that NODATA exists for the given name. Note that this
632 // function does not prove anything as proof may require more than one NSEC and this function
633 // processes only one NSEC at a time.
635 // Returns mDNSfalse if the NSEC does not prove the NODATA error
636 // Returns mDNStrue if the NSEC proves the NODATA error
638 mDNSlocal mDNSBool
NSECNoDataError(mDNS
*const m
, ResourceRecord
*rr
, domainname
*name
, mDNSu16 qtype
, domainname
**wildcard
)
640 const domainname
*oname
= rr
->name
; // owner name
642 if (wildcard
) *wildcard
= mDNSNULL
;
645 // section 3.1.3.1 : Name matches. Prove that the type does not exist and also CNAME is
646 // not set as in that case CNAME should have been returned ( CNAME part is mentioned in
647 // section 4.3 of dnssec-bis-updates.) Without the CNAME check, a positive response can
648 // be converted to a NODATA/NOERROR response.
650 // section 3.1.3.4 : No exact match for the name but there is a wildcard that could match
651 // the name but not the type. There are two NSECs in this case. One of them is a wildcard
652 // NSEC and another NSEC proving that the qname does not exist. We are called with one
653 // NSEC at a time. We return what we matched and the caller should decide whether all
654 // conditions are met for the proof.
655 if (SameDomainName(oname
, name
))
657 mDNSBool soa
= RRAssertsExistence(rr
, kDNSType_SOA
);
658 mDNSBool ns
= RRAssertsExistence(rr
, kDNSType_NS
);
659 if (qtype
!= kDNSType_DS
)
661 // For non-DS type questions, we don't want to use the parent side records to
665 LogDNSSEC("NSECNoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
666 RRDisplayString(m
, rr
), name
->c
, DNSTypeName(qtype
));
674 LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
675 RRDisplayString(m
, rr
), name
->c
, DNSTypeName(qtype
));
679 if (RRAssertsExistence(rr
, qtype
) || RRAssertsExistence(rr
, kDNSType_CNAME
))
681 LogMsg("NSECNoDataError: ERROR!! qtype %s exists in %s", DNSTypeName(qtype
), RRDisplayString(m
, rr
));
684 LogDNSSEC("NSECNoDataError: qype %s does not exist in %s", DNSTypeName(qtype
), RRDisplayString(m
, rr
));
689 // Name does not exist. Before we check for a wildcard match, make sure that
690 // this is not an ENT.
692 if (NSECAnswersENT(rr
, name
))
694 LogDNSSEC("NSECNoDataError: ERROR!! name %##s exists %s", name
->c
, RRDisplayString(m
, rr
));
698 // Wildcard check. If this is a wildcard NSEC, then check to see if we could
699 // have answered the question using this wildcard and it should not have the
700 // "qtype" passed in with its bitmap.
702 // See RFC 4592, on how wildcards are used to synthesize answers. Find the
703 // closest encloser and the qname should be a subdomain i.e if the wildcard
704 // is *.x.example, x.example is the closest encloser and the qname should be
705 // a subdomain e.g., y.x.example or z.y.x.example and so on.
706 if (oname
->c
[0] == 1 && oname
->c
[1] == '*')
709 const domainname
*ce
= SkipLeadingLabels(oname
, 1);
711 r
= DNSSECCanonicalOrder(name
, ce
, &s
);
714 if (RRAssertsExistence(rr
, qtype
) || RRAssertsExistence(rr
, kDNSType_CNAME
))
716 LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype
), RRDisplayString(m
, rr
));
719 // It is odd for a wildcard to match when we are looking up DS
721 if (qtype
== kDNSType_DS
)
723 LogMsg("NSECNoDataError: ERROR!! DS qtype exists in wildcard %s", RRDisplayString(m
, rr
));
726 // Don't use the parent side record for this
727 if (RRAssertsNonexistence(rr
, kDNSType_SOA
) &&
728 RRAssertsExistence(rr
, kDNSType_NS
))
730 LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)",
731 RRDisplayString(m
, rr
), name
->c
, DNSTypeName(qtype
));
734 *wildcard
= (domainname
*)ce
;
735 LogDNSSEC("NSECNoDataError: qtype %s does not exist in wildcard %s", DNSTypeName(qtype
), RRDisplayString(m
, rr
));
743 mDNSlocal
void NoDataNSECCallback(mDNS
*const m
, DNSSECVerifier
*dv
, DNSSECStatus status
)
749 LogDNSSEC("NoDataNSECCallback: called");
752 LogMsg("NoDataNSECCCallback: no parent DV");
753 FreeDNSSECVerifier(m
, dv
);
759 // Before we free the "dv", we need to update the
760 // parent with our AuthChain information
761 AuthChainLink(dv
->parent
, dv
->ac
);
763 dv
->actail
= &dv
->ac
;
767 if (status
!= DNSSEC_Secure
)
771 if (!(pdv
->flags
& NSEC_PROVES_NONAME_EXISTS
))
773 LogMsg("NoDataNSECCCallback: ERROR!! NSEC_PROVES_NONAME_EXISTS not set");
776 if (!(pdv
->flags
& WILDCARD_PROVES_NONAME_EXISTS
))
778 LogMsg("NoDataNSECCCallback: ERROR!! WILDCARD_PROVES_NONAME_EXISTS not set");
782 // We don't care about the "dv" that was allocated in VerifyNSEC.
783 // Get the original verifier and verify the other NSEC like we did
785 dv
->parent
= mDNSNULL
;
786 FreeDNSSECVerifier(m
, dv
);
788 ncr
= NSECParentForQuestion(m
, &pdv
->q
);
791 LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv
->q
.qname
.c
, DNSTypeName(pdv
->q
.qtype
));
795 rv
= pdv
->pendingNSEC
;
796 pdv
->pendingNSEC
= mDNSNULL
;
797 // Verify the pendingNSEC and we don't need to come back here. Let the regular
798 // NSECCallback call the original callback.
799 VerifyNSEC(m
, mDNSNULL
, rv
, pdv
, ncr
, mDNSNULL
);
803 dv
->parent
->DVCallback(m
, dv
->parent
, status
);
804 dv
->parent
= mDNSNULL
;
805 FreeDNSSECVerifier(m
, dv
);
808 mDNSlocal
void NameErrorNSECCallback(mDNS
*const m
, DNSSECVerifier
*dv
, DNSSECStatus status
)
814 LogDNSSEC("NameErrorNSECCallback: called");
817 LogMsg("NameErrorNSECCCallback: no parent DV");
818 FreeDNSSECVerifier(m
, dv
);
824 // Before we free the "dv", we need to update the
825 // parent with our AuthChain information
826 AuthChainLink(dv
->parent
, dv
->ac
);
828 dv
->actail
= &dv
->ac
;
832 if (status
!= DNSSEC_Secure
)
836 // We don't care about the "dv" that was allocated in VerifyNSEC.
837 // Get the original verifier and verify the other NSEC like we did
839 dv
->parent
= mDNSNULL
;
840 FreeDNSSECVerifier(m
, dv
);
842 ncr
= NSECParentForQuestion(m
, &pdv
->q
);
845 LogMsg("NameErrorNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv
->q
.qname
.c
, DNSTypeName(pdv
->q
.qtype
));
848 rv
= pdv
->pendingNSEC
;
849 pdv
->pendingNSEC
= mDNSNULL
;
850 // Verify the pendingNSEC and we don't need to come back here. Let the regular
851 // NSECCallback call the original callback.
852 VerifyNSEC(m
, mDNSNULL
, rv
, pdv
, ncr
, mDNSNULL
);
856 dv
->parent
->DVCallback(m
, dv
->parent
, status
);
857 dv
->parent
= mDNSNULL
;
858 FreeDNSSECVerifier(m
, dv
);
861 // We get a NODATA error with no records in answer section. This proves
862 // that qname does not exist.
863 mDNSlocal
void NoDataProof(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
)
866 domainname
*wildcard
= mDNSNULL
;
867 const domainname
*ce
= mDNSNULL
;
868 ResourceRecord
*nsec_wild
= mDNSNULL
;
869 ResourceRecord
*nsec_noname
= mDNSNULL
;
871 // NODATA Error could mean two things. The name exists with no type or there is a
872 // wildcard that matches the name but no type. This is done by NSECNoDataError.
874 // If it is the case of wildcard, there are two NSECs. One is the wildcard NSEC and
875 // the other NSEC to prove that there is no other closer match.
881 if ((*rp
)->resrec
.rrtype
== kDNSType_NSEC
)
883 CacheRecord
*cr
= *rp
;
884 if (NSECNoDataError(m
, &cr
->resrec
, &dv
->q
.qname
, dv
->q
.qtype
, &wildcard
))
888 dv
->flags
|= WILDCARD_PROVES_NONAME_EXISTS
;
889 LogDNSSEC("NoDataProof: NSEC %s proves NODATA error for %##s (%s)",
890 RRDisplayString(m
, &(*rp
)->resrec
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
894 dv
->flags
|= NSEC_PROVES_NOTYPE_EXISTS
;
895 LogDNSSEC("NoDataProof: NSEC %s proves NOTYPE error for %##s (%s)",
896 RRDisplayString(m
, &(*rp
)->resrec
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
898 nsec_wild
= &cr
->resrec
;
900 if (!NSECNameExists(m
, &cr
->resrec
, &dv
->q
.qname
, dv
->q
.qtype
))
902 LogDNSSEC("NoDataProof: NSEC %s proves that name %##s (%s) does not exist",
903 RRDisplayString(m
, &(*rp
)->resrec
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
904 // If we have a wildcard, then we should check to see if the closest
905 // encloser is the same as the wildcard.
906 ce
= NSECClosestEncloser(&cr
->resrec
, &dv
->q
.qname
);
907 dv
->flags
|= NSEC_PROVES_NONAME_EXISTS
;
908 nsec_noname
= &cr
->resrec
;
913 // If the type exists, then we have to verify just that NSEC
914 if (!(dv
->flags
& NSEC_PROVES_NOTYPE_EXISTS
))
916 // If we have a wildcard, then we should have a "ce" which matches the wildcard
917 // If we don't have a wildcard, then we should have proven that the name does not
918 // exist which means we would have set the "ce".
921 LogMsg("NoDataProof: Cannot prove that the name %##s (%s) does not exist", dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
924 if (wildcard
&& !SameDomainName(wildcard
, ce
))
926 LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard
->c
, ce
->c
);
931 if ((dv
->flags
& (WILDCARD_PROVES_NONAME_EXISTS
|NSEC_PROVES_NONAME_EXISTS
)) ==
932 (WILDCARD_PROVES_NONAME_EXISTS
|NSEC_PROVES_NONAME_EXISTS
))
935 RRVerifier
*r
= AllocateRRVerifier(nsec_noname
, &status
);
937 // First verify wildcard NSEC and then when we are done, we
938 // will verify the noname nsec
940 VerifyNSEC(m
, nsec_wild
, mDNSNULL
, dv
, ncr
, NoDataNSECCallback
);
942 else if ((dv
->flags
& WILDCARD_PROVES_NONAME_EXISTS
) ||
943 (dv
->flags
& NSEC_PROVES_NOTYPE_EXISTS
))
945 VerifyNSEC(m
, nsec_wild
, mDNSNULL
, dv
, ncr
, mDNSNULL
);
947 else if (dv
->flags
& NSEC_PROVES_NONAME_EXISTS
)
949 VerifyNSEC(m
, nsec_noname
, mDNSNULL
, dv
, ncr
, mDNSNULL
);
953 LogDNSSEC("NoDataProof: Error return");
954 dv
->DVCallback(m
, dv
, DNSSEC_Insecure
);
957 mDNSlocal mDNSBool
NSECNoWildcard(mDNS
*const m
, ResourceRecord
*rr
, domainname
*qname
, mDNSu16 qtype
)
959 const domainname
*ce
;
962 // If the query name is c.x.w.example and if the name does not exist, we should get
963 // get a nsec back that looks something like this:
965 // w.example NSEC a.w.example
967 // First, we need to get the closest encloser which in this case is w.example. Wild
968 // card synthesis works by finding the closest encloser first and then look for
969 // a "*" label (assuming * label does not appear in the question). If it does not
970 // exists, it would return the NSEC at that name. And the wildcard name at the
971 // closest encloser "*.w.example" would be covered by such an NSEC. (Appending "*"
972 // makes it bigger than w.example and "* is smaller than "a" for the above NSEC)
974 ce
= NSECClosestEncloser(rr
, qname
);
975 if (!ce
) { LogMsg("NSECNoWildcard: No closest encloser for rr %s, qname %##s (%s)", qname
->c
, DNSTypeName(qtype
)); return mDNSfalse
; }
980 if (!AppendDomainName(&wild
, ce
))
982 LogMsg("NSECNoWildcard: ERROR!! Can't append domainname closest encloser name %##s, qname %##s (%s)", ce
->c
, qname
->c
, DNSTypeName(qtype
));
985 if (NSECNameExists(m
, rr
, &wild
, qtype
) != 0)
987 LogDNSSEC("NSECNoWildcard: Wildcard name %##s exists or not valid qname %##s (%s)", wild
.c
, qname
->c
, DNSTypeName(qtype
));
990 LogDNSSEC("NSECNoWildcard: Wildcard name %##s does not exist for record %s, qname %##s (%s)", wild
.c
,
991 RRDisplayString(m
, rr
), qname
->c
, DNSTypeName(qtype
));
995 // We get a NXDOMAIN error with no records in answer section. This proves
996 // that qname does not exist.
997 mDNSlocal
void NameErrorProof(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
)
1000 ResourceRecord
*nsec_wild
= mDNSNULL
;
1001 ResourceRecord
*nsec_noname
= mDNSNULL
;
1004 // NXDOMAIN Error. We need to prove that the qname does not exist and there
1005 // is no wildcard that can be used to answer the question.
1010 if ((*rp
)->resrec
.rrtype
== kDNSType_NSEC
)
1012 CacheRecord
*cr
= *rp
;
1013 if (!NSECNameExists(m
, &cr
->resrec
, &dv
->q
.qname
, dv
->q
.qtype
))
1015 LogDNSSEC("NameErrorProof: NSEC %s proves name does not exist for %##s (%s)",
1016 RRDisplayString(m
, &(*rp
)->resrec
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
1017 // If we have a wildcard, then we should check to see if the closest
1018 // encloser is the same as the wildcard.
1019 dv
->flags
|= NSEC_PROVES_NONAME_EXISTS
;
1020 nsec_noname
= &cr
->resrec
;
1022 if (NSECNoWildcard(m
, &cr
->resrec
, &dv
->q
.qname
, dv
->q
.qtype
))
1024 dv
->flags
|= WILDCARD_PROVES_NONAME_EXISTS
;
1025 nsec_wild
= &cr
->resrec
;
1026 LogDNSSEC("NameErrorProof: NSEC %s proves wildcard cannot answer question for %##s (%s)",
1027 RRDisplayString(m
, &(*rp
)->resrec
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
1032 if (!nsec_noname
|| !nsec_wild
)
1034 LogMsg("NameErrorProof: Proof failed for %##s (%s) noname %p, wild %p", dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
), nsec_noname
, nsec_wild
);
1038 // First verify wildcard NSEC and then when we are done, we will verify the noname nsec.
1039 // Sometimes a single NSEC can prove both that the "qname" does not exist and a wildcard
1040 // could not have produced qname. These are a few examples where this can happen.
1042 // 1. If the zone is example.com and you look up *.example.com and if there are no wildcards,
1043 // you will get a NSEC back "example.com NSEC a.example.com". This proves that both the
1044 // name does not exist and *.example.com also does not exist
1046 // 2. If the zone is example.com and it has a record like this:
1048 // example.com NSEC d.example.com
1050 // any name you lookup in between like a.example.com,b.example.com etc. you will get a single
1051 // NSEC back. In that case we just have to verify only once.
1053 if (nsec_wild
!= nsec_noname
)
1055 RRVerifier
*r
= AllocateRRVerifier(nsec_noname
, &status
);
1057 dv
->pendingNSEC
= r
;
1058 VerifyNSEC(m
, nsec_wild
, mDNSNULL
, dv
, ncr
, NameErrorNSECCallback
);
1062 VerifyNSEC(m
, nsec_wild
, mDNSNULL
, dv
, ncr
, mDNSNULL
);
1066 dv
->DVCallback(m
, dv
, DNSSEC_Insecure
);
1069 mDNSexport
void ValidateWithNSECS(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*cr
)
1071 LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m
, cr
));
1072 // "parent" is set when we are validating a NSEC. In the process of validating that
1073 // nsec, we encountered another NSEC. For example, we are looking up the A record for
1074 // www.example.com, we got an NSEC at some stage. We come here to validate the NSEC
1075 // the first time. While validating the NSEC we remember the original validation result
1076 // in the parent. But while validating the NSEC, we got another NSEC back e.g., not
1077 // a secure delegation i.e., we got an NSEC proving that DS does not exist. We prove
1078 // that again. But if we receive more NSECs after this, we stop.
1082 if (dv
->parent
->parent
)
1084 LogMsg("ValidateWithNSECS: ERROR!! dv parent is set already");
1085 dv
->DVCallback(m
, dv
, DNSSEC_Indeterminate
);
1090 DNSSECVerifier
*pdv
= dv
;
1091 dv
= AllocateDNSSECVerifier(m
, &pdv
->q
.qname
, pdv
->q
.qtype
, pdv
->q
.InterfaceID
, VerifyNSECCallback
, mDNSNULL
);
1094 LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed");
1095 pdv
->DVCallback(m
, pdv
, DNSSEC_Indeterminate
);
1098 LogDNSSEC("ValidateWithNSECS: Parent set, Verifying dv %p %##s (%s)", dv
, pdv
->q
.qname
.c
, DNSTypeName(pdv
->q
.qtype
));
1102 if (cr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
)
1104 CacheRecord
*neg
= cr
->nsec
;
1107 LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m
, neg
));
1111 if (cr
->rcode
== kDNSFlag1_RC_NoErr
)
1113 NoDataProof(m
, dv
, cr
);
1115 else if (cr
->rcode
== kDNSFlag1_RC_NXDomain
)
1117 NameErrorProof(m
, dv
, cr
);
1121 LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", cr
->rcode
);
1122 dv
->DVCallback(m
, dv
, DNSSEC_Insecure
);
1127 LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m
, cr
));
1128 dv
->DVCallback(m
, dv
, DNSSEC_Insecure
);