From adda11330b379778f2a411236dff7860ae033307 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 17 Mar 2026 11:40:13 +0100 Subject: [PATCH] transfer: enhance secure check Introduce `Curl_xfer_is_secure(data)` that returns TRUE for transfers that happen(ed) over a end-to-end secured connection, e.g. SSL. Add test1586 to verify behaviour for http: transfers via a https: proxy. Reported-by: lg_oled77c5pua on hackerone Closes #20951 --- lib/http.c | 6 ++-- lib/imap.c | 3 +- lib/transfer.c | 22 +++++++++++++ lib/transfer.h | 4 +++ tests/data/Makefile.am | 2 +- tests/data/test1586 | 73 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 tests/data/test1586 diff --git a/lib/http.c b/lib/http.c index 37a7aa98b7..6083c2c4ba 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3219,7 +3219,7 @@ static CURLcode http_header_a(struct Curl_easy *data, const char *v; struct connectdata *conn = data->conn; v = (data->asi && - (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") @@ -3573,7 +3573,7 @@ static CURLcode http_header_s(struct Curl_easy *data, #ifndef CURL_DISABLE_HSTS /* If enabled, the header is incoming and this is over HTTPS */ v = (data->hsts && - (Curl_conn_is_ssl(conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_HSTS_HTTP") @@ -4906,7 +4906,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); } else { - scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ? "https" : "http"; + scheme = Curl_xfer_is_secure(data) ? "https" : "http"; } } diff --git a/lib/imap.c b/lib/imap.c index 8b224d4b53..7bd58c0004 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -1024,7 +1024,6 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, imapstate instate) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); (void)instate; @@ -1076,7 +1075,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, line += wordlen; } } - else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + else if(data->set.use_ssl && !Curl_xfer_is_secure(data)) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ diff --git a/lib/transfer.c b/lib/transfer.c index 82eaf10b86..63c8fcd4db 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -903,3 +903,25 @@ CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable) Curl_pgrsRecvPause(data, enable); return result; } + +bool Curl_xfer_is_secure(struct Curl_easy *data) +{ + const struct Curl_scheme *scheme = NULL; + + if(data->conn) { + scheme = data->conn->scheme; + /* if we are connected, but not use SSL, the transfer is not secure. + * This covers an insecure http:// proxy that is not tunneling. + * We enforce tunneling for such cases, but better be sure here. */ + if(Curl_conn_is_connected(data->conn, FIRSTSOCKET) && + !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) + return FALSE; + } + else if(data->info.conn_scheme) { /* was connected once */ + scheme = Curl_get_scheme(data->info.conn_scheme); + } + else { /* never connected (yet?) */ + DEBUGASSERT(0); /* not implemented, would need to parse URL */ + } + return scheme ? (scheme->flags & PROTOPT_SSL) : FALSE; +} diff --git a/lib/transfer.h b/lib/transfer.h index b9d7c79bc0..41ec0357f6 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -143,4 +143,8 @@ bool Curl_xfer_recv_is_paused(struct Curl_easy *data); CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable); CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); +/* TRUE if the transfer is secure (e.g. TLS) from libcurl to the + * URL's host. */ +bool Curl_xfer_is_secure(struct Curl_easy *data); + #endif /* HEADER_CURL_TRANSFER_H */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 81fcbdc6ff..fe74ef6e2f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -208,7 +208,7 @@ test1548 test1549 test1550 test1551 test1552 test1553 test1554 test1555 \ test1556 test1557 test1558 test1559 test1560 test1561 test1562 test1563 \ test1564 test1565 test1566 test1567 test1568 test1569 test1570 test1571 \ test1572 test1573 test1574 test1575 test1576 test1577 test1578 test1579 \ -test1580 test1581 test1582 test1583 test1584 test1585 \ +test1580 test1581 test1582 test1583 test1584 test1585 test1586 \ \ test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 \ test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 \ diff --git a/tests/data/test1586 b/tests/data/test1586 new file mode 100644 index 0000000000..2ce247147e --- /dev/null +++ b/tests/data/test1586 @@ -0,0 +1,73 @@ + + + + +HTTP +HTTPS proxy +HSTS +trailing-dot + + + + + +HTTP/1.1 200 OK +Content-Length: 6 +Strict-Transport-Security: max-age=604800 + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Strict-Transport-Security: max-age=6048000 + +-baa- + + + + + +http +https-proxy + + +HSTS +proxy +http + + + + + +HSTS via https: proxy ignored for http: URLs + + +-x https://%HOSTIP:%HTTPSPROXYPORT --proxy-insecure --hsts %LOGDIR/hsts%TESTNUMBER http://this.hsts.example./%TESTNUMBER http://another.example.com/%TESTNUMBER0002 + + + + + +GET http://this.hsts.example./%TESTNUMBER HTTP/1.1 +Host: this.hsts.example. +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://another.example.com/%TESTNUMBER0002 HTTP/1.1 +Host: another.example.com +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + +# The saved HSTS file must be empty as the HSTS headers were not secured + +# Your HSTS cache. https://curl.se/docs/hsts.html +# This file was generated by libcurl! Edit at your own risk. + + + +