2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "validate.hh"
25 #include "dnssecinfra.hh"
26 #include "dnsseckeeper.hh"
27 #include "rec-lua-conf.hh"
31 time_t g_signatureInceptionSkew
{0};
32 uint16_t g_maxNSEC3Iterations
{0};
33 uint16_t g_maxRRSIGsPerRecordToConsider
{0};
34 uint16_t g_maxNSEC3sPerRecordToConsider
{0};
35 uint16_t g_maxDNSKEYsToConsider
{0};
36 uint16_t g_maxDSsToConsider
{0};
38 static bool isAZoneKey(const DNSKEYRecordContent
& key
)
40 /* rfc4034 Section 2.1.1:
41 "Bit 7 of the Flags field is the Zone Key flag. If bit 7 has value 1,
42 then the DNSKEY record holds a DNS zone key, and the DNSKEY RR's
43 owner name MUST be the name of a zone. If bit 7 has value 0, then
44 the DNSKEY record holds some other type of DNS public key and MUST
45 NOT be used to verify RRSIGs that cover RRsets."
47 Let's check that this is a ZONE key, even though there is no other
48 types of DNSKEYs at the moment.
50 return (key
.d_flags
& 256) != 0;
53 static bool isRevokedKey(const DNSKEYRecordContent
& key
)
55 /* rfc5011 Section 3 */
56 return (key
.d_flags
& 128) != 0;
59 static vector
<shared_ptr
<const DNSKEYRecordContent
> > getByTag(const skeyset_t
& keys
, uint16_t tag
, uint8_t algorithm
, const OptLog
& log
)
61 vector
<shared_ptr
<const DNSKEYRecordContent
>> ret
;
63 for (const auto& key
: keys
) {
64 if (!isAZoneKey(*key
)) {
65 VLOG(log
, "Key for tag "<<std::to_string(tag
)<<" and algorithm "<<std::to_string(algorithm
)<<" is not a zone key, skipping"<<endl
;);
69 if (isRevokedKey(*key
)) {
70 VLOG(log
, "Key for tag "<<std::to_string(tag
)<<" and algorithm "<<std::to_string(algorithm
)<<" has been revoked, skipping"<<endl
;);
74 if (key
->d_protocol
== 3 && key
->getTag() == tag
&& key
->d_algorithm
== algorithm
) {
82 bool isCoveredByNSEC3Hash(const std::string
& hash
, const std::string
& beginHash
, const std::string
& nextHash
)
84 return ((beginHash
< hash
&& hash
< nextHash
) || // no wrap BEGINNING --- HASH -- END
85 (nextHash
> hash
&& beginHash
> nextHash
) || // wrap HASH --- END --- BEGINNING
86 (nextHash
< beginHash
&& beginHash
< hash
) || // wrap other case END --- BEGINNING --- HASH
87 (beginHash
== nextHash
&& hash
!= beginHash
)); // "we have only 1 NSEC3 record, LOL!"
90 bool isCoveredByNSEC3Hash(const DNSName
& name
, const DNSName
& beginHash
, const DNSName
& nextHash
)
92 return ((beginHash
.canonCompare(name
) && name
.canonCompare(nextHash
)) || // no wrap BEGINNING --- HASH -- END
93 (name
.canonCompare(nextHash
) && nextHash
.canonCompare(beginHash
)) || // wrap HASH --- END --- BEGINNING
94 (nextHash
.canonCompare(beginHash
) && beginHash
.canonCompare(name
)) || // wrap other case END --- BEGINNING --- HASH
95 (beginHash
== nextHash
&& name
!= beginHash
)); // "we have only 1 NSEC3 record, LOL!"
98 bool isCoveredByNSEC(const DNSName
& name
, const DNSName
& begin
, const DNSName
& next
)
100 return ((begin
.canonCompare(name
) && name
.canonCompare(next
)) || // no wrap BEGINNING --- NAME --- NEXT
101 (name
.canonCompare(next
) && next
.canonCompare(begin
)) || // wrap NAME --- NEXT --- BEGINNING
102 (next
.canonCompare(begin
) && begin
.canonCompare(name
)) || // wrap other case NEXT --- BEGINNING --- NAME
103 (begin
== next
&& name
!= begin
)); // "we have only 1 NSEC record, LOL!"
106 static bool nsecProvesENT(const DNSName
& name
, const DNSName
& begin
, const DNSName
& next
)
108 /* if name is an ENT:
110 - next is a child of name
112 return begin
.canonCompare(name
) && next
!= name
&& next
.isPartOf(name
);
115 [[nodiscard
]] std::string
getHashFromNSEC3(const DNSName
& qname
, uint16_t iterations
, const std::string
& salt
, pdns::validation::ValidationContext
& context
)
119 if (g_maxNSEC3Iterations
!= 0 && iterations
> g_maxNSEC3Iterations
) {
123 auto key
= std::tuple(qname
, salt
, iterations
);
124 auto iter
= context
.d_nsec3Cache
.find(key
);
125 if (iter
!= context
.d_nsec3Cache
.end()) {
129 if (context
.d_nsec3IterationsRemainingQuota
< iterations
) {
130 // we throw here because we cannot take the risk that the result
131 // be cached, since a different query can try to validate the
132 // same result with a bigger NSEC3 iterations quota
133 throw pdns::validation::TooManySEC3IterationsException();
136 result
= hashQNameWithSalt(salt
, iterations
, qname
);
137 context
.d_nsec3IterationsRemainingQuota
-= iterations
;
138 context
.d_nsec3Cache
[key
] = result
;
142 [[nodiscard
]] static std::string
getHashFromNSEC3(const DNSName
& qname
, const NSEC3RecordContent
& nsec3
, pdns::validation::ValidationContext
& context
)
144 return getHashFromNSEC3(qname
, nsec3
.d_iterations
, nsec3
.d_salt
, context
);
147 /* There is no delegation at this exact point if:
148 - the name exists but the NS type is not set
149 - the name does not exist
150 One exception, if the name is covered by an opt-out NSEC3
151 it doesn't prove that an insecure delegation doesn't exist.
153 bool denialProvesNoDelegation(const DNSName
& zone
, const std::vector
<DNSRecord
>& dsrecords
, pdns::validation::ValidationContext
& context
)
155 uint16_t nsec3sConsidered
= 0;
157 for (const auto& record
: dsrecords
) {
158 if (record
.d_type
== QType::NSEC
) {
159 const auto nsec
= getRR
<NSECRecordContent
>(record
);
164 if (record
.d_name
== zone
) {
165 return !nsec
->isSet(QType::NS
);
168 if (isCoveredByNSEC(zone
, record
.d_name
, nsec
->d_next
)) {
172 else if (record
.d_type
== QType::NSEC3
) {
173 const auto nsec3
= getRR
<NSEC3RecordContent
>(record
);
178 if (g_maxNSEC3sPerRecordToConsider
> 0 && nsec3sConsidered
>= g_maxNSEC3sPerRecordToConsider
) {
179 context
.d_limitHit
= true;
184 const string hash
= getHashFromNSEC3(zone
, *nsec3
, context
);
189 const string beginHash
= fromBase32Hex(record
.d_name
.getRawLabels()[0]);
190 if (beginHash
== hash
) {
191 return !nsec3
->isSet(QType::NS
);
194 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
195 return !(nsec3
->isOptOut());
203 /* RFC 4035 section-5.3.4:
204 "If the number of labels in an RRset's owner name is greater than the
205 Labels field of the covering RRSIG RR, then the RRset and its
206 covering RRSIG RR were created as a result of wildcard expansion."
208 bool isWildcardExpanded(unsigned int labelCount
, const RRSIGRecordContent
& sign
)
210 return sign
.d_labels
< labelCount
;
213 static bool isWildcardExpanded(const DNSName
& owner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
215 if (signatures
.empty()) {
219 const auto& sign
= signatures
.at(0);
220 unsigned int labelsCount
= owner
.countLabels();
221 return isWildcardExpanded(labelsCount
, *sign
);
224 bool isWildcardExpandedOntoItself(const DNSName
& owner
, unsigned int labelCount
, const RRSIGRecordContent
& sign
)
226 /* this is a wildcard alright, but it has not been expanded */
227 return owner
.isWildcard() && (labelCount
- 1) == sign
.d_labels
;
230 static bool isWildcardExpandedOntoItself(const DNSName
& owner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
232 if (signatures
.empty()) {
236 const auto& sign
= signatures
.at(0);
237 unsigned int labelsCount
= owner
.countLabels();
238 return isWildcardExpandedOntoItself(owner
, labelsCount
, *sign
);
241 /* if this is a wildcard NSEC, the owner name has been modified
242 to match the name. Make sure we use the original '*' form. */
243 DNSName
getNSECOwnerName(const DNSName
& initialOwner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
245 DNSName result
= initialOwner
;
247 if (signatures
.empty()) {
251 const auto& sign
= signatures
.at(0);
252 unsigned int labelsCount
= initialOwner
.countLabels();
253 if (sign
&& sign
->d_labels
< labelsCount
) {
258 while (sign
->d_labels
< labelsCount
);
260 result
= g_wildcarddnsname
+ result
;
266 static bool isNSECAncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const NSECRecordContent
& nsec
)
268 return nsec
.isSet(QType::NS
) &&
269 !nsec
.isSet(QType::SOA
) &&
270 signer
.countLabels() < owner
.countLabels();
273 bool isNSEC3AncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const NSEC3RecordContent
& nsec3
)
275 return nsec3
.isSet(QType::NS
) &&
276 !nsec3
.isSet(QType::SOA
) &&
277 signer
.countLabels() < owner
.countLabels();
280 static bool provesNoDataWildCard(const DNSName
& qname
, const uint16_t qtype
, const DNSName
& closestEncloser
, const cspmap_t
& validrrsets
, const OptLog
& log
)
282 const DNSName wildcard
= g_wildcarddnsname
+ closestEncloser
;
283 VLOG(log
, qname
<< ": Trying to prove that there is no data in wildcard for "<<qname
<<"/"<<QType(qtype
)<<endl
);
284 for (const auto& validset
: validrrsets
) {
285 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
286 if (validset
.first
.second
== QType::NSEC
) {
287 for (const auto& record
: validset
.second
.records
) {
288 VLOG(log
, ":\t"<<record
->getZoneRepresentation()<<endl
);
289 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(record
);
294 DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
295 if (owner
!= wildcard
) {
299 VLOG(log
, qname
<< ":\tWildcard matches");
300 if (qtype
== 0 || isTypeDenied(*nsec
, QType(qtype
))) {
301 VLOG_NO_PREFIX(log
, " and proves that the type did not exist"<<endl
);
304 VLOG_NO_PREFIX(log
, " BUT the type did exist!"<<endl
);
313 DNSName
getClosestEncloserFromNSEC(const DNSName
& name
, const DNSName
& owner
, const DNSName
& next
)
315 DNSName
commonWithOwner(name
.getCommonLabels(owner
));
316 DNSName
commonWithNext(name
.getCommonLabels(next
));
317 if (commonWithOwner
.countLabels() >= commonWithNext
.countLabels()) {
318 return commonWithOwner
;
320 return commonWithNext
;
324 This function checks whether the non-existence of a wildcard covering qname|qtype
325 is proven by the NSEC records in validrrsets.
327 static bool provesNoWildCard(const DNSName
& qname
, const uint16_t qtype
, const DNSName
& closestEncloser
, const cspmap_t
& validrrsets
, const OptLog
& log
)
329 VLOG(log
, qname
<< ": Trying to prove that there is no wildcard for "<<qname
<<"/"<<QType(qtype
)<<endl
);
330 const DNSName wildcard
= g_wildcarddnsname
+ closestEncloser
;
331 for (const auto& validset
: validrrsets
) {
332 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
333 if (validset
.first
.second
== QType::NSEC
) {
334 for (const auto& records
: validset
.second
.records
) {
335 VLOG(log
, qname
<< ":\t"<<records
->getZoneRepresentation()<<endl
);
336 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(records
);
341 const DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
342 VLOG(log
, qname
<< ": Comparing owner: "<<owner
<<" with target: "<<wildcard
<<endl
);
344 if (qname
!= owner
&& qname
.isPartOf(owner
) && nsec
->isSet(QType::DNAME
)) {
345 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
347 In any negative response, the NSEC or NSEC3 [RFC5155] record type
348 bitmap SHOULD be checked to see that there was no DNAME that could
349 have been applied. If the DNAME bit in the type bitmap is set and
350 the query name is a subdomain of the closest encloser that is
351 asserted, then DNAME substitution should have been done, but the
352 substitution has not been done as specified.
354 VLOG(log
, qname
<< ":\tThe qname is a subdomain of the NSEC and the DNAME bit is set"<<endl
);
358 if (wildcard
!= owner
&& isCoveredByNSEC(wildcard
, owner
, nsec
->d_next
)) {
359 VLOG(log
, qname
<< ":\tWildcard is covered"<<endl
);
370 This function checks whether the non-existence of a wildcard covering qname|qtype
371 is proven by the NSEC3 records in validrrsets.
372 If `wildcardExists` is not NULL, if will be set to true if a wildcard exists
373 for this qname but doesn't have this qtype.
375 static bool provesNSEC3NoWildCard(const DNSName
& closestEncloser
, uint16_t const qtype
, const cspmap_t
& validrrsets
, bool* wildcardExists
, const OptLog
& log
, pdns::validation::ValidationContext
& context
)
377 auto wildcard
= g_wildcarddnsname
+ closestEncloser
;
378 VLOG(log
, closestEncloser
<< ": Trying to prove that there is no wildcard for "<<wildcard
<<"/"<<QType(qtype
)<<endl
);
380 for (const auto& validset
: validrrsets
) {
381 VLOG(log
, closestEncloser
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
382 if (validset
.first
.second
== QType::NSEC3
) {
383 for (const auto& records
: validset
.second
.records
) {
384 VLOG(log
, closestEncloser
<< ":\t"<<records
->getZoneRepresentation()<<endl
);
385 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(records
);
390 const DNSName signer
= getSigner(validset
.second
.signatures
);
391 if (!validset
.first
.first
.isPartOf(signer
)) {
395 string hash
= getHashFromNSEC3(wildcard
, *nsec3
, context
);
397 VLOG(log
, closestEncloser
<< ": Unsupported hash, ignoring"<<endl
);
400 VLOG(log
, closestEncloser
<< ":\tWildcard hash: "<<toBase32Hex(hash
)<<endl
);
401 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
402 VLOG(log
, closestEncloser
<< ":\tNSEC3 hash: "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
404 if (beginHash
== hash
) {
405 VLOG(log
, closestEncloser
<< ":\tWildcard hash matches");
406 if (wildcardExists
!= nullptr) {
407 *wildcardExists
= true;
410 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
411 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
412 nonexistence of any RRs below that zone cut, which include all RRs at
413 that (original) owner name other than DS RRs, and all RRs below that
414 owner name regardless of type.
416 if (qtype
!= QType::DS
&& isNSEC3AncestorDelegation(signer
, validset
.first
.first
, *nsec3
)) {
417 /* this is an "ancestor delegation" NSEC3 RR */
418 VLOG_NO_PREFIX(log
, " BUT an ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
422 if (qtype
== 0 || isTypeDenied(*nsec3
, QType(qtype
))) {
423 VLOG_NO_PREFIX(log
, " and proves that the type did not exist"<<endl
);
426 VLOG_NO_PREFIX(log
, " BUT the type did exist!"<<endl
);
430 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
431 VLOG(log
, closestEncloser
<< ":\tWildcard hash is covered"<<endl
);
441 dState
matchesNSEC(const DNSName
& name
, uint16_t qtype
, const DNSName
& nsecOwner
, const NSECRecordContent
& nsec
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
>>& signatures
, const OptLog
& log
)
443 const DNSName signer
= getSigner(signatures
);
444 if (!name
.isPartOf(signer
) || !nsecOwner
.isPartOf(signer
)) {
445 return dState::INCONCLUSIVE
;
448 const DNSName owner
= getNSECOwnerName(nsecOwner
, signatures
);
449 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
450 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
451 nonexistence of any RRs below that zone cut, which include all RRs at
452 that (original) owner name other than DS RRs, and all RRs below that
453 owner name regardless of type.
455 if (name
.isPartOf(owner
) && isNSECAncestorDelegation(signer
, owner
, nsec
)) {
456 /* this is an "ancestor delegation" NSEC RR */
457 if (qtype
!= QType::DS
|| name
!= owner
) {
458 VLOG_NO_PREFIX(log
, "An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl
);
459 return dState::NODENIAL
;
463 /* check if the type is denied */
465 if (!isTypeDenied(nsec
, QType(qtype
))) {
466 VLOG_NO_PREFIX(log
, "does _not_ deny existence of type "<<QType(qtype
)<<endl
);
467 return dState::NODENIAL
;
470 if (qtype
== QType::DS
&& signer
== name
) {
471 VLOG_NO_PREFIX(log
, "the NSEC comes from the child zone and cannot be used to deny a DS"<<endl
);
472 return dState::NODENIAL
;
475 VLOG_NO_PREFIX(log
, "Denies existence of type "<<QType(qtype
)<<endl
);
476 return dState::NXQTYPE
;
479 if (name
.isPartOf(owner
) && nsec
.isSet(QType::DNAME
)) {
480 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
482 In any negative response, the NSEC or NSEC3 [RFC5155] record type
483 bitmap SHOULD be checked to see that there was no DNAME that could
484 have been applied. If the DNAME bit in the type bitmap is set and
485 the query name is a subdomain of the closest encloser that is
486 asserted, then DNAME substitution should have been done, but the
487 substitution has not been done as specified.
489 VLOG(log
, "the DNAME bit is set and the query name is a subdomain of that NSEC");
490 return dState::NODENIAL
;
493 if (isCoveredByNSEC(name
, owner
, nsec
.d_next
)) {
494 VLOG_NO_PREFIX(log
, name
<< ": is covered by ("<<owner
<<" to "<<nsec
.d_next
<<")");
496 if (nsecProvesENT(name
, owner
, nsec
.d_next
)) {
497 VLOG_NO_PREFIX(log
, " denies existence of type "<<name
<<"/"<<QType(qtype
)<<" by proving that "<<name
<<" is an ENT"<<endl
);
498 return dState::NXQTYPE
;
501 return dState::NXDOMAIN
;
504 return dState::INCONCLUSIVE
;
507 [[nodiscard
]] uint64_t getNSEC3DenialProofWorstCaseIterationsCount(uint8_t maxLabels
, uint16_t iterations
, size_t saltLength
)
509 return static_cast<uint64_t>((iterations
+ 1U + (saltLength
> 0 ? 1U : 0U))) * maxLabels
;
513 This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
515 - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODENIAL
516 if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
517 - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
518 NXQTYPE if the name is proven to be ENT and NXDOMAIN otherwise.
519 - If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
520 useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
523 dState
getDenial(const cspmap_t
&validrrsets
, const DNSName
& qname
, const uint16_t qtype
, bool referralToUnsigned
, bool wantsNoDataProof
, pdns::validation::ValidationContext
& context
, const OptLog
& log
, bool needWildcardProof
, unsigned int wildcardLabelsCount
) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
525 bool nsec3Seen
= false;
526 if (!needWildcardProof
&& wildcardLabelsCount
== 0) {
527 throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
530 uint8_t numberOfLabelsOfParentZone
{std::numeric_limits
<uint8_t>::max()};
531 uint16_t nsec3sConsidered
= 0;
532 for (const auto& validset
: validrrsets
) {
533 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
535 if (validset
.first
.second
==QType::NSEC
) {
536 for (const auto& record
: validset
.second
.records
) {
537 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
539 if (validset
.second
.signatures
.empty()) {
543 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(record
);
548 const DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
549 const DNSName signer
= getSigner(validset
.second
.signatures
);
550 if (!validset
.first
.first
.isPartOf(signer
) || !owner
.isPartOf(signer
) ) {
554 /* The NSEC is either a delegation one, from the parent zone, and
555 * must have the NS bit set but not the SOA one, or a regular NSEC
556 * either at apex (signer == owner) or with the SOA or NS bits clear.
558 const bool notApex
= signer
.countLabels() < owner
.countLabels();
559 if (notApex
&& nsec
->isSet(QType::NS
) && nsec
->isSet(QType::SOA
)) {
560 VLOG(log
, qname
<< ": However, that NSEC is not at the apex and has both the NS and the SOA bits set!"<<endl
);
564 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
565 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
566 nonexistence of any RRs below that zone cut, which include all RRs at
567 that (original) owner name other than DS RRs, and all RRs below that
568 owner name regardless of type.
570 if (qname
.isPartOf(owner
) && isNSECAncestorDelegation(signer
, owner
, *nsec
)) {
571 /* this is an "ancestor delegation" NSEC RR */
572 if (qtype
!= QType::DS
|| qname
!= owner
) {
573 VLOG(log
, qname
<< ": An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl
);
574 return dState::NODENIAL
;
578 if (qtype
== QType::DS
&& !qname
.isRoot() && signer
== qname
) {
579 VLOG(log
, qname
<< ": A NSEC RR from the child zone cannot deny the existence of a DS"<<endl
);
583 /* check if the type is denied */
584 if (qname
== owner
) {
585 if (!isTypeDenied(*nsec
, QType(qtype
))) {
586 VLOG(log
, qname
<< ": Does _not_ deny existence of type "<<QType(qtype
)<<endl
);
587 return dState::NODENIAL
;
590 VLOG(log
, qname
<< ": Denies existence of type "<<QType(qtype
)<<endl
);
593 * RFC 4035 Section 2.3:
594 * The bitmap for the NSEC RR at a delegation point requires special
595 * attention. Bits corresponding to the delegation NS RRset and any
596 * RRsets for which the parent zone has authoritative data MUST be set
598 if (referralToUnsigned
&& qtype
== QType::DS
) {
599 if (!nsec
->isSet(QType::NS
)) {
600 VLOG(log
, qname
<< ": However, no NS record exists at this level!"<<endl
);
601 return dState::NODENIAL
;
605 /* we know that the name exists (but this qtype doesn't) so except
606 if the answer was generated by a wildcard expansion, no wildcard
607 could have matched (rfc4035 section 5.4 bullet 1) */
608 if (needWildcardProof
&& (!isWildcardExpanded(owner
, validset
.second
.signatures
) || isWildcardExpandedOntoItself(owner
, validset
.second
.signatures
))) {
609 needWildcardProof
= false;
612 if (!needWildcardProof
) {
613 return dState::NXQTYPE
;
616 DNSName closestEncloser
= getClosestEncloserFromNSEC(qname
, owner
, nsec
->d_next
);
617 if (provesNoWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
618 return dState::NXQTYPE
;
621 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<endl
);
622 return dState::NODENIAL
;
625 if (qname
.isPartOf(owner
) && nsec
->isSet(QType::DNAME
)) {
626 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
628 In any negative response, the NSEC or NSEC3 [RFC5155] record type
629 bitmap SHOULD be checked to see that there was no DNAME that could
630 have been applied. If the DNAME bit in the type bitmap is set and
631 the query name is a subdomain of the closest encloser that is
632 asserted, then DNAME substitution should have been done, but the
633 substitution has not been done as specified.
635 VLOG(log
, qname
<< ": The DNAME bit is set and the query name is a subdomain of that NSEC"<< endl
);
636 return dState::NODENIAL
;
639 /* check if the whole NAME is denied existing */
640 if (isCoveredByNSEC(qname
, owner
, nsec
->d_next
)) {
641 VLOG(log
, qname
<< ": Is covered by ("<<owner
<<" to "<<nsec
->d_next
<<") ");
643 if (nsecProvesENT(qname
, owner
, nsec
->d_next
)) {
644 if (wantsNoDataProof
) {
645 /* if the name is an ENT and we received a NODATA answer,
646 we are fine with a NSEC proving that the name does not exist. */
647 VLOG_NO_PREFIX(log
, "Denies existence of type "<<qname
<<"/"<<QType(qtype
)<<" by proving that "<<qname
<<" is an ENT"<<endl
);
648 return dState::NXQTYPE
;
650 /* but for a NXDOMAIN proof, this doesn't make sense! */
651 VLOG_NO_PREFIX(log
, "but it tries to deny the existence of "<<qname
<<" by proving that "<<qname
<<" is an ENT, this does not make sense!"<<endl
);
652 return dState::NODENIAL
;
655 if (!needWildcardProof
) {
656 VLOG_NO_PREFIX(log
, "and we did not need a wildcard proof"<<endl
);
657 return dState::NXDOMAIN
;
660 VLOG_NO_PREFIX(log
, "but we do need a wildcard proof so ");
661 DNSName closestEncloser
= getClosestEncloserFromNSEC(qname
, owner
, nsec
->d_next
);
662 if (wantsNoDataProof
) {
663 VLOG_NO_PREFIX(log
, "looking for NODATA proof"<<endl
);
664 if (provesNoDataWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
665 return dState::NXQTYPE
;
669 VLOG_NO_PREFIX(log
, "looking for NO wildcard proof"<<endl
);
670 if (provesNoWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
671 return dState::NXDOMAIN
;
675 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<endl
);
676 return dState::NODENIAL
;
679 VLOG(log
, qname
<< ": Did not deny existence of "<<QType(qtype
)<<", "<<validset
.first
.first
<<"?="<<qname
<<", "<<nsec
->isSet(qtype
)<<", next: "<<nsec
->d_next
<<endl
);
681 } else if(validset
.first
.second
==QType::NSEC3
) {
682 for (const auto& record
: validset
.second
.records
) {
683 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
684 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
689 if (validset
.second
.signatures
.empty()) {
693 const DNSName
& hashedOwner
= validset
.first
.first
;
694 const DNSName signer
= getSigner(validset
.second
.signatures
);
695 if (!hashedOwner
.isPartOf(signer
)) {
696 VLOG(log
, qname
<< ": Owner "<<hashedOwner
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
699 numberOfLabelsOfParentZone
= std::min(numberOfLabelsOfParentZone
, static_cast<uint8_t>(signer
.countLabels()));
701 if (qtype
== QType::DS
&& !qname
.isRoot() && signer
== qname
) {
702 VLOG(log
, qname
<< ": A NSEC3 RR from the child zone cannot deny the existence of a DS"<<endl
);
706 if (g_maxNSEC3sPerRecordToConsider
> 0 && nsec3sConsidered
>= g_maxNSEC3sPerRecordToConsider
) {
707 VLOG(log
, qname
<< ": Too many NSEC3s for this record"<<endl
);
708 context
.d_limitHit
= true;
709 return dState::NODENIAL
;
713 string hash
= getHashFromNSEC3(qname
, *nsec3
, context
);
715 VLOG(log
, qname
<< ": Unsupported hash, ignoring"<<endl
);
716 return dState::INSECURE
;
721 VLOG(log
, qname
<< ":\tquery hash: "<<toBase32Hex(hash
)<<endl
);
722 string beginHash
= fromBase32Hex(hashedOwner
.getRawLabels()[0]);
724 // If the name exists, check if the qtype is denied
725 if (beginHash
== hash
) {
727 /* The NSEC3 is either a delegation one, from the parent zone, and
728 * must have the NS bit set but not the SOA one, or a regular NSEC3
729 * either at apex (signer == owner) or with the SOA or NS bits clear.
731 const bool notApex
= signer
.countLabels() < qname
.countLabels();
732 if (notApex
&& nsec3
->isSet(QType::NS
) && nsec3
->isSet(QType::SOA
)) {
733 VLOG(log
, qname
<< ": However, that NSEC3 is not at the apex and has both the NS and the SOA bits set!"<<endl
);
737 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
738 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
739 nonexistence of any RRs below that zone cut, which include all RRs at
740 that (original) owner name other than DS RRs, and all RRs below that
741 owner name regardless of type.
743 if (qtype
!= QType::DS
&& isNSEC3AncestorDelegation(signer
, qname
, *nsec3
)) {
744 /* this is an "ancestor delegation" NSEC3 RR */
745 VLOG(log
, qname
<< ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
746 return dState::NODENIAL
;
749 if (!isTypeDenied(*nsec3
, QType(qtype
))) {
750 VLOG(log
, qname
<< ": Does _not_ deny existence of type "<<QType(qtype
)<<" for name "<<qname
<<" (not opt-out)."<<endl
);
751 return dState::NODENIAL
;
754 VLOG(log
, qname
<< ": Denies existence of type "<<QType(qtype
)<<" for name "<<qname
<<" (not opt-out)."<<endl
);
757 * RFC 5155 section 8.9:
758 * If there is an NSEC3 RR present in the response that matches the
759 * delegation name, then the validator MUST ensure that the NS bit is
760 * set and that the DS bit is not set in the Type Bit Maps field of the
763 if (referralToUnsigned
&& qtype
== QType::DS
) {
764 if (!nsec3
->isSet(QType::NS
)) {
765 VLOG(log
, qname
<< ": However, no NS record exists at this level!"<<endl
);
766 return dState::NODENIAL
;
770 return dState::NXQTYPE
;
776 /* if we have no NSEC3 records, we are done */
778 return dState::NODENIAL
;
781 DNSName
closestEncloser(qname
);
783 if (needWildcardProof
) {
784 /* We now need to look for a NSEC3 covering the closest (provable) encloser
785 RFC 5155 section-7.2.1
788 VLOG(log
, qname
<< ": Now looking for the closest encloser for "<<qname
<<endl
);
790 while (!found
&& closestEncloser
.chopOff() && closestEncloser
.countLabels() >= numberOfLabelsOfParentZone
) {
791 nsec3sConsidered
= 0;
793 for(const auto& validset
: validrrsets
) {
794 if(validset
.first
.second
==QType::NSEC3
) {
795 for(const auto& record
: validset
.second
.records
) {
796 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
797 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
802 const DNSName signer
= getSigner(validset
.second
.signatures
);
803 if (!validset
.first
.first
.isPartOf(signer
)) {
804 VLOG(log
, qname
<< ": Owner "<<validset
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
808 if (g_maxNSEC3sPerRecordToConsider
> 0 && nsec3sConsidered
>= g_maxNSEC3sPerRecordToConsider
) {
809 VLOG(log
, qname
<< ": Too many NSEC3s for this record"<<endl
);
810 context
.d_limitHit
= true;
811 return dState::NODENIAL
;
815 string hash
= getHashFromNSEC3(closestEncloser
, *nsec3
, context
);
817 VLOG(log
, qname
<< ": Unsupported hash, ignoring"<<endl
);
818 return dState::INSECURE
;
821 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
823 VLOG(log
, qname
<< ": Comparing "<<toBase32Hex(hash
)<<" ("<<closestEncloser
<<") against "<<toBase32Hex(beginHash
)<<endl
);
824 if (beginHash
== hash
) {
825 /* If the closest encloser is a delegation NS we know nothing about the names in the child zone. */
826 if (isNSEC3AncestorDelegation(signer
, validset
.first
.first
, *nsec3
)) {
827 VLOG(log
, qname
<< ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
831 VLOG(log
, qname
<< ": Closest encloser for "<<qname
<<" is "<<closestEncloser
<<endl
);
834 if (nsec3
->isSet(QType::DNAME
)) {
835 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
837 In any negative response, the NSEC or NSEC3 [RFC5155] record type
838 bitmap SHOULD be checked to see that there was no DNAME that could
839 have been applied. If the DNAME bit in the type bitmap is set and
840 the query name is a subdomain of the closest encloser that is
841 asserted, then DNAME substitution should have been done, but the
842 substitution has not been done as specified.
844 VLOG(log
, qname
<< ":\tThe closest encloser NSEC3 has the DNAME bit is set"<<endl
);
845 return dState::NODENIAL
;
859 /* RFC 5155 section-7.2.6:
860 "It is not necessary to return an NSEC3 RR that matches the closest encloser,
861 as the existence of this closest encloser is proven by the presence of the
862 expanded wildcard in the response.
865 unsigned int closestEncloserLabelsCount
= closestEncloser
.countLabels();
866 while (wildcardLabelsCount
> 0 && closestEncloserLabelsCount
> wildcardLabelsCount
) {
867 closestEncloser
.chopOff();
868 closestEncloserLabelsCount
--;
872 bool nextCloserFound
= false;
873 bool isOptOut
= false;
876 /* now that we have found the closest (provable) encloser,
877 we can construct the next closer (RFC7129 section-5.5) name
878 and look for a NSEC3 RR covering it */
879 unsigned int labelIdx
= qname
.countLabels() - closestEncloser
.countLabels();
881 DNSName
nextCloser(closestEncloser
);
882 nextCloser
.prependRawLabel(qname
.getRawLabel(labelIdx
- 1));
883 nsec3sConsidered
= 0;
884 VLOG(log
, qname
<< ":Looking for a NSEC3 covering the next closer name "<<nextCloser
<<endl
);
886 for (const auto& validset
: validrrsets
) {
887 if (validset
.first
.second
== QType::NSEC3
) {
888 for (const auto& record
: validset
.second
.records
) {
889 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
890 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
895 if (g_maxNSEC3sPerRecordToConsider
> 0 && nsec3sConsidered
>= g_maxNSEC3sPerRecordToConsider
) {
896 VLOG(log
, qname
<< ": Too many NSEC3s for this record"<<endl
);
897 context
.d_limitHit
= true;
898 return dState::NODENIAL
;
902 string hash
= getHashFromNSEC3(nextCloser
, *nsec3
, context
);
904 VLOG(log
, qname
<< ": Unsupported hash, ignoring"<<endl
);
905 return dState::INSECURE
;
908 const DNSName signer
= getSigner(validset
.second
.signatures
);
909 if (!validset
.first
.first
.isPartOf(signer
)) {
910 VLOG(log
, qname
<< ": Owner "<<validset
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
914 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
916 VLOG(log
, qname
<< ": Comparing "<<toBase32Hex(hash
)<<" against "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
917 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
918 VLOG(log
, qname
<< ": Denies existence of name "<<qname
<<"/"<<QType(qtype
));
919 nextCloserFound
= true;
921 if (nsec3
->isOptOut()) {
922 VLOG_NO_PREFIX(log
, " but is opt-out!");
926 VLOG_NO_PREFIX(log
, endl
);
929 VLOG(log
, qname
<< ": Did not cover us ("<<qname
<<"), start="<<validset
.first
.first
<<", us="<<toBase32Hex(hash
)<<", end="<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
932 if (nextCloserFound
) {
939 if (nextCloserFound
) {
940 bool wildcardExists
= false;
941 /* RFC 7129 section-5.6 */
942 if (needWildcardProof
&& !provesNSEC3NoWildCard(closestEncloser
, qtype
, validrrsets
, &wildcardExists
, log
, context
)) {
944 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<QType(qtype
)<<endl
);
945 return dState::NODENIAL
;
950 return dState::OPTOUT
;
952 if (wildcardExists
) {
953 return dState::NXQTYPE
;
955 return dState::NXDOMAIN
;
958 // There were no valid NSEC(3) records
959 return dState::NODENIAL
;
962 bool isRRSIGNotExpired(const time_t now
, const RRSIGRecordContent
& sig
)
964 // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
965 return sig
.d_sigexpire
>= now
;
968 bool isRRSIGIncepted(const time_t now
, const RRSIGRecordContent
& sig
)
970 // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
971 return sig
.d_siginception
- g_signatureInceptionSkew
<= now
;
975 [[nodiscard
]] bool checkSignatureInceptionAndExpiry(const DNSName
& qname
, time_t now
, const RRSIGRecordContent
& sig
, vState
& ede
, const OptLog
& log
)
978 - 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.
979 - 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.
981 if (isRRSIGIncepted(now
, sig
) && isRRSIGNotExpired(now
, sig
)) {
984 ede
= ((sig
.d_siginception
- g_signatureInceptionSkew
) > now
) ? vState::BogusSignatureNotYetValid
: vState::BogusSignatureExpired
;
985 VLOG(log
, qname
<< ": Signature is "<<(ede
== vState::BogusSignatureNotYetValid
? "not yet valid" : "expired")<<" (inception: "<<sig
.d_siginception
<<", inception skew: "<<g_signatureInceptionSkew
<<", expiration: "<<sig
.d_sigexpire
<<", now: "<<now
<<")"<<endl
);
989 [[nodiscard
]] bool checkSignatureWithKey(const DNSName
& qname
, const RRSIGRecordContent
& sig
, const DNSKEYRecordContent
& key
, const std::string
& msg
, vState
& ede
, const OptLog
& log
)
993 auto dke
= DNSCryptoKeyEngine::makeFromPublicKeyString(key
.d_algorithm
, key
.d_key
);
994 result
= dke
->verify(msg
, sig
.d_signature
);
995 VLOG(log
, qname
<< ": Signature by key with tag "<<sig
.d_tag
<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig
.d_algorithm
)<<" was " << (result
? "" : "NOT ")<<"valid"<<endl
);
997 ede
= vState::BogusNoValidRRSIG
;
1000 catch (const std::exception
& e
) {
1001 VLOG(log
, qname
<< ": Could not make a validator for signature: "<<e
.what()<<endl
);
1002 ede
= vState::BogusUnsupportedDNSKEYAlgo
;
1009 vState
validateWithKeySet(time_t now
, const DNSName
& name
, const sortedRecords_t
& toSign
, const vector
<shared_ptr
<const RRSIGRecordContent
> >& signatures
, const skeyset_t
& keys
, const OptLog
& log
, pdns::validation::ValidationContext
& context
, bool validateAllSigs
)
1011 bool missingKey
= false;
1012 bool isValid
= false;
1013 bool allExpired
= true;
1014 bool noneIncepted
= true;
1015 uint16_t signaturesConsidered
= 0;
1017 for (const auto& signature
: signatures
) {
1018 unsigned int labelCount
= name
.countLabels();
1019 if (signature
->d_labels
> labelCount
) {
1020 VLOG(log
, name
<<": Discarding invalid RRSIG whose label count is "<<signature
->d_labels
<<" while the RRset owner name has only "<<labelCount
<<endl
);
1024 vState ede
= vState::Indeterminate
;
1025 if (!checkSignatureInceptionAndExpiry(name
, now
, *signature
, ede
, log
)) {
1026 if (isRRSIGIncepted(now
, *signature
)) {
1027 noneIncepted
= false;
1029 if (isRRSIGNotExpired(now
, *signature
)) {
1035 if (g_maxRRSIGsPerRecordToConsider
> 0 && signaturesConsidered
>= g_maxRRSIGsPerRecordToConsider
) {
1036 VLOG(log
, name
<<": We have already considered "<<std::to_string(signaturesConsidered
)<<" RRSIG"<<addS(signaturesConsidered
)<<" for this record, stopping now"<<endl
;);
1037 // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1038 context
.d_limitHit
= true;
1041 signaturesConsidered
++;
1042 context
.d_validationsCounter
++;
1044 auto keysMatchingTag
= getByTag(keys
, signature
->d_tag
, signature
->d_algorithm
, log
);
1046 if (keysMatchingTag
.empty()) {
1047 VLOG(log
, name
<< ": No key provided for "<<signature
->d_tag
<<" and algorithm "<<std::to_string(signature
->d_algorithm
)<<endl
;);
1052 string msg
= getMessageForRRSET(name
, *signature
, toSign
, true);
1053 uint16_t dnskeysConsidered
= 0;
1054 for (const auto& key
: keysMatchingTag
) {
1055 if (g_maxDNSKEYsToConsider
> 0 && dnskeysConsidered
>= g_maxDNSKEYsToConsider
) {
1056 VLOG(log
, name
<< ": We have already considered "<<std::to_string(dnskeysConsidered
)<<" DNSKEY"<<addS(dnskeysConsidered
)<<" for tag "<<std::to_string(signature
->d_tag
)<<" and algorithm "<<std::to_string(signature
->d_algorithm
)<<", not considering the remaining ones for this signature"<<endl
;);
1058 context
.d_limitHit
= true;
1060 return isValid
? vState::Secure
: vState::BogusNoValidRRSIG
;
1062 dnskeysConsidered
++;
1064 bool signIsValid
= checkSignatureWithKey(name
, *signature
, *key
, msg
, ede
, log
);
1068 VLOG(log
, name
<< ": Validated "<<name
<<"/"<<DNSRecordContent::NumberToType(signature
->d_type
)<<endl
);
1069 // cerr<<"valid"<<endl;
1070 // cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
1073 VLOG(log
, name
<< ": signature invalid"<<endl
);
1074 if (isRRSIGIncepted(now
, *signature
)) {
1075 noneIncepted
= false;
1077 if (isRRSIGNotExpired(now
, *signature
)) {
1082 if (signIsValid
&& !validateAllSigs
) {
1083 return vState::Secure
;
1089 return vState::Secure
;
1092 return vState::BogusNoValidRRSIG
;
1095 // ede should be vState::BogusSignatureNotYetValid
1096 return vState::BogusSignatureNotYetValid
;
1099 // ede should be vState::BogusSignatureExpired);
1100 return vState::BogusSignatureExpired
;
1103 return vState::BogusNoValidRRSIG
;
1106 bool getTrustAnchor(const map
<DNSName
,dsmap_t
>& anchors
, const DNSName
& zone
, dsmap_t
&res
)
1108 const auto& iter
= anchors
.find(zone
);
1110 if (iter
== anchors
.cend()) {
1118 bool haveNegativeTrustAnchor(const map
<DNSName
,std::string
>& negAnchors
, const DNSName
& zone
, std::string
& reason
)
1120 const auto& iter
= negAnchors
.find(zone
);
1122 if (iter
== negAnchors
.cend()) {
1126 reason
= iter
->second
;
1130 vState
validateDNSKeysAgainstDS(time_t now
, const DNSName
& zone
, const dsmap_t
& dsmap
, const skeyset_t
& tkeys
, const sortedRecords_t
& toSign
, const vector
<shared_ptr
<const RRSIGRecordContent
> >& sigs
, skeyset_t
& validkeys
, const OptLog
& log
, pdns::validation::ValidationContext
& context
)
1133 * Check all DNSKEY records against all DS records and place all DNSKEY records
1134 * that have DS records (that we support the algo for) in the tentative key storage
1136 uint16_t dssConsidered
= 0;
1137 for (const auto& dsrc
: dsmap
) {
1138 if (g_maxDSsToConsider
> 0 && dssConsidered
> g_maxDSsToConsider
) {
1139 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(dssConsidered
)<<" DS"<<addS(dssConsidered
)<<", not considering the remaining ones"<<endl
;);
1140 return vState::BogusNoValidDNSKEY
;
1144 uint16_t dnskeysConsidered
= 0;
1145 auto record
= getByTag(tkeys
, dsrc
.d_tag
, dsrc
.d_algorithm
, log
);
1146 // 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;
1148 for (const auto& drc
: record
) {
1149 bool isValid
= false;
1150 bool dsCreated
= false;
1151 DSRecordContent dsrc2
;
1153 if (g_maxDNSKEYsToConsider
> 0 && dnskeysConsidered
>= g_maxDNSKEYsToConsider
) {
1154 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(dnskeysConsidered
)<<" DNSKEY"<<addS(dnskeysConsidered
)<<" for tag "<<std::to_string(dsrc
.d_tag
)<<" and algorithm "<<std::to_string(dsrc
.d_algorithm
)<<", not considering the remaining ones for this DS"<<endl
;);
1155 // we need to break because we can have a partially validated set
1156 // where the KSK signs the ZSK(s), and even if we don't
1157 // we are going to try to get the correct EDE status (revoked, expired, ...)
1158 context
.d_limitHit
= true;
1161 dnskeysConsidered
++;
1164 dsrc2
= makeDSFromDNSKey(zone
, *drc
, dsrc
.d_digesttype
);
1166 isValid
= dsrc
== dsrc2
;
1168 catch (const std::exception
&e
) {
1169 VLOG(log
, zone
<< ": Unable to make DS from DNSKey: "<<e
.what()<<endl
);
1173 VLOG(log
, zone
<< ": got valid DNSKEY (it matches the DS) with tag "<<dsrc
.d_tag
<<" and algorithm "<<std::to_string(dsrc
.d_algorithm
)<<" for "<<zone
<<endl
);
1175 validkeys
.insert(drc
);
1179 VLOG(log
, zone
<< ": DNSKEY did not match the DS, parent DS: "<<dsrc
.getZoneRepresentation() << " ! = "<<dsrc2
.getZoneRepresentation()<<endl
);
1185 vState ede
= vState::BogusNoValidDNSKEY
;
1187 // cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
1188 // these counts could be off if we somehow ended up with
1189 // duplicate keys. Should switch to a type that prevents that.
1190 if (!tkeys
.empty() && validkeys
.size() < tkeys
.size()) {
1191 // this should mean that we have one or more DS-validated DNSKEYs
1192 // but not a fully validated DNSKEY set, yet
1193 // one of these valid DNSKEYs should be able to validate the
1195 uint16_t signaturesConsidered
= 0;
1196 for (const auto& sig
: sigs
) {
1197 if (!checkSignatureInceptionAndExpiry(zone
, now
, *sig
, ede
, log
)) {
1201 // 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;
1202 auto bytag
= getByTag(validkeys
, sig
->d_tag
, sig
->d_algorithm
, log
);
1204 if (bytag
.empty()) {
1208 if (g_maxRRSIGsPerRecordToConsider
> 0 && signaturesConsidered
>= g_maxRRSIGsPerRecordToConsider
) {
1209 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(signaturesConsidered
)<<" RRSIG"<<addS(signaturesConsidered
)<<" for this record, stopping now"<<endl
;);
1210 // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1211 context
.d_limitHit
= true;
1212 return vState::BogusNoValidDNSKEY
;
1215 string msg
= getMessageForRRSET(zone
, *sig
, toSign
);
1216 uint16_t dnskeysConsidered
= 0;
1217 for (const auto& key
: bytag
) {
1218 if (g_maxDNSKEYsToConsider
> 0 && dnskeysConsidered
>= g_maxDNSKEYsToConsider
) {
1219 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(dnskeysConsidered
)<<" DNSKEY"<<addS(dnskeysConsidered
)<<" for tag "<<std::to_string(sig
->d_tag
)<<" and algorithm "<<std::to_string(sig
->d_algorithm
)<<", not considering the remaining ones for this signature"<<endl
;);
1220 context
.d_limitHit
= true;
1221 return vState::BogusNoValidDNSKEY
;
1223 dnskeysConsidered
++;
1225 if (g_maxRRSIGsPerRecordToConsider
> 0 && signaturesConsidered
>= g_maxRRSIGsPerRecordToConsider
) {
1226 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(signaturesConsidered
)<<" RRSIG"<<addS(signaturesConsidered
)<<" for this record, stopping now"<<endl
;);
1227 // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1228 context
.d_limitHit
= true;
1229 return vState::BogusNoValidDNSKEY
;
1231 // cerr<<"validating : ";
1232 bool signIsValid
= checkSignatureWithKey(zone
, *sig
, *key
, msg
, ede
, log
);
1233 signaturesConsidered
++;
1234 context
.d_validationsCounter
++;
1237 VLOG(log
, zone
<< ": Validation succeeded - whole DNSKEY set is valid"<<endl
);
1241 VLOG(log
, zone
<< ": Validation did not succeed!"<<endl
);
1244 if (validkeys
.size() == tkeys
.size()) {
1245 // we validated the whole DNSKEY set already */
1248 // if(validkeys.empty()) cerr<<"did not manage to validate DNSKEY set based on DS-validated KSK, only passing KSK on"<<endl;
1252 if (validkeys
.size() < tkeys
.size()) {
1253 /* so we failed to validate the whole set, let's try to find out why exactly */
1254 bool dnskeyAlgoSupported
= false;
1255 bool dsDigestSupported
= false;
1257 for (const auto& dsrc
: dsmap
)
1259 if (DNSCryptoKeyEngine::isAlgorithmSupported(dsrc
.d_algorithm
)) {
1260 dnskeyAlgoSupported
= true;
1261 if (DNSCryptoKeyEngine::isDigestSupported(dsrc
.d_digesttype
)) {
1262 dsDigestSupported
= true;
1267 if (!dnskeyAlgoSupported
) {
1268 return vState::BogusUnsupportedDNSKEYAlgo
;
1270 if (!dsDigestSupported
) {
1271 return vState::BogusUnsupportedDSDigestType
;
1274 bool zoneKey
= false;
1275 bool notRevoked
= false;
1276 bool validProtocol
= false;
1278 for (const auto& key
: tkeys
) {
1279 if (!isAZoneKey(*key
)) {
1284 if (isRevokedKey(*key
)) {
1289 if (key
->d_protocol
!= 3) {
1292 validProtocol
= true;
1296 return vState::BogusNoZoneKeyBitSet
;
1299 return vState::BogusRevokedDNSKEY
;
1301 if (!validProtocol
) {
1302 return vState::BogusInvalidDNSKEYProtocol
;
1308 return vState::Secure
;
1311 bool isSupportedDS(const DSRecordContent
& dsrec
, const OptLog
& log
)
1313 if (!DNSCryptoKeyEngine::isAlgorithmSupported(dsrec
.d_algorithm
)) {
1314 VLOG(log
, "Discarding DS "<<dsrec
.d_tag
<<" because we don't support algorithm number "<<std::to_string(dsrec
.d_algorithm
)<<endl
);
1318 if (!DNSCryptoKeyEngine::isDigestSupported(dsrec
.d_digesttype
)) {
1319 VLOG(log
, "Discarding DS "<<dsrec
.d_tag
<<" because we don't support digest number "<<std::to_string(dsrec
.d_digesttype
)<<endl
);
1326 DNSName
getSigner(const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
1328 for (const auto& sig
: signatures
) {
1330 return sig
->d_signer
;
1337 const std::string
& vStateToString(vState state
)
1339 static const std::vector
<std::string
> vStates
= {"Indeterminate", "Insecure", "Secure", "NTA", "TA", "Bogus - No valid DNSKEY", "Bogus - Invalid denial", "Bogus - Unable to get DSs", "Bogus - Unable to get DNSKEYs", "Bogus - Self Signed DS", "Bogus - No RRSIG", "Bogus - No valid RRSIG", "Bogus - Missing negative indication", "Bogus - Signature not yet valid", "Bogus - Signature expired", "Bogus - Unsupported DNSKEY algorithm", "Bogus - Unsupported DS digest type", "Bogus - No zone key bit set", "Bogus - Revoked DNSKEY", "Bogus - Invalid DNSKEY Protocol" };
1340 return vStates
.at(static_cast<size_t>(state
));
1343 std::ostream
& operator<<(std::ostream
&ostr
, const vState dstate
)
1345 ostr
<<vStateToString(dstate
);
1349 std::ostream
& operator<<(std::ostream
&ostr
, const dState dstate
)
1351 static const std::vector
<std::string
> dStates
= {"no denial", "inconclusive", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
1352 ostr
<<dStates
.at(static_cast<size_t>(dstate
));
1356 void updateDNSSECValidationState(vState
& state
, const vState stateUpdate
)
1358 if (stateUpdate
== vState::TA
) {
1359 state
= vState::Secure
;
1361 else if (stateUpdate
== vState::NTA
) {
1362 state
= vState::Insecure
;
1364 else if (vStateIsBogus(stateUpdate
) || state
== vState::Indeterminate
) {
1365 state
= stateUpdate
;
1367 else if (stateUpdate
== vState::Insecure
) {
1368 if (!vStateIsBogus(state
)) {
1369 state
= vState::Insecure
;