From: Remi Gacogne Date: Thu, 31 Dec 2020 15:28:30 +0000 (+0100) Subject: rec: Small cleanup of DNSSEC denial validation X-Git-Tag: dnsdist-1.6.0-alpha2~12^2~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cd4beb37e896519da1772045c1a83d1ef3754741;p=thirdparty%2Fpdns.git rec: Small cleanup of DNSSEC denial validation --- diff --git a/pdns/recursordist/aggressive_nsec.cc b/pdns/recursordist/aggressive_nsec.cc index ffc3071399..a7511cba28 100644 --- a/pdns/recursordist/aggressive_nsec.cc +++ b/pdns/recursordist/aggressive_nsec.cc @@ -289,11 +289,7 @@ bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptrisSet(type.getCode())) { - return false; - } - - if (nsec3->isSet(QType::CNAME)) { + if (!isTypeDenied(nsec3, type)) { return false; } @@ -315,7 +311,6 @@ bool AggressiveNSECCache::getNSEC3Denial(time_t now, std::shared_ptrisSet(type.getCode())) { - return false; - } - - if (nsec3->isSet(QType::CNAME)) { + if (!isTypeDenied(nsec3, type)) { return false; } @@ -440,7 +431,7 @@ bool AggressiveNSECCache::getDenial(time_t now, const DNSName& name, const QType cerr<<"nsecFound "<(wcEntry.d_record); denial = matchesNSEC(wc, type.getCode(), wcEntry.d_owner, nsecContent, wcEntry.d_signatures); - if (denial == dState::NODENIAL) { + if (denial == dState::NODENIAL || denial == dState::INCONCLUSIVE) { /* too complicated for now */ /* we would need: - to store wildcard entries in the non-expanded form in the record cache, in addition to their expanded form ; diff --git a/pdns/validate.cc b/pdns/validate.cc index c991dbe9dc..72fd2bcaef 100644 --- a/pdns/validate.cc +++ b/pdns/validate.cc @@ -207,7 +207,6 @@ static bool isWildcardExpandedOntoItself(const DNSName& owner, const std::vector /* if this is a wildcard NSEC, the owner name has been modified to match the name. Make sure we use the original '*' form. */ -#warning we should not need to export this DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector >& signatures) { DNSName result = initialOwner; @@ -271,7 +270,7 @@ static bool provesNoDataWildCard(const DNSName& qname, const uint16_t qtype, con if (qname.isPartOf(wildcard)) { LOG("\tWildcard matches"); - if (qtype == 0 || (!nsec->isSet(qtype) && !nsec->isSet(QType::CNAME) && !nsec->isSet(QType::DNAME))) { + if (qtype == 0 || isTypeDenied(nsec, QType(qtype))) { LOG(" and proves that the type did not exist"<isSet(qtype) && !nsec3->isSet(QType::CNAME) && !nsec3->isSet(QType::DNAME))) { + + /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs": + Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume + nonexistence of any RRs below that zone cut, which include all RRs at + that (original) owner name other than DS RRs, and all RRs below that + owner name regardless of type. + */ + if (qtype != QType::DS && isNSEC3AncestorDelegation(signer, v.first.first, nsec3)) { + /* this is an "ancestor delegation" NSEC3 RR */ + LOG(" BUT an ancestor delegation NSEC3 RR can only deny the existence of a DS"<isSet(qtype)) { - return dState::NODENIAL; - } - - /* RFC 6840 section 4.3 */ - if (nsec->isSet(QType::CNAME)) { - return dState::NODENIAL; - } - - if (nsec->isSet(QType::DNAME)) { + if (!isTypeDenied(nsec, QType(qtype))) { + LOG("Does _not_ deny existence of type "<d_next)) { + LOG(name<<" is covered "); if (nsecProvesENT(name, owner, nsec->d_next)) { + LOG("Denies existence of type "<getZoneRepresentation()<(r); - if(!nsec) + if (!nsec) { continue; + } + const DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures); const DNSName signer = getSigner(v.second.signatures); - if (!v.first.first.isPartOf(signer)) - continue; + if (!v.first.first.isPartOf(signer) || !owner.isPartOf(signer) ) { + continue; + } - const DNSName owner = getNSECOwnerName(v.first.first, v.second.signatures); /* RFC 6840 section 4.1 "Clarifications on Nonexistence Proofs": Ancestor delegation NSEC or NSEC3 RRs MUST NOT be used to assume nonexistence of any RRs below that zone cut, which include all RRs at @@ -483,7 +493,6 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16 if (qname.isPartOf(owner) && isNSECAncestorDelegation(signer, owner, nsec)) { /* this is an "ancestor delegation" NSEC RR */ if (!(qtype == QType::DS && qname == owner)) { - LOG("type is "<isSet(QType::NS))<<", SOA is "<isSet(QType::SOA))<<", signer is "<isSet(qtype)) { + if (!isTypeDenied(nsec, QType(qtype))) { LOG("Does _not_ deny existence of type "<isSet(QType::CNAME)) { - LOG("However a CNAME exists"<isSet(QType::DNAME)) { - LOG("However a CNAME exists"<isSet(qtype)<<", next: "<d_next<isSet(qtype)<<", next: "<d_next<d_salt.length()<<", iterations: "<d_iterations<<", hashed: "<isSet(QType::NS))<<", SOA is "<isSet(QType::SOA))<<", signer is "<isSet(qtype)) { - LOG("Does _not_ deny existence of type "<isSet(QType::CNAME)) { - LOG("However a CNAME exists"<isSet(QType::DNAME)) { - LOG("However a CNAME exists"< "<d_nexthash)<d_nexthash)) { + if (isCoveredByNSEC3Hash(h, beginHash, nsec3->d_nexthash)) { LOG("Denies existence of name "< dStates = {"no denial", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"}; + static const std::vector dStates = {"no denial", "inconclusive", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"}; os<(d)); return os; } diff --git a/pdns/validate.hh b/pdns/validate.hh index 434e7ab358..ae3831f8dd 100644 --- a/pdns/validate.hh +++ b/pdns/validate.hh @@ -41,7 +41,7 @@ inline bool vStateIsBogus(vState state) } // NSEC(3) results -enum class dState : uint8_t { NODENIAL, NXDOMAIN, NXQTYPE, ENT, INSECURE, OPTOUT}; +enum class dState : uint8_t { NODENIAL, INCONCLUSIVE, NXDOMAIN, NXQTYPE, ENT, INSECURE, OPTOUT}; std::ostream& operator<<(std::ostream &os, const vState d); std::ostream& operator<<(std::ostream &os, const dState d); @@ -93,7 +93,25 @@ bool isWildcardExpanded(unsigned int labelCount, const std::shared_ptr& sign); void updateDNSSECValidationState(vState& state, const vState stateUpdate); -dState matchesNSEC(const DNSName& name, uint16_t qtype, const DNSName& nsecOwner, const std::shared_ptr& nsecRecord, const std::vector>& signatures); +dState matchesNSEC(const DNSName& name, uint16_t qtype, const DNSName& nsecOwner, const std::shared_ptr& nsec, const std::vector>& signatures); bool isNSEC3AncestorDelegation(const DNSName& signer, const DNSName& owner, const std::shared_ptr& nsec3); DNSName getNSECOwnerName(const DNSName& initialOwner, const std::vector >& signatures); + +template bool isTypeDenied(const NSEC& nsec, const QType& type) +{ + if (nsec->isSet(type.getCode())) { + return false; + } + + /* RFC 6840 section 4.3 */ + if (nsec->isSet(QType::CNAME)) { + return false; + } + + if (nsec->isSet(QType::DNAME)) { + return false; + } + + return true; +}