]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
implement security polling for auth
authorbert hubert <bert.hubert@netherlabs.nl>
Wed, 22 Oct 2014 14:55:06 +0000 (16:55 +0200)
committerbert hubert <bert.hubert@netherlabs.nl>
Wed, 22 Oct 2014 14:55:06 +0000 (16:55 +0200)
pdns/Makefile.am
pdns/common_startup.cc
pdns/secpoll-auth.cc [new file with mode: 0644]
pdns/secpoll-auth.hh [new file with mode: 0644]

index 6ab9c7ae8c0b000fe0f0d38e5acd39da4f394821..550b94113844f6a97f600a25890756ab2d2e246a 100644 (file)
@@ -163,6 +163,7 @@ pdns_server_SOURCES = \
        resolver.cc resolver.hh \
        responsestats.cc responsestats.hh \
        rfc2136handler.cc \
+       secpoll-auth.cc secpoll-auth.hh \
        serialtweaker.cc \
        sha.hh \
        signingpipe.cc signingpipe.hh \
index 7fdf0474853cf644ae5e7a1aaffe4d452adc06b4..824f8e8fd1eae403fd6eb1386ea7e4c22c5b6202 100644 (file)
@@ -21,6 +21,7 @@
 */
 #include "common_startup.hh"
 #include "ws-auth.hh"
+#include "secpoll-auth.hh"
 
 bool g_anyToTcp;
 typedef Distributor<DNSPacket,DNSPacket,PacketHandler> DNSDistributor;
@@ -160,6 +161,7 @@ void declareArguments()
   ::arg().set("max-nsec3-iterations","Limit the number of NSEC3 hash iterations")="500"; // RFC5155 10.3
 
   ::arg().set("include-dir","Include *.conf files from this directory");
+  ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="secpoll.powerdns.com.";
 }
 
 void declareStats(void)
@@ -199,7 +201,7 @@ void declareStats(void)
   S.declare("servfail-packets","Number of times a server-failed packet was sent out");
   S.declare("latency","Average number of microseconds needed to answer a question");
   S.declare("timedout-packets","Number of packets which weren't answered within timeout set");
-
+  S.declare("security-status", "Security status based on regular polling");
   S.declareRing("queries","UDP Queries Received");
   S.declareRing("nxdomain-queries","Queries for non-existent records within existent domains");
   S.declareRing("noerror-queries","Queries for existing records, but for type we don't have");
@@ -363,6 +365,9 @@ void mainthread()
 
    DNSPacket::s_udpTruncationThreshold = std::max(512, ::arg().asNum("udp-truncation-threshold"));
    DNSPacket::s_doEDNSSubnetProcessing = ::arg().mustDo("edns-subnet-processing");
+
+   doSecPoll(true); // this must be BEFORE chroot
+
    if(!::arg()["chroot"].empty()) {  
      if(::arg().mustDo("master") || ::arg().mustDo("slave"))
         gethostbyname("a.root-servers.net"); // this forces all lookup libraries to be loaded
@@ -400,13 +405,16 @@ void mainthread()
     TN->go(); // tcp nameserver launch
 
   pthread_create(&qtid,0,carbonDumpThread, 0); // runs even w/o carbon, might change @ runtime    
+
   //  fork(); (this worked :-))
   unsigned int max_rthreads= ::arg().asNum("receiver-threads", 1);
   for(unsigned int n=0; n < max_rthreads; ++n)
     pthread_create(&qtid,0,qthread, reinterpret_cast<void *>(n)); // receives packets
 
-  void *p;
-  pthread_join(qtid, &p);
+  for(;;) {
+    sleep(1800);
+    doSecPoll(false);
+  }
   
   L<<Logger::Error<<"Mainthread exiting - should never happen"<<endl;
 }
