2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-29 13:38:26 +00:00

automatic updating of SIGs and NXTs in secure zones

This commit is contained in:
Andreas Gustafsson 1999-09-09 08:58:00 +00:00
parent d1bf2d2df2
commit b2c71d98df

View File

@ -36,11 +36,14 @@
#include <dns/rdataset.h> #include <dns/rdataset.h>
#include <dns/rdatasetiter.h> #include <dns/rdatasetiter.h>
#include <dns/db.h> #include <dns/db.h>
#include <dns/dbiterator.h>
#include <dns/dbtable.h> #include <dns/dbtable.h>
#include <dns/message.h> #include <dns/message.h>
#include <dns/rdatastruct.h> #include <dns/rdatastruct.h>
#include <dns/journal.h> #include <dns/journal.h>
#include <dns/view.h> #include <dns/view.h>
#include <dns/dnssec.h>
#include <dns/nxt.h>
#include <named/globals.h> #include <named/globals.h>
#include <named/client.h> #include <named/client.h>
@ -66,6 +69,12 @@
#define CHECK(op) do { result = (op); \ #define CHECK(op) do { result = (op); \
if (result != DNS_R_SUCCESS) goto failure; \ if (result != DNS_R_SUCCESS) goto failure; \
} while (0) } while (0)
#define CHECKMSG(op, msg) do { result = (op); \
if (result != DNS_R_SUCCESS) { \
printf("%s\n", msg); \
goto failure; \
} \
} while (0)
typedef struct rr rr_t; typedef struct rr rr_t;
@ -80,6 +89,7 @@ struct rr {
/* /*
* Update a single RR in version 'ver' of 'db' and log the * Update a single RR in version 'ver' of 'db' and log the
* update in 'diff'. * update in 'diff'.
*
* Ensures: * Ensures:
* '*tuple' == NULL. Either the tuple is freed, or its * '*tuple' == NULL. Either the tuple is freed, or its
* ownership has been transferred to the diff. * ownership has been transferred to the diff.
@ -376,18 +386,6 @@ rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
RETURN_EXISTENCE_FLAG; RETURN_EXISTENCE_FLAG;
} }
/* Helper function for cname_incompatible_rrset_exists(). */
/*
* XXXRTH We should move this to rdata.c.
*/
static isc_boolean_t
is_dnssec_type(dns_rdatatype_t type) {
return ((type == dns_rdatatype_sig ||
type == dns_rdatatype_key ||
type == dns_rdatatype_nxt) ?
ISC_TRUE : ISC_FALSE);
}
/* Helper function for cname_incompatible_rrset_exists */ /* Helper function for cname_incompatible_rrset_exists */
static dns_result_t static dns_result_t
cname_compatibility_action(void *data, dns_rdataset_t *rrset) cname_compatibility_action(void *data, dns_rdataset_t *rrset)
@ -395,7 +393,7 @@ cname_compatibility_action(void *data, dns_rdataset_t *rrset)
{ {
data = data; /* Unused */ data = data; /* Unused */
if (rrset->type != dns_rdatatype_cname && if (rrset->type != dns_rdatatype_cname &&
! is_dnssec_type(rrset->type)) ! dns_rdatatype_isdnssec(rrset->type))
return (DNS_R_EXISTS); return (DNS_R_EXISTS);
return (DNS_R_SUCCESS); return (DNS_R_SUCCESS);
} }
@ -533,13 +531,11 @@ temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata)
dns_difftuple_t *tuple = NULL; dns_difftuple_t *tuple = NULL;
REQUIRE(VALID_DIFF(diff)); REQUIRE(VALID_DIFF(diff));
result = dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS, CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS,
name, 0, rdata, &tuple); name, 0, rdata, &tuple));
if (result != DNS_R_SUCCESS)
return (result);
ISC_LIST_APPEND(diff->tuples, tuple, link); ISC_LIST_APPEND(diff->tuples, tuple, link);
return (DNS_R_SUCCESS); failure:
return (result);
} }
/* /*
@ -979,6 +975,623 @@ check_soa_increment(dns_db_t *db, dns_dbversion_t *ver,
} }
/**************************************************************************/
/*
* Incremental updating of NXTs and SIGs.
*/
#define MAXZONEKEYS 32 /* Maximum number of zone keys supported. */
/*
* We abuse the dns_diff_t type to represent a set of domain names
* affected by the update.
*/
static dns_result_t
namelist_append_name(dns_diff_t *list, dns_name_t *name) {
dns_result_t result;
dns_difftuple_t *tuple = NULL;
static dns_rdata_t dummy_rdata = { NULL, 0, 0, 0, { NULL, NULL } };
CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0,
&dummy_rdata, &tuple));
dns_diff_append(list, &tuple);
failure:
return (result);
}
static dns_result_t
namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected)
{
dns_result_t result;
dns_fixedname_t fixedname;
dns_name_t *child;
dns_dbiterator_t *dbit = NULL;
dns_fixedname_init(&fixedname);
child = dns_fixedname_name(&fixedname);
CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
for (result = dns_dbiterator_seek(dbit, name);
result == DNS_R_SUCCESS;
result = dns_dbiterator_next(dbit))
{
dns_dbnode_t *node = NULL;
result = dns_dbiterator_current(dbit, &node, child);
dns_db_detachnode(db, &node);
CHECK(result);
if (! dns_name_issubdomain(child, name))
break;
CHECK(namelist_append_name(affected, child));
}
failure:
if (dbit != NULL)
dns_dbiterator_destroy(&dbit);
return (result);
}
/* Helper function for non_nxt_rrset_exists(). */
static dns_result_t
is_non_nxt_action(void *data, dns_rdataset_t *rrset)
/*ARGSUSED*/
{
data = data; /* Unused */
if (!(rrset->type == dns_rdatatype_nxt ||
(rrset->type == dns_rdatatype_sig &&
rrset->covers == dns_rdatatype_nxt)))
return (DNS_R_EXISTS);
return (DNS_R_SUCCESS);
}
/*
* Check whether there is an rrset other than a NXT or SIG NXT,
* i.e., anything that justifies the continued existence of a name
* after a secure update.
*
* If such an rrset exists, set '*exists' to ISC_TRUE.
* Otherwise, set it to ISC_FALSE.
*/
static dns_result_t
non_nxt_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
dns_name_t *name, isc_boolean_t *exists) {
dns_result_t result;
result = foreach_rrset(db, ver, name,
is_non_nxt_action, NULL);
RETURN_EXISTENCE_FLAG;
}
/*
* A comparison function for sorting dns_diff_t:s by name.
*/
static int
name_order(const void *av, const void *bv)
{
dns_difftuple_t * const *ap = av;
dns_difftuple_t * const *bp = bv;
dns_difftuple_t *a = *ap;
dns_difftuple_t *b = *bp;
return (dns_name_compare(&a->name, &b->name));
}
static dns_result_t
uniqify_name_list(dns_diff_t *list) {
dns_result_t result;
dns_difftuple_t *p, *q;
CHECK(dns_diff_sort(list, name_order));
p = ISC_LIST_HEAD(list->tuples);
while (p != NULL) {
while (1) {
q = ISC_LIST_NEXT(p, link);
if (q == NULL || ! dns_name_equal(&p->name, &q->name))
break;
ISC_LIST_UNLINK(list->tuples, q, link);
dns_difftuple_free(&q);
}
p = ISC_LIST_NEXT(p, link);
}
failure:
return (result);
}
static dns_result_t
is_glue(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
isc_boolean_t *flag)
{
dns_result_t result;
dns_fixedname_t foundname;
dns_fixedname_init(&foundname);
result = dns_db_find(db, name, ver, dns_rdatatype_any,
DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
(isc_stdtime_t) 0, NULL,
dns_fixedname_name(&foundname),
NULL, NULL);
if (result == DNS_R_SUCCESS) {
*flag = ISC_FALSE;
return (DNS_R_SUCCESS);
} else if (result == DNS_R_ZONECUT) {
/* XXX should omit non-delegation types from NXT */
*flag = ISC_FALSE;
return (DNS_R_SUCCESS);
} else if (result == DNS_R_GLUE) {
*flag = ISC_TRUE;
return (DNS_R_SUCCESS);
} else {
return (result);
}
}
/*
* Find the next/previous name that has a NXT record.
* In other words, skip empty database nodes and names that
* have had their NXTs removed because they are obscured by
* a zone cut.
*/
static dns_result_t
next_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *oldname,
dns_name_t *newname, isc_boolean_t forward)
{
dns_result_t result;
dns_dbiterator_t *dbit = NULL;
isc_boolean_t has_nxt;
CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
CHECK(dns_dbiterator_seek(dbit, oldname));
do {
dns_dbnode_t *node = NULL;
if (forward)
result = dns_dbiterator_next(dbit);
else
result = dns_dbiterator_prev(dbit);
if (result == DNS_R_NOMORE) {
/* Wrap around. */
if (forward)
CHECK(dns_dbiterator_first(dbit));
else
CHECK(dns_dbiterator_last(dbit));
}
dns_dbiterator_current(dbit, &node, newname);
dns_db_detachnode(db, &node);
/*
* The iterator may hold the tree lock, and
* rrset_exists() cals dns_db_findnode() which
* may try to reacquire it. To avoid deadlock
* we must pause the iterator first.
*/
CHECK(dns_dbiterator_pause(dbit));
CHECK(rrset_exists(db, ver, newname,
dns_rdatatype_nxt, 0, &has_nxt));
} while (! has_nxt);
failure:
if (dbit != NULL)
dns_dbiterator_destroy(&dbit);
return (result);
}
/*
* Add a NXT record for "name", recording the change in "diff".
*/
static dns_result_t
add_nxt(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_diff_t *diff) {
dns_result_t result;
dns_dbnode_t *node = NULL;
unsigned char buffer[DNS_NXT_BUFFERSIZE];
dns_rdata_t rdata;
dns_difftuple_t *tuple = NULL;
dns_fixedname_t fixedname;
dns_name_t *target;
dns_fixedname_init(&fixedname);
target = dns_fixedname_name(&fixedname);
/* Find the successor name, aka NXT target. */
CHECK(next_active(db, ver, name, target, ISC_TRUE));
/* Create the NXT RDATA. */
CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
dns_rdata_init(&rdata);
CHECK(dns_buildnxtrdata(db, ver, node, target, buffer, &rdata));
dns_db_detachnode(db, &node);
/* Create a diff tuple, update the database, and record the change. */
CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
3600, /* XXXRTH */
&rdata, &tuple));
CHECK(do_one_tuple(&tuple, db, ver, diff));
INSIST(tuple == NULL);
failure:
if (node != NULL)
dns_db_detachnode(db, &node);
return (result);
}
/*
* Add a placeholder NXT record for "name", recording the change in "diff".
*/
static dns_result_t
add_placeholder_nxt(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, dns_diff_t *diff) {
dns_result_t result;
dns_difftuple_t *tuple = NULL;
isc_region_t r;
unsigned char data[1] = { 0 }; /* The root domain, no bits. */
dns_rdata_t rdata;
r.base = data;
r.length = sizeof data;
dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nxt, &r);
CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0,
&rdata, &tuple));
CHECK(do_one_tuple(&tuple, db, ver, diff));
failure:
return (result);
}
static dns_result_t
find_zone_keys(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx,
unsigned int maxkeys, dst_key_t **keys, unsigned int *nkeys)
{
dns_result_t result;
dns_dbnode_t *node = NULL;
CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
CHECK(dns_dnssec_findzonekeys(db, ver, node, dns_db_origin(db),
mctx, maxkeys, keys, nkeys));
failure:
if (node != NULL)
dns_db_detachnode(db, &node);
return (result);
}
/*
* Add SIG records for an RRset, recording the change in "diff".
*/
static dns_result_t
add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys,
unsigned int nkeys, isc_mem_t *mctx, isc_stdtime_t now,
isc_stdtime_t expire)
{
dns_result_t result;
dns_dbnode_t *node = NULL;
dns_rdataset_t rdataset;
dns_rdata_t sig_rdata;
isc_buffer_t buffer;
unsigned char data[1024]; /* XXX */
unsigned int i;
dns_rdataset_init(&rdataset);
isc_buffer_init(&buffer, data, sizeof data, ISC_BUFFERTYPE_BINARY);
/* Get the rdataset to sign. */
CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
CHECK(dns_db_findrdataset(db, node, ver, type, 0,
(isc_stdtime_t) 0,
&rdataset, NULL));
dns_db_detachnode(db, &node);
for (i = 0; i < nkeys; i++) {
/* Calculate the signature, creating a SIG RDATA. */
CHECK(dns_dnssec_sign(name, &rdataset, keys[i],
&now, &expire,
mctx, &buffer, &sig_rdata));
/* Update the database and journal with the SIG. */
/* XXX inefficient - will cause dataset merging */
CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name,
rdataset.ttl, &sig_rdata));
}
failure:
if (dns_rdataset_isassociated(&rdataset))
dns_rdataset_disassociate(&rdataset);
if (node != NULL)
dns_db_detachnode(db, &node);
return (result);
}
/*
* Update SIG and NXT records affected by an update. The original
* update, including the SOA serial update but exluding the SIG & NXT
* changes, is in "diff" and has already been applied to "newver" of "db".
* The database version prior to the update is "oldver".
*
* The necessary SIG and NXT changes will be applied to "newver"
* and added (as a minimal diff) to "diff".
*/
static dns_result_t
update_signatures(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *oldver,
dns_dbversion_t *newver, dns_diff_t *diff)
{
dns_result_t result;
dns_difftuple_t *t;
dns_diff_t diffnames;
dns_diff_t affected;
dns_diff_t sig_diff;
dns_diff_t nxt_diff;
dns_diff_t nxt_mindiff;
isc_boolean_t flag;
dst_key_t *zone_keys[MAXZONEKEYS];
unsigned int nkeys = 0;
unsigned int i;
isc_stdtime_t now, expire;
dns_diff_init(mctx, &diffnames);
dns_diff_init(mctx, &affected);
dns_diff_init(mctx, &sig_diff);
dns_diff_init(mctx, &nxt_diff);
dns_diff_init(mctx, &nxt_mindiff);
result = find_zone_keys(db, newver, mctx,
MAXZONEKEYS, zone_keys, &nkeys);
CHECKMSG(result, "could not get zone keys");
CHECK(isc_stdtime_get(&now));
expire = 100000 + now; /* XXX */
/*
* Find all RRsets directly affected by the update, and
* update their SIGs. Also build a list of names affected
* by the update in "diffnames".
*/
CHECK(dns_diff_sort(diff, temp_order));
t = ISC_LIST_HEAD(diff->tuples);
while (t != NULL) {
dns_name_t *name = &t->name;
/* Now "name" is a new, unique name affected by the update. */
CHECK(namelist_append_name(&diffnames, name));
while (t != NULL && dns_name_equal(&t->name, name)) {
dns_rdatatype_t type;
type = t->rdata.type;
/*
* Now "name" and "type" denote a new unique RRset
* affected by the update.
*/
/* Don't sign SIGs. */
if (type == dns_rdatatype_sig)
goto skip;
/*
* Delete all old SIGs covering this type, since they
* are all invalid when the signed RRset has changed.
* We may not be able to recreate all of them - tough.
*/
CHECK(delete_if(true_p, db, newver, name,
dns_rdatatype_sig, type,
NULL, &sig_diff));
/*
* If this RRset still exists after the update,
* add a new signature for it.
*/
CHECK(rrset_exists(db, newver, name, type, 0, &flag));
if (flag) {
CHECK(add_sigs(db, newver, name, type,
&sig_diff, zone_keys, nkeys,
mctx, now, expire));
}
skip:
/* Skip any other updates to the same RRset. */
while (t != NULL &&
dns_name_equal(&t->name, name) &&
t->rdata.type == type)
{
t = ISC_LIST_NEXT(t, link);
}
}
}
/* Remove orphaned NXTs and SIG NXTs. */
for (t = ISC_LIST_HEAD(diffnames.tuples);
t != NULL;
t = ISC_LIST_NEXT(t, link))
{
CHECK(non_nxt_rrset_exists(db, newver, &t->name, &flag));
if (! flag) {
CHECK(delete_if(true_p, db, newver, &t->name,
dns_rdatatype_any, 0,
NULL, &sig_diff));
}
}
/*
* When a name is created or deleted, its predecessor needs to
* have its NXT updated.
*/
for (t = ISC_LIST_HEAD(diffnames.tuples);
t != NULL;
t = ISC_LIST_NEXT(t, link))
{
isc_boolean_t existed, exists;
dns_fixedname_t fixedname;
dns_name_t *prevname;
dns_fixedname_init(&fixedname);
prevname = dns_fixedname_name(&fixedname);
CHECK(name_exists(db, oldver, &t->name, &existed));
CHECK(name_exists(db, newver, &t->name, &exists));
if (exists == existed)
continue;
/*
* Find the predecessor.
* When names become obscured or unobscured in this update
* transaction, we may find the wrong predecessor because
* the NXTs have not yet been updated to reflect the delegation
* change. This should not matter because in this case,
* the correct predecessor is either the delegation node or
* a newly unobscured node, and those nodes are on the
* "affected" list in any case.
*/
CHECK(next_active(db, newver, &t->name, prevname, ISC_FALSE));
CHECK(namelist_append_name(&affected, prevname));
}
/* Find names affected by delegation changes. */
for (t = ISC_LIST_HEAD(diffnames.tuples);
t != NULL;
t = ISC_LIST_NEXT(t, link))
{
isc_boolean_t existed, exists;
CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0,
&existed));
CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0,
&exists));
if (exists == existed)
continue;
/*
* There was a delegation change. Mark all subdomains
* of t->name as potentially needing a NXT update.
*/
CHECK(namelist_append_subdomain(db, &t->name, &affected));
}
ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link);
INSIST(ISC_LIST_EMPTY(diffnames.tuples));
CHECK(uniqify_name_list(&affected));
/*
* Determine which names should have NXTs, and delete/create
* NXTs to make it so. We don't know the final NXT targets yet,
* so we just create placeholder NXTs with arbitrary contents
* to indicate that their respective owner names should be part of
* the NXT chain.
*/
for (t = ISC_LIST_HEAD(affected.tuples);
t != NULL;
t = ISC_LIST_NEXT(t, link))
{
isc_boolean_t exists;
CHECK(name_exists(db, newver, &t->name, &exists));
if (! exists)
continue;
CHECK(is_glue(db, newver, &t->name, &flag));
if (flag) {
/*
* This name is obscured. Delete any
* existing NXT record.
*/
CHECK(delete_if(true_p, db, newver, &t->name,
dns_rdatatype_nxt, 0,
NULL, &nxt_diff));
} else {
/* This name is not obscured. It should have a NXT. */
CHECK(rrset_exists(db, newver, &t->name,
dns_rdatatype_nxt, 0, &flag));
if (! flag) {
add_placeholder_nxt(db, newver, &t->name, diff);
}
}
}
/*
* Now we know which names are part of the NXT chain.
* Make them all point at their correct targets.
*/
for (t = ISC_LIST_HEAD(affected.tuples);
t != NULL;
t = ISC_LIST_NEXT(t, link))
{
CHECK(rrset_exists(db, newver, &t->name,
dns_rdatatype_nxt, 0, &flag));
if (flag) {
/*
* There is a NXT, but we don't know if it is correct.
* Delete it and create a correct one to be sure.
* If the update was unnecessary, the diff minimization
* will take care of eliminating it from the journal,
* IXFRs, etc.
*
* The SIG bit should always be set in the NXTs
* we generate, because they will all get SIG NXTs.
* (XXX what if the zone keys are missing?).
* Because the SIG NXTs have not necessarily been
* created yet, the correctness of the bit mask relies
* on the assumption that NXTs are only created if
* there is other data, and if there is other data,
* there are other SIGs.
*/
CHECK(delete_if(true_p, db, newver, &t->name,
dns_rdatatype_nxt, 0,
NULL, &nxt_diff));
CHECK(add_nxt(db, newver, &t->name, &nxt_diff));
}
}
/*
* Minimize the set of NXT updates so that we don't
* have to regenerate the SIG NXTs for NXTs that were
* replaced with identical ones.
*/
while ((t = ISC_LIST_HEAD(nxt_diff.tuples)) != NULL) {
ISC_LIST_UNLINK(nxt_diff.tuples, t, link);
dns_diff_appendminimal(&nxt_mindiff, &t);
}
/* Update SIG NXTs. */
for (t = ISC_LIST_HEAD(nxt_mindiff.tuples);
t != NULL;
t = ISC_LIST_NEXT(t, link))
{
if (t->op == DNS_DIFFOP_DEL) {
CHECK(delete_if(true_p, db, newver, &t->name,
dns_rdatatype_sig, dns_rdatatype_nxt,
NULL, &sig_diff));
} else if (t->op == DNS_DIFFOP_ADD) {
CHECK(add_sigs(db, newver, &t->name, dns_rdatatype_nxt,
&sig_diff, zone_keys, nkeys, mctx, now,
expire));
} else {
INSIST(0);
}
}
/* Record our changes for the journal. */
while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
ISC_LIST_UNLINK(sig_diff.tuples, t, link);
dns_diff_appendminimal(diff, &t);
}
while ((t = ISC_LIST_HEAD(nxt_mindiff.tuples)) != NULL) {
ISC_LIST_UNLINK(nxt_mindiff.tuples, t, link);
dns_diff_appendminimal(diff, &t);
}
INSIST(ISC_LIST_EMPTY(sig_diff.tuples));
INSIST(ISC_LIST_EMPTY(nxt_diff.tuples));
INSIST(ISC_LIST_EMPTY(nxt_mindiff.tuples));
failure:
dns_diff_clear(&sig_diff);
dns_diff_clear(&nxt_diff);
dns_diff_clear(&nxt_mindiff);
dns_diff_clear(&affected);
dns_diff_clear(&diffnames);
for (i = 0; i < nkeys; i++)
dst_key_free(zone_keys[i]);
return (result);
}
/**************************************************************************/ /**************************************************************************/
/* /*
* The actual update code in all its glory. We try to follow * The actual update code in all its glory. We try to follow
@ -993,6 +1606,7 @@ ns_req_update(ns_client_t *client,
dns_name_t *zonename; dns_name_t *zonename;
dns_rdataset_t *zone_rdataset; dns_rdataset_t *zone_rdataset;
dns_db_t *db = NULL; dns_db_t *db = NULL;
dns_dbversion_t *oldver = NULL;
dns_dbversion_t *ver = NULL; dns_dbversion_t *ver = NULL;
dns_rdataclass_t zoneclass; dns_rdataclass_t zoneclass;
dns_message_t *response = NULL; dns_message_t *response = NULL;
@ -1047,11 +1661,6 @@ ns_req_update(ns_client_t *client,
FAILMSG(DNS_R_NOTAUTH, FAILMSG(DNS_R_NOTAUTH,
"not authoritative for update zone"); "not authoritative for update zone");
/* XXX this should go away when caches are no longer in the dbtable */
if (dns_db_iscache(db))
FAILMSG(DNS_R_NOTAUTH,
"not authoritative for update zone");
printf("zone section checked out OK\n"); printf("zone section checked out OK\n");
/* Create a new database version. */ /* Create a new database version. */
@ -1059,6 +1668,7 @@ ns_req_update(ns_client_t *client,
/* XXX should queue an update event here if someone else /* XXX should queue an update event here if someone else
has a writable version open */ has a writable version open */
dns_db_currentversion(db, &oldver);
CHECK(dns_db_newversion(db, &ver)); CHECK(dns_db_newversion(db, &ver));
printf("new database version created\n"); printf("new database version created\n");
@ -1198,6 +1808,16 @@ ns_req_update(ns_client_t *client,
update_class); update_class);
FAIL(DNS_R_FORMERR); FAIL(DNS_R_FORMERR);
} }
/*
* draft-ietf-dnsind-simple-secure-update-01 says
* "Unlike traditional dynamic update, the client
* is forbidden from updating NXT records."
*/
if (dns_db_issecure(db) && rdata.type == dns_rdatatype_nxt) {
FAILMSG(DNS_R_REFUSED,
"explicit NXT updates are not allowed "
"in secure zones");
}
} }
if (result != DNS_R_NOMORE) if (result != DNS_R_NOMORE)
FAIL(result); FAIL(result);
@ -1234,7 +1854,9 @@ ns_req_update(ns_client_t *client,
CHECK(rrset_exists(db, ver, name, CHECK(rrset_exists(db, ver, name,
dns_rdatatype_cname, 0, dns_rdatatype_cname, 0,
&flag)); &flag));
if (flag && ! is_dnssec_type(rdata.type)) { if (flag &&
! dns_rdatatype_isdnssec(rdata.type))
{
printf("attempt to add non-cname " printf("attempt to add non-cname "
"alongside cname ignored\n"); "alongside cname ignored\n");
continue; continue;
@ -1330,20 +1952,27 @@ ns_req_update(ns_client_t *client,
FAIL(result); FAIL(result);
/* /*
* If any changes were made, increment the SOA serial number * If any changes were made, increment the SOA serial number,
* and write the update to the journal. * update SIGs and NXTs (if zone is secure), and write the update
* to the journal.
*/ */
if (! ISC_LIST_EMPTY(diff.tuples)) { if (! ISC_LIST_EMPTY(diff.tuples)) {
dns_journal_t *journal; dns_journal_t *journal;
/* /*
* Increment the SOA serial, but only if it was not changed as * Increment the SOA serial, but only if it was not
* a result of an update operation. * changed as a result of an update operation.
*/ */
if (! soa_serial_changed) { if (! soa_serial_changed) {
CHECK(increment_soa_serial(db, ver, &diff, mctx)); CHECK(increment_soa_serial(db, ver, &diff, mctx));
} }
if (dns_db_issecure(db)) {
result = update_signatures(mctx, db,
oldver, ver, &diff);
CHECKMSG(result, "signature update failed");
}
printf("write journal\n"); printf("write journal\n");
/* XXX use a real file name */ /* XXX use a real file name */
@ -1386,6 +2015,9 @@ ns_req_update(ns_client_t *client,
dns_diff_clear(&temp); dns_diff_clear(&temp);
dns_diff_clear(&diff); dns_diff_clear(&diff);
if (oldver != NULL)
dns_db_closeversion(db, &oldver, ISC_FALSE);
if (db != NULL) if (db != NULL)
dns_db_detach(&db); dns_db_detach(&db);