]> granicus.if.org Git - pdns/commitdiff
Implement Negative Trust Anchors
authorPieter Lexis <pieter.lexis@powerdns.com>
Thu, 26 May 2016 21:36:22 +0000 (23:36 +0200)
committerPieter Lexis <pieter.lexis@powerdns.com>
Tue, 7 Jun 2016 09:17:34 +0000 (11:17 +0200)
pdns/rec-lua-conf.cc
pdns/rec-lua-conf.hh
pdns/validate-recursor.cc
pdns/validate.cc
pdns/validate.hh

index 4dd1010bb3f2affe0a17b90b1f968f7da6a2d6fa..cf1a5aa982c3668b4bd0a1c6affeeb8d8e631c25 100644 (file)
@@ -225,6 +225,20 @@ void loadRecursorLuaConfig(const std::string& fname)
         lci.dsAnchors.clear();
     });
 
+  Lua.writeFunction("addNTA", [&lci](const std::string& who, const boost::optional<std::string> why) {
+      if(why)
+        lci.negAnchors[DNSName(who)] = static_cast<string>(*why);
+      else
+        lci.negAnchors[DNSName(who)] = "";
+    });
+
+  Lua.writeFunction("clearNTA", [&lci](boost::optional<string> who) {
+      if(who)
+        lci.negAnchors.erase(DNSName(*who));
+      else
+        lci.negAnchors.clear();
+    });
+
 #if HAVE_PROTOBUF
   Lua.writeFunction("protobufServer", [&lci](const string& server_, const boost::optional<uint16_t> timeout, const boost::optional<uint64_t> maxQueuedEntries, const boost::optional<uint8_t> reconnectWaitTime) {
       try {
index a9349b1ab784cfe6e4684c10550e4a813611d1bb..57f1f9ed492d210127b55a8997ea2ae13768b9f7 100644 (file)
@@ -11,6 +11,7 @@ public:
   SortList sortlist;
   DNSFilterEngine dfe;
   map<DNSName,DSRecordContent> dsAnchors;
+  map<DNSName,std::string> negAnchors;
   std::shared_ptr<RemoteLogger> protobufServer{nullptr};
 };
 
index 5e6579ff766ef083334eedd77f934bd3dbe544d9..227f9ddf8163629858bf0ff874d303bfefff0236 100644 (file)
@@ -49,6 +49,8 @@ vState validateRecords(const vector<DNSRecord>& recs)
     for(const auto& csp : cspmap) {
       for(const auto& sig : csp.second.signatures) {
         state = getKeysFor(sro, sig->d_signer, keys); // XXX check validity here
+        if(state == NTA)
+          return Insecure;
         LOG("! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys"<<endl);
         // this sort of charges on and 'state' ends up as the last thing to have been checked
         // maybe not the right idea
index 8d85b4e37068875678d99a234318c84ea126ac32..222c8947dcf1e7ef0be409e07c0facb54b66f0e4 100644 (file)
@@ -13,7 +13,7 @@ string dotName(string type, DNSName name, string tag);
 string dotEscape(string name);
 
 const char *dStates[]={"nodata", "nxdomain", "empty non-terminal", "insecure (no-DS proof)"};
-const char *vStates[]={"Indeterminate", "Bogus", "Insecure", "Secure"};
+const char *vStates[]={"Indeterminate", "Bogus", "Insecure", "Secure", "NTA"};
 
 typedef set<DNSKEYRecordContent> keyset_t;
 vector<DNSKEYRecordContent> getByTag(const keyset_t& keys, uint16_t tag)
@@ -165,6 +165,40 @@ cspmap_t harvestCSPFromRecs(const vector<DNSRecord>& recs)
 
 vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
 {
+  auto luaLocal = g_luaconfs.getLocal();
+  auto anchors = luaLocal->dsAnchors;
+
+  // Before searching for the keys, see if we have a Negative Trust Anchor. If
+  // so, test if the NTA is valid and return an NTA state
+  auto negAnchors = luaLocal->negAnchors;
+
+  if (!negAnchors.empty()) {
+    DNSName lowestNTA, lowestTA;
+
+    for (auto const &negAnchor : negAnchors)
+      if (zone.isPartOf(negAnchor.first) && lowestNTA.countLabels() < negAnchor.first.countLabels())
+        lowestNTA = negAnchor.first;
+
+    for (auto const &anchor : anchors)
+      if (zone.isPartOf(anchor.first) && lowestTA.countLabels() < anchor.first.countLabels())
+        lowestTA = anchor.first;
+
+    if(!lowestNTA.empty()) {
+      LOG("Found a Negative Trust Anchor for "<<lowestNTA.toStringRootDot()<<", which was added with reason '"<<negAnchors[lowestNTA]<<"', ");
+
+      /* RFC 7646 section 2.1 tells us that we SHOULD still validate if there
+       * is a Trust Anchor below the Negative Trust Anchor for the name we
+       * attempt validation for. However, section 3 tells us this positive
+       * Trust Anchor MUST be *below* the name and not the name itself
+       */
+      if(lowestTA.countLabels() < lowestNTA.countLabels()) {
+        LOG("marking answer Insecure"<<endl);
+        return NTA; // Not Insecure, this way validateRecords() can shortcut
+      }
+      LOG("but a Trust Anchor for "<<lowestTA.toStringRootDot()<<" is configured, continuing validation."<<endl);
+    }
+  }
+
   vector<string> labels = zone.getRawLabels();
   vState state;
 
@@ -176,7 +210,6 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
 
   DNSName qname(".");
   state = Secure; // the root is secure
-  auto luaLocal = g_luaconfs.getLocal();
   while(zone.isPartOf(qname))
   {
     if(auto ds = rplookup(luaLocal->dsAnchors, qname))
index f72a98cfc7fe477ae401c92d1f166c99b6db48f9..a0a7f3578247611d394109000eb40f04e596fa10 100644 (file)
@@ -9,7 +9,7 @@
 extern bool g_dnssecLOG;
 
 // 4033 5
-enum vState { Indeterminate, Bogus, Insecure, Secure };
+enum vState { Indeterminate, Bogus, Insecure, Secure, NTA };
 extern const char *vStates[];
 
 // NSEC(3) results