vtls: init ssl peer only once

- check that `struct ssl_peer` is only initialized once
- fix vtls peer init to run only once
- check in peer init that hostname is not empty, fail otherwise

Closes #14152
This commit is contained in:
Stefan Eissing 2024-07-11 12:44:15 +02:00 committed by Daniel Stenberg
parent 5a9262a333
commit 0472afe5f9
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
2 changed files with 62 additions and 54 deletions

View file

@ -272,7 +272,7 @@ struct ssl_peer {
char *sni; /* SNI version of hostname or NULL if not usable */
ssl_peer_type type; /* type of the peer information */
int port; /* port we are talking to */
int transport; /* TCP or QUIC */
int transport; /* one of TRNSPRT_* defines */
};
struct ssl_primary_config {

View file

@ -1566,69 +1566,70 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
int transport)
{
const char *ehostname, *edispname;
int eport;
CURLcode result = CURLE_OUT_OF_MEMORY;
/* We expect a clean struct, e.g. called only ONCE */
DEBUGASSERT(peer);
DEBUGASSERT(!peer->hostname);
DEBUGASSERT(!peer->dispname);
DEBUGASSERT(!peer->sni);
/* We need the hostname for SNI negotiation. Once handshaked, this remains
* the SNI hostname for the TLS connection. When the connection is reused,
* the settings in cf->conn might change. We keep a copy of the hostname we
* use for SNI.
*/
peer->transport = transport;
#ifndef CURL_DISABLE_PROXY
if(Curl_ssl_cf_is_proxy(cf)) {
ehostname = cf->conn->http_proxy.host.name;
edispname = cf->conn->http_proxy.host.dispname;
eport = cf->conn->http_proxy.port;
peer->port = cf->conn->http_proxy.port;
}
else
#endif
{
ehostname = cf->conn->host.name;
edispname = cf->conn->host.dispname;
eport = cf->conn->remote_port;
peer->port = cf->conn->remote_port;
}
/* change if ehostname changed */
DEBUGASSERT(!ehostname || ehostname[0]);
if(ehostname && (!peer->hostname
|| strcmp(ehostname, peer->hostname))) {
/* hostname MUST exist and not be empty */
if(!ehostname || !ehostname[0]) {
result = CURLE_FAILED_INIT;
goto out;
}
peer->hostname = strdup(ehostname);
if(!peer->hostname)
goto out;
if(!edispname || !strcmp(ehostname, edispname))
peer->dispname = peer->hostname;
else {
peer->dispname = strdup(edispname);
if(!peer->dispname)
goto out;
}
peer->type = get_peer_type(peer->hostname);
if(peer->type == CURL_SSL_PEER_DNS) {
/* not an IP address, normalize according to RCC 6066 ch. 3,
* max len of SNI is 2^16-1, no trailing dot */
size_t len = strlen(peer->hostname);
if(len && (peer->hostname[len-1] == '.'))
len--;
if(len < USHRT_MAX) {
peer->sni = calloc(1, len + 1);
if(!peer->sni)
goto out;
Curl_strntolower(peer->sni, peer->hostname, len);
peer->sni[len] = 0;
}
}
result = CURLE_OK;
out:
if(result)
Curl_ssl_peer_cleanup(peer);
peer->hostname = strdup(ehostname);
if(!peer->hostname) {
Curl_ssl_peer_cleanup(peer);
return CURLE_OUT_OF_MEMORY;
}
if(!edispname || !strcmp(ehostname, edispname))
peer->dispname = peer->hostname;
else {
peer->dispname = strdup(edispname);
if(!peer->dispname) {
Curl_ssl_peer_cleanup(peer);
return CURLE_OUT_OF_MEMORY;
}
}
peer->type = get_peer_type(peer->hostname);
if(peer->type == CURL_SSL_PEER_DNS && peer->hostname[0]) {
/* not an IP address, normalize according to RCC 6066 ch. 3,
* max len of SNI is 2^16-1, no trailing dot */
size_t len = strlen(peer->hostname);
if(len && (peer->hostname[len-1] == '.'))
len--;
if(len < USHRT_MAX) {
peer->sni = calloc(1, len + 1);
if(!peer->sni) {
Curl_ssl_peer_cleanup(peer);
return CURLE_OUT_OF_MEMORY;
}
Curl_strntolower(peer->sni, peer->hostname, len);
peer->sni[len] = 0;
}
}
}
peer->port = eport;
peer->transport = transport;
return CURLE_OK;
return result;
}
static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
@ -1667,22 +1668,29 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
if(!cf->next) {
*done = FALSE;
return CURLE_FAILED_INIT;
}
if(!cf->next->connected) {
result = cf->next->cft->do_connect(cf->next, data, blocking, done);
if(result || !*done)
return result;
}
CF_DATA_SAVE(save, cf, data);
CURL_TRC_CF(data, cf, "cf_connect()");
(void)connssl;
DEBUGASSERT(data->conn);
DEBUGASSERT(data->conn == cf->conn);
DEBUGASSERT(connssl);
DEBUGASSERT(cf->conn->host.name);
result = cf->next->cft->do_connect(cf->next, data, blocking, done);
if(result || !*done)
goto out;
*done = FALSE;
result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
if(result)
goto out;
if(!connssl->peer.hostname) {
result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
if(result)
goto out;
}
if(blocking) {
result = ssl_connect(cf, data);