tsig.c 49.8 KB
Newer Older
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2013  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2002  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
6
7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9
10
11
12
13
14
15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
16
17
18
 */

/*
Mark Andrews's avatar
Mark Andrews committed
19
 * $Id$
20
 */
21
/*! \file */
22
#include <config.h>
23
#include <stdlib.h>
24

25
#include <isc/buffer.h>
26
#include <isc/mem.h>
Brian Wellington's avatar
Brian Wellington committed
27
#include <isc/print.h>
28
#include <isc/refcount.h>
29
#include <isc/serial.h>
30
#include <isc/string.h>		/* Required for HP/UX (and others?) */
Bob Halley's avatar
Bob Halley committed
31
#include <isc/util.h>
32
#include <isc/time.h>
33
34

#include <dns/keyvalues.h>
35
#include <dns/log.h>
36
#include <dns/message.h>
37
#include <dns/fixedname.h>
38
#include <dns/rbt.h>
39
40
41
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
42
#include <dns/rdatastruct.h>
43
#include <dns/result.h>
44
45
46
47
#include <dns/tsig.h>

#include <dst/result.h>

48
#define TSIG_MAGIC		ISC_MAGIC('T', 'S', 'I', 'G')
49
#define VALID_TSIG_KEY(x)	ISC_MAGIC_VALID(x, TSIG_MAGIC)
50

51
52
53
54
#ifndef DNS_TSIG_MAXGENERATEDKEYS
#define DNS_TSIG_MAXGENERATEDKEYS 4096
#endif

Brian Wellington's avatar
Brian Wellington committed
55
#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
Brian Wellington's avatar
Brian Wellington committed
56
57
#define algname_is_allocated(algname) \
	((algname) != dns_tsig_hmacmd5_name && \
58
59
60
61
62
	 (algname) != dns_tsig_hmacsha1_name && \
	 (algname) != dns_tsig_hmacsha224_name && \
	 (algname) != dns_tsig_hmacsha256_name && \
	 (algname) != dns_tsig_hmacsha384_name && \
	 (algname) != dns_tsig_hmacsha512_name && \
63
64
	 (algname) != dns_tsig_gssapi_name && \
	 (algname) != dns_tsig_gssapims_name)
Brian Wellington's avatar
Brian Wellington committed
65

Brian Wellington's avatar
Brian Wellington committed
66
67
#define BADTIMELEN 6

68
69
static unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
static unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
Brian Wellington's avatar
Brian Wellington committed
70

71
72
73
74
75
76
77
static dns_name_t hmacmd5 = {
	DNS_NAME_MAGIC,
	hmacmd5_ndata, 26, 5,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	hmacmd5_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
Brian Wellington's avatar
Brian Wellington committed
78
79
};

80
81
82
83
84
85
86
87
88
89
90
dns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;

static unsigned char gsstsig_ndata[] = "\010gss-tsig";
static unsigned char gsstsig_offsets[] = { 0, 9 };
static dns_name_t gsstsig = {
	DNS_NAME_MAGIC,
	gsstsig_ndata, 10, 2,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	gsstsig_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
Brian Wellington's avatar
Brian Wellington committed
91
};
92
LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig;
Brian Wellington's avatar
Brian Wellington committed
93

94
95
96
97
/*
 * Since Microsoft doesn't follow its own standard, we will use this
 * alternate name as a second guess.
 */
98
99
100
101
102
103
104
105
106
static unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
static unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
static dns_name_t gsstsigms = {
	DNS_NAME_MAGIC,
	gsstsigms_ndata, 19, 4,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	gsstsigms_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
107
};
108
LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapims_name = &gsstsigms;
109

110
111
112
113
static unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
static unsigned char hmacsha1_offsets[] = { 0, 10 };

static dns_name_t  hmacsha1 = {
Automatic Updater's avatar
Automatic Updater committed
114
115
116
117
118
119
	DNS_NAME_MAGIC,
	hmacsha1_ndata, 11, 2,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	hmacsha1_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
120
121
122
123
124
125
126
127
};

LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;

static unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
static unsigned char hmacsha224_offsets[] = { 0, 12 };

static dns_name_t hmacsha224 = {
Automatic Updater's avatar
Automatic Updater committed
128
129
130
131
132
133
	DNS_NAME_MAGIC,
	hmacsha224_ndata, 13, 2,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	hmacsha224_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
134
135
136
137
138
139
140
141
};

LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;

static unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
static unsigned char hmacsha256_offsets[] = { 0, 12 };

static dns_name_t hmacsha256 = {
Automatic Updater's avatar
Automatic Updater committed
142
143
144
145
146
147
	DNS_NAME_MAGIC,
	hmacsha256_ndata, 13, 2,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	hmacsha256_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
148
149
150
151
152
153
154
155
};

LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;

static unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
static unsigned char hmacsha384_offsets[] = { 0, 12 };

static dns_name_t hmacsha384 = {
Automatic Updater's avatar
Automatic Updater committed
156
157
158
159
160
161
	DNS_NAME_MAGIC,
	hmacsha384_ndata, 13, 2,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	hmacsha384_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
162
163
164
165
166
167
168
169
};

LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;

static unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
static unsigned char hmacsha512_offsets[] = { 0, 12 };

