]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Validate address lookups from ADB
authorMark Andrews <marka@isc.org>
Fri, 17 Jan 2025 08:32:28 +0000 (19:32 +1100)
committerMark Andrews <marka@isc.org>
Mon, 3 Feb 2025 00:24:34 +0000 (00:24 +0000)
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.

lib/dns/adb.c
lib/dns/include/dns/adb.h
lib/dns/resolver.c

index 07b757308a81510b6accbd94a2404bfb3d154671..f4fa79166e12b3a30c830af7c71165c3818fd09c 100644 (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);
 }
 
index 4cb4e9eae8a3c3f866113429596549ce412b2678..3588df344f67a906ef946493799f7fc802d61968 100644 (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.
index 98abe475574fd43f3160a6b92316df45257500a8..ec23f3b185262821492580f548c09c2d04c655e6 100644 (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.
         */