xref: /netbsd-src/external/mpl/bind/dist/lib/dns/tkey.c (revision aef5eb5f59cdfe8314f1b5f78ac04eb144e44010)
1 /*	$NetBSD: tkey.c,v 1.12 2022/09/23 12:15:30 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <inttypes.h>
19 #include <stdbool.h>
20 
21 #include <isc/buffer.h>
22 #include <isc/md.h>
23 #include <isc/mem.h>
24 #include <isc/nonce.h>
25 #include <isc/print.h>
26 #include <isc/random.h>
27 #include <isc/string.h>
28 #include <isc/util.h>
29 
30 #include <pk11/site.h>
31 
32 #include <dns/dnssec.h>
33 #include <dns/fixedname.h>
34 #include <dns/keyvalues.h>
35 #include <dns/log.h>
36 #include <dns/message.h>
37 #include <dns/name.h>
38 #include <dns/rdata.h>
39 #include <dns/rdatalist.h>
40 #include <dns/rdataset.h>
41 #include <dns/rdatastruct.h>
42 #include <dns/result.h>
43 #include <dns/tkey.h>
44 #include <dns/tsig.h>
45 
46 #include <dst/dst.h>
47 #include <dst/gssapi.h>
48 
49 #include "dst_internal.h"
50 
51 #define TEMP_BUFFER_SZ	   8192
52 #define TKEY_RANDOM_AMOUNT 16
53 
54 #if USE_PKCS11
55 #include <pk11/pk11.h>
56 #endif /* if USE_PKCS11 */
57 
58 #define RETERR(x)                            \
59 	do {                                 \
60 		result = (x);                \
61 		if (result != ISC_R_SUCCESS) \
62 			goto failure;        \
63 	} while (0)
64 
65 static void
66 tkey_log(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2);
67 
68 static void
69 tkey_log(const char *fmt, ...) {
70 	va_list ap;
71 
72 	va_start(ap, fmt);
73 	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_REQUEST,
74 		       ISC_LOG_DEBUG(4), fmt, ap);
75 	va_end(ap);
76 }
77 
78 static void
79 dumpmessage(dns_message_t *msg) {
80 	isc_buffer_t outbuf;
81 	unsigned char *output;
82 	int len = TEMP_BUFFER_SZ;
83 	isc_result_t result;
84 
85 	for (;;) {
86 		output = isc_mem_get(msg->mctx, len);
87 
88 		isc_buffer_init(&outbuf, output, len);
89 		result = dns_message_totext(msg, &dns_master_style_debug, 0,
90 					    &outbuf);
91 		if (result == ISC_R_NOSPACE) {
92 			isc_mem_put(msg->mctx, output, len);
93 			len *= 2;
94 			continue;
95 		}
96 
97 		if (result == ISC_R_SUCCESS) {
98 			tkey_log("%.*s", (int)isc_buffer_usedlength(&outbuf),
99 				 (char *)isc_buffer_base(&outbuf));
100 		} else {
101 			tkey_log("Warning: dns_message_totext: %s",
102 				 dns_result_totext(result));
103 		}
104 		break;
105 	}
106 
107 	if (output != NULL) {
108 		isc_mem_put(msg->mctx, output, len);
109 	}
110 }
111 
112 isc_result_t
113 dns_tkeyctx_create(isc_mem_t *mctx, dns_tkeyctx_t **tctxp) {
114 	dns_tkeyctx_t *tctx;
115 
116 	REQUIRE(mctx != NULL);
117 	REQUIRE(tctxp != NULL && *tctxp == NULL);
118 
119 	tctx = isc_mem_get(mctx, sizeof(dns_tkeyctx_t));
120 	tctx->mctx = NULL;
121 	isc_mem_attach(mctx, &tctx->mctx);
122 	tctx->dhkey = NULL;
123 	tctx->domain = NULL;
124 	tctx->gsscred = NULL;
125 	tctx->gssapi_keytab = NULL;
126 
127 	*tctxp = tctx;
128 	return (ISC_R_SUCCESS);
129 }
130 
131 void
132 dns_tkeyctx_destroy(dns_tkeyctx_t **tctxp) {
133 	isc_mem_t *mctx;
134 	dns_tkeyctx_t *tctx;
135 
136 	REQUIRE(tctxp != NULL && *tctxp != NULL);
137 
138 	tctx = *tctxp;
139 	*tctxp = NULL;
140 	mctx = tctx->mctx;
141 
142 	if (tctx->dhkey != NULL) {
143 		dst_key_free(&tctx->dhkey);
144 	}
145 	if (tctx->domain != NULL) {
146 		if (dns_name_dynamic(tctx->domain)) {
147 			dns_name_free(tctx->domain, mctx);
148 		}
149 		isc_mem_put(mctx, tctx->domain, sizeof(dns_name_t));
150 	}
151 	if (tctx->gssapi_keytab != NULL) {
152 		isc_mem_free(mctx, tctx->gssapi_keytab);
153 	}
154 	if (tctx->gsscred != NULL) {
155 		dst_gssapi_releasecred(&tctx->gsscred);
156 	}
157 	isc_mem_putanddetach(&mctx, tctx, sizeof(dns_tkeyctx_t));
158 }
159 
160 static isc_result_t
161 add_rdata_to_list(dns_message_t *msg, dns_name_t *name, dns_rdata_t *rdata,
162 		  uint32_t ttl, dns_namelist_t *namelist) {
163 	isc_result_t result;
164 	isc_region_t r, newr;
165 	dns_rdata_t *newrdata = NULL;
166 	dns_name_t *newname = NULL;
167 	dns_rdatalist_t *newlist = NULL;
168 	dns_rdataset_t *newset = NULL;
169 	isc_buffer_t *tmprdatabuf = NULL;
170 
171 	RETERR(dns_message_gettemprdata(msg, &newrdata));
172 
173 	dns_rdata_toregion(rdata, &r);
174 	isc_buffer_allocate(msg->mctx, &tmprdatabuf, r.length);
175 	isc_buffer_availableregion(tmprdatabuf, &newr);
176 	memmove(newr.base, r.base, r.length);
177 	dns_rdata_fromregion(newrdata, rdata->rdclass, rdata->type, &newr);
178 	dns_message_takebuffer(msg, &tmprdatabuf);
179 
180 	RETERR(dns_message_gettempname(msg, &newname));
181 	dns_name_copynf(name, newname);
182 
183 	RETERR(dns_message_gettemprdatalist(msg, &newlist));
184 	newlist->rdclass = newrdata->rdclass;
185 	newlist->type = newrdata->type;
186 	newlist->ttl = ttl;
187 	ISC_LIST_APPEND(newlist->rdata, newrdata, link);
188 
189 	RETERR(dns_message_gettemprdataset(msg, &newset));
190 	RETERR(dns_rdatalist_tordataset(newlist, newset));
191 
192 	ISC_LIST_INIT(newname->list);
193 	ISC_LIST_APPEND(newname->list, newset, link);
194 
195 	ISC_LIST_APPEND(*namelist, newname, link);
196 
197 	return (ISC_R_SUCCESS);
198 
199 failure:
200 	if (newrdata != NULL) {
201 		if (ISC_LINK_LINKED(newrdata, link)) {
202 			INSIST(newlist != NULL);
203 			ISC_LIST_UNLINK(newlist->rdata, newrdata, link);
204 		}
205 		dns_message_puttemprdata(msg, &newrdata);
206 	}
207 	if (newname != NULL) {
208 		dns_message_puttempname(msg, &newname);
209 	}
210 	if (newset != NULL) {
211 		dns_rdataset_disassociate(newset);
212 		dns_message_puttemprdataset(msg, &newset);
213 	}
214 	if (newlist != NULL) {
215 		dns_message_puttemprdatalist(msg, &newlist);
216 	}
217 	return (result);
218 }
219 
220 static void
221 free_namelist(dns_message_t *msg, dns_namelist_t *namelist) {
222 	dns_name_t *name;
223 	dns_rdataset_t *set;
224 
225 	while (!ISC_LIST_EMPTY(*namelist)) {
226 		name = ISC_LIST_HEAD(*namelist);
227 		ISC_LIST_UNLINK(*namelist, name, link);
228 		while (!ISC_LIST_EMPTY(name->list)) {
229 			set = ISC_LIST_HEAD(name->list);
230 			ISC_LIST_UNLINK(name->list, set, link);
231 			if (dns_rdataset_isassociated(set)) {
232 				dns_rdataset_disassociate(set);
233 			}
234 			dns_message_puttemprdataset(msg, &set);
235 		}
236 		dns_message_puttempname(msg, &name);
237 	}
238 }
239 
240 static isc_result_t
241 compute_secret(isc_buffer_t *shared, isc_region_t *queryrandomness,
242 	       isc_region_t *serverrandomness, isc_buffer_t *secret) {
243 	isc_md_t *md;
244 	isc_region_t r, r2;
245 	unsigned char digests[ISC_MAX_MD_SIZE * 2];
246 	unsigned char *digest1, *digest2;
247 	unsigned int digestslen, digestlen1 = 0, digestlen2 = 0;
248 	unsigned int i;
249 	isc_result_t result;
250 
251 	isc_buffer_usedregion(shared, &r);
252 
253 	md = isc_md_new();
254 	if (md == NULL) {
255 		return (ISC_R_NOSPACE);
256 	}
257 
258 	/*
259 	 * MD5 ( query data | DH value ).
260 	 */
261 	digest1 = digests;
262 
263 	result = isc_md_init(md, ISC_MD_MD5);
264 	if (result != ISC_R_SUCCESS) {
265 		goto end;
266 	}
267 
268 	result = isc_md_update(md, queryrandomness->base,
269 			       queryrandomness->length);
270 	if (result != ISC_R_SUCCESS) {
271 		goto end;
272 	}
273 
274 	result = isc_md_update(md, r.base, r.length);
275 	if (result != ISC_R_SUCCESS) {
276 		goto end;
277 	}
278 
279 	result = isc_md_final(md, digest1, &digestlen1);
280 	if (result != ISC_R_SUCCESS) {
281 		goto end;
282 	}
283 
284 	result = isc_md_reset(md);
285 	if (result != ISC_R_SUCCESS) {
286 		goto end;
287 	}
288 
289 	/*
290 	 * MD5 ( server data | DH value ).
291 	 */
292 	digest2 = digests + digestlen1;
293 
294 	result = isc_md_init(md, ISC_MD_MD5);
295 	if (result != ISC_R_SUCCESS) {
296 		goto end;
297 	}
298 
299 	result = isc_md_update(md, serverrandomness->base,
300 			       serverrandomness->length);
301 	if (result != ISC_R_SUCCESS) {
302 		goto end;
303 	}
304 
305 	result = isc_md_update(md, r.base, r.length);
306 	if (result != ISC_R_SUCCESS) {
307 		goto end;
308 	}
309 
310 	result = isc_md_final(md, digest2, &digestlen2);
311 	if (result != ISC_R_SUCCESS) {
312 		goto end;
313 	}
314 
315 	isc_md_free(md);
316 	md = NULL;
317 
318 	digestslen = digestlen1 + digestlen2;
319 
320 	/*
321 	 * XOR ( DH value, MD5-1 | MD5-2).
322 	 */
323 	isc_buffer_availableregion(secret, &r);
324 	isc_buffer_usedregion(shared, &r2);
325 	if (r.length < digestslen || r.length < r2.length) {
326 		return (ISC_R_NOSPACE);
327 	}
328 	if (r2.length > digestslen) {
329 		memmove(r.base, r2.base, r2.length);
330 		for (i = 0; i < digestslen; i++) {
331 			r.base[i] ^= digests[i];
332 		}
333 		isc_buffer_add(secret, r2.length);
334 	} else {
335 		memmove(r.base, digests, digestslen);
336 		for (i = 0; i < r2.length; i++) {
337 			r.base[i] ^= r2.base[i];
338 		}
339 		isc_buffer_add(secret, digestslen);
340 	}
341 	result = ISC_R_SUCCESS;
342 end:
343 	if (md != NULL) {
344 		isc_md_free(md);
345 	}
346 	return (result);
347 }
348 
349 static isc_result_t
350 process_dhtkey(dns_message_t *msg, dns_name_t *signer, dns_name_t *name,
351 	       dns_rdata_tkey_t *tkeyin, dns_tkeyctx_t *tctx,
352 	       dns_rdata_tkey_t *tkeyout, dns_tsig_keyring_t *ring,
353 	       dns_namelist_t *namelist) {
354 	isc_result_t result = ISC_R_SUCCESS;
355 	dns_name_t *keyname, ourname;
356 	dns_rdataset_t *keyset = NULL;
357 	dns_rdata_t keyrdata = DNS_RDATA_INIT, ourkeyrdata = DNS_RDATA_INIT;
358 	bool found_key = false, found_incompatible = false;
359 	dst_key_t *pubkey = NULL;
360 	isc_buffer_t ourkeybuf, *shared = NULL;
361 	isc_region_t r, r2, ourkeyr;
362 	unsigned char keydata[DST_KEY_MAXSIZE];
363 	unsigned int sharedsize;
364 	isc_buffer_t secret;
365 	unsigned char *randomdata = NULL, secretdata[256];
366 	dns_ttl_t ttl = 0;
367 
368 	if (tctx->dhkey == NULL) {
369 		tkey_log("process_dhtkey: tkey-dhkey not defined");
370 		tkeyout->error = dns_tsigerror_badalg;
371 		return (DNS_R_REFUSED);
372 	}
373 
374 	if (!dns_name_equal(&tkeyin->algorithm, DNS_TSIG_HMACMD5_NAME)) {
375 		tkey_log("process_dhtkey: algorithms other than "
376 			 "hmac-md5 are not supported");
377 		tkeyout->error = dns_tsigerror_badalg;
378 		return (ISC_R_SUCCESS);
379 	}
380 
381 	/*
382 	 * Look for a DH KEY record that will work with ours.
383 	 */
384 	for (result = dns_message_firstname(msg, DNS_SECTION_ADDITIONAL);
385 	     result == ISC_R_SUCCESS && !found_key;
386 	     result = dns_message_nextname(msg, DNS_SECTION_ADDITIONAL))
387 	{
388 		keyname = NULL;
389 		dns_message_currentname(msg, DNS_SECTION_ADDITIONAL, &keyname);
390 		keyset = NULL;
391 		result = dns_message_findtype(keyname, dns_rdatatype_key, 0,
392 					      &keyset);
393 		if (result != ISC_R_SUCCESS) {
394 			continue;
395 		}
396 
397 		for (result = dns_rdataset_first(keyset);
398 		     result == ISC_R_SUCCESS && !found_key;
399 		     result = dns_rdataset_next(keyset))
400 		{
401 			dns_rdataset_current(keyset, &keyrdata);
402 			pubkey = NULL;
403 			result = dns_dnssec_keyfromrdata(keyname, &keyrdata,
404 							 msg->mctx, &pubkey);
405 			if (result != ISC_R_SUCCESS) {
406 				dns_rdata_reset(&keyrdata);
407 				continue;
408 			}
409 			if (dst_key_alg(pubkey) == DNS_KEYALG_DH) {
410 				if (dst_key_paramcompare(pubkey, tctx->dhkey)) {
411 					found_key = true;
412 					ttl = keyset->ttl;
413 					break;
414 				} else {
415 					found_incompatible = true;
416 				}
417 			}
418 			dst_key_free(&pubkey);
419 			dns_rdata_reset(&keyrdata);
420 		}
421 	}
422 
423 	if (!found_key) {
424 		if (found_incompatible) {
425 			tkey_log("process_dhtkey: found an incompatible key");
426 			tkeyout->error = dns_tsigerror_badkey;
427 			return (ISC_R_SUCCESS);
428 		} else {
429 			tkey_log("process_dhtkey: failed to find a key");
430 			return (DNS_R_FORMERR);
431 		}
432 	}
433 
434 	RETERR(add_rdata_to_list(msg, keyname, &keyrdata, ttl, namelist));
435 
436 	isc_buffer_init(&ourkeybuf, keydata, sizeof(keydata));
437 	RETERR(dst_key_todns(tctx->dhkey, &ourkeybuf));
438 	isc_buffer_usedregion(&ourkeybuf, &ourkeyr);
439 	dns_rdata_fromregion(&ourkeyrdata, dns_rdataclass_any,
440 			     dns_rdatatype_key, &ourkeyr);
441 
442 	dns_name_init(&ourname, NULL);
443 	dns_name_clone(dst_key_name(tctx->dhkey), &ourname);
444 
445 	/*
446 	 * XXXBEW The TTL should be obtained from the database, if it exists.
447 	 */
448 	RETERR(add_rdata_to_list(msg, &ourname, &ourkeyrdata, 0, namelist));
449 
450 	RETERR(dst_key_secretsize(tctx->dhkey, &sharedsize));
451 	isc_buffer_allocate(msg->mctx, &shared, sharedsize);
452 
453 	result = dst_key_computesecret(pubkey, tctx->dhkey, shared);
454 	if (result != ISC_R_SUCCESS) {
455 		tkey_log("process_dhtkey: failed to compute shared secret: %s",
456 			 isc_result_totext(result));
457 		goto failure;
458 	}
459 	dst_key_free(&pubkey);
460 
461 	isc_buffer_init(&secret, secretdata, sizeof(secretdata));
462 
463 	randomdata = isc_mem_get(tkeyout->mctx, TKEY_RANDOM_AMOUNT);
464 
465 	isc_nonce_buf(randomdata, TKEY_RANDOM_AMOUNT);
466 
467 	r.base = randomdata;
468 	r.length = TKEY_RANDOM_AMOUNT;
469 	r2.base = tkeyin->key;
470 	r2.length = tkeyin->keylen;
471 	RETERR(compute_secret(shared, &r2, &r, &secret));
472 	isc_buffer_free(&shared);
473 
474 	RETERR(dns_tsigkey_create(
475 		name, &tkeyin->algorithm, isc_buffer_base(&secret),
476 		isc_buffer_usedlength(&secret), true, signer, tkeyin->inception,
477 		tkeyin->expire, ring->mctx, ring, NULL));
478 
479 	/* This key is good for a long time */
480 	tkeyout->inception = tkeyin->inception;
481 	tkeyout->expire = tkeyin->expire;
482 
483 	tkeyout->key = randomdata;
484 	tkeyout->keylen = TKEY_RANDOM_AMOUNT;
485 
486 	return (ISC_R_SUCCESS);
487 
488 failure:
489 	if (!ISC_LIST_EMPTY(*namelist)) {
490 		free_namelist(msg, namelist);
491 	}
492 	if (shared != NULL) {
493 		isc_buffer_free(&shared);
494 	}
495 	if (pubkey != NULL) {
496 		dst_key_free(&pubkey);
497 	}
498 	if (randomdata != NULL) {
499 		isc_mem_put(tkeyout->mctx, randomdata, TKEY_RANDOM_AMOUNT);
500 	}
501 	return (result);
502 }
503 
504 static isc_result_t
505 process_gsstkey(dns_message_t *msg, dns_name_t *name, dns_rdata_tkey_t *tkeyin,
506 		dns_tkeyctx_t *tctx, dns_rdata_tkey_t *tkeyout,
507 		dns_tsig_keyring_t *ring) {
508 	isc_result_t result = ISC_R_SUCCESS;
509 	dst_key_t *dstkey = NULL;
510 	dns_tsigkey_t *tsigkey = NULL;
511 	dns_fixedname_t fixed;
512 	dns_name_t *principal;
513 	isc_stdtime_t now;
514 	isc_region_t intoken;
515 	isc_buffer_t *outtoken = NULL;
516 	dns_gss_ctx_id_t gss_ctx = NULL;
517 
518 	/*
519 	 * You have to define either a gss credential (principal) to
520 	 * accept with tkey-gssapi-credential, or you have to
521 	 * configure a specific keytab (with tkey-gssapi-keytab) in
522 	 * order to use gsstkey.
523 	 */
524 	if (tctx->gsscred == NULL && tctx->gssapi_keytab == NULL) {
525 		tkey_log("process_gsstkey(): no tkey-gssapi-credential "
526 			 "or tkey-gssapi-keytab configured");
527 		return (ISC_R_NOPERM);
528 	}
529 
530 	if (!dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPI_NAME) &&
531 	    !dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPIMS_NAME))
532 	{
533 		tkeyout->error = dns_tsigerror_badalg;
534 		tkey_log("process_gsstkey(): dns_tsigerror_badalg"); /* XXXSRA
535 								      */
536 		return (ISC_R_SUCCESS);
537 	}
538 
539 	/*
540 	 * XXXDCL need to check for key expiry per 4.1.1
541 	 * XXXDCL need a way to check fully established, perhaps w/key_flags
542 	 */
543 
544 	intoken.base = tkeyin->key;
545 	intoken.length = tkeyin->keylen;
546 
547 	result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
548 	if (result == ISC_R_SUCCESS) {
549 		gss_ctx = dst_key_getgssctx(tsigkey->key);
550 	}
551 
552 	principal = dns_fixedname_initname(&fixed);
553 
554 	/*
555 	 * Note that tctx->gsscred may be NULL if tctx->gssapi_keytab is set
556 	 */
557 	result = dst_gssapi_acceptctx(tctx->gsscred, tctx->gssapi_keytab,
558 				      &intoken, &outtoken, &gss_ctx, principal,
559 				      tctx->mctx);
560 	if (result == DNS_R_INVALIDTKEY) {
561 		if (tsigkey != NULL) {
562 			dns_tsigkey_detach(&tsigkey);
563 		}
564 		tkeyout->error = dns_tsigerror_badkey;
565 		tkey_log("process_gsstkey(): dns_tsigerror_badkey"); /* XXXSRA
566 								      */
567 		return (ISC_R_SUCCESS);
568 	}
569 	if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) {
570 		goto failure;
571 	}
572 	/*
573 	 * XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times.
574 	 */
575 
576 	isc_stdtime_get(&now);
577 
578 	if (dns_name_countlabels(principal) == 0U) {
579 		if (tsigkey != NULL) {
580 			dns_tsigkey_detach(&tsigkey);
581 		}
582 	} else if (tsigkey == NULL) {
583 #ifdef GSSAPI
584 		OM_uint32 gret, minor, lifetime;
585 #endif /* ifdef GSSAPI */
586 		uint32_t expire;
587 
588 		RETERR(dst_key_fromgssapi(name, gss_ctx, ring->mctx, &dstkey,
589 					  &intoken));
590 		/*
591 		 * Limit keys to 1 hour or the context's lifetime whichever
592 		 * is smaller.
593 		 */
594 		expire = now + 3600;
595 #ifdef GSSAPI
596 		gret = gss_context_time(&minor, gss_ctx, &lifetime);
597 		if (gret == GSS_S_COMPLETE && now + lifetime < expire) {
598 			expire = now + lifetime;
599 		}
600 #endif /* ifdef GSSAPI */
601 		RETERR(dns_tsigkey_createfromkey(
602 			name, &tkeyin->algorithm, dstkey, true, principal, now,
603 			expire, ring->mctx, ring, &tsigkey));
604 		dst_key_free(&dstkey);
605 		tkeyout->inception = now;
606 		tkeyout->expire = expire;
607 	} else {
608 		tkeyout->inception = tsigkey->inception;
609 		tkeyout->expire = tsigkey->expire;
610 	}
611 
612 	if (outtoken) {
613 		tkeyout->key = isc_mem_get(tkeyout->mctx,
614 					   isc_buffer_usedlength(outtoken));
615 		tkeyout->keylen = isc_buffer_usedlength(outtoken);
616 		memmove(tkeyout->key, isc_buffer_base(outtoken),
617 			isc_buffer_usedlength(outtoken));
618 		isc_buffer_free(&outtoken);
619 	} else {
620 		tkeyout->key = isc_mem_get(tkeyout->mctx, tkeyin->keylen);
621 		tkeyout->keylen = tkeyin->keylen;
622 		memmove(tkeyout->key, tkeyin->key, tkeyin->keylen);
623 	}
624 
625 	tkeyout->error = dns_rcode_noerror;
626 
627 	tkey_log("process_gsstkey(): dns_tsigerror_noerror"); /* XXXSRA */
628 
629 	/*
630 	 * We found a TKEY to respond with.  If the request is not TSIG signed,
631 	 * we need to make sure the response is signed (see RFC 3645, Section
632 	 * 2.2).
633 	 */
634 	if (tsigkey != NULL) {
635 		if (msg->tsigkey == NULL && msg->sig0key == NULL) {
636 			dns_message_settsigkey(msg, tsigkey);
637 		}
638 		dns_tsigkey_detach(&tsigkey);
639 	}
640 
641 	return (ISC_R_SUCCESS);
642 
643 failure:
644 	if (tsigkey != NULL) {
645 		dns_tsigkey_detach(&tsigkey);
646 	}
647 
648 	if (dstkey != NULL) {
649 		dst_key_free(&dstkey);
650 	}
651 
652 	if (outtoken != NULL) {
653 		isc_buffer_free(&outtoken);
654 	}
655 
656 	tkey_log("process_gsstkey(): %s", isc_result_totext(result)); /* XXXSRA
657 								       */
658 
659 	return (result);
660 }
661 
662 static isc_result_t
663 process_deletetkey(dns_name_t *signer, dns_name_t *name,
664 		   dns_rdata_tkey_t *tkeyin, dns_rdata_tkey_t *tkeyout,
665 		   dns_tsig_keyring_t *ring) {
666 	isc_result_t result;
667 	dns_tsigkey_t *tsigkey = NULL;
668 	const dns_name_t *identity;
669 
670 	result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
671 	if (result != ISC_R_SUCCESS) {
672 		tkeyout->error = dns_tsigerror_badname;
673 		return (ISC_R_SUCCESS);
674 	}
675 
676 	/*
677 	 * Only allow a delete if the identity that created the key is the
678 	 * same as the identity that signed the message.
679 	 */
680 	identity = dns_tsigkey_identity(tsigkey);
681 	if (identity == NULL || !dns_name_equal(identity, signer)) {
682 		dns_tsigkey_detach(&tsigkey);
683 		return (DNS_R_REFUSED);
684 	}
685 
686 	/*
687 	 * Set the key to be deleted when no references are left.  If the key
688 	 * was not generated with TKEY and is in the config file, it may be
689 	 * reloaded later.
690 	 */
691 	dns_tsigkey_setdeleted(tsigkey);
692 
693 	/* Release the reference */
694 	dns_tsigkey_detach(&tsigkey);
695 
696 	return (ISC_R_SUCCESS);
697 }
698 
699 isc_result_t
700 dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
701 		      dns_tsig_keyring_t *ring) {
702 	isc_result_t result = ISC_R_SUCCESS;
703 	dns_rdata_tkey_t tkeyin, tkeyout;
704 	bool freetkeyin = false;
705 	dns_name_t *qname, *name, *keyname, *signer, tsigner;
706 	dns_fixedname_t fkeyname;
707 	dns_rdataset_t *tkeyset;
708 	dns_rdata_t rdata;
709 	dns_namelist_t namelist;
710 	char tkeyoutdata[512];
711 	isc_buffer_t tkeyoutbuf;
712 
713 	REQUIRE(msg != NULL);
714 	REQUIRE(tctx != NULL);
715 	REQUIRE(ring != NULL);
716 
717 	ISC_LIST_INIT(namelist);
718 
719 	/*
720 	 * Interpret the question section.
721 	 */
722 	result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
723 	if (result != ISC_R_SUCCESS) {
724 		return (DNS_R_FORMERR);
725 	}
726 
727 	qname = NULL;
728 	dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname);
729 
730 	/*
731 	 * Look for a TKEY record that matches the question.
732 	 */
733 	tkeyset = NULL;
734 	name = NULL;
735 	result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname,
736 				      dns_rdatatype_tkey, 0, &name, &tkeyset);
737 	if (result != ISC_R_SUCCESS) {
738 		/*
739 		 * Try the answer section, since that's where Win2000
740 		 * puts it.
741 		 */
742 		name = NULL;
743 		if (dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
744 					 dns_rdatatype_tkey, 0, &name,
745 					 &tkeyset) != ISC_R_SUCCESS)
746 		{
747 			result = DNS_R_FORMERR;
748 			tkey_log("dns_tkey_processquery: couldn't find a TKEY "
749 				 "matching the question");
750 			goto failure;
751 		}
752 	}
753 	result = dns_rdataset_first(tkeyset);
754 	if (result != ISC_R_SUCCESS) {
755 		result = DNS_R_FORMERR;
756 		goto failure;
757 	}
758 	dns_rdata_init(&rdata);
759 	dns_rdataset_current(tkeyset, &rdata);
760 
761 	RETERR(dns_rdata_tostruct(&rdata, &tkeyin, NULL));
762 	freetkeyin = true;
763 
764 	if (tkeyin.error != dns_rcode_noerror) {
765 		result = DNS_R_FORMERR;
766 		goto failure;
767 	}
768 
769 	/*
770 	 * Before we go any farther, verify that the message was signed.
771 	 * GSSAPI TKEY doesn't require a signature, the rest do.
772 	 */
773 	dns_name_init(&tsigner, NULL);
774 	result = dns_message_signer(msg, &tsigner);
775 	if (result != ISC_R_SUCCESS) {
776 		if (tkeyin.mode == DNS_TKEYMODE_GSSAPI &&
777 		    result == ISC_R_NOTFOUND) {
778 			signer = NULL;
779 		} else {
780 			tkey_log("dns_tkey_processquery: query was not "
781 				 "properly signed - rejecting");
782 			result = DNS_R_FORMERR;
783 			goto failure;
784 		}
785 	} else {
786 		signer = &tsigner;
787 	}
788 
789 	tkeyout.common.rdclass = tkeyin.common.rdclass;
790 	tkeyout.common.rdtype = tkeyin.common.rdtype;
791 	ISC_LINK_INIT(&tkeyout.common, link);
792 	tkeyout.mctx = msg->mctx;
793 
794 	dns_name_init(&tkeyout.algorithm, NULL);
795 	dns_name_clone(&tkeyin.algorithm, &tkeyout.algorithm);
796 
797 	tkeyout.inception = tkeyout.expire = 0;
798 	tkeyout.mode = tkeyin.mode;
799 	tkeyout.error = 0;
800 	tkeyout.keylen = tkeyout.otherlen = 0;
801 	tkeyout.key = tkeyout.other = NULL;
802 
803 	/*
804 	 * A delete operation must have a fully specified key name.  If this
805 	 * is not a delete, we do the following:
806 	 * if (qname != ".")
807 	 *	keyname = qname + defaultdomain
808 	 * else
809 	 *	keyname = <random hex> + defaultdomain
810 	 */
811 	if (tkeyin.mode != DNS_TKEYMODE_DELETE) {
812 		dns_tsigkey_t *tsigkey = NULL;
813 
814 		if (tctx->domain == NULL && tkeyin.mode != DNS_TKEYMODE_GSSAPI)
815 		{
816 			tkey_log("dns_tkey_processquery: tkey-domain not set");
817 			result = DNS_R_REFUSED;
818 			goto failure;
819 		}
820 
821 		keyname = dns_fixedname_initname(&fkeyname);
822 
823 		if (!dns_name_equal(qname, dns_rootname)) {
824 			unsigned int n = dns_name_countlabels(qname);
825 			dns_name_copynf(qname, keyname);
826 			dns_name_getlabelsequence(keyname, 0, n - 1, keyname);
827 		} else {
828 			static char hexdigits[16] = { '0', '1', '2', '3',
829 						      '4', '5', '6', '7',
830 						      '8', '9', 'A', 'B',
831 						      'C', 'D', 'E', 'F' };
832 			unsigned char randomdata[16];
833 			char randomtext[32];
834 			isc_buffer_t b;
835 			unsigned int i, j;
836 
837 			isc_nonce_buf(randomdata, sizeof(randomdata));
838 
839 			for (i = 0, j = 0; i < sizeof(randomdata); i++) {
840 				unsigned char val = randomdata[i];
841 				randomtext[j++] = hexdigits[val >> 4];
842 				randomtext[j++] = hexdigits[val & 0xF];
843 			}
844 			isc_buffer_init(&b, randomtext, sizeof(randomtext));
845 			isc_buffer_add(&b, sizeof(randomtext));
846 			result = dns_name_fromtext(keyname, &b, NULL, 0, NULL);
847 			if (result != ISC_R_SUCCESS) {
848 				goto failure;
849 			}
850 		}
851 
852 		if (tkeyin.mode == DNS_TKEYMODE_GSSAPI) {
853 			/* Yup.  This is a hack */
854 			result = dns_name_concatenate(keyname, dns_rootname,
855 						      keyname, NULL);
856 			if (result != ISC_R_SUCCESS) {
857 				goto failure;
858 			}
859 		} else {
860 			result = dns_name_concatenate(keyname, tctx->domain,
861 						      keyname, NULL);
862 			if (result != ISC_R_SUCCESS) {
863 				goto failure;
864 			}
865 		}
866 
867 		result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring);
868 
869 		if (result == ISC_R_SUCCESS) {
870 			tkeyout.error = dns_tsigerror_badname;
871 			dns_tsigkey_detach(&tsigkey);
872 			goto failure_with_tkey;
873 		} else if (result != ISC_R_NOTFOUND) {
874 			goto failure;
875 		}
876 	} else {
877 		keyname = qname;
878 	}
879 
880 	switch (tkeyin.mode) {
881 	case DNS_TKEYMODE_DIFFIEHELLMAN:
882 		tkeyout.error = dns_rcode_noerror;
883 		RETERR(process_dhtkey(msg, signer, keyname, &tkeyin, tctx,
884 				      &tkeyout, ring, &namelist));
885 		break;
886 	case DNS_TKEYMODE_GSSAPI:
887 		tkeyout.error = dns_rcode_noerror;
888 		RETERR(process_gsstkey(msg, keyname, &tkeyin, tctx, &tkeyout,
889 				       ring));
890 		break;
891 	case DNS_TKEYMODE_DELETE:
892 		tkeyout.error = dns_rcode_noerror;
893 		RETERR(process_deletetkey(signer, keyname, &tkeyin, &tkeyout,
894 					  ring));
895 		break;
896 	case DNS_TKEYMODE_SERVERASSIGNED:
897 	case DNS_TKEYMODE_RESOLVERASSIGNED:
898 		result = DNS_R_NOTIMP;
899 		goto failure;
900 	default:
901 		tkeyout.error = dns_tsigerror_badmode;
902 	}
903 
904 failure_with_tkey:
905 
906 	dns_rdata_init(&rdata);
907 	isc_buffer_init(&tkeyoutbuf, tkeyoutdata, sizeof(tkeyoutdata));
908 	result = dns_rdata_fromstruct(&rdata, tkeyout.common.rdclass,
909 				      tkeyout.common.rdtype, &tkeyout,
910 				      &tkeyoutbuf);
911 
912 	if (freetkeyin) {
913 		dns_rdata_freestruct(&tkeyin);
914 		freetkeyin = false;
915 	}
916 
917 	if (tkeyout.key != NULL) {
918 		isc_mem_put(tkeyout.mctx, tkeyout.key, tkeyout.keylen);
919 	}
920 	if (tkeyout.other != NULL) {
921 		isc_mem_put(tkeyout.mctx, tkeyout.other, tkeyout.otherlen);
922 	}
923 	if (result != ISC_R_SUCCESS) {
924 		goto failure;
925 	}
926 
927 	RETERR(add_rdata_to_list(msg, keyname, &rdata, 0, &namelist));
928 
929 	RETERR(dns_message_reply(msg, true));
930 
931 	name = ISC_LIST_HEAD(namelist);
932 	while (name != NULL) {
933 		dns_name_t *next = ISC_LIST_NEXT(name, link);
934 		ISC_LIST_UNLINK(namelist, name, link);
935 		dns_message_addname(msg, name, DNS_SECTION_ANSWER);
936 		name = next;
937 	}
938 
939 	return (ISC_R_SUCCESS);
940 
941 failure:
942 
943 	if (freetkeyin) {
944 		dns_rdata_freestruct(&tkeyin);
945 	}
946 	if (!ISC_LIST_EMPTY(namelist)) {
947 		free_namelist(msg, &namelist);
948 	}
949 	return (result);
950 }
951 
952 static isc_result_t
953 buildquery(dns_message_t *msg, const dns_name_t *name, dns_rdata_tkey_t *tkey,
954 	   bool win2k) {
955 	dns_name_t *qname = NULL, *aname = NULL;
956 	dns_rdataset_t *question = NULL, *tkeyset = NULL;
957 	dns_rdatalist_t *tkeylist = NULL;
958 	dns_rdata_t *rdata = NULL;
959 	isc_buffer_t *dynbuf = NULL;
960 	isc_result_t result;
961 	unsigned int len;
962 
963 	REQUIRE(msg != NULL);
964 	REQUIRE(name != NULL);
965 	REQUIRE(tkey != NULL);
966 
967 	RETERR(dns_message_gettempname(msg, &qname));
968 	RETERR(dns_message_gettempname(msg, &aname));
969 
970 	RETERR(dns_message_gettemprdataset(msg, &question));
971 	dns_rdataset_makequestion(question, dns_rdataclass_any,
972 				  dns_rdatatype_tkey);
973 
974 	len = 16 + tkey->algorithm.length + tkey->keylen + tkey->otherlen;
975 	isc_buffer_allocate(msg->mctx, &dynbuf, len);
976 	RETERR(dns_message_gettemprdata(msg, &rdata));
977 
978 	RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any,
979 				    dns_rdatatype_tkey, tkey, dynbuf));
980 	dns_message_takebuffer(msg, &dynbuf);
981 
982 	RETERR(dns_message_gettemprdatalist(msg, &tkeylist));
983 	tkeylist->rdclass = dns_rdataclass_any;
984 	tkeylist->type = dns_rdatatype_tkey;
985 	ISC_LIST_APPEND(tkeylist->rdata, rdata, link);
986 
987 	RETERR(dns_message_gettemprdataset(msg, &tkeyset));
988 	RETERR(dns_rdatalist_tordataset(tkeylist, tkeyset));
989 
990 	dns_name_copynf(name, qname);
991 	dns_name_copynf(name, aname);
992 
993 	ISC_LIST_APPEND(qname->list, question, link);
994 	ISC_LIST_APPEND(aname->list, tkeyset, link);
995 
996 	dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
997 
998 	/*
999 	 * Windows 2000 needs this in the answer section, not the additional
1000 	 * section where the RFC specifies.
1001 	 */
1002 	if (win2k) {
1003 		dns_message_addname(msg, aname, DNS_SECTION_ANSWER);
1004 	} else {
1005 		dns_message_addname(msg, aname, DNS_SECTION_ADDITIONAL);
1006 	}
1007 
1008 	return (ISC_R_SUCCESS);
1009 
1010 failure:
1011 	if (qname != NULL) {
1012 		dns_message_puttempname(msg, &qname);
1013 	}
1014 	if (aname != NULL) {
1015 		dns_message_puttempname(msg, &aname);
1016 	}
1017 	if (question != NULL) {
1018 		dns_rdataset_disassociate(question);
1019 		dns_message_puttemprdataset(msg, &question);
1020 	}
1021 	if (dynbuf != NULL) {
1022 		isc_buffer_free(&dynbuf);
1023 	}
1024 	if (rdata != NULL) {
1025 		dns_message_puttemprdata(msg, &rdata);
1026 	}
1027 	if (tkeylist != NULL) {
1028 		dns_message_puttemprdatalist(msg, &tkeylist);
1029 	}
1030 	if (tkeyset != NULL) {
1031 		if (dns_rdataset_isassociated(tkeyset)) {
1032 			dns_rdataset_disassociate(tkeyset);
1033 		}
1034 		dns_message_puttemprdataset(msg, &tkeyset);
1035 	}
1036 	return (result);
1037 }
1038 
1039 isc_result_t
1040 dns_tkey_builddhquery(dns_message_t *msg, dst_key_t *key,
1041 		      const dns_name_t *name, const dns_name_t *algorithm,
1042 		      isc_buffer_t *nonce, uint32_t lifetime) {
1043 	dns_rdata_tkey_t tkey;
1044 	dns_rdata_t *rdata = NULL;
1045 	isc_buffer_t *dynbuf = NULL;
1046 	isc_region_t r;
1047 	dns_name_t keyname;
1048 	dns_namelist_t namelist;
1049 	isc_result_t result;
1050 	isc_stdtime_t now;
1051 	dns_name_t *item;
1052 
1053 	REQUIRE(msg != NULL);
1054 	REQUIRE(key != NULL);
1055 	REQUIRE(dst_key_alg(key) == DNS_KEYALG_DH);
1056 	REQUIRE(dst_key_isprivate(key));
1057 	REQUIRE(name != NULL);
1058 	REQUIRE(algorithm != NULL);
1059 
1060 	tkey.common.rdclass = dns_rdataclass_any;
1061 	tkey.common.rdtype = dns_rdatatype_tkey;
1062 	ISC_LINK_INIT(&tkey.common, link);
1063 	tkey.mctx = msg->mctx;
1064 	dns_name_init(&tkey.algorithm, NULL);
1065 	dns_name_clone(algorithm, &tkey.algorithm);
1066 	isc_stdtime_get(&now);
1067 	tkey.inception = now;
1068 	tkey.expire = now + lifetime;
1069 	tkey.mode = DNS_TKEYMODE_DIFFIEHELLMAN;
1070 	if (nonce != NULL) {
1071 		isc_buffer_usedregion(nonce, &r);
1072 	} else {
1073 		r.base = NULL;
1074 		r.length = 0;
1075 	}
1076 	tkey.error = 0;
1077 	tkey.key = r.base;
1078 	tkey.keylen = r.length;
1079 	tkey.other = NULL;
1080 	tkey.otherlen = 0;
1081 
1082 	RETERR(buildquery(msg, name, &tkey, false));
1083 
1084 	RETERR(dns_message_gettemprdata(msg, &rdata));
1085 	isc_buffer_allocate(msg->mctx, &dynbuf, 1024);
1086 	RETERR(dst_key_todns(key, dynbuf));
1087 	isc_buffer_usedregion(dynbuf, &r);
1088 	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_key, &r);
1089 	dns_message_takebuffer(msg, &dynbuf);
1090 
1091 	dns_name_init(&keyname, NULL);
1092 	dns_name_clone(dst_key_name(key), &keyname);
1093 
1094 	ISC_LIST_INIT(namelist);
1095 	RETERR(add_rdata_to_list(msg, &keyname, rdata, 0, &namelist));
1096 	item = ISC_LIST_HEAD(namelist);
1097 	while (item != NULL) {
1098 		dns_name_t *next = ISC_LIST_NEXT(item, link);
1099 		ISC_LIST_UNLINK(namelist, item, link);
1100 		dns_message_addname(msg, item, DNS_SECTION_ADDITIONAL);
1101 		item = next;
1102 	}
1103 
1104 	return (ISC_R_SUCCESS);
1105 
1106 failure:
1107 
1108 	if (dynbuf != NULL) {
1109 		isc_buffer_free(&dynbuf);
1110 	}
1111 	return (result);
1112 }
1113 
1114 isc_result_t
1115 dns_tkey_buildgssquery(dns_message_t *msg, const dns_name_t *name,
1116 		       const dns_name_t *gname, isc_buffer_t *intoken,
1117 		       uint32_t lifetime, dns_gss_ctx_id_t *context, bool win2k,
1118 		       isc_mem_t *mctx, char **err_message) {
1119 	dns_rdata_tkey_t tkey;
1120 	isc_result_t result;
1121 	isc_stdtime_t now;
1122 	isc_buffer_t token;
1123 	unsigned char array[TEMP_BUFFER_SZ];
1124 
1125 	UNUSED(intoken);
1126 
1127 	REQUIRE(msg != NULL);
1128 	REQUIRE(name != NULL);
1129 	REQUIRE(gname != NULL);
1130 	REQUIRE(context != NULL);
1131 	REQUIRE(mctx != NULL);
1132 
1133 	isc_buffer_init(&token, array, sizeof(array));
1134 	result = dst_gssapi_initctx(gname, NULL, &token, context, mctx,
1135 				    err_message);
1136 	if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) {
1137 		return (result);
1138 	}
1139 
1140 	tkey.common.rdclass = dns_rdataclass_any;
1141 	tkey.common.rdtype = dns_rdatatype_tkey;
1142 	ISC_LINK_INIT(&tkey.common, link);
1143 	tkey.mctx = NULL;
1144 	dns_name_init(&tkey.algorithm, NULL);
1145 
1146 	if (win2k) {
1147 		dns_name_clone(DNS_TSIG_GSSAPIMS_NAME, &tkey.algorithm);
1148 	} else {
1149 		dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm);
1150 	}
1151 
1152 	isc_stdtime_get(&now);
1153 	tkey.inception = now;
1154 	tkey.expire = now + lifetime;
1155 	tkey.mode = DNS_TKEYMODE_GSSAPI;
1156 	tkey.error = 0;
1157 	tkey.key = isc_buffer_base(&token);
1158 	tkey.keylen = isc_buffer_usedlength(&token);
1159 	tkey.other = NULL;
1160 	tkey.otherlen = 0;
1161 
1162 	return (buildquery(msg, name, &tkey, win2k));
1163 }
1164 
1165 isc_result_t
1166 dns_tkey_builddeletequery(dns_message_t *msg, dns_tsigkey_t *key) {
1167 	dns_rdata_tkey_t tkey;
1168 
1169 	REQUIRE(msg != NULL);
1170 	REQUIRE(key != NULL);
1171 
1172 	tkey.common.rdclass = dns_rdataclass_any;
1173 	tkey.common.rdtype = dns_rdatatype_tkey;
1174 	ISC_LINK_INIT(&tkey.common, link);
1175 	tkey.mctx = msg->mctx;
1176 	dns_name_init(&tkey.algorithm, NULL);
1177 	dns_name_clone(key->algorithm, &tkey.algorithm);
1178 	tkey.inception = tkey.expire = 0;
1179 	tkey.mode = DNS_TKEYMODE_DELETE;
1180 	tkey.error = 0;
1181 	tkey.keylen = tkey.otherlen = 0;
1182 	tkey.key = tkey.other = NULL;
1183 
1184 	return (buildquery(msg, &key->name, &tkey, false));
1185 }
1186 
1187 static isc_result_t
1188 find_tkey(dns_message_t *msg, dns_name_t **name, dns_rdata_t *rdata,
1189 	  int section) {
1190 	dns_rdataset_t *tkeyset;
1191 	isc_result_t result;
1192 
1193 	result = dns_message_firstname(msg, section);
1194 	while (result == ISC_R_SUCCESS) {
1195 		*name = NULL;
1196 		dns_message_currentname(msg, section, name);
1197 		tkeyset = NULL;
1198 		result = dns_message_findtype(*name, dns_rdatatype_tkey, 0,
1199 					      &tkeyset);
1200 		if (result == ISC_R_SUCCESS) {
1201 			result = dns_rdataset_first(tkeyset);
1202 			if (result != ISC_R_SUCCESS) {
1203 				return (result);
1204 			}
1205 			dns_rdataset_current(tkeyset, rdata);
1206 			return (ISC_R_SUCCESS);
1207 		}
1208 		result = dns_message_nextname(msg, section);
1209 	}
1210 	if (result == ISC_R_NOMORE) {
1211 		return (ISC_R_NOTFOUND);
1212 	}
1213 	return (result);
1214 }
1215 
1216 isc_result_t
1217 dns_tkey_processdhresponse(dns_message_t *qmsg, dns_message_t *rmsg,
1218 			   dst_key_t *key, isc_buffer_t *nonce,
1219 			   dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring) {
1220 	dns_rdata_t qtkeyrdata = DNS_RDATA_INIT, rtkeyrdata = DNS_RDATA_INIT;
1221 	dns_name_t keyname, *tkeyname, *theirkeyname, *ourkeyname, *tempname;
1222 	dns_rdataset_t *theirkeyset = NULL, *ourkeyset = NULL;
1223 	dns_rdata_t theirkeyrdata = DNS_RDATA_INIT;
1224 	dst_key_t *theirkey = NULL;
1225 	dns_rdata_tkey_t qtkey, rtkey;
1226 	unsigned char secretdata[256];
1227 	unsigned int sharedsize;
1228 	isc_buffer_t *shared = NULL, secret;
1229 	isc_region_t r, r2;
1230 	isc_result_t result;
1231 	bool freertkey = false;
1232 
1233 	REQUIRE(qmsg != NULL);
1234 	REQUIRE(rmsg != NULL);
1235 	REQUIRE(key != NULL);
1236 	REQUIRE(dst_key_alg(key) == DNS_KEYALG_DH);
1237 	REQUIRE(dst_key_isprivate(key));
1238 	if (outkey != NULL) {
1239 		REQUIRE(*outkey == NULL);
1240 	}
1241 
1242 	if (rmsg->rcode != dns_rcode_noerror) {
1243 		return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
1244 	}
1245 	RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
1246 	RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
1247 	freertkey = true;
1248 
1249 	RETERR(find_tkey(qmsg, &tempname, &qtkeyrdata, DNS_SECTION_ADDITIONAL));
1250 	RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
1251 
1252 	if (rtkey.error != dns_rcode_noerror ||
1253 	    rtkey.mode != DNS_TKEYMODE_DIFFIEHELLMAN ||
1254 	    rtkey.mode != qtkey.mode ||
1255 	    !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm) ||
1256 	    rmsg->rcode != dns_rcode_noerror)
1257 	{
1258 		tkey_log("dns_tkey_processdhresponse: tkey mode invalid "
1259 			 "or error set(1)");
1260 		result = DNS_R_INVALIDTKEY;
1261 		dns_rdata_freestruct(&qtkey);
1262 		goto failure;
1263 	}
1264 
1265 	dns_rdata_freestruct(&qtkey);
1266 
1267 	dns_name_init(&keyname, NULL);
1268 	dns_name_clone(dst_key_name(key), &keyname);
1269 
1270 	ourkeyname = NULL;
1271 	ourkeyset = NULL;
1272 	RETERR(dns_message_findname(rmsg, DNS_SECTION_ANSWER, &keyname,
1273 				    dns_rdatatype_key, 0, &ourkeyname,
1274 				    &ourkeyset));
1275 
1276 	result = dns_message_firstname(rmsg, DNS_SECTION_ANSWER);
1277 	while (result == ISC_R_SUCCESS) {
1278 		theirkeyname = NULL;
1279 		dns_message_currentname(rmsg, DNS_SECTION_ANSWER,
1280 					&theirkeyname);
1281 		if (dns_name_equal(theirkeyname, ourkeyname)) {
1282 			goto next;
1283 		}
1284 		theirkeyset = NULL;
1285 		result = dns_message_findtype(theirkeyname, dns_rdatatype_key,
1286 					      0, &theirkeyset);
1287 		if (result == ISC_R_SUCCESS) {
1288 			RETERR(dns_rdataset_first(theirkeyset));
1289 			break;
1290 		}
1291 	next:
1292 		result = dns_message_nextname(rmsg, DNS_SECTION_ANSWER);
1293 	}
1294 
1295 	if (theirkeyset == NULL) {
1296 		tkey_log("dns_tkey_processdhresponse: failed to find server "
1297 			 "key");
1298 		result = ISC_R_NOTFOUND;
1299 		goto failure;
1300 	}
1301 
1302 	dns_rdataset_current(theirkeyset, &theirkeyrdata);
1303 	RETERR(dns_dnssec_keyfromrdata(theirkeyname, &theirkeyrdata, rmsg->mctx,
1304 				       &theirkey));
1305 
1306 	RETERR(dst_key_secretsize(key, &sharedsize));
1307 	isc_buffer_allocate(rmsg->mctx, &shared, sharedsize);
1308 
1309 	RETERR(dst_key_computesecret(theirkey, key, shared));
1310 
1311 	isc_buffer_init(&secret, secretdata, sizeof(secretdata));
1312 
1313 	r.base = rtkey.key;
1314 	r.length = rtkey.keylen;
1315 	if (nonce != NULL) {
1316 		isc_buffer_usedregion(nonce, &r2);
1317 	} else {
1318 		r2.base = NULL;
1319 		r2.length = 0;
1320 	}
1321 	RETERR(compute_secret(shared, &r2, &r, &secret));
1322 
1323 	isc_buffer_usedregion(&secret, &r);
1324 	result = dns_tsigkey_create(tkeyname, &rtkey.algorithm, r.base,
1325 				    r.length, true, NULL, rtkey.inception,
1326 				    rtkey.expire, rmsg->mctx, ring, outkey);
1327 	isc_buffer_free(&shared);
1328 	dns_rdata_freestruct(&rtkey);
1329 	dst_key_free(&theirkey);
1330 	return (result);
1331 
1332 failure:
1333 	if (shared != NULL) {
1334 		isc_buffer_free(&shared);
1335 	}
1336 
1337 	if (theirkey != NULL) {
1338 		dst_key_free(&theirkey);
1339 	}
1340 
1341 	if (freertkey) {
1342 		dns_rdata_freestruct(&rtkey);
1343 	}
1344 
1345 	return (result);
1346 }
1347 
1348 isc_result_t
1349 dns_tkey_processgssresponse(dns_message_t *qmsg, dns_message_t *rmsg,
1350 			    const dns_name_t *gname, dns_gss_ctx_id_t *context,
1351 			    isc_buffer_t *outtoken, dns_tsigkey_t **outkey,
1352 			    dns_tsig_keyring_t *ring, char **err_message) {
1353 	dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
1354 	dns_name_t *tkeyname;
1355 	dns_rdata_tkey_t rtkey, qtkey;
1356 	dst_key_t *dstkey = NULL;
1357 	isc_buffer_t intoken;
1358 	isc_result_t result;
1359 	unsigned char array[TEMP_BUFFER_SZ];
1360 
1361 	REQUIRE(outtoken != NULL);
1362 	REQUIRE(qmsg != NULL);
1363 	REQUIRE(rmsg != NULL);
1364 	REQUIRE(gname != NULL);
1365 	REQUIRE(ring != NULL);
1366 	if (outkey != NULL) {
1367 		REQUIRE(*outkey == NULL);
1368 	}
1369 
1370 	if (rmsg->rcode != dns_rcode_noerror) {
1371 		return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
1372 	}
1373 	RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
1374 	RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
1375 
1376 	/*
1377 	 * Win2k puts the item in the ANSWER section, while the RFC
1378 	 * specifies it should be in the ADDITIONAL section.  Check first
1379 	 * where it should be, and then where it may be.
1380 	 */
1381 	result = find_tkey(qmsg, &tkeyname, &qtkeyrdata,
1382 			   DNS_SECTION_ADDITIONAL);
1383 	if (result == ISC_R_NOTFOUND) {
1384 		result = find_tkey(qmsg, &tkeyname, &qtkeyrdata,
1385 				   DNS_SECTION_ANSWER);
1386 	}
1387 	if (result != ISC_R_SUCCESS) {
1388 		goto failure;
1389 	}
1390 
1391 	RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
1392 
1393 	if (rtkey.error != dns_rcode_noerror ||
1394 	    rtkey.mode != DNS_TKEYMODE_GSSAPI ||
1395 	    !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm))
1396 	{
1397 		tkey_log("dns_tkey_processgssresponse: tkey mode invalid "
1398 			 "or error set(2) %d",
1399 			 rtkey.error);
1400 		dumpmessage(qmsg);
1401 		dumpmessage(rmsg);
1402 		result = DNS_R_INVALIDTKEY;
1403 		goto failure;
1404 	}
1405 
1406 	isc_buffer_init(outtoken, array, sizeof(array));
1407 	isc_buffer_init(&intoken, rtkey.key, rtkey.keylen);
1408 	RETERR(dst_gssapi_initctx(gname, &intoken, outtoken, context,
1409 				  ring->mctx, err_message));
1410 
1411 	RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey,
1412 				  NULL));
1413 
1414 	RETERR(dns_tsigkey_createfromkey(
1415 		tkeyname, DNS_TSIG_GSSAPI_NAME, dstkey, false, NULL,
1416 		rtkey.inception, rtkey.expire, ring->mctx, ring, outkey));
1417 	dst_key_free(&dstkey);
1418 	dns_rdata_freestruct(&rtkey);
1419 	return (result);
1420 
1421 failure:
1422 	/*
1423 	 * XXXSRA This probably leaks memory from rtkey and qtkey.
1424 	 */
1425 	if (dstkey != NULL) {
1426 		dst_key_free(&dstkey);
1427 	}
1428 	return (result);
1429 }
1430 
1431 isc_result_t
1432 dns_tkey_processdeleteresponse(dns_message_t *qmsg, dns_message_t *rmsg,
1433 			       dns_tsig_keyring_t *ring) {
1434 	dns_rdata_t qtkeyrdata = DNS_RDATA_INIT, rtkeyrdata = DNS_RDATA_INIT;
1435 	dns_name_t *tkeyname, *tempname;
1436 	dns_rdata_tkey_t qtkey, rtkey;
1437 	dns_tsigkey_t *tsigkey = NULL;
1438 	isc_result_t result;
1439 
1440 	REQUIRE(qmsg != NULL);
1441 	REQUIRE(rmsg != NULL);
1442 
1443 	if (rmsg->rcode != dns_rcode_noerror) {
1444 		return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
1445 	}
1446 
1447 	RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
1448 	RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
1449 
1450 	RETERR(find_tkey(qmsg, &tempname, &qtkeyrdata, DNS_SECTION_ADDITIONAL));
1451 	RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
1452 
1453 	if (rtkey.error != dns_rcode_noerror ||
1454 	    rtkey.mode != DNS_TKEYMODE_DELETE || rtkey.mode != qtkey.mode ||
1455 	    !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm) ||
1456 	    rmsg->rcode != dns_rcode_noerror)
1457 	{
1458 		tkey_log("dns_tkey_processdeleteresponse: tkey mode invalid "
1459 			 "or error set(3)");
1460 		result = DNS_R_INVALIDTKEY;
1461 		dns_rdata_freestruct(&qtkey);
1462 		dns_rdata_freestruct(&rtkey);
1463 		goto failure;
1464 	}
1465 
1466 	dns_rdata_freestruct(&qtkey);
1467 
1468 	RETERR(dns_tsigkey_find(&tsigkey, tkeyname, &rtkey.algorithm, ring));
1469 
1470 	dns_rdata_freestruct(&rtkey);
1471 
1472 	/*
1473 	 * Mark the key as deleted.
1474 	 */
1475 	dns_tsigkey_setdeleted(tsigkey);
1476 	/*
1477 	 * Release the reference.
1478 	 */
1479 	dns_tsigkey_detach(&tsigkey);
1480 
1481 failure:
1482 	return (result);
1483 }
1484 
1485 isc_result_t
1486 dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg,
1487 		      const dns_name_t *server, dns_gss_ctx_id_t *context,
1488 		      dns_tsigkey_t **outkey, dns_tsig_keyring_t *ring,
1489 		      bool win2k, char **err_message) {
1490 	dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
1491 	dns_name_t *tkeyname;
1492 	dns_rdata_tkey_t rtkey, qtkey, tkey;
1493 	isc_buffer_t intoken, outtoken;
1494 	dst_key_t *dstkey = NULL;
1495 	isc_result_t result;
1496 	unsigned char array[TEMP_BUFFER_SZ];
1497 	bool freertkey = false;
1498 
1499 	REQUIRE(qmsg != NULL);
1500 	REQUIRE(rmsg != NULL);
1501 	REQUIRE(server != NULL);
1502 	if (outkey != NULL) {
1503 		REQUIRE(*outkey == NULL);
1504 	}
1505 
1506 	if (rmsg->rcode != dns_rcode_noerror) {
1507 		return (ISC_RESULTCLASS_DNSRCODE + rmsg->rcode);
1508 	}
1509 
1510 	RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
1511 	RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
1512 	freertkey = true;
1513 
1514 	if (win2k) {
1515 		RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata,
1516 				 DNS_SECTION_ANSWER));
1517 	} else {
1518 		RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata,
1519 				 DNS_SECTION_ADDITIONAL));
1520 	}
1521 
1522 	RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
1523 
1524 	if (rtkey.error != dns_rcode_noerror ||
1525 	    rtkey.mode != DNS_TKEYMODE_GSSAPI ||
1526 	    !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm))
1527 	{
1528 		tkey_log("dns_tkey_processdhresponse: tkey mode invalid "
1529 			 "or error set(4)");
1530 		result = DNS_R_INVALIDTKEY;
1531 		goto failure;
1532 	}
1533 
1534 	isc_buffer_init(&intoken, rtkey.key, rtkey.keylen);
1535 	isc_buffer_init(&outtoken, array, sizeof(array));
1536 
1537 	result = dst_gssapi_initctx(server, &intoken, &outtoken, context,
1538 				    ring->mctx, err_message);
1539 	if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) {
1540 		return (result);
1541 	}
1542 
1543 	if (result == DNS_R_CONTINUE) {
1544 		dns_fixedname_t fixed;
1545 
1546 		dns_fixedname_init(&fixed);
1547 		dns_name_copynf(tkeyname, dns_fixedname_name(&fixed));
1548 		tkeyname = dns_fixedname_name(&fixed);
1549 
1550 		tkey.common.rdclass = dns_rdataclass_any;
1551 		tkey.common.rdtype = dns_rdatatype_tkey;
1552 		ISC_LINK_INIT(&tkey.common, link);
1553 		tkey.mctx = NULL;
1554 		dns_name_init(&tkey.algorithm, NULL);
1555 
1556 		if (win2k) {
1557 			dns_name_clone(DNS_TSIG_GSSAPIMS_NAME, &tkey.algorithm);
1558 		} else {
1559 			dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm);
1560 		}
1561 
1562 		tkey.inception = qtkey.inception;
1563 		tkey.expire = qtkey.expire;
1564 		tkey.mode = DNS_TKEYMODE_GSSAPI;
1565 		tkey.error = 0;
1566 		tkey.key = isc_buffer_base(&outtoken);
1567 		tkey.keylen = isc_buffer_usedlength(&outtoken);
1568 		tkey.other = NULL;
1569 		tkey.otherlen = 0;
1570 
1571 		dns_message_reset(qmsg, DNS_MESSAGE_INTENTRENDER);
1572 		RETERR(buildquery(qmsg, tkeyname, &tkey, win2k));
1573 		return (DNS_R_CONTINUE);
1574 	}
1575 
1576 	RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey,
1577 				  NULL));
1578 
1579 	/*
1580 	 * XXXSRA This seems confused.  If we got CONTINUE from initctx,
1581 	 * the GSS negotiation hasn't completed yet, so we can't sign
1582 	 * anything yet.
1583 	 */
1584 
1585 	RETERR(dns_tsigkey_createfromkey(
1586 		tkeyname,
1587 		(win2k ? DNS_TSIG_GSSAPIMS_NAME : DNS_TSIG_GSSAPI_NAME), dstkey,
1588 		true, NULL, rtkey.inception, rtkey.expire, ring->mctx, ring,
1589 		outkey));
1590 	dst_key_free(&dstkey);
1591 	dns_rdata_freestruct(&rtkey);
1592 	return (result);
1593 
1594 failure:
1595 	/*
1596 	 * XXXSRA This probably leaks memory from qtkey.
1597 	 */
1598 	if (freertkey) {
1599 		dns_rdata_freestruct(&rtkey);
1600 	}
1601 	if (dstkey != NULL) {
1602 		dst_key_free(&dstkey);
1603 	}
1604 	return (result);
1605 }
1606