From 6fac7ff1f9ec9c3873d3b55c5079fa79aba1f146 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 14 May 2004 04:45:58 +0000 Subject: [PATCH] 1606. [bug] DVL insecurity proof was failing. 1605. [func] New dns_db_find() option DNS_DBFIND_COVERINGNSEC. --- CHANGES | 5 +- lib/dns/include/dns/db.h | 12 +- lib/dns/include/dns/result.h | 5 +- lib/dns/include/dns/validator.h | 4 +- lib/dns/rbtdb.c | 153 ++++++++++++++---- lib/dns/resolver.c | 51 ++++-- lib/dns/result.c | 5 +- lib/dns/validator.c | 264 +++++++++++++++++++++++++++++++- util/copyrights | 19 +++ 9 files changed, 462 insertions(+), 56 deletions(-) diff --git a/CHANGES b/CHANGES index 148fcace27..34b2b9ead9 100644 --- a/CHANGES +++ b/CHANGES @@ -87,9 +87,10 @@ 1607. [bug] dig, host and nslookup were still using random() to generate query ids. [RT# 11013] -1606. [placeholder] rt10440a +1606. [bug] DVL insecurity proof was failing. + +1605. [func] New dns_db_find() option DNS_DBFIND_COVERINGNSEC. -1605. [placeholder] rt10440a 1604. [bug] A xfrout_ctx_create() failure would result in xfrout_ctx_destroy() being called with a diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index b62541f60f..b4c7261da5 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.h,v 1.76 2004/03/05 05:09:41 marka Exp $ */ +/* $Id: db.h,v 1.77 2004/05/14 04:45:57 marka Exp $ */ #ifndef DNS_DB_H #define DNS_DB_H 1 @@ -188,6 +188,7 @@ struct dns_db { #define DNS_DBFIND_PENDINGOK 0x08 #define DNS_DBFIND_NOEXACT 0x10 #define DNS_DBFIND_FORCENSEC 0x20 +#define DNS_DBFIND_COVERINGNSEC 0x40 /* * Options that can be specified for dns_db_addrdataset(). @@ -647,6 +648,12 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * is only necessary when querying a database that was not secure * when created. * + * If the DNS_DBFIND_COVERINGNSEC option is set, then look for a + * NSEC record that potentially covers 'name' if a answer cannot + * be found. Note the returned NSEC needs to be checked to ensure + * that it is correct. This only affects answers returned from the + * cache. + * * To respond to a query for SIG records, the caller should create a * rdataset iterator and extract the signatures from each rdataset. * @@ -770,6 +777,9 @@ dns_db_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * DNS_R_EMPTYNAME The name exists but there is * no data at the name. * + * DNS_R_COVERINGNSEC The returned data is a NSEC + * that potentially covers 'name'. + * * Error results: * * ISC_R_NOMEMORY diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index a41fbfa80f..c134a9f398 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.h,v 1.105 2004/04/15 23:40:26 marka Exp $ */ +/* $Id: result.h,v 1.106 2004/05/14 04:45:58 marka Exp $ */ #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 @@ -142,8 +142,9 @@ #define DNS_R_DYNAMIC (ISC_RESULTCLASS_DNS + 98) #define DNS_R_UNKNOWNCOMMAND (ISC_RESULTCLASS_DNS + 99) #define DNS_R_MUSTBESECURE (ISC_RESULTCLASS_DNS + 100) +#define DNS_R_COVERINGNSEC (ISC_RESULTCLASS_DNS + 101) -#define DNS_R_NRESULTS 101 /* Number of results */ +#define DNS_R_NRESULTS 102 /* Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index 59d2aefe33..21be023d71 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: validator.h,v 1.28 2004/04/15 23:40:26 marka Exp $ */ +/* $Id: validator.h,v 1.29 2004/05/14 04:45:58 marka Exp $ */ #ifndef DNS_VALIDATOR_H #define DNS_VALIDATOR_H 1 @@ -121,6 +121,8 @@ struct dns_validator { dns_fixedname_t wild; ISC_LINK(dns_validator_t) link; dns_rdataset_t * dlv; + dns_fixedname_t dlvsep; + isc_boolean_t havedlvsep; isc_boolean_t mustbesecure; }; diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 0c85c8238c..48440fdf06 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.198 2004/05/14 01:05:53 marka Exp $ */ +/* $Id: rbtdb.c,v 1.199 2004/05/14 04:45:56 marka Exp $ */ /* * Principal Author: Bob Halley @@ -842,8 +842,7 @@ clean_zone_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, /* * If this is a NONEXISTENT rdataset, we can delete it. */ - if ((current->attributes & RDATASET_ATTR_NONEXISTENT) - != 0) { + if (NONEXISTENT(current)) { if (top_prev != NULL) top_prev->next = current->next; else @@ -1931,8 +1930,7 @@ find_closest_nsec(rbtdb_search_t *search, dns_dbnode_t **nodep, * Is this a "this rdataset doesn't * exist" record? */ - if ((header->attributes & - RDATASET_ATTR_NONEXISTENT) != 0) + if (NONEXISTENT(header)) header = NULL; break; } else @@ -2204,8 +2202,7 @@ zone_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, * Is this a "this rdataset doesn't * exist" record? */ - if ((header->attributes & - RDATASET_ATTR_NONEXISTENT) != 0) + if (NONEXISTENT(header)) header = NULL; break; } else @@ -2658,8 +2655,7 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, node->dirty = 1; header_prev = header; } - } else if ((header->attributes & - RDATASET_ATTR_NONEXISTENT) == 0) { + } else if (EXISTS(header)) { /* * We've found an extant rdataset. See if * we're interested in it. @@ -2735,6 +2731,104 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node, return (result); } +static isc_result_t +find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep, + isc_stdtime_t now, dns_name_t *foundname, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + dns_rbtnode_t *node; + rdatasetheader_t *header, *header_next, *header_prev; + rdatasetheader_t *found, *foundsig; + isc_boolean_t empty_node; + isc_result_t result; + dns_fixedname_t fname, forigin; + dns_name_t *name, *origin; + rbtdb_rdatatype_t matchtype, sigmatchtype, nsectype; + + matchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_nsec, 0); + nsectype = RBTDB_RDATATYPE_VALUE(0, dns_rdatatype_nsec); + sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, + dns_rdatatype_nsec); + + do { + node = NULL; + dns_fixedname_init(&fname); + name = dns_fixedname_name(&fname); + dns_fixedname_init(&forigin); + origin = dns_fixedname_name(&forigin); + result = dns_rbtnodechain_current(&search->chain, name, + origin, &node); + if (result != ISC_R_SUCCESS) + return (result); + LOCK(&(search->rbtdb->node_locks[node->locknum].lock)); + found = NULL; + foundsig = NULL; + empty_node = ISC_TRUE; + header_prev = NULL; + for (header = node->data; + header != NULL; + header = header_next) { + header_next = header->next; + if (header->ttl <= now) { + /* + * This rdataset is stale. If no one else is + * using the node, we can clean it up right + * now, otherwise we mark it as stale, and the + * node as dirty, so it will get cleaned up + * later. + */ + if (node->references == 0) { + INSIST(header->down == NULL); + if (header_prev != NULL) + header_prev->next = + header->next; + else + node->data = header->next; + free_rdataset(search->rbtdb->common.mctx, + header); + } else { + header->attributes |= + RDATASET_ATTR_STALE; + node->dirty = 1; + header_prev = header; + } + continue; + } + if (NONEXISTENT(header) || NXDOMAIN(header)) { + header_prev = header; + continue; + } + empty_node = ISC_FALSE; + if (header->type == matchtype) + found = header; + else if (header->type == sigmatchtype) + foundsig = header; + header_prev = header; + } + if (found != NULL) { + result = dns_name_concatenate(name, origin, + foundname, NULL); + if (result != ISC_R_SUCCESS) + goto unlock_node; + bind_rdataset(search->rbtdb, node, found, + now, rdataset); + if (foundsig != NULL) + bind_rdataset(search->rbtdb, node, foundsig, + now, sigrdataset); + new_reference(search->rbtdb, node); + *nodep = node; + result = DNS_R_COVERINGNSEC; + } else if (!empty_node) { + result = ISC_R_NOTFOUND; + }else + result = dns_rbtnodechain_prev(&search->chain, NULL, + NULL); + unlock_node: + UNLOCK(&(search->rbtdb->node_locks[node->locknum].lock)); + } while (empty_node && result == ISC_R_SUCCESS); + return (result); +} + static isc_result_t cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, dns_rdatatype_t type, unsigned int options, isc_stdtime_t now, @@ -2750,7 +2844,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, rdatasetheader_t *header, *header_prev, *header_next; rdatasetheader_t *found, *nsheader; rdatasetheader_t *foundsig, *nssig, *cnamesig; - rbtdb_rdatatype_t sigtype, nsecype; + rbtdb_rdatatype_t sigtype, nsectype; UNUSED(version); @@ -2785,6 +2879,13 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, cache_zonecut_callback, &search); if (result == DNS_R_PARTIALMATCH) { + if ((search.options & DNS_DBFIND_COVERINGNSEC) != 0) { + result = find_coveringnsec(&search, nodep, now, + foundname, rdataset, + sigrdataset); + if (result == DNS_R_COVERINGNSEC) + goto tree_exit; + } if (search.zonecut != NULL) { result = setup_delegation(&search, nodep, foundname, rdataset, sigrdataset); @@ -2818,7 +2919,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, found = NULL; foundsig = NULL; sigtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); - nsecype = RBTDB_RDATATYPE_VALUE(0, type); + nsectype = RBTDB_RDATATYPE_VALUE(0, type); nsheader = NULL; nssig = NULL; cnamesig = NULL; @@ -2846,8 +2947,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, node->dirty = 1; header_prev = header; } - } else if ((header->attributes & RDATASET_ATTR_NONEXISTENT) - == 0) { + } else if (EXISTS(header)) { /* * We now know that there is at least one active * non-stale rdataset at this node. @@ -2889,7 +2989,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version, */ foundsig = header; } else if (header->type == RBTDB_RDATATYPE_NCACHEANY || - header->type == nsecype) { + header->type == nsectype) { /* * We've found a negative cache entry. */ @@ -3114,8 +3214,7 @@ cache_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options, node->dirty = 1; header_prev = header; } - } else if ((header->attributes & RDATASET_ATTR_NONEXISTENT) - == 0) { + } else if (EXISTS(header)) { /* * If we found a type we were looking for, remember * it. @@ -3449,8 +3548,7 @@ zone_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, * Is this a "this rdataset doesn't * exist" record? */ - if ((header->attributes & - RDATASET_ATTR_NONEXISTENT) != 0) + if (NONEXISTENT(header)) header = NULL; break; } else @@ -3500,7 +3598,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; dns_rbtnode_t *rbtnode = (dns_rbtnode_t *)node; rdatasetheader_t *header, *header_next, *found, *foundsig; - rbtdb_rdatatype_t matchtype, sigmatchtype, nsecype; + rbtdb_rdatatype_t matchtype, sigmatchtype, nsectype; isc_result_t result; REQUIRE(VALID_RBTDB(rbtdb)); @@ -3518,7 +3616,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, found = NULL; foundsig = NULL; matchtype = RBTDB_RDATATYPE_VALUE(type, covers); - nsecype = RBTDB_RDATATYPE_VALUE(0, type); + nsectype = RBTDB_RDATATYPE_VALUE(0, type); if (covers == 0) sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, type); else @@ -3535,12 +3633,11 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, */ header->attributes |= RDATASET_ATTR_STALE; rbtnode->dirty = 1; - } else if ((header->attributes & RDATASET_ATTR_NONEXISTENT) == - 0) { + } else if (EXISTS(header)) { if (header->type == matchtype) found = header; else if (header->type == RBTDB_RDATATYPE_NCACHEANY || - header->type == nsecype) + header->type == nsectype) found = header; else if (header->type == sigmatchtype) foundsig = header; @@ -3720,7 +3817,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, isc_boolean_t header_nx; isc_boolean_t newheader_nx; isc_boolean_t merge; - dns_rdatatype_t nsecype, rdtype, covers; + dns_rdatatype_t nsectype, rdtype, covers; dns_trust_t trust; /* @@ -3758,7 +3855,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, newheader_nx = NONEXISTENT(newheader) ? ISC_TRUE : ISC_FALSE; topheader_prev = NULL; - nsecype = 0; + nsectype = 0; if (rbtversion == NULL && !newheader_nx) { rdtype = RBTDB_RDATATYPE_BASE(newheader->type); if (rdtype == 0) { @@ -3785,7 +3882,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, rbtnode->dirty = 1; goto find_header; } - nsecype = RBTDB_RDATATYPE_VALUE(covers, 0); + nsectype = RBTDB_RDATATYPE_VALUE(covers, 0); } else { /* * We're adding something that isn't a @@ -3825,7 +3922,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, topheader = NULL; goto find_header; } - nsecype = RBTDB_RDATATYPE_VALUE(0, rdtype); + nsectype = RBTDB_RDATATYPE_VALUE(0, rdtype); } } @@ -3833,7 +3930,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion, topheader != NULL; topheader = topheader->next) { if (topheader->type == newheader->type || - topheader->type == nsecype) + topheader->type == nsectype) break; topheader_prev = topheader; } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index b7eb0297a6..7f89953253 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.287 2004/04/19 23:16:20 marka Exp $ */ +/* $Id: resolver.c,v 1.288 2004/05/14 04:45:56 marka Exp $ */ #include @@ -65,25 +65,28 @@ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ - "fctx %p: %s", fctx, (m)) + "fctx %p(%s'): %s", fctx, fctx->info, (m)) #define FCTXTRACE2(m1, m2) \ isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ - "fctx %p: %s %s", fctx, (m1), (m2)) + "fctx %p(%s): %s %s", \ + fctx, fctx->info, (m1), (m2)) #define FTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ - "fetch %p (fctx %p): %s", \ - fetch, fetch->private, (m)) + "fetch %p (fctx %p(%s)): %s", \ + fetch, fetch->private, \ + fetch->private->info, (m)) #define QTRACE(m) isc_log_write(dns_lctx, \ DNS_LOGCATEGORY_RESOLVER, \ DNS_LOGMODULE_RESOLVER, \ ISC_LOG_DEBUG(3), \ - "resquery %p (fctx %p): %s", \ - query, query->fctx, (m)) + "resquery %p (fctx %p(%s)): %s", \ + query, query->fctx, \ + query->fctx->info, (m)) #else #define RTRACE(m) #define RRTRACE(r, m) @@ -152,6 +155,7 @@ struct fetchctx { dns_rdatatype_t type; unsigned int options; unsigned int bucketnum; + char * info; /* Locked by appropriate bucket lock. */ fetchstate state; isc_boolean_t want_shutdown; @@ -1121,6 +1125,8 @@ resquery_send(resquery_t *query) { &secure_domain); if (result != ISC_R_SUCCESS) secure_domain = ISC_FALSE; + if (res->view->dlv != NULL) + secure_domain = ISC_TRUE; if (secure_domain) fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; } else @@ -2287,6 +2293,7 @@ fctx_destroy(fetchctx_t *fctx) { dns_name_free(&fctx->name, res->mctx); dns_db_detach(&fctx->cache); dns_adb_detach(&fctx->adb); + isc_mem_free(res->mctx, fctx->info); isc_mem_put(res->mctx, fctx, sizeof(*fctx)); LOCK(&res->nlock); @@ -2575,6 +2582,8 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, isc_interval_t interval; dns_fixedname_t qdomain; unsigned int findoptions = 0; + char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; /* * Caller must be holding the lock for bucket number 'bucketnum'. @@ -2584,11 +2593,18 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, fctx = isc_mem_get(res->mctx, sizeof(*fctx)); if (fctx == NULL) return (ISC_R_NOMEMORY); + dns_name_format(name, buf, sizeof(buf)); + dns_rdatatype_format(type, typebuf, sizeof(typebuf)); + strcat(buf, "/"); /* checked */ + strcat(buf, typebuf); /* checked */ + fctx->info = isc_mem_strdup(res->mctx, buf); + if (fctx->info == NULL) + goto cleanup_fetch; FCTXTRACE("create"); dns_name_init(&fctx->name, NULL); result = dns_name_dup(name, res->mctx, &fctx->name); if (result != ISC_R_SUCCESS) - goto cleanup_fetch; + goto cleanup_info; dns_name_init(&fctx->domain, NULL); dns_rdataset_init(&fctx->nameservers); @@ -2761,6 +2777,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, cleanup_name: dns_name_free(&fctx->name, res->mctx); + cleanup_info: + isc_mem_free(res->mctx, fctx->info); + cleanup_fetch: isc_mem_put(res->mctx, fctx, sizeof(*fctx)); @@ -3091,7 +3110,6 @@ validated(isc_task_t *task, isc_event_t *event) { ardataset, &eresult); if (result != ISC_R_SUCCESS) goto noanswer_response; - goto answer_response; } @@ -3152,8 +3170,9 @@ validated(isc_task_t *task, isc_event_t *event) { goto cleanup_event; } + answer_response: /* - * Cache any NS records that happened to be validate. + * Cache any NS/NSEC records that happened to be validated. */ result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY); while (result == ISC_R_SUCCESS) { @@ -3163,14 +3182,15 @@ validated(isc_task_t *task, isc_event_t *event) { for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; rdataset = ISC_LIST_NEXT(rdataset, link)) { - if (rdataset->type != dns_rdatatype_ns || + if ((rdataset->type != dns_rdatatype_ns && + rdataset->type != dns_rdatatype_nsec) || rdataset->trust != dns_trust_secure) continue; for (sigrdataset = ISC_LIST_HEAD(name->list); sigrdataset != NULL; sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) { if (sigrdataset->type != dns_rdatatype_rrsig || - sigrdataset->covers != dns_rdatatype_ns) + sigrdataset->covers != rdataset->type) continue; break; } @@ -3197,7 +3217,6 @@ validated(isc_task_t *task, isc_event_t *event) { result = ISC_R_SUCCESS; - answer_response: /* * Respond with an answer, positive or negative, * as opposed to an error. 'node' must be non-NULL. @@ -3262,6 +3281,9 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, isc_stdtime_t now) { if (result != ISC_R_SUCCESS) return (result); + if (res->view->dlv != NULL) + secure_domain = ISC_TRUE; + if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) need_validation = ISC_FALSE; else @@ -3686,6 +3708,9 @@ ncache_message(fetchctx_t *fctx, dns_rdatatype_t covers, isc_stdtime_t now) { if (result != ISC_R_SUCCESS) return (result); + if (res->view->dlv != NULL) + secure_domain = ISC_TRUE; + if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) need_validation = ISC_FALSE; else diff --git a/lib/dns/result.c b/lib/dns/result.c index 982cc4b1f5..d5ba87ce12 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.c,v 1.116 2004/04/15 23:40:25 marka Exp $ */ +/* $Id: result.c,v 1.117 2004/05/14 04:45:57 marka Exp $ */ #include @@ -150,7 +150,8 @@ static const char *text[DNS_R_NRESULTS] = { "dynamic zone", /* 98 DNS_R_DYNAMIC */ "unknown command", /* 99 DNS_R_UNKNOWNCOMMAND */ - "must-be-secure" /* 100 DNS_R_MUSTBESECURE */ + "must-be-secure", /* 100 DNS_R_MUSTBESECURE */ + "covering NSEC record returned" /* 101 DNS_R_COVERINGNSEC */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/validator.c b/lib/dns/validator.c index ddaf8c00c9..078f9dbd35 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: validator.c,v 1.121 2004/04/15 23:40:25 marka Exp $ */ +/* $Id: validator.c,v 1.122 2004/05/14 04:45:57 marka Exp $ */ #include @@ -53,6 +53,7 @@ #define VALATTR_INSECURITY 0x0010 #define VALATTR_DLV 0x0020 #define VALATTR_DLVTRIED 0x0040 +#define VALATTR_DLVSEPTRIED 0x0080 #define VALATTR_NEEDNOQNAME 0x0100 #define VALATTR_NEEDNOWILDCARD 0x0200 @@ -68,6 +69,7 @@ #define NEEDNOWILDCARD(val) ((val->attributes & VALATTR_NEEDNOWILDCARD) != 0) #define DLV(val) ((val->attributes & VALATTR_DLV) != 0) #define DLVTRIED(val) ((val->attributes & VALATTR_DLVTRIED) != 0) +#define DLVSEPTRIED(val) ((val->attributes & VALATTR_DLVSEPTRIED) != 0) #define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0) @@ -107,6 +109,9 @@ validator_logcreate(dns_validator_t *val, static isc_result_t dlv_validatezonekey(dns_validator_t *val); +static isc_result_t +finddlvsep(dns_validator_t *val, isc_boolean_t resume); + static void validator_done(dns_validator_t *val, isc_result_t result) { isc_task_t *task; @@ -735,6 +740,16 @@ negauthvalidated(isc_task_t *task, isc_event_t *event) { static inline isc_result_t view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { + dns_fixedname_t fixedname; + dns_name_t *foundname; + dns_rdata_nsec_t nsec; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_result_t result; + unsigned int options; + char buf1[DNS_NAME_FORMATSIZE]; + char buf2[DNS_NAME_FORMATSIZE]; + char buf3[DNS_NAME_FORMATSIZE]; + if (dns_rdataset_isassociated(&val->frdataset)) dns_rdataset_disassociate(&val->frdataset); if (dns_rdataset_isassociated(&val->fsigrdataset)) @@ -742,9 +757,106 @@ view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { 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)); + + options = DNS_DBFIND_PENDINGOK; + if (type == dns_rdatatype_dlv) + options |= DNS_DBFIND_COVERINGNSEC; + dns_fixedname_init(&fixedname); + foundname = dns_fixedname_name(&fixedname); + result = dns_view_find(val->view, name, type, 0, options, + ISC_FALSE, NULL, NULL, foundname, + &val->frdataset, &val->fsigrdataset); + if (result == DNS_R_NXDOMAIN) { + if (dns_rdataset_isassociated(&val->frdataset)) + dns_rdataset_disassociate(&val->frdataset); + if (dns_rdataset_isassociated(&val->fsigrdataset)) + dns_rdataset_disassociate(&val->fsigrdataset); + } else if (result == DNS_R_COVERINGNSEC) { + validator_log(val, ISC_LOG_DEBUG(3), "DNS_R_COVERINGNSEC"); + /* + * Check if the returned NSEC covers the name. + */ + INSIST(type == dns_rdatatype_dlv); + if (val->frdataset.trust != dns_trust_secure) { + validator_log(val, ISC_LOG_DEBUG(3), + "covering nsec: trust %u", + val->frdataset.trust); + goto notfound; + } + result = dns_rdataset_first(&val->frdataset); + if (result != ISC_R_SUCCESS) + goto notfound; + dns_rdataset_current(&val->frdataset, &rdata); + if (dns_nsec_typepresent(&rdata, dns_rdatatype_ns) && + !dns_nsec_typepresent(&rdata, dns_rdatatype_soa)) { + /* Parent NSEC record. */ + if (dns_name_issubdomain(name, foundname)) { + validator_log(val, ISC_LOG_DEBUG(3), + "covering nsec: for parent"); + goto notfound; + } + } + result = dns_rdata_tostruct(&rdata, &nsec, NULL); + if (result != ISC_R_SUCCESS) + goto notfound; + if (dns_name_compare(foundname, &nsec.next) >= 0) { + /* End of zone chain. */ + if (!dns_name_issubdomain(name, &nsec.next)) { + /* + * XXXMPA We could look for a parent NSEC + * at nsec.next and if found retest with + * this NSEC. + */ + dns_rdata_freestruct(&nsec); + validator_log(val, ISC_LOG_DEBUG(3), + "covering nsec: not in zone"); + goto notfound; + } + } else if (dns_name_compare(name, &nsec.next) >= 0) { + /* + * XXXMPA We could check if this NSEC is at a zone + * apex and if the qname is not below it and look for + * a parent NSEC with the same name. This requires + * that we can cache both NSEC records which we + * currently don't support. + */ + dns_rdata_freestruct(&nsec); + validator_log(val, ISC_LOG_DEBUG(3), + "covering nsec: not in range"); + goto notfound; + } + if (isc_log_wouldlog(dns_lctx,ISC_LOG_DEBUG(3))) { + dns_name_format(name, buf1, sizeof buf1); + dns_name_format(foundname, buf2, sizeof buf2); + dns_name_format(&nsec.next, buf3, sizeof buf3); + validator_log(val, ISC_LOG_DEBUG(3), + "covering nsec found: '%s' '%s' '%s'", + buf1, buf2, buf3); + } + if (dns_rdataset_isassociated(&val->frdataset)) + dns_rdataset_disassociate(&val->frdataset); + if (dns_rdataset_isassociated(&val->fsigrdataset)) + dns_rdataset_disassociate(&val->fsigrdataset); + dns_rdata_freestruct(&nsec); + result = DNS_R_NCACHENXDOMAIN; + } else if (result != ISC_R_SUCCESS && + result != DNS_R_GLUE && + result != DNS_R_HINT && + result != DNS_R_NCACHENXDOMAIN && + result != DNS_R_NCACHENXRRSET && + result != DNS_R_NXRRSET && + result != DNS_R_HINTNXRRSET && + result != ISC_R_NOTFOUND) { + goto notfound; + } + return (result); + + notfound: + if (dns_rdataset_isassociated(&val->frdataset)) + dns_rdataset_disassociate(&val->frdataset); + if (dns_rdataset_isassociated(&val->fsigrdataset)) + dns_rdataset_disassociate(&val->fsigrdataset); + return (ISC_R_NOTFOUND); } static inline isc_boolean_t @@ -2097,9 +2209,127 @@ check_ds_algorithm(dns_validator_t *val, dns_name_t *name, return (ISC_FALSE); } +static void +dlv_fetched2(isc_task_t *task, isc_event_t *event) { + dns_fetchevent_t *devent; + dns_validator_t *val; + isc_boolean_t want_destroy; + isc_result_t eresult; + isc_result_t result; + + UNUSED(task); + INSIST(event->ev_type == DNS_EVENT_FETCHDONE); + devent = (dns_fetchevent_t *)event; + val = devent->ev_arg; + eresult = devent->result; + + isc_event_free(&event); + dns_resolver_destroyfetch(&val->fetch); + + INSIST(val->event != NULL); + validator_log(val, ISC_LOG_DEBUG(3), "in dlv_fetched2: %s", + dns_result_totext(eresult)); + + LOCK(&val->lock); + if (eresult == ISC_R_SUCCESS) { + val->havedlvsep = ISC_TRUE; + result = proveunsecure(val, ISC_FALSE); + if (result != DNS_R_WAIT) + validator_done(val, result); + } else if (eresult == DNS_R_NXRRSET || + eresult == DNS_R_NXDOMAIN || + eresult == DNS_R_NCACHENXRRSET || + eresult == DNS_R_NCACHENXDOMAIN) { + result = finddlvsep(val, ISC_TRUE); + if (result == ISC_R_SUCCESS) { + result = proveunsecure(val, ISC_FALSE); + if (result != DNS_R_WAIT) + validator_done(val, result); + } else if (result == ISC_R_NOTFOUND) { + validator_done(val, ISC_R_SUCCESS); + } else if (result != DNS_R_WAIT) + validator_done(val, result); + } + want_destroy = exit_check(val); + UNLOCK(&val->lock); + if (want_destroy) + destroy(val); +} + +static isc_result_t +finddlvsep(dns_validator_t *val, isc_boolean_t resume) { + dns_fixedname_t dlvfixed; + dns_name_t *dlvname; + dns_name_t *dlvsep; + dns_name_t noroot; + isc_result_t result; + unsigned int labels; + + if (!resume) { + dns_fixedname_init(&val->dlvsep); + dlvsep = dns_fixedname_name(&val->dlvsep); + dns_name_copy(val->event->name, dlvsep, NULL); + val->attributes |= VALATTR_DLVSEPTRIED; + } else { + dlvsep = dns_fixedname_name(&val->dlvsep); + labels = dns_name_countlabels(dlvsep); + dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep); + } + dns_name_init(&noroot, NULL); + dns_fixedname_init(&dlvfixed); + dlvname = dns_fixedname_name(&dlvfixed); + labels = dns_name_countlabels(dlvsep); + dns_name_getlabelsequence(dlvsep, 0, labels - 1, &noroot); + result = dns_name_concatenate(&noroot, val->view->dlv, dlvname, NULL); + while (result == ISC_R_NOSPACE) { + labels = dns_name_countlabels(dlvsep); + dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep); + dns_name_getlabelsequence(dlvsep, 0, labels - 2, &noroot); + result = dns_name_concatenate(&noroot, val->view->dlv, + dlvname, NULL); + } + if (result != ISC_R_SUCCESS) { + validator_log(val, ISC_LOG_DEBUG(2), "DLV concatenate failed"); + return (DNS_R_NOVALIDSIG); + } + + while (dns_name_countlabels(dlvname) > + dns_name_countlabels(val->view->dlv)) + { + result = view_find(val, dlvname, dns_rdatatype_dlv); + if (result == ISC_R_SUCCESS) { + if (val->frdataset.trust < dns_trust_secure) + return (DNS_R_NOVALIDSIG); + val->havedlvsep = ISC_TRUE; + return (ISC_R_SUCCESS); + } + if (result == ISC_R_NOTFOUND) { + result = create_fetch(val, dlvname, dns_rdatatype_dlv, + dlv_fetched2, "finddlvsep"); + if (result != ISC_R_SUCCESS) + return (result); + return (DNS_R_WAIT); + } + if (result != DNS_R_NXRRSET && + result != DNS_R_NXDOMAIN && + result != DNS_R_NCACHENXRRSET && + result != DNS_R_NCACHENXDOMAIN) + return (result); + /* + * Strip first labels from both dlvsep and dlvname. + */ + labels = dns_name_countlabels(dlvsep); + dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep); + labels = dns_name_countlabels(dlvname); + dns_name_getlabelsequence(dlvname, 1, labels - 1, dlvname); + } + return (ISC_R_NOTFOUND); +} + static isc_result_t proveunsecure(dns_validator_t *val, isc_boolean_t resume) { isc_result_t result; + isc_result_t tresult; dns_fixedname_t secroot; dns_name_t *tname; @@ -2110,11 +2340,30 @@ proveunsecure(dns_validator_t *val, isc_boolean_t resume) { /* * If the name is not under a security root, it must be insecure. */ - if (result == ISC_R_NOTFOUND) - return (ISC_R_SUCCESS); + if (val->view->dlv != NULL && !DLVSEPTRIED(val) && + !dns_name_issubdomain(val->event->name, val->view->dlv)) { + tresult = finddlvsep(val, ISC_FALSE); + if (tresult != ISC_R_NOTFOUND && tresult != ISC_R_SUCCESS) { + validator_log(val, ISC_LOG_DEBUG(3), + "finddlvsep returned: %s", + dns_result_totext(tresult)); + return (tresult); + } + } - else if (result != ISC_R_SUCCESS) + if (result == ISC_R_NOTFOUND) { + if (!val->havedlvsep) + return (ISC_R_SUCCESS); + dns_name_copy(dns_fixedname_name(&val->dlvsep), + dns_fixedname_name(&secroot), NULL); + } else if (result != ISC_R_SUCCESS) return (result); + else if (val->havedlvsep && + dns_name_issubdomain(dns_fixedname_name(&val->dlvsep), + dns_fixedname_name(&secroot))) { + dns_name_copy(dns_fixedname_name(&val->dlvsep), + dns_fixedname_name(&secroot), NULL); + } if (!resume) { val->labels = @@ -2427,6 +2676,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, val->nsecset = NULL; val->soaname = NULL; val->seensig = ISC_FALSE; + val->havedlvsep = ISC_FALSE; val->mustbesecure = dns_resolver_getmustbesecure(view->resolver, name); dns_rdataset_init(&val->frdataset); dns_rdataset_init(&val->fsigrdataset); diff --git a/util/copyrights b/util/copyrights index 85b1cbda74..a148849496 100644 --- a/util/copyrights +++ b/util/copyrights @@ -457,6 +457,25 @@ ./bin/tests/system/dialup/setup.sh SH 2000,2001,2004 ./bin/tests/system/dialup/tests.sh SH 2000,2001,2004 ./bin/tests/system/digcomp.pl PERL 2000,2001,2004 +./bin/tests/system/dlv/clean.sh SH 2004 +./bin/tests/system/dlv/setup.sh SH 2004 +./bin/tests/system/dlv/tests.sh SH 2004 +./bin/tests/system/dlv/ns1/named.conf CONF-C 2004 +./bin/tests/system/dlv/ns1/root.db ZONE 2004 +./bin/tests/system/dlv/ns1/rootservers.utld.db ZONE 2004 +./bin/tests/system/dlv/ns2/hints ZONE 2004 +./bin/tests/system/dlv/ns2/named.conf CONF-C 2004 +./bin/tests/system/dlv/ns2/utld.db ZONE 2004 +./bin/tests/system/dlv/ns3/child.db.in ZONE 2004 +./bin/tests/system/dlv/ns3/dlv.db.in ZONE 2004 +./bin/tests/system/dlv/ns3/hints ZONE 2004 +./bin/tests/system/dlv/ns3/named.conf CONF-C 2004 +./bin/tests/system/dlv/ns3/sign.sh SH 2004 +./bin/tests/system/dlv/ns4/child.db ZONE 2004 +./bin/tests/system/dlv/ns4/hints ZONE 2004 +./bin/tests/system/dlv/ns4/named.conf CONF-C 2004 +./bin/tests/system/dlv/ns5/hints ZONE 2004 +./bin/tests/system/dlv/ns5/named.conf CONF-C 2004 ./bin/tests/system/dnssec/README TXT.BRIEF 2000,2001,2002,2004 ./bin/tests/system/dnssec/clean.sh SH 2000,2001,2002,2004 ./bin/tests/system/dnssec/dnssec_update_test.pl PERL 2002,2004