From 8259daad7242ab2af8731681177ef7e948a15ece Mon Sep 17 00:00:00 2001
From: Mark Andrews <marka@isc.org>
Date: Mon, 16 Nov 2015 13:12:20 +1100
Subject: [PATCH] 4260.   [security]      Insufficient testing when parsing a
 message allowed                         records with an incorrect class to be
 be accepted,                         triggering a REQUIRE failure when those
 records                         were subsequently cached. (CVE-2015-8000) [RT
 #4098]

(cherry picked from commit c8821d124c532e0a65752b378f924d4259499fd3)
(cherry picked from commit 3a4c24c4a52d4a2d21d2decbde3d4e514e27d51c)


Upstream-Status: Backport

https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=8259daad7242ab2af8731681177ef7e948a15ece

CVE: CVE-2015-8000

Signed-off-by: Armin Kuster <akuster@mvista.com>

---
 CHANGES                       |  5 +++++
 bin/tests/system/start.pl     |  5 ++++-
 doc/arm/notes.xml             |  9 +++++++++
 lib/dns/include/dns/message.h | 13 +++++++++++--
 lib/dns/message.c             | 45 ++++++++++++++++++++++++++++++++++++++-----
 lib/dns/resolver.c            |  9 +++++++++
 lib/dns/xfrin.c               |  2 ++
 7 files changed, 80 insertions(+), 8 deletions(-)

Index: bind-9.10.2-P4/bin/tests/system/start.pl
===================================================================
--- bind-9.10.2-P4.orig/bin/tests/system/start.pl
+++ bind-9.10.2-P4/bin/tests/system/start.pl
@@ -68,6 +68,7 @@ my $NAMED = $ENV{'NAMED'};
 my $LWRESD = $ENV{'LWRESD'};
 my $DIG = $ENV{'DIG'};
 my $PERL = $ENV{'PERL'};
+my $PYTHON = $ENV{'PYTHON'};
 
 # Start the server(s)
 
@@ -213,7 +214,9 @@ sub start_server {
 		$pid_file = "lwresd.pid";
 	} elsif ($server =~ /^ans/) {
 		$cleanup_files = "{ans.run}";
-                if (-e "$testdir/$server/ans.pl") {
+                if (-e "$testdir/$server/ans.py") {
+                        $command = "$PYTHON ans.py 10.53.0.$' 5300";
+                } elsif (-e "$testdir/$server/ans.pl") {
                         $command = "$PERL ans.pl";
                 } else {
                         $command = "$PERL $topdir/ans.pl 10.53.0.$'";
Index: bind-9.10.2-P4/doc/arm/notes.xml
===================================================================
--- bind-9.10.2-P4.orig/doc/arm/notes.xml
+++ bind-9.10.2-P4/doc/arm/notes.xml
@@ -62,6 +62,15 @@
     <itemizedlist>
       <listitem>
 	<para>
+	  Insufficient testing when parsing a message allowed
+	  records with an incorrect class to be be accepted,
+	  triggering a REQUIRE failure when those records
+	  were subsequently cached.  This flaw is disclosed
+	  in CVE-2015-8000. [RT #4098]
+	</para>
+      </listitem>
+      <listitem>
+	<para>
 	  An incorrect boundary check in the OPENPGPKEY rdatatype
 	  could trigger an assertion failure. This flaw is disclosed
 	  in CVE-2015-5986. [RT #40286]
Index: bind-9.10.2-P4/lib/dns/include/dns/message.h
===================================================================
--- bind-9.10.2-P4.orig/lib/dns/include/dns/message.h
+++ bind-9.10.2-P4/lib/dns/include/dns/message.h
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 #ifndef DNS_MESSAGE_H
 #define DNS_MESSAGE_H 1
 
@@ -221,6 +219,8 @@ struct dns_message {
 	unsigned int			free_saved : 1;
 	unsigned int			sitok : 1;
 	unsigned int			sitbad : 1;
+	unsigned int			tkey : 1;
+	unsigned int			rdclass_set : 1;
 
 	unsigned int			opt_reserved;
 	unsigned int			sig_reserved;
@@ -1400,6 +1400,15 @@ dns_message_buildopt(dns_message_t *msg,
  * \li	 other.
  */
 
+void
+dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass);
+/*%<
+ * Set the expected class of records in the response.
+ *
+ * Requires:
+ * \li   msg be a valid message with parsing intent.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_MESSAGE_H */
Index: bind-9.10.2-P4/lib/dns/message.c
===================================================================
--- bind-9.10.2-P4.orig/lib/dns/message.c
+++ bind-9.10.2-P4/lib/dns/message.c
@@ -439,6 +439,8 @@ msginit(dns_message_t *m) {
 	m->free_saved = 0;
 	m->sitok = 0;
 	m->sitbad = 0;
+	m->tkey = 0;
+	m->rdclass_set = 0;
 	m->querytsig = NULL;
 }
 
@@ -1091,13 +1093,19 @@ getquestions(isc_buffer_t *source, dns_m
 		 * If this class is different than the one we already read,
 		 * this is an error.
 		 */
-		if (msg->state == DNS_SECTION_ANY) {
-			msg->state = DNS_SECTION_QUESTION;
+		if (msg->rdclass_set == 0) {
 			msg->rdclass = rdclass;
+			msg->rdclass_set = 1;
 		} else if (msg->rdclass != rdclass)
 			DO_FORMERR;
 
 		/*
+		 * Is this a TKEY query?
+		 */
+		if (rdtype == dns_rdatatype_tkey)
+			msg->tkey = 1;
+
+		/*
 		 * Can't ask the same question twice.
 		 */
 		result = dns_message_find(name, rdclass, rdtype, 0, NULL);
@@ -1241,12 +1249,12 @@ getsection(isc_buffer_t *source, dns_mes
 		 * If there was no question section, we may not yet have
 		 * established a class.  Do so now.
 		 */
-		if (msg->state == DNS_SECTION_ANY &&
+		if (msg->rdclass_set == 0 &&
 		    rdtype != dns_rdatatype_opt &&	/* class is UDP SIZE */
 		    rdtype != dns_rdatatype_tsig &&	/* class is ANY */
 		    rdtype != dns_rdatatype_tkey) {	/* class is undefined */
 			msg->rdclass = rdclass;
-			msg->state = DNS_SECTION_QUESTION;
+			msg->rdclass_set = 1;
 		}
 
 		/*
@@ -1256,7 +1264,7 @@ getsection(isc_buffer_t *source, dns_mes
 		if (msg->opcode != dns_opcode_update
 		    && rdtype != dns_rdatatype_tsig
 		    && rdtype != dns_rdatatype_opt
-		    && rdtype != dns_rdatatype_dnskey /* in a TKEY query */
+		    && rdtype != dns_rdatatype_key /* in a TKEY query */
 		    && rdtype != dns_rdatatype_sig /* SIG(0) */
 		    && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
 		    && msg->rdclass != dns_rdataclass_any
@@ -1264,6 +1272,16 @@ getsection(isc_buffer_t *source, dns_mes
 			DO_FORMERR;
 
 		/*
+		 * If this is not a TKEY query/response then the KEY
+		 * record's class needs to match.
+		 */
+		if (msg->opcode != dns_opcode_update && !msg->tkey &&
+		    rdtype == dns_rdatatype_key &&
+		    msg->rdclass != dns_rdataclass_any &&
+		    msg->rdclass != rdclass)
+			DO_FORMERR;
+
+		/*
 		 * Special type handling for TSIG, OPT, and TKEY.
 		 */
 		if (rdtype == dns_rdatatype_tsig) {
@@ -1377,6 +1395,10 @@ getsection(isc_buffer_t *source, dns_mes
 				skip_name_search = ISC_TRUE;
 				skip_type_search = ISC_TRUE;
 				issigzero = ISC_TRUE;
+			} else {
+				if (msg->rdclass != dns_rdataclass_any &&
+				    msg->rdclass != rdclass)
+					DO_FORMERR;
 			}
 		} else
 			covers = 0;
@@ -1625,6 +1647,7 @@ dns_message_parse(dns_message_t *msg, is
 	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
 
 	msg->header_ok = 1;
+	msg->state = DNS_SECTION_QUESTION;
 
 	/*
 	 * -1 means no EDNS.
@@ -3706,3 +3729,15 @@ dns_message_buildopt(dns_message_t *mess
 		dns_message_puttemprdatalist(message, &rdatalist);
 	return (result);
 }
+
+void
+dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) {
+
+	REQUIRE(DNS_MESSAGE_VALID(msg));
+	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
+	REQUIRE(msg->state == DNS_SECTION_ANY);
+	REQUIRE(msg->rdclass_set == 0);
+
+	msg->rdclass = rdclass;
+	msg->rdclass_set = 1;
+}
Index: bind-9.10.2-P4/lib/dns/resolver.c
===================================================================
--- bind-9.10.2-P4.orig/lib/dns/resolver.c
+++ bind-9.10.2-P4/lib/dns/resolver.c
@@ -7309,6 +7309,8 @@ resquery_response(isc_task_t *task, isc_
 			goto done;
 	}
 
+	dns_message_setclass(message, fctx->res->rdclass);
+
 	if ((options & DNS_FETCHOPT_TCP) == 0) {
 		if ((options & DNS_FETCHOPT_NOEDNS0) == 0)
 			dns_adb_setudpsize(fctx->adb, query->addrinfo,
@@ -7391,6 +7393,13 @@ resquery_response(isc_task_t *task, isc_
 				 &dns_master_style_comment,
 				 ISC_LOG_DEBUG(10),
 				 fctx->res->mctx);
+
+	if (message->rdclass != fctx->res->rdclass) {
+		resend = ISC_TRUE;
+		FCTXTRACE("bad class");
+		goto done;
+	}
+
 	/*
 	 * Process receive opt record.
 	 */
Index: bind-9.10.2-P4/lib/dns/xfrin.c
===================================================================
--- bind-9.10.2-P4.orig/lib/dns/xfrin.c
+++ bind-9.10.2-P4/lib/dns/xfrin.c
@@ -1225,6 +1225,8 @@ xfrin_recv_done(isc_task_t *task, isc_ev
 	msg->tsigctx = xfr->tsigctx;
 	xfr->tsigctx = NULL;
 
+	dns_message_setclass(msg, xfr->rdclass);
+
 	if (xfr->nmsg > 0)
 		msg->tcp_continuation = 1;
 
Index: bind-9.10.2-P4/CHANGES
===================================================================
--- bind-9.10.2-P4.orig/CHANGES
+++ bind-9.10.2-P4/CHANGES
@@ -1,4 +1,9 @@
-	--- 9.10.2-P4 released ---
+4260.  [security]      Insufficient testing when parsing a message allowed
+                       records with an incorrect class to be be accepted,
+                       triggering a REQUIRE failure when those records
+                       were subsequently cached. (CVE-2015-8000) [RT #4098]
+
+    --- 9.10.2-P4 released ---
 
 4170.	[security]	An incorrect boundary check in the OPENPGPKEY
 			rdatatype could trigger an assertion failure.
