c2d1e6
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
c2d1e6
index 00a0080..336c4da 100644
c2d1e6
--- a/lib/dns/dnssec.c
c2d1e6
+++ b/lib/dns/dnssec.c
c2d1e6
@@ -982,6 +982,8 @@ dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg,
c2d1e6
 	mctx = msg->mctx;
c2d1e6
 
c2d1e6
 	msg->verify_attempted = 1;
c2d1e6
+	msg->verified_sig = 0;
c2d1e6
+	msg->sig0status = dns_tsigerror_badsig;
c2d1e6
 
c2d1e6
 	if (is_response(msg)) {
c2d1e6
 		if (msg->query.base == NULL)
c2d1e6
@@ -1076,6 +1078,7 @@ dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg,
c2d1e6
 	}
c2d1e6
 
c2d1e6
 	msg->verified_sig = 1;
c2d1e6
+	msg->sig0status = dns_rcode_noerror;
c2d1e6
 
c2d1e6
 	dst_context_destroy(&ctx;;
c2d1e6
 	dns_rdata_freestruct(&sig);
c2d1e6
diff --git a/lib/dns/message.c b/lib/dns/message.c
c2d1e6
index 1417067..0621175 100644
c2d1e6
--- a/lib/dns/message.c
c2d1e6
+++ b/lib/dns/message.c
c2d1e6
@@ -3052,12 +3052,19 @@ dns_message_signer(dns_message_t *msg, dns_name_t *signer) {
c2d1e6
 
c2d1e6
 		result = dns_rdata_tostruct(&rdata, &tsig, NULL);
c2d1e6
 		INSIST(result == ISC_R_SUCCESS);
c2d1e6
-		if (msg->tsigstatus != dns_rcode_noerror)
c2d1e6
+		if (msg->verified_sig &&
c2d1e6
+		    msg->tsigstatus == dns_rcode_noerror &&
c2d1e6
+		    tsig.error == dns_rcode_noerror)
c2d1e6
+		{
c2d1e6
+			result = ISC_R_SUCCESS;
c2d1e6
+		} else if ((!msg->verified_sig) ||
c2d1e6
+			   (msg->tsigstatus != dns_rcode_noerror))
c2d1e6
+		{
c2d1e6
 			result = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
-		else if (tsig.error != dns_rcode_noerror)
c2d1e6
+		} else {
c2d1e6
+			INSIST(tsig.error != dns_rcode_noerror);
c2d1e6
 			result = DNS_R_TSIGERRORSET;
c2d1e6
-		else
c2d1e6
-			result = ISC_R_SUCCESS;
c2d1e6
+		}
c2d1e6
 		dns_rdata_freestruct(&tsig);
c2d1e6
 
c2d1e6
 		if (msg->tsigkey == NULL) {
c2d1e6
diff --git a/lib/dns/tsig.c b/lib/dns/tsig.c
c2d1e6
index 3239bff..7b91d1e 100644
c2d1e6
--- a/lib/dns/tsig.c
c2d1e6
+++ b/lib/dns/tsig.c
c2d1e6
@@ -941,11 +941,20 @@ dns_tsig_sign(dns_message_t *msg) {
c2d1e6
 		isc_buffer_putuint48(&otherbuf, tsig.timesigned);
c2d1e6
 	}
c2d1e6
 
c2d1e6
-	if (key->key != NULL && tsig.error != dns_tsigerror_badsig) {
c2d1e6
+	if ((key->key != NULL) &&
c2d1e6
+	    (tsig.error != dns_tsigerror_badsig) &&
c2d1e6
+	    (tsig.error != dns_tsigerror_badkey))
c2d1e6
+	{
c2d1e6
 		unsigned char header[DNS_MESSAGE_HEADERLEN];
c2d1e6
 		isc_buffer_t headerbuf;
c2d1e6
 		isc_uint16_t digestbits;
c2d1e6
 
c2d1e6
+		/*
c2d1e6
+		 * If it is a response, we assume that the request MAC
c2d1e6
+		 * has validated at this point. This is why we include a
c2d1e6
+		 * MAC length > 0 in the reply.
c2d1e6
+		 */
c2d1e6
+
c2d1e6
 		ret = dst_context_create3(key->key, mctx,
c2d1e6
 					  DNS_LOGCATEGORY_DNSSEC,
c2d1e6
 					  ISC_TRUE, &ctx;;
c2d1e6
@@ -953,7 +962,7 @@ dns_tsig_sign(dns_message_t *msg) {
c2d1e6
 			return (ret);
c2d1e6
 
c2d1e6
 		/*
c2d1e6
-		 * If this is a response, digest the query signature.
c2d1e6
+		 * If this is a response, digest the request's MAC.
c2d1e6
 		 */
c2d1e6
 		if (response) {
c2d1e6
 			dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
c2d1e6
@@ -1083,6 +1092,17 @@ dns_tsig_sign(dns_message_t *msg) {
c2d1e6
 		dst_context_destroy(&ctx;;
c2d1e6
 		digestbits = dst_key_getbits(key->key);
c2d1e6
 		if (digestbits != 0) {
c2d1e6
+			/*
c2d1e6
+			 * XXXRAY: Is this correct? What is the
c2d1e6
+			 * expected behavior when digestbits is not an
c2d1e6
+			 * integral multiple of 8? It looks like bytes
c2d1e6
+			 * should either be (digestbits/8) or
c2d1e6
+			 * (digestbits+7)/8.
c2d1e6
+			 *
c2d1e6
+			 * In any case, for current algorithms,
c2d1e6
+			 * digestbits are an integral multiple of 8, so
c2d1e6
+			 * it has the same effect as (digestbits/8).
c2d1e6
+			 */
c2d1e6
 			unsigned int bytes = (digestbits + 1) / 8;
c2d1e6
 			if (response && bytes < querytsig.siglen)
c2d1e6
 				bytes = querytsig.siglen;
c2d1e6
@@ -1196,6 +1216,8 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
c2d1e6
 	REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
c2d1e6
 
c2d1e6
 	msg->verify_attempted = 1;
c2d1e6
+	msg->verified_sig = 0;
c2d1e6
+	msg->tsigstatus = dns_tsigerror_badsig;
c2d1e6
 
c2d1e6
 	if (msg->tcp_continuation) {
c2d1e6
 		if (tsigkey == NULL || msg->querytsig == NULL)
c2d1e6
@@ -1294,19 +1316,6 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
c2d1e6
 	key = tsigkey->key;
c2d1e6
 
c2d1e6
 	/*
c2d1e6
-	 * Is the time ok?
c2d1e6
-	 */
c2d1e6
-	if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
c2d1e6
-		msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
-		tsig_log(msg->tsigkey, 2, "signature has expired");
c2d1e6
-		return (DNS_R_CLOCKSKEW);
c2d1e6
-	} else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
c2d1e6
-		msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
-		tsig_log(msg->tsigkey, 2, "signature is in the future");
c2d1e6
-		return (DNS_R_CLOCKSKEW);
c2d1e6
-	}
c2d1e6
-
c2d1e6
-	/*
c2d1e6
 	 * Check digest length.
c2d1e6
 	 */
c2d1e6
 	alg = dst_key_alg(key);
c2d1e6
@@ -1315,31 +1324,19 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
c2d1e6
 		return (ret);
c2d1e6
 	if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 ||
c2d1e6
 	    alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
c2d1e6
-	    alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) {
c2d1e6
-		isc_uint16_t digestbits = dst_key_getbits(key);
c2d1e6
+	    alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
c2d1e6
+	{
c2d1e6
 		if (tsig.siglen > siglen) {
c2d1e6
-			tsig_log(msg->tsigkey, 2, "signature length to big");
c2d1e6
+			tsig_log(msg->tsigkey, 2, "signature length too big");
c2d1e6
 			return (DNS_R_FORMERR);
c2d1e6
 		}
c2d1e6
 		if (tsig.siglen > 0 &&
c2d1e6
-		    (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) {
c2d1e6
+		    (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2)))
c2d1e6
+		{
c2d1e6
 			tsig_log(msg->tsigkey, 2,
c2d1e6
 				 "signature length below minimum");
c2d1e6
 			return (DNS_R_FORMERR);
c2d1e6
 		}
c2d1e6
-		if (tsig.siglen > 0 && digestbits != 0 &&
c2d1e6
-		    tsig.siglen < ((digestbits + 1) / 8)) {
c2d1e6
-			msg->tsigstatus = dns_tsigerror_badtrunc;
c2d1e6
-			tsig_log(msg->tsigkey, 2,
c2d1e6
-				 "truncated signature length too small");
c2d1e6
-			return (DNS_R_TSIGVERIFYFAILURE);
c2d1e6
-		}
c2d1e6
-		if (tsig.siglen > 0 && digestbits == 0 &&
c2d1e6
-		    tsig.siglen < siglen) {
c2d1e6
-			msg->tsigstatus = dns_tsigerror_badtrunc;
c2d1e6
-			tsig_log(msg->tsigkey, 2, "signature length too small");
c2d1e6
-			return (DNS_R_TSIGVERIFYFAILURE);
c2d1e6
-		}
c2d1e6
 	}
c2d1e6
 
c2d1e6
 	if (tsig.siglen > 0) {
c2d1e6
@@ -1451,34 +1448,92 @@ dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
c2d1e6
 
c2d1e6
 		ret = dst_context_verify(ctx, &sig_r);
c2d1e6
 		if (ret == DST_R_VERIFYFAILURE) {
c2d1e6
-			msg->tsigstatus = dns_tsigerror_badsig;
c2d1e6
 			ret = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
 			tsig_log(msg->tsigkey, 2,
c2d1e6
 				 "signature failed to verify(1)");
c2d1e6
 			goto cleanup_context;
c2d1e6
-		} else if (ret != ISC_R_SUCCESS)
c2d1e6
+		} else if (ret != ISC_R_SUCCESS) {
c2d1e6
 			goto cleanup_context;
c2d1e6
-
c2d1e6
-		dst_context_destroy(&ctx;;
c2d1e6
+		}
c2d1e6
 	} else if (tsig.error != dns_tsigerror_badsig &&
c2d1e6
 		   tsig.error != dns_tsigerror_badkey) {
c2d1e6
-		msg->tsigstatus = dns_tsigerror_badsig;
c2d1e6
 		tsig_log(msg->tsigkey, 2, "signature was empty");
c2d1e6
 		return (DNS_R_TSIGVERIFYFAILURE);
c2d1e6
 	}
c2d1e6
 
c2d1e6
-	msg->tsigstatus = dns_rcode_noerror;
c2d1e6
+	/*
c2d1e6
+	 * Here at this point, the MAC has been verified. Even if any of
c2d1e6
+	 * the following code returns a TSIG error, the reply will be
c2d1e6
+	 * signed and WILL always include the request MAC in the digest
c2d1e6
+	 * computation.
c2d1e6
+	 */
c2d1e6
+
c2d1e6
+	/*
c2d1e6
+	 * Is the time ok?
c2d1e6
+	 */
c2d1e6
+	if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
c2d1e6
+		msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
+		tsig_log(msg->tsigkey, 2, "signature has expired");
c2d1e6
+		ret = DNS_R_CLOCKSKEW;
c2d1e6
+		goto cleanup_context;
c2d1e6
+	} else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
c2d1e6
+		msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
+		tsig_log(msg->tsigkey, 2, "signature is in the future");
c2d1e6
+		ret = DNS_R_CLOCKSKEW;
c2d1e6
+		goto cleanup_context;
c2d1e6
+	}
c2d1e6
+
c2d1e6
+	if (
c2d1e6
+#ifndef PK11_MD5_DISABLE
c2d1e6
+	    alg == DST_ALG_HMACMD5 ||
c2d1e6
+#endif
c2d1e6
+	    alg == DST_ALG_HMACSHA1 ||
c2d1e6
+	    alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
c2d1e6
+	    alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
c2d1e6
+	{
c2d1e6
+		isc_uint16_t digestbits = dst_key_getbits(key);
c2d1e6
+
c2d1e6
+		/*
c2d1e6
+		 * XXXRAY: Is this correct? What is the expected
c2d1e6
+		 * behavior when digestbits is not an integral multiple
c2d1e6
+		 * of 8? It looks like bytes should either be
c2d1e6
+		 * (digestbits/8) or (digestbits+7)/8.
c2d1e6
+		 *
c2d1e6
+		 * In any case, for current algorithms, digestbits are
c2d1e6
+		 * an integral multiple of 8, so it has the same effect
c2d1e6
+		 * as (digestbits/8).
c2d1e6
+		 */
c2d1e6
+		if (tsig.siglen > 0 && digestbits != 0 &&
c2d1e6
+		    tsig.siglen < ((digestbits + 1) / 8))
c2d1e6
+		{
c2d1e6
+			msg->tsigstatus = dns_tsigerror_badtrunc;
c2d1e6
+			tsig_log(msg->tsigkey, 2,
c2d1e6
+				 "truncated signature length too small");
c2d1e6
+			ret = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
+			goto cleanup_context;
c2d1e6
+		}
c2d1e6
+		if (tsig.siglen > 0 && digestbits == 0 &&
c2d1e6
+		    tsig.siglen < siglen)
c2d1e6
+		{
c2d1e6
+			msg->tsigstatus = dns_tsigerror_badtrunc;
c2d1e6
+			tsig_log(msg->tsigkey, 2, "signature length too small");
c2d1e6
+			ret = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
+			goto cleanup_context;
c2d1e6
+		}
c2d1e6
+	}
c2d1e6
 
c2d1e6
 	if (tsig.error != dns_rcode_noerror) {
c2d1e6
+		msg->tsigstatus = tsig.error;
c2d1e6
 		if (tsig.error == dns_tsigerror_badtime)
c2d1e6
-			return (DNS_R_CLOCKSKEW);
c2d1e6
+			ret = DNS_R_CLOCKSKEW;
c2d1e6
 		else
c2d1e6
-			return (DNS_R_TSIGERRORSET);
c2d1e6
+			ret = DNS_R_TSIGERRORSET;
c2d1e6
+		goto cleanup_context;
c2d1e6
 	}
c2d1e6
 
c2d1e6
+	msg->tsigstatus = dns_rcode_noerror;
c2d1e6
 	msg->verified_sig = 1;
c2d1e6
-
c2d1e6
-	return (ISC_R_SUCCESS);
c2d1e6
+	ret = ISC_R_SUCCESS;
c2d1e6
 
c2d1e6
 cleanup_context:
c2d1e6
 	if (ctx != NULL)
c2d1e6
@@ -1503,6 +1558,8 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
c2d1e6
 	isc_uint16_t addcount, id;
c2d1e6
 	isc_boolean_t has_tsig = ISC_FALSE;
c2d1e6
 	isc_mem_t *mctx;
c2d1e6
+	unsigned int siglen;
c2d1e6
+	unsigned int alg;
c2d1e6
 
c2d1e6
 	REQUIRE(source != NULL);
c2d1e6
 	REQUIRE(msg != NULL);
c2d1e6
@@ -1510,12 +1567,16 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
c2d1e6
 	REQUIRE(msg->tcp_continuation == 1);
c2d1e6
 	REQUIRE(msg->querytsig != NULL);
c2d1e6
 
c2d1e6
+	msg->verified_sig = 0;
c2d1e6
+	msg->tsigstatus = dns_tsigerror_badsig;
c2d1e6
+
c2d1e6
 	if (!is_response(msg))
c2d1e6
 		return (DNS_R_EXPECTEDRESPONSE);
c2d1e6
 
c2d1e6
 	mctx = msg->mctx;
c2d1e6
 
c2d1e6
 	tsigkey = dns_message_gettsigkey(msg);
c2d1e6
+	key = tsigkey->key;
c2d1e6
 
c2d1e6
 	/*
c2d1e6
 	 * Extract and parse the previous TSIG
c2d1e6
@@ -1548,7 +1609,8 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
c2d1e6
 		 * Do the key name and algorithm match that of the query?
c2d1e6
 		 */
c2d1e6
 		if (!dns_name_equal(keyname, &tsigkey->name) ||
c2d1e6
-		    !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) {
c2d1e6
+		    !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))
c2d1e6
+		{
c2d1e6
 			msg->tsigstatus = dns_tsigerror_badkey;
c2d1e6
 			ret = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
 			tsig_log(msg->tsigkey, 2,
c2d1e6
@@ -1557,27 +1619,40 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
c2d1e6
 		}
c2d1e6
 
c2d1e6
 		/*
c2d1e6
-		 * Is the time ok?
c2d1e6
+		 * Check digest length.
c2d1e6
 		 */
c2d1e6
-		isc_stdtime_get(&now;;
c2d1e6
-
c2d1e6
-		if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
c2d1e6
-			msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
-			tsig_log(msg->tsigkey, 2, "signature has expired");
c2d1e6
-			ret = DNS_R_CLOCKSKEW;
c2d1e6
-			goto cleanup_querystruct;
c2d1e6
-		} else if (now + msg->timeadjust <
c2d1e6
-			   tsig.timesigned - tsig.fudge) {
c2d1e6
-			msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
-			tsig_log(msg->tsigkey, 2,
c2d1e6
-				 "signature is in the future");
c2d1e6
-			ret = DNS_R_CLOCKSKEW;
c2d1e6
+		alg = dst_key_alg(key);
c2d1e6
+		ret = dst_key_sigsize(key, &siglen);
c2d1e6
+		if (ret != ISC_R_SUCCESS)
c2d1e6
 			goto cleanup_querystruct;
c2d1e6
+		if (
c2d1e6
+#ifndef PK11_MD5_DISABLE
c2d1e6
+			alg == DST_ALG_HMACMD5 ||
c2d1e6
+#endif
c2d1e6
+			alg == DST_ALG_HMACSHA1 ||
c2d1e6
+			alg == DST_ALG_HMACSHA224 ||
c2d1e6
+			alg == DST_ALG_HMACSHA256 ||
c2d1e6
+			alg == DST_ALG_HMACSHA384 ||
c2d1e6
+			alg == DST_ALG_HMACSHA512)
c2d1e6
+		{
c2d1e6
+			if (tsig.siglen > siglen) {
c2d1e6
+				tsig_log(tsigkey, 2,
c2d1e6
+					 "signature length too big");
c2d1e6
+				ret = DNS_R_FORMERR;
c2d1e6
+				goto cleanup_querystruct;
c2d1e6
+			}
c2d1e6
+			if (tsig.siglen > 0 &&
c2d1e6
+			    (tsig.siglen < 10 ||
c2d1e6
+			     tsig.siglen < ((siglen + 1) / 2)))
c2d1e6
+			{
c2d1e6
+				tsig_log(tsigkey, 2,
c2d1e6
+					 "signature length below minimum");
c2d1e6
+				ret = DNS_R_FORMERR;
c2d1e6
+				goto cleanup_querystruct;
c2d1e6
+			}
c2d1e6
 		}
c2d1e6
 	}
c2d1e6
 
c2d1e6
-	key = tsigkey->key;
c2d1e6
-
c2d1e6
 	if (msg->tsigctx == NULL) {
c2d1e6
 		ret = dst_context_create3(key, mctx,
c2d1e6
 					  DNS_LOGCATEGORY_DNSSEC,
c2d1e6
@@ -1670,10 +1745,12 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
c2d1e6
 		sig_r.length = tsig.siglen;
c2d1e6
 		if (tsig.siglen == 0) {
c2d1e6
 			if (tsig.error != dns_rcode_noerror) {
c2d1e6
-				if (tsig.error == dns_tsigerror_badtime)
c2d1e6
+				msg->tsigstatus = tsig.error;
c2d1e6
+				if (tsig.error == dns_tsigerror_badtime) {
c2d1e6
 					ret = DNS_R_CLOCKSKEW;
c2d1e6
-				else
c2d1e6
+				} else {
c2d1e6
 					ret = DNS_R_TSIGERRORSET;
c2d1e6
+				}
c2d1e6
 			} else {
c2d1e6
 				tsig_log(msg->tsigkey, 2,
c2d1e6
 					 "signature is empty");
c2d1e6
@@ -1684,29 +1761,111 @@ tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
c2d1e6
 
c2d1e6
 		ret = dst_context_verify(msg->tsigctx, &sig_r);
c2d1e6
 		if (ret == DST_R_VERIFYFAILURE) {
c2d1e6
-			msg->tsigstatus = dns_tsigerror_badsig;
c2d1e6
 			tsig_log(msg->tsigkey, 2,
c2d1e6
 				 "signature failed to verify(2)");
c2d1e6
 			ret = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
 			goto cleanup_context;
c2d1e6
+		} else if (ret != ISC_R_SUCCESS) {
c2d1e6
+			goto cleanup_context;
c2d1e6
 		}
c2d1e6
-		else if (ret != ISC_R_SUCCESS)
c2d1e6
+
c2d1e6
+		/*
c2d1e6
+		 * Here at this point, the MAC has been verified. Even
c2d1e6
+		 * if any of the following code returns a TSIG error,
c2d1e6
+		 * the reply will be signed and WILL always include the
c2d1e6
+		 * request MAC in the digest computation.
c2d1e6
+		 */
c2d1e6
+
c2d1e6
+		/*
c2d1e6
+		 * Is the time ok?
c2d1e6
+		 */
c2d1e6
+		isc_stdtime_get(&now;;
c2d1e6
+
c2d1e6
+		if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
c2d1e6
+			msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
+			tsig_log(msg->tsigkey, 2, "signature has expired");
c2d1e6
+			ret = DNS_R_CLOCKSKEW;
c2d1e6
+			goto cleanup_context;
c2d1e6
+		} else if (now + msg->timeadjust <
c2d1e6
+			   tsig.timesigned - tsig.fudge)
c2d1e6
+		{
c2d1e6
+			msg->tsigstatus = dns_tsigerror_badtime;
c2d1e6
+			tsig_log(msg->tsigkey, 2,
c2d1e6
+				 "signature is in the future");
c2d1e6
+			ret = DNS_R_CLOCKSKEW;
c2d1e6
 			goto cleanup_context;
c2d1e6
+		}
c2d1e6
 
c2d1e6
-		dst_context_destroy(&msg->tsigctx);
c2d1e6
+		alg = dst_key_alg(key);
c2d1e6
+		ret = dst_key_sigsize(key, &siglen);
c2d1e6
+		if (ret != ISC_R_SUCCESS)
c2d1e6
+			goto cleanup_context;
c2d1e6
+		if (
c2d1e6
+#ifndef PK11_MD5_DISABLE
c2d1e6
+			alg == DST_ALG_HMACMD5 ||
c2d1e6
+#endif
c2d1e6
+			alg == DST_ALG_HMACSHA1 ||
c2d1e6
+			alg == DST_ALG_HMACSHA224 ||
c2d1e6
+			alg == DST_ALG_HMACSHA256 ||
c2d1e6
+			alg == DST_ALG_HMACSHA384 ||
c2d1e6
+			alg == DST_ALG_HMACSHA512)
c2d1e6
+		{
c2d1e6
+			isc_uint16_t digestbits = dst_key_getbits(key);
c2d1e6
+
c2d1e6
+			/*
c2d1e6
+			 * XXXRAY: Is this correct? What is the
c2d1e6
+			 * expected behavior when digestbits is not an
c2d1e6
+			 * integral multiple of 8? It looks like bytes
c2d1e6
+			 * should either be (digestbits/8) or
c2d1e6
+			 * (digestbits+7)/8.
c2d1e6
+			 *
c2d1e6
+			 * In any case, for current algorithms,
c2d1e6
+			 * digestbits are an integral multiple of 8, so
c2d1e6
+			 * it has the same effect as (digestbits/8).
c2d1e6
+			 */
c2d1e6
+			if (tsig.siglen > 0 && digestbits != 0 &&
c2d1e6
+			    tsig.siglen < ((digestbits + 1) / 8))
c2d1e6
+			{
c2d1e6
+				msg->tsigstatus = dns_tsigerror_badtrunc;
c2d1e6
+				tsig_log(msg->tsigkey, 2,
c2d1e6
+					 "truncated signature length "
c2d1e6
+					 "too small");
c2d1e6
+				ret = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
+				goto cleanup_context;
c2d1e6
+			}
c2d1e6
+			if (tsig.siglen > 0 && digestbits == 0 &&
c2d1e6
+			    tsig.siglen < siglen)
c2d1e6
+			{
c2d1e6
+				msg->tsigstatus = dns_tsigerror_badtrunc;
c2d1e6
+				tsig_log(msg->tsigkey, 2,
c2d1e6
+					 "signature length too small");
c2d1e6
+				ret = DNS_R_TSIGVERIFYFAILURE;
c2d1e6
+				goto cleanup_context;
c2d1e6
+			}
c2d1e6
+		}
c2d1e6
+
c2d1e6
+		if (tsig.error != dns_rcode_noerror) {
c2d1e6
+			msg->tsigstatus = tsig.error;
c2d1e6
+			if (tsig.error == dns_tsigerror_badtime)
c2d1e6
+				ret = DNS_R_CLOCKSKEW;
c2d1e6
+			else
c2d1e6
+				ret = DNS_R_TSIGERRORSET;
c2d1e6
+			goto cleanup_context;
c2d1e6
+		}
c2d1e6
 	}
c2d1e6
 
c2d1e6
 	msg->tsigstatus = dns_rcode_noerror;
c2d1e6
-	return (ISC_R_SUCCESS);
c2d1e6
+	msg->verified_sig = 1;
c2d1e6
+	ret = ISC_R_SUCCESS;
c2d1e6
 
c2d1e6
  cleanup_context:
c2d1e6
-	dst_context_destroy(&msg->tsigctx);
c2d1e6
+	if (msg->tsigctx != NULL)
c2d1e6
+		dst_context_destroy(&msg->tsigctx);
c2d1e6
 
c2d1e6
  cleanup_querystruct:
c2d1e6
 	dns_rdata_freestruct(&querytsig);
c2d1e6
 
c2d1e6
 	return (ret);
c2d1e6
-
c2d1e6
 }
c2d1e6
 
c2d1e6
 isc_result_t