]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add support for EDE code 1 and 2
authorColin Vidal <colin@isc.org>
Mon, 13 Jan 2025 13:50:01 +0000 (14:50 +0100)
committerColin Vidal <colin@isc.org>
Fri, 24 Jan 2025 13:27:16 +0000 (14:27 +0100)
Add support for EDE codes 1 (Unsupported DNSKEY Algorithm) and 2
(Unsupported DS Digest Type) which might occurs during DNSSEC
validation in case of unsupported DNSKEY algorithm or DS digest type.

Because DNSSEC internally kicks off various fetches, we need to copy
all encountered extended errors from fetch responses to the fetch
context. Upon an event, the errors from the fetch context are copied
to the client response.

(cherry picked from commit 46a58acdf511e0ce8ab3af001f248688afc24998)

lib/dns/include/dns/resolver.h
lib/dns/include/dns/validator.h
lib/dns/resolver.c
lib/dns/validator.c
lib/ns/query.c

index a9aa1ffa97c0596c9a19f0561608a216d3f6a2bd..155e35b8de1c49ad2bbf4125d6443aa2ea51cd30 100644 (file)
@@ -265,6 +265,8 @@ ISC_REFCOUNT_TRACE_DECL(dns_resolver);
 ISC_REFCOUNT_DECL(dns_resolver);
 #endif
 
+typedef struct fetchctx fetchctx_t;
+
 isc_result_t
 dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
                         dns_rdatatype_t type, const dns_name_t *domain,
@@ -646,4 +648,32 @@ dns_resolver_freefresp(dns_fetchresponse_t **frespp);
  * \li 'frespp' is valid. No-op if *frespp == NULL
  */
 
+void
+dns_resolver_edeappend(fetchctx_t *fctx, uint16_t info_code, const char *what,
+                      const dns_name_t *name, dns_rdatatype_t type);
+/*%<
+ * Helper for EDE message creation in resolver context. Creates message
+ * containing the "what" context message as well as the "name"/"type" being
+ * resolved
+ *
+ * Requires:
+ * \li "fctx" is valid
+ * \li "what" is valid
+ * \li "info_code" is within the range of defined EDE codes
+ * \li "name" is valid
+ */
+
+void
+dns_resolver_copyede(dns_fetch_t *from, fetchctx_t *to);
+/*%<
+ * Copy all EDE messages from the fetchctx_t "from->private" to the fetchctx_t
+ * "to". The fetchctx_t from "from" is not locked. This is reponsability of the
+ * caller to lock it if this function is called in a context needing "from"
+ * synchronization.
+ *
+ * Requires:
+ * \li "from" is valid
+ * \li "to" is valid
+ */
+
 ISC_LANG_ENDDECLS
index 09dc3a13a305636132e880ca77bbb7b45d53e2e5..973adbc510a743952402015731edad6d86957b00 100644 (file)
@@ -148,13 +148,20 @@ struct dns_validator {
        isc_stdtime_t start;
 
        bool           digest_sha1;
-       bool           supported_algorithm;
+       uint8_t        unsupported_algorithm;
+       uint8_t        unsupported_digest;
        dns_rdata_t    rdata;
        bool           resume;
        uint32_t      *nvalidations;
        uint32_t      *nfails;
        isc_counter_t *qc;
        isc_counter_t *gqc;
+
+       /*
+        * opaque type here, used to send EDE errors during DNSSEC valiration
+        * to the fetch context.
+        */
+       fetchctx_t *fctx;
 };
 
 /*%
@@ -173,7 +180,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
                     dns_message_t *message, unsigned int options,
                     isc_loop_t *loop, isc_job_cb cb, void *arg,
                     uint32_t *nvalidations, uint32_t *nfails,
-                    isc_counter_t *qc, isc_counter_t *gqc,
+                    isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *fctx,
                     dns_validator_t **validatorp);
 /*%<
  * Start a DNSSEC validation.
index 75d8e88bacfd67c6965ecbc16c0577d4ca3c7c84..31d3c4b3e1eca0864bade63b108800ef27ec7c1d 100644 (file)
@@ -988,7 +988,7 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
        result = dns_validator_create(
                fctx->res->view, name, type, rdataset, sigrdataset, message,
                valoptions, fctx->loop, validated, valarg, &fctx->nvalidations,
-               &fctx->nfails, fctx->qc, fctx->gqc, &validator);
+               &fctx->nfails, fctx->qc, fctx->gqc, fctx, &validator);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
        inc_stats(fctx->res, dns_resstatscounter_val);
        if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
@@ -1337,8 +1337,6 @@ fctx_cleanup(fetchctx_t *fctx) {
                ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
                dns_adb_freeaddrinfo(fctx->adb, &addr);
        }
-
-       dns_ede_unlinkall(fctx->mctx, &fctx->edelist);
 }
 
 static void
@@ -1603,7 +1601,7 @@ fctx_sendevents(fetchctx_t *fctx, isc_result_t result) {
 
                /*
                 * Copy EDE that occured during the resolution to all
-                * clients
+                * clients.
                 */
                for (dns_ede_t *ede = ISC_LIST_HEAD(fctx->edelist); ede != NULL;
                     ede = ISC_LIST_NEXT(ede, link))