static dns_name_t hmacsha512 = {
Automatic Updater's avatar
Automatic Updater committed
170
171
172
173
174
175
	DNS_NAME_MAGIC,
	hmacsha512_ndata, 13, 2,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	hmacsha512_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
176
177
178
179
};

LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;

Brian Wellington's avatar
Brian Wellington committed
180
static isc_result_t
181
tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
182

183
184
185
186
static void
tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
     ISC_FORMAT_PRINTF(3, 4);

187
188
189
190
191
static void
cleanup_ring(dns_tsig_keyring_t *ring);
static void
tsigkey_free(dns_tsigkey_t *key);

192
193
194
195
196
static void
tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
	va_list ap;
	char message[4096];
	char namestr[DNS_NAME_FORMATSIZE];
197
	char creatorstr[DNS_NAME_FORMATSIZE];
198
199
200
201
202
203
204

	if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
		return;
	if (key != NULL)
		dns_name_format(&key->name, namestr, sizeof(namestr));
	else
		strcpy(namestr, "<null>");
205

Evan Hunt's avatar
Evan Hunt committed
206
	if (key != NULL && key->generated && key->creator)
207
		dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
Evan Hunt's avatar
Evan Hunt committed
208
209
	else
		strcpy(creatorstr, "<null>");
210

211
212
213
	va_start(ap, fmt);
	vsnprintf(message, sizeof(message), fmt, ap);
	va_end(ap);
214
215
216
217
218
219
220
221
222
	if (key != NULL && key->generated)
		isc_log_write(dns_lctx,
			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
			      level, "tsig key '%s' (%s): %s",
			      namestr, creatorstr, message);
	else
		isc_log_write(dns_lctx,
			      DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
			      level, "tsig key '%s': %s", namestr, message);
223
224
}

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
static void
remove_fromring(dns_tsigkey_t *tkey) {
	if (tkey->generated) {
		ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
		tkey->ring->generated--;
	}
	(void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, ISC_FALSE);
}

static void
adjust_lru(dns_tsigkey_t *tkey) {
	if (tkey->generated) {
		RWLOCK(&tkey->ring->lock, isc_rwlocktype_write);
		/*
		 * We may have been removed from the LRU list between
		 * removing the read lock and aquiring the write lock.
		 */
Evan Hunt's avatar
Evan Hunt committed
242
		if (ISC_LINK_LINKED(tkey, link) &&
243
		    tkey->ring->lru.tail != tkey)
Evan Hunt's avatar
Evan Hunt committed
244
		{
245
246
247
248
249
250
251
			ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
			ISC_LIST_APPEND(tkey->ring->lru, tkey, link);
		}
		RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write);
	}
}

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * A supplemental routine just to add a key to ring.  Note that reference
 * counter should be counted separately because we may be adding the key
 * as part of creation of the key, in which case the reference counter was
 * already initialized.  Also note we don't need RWLOCK for the reference
 * counter: it's protected by a separate lock.
 */
static isc_result_t
keyring_add(dns_tsig_keyring_t *ring, dns_name_t *name,
	    dns_tsigkey_t *tkey)
{
	isc_result_t result;

	RWLOCK(&ring->lock, isc_rwlocktype_write);
	ring->writecount++;

	/*
	 * Do on the fly cleaning.  Find some nodes we might not
	 * want around any more.
	 */
	if (ring->writecount > 10) {
		cleanup_ring(ring);
		ring->writecount = 0;
	}

	result = dns_rbt_addname(ring->keys, name, tkey);
278
279
280
281
282
283
284
285
286
	if (tkey->generated) {
		/*
		 * Add the new key to the LRU list and remove the least
		 * recently used key if there are too many keys on the list.
		 */
		ISC_LIST_INITANDAPPEND(ring->lru, tkey, link);
		if (ring->generated++ > ring->maxgenerated)
			remove_fromring(ISC_LIST_HEAD(ring->lru));
	}
287
288
289
290
291
	RWUNLOCK(&ring->lock, isc_rwlocktype_write);

	return (result);
}

292
isc_result_t
Brian Wellington's avatar
Brian Wellington committed
293
dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
294
			  dst_key_t *dstkey, isc_boolean_t generated,
Brian Wellington's avatar
Brian Wellington committed
295
296
297
			  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)
298
{
299
	dns_tsigkey_t *tkey;
300
	isc_result_t ret;
301
	unsigned int refs = 0;
302

303
	REQUIRE(key == NULL || *key == NULL);
304
305
306
	REQUIRE(name != NULL);
	REQUIRE(algorithm != NULL);
	REQUIRE(mctx != NULL);
307
	REQUIRE(key != NULL || ring != NULL);
308

309
310
	tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t));
	if (tkey == NULL)
311
312
313
314
315
316
		return (ISC_R_NOMEMORY);

	dns_name_init(&tkey->name, NULL);
	ret = dns_name_dup(name, mctx, &tkey->name);
	if (ret != ISC_R_SUCCESS)
		goto cleanup_key;
