--- Begin Message ---
- To: submit@bugs.debian.org
- Subject: buster-pu: package pdns_4.1.6-3+deb10u1
- From: Chris Hofstaedtler <zeha@debian.org>
- Date: Tue, 22 Sep 2020 22:22:57 +0200
- Message-id: <20200922202257.pkuowasu4nwcpbgv@zeha.at>
Package: release.debian.org
Severity: normal
Tags: buster
User: release.debian.org@packages.debian.org
Usertags: pu
Fixes for low-severity issues CVE-2019-10203 and CVE-2020-17482.
Both using upstream patches for the 4.1 branch.
Maybe it should be pointed out in the stable update notes that
manual action is needed to remedy CVE-2019-10203 for existing
installations using postgres. "Manual schema update required for
PostgreSQL"?
Chris
diff -Nru pdns-4.1.6/debian/changelog pdns-4.1.6/debian/changelog
--- pdns-4.1.6/debian/changelog 2019-06-21 19:07:07.000000000 +0000
+++ pdns-4.1.6/debian/changelog 2020-09-22 19:07:45.000000000 +0000
@@ -1,3 +1,13 @@
+pdns (4.1.6-3+deb10u1) buster; urgency=medium
+
+ * Apply upstream patches to fix CVE-2019-10203.
+ To actually fix this problem in existing installations, the newly
+ supplied schema file 4.1.10_to_4.1.11.schema.pgsql.sql has to be
+ manually applied to the backing PostgreSQL database. (Closes: #970729)
+ * Apply upstream patches to fix CVE-2020-17482 (Closes: #970737)
+
+ -- Chris Hofstaedtler <zeha@debian.org> Tue, 22 Sep 2020 19:07:45 +0000
+
pdns (4.1.6-3) unstable; urgency=medium
* Fix Denial of service via crafted zone records (CVE-2019-10162)
diff -Nru pdns-4.1.6/debian/patches/CVE-2019-10203.patch pdns-4.1.6/debian/patches/CVE-2019-10203.patch
--- pdns-4.1.6/debian/patches/CVE-2019-10203.patch 1970-01-01 00:00:00.000000000 +0000
+++ pdns-4.1.6/debian/patches/CVE-2019-10203.patch 2020-09-22 19:07:45.000000000 +0000
@@ -0,0 +1,54 @@
+From 6b48327a0da913d8eeb1c1a4938d3f22d80f9fb3 Mon Sep 17 00:00:00 2001
+From: Peter van Dijk <peter.van.dijk@powerdns.com>
+Date: Tue, 30 Jul 2019 15:40:09 +0200
+Subject: [PATCH] adjust gpgsql schema for advisory 2019-06
+
+---
+ modules/gpgsqlbackend/4.1.10_to_4.1.11.schema.pgsql.sql | 1 +
+ modules/gpgsqlbackend/schema.pgsql.sql | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+ create mode 100644 modules/gpgsqlbackend/4.1.10_to_4.1.11.schema.pgsql.sql
+
+diff --git a/modules/gpgsqlbackend/4.1.10_to_4.1.11.schema.pgsql.sql b/modules/gpgsqlbackend/4.1.10_to_4.1.11.schema.pgsql.sql
+new file mode 100644
+index 0000000000..b0c2ee1efa
+--- /dev/null
++++ b/modules/gpgsqlbackend/4.1.10_to_4.1.11.schema.pgsql.sql
+@@ -0,0 +1 @@
++ALTER TABLE domains ALTER notified_serial TYPE bigint USING CASE WHEN notified_serial >= 0 THEN notified_serial::bigint END;
+diff --git a/modules/gpgsqlbackend/schema.pgsql.sql b/modules/gpgsqlbackend/schema.pgsql.sql
+index b105d87951..cad35d5f19 100644
+--- a/modules/gpgsqlbackend/schema.pgsql.sql
++++ b/modules/gpgsqlbackend/schema.pgsql.sql
+@@ -4,7 +4,7 @@ CREATE TABLE domains (
+ master VARCHAR(128) DEFAULT NULL,
+ last_check INT DEFAULT NULL,
+ type VARCHAR(6) NOT NULL,
+- notified_serial INT DEFAULT NULL,
++ notified_serial BIGINT DEFAULT NULL,
+ account VARCHAR(40) DEFAULT NULL,
+ CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT)))
+ );
+
+
+From 15b1f3607691e6b0443696d6edca40cc3a04bbb0 Mon Sep 17 00:00:00 2001
+From: tcely <tcely@users.noreply.github.com>
+Date: Sun, 4 Aug 2019 05:12:30 -0400
+Subject: [PATCH] gpgsqlbackend: add missing schema file to Makefile
+
+---
+ modules/gpgsqlbackend/Makefile.am | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/modules/gpgsqlbackend/Makefile.am b/modules/gpgsqlbackend/Makefile.am
+index 8a820d516b..9e2f271702 100644
+--- a/modules/gpgsqlbackend/Makefile.am
++++ b/modules/gpgsqlbackend/Makefile.am
+@@ -12,6 +12,7 @@ dist_doc_DATA = \
+ schema.pgsql.sql \
+ nodnssec-3.x_to_3.4.0_schema.pgsql.sql \
+ dnssec-3.x_to_3.4.0_schema.pgsql.sql \
++ 4.1.10_to_4.1.11.schema.pgsql.sql \
+ 3.4.0_to_4.1.0_schema.pgsql.sql
+
+ libgpgsqlbackend_la_SOURCES = \
diff -Nru pdns-4.1.6/debian/patches/CVE-2020-17482.patch pdns-4.1.6/debian/patches/CVE-2020-17482.patch
--- pdns-4.1.6/debian/patches/CVE-2020-17482.patch 1970-01-01 00:00:00.000000000 +0000
+++ pdns-4.1.6/debian/patches/CVE-2020-17482.patch 2020-09-22 19:07:45.000000000 +0000
@@ -0,0 +1,153 @@
+From 3b88cb8c8cdd166b566ef7bd87f47732b2783f6a Mon Sep 17 00:00:00 2001
+From: Remi Gacogne <remi.gacogne@powerdns.com>
+Date: Tue, 11 Aug 2020 11:25:06 +0200
+Subject: [PATCH 1/2] Raise an exception on invalid hex content in unknown
+ records
+
+Otherwise we can end up reading uninitialised memory from the stack,
+possibly leaking information.
+This is only an issue if the content is read from an untrusted source
+and can be passed back to an attacker.
+---
+ pdns/dnsparser.cc | 24 ++++++++++++++++--------
+ pdns/test-dnsrecords_cc.cc | 32 ++++++++++++++++++++++++++++++++
+ 2 files changed, 48 insertions(+), 8 deletions(-)
+
+diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc
+index b6108d8c6b..3a4e193f01 100644
+--- a/pdns/dnsparser.cc
++++ b/pdns/dnsparser.cc
+@@ -40,17 +40,25 @@ class UnknownRecordContent : public DNSRecordContent
+ // parse the input
+ vector<string> parts;
+ stringtok(parts, zone);
+- if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) )
+- throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+std::to_string(parts.size())+": "+zone );
+- const string& relevant=(parts.size() > 2) ? parts[2] : "";
+- unsigned int total=pdns_stou(parts[1]);
+- if(relevant.size() % 2 || relevant.size() / 2 != total)
++ // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
++ if (parts.size() != 3 && !(parts.size() == 2 && equals(parts[1], "0"))) {
++ throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
++ }
++
++ const string& relevant = (parts.size() > 2) ? parts[2] : "";
++ unsigned int total = pdns_stou(parts[1]);
++ if (relevant.size() % 2 || (relevant.size() / 2) != total) {
+ throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
++ }
++
+ string out;
+- out.reserve(total+1);
+- for(unsigned int n=0; n < total; ++n) {
++ out.reserve(total + 1);
++
++ for (unsigned int n = 0; n < total; ++n) {
+ int c;
+- sscanf(relevant.c_str()+2*n, "%02x", &c);
++ if (sscanf(relevant.c_str()+2*n, "%02x", &c) != 1) {
++ throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
++ }
+ out.append(1, (char)c);
+ }
+
+diff --git a/pdns/test-dnsrecords_cc.cc b/pdns/test-dnsrecords_cc.cc
+index df4102a4fb..854de2125e 100644
+--- a/pdns/test-dnsrecords_cc.cc
++++ b/pdns/test-dnsrecords_cc.cc
+@@ -327,4 +327,36 @@ BOOST_AUTO_TEST_CASE(test_opt_record_out) {
+ BOOST_CHECK_EQUAL(makeHexDump(std::string(pak.begin(),pak.end())), makeHexDump(packet));
+ }
+
++// special record test, because Unknown record types are the worst
++BOOST_AUTO_TEST_CASE(test_unknown_records_in) {
++
++ auto validUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1 42");
++
++ // we need at least two parts
++ BOOST_CHECK_THROW(auto notEnoughPartsUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\#"), MOADNSException);
++
++ // two parts are OK when the RDATA size is 0, not OK otherwise
++ auto validEmptyUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 0");
++ BOOST_CHECK_THROW(auto twoPartsNotZeroUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1"), MOADNSException);
++
++ // RDATA length is not even
++ BOOST_CHECK_THROW(auto unevenUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1 A"), MOADNSException);
++
++ // RDATA length is not equal to the expected size
++ BOOST_CHECK_THROW(auto wrongRDATASizeUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 2 AA"), MOADNSException);
++
++ // RDATA is invalid (invalid hex value)
++ try {
++ auto invalidRDATAUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1 JJ");
++ // we should not reach that code
++ BOOST_CHECK(false);
++ // but if we do let's see what we got (likely what was left over on the stack)
++ BOOST_CHECK_EQUAL(invalidRDATAUnknown->getZoneRepresentation(), "\\# 1 jj");
++ }
++ catch (const MOADNSException& e)
++ {
++ // it's expected to end up there
++ }
++}
++
+ BOOST_AUTO_TEST_SUITE_END()
+
+From 11e1d5c7b676c1f51365b54aada0af4f63852ea0 Mon Sep 17 00:00:00 2001
+From: Remi Gacogne <remi.gacogne@powerdns.com>
+Date: Tue, 11 Aug 2020 14:07:32 +0200
+Subject: [PATCH 2/2] Raise an exception on invalid first part (!= \#) in
+ unknown records
+
+---
+ pdns/dnsparser.cc | 12 ++++++++----
+ pdns/test-dnsrecords_cc.cc | 3 +++
+ 2 files changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc
+index 3a4e193f01..356e4a6cef 100644
+--- a/pdns/dnsparser.cc
++++ b/pdns/dnsparser.cc
+@@ -41,12 +41,16 @@ class UnknownRecordContent : public DNSRecordContent
+ vector<string> parts;
+ stringtok(parts, zone);
+ // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
+- if (parts.size() != 3 && !(parts.size() == 2 && equals(parts[1], "0"))) {
++ if (parts.size() != 3 && !(parts.size() == 2 && equals(parts.at(1), "0"))) {
+ throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
+ }
+
+- const string& relevant = (parts.size() > 2) ? parts[2] : "";
+- unsigned int total = pdns_stou(parts[1]);
++ if (parts.at(0) != "\\#") {
++ throw MOADNSException("Unknown record was stored incorrectly, first part should be '\\#', got '" + parts.at(0) + "'");
++ }
++
++ const string& relevant = (parts.size() > 2) ? parts.at(2) : "";
++ unsigned int total = pdns_stou(parts.at(1));
+ if (relevant.size() % 2 || (relevant.size() / 2) != total) {
+ throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
+ }
+@@ -56,7 +60,7 @@ class UnknownRecordContent : public DNSRecordContent
+
+ for (unsigned int n = 0; n < total; ++n) {
+ int c;
+- if (sscanf(relevant.c_str()+2*n, "%02x", &c) != 1) {
++ if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
+ throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
+ }
+ out.append(1, (char)c);
+diff --git a/pdns/test-dnsrecords_cc.cc b/pdns/test-dnsrecords_cc.cc
+index 854de2125e..770102f1fd 100644
+--- a/pdns/test-dnsrecords_cc.cc
++++ b/pdns/test-dnsrecords_cc.cc
+@@ -339,6 +339,9 @@ BOOST_AUTO_TEST_CASE(test_unknown_records_in) {
+ auto validEmptyUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 0");
+ BOOST_CHECK_THROW(auto twoPartsNotZeroUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1"), MOADNSException);
+
++ // the first part has to be "\#"
++ BOOST_CHECK_THROW(auto invalidFirstPartUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\$ 0"), MOADNSException);
++
+ // RDATA length is not even
+ BOOST_CHECK_THROW(auto unevenUnknown = DNSRecordContent::mastermake(static_cast<QType::typeenum>(65534), QClass::IN, "\\# 1 A"), MOADNSException);
+
diff -Nru pdns-4.1.6/debian/patches/series pdns-4.1.6/debian/patches/series
--- pdns-4.1.6/debian/patches/series 2019-06-21 19:07:07.000000000 +0000
+++ pdns-4.1.6/debian/patches/series 2020-09-22 19:07:45.000000000 +0000
@@ -1,3 +1,5 @@
CVE-2019-3871-auth-4.1.6.patch
CVE-2019-10162-4.1.8-invalidrecords.patch
CVE-2019-10163-4.1.8-busyloop.patch
+CVE-2019-10203.patch
+CVE-2020-17482.patch
--- End Message ---