2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 14:35:26 +00:00

Validate address lookups from ADB

The address lookups from ADB were not being validated, allowing
spoofed responses to be accepted and used for other lookups.

Validate the answers except when CD=1 is set in the triggering
request.  Separate ADB names looked up with CD=1 from those without
CD=1, to prevent the use of unvalidated answers in the normal lookup
case (CD=0).  Set the TTL on unvalidated (pending) responses to
ADB_CACHE_MINIMUM when adding them to the ADB.
This commit is contained in:
Mark Andrews
2025-01-17 19:32:28 +11:00
parent 282b0ed514
commit ea9d7080cd
3 changed files with 36 additions and 13 deletions

View File

@@ -332,7 +332,7 @@ expire_entry(dns_adbentry_t *adbentry);
static isc_result_t static isc_result_t
dbfind_name(dns_adbname_t *, isc_stdtime_t, dns_rdatatype_t); dbfind_name(dns_adbname_t *, isc_stdtime_t, dns_rdatatype_t);
static isc_result_t static isc_result_t
fetch_name(dns_adbname_t *, bool, unsigned int, isc_counter_t *qc, fetch_name(dns_adbname_t *, bool, bool, unsigned int, isc_counter_t *qc,
isc_counter_t *gqc, dns_rdatatype_t); isc_counter_t *gqc, dns_rdatatype_t);
static void static void
destroy(dns_adb_t *); destroy(dns_adb_t *);
@@ -410,10 +410,13 @@ enum {
#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) != 0) #define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) != 0)
#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) != 0) #define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) != 0)
#define FIND_STATICSTUB(fn) (((fn)->options & DNS_ADBFIND_STATICSTUB) != 0) #define FIND_STATICSTUB(fn) (((fn)->options & DNS_ADBFIND_STATICSTUB) != 0)
#define FIND_NOVALIDATE(fn) (((fn)->options & DNS_ADBFIND_NOVALIDATE) != 0)
#define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list)) #define FIND_HAS_ADDRS(fn) (!ISC_LIST_EMPTY((fn)->list))
#define FIND_NOFETCH(fn) (((fn)->options & DNS_ADBFIND_NOFETCH) != 0) #define FIND_NOFETCH(fn) (((fn)->options & DNS_ADBFIND_NOFETCH) != 0)
#define ADBNAME_FLAGS_MASK (DNS_ADBFIND_STARTATZONE | DNS_ADBFIND_STATICSTUB) #define ADBNAME_FLAGS_MASK \
(DNS_ADBFIND_STARTATZONE | DNS_ADBFIND_STATICSTUB | \
DNS_ADBFIND_NOVALIDATE)
/* /*
* These are currently used on simple unsigned ints, so they are * These are currently used on simple unsigned ints, so they are
@@ -555,6 +558,8 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
switch (rdataset->trust) { switch (rdataset->trust) {
case dns_trust_glue: case dns_trust_glue:
case dns_trust_additional: case dns_trust_additional:
case dns_trust_pending_answer:
case dns_trust_pending_additional:
rdataset->ttl = ADB_CACHE_MINIMUM; rdataset->ttl = ADB_CACHE_MINIMUM;
break; break;
case dns_trust_ultimate: case dns_trust_ultimate:
@@ -2118,6 +2123,8 @@ fetch:
if (wanted_fetches != 0 && !(FIND_AVOIDFETCHES(find) && have_address) && if (wanted_fetches != 0 && !(FIND_AVOIDFETCHES(find) && have_address) &&
!FIND_NOFETCH(find)) !FIND_NOFETCH(find))
{ {
bool no_validate = FIND_NOVALIDATE(find);
/* /*
* We're missing at least one address family. Either the * We're missing at least one address family. Either the
* caller hasn't instructed us to avoid fetches, or we don't * caller hasn't instructed us to avoid fetches, or we don't
@@ -2133,8 +2140,8 @@ fetch:
* Start V4. * Start V4.
*/ */
if (WANT_INET(wanted_fetches) && if (WANT_INET(wanted_fetches) &&
fetch_name(adbname, start_at_zone, depth, qc, gqc, fetch_name(adbname, start_at_zone, no_validate, depth, qc,
dns_rdatatype_a) == ISC_R_SUCCESS) gqc, dns_rdatatype_a) == ISC_R_SUCCESS)
{ {
DP(DEF_LEVEL, DP(DEF_LEVEL,
"dns_adb_createfind: " "dns_adb_createfind: "
@@ -2146,8 +2153,8 @@ fetch:
* Start V6. * Start V6.
*/ */
if (WANT_INET6(wanted_fetches) && if (WANT_INET6(wanted_fetches) &&
fetch_name(adbname, start_at_zone, depth, qc, gqc, fetch_name(adbname, start_at_zone, no_validate, depth, qc,
dns_rdatatype_aaaa) == ISC_R_SUCCESS) gqc, dns_rdatatype_aaaa) == ISC_R_SUCCESS)
{ {
DP(DEF_LEVEL, DP(DEF_LEVEL,
"dns_adb_createfind: " "dns_adb_createfind: "
@@ -2951,8 +2958,9 @@ out:
} }
static isc_result_t static isc_result_t
fetch_name(dns_adbname_t *adbname, bool start_at_zone, unsigned int depth, fetch_name(dns_adbname_t *adbname, bool start_at_zone, bool no_validation,
isc_counter_t *qc, isc_counter_t *gqc, dns_rdatatype_t type) { unsigned int depth, isc_counter_t *qc, isc_counter_t *gqc,
dns_rdatatype_t type) {
isc_result_t result; isc_result_t result;
dns_adbfetch_t *fetch = NULL; dns_adbfetch_t *fetch = NULL;
dns_adb_t *adb = NULL; dns_adb_t *adb = NULL;
@@ -2960,7 +2968,7 @@ fetch_name(dns_adbname_t *adbname, bool start_at_zone, unsigned int depth,
dns_name_t *name = NULL; dns_name_t *name = NULL;
dns_rdataset_t rdataset; dns_rdataset_t rdataset;
dns_rdataset_t *nameservers = NULL; dns_rdataset_t *nameservers = NULL;
unsigned int options; unsigned int options = no_validation ? DNS_FETCHOPT_NOVALIDATE : 0;
REQUIRE(DNS_ADBNAME_VALID(adbname)); REQUIRE(DNS_ADBNAME_VALID(adbname));
@@ -2975,8 +2983,6 @@ fetch_name(dns_adbname_t *adbname, bool start_at_zone, unsigned int depth,
dns_rdataset_init(&rdataset); dns_rdataset_init(&rdataset);
options = DNS_FETCHOPT_NOVALIDATE;
if (start_at_zone) { if (start_at_zone) {
DP(ENTER_LEVEL, "fetch_name: starting at zone for name %p", DP(ENTER_LEVEL, "fetch_name: starting at zone for name %p",
adbname); adbname);
@@ -3411,6 +3417,7 @@ dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) {
isc_result_t result; isc_result_t result;
bool start_at_zone = false; bool start_at_zone = false;
bool static_stub = false; bool static_stub = false;
bool novalidate = false;
dns_adbname_t key = { .name = UNCONST(name) }; dns_adbname_t key = { .name = UNCONST(name) };
REQUIRE(DNS_ADB_VALID(adb)); REQUIRE(DNS_ADB_VALID(adb));
@@ -3424,10 +3431,12 @@ dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) {
again: again:
/* /*
* Delete all entries - with and without DNS_ADBFIND_STARTATZONE set * Delete all entries - with and without DNS_ADBFIND_STARTATZONE set
* and with and without DNS_ADBFIND_STATICSTUB set. * with and without DNS_ADBFIND_STATICSTUB set and with and without
* DNS_ADBFIND_NOVALIDATE set.
*/ */
key.flags = ((static_stub) ? DNS_ADBFIND_STATICSTUB : 0) | key.flags = ((static_stub) ? DNS_ADBFIND_STATICSTUB : 0) |
((start_at_zone) ? DNS_ADBFIND_STARTATZONE : 0); ((start_at_zone) ? DNS_ADBFIND_STARTATZONE : 0) |
((novalidate) ? DNS_ADBFIND_NOVALIDATE : 0);
result = isc_hashmap_find(adb->names, hash_adbname(&key), match_adbname, result = isc_hashmap_find(adb->names, hash_adbname(&key), match_adbname,
(void *)&key, (void **)&adbname); (void *)&key, (void **)&adbname);
@@ -3448,6 +3457,12 @@ again:
static_stub = true; static_stub = true;
goto again; goto again;
} }
if (!novalidate) {
start_at_zone = false;
static_stub = false;
novalidate = true;
goto again;
}
RWUNLOCK(&adb->names_lock, isc_rwlocktype_write); RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
} }

View File

@@ -193,6 +193,7 @@ struct dns_adbfind {
* Only look for glue record for static stub. * Only look for glue record for static stub.
*/ */
#define DNS_ADBFIND_STATICSTUB 0x00001000 #define DNS_ADBFIND_STATICSTUB 0x00001000
#define DNS_ADBFIND_NOVALIDATE 0x00002000
/*% /*%
* The answers to queries come back as a list of these. * The answers to queries come back as a list of these.

View File

@@ -3265,6 +3265,13 @@ findname(fetchctx_t *fctx, const dns_name_t *name, in_port_t port,
options |= DNS_ADBFIND_QUOTAEXEMPT; options |= DNS_ADBFIND_QUOTAEXEMPT;
} }
/*
* Pass through NOVALIDATE to any lookups ADB makes.
*/
if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
options |= DNS_ADBFIND_NOVALIDATE;
}
/* /*
* See what we know about this address. * See what we know about this address.
*/ */