317
	(void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
318

319
	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
Brian Wellington's avatar
Brian Wellington committed
320
		tkey->algorithm = DNS_TSIG_HMACMD5_NAME;
321
322
323
324
		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACMD5) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
		tkey->algorithm = DNS_TSIG_HMACSHA1_NAME;
		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
		tkey->algorithm = DNS_TSIG_HMACSHA224_NAME;
		if (dstkey != NULL &&
		    dst_key_alg(dstkey) != DST_ALG_HMACSHA224) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
		tkey->algorithm = DNS_TSIG_HMACSHA256_NAME;
		if (dstkey != NULL &&
		    dst_key_alg(dstkey) != DST_ALG_HMACSHA256) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
		tkey->algorithm = DNS_TSIG_HMACSHA384_NAME;
		if (dstkey != NULL &&
		    dst_key_alg(dstkey) != DST_ALG_HMACSHA384) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
		tkey->algorithm = DNS_TSIG_HMACSHA512_NAME;
		if (dstkey != NULL &&
		    dst_key_alg(dstkey) != DST_ALG_HMACSHA512) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
359
	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
Brian Wellington's avatar
Brian Wellington committed
360
		tkey->algorithm = DNS_TSIG_GSSAPI_NAME;
361
362
363
364
365
		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
366
		tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME;
367
368
369
370
371
		if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
			ret = DNS_R_BADALG;
			goto cleanup_name;
		}
	} else {
372
		if (dstkey != NULL) {
373
			ret = DNS_R_BADALG;
Brian Wellington's avatar
Brian Wellington committed
374
375
376
377
378
379
380
381
382
383
384
			goto cleanup_name;
		}
		tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t));
		if (tkey->algorithm == NULL) {
			ret = ISC_R_NOMEMORY;
			goto cleanup_name;
		}
		dns_name_init(tkey->algorithm, NULL);
		ret = dns_name_dup(algorithm, mctx, tkey->algorithm);
		if (ret != ISC_R_SUCCESS)
			goto cleanup_algorithm;
385
386
		(void)dns_name_downcase(tkey->algorithm, tkey->algorithm,
					NULL);
Brian Wellington's avatar
Brian Wellington committed
387
	}
388

389
390
391
392
393
394
395
	if (creator != NULL) {
		tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
		if (tkey->creator == NULL) {
			ret = ISC_R_NOMEMORY;
			goto cleanup_algorithm;
		}
		dns_name_init(tkey->creator, NULL);
396
		ret = dns_name_dup(creator, mctx, tkey->creator);
397
398
399
400
		if (ret != ISC_R_SUCCESS) {
			isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
			goto cleanup_algorithm;
		}
Brian Wellington's avatar
Brian Wellington committed
401
	} else
Brian Wellington's avatar
Brian Wellington committed
402
		tkey->creator = NULL;
403

404
405
406
	tkey->key = NULL;
	if (dstkey != NULL)
		dst_key_attach(dstkey, &tkey->key);
407
408
	tkey->ring = ring;

409
	if (key != NULL)
410
		refs = 1;
411
412
413
414
415
416
	if (ring != NULL)
		refs++;
	ret = isc_refcount_init(&tkey->refs, refs);
	if (ret != ISC_R_SUCCESS)
		goto cleanup_creator;

417
	tkey->generated = generated;
418
419
	tkey->inception = inception;
	tkey->expire = expire;
420
421
	tkey->mctx = NULL;
	isc_mem_attach(mctx, &tkey->mctx);
422

423
	tkey->magic = TSIG_MAGIC;
424

425
	if (ring != NULL) {
426
427
		ret = keyring_add(ring, name, tkey);
		if (ret != ISC_R_SUCCESS)
428
429
430
			goto cleanup_refs;
	}

431
432
433
434
435
436
	/*
	 * Ignore this if it's a GSS key, since the key size is meaningless.
	 */
	if (dstkey != NULL && dst_key_size(dstkey) < 64 &&
	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME) &&
	    !dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
437
438
439
440
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(name, namestr, sizeof(namestr));
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
			      DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
441
442
			      "the key '%s' is too short to be secure",
			      namestr);
443
	}
444

445
446
447
	if (key != NULL)
		*key = tkey;

448
449
	return (ISC_R_SUCCESS);

450
451
452
453
454
455
 cleanup_refs:
	tkey->magic = 0;
	while (refs-- > 0)
		isc_refcount_decrement(&tkey->refs, NULL);
	isc_refcount_destroy(&tkey->refs);
 cleanup_creator:
456
457
	if (tkey->key != NULL)
		dst_key_free(&tkey->key);
458
459
460
461
	if (tkey->creator != NULL) {
		dns_name_free(tkey->creator, mctx);
		isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
	}
Brian Wellington's avatar
Brian Wellington committed
462
463
464
465
466
467
468
 cleanup_algorithm:
	if (algname_is_allocated(tkey->algorithm)) {
		if (dns_name_dynamic(tkey->algorithm))
			dns_name_free(tkey->algorithm, mctx);
		isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t));
	}
 cleanup_name:
469
	dns_name_free(&tkey->name, mctx);
Brian Wellington's avatar
Brian Wellington committed
470
 cleanup_key:
471
	isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
472
473
474
475

	return (ret);
}

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
/*
 * Find a few nodes to destroy if possible.
 */
