]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec: Only the first filtering policy should match
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 15 Jan 2020 13:28:25 +0000 (14:28 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Mon, 20 Jan 2020 14:42:47 +0000 (15:42 +0100)
Subsequent ones should not be applied.
Also make sure that NSDNAME and NSIP triggers really stop the
processing of the query, instead of just causing the current NS to
be skipped.

(cherry picked from commit 124dd1d4124c52c56a93d6e765f091c88f7bc88a)

pdns/pdns_recursor.cc
pdns/recursordist/test-syncres_cc3.cc
pdns/syncres.cc
pdns/syncres.hh

index 5e7374d8c19d8b0dc724e5a3efef4e8bb873ea40..a4f3196d4d3d41e8ec6a4409300c132d939d8f99 100644 (file)
@@ -1326,7 +1326,7 @@ static void startDoResolve(void *p)
     }
 
     // Check if the query has a policy attached to it
-    if (wantsRPZ) {
+    if (wantsRPZ && appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
       appliedPolicy = luaconfsLocal->dfe.getQueryPolicy(dc->d_mdp.d_qname, dc->d_source, sr.d_discardedPolicies);
     }
 
@@ -1372,15 +1372,19 @@ static void startDoResolve(void *p)
 
       // Query got not handled for QNAME Policy reasons, now actually go out to find an answer
       try {
+        sr.d_appliedPolicy = appliedPolicy;
         res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
         shouldNotValidate = sr.wasOutOfBand();
       }
-      catch(ImmediateServFailException &e) {
-        if(g_logCommonErrors)
+      catch(const ImmediateServFailException &e) {
+        if(g_logCommonErrors) {
           g_log<<Logger::Notice<<"Sending SERVFAIL to "<<dc->getRemote()<<" during resolve of '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
+        }
         res = RCode::ServFail;
       }
-
+      catch(const PolicyHitException& e) {
+        res = -2;
+      }
       dq.validationState = sr.getValidationState();
 
       // During lookup, an NSDNAME or NSIP trigger was hit in RPZ
@@ -1424,7 +1428,7 @@ static void startDoResolve(void *p)
         }
       }
 
-      if (wantsRPZ) {
+      if (wantsRPZ && appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
         appliedPolicy = luaconfsLocal->dfe.getPostPolicy(ret, sr.d_discardedPolicies);
       }
 
@@ -1559,7 +1563,7 @@ static void startDoResolve(void *p)
             }
           }
         }
-        catch(ImmediateServFailException &e) {
+        catch(const ImmediateServFailException &e) {
           if(g_logCommonErrors)
             g_log<<Logger::Notice<<"Sending SERVFAIL to "<<dc->getRemote()<<" during validation of '"<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<"' because: "<<e.reason<<endl;
           pw.getHeader()->rcode=RCode::ServFail;
@@ -2939,18 +2943,21 @@ static void houseKeeping(void *)
        try {
          doSecPoll(&last_secpoll);
        }
-       catch(std::exception& e)
+       catch(const std::exception& e)
         {
           g_log<<Logger::Error<<"Exception while performing security poll: "<<e.what()<<endl;
         }
-        catch(PDNSException& e)
+        catch(const PDNSException& e)
         {
           g_log<<Logger::Error<<"Exception while performing security poll: "<<e.reason<<endl;
         }
-        catch(ImmediateServFailException &e)
+        catch(const ImmediateServFailException &e)
         {
           g_log<<Logger::Error<<"Exception while performing security poll: "<<e.reason<<endl;
         }
+        catch(const PolicyHitException& e) {
+          g_log<<Logger::Error<<"Policy hit while performing security poll"<<endl;
+        }
         catch(...)
         {
           g_log<<Logger::Error<<"Exception while performing security poll"<<endl;
index 2a544ac886a7d4a45f291aae162b5a2cb826e2a7..bc6497c2d0114e3e79ca4e29cced6ba0eb27db8e 100644 (file)
@@ -521,9 +521,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv4_rpz)
   g_luaconfs.setState(luaconfsCopy);
 
   vector<DNSRecord> ret;
-  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
-  BOOST_CHECK_EQUAL(res, -2);
-  BOOST_CHECK_EQUAL(ret.size(), 0U);
+  BOOST_CHECK_THROW(sr->beginResolve(target, QType(QType::A), QClass::IN, ret), PolicyHitException);
 }
 
 BOOST_AUTO_TEST_CASE(test_nameserver_ipv6_rpz)
@@ -564,9 +562,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_ipv6_rpz)
   g_luaconfs.setState(luaconfsCopy);
 
   vector<DNSRecord> ret;
