]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: NS-consistency check is only when we expect an insecure delegation 5716/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 21 Sep 2017 14:49:18 +0000 (16:49 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 21 Sep 2017 14:49:18 +0000 (16:49 +0200)
pdns/dnssecinfra.cc
pdns/dnssecinfra.hh
pdns/packethandler.cc
pdns/recursordist/test-syncres_cc.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/validate.cc
pdns/validate.hh

index 95cbc3bdaea1626514809025c127b2b6ab3303ad..8964e1e72a87392583d3c918f5ccfa0e51c3ee77 100644 (file)
@@ -498,6 +498,36 @@ string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const
   return toHash;
 }
 
+void incrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
+{
+  if(raw.empty())
+    return;
+
+  for(string::size_type pos=raw.size(); pos; ) {
+    --pos;
+    unsigned char c = (unsigned char)raw[pos];
+    ++c;
+    raw[pos] = (char) c;
+    if(c)
+      break;
+  }
+}
+
+void decrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
+{
+  if(raw.empty())
+    return;
+
+  for(string::size_type pos=raw.size(); pos; ) {
+    --pos;
+    unsigned char c = (unsigned char)raw[pos];
+    --c;
+    raw[pos] = (char) c;
+    if(c != 0xff)
+      break;
+  }
+}
+
 DNSKEYRecordContent DNSSECPrivateKey::getDNSKEY() const
 {
   return makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm, d_flags);
index d094fdb552215e8a489d4b97499b374fcf2edd9a..f5b1c0e2479762ee86a880be6f12e6c7a38e035e 100644 (file)
@@ -155,6 +155,9 @@ uint32_t getStartOfWeek();
 string hashQNameWithSalt(const NSEC3PARAMRecordContent& ns3prc, const DNSName& qname);
 string hashQNameWithSalt(const std::string& salt, unsigned int iterations, const DNSName& qname);
 
+void incrementHash(std::string& raw);
+void decrementHash(std::string& raw);
+
 void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const std::set<DNSName>& authMap, vector<DNSZoneRecord>& rrs);
 
 void addTSIG(DNSPacketWriter& pw, TSIGRecordContent& trc, const DNSName& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly);
index 853128404f9d1eb387b638f0e946c46fd05042c6..dd33e75a9c8a0ce934e8163debd04c075414fe5a 100644 (file)
@@ -544,37 +544,6 @@ void PacketHandler::addNSECX(DNSPacket *p, DNSPacket *r, const DNSName& target,
   }
 }
 