static void
cleanup_ring(dns_tsig_keyring_t *ring)
{
	isc_result_t result;
	dns_rbtnodechain_t chain;
	dns_name_t foundname;
	dns_fixedname_t fixedorigin;
	dns_name_t *origin;
	isc_stdtime_t now;
	dns_rbtnode_t *node;
	dns_tsigkey_t *tkey;

	/*
	 * Start up a new iterator each time.
	 */
	isc_stdtime_get(&now);
	dns_name_init(&foundname, NULL);
	dns_fixedname_init(&fixedorigin);
	origin = dns_fixedname_name(&fixedorigin);

 again:
Automatic Updater's avatar
Automatic Updater committed
500
	dns_rbtnodechain_init(&chain, ring->mctx);
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
					origin);
	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
		dns_rbtnodechain_invalidate(&chain);
		return;
	}

	for (;;) {
		node = NULL;
		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
		tkey = node->data;
		if (tkey != NULL) {
			if (tkey->generated
			    && isc_refcount_current(&tkey->refs) == 1
			    && tkey->inception != tkey->expire
			    && tkey->expire < now) {
				tsig_log(tkey, 2, "tsig expire: deleting");
				/* delete the key */
				dns_rbtnodechain_invalidate(&chain);
520
				remove_fromring(tkey);
521
522
523
524
525
526
527
528
529
530
531
532
				goto again;
			}
		}
		result = dns_rbtnodechain_next(&chain, &foundname,
					       origin);
		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
			dns_rbtnodechain_invalidate(&chain);
			return;
		}
	}
}

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
static void
destroyring(dns_tsig_keyring_t *ring) {
	dns_rbt_destroy(&ring->keys);
	isc_rwlock_destroy(&ring->lock);
	isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t));
}

static unsigned int
dst_alg_fromname(dns_name_t *algorithm) {
	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
		return (DST_ALG_HMACMD5);
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
		return (DST_ALG_HMACSHA1);
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
		return (DST_ALG_HMACSHA224);
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
		return (DST_ALG_HMACSHA256);
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
		return (DST_ALG_HMACSHA384);
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
		return (DST_ALG_HMACSHA512);
	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
Automatic Updater's avatar
Automatic Updater committed
555
		return (DST_ALG_GSSAPI);
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
	} else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
		return (DST_ALG_GSSAPI);
	} else
		return (0);
}

static isc_result_t
restore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) {
	dst_key_t *dstkey = NULL;
	char namestr[1024];
	char creatorstr[1024];
	char algorithmstr[1024];
	char keystr[4096];
	unsigned int inception, expire;
	int n;
	isc_buffer_t b;
	dns_name_t *name, *creator, *algorithm;
	dns_fixedname_t fname, fcreator, falgorithm;
	isc_result_t result;
	unsigned int dstalg;

	n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr,
		   creatorstr, &inception, &expire, algorithmstr, keystr);
	if (n == EOF)
		return (ISC_R_NOMORE);
	if (n != 6)
		return (ISC_R_FAILURE);

	if (isc_serial_lt(expire, now))
		return (DNS_R_EXPIRED);

	dns_fixedname_init(&fname);
	name = dns_fixedname_name(&fname);
	isc_buffer_init(&b, namestr, strlen(namestr));
	isc_buffer_add(&b, strlen(namestr));
	result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
	if (result != ISC_R_SUCCESS)
		return (result);

	dns_fixedname_init(&fcreator);
	creator = dns_fixedname_name(&fcreator);
	isc_buffer_init(&b, creatorstr, strlen(creatorstr));
	isc_buffer_add(&b, strlen(creatorstr));
	result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL);
	if (result != ISC_R_SUCCESS)
		return (result);

	dns_fixedname_init(&falgorithm);
	algorithm = dns_fixedname_name(&falgorithm);
	isc_buffer_init(&b, algorithmstr, strlen(algorithmstr));
	isc_buffer_add(&b, strlen(algorithmstr));
	result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL);
	if (result != ISC_R_SUCCESS)
		return (result);

	dstalg = dst_alg_fromname(algorithm);
	if (dstalg == 0)
		return (DNS_R_BADALG);

	result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY,
				 DNS_KEYPROTO_DNSSEC, dns_rdataclass_in,
				 ring->mctx, keystr, &dstkey);
	if (result != ISC_R_SUCCESS)
		return (result);

Mark Andrews's avatar
Mark Andrews committed
621
	result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
622
623
					   ISC_TRUE, creator, inception,
					   expire, ring->mctx, ring, NULL);
624
	if (dstkey != NULL)
625
626
627
628
629
		dst_key_free(&dstkey);
	return (result);
}

static void
Evan Hunt's avatar
Evan Hunt committed
630
dump_key(dns_tsigkey_t *tkey, FILE *fp) {
631
632
	char *buffer = NULL;
	int length = 0;
Automatic Updater's avatar
Automatic Updater committed
633
634
635
	char namestr[DNS_NAME_FORMATSIZE];
	char creatorstr[DNS_NAME_FORMATSIZE];
	char algorithmstr[DNS_NAME_FORMATSIZE];
636
637
	isc_result_t result;

Evan Hunt's avatar
Evan Hunt committed
638
639
640
	REQUIRE(tkey != NULL);
	REQUIRE(fp != NULL);

641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
	dns_name_format(&tkey->name, namestr, sizeof(namestr));
	dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr));
	dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr));
	result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length);
	if (result == ISC_R_SUCCESS)
		fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr,
			tkey->inception, tkey->expire, algorithmstr,
			length, buffer);
	if (buffer != NULL)
		isc_mem_put(tkey->mctx, buffer, length);
}

