From: PowerDNS Team
Date: Fri Jan 13 09:12:00 CET 2017
Subject: Address CVE-2016-7073 and CVE-2016-7074

Origin: upstream, https://downloads.powerdns.com/patches/2016-04/pdns-3.3.3-tsig-165253.patch

---
 pdns/dnspacket.cc   |    9 ++++++---
 pdns/dnsparser.cc   |    6 +++++-
 pdns/dnsparser.hh   |    2 +-
 pdns/dnssecinfra.cc |   17 +++++++++++++++++
 pdns/dnssecinfra.hh |    1 +
 pdns/resolver.cc    |    8 +++++++-
 pdns/tcpreceiver.cc |   16 ++++++++--------
 7 files changed, 45 insertions(+), 14 deletions(-)

--- a/pdns/dnspacket.cc
+++ b/pdns/dnspacket.cc
@@ -438,7 +438,7 @@ bool DNSPacket::getTSIGDetails(TSIGRecor
   
   bool gotit=false;
   for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {          
-    if(i->first.d_type == QType::TSIG) {
+    if(i->first.d_type == QType::TSIG && i->first.d_class == 0xff) {
       *trc = *boost::dynamic_pointer_cast<TSIGRecordContent>(i->first.d_content);
       
       gotit=true;
@@ -574,7 +574,10 @@ bool checkForCorrectTSIG(const DNSPacket
 {
   string message;
   
-  q->getTSIGDetails(trc, keyname, &message);
+  if (!q->getTSIGDetails(trc, keyname, &message)) {
+    return false;
+  }
+
   uint64_t now = time(0);
   if(abs(trc->d_time - now) > trc->d_fudge) {
     L<<Logger::Error<<"Packet for '"<<q->qdomain<<"' denied: TSIG (key '"<<*keyname<<"') time delta "<< abs(trc->d_time - now)<<" > 'fudge' "<<trc->d_fudge<<endl;
@@ -589,7 +592,7 @@ bool checkForCorrectTSIG(const DNSPacket
   }
   trc->d_algoName += ".sig-alg.reg.int.";
   B64Decode(secret64, *secret);
-  bool result=calculateMD5HMAC(*secret, message) == trc->d_mac;
+  bool result=constantTimeStringEquals(calculateMD5HMAC(*secret, message), trc->d_mac);
   if(!result) {
     L<<Logger::Error<<"Packet for domain '"<<q->qdomain<<"' denied: TSIG signature mismatch using '"<<*keyname<<"' and algorithm '"<<trc->d_algoName<<"'"<<endl;
   }
--- a/pdns/dnsparser.cc
+++ b/pdns/dnsparser.cc
@@ -264,8 +264,12 @@ void MOADNSParser::init(bool query, cons
 
       d_answers.push_back(make_pair(dr, pr.d_pos));
 
-      if(dr.d_type == QType::TSIG && dr.d_class == 0xff) 
+      if(dr.d_type == QType::TSIG && dr.d_class == 0xff) {
+        if(dr.d_place != DNSRecord::Additional || n != (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount) - 1) {
+          throw MOADNSException("Packet ("+d_qname+"|#"+lexical_cast<string>(d_qtype)+") has a TSIG record in an invalid position.");
+        }
         d_tsigPos = recordStartPos + sizeof(struct dnsheader);
+      }
     }
 
 #if 0    
--- a/pdns/dnsparser.hh
+++ b/pdns/dnsparser.hh
@@ -323,7 +323,7 @@ public:
     return pr;
   }
 
-  uint16_t getTSIGPos()
+  uint16_t getTSIGPos() const
   {
     return d_tsigPos;
   }
--- a/pdns/dnssecinfra.cc
+++ b/pdns/dnssecinfra.cc
@@ -422,6 +422,23 @@ string calculateMD5HMAC(const std::strin
   return md5_2.get();
 }
 
+bool constantTimeStringEquals(const std::string& a, const std::string& b)
+{
+  if (a.size() != b.size()) {
+    return false;
+  }
+  const size_t size = a.size();
+  const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
+  const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
+  unsigned char res = 0;
+
+  for (size_t idx = 0; idx < size; idx++) {
+    res |= _a[idx] ^ _b[idx];
+  }
+
+  return res == 0;
+}
+
 string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigOffset, const string& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset)
 {
   string message;
--- a/pdns/dnssecinfra.hh
+++ b/pdns/dnssecinfra.hh
@@ -129,6 +129,7 @@ class DNSPacket;
 void addRRSigs(DNSSECKeeper& dk, DNSBackend& db, const std::set<string, CIStringCompare>& authMap, vector<DNSResourceRecord>& rrs);
 
 string calculateMD5HMAC(const std::string& key_, const std::string& text);
+bool constantTimeStringEquals(const std::string& a, const std::string& b);
 string makeTSIGMessageFromTSIGPacket(const string& opacket, unsigned int tsigoffset, const string& keyname, const TSIGRecordContent& trc, const string& previous, bool timersonly, unsigned int dnsHeaderOffset=0);
 void addTSIG(DNSPacketWriter& pw, TSIGRecordContent* trc, const string& tsigkeyname, const string& tsigsecret, const string& tsigprevious, bool timersonly);
 
--- a/pdns/resolver.cc
+++ b/pdns/resolver.cc
@@ -420,6 +420,7 @@ int AXFRRetriever::getChunk(Resolver::re
         shared_ptr<TSIGRecordContent> trc = boost::dynamic_pointer_cast<TSIGRecordContent>(answer.first.d_content);
         theirMac = trc->d_mac;
         d_trc.d_time = trc->d_time;
+	d_trc.d_fudge = trc->d_fudge;
         checkTSIG = true;
       }
     }
@@ -432,6 +433,11 @@ int AXFRRetriever::getChunk(Resolver::re
       if (theirMac.empty())
         throw ResolverException("No TSIG on AXFR response from "+d_remote.toStringWithPort()+" , should be signed with TSIG key '"+d_tsigkeyname+"'");
 
+      uint64_t delta = std::abs((int64_t)d_trc.d_time - (int64_t)time(0));
+      if(delta > d_trc.d_fudge) {
+        throw ResolverException("Invalid TSIG time delta " + lexical_cast<string>(delta) + " >  fudge " + lexical_cast<string>(d_trc.d_fudge));
+      }
+
       string message;
       if (!d_prevMac.empty()) {
         message = makeTSIGMessageFromTSIGPacket(d_signData, d_tsigPos, d_tsigkeyname, d_trc, d_prevMac, true, d_signData.size()-len);
@@ -441,7 +447,7 @@ int AXFRRetriever::getChunk(Resolver::re
       string ourMac=calculateMD5HMAC(d_tsigsecret, message);
 
       // ourMac[0]++; // sabotage == for testing :-)
-      if(ourMac != theirMac) {
+      if(!constantTimeStringEquals(ourMac, theirMac)) {
         throw ResolverException("Signature failed to validate on AXFR response from "+d_remote.toStringWithPort()+" signed with TSIG key '"+d_tsigkeyname+"'");
       }
 
--- a/pdns/tcpreceiver.cc
+++ b/pdns/tcpreceiver.cc
@@ -550,9 +550,9 @@ int TCPNameserver::doAXFR(const string &
   TSIGRecordContent trc;
   string tsigkeyname, tsigsecret;
 
-  q->getTSIGDetails(&trc, &tsigkeyname, 0);
+  bool haveTSIGDetails = q->getTSIGDetails(&trc, &tsigkeyname, 0);
 
-  if(!tsigkeyname.empty()) {
+  if(haveTSIGDetails && !tsigkeyname.empty()) {
     string tsig64, algorithm;
     Lock l(&s_plock);
     s_P->getBackend()->getTSIGKey(tsigkeyname, &algorithm, &tsig64);
@@ -573,7 +573,7 @@ int TCPNameserver::doAXFR(const string &
     addRRSigs(dk, signatureDB, authSet, outpacket->getRRS());
   }
   
-  if(!tsigkeyname.empty())
+  if(haveTSIGDetails && !tsigkeyname.empty())
     outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac); // first answer is 'normal'
   
   sendPacket(outpacket, outsock);
@@ -647,7 +647,7 @@ int TCPNameserver::doAXFR(const string &
       for(;;) {
         outpacket->getRRS() = csp.getChunk();
         if(!outpacket->getRRS().empty()) {
-          if(!tsigkeyname.empty()) 
+          if(haveTSIGDetails && !tsigkeyname.empty()) 
             outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true);
           sendPacket(outpacket, outsock);
           trc.d_mac=outpacket->d_trc.d_mac;
@@ -691,7 +691,7 @@ int TCPNameserver::doAXFR(const string &
           for(;;) {
             outpacket->getRRS() = csp.getChunk();
             if(!outpacket->getRRS().empty()) {
-              if(!tsigkeyname.empty())
+              if(haveTSIGDetails && !tsigkeyname.empty())
                 outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true);
               sendPacket(outpacket, outsock);
               trc.d_mac=outpacket->d_trc.d_mac;
@@ -726,7 +726,7 @@ int TCPNameserver::doAXFR(const string &
         for(;;) {
           outpacket->getRRS() = csp.getChunk();
           if(!outpacket->getRRS().empty()) {
-            if(!tsigkeyname.empty())
+            if(haveTSIGDetails && !tsigkeyname.empty())
               outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); 
             sendPacket(outpacket, outsock);
             trc.d_mac=outpacket->d_trc.d_mac;
@@ -747,7 +747,7 @@ int TCPNameserver::doAXFR(const string &
   for(;;) { 
     outpacket->getRRS() = csp.getChunk(true); // flush the pipe
     if(!outpacket->getRRS().empty()) {
-      if(!tsigkeyname.empty())
+      if(haveTSIGDetails && !tsigkeyname.empty())
         outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); // first answer is 'normal'
       sendPacket(outpacket, outsock);
       trc.d_mac=outpacket->d_trc.d_mac;
@@ -766,7 +766,7 @@ int TCPNameserver::doAXFR(const string &
   outpacket=getFreshAXFRPacket(q);
   outpacket->addRecord(soa);
   editSOA(dk, sd.qname, outpacket.get());
-  if(!tsigkeyname.empty())
+  if(haveTSIGDetails && !tsigkeyname.empty())
     outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); 
   
   sendPacket(outpacket, outsock);
