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};
34 static bool isAZoneKey(const DNSKEYRecordContent
& key
)
36 /* rfc4034 Section 2.1.1:
37 "Bit 7 of the Flags field is the Zone Key flag. If bit 7 has value 1,
38 then the DNSKEY record holds a DNS zone key, and the DNSKEY RR's
39 owner name MUST be the name of a zone. If bit 7 has value 0, then
40 the DNSKEY record holds some other type of DNS public key and MUST
41 NOT be used to verify RRSIGs that cover RRsets."
43 Let's check that this is a ZONE key, even though there is no other
44 types of DNSKEYs at the moment.
46 return (key
.d_flags
& 256) != 0;
49 static bool isRevokedKey(const DNSKEYRecordContent
& key
)
51 /* rfc5011 Section 3 */
52 return (key
.d_flags
& 128) != 0;
55 static vector
<shared_ptr
<const DNSKEYRecordContent
> > getByTag(const skeyset_t
& keys
, uint16_t tag
, uint8_t algorithm
, const OptLog
& log
)
57 vector
<shared_ptr
<const DNSKEYRecordContent
>> ret
;
59 for (const auto& key
: keys
) {
60 if (!isAZoneKey(*key
)) {
61 VLOG(log
, "Key for tag "<<std::to_string(tag
)<<" and algorithm "<<std::to_string(algorithm
)<<" is not a zone key, skipping"<<endl
;);
65 if (isRevokedKey(*key
)) {
66 VLOG(log
, "Key for tag "<<std::to_string(tag
)<<" and algorithm "<<std::to_string(algorithm
)<<" has been revoked, skipping"<<endl
;);
70 if (key
->d_protocol
== 3 && key
->getTag() == tag
&& key
->d_algorithm
== algorithm
) {
78 bool isCoveredByNSEC3Hash(const std::string
& hash
, const std::string
& beginHash
, const std::string
& nextHash
)
80 return ((beginHash
< hash
&& hash
< nextHash
) || // no wrap BEGINNING --- HASH -- END
81 (nextHash
> hash
&& beginHash
> nextHash
) || // wrap HASH --- END --- BEGINNING
82 (nextHash
< beginHash
&& beginHash
< hash
) || // wrap other case END --- BEGINNING --- HASH
83 (beginHash
== nextHash
&& hash
!= beginHash
)); // "we have only 1 NSEC3 record, LOL!"
86 bool isCoveredByNSEC3Hash(const DNSName
& name
, const DNSName
& beginHash
, const DNSName
& nextHash
)
88 return ((beginHash
.canonCompare(name
) && name
.canonCompare(nextHash
)) || // no wrap BEGINNING --- HASH -- END
89 (name
.canonCompare(nextHash
) && nextHash
.canonCompare(beginHash
)) || // wrap HASH --- END --- BEGINNING
90 (nextHash
.canonCompare(beginHash
) && beginHash
.canonCompare(name
)) || // wrap other case END --- BEGINNING --- HASH
91 (beginHash
== nextHash
&& name
!= beginHash
)); // "we have only 1 NSEC3 record, LOL!"
94 bool isCoveredByNSEC(const DNSName
& name
, const DNSName
& begin
, const DNSName
& next
)
96 return ((begin
.canonCompare(name
) && name
.canonCompare(next
)) || // no wrap BEGINNING --- NAME --- NEXT
97 (name
.canonCompare(next
) && next
.canonCompare(begin
)) || // wrap NAME --- NEXT --- BEGINNING
98 (next
.canonCompare(begin
) && begin
.canonCompare(name
)) || // wrap other case NEXT --- BEGINNING --- NAME
99 (begin
== next
&& name
!= begin
)); // "we have only 1 NSEC record, LOL!"
102 static bool nsecProvesENT(const DNSName
& name
, const DNSName
& begin
, const DNSName
& next
)
104 /* if name is an ENT:
106 - next is a child of name
108 return begin
.canonCompare(name
) && next
!= name
&& next
.isPartOf(name
);
111 using nsec3HashesCache
= std::map
<std::tuple
<DNSName
, std::string
, uint16_t>, std::string
>;
113 static std::string
getHashFromNSEC3(const DNSName
& qname
, const NSEC3RecordContent
& nsec3
, nsec3HashesCache
& cache
)
117 if (g_maxNSEC3Iterations
!= 0 && nsec3
.d_iterations
> g_maxNSEC3Iterations
) {
121 auto key
= std::tuple(qname
, nsec3
.d_salt
, nsec3
.d_iterations
);
122 auto iter
= cache
.find(key
);
123 if (iter
!= cache
.end())
128 result
= hashQNameWithSalt(nsec3
.d_salt
, nsec3
.d_iterations
, qname
);
133 /* There is no delegation at this exact point if:
134 - the name exists but the NS type is not set
135 - the name does not exist
136 One exception, if the name is covered by an opt-out NSEC3
137 it doesn't prove that an insecure delegation doesn't exist.
139 bool denialProvesNoDelegation(const DNSName
& zone
, const std::vector
<DNSRecord
>& dsrecords
)
141 nsec3HashesCache cache
;
143 for (const auto& record
: dsrecords
) {
144 if (record
.d_type
== QType::NSEC
) {
145 const auto nsec
= getRR
<NSECRecordContent
>(record
);
150 if (record
.d_name
== zone
) {
151 return !nsec
->isSet(QType::NS
);
154 if (isCoveredByNSEC(zone
, record
.d_name
, nsec
->d_next
)) {
158 else if (record
.d_type
== QType::NSEC3
) {
159 const auto nsec3
= getRR
<NSEC3RecordContent
>(record
);
164 const string hash
= getHashFromNSEC3(zone
, *nsec3
, cache
);
169 const string beginHash
= fromBase32Hex(record
.d_name
.getRawLabels()[0]);
170 if (beginHash
== hash
) {
171 return !nsec3
->isSet(QType::NS
);
174 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
175 return !(nsec3
->isOptOut());
183 /* RFC 4035 section-5.3.4:
184 "If the number of labels in an RRset's owner name is greater than the
185 Labels field of the covering RRSIG RR, then the RRset and its
186 covering RRSIG RR were created as a result of wildcard expansion."
188 bool isWildcardExpanded(unsigned int labelCount
, const RRSIGRecordContent
& sign
)
190 return sign
.d_labels
< labelCount
;
193 static bool isWildcardExpanded(const DNSName
& owner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
195 if (signatures
.empty()) {
199 const auto& sign
= signatures
.at(0);
200 unsigned int labelsCount
= owner
.countLabels();
201 return isWildcardExpanded(labelsCount
, *sign
);
204 bool isWildcardExpandedOntoItself(const DNSName
& owner
, unsigned int labelCount
, const RRSIGRecordContent
& sign
)
206 /* this is a wildcard alright, but it has not been expanded */
207 return owner
.isWildcard() && (labelCount
- 1) == sign
.d_labels
;
210 static bool isWildcardExpandedOntoItself(const DNSName
& owner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
212 if (signatures
.empty()) {
216 const auto& sign
= signatures
.at(0);
217 unsigned int labelsCount
= owner
.countLabels();
218 return isWildcardExpandedOntoItself(owner
, labelsCount
, *sign
);
221 /* if this is a wildcard NSEC, the owner name has been modified
222 to match the name. Make sure we use the original '*' form. */
223 DNSName
getNSECOwnerName(const DNSName
& initialOwner
, const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
225 DNSName result
= initialOwner
;
227 if (signatures
.empty()) {
231 const auto& sign
= signatures
.at(0);
232 unsigned int labelsCount
= initialOwner
.countLabels();
233 if (sign
&& sign
->d_labels
< labelsCount
) {
238 while (sign
->d_labels
< labelsCount
);
240 result
= g_wildcarddnsname
+ result
;
246 static bool isNSECAncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const NSECRecordContent
& nsec
)
248 return nsec
.isSet(QType::NS
) &&
249 !nsec
.isSet(QType::SOA
) &&
250 signer
.countLabels() < owner
.countLabels();
253 bool isNSEC3AncestorDelegation(const DNSName
& signer
, const DNSName
& owner
, const NSEC3RecordContent
& nsec3
)
255 return nsec3
.isSet(QType::NS
) &&
256 !nsec3
.isSet(QType::SOA
) &&
257 signer
.countLabels() < owner
.countLabels();
260 static bool provesNoDataWildCard(const DNSName
& qname
, const uint16_t qtype
, const DNSName
& closestEncloser
, const cspmap_t
& validrrsets
, const OptLog
& log
)
262 const DNSName wildcard
= g_wildcarddnsname
+ closestEncloser
;
263 VLOG(log
, qname
<< ": Trying to prove that there is no data in wildcard for "<<qname
<<"/"<<QType(qtype
)<<endl
);
264 for (const auto& validset
: validrrsets
) {
265 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
266 if (validset
.first
.second
== QType::NSEC
) {
267 for (const auto& record
: validset
.second
.records
) {
268 VLOG(log
, ":\t"<<record
->getZoneRepresentation()<<endl
);
269 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(record
);
274 DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
275 if (owner
!= wildcard
) {
279 VLOG(log
, qname
<< ":\tWildcard matches");
280 if (qtype
== 0 || isTypeDenied(*nsec
, QType(qtype
))) {
281 VLOG_NO_PREFIX(log
, " and proves that the type did not exist"<<endl
);
284 VLOG_NO_PREFIX(log
, " BUT the type did exist!"<<endl
);
293 DNSName
getClosestEncloserFromNSEC(const DNSName
& name
, const DNSName
& owner
, const DNSName
& next
)
295 DNSName
commonWithOwner(name
.getCommonLabels(owner
));
296 DNSName
commonWithNext(name
.getCommonLabels(next
));
297 if (commonWithOwner
.countLabels() >= commonWithNext
.countLabels()) {
298 return commonWithOwner
;
300 return commonWithNext
;
304 This function checks whether the non-existence of a wildcard covering qname|qtype
305 is proven by the NSEC records in validrrsets.
307 static bool provesNoWildCard(const DNSName
& qname
, const uint16_t qtype
, const DNSName
& closestEncloser
, const cspmap_t
& validrrsets
, const OptLog
& log
)
309 VLOG(log
, qname
<< ": Trying to prove that there is no wildcard for "<<qname
<<"/"<<QType(qtype
)<<endl
);
310 const DNSName wildcard
= g_wildcarddnsname
+ closestEncloser
;
311 for (const auto& validset
: validrrsets
) {
312 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
313 if (validset
.first
.second
== QType::NSEC
) {
314 for (const auto& records
: validset
.second
.records
) {
315 VLOG(log
, qname
<< ":\t"<<records
->getZoneRepresentation()<<endl
);
316 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(records
);
321 const DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
322 VLOG(log
, qname
<< ": Comparing owner: "<<owner
<<" with target: "<<wildcard
<<endl
);
324 if (qname
!= owner
&& qname
.isPartOf(owner
) && nsec
->isSet(QType::DNAME
)) {
325 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
327 In any negative response, the NSEC or NSEC3 [RFC5155] record type
328 bitmap SHOULD be checked to see that there was no DNAME that could
329 have been applied. If the DNAME bit in the type bitmap is set and
330 the query name is a subdomain of the closest encloser that is
331 asserted, then DNAME substitution should have been done, but the
332 substitution has not been done as specified.
334 VLOG(log
, qname
<< ":\tThe qname is a subdomain of the NSEC and the DNAME bit is set"<<endl
);
338 if (wildcard
!= owner
&& isCoveredByNSEC(wildcard
, owner
, nsec
->d_next
)) {
339 VLOG(log
, qname
<< ":\tWildcard is covered"<<endl
);
350 This function checks whether the non-existence of a wildcard covering qname|qtype
351 is proven by the NSEC3 records in validrrsets.
352 If `wildcardExists` is not NULL, if will be set to true if a wildcard exists
353 for this qname but doesn't have this qtype.
355 static bool provesNSEC3NoWildCard(const DNSName
& closestEncloser
, uint16_t const qtype
, const cspmap_t
& validrrsets
, bool* wildcardExists
, nsec3HashesCache
& cache
, const OptLog
& log
)
357 auto wildcard
= g_wildcarddnsname
+ closestEncloser
;
358 VLOG(log
, closestEncloser
<< ": Trying to prove that there is no wildcard for "<<wildcard
<<"/"<<QType(qtype
)<<endl
);
360 for (const auto& validset
: validrrsets
) {
361 VLOG(log
, closestEncloser
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
362 if (validset
.first
.second
== QType::NSEC3
) {
363 for (const auto& records
: validset
.second
.records
) {
364 VLOG(log
, closestEncloser
<< ":\t"<<records
->getZoneRepresentation()<<endl
);
365 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(records
);
370 const DNSName signer
= getSigner(validset
.second
.signatures
);
371 if (!validset
.first
.first
.isPartOf(signer
)) {
375 string hash
= getHashFromNSEC3(wildcard
, *nsec3
, cache
);
379 VLOG(log
, closestEncloser
<< ":\tWildcard hash: "<<toBase32Hex(hash
)<<endl
);
380 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
381 VLOG(log
, closestEncloser
<< ":\tNSEC3 hash: "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
383 if (beginHash
== hash
) {
384 VLOG(log
, closestEncloser
<< ":\tWildcard hash matches");
385 if (wildcardExists
!= nullptr) {
386 *wildcardExists
= true;
389 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
390 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
391 nonexistence of any RRs below that zone cut, which include all RRs at
392 that (original) owner name other than DS RRs, and all RRs below that
393 owner name regardless of type.
395 if (qtype
!= QType::DS
&& isNSEC3AncestorDelegation(signer
, validset
.first
.first
, *nsec3
)) {
396 /* this is an "ancestor delegation" NSEC3 RR */
397 VLOG_NO_PREFIX(log
, " BUT an ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
401 if (qtype
== 0 || isTypeDenied(*nsec3
, QType(qtype
))) {
402 VLOG_NO_PREFIX(log
, " and proves that the type did not exist"<<endl
);
405 VLOG_NO_PREFIX(log
, " BUT the type did exist!"<<endl
);
409 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
410 VLOG(log
, closestEncloser
<< ":\tWildcard hash is covered"<<endl
);
420 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
)
422 const DNSName signer
= getSigner(signatures
);
423 if (!name
.isPartOf(signer
) || !nsecOwner
.isPartOf(signer
)) {
424 return dState::INCONCLUSIVE
;
427 const DNSName owner
= getNSECOwnerName(nsecOwner
, signatures
);
428 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
429 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
430 nonexistence of any RRs below that zone cut, which include all RRs at
431 that (original) owner name other than DS RRs, and all RRs below that
432 owner name regardless of type.
434 if (name
.isPartOf(owner
) && isNSECAncestorDelegation(signer
, owner
, nsec
)) {
435 /* this is an "ancestor delegation" NSEC RR */
436 if (qtype
!= QType::DS
|| name
!= owner
) {
437 VLOG_NO_PREFIX(log
, "An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl
);
438 return dState::NODENIAL
;
442 /* check if the type is denied */
444 if (!isTypeDenied(nsec
, QType(qtype
))) {
445 VLOG_NO_PREFIX(log
, "does _not_ deny existence of type "<<QType(qtype
)<<endl
);
446 return dState::NODENIAL
;
449 if (qtype
== QType::DS
&& signer
== name
) {
450 VLOG_NO_PREFIX(log
, "the NSEC comes from the child zone and cannot be used to deny a DS"<<endl
);
451 return dState::NODENIAL
;
454 VLOG_NO_PREFIX(log
, "Denies existence of type "<<QType(qtype
)<<endl
);
455 return dState::NXQTYPE
;
458 if (name
.isPartOf(owner
) && nsec
.isSet(QType::DNAME
)) {
459 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
461 In any negative response, the NSEC or NSEC3 [RFC5155] record type
462 bitmap SHOULD be checked to see that there was no DNAME that could
463 have been applied. If the DNAME bit in the type bitmap is set and
464 the query name is a subdomain of the closest encloser that is
465 asserted, then DNAME substitution should have been done, but the
466 substitution has not been done as specified.
468 VLOG(log
, "the DNAME bit is set and the query name is a subdomain of that NSEC");
469 return dState::NODENIAL
;
472 if (isCoveredByNSEC(name
, owner
, nsec
.d_next
)) {
473 VLOG_NO_PREFIX(log
, name
<< ": is covered by ("<<owner
<<" to "<<nsec
.d_next
<<")");
475 if (nsecProvesENT(name
, owner
, nsec
.d_next
)) {
476 VLOG_NO_PREFIX(log
, " denies existence of type "<<name
<<"/"<<QType(qtype
)<<" by proving that "<<name
<<" is an ENT"<<endl
);
477 return dState::NXQTYPE
;
480 return dState::NXDOMAIN
;
483 return dState::INCONCLUSIVE
;
487 This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
489 - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODENIAL
490 if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
491 - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
492 NXQTYPE if the name is proven to be ENT and NXDOMAIN otherwise.
493 - If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
494 useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
498 dState
getDenial(const cspmap_t
&validrrsets
, const DNSName
& qname
, const uint16_t qtype
, bool referralToUnsigned
, bool wantsNoDataProof
, const OptLog
& log
, bool needWildcardProof
, unsigned int wildcardLabelsCount
) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
500 nsec3HashesCache cache
;
501 bool nsec3Seen
= false;
502 if (!needWildcardProof
&& wildcardLabelsCount
== 0) {
503 throw PDNSException("Invalid wildcard labels count for the validation of a positive answer synthesized from a wildcard");
506 for (const auto& validset
: validrrsets
) {
507 VLOG(log
, qname
<< ": Do have: "<<validset
.first
.first
<<"/"<<DNSRecordContent::NumberToType(validset
.first
.second
)<<endl
);
509 if (validset
.first
.second
==QType::NSEC
) {
510 for (const auto& record
: validset
.second
.records
) {
511 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
513 if (validset
.second
.signatures
.empty()) {
517 auto nsec
= std::dynamic_pointer_cast
<const NSECRecordContent
>(record
);
522 const DNSName owner
= getNSECOwnerName(validset
.first
.first
, validset
.second
.signatures
);
523 const DNSName signer
= getSigner(validset
.second
.signatures
);
524 if (!validset
.first
.first
.isPartOf(signer
) || !owner
.isPartOf(signer
) ) {
528 /* The NSEC is either a delegation one, from the parent zone, and
529 * must have the NS bit set but not the SOA one, or a regular NSEC
530 * either at apex (signer == owner) or with the SOA or NS bits clear.
532 const bool notApex
= signer
.countLabels() < owner
.countLabels();
533 if (notApex
&& nsec
->isSet(QType::NS
) && nsec
->isSet(QType::SOA
)) {
534 VLOG(log
, qname
<< ": However, that NSEC is not at the apex and has both the NS and the SOA bits set!"<<endl
);
538 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
539 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
540 nonexistence of any RRs below that zone cut, which include all RRs at
541 that (original) owner name other than DS RRs, and all RRs below that
542 owner name regardless of type.
544 if (qname
.isPartOf(owner
) && isNSECAncestorDelegation(signer
, owner
, *nsec
)) {
545 /* this is an "ancestor delegation" NSEC RR */
546 if (qtype
!= QType::DS
|| qname
!= owner
) {
547 VLOG(log
, qname
<< ": An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl
);
548 return dState::NODENIAL
;
552 if (qtype
== QType::DS
&& !qname
.isRoot() && signer
== qname
) {
553 VLOG(log
, qname
<< ": A NSEC RR from the child zone cannot deny the existence of a DS"<<endl
);
557 /* check if the type is denied */
558 if (qname
== owner
) {
559 if (!isTypeDenied(*nsec
, QType(qtype
))) {
560 VLOG(log
, qname
<< ": Does _not_ deny existence of type "<<QType(qtype
)<<endl
);
561 return dState::NODENIAL
;
564 VLOG(log
, qname
<< ": Denies existence of type "<<QType(qtype
)<<endl
);
567 * RFC 4035 Section 2.3:
568 * The bitmap for the NSEC RR at a delegation point requires special
569 * attention. Bits corresponding to the delegation NS RRset and any
570 * RRsets for which the parent zone has authoritative data MUST be set
572 if (referralToUnsigned
&& qtype
== QType::DS
) {
573 if (!nsec
->isSet(QType::NS
)) {
574 VLOG(log
, qname
<< ": However, no NS record exists at this level!"<<endl
);
575 return dState::NODENIAL
;
579 /* we know that the name exists (but this qtype doesn't) so except
580 if the answer was generated by a wildcard expansion, no wildcard
581 could have matched (rfc4035 section 5.4 bullet 1) */
582 if (needWildcardProof
&& (!isWildcardExpanded(owner
, validset
.second
.signatures
) || isWildcardExpandedOntoItself(owner
, validset
.second
.signatures
))) {
583 needWildcardProof
= false;
586 if (!needWildcardProof
) {
587 return dState::NXQTYPE
;
590 DNSName closestEncloser
= getClosestEncloserFromNSEC(qname
, owner
, nsec
->d_next
);
591 if (provesNoWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
592 return dState::NXQTYPE
;
595 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<endl
);
596 return dState::NODENIAL
;
599 if (qname
.isPartOf(owner
) && nsec
->isSet(QType::DNAME
)) {
600 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
602 In any negative response, the NSEC or NSEC3 [RFC5155] record type
603 bitmap SHOULD be checked to see that there was no DNAME that could
604 have been applied. If the DNAME bit in the type bitmap is set and
605 the query name is a subdomain of the closest encloser that is
606 asserted, then DNAME substitution should have been done, but the
607 substitution has not been done as specified.
609 VLOG(log
, qname
<< ": The DNAME bit is set and the query name is a subdomain of that NSEC"<< endl
);
610 return dState::NODENIAL
;
613 /* check if the whole NAME is denied existing */
614 if (isCoveredByNSEC(qname
, owner
, nsec
->d_next
)) {
615 VLOG(log
, qname
<< ": Is covered by ("<<owner
<<" to "<<nsec
->d_next
<<") ");
617 if (nsecProvesENT(qname
, owner
, nsec
->d_next
)) {
618 if (wantsNoDataProof
) {
619 /* if the name is an ENT and we received a NODATA answer,
620 we are fine with a NSEC proving that the name does not exist. */
621 VLOG_NO_PREFIX(log
, "Denies existence of type "<<qname
<<"/"<<QType(qtype
)<<" by proving that "<<qname
<<" is an ENT"<<endl
);
622 return dState::NXQTYPE
;
624 /* but for a NXDOMAIN proof, this doesn't make sense! */
625 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
);
626 return dState::NODENIAL
;
629 if (!needWildcardProof
) {
630 VLOG_NO_PREFIX(log
, "and we did not need a wildcard proof"<<endl
);
631 return dState::NXDOMAIN
;
634 VLOG_NO_PREFIX(log
, "but we do need a wildcard proof so ");
635 DNSName closestEncloser
= getClosestEncloserFromNSEC(qname
, owner
, nsec
->d_next
);
636 if (wantsNoDataProof
) {
637 VLOG_NO_PREFIX(log
, "looking for NODATA proof"<<endl
);
638 if (provesNoDataWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
639 return dState::NXQTYPE
;
643 VLOG_NO_PREFIX(log
, "looking for NO wildcard proof"<<endl
);
644 if (provesNoWildCard(qname
, qtype
, closestEncloser
, validrrsets
, log
)) {
645 return dState::NXDOMAIN
;
649 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<endl
);
650 return dState::NODENIAL
;
653 VLOG(log
, qname
<< ": Did not deny existence of "<<QType(qtype
)<<", "<<validset
.first
.first
<<"?="<<qname
<<", "<<nsec
->isSet(qtype
)<<", next: "<<nsec
->d_next
<<endl
);
655 } else if(validset
.first
.second
==QType::NSEC3
) {
656 for (const auto& record
: validset
.second
.records
) {
657 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
658 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
663 if (validset
.second
.signatures
.empty()) {
667 const DNSName
& hashedOwner
= validset
.first
.first
;
668 const DNSName signer
= getSigner(validset
.second
.signatures
);
669 if (!hashedOwner
.isPartOf(signer
)) {
670 VLOG(log
, qname
<< ": Owner "<<hashedOwner
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
674 if (qtype
== QType::DS
&& !qname
.isRoot() && signer
== qname
) {
675 VLOG(log
, qname
<< ": A NSEC3 RR from the child zone cannot deny the existence of a DS"<<endl
);
679 string hash
= getHashFromNSEC3(qname
, *nsec3
, cache
);
681 VLOG(log
, qname
<< ": Unsupported hash, ignoring"<<endl
);
682 return dState::INSECURE
;
687 VLOG(log
, qname
<< ":\tquery hash: "<<toBase32Hex(hash
)<<endl
);
688 string beginHash
= fromBase32Hex(hashedOwner
.getRawLabels()[0]);
690 // If the name exists, check if the qtype is denied
691 if (beginHash
== hash
) {
693 /* The NSEC3 is either a delegation one, from the parent zone, and
694 * must have the NS bit set but not the SOA one, or a regular NSEC3
695 * either at apex (signer == owner) or with the SOA or NS bits clear.
697 const bool notApex
= signer
.countLabels() < qname
.countLabels();
698 if (notApex
&& nsec3
->isSet(QType::NS
) && nsec3
->isSet(QType::SOA
)) {
699 VLOG(log
, qname
<< ": However, that NSEC3 is not at the apex and has both the NS and the SOA bits set!"<<endl
);
703 /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs":
704 Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume
705 nonexistence of any RRs below that zone cut, which include all RRs at
706 that (original) owner name other than DS RRs, and all RRs below that
707 owner name regardless of type.
709 if (qtype
!= QType::DS
&& isNSEC3AncestorDelegation(signer
, qname
, *nsec3
)) {
710 /* this is an "ancestor delegation" NSEC3 RR */
711 VLOG(log
, qname
<< ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
712 return dState::NODENIAL
;
715 if (!isTypeDenied(*nsec3
, QType(qtype
))) {
716 VLOG(log
, qname
<< ": Does _not_ deny existence of type "<<QType(qtype
)<<" for name "<<qname
<<" (not opt-out)."<<endl
);
717 return dState::NODENIAL
;
720 VLOG(log
, qname
<< ": Denies existence of type "<<QType(qtype
)<<" for name "<<qname
<<" (not opt-out)."<<endl
);
723 * RFC 5155 section 8.9:
724 * If there is an NSEC3 RR present in the response that matches the
725 * delegation name, then the validator MUST ensure that the NS bit is
726 * set and that the DS bit is not set in the Type Bit Maps field of the
729 if (referralToUnsigned
&& qtype
== QType::DS
) {
730 if (!nsec3
->isSet(QType::NS
)) {
731 VLOG(log
, qname
<< ": However, no NS record exists at this level!"<<endl
);
732 return dState::NODENIAL
;
736 return dState::NXQTYPE
;
742 /* if we have no NSEC3 records, we are done */
744 return dState::NODENIAL
;
747 DNSName
closestEncloser(qname
);
750 if (needWildcardProof
) {
751 /* We now need to look for a NSEC3 covering the closest (provable) encloser
752 RFC 5155 section-7.2.1
755 VLOG(log
, qname
<< ": Now looking for the closest encloser for "<<qname
<<endl
);
757 while (!found
&& closestEncloser
.chopOff()) {
759 for(const auto& validset
: validrrsets
) {
760 if(validset
.first
.second
==QType::NSEC3
) {
761 for(const auto& record
: validset
.second
.records
) {
762 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
763 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
768 const DNSName signer
= getSigner(validset
.second
.signatures
);
769 if (!validset
.first
.first
.isPartOf(signer
)) {
770 VLOG(log
, qname
<< ": Owner "<<validset
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
774 string hash
= getHashFromNSEC3(closestEncloser
, *nsec3
, cache
);
776 return dState::INSECURE
;
779 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
781 VLOG(log
, qname
<< ": Comparing "<<toBase32Hex(hash
)<<" ("<<closestEncloser
<<") against "<<toBase32Hex(beginHash
)<<endl
);
782 if (beginHash
== hash
) {
783 /* If the closest encloser is a delegation NS we know nothing about the names in the child zone. */
784 if (isNSEC3AncestorDelegation(signer
, validset
.first
.first
, *nsec3
)) {
785 VLOG(log
, qname
<< ": An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl
);
789 VLOG(log
, qname
<< ": Closest encloser for "<<qname
<<" is "<<closestEncloser
<<endl
);
792 if (nsec3
->isSet(QType::DNAME
)) {
793 /* rfc6672 section 5.3.2: DNAME Bit in NSEC Type Map
795 In any negative response, the NSEC or NSEC3 [RFC5155] record type
796 bitmap SHOULD be checked to see that there was no DNAME that could
797 have been applied. If the DNAME bit in the type bitmap is set and
798 the query name is a subdomain of the closest encloser that is
799 asserted, then DNAME substitution should have been done, but the
800 substitution has not been done as specified.
802 VLOG(log
, qname
<< ":\tThe closest encloser NSEC3 has the DNAME bit is set"<<endl
);
803 return dState::NODENIAL
;
817 /* RFC 5155 section-7.2.6:
818 "It is not necessary to return an NSEC3 RR that matches the closest encloser,
819 as the existence of this closest encloser is proven by the presence of the
820 expanded wildcard in the response.
823 unsigned int closestEncloserLabelsCount
= closestEncloser
.countLabels();
824 while (wildcardLabelsCount
> 0 && closestEncloserLabelsCount
> wildcardLabelsCount
) {
825 closestEncloser
.chopOff();
826 closestEncloserLabelsCount
--;
830 bool nextCloserFound
= false;
831 bool isOptOut
= false;
834 /* now that we have found the closest (provable) encloser,
835 we can construct the next closer (RFC7129 section-5.5) name
836 and look for a NSEC3 RR covering it */
837 unsigned int labelIdx
= qname
.countLabels() - closestEncloser
.countLabels();
839 DNSName
nextCloser(closestEncloser
);
840 nextCloser
.prependRawLabel(qname
.getRawLabel(labelIdx
- 1));
841 VLOG(log
, qname
<< ":Looking for a NSEC3 covering the next closer name "<<nextCloser
<<endl
);
843 for(const auto& validset
: validrrsets
) {
844 if(validset
.first
.second
==QType::NSEC3
) {
845 for(const auto& record
: validset
.second
.records
) {
846 VLOG(log
, qname
<< ":\t"<<record
->getZoneRepresentation()<<endl
);
847 auto nsec3
= std::dynamic_pointer_cast
<const NSEC3RecordContent
>(record
);
852 string hash
= getHashFromNSEC3(nextCloser
, *nsec3
, cache
);
854 return dState::INSECURE
;
857 const DNSName signer
= getSigner(validset
.second
.signatures
);
858 if (!validset
.first
.first
.isPartOf(signer
)) {
859 VLOG(log
, qname
<< ": Owner "<<validset
.first
.first
<<" is not part of the signer "<<signer
<<", ignoring"<<endl
);
863 string beginHash
=fromBase32Hex(validset
.first
.first
.getRawLabels()[0]);
865 VLOG(log
, qname
<< ": Comparing "<<toBase32Hex(hash
)<<" against "<<toBase32Hex(beginHash
)<<" -> "<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
866 if (isCoveredByNSEC3Hash(hash
, beginHash
, nsec3
->d_nexthash
)) {
867 VLOG(log
, qname
<< ": Denies existence of name "<<qname
<<"/"<<QType(qtype
));
868 nextCloserFound
= true;
870 if (nsec3
->isOptOut()) {
871 VLOG_NO_PREFIX(log
, " but is opt-out!");
875 VLOG_NO_PREFIX(log
, endl
);
878 VLOG(log
, qname
<< ": Did not cover us ("<<qname
<<"), start="<<validset
.first
.first
<<", us="<<toBase32Hex(hash
)<<", end="<<toBase32Hex(nsec3
->d_nexthash
)<<endl
);
881 if (nextCloserFound
) {
888 if (nextCloserFound
) {
889 bool wildcardExists
= false;
890 /* RFC 7129 section-5.6 */
891 if (needWildcardProof
&& !provesNSEC3NoWildCard(closestEncloser
, qtype
, validrrsets
, &wildcardExists
, cache
, log
)) {
893 VLOG(log
, qname
<< ": But the existence of a wildcard is not denied for "<<qname
<<"/"<<QType(qtype
)<<endl
);
894 return dState::NODENIAL
;
899 return dState::OPTOUT
;
901 if (wildcardExists
) {
902 return dState::NXQTYPE
;
904 return dState::NXDOMAIN
;
907 // There were no valid NSEC(3) records
908 return dState::NODENIAL
;
911 bool isRRSIGNotExpired(const time_t now
, const RRSIGRecordContent
& sig
)
913 // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
914 return sig
.d_sigexpire
>= now
;
917 bool isRRSIGIncepted(const time_t now
, const RRSIGRecordContent
& sig
)
919 // Should use https://www.rfc-editor.org/rfc/rfc4034.txt section 3.1.5
920 return sig
.d_siginception
- g_signatureInceptionSkew
<= now
;
923 static bool checkSignatureWithKey(const DNSName
& qname
, time_t now
, const RRSIGRecordContent
& sig
, const DNSKEYRecordContent
& key
, const std::string
& msg
, vState
& ede
, const OptLog
& log
)
928 - 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.
929 - 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.
931 if (isRRSIGIncepted(now
, sig
) && isRRSIGNotExpired(now
, sig
)) {
932 auto dke
= DNSCryptoKeyEngine::makeFromPublicKeyString(key
.d_algorithm
, key
.d_key
);
933 result
= dke
->verify(msg
, sig
.d_signature
);
934 VLOG(log
, qname
<< ": Signature by key with tag "<<sig
.d_tag
<<" and algorithm "<<DNSSECKeeper::algorithm2name(sig
.d_algorithm
)<<" was " << (result
? "" : "NOT ")<<"valid"<<endl
);
936 ede
= vState::BogusNoValidRRSIG
;
940 ede
= ((sig
.d_siginception
- g_signatureInceptionSkew
) > now
) ? vState::BogusSignatureNotYetValid
: vState::BogusSignatureExpired
;
941 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
);
944 catch (const std::exception
& e
) {
945 VLOG(log
, qname
<< ": Could not make a validator for signature: "<<e
.what()<<endl
);
946 ede
= vState::BogusUnsupportedDNSKEYAlgo
;
951 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
, bool validateAllSigs
)
953 bool foundKey
= false;
954 bool isValid
= false;
955 bool allExpired
= true;
956 bool noneIncepted
= true;
958 for(const auto& signature
: signatures
) {
959 unsigned int labelCount
= name
.countLabels();
960 if (signature
->d_labels
> labelCount
) {
961 VLOG(log
, name
<<": Discarding invalid RRSIG whose label count is "<<signature
->d_labels
<<" while the RRset owner name has only "<<labelCount
<<endl
);
965 auto keysMatchingTag
= getByTag(keys
, signature
->d_tag
, signature
->d_algorithm
, log
);
967 if (keysMatchingTag
.empty()) {
968 VLOG(log
, name
<<": No key provided for "<<signature
->d_tag
<<" and algorithm "<<std::to_string(signature
->d_algorithm
)<<endl
;);
972 string msg
= getMessageForRRSET(name
, *signature
, toSign
, true);
973 for (const auto& key
: keysMatchingTag
) {
974 vState ede
= vState::Indeterminate
;
975 bool signIsValid
= checkSignatureWithKey(name
, now
, *signature
, *key
, msg
, ede
, log
);
980 VLOG(log
, name
<< ": Validated "<<name
<<"/"<<DNSRecordContent::NumberToType(signature
->d_type
)<<endl
);
981 // cerr<<"valid"<<endl;
982 // cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
985 VLOG(log
, name
<< ": signature invalid"<<endl
);
986 if (isRRSIGIncepted(now
, *signature
)) {
987 noneIncepted
= false;
989 if (isRRSIGNotExpired(now
, *signature
)) {
994 if (signIsValid
&& !validateAllSigs
) {
995 return vState::Secure
;
1001 return vState::Secure
;
1004 return vState::BogusNoValidRRSIG
;
1007 // ede should be vState::BogusSignatureNotYetValid
1008 return vState::BogusSignatureNotYetValid
;
1011 // ede should be vState::BogusSignatureExpired);
1012 return vState::BogusSignatureExpired
;
1015 return vState::BogusNoValidRRSIG
;
1019 // should return vState, zone cut and validated keyset
1020 // i.e. www.7bits.nl -> insecure/7bits.nl/[]
1021 // www.powerdnssec.org -> secure/powerdnssec.org/[keys]
1022 // www.dnssec-failed.org -> bogus/dnssec-failed.org/[]
1024 cspmap_t
harvestCSPFromRecs(const vector
<DNSRecord
>& recs
)
1027 for(const auto& rec
: recs
) {
1028 // cerr<<"res "<<rec.d_name<<"/"<<rec.d_type<<endl;
1029 if (rec
.d_type
== QType::OPT
) {
1033 if(rec
.d_type
== QType::RRSIG
) {
1034 auto rrc
= getRR
<RRSIGRecordContent
>(rec
);
1036 cspmap
[{rec
.d_name
,rrc
->d_type
}].signatures
.push_back(rrc
);
1040 cspmap
[{rec
.d_name
, rec
.d_type
}].records
.insert(rec
.getContent());
1046 bool getTrustAnchor(const map
<DNSName
,dsmap_t
>& anchors
, const DNSName
& zone
, dsmap_t
&res
)
1048 const auto& iter
= anchors
.find(zone
);
1050 if (iter
== anchors
.cend()) {
1058 bool haveNegativeTrustAnchor(const map
<DNSName
,std::string
>& negAnchors
, const DNSName
& zone
, std::string
& reason
)
1060 const auto& iter
= negAnchors
.find(zone
);
1062 if (iter
== negAnchors
.cend()) {
1066 reason
= iter
->second
;
1070 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
)
1073 * Check all DNSKEY records against all DS records and place all DNSKEY records
1074 * that have DS records (that we support the algo for) in the tentative key storage
1076 for (const auto& dsrc
: dsmap
)
1078 auto record
= getByTag(tkeys
, dsrc
.d_tag
, dsrc
.d_algorithm
, log
);
1079 // 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;
1081 for (const auto& drc
: record
)
1083 bool isValid
= false;
1084 bool dsCreated
= false;
1085 DSRecordContent dsrc2
;
1087 dsrc2
= makeDSFromDNSKey(zone
, *drc
, dsrc
.d_digesttype
);
1089 isValid
= dsrc
== dsrc2
;
1091 catch (const std::exception
&e
) {
1092 VLOG(log
, zone
<< ": Unable to make DS from DNSKey: "<<e
.what()<<endl
);
1096 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
);
1098 validkeys
.insert(drc
);
1102 VLOG(log
, zone
<< ": DNSKEY did not match the DS, parent DS: "<<dsrc
.getZoneRepresentation() << " ! = "<<dsrc2
.getZoneRepresentation()<<endl
);
1108 vState ede
= vState::BogusNoValidDNSKEY
;
1110 // cerr<<"got "<<validkeys.size()<<"/"<<tkeys.size()<<" valid/tentative keys"<<endl;
1111 // these counts could be off if we somehow ended up with
1112 // duplicate keys. Should switch to a type that prevents that.
1113 if (validkeys
.size() < tkeys
.size())
1115 // this should mean that we have one or more DS-validated DNSKEYs
1116 // but not a fully validated DNSKEY set, yet
1117 // one of these valid DNSKEYs should be able to validate the
1119 for (const auto& sig
: sigs
)
1121 // 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;
1122 auto bytag
= getByTag(validkeys
, sig
->d_tag
, sig
->d_algorithm
, log
);
1124 if (bytag
.empty()) {
1128 string msg
= getMessageForRRSET(zone
, *sig
, toSign
);
1129 for (const auto& key
: bytag
) {
1130 // cerr<<"validating : ";
1131 bool signIsValid
= checkSignatureWithKey(zone
, now
, *sig
, *key
, msg
, ede
, log
);
1134 VLOG(log
, zone
<< ": Validation succeeded - whole DNSKEY set is valid"<<endl
);
1138 VLOG(log
, zone
<< ": Validation did not succeed!"<<endl
);
1140 // if(validkeys.empty()) cerr<<"did not manage to validate DNSKEY set based on DS-validated KSK, only passing KSK on"<<endl;
1144 if (validkeys
.size() < tkeys
.size()) {
1145 /* so we failed to validate the whole set, let's try to find out why exactly */
1146 bool dnskeyAlgoSupported
= false;
1147 bool dsDigestSupported
= false;
1149 for (const auto& dsrc
: dsmap
)
1151 if (DNSCryptoKeyEngine::isAlgorithmSupported(dsrc
.d_algorithm
)) {
1152 dnskeyAlgoSupported
= true;
1153 if (DNSCryptoKeyEngine::isDigestSupported(dsrc
.d_digesttype
)) {
1154 dsDigestSupported
= true;
1159 if (!dnskeyAlgoSupported
) {
1160 return vState::BogusUnsupportedDNSKEYAlgo
;
1162 if (!dsDigestSupported
) {
1163 return vState::BogusUnsupportedDSDigestType
;
1166 bool zoneKey
= false;
1167 bool notRevoked
= false;
1168 bool validProtocol
= false;
1170 for (const auto& key
: tkeys
) {
1171 if (!isAZoneKey(*key
)) {
1176 if (isRevokedKey(*key
)) {
1181 if (key
->d_protocol
!= 3) {
1184 validProtocol
= true;
1188 return vState::BogusNoZoneKeyBitSet
;
1191 return vState::BogusRevokedDNSKEY
;
1193 if (!validProtocol
) {
1194 return vState::BogusInvalidDNSKEYProtocol
;
1200 return vState::Secure
;
1203 bool isSupportedDS(const DSRecordContent
& dsrec
, const OptLog
& log
)
1205 if (!DNSCryptoKeyEngine::isAlgorithmSupported(dsrec
.d_algorithm
)) {
1206 VLOG(log
, "Discarding DS "<<dsrec
.d_tag
<<" because we don't support algorithm number "<<std::to_string(dsrec
.d_algorithm
)<<endl
);
1210 if (!DNSCryptoKeyEngine::isDigestSupported(dsrec
.d_digesttype
)) {
1211 VLOG(log
, "Discarding DS "<<dsrec
.d_tag
<<" because we don't support digest number "<<std::to_string(dsrec
.d_digesttype
)<<endl
);
1218 DNSName
getSigner(const std::vector
<std::shared_ptr
<const RRSIGRecordContent
> >& signatures
)
1220 for (const auto& sig
: signatures
) {
1222 return sig
->d_signer
;
1229 const std::string
& vStateToString(vState state
)
1231 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" };
1232 return vStates
.at(static_cast<size_t>(state
));
1235 std::ostream
& operator<<(std::ostream
&ostr
, const vState dstate
)
1237 ostr
<<vStateToString(dstate
);
1241 std::ostream
& operator<<(std::ostream
&ostr
, const dState dstate
)
1243 static const std::vector
<std::string
> dStates
= {"no denial", "inconclusive", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
1244 ostr
<<dStates
.at(static_cast<size_t>(dstate
));
1248 void updateDNSSECValidationState(vState
& state
, const vState stateUpdate
)
1250 if (stateUpdate
== vState::TA
) {
1251 state
= vState::Secure
;
1253 else if (stateUpdate
== vState::NTA
) {
1254 state
= vState::Insecure
;
1256 else if (vStateIsBogus(stateUpdate
) || state
== vState::Indeterminate
) {
1257 state
= stateUpdate
;
1259 else if (stateUpdate
== vState::Insecure
) {
1260 if (!vStateIsBogus(state
)) {
1261 state
= vState::Insecure
;