3 #include "dnssecinfra.hh"
4 #include "dnsseckeeper.hh"
5 #include "rec-lua-conf.hh"
8 bool g_dnssecLOG
{false};
9 time_t g_signatureInceptionSkew
{0};
10 uint16_t g_maxNSEC3Iterations
{0};
12 #define LOG(x) if(g_dnssecLOG) { g_log <<Logger::Warning << x; }
14 const char *dStates
[]={"nodata", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
15 const char *vStates
[]={"Indeterminate", "Bogus", "Insecure", "Secure", "NTA", "TA"};
17 static vector
<shared_ptr
<DNSKEYRecordContent
> > getByTag(const skeyset_t
& keys
, uint16_t tag
, uint8_t algorithm
)
19 vector
<shared_ptr
<DNSKEYRecordContent
>> ret
;
20 for(const auto& key
: keys
)
21 if(key
->d_protocol
== 3 && key
->getTag() == tag
&& key
->d_algorithm
== algorithm
)
26 static bool isCoveredByNSEC3Hash(const std::string
& h
, const std::string
& beginHash
, const std::string
& nextHash
)
28 return ((beginHash
< h
&& h
< nextHash
) || // no wrap BEGINNING --- HASH -- END
29 (nextHash
> h
&& beginHash
> nextHash
) || // wrap HASH --- END --- BEGINNING
30 (nextHash
< beginHash
&& beginHash
< h
) || // wrap other case END --- BEGINNING --- HASH
31 (beginHash
== nextHash
&& h
!= beginHash
)); // "we have only 1 NSEC3 record, LOL!"
34 static bool isCoveredByNSEC(const DNSName
& name
, const DNSName
& begin
, const DNSName
& next
)
36 return ((begin
.canonCompare(name
) && name
.canonCompare(next
)) || // no wrap BEGINNING --- NAME --- NEXT
37 (name
.canonCompare(next
) && next
.canonCompare(begin
)) || // wrap NAME --- NEXT --- BEGINNING
38 (next
.canonCompare(begin
) && begin
.canonCompare(name
)) || // wrap other case NEXT --- BEGINNING --- NAME
39 (begin
== next
&& name
!= begin
)); // "we have only 1 NSEC record, LOL!"
42 static bool nsecProvesENT(const DNSName
& name
, const DNSName
& begin
, const DNSName
& next
)
46 - next is a child of name
48 return begin
.canonCompare(name
) && next
!= name
&& next
.isPartOf(name
);
51 static std::string
getHashFromNSEC3(const DNSName
& qname
, const std::shared_ptr
<NSEC3RecordContent
>& nsec3
)
55 if (g_maxNSEC3Iterations
&& nsec3
->d_iterations
> g_maxNSEC3Iterations
) {
59 return hashQNameWithSalt(nsec3
->d_salt
, nsec3
->d_iterations
, qname
);
62 /* There is no delegation at this exact point if:
63 - the name exists but the NS type is not set
64 - the name does not exist
65 One exception, if the name is covered by an opt-out NSEC3
66 it doesn't prove that an insecure delegation doesn't exist.
68 bool denialProvesNoDelegation(const DNSName
& zone
, const std::vector
<DNSRecord
>& dsrecords
)
70 for (const auto& record
: dsrecords
) {
71 if (record
.d_type
== QType::NSEC
) {
72 const auto nsec
= getRR
<NSECRecordContent
>(record
);
77 if (record
.d_name
== zone
) {
78 return !nsec
->isSet(QType::NS
);
81 if (isCoveredByNSEC(zone
, record
.d_name
, nsec
->d_next
)) {
85 else if (record
.d_type
== QType::NSEC3
) {
86 const auto nsec3
= getRR
<NSEC3RecordContent
>(record
);
91 const string h
= getHashFromNSEC3(zone
, nsec3
);
96 const string beginHash
= fromBase32Hex(record
.d_name
.getRawLabels()[0]);
98 return !nsec3
->isSet(QType::NS
);
101 if (isCoveredByNSEC3Hash(h
, beginHash
, nsec3
->d_nexthash
)) {
102 return !(nsec3
->d_flags
& 1);
110 /* RFC 4035 section-5.3.4:
111 "If the number of labels in an RRset's owner name is greater than the
112 Labels field of the covering RRSIG RR, then the RRset and its
113 covering RRSIG RR were created as a result of wildcard expansion."
115 bool isWildcardExpanded(unsigned int labelCount
, const std::shared_ptr
<RRSIGRecordContent
>& sign
)
117 if (sign
&& sign
->d_labels
< labelCount
) {
124 static bool isWildcardExpanded(const DNSName
& owner
, const std::vector
<std::shared_ptr
<RRSIGRecordContent
> >& signatures
)
126 if (signatures
.empty()) {
130 const auto& sign
= signatures
.at(0);
131 unsigned int labelsCount
= owner
.countLabels();
132 return isWildcardExpanded(labelsCount
, sign
);
135 bool isWildcardExpandedOntoItself(const DNSName
& owner
, unsigned int labelCount
, const std::shared_ptr
<RRSIGRecordContent
>& sign
)
137 if (owner
.isWildcard() && (labelCount
- 1) == sign
->d_labels
) {
138 /* this is a wildcard alright, but it has not been expanded */
144 static bool isWildcardExpandedOntoItself(const DNSName
& owner
, const std::vector
<std::shared_ptr
<RRSIGRecordContent
> >& signatures
)
146 if (signatures
.empty()) {
150 const auto& sign
= signatures
.at(0);
151 unsigned int labelsCount
= owner
.countLabels();
152 return isWildcardExpandedOntoItself(owner
, labelsCount
, sign
);
155 /* if this is a wildcard NSEC, the owner name has been modified
156 to match the name. Make sure we use the original '*' form. */
157 static DNSName
getNSECOwnerName(const DNSName
& initialOwner
, const std::vector
<std::shared_ptr
<RRSIGRecordContent
> >& signatures
)
159 DNSName result
= initialOwner
;
161 if (signatures
.empty()) {
165 const auto& sign
= signatures
.at(0);
166 unsigned int labelsCount
= initialOwner
.countLabels();
167 if (sign
&& sign
->d_labels
< labelsCount
) {
172 while (sign
->d_labels
< labelsCount
);
174 result
= g_wildcarddnsname
+ result
;
180 static bool isNSECAncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const std::shared_ptr
<NSECRecordContent
>& nsec
)
182 return nsec
->isSet(QType::NS
) &&
183 !nsec
->isSet(QType::SOA
) &&
184 signer
.countLabels() < owner
.countLabels();
187 static bool isNSEC3AncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const std::shared_ptr
<NSEC3RecordContent
>& nsec3
)
189 return nsec3
->isSet(QType::NS
) &&
190 !nsec3
->isSet(QType::SOA
) &&
191 signer
.countLabels() < owner
.countLabels();
194 static bool provesNoDataWildCard(const DNSName
& qname
, const uint16_t qtype
, const cspmap_t
& validrrsets
)
196 LOG("Trying to prove that there is no data in wildcard for "<<qname
<<"/"<<QType(qtype
).getName()<<endl
);
197 for (const auto& v
: validrrsets
) {
198 LOG("Do have: "<<v
.first
.first
<<"/"<<DNSRecordContent::NumberToType(v
.first
.second
)<<endl
);
199 if (v
.first
.second
== QType::NSEC
) {
200 for (const auto& r
: v
.second
.records
) {
201 LOG("\t"<<r
->getZoneRepresentation()<<endl
);
202 auto nsec
= std::dynamic_pointer_cast
<NSECRecordContent
>(r
);
207 if (!v
.first
.first
.isWildcard()) {
210 DNSName wildcard
= getNSECOwnerName(v
.first
.first
, v
.second
.signatures
);
211 if (qname
.countLabels() < wildcard
.countLabels()) {
217 if (qname
.isPartOf(wildcard
)) {
218 LOG("\tWildcard matches");
219 if (qtype
== 0 || !nsec
->isSet(qtype
)) {
220 LOG(" and proves that the type did not exist"<<endl
);
223 LOG(" BUT the type did exist!"<<endl
);
234 This function checks whether the non-existence of a wildcard covering qname|qtype
235 is proven by the NSEC records in validrrsets.
237 static bool provesNoWildCard(const DNSName
& qname
, const uint16_t qtype
, const cspmap_t
& validrrsets
)
239 LOG("Trying to prove that there is no wildcard for "<<qname
<<"/"<<QType(qtype
).getName()<<endl
);
240 for (const auto& v
: validrrsets
) {
241 LOG("Do have: "<<v
.first
.first
<<"/"<<DNSRecordContent::NumberToType(v
.first
.second
)<<endl
);
242 if (v
.first
.second
== QType::NSEC
) {
243 for (const auto& r
: v
.second
.records
) {
244 LOG("\t"<<r
->getZoneRepresentation()<<endl
);
245 auto nsec
= std::dynamic_pointer_cast
<NSECRecordContent
>(r
);
250 const DNSName owner
= getNSECOwnerName(v
.first
.first
, v
.second
.signatures
);
252 A NSEC can only prove the non-existence of a wildcard with at least the same
253 number of labels than the intersection of its owner name and next name.
255 const DNSName commonLabels
= owner
.getCommonLabels(nsec
->d_next
);
256 unsigned int commonLabelsCount
= commonLabels
.countLabels();
258 DNSName
wildcard(qname
);
259 unsigned int wildcardLabelsCount
= wildcard
.countLabels();
260 while (wildcard
.chopOff() && wildcardLabelsCount
>= commonLabelsCount
) {
261 DNSName target
= g_wildcarddnsname
+ wildcard
;
263 LOG("Comparing owner: "<<owner
<<" with target: "<<target
<<endl
);
265 if (isCoveredByNSEC(target
, owner
, nsec
->d_next
)) {
266 LOG("\tWildcard is covered"<<endl
);
278 This function checks whether the non-existence of a wildcard covering qname|qtype
279 is proven by the NSEC3 records in validrrsets.
280 If `wildcardExists` is not NULL, if will be set to true if a wildcard exists
281 for this qname but doesn't have this qtype.
283 static bool provesNSEC3NoWildCard(DNSName wildcard
, uint16_t const qtype
, const cspmap_t
& validrrsets
, bool * wildcardExists
=nullptr)
285 wildcard
= g_wildcarddnsname
+ wildcard
;
286 LOG("Trying to prove that there is no wildcard for "<<wildcard
<<"/"<<QType(qtype
).getName()<<endl
);
288 for (const auto& v
: validrrsets
) {
289 LOG("Do have: "<<v
.first
.first
<<"/"<<DNSRecordContent::NumberToType(v
.first
.second
)<<endl
);
290 if (v
.first
.second
== QType::NSEC3
) {
291 for (const auto& r
: v
.second
.records
) {
292 LOG("\t"<<r
->getZoneRepresentation()<<endl
);
293 auto nsec3
= std::dynamic_pointer_cast
<NSEC3RecordContent
>(r
);
298 const DNSName signer
= getSigner(v
.second
.signatures
);
299 if (!v
.first
.first
.isPartOf(signer
))
302 string h
= getHashFromNSEC3(wildcard
, nsec3
);
306 LOG("\tWildcard hash: "<<toBase32Hex(h
)<<endl
);
307 string beginHash
=fromBase32Hex(v
.first
.first
.getRawLabels()[0]);
308 LOG("\tNSEC3 hash: "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
310 if (beginHash
== h
) {
311 LOG("\tWildcard hash matches");
312 if (wildcardExists
) {
313 *wildcardExists
= true;
315 if (qtype
== 0 || !nsec3
->isSet(qtype
)) {
316 LOG(" and proves that the type did not exist"<<endl
);
319 LOG(" BUT the type did exist!"<<endl
);
323 if (isCoveredByNSEC3Hash(h
, beginHash
, nsec3
->d_nexthash
)) {
324 LOG("\tWildcard hash is covered"<<endl
);
335 This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
337 - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODATA
338 if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
339 - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
340 NXQTYPE is the name is proven to be ENT and NXDOMAIN otherwise.
341 - If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
342 useful when we have a positive answer synthetized from a wildcard and we only need to prove that the exact
345 dState
getDenial(const cspmap_t
&validrrsets
, const DNSName
& qname
, const uint16_t qtype
, bool referralToUnsigned
, bool wantsNoDataProof
, bool needWildcardProof
, unsigned int wildcardLabelsCount
)
347 bool nsec3Seen
= false;
348 if (!needWildcardProof
&& wildcardLabelsCount
== 0) {
349 throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthetized from a wildcard");
352 for(const auto& v
: validrrsets
) {
353 LOG("Do have: "<<v
.first
.first
<<"/"<<DNSRecordContent::NumberToType(v
.first
.second
)<<endl
);
355 if(v
.first
.second
==QType::NSEC
) {
356 for(const auto& r
: v
.second
.records
) {
357 LOG("\t"<<r
->getZoneRepresentation()<<endl
);
358 auto nsec
= std::dynamic_pointer_cast
<NSECRecordContent
>(r
);
362 const DNSName signer
= getSigner(v
.second
.signatures
);
363 if (!v
.first
.first
.isPartOf(signer
))
366 const DNSName owner
= getNSECOwnerName(v
.first
.first
, v
.second
.signatures
);
367 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
368 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
369 nonexistence of any RRs below that zone cut, which include all RRs at
370 that (original) owner name other than DS RRs, and all RRs below that
371 owner name regardless of type.
373 if (qtype
!= QType::DS
&& (qname
== owner
|| qname
.isPartOf(owner
)) && isNSECAncestorDelegation(signer
, owner
, nsec
)) {
374 LOG("type is "<<QType(qtype
).getName()<<", NS is "<<std::to_string(nsec
->isSet(QType::NS
))<<", SOA is "<<std::to_string(nsec
->isSet(QType::SOA
))<<", signer is "<<signer
<<", owner name is "<<owner
<<endl
);
375 /* this is an "ancestor delegation" NSEC RR */
376 LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl
);
380 /* check if the type is denied */
382 if (nsec
->isSet(qtype
)) {
383 LOG("Does _not_ deny existence of type "<<QType(qtype
).getName()<<endl
);
387 LOG("Denies existence of type "<<QType(qtype
).getName()<<endl
);
389 /* RFC 6840 section 4.3 */
390 if (nsec
->isSet(QType::CNAME
)) {
391 LOG("However a CNAME exists"<<endl
);
396 * RFC 4035 Section 2.3:
397 * The bitmap for the NSEC RR at a delegation point requires special
398 * attention. Bits corresponding to the delegation NS RRset and any
399 * RRsets for which the parent zone has authoritative data MUST be set
401 if (referralToUnsigned
&& qtype
== QType::DS
&& !nsec
->isSet(QType::NS
)) {
402 LOG("However, no NS record exists at this level!"<<endl
);
406 /* we know that the name exists (but this qtype doesn't) so except
407 if the answer was generated by a wildcard expansion, no wildcard
408 could have matched (rfc4035 section 5.4 bullet 1) */
409 if (!isWildcardExpanded(owner
, v
.second
.signatures
) || isWildcardExpandedOntoItself(owner
, v
.second
.signatures
)) {
410 needWildcardProof
= false;
413 if (!needWildcardProof
|| provesNoWildCard(qname
, qtype
, validrrsets
)) {
417 LOG("But the existence of a wildcard is not denied for "<<qname
<<"/"<<endl
);
421 /* check if the whole NAME is denied existing */
422 if(isCoveredByNSEC(qname
, owner
, nsec
->d_next
)) {
423 LOG(qname
<<" is covered ");
424 /* if the name is an ENT and we received a NODATA answer,
425 we are fine with a NSEC proving that the name does not exist. */
426 if (wantsNoDataProof
&& nsecProvesENT(qname
, owner
, nsec
->d_next
)) {
427 LOG("Denies existence of type "<<qname
<<"/"<<QType(qtype
).getName()<<" by proving that "<<qname
<<" is an ENT"<<endl
);
431 if (!needWildcardProof
) {
432 LOG("and we did not need a wildcard proof"<<endl
);
436 LOG("but we do need a wildcard proof so ");
437 if (wantsNoDataProof
) {
438 LOG("looking for NODATA proof"<<endl
);
439 if (provesNoDataWildCard(qname
, qtype
, validrrsets
)) {
444 LOG("looking for NO wildcard proof"<<endl
);
445 if (provesNoWildCard(qname
, qtype
, validrrsets
)) {
450 LOG("But the existence of a wildcard is not denied for "<<qname
<<"/"<<QType(qtype
).getName()<<endl
);
454 LOG("Did not deny existence of "<<QType(qtype
).getName()<<", "<<owner
<<"?="<<qname
<<", "<<nsec
->isSet(qtype
)<<", next: "<<nsec
->d_next
<<endl
);
456 } else if(v
.first
.second
==QType::NSEC3
) {
457 for(const auto& r
: v
.second
.records
) {
458 LOG("\t"<<r
->getZoneRepresentation()<<endl
);
459 auto nsec3
= std::dynamic_pointer_cast
<NSEC3RecordContent
>(r
);
463 const DNSName signer
= getSigner(v
.second
.signatures
);
464 if (!v
.first
.first
.isPartOf(signer
)) {
465 LOG("Owner "<<v
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
469 string h
= getHashFromNSEC3(qname
, nsec3
);
471 LOG("Unsupported hash, ignoring"<<endl
);
477 // cerr<<"Salt length: "<<nsec3->d_salt.length()<<", iterations: "<<nsec3->d_iterations<<", hashed: "<<qname<<endl;
478 LOG("\tquery hash: "<<toBase32Hex(h
)<<endl
);
479 string beginHash
=fromBase32Hex(v
.first
.first
.getRawLabels()[0]);
481 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
482 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
483 nonexistence of any RRs below that zone cut, which include all RRs at
484 that (original) owner name other than DS RRs, and all RRs below that
485 owner name regardless of type.
487 if (qtype
!= QType::DS
&& beginHash
== h
&& isNSEC3AncestorDelegation(signer
, v
.first
.first
, nsec3
)) {
488 LOG("type is "<<QType(qtype
).getName()<<", NS is "<<std::to_string(nsec3
->isSet(QType::NS
))<<", SOA is "<<std::to_string(nsec3
->isSet(QType::SOA
))<<", signer is "<<signer
<<", owner name is "<<v
.first
.first
<<endl
);
489 /* this is an "ancestor delegation" NSEC3 RR */
490 LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
494 // If the name exists, check if the qtype is denied
496 if (nsec3
->isSet(qtype
)) {
497 LOG("Does _not_ deny existence of type "<<QType(qtype
).getName()<<" for name "<<qname
<<" (not opt-out)."<<endl
);
501 LOG("Denies existence of type "<<QType(qtype
).getName()<<" for name "<<qname
<<" (not opt-out)."<<endl
);
503 /* RFC 6840 section 4.3 */
504 if (nsec3
->isSet(QType::CNAME
)) {
505 LOG("However a CNAME exists"<<endl
);
510 * RFC 5155 section 8.9:
511 * If there is an NSEC3 RR present in the response that matches the
512 * delegation name, then the validator MUST ensure that the NS bit is
513 * set and that the DS bit is not set in the Type Bit Maps field of the
516 if (referralToUnsigned
&& qtype
== QType::DS
&& !nsec3
->isSet(QType::NS
)) {
517 LOG("However, no NS record exists at this level!"<<endl
);
527 /* if we have no NSEC3 records, we are done */
532 DNSName
closestEncloser(qname
);
535 if (needWildcardProof
) {
536 /* We now need to look for a NSEC3 covering the closest (provable) encloser
537 RFC 5155 section-7.2.1
540 LOG("Now looking for the closest encloser for "<<qname
<<endl
);
542 while (found
== false && closestEncloser
.chopOff()) {
543 for(const auto& v
: validrrsets
) {
544 if(v
.first
.second
==QType::NSEC3
) {
545 for(const auto& r
: v
.second
.records
) {
546 LOG("\t"<<r
->getZoneRepresentation()<<endl
);
547 auto nsec3
= std::dynamic_pointer_cast
<NSEC3RecordContent
>(r
);
551 const DNSName signer
= getSigner(v
.second
.signatures
);
552 if (!v
.first
.first
.isPartOf(signer
)) {
553 LOG("Owner "<<v
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
557 string h
= getHashFromNSEC3(closestEncloser
, nsec3
);
562 string beginHash
=fromBase32Hex(v
.first
.first
.getRawLabels()[0]);
564 LOG("Comparing "<<toBase32Hex(h
)<<" ("<<closestEncloser
<<") against "<<toBase32Hex(beginHash
)<<endl
);
566 if (qtype
!= QType::DS
&& isNSEC3AncestorDelegation(signer
, v
.first
.first
, nsec3
)) {
567 LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
571 LOG("Closest encloser for "<<qname
<<" is "<<closestEncloser
<<endl
);
584 /* RFC 5155 section-7.2.6:
585 "It is not necessary to return an NSEC3 RR that matches the closest encloser,
586 as the existence of this closest encloser is proven by the presence of the
587 expanded wildcard in the response.
590 unsigned int closestEncloserLabelsCount
= closestEncloser
.countLabels();
591 while (wildcardLabelsCount
> 0 && closestEncloserLabelsCount
> wildcardLabelsCount
) {
592 closestEncloser
.chopOff();
593 closestEncloserLabelsCount
--;
597 bool nextCloserFound
= false;
598 bool isOptOut
= false;
601 /* now that we have found the closest (provable) encloser,
602 we can construct the next closer (FRC7129 section-5.5) name
603 and look for a NSEC3 RR covering it */
604 unsigned int labelIdx
= qname
.countLabels() - closestEncloser
.countLabels();
606 DNSName
nextCloser(closestEncloser
);
607 nextCloser
.prependRawLabel(qname
.getRawLabel(labelIdx
- 1));
608 LOG("Looking for a NSEC3 covering the next closer name "<<nextCloser
<<endl
);
610 for(const auto& v
: validrrsets
) {
611 if(v
.first
.second
==QType::NSEC3
) {
612 for(const auto& r
: v
.second
.records
) {
613 LOG("\t"<<r
->getZoneRepresentation()<<endl
);
614 auto nsec3
= std::dynamic_pointer_cast
<NSEC3RecordContent
>(r
);
618 string h
= getHashFromNSEC3(nextCloser
, nsec3
);
623 string beginHash
=fromBase32Hex(v
.first
.first
.getRawLabels()[0]);
625 LOG("Comparing "<<toBase32Hex(h
)<<" against "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
626 if(isCoveredByNSEC3Hash(h
, beginHash
, nsec3
->d_nexthash
)) {
627 LOG("Denies existence of name "<<qname
<<"/"<<QType(qtype
).getName());
628 nextCloserFound
= true;
630 if (qtype
== QType::DS
&& nsec3
->d_flags
& 1) {
631 LOG(" but is opt-out!");
637 LOG("Did not cover us ("<<qname
<<"), start="<<v
.first
.first
<<", us="<<toBase32Hex(h
)<<", end="<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
640 if (nextCloserFound
) {
647 if (nextCloserFound
) {
648 bool wildcardExists
= false;
649 /* RFC 7129 section-5.6 */
650 if (needWildcardProof
&& !provesNSEC3NoWildCard(closestEncloser
, qtype
, validrrsets
, &wildcardExists
)) {
652 LOG("But the existence of a wildcard is not denied for "<<qname
<<"/"<<QType(qtype
).getName()<<endl
);
661 if (wildcardExists
) {
668 // There were no valid NSEC(3) records
669 // XXX maybe this should be INSECURE... it depends on the semantics of this function
674 * Finds all the zone-cuts between begin (longest name) and end (shortest name),
675 * returns them all zone cuts, including end, but (possibly) not begin
677 static const vector
<DNSName
> getZoneCuts(const DNSName
& begin
, const DNSName
& end
, DNSRecordOracle
& dro
)
680 if(!begin
.isPartOf(end
))
681 throw PDNSException(end
.toLogString() + "is not part of " + begin
.toLogString());
684 vector
<string
> labelsToAdd
= begin
.makeRelative(end
).getRawLabels();
686 // The shortest name is assumed to a zone cut
687 ret
.push_back(qname
);
688 while(qname
!= begin
) {
689 bool foundCut
= false;
690 if (labelsToAdd
.empty())
693 qname
.prependRawLabel(labelsToAdd
.back());
694 labelsToAdd
.pop_back();
695 auto records
= dro
.get(qname
, (uint16_t)QType::NS
);
696 for (const auto record
: records
) {
697 if(record
.d_type
!= QType::NS
|| record
.d_name
!= qname
)
703 ret
.push_back(qname
);
708 bool isRRSIGNotExpired(const time_t now
, const shared_ptr
<RRSIGRecordContent
> sig
)
710 return sig
->d_siginception
- g_signatureInceptionSkew
<= now
&& sig
->d_sigexpire
>= now
;
713 static bool checkSignatureWithKey(time_t now
, const shared_ptr
<RRSIGRecordContent
> sig
, const shared_ptr
<DNSKEYRecordContent
> key
, const std::string
& msg
)
718 - The validator's notion of the current time MUST be less than or equal to the time listed in the RRSIG RR's Expiration field.
719 - The validator's notion of the current time MUST be greater than or equal to the time listed in the RRSIG RR's Inception field.
721 if(isRRSIGNotExpired(now
, sig
)) {
722 std::shared_ptr
<DNSCryptoKeyEngine
> dke
= shared_ptr
<DNSCryptoKeyEngine
>(DNSCryptoKeyEngine::makeFromPublicKeyString(key
->d_algorithm
, key
->d_key
));
723 result
= dke
->verify(msg
, sig
->d_signature
);
724 LOG("signature by key with tag "<<sig
->d_tag
<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig
->d_algorithm
)<<" was " << (result
? "" : "NOT ")<<"valid"<<endl
);
727 LOG("Signature is "<<((sig
->d_siginception
- g_signatureInceptionSkew
> now
) ? "not yet valid" : "expired")<<" (inception: "<<sig
->d_siginception
<<", inception skew: "<<g_signatureInceptionSkew
<<", expiration: "<<sig
->d_sigexpire
<<", now: "<<now
<<")"<<endl
);
730 catch(const std::exception
& e
) {
731 LOG("Could not make a validator for signature: "<<e
.what()<<endl
);
736 bool validateWithKeySet(time_t now
, const DNSName
& name
, const vector
<shared_ptr
<DNSRecordContent
> >& records
, const vector
<shared_ptr
<RRSIGRecordContent
> >& signatures
, const skeyset_t
& keys
, bool validateAllSigs
)
738 bool isValid
= false;
740 for(const auto& signature
: signatures
) {
741 unsigned int labelCount
= name
.countLabels();
742 if (signature
->d_labels
> labelCount
) {
743 LOG(name
<<": Discarding invalid RRSIG whose label count is "<<signature
->d_labels
<<" while the RRset owner name has only "<<labelCount
<<endl
);
746 vector
<shared_ptr
<DNSRecordContent
> > toSign
= records
;
748 auto r
= getByTag(keys
, signature
->d_tag
, signature
->d_algorithm
);
751 LOG("No key provided for "<<signature
->d_tag
<<" and algorithm "<<std::to_string(signature
->d_algorithm
)<<endl
;);
755 string msg
=getMessageForRRSET(name
, *signature
, toSign
, true);
756 for(const auto& l
: r
) {
757 bool signIsValid
= checkSignatureWithKey(now
, signature
, l
, msg
);
760 LOG("Validated "<<name
<<"/"<<DNSRecordContent::NumberToType(signature
->d_type
)<<endl
);
761 // cerr<<"valid"<<endl;
762 // cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
765 LOG("signature invalid"<<endl
);
767 if (signIsValid
&& !validateAllSigs
) {
776 void validateWithKeySet(const cspmap_t
& rrsets
, cspmap_t
& validated
, const skeyset_t
& keys
)
779 /* cerr<<"Validating an rrset with following keys: "<<endl;
780 for(auto& key : keys) {
781 cerr<<"\tTag: "<<key->getTag()<<" -> "<<key->getZoneRepresentation()<<endl;
784 time_t now
= time(nullptr);
785 for(auto i
=rrsets
.cbegin(); i
!=rrsets
.cend(); i
++) {
786 LOG("validating "<<(i
->first
.first
)<<"/"<<DNSRecordContent::NumberToType(i
->first
.second
)<<" with "<<i
->second
.signatures
.size()<<" sigs"<<endl
);
787 if (validateWithKeySet(now
, i
->first
.first
, i
->second
.records
, i
->second
.signatures
, keys
, true)) {
788 validated
[i
->first
] = i
->second
;
794 // should return vState, zone cut and validated keyset
795 // i.e. www.7bits.nl -> insecure/7bits.nl/[]
796 // www.powerdnssec.org -> secure/powerdnssec.org/[keys]
797 // www.dnssec-failed.org -> bogus/dnssec-failed.org/[]
799 cspmap_t
harvestCSPFromRecs(const vector
<DNSRecord
>& recs
)
802 for(const auto& rec
: recs
) {
803 // cerr<<"res "<<rec.d_name<<"/"<<rec.d_type<<endl;
804 if(rec
.d_type
== QType::OPT
) continue;
806 if(rec
.d_type
== QType::RRSIG
) {
807 auto rrc
= getRR
<RRSIGRecordContent
>(rec
);
809 cspmap
[{rec
.d_name
,rrc
->d_type
}].signatures
.push_back(rrc
);
813 cspmap
[{rec
.d_name
, rec
.d_type
}].records
.push_back(rec
.d_content
);
819 bool getTrustAnchor(const map
<DNSName
,dsmap_t
>& anchors
, const DNSName
& zone
, dsmap_t
&res
)
821 const auto& it
= anchors
.find(zone
);
823 if (it
== anchors
.cend()) {
831 bool haveNegativeTrustAnchor(const map
<DNSName
,std::string
>& negAnchors
, const DNSName
& zone
, std::string
& reason
)
833 const auto& it
= negAnchors
.find(zone
);
835 if (it
== negAnchors
.cend()) {
843 void validateDNSKeysAgainstDS(time_t now
, const DNSName
& zone
, const dsmap_t
& dsmap
, const skeyset_t
& tkeys
, vector
<shared_ptr
<DNSRecordContent
> >& toSign
, const vector
<shared_ptr
<RRSIGRecordContent
> >& sigs
, skeyset_t
& validkeys
)
846 * Check all DNSKEY records against all DS records and place all DNSKEY records
847 * that have DS records (that we support the algo for) in the tentative key storage
849 for(auto const& dsrc
: dsmap
)
851 auto r
= getByTag(tkeys
, dsrc
.d_tag
, dsrc
.d_algorithm
);
852 // cerr<<"looking at DS with tag "<<dsrc.d_tag<<", algo "<<DNSSECKeeper::algorithm2name(dsrc.d_algorithm)<<", digest "<<std::to_string(dsrc.d_digesttype)<<" for "<<zone<<", got "<<r.size()<<" DNSKEYs for tag"<<endl;
854 for(const auto& drc
: r
)
856 bool isValid
= false;
857 bool dsCreated
= false;
858 DSRecordContent dsrc2
;
860 dsrc2
= makeDSFromDNSKey(zone
, *drc
, dsrc
.d_digesttype
);
862 isValid
= dsrc
== dsrc2
;
864 catch(const std::exception
&e
) {
865 LOG("Unable to make DS from DNSKey: "<<e
.what()<<endl
);
869 LOG("got valid DNSKEY (it matches the DS) with tag "<<dsrc
.d_tag
<<" and algorithm "<<std::to_string(dsrc
.d_algorithm
)<<" for "<<zone
<<endl
);
871 validkeys
.insert(drc
);
875 LOG("DNSKEY did not match the DS, parent DS: "<<dsrc
.getZoneRepresentation() << " ! = "<<dsrc2
.getZoneRepresentation()<<endl
);
881 vector
<uint16_t> toSignTags
;
882 for (const auto& key
: tkeys
) {
883 toSignTags
.push_back(key
->getTag());
886 // cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
887 // these counts could be off if we somehow ended up with
888 // duplicate keys. Should switch to a type that prevents that.
889 if(validkeys
.size() < tkeys
.size())
891 // this should mean that we have one or more DS-validated DNSKEYs
892 // but not a fully validated DNSKEY set, yet
893 // one of these valid DNSKEYs should be able to validate the
895 for(const auto& sig
: sigs
)
897 // cerr<<"got sig for keytag "<<i->d_tag<<" matching "<<getByTag(tkeys, i->d_tag).size()<<" keys of which "<<getByTag(validkeys, i->d_tag).size()<<" valid"<<endl;
898 auto bytag
= getByTag(validkeys
, sig
->d_tag
, sig
->d_algorithm
);
904 string msg
= getMessageForRRSET(zone
, *sig
, toSign
);
905 for(const auto& key
: bytag
) {
906 // cerr<<"validating : ";
907 bool signIsValid
= checkSignatureWithKey(now
, sig
, key
, msg
);
911 LOG("validation succeeded - whole DNSKEY set is valid"<<endl
);
916 LOG("Validation did not succeed!"<<endl
);
919 // if(validkeys.empty()) cerr<<"did not manage to validate DNSKEY set based on DS-validated KSK, only passing KSK on"<<endl;
924 vState
getKeysFor(DNSRecordOracle
& dro
, const DNSName
& zone
, skeyset_t
& keyset
)
926 auto luaLocal
= g_luaconfs
.getLocal();
927 const auto anchors
= luaLocal
->dsAnchors
;
928 if (anchors
.empty()) // Nothing to do here
931 // Determine the lowest (i.e. with the most labels) Trust Anchor for zone
932 DNSName
lowestTA(".");
933 for (auto const &anchor
: anchors
)
934 if (zone
.isPartOf(anchor
.first
) && lowestTA
.countLabels() < anchor
.first
.countLabels())
935 lowestTA
= anchor
.first
;
937 // Before searching for the keys, see if we have a Negative Trust Anchor. If
938 // so, test if the NTA is valid and return an NTA state
939 const auto negAnchors
= luaLocal
->negAnchors
;
941 if (!negAnchors
.empty()) {
944 for (auto const &negAnchor
: negAnchors
)
945 if (zone
.isPartOf(negAnchor
.first
) && lowestNTA
.countLabels() <= negAnchor
.first
.countLabels())
946 lowestNTA
= negAnchor
.first
;
948 if(!lowestNTA
.empty()) {
949 LOG("Found a Negative Trust Anchor for "<<lowestNTA
<<", which was added with reason '"<<negAnchors
.at(lowestNTA
)<<"', ");
951 /* RFC 7646 section 2.1 tells us that we SHOULD still validate if there
952 * is a Trust Anchor below the Negative Trust Anchor for the name we
953 * attempt validation for. However, section 3 tells us this positive
954 * Trust Anchor MUST be *below* the name and not the name itself
956 if(lowestTA
.countLabels() <= lowestNTA
.countLabels()) {
957 LOG("marking answer Insecure"<<endl
);
958 return NTA
; // Not Insecure, this way validateRecords() can shortcut
960 LOG("but a Trust Anchor for "<<lowestTA
<<" is configured, continuing validation."<<endl
);
967 dsmap_t
* tmp
= (dsmap_t
*) rplookup(anchors
, lowestTA
);
971 auto zoneCuts
= getZoneCuts(zone
, lowestTA
, dro
);
973 LOG("Found the following zonecuts:")
974 for(const auto& zonecut
: zoneCuts
)
975 LOG(" => "<<zonecut
);
978 for(auto zoneCutIter
= zoneCuts
.cbegin(); zoneCutIter
!= zoneCuts
.cend(); ++zoneCutIter
)
980 vector
<shared_ptr
<RRSIGRecordContent
> > sigs
;
981 vector
<shared_ptr
<DNSRecordContent
> > toSign
;
983 skeyset_t tkeys
; // tentative keys
986 // cerr<<"got DS for ["<<qname<<"], grabbing DNSKEYs"<<endl;
987 auto records
=dro
.get(*zoneCutIter
, (uint16_t)QType::DNSKEY
);
988 // this should use harvest perhaps
989 for(const auto& rec
: records
) {
990 if(rec
.d_name
!= *zoneCutIter
)
993 if(rec
.d_type
== QType::RRSIG
)
995 auto rrc
=getRR
<RRSIGRecordContent
> (rec
);
997 LOG("Got signature: "<<rrc
->getZoneRepresentation()<<" with tag "<<rrc
->d_tag
<<", for type "<<DNSRecordContent::NumberToType(rrc
->d_type
)<<endl
);
998 if(rrc
->d_type
!= QType::DNSKEY
)
1000 sigs
.push_back(rrc
);
1003 else if(rec
.d_type
== QType::DNSKEY
)
1005 auto drc
=getRR
<DNSKEYRecordContent
> (rec
);
1008 LOG("Inserting key with tag "<<drc
->getTag()<<" and algorithm "<<DNSSECKeeper::algorithm2name(drc
->d_algorithm
)<<": "<<drc
->getZoneRepresentation()<<endl
);
1010 toSign
.push_back(rec
.d_content
);
1014 LOG("got "<<tkeys
.size()<<" keys and "<<sigs
.size()<<" sigs from server"<<endl
);
1017 * Check all DNSKEY records against all DS records and place all DNSKEY records
1018 * that have DS records (that we support the algo for) in the tentative key storage
1020 validateDNSKeysAgainstDS(time(nullptr), *zoneCutIter
, dsmap
, tkeys
, toSign
, sigs
, validkeys
);
1022 if(validkeys
.empty())
1024 LOG("ended up with zero valid DNSKEYs, going Bogus"<<endl
);
1027 LOG("situation: we have one or more valid DNSKEYs for ["<<*zoneCutIter
<<"] (want ["<<zone
<<"])"<<endl
);
1029 if(zoneCutIter
== zoneCuts
.cend()-1) {
1030 LOG("requested keyset found! returning Secure for the keyset"<<endl
);
1031 keyset
.insert(validkeys
.cbegin(), validkeys
.cend());
1035 // We now have the DNSKEYs, use them to validate the DS records at the next zonecut
1036 LOG("next name ["<<*(zoneCutIter
+1)<<"], trying to get DS"<<endl
);
1038 dsmap_t tdsmap
; // tentative DSes
1042 auto recs
=dro
.get(*(zoneCutIter
+1), QType::DS
);
1044 cspmap_t cspmap
=harvestCSPFromRecs(recs
);
1046 cspmap_t validrrsets
;
1047 validateWithKeySet(cspmap
, validrrsets
, validkeys
);
1049 LOG("got "<<cspmap
.count(make_pair(*(zoneCutIter
+1),QType::DS
))<<" records for DS query of which "<<validrrsets
.count(make_pair(*(zoneCutIter
+1),QType::DS
))<<" valid "<<endl
);
1051 auto r
= validrrsets
.equal_range(make_pair(*(zoneCutIter
+1), QType::DS
));
1052 if(r
.first
== r
.second
) {
1053 LOG("No DS for "<<*(zoneCutIter
+1)<<", now look for a secure denial"<<endl
);
1054 dState res
= getDenial(validrrsets
, *(zoneCutIter
+1), QType::DS
, true, true);
1055 if (res
== INSECURE
|| res
== NXDOMAIN
)
1057 if (res
== NXQTYPE
|| res
== OPTOUT
)
1062 * Collect all DS records and add them to the dsmap for the next iteration
1064 for(auto cspiter
=r
.first
; cspiter
!=r
.second
; cspiter
++) {
1065 for(auto j
=cspiter
->second
.records
.cbegin(); j
!=cspiter
->second
.records
.cend(); j
++)
1067 const auto dsrc
=std::dynamic_pointer_cast
<DSRecordContent
>(*j
);
1069 dsmap
.insert(*dsrc
);
1074 // There were no zone cuts (aka, we should never get here)
1078 bool isSupportedDS(const DSRecordContent
& ds
)
1080 if (!DNSCryptoKeyEngine::isAlgorithmSupported(ds
.d_algorithm
)) {
1081 LOG("Discarding DS "<<ds
.d_tag
<<" because we don't support algorithm number "<<std::to_string(ds
.d_algorithm
)<<endl
);
1085 if (!DNSCryptoKeyEngine::isDigestSupported(ds
.d_digesttype
)) {
1086 LOG("Discarding DS "<<ds
.d_tag
<<" because we don't support digest number "<<std::to_string(ds
.d_digesttype
)<<endl
);
1093 DNSName
getSigner(const std::vector
<std::shared_ptr
<RRSIGRecordContent
> >& signatures
)
1095 for (const auto sig
: signatures
) {
1097 return sig
->d_signer
;