isc_result_t
dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) {
	isc_result_t result;
	dns_rbtnodechain_t chain;
	dns_name_t foundname;
	dns_fixedname_t fixedorigin;
	dns_name_t *origin;
	isc_stdtime_t now;
	dns_rbtnode_t *node;
	dns_tsigkey_t *tkey;
	dns_tsig_keyring_t *ring;
	unsigned int references;

	REQUIRE(ringp != NULL && *ringp != NULL);

	ring = *ringp;
	*ringp = NULL;

	RWLOCK(&ring->lock, isc_rwlocktype_write);
	INSIST(ring->references > 0);
	ring->references--;
	references = ring->references;
	RWUNLOCK(&ring->lock, isc_rwlocktype_write);

	if (references != 0)
		return (DNS_R_CONTINUE);

	isc_stdtime_get(&now);
	dns_name_init(&foundname, NULL);
	dns_fixedname_init(&fixedorigin);
	origin = dns_fixedname_name(&fixedorigin);
	dns_rbtnodechain_init(&chain, ring->mctx);
	result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
					origin);
	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
		dns_rbtnodechain_invalidate(&chain);
		goto destroy;
	}

	for (;;) {
		node = NULL;
		dns_rbtnodechain_current(&chain, &foundname, origin, &node);
		tkey = node->data;
		if (tkey != NULL && tkey->generated && tkey->expire >= now)
			dump_key(tkey, fp);
		result = dns_rbtnodechain_next(&chain, &foundname,
					       origin);
		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
			dns_rbtnodechain_invalidate(&chain);
			if (result == ISC_R_NOMORE)
				result = ISC_R_SUCCESS;
			goto destroy;
		}
	}

 destroy:
	destroyring(ring);
	return (result);
}

Brian Wellington's avatar
Brian Wellington committed
713
714
715
716
717
718
719
720
721
722
723
724
725
726
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)
{
	dst_key_t *dstkey = NULL;
	isc_result_t result;

	REQUIRE(length >= 0);
	if (length > 0)
		REQUIRE(secret != NULL);

727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
	if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
		if (secret != NULL) {
			isc_buffer_t b;

			isc_buffer_init(&b, secret, length);
			isc_buffer_add(&b, length);
			result = dst_key_frombuffer(name, DST_ALG_HMACMD5,
						    DNS_KEYOWNER_ENTITY,
						    DNS_KEYPROTO_DNSSEC,
						    dns_rdataclass_in,
						    &b, mctx, &dstkey);
				if (result != ISC_R_SUCCESS)
					return (result);
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) {
		if (secret != NULL) {
			isc_buffer_t b;

			isc_buffer_init(&b, secret, length);
			isc_buffer_add(&b, length);
			result = dst_key_frombuffer(name, DST_ALG_HMACSHA1,
						    DNS_KEYOWNER_ENTITY,
						    DNS_KEYPROTO_DNSSEC,
						    dns_rdataclass_in,
						    &b, mctx, &dstkey);
				if (result != ISC_R_SUCCESS)
					return (result);
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) {
		if (secret != NULL) {
			isc_buffer_t b;

			isc_buffer_init(&b, secret, length);
			isc_buffer_add(&b, length);
			result = dst_key_frombuffer(name, DST_ALG_HMACSHA224,
						    DNS_KEYOWNER_ENTITY,
						    DNS_KEYPROTO_DNSSEC,
						    dns_rdataclass_in,
						    &b, mctx, &dstkey);
				if (result != ISC_R_SUCCESS)
					return (result);
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) {
		if (secret != NULL) {
			isc_buffer_t b;

			isc_buffer_init(&b, secret, length);
			isc_buffer_add(&b, length);
			result = dst_key_frombuffer(name, DST_ALG_HMACSHA256,
						    DNS_KEYOWNER_ENTITY,
						    DNS_KEYPROTO_DNSSEC,
						    dns_rdataclass_in,
						    &b, mctx, &dstkey);
				if (result != ISC_R_SUCCESS)
					return (result);
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) {
		if (secret != NULL) {
			isc_buffer_t b;

			isc_buffer_init(&b, secret, length);
			isc_buffer_add(&b, length);
			result = dst_key_frombuffer(name, DST_ALG_HMACSHA384,
						    DNS_KEYOWNER_ENTITY,
						    DNS_KEYPROTO_DNSSEC,
						    dns_rdataclass_in,
						    &b, mctx, &dstkey);
				if (result != ISC_R_SUCCESS)
					return (result);
		}
	} else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) {
		if (secret != NULL) {
			isc_buffer_t b;

			isc_buffer_init(&b, secret, length);
			isc_buffer_add(&b, length);
			result = dst_key_frombuffer(name, DST_ALG_HMACSHA512,
						    DNS_KEYOWNER_ENTITY,
						    DNS_KEYPROTO_DNSSEC,
						    dns_rdataclass_in,
						    &b, mctx, &dstkey);
				if (result != ISC_R_SUCCESS)
					return (result);
		}
	} else if (length > 0)
812
		return (DNS_R_BADALG);
Brian Wellington's avatar
Brian Wellington committed
813

814
	result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
Brian Wellington's avatar
Brian Wellington committed
815
816
					   generated, creator,
					   inception, expire, mctx, ring, key);
817
	if (dstkey != NULL)
Brian Wellington's avatar
Brian Wellington committed
818
819
820
821
		dst_key_free(&dstkey);
	return (result);
}

822
823
824
825
826
void
dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
	REQUIRE(VALID_TSIG_KEY(source));
	REQUIRE(targetp != NULL && *targetp == NULL);

827
	isc_refcount_increment(&source->refs, NULL);
828
829
830
	*targetp = source;
}

