From: Daniel McCarney Date: Mon, 24 Mar 2025 16:26:21 +0000 (-0400) Subject: rustls: support ECH w/ DoH lookup for config X-Git-Tag: curl-8_13_0~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=07addb54f45f736f98dcf35783717d0765cbf8b1;p=thirdparty%2Fcurl.git 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 --- diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 4d2d195a5d..e73e867917 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -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; + result = CURLE_SSL_CONNECT_ERROR; + goto cleanup; } - 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; + } + 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;