Permalink
Browse files

implement security polling for auth

  • Loading branch information...
1 parent b3dec9c commit 8115a837fac21ee77bd325e61c8f85c8674016fe @ahupowerdns ahupowerdns committed with mind04 Oct 22, 2014
Showing with 189 additions and 4 deletions.
  1. +2 −1 pdns/Makefile.am
  2. +11 −3 pdns/common_startup.cc
  3. +167 −0 pdns/secpoll-auth.cc
  4. +9 −0 pdns/secpoll-auth.hh
View
@@ -59,7 +59,8 @@ randomhelper.cc namespaces.hh nsecrecords.cc base32.cc dbdnsseckeeper.cc dnsseci
dnsseckeeper.hh dnssecinfra.hh base32.hh dns.cc dnssecsigner.cc polarrsakeyinfra.cc \
md5.hh signingpipe.cc signingpipe.hh dnslabeltext.cc lua-pdns.cc lua-auth.cc lua-auth.hh serialtweaker.cc \
ednssubnet.cc ednssubnet.hh cachecleaner.hh json.cc json.hh \
-version.hh version.cc
+version.hh version.cc secpoll-auth.cc secpoll-auth.hh
+
#
pdns_server_LDFLAGS=@moduleobjects@ @modulelibs@ @DYNLINKFLAGS@ @LIBDL@ @THREADFLAGS@ $(BOOST_SERIALIZATION_LDFLAGS) -rdynamic
@@ -16,6 +16,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "common_startup.hh"
+#include "secpoll-auth.hh"
+
bool g_anyToTcp;
bool g_addSuperfluousNSEC3;
typedef Distributor<DNSPacket,DNSPacket,PacketHandler> DNSDistributor;
@@ -147,6 +149,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)
@@ -178,7 +181,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");
@@ -338,6 +341,9 @@ void mainthread()
DNSPacket::s_doEDNSSubnetProcessing = ::arg().mustDo("edns-subnet-processing");
#ifndef WIN32
+
+ 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
@@ -377,8 +383,10 @@ void mainthread()
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;
}
View
@@ -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);
+
+}
@@ -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

0 comments on commit 8115a83

Please sign in to comment.