831
static void
832
833
tsigkey_free(dns_tsigkey_t *key) {
	REQUIRE(VALID_TSIG_KEY(key));
834

835
836
	key->magic = 0;
	dns_name_free(&key->name, key->mctx);
Brian Wellington's avatar
Brian Wellington committed
837
838
839
840
	if (algname_is_allocated(key->algorithm)) {
		dns_name_free(key->algorithm, key->mctx);
		isc_mem_put(key->mctx, key->algorithm, sizeof(dns_name_t));
	}
841
842
843
844
845
	if (key->key != NULL)
		dst_key_free(&key->key);
	if (key->creator != NULL) {
		dns_name_free(key->creator, key->mctx);
		isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
846
	}
847
	isc_refcount_destroy(&key->refs);
848
	isc_mem_putanddetach(&key->mctx, key, sizeof(dns_tsigkey_t));
849
850
}

851
void
852
853
dns_tsigkey_detach(dns_tsigkey_t **keyp) {
	dns_tsigkey_t *key;
854
	unsigned int refs;
855

856
857
	REQUIRE(keyp != NULL);
	REQUIRE(VALID_TSIG_KEY(*keyp));
858

859
	key = *keyp;
860
	isc_refcount_decrement(&key->refs, &refs);
861

862
	if (refs == 0)
863
		tsigkey_free(key);
864
865

	*keyp = NULL;
866
867
}

868
869
void
dns_tsigkey_setdeleted(dns_tsigkey_t *key) {
870
871
872
873
	REQUIRE(VALID_TSIG_KEY(key));
	REQUIRE(key->ring != NULL);

	RWLOCK(&key->ring->lock, isc_rwlocktype_write);
874
	remove_fromring(key);
875
	RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
876
877
}

878
879
isc_result_t
dns_tsig_sign(dns_message_t *msg) {
880
	dns_tsigkey_t *key;
881
	dns_rdata_any_tsig_t tsig, querytsig;
882
	unsigned char data[128];
Michael Graff's avatar
Michael Graff committed
883
	isc_buffer_t databuf, sigbuf;
884
	isc_buffer_t *dynbuf;
885
	dns_name_t *owner;
886
	dns_rdata_t *rdata = NULL;
887
888
	dns_rdatalist_t *datalist;
	dns_rdataset_t *dataset;
889
	isc_region_t r;
890
	isc_stdtime_t now;
891
	isc_mem_t *mctx;
Brian Wellington's avatar
Brian Wellington committed
892
	dst_context_t *ctx = NULL;
893
	isc_result_t ret;
Brian Wellington's avatar
Brian Wellington committed
894
	unsigned char badtimedata[BADTIMELEN];
Brian Wellington's avatar
Brian Wellington committed
895
	unsigned int sigsize = 0;
896
	isc_boolean_t response = is_response(msg);
897
898

	REQUIRE(msg != NULL);
899
	REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg)));
Brian Wellington's avatar
Brian Wellington committed
900

901
902
903
	/*
	 * If this is a response, there should be a query tsig.
	 */
904
	if (response && msg->querytsig == NULL)
Brian Wellington's avatar
Brian Wellington committed
905
		return (DNS_R_EXPECTEDTSIG);
906

907
908
	dynbuf = NULL;

909
	mctx = msg->mctx;
910
	key = dns_message_gettsigkey(msg);
911

912
913
914
915
916
	tsig.mctx = mctx;
	tsig.common.rdclass = dns_rdataclass_any;
	tsig.common.rdtype = dns_rdatatype_tsig;
	ISC_LINK_INIT(&tsig.common, link);
	dns_name_init(&tsig.algorithm, NULL);
Brian Wellington's avatar
Brian Wellington committed
917
	dns_name_clone(key->algorithm, &tsig.algorithm);
918

919
	isc_stdtime_get(&now);
920
	tsig.timesigned = now + msg->timeadjust;
921
	tsig.fudge = DNS_TSIG_FUDGE;
922

923
	tsig.originalid = msg->id;
924

925
	isc_buffer_init(&databuf, data, sizeof(data));
926

927
	if (response)
928
		tsig.error = msg->querytsigstatus;
929
	else
930
		tsig.error = dns_rcode_noerror;
931

932
933
934
	if (tsig.error != dns_tsigerror_badtime) {
		tsig.otherlen = 0;
		tsig.other = NULL;
Brian Wellington's avatar
Brian Wellington committed
935
	} else {
936
		isc_buffer_t otherbuf;
937

Brian Wellington's avatar
Brian Wellington committed
938
939
940
		tsig.otherlen = BADTIMELEN;
		tsig.other = badtimedata;
		isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
941
		isc_buffer_putuint48(&otherbuf, tsig.timesigned);
942
	}
