CVE-2016-5426, CVE-2016-5427, CVE-2016-6172

This commit is contained in:
Ruben Kerkhof 2016-10-31 22:27:09 +01:00
parent 03fab67ac8
commit 8fe85afbc6
5 changed files with 318 additions and 14 deletions

View File

@ -0,0 +1,167 @@
From 0086c2b1374072d2b0609407f8e60fade192ae88 Mon Sep 17 00:00:00 2001
From: Remi Gacogne <remi.gacogne@powerdns.com>
Date: Fri, 1 Jul 2016 15:30:20 +0200
Subject: [PATCH 1/2] Reject qname's wirelength > 255, `chopOff()` handle dot
inside labels
(cherry picked from commit 881b5b03a590198d03008e4200dd00cc537712f3)
---
pdns/dnsparser.cc | 17 +++++++++++----
pdns/dnsparser.hh | 2 +-
pdns/misc.cc | 65 +++++++++++++++++++++++++++++++++++++------------------
3 files changed, 58 insertions(+), 26 deletions(-)
diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc
index e54b678..f3e3d87 100644
--- a/pdns/dnsparser.cc
+++ b/pdns/dnsparser.cc
@@ -380,8 +380,9 @@ uint8_t PacketReader::get8BitInt()
string PacketReader::getLabel(unsigned int recurs)
{
string ret;
+ size_t wirelength = 0;
ret.reserve(40);
- getLabelFromContent(d_content, d_pos, ret, recurs++);
+ getLabelFromContent(d_content, d_pos, ret, recurs++, wirelength);
return ret;
}
@@ -431,7 +432,7 @@ string PacketReader::getText(bool multi)
}
-void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs)
+void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs, size_t& wirelength)
{
if(recurs > 100) // the forward reference-check below should make this test 100% obsolete
throw MOADNSException("Loop");
@@ -440,6 +441,10 @@ void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t&
// it is tempting to call reserve on ret, but it turns out it creates a malloc/free storm in the loop
for(;;) {
unsigned char labellen=content.at(frompos++);
+ wirelength++;
+ if (wirelength > 255) {
+ throw MOADNSException("Overly long DNS name ("+lexical_cast<string>(wirelength)+")");
+ }
if(!labellen) {
if(ret.empty())
@@ -452,11 +457,15 @@ void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t&
if(offset >= pos)
throw MOADNSException("forward reference during label decompression");
- return getLabelFromContent(content, offset, ret, ++recurs);
+ /* the compression pointer does not count into the wire length */
+ return getLabelFromContent(content, offset, ret, ++recurs, --wirelength);
}
else {
+ if (wirelength + labellen > 255) {
+ throw MOADNSException("Overly long DNS name ("+lexical_cast<string>(wirelength)+")");
+ }
+ wirelength += labellen;
// XXX FIXME THIS MIGHT BE VERY SLOW!
-
for(string::size_type n = 0 ; n < labellen; ++n, frompos++) {
if(content.at(frompos)=='.' || content.at(frompos)=='\\') {
ret.append(1, '\\');
diff --git a/pdns/dnsparser.hh b/pdns/dnsparser.hh
index 414c73e..cc0cf44 100644
--- a/pdns/dnsparser.hh
+++ b/pdns/dnsparser.hh
@@ -128,7 +128,7 @@ public:
void xfrHexBlob(string& blob, bool keepReading=false);
static uint16_t get16BitInt(const vector<unsigned char>&content, uint16_t& pos);
- static void getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs);
+ static void getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs, size_t& wirelength);
void getDnsrecordheader(struct dnsrecordheader &ah);
void copyRecord(vector<unsigned char>& dest, uint16_t len);
diff --git a/pdns/misc.cc b/pdns/misc.cc
index 2d5dc21..73a8433 100644
--- a/pdns/misc.cc
+++ b/pdns/misc.cc
@@ -125,16 +125,27 @@ bool chopOff(string &domain)
if(domain.empty())
return false;
- string::size_type fdot=domain.find('.');
-
- if(fdot==string::npos)
- domain="";
- else {
- string::size_type remain = domain.length() - (fdot + 1);
- char tmp[remain];
- memcpy(tmp, domain.c_str()+fdot+1, remain);
- domain.assign(tmp, remain); // don't dare to do this w/o tmp holder :-)
+ bool escaped = false;
+ const string::size_type domainLen = domain.length();
+ for (size_t fdot = 0; fdot < domainLen; fdot++)
+ {
+ if (domain[fdot] == '.' && !escaped) {
+ string::size_type remain = domainLen - (fdot + 1);
+ char tmp[remain];
+ memcpy(tmp, domain.c_str()+fdot+1, remain);
+ domain.assign(tmp, remain); // don't dare to do this w/o tmp holder :-)
+
+ return true;
+ }
+ else if (domain[fdot] == '\\' && !escaped) {
+ escaped = true;
+ }
+ else {
+ escaped = false;
+ }
}
+
+ domain = "";
return true;
}
@@ -144,19 +155,31 @@ bool chopOffDotted(string &domain)
if(domain.empty() || (domain.size()==1 && domain[0]=='.'))
return false;
- string::size_type fdot=domain.find('.');
- if(fdot == string::npos)
- return false;
-
- if(fdot==domain.size()-1)
- domain=".";
- else {
- string::size_type remain = domain.length() - (fdot + 1);
- char tmp[remain];
- memcpy(tmp, domain.c_str()+fdot+1, remain);
- domain.assign(tmp, remain);
+ bool escaped = false;
+ const string::size_type domainLen = domain.length();
+ for (size_t fdot = 0; fdot < domainLen; fdot++)
+ {
+ if (domain[fdot] == '.' && !escaped) {
+ if (fdot==domain.size()-1) {
+ domain=".";
+ }
+ else {
+ string::size_type remain = domainLen - (fdot + 1);
+ char tmp[remain];
+ memcpy(tmp, domain.c_str()+fdot+1, remain);
+ domain.assign(tmp, remain); // don't dare to do this w/o tmp holder :-)
+ }
+ return true;
+ }
+ else if (domain[fdot] == '\\' && !escaped) {
+ escaped = true;
+ }
+ else {
+ escaped = false;
+ }
}
- return true;
+
+ return false;
}
--
2.10.2

View File

@ -0,0 +1,116 @@
From 2886c5826dd9af891cbaa3ac9fd928838ef75387 Mon Sep 17 00:00:00 2001
From: Remi Gacogne <remi.gacogne@powerdns.com>
Date: Thu, 7 Jul 2016 16:17:22 +0200
Subject: [PATCH 2/2] Add limits to the size of received AXFR, in megabytes
This prevents resource exhaustion in case the master is sending a
very large amount of data in an update.
(cherry picked from commit a014f4c224a7b21f1c648257d1fd1128413129aa)
---
pdns/common_startup.cc | 2 ++
pdns/docs/pdns.xml | 11 +++++++++++
pdns/pdns.conf-dist | 4 ++++
pdns/resolver.cc | 15 +++++++++++----
pdns/resolver.hh | 5 ++++-
pdns/slavecommunicator.cc | 2 +-
6 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc
index 41ca348..adc50e2 100644
--- a/pdns/common_startup.cc
+++ b/pdns/common_startup.cc
@@ -150,6 +150,8 @@ void declareArguments()
::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.";
+
+ ::arg().set("xfr-max-received-mbytes", "Maximum number of megabytes received from an incoming AXFR")="100";
}
void declareStats(void)
diff --git a/pdns/pdns.conf-dist b/pdns/pdns.conf-dist
index ccb03f9..146f89f 100644
--- a/pdns/pdns.conf-dist
+++ b/pdns/pdns.conf-dist
@@ -464,4 +464,8 @@
#
# wildcard-url=no
+#################################
+# xfr-max-received-mbytes Maximum number of megabytes received from an incoming AXFR
+#
+# xfr-max-received-mbytes=100
diff --git a/pdns/resolver.cc b/pdns/resolver.cc
index 792c8fb..9dd9182 100644
--- a/pdns/resolver.cc
+++ b/pdns/resolver.cc
@@ -305,8 +305,9 @@ AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
const string& tsigkeyname,
const string& tsigalgorithm,
const string& tsigsecret,
- const ComboAddress* laddr)
-: d_tsigkeyname(tsigkeyname), d_tsigsecret(tsigsecret), d_tsigPos(0), d_nonSignedMessages(0)
+ const ComboAddress* laddr,
+ size_t maxReceivedBytes)
+: d_tsigkeyname(tsigkeyname), d_tsigsecret(tsigsecret), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes), d_tsigPos(0), d_nonSignedMessages(0)
{
ComboAddress local;
if (laddr != NULL) {
@@ -384,8 +385,14 @@ int AXFRRetriever::getChunk(Resolver::res_t &res) // Implementation is making su
int len=getLength();
if(len<0)
throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
-
- timeoutReadn(len);
+
+ if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
+ throw ResolverException("Reached the maximum number of received bytes during AXFR");
+
+ timeoutReadn(len);
+
+ d_receivedBytes += (uint16_t) len;
+
MOADNSParser mdp(d_buf.get(), len);
int err = parseResult(mdp, "", 0, 0, &res);
diff --git a/pdns/resolver.hh b/pdns/resolver.hh
index 3633bf2..a783b84 100644
--- a/pdns/resolver.hh
+++ b/pdns/resolver.hh
@@ -86,7 +86,8 @@ class AXFRRetriever : public boost::noncopyable
const string& tsigkeyname=string(),
const string& tsigalgorithm=string(),
const string& tsigsecret=string(),
- const ComboAddress* laddr = NULL);
+ const ComboAddress* laddr = NULL,
+ size_t maxReceivedBytes=0);
~AXFRRetriever();
int getChunk(Resolver::res_t &res);
@@ -105,6 +106,8 @@ class AXFRRetriever : public boost::noncopyable
string d_tsigsecret;
string d_prevMac; // RFC2845 4.4
string d_signData;
+ size_t d_receivedBytes;
+ size_t d_maxReceivedBytes;
uint32_t d_tsigPos;
uint d_nonSignedMessages; // RFC2845 4.4
TSIGRecordContent d_trc;
diff --git a/pdns/slavecommunicator.cc b/pdns/slavecommunicator.cc
index 492ac41..12c2316 100644
--- a/pdns/slavecommunicator.cc
+++ b/pdns/slavecommunicator.cc
@@ -153,7 +153,7 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
vector<DNSResourceRecord> rrs;
ComboAddress raddr(remote, 53);
- AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret, (laddr.sin4.sin_family == 0) ? NULL : &laddr);
+ AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret, (laddr.sin4.sin_family == 0) ? NULL : &laddr, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024);
Resolver::res_t recs;
while(retriever.getChunk(recs)) {
if(first) {
--
2.10.2

View File

@ -0,0 +1,25 @@
From 2bdeeb685e70105826739e774e93427e1bc69053 Mon Sep 17 00:00:00 2001
From: Ruben Kerkhof <ruben@rubenkerkhof.com>
Date: Mon, 31 Oct 2016 22:17:30 +0100
Subject: [PATCH 3/3] Disable secpoll
---
pdns/common_startup.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pdns/common_startup.cc b/pdns/common_startup.cc
index adc50e2..a5ff792 100644
--- a/pdns/common_startup.cc
+++ b/pdns/common_startup.cc
@@ -149,7 +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.";
+ ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="";
::arg().set("xfr-max-received-mbytes", "Maximum number of megabytes received from an incoming AXFR")="100";
}
--
2.10.2

View File

@ -1,11 +0,0 @@
--- pdns-3.3.3/pdns/common_startup.cc.orig 2015-06-08 13:59:02.000000000 +0200
+++ pdns-3.3.3/pdns/common_startup.cc 2015-06-10 14:11:35.253756981 +0200
@@ -149,7 +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.";
+ ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="";
}
void declareStats(void)

