1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2011-2013 Apple 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 // nsec3.c: This file contains support functions to validate NSEC3 records for
20 // NODATA and NXDOMAIN error.
21 // ***************************************************************************
23 #include "mDNSEmbeddedAPI.h"
24 #include "DNSCommon.h"
25 #include "CryptoAlg.h"
29 // Define DNSSEC_DISABLED to remove all the DNSSEC functionality
30 // and use the stub functions implemented later in this file.
32 #ifndef DNSSEC_DISABLED
41 //#define NSEC3_DEBUG 1
44 mDNSlocal
void PrintHash(mDNSu8
*digest
, int digestlen
, char *buffer
, int buflen
)
47 for (int j
= 0; j
< digestlen
; j
++)
49 length
+= mDNS_snprintf(buffer
+length
, buflen
-length
-1, "%x", digest
[j
]);
54 mDNSlocal mDNSBool
NSEC3OptOut(CacheRecord
*cr
)
56 const RDataBody2
*const rdb
= (RDataBody2
*)cr
->resrec
.rdata
->u
.data
;
57 rdataNSEC3
*nsec3
= (rdataNSEC3
*)rdb
->data
;
58 return (nsec3
->flags
& NSEC3_FLAGS_OPTOUT
);
61 mDNSlocal
int NSEC3SameName(const mDNSu8
*name
, int namelen
, const mDNSu8
*nsecName
, int nsecLen
)
65 // Note: With NSEC3, the lengths should always be same.
66 if (namelen
!= nsecLen
)
68 LogMsg("NSEC3SameName: ERROR!! namelen %d, nsecLen %d", namelen
, nsecLen
);
69 return ((namelen
< nsecLen
) ? -1 : 1);
72 for (i
= 0; i
< namelen
; i
++)
75 mDNSu8 bc
= *nsecName
++;
76 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
77 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
80 verbosedebugf("NSEC3SameName: returning ac %c, bc %c", ac
, bc
);
81 return ((ac
< bc
) ? -1 : 1);
87 // Does the NSEC3 in "ncr" covers the "name" ?
88 // hashName is hash of the "name" and b32Name is the base32 encoded equivalent.
89 mDNSlocal mDNSBool
NSEC3CoversName(mDNS
*const m
, CacheRecord
*ncr
, const mDNSu8
*hashName
, int hashLen
, const mDNSu8
*b32Name
,
95 const mDNSu8 b32nxtname
[NSEC3_MAX_B32_LEN
+1];
98 NSEC3Parse(&ncr
->resrec
, mDNSNULL
, &nxtLength
, &nxtName
, mDNSNULL
, mDNSNULL
);
100 if (nxtLength
!= hashLen
|| ncr
->resrec
.name
->c
[0] != b32len
)
103 // Compare the owner names and the "nxt" names.
105 // Owner name is base32 encoded and hence use the base32 encoded name b32name.
106 // nxt name is binary and hence use the binary value in hashName.
107 ret1
= NSEC3SameName(&ncr
->resrec
.name
->c
[1], ncr
->resrec
.name
->c
[0], b32Name
, b32len
);
108 ret2
= DNSMemCmp(nxtName
, hashName
, hashLen
);
115 PrintHash(nxtName
, nxtLength
, nxtbuf1
, sizeof(nxtbuf1
));
116 PrintHash((mDNSu8
*)hashName
, hashLen
, nxtbuf2
, sizeof(nxtbuf2
));
117 LogMsg("NSEC3CoversName: Owner name %s, name %s", &ncr
->resrec
.name
->c
[1], b32Name
);
118 LogMsg("NSEC3CoversName: Nxt hash name %s, name %s", nxtbuf1
, nxtbuf2
);
122 // "name" is greater than the owner name and smaller than nxtName. This also implies
123 // that nxtName > owner name implying that it is normal NSEC3.
124 if (ret1
< 0 && ret2
> 0)
126 LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Normal)", CRDisplayString(m
, ncr
), b32Name
);
129 // Need to compare the owner name and "nxt" to see if this is the last
130 // NSEC3 in the zone. Only the owner name is in base32 and hence we need to
131 // convert the nxtName to base32.
132 b32nxtlen
= baseEncode((char *)b32nxtname
, sizeof(b32nxtname
), nxtName
, nxtLength
, ENC_BASE32
);
135 LogDNSSEC("NSEC3CoversName: baseEncode of nxtName of %s failed", CRDisplayString(m
, ncr
));
138 if (b32len
!= b32nxtlen
)
140 LogDNSSEC("NSEC3CoversName: baseEncode of nxtName for %s resulted in wrong length b32nxtlen %d, b32len %d",
141 CRDisplayString(m
, ncr
), b32len
, b32nxtlen
);
144 LogDNSSEC("NSEC3CoversName: Owner name %s, b32nxtname %s, ret1 %d, ret2 %d", &ncr
->resrec
.name
->c
[1], b32nxtname
, ret1
, ret2
);
146 // If it is the last NSEC3 in the zone nxt < "name" and NSEC3SameName returns -1.
148 // - ret1 < 0 means "name > owner"
149 // - ret2 > 0 means "name < nxt"
151 // Note: We also handle the case of only NSEC3 in the zone where NSEC3SameName returns zero.
152 ret
= NSEC3SameName(b32nxtname
, b32nxtlen
, &ncr
->resrec
.name
->c
[1], ncr
->resrec
.name
->c
[0]);
154 (ret1
< 0 || ret2
> 0))
156 LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Last), ret1 %d, ret2 %d", CRDisplayString(m
, ncr
), b32Name
, ret1
, ret2
);
163 // This function can be called with NSEC3ClosestEncloser, NSEC3Covers and NSEC3CEProof
165 // Passing in NSEC3ClosestEncloser means "find an exact match for the origName".
166 // Passing in NSEC3Covers means "find an NSEC3 that covers the origName".
168 // i.e., in both cases the nsec3 records are iterated to find the best match and returned.
169 // With NSEC3ClosestEncloser, as we are just looking for a name match, extra checks for
170 // the types being present or absent will not be checked.
172 // If NSEC3CEProof is passed, the name is tried as such first by iterating through all NSEC3s
173 // finding a ClosestEncloser or CloserEncloser and then one label skipped from the left and
174 // retried again till both the closest and closer encloser is found.
176 // ncr is the negative cache record that has the NSEC3 chain
177 // origName is the name for which we are trying to find the ClosestEncloser etc.
178 // closestEncloser and closerEncloser are the return values of the function
179 // ce is the closest encloser that will be returned if we find one
180 mDNSlocal mDNSBool
NSEC3Find(mDNS
*const m
, NSEC3FindValues val
, CacheRecord
*ncr
, domainname
*origName
, CacheRecord
**closestEncloser
,
181 CacheRecord
**closerEncloser
, const domainname
**ce
, mDNSu16 qtype
)
184 int labelCount
= CountLabels(origName
);
188 (void) qtype
; // unused
189 // Pick the first NSEC for the iterations, salt etc.
190 for (cr
= ncr
->nsec
; cr
; cr
= cr
->next
)
192 if (cr
->resrec
.rrtype
== kDNSType_NSEC3
)
194 const RDataBody2
*const rdb
= (RDataBody2
*)cr
->resrec
.rdata
->u
.data
;
195 nsec3
= (rdataNSEC3
*)rdb
->data
;
201 LogMsg("NSEC3Find: cr NULL");
205 // Note: The steps defined in this function are for "NSEC3CEProof". As part of NSEC3CEProof,
206 // we need to find both the closestEncloser and closerEncloser which can also be found
207 // by passing NSEC3ClosestEncloser and NSEC3Covers respectively.
209 // Section 8.3 of RFC 5155.
210 // 1. Set SNAME=QNAME. Clear the flag.
212 // closerEncloser is the "flag". "name" below is SNAME.
217 *closestEncloser
= mDNSNULL
;
220 *closerEncloser
= mDNSNULL
;
222 // If we are looking for a closestEncloser or a covering NSEC3, we don't have
223 // to truncate the name. For the give name, try to find the closest or closer
225 if (val
!= NSEC3CEProof
)
230 for (i
= 0; i
< labelCount
+ 1; i
++)
233 const mDNSu8 hashName
[NSEC3_MAX_HASH_LEN
];
234 const domainname
*name
;
235 const mDNSu8 b32Name
[NSEC3_MAX_B32_LEN
+1];
238 name
= SkipLeadingLabels(origName
, i
);
239 if (!NSEC3HashName(name
, nsec3
, mDNSNULL
, 0, hashName
, &hlen
))
241 LogMsg("NSEC3Find: NSEC3HashName failed for %##s", name
->c
);
245 b32len
= baseEncode((char *)b32Name
, sizeof(b32Name
), (mDNSu8
*)hashName
, hlen
, ENC_BASE32
);
248 LogMsg("NSEC3Find: baseEncode of name %##s failed", name
->c
);
253 for (cr
= ncr
->nsec
; cr
; cr
= cr
->next
)
255 const domainname
*nsecZone
;
256 int result
, subdomain
;
258 if (cr
->resrec
.rrtype
!= kDNSType_NSEC3
)
261 nsecZone
= SkipLeadingLabels(cr
->resrec
.name
, 1);
264 LogMsg("NSEC3Find: SkipLeadingLabel failed for %s, current name %##s",
265 CRDisplayString(m
, cr
), name
->c
);
269 // NSEC3 owner names are formed by hashing the owner name and then appending
270 // the zone name to it. If we skip the first label, the rest should be
271 // the zone name. See whether it is the subdomain of the name we are looking
273 result
= DNSSECCanonicalOrder(origName
, nsecZone
, &subdomain
);
275 // The check can't be a strict subdomain check. When NSEC3ClosestEncloser is
276 // passed in, there can be an exact match. If it is a subdomain or an exact
277 // match, we should continue with the proof.
278 if (!(subdomain
|| !result
))
280 LogMsg("NSEC3Find: NSEC3 %s not a subdomain of %##s, result %d", CRDisplayString(m
, cr
),
281 origName
->c
, result
);
285 // 2.1) If there is no NSEC3 RR in the response that matches SNAME
286 // (i.e., an NSEC3 RR whose owner name is the same as the hash of
287 // SNAME, prepended as a single label to the zone name), clear
290 // Note: We don't try to determine the actual zone name. We know that
291 // the labels following the hash (nsecZone) is the ancestor and we don't
292 // know where the zone cut is. Hence, we verify just the hash to be
295 if (val
== NSEC3ClosestEncloser
|| val
== NSEC3CEProof
)
297 if (!NSEC3SameName(&cr
->resrec
.name
->c
[1], cr
->resrec
.name
->c
[0], (const mDNSu8
*)b32Name
, b32len
))
302 // For NSEC3ClosestEncloser, we are finding an exact match and
303 // "type" specific checks should be done by the caller.
304 if (val
!= NSEC3ClosestEncloser
)
306 // DNAME bit must not be set and NS bit may be set only if SOA bit is set
307 NSEC3Parse(&cr
->resrec
, mDNSNULL
, mDNSNULL
, mDNSNULL
, &bmaplen
, &bmap
);
308 if (BitmapTypeCheck(bmap
, bmaplen
, kDNSType_DNAME
))
310 LogDNSSEC("NSEC3Find: DNAME bit set in %s, ignoring", CRDisplayString(m
, cr
));
313 // This is the closest encloser and should come from the right zone.
314 if (BitmapTypeCheck(bmap
, bmaplen
, kDNSType_NS
) &&
315 !BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
))
317 LogDNSSEC("NSEC3Find: NS bit set without SOA bit in %s, ignoring", CRDisplayString(m
, cr
));
322 LogDNSSEC("NSEC3Find: ClosestEncloser %s found for name %##s", CRDisplayString(m
, cr
), name
->c
);
326 *closestEncloser
= cr
;
328 if (val
== NSEC3ClosestEncloser
)
335 if ((val
== NSEC3Covers
|| val
== NSEC3CEProof
) && (!closerEncloser
|| !(*closerEncloser
)))
337 if (NSEC3CoversName(m
, cr
, hashName
, hlen
, b32Name
, b32len
))
339 // 2.2) If there is an NSEC3 RR in the response that covers SNAME, set the flag.
341 *closerEncloser
= cr
;
342 if (val
== NSEC3Covers
)
349 // 2.3) If there is a matching NSEC3 RR in the response and the flag
350 // was set, then the proof is complete, and SNAME is the closest
352 if (val
== NSEC3CEProof
&& closestEncloser
&& *closestEncloser
)
354 if (closerEncloser
&& *closerEncloser
)
356 LogDNSSEC("NSEC3Find: Found closest and closer encloser");
361 // 2.4) If there is a matching NSEC3 RR in the response, but the flag
362 // is not set, then the response is bogus.
364 // Note: We don't have to wait till we finish trying all the names. If the matchName
365 // happens, we found the closest encloser which means we should have found the closer
368 LogDNSSEC("NSEC3Find: Found closest, but not closer encloser");
372 // 3. Truncate SNAME by one label from the left, go to step 2.
374 LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName
->c
, DNSTypeName(qtype
));
378 mDNSlocal mDNSBool
NSEC3ClosestEncloserProof(mDNS
*const m
, CacheRecord
*ncr
, domainname
*name
, CacheRecord
**closestEncloser
, CacheRecord
**closerEncloser
,
379 const domainname
**ce
, mDNSu16 qtype
)
381 if (!NSEC3Find(m
, NSEC3CEProof
, ncr
, name
, closestEncloser
, closerEncloser
, ce
, qtype
))
383 LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof");
387 // Note: It is possible that closestEncloser and closerEncloser are the same.
388 if (!closestEncloser
|| !closerEncloser
|| !ce
)
390 LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", closestEncloser
, closerEncloser
, ce
);
394 // If the name exists, we should not have gotten the name error
395 if (SameDomainName((*ce
), name
))
397 LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m
, *closestEncloser
),
404 mDNSlocal mDNSBool
VerifyNSEC3(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
, CacheRecord
*closestEncloser
,
405 CacheRecord
*closerEncloser
, CacheRecord
*wildcard
, DNSSECVerifierCallback callback
)
410 // We have three NSEC3s. If any of two are same, we should just prove one of them.
411 // This is just not an optimization; DNSSECNegativeValidationCB does not handle
412 // identical NSEC3s very well.
414 if (closestEncloser
== closerEncloser
)
415 closestEncloser
= mDNSNULL
;
416 if (closerEncloser
== wildcard
)
417 closerEncloser
= mDNSNULL
;
418 if (closestEncloser
== wildcard
)
419 closestEncloser
= mDNSNULL
;
421 dv
->pendingNSEC
= mDNSNULL
;
424 r
= AllocateRRVerifier(&closestEncloser
->resrec
, &status
);
427 r
->next
= dv
->pendingNSEC
;
432 r
= AllocateRRVerifier(&closerEncloser
->resrec
, &status
);
435 r
->next
= dv
->pendingNSEC
;
440 r
= AllocateRRVerifier(&wildcard
->resrec
, &status
);
443 r
->next
= dv
->pendingNSEC
;
446 if (!dv
->pendingNSEC
)
448 LogMsg("VerifyNSEC3: ERROR!! pending NSEC null");
452 dv
->pendingNSEC
= r
->next
;
455 LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r
->name
.c
, DNSTypeName(r
->rrtype
));
456 if (!dv
->pendingNSEC
)
457 VerifyNSEC(m
, mDNSNULL
, r
, dv
, ncr
, mDNSNULL
);
459 VerifyNSEC(m
, mDNSNULL
, r
, dv
, ncr
, callback
);
463 mDNSexport
void NSEC3NameErrorProof(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
)
465 CacheRecord
*closerEncloser
;
466 CacheRecord
*closestEncloser
;
467 CacheRecord
*wildcard
;
468 const domainname
*ce
= mDNSNULL
;
471 if (!NSEC3ClosestEncloserProof(m
, ncr
, &dv
->q
.qname
, &closestEncloser
, &closerEncloser
, &ce
, dv
->q
.qtype
))
475 LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m
, closestEncloser
), ce
->c
);
476 LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m
, closerEncloser
));
478 // *.closestEncloser should be covered by some nsec3 which would then prove
479 // that the wildcard does not exist
483 if (!AppendDomainName(&wild
, ce
))
485 LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce
->c
);
488 if (!NSEC3Find(m
, NSEC3Covers
, ncr
, &wild
, mDNSNULL
, &wildcard
, mDNSNULL
, dv
->q
.qtype
))
490 LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard");
495 LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild
.c
, CRDisplayString(m
, wildcard
));
496 if (wildcard
== closestEncloser
)
498 LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m
, wildcard
));
501 if (NSEC3OptOut(closerEncloser
))
503 dv
->flags
|= NSEC3_OPT_OUT
;
505 if (!VerifyNSEC3(m
, dv
, ncr
, closestEncloser
, closerEncloser
, wildcard
, NameErrorNSECCallback
))
511 dv
->DVCallback(m
, dv
, DNSSEC_Bogus
);
514 // Section 8.5, 8.6 of RFC 5155 first paragraph
515 mDNSlocal mDNSBool
NSEC3NoDataError(mDNS
*const m
, CacheRecord
*ncr
, domainname
*name
, mDNSu16 qtype
, CacheRecord
**closestEncloser
)
517 const domainname
*ce
= mDNSNULL
;
519 *closestEncloser
= mDNSNULL
;
520 // Note: This also covers ENT in which case the bitmap is empty
521 if (NSEC3Find(m
, NSEC3ClosestEncloser
, ncr
, name
, closestEncloser
, mDNSNULL
, &ce
, qtype
))
527 NSEC3Parse(&(*closestEncloser
)->resrec
, mDNSNULL
, mDNSNULL
, mDNSNULL
, &bmaplen
, &bmap
);
528 if (BitmapTypeCheck(bmap
, bmaplen
, qtype
) || BitmapTypeCheck(bmap
, bmaplen
, kDNSType_CNAME
))
530 LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype
), CRDisplayString(m
, *closestEncloser
));
533 ns
= BitmapTypeCheck(bmap
, bmaplen
, kDNSType_NS
);
534 soa
= BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
);
535 if (qtype
!= kDNSType_DS
)
537 // For non-DS type questions, we don't want to use the parent side records to
541 LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
542 CRDisplayString(m
, *closestEncloser
), name
->c
, DNSTypeName(qtype
));
550 LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
551 CRDisplayString(m
, *closestEncloser
), name
->c
, DNSTypeName(qtype
));
555 LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name
->c
, DNSTypeName(qtype
), CRDisplayString(m
, *closestEncloser
));
561 mDNSexport
void NSEC3NoDataProof(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
)
563 CacheRecord
*closerEncloser
= mDNSNULL
;
564 CacheRecord
*closestEncloser
= mDNSNULL
;
565 CacheRecord
*wildcard
= mDNSNULL
;
566 const domainname
*ce
= mDNSNULL
;
569 // Section 8.5, 8.6 of RFC 5155
570 if (NSEC3NoDataError(m
, ncr
, &dv
->q
.qname
, dv
->q
.qtype
, &closestEncloser
))
574 // Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof
575 // for QNAME and the "next closer" should have the opt out
576 if (!NSEC3ClosestEncloserProof(m
, ncr
, &dv
->q
.qname
, &closestEncloser
, &closerEncloser
, &ce
, dv
->q
.qtype
))
581 // Section 8.7: find a matching NSEC3 for *.closestEncloser
585 if (!AppendDomainName(&wild
, ce
))
587 LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce
->c
);
590 if (!NSEC3Find(m
, NSEC3ClosestEncloser
, ncr
, &wild
, &wildcard
, mDNSNULL
, &ce
, dv
->q
.qtype
))
592 // Not a wild card case. Section 8.6 second para applies.
593 LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case");
594 if (!NSEC3OptOut(closerEncloser
))
596 LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
599 LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
600 dv
->flags
|= NSEC3_OPT_OUT
;
606 NSEC3Parse(&wildcard
->resrec
, mDNSNULL
, mDNSNULL
, mDNSNULL
, &bmaplen
, &bmap
);
607 if (BitmapTypeCheck(bmap
, bmaplen
, dv
->q
.qtype
) || BitmapTypeCheck(bmap
, bmaplen
, kDNSType_CNAME
))
609 LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv
->q
.qtype
), CRDisplayString(m
, wildcard
));
612 if (dv
->q
.qtype
== kDNSType_DS
&& BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
))
614 LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)",
615 CRDisplayString(m
, wildcard
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
618 else if (dv
->q
.qtype
!= kDNSType_DS
&& !BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
) &&
619 BitmapTypeCheck(bmap
, bmaplen
, kDNSType_NS
))
621 // Don't use the parent side record for this
622 LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)",
623 CRDisplayString(m
, wildcard
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
626 LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild
.c
, CRDisplayString(m
, wildcard
));
630 if (!VerifyNSEC3(m
, dv
, ncr
, closestEncloser
, closerEncloser
, wildcard
, NoDataNSECCallback
))
635 dv
->DVCallback(m
, dv
, DNSSEC_Bogus
);
638 mDNSexport mDNSBool
NSEC3WildcardAnswerProof(mDNS
*const m
, CacheRecord
*ncr
, DNSSECVerifier
*dv
)
641 const domainname
*nc
;
642 CacheRecord
*closerEncloser
;
646 // Find the next closer name and prove that it is covered by the NSEC3
647 skip
= CountLabels(&dv
->origName
) - CountLabels(dv
->wildcardName
) - 1;
649 nc
= SkipLeadingLabels(&dv
->origName
, skip
);
653 LogDNSSEC("NSEC3WildcardAnswerProof: wildcard name %##s", nc
->c
);
655 if (!NSEC3Find(m
, NSEC3Covers
, ncr
, (domainname
*)nc
, mDNSNULL
, &closerEncloser
, mDNSNULL
, dv
->q
.qtype
))
657 LogMsg("NSEC3WildcardAnswerProof: Cannot find closer encloser");
662 LogMsg("NSEC3WildcardAnswerProof: closerEncloser NULL");
665 if (NSEC3OptOut(closerEncloser
))
667 dv
->flags
|= NSEC3_OPT_OUT
;
669 // NSEC3 Verification is done by the caller
673 mDNSexport CacheRecord
*NSEC3RecordIsDelegation(mDNS
*const m
, domainname
*name
, mDNSu16 qtype
)
680 namehash
= DomainNameHashValue(name
);
682 cg
= CacheGroupForName(m
, namehash
, name
);
685 LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name
);
688 for (ncr
= cg
->members
; ncr
; ncr
= ncr
->next
)
690 if (ncr
->resrec
.RecordType
!= kDNSRecordTypePacketNegative
||
691 ncr
->resrec
.rrtype
!= qtype
)
695 for (cr
= ncr
->nsec
; cr
; cr
= cr
->next
)
698 const mDNSu8 hashName
[NSEC3_MAX_HASH_LEN
];
699 const mDNSu8 b32Name
[NSEC3_MAX_B32_LEN
+1];
700 const RDataBody2
*const rdb
= (RDataBody2
*)cr
->resrec
.rdata
->u
.data
;
703 if (cr
->resrec
.rrtype
!= kDNSType_NSEC3
)
706 nsec3
= (rdataNSEC3
*)rdb
->data
;
708 if (!NSEC3HashName(name
, nsec3
, mDNSNULL
, 0, hashName
, &hlen
))
710 LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for %##s", name
->c
);
714 b32len
= baseEncode((char *)b32Name
, sizeof(b32Name
), (mDNSu8
*)hashName
, hlen
, ENC_BASE32
);
717 LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name
->c
);
720 // Section 2.3 of RFC 4035 states that:
722 // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
723 // have an NSEC resource record.
725 // This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the
726 // NS bit set, then this is a delegation.
728 if (!NSEC3SameName(&cr
->resrec
.name
->c
[1], cr
->resrec
.name
->c
[0], (const mDNSu8
*)b32Name
, b32len
))
733 LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m
, cr
), name
->c
, b32Name
);
734 NSEC3Parse(&cr
->resrec
, mDNSNULL
, mDNSNULL
, mDNSNULL
, &bmaplen
, &bmap
);
736 // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
738 if (BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
) ||
739 BitmapTypeCheck(bmap
, bmaplen
, kDNSType_DS
))
741 LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m
, cr
));
744 if (BitmapTypeCheck(bmap
, bmaplen
, kDNSType_NS
))
749 // If opt-out is not set, then it does not cover any delegations
750 if (!(nsec3
->flags
& NSEC3_FLAGS_OPTOUT
))
752 // Opt-out allows insecure delegations to exist without the NSEC3 RR at the
753 // hashed owner name (see RFC 5155 section 6.0).
754 if (NSEC3CoversName(m
, cr
, hashName
, hlen
, b32Name
, b32len
))
756 LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m
, cr
), name
->c
);
764 #else // !DNSSEC_DISABLED
766 #endif // !DNSSEC_DISABLED