[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: