]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
If we do not find ZONEMD record(s) and the zone is DNSSEC validated, validate the...
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 24 Jan 2022 09:19:51 +0000 (10:19 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 24 Jan 2022 09:35:42 +0000 (10:35 +0100)
pdns/recursordist/rec-zonetocache.cc
pdns/validate.cc
pdns/zonemd.cc
pdns/zonemd.hh

index 94b4e87d9e84825d4eaa45f84e3fc2f06f119cc8..68f2aed6c8c9e5dc0bf04013c7b1225f3dcf5b19 100644 (file)
@@ -275,14 +275,42 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
 
   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);
   }
@@ -332,7 +360,7 @@ void ZoneData::ZoneToCache(const RecZoneToCache::Config& config)
       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");
     }
   }
 
index 1417e5ab491cae10d1822278caa6681cfbc74948..bcf9c95d2e72b6afc9624d41ae765fc1691f5f2a 100644 (file)
@@ -476,7 +476,7 @@ dState matchesNSEC(const DNSName& name, uint16_t qtype, const DNSName& nsecOwner
   - 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.
index 0fe904a45a9e60b050b68e5f2e3cd5cece703913..8aa876a1c4304ef237d14b7b5584d5e2fa40e639 100644 (file)
@@ -46,8 +46,22 @@ void pdns::ZoneMD::readRecords(ZoneParserTNG& zpt)
         }
         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;
       }
     }
@@ -90,8 +104,22 @@ void pdns::ZoneMD::readRecord(const DNSRecord& record)
       }
       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;
     }
   }
index 8f717a0b1f3fb7a093b0e6eb618359c5248c274a..ea76663aae67870233b084b6a871940c2b78e058 100644 (file)
@@ -28,6 +28,7 @@
 #include "dnsname.hh"
 #include "qtype.hh"
 #include "dnsrecords.hh"
+#include "validate.hh"
 
 class ZoneParserTNG;
 
@@ -79,6 +80,18 @@ public:
     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;
@@ -115,6 +128,8 @@ private:
   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;
 };