-  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
-  BOOST_CHECK_EQUAL(res, -2);
-  BOOST_CHECK_EQUAL(ret.size(), 0U);
+  BOOST_CHECK_THROW(sr->beginResolve(target, QType(QType::A), QClass::IN, ret), PolicyHitException);
 }
 
 BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz)
@@ -608,9 +604,7 @@ BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz)
   g_luaconfs.setState(luaconfsCopy);
 
   vector<DNSRecord> ret;
-  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
-  BOOST_CHECK_EQUAL(res, -2);
-  BOOST_CHECK_EQUAL(ret.size(), 0U);
+  BOOST_CHECK_THROW(sr->beginResolve(target, QType(QType::A), QClass::IN, ret), PolicyHitException);
 }
 
 BOOST_AUTO_TEST_CASE(test_nameserver_name_rpz_disabled)
index e4a3b962eef8bc39da1e1b29ea725941eb4c5217..486bd2bee5e3c78197e029297e76e290b9ca6e23 100644 (file)
@@ -733,6 +733,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
         QLOG("Delegation seen, continue at step 1");
         break;
       }
+
       if (res != RCode::NoError) {
         // Case 5: unexpected answer
         QLOG("Step5: other rcode, last effort final resolve");
@@ -923,46 +924,52 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
   d_requireAuthData = false;
   d_DNSSECValidationRequested = false;
 
-  vState newState = Indeterminate;
-  res_t resv4;
-  // If IPv4 ever becomes second class, we should revisit this
-  if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
-    for (auto const &i : resv4) {
-      if (i.d_type == QType::A) {
-        if (auto rec = getRR<ARecordContent>(i)) {
-          ret.push_back(rec->getCA(53));
+  try {
+    vState newState = Indeterminate;
+    res_t resv4;
+    // If IPv4 ever becomes second class, we should revisit this
+    if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
+      for (auto const &i : resv4) {
+        if (i.d_type == QType::A) {
+          if (auto rec = getRR<ARecordContent>(i)) {
+            ret.push_back(rec->getCA(53));
+          }
         }
       }
     }
-  }
-  if (s_doIPv6) {
-    if (ret.empty()) {
-      // We did not find IPv4 addresses, try to get IPv6 ones
-      newState = Indeterminate;
-      res_t resv6;
-      if (doResolve(qname, QType::AAAA, resv6, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
-        for (const auto &i : resv6) {
-          if (i.d_type == QType::AAAA) {
-            if (auto rec = getRR<AAAARecordContent>(i))
-              ret.push_back(rec->getCA(53));
+    if (s_doIPv6) {
+      if (ret.empty()) {
+        // We did not find IPv4 addresses, try to get IPv6 ones
+        newState = Indeterminate;
+        res_t resv6;
+        if (doResolve(qname, QType::AAAA, resv6, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
+          for (const auto &i : resv6) {
+            if (i.d_type == QType::AAAA) {
+              if (auto rec = getRR<AAAARecordContent>(i))
+                ret.push_back(rec->getCA(53));
+            }
           }
         }
-      }
-    } else {
-      // We have some IPv4 records, don't bother with going out to get IPv6, but do consult the cache
-      // Once IPv6 adoption matters, this needs to be revisited
-      res_t cset;
-      if (t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote) > 0) {
-        for (const auto &i : cset) {
-          if (i.d_ttl > (unsigned int)d_now.tv_sec ) {
-            if (auto rec = getRR<AAAARecordContent>(i)) {
-              ret.push_back(rec->getCA(53));
+      } else {
+        // We have some IPv4 records, don't bother with going out to get IPv6, but do consult the cache
+        // Once IPv6 adoption matters, this needs to be revisited
+        res_t cset;
+        if (t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote) > 0) {
+          for (const auto &i : cset) {
+            if (i.d_ttl > (unsigned int)d_now.tv_sec ) {
+              if (auto rec = getRR<AAAARecordContent>(i)) {
+                ret.push_back(rec->getCA(53));
+              }
             }
           }
         }
       }
     }
   }
+  catch (const PolicyHitException& e) {
+    /* we ignore a policy hit while trying to retrieve the addresses
+       of a NS and keep processing the current query */
+  }
 
   d_requireAuthData = oldRequireAuthData;
   d_DNSSECValidationRequested = oldValidationRequested;
@@ -1790,7 +1797,13 @@ static void addNXNSECS(vector<DNSRecord>&ret, const vector<DNSRecord>& records)
 
 bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers)
 {
-  if(d_wantsRPZ) {
+  /* we skip RPZ processing if:
+     - it was disabled (d_wantsRPZ is false) ;
+     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
+     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
+     process any further RPZ rules.
+  */
+  if (d_wantsRPZ && d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
     for (auto const &ns : nameservers) {
       d_appliedPolicy = dfe.getProcessingPolicy(ns.first, d_discardedPolicies);
       if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
@@ -1813,7 +1826,13 @@ bool SyncRes::nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& n
 
 bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress& remoteIP)
 {
-  if (d_wantsRPZ) {
+  /* we skip RPZ processing if:
+     - it was disabled (d_wantsRPZ is false) ;
+     - we already got a RPZ hit (d_appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) since
+     the only way we can get back here is that it was a 'pass-thru' (NoAction) meaning that we should not
+     process any further RPZ rules.
+  */
+  if (d_wantsRPZ && d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
     d_appliedPolicy = dfe.getProcessingPolicy(remoteIP, d_discardedPolicies);
     if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
       LOG(" (blocked by RPZ policy '"+(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")+"')");
@@ -3385,12 +3404,11 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
 
     nameservers.clear();
     for (auto const &nameserver : nsset) {
-      if (d_wantsRPZ) {
+      if (d_wantsRPZ && d_appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
         d_appliedPolicy = dfe.getProcessingPolicy(nameserver, d_discardedPolicies);
         if (d_appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) { // client query needs an RPZ response
           LOG("however "<<nameserver<<" was blocked by RPZ policy '"<<(d_appliedPolicy.d_name ? *d_appliedPolicy.d_name : "")<<"'"<<endl);
-          *rcode = -2;
-          return true;
+          throw PolicyHitException();
         }
       }
       nameservers.insert({nameserver, {{}, false}});
@@ -3424,7 +3442,8 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
   LOG(prefix<<qname<<": Cache consultations done, have "<<(unsigned int)nameservers.size()<<" NS to contact");
 
   if (nameserversBlockedByRPZ(luaconfsLocal->dfe, nameservers)) {
-    return -2;
+    /* RPZ hit */
+    throw PolicyHitException();
   }
 
   LOG(endl);
@@ -3508,8 +3527,10 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
             }
           }
           LOG(endl);
-          if (hitPolicy) //implies d_wantsRPZ
-            return -2;
+          if (hitPolicy) { //implies d_wantsRPZ
+            /* RPZ hit */
+            throw PolicyHitException();
+          }
         }
 
         for(remoteIP = remoteIPs.cbegin(); remoteIP != remoteIPs.cend(); ++remoteIP) {
@@ -3663,6 +3684,10 @@ int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<D
     g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got ImmediateServFailException: "<<e.reason<<endl;
     ret.clear();
   }
+  catch(const PolicyHitException& e) {
+    g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got a policy hit"<<endl;
+    ret.clear();
+  }
   catch(const std::exception& e) {
     g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got STL error: "<<e.what()<<endl;
     ret.clear();
@@ -3700,6 +3725,10 @@ int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback) {
   catch(const ImmediateServFailException& e) {
     g_log<<Logger::Error<<"Failed to update . records, got an exception: "<<e.reason<<endl;
   }
+  catch(const PolicyHitException& e) {
+    g_log<<Logger::Error<<"Failed to update . records, got a policy hit"<<endl;
+    ret.clear();
+  }
   catch(const std::exception& e) {
     g_log<<Logger::Error<<"Failed to update . records, got an exception: "<<e.what()<<endl;
   }
index 5b984a70cfae5e2394b6859a63821ea1a22651c7..411ad3dd9e6af3e1a5b3bfac00d1f229805d19ab 100644 (file)
@@ -1055,6 +1055,10 @@ public:
   string reason; //! Print this to tell the user what went wrong
 };
 
+class PolicyHitException
+{
+};
+
 typedef boost::circular_buffer<ComboAddress> addrringbuf_t;
 extern thread_local std::unique_ptr<addrringbuf_t> t_servfailremotes, t_largeanswerremotes, t_remotes, t_bogusremotes, t_timeouts;