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 // 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
))
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
)
354 if (*closestEncloser
&& *closerEncloser
)
356 LogDNSSEC("NSEC3Find: Found closest and closer encloser");
360 // 2.4) If there is a matching NSEC3 RR in the response, but the flag
361 // is not set, then the response is bogus.
363 // Note: We don't have to wait till we finish trying all the names. If the matchName
364 // happens, we found the closest encloser which means we should have found the closer
367 if (*closestEncloser
&& !(*closerEncloser
))
369 LogDNSSEC("NSEC3Find: Found closest, but not closer encloser");
373 // 3. Truncate SNAME by one label from the left, go to step 2.
375 LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName
->c
, DNSTypeName(qtype
));
379 mDNSlocal mDNSBool
NSEC3ClosestEncloserProof(mDNS
*const m
, CacheRecord
*ncr
, domainname
*name
, CacheRecord
**closestEncloser
, CacheRecord
**closerEncloser
,
380 const domainname
**ce
, mDNSu16 qtype
)
382 if (!NSEC3Find(m
, NSEC3CEProof
, ncr
, name
, closestEncloser
, closerEncloser
, ce
, qtype
))
384 LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof");
388 // Note: It is possible that closestEncloser and closerEncloser are the same.
389 if (!closestEncloser
|| !closerEncloser
|| !ce
)
391 LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", *closestEncloser
,
392 *closerEncloser
, *ce
);
396 // If the name exists, we should not have gotten the name error
397 if (SameDomainName((*ce
), name
))
399 LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m
, *closestEncloser
),
406 mDNSlocal mDNSBool
VerifyNSEC3(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
, CacheRecord
*closestEncloser
,
407 CacheRecord
*closerEncloser
, CacheRecord
*wildcard
, DNSSECVerifierCallback callback
)
412 // We have three NSEC3s. If any of two are same, we should just prove one of them.
413 // This is just not an optimization; DNSSECNegativeValidationCB does not handle
414 // identical NSEC3s very well.
416 if (closestEncloser
== closerEncloser
)
417 closestEncloser
= mDNSNULL
;
418 if (closerEncloser
== wildcard
)
419 closerEncloser
= mDNSNULL
;
420 if (closestEncloser
== wildcard
)
421 closestEncloser
= mDNSNULL
;
423 dv
->pendingNSEC
= mDNSNULL
;
426 r
= AllocateRRVerifier(&closestEncloser
->resrec
, &status
);
429 r
->next
= dv
->pendingNSEC
;
434 r
= AllocateRRVerifier(&closerEncloser
->resrec
, &status
);
437 r
->next
= dv
->pendingNSEC
;
442 r
= AllocateRRVerifier(&wildcard
->resrec
, &status
);
445 r
->next
= dv
->pendingNSEC
;
448 if (!dv
->pendingNSEC
)
450 LogMsg("VerifyNSEC3: ERROR!! pending NSEC null");
454 dv
->pendingNSEC
= r
->next
;
457 LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r
->name
.c
, DNSTypeName(r
->rrtype
));
458 if (!dv
->pendingNSEC
)
459 VerifyNSEC(m
, mDNSNULL
, r
, dv
, ncr
, mDNSNULL
);
461 VerifyNSEC(m
, mDNSNULL
, r
, dv
, ncr
, callback
);
465 mDNSexport
void NSEC3NameErrorProof(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
)
467 CacheRecord
*closerEncloser
;
468 CacheRecord
*closestEncloser
;
469 CacheRecord
*wildcard
;
470 const domainname
*ce
= mDNSNULL
;
473 if (!NSEC3ClosestEncloserProof(m
, ncr
, &dv
->q
.qname
, &closestEncloser
, &closerEncloser
, &ce
, dv
->q
.qtype
))
477 LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m
, closestEncloser
), ce
->c
);
478 LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m
, closerEncloser
));
480 // *.closestEncloser should be covered by some nsec3 which would then prove
481 // that the wildcard does not exist
485 if (!AppendDomainName(&wild
, ce
))
487 LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce
->c
);
490 if (!NSEC3Find(m
, NSEC3Covers
, ncr
, &wild
, mDNSNULL
, &wildcard
, mDNSNULL
, dv
->q
.qtype
))
492 LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard");
497 LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild
.c
, CRDisplayString(m
, wildcard
));
498 if (wildcard
== closestEncloser
)
500 LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m
, wildcard
));
503 if (NSEC3OptOut(closerEncloser
))
505 dv
->flags
|= NSEC3_OPT_OUT
;
507 if (!VerifyNSEC3(m
, dv
, ncr
, closestEncloser
, closerEncloser
, wildcard
, NameErrorNSECCallback
))
513 dv
->DVCallback(m
, dv
, DNSSEC_Bogus
);
516 // Section 8.5, 8.6 of RFC 5155 first paragraph
517 mDNSlocal mDNSBool
NSEC3NoDataError(mDNS
*const m
, CacheRecord
*ncr
, domainname
*name
, mDNSu16 qtype
, CacheRecord
**closestEncloser
)
519 const domainname
*ce
= mDNSNULL
;
521 *closestEncloser
= mDNSNULL
;
522 // Note: This also covers ENT in which case the bitmap is empty
523 if (NSEC3Find(m
, NSEC3ClosestEncloser
, ncr
, name
, closestEncloser
, mDNSNULL
, &ce
, qtype
))
529 NSEC3Parse(&(*closestEncloser
)->resrec
, mDNSNULL
, mDNSNULL
, mDNSNULL
, &bmaplen
, &bmap
);
530 if (BitmapTypeCheck(bmap
, bmaplen
, qtype
) || BitmapTypeCheck(bmap
, bmaplen
, kDNSType_CNAME
))
532 LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype
), CRDisplayString(m
, *closestEncloser
));
535 ns
= BitmapTypeCheck(bmap
, bmaplen
, kDNSType_NS
);
536 soa
= BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
);
537 if (qtype
!= kDNSType_DS
)
539 // For non-DS type questions, we don't want to use the parent side records to
543 LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)",
544 CRDisplayString(m
, *closestEncloser
), name
->c
, DNSTypeName(qtype
));
552 LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)",
553 CRDisplayString(m
, *closestEncloser
), name
->c
, DNSTypeName(qtype
));
557 LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name
->c
, DNSTypeName(qtype
), CRDisplayString(m
, *closestEncloser
));
563 mDNSexport
void NSEC3NoDataProof(mDNS
*const m
, DNSSECVerifier
*dv
, CacheRecord
*ncr
)
565 CacheRecord
*closerEncloser
= mDNSNULL
;
566 CacheRecord
*closestEncloser
= mDNSNULL
;
567 CacheRecord
*wildcard
= mDNSNULL
;
568 const domainname
*ce
= mDNSNULL
;
571 // Section 8.5, 8.6 of RFC 5155
572 if (NSEC3NoDataError(m
, ncr
, &dv
->q
.qname
, dv
->q
.qtype
, &closestEncloser
))
576 // Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof
577 // for QNAME and the "next closer" should have the opt out
578 if (!NSEC3ClosestEncloserProof(m
, ncr
, &dv
->q
.qname
, &closestEncloser
, &closerEncloser
, &ce
, dv
->q
.qtype
))
583 // Section 8.7: find a matching NSEC3 for *.closestEncloser
587 if (!AppendDomainName(&wild
, ce
))
589 LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce
->c
);
592 if (!NSEC3Find(m
, NSEC3ClosestEncloser
, ncr
, &wild
, &wildcard
, mDNSNULL
, &ce
, dv
->q
.qtype
))
594 // Not a wild card case. Section 8.6 second para applies.
595 LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case");
596 if (!NSEC3OptOut(closerEncloser
))
598 LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
601 LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
602 dv
->flags
|= NSEC3_OPT_OUT
;
608 NSEC3Parse(&wildcard
->resrec
, mDNSNULL
, mDNSNULL
, mDNSNULL
, &bmaplen
, &bmap
);
609 if (BitmapTypeCheck(bmap
, bmaplen
, dv
->q
.qtype
) || BitmapTypeCheck(bmap
, bmaplen
, kDNSType_CNAME
))
611 LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv
->q
.qtype
), CRDisplayString(m
, wildcard
));
614 if (dv
->q
.qtype
== kDNSType_DS
&& BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
))
616 LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)",
617 CRDisplayString(m
, wildcard
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
620 else if (dv
->q
.qtype
!= kDNSType_DS
&& !BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
) &&
621 BitmapTypeCheck(bmap
, bmaplen
, kDNSType_NS
))
623 // Don't use the parent side record for this
624 LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)",
625 CRDisplayString(m
, wildcard
), dv
->q
.qname
.c
, DNSTypeName(dv
->q
.qtype
));
628 LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild
.c
, CRDisplayString(m
, wildcard
));
632 if (!VerifyNSEC3(m
, dv
, ncr
, closestEncloser
, closerEncloser
, wildcard
, NoDataNSECCallback
))
637 dv
->DVCallback(m
, dv
, DNSSEC_Bogus
);
640 mDNSexport mDNSBool
NSEC3WildcardAnswerProof(mDNS
*const m
, CacheRecord
*ncr
, DNSSECVerifier
*dv
)
643 const domainname
*nc
;
644 CacheRecord
*closerEncloser
;
648 // Find the next closer name and prove that it is covered by the NSEC3
649 skip
= CountLabels(&dv
->origName
) - CountLabels(dv
->wildcardName
) - 1;
651 nc
= SkipLeadingLabels(&dv
->origName
, skip
);
655 LogDNSSEC("NSEC3WildcardAnswerProof: wildcard name %##s", nc
->c
);
657 if (!NSEC3Find(m
, NSEC3Covers
, ncr
, (domainname
*)nc
, mDNSNULL
, &closerEncloser
, mDNSNULL
, dv
->q
.qtype
))
659 LogMsg("NSEC3WildcardAnswerProof: Cannot find closer encloser");
664 LogMsg("NSEC3WildcardAnswerProof: closerEncloser NULL");
667 if (NSEC3OptOut(closerEncloser
))
669 dv
->flags
|= NSEC3_OPT_OUT
;
671 // NSEC3 Verification is done by the caller
675 mDNSexport CacheRecord
*NSEC3RecordIsDelegation(mDNS
*const m
, domainname
*name
, mDNSu16 qtype
)
680 mDNSu32 slot
, namehash
;
682 slot
= HashSlot(name
);
683 namehash
= DomainNameHashValue(name
);
685 cg
= CacheGroupForName(m
, (const mDNSu32
)slot
, namehash
, name
);
688 LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name
);
691 for (ncr
= cg
->members
; ncr
; ncr
= ncr
->next
)
693 if (ncr
->resrec
.RecordType
!= kDNSRecordTypePacketNegative
||
694 ncr
->resrec
.rrtype
!= qtype
)
698 for (cr
= ncr
->nsec
; cr
; cr
= cr
->next
)
701 const mDNSu8 hashName
[NSEC3_MAX_HASH_LEN
];
702 const mDNSu8 b32Name
[NSEC3_MAX_B32_LEN
+1];
703 const RDataBody2
*const rdb
= (RDataBody2
*)cr
->resrec
.rdata
->u
.data
;
706 if (cr
->resrec
.rrtype
!= kDNSType_NSEC3
)
709 nsec3
= (rdataNSEC3
*)rdb
->data
;
711 if (!NSEC3HashName(name
, nsec3
, mDNSNULL
, 0, hashName
, &hlen
))
713 LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name
->c
);
717 b32len
= baseEncode((char *)b32Name
, sizeof(b32Name
), (mDNSu8
*)hashName
, hlen
, ENC_BASE32
);
720 LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name
->c
);
723 // Section 2.3 of RFC 4035 states that:
725 // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST
726 // have an NSEC resource record.
728 // This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the
729 // NS bit set, then this is a delegation.
731 if (!NSEC3SameName(&cr
->resrec
.name
->c
[1], cr
->resrec
.name
->c
[0], (const mDNSu8
*)b32Name
, b32len
))
736 LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m
, cr
), name
->c
, b32Name
);
737 NSEC3Parse(&cr
->resrec
, mDNSNULL
, mDNSNULL
, mDNSNULL
, &bmaplen
, &bmap
);
739 // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit
741 if (BitmapTypeCheck(bmap
, bmaplen
, kDNSType_SOA
) ||
742 BitmapTypeCheck(bmap
, bmaplen
, kDNSType_DS
))
744 LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m
, cr
));
747 if (BitmapTypeCheck(bmap
, bmaplen
, kDNSType_NS
))
752 // If opt-out is not set, then it does not cover any delegations
753 if (!(nsec3
->flags
& NSEC3_FLAGS_OPTOUT
))
755 // Opt-out allows insecure delegations to exist without the NSEC3 RR at the
756 // hashed owner name (see RFC 5155 section 6.0).
757 if (NSEC3CoversName(m
, cr
, hashName
, hlen
, b32Name
, b32len
))
759 LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m
, cr
), name
->c
);
767 #else // !DNSSEC_DISABLED
769 #endif // !DNSSEC_DISABLED