From: Colin Vidal Date: Mon, 13 Jan 2025 13:50:01 +0000 (+0100) Subject: add support for EDE code 1 and 2 X-Git-Tag: v9.21.5~28^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=46a58acdf511e0ce8ab3af001f248688afc24998;p=thirdparty%2Fbind9.git add support for EDE code 1 and 2 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. --- diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 6a8f715afea..e9d0a537e70 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -262,6 +262,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, @@ -631,4 +633,32 @@ dns_resolver_freefresp(dns_fetchresponse_t **frespp); * * Requires: * \li 'frespp' is valid. No-op if *frespp == NULL - */ \ No newline at end of file + */ + +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 + */ diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h index e097c986115..a0c87268ee4 100644 --- a/lib/dns/include/dns/validator.h +++ b/lib/dns/include/dns/validator.h @@ -146,13 +146,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; }; /*% @@ -169,7 +176,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. diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 80b6cbe388a..13fbed4215a 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -977,7 +977,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); ISC_LIST_APPEND(fctx->validators, validator, link); @@ -1322,8 +1322,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 @@ -1588,7 +1586,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)) @@ -4083,7 +4081,6 @@ fctx_try(fetchctx_t *fctx, bool retrying) { sizeof(namebuf)); dns_rdatatype_format(fctx->qmintype, typebuf, sizeof(typebuf)); - isc_log_write(DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR, "fctx %p(%s): attempting QNAME " @@ -4200,6 +4197,7 @@ resume_qmin(void *arg) { } UNLOCK(&fctx->lock); + dns_resolver_copyede(fctx->qminfetch, fctx); dns_resolver_destroyfetch(&fctx->qminfetch); /* @@ -4338,7 +4336,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); @@ -4374,6 +4371,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); @@ -7201,6 +7200,7 @@ resume_dslookup(void *arg) { } cleanup: + dns_resolver_copyede(fetch, fctx); dns_resolver_destroyfetch(&fetch); if (result != ISC_R_SUCCESS) { @@ -11077,4 +11077,45 @@ dns_resolver_freefresp(dns_fetchresponse_t **frespp) { *frespp = NULL; dns_ede_unlinkall(fresp->mctx, &fresp->edelist); isc_mem_putanddetach(&fresp->mctx, fresp, sizeof(*fresp)); -} \ No newline at end of file +} + +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); +} diff --git a/lib/dns/validator.c b/lib/dns/validator.c index 4dc4edbd716..9cecd6a781e 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -174,6 +174,9 @@ expire_rdatasets(dns_validator_t *val) { } } +static void +validate_extendederror(dns_validator_t *val); + /*% * Ensure the validator's rdatasets are disassociated. */ @@ -402,6 +405,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)) { @@ -476,6 +480,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)) { @@ -965,7 +970,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; @@ -1525,6 +1530,8 @@ cleanup: return; } + val->unsupported_algorithm = 0; + val->unsupported_digest = 0; result = validate_async_run(val, validate_answer_process); INSIST(result == DNS_R_WAIT); } @@ -1645,6 +1652,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; } @@ -1768,6 +1778,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); } @@ -1968,10 +1982,13 @@ 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)"); + validate_extendederror(val); break; } FALLTHROUGH; @@ -2008,17 +2025,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... */ @@ -2190,8 +2211,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 @@ -2929,6 +2950,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; } @@ -3372,7 +3400,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; @@ -3405,6 +3433,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); @@ -3511,8 +3540,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)); } @@ -3610,6 +3641,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 diff --git a/lib/ns/query.c b/lib/ns/query.c index 4584be6e267..486e667e641 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -2492,6 +2492,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)) {