-static void incrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
-{
-  if(raw.empty())
-    return;
-    
-  for(string::size_type pos=raw.size(); pos; ) {
-    --pos;
-    unsigned char c = (unsigned char)raw[pos];
-    ++c;
-    raw[pos] = (char) c;
-    if(c)
-      break;
-  }
-}
-
-static void decrementHash(std::string& raw) // I wonder if this is correct, cmouse? ;-)
-{
-  if(raw.empty())
-    return;
-    
-  for(string::size_type pos=raw.size(); pos; ) {
-    --pos;
-    unsigned char c = (unsigned char)raw[pos];
-    --c;
-    raw[pos] = (char) c;
-    if(c != 0xff)
-      break;
-  }
-}
-
-
 bool getNSEC3Hashes(bool narrow, DNSBackend* db, int id, const std::string& hashed, bool decrement, DNSName& unhashed, std::string& before, std::string& after, int mode)
 {
   bool ret;
index 554986240a5d64be3bfb0136187bc9d08f2ee8ea..2301a097e991c8d1c008c8940355df0a45987177 100644 (file)
@@ -3,6 +3,7 @@
 #include <boost/test/unit_test.hpp>
 
 #include "arguments.hh"
+#include "base32.hh"
 #include "dnssecinfra.hh"
 #include "dnsseckeeper.hh"
 #include "lua-recursor4.hh"
@@ -339,6 +340,48 @@ static void addNSECRecordToLW(const DNSName& domain, const DNSName& next, const
   records.push_back(rec);
 }
 
+static void addNSEC3RecordToLW(const DNSName& hashedName, const std::string& hashedNext, const std::string& salt, unsigned int iterations, const std::set<uint16_t>& types,  uint32_t ttl, std::vector<DNSRecord>& records)
+{
+  NSEC3RecordContent nrc;
+  nrc.d_algorithm = 1;
+  nrc.d_flags = 0;
+  nrc.d_iterations = iterations;
+  nrc.d_salt = salt;
+  nrc.d_nexthash = hashedNext;
+  nrc.d_set = types;
+
+  DNSRecord rec;
+  rec.d_name = hashedName;
+  rec.d_ttl = ttl;
+  rec.d_type = QType::NSEC3;
+  rec.d_content = std::make_shared<NSEC3RecordContent>(nrc);
+  rec.d_place = DNSResourceRecord::AUTHORITY;
+
+  records.push_back(rec);
+}
+
+static void addNSEC3UnhashedRecordToLW(const DNSName& domain, const std::string& next, const std::set<uint16_t>& types,  uint32_t ttl, std::vector<DNSRecord>& records)
+{
+  static const std::string salt = "deadbeef";
+  static const unsigned int iterations = 10;
+  std::string hashed = hashQNameWithSalt(salt, iterations, domain);
+
+  addNSEC3RecordToLW(DNSName(toBase32Hex(hashed)), next, salt, iterations, types, ttl, records);
+}
+
+void addNSEC3NarrowRecordToLW(const DNSName& domain, const std::set<uint16_t>& types,  uint32_t ttl, std::vector<DNSRecord>& records)
+{
+  static const std::string salt = "deadbeef";
+  static const unsigned int iterations = 10;
+  std::string hashed = hashQNameWithSalt(salt, iterations, domain);
+
+  std::string hashedNext(hashed);
+  incrementHash(hashedNext);
+  decrementHash(hashed);
+
+  addNSEC3RecordToLW(DNSName(toBase32Hex(hashed)), hashedNext, salt, iterations, types, ttl, records);
+}
+
 static void generateKeyMaterial(const DNSName& name, unsigned int algo, uint8_t digest, testkeysset_t& keys)
 {
   auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(algo));
@@ -6801,10 +6844,10 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_nowrap) {
   cspmap_t denialMap;
   denialMap[std::make_pair(DNSName("a.example.org."), QType::NSEC)] = pair;
 
-  dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A);
+  dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false);
   BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
 
-  denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A);
+  denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false);
   /* let's check that d.example.org. is not denied by this proof */
   BOOST_CHECK_EQUAL(denialState, NODATA);
 }
@@ -6836,10 +6879,10 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_1) {
   cspmap_t denialMap;
   denialMap[std::make_pair(DNSName("z.example.org."), QType::NSEC)] = pair;
 
-  dState denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A);
+  dState denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false);
   BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
 
-  denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A);
+  denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false);
   /* let's check that d.example.org. is not denied by this proof */
   BOOST_CHECK_EQUAL(denialState, NODATA);
 }
@@ -6871,10 +6914,10 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_2) {
   cspmap_t denialMap;
   denialMap[std::make_pair(DNSName("y.example.org."), QType::NSEC)] = pair;
 
-  dState denialState = getDenial(denialMap, DNSName("z.example.org."), QType::A);
+  dState denialState = getDenial(denialMap, DNSName("z.example.org."), QType::A, false);
   BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
 
-  denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A);
+  denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false);
   /* let's check that d.example.org. is not denied by this proof */
   BOOST_CHECK_EQUAL(denialState, NODATA);
 }
@@ -6906,10 +6949,10 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_only_one_nsec) {
   cspmap_t denialMap;
   denialMap[std::make_pair(DNSName("a.example.org."), QType::NSEC)] = pair;
 
-  dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A);
+  dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false);
   BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
 
-  denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A);
+  denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false);
   /* let's check that d.example.org. is not denied by this proof */
   BOOST_CHECK_EQUAL(denialState, NODATA);
 }
@@ -6941,7 +6984,7 @@ BOOST_AUTO_TEST_CASE(test_nsec_root_nxd_denial) {
   cspmap_t denialMap;
   denialMap[std::make_pair(DNSName("a."), QType::NSEC)] = pair;
 
-  dState denialState = getDenial(denialMap, DNSName("b."), QType::A);
+  dState denialState = getDenial(denialMap, DNSName("b."), QType::A, false);
   BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
 }
 
@@ -6981,15 +7024,142 @@ BOOST_AUTO_TEST_CASE(test_nsec_ancestor_nxqtype_denial) {
      owner name regardless of type.
   */
 
-  dState denialState = getDenial(denialMap, DNSName("a."), QType::A);
+  dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false);
   /* no data means the qname/qtype is not denied, because an ancestor
      delegation NSEC can only deny the DS */
   BOOST_CHECK_EQUAL(denialState, NODATA);
 