diff --git a/pdns/secpoll-auth.cc b/pdns/secpoll-auth.cc
new file mode 100644 (file)
index 0000000..a5f9d04
--- /dev/null
@@ -0,0 +1,167 @@
+#include "secpoll-auth.hh"
+
+#include "logger.hh"
+#include "arguments.hh"
+#include "version.hh"
+#include "version_generated.h"
+#include "dnsparser.hh"
+#include "misc.hh"
+#include <boost/foreach.hpp>
+#include "sstuff.hh"
+#include "dnswriter.hh"
+#include "dns_random.hh"
+#include "namespaces.hh"
+#include "statbag.hh"
+#ifndef PACKAGEVERSION 
+#define PACKAGEVERSION PDNS_VERSION
+#endif
+
+string g_security_message;
+
+extern StatBag S;
+
+static vector<ComboAddress> parseResolveConf()
+{
+  vector<ComboAddress> ret;
+  ifstream ifs("/etc/resolv.conf");
+  if(!ifs)
+    return ret;
+
+  string line;
+  while(std::getline(ifs, line)) {
+    boost::trim_right_if(line, is_any_of(" \r\n\x1a"));
+    boost::trim_left(line); // leading spaces, let's be nice
+
+    string::size_type tpos = line.find_first_of(";#");
+    if(tpos != string::npos)
+      line.resize(tpos);
+
+    if(boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
+      vector<string> parts;
+      stringtok(parts, line, " \t,"); // be REALLY nice
+      for(vector<string>::const_iterator iter = parts.begin()+1; iter != parts.end(); ++iter) {
+       
+       try {
+         ret.push_back(ComboAddress(*iter, 53));
+       }
+       catch(...)
+       {
+       }
+      }
+    }
+
+  }
+
+  return ret;
+}
+
+int doResolve(const string& qname, uint16_t qtype, vector<DNSResourceRecord>& ret) 
+{
+  vector<uint8_t> packet;
+
+  DNSPacketWriter pw(packet, qname, qtype);
+  pw.getHeader()->id=dns_random(0xffff);
+  pw.getHeader()->rd=1;
+
+  static vector<ComboAddress> s_servers;
+  vector<ComboAddress> servers = parseResolveConf();
+  if(!servers.empty())
+    s_servers = servers; // in case we chrooted in the meantime
+
+  if(s_servers.empty())
+    L<<Logger::Warning<<"Unable to poll PowerDNS security status, did not get any servers from resolv.conf"<<endl;
+
+  BOOST_FOREACH(ComboAddress& dest, s_servers) {
+    Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
+    sock.setNonBlocking();
+    sock.sendTo(string((char*)&*packet.begin(), (char*)&*packet.end()), dest);
+    
+    string reply;
+
+    waitForData(sock.getHandle(), 2, 0);
+    try {
+    retry:
+      sock.recvFrom(reply, dest);
+      if(reply.size() > sizeof(struct dnsheader)) {
+       struct dnsheader d;
+       memcpy(&d, reply.c_str(), sizeof(d));
+       if(d.id != pw.getHeader()->id)
+         goto retry;
+      }
+    }
+    catch(...) {
+      continue;
+    }
+    MOADNSParser mdp(reply);
+    if(mdp.d_header.rcode == RCode::ServFail)
+      continue;    
+    
+
+    for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {          
+      if(i->first.d_place == 1 && i->first.d_type==QType::TXT) {
+       DNSResourceRecord rr;
+       rr.qname = i->first.d_label;
+       rr.qtype = QType(i->first.d_type);
+       rr.content = i->first.d_content->getZoneRepresentation();
+       rr.ttl=i->first.d_ttl;
+       ret.push_back(rr);
+      }
+    }
+   
+    return mdp.d_header.rcode;
+  }
+  return RCode::ServFail;
+}
+
+void doSecPoll(bool first)
+{
+  if(::arg()["security-poll-suffix"].empty())
+    return;
+
+  struct timeval now;
+  gettimeofday(&now, 0);
+
+  string query = "auth-" PACKAGEVERSION ".security-status."+::arg()["security-poll-suffix"];
+
+  if(*query.rbegin()!='.')
+    query+='.';
+
+  boost::replace_all(query, "+", "_");
+
+  vector<DNSResourceRecord> ret;
+
+  int res=doResolve(query, QType::TXT, ret);
+
+  int security_status;
+
+  if(!res && !ret.empty()) {
+    string content=ret.begin()->content;
+    if(!content.empty() && content[0]=='"' && content[content.size()-1]=='"') {
+      content=content.substr(1, content.length()-2);
+    }
+      
+    pair<string, string> split = splitField(content, ' ');
+    
+    security_status = atoi(split.first.c_str());
+    g_security_message = split.second;
+
+  }
+  else {
+    L<<Logger::Warning<<"Could not retrieve security status update for '" PACKAGEVERSION "' on '"+query+"', RCODE = "<< RCode::to_s(res)<<endl;
+    if(security_status == 1) // it was ok, not it is unknown
+      security_status = 0;
+  }
+
+  if(security_status == 1 && first) {
+    L<<Logger::Warning << "Polled security status of version "<<PACKAGEVERSION<<" at startup, no known issues reported: " <<g_security_message<<endl;
+  }
+  if(security_status == 2) {
+    L<<Logger::Error<<"PowerDNS Security Update Recommended: "<<g_security_message<<endl;
+  }
+  else if(security_status == 3) {
+    L<<Logger::Error<<"PowerDNS Security Update Mandatory: "<<g_security_message<<endl;
+  }
+
+  S.set("security-status",security_status);
+
+}
diff --git a/pdns/secpoll-auth.hh b/pdns/secpoll-auth.hh
new file mode 100644 (file)
index 0000000..1b0c740
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef PDNS_SECPOLL_AUTH_HH
+#define PDNS_SECPOLL_AUTH_HH
+#include <time.h>
+#include "namespaces.hh"
+
+void doSecPoll(bool first);
+extern std::string g_security_message;
+
+#endif