@@ -4201,6 +4199,7 @@ resume_qmin(void *arg) {
        }
        UNLOCK(&fctx->lock);
 
+       dns_resolver_copyede(fctx->qminfetch, fctx);
        dns_resolver_destroyfetch(&fctx->qminfetch);
 
        /*
@@ -4339,7 +4338,6 @@ fctx_destroy(fetchctx_t *fctx) {
        REQUIRE(ISC_LIST_EMPTY(fctx->queries));
        REQUIRE(ISC_LIST_EMPTY(fctx->finds));
        REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
-       REQUIRE(ISC_LIST_EMPTY(fctx->edelist));
        REQUIRE(atomic_load_acquire(&fctx->pending) == 0);
        REQUIRE(ISC_LIST_EMPTY(fctx->validators));
        REQUIRE(fctx->state != fetchstate_active);
@@ -4375,6 +4373,8 @@ fctx_destroy(fetchctx_t *fctx) {
                isc_mem_put(fctx->mctx, sa, sizeof(*sa));
        }
 
+       dns_ede_unlinkall(fctx->mctx, &fctx->edelist);
+
        isc_counter_detach(&fctx->qc);
        if (fctx->gqc != NULL) {
                isc_counter_detach(&fctx->gqc);
@@ -7208,6 +7208,7 @@ resume_dslookup(void *arg) {
        }
 
 cleanup:
+       dns_resolver_copyede(fetch, fctx);
        dns_resolver_destroyfetch(&fetch);
 
        if (result != ISC_R_SUCCESS) {
@@ -11114,3 +11115,44 @@ dns_resolver_freefresp(dns_fetchresponse_t **frespp) {
        dns_ede_unlinkall(fresp->mctx, &fresp->edelist);
        isc_mem_putanddetach(&fresp->mctx, fresp, sizeof(*fresp));
 }
+
+void
+dns_resolver_edeappend(fetchctx_t *fctx, uint16_t info_code, const char *what,
+                      const dns_name_t *name, dns_rdatatype_t type) {
+       REQUIRE(VALID_FCTX(fctx));
+       REQUIRE(what);
+       REQUIRE(name);
+
+       char extra[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE +
+                  DNS_EDE_EXTRATEXT_LEN];
+       size_t offset = 0;
+
+       /*
+        * -2 to leave room for separator "/" and NULL terminator
+        */
+       snprintf(extra, DNS_EDE_EXTRATEXT_LEN - 2, "%s ", what);
+       offset += strlen(extra);
+       dns_name_format(name, extra + offset, DNS_NAME_FORMATSIZE);
+       offset = strlcat(extra, "/", sizeof(extra));
+       dns_rdatatype_format(type, extra + offset,
+                            DNS_RDATATYPE_FORMATSIZE + 1);
+
+       LOCK(&fctx->lock);
+       dns_ede_append(fctx->mctx, &fctx->edelist, info_code, extra);
+       UNLOCK(&fctx->lock)
+}
+
+void
+dns_resolver_copyede(dns_fetch_t *from, fetchctx_t *to) {
+       REQUIRE(DNS_FETCH_VALID(from));
+       REQUIRE(VALID_FCTX(to));
+
+       LOCK(&to->lock);
+       for (dns_ede_t *ede = ISC_LIST_HEAD(from->private->edelist);
+            ede != NULL; ede = ISC_LIST_NEXT(ede, link))
+       {
+               dns_ede_append(to->mctx, &to->edelist, ede->info_code,
+                              ede->extra_text);
+       }
+       UNLOCK(&to->lock);
+}
index 7a4789229a335914701b5298f29b043aed94f70a..30a8b8de51b48b74eee1a68f5e05b0e1307d9ecf 100644 (file)
@@ -173,6 +173,9 @@ expire_rdatasets(dns_validator_t *val) {
        }
 }
 
