1135446Strhodes/* 2262706Serwin * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2002 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18135446Strhodes/* 19234010Sdougb * $Id$ 20135446Strhodes */ 21170222Sdougb/*! \file */ 22135446Strhodes#include <config.h> 23135446Strhodes#include <stdlib.h> 24135446Strhodes 25135446Strhodes#include <isc/buffer.h> 26135446Strhodes#include <isc/mem.h> 27135446Strhodes#include <isc/print.h> 28135446Strhodes#include <isc/refcount.h> 29218384Sdougb#include <isc/serial.h> 30135446Strhodes#include <isc/string.h> /* Required for HP/UX (and others?) */ 31135446Strhodes#include <isc/util.h> 32193149Sdougb#include <isc/time.h> 33135446Strhodes 34135446Strhodes#include <dns/keyvalues.h> 35135446Strhodes#include <dns/log.h> 36135446Strhodes#include <dns/message.h> 37193149Sdougb#include <dns/fixedname.h> 38135446Strhodes#include <dns/rbt.h> 39135446Strhodes#include <dns/rdata.h> 40135446Strhodes#include <dns/rdatalist.h> 41135446Strhodes#include <dns/rdataset.h> 42135446Strhodes#include <dns/rdatastruct.h> 43135446Strhodes#include <dns/result.h> 44135446Strhodes#include <dns/tsig.h> 45135446Strhodes 46135446Strhodes#include <dst/result.h> 47135446Strhodes 48135446Strhodes#define TSIG_MAGIC ISC_MAGIC('T', 'S', 'I', 'G') 49135446Strhodes#define VALID_TSIG_KEY(x) ISC_MAGIC_VALID(x, TSIG_MAGIC) 50135446Strhodes 51218384Sdougb#ifndef DNS_TSIG_MAXGENERATEDKEYS 52218384Sdougb#define DNS_TSIG_MAXGENERATEDKEYS 4096 53218384Sdougb#endif 54218384Sdougb 55135446Strhodes#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR) 56135446Strhodes#define algname_is_allocated(algname) \ 57135446Strhodes ((algname) != dns_tsig_hmacmd5_name && \ 58170222Sdougb (algname) != dns_tsig_hmacsha1_name && \ 59170222Sdougb (algname) != dns_tsig_hmacsha224_name && \ 60170222Sdougb (algname) != dns_tsig_hmacsha256_name && \ 61170222Sdougb (algname) != dns_tsig_hmacsha384_name && \ 62170222Sdougb (algname) != dns_tsig_hmacsha512_name && \ 63135446Strhodes (algname) != dns_tsig_gssapi_name && \ 64135446Strhodes (algname) != dns_tsig_gssapims_name) 65135446Strhodes 66135446Strhodes#define BADTIMELEN 6 67135446Strhodes 68135446Strhodesstatic unsigned char hmacmd5_ndata[] = "\010hmac-md5\007sig-alg\003reg\003int"; 69135446Strhodesstatic unsigned char hmacmd5_offsets[] = { 0, 9, 17, 21, 25 }; 70135446Strhodes 71135446Strhodesstatic dns_name_t hmacmd5 = { 72135446Strhodes DNS_NAME_MAGIC, 73135446Strhodes hmacmd5_ndata, 26, 5, 74135446Strhodes DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 75135446Strhodes hmacmd5_offsets, NULL, 76135446Strhodes {(void *)-1, (void *)-1}, 77135446Strhodes {NULL, NULL} 78135446Strhodes}; 79135446Strhodes 80135446Strhodesdns_name_t *dns_tsig_hmacmd5_name = &hmacmd5; 81135446Strhodes 82135446Strhodesstatic unsigned char gsstsig_ndata[] = "\010gss-tsig"; 83135446Strhodesstatic unsigned char gsstsig_offsets[] = { 0, 9 }; 84135446Strhodesstatic dns_name_t gsstsig = { 85135446Strhodes DNS_NAME_MAGIC, 86135446Strhodes gsstsig_ndata, 10, 2, 87135446Strhodes DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 88135446Strhodes gsstsig_offsets, NULL, 89135446Strhodes {(void *)-1, (void *)-1}, 90135446Strhodes {NULL, NULL} 91135446Strhodes}; 92135446StrhodesLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig; 93135446Strhodes 94193149Sdougb/* 95193149Sdougb * Since Microsoft doesn't follow its own standard, we will use this 96193149Sdougb * alternate name as a second guess. 97193149Sdougb */ 98135446Strhodesstatic unsigned char gsstsigms_ndata[] = "\003gss\011microsoft\003com"; 99135446Strhodesstatic unsigned char gsstsigms_offsets[] = { 0, 4, 14, 18 }; 100135446Strhodesstatic dns_name_t gsstsigms = { 101135446Strhodes DNS_NAME_MAGIC, 102135446Strhodes gsstsigms_ndata, 19, 4, 103135446Strhodes DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 104135446Strhodes gsstsigms_offsets, NULL, 105135446Strhodes {(void *)-1, (void *)-1}, 106135446Strhodes {NULL, NULL} 107135446Strhodes}; 108135446StrhodesLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapims_name = &gsstsigms; 109135446Strhodes 110170222Sdougbstatic unsigned char hmacsha1_ndata[] = "\011hmac-sha1"; 111170222Sdougbstatic unsigned char hmacsha1_offsets[] = { 0, 10 }; 112170222Sdougb 113170222Sdougbstatic dns_name_t hmacsha1 = { 114186462Sdougb DNS_NAME_MAGIC, 115186462Sdougb hmacsha1_ndata, 11, 2, 116186462Sdougb DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 117186462Sdougb hmacsha1_offsets, NULL, 118186462Sdougb {(void *)-1, (void *)-1}, 119186462Sdougb {NULL, NULL} 120170222Sdougb}; 121170222Sdougb 122170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha1_name = &hmacsha1; 123170222Sdougb 124170222Sdougbstatic unsigned char hmacsha224_ndata[] = "\013hmac-sha224"; 125170222Sdougbstatic unsigned char hmacsha224_offsets[] = { 0, 12 }; 126170222Sdougb 127170222Sdougbstatic dns_name_t hmacsha224 = { 128186462Sdougb DNS_NAME_MAGIC, 129186462Sdougb hmacsha224_ndata, 13, 2, 130186462Sdougb DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 131186462Sdougb hmacsha224_offsets, NULL, 132186462Sdougb {(void *)-1, (void *)-1}, 133186462Sdougb {NULL, NULL} 134170222Sdougb}; 135170222Sdougb 136170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha224_name = &hmacsha224; 137170222Sdougb 138170222Sdougbstatic unsigned char hmacsha256_ndata[] = "\013hmac-sha256"; 139170222Sdougbstatic unsigned char hmacsha256_offsets[] = { 0, 12 }; 140170222Sdougb 141170222Sdougbstatic dns_name_t hmacsha256 = { 142186462Sdougb DNS_NAME_MAGIC, 143186462Sdougb hmacsha256_ndata, 13, 2, 144186462Sdougb DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 145186462Sdougb hmacsha256_offsets, NULL, 146186462Sdougb {(void *)-1, (void *)-1}, 147186462Sdougb {NULL, NULL} 148170222Sdougb}; 149170222Sdougb 150170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha256_name = &hmacsha256; 151170222Sdougb 152170222Sdougbstatic unsigned char hmacsha384_ndata[] = "\013hmac-sha384"; 153170222Sdougbstatic unsigned char hmacsha384_offsets[] = { 0, 12 }; 154170222Sdougb 155170222Sdougbstatic dns_name_t hmacsha384 = { 156186462Sdougb DNS_NAME_MAGIC, 157186462Sdougb hmacsha384_ndata, 13, 2, 158186462Sdougb DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 159186462Sdougb hmacsha384_offsets, NULL, 160186462Sdougb {(void *)-1, (void *)-1}, 161186462Sdougb {NULL, NULL} 162170222Sdougb}; 163170222Sdougb 164170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha384_name = &hmacsha384; 165170222Sdougb 166170222Sdougbstatic unsigned char hmacsha512_ndata[] = "\013hmac-sha512"; 167170222Sdougbstatic unsigned char hmacsha512_offsets[] = { 0, 12 }; 168170222Sdougb 169170222Sdougbstatic dns_name_t hmacsha512 = { 170186462Sdougb DNS_NAME_MAGIC, 171186462Sdougb hmacsha512_ndata, 13, 2, 172186462Sdougb DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, 173186462Sdougb hmacsha512_offsets, NULL, 174186462Sdougb {(void *)-1, (void *)-1}, 175186462Sdougb {NULL, NULL} 176170222Sdougb}; 177170222Sdougb 178170222SdougbLIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_hmacsha512_name = &hmacsha512; 179170222Sdougb 180135446Strhodesstatic isc_result_t 181135446Strhodestsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg); 182135446Strhodes 183135446Strhodesstatic void 184135446Strhodestsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) 185135446Strhodes ISC_FORMAT_PRINTF(3, 4); 186135446Strhodes 187135446Strhodesstatic void 188193149Sdougbcleanup_ring(dns_tsig_keyring_t *ring); 189193149Sdougbstatic void 190193149Sdougbtsigkey_free(dns_tsigkey_t *key); 191193149Sdougb 192193149Sdougbstatic void 193135446Strhodestsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) { 194135446Strhodes va_list ap; 195135446Strhodes char message[4096]; 196135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 197193149Sdougb char creatorstr[DNS_NAME_FORMATSIZE]; 198135446Strhodes 199135446Strhodes if (isc_log_wouldlog(dns_lctx, level) == ISC_FALSE) 200135446Strhodes return; 201135446Strhodes if (key != NULL) 202135446Strhodes dns_name_format(&key->name, namestr, sizeof(namestr)); 203135446Strhodes else 204135446Strhodes strcpy(namestr, "<null>"); 205193149Sdougb 206224092Sdougb if (key != NULL && key->generated && key->creator) 207193149Sdougb dns_name_format(key->creator, creatorstr, sizeof(creatorstr)); 208224092Sdougb else 209224092Sdougb strcpy(creatorstr, "<null>"); 210193149Sdougb 211135446Strhodes va_start(ap, fmt); 212135446Strhodes vsnprintf(message, sizeof(message), fmt, ap); 213135446Strhodes va_end(ap); 214193149Sdougb if (key != NULL && key->generated) 215193149Sdougb isc_log_write(dns_lctx, 216193149Sdougb DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG, 217193149Sdougb level, "tsig key '%s' (%s): %s", 218193149Sdougb namestr, creatorstr, message); 219193149Sdougb else 220193149Sdougb isc_log_write(dns_lctx, 221193149Sdougb DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_TSIG, 222193149Sdougb level, "tsig key '%s': %s", namestr, message); 223135446Strhodes} 224135446Strhodes 225224092Sdougbstatic void 226224092Sdougbremove_fromring(dns_tsigkey_t *tkey) { 227224092Sdougb if (tkey->generated) { 228224092Sdougb ISC_LIST_UNLINK(tkey->ring->lru, tkey, link); 229224092Sdougb tkey->ring->generated--; 230224092Sdougb } 231224092Sdougb (void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, ISC_FALSE); 232224092Sdougb} 233224092Sdougb 234224092Sdougbstatic void 235224092Sdougbadjust_lru(dns_tsigkey_t *tkey) { 236224092Sdougb if (tkey->generated) { 237224092Sdougb RWLOCK(&tkey->ring->lock, isc_rwlocktype_write); 238224092Sdougb /* 239224092Sdougb * We may have been removed from the LRU list between 240224092Sdougb * removing the read lock and aquiring the write lock. 241224092Sdougb */ 242254402Serwin if (ISC_LINK_LINKED(tkey, link) && 243254402Serwin tkey->ring->lru.tail != tkey) 244254402Serwin { 245224092Sdougb ISC_LIST_UNLINK(tkey->ring->lru, tkey, link); 246224092Sdougb ISC_LIST_APPEND(tkey->ring->lru, tkey, link); 247224092Sdougb } 248224092Sdougb RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write); 249224092Sdougb } 250224092Sdougb} 251224092Sdougb 252224092Sdougb/* 253224092Sdougb * A supplemental routine just to add a key to ring. Note that reference 254224092Sdougb * counter should be counted separately because we may be adding the key 255224092Sdougb * as part of creation of the key, in which case the reference counter was 256224092Sdougb * already initialized. Also note we don't need RWLOCK for the reference 257224092Sdougb * counter: it's protected by a separate lock. 258224092Sdougb */ 259224092Sdougbstatic isc_result_t 260224092Sdougbkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name, 261224092Sdougb dns_tsigkey_t *tkey) 262224092Sdougb{ 263224092Sdougb isc_result_t result; 264224092Sdougb 265224092Sdougb RWLOCK(&ring->lock, isc_rwlocktype_write); 266224092Sdougb ring->writecount++; 267224092Sdougb 268224092Sdougb /* 269224092Sdougb * Do on the fly cleaning. Find some nodes we might not 270224092Sdougb * want around any more. 271224092Sdougb */ 272224092Sdougb if (ring->writecount > 10) { 273224092Sdougb cleanup_ring(ring); 274224092Sdougb ring->writecount = 0; 275224092Sdougb } 276224092Sdougb 277224092Sdougb result = dns_rbt_addname(ring->keys, name, tkey); 278224092Sdougb if (tkey->generated) { 279224092Sdougb /* 280224092Sdougb * Add the new key to the LRU list and remove the least 281224092Sdougb * recently used key if there are too many keys on the list. 282224092Sdougb */ 283224092Sdougb ISC_LIST_INITANDAPPEND(ring->lru, tkey, link); 284224092Sdougb if (ring->generated++ > ring->maxgenerated) 285224092Sdougb remove_fromring(ISC_LIST_HEAD(ring->lru)); 286224092Sdougb } 287224092Sdougb RWUNLOCK(&ring->lock, isc_rwlocktype_write); 288224092Sdougb 289224092Sdougb return (result); 290224092Sdougb} 291224092Sdougb 292135446Strhodesisc_result_t 293135446Strhodesdns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm, 294135446Strhodes dst_key_t *dstkey, isc_boolean_t generated, 295135446Strhodes dns_name_t *creator, isc_stdtime_t inception, 296135446Strhodes isc_stdtime_t expire, isc_mem_t *mctx, 297135446Strhodes dns_tsig_keyring_t *ring, dns_tsigkey_t **key) 298135446Strhodes{ 299135446Strhodes dns_tsigkey_t *tkey; 300135446Strhodes isc_result_t ret; 301135446Strhodes unsigned int refs = 0; 302135446Strhodes 303135446Strhodes REQUIRE(key == NULL || *key == NULL); 304135446Strhodes REQUIRE(name != NULL); 305135446Strhodes REQUIRE(algorithm != NULL); 306135446Strhodes REQUIRE(mctx != NULL); 307170222Sdougb REQUIRE(key != NULL || ring != NULL); 308135446Strhodes 309135446Strhodes tkey = (dns_tsigkey_t *) isc_mem_get(mctx, sizeof(dns_tsigkey_t)); 310135446Strhodes if (tkey == NULL) 311135446Strhodes return (ISC_R_NOMEMORY); 312135446Strhodes 313135446Strhodes dns_name_init(&tkey->name, NULL); 314135446Strhodes ret = dns_name_dup(name, mctx, &tkey->name); 315135446Strhodes if (ret != ISC_R_SUCCESS) 316135446Strhodes goto cleanup_key; 317135446Strhodes (void)dns_name_downcase(&tkey->name, &tkey->name, NULL); 318135446Strhodes 319135446Strhodes if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) { 320135446Strhodes tkey->algorithm = DNS_TSIG_HMACMD5_NAME; 321135446Strhodes if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACMD5) { 322135446Strhodes ret = DNS_R_BADALG; 323135446Strhodes goto cleanup_name; 324135446Strhodes } 325170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) { 326170222Sdougb tkey->algorithm = DNS_TSIG_HMACSHA1_NAME; 327170222Sdougb if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_HMACSHA1) { 328170222Sdougb ret = DNS_R_BADALG; 329170222Sdougb goto cleanup_name; 330170222Sdougb } 331170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) { 332170222Sdougb tkey->algorithm = DNS_TSIG_HMACSHA224_NAME; 333170222Sdougb if (dstkey != NULL && 334170222Sdougb dst_key_alg(dstkey) != DST_ALG_HMACSHA224) { 335170222Sdougb ret = DNS_R_BADALG; 336170222Sdougb goto cleanup_name; 337170222Sdougb } 338170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) { 339170222Sdougb tkey->algorithm = DNS_TSIG_HMACSHA256_NAME; 340170222Sdougb if (dstkey != NULL && 341170222Sdougb dst_key_alg(dstkey) != DST_ALG_HMACSHA256) { 342170222Sdougb ret = DNS_R_BADALG; 343170222Sdougb goto cleanup_name; 344170222Sdougb } 345170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) { 346170222Sdougb tkey->algorithm = DNS_TSIG_HMACSHA384_NAME; 347170222Sdougb if (dstkey != NULL && 348170222Sdougb dst_key_alg(dstkey) != DST_ALG_HMACSHA384) { 349170222Sdougb ret = DNS_R_BADALG; 350170222Sdougb goto cleanup_name; 351170222Sdougb } 352170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) { 353170222Sdougb tkey->algorithm = DNS_TSIG_HMACSHA512_NAME; 354170222Sdougb if (dstkey != NULL && 355170222Sdougb dst_key_alg(dstkey) != DST_ALG_HMACSHA512) { 356170222Sdougb ret = DNS_R_BADALG; 357170222Sdougb goto cleanup_name; 358170222Sdougb } 359135446Strhodes } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) { 360135446Strhodes tkey->algorithm = DNS_TSIG_GSSAPI_NAME; 361135446Strhodes if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) { 362135446Strhodes ret = DNS_R_BADALG; 363135446Strhodes goto cleanup_name; 364135446Strhodes } 365135446Strhodes } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) { 366135446Strhodes tkey->algorithm = DNS_TSIG_GSSAPIMS_NAME; 367135446Strhodes if (dstkey != NULL && dst_key_alg(dstkey) != DST_ALG_GSSAPI) { 368135446Strhodes ret = DNS_R_BADALG; 369135446Strhodes goto cleanup_name; 370135446Strhodes } 371135446Strhodes } else { 372153816Sdougb if (dstkey != NULL) { 373135446Strhodes ret = DNS_R_BADALG; 374135446Strhodes goto cleanup_name; 375135446Strhodes } 376135446Strhodes tkey->algorithm = isc_mem_get(mctx, sizeof(dns_name_t)); 377135446Strhodes if (tkey->algorithm == NULL) { 378135446Strhodes ret = ISC_R_NOMEMORY; 379135446Strhodes goto cleanup_name; 380135446Strhodes } 381135446Strhodes dns_name_init(tkey->algorithm, NULL); 382135446Strhodes ret = dns_name_dup(algorithm, mctx, tkey->algorithm); 383135446Strhodes if (ret != ISC_R_SUCCESS) 384135446Strhodes goto cleanup_algorithm; 385135446Strhodes (void)dns_name_downcase(tkey->algorithm, tkey->algorithm, 386135446Strhodes NULL); 387135446Strhodes } 388135446Strhodes 389135446Strhodes if (creator != NULL) { 390135446Strhodes tkey->creator = isc_mem_get(mctx, sizeof(dns_name_t)); 391135446Strhodes if (tkey->creator == NULL) { 392135446Strhodes ret = ISC_R_NOMEMORY; 393135446Strhodes goto cleanup_algorithm; 394135446Strhodes } 395135446Strhodes dns_name_init(tkey->creator, NULL); 396135446Strhodes ret = dns_name_dup(creator, mctx, tkey->creator); 397135446Strhodes if (ret != ISC_R_SUCCESS) { 398135446Strhodes isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t)); 399135446Strhodes goto cleanup_algorithm; 400135446Strhodes } 401135446Strhodes } else 402135446Strhodes tkey->creator = NULL; 403135446Strhodes 404218384Sdougb tkey->key = NULL; 405218384Sdougb if (dstkey != NULL) 406218384Sdougb dst_key_attach(dstkey, &tkey->key); 407135446Strhodes tkey->ring = ring; 408135446Strhodes 409170222Sdougb if (key != NULL) 410224092Sdougb refs = 1; 411170222Sdougb if (ring != NULL) 412170222Sdougb refs++; 413170222Sdougb ret = isc_refcount_init(&tkey->refs, refs); 414170222Sdougb if (ret != ISC_R_SUCCESS) 415170222Sdougb goto cleanup_creator; 416170222Sdougb 417170222Sdougb tkey->generated = generated; 418170222Sdougb tkey->inception = inception; 419170222Sdougb tkey->expire = expire; 420186462Sdougb tkey->mctx = NULL; 421186462Sdougb isc_mem_attach(mctx, &tkey->mctx); 422170222Sdougb 423170222Sdougb tkey->magic = TSIG_MAGIC; 424170222Sdougb 425135446Strhodes if (ring != NULL) { 426224092Sdougb ret = keyring_add(ring, name, tkey); 427224092Sdougb if (ret != ISC_R_SUCCESS) 428170222Sdougb goto cleanup_refs; 429135446Strhodes } 430135446Strhodes 431193149Sdougb /* 432193149Sdougb * Ignore this if it's a GSS key, since the key size is meaningless. 433193149Sdougb */ 434193149Sdougb if (dstkey != NULL && dst_key_size(dstkey) < 64 && 435193149Sdougb !dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME) && 436193149Sdougb !dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) { 437135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 438135446Strhodes dns_name_format(name, namestr, sizeof(namestr)); 439135446Strhodes isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, 440135446Strhodes DNS_LOGMODULE_TSIG, ISC_LOG_INFO, 441135446Strhodes "the key '%s' is too short to be secure", 442135446Strhodes namestr); 443135446Strhodes } 444224092Sdougb 445135446Strhodes if (key != NULL) 446135446Strhodes *key = tkey; 447135446Strhodes 448135446Strhodes return (ISC_R_SUCCESS); 449135446Strhodes 450170222Sdougb cleanup_refs: 451170222Sdougb tkey->magic = 0; 452170222Sdougb while (refs-- > 0) 453170222Sdougb isc_refcount_decrement(&tkey->refs, NULL); 454170222Sdougb isc_refcount_destroy(&tkey->refs); 455170222Sdougb cleanup_creator: 456218384Sdougb if (tkey->key != NULL) 457218384Sdougb dst_key_free(&tkey->key); 458170222Sdougb if (tkey->creator != NULL) { 459170222Sdougb dns_name_free(tkey->creator, mctx); 460170222Sdougb isc_mem_put(mctx, tkey->creator, sizeof(dns_name_t)); 461170222Sdougb } 462135446Strhodes cleanup_algorithm: 463135446Strhodes if (algname_is_allocated(tkey->algorithm)) { 464135446Strhodes if (dns_name_dynamic(tkey->algorithm)) 465135446Strhodes dns_name_free(tkey->algorithm, mctx); 466135446Strhodes isc_mem_put(mctx, tkey->algorithm, sizeof(dns_name_t)); 467135446Strhodes } 468135446Strhodes cleanup_name: 469135446Strhodes dns_name_free(&tkey->name, mctx); 470135446Strhodes cleanup_key: 471135446Strhodes isc_mem_put(mctx, tkey, sizeof(dns_tsigkey_t)); 472135446Strhodes 473135446Strhodes return (ret); 474135446Strhodes} 475135446Strhodes 476193149Sdougb/* 477193149Sdougb * Find a few nodes to destroy if possible. 478193149Sdougb */ 479193149Sdougbstatic void 480193149Sdougbcleanup_ring(dns_tsig_keyring_t *ring) 481193149Sdougb{ 482193149Sdougb isc_result_t result; 483193149Sdougb dns_rbtnodechain_t chain; 484193149Sdougb dns_name_t foundname; 485193149Sdougb dns_fixedname_t fixedorigin; 486193149Sdougb dns_name_t *origin; 487193149Sdougb isc_stdtime_t now; 488193149Sdougb dns_rbtnode_t *node; 489193149Sdougb dns_tsigkey_t *tkey; 490193149Sdougb 491193149Sdougb /* 492193149Sdougb * Start up a new iterator each time. 493193149Sdougb */ 494193149Sdougb isc_stdtime_get(&now); 495193149Sdougb dns_name_init(&foundname, NULL); 496193149Sdougb dns_fixedname_init(&fixedorigin); 497193149Sdougb origin = dns_fixedname_name(&fixedorigin); 498193149Sdougb 499193149Sdougb again: 500193149Sdougb dns_rbtnodechain_init(&chain, ring->mctx); 501193149Sdougb result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, 502193149Sdougb origin); 503193149Sdougb if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { 504193149Sdougb dns_rbtnodechain_invalidate(&chain); 505193149Sdougb return; 506193149Sdougb } 507193149Sdougb 508193149Sdougb for (;;) { 509193149Sdougb node = NULL; 510193149Sdougb dns_rbtnodechain_current(&chain, &foundname, origin, &node); 511193149Sdougb tkey = node->data; 512193149Sdougb if (tkey != NULL) { 513193149Sdougb if (tkey->generated 514193149Sdougb && isc_refcount_current(&tkey->refs) == 1 515193149Sdougb && tkey->inception != tkey->expire 516193149Sdougb && tkey->expire < now) { 517193149Sdougb tsig_log(tkey, 2, "tsig expire: deleting"); 518193149Sdougb /* delete the key */ 519193149Sdougb dns_rbtnodechain_invalidate(&chain); 520218384Sdougb remove_fromring(tkey); 521193149Sdougb goto again; 522193149Sdougb } 523193149Sdougb } 524193149Sdougb result = dns_rbtnodechain_next(&chain, &foundname, 525193149Sdougb origin); 526193149Sdougb if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { 527193149Sdougb dns_rbtnodechain_invalidate(&chain); 528193149Sdougb return; 529193149Sdougb } 530193149Sdougb } 531193149Sdougb} 532193149Sdougb 533224092Sdougbstatic void 534224092Sdougbdestroyring(dns_tsig_keyring_t *ring) { 535224092Sdougb dns_rbt_destroy(&ring->keys); 536224092Sdougb isc_rwlock_destroy(&ring->lock); 537224092Sdougb isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t)); 538224092Sdougb} 539224092Sdougb 540224092Sdougbstatic unsigned int 541224092Sdougbdst_alg_fromname(dns_name_t *algorithm) { 542224092Sdougb if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) { 543224092Sdougb return (DST_ALG_HMACMD5); 544224092Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) { 545224092Sdougb return (DST_ALG_HMACSHA1); 546224092Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) { 547224092Sdougb return (DST_ALG_HMACSHA224); 548224092Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) { 549224092Sdougb return (DST_ALG_HMACSHA256); 550224092Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) { 551224092Sdougb return (DST_ALG_HMACSHA384); 552224092Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) { 553224092Sdougb return (DST_ALG_HMACSHA512); 554224092Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) { 555224092Sdougb return (DST_ALG_GSSAPI); 556224092Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) { 557224092Sdougb return (DST_ALG_GSSAPI); 558224092Sdougb } else 559224092Sdougb return (0); 560224092Sdougb} 561224092Sdougb 562224092Sdougbstatic isc_result_t 563224092Sdougbrestore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) { 564224092Sdougb dst_key_t *dstkey = NULL; 565224092Sdougb char namestr[1024]; 566224092Sdougb char creatorstr[1024]; 567224092Sdougb char algorithmstr[1024]; 568224092Sdougb char keystr[4096]; 569224092Sdougb unsigned int inception, expire; 570224092Sdougb int n; 571224092Sdougb isc_buffer_t b; 572224092Sdougb dns_name_t *name, *creator, *algorithm; 573224092Sdougb dns_fixedname_t fname, fcreator, falgorithm; 574224092Sdougb isc_result_t result; 575224092Sdougb unsigned int dstalg; 576224092Sdougb 577224092Sdougb n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr, 578224092Sdougb creatorstr, &inception, &expire, algorithmstr, keystr); 579224092Sdougb if (n == EOF) 580224092Sdougb return (ISC_R_NOMORE); 581224092Sdougb if (n != 6) 582224092Sdougb return (ISC_R_FAILURE); 583224092Sdougb 584224092Sdougb if (isc_serial_lt(expire, now)) 585224092Sdougb return (DNS_R_EXPIRED); 586224092Sdougb 587224092Sdougb dns_fixedname_init(&fname); 588224092Sdougb name = dns_fixedname_name(&fname); 589224092Sdougb isc_buffer_init(&b, namestr, strlen(namestr)); 590224092Sdougb isc_buffer_add(&b, strlen(namestr)); 591224092Sdougb result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); 592224092Sdougb if (result != ISC_R_SUCCESS) 593224092Sdougb return (result); 594224092Sdougb 595224092Sdougb dns_fixedname_init(&fcreator); 596224092Sdougb creator = dns_fixedname_name(&fcreator); 597224092Sdougb isc_buffer_init(&b, creatorstr, strlen(creatorstr)); 598224092Sdougb isc_buffer_add(&b, strlen(creatorstr)); 599224092Sdougb result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL); 600224092Sdougb if (result != ISC_R_SUCCESS) 601224092Sdougb return (result); 602224092Sdougb 603224092Sdougb dns_fixedname_init(&falgorithm); 604224092Sdougb algorithm = dns_fixedname_name(&falgorithm); 605224092Sdougb isc_buffer_init(&b, algorithmstr, strlen(algorithmstr)); 606224092Sdougb isc_buffer_add(&b, strlen(algorithmstr)); 607224092Sdougb result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL); 608224092Sdougb if (result != ISC_R_SUCCESS) 609224092Sdougb return (result); 610224092Sdougb 611224092Sdougb dstalg = dst_alg_fromname(algorithm); 612224092Sdougb if (dstalg == 0) 613224092Sdougb return (DNS_R_BADALG); 614224092Sdougb 615224092Sdougb result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY, 616224092Sdougb DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, 617224092Sdougb ring->mctx, keystr, &dstkey); 618224092Sdougb if (result != ISC_R_SUCCESS) 619224092Sdougb return (result); 620224092Sdougb 621224092Sdougb result = dns_tsigkey_createfromkey(name, algorithm, dstkey, 622224092Sdougb ISC_TRUE, creator, inception, 623224092Sdougb expire, ring->mctx, ring, NULL); 624225361Sdougb if (dstkey != NULL) 625224092Sdougb dst_key_free(&dstkey); 626224092Sdougb return (result); 627224092Sdougb} 628224092Sdougb 629224092Sdougbstatic void 630254402Serwindump_key(dns_tsigkey_t *tkey, FILE *fp) { 631224092Sdougb char *buffer = NULL; 632224092Sdougb int length = 0; 633224092Sdougb char namestr[DNS_NAME_FORMATSIZE]; 634224092Sdougb char creatorstr[DNS_NAME_FORMATSIZE]; 635224092Sdougb char algorithmstr[DNS_NAME_FORMATSIZE]; 636224092Sdougb isc_result_t result; 637224092Sdougb 638254402Serwin REQUIRE(tkey != NULL); 639254402Serwin REQUIRE(fp != NULL); 640254402Serwin 641224092Sdougb dns_name_format(&tkey->name, namestr, sizeof(namestr)); 642224092Sdougb dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr)); 643224092Sdougb dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr)); 644224092Sdougb result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length); 645224092Sdougb if (result == ISC_R_SUCCESS) 646224092Sdougb fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr, 647224092Sdougb tkey->inception, tkey->expire, algorithmstr, 648224092Sdougb length, buffer); 649224092Sdougb if (buffer != NULL) 650224092Sdougb isc_mem_put(tkey->mctx, buffer, length); 651224092Sdougb} 652224092Sdougb 653135446Strhodesisc_result_t 654224092Sdougbdns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) { 655224092Sdougb isc_result_t result; 656224092Sdougb dns_rbtnodechain_t chain; 657224092Sdougb dns_name_t foundname; 658224092Sdougb dns_fixedname_t fixedorigin; 659224092Sdougb dns_name_t *origin; 660224092Sdougb isc_stdtime_t now; 661224092Sdougb dns_rbtnode_t *node; 662224092Sdougb dns_tsigkey_t *tkey; 663224092Sdougb dns_tsig_keyring_t *ring; 664224092Sdougb unsigned int references; 665224092Sdougb 666224092Sdougb REQUIRE(ringp != NULL && *ringp != NULL); 667224092Sdougb 668224092Sdougb ring = *ringp; 669224092Sdougb *ringp = NULL; 670224092Sdougb 671224092Sdougb RWLOCK(&ring->lock, isc_rwlocktype_write); 672224092Sdougb INSIST(ring->references > 0); 673224092Sdougb ring->references--; 674224092Sdougb references = ring->references; 675224092Sdougb RWUNLOCK(&ring->lock, isc_rwlocktype_write); 676224092Sdougb 677224092Sdougb if (references != 0) 678224092Sdougb return (DNS_R_CONTINUE); 679224092Sdougb 680224092Sdougb isc_stdtime_get(&now); 681224092Sdougb dns_name_init(&foundname, NULL); 682224092Sdougb dns_fixedname_init(&fixedorigin); 683224092Sdougb origin = dns_fixedname_name(&fixedorigin); 684224092Sdougb dns_rbtnodechain_init(&chain, ring->mctx); 685224092Sdougb result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, 686224092Sdougb origin); 687224092Sdougb if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { 688224092Sdougb dns_rbtnodechain_invalidate(&chain); 689224092Sdougb goto destroy; 690224092Sdougb } 691224092Sdougb 692224092Sdougb for (;;) { 693224092Sdougb node = NULL; 694224092Sdougb dns_rbtnodechain_current(&chain, &foundname, origin, &node); 695224092Sdougb tkey = node->data; 696224092Sdougb if (tkey != NULL && tkey->generated && tkey->expire >= now) 697224092Sdougb dump_key(tkey, fp); 698224092Sdougb result = dns_rbtnodechain_next(&chain, &foundname, 699224092Sdougb origin); 700224092Sdougb if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { 701224092Sdougb dns_rbtnodechain_invalidate(&chain); 702224092Sdougb if (result == ISC_R_NOMORE) 703224092Sdougb result = ISC_R_SUCCESS; 704224092Sdougb goto destroy; 705224092Sdougb } 706224092Sdougb } 707224092Sdougb 708224092Sdougb destroy: 709224092Sdougb destroyring(ring); 710224092Sdougb return (result); 711224092Sdougb} 712224092Sdougb 713224092Sdougbisc_result_t 714135446Strhodesdns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm, 715135446Strhodes unsigned char *secret, int length, isc_boolean_t generated, 716135446Strhodes dns_name_t *creator, isc_stdtime_t inception, 717135446Strhodes isc_stdtime_t expire, isc_mem_t *mctx, 718135446Strhodes dns_tsig_keyring_t *ring, dns_tsigkey_t **key) 719135446Strhodes{ 720135446Strhodes dst_key_t *dstkey = NULL; 721135446Strhodes isc_result_t result; 722135446Strhodes 723135446Strhodes REQUIRE(length >= 0); 724135446Strhodes if (length > 0) 725135446Strhodes REQUIRE(secret != NULL); 726135446Strhodes 727170222Sdougb if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) { 728170222Sdougb if (secret != NULL) { 729170222Sdougb isc_buffer_t b; 730170222Sdougb 731170222Sdougb isc_buffer_init(&b, secret, length); 732170222Sdougb isc_buffer_add(&b, length); 733170222Sdougb result = dst_key_frombuffer(name, DST_ALG_HMACMD5, 734170222Sdougb DNS_KEYOWNER_ENTITY, 735170222Sdougb DNS_KEYPROTO_DNSSEC, 736170222Sdougb dns_rdataclass_in, 737170222Sdougb &b, mctx, &dstkey); 738170222Sdougb if (result != ISC_R_SUCCESS) 739170222Sdougb return (result); 740170222Sdougb } 741170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) { 742170222Sdougb if (secret != NULL) { 743170222Sdougb isc_buffer_t b; 744170222Sdougb 745170222Sdougb isc_buffer_init(&b, secret, length); 746170222Sdougb isc_buffer_add(&b, length); 747170222Sdougb result = dst_key_frombuffer(name, DST_ALG_HMACSHA1, 748170222Sdougb DNS_KEYOWNER_ENTITY, 749170222Sdougb DNS_KEYPROTO_DNSSEC, 750170222Sdougb dns_rdataclass_in, 751170222Sdougb &b, mctx, &dstkey); 752170222Sdougb if (result != ISC_R_SUCCESS) 753170222Sdougb return (result); 754170222Sdougb } 755170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) { 756170222Sdougb if (secret != NULL) { 757170222Sdougb isc_buffer_t b; 758170222Sdougb 759170222Sdougb isc_buffer_init(&b, secret, length); 760170222Sdougb isc_buffer_add(&b, length); 761170222Sdougb result = dst_key_frombuffer(name, DST_ALG_HMACSHA224, 762170222Sdougb DNS_KEYOWNER_ENTITY, 763170222Sdougb DNS_KEYPROTO_DNSSEC, 764170222Sdougb dns_rdataclass_in, 765170222Sdougb &b, mctx, &dstkey); 766170222Sdougb if (result != ISC_R_SUCCESS) 767170222Sdougb return (result); 768170222Sdougb } 769170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) { 770170222Sdougb if (secret != NULL) { 771170222Sdougb isc_buffer_t b; 772170222Sdougb 773170222Sdougb isc_buffer_init(&b, secret, length); 774170222Sdougb isc_buffer_add(&b, length); 775170222Sdougb result = dst_key_frombuffer(name, DST_ALG_HMACSHA256, 776170222Sdougb DNS_KEYOWNER_ENTITY, 777170222Sdougb DNS_KEYPROTO_DNSSEC, 778170222Sdougb dns_rdataclass_in, 779170222Sdougb &b, mctx, &dstkey); 780170222Sdougb if (result != ISC_R_SUCCESS) 781170222Sdougb return (result); 782170222Sdougb } 783170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) { 784170222Sdougb if (secret != NULL) { 785170222Sdougb isc_buffer_t b; 786170222Sdougb 787170222Sdougb isc_buffer_init(&b, secret, length); 788170222Sdougb isc_buffer_add(&b, length); 789170222Sdougb result = dst_key_frombuffer(name, DST_ALG_HMACSHA384, 790170222Sdougb DNS_KEYOWNER_ENTITY, 791170222Sdougb DNS_KEYPROTO_DNSSEC, 792170222Sdougb dns_rdataclass_in, 793170222Sdougb &b, mctx, &dstkey); 794170222Sdougb if (result != ISC_R_SUCCESS) 795170222Sdougb return (result); 796170222Sdougb } 797170222Sdougb } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) { 798170222Sdougb if (secret != NULL) { 799170222Sdougb isc_buffer_t b; 800170222Sdougb 801170222Sdougb isc_buffer_init(&b, secret, length); 802170222Sdougb isc_buffer_add(&b, length); 803170222Sdougb result = dst_key_frombuffer(name, DST_ALG_HMACSHA512, 804170222Sdougb DNS_KEYOWNER_ENTITY, 805170222Sdougb DNS_KEYPROTO_DNSSEC, 806170222Sdougb dns_rdataclass_in, 807170222Sdougb &b, mctx, &dstkey); 808170222Sdougb if (result != ISC_R_SUCCESS) 809170222Sdougb return (result); 810170222Sdougb } 811170222Sdougb } else if (length > 0) 812135446Strhodes return (DNS_R_BADALG); 813135446Strhodes 814135446Strhodes result = dns_tsigkey_createfromkey(name, algorithm, dstkey, 815135446Strhodes generated, creator, 816135446Strhodes inception, expire, mctx, ring, key); 817218384Sdougb if (dstkey != NULL) 818135446Strhodes dst_key_free(&dstkey); 819135446Strhodes return (result); 820135446Strhodes} 821135446Strhodes 822135446Strhodesvoid 823135446Strhodesdns_tsigkey_attach(dns_tsigkey_t *source, dns_tsigkey_t **targetp) { 824135446Strhodes REQUIRE(VALID_TSIG_KEY(source)); 825135446Strhodes REQUIRE(targetp != NULL && *targetp == NULL); 826135446Strhodes 827135446Strhodes isc_refcount_increment(&source->refs, NULL); 828135446Strhodes *targetp = source; 829135446Strhodes} 830135446Strhodes 831135446Strhodesstatic void 832135446Strhodestsigkey_free(dns_tsigkey_t *key) { 833135446Strhodes REQUIRE(VALID_TSIG_KEY(key)); 834135446Strhodes 835135446Strhodes key->magic = 0; 836135446Strhodes dns_name_free(&key->name, key->mctx); 837135446Strhodes if (algname_is_allocated(key->algorithm)) { 838135446Strhodes dns_name_free(key->algorithm, key->mctx); 839135446Strhodes isc_mem_put(key->mctx, key->algorithm, sizeof(dns_name_t)); 840135446Strhodes } 841135446Strhodes if (key->key != NULL) 842135446Strhodes dst_key_free(&key->key); 843135446Strhodes if (key->creator != NULL) { 844135446Strhodes dns_name_free(key->creator, key->mctx); 845135446Strhodes isc_mem_put(key->mctx, key->creator, sizeof(dns_name_t)); 846135446Strhodes } 847135446Strhodes isc_refcount_destroy(&key->refs); 848186462Sdougb isc_mem_putanddetach(&key->mctx, key, sizeof(dns_tsigkey_t)); 849135446Strhodes} 850135446Strhodes 851135446Strhodesvoid 852135446Strhodesdns_tsigkey_detach(dns_tsigkey_t **keyp) { 853135446Strhodes dns_tsigkey_t *key; 854135446Strhodes unsigned int refs; 855135446Strhodes 856135446Strhodes REQUIRE(keyp != NULL); 857135446Strhodes REQUIRE(VALID_TSIG_KEY(*keyp)); 858135446Strhodes 859135446Strhodes key = *keyp; 860135446Strhodes isc_refcount_decrement(&key->refs, &refs); 861135446Strhodes 862135446Strhodes if (refs == 0) 863135446Strhodes tsigkey_free(key); 864135446Strhodes 865135446Strhodes *keyp = NULL; 866135446Strhodes} 867135446Strhodes 868135446Strhodesvoid 869135446Strhodesdns_tsigkey_setdeleted(dns_tsigkey_t *key) { 870135446Strhodes REQUIRE(VALID_TSIG_KEY(key)); 871135446Strhodes REQUIRE(key->ring != NULL); 872135446Strhodes 873135446Strhodes RWLOCK(&key->ring->lock, isc_rwlocktype_write); 874218384Sdougb remove_fromring(key); 875135446Strhodes RWUNLOCK(&key->ring->lock, isc_rwlocktype_write); 876135446Strhodes} 877135446Strhodes 878135446Strhodesisc_result_t 879135446Strhodesdns_tsig_sign(dns_message_t *msg) { 880135446Strhodes dns_tsigkey_t *key; 881135446Strhodes dns_rdata_any_tsig_t tsig, querytsig; 882135446Strhodes unsigned char data[128]; 883135446Strhodes isc_buffer_t databuf, sigbuf; 884135446Strhodes isc_buffer_t *dynbuf; 885135446Strhodes dns_name_t *owner; 886165071Sdougb dns_rdata_t *rdata = NULL; 887135446Strhodes dns_rdatalist_t *datalist; 888135446Strhodes dns_rdataset_t *dataset; 889135446Strhodes isc_region_t r; 890135446Strhodes isc_stdtime_t now; 891135446Strhodes isc_mem_t *mctx; 892135446Strhodes dst_context_t *ctx = NULL; 893135446Strhodes isc_result_t ret; 894135446Strhodes unsigned char badtimedata[BADTIMELEN]; 895135446Strhodes unsigned int sigsize = 0; 896234010Sdougb isc_boolean_t response = is_response(msg); 897135446Strhodes 898135446Strhodes REQUIRE(msg != NULL); 899135446Strhodes REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg))); 900135446Strhodes 901135446Strhodes /* 902135446Strhodes * If this is a response, there should be a query tsig. 903135446Strhodes */ 904234010Sdougb if (response && msg->querytsig == NULL) 905135446Strhodes return (DNS_R_EXPECTEDTSIG); 906135446Strhodes 907135446Strhodes dynbuf = NULL; 908135446Strhodes 909135446Strhodes mctx = msg->mctx; 910135446Strhodes key = dns_message_gettsigkey(msg); 911135446Strhodes 912135446Strhodes tsig.mctx = mctx; 913135446Strhodes tsig.common.rdclass = dns_rdataclass_any; 914135446Strhodes tsig.common.rdtype = dns_rdatatype_tsig; 915135446Strhodes ISC_LINK_INIT(&tsig.common, link); 916135446Strhodes dns_name_init(&tsig.algorithm, NULL); 917135446Strhodes dns_name_clone(key->algorithm, &tsig.algorithm); 918135446Strhodes 919135446Strhodes isc_stdtime_get(&now); 920135446Strhodes tsig.timesigned = now + msg->timeadjust; 921135446Strhodes tsig.fudge = DNS_TSIG_FUDGE; 922135446Strhodes 923135446Strhodes tsig.originalid = msg->id; 924135446Strhodes 925135446Strhodes isc_buffer_init(&databuf, data, sizeof(data)); 926135446Strhodes 927234010Sdougb if (response) 928135446Strhodes tsig.error = msg->querytsigstatus; 929135446Strhodes else 930135446Strhodes tsig.error = dns_rcode_noerror; 931135446Strhodes 932135446Strhodes if (tsig.error != dns_tsigerror_badtime) { 933135446Strhodes tsig.otherlen = 0; 934135446Strhodes tsig.other = NULL; 935135446Strhodes } else { 936135446Strhodes isc_buffer_t otherbuf; 937135446Strhodes 938135446Strhodes tsig.otherlen = BADTIMELEN; 939135446Strhodes tsig.other = badtimedata; 940135446Strhodes isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen); 941193149Sdougb isc_buffer_putuint48(&otherbuf, tsig.timesigned); 942135446Strhodes } 943135446Strhodes 944135446Strhodes if (key->key != NULL && tsig.error != dns_tsigerror_badsig) { 945135446Strhodes unsigned char header[DNS_MESSAGE_HEADERLEN]; 946135446Strhodes isc_buffer_t headerbuf; 947170222Sdougb isc_uint16_t digestbits; 948135446Strhodes 949254402Serwin ret = dst_context_create2(key->key, mctx, 950254402Serwin DNS_LOGCATEGORY_DNSSEC, &ctx); 951135446Strhodes if (ret != ISC_R_SUCCESS) 952135446Strhodes return (ret); 953135446Strhodes 954135446Strhodes /* 955135446Strhodes * If this is a response, digest the query signature. 956135446Strhodes */ 957234010Sdougb if (response) { 958135446Strhodes dns_rdata_t querytsigrdata = DNS_RDATA_INIT; 959135446Strhodes 960135446Strhodes ret = dns_rdataset_first(msg->querytsig); 961135446Strhodes if (ret != ISC_R_SUCCESS) 962135446Strhodes goto cleanup_context; 963135446Strhodes dns_rdataset_current(msg->querytsig, &querytsigrdata); 964135446Strhodes ret = dns_rdata_tostruct(&querytsigrdata, &querytsig, 965135446Strhodes NULL); 966135446Strhodes if (ret != ISC_R_SUCCESS) 967135446Strhodes goto cleanup_context; 968135446Strhodes isc_buffer_putuint16(&databuf, querytsig.siglen); 969135446Strhodes if (isc_buffer_availablelength(&databuf) < 970193149Sdougb querytsig.siglen) { 971135446Strhodes ret = ISC_R_NOSPACE; 972135446Strhodes goto cleanup_context; 973135446Strhodes } 974135446Strhodes isc_buffer_putmem(&databuf, querytsig.signature, 975135446Strhodes querytsig.siglen); 976135446Strhodes isc_buffer_usedregion(&databuf, &r); 977135446Strhodes ret = dst_context_adddata(ctx, &r); 978135446Strhodes if (ret != ISC_R_SUCCESS) 979135446Strhodes goto cleanup_context; 980135446Strhodes } 981254402Serwin#if defined(__clang__) && \ 982254402Serwin ( __clang_major__ < 3 || \ 983254402Serwin (__clang_major__ == 3 && __clang_minor__ < 2) || \ 984254402Serwin (__clang_major__ == 4 && __clang_minor__ < 2)) 985254402Serwin /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */ 986254402Serwin else memset(&querytsig, 0, sizeof(querytsig)); 987254402Serwin#endif 988135446Strhodes 989135446Strhodes /* 990135446Strhodes * Digest the header. 991135446Strhodes */ 992135446Strhodes isc_buffer_init(&headerbuf, header, sizeof(header)); 993135446Strhodes dns_message_renderheader(msg, &headerbuf); 994135446Strhodes isc_buffer_usedregion(&headerbuf, &r); 995135446Strhodes ret = dst_context_adddata(ctx, &r); 996135446Strhodes if (ret != ISC_R_SUCCESS) 997135446Strhodes goto cleanup_context; 998135446Strhodes 999135446Strhodes /* 1000135446Strhodes * Digest the remainder of the message. 1001135446Strhodes */ 1002135446Strhodes isc_buffer_usedregion(msg->buffer, &r); 1003135446Strhodes isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); 1004135446Strhodes ret = dst_context_adddata(ctx, &r); 1005135446Strhodes if (ret != ISC_R_SUCCESS) 1006135446Strhodes goto cleanup_context; 1007135446Strhodes 1008135446Strhodes if (msg->tcp_continuation == 0) { 1009135446Strhodes /* 1010135446Strhodes * Digest the name, class, ttl, alg. 1011135446Strhodes */ 1012135446Strhodes dns_name_toregion(&key->name, &r); 1013135446Strhodes ret = dst_context_adddata(ctx, &r); 1014135446Strhodes if (ret != ISC_R_SUCCESS) 1015135446Strhodes goto cleanup_context; 1016135446Strhodes 1017135446Strhodes isc_buffer_clear(&databuf); 1018135446Strhodes isc_buffer_putuint16(&databuf, dns_rdataclass_any); 1019135446Strhodes isc_buffer_putuint32(&databuf, 0); /* ttl */ 1020135446Strhodes isc_buffer_usedregion(&databuf, &r); 1021135446Strhodes ret = dst_context_adddata(ctx, &r); 1022135446Strhodes if (ret != ISC_R_SUCCESS) 1023135446Strhodes goto cleanup_context; 1024135446Strhodes 1025135446Strhodes dns_name_toregion(&tsig.algorithm, &r); 1026135446Strhodes ret = dst_context_adddata(ctx, &r); 1027135446Strhodes if (ret != ISC_R_SUCCESS) 1028135446Strhodes goto cleanup_context; 1029135446Strhodes 1030135446Strhodes } 1031135446Strhodes /* Digest the timesigned and fudge */ 1032135446Strhodes isc_buffer_clear(&databuf); 1033234010Sdougb if (tsig.error == dns_tsigerror_badtime) { 1034234010Sdougb INSIST(response); 1035135446Strhodes tsig.timesigned = querytsig.timesigned; 1036234010Sdougb } 1037193149Sdougb isc_buffer_putuint48(&databuf, tsig.timesigned); 1038135446Strhodes isc_buffer_putuint16(&databuf, tsig.fudge); 1039135446Strhodes isc_buffer_usedregion(&databuf, &r); 1040135446Strhodes ret = dst_context_adddata(ctx, &r); 1041135446Strhodes if (ret != ISC_R_SUCCESS) 1042135446Strhodes goto cleanup_context; 1043135446Strhodes 1044135446Strhodes if (msg->tcp_continuation == 0) { 1045135446Strhodes /* 1046135446Strhodes * Digest the error and other data length. 1047135446Strhodes */ 1048135446Strhodes isc_buffer_clear(&databuf); 1049135446Strhodes isc_buffer_putuint16(&databuf, tsig.error); 1050135446Strhodes isc_buffer_putuint16(&databuf, tsig.otherlen); 1051135446Strhodes 1052135446Strhodes isc_buffer_usedregion(&databuf, &r); 1053135446Strhodes ret = dst_context_adddata(ctx, &r); 1054135446Strhodes if (ret != ISC_R_SUCCESS) 1055135446Strhodes goto cleanup_context; 1056135446Strhodes 1057135446Strhodes /* 1058234010Sdougb * Digest other data. 1059135446Strhodes */ 1060135446Strhodes if (tsig.otherlen > 0) { 1061135446Strhodes r.length = tsig.otherlen; 1062135446Strhodes r.base = tsig.other; 1063135446Strhodes ret = dst_context_adddata(ctx, &r); 1064135446Strhodes if (ret != ISC_R_SUCCESS) 1065135446Strhodes goto cleanup_context; 1066135446Strhodes } 1067135446Strhodes } 1068135446Strhodes 1069135446Strhodes ret = dst_key_sigsize(key->key, &sigsize); 1070135446Strhodes if (ret != ISC_R_SUCCESS) 1071135446Strhodes goto cleanup_context; 1072135446Strhodes tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize); 1073135446Strhodes if (tsig.signature == NULL) { 1074135446Strhodes ret = ISC_R_NOMEMORY; 1075135446Strhodes goto cleanup_context; 1076135446Strhodes } 1077135446Strhodes 1078135446Strhodes isc_buffer_init(&sigbuf, tsig.signature, sigsize); 1079135446Strhodes ret = dst_context_sign(ctx, &sigbuf); 1080135446Strhodes if (ret != ISC_R_SUCCESS) 1081135446Strhodes goto cleanup_signature; 1082135446Strhodes dst_context_destroy(&ctx); 1083170222Sdougb digestbits = dst_key_getbits(key->key); 1084170222Sdougb if (digestbits != 0) { 1085170222Sdougb unsigned int bytes = (digestbits + 1) / 8; 1086234010Sdougb if (response && bytes < querytsig.siglen) 1087170222Sdougb bytes = querytsig.siglen; 1088170222Sdougb if (bytes > isc_buffer_usedlength(&sigbuf)) 1089170222Sdougb bytes = isc_buffer_usedlength(&sigbuf); 1090170222Sdougb tsig.siglen = bytes; 1091170222Sdougb } else 1092170222Sdougb tsig.siglen = isc_buffer_usedlength(&sigbuf); 1093135446Strhodes } else { 1094135446Strhodes tsig.siglen = 0; 1095135446Strhodes tsig.signature = NULL; 1096135446Strhodes } 1097135446Strhodes 1098135446Strhodes ret = dns_message_gettemprdata(msg, &rdata); 1099135446Strhodes if (ret != ISC_R_SUCCESS) 1100135446Strhodes goto cleanup_signature; 1101135446Strhodes ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512); 1102135446Strhodes if (ret != ISC_R_SUCCESS) 1103165071Sdougb goto cleanup_rdata; 1104135446Strhodes ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any, 1105135446Strhodes dns_rdatatype_tsig, &tsig, dynbuf); 1106135446Strhodes if (ret != ISC_R_SUCCESS) 1107135446Strhodes goto cleanup_dynbuf; 1108135446Strhodes 1109135446Strhodes dns_message_takebuffer(msg, &dynbuf); 1110135446Strhodes 1111135446Strhodes if (tsig.signature != NULL) { 1112135446Strhodes isc_mem_put(mctx, tsig.signature, sigsize); 1113135446Strhodes tsig.signature = NULL; 1114135446Strhodes } 1115135446Strhodes 1116135446Strhodes owner = NULL; 1117135446Strhodes ret = dns_message_gettempname(msg, &owner); 1118135446Strhodes if (ret != ISC_R_SUCCESS) 1119165071Sdougb goto cleanup_rdata; 1120135446Strhodes dns_name_init(owner, NULL); 1121135446Strhodes ret = dns_name_dup(&key->name, msg->mctx, owner); 1122135446Strhodes if (ret != ISC_R_SUCCESS) 1123135446Strhodes goto cleanup_owner; 1124135446Strhodes 1125135446Strhodes datalist = NULL; 1126135446Strhodes ret = dns_message_gettemprdatalist(msg, &datalist); 1127135446Strhodes if (ret != ISC_R_SUCCESS) 1128135446Strhodes goto cleanup_owner; 1129165071Sdougb dataset = NULL; 1130165071Sdougb ret = dns_message_gettemprdataset(msg, &dataset); 1131165071Sdougb if (ret != ISC_R_SUCCESS) 1132165071Sdougb goto cleanup_rdatalist; 1133135446Strhodes datalist->rdclass = dns_rdataclass_any; 1134135446Strhodes datalist->type = dns_rdatatype_tsig; 1135135446Strhodes datalist->covers = 0; 1136135446Strhodes datalist->ttl = 0; 1137135446Strhodes ISC_LIST_INIT(datalist->rdata); 1138135446Strhodes ISC_LIST_APPEND(datalist->rdata, rdata, link); 1139135446Strhodes dns_rdataset_init(dataset); 1140135446Strhodes RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) 1141135446Strhodes == ISC_R_SUCCESS); 1142135446Strhodes msg->tsig = dataset; 1143135446Strhodes msg->tsigname = owner; 1144135446Strhodes 1145218384Sdougb /* Windows does not like the tsig name being compressed. */ 1146218384Sdougb msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; 1147218384Sdougb 1148135446Strhodes return (ISC_R_SUCCESS); 1149135446Strhodes 1150165071Sdougb cleanup_rdatalist: 1151165071Sdougb dns_message_puttemprdatalist(msg, &datalist); 1152165071Sdougb cleanup_owner: 1153165071Sdougb dns_message_puttempname(msg, &owner); 1154165071Sdougb goto cleanup_rdata; 1155165071Sdougb cleanup_dynbuf: 1156165071Sdougb isc_buffer_free(&dynbuf); 1157165071Sdougb cleanup_rdata: 1158165071Sdougb dns_message_puttemprdata(msg, &rdata); 1159165071Sdougb cleanup_signature: 1160135446Strhodes if (tsig.signature != NULL) 1161135446Strhodes isc_mem_put(mctx, tsig.signature, sigsize); 1162165071Sdougb cleanup_context: 1163135446Strhodes if (ctx != NULL) 1164135446Strhodes dst_context_destroy(&ctx); 1165135446Strhodes return (ret); 1166135446Strhodes} 1167135446Strhodes 1168135446Strhodesisc_result_t 1169135446Strhodesdns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, 1170135446Strhodes dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2) 1171135446Strhodes{ 1172135446Strhodes dns_rdata_any_tsig_t tsig, querytsig; 1173135446Strhodes isc_region_t r, source_r, header_r, sig_r; 1174135446Strhodes isc_buffer_t databuf; 1175135446Strhodes unsigned char data[32]; 1176135446Strhodes dns_name_t *keyname; 1177135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1178135446Strhodes isc_stdtime_t now; 1179135446Strhodes isc_result_t ret; 1180135446Strhodes dns_tsigkey_t *tsigkey; 1181135446Strhodes dst_key_t *key = NULL; 1182135446Strhodes unsigned char header[DNS_MESSAGE_HEADERLEN]; 1183135446Strhodes dst_context_t *ctx = NULL; 1184135446Strhodes isc_mem_t *mctx; 1185135446Strhodes isc_uint16_t addcount, id; 1186170222Sdougb unsigned int siglen; 1187170222Sdougb unsigned int alg; 1188234010Sdougb isc_boolean_t response; 1189135446Strhodes 1190135446Strhodes REQUIRE(source != NULL); 1191135446Strhodes REQUIRE(DNS_MESSAGE_VALID(msg)); 1192135446Strhodes tsigkey = dns_message_gettsigkey(msg); 1193234010Sdougb response = is_response(msg); 1194193149Sdougb 1195135446Strhodes REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey)); 1196135446Strhodes 1197135446Strhodes msg->verify_attempted = 1; 1198135446Strhodes 1199165071Sdougb if (msg->tcp_continuation) { 1200165071Sdougb if (tsigkey == NULL || msg->querytsig == NULL) 1201165071Sdougb return (DNS_R_UNEXPECTEDTSIG); 1202135446Strhodes return (tsig_verify_tcp(source, msg)); 1203165071Sdougb } 1204135446Strhodes 1205135446Strhodes /* 1206135446Strhodes * There should be a TSIG record... 1207135446Strhodes */ 1208135446Strhodes if (msg->tsig == NULL) 1209135446Strhodes return (DNS_R_EXPECTEDTSIG); 1210135446Strhodes 1211135446Strhodes /* 1212135446Strhodes * If this is a response and there's no key or query TSIG, there 1213135446Strhodes * shouldn't be one on the response. 1214135446Strhodes */ 1215234010Sdougb if (response && (tsigkey == NULL || msg->querytsig == NULL)) 1216135446Strhodes return (DNS_R_UNEXPECTEDTSIG); 1217135446Strhodes 1218135446Strhodes mctx = msg->mctx; 1219135446Strhodes 1220135446Strhodes /* 1221135446Strhodes * If we're here, we know the message is well formed and contains a 1222135446Strhodes * TSIG record. 1223135446Strhodes */ 1224135446Strhodes 1225135446Strhodes keyname = msg->tsigname; 1226135446Strhodes ret = dns_rdataset_first(msg->tsig); 1227135446Strhodes if (ret != ISC_R_SUCCESS) 1228135446Strhodes return (ret); 1229135446Strhodes dns_rdataset_current(msg->tsig, &rdata); 1230135446Strhodes ret = dns_rdata_tostruct(&rdata, &tsig, NULL); 1231135446Strhodes if (ret != ISC_R_SUCCESS) 1232135446Strhodes return (ret); 1233135446Strhodes dns_rdata_reset(&rdata); 1234234010Sdougb if (response) { 1235135446Strhodes ret = dns_rdataset_first(msg->querytsig); 1236135446Strhodes if (ret != ISC_R_SUCCESS) 1237135446Strhodes return (ret); 1238135446Strhodes dns_rdataset_current(msg->querytsig, &rdata); 1239135446Strhodes ret = dns_rdata_tostruct(&rdata, &querytsig, NULL); 1240135446Strhodes if (ret != ISC_R_SUCCESS) 1241135446Strhodes return (ret); 1242135446Strhodes } 1243254402Serwin#if defined(__clang__) && \ 1244254402Serwin ( __clang_major__ < 3 || \ 1245254402Serwin (__clang_major__ == 3 && __clang_minor__ < 2) || \ 1246254402Serwin (__clang_major__ == 4 && __clang_minor__ < 2)) 1247254402Serwin /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */ 1248254402Serwin else memset(&querytsig, 0, sizeof(querytsig)); 1249254402Serwin#endif 1250135446Strhodes 1251135446Strhodes /* 1252135446Strhodes * Do the key name and algorithm match that of the query? 1253135446Strhodes */ 1254234010Sdougb if (response && 1255135446Strhodes (!dns_name_equal(keyname, &tsigkey->name) || 1256193149Sdougb !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) { 1257135446Strhodes msg->tsigstatus = dns_tsigerror_badkey; 1258135446Strhodes tsig_log(msg->tsigkey, 2, 1259135446Strhodes "key name and algorithm do not match"); 1260135446Strhodes return (DNS_R_TSIGVERIFYFAILURE); 1261135446Strhodes } 1262135446Strhodes 1263135446Strhodes /* 1264135446Strhodes * Get the current time. 1265135446Strhodes */ 1266135446Strhodes isc_stdtime_get(&now); 1267135446Strhodes 1268135446Strhodes /* 1269135446Strhodes * Find dns_tsigkey_t based on keyname. 1270135446Strhodes */ 1271135446Strhodes if (tsigkey == NULL) { 1272135446Strhodes ret = ISC_R_NOTFOUND; 1273135446Strhodes if (ring1 != NULL) 1274135446Strhodes ret = dns_tsigkey_find(&tsigkey, keyname, 1275135446Strhodes &tsig.algorithm, ring1); 1276135446Strhodes if (ret == ISC_R_NOTFOUND && ring2 != NULL) 1277135446Strhodes ret = dns_tsigkey_find(&tsigkey, keyname, 1278135446Strhodes &tsig.algorithm, ring2); 1279135446Strhodes if (ret != ISC_R_SUCCESS) { 1280135446Strhodes msg->tsigstatus = dns_tsigerror_badkey; 1281135446Strhodes ret = dns_tsigkey_create(keyname, &tsig.algorithm, 1282135446Strhodes NULL, 0, ISC_FALSE, NULL, 1283135446Strhodes now, now, 1284135446Strhodes mctx, NULL, &msg->tsigkey); 1285135446Strhodes if (ret != ISC_R_SUCCESS) 1286135446Strhodes return (ret); 1287135446Strhodes tsig_log(msg->tsigkey, 2, "unknown key"); 1288135446Strhodes return (DNS_R_TSIGVERIFYFAILURE); 1289135446Strhodes } 1290135446Strhodes msg->tsigkey = tsigkey; 1291135446Strhodes } 1292135446Strhodes 1293135446Strhodes key = tsigkey->key; 1294135446Strhodes 1295135446Strhodes /* 1296135446Strhodes * Is the time ok? 1297135446Strhodes */ 1298135446Strhodes if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { 1299135446Strhodes msg->tsigstatus = dns_tsigerror_badtime; 1300135446Strhodes tsig_log(msg->tsigkey, 2, "signature has expired"); 1301135446Strhodes return (DNS_R_CLOCKSKEW); 1302135446Strhodes } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) { 1303135446Strhodes msg->tsigstatus = dns_tsigerror_badtime; 1304135446Strhodes tsig_log(msg->tsigkey, 2, "signature is in the future"); 1305135446Strhodes return (DNS_R_CLOCKSKEW); 1306135446Strhodes } 1307135446Strhodes 1308170222Sdougb /* 1309170222Sdougb * Check digest length. 1310170222Sdougb */ 1311170222Sdougb alg = dst_key_alg(key); 1312170222Sdougb ret = dst_key_sigsize(key, &siglen); 1313170222Sdougb if (ret != ISC_R_SUCCESS) 1314170222Sdougb return (ret); 1315170222Sdougb if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 || 1316170222Sdougb alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 || 1317170222Sdougb alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) { 1318170222Sdougb isc_uint16_t digestbits = dst_key_getbits(key); 1319170222Sdougb if (tsig.siglen > siglen) { 1320170222Sdougb tsig_log(msg->tsigkey, 2, "signature length to big"); 1321170222Sdougb return (DNS_R_FORMERR); 1322170222Sdougb } 1323170222Sdougb if (tsig.siglen > 0 && 1324170222Sdougb (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) { 1325170222Sdougb tsig_log(msg->tsigkey, 2, 1326170222Sdougb "signature length below minimum"); 1327170222Sdougb return (DNS_R_FORMERR); 1328170222Sdougb } 1329170222Sdougb if (tsig.siglen > 0 && digestbits != 0 && 1330186462Sdougb tsig.siglen < ((digestbits + 1) / 8)) { 1331170222Sdougb msg->tsigstatus = dns_tsigerror_badtrunc; 1332170222Sdougb tsig_log(msg->tsigkey, 2, 1333170222Sdougb "truncated signature length too small"); 1334170222Sdougb return (DNS_R_TSIGVERIFYFAILURE); 1335170222Sdougb } 1336170222Sdougb if (tsig.siglen > 0 && digestbits == 0 && 1337170222Sdougb tsig.siglen < siglen) { 1338170222Sdougb msg->tsigstatus = dns_tsigerror_badtrunc; 1339170222Sdougb tsig_log(msg->tsigkey, 2, "signature length too small"); 1340170222Sdougb return (DNS_R_TSIGVERIFYFAILURE); 1341170222Sdougb } 1342170222Sdougb } 1343170222Sdougb 1344135446Strhodes if (tsig.siglen > 0) { 1345135446Strhodes sig_r.base = tsig.signature; 1346135446Strhodes sig_r.length = tsig.siglen; 1347135446Strhodes 1348254402Serwin ret = dst_context_create2(key, mctx, 1349254402Serwin DNS_LOGCATEGORY_DNSSEC, &ctx); 1350135446Strhodes if (ret != ISC_R_SUCCESS) 1351135446Strhodes return (ret); 1352135446Strhodes 1353234010Sdougb if (response) { 1354135446Strhodes isc_buffer_init(&databuf, data, sizeof(data)); 1355135446Strhodes isc_buffer_putuint16(&databuf, querytsig.siglen); 1356135446Strhodes isc_buffer_usedregion(&databuf, &r); 1357135446Strhodes ret = dst_context_adddata(ctx, &r); 1358135446Strhodes if (ret != ISC_R_SUCCESS) 1359135446Strhodes goto cleanup_context; 1360135446Strhodes if (querytsig.siglen > 0) { 1361135446Strhodes r.length = querytsig.siglen; 1362135446Strhodes r.base = querytsig.signature; 1363135446Strhodes ret = dst_context_adddata(ctx, &r); 1364135446Strhodes if (ret != ISC_R_SUCCESS) 1365135446Strhodes goto cleanup_context; 1366135446Strhodes } 1367135446Strhodes } 1368135446Strhodes 1369135446Strhodes /* 1370135446Strhodes * Extract the header. 1371135446Strhodes */ 1372135446Strhodes isc_buffer_usedregion(source, &r); 1373262706Serwin memmove(header, r.base, DNS_MESSAGE_HEADERLEN); 1374135446Strhodes isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); 1375135446Strhodes 1376135446Strhodes /* 1377135446Strhodes * Decrement the additional field counter. 1378135446Strhodes */ 1379262706Serwin memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); 1380135446Strhodes addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); 1381262706Serwin memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); 1382135446Strhodes 1383135446Strhodes /* 1384135446Strhodes * Put in the original id. 1385135446Strhodes */ 1386135446Strhodes id = htons(tsig.originalid); 1387262706Serwin memmove(&header[0], &id, 2); 1388135446Strhodes 1389135446Strhodes /* 1390135446Strhodes * Digest the modified header. 1391135446Strhodes */ 1392135446Strhodes header_r.base = (unsigned char *) header; 1393135446Strhodes header_r.length = DNS_MESSAGE_HEADERLEN; 1394135446Strhodes ret = dst_context_adddata(ctx, &header_r); 1395135446Strhodes if (ret != ISC_R_SUCCESS) 1396135446Strhodes goto cleanup_context; 1397135446Strhodes 1398135446Strhodes /* 1399135446Strhodes * Digest all non-TSIG records. 1400135446Strhodes */ 1401135446Strhodes isc_buffer_usedregion(source, &source_r); 1402135446Strhodes r.base = source_r.base + DNS_MESSAGE_HEADERLEN; 1403135446Strhodes r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; 1404135446Strhodes ret = dst_context_adddata(ctx, &r); 1405135446Strhodes if (ret != ISC_R_SUCCESS) 1406135446Strhodes goto cleanup_context; 1407135446Strhodes 1408135446Strhodes /* 1409135446Strhodes * Digest the key name. 1410135446Strhodes */ 1411135446Strhodes dns_name_toregion(&tsigkey->name, &r); 1412135446Strhodes ret = dst_context_adddata(ctx, &r); 1413135446Strhodes if (ret != ISC_R_SUCCESS) 1414135446Strhodes goto cleanup_context; 1415135446Strhodes 1416135446Strhodes isc_buffer_init(&databuf, data, sizeof(data)); 1417135446Strhodes isc_buffer_putuint16(&databuf, tsig.common.rdclass); 1418135446Strhodes isc_buffer_putuint32(&databuf, msg->tsig->ttl); 1419135446Strhodes isc_buffer_usedregion(&databuf, &r); 1420135446Strhodes ret = dst_context_adddata(ctx, &r); 1421135446Strhodes if (ret != ISC_R_SUCCESS) 1422135446Strhodes goto cleanup_context; 1423135446Strhodes 1424135446Strhodes /* 1425135446Strhodes * Digest the key algorithm. 1426135446Strhodes */ 1427135446Strhodes dns_name_toregion(tsigkey->algorithm, &r); 1428135446Strhodes ret = dst_context_adddata(ctx, &r); 1429135446Strhodes if (ret != ISC_R_SUCCESS) 1430135446Strhodes goto cleanup_context; 1431135446Strhodes 1432135446Strhodes isc_buffer_clear(&databuf); 1433193149Sdougb isc_buffer_putuint48(&databuf, tsig.timesigned); 1434135446Strhodes isc_buffer_putuint16(&databuf, tsig.fudge); 1435135446Strhodes isc_buffer_putuint16(&databuf, tsig.error); 1436135446Strhodes isc_buffer_putuint16(&databuf, tsig.otherlen); 1437135446Strhodes isc_buffer_usedregion(&databuf, &r); 1438135446Strhodes ret = dst_context_adddata(ctx, &r); 1439135446Strhodes if (ret != ISC_R_SUCCESS) 1440135446Strhodes goto cleanup_context; 1441135446Strhodes 1442135446Strhodes if (tsig.otherlen > 0) { 1443135446Strhodes r.base = tsig.other; 1444135446Strhodes r.length = tsig.otherlen; 1445135446Strhodes ret = dst_context_adddata(ctx, &r); 1446135446Strhodes if (ret != ISC_R_SUCCESS) 1447135446Strhodes goto cleanup_context; 1448135446Strhodes } 1449135446Strhodes 1450135446Strhodes ret = dst_context_verify(ctx, &sig_r); 1451135446Strhodes if (ret == DST_R_VERIFYFAILURE) { 1452135446Strhodes msg->tsigstatus = dns_tsigerror_badsig; 1453135446Strhodes ret = DNS_R_TSIGVERIFYFAILURE; 1454135446Strhodes tsig_log(msg->tsigkey, 2, 1455193149Sdougb "signature failed to verify(1)"); 1456135446Strhodes goto cleanup_context; 1457135446Strhodes } else if (ret != ISC_R_SUCCESS) 1458135446Strhodes goto cleanup_context; 1459135446Strhodes 1460135446Strhodes dst_context_destroy(&ctx); 1461135446Strhodes } else if (tsig.error != dns_tsigerror_badsig && 1462193149Sdougb tsig.error != dns_tsigerror_badkey) { 1463135446Strhodes msg->tsigstatus = dns_tsigerror_badsig; 1464135446Strhodes tsig_log(msg->tsigkey, 2, "signature was empty"); 1465135446Strhodes return (DNS_R_TSIGVERIFYFAILURE); 1466135446Strhodes } 1467135446Strhodes 1468135446Strhodes msg->tsigstatus = dns_rcode_noerror; 1469135446Strhodes 1470135446Strhodes if (tsig.error != dns_rcode_noerror) { 1471135446Strhodes if (tsig.error == dns_tsigerror_badtime) 1472135446Strhodes return (DNS_R_CLOCKSKEW); 1473135446Strhodes else 1474135446Strhodes return (DNS_R_TSIGERRORSET); 1475135446Strhodes } 1476135446Strhodes 1477135446Strhodes msg->verified_sig = 1; 1478135446Strhodes 1479135446Strhodes return (ISC_R_SUCCESS); 1480135446Strhodes 1481135446Strhodescleanup_context: 1482135446Strhodes if (ctx != NULL) 1483135446Strhodes dst_context_destroy(&ctx); 1484135446Strhodes 1485135446Strhodes return (ret); 1486135446Strhodes} 1487135446Strhodes 1488135446Strhodesstatic isc_result_t 1489135446Strhodestsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { 1490135446Strhodes dns_rdata_any_tsig_t tsig, querytsig; 1491135446Strhodes isc_region_t r, source_r, header_r, sig_r; 1492135446Strhodes isc_buffer_t databuf; 1493135446Strhodes unsigned char data[32]; 1494135446Strhodes dns_name_t *keyname; 1495135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1496135446Strhodes isc_stdtime_t now; 1497135446Strhodes isc_result_t ret; 1498135446Strhodes dns_tsigkey_t *tsigkey; 1499135446Strhodes dst_key_t *key = NULL; 1500135446Strhodes unsigned char header[DNS_MESSAGE_HEADERLEN]; 1501135446Strhodes isc_uint16_t addcount, id; 1502135446Strhodes isc_boolean_t has_tsig = ISC_FALSE; 1503135446Strhodes isc_mem_t *mctx; 1504135446Strhodes 1505135446Strhodes REQUIRE(source != NULL); 1506135446Strhodes REQUIRE(msg != NULL); 1507135446Strhodes REQUIRE(dns_message_gettsigkey(msg) != NULL); 1508135446Strhodes REQUIRE(msg->tcp_continuation == 1); 1509135446Strhodes REQUIRE(msg->querytsig != NULL); 1510135446Strhodes 1511135446Strhodes if (!is_response(msg)) 1512135446Strhodes return (DNS_R_EXPECTEDRESPONSE); 1513135446Strhodes 1514135446Strhodes mctx = msg->mctx; 1515135446Strhodes 1516135446Strhodes tsigkey = dns_message_gettsigkey(msg); 1517135446Strhodes 1518135446Strhodes /* 1519135446Strhodes * Extract and parse the previous TSIG 1520135446Strhodes */ 1521135446Strhodes ret = dns_rdataset_first(msg->querytsig); 1522135446Strhodes if (ret != ISC_R_SUCCESS) 1523135446Strhodes return (ret); 1524135446Strhodes dns_rdataset_current(msg->querytsig, &rdata); 1525135446Strhodes ret = dns_rdata_tostruct(&rdata, &querytsig, NULL); 1526135446Strhodes if (ret != ISC_R_SUCCESS) 1527135446Strhodes return (ret); 1528135446Strhodes dns_rdata_reset(&rdata); 1529135446Strhodes 1530135446Strhodes /* 1531135446Strhodes * If there is a TSIG in this message, do some checks. 1532135446Strhodes */ 1533135446Strhodes if (msg->tsig != NULL) { 1534135446Strhodes has_tsig = ISC_TRUE; 1535135446Strhodes 1536135446Strhodes keyname = msg->tsigname; 1537135446Strhodes ret = dns_rdataset_first(msg->tsig); 1538135446Strhodes if (ret != ISC_R_SUCCESS) 1539135446Strhodes goto cleanup_querystruct; 1540135446Strhodes dns_rdataset_current(msg->tsig, &rdata); 1541135446Strhodes ret = dns_rdata_tostruct(&rdata, &tsig, NULL); 1542135446Strhodes if (ret != ISC_R_SUCCESS) 1543135446Strhodes goto cleanup_querystruct; 1544135446Strhodes 1545135446Strhodes /* 1546135446Strhodes * Do the key name and algorithm match that of the query? 1547135446Strhodes */ 1548135446Strhodes if (!dns_name_equal(keyname, &tsigkey->name) || 1549193149Sdougb !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) { 1550135446Strhodes msg->tsigstatus = dns_tsigerror_badkey; 1551135446Strhodes ret = DNS_R_TSIGVERIFYFAILURE; 1552135446Strhodes tsig_log(msg->tsigkey, 2, 1553135446Strhodes "key name and algorithm do not match"); 1554135446Strhodes goto cleanup_querystruct; 1555135446Strhodes } 1556135446Strhodes 1557135446Strhodes /* 1558135446Strhodes * Is the time ok? 1559135446Strhodes */ 1560135446Strhodes isc_stdtime_get(&now); 1561135446Strhodes 1562135446Strhodes if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { 1563135446Strhodes msg->tsigstatus = dns_tsigerror_badtime; 1564135446Strhodes tsig_log(msg->tsigkey, 2, "signature has expired"); 1565135446Strhodes ret = DNS_R_CLOCKSKEW; 1566135446Strhodes goto cleanup_querystruct; 1567135446Strhodes } else if (now + msg->timeadjust < 1568193149Sdougb tsig.timesigned - tsig.fudge) { 1569135446Strhodes msg->tsigstatus = dns_tsigerror_badtime; 1570135446Strhodes tsig_log(msg->tsigkey, 2, 1571135446Strhodes "signature is in the future"); 1572135446Strhodes ret = DNS_R_CLOCKSKEW; 1573135446Strhodes goto cleanup_querystruct; 1574135446Strhodes } 1575135446Strhodes } 1576135446Strhodes 1577135446Strhodes key = tsigkey->key; 1578135446Strhodes 1579135446Strhodes if (msg->tsigctx == NULL) { 1580254402Serwin ret = dst_context_create2(key, mctx, 1581254402Serwin DNS_LOGCATEGORY_DNSSEC, 1582254402Serwin &msg->tsigctx); 1583135446Strhodes if (ret != ISC_R_SUCCESS) 1584135446Strhodes goto cleanup_querystruct; 1585135446Strhodes 1586135446Strhodes /* 1587135446Strhodes * Digest the length of the query signature 1588135446Strhodes */ 1589135446Strhodes isc_buffer_init(&databuf, data, sizeof(data)); 1590135446Strhodes isc_buffer_putuint16(&databuf, querytsig.siglen); 1591135446Strhodes isc_buffer_usedregion(&databuf, &r); 1592135446Strhodes ret = dst_context_adddata(msg->tsigctx, &r); 1593135446Strhodes if (ret != ISC_R_SUCCESS) 1594135446Strhodes goto cleanup_context; 1595135446Strhodes 1596135446Strhodes /* 1597135446Strhodes * Digest the data of the query signature 1598135446Strhodes */ 1599135446Strhodes if (querytsig.siglen > 0) { 1600135446Strhodes r.length = querytsig.siglen; 1601135446Strhodes r.base = querytsig.signature; 1602135446Strhodes ret = dst_context_adddata(msg->tsigctx, &r); 1603135446Strhodes if (ret != ISC_R_SUCCESS) 1604135446Strhodes goto cleanup_context; 1605135446Strhodes } 1606135446Strhodes } 1607135446Strhodes 1608135446Strhodes /* 1609135446Strhodes * Extract the header. 1610135446Strhodes */ 1611135446Strhodes isc_buffer_usedregion(source, &r); 1612262706Serwin memmove(header, r.base, DNS_MESSAGE_HEADERLEN); 1613135446Strhodes isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); 1614135446Strhodes 1615135446Strhodes /* 1616135446Strhodes * Decrement the additional field counter if necessary. 1617135446Strhodes */ 1618135446Strhodes if (has_tsig) { 1619262706Serwin memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); 1620135446Strhodes addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); 1621262706Serwin memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); 1622135446Strhodes } 1623135446Strhodes 1624135446Strhodes /* 1625135446Strhodes * Put in the original id. 1626135446Strhodes */ 1627135446Strhodes /* XXX Can TCP transfers be forwarded? How would that work? */ 1628135446Strhodes if (has_tsig) { 1629135446Strhodes id = htons(tsig.originalid); 1630262706Serwin memmove(&header[0], &id, 2); 1631135446Strhodes } 1632135446Strhodes 1633135446Strhodes /* 1634135446Strhodes * Digest the modified header. 1635135446Strhodes */ 1636135446Strhodes header_r.base = (unsigned char *) header; 1637135446Strhodes header_r.length = DNS_MESSAGE_HEADERLEN; 1638135446Strhodes ret = dst_context_adddata(msg->tsigctx, &header_r); 1639135446Strhodes if (ret != ISC_R_SUCCESS) 1640135446Strhodes goto cleanup_context; 1641135446Strhodes 1642135446Strhodes /* 1643135446Strhodes * Digest all non-TSIG records. 1644135446Strhodes */ 1645135446Strhodes isc_buffer_usedregion(source, &source_r); 1646135446Strhodes r.base = source_r.base + DNS_MESSAGE_HEADERLEN; 1647135446Strhodes if (has_tsig) 1648135446Strhodes r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; 1649135446Strhodes else 1650135446Strhodes r.length = source_r.length - DNS_MESSAGE_HEADERLEN; 1651135446Strhodes ret = dst_context_adddata(msg->tsigctx, &r); 1652135446Strhodes if (ret != ISC_R_SUCCESS) 1653135446Strhodes goto cleanup_context; 1654135446Strhodes 1655135446Strhodes /* 1656135446Strhodes * Digest the time signed and fudge. 1657135446Strhodes */ 1658135446Strhodes if (has_tsig) { 1659135446Strhodes isc_buffer_init(&databuf, data, sizeof(data)); 1660193149Sdougb isc_buffer_putuint48(&databuf, tsig.timesigned); 1661135446Strhodes isc_buffer_putuint16(&databuf, tsig.fudge); 1662135446Strhodes isc_buffer_usedregion(&databuf, &r); 1663135446Strhodes ret = dst_context_adddata(msg->tsigctx, &r); 1664135446Strhodes if (ret != ISC_R_SUCCESS) 1665135446Strhodes goto cleanup_context; 1666135446Strhodes 1667135446Strhodes sig_r.base = tsig.signature; 1668135446Strhodes sig_r.length = tsig.siglen; 1669135446Strhodes if (tsig.siglen == 0) { 1670135446Strhodes if (tsig.error != dns_rcode_noerror) { 1671135446Strhodes if (tsig.error == dns_tsigerror_badtime) 1672135446Strhodes ret = DNS_R_CLOCKSKEW; 1673135446Strhodes else 1674135446Strhodes ret = DNS_R_TSIGERRORSET; 1675135446Strhodes } else { 1676135446Strhodes tsig_log(msg->tsigkey, 2, 1677135446Strhodes "signature is empty"); 1678135446Strhodes ret = DNS_R_TSIGVERIFYFAILURE; 1679135446Strhodes } 1680135446Strhodes goto cleanup_context; 1681135446Strhodes } 1682135446Strhodes 1683135446Strhodes ret = dst_context_verify(msg->tsigctx, &sig_r); 1684135446Strhodes if (ret == DST_R_VERIFYFAILURE) { 1685135446Strhodes msg->tsigstatus = dns_tsigerror_badsig; 1686135446Strhodes tsig_log(msg->tsigkey, 2, 1687193149Sdougb "signature failed to verify(2)"); 1688135446Strhodes ret = DNS_R_TSIGVERIFYFAILURE; 1689135446Strhodes goto cleanup_context; 1690135446Strhodes } 1691135446Strhodes else if (ret != ISC_R_SUCCESS) 1692135446Strhodes goto cleanup_context; 1693135446Strhodes 1694135446Strhodes dst_context_destroy(&msg->tsigctx); 1695135446Strhodes } 1696135446Strhodes 1697135446Strhodes msg->tsigstatus = dns_rcode_noerror; 1698135446Strhodes return (ISC_R_SUCCESS); 1699135446Strhodes 1700135446Strhodes cleanup_context: 1701135446Strhodes dst_context_destroy(&msg->tsigctx); 1702135446Strhodes 1703135446Strhodes cleanup_querystruct: 1704135446Strhodes dns_rdata_freestruct(&querytsig); 1705135446Strhodes 1706135446Strhodes return (ret); 1707135446Strhodes 1708135446Strhodes} 1709135446Strhodes 1710135446Strhodesisc_result_t 1711135446Strhodesdns_tsigkey_find(dns_tsigkey_t **tsigkey, dns_name_t *name, 1712135446Strhodes dns_name_t *algorithm, dns_tsig_keyring_t *ring) 1713135446Strhodes{ 1714135446Strhodes dns_tsigkey_t *key; 1715135446Strhodes isc_stdtime_t now; 1716135446Strhodes isc_result_t result; 1717135446Strhodes 1718135446Strhodes REQUIRE(tsigkey != NULL); 1719135446Strhodes REQUIRE(*tsigkey == NULL); 1720135446Strhodes REQUIRE(name != NULL); 1721135446Strhodes REQUIRE(ring != NULL); 1722135446Strhodes 1723193149Sdougb RWLOCK(&ring->lock, isc_rwlocktype_write); 1724193149Sdougb cleanup_ring(ring); 1725193149Sdougb RWUNLOCK(&ring->lock, isc_rwlocktype_write); 1726193149Sdougb 1727135446Strhodes isc_stdtime_get(&now); 1728135446Strhodes RWLOCK(&ring->lock, isc_rwlocktype_read); 1729135446Strhodes key = NULL; 1730135446Strhodes result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key); 1731135446Strhodes if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) { 1732135446Strhodes RWUNLOCK(&ring->lock, isc_rwlocktype_read); 1733135446Strhodes return (ISC_R_NOTFOUND); 1734135446Strhodes } 1735135446Strhodes if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) { 1736135446Strhodes RWUNLOCK(&ring->lock, isc_rwlocktype_read); 1737135446Strhodes return (ISC_R_NOTFOUND); 1738135446Strhodes } 1739218384Sdougb if (key->inception != key->expire && isc_serial_lt(key->expire, now)) { 1740135446Strhodes /* 1741135446Strhodes * The key has expired. 1742135446Strhodes */ 1743135446Strhodes RWUNLOCK(&ring->lock, isc_rwlocktype_read); 1744135446Strhodes RWLOCK(&ring->lock, isc_rwlocktype_write); 1745218384Sdougb remove_fromring(key); 1746135446Strhodes RWUNLOCK(&ring->lock, isc_rwlocktype_write); 1747135446Strhodes return (ISC_R_NOTFOUND); 1748135446Strhodes } 1749218384Sdougb#if 0 1750218384Sdougb /* 1751218384Sdougb * MPAXXX We really should look at the inception time. 1752218384Sdougb */ 1753218384Sdougb if (key->inception != key->expire && 1754218384Sdougb isc_serial_lt(key->inception, now)) { 1755218384Sdougb RWUNLOCK(&ring->lock, isc_rwlocktype_read); 1756218384Sdougb adjust_lru(key); 1757218384Sdougb return (ISC_R_NOTFOUND); 1758218384Sdougb } 1759218384Sdougb#endif 1760135446Strhodes isc_refcount_increment(&key->refs, NULL); 1761135446Strhodes RWUNLOCK(&ring->lock, isc_rwlocktype_read); 1762218384Sdougb adjust_lru(key); 1763135446Strhodes *tsigkey = key; 1764135446Strhodes return (ISC_R_SUCCESS); 1765135446Strhodes} 1766135446Strhodes 1767135446Strhodesstatic void 1768135446Strhodesfree_tsignode(void *node, void *_unused) { 1769135446Strhodes dns_tsigkey_t *key; 1770135446Strhodes 1771254402Serwin REQUIRE(node != NULL); 1772254402Serwin 1773135446Strhodes UNUSED(_unused); 1774135446Strhodes 1775135446Strhodes key = node; 1776254402Serwin if (key->generated) { 1777254402Serwin if (ISC_LINK_LINKED(key, link)) 1778254402Serwin ISC_LIST_UNLINK(key->ring->lru, key, link); 1779254402Serwin } 1780135446Strhodes dns_tsigkey_detach(&key); 1781135446Strhodes} 1782135446Strhodes 1783135446Strhodesisc_result_t 1784135446Strhodesdns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) { 1785135446Strhodes isc_result_t result; 1786135446Strhodes dns_tsig_keyring_t *ring; 1787135446Strhodes 1788135446Strhodes REQUIRE(mctx != NULL); 1789135446Strhodes REQUIRE(ringp != NULL); 1790135446Strhodes REQUIRE(*ringp == NULL); 1791135446Strhodes 1792135446Strhodes ring = isc_mem_get(mctx, sizeof(dns_tsig_keyring_t)); 1793135446Strhodes if (ring == NULL) 1794135446Strhodes return (ISC_R_NOMEMORY); 1795135446Strhodes 1796135446Strhodes result = isc_rwlock_init(&ring->lock, 0, 0); 1797174187Sdougb if (result != ISC_R_SUCCESS) { 1798174187Sdougb isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t)); 1799170222Sdougb return (result); 1800174187Sdougb } 1801135446Strhodes 1802135446Strhodes ring->keys = NULL; 1803135446Strhodes result = dns_rbt_create(mctx, free_tsignode, NULL, &ring->keys); 1804135446Strhodes if (result != ISC_R_SUCCESS) { 1805135446Strhodes isc_rwlock_destroy(&ring->lock); 1806135446Strhodes isc_mem_put(mctx, ring, sizeof(dns_tsig_keyring_t)); 1807135446Strhodes return (result); 1808135446Strhodes } 1809135446Strhodes 1810193149Sdougb ring->writecount = 0; 1811186462Sdougb ring->mctx = NULL; 1812218384Sdougb ring->generated = 0; 1813218384Sdougb ring->maxgenerated = DNS_TSIG_MAXGENERATEDKEYS; 1814218384Sdougb ISC_LIST_INIT(ring->lru); 1815186462Sdougb isc_mem_attach(mctx, &ring->mctx); 1816224092Sdougb ring->references = 1; 1817135446Strhodes 1818135446Strhodes *ringp = ring; 1819135446Strhodes return (ISC_R_SUCCESS); 1820135446Strhodes} 1821135446Strhodes 1822224092Sdougbisc_result_t 1823224092Sdougbdns_tsigkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name, 1824224092Sdougb dns_tsigkey_t *tkey) 1825224092Sdougb{ 1826224092Sdougb isc_result_t result; 1827224092Sdougb 1828224092Sdougb result = keyring_add(ring, name, tkey); 1829224092Sdougb if (result == ISC_R_SUCCESS) 1830224092Sdougb isc_refcount_increment(&tkey->refs, NULL); 1831224092Sdougb 1832224092Sdougb return (result); 1833224092Sdougb} 1834224092Sdougb 1835135446Strhodesvoid 1836224092Sdougbdns_tsigkeyring_attach(dns_tsig_keyring_t *source, dns_tsig_keyring_t **target) 1837224092Sdougb{ 1838224092Sdougb REQUIRE(source != NULL); 1839224092Sdougb REQUIRE(target != NULL && *target == NULL); 1840224092Sdougb 1841224092Sdougb RWLOCK(&source->lock, isc_rwlocktype_write); 1842224092Sdougb INSIST(source->references > 0); 1843224092Sdougb source->references++; 1844224092Sdougb INSIST(source->references > 0); 1845224092Sdougb *target = source; 1846224092Sdougb RWUNLOCK(&source->lock, isc_rwlocktype_write); 1847224092Sdougb} 1848224092Sdougb 1849224092Sdougbvoid 1850224092Sdougbdns_tsigkeyring_detach(dns_tsig_keyring_t **ringp) { 1851135446Strhodes dns_tsig_keyring_t *ring; 1852224092Sdougb unsigned int references; 1853135446Strhodes 1854135446Strhodes REQUIRE(ringp != NULL); 1855135446Strhodes REQUIRE(*ringp != NULL); 1856135446Strhodes 1857135446Strhodes ring = *ringp; 1858135446Strhodes *ringp = NULL; 1859135446Strhodes 1860224092Sdougb RWLOCK(&ring->lock, isc_rwlocktype_write); 1861224092Sdougb INSIST(ring->references > 0); 1862224092Sdougb ring->references--; 1863224092Sdougb references = ring->references; 1864224092Sdougb RWUNLOCK(&ring->lock, isc_rwlocktype_write); 1865224092Sdougb 1866224092Sdougb if (references == 0) 1867224092Sdougb destroyring(ring); 1868135446Strhodes} 1869224092Sdougb 1870224092Sdougbvoid 1871224092Sdougbdns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp) { 1872224092Sdougb isc_stdtime_t now; 1873224092Sdougb isc_result_t result; 1874224092Sdougb 1875224092Sdougb isc_stdtime_get(&now); 1876224092Sdougb do { 1877224092Sdougb result = restore_key(ring, now, fp); 1878224092Sdougb if (result == ISC_R_NOMORE) 1879224092Sdougb return; 1880224092Sdougb if (result == DNS_R_BADALG || result == DNS_R_EXPIRED) 1881224092Sdougb result = ISC_R_SUCCESS; 1882224092Sdougb } while (result == ISC_R_SUCCESS); 1883224092Sdougb} 1884