]> granicus.if.org Git - pdns/commitdiff
Add new lua-auth code with updatePolicy hook
authorAki Tuomi <cmouse@cmouse.fi>
Sun, 26 Jun 2016 16:57:43 +0000 (19:57 +0300)
committerAki Tuomi <cmouse@cmouse.fi>
Sat, 3 Sep 2016 21:26:36 +0000 (00:26 +0300)
pdns/Makefile.am
pdns/common_startup.cc
pdns/lua-auth4.cc [new file with mode: 0644]
pdns/lua-auth4.hh [new file with mode: 0644]
pdns/packethandler.cc
pdns/packethandler.hh
pdns/rfc2136handler.cc

index 450e12c0354da6a1b1af902c38f12b4b4ecb099e..89fae83fc780d242dd37f0e19cd705b825e03862 100644 (file)
@@ -170,6 +170,7 @@ pdns_server_SOURCES = \
        lock.hh \
        logger.cc logger.hh \
        lua-auth.cc lua-auth.hh \
+       lua-auth4.cc lua-auth4.hh \
        lua-pdns.cc lua-pdns.hh lua-iputils.cc \
        mastercommunicator.cc \
        md5.hh \
index 5d3d4984dbcce909e37860e4e7a285f0eaad3b41..a9eabf271feb12b9e00b829c94f9d3b40634f378 100644 (file)
@@ -172,6 +172,7 @@ void declareArguments()
   ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
 
   ::arg().set("lua-prequery-script", "Lua script with prequery handler (DO NOT USE)")="";
+  ::arg().set("lua-dnsupdate-policy-script", "Lua script with DNS update policy handler")="";
   ::arg().set("experimental-lua-policy-script", "Lua script for the policy engine")="";
 
   ::arg().setSwitch("traceback-handler","Enable the traceback handler (Linux only)")="yes";
