diff options
Diffstat (limited to 'contrib/bind9/lib/dns/tsig.c')
-rw-r--r-- | contrib/bind9/lib/dns/tsig.c | 367 |
1 files changed, 305 insertions, 62 deletions
diff --git a/contrib/bind9/lib/dns/tsig.c b/contrib/bind9/lib/dns/tsig.c index 65d32dc8614f..cec5222f2f8b 100644 --- a/contrib/bind9/lib/dns/tsig.c +++ b/contrib/bind9/lib/dns/tsig.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008, 2010 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -16,7 +16,7 @@ */ /* - * $Id: tsig.c,v 1.136.18.5 2010-12-09 01:12:55 marka Exp $ + * $Id: tsig.c,v 1.147 2011-01-11 23:47:13 tbox Exp $ */ /*! \file */ #include <config.h> @@ -91,31 +91,6 @@ static dns_name_t gsstsig = { }; LIBDNS_EXTERNAL_DATA dns_name_t *dns_tsig_gssapi_name = &gsstsig; -static void -remove_fromring(dns_tsigkey_t *tkey) { - if (tkey->generated) { - ISC_LIST_UNLINK(tkey->ring->lru, tkey, link); - tkey->ring->generated--; - } - (void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, ISC_FALSE); -} - -static void -adjust_lru(dns_tsigkey_t *tkey) { - if (tkey->generated) { - RWLOCK(&tkey->ring->lock, isc_rwlocktype_write); - /* - * We may have been removed from the LRU list between - * removing the read lock and aquiring the write lock. - */ - if (ISC_LINK_LINKED(tkey, link)) { - ISC_LIST_UNLINK(tkey->ring->lru, tkey, link); - ISC_LIST_APPEND(tkey->ring->lru, tkey, link); - } - RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write); - } -} - /* * Since Microsoft doesn't follow its own standard, we will use this * alternate name as a second guess. @@ -228,8 +203,10 @@ tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) { else strcpy(namestr, "<null>"); - if (key != NULL && key->generated) + if (key != NULL && key->generated && key->creator) dns_name_format(key->creator, creatorstr, sizeof(creatorstr)); + else + strcpy(creatorstr, "<null>"); va_start(ap, fmt); vsnprintf(message, sizeof(message), fmt, ap); @@ -245,6 +222,71 @@ tsig_log(dns_tsigkey_t *key, int level, const char *fmt, ...) { level, "tsig key '%s': %s", namestr, message); } +static void +remove_fromring(dns_tsigkey_t *tkey) { + if (tkey->generated) { + ISC_LIST_UNLINK(tkey->ring->lru, tkey, link); + tkey->ring->generated--; + } + (void)dns_rbt_deletename(tkey->ring->keys, &tkey->name, ISC_FALSE); +} + +static void +adjust_lru(dns_tsigkey_t *tkey) { + if (tkey->generated) { + RWLOCK(&tkey->ring->lock, isc_rwlocktype_write); + /* + * We may have been removed from the LRU list between + * removing the read lock and aquiring the write lock. + */ + if (ISC_LINK_LINKED(tkey, link)) { + ISC_LIST_UNLINK(tkey->ring->lru, tkey, link); + ISC_LIST_APPEND(tkey->ring->lru, tkey, link); + } + RWUNLOCK(&tkey->ring->lock, isc_rwlocktype_write); + } +} + +/* + * A supplemental routine just to add a key to ring. Note that reference + * counter should be counted separately because we may be adding the key + * as part of creation of the key, in which case the reference counter was + * already initialized. Also note we don't need RWLOCK for the reference + * counter: it's protected by a separate lock. + */ +static isc_result_t +keyring_add(dns_tsig_keyring_t *ring, dns_name_t *name, + dns_tsigkey_t *tkey) +{ + isc_result_t result; + + RWLOCK(&ring->lock, isc_rwlocktype_write); + ring->writecount++; + + /* + * Do on the fly cleaning. Find some nodes we might not + * want around any more. + */ + if (ring->writecount > 10) { + cleanup_ring(ring); + ring->writecount = 0; + } + + result = dns_rbt_addname(ring->keys, name, tkey); + if (tkey->generated) { + /* + * Add the new key to the LRU list and remove the least + * recently used key if there are too many keys on the list. + */ + ISC_LIST_INITANDAPPEND(ring->lru, tkey, link); + if (ring->generated++ > ring->maxgenerated) + remove_fromring(ISC_LIST_HEAD(ring->lru)); + } + RWUNLOCK(&ring->lock, isc_rwlocktype_write); + + return (result); +} + isc_result_t dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm, dst_key_t *dstkey, isc_boolean_t generated, @@ -363,7 +405,7 @@ dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm, tkey->ring = ring; if (key != NULL) - refs++; + refs = 1; if (ring != NULL) refs++; ret = isc_refcount_init(&tkey->refs, refs); @@ -379,36 +421,9 @@ dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm, tkey->magic = TSIG_MAGIC; if (ring != NULL) { - RWLOCK(&ring->lock, isc_rwlocktype_write); - ring->writecount++; - - /* - * Do on the fly cleaning. Find some nodes we might not - * want around any more. - */ - if (ring->writecount > 10) { - cleanup_ring(ring); - ring->writecount = 0; - } - - ret = dns_rbt_addname(ring->keys, name, tkey); - if (ret != ISC_R_SUCCESS) { - RWUNLOCK(&ring->lock, isc_rwlocktype_write); + ret = keyring_add(ring, name, tkey); + if (ret != ISC_R_SUCCESS) goto cleanup_refs; - } - - if (tkey->generated) { - /* - * Add the new key to the LRU list and remove the - * least recently used key if there are too many - * keys on the list. - */ - ISC_LIST_INITANDAPPEND(ring->lru, tkey, link); - if (ring->generated++ > ring->maxgenerated) - remove_fromring(ISC_LIST_HEAD(ring->lru)); - } - - RWUNLOCK(&ring->lock, isc_rwlocktype_write); } /* @@ -424,6 +439,7 @@ dns_tsigkey_createfromkey(dns_name_t *name, dns_name_t *algorithm, "the key '%s' is too short to be secure", namestr); } + if (key != NULL) *key = tkey; @@ -512,6 +528,184 @@ cleanup_ring(dns_tsig_keyring_t *ring) } } +static void +destroyring(dns_tsig_keyring_t *ring) { + dns_rbt_destroy(&ring->keys); + isc_rwlock_destroy(&ring->lock); + isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t)); +} + +static unsigned int +dst_alg_fromname(dns_name_t *algorithm) { + if (dns_name_equal(algorithm, DNS_TSIG_HMACMD5_NAME)) { + return (DST_ALG_HMACMD5); + } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA1_NAME)) { + return (DST_ALG_HMACSHA1); + } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA224_NAME)) { + return (DST_ALG_HMACSHA224); + } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA256_NAME)) { + return (DST_ALG_HMACSHA256); + } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA384_NAME)) { + return (DST_ALG_HMACSHA384); + } else if (dns_name_equal(algorithm, DNS_TSIG_HMACSHA512_NAME)) { + return (DST_ALG_HMACSHA512); + } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPI_NAME)) { + return (DST_ALG_GSSAPI); + } else if (dns_name_equal(algorithm, DNS_TSIG_GSSAPIMS_NAME)) { + return (DST_ALG_GSSAPI); + } else + return (0); +} + +static isc_result_t +restore_key(dns_tsig_keyring_t *ring, isc_stdtime_t now, FILE *fp) { + dst_key_t *dstkey = NULL; + char namestr[1024]; + char creatorstr[1024]; + char algorithmstr[1024]; + char keystr[4096]; + unsigned int inception, expire; + int n; + isc_buffer_t b; + dns_name_t *name, *creator, *algorithm; + dns_fixedname_t fname, fcreator, falgorithm; + isc_result_t result; + unsigned int dstalg; + + n = fscanf(fp, "%1023s %1023s %u %u %1023s %4095s\n", namestr, + creatorstr, &inception, &expire, algorithmstr, keystr); + if (n == EOF) + return (ISC_R_NOMORE); + if (n != 6) + return (ISC_R_FAILURE); + + if (isc_serial_lt(expire, now)) + return (DNS_R_EXPIRED); + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + isc_buffer_init(&b, namestr, strlen(namestr)); + isc_buffer_add(&b, strlen(namestr)); + result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + dns_fixedname_init(&fcreator); + creator = dns_fixedname_name(&fcreator); + isc_buffer_init(&b, creatorstr, strlen(creatorstr)); + isc_buffer_add(&b, strlen(creatorstr)); + result = dns_name_fromtext(creator, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + dns_fixedname_init(&falgorithm); + algorithm = dns_fixedname_name(&falgorithm); + isc_buffer_init(&b, algorithmstr, strlen(algorithmstr)); + isc_buffer_add(&b, strlen(algorithmstr)); + result = dns_name_fromtext(algorithm, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) + return (result); + + dstalg = dst_alg_fromname(algorithm); + if (dstalg == 0) + return (DNS_R_BADALG); + + result = dst_key_restore(name, dstalg, DNS_KEYOWNER_ENTITY, + DNS_KEYPROTO_DNSSEC, dns_rdataclass_in, + ring->mctx, keystr, &dstkey); + if (result != ISC_R_SUCCESS) + return (result); + + result = dns_tsigkey_createfromkey(name, algorithm, dstkey, + ISC_TRUE, creator, inception, + expire, ring->mctx, ring, NULL); + if (result != ISC_R_SUCCESS && dstkey != NULL) + dst_key_free(&dstkey); + return (result); +} + +static void +dump_key(dns_tsigkey_t *tkey, FILE *fp) +{ + char *buffer = NULL; + int length = 0; + char namestr[DNS_NAME_FORMATSIZE]; + char creatorstr[DNS_NAME_FORMATSIZE]; + char algorithmstr[DNS_NAME_FORMATSIZE]; + isc_result_t result; + + dns_name_format(&tkey->name, namestr, sizeof(namestr)); + dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr)); + dns_name_format(tkey->algorithm, algorithmstr, sizeof(algorithmstr)); + result = dst_key_dump(tkey->key, tkey->mctx, &buffer, &length); + if (result == ISC_R_SUCCESS) + fprintf(fp, "%s %s %u %u %s %.*s\n", namestr, creatorstr, + tkey->inception, tkey->expire, algorithmstr, + length, buffer); + if (buffer != NULL) + isc_mem_put(tkey->mctx, buffer, length); +} + +isc_result_t +dns_tsigkeyring_dumpanddetach(dns_tsig_keyring_t **ringp, FILE *fp) { + isc_result_t result; + dns_rbtnodechain_t chain; + dns_name_t foundname; + dns_fixedname_t fixedorigin; + dns_name_t *origin; + isc_stdtime_t now; + dns_rbtnode_t *node; + dns_tsigkey_t *tkey; + dns_tsig_keyring_t *ring; + unsigned int references; + + REQUIRE(ringp != NULL && *ringp != NULL); + + ring = *ringp; + *ringp = NULL; + + RWLOCK(&ring->lock, isc_rwlocktype_write); + INSIST(ring->references > 0); + ring->references--; + references = ring->references; + RWUNLOCK(&ring->lock, isc_rwlocktype_write); + + if (references != 0) + return (DNS_R_CONTINUE); + + isc_stdtime_get(&now); + dns_name_init(&foundname, NULL); + dns_fixedname_init(&fixedorigin); + origin = dns_fixedname_name(&fixedorigin); + dns_rbtnodechain_init(&chain, ring->mctx); + result = dns_rbtnodechain_first(&chain, ring->keys, &foundname, + origin); + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + dns_rbtnodechain_invalidate(&chain); + goto destroy; + } + + for (;;) { + node = NULL; + dns_rbtnodechain_current(&chain, &foundname, origin, &node); + tkey = node->data; + if (tkey != NULL && tkey->generated && tkey->expire >= now) + dump_key(tkey, fp); + result = dns_rbtnodechain_next(&chain, &foundname, + origin); + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + dns_rbtnodechain_invalidate(&chain); + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + goto destroy; + } + } + + destroy: + destroyring(ring); + return (result); +} + isc_result_t dns_tsigkey_create(dns_name_t *name, dns_name_t *algorithm, unsigned char *secret, int length, isc_boolean_t generated, @@ -1589,14 +1783,43 @@ dns_tsigkeyring_create(isc_mem_t *mctx, dns_tsig_keyring_t **ringp) { ring->maxgenerated = DNS_TSIG_MAXGENERATEDKEYS; ISC_LIST_INIT(ring->lru); isc_mem_attach(mctx, &ring->mctx); + ring->references = 1; *ringp = ring; return (ISC_R_SUCCESS); } +isc_result_t +dns_tsigkeyring_add(dns_tsig_keyring_t *ring, dns_name_t *name, + dns_tsigkey_t *tkey) +{ + isc_result_t result; + + result = keyring_add(ring, name, tkey); + if (result == ISC_R_SUCCESS) + isc_refcount_increment(&tkey->refs, NULL); + + return (result); +} + void -dns_tsigkeyring_destroy(dns_tsig_keyring_t **ringp) { +dns_tsigkeyring_attach(dns_tsig_keyring_t *source, dns_tsig_keyring_t **target) +{ + REQUIRE(source != NULL); + REQUIRE(target != NULL && *target == NULL); + + RWLOCK(&source->lock, isc_rwlocktype_write); + INSIST(source->references > 0); + source->references++; + INSIST(source->references > 0); + *target = source; + RWUNLOCK(&source->lock, isc_rwlocktype_write); +} + +void +dns_tsigkeyring_detach(dns_tsig_keyring_t **ringp) { dns_tsig_keyring_t *ring; + unsigned int references; REQUIRE(ringp != NULL); REQUIRE(*ringp != NULL); @@ -1604,7 +1827,27 @@ dns_tsigkeyring_destroy(dns_tsig_keyring_t **ringp) { ring = *ringp; *ringp = NULL; - dns_rbt_destroy(&ring->keys); - isc_rwlock_destroy(&ring->lock); - isc_mem_putanddetach(&ring->mctx, ring, sizeof(dns_tsig_keyring_t)); + RWLOCK(&ring->lock, isc_rwlocktype_write); + INSIST(ring->references > 0); + ring->references--; + references = ring->references; + RWUNLOCK(&ring->lock, isc_rwlocktype_write); + + if (references == 0) + destroyring(ring); +} + +void +dns_keyring_restore(dns_tsig_keyring_t *ring, FILE *fp) { + isc_stdtime_t now; + isc_result_t result; + + isc_stdtime_get(&now); + do { + result = restore_key(ring, now, fp); + if (result == ISC_R_NOMORE) + return; + if (result == DNS_R_BADALG || result == DNS_R_EXPIRED) + result = ISC_R_SUCCESS; + } while (result == ISC_R_SUCCESS); } |