curl · Report #4053066
Missing applicability validation allows non-applicable HTTPS RR to reach ECH processing
How it was found
Summary
The HTTPS RR applicability check (Curl_httpsrr_applicable()) is applied before ALPN selection, but the same check is not applied consistently in the ECH initialization paths. When an HTTPS RR is not applicable to the active connection, curl correctly reports it as not applicable and avoids using it for ALPN selection. However, the ECH code paths retrieve the HTTPS RR data directly through Curl_conn_dns_get_https() and check echconfiglist without first applying the same applicability gate. This creates an implementation inconsistency between two consumers of the same HTTPS RR data: ALPN respects applicability, while ECH does not appear to enforce the same policy at the call site. RFC 9460 models an HTTPS RR as an endpoint description where TargetName and SvcParams are associated as a unit; curl’s ALPN path appears consistent with that model by checking applicability before use, while the ECH paths do not apply the same gate at the call site. Runtime testing confirmed that curl can mark the HTTPS RR as not applicable, and a unit test confirmed that echconfiglist can remain populated when Curl_httpsrr_applicable() returns false. I was not able to complete end-to-end runtime validation reaching the backend ECH call site in my local build environment, so this report does not claim confirmed end-to-end security impact.
Affected version
Repository: curl master / development build
Version tested:
curl 8.23.0-DEV (x86_64-pc-linux-gnu) libcurl/8.23.0-DEV
Features: alt-svc AsynchDNS brotli HSTS HTTP2 HTTPS-proxy HTTPSRR IDN IPv6 Largefile libz PSL SSL threadsafe UnixSockets zstd
Platform:
Linux / WSL2
Build configuration used during testing:
USE_HTTPSRR=ON
USE_ARES=ON
ECH build attempted with wolfSSL, but the ECH runtime path was not confirmed active in the final binary due to local build/runtime backend constraints.
Steps To Reproduce
-
Build curl from the current master branch with HTTPS RR support enabled.
-
Configure a local DNS resolver that returns an HTTPS RR which is not applicable to the active connection.
Example record:
test.local. 300 IN A 127.0.0.1
test.local. 300 IN HTTPS 1 . alpn=h2 port=8443 ech=<ECHConfigList>
The client connects to https://test.local/ on port 443, while the HTTPS RR specifies port=8443.
- Run curl against the local resolver:
./build-wolfssl/src/curl \
--dns-servers 127.0.0.1:5353 \
-v \
https://test.local/
Observed output:
* HTTPS-RR test.local:443: not applicable
- I also attempted to force ECH processing with:
./build-wolfssl/src/curl \
--dns-servers 127.0.0.1:5353 \
-v --ech hard \
https://test.local/
However, I was not able to complete runtime verification of the backend ECH call site in the available environment.
- Inspect the ALPN path. The applicability gate is applied before using HTTPS RR ALPN data:
if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) {
/* use rr->alpns */
}
- Inspect the ECH initialization paths. They retrieve the HTTPS RR information and check
echconfiglist, but do not apply the sameCurl_httpsrr_applicable()check before processing the ECH configuration.
OpenSSL path:
const struct Curl_https_rrinfo *rinfo =
Curl_conn_dns_get_https(data, cf->sockindex, peer->origin);
if(rinfo && rinfo->echconfiglist) {
SSL_set1_ech_config_list(octx->ssl, rinfo->echconfiglist,
rinfo->echconfiglist_len);
}
The same pattern exists in the wolfSSL and rustls ECH initialization paths.
- Runtime unit test evidence:
Case 1 (non-empty target): applicable()=0 (expect 0/FALSE)
Case 1: echconfiglist still present after applicable()=FALSE call: YES
Case 2 (alias target "."): applicable()=1 (expect 1/TRUE)
Case 3 (empty target, port match): applicable()=1 (expect 1/TRUE)
Case 4 (empty target, port MISMATCH): applicable()=0 (expect 0/FALSE)
Test ended with result 0
This confirms that the applicability check can reject an HTTPS RR while the associated ECH data remains present in the HTTPS RR info structure.
Supporting Evidence
Curl_httpsrr_applicable() call sites were checked across lib/:
lib/cf-https-connect.c — ALPN path, applicability gate present
lib/vdns/cf-dns.c — verbose logging only
lib/vtls/openssl.c — ECH path, applicability gate absent
lib/vtls/wolfssl.c — ECH path, applicability gate absent
lib/vtls/rustls.c — ECH path, applicability gate absent
The relevant implementation difference is that ALPN processing checks applicability before consuming HTTPS RR data, while ECH initialization retrieves the HTTPS RR info and checks only whether echconfiglist is present.
Expected Result
ECH configuration derived from HTTPS RR should only be used when the HTTPS RR is applicable to the active connection. The ECH path should enforce the same applicability policy already used by the ALPN path.
Actual Result
ALPN processing applies Curl_httpsrr_applicable() before consuming HTTPS RR data. ECH initialization retrieves HTTPS RR data directly and checks echconfiglist without applying the same applicability gate first.
Security Consideration
HTTPS RR data can come from DNS and influences connection setup. If different consumers of the same HTTPS RR object apply different applicability rules, curl may make inconsistent decisions from the same DNS input. This report does not claim a confirmed end-to-end exploit. The issue reported here is the inconsistent enforcement of HTTPS RR applicability checks in security-sensitive ECH initialization paths.
Suggested Fix
Apply Curl_httpsrr_applicable() before using echconfiglist in each ECH initialization path.
Example for OpenSSL:
const struct Curl_https_rrinfo *rinfo =
Curl_conn_dns_get_https(data, cf->sockindex, peer->origin);
if(rinfo && Curl_httpsrr_applicable(data, rinfo) && rinfo->echconfiglist) {
SSL_set1_ech_config_list(octx->ssl, rinfo->echconfiglist,
rinfo->echconfiglist_len);
}
The same check should be applied in the wolfSSL and rustls ECH paths.
Validation Notes
I was able to validate the HTTPS RR applicability behavior and the unit-test condition where Curl_httpsrr_applicable() returns false while echconfiglist remains populated. I was not able to complete runtime validation that reaches the backend-specific ECH configuration call site in my local environment due to build/runtime constraints around isolating an ECH-capable TLS backend.
Impact
ECH configuration may be processed from an HTTPS RR that curl has already considered not applicable to the active connection. This can cause inconsistent connection setup decisions between ALPN and ECH handling for the same DNS input. The practical end-to-end security impact has not been confirmed in my environment.