Brian Wellington's avatar
Brian Wellington committed
943
944

	if (key->key != NULL && tsig.error != dns_tsigerror_badsig) {
945
946
		unsigned char header[DNS_MESSAGE_HEADERLEN];
		isc_buffer_t headerbuf;
947
		isc_uint16_t digestbits;
948

949
950
		ret = dst_context_create2(key->key, mctx,
					  DNS_LOGCATEGORY_DNSSEC, &ctx);
951
		if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
952
			return (ret);
953

954
955
956
		/*
		 * If this is a response, digest the query signature.
		 */
957
		if (response) {
958
			dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
959

960
			ret = dns_rdataset_first(msg->querytsig);
961
			if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
962
				goto cleanup_context;
963
			dns_rdataset_current(msg->querytsig, &querytsigrdata);
964
965
966
			ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
						 NULL);
			if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
967
				goto cleanup_context;
968
969
			isc_buffer_putuint16(&databuf, querytsig.siglen);
			if (isc_buffer_availablelength(&databuf) <
970
			    querytsig.siglen) {
971
				ret = ISC_R_NOSPACE;
Brian Wellington's avatar
Brian Wellington committed
972
				goto cleanup_context;
973
974
975
			}
			isc_buffer_putmem(&databuf, querytsig.signature,
					  querytsig.siglen);
976
			isc_buffer_usedregion(&databuf, &r);
Brian Wellington's avatar
Brian Wellington committed
977
			ret = dst_context_adddata(ctx, &r);
978
			if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
979
				goto cleanup_context;
980
		}
981
#if defined(__clang__)  && \
982
983
984
       ( __clang_major__ < 3 || \
	(__clang_major__ == 3 && __clang_minor__ < 2) || \
	(__clang_major__ == 4 && __clang_minor__ < 2))
985
986
987
	/* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */
		else memset(&querytsig, 0, sizeof(querytsig));
#endif
988

989
990
991
		/*
		 * Digest the header.
		 */
992
		isc_buffer_init(&headerbuf, header, sizeof(header));
993
		dns_message_renderheader(msg, &headerbuf);
994
		isc_buffer_usedregion(&headerbuf, &r);
Brian Wellington's avatar
Brian Wellington committed
995
		ret = dst_context_adddata(ctx, &r);
996
		if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
997
			goto cleanup_context;
Brian Wellington's avatar
Brian Wellington committed
998

999
1000
1001
		/*
		 * Digest the remainder of the message.
		 */
1002
		isc_buffer_usedregion(msg->buffer, &r);
1003
		isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
Brian Wellington's avatar
Brian Wellington committed
1004
		ret = dst_context_adddata(ctx, &r);
1005
		if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1006
			goto cleanup_context;
1007

Brian Wellington's avatar
Brian Wellington committed
1008
		if (msg->tcp_continuation == 0) {
1009
1010
1011
			/*
			 * Digest the name, class, ttl, alg.
			 */
Brian Wellington's avatar
Brian Wellington committed
1012
			dns_name_toregion(&key->name, &r);
Brian Wellington's avatar
Brian Wellington committed
1013
			ret = dst_context_adddata(ctx, &r);
Brian Wellington's avatar
Brian Wellington committed
1014
			if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1015
				goto cleanup_context;
1016

Brian Wellington's avatar
Brian Wellington committed
1017
1018
1019
			isc_buffer_clear(&databuf);
			isc_buffer_putuint16(&databuf, dns_rdataclass_any);
			isc_buffer_putuint32(&databuf, 0); /* ttl */
1020
			isc_buffer_usedregion(&databuf, &r);
Brian Wellington's avatar
Brian Wellington committed
1021
			ret = dst_context_adddata(ctx, &r);
Brian Wellington's avatar
Brian Wellington committed
1022
			if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1023
				goto cleanup_context;
Brian Wellington's avatar
Brian Wellington committed
1024

1025
			dns_name_toregion(&tsig.algorithm, &r);
Brian Wellington's avatar
Brian Wellington committed
1026
			ret = dst_context_adddata(ctx, &r);
Brian Wellington's avatar
Brian Wellington committed
1027
			if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1028
				goto cleanup_context;
1029

Brian Wellington's avatar
Brian Wellington committed
1030
1031
		}
		/* Digest the timesigned and fudge */
1032
		isc_buffer_clear(&databuf);
Mark Andrews's avatar
Mark Andrews committed
1033
1034
		if (tsig.error == dns_tsigerror_badtime) {
			INSIST(response);
1035
			tsig.timesigned = querytsig.timesigned;
Mark Andrews's avatar
Mark Andrews committed
1036
		}
1037
		isc_buffer_putuint48(&databuf, tsig.timesigned);
1038
		isc_buffer_putuint16(&databuf, tsig.fudge);
1039
		isc_buffer_usedregion(&databuf, &r);
Brian Wellington's avatar
Brian Wellington committed
1040
		ret = dst_context_adddata(ctx, &r);
1041
		if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1042
			goto cleanup_context;
1043

