mirror of
https://github.com/isc-projects/bind9.git
synced 2026-02-28 12:31:29 -05:00
BIND's rdataset structure is a view of some DNS records. It is polymorphic, so the details of how the records are stored can vary. For instance, the records can be held in an rdatalist, or in an rdataslab in the rbtdb. The dns_rdataset structure previously had a number of fields called `private1` up to `private7`, which were used by the various rdataset implementations. It was not at all clear what these fields were for, without reading the code and working it out from context. This change makes the rdataset inheritance hierarchy more clear. The polymorphic part of a `struct dns_rdataset` is now a union of structs, each of which is named for the class of implementation using it. The fields of these structs replace the old `privateN` fields. (Note: the term "inheritance hierarchy" refers to the fact that the builtin and SDLZ implementations are based on and inherit from the rdatalist implementation, which in turn inherits from the generic rdataset. Most of this change is mechanical, but there are a few extras. In keynode.c there were a number of REQUIRE()ments that were not necessary: they had already been checked by the rdataset method dispatch code. On the other hand, In ncache.c there was a public function which needed to REQUIRE() that an rdataset was valid. I have removed lots of "reset iterator state" comments, because it should now be clear from `target->iter = NULL` where before `target->private5 = NULL` could have been doing anything. Initialization is a bit neater in a few places, using C structure literals where appropriate. The pointer arithmetic for translating between an rdataslab header and its raw contents is now fractionally safer.
922 lines
23 KiB
C
922 lines
23 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <isc/mem.h>
|
|
#include <isc/refcount.h>
|
|
#include <isc/result.h>
|
|
#include <isc/rwlock.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/dnssec.h>
|
|
#include <dns/fixedname.h>
|
|
#include <dns/keytable.h>
|
|
#include <dns/rbt.h>
|
|
#include <dns/rdata.h>
|
|
#include <dns/rdatalist.h>
|
|
#include <dns/rdataset.h>
|
|
#include <dns/rdatastruct.h>
|
|
|
|
#define KEYTABLE_MAGIC ISC_MAGIC('K', 'T', 'b', 'l')
|
|
#define VALID_KEYTABLE(kt) ISC_MAGIC_VALID(kt, KEYTABLE_MAGIC)
|
|
|
|
#define KEYNODE_MAGIC ISC_MAGIC('K', 'N', 'o', 'd')
|
|
#define VALID_KEYNODE(kn) ISC_MAGIC_VALID(kn, KEYNODE_MAGIC)
|
|
|
|
struct dns_keytable {
|
|
/* Unlocked. */
|
|
unsigned int magic;
|
|
isc_mem_t *mctx;
|
|
isc_refcount_t references;
|
|
isc_rwlock_t rwlock;
|
|
/* Locked by rwlock. */
|
|
dns_rbt_t *table;
|
|
};
|
|
|
|
struct dns_keynode {
|
|
unsigned int magic;
|
|
isc_mem_t *mctx;
|
|
isc_refcount_t refcount;
|
|
isc_rwlock_t rwlock;
|
|
dns_rdatalist_t *dslist;
|
|
dns_rdataset_t dsset;
|
|
bool managed;
|
|
bool initial;
|
|
};
|
|
|
|
static dns_keynode_t *
|
|
new_keynode(dns_rdata_ds_t *ds, dns_keytable_t *keytable, bool managed,
|
|
bool initial);
|
|
|
|
static void
|
|
keynode_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG);
|
|
static isc_result_t
|
|
keynode_first(dns_rdataset_t *rdataset);
|
|
static isc_result_t
|
|
keynode_next(dns_rdataset_t *rdataset);
|
|
static void
|
|
keynode_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);
|
|
static void
|
|
keynode_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG);
|
|
|
|
static dns_rdatasetmethods_t methods = {
|
|
.disassociate = keynode_disassociate,
|
|
.first = keynode_first,
|
|
.next = keynode_next,
|
|
.current = keynode_current,
|
|
.clone = keynode_clone,
|
|
};
|
|
|
|
static void
|
|
keynode_attach(dns_keynode_t *source, dns_keynode_t **target) {
|
|
REQUIRE(VALID_KEYNODE(source));
|
|
isc_refcount_increment(&source->refcount);
|
|
*target = source;
|
|
}
|
|
|
|
static void
|
|
keynode_detach(isc_mem_t *mctx, dns_keynode_t **keynodep) {
|
|
REQUIRE(keynodep != NULL && VALID_KEYNODE(*keynodep));
|
|
dns_keynode_t *knode = *keynodep;
|
|
*keynodep = NULL;
|
|
|
|
if (isc_refcount_decrement(&knode->refcount) == 1) {
|
|
dns_rdata_t *rdata = NULL;
|
|
isc_refcount_destroy(&knode->refcount);
|
|
isc_rwlock_destroy(&knode->rwlock);
|
|
if (knode->dslist != NULL) {
|
|
for (rdata = ISC_LIST_HEAD(knode->dslist->rdata);
|
|
rdata != NULL;
|
|
rdata = ISC_LIST_HEAD(knode->dslist->rdata))
|
|
{
|
|
ISC_LIST_UNLINK(knode->dslist->rdata, rdata,
|
|
link);
|
|
isc_mem_put(mctx, rdata->data,
|
|
DNS_DS_BUFFERSIZE);
|
|
isc_mem_put(mctx, rdata, sizeof(*rdata));
|
|
}
|
|
|
|
isc_mem_put(mctx, knode->dslist,
|
|
sizeof(*knode->dslist));
|
|
knode->dslist = NULL;
|
|
}
|
|
isc_mem_putanddetach(&knode->mctx, knode,
|
|
sizeof(dns_keynode_t));
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_keynode(void *node, void *arg) {
|
|
dns_keynode_t *keynode = node;
|
|
isc_mem_t *mctx = arg;
|
|
|
|
keynode_detach(mctx, &keynode);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_create(isc_mem_t *mctx, dns_keytable_t **keytablep) {
|
|
dns_keytable_t *keytable;
|
|
isc_result_t result;
|
|
|
|
/*
|
|
* Create a keytable.
|
|
*/
|
|
|
|
REQUIRE(keytablep != NULL && *keytablep == NULL);
|
|
|
|
keytable = isc_mem_get(mctx, sizeof(*keytable));
|
|
|
|
keytable->table = NULL;
|
|
result = dns_rbt_create(mctx, free_keynode, mctx, &keytable->table);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto cleanup_keytable;
|
|
}
|
|
|
|
isc_rwlock_init(&keytable->rwlock);
|
|
isc_refcount_init(&keytable->references, 1);
|
|
|
|
keytable->mctx = NULL;
|
|
isc_mem_attach(mctx, &keytable->mctx);
|
|
keytable->magic = KEYTABLE_MAGIC;
|
|
*keytablep = keytable;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
cleanup_keytable:
|
|
isc_mem_putanddetach(&mctx, keytable, sizeof(*keytable));
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_keytable_attach(dns_keytable_t *source, dns_keytable_t **targetp) {
|
|
REQUIRE(VALID_KEYTABLE(source));
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
isc_refcount_increment(&source->references);
|
|
|
|
*targetp = source;
|
|
}
|
|
|
|
void
|
|
dns_keytable_detach(dns_keytable_t **keytablep) {
|
|
REQUIRE(keytablep != NULL && VALID_KEYTABLE(*keytablep));
|
|
dns_keytable_t *keytable = *keytablep;
|
|
*keytablep = NULL;
|
|
|
|
if (isc_refcount_decrement(&keytable->references) == 1) {
|
|
isc_refcount_destroy(&keytable->references);
|
|
dns_rbt_destroy(&keytable->table);
|
|
isc_rwlock_destroy(&keytable->rwlock);
|
|
keytable->magic = 0;
|
|
isc_mem_putanddetach(&keytable->mctx, keytable,
|
|
sizeof(*keytable));
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_ds(dns_keynode_t *knode, dns_rdata_ds_t *ds, isc_mem_t *mctx) {
|
|
isc_result_t result;
|
|
dns_rdata_t *dsrdata = NULL, *rdata = NULL;
|
|
void *data = NULL;
|
|
bool exists = false;
|
|
isc_buffer_t b;
|
|
|
|
dsrdata = isc_mem_get(mctx, sizeof(*dsrdata));
|
|
dns_rdata_init(dsrdata);
|
|
|
|
data = isc_mem_get(mctx, DNS_DS_BUFFERSIZE);
|
|
isc_buffer_init(&b, data, DNS_DS_BUFFERSIZE);
|
|
|
|
result = dns_rdata_fromstruct(dsrdata, dns_rdataclass_in,
|
|
dns_rdatatype_ds, ds, &b);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
RWLOCK(&knode->rwlock, isc_rwlocktype_write);
|
|
|
|
if (knode->dslist == NULL) {
|
|
knode->dslist = isc_mem_get(mctx, sizeof(*knode->dslist));
|
|
dns_rdatalist_init(knode->dslist);
|
|
knode->dslist->rdclass = dns_rdataclass_in;
|
|
knode->dslist->type = dns_rdatatype_ds;
|
|
|
|
INSIST(knode->dsset.methods == NULL);
|
|
knode->dsset.methods = &methods;
|
|
knode->dsset.rdclass = knode->dslist->rdclass;
|
|
knode->dsset.type = knode->dslist->type;
|
|
knode->dsset.covers = knode->dslist->covers;
|
|
knode->dsset.ttl = knode->dslist->ttl;
|
|
knode->dsset.keytable.node = knode;
|
|
knode->dsset.keytable.iter = NULL;
|
|
knode->dsset.trust = dns_trust_ultimate;
|
|
}
|
|
|
|
for (rdata = ISC_LIST_HEAD(knode->dslist->rdata); rdata != NULL;
|
|
rdata = ISC_LIST_NEXT(rdata, link))
|
|
{
|
|
if (dns_rdata_compare(rdata, dsrdata) == 0) {
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (exists) {
|
|
isc_mem_put(mctx, dsrdata->data, DNS_DS_BUFFERSIZE);
|
|
isc_mem_put(mctx, dsrdata, sizeof(*dsrdata));
|
|
} else {
|
|
ISC_LIST_APPEND(knode->dslist->rdata, dsrdata, link);
|
|
}
|
|
|
|
RWUNLOCK(&knode->rwlock, isc_rwlocktype_write);
|
|
}
|
|
|
|
static isc_result_t
|
|
delete_ds(dns_keytable_t *keytable, dns_rbtnode_t *node, dns_rdata_ds_t *ds) {
|
|
dns_keynode_t *knode = node->data;
|
|
isc_result_t result;
|
|
dns_rdata_t dsrdata = DNS_RDATA_INIT;
|
|
dns_rdata_t *rdata = NULL;
|
|
unsigned char data[DNS_DS_BUFFERSIZE];
|
|
bool found = false;
|
|
isc_buffer_t b;
|
|
|
|
RWLOCK(&knode->rwlock, isc_rwlocktype_read);
|
|
if (knode->dslist == NULL) {
|
|
RWUNLOCK(&knode->rwlock, isc_rwlocktype_read);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_buffer_init(&b, data, DNS_DS_BUFFERSIZE);
|
|
|
|
result = dns_rdata_fromstruct(&dsrdata, dns_rdataclass_in,
|
|
dns_rdatatype_ds, ds, &b);
|
|
if (result != ISC_R_SUCCESS) {
|
|
RWUNLOCK(&knode->rwlock, isc_rwlocktype_write);
|
|
return (result);
|
|
}
|
|
|
|
for (rdata = ISC_LIST_HEAD(knode->dslist->rdata); rdata != NULL;
|
|
rdata = ISC_LIST_NEXT(rdata, link))
|
|
{
|
|
if (dns_rdata_compare(rdata, &dsrdata) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
RWUNLOCK(&knode->rwlock, isc_rwlocktype_read);
|
|
/*
|
|
* The keyname must have matched or we wouldn't be here,
|
|
* so we use DNS_R_PARTIALMATCH instead of ISC_R_NOTFOUND.
|
|
*/
|
|
return (DNS_R_PARTIALMATCH);
|
|
}
|
|
|
|
/*
|
|
* Replace knode with a new instance without the DS.
|
|
*/
|
|
node->data = new_keynode(NULL, keytable, knode->managed,
|
|
knode->initial);
|
|
for (rdata = ISC_LIST_HEAD(knode->dslist->rdata); rdata != NULL;
|
|
rdata = ISC_LIST_NEXT(rdata, link))
|
|
{
|
|
if (dns_rdata_compare(rdata, &dsrdata) != 0) {
|
|
dns_rdata_ds_t ds0;
|
|
result = dns_rdata_tostruct(rdata, &ds0, NULL);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
add_ds(node->data, &ds0, keytable->mctx);
|
|
}
|
|
}
|
|
RWUNLOCK(&knode->rwlock, isc_rwlocktype_read);
|
|
|
|
keynode_detach(keytable->mctx, &knode);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*%
|
|
* Create a keynode for "ds" (or a null key node if "ds" is NULL), set
|
|
* "managed" and "initial" as requested and attach the keynode to
|
|
* to "node" in "keytable".
|
|
*/
|
|
static dns_keynode_t *
|
|
new_keynode(dns_rdata_ds_t *ds, dns_keytable_t *keytable, bool managed,
|
|
bool initial) {
|
|
dns_keynode_t *knode = NULL;
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
REQUIRE(!initial || managed);
|
|
|
|
knode = isc_mem_get(keytable->mctx, sizeof(dns_keynode_t));
|
|
*knode = (dns_keynode_t){ .magic = KEYNODE_MAGIC };
|
|
|
|
dns_rdataset_init(&knode->dsset);
|
|
isc_refcount_init(&knode->refcount, 1);
|
|
isc_rwlock_init(&knode->rwlock);
|
|
|
|
/*
|
|
* If a DS was supplied, initialize an rdatalist.
|
|
*/
|
|
if (ds != NULL) {
|
|
add_ds(knode, ds, keytable->mctx);
|
|
}
|
|
|
|
isc_mem_attach(keytable->mctx, &knode->mctx);
|
|
knode->managed = managed;
|
|
knode->initial = initial;
|
|
|
|
return (knode);
|
|
}
|
|
|
|
/*%
|
|
* Add key trust anchor "ds" at "keyname" in "keytable". If an anchor
|
|
* already exists at the requested name does not contain "ds", update it.
|
|
* If "ds" is NULL, add a null key to indicate that "keyname" should be
|
|
* treated as a secure domain without supplying key data which would allow
|
|
* the domain to be validated.
|
|
*/
|
|
static isc_result_t
|
|
insert(dns_keytable_t *keytable, bool managed, bool initial,
|
|
const dns_name_t *keyname, dns_rdata_ds_t *ds,
|
|
dns_keytable_callback_t callback, void *callback_arg) {
|
|
dns_rbtnode_t *node = NULL;
|
|
isc_result_t result;
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
|
|
RWLOCK(&keytable->rwlock, isc_rwlocktype_write);
|
|
|
|
result = dns_rbt_addnode(keytable->table, keyname, &node);
|
|
if (result == ISC_R_SUCCESS) {
|
|
/*
|
|
* There was no node for "keyname" in "keytable" yet, so one
|
|
* was created. Create a new key node for the supplied
|
|
* trust anchor (or a null key node if "ds" is NULL)
|
|
* and attach it to the created node.
|
|
*/
|
|
node->data = new_keynode(ds, keytable, managed, initial);
|
|
if (callback != NULL) {
|
|
(*callback)(keyname, callback_arg);
|
|
}
|
|
} else if (result == ISC_R_EXISTS) {
|
|
/*
|
|
* A node already exists for "keyname" in "keytable".
|
|
*/
|
|
if (ds != NULL) {
|
|
dns_keynode_t *knode = node->data;
|
|
if (knode == NULL) {
|
|
node->data = new_keynode(ds, keytable, managed,
|
|
initial);
|
|
if (callback != NULL) {
|
|
(*callback)(keyname, callback_arg);
|
|
}
|
|
} else {
|
|
add_ds(knode, ds, keytable->mctx);
|
|
}
|
|
}
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
|
|
RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_add(dns_keytable_t *keytable, bool managed, bool initial,
|
|
dns_name_t *name, dns_rdata_ds_t *ds,
|
|
dns_keytable_callback_t callback, void *callback_arg) {
|
|
REQUIRE(ds != NULL);
|
|
REQUIRE(!initial || managed);
|
|
|
|
return (insert(keytable, managed, initial, name, ds, callback,
|
|
callback_arg));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_marksecure(dns_keytable_t *keytable, const dns_name_t *name) {
|
|
return (insert(keytable, true, false, name, NULL, NULL, NULL));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_delete(dns_keytable_t *keytable, const dns_name_t *keyname,
|
|
dns_keytable_callback_t callback, void *callback_arg) {
|
|
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,
|
|
false);
|
|
if (callback != NULL) {
|
|
(*callback)(keyname, callback_arg);
|
|
}
|
|
} 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_deletekey(dns_keytable_t *keytable, const dns_name_t *keyname,
|
|
dns_rdata_dnskey_t *dnskey) {
|
|
isc_result_t result;
|
|
dns_rbtnode_t *node = NULL;
|
|
dns_keynode_t *knode = NULL;
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
unsigned char data[4096], digest[DNS_DS_BUFFERSIZE];
|
|
dns_rdata_ds_t ds;
|
|
isc_buffer_t b;
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
REQUIRE(dnskey != NULL);
|
|
|
|
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;
|
|
|
|
RWLOCK(&knode->rwlock, isc_rwlocktype_read);
|
|
if (knode->dslist == NULL) {
|
|
RWUNLOCK(&knode->rwlock, isc_rwlocktype_read);
|
|
result = DNS_R_PARTIALMATCH;
|
|
goto finish;
|
|
}
|
|
RWUNLOCK(&knode->rwlock, isc_rwlocktype_read);
|
|
|
|
isc_buffer_init(&b, data, sizeof(data));
|
|
result = dns_rdata_fromstruct(&rdata, dnskey->common.rdclass,
|
|
dns_rdatatype_dnskey, dnskey, &b);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto finish;
|
|
}
|
|
|
|
result = dns_ds_fromkeyrdata(keyname, &rdata, DNS_DSDIGEST_SHA256,
|
|
digest, &ds);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto finish;
|
|
}
|
|
|
|
result = delete_ds(keytable, node, &ds);
|
|
|
|
finish:
|
|
RWUNLOCK(&keytable->rwlock, isc_rwlocktype_write);
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_find(dns_keytable_t *keytable, const 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) {
|
|
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_finddeepestmatch(dns_keytable_t *keytable, const dns_name_t *name,
|
|
dns_name_t *foundname) {
|
|
isc_result_t result;
|
|
void *data;
|
|
|
|
/*
|
|
* Search for the deepest match in 'keytable'.
|
|
*/
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
REQUIRE(foundname != NULL);
|
|
|
|
RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
|
|
|
|
data = NULL;
|
|
result = dns_rbt_findname(keytable->table, name, 0, foundname, &data);
|
|
|
|
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
|
|
RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_keytable_detachkeynode(dns_keytable_t *keytable, dns_keynode_t **keynodep) {
|
|
/*
|
|
* Give back a keynode found via dns_keytable_findkeynode().
|
|
*/
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
REQUIRE(keynodep != NULL && VALID_KEYNODE(*keynodep));
|
|
|
|
keynode_detach(keytable->mctx, keynodep);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_issecuredomain(dns_keytable_t *keytable, const dns_name_t *name,
|
|
dns_name_t *foundname, bool *wantdnssecp) {
|
|
isc_result_t result;
|
|
dns_rbtnode_t *node = NULL;
|
|
|
|
/*
|
|
* Is 'name' at or beneath a trusted key?
|
|
*/
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
REQUIRE(dns_name_isabsolute(name));
|
|
REQUIRE(wantdnssecp != NULL);
|
|
|
|
RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
|
|
|
|
result = dns_rbt_findnode(keytable->table, name, foundname, &node, NULL,
|
|
DNS_RBTFIND_NOOPTIONS, NULL, NULL);
|
|
if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
|
|
INSIST(node->data != NULL);
|
|
*wantdnssecp = true;
|
|
result = ISC_R_SUCCESS;
|
|
} else if (result == ISC_R_NOTFOUND) {
|
|
*wantdnssecp = false;
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
|
|
RWUNLOCK(&keytable->rwlock, isc_rwlocktype_read);
|
|
|
|
return (result);
|
|
}
|
|
|
|
static isc_result_t
|
|
putstr(isc_buffer_t **b, const char *str) {
|
|
isc_result_t result;
|
|
|
|
result = isc_buffer_reserve(*b, strlen(str));
|
|
if (result != ISC_R_SUCCESS) {
|
|
return (result);
|
|
}
|
|
|
|
isc_buffer_putstr(*b, str);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_dump(dns_keytable_t *keytable, FILE *fp) {
|
|
isc_result_t result;
|
|
isc_buffer_t *text = NULL;
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
REQUIRE(fp != NULL);
|
|
|
|
isc_buffer_allocate(keytable->mctx, &text, 4096);
|
|
|
|
result = dns_keytable_totext(keytable, &text);
|
|
|
|
if (isc_buffer_usedlength(text) != 0) {
|
|
(void)putstr(&text, "\n");
|
|
} else if (result == ISC_R_SUCCESS) {
|
|
(void)putstr(&text, "none");
|
|
} else {
|
|
(void)putstr(&text, "could not dump key table: ");
|
|
(void)putstr(&text, isc_result_totext(result));
|
|
}
|
|
|
|
fprintf(fp, "%.*s", (int)isc_buffer_usedlength(text),
|
|
(char *)isc_buffer_base(text));
|
|
|
|
isc_buffer_free(&text);
|
|
return (result);
|
|
}
|
|
|
|
static isc_result_t
|
|
keynode_dslist_totext(dns_name_t *name, dns_keynode_t *keynode,
|
|
isc_buffer_t **text) {
|
|
isc_result_t result;
|
|
char namebuf[DNS_NAME_FORMATSIZE];
|
|
char obuf[DNS_NAME_FORMATSIZE + 200];
|
|
dns_rdataset_t dsset;
|
|
|
|
dns_name_format(name, namebuf, sizeof(namebuf));
|
|
|
|
dns_rdataset_init(&dsset);
|
|
if (!dns_keynode_dsset(keynode, &dsset)) {
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
for (result = dns_rdataset_first(&dsset); result == ISC_R_SUCCESS;
|
|
result = dns_rdataset_next(&dsset))
|
|
{
|
|
char algbuf[DNS_SECALG_FORMATSIZE];
|
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
|
dns_rdata_ds_t ds;
|
|
|
|
dns_rdataset_current(&dsset, &rdata);
|
|
result = dns_rdata_tostruct(&rdata, &ds, NULL);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
|
|
dns_secalg_format(ds.algorithm, algbuf, sizeof(algbuf));
|
|
|
|
RWLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
snprintf(obuf, sizeof(obuf), "%s/%s/%d ; %s%s\n", namebuf,
|
|
algbuf, ds.key_tag,
|
|
keynode->initial ? "initializing " : "",
|
|
keynode->managed ? "managed" : "static");
|
|
RWUNLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
|
|
result = putstr(text, obuf);
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_rdataset_disassociate(&dsset);
|
|
return (result);
|
|
}
|
|
}
|
|
dns_rdataset_disassociate(&dsset);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_totext(dns_keytable_t *keytable, isc_buffer_t **text) {
|
|
isc_result_t result;
|
|
dns_keynode_t *knode;
|
|
dns_rbtnode_t *node;
|
|
dns_rbtnodechain_t chain;
|
|
dns_name_t *foundname, *origin, *fullname;
|
|
dns_fixedname_t fixedfoundname, fixedorigin, fixedfullname;
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
REQUIRE(text != NULL && *text != NULL);
|
|
|
|
origin = dns_fixedname_initname(&fixedorigin);
|
|
fullname = dns_fixedname_initname(&fixedfullname);
|
|
foundname = dns_fixedname_initname(&fixedfoundname);
|
|
|
|
RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
|
|
dns_rbtnodechain_init(&chain);
|
|
result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL);
|
|
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
|
|
if (result == ISC_R_NOTFOUND) {
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
for (;;) {
|
|
dns_rbtnodechain_current(&chain, foundname, origin, &node);
|
|
|
|
knode = node->data;
|
|
if (knode != NULL && knode->dslist != NULL) {
|
|
result = dns_name_concatenate(foundname, origin,
|
|
fullname, NULL);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
|
|
result = keynode_dslist_totext(fullname, knode, text);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_keytable_forall(dns_keytable_t *keytable,
|
|
void (*func)(dns_keytable_t *, dns_keynode_t *,
|
|
dns_name_t *, void *),
|
|
void *arg) {
|
|
isc_result_t result;
|
|
dns_rbtnode_t *node;
|
|
dns_rbtnodechain_t chain;
|
|
dns_fixedname_t fixedfoundname, fixedorigin, fixedfullname;
|
|
dns_name_t *foundname, *origin, *fullname;
|
|
|
|
REQUIRE(VALID_KEYTABLE(keytable));
|
|
|
|
origin = dns_fixedname_initname(&fixedorigin);
|
|
fullname = dns_fixedname_initname(&fixedfullname);
|
|
foundname = dns_fixedname_initname(&fixedfoundname);
|
|
|
|
RWLOCK(&keytable->rwlock, isc_rwlocktype_read);
|
|
dns_rbtnodechain_init(&chain);
|
|
result = dns_rbtnodechain_first(&chain, keytable->table, NULL, NULL);
|
|
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
|
|
if (result == ISC_R_NOTFOUND) {
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
for (;;) {
|
|
dns_rbtnodechain_current(&chain, foundname, origin, &node);
|
|
if (node->data != NULL) {
|
|
result = dns_name_concatenate(foundname, origin,
|
|
fullname, NULL);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
(*func)(keytable, node->data, fullname, arg);
|
|
}
|
|
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);
|
|
}
|
|
|
|
bool
|
|
dns_keynode_dsset(dns_keynode_t *keynode, dns_rdataset_t *rdataset) {
|
|
bool result;
|
|
REQUIRE(VALID_KEYNODE(keynode));
|
|
REQUIRE(rdataset == NULL || DNS_RDATASET_VALID(rdataset));
|
|
|
|
RWLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
if (keynode->dslist != NULL) {
|
|
if (rdataset != NULL) {
|
|
keynode_clone(&keynode->dsset,
|
|
rdataset DNS__DB_FILELINE);
|
|
}
|
|
result = true;
|
|
} else {
|
|
result = false;
|
|
}
|
|
RWUNLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
return (result);
|
|
}
|
|
|
|
bool
|
|
dns_keynode_managed(dns_keynode_t *keynode) {
|
|
bool managed;
|
|
|
|
REQUIRE(VALID_KEYNODE(keynode));
|
|
|
|
RWLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
managed = keynode->managed;
|
|
RWUNLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
|
|
return (managed);
|
|
}
|
|
|
|
bool
|
|
dns_keynode_initial(dns_keynode_t *keynode) {
|
|
bool initial;
|
|
|
|
REQUIRE(VALID_KEYNODE(keynode));
|
|
|
|
RWLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
initial = keynode->initial;
|
|
RWUNLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
|
|
return (initial);
|
|
}
|
|
|
|
void
|
|
dns_keynode_trust(dns_keynode_t *keynode) {
|
|
REQUIRE(VALID_KEYNODE(keynode));
|
|
|
|
RWLOCK(&keynode->rwlock, isc_rwlocktype_write);
|
|
keynode->initial = false;
|
|
RWUNLOCK(&keynode->rwlock, isc_rwlocktype_write);
|
|
}
|
|
|
|
static void
|
|
keynode_disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) {
|
|
dns_keynode_t *keynode;
|
|
|
|
rdataset->methods = NULL;
|
|
keynode = rdataset->keytable.node;
|
|
rdataset->keytable.node = NULL;
|
|
|
|
keynode_detach(keynode->mctx, &keynode);
|
|
}
|
|
|
|
static isc_result_t
|
|
keynode_first(dns_rdataset_t *rdataset) {
|
|
dns_keynode_t *keynode;
|
|
|
|
keynode = rdataset->keytable.node;
|
|
RWLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
rdataset->keytable.iter = ISC_LIST_HEAD(keynode->dslist->rdata);
|
|
RWUNLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
|
|
if (rdataset->keytable.iter == NULL) {
|
|
return (ISC_R_NOMORE);
|
|
}
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static isc_result_t
|
|
keynode_next(dns_rdataset_t *rdataset) {
|
|
dns_keynode_t *keynode;
|
|
dns_rdata_t *rdata;
|
|
|
|
rdata = rdataset->keytable.iter;
|
|
if (rdata == NULL) {
|
|
return (ISC_R_NOMORE);
|
|
}
|
|
|
|
keynode = rdataset->keytable.node;
|
|
RWLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
rdataset->keytable.iter = ISC_LIST_NEXT(rdata, link);
|
|
RWUNLOCK(&keynode->rwlock, isc_rwlocktype_read);
|
|
|
|
if (rdataset->keytable.iter == NULL) {
|
|
return (ISC_R_NOMORE);
|
|
}
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
keynode_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
|
|
dns_rdata_t *list_rdata;
|
|
|
|
list_rdata = rdataset->keytable.iter;
|
|
INSIST(list_rdata != NULL);
|
|
|
|
dns_rdata_clone(list_rdata, rdata);
|
|
}
|
|
|
|
static void
|
|
keynode_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) {
|
|
dns_keynode_t *keynode;
|
|
|
|
keynode = source->keytable.node;
|
|
isc_refcount_increment(&keynode->refcount);
|
|
|
|
*target = *source;
|
|
target->keytable.iter = NULL;
|
|
}
|