From 0b09763c354ec91fb352b6b4cea383bd0195b2d8 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Mon, 17 Jun 2002 04:01:37 +0000 Subject: [PATCH] 1328. [func] DS (delegation signer) support. --- CHANGES | 2 + bin/dnssec/Makefile.in | 27 +- bin/dnssec/dnssec-signzone.c | 607 +++++++------- bin/named/query.c | 232 ++---- bin/tests/system/conf.sh.in | 4 +- bin/tests/system/dnssec/ns1/sign.sh | 10 +- bin/tests/system/dnssec/ns2/sign.sh | 20 +- bin/tests/system/dnssec/ns3/sign.sh | 10 +- bin/tests/system/dnssec/tests.sh | 12 +- lib/dns/Makefile.in | 6 +- lib/dns/ds.c | 80 ++ lib/dns/include/dns/Makefile.in | 4 +- lib/dns/include/dns/ds.h | 56 ++ lib/dns/include/dns/rdata.h | 15 +- lib/dns/include/dns/result.h | 5 +- lib/dns/include/dns/validator.h | 9 +- lib/dns/rbtdb.c | 5 +- lib/dns/rdata.c | 9 +- lib/dns/rdata/generic/ds_43.c | 257 ++++++ lib/dns/rdata/generic/ds_43.h | 34 + lib/dns/resolver.c | 62 +- lib/dns/result.c | 5 +- lib/dns/validator.c | 1131 ++++++++++++++++----------- version | 2 +- 24 files changed, 1628 insertions(+), 976 deletions(-) create mode 100644 lib/dns/ds.c create mode 100644 lib/dns/include/dns/ds.h create mode 100644 lib/dns/rdata/generic/ds_43.c create mode 100644 lib/dns/rdata/generic/ds_43.h diff --git a/CHANGES b/CHANGES index adec805082..d4ec2a17d9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +1328. [func] DS (delegation signer) support. + 1327. [bug] nsupdate: allow white space base64 key data. 1326. [bug] Badly encoded LOC record when the size, horizontal diff --git a/bin/dnssec/Makefile.in b/bin/dnssec/Makefile.in index 6dbf709236..d9b363bfa4 100644 --- a/bin/dnssec/Makefile.in +++ b/bin/dnssec/Makefile.in @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.21 2001/11/06 20:05:00 bwelling Exp $ +# $Id: Makefile.in,v 1.22 2002/06/17 04:01:03 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -39,26 +39,15 @@ DEPLIBS = ${DNSDEPLIBS} ${ISCDEPLIBS} LIBS = ${DNSLIBS} ${ISCLIBS} @LIBS@ # Alphabetically -TARGETS = dnssec-keygen \ - dnssec-makekeyset \ - dnssec-signkey \ - dnssec-signzone +TARGETS = dnssec-keygen dnssec-signzone OBJS = dnssectool.@O@ -SRCS = dnssec-keygen.c dnssec-makekeyset.c \ - dnssec-signkey.c dnssec-signzone.c \ - dnssectool.c +SRCS = dnssec-keygen.c dnssec-signzone.c dnssectool.c -MANPAGES = dnssec-keygen.8 \ - dnssec-makekeyset.8 \ - dnssec-signkey.8 \ - dnssec-signzone.8 +MANPAGES = dnssec-keygen.8 dnssec-signzone.8 -HTMLPAGES = dnssec-keygen.html \ - dnssec-makekeyset.html \ - dnssec-signkey.html \ - dnssec-signzone.html +HTMLPAGES = dnssec-keygen.html dnssec-signzone.html MANOBJS = ${MANPAGES} ${HTMLPAGES} @@ -67,12 +56,6 @@ MANOBJS = ${MANPAGES} ${HTMLPAGES} dnssec-keygen: dnssec-keygen.@O@ ${OBJS} ${DEPLIBS} ${LIBTOOL} ${PURIFY} ${CC} ${CFLAGS} -o $@ dnssec-keygen.@O@ ${OBJS} ${LIBS} -dnssec-makekeyset: dnssec-makekeyset.@O@ ${OBJS} ${DEPLIBS} - ${LIBTOOL} ${PURIFY} ${CC} ${CFLAGS} -o $@ dnssec-makekeyset.@O@ ${OBJS} ${LIBS} - -dnssec-signkey: dnssec-signkey.@O@ ${OBJS} ${DEPLIBS} - ${LIBTOOL} ${PURIFY} ${CC} ${CFLAGS} -o $@ dnssec-signkey.@O@ ${OBJS} ${LIBS} - dnssec-signzone.@O@: dnssec-signzone.c ${LIBTOOL} ${PURIFY} ${CC} ${ALL_CFLAGS} -DVERSION=\"${VERSION}\" -c $< diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c index 57f2068ebc..0073f6d040 100644 --- a/bin/dnssec/dnssec-signzone.c +++ b/bin/dnssec/dnssec-signzone.c @@ -17,7 +17,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dnssec-signzone.c,v 1.159 2002/02/20 03:33:02 marka Exp $ */ +/* $Id: dnssec-signzone.c,v 1.160 2002/06/17 04:01:05 marka Exp $ */ #include @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -65,12 +66,14 @@ const char *program = "dnssec-signzone"; int verbose; #define BUFSIZE 2048 +#define MAXDSKEYS 8 typedef struct signer_key_struct signer_key_t; struct signer_key_struct { dst_key_t *key; isc_boolean_t isdefault; + isc_boolean_t keysigning; unsigned int position; ISC_LINK(signer_key_t) link; }; @@ -106,6 +109,7 @@ static isc_taskmgr_t *taskmgr = NULL; static dns_db_t *gdb; /* The database */ static dns_dbversion_t *gversion; /* The database version */ static dns_dbiterator_t *gdbiter; /* The database iterator */ +static dns_rdataclass_t gclass; /* The class */ static dns_name_t *gorigin; /* The database origin */ static isc_task_t *master = NULL; static unsigned int ntasks = 0; @@ -147,8 +151,41 @@ dumpnode(dns_name_t *name, dns_dbnode_t *node) { check_result(result, "dns_master_dumpnodetostream"); } +static void +dumpdb(dns_db_t *db) { + dns_dbiterator_t *dbiter = NULL; + dns_dbnode_t *node; + dns_fixedname_t fname; + dns_name_t *name; + isc_result_t result; + + dbiter = NULL; + result = dns_db_createiterator(db, ISC_FALSE, &dbiter); + check_result(result, "dns_db_createiterator()"); + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + node = NULL; + + for (result = dns_dbiterator_first(dbiter); + result == ISC_R_SUCCESS; + result = dns_dbiterator_next(dbiter)) + { + result = dns_dbiterator_current(dbiter, &node, name); + check_result(result, "dns_dbiterator_current()"); + dumpnode(name, node); + dns_db_detachnode(db, &node); + } + if (result != ISC_R_NOMORE) + fatal("iterating database: %s", isc_result_totext(result)); + + dns_dbiterator_destroy(&dbiter); +} + static signer_key_t * -newkeystruct(dst_key_t *dstkey, isc_boolean_t isdefault) { +newkeystruct(dst_key_t *dstkey, isc_boolean_t isdefault, + isc_boolean_t iskeysigning) +{ signer_key_t *key; key = isc_mem_get(mctx, sizeof(signer_key_t)); @@ -156,6 +193,7 @@ newkeystruct(dst_key_t *dstkey, isc_boolean_t isdefault) { fatal("out of memory"); key->key = dstkey; key->isdefault = isdefault; + key->keysigning = iskeysigning; key->position = keycount++; ISC_LINK_INIT(key, link); return (key); @@ -231,9 +269,9 @@ keythatsigned(dns_rdata_sig_t *sig) { NULL, mctx, &privkey); if (result == ISC_R_SUCCESS) { dst_key_free(&pubkey); - key = newkeystruct(privkey, ISC_FALSE); + key = newkeystruct(privkey, ISC_FALSE, ISC_FALSE); } else - key = newkeystruct(pubkey, ISC_FALSE); + key = newkeystruct(pubkey, ISC_FALSE, ISC_FALSE); ISC_LIST_APPEND(keylist, key, link); return (key); } @@ -462,7 +500,13 @@ signset(dns_diff_t *diff, dns_dbnode_t *node, dns_name_t *name, unsigned char array[BUFSIZE]; char keystr[KEY_FORMATSIZE]; - if (!key->isdefault || nowsignedby[key->position]) + if (nowsignedby[key->position]) + continue; + + if (!(key->isdefault || + (key->keysigning && + set->type == dns_rdatatype_key && + dns_name_equal(name, gorigin)))) continue; key_format(key->key, keystr, sizeof(keystr)); @@ -481,36 +525,6 @@ signset(dns_diff_t *diff, dns_dbnode_t *node, dns_name_t *name, isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t)); } -/* Determine if a KEY set contains a null key */ -static isc_boolean_t -hasnullkey(dns_rdataset_t *rdataset) { - isc_result_t result; - dns_rdata_t rdata = DNS_RDATA_INIT; - - for (result = dns_rdataset_first(rdataset); - result == ISC_R_SUCCESS; - result = dns_rdataset_next(rdataset)) - { - dst_key_t *key = NULL; - - dns_rdata_reset(&rdata); - dns_rdataset_current(rdataset, &rdata); - result = dns_dnssec_keyfromrdata(dns_rootname, - &rdata, mctx, &key); - if (result != ISC_R_SUCCESS) - fatal("could not convert KEY into internal " - "format: %s", isc_result_totext(result)); - if (dst_key_isnullkey(key)) { - dst_key_free(&key); - return (ISC_TRUE); - } - dst_key_free(&key); - } - if (result != ISC_R_NOMORE) - fatal("failure looking for null keys"); - return (ISC_FALSE); -} - static void opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass, dns_db_t **dbp) @@ -545,238 +559,139 @@ opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass, } /* - * Looks for signatures of the zone keys by the parent, and imports them - * if found. + * Loads the key set for a child zone, if there is one, and builds DS records. */ -static void -importparentsig(dns_diff_t *diff, dns_name_t *name, dns_rdataset_t *set) { - dns_db_t *newdb = NULL; - dns_dbnode_t *newnode = NULL; - dns_rdataset_t newset, sigset; - dns_rdata_t rdata = DNS_RDATA_INIT, newrdata = DNS_RDATA_INIT; +static isc_result_t +loadds(dns_name_t *name, dns_rdataset_t *dsset) { + dns_db_t *db = NULL; + dns_dbversion_t *ver = NULL; + dns_dbnode_t *node = NULL; isc_result_t result; + dns_rdataset_t keyset; + dns_rdata_t key, ds; + unsigned char dsbuf[DNS_DS_BUFFERSIZE]; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL; - dns_rdataset_init(&newset); - dns_rdataset_init(&sigset); + opendb("keyset-", name, gclass, &db); + if (db == NULL) + return (ISC_R_NOTFOUND); - opendb("signedkey-", name, dns_db_class(gdb), &newdb); - if (newdb == NULL) - return; - - result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode); - if (result != ISC_R_SUCCESS) - goto failure; - result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key, - 0, 0, &newset, &sigset); - if (result != ISC_R_SUCCESS) - goto failure; - - if (!dns_rdataset_isassociated(&newset) || - !dns_rdataset_isassociated(&sigset)) - goto failure; - - if (dns_rdataset_count(set) != dns_rdataset_count(&newset)) { - result = DNS_R_BADDB; - goto failure; + result = dns_db_findnode(db, name, ISC_FALSE, &node); + if (result != ISC_R_SUCCESS) { + dns_db_detach(&db); + return (DNS_R_BADDB); + } + dns_rdataset_init(&keyset); + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_key, 0, 0, + &keyset, NULL); + if (result != ISC_R_SUCCESS) { + dns_db_detachnode(db, &node); + dns_db_detach(&db); + return (result); } - result = dns_rdataset_first(set); - check_result(result, "dns_rdataset_first()"); - for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) { - dns_rdataset_current(set, &rdata); - result = dns_rdataset_first(&newset); - check_result(result, "dns_rdataset_first()"); - for (; - result == ISC_R_SUCCESS; - result = dns_rdataset_next(&newset)) - { - dns_rdataset_current(&newset, &newrdata); - if (dns_rdata_compare(&rdata, &newrdata) == 0) - break; - dns_rdata_reset(&newrdata); - } - dns_rdata_reset(&newrdata); - dns_rdata_reset(&rdata); - if (result != ISC_R_SUCCESS) - break; - } - if (result != ISC_R_NOMORE) - goto failure; + vbprintf(2, "found KEY records\n"); - vbprintf(2, "found the parent's signature of our zone key\n"); + result = dns_db_newversion(db, &ver); + check_result(result, "dns_db_newversion"); - result = dns_rdataset_first(&sigset); - while (result == ISC_R_SUCCESS) { - dns_difftuple_t *tuple = NULL; + dns_diff_init(mctx, &diff); - dns_rdataset_current(&sigset, &rdata); - result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, - sigset.ttl, &rdata, &tuple); + for (result = dns_rdataset_first(&keyset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&keyset)) + { + dns_rdata_init(&key); + dns_rdata_init(&ds); + dns_rdataset_current(&keyset, &key); + result = dns_ds_buildrdata(name, &key, DNS_DSDIGEST_SHA1, + dsbuf, &ds); + check_result(result, "dns_ds_buildrdata"); + + result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, + zonettl, &ds, &tuple); check_result(result, "dns_difftuple_create"); - dns_diff_append(diff, &tuple); - result = dns_rdataset_next(&sigset); - dns_rdata_reset(&rdata); + dns_diff_append(&diff, &tuple); } - if (result == ISC_R_NOMORE) - result = ISC_R_SUCCESS; + result = dns_diff_apply(&diff, db, ver); + check_result(result, "dns_diff_apply"); + dns_diff_clear(&diff); - failure: - if (dns_rdataset_isassociated(&newset)) - dns_rdataset_disassociate(&newset); - if (dns_rdataset_isassociated(&sigset)) - dns_rdataset_disassociate(&sigset); - if (newnode != NULL) - dns_db_detachnode(newdb, &newnode); - if (newdb != NULL) - dns_db_detach(&newdb); - if (result != ISC_R_SUCCESS) - fatal("zone signedkey file is invalid or does not match zone"); + dns_db_closeversion(db, &ver, ISC_TRUE); + + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds, 0, 0, + dsset, NULL); + check_result(result, "dns_db_findrdataset"); + + dns_rdataset_disassociate(&keyset); + dns_db_detachnode(db, &node); + dns_db_detach(&db); + return (result); } -/* - * Looks for our signatures of child keys. If present, inform the caller. - */ -static isc_boolean_t -haschildkey(dns_name_t *name) { - dns_db_t *newdb = NULL; - dns_dbnode_t *newnode = NULL; - dns_rdataset_t set, sigset; - dns_rdata_t sigrdata = DNS_RDATA_INIT; - isc_result_t result; - isc_boolean_t found = ISC_FALSE; - dns_rdata_sig_t sig; - signer_key_t *key; - - dns_rdataset_init(&set); - dns_rdataset_init(&sigset); - - opendb("signedkey-", name, dns_db_class(gdb), &newdb); - if (newdb == NULL) - return (ISC_FALSE); - - result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode); - if (result != ISC_R_SUCCESS) - goto failure; - result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key, - 0, 0, &set, &sigset); - if (result != ISC_R_SUCCESS) - goto failure; - - if (!dns_rdataset_isassociated(&set) || - !dns_rdataset_isassociated(&sigset)) - goto failure; - - result = dns_rdataset_first(&sigset); - check_result(result, "dns_rdataset_first()"); - dns_rdata_init(&sigrdata); - for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(&sigset)) { - dns_rdataset_current(&sigset, &sigrdata); - result = dns_rdata_tostruct(&sigrdata, &sig, NULL); - if (result != ISC_R_SUCCESS) - goto failure; - key = keythatsigned(&sig); - dns_rdata_freestruct(&sig); - if (key == NULL) { - char namestr[DNS_NAME_FORMATSIZE]; - dns_name_format(name, namestr, sizeof(namestr)); - vbprintf(1, "unknown KEY in %s signedkey file\n", - namestr); - goto failure; - } - result = dns_dnssec_verify(name, &set, key->key, - ISC_FALSE, mctx, &sigrdata); - if (result == ISC_R_SUCCESS) { - found = ISC_TRUE; - break; - } else { - char namestr[DNS_NAME_FORMATSIZE]; - dns_name_format(name, namestr, sizeof(namestr)); - vbprintf(1, "verifying SIG in %s signedkey file: %s\n", - namestr, isc_result_totext(result)); - } - dns_rdata_reset(&sigrdata); - } - - failure: - if (dns_rdataset_isassociated(&set)) - dns_rdataset_disassociate(&set); - if (dns_rdataset_isassociated(&sigset)) - dns_rdataset_disassociate(&sigset); - if (newnode != NULL) - dns_db_detachnode(newdb, &newnode); - if (newdb != NULL) - dns_db_detach(&newdb); - - return (found); -} - -/* - * There probably should be a dns_nxt_setbit, but it can get complicated if - * the length of the bit set needs to be increased. In this case, since the - * NXT bit is set and both SIG and KEY are less than NXT, the easy way works. - */ +/* XXX fix me */ static void -nxt_setbit(dns_rdataset_t *rdataset, dns_rdatatype_t type) { +nxt_setbit(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdatatype_t type, + unsigned int val) +{ isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_nxt_t nxt; + unsigned int newlen; - REQUIRE(type <= dns_rdatatype_nxt); + INSIST(type < 128); result = dns_rdataset_first(rdataset); check_result(result, "dns_rdataset_first()"); dns_rdataset_current(rdataset, &rdata); result = dns_rdata_tostruct(&rdata, &nxt, NULL); check_result(result, "dns_rdata_tostruct"); - set_bit(nxt.typebits, type, 1); + + newlen = type / 8 + 1; + + if (newlen <= nxt.len) + set_bit(nxt.typebits, type, val); + else { + unsigned char bitmap[16]; + unsigned char nxtdata[16 + DNS_NAME_MAXWIRE]; + unsigned int len = newlen; + dns_rdata_t newrdata = DNS_RDATA_INIT; + isc_buffer_t b; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL; + + INSIST(nxt.len < sizeof(bitmap)); + + dns_diff_init(mctx, &diff); + result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL, name, + rdataset->ttl, &rdata, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(&diff, &tuple); + + memset(bitmap, 0, sizeof(bitmap)); + memcpy(bitmap, nxt.typebits, nxt.len); + set_bit(bitmap, type, val); + nxt.typebits = bitmap; + nxt.len = len; + isc_buffer_init(&b, nxtdata, sizeof(nxtdata)); + result = dns_rdata_fromstruct(&newrdata, rdata.rdclass, + dns_rdatatype_nxt, &nxt, &b); + check_result(result, "dns_rdata_fromstruct"); + + result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, + rdataset->ttl, &newrdata, + &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(&diff, &tuple); + + result = dns_diff_apply(&diff, gdb, gversion); + check_result(result, "dns_difftuple_apply"); + dns_diff_clear(&diff); + } dns_rdata_freestruct(&nxt); } -static void -createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name, - dns_ttl_t ttl) -{ - unsigned char keydata[4]; - dns_rdata_t keyrdata = DNS_RDATA_INIT; - dns_rdata_key_t key; - dns_diff_t diff; - dns_difftuple_t *tuple = NULL; - isc_buffer_t b; - isc_result_t result; - char namestr[DNS_NAME_FORMATSIZE]; - - dns_name_format(name, namestr, sizeof(namestr)); - vbprintf(2, "adding null key at %s\n", namestr); - - key.common.rdclass = dns_db_class(db); - key.common.rdtype = dns_rdatatype_key; - ISC_LINK_INIT(&key.common, link); - key.mctx = NULL; - key.flags = DNS_KEYTYPE_NOKEY | DNS_KEYOWNER_ZONE; - key.protocol = DNS_KEYPROTO_DNSSEC; - key.algorithm = DNS_KEYALG_DSA; - key.datalen = 0; - key.data = NULL; - isc_buffer_init(&b, keydata, sizeof(keydata)); - result = dns_rdata_fromstruct(&keyrdata, dns_db_class(db), - dns_rdatatype_key, &key, &b); - if (result != ISC_R_SUCCESS) - fatal("failed to build null key"); - - dns_diff_init(mctx, &diff); - - result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, ttl, - &keyrdata, &tuple); - check_result(result, "dns_difftuple_create"); - - dns_diff_append(&diff, &tuple); - - result = dns_diff_apply(&diff, db, version); - check_result(result, "dns_diff_apply"); - - dns_diff_clear(&diff); -} - static void warnwild(const char *name) { static int warned = 0; @@ -822,9 +737,8 @@ signname(dns_dbnode_t *node, dns_name_t *name) { dns_rdataset_t rdataset; dns_rdatasetiter_t *rdsiter; isc_boolean_t isdelegation = ISC_FALSE; - isc_boolean_t childkey = ISC_FALSE; + isc_boolean_t hasds = ISC_FALSE; isc_boolean_t atorigin; - isc_boolean_t neednullkey = ISC_FALSE; dns_diff_t diff; char namestr[DNS_NAME_FORMATSIZE]; @@ -842,43 +756,20 @@ signname(dns_dbnode_t *node, dns_name_t *name) { isdelegation = ISC_TRUE; /* - * If this is a delegation point, determine if we need to generate - * a null key. + * If this is a delegation point, look for a DS set. */ if (isdelegation) { - dns_rdataset_t keyset; - dns_ttl_t nullkeyttl; + dns_rdataset_t dsset; - childkey = haschildkey(name); - neednullkey = ISC_TRUE; - nullkeyttl = zonettl; - - dns_rdataset_init(&keyset); - result = dns_db_findrdataset(gdb, node, gversion, - dns_rdatatype_key, 0, 0, &keyset, - NULL); - if (result == ISC_R_SUCCESS && childkey) { - if (hasnullkey(&keyset)) { - fatal("%s has both a signedkey file and " - "null keys in the zone. Aborting.", - namestr); - } - vbprintf(2, "child key for %s found\n", namestr); - neednullkey = ISC_FALSE; - dns_rdataset_disassociate(&keyset); + dns_rdataset_init(&dsset); + result = loadds(name, &dsset); + if (result == ISC_R_SUCCESS) { + result = dns_db_addrdataset(gdb, node, gversion, 0, + &dsset, 0, NULL); + check_result(result, "dns_db_deleterdataset"); + hasds = ISC_TRUE; + dns_rdataset_disassociate(&dsset); } - else if (result == ISC_R_SUCCESS) { - if (hasnullkey(&keyset)) - neednullkey = ISC_FALSE; - nullkeyttl = keyset.ttl; - dns_rdataset_disassociate(&keyset); - } else if (childkey) { - vbprintf(2, "child key for %s found\n", namestr); - neednullkey = ISC_FALSE; - } - - if (neednullkey) - createnullkey(gdb, gversion, name, nullkeyttl); } /* @@ -897,30 +788,23 @@ signname(dns_dbnode_t *node, dns_name_t *name) { if (rdataset.type == dns_rdatatype_sig) goto skip; - /* - * If this is a KEY set at the apex, look for a signedkey file. - */ - if (atorigin && rdataset.type == dns_rdatatype_key) { - importparentsig(&diff, name, &rdataset); - goto skip; - } - /* * If this name is a delegation point, skip all records - * except an NXT set and a KEY set containing a null key. + * except NXT and DS sets. */ if (isdelegation) { - if (!(rdataset.type == dns_rdatatype_nxt || - (rdataset.type == dns_rdatatype_key && - hasnullkey(&rdataset)))) + if (rdataset.type != dns_rdatatype_nxt && + rdataset.type != dns_rdatatype_ds) goto skip; } if (rdataset.type == dns_rdatatype_nxt) { if (!nokeys) - nxt_setbit(&rdataset, dns_rdatatype_sig); - if (neednullkey) - nxt_setbit(&rdataset, dns_rdatatype_key); + nxt_setbit(name, &rdataset, dns_rdatatype_sig, + 1); + if (hasds) + nxt_setbit(name, &rdataset, dns_rdatatype_ds, + 1); } signset(&diff, node, name, &rdataset); @@ -1333,7 +1217,7 @@ loadzonekeys(dns_db_t *db) { for (i = 0; i < nkeys; i++) { signer_key_t *key; - key = newkeystruct(keys[i], ISC_FALSE); + key = newkeystruct(keys[i], ISC_FALSE, ISC_FALSE); ISC_LIST_APPEND(keylist, key, link); } dns_db_detachnode(db, &node); @@ -1381,7 +1265,7 @@ loadzonepubkeys(dns_db_t *db) { goto next; } - key = newkeystruct(pubkey, ISC_FALSE); + key = newkeystruct(pubkey, ISC_FALSE, ISC_FALSE); ISC_LIST_APPEND(keylist, key, link); next: result = dns_rdataset_next(&rdataset); @@ -1391,6 +1275,80 @@ loadzonepubkeys(dns_db_t *db) { dns_db_closeversion(db, ¤tversion, ISC_FALSE); } +static void +writekeyset(void) { + char namestr[DNS_NAME_FORMATSIZE]; + isc_buffer_t namebuf; + unsigned int filenamelen; + char *keyfile; + signer_key_t *key; + unsigned char keybuf[DST_KEY_MAXSIZE]; + dns_diff_t diff; + dns_difftuple_t *tuple = NULL; + dns_db_t *db = NULL; + dns_dbversion_t *version = NULL; + dns_rdata_t rdata; + isc_buffer_t b; + isc_region_t r; + isc_result_t result; + + isc_buffer_init(&namebuf, namestr, sizeof(namestr)); + result = dns_name_tofilenametext(gorigin, ISC_FALSE, &namebuf); + check_result(result, "dns_name_tofilenametext"); + isc_buffer_putuint8(&namebuf, 0); + filenamelen = strlen("keyset-") + strlen(namestr); + if (directory != NULL) + filenamelen += strlen(directory) + 1; + keyfile = isc_mem_get(mctx, filenamelen + 1); + if (keyfile == NULL) + fatal("out of memory"); + if (directory != NULL) + sprintf(keyfile, "%s/", directory); + else + keyfile[0] = 0; + strcat(keyfile, "keyset-"); + strcat(keyfile, namestr); + + dns_diff_init(mctx, &diff); + + for (key = ISC_LIST_HEAD(keylist); + key != NULL; + key = ISC_LIST_NEXT(key, link)) + { + if (!key->keysigning) + continue; + dns_rdata_init(&rdata); + isc_buffer_init(&b, keybuf, sizeof(keybuf)); + result = dst_key_todns(key->key, &b); + check_result(result, "dst_key_todns"); + isc_buffer_usedregion(&b, &r); + dns_rdata_fromregion(&rdata, gclass, dns_rdatatype_key, &r); + result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, gorigin, + zonettl, &rdata, &tuple); + check_result(result, "dns_difftuple_create"); + dns_diff_append(&diff, &tuple); + } + + result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone, + gclass, 0, NULL, &db); + check_result(result, "dns_db_create"); + + result = dns_db_newversion(db, &version); + check_result(result, "dns_db_newversion"); + + result = dns_diff_apply(&diff, db, version); + check_result(result, "dns_diff_apply"); + dns_diff_clear(&diff); + + result = dns_master_dump(mctx, db, version, masterstyle, keyfile); + check_result(result, "dns_master_dump"); + + isc_mem_put(mctx, keyfile, filenamelen + 1); + + dns_db_closeversion(db, &version, ISC_FALSE); + dns_db_detach(&db); +} + static void print_time(FILE *fp) { time_t currenttime; @@ -1438,6 +1396,7 @@ usage(void) { fprintf(stderr, "\t-t:\t"); fprintf(stderr, "print statistics\n"); fprintf(stderr, "\t-n ncpus (number of cpus present)\n"); + fprintf(stderr, "\t-k key_signing_key\n"); fprintf(stderr, "\n"); @@ -1483,6 +1442,8 @@ main(int argc, char *argv[]) { int i, ch; char *startstr = NULL, *endstr = NULL, *classname = NULL; char *origin = NULL, *file = NULL, *output = NULL; + char *dskeyfile[MAXDSKEYS]; + int ndskeys = 0; char *endp; isc_time_t timer_start, timer_finish; signer_key_t *key; @@ -1493,6 +1454,7 @@ main(int argc, char *argv[]) { isc_boolean_t free_output = ISC_FALSE; int tempfilelen; dns_rdataclass_t rdclass; + dns_db_t *udb = NULL; isc_task_t **tasks = NULL; masterstyle = &dns_master_style_explicitttl; @@ -1505,7 +1467,7 @@ main(int argc, char *argv[]) { dns_result_register(); while ((ch = isc_commandline_parse(argc, argv, - "c:s:e:i:v:o:f:ahpr:td:n:S")) + "c:s:e:i:v:o:f:ahpr:td:n:Sk:")) != -1) { switch (ch) { case 'c': @@ -1576,6 +1538,12 @@ main(int argc, char *argv[]) { masterstyle = &dns_master_style_simple; break; + case 'k': + if (ndskeys == MAXDSKEYS) + fatal("too many key-signing keys specified"); + dskeyfile[ndskeys++] = isc_commandline_argument; + break; + case 'h': default: usage(); @@ -1626,6 +1594,9 @@ main(int argc, char *argv[]) { argc -= 1; argv += 1; + if (origin == NULL) + origin = file; + if (output == NULL) { free_output = ISC_TRUE; output = isc_mem_allocate(mctx, @@ -1635,13 +1606,11 @@ main(int argc, char *argv[]) { sprintf(output, "%s.signed", file); } - if (origin == NULL) - origin = file; - gdb = NULL; TIME_NOW(&timer_start); loadzone(file, origin, rdclass, &gdb); gorigin = dns_db_origin(gdb); + gclass = dns_db_class(gdb); zonettl = soattl(); ISC_LIST_INIT(keylist); @@ -1652,6 +1621,8 @@ main(int argc, char *argv[]) { key = ISC_LIST_HEAD(keylist); while (key != NULL) { key->isdefault = ISC_TRUE; + if (ndskeys == 0) + key->keysigning = ISC_TRUE; key = ISC_LIST_NEXT(key, link); } } else { @@ -1684,7 +1655,11 @@ main(int argc, char *argv[]) { key = ISC_LIST_NEXT(key, link); } if (key == NULL) { - key = newkeystruct(newkey, ISC_TRUE); + isc_boolean_t iskeysigning = ISC_FALSE; + if (ndskeys == 0) + iskeysigning = ISC_TRUE; + key = newkeystruct(newkey, ISC_TRUE, + iskeysigning); ISC_LIST_APPEND(keylist, key, link); } else dst_key_free(&newkey); @@ -1693,6 +1668,38 @@ main(int argc, char *argv[]) { loadzonepubkeys(gdb); } + for (i = 0; i < ndskeys; i++) { + dst_key_t *newkey = NULL; + + result = dst_key_fromnamedfile(dskeyfile[i], + DST_TYPE_PUBLIC | + DST_TYPE_PRIVATE, + mctx, &newkey); + if (result != ISC_R_SUCCESS) + fatal("cannot load key %s: %s", dskeyfile[i], + isc_result_totext(result)); + + key = ISC_LIST_HEAD(keylist); + while (key != NULL) { + dst_key_t *dkey = key->key; + if (dst_key_id(dkey) == dst_key_id(newkey) && + dst_key_alg(dkey) == dst_key_alg(newkey) && + dns_name_equal(dst_key_name(dkey), + dst_key_name(newkey))) + { + key->keysigning = ISC_TRUE; + dst_key_free(&dkey); + key->key = newkey; + break; + } + key = ISC_LIST_NEXT(key, link); + } + if (key == NULL) { + key = newkeystruct(newkey, ISC_FALSE, ISC_TRUE); + ISC_LIST_APPEND(keylist, key, link); + } + } + if (ISC_LIST_EMPTY(keylist)) { fprintf(stderr, "%s: warning: No keys specified or found\n", program); @@ -1705,6 +1712,9 @@ main(int argc, char *argv[]) { nxtify(); + if (!nokeys) + writekeyset(); + tempfilelen = strlen(output) + 20; tempfile = isc_mem_get(mctx, tempfilelen); if (tempfile == NULL) @@ -1764,6 +1774,11 @@ main(int argc, char *argv[]) { isc_mem_put(mctx, tasks, ntasks * sizeof(isc_task_t *)); postsign(); + if (udb != NULL) { + dumpdb(udb); + dns_db_detach(&udb); + } + result = isc_stdio_close(fp); check_result(result, "isc_stdio_close"); removefile = ISC_FALSE; diff --git a/bin/named/query.c b/bin/named/query.c index d75a006019..a1a45236a4 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.c,v 1.219 2002/03/28 04:03:50 marka Exp $ */ +/* $Id: query.c,v 1.220 2002/06/17 04:01:08 marka Exp $ */ #include @@ -2045,6 +2045,70 @@ query_addbestns(ns_client_t *client) { } } +static void +query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node) { + dns_name_t *rname; + dns_rdataset_t *rdataset, *sigrdataset; + isc_result_t result; + + CTRACE("query_addds"); + rname = NULL; + rdataset = NULL; + sigrdataset = NULL; + + /* + * We'll need some resources... + */ + rdataset = query_newrdataset(client); + sigrdataset = query_newrdataset(client); + if (rdataset == NULL || sigrdataset == NULL) + return; + + /* + * Look for the DS record, which may or may not be present. + */ + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds, 0, + client->now, rdataset, sigrdataset); + /* + * If we didn't find it, look for an NXT. */ + if (result == ISC_R_NOTFOUND) + result = dns_db_findrdataset(db, node, NULL, + dns_rdatatype_nxt, 0, client->now, + rdataset, sigrdataset); + if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) + goto cleanup; + if (!dns_rdataset_isassociated(rdataset) || + !dns_rdataset_isassociated(sigrdataset)) + goto cleanup; + + /* + * We've already added the NS record, so if the name's not there, + * we have other problems. Use this name rather than calling + * query_addrrset(). + */ + result = dns_message_firstname(client->message, DNS_SECTION_AUTHORITY); + if (result != ISC_R_SUCCESS) + goto cleanup; + + rname = NULL; + dns_message_currentname(client->message, DNS_SECTION_AUTHORITY, + &rname); + result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL); + if (result != ISC_R_SUCCESS) + goto cleanup; + + ISC_LIST_APPEND(rname->list, rdataset, link); + ISC_LIST_APPEND(rname->list, sigrdataset, link); + rdataset = NULL; + sigrdataset = NULL; + + cleanup: + if (rdataset != NULL) + query_putrdataset(client, &rdataset); + if (sigrdataset != NULL) + query_putrdataset(client, &sigrdataset); +} + static void query_resume(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent = (dns_fetchevent_t *)event; @@ -2206,98 +2270,6 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain, return (result); } -static inline isc_result_t -query_findparentkey(ns_client_t *client, dns_name_t *name, - dns_zone_t **zonep, dns_db_t **dbp, - dns_dbversion_t **versionp, dns_dbnode_t **nodep, - dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) -{ - dns_db_t *pdb; - dns_dbnode_t *pnode; - dns_dbversion_t *pversion; - dns_rdataset_t prdataset, psigrdataset; - dns_rdataset_t *psigrdatasetp; - isc_result_t result; - dns_zone_t *pzone; - isc_boolean_t is_zone; - dns_fixedname_t pfoundname; - - /* - * 'name' is at a zone cut. Try to find a KEY for 'name' in - * the deepest ancestor zone of 'name' (if any). If it exists, - * update *zonep, *dbp, *nodep, rdataset, and sigrdataset and - * return ISC_R_SUCCESS. If not, leave them alone and return a - * non-success status. - */ - - pzone = NULL; - pdb = NULL; - pnode = NULL; - pversion = NULL; - dns_rdataset_init(&prdataset); - if (sigrdataset != NULL) - dns_rdataset_init(&psigrdataset); - is_zone = ISC_FALSE; - dns_fixedname_init(&pfoundname); - - result = query_getdb(client, name, DNS_GETDB_NOEXACT, - &pzone, &pdb, &pversion, &is_zone); - if (result != ISC_R_SUCCESS) - goto cleanup; - if (!is_zone) { - result = ISC_R_FAILURE; - goto cleanup; - } - - if (sigrdataset != NULL) - psigrdatasetp = &psigrdataset; - else - psigrdatasetp = NULL; - result = dns_db_find(pdb, name, pversion, dns_rdatatype_key, - client->query.dboptions, - client->now, &pnode, - dns_fixedname_name(&pfoundname), - &prdataset, psigrdatasetp); - if (result == ISC_R_SUCCESS) { - if (dns_rdataset_isassociated(rdataset)) - dns_rdataset_disassociate(rdataset); - dns_rdataset_clone(&prdataset, rdataset); - if (sigrdataset != NULL) { - if (dns_rdataset_isassociated(sigrdataset)) - dns_rdataset_disassociate(sigrdataset); - if (dns_rdataset_isassociated(&psigrdataset)) - dns_rdataset_clone(&psigrdataset, sigrdataset); - } - if (*nodep != NULL) - dns_db_detachnode(*dbp, nodep); - *nodep = pnode; - pnode = NULL; - *versionp = pversion; - if (*dbp != NULL) - dns_db_detach(dbp); - *dbp = pdb; - pdb = NULL; - if (*zonep != NULL) - dns_zone_detach(zonep); - *zonep = pzone; - pzone = NULL; - } - - cleanup: - if (dns_rdataset_isassociated(&prdataset)) - dns_rdataset_disassociate(&prdataset); - if (sigrdataset != NULL && dns_rdataset_isassociated(&psigrdataset)) - dns_rdataset_disassociate(&psigrdataset); - if (pnode != NULL) - dns_db_detachnode(pdb, &pnode); - if (pdb != NULL) - dns_db_detach(&pdb); - if (pzone != NULL) - dns_zone_detach(&pzone); - - return (result); -} - #define MAX_RESTARTS 16 #define QUERY_ERROR(r) \ @@ -2419,6 +2391,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) dns_zone_t *zone; dns_rdata_cname_t cname; dns_rdata_dname_t dname; + unsigned int options; CTRACE("query_find"); @@ -2508,7 +2481,11 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) /* * First we must find the right database. */ - result = query_getdb(client, client->query.qname, 0, &zone, &db, + options = 0; + if (dns_rdatatype_atparent(qtype) && + !dns_name_equal(client->query.qname, dns_rootname)) + options |= DNS_GETDB_NOEXACT; + result = query_getdb(client, client->query.qname, options, &zone, &db, &version, &is_zone); if (result != ISC_R_SUCCESS) { if (result == DNS_R_REFUSED) @@ -2560,63 +2537,6 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) client->query.dboptions, client->now, &node, fname, rdataset, sigrdataset); - /* - * We interrupt our normal query processing to bring you this special - * case... - * - * RFC 2535 (DNSSEC), section 2.3.4, discusses various special - * cases that can occur at delegation points. - * - * One of these cases is that the NULL KEY for an unsecure zone - * may occur in the delegating zone instead of in the delegated zone. - * If we're authoritative for both zones, we need to look for the - * key in the delegator if we didn't find it in the delegatee. If - * we didn't do this, a client doing DNSSEC validation could fail - * because it couldn't get the NULL KEY. - */ - if (type == dns_rdatatype_key && - is_zone && - result == DNS_R_NXRRSET && - !dns_db_issecure(db) && - dns_name_equal(client->query.qname, dns_db_origin(db))) { - /* - * We're looking for a KEY at the top of an unsecure zone, - * and we didn't find it. - */ - result = query_findparentkey(client, client->query.qname, - &zone, &db, &version, &node, - rdataset, sigrdataset); - if (result == ISC_R_SUCCESS) { - /* - * We found the parent KEY. - * - * zone, db, version, node, rdataset, and sigrdataset - * have all been updated to refer to the parent's - * data. We will resume query processing as if - * we had looked for the KEY in the parent zone in - * the first place. - * - * We need to set fname correctly. We do this here - * instead of in query_findparentkey() because - * dns_name_copy() can fail (though it shouldn't - * ever do so since we should have enough space). - */ - result = dns_name_copy(client->query.qname, - fname, NULL); - if (result != ISC_R_SUCCESS) { - QUERY_ERROR(DNS_R_SERVFAIL); - goto cleanup; - } - } else { - /* - * We couldn't find the KEY in a parent zone. - * Continue with processing of the original - * results of dns_db_find(). - */ - result = DNS_R_NXRRSET; - } - } - resume: CTRACE("query_find: resume"); switch (result) { @@ -2730,6 +2650,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) &rdataset, sigrdatasetp, dbuf, DNS_SECTION_AUTHORITY); client->query.gluedb = NULL; + if (WANTDNSSEC(client) && dns_db_issecure(db)) + query_addds(client, db, node); } else { /* * We might have a better answer or delegation @@ -2789,6 +2711,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) /* * Recurse! */ + /* XXXBEW look at this? */ if (type == dns_rdatatype_key) result = query_recurse(client, qtype, NULL, NULL); @@ -2826,6 +2749,9 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) client->query.gluedb = NULL; client->query.attributes &= ~NS_QUERYATTR_CACHEGLUEOK; + if (WANTDNSSEC(client) && + !dns_rdataset_isassociated(sigrdataset)) + query_addds(client, db, node); } } goto cleanup; @@ -2857,8 +2783,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) /* * Add NXT record if we found one. */ - if (dns_rdataset_isassociated(rdataset)) { - if (WANTDNSSEC(client)) + if (WANTDNSSEC(client)) { + if (dns_rdataset_isassociated(rdataset)) query_addrrset(client, &fname, &rdataset, &sigrdataset, NULL, DNS_SECTION_AUTHORITY); diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 5b45b7df32..5d62fd16e0 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -15,7 +15,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: conf.sh.in,v 1.23 2001/08/01 19:00:58 gson Exp $ +# $Id: conf.sh.in,v 1.24 2002/06/17 04:01:10 marka Exp $ # # Common configuration data for system tests, to be sourced into @@ -37,8 +37,6 @@ RNDC=$TOP/bin/rndc/rndc NSUPDATE=$TOP/bin/nsupdate/nsupdate KEYGEN=$TOP/bin/dnssec/dnssec-keygen SIGNER=$TOP/bin/dnssec/dnssec-signzone -KEYSIGNER=$TOP/bin/dnssec/dnssec-signkey -KEYSETTOOL=$TOP/bin/dnssec/dnssec-makekeyset # The "stress" test is not run by default since it creates enough # load on the machine to make it unusable to other users. diff --git a/bin/tests/system/dnssec/ns1/sign.sh b/bin/tests/system/dnssec/ns1/sign.sh index aa4e53100b..c99aa35aba 100644 --- a/bin/tests/system/dnssec/ns1/sign.sh +++ b/bin/tests/system/dnssec/ns1/sign.sh @@ -15,7 +15,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: sign.sh,v 1.13 2001/09/17 17:42:04 bwelling Exp $ +# $Id: sign.sh,v 1.14 2002/06/17 04:01:12 marka Exp $ SYSTEMTESTTOP=../.. . $SYSTEMTESTTOP/conf.sh @@ -26,17 +26,11 @@ zone=. infile=root.db.in zonefile=root.db -keyname=`$KEYGEN -r $RANDFILE -a RSA -b 768 -n zone $zone` - (cd ../ns2 && sh sign.sh ) cp ../ns2/keyset-example. . -$KEYSIGNER -r $RANDFILE keyset-example. $keyname > /dev/null - -cat signedkey-example. >> ../ns2/example.db.signed - -$KEYSETTOOL -r $RANDFILE -t 3600 $keyname > /dev/null +keyname=`$KEYGEN -r $RANDFILE -a RSA -b 768 -n zone $zone` cat $infile $keyname.key > $zonefile diff --git a/bin/tests/system/dnssec/ns2/sign.sh b/bin/tests/system/dnssec/ns2/sign.sh index 56022836c4..5708b627ce 100644 --- a/bin/tests/system/dnssec/ns2/sign.sh +++ b/bin/tests/system/dnssec/ns2/sign.sh @@ -15,7 +15,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: sign.sh,v 1.19 2002/02/20 03:33:55 marka Exp $ +# $Id: sign.sh,v 1.20 2002/06/17 04:01:14 marka Exp $ SYSTEMTESTTOP=../.. . $SYSTEMTESTTOP/conf.sh @@ -26,29 +26,21 @@ zone=example. infile=example.db.in zonefile=example.db -keyname=`$KEYGEN -r $RANDFILE -a DSA -b 768 -n zone $zone` - -# Have the child generate a zone key and pass it to us, -# sign it, and pass it back +# Have the child generate a zone key and pass it to us. ( cd ../ns3 && sh sign.sh ) for subdomain in secure bogus dynamic do cp ../ns3/keyset-$subdomain.example. . - - $KEYSIGNER -r $RANDFILE keyset-$subdomain.example. $keyname > /dev/null - - # This will leave two copies of the child's zone key in the signed db file; - # that shouldn't cause any problems. - cat signedkey-$subdomain.example. >>../ns3/$subdomain.example.db.signed done -$KEYSETTOOL -r $RANDFILE -t 3600 $keyname > /dev/null +keyname1=`$KEYGEN -r $RANDFILE -a DSA -b 768 -n zone $zone` +keyname2=`$KEYGEN -r $RANDFILE -a DSA -b 768 -n zone $zone` -cat $infile $keyname.key >$zonefile +cat $infile $keyname1.key $keyname2.key >$zonefile -$SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null +$SIGNER -r $RANDFILE -o $zone -k $keyname1 $zonefile $keyname2 > /dev/null # Sign the privately secure file diff --git a/bin/tests/system/dnssec/ns3/sign.sh b/bin/tests/system/dnssec/ns3/sign.sh index 70f704a18a..ead54452b4 100644 --- a/bin/tests/system/dnssec/ns3/sign.sh +++ b/bin/tests/system/dnssec/ns3/sign.sh @@ -15,7 +15,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: sign.sh,v 1.16 2002/02/20 03:33:59 marka Exp $ +# $Id: sign.sh,v 1.17 2002/06/17 04:01:15 marka Exp $ RANDFILE=../random.data @@ -25,8 +25,6 @@ zonefile=secure.example.db keyname=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 768 -n zone $zone` -$KEYSETTOOL -r $RANDFILE -t 3600 $keyname.key > /dev/null - cat $infile $keyname.key >$zonefile $SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null @@ -37,8 +35,6 @@ zonefile=bogus.example.db keyname=`$KEYGEN -r $RANDFILE -a RSA -b 768 -n zone $zone` -$KEYSETTOOL -r $RANDFILE -t 3600 $keyname.key > /dev/null - cat $infile $keyname.key >$zonefile $SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null @@ -49,8 +45,6 @@ zonefile=dynamic.example.db keyname=`$KEYGEN -r $RANDFILE -a RSA -b 768 -n zone $zone` -$KEYSETTOOL -r $RANDFILE -t 3600 $keyname.key > /dev/null - cat $infile $keyname.key >$zonefile $SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null @@ -61,8 +55,6 @@ zonefile=keyless.example.db keyname=`$KEYGEN -r $RANDFILE -a RSA -b 768 -n zone $zone` -$KEYSETTOOL -r $RANDFILE -t 3600 $keyname.key > /dev/null - cat $infile $keyname.key >$zonefile $SIGNER -r $RANDFILE -o $zone $zonefile > /dev/null diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 03a61d7a43..760473fd78 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -15,7 +15,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: tests.sh,v 1.39 2002/02/20 03:33:50 marka Exp $ +# $Id: tests.sh,v 1.40 2002/06/17 04:01:11 marka Exp $ SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh @@ -25,7 +25,7 @@ n=0 rm -f dig.out.* -DIGOPTS="+tcp +noadd +nosea +nostat +noquest +nocmd +dnssec -p 5300" +DIGOPTS="+tcp +noadd +nosea +nostat +nocmd +dnssec -p 5300" # Check the example. domain @@ -113,6 +113,14 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` +echo "I:checking that insecurity proofs fail with a misconfigured trusted key ($n)" +ret=0 +$DIG $DIGOPTS a.insecure.example. a @10.53.0.5 > dig.out.ns5.test$n || ret=1 +grep "SERVFAIL" dig.out.ns5.test$n > /dev/null || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + echo "I:checking that validation fails when key record is missing ($n)" ret=0 $DIG $DIGOPTS a.b.keyless.example. a @10.53.0.4 > dig.out.ns4.test$n || ret=1 diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index 85a4dfb8e9..1a9a019cfc 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.129 2002/03/07 06:29:36 marka Exp $ +# $Id: Makefile.in,v 1.130 2002/06/17 04:01:17 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -46,7 +46,7 @@ DSTOBJS = sec/dst/dst_api.@O@ \ OBJS = a6.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ cache.@O@ callbacks.@O@ compress.@O@ \ db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \ - dnssec.@O@ forward.@O@ journal.@O@ keytable.@O@ \ + dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \ lib.@O@ log.@O@ lookup.@O@ \ master.@O@ masterdump.@O@ message.@O@ \ name.@O@ ncache.@O@ nxt.@O@ order.@O@ peer.@O@ \ @@ -62,7 +62,7 @@ OBJS = a6.@O@ acl.@O@ adb.@O@ byaddr.@O@ \ SRCS = a6.c acl.c adb.c byaddr.c \ cache.c callbacks.c compress.c \ db.c dbiterator.c dbtable.c diff.c dispatch.c \ - dnssec.c forward.c journal.c keytable.c \ + dnssec.c ds.c forward.c journal.c keytable.c \ lib.c log.c lookup.c \ master.c masterdump.c message.c \ name.c ncache.c nxt.c order.c peer.c \ diff --git a/lib/dns/ds.c b/lib/dns/ds.c new file mode 100644 index 0000000000..c89e102722 --- /dev/null +++ b/lib/dns/ds.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: ds.c,v 1.2 2002/06/17 04:01:18 marka Exp $ */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +isc_result_t +dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key, + unsigned int digest_type, unsigned char *buffer, + dns_rdata_t *rdata) +{ + isc_sha1_t sha1; + dns_fixedname_t fname; + dns_name_t *name; + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + isc_region_t r; + isc_buffer_t b; + dns_rdata_ds_t ds; + + if (digest_type != DNS_DSDIGEST_SHA1) + return (ISC_R_NOTIMPLEMENTED); + + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + (void)dns_name_downcase(owner, name, NULL); + + memset(buffer, 0, DNS_DS_BUFFERSIZE); + isc_buffer_init(&b, buffer, DNS_DS_BUFFERSIZE); + + isc_sha1_init(&sha1); + dns_name_toregion(name, &r); + isc_sha1_update(&sha1, r.base, r.length); + dns_rdata_toregion(key, &r); + INSIST(r.length >= 4); + isc_sha1_update(&sha1, r.base, r.length); + isc_sha1_final(&sha1, digest); + + ds.mctx = NULL; + ds.common.rdclass = key->rdclass; + ds.common.rdtype = dns_rdatatype_ds; + ds.algorithm = r.base[3]; + ds.key_tag = dst_region_computeid(&r, ds.algorithm); + ds.digest_type = DNS_DSDIGEST_SHA1; + ds.length = ISC_SHA1_DIGESTLENGTH; + ds.digest = digest; + + return (dns_rdata_fromstruct(rdata, key->rdclass, dns_rdatatype_ds, + &ds, &b)); +} diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index 4397333855..e2155105d3 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -13,7 +13,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# $Id: Makefile.in,v 1.45 2001/11/09 23:09:29 bwelling Exp $ +# $Id: Makefile.in,v 1.46 2002/06/17 04:01:28 marka Exp $ srcdir = @srcdir@ VPATH = @srcdir@ @@ -24,7 +24,7 @@ top_srcdir = @top_srcdir@ HEADERS = a6.h acl.h adb.h byaddr.h cache.h callbacks.h \ cert.h compress.h \ db.h dbiterator.h dbtable.h diff.h dispatch.h \ - dnssec.h events.h fixedname.h journal.h keyflags.h \ + dnssec.h ds.h events.h fixedname.h journal.h keyflags.h \ keytable.h keyvalues.h lib.h log.h master.h masterdump.h \ message.h name.h ncache.h \ nxt.h peer.h rbt.h rcode.h \ diff --git a/lib/dns/include/dns/ds.h b/lib/dns/include/dns/ds.h new file mode 100644 index 0000000000..a6e6843c5e --- /dev/null +++ b/lib/dns/include/dns/ds.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: ds.h,v 1.2 2002/06/17 04:01:29 marka Exp $ */ + +#ifndef DNS_DS_H +#define DNS_DS_H 1 + +#include + +#include + +#define DNS_DSDIGEST_SHA1 (1) + +/* + * Assuming SHA-1 digest type. + */ +#define DNS_DS_BUFFERSIZE (24) + +ISC_LANG_BEGINDECLS + +isc_result_t +dns_ds_buildrdata(dns_name_t *owner, dns_rdata_t *key, + unsigned int digest_type, unsigned char *buffer, + dns_rdata_t *rdata); +/* + * Build the rdata of a DS record. + * + * Requires: + * key Points to a valid DNS KEY record. + * buffer Points to a temporary buffer of at least + * DNS_DS_BUFFERSIZE bytes. + * rdata Points to an initialized dns_rdata_t. + * + * Ensures: + * *rdata Contains a valid DS rdata. The 'data' member refers + * to 'buffer'. + */ + +ISC_LANG_ENDDECLS + +#endif /* DNS_DS_H */ diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h index 38e62d2608..21b09a543b 100644 --- a/lib/dns/include/dns/rdata.h +++ b/lib/dns/include/dns/rdata.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.h,v 1.54 2002/02/20 03:34:36 marka Exp $ */ +/* $Id: rdata.h,v 1.55 2002/06/17 04:01:31 marka Exp $ */ #ifndef DNS_RDATA_H #define DNS_RDATA_H 1 @@ -610,6 +610,17 @@ dns_rdatatype_notquestion(dns_rdatatype_t type); * */ +isc_boolean_t +dns_rdatatype_atparent(dns_rdatatype_t type); +/* + * Return true iff rdata of type 'type' should appear at the parent of + * a zone cut. + * + * Requires: + * 'type' is a valid rdata type. + * + */ + unsigned int dns_rdatatype_attributes(dns_rdatatype_t rdtype); /* @@ -640,6 +651,8 @@ dns_rdatatype_attributes(dns_rdatatype_t rdtype); #define DNS_RDATATYPEATTR_QUESTIONONLY 0x00000080U /* is META, and can NOT be in a question section */ #define DNS_RDATATYPEATTR_NOTQUESTION 0x00000100U +/* Is present at zone cuts in the parent, not the child */ +#define DNS_RDATATYPEATTR_ATPARENT 0x00000200U dns_rdatatype_t dns_rdata_covers(dns_rdata_t *rdata); diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index b3d23a7b6c..3983d74b0d 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.h,v 1.88 2002/03/14 18:34:46 bwelling Exp $ */ +/* $Id: result.h,v 1.89 2002/06/17 04:01:32 marka Exp $ */ #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 @@ -120,8 +120,9 @@ #define DNS_R_FROZEN (ISC_RESULTCLASS_DNS + 81) #define DNS_R_UNKNOWNFLAG (ISC_RESULTCLASS_DNS + 82) #define DNS_R_EXPECTEDRESPONSE (ISC_RESULTCLASS_DNS + 83) +#define DNS_R_NOVALIDDS (ISC_RESULTCLASS_DNS + 84) -#define DNS_R_NRESULTS 84 /* Number of results */ +#define DNS_R_NRESULTS 85 /* Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index da55dae692..81020ee3bc 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: validator.h,v 1.20 2001/09/14 20:53:33 gson Exp $ */ +/* $Id: validator.h,v 1.21 2002/06/17 04:01:33 marka Exp $ */ #ifndef DNS_VALIDATOR_H #define DNS_VALIDATOR_H 1 @@ -93,8 +93,8 @@ struct dns_validator { unsigned int attributes; dns_validatorevent_t * event; dns_fetch_t * fetch; - dns_validator_t * keyvalidator; - dns_validator_t * authvalidator; + dns_validator_t * subvalidator; + dns_validator_t * parent; dns_keytable_t * keytable; dns_keynode_t * keynode; dst_key_t * key; @@ -106,8 +106,11 @@ struct dns_validator { dns_rdataset_t * currentset; isc_boolean_t seensig; dns_rdataset_t * keyset; + dns_rdataset_t * dsset; + dns_rdataset_t * soaset; dns_rdataset_t frdataset; dns_rdataset_t fsigrdataset; + dns_fixedname_t fname; ISC_LINK(dns_validator_t) link; }; diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 9dc15ae5ee..06c5436a80 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.175 2002/05/28 05:49:20 marka Exp $ */ +/* $Id: rbtdb.c,v 1.176 2002/06/17 04:01:20 marka Exp $ */ /* * Principal Author: Bob Halley @@ -1839,7 +1839,8 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, */ if (node->find_callback && (node != search.rbtdb->origin_node || - IS_STUB(search.rbtdb))) + IS_STUB(search.rbtdb)) && + !dns_rdatatype_atparent(type)) maybe_zonecut = ISC_TRUE; } diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index e0d7ce71a6..6984b0c49a 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.c,v 1.171 2002/03/27 23:31:32 marka Exp $ */ +/* $Id: rdata.c,v 1.172 2002/06/17 04:01:21 marka Exp $ */ #include #include @@ -2047,6 +2047,13 @@ dns_rdatatype_questiononly(dns_rdatatype_t type) { return (ISC_FALSE); } +isc_boolean_t +dns_rdatatype_atparent(dns_rdatatype_t type) { + if ((dns_rdatatype_attributes(type) & DNS_RDATATYPEATTR_ATPARENT) != 0) + return (ISC_TRUE); + return (ISC_FALSE); +} + isc_boolean_t dns_rdataclass_ismeta(dns_rdataclass_t rdclass) { REQUIRE(rdclass < 65536); diff --git a/lib/dns/rdata/generic/ds_43.c b/lib/dns/rdata/generic/ds_43.c new file mode 100644 index 0000000000..009b8878bc --- /dev/null +++ b/lib/dns/rdata/generic/ds_43.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: ds_43.c,v 1.2 2002/06/17 04:01:35 marka Exp $ */ + +/* draft-ietf-dnsext-delegation-signer-05.txt */ + +#ifndef RDATA_GENERIC_DS_43_C +#define RDATA_GENERIC_DS_43_C + +#define RRTYPE_DS_ATTRIBUTES (DNS_RDATATYPEATTR_ATPARENT) + +static inline isc_result_t +fromtext_ds(ARGS_FROMTEXT) { + isc_token_t token; + + REQUIRE(type == 43); + + UNUSED(type); + UNUSED(rdclass); + UNUSED(origin); + UNUSED(downcase); + UNUSED(callbacks); + + /* + * Key tag. + */ + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, + ISC_FALSE)); + if (token.value.as_ulong > 0xffff) + RETTOK(ISC_R_RANGE); + RETERR(uint16_tobuffer(token.value.as_ulong, target)); + + /* + * Algorithm. + */ + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, + ISC_FALSE)); + if (token.value.as_ulong > 0xff) + RETTOK(ISC_R_RANGE); + RETERR(uint8_tobuffer(token.value.as_ulong, target)); + + /* + * Digest type. + */ + RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, + ISC_FALSE)); + if (token.value.as_ulong > 0xff) + RETTOK(ISC_R_RANGE); + RETERR(uint8_tobuffer(token.value.as_ulong, target)); + type = token.value.as_ulong; + + /* + * Digest. + */ + return (isc_hex_tobuffer(lexer, target, -1)); +} + +static inline isc_result_t +totext_ds(ARGS_TOTEXT) { + isc_region_t sr; + char buf[sizeof("64000 ")]; + unsigned int n; + + REQUIRE(rdata->type == 43); + REQUIRE(rdata->length != 0); + + UNUSED(tctx); + + dns_rdata_toregion(rdata, &sr); + + /* + * Key tag. + */ + n = uint16_fromregion(&sr); + isc_region_consume(&sr, 2); + sprintf(buf, "%u ", n); + RETERR(str_totext(buf, target)); + + /* + * Algorithm. + */ + n = uint8_fromregion(&sr); + isc_region_consume(&sr, 1); + sprintf(buf, "%u ", n); + RETERR(str_totext(buf, target)); + + /* + * Digest type. + */ + n = uint8_fromregion(&sr); + isc_region_consume(&sr, 1); + sprintf(buf, "%u", n); + RETERR(str_totext(buf, target)); + + /* + * Digest. + */ + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) + RETERR(str_totext(" (", target)); + RETERR(str_totext(tctx->linebreak, target)); + RETERR(isc_hex_totext(&sr, tctx->width - 2, tctx->linebreak, target)); + if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) + RETERR(str_totext(" )", target)); + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +fromwire_ds(ARGS_FROMWIRE) { + isc_region_t sr; + + REQUIRE(type == 43); + + UNUSED(type); + UNUSED(rdclass); + UNUSED(dctx); + UNUSED(downcase); + + isc_buffer_activeregion(source, &sr); + if (sr.length < 4) + return (ISC_R_UNEXPECTEDEND); + + isc_buffer_forward(source, sr.length); + return (mem_tobuffer(target, sr.base, sr.length)); +} + +static inline isc_result_t +towire_ds(ARGS_TOWIRE) { + isc_region_t sr; + + REQUIRE(rdata->type == 43); + REQUIRE(rdata->length != 0); + + UNUSED(cctx); + + dns_rdata_toregion(rdata, &sr); + return (mem_tobuffer(target, sr.base, sr.length)); +} + +static inline int +compare_ds(ARGS_COMPARE) { + isc_region_t r1; + isc_region_t r2; + + REQUIRE(rdata1->type == rdata2->type); + REQUIRE(rdata1->rdclass == rdata2->rdclass); + REQUIRE(rdata1->type == 43); + REQUIRE(rdata1->length != 0); + REQUIRE(rdata2->length != 0); + + dns_rdata_toregion(rdata1, &r1); + dns_rdata_toregion(rdata2, &r2); + return (isc_region_compare(&r1, &r2)); +} + +static inline isc_result_t +fromstruct_ds(ARGS_FROMSTRUCT) { + dns_rdata_ds_t *ds = source; + + REQUIRE(type == 43); + REQUIRE(source != NULL); + REQUIRE(ds->common.rdtype == type); + REQUIRE(ds->common.rdclass == rdclass); + + UNUSED(type); + UNUSED(rdclass); + + RETERR(uint16_tobuffer(ds->key_tag, target)); + RETERR(uint8_tobuffer(ds->algorithm, target)); + RETERR(uint8_tobuffer(ds->digest_type, target)); + + return (mem_tobuffer(target, ds->digest, ds->length)); +} + +static inline isc_result_t +tostruct_ds(ARGS_TOSTRUCT) { + dns_rdata_ds_t *ds = target; + isc_region_t region; + + REQUIRE(rdata->type == 43); + REQUIRE(target != NULL); + REQUIRE(rdata->length != 0); + + ds->common.rdclass = rdata->rdclass; + ds->common.rdtype = rdata->type; + ISC_LINK_INIT(&ds->common, link); + + dns_rdata_toregion(rdata, ®ion); + + ds->key_tag = uint16_fromregion(®ion); + isc_region_consume(®ion, 2); + ds->algorithm = uint8_fromregion(®ion); + isc_region_consume(®ion, 1); + ds->digest_type = uint8_fromregion(®ion); + isc_region_consume(®ion, 1); + ds->length = region.length; + + ds->digest = mem_maybedup(mctx, region.base, region.length); + if (ds->digest == NULL) + return (ISC_R_NOMEMORY); + + ds->mctx = mctx; + return (ISC_R_SUCCESS); +} + +static inline void +freestruct_ds(ARGS_FREESTRUCT) { + dns_rdata_ds_t *ds = source; + + REQUIRE(ds != NULL); + REQUIRE(ds->common.rdtype == 43); + + if (ds->mctx == NULL) + return; + + if (ds->digest != NULL) + isc_mem_free(ds->mctx, ds->digest); + ds->mctx = NULL; +} + +static inline isc_result_t +additionaldata_ds(ARGS_ADDLDATA) { + REQUIRE(rdata->type == 43); + + UNUSED(rdata); + UNUSED(add); + UNUSED(arg); + + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +digest_ds(ARGS_DIGEST) { + isc_region_t r; + + REQUIRE(rdata->type == 43); + + dns_rdata_toregion(rdata, &r); + + return ((digest)(arg, &r)); +} + +#endif /* RDATA_GENERIC_DS_43_C */ diff --git a/lib/dns/rdata/generic/ds_43.h b/lib/dns/rdata/generic/ds_43.h new file mode 100644 index 0000000000..81e49bb28b --- /dev/null +++ b/lib/dns/rdata/generic/ds_43.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: ds_43.h,v 1.2 2002/06/17 04:01:37 marka Exp $ */ + +/* draft-ietf-dnsext-delegation-signer-05.txt */ +#ifndef GENERIC_DS_43_H +#define GENERIC_DS_43_H 1 + +typedef struct dns_rdata_ds { + dns_rdatacommon_t common; + isc_mem_t *mctx; + isc_uint16_t key_tag; + isc_uint8_t algorithm; + isc_uint8_t digest_type; + isc_uint16_t length; + unsigned char *digest; +} dns_rdata_ds_t; + +#endif /* GENERIC_DS_43_H */ diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index ef5a67e8e0..0b1ec444a2 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.242 2002/05/27 06:30:24 marka Exp $ */ +/* $Id: resolver.c,v 1.243 2002/06/17 04:01:24 marka Exp $ */ #include @@ -2198,7 +2198,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, * nameservers, and we're not in forward-only mode, * so find the best nameservers to use. */ - if (type == dns_rdatatype_key) + if (dns_rdatatype_atparent(type)) findoptions |= DNS_DBFIND_NOEXACT; dns_fixedname_init(&qdomain); result = dns_view_findzonecut(res->view, name, @@ -3478,7 +3478,7 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, { isc_result_t result; dns_message_t *message; - dns_name_t *name, *qname, *ns_name, *soa_name; + dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name; dns_rdataset_t *rdataset, *ns_rdataset; isc_boolean_t done, aa, negative_response; dns_rdatatype_t type; @@ -3543,6 +3543,7 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, ns_name = NULL; ns_rdataset = NULL; soa_name = NULL; + ds_name = NULL; result = dns_message_firstname(message, section); while (!done && result == ISC_R_SUCCESS) { name = NULL; @@ -3591,11 +3592,22 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, return (DNS_R_FORMERR); soa_name = name; } - negative_response = ISC_TRUE; - name->attributes |= - DNS_NAMEATTR_NCACHE; - rdataset->attributes |= - DNS_RDATASETATTR_NCACHE; + /* + * This is wrong, but maybe it'll + * work for now. + */ + if (ns_name == NULL) { + negative_response = ISC_TRUE; + name->attributes |= + DNS_NAMEATTR_NCACHE; + rdataset->attributes |= + DNS_RDATASETATTR_NCACHE; + } else { + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + } if (aa) rdataset->trust = dns_trust_authauthority; @@ -3606,6 +3618,33 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname, * No additional data needs to be * marked. */ + } else if (type == dns_rdatatype_ds) { + /* + * DS or SIG DS. + * + * These should only be here if + * this is a referral, and there + * should only be one DS. + */ + if (negative_response) + return (DNS_R_FORMERR); + if (rdataset->type == + dns_rdatatype_ds) { + if (ds_name != NULL && + name != ds_name) + return (DNS_R_FORMERR); + ds_name = name; + } + name->attributes |= + DNS_NAMEATTR_CACHE; + rdataset->attributes |= + DNS_RDATASETATTR_CACHE; + if (aa) + rdataset->trust = + dns_trust_authauthority; + else + rdataset->trust = + dns_trust_additional; } } } @@ -4112,6 +4151,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) { isc_time_t tnow, *finish; dns_adbaddrinfo_t *addrinfo; unsigned int options; + unsigned int findoptions; REQUIRE(VALID_QUERY(query)); fctx = query->fctx; @@ -4548,10 +4588,14 @@ resquery_response(isc_task_t *task, isc_event_t *event) { fctx_done(fctx, DNS_R_SERVFAIL); return; } + findoptions = 0; + if (dns_rdatatype_atparent(fctx->type)) + findoptions |= DNS_DBFIND_NOEXACT; result = dns_view_findzonecut(fctx->res->view, &fctx->domain, fname, - now, 0, ISC_TRUE, + now, findoptions, + ISC_TRUE, &fctx->nameservers, NULL); if (result != ISC_R_SUCCESS) { diff --git a/lib/dns/result.c b/lib/dns/result.c index 8ce4aeb103..a65e32c280 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.c,v 1.98 2002/03/14 18:34:44 bwelling Exp $ */ +/* $Id: result.c,v 1.99 2002/06/17 04:01:25 marka Exp $ */ #include @@ -125,7 +125,8 @@ static const char *text[DNS_R_NRESULTS] = { "obsolete", /* 80 DNS_R_OBSOLETE */ "already frozen", /* 81 DNS_R_FROZEN */ "unknown flag", /* 82 DNS_R_UNKNOWNFLAG */ - "expected a response" /* 83 DNS_R_EXPECTEDRESPONSE */ + "expected a response", /* 83 DNS_R_EXPECTEDRESPONSE */ + "no valid DS" /* 84 DNS_R_NOVALIDDS */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 78a6705cb4..3bb82c2cce 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -15,7 +15,7 @@ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: validator.c,v 1.104 2002/04/29 23:50:24 marka Exp $ */ +/* $Id: validator.c,v 1.105 2002/06/17 04:01:26 marka Exp $ */ #include @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -46,13 +47,12 @@ #define VALATTR_SHUTDOWN 0x01 #define VALATTR_FOUNDNONEXISTENCE 0x02 #define VALATTR_TRIEDVERIFY 0x04 +#define VALATTR_NEGATIVE 0x08 +#define VALATTR_INSECURITY 0x10 #define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0) static void -nullkeyvalidated(isc_task_t *task, isc_event_t *event); - -static isc_boolean_t -containsnullkey(dns_validator_t *val, dns_rdataset_t *rdataset); +destroy(dns_validator_t *val); static isc_result_t get_dst_key(dns_validator_t *val, dns_rdata_sig_t *siginfo, @@ -61,6 +61,9 @@ get_dst_key(dns_validator_t *val, dns_rdata_sig_t *siginfo, static isc_result_t validate(dns_validator_t *val, isc_boolean_t resume); +static isc_result_t +validatezonekey(dns_validator_t *val, isc_boolean_t resume); + static isc_result_t nxtvalidate(dns_validator_t *val, isc_boolean_t resume); @@ -76,6 +79,11 @@ static void validator_log(dns_validator_t *val, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4); +static void +validator_logcreate(dns_validator_t *val, + dns_name_t *name, dns_rdatatype_t type, + const char *caller, const char *operation); + static void validator_done(dns_validator_t *val, isc_result_t result) { isc_task_t *task; @@ -96,6 +104,22 @@ validator_done(dns_validator_t *val, isc_result_t result) { isc_task_sendanddetach(&task, (isc_event_t **)&val->event); } +static inline isc_boolean_t +exit_check(dns_validator_t *val) { + /* + * Caller must be holding the lock. + */ + if (!SHUTDOWN(val)) + return (ISC_FALSE); + + INSIST(val->event == NULL); + + if (val->fetch != NULL || val->subvalidator != NULL) + return (ISC_FALSE); + + return (ISC_TRUE); +} + static void auth_nonpending(dns_message_t *message) { isc_result_t result; @@ -123,6 +147,7 @@ fetch_callback_validator(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent; dns_validator_t *val; dns_rdataset_t *rdataset; + isc_boolean_t want_destroy; isc_result_t result; isc_result_t eresult; @@ -152,10 +177,8 @@ fetch_callback_validator(isc_task_t *task, isc_event_t *event) { val->keyset = &val->frdataset; } result = validate(val, ISC_TRUE); - if (result != DNS_R_WAIT) { + if (result != DNS_R_WAIT) validator_done(val, result); - goto out; - } } else { validator_log(val, ISC_LOG_DEBUG(3), "fetch_callback_validator: got %s", @@ -165,24 +188,18 @@ fetch_callback_validator(isc_task_t *task, isc_event_t *event) { else validator_done(val, DNS_R_NOVALIDKEY); } - - out: + want_destroy = exit_check(val); UNLOCK(&val->lock); - /* - * Free stuff from the event. - */ - if (dns_rdataset_isassociated(&val->frdataset) && - val->keyset != &val->frdataset) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + if (want_destroy) + destroy(val); } static void -fetch_callback_nullkey(isc_task_t *task, isc_event_t *event) { +dsfetched(isc_task_t *task, isc_event_t *event) { dns_fetchevent_t *devent; dns_validator_t *val; - dns_rdataset_t *rdataset, *sigrdataset; + dns_rdataset_t *rdataset; + isc_boolean_t want_destroy; isc_result_t result; isc_result_t eresult; @@ -191,106 +208,105 @@ fetch_callback_nullkey(isc_task_t *task, isc_event_t *event) { devent = (dns_fetchevent_t *)event; val = devent->ev_arg; rdataset = &val->frdataset; - sigrdataset = &val->fsigrdataset; eresult = devent->result; + isc_event_free(&event); dns_resolver_destroyfetch(&val->fetch); INSIST(val->event != NULL); - validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_nullkey"); - + validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched"); LOCK(&val->lock); if (eresult == ISC_R_SUCCESS) { - if (!containsnullkey(val, rdataset)) { - /* - * No null key. - */ - validator_log(val, ISC_LOG_DEBUG(3), - "found a keyset, no null key"); - result = proveunsecure(val, ISC_TRUE); - if (result != DNS_R_WAIT) - validator_done(val, result); - else { - /* - * Don't free rdataset & sigrdataset, since - * they'll be freed in nullkeyvalidated. - */ - isc_event_free(&event); - UNLOCK(&val->lock); - return; - } - } else { - validator_log(val, ISC_LOG_DEBUG(3), - "found a keyset with a null key"); - if (rdataset->trust >= dns_trust_secure) { - validator_log(val, ISC_LOG_DEBUG(3), - "insecurity proof succeeded"); - val->event->rdataset->trust = dns_trust_answer; - validator_done(val, ISC_R_SUCCESS); - } else if (!dns_rdataset_isassociated(sigrdataset)) { - validator_log(val, ISC_LOG_DEBUG(3), - "insecurity proof failed"); - validator_done(val, DNS_R_NOTINSECURE); - } else { - dns_name_t *tname; - tname = dns_fixedname_name(&devent->foundname); - result = dns_validator_create(val->view, tname, - dns_rdatatype_key, - rdataset, - sigrdataset, NULL, - 0, val->task, - nullkeyvalidated, - val, - &val->keyvalidator); - if (result != ISC_R_SUCCESS) - validator_done(val, result); - /* - * Don't free rdataset & sigrdataset, since - * they'll be freed in nullkeyvalidated. - */ - isc_event_free(&event); - UNLOCK(&val->lock); - return; - } - } - } else if (eresult == DNS_R_NCACHENXDOMAIN || - eresult == DNS_R_NCACHENXRRSET || - eresult == DNS_R_NXDOMAIN || - eresult == DNS_R_NXRRSET) + validator_log(val, ISC_LOG_DEBUG(3), + "dsset with trust %d", rdataset->trust); + val->dsset = &val->frdataset; + result = validatezonekey(val, ISC_TRUE); + if (result != DNS_R_WAIT) + validator_done(val, result); + } else if (eresult == DNS_R_NXRRSET || + eresult == DNS_R_NCACHENXRRSET) { - /* - * No keys. - */ - validator_log(val, ISC_LOG_DEBUG(3), "no keys found"); - result = proveunsecure(val, ISC_TRUE); + validator_log(val, ISC_LOG_DEBUG(3), + "falling back to insecurity proof"); + val->attributes |= VALATTR_INSECURITY; + result = proveunsecure(val, ISC_FALSE); if (result != DNS_R_WAIT) validator_done(val, result); } else { validator_log(val, ISC_LOG_DEBUG(3), - "fetch_callback_nullkey: got %s", + "dsfetched: got %s", isc_result_totext(eresult)); if (eresult == ISC_R_CANCELED) validator_done(val, eresult); else - validator_done(val, DNS_R_NOVALIDKEY); + validator_done(val, DNS_R_NOVALIDDS); } + want_destroy = exit_check(val); UNLOCK(&val->lock); + if (want_destroy) + destroy(val); +} + +/* + * XXX there's too much duplicated code here. + */ +static void +dsfetched2(isc_task_t *task, isc_event_t *event) { + dns_fetchevent_t *devent; + dns_validator_t *val; + dns_rdataset_t *rdataset; + isc_boolean_t want_destroy; + isc_result_t result; + isc_result_t eresult; + + UNUSED(task); + INSIST(event->ev_type == DNS_EVENT_FETCHDONE); + devent = (dns_fetchevent_t *)event; + val = devent->ev_arg; + rdataset = &val->frdataset; + eresult = devent->result; - /* - * Free stuff from the event. - */ - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); isc_event_free(&event); + dns_resolver_destroyfetch(&val->fetch); + + INSIST(val->event != NULL); + + validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2"); + LOCK(&val->lock); + if (eresult == DNS_R_NXRRSET || eresult == DNS_R_NCACHENXRRSET) { + /* + * There is no DS. We're done. + */ + val->event->rdataset->trust = dns_trust_answer; + validator_done(val, ISC_R_SUCCESS); + } else if (eresult == ISC_R_SUCCESS || + eresult == DNS_R_NXDOMAIN || + eresult == DNS_R_NCACHENXDOMAIN) + { + /* + * Either there is a DS or this is not a zone cut. Continue. + */ + result = proveunsecure(val, ISC_TRUE); + if (result != DNS_R_WAIT) + validator_done(val, result); + } else { + if (eresult == ISC_R_CANCELED) + validator_done(val, eresult); + else + validator_done(val, DNS_R_NOVALIDDS); + } + want_destroy = exit_check(val); + UNLOCK(&val->lock); + if (want_destroy) + destroy(val); } static void keyvalidated(isc_task_t *task, isc_event_t *event) { dns_validatorevent_t *devent; dns_validator_t *val; + isc_boolean_t want_destroy; isc_result_t result; isc_result_t eresult; @@ -302,6 +318,7 @@ keyvalidated(isc_task_t *task, isc_event_t *event) { eresult = devent->result; isc_event_free(&event); + dns_validator_destroy(&val->subvalidator); INSIST(val->event != NULL); @@ -316,27 +333,61 @@ keyvalidated(isc_task_t *task, isc_event_t *event) { if (val->frdataset.trust >= dns_trust_secure) (void) get_dst_key(val, val->siginfo, &val->frdataset); result = validate(val, ISC_TRUE); - if (result != DNS_R_WAIT) { + if (result != DNS_R_WAIT) validator_done(val, result); - goto out; - } } else { validator_log(val, ISC_LOG_DEBUG(3), "keyvalidated: got %s", isc_result_totext(eresult)); validator_done(val, eresult); } - out: - + want_destroy = exit_check(val); UNLOCK(&val->lock); - dns_validator_destroy(&val->keyvalidator); - /* - * Free stuff from the event. - */ - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); + if (want_destroy) + destroy(val); +} + +static void +dsvalidated(isc_task_t *task, isc_event_t *event) { + dns_validatorevent_t *devent; + dns_validator_t *val; + isc_boolean_t want_destroy; + isc_result_t result; + isc_result_t eresult; + + UNUSED(task); + INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); + + devent = (dns_validatorevent_t *)event; + val = devent->ev_arg; + eresult = devent->result; + + isc_event_free(&event); + dns_validator_destroy(&val->subvalidator); + + INSIST(val->event != NULL); + + validator_log(val, ISC_LOG_DEBUG(3), "in dsvalidated"); + LOCK(&val->lock); + if (eresult == ISC_R_SUCCESS) { + validator_log(val, ISC_LOG_DEBUG(3), + "dsset with trust %d", val->frdataset.trust); + if ((val->attributes & VALATTR_INSECURITY) != 0) + result = proveunsecure(val, ISC_TRUE); + else + result = validatezonekey(val, ISC_TRUE); + if (result != DNS_R_WAIT) + validator_done(val, result); + } else { + validator_log(val, ISC_LOG_DEBUG(3), + "dsvalidated: got %s", + isc_result_totext(eresult)); + validator_done(val, eresult); + } + want_destroy = exit_check(val); + UNLOCK(&val->lock); + if (want_destroy) + destroy(val); } static isc_boolean_t @@ -452,6 +503,7 @@ authvalidated(isc_task_t *task, isc_event_t *event) { dns_validatorevent_t *devent; dns_validator_t *val; dns_rdataset_t *rdataset, *sigrdataset; + isc_boolean_t want_destroy; isc_result_t result; isc_result_t eresult; @@ -463,7 +515,7 @@ authvalidated(isc_task_t *task, isc_event_t *event) { sigrdataset = devent->sigrdataset; val = devent->ev_arg; eresult = devent->result; - dns_validator_destroy(&val->authvalidator); + dns_validator_destroy(&val->subvalidator); INSIST(val->event != NULL); @@ -473,9 +525,13 @@ authvalidated(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "authvalidated: got %s", isc_result_totext(eresult)); - result = nxtvalidate(val, ISC_TRUE); - if (result != DNS_R_WAIT) + if (result == ISC_R_CANCELED) validator_done(val, result); + else { + result = nxtvalidate(val, ISC_TRUE); + if (result != DNS_R_WAIT) + validator_done(val, result); + } } else { if (rdataset->type == dns_rdatatype_nxt && nxtprovesnonexistence(val, devent->name, rdataset, @@ -486,7 +542,10 @@ authvalidated(isc_task_t *task, isc_event_t *event) { if (result != DNS_R_WAIT) validator_done(val, result); } + want_destroy = exit_check(val); UNLOCK(&val->lock); + if (want_destroy) + destroy(val); /* * Free stuff from the event. @@ -498,6 +557,7 @@ static void negauthvalidated(isc_task_t *task, isc_event_t *event) { dns_validatorevent_t *devent; dns_validator_t *val; + isc_boolean_t want_destroy; isc_result_t eresult; UNUSED(task); @@ -507,7 +567,7 @@ negauthvalidated(isc_task_t *task, isc_event_t *event) { val = devent->ev_arg; eresult = devent->result; isc_event_free(&event); - dns_validator_destroy(&val->authvalidator); + dns_validator_destroy(&val->subvalidator); INSIST(val->event != NULL); @@ -525,95 +585,84 @@ negauthvalidated(isc_task_t *task, isc_event_t *event) { isc_result_totext(eresult)); validator_done(val, eresult); } + want_destroy = exit_check(val); UNLOCK(&val->lock); - - /* - * Free stuff from the event. - */ - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); + if (want_destroy) + destroy(val); } -static void -nullkeyvalidated(isc_task_t *task, isc_event_t *event) { - dns_validatorevent_t *devent; - dns_validator_t *val; - isc_result_t result; - isc_result_t eresult; - - UNUSED(task); - INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); - - devent = (dns_validatorevent_t *)event; - val = devent->ev_arg; - eresult = devent->result; - - dns_name_free(devent->name, val->view->mctx); - isc_mem_put(val->view->mctx, devent->name, sizeof(dns_name_t)); - dns_validator_destroy(&val->keyvalidator); - isc_event_free(&event); - - INSIST(val->event != NULL); - - validator_log(val, ISC_LOG_DEBUG(3), "in nullkeyvalidated"); - LOCK(&val->lock); - if (eresult == ISC_R_SUCCESS) { - validator_log(val, ISC_LOG_DEBUG(3), - "proved that name is in an unsecure domain"); - validator_log(val, ISC_LOG_DEBUG(3), "marking as answer"); - val->event->rdataset->trust = dns_trust_answer; - validator_done(val, ISC_R_SUCCESS); - } else { - result = proveunsecure(val, ISC_TRUE); - if (result != DNS_R_WAIT) - validator_done(val, result); - } - UNLOCK(&val->lock); - - /* - * Free stuff from the event. - */ +static inline isc_result_t +view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { if (dns_rdataset_isassociated(&val->frdataset)) dns_rdataset_disassociate(&val->frdataset); if (dns_rdataset_isassociated(&val->fsigrdataset)) dns_rdataset_disassociate(&val->fsigrdataset); + + if (val->view->zonetable == NULL) + return (ISC_R_CANCELED); + return (dns_view_simplefind(val->view, name, type, 0, + DNS_DBFIND_PENDINGOK, ISC_FALSE, + &val->frdataset, &val->fsigrdataset)); } -/* - * Try to find a null zone key among those in 'rdataset'. If found, build - * a dst_key_t for it and point val->key at it. - */ -static isc_boolean_t -containsnullkey(dns_validator_t *val, dns_rdataset_t *rdataset) { - isc_result_t result; - dst_key_t *key = NULL; - isc_buffer_t b; - dns_rdata_t rdata = DNS_RDATA_INIT; - isc_boolean_t found = ISC_FALSE; +static inline isc_boolean_t +check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { + dns_validator_t *parent; - result = dns_rdataset_first(rdataset); - if (result != ISC_R_SUCCESS) - return (ISC_FALSE); - while (result == ISC_R_SUCCESS && !found) { - dns_rdataset_current(rdataset, &rdata); - isc_buffer_init(&b, rdata.data, rdata.length); - isc_buffer_add(&b, rdata.length); - key = NULL; - /* - * The key name is unimportant, so we can avoid any name/text - * conversion. - */ - result = dst_key_fromdns(dns_rootname, rdata.rdclass, &b, - val->view->mctx, &key); - if (result != ISC_R_SUCCESS) - continue; - if (dst_key_isnullkey(key)) - found = ISC_TRUE; - dst_key_free(&key); - dns_rdata_reset(&rdata); - result = dns_rdataset_next(rdataset); + for (parent = val->parent; parent != NULL; parent = parent->parent) { + if (parent->event != NULL && + parent->event->type == type && + dns_name_equal(parent->event->name, name)) + { + validator_log(val, ISC_LOG_DEBUG(3), + "continuing validation would lead to " + "deadlock: aborting validation"); + return (ISC_TRUE); + } } - return (found); + return (ISC_FALSE); +} + +static inline isc_result_t +create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, + isc_taskaction_t callback, const char *caller) +{ + if (dns_rdataset_isassociated(&val->frdataset)) + dns_rdataset_disassociate(&val->frdataset); + if (dns_rdataset_isassociated(&val->fsigrdataset)) + dns_rdataset_disassociate(&val->fsigrdataset); + + if (check_deadlock(val, name, type)) + return (DNS_R_NOVALIDSIG); + + validator_logcreate(val, name, type, caller, "fetch"); + return (dns_resolver_createfetch(val->view->resolver, name, type, + NULL, NULL, NULL, 0, + val->event->ev_sender, + callback, val, + &val->frdataset, + &val->fsigrdataset, + &val->fetch)); +} + +static inline isc_result_t +create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, + isc_taskaction_t action, const char *caller) +{ + isc_result_t result; + + if (check_deadlock(val, name, type)) + return (DNS_R_NOVALIDSIG); + + validator_logcreate(val, name, type, caller, "validator"); + result = dns_validator_create(val->view, name, type, + rdataset, sigrdataset, NULL, 0, + val->task, action, val, + &val->subvalidator); + if (result == ISC_R_SUCCESS) + val->subvalidator->parent = val; + return (result); } /* @@ -703,47 +752,26 @@ get_key(dns_validator_t *val, dns_rdata_sig_t *siginfo) { namereln != dns_namereln_equal) return (DNS_R_CONTINUE); - /* - * Is the key used for the signature a security root? - */ - INSIST(val->keynode == NULL); - result = dns_keytable_findkeynode(val->keytable, - &siginfo->signer, - siginfo->algorithm, siginfo->keyid, - &val->keynode); - if (result == ISC_R_SUCCESS) { + if (namereln == dns_namereln_equal) { /* - * The key is a security root. + * If this is a self-signed keyset, it must not be a zone key + * (since get_key is not called from validatezonekey). */ - val->key = dns_keynode_key(val->keynode); - return (ISC_R_SUCCESS); - } + if (val->event->rdataset->type == dns_rdatatype_key) + return (DNS_R_CONTINUE); - /* - * A key set may not be self-signed unless the signing key is a - * security root. We don't want a KEY RR to authenticate - * itself, so we ignore the signature if it was not made by - * an ancestor of the KEY or a preconfigured key. - */ - if (val->event->rdataset->type == dns_rdatatype_key && - namereln == dns_namereln_equal) - { - validator_log(val, ISC_LOG_DEBUG(3), - "keyset was self-signed but not preconfigured"); - return (DNS_R_CONTINUE); + /* + * Records appearing in the parent zone at delegation + * points cannot be self-signed. + */ + if (dns_rdatatype_atparent(val->event->rdataset->type)) + return (DNS_R_CONTINUE); } /* * Do we know about this key? */ - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); - result = dns_view_simplefind(val->view, &siginfo->signer, - dns_rdatatype_key, 0, - DNS_DBFIND_PENDINGOK, ISC_FALSE, - &val->frdataset, &val->fsigrdataset); + result = view_find(val, &siginfo->signer, dns_rdatatype_key); if (result == ISC_R_SUCCESS) { /* * We have an rrset for the given keyname. @@ -755,17 +783,12 @@ get_key(dns_validator_t *val, dns_rdata_sig_t *siginfo) { /* * We know the key but haven't validated it yet. */ - result = dns_validator_create(val->view, - &siginfo->signer, - dns_rdatatype_key, - &val->frdataset, - &val->fsigrdataset, - NULL, - 0, - val->task, - keyvalidated, - val, - &val->keyvalidator); + result = create_validator(val, &siginfo->signer, + dns_rdatatype_key, + &val->frdataset, + &val->fsigrdataset, + keyvalidated, + "get_key"); if (result != ISC_R_SUCCESS) return (result); return (DNS_R_WAIT); @@ -803,17 +826,8 @@ get_key(dns_validator_t *val, dns_rdata_sig_t *siginfo) { /* * We don't know anything about this key. */ - val->fetch = NULL; - result = dns_resolver_createfetch(val->view->resolver, - &siginfo->signer, - dns_rdatatype_key, - NULL, NULL, NULL, 0, - val->event->ev_sender, - fetch_callback_validator, - val, - &val->frdataset, - &val->fsigrdataset, - &val->fetch); + result = create_fetch(val, &siginfo->signer, dns_rdatatype_key, + fetch_callback_validator, "get_key"); if (result != ISC_R_SUCCESS) return (result); return (DNS_R_WAIT); @@ -837,66 +851,71 @@ get_key(dns_validator_t *val, dns_rdata_sig_t *siginfo) { return (result); } +static dns_keytag_t +compute_keytag(dns_rdata_t *rdata, dns_rdata_key_t *key) { + isc_region_t r; + + dns_rdata_toregion(rdata, &r); + return (dst_region_computeid(&r, key->algorithm)); +} + /* - * If the rdataset being validated is a key set, is each key a security root? + * Is this keyset self-signed? */ static isc_boolean_t -issecurityroot(dns_validator_t *val) { - dns_name_t *name; - dns_rdataset_t *rdataset; - isc_mem_t *mctx; +isselfsigned(dns_validator_t *val) { + dns_rdataset_t *rdataset, *sigrdataset; dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_t sigrdata = DNS_RDATA_INIT; + dns_rdata_key_t key; + dns_rdata_sig_t sig; + dns_keytag_t keytag; isc_result_t result; - dns_keynode_t *keynode, *nextnode; - dst_key_t *key, *secrootkey; - isc_boolean_t match = ISC_FALSE; - name = val->event->name; rdataset = val->event->rdataset; - mctx = val->view->mctx; + sigrdataset = val->event->sigrdataset; + + INSIST(rdataset->type == dns_rdatatype_key); for (result = dns_rdataset_first(rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { - dns_rdataset_current(rdataset, &rdata); - key = NULL; - result = dns_dnssec_keyfromrdata(name, &rdata, mctx, &key); dns_rdata_reset(&rdata); - if (result != ISC_R_SUCCESS) - continue; - keynode = NULL; - result = dns_keytable_findkeynode( - val->keytable, name, - (dns_secalg_t)dst_key_alg(key), - dst_key_id(key), - &keynode); + dns_rdataset_current(rdataset, &rdata); + (void)dns_rdata_tostruct(&rdata, &key, NULL); + keytag = compute_keytag(&rdata, &key); + for (result = dns_rdataset_first(sigrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(sigrdataset)) + { + dns_rdata_reset(&sigrdata); + dns_rdataset_current(sigrdataset, &sigrdata); + (void)dns_rdata_tostruct(&sigrdata, &sig, NULL); - match = ISC_FALSE; - while (result == ISC_R_SUCCESS) { - secrootkey = dns_keynode_key(keynode); - if (dst_key_compare(key, secrootkey)) { - match = ISC_TRUE; - dns_keytable_detachkeynode(val->keytable, - &keynode); - break; - } - nextnode = NULL; - result = dns_keytable_findnextkeynode(val->keytable, - keynode, - &nextnode); - dns_keytable_detachkeynode(val->keytable, &keynode); + if (sig.algorithm == key.algorithm && + sig.keyid == keytag) + return (ISC_TRUE); } - - dst_key_free(&key); - if (!match) - return (ISC_FALSE); } - return (match); + return (ISC_FALSE); +} + +static isc_result_t +verify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata) { + isc_result_t result; + + val->attributes |= VALATTR_TRIEDVERIFY; + result = dns_dnssec_verify(val->event->name, val->event->rdataset, + key, ISC_FALSE, val->view->mctx, rdata); + validator_log(val, ISC_LOG_DEBUG(3), + "verify rdataset: %s", + isc_result_totext(result)); + return (result); } /* - * Attempts positive response validation. + * Attempts positive response validation of a normal RRset. * * Returns: * ISC_R_SUCCESS Validation completed successfully @@ -916,27 +935,6 @@ validate(dns_validator_t *val, isc_boolean_t resume) { event = val->event; - /* - * If this is a security root, it's ok. - */ - if (val->event->type == dns_rdatatype_key && !resume) { - dns_fixedname_t fsecroot; - dns_name_t *secroot; - - dns_fixedname_init(&fsecroot); - secroot = dns_fixedname_name(&fsecroot); - result = dns_keytable_finddeepestmatch(val->keytable, - val->event->name, - secroot); - if (result == ISC_R_SUCCESS && - dns_name_equal(val->event->name, secroot) && - issecurityroot(val)) - { - val->event->rdataset->trust = dns_trust_secure; - return (ISC_R_SUCCESS); - } - } - if (resume) { /* * We already have a sigrdataset. @@ -990,14 +988,7 @@ validate(dns_validator_t *val, isc_boolean_t resume) { } do { - val->attributes |= VALATTR_TRIEDVERIFY; - result = dns_dnssec_verify(event->name, - event->rdataset, - val->key, ISC_FALSE, - val->view->mctx, &rdata); - validator_log(val, ISC_LOG_DEBUG(3), - "verify rdataset: %s", - isc_result_totext(result)); + result = verify(val, val->key, &rdata); if (result == ISC_R_SUCCESS) break; if (val->keynode != NULL) { @@ -1071,6 +1062,271 @@ validate(dns_validator_t *val, isc_boolean_t resume) { return (DNS_R_NOVALIDSIG); } +/* + * Attempts positive response validation of an RRset containing zone keys. + * + * Returns: + * ISC_R_SUCCESS Validation completed successfully + * DNS_R_WAIT Validation has started but is waiting + * for an event. + * Other return codes are possible and all indicate failure. + */ +static isc_result_t +validatezonekey(dns_validator_t *val, isc_boolean_t resume) { + isc_result_t result; + dns_validatorevent_t *event; + dns_rdataset_t trdataset; + dns_rdata_t dsrdata = DNS_RDATA_INIT; + dns_rdata_t newdsrdata = DNS_RDATA_INIT; + dns_rdata_t keyrdata = DNS_RDATA_INIT; + dns_rdata_t sigrdata = DNS_RDATA_INIT; + unsigned char dsbuf[DNS_DS_BUFFERSIZE]; + dns_keytag_t keytag; + dns_rdata_ds_t ds; + dns_rdata_key_t key; + dns_rdata_sig_t sig; + dst_key_t *dstkey; + + UNUSED(resume); + + /* + * Caller must be holding the validator lock. + */ + + event = val->event; + + if (val->dsset == NULL) { + /* + * First, see if this key was signed by a trusted key. + */ + for (result = dns_rdataset_first(val->event->sigrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(val->event->sigrdataset)) + { + dns_keynode_t *keynode = NULL, *nextnode = NULL; + + dns_rdata_reset(&sigrdata); + dns_rdataset_current(val->event->sigrdataset, + &sigrdata); + (void)dns_rdata_tostruct(&sigrdata, &sig, NULL); + result = dns_keytable_findkeynode(val->keytable, + val->event->name, + sig.algorithm, + sig.keyid, + &keynode); + while (result == ISC_R_SUCCESS) { + dstkey = dns_keynode_key(keynode); + result = verify(val, dstkey, &sigrdata); + if (result == ISC_R_SUCCESS) { + dns_keytable_detachkeynode(val->keytable, + &keynode); + break; + } + result = dns_keytable_findnextkeynode( + val->keytable, + keynode, + &nextnode); + dns_keytable_detachkeynode(val->keytable, + &keynode); + keynode = nextnode; + } + if (result == ISC_R_SUCCESS) { + event->rdataset->trust = dns_trust_secure; + event->sigrdataset->trust = dns_trust_secure; + validator_log(val, ISC_LOG_DEBUG(3), + "signed by trusted key; " + "marking as secure"); + return (result); + } + } + + /* + * If this is the root name and there was no trusted key, + * give up, since there's no DS at the root. + */ + if (dns_name_equal(event->name, dns_rootname)) { + if ((val->attributes & VALATTR_TRIEDVERIFY) != 0) + return (DNS_R_NOVALIDSIG); + else + return (DNS_R_NOVALIDDS); + } + + /* + * Otherwise, try to find the DS record. + */ + result = view_find(val, val->event->name, dns_rdatatype_ds); + if (result == ISC_R_SUCCESS) { + /* + * We have DS records. + */ + val->dsset = &val->frdataset; + if (val->frdataset.trust == dns_trust_pending && + dns_rdataset_isassociated(&val->fsigrdataset)) + { + result = create_validator(val, + val->event->name, + dns_rdatatype_ds, + &val->frdataset, + &val->fsigrdataset, + dsvalidated, + "validatezonekey"); + if (result != ISC_R_SUCCESS) + return (result); + return (DNS_R_WAIT); + } else if (val->frdataset.trust == dns_trust_pending) { + /* + * There should never be an unsigned DS. + */ + dns_rdataset_disassociate(&val->frdataset); + validator_log(val, ISC_LOG_DEBUG(2), + "unsigned DS record"); + return (DNS_R_NOVALIDSIG); + } else + result = ISC_R_SUCCESS; + } else if (result == ISC_R_NOTFOUND) { + /* + * We don't have the DS. Find it. + */ + result = create_fetch(val, val->event->name, + dns_rdatatype_ds, dsfetched, + "validatezonekey"); + if (result != ISC_R_SUCCESS) + return (result); + return (DNS_R_WAIT); + } else if (result == DNS_R_NCACHENXDOMAIN || + result == DNS_R_NCACHENXRRSET || + result == DNS_R_NXDOMAIN || + result == DNS_R_NXRRSET) + { + /* + * The DS does not exist. + */ + if (dns_rdataset_isassociated(&val->frdataset)) + dns_rdataset_disassociate(&val->frdataset); + if (dns_rdataset_isassociated(&val->fsigrdataset)) + dns_rdataset_disassociate(&val->fsigrdataset); + validator_log(val, ISC_LOG_DEBUG(2), "no DS record"); + return (DNS_R_NOVALIDSIG); + } + } + + /* + * We have a DS set. + */ + INSIST(val->dsset != NULL); + + if (val->dsset->trust < dns_trust_secure) { + val->event->rdataset->trust = dns_trust_answer; + val->event->sigrdataset->trust = dns_trust_answer; + return (ISC_R_SUCCESS); + } + + /* + * Look through the DS record and find the keys that can sign the + * key set and the matching signature. For each such key, attempt + * verification. + */ + + for (result = dns_rdataset_first(val->dsset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(val->dsset)) + { + dns_rdata_reset(&dsrdata); + dns_rdataset_current(val->dsset, &dsrdata); + (void)dns_rdata_tostruct(&dsrdata, &ds, NULL); + + dns_rdataset_init(&trdataset); + dns_rdataset_clone(val->event->rdataset, &trdataset); + + for (result = dns_rdataset_first(&trdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(&trdataset)) + { + dns_rdata_reset(&keyrdata); + dns_rdataset_current(&trdataset, &keyrdata); + (void)dns_rdata_tostruct(&keyrdata, &key, NULL); + keytag = compute_keytag(&keyrdata, &key); + if (ds.key_tag != keytag || + ds.algorithm != key.algorithm) + continue; + dns_rdata_reset(&newdsrdata); + result = dns_ds_buildrdata(val->event->name, + &keyrdata, ds.digest_type, + dsbuf, &newdsrdata); + if (result != ISC_R_SUCCESS) + continue; + if (dns_rdata_compare(&dsrdata, &newdsrdata) == 0) + break; + } + if (result != ISC_R_SUCCESS) { + validator_log(val, ISC_LOG_DEBUG(3), + "no KEY matching DS"); + continue; + } + + for (result = dns_rdataset_first(val->event->sigrdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(val->event->sigrdataset)) + { + dns_rdata_reset(&sigrdata); + dns_rdataset_current(val->event->sigrdataset, + &sigrdata); + (void)dns_rdata_tostruct(&sigrdata, &sig, NULL); + if (ds.key_tag == sig.keyid && + ds.algorithm == sig.algorithm) + break; + } + if (result != ISC_R_SUCCESS) { + validator_log(val, ISC_LOG_DEBUG(3), + "no SIG matching DS key"); + continue; + } + + dstkey = NULL; + result = dns_dnssec_keyfromrdata(val->event->name, + &keyrdata, + val->view->mctx, + &dstkey); + if (result != ISC_R_SUCCESS) + /* + * This really shouldn't happen, but... + */ + continue; + + result = verify(val, dstkey, &sigrdata); + dst_key_free(&dstkey); + dns_rdataset_disassociate(&trdataset); + if (result == ISC_R_SUCCESS) + break; + } + if (result == ISC_R_SUCCESS) { + event->rdataset->trust = dns_trust_secure; + event->sigrdataset->trust = dns_trust_secure; + validator_log(val, ISC_LOG_DEBUG(3), "marking as secure"); + return (result); + } else + return (DNS_R_NOVALIDSIG); +} + +/* + * Starts a positive response validation. + * + * Returns: + * ISC_R_SUCCESS Validation completed successfully + * DNS_R_WAIT Validation has started but is waiting + * for an event. + * Other return codes are possible and all indicate failure. + */ +static isc_result_t +start_positive_validation(dns_validator_t *val) { + /* + * If this is not a key, go straight into validate(). + */ + if (val->event->type != dns_rdatatype_key || !isselfsigned(val)) + return (validate(val, ISC_FALSE)); + + return (validatezonekey(val, ISC_FALSE)); +} static isc_result_t nxtvalidate(dns_validator_t *val, isc_boolean_t resume) { @@ -1078,11 +1334,9 @@ nxtvalidate(dns_validator_t *val, isc_boolean_t resume) { dns_message_t *message = val->event->message; isc_result_t result; - if (!resume) { + if (!resume) result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); - if (result != ISC_R_SUCCESS) - validator_done(val, ISC_R_NOTFOUND); - } else { + else { result = ISC_R_SUCCESS; validator_log(val, ISC_LOG_DEBUG(3), "resuming nxtvalidate"); } @@ -1110,6 +1364,9 @@ nxtvalidate(dns_validator_t *val, isc_boolean_t resume) { if (rdataset->type == dns_rdatatype_sig) continue; + if (rdataset->type == dns_rdatatype_soa) + val->soaset = rdataset; + for (sigrdataset = ISC_LIST_HEAD(name->list); sigrdataset != NULL; sigrdataset = ISC_LIST_NEXT(sigrdataset, @@ -1148,17 +1405,11 @@ nxtvalidate(dns_validator_t *val, isc_boolean_t resume) { dns_rdatatype_soa)) continue; } - val->authvalidator = NULL; val->currentset = rdataset; - result = dns_validator_create(val->view, name, - rdataset->type, - rdataset, - sigrdataset, - NULL, 0, - val->task, - authvalidated, - val, - &val->authvalidator); + result = create_validator(val, name, rdataset->type, + rdataset, sigrdataset, + authvalidated, + "nxtvalidate"); if (result != ISC_R_SUCCESS) return (result); return (DNS_R_WAIT); @@ -1168,18 +1419,14 @@ nxtvalidate(dns_validator_t *val, isc_boolean_t resume) { if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; if (result != ISC_R_SUCCESS) - validator_done(val, result); + return (result); if ((val->attributes & VALATTR_FOUNDNONEXISTENCE) == 0) { - if (!val->seensig) { - result = dns_validator_create(val->view, name, - dns_rdatatype_soa, - &val->frdataset, - NULL, NULL, 0, - val->task, - negauthvalidated, - val, - &val->authvalidator); + if (!val->seensig && val->soaset != NULL) { + result = create_validator(val, name, dns_rdatatype_soa, + val->soaset, NULL, + negauthvalidated, + "nxtvalidate"); if (result != ISC_R_SUCCESS) return (result); return (DNS_R_WAIT); @@ -1197,11 +1444,10 @@ nxtvalidate(dns_validator_t *val, isc_boolean_t resume) { static isc_result_t proveunsecure(dns_validator_t *val, isc_boolean_t resume) { isc_result_t result; - dns_fixedname_t secroot, tfname; + dns_fixedname_t secroot; dns_name_t *tname; dns_fixedname_init(&secroot); - dns_fixedname_init(&tfname); result = dns_keytable_finddeepestmatch(val->keytable, val->event->name, dns_fixedname_name(&secroot)); @@ -1214,17 +1460,6 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { else if (result != ISC_R_SUCCESS) return (result); - /* - * If this is a security root, it's ok. - */ - if (val->event->type == dns_rdatatype_key && - dns_name_equal(val->event->name, dns_fixedname_name(&secroot)) && - issecurityroot(val)) - { - val->event->rdataset->trust = dns_trust_secure; - return (ISC_R_SUCCESS); - } - if (!resume) val->labels = dns_name_depth(dns_fixedname_name(&secroot)) + 1; else { @@ -1239,11 +1474,10 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { char namebuf[DNS_NAME_FORMATSIZE]; if (val->labels == dns_name_depth(val->event->name)) { - if (val->event->type == dns_rdatatype_key) - break; tname = val->event->name; } else { - tname = dns_fixedname_name(&tfname); + dns_fixedname_init(&val->fname); + tname = dns_fixedname_name(&val->fname); result = dns_name_splitatdepth(val->event->name, val->labels, NULL, tname); @@ -1253,91 +1487,84 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { dns_name_format(tname, namebuf, sizeof(namebuf)); validator_log(val, ISC_LOG_DEBUG(3), - "looking for null keyset at '%s'", + "checking existence of DS at '%s'", namebuf); - if (dns_rdataset_isassociated(&val->frdataset)) - dns_rdataset_disassociate(&val->frdataset); - if (dns_rdataset_isassociated(&val->fsigrdataset)) - dns_rdataset_disassociate(&val->fsigrdataset); - - result = dns_view_simplefind(val->view, tname, - dns_rdatatype_key, 0, - DNS_DBFIND_PENDINGOK, ISC_FALSE, - &val->frdataset, - &val->fsigrdataset); - if (result == ISC_R_SUCCESS) { - dns_name_t *fname = NULL; - - if (!dns_rdataset_isassociated(&val->fsigrdataset)) { - result = DNS_R_NOTINSECURE; + result = view_find(val, tname, dns_rdatatype_ds); + if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) { + /* + * There is no DS. We're hopefully done. + */ + if (val->frdataset.trust < dns_trust_secure) { + /* + * This shouldn't happen, since the negative + * response should have been validated. Since + * there's no way of validating existing + * negative response blobs, give up. + */ + result = DNS_R_NOVALIDSIG; goto out; } - validator_log(val, ISC_LOG_DEBUG(3), - "found keyset, looking for null key"); - if (!containsnullkey(val, &val->frdataset)) + val->event->rdataset->trust = dns_trust_answer; + return (ISC_R_SUCCESS); + } else if (result == ISC_R_SUCCESS) { + /* + * There is a DS here. Verify that it's secure and + * continue. + */ + if (val->frdataset.trust >= dns_trust_secure) continue; - - if (val->frdataset.trust >= dns_trust_secure) { - validator_log(val, ISC_LOG_DEBUG(3), - "insecurity proof succeeded"); - val->event->rdataset->trust = dns_trust_answer; - result = ISC_R_SUCCESS; + else if (!dns_rdataset_isassociated(&val->fsigrdataset)) + { + result = DNS_R_NOVALIDSIG; goto out; } - - fname = isc_mem_get(val->view->mctx, sizeof(*fname)); - if (fname == NULL) - return (ISC_R_NOMEMORY); - dns_name_init(fname, NULL); - result = dns_name_dup(tname, val->view->mctx, fname); - if (result != ISC_R_SUCCESS) { - isc_mem_put(val->view->mctx, fname, - sizeof(*fname)); - result = ISC_R_NOMEMORY; - goto out; - } - - result = dns_validator_create(val->view, - fname, - dns_rdatatype_key, - &val->frdataset, - &val->fsigrdataset, - NULL, - 0, - val->task, - nullkeyvalidated, - val, - &val->keyvalidator); + result = create_validator(val, tname, dns_rdatatype_ds, + &val->frdataset, + &val->fsigrdataset, + dsvalidated, + "proveunsecure"); if (result != ISC_R_SUCCESS) goto out; return (DNS_R_WAIT); - } else if (result == ISC_R_NOTFOUND) { - val->fetch = NULL; - result = dns_resolver_createfetch(val->view->resolver, - tname, - dns_rdatatype_key, - NULL, NULL, NULL, 0, - val->event->ev_sender, - fetch_callback_nullkey, - val, - &val->frdataset, - &val->fsigrdataset, - &val->fetch); - if (result != ISC_R_SUCCESS) - goto out; - return (DNS_R_WAIT); - } else if (result == DNS_R_NCACHENXDOMAIN || - result == DNS_R_NCACHENXRRSET || - result == DNS_R_NXDOMAIN || - result == DNS_R_NXRRSET) + } else if (result == DNS_R_NXDOMAIN || + result == DNS_R_NCACHENXDOMAIN) { + /* + * This is not a zone cut. Assuming things are + * as expected, continue. + */ + if (!dns_rdataset_isassociated(&val->frdataset)) { + /* + * There should be an NXT here, since we + * are still in a secure zone. + */ + result = DNS_R_NOVALIDNXT; + goto out; + } else if (val->frdataset.trust < dns_trust_secure) { + /* + * This shouldn't happen, since the negative + * response should have been validated. Since + * there's no way of validating existing + * negative response blobs, give up. + */ + result = DNS_R_NOVALIDSIG; + goto out; + } continue; - } else - goto out; + } else if (result == ISC_R_NOTFOUND) { + /* + * We don't know anything about the DS. Find it. + */ + result = create_fetch(val, tname, dns_rdatatype_ds, + dsfetched2, "proveunsecure"); + if (result != ISC_R_SUCCESS) + goto out; + return (DNS_R_WAIT); + } } validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed"); - return (DNS_R_NOTINSECURE); /* Didn't find a null key */ + return (DNS_R_NOTINSECURE); /* Couldn't complete insecurity proof */ out: if (dns_rdataset_isassociated(&val->frdataset)) @@ -1351,6 +1578,7 @@ static void validator_start(isc_task_t *task, isc_event_t *event) { dns_validator_t *val; dns_validatorevent_t *vevent; + isc_boolean_t want_destroy = ISC_FALSE; isc_result_t result = ISC_R_FAILURE; UNUSED(task); @@ -1376,13 +1604,16 @@ validator_start(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "attempting positive response validation"); - result = validate(val, ISC_FALSE); + INSIST(dns_rdataset_isassociated(val->event->rdataset)); + INSIST(dns_rdataset_isassociated(val->event->sigrdataset)); + result = start_positive_validation(val); if (result == DNS_R_NOVALIDSIG && (val->attributes & VALATTR_TRIEDVERIFY) == 0) { saved_result = result; validator_log(val, ISC_LOG_DEBUG(3), "falling back to insecurity proof"); + val->attributes |= VALATTR_INSECURITY; result = proveunsecure(val, ISC_FALSE); if (result == DNS_R_NOTINSECURE) result = saved_result; @@ -1392,9 +1623,11 @@ validator_start(isc_task_t *task, isc_event_t *event) { * This is either an unsecure subdomain or a response from * a broken server. */ + INSIST(dns_rdataset_isassociated(val->event->rdataset)); validator_log(val, ISC_LOG_DEBUG(3), "attempting insecurity proof"); + val->attributes |= VALATTR_INSECURITY; result = proveunsecure(val, ISC_FALSE); } else if (val->event->rdataset == NULL && val->event->sigrdataset == NULL) @@ -1405,6 +1638,7 @@ validator_start(isc_task_t *task, isc_event_t *event) { validator_log(val, ISC_LOG_DEBUG(3), "attempting negative response validation"); + val->attributes |= VALATTR_NEGATIVE; result = nxtvalidate(val, ISC_FALSE); } else { /* @@ -1413,10 +1647,14 @@ validator_start(isc_task_t *task, isc_event_t *event) { INSIST(0); } - if (result != DNS_R_WAIT) + if (result != DNS_R_WAIT) { + want_destroy = exit_check(val); validator_done(val, result); + } UNLOCK(&val->lock); + if (want_destroy) + destroy(val); } isc_result_t @@ -1470,8 +1708,8 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, val->options = options; val->attributes = 0; val->fetch = NULL; - val->keyvalidator = NULL; - val->authvalidator = NULL; + val->subvalidator = NULL; + val->parent = NULL; val->keytable = NULL; dns_keytable_attach(val->view->secroots, &val->keytable); val->keynode = NULL; @@ -1483,6 +1721,8 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, val->labels = 0; val->currentset = NULL; val->keyset = NULL; + val->dsset = NULL; + val->soaset = NULL; val->seensig = ISC_FALSE; dns_rdataset_init(&val->frdataset); dns_rdataset_init(&val->fsigrdataset); @@ -1518,11 +1758,8 @@ dns_validator_cancel(dns_validator_t *validator) { if (validator->fetch != NULL) dns_resolver_cancelfetch(validator->fetch); - if (validator->keyvalidator != NULL) - dns_validator_cancel(validator->keyvalidator); - - if (validator->authvalidator != NULL) - dns_validator_cancel(validator->authvalidator); + if (validator->subvalidator != NULL) + dns_validator_cancel(validator->subvalidator); } UNLOCK(&validator->lock); } @@ -1541,10 +1778,8 @@ destroy(dns_validator_t *val) { dst_key_free(&val->key); if (val->keytable != NULL) dns_keytable_detach(&val->keytable); - if (val->keyvalidator != NULL) - dns_validator_destroy(&val->keyvalidator); - if (val->authvalidator != NULL) - dns_validator_destroy(&val->authvalidator); + if (val->subvalidator != NULL) + dns_validator_destroy(&val->subvalidator); mctx = val->view->mctx; if (val->siginfo != NULL) isc_mem_put(mctx, val->siginfo, sizeof(*val->siginfo)); @@ -1565,14 +1800,10 @@ dns_validator_destroy(dns_validator_t **validatorp) { LOCK(&val->lock); - REQUIRE(val->event == NULL); - + val->attributes |= VALATTR_SHUTDOWN; validator_log(val, ISC_LOG_DEBUG(3), "dns_validator_destroy"); - val->attributes |= VALATTR_SHUTDOWN; - if (val->fetch == NULL && val->keyvalidator == NULL && - val->authvalidator == NULL) - want_destroy = ISC_TRUE; + want_destroy = exit_check(val); UNLOCK(&val->lock); @@ -1619,3 +1850,17 @@ validator_log(dns_validator_t *val, int level, const char *fmt, ...) { DNS_LOGMODULE_VALIDATOR, level, fmt, ap); va_end(ap); } + +static void +validator_logcreate(dns_validator_t *val, + dns_name_t *name, dns_rdatatype_t type, + const char *caller, const char *operation) +{ + char namestr[DNS_NAME_FORMATSIZE]; + char typestr[DNS_RDATATYPE_FORMATSIZE]; + + dns_name_format(name, namestr, sizeof(namestr)); + dns_rdatatype_format(type, typestr, sizeof(typestr)); + validator_log(val, ISC_LOG_DEBUG(9), "%s: creating %s for %s %s", + caller, operation, namestr, typestr); +} diff --git a/version b/version index c1bf10f922..e8ea5b6aa3 100644 --- a/version +++ b/version @@ -1,4 +1,4 @@ -# $Id: version,v 1.27 2001/08/30 05:02:20 marka Exp $ +# $Id: version,v 1.28 2002/06/17 04:00:59 marka Exp $ # # This file must follow /bin/sh rules. It is imported directly via # configure.