+static void
+validate_extendederror(dns_validator_t *val);
+
 /*%
  * Ensure the validator's rdatasets are disassociated.
  */
@@ -410,6 +413,7 @@ fetch_callback_dnskey(void *arg) {
        }
 
        validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_dnskey");
+       dns_resolver_copyede(val->fetch, val->fctx);
        dns_resolver_destroyfetch(&val->fetch);
 
        if (CANCELED(val) || CANCELING(val)) {
@@ -484,6 +488,7 @@ fetch_callback_ds(void *arg) {
        }
 
        validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_ds");
+       dns_resolver_copyede(val->fetch, val->fctx);
        dns_resolver_destroyfetch(&val->fetch);
 
        if (CANCELED(val) || CANCELING(val)) {
@@ -976,7 +981,7 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
        result = dns_validator_create(val->view, name, type, rdataset, sig,
                                      NULL, vopts, val->loop, cb, val,
                                      val->nvalidations, val->nfails, val->qc,
-                                     val->gqc, &val->subvalidator);
+                                     val->gqc, val->fctx, &val->subvalidator);
        if (result == ISC_R_SUCCESS) {
                dns_validator_attach(val, &val->subvalidator->parent);
                val->subvalidator->depth = val->depth + 1;
@@ -1536,6 +1541,8 @@ cleanup:
                return;
        }
 
+       val->unsupported_algorithm = 0;
+       val->unsupported_digest = 0;
        result = validate_async_run(val, validate_answer_process);
        INSIST(result == DNS_R_WAIT);
 }
@@ -1656,6 +1663,9 @@ validate_answer_process(void *arg) {
        if (!dns_resolver_algorithm_supported(val->view->resolver, val->name,
                                              val->siginfo->algorithm))
        {
+               if (val->unsupported_algorithm == 0) {
+                       val->unsupported_algorithm = val->siginfo->algorithm;
+               }
                goto next_key;
        }
 
@@ -1779,6 +1789,10 @@ validate_answer_iter_done(dns_validator_t *val, isc_result_t result) {
                return;
        }
 
+       if (result != ISC_R_SUCCESS && result != DNS_R_WAIT) {
+               validate_extendederror(val);
+       }
+
        validator_log(val, ISC_LOG_INFO, "no valid signature found");
        validate_async_done(val, val->result);
 }