diff --git a/pdns/lua-auth4.cc b/pdns/lua-auth4.cc
new file mode 100644 (file)
index 0000000..4a86ef4
--- /dev/null
@@ -0,0 +1,240 @@
+#include "lua-auth4.hh"
+#include "stubresolver.hh"
+#include <fstream>
+#include "logger.hh"
+#include "dnsparser.hh"
+#include "syncres.hh"
+#include "namespaces.hh"
+#include "rec_channel.hh"
+#include "ednssubnet.hh"
+#include <unordered_set>
+
+#if !defined(HAVE_LUA)
+
+AuthLua4::AuthLua4(const std::string& fname) { }
+bool AuthLua4::updatePolicy(const DNSName &qname, QType qtype, const DNSName &zonename, DNSPacket *packet) { return false; }
+
+#else
+
+#undef L
+#include "ext/luawrapper/include/LuaContext.hpp"
+
+AuthLua4::AuthLua4(const std::string& fname) {
+  d_lw = std::unique_ptr<LuaContext>(new LuaContext);
+  stubParseResolveConf();
+  d_lw->registerFunction<int(dnsheader::*)()>("getID", [](dnsheader& dh) { return dh.id; });
+  d_lw->registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) { return dh.cd; });
+  d_lw->registerFunction<bool(dnsheader::*)()>("getTC", [](dnsheader& dh) { return dh.tc; });
+  d_lw->registerFunction<bool(dnsheader::*)()>("getRA", [](dnsheader& dh) { return dh.ra; });
+  d_lw->registerFunction<bool(dnsheader::*)()>("getAD", [](dnsheader& dh) { return dh.ad; });
+  d_lw->registerFunction<bool(dnsheader::*)()>("getAA", [](dnsheader& dh) { return dh.aa; });
+  d_lw->registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) { return dh.rd; });
+  d_lw->registerFunction<int(dnsheader::*)()>("getRCODE", [](dnsheader& dh) { return dh.rcode; });
+  d_lw->registerFunction<int(dnsheader::*)()>("getOPCODE", [](dnsheader& dh) { return dh.opcode; });
+  d_lw->registerFunction<int(dnsheader::*)()>("getQDCOUNT", [](dnsheader& dh) { return ntohs(dh.qdcount); });
+  d_lw->registerFunction<int(dnsheader::*)()>("getANCOUNT", [](dnsheader& dh) { return ntohs(dh.ancount); });
+  d_lw->registerFunction<int(dnsheader::*)()>("getNSCOUNT", [](dnsheader& dh) { return ntohs(dh.nscount); });
+  d_lw->registerFunction<int(dnsheader::*)()>("getARCOUNT", [](dnsheader& dh) { return ntohs(dh.arcount); });
+
+  d_lw->writeFunction("newDN", [](const std::string& dom){ return DNSName(dom); });
+  d_lw->registerFunction("isPartOf", &DNSName::isPartOf);
+  d_lw->registerFunction<bool(DNSName::*)(const std::string&)>("equal",
+                                                              [](const DNSName& lhs, const std::string& rhs) { return lhs==DNSName(rhs); });
+  d_lw->registerFunction("__eq", &DNSName::operator==);
+
+  d_lw->registerFunction("__eq", &DNSResourceRecord::operator==);
+  d_lw->registerFunction("__lt", &DNSResourceRecord::operator<);
+
+  d_lw->registerFunction<string(DNSResourceRecord::*)()>("toString", [](const DNSResourceRecord& rec) { return rec.getZoneRepresentation();} );
+
+  d_lw->registerFunction<DNSName(DNSResourceRecord::*)()>("qname", [](DNSResourceRecord& rec) { return rec.qname; });
+  d_lw->registerFunction<DNSName(DNSResourceRecord::*)()>("wildcardname", [](DNSResourceRecord& rec) { return rec.wildcardname; });
+  d_lw->registerFunction<string(DNSResourceRecord::*)()>("content", [](DNSResourceRecord& rec) { return rec.content; });
+  d_lw->registerFunction<time_t(DNSResourceRecord::*)()>("last_modified", [](DNSResourceRecord& rec) { return rec.last_modified; });
+  d_lw->registerFunction<uint32_t(DNSResourceRecord::*)()>("ttl", [](DNSResourceRecord& rec) { return rec.ttl; });
+  d_lw->registerFunction<uint32_t(DNSResourceRecord::*)()>("signttl", [](DNSResourceRecord& rec) { return rec.signttl; });
+  d_lw->registerFunction<int(DNSResourceRecord::*)()>("domain_id", [](DNSResourceRecord& rec) { return rec.domain_id; });
+  d_lw->registerFunction<uint16_t(DNSResourceRecord::*)()>("qtype", [](DNSResourceRecord& rec) { return rec.qtype.getCode(); });
+  d_lw->registerFunction<uint16_t(DNSResourceRecord::*)()>("qclass", [](DNSResourceRecord& rec) { return rec.qclass; });
+  d_lw->registerFunction<int(DNSResourceRecord::*)()>("d_place", [](DNSResourceRecord& rec) { return rec.d_place; });
+  d_lw->registerFunction<uint8_t(DNSResourceRecord::*)()>("scopeMask", [](DNSResourceRecord& rec) { return rec.scopeMask; });
+  d_lw->registerFunction<bool(DNSResourceRecord::*)()>("auth", [](DNSResourceRecord& rec) { return rec.auth; });
+  d_lw->registerFunction<bool(DNSResourceRecord::*)()>("disabled", [](DNSResourceRecord& rec) { return rec.disabled; });
+
+  d_lw->registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
+  d_lw->registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
+  d_lw->registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
+  d_lw->registerFunction<string(ComboAddress::*)()>("getRaw", [](const ComboAddress& ca) {
+      if(ca.sin4.sin_family == AF_INET) {
+        auto t=ca.sin4.sin_addr.s_addr; return string((const char*)&t, 4);
+      }
+      else
+        return string((const char*)&ca.sin6.sin6_addr.s6_addr, 16);
+    } );
+
+  d_lw->writeFunction("newCA", [](const std::string& a) { return ComboAddress(a); });
+  typedef std::unordered_set<ComboAddress,ComboAddress::addressOnlyHash,ComboAddress::addressOnlyEqual> cas_t;
+  d_lw->writeFunction("newCAS", []{ return cas_t(); });
+
+
+  d_lw->registerFunction<void(cas_t::*)(boost::variant<string,ComboAddress, vector<pair<unsigned int,string> > >)>("add",
+                                                                                   [](cas_t& cas, const boost::variant<string,ComboAddress,vector<pair<unsigned int,string> > >& in)
+                                                                                   {
+                                                                                     try {
+                                                                                     if(auto s = boost::get<string>(&in)) {
+                                                                                       cas.insert(ComboAddress(*s));
+                                                                                     }
+                                                                                     else if(auto v = boost::get<vector<pair<unsigned int, string> > >(&in)) {
+                                                                                       for(const auto& s : *v)
+                                                                                         cas.insert(ComboAddress(s.second));
+                                                                                     }
+                                                                                     else
+                                                                                       cas.insert(boost::get<ComboAddress>(in));
+                                                                                     }
+                                                                                     catch(std::exception& e) { theL() <<Logger::Error<<e.what()<<endl; }
+                                                                                   });
+
+  d_lw->registerFunction<bool(cas_t::*)(const ComboAddress&)>("check",[](const cas_t& cas, const ComboAddress&ca) {
+      return (bool)cas.count(ca);
+    });
+
+
+
+  d_lw->registerFunction<bool(ComboAddress::*)(const ComboAddress&)>("equal", [](const ComboAddress& lhs, const ComboAddress& rhs) {
+      return ComboAddress::addressOnlyEqual()(lhs, rhs);
+    });
+
+
+  d_lw->registerFunction<ComboAddress(Netmask::*)()>("getNetwork", [](const Netmask& nm) { return nm.getNetwork(); } ); // const reference makes this necessary
+  d_lw->registerFunction("toString", &Netmask::toString);
+  d_lw->registerFunction("empty", &Netmask::empty);
+
+  d_lw->writeFunction("newNMG", []() { return NetmaskGroup(); });
+  d_lw->registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
+                         {
+                           nmg.addMask(mask);
+                         });
+
+  d_lw->registerFunction<void(NetmaskGroup::*)(const vector<pair<unsigned int, std::string>>&)>("addMasks", [](NetmaskGroup&nmg, const vector<pair<unsigned int, std::string>>& masks)
+                         {
+                           for(const auto& mask: masks)
+                             nmg.addMask(mask.second);
+                         });
+
+
+  d_lw->registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
+  d_lw->registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
+  d_lw->registerFunction<string(DNSName::*)()>("toStringNoDot", [](const DNSName&dn ) { return dn.toStringNoDot(); });
+  d_lw->registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
+  d_lw->registerMember("name", &DNSRecord::d_name);
+  d_lw->registerMember("type", &DNSRecord::d_type);
+  d_lw->registerMember("ttl", &DNSRecord::d_ttl);
+
+
+  d_lw->registerFunction<string(DNSRecord::*)()>("getContent", [](const DNSRecord& dr) { return dr.d_content->getZoneRepresentation(); });
+  d_lw->registerFunction<boost::optional<ComboAddress>(DNSRecord::*)()>("getCA", [](const DNSRecord& dr) {
+      boost::optional<ComboAddress> ret;
+
+      if(auto rec = std::dynamic_pointer_cast<ARecordContent>(dr.d_content))
+        ret=rec->getCA(53);
+      else if(auto rec = std::dynamic_pointer_cast<AAAARecordContent>(dr.d_content))
+        ret=rec->getCA(53);
+      return ret;
+    });
+
+
+  d_lw->registerFunction<void(DNSRecord::*)(const std::string&)>("changeContent", [](DNSRecord& dr, const std::string& newContent) { dr.d_content = shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr.d_type, 1, newContent)); });
+
+  d_lw->writeFunction("pdnslog", [](const std::string& msg, boost::optional<int> loglevel) {
+      theL() << (Logger::Urgency)loglevel.get_value_or(Logger::Warning) << msg<<endl;
+    });
+  typedef vector<pair<string, int> > in_t;
+  vector<pair<string, boost::variant<int, in_t, struct timeval* > > >  pd{
+    {"PASS", (int)PolicyDecision::PASS}, {"DROP",  (int)PolicyDecision::DROP},
+    {"TRUNCATE", (int)PolicyDecision::TRUNCATE}
+  };
+
+  vector<pair<string, int> > rcodes = {{"NOERROR",  RCode::NoError  },
+                                       {"FORMERR",  RCode::FormErr  },
+                                       {"SERVFAIL", RCode::ServFail },
+                                       {"NXDOMAIN", RCode::NXDomain },
+                                       {"NOTIMP",   RCode::NotImp   },
+                                       {"REFUSED",  RCode::Refused  },
+                                       {"YXDOMAIN", RCode::YXDomain },
+                                       {"YXRRSET",  RCode::YXRRSet  },
+                                       {"NXRRSET",  RCode::NXRRSet  },
+                                       {"NOTAUTH",  RCode::NotAuth  },
+                                       {"NOTZONE",  RCode::NotZone  }};
+  for(const auto& rcode : rcodes)
+    pd.push_back({rcode.first, rcode.second});
+
+  pd.push_back({"place", in_t{
+    {"QUESTION", 0},
+    {"ANSWER", 1},
+    {"AUTHORITY", 2},
+    {"ADDITIONAL", 3}
+  }});
+
+  pd.push_back({"loglevels", in_t{
+        {"Alert", LOG_ALERT},
+        {"Critical", LOG_CRIT},
+        {"Debug", LOG_DEBUG},
+        {"Emergency", LOG_EMERG},
+        {"Info", LOG_INFO},
+        {"Notice", LOG_NOTICE},
+        {"Warning", LOG_WARNING},
+        {"Error", LOG_ERR}
+          }});
+
+  for(const auto& n : QType::names)
+    pd.push_back({n.first, n.second});
+  d_lw->registerMember("tv_sec", &timeval::tv_sec);
+  d_lw->registerMember("tv_usec", &timeval::tv_usec);
+
+  d_lw->writeVariable("pdns", pd);
+
+  d_lw->writeFunction("resolve", [](const std::string& qname, uint16_t qtype) {
+      std::vector<DNSResourceRecord> ret;
+      std::unordered_map<int, DNSResourceRecord> luaResult;
+      stubDoResolve(qname, qtype, ret);
+      int i = 0;
+      for(const auto &row: ret) luaResult[++i] = row;
+      return luaResult;
+  });
+
+/* update policy */
+  d_lw->registerFunction<DNSName(UpdatePolicyQuery::*)()>("getQName", [](UpdatePolicyQuery& upq) { return upq.qname; });
+  d_lw->registerFunction<DNSName(UpdatePolicyQuery::*)()>("getZoneName", [](UpdatePolicyQuery& upq) { return upq.zonename; });
+  d_lw->registerFunction<uint16_t(UpdatePolicyQuery::*)()>("getQType", [](UpdatePolicyQuery& upq) { return upq.qtype; });
+  d_lw->registerFunction<ComboAddress(UpdatePolicyQuery::*)()>("getLocal", [](UpdatePolicyQuery& upq) { return upq.local; });
+  d_lw->registerFunction<ComboAddress(UpdatePolicyQuery::*)()>("getRemote", [](UpdatePolicyQuery& upq) { return upq.remote; });
+  d_lw->registerFunction<Netmask(UpdatePolicyQuery::*)()>("getRealRemote", [](UpdatePolicyQuery& upq) { return upq.realRemote; });
+  d_lw->registerFunction<DNSName(UpdatePolicyQuery::*)()>("getTsigName", [](UpdatePolicyQuery& upq) { return upq.tsigName; });
+  d_lw->registerFunction<std::string(UpdatePolicyQuery::*)()>("getPeerPrincipal", [](UpdatePolicyQuery& upq) { return upq.peerPrincipal; });
+/* end of update policy */
+
+  ifstream ifs(fname);
+  if(!ifs) {
+    theL()<<Logger::Error<<"Unable to read configuration file from '"<<fname<<"': "<<strerror(errno)<<endl;
+    return;
+  }
+  d_lw->executeCode(ifs);
+
+  d_update_policy = d_lw->readVariable<boost::optional<luacall_update_policy_t>>("updatepolicy").get_value_or(0);
+}
+
+bool AuthLua4::updatePolicy(const DNSName &qname, QType qtype, const DNSName &zonename, DNSPacket *packet) {
+  UpdatePolicyQuery upq;
+  upq.qname = qname;
+  upq.qtype = qtype.getCode();
+  upq.zonename = zonename;
+  upq.local = packet->getLocal();
+  upq.remote = packet->getRemote();
+  upq.realRemote = packet->getRealRemote();
+  upq.tsigName = packet->getTSIGKeyname();
+  upq.peerPrincipal = packet->d_peer_principal;
+
+  return d_update_policy(upq);
+}
+
+#endif
diff --git a/pdns/lua-auth4.hh b/pdns/lua-auth4.hh
new file mode 100644 (file)
index 0000000..103460c
--- /dev/null
@@ -0,0 +1,40 @@
+#pragma once
+#include "iputils.hh"
+#include "dnsname.hh"
+#include "namespaces.hh"
+#include "dnsrecords.hh"
+#include "dnspacket.hh"
+#include <unordered_map>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+class LuaContext;
+
+class AuthLua4 : public boost::noncopyable
+{
+private:
+#ifdef HAVE_LUA
+  std::unique_ptr<LuaContext> d_lw; // this is way on top because it must get destroyed _last_
+#endif
+
+public:
+  explicit AuthLua4(const std::string& fname);
+  bool updatePolicy(const DNSName &qname, QType qtype, const DNSName &zonename, DNSPacket *packet);
+
+  ~AuthLua4(); // this is so unique_ptr works with an incomplete type
+private:
+  struct UpdatePolicyQuery {
+    DNSName qname;
+    DNSName zonename;
+    uint16_t qtype;
+    ComboAddress local, remote;
+    Netmask realRemote;
+    DNSName tsigName;
+    std::string peerPrincipal;
+  };
+
+  typedef std::function<bool(const UpdatePolicyQuery&)> luacall_update_policy_t;
+
+  luacall_update_policy_t d_update_policy;
+};
index c1a03b376f865903f0063d345a693fd899a0b6f1..57d02bee9fd2558a4f4dd13ea2f8d81b77203258 100644 (file)
@@ -71,7 +71,15 @@ PacketHandler::PacketHandler():B(s_programname), d_dk(&B)
   {
     d_pdl = new AuthLua(fname);
   }
