Merge from vendor branch OPENSSL:
[dragonfly.git] / contrib / bind-9.3 / lib / dns / tsig.c
1 /*
2  * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2002  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /*
19  * $Id: tsig.c,v 1.112.2.3.8.10 2006/05/02 04:21:42 marka Exp $
20  */
21
22 #include <config.h>
23 #include <stdlib.h>
24
25 #include <isc/buffer.h>
26 #include <isc/mem.h>
27 #include <isc/print.h>
28 #include <isc/refcount.h>
29 #include <isc/string.h>         /* Required for HP/UX (and others?) */
30 #include <isc/util.h>
31
32 #include <dns/keyvalues.h>
33 #include <dns/log.h>
34 #include <dns/message.h>
35 #include <dns/rbt.h>
36 #include <dns/rdata.h>
37 #include <dns/rdatalist.h>
38 #include <dns/rdataset.h>
39 #include <dns/rdatastruct.h>
40 #include <dns/result.h>
41 #include <dns/tsig.h>
42
43 #include <dst/result.h>
44
45 #define TSIG_MAGIC              ISC_MAGIC('T', 'S', 'I', 'G')
46 #define VALID_TSIG_KEY(x)       ISC_MAGIC_VALID(x, TSIG_MAGIC)
47
48 #define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
49 #define algname_is_allocated(algname) \
50         ((algname) != dns_tsig_hmacmd5_name && \
51          (algname) != dns_tsig_gssapi_name && \
52          (algname) != dns_tsig_gssapims_name)
53
54 #define BADTIMELEN 6
55
56 static unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
57 static unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
58
59 static dns_name_t hmacmd5 = {
60         DNS_NAME_MAGIC,
61         hmacmd5_ndata, 26, 5,
62         DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
63         hmacmd5_offsets, NULL,
64         {(void *)-1, (void *)-1},
65         {NULL, NULL}
66 };
67
68 dns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
69
70 static unsigned char gsstsig_ndata[] = "\010gss-tsig";
71 static unsigned char gsstsig_offsets[] = { 0, 9 };
72
73 static dns_name_t gsstsig = {
74         DNS_NAME_MAGIC,
75         gsstsig_ndata, 10, 2,
76         DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
77         gsstsig_offsets, NULL,
78         {(void *)-1, (void *)-1},
79         {NULL, NULL}
80 };
81
82 LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig;
83
84 /* It's nice of Microsoft to conform to their own standard. */
85 static unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
86 static unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
87
88 static dns_name_t gsstsigms = {
89         DNS_NAME_MAGIC,
90         gsstsigms_ndata, 19, 4,
91         DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
92         gsstsigms_offsets, NULL,
93         {(void *)-1, (void *)-1},
94         {NULL, NULL}
95 };
96
97 LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapims_name = &gsstsigms;
98
99 static isc_result_t
100 tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
101
102 static void
103 tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
104      ISC_FORMAT_PRINTF(3, 4);
105
106 static void
107 tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
108         va_list ap;
109         char message[4096];
110         char namestr[DNS_NAME_FORMATSIZE];
111
112         if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE)
113                 return;
114         if (key != NULL)
115                 dns_name_format(&key->name, namestr, sizeof(namestr));
116         else
117                 strcpy(namestr, "<null>");
118         va_start(ap, fmt);
119         vsnprintf(message, sizeof(message), fmt, ap);
120         va_end(ap);
121         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG,
122                       level, "tsig key '%s': %s", namestr, message);
123 }
124
125 isc_result_t
126 dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm,
127                           dst_key_t *dstkey, isc_boolean_t generated,
128                           dns_name_t *creator, isc_stdtime_t inception,
129                           isc_stdtime_t expire, isc_mem_t *mctx,
130                           dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
131 {
132         dns_tsigkey_t *tkey;
133         isc_result_t ret;
134         unsigned int refs = 0;
135
136         REQUIRE(key == NULL || *key == NULL);
137         REQUIRE(name != NULL);
138         REQUIRE(algorithm != NULL);
139         REQUIRE(mctx != NULL);
140
141         tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t));
142         if (tkey == NULL)
143                 return (ISC_R_NOMEMORY);
144
145         dns_name_init(&tkey->name, NULL);
146         ret = dns_name_dup(name, mctx, &tkey->name);
147         if (ret != ISC_R_SUCCESS)
148                 goto cleanup_key;
149         (void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
150
151         if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) {
152                 tkey->algorithm = DNS_TSIG_HMACMD5_NAME;
153                 if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACMD5) {
154                         ret = DNS_R_BADALG;
155                         goto cleanup_name;
156                 }
157         } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) {
158                 tkey->algorithm = DNS_TSIG_GSSAPI_NAME;
159                 if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
160                         ret = DNS_R_BADALG;
161                         goto cleanup_name;
162                 }
163         } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) {
164                 tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME;
165                 if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) {
166                         ret = DNS_R_BADALG;
167                         goto cleanup_name;
168                 }
169         } else {
170                 if (dstkey != NULL) {
171                         ret = DNS_R_BADALG;
172                         goto cleanup_name;
173                 }
174                 tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t));
175                 if (tkey->algorithm == NULL) {
176                         ret = ISC_R_NOMEMORY;
177                         goto cleanup_name;
178                 }
179                 dns_name_init(tkey->algorithm, NULL);
180                 ret = dns_name_dup(algorithm, mctx, tkey->algorithm);
181                 if (ret != ISC_R_SUCCESS)
182                         goto cleanup_algorithm;
183                 (void)dns_name_downcase(tkey->algorithm, tkey->algorithm,
184                                         NULL);
185         }
186
187         if (creator != NULL) {
188                 tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
189                 if (tkey->creator == NULL) {
190                         ret = ISC_R_NOMEMORY;
191                         goto cleanup_algorithm;
192                 }
193                 dns_name_init(tkey->creator, NULL);
194                 ret = dns_name_dup(creator, mctx, tkey->creator);
195                 if (ret != ISC_R_SUCCESS) {
196                         isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
197                         goto cleanup_algorithm;
198                 }
199         } else
200                 tkey->creator = NULL;
201
202         tkey->key = dstkey;
203         tkey->ring = ring;
204
205         if (ring != NULL) {
206                 RWLOCK(&ring->lock, isc_rwlocktype_write);
207                 ret = dns_rbt_addname(ring->keys, name, tkey);
208                 if (ret != ISC_R_SUCCESS) {
209                         RWUNLOCK(&ring->lock, isc_rwlocktype_write);
210                         goto cleanup_algorithm;
211                 }
212                 refs++;
213                 RWUNLOCK(&ring->lock, isc_rwlocktype_write);
214         }
215
216         if (key != NULL)
217                 refs++;
218         isc_refcount_init(&tkey->refs, refs);
219         tkey->generated = generated;
220         tkey->inception = inception;
221         tkey->expire = expire;
222         tkey->mctx = mctx;
223
224         tkey->magic = TSIG_MAGIC;
225
226         if (dstkey != NULL && dst_key_size(dstkey) < 64) {
227                 char namestr[DNS_NAME_FORMATSIZE];
228                 dns_name_format(name, namestr, sizeof(namestr));
229                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
230                               DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
231                               "the key '%s' is too short to be secure",
232                               namestr);
233         }
234         if (key != NULL)
235                 *key = tkey;
236
237         return (ISC_R_SUCCESS);
238
239  cleanup_algorithm:
240         if (algname_is_allocated(tkey->algorithm)) {
241                 if (dns_name_dynamic(tkey->algorithm))
242                         dns_name_free(tkey->algorithm, mctx);
243                 isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t));
244         }
245  cleanup_name:
246         dns_name_free(&tkey->name, mctx);
247  cleanup_key:
248         isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
249
250         return (ret);
251 }
252
253 isc_result_t
254 dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm,
255                    unsigned char *secret, int length, isc_boolean_t generated,
256                    dns_name_t *creator, isc_stdtime_t inception,
257                    isc_stdtime_t expire, isc_mem_t *mctx,
258                    dns_tsig_keyring_t *ring, dns_tsigkey_t **key)
259 {
260         dst_key_t *dstkey = NULL;
261         isc_result_t result;
262
263         REQUIRE(length >= 0);
264         if (length > 0)
265                 REQUIRE(secret != NULL);
266
267         if (!dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME) && length > 0)
268                 return (DNS_R_BADALG);
269
270         if (secret != NULL) {
271                 isc_buffer_t b;
272
273                 isc_buffer_init(&b, secret, length);
274                 isc_buffer_add(&b, length);
275                 result = dst_key_frombuffer(name, DST_ALG_HMACMD5,
276                                             DNS_KEYOWNER_ENTITY,
277                                             DNS_KEYPROTO_DNSSEC,
278                                             dns_rdataclass_in,
279                                             &b, mctx, &dstkey);
280                 if (result != ISC_R_SUCCESS)
281                         return (result);
282         }
283         result = dns_tsigkey_createfromkey(name, algorithm, dstkey,
284                                            generated, creator,
285                                            inception, expire, mctx, ring, key);
286         if (result != ISC_R_SUCCESS && dstkey != NULL)
287                 dst_key_free(&dstkey);
288         return (result);
289 }
290
291 void
292 dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
293         REQUIRE(VALID_TSIG_KEY(source));
294         REQUIRE(targetp != NULL && *targetp == NULL);
295
296         isc_refcount_increment(&source->refs, NULL);
297         *targetp = source;
298 }
299
300 static void
301 tsigkey_free(dns_tsigkey_t *key) {
302         REQUIRE(VALID_TSIG_KEY(key));
303
304         key->magic = 0;
305         dns_name_free(&key->name, key->mctx);
306         if (algname_is_allocated(key->algorithm)) {
307                 dns_name_free(key->algorithm, key->mctx);
308                 isc_mem_put(key->mctx, key->algorithm, sizeof(dns_name_t));
309         }
310         if (key->key != NULL)
311                 dst_key_free(&key->key);
312         if (key->creator != NULL) {
313                 dns_name_free(key->creator, key->mctx);
314                 isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
315         }
316         isc_refcount_destroy(&key->refs);
317         isc_mem_put(key->mctx, key, sizeof(dns_tsigkey_t));
318 }
319
320 void
321 dns_tsigkey_detach(dns_tsigkey_t **keyp) {
322         dns_tsigkey_t *key;
323         unsigned int refs;
324
325         REQUIRE(keyp != NULL);
326         REQUIRE(VALID_TSIG_KEY(*keyp));
327
328         key = *keyp;
329         isc_refcount_decrement(&key->refs, &refs);
330
331         if (refs == 0)
332                 tsigkey_free(key);
333
334         *keyp = NULL;
335 }
336
337 void
338 dns_tsigkey_setdeleted(dns_tsigkey_t *key) {
339         REQUIRE(VALID_TSIG_KEY(key));
340         REQUIRE(key->ring != NULL);
341
342         RWLOCK(&key->ring->lock, isc_rwlocktype_write);
343         (void)dns_rbt_deletename(key->ring->keys, &key->name, ISC_FALSE);
344         RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
345 }
346
347 static void
348 buffer_putuint48(isc_buffer_t *b, isc_uint64_t val) {
349         isc_uint16_t valhi;
350         isc_uint32_t vallo;
351
352         valhi = (isc_uint16_t)(val >> 32);
353         vallo = (isc_uint32_t)(val & 0xFFFFFFFF);
354         isc_buffer_putuint16(b, valhi);
355         isc_buffer_putuint32(b, vallo);
356 }
357
358 isc_result_t
359 dns_tsig_sign(dns_message_t *msg) {
360         dns_tsigkey_t *key;
361         dns_rdata_any_tsig_t tsig, querytsig;
362         unsigned char data[128];
363         isc_buffer_t databuf, sigbuf;
364         isc_buffer_t *dynbuf;
365         dns_name_t *owner;
366         dns_rdata_t *rdata = NULL;
367         dns_rdatalist_t *datalist;
368         dns_rdataset_t *dataset;
369         isc_region_t r;
370         isc_stdtime_t now;
371         isc_mem_t *mctx;
372         dst_context_t *ctx = NULL;
373         isc_result_t ret;
374         unsigned char badtimedata[BADTIMELEN];
375         unsigned int sigsize = 0;
376
377         REQUIRE(msg != NULL);
378         REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg)));
379
380         /*
381          * If this is a response, there should be a query tsig.
382          */
383         if (is_response(msg) && msg->querytsig == NULL)
384                 return (DNS_R_EXPECTEDTSIG);
385
386         dynbuf = NULL;
387
388         mctx = msg->mctx;
389         key = dns_message_gettsigkey(msg);
390
391         tsig.mctx = mctx;
392         tsig.common.rdclass = dns_rdataclass_any;
393         tsig.common.rdtype = dns_rdatatype_tsig;
394         ISC_LINK_INIT(&tsig.common, link);
395         dns_name_init(&tsig.algorithm, NULL);
396         dns_name_clone(key->algorithm, &tsig.algorithm);
397
398         isc_stdtime_get(&now);
399         tsig.timesigned = now + msg->timeadjust;
400         tsig.fudge = DNS_TSIG_FUDGE;
401
402         tsig.originalid = msg->id;
403
404         isc_buffer_init(&databuf, data, sizeof(data));
405
406         if (is_response(msg))
407                 tsig.error = msg->querytsigstatus;
408         else
409                 tsig.error = dns_rcode_noerror;
410
411         if (tsig.error != dns_tsigerror_badtime) {
412                 tsig.otherlen = 0;
413                 tsig.other = NULL;
414         } else {
415                 isc_buffer_t otherbuf;
416
417                 tsig.otherlen = BADTIMELEN;
418                 tsig.other = badtimedata;
419                 isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
420                 buffer_putuint48(&otherbuf, tsig.timesigned);
421         }
422
423         if (key->key != NULL && tsig.error != dns_tsigerror_badsig) {
424                 unsigned char header[DNS_MESSAGE_HEADERLEN];
425                 isc_buffer_t headerbuf;
426
427                 ret = dst_context_create(key->key, mctx, &ctx);
428                 if (ret != ISC_R_SUCCESS)
429                         return (ret);
430
431                 /*
432                  * If this is a response, digest the query signature.
433                  */
434                 if (is_response(msg)) {
435                         dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
436
437                         ret = dns_rdataset_first(msg->querytsig);
438                         if (ret != ISC_R_SUCCESS)
439                                 goto cleanup_context;
440                         dns_rdataset_current(msg->querytsig, &querytsigrdata);
441                         ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
442                                                  NULL);
443                         if (ret != ISC_R_SUCCESS)
444                                 goto cleanup_context;
445                         isc_buffer_putuint16(&databuf, querytsig.siglen);
446                         if (isc_buffer_availablelength(&databuf) <
447                             querytsig.siglen)
448                         {
449                                 ret = ISC_R_NOSPACE;
450                                 goto cleanup_context;
451                         }
452                         isc_buffer_putmem(&databuf, querytsig.signature,
453                                           querytsig.siglen);
454                         isc_buffer_usedregion(&databuf, &r);
455                         ret = dst_context_adddata(ctx, &r);
456                         if (ret != ISC_R_SUCCESS)
457                                 goto cleanup_context;
458                 }
459
460                 /*
461                  * Digest the header.
462                  */
463                 isc_buffer_init(&headerbuf, header, sizeof(header));
464                 dns_message_renderheader(msg, &headerbuf);
465                 isc_buffer_usedregion(&headerbuf, &r);
466                 ret = dst_context_adddata(ctx, &r);
467                 if (ret != ISC_R_SUCCESS)
468                         goto cleanup_context;
469
470                 /*
471                  * Digest the remainder of the message.
472                  */
473                 isc_buffer_usedregion(msg->buffer, &r);
474                 isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
475                 ret = dst_context_adddata(ctx, &r);
476                 if (ret != ISC_R_SUCCESS)
477                         goto cleanup_context;
478
479                 if (msg->tcp_continuation == 0) {
480                         /*
481                          * Digest the name, class, ttl, alg.
482                          */
483                         dns_name_toregion(&key->name, &r);
484                         ret = dst_context_adddata(ctx, &r);
485                         if (ret != ISC_R_SUCCESS)
486                                 goto cleanup_context;
487
488                         isc_buffer_clear(&databuf);
489                         isc_buffer_putuint16(&databuf, dns_rdataclass_any);
490                         isc_buffer_putuint32(&databuf, 0); /* ttl */
491                         isc_buffer_usedregion(&databuf, &r);
492                         ret = dst_context_adddata(ctx, &r);
493                         if (ret != ISC_R_SUCCESS)
494                                 goto cleanup_context;
495
496                         dns_name_toregion(&tsig.algorithm, &r);
497                         ret = dst_context_adddata(ctx, &r);
498                         if (ret != ISC_R_SUCCESS)
499                                 goto cleanup_context;
500
501                 }
502                 /* Digest the timesigned and fudge */
503                 isc_buffer_clear(&databuf);
504                 if (tsig.error == dns_tsigerror_badtime)
505                         tsig.timesigned = querytsig.timesigned;
506                 buffer_putuint48(&databuf, tsig.timesigned);
507                 isc_buffer_putuint16(&databuf, tsig.fudge);
508                 isc_buffer_usedregion(&databuf, &r);
509                 ret = dst_context_adddata(ctx, &r);
510                 if (ret != ISC_R_SUCCESS)
511                         goto cleanup_context;
512
513                 if (msg->tcp_continuation == 0) {
514                         /*
515                          * Digest the error and other data length.
516                          */
517                         isc_buffer_clear(&databuf);
518                         isc_buffer_putuint16(&databuf, tsig.error);
519                         isc_buffer_putuint16(&databuf, tsig.otherlen);
520
521                         isc_buffer_usedregion(&databuf, &r);
522                         ret = dst_context_adddata(ctx, &r);
523                         if (ret != ISC_R_SUCCESS)
524                                 goto cleanup_context;
525
526                         /*
527                          * Digest the error and other data.
528                          */
529                         if (tsig.otherlen > 0) {
530                                 r.length = tsig.otherlen;
531                                 r.base = tsig.other;
532                                 ret = dst_context_adddata(ctx, &r);
533                                 if (ret != ISC_R_SUCCESS)
534                                         goto cleanup_context;
535                         }
536                 }
537
538                 ret = dst_key_sigsize(key->key, &sigsize);
539                 if (ret != ISC_R_SUCCESS)
540                         goto cleanup_context;
541                 tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize);
542                 if (tsig.signature == NULL) {
543                         ret = ISC_R_NOMEMORY;
544                         goto cleanup_context;
545                 }
546
547                 isc_buffer_init(&sigbuf, tsig.signature, sigsize);
548                 ret = dst_context_sign(ctx, &sigbuf);
549                 if (ret != ISC_R_SUCCESS)
550                         goto cleanup_signature;
551                 dst_context_destroy(&ctx);
552                 tsig.siglen = isc_buffer_usedlength(&sigbuf);
553         } else {
554                 tsig.siglen = 0;
555                 tsig.signature = NULL;
556         }
557
558         ret = dns_message_gettemprdata(msg, &rdata);
559         if (ret != ISC_R_SUCCESS)
560                 goto cleanup_signature;
561         ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
562         if (ret != ISC_R_SUCCESS)
563                 goto cleanup_rdata;
564         ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
565                                    dns_rdatatype_tsig, &tsig, dynbuf);
566         if (ret != ISC_R_SUCCESS)
567                 goto cleanup_dynbuf;
568
569         dns_message_takebuffer(msg, &dynbuf);
570
571         if (tsig.signature != NULL) {
572                 isc_mem_put(mctx, tsig.signature, sigsize);
573                 tsig.signature = NULL;
574         }
575
576         owner = NULL;
577         ret = dns_message_gettempname(msg, &owner);
578         if (ret != ISC_R_SUCCESS)
579                 goto cleanup_rdata;
580         dns_name_init(owner, NULL);
581         ret = dns_name_dup(&key->name, msg->mctx, owner);
582         if (ret != ISC_R_SUCCESS)
583                 goto cleanup_owner;
584
585         datalist = NULL;
586         ret = dns_message_gettemprdatalist(msg, &datalist);
587         if (ret != ISC_R_SUCCESS)
588                 goto cleanup_owner;
589         dataset = NULL;
590         ret = dns_message_gettemprdataset(msg, &dataset);
591         if (ret != ISC_R_SUCCESS)
592                 goto cleanup_rdatalist;
593         datalist->rdclass = dns_rdataclass_any;
594         datalist->type = dns_rdatatype_tsig;
595         datalist->covers = 0;
596         datalist->ttl = 0;
597         ISC_LIST_INIT(datalist->rdata);
598         ISC_LIST_APPEND(datalist->rdata, rdata, link);
599         dns_rdataset_init(dataset);
600         RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset)
601                       == ISC_R_SUCCESS);
602         msg->tsig = dataset;
603         msg->tsigname = owner;
604
605         return (ISC_R_SUCCESS);
606
607  cleanup_rdatalist:
608         dns_message_puttemprdatalist(msg, &datalist);
609  cleanup_owner:
610         dns_message_puttempname(msg, &owner);
611         goto cleanup_rdata;
612  cleanup_dynbuf:
613         isc_buffer_free(&dynbuf);
614  cleanup_rdata:
615         dns_message_puttemprdata(msg, &rdata);
616  cleanup_signature:
617         if (tsig.signature != NULL)
618                 isc_mem_put(mctx, tsig.signature, sigsize);
619  cleanup_context:
620         if (ctx != NULL)
621                 dst_context_destroy(&ctx);
622         return (ret);
623 }
624
625 isc_result_t
626 dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
627                 dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2)
628 {
629         dns_rdata_any_tsig_t tsig, querytsig;
630         isc_region_t r, source_r, header_r, sig_r;
631         isc_buffer_t databuf;
632         unsigned char data[32];
633         dns_name_t *keyname;
634         dns_rdata_t rdata = DNS_RDATA_INIT;
635         isc_stdtime_t now;
636         isc_result_t ret;
637         dns_tsigkey_t *tsigkey;
638         dst_key_t *key = NULL;
639         unsigned char header[DNS_MESSAGE_HEADERLEN];
640         dst_context_t *ctx = NULL;
641         isc_mem_t *mctx;
642         isc_uint16_t addcount, id;
643
644         REQUIRE(source != NULL);
645         REQUIRE(DNS_MESSAGE_VALID(msg));
646         tsigkey = dns_message_gettsigkey(msg);
647         REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
648
649         msg->verify_attempted = 1;
650
651         if (msg->tcp_continuation) {
652                 if (tsigkey == NULL || msg->querytsig == NULL)
653                         return (DNS_R_UNEXPECTEDTSIG);
654                 return (tsig_verify_tcp(source, msg));
655         }
656
657         /*
658          * There should be a TSIG record...
659          */
660         if (msg->tsig == NULL)
661                 return (DNS_R_EXPECTEDTSIG);
662
663         /*
664          * If this is a response and there's no key or query TSIG, there
665          * shouldn't be one on the response.
666          */
667         if (is_response(msg) &&
668             (tsigkey == NULL || msg->querytsig == NULL))
669                 return (DNS_R_UNEXPECTEDTSIG);
670
671         mctx = msg->mctx;
672
673         /*
674          * If we're here, we know the message is well formed and contains a
675          * TSIG record.
676          */
677
678         keyname = msg->tsigname;
679         ret = dns_rdataset_first(msg->tsig);
680         if (ret != ISC_R_SUCCESS)
681                 return (ret);
682         dns_rdataset_current(msg->tsig, &rdata);
683         ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
684         if (ret != ISC_R_SUCCESS)
685                 return (ret);
686         dns_rdata_reset(&rdata);
687         if (is_response(msg)) {
688                 ret = dns_rdataset_first(msg->querytsig);
689                 if (ret != ISC_R_SUCCESS)
690                         return (ret);
691                 dns_rdataset_current(msg->querytsig, &rdata);
692                 ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
693                 if (ret != ISC_R_SUCCESS)
694                         return (ret);
695         }
696
697         /*
698          * Do the key name and algorithm match that of the query?
699          */
700         if (is_response(msg) &&
701             (!dns_name_equal(keyname, &tsigkey->name) ||
702              !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)))
703         {
704                 msg->tsigstatus = dns_tsigerror_badkey;
705                 tsig_log(msg->tsigkey, 2,
706                          "key name and algorithm do not match");
707                 return (DNS_R_TSIGVERIFYFAILURE);
708         }
709
710         /*
711          * Get the current time.
712          */
713         isc_stdtime_get(&now);
714
715         /*
716          * Find dns_tsigkey_t based on keyname.
717          */
718         if (tsigkey == NULL) {
719                 ret = ISC_R_NOTFOUND;
720                 if (ring1 != NULL)
721                         ret = dns_tsigkey_find(&tsigkey, keyname,
722                                                &tsig.algorithm, ring1);
723                 if (ret == ISC_R_NOTFOUND && ring2 != NULL)
724                         ret = dns_tsigkey_find(&tsigkey, keyname,
725                                                &tsig.algorithm, ring2);
726                 if (ret != ISC_R_SUCCESS) {
727                         msg->tsigstatus = dns_tsigerror_badkey;
728                         ret = dns_tsigkey_create(keyname, &tsig.algorithm,
729                                                  NULL, 0, ISC_FALSE, NULL,
730                                                  now, now,
731                                                  mctx, NULL, &msg->tsigkey);
732                         if (ret != ISC_R_SUCCESS)
733                                 return (ret);
734                         tsig_log(msg->tsigkey, 2, "unknown key");
735                         return (DNS_R_TSIGVERIFYFAILURE);
736                 }
737                 msg->tsigkey = tsigkey;
738         }
739
740         key = tsigkey->key;
741
742         /*
743          * Is the time ok?
744          */
745         if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
746                 msg->tsigstatus = dns_tsigerror_badtime;
747                 tsig_log(msg->tsigkey, 2, "signature has expired");
748                 return (DNS_R_CLOCKSKEW);
749         } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
750                 msg->tsigstatus = dns_tsigerror_badtime;
751                 tsig_log(msg->tsigkey, 2, "signature is in the future");
752                 return (DNS_R_CLOCKSKEW);
753         }
754
755         if (tsig.siglen > 0) {
756                 sig_r.base = tsig.signature;
757                 sig_r.length = tsig.siglen;
758
759                 ret = dst_context_create(key, mctx, &ctx);
760                 if (ret != ISC_R_SUCCESS)
761                         return (ret);
762
763                 if (is_response(msg)) {
764                         isc_buffer_init(&databuf, data, sizeof(data));
765                         isc_buffer_putuint16(&databuf, querytsig.siglen);
766                         isc_buffer_usedregion(&databuf, &r);
767                         ret = dst_context_adddata(ctx, &r);
768                         if (ret != ISC_R_SUCCESS)
769                                 goto cleanup_context;
770                         if (querytsig.siglen > 0) {
771                                 r.length = querytsig.siglen;
772                                 r.base = querytsig.signature;
773                                 ret = dst_context_adddata(ctx, &r);
774                                 if (ret != ISC_R_SUCCESS)
775                                         goto cleanup_context;
776                         }
777                 }
778
779                 /*
780                  * Extract the header.
781                  */
782                 isc_buffer_usedregion(source, &r);
783                 memcpy(header, r.base, DNS_MESSAGE_HEADERLEN);
784                 isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
785
786                 /*
787                  * Decrement the additional field counter.
788                  */
789                 memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
790                 addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
791                 memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
792
793                 /*
794                  * Put in the original id.
795                  */
796                 id = htons(tsig.originalid);
797                 memcpy(&header[0], &id, 2);
798
799                 /*
800                  * Digest the modified header.
801                  */
802                 header_r.base = (unsigned char *) header;
803                 header_r.length = DNS_MESSAGE_HEADERLEN;
804                 ret = dst_context_adddata(ctx, &header_r);
805                 if (ret != ISC_R_SUCCESS)
806                         goto cleanup_context;
807
808                 /*
809                  * Digest all non-TSIG records.
810                  */
811                 isc_buffer_usedregion(source, &source_r);
812                 r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
813                 r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
814                 ret = dst_context_adddata(ctx, &r);
815                 if (ret != ISC_R_SUCCESS)
816                         goto cleanup_context;
817
818                 /*
819                  * Digest the key name.
820                  */
821                 dns_name_toregion(&tsigkey->name, &r);
822                 ret = dst_context_adddata(ctx, &r);
823                 if (ret != ISC_R_SUCCESS)
824                         goto cleanup_context;
825
826                 isc_buffer_init(&databuf, data, sizeof(data));
827                 isc_buffer_putuint16(&databuf, tsig.common.rdclass);
828                 isc_buffer_putuint32(&databuf, msg->tsig->ttl);
829                 isc_buffer_usedregion(&databuf, &r);
830                 ret = dst_context_adddata(ctx, &r);
831                 if (ret != ISC_R_SUCCESS)
832                         goto cleanup_context;
833
834                 /*
835                  * Digest the key algorithm.
836                  */
837                 dns_name_toregion(tsigkey->algorithm, &r);
838                 ret = dst_context_adddata(ctx, &r);
839                 if (ret != ISC_R_SUCCESS)
840                         goto cleanup_context;
841
842                 isc_buffer_clear(&databuf);
843                 buffer_putuint48(&databuf, tsig.timesigned);
844                 isc_buffer_putuint16(&databuf, tsig.fudge);
845                 isc_buffer_putuint16(&databuf, tsig.error);
846                 isc_buffer_putuint16(&databuf, tsig.otherlen);
847                 isc_buffer_usedregion(&databuf, &r);
848                 ret = dst_context_adddata(ctx, &r);
849                 if (ret != ISC_R_SUCCESS)
850                         goto cleanup_context;
851
852                 if (tsig.otherlen > 0) {
853                         r.base = tsig.other;
854                         r.length = tsig.otherlen;
855                         ret = dst_context_adddata(ctx, &r);
856                         if (ret != ISC_R_SUCCESS)
857                                 goto cleanup_context;
858                 }
859
860                 ret = dst_context_verify(ctx, &sig_r);
861                 if (ret == DST_R_VERIFYFAILURE) {
862                         msg->tsigstatus = dns_tsigerror_badsig;
863                         ret = DNS_R_TSIGVERIFYFAILURE;
864                         tsig_log(msg->tsigkey, 2,
865                                  "signature failed to verify");
866                         goto cleanup_context;
867                 } else if (ret != ISC_R_SUCCESS)
868                         goto cleanup_context;
869
870                 dst_context_destroy(&ctx);
871         } else if (tsig.error != dns_tsigerror_badsig &&
872                    tsig.error != dns_tsigerror_badkey)
873         {
874                 msg->tsigstatus = dns_tsigerror_badsig;
875                 tsig_log(msg->tsigkey, 2, "signature was empty");
876                 return (DNS_R_TSIGVERIFYFAILURE);
877         }
878
879         msg->tsigstatus = dns_rcode_noerror;
880
881         if (tsig.error != dns_rcode_noerror) {
882                 if (tsig.error == dns_tsigerror_badtime)
883                         return (DNS_R_CLOCKSKEW);
884                 else
885                         return (DNS_R_TSIGERRORSET);
886         }
887
888         msg->verified_sig = 1;
889
890         return (ISC_R_SUCCESS);
891
892 cleanup_context:
893         if (ctx != NULL)
894                 dst_context_destroy(&ctx);
895
896         return (ret);
897 }
898
899 static isc_result_t
900 tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
901         dns_rdata_any_tsig_t tsig, querytsig;
902         isc_region_t r, source_r, header_r, sig_r;
903         isc_buffer_t databuf;
904         unsigned char data[32];
905         dns_name_t *keyname;
906         dns_rdata_t rdata = DNS_RDATA_INIT;
907         isc_stdtime_t now;
908         isc_result_t ret;
909         dns_tsigkey_t *tsigkey;
910         dst_key_t *key = NULL;
911         unsigned char header[DNS_MESSAGE_HEADERLEN];
912         isc_uint16_t addcount, id;
913         isc_boolean_t has_tsig = ISC_FALSE;
914         isc_mem_t *mctx;
915
916         REQUIRE(source != NULL);
917         REQUIRE(msg != NULL);
918         REQUIRE(dns_message_gettsigkey(msg) != NULL);
919         REQUIRE(msg->tcp_continuation == 1);
920         REQUIRE(msg->querytsig != NULL);
921
922         if (!is_response(msg))
923                 return (DNS_R_EXPECTEDRESPONSE);
924
925         mctx = msg->mctx;
926
927         tsigkey = dns_message_gettsigkey(msg);
928
929         /*
930          * Extract and parse the previous TSIG
931          */
932         ret = dns_rdataset_first(msg->querytsig);
933         if (ret != ISC_R_SUCCESS)
934                 return (ret);
935         dns_rdataset_current(msg->querytsig, &rdata);
936         ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
937         if (ret != ISC_R_SUCCESS)
938                 return (ret);
939         dns_rdata_reset(&rdata);
940
941         /*
942          * If there is a TSIG in this message, do some checks.
943          */
944         if (msg->tsig != NULL) {
945                 has_tsig = ISC_TRUE;
946
947                 keyname = msg->tsigname;
948                 ret = dns_rdataset_first(msg->tsig);
949                 if (ret != ISC_R_SUCCESS)
950                         goto cleanup_querystruct;
951                 dns_rdataset_current(msg->tsig, &rdata);
952                 ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
953                 if (ret != ISC_R_SUCCESS)
954                         goto cleanup_querystruct;
955
956                 /*
957                  * Do the key name and algorithm match that of the query?
958                  */
959                 if (!dns_name_equal(keyname, &tsigkey->name) ||
960                     !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))
961                 {
962                         msg->tsigstatus = dns_tsigerror_badkey;
963                         ret = DNS_R_TSIGVERIFYFAILURE;
964                         tsig_log(msg->tsigkey, 2,
965                                  "key name and algorithm do not match");
966                         goto cleanup_querystruct;
967                 }
968
969                 /*
970                  * Is the time ok?
971                  */
972                 isc_stdtime_get(&now);
973
974                 if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
975                         msg->tsigstatus = dns_tsigerror_badtime;
976                         tsig_log(msg->tsigkey, 2, "signature has expired");
977                         ret = DNS_R_CLOCKSKEW;
978                         goto cleanup_querystruct;
979                 } else if (now + msg->timeadjust <
980                            tsig.timesigned - tsig.fudge)
981                 {
982                         msg->tsigstatus = dns_tsigerror_badtime;
983                         tsig_log(msg->tsigkey, 2,
984                                  "signature is in the future");
985                         ret = DNS_R_CLOCKSKEW;
986                         goto cleanup_querystruct;
987                 }
988         }
989
990         key = tsigkey->key;
991
992         if (msg->tsigctx == NULL) {
993                 ret = dst_context_create(key, mctx, &msg->tsigctx);
994                 if (ret != ISC_R_SUCCESS)
995                         goto cleanup_querystruct;
996
997                 /*
998                  * Digest the length of the query signature
999                  */
1000                 isc_buffer_init(&databuf, data, sizeof(data));
1001                 isc_buffer_putuint16(&databuf, querytsig.siglen);
1002                 isc_buffer_usedregion(&databuf, &r);
1003                 ret = dst_context_adddata(msg->tsigctx, &r);
1004                 if (ret != ISC_R_SUCCESS)
1005                         goto cleanup_context;
1006
1007                 /*
1008                  * Digest the data of the query signature
1009                  */
1010                 if (querytsig.siglen > 0) {
1011                         r.length = querytsig.siglen;
1012                         r.base = querytsig.signature;
1013                         ret = dst_context_adddata(msg->tsigctx, &r);
1014                         if (ret != ISC_R_SUCCESS)
1015                                 goto cleanup_context;
1016                 }
1017         }
1018
1019         /*
1020          * Extract the header.
1021          */
1022         isc_buffer_usedregion(source, &r);
1023         memcpy(header, r.base, DNS_MESSAGE_HEADERLEN);
1024         isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1025
1026         /*
1027          * Decrement the additional field counter if necessary.
1028          */
1029         if (has_tsig) {
1030                 memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1031                 addcount = htons((isc_uint16_t)(ntohs(addcount) - 1));
1032                 memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1033         }
1034
1035         /*
1036          * Put in the original id.
1037          */
1038         /* XXX Can TCP transfers be forwarded?  How would that work? */
1039         if (has_tsig) {
1040                 id = htons(tsig.originalid);
1041                 memcpy(&header[0], &id, 2);
1042         }
1043
1044         /*
1045          * Digest the modified header.
1046          */
1047         header_r.base = (unsigned char *) header;
1048         header_r.length = DNS_MESSAGE_HEADERLEN;
1049         ret = dst_context_adddata(msg->tsigctx, &header_r);
1050         if (ret != ISC_R_SUCCESS)
1051                 goto cleanup_context;
1052
1053         /*
1054          * Digest all non-TSIG records.
1055          */
1056         isc_buffer_usedregion(source, &source_r);
1057         r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
1058         if (has_tsig)
1059                 r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
1060         else
1061                 r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
1062         ret = dst_context_adddata(msg->tsigctx, &r);
1063         if (ret != ISC_R_SUCCESS)
1064                 goto cleanup_context;
1065
1066         /*
1067          * Digest the time signed and fudge.
1068          */
1069         if (has_tsig) {
1070                 isc_buffer_init(&databuf, data, sizeof(data));
1071                 buffer_putuint48(&databuf, tsig.timesigned);
1072                 isc_buffer_putuint16(&databuf, tsig.fudge);
1073                 isc_buffer_usedregion(&databuf, &r);
1074                 ret = dst_context_adddata(msg->tsigctx, &r);
1075                 if (ret != ISC_R_SUCCESS)
1076                         goto cleanup_context;
1077
1078                 sig_r.base = tsig.signature;
1079                 sig_r.length = tsig.siglen;
1080                 if (tsig.siglen == 0) {
1081                         if (tsig.error != dns_rcode_noerror) {
1082                                 if (tsig.error == dns_tsigerror_badtime)
1083                                         ret = DNS_R_CLOCKSKEW;
1084                                 else
1085                                         ret = DNS_R_TSIGERRORSET;
1086                         } else {
1087                                 tsig_log(msg->tsigkey, 2,
1088                                          "signature is empty");
1089                                 ret = DNS_R_TSIGVERIFYFAILURE;
1090                         }
1091                         goto cleanup_context;
1092                 }
1093
1094                 ret = dst_context_verify(msg->tsigctx, &sig_r);
1095                 if (ret == DST_R_VERIFYFAILURE) {
1096                         msg->tsigstatus = dns_tsigerror_badsig;
1097                         tsig_log(msg->tsigkey, 2,
1098                                  "signature failed to verify");
1099                         ret = DNS_R_TSIGVERIFYFAILURE;
1100                         goto cleanup_context;
1101                 }
1102                 else if (ret != ISC_R_SUCCESS)
1103                         goto cleanup_context;
1104
1105                 dst_context_destroy(&msg->tsigctx);
1106         }
1107
1108         msg->tsigstatus = dns_rcode_noerror;
1109         return (ISC_R_SUCCESS);
1110
1111  cleanup_context:
1112         dst_context_destroy(&msg->tsigctx);
1113
1114  cleanup_querystruct:
1115         dns_rdata_freestruct(&querytsig);
1116
1117         return (ret);
1118
1119 }
1120
1121 isc_result_t
1122 dns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name,
1123                  dns_name_t *algorithm, dns_tsig_keyring_t *ring)
1124 {
1125         dns_tsigkey_t *key;
1126         isc_stdtime_t now;
1127         isc_result_t result;
1128
1129         REQUIRE(tsigkey != NULL);
1130         REQUIRE(*tsigkey == NULL);
1131         REQUIRE(name != NULL);
1132         REQUIRE(ring != NULL);
1133
1134         isc_stdtime_get(&now);
1135         RWLOCK(&ring->lock, isc_rwlocktype_read);
1136         key = NULL;
1137         result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
1138         if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
1139                 RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1140                 return (ISC_R_NOTFOUND);
1141         }
1142         if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
1143                 RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1144                 return (ISC_R_NOTFOUND);
1145         }
1146         if (key->inception != key->expire && key->expire < now) {
1147                 /*
1148                  * The key has expired.
1149                  */
1150                 RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1151                 RWLOCK(&ring->lock, isc_rwlocktype_write);
1152                 (void) dns_rbt_deletename(ring->keys, name, ISC_FALSE);
1153                 RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1154                 return (ISC_R_NOTFOUND);
1155         }
1156
1157         isc_refcount_increment(&key->refs, NULL);
1158         RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1159         *tsigkey = key;
1160         return (ISC_R_SUCCESS);
1161 }
1162
1163 static void
1164 free_tsignode(void *node, void *_unused) {
1165         dns_tsigkey_t *key;
1166
1167         UNUSED(_unused);
1168
1169         REQUIRE(node != NULL);
1170
1171         key = node;
1172         dns_tsigkey_detach(&key);
1173 }
1174
1175 isc_result_t
1176 dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
1177         isc_result_t result;
1178         dns_tsig_keyring_t *ring;
1179
1180         REQUIRE(mctx != NULL);
1181         REQUIRE(ringp != NULL);
1182         REQUIRE(*ringp == NULL);
1183
1184         ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t));
1185         if (ring == NULL)
1186                 return (ISC_R_NOMEMORY);
1187
1188         result = isc_rwlock_init(&ring->lock, 0, 0);
1189         if (result != ISC_R_SUCCESS) {
1190                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1191                                  "isc_rwlock_init() failed: %s",
1192                                  isc_result_totext(result));
1193                 return (ISC_R_UNEXPECTED);
1194         }
1195
1196         ring->keys = NULL;
1197         result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys);
1198         if (result != ISC_R_SUCCESS) {
1199                 isc_rwlock_destroy(&ring->lock);
1200                 isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
1201                 return (result);
1202         }
1203
1204         ring->mctx = mctx;
1205
1206         *ringp = ring;
1207         return (ISC_R_SUCCESS);
1208 }
1209
1210 void
1211 dns_tsigkeyring_destroy(dns_tsig_keyring_t **ringp) {
1212         dns_tsig_keyring_t *ring;
1213
1214         REQUIRE(ringp != NULL);
1215         REQUIRE(*ringp != NULL);
1216
1217         ring = *ringp;
1218         *ringp = NULL;
1219
1220         dns_rbt_destroy(&ring->keys);
1221         isc_rwlock_destroy(&ring->lock);
1222         isc_mem_put(ring->mctx, ring, sizeof(dns_tsig_keyring_t));
1223 }