1*4ac1c27eSchristos /* $NetBSD: tsig.c,v 1.12 2023/01/25 21:43:30 christos Exp $ */
2e2b1b9c0Schristos
3e2b1b9c0Schristos /*
4e2b1b9c0Schristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5e2b1b9c0Schristos *
6c0b5d9fbSchristos * SPDX-License-Identifier: MPL-2.0
7c0b5d9fbSchristos *
8e2b1b9c0Schristos * This Source Code Form is subject to the terms of the Mozilla Public
9e2b1b9c0Schristos * License, v. 2.0. If a copy of the MPL was not distributed with this
1073584a28Schristos * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11e2b1b9c0Schristos *
12e2b1b9c0Schristos * See the COPYRIGHT file distributed with this work for additional
13e2b1b9c0Schristos * information regarding copyright ownership.
14e2b1b9c0Schristos */
15e2b1b9c0Schristos
16e2b1b9c0Schristos /*! \file */
17f2e20987Schristos
18f2e20987Schristos #include <inttypes.h>
19f2e20987Schristos #include <stdbool.h>
20e2b1b9c0Schristos #include <stdlib.h>
21e2b1b9c0Schristos
22e2b1b9c0Schristos #include <isc/buffer.h>
23e2b1b9c0Schristos #include <isc/mem.h>
24e2b1b9c0Schristos #include <isc/print.h>
25e2b1b9c0Schristos #include <isc/refcount.h>
26e2b1b9c0Schristos #include <isc/serial.h>
27e2b1b9c0Schristos #include <isc/string.h> /* Required for HP/UX (and others?) */
28e2b1b9c0Schristos #include <isc/time.h>
299742fdb4Schristos #include <isc/util.h>
30e2b1b9c0Schristos
31e2b1b9c0Schristos #include <pk11/site.h>
32e2b1b9c0Schristos
339742fdb4Schristos #include <dns/fixedname.h>
34e2b1b9c0Schristos #include <dns/keyvalues.h>
35e2b1b9c0Schristos #include <dns/log.h>
36e2b1b9c0Schristos #include <dns/message.h>
37e2b1b9c0Schristos #include <dns/rbt.h>
38e2b1b9c0Schristos #include <dns/rdata.h>
39e2b1b9c0Schristos #include <dns/rdatalist.h>
40e2b1b9c0Schristos #include <dns/rdataset.h>
41e2b1b9c0Schristos #include <dns/rdatastruct.h>
42e2b1b9c0Schristos #include <dns/result.h>
43e2b1b9c0Schristos #include <dns/tsig.h>
44e2b1b9c0Schristos
45e2b1b9c0Schristos #include <dst/result.h>
46e2b1b9c0Schristos
479742fdb4Schristos #include "tsig_p.h"
489742fdb4Schristos
49e2b1b9c0Schristos #define TSIG_MAGIC ISC_MAGIC('T', 'S', 'I', 'G')
50e2b1b9c0Schristos #define VALID_TSIG_KEY(x) ISC_MAGIC_VALID(x, TSIG_MAGIC)
51e2b1b9c0Schristos
52e2b1b9c0Schristos #ifndef DNS_TSIG_MAXGENERATEDKEYS
53e2b1b9c0Schristos #define DNS_TSIG_MAXGENERATEDKEYS 4096
549742fdb4Schristos #endif /* ifndef DNS_TSIG_MAXGENERATEDKEYS */
55e2b1b9c0Schristos
56f2e20987Schristos #define is_response(msg) ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
57e2b1b9c0Schristos
58e2b1b9c0Schristos #define BADTIMELEN 6
59e2b1b9c0Schristos
60e2b1b9c0Schristos static unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int";
61e2b1b9c0Schristos static unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 };
62e2b1b9c0Schristos
639742fdb4Schristos static dns_name_t const hmacmd5 = DNS_NAME_INITABSOLUTE(hmacmd5_ndata,
649742fdb4Schristos hmacmd5_offsets);
65e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacmd5_name = &hmacmd5;
66e2b1b9c0Schristos
67e2b1b9c0Schristos static unsigned char gsstsig_ndata[] = "\010gss-tsig";
68e2b1b9c0Schristos static unsigned char gsstsig_offsets[] = { 0, 9 };
699742fdb4Schristos static dns_name_t const gsstsig = DNS_NAME_INITABSOLUTE(gsstsig_ndata,
709742fdb4Schristos gsstsig_offsets);
71e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_gssapi_name = &gsstsig;
72e2b1b9c0Schristos
73e2b1b9c0Schristos /*
74e2b1b9c0Schristos * Since Microsoft doesn't follow its own standard, we will use this
75e2b1b9c0Schristos * alternate name as a second guess.
76e2b1b9c0Schristos */
77e2b1b9c0Schristos static unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com";
78e2b1b9c0Schristos static unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 };
799742fdb4Schristos static dns_name_t const gsstsigms = DNS_NAME_INITABSOLUTE(gsstsigms_ndata,
809742fdb4Schristos gsstsigms_offsets);
81e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_gssapims_name = &gsstsigms;
82e2b1b9c0Schristos
83e2b1b9c0Schristos static unsigned char hmacsha1_ndata[] = "\011hmac-sha1";
84e2b1b9c0Schristos static unsigned char hmacsha1_offsets[] = { 0, 10 };
859742fdb4Schristos static dns_name_t const hmacsha1 = DNS_NAME_INITABSOLUTE(hmacsha1_ndata,
869742fdb4Schristos hmacsha1_offsets);
87e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1;
88e2b1b9c0Schristos
89e2b1b9c0Schristos static unsigned char hmacsha224_ndata[] = "\013hmac-sha224";
90e2b1b9c0Schristos static unsigned char hmacsha224_offsets[] = { 0, 12 };
919742fdb4Schristos static dns_name_t const hmacsha224 = DNS_NAME_INITABSOLUTE(hmacsha224_ndata,
929742fdb4Schristos hmacsha224_offsets);
93e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224;
94e2b1b9c0Schristos
95e2b1b9c0Schristos static unsigned char hmacsha256_ndata[] = "\013hmac-sha256";
96e2b1b9c0Schristos static unsigned char hmacsha256_offsets[] = { 0, 12 };
979742fdb4Schristos static dns_name_t const hmacsha256 = DNS_NAME_INITABSOLUTE(hmacsha256_ndata,
989742fdb4Schristos hmacsha256_offsets);
99e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256;
100e2b1b9c0Schristos
101e2b1b9c0Schristos static unsigned char hmacsha384_ndata[] = "\013hmac-sha384";
102e2b1b9c0Schristos static unsigned char hmacsha384_offsets[] = { 0, 12 };
1039742fdb4Schristos static dns_name_t const hmacsha384 = DNS_NAME_INITABSOLUTE(hmacsha384_ndata,
1049742fdb4Schristos hmacsha384_offsets);
105e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384;
106e2b1b9c0Schristos
107e2b1b9c0Schristos static unsigned char hmacsha512_ndata[] = "\013hmac-sha512";
108e2b1b9c0Schristos static unsigned char hmacsha512_offsets[] = { 0, 12 };
1099742fdb4Schristos static dns_name_t const hmacsha512 = DNS_NAME_INITABSOLUTE(hmacsha512_ndata,
1109742fdb4Schristos hmacsha512_offsets);
111e2b1b9c0Schristos LIBDNS_EXTERNAL_DATA const dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512;
112e2b1b9c0Schristos
113e2b1b9c0Schristos static const struct {
114e2b1b9c0Schristos const dns_name_t *name;
115e2b1b9c0Schristos unsigned int dstalg;
1169742fdb4Schristos } known_algs[] = { { &hmacmd5, DST_ALG_HMACMD5 },
117e2b1b9c0Schristos { &gsstsig, DST_ALG_GSSAPI },
118e2b1b9c0Schristos { &gsstsigms, DST_ALG_GSSAPI },
119e2b1b9c0Schristos { &hmacsha1, DST_ALG_HMACSHA1 },
120e2b1b9c0Schristos { &hmacsha224, DST_ALG_HMACSHA224 },
121e2b1b9c0Schristos { &hmacsha256, DST_ALG_HMACSHA256 },
122e2b1b9c0Schristos { &hmacsha384, DST_ALG_HMACSHA384 },
1239742fdb4Schristos { &hmacsha512, DST_ALG_HMACSHA512 } };
124e2b1b9c0Schristos
125e2b1b9c0Schristos static isc_result_t
126e2b1b9c0Schristos tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg);
127e2b1b9c0Schristos
128e2b1b9c0Schristos static void
129e2b1b9c0Schristos tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...)
130e2b1b9c0Schristos ISC_FORMAT_PRINTF(3, 4);
131e2b1b9c0Schristos
132e2b1b9c0Schristos static void
133e2b1b9c0Schristos cleanup_ring(dns_tsig_keyring_t *ring);
134e2b1b9c0Schristos static void
135e2b1b9c0Schristos tsigkey_free(dns_tsigkey_t *key);
136e2b1b9c0Schristos
137f2e20987Schristos bool
dns__tsig_algvalid(unsigned int alg)138e2b1b9c0Schristos dns__tsig_algvalid(unsigned int alg) {
1399742fdb4Schristos return (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 ||
1409742fdb4Schristos alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 ||
1419742fdb4Schristos alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512);
142e2b1b9c0Schristos }
143e2b1b9c0Schristos
144e2b1b9c0Schristos static void
tsig_log(dns_tsigkey_t * key,int level,const char * fmt,...)145e2b1b9c0Schristos tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) {
146e2b1b9c0Schristos va_list ap;
147e2b1b9c0Schristos char message[4096];
148e2b1b9c0Schristos char namestr[DNS_NAME_FORMATSIZE];
149e2b1b9c0Schristos char creatorstr[DNS_NAME_FORMATSIZE];
150e2b1b9c0Schristos
151803e9293Schristos if (!isc_log_wouldlog(dns_lctx, level)) {
152e2b1b9c0Schristos return;
1539742fdb4Schristos }
154e2b1b9c0Schristos if (key != NULL) {
155e2b1b9c0Schristos dns_name_format(&key->name, namestr, sizeof(namestr));
156e2b1b9c0Schristos } else {
157e2b1b9c0Schristos strlcpy(namestr, "<null>", sizeof(namestr));
158e2b1b9c0Schristos }
159e2b1b9c0Schristos
160e2b1b9c0Schristos if (key != NULL && key->generated && key->creator) {
161e2b1b9c0Schristos dns_name_format(key->creator, creatorstr, sizeof(creatorstr));
162e2b1b9c0Schristos } else {
163e2b1b9c0Schristos strlcpy(creatorstr, "<null>", sizeof(creatorstr));
164e2b1b9c0Schristos }
165e2b1b9c0Schristos
166e2b1b9c0Schristos va_start(ap, fmt);
167e2b1b9c0Schristos vsnprintf(message, sizeof(message), fmt, ap);
168e2b1b9c0Schristos va_end(ap);
169e2b1b9c0Schristos if (key != NULL && key->generated) {
1709742fdb4Schristos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
1719742fdb4Schristos DNS_LOGMODULE_TSIG, level,
1729742fdb4Schristos "tsig key '%s' (%s): %s", namestr, creatorstr,
1739742fdb4Schristos message);
174e2b1b9c0Schristos } else {
1759742fdb4Schristos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
1769742fdb4Schristos DNS_LOGMODULE_TSIG, level, "tsig key '%s': %s",
1779742fdb4Schristos namestr, message);
178e2b1b9c0Schristos }
179e2b1b9c0Schristos }
180e2b1b9c0Schristos
181e2b1b9c0Schristos static void
remove_fromring(dns_tsigkey_t * tkey)182e2b1b9c0Schristos remove_fromring(dns_tsigkey_t *tkey) {
183e2b1b9c0Schristos if (tkey->generated) {
184e2b1b9c0Schristos ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
185e2b1b9c0Schristos tkey->ring->generated--;
186e2b1b9c0Schristos }
187f2e20987Schristos (void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, false);
188e2b1b9c0Schristos }
189e2b1b9c0Schristos
190e2b1b9c0Schristos static void
adjust_lru(dns_tsigkey_t * tkey)191e2b1b9c0Schristos adjust_lru(dns_tsigkey_t *tkey) {
192e2b1b9c0Schristos if (tkey->generated) {
193e2b1b9c0Schristos RWLOCK(&tkey->ring->lock, isc_rwlocktype_write);
194e2b1b9c0Schristos /*
195e2b1b9c0Schristos * We may have been removed from the LRU list between
1969742fdb4Schristos * removing the read lock and acquiring the write lock.
197e2b1b9c0Schristos */
1989742fdb4Schristos if (ISC_LINK_LINKED(tkey, link) && tkey->ring->lru.tail != tkey)
199e2b1b9c0Schristos {
200e2b1b9c0Schristos ISC_LIST_UNLINK(tkey->ring->lru, tkey, link);
201e2b1b9c0Schristos ISC_LIST_APPEND(tkey->ring->lru, tkey, link);
202e2b1b9c0Schristos }
203e2b1b9c0Schristos RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write);
204e2b1b9c0Schristos }
205e2b1b9c0Schristos }
206e2b1b9c0Schristos
207e2b1b9c0Schristos /*
208e2b1b9c0Schristos * A supplemental routine just to add a key to ring. Note that reference
209e2b1b9c0Schristos * counter should be counted separately because we may be adding the key
210e2b1b9c0Schristos * as part of creation of the key, in which case the reference counter was
211e2b1b9c0Schristos * already initialized. Also note we don't need RWLOCK for the reference
212e2b1b9c0Schristos * counter: it's protected by a separate lock.
213e2b1b9c0Schristos */
214e2b1b9c0Schristos static isc_result_t
keyring_add(dns_tsig_keyring_t * ring,const dns_name_t * name,dns_tsigkey_t * tkey)215e2b1b9c0Schristos keyring_add(dns_tsig_keyring_t *ring, const dns_name_t *name,
2169742fdb4Schristos dns_tsigkey_t *tkey) {
217e2b1b9c0Schristos isc_result_t result;
218e2b1b9c0Schristos
219e2b1b9c0Schristos RWLOCK(&ring->lock, isc_rwlocktype_write);
220e2b1b9c0Schristos ring->writecount++;
221e2b1b9c0Schristos
222e2b1b9c0Schristos /*
223e2b1b9c0Schristos * Do on the fly cleaning. Find some nodes we might not
224e2b1b9c0Schristos * want around any more.
225e2b1b9c0Schristos */
226e2b1b9c0Schristos if (ring->writecount > 10) {
227e2b1b9c0Schristos cleanup_ring(ring);
228e2b1b9c0Schristos ring->writecount = 0;
229e2b1b9c0Schristos }
230e2b1b9c0Schristos
231e2b1b9c0Schristos result = dns_rbt_addname(ring->keys, name, tkey);
232e2b1b9c0Schristos if (result == ISC_R_SUCCESS && tkey->generated) {
233e2b1b9c0Schristos /*
234e2b1b9c0Schristos * Add the new key to the LRU list and remove the least
235e2b1b9c0Schristos * recently used key if there are too many keys on the list.
236e2b1b9c0Schristos */
237e2b1b9c0Schristos ISC_LIST_APPEND(ring->lru, tkey, link);
2389742fdb4Schristos if (ring->generated++ > ring->maxgenerated) {
239e2b1b9c0Schristos remove_fromring(ISC_LIST_HEAD(ring->lru));
240e2b1b9c0Schristos }
2419742fdb4Schristos }
242e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_write);
243e2b1b9c0Schristos
244e2b1b9c0Schristos return (result);
245e2b1b9c0Schristos }
246e2b1b9c0Schristos
247e2b1b9c0Schristos isc_result_t
dns_tsigkey_createfromkey(const dns_name_t * name,const dns_name_t * algorithm,dst_key_t * dstkey,bool generated,const 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)248e2b1b9c0Schristos dns_tsigkey_createfromkey(const dns_name_t *name, const dns_name_t *algorithm,
249f2e20987Schristos dst_key_t *dstkey, bool generated,
250e2b1b9c0Schristos const dns_name_t *creator, isc_stdtime_t inception,
251e2b1b9c0Schristos isc_stdtime_t expire, isc_mem_t *mctx,
2529742fdb4Schristos dns_tsig_keyring_t *ring, dns_tsigkey_t **key) {
253e2b1b9c0Schristos dns_tsigkey_t *tkey;
254e2b1b9c0Schristos isc_result_t ret;
255e2b1b9c0Schristos unsigned int refs = 0;
256e2b1b9c0Schristos unsigned int dstalg = 0;
257e2b1b9c0Schristos
258e2b1b9c0Schristos REQUIRE(key == NULL || *key == NULL);
259e2b1b9c0Schristos REQUIRE(name != NULL);
260e2b1b9c0Schristos REQUIRE(algorithm != NULL);
261e2b1b9c0Schristos REQUIRE(mctx != NULL);
262e2b1b9c0Schristos REQUIRE(key != NULL || ring != NULL);
263e2b1b9c0Schristos
2649742fdb4Schristos tkey = isc_mem_get(mctx, sizeof(dns_tsigkey_t));
265e2b1b9c0Schristos
266e2b1b9c0Schristos dns_name_init(&tkey->name, NULL);
2679742fdb4Schristos dns_name_dup(name, mctx, &tkey->name);
268e2b1b9c0Schristos (void)dns_name_downcase(&tkey->name, &tkey->name, NULL);
269e2b1b9c0Schristos
270e2b1b9c0Schristos /* Check against known algorithm names */
271e2b1b9c0Schristos dstalg = dns__tsig_algfromname(algorithm);
272e2b1b9c0Schristos if (dstalg != 0) {
273e2b1b9c0Schristos /*
274e2b1b9c0Schristos * 'algorithm' must be set to a static pointer
275e2b1b9c0Schristos * so that dns__tsig_algallocated() can compare them.
276e2b1b9c0Schristos */
277e2b1b9c0Schristos tkey->algorithm = dns__tsig_algnamefromname(algorithm);
278e2b1b9c0Schristos if (dstkey != NULL && dst_key_alg(dstkey) != dstalg) {
279e2b1b9c0Schristos ret = DNS_R_BADALG;
280e2b1b9c0Schristos goto cleanup_name;
281e2b1b9c0Schristos }
282e2b1b9c0Schristos } else {
283e2b1b9c0Schristos dns_name_t *tmpname;
284e2b1b9c0Schristos if (dstkey != NULL) {
285e2b1b9c0Schristos ret = DNS_R_BADALG;
286e2b1b9c0Schristos goto cleanup_name;
287e2b1b9c0Schristos }
288e2b1b9c0Schristos tmpname = isc_mem_get(mctx, sizeof(dns_name_t));
289e2b1b9c0Schristos dns_name_init(tmpname, NULL);
2909742fdb4Schristos dns_name_dup(algorithm, mctx, tmpname);
291e2b1b9c0Schristos (void)dns_name_downcase(tmpname, tmpname, NULL);
292e2b1b9c0Schristos tkey->algorithm = tmpname;
293e2b1b9c0Schristos }
294e2b1b9c0Schristos
295e2b1b9c0Schristos if (creator != NULL) {
296e2b1b9c0Schristos tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t));
297e2b1b9c0Schristos dns_name_init(tkey->creator, NULL);
2989742fdb4Schristos dns_name_dup(creator, mctx, tkey->creator);
2999742fdb4Schristos } else {
300e2b1b9c0Schristos tkey->creator = NULL;
3019742fdb4Schristos }
302e2b1b9c0Schristos
303e2b1b9c0Schristos tkey->key = NULL;
3049742fdb4Schristos if (dstkey != NULL) {
305e2b1b9c0Schristos dst_key_attach(dstkey, &tkey->key);
3069742fdb4Schristos }
307e2b1b9c0Schristos tkey->ring = ring;
308e2b1b9c0Schristos
3099742fdb4Schristos if (key != NULL) {
310e2b1b9c0Schristos refs = 1;
3119742fdb4Schristos }
3129742fdb4Schristos if (ring != NULL) {
313e2b1b9c0Schristos refs++;
3149742fdb4Schristos }
315f2e20987Schristos
316f2e20987Schristos isc_refcount_init(&tkey->refs, refs);
317e2b1b9c0Schristos
318e2b1b9c0Schristos tkey->generated = generated;
319e2b1b9c0Schristos tkey->inception = inception;
320e2b1b9c0Schristos tkey->expire = expire;
321e2b1b9c0Schristos tkey->mctx = NULL;
322e2b1b9c0Schristos isc_mem_attach(mctx, &tkey->mctx);
323e2b1b9c0Schristos ISC_LINK_INIT(tkey, link);
324e2b1b9c0Schristos
325e2b1b9c0Schristos tkey->magic = TSIG_MAGIC;
326e2b1b9c0Schristos
327e2b1b9c0Schristos if (ring != NULL) {
328e2b1b9c0Schristos ret = keyring_add(ring, name, tkey);
3299742fdb4Schristos if (ret != ISC_R_SUCCESS) {
330e2b1b9c0Schristos goto cleanup_refs;
331e2b1b9c0Schristos }
3329742fdb4Schristos }
333e2b1b9c0Schristos
334e2b1b9c0Schristos /*
335e2b1b9c0Schristos * Ignore this if it's a GSS key, since the key size is meaningless.
336e2b1b9c0Schristos */
337e2b1b9c0Schristos if (dstkey != NULL && dst_key_size(dstkey) < 64 &&
338*4ac1c27eSchristos dstalg != DST_ALG_GSSAPI)
339*4ac1c27eSchristos {
340e2b1b9c0Schristos char namestr[DNS_NAME_FORMATSIZE];
341e2b1b9c0Schristos dns_name_format(name, namestr, sizeof(namestr));
342e2b1b9c0Schristos isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
343e2b1b9c0Schristos DNS_LOGMODULE_TSIG, ISC_LOG_INFO,
344e2b1b9c0Schristos "the key '%s' is too short to be secure",
345e2b1b9c0Schristos namestr);
346e2b1b9c0Schristos }
347e2b1b9c0Schristos
3489742fdb4Schristos if (key != NULL) {
349e2b1b9c0Schristos *key = tkey;
3509742fdb4Schristos }
351e2b1b9c0Schristos
352e2b1b9c0Schristos return (ISC_R_SUCCESS);
353e2b1b9c0Schristos
354e2b1b9c0Schristos cleanup_refs:
355e2b1b9c0Schristos tkey->magic = 0;
356f2e20987Schristos while (refs-- > 0) {
35773584a28Schristos isc_refcount_decrement0(&tkey->refs);
358f2e20987Schristos }
359e2b1b9c0Schristos isc_refcount_destroy(&tkey->refs);
360f2e20987Schristos
3619742fdb4Schristos if (tkey->key != NULL) {
362e2b1b9c0Schristos dst_key_free(&tkey->key);
3639742fdb4Schristos }
364e2b1b9c0Schristos if (tkey->creator != NULL) {
365e2b1b9c0Schristos dns_name_free(tkey->creator, mctx);
366e2b1b9c0Schristos isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t));
367e2b1b9c0Schristos }
368e2b1b9c0Schristos if (dns__tsig_algallocated(tkey->algorithm)) {
369e2b1b9c0Schristos dns_name_t *tmpname;
370e2b1b9c0Schristos DE_CONST(tkey->algorithm, tmpname);
3719742fdb4Schristos if (dns_name_dynamic(tmpname)) {
372e2b1b9c0Schristos dns_name_free(tmpname, mctx);
3739742fdb4Schristos }
374e2b1b9c0Schristos isc_mem_put(mctx, tmpname, sizeof(dns_name_t));
375e2b1b9c0Schristos }
376e2b1b9c0Schristos cleanup_name:
377e2b1b9c0Schristos dns_name_free(&tkey->name, mctx);
378e2b1b9c0Schristos isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t));
379e2b1b9c0Schristos
380e2b1b9c0Schristos return (ret);
381e2b1b9c0Schristos }
382e2b1b9c0Schristos
383e2b1b9c0Schristos /*
384e2b1b9c0Schristos * Find a few nodes to destroy if possible.
385e2b1b9c0Schristos */
386e2b1b9c0Schristos static void
cleanup_ring(dns_tsig_keyring_t * ring)3879742fdb4Schristos cleanup_ring(dns_tsig_keyring_t *ring) {
388e2b1b9c0Schristos isc_result_t result;
389e2b1b9c0Schristos dns_rbtnodechain_t chain;
390e2b1b9c0Schristos dns_name_t foundname;
391e2b1b9c0Schristos dns_fixedname_t fixedorigin;
392e2b1b9c0Schristos dns_name_t *origin;
393e2b1b9c0Schristos isc_stdtime_t now;
394e2b1b9c0Schristos dns_rbtnode_t *node;
395e2b1b9c0Schristos dns_tsigkey_t *tkey;
396e2b1b9c0Schristos
397e2b1b9c0Schristos /*
398e2b1b9c0Schristos * Start up a new iterator each time.
399e2b1b9c0Schristos */
400e2b1b9c0Schristos isc_stdtime_get(&now);
401e2b1b9c0Schristos dns_name_init(&foundname, NULL);
402e2b1b9c0Schristos origin = dns_fixedname_initname(&fixedorigin);
403e2b1b9c0Schristos
404e2b1b9c0Schristos again:
4059742fdb4Schristos dns_rbtnodechain_init(&chain);
4069742fdb4Schristos result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin);
407e2b1b9c0Schristos if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
408e2b1b9c0Schristos dns_rbtnodechain_invalidate(&chain);
409e2b1b9c0Schristos return;
410e2b1b9c0Schristos }
411e2b1b9c0Schristos
412e2b1b9c0Schristos for (;;) {
413e2b1b9c0Schristos node = NULL;
414e2b1b9c0Schristos dns_rbtnodechain_current(&chain, &foundname, origin, &node);
415e2b1b9c0Schristos tkey = node->data;
416e2b1b9c0Schristos if (tkey != NULL) {
4179742fdb4Schristos if (tkey->generated &&
4189742fdb4Schristos isc_refcount_current(&tkey->refs) == 1 &&
4199742fdb4Schristos tkey->inception != tkey->expire &&
4209742fdb4Schristos tkey->expire < now)
4219742fdb4Schristos {
422e2b1b9c0Schristos tsig_log(tkey, 2, "tsig expire: deleting");
423e2b1b9c0Schristos /* delete the key */
424e2b1b9c0Schristos dns_rbtnodechain_invalidate(&chain);
425e2b1b9c0Schristos remove_fromring(tkey);
426e2b1b9c0Schristos goto again;
427e2b1b9c0Schristos }
428e2b1b9c0Schristos }
4299742fdb4Schristos result = dns_rbtnodechain_next(&chain, &foundname, origin);
430e2b1b9c0Schristos if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
431e2b1b9c0Schristos dns_rbtnodechain_invalidate(&chain);
432e2b1b9c0Schristos return;
433e2b1b9c0Schristos }
434e2b1b9c0Schristos }
435e2b1b9c0Schristos }
436e2b1b9c0Schristos
437e2b1b9c0Schristos static void
destroyring(dns_tsig_keyring_t * ring)438e2b1b9c0Schristos destroyring(dns_tsig_keyring_t *ring) {
4399742fdb4Schristos isc_refcount_destroy(&ring->references);
440e2b1b9c0Schristos dns_rbt_destroy(&ring->keys);
441e2b1b9c0Schristos isc_rwlock_destroy(&ring->lock);
442e2b1b9c0Schristos isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t));
443e2b1b9c0Schristos }
444e2b1b9c0Schristos
445e2b1b9c0Schristos /*
446e2b1b9c0Schristos * Look up the DST_ALG_ constant for a given name.
447e2b1b9c0Schristos */
448e2b1b9c0Schristos unsigned int
dns__tsig_algfromname(const dns_name_t * algorithm)449e2b1b9c0Schristos dns__tsig_algfromname(const dns_name_t *algorithm) {
450e2b1b9c0Schristos int i;
451e2b1b9c0Schristos int n = sizeof(known_algs) / sizeof(*known_algs);
452e2b1b9c0Schristos for (i = 0; i < n; ++i) {
453e2b1b9c0Schristos const dns_name_t *name = known_algs[i].name;
454e2b1b9c0Schristos if (algorithm == name || dns_name_equal(algorithm, name)) {
455e2b1b9c0Schristos return (known_algs[i].dstalg);
456e2b1b9c0Schristos }
457e2b1b9c0Schristos }
458e2b1b9c0Schristos return (0);
459e2b1b9c0Schristos }
460e2b1b9c0Schristos
461e2b1b9c0Schristos /*
462e2b1b9c0Schristos * Convert an algorithm name into a pointer to the
463e2b1b9c0Schristos * corresponding pre-defined dns_name_t structure.
464e2b1b9c0Schristos */
465e2b1b9c0Schristos const dns_name_t *
dns__tsig_algnamefromname(const dns_name_t * algorithm)466e2b1b9c0Schristos dns__tsig_algnamefromname(const dns_name_t *algorithm) {
467e2b1b9c0Schristos int i;
468e2b1b9c0Schristos int n = sizeof(known_algs) / sizeof(*known_algs);
469e2b1b9c0Schristos for (i = 0; i < n; ++i) {
470e2b1b9c0Schristos const dns_name_t *name = known_algs[i].name;
471e2b1b9c0Schristos if (algorithm == name || dns_name_equal(algorithm, name)) {
472e2b1b9c0Schristos return (name);
473e2b1b9c0Schristos }
474e2b1b9c0Schristos }
475e2b1b9c0Schristos return (NULL);
476e2b1b9c0Schristos }
477e2b1b9c0Schristos
478e2b1b9c0Schristos /*
479e2b1b9c0Schristos * Test whether the passed algorithm is NOT a pointer to one of the
480e2b1b9c0Schristos * pre-defined known algorithms (and therefore one that has been
481e2b1b9c0Schristos * dynamically allocated).
482e2b1b9c0Schristos *
483e2b1b9c0Schristos * This will return an incorrect result if passed a dynamically allocated
484e2b1b9c0Schristos * dns_name_t that happens to match one of the pre-defined names.
485e2b1b9c0Schristos */
486f2e20987Schristos bool
dns__tsig_algallocated(const dns_name_t * algorithm)487e2b1b9c0Schristos dns__tsig_algallocated(const dns_name_t *algorithm) {
488e2b1b9c0Schristos int i;
489e2b1b9c0Schristos int n = sizeof(known_algs) / sizeof(*known_algs);
490e2b1b9c0Schristos for (i = 0; i < n; ++i) {
491e2b1b9c0Schristos const dns_name_t *name = known_algs[i].name;
492e2b1b9c0Schristos if (algorithm == name) {
493f2e20987Schristos return (false);
494e2b1b9c0Schristos }
495e2b1b9c0Schristos }
496f2e20987Schristos return (true);
497e2b1b9c0Schristos }
498e2b1b9c0Schristos
499e2b1b9c0Schristos static isc_result_t
restore_key(dns_tsig_keyring_t * ring,isc_stdtime_t now,FILE * fp)500e2b1b9c0Schristos restore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) {
501e2b1b9c0Schristos dst_key_t *dstkey = NULL;
502e2b1b9c0Schristos char namestr[1024];
503e2b1b9c0Schristos char creatorstr[1024];
504e2b1b9c0Schristos char algorithmstr[1024];
505e2b1b9c0Schristos char keystr[4096];
506e2b1b9c0Schristos unsigned int inception, expire;
507e2b1b9c0Schristos int n;
508e2b1b9c0Schristos isc_buffer_t b;
509e2b1b9c0Schristos dns_name_t *name, *creator, *algorithm;
510e2b1b9c0Schristos dns_fixedname_t fname, fcreator, falgorithm;
511e2b1b9c0Schristos isc_result_t result;
512e2b1b9c0Schristos unsigned int dstalg;
513e2b1b9c0Schristos
514e2b1b9c0Schristos n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr,
515e2b1b9c0Schristos creatorstr, &inception, &expire, algorithmstr, keystr);
5169742fdb4Schristos if (n == EOF) {
517e2b1b9c0Schristos return (ISC_R_NOMORE);
5189742fdb4Schristos }
5199742fdb4Schristos if (n != 6) {
520e2b1b9c0Schristos return (ISC_R_FAILURE);
5219742fdb4Schristos }
522e2b1b9c0Schristos
5239742fdb4Schristos if (isc_serial_lt(expire, now)) {
524e2b1b9c0Schristos return (DNS_R_EXPIRED);
5259742fdb4Schristos }
526e2b1b9c0Schristos
527e2b1b9c0Schristos name = dns_fixedname_initname(&fname);
528e2b1b9c0Schristos isc_buffer_init(&b, namestr, strlen(namestr));
529e2b1b9c0Schristos isc_buffer_add(&b, strlen(namestr));
530e2b1b9c0Schristos result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
5319742fdb4Schristos if (result != ISC_R_SUCCESS) {
532e2b1b9c0Schristos return (result);
5339742fdb4Schristos }
534e2b1b9c0Schristos
535e2b1b9c0Schristos creator = dns_fixedname_initname(&fcreator);
536e2b1b9c0Schristos isc_buffer_init(&b, creatorstr, strlen(creatorstr));
537e2b1b9c0Schristos isc_buffer_add(&b, strlen(creatorstr));
538e2b1b9c0Schristos result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL);
5399742fdb4Schristos if (result != ISC_R_SUCCESS) {
540e2b1b9c0Schristos return (result);
5419742fdb4Schristos }
542e2b1b9c0Schristos
543e2b1b9c0Schristos algorithm = dns_fixedname_initname(&falgorithm);
544e2b1b9c0Schristos isc_buffer_init(&b, algorithmstr, strlen(algorithmstr));
545e2b1b9c0Schristos isc_buffer_add(&b, strlen(algorithmstr));
546e2b1b9c0Schristos result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL);
5479742fdb4Schristos if (result != ISC_R_SUCCESS) {
548e2b1b9c0Schristos return (result);
5499742fdb4Schristos }
550e2b1b9c0Schristos
551e2b1b9c0Schristos dstalg = dns__tsig_algfromname(algorithm);
5529742fdb4Schristos if (dstalg == 0) {
553e2b1b9c0Schristos return (DNS_R_BADALG);
5549742fdb4Schristos }
555e2b1b9c0Schristos
556e2b1b9c0Schristos result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY,
557e2b1b9c0Schristos DNS_KEYPROTO_DNSSEC, dns_rdataclass_in,
558e2b1b9c0Schristos ring->mctx, keystr, &dstkey);
5599742fdb4Schristos if (result != ISC_R_SUCCESS) {
560e2b1b9c0Schristos return (result);
5619742fdb4Schristos }
562e2b1b9c0Schristos
5639742fdb4Schristos result = dns_tsigkey_createfromkey(name, algorithm, dstkey, true,
5649742fdb4Schristos creator, inception, expire,
5659742fdb4Schristos ring->mctx, ring, NULL);
5669742fdb4Schristos if (dstkey != NULL) {
567e2b1b9c0Schristos dst_key_free(&dstkey);
5689742fdb4Schristos }
569e2b1b9c0Schristos return (result);
570e2b1b9c0Schristos }
571e2b1b9c0Schristos
572e2b1b9c0Schristos static void
dump_key(dns_tsigkey_t * tkey,FILE * fp)573e2b1b9c0Schristos dump_key(dns_tsigkey_t *tkey, FILE *fp) {
574e2b1b9c0Schristos char *buffer = NULL;
575e2b1b9c0Schristos int length = 0;
576e2b1b9c0Schristos char namestr[DNS_NAME_FORMATSIZE];
577e2b1b9c0Schristos char creatorstr[DNS_NAME_FORMATSIZE];
578e2b1b9c0Schristos char algorithmstr[DNS_NAME_FORMATSIZE];
579e2b1b9c0Schristos isc_result_t result;
580e2b1b9c0Schristos
581e2b1b9c0Schristos REQUIRE(tkey != NULL);
582e2b1b9c0Schristos REQUIRE(fp != NULL);
583e2b1b9c0Schristos
584e2b1b9c0Schristos dns_name_format(&tkey->name, namestr, sizeof(namestr));
585e2b1b9c0Schristos dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr));
586e2b1b9c0Schristos dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr));
587e2b1b9c0Schristos result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length);
5889742fdb4Schristos if (result == ISC_R_SUCCESS) {
589e2b1b9c0Schristos fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr,
5909742fdb4Schristos tkey->inception, tkey->expire, algorithmstr, length,
5919742fdb4Schristos buffer);
5929742fdb4Schristos }
5939742fdb4Schristos if (buffer != NULL) {
594e2b1b9c0Schristos isc_mem_put(tkey->mctx, buffer, length);
595e2b1b9c0Schristos }
5969742fdb4Schristos }
597e2b1b9c0Schristos
598e2b1b9c0Schristos isc_result_t
dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t ** ringp,FILE * fp)599e2b1b9c0Schristos dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) {
600e2b1b9c0Schristos isc_result_t result;
601e2b1b9c0Schristos dns_rbtnodechain_t chain;
602e2b1b9c0Schristos dns_name_t foundname;
603e2b1b9c0Schristos dns_fixedname_t fixedorigin;
604e2b1b9c0Schristos dns_name_t *origin;
605e2b1b9c0Schristos isc_stdtime_t now;
606e2b1b9c0Schristos dns_rbtnode_t *node;
607e2b1b9c0Schristos dns_tsigkey_t *tkey;
608e2b1b9c0Schristos dns_tsig_keyring_t *ring;
609e2b1b9c0Schristos
610e2b1b9c0Schristos REQUIRE(ringp != NULL && *ringp != NULL);
611e2b1b9c0Schristos
612e2b1b9c0Schristos ring = *ringp;
613e2b1b9c0Schristos *ringp = NULL;
614e2b1b9c0Schristos
6159742fdb4Schristos if (isc_refcount_decrement(&ring->references) > 1) {
616e2b1b9c0Schristos return (DNS_R_CONTINUE);
6179742fdb4Schristos }
618e2b1b9c0Schristos
619e2b1b9c0Schristos isc_stdtime_get(&now);
620e2b1b9c0Schristos dns_name_init(&foundname, NULL);
621e2b1b9c0Schristos origin = dns_fixedname_initname(&fixedorigin);
6229742fdb4Schristos dns_rbtnodechain_init(&chain);
6239742fdb4Schristos result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, origin);
624e2b1b9c0Schristos if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
625e2b1b9c0Schristos dns_rbtnodechain_invalidate(&chain);
626e2b1b9c0Schristos goto destroy;
627e2b1b9c0Schristos }
628e2b1b9c0Schristos
629e2b1b9c0Schristos for (;;) {
630e2b1b9c0Schristos node = NULL;
631e2b1b9c0Schristos dns_rbtnodechain_current(&chain, &foundname, origin, &node);
632e2b1b9c0Schristos tkey = node->data;
6339742fdb4Schristos if (tkey != NULL && tkey->generated && tkey->expire >= now) {
634e2b1b9c0Schristos dump_key(tkey, fp);
6359742fdb4Schristos }
6369742fdb4Schristos result = dns_rbtnodechain_next(&chain, &foundname, origin);
637e2b1b9c0Schristos if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
638e2b1b9c0Schristos dns_rbtnodechain_invalidate(&chain);
6399742fdb4Schristos if (result == ISC_R_NOMORE) {
640e2b1b9c0Schristos result = ISC_R_SUCCESS;
6419742fdb4Schristos }
642e2b1b9c0Schristos goto destroy;
643e2b1b9c0Schristos }
644e2b1b9c0Schristos }
645e2b1b9c0Schristos
646e2b1b9c0Schristos destroy:
647e2b1b9c0Schristos destroyring(ring);
648e2b1b9c0Schristos return (result);
649e2b1b9c0Schristos }
650e2b1b9c0Schristos
6518b4c8a26Schristos const dns_name_t *
dns_tsigkey_identity(const dns_tsigkey_t * tsigkey)6528b4c8a26Schristos dns_tsigkey_identity(const dns_tsigkey_t *tsigkey) {
6538b4c8a26Schristos REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
6548b4c8a26Schristos
6558b4c8a26Schristos if (tsigkey == NULL) {
6568b4c8a26Schristos return (NULL);
6578b4c8a26Schristos }
6588b4c8a26Schristos if (tsigkey->generated) {
6598b4c8a26Schristos return (tsigkey->creator);
6608b4c8a26Schristos } else {
6618b4c8a26Schristos return (&tsigkey->name);
6628b4c8a26Schristos }
6638b4c8a26Schristos }
6648b4c8a26Schristos
665e2b1b9c0Schristos isc_result_t
dns_tsigkey_create(const dns_name_t * name,const dns_name_t * algorithm,unsigned char * secret,int length,bool generated,const 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)666e2b1b9c0Schristos dns_tsigkey_create(const dns_name_t *name, const dns_name_t *algorithm,
667f2e20987Schristos unsigned char *secret, int length, bool generated,
668e2b1b9c0Schristos const dns_name_t *creator, isc_stdtime_t inception,
669e2b1b9c0Schristos isc_stdtime_t expire, isc_mem_t *mctx,
6709742fdb4Schristos dns_tsig_keyring_t *ring, dns_tsigkey_t **key) {
671e2b1b9c0Schristos dst_key_t *dstkey = NULL;
672e2b1b9c0Schristos isc_result_t result;
673e2b1b9c0Schristos unsigned int dstalg = 0;
674e2b1b9c0Schristos
675e2b1b9c0Schristos REQUIRE(length >= 0);
6769742fdb4Schristos if (length > 0) {
677e2b1b9c0Schristos REQUIRE(secret != NULL);
6789742fdb4Schristos }
679e2b1b9c0Schristos
680e2b1b9c0Schristos dstalg = dns__tsig_algfromname(algorithm);
681e2b1b9c0Schristos if (dns__tsig_algvalid(dstalg)) {
682e2b1b9c0Schristos if (secret != NULL) {
683e2b1b9c0Schristos isc_buffer_t b;
684e2b1b9c0Schristos
685e2b1b9c0Schristos isc_buffer_init(&b, secret, length);
686e2b1b9c0Schristos isc_buffer_add(&b, length);
6879742fdb4Schristos result = dst_key_frombuffer(
6889742fdb4Schristos name, dstalg, DNS_KEYOWNER_ENTITY,
6899742fdb4Schristos DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, &b,
6909742fdb4Schristos mctx, &dstkey);
6919742fdb4Schristos if (result != ISC_R_SUCCESS) {
692e2b1b9c0Schristos return (result);
693e2b1b9c0Schristos }
6949742fdb4Schristos }
695e2b1b9c0Schristos } else if (length > 0) {
696e2b1b9c0Schristos return (DNS_R_BADALG);
697e2b1b9c0Schristos }
698e2b1b9c0Schristos
6999742fdb4Schristos result = dns_tsigkey_createfromkey(name, algorithm, dstkey, generated,
7009742fdb4Schristos creator, inception, expire, mctx,
7019742fdb4Schristos ring, key);
7029742fdb4Schristos if (dstkey != NULL) {
703e2b1b9c0Schristos dst_key_free(&dstkey);
7049742fdb4Schristos }
705e2b1b9c0Schristos return (result);
706e2b1b9c0Schristos }
707e2b1b9c0Schristos
708e2b1b9c0Schristos void
dns_tsigkey_attach(dns_tsigkey_t * source,dns_tsigkey_t ** targetp)709e2b1b9c0Schristos dns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) {
710e2b1b9c0Schristos REQUIRE(VALID_TSIG_KEY(source));
711e2b1b9c0Schristos REQUIRE(targetp != NULL && *targetp == NULL);
712e2b1b9c0Schristos
713f2e20987Schristos isc_refcount_increment(&source->refs);
714e2b1b9c0Schristos *targetp = source;
715e2b1b9c0Schristos }
716e2b1b9c0Schristos
717e2b1b9c0Schristos static void
tsigkey_free(dns_tsigkey_t * key)718e2b1b9c0Schristos tsigkey_free(dns_tsigkey_t *key) {
719e2b1b9c0Schristos REQUIRE(VALID_TSIG_KEY(key));
720e2b1b9c0Schristos
721e2b1b9c0Schristos key->magic = 0;
722e2b1b9c0Schristos dns_name_free(&key->name, key->mctx);
723e2b1b9c0Schristos if (dns__tsig_algallocated(key->algorithm)) {
724e2b1b9c0Schristos dns_name_t *name;
725e2b1b9c0Schristos DE_CONST(key->algorithm, name);
726e2b1b9c0Schristos dns_name_free(name, key->mctx);
727e2b1b9c0Schristos isc_mem_put(key->mctx, name, sizeof(dns_name_t));
728e2b1b9c0Schristos }
7299742fdb4Schristos if (key->key != NULL) {
730e2b1b9c0Schristos dst_key_free(&key->key);
7319742fdb4Schristos }
732e2b1b9c0Schristos if (key->creator != NULL) {
733e2b1b9c0Schristos dns_name_free(key->creator, key->mctx);
734e2b1b9c0Schristos isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t));
735e2b1b9c0Schristos }
736e2b1b9c0Schristos isc_mem_putanddetach(&key->mctx, key, sizeof(dns_tsigkey_t));
737e2b1b9c0Schristos }
738e2b1b9c0Schristos
739e2b1b9c0Schristos void
dns_tsigkey_detach(dns_tsigkey_t ** keyp)740e2b1b9c0Schristos dns_tsigkey_detach(dns_tsigkey_t **keyp) {
741f2e20987Schristos REQUIRE(keyp != NULL && VALID_TSIG_KEY(*keyp));
742f2e20987Schristos dns_tsigkey_t *key = *keyp;
743e2b1b9c0Schristos *keyp = NULL;
744f2e20987Schristos
745f2e20987Schristos if (isc_refcount_decrement(&key->refs) == 1) {
746f2e20987Schristos isc_refcount_destroy(&key->refs);
747f2e20987Schristos tsigkey_free(key);
748f2e20987Schristos }
749e2b1b9c0Schristos }
750e2b1b9c0Schristos
751e2b1b9c0Schristos void
dns_tsigkey_setdeleted(dns_tsigkey_t * key)752e2b1b9c0Schristos dns_tsigkey_setdeleted(dns_tsigkey_t *key) {
753e2b1b9c0Schristos REQUIRE(VALID_TSIG_KEY(key));
754e2b1b9c0Schristos REQUIRE(key->ring != NULL);
755e2b1b9c0Schristos
756e2b1b9c0Schristos RWLOCK(&key->ring->lock, isc_rwlocktype_write);
757e2b1b9c0Schristos remove_fromring(key);
758e2b1b9c0Schristos RWUNLOCK(&key->ring->lock, isc_rwlocktype_write);
759e2b1b9c0Schristos }
760e2b1b9c0Schristos
761e2b1b9c0Schristos isc_result_t
dns_tsig_sign(dns_message_t * msg)762e2b1b9c0Schristos dns_tsig_sign(dns_message_t *msg) {
763cc2e102bSchristos dns_tsigkey_t *key = NULL;
764e2b1b9c0Schristos dns_rdata_any_tsig_t tsig, querytsig;
765e2b1b9c0Schristos unsigned char data[128];
766e2b1b9c0Schristos isc_buffer_t databuf, sigbuf;
767cc2e102bSchristos isc_buffer_t *dynbuf = NULL;
768fadf0758Schristos dns_name_t *owner = NULL;
769e2b1b9c0Schristos dns_rdata_t *rdata = NULL;
770cc2e102bSchristos dns_rdatalist_t *datalist = NULL;
771cc2e102bSchristos dns_rdataset_t *dataset = NULL;
772e2b1b9c0Schristos isc_region_t r;
773e2b1b9c0Schristos isc_stdtime_t now;
774e2b1b9c0Schristos isc_mem_t *mctx;
775e2b1b9c0Schristos dst_context_t *ctx = NULL;
776e2b1b9c0Schristos isc_result_t ret;
777e2b1b9c0Schristos unsigned char badtimedata[BADTIMELEN];
778e2b1b9c0Schristos unsigned int sigsize = 0;
779f2e20987Schristos bool response;
780e2b1b9c0Schristos
781e2b1b9c0Schristos REQUIRE(msg != NULL);
782e2b1b9c0Schristos key = dns_message_gettsigkey(msg);
783e2b1b9c0Schristos REQUIRE(VALID_TSIG_KEY(key));
784e2b1b9c0Schristos
785e2b1b9c0Schristos /*
786cc2e102bSchristos * If this is a response, there should be a TSIG in the query with the
787cc2e102bSchristos * the exception if this is a TKEY request (see RFC 3645, Section 2.2).
788e2b1b9c0Schristos */
789e2b1b9c0Schristos response = is_response(msg);
790cc2e102bSchristos if (response && msg->querytsig == NULL) {
791cc2e102bSchristos if (msg->tkey != 1) {
792e2b1b9c0Schristos return (DNS_R_EXPECTEDTSIG);
793cc2e102bSchristos }
794cc2e102bSchristos }
795e2b1b9c0Schristos
796e2b1b9c0Schristos mctx = msg->mctx;
797e2b1b9c0Schristos
798e2b1b9c0Schristos tsig.mctx = mctx;
799e2b1b9c0Schristos tsig.common.rdclass = dns_rdataclass_any;
800e2b1b9c0Schristos tsig.common.rdtype = dns_rdatatype_tsig;
801e2b1b9c0Schristos ISC_LINK_INIT(&tsig.common, link);
802e2b1b9c0Schristos dns_name_init(&tsig.algorithm, NULL);
803e2b1b9c0Schristos dns_name_clone(key->algorithm, &tsig.algorithm);
804e2b1b9c0Schristos
805e2b1b9c0Schristos isc_stdtime_get(&now);
806e2b1b9c0Schristos tsig.timesigned = now + msg->timeadjust;
807e2b1b9c0Schristos tsig.fudge = DNS_TSIG_FUDGE;
808e2b1b9c0Schristos
809e2b1b9c0Schristos tsig.originalid = msg->id;
810e2b1b9c0Schristos
811e2b1b9c0Schristos isc_buffer_init(&databuf, data, sizeof(data));
812e2b1b9c0Schristos
8139742fdb4Schristos if (response) {
814e2b1b9c0Schristos tsig.error = msg->querytsigstatus;
8159742fdb4Schristos } else {
816e2b1b9c0Schristos tsig.error = dns_rcode_noerror;
8179742fdb4Schristos }
818e2b1b9c0Schristos
819e2b1b9c0Schristos if (tsig.error != dns_tsigerror_badtime) {
820e2b1b9c0Schristos tsig.otherlen = 0;
821e2b1b9c0Schristos tsig.other = NULL;
822e2b1b9c0Schristos } else {
823e2b1b9c0Schristos isc_buffer_t otherbuf;
824e2b1b9c0Schristos
825e2b1b9c0Schristos tsig.otherlen = BADTIMELEN;
826e2b1b9c0Schristos tsig.other = badtimedata;
827e2b1b9c0Schristos isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen);
828e2b1b9c0Schristos isc_buffer_putuint48(&otherbuf, tsig.timesigned);
829e2b1b9c0Schristos }
830e2b1b9c0Schristos
8319742fdb4Schristos if ((key->key != NULL) && (tsig.error != dns_tsigerror_badsig) &&
832e2b1b9c0Schristos (tsig.error != dns_tsigerror_badkey))
833e2b1b9c0Schristos {
834e2b1b9c0Schristos unsigned char header[DNS_MESSAGE_HEADERLEN];
835e2b1b9c0Schristos isc_buffer_t headerbuf;
836f2e20987Schristos uint16_t digestbits;
837cc2e102bSchristos bool querytsig_ok = false;
838e2b1b9c0Schristos
839e2b1b9c0Schristos /*
840e2b1b9c0Schristos * If it is a response, we assume that the request MAC
841e2b1b9c0Schristos * has validated at this point. This is why we include a
842e2b1b9c0Schristos * MAC length > 0 in the reply.
843e2b1b9c0Schristos */
8449742fdb4Schristos ret = dst_context_create(key->key, mctx, DNS_LOGCATEGORY_DNSSEC,
845f2e20987Schristos true, 0, &ctx);
8469742fdb4Schristos if (ret != ISC_R_SUCCESS) {
847e2b1b9c0Schristos return (ret);
8489742fdb4Schristos }
849e2b1b9c0Schristos
850e2b1b9c0Schristos /*
851cc2e102bSchristos * If this is a response, and if there was a TSIG in
852cc2e102bSchristos * the query, digest the request's MAC.
853cc2e102bSchristos *
854cc2e102bSchristos * (Note: querytsig should be non-NULL for all
855cc2e102bSchristos * responses except TKEY responses. Those may be signed
856cc2e102bSchristos * with the newly-negotiated TSIG key even if the query
857cc2e102bSchristos * wasn't signed.)
858e2b1b9c0Schristos */
859cc2e102bSchristos if (response && msg->querytsig != NULL) {
860e2b1b9c0Schristos dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
861e2b1b9c0Schristos
862e2b1b9c0Schristos INSIST(msg->verified_sig);
863e2b1b9c0Schristos
864e2b1b9c0Schristos ret = dns_rdataset_first(msg->querytsig);
8659742fdb4Schristos if (ret != ISC_R_SUCCESS) {
866e2b1b9c0Schristos goto cleanup_context;
8679742fdb4Schristos }
868e2b1b9c0Schristos dns_rdataset_current(msg->querytsig, &querytsigrdata);
869e2b1b9c0Schristos ret = dns_rdata_tostruct(&querytsigrdata, &querytsig,
870e2b1b9c0Schristos NULL);
8719742fdb4Schristos if (ret != ISC_R_SUCCESS) {
872e2b1b9c0Schristos goto cleanup_context;
8739742fdb4Schristos }
874e2b1b9c0Schristos isc_buffer_putuint16(&databuf, querytsig.siglen);
875e2b1b9c0Schristos if (isc_buffer_availablelength(&databuf) <
876*4ac1c27eSchristos querytsig.siglen)
877*4ac1c27eSchristos {
878e2b1b9c0Schristos ret = ISC_R_NOSPACE;
879e2b1b9c0Schristos goto cleanup_context;
880e2b1b9c0Schristos }
881e2b1b9c0Schristos isc_buffer_putmem(&databuf, querytsig.signature,
882e2b1b9c0Schristos querytsig.siglen);
883e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
884e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
8859742fdb4Schristos if (ret != ISC_R_SUCCESS) {
886e2b1b9c0Schristos goto cleanup_context;
8879742fdb4Schristos }
888cc2e102bSchristos querytsig_ok = true;
889e2b1b9c0Schristos }
890e2b1b9c0Schristos
891e2b1b9c0Schristos /*
892e2b1b9c0Schristos * Digest the header.
893e2b1b9c0Schristos */
894e2b1b9c0Schristos isc_buffer_init(&headerbuf, header, sizeof(header));
895e2b1b9c0Schristos dns_message_renderheader(msg, &headerbuf);
896e2b1b9c0Schristos isc_buffer_usedregion(&headerbuf, &r);
897e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
8989742fdb4Schristos if (ret != ISC_R_SUCCESS) {
899e2b1b9c0Schristos goto cleanup_context;
9009742fdb4Schristos }
901e2b1b9c0Schristos
902e2b1b9c0Schristos /*
903e2b1b9c0Schristos * Digest the remainder of the message.
904e2b1b9c0Schristos */
905e2b1b9c0Schristos isc_buffer_usedregion(msg->buffer, &r);
906e2b1b9c0Schristos isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
907e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
9089742fdb4Schristos if (ret != ISC_R_SUCCESS) {
909e2b1b9c0Schristos goto cleanup_context;
9109742fdb4Schristos }
911e2b1b9c0Schristos
912e2b1b9c0Schristos if (msg->tcp_continuation == 0) {
913e2b1b9c0Schristos /*
914e2b1b9c0Schristos * Digest the name, class, ttl, alg.
915e2b1b9c0Schristos */
916e2b1b9c0Schristos dns_name_toregion(&key->name, &r);
917e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
9189742fdb4Schristos if (ret != ISC_R_SUCCESS) {
919e2b1b9c0Schristos goto cleanup_context;
9209742fdb4Schristos }
921e2b1b9c0Schristos
922e2b1b9c0Schristos isc_buffer_clear(&databuf);
923e2b1b9c0Schristos isc_buffer_putuint16(&databuf, dns_rdataclass_any);
924e2b1b9c0Schristos isc_buffer_putuint32(&databuf, 0); /* ttl */
925e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
926e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
9279742fdb4Schristos if (ret != ISC_R_SUCCESS) {
928e2b1b9c0Schristos goto cleanup_context;
9299742fdb4Schristos }
930e2b1b9c0Schristos
931e2b1b9c0Schristos dns_name_toregion(&tsig.algorithm, &r);
932e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
9339742fdb4Schristos if (ret != ISC_R_SUCCESS) {
934e2b1b9c0Schristos goto cleanup_context;
9359742fdb4Schristos }
936e2b1b9c0Schristos }
937e2b1b9c0Schristos /* Digest the timesigned and fudge */
938e2b1b9c0Schristos isc_buffer_clear(&databuf);
939cc2e102bSchristos if (tsig.error == dns_tsigerror_badtime && querytsig_ok) {
940e2b1b9c0Schristos tsig.timesigned = querytsig.timesigned;
941e2b1b9c0Schristos }
942e2b1b9c0Schristos isc_buffer_putuint48(&databuf, tsig.timesigned);
943e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.fudge);
944e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
945e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
9469742fdb4Schristos if (ret != ISC_R_SUCCESS) {
947e2b1b9c0Schristos goto cleanup_context;
9489742fdb4Schristos }
949e2b1b9c0Schristos
950e2b1b9c0Schristos if (msg->tcp_continuation == 0) {
951e2b1b9c0Schristos /*
952e2b1b9c0Schristos * Digest the error and other data length.
953e2b1b9c0Schristos */
954e2b1b9c0Schristos isc_buffer_clear(&databuf);
955e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.error);
956e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.otherlen);
957e2b1b9c0Schristos
958e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
959e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
9609742fdb4Schristos if (ret != ISC_R_SUCCESS) {
961e2b1b9c0Schristos goto cleanup_context;
9629742fdb4Schristos }
963e2b1b9c0Schristos
964e2b1b9c0Schristos /*
965e2b1b9c0Schristos * Digest other data.
966e2b1b9c0Schristos */
967e2b1b9c0Schristos if (tsig.otherlen > 0) {
968e2b1b9c0Schristos r.length = tsig.otherlen;
969e2b1b9c0Schristos r.base = tsig.other;
970e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
9719742fdb4Schristos if (ret != ISC_R_SUCCESS) {
972e2b1b9c0Schristos goto cleanup_context;
973e2b1b9c0Schristos }
974e2b1b9c0Schristos }
9759742fdb4Schristos }
976e2b1b9c0Schristos
977e2b1b9c0Schristos ret = dst_key_sigsize(key->key, &sigsize);
9789742fdb4Schristos if (ret != ISC_R_SUCCESS) {
979e2b1b9c0Schristos goto cleanup_context;
980e2b1b9c0Schristos }
9819742fdb4Schristos tsig.signature = isc_mem_get(mctx, sigsize);
982e2b1b9c0Schristos
983e2b1b9c0Schristos isc_buffer_init(&sigbuf, tsig.signature, sigsize);
984e2b1b9c0Schristos ret = dst_context_sign(ctx, &sigbuf);
9859742fdb4Schristos if (ret != ISC_R_SUCCESS) {
986e2b1b9c0Schristos goto cleanup_signature;
9879742fdb4Schristos }
988e2b1b9c0Schristos dst_context_destroy(&ctx);
989e2b1b9c0Schristos digestbits = dst_key_getbits(key->key);
990e2b1b9c0Schristos if (digestbits != 0) {
991cc2e102bSchristos unsigned int bytes = (digestbits + 7) / 8;
9929742fdb4Schristos if (querytsig_ok && bytes < querytsig.siglen) {
993e2b1b9c0Schristos bytes = querytsig.siglen;
9949742fdb4Schristos }
9959742fdb4Schristos if (bytes > isc_buffer_usedlength(&sigbuf)) {
996e2b1b9c0Schristos bytes = isc_buffer_usedlength(&sigbuf);
9979742fdb4Schristos }
998e2b1b9c0Schristos tsig.siglen = bytes;
9999742fdb4Schristos } else {
1000e2b1b9c0Schristos tsig.siglen = isc_buffer_usedlength(&sigbuf);
10019742fdb4Schristos }
1002e2b1b9c0Schristos } else {
1003e2b1b9c0Schristos tsig.siglen = 0;
1004e2b1b9c0Schristos tsig.signature = NULL;
1005e2b1b9c0Schristos }
1006e2b1b9c0Schristos
1007e2b1b9c0Schristos ret = dns_message_gettemprdata(msg, &rdata);
10089742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1009e2b1b9c0Schristos goto cleanup_signature;
10109742fdb4Schristos }
10119742fdb4Schristos isc_buffer_allocate(msg->mctx, &dynbuf, 512);
1012e2b1b9c0Schristos ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
1013e2b1b9c0Schristos dns_rdatatype_tsig, &tsig, dynbuf);
10149742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1015e2b1b9c0Schristos goto cleanup_dynbuf;
10169742fdb4Schristos }
1017e2b1b9c0Schristos
1018e2b1b9c0Schristos dns_message_takebuffer(msg, &dynbuf);
1019e2b1b9c0Schristos
1020e2b1b9c0Schristos if (tsig.signature != NULL) {
1021e2b1b9c0Schristos isc_mem_put(mctx, tsig.signature, sigsize);
1022e2b1b9c0Schristos tsig.signature = NULL;
1023e2b1b9c0Schristos }
1024e2b1b9c0Schristos
1025e2b1b9c0Schristos ret = dns_message_gettempname(msg, &owner);
10269742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1027e2b1b9c0Schristos goto cleanup_rdata;
10289742fdb4Schristos }
1029fadf0758Schristos dns_name_copynf(&key->name, owner);
1030e2b1b9c0Schristos
1031e2b1b9c0Schristos ret = dns_message_gettemprdatalist(msg, &datalist);
10329742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1033e2b1b9c0Schristos goto cleanup_owner;
10349742fdb4Schristos }
1035fadf0758Schristos
1036e2b1b9c0Schristos ret = dns_message_gettemprdataset(msg, &dataset);
10379742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1038e2b1b9c0Schristos goto cleanup_rdatalist;
10399742fdb4Schristos }
1040e2b1b9c0Schristos datalist->rdclass = dns_rdataclass_any;
1041e2b1b9c0Schristos datalist->type = dns_rdatatype_tsig;
1042e2b1b9c0Schristos ISC_LIST_APPEND(datalist->rdata, rdata, link);
10439742fdb4Schristos RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) ==
10449742fdb4Schristos ISC_R_SUCCESS);
1045e2b1b9c0Schristos msg->tsig = dataset;
1046e2b1b9c0Schristos msg->tsigname = owner;
1047e2b1b9c0Schristos
1048e2b1b9c0Schristos /* Windows does not like the tsig name being compressed. */
1049e2b1b9c0Schristos msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
1050e2b1b9c0Schristos
1051e2b1b9c0Schristos return (ISC_R_SUCCESS);
1052e2b1b9c0Schristos
1053e2b1b9c0Schristos cleanup_rdatalist:
1054e2b1b9c0Schristos dns_message_puttemprdatalist(msg, &datalist);
1055e2b1b9c0Schristos cleanup_owner:
1056e2b1b9c0Schristos dns_message_puttempname(msg, &owner);
1057e2b1b9c0Schristos goto cleanup_rdata;
1058e2b1b9c0Schristos cleanup_dynbuf:
1059e2b1b9c0Schristos isc_buffer_free(&dynbuf);
1060e2b1b9c0Schristos cleanup_rdata:
1061e2b1b9c0Schristos dns_message_puttemprdata(msg, &rdata);
1062e2b1b9c0Schristos cleanup_signature:
10639742fdb4Schristos if (tsig.signature != NULL) {
1064e2b1b9c0Schristos isc_mem_put(mctx, tsig.signature, sigsize);
10659742fdb4Schristos }
1066e2b1b9c0Schristos cleanup_context:
10679742fdb4Schristos if (ctx != NULL) {
1068e2b1b9c0Schristos dst_context_destroy(&ctx);
10699742fdb4Schristos }
1070e2b1b9c0Schristos return (ret);
1071e2b1b9c0Schristos }
1072e2b1b9c0Schristos
1073e2b1b9c0Schristos isc_result_t
dns_tsig_verify(isc_buffer_t * source,dns_message_t * msg,dns_tsig_keyring_t * ring1,dns_tsig_keyring_t * ring2)1074e2b1b9c0Schristos dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg,
10759742fdb4Schristos dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2) {
1076e2b1b9c0Schristos dns_rdata_any_tsig_t tsig, querytsig;
1077e2b1b9c0Schristos isc_region_t r, source_r, header_r, sig_r;
1078e2b1b9c0Schristos isc_buffer_t databuf;
1079e2b1b9c0Schristos unsigned char data[32];
1080e2b1b9c0Schristos dns_name_t *keyname;
1081e2b1b9c0Schristos dns_rdata_t rdata = DNS_RDATA_INIT;
1082e2b1b9c0Schristos isc_stdtime_t now;
1083e2b1b9c0Schristos isc_result_t ret;
1084e2b1b9c0Schristos dns_tsigkey_t *tsigkey;
1085e2b1b9c0Schristos dst_key_t *key = NULL;
1086e2b1b9c0Schristos unsigned char header[DNS_MESSAGE_HEADERLEN];
1087e2b1b9c0Schristos dst_context_t *ctx = NULL;
1088e2b1b9c0Schristos isc_mem_t *mctx;
1089f2e20987Schristos uint16_t addcount, id;
1090e2b1b9c0Schristos unsigned int siglen;
1091e2b1b9c0Schristos unsigned int alg;
1092f2e20987Schristos bool response;
1093e2b1b9c0Schristos
1094e2b1b9c0Schristos REQUIRE(source != NULL);
1095e2b1b9c0Schristos REQUIRE(DNS_MESSAGE_VALID(msg));
1096e2b1b9c0Schristos tsigkey = dns_message_gettsigkey(msg);
1097e2b1b9c0Schristos response = is_response(msg);
1098e2b1b9c0Schristos
1099e2b1b9c0Schristos REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey));
1100e2b1b9c0Schristos
1101e2b1b9c0Schristos msg->verify_attempted = 1;
1102e2b1b9c0Schristos msg->verified_sig = 0;
1103e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badsig;
1104e2b1b9c0Schristos
1105e2b1b9c0Schristos if (msg->tcp_continuation) {
11069742fdb4Schristos if (tsigkey == NULL || msg->querytsig == NULL) {
1107e2b1b9c0Schristos return (DNS_R_UNEXPECTEDTSIG);
11089742fdb4Schristos }
1109e2b1b9c0Schristos return (tsig_verify_tcp(source, msg));
1110e2b1b9c0Schristos }
1111e2b1b9c0Schristos
1112e2b1b9c0Schristos /*
1113e2b1b9c0Schristos * There should be a TSIG record...
1114e2b1b9c0Schristos */
11159742fdb4Schristos if (msg->tsig == NULL) {
1116e2b1b9c0Schristos return (DNS_R_EXPECTEDTSIG);
11179742fdb4Schristos }
1118e2b1b9c0Schristos
1119e2b1b9c0Schristos /*
1120e2b1b9c0Schristos * If this is a response and there's no key or query TSIG, there
1121e2b1b9c0Schristos * shouldn't be one on the response.
1122e2b1b9c0Schristos */
11239742fdb4Schristos if (response && (tsigkey == NULL || msg->querytsig == NULL)) {
1124e2b1b9c0Schristos return (DNS_R_UNEXPECTEDTSIG);
11259742fdb4Schristos }
1126e2b1b9c0Schristos
1127e2b1b9c0Schristos mctx = msg->mctx;
1128e2b1b9c0Schristos
1129e2b1b9c0Schristos /*
1130e2b1b9c0Schristos * If we're here, we know the message is well formed and contains a
1131e2b1b9c0Schristos * TSIG record.
1132e2b1b9c0Schristos */
1133e2b1b9c0Schristos
1134e2b1b9c0Schristos keyname = msg->tsigname;
1135e2b1b9c0Schristos ret = dns_rdataset_first(msg->tsig);
11369742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1137e2b1b9c0Schristos return (ret);
11389742fdb4Schristos }
1139e2b1b9c0Schristos dns_rdataset_current(msg->tsig, &rdata);
1140e2b1b9c0Schristos ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
11419742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1142e2b1b9c0Schristos return (ret);
11439742fdb4Schristos }
1144e2b1b9c0Schristos dns_rdata_reset(&rdata);
1145e2b1b9c0Schristos if (response) {
1146e2b1b9c0Schristos ret = dns_rdataset_first(msg->querytsig);
11479742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1148e2b1b9c0Schristos return (ret);
1149e2b1b9c0Schristos }
11509742fdb4Schristos dns_rdataset_current(msg->querytsig, &rdata);
11519742fdb4Schristos ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
11529742fdb4Schristos if (ret != ISC_R_SUCCESS) {
11539742fdb4Schristos return (ret);
11549742fdb4Schristos }
11559742fdb4Schristos }
1156e2b1b9c0Schristos
1157e2b1b9c0Schristos /*
1158e2b1b9c0Schristos * Do the key name and algorithm match that of the query?
1159e2b1b9c0Schristos */
1160e2b1b9c0Schristos if (response &&
1161e2b1b9c0Schristos (!dns_name_equal(keyname, &tsigkey->name) ||
11629742fdb4Schristos !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)))
11639742fdb4Schristos {
1164e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badkey;
1165e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1166e2b1b9c0Schristos "key name and algorithm do not match");
1167e2b1b9c0Schristos return (DNS_R_TSIGVERIFYFAILURE);
1168e2b1b9c0Schristos }
1169e2b1b9c0Schristos
1170e2b1b9c0Schristos /*
1171e2b1b9c0Schristos * Get the current time.
1172e2b1b9c0Schristos */
1173e2b1b9c0Schristos isc_stdtime_get(&now);
1174e2b1b9c0Schristos
1175e2b1b9c0Schristos /*
1176e2b1b9c0Schristos * Find dns_tsigkey_t based on keyname.
1177e2b1b9c0Schristos */
1178e2b1b9c0Schristos if (tsigkey == NULL) {
1179e2b1b9c0Schristos ret = ISC_R_NOTFOUND;
11809742fdb4Schristos if (ring1 != NULL) {
1181e2b1b9c0Schristos ret = dns_tsigkey_find(&tsigkey, keyname,
1182e2b1b9c0Schristos &tsig.algorithm, ring1);
11839742fdb4Schristos }
11849742fdb4Schristos if (ret == ISC_R_NOTFOUND && ring2 != NULL) {
1185e2b1b9c0Schristos ret = dns_tsigkey_find(&tsigkey, keyname,
1186e2b1b9c0Schristos &tsig.algorithm, ring2);
11879742fdb4Schristos }
1188e2b1b9c0Schristos if (ret != ISC_R_SUCCESS) {
1189e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badkey;
11909742fdb4Schristos ret = dns_tsigkey_create(keyname, &tsig.algorithm, NULL,
11919742fdb4Schristos 0, false, NULL, now, now, mctx,
11929742fdb4Schristos NULL, &msg->tsigkey);
11939742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1194e2b1b9c0Schristos return (ret);
11959742fdb4Schristos }
1196e2b1b9c0Schristos tsig_log(msg->tsigkey, 2, "unknown key");
1197e2b1b9c0Schristos return (DNS_R_TSIGVERIFYFAILURE);
1198e2b1b9c0Schristos }
1199e2b1b9c0Schristos msg->tsigkey = tsigkey;
1200e2b1b9c0Schristos }
1201e2b1b9c0Schristos
1202e2b1b9c0Schristos key = tsigkey->key;
1203e2b1b9c0Schristos
1204e2b1b9c0Schristos /*
1205e2b1b9c0Schristos * Check digest length.
1206e2b1b9c0Schristos */
1207e2b1b9c0Schristos alg = dst_key_alg(key);
1208e2b1b9c0Schristos ret = dst_key_sigsize(key, &siglen);
12099742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1210e2b1b9c0Schristos return (ret);
12119742fdb4Schristos }
1212e2b1b9c0Schristos if (dns__tsig_algvalid(alg)) {
1213e2b1b9c0Schristos if (tsig.siglen > siglen) {
1214e2b1b9c0Schristos tsig_log(msg->tsigkey, 2, "signature length too big");
1215e2b1b9c0Schristos return (DNS_R_FORMERR);
1216e2b1b9c0Schristos }
1217e2b1b9c0Schristos if (tsig.siglen > 0 &&
1218*4ac1c27eSchristos (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2)))
1219*4ac1c27eSchristos {
1220e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1221e2b1b9c0Schristos "signature length below minimum");
1222e2b1b9c0Schristos return (DNS_R_FORMERR);
1223e2b1b9c0Schristos }
1224e2b1b9c0Schristos }
1225e2b1b9c0Schristos
1226e2b1b9c0Schristos if (tsig.siglen > 0) {
1227f2e20987Schristos uint16_t addcount_n;
1228e2b1b9c0Schristos
1229e2b1b9c0Schristos sig_r.base = tsig.signature;
1230e2b1b9c0Schristos sig_r.length = tsig.siglen;
1231e2b1b9c0Schristos
12329742fdb4Schristos ret = dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC,
1233f2e20987Schristos false, 0, &ctx);
12349742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1235e2b1b9c0Schristos return (ret);
12369742fdb4Schristos }
1237e2b1b9c0Schristos
1238e2b1b9c0Schristos if (response) {
1239e2b1b9c0Schristos isc_buffer_init(&databuf, data, sizeof(data));
1240e2b1b9c0Schristos isc_buffer_putuint16(&databuf, querytsig.siglen);
1241e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
1242e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
12439742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1244e2b1b9c0Schristos goto cleanup_context;
12459742fdb4Schristos }
1246e2b1b9c0Schristos if (querytsig.siglen > 0) {
1247e2b1b9c0Schristos r.length = querytsig.siglen;
1248e2b1b9c0Schristos r.base = querytsig.signature;
1249e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
12509742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1251e2b1b9c0Schristos goto cleanup_context;
1252e2b1b9c0Schristos }
1253e2b1b9c0Schristos }
12549742fdb4Schristos }
1255e2b1b9c0Schristos
1256e2b1b9c0Schristos /*
1257e2b1b9c0Schristos * Extract the header.
1258e2b1b9c0Schristos */
1259e2b1b9c0Schristos isc_buffer_usedregion(source, &r);
1260e2b1b9c0Schristos memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1261e2b1b9c0Schristos isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1262e2b1b9c0Schristos
1263e2b1b9c0Schristos /*
1264e2b1b9c0Schristos * Decrement the additional field counter.
1265e2b1b9c0Schristos */
1266e2b1b9c0Schristos memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1267e2b1b9c0Schristos addcount_n = ntohs(addcount);
1268f2e20987Schristos addcount = htons((uint16_t)(addcount_n - 1));
1269e2b1b9c0Schristos memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1270e2b1b9c0Schristos
1271e2b1b9c0Schristos /*
1272e2b1b9c0Schristos * Put in the original id.
1273e2b1b9c0Schristos */
1274e2b1b9c0Schristos id = htons(tsig.originalid);
1275e2b1b9c0Schristos memmove(&header[0], &id, 2);
1276e2b1b9c0Schristos
1277e2b1b9c0Schristos /*
1278e2b1b9c0Schristos * Digest the modified header.
1279e2b1b9c0Schristos */
1280e2b1b9c0Schristos header_r.base = (unsigned char *)header;
1281e2b1b9c0Schristos header_r.length = DNS_MESSAGE_HEADERLEN;
1282e2b1b9c0Schristos ret = dst_context_adddata(ctx, &header_r);
12839742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1284e2b1b9c0Schristos goto cleanup_context;
12859742fdb4Schristos }
1286e2b1b9c0Schristos
1287e2b1b9c0Schristos /*
1288e2b1b9c0Schristos * Digest all non-TSIG records.
1289e2b1b9c0Schristos */
1290e2b1b9c0Schristos isc_buffer_usedregion(source, &source_r);
1291e2b1b9c0Schristos r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
1292e2b1b9c0Schristos r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
1293e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
12949742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1295e2b1b9c0Schristos goto cleanup_context;
12969742fdb4Schristos }
1297e2b1b9c0Schristos
1298e2b1b9c0Schristos /*
1299e2b1b9c0Schristos * Digest the key name.
1300e2b1b9c0Schristos */
1301e2b1b9c0Schristos dns_name_toregion(&tsigkey->name, &r);
1302e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
13039742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1304e2b1b9c0Schristos goto cleanup_context;
13059742fdb4Schristos }
1306e2b1b9c0Schristos
1307e2b1b9c0Schristos isc_buffer_init(&databuf, data, sizeof(data));
1308e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.common.rdclass);
1309e2b1b9c0Schristos isc_buffer_putuint32(&databuf, msg->tsig->ttl);
1310e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
1311e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
13129742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1313e2b1b9c0Schristos goto cleanup_context;
13149742fdb4Schristos }
1315e2b1b9c0Schristos
1316e2b1b9c0Schristos /*
1317e2b1b9c0Schristos * Digest the key algorithm.
1318e2b1b9c0Schristos */
1319e2b1b9c0Schristos dns_name_toregion(tsigkey->algorithm, &r);
1320e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
13219742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1322e2b1b9c0Schristos goto cleanup_context;
13239742fdb4Schristos }
1324e2b1b9c0Schristos
1325e2b1b9c0Schristos isc_buffer_clear(&databuf);
1326e2b1b9c0Schristos isc_buffer_putuint48(&databuf, tsig.timesigned);
1327e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.fudge);
1328e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.error);
1329e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.otherlen);
1330e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
1331e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
13329742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1333e2b1b9c0Schristos goto cleanup_context;
13349742fdb4Schristos }
1335e2b1b9c0Schristos
1336e2b1b9c0Schristos if (tsig.otherlen > 0) {
1337e2b1b9c0Schristos r.base = tsig.other;
1338e2b1b9c0Schristos r.length = tsig.otherlen;
1339e2b1b9c0Schristos ret = dst_context_adddata(ctx, &r);
13409742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1341e2b1b9c0Schristos goto cleanup_context;
1342e2b1b9c0Schristos }
13439742fdb4Schristos }
1344e2b1b9c0Schristos
1345e2b1b9c0Schristos ret = dst_context_verify(ctx, &sig_r);
1346e2b1b9c0Schristos if (ret == DST_R_VERIFYFAILURE) {
1347e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1348e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1349e2b1b9c0Schristos "signature failed to verify(1)");
1350e2b1b9c0Schristos goto cleanup_context;
1351e2b1b9c0Schristos } else if (ret != ISC_R_SUCCESS) {
1352e2b1b9c0Schristos goto cleanup_context;
1353e2b1b9c0Schristos }
1354e2b1b9c0Schristos msg->verified_sig = 1;
13559742fdb4Schristos } else if (!response || (tsig.error != dns_tsigerror_badsig &&
13569742fdb4Schristos tsig.error != dns_tsigerror_badkey))
13579742fdb4Schristos {
1358e2b1b9c0Schristos tsig_log(msg->tsigkey, 2, "signature was empty");
1359e2b1b9c0Schristos return (DNS_R_TSIGVERIFYFAILURE);
1360e2b1b9c0Schristos }
1361e2b1b9c0Schristos
1362e2b1b9c0Schristos /*
1363e2b1b9c0Schristos * Here at this point, the MAC has been verified. Even if any of
1364e2b1b9c0Schristos * the following code returns a TSIG error, the reply will be
1365e2b1b9c0Schristos * signed and WILL always include the request MAC in the digest
1366e2b1b9c0Schristos * computation.
1367e2b1b9c0Schristos */
1368e2b1b9c0Schristos
1369e2b1b9c0Schristos /*
1370e2b1b9c0Schristos * Is the time ok?
1371e2b1b9c0Schristos */
1372e2b1b9c0Schristos if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1373e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtime;
1374e2b1b9c0Schristos tsig_log(msg->tsigkey, 2, "signature has expired");
1375e2b1b9c0Schristos ret = DNS_R_CLOCKSKEW;
1376e2b1b9c0Schristos goto cleanup_context;
1377e2b1b9c0Schristos } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) {
1378e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtime;
1379e2b1b9c0Schristos tsig_log(msg->tsigkey, 2, "signature is in the future");
1380e2b1b9c0Schristos ret = DNS_R_CLOCKSKEW;
1381e2b1b9c0Schristos goto cleanup_context;
1382e2b1b9c0Schristos }
1383e2b1b9c0Schristos
1384e2b1b9c0Schristos if (dns__tsig_algvalid(alg)) {
1385f2e20987Schristos uint16_t digestbits = dst_key_getbits(key);
1386e2b1b9c0Schristos
1387e2b1b9c0Schristos if (tsig.siglen > 0 && digestbits != 0 &&
1388*4ac1c27eSchristos tsig.siglen < ((digestbits + 7) / 8))
1389*4ac1c27eSchristos {
1390e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtrunc;
1391e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1392e2b1b9c0Schristos "truncated signature length too small");
1393e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1394e2b1b9c0Schristos goto cleanup_context;
1395e2b1b9c0Schristos }
13969742fdb4Schristos if (tsig.siglen > 0 && digestbits == 0 && tsig.siglen < siglen)
1397e2b1b9c0Schristos {
1398e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtrunc;
1399e2b1b9c0Schristos tsig_log(msg->tsigkey, 2, "signature length too small");
1400e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1401e2b1b9c0Schristos goto cleanup_context;
1402e2b1b9c0Schristos }
1403e2b1b9c0Schristos }
1404e2b1b9c0Schristos
14059742fdb4Schristos if (response && tsig.error != dns_rcode_noerror) {
1406e2b1b9c0Schristos msg->tsigstatus = tsig.error;
14079742fdb4Schristos if (tsig.error == dns_tsigerror_badtime) {
1408e2b1b9c0Schristos ret = DNS_R_CLOCKSKEW;
14099742fdb4Schristos } else {
1410e2b1b9c0Schristos ret = DNS_R_TSIGERRORSET;
14119742fdb4Schristos }
1412e2b1b9c0Schristos goto cleanup_context;
1413e2b1b9c0Schristos }
1414e2b1b9c0Schristos
1415e2b1b9c0Schristos msg->tsigstatus = dns_rcode_noerror;
1416e2b1b9c0Schristos ret = ISC_R_SUCCESS;
1417e2b1b9c0Schristos
1418e2b1b9c0Schristos cleanup_context:
14199742fdb4Schristos if (ctx != NULL) {
1420e2b1b9c0Schristos dst_context_destroy(&ctx);
14219742fdb4Schristos }
1422e2b1b9c0Schristos
1423e2b1b9c0Schristos return (ret);
1424e2b1b9c0Schristos }
1425e2b1b9c0Schristos
1426e2b1b9c0Schristos static isc_result_t
tsig_verify_tcp(isc_buffer_t * source,dns_message_t * msg)1427e2b1b9c0Schristos tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) {
1428e2b1b9c0Schristos dns_rdata_any_tsig_t tsig, querytsig;
1429e2b1b9c0Schristos isc_region_t r, source_r, header_r, sig_r;
1430e2b1b9c0Schristos isc_buffer_t databuf;
1431e2b1b9c0Schristos unsigned char data[32];
1432e2b1b9c0Schristos dns_name_t *keyname;
1433e2b1b9c0Schristos dns_rdata_t rdata = DNS_RDATA_INIT;
1434e2b1b9c0Schristos isc_stdtime_t now;
1435e2b1b9c0Schristos isc_result_t ret;
1436e2b1b9c0Schristos dns_tsigkey_t *tsigkey;
1437e2b1b9c0Schristos dst_key_t *key = NULL;
1438e2b1b9c0Schristos unsigned char header[DNS_MESSAGE_HEADERLEN];
1439f2e20987Schristos uint16_t addcount, id;
1440f2e20987Schristos bool has_tsig = false;
1441e2b1b9c0Schristos isc_mem_t *mctx;
1442e2b1b9c0Schristos unsigned int siglen;
1443e2b1b9c0Schristos unsigned int alg;
1444e2b1b9c0Schristos
1445e2b1b9c0Schristos REQUIRE(source != NULL);
1446e2b1b9c0Schristos REQUIRE(msg != NULL);
1447e2b1b9c0Schristos REQUIRE(dns_message_gettsigkey(msg) != NULL);
1448e2b1b9c0Schristos REQUIRE(msg->tcp_continuation == 1);
1449e2b1b9c0Schristos REQUIRE(msg->querytsig != NULL);
1450e2b1b9c0Schristos
1451e2b1b9c0Schristos msg->verified_sig = 0;
1452e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badsig;
1453e2b1b9c0Schristos
14549742fdb4Schristos if (!is_response(msg)) {
1455e2b1b9c0Schristos return (DNS_R_EXPECTEDRESPONSE);
14569742fdb4Schristos }
1457e2b1b9c0Schristos
1458e2b1b9c0Schristos mctx = msg->mctx;
1459e2b1b9c0Schristos
1460e2b1b9c0Schristos tsigkey = dns_message_gettsigkey(msg);
1461e2b1b9c0Schristos key = tsigkey->key;
1462e2b1b9c0Schristos
1463e2b1b9c0Schristos /*
1464e2b1b9c0Schristos * Extract and parse the previous TSIG
1465e2b1b9c0Schristos */
1466e2b1b9c0Schristos ret = dns_rdataset_first(msg->querytsig);
14679742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1468e2b1b9c0Schristos return (ret);
14699742fdb4Schristos }
1470e2b1b9c0Schristos dns_rdataset_current(msg->querytsig, &rdata);
1471e2b1b9c0Schristos ret = dns_rdata_tostruct(&rdata, &querytsig, NULL);
14729742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1473e2b1b9c0Schristos return (ret);
14749742fdb4Schristos }
1475e2b1b9c0Schristos dns_rdata_reset(&rdata);
1476e2b1b9c0Schristos
1477e2b1b9c0Schristos /*
1478e2b1b9c0Schristos * If there is a TSIG in this message, do some checks.
1479e2b1b9c0Schristos */
1480e2b1b9c0Schristos if (msg->tsig != NULL) {
1481f2e20987Schristos has_tsig = true;
1482e2b1b9c0Schristos
1483e2b1b9c0Schristos keyname = msg->tsigname;
1484e2b1b9c0Schristos ret = dns_rdataset_first(msg->tsig);
14859742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1486e2b1b9c0Schristos goto cleanup_querystruct;
14879742fdb4Schristos }
1488e2b1b9c0Schristos dns_rdataset_current(msg->tsig, &rdata);
1489e2b1b9c0Schristos ret = dns_rdata_tostruct(&rdata, &tsig, NULL);
14909742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1491e2b1b9c0Schristos goto cleanup_querystruct;
14929742fdb4Schristos }
1493e2b1b9c0Schristos
1494e2b1b9c0Schristos /*
1495e2b1b9c0Schristos * Do the key name and algorithm match that of the query?
1496e2b1b9c0Schristos */
1497e2b1b9c0Schristos if (!dns_name_equal(keyname, &tsigkey->name) ||
1498e2b1b9c0Schristos !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))
1499e2b1b9c0Schristos {
1500e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badkey;
1501e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1502e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1503e2b1b9c0Schristos "key name and algorithm do not match");
1504e2b1b9c0Schristos goto cleanup_querystruct;
1505e2b1b9c0Schristos }
1506e2b1b9c0Schristos
1507e2b1b9c0Schristos /*
1508e2b1b9c0Schristos * Check digest length.
1509e2b1b9c0Schristos */
1510e2b1b9c0Schristos alg = dst_key_alg(key);
1511e2b1b9c0Schristos ret = dst_key_sigsize(key, &siglen);
15129742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1513e2b1b9c0Schristos goto cleanup_querystruct;
15149742fdb4Schristos }
1515e2b1b9c0Schristos if (dns__tsig_algvalid(alg)) {
1516e2b1b9c0Schristos if (tsig.siglen > siglen) {
1517e2b1b9c0Schristos tsig_log(tsigkey, 2,
1518e2b1b9c0Schristos "signature length too big");
1519e2b1b9c0Schristos ret = DNS_R_FORMERR;
1520e2b1b9c0Schristos goto cleanup_querystruct;
1521e2b1b9c0Schristos }
1522e2b1b9c0Schristos if (tsig.siglen > 0 &&
1523e2b1b9c0Schristos (tsig.siglen < 10 ||
1524*4ac1c27eSchristos tsig.siglen < ((siglen + 1) / 2)))
1525*4ac1c27eSchristos {
1526e2b1b9c0Schristos tsig_log(tsigkey, 2,
1527e2b1b9c0Schristos "signature length below minimum");
1528e2b1b9c0Schristos ret = DNS_R_FORMERR;
1529e2b1b9c0Schristos goto cleanup_querystruct;
1530e2b1b9c0Schristos }
1531e2b1b9c0Schristos }
1532e2b1b9c0Schristos }
1533e2b1b9c0Schristos
1534e2b1b9c0Schristos if (msg->tsigctx == NULL) {
15359742fdb4Schristos ret = dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC,
1536f2e20987Schristos false, 0, &msg->tsigctx);
15379742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1538e2b1b9c0Schristos goto cleanup_querystruct;
15399742fdb4Schristos }
1540e2b1b9c0Schristos
1541e2b1b9c0Schristos /*
1542e2b1b9c0Schristos * Digest the length of the query signature
1543e2b1b9c0Schristos */
1544e2b1b9c0Schristos isc_buffer_init(&databuf, data, sizeof(data));
1545e2b1b9c0Schristos isc_buffer_putuint16(&databuf, querytsig.siglen);
1546e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
1547e2b1b9c0Schristos ret = dst_context_adddata(msg->tsigctx, &r);
15489742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1549e2b1b9c0Schristos goto cleanup_context;
15509742fdb4Schristos }
1551e2b1b9c0Schristos
1552e2b1b9c0Schristos /*
1553e2b1b9c0Schristos * Digest the data of the query signature
1554e2b1b9c0Schristos */
1555e2b1b9c0Schristos if (querytsig.siglen > 0) {
1556e2b1b9c0Schristos r.length = querytsig.siglen;
1557e2b1b9c0Schristos r.base = querytsig.signature;
1558e2b1b9c0Schristos ret = dst_context_adddata(msg->tsigctx, &r);
15599742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1560e2b1b9c0Schristos goto cleanup_context;
1561e2b1b9c0Schristos }
1562e2b1b9c0Schristos }
15639742fdb4Schristos }
1564e2b1b9c0Schristos
1565e2b1b9c0Schristos /*
1566e2b1b9c0Schristos * Extract the header.
1567e2b1b9c0Schristos */
1568e2b1b9c0Schristos isc_buffer_usedregion(source, &r);
1569e2b1b9c0Schristos memmove(header, r.base, DNS_MESSAGE_HEADERLEN);
1570e2b1b9c0Schristos isc_region_consume(&r, DNS_MESSAGE_HEADERLEN);
1571e2b1b9c0Schristos
1572e2b1b9c0Schristos /*
1573e2b1b9c0Schristos * Decrement the additional field counter if necessary.
1574e2b1b9c0Schristos */
1575e2b1b9c0Schristos if (has_tsig) {
1576f2e20987Schristos uint16_t addcount_n;
1577e2b1b9c0Schristos
1578e2b1b9c0Schristos memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2);
1579e2b1b9c0Schristos addcount_n = ntohs(addcount);
1580f2e20987Schristos addcount = htons((uint16_t)(addcount_n - 1));
1581e2b1b9c0Schristos memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2);
1582e2b1b9c0Schristos
1583e2b1b9c0Schristos /*
1584e2b1b9c0Schristos * Put in the original id.
1585e2b1b9c0Schristos *
1586e2b1b9c0Schristos * XXX Can TCP transfers be forwarded? How would that
1587e2b1b9c0Schristos * work?
1588e2b1b9c0Schristos */
15899742fdb4Schristos /* cppcheck-suppress uninitStructMember
15909742fdb4Schristos * symbolName=tsig.originalid */
1591e2b1b9c0Schristos id = htons(tsig.originalid);
1592e2b1b9c0Schristos memmove(&header[0], &id, 2);
1593e2b1b9c0Schristos }
1594e2b1b9c0Schristos
1595e2b1b9c0Schristos /*
1596e2b1b9c0Schristos * Digest the modified header.
1597e2b1b9c0Schristos */
1598e2b1b9c0Schristos header_r.base = (unsigned char *)header;
1599e2b1b9c0Schristos header_r.length = DNS_MESSAGE_HEADERLEN;
1600e2b1b9c0Schristos ret = dst_context_adddata(msg->tsigctx, &header_r);
16019742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1602e2b1b9c0Schristos goto cleanup_context;
16039742fdb4Schristos }
1604e2b1b9c0Schristos
1605e2b1b9c0Schristos /*
1606e2b1b9c0Schristos * Digest all non-TSIG records.
1607e2b1b9c0Schristos */
1608e2b1b9c0Schristos isc_buffer_usedregion(source, &source_r);
1609e2b1b9c0Schristos r.base = source_r.base + DNS_MESSAGE_HEADERLEN;
16109742fdb4Schristos if (has_tsig) {
1611e2b1b9c0Schristos r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN;
16129742fdb4Schristos } else {
1613e2b1b9c0Schristos r.length = source_r.length - DNS_MESSAGE_HEADERLEN;
16149742fdb4Schristos }
1615e2b1b9c0Schristos ret = dst_context_adddata(msg->tsigctx, &r);
16169742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1617e2b1b9c0Schristos goto cleanup_context;
16189742fdb4Schristos }
1619e2b1b9c0Schristos
1620e2b1b9c0Schristos /*
1621e2b1b9c0Schristos * Digest the time signed and fudge.
1622e2b1b9c0Schristos */
1623e2b1b9c0Schristos if (has_tsig) {
1624e2b1b9c0Schristos isc_buffer_init(&databuf, data, sizeof(data));
1625e2b1b9c0Schristos isc_buffer_putuint48(&databuf, tsig.timesigned);
1626e2b1b9c0Schristos isc_buffer_putuint16(&databuf, tsig.fudge);
1627e2b1b9c0Schristos isc_buffer_usedregion(&databuf, &r);
1628e2b1b9c0Schristos ret = dst_context_adddata(msg->tsigctx, &r);
16299742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1630e2b1b9c0Schristos goto cleanup_context;
16319742fdb4Schristos }
1632e2b1b9c0Schristos
1633e2b1b9c0Schristos sig_r.base = tsig.signature;
1634e2b1b9c0Schristos sig_r.length = tsig.siglen;
1635e2b1b9c0Schristos if (tsig.siglen == 0) {
1636e2b1b9c0Schristos if (tsig.error != dns_rcode_noerror) {
1637e2b1b9c0Schristos msg->tsigstatus = tsig.error;
1638e2b1b9c0Schristos if (tsig.error == dns_tsigerror_badtime) {
1639e2b1b9c0Schristos ret = DNS_R_CLOCKSKEW;
1640e2b1b9c0Schristos } else {
1641e2b1b9c0Schristos ret = DNS_R_TSIGERRORSET;
1642e2b1b9c0Schristos }
1643e2b1b9c0Schristos } else {
16449742fdb4Schristos tsig_log(msg->tsigkey, 2, "signature is empty");
1645e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1646e2b1b9c0Schristos }
1647e2b1b9c0Schristos goto cleanup_context;
1648e2b1b9c0Schristos }
1649e2b1b9c0Schristos
1650e2b1b9c0Schristos ret = dst_context_verify(msg->tsigctx, &sig_r);
1651e2b1b9c0Schristos if (ret == DST_R_VERIFYFAILURE) {
1652e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1653e2b1b9c0Schristos "signature failed to verify(2)");
1654e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1655e2b1b9c0Schristos goto cleanup_context;
1656e2b1b9c0Schristos } else if (ret != ISC_R_SUCCESS) {
1657e2b1b9c0Schristos goto cleanup_context;
1658e2b1b9c0Schristos }
1659e2b1b9c0Schristos msg->verified_sig = 1;
1660e2b1b9c0Schristos
1661e2b1b9c0Schristos /*
1662e2b1b9c0Schristos * Here at this point, the MAC has been verified. Even
1663e2b1b9c0Schristos * if any of the following code returns a TSIG error,
1664e2b1b9c0Schristos * the reply will be signed and WILL always include the
1665e2b1b9c0Schristos * request MAC in the digest computation.
1666e2b1b9c0Schristos */
1667e2b1b9c0Schristos
1668e2b1b9c0Schristos /*
1669e2b1b9c0Schristos * Is the time ok?
1670e2b1b9c0Schristos */
1671e2b1b9c0Schristos isc_stdtime_get(&now);
1672e2b1b9c0Schristos
1673e2b1b9c0Schristos if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) {
1674e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtime;
1675e2b1b9c0Schristos tsig_log(msg->tsigkey, 2, "signature has expired");
1676e2b1b9c0Schristos ret = DNS_R_CLOCKSKEW;
1677e2b1b9c0Schristos goto cleanup_context;
16789742fdb4Schristos } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge)
1679e2b1b9c0Schristos {
1680e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtime;
16819742fdb4Schristos tsig_log(msg->tsigkey, 2, "signature is in the future");
1682e2b1b9c0Schristos ret = DNS_R_CLOCKSKEW;
1683e2b1b9c0Schristos goto cleanup_context;
1684e2b1b9c0Schristos }
1685e2b1b9c0Schristos
1686e2b1b9c0Schristos alg = dst_key_alg(key);
1687e2b1b9c0Schristos ret = dst_key_sigsize(key, &siglen);
16889742fdb4Schristos if (ret != ISC_R_SUCCESS) {
1689e2b1b9c0Schristos goto cleanup_context;
16909742fdb4Schristos }
1691e2b1b9c0Schristos if (dns__tsig_algvalid(alg)) {
1692f2e20987Schristos uint16_t digestbits = dst_key_getbits(key);
1693e2b1b9c0Schristos
1694e2b1b9c0Schristos if (tsig.siglen > 0 && digestbits != 0 &&
1695*4ac1c27eSchristos tsig.siglen < ((digestbits + 7) / 8))
1696*4ac1c27eSchristos {
1697e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtrunc;
1698e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1699e2b1b9c0Schristos "truncated signature length "
1700e2b1b9c0Schristos "too small");
1701e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1702e2b1b9c0Schristos goto cleanup_context;
1703e2b1b9c0Schristos }
1704e2b1b9c0Schristos if (tsig.siglen > 0 && digestbits == 0 &&
1705*4ac1c27eSchristos tsig.siglen < siglen)
1706*4ac1c27eSchristos {
1707e2b1b9c0Schristos msg->tsigstatus = dns_tsigerror_badtrunc;
1708e2b1b9c0Schristos tsig_log(msg->tsigkey, 2,
1709e2b1b9c0Schristos "signature length too small");
1710e2b1b9c0Schristos ret = DNS_R_TSIGVERIFYFAILURE;
1711e2b1b9c0Schristos goto cleanup_context;
1712e2b1b9c0Schristos }
1713e2b1b9c0Schristos }
1714e2b1b9c0Schristos
1715e2b1b9c0Schristos if (tsig.error != dns_rcode_noerror) {
1716e2b1b9c0Schristos msg->tsigstatus = tsig.error;
17179742fdb4Schristos if (tsig.error == dns_tsigerror_badtime) {
1718e2b1b9c0Schristos ret = DNS_R_CLOCKSKEW;
17199742fdb4Schristos } else {
1720e2b1b9c0Schristos ret = DNS_R_TSIGERRORSET;
17219742fdb4Schristos }
1722e2b1b9c0Schristos goto cleanup_context;
1723e2b1b9c0Schristos }
1724e2b1b9c0Schristos }
1725e2b1b9c0Schristos
1726e2b1b9c0Schristos msg->tsigstatus = dns_rcode_noerror;
1727e2b1b9c0Schristos ret = ISC_R_SUCCESS;
1728e2b1b9c0Schristos
1729e2b1b9c0Schristos cleanup_context:
1730e2b1b9c0Schristos /*
1731e2b1b9c0Schristos * Except in error conditions, don't destroy the DST context
1732e2b1b9c0Schristos * for unsigned messages; it is a running sum till the next
1733e2b1b9c0Schristos * TSIG signed message.
1734e2b1b9c0Schristos */
1735e2b1b9c0Schristos if ((ret != ISC_R_SUCCESS || has_tsig) && msg->tsigctx != NULL) {
1736e2b1b9c0Schristos dst_context_destroy(&msg->tsigctx);
1737e2b1b9c0Schristos }
1738e2b1b9c0Schristos
1739e2b1b9c0Schristos cleanup_querystruct:
1740e2b1b9c0Schristos dns_rdata_freestruct(&querytsig);
1741e2b1b9c0Schristos
1742e2b1b9c0Schristos return (ret);
1743e2b1b9c0Schristos }
1744e2b1b9c0Schristos
1745e2b1b9c0Schristos isc_result_t
dns_tsigkey_find(dns_tsigkey_t ** tsigkey,const dns_name_t * name,const dns_name_t * algorithm,dns_tsig_keyring_t * ring)1746e2b1b9c0Schristos dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
17479742fdb4Schristos const dns_name_t *algorithm, dns_tsig_keyring_t *ring) {
1748e2b1b9c0Schristos dns_tsigkey_t *key;
1749e2b1b9c0Schristos isc_stdtime_t now;
1750e2b1b9c0Schristos isc_result_t result;
1751e2b1b9c0Schristos
1752e2b1b9c0Schristos REQUIRE(tsigkey != NULL);
1753e2b1b9c0Schristos REQUIRE(*tsigkey == NULL);
1754e2b1b9c0Schristos REQUIRE(name != NULL);
1755e2b1b9c0Schristos REQUIRE(ring != NULL);
1756e2b1b9c0Schristos
1757e2b1b9c0Schristos RWLOCK(&ring->lock, isc_rwlocktype_write);
1758e2b1b9c0Schristos cleanup_ring(ring);
1759e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1760e2b1b9c0Schristos
1761e2b1b9c0Schristos isc_stdtime_get(&now);
1762e2b1b9c0Schristos RWLOCK(&ring->lock, isc_rwlocktype_read);
1763e2b1b9c0Schristos key = NULL;
1764e2b1b9c0Schristos result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
1765e2b1b9c0Schristos if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
1766e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1767e2b1b9c0Schristos return (ISC_R_NOTFOUND);
1768e2b1b9c0Schristos }
1769e2b1b9c0Schristos if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
1770e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1771e2b1b9c0Schristos return (ISC_R_NOTFOUND);
1772e2b1b9c0Schristos }
1773e2b1b9c0Schristos if (key->inception != key->expire && isc_serial_lt(key->expire, now)) {
1774e2b1b9c0Schristos /*
1775e2b1b9c0Schristos * The key has expired.
1776e2b1b9c0Schristos */
1777e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1778e2b1b9c0Schristos RWLOCK(&ring->lock, isc_rwlocktype_write);
1779e2b1b9c0Schristos remove_fromring(key);
1780e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_write);
1781e2b1b9c0Schristos return (ISC_R_NOTFOUND);
1782e2b1b9c0Schristos }
1783e2b1b9c0Schristos #if 0
1784e2b1b9c0Schristos /*
1785e2b1b9c0Schristos * MPAXXX We really should look at the inception time.
1786e2b1b9c0Schristos */
1787e2b1b9c0Schristos if (key->inception != key->expire &&
1788e2b1b9c0Schristos isc_serial_lt(key->inception, now)) {
1789e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1790e2b1b9c0Schristos adjust_lru(key);
1791e2b1b9c0Schristos return (ISC_R_NOTFOUND);
1792e2b1b9c0Schristos }
17939742fdb4Schristos #endif /* if 0 */
1794f2e20987Schristos isc_refcount_increment(&key->refs);
1795e2b1b9c0Schristos RWUNLOCK(&ring->lock, isc_rwlocktype_read);
1796e2b1b9c0Schristos adjust_lru(key);
1797e2b1b9c0Schristos *tsigkey = key;
1798e2b1b9c0Schristos return (ISC_R_SUCCESS);
1799e2b1b9c0Schristos }
1800e2b1b9c0Schristos
1801e2b1b9c0Schristos static void
free_tsignode(void * node,void * _unused)1802e2b1b9c0Schristos free_tsignode(void *node, void *_unused) {
1803e2b1b9c0Schristos dns_tsigkey_t *key;
1804e2b1b9c0Schristos
1805e2b1b9c0Schristos REQUIRE(node != NULL);
1806e2b1b9c0Schristos
1807e2b1b9c0Schristos UNUSED(_unused);
1808e2b1b9c0Schristos
1809e2b1b9c0Schristos key = node;
1810e2b1b9c0Schristos if (key->generated) {
18119742fdb4Schristos if (ISC_LINK_LINKED(key, link)) {
1812e2b1b9c0Schristos ISC_LIST_UNLINK(key->ring->lru, key, link);
1813e2b1b9c0Schristos }
18149742fdb4Schristos }
1815e2b1b9c0Schristos dns_tsigkey_detach(&key);
1816e2b1b9c0Schristos }
1817e2b1b9c0Schristos
1818e2b1b9c0Schristos isc_result_t
dns_tsigkeyring_create(isc_mem_t * mctx,dns_tsig_keyring_t ** ringp)1819e2b1b9c0Schristos dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) {
1820e2b1b9c0Schristos isc_result_t result;
1821e2b1b9c0Schristos dns_tsig_keyring_t *ring;
1822e2b1b9c0Schristos
1823e2b1b9c0Schristos REQUIRE(mctx != NULL);
1824e2b1b9c0Schristos REQUIRE(ringp != NULL);
1825e2b1b9c0Schristos REQUIRE(*ringp == NULL);
1826e2b1b9c0Schristos
1827e2b1b9c0Schristos ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t));
1828e2b1b9c0Schristos
182976b1fd8fSchristos isc_rwlock_init(&ring->lock, 0, 0);
1830e2b1b9c0Schristos ring->keys = NULL;
1831e2b1b9c0Schristos result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys);
1832e2b1b9c0Schristos if (result != ISC_R_SUCCESS) {
1833e2b1b9c0Schristos isc_rwlock_destroy(&ring->lock);
1834e2b1b9c0Schristos isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t));
1835e2b1b9c0Schristos return (result);
1836e2b1b9c0Schristos }
1837e2b1b9c0Schristos
1838e2b1b9c0Schristos ring->writecount = 0;
1839e2b1b9c0Schristos ring->mctx = NULL;
1840e2b1b9c0Schristos ring->generated = 0;
1841e2b1b9c0Schristos ring->maxgenerated = DNS_TSIG_MAXGENERATEDKEYS;
1842e2b1b9c0Schristos ISC_LIST_INIT(ring->lru);
1843e2b1b9c0Schristos isc_mem_attach(mctx, &ring->mctx);
18449742fdb4Schristos isc_refcount_init(&ring->references, 1);
1845e2b1b9c0Schristos
1846e2b1b9c0Schristos *ringp = ring;
1847e2b1b9c0Schristos return (ISC_R_SUCCESS);
1848e2b1b9c0Schristos }
1849e2b1b9c0Schristos
1850e2b1b9c0Schristos isc_result_t
dns_tsigkeyring_add(dns_tsig_keyring_t * ring,const dns_name_t * name,dns_tsigkey_t * tkey)1851e2b1b9c0Schristos dns_tsigkeyring_add(dns_tsig_keyring_t *ring, const dns_name_t *name,
18529742fdb4Schristos dns_tsigkey_t *tkey) {
1853e2b1b9c0Schristos isc_result_t result;
1854e2b1b9c0Schristos
1855e2b1b9c0Schristos result = keyring_add(ring, name, tkey);
18569742fdb4Schristos if (result == ISC_R_SUCCESS) {
1857f2e20987Schristos isc_refcount_increment(&tkey->refs);
18589742fdb4Schristos }
1859e2b1b9c0Schristos
1860e2b1b9c0Schristos return (result);
1861e2b1b9c0Schristos }
1862e2b1b9c0Schristos
1863e2b1b9c0Schristos void
dns_tsigkeyring_attach(dns_tsig_keyring_t * source,dns_tsig_keyring_t ** target)18649742fdb4Schristos dns_tsigkeyring_attach(dns_tsig_keyring_t *source,
18659742fdb4Schristos dns_tsig_keyring_t **target) {
1866e2b1b9c0Schristos REQUIRE(source != NULL);
1867e2b1b9c0Schristos REQUIRE(target != NULL && *target == NULL);
1868e2b1b9c0Schristos
18699742fdb4Schristos isc_refcount_increment(&source->references);
18709742fdb4Schristos
1871e2b1b9c0Schristos *target = source;
1872e2b1b9c0Schristos }
1873e2b1b9c0Schristos
1874e2b1b9c0Schristos void
dns_tsigkeyring_detach(dns_tsig_keyring_t ** ringp)1875e2b1b9c0Schristos dns_tsigkeyring_detach(dns_tsig_keyring_t **ringp) {
1876e2b1b9c0Schristos dns_tsig_keyring_t *ring;
1877e2b1b9c0Schristos
1878e2b1b9c0Schristos REQUIRE(ringp != NULL);
1879e2b1b9c0Schristos REQUIRE(*ringp != NULL);
1880e2b1b9c0Schristos
1881e2b1b9c0Schristos ring = *ringp;
1882e2b1b9c0Schristos *ringp = NULL;
1883e2b1b9c0Schristos
18849742fdb4Schristos if (isc_refcount_decrement(&ring->references) == 1) {
1885e2b1b9c0Schristos destroyring(ring);
1886e2b1b9c0Schristos }
18879742fdb4Schristos }
1888e2b1b9c0Schristos
1889e2b1b9c0Schristos void
dns_keyring_restore(dns_tsig_keyring_t * ring,FILE * fp)1890e2b1b9c0Schristos dns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp) {
1891e2b1b9c0Schristos isc_stdtime_t now;
1892e2b1b9c0Schristos isc_result_t result;
1893e2b1b9c0Schristos
1894e2b1b9c0Schristos isc_stdtime_get(&now);
1895e2b1b9c0Schristos do {
1896e2b1b9c0Schristos result = restore_key(ring, now, fp);
18979742fdb4Schristos if (result == ISC_R_NOMORE) {
1898e2b1b9c0Schristos return;
18999742fdb4Schristos }
19009742fdb4Schristos if (result == DNS_R_BADALG || result == DNS_R_EXPIRED) {
1901e2b1b9c0Schristos result = ISC_R_SUCCESS;
19029742fdb4Schristos }
1903e2b1b9c0Schristos } while (result == ISC_R_SUCCESS);
1904e2b1b9c0Schristos }
1905