@@ -1979,12 +1993,15 @@ validate_dnskey_dsset_done(dns_validator_t *val, isc_result_t result) {
                validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (DS)");
                break;
        case ISC_R_NOMORE:
-               if (!val->supported_algorithm) {
+               if (val->unsupported_algorithm != 0 ||
+                   val->unsupported_digest != 0)
+               {
                        validator_log(val, ISC_LOG_DEBUG(3),
                                      "no supported algorithm/digest (DS)");
                        result = markanswer(
                                val, "validate_dnskey (3)",
                                "no supported algorithm/digest (DS)");
+                       validate_extendederror(val);
                        break;
                }
                FALLTHROUGH;
@@ -2021,17 +2038,21 @@ validate_dnskey_dsset(dns_validator_t *val) {
        if (!dns_resolver_ds_digest_supported(val->view->resolver, val->name,
                                              ds.digest_type))
        {
+               if (val->unsupported_digest == 0) {
+                       val->unsupported_digest = ds.digest_type;
+               }
                return DNS_R_BADALG;
        }
 
        if (!dns_resolver_algorithm_supported(val->view->resolver, val->name,
                                              ds.algorithm))
        {
+               if (val->unsupported_algorithm == 0) {
+                       val->unsupported_algorithm = ds.algorithm;
+               }
                return DNS_R_BADALG;
        }
 
-       val->supported_algorithm = true;
-
        /*
         * Find the DNSKEY matching the DS...
         */
@@ -2203,8 +2224,8 @@ validate_dnskey(void *arg) {
         * key set and the matching signature.  For each such key, attempt
         * verification.
         */
-
-       val->supported_algorithm = false;
+       val->unsupported_algorithm = 0;
+       val->unsupported_digest = 0;
 
        /*
         * If DNS_DSDIGEST_SHA256 or DNS_DSDIGEST_SHA384 is present we
@@ -2942,6 +2963,13 @@ check_ds_algs(dns_validator_t *val, dns_name_t *name,
                }
                dns_rdata_reset(&dsrdata);
        }
+
+       /*
+        * No unsupported alg/digest EDE error is raised here because the prove
+        * unsecure flow always runs after a validate/validatenx flow. So if an
+        * unsupported alg/digest was found while building the chain of trust,
+        * it would be raised already.
+        */
        return false;
 }
 
@@ -3392,7 +3420,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
                     dns_message_t *message, unsigned int options,
                     isc_loop_t *loop, isc_job_cb cb, void *arg,
                     uint32_t *nvalidations, uint32_t *nfails,
-                    isc_counter_t *qc, isc_counter_t *gqc,
+                    isc_counter_t *qc, isc_counter_t *gqc, fetchctx_t *fctx,
                     dns_validator_t **validatorp) {
        isc_result_t result = ISC_R_FAILURE;
        dns_validator_t *val = NULL;
@@ -3425,6 +3453,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
                .rdata = DNS_RDATA_INIT,
                .nvalidations = nvalidations,
                .nfails = nfails,
+               .fctx = fctx,
        };
 
        isc_refcount_init(&val->references, 1);
@@ -3532,8 +3561,10 @@ destroy_validator(dns_validator_t *val) {
        if (val->gqc != NULL) {
                isc_counter_detach(&val->gqc);
        }
+
        dns_view_detach(&val->view);
        isc_loop_detach(&val->loop);
+
        isc_mem_put(mctx, val, sizeof(*val));
 }
 
@@ -3632,6 +3663,25 @@ validator_logcreate(dns_validator_t *val, dns_name_t *name,
                      caller, operation, namestr, typestr);
 }
 
+static void
+validate_extendederror(dns_validator_t *val) {
+       char txt[32];
+
+       REQUIRE(VALID_VALIDATOR(val));
+
+       if (val->unsupported_algorithm != 0) {
+               dns_secalg_format(val->unsupported_algorithm, txt, sizeof(txt));
+               dns_resolver_edeappend(val->fctx, DNS_EDE_DNSKEYALG, txt,
+                                      val->name, val->type);
+       }
+
+       if (val->unsupported_digest != 0) {
+               dns_dsdigest_format(val->unsupported_digest, txt, sizeof(txt));
+               dns_resolver_edeappend(val->fctx, DNS_EDE_DSDIGESTTYPE, txt,
+                                      val->name, val->type);
+       }
+}
+
 #if DNS_VALIDATOR_TRACE
 ISC_REFCOUNT_TRACE_IMPL(dns_validator, destroy_validator);
 #else
index 714c8d6c93268153f7f7cfb6c0e2972fa6c11900..e7372f87a8f00359c814c48ea9d9083d12674c46 100644 (file)
@@ -2499,6 +2499,18 @@ validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
                if (!dns_resolver_algorithm_supported(client->view->resolver,
                                                      name, rrsig.algorithm))
                {
+                       char txt[DNS_NAME_FORMATSIZE + 32];
+                       isc_buffer_t buffer;
+
+                       isc_buffer_init(&buffer, txt, sizeof(txt));
+                       dns_secalg_totext(rrsig.algorithm, &buffer);
+                       isc_buffer_putstr(&buffer, " ");
+                       dns_name_totext(name, DNS_NAME_OMITFINALDOT, &buffer);
+                       isc_buffer_putstr(&buffer, " (cached)");
+                       isc_buffer_putuint8(&buffer, 0);
+
+                       ns_client_extendederror(client, DNS_EDE_DNSKEYALG,
+                                               isc_buffer_base(&buffer));
                        continue;
                }
                if (!dns_name_issubdomain(name, &rrsig.signer)) {