Bug#862569: unblock: bind9/1:9.10.3.dfsg.P4-12.3
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
Hi
Please unblock package bind9
The upload fixes three issues:
+bind9 (1:9.10.3.dfsg.P4-12.3) unstable; urgency=high
+
+ * Non-maintainer upload.
+ * Dns64 with "break-dnssec yes;" can result in a assertion failure
+ (CVE-2017-3136) (Closes: #860224)
+ * Some chaining (CNAME or DNAME) responses to upstream queries could trigger
+ assertion failures (CVE-2017-3137) (Closes: #860225)
+ * 'rndc ""' could trigger a assertion failure in named (CVE-2017-3138)
+ (Closes: #860226)
+
+ -- Salvatore Bonaccorso <carnil@debian.org> Sun, 07 May 2017 15:22:46 +0200
of which CVE-2017-3137 should be considered RC (and filled as such),
the other two are minor, but were included as well in the stable
update released as DSA-3854-1.
unblock bind9/1:9.10.3.dfsg.P4-12.3
Attached is the full debdiff agains the current version in testing.
Regards,
Salvatore
diff -Nru bind9-9.10.3.dfsg.P4/debian/changelog bind9-9.10.3.dfsg.P4/debian/changelog
--- bind9-9.10.3.dfsg.P4/debian/changelog 2017-04-18 17:42:50.000000000 +0200
+++ bind9-9.10.3.dfsg.P4/debian/changelog 2017-05-07 15:22:46.000000000 +0200
@@ -1,3 +1,15 @@
+bind9 (1:9.10.3.dfsg.P4-12.3) unstable; urgency=high
+
+ * Non-maintainer upload.
+ * Dns64 with "break-dnssec yes;" can result in a assertion failure
+ (CVE-2017-3136) (Closes: #860224)
+ * Some chaining (CNAME or DNAME) responses to upstream queries could trigger
+ assertion failures (CVE-2017-3137) (Closes: #860225)
+ * 'rndc ""' could trigger a assertion failure in named (CVE-2017-3138)
+ (Closes: #860226)
+
+ -- Salvatore Bonaccorso <carnil@debian.org> Sun, 07 May 2017 15:22:46 +0200
+
bind9 (1:9.10.3.dfsg.P4-12.2) unstable; urgency=medium
* Non-maintainer upload.
diff -Nru bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3136.patch bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3136.patch
--- bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3136.patch 1970-01-01 01:00:00.000000000 +0100
+++ bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3136.patch 2017-05-07 15:22:46.000000000 +0200
@@ -0,0 +1,19 @@
+From 764240ca07ab1b796226d5402ccd9fbfa77ec32a Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Wed, 15 Feb 2017 12:18:51 +1100
+Subject: [PATCH] 4575. [security] Dns64 with break-dnssec yes; can
+ result in a assertion failure. (CVE-2017-3136) [RT
+ #44653]
+
+(cherry picked from commit 3bce12e4b6d37f570ffc7747b499f8b90e8521ac)
+---
+--- a/bin/named/query.c
++++ b/bin/named/query.c
+@@ -8145,6 +8145,7 @@ query_find(ns_client_t *client, dns_fetc
+ result = query_dns64(client, &fname, rdataset,
+ sigrdataset, dbuf,
+ DNS_SECTION_ANSWER);
++ noqname = NULL;
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, &rdataset);
+ if (result == ISC_R_NOMORE) {
diff -Nru bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-1.patch bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-1.patch
--- bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-1.patch 1970-01-01 01:00:00.000000000 +0100
+++ bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-1.patch 2017-05-07 15:22:46.000000000 +0200
@@ -0,0 +1,83 @@
+From 69fd759b4aa02047e42e5cf4227f8257c4547988 Mon Sep 17 00:00:00 2001
+From: Evan Hunt <each@isc.org>
+Date: Thu, 23 Feb 2017 15:01:30 -0800
+Subject: [PATCH] [v9_10] remove unnecessary INSIST and prep 9.10.5rc2
+
+4578. [security] Some chaining (CNAME or DNAME) responses to upstream
+ queries could trigger assertion failures.
+ (CVE-2017-3137) [RT #44734]
+
+(cherry picked from commit a1365a0042db8c1cd0ee4dbd0c91ce65ae09e098)
+(cherry picked from commit 559cbe04e73cf601784a371e09554c20407a6c7b)
+---
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -6924,15 +6924,15 @@ answer_response(fetchctx_t *fctx) {
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+ rdataset->trust = dns_trust_answer;
+- if (chaining == 0) {
++ if (external) {
+ /*
+- * This data is "the" answer
+- * to our question only if
+- * we're not chaining (i.e.
+- * if we haven't followed
+- * a CNAME or DNAME).
++ * This data is outside of
++ * our query domain, and
++ * may not be cached.
+ */
+- INSIST(!external);
++ rdataset->attributes |=
++ DNS_RDATASETATTR_EXTERNAL;
++ } else if (chaining == 0) {
+ /*
+ * Don't use found_cname here
+ * as we have just set it
+@@ -6954,14 +6954,6 @@ answer_response(fetchctx_t *fctx) {
+ if (aa)
+ rdataset->trust =
+ dns_trust_authanswer;
+- } else if (external) {
+- /*
+- * This data is outside of
+- * our query domain, and
+- * may not be cached.
+- */
+- rdataset->attributes |=
+- DNS_RDATASETATTR_EXTERNAL;
+ }
+
+ /*
+@@ -7136,15 +7128,12 @@ answer_response(fetchctx_t *fctx) {
+ * If we are not chaining or the first CNAME
+ * is a synthesised CNAME before the DNAME.
+ */
+- if ((chaining == 0) ||
+- (chaining == 1U && synthcname))
++ if (external) {
++ rdataset->attributes |=
++ DNS_RDATASETATTR_EXTERNAL;
++ } else if ((chaining == 0) ||
++ (chaining == 1U && synthcname))
+ {
+- /*
+- * This data is "the" answer to
+- * our question only if we're
+- * not chaining.
+- */
+- INSIST(!external);
+ if (aflag == DNS_RDATASETATTR_ANSWER) {
+ have_answer = ISC_TRUE;
+ found_dname = ISC_TRUE;
+@@ -7161,9 +7150,6 @@ answer_response(fetchctx_t *fctx) {
+ if (aa)
+ rdataset->trust =
+ dns_trust_authanswer;
+- } else if (external) {
+- rdataset->attributes |=
+- DNS_RDATASETATTR_EXTERNAL;
+ }
+ }
+
diff -Nru bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-2.patch bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-2.patch
--- bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-2.patch 1970-01-01 01:00:00.000000000 +0100
+++ bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-2.patch 2017-05-07 15:22:46.000000000 +0200
@@ -0,0 +1,1022 @@
+From 6841d7b854c15df9ec56cab38da201b315bbcabb Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Wed, 1 Mar 2017 12:01:16 +1100
+Subject: [PATCH] Reimplement: 4578. [security] Some chaining (CNAME or
+ DNAME) responses to upstream queries could trigger
+ assertion failures. (CVE-2017-3137) [RT #44734]
+
+(cherry picked from commit f240f4a5decae09cdabb83f824e0fd339377ad7e)
+---
+ bin/tests/system/dname/ns2/example.db | 1 +
+ bin/tests/system/dname/tests.sh | 15 +-
+ bin/tests/system/rpz/tests.sh | 2 +-
+ lib/dns/resolver.c | 813 +++++++++++++---------------------
+ 4 files changed, 320 insertions(+), 511 deletions(-)
+
+--- a/bin/tests/system/dname/ns2/example.db
++++ b/bin/tests/system/dname/ns2/example.db
+@@ -29,4 +29,5 @@ a.short A 10.0.0.1
+ short-dname DNAME short
+ a.longlonglonglonglonglonglonglonglonglonglonglonglong A 10.0.0.2
+ long-dname DNAME longlonglonglonglonglonglonglonglonglonglonglonglong
++toolong-dname DNAME longlonglonglonglonglonglonglonglonglonglonglonglong
+ ;
+--- a/bin/tests/system/dname/tests.sh
++++ b/bin/tests/system/dname/tests.sh
+@@ -56,10 +56,19 @@ grep "status: YXDOMAIN" dig.out.ns2.tool
+ if [ $ret != 0 ]; then echo "I:failed"; fi
+ status=`expr $status + $ret`
+
+-echo "I:checking (too) long dname from recursive"
++echo "I:checking (too) long dname from recursive with cached DNAME"
++ret=0
++$DIG 01234567890123456789012345678901234567890123456789.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.long-dname.example @10.53.0.4 a -p 5300 > dig.out.ns4.cachedtoolong || ret=1
++grep "status: YXDOMAIN" dig.out.ns4.cachedtoolong > /dev/null || ret=1
++grep '^long-dname\.example\..*DNAME.*long' dig.out.ns4.cachedtoolong > /dev/null || ret=1
++if [ $ret != 0 ]; then echo "I:failed"; fi
++status=`expr $status + $ret`
++
++echo "I:checking (too) long dname from recursive without cached DNAME"
+ ret=0
+-$DIG 01234567890123456789012345678901234567890123456789.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.long-dname.example @10.53.0.4 a -p 5300 > dig.out.ns4.toolong || ret=1
+-grep "status: YXDOMAIN" dig.out.ns4.toolong > /dev/null || ret=1
++$DIG 01234567890123456789012345678901234567890123456789.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglonglong.longlonglonglonglonglonglonglonglonglonglonglonglonglong.toolong-dname.example @10.53.0.4 a -p 5300 > dig.out.ns4.uncachedtoolong || ret=1
++grep "status: YXDOMAIN" dig.out.ns4.uncachedtoolong > /dev/null || ret=1
++grep '^toolong-dname\.example\..*DNAME.*long' dig.out.ns4.uncachedtoolong > /dev/null || ret=1
+ if [ $ret != 0 ]; then echo "I:failed"; fi
+ status=`expr $status + $ret`
+
+--- a/bin/tests/system/rpz/tests.sh
++++ b/bin/tests/system/rpz/tests.sh
+@@ -383,7 +383,7 @@ nxdomain a0-1s-cname.tld2s +dnssec @$ns
+ drop a3-8.tld2 any @$ns6 # 20 drop
+
+ end_group
+-ckstatsrange $ns3 test1 ns3 22 25
++ckstatsrange $ns3 test1 ns3 22 27
+ ckstats $ns5 test1 ns5 0
+ ckstats $ns6 test1 ns6 0
+
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -4443,6 +4443,7 @@ is_lame(fetchctx_t *fctx) {
+ isc_result_t result;
+
+ if (message->rcode != dns_rcode_noerror &&
++ message->rcode != dns_rcode_yxdomain &&
+ message->rcode != dns_rcode_nxdomain)
+ return (ISC_FALSE);
+
+@@ -6061,79 +6062,6 @@ chase_additional(fetchctx_t *fctx) {
+ goto again;
+ }
+
+-static inline isc_result_t
+-cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) {
+- isc_result_t result;
+- dns_rdata_t rdata = DNS_RDATA_INIT;
+- dns_rdata_cname_t cname;
+-
+- result = dns_rdataset_first(rdataset);
+- if (result != ISC_R_SUCCESS)
+- return (result);
+- dns_rdataset_current(rdataset, &rdata);
+- result = dns_rdata_tostruct(&rdata, &cname, NULL);
+- if (result != ISC_R_SUCCESS)
+- return (result);
+- dns_name_init(tname, NULL);
+- dns_name_clone(&cname.cname, tname);
+- dns_rdata_freestruct(&cname);
+-
+- return (ISC_R_SUCCESS);
+-}
+-
+-/*%
+- * Construct the synthesised CNAME from the existing QNAME and
+- * the DNAME RR and store it in 'target'.
+- */
+-static inline isc_result_t
+-dname_target(dns_rdataset_t *rdataset, dns_name_t *qname,
+- unsigned int nlabels, dns_name_t *target)
+-{
+- isc_result_t result;
+- dns_rdata_t rdata = DNS_RDATA_INIT;
+- dns_rdata_dname_t dname;
+- dns_fixedname_t prefix;
+-
+- /*
+- * Get the target name of the DNAME.
+- */
+- result = dns_rdataset_first(rdataset);
+- if (result != ISC_R_SUCCESS)
+- return (result);
+- dns_rdataset_current(rdataset, &rdata);
+- result = dns_rdata_tostruct(&rdata, &dname, NULL);
+- if (result != ISC_R_SUCCESS)
+- return (result);
+-
+- dns_fixedname_init(&prefix);
+- dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL);
+- result = dns_name_concatenate(dns_fixedname_name(&prefix),
+- &dname.dname, target, NULL);
+- dns_rdata_freestruct(&dname);
+- return (result);
+-}
+-
+-/*%
+- * Check if it was possible to construct 'qname' from 'lastcname'
+- * and 'rdataset'.
+- */
+-static inline isc_result_t
+-fromdname(dns_rdataset_t *rdataset, dns_name_t *lastcname,
+- unsigned int nlabels, const dns_name_t *qname)
+-{
+- dns_fixedname_t fixed;
+- isc_result_t result;
+- dns_name_t *target;
+-
+- dns_fixedname_init(&fixed);
+- target = dns_fixedname_name(&fixed);
+- result = dname_target(rdataset, lastcname, nlabels, target);
+- if (result != ISC_R_SUCCESS || !dns_name_equal(qname, target))
+- return (ISC_R_NOTFOUND);
+-
+- return (ISC_R_SUCCESS);
+-}
+-
+ static isc_boolean_t
+ is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
+ dns_rdataset_t *rdataset)
+@@ -6209,9 +6137,8 @@ is_answeraddress_allowed(dns_view_t *vie
+ }
+
+ static isc_boolean_t
+-is_answertarget_allowed(dns_view_t *view, dns_name_t *name,
+- dns_rdatatype_t type, dns_name_t *tname,
+- dns_name_t *domain)
++is_answertarget_allowed(fetchctx_t *fctx, dns_name_t *qname, dns_name_t *rname,
++ dns_rdataset_t *rdataset)
+ {
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+@@ -6219,18 +6146,58 @@ is_answertarget_allowed(dns_view_t *view
+ char tnamebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[64];
+ char typebuf[64];
++ dns_name_t *tname = NULL;
++ dns_rdata_cname_t cname;
++ dns_rdata_dname_t dname;
++ dns_view_t *view = fctx->res->view;
++ dns_rdata_t rdata = DNS_RDATA_INIT;
++ unsigned int nlabels;
++ dns_fixedname_t fixed;
++ dns_name_t prefix;
++
++ REQUIRE(rdataset != NULL);
++ REQUIRE(rdataset->type == dns_rdatatype_cname ||
++ rdataset->type == dns_rdatatype_dname);
+
+ /* By default, we allow any target name. */
+ if (view->denyanswernames == NULL)
+ return (ISC_TRUE);
+
++ result = dns_rdataset_first(rdataset);
++ RUNTIME_CHECK(result == ISC_R_SUCCESS);
++ dns_rdataset_current(rdataset, &rdata);
++ switch (rdataset->type) {
++ case dns_rdatatype_cname:
++ result = dns_rdata_tostruct(&rdata, &cname, NULL);
++ RUNTIME_CHECK(result == ISC_R_SUCCESS);
++ tname = &cname.cname;
++ break;
++ case dns_rdatatype_dname:
++ result = dns_rdata_tostruct(&rdata, &dname, NULL);
++ RUNTIME_CHECK(result == ISC_R_SUCCESS);
++ dns_name_init(&prefix, NULL);
++ dns_fixedname_init(&fixed);
++ tname = dns_fixedname_name(&fixed);
++ nlabels = dns_name_countlabels(qname) -
++ dns_name_countlabels(rname);
++ dns_name_split(qname, nlabels, &prefix, NULL);
++ result = dns_name_concatenate(&prefix, &dname.dname, tname,
++ NULL);
++ if (result == ISC_R_NOSPACE)
++ return (ISC_TRUE);
++ RUNTIME_CHECK(result == ISC_R_SUCCESS);
++ break;
++ default:
++ INSIST(0);
++ }
++
+ /*
+ * If the owner name matches one in the exclusion list, either exactly
+ * or partially, allow it.
+ */
+ if (view->answernames_exclude != NULL) {
+- result = dns_rbt_findnode(view->answernames_exclude, name, NULL,
+- &node, NULL, 0, NULL, NULL);
++ result = dns_rbt_findnode(view->answernames_exclude, qname,
++ NULL, &node, NULL, 0, NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ return (ISC_TRUE);
+ }
+@@ -6238,7 +6205,7 @@ is_answertarget_allowed(dns_view_t *view
+ /*
+ * If the target name is a subdomain of the search domain, allow it.
+ */
+- if (dns_name_issubdomain(tname, domain))
++ if (dns_name_issubdomain(tname, &fctx->domain))
+ return (ISC_TRUE);
+
+ /*
+@@ -6247,9 +6214,9 @@ is_answertarget_allowed(dns_view_t *view
+ result = dns_rbt_findnode(view->denyanswernames, tname, NULL, &node,
+ NULL, 0, NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+- dns_name_format(name, qnamebuf, sizeof(qnamebuf));
++ dns_name_format(qname, qnamebuf, sizeof(qnamebuf));
+ dns_name_format(tname, tnamebuf, sizeof(tnamebuf));
+- dns_rdatatype_format(type, typebuf, sizeof(typebuf));
++ dns_rdatatype_format(rdataset->type, typebuf, sizeof(typebuf));
+ dns_rdataclass_format(view->rdclass, classbuf,
+ sizeof(classbuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+@@ -6745,459 +6712,297 @@ noanswer_response(fetchctx_t *fctx, dns_
+ return (ISC_R_SUCCESS);
+ }
+
++static isc_boolean_t
++validinanswer(dns_rdataset_t *rdataset, fetchctx_t *fctx) {
++ if (rdataset->type == dns_rdatatype_nsec3) {
++ /*
++ * NSEC3 records are not allowed to
++ * appear in the answer section.
++ */
++ log_formerr(fctx, "NSEC3 in answer");
++ return (ISC_FALSE);
++ }
++ if (rdataset->type == dns_rdatatype_tkey) {
++ /*
++ * TKEY is not a valid record in a
++ * response to any query we can make.
++ */
++ log_formerr(fctx, "TKEY in answer");
++ return (ISC_FALSE);
++ }
++ if (rdataset->rdclass != fctx->res->rdclass) {
++ log_formerr(fctx, "Mismatched class in answer");
++ return (ISC_FALSE);
++ }
++ return (ISC_TRUE);
++}
++
+ static isc_result_t
+ answer_response(fetchctx_t *fctx) {
+ isc_result_t result;
+- dns_message_t *message;
+- dns_name_t *name, *dname = NULL, *qname, tname, *ns_name;
+- dns_name_t *cname = NULL, *lastcname = NULL;
+- dns_rdataset_t *rdataset, *ns_rdataset;
+- isc_boolean_t done, external, aa, found, want_chaining;
+- isc_boolean_t have_answer, found_cname, found_dname, found_type;
+- isc_boolean_t wanted_chaining;
+- unsigned int aflag, chaining;
++ dns_message_t *message = NULL;
++ dns_name_t *name = NULL, *qname = NULL, *ns_name = NULL;
++ dns_name_t *aname = NULL, *cname = NULL, *dname = NULL;
++ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
++ dns_rdataset_t *ardataset = NULL, *crdataset = NULL;
++ dns_rdataset_t *drdataset = NULL, *ns_rdataset = NULL;
++ isc_boolean_t done = ISC_FALSE, aa;
++ unsigned int dname_labels, domain_labels;
++ isc_boolean_t chaining = ISC_FALSE;
+ dns_rdatatype_t type;
+- dns_fixedname_t fdname, fqname;
+- dns_view_t *view;
++ dns_view_t *view = NULL;
++ dns_trust_t trust;
++
++ REQUIRE(VALID_FCTX(fctx));
+
+ FCTXTRACE("answer_response");
+
+ message = fctx->rmessage;
++ qname = &fctx->name;
++ view = fctx->res->view;
++ type = fctx->type;
+
+ /*
+- * Examine the answer section, marking those rdatasets which are
+- * part of the answer and should be cached.
++ * There can be multiple RRSIG and SIG records at a name so
++ * we treat these types as a subset of ANY.
+ */
++ if (type == dns_rdatatype_rrsig || type == dns_rdatatype_sig) {
++ type = dns_rdatatype_any;
++ }
+
+- done = ISC_FALSE;
+- found_cname = ISC_FALSE;
+- found_dname = ISC_FALSE;
+- found_type = ISC_FALSE;
+- have_answer = ISC_FALSE;
+- want_chaining = ISC_FALSE;
+- chaining = 0;
+- POST(want_chaining);
+- if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
+- aa = ISC_TRUE;
+- else
+- aa = ISC_FALSE;
+- qname = &fctx->name;
+- type = fctx->type;
+- view = fctx->res->view;
+- result = dns_message_firstname(message, DNS_SECTION_ANSWER);
+- while (!done && result == ISC_R_SUCCESS) {
+- dns_namereln_t namereln, lastreln;
+- int order, lastorder;
+- unsigned int nlabels, lastnlabels;
++ /*
++ * Bigger than any valid DNAME label count.
++ */
++ dname_labels = dns_name_countlabels(qname);
++ domain_labels = dns_name_countlabels(&fctx->domain);
++
++ /*
++ * Perform a single pass looking for the answer, cname or covering
++ * dname.
++ */
++ for (result = dns_message_firstname(message, DNS_SECTION_ANSWER);
++ result == ISC_R_SUCCESS;
++ result = dns_message_nextname(message, DNS_SECTION_ANSWER))
++ {
++ int order;
++ unsigned int nlabels;
++ dns_namereln_t namereln;
+
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
+- external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
+ namereln = dns_name_fullcompare(qname, name, &order, &nlabels);
+-
+- if (namereln == dns_namereln_equal) {
+- wanted_chaining = ISC_FALSE;
++ switch (namereln) {
++ case dns_namereln_equal:
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+- rdataset = ISC_LIST_NEXT(rdataset, link)) {
+- found = ISC_FALSE;
+- want_chaining = ISC_FALSE;
+- aflag = 0;
+- if (rdataset->type == dns_rdatatype_nsec3) {
+- /*
+- * NSEC3 records are not allowed to
+- * appear in the answer section.
+- */
+- log_formerr(fctx, "NSEC3 in answer");
+- return (DNS_R_FORMERR);
+- }
+- if (rdataset->type == dns_rdatatype_tkey) {
+- /*
+- * TKEY is not a valid record in a
+- * response to any query we can make.
+- */
+- log_formerr(fctx, "TKEY in answer");
+- return (DNS_R_FORMERR);
+- }
+- if (rdataset->rdclass != fctx->res->rdclass) {
+- log_formerr(fctx, "Mismatched class "
+- "in answer");
+- return (DNS_R_FORMERR);
+- }
+-
+- /*
+- * Apply filters, if given, on answers to reject
+- * a malicious attempt of rebinding.
+- */
+- if ((rdataset->type == dns_rdatatype_a ||
+- rdataset->type == dns_rdatatype_aaaa) &&
+- !is_answeraddress_allowed(view, name,
+- rdataset)) {
+- return (DNS_R_SERVFAIL);
+- }
+-
+- if (rdataset->type == type && !found_cname) {
+- /*
+- * We've found an ordinary answer.
+- */
+- found = ISC_TRUE;
+- found_type = ISC_TRUE;
+- done = ISC_TRUE;
+- aflag = DNS_RDATASETATTR_ANSWER;
+- } else if (type == dns_rdatatype_any) {
+- /*
+- * We've found an answer matching
+- * an ANY query. There may be
+- * more.
+- */
+- found = ISC_TRUE;
+- aflag = DNS_RDATASETATTR_ANSWER;
+- } else if (rdataset->type == dns_rdatatype_rrsig
+- && rdataset->covers == type
+- && !found_cname) {
+- /*
+- * We've found a signature that
+- * covers the type we're looking for.
+- */
+- found = ISC_TRUE;
+- found_type = ISC_TRUE;
+- aflag = DNS_RDATASETATTR_ANSWERSIG;
+- } else if (rdataset->type ==
+- dns_rdatatype_cname
+- && !found_type) {
+- /*
+- * We're looking for something else,
+- * but we found a CNAME.
+- *
+- * Getting a CNAME response for some
+- * query types is an error, see
+- * RFC 4035, Section 2.5.
+- */
+- if (type == dns_rdatatype_rrsig ||
+- type == dns_rdatatype_key ||
+- type == dns_rdatatype_nsec) {
+- char buf[DNS_RDATATYPE_FORMATSIZE];
+- dns_rdatatype_format(fctx->type,
+- buf, sizeof(buf));
+- log_formerr(fctx,
+- "CNAME response "
+- "for %s RR", buf);
+- return (DNS_R_FORMERR);
+- }
+- found = ISC_TRUE;
+- found_cname = ISC_TRUE;
+- want_chaining = ISC_TRUE;
+- aflag = DNS_RDATASETATTR_ANSWER;
+- result = cname_target(rdataset,
+- &tname);
+- if (result != ISC_R_SUCCESS)
+- return (result);
+- /* Apply filters on the target name. */
+- if (!is_answertarget_allowed(view,
+- name,
+- rdataset->type,
+- &tname,
+- &fctx->domain)) {
+- return (DNS_R_SERVFAIL);
++ rdataset = ISC_LIST_NEXT(rdataset, link))
++ {
++ if (rdataset->type == type ||
++ type == dns_rdatatype_any)
++ {
++ aname = name;
++ if (type != dns_rdatatype_any) {
++ ardataset = rdataset;
+ }
+- lastcname = name;
+- } else if (rdataset->type == dns_rdatatype_rrsig
+- && rdataset->covers ==
+- dns_rdatatype_cname
+- && !found_type) {
+- /*
+- * We're looking for something else,
+- * but we found a SIG CNAME.
+- */
+- found = ISC_TRUE;
+- found_cname = ISC_TRUE;
+- aflag = DNS_RDATASETATTR_ANSWERSIG;
++ break;
+ }
+-
+- if (found) {
+- /*
+- * We've found an answer to our
+- * question.
+- */
+- name->attributes |=
+- DNS_NAMEATTR_CACHE;
+- rdataset->attributes |=
+- DNS_RDATASETATTR_CACHE;
+- rdataset->trust = dns_trust_answer;
+- if (external) {
+- /*
+- * This data is outside of
+- * our query domain, and
+- * may not be cached.
+- */
+- rdataset->attributes |=
+- DNS_RDATASETATTR_EXTERNAL;
+- } else if (chaining == 0) {
+- /*
+- * Don't use found_cname here
+- * as we have just set it
+- * above.
+- */
+- if (cname == NULL &&
+- !found_dname &&
+- aflag ==
+- DNS_RDATASETATTR_ANSWER)
+- {
+- have_answer = ISC_TRUE;
+- if (found_cname &&
+- cname == NULL)
+- cname = name;
+- name->attributes |=
+- DNS_NAMEATTR_ANSWER;
+- }
+- rdataset->attributes |= aflag;
+- if (aa)
+- rdataset->trust =
+- dns_trust_authanswer;
+- }
+-
+- /*
+- * Mark any additional data related
+- * to this rdataset.
+- */
+- (void)dns_rdataset_additionaldata(
+- rdataset,
+- check_related,
+- fctx);
+-
+- /*
+- * CNAME chaining.
+- */
+- if (want_chaining) {
+- wanted_chaining = ISC_TRUE;
+- name->attributes |=
+- DNS_NAMEATTR_CHAINING;
+- rdataset->attributes |=
+- DNS_RDATASETATTR_CHAINING;
+- qname = &tname;
+- }
++ if (rdataset->type == dns_rdatatype_cname) {
++ cname = name;
++ crdataset = rdataset;
++ break;
+ }
+- /*
+- * We could add an "else" clause here and
+- * log that we're ignoring this rdataset.
+- */
+ }
++ break;
++
++ case dns_namereln_subdomain:
+ /*
+- * If wanted_chaining is true, we've done
+- * some chaining as the result of processing
+- * this node, and thus we need to set
+- * chaining to true.
+- *
+- * We don't set chaining inside of the
+- * rdataset loop because doing that would
+- * cause us to ignore the signatures of
+- * CNAMEs.
++ * In-scope DNAME records must have at least
++ * as many labels as the domain being queried.
++ * They also must be less that qname's labels
++ * and any previously found dname.
+ */
+- if (wanted_chaining && chaining < 2U)
+- chaining++;
+- } else {
+- dns_rdataset_t *dnameset = NULL;
+- isc_boolean_t synthcname = ISC_FALSE;
+-
+- if (lastcname != NULL) {
+- lastreln = dns_name_fullcompare(lastcname,
+- name,
+- &lastorder,
+- &lastnlabels);
+- if (lastreln == dns_namereln_subdomain &&
+- lastnlabels == dns_name_countlabels(name))
+- synthcname = ISC_TRUE;
++ if (nlabels >= dname_labels || nlabels < domain_labels)
++ {
++ continue;
+ }
+
+ /*
+- * Look for a DNAME (or its SIG). Anything else is
+- * ignored.
++ * We are looking for the shortest DNAME if there
++ * are multiple ones (which there shouldn't be).
+ */
+- wanted_chaining = ISC_FALSE;
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ {
+- if (rdataset->rdclass != fctx->res->rdclass) {
+- log_formerr(fctx, "Mismatched class "
+- "in answer");
+- return (DNS_R_FORMERR);
+- }
+-
+- /*
+- * Only pass DNAME or RRSIG(DNAME).
+- */
+- if (rdataset->type != dns_rdatatype_dname &&
+- (rdataset->type != dns_rdatatype_rrsig ||
+- rdataset->covers != dns_rdatatype_dname))
++ if (rdataset->type != dns_rdatatype_dname) {
+ continue;
+-
+- /*
+- * If we're not chaining, then the DNAME and
+- * its signature should not be external.
+- */
+- if (chaining == 0 && external) {
+- char qbuf[DNS_NAME_FORMATSIZE];
+- char obuf[DNS_NAME_FORMATSIZE];
+-
+- dns_name_format(name, qbuf,
+- sizeof(qbuf));
+- dns_name_format(&fctx->domain, obuf,
+- sizeof(obuf));
+- log_formerr(fctx, "external DNAME or "
+- "RRSIG covering DNAME "
+- "in answer: %s is "
+- "not in %s", qbuf, obuf);
+- return (DNS_R_FORMERR);
+ }
++ dname = name;
++ drdataset = rdataset;
++ dname_labels = nlabels;
++ break;
++ }
++ break;
++ default:
++ break;
++ }
++ }
+
+- /*
+- * If DNAME + synthetic CNAME then the
+- * namereln is dns_namereln_subdomain.
+- */
+- if (namereln != dns_namereln_subdomain &&
+- !synthcname)
+- {
+- char qbuf[DNS_NAME_FORMATSIZE];
+- char obuf[DNS_NAME_FORMATSIZE];
+-
+- dns_name_format(qname, qbuf,
+- sizeof(qbuf));
+- dns_name_format(name, obuf,
+- sizeof(obuf));
+- log_formerr(fctx, "unrelated DNAME "
+- "in answer: %s is "
+- "not in %s", qbuf, obuf);
+- return (DNS_R_FORMERR);
+- }
+-
+- aflag = 0;
+- if (rdataset->type == dns_rdatatype_dname) {
+- want_chaining = ISC_TRUE;
+- POST(want_chaining);
+- aflag = DNS_RDATASETATTR_ANSWER;
+- dns_fixedname_init(&fdname);
+- dname = dns_fixedname_name(&fdname);
+- if (synthcname) {
+- result = fromdname(rdataset,
+- lastcname,
+- lastnlabels,
+- qname);
+- } else {
+- result = dname_target(rdataset,
+- qname,
+- nlabels,
+- dname);
+- }
+- if (result == ISC_R_NOSPACE) {
+- /*
+- * We can't construct the
+- * DNAME target. Do not
+- * try to continue.
+- */
+- want_chaining = ISC_FALSE;
+- POST(want_chaining);
+- } else if (result != ISC_R_SUCCESS)
+- return (result);
+- else
+- dnameset = rdataset;
++ if (dname != NULL) {
++ aname = NULL;
++ ardataset = NULL;
++ cname = NULL;
++ crdataset = NULL;
++ } else if (aname != NULL) {
++ cname = NULL;
++ crdataset = NULL;
++ }
+
+- if (!synthcname &&
+- !is_answertarget_allowed(view,
+- qname, rdataset->type,
+- dname, &fctx->domain))
+- {
+- return (DNS_R_SERVFAIL);
+- }
+- } else {
+- /*
+- * We've found a signature that
+- * covers the DNAME.
+- */
+- aflag = DNS_RDATASETATTR_ANSWERSIG;
+- }
++ aa = ISC_TF((message->flags & DNS_MESSAGEFLAG_AA) != 0);
++ trust = aa ? dns_trust_authanswer : dns_trust_answer;
+
+- /*
+- * We've found an answer to our
+- * question.
+- */
+- name->attributes |= DNS_NAMEATTR_CACHE;
+- rdataset->attributes |= DNS_RDATASETATTR_CACHE;
+- rdataset->trust = dns_trust_answer;
+- /*
+- * If we are not chaining or the first CNAME
+- * is a synthesised CNAME before the DNAME.
+- */
+- if (external) {
+- rdataset->attributes |=
+- DNS_RDATASETATTR_EXTERNAL;
+- } else if ((chaining == 0) ||
+- (chaining == 1U && synthcname))
+- {
+- if (aflag == DNS_RDATASETATTR_ANSWER) {
+- have_answer = ISC_TRUE;
+- found_dname = ISC_TRUE;
+- if (cname != NULL &&
+- synthcname)
+- {
+- cname->attributes &=
+- ~DNS_NAMEATTR_ANSWER;
+- }
+- name->attributes |=
+- DNS_NAMEATTR_ANSWER;
+- }
+- rdataset->attributes |= aflag;
+- if (aa)
+- rdataset->trust =
+- dns_trust_authanswer;
+- }
++ if (aname != NULL && type == dns_rdatatype_any) {
++ for (rdataset = ISC_LIST_HEAD(aname->list);
++ rdataset != NULL;
++ rdataset = ISC_LIST_NEXT(rdataset, link))
++ {
++ if (!validinanswer(rdataset, fctx)) {
++ return (DNS_R_FORMERR);
+ }
+-
+- /*
+- * DNAME chaining.
+- */
+- if (dnameset != NULL) {
+- if (!synthcname) {
+- /*
+- * Copy the dname into the qname fixed
+- * name.
+- *
+- * Although we check for failure of the
+- * copy operation, in practice it
+- * should never fail since we already
+- * know that the result fits in a
+- * fixedname.
+- */
+- dns_fixedname_init(&fqname);
+- qname = dns_fixedname_name(&fqname);
+- result = dns_name_copy(dname, qname,
+- NULL);
+- if (result != ISC_R_SUCCESS)
+- return (result);
+- }
+- wanted_chaining = ISC_TRUE;
+- name->attributes |= DNS_NAMEATTR_CHAINING;
+- dnameset->attributes |=
+- DNS_RDATASETATTR_CHAINING;
++ if ((fctx->type == dns_rdatatype_sig ||
++ fctx->type == dns_rdatatype_rrsig) &&
++ rdataset->type != fctx->type)
++ {
++ continue;
+ }
+- /*
+- * Ensure that we can't ever get chaining == 1
+- * above if we have processed a DNAME.
+- */
+- if (wanted_chaining && chaining < 2U)
+- chaining += 2;
++ if ((rdataset->type == dns_rdatatype_a ||
++ rdataset->type == dns_rdatatype_aaaa) &&
++ !is_answeraddress_allowed(view, aname, rdataset))
++ {
++ return (DNS_R_SERVFAIL);
++ }
++ if ((rdataset->type == dns_rdatatype_cname ||
++ rdataset->type == dns_rdatatype_dname) &&
++ !is_answertarget_allowed(fctx, qname, aname,
++ rdataset))
++ {
++ return (DNS_R_SERVFAIL);
++ }
++ aname->attributes |= DNS_NAMEATTR_CACHE;
++ aname->attributes |= DNS_NAMEATTR_ANSWER;
++ rdataset->attributes |= DNS_RDATASETATTR_ANSWER;
++ rdataset->attributes |= DNS_RDATASETATTR_CACHE;
++ rdataset->trust = trust;
++ (void)dns_rdataset_additionaldata(rdataset,
++ check_related,
++ fctx);
+ }
+- result = dns_message_nextname(message, DNS_SECTION_ANSWER);
+- }
+- if (result == ISC_R_NOMORE)
+- result = ISC_R_SUCCESS;
+- if (result != ISC_R_SUCCESS)
+- return (result);
+-
+- /*
+- * We should have found an answer.
+- */
+- if (!have_answer) {
++ } else if (aname != NULL) {
++ if (!validinanswer(ardataset, fctx))
++ return (DNS_R_FORMERR);
++ if ((ardataset->type == dns_rdatatype_a ||
++ ardataset->type == dns_rdatatype_aaaa) &&
++ !is_answeraddress_allowed(view, aname, ardataset)) {
++ return (DNS_R_SERVFAIL);
++ }
++ if ((ardataset->type == dns_rdatatype_cname ||
++ ardataset->type == dns_rdatatype_dname) &&
++ !is_answertarget_allowed(fctx, qname, aname, ardataset)) {
++ return (DNS_R_SERVFAIL);
++ }
++ aname->attributes |= DNS_NAMEATTR_CACHE;
++ aname->attributes |= DNS_NAMEATTR_ANSWER;
++ ardataset->attributes |= DNS_RDATASETATTR_ANSWER;
++ ardataset->attributes |= DNS_RDATASETATTR_CACHE;
++ ardataset->trust = trust;
++ (void)dns_rdataset_additionaldata(ardataset, check_related,
++ fctx);
++ for (sigrdataset = ISC_LIST_HEAD(aname->list);
++ sigrdataset != NULL;
++ sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
++ if (!validinanswer(sigrdataset, fctx))
++ return (DNS_R_FORMERR);
++ if (sigrdataset->type != dns_rdatatype_rrsig ||
++ sigrdataset->covers != type)
++ continue;
++ sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG;
++ sigrdataset->attributes |= DNS_RDATASETATTR_CACHE;
++ sigrdataset->trust = trust;
++ break;
++ }
++ } else if (cname != NULL) {
++ if (!validinanswer(crdataset, fctx)) {
++ return (DNS_R_FORMERR);
++ }
++ if (type == dns_rdatatype_rrsig || type == dns_rdatatype_key ||
++ type == dns_rdatatype_nsec)
++ {
++ char buf[DNS_RDATATYPE_FORMATSIZE];
++ dns_rdatatype_format(type, buf, sizeof(buf));
++ log_formerr(fctx, "CNAME response for %s RR", buf);
++ return (DNS_R_FORMERR);
++ }
++ if (!is_answertarget_allowed(fctx, qname, cname, crdataset)) {
++ return (DNS_R_SERVFAIL);
++ }
++ cname->attributes |= DNS_NAMEATTR_CACHE;
++ cname->attributes |= DNS_NAMEATTR_ANSWER;
++ cname->attributes |= DNS_NAMEATTR_CHAINING;
++ crdataset->attributes |= DNS_RDATASETATTR_ANSWER;
++ crdataset->attributes |= DNS_RDATASETATTR_CACHE;
++ crdataset->attributes |= DNS_RDATASETATTR_CHAINING;
++ crdataset->trust = trust;
++ for (sigrdataset = ISC_LIST_HEAD(cname->list);
++ sigrdataset != NULL;
++ sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
++ {
++ if (!validinanswer(sigrdataset, fctx)) {
++ return (DNS_R_FORMERR);
++ }
++ if (sigrdataset->type != dns_rdatatype_rrsig ||
++ sigrdataset->covers != dns_rdatatype_cname)
++ {
++ continue;
++ }
++ sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG;
++ sigrdataset->attributes |= DNS_RDATASETATTR_CACHE;
++ sigrdataset->trust = trust;
++ break;
++ }
++ chaining = ISC_TRUE;
++ } else if (dname != NULL) {
++ if (!validinanswer(drdataset, fctx)) {
++ return (DNS_R_FORMERR);
++ }
++ if (!is_answertarget_allowed(fctx, qname, dname, drdataset)) {
++ return (DNS_R_SERVFAIL);
++ }
++ dname->attributes |= DNS_NAMEATTR_CACHE;
++ dname->attributes |= DNS_NAMEATTR_ANSWER;
++ dname->attributes |= DNS_NAMEATTR_CHAINING;
++ drdataset->attributes |= DNS_RDATASETATTR_ANSWER;
++ drdataset->attributes |= DNS_RDATASETATTR_CACHE;
++ drdataset->attributes |= DNS_RDATASETATTR_CHAINING;
++ drdataset->trust = trust;
++ for (sigrdataset = ISC_LIST_HEAD(dname->list);
++ sigrdataset != NULL;
++ sigrdataset = ISC_LIST_NEXT(sigrdataset, link))
++ {
++ if (!validinanswer(sigrdataset, fctx)) {
++ return (DNS_R_FORMERR);
++ }
++ if (sigrdataset->type != dns_rdatatype_rrsig ||
++ sigrdataset->covers != dns_rdatatype_dname)
++ {
++ continue;
++ }
++ sigrdataset->attributes |= DNS_RDATASETATTR_ANSWERSIG;
++ sigrdataset->attributes |= DNS_RDATASETATTR_CACHE;
++ sigrdataset->trust = trust;
++ break;
++ }
++ chaining = ISC_TRUE;
++ } else {
+ log_formerr(fctx, "reply has no answer");
+ return (DNS_R_FORMERR);
+ }
+@@ -7210,7 +7015,7 @@ answer_response(fetchctx_t *fctx) {
+ /*
+ * Did chaining end before we got the final answer?
+ */
+- if (chaining != 0) {
++ if (chaining) {
+ /*
+ * Yes. This may be a negative reply, so hand off
+ * authority section processing to the noanswer code.
+@@ -7236,11 +7041,9 @@ answer_response(fetchctx_t *fctx) {
+ * We expect there to be only one owner name for all the rdatasets
+ * in this section, and we expect that it is not external.
+ */
+- done = ISC_FALSE;
+- ns_name = NULL;
+- ns_rdataset = NULL;
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+ while (!done && result == ISC_R_SUCCESS) {
++ isc_boolean_t external;
+ name = NULL;
+ dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
+ external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
+@@ -7259,12 +7062,13 @@ answer_response(fetchctx_t *fctx) {
+ DNS_NAMEATTR_CACHE;
+ rdataset->attributes |=
+ DNS_RDATASETATTR_CACHE;
+- if (aa && chaining == 0)
++ if (aa && !chaining) {
+ rdataset->trust =
+ dns_trust_authauthority;
+- else
++ } else {
+ rdataset->trust =
+ dns_trust_additional;
++ }
+
+ if (rdataset->type == dns_rdatatype_ns)
+ {
+@@ -8064,6 +7868,7 @@ resquery_response(isc_task_t *task, isc_
+ * Is the remote server broken, or does it dislike us?
+ */
+ if (message->rcode != dns_rcode_noerror &&
++ message->rcode != dns_rcode_yxdomain &&
+ message->rcode != dns_rcode_nxdomain) {
+ isc_buffer_t b;
+ char code[64];
+@@ -8128,13 +7933,6 @@ resquery_response(isc_task_t *task, isc_
+ log_formerr(fctx, "server sent FORMERR");
+ result = DNS_R_FORMERR;
+ }
+- } else if (message->rcode == dns_rcode_yxdomain) {
+- /*
+- * DNAME mapping failed because the new name
+- * was too long. There's no chance of success
+- * for this fetch.
+- */
+- result = DNS_R_YXDOMAIN;
+ } else if (message->rcode == dns_rcode_badvers) {
+ unsigned int flags, mask;
+ unsigned int version;
+@@ -8293,6 +8091,7 @@ resquery_response(isc_task_t *task, isc_
+ */
+ if (message->counts[DNS_SECTION_ANSWER] > 0 &&
+ (message->rcode == dns_rcode_noerror ||
++ message->rcode == dns_rcode_yxdomain ||
+ message->rcode == dns_rcode_nxdomain)) {
+ /*
+ * [normal case]
diff -Nru bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-3.patch bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-3.patch
--- bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-3.patch 1970-01-01 01:00:00.000000000 +0100
+++ bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3137-3.patch 2017-05-07 15:22:46.000000000 +0200
@@ -0,0 +1,120 @@
+From 7ab9e8e00775782d474522a5b2bffba8daefefa5 Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Tue, 14 Mar 2017 15:07:00 +1100
+Subject: [PATCH] 4580. [bug] 4578 introduced a regression when
+ handling CNAME to referral below the current domain.
+ [RT #44850]
+
+(cherry picked from commit 638c7c635ddab0b717a675f49b1180dbf8ef803e)
+---
+--- a/lib/dns/resolver.c
++++ b/lib/dns/resolver.c
+@@ -6138,7 +6138,7 @@ is_answeraddress_allowed(dns_view_t *vie
+
+ static isc_boolean_t
+ is_answertarget_allowed(fetchctx_t *fctx, dns_name_t *qname, dns_name_t *rname,
+- dns_rdataset_t *rdataset)
++ dns_rdataset_t *rdataset, isc_boolean_t *chainingp)
+ {
+ isc_result_t result;
+ dns_rbtnode_t *node = NULL;
+@@ -6159,8 +6159,11 @@ is_answertarget_allowed(fetchctx_t *fctx
+ REQUIRE(rdataset->type == dns_rdatatype_cname ||
+ rdataset->type == dns_rdatatype_dname);
+
+- /* By default, we allow any target name. */
+- if (view->denyanswernames == NULL)
++ /*
++ * By default, we allow any target name.
++ * If newqname != NULL we also need to extract the newqname.
++ */
++ if (chainingp == NULL && view->denyanswernames == NULL)
+ return (ISC_TRUE);
+
+ result = dns_rdataset_first(rdataset);
+@@ -6183,7 +6186,7 @@ is_answertarget_allowed(fetchctx_t *fctx
+ dns_name_split(qname, nlabels, &prefix, NULL);
+ result = dns_name_concatenate(&prefix, &dname.dname, tname,
+ NULL);
+- if (result == ISC_R_NOSPACE)
++ if (result == DNS_R_NAMETOOLONG)
+ return (ISC_TRUE);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ break;
+@@ -6191,6 +6194,12 @@ is_answertarget_allowed(fetchctx_t *fctx
+ INSIST(0);
+ }
+
++ if (chainingp != NULL)
++ *chainingp = ISC_TRUE;
++
++ if (view->denyanswernames == NULL)
++ return (ISC_TRUE);
++
+ /*
+ * If the owner name matches one in the exclusion list, either exactly
+ * or partially, allow it.
+@@ -6884,7 +6893,7 @@ answer_response(fetchctx_t *fctx) {
+ if ((rdataset->type == dns_rdatatype_cname ||
+ rdataset->type == dns_rdatatype_dname) &&
+ !is_answertarget_allowed(fctx, qname, aname,
+- rdataset))
++ rdataset, NULL))
+ {
+ return (DNS_R_SERVFAIL);
+ }
+@@ -6907,7 +6916,9 @@ answer_response(fetchctx_t *fctx) {
+ }
+ if ((ardataset->type == dns_rdatatype_cname ||
+ ardataset->type == dns_rdatatype_dname) &&
+- !is_answertarget_allowed(fctx, qname, aname, ardataset)) {
++ !is_answertarget_allowed(fctx, qname, aname, ardataset,
++ NULL))
++ {
+ return (DNS_R_SERVFAIL);
+ }
+ aname->attributes |= DNS_NAMEATTR_CACHE;
+@@ -6942,7 +6953,9 @@ answer_response(fetchctx_t *fctx) {
+ log_formerr(fctx, "CNAME response for %s RR", buf);
+ return (DNS_R_FORMERR);
+ }
+- if (!is_answertarget_allowed(fctx, qname, cname, crdataset)) {
++ if (!is_answertarget_allowed(fctx, qname, cname, crdataset,
++ NULL))
++ {
+ return (DNS_R_SERVFAIL);
+ }
+ cname->attributes |= DNS_NAMEATTR_CACHE;
+@@ -6974,7 +6987,8 @@ answer_response(fetchctx_t *fctx) {
+ if (!validinanswer(drdataset, fctx)) {
+ return (DNS_R_FORMERR);
+ }
+- if (!is_answertarget_allowed(fctx, qname, dname, drdataset)) {
++ if (!is_answertarget_allowed(fctx, qname, dname, drdataset,
++ &chaining)) {
+ return (DNS_R_SERVFAIL);
+ }
+ dname->attributes |= DNS_NAMEATTR_CACHE;
+@@ -7001,7 +7015,6 @@ answer_response(fetchctx_t *fctx) {
+ sigrdataset->trust = trust;
+ break;
+ }
+- chaining = ISC_TRUE;
+ } else {
+ log_formerr(fctx, "reply has no answer");
+ return (DNS_R_FORMERR);
+@@ -7016,13 +7029,7 @@ answer_response(fetchctx_t *fctx) {
+ * Did chaining end before we got the final answer?
+ */
+ if (chaining) {
+- /*
+- * Yes. This may be a negative reply, so hand off
+- * authority section processing to the noanswer code.
+- * If it isn't a noanswer response, no harm will be
+- * done.
+- */
+- return (noanswer_response(fctx, qname, 0));
++ return (ISC_R_SUCCESS);
+ }
+
+ /*
diff -Nru bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3138.patch bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3138.patch
--- bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3138.patch 1970-01-01 01:00:00.000000000 +0100
+++ bind9-9.10.3.dfsg.P4/debian/patches/CVE-2017-3138.patch 2017-05-07 15:22:46.000000000 +0200
@@ -0,0 +1,51 @@
+From a636604b20cc0aaabc8edbb7595f7c1c820b7610 Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@isc.org>
+Date: Sat, 25 Mar 2017 02:00:17 +1100
+Subject: [PATCH] 4582. [security] 'rndc ""' could trigger a assertion
+ failure in named. (CVE-2017-3138) [RT #44924]
+
+(cherry picked from commit 8e8dfc5941e2375f2f8dadf3706258dd0db5f2e6)
+---
+
+--- a/bin/tests/system/rndc/tests.sh
++++ b/bin/tests/system/rndc/tests.sh
+@@ -386,5 +386,13 @@ sleep 1
+ if [ $ret != 0 ]; then echo "I:failed"; fi
+ status=`expr $status + $ret`
+
++n=`expr $n + 1`
++echo "I:check 'rndc \"\"' is handled ($n)"
++ret=0
++$RNDCCMD "" > rndc.out.test$n 2>&1 && ret=1
++grep "rndc: '' failed: failure" rndc.out.test$n > /dev/null
++if [ $ret != 0 ]; then echo "I:failed"; fi
++status=`expr $status + $ret`
++
+ echo "I:exit status: $status"
+ exit $status
+--- a/lib/isc/include/isc/lex.h
++++ b/lib/isc/include/isc/lex.h
+@@ -152,8 +152,6 @@ isc_lex_create(isc_mem_t *mctx, size_t m
+ * Requires:
+ *\li '*lexp' is a valid lexer.
+ *
+- *\li max_token > 0.
+- *
+ * Ensures:
+ *\li On success, *lexp is attached to the newly created lexer.
+ *
+--- a/lib/isc/lex.c
++++ b/lib/isc/lex.c
+@@ -94,9 +94,10 @@ isc_lex_create(isc_mem_t *mctx, size_t m
+ /*
+ * Create a lexer.
+ */
+-
+ REQUIRE(lexp != NULL && *lexp == NULL);
+- REQUIRE(max_token > 0U);
++
++ if (max_token == 0U)
++ max_token = 1;
+
+ lex = isc_mem_get(mctx, sizeof(*lex));
+ if (lex == NULL)
diff -Nru bind9-9.10.3.dfsg.P4/debian/patches/series bind9-9.10.3.dfsg.P4/debian/patches/series
--- bind9-9.10.3.dfsg.P4/debian/patches/series 2017-02-19 23:39:32.000000000 +0100
+++ bind9-9.10.3.dfsg.P4/debian/patches/series 2017-05-07 15:22:46.000000000 +0200
@@ -22,3 +22,11 @@
CVE-2016-8864-regression2.patch
CVE-2017-3135.patch
+
+CVE-2017-3136.patch
+
+CVE-2017-3137-1.patch
+CVE-2017-3137-2.patch
+CVE-2017-3137-3.patch
+
+CVE-2017-3138.patch
Reply to: