xref: /minix3/external/bsd/bind/dist/lib/dns/tsig.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1*00b67f09SDavid van Moolenbroek /*	$NetBSD: tsig.c,v 1.8 2015/07/08 17:28:59 christos Exp $	*/
2*00b67f09SDavid van Moolenbroek 
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek  * Copyright (C) 2004-2015  Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek  * Copyright (C) 1999-2002  Internet Software Consortium.
6*00b67f09SDavid van Moolenbroek  *
7*00b67f09SDavid van Moolenbroek  * Permission to use, copy, modify, and/or distribute this software for any
8*00b67f09SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
9*00b67f09SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
10*00b67f09SDavid van Moolenbroek  *
11*00b67f09SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12*00b67f09SDavid van Moolenbroek  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13*00b67f09SDavid van Moolenbroek  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14*00b67f09SDavid van Moolenbroek  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15*00b67f09SDavid van Moolenbroek  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16*00b67f09SDavid van Moolenbroek  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*00b67f09SDavid van Moolenbroek  * PERFORMANCE OF THIS SOFTWARE.
18*00b67f09SDavid van Moolenbroek  */
19*00b67f09SDavid van Moolenbroek 
20*00b67f09SDavid van Moolenbroek /*
21*00b67f09SDavid van Moolenbroek  * Id
22*00b67f09SDavid van Moolenbroek  */
23*00b67f09SDavid van Moolenbroek /*! \file */
24*00b67f09SDavid van Moolenbroek #include <config.h>
25*00b67f09SDavid van Moolenbroek #include <stdlib.h>
26*00b67f09SDavid van Moolenbroek 
27*00b67f09SDavid van Moolenbroek #include <isc/buffer.h>
28*00b67f09SDavid van Moolenbroek #include <isc/mem.h>
29*00b67f09SDavid van Moolenbroek #include <isc/print.h>
30*00b67f09SDavid van Moolenbroek #include <isc/refcount.h>
31*00b67f09SDavid van Moolenbroek #include <isc/serial.h>
32*00b67f09SDavid van Moolenbroek #include <isc/string.h>		/* Required for HP/UX (and others?) */
33*00b67f09SDavid van Moolenbroek #include <isc/util.h>
34*00b67f09SDavid van Moolenbroek #include <isc/time.h>
35*00b67f09SDavid van Moolenbroek 
36*00b67f09SDavid van Moolenbroek #include <dns/keyvalues.h>
37*00b67f09SDavid van Moolenbroek #include <dns/log.h>
38*00b67f09SDavid van Moolenbroek #include <dns/message.h>
39*00b67f09SDavid van Moolenbroek #include <dns/fixedname.h>
40*00b67f09SDavid van Moolenbroek #include <dns/rbt.h>
41*00b67f09SDavid van Moolenbroek #include <dns/rdata.h>
42*00b67f09SDavid van Moolenbroek #include <dns/rdatalist.h>
43*00b67f09SDavid van Moolenbroek #include <dns/rdataset.h>
44*00b67f09SDavid van Moolenbroek #include <dns/rdatastruct.h>
45*00b67f09SDavid van Moolenbroek #include <dns/result.h>
46*00b67f09SDavid van Moolenbroek #include <dns/tsig.h>
47*00b67f09SDavid van Moolenbroek 
48*00b67f09SDavid van Moolenbroek #include <dst/result.h>
49*00b67f09SDavid van Moolenbroek 
50*00b67f09SDavid van Moolenbroek #define TSIG_MAGIC		ISC_MAGIC('T', 'S', 'I', 'G')
51*00b67f09SDavid van Moolenbroek #define VALID_TSIG_KEY(x)	ISC_MAGIC_VALID(x, TSIG_MAGIC)
52*00b67f09SDavid van Moolenbroek 
53*00b67f09SDavid van Moolenbroek #ifndef DNS_TSIG_MAXGENERATEDKEYS
54*00b67f09SDavid van Moolenbroek #define DNS_TSIG_MAXGENERATEDKEYS 4096
55*00b67f09SDavid van Moolenbroek #endif
56*00b67f09SDavid van Moolenbroek 
57*00b67f09SDavid van Moolenbroek #define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
58*00b67f09SDavid van Moolenbroek #define algname_is_allocated(algname) \
59*00b67f09SDavid van Moolenbroek 	((algname) != dns_tsig_hmacmd5_name && \
60*00b67f09SDavid van Moolenbroek 	 (algname) != dns_tsig_hmacsha1_name && \
61*00b67f09SDavid van Moolenbroek 	 (algname) != dns_tsig_hmacsha224_name && \
62*00b67f09SDavid van Moolenbroek 	 (algname) != dns_tsig_hmacsha256_name && \
63*00b67f09SDavid van Moolenbroek 	 (algname) != dns_tsig_hmacsha384_name && \
64*00b67f09SDavid van Moolenbroek 	 (algname) != dns_tsig_hmacsha512_name && \
65*00b67f09SDavid van Moolenbroek 	 (algname) != dns_tsig_gssapi_name && \
66*00b67f09SDavid van Moolenbroek 	 (algname) != dns_tsig_gssapims_name)
67*00b67f09SDavid van Moolenbroek 
68*00b67f09SDavid van Moolenbroek #define BADTIMELEN 6
69*00b67f09SDavid van Moolenbroek 
70*00b67f09SDavid van Moolenbroek static unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
71*00b67f09SDavid van Moolenbroek static unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
72*00b67f09SDavid van Moolenbroek 
73*00b67f09SDavid van Moolenbroek static dns_name_t hmacmd5 = {
74*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
75*00b67f09SDavid van Moolenbroek 	hmacmd5_ndata, 26, 5,
76*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
77*00b67f09SDavid van Moolenbroek 	hmacmd5_offsets, NULL,
78*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
79*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
80*00b67f09SDavid van Moolenbroek };
81*00b67f09SDavid van Moolenbroek 
82*00b67f09SDavid van Moolenbroek dns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
83*00b67f09SDavid van Moolenbroek 
84*00b67f09SDavid van Moolenbroek static unsigned char gsstsig_ndata[] = "\010gss-tsig";
85*00b67f09SDavid van Moolenbroek static unsigned char gsstsig_offsets[] = { 0, 9 };
86*00b67f09SDavid van Moolenbroek static dns_name_t gsstsig = {
87*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
88*00b67f09SDavid van Moolenbroek 	gsstsig_ndata, 10, 2,
89*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
90*00b67f09SDavid van Moolenbroek 	gsstsig_offsets, NULL,
91*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
92*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
93*00b67f09SDavid van Moolenbroek };
94*00b67f09SDavid van Moolenbroek LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig;
95*00b67f09SDavid van Moolenbroek 
96*00b67f09SDavid van Moolenbroek /*
97*00b67f09SDavid van Moolenbroek  * Since Microsoft doesn't follow its own standard, we will use this
98*00b67f09SDavid van Moolenbroek  * alternate name as a second guess.
99*00b67f09SDavid van Moolenbroek  */
100*00b67f09SDavid van Moolenbroek static unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
101*00b67f09SDavid van Moolenbroek static unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
102*00b67f09SDavid van Moolenbroek static dns_name_t gsstsigms = {
103*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
104*00b67f09SDavid van Moolenbroek 	gsstsigms_ndata, 19, 4,
105*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
106*00b67f09SDavid van Moolenbroek 	gsstsigms_offsets, NULL,
107*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
108*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
109*00b67f09SDavid van Moolenbroek };
110*00b67f09SDavid van Moolenbroek LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapims_name = &gsstsigms;
111*00b67f09SDavid van Moolenbroek 
112*00b67f09SDavid van Moolenbroek static unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
113*00b67f09SDavid van Moolenbroek static unsigned char hmacsha1_offsets[] = { 0, 10 };
114*00b67f09SDavid van Moolenbroek 
115*00b67f09SDavid van Moolenbroek static dns_name_t  hmacsha1 = {
116*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
117*00b67f09SDavid van Moolenbroek 	hmacsha1_ndata, 11, 2,
118*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
119*00b67f09SDavid van Moolenbroek 	hmacsha1_offsets, NULL,
120*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
121*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
122*00b67f09SDavid van Moolenbroek };
123*00b67f09SDavid van Moolenbroek 
124*00b67f09SDavid van Moolenbroek LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;
125*00b67f09SDavid van Moolenbroek 
126*00b67f09SDavid van Moolenbroek static unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
127*00b67f09SDavid van Moolenbroek static unsigned char hmacsha224_offsets[] = { 0, 12 };
128*00b67f09SDavid van Moolenbroek 
129*00b67f09SDavid van Moolenbroek static dns_name_t hmacsha224 = {
130*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
131*00b67f09SDavid van Moolenbroek 	hmacsha224_ndata, 13, 2,
132*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
133*00b67f09SDavid van Moolenbroek 	hmacsha224_offsets, NULL,
134*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
135*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
136*00b67f09SDavid van Moolenbroek };
137*00b67f09SDavid van Moolenbroek 
138*00b67f09SDavid van Moolenbroek LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;
139*00b67f09SDavid van Moolenbroek 
140*00b67f09SDavid van Moolenbroek static unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
141*00b67f09SDavid van Moolenbroek static unsigned char hmacsha256_offsets[] = { 0, 12 };
142*00b67f09SDavid van Moolenbroek 
143*00b67f09SDavid van Moolenbroek static dns_name_t hmacsha256 = {
144*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
145*00b67f09SDavid van Moolenbroek 	hmacsha256_ndata, 13, 2,
146*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
147*00b67f09SDavid van Moolenbroek 	hmacsha256_offsets, NULL,
148*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
149*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
150*00b67f09SDavid van Moolenbroek };
151*00b67f09SDavid van Moolenbroek 
152*00b67f09SDavid van Moolenbroek LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;
153*00b67f09SDavid van Moolenbroek 
154*00b67f09SDavid van Moolenbroek static unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
155*00b67f09SDavid van Moolenbroek static unsigned char hmacsha384_offsets[] = { 0, 12 };
156*00b67f09SDavid van Moolenbroek 
157*00b67f09SDavid van Moolenbroek static dns_name_t hmacsha384 = {
158*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
159*00b67f09SDavid van Moolenbroek 	hmacsha384_ndata, 13, 2,
160*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
161*00b67f09SDavid van Moolenbroek 	hmacsha384_offsets, NULL,
162*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
163*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
164*00b67f09SDavid van Moolenbroek };
165*00b67f09SDavid van Moolenbroek 
166*00b67f09SDavid van Moolenbroek LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;
167*00b67f09SDavid van Moolenbroek 
168*00b67f09SDavid van Moolenbroek static unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
169*00b67f09SDavid van Moolenbroek static unsigned char hmacsha512_offsets[] = { 0, 12 };
170*00b67f09SDavid van Moolenbroek 
171*00b67f09SDavid van Moolenbroek static dns_name_t hmacsha512 = {
172*00b67f09SDavid van Moolenbroek 	DNS_NAME_MAGIC,
173*00b67f09SDavid van Moolenbroek 	hmacsha512_ndata, 13, 2,
174*00b67f09SDavid van Moolenbroek 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
175*00b67f09SDavid van Moolenbroek 	hmacsha512_offsets, NULL,
176*00b67f09SDavid van Moolenbroek 	{(void *)-1, (void *)-1},
177*00b67f09SDavid van Moolenbroek 	{NULL, NULL}
178*00b67f09SDavid van Moolenbroek };
179*00b67f09SDavid van Moolenbroek 
180*00b67f09SDavid van Moolenbroek LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;
181*00b67f09SDavid van Moolenbroek 
182*00b67f09SDavid van Moolenbroek static isc_result_t
183*00b67f09SDavid van Moolenbroek tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
184*00b67f09SDavid van Moolenbroek 
185*00b67f09SDavid van Moolenbroek static void
186*00b67f09SDavid van Moolenbroek tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
187*00b67f09SDavid van Moolenbroek      ISC_FORMAT_PRINTF(3, 4);
188*00b67f09SDavid van Moolenbroek 
189*00b67f09SDavid van Moolenbroek static void
190*00b67f09SDavid van Moolenbroek cleanup_ring(dns_tsig_keyring_t *ring);
191*00b67f09SDavid van Moolenbroek static void
192*00b67f09SDavid van Moolenbroek tsigkey_free(dns_tsigkey_t *key);
193*00b67f09SDavid van Moolenbroek 
194*00b67f09SDavid van Moolenbroek static void
tsig_log(dns_tsigkey_t * key,int level,const char * fmt,...)195*00b67f09SDavid van Moolenbroek tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
196*00b67f09SDavid van Moolenbroek 	va_list ap;
197*00b67f09SDavid van Moolenbroek 	char message[4096];
198*00b67f09SDavid van Moolenbroek 	char namestr[DNS_NAME_FORMATSIZE];
199*00b67f09SDavid van Moolenbroek 	char creatorstr[DNS_NAME_FORMATSIZE];
200*00b67f09SDavid van Moolenbroek 
201*00b67f09SDavid van Moolenbroek 	if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
202*00b67f09SDavid van Moolenbroek 		return;
203*00b67f09SDavid van Moolenbroek 	if (key != NULL)
204*00b67f09SDavid van Moolenbroek 		dns_name_format(&key->name, namestr, sizeof(namestr));
205*00b67f09SDavid van Moolenbroek 	else
206*00b67f09SDavid van Moolenbroek 		strcpy(namestr, "<null>");
207*00b67f09SDavid van Moolenbroek 
208*00b67f09SDavid van Moolenbroek 	if (key != NULL && key->generated && key->creator)
209*00b67f09SDavid van Moolenbroek 		dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
210*00b67f09SDavid van Moolenbroek 	else
211*00b67f09SDavid van Moolenbroek 		strcpy(creatorstr, "<null>");
212*00b67f09SDavid van Moolenbroek 
213*00b67f09SDavid van Moolenbroek 	va_start(ap, fmt);
214*00b67f09SDavid van Moolenbroek 	vsnprintf(message, sizeof(message), fmt, ap);
215*00b67f09SDavid van Moolenbroek 	va_end(ap);
216*00b67f09SDavid van Moolenbroek 	if (key != NULL && key->generated)
217*00b67f09SDavid van Moolenbroek 		isc_log_write(dns_lctx,
218*00b67f09SDavid van Moolenbroek 			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
219*00b67f09SDavid van Moolenbroek 			      level, "tsig key '%s' (%s): %s",
220*00b67f09SDavid van Moolenbroek 			      namestr, creatorstr, message);
221*00b67f09SDavid van Moolenbroek 	else
222*00b67f09SDavid van Moolenbroek 		isc_log_write(dns_lctx,
223*00b67f09SDavid van Moolenbroek 			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
224*00b67f09SDavid van Moolenbroek 			      level, "tsig key '%s': %s", namestr, message);
225*00b67f09SDavid van Moolenbroek }
226*00b67f09SDavid van Moolenbroek 
227*00b67f09SDavid van Moolenbroek static void
remove_fromring(dns_tsigkey_t * tkey)228*00b67f09SDavid van Moolenbroek remove_fromring(dns_tsigkey_t *tkey) {
229*00b67f09SDavid van Moolenbroek 	if (tkey->generated) {
230*00b67f09SDavid van Moolenbroek 		ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
231*00b67f09SDavid van Moolenbroek 		tkey->ring->generated--;
232*00b67f09SDavid van Moolenbroek 	}
233*00b67f09SDavid van Moolenbroek 	(void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, ISC_FALSE);
234*00b67f09SDavid van Moolenbroek }
235*00b67f09SDavid van Moolenbroek 
236*00b67f09SDavid van Moolenbroek static void
adjust_lru(dns_tsigkey_t * tkey)237*00b67f09SDavid van Moolenbroek adjust_lru(dns_tsigkey_t *tkey) {
238*00b67f09SDavid van Moolenbroek 	if (tkey->generated) {
239*00b67f09SDavid van Moolenbroek 		RWLOCK(&tkey->ring->lock, isc_rwlocktype_write);
240*00b67f09SDavid van Moolenbroek 		/*
241*00b67f09SDavid van Moolenbroek 		 * We may have been removed from the LRU list between
242*00b67f09SDavid van Moolenbroek 		 * removing the read lock and aquiring the write lock.
243*00b67f09SDavid van Moolenbroek 		 */
244*00b67f09SDavid van Moolenbroek 		if (ISC_LINK_LINKED(tkey, link) &&
245*00b67f09SDavid van Moolenbroek 		    tkey->ring->lru.tail != tkey)
246*00b67f09SDavid van Moolenbroek 		{
247*00b67f09SDavid van Moolenbroek 			ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
248*00b67f09SDavid van Moolenbroek 			ISC_LIST_APPEND(tkey->ring->lru, tkey, link);
249*00b67f09SDavid van Moolenbroek 		}
250*00b67f09SDavid van Moolenbroek 		RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write);
251*00b67f09SDavid van Moolenbroek 	}
252*00b67f09SDavid van Moolenbroek }
253*00b67f09SDavid van Moolenbroek 
254*00b67f09SDavid van Moolenbroek /*
255*00b67f09SDavid van Moolenbroek  * A supplemental routine just to add a key to ring.  Note that reference
256*00b67f09SDavid van Moolenbroek  * counter should be counted separately because we may be adding the key
257*00b67f09SDavid van Moolenbroek  * as part of creation of the key, in which case the reference counter was
258*00b67f09SDavid van Moolenbroek  * already initialized.  Also note we don't need RWLOCK for the reference
259*00b67f09SDavid van Moolenbroek  * counter: it's protected by a separate lock.
260*00b67f09SDavid van Moolenbroek  */
261*00b67f09SDavid van Moolenbroek static isc_result_t
keyring_add(dns_tsig_keyring_t * ring,dns_name_t * name,dns_tsigkey_t * tkey)262*00b67f09SDavid van Moolenbroek keyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
263*00b67f09SDavid van Moolenbroek 	    dns_tsigkey_t *tkey)
264*00b67f09SDavid van Moolenbroek {
265*00b67f09SDavid van Moolenbroek 	isc_result_t result;
266*00b67f09SDavid van Moolenbroek 
267*00b67f09SDavid van Moolenbroek 	RWLOCK(&ring->lock, isc_rwlocktype_write);
268*00b67f09SDavid van Moolenbroek 	ring->writecount++;
269*00b67f09SDavid van Moolenbroek 
270*00b67f09SDavid van Moolenbroek 	/*
271*00b67f09SDavid van Moolenbroek 	 * Do on the fly cleaning.  Find some nodes we might not
272*00b67f09SDavid van Moolenbroek 	 * want around any more.
273*00b67f09SDavid van Moolenbroek 	 */
274*00b67f09SDavid van Moolenbroek 	if (ring->writecount > 10) {
275*00b67f09SDavid van Moolenbroek 		cleanup_ring(ring);
276*00b67f09SDavid van Moolenbroek 		ring->writecount = 0;
277*00b67f09SDavid van Moolenbroek 	}
278*00b67f09SDavid van Moolenbroek 
279*00b67f09SDavid van Moolenbroek 	result = dns_rbt_addname(ring->keys, name, tkey);
280*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS && tkey->generated) {
281*00b67f09SDavid van Moolenbroek 		/*
282*00b67f09SDavid van Moolenbroek 		 * Add the new key to the LRU list and remove the least
283*00b67f09SDavid van Moolenbroek 		 * recently used key if there are too many keys on the list.
284*00b67f09SDavid van Moolenbroek 		 */
285*00b67f09SDavid van Moolenbroek 		ISC_LIST_APPEND(ring->lru, tkey, link);
286*00b67f09SDavid van Moolenbroek 		if (ring->generated++ > ring->maxgenerated)
287*00b67f09SDavid van Moolenbroek 			remove_fromring(ISC_LIST_HEAD(ring->lru));
288*00b67f09SDavid van Moolenbroek 	}
289*00b67f09SDavid van Moolenbroek 	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
290*00b67f09SDavid van Moolenbroek 
291*00b67f09SDavid van Moolenbroek 	return (result);
292*00b67f09SDavid van Moolenbroek }
293*00b67f09SDavid van Moolenbroek 
294*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsigkey_createfromkey(dns_name_t * name,dns_name_t * algorithm,dst_key_t * dstkey,isc_boolean_t generated,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)295*00b67f09SDavid van Moolenbroek dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
296*00b67f09SDavid van Moolenbroek 			  dst_key_t *dstkey, isc_boolean_t generated,
297*00b67f09SDavid van Moolenbroek 			  dns_name_t *creator, isc_stdtime_t inception,
298*00b67f09SDavid van Moolenbroek 			  isc_stdtime_t expire, isc_mem_t *mctx,
299*00b67f09SDavid van Moolenbroek 			  dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
300*00b67f09SDavid van Moolenbroek {
301*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *tkey;
302*00b67f09SDavid van Moolenbroek 	isc_result_t ret;
303*00b67f09SDavid van Moolenbroek 	unsigned int refs = 0;
304*00b67f09SDavid van Moolenbroek 
305*00b67f09SDavid van Moolenbroek 	REQUIRE(key == NULL || *key == NULL);
306*00b67f09SDavid van Moolenbroek 	REQUIRE(name != NULL);
307*00b67f09SDavid van Moolenbroek 	REQUIRE(algorithm != NULL);
308*00b67f09SDavid van Moolenbroek 	REQUIRE(mctx != NULL);
309*00b67f09SDavid van Moolenbroek 	REQUIRE(key != NULL || ring != NULL);
310*00b67f09SDavid van Moolenbroek 
311*00b67f09SDavid van Moolenbroek 	tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t));
312*00b67f09SDavid van Moolenbroek 	if (tkey == NULL)
313*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
314*00b67f09SDavid van Moolenbroek 
315*00b67f09SDavid van Moolenbroek 	dns_name_init(&tkey->name, NULL);
316*00b67f09SDavid van Moolenbroek 	ret = dns_name_dup(name, mctx, &tkey->name);
317*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
318*00b67f09SDavid van Moolenbroek 		goto cleanup_key;
319*00b67f09SDavid van Moolenbroek 	(void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
320*00b67f09SDavid van Moolenbroek 
321*00b67f09SDavid van Moolenbroek 	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
322*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_HMACMD5_NAME;
323*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACMD5) {
324*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
325*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
326*00b67f09SDavid van Moolenbroek 		}
327*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
328*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_HMACSHA1_NAME;
329*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) {
330*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
331*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
332*00b67f09SDavid van Moolenbroek 		}
333*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
334*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_HMACSHA224_NAME;
335*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL &&
336*00b67f09SDavid van Moolenbroek 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA224) {
337*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
338*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
339*00b67f09SDavid van Moolenbroek 		}
340*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
341*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_HMACSHA256_NAME;
342*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL &&
343*00b67f09SDavid van Moolenbroek 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA256) {
344*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
345*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
346*00b67f09SDavid van Moolenbroek 		}
347*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
348*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_HMACSHA384_NAME;
349*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL &&
350*00b67f09SDavid van Moolenbroek 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA384) {
351*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
352*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
353*00b67f09SDavid van Moolenbroek 		}
354*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
355*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_HMACSHA512_NAME;
356*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL &&
357*00b67f09SDavid van Moolenbroek 		    dst_key_alg(dstkey) != DST_ALG_HMACSHA512) {
358*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
359*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
360*00b67f09SDavid van Moolenbroek 		}
361*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
362*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_GSSAPI_NAME;
363*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
364*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
365*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
366*00b67f09SDavid van Moolenbroek 		}
367*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
368*00b67f09SDavid van Moolenbroek 		tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME;
369*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
370*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
371*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
372*00b67f09SDavid van Moolenbroek 		}
373*00b67f09SDavid van Moolenbroek 	} else {
374*00b67f09SDavid van Moolenbroek 		if (dstkey != NULL) {
375*00b67f09SDavid van Moolenbroek 			ret = DNS_R_BADALG;
376*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
377*00b67f09SDavid van Moolenbroek 		}
378*00b67f09SDavid van Moolenbroek 		tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t));
379*00b67f09SDavid van Moolenbroek 		if (tkey->algorithm == NULL) {
380*00b67f09SDavid van Moolenbroek 			ret = ISC_R_NOMEMORY;
381*00b67f09SDavid van Moolenbroek 			goto cleanup_name;
382*00b67f09SDavid van Moolenbroek 		}
383*00b67f09SDavid van Moolenbroek 		dns_name_init(tkey->algorithm, NULL);
384*00b67f09SDavid van Moolenbroek 		ret = dns_name_dup(algorithm, mctx, tkey->algorithm);
385*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
386*00b67f09SDavid van Moolenbroek 			goto cleanup_algorithm;
387*00b67f09SDavid van Moolenbroek 		(void)dns_name_downcase(tkey->algorithm, tkey->algorithm,
388*00b67f09SDavid van Moolenbroek 					NULL);
389*00b67f09SDavid van Moolenbroek 	}
390*00b67f09SDavid van Moolenbroek 
391*00b67f09SDavid van Moolenbroek 	if (creator != NULL) {
392*00b67f09SDavid van Moolenbroek 		tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
393*00b67f09SDavid van Moolenbroek 		if (tkey->creator == NULL) {
394*00b67f09SDavid van Moolenbroek 			ret = ISC_R_NOMEMORY;
395*00b67f09SDavid van Moolenbroek 			goto cleanup_algorithm;
396*00b67f09SDavid van Moolenbroek 		}
397*00b67f09SDavid van Moolenbroek 		dns_name_init(tkey->creator, NULL);
398*00b67f09SDavid van Moolenbroek 		ret = dns_name_dup(creator, mctx, tkey->creator);
399*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS) {
400*00b67f09SDavid van Moolenbroek 			isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
401*00b67f09SDavid van Moolenbroek 			goto cleanup_algorithm;
402*00b67f09SDavid van Moolenbroek 		}
403*00b67f09SDavid van Moolenbroek 	} else
404*00b67f09SDavid van Moolenbroek 		tkey->creator = NULL;
405*00b67f09SDavid van Moolenbroek 
406*00b67f09SDavid van Moolenbroek 	tkey->key = NULL;
407*00b67f09SDavid van Moolenbroek 	if (dstkey != NULL)
408*00b67f09SDavid van Moolenbroek 		dst_key_attach(dstkey, &tkey->key);
409*00b67f09SDavid van Moolenbroek 	tkey->ring = ring;
410*00b67f09SDavid van Moolenbroek 
411*00b67f09SDavid van Moolenbroek 	if (key != NULL)
412*00b67f09SDavid van Moolenbroek 		refs = 1;
413*00b67f09SDavid van Moolenbroek 	if (ring != NULL)
414*00b67f09SDavid van Moolenbroek 		refs++;
415*00b67f09SDavid van Moolenbroek 	ret = isc_refcount_init(&tkey->refs, refs);
416*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
417*00b67f09SDavid van Moolenbroek 		goto cleanup_creator;
418*00b67f09SDavid van Moolenbroek 
419*00b67f09SDavid van Moolenbroek 	tkey->generated = generated;
420*00b67f09SDavid van Moolenbroek 	tkey->inception = inception;
421*00b67f09SDavid van Moolenbroek 	tkey->expire = expire;
422*00b67f09SDavid van Moolenbroek 	tkey->mctx = NULL;
423*00b67f09SDavid van Moolenbroek 	isc_mem_attach(mctx, &tkey->mctx);
424*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(tkey, link);
425*00b67f09SDavid van Moolenbroek 
426*00b67f09SDavid van Moolenbroek 	tkey->magic = TSIG_MAGIC;
427*00b67f09SDavid van Moolenbroek 
428*00b67f09SDavid van Moolenbroek 	if (ring != NULL) {
429*00b67f09SDavid van Moolenbroek 		ret = keyring_add(ring, name, tkey);
430*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
431*00b67f09SDavid van Moolenbroek 			goto cleanup_refs;
432*00b67f09SDavid van Moolenbroek 	}
433*00b67f09SDavid van Moolenbroek 
434*00b67f09SDavid van Moolenbroek 	/*
435*00b67f09SDavid van Moolenbroek 	 * Ignore this if it's a GSS key, since the key size is meaningless.
436*00b67f09SDavid van Moolenbroek 	 */
437*00b67f09SDavid van Moolenbroek 	if (dstkey != NULL && dst_key_size(dstkey) < 64 &&
438*00b67f09SDavid van Moolenbroek 	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME) &&
439*00b67f09SDavid van Moolenbroek 	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
440*00b67f09SDavid van Moolenbroek 		char namestr[DNS_NAME_FORMATSIZE];
441*00b67f09SDavid van Moolenbroek 		dns_name_format(name, namestr, sizeof(namestr));
442*00b67f09SDavid van Moolenbroek 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
443*00b67f09SDavid van Moolenbroek 			      DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
444*00b67f09SDavid van Moolenbroek 			      "the key '%s' is too short to be secure",
445*00b67f09SDavid van Moolenbroek 			      namestr);
446*00b67f09SDavid van Moolenbroek 	}
447*00b67f09SDavid van Moolenbroek 
448*00b67f09SDavid van Moolenbroek 	if (key != NULL)
449*00b67f09SDavid van Moolenbroek 		*key = tkey;
450*00b67f09SDavid van Moolenbroek 
451*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
452*00b67f09SDavid van Moolenbroek 
453*00b67f09SDavid van Moolenbroek  cleanup_refs:
454*00b67f09SDavid van Moolenbroek 	tkey->magic = 0;
455*00b67f09SDavid van Moolenbroek 	while (refs-- > 0)
456*00b67f09SDavid van Moolenbroek 		isc_refcount_decrement(&tkey->refs, NULL);
457*00b67f09SDavid van Moolenbroek 	isc_refcount_destroy(&tkey->refs);
458*00b67f09SDavid van Moolenbroek  cleanup_creator:
459*00b67f09SDavid van Moolenbroek 	if (tkey->key != NULL)
460*00b67f09SDavid van Moolenbroek 		dst_key_free(&tkey->key);
461*00b67f09SDavid van Moolenbroek 	if (tkey->creator != NULL) {
462*00b67f09SDavid van Moolenbroek 		dns_name_free(tkey->creator, mctx);
463*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
464*00b67f09SDavid van Moolenbroek 	}
465*00b67f09SDavid van Moolenbroek  cleanup_algorithm:
466*00b67f09SDavid van Moolenbroek 	if (algname_is_allocated(tkey->algorithm)) {
467*00b67f09SDavid van Moolenbroek 		if (dns_name_dynamic(tkey->algorithm))
468*00b67f09SDavid van Moolenbroek 			dns_name_free(tkey->algorithm, mctx);
469*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t));
470*00b67f09SDavid van Moolenbroek 	}
471*00b67f09SDavid van Moolenbroek  cleanup_name:
472*00b67f09SDavid van Moolenbroek 	dns_name_free(&tkey->name, mctx);
473*00b67f09SDavid van Moolenbroek  cleanup_key:
474*00b67f09SDavid van Moolenbroek 	isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
475*00b67f09SDavid van Moolenbroek 
476*00b67f09SDavid van Moolenbroek 	return (ret);
477*00b67f09SDavid van Moolenbroek }
478*00b67f09SDavid van Moolenbroek 
479*00b67f09SDavid van Moolenbroek /*
480*00b67f09SDavid van Moolenbroek  * Find a few nodes to destroy if possible.
481*00b67f09SDavid van Moolenbroek  */
482*00b67f09SDavid van Moolenbroek static void
cleanup_ring(dns_tsig_keyring_t * ring)483*00b67f09SDavid van Moolenbroek cleanup_ring(dns_tsig_keyring_t *ring)
484*00b67f09SDavid van Moolenbroek {
485*00b67f09SDavid van Moolenbroek 	isc_result_t result;
486*00b67f09SDavid van Moolenbroek 	dns_rbtnodechain_t chain;
487*00b67f09SDavid van Moolenbroek 	dns_name_t foundname;
488*00b67f09SDavid van Moolenbroek 	dns_fixedname_t fixedorigin;
489*00b67f09SDavid van Moolenbroek 	dns_name_t *origin;
490*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
491*00b67f09SDavid van Moolenbroek 	dns_rbtnode_t *node;
492*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *tkey;
493*00b67f09SDavid van Moolenbroek 
494*00b67f09SDavid van Moolenbroek 	/*
495*00b67f09SDavid van Moolenbroek 	 * Start up a new iterator each time.
496*00b67f09SDavid van Moolenbroek 	 */
497*00b67f09SDavid van Moolenbroek 	isc_stdtime_get(&now);
498*00b67f09SDavid van Moolenbroek 	dns_name_init(&foundname, NULL);
499*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fixedorigin);
500*00b67f09SDavid van Moolenbroek 	origin = dns_fixedname_name(&fixedorigin);
501*00b67f09SDavid van Moolenbroek 
502*00b67f09SDavid van Moolenbroek  again:
503*00b67f09SDavid van Moolenbroek 	dns_rbtnodechain_init(&chain, ring->mctx);
504*00b67f09SDavid van Moolenbroek 	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
505*00b67f09SDavid van Moolenbroek 					origin);
506*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
507*00b67f09SDavid van Moolenbroek 		dns_rbtnodechain_invalidate(&chain);
508*00b67f09SDavid van Moolenbroek 		return;
509*00b67f09SDavid van Moolenbroek 	}
510*00b67f09SDavid van Moolenbroek 
511*00b67f09SDavid van Moolenbroek 	for (;;) {
512*00b67f09SDavid van Moolenbroek 		node = NULL;
513*00b67f09SDavid van Moolenbroek 		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
514*00b67f09SDavid van Moolenbroek 		tkey = node->data;
515*00b67f09SDavid van Moolenbroek 		if (tkey != NULL) {
516*00b67f09SDavid van Moolenbroek 			if (tkey->generated
517*00b67f09SDavid van Moolenbroek 			    && isc_refcount_current(&tkey->refs) == 1
518*00b67f09SDavid van Moolenbroek 			    && tkey->inception != tkey->expire
519*00b67f09SDavid van Moolenbroek 			    && tkey->expire < now) {
520*00b67f09SDavid van Moolenbroek 				tsig_log(tkey, 2, "tsig expire: deleting");
521*00b67f09SDavid van Moolenbroek 				/* delete the key */
522*00b67f09SDavid van Moolenbroek 				dns_rbtnodechain_invalidate(&chain);
523*00b67f09SDavid van Moolenbroek 				remove_fromring(tkey);
524*00b67f09SDavid van Moolenbroek 				goto again;
525*00b67f09SDavid van Moolenbroek 			}
526*00b67f09SDavid van Moolenbroek 		}
527*00b67f09SDavid van Moolenbroek 		result = dns_rbtnodechain_next(&chain, &foundname,
528*00b67f09SDavid van Moolenbroek 					       origin);
529*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
530*00b67f09SDavid van Moolenbroek 			dns_rbtnodechain_invalidate(&chain);
531*00b67f09SDavid van Moolenbroek 			return;
532*00b67f09SDavid van Moolenbroek 		}
533*00b67f09SDavid van Moolenbroek 	}
534*00b67f09SDavid van Moolenbroek }
535*00b67f09SDavid van Moolenbroek 
536*00b67f09SDavid van Moolenbroek static void
destroyring(dns_tsig_keyring_t * ring)537*00b67f09SDavid van Moolenbroek destroyring(dns_tsig_keyring_t *ring) {
538*00b67f09SDavid van Moolenbroek 	dns_rbt_destroy(&ring->keys);
539*00b67f09SDavid van Moolenbroek 	isc_rwlock_destroy(&ring->lock);
540*00b67f09SDavid van Moolenbroek 	isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t));
541*00b67f09SDavid van Moolenbroek }
542*00b67f09SDavid van Moolenbroek 
543*00b67f09SDavid van Moolenbroek static unsigned int
dst_alg_fromname(dns_name_t * algorithm)544*00b67f09SDavid van Moolenbroek dst_alg_fromname(dns_name_t *algorithm) {
545*00b67f09SDavid van Moolenbroek 	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
546*00b67f09SDavid van Moolenbroek 		return (DST_ALG_HMACMD5);
547*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
548*00b67f09SDavid van Moolenbroek 		return (DST_ALG_HMACSHA1);
549*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
550*00b67f09SDavid van Moolenbroek 		return (DST_ALG_HMACSHA224);
551*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
552*00b67f09SDavid van Moolenbroek 		return (DST_ALG_HMACSHA256);
553*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
554*00b67f09SDavid van Moolenbroek 		return (DST_ALG_HMACSHA384);
555*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
556*00b67f09SDavid van Moolenbroek 		return (DST_ALG_HMACSHA512);
557*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
558*00b67f09SDavid van Moolenbroek 		return (DST_ALG_GSSAPI);
559*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
560*00b67f09SDavid van Moolenbroek 		return (DST_ALG_GSSAPI);
561*00b67f09SDavid van Moolenbroek 	} else
562*00b67f09SDavid van Moolenbroek 		return (0);
563*00b67f09SDavid van Moolenbroek }
564*00b67f09SDavid van Moolenbroek 
565*00b67f09SDavid van Moolenbroek static isc_result_t
restore_key(dns_tsig_keyring_t * ring,isc_stdtime_t now,FILE * fp)566*00b67f09SDavid van Moolenbroek restore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) {
567*00b67f09SDavid van Moolenbroek 	dst_key_t *dstkey = NULL;
568*00b67f09SDavid van Moolenbroek 	char namestr[1024];
569*00b67f09SDavid van Moolenbroek 	char creatorstr[1024];
570*00b67f09SDavid van Moolenbroek 	char algorithmstr[1024];
571*00b67f09SDavid van Moolenbroek 	char keystr[4096];
572*00b67f09SDavid van Moolenbroek 	unsigned int inception, expire;
573*00b67f09SDavid van Moolenbroek 	int n;
574*00b67f09SDavid van Moolenbroek 	isc_buffer_t b;
575*00b67f09SDavid van Moolenbroek 	dns_name_t *name, *creator, *algorithm;
576*00b67f09SDavid van Moolenbroek 	dns_fixedname_t fname, fcreator, falgorithm;
577*00b67f09SDavid van Moolenbroek 	isc_result_t result;
578*00b67f09SDavid van Moolenbroek 	unsigned int dstalg;
579*00b67f09SDavid van Moolenbroek 
580*00b67f09SDavid van Moolenbroek 	n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr,
581*00b67f09SDavid van Moolenbroek 		   creatorstr, &inception, &expire, algorithmstr, keystr);
582*00b67f09SDavid van Moolenbroek 	if (n == EOF)
583*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMORE);
584*00b67f09SDavid van Moolenbroek 	if (n != 6)
585*00b67f09SDavid van Moolenbroek 		return (ISC_R_FAILURE);
586*00b67f09SDavid van Moolenbroek 
587*00b67f09SDavid van Moolenbroek 	if (isc_serial_lt(expire, now))
588*00b67f09SDavid van Moolenbroek 		return (DNS_R_EXPIRED);
589*00b67f09SDavid van Moolenbroek 
590*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fname);
591*00b67f09SDavid van Moolenbroek 	name = dns_fixedname_name(&fname);
592*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, namestr, strlen(namestr));
593*00b67f09SDavid van Moolenbroek 	isc_buffer_add(&b, strlen(namestr));
594*00b67f09SDavid van Moolenbroek 	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
595*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
596*00b67f09SDavid van Moolenbroek 		return (result);
597*00b67f09SDavid van Moolenbroek 
598*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fcreator);
599*00b67f09SDavid van Moolenbroek 	creator = dns_fixedname_name(&fcreator);
600*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, creatorstr, strlen(creatorstr));
601*00b67f09SDavid van Moolenbroek 	isc_buffer_add(&b, strlen(creatorstr));
602*00b67f09SDavid van Moolenbroek 	result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL);
603*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
604*00b67f09SDavid van Moolenbroek 		return (result);
605*00b67f09SDavid van Moolenbroek 
606*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&falgorithm);
607*00b67f09SDavid van Moolenbroek 	algorithm = dns_fixedname_name(&falgorithm);
608*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&b, algorithmstr, strlen(algorithmstr));
609*00b67f09SDavid van Moolenbroek 	isc_buffer_add(&b, strlen(algorithmstr));
610*00b67f09SDavid van Moolenbroek 	result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL);
611*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
612*00b67f09SDavid van Moolenbroek 		return (result);
613*00b67f09SDavid van Moolenbroek 
614*00b67f09SDavid van Moolenbroek 	dstalg = dst_alg_fromname(algorithm);
615*00b67f09SDavid van Moolenbroek 	if (dstalg == 0)
616*00b67f09SDavid van Moolenbroek 		return (DNS_R_BADALG);
617*00b67f09SDavid van Moolenbroek 
618*00b67f09SDavid van Moolenbroek 	result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY,
619*00b67f09SDavid van Moolenbroek 				 DNS_KEYPROTO_DNSSEC, dns_rdataclass_in,
620*00b67f09SDavid van Moolenbroek 				 ring->mctx, keystr, &dstkey);
621*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
622*00b67f09SDavid van Moolenbroek 		return (result);
623*00b67f09SDavid van Moolenbroek 
624*00b67f09SDavid van Moolenbroek 	result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
625*00b67f09SDavid van Moolenbroek 					   ISC_TRUE, creator, inception,
626*00b67f09SDavid van Moolenbroek 					   expire, ring->mctx, ring, NULL);
627*00b67f09SDavid van Moolenbroek 	if (dstkey != NULL)
628*00b67f09SDavid van Moolenbroek 		dst_key_free(&dstkey);
629*00b67f09SDavid van Moolenbroek 	return (result);
630*00b67f09SDavid van Moolenbroek }
631*00b67f09SDavid van Moolenbroek 
632*00b67f09SDavid van Moolenbroek static void
dump_key(dns_tsigkey_t * tkey,FILE * fp)633*00b67f09SDavid van Moolenbroek dump_key(dns_tsigkey_t *tkey, FILE *fp) {
634*00b67f09SDavid van Moolenbroek 	char *buffer = NULL;
635*00b67f09SDavid van Moolenbroek 	int length = 0;
636*00b67f09SDavid van Moolenbroek 	char namestr[DNS_NAME_FORMATSIZE];
637*00b67f09SDavid van Moolenbroek 	char creatorstr[DNS_NAME_FORMATSIZE];
638*00b67f09SDavid van Moolenbroek 	char algorithmstr[DNS_NAME_FORMATSIZE];
639*00b67f09SDavid van Moolenbroek 	isc_result_t result;
640*00b67f09SDavid van Moolenbroek 
641*00b67f09SDavid van Moolenbroek 	REQUIRE(tkey != NULL);
642*00b67f09SDavid van Moolenbroek 	REQUIRE(fp != NULL);
643*00b67f09SDavid van Moolenbroek 
644*00b67f09SDavid van Moolenbroek 	dns_name_format(&tkey->name, namestr, sizeof(namestr));
645*00b67f09SDavid van Moolenbroek 	dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr));
646*00b67f09SDavid van Moolenbroek 	dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr));
647*00b67f09SDavid van Moolenbroek 	result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length);
648*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
649*00b67f09SDavid van Moolenbroek 		fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr,
650*00b67f09SDavid van Moolenbroek 			tkey->inception, tkey->expire, algorithmstr,
651*00b67f09SDavid van Moolenbroek 			length, buffer);
652*00b67f09SDavid van Moolenbroek 	if (buffer != NULL)
653*00b67f09SDavid van Moolenbroek 		isc_mem_put(tkey->mctx, buffer, length);
654*00b67f09SDavid van Moolenbroek }
655*00b67f09SDavid van Moolenbroek 
656*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t ** ringp,FILE * fp)657*00b67f09SDavid van Moolenbroek dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) {
658*00b67f09SDavid van Moolenbroek 	isc_result_t result;
659*00b67f09SDavid van Moolenbroek 	dns_rbtnodechain_t chain;
660*00b67f09SDavid van Moolenbroek 	dns_name_t foundname;
661*00b67f09SDavid van Moolenbroek 	dns_fixedname_t fixedorigin;
662*00b67f09SDavid van Moolenbroek 	dns_name_t *origin;
663*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
664*00b67f09SDavid van Moolenbroek 	dns_rbtnode_t *node;
665*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *tkey;
666*00b67f09SDavid van Moolenbroek 	dns_tsig_keyring_t *ring;
667*00b67f09SDavid van Moolenbroek 	unsigned int references;
668*00b67f09SDavid van Moolenbroek 
669*00b67f09SDavid van Moolenbroek 	REQUIRE(ringp != NULL && *ringp != NULL);
670*00b67f09SDavid van Moolenbroek 
671*00b67f09SDavid van Moolenbroek 	ring = *ringp;
672*00b67f09SDavid van Moolenbroek 	*ringp = NULL;
673*00b67f09SDavid van Moolenbroek 
674*00b67f09SDavid van Moolenbroek 	RWLOCK(&ring->lock, isc_rwlocktype_write);
675*00b67f09SDavid van Moolenbroek 	INSIST(ring->references > 0);
676*00b67f09SDavid van Moolenbroek 	ring->references--;
677*00b67f09SDavid van Moolenbroek 	references = ring->references;
678*00b67f09SDavid van Moolenbroek 	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
679*00b67f09SDavid van Moolenbroek 
680*00b67f09SDavid van Moolenbroek 	if (references != 0)
681*00b67f09SDavid van Moolenbroek 		return (DNS_R_CONTINUE);
682*00b67f09SDavid van Moolenbroek 
683*00b67f09SDavid van Moolenbroek 	isc_stdtime_get(&now);
684*00b67f09SDavid van Moolenbroek 	dns_name_init(&foundname, NULL);
685*00b67f09SDavid van Moolenbroek 	dns_fixedname_init(&fixedorigin);
686*00b67f09SDavid van Moolenbroek 	origin = dns_fixedname_name(&fixedorigin);
687*00b67f09SDavid van Moolenbroek 	dns_rbtnodechain_init(&chain, ring->mctx);
688*00b67f09SDavid van Moolenbroek 	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
689*00b67f09SDavid van Moolenbroek 					origin);
690*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
691*00b67f09SDavid van Moolenbroek 		dns_rbtnodechain_invalidate(&chain);
692*00b67f09SDavid van Moolenbroek 		goto destroy;
693*00b67f09SDavid van Moolenbroek 	}
694*00b67f09SDavid van Moolenbroek 
695*00b67f09SDavid van Moolenbroek 	for (;;) {
696*00b67f09SDavid van Moolenbroek 		node = NULL;
697*00b67f09SDavid van Moolenbroek 		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
698*00b67f09SDavid van Moolenbroek 		tkey = node->data;
699*00b67f09SDavid van Moolenbroek 		if (tkey != NULL && tkey->generated && tkey->expire >= now)
700*00b67f09SDavid van Moolenbroek 			dump_key(tkey, fp);
701*00b67f09SDavid van Moolenbroek 		result = dns_rbtnodechain_next(&chain, &foundname,
702*00b67f09SDavid van Moolenbroek 					       origin);
703*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
704*00b67f09SDavid van Moolenbroek 			dns_rbtnodechain_invalidate(&chain);
705*00b67f09SDavid van Moolenbroek 			if (result == ISC_R_NOMORE)
706*00b67f09SDavid van Moolenbroek 				result = ISC_R_SUCCESS;
707*00b67f09SDavid van Moolenbroek 			goto destroy;
708*00b67f09SDavid van Moolenbroek 		}
709*00b67f09SDavid van Moolenbroek 	}
710*00b67f09SDavid van Moolenbroek 
711*00b67f09SDavid van Moolenbroek  destroy:
712*00b67f09SDavid van Moolenbroek 	destroyring(ring);
713*00b67f09SDavid van Moolenbroek 	return (result);
714*00b67f09SDavid van Moolenbroek }
715*00b67f09SDavid van Moolenbroek 
716*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsigkey_create(dns_name_t * name,dns_name_t * algorithm,unsigned char * secret,int length,isc_boolean_t generated,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)717*00b67f09SDavid van Moolenbroek dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
718*00b67f09SDavid van Moolenbroek 		   unsigned char *secret, int length, isc_boolean_t generated,
719*00b67f09SDavid van Moolenbroek 		   dns_name_t *creator, isc_stdtime_t inception,
720*00b67f09SDavid van Moolenbroek 		   isc_stdtime_t expire, isc_mem_t *mctx,
721*00b67f09SDavid van Moolenbroek 		   dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
722*00b67f09SDavid van Moolenbroek {
723*00b67f09SDavid van Moolenbroek 	dst_key_t *dstkey = NULL;
724*00b67f09SDavid van Moolenbroek 	isc_result_t result;
725*00b67f09SDavid van Moolenbroek 
726*00b67f09SDavid van Moolenbroek 	REQUIRE(length >= 0);
727*00b67f09SDavid van Moolenbroek 	if (length > 0)
728*00b67f09SDavid van Moolenbroek 		REQUIRE(secret != NULL);
729*00b67f09SDavid van Moolenbroek 
730*00b67f09SDavid van Moolenbroek 	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
731*00b67f09SDavid van Moolenbroek 		if (secret != NULL) {
732*00b67f09SDavid van Moolenbroek 			isc_buffer_t b;
733*00b67f09SDavid van Moolenbroek 
734*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, secret, length);
735*00b67f09SDavid van Moolenbroek 			isc_buffer_add(&b, length);
736*00b67f09SDavid van Moolenbroek 			result = dst_key_frombuffer(name, DST_ALG_HMACMD5,
737*00b67f09SDavid van Moolenbroek 						    DNS_KEYOWNER_ENTITY,
738*00b67f09SDavid van Moolenbroek 						    DNS_KEYPROTO_DNSSEC,
739*00b67f09SDavid van Moolenbroek 						    dns_rdataclass_in,
740*00b67f09SDavid van Moolenbroek 						    &b, mctx, &dstkey);
741*00b67f09SDavid van Moolenbroek 				if (result != ISC_R_SUCCESS)
742*00b67f09SDavid van Moolenbroek 					return (result);
743*00b67f09SDavid van Moolenbroek 		}
744*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
745*00b67f09SDavid van Moolenbroek 		if (secret != NULL) {
746*00b67f09SDavid van Moolenbroek 			isc_buffer_t b;
747*00b67f09SDavid van Moolenbroek 
748*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, secret, length);
749*00b67f09SDavid van Moolenbroek 			isc_buffer_add(&b, length);
750*00b67f09SDavid van Moolenbroek 			result = dst_key_frombuffer(name, DST_ALG_HMACSHA1,
751*00b67f09SDavid van Moolenbroek 						    DNS_KEYOWNER_ENTITY,
752*00b67f09SDavid van Moolenbroek 						    DNS_KEYPROTO_DNSSEC,
753*00b67f09SDavid van Moolenbroek 						    dns_rdataclass_in,
754*00b67f09SDavid van Moolenbroek 						    &b, mctx, &dstkey);
755*00b67f09SDavid van Moolenbroek 				if (result != ISC_R_SUCCESS)
756*00b67f09SDavid van Moolenbroek 					return (result);
757*00b67f09SDavid van Moolenbroek 		}
758*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
759*00b67f09SDavid van Moolenbroek 		if (secret != NULL) {
760*00b67f09SDavid van Moolenbroek 			isc_buffer_t b;
761*00b67f09SDavid van Moolenbroek 
762*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, secret, length);
763*00b67f09SDavid van Moolenbroek 			isc_buffer_add(&b, length);
764*00b67f09SDavid van Moolenbroek 			result = dst_key_frombuffer(name, DST_ALG_HMACSHA224,
765*00b67f09SDavid van Moolenbroek 						    DNS_KEYOWNER_ENTITY,
766*00b67f09SDavid van Moolenbroek 						    DNS_KEYPROTO_DNSSEC,
767*00b67f09SDavid van Moolenbroek 						    dns_rdataclass_in,
768*00b67f09SDavid van Moolenbroek 						    &b, mctx, &dstkey);
769*00b67f09SDavid van Moolenbroek 				if (result != ISC_R_SUCCESS)
770*00b67f09SDavid van Moolenbroek 					return (result);
771*00b67f09SDavid van Moolenbroek 		}
772*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
773*00b67f09SDavid van Moolenbroek 		if (secret != NULL) {
774*00b67f09SDavid van Moolenbroek 			isc_buffer_t b;
775*00b67f09SDavid van Moolenbroek 
776*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, secret, length);
777*00b67f09SDavid van Moolenbroek 			isc_buffer_add(&b, length);
778*00b67f09SDavid van Moolenbroek 			result = dst_key_frombuffer(name, DST_ALG_HMACSHA256,
779*00b67f09SDavid van Moolenbroek 						    DNS_KEYOWNER_ENTITY,
780*00b67f09SDavid van Moolenbroek 						    DNS_KEYPROTO_DNSSEC,
781*00b67f09SDavid van Moolenbroek 						    dns_rdataclass_in,
782*00b67f09SDavid van Moolenbroek 						    &b, mctx, &dstkey);
783*00b67f09SDavid van Moolenbroek 				if (result != ISC_R_SUCCESS)
784*00b67f09SDavid van Moolenbroek 					return (result);
785*00b67f09SDavid van Moolenbroek 		}
786*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
787*00b67f09SDavid van Moolenbroek 		if (secret != NULL) {
788*00b67f09SDavid van Moolenbroek 			isc_buffer_t b;
789*00b67f09SDavid van Moolenbroek 
790*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, secret, length);
791*00b67f09SDavid van Moolenbroek 			isc_buffer_add(&b, length);
792*00b67f09SDavid van Moolenbroek 			result = dst_key_frombuffer(name, DST_ALG_HMACSHA384,
793*00b67f09SDavid van Moolenbroek 						    DNS_KEYOWNER_ENTITY,
794*00b67f09SDavid van Moolenbroek 						    DNS_KEYPROTO_DNSSEC,
795*00b67f09SDavid van Moolenbroek 						    dns_rdataclass_in,
796*00b67f09SDavid van Moolenbroek 						    &b, mctx, &dstkey);
797*00b67f09SDavid van Moolenbroek 				if (result != ISC_R_SUCCESS)
798*00b67f09SDavid van Moolenbroek 					return (result);
799*00b67f09SDavid van Moolenbroek 		}
800*00b67f09SDavid van Moolenbroek 	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
801*00b67f09SDavid van Moolenbroek 		if (secret != NULL) {
802*00b67f09SDavid van Moolenbroek 			isc_buffer_t b;
803*00b67f09SDavid van Moolenbroek 
804*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&b, secret, length);
805*00b67f09SDavid van Moolenbroek 			isc_buffer_add(&b, length);
806*00b67f09SDavid van Moolenbroek 			result = dst_key_frombuffer(name, DST_ALG_HMACSHA512,
807*00b67f09SDavid van Moolenbroek 						    DNS_KEYOWNER_ENTITY,
808*00b67f09SDavid van Moolenbroek 						    DNS_KEYPROTO_DNSSEC,
809*00b67f09SDavid van Moolenbroek 						    dns_rdataclass_in,
810*00b67f09SDavid van Moolenbroek 						    &b, mctx, &dstkey);
811*00b67f09SDavid van Moolenbroek 				if (result != ISC_R_SUCCESS)
812*00b67f09SDavid van Moolenbroek 					return (result);
813*00b67f09SDavid van Moolenbroek 		}
814*00b67f09SDavid van Moolenbroek 	} else if (length > 0)
815*00b67f09SDavid van Moolenbroek 		return (DNS_R_BADALG);
816*00b67f09SDavid van Moolenbroek 
817*00b67f09SDavid van Moolenbroek 	result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
818*00b67f09SDavid van Moolenbroek 					   generated, creator,
819*00b67f09SDavid van Moolenbroek 					   inception, expire, mctx, ring, key);
820*00b67f09SDavid van Moolenbroek 	if (dstkey != NULL)
821*00b67f09SDavid van Moolenbroek 		dst_key_free(&dstkey);
822*00b67f09SDavid van Moolenbroek 	return (result);
823*00b67f09SDavid van Moolenbroek }
824*00b67f09SDavid van Moolenbroek 
825*00b67f09SDavid van Moolenbroek void
dns_tsigkey_attach(dns_tsigkey_t * source,dns_tsigkey_t ** targetp)826*00b67f09SDavid van Moolenbroek dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
827*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_TSIG_KEY(source));
828*00b67f09SDavid van Moolenbroek 	REQUIRE(targetp != NULL && *targetp == NULL);
829*00b67f09SDavid van Moolenbroek 
830*00b67f09SDavid van Moolenbroek 	isc_refcount_increment(&source->refs, NULL);
831*00b67f09SDavid van Moolenbroek 	*targetp = source;
832*00b67f09SDavid van Moolenbroek }
833*00b67f09SDavid van Moolenbroek 
834*00b67f09SDavid van Moolenbroek static void
tsigkey_free(dns_tsigkey_t * key)835*00b67f09SDavid van Moolenbroek tsigkey_free(dns_tsigkey_t *key) {
836*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_TSIG_KEY(key));
837*00b67f09SDavid van Moolenbroek 
838*00b67f09SDavid van Moolenbroek 	key->magic = 0;
839*00b67f09SDavid van Moolenbroek 	dns_name_free(&key->name, key->mctx);
840*00b67f09SDavid van Moolenbroek 	if (algname_is_allocated(key->algorithm)) {
841*00b67f09SDavid van Moolenbroek 		dns_name_free(key->algorithm, key->mctx);
842*00b67f09SDavid van Moolenbroek 		isc_mem_put(key->mctx, key->algorithm, sizeof(dns_name_t));
843*00b67f09SDavid van Moolenbroek 	}
844*00b67f09SDavid van Moolenbroek 	if (key->key != NULL)
845*00b67f09SDavid van Moolenbroek 		dst_key_free(&key->key);
846*00b67f09SDavid van Moolenbroek 	if (key->creator != NULL) {
847*00b67f09SDavid van Moolenbroek 		dns_name_free(key->creator, key->mctx);
848*00b67f09SDavid van Moolenbroek 		isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
849*00b67f09SDavid van Moolenbroek 	}
850*00b67f09SDavid van Moolenbroek 	isc_refcount_destroy(&key->refs);
851*00b67f09SDavid van Moolenbroek 	isc_mem_putanddetach(&key->mctx, key, sizeof(dns_tsigkey_t));
852*00b67f09SDavid van Moolenbroek }
853*00b67f09SDavid van Moolenbroek 
854*00b67f09SDavid van Moolenbroek void
dns_tsigkey_detach(dns_tsigkey_t ** keyp)855*00b67f09SDavid van Moolenbroek dns_tsigkey_detach(dns_tsigkey_t **keyp) {
856*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *key;
857*00b67f09SDavid van Moolenbroek 	unsigned int refs;
858*00b67f09SDavid van Moolenbroek 
859*00b67f09SDavid van Moolenbroek 	REQUIRE(keyp != NULL);
860*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_TSIG_KEY(*keyp));
861*00b67f09SDavid van Moolenbroek 
862*00b67f09SDavid van Moolenbroek 	key = *keyp;
863*00b67f09SDavid van Moolenbroek 	isc_refcount_decrement(&key->refs, &refs);
864*00b67f09SDavid van Moolenbroek 
865*00b67f09SDavid van Moolenbroek 	if (refs == 0)
866*00b67f09SDavid van Moolenbroek 		tsigkey_free(key);
867*00b67f09SDavid van Moolenbroek 
868*00b67f09SDavid van Moolenbroek 	*keyp = NULL;
869*00b67f09SDavid van Moolenbroek }
870*00b67f09SDavid van Moolenbroek 
871*00b67f09SDavid van Moolenbroek void
dns_tsigkey_setdeleted(dns_tsigkey_t * key)872*00b67f09SDavid van Moolenbroek dns_tsigkey_setdeleted(dns_tsigkey_t *key) {
873*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_TSIG_KEY(key));
874*00b67f09SDavid van Moolenbroek 	REQUIRE(key->ring != NULL);
875*00b67f09SDavid van Moolenbroek 
876*00b67f09SDavid van Moolenbroek 	RWLOCK(&key->ring->lock, isc_rwlocktype_write);
877*00b67f09SDavid van Moolenbroek 	remove_fromring(key);
878*00b67f09SDavid van Moolenbroek 	RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
879*00b67f09SDavid van Moolenbroek }
880*00b67f09SDavid van Moolenbroek 
881*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsig_sign(dns_message_t * msg)882*00b67f09SDavid van Moolenbroek dns_tsig_sign(dns_message_t *msg) {
883*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *key;
884*00b67f09SDavid van Moolenbroek 	dns_rdata_any_tsig_t tsig, querytsig;
885*00b67f09SDavid van Moolenbroek 	unsigned char data[128];
886*00b67f09SDavid van Moolenbroek 	isc_buffer_t databuf, sigbuf;
887*00b67f09SDavid van Moolenbroek 	isc_buffer_t *dynbuf;
888*00b67f09SDavid van Moolenbroek 	dns_name_t *owner;
889*00b67f09SDavid van Moolenbroek 	dns_rdata_t *rdata = NULL;
890*00b67f09SDavid van Moolenbroek 	dns_rdatalist_t *datalist;
891*00b67f09SDavid van Moolenbroek 	dns_rdataset_t *dataset;
892*00b67f09SDavid van Moolenbroek 	isc_region_t r;
893*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
894*00b67f09SDavid van Moolenbroek 	isc_mem_t *mctx;
895*00b67f09SDavid van Moolenbroek 	dst_context_t *ctx = NULL;
896*00b67f09SDavid van Moolenbroek 	isc_result_t ret;
897*00b67f09SDavid van Moolenbroek 	unsigned char badtimedata[BADTIMELEN];
898*00b67f09SDavid van Moolenbroek 	unsigned int sigsize = 0;
899*00b67f09SDavid van Moolenbroek 	isc_boolean_t response = is_response(msg);
900*00b67f09SDavid van Moolenbroek 
901*00b67f09SDavid van Moolenbroek 	REQUIRE(msg != NULL);
902*00b67f09SDavid van Moolenbroek 	REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg)));
903*00b67f09SDavid van Moolenbroek 
904*00b67f09SDavid van Moolenbroek 	/*
905*00b67f09SDavid van Moolenbroek 	 * If this is a response, there should be a query tsig.
906*00b67f09SDavid van Moolenbroek 	 */
907*00b67f09SDavid van Moolenbroek 	if (response && msg->querytsig == NULL)
908*00b67f09SDavid van Moolenbroek 		return (DNS_R_EXPECTEDTSIG);
909*00b67f09SDavid van Moolenbroek 
910*00b67f09SDavid van Moolenbroek 	dynbuf = NULL;
911*00b67f09SDavid van Moolenbroek 
912*00b67f09SDavid van Moolenbroek 	mctx = msg->mctx;
913*00b67f09SDavid van Moolenbroek 	key = dns_message_gettsigkey(msg);
914*00b67f09SDavid van Moolenbroek 
915*00b67f09SDavid van Moolenbroek 	tsig.mctx = mctx;
916*00b67f09SDavid van Moolenbroek 	tsig.common.rdclass = dns_rdataclass_any;
917*00b67f09SDavid van Moolenbroek 	tsig.common.rdtype = dns_rdatatype_tsig;
918*00b67f09SDavid van Moolenbroek 	ISC_LINK_INIT(&tsig.common, link);
919*00b67f09SDavid van Moolenbroek 	dns_name_init(&tsig.algorithm, NULL);
920*00b67f09SDavid van Moolenbroek 	dns_name_clone(key->algorithm, &tsig.algorithm);
921*00b67f09SDavid van Moolenbroek 
922*00b67f09SDavid van Moolenbroek 	isc_stdtime_get(&now);
923*00b67f09SDavid van Moolenbroek 	tsig.timesigned = now + msg->timeadjust;
924*00b67f09SDavid van Moolenbroek 	tsig.fudge = DNS_TSIG_FUDGE;
925*00b67f09SDavid van Moolenbroek 
926*00b67f09SDavid van Moolenbroek 	tsig.originalid = msg->id;
927*00b67f09SDavid van Moolenbroek 
928*00b67f09SDavid van Moolenbroek 	isc_buffer_init(&databuf, data, sizeof(data));
929*00b67f09SDavid van Moolenbroek 
930*00b67f09SDavid van Moolenbroek 	if (response)
931*00b67f09SDavid van Moolenbroek 		tsig.error = msg->querytsigstatus;
932*00b67f09SDavid van Moolenbroek 	else
933*00b67f09SDavid van Moolenbroek 		tsig.error = dns_rcode_noerror;
934*00b67f09SDavid van Moolenbroek 
935*00b67f09SDavid van Moolenbroek 	if (tsig.error != dns_tsigerror_badtime) {
936*00b67f09SDavid van Moolenbroek 		tsig.otherlen = 0;
937*00b67f09SDavid van Moolenbroek 		tsig.other = NULL;
938*00b67f09SDavid van Moolenbroek 	} else {
939*00b67f09SDavid van Moolenbroek 		isc_buffer_t otherbuf;
940*00b67f09SDavid van Moolenbroek 
941*00b67f09SDavid van Moolenbroek 		tsig.otherlen = BADTIMELEN;
942*00b67f09SDavid van Moolenbroek 		tsig.other = badtimedata;
943*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
944*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint48(&otherbuf, tsig.timesigned);
945*00b67f09SDavid van Moolenbroek 	}
946*00b67f09SDavid van Moolenbroek 
947*00b67f09SDavid van Moolenbroek 	if (key->key != NULL && tsig.error != dns_tsigerror_badsig) {
948*00b67f09SDavid van Moolenbroek 		unsigned char header[DNS_MESSAGE_HEADERLEN];
949*00b67f09SDavid van Moolenbroek 		isc_buffer_t headerbuf;
950*00b67f09SDavid van Moolenbroek 		isc_uint16_t digestbits;
951*00b67f09SDavid van Moolenbroek 
952*00b67f09SDavid van Moolenbroek 		ret = dst_context_create3(key->key, mctx,
953*00b67f09SDavid van Moolenbroek 					  DNS_LOGCATEGORY_DNSSEC,
954*00b67f09SDavid van Moolenbroek 					  ISC_TRUE, &ctx);
955*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
956*00b67f09SDavid van Moolenbroek 			return (ret);
957*00b67f09SDavid van Moolenbroek 
958*00b67f09SDavid van Moolenbroek 		/*
959*00b67f09SDavid van Moolenbroek 		 * If this is a response, digest the query signature.
960*00b67f09SDavid van Moolenbroek 		 */
961*00b67f09SDavid van Moolenbroek 		if (response) {
962*00b67f09SDavid van Moolenbroek 			dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
963*00b67f09SDavid van Moolenbroek 
964*00b67f09SDavid van Moolenbroek 			ret = dns_rdataset_first(msg->querytsig);
965*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
966*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
967*00b67f09SDavid van Moolenbroek 			dns_rdataset_current(msg->querytsig, &querytsigrdata);
968*00b67f09SDavid van Moolenbroek 			ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
969*00b67f09SDavid van Moolenbroek 						 NULL);
970*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
971*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
972*00b67f09SDavid van Moolenbroek 			isc_buffer_putuint16(&databuf, querytsig.siglen);
973*00b67f09SDavid van Moolenbroek 			if (isc_buffer_availablelength(&databuf) <
974*00b67f09SDavid van Moolenbroek 			    querytsig.siglen) {
975*00b67f09SDavid van Moolenbroek 				ret = ISC_R_NOSPACE;
976*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
977*00b67f09SDavid van Moolenbroek 			}
978*00b67f09SDavid van Moolenbroek 			isc_buffer_putmem(&databuf, querytsig.signature,
979*00b67f09SDavid van Moolenbroek 					  querytsig.siglen);
980*00b67f09SDavid van Moolenbroek 			isc_buffer_usedregion(&databuf, &r);
981*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(ctx, &r);
982*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
983*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
984*00b67f09SDavid van Moolenbroek 		}
985*00b67f09SDavid van Moolenbroek #if defined(__clang__)  && \
986*00b67f09SDavid van Moolenbroek        ( __clang_major__ < 3 || \
987*00b67f09SDavid van Moolenbroek 	(__clang_major__ == 3 && __clang_minor__ < 2) || \
988*00b67f09SDavid van Moolenbroek 	(__clang_major__ == 4 && __clang_minor__ < 2))
989*00b67f09SDavid van Moolenbroek 	/* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */
990*00b67f09SDavid van Moolenbroek 		else memset(&querytsig, 0, sizeof(querytsig));
991*00b67f09SDavid van Moolenbroek #endif
992*00b67f09SDavid van Moolenbroek 
993*00b67f09SDavid van Moolenbroek 		/*
994*00b67f09SDavid van Moolenbroek 		 * Digest the header.
995*00b67f09SDavid van Moolenbroek 		 */
996*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&headerbuf, header, sizeof(header));
997*00b67f09SDavid van Moolenbroek 		dns_message_renderheader(msg, &headerbuf);
998*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(&headerbuf, &r);
999*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1000*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1001*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1002*00b67f09SDavid van Moolenbroek 
1003*00b67f09SDavid van Moolenbroek 		/*
1004*00b67f09SDavid van Moolenbroek 		 * Digest the remainder of the message.
1005*00b67f09SDavid van Moolenbroek 		 */
1006*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(msg->buffer, &r);
1007*00b67f09SDavid van Moolenbroek 		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1008*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1009*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1010*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1011*00b67f09SDavid van Moolenbroek 
1012*00b67f09SDavid van Moolenbroek 		if (msg->tcp_continuation == 0) {
1013*00b67f09SDavid van Moolenbroek 			/*
1014*00b67f09SDavid van Moolenbroek 			 * Digest the name, class, ttl, alg.
1015*00b67f09SDavid van Moolenbroek 			 */
1016*00b67f09SDavid van Moolenbroek 			dns_name_toregion(&key->name, &r);
1017*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(ctx, &r);
1018*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1019*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
1020*00b67f09SDavid van Moolenbroek 
1021*00b67f09SDavid van Moolenbroek 			isc_buffer_clear(&databuf);
1022*00b67f09SDavid van Moolenbroek 			isc_buffer_putuint16(&databuf, dns_rdataclass_any);
1023*00b67f09SDavid van Moolenbroek 			isc_buffer_putuint32(&databuf, 0); /* ttl */
1024*00b67f09SDavid van Moolenbroek 			isc_buffer_usedregion(&databuf, &r);
1025*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(ctx, &r);
1026*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1027*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
1028*00b67f09SDavid van Moolenbroek 
1029*00b67f09SDavid van Moolenbroek 			dns_name_toregion(&tsig.algorithm, &r);
1030*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(ctx, &r);
1031*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1032*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
1033*00b67f09SDavid van Moolenbroek 
1034*00b67f09SDavid van Moolenbroek 		}
1035*00b67f09SDavid van Moolenbroek 		/* Digest the timesigned and fudge */
1036*00b67f09SDavid van Moolenbroek 		isc_buffer_clear(&databuf);
1037*00b67f09SDavid van Moolenbroek 		if (tsig.error == dns_tsigerror_badtime) {
1038*00b67f09SDavid van Moolenbroek 			INSIST(response);
1039*00b67f09SDavid van Moolenbroek 			tsig.timesigned = querytsig.timesigned;
1040*00b67f09SDavid van Moolenbroek 		}
1041*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint48(&databuf, tsig.timesigned);
1042*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&databuf, tsig.fudge);
1043*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(&databuf, &r);
1044*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1045*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1046*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1047*00b67f09SDavid van Moolenbroek 
1048*00b67f09SDavid van Moolenbroek 		if (msg->tcp_continuation == 0) {
1049*00b67f09SDavid van Moolenbroek 			/*
1050*00b67f09SDavid van Moolenbroek 			 * Digest the error and other data length.
1051*00b67f09SDavid van Moolenbroek 			 */
1052*00b67f09SDavid van Moolenbroek 			isc_buffer_clear(&databuf);
1053*00b67f09SDavid van Moolenbroek 			isc_buffer_putuint16(&databuf, tsig.error);
1054*00b67f09SDavid van Moolenbroek 			isc_buffer_putuint16(&databuf, tsig.otherlen);
1055*00b67f09SDavid van Moolenbroek 
1056*00b67f09SDavid van Moolenbroek 			isc_buffer_usedregion(&databuf, &r);
1057*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(ctx, &r);
1058*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1059*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
1060*00b67f09SDavid van Moolenbroek 
1061*00b67f09SDavid van Moolenbroek 			/*
1062*00b67f09SDavid van Moolenbroek 			 * Digest other data.
1063*00b67f09SDavid van Moolenbroek 			 */
1064*00b67f09SDavid van Moolenbroek 			if (tsig.otherlen > 0) {
1065*00b67f09SDavid van Moolenbroek 				r.length = tsig.otherlen;
1066*00b67f09SDavid van Moolenbroek 				r.base = tsig.other;
1067*00b67f09SDavid van Moolenbroek 				ret = dst_context_adddata(ctx, &r);
1068*00b67f09SDavid van Moolenbroek 				if (ret != ISC_R_SUCCESS)
1069*00b67f09SDavid van Moolenbroek 					goto cleanup_context;
1070*00b67f09SDavid van Moolenbroek 			}
1071*00b67f09SDavid van Moolenbroek 		}
1072*00b67f09SDavid van Moolenbroek 
1073*00b67f09SDavid van Moolenbroek 		ret = dst_key_sigsize(key->key, &sigsize);
1074*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1075*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1076*00b67f09SDavid van Moolenbroek 		tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
1077*00b67f09SDavid van Moolenbroek 		if (tsig.signature == NULL) {
1078*00b67f09SDavid van Moolenbroek 			ret = ISC_R_NOMEMORY;
1079*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1080*00b67f09SDavid van Moolenbroek 		}
1081*00b67f09SDavid van Moolenbroek 
1082*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&sigbuf, tsig.signature, sigsize);
1083*00b67f09SDavid van Moolenbroek 		ret = dst_context_sign(ctx, &sigbuf);
1084*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1085*00b67f09SDavid van Moolenbroek 			goto cleanup_signature;
1086*00b67f09SDavid van Moolenbroek 		dst_context_destroy(&ctx);
1087*00b67f09SDavid van Moolenbroek 		digestbits = dst_key_getbits(key->key);
1088*00b67f09SDavid van Moolenbroek 		if (digestbits != 0) {
1089*00b67f09SDavid van Moolenbroek 			unsigned int bytes = (digestbits + 1) / 8;
1090*00b67f09SDavid van Moolenbroek 			if (response && bytes < querytsig.siglen)
1091*00b67f09SDavid van Moolenbroek 				bytes = querytsig.siglen;
1092*00b67f09SDavid van Moolenbroek 			if (bytes > isc_buffer_usedlength(&sigbuf))
1093*00b67f09SDavid van Moolenbroek 				bytes = isc_buffer_usedlength(&sigbuf);
1094*00b67f09SDavid van Moolenbroek 			tsig.siglen = bytes;
1095*00b67f09SDavid van Moolenbroek 		} else
1096*00b67f09SDavid van Moolenbroek 			tsig.siglen = isc_buffer_usedlength(&sigbuf);
1097*00b67f09SDavid van Moolenbroek 	} else {
1098*00b67f09SDavid van Moolenbroek 		tsig.siglen = 0;
1099*00b67f09SDavid van Moolenbroek 		tsig.signature = NULL;
1100*00b67f09SDavid van Moolenbroek 	}
1101*00b67f09SDavid van Moolenbroek 
1102*00b67f09SDavid van Moolenbroek 	ret = dns_message_gettemprdata(msg, &rdata);
1103*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1104*00b67f09SDavid van Moolenbroek 		goto cleanup_signature;
1105*00b67f09SDavid van Moolenbroek 	ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
1106*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1107*00b67f09SDavid van Moolenbroek 		goto cleanup_rdata;
1108*00b67f09SDavid van Moolenbroek 	ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
1109*00b67f09SDavid van Moolenbroek 				   dns_rdatatype_tsig, &tsig, dynbuf);
1110*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1111*00b67f09SDavid van Moolenbroek 		goto cleanup_dynbuf;
1112*00b67f09SDavid van Moolenbroek 
1113*00b67f09SDavid van Moolenbroek 	dns_message_takebuffer(msg, &dynbuf);
1114*00b67f09SDavid van Moolenbroek 
1115*00b67f09SDavid van Moolenbroek 	if (tsig.signature != NULL) {
1116*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, tsig.signature, sigsize);
1117*00b67f09SDavid van Moolenbroek 		tsig.signature = NULL;
1118*00b67f09SDavid van Moolenbroek 	}
1119*00b67f09SDavid van Moolenbroek 
1120*00b67f09SDavid van Moolenbroek 	owner = NULL;
1121*00b67f09SDavid van Moolenbroek 	ret = dns_message_gettempname(msg, &owner);
1122*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1123*00b67f09SDavid van Moolenbroek 		goto cleanup_rdata;
1124*00b67f09SDavid van Moolenbroek 	dns_name_init(owner, NULL);
1125*00b67f09SDavid van Moolenbroek 	ret = dns_name_dup(&key->name, msg->mctx, owner);
1126*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1127*00b67f09SDavid van Moolenbroek 		goto cleanup_owner;
1128*00b67f09SDavid van Moolenbroek 
1129*00b67f09SDavid van Moolenbroek 	datalist = NULL;
1130*00b67f09SDavid van Moolenbroek 	ret = dns_message_gettemprdatalist(msg, &datalist);
1131*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1132*00b67f09SDavid van Moolenbroek 		goto cleanup_owner;
1133*00b67f09SDavid van Moolenbroek 	dataset = NULL;
1134*00b67f09SDavid van Moolenbroek 	ret = dns_message_gettemprdataset(msg, &dataset);
1135*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1136*00b67f09SDavid van Moolenbroek 		goto cleanup_rdatalist;
1137*00b67f09SDavid van Moolenbroek 	datalist->rdclass = dns_rdataclass_any;
1138*00b67f09SDavid van Moolenbroek 	datalist->type = dns_rdatatype_tsig;
1139*00b67f09SDavid van Moolenbroek 	datalist->covers = 0;
1140*00b67f09SDavid van Moolenbroek 	datalist->ttl = 0;
1141*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(datalist->rdata);
1142*00b67f09SDavid van Moolenbroek 	ISC_LIST_APPEND(datalist->rdata, rdata, link);
1143*00b67f09SDavid van Moolenbroek 	RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
1144*00b67f09SDavid van Moolenbroek 		      == ISC_R_SUCCESS);
1145*00b67f09SDavid van Moolenbroek 	msg->tsig = dataset;
1146*00b67f09SDavid van Moolenbroek 	msg->tsigname = owner;
1147*00b67f09SDavid van Moolenbroek 
1148*00b67f09SDavid van Moolenbroek 	/* Windows does not like the tsig name being compressed. */
1149*00b67f09SDavid van Moolenbroek 	msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
1150*00b67f09SDavid van Moolenbroek 
1151*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1152*00b67f09SDavid van Moolenbroek 
1153*00b67f09SDavid van Moolenbroek  cleanup_rdatalist:
1154*00b67f09SDavid van Moolenbroek 	dns_message_puttemprdatalist(msg, &datalist);
1155*00b67f09SDavid van Moolenbroek  cleanup_owner:
1156*00b67f09SDavid van Moolenbroek 	dns_message_puttempname(msg, &owner);
1157*00b67f09SDavid van Moolenbroek 	goto cleanup_rdata;
1158*00b67f09SDavid van Moolenbroek  cleanup_dynbuf:
1159*00b67f09SDavid van Moolenbroek 	isc_buffer_free(&dynbuf);
1160*00b67f09SDavid van Moolenbroek  cleanup_rdata:
1161*00b67f09SDavid van Moolenbroek 	dns_message_puttemprdata(msg, &rdata);
1162*00b67f09SDavid van Moolenbroek  cleanup_signature:
1163*00b67f09SDavid van Moolenbroek 	if (tsig.signature != NULL)
1164*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, tsig.signature, sigsize);
1165*00b67f09SDavid van Moolenbroek  cleanup_context:
1166*00b67f09SDavid van Moolenbroek 	if (ctx != NULL)
1167*00b67f09SDavid van Moolenbroek 		dst_context_destroy(&ctx);
1168*00b67f09SDavid van Moolenbroek 	return (ret);
1169*00b67f09SDavid van Moolenbroek }
1170*00b67f09SDavid van Moolenbroek 
1171*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsig_verify(isc_buffer_t * source,dns_message_t * msg,dns_tsig_keyring_t * ring1,dns_tsig_keyring_t * ring2)1172*00b67f09SDavid van Moolenbroek dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
1173*00b67f09SDavid van Moolenbroek 		dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2)
1174*00b67f09SDavid van Moolenbroek {
1175*00b67f09SDavid van Moolenbroek 	dns_rdata_any_tsig_t tsig, querytsig;
1176*00b67f09SDavid van Moolenbroek 	isc_region_t r, source_r, header_r, sig_r;
1177*00b67f09SDavid van Moolenbroek 	isc_buffer_t databuf;
1178*00b67f09SDavid van Moolenbroek 	unsigned char data[32];
1179*00b67f09SDavid van Moolenbroek 	dns_name_t *keyname;
1180*00b67f09SDavid van Moolenbroek 	dns_rdata_t rdata = DNS_RDATA_INIT;
1181*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
1182*00b67f09SDavid van Moolenbroek 	isc_result_t ret;
1183*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *tsigkey;
1184*00b67f09SDavid van Moolenbroek 	dst_key_t *key = NULL;
1185*00b67f09SDavid van Moolenbroek 	unsigned char header[DNS_MESSAGE_HEADERLEN];
1186*00b67f09SDavid van Moolenbroek 	dst_context_t *ctx = NULL;
1187*00b67f09SDavid van Moolenbroek 	isc_mem_t *mctx;
1188*00b67f09SDavid van Moolenbroek 	isc_uint16_t addcount, id;
1189*00b67f09SDavid van Moolenbroek 	unsigned int siglen;
1190*00b67f09SDavid van Moolenbroek 	unsigned int alg;
1191*00b67f09SDavid van Moolenbroek 	isc_boolean_t response;
1192*00b67f09SDavid van Moolenbroek 
1193*00b67f09SDavid van Moolenbroek 	REQUIRE(source != NULL);
1194*00b67f09SDavid van Moolenbroek 	REQUIRE(DNS_MESSAGE_VALID(msg));
1195*00b67f09SDavid van Moolenbroek 	tsigkey = dns_message_gettsigkey(msg);
1196*00b67f09SDavid van Moolenbroek 	response = is_response(msg);
1197*00b67f09SDavid van Moolenbroek 
1198*00b67f09SDavid van Moolenbroek 	REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
1199*00b67f09SDavid van Moolenbroek 
1200*00b67f09SDavid van Moolenbroek 	msg->verify_attempted = 1;
1201*00b67f09SDavid van Moolenbroek 
1202*00b67f09SDavid van Moolenbroek 	if (msg->tcp_continuation) {
1203*00b67f09SDavid van Moolenbroek 		if (tsigkey == NULL || msg->querytsig == NULL)
1204*00b67f09SDavid van Moolenbroek 			return (DNS_R_UNEXPECTEDTSIG);
1205*00b67f09SDavid van Moolenbroek 		return (tsig_verify_tcp(source, msg));
1206*00b67f09SDavid van Moolenbroek 	}
1207*00b67f09SDavid van Moolenbroek 
1208*00b67f09SDavid van Moolenbroek 	/*
1209*00b67f09SDavid van Moolenbroek 	 * There should be a TSIG record...
1210*00b67f09SDavid van Moolenbroek 	 */
1211*00b67f09SDavid van Moolenbroek 	if (msg->tsig == NULL)
1212*00b67f09SDavid van Moolenbroek 		return (DNS_R_EXPECTEDTSIG);
1213*00b67f09SDavid van Moolenbroek 
1214*00b67f09SDavid van Moolenbroek 	/*
1215*00b67f09SDavid van Moolenbroek 	 * If this is a response and there's no key or query TSIG, there
1216*00b67f09SDavid van Moolenbroek 	 * shouldn't be one on the response.
1217*00b67f09SDavid van Moolenbroek 	 */
1218*00b67f09SDavid van Moolenbroek 	if (response && (tsigkey == NULL || msg->querytsig == NULL))
1219*00b67f09SDavid van Moolenbroek 		return (DNS_R_UNEXPECTEDTSIG);
1220*00b67f09SDavid van Moolenbroek 
1221*00b67f09SDavid van Moolenbroek 	mctx = msg->mctx;
1222*00b67f09SDavid van Moolenbroek 
1223*00b67f09SDavid van Moolenbroek 	/*
1224*00b67f09SDavid van Moolenbroek 	 * If we're here, we know the message is well formed and contains a
1225*00b67f09SDavid van Moolenbroek 	 * TSIG record.
1226*00b67f09SDavid van Moolenbroek 	 */
1227*00b67f09SDavid van Moolenbroek 
1228*00b67f09SDavid van Moolenbroek 	keyname = msg->tsigname;
1229*00b67f09SDavid van Moolenbroek 	ret = dns_rdataset_first(msg->tsig);
1230*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1231*00b67f09SDavid van Moolenbroek 		return (ret);
1232*00b67f09SDavid van Moolenbroek 	dns_rdataset_current(msg->tsig, &rdata);
1233*00b67f09SDavid van Moolenbroek 	ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
1234*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1235*00b67f09SDavid van Moolenbroek 		return (ret);
1236*00b67f09SDavid van Moolenbroek 	dns_rdata_reset(&rdata);
1237*00b67f09SDavid van Moolenbroek 	if (response) {
1238*00b67f09SDavid van Moolenbroek 		ret = dns_rdataset_first(msg->querytsig);
1239*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1240*00b67f09SDavid van Moolenbroek 			return (ret);
1241*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(msg->querytsig, &rdata);
1242*00b67f09SDavid van Moolenbroek 		ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
1243*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1244*00b67f09SDavid van Moolenbroek 			return (ret);
1245*00b67f09SDavid van Moolenbroek 	}
1246*00b67f09SDavid van Moolenbroek #if defined(__clang__) && \
1247*00b67f09SDavid van Moolenbroek        ( __clang_major__ < 3 || \
1248*00b67f09SDavid van Moolenbroek 	(__clang_major__ == 3 && __clang_minor__ < 2) || \
1249*00b67f09SDavid van Moolenbroek 	(__clang_major__ == 4 && __clang_minor__ < 2))
1250*00b67f09SDavid van Moolenbroek 	/* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */
1251*00b67f09SDavid van Moolenbroek 		else memset(&querytsig, 0, sizeof(querytsig));
1252*00b67f09SDavid van Moolenbroek #endif
1253*00b67f09SDavid van Moolenbroek 
1254*00b67f09SDavid van Moolenbroek 	/*
1255*00b67f09SDavid van Moolenbroek 	 * Do the key name and algorithm match that of the query?
1256*00b67f09SDavid van Moolenbroek 	 */
1257*00b67f09SDavid van Moolenbroek 	if (response &&
1258*00b67f09SDavid van Moolenbroek 	    (!dns_name_equal(keyname, &tsigkey->name) ||
1259*00b67f09SDavid van Moolenbroek 	     !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) {
1260*00b67f09SDavid van Moolenbroek 		msg->tsigstatus = dns_tsigerror_badkey;
1261*00b67f09SDavid van Moolenbroek 		tsig_log(msg->tsigkey, 2,
1262*00b67f09SDavid van Moolenbroek 			 "key name and algorithm do not match");
1263*00b67f09SDavid van Moolenbroek 		return (DNS_R_TSIGVERIFYFAILURE);
1264*00b67f09SDavid van Moolenbroek 	}
1265*00b67f09SDavid van Moolenbroek 
1266*00b67f09SDavid van Moolenbroek 	/*
1267*00b67f09SDavid van Moolenbroek 	 * Get the current time.
1268*00b67f09SDavid van Moolenbroek 	 */
1269*00b67f09SDavid van Moolenbroek 	isc_stdtime_get(&now);
1270*00b67f09SDavid van Moolenbroek 
1271*00b67f09SDavid van Moolenbroek 	/*
1272*00b67f09SDavid van Moolenbroek 	 * Find dns_tsigkey_t based on keyname.
1273*00b67f09SDavid van Moolenbroek 	 */
1274*00b67f09SDavid van Moolenbroek 	if (tsigkey == NULL) {
1275*00b67f09SDavid van Moolenbroek 		ret = ISC_R_NOTFOUND;
1276*00b67f09SDavid van Moolenbroek 		if (ring1 != NULL)
1277*00b67f09SDavid van Moolenbroek 			ret = dns_tsigkey_find(&tsigkey, keyname,
1278*00b67f09SDavid van Moolenbroek 					       &tsig.algorithm, ring1);
1279*00b67f09SDavid van Moolenbroek 		if (ret == ISC_R_NOTFOUND && ring2 != NULL)
1280*00b67f09SDavid van Moolenbroek 			ret = dns_tsigkey_find(&tsigkey, keyname,
1281*00b67f09SDavid van Moolenbroek 					       &tsig.algorithm, ring2);
1282*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS) {
1283*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badkey;
1284*00b67f09SDavid van Moolenbroek 			ret = dns_tsigkey_create(keyname, &tsig.algorithm,
1285*00b67f09SDavid van Moolenbroek 						 NULL, 0, ISC_FALSE, NULL,
1286*00b67f09SDavid van Moolenbroek 						 now, now,
1287*00b67f09SDavid van Moolenbroek 						 mctx, NULL, &msg->tsigkey);
1288*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1289*00b67f09SDavid van Moolenbroek 				return (ret);
1290*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2, "unknown key");
1291*00b67f09SDavid van Moolenbroek 			return (DNS_R_TSIGVERIFYFAILURE);
1292*00b67f09SDavid van Moolenbroek 		}
1293*00b67f09SDavid van Moolenbroek 		msg->tsigkey = tsigkey;
1294*00b67f09SDavid van Moolenbroek 	}
1295*00b67f09SDavid van Moolenbroek 
1296*00b67f09SDavid van Moolenbroek 	key = tsigkey->key;
1297*00b67f09SDavid van Moolenbroek 
1298*00b67f09SDavid van Moolenbroek 	/*
1299*00b67f09SDavid van Moolenbroek 	 * Is the time ok?
1300*00b67f09SDavid van Moolenbroek 	 */
1301*00b67f09SDavid van Moolenbroek 	if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1302*00b67f09SDavid van Moolenbroek 		msg->tsigstatus = dns_tsigerror_badtime;
1303*00b67f09SDavid van Moolenbroek 		tsig_log(msg->tsigkey, 2, "signature has expired");
1304*00b67f09SDavid van Moolenbroek 		return (DNS_R_CLOCKSKEW);
1305*00b67f09SDavid van Moolenbroek 	} else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
1306*00b67f09SDavid van Moolenbroek 		msg->tsigstatus = dns_tsigerror_badtime;
1307*00b67f09SDavid van Moolenbroek 		tsig_log(msg->tsigkey, 2, "signature is in the future");
1308*00b67f09SDavid van Moolenbroek 		return (DNS_R_CLOCKSKEW);
1309*00b67f09SDavid van Moolenbroek 	}
1310*00b67f09SDavid van Moolenbroek 
1311*00b67f09SDavid van Moolenbroek 	/*
1312*00b67f09SDavid van Moolenbroek 	 * Check digest length.
1313*00b67f09SDavid van Moolenbroek 	 */
1314*00b67f09SDavid van Moolenbroek 	alg = dst_key_alg(key);
1315*00b67f09SDavid van Moolenbroek 	ret = dst_key_sigsize(key, &siglen);
1316*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1317*00b67f09SDavid van Moolenbroek 		return (ret);
1318*00b67f09SDavid van Moolenbroek 	if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 ||
1319*00b67f09SDavid van Moolenbroek 	    alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
1320*00b67f09SDavid van Moolenbroek 	    alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) {
1321*00b67f09SDavid van Moolenbroek 		isc_uint16_t digestbits = dst_key_getbits(key);
1322*00b67f09SDavid van Moolenbroek 		if (tsig.siglen > siglen) {
1323*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2, "signature length too big");
1324*00b67f09SDavid van Moolenbroek 			return (DNS_R_FORMERR);
1325*00b67f09SDavid van Moolenbroek 		}
1326*00b67f09SDavid van Moolenbroek 		if (tsig.siglen > 0 &&
1327*00b67f09SDavid van Moolenbroek 		    (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) {
1328*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2,
1329*00b67f09SDavid van Moolenbroek 				 "signature length below minimum");
1330*00b67f09SDavid van Moolenbroek 			return (DNS_R_FORMERR);
1331*00b67f09SDavid van Moolenbroek 		}
1332*00b67f09SDavid van Moolenbroek 		if (tsig.siglen > 0 && digestbits != 0 &&
1333*00b67f09SDavid van Moolenbroek 		    tsig.siglen < ((digestbits + 1) / 8)) {
1334*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badtrunc;
1335*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2,
1336*00b67f09SDavid van Moolenbroek 				 "truncated signature length too small");
1337*00b67f09SDavid van Moolenbroek 			return (DNS_R_TSIGVERIFYFAILURE);
1338*00b67f09SDavid van Moolenbroek 		}
1339*00b67f09SDavid van Moolenbroek 		if (tsig.siglen > 0 && digestbits == 0 &&
1340*00b67f09SDavid van Moolenbroek 		    tsig.siglen < siglen) {
1341*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badtrunc;
1342*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2, "signature length too small");
1343*00b67f09SDavid van Moolenbroek 			return (DNS_R_TSIGVERIFYFAILURE);
1344*00b67f09SDavid van Moolenbroek 		}
1345*00b67f09SDavid van Moolenbroek 	}
1346*00b67f09SDavid van Moolenbroek 
1347*00b67f09SDavid van Moolenbroek 	if (tsig.siglen > 0) {
1348*00b67f09SDavid van Moolenbroek 		sig_r.base = tsig.signature;
1349*00b67f09SDavid van Moolenbroek 		sig_r.length = tsig.siglen;
1350*00b67f09SDavid van Moolenbroek 
1351*00b67f09SDavid van Moolenbroek 		ret = dst_context_create3(key, mctx,
1352*00b67f09SDavid van Moolenbroek 					  DNS_LOGCATEGORY_DNSSEC,
1353*00b67f09SDavid van Moolenbroek 					  ISC_FALSE, &ctx);
1354*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1355*00b67f09SDavid van Moolenbroek 			return (ret);
1356*00b67f09SDavid van Moolenbroek 
1357*00b67f09SDavid van Moolenbroek 		if (response) {
1358*00b67f09SDavid van Moolenbroek 			isc_buffer_init(&databuf, data, sizeof(data));
1359*00b67f09SDavid van Moolenbroek 			isc_buffer_putuint16(&databuf, querytsig.siglen);
1360*00b67f09SDavid van Moolenbroek 			isc_buffer_usedregion(&databuf, &r);
1361*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(ctx, &r);
1362*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1363*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
1364*00b67f09SDavid van Moolenbroek 			if (querytsig.siglen > 0) {
1365*00b67f09SDavid van Moolenbroek 				r.length = querytsig.siglen;
1366*00b67f09SDavid van Moolenbroek 				r.base = querytsig.signature;
1367*00b67f09SDavid van Moolenbroek 				ret = dst_context_adddata(ctx, &r);
1368*00b67f09SDavid van Moolenbroek 				if (ret != ISC_R_SUCCESS)
1369*00b67f09SDavid van Moolenbroek 					goto cleanup_context;
1370*00b67f09SDavid van Moolenbroek 			}
1371*00b67f09SDavid van Moolenbroek 		}
1372*00b67f09SDavid van Moolenbroek 
1373*00b67f09SDavid van Moolenbroek 		/*
1374*00b67f09SDavid van Moolenbroek 		 * Extract the header.
1375*00b67f09SDavid van Moolenbroek 		 */
1376*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(source, &r);
1377*00b67f09SDavid van Moolenbroek 		memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1378*00b67f09SDavid van Moolenbroek 		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1379*00b67f09SDavid van Moolenbroek 
1380*00b67f09SDavid van Moolenbroek 		/*
1381*00b67f09SDavid van Moolenbroek 		 * Decrement the additional field counter.
1382*00b67f09SDavid van Moolenbroek 		 */
1383*00b67f09SDavid van Moolenbroek 		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1384*00b67f09SDavid van Moolenbroek 		addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
1385*00b67f09SDavid van Moolenbroek 		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1386*00b67f09SDavid van Moolenbroek 
1387*00b67f09SDavid van Moolenbroek 		/*
1388*00b67f09SDavid van Moolenbroek 		 * Put in the original id.
1389*00b67f09SDavid van Moolenbroek 		 */
1390*00b67f09SDavid van Moolenbroek 		id = htons(tsig.originalid);
1391*00b67f09SDavid van Moolenbroek 		memmove(&header[0], &id, 2);
1392*00b67f09SDavid van Moolenbroek 
1393*00b67f09SDavid van Moolenbroek 		/*
1394*00b67f09SDavid van Moolenbroek 		 * Digest the modified header.
1395*00b67f09SDavid van Moolenbroek 		 */
1396*00b67f09SDavid van Moolenbroek 		header_r.base = (unsigned char *) header;
1397*00b67f09SDavid van Moolenbroek 		header_r.length = DNS_MESSAGE_HEADERLEN;
1398*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &header_r);
1399*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1400*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1401*00b67f09SDavid van Moolenbroek 
1402*00b67f09SDavid van Moolenbroek 		/*
1403*00b67f09SDavid van Moolenbroek 		 * Digest all non-TSIG records.
1404*00b67f09SDavid van Moolenbroek 		 */
1405*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(source, &source_r);
1406*00b67f09SDavid van Moolenbroek 		r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
1407*00b67f09SDavid van Moolenbroek 		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
1408*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1409*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1410*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1411*00b67f09SDavid van Moolenbroek 
1412*00b67f09SDavid van Moolenbroek 		/*
1413*00b67f09SDavid van Moolenbroek 		 * Digest the key name.
1414*00b67f09SDavid van Moolenbroek 		 */
1415*00b67f09SDavid van Moolenbroek 		dns_name_toregion(&tsigkey->name, &r);
1416*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1417*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1418*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1419*00b67f09SDavid van Moolenbroek 
1420*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&databuf, data, sizeof(data));
1421*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&databuf, tsig.common.rdclass);
1422*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint32(&databuf, msg->tsig->ttl);
1423*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(&databuf, &r);
1424*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1425*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1426*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1427*00b67f09SDavid van Moolenbroek 
1428*00b67f09SDavid van Moolenbroek 		/*
1429*00b67f09SDavid van Moolenbroek 		 * Digest the key algorithm.
1430*00b67f09SDavid van Moolenbroek 		 */
1431*00b67f09SDavid van Moolenbroek 		dns_name_toregion(tsigkey->algorithm, &r);
1432*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1433*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1434*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1435*00b67f09SDavid van Moolenbroek 
1436*00b67f09SDavid van Moolenbroek 		isc_buffer_clear(&databuf);
1437*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint48(&databuf, tsig.timesigned);
1438*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&databuf, tsig.fudge);
1439*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&databuf, tsig.error);
1440*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&databuf, tsig.otherlen);
1441*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(&databuf, &r);
1442*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(ctx, &r);
1443*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1444*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1445*00b67f09SDavid van Moolenbroek 
1446*00b67f09SDavid van Moolenbroek 		if (tsig.otherlen > 0) {
1447*00b67f09SDavid van Moolenbroek 			r.base = tsig.other;
1448*00b67f09SDavid van Moolenbroek 			r.length = tsig.otherlen;
1449*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(ctx, &r);
1450*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1451*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
1452*00b67f09SDavid van Moolenbroek 		}
1453*00b67f09SDavid van Moolenbroek 
1454*00b67f09SDavid van Moolenbroek 		ret = dst_context_verify(ctx, &sig_r);
1455*00b67f09SDavid van Moolenbroek 		if (ret == DST_R_VERIFYFAILURE) {
1456*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badsig;
1457*00b67f09SDavid van Moolenbroek 			ret = DNS_R_TSIGVERIFYFAILURE;
1458*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2,
1459*00b67f09SDavid van Moolenbroek 				 "signature failed to verify(1)");
1460*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1461*00b67f09SDavid van Moolenbroek 		} else if (ret != ISC_R_SUCCESS)
1462*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1463*00b67f09SDavid van Moolenbroek 
1464*00b67f09SDavid van Moolenbroek 		dst_context_destroy(&ctx);
1465*00b67f09SDavid van Moolenbroek 	} else if (tsig.error != dns_tsigerror_badsig &&
1466*00b67f09SDavid van Moolenbroek 		   tsig.error != dns_tsigerror_badkey) {
1467*00b67f09SDavid van Moolenbroek 		msg->tsigstatus = dns_tsigerror_badsig;
1468*00b67f09SDavid van Moolenbroek 		tsig_log(msg->tsigkey, 2, "signature was empty");
1469*00b67f09SDavid van Moolenbroek 		return (DNS_R_TSIGVERIFYFAILURE);
1470*00b67f09SDavid van Moolenbroek 	}
1471*00b67f09SDavid van Moolenbroek 
1472*00b67f09SDavid van Moolenbroek 	msg->tsigstatus = dns_rcode_noerror;
1473*00b67f09SDavid van Moolenbroek 
1474*00b67f09SDavid van Moolenbroek 	if (tsig.error != dns_rcode_noerror) {
1475*00b67f09SDavid van Moolenbroek 		if (tsig.error == dns_tsigerror_badtime)
1476*00b67f09SDavid van Moolenbroek 			return (DNS_R_CLOCKSKEW);
1477*00b67f09SDavid van Moolenbroek 		else
1478*00b67f09SDavid van Moolenbroek 			return (DNS_R_TSIGERRORSET);
1479*00b67f09SDavid van Moolenbroek 	}
1480*00b67f09SDavid van Moolenbroek 
1481*00b67f09SDavid van Moolenbroek 	msg->verified_sig = 1;
1482*00b67f09SDavid van Moolenbroek 
1483*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1484*00b67f09SDavid van Moolenbroek 
1485*00b67f09SDavid van Moolenbroek cleanup_context:
1486*00b67f09SDavid van Moolenbroek 	if (ctx != NULL)
1487*00b67f09SDavid van Moolenbroek 		dst_context_destroy(&ctx);
1488*00b67f09SDavid van Moolenbroek 
1489*00b67f09SDavid van Moolenbroek 	return (ret);
1490*00b67f09SDavid van Moolenbroek }
1491*00b67f09SDavid van Moolenbroek 
1492*00b67f09SDavid van Moolenbroek static isc_result_t
tsig_verify_tcp(isc_buffer_t * source,dns_message_t * msg)1493*00b67f09SDavid van Moolenbroek tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
1494*00b67f09SDavid van Moolenbroek 	dns_rdata_any_tsig_t tsig, querytsig;
1495*00b67f09SDavid van Moolenbroek 	isc_region_t r, source_r, header_r, sig_r;
1496*00b67f09SDavid van Moolenbroek 	isc_buffer_t databuf;
1497*00b67f09SDavid van Moolenbroek 	unsigned char data[32];
1498*00b67f09SDavid van Moolenbroek 	dns_name_t *keyname;
1499*00b67f09SDavid van Moolenbroek 	dns_rdata_t rdata = DNS_RDATA_INIT;
1500*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
1501*00b67f09SDavid van Moolenbroek 	isc_result_t ret;
1502*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *tsigkey;
1503*00b67f09SDavid van Moolenbroek 	dst_key_t *key = NULL;
1504*00b67f09SDavid van Moolenbroek 	unsigned char header[DNS_MESSAGE_HEADERLEN];
1505*00b67f09SDavid van Moolenbroek 	isc_uint16_t addcount, id;
1506*00b67f09SDavid van Moolenbroek 	isc_boolean_t has_tsig = ISC_FALSE;
1507*00b67f09SDavid van Moolenbroek 	isc_mem_t *mctx;
1508*00b67f09SDavid van Moolenbroek 
1509*00b67f09SDavid van Moolenbroek 	REQUIRE(source != NULL);
1510*00b67f09SDavid van Moolenbroek 	REQUIRE(msg != NULL);
1511*00b67f09SDavid van Moolenbroek 	REQUIRE(dns_message_gettsigkey(msg) != NULL);
1512*00b67f09SDavid van Moolenbroek 	REQUIRE(msg->tcp_continuation == 1);
1513*00b67f09SDavid van Moolenbroek 	REQUIRE(msg->querytsig != NULL);
1514*00b67f09SDavid van Moolenbroek 
1515*00b67f09SDavid van Moolenbroek 	if (!is_response(msg))
1516*00b67f09SDavid van Moolenbroek 		return (DNS_R_EXPECTEDRESPONSE);
1517*00b67f09SDavid van Moolenbroek 
1518*00b67f09SDavid van Moolenbroek 	mctx = msg->mctx;
1519*00b67f09SDavid van Moolenbroek 
1520*00b67f09SDavid van Moolenbroek 	tsigkey = dns_message_gettsigkey(msg);
1521*00b67f09SDavid van Moolenbroek 
1522*00b67f09SDavid van Moolenbroek 	/*
1523*00b67f09SDavid van Moolenbroek 	 * Extract and parse the previous TSIG
1524*00b67f09SDavid van Moolenbroek 	 */
1525*00b67f09SDavid van Moolenbroek 	ret = dns_rdataset_first(msg->querytsig);
1526*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1527*00b67f09SDavid van Moolenbroek 		return (ret);
1528*00b67f09SDavid van Moolenbroek 	dns_rdataset_current(msg->querytsig, &rdata);
1529*00b67f09SDavid van Moolenbroek 	ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
1530*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1531*00b67f09SDavid van Moolenbroek 		return (ret);
1532*00b67f09SDavid van Moolenbroek 	dns_rdata_reset(&rdata);
1533*00b67f09SDavid van Moolenbroek 
1534*00b67f09SDavid van Moolenbroek 	/*
1535*00b67f09SDavid van Moolenbroek 	 * If there is a TSIG in this message, do some checks.
1536*00b67f09SDavid van Moolenbroek 	 */
1537*00b67f09SDavid van Moolenbroek 	if (msg->tsig != NULL) {
1538*00b67f09SDavid van Moolenbroek 		has_tsig = ISC_TRUE;
1539*00b67f09SDavid van Moolenbroek 
1540*00b67f09SDavid van Moolenbroek 		keyname = msg->tsigname;
1541*00b67f09SDavid van Moolenbroek 		ret = dns_rdataset_first(msg->tsig);
1542*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1543*00b67f09SDavid van Moolenbroek 			goto cleanup_querystruct;
1544*00b67f09SDavid van Moolenbroek 		dns_rdataset_current(msg->tsig, &rdata);
1545*00b67f09SDavid van Moolenbroek 		ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
1546*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1547*00b67f09SDavid van Moolenbroek 			goto cleanup_querystruct;
1548*00b67f09SDavid van Moolenbroek 
1549*00b67f09SDavid van Moolenbroek 		/*
1550*00b67f09SDavid van Moolenbroek 		 * Do the key name and algorithm match that of the query?
1551*00b67f09SDavid van Moolenbroek 		 */
1552*00b67f09SDavid van Moolenbroek 		if (!dns_name_equal(keyname, &tsigkey->name) ||
1553*00b67f09SDavid van Moolenbroek 		    !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) {
1554*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badkey;
1555*00b67f09SDavid van Moolenbroek 			ret = DNS_R_TSIGVERIFYFAILURE;
1556*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2,
1557*00b67f09SDavid van Moolenbroek 				 "key name and algorithm do not match");
1558*00b67f09SDavid van Moolenbroek 			goto cleanup_querystruct;
1559*00b67f09SDavid van Moolenbroek 		}
1560*00b67f09SDavid van Moolenbroek 
1561*00b67f09SDavid van Moolenbroek 		/*
1562*00b67f09SDavid van Moolenbroek 		 * Is the time ok?
1563*00b67f09SDavid van Moolenbroek 		 */
1564*00b67f09SDavid van Moolenbroek 		isc_stdtime_get(&now);
1565*00b67f09SDavid van Moolenbroek 
1566*00b67f09SDavid van Moolenbroek 		if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1567*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badtime;
1568*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2, "signature has expired");
1569*00b67f09SDavid van Moolenbroek 			ret = DNS_R_CLOCKSKEW;
1570*00b67f09SDavid van Moolenbroek 			goto cleanup_querystruct;
1571*00b67f09SDavid van Moolenbroek 		} else if (now + msg->timeadjust <
1572*00b67f09SDavid van Moolenbroek 			   tsig.timesigned - tsig.fudge) {
1573*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badtime;
1574*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2,
1575*00b67f09SDavid van Moolenbroek 				 "signature is in the future");
1576*00b67f09SDavid van Moolenbroek 			ret = DNS_R_CLOCKSKEW;
1577*00b67f09SDavid van Moolenbroek 			goto cleanup_querystruct;
1578*00b67f09SDavid van Moolenbroek 		}
1579*00b67f09SDavid van Moolenbroek 	}
1580*00b67f09SDavid van Moolenbroek 
1581*00b67f09SDavid van Moolenbroek 	key = tsigkey->key;
1582*00b67f09SDavid van Moolenbroek 
1583*00b67f09SDavid van Moolenbroek 	if (msg->tsigctx == NULL) {
1584*00b67f09SDavid van Moolenbroek 		ret = dst_context_create3(key, mctx,
1585*00b67f09SDavid van Moolenbroek 					  DNS_LOGCATEGORY_DNSSEC,
1586*00b67f09SDavid van Moolenbroek 					  ISC_FALSE, &msg->tsigctx);
1587*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1588*00b67f09SDavid van Moolenbroek 			goto cleanup_querystruct;
1589*00b67f09SDavid van Moolenbroek 
1590*00b67f09SDavid van Moolenbroek 		/*
1591*00b67f09SDavid van Moolenbroek 		 * Digest the length of the query signature
1592*00b67f09SDavid van Moolenbroek 		 */
1593*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&databuf, data, sizeof(data));
1594*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&databuf, querytsig.siglen);
1595*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(&databuf, &r);
1596*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(msg->tsigctx, &r);
1597*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1598*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1599*00b67f09SDavid van Moolenbroek 
1600*00b67f09SDavid van Moolenbroek 		/*
1601*00b67f09SDavid van Moolenbroek 		 * Digest the data of the query signature
1602*00b67f09SDavid van Moolenbroek 		 */
1603*00b67f09SDavid van Moolenbroek 		if (querytsig.siglen > 0) {
1604*00b67f09SDavid van Moolenbroek 			r.length = querytsig.siglen;
1605*00b67f09SDavid van Moolenbroek 			r.base = querytsig.signature;
1606*00b67f09SDavid van Moolenbroek 			ret = dst_context_adddata(msg->tsigctx, &r);
1607*00b67f09SDavid van Moolenbroek 			if (ret != ISC_R_SUCCESS)
1608*00b67f09SDavid van Moolenbroek 				goto cleanup_context;
1609*00b67f09SDavid van Moolenbroek 		}
1610*00b67f09SDavid van Moolenbroek 	}
1611*00b67f09SDavid van Moolenbroek 
1612*00b67f09SDavid van Moolenbroek 	/*
1613*00b67f09SDavid van Moolenbroek 	 * Extract the header.
1614*00b67f09SDavid van Moolenbroek 	 */
1615*00b67f09SDavid van Moolenbroek 	isc_buffer_usedregion(source, &r);
1616*00b67f09SDavid van Moolenbroek 	memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1617*00b67f09SDavid van Moolenbroek 	isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1618*00b67f09SDavid van Moolenbroek 
1619*00b67f09SDavid van Moolenbroek 	/*
1620*00b67f09SDavid van Moolenbroek 	 * Decrement the additional field counter if necessary.
1621*00b67f09SDavid van Moolenbroek 	 */
1622*00b67f09SDavid van Moolenbroek 	if (has_tsig) {
1623*00b67f09SDavid van Moolenbroek 		memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1624*00b67f09SDavid van Moolenbroek 		addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
1625*00b67f09SDavid van Moolenbroek 		memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1626*00b67f09SDavid van Moolenbroek 	}
1627*00b67f09SDavid van Moolenbroek 
1628*00b67f09SDavid van Moolenbroek 	/*
1629*00b67f09SDavid van Moolenbroek 	 * Put in the original id.
1630*00b67f09SDavid van Moolenbroek 	 */
1631*00b67f09SDavid van Moolenbroek 	/* XXX Can TCP transfers be forwarded?  How would that work? */
1632*00b67f09SDavid van Moolenbroek 	if (has_tsig) {
1633*00b67f09SDavid van Moolenbroek 		id = htons(tsig.originalid);
1634*00b67f09SDavid van Moolenbroek 		memmove(&header[0], &id, 2);
1635*00b67f09SDavid van Moolenbroek 	}
1636*00b67f09SDavid van Moolenbroek 
1637*00b67f09SDavid van Moolenbroek 	/*
1638*00b67f09SDavid van Moolenbroek 	 * Digest the modified header.
1639*00b67f09SDavid van Moolenbroek 	 */
1640*00b67f09SDavid van Moolenbroek 	header_r.base = (unsigned char *) header;
1641*00b67f09SDavid van Moolenbroek 	header_r.length = DNS_MESSAGE_HEADERLEN;
1642*00b67f09SDavid van Moolenbroek 	ret = dst_context_adddata(msg->tsigctx, &header_r);
1643*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1644*00b67f09SDavid van Moolenbroek 		goto cleanup_context;
1645*00b67f09SDavid van Moolenbroek 
1646*00b67f09SDavid van Moolenbroek 	/*
1647*00b67f09SDavid van Moolenbroek 	 * Digest all non-TSIG records.
1648*00b67f09SDavid van Moolenbroek 	 */
1649*00b67f09SDavid van Moolenbroek 	isc_buffer_usedregion(source, &source_r);
1650*00b67f09SDavid van Moolenbroek 	r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
1651*00b67f09SDavid van Moolenbroek 	if (has_tsig)
1652*00b67f09SDavid van Moolenbroek 		r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
1653*00b67f09SDavid van Moolenbroek 	else
1654*00b67f09SDavid van Moolenbroek 		r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
1655*00b67f09SDavid van Moolenbroek 	ret = dst_context_adddata(msg->tsigctx, &r);
1656*00b67f09SDavid van Moolenbroek 	if (ret != ISC_R_SUCCESS)
1657*00b67f09SDavid van Moolenbroek 		goto cleanup_context;
1658*00b67f09SDavid van Moolenbroek 
1659*00b67f09SDavid van Moolenbroek 	/*
1660*00b67f09SDavid van Moolenbroek 	 * Digest the time signed and fudge.
1661*00b67f09SDavid van Moolenbroek 	 */
1662*00b67f09SDavid van Moolenbroek 	if (has_tsig) {
1663*00b67f09SDavid van Moolenbroek 		isc_buffer_init(&databuf, data, sizeof(data));
1664*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint48(&databuf, tsig.timesigned);
1665*00b67f09SDavid van Moolenbroek 		isc_buffer_putuint16(&databuf, tsig.fudge);
1666*00b67f09SDavid van Moolenbroek 		isc_buffer_usedregion(&databuf, &r);
1667*00b67f09SDavid van Moolenbroek 		ret = dst_context_adddata(msg->tsigctx, &r);
1668*00b67f09SDavid van Moolenbroek 		if (ret != ISC_R_SUCCESS)
1669*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1670*00b67f09SDavid van Moolenbroek 
1671*00b67f09SDavid van Moolenbroek 		sig_r.base = tsig.signature;
1672*00b67f09SDavid van Moolenbroek 		sig_r.length = tsig.siglen;
1673*00b67f09SDavid van Moolenbroek 		if (tsig.siglen == 0) {
1674*00b67f09SDavid van Moolenbroek 			if (tsig.error != dns_rcode_noerror) {
1675*00b67f09SDavid van Moolenbroek 				if (tsig.error == dns_tsigerror_badtime)
1676*00b67f09SDavid van Moolenbroek 					ret = DNS_R_CLOCKSKEW;
1677*00b67f09SDavid van Moolenbroek 				else
1678*00b67f09SDavid van Moolenbroek 					ret = DNS_R_TSIGERRORSET;
1679*00b67f09SDavid van Moolenbroek 			} else {
1680*00b67f09SDavid van Moolenbroek 				tsig_log(msg->tsigkey, 2,
1681*00b67f09SDavid van Moolenbroek 					 "signature is empty");
1682*00b67f09SDavid van Moolenbroek 				ret = DNS_R_TSIGVERIFYFAILURE;
1683*00b67f09SDavid van Moolenbroek 			}
1684*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1685*00b67f09SDavid van Moolenbroek 		}
1686*00b67f09SDavid van Moolenbroek 
1687*00b67f09SDavid van Moolenbroek 		ret = dst_context_verify(msg->tsigctx, &sig_r);
1688*00b67f09SDavid van Moolenbroek 		if (ret == DST_R_VERIFYFAILURE) {
1689*00b67f09SDavid van Moolenbroek 			msg->tsigstatus = dns_tsigerror_badsig;
1690*00b67f09SDavid van Moolenbroek 			tsig_log(msg->tsigkey, 2,
1691*00b67f09SDavid van Moolenbroek 				 "signature failed to verify(2)");
1692*00b67f09SDavid van Moolenbroek 			ret = DNS_R_TSIGVERIFYFAILURE;
1693*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1694*00b67f09SDavid van Moolenbroek 		}
1695*00b67f09SDavid van Moolenbroek 		else if (ret != ISC_R_SUCCESS)
1696*00b67f09SDavid van Moolenbroek 			goto cleanup_context;
1697*00b67f09SDavid van Moolenbroek 
1698*00b67f09SDavid van Moolenbroek 		dst_context_destroy(&msg->tsigctx);
1699*00b67f09SDavid van Moolenbroek 	}
1700*00b67f09SDavid van Moolenbroek 
1701*00b67f09SDavid van Moolenbroek 	msg->tsigstatus = dns_rcode_noerror;
1702*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1703*00b67f09SDavid van Moolenbroek 
1704*00b67f09SDavid van Moolenbroek  cleanup_context:
1705*00b67f09SDavid van Moolenbroek 	dst_context_destroy(&msg->tsigctx);
1706*00b67f09SDavid van Moolenbroek 
1707*00b67f09SDavid van Moolenbroek  cleanup_querystruct:
1708*00b67f09SDavid van Moolenbroek 	dns_rdata_freestruct(&querytsig);
1709*00b67f09SDavid van Moolenbroek 
1710*00b67f09SDavid van Moolenbroek 	return (ret);
1711*00b67f09SDavid van Moolenbroek 
1712*00b67f09SDavid van Moolenbroek }
1713*00b67f09SDavid van Moolenbroek 
1714*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsigkey_find(dns_tsigkey_t ** tsigkey,dns_name_t * name,dns_name_t * algorithm,dns_tsig_keyring_t * ring)1715*00b67f09SDavid van Moolenbroek dns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name,
1716*00b67f09SDavid van Moolenbroek 		 dns_name_t *algorithm, dns_tsig_keyring_t *ring)
1717*00b67f09SDavid van Moolenbroek {
1718*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *key;
1719*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
1720*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1721*00b67f09SDavid van Moolenbroek 
1722*00b67f09SDavid van Moolenbroek 	REQUIRE(tsigkey != NULL);
1723*00b67f09SDavid van Moolenbroek 	REQUIRE(*tsigkey == NULL);
1724*00b67f09SDavid van Moolenbroek 	REQUIRE(name != NULL);
1725*00b67f09SDavid van Moolenbroek 	REQUIRE(ring != NULL);
1726*00b67f09SDavid van Moolenbroek 
1727*00b67f09SDavid van Moolenbroek 	RWLOCK(&ring->lock, isc_rwlocktype_write);
1728*00b67f09SDavid van Moolenbroek 	cleanup_ring(ring);
1729*00b67f09SDavid van Moolenbroek 	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1730*00b67f09SDavid van Moolenbroek 
1731*00b67f09SDavid van Moolenbroek 	isc_stdtime_get(&now);
1732*00b67f09SDavid van Moolenbroek 	RWLOCK(&ring->lock, isc_rwlocktype_read);
1733*00b67f09SDavid van Moolenbroek 	key = NULL;
1734*00b67f09SDavid van Moolenbroek 	result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
1735*00b67f09SDavid van Moolenbroek 	if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
1736*00b67f09SDavid van Moolenbroek 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1737*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOTFOUND);
1738*00b67f09SDavid van Moolenbroek 	}
1739*00b67f09SDavid van Moolenbroek 	if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
1740*00b67f09SDavid van Moolenbroek 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1741*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOTFOUND);
1742*00b67f09SDavid van Moolenbroek 	}
1743*00b67f09SDavid van Moolenbroek 	if (key->inception != key->expire && isc_serial_lt(key->expire, now)) {
1744*00b67f09SDavid van Moolenbroek 		/*
1745*00b67f09SDavid van Moolenbroek 		 * The key has expired.
1746*00b67f09SDavid van Moolenbroek 		 */
1747*00b67f09SDavid van Moolenbroek 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1748*00b67f09SDavid van Moolenbroek 		RWLOCK(&ring->lock, isc_rwlocktype_write);
1749*00b67f09SDavid van Moolenbroek 		remove_fromring(key);
1750*00b67f09SDavid van Moolenbroek 		RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1751*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOTFOUND);
1752*00b67f09SDavid van Moolenbroek 	}
1753*00b67f09SDavid van Moolenbroek #if 0
1754*00b67f09SDavid van Moolenbroek 	/*
1755*00b67f09SDavid van Moolenbroek 	 * MPAXXX We really should look at the inception time.
1756*00b67f09SDavid van Moolenbroek 	 */
1757*00b67f09SDavid van Moolenbroek 	if (key->inception != key->expire &&
1758*00b67f09SDavid van Moolenbroek 	    isc_serial_lt(key->inception, now)) {
1759*00b67f09SDavid van Moolenbroek 		RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1760*00b67f09SDavid van Moolenbroek 		adjust_lru(key);
1761*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOTFOUND);
1762*00b67f09SDavid van Moolenbroek 	}
1763*00b67f09SDavid van Moolenbroek #endif
1764*00b67f09SDavid van Moolenbroek 	isc_refcount_increment(&key->refs, NULL);
1765*00b67f09SDavid van Moolenbroek 	RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1766*00b67f09SDavid van Moolenbroek 	adjust_lru(key);
1767*00b67f09SDavid van Moolenbroek 	*tsigkey = key;
1768*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1769*00b67f09SDavid van Moolenbroek }
1770*00b67f09SDavid van Moolenbroek 
1771*00b67f09SDavid van Moolenbroek static void
free_tsignode(void * node,void * _unused)1772*00b67f09SDavid van Moolenbroek free_tsignode(void *node, void *_unused) {
1773*00b67f09SDavid van Moolenbroek 	dns_tsigkey_t *key;
1774*00b67f09SDavid van Moolenbroek 
1775*00b67f09SDavid van Moolenbroek 	REQUIRE(node != NULL);
1776*00b67f09SDavid van Moolenbroek 
1777*00b67f09SDavid van Moolenbroek 	UNUSED(_unused);
1778*00b67f09SDavid van Moolenbroek 
1779*00b67f09SDavid van Moolenbroek 	key = node;
1780*00b67f09SDavid van Moolenbroek 	if (key->generated) {
1781*00b67f09SDavid van Moolenbroek 		if (ISC_LINK_LINKED(key, link))
1782*00b67f09SDavid van Moolenbroek 			ISC_LIST_UNLINK(key->ring->lru, key, link);
1783*00b67f09SDavid van Moolenbroek 	}
1784*00b67f09SDavid van Moolenbroek 	dns_tsigkey_detach(&key);
1785*00b67f09SDavid van Moolenbroek }
1786*00b67f09SDavid van Moolenbroek 
1787*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsigkeyring_create(isc_mem_t * mctx,dns_tsig_keyring_t ** ringp)1788*00b67f09SDavid van Moolenbroek dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
1789*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1790*00b67f09SDavid van Moolenbroek 	dns_tsig_keyring_t *ring;
1791*00b67f09SDavid van Moolenbroek 
1792*00b67f09SDavid van Moolenbroek 	REQUIRE(mctx != NULL);
1793*00b67f09SDavid van Moolenbroek 	REQUIRE(ringp != NULL);
1794*00b67f09SDavid van Moolenbroek 	REQUIRE(*ringp == NULL);
1795*00b67f09SDavid van Moolenbroek 
1796*00b67f09SDavid van Moolenbroek 	ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t));
1797*00b67f09SDavid van Moolenbroek 	if (ring == NULL)
1798*00b67f09SDavid van Moolenbroek 		return (ISC_R_NOMEMORY);
1799*00b67f09SDavid van Moolenbroek 
1800*00b67f09SDavid van Moolenbroek 	result = isc_rwlock_init(&ring->lock, 0, 0);
1801*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
1802*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
1803*00b67f09SDavid van Moolenbroek 		return (result);
1804*00b67f09SDavid van Moolenbroek 	}
1805*00b67f09SDavid van Moolenbroek 
1806*00b67f09SDavid van Moolenbroek 	ring->keys = NULL;
1807*00b67f09SDavid van Moolenbroek 	result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys);
1808*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS) {
1809*00b67f09SDavid van Moolenbroek 		isc_rwlock_destroy(&ring->lock);
1810*00b67f09SDavid van Moolenbroek 		isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
1811*00b67f09SDavid van Moolenbroek 		return (result);
1812*00b67f09SDavid van Moolenbroek 	}
1813*00b67f09SDavid van Moolenbroek 
1814*00b67f09SDavid van Moolenbroek 	ring->writecount = 0;
1815*00b67f09SDavid van Moolenbroek 	ring->mctx = NULL;
1816*00b67f09SDavid van Moolenbroek 	ring->generated = 0;
1817*00b67f09SDavid van Moolenbroek 	ring->maxgenerated = DNS_TSIG_MAXGENERATEDKEYS;
1818*00b67f09SDavid van Moolenbroek 	ISC_LIST_INIT(ring->lru);
1819*00b67f09SDavid van Moolenbroek 	isc_mem_attach(mctx, &ring->mctx);
1820*00b67f09SDavid van Moolenbroek 	ring->references = 1;
1821*00b67f09SDavid van Moolenbroek 
1822*00b67f09SDavid van Moolenbroek 	*ringp = ring;
1823*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
1824*00b67f09SDavid van Moolenbroek }
1825*00b67f09SDavid van Moolenbroek 
1826*00b67f09SDavid van Moolenbroek isc_result_t
dns_tsigkeyring_add(dns_tsig_keyring_t * ring,dns_name_t * name,dns_tsigkey_t * tkey)1827*00b67f09SDavid van Moolenbroek dns_tsigkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
1828*00b67f09SDavid van Moolenbroek 		    dns_tsigkey_t *tkey)
1829*00b67f09SDavid van Moolenbroek {
1830*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1831*00b67f09SDavid van Moolenbroek 
1832*00b67f09SDavid van Moolenbroek 	result = keyring_add(ring, name, tkey);
1833*00b67f09SDavid van Moolenbroek 	if (result == ISC_R_SUCCESS)
1834*00b67f09SDavid van Moolenbroek 		isc_refcount_increment(&tkey->refs, NULL);
1835*00b67f09SDavid van Moolenbroek 
1836*00b67f09SDavid van Moolenbroek 	return (result);
1837*00b67f09SDavid van Moolenbroek }
1838*00b67f09SDavid van Moolenbroek 
1839*00b67f09SDavid van Moolenbroek void
dns_tsigkeyring_attach(dns_tsig_keyring_t * source,dns_tsig_keyring_t ** target)1840*00b67f09SDavid van Moolenbroek dns_tsigkeyring_attach(dns_tsig_keyring_t *source, dns_tsig_keyring_t **target)
1841*00b67f09SDavid van Moolenbroek {
1842*00b67f09SDavid van Moolenbroek 	REQUIRE(source != NULL);
1843*00b67f09SDavid van Moolenbroek 	REQUIRE(target != NULL && *target == NULL);
1844*00b67f09SDavid van Moolenbroek 
1845*00b67f09SDavid van Moolenbroek 	RWLOCK(&source->lock, isc_rwlocktype_write);
1846*00b67f09SDavid van Moolenbroek 	INSIST(source->references > 0);
1847*00b67f09SDavid van Moolenbroek 	source->references++;
1848*00b67f09SDavid van Moolenbroek 	INSIST(source->references > 0);
1849*00b67f09SDavid van Moolenbroek 	*target = source;
1850*00b67f09SDavid van Moolenbroek 	RWUNLOCK(&source->lock, isc_rwlocktype_write);
1851*00b67f09SDavid van Moolenbroek }
1852*00b67f09SDavid van Moolenbroek 
1853*00b67f09SDavid van Moolenbroek void
dns_tsigkeyring_detach(dns_tsig_keyring_t ** ringp)1854*00b67f09SDavid van Moolenbroek dns_tsigkeyring_detach(dns_tsig_keyring_t **ringp) {
1855*00b67f09SDavid van Moolenbroek 	dns_tsig_keyring_t *ring;
1856*00b67f09SDavid van Moolenbroek 	unsigned int references;
1857*00b67f09SDavid van Moolenbroek 
1858*00b67f09SDavid van Moolenbroek 	REQUIRE(ringp != NULL);
1859*00b67f09SDavid van Moolenbroek 	REQUIRE(*ringp != NULL);
1860*00b67f09SDavid van Moolenbroek 
1861*00b67f09SDavid van Moolenbroek 	ring = *ringp;
1862*00b67f09SDavid van Moolenbroek 	*ringp = NULL;
1863*00b67f09SDavid van Moolenbroek 
1864*00b67f09SDavid van Moolenbroek 	RWLOCK(&ring->lock, isc_rwlocktype_write);
1865*00b67f09SDavid van Moolenbroek 	INSIST(ring->references > 0);
1866*00b67f09SDavid van Moolenbroek 	ring->references--;
1867*00b67f09SDavid van Moolenbroek 	references = ring->references;
1868*00b67f09SDavid van Moolenbroek 	RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1869*00b67f09SDavid van Moolenbroek 
1870*00b67f09SDavid van Moolenbroek 	if (references == 0)
1871*00b67f09SDavid van Moolenbroek 		destroyring(ring);
1872*00b67f09SDavid van Moolenbroek }
1873*00b67f09SDavid van Moolenbroek 
1874*00b67f09SDavid van Moolenbroek void
dns_keyring_restore(dns_tsig_keyring_t * ring,FILE * fp)1875*00b67f09SDavid van Moolenbroek dns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp) {
1876*00b67f09SDavid van Moolenbroek 	isc_stdtime_t now;
1877*00b67f09SDavid van Moolenbroek 	isc_result_t result;
1878*00b67f09SDavid van Moolenbroek 
1879*00b67f09SDavid van Moolenbroek 	isc_stdtime_get(&now);
1880*00b67f09SDavid van Moolenbroek 	do {
1881*00b67f09SDavid van Moolenbroek 		result = restore_key(ring, now, fp);
1882*00b67f09SDavid van Moolenbroek 		if (result == ISC_R_NOMORE)
1883*00b67f09SDavid van Moolenbroek 			return;
1884*00b67f09SDavid van Moolenbroek 		if (result == DNS_R_BADALG || result == DNS_R_EXPIRED)
1885*00b67f09SDavid van Moolenbroek 			result = ISC_R_SUCCESS;
1886*00b67f09SDavid van Moolenbroek 	} while (result == ISC_R_SUCCESS);
1887*00b67f09SDavid van Moolenbroek }
1888