diff options
Diffstat (limited to 'lib/dns/keytable.c')
-rw-r--r-- | lib/dns/keytable.c | 389 |
1 files changed, 333 insertions, 56 deletions
diff --git a/lib/dns/keytable.c b/lib/dns/keytable.c index 874868d..3edc3d6 100644 --- a/lib/dns/keytable.c +++ b/lib/dns/keytable.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2007, 2009, 2010 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2000, 2001 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: keytable.c,v 1.34 2007-06-19 23:47:16 tbox Exp $ */ +/* $Id: keytable.c,v 1.41 2010-06-25 23:46:51 tbox Exp $ */ /*! \file */ @@ -31,41 +31,12 @@ #include <dns/rbt.h> #include <dns/result.h> -struct dns_keytable { - /* Unlocked. */ - unsigned int magic; - isc_mem_t *mctx; - isc_mutex_t lock; - isc_rwlock_t rwlock; - /* Locked by lock. */ - isc_uint32_t active_nodes; - /* Locked by rwlock. */ - isc_uint32_t references; - dns_rbt_t *table; -}; - -#define KEYTABLE_MAGIC ISC_MAGIC('K', 'T', 'b', 'l') -#define VALID_KEYTABLE(kt) ISC_MAGIC_VALID(kt, KEYTABLE_MAGIC) - -struct dns_keynode { - unsigned int magic; - dst_key_t * key; - struct dns_keynode * next; -}; - -#define KEYNODE_MAGIC ISC_MAGIC('K', 'N', 'o', 'd') -#define VALID_KEYNODE(kn) ISC_MAGIC_VALID(kn, KEYNODE_MAGIC) - static void free_keynode(void *node, void *arg) { dns_keynode_t *keynode = node; isc_mem_t *mctx = arg; - REQUIRE(VALID_KEYNODE(keynode)); - dst_key_free(&keynode->key); - if (keynode->next != NULL) - free_keynode(keynode->next, mctx); - isc_mem_put(mctx, keynode, sizeof(dns_keynode_t)); + dns_keynode_detachall(mctx, &keynode); } isc_result_t @@ -116,7 +87,6 @@ dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) { return (result); } - void dns_keytable_attach(dns_keytable_t *source, dns_keytable_t **targetp) { @@ -173,50 +143,224 @@ dns_keytable_detach(dns_keytable_t **keytablep) { *keytablep = NULL; } -isc_result_t -dns_keytable_add(dns_keytable_t *keytable, dst_key_t **keyp) { +static isc_result_t +insert(dns_keytable_t *keytable, isc_boolean_t managed, + dns_name_t *keyname, dst_key_t **keyp) +{ isc_result_t result; - dns_keynode_t *knode; + dns_keynode_t *knode = NULL; dns_rbtnode_t *node; - dns_name_t *keyname; - - /* - * Add '*keyp' to 'keytable'. - */ + REQUIRE(keyp == NULL || *keyp != NULL); REQUIRE(VALID_KEYTABLE(keytable)); - REQUIRE(keyp != NULL); - keyname = dst_key_name(*keyp); + result = dns_keynode_create(keytable->mctx, &knode); + if (result != ISC_R_SUCCESS) + return (result); - knode = isc_mem_get(keytable->mctx, sizeof(*knode)); - if (knode == NULL) - return (ISC_R_NOMEMORY); + knode->managed = managed; RWLOCK(&keytable->rwlock, isc_rwlocktype_write); node = NULL; result = dns_rbt_addnode(keytable->table, keyname, &node); - if (result == ISC_R_SUCCESS || result == ISC_R_EXISTS) { - knode->magic = KEYNODE_MAGIC; - knode->key = *keyp; - knode->next = node->data; + if (keyp != NULL) { + if (result == ISC_R_EXISTS) { + /* Key already in table? */ + dns_keynode_t *k; + for (k = node->data; k != NULL; k = k->next) { + if (k->key == NULL) { + k->key = *keyp; + break; + } + if (dst_key_compare(k->key, *keyp) == ISC_TRUE) + break; + } + + if (k == NULL) + result = ISC_R_SUCCESS; + else + dst_key_free(keyp); + } + + if (result == ISC_R_SUCCESS) { + knode->key = *keyp; + knode->next = node->data; + *keyp = NULL; + } + } + + if (result == ISC_R_SUCCESS) { node->data = knode; - *keyp = NULL; knode = NULL; - result = ISC_R_SUCCESS; } + /* Key was already there? That's the same as a success */ + if (result == ISC_R_EXISTS) + result = ISC_R_SUCCESS; + RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write); if (knode != NULL) - isc_mem_put(keytable->mctx, knode, sizeof(*knode)); + dns_keynode_detach(keytable->mctx, &knode); + + return (result); +} + +isc_result_t +dns_keytable_add(dns_keytable_t *keytable, isc_boolean_t managed, + dst_key_t **keyp) +{ + REQUIRE(keyp != NULL && *keyp != NULL); + return (insert(keytable, managed, dst_key_name(*keyp), keyp)); +} + +isc_result_t +dns_keytable_marksecure(dns_keytable_t *keytable, dns_name_t *name) { + return (insert(keytable, ISC_TRUE, name, NULL)); +} + +isc_result_t +dns_keytable_delete(dns_keytable_t *keytable, dns_name_t *keyname) { + isc_result_t result; + dns_rbtnode_t *node = NULL; + + REQUIRE(VALID_KEYTABLE(keytable)); + REQUIRE(keyname != NULL); + + RWLOCK(&keytable->rwlock, isc_rwlocktype_write); + result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL, + DNS_RBTFIND_NOOPTIONS, NULL, NULL); + if (result == ISC_R_SUCCESS) { + if (node->data != NULL) + result = dns_rbt_deletenode(keytable->table, + node, ISC_FALSE); + else + result = ISC_R_NOTFOUND; + } else if (result == DNS_R_PARTIALMATCH) + result = ISC_R_NOTFOUND; + RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write); return (result); } isc_result_t +dns_keytable_deletekeynode(dns_keytable_t *keytable, dst_key_t *dstkey) { + isc_result_t result; + dns_name_t *keyname; + dns_rbtnode_t *node = NULL; + dns_keynode_t *knode = NULL, **kprev = NULL; + + REQUIRE(VALID_KEYTABLE(keytable)); + REQUIRE(dstkey != NULL); + + keyname = dst_key_name(dstkey); + + RWLOCK(&keytable->rwlock, isc_rwlocktype_write); + result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL, + DNS_RBTFIND_NOOPTIONS, NULL, NULL); + + if (result == DNS_R_PARTIALMATCH) + result = ISC_R_NOTFOUND; + if (result != ISC_R_SUCCESS) + goto finish; + + if (node->data == NULL) { + result = ISC_R_NOTFOUND; + goto finish; + } + + knode = node->data; + if (knode->next == NULL && + (knode->key == NULL || + dst_key_compare(knode->key, dstkey) == ISC_TRUE)) { + result = dns_rbt_deletenode(keytable->table, node, ISC_FALSE); + goto finish; + } + + kprev = (dns_keynode_t **) &node->data; + while (knode != NULL) { + if (dst_key_compare(knode->key, dstkey) == ISC_TRUE) + break; + kprev = &knode->next; + knode = knode->next; + } + + if (knode != NULL) { + if (knode->key != NULL) + dst_key_free(&knode->key); + /* + * This is equivalent to: + * dns_keynode_attach(knode->next, &tmp); + * dns_keynode_detach(kprev); + * dns_keynode_attach(tmp, &kprev); + * dns_keynode_detach(&tmp); + */ + *kprev = knode->next; + knode->next = NULL; + dns_keynode_detach(keytable->mctx, &knode); + } else + result = DNS_R_PARTIALMATCH; + finish: + RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write); + return (result); +} + +isc_result_t +dns_keytable_find(dns_keytable_t *keytable, dns_name_t *keyname, + dns_keynode_t **keynodep) +{ + isc_result_t result; + dns_rbtnode_t *node = NULL; + + REQUIRE(VALID_KEYTABLE(keytable)); + REQUIRE(keyname != NULL); + REQUIRE(keynodep != NULL && *keynodep == NULL); + + RWLOCK(&keytable->rwlock, isc_rwlocktype_read); + result = dns_rbt_findnode(keytable->table, keyname, NULL, &node, NULL, + DNS_RBTFIND_NOOPTIONS, NULL, NULL); + if (result == ISC_R_SUCCESS) { + if (node->data != NULL) { + LOCK(&keytable->lock); + keytable->active_nodes++; + UNLOCK(&keytable->lock); + dns_keynode_attach(node->data, keynodep); + } else + result = ISC_R_NOTFOUND; + } else if (result == DNS_R_PARTIALMATCH) + result = ISC_R_NOTFOUND; + RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read); + + return (result); +} + +isc_result_t +dns_keytable_nextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode, + dns_keynode_t **nextnodep) +{ + /* + * Return the next key after 'keynode', regardless of + * properties. + */ + + REQUIRE(VALID_KEYTABLE(keytable)); + REQUIRE(VALID_KEYNODE(keynode)); + REQUIRE(nextnodep != NULL && *nextnodep == NULL); + + if (keynode->next == NULL) + return (ISC_R_NOTFOUND); + + dns_keynode_attach(keynode->next, nextnodep); + LOCK(&keytable->lock); + keytable->active_nodes++; + UNLOCK(&keytable->lock); + + return (ISC_R_SUCCESS); +} + +isc_result_t dns_keytable_findkeynode(dns_keytable_t *keytable, dns_name_t *name, dns_secalg_t algorithm, dns_keytag_t tag, dns_keynode_t **keynodep) @@ -250,6 +394,10 @@ dns_keytable_findkeynode(dns_keytable_t *keytable, dns_name_t *name, if (result == ISC_R_SUCCESS) { INSIST(data != NULL); for (knode = data; knode != NULL; knode = knode->next) { + if (knode->key == NULL) { + knode = NULL; + break; + } if (algorithm == dst_key_alg(knode->key) && tag == dst_key_id(knode->key)) break; @@ -258,7 +406,7 @@ dns_keytable_findkeynode(dns_keytable_t *keytable, dns_name_t *name, LOCK(&keytable->lock); keytable->active_nodes++; UNLOCK(&keytable->lock); - *keynodep = knode; + dns_keynode_attach(knode, keynodep); } else result = DNS_R_PARTIALMATCH; } else if (result == DNS_R_PARTIALMATCH) @@ -286,6 +434,10 @@ dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode, REQUIRE(nextnodep != NULL && *nextnodep == NULL); for (knode = keynode->next; knode != NULL; knode = knode->next) { + if (knode->key == NULL) { + knode = NULL; + break; + } if (dst_key_alg(keynode->key) == dst_key_alg(knode->key) && dst_key_id(keynode->key) == dst_key_id(knode->key)) break; @@ -295,7 +447,7 @@ dns_keytable_findnextkeynode(dns_keytable_t *keytable, dns_keynode_t *keynode, keytable->active_nodes++; UNLOCK(&keytable->lock); result = ISC_R_SUCCESS; - *nextnodep = knode; + dns_keynode_attach(knode, nextnodep); } else result = ISC_R_NOTFOUND; @@ -331,6 +483,25 @@ dns_keytable_finddeepestmatch(dns_keytable_t *keytable, dns_name_t *name, } void +dns_keytable_attachkeynode(dns_keytable_t *keytable, dns_keynode_t *source, + dns_keynode_t **target) +{ + /* + * Give back a keynode found via dns_keytable_findkeynode(). + */ + + REQUIRE(VALID_KEYTABLE(keytable)); + REQUIRE(VALID_KEYNODE(source)); + REQUIRE(target != NULL && *target == NULL); + + LOCK(&keytable->lock); + keytable->active_nodes++; + UNLOCK(&keytable->lock); + + dns_keynode_attach(source, target); +} + +void dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep) { /* @@ -345,7 +516,7 @@ dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep) keytable->active_nodes--; UNLOCK(&keytable->lock); - *keynodep = NULL; + dns_keynode_detach(keytable->mctx, keynodep); } isc_result_t @@ -382,6 +553,44 @@ dns_keytable_issecuredomain(dns_keytable_t *keytable, dns_name_t *name, return (result); } +isc_result_t +dns_keytable_dump(dns_keytable_t *keytable, FILE *fp) +{ + isc_result_t result; + dns_keynode_t *knode; + dns_rbtnode_t *node; + dns_rbtnodechain_t chain; + + REQUIRE(VALID_KEYTABLE(keytable)); + + RWLOCK(&keytable->rwlock, isc_rwlocktype_read); + dns_rbtnodechain_init(&chain, keytable->mctx); + result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL); + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) + goto cleanup; + for (;;) { + char pbuf[DST_KEY_FORMATSIZE]; + + dns_rbtnodechain_current(&chain, NULL, NULL, &node); + for (knode = node->data; knode != NULL; knode = knode->next) { + dst_key_format(knode->key, pbuf, sizeof(pbuf)); + fprintf(fp, "%s ; %s\n", pbuf, + knode->managed ? "managed" : "trusted"); + } + result = dns_rbtnodechain_next(&chain, NULL, NULL); + if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { + if (result == ISC_R_NOMORE) + result = ISC_R_SUCCESS; + break; + } + } + + cleanup: + dns_rbtnodechain_invalidate(&chain); + RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read); + return (result); +} + dst_key_t * dns_keynode_key(dns_keynode_t *keynode) { @@ -393,3 +602,71 @@ dns_keynode_key(dns_keynode_t *keynode) { return (keynode->key); } + +isc_boolean_t +dns_keynode_managed(dns_keynode_t *keynode) { + /* + * Is this a managed key? + */ + REQUIRE(VALID_KEYNODE(keynode)); + + return (keynode->managed); +} + +isc_result_t +dns_keynode_create(isc_mem_t *mctx, dns_keynode_t **target) { + isc_result_t result; + dns_keynode_t *knode = NULL; + + REQUIRE(target != NULL && *target == NULL); + + knode = isc_mem_get(mctx, sizeof(dns_keynode_t)); + if (knode == NULL) + return (ISC_R_NOMEMORY); + + knode->magic = KEYNODE_MAGIC; + knode->managed = ISC_FALSE; + knode->key = NULL; + knode->next = NULL; + + result = isc_refcount_init(&knode->refcount, 1); + if (result != ISC_R_SUCCESS) + return (result); + + *target = knode; + return (ISC_R_SUCCESS); +} + +void +dns_keynode_attach(dns_keynode_t *source, dns_keynode_t **target) { + REQUIRE(VALID_KEYNODE(source)); + isc_refcount_increment(&source->refcount, NULL); + *target = source; +} + +void +dns_keynode_detach(isc_mem_t *mctx, dns_keynode_t **keynode) { + unsigned int refs; + dns_keynode_t *node = *keynode; + REQUIRE(VALID_KEYNODE(node)); + isc_refcount_decrement(&node->refcount, &refs); + if (refs == 0) { + if (node->key != NULL) + dst_key_free(&node->key); + isc_refcount_destroy(&node->refcount); + isc_mem_put(mctx, node, sizeof(dns_keynode_t)); + } + *keynode = NULL; +} + +void +dns_keynode_detachall(isc_mem_t *mctx, dns_keynode_t **keynode) { + dns_keynode_t *next = NULL, *node = *keynode; + REQUIRE(VALID_KEYNODE(node)); + while (node != NULL) { + next = node->next; + dns_keynode_detach(mctx, &node); + node = next; + } + *keynode = NULL; +} |