-
+  fname = ::arg()["lua-dnsupdate-policy-script"];
+  if (fname.empty())
+  {
+    d_update_policy_lua = NULL;
+  }
+  else
+  {
+    d_update_policy_lua = new AuthLua4(fname);
+  }
 }
 
 UeberBackend *PacketHandler::getBackend()
index a80c2963acb3b643dc237321618de6fbc45df7c3..886a5840d8e6dff355414f1c995ebfc335fda228 100644 (file)
@@ -30,6 +30,7 @@
 #include "packetcache.hh"
 #include "dnsseckeeper.hh"
 #include "lua-auth.hh"
+#include "lua-auth4.hh"
 #include "gss_context.hh"
 
 #include "namespaces.hh"
@@ -109,6 +110,7 @@ private:
   bool d_doIPv6AdditionalProcessing;
   bool d_doDNAME;
   AuthLua* d_pdl;
+  AuthLua4* d_update_policy_lua;
 
   UeberBackend B; // every thread an own instance
   DNSSECKeeper d_dk; // B is shared with DNSSECKeeper
index e37357d353faaceaa0124c3680dd0d43c45424de..9c08f8f8b33257f250a9f96637e188ab9f910dec 100644 (file)
@@ -673,61 +673,66 @@ int PacketHandler::processUpdate(DNSPacket *p) {
   string msgPrefix="UPDATE (" + itoa(p->d.id) + ") from " + p->getRemote().toString() + " for " + p->qdomain.toLogString() + ": ";
   L<<Logger::Info<<msgPrefix<<"Processing started."<<endl;
 
-  // Check permissions - IP based
-  vector<string> allowedRanges;
-  B.getDomainMetadata(p->qdomain, "ALLOW-DNSUPDATE-FROM", allowedRanges);
-  if (! ::arg()["allow-dnsupdate-from"].empty())
-    stringtok(allowedRanges, ::arg()["allow-dnsupdate-from"], ", \t" );
-
-  NetmaskGroup ng;
-  for(vector<string>::const_iterator i=allowedRanges.begin(); i != allowedRanges.end(); i++)
-    ng.addMask(*i);
-
-  if ( ! ng.match(&p->d_remote)) {
-    L<<Logger::Error<<msgPrefix<<"Remote not listed in allow-dnsupdate-from or domainmetadata. Sending REFUSED"<<endl;
-    return RCode::Refused;
-  }
+  // if there is policy, we delegate all checks to it
+  if (this->d_update_policy_lua == NULL) {
 
+    // Check permissions - IP based
+    vector<string> allowedRanges;
+    B.getDomainMetadata(p->qdomain, "ALLOW-DNSUPDATE-FROM", allowedRanges);
+    if (! ::arg()["allow-dnsupdate-from"].empty())
+      stringtok(allowedRanges, ::arg()["allow-dnsupdate-from"], ", \t" );
 
-  // Check permissions - TSIG based.
-  vector<string> tsigKeys;
-  B.getDomainMetadata(p->qdomain, "TSIG-ALLOW-DNSUPDATE", tsigKeys);
-  if (tsigKeys.size() > 0) {
-    bool validKey = false;
+    NetmaskGroup ng;
+    for(vector<string>::const_iterator i=allowedRanges.begin(); i != allowedRanges.end(); i++)
+      ng.addMask(*i);
 
-    TSIGRecordContent trc;
-    DNSName inputkey;
-    string message;
-    if (! p->getTSIGDetails(&trc,  &inputkey, 0)) {
-      L<<Logger::Error<<msgPrefix<<"TSIG key required, but packet does not contain key. Sending REFUSED"<<endl;
+    if ( ! ng.match(&p->d_remote)) {
+      L<<Logger::Error<<msgPrefix<<"Remote not listed in allow-dnsupdate-from or domainmetadata. Sending REFUSED"<<endl;
       return RCode::Refused;
     }
 
-    if (p->d_tsig_algo == TSIG_GSS) {
-      GssName inputname(p->d_peer_principal); // match against principal since GSS
-      for(vector<string>::const_iterator key=tsigKeys.begin(); key != tsigKeys.end(); key++) {
-        if (inputname.match(*key)) {
-          validKey = true;
-          break;
-        }
+
+    // Check permissions - TSIG based.
+    vector<string> tsigKeys;
+    B.getDomainMetadata(p->qdomain, "TSIG-ALLOW-DNSUPDATE", tsigKeys);
+    if (tsigKeys.size() > 0) {
+      bool validKey = false;
+
+      TSIGRecordContent trc;
+      DNSName inputkey;
+      string message;
+      if (! p->getTSIGDetails(&trc,  &inputkey, 0)) {
+        L<<Logger::Error<<msgPrefix<<"TSIG key required, but packet does not contain key. Sending REFUSED"<<endl;
+        return RCode::Refused;
       }
-    } else {
-      for(vector<string>::const_iterator key=tsigKeys.begin(); key != tsigKeys.end(); key++) {
-        if (inputkey == DNSName(*key)) { // because checkForCorrectTSIG has already been performed earlier on, if the names of the ky match with the domain given. THis is valid.
-          validKey=true;
-          break;
+
+      if (p->d_tsig_algo == TSIG_GSS) {
+        GssName inputname(p->d_peer_principal); // match against principal since GSS
+        for(vector<string>::const_iterator key=tsigKeys.begin(); key != tsigKeys.end(); key++) {
+          if (inputname.match(*key)) {
+            validKey = true;
+            break;
+          }
+        }
+      } else {
+        for(vector<string>::const_iterator key=tsigKeys.begin(); key != tsigKeys.end(); key++) {
+          if (inputkey == DNSName(*key)) { // because checkForCorrectTSIG has already been performed earlier on, if the names of the ky match with the domain given. THis is valid.
+            validKey=true;
+            break;
+          }
         }
       }
-    }
 
-    if (!validKey) {
-      L<<Logger::Error<<msgPrefix<<"TSIG key ("<<inputkey<<") required, but no matching key found in domainmetadata, tried "<<tsigKeys.size()<<". Sending REFUSED"<<endl;
-      return RCode::Refused;
+      if (!validKey) {
+        L<<Logger::Error<<msgPrefix<<"TSIG key ("<<inputkey<<") required, but no matching key found in domainmetadata, tried "<<tsigKeys.size()<<". Sending REFUSED"<<endl;
+        return RCode::Refused;
+      }
     }
-  }
 
-  if (tsigKeys.size() == 0 && p->d_havetsig)
-    L<<Logger::Warning<<msgPrefix<<"TSIG is provided, but domain is not secured with TSIG. Processing continues"<<endl;
+    if (tsigKeys.size() == 0 && p->d_havetsig)
+      L<<Logger::Warning<<msgPrefix<<"TSIG is provided, but domain is not secured with TSIG. Processing continues"<<endl;
+
+  }
 
   // RFC2136 uses the same DNS Header and Message as defined in RFC1035.
   // This means we can use the MOADNSParser to parse the incoming packet. The result is that we have some different
@@ -871,6 +876,16 @@ int PacketHandler::processUpdate(DNSPacket *p) {
     for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
       const DNSRecord *rr = &i->first;
       if (rr->d_place == DNSResourceRecord::AUTHORITY) {
+        /* see if it's permitted by policy */
+        if (this->d_update_policy_lua != NULL) {
+          if (this->d_update_policy_lua->updatePolicy(rr->d_name, QType(rr->d_type), di.zone, p) == false) {
+            L<<Logger::Warning<<msgPrefix<<"Refusing update for " << rr->d_name << "/" << QType(rr->d_type).getName() << ": Not permitted by policy"<<endl;
+            continue;
+          } else {
+            L<<Logger::Debug<<msgPrefix<<"Accepting update for " << rr->d_name << "/" << QType(rr->d_type).getName() << ": Permitted by policy"<<endl;
+          }
+        }
+
         if (rr->d_class == QClass::NONE  && rr->d_type == QType::NS && rr->d_name == di.zone)
           nsRRtoDelete.push_back(rr);
         else