964723
From 4827d4b06c2aaec913536143e4a26a0904d1fc58 Mon Sep 17 00:00:00 2001
964723
From: Mark Andrews <marka@isc.org>
964723
Date: Fri, 7 Jul 2017 23:19:05 +1000
964723
Subject: [PATCH] 4647. [bug] Change 4643 broke verification of TSIG signed TCP
964723
 message sequences where not all the messages contain TSIG records. These may
964723
 be used in AXFR and IXFR responses. [RT #45509]
964723
964723
(cherry picked from commit 58f0fb325bbd9258d06431281eb8fdea2b126305)
964723
---
964723
 lib/dns/tests/Makefile.in |   9 +-
964723
 lib/dns/tests/tsig_test.c | 489 ++++++++++++++++++++++++++++++++++++++++++++++
964723
 lib/dns/tsig.c            |  10 +-
964723
 3 files changed, 504 insertions(+), 4 deletions(-)
964723
 create mode 100644 lib/dns/tests/tsig_test.c
964723
964723
diff --git a/lib/dns/tests/Makefile.in b/lib/dns/tests/Makefile.in
964723
index 8d1b83e..023e60c 100644
964723
--- a/lib/dns/tests/Makefile.in
964723
+++ b/lib/dns/tests/Makefile.in
964723
@@ -39,13 +39,13 @@ LIBS =		@LIBS@ @ATFLIBS@
964723
 
964723
 OBJS =		dnstest.@O@
964723
 SRCS =		dnstest.c gost_test.c master_test.c dbiterator_test.c time_test.c \
964723
-		private_test.c update_test.c zonemgr_test.c zt_test.c \
964723
+		private_test.c tsig_test.c update_test.c zonemgr_test.c zt_test.c \
964723
 		dbdiff_test.c geoip_test.c dispatch_test.c nsec3_test.c \
964723
 		rdataset_test.c rdata_test.c
964723
 
964723
 SUBDIRS =
964723
 TARGETS =	gost_test@EXEEXT@ master_test@EXEEXT@ dbiterator_test@EXEEXT@ time_test@EXEEXT@ \
964723
-		private_test@EXEEXT@ update_test@EXEEXT@ zonemgr_test@EXEEXT@ \
964723
+		private_test@EXEEXT@ tsig_test@EXEEXT@ update_test@EXEEXT@ zonemgr_test@EXEEXT@ \
964723
 		zt_test@EXEEXT@ dbversion_test@EXEEXT@ dbdiff_test@EXEEXT@ geoip_test@EXEEXT@ \
964723
 		dispatch_test@EXEEXT@ nsec3_test@EXEEXT@ \
964723
 		rdataset_test@EXEEXT@ rdata_test@EXEEXT@
964723
@@ -134,6 +134,11 @@ geoip_test@EXEEXT@: geoip_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
964723
 			geoip_test.@O@ dnstest.@O@ ${DNSLIBS} \
964723
 			${ISCLIBS} ${LIBS}
964723
 
964723
+tsig_test@EXEEXT@: tsig_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
964723
+	${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
964723
+			tsig_test.@O@ dnstest.@O@ ${DNSLIBS} \
964723
+			${ISCLIBS} ${LIBS}
964723
+
964723
 unit::
964723
 	sh ${top_srcdir}/unit/unittest.sh
964723
 
964723
diff --git a/lib/dns/tests/tsig_test.c b/lib/dns/tests/tsig_test.c
964723
new file mode 100644
964723
index 0000000..956e4a0
964723
--- /dev/null
964723
+++ b/lib/dns/tests/tsig_test.c
964723
@@ -0,0 +1,489 @@
964723
+/*
964723
+ * Copyright (C) 2017  Internet Systems Consortium, Inc. ("ISC")
964723
+ *
964723
+ * This Source Code Form is subject to the terms of the Mozilla Public
964723
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
964723
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
964723
+ */
964723
+
964723
+/* ! \file */
964723
+
964723
+#include <config.h>
964723
+#include <atf-c.h>
964723
+#include <isc/mem.h>
964723
+
964723
+#include <dns/rdatalist.h>
964723
+#include <dns/rdataset.h>
964723
+#include <dns/tsig.h>
964723
+
964723
+#include "dnstest.h"
964723
+
964723
+#ifdef HAVE_INTTYPES_H
964723
+#include <inttypes.h> /* uintptr_t */
964723
+#endif
964723
+
964723
+static int debug = 0;
964723
+
964723
+static isc_result_t
964723
+add_mac(dst_context_t *tsigctx, isc_buffer_t *buf) {
964723
+	dns_rdata_any_tsig_t tsig;
964723
+	dns_rdata_t rdata = DNS_RDATA_INIT;
964723
+	isc_buffer_t databuf;
964723
+	isc_region_t r;
964723
+	isc_result_t result;
964723
+	unsigned char tsigbuf[1024];
964723
+
964723
+	isc_buffer_usedregion(buf, &r);
964723
+	dns_rdata_fromregion(&rdata, dns_rdataclass_any,
964723
+			     dns_rdatatype_tsig, &r);
964723
+	isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
964723
+	CHECK(dns_rdata_tostruct(&rdata, &tsig, NULL));
964723
+	isc_buffer_putuint16(&databuf, tsig.siglen);
964723
+	isc_buffer_putmem(&databuf, tsig.signature, tsig.siglen);
964723
+	isc_buffer_usedregion(&databuf, &r);
964723
+	result = dst_context_adddata(tsigctx, &r);
964723
+	dns_rdata_freestruct(&tsig);
964723
+ cleanup:
964723
+	return (result);
964723
+}
964723
+
964723
+static isc_result_t
964723
+add_tsig(dst_context_t *tsigctx, dns_tsigkey_t *key, isc_buffer_t *target) {
964723
+	dns_compress_t cctx;
964723
+	dns_rdata_any_tsig_t tsig;
964723
+	dns_rdata_t rdata = DNS_RDATA_INIT;
964723
+	dns_rdatalist_t rdatalist;
964723
+	dns_rdataset_t rdataset;
964723
+	isc_buffer_t *dynbuf = NULL;
964723
+	isc_buffer_t databuf;
964723
+	isc_buffer_t sigbuf;
964723
+	isc_region_t r;
964723
+	isc_result_t result = ISC_R_SUCCESS;
964723
+	isc_stdtime_t now;
964723
+	unsigned char tsigbuf[1024];
964723
+	unsigned int count;
964723
+	unsigned int sigsize;
964723
+	isc_boolean_t invalidate_ctx = ISC_FALSE;
964723
+
964723
+	CHECK(dns_compress_init(&cctx, -1, mctx));
964723
+	invalidate_ctx = ISC_TRUE;
964723
+
964723
+	memset(&tsig, 0, sizeof(tsig));
964723
+	       tsig.common.rdclass = dns_rdataclass_any;
964723
+	tsig.common.rdtype = dns_rdatatype_tsig;
964723
+	ISC_LINK_INIT(&tsig.common, link);
964723
+	dns_name_init(&tsig.algorithm, NULL);
964723
+	dns_name_clone(key->algorithm, &tsig.algorithm);
964723
+
964723
+	isc_stdtime_get(&now;;
964723
+	tsig.timesigned = now;
964723
+	tsig.fudge = DNS_TSIG_FUDGE;
964723
+	tsig.originalid = 50;
964723
+	tsig.error = dns_rcode_noerror;
964723
+	tsig.otherlen = 0;
964723
+	tsig.other = NULL;
964723
+
964723
+	isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
964723
+	isc_buffer_putuint48(&databuf, tsig.timesigned);
964723
+	isc_buffer_putuint16(&databuf, tsig.fudge);
964723
+	isc_buffer_usedregion(&databuf, &r);
964723
+	CHECK(dst_context_adddata(tsigctx, &r);;
964723
+
964723
+	CHECK(dst_key_sigsize(key->key, &sigsize));
964723
+	tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
964723
+	if (tsig.signature == NULL)
964723
+		CHECK(ISC_R_NOMEMORY);
964723
+	isc_buffer_init(&sigbuf, tsig.signature, sigsize);
964723
+	CHECK(dst_context_sign(tsigctx, &sigbuf));
964723
+	tsig.siglen = isc_buffer_usedlength(&sigbuf);
964723
+
964723
+	CHECK(isc_buffer_allocate(mctx, &dynbuf, 512));
964723
+	CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_any,
964723
+				   dns_rdatatype_tsig, &tsig, dynbuf));
964723
+	dns_rdatalist_init(&rdatalist);
964723
+	rdatalist.rdclass = dns_rdataclass_any;
964723
+	rdatalist.type = dns_rdatatype_tsig;
964723
+	ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
964723
+	dns_rdataset_init(&rdataset);
964723
+	CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
964723
+	CHECK(dns_rdataset_towire(&rdataset, &key->name, &cctx,
964723
+				  target, 0, &count));
964723
+
964723
+	/*
964723
+	 * Fixup additional record count.
964723
+	 */
964723
+	((unsigned char*)target->base)[11]++;
964723
+	if (((unsigned char*)target->base)[11] == 0)
964723
+		((unsigned char*)target->base)[10]++;
964723
+ cleanup:
964723
+	if (tsig.signature != NULL)
964723
+		isc_mem_put(mctx, tsig.signature, sigsize);
964723
+	if (dynbuf != NULL)
964723
+		isc_buffer_free(&dynbuf);
964723
+	if (invalidate_ctx)
964723
+		dns_compress_invalidate(&cctx);
964723
+
964723
+	return (result);
964723
+}
964723
+
964723
+static void
964723
+printmessage(dns_message_t *msg) {
964723
+	isc_buffer_t b;
964723
+	char *buf = NULL;
964723
+	int len = 1024;
964723
+	isc_result_t result = ISC_R_SUCCESS;
964723
+
964723
+	if (!debug)
964723
+		return;
964723
+
964723
+	do {
964723
+		buf = isc_mem_get(mctx, len);
964723
+		if (buf == NULL) {
964723
+			result = ISC_R_NOMEMORY;
964723
+			break;
964723
+		}
964723
+
964723
+		isc_buffer_init(&b, buf, len);
964723
+		result = dns_message_totext(msg, &dns_master_style_debug,
964723
+					    0, &b);
964723
+		if (result == ISC_R_NOSPACE) {
964723
+			isc_mem_put(mctx, buf, len);
964723
+			len *= 2;
964723
+		} else if (result == ISC_R_SUCCESS)
964723
+			printf("%.*s\n", (int) isc_buffer_usedlength(&b), buf);
964723
+	} while (result == ISC_R_NOSPACE);
964723
+
964723
+	if (buf != NULL)
964723
+		isc_mem_put(mctx, buf, len);
964723
+}
964723
+
964723
+static void
964723
+render(isc_buffer_t *buf, unsigned flags, dns_tsigkey_t *key,
964723
+       isc_buffer_t **tsigin, isc_buffer_t **tsigout,
964723
+       dst_context_t *tsigctx)
964723
+{
964723
+	dns_message_t *msg = NULL;
964723
+	dns_compress_t cctx;
964723
+	isc_result_t result;
964723
+
964723
+	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &msg;;
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_create: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	msg->id = 50;
964723
+	msg->rcode = dns_rcode_noerror;
964723
+	msg->flags = flags;
964723
+
964723
+	if (tsigin == tsigout)
964723
+		msg->tcp_continuation = 1;
964723
+
964723
+	if (tsigctx == NULL) {
964723
+		result = dns_message_settsigkey(msg, key);
964723
+		ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+				 "dns_message_settsigkey: %s",
964723
+				 dns_result_totext(result));
964723
+
964723
+		result = dns_message_setquerytsig(msg, *tsigin);
964723
+		ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+				 "dns_message_setquerytsig: %s",
964723
+				 dns_result_totext(result));
964723
+	}
964723
+
964723
+	result = dns_compress_init(&cctx, -1, mctx);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_compress_init: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_message_renderbegin(msg, &cctx, buf);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_renderbegin: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_message_renderend(msg);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_renderend: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	if (tsigctx != NULL) {
964723
+		isc_region_t r;
964723
+
964723
+		isc_buffer_usedregion(buf, &r);
964723
+		result = dst_context_adddata(tsigctx, &r);
964723
+	} else {
964723
+		if (tsigin == tsigout && *tsigin != NULL)
964723
+			isc_buffer_free(tsigin);
964723
+
964723
+		result = dns_message_getquerytsig(msg, mctx, tsigout);
964723
+		ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+				 "dns_message_getquerytsig: %s",
964723
+				 dns_result_totext(result));
964723
+	}
964723
+
964723
+	dns_compress_invalidate(&cctx);
964723
+	dns_message_destroy(&msg;;
964723
+}
964723
+
964723
+/*
964723
+ * Check that a simulated three message TCP sequence where the first
964723
+ * and last messages contain TSIGs but the intermediate message doesn't
964723
+ * correctly verifies.
964723
+ */
964723
+ATF_TC(tsig_tcp);
964723
+ATF_TC_HEAD(tsig_tcp, tc) {
964723
+	atf_tc_set_md_var(tc, "descr", "test tsig tcp-continuation validation");
964723
+}
964723
+ATF_TC_BODY(tsig_tcp, tc) {
964723
+	dns_name_t *tsigowner = NULL;
964723
+	dns_fixedname_t fkeyname;
964723
+	dns_message_t *msg = NULL;
964723
+	dns_name_t *keyname;
964723
+	dns_tsig_keyring_t *ring = NULL;
964723
+	dns_tsigkey_t *key = NULL;
964723
+	isc_buffer_t *buf = NULL;
964723
+	isc_buffer_t *querytsig = NULL;
964723
+	isc_buffer_t *tsigin = NULL;
964723
+	isc_buffer_t *tsigout = NULL;
964723
+	isc_result_t result;
964723
+	unsigned char secret[16] = { 0 };
964723
+	dst_context_t *tsigctx = NULL;
964723
+	dst_context_t *outctx = NULL;
964723
+
964723
+	UNUSED(tc);
964723
+
964723
+	result = dns_test_begin(stderr, ISC_FALSE);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+
964723
+	/* isc_log_setdebuglevel(lctx, 99); */
964723
+
964723
+	dns_fixedname_init(&fkeyname);
964723
+	keyname = dns_fixedname_name(&fkeyname);
964723
+	result = dns_name_fromstring(keyname, "test", 0, NULL);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+
964723
+	result = dns_tsigkeyring_create(mctx, &ring);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+
964723
+	result = dns_tsigkey_create(keyname, dns_tsig_hmacsha256_name,
964723
+				    secret, sizeof(secret), ISC_FALSE,
964723
+				    NULL, 0, 0, mctx, ring, &key);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+
964723
+	/*
964723
+	 * Create request.
964723
+	 */
964723
+	result = isc_buffer_allocate(mctx, &buf, 65535);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+	render(buf, 0, key, &tsigout, &querytsig, NULL);
964723
+	isc_buffer_free(&buf;;
964723
+
964723
+	/*
964723
+	 * Create response message 1.
964723
+	 */
964723
+	result = isc_buffer_allocate(mctx, &buf, 65535);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+	render(buf, DNS_MESSAGEFLAG_QR, key, &querytsig, &tsigout, NULL);
964723
+
964723
+	/*
964723
+	 * Process response message 1.
964723
+	 */
964723
+	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg;;
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_create: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_message_settsigkey(msg, key);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_settsigkey: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_message_parse(msg, buf, 0);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_parse: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	printmessage(msg);
964723
+
964723
+	result = dns_message_setquerytsig(msg, querytsig);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_setquerytsig: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_tsig_verify(buf, msg, NULL, NULL);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_tsig_verify: %s",
964723
+			 dns_result_totext(result));
964723
+	ATF_CHECK_EQ(msg->verified_sig, 1);
964723
+	ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror);
964723
+
964723
+	/*
964723
+	 * Check that we have a TSIG in the first message.
964723
+	 */
964723
+	ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) != NULL);
964723
+
964723
+	result = dns_message_getquerytsig(msg, mctx, &tsigin);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_getquerytsig: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	tsigctx = msg->tsigctx;
964723
+	msg->tsigctx = NULL;
964723
+	isc_buffer_free(&buf;;
964723
+	dns_message_destroy(&msg;;
964723
+
964723
+	result = dst_context_create2(key->key, mctx, DNS_LOGCATEGORY_DNSSEC,
964723
+				     &outctx);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+
964723
+	/*
964723
+	 * Start digesting.
964723
+	 */
964723
+	result = add_mac(outctx, tsigout);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+
964723
+	/*
964723
+	 * Create response message 2.
964723
+	 */
964723
+	result = isc_buffer_allocate(mctx, &buf, 65535);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+	render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
964723
+
964723
+	/*
964723
+	 * Process response message 2.
964723
+	 */
964723
+	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg;;
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_create: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	msg->tcp_continuation = 1;
964723
+	msg->tsigctx = tsigctx;
964723
+	tsigctx = NULL;
964723
+
964723
+	result = dns_message_settsigkey(msg, key);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_settsigkey: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_message_parse(msg, buf, 0);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_parse: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	printmessage(msg);
964723
+
964723
+	result = dns_message_setquerytsig(msg, tsigin);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_setquerytsig: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_tsig_verify(buf, msg, NULL, NULL);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_tsig_verify: %s",
964723
+			 dns_result_totext(result));
964723
+	ATF_CHECK_EQ(msg->verified_sig, 1);
964723
+	ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror);
964723
+
964723
+	/*
964723
+	 * Check that we don't have a TSIG in the second message.
964723
+	 */
964723
+	tsigowner = NULL;
964723
+	ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) == NULL);
964723
+
964723
+	tsigctx = msg->tsigctx;
964723
+	msg->tsigctx = NULL;
964723
+	isc_buffer_free(&buf;;
964723
+	dns_message_destroy(&msg;;
964723
+
964723
+	/*
964723
+	 * Create response message 3.
964723
+	 */
964723
+	result = isc_buffer_allocate(mctx, &buf, 65535);
964723
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
964723
+	render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
964723
+
964723
+	result = add_tsig(outctx, key, buf);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "add_tsig: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	/*
964723
+	 * Process response message 3.
964723
+	 */
964723
+	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg;;
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_create: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	msg->tcp_continuation = 1;
964723
+	msg->tsigctx = tsigctx;
964723
+	tsigctx = NULL;
964723
+
964723
+	result = dns_message_settsigkey(msg, key);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_settsigkey: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_message_parse(msg, buf, 0);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_parse: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	printmessage(msg);
964723
+
964723
+	/*
964723
+	 * Check that we had a TSIG in the third message.
964723
+	 */
964723
+	ATF_REQUIRE(dns_message_gettsig(msg, &tsigowner) != NULL);
964723
+
964723
+	result = dns_message_setquerytsig(msg, tsigin);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_setquerytsig: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	result = dns_tsig_verify(buf, msg, NULL, NULL);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_tsig_verify: %s",
964723
+			 dns_result_totext(result));
964723
+	ATF_CHECK_EQ(msg->verified_sig, 1);
964723
+	ATF_CHECK_EQ(msg->tsigstatus, dns_rcode_noerror);
964723
+
964723
+	if (tsigin != NULL)
964723
+		isc_buffer_free(&tsigin);
964723
+
964723
+	result = dns_message_getquerytsig(msg, mctx, &tsigin);
964723
+	ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS,
964723
+			 "dns_message_getquerytsig: %s",
964723
+			 dns_result_totext(result));
964723
+
964723
+	isc_buffer_free(&buf;;
964723
+	dns_message_destroy(&msg;;
964723
+
964723
+	if (outctx != NULL)
964723
+		dst_context_destroy(&outctx);
964723
+	if (querytsig != NULL)
964723
+		isc_buffer_free(&querytsig);
964723
+	if (tsigin != NULL)
964723
+		isc_buffer_free(&tsigin);
964723
+	if (tsigout != NULL)
964723
+		isc_buffer_free(&tsigout);
964723
+	if (buf != NULL)
964723
+		isc_buffer_free(&buf;;
964723
+	if (msg != NULL)
964723
+		dns_message_destroy(&msg;;
964723
+	if (key != NULL)
964723
+		dns_tsigkey_detach(&key);
964723
+	if (ring != NULL)
964723
+		dns_tsigkeyring_detach(&ring);
964723
+	dns_test_end();
964723
+}
964723
+
964723
+/*
964723
+ * Main
964723
+ */
964723
+ATF_TP_ADD_TCS(tp) {
964723
+	ATF_TP_ADD_TC(tp, tsig_tcp);
964723
+	return (atf_no_error());
964723
+}
964723
diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c
964723
index 7b91d1e..325c901 100644
964723
--- a/lib/dns/tsig.c
964723
+++ b/lib/dns/tsig.c
964723
@@ -1535,7 +1535,7 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
964723
 	msg->verified_sig = 1;
964723
 	ret = ISC_R_SUCCESS;
964723
 
964723
-cleanup_context:
964723
+ cleanup_context:
964723
 	if (ctx != NULL)
964723
 		dst_context_destroy(&ctx;;
964723
 
964723
@@ -1859,8 +1859,14 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
964723
 	ret = ISC_R_SUCCESS;
964723
 
964723
  cleanup_context:
964723
-	if (msg->tsigctx != NULL)
964723
+	/*
964723
+	 * Except in error conditions, don't destroy the DST context
964723
+	 * for unsigned messages; it is a running sum till the next
964723
+	 * TSIG signed message.
964723
+	 */
964723
+	if ((ret != ISC_R_SUCCESS || has_tsig) && msg->tsigctx != NULL) {
964723
 		dst_context_destroy(&msg->tsigctx);
964723
+	}
964723
 
964723
  cleanup_querystruct:
964723
 	dns_rdata_freestruct(&querytsig);
964723
-- 
964723
2.9.4
964723