auto zonemdRecords = zonemd.getZONEMDs();
zonemdCount = zonemdRecords.size();
+
+ // De we need to do a denial validation?
if (zonemdCount == 0) {
- // Per RFC we should actually prove non-existence of ZONEMD
- // as downgrade attacks could be possible by an in the middle party zapping ZONEMDs
- return dnsKeyState;
+ const auto& nsecs = zonemd.getNSECs();
+ const auto& nsec3s = zonemd.getNSEC3s();
+ cspmap_t csp;
+
+ vState nsecValidationStatus;
+ if (nsecs.records.size() > 0 && nsecs.signatures.size() > 0) {
+ nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsecs.records, nsecs.signatures, validKeys);
+ csp.emplace(std::make_pair(d_zone, QType::NSEC), nsecs);
+ } else if (nsec3s.records.size() > 0 && nsec3s.signatures.size() > 0) {
+ nsecValidationStatus = validateWithKeySet(d_now, d_zone, nsec3s.records, nsec3s.signatures, validKeys);
+ csp.emplace(std::make_pair(d_zone, QType::NSEC3), nsec3s);
+ } else {
+ d_log->info("No NSEC(3) records and/or RRSIGS found to deny ZONEMD");
+ return vState::BogusInvalidDenial;
+ }
+
+ if (nsecValidationStatus != vState::Secure) {
+ d_log->info("zone NSEC(3) record does no validate");
+ return nsecValidationStatus;
+ }
+ auto denial = getDenial(csp, d_zone, QType::ZONEMD, false, false, true);
+ switch (denial) {
+ case dState::NXQTYPE:
+ d_log->info("Validated denial of absence of ZONEMD record");
+ return vState::Secure;
+ default:
+ d_log->info("No ZONEMD record, but NSEC(3) record does not deny it");
+ return vState::BogusInvalidDenial;
+ }
}
- records.clear();
- // Collect the ZONEMD records and validate them using the validted DNSSKEYs
+ // Collect the ZONEMD records and validate them using the validated DNSSKEYs
+ records.clear();
for (const auto& rec : zonemdRecords) {
records.emplace(rec);
}
throw PDNSException("ZONEMD required DNSSEC validation failed");
}
if (validationStatus != vState::Secure && validationStatus != vState::Insecure) {
- throw PDNSException("ZONEMD record DNSSEC Validation failed");
+ throw PDNSException("ZONEMD record DNSSEC validation failed");
}
}
- If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODENIAL
if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
- If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
- NXQTYPE is the name is proven to be ENT and NXDOMAIN otherwise.
+ NXQTYPE if the name is proven to be ENT and NXDOMAIN otherwise.
- If `needWildcardProof` is false, the proof that a wildcard covering this qname|qtype is not checked. It is
useful when we have a positive answer synthesized from a wildcard and we only need to prove that the exact
name does not exist.
}
break;
}
- case QType::RRSIG:
- d_rrsigs.emplace_back(std::dynamic_pointer_cast<RRSIGRecordContent>(drc));
+ case QType::RRSIG: {
+ auto rrsig = std::dynamic_pointer_cast<RRSIGRecordContent>(drc);
+ if (rrsig->d_type == QType::NSEC) {
+ d_nsecs.signatures.emplace_back(rrsig);
+ }
+ else if (rrsig->d_type == QType::NSEC3) {
+ d_nsecs3.signatures.emplace_back(rrsig);
+ }
+ d_rrsigs.emplace_back(rrsig);
+ break;
+ }
+ case QType::NSEC:
+ d_nsecs.records.emplace(std::dynamic_pointer_cast<NSECRecordContent>(drc));
+ break;
+ case QType::NSEC3:
+ d_nsecs3.records.emplace(std::dynamic_pointer_cast<NSEC3RecordContent>(drc));
break;
}
}
}
break;
}
- case QType::RRSIG:
- d_rrsigs.emplace_back(std::dynamic_pointer_cast<RRSIGRecordContent>(record.d_content));
+ case QType::RRSIG: {
+ auto rrsig = std::dynamic_pointer_cast<RRSIGRecordContent>(record.d_content);
+ if (rrsig->d_type == QType::NSEC) {
+ d_nsecs.signatures.emplace_back(rrsig);
+ }
+ else if (rrsig->d_type == QType::NSEC3) {
+ d_nsecs3.signatures.emplace_back(rrsig);
+ }
+ d_rrsigs.emplace_back(rrsig);
+ break;
+ }
+ case QType::NSEC:
+ d_nsecs.records.emplace(std::dynamic_pointer_cast<NSECRecordContent>(record.d_content));
+ break;
+ case QType::NSEC3:
+ d_nsecs3.records.emplace(std::dynamic_pointer_cast<NSEC3RecordContent>(record.d_content));
break;
}
}
#include "dnsname.hh"
#include "qtype.hh"
#include "dnsrecords.hh"
+#include "validate.hh"
class ZoneParserTNG;
return ret;
}
+ // Return the zone's apex NSECs with signatures
+ const ContentSigPair& getNSECs() const
+ {
+ return d_nsecs;
+ }
+
+ // Return the zone's apex NSEC3s with signatures
+ const ContentSigPair& getNSEC3s() const
+ {
+ return d_nsecs3;
+ }
+
private:
typedef std::pair<DNSName, QType> RRSetKey_t;
typedef std::vector<std::shared_ptr<DNSRecordContent>> RRVector_t;
std::shared_ptr<SOARecordContent> d_soaRecordContent;
std::set<shared_ptr<DNSKEYRecordContent>> d_dnskeys;
std::vector<shared_ptr<RRSIGRecordContent>> d_rrsigs;
+ ContentSigPair d_nsecs;
+ ContentSigPair d_nsecs3;
const DNSName d_zone;
};