rustls: support ECH w/ DoH lookup for config

E.g. `curl --tlsv1.3 --doh-url https://one.one.one.one/dns-query --ech true ...`

Closes #16828
This commit is contained in:
Daniel McCarney 2025-03-24 12:26:21 -04:00 committed by Daniel Stenberg
parent b1ba919676
commit 07addb54f4
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2

View File

@ -900,58 +900,94 @@ cleanup:
#if defined(USE_ECH)
static CURLcode
init_config_builder_ech(struct Curl_easy *data,
const struct ssl_connect_data *connssl,
struct rustls_client_config_builder *builder)
{
const rustls_hpke *hpke = rustls_supported_hpke();
unsigned char *ech_config = NULL;
size_t ech_config_len = 0;
struct Curl_dns_entry *dns = NULL;
struct Curl_https_rrinfo *rinfo = NULL;
CURLcode result = CURLE_OK;
rustls_result rr;
if(!hpke) {
failf(data,
"rustls: ECH unavailable, rustls-ffi built without "
"HPKE compatible crypto provider");
return CURLE_SSL_CONNECT_ERROR;
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
if(data->set.str[STRING_ECH_PUBLIC]) {
failf(data, "rustls: ECH outername not supported");
return CURLE_SSL_CONNECT_ERROR;
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
if(data->set.tls_ech == CURLECH_GREASE) {
rustls_result rr;
rr = rustls_client_config_builder_enable_ech_grease(builder, hpke);
if(rr != RUSTLS_RESULT_OK) {
rustls_failf(data, rr, "rustls: failed to configure ECH GREASE");
return CURLE_SSL_CONNECT_ERROR;
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
return CURLE_OK;
}
else if(data->set.tls_ech & CURLECH_CLA_CFG
if(data->set.tls_ech & CURLECH_CLA_CFG
&& data->set.str[STRING_ECH_CONFIG]) {
const char *b64 = data->set.str[STRING_ECH_CONFIG];
size_t decode_result;
rustls_result rr;
if(!b64) {
infof(data, "rustls: ECHConfig from command line empty");
return CURLE_SSL_CONNECT_ERROR;
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
/* rustls-ffi expects the raw TLS encoded ECHConfigList bytes */
decode_result = Curl_base64_decode(b64, &ech_config, &ech_config_len);
if(decode_result || !ech_config) {
infof(data, "rustls: cannot base64 decode ECHConfig from command line");
return CURLE_SSL_CONNECT_ERROR;
}
rr = rustls_client_config_builder_enable_ech(builder,
ech_config,
ech_config_len,
hpke);
if(rr != RUSTLS_RESULT_OK) {
rustls_failf(data, rr, "rustls: failed to configure ECH");
return CURLE_SSL_CONNECT_ERROR;
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
}
else {
if(connssl->peer.hostname) {
dns = Curl_fetch_addr(
data,
connssl->peer.hostname,
connssl->peer.port);
}
if(!dns) {
failf(data, "rustls: ECH requested but no DNS info available");
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
rinfo = dns->hinfo;
if(!rinfo || !rinfo->echconfiglist) {
failf(data, "rustls: ECH requested but no ECHConfig available");
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
ech_config = rinfo->echconfiglist;
ech_config_len = rinfo->echconfiglist_len;
}
return CURLE_OK;
rr = rustls_client_config_builder_enable_ech(builder,
ech_config,
ech_config_len,
hpke);
if(rr != RUSTLS_RESULT_OK) {
rustls_failf(data, rr, "rustls: failed to configure ECH");
result = CURLE_SSL_CONNECT_ERROR;
goto cleanup;
}
cleanup:
if(dns) {
Curl_resolv_unlink(data, &dns);
}
return result;
}
#endif /* USE_ECH */
@ -1014,7 +1050,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
#if defined(USE_ECH)
if(ECH_ENABLED(data)) {
result = init_config_builder_ech(data, config_builder);
result = init_config_builder_ech(data, connssl, config_builder);
if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
rustls_client_config_builder_free(config_builder);
return result;