-  denialState = getDenial(denialMap, DNSName("a."), QType::DS);
+  denialState = getDenial(denialMap, DNSName("a."), QType::DS, true);
   BOOST_CHECK_EQUAL(denialState, NXQTYPE);
 }
 
+BOOST_AUTO_TEST_CASE(test_nsec_insecure_delegation_denial) {
+  init();
+
+  testkeysset_t keys;
+  generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+
+  vector<DNSRecord> records;
+
+  vector<shared_ptr<DNSRecordContent>> recordContents;
+  vector<shared_ptr<RRSIGRecordContent>> signatureContents;
+
+  /*
+   * RFC 5155 section 8.9:
+   * If there is an NSEC3 RR present in the response that matches the
+   * delegation name, then the validator MUST ensure that the NS bit is
+   * set and that the DS bit is not set in the Type Bit Maps field of the
+   * NSEC3 RR.
+   */
+  /*
+    The RRSIG from "." denies the existence of any type at a.
+    NS should be set if it was proving an insecure delegation, let's check that
+    we correctly detect that it's not.
+  */
+  addNSECRecordToLW(DNSName("a."), DNSName("b."), { }, 600, records);
+  recordContents.push_back(records.at(0).d_content);
+  addRRSIG(keys, records, DNSName("."), 300);
+  signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+  records.clear();
+
+  ContentSigPair pair;
+  pair.records = recordContents;
+  pair.signatures = signatureContents;
+  cspmap_t denialMap;
+  denialMap[std::make_pair(DNSName("a."), QType::NSEC)] = pair;
+
+  /* Insecure because the NS is not set, so while it does
+     denies the DS, it can't prove an insecure delegation */
+  dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true);
+  BOOST_CHECK_EQUAL(denialState, INSECURE);
+}
+
+BOOST_AUTO_TEST_CASE(test_nsec3_ancestor_nxqtype_denial) {
+  init();
+
+  testkeysset_t keys;
+  generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+
+  vector<DNSRecord> records;
+
+  vector<shared_ptr<DNSRecordContent>> recordContents;
+  vector<shared_ptr<RRSIGRecordContent>> signatureContents;
+
+  /*
+    The RRSIG from "." denies the existence of any type except NS at a.
+    However since it's an ancestor delegation NSEC (NS bit set, SOA bit clear,
+    signer field that is shorter than the owner name of the NSEC RR) it can't
+    be used to deny anything except the whole name or a DS.
+  */
+  addNSEC3UnhashedRecordToLW(DNSName("a."), "whatever", { QType::NS }, 600, records);
+  recordContents.push_back(records.at(0).d_content);
+  addRRSIG(keys, records, DNSName("."), 300);
+  signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+  ContentSigPair pair;
+  pair.records = recordContents;
+  pair.signatures = signatureContents;
+  cspmap_t denialMap;
+  denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+  records.clear();
+
+  /* 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.
+  */
+
+  dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false);
+  /* no data means the qname/qtype is not denied, because an ancestor
+     delegation NSEC3 can only deny the DS */
+  BOOST_CHECK_EQUAL(denialState, NODATA);
+
+  denialState = getDenial(denialMap, DNSName("a."), QType::DS, true);
+  BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+}
+
+BOOST_AUTO_TEST_CASE(test_nsec3_insecure_delegation_denial) {
+  init();
+
+  testkeysset_t keys;
+  generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::SHA256, keys);
+
+  vector<DNSRecord> records;
+
+  vector<shared_ptr<DNSRecordContent>> recordContents;
+  vector<shared_ptr<RRSIGRecordContent>> signatureContents;
+
+  /*
+   * RFC 5155 section 8.9:
+   * If there is an NSEC3 RR present in the response that matches the
+   * delegation name, then the validator MUST ensure that the NS bit is
+   * set and that the DS bit is not set in the Type Bit Maps field of the
+   * NSEC3 RR.
+   */
+  /*
+    The RRSIG from "." denies the existence of any type at a.
+    NS should be set if it was proving an insecure delegation, let's check that
+    we correctly detect that it's not.
+  */
+  addNSEC3UnhashedRecordToLW(DNSName("a."), "whatever", { }, 600, records);
+  recordContents.push_back(records.at(0).d_content);
+  addRRSIG(keys, records, DNSName("."), 300);
+  signatureContents.push_back(getRR<RRSIGRecordContent>(records.at(1)));
+
+  ContentSigPair pair;
+  pair.records = recordContents;
+  pair.signatures = signatureContents;
+  cspmap_t denialMap;
+  denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
+  records.clear();
+
+  /* Insecure because the NS is not set, so while it does
+     denies the DS, it can't prove an insecure delegation */
+  dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true);
+  BOOST_CHECK_EQUAL(denialState, INSECURE);
+}
+
 /*
 // cerr<<"asyncresolve called to ask "<<ip.toStringWithPort()<<" about "<<domain.toString()<<" / "<<QType(type).getName()<<" over "<<(doTCP ? "TCP" : "UDP")<<" (rd: "<<sendRDQuery<<", EDNS0 level: "<<EDNS0Level<<")"<<endl;
 
index d854aff46c629da6f019a8a6c648e4483d94116b..e11aa36335b8ef632e52af19b2eb09a4db2cb710 100644 (file)
@@ -1912,13 +1912,13 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
   return RCode::NoError;
 }
 
-void SyncRes::getDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState expectedState, bool allowOptOut)
+void SyncRes::getDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState expectedState, bool allowOptOut, bool referralToUnsigned)
 {
   ne.d_validationState = state;
 
   if (state == Secure) {
     cspmap_t csp = harvestCSPFromNE(ne);
-    dState res = getDenial(csp, ne.d_name, ne.d_qtype.getCode());
+    dState res = getDenial(csp, ne.d_name, ne.d_qtype.getCode(), referralToUnsigned);
     if (res != expectedState) {
       if (res == OPTOUT && allowOptOut) {
         LOG(d_prefix<<"OPT-out denial found for "<<ne.d_name<<endl);
@@ -1965,7 +1965,7 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
       ne.d_qtype = QType(0); // this encodes 'whole record'
       ne.d_auth = rec.d_name;
       harvestNXRecords(lwr.d_records, ne);
-      getDenialValidationState(ne, state, NXDOMAIN, false);
+      getDenialValidationState(ne, state, NXDOMAIN, false, false);
 
       if(!wasVariable()) {
         t_sstorage.negcache.add(ne);
@@ -2028,7 +2028,7 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
         ne.d_qtype = QType::DS;
         harvestNXRecords(lwr.d_records, ne);
         cspmap_t csp = harvestCSPFromNE(ne);
-        dState denialState = getDenial(csp, newauth, QType::DS);
+        dState denialState = getDenial(csp, newauth, QType::DS, true);
         if (denialState == NXQTYPE || denialState == OPTOUT || denialState == INSECURE) {
           ne.d_validationState = Secure;
           rec.d_ttl = min(s_maxnegttl, rec.d_ttl);
@@ -2074,7 +2074,7 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
         ne.d_name = qname;
         ne.d_qtype = qtype;
         harvestNXRecords(lwr.d_records, ne);
-        getDenialValidationState(ne, state, NXQTYPE, qtype == QType::DS);
+        getDenialValidationState(ne, state, NXQTYPE, qtype == QType::DS, false);
 
         if(!wasVariable()) {
           if(qtype.getCode()) {  // prevents us from blacking out a whole domain
index 04209c945aab42365df7ce6ed888aaf6e15ae3c5..263ee229ddd226a1f5a0b127c420f87622a20dff 100644 (file)
@@ -755,7 +755,7 @@ private:
   vState validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord>& dnskeys, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures, unsigned int depth);
   vState getDSRecords(const DNSName& zone, dsmap_t& ds, bool onlyTA, unsigned int depth, bool bogusOnNXD=true, bool* foundCut=nullptr);
   vState getDNSKeys(const DNSName& signer, skeyset_t& keys, unsigned int depth);
-  void getDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState expectedState, bool allowOptOut);
+  void getDenialValidationState(NegCache::NegCacheEntry& ne, vState& state, const dState expectedState, bool allowOptOut, bool referralToUnsigned);
   vState getTA(const DNSName& zone, dsmap_t& ds);
   bool haveExactValidationStatus(const DNSName& domain);
   vState getValidationStatus(const DNSName& subdomain, bool allowIndeterminate=true);
index 148a320eda735eb5042a4c9082ba2322ad2c828c..7b159e367b16bce308fda0af48e362baeaf46530 100644 (file)
@@ -98,7 +98,7 @@ bool denialProvesNoDelegation(const DNSName& zone, const std::vector<DNSRecord>&
 // FIXME: needs a zone argument, to avoid things like 6840 4.1
 // FIXME: Add ENT support
 // FIXME: Make usable for non-DS records and hook up to validateRecords (or another place)
-dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype)
+dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype, bool referralToUnsigned)
 {
   for(const auto& v : validrrsets) {
     LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
@@ -134,6 +134,18 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           }
 
           LOG("Denies existence of type "<<QType(qtype).getName()<<endl);
+
+          /*
+           * RFC 4035 Section 2.3:
+           * The bitmap for the NSEC RR at a delegation point requires special
+           * attention.  Bits corresponding to the delegation NS RRset and any
+           * RRsets for which the parent zone has authoritative data MUST be set
+           */
+          if (referralToUnsigned && qtype == QType::DS && !nsec->d_set.count(QType::NS)) {
+            LOG("However, no NS record exists at this level!"<<endl);
+            return INSECURE;
+          }
+
           return NXQTYPE;
         }
 
