xref: /netbsd/external/mpl/bind/dist/lib/dns/tsig.c (revision 4ac1c27e)
1*4ac1c27eSchristos /*	$NetBSD: tsig.c,v 1.12 2023/01/25 21:43:30 christos Exp $	*/
2e2b1b9c0Schristos 
3e2b1b9c0Schristos /*
4e2b1b9c0Schristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5e2b1b9c0Schristos  *
6c0b5d9fbSchristos  * SPDX-License-Identifier: MPL-2.0
7c0b5d9fbSchristos  *
8e2b1b9c0Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
9e2b1b9c0Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
1073584a28Schristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11e2b1b9c0Schristos  *
12e2b1b9c0Schristos  * See the COPYRIGHT file distributed with this work for additional
13e2b1b9c0Schristos  * information regarding copyright ownership.
14e2b1b9c0Schristos  */
15e2b1b9c0Schristos 
16e2b1b9c0Schristos /*! \file */
17f2e20987Schristos 
18f2e20987Schristos #include <inttypes.h>
19f2e20987Schristos #include <stdbool.h>
20e2b1b9c0Schristos #include <stdlib.h>
21e2b1b9c0Schristos 
22e2b1b9c0Schristos #include <isc/buffer.h>
23e2b1b9c0Schristos #include <isc/mem.h>
24e2b1b9c0Schristos #include <isc/print.h>
25e2b1b9c0Schristos #include <isc/refcount.h>
26e2b1b9c0Schristos #include <isc/serial.h>
27e2b1b9c0Schristos #include <isc/string.h> /* Required for HP/UX (and others?) */
28e2b1b9c0Schristos #include <isc/time.h>
299742fdb4Schristos #include <isc/util.h>
30e2b1b9c0Schristos 
31e2b1b9c0Schristos #include <pk11/site.h>
32e2b1b9c0Schristos 
339742fdb4Schristos #include <dns/fixedname.h>
34e2b1b9c0Schristos #include <dns/keyvalues.h>
35e2b1b9c0Schristos #include <dns/log.h>
36e2b1b9c0Schristos #include <dns/message.h>
37e2b1b9c0Schristos #include <dns/rbt.h>
38e2b1b9c0Schristos #include <dns/rdata.h>
39e2b1b9c0Schristos #include <dns/rdatalist.h>
40e2b1b9c0Schristos #include <dns/rdataset.h>
41e2b1b9c0Schristos #include <dns/rdatastruct.h>
42e2b1b9c0Schristos #include <dns/result.h>
43e2b1b9c0Schristos #include <dns/tsig.h>
44e2b1b9c0Schristos 
45e2b1b9c0Schristos #include <dst/result.h>
46e2b1b9c0Schristos 
479742fdb4Schristos #include "tsig_p.h"
489742fdb4Schristos 
49e2b1b9c0Schristos #define TSIG_MAGIC	  ISC_MAGIC('T', 'S', 'I', 'G')
50e2b1b9c0Schristos #define VALID_TSIG_KEY(x) ISC_MAGIC_VALID(x, TSIG_MAGIC)
51e2b1b9c0Schristos 
52e2b1b9c0Schristos #ifndef DNS_TSIG_MAXGENERATEDKEYS
53e2b1b9c0Schristos #define DNS_TSIG_MAXGENERATEDKEYS 4096
549742fdb4Schristos #endif /* ifndef DNS_TSIG_MAXGENERATEDKEYS */
55e2b1b9c0Schristos 
56f2e20987Schristos #define is_response(msg) ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
57e2b1b9c0Schristos 
58e2b1b9c0Schristos #define BADTIMELEN 6
59e2b1b9c0Schristos 
60e2b1b9c0Schristos static unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
61e2b1b9c0Schristos static unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
62e2b1b9c0Schristos 
639742fdb4Schristos static dns_name_t const hmacmd5 = DNS_NAME_INITABSOLUTE(hmacmd5_ndata,
649742fdb4Schristos 							hmacmd5_offsets);
65e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
66e2b1b9c0Schristos 
67e2b1b9c0Schristos static unsigned char gsstsig_ndata[] = "\010gss-tsig";
68e2b1b9c0Schristos static unsigned char gsstsig_offsets[] = { 0, 9 };
699742fdb4Schristos static dns_name_t const gsstsig = DNS_NAME_INITABSOLUTE(gsstsig_ndata,
709742fdb4Schristos 							gsstsig_offsets);
71e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_gssapi_name = &gsstsig;
72e2b1b9c0Schristos 
73e2b1b9c0Schristos /*
74e2b1b9c0Schristos  * Since Microsoft doesn't follow its own standard, we will use this
75e2b1b9c0Schristos  * alternate name as a second guess.
76e2b1b9c0Schristos  */
77e2b1b9c0Schristos static unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
78e2b1b9c0Schristos static unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
799742fdb4Schristos static dns_name_t const gsstsigms = DNS_NAME_INITABSOLUTE(gsstsigms_ndata,
809742fdb4Schristos 							  gsstsigms_offsets);
81e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_gssapims_name = &gsstsigms;
82e2b1b9c0Schristos 
83e2b1b9c0Schristos static unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
84e2b1b9c0Schristos static unsigned char hmacsha1_offsets[] = { 0, 10 };
859742fdb4Schristos static dns_name_t const hmacsha1 = DNS_NAME_INITABSOLUTE(hmacsha1_ndata,
869742fdb4Schristos 							 hmacsha1_offsets);
87e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;
88e2b1b9c0Schristos 
89e2b1b9c0Schristos static unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
90e2b1b9c0Schristos static unsigned char hmacsha224_offsets[] = { 0, 12 };
919742fdb4Schristos static dns_name_t const hmacsha224 = DNS_NAME_INITABSOLUTE(hmacsha224_ndata,
929742fdb4Schristos 							   hmacsha224_offsets);
93e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;
94e2b1b9c0Schristos 
95e2b1b9c0Schristos static unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
96e2b1b9c0Schristos static unsigned char hmacsha256_offsets[] = { 0, 12 };
979742fdb4Schristos static dns_name_t const hmacsha256 = DNS_NAME_INITABSOLUTE(hmacsha256_ndata,
989742fdb4Schristos 							   hmacsha256_offsets);
99e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;
100e2b1b9c0Schristos 
101e2b1b9c0Schristos static unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
102e2b1b9c0Schristos static unsigned char hmacsha384_offsets[] = { 0, 12 };
1039742fdb4Schristos static dns_name_t const hmacsha384 = DNS_NAME_INITABSOLUTE(hmacsha384_ndata,
1049742fdb4Schristos 							   hmacsha384_offsets);
105e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;
106e2b1b9c0Schristos 
107e2b1b9c0Schristos static unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
108e2b1b9c0Schristos static unsigned char hmacsha512_offsets[] = { 0, 12 };
1099742fdb4Schristos static dns_name_t const hmacsha512 = DNS_NAME_INITABSOLUTE(hmacsha512_ndata,
1109742fdb4Schristos 							   hmacsha512_offsets);
111e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;
112e2b1b9c0Schristos 
113e2b1b9c0Schristos static const struct {
114e2b1b9c0Schristos 	const dns_name_t *name;
115e2b1b9c0Schristos 	unsigned int dstalg;
1169742fdb4Schristos } known_algs[] = { { &hmacmd5, DST_ALG_HMACMD5 },
117e2b1b9c0Schristos 		   { &gsstsig, DST_ALG_GSSAPI },
118e2b1b9c0Schristos 		   { &gsstsigms, DST_ALG_GSSAPI },
119e2b1b9c0Schristos 		   { &hmacsha1, DST_ALG_HMACSHA1 },
120e2b1b9c0Schristos 		   { &hmacsha224, DST_ALG_HMACSHA224 },
121e2b1b9c0Schristos 		   { &hmacsha256, DST_ALG_HMACSHA256 },
122e2b1b9c0Schristos 		   { &hmacsha384, DST_ALG_HMACSHA384 },
1239742fdb4Schristos 		   { &hmacsha512, DST_ALG_HMACSHA512 } };
124e2b1b9c0Schristos 
125e2b1b9c0Schristos static isc_result_t
126e2b1b9c0Schristos tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
127e2b1b9c0Schristos 
128e2b1b9c0Schristos static void
129e2b1b9c0Schristos tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
130e2b1b9c0Schristos 	ISC_FORMAT_PRINTF(3, 4);
131e2b1b9c0Schristos 
132e2b1b9c0Schristos static void
133e2b1b9c0Schristos cleanup_ring(dns_tsig_keyring_t *ring);
134e2b1b9c0Schristos static void
135e2b1b9c0Schristos tsigkey_free(dns_tsigkey_t *key);
136e2b1b9c0Schristos 
137f2e20987Schristos bool
dns__tsig_algvalid(unsigned int alg)138e2b1b9c0Schristos dns__tsig_algvalid(unsigned int alg) {
1399742fdb4Schristos 	return (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 ||
1409742fdb4Schristos 		alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
1419742fdb4Schristos 		alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512);
142e2b1b9c0Schristos }
143e2b1b9c0Schristos 
144e2b1b9c0Schristos static void
tsig_log(dns_tsigkey_t * key,int level,const char * fmt,...)145e2b1b9c0Schristos tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
146e2b1b9c0Schristos 	va_list ap;
147e2b1b9c0Schristos 	char message[4096];
148e2b1b9c0Schristos 	char namestr[DNS_NAME_FORMATSIZE];
149e2b1b9c0Schristos 	char creatorstr[DNS_NAME_FORMATSIZE];
150e2b1b9c0Schristos 
151803e9293Schristos 	if (!isc_log_wouldlog(dns_lctx, level)) {
152e2b1b9c0Schristos 		return;
1539742fdb4Schristos 	}
154e2b1b9c0Schristos 	if (key != NULL) {
155e2b1b9c0Schristos 		dns_name_format(&key->name, namestr, sizeof(namestr));
156e2b1b9c0Schristos 	} else {
157e2b1b9c0Schristos 		strlcpy(namestr, "<null>", sizeof(namestr));
158e2b1b9c0Schristos 	}
159e2b1b9c0Schristos 
160e2b1b9c0Schristos 	if (key != NULL && key->generated && key->creator) {
161e2b1b9c0Schristos 		dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
162e2b1b9c0Schristos 	} else {
163e2b1b9c0Schristos 		strlcpy(creatorstr, "<null>", sizeof(creatorstr));
164e2b1b9c0Schristos 	}
165e2b1b9c0Schristos 
166e2b1b9c0Schristos 	va_start(ap, fmt);
167e2b1b9c0Schristos 	vsnprintf(message, sizeof(message), fmt, ap);
168e2b1b9c0Schristos 	va_end(ap);
169e2b1b9c0Schristos 	if (key != NULL && key->generated) {
1709742fdb4Schristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
1719742fdb4Schristos 			      DNS_LOGMODULE_TSIG, level,
1729742fdb4Schristos 			      "tsig key '%s' (%s): %s", namestr, creatorstr,
1739742fdb4Schristos 			      message);
174e2b1b9c0Schristos 	} else {
1759742fdb4Schristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
1769742fdb4Schristos 			      DNS_LOGMODULE_TSIG, level, "tsig key '%s': %s",
1779742fdb4Schristos 			      namestr, message);
178e2b1b9c0Schristos 	}
179e2b1b9c0Schristos }
180e2b1b9c0Schristos 
181e2b1b9c0Schristos static void
remove_fromring(dns_tsigkey_t * tkey)182e2b1b9c0Schristos remove_fromring(dns_tsigkey_t *tkey) {
183e2b1b9c0Schristos 	if (tkey->generated) {
184e2b1b9c0Schristos 		ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
185e2b1b9c0Schristos 		tkey->ring->generated--;
186e2b1b9c0Schristos 	}
187f2e20987Schristos 	(void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, false);
188e2b1b9c0Schristos }
189e2b1b9c0Schristos 
190e2b1b9c0Schristos static void
adjust_lru(dns_tsigkey_t * tkey)191e2b1b9c0Schristos adjust_lru(dns_tsigkey_t *tkey) {
192e2b1b9c0Schristos 	if (tkey->generated) {
193e2b1b9c0Schristos 		RWLOCK(&tkey->ring->lock, isc_rwlocktype_write);
194e2b1b9c0Schristos 		/*
195e2b1b9c0Schristos 		 * We may have been removed from the LRU list between
1969742fdb4Schristos 		 * removing the read lock and acquiring the write lock.
197e2b1b9c0Schristos 		 */
1989742fdb4Schristos 		if (ISC_LINK_LINKED(tkey, link) && tkey->ring->lru.tail != tkey)
199e2b1b9c0Schristos 		{
200e2b1b9c0Schristos 			ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
201e2b1b9c0Schristos 			ISC_LIST_APPEND(tkey->ring->lru, tkey, link);
202e2b1b9c0Schristos 		}
203e2b1b9c0Schristos 		RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write);
204e2b1b9c0Schristos 	}
205e2b1b9c0Schristos }
206e2b1b9c0Schristos 
207e2b1b9c0Schristos /*
208e2b1b9c0Schristos  * A supplemental routine just to add a key to ring.  Note that reference
209e2b1b9c0Schristos  * counter should be counted separately because we may be adding the key
210e2b1b9c0Schristos  * as part of creation of the key, in which case the reference counter was
211e2b1b9c0Schristos  * already initialized.  Also note we don't need RWLOCK for the reference
212e2b1b9c0Schristos  * counter: it's protected by a separate lock.
213e2b1b9c0Schristos  */
214e2b1b9c0Schristos static isc_result_t
keyring_add(dns_tsig_keyring_t * ring,const dns_name_t * name,dns_tsigkey_t * tkey)215e2b1b9c0Schristos keyring_add(dns_tsig_keyring_t *ring, const dns_name_t *name,
2169742fdb4Schristos 	    dns_tsigkey_t *tkey) {
217e2b1b9c0Schristos 	isc_result_t result;
218e2b1b9c0Schristos 
219e2b1b9c0Schristos 	RWLOCK(&ring->lock, isc_rwlocktype_write);
220e2b1b9c0Schristos 	ring->writecount++;
221e2b1b9c0Schristos 
222e2b1b9c0Schristos 	/*
223e2b1b9c0Schristos 	 * Do on the fly cleaning.  Find some nodes we might not
224e2b1b9c0Schristos 	 * want around any more.
225e2b1b9c0Schristos 	 */
226e2b1b9c0Schristos 	if (ring->writecount > 10) {
227e2b1b9c0Schristos 		cleanup_ring(ring);
228e2b1b9c0Schristos 		ring->writecount = 0;
229e2b1b9c0Schristos 	}
230e2b1b9c0Schristos 
231e2b1b9c0Schristos 	result = dns_rbt_addname(ring->keys, name, tkey);
232e2b1b9c0Schristos 	if (result == ISC_R_SUCCESS && tkey->generated) {
233e2b1b9c0Schristos 		/*
234e2b1b9c0Schristos 		 * Add the new key to the LRU list and remove the least
235e2b1b9c0Schristos 		 * recently used key if there are too many keys on the list.
236e2b1b9c0Schristos 		 */
237e2b1b9c0Schristos 		ISC_LIST_APPEND(ring->lru, tkey, link);
2389742fdb4Schristos 		if (ring->generated++ > ring->maxgenerated) {
239e2b1b9c0Schristos 			remove_fromring(ISC_LIST_HEAD(ring->lru));
240e2b1b9c0Schristos 		}
2419742fdb4Schristos 	}
242e2b1b9c0Schristos 	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
243e2b1b9c0Schristos 
244e2b1b9c0Schristos 	return (result);
245e2b1b9c0Schristos }
246e2b1b9c0Schristos 
247e2b1b9c0Schristos isc_result_t
dns_tsigkey_createfromkey(const dns_name_t * name,const dns_name_t * algorithm,dst_key_t * dstkey,bool generated,const dns_name_t * creator,isc_stdtime_t inception,isc_stdtime_t expire,isc_mem_t * mctx,dns_tsig_keyring_t * ring,dns_tsigkey_t ** key)248e2b1b9c0Schristos dns_tsigkey_createfromkey(const dns_name_t *name, const dns_name_t *algorithm,
249f2e20987Schristos 			  dst_key_t *dstkey, bool generated,
250e2b1b9c0Schristos 			  const dns_name_t *creator, isc_stdtime_t inception,
251e2b1b9c0Schristos 			  isc_stdtime_t expire, isc_mem_t *mctx,
2529742fdb4Schristos 			  dns_tsig_keyring_t *ring, dns_tsigkey_t **key) {
253e2b1b9c0Schristos 	dns_tsigkey_t *tkey;
254e2b1b9c0Schristos 	isc_result_t ret;
255e2b1b9c0Schristos 	unsigned int refs = 0;
256e2b1b9c0Schristos 	unsigned int dstalg = 0;
257e2b1b9c0Schristos 
258e2b1b9c0Schristos 	REQUIRE(key == NULL || *key == NULL);
259e2b1b9c0Schristos 	REQUIRE(name != NULL);
260e2b1b9c0Schristos 	REQUIRE(algorithm != NULL);
261e2b1b9c0Schristos 	REQUIRE(mctx != NULL);
262e2b1b9c0Schristos 	REQUIRE(key != NULL || ring != NULL);
263e2b1b9c0Schristos 
2649742fdb4Schristos 	tkey = isc_mem_get(mctx, sizeof(dns_tsigkey_t));
265e2b1b9c0Schristos 
266e2b1b9c0Schristos 	dns_name_init(&tkey->name, NULL);
2679742fdb4Schristos 	dns_name_dup(name, mctx, &tkey->name);
268e2b1b9c0Schristos 	(void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
269e2b1b9c0Schristos 
270e2b1b9c0Schristos 	/* Check against known algorithm names */
271e2b1b9c0Schristos 	dstalg = dns__tsig_algfromname(algorithm);
272e2b1b9c0Schristos 	if (dstalg != 0) {
273e2b1b9c0Schristos 		/*
274e2b1b9c0Schristos 		 * 'algorithm' must be set to a static pointer
275e2b1b9c0Schristos 		 * so that dns__tsig_algallocated() can compare them.
276e2b1b9c0Schristos 		 */
277e2b1b9c0Schristos 		tkey->algorithm = dns__tsig_algnamefromname(algorithm);
278e2b1b9c0Schristos 		if (dstkey != NULL && dst_key_alg(dstkey) != dstalg) {
279e2b1b9c0Schristos 			ret = DNS_R_BADALG;
280e2b1b9c0Schristos 			goto cleanup_name;
281e2b1b9c0Schristos 		}
282e2b1b9c0Schristos 	} else {
283e2b1b9c0Schristos 		dns_name_t *tmpname;
284e2b1b9c0Schristos 		if (dstkey != NULL) {
285e2b1b9c0Schristos 			ret = DNS_R_BADALG;
286e2b1b9c0Schristos 			goto cleanup_name;
287e2b1b9c0Schristos 		}
288e2b1b9c0Schristos 		tmpname = isc_mem_get(mctx, sizeof(dns_name_t));
289e2b1b9c0Schristos 		dns_name_init(tmpname, NULL);
2909742fdb4Schristos 		dns_name_dup(algorithm, mctx, tmpname);
291e2b1b9c0Schristos 		(void)dns_name_downcase(tmpname, tmpname, NULL);
292e2b1b9c0Schristos 		tkey->algorithm = tmpname;
293e2b1b9c0Schristos 	}
294e2b1b9c0Schristos 
295e2b1b9c0Schristos 	if (creator != NULL) {
296e2b1b9c0Schristos 		tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
297e2b1b9c0Schristos 		dns_name_init(tkey->creator, NULL);
2989742fdb4Schristos 		dns_name_dup(creator, mctx, tkey->creator);
2999742fdb4Schristos 	} else {
300e2b1b9c0Schristos 		tkey->creator = NULL;
3019742fdb4Schristos 	}
302e2b1b9c0Schristos 
303e2b1b9c0Schristos 	tkey->key = NULL;
3049742fdb4Schristos 	if (dstkey != NULL) {
305e2b1b9c0Schristos 		dst_key_attach(dstkey, &tkey->key);
3069742fdb4Schristos 	}
307e2b1b9c0Schristos 	tkey->ring = ring;
308e2b1b9c0Schristos 
3099742fdb4Schristos 	if (key != NULL) {
310e2b1b9c0Schristos 		refs = 1;
3119742fdb4Schristos 	}
3129742fdb4Schristos 	if (ring != NULL) {
313e2b1b9c0Schristos 		refs++;
3149742fdb4Schristos 	}
315f2e20987Schristos 
316f2e20987Schristos 	isc_refcount_init(&tkey->refs, refs);
317e2b1b9c0Schristos 
318e2b1b9c0Schristos 	tkey->generated = generated;
319e2b1b9c0Schristos 	tkey->inception = inception;
320e2b1b9c0Schristos 	tkey->expire = expire;
321e2b1b9c0Schristos 	tkey->mctx = NULL;
322e2b1b9c0Schristos 	isc_mem_attach(mctx, &tkey->mctx);
323e2b1b9c0Schristos 	ISC_LINK_INIT(tkey, link);
324e2b1b9c0Schristos 
325e2b1b9c0Schristos 	tkey->magic = TSIG_MAGIC;
326e2b1b9c0Schristos 
327e2b1b9c0Schristos 	if (ring != NULL) {
328e2b1b9c0Schristos 		ret = keyring_add(ring, name, tkey);
3299742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
330e2b1b9c0Schristos 			goto cleanup_refs;
331e2b1b9c0Schristos 		}
3329742fdb4Schristos 	}
333e2b1b9c0Schristos 
334e2b1b9c0Schristos 	/*
335e2b1b9c0Schristos 	 * Ignore this if it's a GSS key, since the key size is meaningless.
336e2b1b9c0Schristos 	 */
337e2b1b9c0Schristos 	if (dstkey != NULL && dst_key_size(dstkey) < 64 &&
338*4ac1c27eSchristos 	    dstalg != DST_ALG_GSSAPI)
339*4ac1c27eSchristos 	{
340e2b1b9c0Schristos 		char namestr[DNS_NAME_FORMATSIZE];
341e2b1b9c0Schristos 		dns_name_format(name, namestr, sizeof(namestr));
342e2b1b9c0Schristos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
343e2b1b9c0Schristos 			      DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
344e2b1b9c0Schristos 			      "the key '%s' is too short to be secure",
345e2b1b9c0Schristos 			      namestr);
346e2b1b9c0Schristos 	}
347e2b1b9c0Schristos 
3489742fdb4Schristos 	if (key != NULL) {
349e2b1b9c0Schristos 		*key = tkey;
3509742fdb4Schristos 	}
351e2b1b9c0Schristos 
352e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
353e2b1b9c0Schristos 
354e2b1b9c0Schristos cleanup_refs:
355e2b1b9c0Schristos 	tkey->magic = 0;
356f2e20987Schristos 	while (refs-- > 0) {
35773584a28Schristos 		isc_refcount_decrement0(&tkey->refs);
358f2e20987Schristos 	}
359e2b1b9c0Schristos 	isc_refcount_destroy(&tkey->refs);
360f2e20987Schristos 
3619742fdb4Schristos 	if (tkey->key != NULL) {
362e2b1b9c0Schristos 		dst_key_free(&tkey->key);
3639742fdb4Schristos 	}
364e2b1b9c0Schristos 	if (tkey->creator != NULL) {
365e2b1b9c0Schristos 		dns_name_free(tkey->creator, mctx);
366e2b1b9c0Schristos 		isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
367e2b1b9c0Schristos 	}
368e2b1b9c0Schristos 	if (dns__tsig_algallocated(tkey->algorithm)) {
369e2b1b9c0Schristos 		dns_name_t *tmpname;
370e2b1b9c0Schristos 		DE_CONST(tkey->algorithm, tmpname);
3719742fdb4Schristos 		if (dns_name_dynamic(tmpname)) {
372e2b1b9c0Schristos 			dns_name_free(tmpname, mctx);
3739742fdb4Schristos 		}
374e2b1b9c0Schristos 		isc_mem_put(mctx, tmpname, sizeof(dns_name_t));
375e2b1b9c0Schristos 	}
376e2b1b9c0Schristos cleanup_name:
377e2b1b9c0Schristos 	dns_name_free(&tkey->name, mctx);
378e2b1b9c0Schristos 	isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
379e2b1b9c0Schristos 
380e2b1b9c0Schristos 	return (ret);
381e2b1b9c0Schristos }
382e2b1b9c0Schristos 
383e2b1b9c0Schristos /*
384e2b1b9c0Schristos  * Find a few nodes to destroy if possible.
385e2b1b9c0Schristos  */
386e2b1b9c0Schristos static void
cleanup_ring(dns_tsig_keyring_t * ring)3879742fdb4Schristos cleanup_ring(dns_tsig_keyring_t *ring) {
388e2b1b9c0Schristos 	isc_result_t result;
389e2b1b9c0Schristos 	dns_rbtnodechain_t chain;
390e2b1b9c0Schristos 	dns_name_t foundname;
391e2b1b9c0Schristos 	dns_fixedname_t fixedorigin;
392e2b1b9c0Schristos 	dns_name_t *origin;
393e2b1b9c0Schristos 	isc_stdtime_t now;
394e2b1b9c0Schristos 	dns_rbtnode_t *node;
395e2b1b9c0Schristos 	dns_tsigkey_t *tkey;
396e2b1b9c0Schristos 
397e2b1b9c0Schristos 	/*
398e2b1b9c0Schristos 	 * Start up a new iterator each time.
399e2b1b9c0Schristos 	 */
400e2b1b9c0Schristos 	isc_stdtime_get(&now);
401e2b1b9c0Schristos 	dns_name_init(&foundname, NULL);
402e2b1b9c0Schristos 	origin = dns_fixedname_initname(&fixedorigin);
403e2b1b9c0Schristos 
404e2b1b9c0Schristos again:
4059742fdb4Schristos 	dns_rbtnodechain_init(&chain);
4069742fdb4Schristos 	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin);
407e2b1b9c0Schristos 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
408e2b1b9c0Schristos 		dns_rbtnodechain_invalidate(&chain);
409e2b1b9c0Schristos 		return;
410e2b1b9c0Schristos 	}
411e2b1b9c0Schristos 
412e2b1b9c0Schristos 	for (;;) {
413e2b1b9c0Schristos 		node = NULL;
414e2b1b9c0Schristos 		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
415e2b1b9c0Schristos 		tkey = node->data;
416e2b1b9c0Schristos 		if (tkey != NULL) {
4179742fdb4Schristos 			if (tkey->generated &&
4189742fdb4Schristos 			    isc_refcount_current(&tkey->refs) == 1 &&
4199742fdb4Schristos 			    tkey->inception != tkey->expire &&
4209742fdb4Schristos 			    tkey->expire < now)
4219742fdb4Schristos 			{
422e2b1b9c0Schristos 				tsig_log(tkey, 2, "tsig expire: deleting");
423e2b1b9c0Schristos 				/* delete the key */
424e2b1b9c0Schristos 				dns_rbtnodechain_invalidate(&chain);
425e2b1b9c0Schristos 				remove_fromring(tkey);
426e2b1b9c0Schristos 				goto again;
427e2b1b9c0Schristos 			}
428e2b1b9c0Schristos 		}
4299742fdb4Schristos 		result = dns_rbtnodechain_next(&chain, &foundname, origin);
430e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
431e2b1b9c0Schristos 			dns_rbtnodechain_invalidate(&chain);
432e2b1b9c0Schristos 			return;
433e2b1b9c0Schristos 		}
434e2b1b9c0Schristos 	}
435e2b1b9c0Schristos }
436e2b1b9c0Schristos 
437e2b1b9c0Schristos static void
destroyring(dns_tsig_keyring_t * ring)438e2b1b9c0Schristos destroyring(dns_tsig_keyring_t *ring) {
4399742fdb4Schristos 	isc_refcount_destroy(&ring->references);
440e2b1b9c0Schristos 	dns_rbt_destroy(&ring->keys);
441e2b1b9c0Schristos 	isc_rwlock_destroy(&ring->lock);
442e2b1b9c0Schristos 	isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t));
443e2b1b9c0Schristos }
444e2b1b9c0Schristos 
445e2b1b9c0Schristos /*
446e2b1b9c0Schristos  * Look up the DST_ALG_ constant for a given name.
447e2b1b9c0Schristos  */
448e2b1b9c0Schristos unsigned int
dns__tsig_algfromname(const dns_name_t * algorithm)449e2b1b9c0Schristos dns__tsig_algfromname(const dns_name_t *algorithm) {
450e2b1b9c0Schristos 	int i;
451e2b1b9c0Schristos 	int n = sizeof(known_algs) / sizeof(*known_algs);
452e2b1b9c0Schristos 	for (i = 0; i < n; ++i) {
453e2b1b9c0Schristos 		const dns_name_t *name = known_algs[i].name;
454e2b1b9c0Schristos 		if (algorithm == name || dns_name_equal(algorithm, name)) {
455e2b1b9c0Schristos 			return (known_algs[i].dstalg);
456e2b1b9c0Schristos 		}
457e2b1b9c0Schristos 	}
458e2b1b9c0Schristos 	return (0);
459e2b1b9c0Schristos }
460e2b1b9c0Schristos 
461e2b1b9c0Schristos /*
462e2b1b9c0Schristos  * Convert an algorithm name into a pointer to the
463e2b1b9c0Schristos  * corresponding pre-defined dns_name_t structure.
464e2b1b9c0Schristos  */
465e2b1b9c0Schristos const dns_name_t *
dns__tsig_algnamefromname(const dns_name_t * algorithm)466e2b1b9c0Schristos dns__tsig_algnamefromname(const dns_name_t *algorithm) {
467e2b1b9c0Schristos 	int i;
468e2b1b9c0Schristos 	int n = sizeof(known_algs) / sizeof(*known_algs);
469e2b1b9c0Schristos 	for (i = 0; i < n; ++i) {
470e2b1b9c0Schristos 		const dns_name_t *name = known_algs[i].name;
471e2b1b9c0Schristos 		if (algorithm == name || dns_name_equal(algorithm, name)) {
472e2b1b9c0Schristos 			return (name);
473e2b1b9c0Schristos 		}
474e2b1b9c0Schristos 	}
475e2b1b9c0Schristos 	return (NULL);
476e2b1b9c0Schristos }
477e2b1b9c0Schristos 
478e2b1b9c0Schristos /*
479e2b1b9c0Schristos  * Test whether the passed algorithm is NOT a pointer to one of the
480e2b1b9c0Schristos  * pre-defined known algorithms (and therefore one that has been
481e2b1b9c0Schristos  * dynamically allocated).
482e2b1b9c0Schristos  *
483e2b1b9c0Schristos  * This will return an incorrect result if passed a dynamically allocated
484e2b1b9c0Schristos  * dns_name_t that happens to match one of the pre-defined names.
485e2b1b9c0Schristos  */
486f2e20987Schristos bool
dns__tsig_algallocated(const dns_name_t * algorithm)487e2b1b9c0Schristos dns__tsig_algallocated(const dns_name_t *algorithm) {
488e2b1b9c0Schristos 	int i;
489e2b1b9c0Schristos 	int n = sizeof(known_algs) / sizeof(*known_algs);
490e2b1b9c0Schristos 	for (i = 0; i < n; ++i) {
491e2b1b9c0Schristos 		const dns_name_t *name = known_algs[i].name;
492e2b1b9c0Schristos 		if (algorithm == name) {
493f2e20987Schristos 			return (false);
494e2b1b9c0Schristos 		}
495e2b1b9c0Schristos 	}
496f2e20987Schristos 	return (true);
497e2b1b9c0Schristos }
498e2b1b9c0Schristos 
499e2b1b9c0Schristos static isc_result_t
restore_key(dns_tsig_keyring_t * ring,isc_stdtime_t now,FILE * fp)500e2b1b9c0Schristos restore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) {
501e2b1b9c0Schristos 	dst_key_t *dstkey = NULL;
502e2b1b9c0Schristos 	char namestr[1024];
503e2b1b9c0Schristos 	char creatorstr[1024];
504e2b1b9c0Schristos 	char algorithmstr[1024];
505e2b1b9c0Schristos 	char keystr[4096];
506e2b1b9c0Schristos 	unsigned int inception, expire;
507e2b1b9c0Schristos 	int n;
508e2b1b9c0Schristos 	isc_buffer_t b;
509e2b1b9c0Schristos 	dns_name_t *name, *creator, *algorithm;
510e2b1b9c0Schristos 	dns_fixedname_t fname, fcreator, falgorithm;
511e2b1b9c0Schristos 	isc_result_t result;
512e2b1b9c0Schristos 	unsigned int dstalg;
513e2b1b9c0Schristos 
514e2b1b9c0Schristos 	n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr,
515e2b1b9c0Schristos 		   creatorstr, &inception, &expire, algorithmstr, keystr);
5169742fdb4Schristos 	if (n == EOF) {
517e2b1b9c0Schristos 		return (ISC_R_NOMORE);
5189742fdb4Schristos 	}
5199742fdb4Schristos 	if (n != 6) {
520e2b1b9c0Schristos 		return (ISC_R_FAILURE);
5219742fdb4Schristos 	}
522e2b1b9c0Schristos 
5239742fdb4Schristos 	if (isc_serial_lt(expire, now)) {
524e2b1b9c0Schristos 		return (DNS_R_EXPIRED);
5259742fdb4Schristos 	}
526e2b1b9c0Schristos 
527e2b1b9c0Schristos 	name = dns_fixedname_initname(&fname);
528e2b1b9c0Schristos 	isc_buffer_init(&b, namestr, strlen(namestr));
529e2b1b9c0Schristos 	isc_buffer_add(&b, strlen(namestr));
530e2b1b9c0Schristos 	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
5319742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
532e2b1b9c0Schristos 		return (result);
5339742fdb4Schristos 	}
534e2b1b9c0Schristos 
535e2b1b9c0Schristos 	creator = dns_fixedname_initname(&fcreator);
536e2b1b9c0Schristos 	isc_buffer_init(&b, creatorstr, strlen(creatorstr));
537e2b1b9c0Schristos 	isc_buffer_add(&b, strlen(creatorstr));
538e2b1b9c0Schristos 	result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL);
5399742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
540e2b1b9c0Schristos 		return (result);
5419742fdb4Schristos 	}
542e2b1b9c0Schristos 
543e2b1b9c0Schristos 	algorithm = dns_fixedname_initname(&falgorithm);
544e2b1b9c0Schristos 	isc_buffer_init(&b, algorithmstr, strlen(algorithmstr));
545e2b1b9c0Schristos 	isc_buffer_add(&b, strlen(algorithmstr));
546e2b1b9c0Schristos 	result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL);
5479742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
548e2b1b9c0Schristos 		return (result);
5499742fdb4Schristos 	}
550e2b1b9c0Schristos 
551e2b1b9c0Schristos 	dstalg = dns__tsig_algfromname(algorithm);
5529742fdb4Schristos 	if (dstalg == 0) {
553e2b1b9c0Schristos 		return (DNS_R_BADALG);
5549742fdb4Schristos 	}
555e2b1b9c0Schristos 
556e2b1b9c0Schristos 	result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY,
557e2b1b9c0Schristos 				 DNS_KEYPROTO_DNSSEC, dns_rdataclass_in,
558e2b1b9c0Schristos 				 ring->mctx, keystr, &dstkey);
5599742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
560e2b1b9c0Schristos 		return (result);
5619742fdb4Schristos 	}
562e2b1b9c0Schristos 
5639742fdb4Schristos 	result = dns_tsigkey_createfromkey(name, algorithm, dstkey, true,
5649742fdb4Schristos 					   creator, inception, expire,
5659742fdb4Schristos 					   ring->mctx, ring, NULL);
5669742fdb4Schristos 	if (dstkey != NULL) {
567e2b1b9c0Schristos 		dst_key_free(&dstkey);
5689742fdb4Schristos 	}
569e2b1b9c0Schristos 	return (result);
570e2b1b9c0Schristos }
571e2b1b9c0Schristos 
572e2b1b9c0Schristos static void
dump_key(dns_tsigkey_t * tkey,FILE * fp)573e2b1b9c0Schristos dump_key(dns_tsigkey_t *tkey, FILE *fp) {
574e2b1b9c0Schristos 	char *buffer = NULL;
575e2b1b9c0Schristos 	int length = 0;
576e2b1b9c0Schristos 	char namestr[DNS_NAME_FORMATSIZE];
577e2b1b9c0Schristos 	char creatorstr[DNS_NAME_FORMATSIZE];
578e2b1b9c0Schristos 	char algorithmstr[DNS_NAME_FORMATSIZE];
579e2b1b9c0Schristos 	isc_result_t result;
580e2b1b9c0Schristos 
581e2b1b9c0Schristos 	REQUIRE(tkey != NULL);
582e2b1b9c0Schristos 	REQUIRE(fp != NULL);
583e2b1b9c0Schristos 
584e2b1b9c0Schristos 	dns_name_format(&tkey->name, namestr, sizeof(namestr));
585e2b1b9c0Schristos 	dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr));
586e2b1b9c0Schristos 	dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr));
587e2b1b9c0Schristos 	result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length);
5889742fdb4Schristos 	if (result == ISC_R_SUCCESS) {
589e2b1b9c0Schristos 		fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr,
5909742fdb4Schristos 			tkey->inception, tkey->expire, algorithmstr, length,
5919742fdb4Schristos 			buffer);
5929742fdb4Schristos 	}
5939742fdb4Schristos 	if (buffer != NULL) {
594e2b1b9c0Schristos 		isc_mem_put(tkey->mctx, buffer, length);
595e2b1b9c0Schristos 	}
5969742fdb4Schristos }
597e2b1b9c0Schristos 
598e2b1b9c0Schristos isc_result_t
dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t ** ringp,FILE * fp)599e2b1b9c0Schristos dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) {
600e2b1b9c0Schristos 	isc_result_t result;
601e2b1b9c0Schristos 	dns_rbtnodechain_t chain;
602e2b1b9c0Schristos 	dns_name_t foundname;
603e2b1b9c0Schristos 	dns_fixedname_t fixedorigin;
604e2b1b9c0Schristos 	dns_name_t *origin;
605e2b1b9c0Schristos 	isc_stdtime_t now;
606e2b1b9c0Schristos 	dns_rbtnode_t *node;
607e2b1b9c0Schristos 	dns_tsigkey_t *tkey;
608e2b1b9c0Schristos 	dns_tsig_keyring_t *ring;
609e2b1b9c0Schristos 
610e2b1b9c0Schristos 	REQUIRE(ringp != NULL && *ringp != NULL);
611e2b1b9c0Schristos 
612e2b1b9c0Schristos 	ring = *ringp;
613e2b1b9c0Schristos 	*ringp = NULL;
614e2b1b9c0Schristos 
6159742fdb4Schristos 	if (isc_refcount_decrement(&ring->references) > 1) {
616e2b1b9c0Schristos 		return (DNS_R_CONTINUE);
6179742fdb4Schristos 	}
618e2b1b9c0Schristos 
619e2b1b9c0Schristos 	isc_stdtime_get(&now);
620e2b1b9c0Schristos 	dns_name_init(&foundname, NULL);
621e2b1b9c0Schristos 	origin = dns_fixedname_initname(&fixedorigin);
6229742fdb4Schristos 	dns_rbtnodechain_init(&chain);
6239742fdb4Schristos 	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin);
624e2b1b9c0Schristos 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
625e2b1b9c0Schristos 		dns_rbtnodechain_invalidate(&chain);
626e2b1b9c0Schristos 		goto destroy;
627e2b1b9c0Schristos 	}
628e2b1b9c0Schristos 
629e2b1b9c0Schristos 	for (;;) {
630e2b1b9c0Schristos 		node = NULL;
631e2b1b9c0Schristos 		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
632e2b1b9c0Schristos 		tkey = node->data;
6339742fdb4Schristos 		if (tkey != NULL && tkey->generated && tkey->expire >= now) {
634e2b1b9c0Schristos 			dump_key(tkey, fp);
6359742fdb4Schristos 		}
6369742fdb4Schristos 		result = dns_rbtnodechain_next(&chain, &foundname, origin);
637e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
638e2b1b9c0Schristos 			dns_rbtnodechain_invalidate(&chain);
6399742fdb4Schristos 			if (result == ISC_R_NOMORE) {
640e2b1b9c0Schristos 				result = ISC_R_SUCCESS;
6419742fdb4Schristos 			}
642e2b1b9c0Schristos 			goto destroy;
643e2b1b9c0Schristos 		}
644e2b1b9c0Schristos 	}
645e2b1b9c0Schristos 
646e2b1b9c0Schristos destroy:
647e2b1b9c0Schristos 	destroyring(ring);
648e2b1b9c0Schristos 	return (result);
649e2b1b9c0Schristos }
650e2b1b9c0Schristos 
6518b4c8a26Schristos const dns_name_t *
dns_tsigkey_identity(const dns_tsigkey_t * tsigkey)6528b4c8a26Schristos dns_tsigkey_identity(const dns_tsigkey_t *tsigkey) {
6538b4c8a26Schristos 	REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
6548b4c8a26Schristos 
6558b4c8a26Schristos 	if (tsigkey == NULL) {
6568b4c8a26Schristos 		return (NULL);
6578b4c8a26Schristos 	}
6588b4c8a26Schristos 	if (tsigkey->generated) {
6598b4c8a26Schristos 		return (tsigkey->creator);
6608b4c8a26Schristos 	} else {
6618b4c8a26Schristos 		return (&tsigkey->name);
6628b4c8a26Schristos 	}
6638b4c8a26Schristos }
6648b4c8a26Schristos 
665e2b1b9c0Schristos isc_result_t
dns_tsigkey_create(const dns_name_t * name,const dns_name_t * algorithm,unsigned char * secret,int length,bool generated,const dns_name_t * creator,isc_stdtime_t inception,isc_stdtime_t expire,isc_mem_t * mctx,dns_tsig_keyring_t * ring,dns_tsigkey_t ** key)666e2b1b9c0Schristos dns_tsigkey_create(const dns_name_t *name, const dns_name_t *algorithm,
667f2e20987Schristos 		   unsigned char *secret, int length, bool generated,
668e2b1b9c0Schristos 		   const dns_name_t *creator, isc_stdtime_t inception,
669e2b1b9c0Schristos 		   isc_stdtime_t expire, isc_mem_t *mctx,
6709742fdb4Schristos 		   dns_tsig_keyring_t *ring, dns_tsigkey_t **key) {
671e2b1b9c0Schristos 	dst_key_t *dstkey = NULL;
672e2b1b9c0Schristos 	isc_result_t result;
673e2b1b9c0Schristos 	unsigned int dstalg = 0;
674e2b1b9c0Schristos 
675e2b1b9c0Schristos 	REQUIRE(length >= 0);
6769742fdb4Schristos 	if (length > 0) {
677e2b1b9c0Schristos 		REQUIRE(secret != NULL);
6789742fdb4Schristos 	}
679e2b1b9c0Schristos 
680e2b1b9c0Schristos 	dstalg = dns__tsig_algfromname(algorithm);
681e2b1b9c0Schristos 	if (dns__tsig_algvalid(dstalg)) {
682e2b1b9c0Schristos 		if (secret != NULL) {
683e2b1b9c0Schristos 			isc_buffer_t b;
684e2b1b9c0Schristos 
685e2b1b9c0Schristos 			isc_buffer_init(&b, secret, length);
686e2b1b9c0Schristos 			isc_buffer_add(&b, length);
6879742fdb4Schristos 			result = dst_key_frombuffer(
6889742fdb4Schristos 				name, dstalg, DNS_KEYOWNER_ENTITY,
6899742fdb4Schristos 				DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, &b,
6909742fdb4Schristos 				mctx, &dstkey);
6919742fdb4Schristos 			if (result != ISC_R_SUCCESS) {
692e2b1b9c0Schristos 				return (result);
693e2b1b9c0Schristos 			}
6949742fdb4Schristos 		}
695e2b1b9c0Schristos 	} else if (length > 0) {
696e2b1b9c0Schristos 		return (DNS_R_BADALG);
697e2b1b9c0Schristos 	}
698e2b1b9c0Schristos 
6999742fdb4Schristos 	result = dns_tsigkey_createfromkey(name, algorithm, dstkey, generated,
7009742fdb4Schristos 					   creator, inception, expire, mctx,
7019742fdb4Schristos 					   ring, key);
7029742fdb4Schristos 	if (dstkey != NULL) {
703e2b1b9c0Schristos 		dst_key_free(&dstkey);
7049742fdb4Schristos 	}
705e2b1b9c0Schristos 	return (result);
706e2b1b9c0Schristos }
707e2b1b9c0Schristos 
708e2b1b9c0Schristos void
dns_tsigkey_attach(dns_tsigkey_t * source,dns_tsigkey_t ** targetp)709e2b1b9c0Schristos dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
710e2b1b9c0Schristos 	REQUIRE(VALID_TSIG_KEY(source));
711e2b1b9c0Schristos 	REQUIRE(targetp != NULL && *targetp == NULL);
712e2b1b9c0Schristos 
713f2e20987Schristos 	isc_refcount_increment(&source->refs);
714e2b1b9c0Schristos 	*targetp = source;
715e2b1b9c0Schristos }
716e2b1b9c0Schristos 
717e2b1b9c0Schristos static void
tsigkey_free(dns_tsigkey_t * key)718e2b1b9c0Schristos tsigkey_free(dns_tsigkey_t *key) {
719e2b1b9c0Schristos 	REQUIRE(VALID_TSIG_KEY(key));
720e2b1b9c0Schristos 
721e2b1b9c0Schristos 	key->magic = 0;
722e2b1b9c0Schristos 	dns_name_free(&key->name, key->mctx);
723e2b1b9c0Schristos 	if (dns__tsig_algallocated(key->algorithm)) {
724e2b1b9c0Schristos 		dns_name_t *name;
725e2b1b9c0Schristos 		DE_CONST(key->algorithm, name);
726e2b1b9c0Schristos 		dns_name_free(name, key->mctx);
727e2b1b9c0Schristos 		isc_mem_put(key->mctx, name, sizeof(dns_name_t));
728e2b1b9c0Schristos 	}
7299742fdb4Schristos 	if (key->key != NULL) {
730e2b1b9c0Schristos 		dst_key_free(&key->key);
7319742fdb4Schristos 	}
732e2b1b9c0Schristos 	if (key->creator != NULL) {
733e2b1b9c0Schristos 		dns_name_free(key->creator, key->mctx);
734e2b1b9c0Schristos 		isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
735e2b1b9c0Schristos 	}
736e2b1b9c0Schristos 	isc_mem_putanddetach(&key->mctx, key, sizeof(dns_tsigkey_t));
737e2b1b9c0Schristos }
738e2b1b9c0Schristos 
739e2b1b9c0Schristos void
dns_tsigkey_detach(dns_tsigkey_t ** keyp)740e2b1b9c0Schristos dns_tsigkey_detach(dns_tsigkey_t **keyp) {
741f2e20987Schristos 	REQUIRE(keyp != NULL && VALID_TSIG_KEY(*keyp));
742f2e20987Schristos 	dns_tsigkey_t *key = *keyp;
743e2b1b9c0Schristos 	*keyp = NULL;
744f2e20987Schristos 
745f2e20987Schristos 	if (isc_refcount_decrement(&key->refs) == 1) {
746f2e20987Schristos 		isc_refcount_destroy(&key->refs);
747f2e20987Schristos 		tsigkey_free(key);
748f2e20987Schristos 	}
749e2b1b9c0Schristos }
750e2b1b9c0Schristos 
751e2b1b9c0Schristos void
dns_tsigkey_setdeleted(dns_tsigkey_t * key)752e2b1b9c0Schristos dns_tsigkey_setdeleted(dns_tsigkey_t *key) {
753e2b1b9c0Schristos 	REQUIRE(VALID_TSIG_KEY(key));
754e2b1b9c0Schristos 	REQUIRE(key->ring != NULL);
755e2b1b9c0Schristos 
756e2b1b9c0Schristos 	RWLOCK(&key->ring->lock, isc_rwlocktype_write);
757e2b1b9c0Schristos 	remove_fromring(key);
758e2b1b9c0Schristos 	RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
759e2b1b9c0Schristos }
760e2b1b9c0Schristos 
761e2b1b9c0Schristos isc_result_t
dns_tsig_sign(dns_message_t * msg)762e2b1b9c0Schristos dns_tsig_sign(dns_message_t *msg) {
763cc2e102bSchristos 	dns_tsigkey_t *key = NULL;
764e2b1b9c0Schristos 	dns_rdata_any_tsig_t tsig, querytsig;
765e2b1b9c0Schristos 	unsigned char data[128];
766e2b1b9c0Schristos 	isc_buffer_t databuf, sigbuf;
767cc2e102bSchristos 	isc_buffer_t *dynbuf = NULL;
768fadf0758Schristos 	dns_name_t *owner = NULL;
769e2b1b9c0Schristos 	dns_rdata_t *rdata = NULL;
770cc2e102bSchristos 	dns_rdatalist_t *datalist = NULL;
771cc2e102bSchristos 	dns_rdataset_t *dataset = NULL;
772e2b1b9c0Schristos 	isc_region_t r;
773e2b1b9c0Schristos 	isc_stdtime_t now;
774e2b1b9c0Schristos 	isc_mem_t *mctx;
775e2b1b9c0Schristos 	dst_context_t *ctx = NULL;
776e2b1b9c0Schristos 	isc_result_t ret;
777e2b1b9c0Schristos 	unsigned char badtimedata[BADTIMELEN];
778e2b1b9c0Schristos 	unsigned int sigsize = 0;
779f2e20987Schristos 	bool response;
780e2b1b9c0Schristos 
781e2b1b9c0Schristos 	REQUIRE(msg != NULL);
782e2b1b9c0Schristos 	key = dns_message_gettsigkey(msg);
783e2b1b9c0Schristos 	REQUIRE(VALID_TSIG_KEY(key));
784e2b1b9c0Schristos 
785e2b1b9c0Schristos 	/*
786cc2e102bSchristos 	 * If this is a response, there should be a TSIG in the query with the
787cc2e102bSchristos 	 * the exception if this is a TKEY request (see RFC 3645, Section 2.2).
788e2b1b9c0Schristos 	 */
789e2b1b9c0Schristos 	response = is_response(msg);
790cc2e102bSchristos 	if (response && msg->querytsig == NULL) {
791cc2e102bSchristos 		if (msg->tkey != 1) {
792e2b1b9c0Schristos 			return (DNS_R_EXPECTEDTSIG);
793cc2e102bSchristos 		}
794cc2e102bSchristos 	}
795e2b1b9c0Schristos 
796e2b1b9c0Schristos 	mctx = msg->mctx;
797e2b1b9c0Schristos 
798e2b1b9c0Schristos 	tsig.mctx = mctx;
799e2b1b9c0Schristos 	tsig.common.rdclass = dns_rdataclass_any;
800e2b1b9c0Schristos 	tsig.common.rdtype = dns_rdatatype_tsig;
801e2b1b9c0Schristos 	ISC_LINK_INIT(&tsig.common, link);
802e2b1b9c0Schristos 	dns_name_init(&tsig.algorithm, NULL);
803e2b1b9c0Schristos 	dns_name_clone(key->algorithm, &tsig.algorithm);
804e2b1b9c0Schristos 
805e2b1b9c0Schristos 	isc_stdtime_get(&now);
806e2b1b9c0Schristos 	tsig.timesigned = now + msg->timeadjust;
807e2b1b9c0Schristos 	tsig.fudge = DNS_TSIG_FUDGE;
808e2b1b9c0Schristos 
809e2b1b9c0Schristos 	tsig.originalid = msg->id;
810e2b1b9c0Schristos 
811e2b1b9c0Schristos 	isc_buffer_init(&databuf, data, sizeof(data));
812e2b1b9c0Schristos 
8139742fdb4Schristos 	if (response) {
814e2b1b9c0Schristos 		tsig.error = msg->querytsigstatus;
8159742fdb4Schristos 	} else {
816e2b1b9c0Schristos 		tsig.error = dns_rcode_noerror;
8179742fdb4Schristos 	}
818e2b1b9c0Schristos 
819e2b1b9c0Schristos 	if (tsig.error != dns_tsigerror_badtime) {
820e2b1b9c0Schristos 		tsig.otherlen = 0;
821e2b1b9c0Schristos 		tsig.other = NULL;
822e2b1b9c0Schristos 	} else {
823e2b1b9c0Schristos 		isc_buffer_t otherbuf;
824e2b1b9c0Schristos 
825e2b1b9c0Schristos 		tsig.otherlen = BADTIMELEN;
826e2b1b9c0Schristos 		tsig.other = badtimedata;
827e2b1b9c0Schristos 		isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
828e2b1b9c0Schristos 		isc_buffer_putuint48(&otherbuf, tsig.timesigned);
829e2b1b9c0Schristos 	}
830e2b1b9c0Schristos 
8319742fdb4Schristos 	if ((key->key != NULL) && (tsig.error != dns_tsigerror_badsig) &&
832e2b1b9c0Schristos 	    (tsig.error != dns_tsigerror_badkey))
833e2b1b9c0Schristos 	{
834e2b1b9c0Schristos 		unsigned char header[DNS_MESSAGE_HEADERLEN];
835e2b1b9c0Schristos 		isc_buffer_t headerbuf;
836f2e20987Schristos 		uint16_t digestbits;
837cc2e102bSchristos 		bool querytsig_ok = false;
838e2b1b9c0Schristos 
839e2b1b9c0Schristos 		/*
840e2b1b9c0Schristos 		 * If it is a response, we assume that the request MAC
841e2b1b9c0Schristos 		 * has validated at this point. This is why we include a
842e2b1b9c0Schristos 		 * MAC length > 0 in the reply.
843e2b1b9c0Schristos 		 */
8449742fdb4Schristos 		ret = dst_context_create(key->key, mctx, DNS_LOGCATEGORY_DNSSEC,
845f2e20987Schristos 					 true, 0, &ctx);
8469742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
847e2b1b9c0Schristos 			return (ret);
8489742fdb4Schristos 		}
849e2b1b9c0Schristos 
850e2b1b9c0Schristos 		/*
851cc2e102bSchristos 		 * If this is a response, and if there was a TSIG in
852cc2e102bSchristos 		 * the query, digest the request's MAC.
853cc2e102bSchristos 		 *
854cc2e102bSchristos 		 * (Note: querytsig should be non-NULL for all
855cc2e102bSchristos 		 * responses except TKEY responses. Those may be signed
856cc2e102bSchristos 		 * with the newly-negotiated TSIG key even if the query
857cc2e102bSchristos 		 * wasn't signed.)
858e2b1b9c0Schristos 		 */
859cc2e102bSchristos 		if (response && msg->querytsig != NULL) {
860e2b1b9c0Schristos 			dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
861e2b1b9c0Schristos 
862e2b1b9c0Schristos 			INSIST(msg->verified_sig);
863e2b1b9c0Schristos 
864e2b1b9c0Schristos 			ret = dns_rdataset_first(msg->querytsig);
8659742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
866e2b1b9c0Schristos 				goto cleanup_context;
8679742fdb4Schristos 			}
868e2b1b9c0Schristos 			dns_rdataset_current(msg->querytsig, &querytsigrdata);
869e2b1b9c0Schristos 			ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
870e2b1b9c0Schristos 						 NULL);
8719742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
872e2b1b9c0Schristos 				goto cleanup_context;
8739742fdb4Schristos 			}
874e2b1b9c0Schristos 			isc_buffer_putuint16(&databuf, querytsig.siglen);
875e2b1b9c0Schristos 			if (isc_buffer_availablelength(&databuf) <
876*4ac1c27eSchristos 			    querytsig.siglen)
877*4ac1c27eSchristos 			{
878e2b1b9c0Schristos 				ret = ISC_R_NOSPACE;
879e2b1b9c0Schristos 				goto cleanup_context;
880e2b1b9c0Schristos 			}
881e2b1b9c0Schristos 			isc_buffer_putmem(&databuf, querytsig.signature,
882e2b1b9c0Schristos 					  querytsig.siglen);
883e2b1b9c0Schristos 			isc_buffer_usedregion(&databuf, &r);
884e2b1b9c0Schristos 			ret = dst_context_adddata(ctx, &r);
8859742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
886e2b1b9c0Schristos 				goto cleanup_context;
8879742fdb4Schristos 			}
888cc2e102bSchristos 			querytsig_ok = true;
889e2b1b9c0Schristos 		}
890e2b1b9c0Schristos 
891e2b1b9c0Schristos 		/*
892e2b1b9c0Schristos 		 * Digest the header.
893e2b1b9c0Schristos 		 */
894e2b1b9c0Schristos 		isc_buffer_init(&headerbuf, header, sizeof(header));
895e2b1b9c0Schristos 		dns_message_renderheader(msg, &headerbuf);
896e2b1b9c0Schristos 		isc_buffer_usedregion(&headerbuf, &r);
897e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
8989742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
899e2b1b9c0Schristos 			goto cleanup_context;
9009742fdb4Schristos 		}
901e2b1b9c0Schristos 
902e2b1b9c0Schristos 		/*
903e2b1b9c0Schristos 		 * Digest the remainder of the message.
904e2b1b9c0Schristos 		 */
905e2b1b9c0Schristos 		isc_buffer_usedregion(msg->buffer, &r);
906e2b1b9c0Schristos 		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
907e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
9089742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
909e2b1b9c0Schristos 			goto cleanup_context;
9109742fdb4Schristos 		}
911e2b1b9c0Schristos 
912e2b1b9c0Schristos 		if (msg->tcp_continuation == 0) {
913e2b1b9c0Schristos 			/*
914e2b1b9c0Schristos 			 * Digest the name, class, ttl, alg.
915e2b1b9c0Schristos 			 */
916e2b1b9c0Schristos 			dns_name_toregion(&key->name, &r);
917e2b1b9c0Schristos 			ret = dst_context_adddata(ctx, &r);
9189742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
919e2b1b9c0Schristos 				goto cleanup_context;
9209742fdb4Schristos 			}
921e2b1b9c0Schristos 
922e2b1b9c0Schristos 			isc_buffer_clear(&databuf);
923e2b1b9c0Schristos 			isc_buffer_putuint16(&databuf, dns_rdataclass_any);
924e2b1b9c0Schristos 			isc_buffer_putuint32(&databuf, 0); /* ttl */
925e2b1b9c0Schristos 			isc_buffer_usedregion(&databuf, &r);
926e2b1b9c0Schristos 			ret = dst_context_adddata(ctx, &r);
9279742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
928e2b1b9c0Schristos 				goto cleanup_context;
9299742fdb4Schristos 			}
930e2b1b9c0Schristos 
931e2b1b9c0Schristos 			dns_name_toregion(&tsig.algorithm, &r);
932e2b1b9c0Schristos 			ret = dst_context_adddata(ctx, &r);
9339742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
934e2b1b9c0Schristos 				goto cleanup_context;
9359742fdb4Schristos 			}
936e2b1b9c0Schristos 		}
937e2b1b9c0Schristos 		/* Digest the timesigned and fudge */
938e2b1b9c0Schristos 		isc_buffer_clear(&databuf);
939cc2e102bSchristos 		if (tsig.error == dns_tsigerror_badtime && querytsig_ok) {
940e2b1b9c0Schristos 			tsig.timesigned = querytsig.timesigned;
941e2b1b9c0Schristos 		}
942e2b1b9c0Schristos 		isc_buffer_putuint48(&databuf, tsig.timesigned);
943e2b1b9c0Schristos 		isc_buffer_putuint16(&databuf, tsig.fudge);
944e2b1b9c0Schristos 		isc_buffer_usedregion(&databuf, &r);
945e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
9469742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
947e2b1b9c0Schristos 			goto cleanup_context;
9489742fdb4Schristos 		}
949e2b1b9c0Schristos 
950e2b1b9c0Schristos 		if (msg->tcp_continuation == 0) {
951e2b1b9c0Schristos 			/*
952e2b1b9c0Schristos 			 * Digest the error and other data length.
953e2b1b9c0Schristos 			 */
954e2b1b9c0Schristos 			isc_buffer_clear(&databuf);
955e2b1b9c0Schristos 			isc_buffer_putuint16(&databuf, tsig.error);
956e2b1b9c0Schristos 			isc_buffer_putuint16(&databuf, tsig.otherlen);
957e2b1b9c0Schristos 
958e2b1b9c0Schristos 			isc_buffer_usedregion(&databuf, &r);
959e2b1b9c0Schristos 			ret = dst_context_adddata(ctx, &r);
9609742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
961e2b1b9c0Schristos 				goto cleanup_context;
9629742fdb4Schristos 			}
963e2b1b9c0Schristos 
964e2b1b9c0Schristos 			/*
965e2b1b9c0Schristos 			 * Digest other data.
966e2b1b9c0Schristos 			 */
967e2b1b9c0Schristos 			if (tsig.otherlen > 0) {
968e2b1b9c0Schristos 				r.length = tsig.otherlen;
969e2b1b9c0Schristos 				r.base = tsig.other;
970e2b1b9c0Schristos 				ret = dst_context_adddata(ctx, &r);
9719742fdb4Schristos 				if (ret != ISC_R_SUCCESS) {
972e2b1b9c0Schristos 					goto cleanup_context;
973e2b1b9c0Schristos 				}
974e2b1b9c0Schristos 			}
9759742fdb4Schristos 		}
976e2b1b9c0Schristos 
977e2b1b9c0Schristos 		ret = dst_key_sigsize(key->key, &sigsize);
9789742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
979e2b1b9c0Schristos 			goto cleanup_context;
980e2b1b9c0Schristos 		}
9819742fdb4Schristos 		tsig.signature = isc_mem_get(mctx, sigsize);
982e2b1b9c0Schristos 
983e2b1b9c0Schristos 		isc_buffer_init(&sigbuf, tsig.signature, sigsize);
984e2b1b9c0Schristos 		ret = dst_context_sign(ctx, &sigbuf);
9859742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
986e2b1b9c0Schristos 			goto cleanup_signature;
9879742fdb4Schristos 		}
988e2b1b9c0Schristos 		dst_context_destroy(&ctx);
989e2b1b9c0Schristos 		digestbits = dst_key_getbits(key->key);
990e2b1b9c0Schristos 		if (digestbits != 0) {
991cc2e102bSchristos 			unsigned int bytes = (digestbits + 7) / 8;
9929742fdb4Schristos 			if (querytsig_ok && bytes < querytsig.siglen) {
993e2b1b9c0Schristos 				bytes = querytsig.siglen;
9949742fdb4Schristos 			}
9959742fdb4Schristos 			if (bytes > isc_buffer_usedlength(&sigbuf)) {
996e2b1b9c0Schristos 				bytes = isc_buffer_usedlength(&sigbuf);
9979742fdb4Schristos 			}
998e2b1b9c0Schristos 			tsig.siglen = bytes;
9999742fdb4Schristos 		} else {
1000e2b1b9c0Schristos 			tsig.siglen = isc_buffer_usedlength(&sigbuf);
10019742fdb4Schristos 		}
1002e2b1b9c0Schristos 	} else {
1003e2b1b9c0Schristos 		tsig.siglen = 0;
1004e2b1b9c0Schristos 		tsig.signature = NULL;
1005e2b1b9c0Schristos 	}
1006e2b1b9c0Schristos 
1007e2b1b9c0Schristos 	ret = dns_message_gettemprdata(msg, &rdata);
10089742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1009e2b1b9c0Schristos 		goto cleanup_signature;
10109742fdb4Schristos 	}
10119742fdb4Schristos 	isc_buffer_allocate(msg->mctx, &dynbuf, 512);
1012e2b1b9c0Schristos 	ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
1013e2b1b9c0Schristos 				   dns_rdatatype_tsig, &tsig, dynbuf);
10149742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1015e2b1b9c0Schristos 		goto cleanup_dynbuf;
10169742fdb4Schristos 	}
1017e2b1b9c0Schristos 
1018e2b1b9c0Schristos 	dns_message_takebuffer(msg, &dynbuf);
1019e2b1b9c0Schristos 
1020e2b1b9c0Schristos 	if (tsig.signature != NULL) {
1021e2b1b9c0Schristos 		isc_mem_put(mctx, tsig.signature, sigsize);
1022e2b1b9c0Schristos 		tsig.signature = NULL;
1023e2b1b9c0Schristos 	}
1024e2b1b9c0Schristos 
1025e2b1b9c0Schristos 	ret = dns_message_gettempname(msg, &owner);
10269742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1027e2b1b9c0Schristos 		goto cleanup_rdata;
10289742fdb4Schristos 	}
1029fadf0758Schristos 	dns_name_copynf(&key->name, owner);
1030e2b1b9c0Schristos 
1031e2b1b9c0Schristos 	ret = dns_message_gettemprdatalist(msg, &datalist);
10329742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1033e2b1b9c0Schristos 		goto cleanup_owner;
10349742fdb4Schristos 	}
1035fadf0758Schristos 
1036e2b1b9c0Schristos 	ret = dns_message_gettemprdataset(msg, &dataset);
10379742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1038e2b1b9c0Schristos 		goto cleanup_rdatalist;
10399742fdb4Schristos 	}
1040e2b1b9c0Schristos 	datalist->rdclass = dns_rdataclass_any;
1041e2b1b9c0Schristos 	datalist->type = dns_rdatatype_tsig;
1042e2b1b9c0Schristos 	ISC_LIST_APPEND(datalist->rdata, rdata, link);
10439742fdb4Schristos 	RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) ==
10449742fdb4Schristos 		      ISC_R_SUCCESS);
1045e2b1b9c0Schristos 	msg->tsig = dataset;
1046e2b1b9c0Schristos 	msg->tsigname = owner;
1047e2b1b9c0Schristos 
1048e2b1b9c0Schristos 	/* Windows does not like the tsig name being compressed. */
1049e2b1b9c0Schristos 	msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
1050e2b1b9c0Schristos 
1051e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1052e2b1b9c0Schristos 
1053e2b1b9c0Schristos cleanup_rdatalist:
1054e2b1b9c0Schristos 	dns_message_puttemprdatalist(msg, &datalist);
1055e2b1b9c0Schristos cleanup_owner:
1056e2b1b9c0Schristos 	dns_message_puttempname(msg, &owner);
1057e2b1b9c0Schristos 	goto cleanup_rdata;
1058e2b1b9c0Schristos cleanup_dynbuf:
1059e2b1b9c0Schristos 	isc_buffer_free(&dynbuf);
1060e2b1b9c0Schristos cleanup_rdata:
1061e2b1b9c0Schristos 	dns_message_puttemprdata(msg, &rdata);
1062e2b1b9c0Schristos cleanup_signature:
10639742fdb4Schristos 	if (tsig.signature != NULL) {
1064e2b1b9c0Schristos 		isc_mem_put(mctx, tsig.signature, sigsize);
10659742fdb4Schristos 	}
1066e2b1b9c0Schristos cleanup_context:
10679742fdb4Schristos 	if (ctx != NULL) {
1068e2b1b9c0Schristos 		dst_context_destroy(&ctx);
10699742fdb4Schristos 	}
1070e2b1b9c0Schristos 	return (ret);
1071e2b1b9c0Schristos }
1072e2b1b9c0Schristos 
1073e2b1b9c0Schristos isc_result_t
dns_tsig_verify(isc_buffer_t * source,dns_message_t * msg,dns_tsig_keyring_t * ring1,dns_tsig_keyring_t * ring2)1074e2b1b9c0Schristos dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
10759742fdb4Schristos 		dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2) {
1076e2b1b9c0Schristos 	dns_rdata_any_tsig_t tsig, querytsig;
1077e2b1b9c0Schristos 	isc_region_t r, source_r, header_r, sig_r;
1078e2b1b9c0Schristos 	isc_buffer_t databuf;
1079e2b1b9c0Schristos 	unsigned char data[32];
1080e2b1b9c0Schristos 	dns_name_t *keyname;
1081e2b1b9c0Schristos 	dns_rdata_t rdata = DNS_RDATA_INIT;
1082e2b1b9c0Schristos 	isc_stdtime_t now;
1083e2b1b9c0Schristos 	isc_result_t ret;
1084e2b1b9c0Schristos 	dns_tsigkey_t *tsigkey;
1085e2b1b9c0Schristos 	dst_key_t *key = NULL;
1086e2b1b9c0Schristos 	unsigned char header[DNS_MESSAGE_HEADERLEN];
1087e2b1b9c0Schristos 	dst_context_t *ctx = NULL;
1088e2b1b9c0Schristos 	isc_mem_t *mctx;
1089f2e20987Schristos 	uint16_t addcount, id;
1090e2b1b9c0Schristos 	unsigned int siglen;
1091e2b1b9c0Schristos 	unsigned int alg;
1092f2e20987Schristos 	bool response;
1093e2b1b9c0Schristos 
1094e2b1b9c0Schristos 	REQUIRE(source != NULL);
1095e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
1096e2b1b9c0Schristos 	tsigkey = dns_message_gettsigkey(msg);
1097e2b1b9c0Schristos 	response = is_response(msg);
1098e2b1b9c0Schristos 
1099e2b1b9c0Schristos 	REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
1100e2b1b9c0Schristos 
1101e2b1b9c0Schristos 	msg->verify_attempted = 1;
1102e2b1b9c0Schristos 	msg->verified_sig = 0;
1103e2b1b9c0Schristos 	msg->tsigstatus = dns_tsigerror_badsig;
1104e2b1b9c0Schristos 
1105e2b1b9c0Schristos 	if (msg->tcp_continuation) {
11069742fdb4Schristos 		if (tsigkey == NULL || msg->querytsig == NULL) {
1107e2b1b9c0Schristos 			return (DNS_R_UNEXPECTEDTSIG);
11089742fdb4Schristos 		}
1109e2b1b9c0Schristos 		return (tsig_verify_tcp(source, msg));
1110e2b1b9c0Schristos 	}
1111e2b1b9c0Schristos 
1112e2b1b9c0Schristos 	/*
1113e2b1b9c0Schristos 	 * There should be a TSIG record...
1114e2b1b9c0Schristos 	 */
11159742fdb4Schristos 	if (msg->tsig == NULL) {
1116e2b1b9c0Schristos 		return (DNS_R_EXPECTEDTSIG);
11179742fdb4Schristos 	}
1118e2b1b9c0Schristos 
1119e2b1b9c0Schristos 	/*
1120e2b1b9c0Schristos 	 * If this is a response and there's no key or query TSIG, there
1121e2b1b9c0Schristos 	 * shouldn't be one on the response.
1122e2b1b9c0Schristos 	 */
11239742fdb4Schristos 	if (response && (tsigkey == NULL || msg->querytsig == NULL)) {
1124e2b1b9c0Schristos 		return (DNS_R_UNEXPECTEDTSIG);
11259742fdb4Schristos 	}
1126e2b1b9c0Schristos 
1127e2b1b9c0Schristos 	mctx = msg->mctx;
1128e2b1b9c0Schristos 
1129e2b1b9c0Schristos 	/*
1130e2b1b9c0Schristos 	 * If we're here, we know the message is well formed and contains a
1131e2b1b9c0Schristos 	 * TSIG record.
1132e2b1b9c0Schristos 	 */
1133e2b1b9c0Schristos 
1134e2b1b9c0Schristos 	keyname = msg->tsigname;
1135e2b1b9c0Schristos 	ret = dns_rdataset_first(msg->tsig);
11369742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1137e2b1b9c0Schristos 		return (ret);
11389742fdb4Schristos 	}
1139e2b1b9c0Schristos 	dns_rdataset_current(msg->tsig, &rdata);
1140e2b1b9c0Schristos 	ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
11419742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1142e2b1b9c0Schristos 		return (ret);
11439742fdb4Schristos 	}
1144e2b1b9c0Schristos 	dns_rdata_reset(&rdata);
1145e2b1b9c0Schristos 	if (response) {
1146e2b1b9c0Schristos 		ret = dns_rdataset_first(msg->querytsig);
11479742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1148e2b1b9c0Schristos 			return (ret);
1149e2b1b9c0Schristos 		}
11509742fdb4Schristos 		dns_rdataset_current(msg->querytsig, &rdata);
11519742fdb4Schristos 		ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
11529742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
11539742fdb4Schristos 			return (ret);
11549742fdb4Schristos 		}
11559742fdb4Schristos 	}
1156e2b1b9c0Schristos 
1157e2b1b9c0Schristos 	/*
1158e2b1b9c0Schristos 	 * Do the key name and algorithm match that of the query?
1159e2b1b9c0Schristos 	 */
1160e2b1b9c0Schristos 	if (response &&
1161e2b1b9c0Schristos 	    (!dns_name_equal(keyname, &tsigkey->name) ||
11629742fdb4Schristos 	     !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)))
11639742fdb4Schristos 	{
1164e2b1b9c0Schristos 		msg->tsigstatus = dns_tsigerror_badkey;
1165e2b1b9c0Schristos 		tsig_log(msg->tsigkey, 2,
1166e2b1b9c0Schristos 			 "key name and algorithm do not match");
1167e2b1b9c0Schristos 		return (DNS_R_TSIGVERIFYFAILURE);
1168e2b1b9c0Schristos 	}
1169e2b1b9c0Schristos 
1170e2b1b9c0Schristos 	/*
1171e2b1b9c0Schristos 	 * Get the current time.
1172e2b1b9c0Schristos 	 */
1173e2b1b9c0Schristos 	isc_stdtime_get(&now);
1174e2b1b9c0Schristos 
1175e2b1b9c0Schristos 	/*
1176e2b1b9c0Schristos 	 * Find dns_tsigkey_t based on keyname.
1177e2b1b9c0Schristos 	 */
1178e2b1b9c0Schristos 	if (tsigkey == NULL) {
1179e2b1b9c0Schristos 		ret = ISC_R_NOTFOUND;
11809742fdb4Schristos 		if (ring1 != NULL) {
1181e2b1b9c0Schristos 			ret = dns_tsigkey_find(&tsigkey, keyname,
1182e2b1b9c0Schristos 					       &tsig.algorithm, ring1);
11839742fdb4Schristos 		}
11849742fdb4Schristos 		if (ret == ISC_R_NOTFOUND && ring2 != NULL) {
1185e2b1b9c0Schristos 			ret = dns_tsigkey_find(&tsigkey, keyname,
1186e2b1b9c0Schristos 					       &tsig.algorithm, ring2);
11879742fdb4Schristos 		}
1188e2b1b9c0Schristos 		if (ret != ISC_R_SUCCESS) {
1189e2b1b9c0Schristos 			msg->tsigstatus = dns_tsigerror_badkey;
11909742fdb4Schristos 			ret = dns_tsigkey_create(keyname, &tsig.algorithm, NULL,
11919742fdb4Schristos 						 0, false, NULL, now, now, mctx,
11929742fdb4Schristos 						 NULL, &msg->tsigkey);
11939742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
1194e2b1b9c0Schristos 				return (ret);
11959742fdb4Schristos 			}
1196e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2, "unknown key");
1197e2b1b9c0Schristos 			return (DNS_R_TSIGVERIFYFAILURE);
1198e2b1b9c0Schristos 		}
1199e2b1b9c0Schristos 		msg->tsigkey = tsigkey;
1200e2b1b9c0Schristos 	}
1201e2b1b9c0Schristos 
1202e2b1b9c0Schristos 	key = tsigkey->key;
1203e2b1b9c0Schristos 
1204e2b1b9c0Schristos 	/*
1205e2b1b9c0Schristos 	 * Check digest length.
1206e2b1b9c0Schristos 	 */
1207e2b1b9c0Schristos 	alg = dst_key_alg(key);
1208e2b1b9c0Schristos 	ret = dst_key_sigsize(key, &siglen);
12099742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1210e2b1b9c0Schristos 		return (ret);
12119742fdb4Schristos 	}
1212e2b1b9c0Schristos 	if (dns__tsig_algvalid(alg)) {
1213e2b1b9c0Schristos 		if (tsig.siglen > siglen) {
1214e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2, "signature length too big");
1215e2b1b9c0Schristos 			return (DNS_R_FORMERR);
1216e2b1b9c0Schristos 		}
1217e2b1b9c0Schristos 		if (tsig.siglen > 0 &&
1218*4ac1c27eSchristos 		    (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2)))
1219*4ac1c27eSchristos 		{
1220e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2,
1221e2b1b9c0Schristos 				 "signature length below minimum");
1222e2b1b9c0Schristos 			return (DNS_R_FORMERR);
1223e2b1b9c0Schristos 		}
1224e2b1b9c0Schristos 	}
1225e2b1b9c0Schristos 
1226e2b1b9c0Schristos 	if (tsig.siglen > 0) {
1227f2e20987Schristos 		uint16_t addcount_n;
1228e2b1b9c0Schristos 
1229e2b1b9c0Schristos 		sig_r.base = tsig.signature;
1230e2b1b9c0Schristos 		sig_r.length = tsig.siglen;
1231e2b1b9c0Schristos 
12329742fdb4Schristos 		ret = dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC,
1233f2e20987Schristos 					 false, 0, &ctx);
12349742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1235e2b1b9c0Schristos 			return (ret);
12369742fdb4Schristos 		}
1237e2b1b9c0Schristos 
1238e2b1b9c0Schristos 		if (response) {
1239e2b1b9c0Schristos 			isc_buffer_init(&databuf, data, sizeof(data));
1240e2b1b9c0Schristos 			isc_buffer_putuint16(&databuf, querytsig.siglen);
1241e2b1b9c0Schristos 			isc_buffer_usedregion(&databuf, &r);
1242e2b1b9c0Schristos 			ret = dst_context_adddata(ctx, &r);
12439742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
1244e2b1b9c0Schristos 				goto cleanup_context;
12459742fdb4Schristos 			}
1246e2b1b9c0Schristos 			if (querytsig.siglen > 0) {
1247e2b1b9c0Schristos 				r.length = querytsig.siglen;
1248e2b1b9c0Schristos 				r.base = querytsig.signature;
1249e2b1b9c0Schristos 				ret = dst_context_adddata(ctx, &r);
12509742fdb4Schristos 				if (ret != ISC_R_SUCCESS) {
1251e2b1b9c0Schristos 					goto cleanup_context;
1252e2b1b9c0Schristos 				}
1253e2b1b9c0Schristos 			}
12549742fdb4Schristos 		}
1255e2b1b9c0Schristos 
1256e2b1b9c0Schristos 		/*
1257e2b1b9c0Schristos 		 * Extract the header.
1258e2b1b9c0Schristos 		 */
1259e2b1b9c0Schristos 		isc_buffer_usedregion(source, &r);
1260e2b1b9c0Schristos 		memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1261e2b1b9c0Schristos 		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1262e2b1b9c0Schristos 
1263e2b1b9c0Schristos 		/*
1264e2b1b9c0Schristos 		 * Decrement the additional field counter.
1265e2b1b9c0Schristos 		 */
1266e2b1b9c0Schristos 		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1267e2b1b9c0Schristos 		addcount_n = ntohs(addcount);
1268f2e20987Schristos 		addcount = htons((uint16_t)(addcount_n - 1));
1269e2b1b9c0Schristos 		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1270e2b1b9c0Schristos 
1271e2b1b9c0Schristos 		/*
1272e2b1b9c0Schristos 		 * Put in the original id.
1273e2b1b9c0Schristos 		 */
1274e2b1b9c0Schristos 		id = htons(tsig.originalid);
1275e2b1b9c0Schristos 		memmove(&header[0], &id, 2);
1276e2b1b9c0Schristos 
1277e2b1b9c0Schristos 		/*
1278e2b1b9c0Schristos 		 * Digest the modified header.
1279e2b1b9c0Schristos 		 */
1280e2b1b9c0Schristos 		header_r.base = (unsigned char *)header;
1281e2b1b9c0Schristos 		header_r.length = DNS_MESSAGE_HEADERLEN;
1282e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &header_r);
12839742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1284e2b1b9c0Schristos 			goto cleanup_context;
12859742fdb4Schristos 		}
1286e2b1b9c0Schristos 
1287e2b1b9c0Schristos 		/*
1288e2b1b9c0Schristos 		 * Digest all non-TSIG records.
1289e2b1b9c0Schristos 		 */
1290e2b1b9c0Schristos 		isc_buffer_usedregion(source, &source_r);
1291e2b1b9c0Schristos 		r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
1292e2b1b9c0Schristos 		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
1293e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
12949742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1295e2b1b9c0Schristos 			goto cleanup_context;
12969742fdb4Schristos 		}
1297e2b1b9c0Schristos 
1298e2b1b9c0Schristos 		/*
1299e2b1b9c0Schristos 		 * Digest the key name.
1300e2b1b9c0Schristos 		 */
1301e2b1b9c0Schristos 		dns_name_toregion(&tsigkey->name, &r);
1302e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
13039742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1304e2b1b9c0Schristos 			goto cleanup_context;
13059742fdb4Schristos 		}
1306e2b1b9c0Schristos 
1307e2b1b9c0Schristos 		isc_buffer_init(&databuf, data, sizeof(data));
1308e2b1b9c0Schristos 		isc_buffer_putuint16(&databuf, tsig.common.rdclass);
1309e2b1b9c0Schristos 		isc_buffer_putuint32(&databuf, msg->tsig->ttl);
1310e2b1b9c0Schristos 		isc_buffer_usedregion(&databuf, &r);
1311e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
13129742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1313e2b1b9c0Schristos 			goto cleanup_context;
13149742fdb4Schristos 		}
1315e2b1b9c0Schristos 
1316e2b1b9c0Schristos 		/*
1317e2b1b9c0Schristos 		 * Digest the key algorithm.
1318e2b1b9c0Schristos 		 */
1319e2b1b9c0Schristos 		dns_name_toregion(tsigkey->algorithm, &r);
1320e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
13219742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1322e2b1b9c0Schristos 			goto cleanup_context;
13239742fdb4Schristos 		}
1324e2b1b9c0Schristos 
1325e2b1b9c0Schristos 		isc_buffer_clear(&databuf);
1326e2b1b9c0Schristos 		isc_buffer_putuint48(&databuf, tsig.timesigned);
1327e2b1b9c0Schristos 		isc_buffer_putuint16(&databuf, tsig.fudge);
1328e2b1b9c0Schristos 		isc_buffer_putuint16(&databuf, tsig.error);
1329e2b1b9c0Schristos 		isc_buffer_putuint16(&databuf, tsig.otherlen);
1330e2b1b9c0Schristos 		isc_buffer_usedregion(&databuf, &r);
1331e2b1b9c0Schristos 		ret = dst_context_adddata(ctx, &r);
13329742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1333e2b1b9c0Schristos 			goto cleanup_context;
13349742fdb4Schristos 		}
1335e2b1b9c0Schristos 
1336e2b1b9c0Schristos 		if (tsig.otherlen > 0) {
1337e2b1b9c0Schristos 			r.base = tsig.other;
1338e2b1b9c0Schristos 			r.length = tsig.otherlen;
1339e2b1b9c0Schristos 			ret = dst_context_adddata(ctx, &r);
13409742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
1341e2b1b9c0Schristos 				goto cleanup_context;
1342e2b1b9c0Schristos 			}
13439742fdb4Schristos 		}
1344e2b1b9c0Schristos 
1345e2b1b9c0Schristos 		ret = dst_context_verify(ctx, &sig_r);
1346e2b1b9c0Schristos 		if (ret == DST_R_VERIFYFAILURE) {
1347e2b1b9c0Schristos 			ret = DNS_R_TSIGVERIFYFAILURE;
1348e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2,
1349e2b1b9c0Schristos 				 "signature failed to verify(1)");
1350e2b1b9c0Schristos 			goto cleanup_context;
1351e2b1b9c0Schristos 		} else if (ret != ISC_R_SUCCESS) {
1352e2b1b9c0Schristos 			goto cleanup_context;
1353e2b1b9c0Schristos 		}
1354e2b1b9c0Schristos 		msg->verified_sig = 1;
13559742fdb4Schristos 	} else if (!response || (tsig.error != dns_tsigerror_badsig &&
13569742fdb4Schristos 				 tsig.error != dns_tsigerror_badkey))
13579742fdb4Schristos 	{
1358e2b1b9c0Schristos 		tsig_log(msg->tsigkey, 2, "signature was empty");
1359e2b1b9c0Schristos 		return (DNS_R_TSIGVERIFYFAILURE);
1360e2b1b9c0Schristos 	}
1361e2b1b9c0Schristos 
1362e2b1b9c0Schristos 	/*
1363e2b1b9c0Schristos 	 * Here at this point, the MAC has been verified. Even if any of
1364e2b1b9c0Schristos 	 * the following code returns a TSIG error, the reply will be
1365e2b1b9c0Schristos 	 * signed and WILL always include the request MAC in the digest
1366e2b1b9c0Schristos 	 * computation.
1367e2b1b9c0Schristos 	 */
1368e2b1b9c0Schristos 
1369e2b1b9c0Schristos 	/*
1370e2b1b9c0Schristos 	 * Is the time ok?
1371e2b1b9c0Schristos 	 */
1372e2b1b9c0Schristos 	if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1373e2b1b9c0Schristos 		msg->tsigstatus = dns_tsigerror_badtime;
1374e2b1b9c0Schristos 		tsig_log(msg->tsigkey, 2, "signature has expired");
1375e2b1b9c0Schristos 		ret = DNS_R_CLOCKSKEW;
1376e2b1b9c0Schristos 		goto cleanup_context;
1377e2b1b9c0Schristos 	} else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
1378e2b1b9c0Schristos 		msg->tsigstatus = dns_tsigerror_badtime;
1379e2b1b9c0Schristos 		tsig_log(msg->tsigkey, 2, "signature is in the future");
1380e2b1b9c0Schristos 		ret = DNS_R_CLOCKSKEW;
1381e2b1b9c0Schristos 		goto cleanup_context;
1382e2b1b9c0Schristos 	}
1383e2b1b9c0Schristos 
1384e2b1b9c0Schristos 	if (dns__tsig_algvalid(alg)) {
1385f2e20987Schristos 		uint16_t digestbits = dst_key_getbits(key);
1386e2b1b9c0Schristos 
1387e2b1b9c0Schristos 		if (tsig.siglen > 0 && digestbits != 0 &&
1388*4ac1c27eSchristos 		    tsig.siglen < ((digestbits + 7) / 8))
1389*4ac1c27eSchristos 		{
1390e2b1b9c0Schristos 			msg->tsigstatus = dns_tsigerror_badtrunc;
1391e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2,
1392e2b1b9c0Schristos 				 "truncated signature length too small");
1393e2b1b9c0Schristos 			ret = DNS_R_TSIGVERIFYFAILURE;
1394e2b1b9c0Schristos 			goto cleanup_context;
1395e2b1b9c0Schristos 		}
13969742fdb4Schristos 		if (tsig.siglen > 0 && digestbits == 0 && tsig.siglen < siglen)
1397e2b1b9c0Schristos 		{
1398e2b1b9c0Schristos 			msg->tsigstatus = dns_tsigerror_badtrunc;
1399e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2, "signature length too small");
1400e2b1b9c0Schristos 			ret = DNS_R_TSIGVERIFYFAILURE;
1401e2b1b9c0Schristos 			goto cleanup_context;
1402e2b1b9c0Schristos 		}
1403e2b1b9c0Schristos 	}
1404e2b1b9c0Schristos 
14059742fdb4Schristos 	if (response && tsig.error != dns_rcode_noerror) {
1406e2b1b9c0Schristos 		msg->tsigstatus = tsig.error;
14079742fdb4Schristos 		if (tsig.error == dns_tsigerror_badtime) {
1408e2b1b9c0Schristos 			ret = DNS_R_CLOCKSKEW;
14099742fdb4Schristos 		} else {
1410e2b1b9c0Schristos 			ret = DNS_R_TSIGERRORSET;
14119742fdb4Schristos 		}
1412e2b1b9c0Schristos 		goto cleanup_context;
1413e2b1b9c0Schristos 	}
1414e2b1b9c0Schristos 
1415e2b1b9c0Schristos 	msg->tsigstatus = dns_rcode_noerror;
1416e2b1b9c0Schristos 	ret = ISC_R_SUCCESS;
1417e2b1b9c0Schristos 
1418e2b1b9c0Schristos cleanup_context:
14199742fdb4Schristos 	if (ctx != NULL) {
1420e2b1b9c0Schristos 		dst_context_destroy(&ctx);
14219742fdb4Schristos 	}
1422e2b1b9c0Schristos 
1423e2b1b9c0Schristos 	return (ret);
1424e2b1b9c0Schristos }
1425e2b1b9c0Schristos 
1426e2b1b9c0Schristos static isc_result_t
tsig_verify_tcp(isc_buffer_t * source,dns_message_t * msg)1427e2b1b9c0Schristos tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
1428e2b1b9c0Schristos 	dns_rdata_any_tsig_t tsig, querytsig;
1429e2b1b9c0Schristos 	isc_region_t r, source_r, header_r, sig_r;
1430e2b1b9c0Schristos 	isc_buffer_t databuf;
1431e2b1b9c0Schristos 	unsigned char data[32];
1432e2b1b9c0Schristos 	dns_name_t *keyname;
1433e2b1b9c0Schristos 	dns_rdata_t rdata = DNS_RDATA_INIT;
1434e2b1b9c0Schristos 	isc_stdtime_t now;
1435e2b1b9c0Schristos 	isc_result_t ret;
1436e2b1b9c0Schristos 	dns_tsigkey_t *tsigkey;
1437e2b1b9c0Schristos 	dst_key_t *key = NULL;
1438e2b1b9c0Schristos 	unsigned char header[DNS_MESSAGE_HEADERLEN];
1439f2e20987Schristos 	uint16_t addcount, id;
1440f2e20987Schristos 	bool has_tsig = false;
1441e2b1b9c0Schristos 	isc_mem_t *mctx;
1442e2b1b9c0Schristos 	unsigned int siglen;
1443e2b1b9c0Schristos 	unsigned int alg;
1444e2b1b9c0Schristos 
1445e2b1b9c0Schristos 	REQUIRE(source != NULL);
1446e2b1b9c0Schristos 	REQUIRE(msg != NULL);
1447e2b1b9c0Schristos 	REQUIRE(dns_message_gettsigkey(msg) != NULL);
1448e2b1b9c0Schristos 	REQUIRE(msg->tcp_continuation == 1);
1449e2b1b9c0Schristos 	REQUIRE(msg->querytsig != NULL);
1450e2b1b9c0Schristos 
1451e2b1b9c0Schristos 	msg->verified_sig = 0;
1452e2b1b9c0Schristos 	msg->tsigstatus = dns_tsigerror_badsig;
1453e2b1b9c0Schristos 
14549742fdb4Schristos 	if (!is_response(msg)) {
1455e2b1b9c0Schristos 		return (DNS_R_EXPECTEDRESPONSE);
14569742fdb4Schristos 	}
1457e2b1b9c0Schristos 
1458e2b1b9c0Schristos 	mctx = msg->mctx;
1459e2b1b9c0Schristos 
1460e2b1b9c0Schristos 	tsigkey = dns_message_gettsigkey(msg);
1461e2b1b9c0Schristos 	key = tsigkey->key;
1462e2b1b9c0Schristos 
1463e2b1b9c0Schristos 	/*
1464e2b1b9c0Schristos 	 * Extract and parse the previous TSIG
1465e2b1b9c0Schristos 	 */
1466e2b1b9c0Schristos 	ret = dns_rdataset_first(msg->querytsig);
14679742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1468e2b1b9c0Schristos 		return (ret);
14699742fdb4Schristos 	}
1470e2b1b9c0Schristos 	dns_rdataset_current(msg->querytsig, &rdata);
1471e2b1b9c0Schristos 	ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
14729742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1473e2b1b9c0Schristos 		return (ret);
14749742fdb4Schristos 	}
1475e2b1b9c0Schristos 	dns_rdata_reset(&rdata);
1476e2b1b9c0Schristos 
1477e2b1b9c0Schristos 	/*
1478e2b1b9c0Schristos 	 * If there is a TSIG in this message, do some checks.
1479e2b1b9c0Schristos 	 */
1480e2b1b9c0Schristos 	if (msg->tsig != NULL) {
1481f2e20987Schristos 		has_tsig = true;
1482e2b1b9c0Schristos 
1483e2b1b9c0Schristos 		keyname = msg->tsigname;
1484e2b1b9c0Schristos 		ret = dns_rdataset_first(msg->tsig);
14859742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1486e2b1b9c0Schristos 			goto cleanup_querystruct;
14879742fdb4Schristos 		}
1488e2b1b9c0Schristos 		dns_rdataset_current(msg->tsig, &rdata);
1489e2b1b9c0Schristos 		ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
14909742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1491e2b1b9c0Schristos 			goto cleanup_querystruct;
14929742fdb4Schristos 		}
1493e2b1b9c0Schristos 
1494e2b1b9c0Schristos 		/*
1495e2b1b9c0Schristos 		 * Do the key name and algorithm match that of the query?
1496e2b1b9c0Schristos 		 */
1497e2b1b9c0Schristos 		if (!dns_name_equal(keyname, &tsigkey->name) ||
1498e2b1b9c0Schristos 		    !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))
1499e2b1b9c0Schristos 		{
1500e2b1b9c0Schristos 			msg->tsigstatus = dns_tsigerror_badkey;
1501e2b1b9c0Schristos 			ret = DNS_R_TSIGVERIFYFAILURE;
1502e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2,
1503e2b1b9c0Schristos 				 "key name and algorithm do not match");
1504e2b1b9c0Schristos 			goto cleanup_querystruct;
1505e2b1b9c0Schristos 		}
1506e2b1b9c0Schristos 
1507e2b1b9c0Schristos 		/*
1508e2b1b9c0Schristos 		 * Check digest length.
1509e2b1b9c0Schristos 		 */
1510e2b1b9c0Schristos 		alg = dst_key_alg(key);
1511e2b1b9c0Schristos 		ret = dst_key_sigsize(key, &siglen);
15129742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1513e2b1b9c0Schristos 			goto cleanup_querystruct;
15149742fdb4Schristos 		}
1515e2b1b9c0Schristos 		if (dns__tsig_algvalid(alg)) {
1516e2b1b9c0Schristos 			if (tsig.siglen > siglen) {
1517e2b1b9c0Schristos 				tsig_log(tsigkey, 2,
1518e2b1b9c0Schristos 					 "signature length too big");
1519e2b1b9c0Schristos 				ret = DNS_R_FORMERR;
1520e2b1b9c0Schristos 				goto cleanup_querystruct;
1521e2b1b9c0Schristos 			}
1522e2b1b9c0Schristos 			if (tsig.siglen > 0 &&
1523e2b1b9c0Schristos 			    (tsig.siglen < 10 ||
1524*4ac1c27eSchristos 			     tsig.siglen < ((siglen + 1) / 2)))
1525*4ac1c27eSchristos 			{
1526e2b1b9c0Schristos 				tsig_log(tsigkey, 2,
1527e2b1b9c0Schristos 					 "signature length below minimum");
1528e2b1b9c0Schristos 				ret = DNS_R_FORMERR;
1529e2b1b9c0Schristos 				goto cleanup_querystruct;
1530e2b1b9c0Schristos 			}
1531e2b1b9c0Schristos 		}
1532e2b1b9c0Schristos 	}
1533e2b1b9c0Schristos 
1534e2b1b9c0Schristos 	if (msg->tsigctx == NULL) {
15359742fdb4Schristos 		ret = dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC,
1536f2e20987Schristos 					 false, 0, &msg->tsigctx);
15379742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1538e2b1b9c0Schristos 			goto cleanup_querystruct;
15399742fdb4Schristos 		}
1540e2b1b9c0Schristos 
1541e2b1b9c0Schristos 		/*
1542e2b1b9c0Schristos 		 * Digest the length of the query signature
1543e2b1b9c0Schristos 		 */
1544e2b1b9c0Schristos 		isc_buffer_init(&databuf, data, sizeof(data));
1545e2b1b9c0Schristos 		isc_buffer_putuint16(&databuf, querytsig.siglen);
1546e2b1b9c0Schristos 		isc_buffer_usedregion(&databuf, &r);
1547e2b1b9c0Schristos 		ret = dst_context_adddata(msg->tsigctx, &r);
15489742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1549e2b1b9c0Schristos 			goto cleanup_context;
15509742fdb4Schristos 		}
1551e2b1b9c0Schristos 
1552e2b1b9c0Schristos 		/*
1553e2b1b9c0Schristos 		 * Digest the data of the query signature
1554e2b1b9c0Schristos 		 */
1555e2b1b9c0Schristos 		if (querytsig.siglen > 0) {
1556e2b1b9c0Schristos 			r.length = querytsig.siglen;
1557e2b1b9c0Schristos 			r.base = querytsig.signature;
1558e2b1b9c0Schristos 			ret = dst_context_adddata(msg->tsigctx, &r);
15599742fdb4Schristos 			if (ret != ISC_R_SUCCESS) {
1560e2b1b9c0Schristos 				goto cleanup_context;
1561e2b1b9c0Schristos 			}
1562e2b1b9c0Schristos 		}
15639742fdb4Schristos 	}
1564e2b1b9c0Schristos 
1565e2b1b9c0Schristos 	/*
1566e2b1b9c0Schristos 	 * Extract the header.
1567e2b1b9c0Schristos 	 */
1568e2b1b9c0Schristos 	isc_buffer_usedregion(source, &r);
1569e2b1b9c0Schristos 	memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1570e2b1b9c0Schristos 	isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1571e2b1b9c0Schristos 
1572e2b1b9c0Schristos 	/*
1573e2b1b9c0Schristos 	 * Decrement the additional field counter if necessary.
1574e2b1b9c0Schristos 	 */
1575e2b1b9c0Schristos 	if (has_tsig) {
1576f2e20987Schristos 		uint16_t addcount_n;
1577e2b1b9c0Schristos 
1578e2b1b9c0Schristos 		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1579e2b1b9c0Schristos 		addcount_n = ntohs(addcount);
1580f2e20987Schristos 		addcount = htons((uint16_t)(addcount_n - 1));
1581e2b1b9c0Schristos 		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1582e2b1b9c0Schristos 
1583e2b1b9c0Schristos 		/*
1584e2b1b9c0Schristos 		 * Put in the original id.
1585e2b1b9c0Schristos 		 *
1586e2b1b9c0Schristos 		 * XXX Can TCP transfers be forwarded?  How would that
1587e2b1b9c0Schristos 		 * work?
1588e2b1b9c0Schristos 		 */
15899742fdb4Schristos 		/* cppcheck-suppress uninitStructMember
15909742fdb4Schristos 		 * symbolName=tsig.originalid */
1591e2b1b9c0Schristos 		id = htons(tsig.originalid);
1592e2b1b9c0Schristos 		memmove(&header[0], &id, 2);
1593e2b1b9c0Schristos 	}
1594e2b1b9c0Schristos 
1595e2b1b9c0Schristos 	/*
1596e2b1b9c0Schristos 	 * Digest the modified header.
1597e2b1b9c0Schristos 	 */
1598e2b1b9c0Schristos 	header_r.base = (unsigned char *)header;
1599e2b1b9c0Schristos 	header_r.length = DNS_MESSAGE_HEADERLEN;
1600e2b1b9c0Schristos 	ret = dst_context_adddata(msg->tsigctx, &header_r);
16019742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1602e2b1b9c0Schristos 		goto cleanup_context;
16039742fdb4Schristos 	}
1604e2b1b9c0Schristos 
1605e2b1b9c0Schristos 	/*
1606e2b1b9c0Schristos 	 * Digest all non-TSIG records.
1607e2b1b9c0Schristos 	 */
1608e2b1b9c0Schristos 	isc_buffer_usedregion(source, &source_r);
1609e2b1b9c0Schristos 	r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
16109742fdb4Schristos 	if (has_tsig) {
1611e2b1b9c0Schristos 		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
16129742fdb4Schristos 	} else {
1613e2b1b9c0Schristos 		r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
16149742fdb4Schristos 	}
1615e2b1b9c0Schristos 	ret = dst_context_adddata(msg->tsigctx, &r);
16169742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1617e2b1b9c0Schristos 		goto cleanup_context;
16189742fdb4Schristos 	}
1619e2b1b9c0Schristos 
1620e2b1b9c0Schristos 	/*
1621e2b1b9c0Schristos 	 * Digest the time signed and fudge.
1622e2b1b9c0Schristos 	 */
1623e2b1b9c0Schristos 	if (has_tsig) {
1624e2b1b9c0Schristos 		isc_buffer_init(&databuf, data, sizeof(data));
1625e2b1b9c0Schristos 		isc_buffer_putuint48(&databuf, tsig.timesigned);
1626e2b1b9c0Schristos 		isc_buffer_putuint16(&databuf, tsig.fudge);
1627e2b1b9c0Schristos 		isc_buffer_usedregion(&databuf, &r);
1628e2b1b9c0Schristos 		ret = dst_context_adddata(msg->tsigctx, &r);
16299742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1630e2b1b9c0Schristos 			goto cleanup_context;
16319742fdb4Schristos 		}
1632e2b1b9c0Schristos 
1633e2b1b9c0Schristos 		sig_r.base = tsig.signature;
1634e2b1b9c0Schristos 		sig_r.length = tsig.siglen;
1635e2b1b9c0Schristos 		if (tsig.siglen == 0) {
1636e2b1b9c0Schristos 			if (tsig.error != dns_rcode_noerror) {
1637e2b1b9c0Schristos 				msg->tsigstatus = tsig.error;
1638e2b1b9c0Schristos 				if (tsig.error == dns_tsigerror_badtime) {
1639e2b1b9c0Schristos 					ret = DNS_R_CLOCKSKEW;
1640e2b1b9c0Schristos 				} else {
1641e2b1b9c0Schristos 					ret = DNS_R_TSIGERRORSET;
1642e2b1b9c0Schristos 				}
1643e2b1b9c0Schristos 			} else {
16449742fdb4Schristos 				tsig_log(msg->tsigkey, 2, "signature is empty");
1645e2b1b9c0Schristos 				ret = DNS_R_TSIGVERIFYFAILURE;
1646e2b1b9c0Schristos 			}
1647e2b1b9c0Schristos 			goto cleanup_context;
1648e2b1b9c0Schristos 		}
1649e2b1b9c0Schristos 
1650e2b1b9c0Schristos 		ret = dst_context_verify(msg->tsigctx, &sig_r);
1651e2b1b9c0Schristos 		if (ret == DST_R_VERIFYFAILURE) {
1652e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2,
1653e2b1b9c0Schristos 				 "signature failed to verify(2)");
1654e2b1b9c0Schristos 			ret = DNS_R_TSIGVERIFYFAILURE;
1655e2b1b9c0Schristos 			goto cleanup_context;
1656e2b1b9c0Schristos 		} else if (ret != ISC_R_SUCCESS) {
1657e2b1b9c0Schristos 			goto cleanup_context;
1658e2b1b9c0Schristos 		}
1659e2b1b9c0Schristos 		msg->verified_sig = 1;
1660e2b1b9c0Schristos 
1661e2b1b9c0Schristos 		/*
1662e2b1b9c0Schristos 		 * Here at this point, the MAC has been verified. Even
1663e2b1b9c0Schristos 		 * if any of the following code returns a TSIG error,
1664e2b1b9c0Schristos 		 * the reply will be signed and WILL always include the
1665e2b1b9c0Schristos 		 * request MAC in the digest computation.
1666e2b1b9c0Schristos 		 */
1667e2b1b9c0Schristos 
1668e2b1b9c0Schristos 		/*
1669e2b1b9c0Schristos 		 * Is the time ok?
1670e2b1b9c0Schristos 		 */
1671e2b1b9c0Schristos 		isc_stdtime_get(&now);
1672e2b1b9c0Schristos 
1673e2b1b9c0Schristos 		if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1674e2b1b9c0Schristos 			msg->tsigstatus = dns_tsigerror_badtime;
1675e2b1b9c0Schristos 			tsig_log(msg->tsigkey, 2, "signature has expired");
1676e2b1b9c0Schristos 			ret = DNS_R_CLOCKSKEW;
1677e2b1b9c0Schristos 			goto cleanup_context;
16789742fdb4Schristos 		} else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge)
1679e2b1b9c0Schristos 		{
1680e2b1b9c0Schristos 			msg->tsigstatus = dns_tsigerror_badtime;
16819742fdb4Schristos 			tsig_log(msg->tsigkey, 2, "signature is in the future");
1682e2b1b9c0Schristos 			ret = DNS_R_CLOCKSKEW;
1683e2b1b9c0Schristos 			goto cleanup_context;
1684e2b1b9c0Schristos 		}
1685e2b1b9c0Schristos 
1686e2b1b9c0Schristos 		alg = dst_key_alg(key);
1687e2b1b9c0Schristos 		ret = dst_key_sigsize(key, &siglen);
16889742fdb4Schristos 		if (ret != ISC_R_SUCCESS) {
1689e2b1b9c0Schristos 			goto cleanup_context;
16909742fdb4Schristos 		}
1691e2b1b9c0Schristos 		if (dns__tsig_algvalid(alg)) {
1692f2e20987Schristos 			uint16_t digestbits = dst_key_getbits(key);
1693e2b1b9c0Schristos 
1694e2b1b9c0Schristos 			if (tsig.siglen > 0 && digestbits != 0 &&
1695*4ac1c27eSchristos 			    tsig.siglen < ((digestbits + 7) / 8))
1696*4ac1c27eSchristos 			{
1697e2b1b9c0Schristos 				msg->tsigstatus = dns_tsigerror_badtrunc;
1698e2b1b9c0Schristos 				tsig_log(msg->tsigkey, 2,
1699e2b1b9c0Schristos 					 "truncated signature length "
1700e2b1b9c0Schristos 					 "too small");
1701e2b1b9c0Schristos 				ret = DNS_R_TSIGVERIFYFAILURE;
1702e2b1b9c0Schristos 				goto cleanup_context;
1703e2b1b9c0Schristos 			}
1704e2b1b9c0Schristos 			if (tsig.siglen > 0 && digestbits == 0 &&
1705*4ac1c27eSchristos 			    tsig.siglen < siglen)
1706*4ac1c27eSchristos 			{
1707e2b1b9c0Schristos 				msg->tsigstatus = dns_tsigerror_badtrunc;
1708e2b1b9c0Schristos 				tsig_log(msg->tsigkey, 2,
1709e2b1b9c0Schristos 					 "signature length too small");
1710e2b1b9c0Schristos 				ret = DNS_R_TSIGVERIFYFAILURE;
1711e2b1b9c0Schristos 				goto cleanup_context;
1712e2b1b9c0Schristos 			}
1713e2b1b9c0Schristos 		}
1714e2b1b9c0Schristos 
1715e2b1b9c0Schristos 		if (tsig.error != dns_rcode_noerror) {
1716e2b1b9c0Schristos 			msg->tsigstatus = tsig.error;
17179742fdb4Schristos 			if (tsig.error == dns_tsigerror_badtime) {
1718e2b1b9c0Schristos 				ret = DNS_R_CLOCKSKEW;
17199742fdb4Schristos 			} else {
1720e2b1b9c0Schristos 				ret = DNS_R_TSIGERRORSET;
17219742fdb4Schristos 			}
1722e2b1b9c0Schristos 			goto cleanup_context;
1723e2b1b9c0Schristos 		}
1724e2b1b9c0Schristos 	}
1725e2b1b9c0Schristos 
1726e2b1b9c0Schristos 	msg->tsigstatus = dns_rcode_noerror;
1727e2b1b9c0Schristos 	ret = ISC_R_SUCCESS;
1728e2b1b9c0Schristos 
1729e2b1b9c0Schristos cleanup_context:
1730e2b1b9c0Schristos 	/*
1731e2b1b9c0Schristos 	 * Except in error conditions, don't destroy the DST context
1732e2b1b9c0Schristos 	 * for unsigned messages; it is a running sum till the next
1733e2b1b9c0Schristos 	 * TSIG signed message.
1734e2b1b9c0Schristos 	 */
1735e2b1b9c0Schristos 	if ((ret != ISC_R_SUCCESS || has_tsig) && msg->tsigctx != NULL) {
1736e2b1b9c0Schristos 		dst_context_destroy(&msg->tsigctx);
1737e2b1b9c0Schristos 	}
1738e2b1b9c0Schristos 
1739e2b1b9c0Schristos cleanup_querystruct:
1740e2b1b9c0Schristos 	dns_rdata_freestruct(&querytsig);
1741e2b1b9c0Schristos 
1742e2b1b9c0Schristos 	return (ret);
1743e2b1b9c0Schristos }
1744e2b1b9c0Schristos 
1745e2b1b9c0Schristos isc_result_t
dns_tsigkey_find(dns_tsigkey_t ** tsigkey,const dns_name_t * name,const dns_name_t * algorithm,dns_tsig_keyring_t * ring)1746e2b1b9c0Schristos dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
17479742fdb4Schristos 		 const dns_name_t *algorithm, dns_tsig_keyring_t *ring) {
1748e2b1b9c0Schristos 	dns_tsigkey_t *key;
1749e2b1b9c0Schristos 	isc_stdtime_t now;
1750e2b1b9c0Schristos 	isc_result_t result;
1751e2b1b9c0Schristos 
1752e2b1b9c0Schristos 	REQUIRE(tsigkey != NULL);
1753e2b1b9c0Schristos 	REQUIRE(*tsigkey == NULL);
1754e2b1b9c0Schristos 	REQUIRE(name != NULL);
1755e2b1b9c0Schristos 	REQUIRE(ring != NULL);
1756e2b1b9c0Schristos 
1757e2b1b9c0Schristos 	RWLOCK(&ring->lock, isc_rwlocktype_write);
1758e2b1b9c0Schristos 	cleanup_ring(ring);
1759e2b1b9c0Schristos 	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1760e2b1b9c0Schristos 
1761e2b1b9c0Schristos 	isc_stdtime_get(&now);
1762e2b1b9c0Schristos 	RWLOCK(&ring->lock, isc_rwlocktype_read);
1763e2b1b9c0Schristos 	key = NULL;
1764e2b1b9c0Schristos 	result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
1765e2b1b9c0Schristos 	if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
1766e2b1b9c0Schristos 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1767e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
1768e2b1b9c0Schristos 	}
1769e2b1b9c0Schristos 	if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
1770e2b1b9c0Schristos 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1771e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
1772e2b1b9c0Schristos 	}
1773e2b1b9c0Schristos 	if (key->inception != key->expire && isc_serial_lt(key->expire, now)) {
1774e2b1b9c0Schristos 		/*
1775e2b1b9c0Schristos 		 * The key has expired.
1776e2b1b9c0Schristos 		 */
1777e2b1b9c0Schristos 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1778e2b1b9c0Schristos 		RWLOCK(&ring->lock, isc_rwlocktype_write);
1779e2b1b9c0Schristos 		remove_fromring(key);
1780e2b1b9c0Schristos 		RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1781e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
1782e2b1b9c0Schristos 	}
1783e2b1b9c0Schristos #if 0
1784e2b1b9c0Schristos 	/*
1785e2b1b9c0Schristos 	 * MPAXXX We really should look at the inception time.
1786e2b1b9c0Schristos 	 */
1787e2b1b9c0Schristos 	if (key->inception != key->expire &&
1788e2b1b9c0Schristos 	    isc_serial_lt(key->inception, now)) {
1789e2b1b9c0Schristos 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1790e2b1b9c0Schristos 		adjust_lru(key);
1791e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
1792e2b1b9c0Schristos 	}
17939742fdb4Schristos #endif /* if 0 */
1794f2e20987Schristos 	isc_refcount_increment(&key->refs);
1795e2b1b9c0Schristos 	RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1796e2b1b9c0Schristos 	adjust_lru(key);
1797e2b1b9c0Schristos 	*tsigkey = key;
1798e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1799e2b1b9c0Schristos }
1800e2b1b9c0Schristos 
1801e2b1b9c0Schristos static void
free_tsignode(void * node,void * _unused)1802e2b1b9c0Schristos free_tsignode(void *node, void *_unused) {
1803e2b1b9c0Schristos 	dns_tsigkey_t *key;
1804e2b1b9c0Schristos 
1805e2b1b9c0Schristos 	REQUIRE(node != NULL);
1806e2b1b9c0Schristos 
1807e2b1b9c0Schristos 	UNUSED(_unused);
1808e2b1b9c0Schristos 
1809e2b1b9c0Schristos 	key = node;
1810e2b1b9c0Schristos 	if (key->generated) {
18119742fdb4Schristos 		if (ISC_LINK_LINKED(key, link)) {
1812e2b1b9c0Schristos 			ISC_LIST_UNLINK(key->ring->lru, key, link);
1813e2b1b9c0Schristos 		}
18149742fdb4Schristos 	}
1815e2b1b9c0Schristos 	dns_tsigkey_detach(&key);
1816e2b1b9c0Schristos }
1817e2b1b9c0Schristos 
1818e2b1b9c0Schristos isc_result_t
dns_tsigkeyring_create(isc_mem_t * mctx,dns_tsig_keyring_t ** ringp)1819e2b1b9c0Schristos dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
1820e2b1b9c0Schristos 	isc_result_t result;
1821e2b1b9c0Schristos 	dns_tsig_keyring_t *ring;
1822e2b1b9c0Schristos 
1823e2b1b9c0Schristos 	REQUIRE(mctx != NULL);
1824e2b1b9c0Schristos 	REQUIRE(ringp != NULL);
1825e2b1b9c0Schristos 	REQUIRE(*ringp == NULL);
1826e2b1b9c0Schristos 
1827e2b1b9c0Schristos 	ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t));
1828e2b1b9c0Schristos 
182976b1fd8fSchristos 	isc_rwlock_init(&ring->lock, 0, 0);
1830e2b1b9c0Schristos 	ring->keys = NULL;
1831e2b1b9c0Schristos 	result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys);
1832e2b1b9c0Schristos 	if (result != ISC_R_SUCCESS) {
1833e2b1b9c0Schristos 		isc_rwlock_destroy(&ring->lock);
1834e2b1b9c0Schristos 		isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
1835e2b1b9c0Schristos 		return (result);
1836e2b1b9c0Schristos 	}
1837e2b1b9c0Schristos 
1838e2b1b9c0Schristos 	ring->writecount = 0;
1839e2b1b9c0Schristos 	ring->mctx = NULL;
1840e2b1b9c0Schristos 	ring->generated = 0;
1841e2b1b9c0Schristos 	ring->maxgenerated = DNS_TSIG_MAXGENERATEDKEYS;
1842e2b1b9c0Schristos 	ISC_LIST_INIT(ring->lru);
1843e2b1b9c0Schristos 	isc_mem_attach(mctx, &ring->mctx);
18449742fdb4Schristos 	isc_refcount_init(&ring->references, 1);
1845e2b1b9c0Schristos 
1846e2b1b9c0Schristos 	*ringp = ring;
1847e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1848e2b1b9c0Schristos }
1849e2b1b9c0Schristos 
1850e2b1b9c0Schristos isc_result_t
dns_tsigkeyring_add(dns_tsig_keyring_t * ring,const dns_name_t * name,dns_tsigkey_t * tkey)1851e2b1b9c0Schristos dns_tsigkeyring_add(dns_tsig_keyring_t *ring, const dns_name_t *name,
18529742fdb4Schristos 		    dns_tsigkey_t *tkey) {
1853e2b1b9c0Schristos 	isc_result_t result;
1854e2b1b9c0Schristos 
1855e2b1b9c0Schristos 	result = keyring_add(ring, name, tkey);
18569742fdb4Schristos 	if (result == ISC_R_SUCCESS) {
1857f2e20987Schristos 		isc_refcount_increment(&tkey->refs);
18589742fdb4Schristos 	}
1859e2b1b9c0Schristos 
1860e2b1b9c0Schristos 	return (result);
1861e2b1b9c0Schristos }
1862e2b1b9c0Schristos 
1863e2b1b9c0Schristos void
dns_tsigkeyring_attach(dns_tsig_keyring_t * source,dns_tsig_keyring_t ** target)18649742fdb4Schristos dns_tsigkeyring_attach(dns_tsig_keyring_t *source,
18659742fdb4Schristos 		       dns_tsig_keyring_t **target) {
1866e2b1b9c0Schristos 	REQUIRE(source != NULL);
1867e2b1b9c0Schristos 	REQUIRE(target != NULL && *target == NULL);
1868e2b1b9c0Schristos 
18699742fdb4Schristos 	isc_refcount_increment(&source->references);
18709742fdb4Schristos 
1871e2b1b9c0Schristos 	*target = source;
1872e2b1b9c0Schristos }
1873e2b1b9c0Schristos 
1874e2b1b9c0Schristos void
dns_tsigkeyring_detach(dns_tsig_keyring_t ** ringp)1875e2b1b9c0Schristos dns_tsigkeyring_detach(dns_tsig_keyring_t **ringp) {
1876e2b1b9c0Schristos 	dns_tsig_keyring_t *ring;
1877e2b1b9c0Schristos 
1878e2b1b9c0Schristos 	REQUIRE(ringp != NULL);
1879e2b1b9c0Schristos 	REQUIRE(*ringp != NULL);
1880e2b1b9c0Schristos 
1881e2b1b9c0Schristos 	ring = *ringp;
1882e2b1b9c0Schristos 	*ringp = NULL;
1883e2b1b9c0Schristos 
18849742fdb4Schristos 	if (isc_refcount_decrement(&ring->references) == 1) {
1885e2b1b9c0Schristos 		destroyring(ring);
1886e2b1b9c0Schristos 	}
18879742fdb4Schristos }
1888e2b1b9c0Schristos 
1889e2b1b9c0Schristos void
dns_keyring_restore(dns_tsig_keyring_t * ring,FILE * fp)1890e2b1b9c0Schristos dns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp) {
1891e2b1b9c0Schristos 	isc_stdtime_t now;
1892e2b1b9c0Schristos 	isc_result_t result;
1893e2b1b9c0Schristos 
1894e2b1b9c0Schristos 	isc_stdtime_get(&now);
1895e2b1b9c0Schristos 	do {
1896e2b1b9c0Schristos 		result = restore_key(ring, now, fp);
18979742fdb4Schristos 		if (result == ISC_R_NOMORE) {
1898e2b1b9c0Schristos 			return;
18999742fdb4Schristos 		}
19009742fdb4Schristos 		if (result == DNS_R_BADALG || result == DNS_R_EXPIRED) {
1901e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
19029742fdb4Schristos 		}
1903e2b1b9c0Schristos 	} while (result == ISC_R_SUCCESS);
1904e2b1b9c0Schristos }
1905