]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Improved storage of nsec3 info and a few tweaks
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 24 Jan 2022 15:27:22 +0000 (16:27 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 24 Jan 2022 15:30:10 +0000 (16:30 +0100)
pdns/pdnsutil.cc
pdns/recursordist/rec-zonetocache.cc
pdns/testrunner.cc
pdns/zonemd.cc
pdns/zonemd.hh

index 594a854a3ed3a7e168875839ea70ab0e78c62b66..6b99588d50dd3760d90b71a1a6676e94f4d923ad 100644 (file)
@@ -44,6 +44,7 @@ StatBag S;
 AuthPacketCache PC;
 AuthQueryCache QC;
 AuthZoneCache g_zoneCache;
+uint16_t g_maxNSEC3Iterations{0};
 
 namespace po = boost::program_options;
 po::variables_map g_vm;
index 4ee5c6ec265668b5ab57e7c972f5d7a81a9c1bcb..7347b613212689c8070923d22dcbfc3b6585ffc0 100644 (file)
@@ -87,6 +87,9 @@ bool ZoneData::isRRSetAuth(const DNSName& qname, QType qtype) const
 
 void ZoneData::parseDRForCache(DNSRecord& dr)
 {
+  if (dr.d_class != QClass::IN) {
+    return;
+  }
   const auto key = pair(dr.d_name, dr.d_type);
 
   dr.d_ttl += d_now;
@@ -283,11 +286,24 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
     cspmap_t csp;
 
     vState nsecValidationStatus;
+
     if (nsecs.records.size() > 0 && nsecs.signatures.size() > 0) {
+      // Valdidate the NSEC
       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 && !zonemd.getNSEC3Label().empty()) {
+    else if (nsec3s.records.size() > 0 && nsec3s.signatures.size() > 0) {
+      // Validate NSEC3PARAMS
+      records.clear();
+      for (const auto& rec : zonemd.getNSEC3Params()) {
+        records.emplace(rec);
+      }
+      nsecValidationStatus = validateWithKeySet(d_now, d_zone, records, zonemd.getRRSIGs(), validKeys);
+      if (nsecValidationStatus != vState::Secure) {
+        d_log->info("NSEC3PARAMS records did not validate");
+        return nsecValidationStatus;
+      }
+      // Valdidate the NSEC3
       nsecValidationStatus = validateWithKeySet(d_now, zonemd.getNSEC3Label(), nsec3s.records, nsec3s.signatures, validKeys);
       csp.emplace(std::make_pair(zonemd.getNSEC3Label(), QType::NSEC3), nsec3s);
     }
@@ -297,7 +313,7 @@ vState ZoneData::dnssecValidate(pdns::ZoneMD& zonemd, size_t& zonemdCount) const
     }
 
     if (nsecValidationStatus != vState::Secure) {
-      d_log->info("zone NSEC(3) record does no validate");
+      d_log->info("zone NSEC(3) record does not validate");
       return nsecValidationStatus;
     }
     auto denial = getDenial(csp, d_zone, QType::ZONEMD, false, false, true);
index 7adf7bb1b8437aef6af15e903a65b19e6e19bce9..4f24cb4d655c32cfb5b6e09868d5dd0eb9740cf2 100644 (file)
@@ -13,6 +13,7 @@ StatBag S;
 AuthPacketCache PC;
 AuthQueryCache QC;
 AuthZoneCache g_zoneCache;
+uint16_t g_maxNSEC3Iterations{0};
 
 ArgvMap &arg()
 {
index 843a68cc77454a8746bdd20b9361213bc0877395..1ee0b58b9cde55b601b8544aef46f4afc5c4e51a 100644 (file)
@@ -46,11 +46,11 @@ void pdns::ZoneMD::readRecord(const DNSRecord& record)
   if (!record.d_name.isPartOf(d_zone) && record.d_name != d_zone) {
     return;
   }
-  if (record.d_type == QType::SOA && d_soaRecordContent) {
+  if (record.d_class == QClass::IN && record.d_type == QType::SOA && d_soaRecordContent) {
     return;
   }
 
-  if (record.d_name == d_zone) {
+  if (record.d_class == QClass::IN && record.d_name == d_zone) {
     switch (record.d_type) {
     case QType::SOA: {
       d_soaRecordContent = std::dynamic_pointer_cast<SOARecordContent>(record.d_content);
@@ -84,13 +84,11 @@ void pdns::ZoneMD::readRecord(const DNSRecord& record)
       if (rrsig == nullptr) {
         throw PDNSException("Invalid RRSIG record");
       }
+      d_rrsigs.emplace_back(rrsig);
       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);
+      // RRSIG on NEC3 handled below
       break;
     }
     case QType::NSEC: {
@@ -112,21 +110,31 @@ void pdns::ZoneMD::readRecord(const DNSRecord& record)
       if (g_maxNSEC3Iterations && param->d_iterations > g_maxNSEC3Iterations) {
         return;
       }
+      d_nsec3params.emplace_back(param);
       d_nsec3label = d_zone;
       d_nsec3label.prependRawLabel(toBase32Hex(hashQNameWithSalt(param->d_salt, param->d_iterations, d_zone)));
-      // XXX We could filter the collected NSEC3 and their RRSIGs in d_nsecs3 at this point as we now know the right label
+      // Zap the NSEC3 at labels that we now know are not relevant
+      for (auto it = d_nsec3s.begin(); it != d_nsec3s.end();) {
+        if (it->first != d_nsec3label) {
+          it = d_nsec3s.erase(it);
+        }
+        else {
+          ++it;
+        }
+      }
       break;
     }
     }
   }
-  if (d_nsec3label.empty() || record.d_name == d_nsec3label) {
+  // Until we have seen the NSEC3PARAM record, we save all of them, as we do not know the label for the zone yet
+  if (record.d_class == QClass::IN && (d_nsec3label.empty() || record.d_name == d_nsec3label)) {
     switch (record.d_type) {
     case QType::NSEC3: {
       auto nsec3 = std::dynamic_pointer_cast<NSEC3RecordContent>(record.d_content);
       if (nsec3 == nullptr) {
         throw PDNSException("Invalid NSEC3 record");
       }
-      d_nsecs3.records.emplace(nsec3);
+      d_nsec3s[record.d_name].records.emplace(nsec3);
       break;
     }
     case QType::RRSIG: {
@@ -135,7 +143,7 @@ void pdns::ZoneMD::readRecord(const DNSRecord& record)
         throw PDNSException("Invalid RRSIG record");
       }
       if (rrsig->d_type == QType::NSEC3) {
-        d_nsecs3.signatures.emplace_back(rrsig);
+        d_nsec3s[record.d_name].signatures.emplace_back(rrsig);
       }
       break;
     }
index df834130c1d5cc0d74f89c855c939526c190dc9e..63551fec3e011df9966d88a276a400be7168dd9c 100644 (file)
@@ -89,13 +89,20 @@ public:
   // Return the zone's apex NSEC3s with signatures
   const ContentSigPair& getNSEC3s() const
   {
-    return d_nsecs3;
+    const auto it = d_nsec3s.find(d_nsec3label);
+    return it == d_nsec3s.end() ? empty : d_nsec3s.at(d_nsec3label);
   }
 
-  const DNSName& getNSEC3Label() const {
+  const DNSName& getNSEC3Label() const
+  {
     return d_nsec3label;
   }
 
+  const std::vector<shared_ptr<NSEC3PARAMRecordContent>>& getNSEC3Params() const
+  {
+    return d_nsec3params;
+  }
+
 private:
   typedef std::pair<DNSName, QType> RRSetKey_t;
   typedef std::vector<std::shared_ptr<DNSRecordContent>> RRVector_t;
@@ -132,10 +139,12 @@ private:
   std::shared_ptr<SOARecordContent> d_soaRecordContent;
   std::set<shared_ptr<DNSKEYRecordContent>> d_dnskeys;
   std::vector<shared_ptr<RRSIGRecordContent>> d_rrsigs;
+  std::vector<shared_ptr<NSEC3PARAMRecordContent>> d_nsec3params;
   ContentSigPair d_nsecs;
-  ContentSigPair d_nsecs3;
+  map<DNSName, ContentSigPair> d_nsec3s;
   DNSName d_nsec3label;
   const DNSName d_zone;
+  const ContentSigPair empty;
 };
 
 }