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
) {
183 const string hash
= getHashFromNSEC3(zone
, *nsec3
, context
);
188 const string beginHash
= fromBase32Hex(record
.d_name
.getRawLabels()[0]);
189 if (beginHash
== hash
) {
190 return !nsec3
->isSet(QType::NS
);
193 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
194 return !(nsec3
->isOptOut());
202 /* RFC 4035 section-5.3.4:
203 "If the number of labels in an RRset's owner name is greater than the
204 Labels field of the covering RRSIG RR, then the RRset and its
205 covering RRSIG RR were created as a result of wildcard expansion."
207 bool isWildcardExpanded(unsigned int labelCount
, const RRSIGRecordContent
& sign
)
209 return sign
.d_labels
< labelCount
;
212 static bool isWildcardExpanded(const DNSName
& owner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
214 if (signatures
.empty()) {
218 const auto& sign
= signatures
.at(0);
219 unsigned int labelsCount
= owner
.countLabels();
220 return isWildcardExpanded(labelsCount
, *sign
);
223 bool isWildcardExpandedOntoItself(const DNSName
& owner
, unsigned int labelCount
, const RRSIGRecordContent
& sign
)
225 /* this is a wildcard alright, but it has not been expanded */
226 return owner
.isWildcard() && (labelCount
- 1) == sign
.d_labels
;
229 static bool isWildcardExpandedOntoItself(const DNSName
& owner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
231 if (signatures
.empty()) {
235 const auto& sign
= signatures
.at(0);
236 unsigned int labelsCount
= owner
.countLabels();
237 return isWildcardExpandedOntoItself(owner
, labelsCount
, *sign
);
240 /* if this is a wildcard NSEC, the owner name has been modified
241 to match the name. Make sure we use the original '*' form. */
242 DNSName
getNSECOwnerName(const DNSName
& initialOwner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
244 DNSName result
= initialOwner
;
246 if (signatures
.empty()) {
250 const auto& sign
= signatures
.at(0);
251 unsigned int labelsCount
= initialOwner
.countLabels();
252 if (sign
&& sign
->d_labels
< labelsCount
) {
257 while (sign
->d_labels
< labelsCount
);
259 result
= g_wildcarddnsname
+ result
;
265 static bool isNSECAncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const NSECRecordContent
& nsec
)
267 return nsec
.isSet(QType::NS
) &&
268 !nsec
.isSet(QType::SOA
) &&
269 signer
.countLabels() < owner
.countLabels();
272 bool isNSEC3AncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const NSEC3RecordContent
& nsec3
)
274 return nsec3
.isSet(QType::NS
) &&
275 !nsec3
.isSet(QType::SOA
) &&
276 signer
.countLabels() < owner
.countLabels();
279 static bool provesNoDataWildCard(const DNSName
& qname
, const uint16_t qtype
, const DNSName
& closestEncloser
, const cspmap_t
& validrrsets
, const OptLog
& log
)
281 const DNSName wildcard
= g_wildcarddnsname
+ closestEncloser
;
282 VLOG(log
, qname
<< ": Trying to prove that there is no data in wildcard for "<<qname
<<"/"<<QType(qtype
)<<endl
);
283 for (const auto& validset
: validrrsets
) {
284 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
285 if (validset
.first
.second
== QType::NSEC
) {
286 for (const auto& record
: validset
.second
.records
) {
287 VLOG(log
, ":\t"<<record
->getZoneRepresentation()<<endl
);
288 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(record
);
293 DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
294 if (owner
!= wildcard
) {
298 VLOG(log
, qname
<< ":\tWildcard matches");
299 if (qtype
== 0 || isTypeDenied(*nsec
, QType(qtype
))) {
300 VLOG_NO_PREFIX(log
, " and proves that the type did not exist"<<endl
);
303 VLOG_NO_PREFIX(log
, " BUT the type did exist!"<<endl
);
312 DNSName
getClosestEncloserFromNSEC(const DNSName
& name
, const DNSName
& owner
, const DNSName
& next
)
314 DNSName
commonWithOwner(name
.getCommonLabels(owner
));
315 DNSName
commonWithNext(name
.getCommonLabels(next
));
316 if (commonWithOwner
.countLabels() >= commonWithNext
.countLabels()) {
317 return commonWithOwner
;
319 return commonWithNext
;
323 This function checks whether the non-existence of a wildcard covering qname|qtype
324 is proven by the NSEC records in validrrsets.
326 static bool provesNoWildCard(const DNSName
& qname
, const uint16_t qtype
, const DNSName
& closestEncloser
, const cspmap_t
& validrrsets
, const OptLog
& log
)
328 VLOG(log
, qname
<< ": Trying to prove that there is no wildcard for "<<qname
<<"/"<<QType(qtype
)<<endl
);
329 const DNSName wildcard
= g_wildcarddnsname
+ closestEncloser
;
330 for (const auto& validset
: validrrsets
) {
331 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
332 if (validset
.first
.second
== QType::NSEC
) {
333 for (const auto& records
: validset
.second
.records
) {
334 VLOG(log
, qname
<< ":\t"<<records
->getZoneRepresentation()<<endl
);
335 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(records
);
340 const DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
341 VLOG(log
, qname
<< ": Comparing owner: "<<owner
<<" with target: "<<wildcard
<<endl
);
343 if (qname
!= owner
&& qname
.isPartOf(owner
) && nsec
->isSet(QType::DNAME
)) {
344 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
346 In any negative response, the NSEC or NSEC3 [RFC5155] record type
347 bitmap SHOULD be checked to see that there was no DNAME that could
348 have been applied. If the DNAME bit in the type bitmap is set and
349 the query name is a subdomain of the closest encloser that is
350 asserted, then DNAME substitution should have been done, but the
351 substitution has not been done as specified.
353 VLOG(log
, qname
<< ":\tThe qname is a subdomain of the NSEC and the DNAME bit is set"<<endl
);
357 if (wildcard
!= owner
&& isCoveredByNSEC(wildcard
, owner
, nsec
->d_next
)) {
358 VLOG(log
, qname
<< ":\tWildcard is covered"<<endl
);
369 This function checks whether the non-existence of a wildcard covering qname|qtype
370 is proven by the NSEC3 records in validrrsets.
371 If `wildcardExists` is not NULL, if will be set to true if a wildcard exists
372 for this qname but doesn't have this qtype.
374 static bool provesNSEC3NoWildCard(const DNSName
& closestEncloser
, uint16_t const qtype
, const cspmap_t
& validrrsets
, bool* wildcardExists
, const OptLog
& log
, pdns::validation::ValidationContext
& context
)
376 auto wildcard
= g_wildcarddnsname
+ closestEncloser
;
377 VLOG(log
, closestEncloser
<< ": Trying to prove that there is no wildcard for "<<wildcard
<<"/"<<QType(qtype
)<<endl
);
379 for (const auto& validset
: validrrsets
) {
380 VLOG(log
, closestEncloser
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
381 if (validset
.first
.second
== QType::NSEC3
) {
382 for (const auto& records
: validset
.second
.records
) {
383 VLOG(log
, closestEncloser
<< ":\t"<<records
->getZoneRepresentation()<<endl
);
384 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(records
);
389 const DNSName signer
= getSigner(validset
.second
.signatures
);
390 if (!validset
.first
.first
.isPartOf(signer
)) {
394 string hash
= getHashFromNSEC3(wildcard
, *nsec3
, context
);
396 VLOG(log
, closestEncloser
<< ": Unsupported hash, ignoring"<<endl
);
399 VLOG(log
, closestEncloser
<< ":\tWildcard hash: "<<toBase32Hex(hash
)<<endl
);
400 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
401 VLOG(log
, closestEncloser
<< ":\tNSEC3 hash: "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
403 if (beginHash
== hash
) {
404 VLOG(log
, closestEncloser
<< ":\tWildcard hash matches");
405 if (wildcardExists
!= nullptr) {
406 *wildcardExists
= true;
409 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
410 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
411 nonexistence of any RRs below that zone cut, which include all RRs at
412 that (original) owner name other than DS RRs, and all RRs below that
413 owner name regardless of type.
415 if (qtype
!= QType::DS
&& isNSEC3AncestorDelegation(signer
, validset
.first
.first
, *nsec3
)) {
416 /* this is an "ancestor delegation" NSEC3 RR */
417 VLOG_NO_PREFIX(log
, " BUT an ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
421 if (qtype
== 0 || isTypeDenied(*nsec3
, QType(qtype
))) {
422 VLOG_NO_PREFIX(log
, " and proves that the type did not exist"<<endl
);
425 VLOG_NO_PREFIX(log
, " BUT the type did exist!"<<endl
);
429 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
430 VLOG(log
, closestEncloser
<< ":\tWildcard hash is covered"<<endl
);
440 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
)
442 const DNSName signer
= getSigner(signatures
);
443 if (!name
.isPartOf(signer
) || !nsecOwner
.isPartOf(signer
)) {
444 return dState::INCONCLUSIVE
;
447 const DNSName owner
= getNSECOwnerName(nsecOwner
, signatures
);
448 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
449 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
450 nonexistence of any RRs below that zone cut, which include all RRs at
451 that (original) owner name other than DS RRs, and all RRs below that
452 owner name regardless of type.
454 if (name
.isPartOf(owner
) && isNSECAncestorDelegation(signer
, owner
, nsec
)) {
455 /* this is an "ancestor delegation" NSEC RR */
456 if (qtype
!= QType::DS
|| name
!= owner
) {
457 VLOG_NO_PREFIX(log
, "An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl
);
458 return dState::NODENIAL
;
462 /* check if the type is denied */
464 if (!isTypeDenied(nsec
, QType(qtype
))) {
465 VLOG_NO_PREFIX(log
, "does _not_ deny existence of type "<<QType(qtype
)<<endl
);
466 return dState::NODENIAL
;
469 if (qtype
== QType::DS
&& signer
== name
) {
470 VLOG_NO_PREFIX(log
, "the NSEC comes from the child zone and cannot be used to deny a DS"<<endl
);
471 return dState::NODENIAL
;
474 VLOG_NO_PREFIX(log
, "Denies existence of type "<<QType(qtype
)<<endl
);
475 return dState::NXQTYPE
;
478 if (name
.isPartOf(owner
) && nsec
.isSet(QType::DNAME
)) {
479 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
481 In any negative response, the NSEC or NSEC3 [RFC5155] record type
482 bitmap SHOULD be checked to see that there was no DNAME that could
483 have been applied. If the DNAME bit in the type bitmap is set and
484 the query name is a subdomain of the closest encloser that is
485 asserted, then DNAME substitution should have been done, but the
486 substitution has not been done as specified.
488 VLOG(log
, "the DNAME bit is set and the query name is a subdomain of that NSEC");
489 return dState::NODENIAL
;
492 if (isCoveredByNSEC(name
, owner
, nsec
.d_next
)) {
493 VLOG_NO_PREFIX(log
, name
<< ": is covered by ("<<owner
<<" to "<<nsec
.d_next
<<")");
495 if (nsecProvesENT(name
, owner
, nsec
.d_next
)) {
496 VLOG_NO_PREFIX(log
, " denies existence of type "<<name
<<"/"<<QType(qtype
)<<" by proving that "<<name
<<" is an ENT"<<endl
);
497 return dState::NXQTYPE
;
500 return dState::NXDOMAIN
;
503 return dState::INCONCLUSIVE
;
506 [[nodiscard
]] uint64_t getNSEC3DenialProofWorstCaseIterationsCount(uint8_t maxLabels
, uint16_t iterations
, size_t saltLength
)
508 return (iterations
+ 1 + (saltLength
> 0 ? 1 : 0)) * maxLabels
;
512 This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
514 - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODENIAL
515 if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
516 - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
517 NXQTYPE if the name is proven to be ENT and NXDOMAIN otherwise.
518 - If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
519 useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
522 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
524 bool nsec3Seen
= false;
525 if (!needWildcardProof
&& wildcardLabelsCount
== 0) {
526 throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
529 uint8_t numberOfLabelsOfParentZone
{std::numeric_limits
<uint8_t>::max()};
530 uint16_t nsec3sConsidered
= 0;
531 for (const auto& validset
: validrrsets
) {
532 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
534 if (validset
.first
.second
==QType::NSEC
) {
535 for (const auto& record
: validset
.second
.records
) {
536 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
538 if (validset
.second
.signatures
.empty()) {
542 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(record
);
547 const DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
548 const DNSName signer
= getSigner(validset
.second
.signatures
);
549 if (!validset
.first
.first
.isPartOf(signer
) || !owner
.isPartOf(signer
) ) {
553 /* The NSEC is either a delegation one, from the parent zone, and
554 * must have the NS bit set but not the SOA one, or a regular NSEC
555 * either at apex (signer == owner) or with the SOA or NS bits clear.
557 const bool notApex
= signer
.countLabels() < owner
.countLabels();
558 if (notApex
&& nsec
->isSet(QType::NS
) && nsec
->isSet(QType::SOA
)) {
559 VLOG(log
, qname
<< ": However, that NSEC is not at the apex and has both the NS and the SOA bits set!"<<endl
);
563 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
564 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
565 nonexistence of any RRs below that zone cut, which include all RRs at
566 that (original) owner name other than DS RRs, and all RRs below that
567 owner name regardless of type.
569 if (qname
.isPartOf(owner
) && isNSECAncestorDelegation(signer
, owner
, *nsec
)) {
570 /* this is an "ancestor delegation" NSEC RR */
571 if (qtype
!= QType::DS
|| qname
!= owner
) {
572 VLOG(log
, qname
<< ": An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl
);
573 return dState::NODENIAL
;
577 if (qtype
== QType::DS
&& !qname
.isRoot() && signer
== qname
) {
578 VLOG(log
, qname
<< ": A NSEC RR from the child zone cannot deny the existence of a DS"<<endl
);
582 /* check if the type is denied */
583 if (qname
== owner
) {
584 if (!isTypeDenied(*nsec
, QType(qtype
))) {
585 VLOG(log
, qname
<< ": Does _not_ deny existence of type "<<QType(qtype
)<<endl
);
586 return dState::NODENIAL
;
589 VLOG(log
, qname
<< ": Denies existence of type "<<QType(qtype
)<<endl
);
592 * RFC 4035 Section 2.3:
593 * The bitmap for the NSEC RR at a delegation point requires special
594 * attention. Bits corresponding to the delegation NS RRset and any
595 * RRsets for which the parent zone has authoritative data MUST be set
597 if (referralToUnsigned
&& qtype
== QType::DS
) {
598 if (!nsec
->isSet(QType::NS
)) {
599 VLOG(log
, qname
<< ": However, no NS record exists at this level!"<<endl
);
600 return dState::NODENIAL
;
604 /* we know that the name exists (but this qtype doesn't) so except
605 if the answer was generated by a wildcard expansion, no wildcard
606 could have matched (rfc4035 section 5.4 bullet 1) */
607 if (needWildcardProof
&& (!isWildcardExpanded(owner
, validset
.second
.signatures
) || isWildcardExpandedOntoItself(owner
, validset
.second
.signatures
))) {
608 needWildcardProof
= false;
611 if (!needWildcardProof
) {
612 return dState::NXQTYPE
;
615 DNSName closestEncloser
= getClosestEncloserFromNSEC(qname
, owner
, nsec
->d_next
);
616 if (provesNoWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
617 return dState::NXQTYPE
;
620 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<endl
);
621 return dState::NODENIAL
;
624 if (qname
.isPartOf(owner
) && nsec
->isSet(QType::DNAME
)) {
625 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
627 In any negative response, the NSEC or NSEC3 [RFC5155] record type
628 bitmap SHOULD be checked to see that there was no DNAME that could
629 have been applied. If the DNAME bit in the type bitmap is set and
630 the query name is a subdomain of the closest encloser that is
631 asserted, then DNAME substitution should have been done, but the
632 substitution has not been done as specified.
634 VLOG(log
, qname
<< ": The DNAME bit is set and the query name is a subdomain of that NSEC"<< endl
);
635 return dState::NODENIAL
;
638 /* check if the whole NAME is denied existing */
639 if (isCoveredByNSEC(qname
, owner
, nsec
->d_next
)) {
640 VLOG(log
, qname
<< ": Is covered by ("<<owner
<<" to "<<nsec
->d_next
<<") ");
642 if (nsecProvesENT(qname
, owner
, nsec
->d_next
)) {
643 if (wantsNoDataProof
) {
644 /* if the name is an ENT and we received a NODATA answer,
645 we are fine with a NSEC proving that the name does not exist. */
646 VLOG_NO_PREFIX(log
, "Denies existence of type "<<qname
<<"/"<<QType(qtype
)<<" by proving that "<<qname
<<" is an ENT"<<endl
);
647 return dState::NXQTYPE
;
649 /* but for a NXDOMAIN proof, this doesn't make sense! */
650 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
);
651 return dState::NODENIAL
;
654 if (!needWildcardProof
) {
655 VLOG_NO_PREFIX(log
, "and we did not need a wildcard proof"<<endl
);
656 return dState::NXDOMAIN
;
659 VLOG_NO_PREFIX(log
, "but we do need a wildcard proof so ");
660 DNSName closestEncloser
= getClosestEncloserFromNSEC(qname
, owner
, nsec
->d_next
);
661 if (wantsNoDataProof
) {
662 VLOG_NO_PREFIX(log
, "looking for NODATA proof"<<endl
);
663 if (provesNoDataWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
664 return dState::NXQTYPE
;
668 VLOG_NO_PREFIX(log
, "looking for NO wildcard proof"<<endl
);
669 if (provesNoWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
670 return dState::NXDOMAIN
;
674 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<endl
);
675 return dState::NODENIAL
;
678 VLOG(log
, qname
<< ": Did not deny existence of "<<QType(qtype
)<<", "<<validset
.first
.first
<<"?="<<qname
<<", "<<nsec
->isSet(qtype
)<<", next: "<<nsec
->d_next
<<endl
);
680 } else if(validset
.first
.second
==QType::NSEC3
) {
681 for (const auto& record
: validset
.second
.records
) {
682 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
683 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
688 if (validset
.second
.signatures
.empty()) {
692 const DNSName
& hashedOwner
= validset
.first
.first
;
693 const DNSName signer
= getSigner(validset
.second
.signatures
);
694 if (!hashedOwner
.isPartOf(signer
)) {
695 VLOG(log
, qname
<< ": Owner "<<hashedOwner
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
698 numberOfLabelsOfParentZone
= std::min(numberOfLabelsOfParentZone
, static_cast<uint8_t>(signer
.countLabels()));
700 if (qtype
== QType::DS
&& !qname
.isRoot() && signer
== qname
) {
701 VLOG(log
, qname
<< ": A NSEC3 RR from the child zone cannot deny the existence of a DS"<<endl
);
705 if (g_maxNSEC3sPerRecordToConsider
> 0 && nsec3sConsidered
>= g_maxNSEC3sPerRecordToConsider
) {
706 VLOG(log
, qname
<< ": Too many NSEC3s for this record"<<endl
);
707 return dState::NODENIAL
;
711 string hash
= getHashFromNSEC3(qname
, *nsec3
, context
);
713 VLOG(log
, qname
<< ": Unsupported hash, ignoring"<<endl
);
714 return dState::INSECURE
;
719 VLOG(log
, qname
<< ":\tquery hash: "<<toBase32Hex(hash
)<<endl
);
720 string beginHash
= fromBase32Hex(hashedOwner
.getRawLabels()[0]);
722 // If the name exists, check if the qtype is denied
723 if (beginHash
== hash
) {
725 /* The NSEC3 is either a delegation one, from the parent zone, and
726 * must have the NS bit set but not the SOA one, or a regular NSEC3
727 * either at apex (signer == owner) or with the SOA or NS bits clear.
729 const bool notApex
= signer
.countLabels() < qname
.countLabels();
730 if (notApex
&& nsec3
->isSet(QType::NS
) && nsec3
->isSet(QType::SOA
)) {
731 VLOG(log
, qname
<< ": However, that NSEC3 is not at the apex and has both the NS and the SOA bits set!"<<endl
);
735 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
736 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
737 nonexistence of any RRs below that zone cut, which include all RRs at
738 that (original) owner name other than DS RRs, and all RRs below that
739 owner name regardless of type.
741 if (qtype
!= QType::DS
&& isNSEC3AncestorDelegation(signer
, qname
, *nsec3
)) {
742 /* this is an "ancestor delegation" NSEC3 RR */
743 VLOG(log
, qname
<< ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
744 return dState::NODENIAL
;
747 if (!isTypeDenied(*nsec3
, QType(qtype
))) {
748 VLOG(log
, qname
<< ": Does _not_ deny existence of type "<<QType(qtype
)<<" for name "<<qname
<<" (not opt-out)."<<endl
);
749 return dState::NODENIAL
;
752 VLOG(log
, qname
<< ": Denies existence of type "<<QType(qtype
)<<" for name "<<qname
<<" (not opt-out)."<<endl
);
755 * RFC 5155 section 8.9:
756 * If there is an NSEC3 RR present in the response that matches the
757 * delegation name, then the validator MUST ensure that the NS bit is
758 * set and that the DS bit is not set in the Type Bit Maps field of the
761 if (referralToUnsigned
&& qtype
== QType::DS
) {
762 if (!nsec3
->isSet(QType::NS
)) {
763 VLOG(log
, qname
<< ": However, no NS record exists at this level!"<<endl
);
764 return dState::NODENIAL
;
768 return dState::NXQTYPE
;
774 /* if we have no NSEC3 records, we are done */
776 return dState::NODENIAL
;
779 DNSName
closestEncloser(qname
);
781 if (needWildcardProof
) {
782 nsec3sConsidered
= 0;
783 /* We now need to look for a NSEC3 covering the closest (provable) encloser
784 RFC 5155 section-7.2.1
787 VLOG(log
, qname
<< ": Now looking for the closest encloser for "<<qname
<<endl
);
789 while (!found
&& closestEncloser
.chopOff() && closestEncloser
.countLabels() >= numberOfLabelsOfParentZone
) {
791 for(const auto& validset
: validrrsets
) {
792 if(validset
.first
.second
==QType::NSEC3
) {
793 for(const auto& record
: validset
.second
.records
) {
794 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
795 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
800 const DNSName signer
= getSigner(validset
.second
.signatures
);
801 if (!validset
.first
.first
.isPartOf(signer
)) {
802 VLOG(log
, qname
<< ": Owner "<<validset
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
806 if (g_maxNSEC3sPerRecordToConsider
> 0 && nsec3sConsidered
>= g_maxNSEC3sPerRecordToConsider
) {
807 VLOG(log
, qname
<< ": Too many NSEC3s for this record"<<endl
);
808 return dState::NODENIAL
;
812 string hash
= getHashFromNSEC3(closestEncloser
, *nsec3
, context
);
814 VLOG(log
, qname
<< ": Unsupported hash, ignoring"<<endl
);
815 return dState::INSECURE
;
818 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
820 VLOG(log
, qname
<< ": Comparing "<<toBase32Hex(hash
)<<" ("<<closestEncloser
<<") against "<<toBase32Hex(beginHash
)<<endl
);
821 if (beginHash
== hash
) {
822 /* If the closest encloser is a delegation NS we know nothing about the names in the child zone. */
823 if (isNSEC3AncestorDelegation(signer
, validset
.first
.first
, *nsec3
)) {
824 VLOG(log
, qname
<< ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
828 VLOG(log
, qname
<< ": Closest encloser for "<<qname
<<" is "<<closestEncloser
<<endl
);
831 if (nsec3
->isSet(QType::DNAME
)) {
832 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
834 In any negative response, the NSEC or NSEC3 [RFC5155] record type
835 bitmap SHOULD be checked to see that there was no DNAME that could
836 have been applied. If the DNAME bit in the type bitmap is set and
837 the query name is a subdomain of the closest encloser that is
838 asserted, then DNAME substitution should have been done, but the
839 substitution has not been done as specified.
841 VLOG(log
, qname
<< ":\tThe closest encloser NSEC3 has the DNAME bit is set"<<endl
);
842 return dState::NODENIAL
;
856 /* RFC 5155 section-7.2.6:
857 "It is not necessary to return an NSEC3 RR that matches the closest encloser,
858 as the existence of this closest encloser is proven by the presence of the
859 expanded wildcard in the response.
862 unsigned int closestEncloserLabelsCount
= closestEncloser
.countLabels();
863 while (wildcardLabelsCount
> 0 && closestEncloserLabelsCount
> wildcardLabelsCount
) {
864 closestEncloser
.chopOff();
865 closestEncloserLabelsCount
--;
869 bool nextCloserFound
= false;
870 bool isOptOut
= false;
873 /* now that we have found the closest (provable) encloser,
874 we can construct the next closer (RFC7129 section-5.5) name
875 and look for a NSEC3 RR covering it */
876 unsigned int labelIdx
= qname
.countLabels() - closestEncloser
.countLabels();
878 DNSName
nextCloser(closestEncloser
);
879 nextCloser
.prependRawLabel(qname
.getRawLabel(labelIdx
- 1));
880 nsec3sConsidered
= 0;
881 VLOG(log
, qname
<< ":Looking for a NSEC3 covering the next closer name "<<nextCloser
<<endl
);
883 for (const auto& validset
: validrrsets
) {
884 if (validset
.first
.second
== QType::NSEC3
) {
885 for (const auto& record
: validset
.second
.records
) {
886 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
887 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
892 if (g_maxNSEC3sPerRecordToConsider
> 0 && nsec3sConsidered
>= g_maxNSEC3sPerRecordToConsider
) {
893 VLOG(log
, qname
<< ": Too many NSEC3s for this record"<<endl
);
894 return dState::NODENIAL
;
898 string hash
= getHashFromNSEC3(nextCloser
, *nsec3
, context
);
900 VLOG(log
, qname
<< ": Unsupported hash, ignoring"<<endl
);
901 return dState::INSECURE
;
904 const DNSName signer
= getSigner(validset
.second
.signatures
);
905 if (!validset
.first
.first
.isPartOf(signer
)) {
906 VLOG(log
, qname
<< ": Owner "<<validset
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
910 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
912 VLOG(log
, qname
<< ": Comparing "<<toBase32Hex(hash
)<<" against "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
913 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
914 VLOG(log
, qname
<< ": Denies existence of name "<<qname
<<"/"<<QType(qtype
));
915 nextCloserFound
= true;
917 if (nsec3
->isOptOut()) {
918 VLOG_NO_PREFIX(log
, " but is opt-out!");
922 VLOG_NO_PREFIX(log
, endl
);
925 VLOG(log
, qname
<< ": Did not cover us ("<<qname
<<"), start="<<validset
.first
.first
<<", us="<<toBase32Hex(hash
)<<", end="<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
928 if (nextCloserFound
) {
935 if (nextCloserFound
) {
936 bool wildcardExists
= false;
937 /* RFC 7129 section-5.6 */
938 if (needWildcardProof
&& !provesNSEC3NoWildCard(closestEncloser
, qtype
, validrrsets
, &wildcardExists
, log
, context
)) {
940 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<QType(qtype
)<<endl
);
941 return dState::NODENIAL
;
946 return dState::OPTOUT
;
948 if (wildcardExists
) {
949 return dState::NXQTYPE
;
951 return dState::NXDOMAIN
;
954 // There were no valid NSEC(3) records
955 return dState::NODENIAL
;
958 bool isRRSIGNotExpired(const time_t now
, const RRSIGRecordContent
& sig
)
960 // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
961 return sig
.d_sigexpire
>= now
;
964 bool isRRSIGIncepted(const time_t now
, const RRSIGRecordContent
& sig
)
966 // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
967 return sig
.d_siginception
- g_signatureInceptionSkew
<= now
;
971 [[nodiscard
]] bool checkSignatureInceptionAndExpiry(const DNSName
& qname
, time_t now
, const RRSIGRecordContent
& sig
, vState
& ede
, const OptLog
& log
)
974 - 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.
975 - 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.
977 if (isRRSIGIncepted(now
, sig
) && isRRSIGNotExpired(now
, sig
)) {
980 ede
= ((sig
.d_siginception
- g_signatureInceptionSkew
) > now
) ? vState::BogusSignatureNotYetValid
: vState::BogusSignatureExpired
;
981 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
);
985 [[nodiscard
]] bool checkSignatureWithKey(const DNSName
& qname
, const RRSIGRecordContent
& sig
, const DNSKEYRecordContent
& key
, const std::string
& msg
, vState
& ede
, const OptLog
& log
)
989 auto dke
= DNSCryptoKeyEngine::makeFromPublicKeyString(key
.d_algorithm
, key
.d_key
);
990 result
= dke
->verify(msg
, sig
.d_signature
);
991 VLOG(log
, qname
<< ": Signature by key with tag "<<sig
.d_tag
<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig
.d_algorithm
)<<" was " << (result
? "" : "NOT ")<<"valid"<<endl
);
993 ede
= vState::BogusNoValidRRSIG
;
996 catch (const std::exception
& e
) {
997 VLOG(log
, qname
<< ": Could not make a validator for signature: "<<e
.what()<<endl
);
998 ede
= vState::BogusUnsupportedDNSKEYAlgo
;
1005 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
)
1007 bool missingKey
= false;
1008 bool isValid
= false;
1009 bool allExpired
= true;
1010 bool noneIncepted
= true;
1011 uint16_t signaturesConsidered
= 0;
1013 for (const auto& signature
: signatures
) {
1014 unsigned int labelCount
= name
.countLabels();
1015 if (signature
->d_labels
> labelCount
) {
1016 VLOG(log
, name
<<": Discarding invalid RRSIG whose label count is "<<signature
->d_labels
<<" while the RRset owner name has only "<<labelCount
<<endl
);
1020 vState ede
= vState::Indeterminate
;
1021 if (!checkSignatureInceptionAndExpiry(name
, now
, *signature
, ede
, log
)) {
1022 if (isRRSIGIncepted(now
, *signature
)) {
1023 noneIncepted
= false;
1025 if (isRRSIGNotExpired(now
, *signature
)) {
1031 if (g_maxRRSIGsPerRecordToConsider
> 0 && signaturesConsidered
>= g_maxRRSIGsPerRecordToConsider
) {
1032 VLOG(log
, name
<<": We have already considered "<<std::to_string(signaturesConsidered
)<<" RRSIG"<<addS(signaturesConsidered
)<<" for this record, stopping now"<<endl
;);
1033 // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1036 signaturesConsidered
++;
1037 context
.d_validationsCounter
++;
1039 auto keysMatchingTag
= getByTag(keys
, signature
->d_tag
, signature
->d_algorithm
, log
);
1041 if (keysMatchingTag
.empty()) {
1042 VLOG(log
, name
<< ": No key provided for "<<signature
->d_tag
<<" and algorithm "<<std::to_string(signature
->d_algorithm
)<<endl
;);
1047 string msg
= getMessageForRRSET(name
, *signature
, toSign
, true);
1048 uint16_t dnskeysConsidered
= 0;
1049 for (const auto& key
: keysMatchingTag
) {
1050 if (g_maxDNSKEYsToConsider
> 0 && dnskeysConsidered
>= g_maxDNSKEYsToConsider
) {
1051 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
;);
1052 return isValid
? vState::Secure
: vState::BogusNoValidRRSIG
;
1054 dnskeysConsidered
++;
1056 bool signIsValid
= checkSignatureWithKey(name
, *signature
, *key
, msg
, ede
, log
);
1060 VLOG(log
, name
<< ": Validated "<<name
<<"/"<<DNSRecordContent::NumberToType(signature
->d_type
)<<endl
);
1061 // cerr<<"valid"<<endl;
1062 // cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
1065 VLOG(log
, name
<< ": signature invalid"<<endl
);
1066 if (isRRSIGIncepted(now
, *signature
)) {
1067 noneIncepted
= false;
1069 if (isRRSIGNotExpired(now
, *signature
)) {
1074 if (signIsValid
&& !validateAllSigs
) {
1075 return vState::Secure
;
1081 return vState::Secure
;
1084 return vState::BogusNoValidRRSIG
;
1087 // ede should be vState::BogusSignatureNotYetValid
1088 return vState::BogusSignatureNotYetValid
;
1091 // ede should be vState::BogusSignatureExpired);
1092 return vState::BogusSignatureExpired
;
1095 return vState::BogusNoValidRRSIG
;
1099 // should return vState, zone cut and validated keyset
1100 // i.e. www.7bits.nl -> insecure/7bits.nl/[]
1101 // www.powerdnssec.org -> secure/powerdnssec.org/[keys]
1102 // www.dnssec-failed.org -> bogus/dnssec-failed.org/[]
1104 cspmap_t
harvestCSPFromRecs(const vector
<DNSRecord
>& recs
)
1107 for(const auto& rec
: recs
) {
1108 // cerr<<"res "<<rec.d_name<<"/"<<rec.d_type<<endl;
1109 if (rec
.d_type
== QType::OPT
) {
1113 if(rec
.d_type
== QType::RRSIG
) {
1114 auto rrc
= getRR
<RRSIGRecordContent
>(rec
);
1116 cspmap
[{rec
.d_name
,rrc
->d_type
}].signatures
.push_back(rrc
);
1120 cspmap
[{rec
.d_name
, rec
.d_type
}].records
.insert(rec
.getContent());
1126 bool getTrustAnchor(const map
<DNSName
,dsmap_t
>& anchors
, const DNSName
& zone
, dsmap_t
&res
)
1128 const auto& iter
= anchors
.find(zone
);
1130 if (iter
== anchors
.cend()) {
1138 bool haveNegativeTrustAnchor(const map
<DNSName
,std::string
>& negAnchors
, const DNSName
& zone
, std::string
& reason
)
1140 const auto& iter
= negAnchors
.find(zone
);
1142 if (iter
== negAnchors
.cend()) {
1146 reason
= iter
->second
;
1150 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
)
1153 * Check all DNSKEY records against all DS records and place all DNSKEY records
1154 * that have DS records (that we support the algo for) in the tentative key storage
1156 uint16_t dssConsidered
= 0;
1157 for (const auto& dsrc
: dsmap
) {
1158 if (g_maxDSsToConsider
> 0 && dssConsidered
> g_maxDSsToConsider
) {
1159 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(dssConsidered
)<<" DS"<<addS(dssConsidered
)<<", not considering the remaining ones"<<endl
;);
1160 return vState::BogusNoValidDNSKEY
;
1164 uint16_t dnskeysConsidered
= 0;
1165 auto record
= getByTag(tkeys
, dsrc
.d_tag
, dsrc
.d_algorithm
, log
);
1166 // 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;
1168 for (const auto& drc
: record
) {
1169 bool isValid
= false;
1170 bool dsCreated
= false;
1171 DSRecordContent dsrc2
;
1173 if (g_maxDNSKEYsToConsider
> 0 && dnskeysConsidered
>= g_maxDNSKEYsToConsider
) {
1174 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
;);
1175 // we need to break because we can have a partially validated set
1176 // where the KSK signs the ZSK(s), and even if we don't
1177 // we are going to try to get the correct EDE status (revoked, expired, ...)
1180 dnskeysConsidered
++;
1183 dsrc2
= makeDSFromDNSKey(zone
, *drc
, dsrc
.d_digesttype
);
1185 isValid
= dsrc
== dsrc2
;
1187 catch (const std::exception
&e
) {
1188 VLOG(log
, zone
<< ": Unable to make DS from DNSKey: "<<e
.what()<<endl
);
1192 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
);
1194 validkeys
.insert(drc
);
1198 VLOG(log
, zone
<< ": DNSKEY did not match the DS, parent DS: "<<dsrc
.getZoneRepresentation() << " ! = "<<dsrc2
.getZoneRepresentation()<<endl
);
1204 vState ede
= vState::BogusNoValidDNSKEY
;
1206 // cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
1207 // these counts could be off if we somehow ended up with
1208 // duplicate keys. Should switch to a type that prevents that.
1209 if (!tkeys
.empty() && validkeys
.size() < tkeys
.size()) {
1210 // this should mean that we have one or more DS-validated DNSKEYs
1211 // but not a fully validated DNSKEY set, yet
1212 // one of these valid DNSKEYs should be able to validate the
1214 uint16_t signaturesConsidered
= 0;
1215 for (const auto& sig
: sigs
) {
1216 if (!checkSignatureInceptionAndExpiry(zone
, now
, *sig
, ede
, log
)) {
1220 // 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;
1221 auto bytag
= getByTag(validkeys
, sig
->d_tag
, sig
->d_algorithm
, log
);
1223 if (bytag
.empty()) {
1227 if (g_maxRRSIGsPerRecordToConsider
> 0 && signaturesConsidered
>= g_maxRRSIGsPerRecordToConsider
) {
1228 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(signaturesConsidered
)<<" RRSIG"<<addS(signaturesConsidered
)<<" for this record, stopping now"<<endl
;);
1229 // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1230 return vState::BogusNoValidDNSKEY
;
1233 string msg
= getMessageForRRSET(zone
, *sig
, toSign
);
1234 uint16_t dnskeysConsidered
= 0;
1235 for (const auto& key
: bytag
) {
1236 if (g_maxDNSKEYsToConsider
> 0 && dnskeysConsidered
>= g_maxDNSKEYsToConsider
) {
1237 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
;);
1238 return vState::BogusNoValidDNSKEY
;
1240 dnskeysConsidered
++;
1242 if (g_maxRRSIGsPerRecordToConsider
> 0 && signaturesConsidered
>= g_maxRRSIGsPerRecordToConsider
) {
1243 VLOG(log
, zone
<< ": We have already considered "<<std::to_string(signaturesConsidered
)<<" RRSIG"<<addS(signaturesConsidered
)<<" for this record, stopping now"<<endl
;);
1244 // possibly going Bogus, the RRSIGs have not been validated so Insecure would be wrong
1245 return vState::BogusNoValidDNSKEY
;
1247 // cerr<<"validating : ";
1248 bool signIsValid
= checkSignatureWithKey(zone
, *sig
, *key
, msg
, ede
, log
);
1249 signaturesConsidered
++;
1250 context
.d_validationsCounter
++;
1253 VLOG(log
, zone
<< ": Validation succeeded - whole DNSKEY set is valid"<<endl
);
1257 VLOG(log
, zone
<< ": Validation did not succeed!"<<endl
);
1260 if (validkeys
.size() == tkeys
.size()) {
1261 // we validated the whole DNSKEY set already */
1264 // if(validkeys.empty()) cerr<<"did not manage to validate DNSKEY set based on DS-validated KSK, only passing KSK on"<<endl;
1268 if (validkeys
.size() < tkeys
.size()) {
1269 /* so we failed to validate the whole set, let's try to find out why exactly */
1270 bool dnskeyAlgoSupported
= false;
1271 bool dsDigestSupported
= false;
1273 for (const auto& dsrc
: dsmap
)
1275 if (DNSCryptoKeyEngine::isAlgorithmSupported(dsrc
.d_algorithm
)) {
1276 dnskeyAlgoSupported
= true;
1277 if (DNSCryptoKeyEngine::isDigestSupported(dsrc
.d_digesttype
)) {
1278 dsDigestSupported
= true;
1283 if (!dnskeyAlgoSupported
) {
1284 return vState::BogusUnsupportedDNSKEYAlgo
;
1286 if (!dsDigestSupported
) {
1287 return vState::BogusUnsupportedDSDigestType
;
1290 bool zoneKey
= false;
1291 bool notRevoked
= false;
1292 bool validProtocol
= false;
1294 for (const auto& key
: tkeys
) {
1295 if (!isAZoneKey(*key
)) {
1300 if (isRevokedKey(*key
)) {
1305 if (key
->d_protocol
!= 3) {
1308 validProtocol
= true;
1312 return vState::BogusNoZoneKeyBitSet
;
1315 return vState::BogusRevokedDNSKEY
;
1317 if (!validProtocol
) {
1318 return vState::BogusInvalidDNSKEYProtocol
;
1324 return vState::Secure
;
1327 bool isSupportedDS(const DSRecordContent
& dsrec
, const OptLog
& log
)
1329 if (!DNSCryptoKeyEngine::isAlgorithmSupported(dsrec
.d_algorithm
)) {
1330 VLOG(log
, "Discarding DS "<<dsrec
.d_tag
<<" because we don't support algorithm number "<<std::to_string(dsrec
.d_algorithm
)<<endl
);
1334 if (!DNSCryptoKeyEngine::isDigestSupported(dsrec
.d_digesttype
)) {
1335 VLOG(log
, "Discarding DS "<<dsrec
.d_tag
<<" because we don't support digest number "<<std::to_string(dsrec
.d_digesttype
)<<endl
);
1342 DNSName
getSigner(const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
1344 for (const auto& sig
: signatures
) {
1346 return sig
->d_signer
;
1353 const std::string
& vStateToString(vState state
)
1355 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" };
1356 return vStates
.at(static_cast<size_t>(state
));
1359 std::ostream
& operator<<(std::ostream
&ostr
, const vState dstate
)
1361 ostr
<<vStateToString(dstate
);
1365 std::ostream
& operator<<(std::ostream
&ostr
, const dState dstate
)
1367 static const std::vector
<std::string
> dStates
= {"no denial", "inconclusive", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
1368 ostr
<<dStates
.at(static_cast<size_t>(dstate
));
1372 void updateDNSSECValidationState(vState
& state
, const vState stateUpdate
)
1374 if (stateUpdate
== vState::TA
) {
1375 state
= vState::Secure
;
1377 else if (stateUpdate
== vState::NTA
) {
1378 state
= vState::Insecure
;
1380 else if (vStateIsBogus(stateUpdate
) || state
== vState::Indeterminate
) {
1381 state
= stateUpdate
;
1383 else if (stateUpdate
== vState::Insecure
) {
1384 if (!vStateIsBogus(state
)) {
1385 state
= vState::Insecure
;