Brian Wellington's avatar
Brian Wellington committed
1044
		if (msg->tcp_continuation == 0) {
1045
1046
1047
			/*
			 * Digest the error and other data length.
			 */
Brian Wellington's avatar
Brian Wellington committed
1048
			isc_buffer_clear(&databuf);
1049
1050
			isc_buffer_putuint16(&databuf, tsig.error);
			isc_buffer_putuint16(&databuf, tsig.otherlen);
Brian Wellington's avatar
Brian Wellington committed
1051

1052
			isc_buffer_usedregion(&databuf, &r);
Brian Wellington's avatar
Brian Wellington committed
1053
			ret = dst_context_adddata(ctx, &r);
1054
			if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1055
				goto cleanup_context;
Brian Wellington's avatar
Brian Wellington committed
1056

1057
			/*
Evan Hunt's avatar
Evan Hunt committed
1058
			 * Digest other data.
1059
			 */
1060
1061
1062
			if (tsig.otherlen > 0) {
				r.length = tsig.otherlen;
				r.base = tsig.other;
Brian Wellington's avatar
Brian Wellington committed
1063
				ret = dst_context_adddata(ctx, &r);
Brian Wellington's avatar
Brian Wellington committed
1064
				if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1065
					goto cleanup_context;
Brian Wellington's avatar
Brian Wellington committed
1066
			}
1067
1068
		}

Brian Wellington's avatar
Brian Wellington committed
1069
		ret = dst_key_sigsize(key->key, &sigsize);
Brian Wellington's avatar
Brian Wellington committed
1070
		if (ret != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
1071
			goto cleanup_context;
Brian Wellington's avatar
Brian Wellington committed
1072
		tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
1073
		if (tsig.signature == NULL) {
1074
			ret = ISC_R_NOMEMORY;
Brian Wellington's avatar
Brian Wellington committed
1075
			goto cleanup_context;
1076
1077
		}

Brian Wellington's avatar
Brian Wellington committed
1078
		isc_buffer_init(&sigbuf, tsig.signature, sigsize);
Brian Wellington's avatar
Brian Wellington committed
1079
		ret = dst_context_sign(ctx, &sigbuf);
1080
1081
		if (ret != ISC_R_SUCCESS)
			goto cleanup_signature;
Brian Wellington's avatar
Brian Wellington committed
1082
		dst_context_destroy(&ctx);
1083
1084
1085
		digestbits = dst_key_getbits(key->key);
		if (digestbits != 0) {
			unsigned int bytes = (digestbits + 1) / 8;
1086
			if (response && bytes < querytsig.siglen)
1087
1088
1089
1090
1091
1092
				bytes = querytsig.siglen;
			if (bytes > isc_buffer_usedlength(&sigbuf))
				bytes = isc_buffer_usedlength(&sigbuf);
			tsig.siglen = bytes;
		} else
			tsig.siglen = isc_buffer_usedlength(&sigbuf);
Brian Wellington's avatar
Brian Wellington committed
1093
	} else {
1094
1095
		tsig.siglen = 0;
		tsig.signature = NULL;
1096
1097
1098
1099
1100
	}

	ret = dns_message_gettemprdata(msg, &rdata);
	if (ret != ISC_R_SUCCESS)
		goto cleanup_signature;
1101
	ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
1102
	if (ret != ISC_R_SUCCESS)
1103
		goto cleanup_rdata;
1104
	ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
1105
				   dns_rdatatype_tsig, &tsig, dynbuf);
1106
	if (ret != ISC_R_SUCCESS)
1107
		goto cleanup_dynbuf;
1108

1109
1110
	dns_message_takebuffer(msg, &dynbuf);

1111
	if (tsig.signature != NULL) {
Brian Wellington's avatar
Brian Wellington committed
1112
		isc_mem_put(mctx, tsig.signature, sigsize);
1113
1114
		tsig.signature = NULL;
	}
1115
1116
1117
1118

	owner = NULL;
	ret = dns_message_gettempname(msg, &owner);
	if (ret != ISC_R_SUCCESS)
1119
		goto cleanup_rdata;
1120
	dns_name_init(owner, NULL);
1121
1122
1123
	ret = dns_name_dup(&key->name, msg->mctx, owner);
	if (ret != ISC_R_SUCCESS)
		goto cleanup_owner;
1124
1125
1126
1127

	datalist = NULL;
	ret = dns_message_gettemprdatalist(msg, &datalist);
	if (ret != ISC_R_SUCCESS)
1128
		goto cleanup_owner;
1129
1130
1131
1132
	dataset = NULL;
	ret = dns_message_gettemprdataset(msg, &dataset);
	if (ret != ISC_R_SUCCESS)
		goto cleanup_rdatalist;
1133
1134
	datalist->rdclass = dns_rdataclass_any;
	datalist->type = dns_rdatatype_tsig;
1135
	datalist->covers = 0;
1136
1137
1138
1139
	datalist->ttl = 0;
	ISC_LIST_INIT(datalist->rdata);
	ISC_LIST_APPEND(datalist->rdata, rdata, link);
	dns_rdataset_init(dataset);
1140
1141
	RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
		      == ISC_R_SUCCESS);
1142
	msg->tsig = dataset;
1143
	msg->tsigname = owner;
1144

1145
1146
1147
	/* Windows does not like the tsig name being compressed. */
	msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;

1148
1149
	return (ISC_R_SUCCESS);