@@ -161,14 +173,30 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
         LOG("\tquery hash: "<<toBase32Hex(h)<<endl);
         string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
 
+        /* 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 (nsec3->d_set.count(QType::NS) && !nsec3->d_set.count(QType::SOA) &&
+            getSigner(v.second.signatures).countLabels() < v.first.first.countLabels()) {
+          LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec3->d_set.count(QType::NS))<<", SOA is "<<std::to_string(nsec3->d_set.count(QType::SOA))<<", signer is "<<getSigner(v.second.signatures).toString()<<", owner name is "<<v.first.first.toString()<<endl);
+          /* this is an "ancestor delegation" NSEC3 RR */
+          if (beginHash == h && qtype != QType::DS) {
+            LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
+            continue;
+          }
+        }
+
         // If the name exists, check if the qtype is denied
         if(beginHash == h) {
           if (nsec3->d_set.count(qtype)) {
-            LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<" for name "<<qname<<"  (not opt-out).");
+            LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
             continue;
           }
 
-          LOG("Denies existence of type "<<QType(qtype).getName()<<" for name "<<qname<<"  (not opt-out).");
+          LOG("Denies existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
           /*
            * RFC 5155 section 8.9:
            * If there is an NSEC3 RR present in the response that matches the
@@ -176,7 +204,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
            * set and that the DS bit is not set in the Type Bit Maps field of the
            * NSEC3 RR.
            */
-          if (qtype == QType::DS && !nsec3->d_set.count(QType::NS)) {
+          if (referralToUnsigned && qtype == QType::DS && !nsec3->d_set.count(QType::NS)) {
             LOG("However, no NS record exists at this level!"<<endl);
             return INSECURE;
           }
@@ -671,7 +699,7 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
     auto r = validrrsets.equal_range(make_pair(*(zoneCutIter+1), QType::DS));
     if(r.first == r.second) {
       LOG("No DS for "<<*(zoneCutIter+1)<<", now look for a secure denial"<<endl);
-      dState res = getDenial(validrrsets, *(zoneCutIter+1), QType::DS);
+      dState res = getDenial(validrrsets, *(zoneCutIter+1), QType::DS, true);
       if (res == INSECURE || res == NXDOMAIN)
         return Bogus;
       if (res == NXQTYPE || res == OPTOUT)
index 039177d646e252692e88a4d3705e25e0b5b308ef..e09bcd56175b7e1c3f75edf189d694d794d6d86f 100644 (file)
@@ -71,7 +71,7 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset);
 bool getTrustAnchor(const map<DNSName,dsmap_t>& anchors, const DNSName& zone, dsmap_t &res);
 bool haveNegativeTrustAnchor(const map<DNSName,std::string>& negAnchors, const DNSName& zone, std::string& reason);
 void validateDNSKeysAgainstDS(time_t now, const DNSName& zone, const dsmap_t& dsmap, const skeyset_t& tkeys, vector<shared_ptr<DNSRecordContent> >& toSign, const vector<shared_ptr<RRSIGRecordContent> >& sigs, skeyset_t& validkeys);
-dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype);
+dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16_t qtype, bool referralToUnsigned);
 bool isSupportedDS(const DSRecordContent& ds);
 DNSName getSigner(const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures);
 bool denialProvesNoDelegation(const DNSName& zone, const std::vector<DNSRecord>& dsrecords);