2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 14:07:59 +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
dbfind_name(dns_adbname_t *, isc_stdtime_t, dns_rdatatype_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);
static void
destroy(dns_adb_t *);
@ -410,10 +410,13 @@ enum {
#define FIND_AVOIDFETCHES(fn) (((fn)->options & DNS_ADBFIND_AVOIDFETCHES) != 0)
#define FIND_STARTATZONE(fn) (((fn)->options & DNS_ADBFIND_STARTATZONE) != 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_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
@ -555,6 +558,8 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
switch (rdataset->trust) {
case dns_trust_glue:
case dns_trust_additional:
case dns_trust_pending_answer:
case dns_trust_pending_additional:
rdataset->ttl = ADB_CACHE_MINIMUM;
break;
case dns_trust_ultimate:
@ -2118,6 +2123,8 @@ fetch:
if (wanted_fetches != 0 && !(FIND_AVOIDFETCHES(find) && have_address) &&
!FIND_NOFETCH(find))
{
bool no_validate = FIND_NOVALIDATE(find);
/*
* We're missing at least one address family. Either the
* caller hasn't instructed us to avoid fetches, or we don't
@ -2133,8 +2140,8 @@ fetch:
* Start V4.
*/
if (WANT_INET(wanted_fetches) &&
fetch_name(adbname, start_at_zone, depth, qc, gqc,
dns_rdatatype_a) == ISC_R_SUCCESS)
fetch_name(adbname, start_at_zone, no_validate, depth, qc,
gqc, dns_rdatatype_a) == ISC_R_SUCCESS)
{
DP(DEF_LEVEL,
"dns_adb_createfind: "
@ -2146,8 +2153,8 @@ fetch:
* Start V6.
*/
if (WANT_INET6(wanted_fetches) &&
fetch_name(adbname, start_at_zone, depth, qc, gqc,
dns_rdatatype_aaaa) == ISC_R_SUCCESS)
fetch_name(adbname, start_at_zone, no_validate, depth, qc,
gqc, dns_rdatatype_aaaa) == ISC_R_SUCCESS)
{
DP(DEF_LEVEL,
"dns_adb_createfind: "
@ -2951,8 +2958,9 @@ out:
}
static isc_result_t
fetch_name(dns_adbname_t *adbname, bool start_at_zone, unsigned int depth,
isc_counter_t *qc, isc_counter_t *gqc, dns_rdatatype_t type) {
fetch_name(dns_adbname_t *adbname, bool start_at_zone, bool no_validation,
unsigned int depth, isc_counter_t *qc, isc_counter_t *gqc,
dns_rdatatype_t type) {
isc_result_t result;
dns_adbfetch_t *fetch = 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_rdataset_t rdataset;
dns_rdataset_t *nameservers = NULL;
unsigned int options;
unsigned int options = no_validation ? DNS_FETCHOPT_NOVALIDATE : 0;
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);
options = DNS_FETCHOPT_NOVALIDATE;
if (start_at_zone) {
DP(ENTER_LEVEL, "fetch_name: starting at zone for name %p",
adbname);
@ -3411,6 +3417,7 @@ dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) {
isc_result_t result;
bool start_at_zone = false;
bool static_stub = false;
bool novalidate = false;
dns_adbname_t key = { .name = UNCONST(name) };
REQUIRE(DNS_ADB_VALID(adb));
@ -3424,10 +3431,12 @@ dns_adb_flushname(dns_adb_t *adb, const dns_name_t *name) {
again:
/*
* 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) |
((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,
(void *)&key, (void **)&adbname);
@ -3448,6 +3457,12 @@ again:
static_stub = true;
goto again;
}
if (!novalidate) {
start_at_zone = false;
static_stub = false;
novalidate = true;
goto again;
}
RWUNLOCK(&adb->names_lock, isc_rwlocktype_write);
}

View File

@ -193,6 +193,7 @@ struct dns_adbfind {
* Only look for glue record for static stub.
*/
#define DNS_ADBFIND_STATICSTUB 0x00001000
#define DNS_ADBFIND_NOVALIDATE 0x00002000
/*%
* 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;
}
/*
* 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.
*/