View File

@ -2,7 +2,7 @@
Name: pdns
Version: 3.3.3
Release: 1%{?dist}
Release: 2%{?dist}
Summary: A modern, advanced and high performance authoritative-only nameserver
Group: System Environment/Daemons
License: GPLv2
@ -10,7 +10,9 @@ URL: http://powerdns.com
Source0: http://downloads.powerdns.com/releases/%{name}-%{version}.tar.gz
Patch0: pdns-default-config.patch
Patch1: pdns-fixinit.patch
Patch2: pdns-disable-secpoll.patch
Patch2: 0001-Reject-qname-s-wirelength-255-chopOff-handle-dot-ins.patch
Patch3: 0002-Add-limits-to-the-size-of-received-AXFR-in-megabytes.patch
Patch4: 0003-Disable-secpoll.patch
Requires(pre): shadow-utils
Requires(post): /sbin/chkconfig
@ -119,7 +121,9 @@ This package contains the SQLite backend for %{name}
%setup -q
%patch0 -p1 -b .default-config-patch
%patch1 -p1 -b .fixinit
%patch2 -p1 -b .disable-secpoll
%patch2 -p1
%patch3 -p1
%patch4 -p1
%build
export CPPFLAGS="-DLDAP_DEPRECATED"
@ -242,6 +246,9 @@ fi
%{_libdir}/%{name}/libgsqlite3backend.so
%changelog
* Mon Oct 31 2016 Ruben Kerkhof <ruben@rubenkerkhof.com> - 3.3.3-2
- CVE-2016-5426, CVE-2016-5427, CVE-2016-6172
* Wed Jun 10 2015 Morten Stevens <mstevens@imt-systems.com> - 3.3.3-1
- Update to 3.3.3
- Disable security status polling by default