xref: /openbsd-src/usr.bin/dig/lib/dns/tsig.c (revision e6c7c102cf5d9891f32552a42895134a59937045)
15185a700Sflorian /*
25185a700Sflorian  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian  *
45185a700Sflorian  * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian  * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian  * copyright notice and this permission notice appear in all copies.
75185a700Sflorian  *
85185a700Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian  * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian  */
165185a700Sflorian 
175185a700Sflorian /*
18*e6c7c102Sjsg  * $Id: tsig.c,v 1.15 2024/04/23 13:34:50 jsg Exp $
195185a700Sflorian  */
205185a700Sflorian /*! \file */
215185a700Sflorian 
225185a700Sflorian #include <stdlib.h>
235185a700Sflorian #include <string.h>		/* Required for HP/UX (and others?) */
24c576c3baSflorian #include <time.h>
25c576c3baSflorian 
265185a700Sflorian #include <isc/util.h>
27c576c3baSflorian #include <isc/buffer.h>
28c576c3baSflorian #include <isc/refcount.h>
295185a700Sflorian 
305185a700Sflorian #include <dns/keyvalues.h>
315185a700Sflorian #include <dns/log.h>
325185a700Sflorian #include <dns/message.h>
335185a700Sflorian #include <dns/rdata.h>
345185a700Sflorian #include <dns/rdatalist.h>
355185a700Sflorian #include <dns/rdataset.h>
365185a700Sflorian #include <dns/result.h>
375185a700Sflorian #include <dns/tsig.h>
385185a700Sflorian 
395185a700Sflorian #include <dst/result.h>
405185a700Sflorian 
415185a700Sflorian #define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
425185a700Sflorian #define algname_is_allocated(algname) \
435185a700Sflorian 	((algname) != dns_tsig_hmacsha1_name && \
445185a700Sflorian 	 (algname) != dns_tsig_hmacsha224_name && \
455185a700Sflorian 	 (algname) != dns_tsig_hmacsha256_name && \
465185a700Sflorian 	 (algname) != dns_tsig_hmacsha384_name && \
475185a700Sflorian 	 (algname) != dns_tsig_hmacsha512_name)
485185a700Sflorian 
495185a700Sflorian #define BADTIMELEN 6
505185a700Sflorian 
515185a700Sflorian static unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
525185a700Sflorian static unsigned char hmacsha1_offsets[] = { 0, 10 };
535185a700Sflorian static dns_name_t hmacsha1 =
545185a700Sflorian 	DNS_NAME_INITABSOLUTE(hmacsha1_ndata, hmacsha1_offsets);
555185a700Sflorian dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;
565185a700Sflorian 
575185a700Sflorian static unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
585185a700Sflorian static unsigned char hmacsha224_offsets[] = { 0, 12 };
595185a700Sflorian static dns_name_t hmacsha224 =
605185a700Sflorian 	DNS_NAME_INITABSOLUTE(hmacsha224_ndata, hmacsha224_offsets);
615185a700Sflorian dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;
625185a700Sflorian 
635185a700Sflorian static unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
645185a700Sflorian static unsigned char hmacsha256_offsets[] = { 0, 12 };
655185a700Sflorian static dns_name_t hmacsha256 =
665185a700Sflorian 	DNS_NAME_INITABSOLUTE(hmacsha256_ndata, hmacsha256_offsets);
675185a700Sflorian dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;
685185a700Sflorian 
695185a700Sflorian static unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
705185a700Sflorian static unsigned char hmacsha384_offsets[] = { 0, 12 };
715185a700Sflorian static dns_name_t hmacsha384 =
725185a700Sflorian 	DNS_NAME_INITABSOLUTE(hmacsha384_ndata, hmacsha384_offsets);
735185a700Sflorian dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;
745185a700Sflorian 
755185a700Sflorian static unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
765185a700Sflorian static unsigned char hmacsha512_offsets[] = { 0, 12 };
775185a700Sflorian static dns_name_t hmacsha512 =
785185a700Sflorian 	DNS_NAME_INITABSOLUTE(hmacsha512_ndata, hmacsha512_offsets);
795185a700Sflorian dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;
805185a700Sflorian 
815185a700Sflorian static isc_result_t
825185a700Sflorian tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
835185a700Sflorian 
845185a700Sflorian static void
855185a700Sflorian tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
8687f06ebfSflorian      __attribute__((__format__(__printf__, 3, 4)));
875185a700Sflorian 
885185a700Sflorian static void
895185a700Sflorian tsigkey_free(dns_tsigkey_t *key);
905185a700Sflorian 
915185a700Sflorian static void
tsig_log(dns_tsigkey_t * key,int level,const char * fmt,...)925185a700Sflorian tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
935185a700Sflorian 	va_list ap;
945185a700Sflorian 	char message[4096];
955185a700Sflorian 	char namestr[DNS_NAME_FORMATSIZE];
965185a700Sflorian 	char creatorstr[DNS_NAME_FORMATSIZE];
975185a700Sflorian 
981fb015a8Sflorian 	if (!isc_log_wouldlog(dns_lctx, level))
995185a700Sflorian 		return;
1005185a700Sflorian 	if (key != NULL) {
1015185a700Sflorian 		dns_name_format(&key->name, namestr, sizeof(namestr));
1025185a700Sflorian 	} else {
1035185a700Sflorian 		strlcpy(namestr, "<null>", sizeof(namestr));
1045185a700Sflorian 	}
1055185a700Sflorian 
1065185a700Sflorian 	if (key != NULL && key->generated && key->creator) {
1075185a700Sflorian 		dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
1085185a700Sflorian 	} else {
1095185a700Sflorian 		strlcpy(creatorstr, "<null>", sizeof(creatorstr));
1105185a700Sflorian 	}
1115185a700Sflorian 
1125185a700Sflorian 	va_start(ap, fmt);
1135185a700Sflorian 	vsnprintf(message, sizeof(message), fmt, ap);
1145185a700Sflorian 	va_end(ap);
1155185a700Sflorian 	if (key != NULL && key->generated) {
1165185a700Sflorian 		isc_log_write(dns_lctx,
1175185a700Sflorian 			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
1185185a700Sflorian 			      level, "tsig key '%s' (%s): %s",
1195185a700Sflorian 			      namestr, creatorstr, message);
1205185a700Sflorian 	} else {
1215185a700Sflorian 		isc_log_write(dns_lctx,
1225185a700Sflorian 			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
1235185a700Sflorian 			      level, "tsig key '%s': %s", namestr, message);
1245185a700Sflorian 	}
1255185a700Sflorian }
1265185a700Sflorian 
1275185a700Sflorian isc_result_t
dns_tsigkey_createfromkey(dns_name_t * name,dns_name_t * algorithm,dst_key_t * dstkey,int generated,dns_name_t * creator,time_t inception,time_t expire,dns_tsigkey_t ** key)1285185a700Sflorian dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
1291fb015a8Sflorian 			  dst_key_t *dstkey, int generated,
130c576c3baSflorian 			  dns_name_t *creator, time_t inception,
131c576c3baSflorian 			  time_t expire,
1325185a700Sflorian 			  dns_tsigkey_t **key)
1335185a700Sflorian {
1345185a700Sflorian 	dns_tsigkey_t *tkey;
1355185a700Sflorian 	isc_result_t ret;
1365185a700Sflorian 	unsigned int refs = 0;
1375185a700Sflorian 
1385185a700Sflorian 	REQUIRE(key == NULL || *key == NULL);
1395185a700Sflorian 	REQUIRE(name != NULL);
1405185a700Sflorian 	REQUIRE(algorithm != NULL);
1415185a700Sflorian 	REQUIRE(key != NULL);
1425185a700Sflorian 
1435185a700Sflorian 	tkey = (dns_tsigkey_t *) malloc(sizeof(dns_tsigkey_t));
1445185a700Sflorian 	if (tkey == NULL)
1455185a700Sflorian 		return (ISC_R_NOMEMORY);
1465185a700Sflorian 
1475185a700Sflorian 	dns_name_init(&tkey->name, NULL);
1485185a700Sflorian 	ret = dns_name_dup(name, &tkey->name);
1495185a700Sflorian 	if (ret != ISC_R_SUCCESS)
1505185a700Sflorian 		goto cleanup_key;
1515185a700Sflorian 	(void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
1525185a700Sflorian 
1535185a700Sflorian 	if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
1545185a700Sflorian 		tkey->algorithm = DNS_TSIG_HMACSHA1_NAME;
1555185a700Sflorian 		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) {
1565185a700Sflorian 			ret = DNS_R_BADALG;
1575185a700Sflorian 			goto cleanup_name;
1585185a700Sflorian 		}
1595185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
1605185a700Sflorian 		tkey->algorithm = DNS_TSIG_HMACSHA224_NAME;
1615185a700Sflorian 		if (dstkey != NULL &&
1625185a700Sflorian 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA224) {
1635185a700Sflorian 			ret = DNS_R_BADALG;
1645185a700Sflorian 			goto cleanup_name;
1655185a700Sflorian 		}
1665185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
1675185a700Sflorian 		tkey->algorithm = DNS_TSIG_HMACSHA256_NAME;
1685185a700Sflorian 		if (dstkey != NULL &&
1695185a700Sflorian 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA256) {
1705185a700Sflorian 			ret = DNS_R_BADALG;
1715185a700Sflorian 			goto cleanup_name;
1725185a700Sflorian 		}
1735185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
1745185a700Sflorian 		tkey->algorithm = DNS_TSIG_HMACSHA384_NAME;
1755185a700Sflorian 		if (dstkey != NULL &&
1765185a700Sflorian 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA384) {
1775185a700Sflorian 			ret = DNS_R_BADALG;
1785185a700Sflorian 			goto cleanup_name;
1795185a700Sflorian 		}
1805185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
1815185a700Sflorian 		tkey->algorithm = DNS_TSIG_HMACSHA512_NAME;
1825185a700Sflorian 		if (dstkey != NULL &&
1835185a700Sflorian 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA512) {
1845185a700Sflorian 			ret = DNS_R_BADALG;
1855185a700Sflorian 			goto cleanup_name;
1865185a700Sflorian 		}
1875185a700Sflorian 	} else {
1885185a700Sflorian 		if (dstkey != NULL) {
1895185a700Sflorian 			ret = DNS_R_BADALG;
1905185a700Sflorian 			goto cleanup_name;
1915185a700Sflorian 		}
1925185a700Sflorian 		tkey->algorithm = malloc(sizeof(dns_name_t));
1935185a700Sflorian 		if (tkey->algorithm == NULL) {
1945185a700Sflorian 			ret = ISC_R_NOMEMORY;
1955185a700Sflorian 			goto cleanup_name;
1965185a700Sflorian 		}
1975185a700Sflorian 		dns_name_init(tkey->algorithm, NULL);
1985185a700Sflorian 		ret = dns_name_dup(algorithm, tkey->algorithm);
1995185a700Sflorian 		if (ret != ISC_R_SUCCESS)
2005185a700Sflorian 			goto cleanup_algorithm;
2015185a700Sflorian 		(void)dns_name_downcase(tkey->algorithm, tkey->algorithm,
2025185a700Sflorian 					NULL);
2035185a700Sflorian 	}
2045185a700Sflorian 
2055185a700Sflorian 	if (creator != NULL) {
2065185a700Sflorian 		tkey->creator = malloc(sizeof(dns_name_t));
2075185a700Sflorian 		if (tkey->creator == NULL) {
2085185a700Sflorian 			ret = ISC_R_NOMEMORY;
2095185a700Sflorian 			goto cleanup_algorithm;
2105185a700Sflorian 		}
2115185a700Sflorian 		dns_name_init(tkey->creator, NULL);
2125185a700Sflorian 		ret = dns_name_dup(creator, tkey->creator);
2135185a700Sflorian 		if (ret != ISC_R_SUCCESS) {
2145185a700Sflorian 			free(tkey->creator);
2155185a700Sflorian 			goto cleanup_algorithm;
2165185a700Sflorian 		}
2175185a700Sflorian 	} else
2185185a700Sflorian 		tkey->creator = NULL;
2195185a700Sflorian 
2205185a700Sflorian 	tkey->key = NULL;
2215185a700Sflorian 	if (dstkey != NULL)
2225185a700Sflorian 		dst_key_attach(dstkey, &tkey->key);
2235185a700Sflorian 
2245185a700Sflorian 	if (key != NULL)
2255185a700Sflorian 		refs = 1;
2265185a700Sflorian 
2275185a700Sflorian 	ret = isc_refcount_init(&tkey->refs, refs);
2285185a700Sflorian 	if (ret != ISC_R_SUCCESS)
2295185a700Sflorian 		goto cleanup_creator;
2305185a700Sflorian 
2315185a700Sflorian 	tkey->generated = generated;
2325185a700Sflorian 	tkey->inception = inception;
2335185a700Sflorian 	tkey->expire = expire;
2345185a700Sflorian 	ISC_LINK_INIT(tkey, link);
2355185a700Sflorian 
2365185a700Sflorian 	/*
2375185a700Sflorian 	 * Ignore this if it's a GSS key, since the key size is meaningless.
2385185a700Sflorian 	 */
2395185a700Sflorian 	if (dstkey != NULL && dst_key_size(dstkey) < 64) {
2405185a700Sflorian 		char namestr[DNS_NAME_FORMATSIZE];
2415185a700Sflorian 		dns_name_format(name, namestr, sizeof(namestr));
2425185a700Sflorian 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
2435185a700Sflorian 			      DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
2445185a700Sflorian 			      "the key '%s' is too short to be secure",
2455185a700Sflorian 			      namestr);
2465185a700Sflorian 	}
2475185a700Sflorian 
2485185a700Sflorian 	if (key != NULL)
2495185a700Sflorian 		*key = tkey;
2505185a700Sflorian 
2515185a700Sflorian 	return (ISC_R_SUCCESS);
2525185a700Sflorian 
2535185a700Sflorian  cleanup_creator:
2545185a700Sflorian 	if (tkey->key != NULL)
2555185a700Sflorian 		dst_key_free(&tkey->key);
2565185a700Sflorian 	if (tkey->creator != NULL) {
2575185a700Sflorian 		dns_name_free(tkey->creator);
2585185a700Sflorian 		free(tkey->creator);
2595185a700Sflorian 	}
2605185a700Sflorian  cleanup_algorithm:
2615185a700Sflorian 	if (algname_is_allocated(tkey->algorithm)) {
2625185a700Sflorian 		if (dns_name_dynamic(tkey->algorithm))
2635185a700Sflorian 			dns_name_free(tkey->algorithm);
2645185a700Sflorian 		free(tkey->algorithm);
2655185a700Sflorian 	}
2665185a700Sflorian  cleanup_name:
2675185a700Sflorian 	dns_name_free(&tkey->name);
2685185a700Sflorian  cleanup_key:
2695185a700Sflorian 	free(tkey);
2705185a700Sflorian 
2715185a700Sflorian 	return (ret);
2725185a700Sflorian }
2735185a700Sflorian 
2745185a700Sflorian isc_result_t
dns_tsigkey_create(dns_name_t * name,dns_name_t * algorithm,unsigned char * secret,int length,int generated,dns_name_t * creator,time_t inception,time_t expire,dns_tsigkey_t ** key)2755185a700Sflorian dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
2761fb015a8Sflorian 		   unsigned char *secret, int length, int generated,
277c576c3baSflorian 		   dns_name_t *creator, time_t inception,
278c576c3baSflorian 		   time_t expire,
2795185a700Sflorian 		   dns_tsigkey_t **key)
2805185a700Sflorian {
2815185a700Sflorian 	dst_key_t *dstkey = NULL;
2825185a700Sflorian 	isc_result_t result;
2835185a700Sflorian 
2845185a700Sflorian 	REQUIRE(length >= 0);
2855185a700Sflorian 	if (length > 0)
2865185a700Sflorian 		REQUIRE(secret != NULL);
2875185a700Sflorian 
2885185a700Sflorian 	if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
2895185a700Sflorian 		if (secret != NULL) {
2905185a700Sflorian 			isc_buffer_t b;
2915185a700Sflorian 
2925185a700Sflorian 			isc_buffer_init(&b, secret, length);
2935185a700Sflorian 			isc_buffer_add(&b, length);
29427db9c2cSflorian 			result = dst_key_frombuffer(DST_ALG_HMACSHA1,
2955185a700Sflorian 						    DNS_KEYOWNER_ENTITY,
2965185a700Sflorian 						    DNS_KEYPROTO_DNSSEC,
2975185a700Sflorian 						    &b, &dstkey);
2985185a700Sflorian 			if (result != ISC_R_SUCCESS)
2995185a700Sflorian 				return (result);
3005185a700Sflorian 		}
3015185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
3025185a700Sflorian 		if (secret != NULL) {
3035185a700Sflorian 			isc_buffer_t b;
3045185a700Sflorian 
3055185a700Sflorian 			isc_buffer_init(&b, secret, length);
3065185a700Sflorian 			isc_buffer_add(&b, length);
30727db9c2cSflorian 			result = dst_key_frombuffer(DST_ALG_HMACSHA224,
3085185a700Sflorian 						    DNS_KEYOWNER_ENTITY,
3095185a700Sflorian 						    DNS_KEYPROTO_DNSSEC,
3105185a700Sflorian 						    &b, &dstkey);
3115185a700Sflorian 			if (result != ISC_R_SUCCESS)
3125185a700Sflorian 				return (result);
3135185a700Sflorian 		}
3145185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
3155185a700Sflorian 		if (secret != NULL) {
3165185a700Sflorian 			isc_buffer_t b;
3175185a700Sflorian 
3185185a700Sflorian 			isc_buffer_init(&b, secret, length);
3195185a700Sflorian 			isc_buffer_add(&b, length);
32027db9c2cSflorian 			result = dst_key_frombuffer(DST_ALG_HMACSHA256,
3215185a700Sflorian 						    DNS_KEYOWNER_ENTITY,
3225185a700Sflorian 						    DNS_KEYPROTO_DNSSEC,
3235185a700Sflorian 						    &b, &dstkey);
3245185a700Sflorian 			if (result != ISC_R_SUCCESS)
3255185a700Sflorian 				return (result);
3265185a700Sflorian 		}
3275185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
3285185a700Sflorian 		if (secret != NULL) {
3295185a700Sflorian 			isc_buffer_t b;
3305185a700Sflorian 
3315185a700Sflorian 			isc_buffer_init(&b, secret, length);
3325185a700Sflorian 			isc_buffer_add(&b, length);
33327db9c2cSflorian 			result = dst_key_frombuffer(DST_ALG_HMACSHA384,
3345185a700Sflorian 						    DNS_KEYOWNER_ENTITY,
3355185a700Sflorian 						    DNS_KEYPROTO_DNSSEC,
3365185a700Sflorian 						    &b, &dstkey);
3375185a700Sflorian 			if (result != ISC_R_SUCCESS)
3385185a700Sflorian 				return (result);
3395185a700Sflorian 		}
3405185a700Sflorian 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
3415185a700Sflorian 		if (secret != NULL) {
3425185a700Sflorian 			isc_buffer_t b;
3435185a700Sflorian 
3445185a700Sflorian 			isc_buffer_init(&b, secret, length);
3455185a700Sflorian 			isc_buffer_add(&b, length);
34627db9c2cSflorian 			result = dst_key_frombuffer(DST_ALG_HMACSHA512,
3475185a700Sflorian 						    DNS_KEYOWNER_ENTITY,
3485185a700Sflorian 						    DNS_KEYPROTO_DNSSEC,
3495185a700Sflorian 						    &b, &dstkey);
3505185a700Sflorian 			if (result != ISC_R_SUCCESS)
3515185a700Sflorian 				return (result);
3525185a700Sflorian 		}
3535185a700Sflorian 	} else if (length > 0)
3545185a700Sflorian 		return (DNS_R_BADALG);
3555185a700Sflorian 
3565185a700Sflorian 	result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
3575185a700Sflorian 					   generated, creator,
3585185a700Sflorian 					   inception, expire, key);
3595185a700Sflorian 	if (dstkey != NULL)
3605185a700Sflorian 		dst_key_free(&dstkey);
3615185a700Sflorian 	return (result);
3625185a700Sflorian }
3635185a700Sflorian 
3645185a700Sflorian void
dns_tsigkey_attach(dns_tsigkey_t * source,dns_tsigkey_t ** targetp)3655185a700Sflorian dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
3665185a700Sflorian 	REQUIRE(targetp != NULL && *targetp == NULL);
3675185a700Sflorian 
3685185a700Sflorian 	isc_refcount_increment(&source->refs, NULL);
3695185a700Sflorian 	*targetp = source;
3705185a700Sflorian }
3715185a700Sflorian 
3725185a700Sflorian static void
tsigkey_free(dns_tsigkey_t * key)3735185a700Sflorian tsigkey_free(dns_tsigkey_t *key) {
3745185a700Sflorian 	dns_name_free(&key->name);
3755185a700Sflorian 	if (algname_is_allocated(key->algorithm)) {
3765185a700Sflorian 		dns_name_free(key->algorithm);
3775185a700Sflorian 		free(key->algorithm);
3785185a700Sflorian 	}
3795185a700Sflorian 	if (key->key != NULL)
3805185a700Sflorian 		dst_key_free(&key->key);
3815185a700Sflorian 	if (key->creator != NULL) {
3825185a700Sflorian 		dns_name_free(key->creator);
3835185a700Sflorian 		free(key->creator);
3845185a700Sflorian 	}
3855185a700Sflorian 	isc_refcount_destroy(&key->refs);
3865185a700Sflorian 	free(key);
3875185a700Sflorian }
3885185a700Sflorian 
3895185a700Sflorian void
dns_tsigkey_detach(dns_tsigkey_t ** keyp)3905185a700Sflorian dns_tsigkey_detach(dns_tsigkey_t **keyp) {
3915185a700Sflorian 	dns_tsigkey_t *key;
3925185a700Sflorian 	unsigned int refs;
3935185a700Sflorian 
3945185a700Sflorian 	REQUIRE(keyp != NULL);
3955185a700Sflorian 
3965185a700Sflorian 	key = *keyp;
3975185a700Sflorian 	isc_refcount_decrement(&key->refs, &refs);
3985185a700Sflorian 
3995185a700Sflorian 	if (refs == 0)
4005185a700Sflorian 		tsigkey_free(key);
4015185a700Sflorian 
4025185a700Sflorian 	*keyp = NULL;
4035185a700Sflorian }
4045185a700Sflorian 
4055185a700Sflorian isc_result_t
dns_tsig_sign(dns_message_t * msg)4065185a700Sflorian dns_tsig_sign(dns_message_t *msg) {
4075185a700Sflorian 	dns_tsigkey_t *key;
4085185a700Sflorian 	dns_rdata_any_tsig_t tsig, querytsig;
4095185a700Sflorian 	unsigned char data[128];
4105185a700Sflorian 	isc_buffer_t databuf, sigbuf;
4115185a700Sflorian 	isc_buffer_t *dynbuf;
4125185a700Sflorian 	dns_name_t *owner;
4135185a700Sflorian 	dns_rdata_t *rdata = NULL;
4145185a700Sflorian 	dns_rdatalist_t *datalist;
4155185a700Sflorian 	dns_rdataset_t *dataset;
4165185a700Sflorian 	isc_region_t r;
417c576c3baSflorian 	time_t now;
4185185a700Sflorian 	dst_context_t *ctx = NULL;
4195185a700Sflorian 	isc_result_t ret;
4205185a700Sflorian 	unsigned char badtimedata[BADTIMELEN];
4215185a700Sflorian 	unsigned int sigsize = 0;
4221fb015a8Sflorian 	int response;
4235185a700Sflorian 
4245185a700Sflorian 	REQUIRE(msg != NULL);
4255185a700Sflorian 	key = dns_message_gettsigkey(msg);
4265185a700Sflorian 
4275185a700Sflorian 	/*
4285185a700Sflorian 	 * If this is a response, there should be a query tsig.
4295185a700Sflorian 	 */
4305185a700Sflorian 	response = is_response(msg);
4315185a700Sflorian 	if (response && msg->querytsig == NULL)
4325185a700Sflorian 		return (DNS_R_EXPECTEDTSIG);
4335185a700Sflorian 
4345185a700Sflorian 	dynbuf = NULL;
4355185a700Sflorian 
4365185a700Sflorian 	tsig.common.rdclass = dns_rdataclass_any;
4375185a700Sflorian 	tsig.common.rdtype = dns_rdatatype_tsig;
4385185a700Sflorian 	ISC_LINK_INIT(&tsig.common, link);
4395185a700Sflorian 	dns_name_init(&tsig.algorithm, NULL);
4405185a700Sflorian 	dns_name_clone(key->algorithm, &tsig.algorithm);
4415185a700Sflorian 
442c576c3baSflorian 	time(&now);
4435185a700Sflorian 	tsig.timesigned = now + msg->timeadjust;
4445185a700Sflorian 	tsig.fudge = DNS_TSIG_FUDGE;
4455185a700Sflorian 
4465185a700Sflorian 	tsig.originalid = msg->id;
4475185a700Sflorian 
4485185a700Sflorian 	isc_buffer_init(&databuf, data, sizeof(data));
4495185a700Sflorian 
4505185a700Sflorian 	if (response)
4515185a700Sflorian 		tsig.error = msg->querytsigstatus;
4525185a700Sflorian 	else
4535185a700Sflorian 		tsig.error = dns_rcode_noerror;
4545185a700Sflorian 
4555185a700Sflorian 	if (tsig.error != dns_tsigerror_badtime) {
4565185a700Sflorian 		tsig.otherlen = 0;
4575185a700Sflorian 		tsig.other = NULL;
4585185a700Sflorian 	} else {
4595185a700Sflorian 		isc_buffer_t otherbuf;
4605185a700Sflorian 
4615185a700Sflorian 		tsig.otherlen = BADTIMELEN;
4625185a700Sflorian 		tsig.other = badtimedata;
4635185a700Sflorian 		isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
4645185a700Sflorian 		isc_buffer_putuint48(&otherbuf, tsig.timesigned);
4655185a700Sflorian 	}
4665185a700Sflorian 
4675185a700Sflorian 	if ((key->key != NULL) &&
4685185a700Sflorian 	    (tsig.error != dns_tsigerror_badsig) &&
4695185a700Sflorian 	    (tsig.error != dns_tsigerror_badkey))
4705185a700Sflorian 	{
4715185a700Sflorian 		unsigned char header[DNS_MESSAGE_HEADERLEN];
4725185a700Sflorian 		isc_buffer_t headerbuf;
4735185a700Sflorian 		uint16_t digestbits;
4745185a700Sflorian 
4755185a700Sflorian 		/*
4765185a700Sflorian 		 * If it is a response, we assume that the request MAC
4775185a700Sflorian 		 * has validated at this point. This is why we include a
4785185a700Sflorian 		 * MAC length > 0 in the reply.
4795185a700Sflorian 		 */
4805185a700Sflorian 		ret = dst_context_create3(key->key,
4815185a700Sflorian 					  DNS_LOGCATEGORY_DNSSEC,
4821fb015a8Sflorian 					  1, &ctx);
4835185a700Sflorian 		if (ret != ISC_R_SUCCESS)
4845185a700Sflorian 			return (ret);
4855185a700Sflorian 
4865185a700Sflorian 		/*
4875185a700Sflorian 		 * If this is a response, digest the request's MAC.
4885185a700Sflorian 		 */
4895185a700Sflorian 		if (response) {
4905185a700Sflorian 			dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
4915185a700Sflorian 
4925185a700Sflorian 			INSIST(msg->verified_sig);
4935185a700Sflorian 
4945185a700Sflorian 			ret = dns_rdataset_first(msg->querytsig);
4955185a700Sflorian 			if (ret != ISC_R_SUCCESS)
4965185a700Sflorian 				goto cleanup_context;
4975185a700Sflorian 			dns_rdataset_current(msg->querytsig, &querytsigrdata);
498ee21177aSflorian 			ret = dns_rdata_tostruct_tsig(&querytsigrdata,
499ee21177aSflorian 						      &querytsig);
5005185a700Sflorian 			if (ret != ISC_R_SUCCESS)
5015185a700Sflorian 				goto cleanup_context;
5025185a700Sflorian 			isc_buffer_putuint16(&databuf, querytsig.siglen);
5035185a700Sflorian 			if (isc_buffer_availablelength(&databuf) <
5045185a700Sflorian 			    querytsig.siglen) {
5055185a700Sflorian 				ret = ISC_R_NOSPACE;
5065185a700Sflorian 				goto cleanup_context;
5075185a700Sflorian 			}
5085185a700Sflorian 			isc_buffer_putmem(&databuf, querytsig.signature,
5095185a700Sflorian 					  querytsig.siglen);
5105185a700Sflorian 			isc_buffer_usedregion(&databuf, &r);
5115185a700Sflorian 			ret = dst_context_adddata(ctx, &r);
5125185a700Sflorian 			if (ret != ISC_R_SUCCESS)
5135185a700Sflorian 				goto cleanup_context;
5145185a700Sflorian 		}
5155185a700Sflorian 
5165185a700Sflorian 		/*
5175185a700Sflorian 		 * Digest the header.
5185185a700Sflorian 		 */
5195185a700Sflorian 		isc_buffer_init(&headerbuf, header, sizeof(header));
5205185a700Sflorian 		dns_message_renderheader(msg, &headerbuf);
5215185a700Sflorian 		isc_buffer_usedregion(&headerbuf, &r);
5225185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
5235185a700Sflorian 		if (ret != ISC_R_SUCCESS)
5245185a700Sflorian 			goto cleanup_context;
5255185a700Sflorian 
5265185a700Sflorian 		/*
5275185a700Sflorian 		 * Digest the remainder of the message.
5285185a700Sflorian 		 */
5295185a700Sflorian 		isc_buffer_usedregion(msg->buffer, &r);
5305185a700Sflorian 		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
5315185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
5325185a700Sflorian 		if (ret != ISC_R_SUCCESS)
5335185a700Sflorian 			goto cleanup_context;
5345185a700Sflorian 
5355185a700Sflorian 		if (msg->tcp_continuation == 0) {
5365185a700Sflorian 			/*
5375185a700Sflorian 			 * Digest the name, class, ttl, alg.
5385185a700Sflorian 			 */
5395185a700Sflorian 			dns_name_toregion(&key->name, &r);
5405185a700Sflorian 			ret = dst_context_adddata(ctx, &r);
5415185a700Sflorian 			if (ret != ISC_R_SUCCESS)
5425185a700Sflorian 				goto cleanup_context;
5435185a700Sflorian 
5445185a700Sflorian 			isc_buffer_clear(&databuf);
5455185a700Sflorian 			isc_buffer_putuint16(&databuf, dns_rdataclass_any);
5465185a700Sflorian 			isc_buffer_putuint32(&databuf, 0); /* ttl */
5475185a700Sflorian 			isc_buffer_usedregion(&databuf, &r);
5485185a700Sflorian 			ret = dst_context_adddata(ctx, &r);
5495185a700Sflorian 			if (ret != ISC_R_SUCCESS)
5505185a700Sflorian 				goto cleanup_context;
5515185a700Sflorian 
5525185a700Sflorian 			dns_name_toregion(&tsig.algorithm, &r);
5535185a700Sflorian 			ret = dst_context_adddata(ctx, &r);
5545185a700Sflorian 			if (ret != ISC_R_SUCCESS)
5555185a700Sflorian 				goto cleanup_context;
5565185a700Sflorian 
5575185a700Sflorian 		}
5585185a700Sflorian 		/* Digest the timesigned and fudge */
5595185a700Sflorian 		isc_buffer_clear(&databuf);
5605185a700Sflorian 		if (tsig.error == dns_tsigerror_badtime) {
5615185a700Sflorian 			INSIST(response);
5625185a700Sflorian 			tsig.timesigned = querytsig.timesigned;
5635185a700Sflorian 		}
5645185a700Sflorian 		isc_buffer_putuint48(&databuf, tsig.timesigned);
5655185a700Sflorian 		isc_buffer_putuint16(&databuf, tsig.fudge);
5665185a700Sflorian 		isc_buffer_usedregion(&databuf, &r);
5675185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
5685185a700Sflorian 		if (ret != ISC_R_SUCCESS)
5695185a700Sflorian 			goto cleanup_context;
5705185a700Sflorian 
5715185a700Sflorian 		if (msg->tcp_continuation == 0) {
5725185a700Sflorian 			/*
5735185a700Sflorian 			 * Digest the error and other data length.
5745185a700Sflorian 			 */
5755185a700Sflorian 			isc_buffer_clear(&databuf);
5765185a700Sflorian 			isc_buffer_putuint16(&databuf, tsig.error);
5775185a700Sflorian 			isc_buffer_putuint16(&databuf, tsig.otherlen);
5785185a700Sflorian 
5795185a700Sflorian 			isc_buffer_usedregion(&databuf, &r);
5805185a700Sflorian 			ret = dst_context_adddata(ctx, &r);
5815185a700Sflorian 			if (ret != ISC_R_SUCCESS)
5825185a700Sflorian 				goto cleanup_context;
5835185a700Sflorian 
5845185a700Sflorian 			/*
5855185a700Sflorian 			 * Digest other data.
5865185a700Sflorian 			 */
5875185a700Sflorian 			if (tsig.otherlen > 0) {
5885185a700Sflorian 				r.length = tsig.otherlen;
5895185a700Sflorian 				r.base = tsig.other;
5905185a700Sflorian 				ret = dst_context_adddata(ctx, &r);
5915185a700Sflorian 				if (ret != ISC_R_SUCCESS)
5925185a700Sflorian 					goto cleanup_context;
5935185a700Sflorian 			}
5945185a700Sflorian 		}
5955185a700Sflorian 
5965185a700Sflorian 		ret = dst_key_sigsize(key->key, &sigsize);
5975185a700Sflorian 		if (ret != ISC_R_SUCCESS)
5985185a700Sflorian 			goto cleanup_context;
5995185a700Sflorian 		tsig.signature = (unsigned char *) malloc(sigsize);
6005185a700Sflorian 		if (tsig.signature == NULL) {
6015185a700Sflorian 			ret = ISC_R_NOMEMORY;
6025185a700Sflorian 			goto cleanup_context;
6035185a700Sflorian 		}
6045185a700Sflorian 
6055185a700Sflorian 		isc_buffer_init(&sigbuf, tsig.signature, sigsize);
6065185a700Sflorian 		ret = dst_context_sign(ctx, &sigbuf);
6075185a700Sflorian 		if (ret != ISC_R_SUCCESS)
6085185a700Sflorian 			goto cleanup_signature;
6095185a700Sflorian 		dst_context_destroy(&ctx);
6105185a700Sflorian 		digestbits = dst_key_getbits(key->key);
6115185a700Sflorian 		if (digestbits != 0) {
6125185a700Sflorian 			/*
6135185a700Sflorian 			 * XXXRAY: Is this correct? What is the
6145185a700Sflorian 			 * expected behavior when digestbits is not an
6155185a700Sflorian 			 * integral multiple of 8? It looks like bytes
6165185a700Sflorian 			 * should either be (digestbits/8) or
6175185a700Sflorian 			 * (digestbits+7)/8.
6185185a700Sflorian 			 *
6195185a700Sflorian 			 * In any case, for current algorithms,
6205185a700Sflorian 			 * digestbits are an integral multiple of 8, so
6215185a700Sflorian 			 * it has the same effect as (digestbits/8).
6225185a700Sflorian 			 */
6235185a700Sflorian 			unsigned int bytes = (digestbits + 1) / 8;
6245185a700Sflorian 			if (response && bytes < querytsig.siglen)
6255185a700Sflorian 				bytes = querytsig.siglen;
6265185a700Sflorian 			if (bytes > isc_buffer_usedlength(&sigbuf))
6275185a700Sflorian 				bytes = isc_buffer_usedlength(&sigbuf);
6285185a700Sflorian 			tsig.siglen = bytes;
6295185a700Sflorian 		} else
6305185a700Sflorian 			tsig.siglen = isc_buffer_usedlength(&sigbuf);
6315185a700Sflorian 	} else {
6325185a700Sflorian 		tsig.siglen = 0;
6335185a700Sflorian 		tsig.signature = NULL;
6345185a700Sflorian 	}
6355185a700Sflorian 
6365185a700Sflorian 	ret = dns_message_gettemprdata(msg, &rdata);
6375185a700Sflorian 	if (ret != ISC_R_SUCCESS)
6385185a700Sflorian 		goto cleanup_signature;
6395185a700Sflorian 	ret = isc_buffer_allocate(&dynbuf, 512);
6405185a700Sflorian 	if (ret != ISC_R_SUCCESS)
6415185a700Sflorian 		goto cleanup_rdata;
642a5525167Sflorian 	ret = dns_rdata_fromstruct_tsig(rdata, dns_rdataclass_any,
6435185a700Sflorian 				        dns_rdatatype_tsig, &tsig, dynbuf);
6445185a700Sflorian 	if (ret != ISC_R_SUCCESS)
6455185a700Sflorian 		goto cleanup_dynbuf;
6465185a700Sflorian 
6475185a700Sflorian 	dns_message_takebuffer(msg, &dynbuf);
6485185a700Sflorian 
6495185a700Sflorian 	if (tsig.signature != NULL) {
6505185a700Sflorian 		free(tsig.signature);
6515185a700Sflorian 		tsig.signature = NULL;
6525185a700Sflorian 	}
6535185a700Sflorian 
6545185a700Sflorian 	owner = NULL;
6555185a700Sflorian 	ret = dns_message_gettempname(msg, &owner);
6565185a700Sflorian 	if (ret != ISC_R_SUCCESS)
6575185a700Sflorian 		goto cleanup_rdata;
6585185a700Sflorian 	dns_name_init(owner, NULL);
6595185a700Sflorian 	ret = dns_name_dup(&key->name, owner);
6605185a700Sflorian 	if (ret != ISC_R_SUCCESS)
6615185a700Sflorian 		goto cleanup_owner;
6625185a700Sflorian 
6635185a700Sflorian 	datalist = NULL;
6645185a700Sflorian 	ret = dns_message_gettemprdatalist(msg, &datalist);
6655185a700Sflorian 	if (ret != ISC_R_SUCCESS)
6665185a700Sflorian 		goto cleanup_owner;
6675185a700Sflorian 	dataset = NULL;
6685185a700Sflorian 	ret = dns_message_gettemprdataset(msg, &dataset);
6695185a700Sflorian 	if (ret != ISC_R_SUCCESS)
6705185a700Sflorian 		goto cleanup_rdatalist;
6715185a700Sflorian 	datalist->rdclass = dns_rdataclass_any;
6725185a700Sflorian 	datalist->type = dns_rdatatype_tsig;
6735185a700Sflorian 	ISC_LIST_APPEND(datalist->rdata, rdata, link);
6745185a700Sflorian 	RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
6755185a700Sflorian 		      == ISC_R_SUCCESS);
6765185a700Sflorian 	msg->tsig = dataset;
6775185a700Sflorian 	msg->tsigname = owner;
6785185a700Sflorian 
6795185a700Sflorian 	/* Windows does not like the tsig name being compressed. */
6805185a700Sflorian 	msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
6815185a700Sflorian 
6825185a700Sflorian 	return (ISC_R_SUCCESS);
6835185a700Sflorian 
6845185a700Sflorian  cleanup_rdatalist:
6855185a700Sflorian 	dns_message_puttemprdatalist(msg, &datalist);
6865185a700Sflorian  cleanup_owner:
6875185a700Sflorian 	dns_message_puttempname(msg, &owner);
6885185a700Sflorian 	goto cleanup_rdata;
6895185a700Sflorian  cleanup_dynbuf:
6905185a700Sflorian 	isc_buffer_free(&dynbuf);
6915185a700Sflorian  cleanup_rdata:
6925185a700Sflorian 	dns_message_puttemprdata(msg, &rdata);
6935185a700Sflorian  cleanup_signature:
6945185a700Sflorian 	if (tsig.signature != NULL)
6955185a700Sflorian 		free(tsig.signature);
6965185a700Sflorian  cleanup_context:
6975185a700Sflorian 	if (ctx != NULL)
6985185a700Sflorian 		dst_context_destroy(&ctx);
6995185a700Sflorian 	return (ret);
7005185a700Sflorian }
7015185a700Sflorian 
7025185a700Sflorian isc_result_t
dns_tsig_verify(isc_buffer_t * source,dns_message_t * msg)7035185a700Sflorian dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg)
7045185a700Sflorian {
7055185a700Sflorian 	dns_rdata_any_tsig_t tsig, querytsig;
7065185a700Sflorian 	isc_region_t r, source_r, header_r, sig_r;
7075185a700Sflorian 	isc_buffer_t databuf;
7085185a700Sflorian 	unsigned char data[32];
7095185a700Sflorian 	dns_name_t *keyname;
7105185a700Sflorian 	dns_rdata_t rdata = DNS_RDATA_INIT;
711c576c3baSflorian 	time_t now;
7125185a700Sflorian 	isc_result_t ret;
7135185a700Sflorian 	dns_tsigkey_t *tsigkey;
7145185a700Sflorian 	dst_key_t *key = NULL;
7155185a700Sflorian 	unsigned char header[DNS_MESSAGE_HEADERLEN];
7165185a700Sflorian 	dst_context_t *ctx = NULL;
7175185a700Sflorian 	uint16_t addcount, id;
7185185a700Sflorian 	unsigned int siglen;
7195185a700Sflorian 	unsigned int alg;
7201fb015a8Sflorian 	int response;
7215185a700Sflorian 
7225185a700Sflorian 	REQUIRE(source != NULL);
7235185a700Sflorian 	tsigkey = dns_message_gettsigkey(msg);
7245185a700Sflorian 	response = is_response(msg);
7255185a700Sflorian 
7265185a700Sflorian 	msg->verify_attempted = 1;
7275185a700Sflorian 	msg->verified_sig = 0;
7285185a700Sflorian 	msg->tsigstatus = dns_tsigerror_badsig;
7295185a700Sflorian 
7305185a700Sflorian 	if (msg->tcp_continuation) {
7315185a700Sflorian 		if (tsigkey == NULL || msg->querytsig == NULL)
7325185a700Sflorian 			return (DNS_R_UNEXPECTEDTSIG);
7335185a700Sflorian 		return (tsig_verify_tcp(source, msg));
7345185a700Sflorian 	}
7355185a700Sflorian 
7365185a700Sflorian 	/*
7375185a700Sflorian 	 * There should be a TSIG record...
7385185a700Sflorian 	 */
7395185a700Sflorian 	if (msg->tsig == NULL)
7405185a700Sflorian 		return (DNS_R_EXPECTEDTSIG);
7415185a700Sflorian 
7425185a700Sflorian 	/*
7435185a700Sflorian 	 * If this is a response and there's no key or query TSIG, there
7445185a700Sflorian 	 * shouldn't be one on the response.
7455185a700Sflorian 	 */
7465185a700Sflorian 	if (response && (tsigkey == NULL || msg->querytsig == NULL))
7475185a700Sflorian 		return (DNS_R_UNEXPECTEDTSIG);
7485185a700Sflorian 
7495185a700Sflorian 	/*
7505185a700Sflorian 	 * If we're here, we know the message is well formed and contains a
7515185a700Sflorian 	 * TSIG record.
7525185a700Sflorian 	 */
7535185a700Sflorian 
7545185a700Sflorian 	keyname = msg->tsigname;
7555185a700Sflorian 	ret = dns_rdataset_first(msg->tsig);
7565185a700Sflorian 	if (ret != ISC_R_SUCCESS)
7575185a700Sflorian 		return (ret);
7585185a700Sflorian 	dns_rdataset_current(msg->tsig, &rdata);
759ee21177aSflorian 	ret = dns_rdata_tostruct_tsig(&rdata, &tsig);
7605185a700Sflorian 	if (ret != ISC_R_SUCCESS)
7615185a700Sflorian 		return (ret);
7625185a700Sflorian 	dns_rdata_reset(&rdata);
7635185a700Sflorian 	if (response) {
7645185a700Sflorian 		ret = dns_rdataset_first(msg->querytsig);
7655185a700Sflorian 		if (ret != ISC_R_SUCCESS)
7665185a700Sflorian 			return (ret);
7675185a700Sflorian 		dns_rdataset_current(msg->querytsig, &rdata);
768ee21177aSflorian 		ret = dns_rdata_tostruct_tsig(&rdata, &querytsig);
7695185a700Sflorian 		if (ret != ISC_R_SUCCESS)
7705185a700Sflorian 			return (ret);
7715185a700Sflorian 	}
7725185a700Sflorian 	/*
7735185a700Sflorian 	 * Do the key name and algorithm match that of the query?
7745185a700Sflorian 	 */
7755185a700Sflorian 	if (response &&
7765185a700Sflorian 	    (!dns_name_equal(keyname, &tsigkey->name) ||
7775185a700Sflorian 	     !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) {
7785185a700Sflorian 		msg->tsigstatus = dns_tsigerror_badkey;
7795185a700Sflorian 		tsig_log(msg->tsigkey, 2,
7805185a700Sflorian 			 "key name and algorithm do not match");
7815185a700Sflorian 		return (DNS_R_TSIGVERIFYFAILURE);
7825185a700Sflorian 	}
7835185a700Sflorian 
7845185a700Sflorian 	/*
7855185a700Sflorian 	 * Get the current time.
7865185a700Sflorian 	 */
787c576c3baSflorian 	time(&now);
7885185a700Sflorian 
7895185a700Sflorian 	/*
7905185a700Sflorian 	 * Find dns_tsigkey_t based on keyname.
7915185a700Sflorian 	 */
7925185a700Sflorian 	if (tsigkey == NULL) {
7935185a700Sflorian 		ret = ISC_R_NOTFOUND;
7945185a700Sflorian 		if (ret != ISC_R_SUCCESS) {
7955185a700Sflorian 			msg->tsigstatus = dns_tsigerror_badkey;
7965185a700Sflorian 			ret = dns_tsigkey_create(keyname, &tsig.algorithm,
7971fb015a8Sflorian 						 NULL, 0, 0, NULL,
7985185a700Sflorian 						 now, now,
7995185a700Sflorian 						 &msg->tsigkey);
8005185a700Sflorian 			if (ret != ISC_R_SUCCESS)
8015185a700Sflorian 				return (ret);
8025185a700Sflorian 			tsig_log(msg->tsigkey, 2, "unknown key");
8035185a700Sflorian 			return (DNS_R_TSIGVERIFYFAILURE);
8045185a700Sflorian 		}
8055185a700Sflorian 		msg->tsigkey = tsigkey;
8065185a700Sflorian 	}
8075185a700Sflorian 
8085185a700Sflorian 	key = tsigkey->key;
8095185a700Sflorian 
8105185a700Sflorian 	/*
8115185a700Sflorian 	 * Check digest length.
8125185a700Sflorian 	 */
8135185a700Sflorian 	alg = dst_key_alg(key);
8145185a700Sflorian 	ret = dst_key_sigsize(key, &siglen);
8155185a700Sflorian 	if (ret != ISC_R_SUCCESS)
8165185a700Sflorian 		return (ret);
8175185a700Sflorian 	if (
8185185a700Sflorian 	    alg == DST_ALG_HMACSHA1 ||
8195185a700Sflorian 	    alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
8205185a700Sflorian 	    alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
8215185a700Sflorian 	{
8225185a700Sflorian 		if (tsig.siglen > siglen) {
8235185a700Sflorian 			tsig_log(msg->tsigkey, 2, "signature length too big");
8245185a700Sflorian 			return (DNS_R_FORMERR);
8255185a700Sflorian 		}
8265185a700Sflorian 		if (tsig.siglen > 0 &&
8275185a700Sflorian 		    (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2)))
8285185a700Sflorian 		{
8295185a700Sflorian 			tsig_log(msg->tsigkey, 2,
8305185a700Sflorian 				 "signature length below minimum");
8315185a700Sflorian 			return (DNS_R_FORMERR);
8325185a700Sflorian 		}
8335185a700Sflorian 	}
8345185a700Sflorian 
8355185a700Sflorian 	if (tsig.siglen > 0) {
8365185a700Sflorian 		uint16_t addcount_n;
8375185a700Sflorian 
8385185a700Sflorian 		sig_r.base = tsig.signature;
8395185a700Sflorian 		sig_r.length = tsig.siglen;
8405185a700Sflorian 
8415185a700Sflorian 		ret = dst_context_create3(key,
8425185a700Sflorian 					  DNS_LOGCATEGORY_DNSSEC,
8431fb015a8Sflorian 					  0, &ctx);
8445185a700Sflorian 		if (ret != ISC_R_SUCCESS)
8455185a700Sflorian 			return (ret);
8465185a700Sflorian 
8475185a700Sflorian 		if (response) {
8485185a700Sflorian 			isc_buffer_init(&databuf, data, sizeof(data));
8495185a700Sflorian 			isc_buffer_putuint16(&databuf, querytsig.siglen);
8505185a700Sflorian 			isc_buffer_usedregion(&databuf, &r);
8515185a700Sflorian 			ret = dst_context_adddata(ctx, &r);
8525185a700Sflorian 			if (ret != ISC_R_SUCCESS)
8535185a700Sflorian 				goto cleanup_context;
8545185a700Sflorian 			if (querytsig.siglen > 0) {
8555185a700Sflorian 				r.length = querytsig.siglen;
8565185a700Sflorian 				r.base = querytsig.signature;
8575185a700Sflorian 				ret = dst_context_adddata(ctx, &r);
8585185a700Sflorian 				if (ret != ISC_R_SUCCESS)
8595185a700Sflorian 					goto cleanup_context;
8605185a700Sflorian 			}
8615185a700Sflorian 		}
8625185a700Sflorian 
8635185a700Sflorian 		/*
8645185a700Sflorian 		 * Extract the header.
8655185a700Sflorian 		 */
8665185a700Sflorian 		isc_buffer_usedregion(source, &r);
8675185a700Sflorian 		memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
8685185a700Sflorian 		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
8695185a700Sflorian 
8705185a700Sflorian 		/*
8715185a700Sflorian 		 * Decrement the additional field counter.
8725185a700Sflorian 		 */
8735185a700Sflorian 		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
8745185a700Sflorian 		addcount_n = ntohs(addcount);
8755185a700Sflorian 		addcount = htons((uint16_t)(addcount_n - 1));
8765185a700Sflorian 		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
8775185a700Sflorian 
8785185a700Sflorian 		/*
8795185a700Sflorian 		 * Put in the original id.
8805185a700Sflorian 		 */
8815185a700Sflorian 		id = htons(tsig.originalid);
8825185a700Sflorian 		memmove(&header[0], &id, 2);
8835185a700Sflorian 
8845185a700Sflorian 		/*
8855185a700Sflorian 		 * Digest the modified header.
8865185a700Sflorian 		 */
8875185a700Sflorian 		header_r.base = (unsigned char *) header;
8885185a700Sflorian 		header_r.length = DNS_MESSAGE_HEADERLEN;
8895185a700Sflorian 		ret = dst_context_adddata(ctx, &header_r);
8905185a700Sflorian 		if (ret != ISC_R_SUCCESS)
8915185a700Sflorian 			goto cleanup_context;
8925185a700Sflorian 
8935185a700Sflorian 		/*
8945185a700Sflorian 		 * Digest all non-TSIG records.
8955185a700Sflorian 		 */
8965185a700Sflorian 		isc_buffer_usedregion(source, &source_r);
8975185a700Sflorian 		r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
8985185a700Sflorian 		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
8995185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
9005185a700Sflorian 		if (ret != ISC_R_SUCCESS)
9015185a700Sflorian 			goto cleanup_context;
9025185a700Sflorian 
9035185a700Sflorian 		/*
9045185a700Sflorian 		 * Digest the key name.
9055185a700Sflorian 		 */
9065185a700Sflorian 		dns_name_toregion(&tsigkey->name, &r);
9075185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
9085185a700Sflorian 		if (ret != ISC_R_SUCCESS)
9095185a700Sflorian 			goto cleanup_context;
9105185a700Sflorian 
9115185a700Sflorian 		isc_buffer_init(&databuf, data, sizeof(data));
9125185a700Sflorian 		isc_buffer_putuint16(&databuf, tsig.common.rdclass);
9135185a700Sflorian 		isc_buffer_putuint32(&databuf, msg->tsig->ttl);
9145185a700Sflorian 		isc_buffer_usedregion(&databuf, &r);
9155185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
9165185a700Sflorian 		if (ret != ISC_R_SUCCESS)
9175185a700Sflorian 			goto cleanup_context;
9185185a700Sflorian 
9195185a700Sflorian 		/*
9205185a700Sflorian 		 * Digest the key algorithm.
9215185a700Sflorian 		 */
9225185a700Sflorian 		dns_name_toregion(tsigkey->algorithm, &r);
9235185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
9245185a700Sflorian 		if (ret != ISC_R_SUCCESS)
9255185a700Sflorian 			goto cleanup_context;
9265185a700Sflorian 
9275185a700Sflorian 		isc_buffer_clear(&databuf);
9285185a700Sflorian 		isc_buffer_putuint48(&databuf, tsig.timesigned);
9295185a700Sflorian 		isc_buffer_putuint16(&databuf, tsig.fudge);
9305185a700Sflorian 		isc_buffer_putuint16(&databuf, tsig.error);
9315185a700Sflorian 		isc_buffer_putuint16(&databuf, tsig.otherlen);
9325185a700Sflorian 		isc_buffer_usedregion(&databuf, &r);
9335185a700Sflorian 		ret = dst_context_adddata(ctx, &r);
9345185a700Sflorian 		if (ret != ISC_R_SUCCESS)
9355185a700Sflorian 			goto cleanup_context;
9365185a700Sflorian 
9375185a700Sflorian 		if (tsig.otherlen > 0) {
9385185a700Sflorian 			r.base = tsig.other;
9395185a700Sflorian 			r.length = tsig.otherlen;
9405185a700Sflorian 			ret = dst_context_adddata(ctx, &r);
9415185a700Sflorian 			if (ret != ISC_R_SUCCESS)
9425185a700Sflorian 				goto cleanup_context;
9435185a700Sflorian 		}
9445185a700Sflorian 
9455185a700Sflorian 		ret = dst_context_verify(ctx, &sig_r);
9465185a700Sflorian 		if (ret == DST_R_VERIFYFAILURE) {
9475185a700Sflorian 			ret = DNS_R_TSIGVERIFYFAILURE;
9485185a700Sflorian 			tsig_log(msg->tsigkey, 2,
9495185a700Sflorian 				 "signature failed to verify(1)");
9505185a700Sflorian 			goto cleanup_context;
9515185a700Sflorian 		} else if (ret != ISC_R_SUCCESS) {
9525185a700Sflorian 			goto cleanup_context;
9535185a700Sflorian 		}
9545185a700Sflorian 		msg->verified_sig = 1;
9555185a700Sflorian 	} else if (tsig.error != dns_tsigerror_badsig &&
9565185a700Sflorian 		   tsig.error != dns_tsigerror_badkey) {
9575185a700Sflorian 		tsig_log(msg->tsigkey, 2, "signature was empty");
9585185a700Sflorian 		return (DNS_R_TSIGVERIFYFAILURE);
9595185a700Sflorian 	}
9605185a700Sflorian 
9615185a700Sflorian 	/*
9625185a700Sflorian 	 * Here at this point, the MAC has been verified. Even if any of
9635185a700Sflorian 	 * the following code returns a TSIG error, the reply will be
9645185a700Sflorian 	 * signed and WILL always include the request MAC in the digest
9655185a700Sflorian 	 * computation.
9665185a700Sflorian 	 */
9675185a700Sflorian 
9685185a700Sflorian 	/*
9695185a700Sflorian 	 * Is the time ok?
9705185a700Sflorian 	 */
971c576c3baSflorian 	if (now + msg->timeadjust > (time_t)(tsig.timesigned + tsig.fudge)) {
9725185a700Sflorian 		msg->tsigstatus = dns_tsigerror_badtime;
9735185a700Sflorian 		tsig_log(msg->tsigkey, 2, "signature has expired");
9745185a700Sflorian 		ret = DNS_R_CLOCKSKEW;
9755185a700Sflorian 		goto cleanup_context;
976c576c3baSflorian 	} else if (now + msg->timeadjust < (time_t)(tsig.timesigned -
977c576c3baSflorian 	    tsig.fudge)) {
9785185a700Sflorian 		msg->tsigstatus = dns_tsigerror_badtime;
9795185a700Sflorian 		tsig_log(msg->tsigkey, 2, "signature is in the future");
9805185a700Sflorian 		ret = DNS_R_CLOCKSKEW;
9815185a700Sflorian 		goto cleanup_context;
9825185a700Sflorian 	}
9835185a700Sflorian 
9845185a700Sflorian 	if (
9855185a700Sflorian 	    alg == DST_ALG_HMACSHA1 ||
9865185a700Sflorian 	    alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
9875185a700Sflorian 	    alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512)
9885185a700Sflorian 	{
9895185a700Sflorian 		uint16_t digestbits = dst_key_getbits(key);
9905185a700Sflorian 
9915185a700Sflorian 		/*
9925185a700Sflorian 		 * XXXRAY: Is this correct? What is the expected
9935185a700Sflorian 		 * behavior when digestbits is not an integral multiple
9945185a700Sflorian 		 * of 8? It looks like bytes should either be
9955185a700Sflorian 		 * (digestbits/8) or (digestbits+7)/8.
9965185a700Sflorian 		 *
9975185a700Sflorian 		 * In any case, for current algorithms, digestbits are
9985185a700Sflorian 		 * an integral multiple of 8, so it has the same effect
9995185a700Sflorian 		 * as (digestbits/8).
10005185a700Sflorian 		 */
10015185a700Sflorian 		if (tsig.siglen > 0 && digestbits != 0 &&
10025185a700Sflorian 		    tsig.siglen < ((digestbits + 1) / 8))
10035185a700Sflorian 		{
10045185a700Sflorian 			msg->tsigstatus = dns_tsigerror_badtrunc;
10055185a700Sflorian 			tsig_log(msg->tsigkey, 2,
10065185a700Sflorian 				 "truncated signature length too small");
10075185a700Sflorian 			ret = DNS_R_TSIGVERIFYFAILURE;
10085185a700Sflorian 			goto cleanup_context;
10095185a700Sflorian 		}
10105185a700Sflorian 		if (tsig.siglen > 0 && digestbits == 0 &&
10115185a700Sflorian 		    tsig.siglen < siglen)
10125185a700Sflorian 		{
10135185a700Sflorian 			msg->tsigstatus = dns_tsigerror_badtrunc;
10145185a700Sflorian 			tsig_log(msg->tsigkey, 2, "signature length too small");
10155185a700Sflorian 			ret = DNS_R_TSIGVERIFYFAILURE;
10165185a700Sflorian 			goto cleanup_context;
10175185a700Sflorian 		}
10185185a700Sflorian 	}
10195185a700Sflorian 
10205185a700Sflorian 	if (tsig.error != dns_rcode_noerror) {
10215185a700Sflorian 		msg->tsigstatus = tsig.error;
10225185a700Sflorian 		if (tsig.error == dns_tsigerror_badtime)
10235185a700Sflorian 			ret = DNS_R_CLOCKSKEW;
10245185a700Sflorian 		else
10255185a700Sflorian 			ret = DNS_R_TSIGERRORSET;
10265185a700Sflorian 		goto cleanup_context;
10275185a700Sflorian 	}
10285185a700Sflorian 
10295185a700Sflorian 	msg->tsigstatus = dns_rcode_noerror;
10305185a700Sflorian 	ret = ISC_R_SUCCESS;
10315185a700Sflorian 
10325185a700Sflorian  cleanup_context:
10335185a700Sflorian 	if (ctx != NULL)
10345185a700Sflorian 		dst_context_destroy(&ctx);
10355185a700Sflorian 
10365185a700Sflorian 	return (ret);
10375185a700Sflorian }
10385185a700Sflorian 
10395185a700Sflorian static isc_result_t
tsig_verify_tcp(isc_buffer_t * source,dns_message_t * msg)10405185a700Sflorian tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
10415185a700Sflorian 	dns_rdata_any_tsig_t tsig, querytsig;
10425185a700Sflorian 	isc_region_t r, source_r, header_r, sig_r;
10435185a700Sflorian 	isc_buffer_t databuf;
10445185a700Sflorian 	unsigned char data[32];
10455185a700Sflorian 	dns_name_t *keyname;
10465185a700Sflorian 	dns_rdata_t rdata = DNS_RDATA_INIT;
1047c576c3baSflorian 	time_t now;
10485185a700Sflorian 	isc_result_t ret;
10495185a700Sflorian 	dns_tsigkey_t *tsigkey;
10505185a700Sflorian 	dst_key_t *key = NULL;
10515185a700Sflorian 	unsigned char header[DNS_MESSAGE_HEADERLEN];
10525185a700Sflorian 	uint16_t addcount, id;
10531fb015a8Sflorian 	int has_tsig = 0;
10545185a700Sflorian 	unsigned int siglen;
10555185a700Sflorian 	unsigned int alg;
10565185a700Sflorian 
10575185a700Sflorian 	REQUIRE(source != NULL);
10585185a700Sflorian 	REQUIRE(msg != NULL);
10595185a700Sflorian 	REQUIRE(dns_message_gettsigkey(msg) != NULL);
10605185a700Sflorian 	REQUIRE(msg->tcp_continuation == 1);
10615185a700Sflorian 	REQUIRE(msg->querytsig != NULL);
10625185a700Sflorian 
10635185a700Sflorian 	msg->verified_sig = 0;
10645185a700Sflorian 	msg->tsigstatus = dns_tsigerror_badsig;
10655185a700Sflorian 
10665185a700Sflorian 	if (!is_response(msg))
10675185a700Sflorian 		return (DNS_R_EXPECTEDRESPONSE);
10685185a700Sflorian 
10695185a700Sflorian 	tsigkey = dns_message_gettsigkey(msg);
10705185a700Sflorian 	key = tsigkey->key;
10715185a700Sflorian 
10725185a700Sflorian 	/*
10735185a700Sflorian 	 * Extract and parse the previous TSIG
10745185a700Sflorian 	 */
10755185a700Sflorian 	ret = dns_rdataset_first(msg->querytsig);
10765185a700Sflorian 	if (ret != ISC_R_SUCCESS)
10775185a700Sflorian 		return (ret);
10785185a700Sflorian 	dns_rdataset_current(msg->querytsig, &rdata);
1079ee21177aSflorian 	ret = dns_rdata_tostruct_tsig(&rdata, &querytsig);
10805185a700Sflorian 	if (ret != ISC_R_SUCCESS)
10815185a700Sflorian 		return (ret);
10825185a700Sflorian 	dns_rdata_reset(&rdata);
10835185a700Sflorian 
10845185a700Sflorian 	/*
10855185a700Sflorian 	 * If there is a TSIG in this message, do some checks.
10865185a700Sflorian 	 */
10875185a700Sflorian 	if (msg->tsig != NULL) {
10881fb015a8Sflorian 		has_tsig = 1;
10895185a700Sflorian 
10905185a700Sflorian 		keyname = msg->tsigname;
10915185a700Sflorian 		ret = dns_rdataset_first(msg->tsig);
10925185a700Sflorian 		if (ret != ISC_R_SUCCESS)
10935185a700Sflorian 			goto cleanup_querystruct;
10945185a700Sflorian 		dns_rdataset_current(msg->tsig, &rdata);
1095ee21177aSflorian 		ret = dns_rdata_tostruct_tsig(&rdata, &tsig);
10965185a700Sflorian 		if (ret != ISC_R_SUCCESS)
10975185a700Sflorian 			goto cleanup_querystruct;
10985185a700Sflorian 
10995185a700Sflorian 		/*
11005185a700Sflorian 		 * Do the key name and algorithm match that of the query?
11015185a700Sflorian 		 */
11025185a700Sflorian 		if (!dns_name_equal(keyname, &tsigkey->name) ||
11035185a700Sflorian 		    !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))
11045185a700Sflorian 		{
11055185a700Sflorian 			msg->tsigstatus = dns_tsigerror_badkey;
11065185a700Sflorian 			ret = DNS_R_TSIGVERIFYFAILURE;
11075185a700Sflorian 			tsig_log(msg->tsigkey, 2,
11085185a700Sflorian 				 "key name and algorithm do not match");
11095185a700Sflorian 			goto cleanup_querystruct;
11105185a700Sflorian 		}
11115185a700Sflorian 
11125185a700Sflorian 		/*
11135185a700Sflorian 		 * Check digest length.
11145185a700Sflorian 		 */
11155185a700Sflorian 		alg = dst_key_alg(key);
11165185a700Sflorian 		ret = dst_key_sigsize(key, &siglen);
11175185a700Sflorian 		if (ret != ISC_R_SUCCESS)
11185185a700Sflorian 			goto cleanup_querystruct;
11195185a700Sflorian 		if (
11205185a700Sflorian 			alg == DST_ALG_HMACSHA1 ||
11215185a700Sflorian 			alg == DST_ALG_HMACSHA224 ||
11225185a700Sflorian 			alg == DST_ALG_HMACSHA256 ||
11235185a700Sflorian 			alg == DST_ALG_HMACSHA384 ||
11245185a700Sflorian 			alg == DST_ALG_HMACSHA512)
11255185a700Sflorian 		{
11265185a700Sflorian 			if (tsig.siglen > siglen) {
11275185a700Sflorian 				tsig_log(tsigkey, 2,
11285185a700Sflorian 					 "signature length too big");
11295185a700Sflorian 				ret = DNS_R_FORMERR;
11305185a700Sflorian 				goto cleanup_querystruct;
11315185a700Sflorian 			}
11325185a700Sflorian 			if (tsig.siglen > 0 &&
11335185a700Sflorian 			    (tsig.siglen < 10 ||
11345185a700Sflorian 			     tsig.siglen < ((siglen + 1) / 2)))
11355185a700Sflorian 			{
11365185a700Sflorian 				tsig_log(tsigkey, 2,
11375185a700Sflorian 					 "signature length below minimum");
11385185a700Sflorian 				ret = DNS_R_FORMERR;
11395185a700Sflorian 				goto cleanup_querystruct;
11405185a700Sflorian 			}
11415185a700Sflorian 		}
11425185a700Sflorian 	}
11435185a700Sflorian 
11445185a700Sflorian 	if (msg->tsigctx == NULL) {
11455185a700Sflorian 		ret = dst_context_create3(key,
11465185a700Sflorian 					  DNS_LOGCATEGORY_DNSSEC,
11471fb015a8Sflorian 					  0, &msg->tsigctx);
11485185a700Sflorian 		if (ret != ISC_R_SUCCESS)
11495185a700Sflorian 			goto cleanup_querystruct;
11505185a700Sflorian 
11515185a700Sflorian 		/*
11525185a700Sflorian 		 * Digest the length of the query signature
11535185a700Sflorian 		 */
11545185a700Sflorian 		isc_buffer_init(&databuf, data, sizeof(data));
11555185a700Sflorian 		isc_buffer_putuint16(&databuf, querytsig.siglen);
11565185a700Sflorian 		isc_buffer_usedregion(&databuf, &r);
11575185a700Sflorian 		ret = dst_context_adddata(msg->tsigctx, &r);
11585185a700Sflorian 		if (ret != ISC_R_SUCCESS)
11595185a700Sflorian 			goto cleanup_context;
11605185a700Sflorian 
11615185a700Sflorian 		/*
11625185a700Sflorian 		 * Digest the data of the query signature
11635185a700Sflorian 		 */
11645185a700Sflorian 		if (querytsig.siglen > 0) {
11655185a700Sflorian 			r.length = querytsig.siglen;
11665185a700Sflorian 			r.base = querytsig.signature;
11675185a700Sflorian 			ret = dst_context_adddata(msg->tsigctx, &r);
11685185a700Sflorian 			if (ret != ISC_R_SUCCESS)
11695185a700Sflorian 				goto cleanup_context;
11705185a700Sflorian 		}
11715185a700Sflorian 	}
11725185a700Sflorian 
11735185a700Sflorian 	/*
11745185a700Sflorian 	 * Extract the header.
11755185a700Sflorian 	 */
11765185a700Sflorian 	isc_buffer_usedregion(source, &r);
11775185a700Sflorian 	memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
11785185a700Sflorian 	isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
11795185a700Sflorian 
11805185a700Sflorian 	/*
11815185a700Sflorian 	 * Decrement the additional field counter if necessary.
11825185a700Sflorian 	 */
11835185a700Sflorian 	if (has_tsig) {
11845185a700Sflorian 		uint16_t addcount_n;
11855185a700Sflorian 
11865185a700Sflorian 		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
11875185a700Sflorian 		addcount_n = ntohs(addcount);
11885185a700Sflorian 		addcount = htons((uint16_t)(addcount_n - 1));
11895185a700Sflorian 		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
11905185a700Sflorian 
11915185a700Sflorian 		/*
11925185a700Sflorian 		 * Put in the original id.
11935185a700Sflorian 		 *
11945185a700Sflorian 		 * XXX Can TCP transfers be forwarded?  How would that
11955185a700Sflorian 		 * work?
11965185a700Sflorian 		 */
11975185a700Sflorian 		id = htons(tsig.originalid);
11985185a700Sflorian 		memmove(&header[0], &id, 2);
11995185a700Sflorian 	}
12005185a700Sflorian 
12015185a700Sflorian 	/*
12025185a700Sflorian 	 * Digest the modified header.
12035185a700Sflorian 	 */
12045185a700Sflorian 	header_r.base = (unsigned char *) header;
12055185a700Sflorian 	header_r.length = DNS_MESSAGE_HEADERLEN;
12065185a700Sflorian 	ret = dst_context_adddata(msg->tsigctx, &header_r);
12075185a700Sflorian 	if (ret != ISC_R_SUCCESS)
12085185a700Sflorian 		goto cleanup_context;
12095185a700Sflorian 
12105185a700Sflorian 	/*
12115185a700Sflorian 	 * Digest all non-TSIG records.
12125185a700Sflorian 	 */
12135185a700Sflorian 	isc_buffer_usedregion(source, &source_r);
12145185a700Sflorian 	r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
12155185a700Sflorian 	if (has_tsig)
12165185a700Sflorian 		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
12175185a700Sflorian 	else
12185185a700Sflorian 		r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
12195185a700Sflorian 	ret = dst_context_adddata(msg->tsigctx, &r);
12205185a700Sflorian 	if (ret != ISC_R_SUCCESS)
12215185a700Sflorian 		goto cleanup_context;
12225185a700Sflorian 
12235185a700Sflorian 	/*
12245185a700Sflorian 	 * Digest the time signed and fudge.
12255185a700Sflorian 	 */
12265185a700Sflorian 	if (has_tsig) {
12275185a700Sflorian 		isc_buffer_init(&databuf, data, sizeof(data));
12285185a700Sflorian 		isc_buffer_putuint48(&databuf, tsig.timesigned);
12295185a700Sflorian 		isc_buffer_putuint16(&databuf, tsig.fudge);
12305185a700Sflorian 		isc_buffer_usedregion(&databuf, &r);
12315185a700Sflorian 		ret = dst_context_adddata(msg->tsigctx, &r);
12325185a700Sflorian 		if (ret != ISC_R_SUCCESS)
12335185a700Sflorian 			goto cleanup_context;
12345185a700Sflorian 
12355185a700Sflorian 		sig_r.base = tsig.signature;
12365185a700Sflorian 		sig_r.length = tsig.siglen;
12375185a700Sflorian 		if (tsig.siglen == 0) {
12385185a700Sflorian 			if (tsig.error != dns_rcode_noerror) {
12395185a700Sflorian 				msg->tsigstatus = tsig.error;
12405185a700Sflorian 				if (tsig.error == dns_tsigerror_badtime) {
12415185a700Sflorian 					ret = DNS_R_CLOCKSKEW;
12425185a700Sflorian 				} else {
12435185a700Sflorian 					ret = DNS_R_TSIGERRORSET;
12445185a700Sflorian 				}
12455185a700Sflorian 			} else {
12465185a700Sflorian 				tsig_log(msg->tsigkey, 2,
12475185a700Sflorian 					 "signature is empty");
12485185a700Sflorian 				ret = DNS_R_TSIGVERIFYFAILURE;
12495185a700Sflorian 			}
12505185a700Sflorian 			goto cleanup_context;
12515185a700Sflorian 		}
12525185a700Sflorian 
12535185a700Sflorian 		ret = dst_context_verify(msg->tsigctx, &sig_r);
12545185a700Sflorian 		if (ret == DST_R_VERIFYFAILURE) {
12555185a700Sflorian 			tsig_log(msg->tsigkey, 2,
12565185a700Sflorian 				 "signature failed to verify(2)");
12575185a700Sflorian 			ret = DNS_R_TSIGVERIFYFAILURE;
12585185a700Sflorian 			goto cleanup_context;
12595185a700Sflorian 		} else if (ret != ISC_R_SUCCESS) {
12605185a700Sflorian 			goto cleanup_context;
12615185a700Sflorian 		}
12625185a700Sflorian 		msg->verified_sig = 1;
12635185a700Sflorian 
12645185a700Sflorian 		/*
12655185a700Sflorian 		 * Here at this point, the MAC has been verified. Even
12665185a700Sflorian 		 * if any of the following code returns a TSIG error,
12675185a700Sflorian 		 * the reply will be signed and WILL always include the
12685185a700Sflorian 		 * request MAC in the digest computation.
12695185a700Sflorian 		 */
12705185a700Sflorian 
12715185a700Sflorian 		/*
12725185a700Sflorian 		 * Is the time ok?
12735185a700Sflorian 		 */
1274c576c3baSflorian 		time(&now);
12755185a700Sflorian 
1276c576c3baSflorian 		if (now + msg->timeadjust > (time_t)(tsig.timesigned +
1277c576c3baSflorian 		    tsig.fudge)) {
12785185a700Sflorian 			msg->tsigstatus = dns_tsigerror_badtime;
12795185a700Sflorian 			tsig_log(msg->tsigkey, 2, "signature has expired");
12805185a700Sflorian 			ret = DNS_R_CLOCKSKEW;
12815185a700Sflorian 			goto cleanup_context;
1282c576c3baSflorian 		} else if (now + msg->timeadjust < (time_t)(tsig.timesigned -
1283c576c3baSflorian 		    tsig.fudge)) {
12845185a700Sflorian 			msg->tsigstatus = dns_tsigerror_badtime;
12855185a700Sflorian 			tsig_log(msg->tsigkey, 2,
12865185a700Sflorian 				 "signature is in the future");
12875185a700Sflorian 			ret = DNS_R_CLOCKSKEW;
12885185a700Sflorian 			goto cleanup_context;
12895185a700Sflorian 		}
12905185a700Sflorian 
12915185a700Sflorian 		alg = dst_key_alg(key);
12925185a700Sflorian 		ret = dst_key_sigsize(key, &siglen);
12935185a700Sflorian 		if (ret != ISC_R_SUCCESS)
12945185a700Sflorian 			goto cleanup_context;
12955185a700Sflorian 		if (
12965185a700Sflorian 			alg == DST_ALG_HMACSHA1 ||
12975185a700Sflorian 			alg == DST_ALG_HMACSHA224 ||
12985185a700Sflorian 			alg == DST_ALG_HMACSHA256 ||
12995185a700Sflorian 			alg == DST_ALG_HMACSHA384 ||
13005185a700Sflorian 			alg == DST_ALG_HMACSHA512)
13015185a700Sflorian 		{
13025185a700Sflorian 			uint16_t digestbits = dst_key_getbits(key);
13035185a700Sflorian 
13045185a700Sflorian 			/*
13055185a700Sflorian 			 * XXXRAY: Is this correct? What is the
13065185a700Sflorian 			 * expected behavior when digestbits is not an
13075185a700Sflorian 			 * integral multiple of 8? It looks like bytes
13085185a700Sflorian 			 * should either be (digestbits/8) or
13095185a700Sflorian 			 * (digestbits+7)/8.
13105185a700Sflorian 			 *
13115185a700Sflorian 			 * In any case, for current algorithms,
13125185a700Sflorian 			 * digestbits are an integral multiple of 8, so
13135185a700Sflorian 			 * it has the same effect as (digestbits/8).
13145185a700Sflorian 			 */
13155185a700Sflorian 			if (tsig.siglen > 0 && digestbits != 0 &&
13165185a700Sflorian 			    tsig.siglen < ((digestbits + 1) / 8))
13175185a700Sflorian 			{
13185185a700Sflorian 				msg->tsigstatus = dns_tsigerror_badtrunc;
13195185a700Sflorian 				tsig_log(msg->tsigkey, 2,
13205185a700Sflorian 					 "truncated signature length "
13215185a700Sflorian 					 "too small");
13225185a700Sflorian 				ret = DNS_R_TSIGVERIFYFAILURE;
13235185a700Sflorian 				goto cleanup_context;
13245185a700Sflorian 			}
13255185a700Sflorian 			if (tsig.siglen > 0 && digestbits == 0 &&
13265185a700Sflorian 			    tsig.siglen < siglen)
13275185a700Sflorian 			{
13285185a700Sflorian 				msg->tsigstatus = dns_tsigerror_badtrunc;
13295185a700Sflorian 				tsig_log(msg->tsigkey, 2,
13305185a700Sflorian 					 "signature length too small");
13315185a700Sflorian 				ret = DNS_R_TSIGVERIFYFAILURE;
13325185a700Sflorian 				goto cleanup_context;
13335185a700Sflorian 			}
13345185a700Sflorian 		}
13355185a700Sflorian 
13365185a700Sflorian 		if (tsig.error != dns_rcode_noerror) {
13375185a700Sflorian 			msg->tsigstatus = tsig.error;
13385185a700Sflorian 			if (tsig.error == dns_tsigerror_badtime)
13395185a700Sflorian 				ret = DNS_R_CLOCKSKEW;
13405185a700Sflorian 			else
13415185a700Sflorian 				ret = DNS_R_TSIGERRORSET;
13425185a700Sflorian 			goto cleanup_context;
13435185a700Sflorian 		}
13445185a700Sflorian 	}
13455185a700Sflorian 
13465185a700Sflorian 	msg->tsigstatus = dns_rcode_noerror;
13475185a700Sflorian 	ret = ISC_R_SUCCESS;
13485185a700Sflorian 
13495185a700Sflorian  cleanup_context:
13505185a700Sflorian 	/*
13515185a700Sflorian 	 * Except in error conditions, don't destroy the DST context
13525185a700Sflorian 	 * for unsigned messages; it is a running sum till the next
13535185a700Sflorian 	 * TSIG signed message.
13545185a700Sflorian 	 */
13555185a700Sflorian 	if ((ret != ISC_R_SUCCESS || has_tsig) && msg->tsigctx != NULL) {
13565185a700Sflorian 		dst_context_destroy(&msg->tsigctx);
13575185a700Sflorian 	}
13585185a700Sflorian 
13595185a700Sflorian  cleanup_querystruct:
1360a0b66ce4Sflorian 	dns_rdata_freestruct_tsig(&querytsig);
13615185a700Sflorian 
13625185a700Sflorian 	return (ret);
13635185a700Sflorian }
13645185a700Sflorian 
1365