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,
*
* 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
+ */
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;
};
/*%
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.
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);
ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
dns_adb_freeaddrinfo(fctx->adb, &addr);
}
-
- dns_ede_unlinkall(fctx->mctx, &fctx->edelist);
}
static void
/*
* 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))
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 "
}
UNLOCK(&fctx->lock);
+ dns_resolver_copyede(fctx->qminfetch, fctx);
dns_resolver_destroyfetch(&fctx->qminfetch);
/*
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);
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);
}
cleanup:
+ dns_resolver_copyede(fetch, fctx);
dns_resolver_destroyfetch(&fetch);
if (result != ISC_R_SUCCESS) {
*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);
+}
}
}
+static void
+validate_extendederror(dns_validator_t *val);
+
/*%
* Ensure the validator's rdatasets are disassociated.
*/
}
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)) {
}
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)) {
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;
return;
}
+ val->unsupported_algorithm = 0;
+ val->unsupported_digest = 0;
result = validate_async_run(val, validate_answer_process);
INSIST(result == DNS_R_WAIT);
}
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;
}
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);
}
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;
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...
*/
* 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
}
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;
}
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;
.rdata = DNS_RDATA_INIT,
.nvalidations = nvalidations,
.nfails = nfails,
+ .fctx = fctx,
};
isc_refcount_init(&val->references, 1);
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));
}
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
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)) {