1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2002  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18135446Strhodes/*
19234010Sdougb * $Id$
20135446Strhodes */
21170222Sdougb/*! \file */
22135446Strhodes#include <config.h>
23135446Strhodes#include <stdlib.h>
24135446Strhodes
25135446Strhodes#include <isc/buffer.h>
26135446Strhodes#include <isc/mem.h>
27135446Strhodes#include <isc/print.h>
28135446Strhodes#include <isc/refcount.h>
29218384Sdougb#include <isc/serial.h>
30135446Strhodes#include <isc/string.h>		/* Required for HP/UX (and others?) */
31135446Strhodes#include <isc/util.h>
32193149Sdougb#include <isc/time.h>
33135446Strhodes
34135446Strhodes#include <dns/keyvalues.h>
35135446Strhodes#include <dns/log.h>
36135446Strhodes#include <dns/message.h>
37193149Sdougb#include <dns/fixedname.h>
38135446Strhodes#include <dns/rbt.h>
39135446Strhodes#include <dns/rdata.h>
40135446Strhodes#include <dns/rdatalist.h>
41135446Strhodes#include <dns/rdataset.h>
42135446Strhodes#include <dns/rdatastruct.h>
43135446Strhodes#include <dns/result.h>
44135446Strhodes#include <dns/tsig.h>
45135446Strhodes
46135446Strhodes#include <dst/result.h>
47135446Strhodes
48135446Strhodes#define TSIG_MAGIC		ISC_MAGIC('T', 'S', 'I', 'G')
49135446Strhodes#define VALID_TSIG_KEY(x)	ISC_MAGIC_VALID(x, TSIG_MAGIC)
50135446Strhodes
51218384Sdougb#ifndef DNS_TSIG_MAXGENERATEDKEYS
52218384Sdougb#define DNS_TSIG_MAXGENERATEDKEYS 4096
53218384Sdougb#endif
54218384Sdougb
55135446Strhodes#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
56135446Strhodes#define algname_is_allocated(algname) \
57135446Strhodes	((algname) != dns_tsig_hmacmd5_name && \
58170222Sdougb	 (algname) != dns_tsig_hmacsha1_name && \
59170222Sdougb	 (algname) != dns_tsig_hmacsha224_name && \
60170222Sdougb	 (algname) != dns_tsig_hmacsha256_name && \
61170222Sdougb	 (algname) != dns_tsig_hmacsha384_name && \
62170222Sdougb	 (algname) != dns_tsig_hmacsha512_name && \
63135446Strhodes	 (algname) != dns_tsig_gssapi_name && \
64135446Strhodes	 (algname) != dns_tsig_gssapims_name)
65135446Strhodes
66135446Strhodes#define BADTIMELEN 6
67135446Strhodes
68135446Strhodesstatic unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
69135446Strhodesstatic unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
70135446Strhodes
71135446Strhodesstatic dns_name_t hmacmd5 = {
72135446Strhodes	DNS_NAME_MAGIC,
73135446Strhodes	hmacmd5_ndata, 26, 5,
74135446Strhodes	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
75135446Strhodes	hmacmd5_offsets, NULL,
76135446Strhodes	{(void *)-1, (void *)-1},
77135446Strhodes	{NULL, NULL}
78135446Strhodes};
79135446Strhodes
80135446Strhodesdns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
81135446Strhodes
82135446Strhodesstatic unsigned char gsstsig_ndata[] = "\010gss-tsig";
83135446Strhodesstatic unsigned char gsstsig_offsets[] = { 0, 9 };
84135446Strhodesstatic dns_name_t gsstsig = {
85135446Strhodes	DNS_NAME_MAGIC,
86135446Strhodes	gsstsig_ndata, 10, 2,
87135446Strhodes	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
88135446Strhodes	gsstsig_offsets, NULL,
89135446Strhodes	{(void *)-1, (void *)-1},
90135446Strhodes	{NULL, NULL}
91135446Strhodes};
92135446StrhodesLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig;
93135446Strhodes
94193149Sdougb/*
95193149Sdougb * Since Microsoft doesn't follow its own standard, we will use this
96193149Sdougb * alternate name as a second guess.
97193149Sdougb */
98135446Strhodesstatic unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
99135446Strhodesstatic unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
100135446Strhodesstatic dns_name_t gsstsigms = {
101135446Strhodes	DNS_NAME_MAGIC,
102135446Strhodes	gsstsigms_ndata, 19, 4,
103135446Strhodes	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
104135446Strhodes	gsstsigms_offsets, NULL,
105135446Strhodes	{(void *)-1, (void *)-1},
106135446Strhodes	{NULL, NULL}
107135446Strhodes};
108135446StrhodesLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapims_name = &gsstsigms;
109135446Strhodes
110170222Sdougbstatic unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
111170222Sdougbstatic unsigned char hmacsha1_offsets[] = { 0, 10 };
112170222Sdougb
113170222Sdougbstatic dns_name_t  hmacsha1 = {
114186462Sdougb	DNS_NAME_MAGIC,
115186462Sdougb	hmacsha1_ndata, 11, 2,
116186462Sdougb	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
117186462Sdougb	hmacsha1_offsets, NULL,
118186462Sdougb	{(void *)-1, (void *)-1},
119186462Sdougb	{NULL, NULL}
120170222Sdougb};
121170222Sdougb
122170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;
123170222Sdougb
124170222Sdougbstatic unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
125170222Sdougbstatic unsigned char hmacsha224_offsets[] = { 0, 12 };
126170222Sdougb
127170222Sdougbstatic dns_name_t hmacsha224 = {
128186462Sdougb	DNS_NAME_MAGIC,
129186462Sdougb	hmacsha224_ndata, 13, 2,
130186462Sdougb	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
131186462Sdougb	hmacsha224_offsets, NULL,
132186462Sdougb	{(void *)-1, (void *)-1},
133186462Sdougb	{NULL, NULL}
134170222Sdougb};
135170222Sdougb
136170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;
137170222Sdougb
138170222Sdougbstatic unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
139170222Sdougbstatic unsigned char hmacsha256_offsets[] = { 0, 12 };
140170222Sdougb
141170222Sdougbstatic dns_name_t hmacsha256 = {
142186462Sdougb	DNS_NAME_MAGIC,
143186462Sdougb	hmacsha256_ndata, 13, 2,
144186462Sdougb	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
145186462Sdougb	hmacsha256_offsets, NULL,
146186462Sdougb	{(void *)-1, (void *)-1},
147186462Sdougb	{NULL, NULL}
148170222Sdougb};
149170222Sdougb
150170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;
151170222Sdougb
152170222Sdougbstatic unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
153170222Sdougbstatic unsigned char hmacsha384_offsets[] = { 0, 12 };
154170222Sdougb
155170222Sdougbstatic dns_name_t hmacsha384 = {
156186462Sdougb	DNS_NAME_MAGIC,
157186462Sdougb	hmacsha384_ndata, 13, 2,
158186462Sdougb	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
159186462Sdougb	hmacsha384_offsets, NULL,
160186462Sdougb	{(void *)-1, (void *)-1},
161186462Sdougb	{NULL, NULL}
162170222Sdougb};
163170222Sdougb
164170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;
165170222Sdougb
166170222Sdougbstatic unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
167170222Sdougbstatic unsigned char hmacsha512_offsets[] = { 0, 12 };
168170222Sdougb
169170222Sdougbstatic dns_name_t hmacsha512 = {
170186462Sdougb	DNS_NAME_MAGIC,
171186462Sdougb	hmacsha512_ndata, 13, 2,
172186462Sdougb	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
173186462Sdougb	hmacsha512_offsets, NULL,
174186462Sdougb	{(void *)-1, (void *)-1},
175186462Sdougb	{NULL, NULL}
176170222Sdougb};
177170222Sdougb
178170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;
179170222Sdougb
180135446Strhodesstatic isc_result_t
181135446Strhodestsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
182135446Strhodes
183135446Strhodesstatic void
184135446Strhodestsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
185135446Strhodes     ISC_FORMAT_PRINTF(3, 4);
186135446Strhodes
187135446Strhodesstatic void
188193149Sdougbcleanup_ring(dns_tsig_keyring_t *ring);
189193149Sdougbstatic void
190193149Sdougbtsigkey_free(dns_tsigkey_t *key);
191193149Sdougb
192193149Sdougbstatic void
193135446Strhodestsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
194135446Strhodes	va_list ap;
195135446Strhodes	char message[4096];
196135446Strhodes	char namestr[DNS_NAME_FORMATSIZE];
197193149Sdougb	char creatorstr[DNS_NAME_FORMATSIZE];
198135446Strhodes
199135446Strhodes	if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
200135446Strhodes		return;
201135446Strhodes	if (key != NULL)
202135446Strhodes		dns_name_format(&key->name, namestr, sizeof(namestr));
203135446Strhodes	else
204135446Strhodes		strcpy(namestr, "<null>");
205193149Sdougb
206224092Sdougb	if (key != NULL && key->generated && key->creator)
207193149Sdougb		dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
208224092Sdougb	else
209224092Sdougb		strcpy(creatorstr, "<null>");
210193149Sdougb
211135446Strhodes	va_start(ap, fmt);
212135446Strhodes	vsnprintf(message, sizeof(message), fmt, ap);
213135446Strhodes	va_end(ap);
214193149Sdougb	if (key != NULL && key->generated)
215193149Sdougb		isc_log_write(dns_lctx,
216193149Sdougb			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
217193149Sdougb			      level, "tsig key '%s' (%s): %s",
218193149Sdougb			      namestr, creatorstr, message);
219193149Sdougb	else
220193149Sdougb		isc_log_write(dns_lctx,
221193149Sdougb			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
222193149Sdougb			      level, "tsig key '%s': %s", namestr, message);
223135446Strhodes}
224135446Strhodes
225224092Sdougbstatic void
226224092Sdougbremove_fromring(dns_tsigkey_t *tkey) {
227224092Sdougb	if (tkey->generated) {
228224092Sdougb		ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
229224092Sdougb		tkey->ring->generated--;
230224092Sdougb	}
231224092Sdougb	(void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, ISC_FALSE);
232224092Sdougb}
233224092Sdougb
234224092Sdougbstatic void
235224092Sdougbadjust_lru(dns_tsigkey_t *tkey) {
236224092Sdougb	if (tkey->generated) {
237224092Sdougb		RWLOCK(&tkey->ring->lock, isc_rwlocktype_write);
238224092Sdougb		/*
239224092Sdougb		 * We may have been removed from the LRU list between
240224092Sdougb		 * removing the read lock and aquiring the write lock.
241224092Sdougb		 */
242254402Serwin		if (ISC_LINK_LINKED(tkey, link) &&
243254402Serwin		    tkey->ring->lru.tail != tkey)
244254402Serwin		{
245224092Sdougb			ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
246224092Sdougb			ISC_LIST_APPEND(tkey->ring->lru, tkey, link);
247224092Sdougb		}
248224092Sdougb		RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write);
249224092Sdougb	}
250224092Sdougb}
251224092Sdougb
252224092Sdougb/*
253224092Sdougb * A supplemental routine just to add a key to ring.  Note that reference
254224092Sdougb * counter should be counted separately because we may be adding the key
255224092Sdougb * as part of creation of the key, in which case the reference counter was
256224092Sdougb * already initialized.  Also note we don't need RWLOCK for the reference
257224092Sdougb * counter: it's protected by a separate lock.
258224092Sdougb */
259224092Sdougbstatic isc_result_t
260224092Sdougbkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
261224092Sdougb	    dns_tsigkey_t *tkey)
262224092Sdougb{
263224092Sdougb	isc_result_t result;
264224092Sdougb
265224092Sdougb	RWLOCK(&ring->lock, isc_rwlocktype_write);
266224092Sdougb	ring->writecount++;
267224092Sdougb
268224092Sdougb	/*
269224092Sdougb	 * Do on the fly cleaning.  Find some nodes we might not
270224092Sdougb	 * want around any more.
271224092Sdougb	 */
272224092Sdougb	if (ring->writecount > 10) {
273224092Sdougb		cleanup_ring(ring);
274224092Sdougb		ring->writecount = 0;
275224092Sdougb	}
276224092Sdougb
277224092Sdougb	result = dns_rbt_addname(ring->keys, name, tkey);
278224092Sdougb	if (tkey->generated) {
279224092Sdougb		/*
280224092Sdougb		 * Add the new key to the LRU list and remove the least
281224092Sdougb		 * recently used key if there are too many keys on the list.
282224092Sdougb		 */
283224092Sdougb		ISC_LIST_INITANDAPPEND(ring->lru, tkey, link);
284224092Sdougb		if (ring->generated++ > ring->maxgenerated)
285224092Sdougb			remove_fromring(ISC_LIST_HEAD(ring->lru));
286224092Sdougb	}
287224092Sdougb	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
288224092Sdougb
289224092Sdougb	return (result);
290224092Sdougb}
291224092Sdougb
292135446Strhodesisc_result_t
293135446Strhodesdns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
294135446Strhodes			  dst_key_t *dstkey, isc_boolean_t generated,
295135446Strhodes			  dns_name_t *creator, isc_stdtime_t inception,
296135446Strhodes			  isc_stdtime_t expire, isc_mem_t *mctx,
297135446Strhodes			  dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
298135446Strhodes{
299135446Strhodes	dns_tsigkey_t *tkey;
300135446Strhodes	isc_result_t ret;
301135446Strhodes	unsigned int refs = 0;
302135446Strhodes
303135446Strhodes	REQUIRE(key == NULL || *key == NULL);
304135446Strhodes	REQUIRE(name != NULL);
305135446Strhodes	REQUIRE(algorithm != NULL);
306135446Strhodes	REQUIRE(mctx != NULL);
307170222Sdougb	REQUIRE(key != NULL || ring != NULL);
308135446Strhodes
309135446Strhodes	tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t));
310135446Strhodes	if (tkey == NULL)
311135446Strhodes		return (ISC_R_NOMEMORY);
312135446Strhodes
313135446Strhodes	dns_name_init(&tkey->name, NULL);
314135446Strhodes	ret = dns_name_dup(name, mctx, &tkey->name);
315135446Strhodes	if (ret != ISC_R_SUCCESS)
316135446Strhodes		goto cleanup_key;
317135446Strhodes	(void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
318135446Strhodes
319135446Strhodes	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
320135446Strhodes		tkey->algorithm = DNS_TSIG_HMACMD5_NAME;
321135446Strhodes		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACMD5) {
322135446Strhodes			ret = DNS_R_BADALG;
323135446Strhodes			goto cleanup_name;
324135446Strhodes		}
325170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
326170222Sdougb		tkey->algorithm = DNS_TSIG_HMACSHA1_NAME;
327170222Sdougb		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) {
328170222Sdougb			ret = DNS_R_BADALG;
329170222Sdougb			goto cleanup_name;
330170222Sdougb		}
331170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
332170222Sdougb		tkey->algorithm = DNS_TSIG_HMACSHA224_NAME;
333170222Sdougb		if (dstkey != NULL &&
334170222Sdougb		    dst_key_alg(dstkey) != DST_ALG_HMACSHA224) {
335170222Sdougb			ret = DNS_R_BADALG;
336170222Sdougb			goto cleanup_name;
337170222Sdougb		}
338170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
339170222Sdougb		tkey->algorithm = DNS_TSIG_HMACSHA256_NAME;
340170222Sdougb		if (dstkey != NULL &&
341170222Sdougb		    dst_key_alg(dstkey) != DST_ALG_HMACSHA256) {
342170222Sdougb			ret = DNS_R_BADALG;
343170222Sdougb			goto cleanup_name;
344170222Sdougb		}
345170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
346170222Sdougb		tkey->algorithm = DNS_TSIG_HMACSHA384_NAME;
347170222Sdougb		if (dstkey != NULL &&
348170222Sdougb		    dst_key_alg(dstkey) != DST_ALG_HMACSHA384) {
349170222Sdougb			ret = DNS_R_BADALG;
350170222Sdougb			goto cleanup_name;
351170222Sdougb		}
352170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
353170222Sdougb		tkey->algorithm = DNS_TSIG_HMACSHA512_NAME;
354170222Sdougb		if (dstkey != NULL &&
355170222Sdougb		    dst_key_alg(dstkey) != DST_ALG_HMACSHA512) {
356170222Sdougb			ret = DNS_R_BADALG;
357170222Sdougb			goto cleanup_name;
358170222Sdougb		}
359135446Strhodes	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
360135446Strhodes		tkey->algorithm = DNS_TSIG_GSSAPI_NAME;
361135446Strhodes		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
362135446Strhodes			ret = DNS_R_BADALG;
363135446Strhodes			goto cleanup_name;
364135446Strhodes		}
365135446Strhodes	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
366135446Strhodes		tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME;
367135446Strhodes		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
368135446Strhodes			ret = DNS_R_BADALG;
369135446Strhodes			goto cleanup_name;
370135446Strhodes		}
371135446Strhodes	} else {
372153816Sdougb		if (dstkey != NULL) {
373135446Strhodes			ret = DNS_R_BADALG;
374135446Strhodes			goto cleanup_name;
375135446Strhodes		}
376135446Strhodes		tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t));
377135446Strhodes		if (tkey->algorithm == NULL) {
378135446Strhodes			ret = ISC_R_NOMEMORY;
379135446Strhodes			goto cleanup_name;
380135446Strhodes		}
381135446Strhodes		dns_name_init(tkey->algorithm, NULL);
382135446Strhodes		ret = dns_name_dup(algorithm, mctx, tkey->algorithm);
383135446Strhodes		if (ret != ISC_R_SUCCESS)
384135446Strhodes			goto cleanup_algorithm;
385135446Strhodes		(void)dns_name_downcase(tkey->algorithm, tkey->algorithm,
386135446Strhodes					NULL);
387135446Strhodes	}
388135446Strhodes
389135446Strhodes	if (creator != NULL) {
390135446Strhodes		tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
391135446Strhodes		if (tkey->creator == NULL) {
392135446Strhodes			ret = ISC_R_NOMEMORY;
393135446Strhodes			goto cleanup_algorithm;
394135446Strhodes		}
395135446Strhodes		dns_name_init(tkey->creator, NULL);
396135446Strhodes		ret = dns_name_dup(creator, mctx, tkey->creator);
397135446Strhodes		if (ret != ISC_R_SUCCESS) {
398135446Strhodes			isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
399135446Strhodes			goto cleanup_algorithm;
400135446Strhodes		}
401135446Strhodes	} else
402135446Strhodes		tkey->creator = NULL;
403135446Strhodes
404218384Sdougb	tkey->key = NULL;
405218384Sdougb	if (dstkey != NULL)
406218384Sdougb		dst_key_attach(dstkey, &tkey->key);
407135446Strhodes	tkey->ring = ring;
408135446Strhodes
409170222Sdougb	if (key != NULL)
410224092Sdougb		refs = 1;
411170222Sdougb	if (ring != NULL)
412170222Sdougb		refs++;
413170222Sdougb	ret = isc_refcount_init(&tkey->refs, refs);
414170222Sdougb	if (ret != ISC_R_SUCCESS)
415170222Sdougb		goto cleanup_creator;
416170222Sdougb
417170222Sdougb	tkey->generated = generated;
418170222Sdougb	tkey->inception = inception;
419170222Sdougb	tkey->expire = expire;
420186462Sdougb	tkey->mctx = NULL;
421186462Sdougb	isc_mem_attach(mctx, &tkey->mctx);
422170222Sdougb
423170222Sdougb	tkey->magic = TSIG_MAGIC;
424170222Sdougb
425135446Strhodes	if (ring != NULL) {
426224092Sdougb		ret = keyring_add(ring, name, tkey);
427224092Sdougb		if (ret != ISC_R_SUCCESS)
428170222Sdougb			goto cleanup_refs;
429135446Strhodes	}
430135446Strhodes
431193149Sdougb	/*
432193149Sdougb	 * Ignore this if it's a GSS key, since the key size is meaningless.
433193149Sdougb	 */
434193149Sdougb	if (dstkey != NULL && dst_key_size(dstkey) < 64 &&
435193149Sdougb	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME) &&
436193149Sdougb	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
437135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
438135446Strhodes		dns_name_format(name, namestr, sizeof(namestr));
439135446Strhodes		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
440135446Strhodes			      DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
441135446Strhodes			      "the key '%s' is too short to be secure",
442135446Strhodes			      namestr);
443135446Strhodes	}
444224092Sdougb
445135446Strhodes	if (key != NULL)
446135446Strhodes		*key = tkey;
447135446Strhodes
448135446Strhodes	return (ISC_R_SUCCESS);
449135446Strhodes
450170222Sdougb cleanup_refs:
451170222Sdougb	tkey->magic = 0;
452170222Sdougb	while (refs-- > 0)
453170222Sdougb		isc_refcount_decrement(&tkey->refs, NULL);
454170222Sdougb	isc_refcount_destroy(&tkey->refs);
455170222Sdougb cleanup_creator:
456218384Sdougb	if (tkey->key != NULL)
457218384Sdougb		dst_key_free(&tkey->key);
458170222Sdougb	if (tkey->creator != NULL) {
459170222Sdougb		dns_name_free(tkey->creator, mctx);
460170222Sdougb		isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
461170222Sdougb	}
462135446Strhodes cleanup_algorithm:
463135446Strhodes	if (algname_is_allocated(tkey->algorithm)) {
464135446Strhodes		if (dns_name_dynamic(tkey->algorithm))
465135446Strhodes			dns_name_free(tkey->algorithm, mctx);
466135446Strhodes		isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t));
467135446Strhodes	}
468135446Strhodes cleanup_name:
469135446Strhodes	dns_name_free(&tkey->name, mctx);
470135446Strhodes cleanup_key:
471135446Strhodes	isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
472135446Strhodes
473135446Strhodes	return (ret);
474135446Strhodes}
475135446Strhodes
476193149Sdougb/*
477193149Sdougb * Find a few nodes to destroy if possible.
478193149Sdougb */
479193149Sdougbstatic void
480193149Sdougbcleanup_ring(dns_tsig_keyring_t *ring)
481193149Sdougb{
482193149Sdougb	isc_result_t result;
483193149Sdougb	dns_rbtnodechain_t chain;
484193149Sdougb	dns_name_t foundname;
485193149Sdougb	dns_fixedname_t fixedorigin;
486193149Sdougb	dns_name_t *origin;
487193149Sdougb	isc_stdtime_t now;
488193149Sdougb	dns_rbtnode_t *node;
489193149Sdougb	dns_tsigkey_t *tkey;
490193149Sdougb
491193149Sdougb	/*
492193149Sdougb	 * Start up a new iterator each time.
493193149Sdougb	 */
494193149Sdougb	isc_stdtime_get(&now);
495193149Sdougb	dns_name_init(&foundname, NULL);
496193149Sdougb	dns_fixedname_init(&fixedorigin);
497193149Sdougb	origin = dns_fixedname_name(&fixedorigin);
498193149Sdougb
499193149Sdougb again:
500193149Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
501193149Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
502193149Sdougb					origin);
503193149Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
504193149Sdougb		dns_rbtnodechain_invalidate(&chain);
505193149Sdougb		return;
506193149Sdougb	}
507193149Sdougb
508193149Sdougb	for (;;) {
509193149Sdougb		node = NULL;
510193149Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
511193149Sdougb		tkey = node->data;
512193149Sdougb		if (tkey != NULL) {
513193149Sdougb			if (tkey->generated
514193149Sdougb			    && isc_refcount_current(&tkey->refs) == 1
515193149Sdougb			    && tkey->inception != tkey->expire
516193149Sdougb			    && tkey->expire < now) {
517193149Sdougb				tsig_log(tkey, 2, "tsig expire: deleting");
518193149Sdougb				/* delete the key */
519193149Sdougb				dns_rbtnodechain_invalidate(&chain);
520218384Sdougb				remove_fromring(tkey);
521193149Sdougb				goto again;
522193149Sdougb			}
523193149Sdougb		}
524193149Sdougb		result = dns_rbtnodechain_next(&chain, &foundname,
525193149Sdougb					       origin);
526193149Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
527193149Sdougb			dns_rbtnodechain_invalidate(&chain);
528193149Sdougb			return;
529193149Sdougb		}
530193149Sdougb	}
531193149Sdougb}
532193149Sdougb
533224092Sdougbstatic void
534224092Sdougbdestroyring(dns_tsig_keyring_t *ring) {
535224092Sdougb	dns_rbt_destroy(&ring->keys);
536224092Sdougb	isc_rwlock_destroy(&ring->lock);
537224092Sdougb	isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t));
538224092Sdougb}
539224092Sdougb
540224092Sdougbstatic unsigned int
541224092Sdougbdst_alg_fromname(dns_name_t *algorithm) {
542224092Sdougb	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
543224092Sdougb		return (DST_ALG_HMACMD5);
544224092Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
545224092Sdougb		return (DST_ALG_HMACSHA1);
546224092Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
547224092Sdougb		return (DST_ALG_HMACSHA224);
548224092Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
549224092Sdougb		return (DST_ALG_HMACSHA256);
550224092Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
551224092Sdougb		return (DST_ALG_HMACSHA384);
552224092Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
553224092Sdougb		return (DST_ALG_HMACSHA512);
554224092Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
555224092Sdougb		return (DST_ALG_GSSAPI);
556224092Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
557224092Sdougb		return (DST_ALG_GSSAPI);
558224092Sdougb	} else
559224092Sdougb		return (0);
560224092Sdougb}
561224092Sdougb
562224092Sdougbstatic isc_result_t
563224092Sdougbrestore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) {
564224092Sdougb	dst_key_t *dstkey = NULL;
565224092Sdougb	char namestr[1024];
566224092Sdougb	char creatorstr[1024];
567224092Sdougb	char algorithmstr[1024];
568224092Sdougb	char keystr[4096];
569224092Sdougb	unsigned int inception, expire;
570224092Sdougb	int n;
571224092Sdougb	isc_buffer_t b;
572224092Sdougb	dns_name_t *name, *creator, *algorithm;
573224092Sdougb	dns_fixedname_t fname, fcreator, falgorithm;
574224092Sdougb	isc_result_t result;
575224092Sdougb	unsigned int dstalg;
576224092Sdougb
577224092Sdougb	n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr,
578224092Sdougb		   creatorstr, &inception, &expire, algorithmstr, keystr);
579224092Sdougb	if (n == EOF)
580224092Sdougb		return (ISC_R_NOMORE);
581224092Sdougb	if (n != 6)
582224092Sdougb		return (ISC_R_FAILURE);
583224092Sdougb
584224092Sdougb	if (isc_serial_lt(expire, now))
585224092Sdougb		return (DNS_R_EXPIRED);
586224092Sdougb
587224092Sdougb	dns_fixedname_init(&fname);
588224092Sdougb	name = dns_fixedname_name(&fname);
589224092Sdougb	isc_buffer_init(&b, namestr, strlen(namestr));
590224092Sdougb	isc_buffer_add(&b, strlen(namestr));
591224092Sdougb	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
592224092Sdougb	if (result != ISC_R_SUCCESS)
593224092Sdougb		return (result);
594224092Sdougb
595224092Sdougb	dns_fixedname_init(&fcreator);
596224092Sdougb	creator = dns_fixedname_name(&fcreator);
597224092Sdougb	isc_buffer_init(&b, creatorstr, strlen(creatorstr));
598224092Sdougb	isc_buffer_add(&b, strlen(creatorstr));
599224092Sdougb	result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL);
600224092Sdougb	if (result != ISC_R_SUCCESS)
601224092Sdougb		return (result);
602224092Sdougb
603224092Sdougb	dns_fixedname_init(&falgorithm);
604224092Sdougb	algorithm = dns_fixedname_name(&falgorithm);
605224092Sdougb	isc_buffer_init(&b, algorithmstr, strlen(algorithmstr));
606224092Sdougb	isc_buffer_add(&b, strlen(algorithmstr));
607224092Sdougb	result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL);
608224092Sdougb	if (result != ISC_R_SUCCESS)
609224092Sdougb		return (result);
610224092Sdougb
611224092Sdougb	dstalg = dst_alg_fromname(algorithm);
612224092Sdougb	if (dstalg == 0)
613224092Sdougb		return (DNS_R_BADALG);
614224092Sdougb
615224092Sdougb	result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY,
616224092Sdougb				 DNS_KEYPROTO_DNSSEC, dns_rdataclass_in,
617224092Sdougb				 ring->mctx, keystr, &dstkey);
618224092Sdougb	if (result != ISC_R_SUCCESS)
619224092Sdougb		return (result);
620224092Sdougb
621224092Sdougb	result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
622224092Sdougb					   ISC_TRUE, creator, inception,
623224092Sdougb					   expire, ring->mctx, ring, NULL);
624225361Sdougb	if (dstkey != NULL)
625224092Sdougb		dst_key_free(&dstkey);
626224092Sdougb	return (result);
627224092Sdougb}
628224092Sdougb
629224092Sdougbstatic void
630254402Serwindump_key(dns_tsigkey_t *tkey, FILE *fp) {
631224092Sdougb	char *buffer = NULL;
632224092Sdougb	int length = 0;
633224092Sdougb	char namestr[DNS_NAME_FORMATSIZE];
634224092Sdougb	char creatorstr[DNS_NAME_FORMATSIZE];
635224092Sdougb	char algorithmstr[DNS_NAME_FORMATSIZE];
636224092Sdougb	isc_result_t result;
637224092Sdougb
638254402Serwin	REQUIRE(tkey != NULL);
639254402Serwin	REQUIRE(fp != NULL);
640254402Serwin
641224092Sdougb	dns_name_format(&tkey->name, namestr, sizeof(namestr));
642224092Sdougb	dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr));
643224092Sdougb	dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr));
644224092Sdougb	result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length);
645224092Sdougb	if (result == ISC_R_SUCCESS)
646224092Sdougb		fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr,
647224092Sdougb			tkey->inception, tkey->expire, algorithmstr,
648224092Sdougb			length, buffer);
649224092Sdougb	if (buffer != NULL)
650224092Sdougb		isc_mem_put(tkey->mctx, buffer, length);
651224092Sdougb}
652224092Sdougb
653135446Strhodesisc_result_t
654224092Sdougbdns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) {
655224092Sdougb	isc_result_t result;
656224092Sdougb	dns_rbtnodechain_t chain;
657224092Sdougb	dns_name_t foundname;
658224092Sdougb	dns_fixedname_t fixedorigin;
659224092Sdougb	dns_name_t *origin;
660224092Sdougb	isc_stdtime_t now;
661224092Sdougb	dns_rbtnode_t *node;
662224092Sdougb	dns_tsigkey_t *tkey;
663224092Sdougb	dns_tsig_keyring_t *ring;
664224092Sdougb	unsigned int references;
665224092Sdougb
666224092Sdougb	REQUIRE(ringp != NULL && *ringp != NULL);
667224092Sdougb
668224092Sdougb	ring = *ringp;
669224092Sdougb	*ringp = NULL;
670224092Sdougb
671224092Sdougb	RWLOCK(&ring->lock, isc_rwlocktype_write);
672224092Sdougb	INSIST(ring->references > 0);
673224092Sdougb	ring->references--;
674224092Sdougb	references = ring->references;
675224092Sdougb	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
676224092Sdougb
677224092Sdougb	if (references != 0)
678224092Sdougb		return (DNS_R_CONTINUE);
679224092Sdougb
680224092Sdougb	isc_stdtime_get(&now);
681224092Sdougb	dns_name_init(&foundname, NULL);
682224092Sdougb	dns_fixedname_init(&fixedorigin);
683224092Sdougb	origin = dns_fixedname_name(&fixedorigin);
684224092Sdougb	dns_rbtnodechain_init(&chain, ring->mctx);
685224092Sdougb	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
686224092Sdougb					origin);
687224092Sdougb	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
688224092Sdougb		dns_rbtnodechain_invalidate(&chain);
689224092Sdougb		goto destroy;
690224092Sdougb	}
691224092Sdougb
692224092Sdougb	for (;;) {
693224092Sdougb		node = NULL;
694224092Sdougb		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
695224092Sdougb		tkey = node->data;
696224092Sdougb		if (tkey != NULL && tkey->generated && tkey->expire >= now)
697224092Sdougb			dump_key(tkey, fp);
698224092Sdougb		result = dns_rbtnodechain_next(&chain, &foundname,
699224092Sdougb					       origin);
700224092Sdougb		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
701224092Sdougb			dns_rbtnodechain_invalidate(&chain);
702224092Sdougb			if (result == ISC_R_NOMORE)
703224092Sdougb				result = ISC_R_SUCCESS;
704224092Sdougb			goto destroy;
705224092Sdougb		}
706224092Sdougb	}
707224092Sdougb
708224092Sdougb destroy:
709224092Sdougb	destroyring(ring);
710224092Sdougb	return (result);
711224092Sdougb}
712224092Sdougb
713224092Sdougbisc_result_t
714135446Strhodesdns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
715135446Strhodes		   unsigned char *secret, int length, isc_boolean_t generated,
716135446Strhodes		   dns_name_t *creator, isc_stdtime_t inception,
717135446Strhodes		   isc_stdtime_t expire, isc_mem_t *mctx,
718135446Strhodes		   dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
719135446Strhodes{
720135446Strhodes	dst_key_t *dstkey = NULL;
721135446Strhodes	isc_result_t result;
722135446Strhodes
723135446Strhodes	REQUIRE(length >= 0);
724135446Strhodes	if (length > 0)
725135446Strhodes		REQUIRE(secret != NULL);
726135446Strhodes
727170222Sdougb	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
728170222Sdougb		if (secret != NULL) {
729170222Sdougb			isc_buffer_t b;
730170222Sdougb
731170222Sdougb			isc_buffer_init(&b, secret, length);
732170222Sdougb			isc_buffer_add(&b, length);
733170222Sdougb			result = dst_key_frombuffer(name, DST_ALG_HMACMD5,
734170222Sdougb						    DNS_KEYOWNER_ENTITY,
735170222Sdougb						    DNS_KEYPROTO_DNSSEC,
736170222Sdougb						    dns_rdataclass_in,
737170222Sdougb						    &b, mctx, &dstkey);
738170222Sdougb				if (result != ISC_R_SUCCESS)
739170222Sdougb					return (result);
740170222Sdougb		}
741170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
742170222Sdougb		if (secret != NULL) {
743170222Sdougb			isc_buffer_t b;
744170222Sdougb
745170222Sdougb			isc_buffer_init(&b, secret, length);
746170222Sdougb			isc_buffer_add(&b, length);
747170222Sdougb			result = dst_key_frombuffer(name, DST_ALG_HMACSHA1,
748170222Sdougb						    DNS_KEYOWNER_ENTITY,
749170222Sdougb						    DNS_KEYPROTO_DNSSEC,
750170222Sdougb						    dns_rdataclass_in,
751170222Sdougb						    &b, mctx, &dstkey);
752170222Sdougb				if (result != ISC_R_SUCCESS)
753170222Sdougb					return (result);
754170222Sdougb		}
755170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
756170222Sdougb		if (secret != NULL) {
757170222Sdougb			isc_buffer_t b;
758170222Sdougb
759170222Sdougb			isc_buffer_init(&b, secret, length);
760170222Sdougb			isc_buffer_add(&b, length);
761170222Sdougb			result = dst_key_frombuffer(name, DST_ALG_HMACSHA224,
762170222Sdougb						    DNS_KEYOWNER_ENTITY,
763170222Sdougb						    DNS_KEYPROTO_DNSSEC,
764170222Sdougb						    dns_rdataclass_in,
765170222Sdougb						    &b, mctx, &dstkey);
766170222Sdougb				if (result != ISC_R_SUCCESS)
767170222Sdougb					return (result);
768170222Sdougb		}
769170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
770170222Sdougb		if (secret != NULL) {
771170222Sdougb			isc_buffer_t b;
772170222Sdougb
773170222Sdougb			isc_buffer_init(&b, secret, length);
774170222Sdougb			isc_buffer_add(&b, length);
775170222Sdougb			result = dst_key_frombuffer(name, DST_ALG_HMACSHA256,
776170222Sdougb						    DNS_KEYOWNER_ENTITY,
777170222Sdougb						    DNS_KEYPROTO_DNSSEC,
778170222Sdougb						    dns_rdataclass_in,
779170222Sdougb						    &b, mctx, &dstkey);
780170222Sdougb				if (result != ISC_R_SUCCESS)
781170222Sdougb					return (result);
782170222Sdougb		}
783170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
784170222Sdougb		if (secret != NULL) {
785170222Sdougb			isc_buffer_t b;
786170222Sdougb
787170222Sdougb			isc_buffer_init(&b, secret, length);
788170222Sdougb			isc_buffer_add(&b, length);
789170222Sdougb			result = dst_key_frombuffer(name, DST_ALG_HMACSHA384,
790170222Sdougb						    DNS_KEYOWNER_ENTITY,
791170222Sdougb						    DNS_KEYPROTO_DNSSEC,
792170222Sdougb						    dns_rdataclass_in,
793170222Sdougb						    &b, mctx, &dstkey);
794170222Sdougb				if (result != ISC_R_SUCCESS)
795170222Sdougb					return (result);
796170222Sdougb		}
797170222Sdougb	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
798170222Sdougb		if (secret != NULL) {
799170222Sdougb			isc_buffer_t b;
800170222Sdougb
801170222Sdougb			isc_buffer_init(&b, secret, length);
802170222Sdougb			isc_buffer_add(&b, length);
803170222Sdougb			result = dst_key_frombuffer(name, DST_ALG_HMACSHA512,
804170222Sdougb						    DNS_KEYOWNER_ENTITY,
805170222Sdougb						    DNS_KEYPROTO_DNSSEC,
806170222Sdougb						    dns_rdataclass_in,
807170222Sdougb						    &b, mctx, &dstkey);
808170222Sdougb				if (result != ISC_R_SUCCESS)
809170222Sdougb					return (result);
810170222Sdougb		}
811170222Sdougb	} else if (length > 0)
812135446Strhodes		return (DNS_R_BADALG);
813135446Strhodes
814135446Strhodes	result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
815135446Strhodes					   generated, creator,
816135446Strhodes					   inception, expire, mctx, ring, key);
817218384Sdougb	if (dstkey != NULL)
818135446Strhodes		dst_key_free(&dstkey);
819135446Strhodes	return (result);
820135446Strhodes}
821135446Strhodes
822135446Strhodesvoid
823135446Strhodesdns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
824135446Strhodes	REQUIRE(VALID_TSIG_KEY(source));
825135446Strhodes	REQUIRE(targetp != NULL && *targetp == NULL);
826135446Strhodes
827135446Strhodes	isc_refcount_increment(&source->refs, NULL);
828135446Strhodes	*targetp = source;
829135446Strhodes}
830135446Strhodes
831135446Strhodesstatic void
832135446Strhodestsigkey_free(dns_tsigkey_t *key) {
833135446Strhodes	REQUIRE(VALID_TSIG_KEY(key));
834135446Strhodes
835135446Strhodes	key->magic = 0;
836135446Strhodes	dns_name_free(&key->name, key->mctx);
837135446Strhodes	if (algname_is_allocated(key->algorithm)) {
838135446Strhodes		dns_name_free(key->algorithm, key->mctx);
839135446Strhodes		isc_mem_put(key->mctx, key->algorithm, sizeof(dns_name_t));
840135446Strhodes	}
841135446Strhodes	if (key->key != NULL)
842135446Strhodes		dst_key_free(&key->key);
843135446Strhodes	if (key->creator != NULL) {
844135446Strhodes		dns_name_free(key->creator, key->mctx);
845135446Strhodes		isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
846135446Strhodes	}
847135446Strhodes	isc_refcount_destroy(&key->refs);
848186462Sdougb	isc_mem_putanddetach(&key->mctx, key, sizeof(dns_tsigkey_t));
849135446Strhodes}
850135446Strhodes
851135446Strhodesvoid
852135446Strhodesdns_tsigkey_detach(dns_tsigkey_t **keyp) {
853135446Strhodes	dns_tsigkey_t *key;
854135446Strhodes	unsigned int refs;
855135446Strhodes
856135446Strhodes	REQUIRE(keyp != NULL);
857135446Strhodes	REQUIRE(VALID_TSIG_KEY(*keyp));
858135446Strhodes
859135446Strhodes	key = *keyp;
860135446Strhodes	isc_refcount_decrement(&key->refs, &refs);
861135446Strhodes
862135446Strhodes	if (refs == 0)
863135446Strhodes		tsigkey_free(key);
864135446Strhodes
865135446Strhodes	*keyp = NULL;
866135446Strhodes}
867135446Strhodes
868135446Strhodesvoid
869135446Strhodesdns_tsigkey_setdeleted(dns_tsigkey_t *key) {
870135446Strhodes	REQUIRE(VALID_TSIG_KEY(key));
871135446Strhodes	REQUIRE(key->ring != NULL);
872135446Strhodes
873135446Strhodes	RWLOCK(&key->ring->lock, isc_rwlocktype_write);
874218384Sdougb	remove_fromring(key);
875135446Strhodes	RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
876135446Strhodes}
877135446Strhodes
878135446Strhodesisc_result_t
879135446Strhodesdns_tsig_sign(dns_message_t *msg) {
880135446Strhodes	dns_tsigkey_t *key;
881135446Strhodes	dns_rdata_any_tsig_t tsig, querytsig;
882135446Strhodes	unsigned char data[128];
883135446Strhodes	isc_buffer_t databuf, sigbuf;
884135446Strhodes	isc_buffer_t *dynbuf;
885135446Strhodes	dns_name_t *owner;
886165071Sdougb	dns_rdata_t *rdata = NULL;
887135446Strhodes	dns_rdatalist_t *datalist;
888135446Strhodes	dns_rdataset_t *dataset;
889135446Strhodes	isc_region_t r;
890135446Strhodes	isc_stdtime_t now;
891135446Strhodes	isc_mem_t *mctx;
892135446Strhodes	dst_context_t *ctx = NULL;
893135446Strhodes	isc_result_t ret;
894135446Strhodes	unsigned char badtimedata[BADTIMELEN];
895135446Strhodes	unsigned int sigsize = 0;
896234010Sdougb	isc_boolean_t response = is_response(msg);
897135446Strhodes
898135446Strhodes	REQUIRE(msg != NULL);
899135446Strhodes	REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg)));
900135446Strhodes
901135446Strhodes	/*
902135446Strhodes	 * If this is a response, there should be a query tsig.
903135446Strhodes	 */
904234010Sdougb	if (response && msg->querytsig == NULL)
905135446Strhodes		return (DNS_R_EXPECTEDTSIG);
906135446Strhodes
907135446Strhodes	dynbuf = NULL;
908135446Strhodes
909135446Strhodes	mctx = msg->mctx;
910135446Strhodes	key = dns_message_gettsigkey(msg);
911135446Strhodes
912135446Strhodes	tsig.mctx = mctx;
913135446Strhodes	tsig.common.rdclass = dns_rdataclass_any;
914135446Strhodes	tsig.common.rdtype = dns_rdatatype_tsig;
915135446Strhodes	ISC_LINK_INIT(&tsig.common, link);
916135446Strhodes	dns_name_init(&tsig.algorithm, NULL);
917135446Strhodes	dns_name_clone(key->algorithm, &tsig.algorithm);
918135446Strhodes
919135446Strhodes	isc_stdtime_get(&now);
920135446Strhodes	tsig.timesigned = now + msg->timeadjust;
921135446Strhodes	tsig.fudge = DNS_TSIG_FUDGE;
922135446Strhodes
923135446Strhodes	tsig.originalid = msg->id;
924135446Strhodes
925135446Strhodes	isc_buffer_init(&databuf, data, sizeof(data));
926135446Strhodes
927234010Sdougb	if (response)
928135446Strhodes		tsig.error = msg->querytsigstatus;
929135446Strhodes	else
930135446Strhodes		tsig.error = dns_rcode_noerror;
931135446Strhodes
932135446Strhodes	if (tsig.error != dns_tsigerror_badtime) {
933135446Strhodes		tsig.otherlen = 0;
934135446Strhodes		tsig.other = NULL;
935135446Strhodes	} else {
936135446Strhodes		isc_buffer_t otherbuf;
937135446Strhodes
938135446Strhodes		tsig.otherlen = BADTIMELEN;
939135446Strhodes		tsig.other = badtimedata;
940135446Strhodes		isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
941193149Sdougb		isc_buffer_putuint48(&otherbuf, tsig.timesigned);
942135446Strhodes	}
943135446Strhodes
944135446Strhodes	if (key->key != NULL && tsig.error != dns_tsigerror_badsig) {
945135446Strhodes		unsigned char header[DNS_MESSAGE_HEADERLEN];
946135446Strhodes		isc_buffer_t headerbuf;
947170222Sdougb		isc_uint16_t digestbits;
948135446Strhodes
949254402Serwin		ret = dst_context_create2(key->key, mctx,
950254402Serwin					  DNS_LOGCATEGORY_DNSSEC, &ctx);
951135446Strhodes		if (ret != ISC_R_SUCCESS)
952135446Strhodes			return (ret);
953135446Strhodes
954135446Strhodes		/*
955135446Strhodes		 * If this is a response, digest the query signature.
956135446Strhodes		 */
957234010Sdougb		if (response) {
958135446Strhodes			dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
959135446Strhodes
960135446Strhodes			ret = dns_rdataset_first(msg->querytsig);
961135446Strhodes			if (ret != ISC_R_SUCCESS)
962135446Strhodes				goto cleanup_context;
963135446Strhodes			dns_rdataset_current(msg->querytsig, &querytsigrdata);
964135446Strhodes			ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
965135446Strhodes						 NULL);
966135446Strhodes			if (ret != ISC_R_SUCCESS)
967135446Strhodes				goto cleanup_context;
968135446Strhodes			isc_buffer_putuint16(&databuf, querytsig.siglen);
969135446Strhodes			if (isc_buffer_availablelength(&databuf) <
970193149Sdougb			    querytsig.siglen) {
971135446Strhodes				ret = ISC_R_NOSPACE;
972135446Strhodes				goto cleanup_context;
973135446Strhodes			}
974135446Strhodes			isc_buffer_putmem(&databuf, querytsig.signature,
975135446Strhodes					  querytsig.siglen);
976135446Strhodes			isc_buffer_usedregion(&databuf, &r);
977135446Strhodes			ret = dst_context_adddata(ctx, &r);
978135446Strhodes			if (ret != ISC_R_SUCCESS)
979135446Strhodes				goto cleanup_context;
980135446Strhodes		}
981254402Serwin#if defined(__clang__)  && \
982254402Serwin       ( __clang_major__ < 3 || \
983254402Serwin	(__clang_major__ == 3 && __clang_minor__ < 2) || \
984254402Serwin	(__clang_major__ == 4 && __clang_minor__ < 2))
985254402Serwin	/* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */
986254402Serwin		else memset(&querytsig, 0, sizeof(querytsig));
987254402Serwin#endif
988135446Strhodes
989135446Strhodes		/*
990135446Strhodes		 * Digest the header.
991135446Strhodes		 */
992135446Strhodes		isc_buffer_init(&headerbuf, header, sizeof(header));
993135446Strhodes		dns_message_renderheader(msg, &headerbuf);
994135446Strhodes		isc_buffer_usedregion(&headerbuf, &r);
995135446Strhodes		ret = dst_context_adddata(ctx, &r);
996135446Strhodes		if (ret != ISC_R_SUCCESS)
997135446Strhodes			goto cleanup_context;
998135446Strhodes
999135446Strhodes		/*
1000135446Strhodes		 * Digest the remainder of the message.
1001135446Strhodes		 */
1002135446Strhodes		isc_buffer_usedregion(msg->buffer, &r);
1003135446Strhodes		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1004135446Strhodes		ret = dst_context_adddata(ctx, &r);
1005135446Strhodes		if (ret != ISC_R_SUCCESS)
1006135446Strhodes			goto cleanup_context;
1007135446Strhodes
1008135446Strhodes		if (msg->tcp_continuation == 0) {
1009135446Strhodes			/*
1010135446Strhodes			 * Digest the name, class, ttl, alg.
1011135446Strhodes			 */
1012135446Strhodes			dns_name_toregion(&key->name, &r);
1013135446Strhodes			ret = dst_context_adddata(ctx, &r);
1014135446Strhodes			if (ret != ISC_R_SUCCESS)
1015135446Strhodes				goto cleanup_context;
1016135446Strhodes
1017135446Strhodes			isc_buffer_clear(&databuf);
1018135446Strhodes			isc_buffer_putuint16(&databuf, dns_rdataclass_any);
1019135446Strhodes			isc_buffer_putuint32(&databuf, 0); /* ttl */
1020135446Strhodes			isc_buffer_usedregion(&databuf, &r);
1021135446Strhodes			ret = dst_context_adddata(ctx, &r);
1022135446Strhodes			if (ret != ISC_R_SUCCESS)
1023135446Strhodes				goto cleanup_context;
1024135446Strhodes
1025135446Strhodes			dns_name_toregion(&tsig.algorithm, &r);
1026135446Strhodes			ret = dst_context_adddata(ctx, &r);
1027135446Strhodes			if (ret != ISC_R_SUCCESS)
1028135446Strhodes				goto cleanup_context;
1029135446Strhodes
1030135446Strhodes		}
1031135446Strhodes		/* Digest the timesigned and fudge */
1032135446Strhodes		isc_buffer_clear(&databuf);
1033234010Sdougb		if (tsig.error == dns_tsigerror_badtime) {
1034234010Sdougb			INSIST(response);
1035135446Strhodes			tsig.timesigned = querytsig.timesigned;
1036234010Sdougb		}
1037193149Sdougb		isc_buffer_putuint48(&databuf, tsig.timesigned);
1038135446Strhodes		isc_buffer_putuint16(&databuf, tsig.fudge);
1039135446Strhodes		isc_buffer_usedregion(&databuf, &r);
1040135446Strhodes		ret = dst_context_adddata(ctx, &r);
1041135446Strhodes		if (ret != ISC_R_SUCCESS)
1042135446Strhodes			goto cleanup_context;
1043135446Strhodes
1044135446Strhodes		if (msg->tcp_continuation == 0) {
1045135446Strhodes			/*
1046135446Strhodes			 * Digest the error and other data length.
1047135446Strhodes			 */
1048135446Strhodes			isc_buffer_clear(&databuf);
1049135446Strhodes			isc_buffer_putuint16(&databuf, tsig.error);
1050135446Strhodes			isc_buffer_putuint16(&databuf, tsig.otherlen);
1051135446Strhodes
1052135446Strhodes			isc_buffer_usedregion(&databuf, &r);
1053135446Strhodes			ret = dst_context_adddata(ctx, &r);
1054135446Strhodes			if (ret != ISC_R_SUCCESS)
1055135446Strhodes				goto cleanup_context;
1056135446Strhodes
1057135446Strhodes			/*
1058234010Sdougb			 * Digest other data.
1059135446Strhodes			 */
1060135446Strhodes			if (tsig.otherlen > 0) {
1061135446Strhodes				r.length = tsig.otherlen;
1062135446Strhodes				r.base = tsig.other;
1063135446Strhodes				ret = dst_context_adddata(ctx, &r);
1064135446Strhodes				if (ret != ISC_R_SUCCESS)
1065135446Strhodes					goto cleanup_context;
1066135446Strhodes			}
1067135446Strhodes		}
1068135446Strhodes
1069135446Strhodes		ret = dst_key_sigsize(key->key, &sigsize);
1070135446Strhodes		if (ret != ISC_R_SUCCESS)
1071135446Strhodes			goto cleanup_context;
1072135446Strhodes		tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
1073135446Strhodes		if (tsig.signature == NULL) {
1074135446Strhodes			ret = ISC_R_NOMEMORY;
1075135446Strhodes			goto cleanup_context;
1076135446Strhodes		}
1077135446Strhodes
1078135446Strhodes		isc_buffer_init(&sigbuf, tsig.signature, sigsize);
1079135446Strhodes		ret = dst_context_sign(ctx, &sigbuf);
1080135446Strhodes		if (ret != ISC_R_SUCCESS)
1081135446Strhodes			goto cleanup_signature;
1082135446Strhodes		dst_context_destroy(&ctx);
1083170222Sdougb		digestbits = dst_key_getbits(key->key);
1084170222Sdougb		if (digestbits != 0) {
1085170222Sdougb			unsigned int bytes = (digestbits + 1) / 8;
1086234010Sdougb			if (response && bytes < querytsig.siglen)
1087170222Sdougb				bytes = querytsig.siglen;
1088170222Sdougb			if (bytes > isc_buffer_usedlength(&sigbuf))
1089170222Sdougb				bytes = isc_buffer_usedlength(&sigbuf);
1090170222Sdougb			tsig.siglen = bytes;
1091170222Sdougb		} else
1092170222Sdougb			tsig.siglen = isc_buffer_usedlength(&sigbuf);
1093135446Strhodes	} else {
1094135446Strhodes		tsig.siglen = 0;
1095135446Strhodes		tsig.signature = NULL;
1096135446Strhodes	}
1097135446Strhodes
1098135446Strhodes	ret = dns_message_gettemprdata(msg, &rdata);
1099135446Strhodes	if (ret != ISC_R_SUCCESS)
1100135446Strhodes		goto cleanup_signature;
1101135446Strhodes	ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
1102135446Strhodes	if (ret != ISC_R_SUCCESS)
1103165071Sdougb		goto cleanup_rdata;
1104135446Strhodes	ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
1105135446Strhodes				   dns_rdatatype_tsig, &tsig, dynbuf);
1106135446Strhodes	if (ret != ISC_R_SUCCESS)
1107135446Strhodes		goto cleanup_dynbuf;
1108135446Strhodes
1109135446Strhodes	dns_message_takebuffer(msg, &dynbuf);
1110135446Strhodes
1111135446Strhodes	if (tsig.signature != NULL) {
1112135446Strhodes		isc_mem_put(mctx, tsig.signature, sigsize);
1113135446Strhodes		tsig.signature = NULL;
1114135446Strhodes	}
1115135446Strhodes
1116135446Strhodes	owner = NULL;
1117135446Strhodes	ret = dns_message_gettempname(msg, &owner);
1118135446Strhodes	if (ret != ISC_R_SUCCESS)
1119165071Sdougb		goto cleanup_rdata;
1120135446Strhodes	dns_name_init(owner, NULL);
1121135446Strhodes	ret = dns_name_dup(&key->name, msg->mctx, owner);
1122135446Strhodes	if (ret != ISC_R_SUCCESS)
1123135446Strhodes		goto cleanup_owner;
1124135446Strhodes
1125135446Strhodes	datalist = NULL;
1126135446Strhodes	ret = dns_message_gettemprdatalist(msg, &datalist);
1127135446Strhodes	if (ret != ISC_R_SUCCESS)
1128135446Strhodes		goto cleanup_owner;
1129165071Sdougb	dataset = NULL;
1130165071Sdougb	ret = dns_message_gettemprdataset(msg, &dataset);
1131165071Sdougb	if (ret != ISC_R_SUCCESS)
1132165071Sdougb		goto cleanup_rdatalist;
1133135446Strhodes	datalist->rdclass = dns_rdataclass_any;
1134135446Strhodes	datalist->type = dns_rdatatype_tsig;
1135135446Strhodes	datalist->covers = 0;
1136135446Strhodes	datalist->ttl = 0;
1137135446Strhodes	ISC_LIST_INIT(datalist->rdata);
1138135446Strhodes	ISC_LIST_APPEND(datalist->rdata, rdata, link);
1139135446Strhodes	dns_rdataset_init(dataset);
1140135446Strhodes	RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
1141135446Strhodes		      == ISC_R_SUCCESS);
1142135446Strhodes	msg->tsig = dataset;
1143135446Strhodes	msg->tsigname = owner;
1144135446Strhodes
1145218384Sdougb	/* Windows does not like the tsig name being compressed. */
1146218384Sdougb	msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
1147218384Sdougb
1148135446Strhodes	return (ISC_R_SUCCESS);
1149135446Strhodes
1150165071Sdougb cleanup_rdatalist:
1151165071Sdougb	dns_message_puttemprdatalist(msg, &datalist);
1152165071Sdougb cleanup_owner:
1153165071Sdougb	dns_message_puttempname(msg, &owner);
1154165071Sdougb	goto cleanup_rdata;
1155165071Sdougb cleanup_dynbuf:
1156165071Sdougb	isc_buffer_free(&dynbuf);
1157165071Sdougb cleanup_rdata:
1158165071Sdougb	dns_message_puttemprdata(msg, &rdata);
1159165071Sdougb cleanup_signature:
1160135446Strhodes	if (tsig.signature != NULL)
1161135446Strhodes		isc_mem_put(mctx, tsig.signature, sigsize);
1162165071Sdougb cleanup_context:
1163135446Strhodes	if (ctx != NULL)
1164135446Strhodes		dst_context_destroy(&ctx);
1165135446Strhodes	return (ret);
1166135446Strhodes}
1167135446Strhodes
1168135446Strhodesisc_result_t
1169135446Strhodesdns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
1170135446Strhodes		dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2)
1171135446Strhodes{
1172135446Strhodes	dns_rdata_any_tsig_t tsig, querytsig;
1173135446Strhodes	isc_region_t r, source_r, header_r, sig_r;
1174135446Strhodes	isc_buffer_t databuf;
1175135446Strhodes	unsigned char data[32];
1176135446Strhodes	dns_name_t *keyname;
1177135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1178135446Strhodes	isc_stdtime_t now;
1179135446Strhodes	isc_result_t ret;
1180135446Strhodes	dns_tsigkey_t *tsigkey;
1181135446Strhodes	dst_key_t *key = NULL;
1182135446Strhodes	unsigned char header[DNS_MESSAGE_HEADERLEN];
1183135446Strhodes	dst_context_t *ctx = NULL;
1184135446Strhodes	isc_mem_t *mctx;
1185135446Strhodes	isc_uint16_t addcount, id;
1186170222Sdougb	unsigned int siglen;
1187170222Sdougb	unsigned int alg;
1188234010Sdougb	isc_boolean_t response;
1189135446Strhodes
1190135446Strhodes	REQUIRE(source != NULL);
1191135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1192135446Strhodes	tsigkey = dns_message_gettsigkey(msg);
1193234010Sdougb	response = is_response(msg);
1194193149Sdougb
1195135446Strhodes	REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
1196135446Strhodes
1197135446Strhodes	msg->verify_attempted = 1;
1198135446Strhodes
1199165071Sdougb	if (msg->tcp_continuation) {
1200165071Sdougb		if (tsigkey == NULL || msg->querytsig == NULL)
1201165071Sdougb			return (DNS_R_UNEXPECTEDTSIG);
1202135446Strhodes		return (tsig_verify_tcp(source, msg));
1203165071Sdougb	}
1204135446Strhodes
1205135446Strhodes	/*
1206135446Strhodes	 * There should be a TSIG record...
1207135446Strhodes	 */
1208135446Strhodes	if (msg->tsig == NULL)
1209135446Strhodes		return (DNS_R_EXPECTEDTSIG);
1210135446Strhodes
1211135446Strhodes	/*
1212135446Strhodes	 * If this is a response and there's no key or query TSIG, there
1213135446Strhodes	 * shouldn't be one on the response.
1214135446Strhodes	 */
1215234010Sdougb	if (response && (tsigkey == NULL || msg->querytsig == NULL))
1216135446Strhodes		return (DNS_R_UNEXPECTEDTSIG);
1217135446Strhodes
1218135446Strhodes	mctx = msg->mctx;
1219135446Strhodes
1220135446Strhodes	/*
1221135446Strhodes	 * If we're here, we know the message is well formed and contains a
1222135446Strhodes	 * TSIG record.
1223135446Strhodes	 */
1224135446Strhodes
1225135446Strhodes	keyname = msg->tsigname;
1226135446Strhodes	ret = dns_rdataset_first(msg->tsig);
1227135446Strhodes	if (ret != ISC_R_SUCCESS)
1228135446Strhodes		return (ret);
1229135446Strhodes	dns_rdataset_current(msg->tsig, &rdata);
1230135446Strhodes	ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
1231135446Strhodes	if (ret != ISC_R_SUCCESS)
1232135446Strhodes		return (ret);
1233135446Strhodes	dns_rdata_reset(&rdata);
1234234010Sdougb	if (response) {
1235135446Strhodes		ret = dns_rdataset_first(msg->querytsig);
1236135446Strhodes		if (ret != ISC_R_SUCCESS)
1237135446Strhodes			return (ret);
1238135446Strhodes		dns_rdataset_current(msg->querytsig, &rdata);
1239135446Strhodes		ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
1240135446Strhodes		if (ret != ISC_R_SUCCESS)
1241135446Strhodes			return (ret);
1242135446Strhodes	}
1243254402Serwin#if defined(__clang__) && \
1244254402Serwin       ( __clang_major__ < 3 || \
1245254402Serwin	(__clang_major__ == 3 && __clang_minor__ < 2) || \
1246254402Serwin	(__clang_major__ == 4 && __clang_minor__ < 2))
1247254402Serwin	/* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */
1248254402Serwin		else memset(&querytsig, 0, sizeof(querytsig));
1249254402Serwin#endif
1250135446Strhodes
1251135446Strhodes	/*
1252135446Strhodes	 * Do the key name and algorithm match that of the query?
1253135446Strhodes	 */
1254234010Sdougb	if (response &&
1255135446Strhodes	    (!dns_name_equal(keyname, &tsigkey->name) ||
1256193149Sdougb	     !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) {
1257135446Strhodes		msg->tsigstatus = dns_tsigerror_badkey;
1258135446Strhodes		tsig_log(msg->tsigkey, 2,
1259135446Strhodes			 "key name and algorithm do not match");
1260135446Strhodes		return (DNS_R_TSIGVERIFYFAILURE);
1261135446Strhodes	}
1262135446Strhodes
1263135446Strhodes	/*
1264135446Strhodes	 * Get the current time.
1265135446Strhodes	 */
1266135446Strhodes	isc_stdtime_get(&now);
1267135446Strhodes
1268135446Strhodes	/*
1269135446Strhodes	 * Find dns_tsigkey_t based on keyname.
1270135446Strhodes	 */
1271135446Strhodes	if (tsigkey == NULL) {
1272135446Strhodes		ret = ISC_R_NOTFOUND;
1273135446Strhodes		if (ring1 != NULL)
1274135446Strhodes			ret = dns_tsigkey_find(&tsigkey, keyname,
1275135446Strhodes					       &tsig.algorithm, ring1);
1276135446Strhodes		if (ret == ISC_R_NOTFOUND && ring2 != NULL)
1277135446Strhodes			ret = dns_tsigkey_find(&tsigkey, keyname,
1278135446Strhodes					       &tsig.algorithm, ring2);
1279135446Strhodes		if (ret != ISC_R_SUCCESS) {
1280135446Strhodes			msg->tsigstatus = dns_tsigerror_badkey;
1281135446Strhodes			ret = dns_tsigkey_create(keyname, &tsig.algorithm,
1282135446Strhodes						 NULL, 0, ISC_FALSE, NULL,
1283135446Strhodes						 now, now,
1284135446Strhodes						 mctx, NULL, &msg->tsigkey);
1285135446Strhodes			if (ret != ISC_R_SUCCESS)
1286135446Strhodes				return (ret);
1287135446Strhodes			tsig_log(msg->tsigkey, 2, "unknown key");
1288135446Strhodes			return (DNS_R_TSIGVERIFYFAILURE);
1289135446Strhodes		}
1290135446Strhodes		msg->tsigkey = tsigkey;
1291135446Strhodes	}
1292135446Strhodes
1293135446Strhodes	key = tsigkey->key;
1294135446Strhodes
1295135446Strhodes	/*
1296135446Strhodes	 * Is the time ok?
1297135446Strhodes	 */
1298135446Strhodes	if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1299135446Strhodes		msg->tsigstatus = dns_tsigerror_badtime;
1300135446Strhodes		tsig_log(msg->tsigkey, 2, "signature has expired");
1301135446Strhodes		return (DNS_R_CLOCKSKEW);
1302135446Strhodes	} else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
1303135446Strhodes		msg->tsigstatus = dns_tsigerror_badtime;
1304135446Strhodes		tsig_log(msg->tsigkey, 2, "signature is in the future");
1305135446Strhodes		return (DNS_R_CLOCKSKEW);
1306135446Strhodes	}
1307135446Strhodes
1308170222Sdougb	/*
1309170222Sdougb	 * Check digest length.
1310170222Sdougb	 */
1311170222Sdougb	alg = dst_key_alg(key);
1312170222Sdougb	ret = dst_key_sigsize(key, &siglen);
1313170222Sdougb	if (ret != ISC_R_SUCCESS)
1314170222Sdougb		return (ret);
1315170222Sdougb	if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 ||
1316170222Sdougb	    alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
1317170222Sdougb	    alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) {
1318170222Sdougb		isc_uint16_t digestbits = dst_key_getbits(key);
1319170222Sdougb		if (tsig.siglen > siglen) {
1320170222Sdougb			tsig_log(msg->tsigkey, 2, "signature length to big");
1321170222Sdougb			return (DNS_R_FORMERR);
1322170222Sdougb		}
1323170222Sdougb		if (tsig.siglen > 0 &&
1324170222Sdougb		    (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) {
1325170222Sdougb			tsig_log(msg->tsigkey, 2,
1326170222Sdougb				 "signature length below minimum");
1327170222Sdougb			return (DNS_R_FORMERR);
1328170222Sdougb		}
1329170222Sdougb		if (tsig.siglen > 0 && digestbits != 0 &&
1330186462Sdougb		    tsig.siglen < ((digestbits + 1) / 8)) {
1331170222Sdougb			msg->tsigstatus = dns_tsigerror_badtrunc;
1332170222Sdougb			tsig_log(msg->tsigkey, 2,
1333170222Sdougb				 "truncated signature length too small");
1334170222Sdougb			return (DNS_R_TSIGVERIFYFAILURE);
1335170222Sdougb		}
1336170222Sdougb		if (tsig.siglen > 0 && digestbits == 0 &&
1337170222Sdougb		    tsig.siglen < siglen) {
1338170222Sdougb			msg->tsigstatus = dns_tsigerror_badtrunc;
1339170222Sdougb			tsig_log(msg->tsigkey, 2, "signature length too small");
1340170222Sdougb			return (DNS_R_TSIGVERIFYFAILURE);
1341170222Sdougb		}
1342170222Sdougb	}
1343170222Sdougb
1344135446Strhodes	if (tsig.siglen > 0) {
1345135446Strhodes		sig_r.base = tsig.signature;
1346135446Strhodes		sig_r.length = tsig.siglen;
1347135446Strhodes
1348254402Serwin		ret = dst_context_create2(key, mctx,
1349254402Serwin					  DNS_LOGCATEGORY_DNSSEC, &ctx);
1350135446Strhodes		if (ret != ISC_R_SUCCESS)
1351135446Strhodes			return (ret);
1352135446Strhodes
1353234010Sdougb		if (response) {
1354135446Strhodes			isc_buffer_init(&databuf, data, sizeof(data));
1355135446Strhodes			isc_buffer_putuint16(&databuf, querytsig.siglen);
1356135446Strhodes			isc_buffer_usedregion(&databuf, &r);
1357135446Strhodes			ret = dst_context_adddata(ctx, &r);
1358135446Strhodes			if (ret != ISC_R_SUCCESS)
1359135446Strhodes				goto cleanup_context;
1360135446Strhodes			if (querytsig.siglen > 0) {
1361135446Strhodes				r.length = querytsig.siglen;
1362135446Strhodes				r.base = querytsig.signature;
1363135446Strhodes				ret = dst_context_adddata(ctx, &r);
1364135446Strhodes				if (ret != ISC_R_SUCCESS)
1365135446Strhodes					goto cleanup_context;
1366135446Strhodes			}
1367135446Strhodes		}
1368135446Strhodes
1369135446Strhodes		/*
1370135446Strhodes		 * Extract the header.
1371135446Strhodes		 */
1372135446Strhodes		isc_buffer_usedregion(source, &r);
1373262706Serwin		memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1374135446Strhodes		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1375135446Strhodes
1376135446Strhodes		/*
1377135446Strhodes		 * Decrement the additional field counter.
1378135446Strhodes		 */
1379262706Serwin		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1380135446Strhodes		addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
1381262706Serwin		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1382135446Strhodes
1383135446Strhodes		/*
1384135446Strhodes		 * Put in the original id.
1385135446Strhodes		 */
1386135446Strhodes		id = htons(tsig.originalid);
1387262706Serwin		memmove(&header[0], &id, 2);
1388135446Strhodes
1389135446Strhodes		/*
1390135446Strhodes		 * Digest the modified header.
1391135446Strhodes		 */
1392135446Strhodes		header_r.base = (unsigned char *) header;
1393135446Strhodes		header_r.length = DNS_MESSAGE_HEADERLEN;
1394135446Strhodes		ret = dst_context_adddata(ctx, &header_r);
1395135446Strhodes		if (ret != ISC_R_SUCCESS)
1396135446Strhodes			goto cleanup_context;
1397135446Strhodes
1398135446Strhodes		/*
1399135446Strhodes		 * Digest all non-TSIG records.
1400135446Strhodes		 */
1401135446Strhodes		isc_buffer_usedregion(source, &source_r);
1402135446Strhodes		r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
1403135446Strhodes		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
1404135446Strhodes		ret = dst_context_adddata(ctx, &r);
1405135446Strhodes		if (ret != ISC_R_SUCCESS)
1406135446Strhodes			goto cleanup_context;
1407135446Strhodes
1408135446Strhodes		/*
1409135446Strhodes		 * Digest the key name.
1410135446Strhodes		 */
1411135446Strhodes		dns_name_toregion(&tsigkey->name, &r);
1412135446Strhodes		ret = dst_context_adddata(ctx, &r);
1413135446Strhodes		if (ret != ISC_R_SUCCESS)
1414135446Strhodes			goto cleanup_context;
1415135446Strhodes
1416135446Strhodes		isc_buffer_init(&databuf, data, sizeof(data));
1417135446Strhodes		isc_buffer_putuint16(&databuf, tsig.common.rdclass);
1418135446Strhodes		isc_buffer_putuint32(&databuf, msg->tsig->ttl);
1419135446Strhodes		isc_buffer_usedregion(&databuf, &r);
1420135446Strhodes		ret = dst_context_adddata(ctx, &r);
1421135446Strhodes		if (ret != ISC_R_SUCCESS)
1422135446Strhodes			goto cleanup_context;
1423135446Strhodes
1424135446Strhodes		/*
1425135446Strhodes		 * Digest the key algorithm.
1426135446Strhodes		 */
1427135446Strhodes		dns_name_toregion(tsigkey->algorithm, &r);
1428135446Strhodes		ret = dst_context_adddata(ctx, &r);
1429135446Strhodes		if (ret != ISC_R_SUCCESS)
1430135446Strhodes			goto cleanup_context;
1431135446Strhodes
1432135446Strhodes		isc_buffer_clear(&databuf);
1433193149Sdougb		isc_buffer_putuint48(&databuf, tsig.timesigned);
1434135446Strhodes		isc_buffer_putuint16(&databuf, tsig.fudge);
1435135446Strhodes		isc_buffer_putuint16(&databuf, tsig.error);
1436135446Strhodes		isc_buffer_putuint16(&databuf, tsig.otherlen);
1437135446Strhodes		isc_buffer_usedregion(&databuf, &r);
1438135446Strhodes		ret = dst_context_adddata(ctx, &r);
1439135446Strhodes		if (ret != ISC_R_SUCCESS)
1440135446Strhodes			goto cleanup_context;
1441135446Strhodes
1442135446Strhodes		if (tsig.otherlen > 0) {
1443135446Strhodes			r.base = tsig.other;
1444135446Strhodes			r.length = tsig.otherlen;
1445135446Strhodes			ret = dst_context_adddata(ctx, &r);
1446135446Strhodes			if (ret != ISC_R_SUCCESS)
1447135446Strhodes				goto cleanup_context;
1448135446Strhodes		}
1449135446Strhodes
1450135446Strhodes		ret = dst_context_verify(ctx, &sig_r);
1451135446Strhodes		if (ret == DST_R_VERIFYFAILURE) {
1452135446Strhodes			msg->tsigstatus = dns_tsigerror_badsig;
1453135446Strhodes			ret = DNS_R_TSIGVERIFYFAILURE;
1454135446Strhodes			tsig_log(msg->tsigkey, 2,
1455193149Sdougb				 "signature failed to verify(1)");
1456135446Strhodes			goto cleanup_context;
1457135446Strhodes		} else if (ret != ISC_R_SUCCESS)
1458135446Strhodes			goto cleanup_context;
1459135446Strhodes
1460135446Strhodes		dst_context_destroy(&ctx);
1461135446Strhodes	} else if (tsig.error != dns_tsigerror_badsig &&
1462193149Sdougb		   tsig.error != dns_tsigerror_badkey) {
1463135446Strhodes		msg->tsigstatus = dns_tsigerror_badsig;
1464135446Strhodes		tsig_log(msg->tsigkey, 2, "signature was empty");
1465135446Strhodes		return (DNS_R_TSIGVERIFYFAILURE);
1466135446Strhodes	}
1467135446Strhodes
1468135446Strhodes	msg->tsigstatus = dns_rcode_noerror;
1469135446Strhodes
1470135446Strhodes	if (tsig.error != dns_rcode_noerror) {
1471135446Strhodes		if (tsig.error == dns_tsigerror_badtime)
1472135446Strhodes			return (DNS_R_CLOCKSKEW);
1473135446Strhodes		else
1474135446Strhodes			return (DNS_R_TSIGERRORSET);
1475135446Strhodes	}
1476135446Strhodes
1477135446Strhodes	msg->verified_sig = 1;
1478135446Strhodes
1479135446Strhodes	return (ISC_R_SUCCESS);
1480135446Strhodes
1481135446Strhodescleanup_context:
1482135446Strhodes	if (ctx != NULL)
1483135446Strhodes		dst_context_destroy(&ctx);
1484135446Strhodes
1485135446Strhodes	return (ret);
1486135446Strhodes}
1487135446Strhodes
1488135446Strhodesstatic isc_result_t
1489135446Strhodestsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
1490135446Strhodes	dns_rdata_any_tsig_t tsig, querytsig;
1491135446Strhodes	isc_region_t r, source_r, header_r, sig_r;
1492135446Strhodes	isc_buffer_t databuf;
1493135446Strhodes	unsigned char data[32];
1494135446Strhodes	dns_name_t *keyname;
1495135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
1496135446Strhodes	isc_stdtime_t now;
1497135446Strhodes	isc_result_t ret;
1498135446Strhodes	dns_tsigkey_t *tsigkey;
1499135446Strhodes	dst_key_t *key = NULL;
1500135446Strhodes	unsigned char header[DNS_MESSAGE_HEADERLEN];
1501135446Strhodes	isc_uint16_t addcount, id;
1502135446Strhodes	isc_boolean_t has_tsig = ISC_FALSE;
1503135446Strhodes	isc_mem_t *mctx;
1504135446Strhodes
1505135446Strhodes	REQUIRE(source != NULL);
1506135446Strhodes	REQUIRE(msg != NULL);
1507135446Strhodes	REQUIRE(dns_message_gettsigkey(msg) != NULL);
1508135446Strhodes	REQUIRE(msg->tcp_continuation == 1);
1509135446Strhodes	REQUIRE(msg->querytsig != NULL);
1510135446Strhodes
1511135446Strhodes	if (!is_response(msg))
1512135446Strhodes		return (DNS_R_EXPECTEDRESPONSE);
1513135446Strhodes
1514135446Strhodes	mctx = msg->mctx;
1515135446Strhodes
1516135446Strhodes	tsigkey = dns_message_gettsigkey(msg);
1517135446Strhodes
1518135446Strhodes	/*
1519135446Strhodes	 * Extract and parse the previous TSIG
1520135446Strhodes	 */
1521135446Strhodes	ret = dns_rdataset_first(msg->querytsig);
1522135446Strhodes	if (ret != ISC_R_SUCCESS)
1523135446Strhodes		return (ret);
1524135446Strhodes	dns_rdataset_current(msg->querytsig, &rdata);
1525135446Strhodes	ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
1526135446Strhodes	if (ret != ISC_R_SUCCESS)
1527135446Strhodes		return (ret);
1528135446Strhodes	dns_rdata_reset(&rdata);
1529135446Strhodes
1530135446Strhodes	/*
1531135446Strhodes	 * If there is a TSIG in this message, do some checks.
1532135446Strhodes	 */
1533135446Strhodes	if (msg->tsig != NULL) {
1534135446Strhodes		has_tsig = ISC_TRUE;
1535135446Strhodes
1536135446Strhodes		keyname = msg->tsigname;
1537135446Strhodes		ret = dns_rdataset_first(msg->tsig);
1538135446Strhodes		if (ret != ISC_R_SUCCESS)
1539135446Strhodes			goto cleanup_querystruct;
1540135446Strhodes		dns_rdataset_current(msg->tsig, &rdata);
1541135446Strhodes		ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
1542135446Strhodes		if (ret != ISC_R_SUCCESS)
1543135446Strhodes			goto cleanup_querystruct;
1544135446Strhodes
1545135446Strhodes		/*
1546135446Strhodes		 * Do the key name and algorithm match that of the query?
1547135446Strhodes		 */
1548135446Strhodes		if (!dns_name_equal(keyname, &tsigkey->name) ||
1549193149Sdougb		    !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) {
1550135446Strhodes			msg->tsigstatus = dns_tsigerror_badkey;
1551135446Strhodes			ret = DNS_R_TSIGVERIFYFAILURE;
1552135446Strhodes			tsig_log(msg->tsigkey, 2,
1553135446Strhodes				 "key name and algorithm do not match");
1554135446Strhodes			goto cleanup_querystruct;
1555135446Strhodes		}
1556135446Strhodes
1557135446Strhodes		/*
1558135446Strhodes		 * Is the time ok?
1559135446Strhodes		 */
1560135446Strhodes		isc_stdtime_get(&now);
1561135446Strhodes
1562135446Strhodes		if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1563135446Strhodes			msg->tsigstatus = dns_tsigerror_badtime;
1564135446Strhodes			tsig_log(msg->tsigkey, 2, "signature has expired");
1565135446Strhodes			ret = DNS_R_CLOCKSKEW;
1566135446Strhodes			goto cleanup_querystruct;
1567135446Strhodes		} else if (now + msg->timeadjust <
1568193149Sdougb			   tsig.timesigned - tsig.fudge) {
1569135446Strhodes			msg->tsigstatus = dns_tsigerror_badtime;
1570135446Strhodes			tsig_log(msg->tsigkey, 2,
1571135446Strhodes				 "signature is in the future");
1572135446Strhodes			ret = DNS_R_CLOCKSKEW;
1573135446Strhodes			goto cleanup_querystruct;
1574135446Strhodes		}
1575135446Strhodes	}
1576135446Strhodes
1577135446Strhodes	key = tsigkey->key;
1578135446Strhodes
1579135446Strhodes	if (msg->tsigctx == NULL) {
1580254402Serwin		ret = dst_context_create2(key, mctx,
1581254402Serwin					  DNS_LOGCATEGORY_DNSSEC,
1582254402Serwin					  &msg->tsigctx);
1583135446Strhodes		if (ret != ISC_R_SUCCESS)
1584135446Strhodes			goto cleanup_querystruct;
1585135446Strhodes
1586135446Strhodes		/*
1587135446Strhodes		 * Digest the length of the query signature
1588135446Strhodes		 */
1589135446Strhodes		isc_buffer_init(&databuf, data, sizeof(data));
1590135446Strhodes		isc_buffer_putuint16(&databuf, querytsig.siglen);
1591135446Strhodes		isc_buffer_usedregion(&databuf, &r);
1592135446Strhodes		ret = dst_context_adddata(msg->tsigctx, &r);
1593135446Strhodes		if (ret != ISC_R_SUCCESS)
1594135446Strhodes			goto cleanup_context;
1595135446Strhodes
1596135446Strhodes		/*
1597135446Strhodes		 * Digest the data of the query signature
1598135446Strhodes		 */
1599135446Strhodes		if (querytsig.siglen > 0) {
1600135446Strhodes			r.length = querytsig.siglen;
1601135446Strhodes			r.base = querytsig.signature;
1602135446Strhodes			ret = dst_context_adddata(msg->tsigctx, &r);
1603135446Strhodes			if (ret != ISC_R_SUCCESS)
1604135446Strhodes				goto cleanup_context;
1605135446Strhodes		}
1606135446Strhodes	}
1607135446Strhodes
1608135446Strhodes	/*
1609135446Strhodes	 * Extract the header.
1610135446Strhodes	 */
1611135446Strhodes	isc_buffer_usedregion(source, &r);
1612262706Serwin	memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1613135446Strhodes	isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1614135446Strhodes
1615135446Strhodes	/*
1616135446Strhodes	 * Decrement the additional field counter if necessary.
1617135446Strhodes	 */
1618135446Strhodes	if (has_tsig) {
1619262706Serwin		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1620135446Strhodes		addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
1621262706Serwin		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1622135446Strhodes	}
1623135446Strhodes
1624135446Strhodes	/*
1625135446Strhodes	 * Put in the original id.
1626135446Strhodes	 */
1627135446Strhodes	/* XXX Can TCP transfers be forwarded?  How would that work? */
1628135446Strhodes	if (has_tsig) {
1629135446Strhodes		id = htons(tsig.originalid);
1630262706Serwin		memmove(&header[0], &id, 2);
1631135446Strhodes	}
1632135446Strhodes
1633135446Strhodes	/*
1634135446Strhodes	 * Digest the modified header.
1635135446Strhodes	 */
1636135446Strhodes	header_r.base = (unsigned char *) header;
1637135446Strhodes	header_r.length = DNS_MESSAGE_HEADERLEN;
1638135446Strhodes	ret = dst_context_adddata(msg->tsigctx, &header_r);
1639135446Strhodes	if (ret != ISC_R_SUCCESS)
1640135446Strhodes		goto cleanup_context;
1641135446Strhodes
1642135446Strhodes	/*
1643135446Strhodes	 * Digest all non-TSIG records.
1644135446Strhodes	 */
1645135446Strhodes	isc_buffer_usedregion(source, &source_r);
1646135446Strhodes	r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
1647135446Strhodes	if (has_tsig)
1648135446Strhodes		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
1649135446Strhodes	else
1650135446Strhodes		r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
1651135446Strhodes	ret = dst_context_adddata(msg->tsigctx, &r);
1652135446Strhodes	if (ret != ISC_R_SUCCESS)
1653135446Strhodes		goto cleanup_context;
1654135446Strhodes
1655135446Strhodes	/*
1656135446Strhodes	 * Digest the time signed and fudge.
1657135446Strhodes	 */
1658135446Strhodes	if (has_tsig) {
1659135446Strhodes		isc_buffer_init(&databuf, data, sizeof(data));
1660193149Sdougb		isc_buffer_putuint48(&databuf, tsig.timesigned);
1661135446Strhodes		isc_buffer_putuint16(&databuf, tsig.fudge);
1662135446Strhodes		isc_buffer_usedregion(&databuf, &r);
1663135446Strhodes		ret = dst_context_adddata(msg->tsigctx, &r);
1664135446Strhodes		if (ret != ISC_R_SUCCESS)
1665135446Strhodes			goto cleanup_context;
1666135446Strhodes
1667135446Strhodes		sig_r.base = tsig.signature;
1668135446Strhodes		sig_r.length = tsig.siglen;
1669135446Strhodes		if (tsig.siglen == 0) {
1670135446Strhodes			if (tsig.error != dns_rcode_noerror) {
1671135446Strhodes				if (tsig.error == dns_tsigerror_badtime)
1672135446Strhodes					ret = DNS_R_CLOCKSKEW;
1673135446Strhodes				else
1674135446Strhodes					ret = DNS_R_TSIGERRORSET;
1675135446Strhodes			} else {
1676135446Strhodes				tsig_log(msg->tsigkey, 2,
1677135446Strhodes					 "signature is empty");
1678135446Strhodes				ret = DNS_R_TSIGVERIFYFAILURE;
1679135446Strhodes			}
1680135446Strhodes			goto cleanup_context;
1681135446Strhodes		}
1682135446Strhodes
1683135446Strhodes		ret = dst_context_verify(msg->tsigctx, &sig_r);
1684135446Strhodes		if (ret == DST_R_VERIFYFAILURE) {
1685135446Strhodes			msg->tsigstatus = dns_tsigerror_badsig;
1686135446Strhodes			tsig_log(msg->tsigkey, 2,
1687193149Sdougb				 "signature failed to verify(2)");
1688135446Strhodes			ret = DNS_R_TSIGVERIFYFAILURE;
1689135446Strhodes			goto cleanup_context;
1690135446Strhodes		}
1691135446Strhodes		else if (ret != ISC_R_SUCCESS)
1692135446Strhodes			goto cleanup_context;
1693135446Strhodes
1694135446Strhodes		dst_context_destroy(&msg->tsigctx);
1695135446Strhodes	}
1696135446Strhodes
1697135446Strhodes	msg->tsigstatus = dns_rcode_noerror;
1698135446Strhodes	return (ISC_R_SUCCESS);
1699135446Strhodes
1700135446Strhodes cleanup_context:
1701135446Strhodes	dst_context_destroy(&msg->tsigctx);
1702135446Strhodes
1703135446Strhodes cleanup_querystruct:
1704135446Strhodes	dns_rdata_freestruct(&querytsig);
1705135446Strhodes
1706135446Strhodes	return (ret);
1707135446Strhodes
1708135446Strhodes}
1709135446Strhodes
1710135446Strhodesisc_result_t
1711135446Strhodesdns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name,
1712135446Strhodes		 dns_name_t *algorithm, dns_tsig_keyring_t *ring)
1713135446Strhodes{
1714135446Strhodes	dns_tsigkey_t *key;
1715135446Strhodes	isc_stdtime_t now;
1716135446Strhodes	isc_result_t result;
1717135446Strhodes
1718135446Strhodes	REQUIRE(tsigkey != NULL);
1719135446Strhodes	REQUIRE(*tsigkey == NULL);
1720135446Strhodes	REQUIRE(name != NULL);
1721135446Strhodes	REQUIRE(ring != NULL);
1722135446Strhodes
1723193149Sdougb	RWLOCK(&ring->lock, isc_rwlocktype_write);
1724193149Sdougb	cleanup_ring(ring);
1725193149Sdougb	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1726193149Sdougb
1727135446Strhodes	isc_stdtime_get(&now);
1728135446Strhodes	RWLOCK(&ring->lock, isc_rwlocktype_read);
1729135446Strhodes	key = NULL;
1730135446Strhodes	result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
1731135446Strhodes	if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
1732135446Strhodes		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1733135446Strhodes		return (ISC_R_NOTFOUND);
1734135446Strhodes	}
1735135446Strhodes	if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
1736135446Strhodes		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1737135446Strhodes		return (ISC_R_NOTFOUND);
1738135446Strhodes	}
1739218384Sdougb	if (key->inception != key->expire && isc_serial_lt(key->expire, now)) {
1740135446Strhodes		/*
1741135446Strhodes		 * The key has expired.
1742135446Strhodes		 */
1743135446Strhodes		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1744135446Strhodes		RWLOCK(&ring->lock, isc_rwlocktype_write);
1745218384Sdougb		remove_fromring(key);
1746135446Strhodes		RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1747135446Strhodes		return (ISC_R_NOTFOUND);
1748135446Strhodes	}
1749218384Sdougb#if 0
1750218384Sdougb	/*
1751218384Sdougb	 * MPAXXX We really should look at the inception time.
1752218384Sdougb	 */
1753218384Sdougb	if (key->inception != key->expire &&
1754218384Sdougb	    isc_serial_lt(key->inception, now)) {
1755218384Sdougb		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1756218384Sdougb		adjust_lru(key);
1757218384Sdougb		return (ISC_R_NOTFOUND);
1758218384Sdougb	}
1759218384Sdougb#endif
1760135446Strhodes	isc_refcount_increment(&key->refs, NULL);
1761135446Strhodes	RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1762218384Sdougb	adjust_lru(key);
1763135446Strhodes	*tsigkey = key;
1764135446Strhodes	return (ISC_R_SUCCESS);
1765135446Strhodes}
1766135446Strhodes
1767135446Strhodesstatic void
1768135446Strhodesfree_tsignode(void *node, void *_unused) {
1769135446Strhodes	dns_tsigkey_t *key;
1770135446Strhodes
1771254402Serwin	REQUIRE(node != NULL);
1772254402Serwin
1773135446Strhodes	UNUSED(_unused);
1774135446Strhodes
1775135446Strhodes	key = node;
1776254402Serwin	if (key->generated) {
1777254402Serwin		if (ISC_LINK_LINKED(key, link))
1778254402Serwin			ISC_LIST_UNLINK(key->ring->lru, key, link);
1779254402Serwin	}
1780135446Strhodes	dns_tsigkey_detach(&key);
1781135446Strhodes}
1782135446Strhodes
1783135446Strhodesisc_result_t
1784135446Strhodesdns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
1785135446Strhodes	isc_result_t result;
1786135446Strhodes	dns_tsig_keyring_t *ring;
1787135446Strhodes
1788135446Strhodes	REQUIRE(mctx != NULL);
1789135446Strhodes	REQUIRE(ringp != NULL);
1790135446Strhodes	REQUIRE(*ringp == NULL);
1791135446Strhodes
1792135446Strhodes	ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t));
1793135446Strhodes	if (ring == NULL)
1794135446Strhodes		return (ISC_R_NOMEMORY);
1795135446Strhodes
1796135446Strhodes	result = isc_rwlock_init(&ring->lock, 0, 0);
1797174187Sdougb	if (result != ISC_R_SUCCESS) {
1798174187Sdougb		isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
1799170222Sdougb		return (result);
1800174187Sdougb	}
1801135446Strhodes
1802135446Strhodes	ring->keys = NULL;
1803135446Strhodes	result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys);
1804135446Strhodes	if (result != ISC_R_SUCCESS) {
1805135446Strhodes		isc_rwlock_destroy(&ring->lock);
1806135446Strhodes		isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
1807135446Strhodes		return (result);
1808135446Strhodes	}
1809135446Strhodes
1810193149Sdougb	ring->writecount = 0;
1811186462Sdougb	ring->mctx = NULL;
1812218384Sdougb	ring->generated = 0;
1813218384Sdougb	ring->maxgenerated = DNS_TSIG_MAXGENERATEDKEYS;
1814218384Sdougb	ISC_LIST_INIT(ring->lru);
1815186462Sdougb	isc_mem_attach(mctx, &ring->mctx);
1816224092Sdougb	ring->references = 1;
1817135446Strhodes
1818135446Strhodes	*ringp = ring;
1819135446Strhodes	return (ISC_R_SUCCESS);
1820135446Strhodes}
1821135446Strhodes
1822224092Sdougbisc_result_t
1823224092Sdougbdns_tsigkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
1824224092Sdougb		    dns_tsigkey_t *tkey)
1825224092Sdougb{
1826224092Sdougb	isc_result_t result;
1827224092Sdougb
1828224092Sdougb	result = keyring_add(ring, name, tkey);
1829224092Sdougb	if (result == ISC_R_SUCCESS)
1830224092Sdougb		isc_refcount_increment(&tkey->refs, NULL);
1831224092Sdougb
1832224092Sdougb	return (result);
1833224092Sdougb}
1834224092Sdougb
1835135446Strhodesvoid
1836224092Sdougbdns_tsigkeyring_attach(dns_tsig_keyring_t *source, dns_tsig_keyring_t **target)
1837224092Sdougb{
1838224092Sdougb	REQUIRE(source != NULL);
1839224092Sdougb	REQUIRE(target != NULL && *target == NULL);
1840224092Sdougb
1841224092Sdougb	RWLOCK(&source->lock, isc_rwlocktype_write);
1842224092Sdougb	INSIST(source->references > 0);
1843224092Sdougb	source->references++;
1844224092Sdougb	INSIST(source->references > 0);
1845224092Sdougb	*target = source;
1846224092Sdougb	RWUNLOCK(&source->lock, isc_rwlocktype_write);
1847224092Sdougb}
1848224092Sdougb
1849224092Sdougbvoid
1850224092Sdougbdns_tsigkeyring_detach(dns_tsig_keyring_t **ringp) {
1851135446Strhodes	dns_tsig_keyring_t *ring;
1852224092Sdougb	unsigned int references;
1853135446Strhodes
1854135446Strhodes	REQUIRE(ringp != NULL);
1855135446Strhodes	REQUIRE(*ringp != NULL);
1856135446Strhodes
1857135446Strhodes	ring = *ringp;
1858135446Strhodes	*ringp = NULL;
1859135446Strhodes
1860224092Sdougb	RWLOCK(&ring->lock, isc_rwlocktype_write);
1861224092Sdougb	INSIST(ring->references > 0);
1862224092Sdougb	ring->references--;
1863224092Sdougb	references = ring->references;
1864224092Sdougb	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1865224092Sdougb
1866224092Sdougb	if (references == 0)
1867224092Sdougb		destroyring(ring);
1868135446Strhodes}
1869224092Sdougb
1870224092Sdougbvoid
1871224092Sdougbdns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp) {
1872224092Sdougb	isc_stdtime_t now;
1873224092Sdougb	isc_result_t result;
1874224092Sdougb
1875224092Sdougb	isc_stdtime_get(&now);
1876224092Sdougb	do {
1877224092Sdougb		result = restore_key(ring, now, fp);
1878224092Sdougb		if (result == ISC_R_NOMORE)
1879224092Sdougb			return;
1880224092Sdougb		if (result == DNS_R_BADALG || result == DNS_R_EXPIRED)
1881224092Sdougb			result = ISC_R_SUCCESS;
1882224092Sdougb	} while (result == ISC_R_SUCCESS);
1883224092Sdougb}
1884