Control: tags 860224 + patch
Control: tags 860224 + pending
Control: tags 860225 + pending
Control: tags 860226 + patch
Control: tags 860226 + pending

Dear maintainer,

I've prepared an NMU for bind9 (versioned as 1:9.10.3.dfsg.P4-12.3) and
uploaded it to DELAYED/5. Please feel free to tell me if I
should delay it longer.

Please note, I could not test bind9 under real conditions with those
patches. The testsuite passed though the dname tests.

I'm cc'ing as well Mike and Florian for possible review.

Furthermore the version for jessie ist still not yet done.

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 <car...@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 <ma...@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 <e...@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 <ma...@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 <ma...@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 <ma...@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 via email to