vtls/vquic, keep peer name information together

- add `struct ssl_peer` to keep hostname, dispname and sni
  for a filter
- allocate `sni` for use in VTLS backend
- eliminate `Curl_ssl_snihost()` and its use of the download buffer
- use ssl_peer in SSL and QUIC filters

Closes #12349
This commit is contained in:
Stefan Eissing 2023-11-17 15:26:08 +01:00 committed by Daniel Stenberg
parent a9fd0d0083
commit fa714830e9
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
18 changed files with 190 additions and 273 deletions

View File

@ -266,6 +266,13 @@ typedef enum {
/* SSL backend-specific data; declared differently by each SSL backend */
struct ssl_backend_data;
struct ssl_peer {
char *hostname; /* hostname for verification */
char *dispname; /* display version of hostname */
char *sni; /* SNI version of hostname or NULL if not usable */
BIT(is_ip_address); /* if hostname is an IPv4|6 address */
};
struct ssl_primary_config {
char *CApath; /* certificate dir (doesn't work on windows) */
char *CAfile; /* certificate to verify peer against */

View File

@ -133,6 +133,7 @@ void Curl_ngtcp2_ver(char *p, size_t len)
struct cf_ngtcp2_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
ngtcp2_path connected_path;
ngtcp2_conn *qconn;
ngtcp2_cid dcid;
@ -561,7 +562,6 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
struct cf_ngtcp2_ctx *ctx = cf->ctx;
const uint8_t *alpn = NULL;
size_t alpnlen = 0;
unsigned char checkip[16];
DEBUGASSERT(!ctx->ssl);
ctx->ssl = SSL_new(ctx->sslctx);
@ -576,13 +576,8 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
/* set SNI */
if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
#ifdef ENABLE_IPV6
&& (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
#endif
) {
char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
if(ctx->peer.sni) {
if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) {
failf(data, "Failed set SNI");
SSL_free(ctx->ssl);
ctx->ssl = NULL;
@ -600,7 +595,6 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
CURLcode result;
gnutls_datum_t alpn[2];
/* this will need some attention when HTTPS proxy over QUIC get fixed */
const char * const hostname = cf->conn->host.name;
long * const pverifyresult = &data->set.ssl.certverifyresult;
int rc;
@ -614,7 +608,7 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
return CURLE_OUT_OF_MEMORY;
result = gtls_client_init(data, conn_config, &data->set.ssl,
hostname, ctx->gtls, pverifyresult);
&ctx->peer, ctx->gtls, pverifyresult);
if(result)
return result;
@ -1932,19 +1926,11 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct ssl_primary_config *conn_config;
CURLcode result = CURLE_OK;
const char *hostname, *disp_hostname;
int port;
char *snihost;
conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!conn_config)
return CURLE_FAILED_INIT;
Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port);
snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost)
return CURLE_PEER_FAILED_VERIFICATION;
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
cf->conn->httpversion = 30;
cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
@ -1956,19 +1942,19 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
if(!server_cert) {
return CURLE_PEER_FAILED_VERIFICATION;
}
result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, server_cert);
X509_free(server_cert);
if(result)
return result;
#elif defined(USE_GNUTLS)
result = Curl_gtls_verifyserver(data, ctx->gtls->session,
conn_config, &data->set.ssl,
hostname, disp_hostname,
conn_config, &data->set.ssl, &ctx->peer,
data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
if(result)
return result;
#elif defined(USE_WOLFSSL)
if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE)
if(!ctx->peer.sni ||
wolfSSL_check_domain_name(ctx->ssl, ctx->peer.sni) == SSL_FAILURE)
return CURLE_PEER_FAILED_VERIFICATION;
#endif
infof(data, "Verified certificate just fine");
@ -2399,6 +2385,7 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
if(ctx->qconn)
ngtcp2_conn_del(ctx->qconn);
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_ssl_peer_cleanup(&ctx->peer);
memset(ctx, 0, sizeof(*ctx));
ctx->qlogfd = -1;
@ -2470,6 +2457,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
result = Curl_ssl_peer_init(&ctx->peer, cf);
if(result)
return result;
#ifdef USE_OPENSSL
result = quic_ssl_ctx(&ctx->sslctx, cf, data);
if(result)

View File

@ -91,6 +91,7 @@ static void keylog_callback(const SSL *ssl, const char *line)
struct cf_quiche_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
quiche_conn *qconn;
quiche_config *cfg;
quiche_h3_conn *h3c;
@ -131,6 +132,8 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
if(ctx->cfg)
quiche_config_free(ctx->cfg);
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_ssl_peer_cleanup(&ctx->peer);
memset(ctx, 0, sizeof(*ctx));
}
}
@ -182,12 +185,16 @@ static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
struct ssl_primary_config *conn_config;
unsigned char checkip[16];
CURLcode result;
conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!conn_config)
return CURLE_FAILED_INIT;
result = Curl_ssl_peer_init(&ctx->peer, cf);
if(result)
return result;
DEBUGASSERT(!ctx->sslctx);
ctx->sslctx = SSL_CTX_new(TLS_method());
if(!ctx->sslctx)
@ -218,13 +225,8 @@ static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data)
SSL_set_app_data(ctx->ssl, cf);
if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
#ifdef ENABLE_IPV6
&& (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
#endif
) {
char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
if(ctx->peer.sni) {
if(!SSL_set_tlsext_host_name(ctx->ssl, ctx->peer.sni)) {
failf(data, "Failed set SNI");
SSL_free(ctx->ssl);
ctx->ssl = NULL;
@ -1267,7 +1269,7 @@ static CURLcode cf_verify_peer(struct Curl_cfilter *cf,
result = CURLE_PEER_FAILED_VERIFICATION;
goto out;
}
result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
result = Curl_ossl_verifyhost(data, cf->conn, &ctx->peer, server_cert);
X509_free(server_cert);
if(result)
goto out;

View File

@ -582,17 +582,12 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
const char * const ssl_cafile =
/* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
(ca_info_blob ? NULL : conn_config->CAfile);
const char *hostname = connssl->hostname;
const char *hostname = connssl->peer.hostname;
const bool verifypeer = conn_config->verifypeer;
const bool verifyhost = conn_config->verifyhost;
CURLcode ret;
unsigned version_min, version_max;
int session_set = 0;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
DEBUGASSERT(backend);
CURL_TRC_CF(data, cf, "connect_step1");
@ -706,11 +701,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
if((1 == Curl_inet_pton(AF_INET, hostname, &addr))
#ifdef ENABLE_IPV6
|| (1 == Curl_inet_pton(AF_INET6, hostname, &addr))
#endif
) {
if(connssl->peer.is_ip_address) {
if(verifyhost) {
failf(data, "BearSSL: "
"host verification of IP address is not supported");
@ -719,12 +710,11 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
hostname = NULL;
}
else {
char *snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost) {
if(!connssl->peer.sni) {
failf(data, "Failed to set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
hostname = snihost;
hostname = connssl->peer.sni;
CURL_TRC_CF(data, cf, "connect_step1, SNI set");
}

View File

@ -402,18 +402,13 @@ set_ssl_version_min_max(struct Curl_easy *data,
CURLcode gtls_client_init(struct Curl_easy *data,
struct ssl_primary_config *config,
struct ssl_config_data *ssl_config,
const char *hostname,
struct ssl_peer *peer,
struct gtls_instance *gtls,
long *pverifyresult)
{
unsigned int init_flags;
int rc;
bool sni = TRUE; /* default is SNI enabled */
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
const char *prioritylist;
const char *err = NULL;
const char *tls13support;
@ -547,15 +542,9 @@ CURLcode gtls_client_init(struct Curl_easy *data,
return CURLE_SSL_CONNECT_ERROR;
}
if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
#endif
sni) {
size_t snilen;
char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
if(!snihost || gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS,
snihost, snilen) < 0) {
if(sni && peer->sni) {
if(gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS,
peer->sni, strlen(peer->sni)) < 0) {
failf(data, "Failed to set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
@ -709,7 +698,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_OK;
result = gtls_client_init(data, conn_config, ssl_config,
connssl->hostname,
&connssl->peer,
&backend->gtls, pverifyresult);
if(result)
return result;
@ -821,8 +810,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
gnutls_session_t session,
struct ssl_primary_config *config,
struct ssl_config_data *ssl_config,
const char *hostname,
const char *dispname,
struct ssl_peer *peer,
const char *pinned_key)
{
unsigned int cert_list_size;
@ -1078,7 +1066,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
in RFC2818 (HTTPS), which takes into account wildcards, and the subject
alternative name PKIX extension. Returns non zero on success, and zero on
failure. */
rc = gnutls_x509_crt_check_hostname(x509_cert, hostname);
rc = gnutls_x509_crt_check_hostname(x509_cert, peer->hostname);
#if GNUTLS_VERSION_NUMBER < 0x030306
/* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP
addresses. */
@ -1091,10 +1079,10 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
unsigned char addrbuf[sizeof(struct use_addr)];
size_t addrlen = 0;
if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0)
if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
addrlen = 4;
#ifdef ENABLE_IPV6
else if(Curl_inet_pton(AF_INET6, hostname, addrbuf) > 0)
else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
addrlen = 16;
#endif
@ -1124,13 +1112,13 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
if(!rc) {
if(config->verifyhost) {
failf(data, "SSL: certificate subject name (%s) does not match "
"target host name '%s'", certname, dispname);
"target host name '%s'", certname, peer->dispname);
gnutls_x509_crt_deinit(x509_cert);
return CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, " common name: %s (does not match '%s')",
certname, dispname);
certname, peer->dispname);
}
else
infof(data, " common name: %s (matched)", certname);
@ -1263,8 +1251,7 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
CURLcode result;
result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config,
connssl->hostname, connssl->dispname,
pinned_key);
&connssl->peer, pinned_key);
if(result)
goto out;

View File

@ -43,6 +43,7 @@ struct Curl_easy;
struct Curl_cfilter;
struct ssl_primary_config;
struct ssl_config_data;
struct ssl_peer;
struct gtls_instance {
gnutls_session_t session;
@ -56,7 +57,7 @@ CURLcode
gtls_client_init(struct Curl_easy *data,
struct ssl_primary_config *config,
struct ssl_config_data *ssl_config,
const char *hostname,
struct ssl_peer *peer,
struct gtls_instance *gtls,
long *pverifyresult);
@ -65,8 +66,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
gnutls_session_t session,
struct ssl_primary_config *config,
struct ssl_config_data *ssl_config,
const char *hostname,
const char *dispname,
struct ssl_peer *peer,
const char *pinned_key);
extern const struct Curl_ssl Curl_ssl_gnutls;

View File

@ -322,7 +322,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
const char * const ssl_crlfile = ssl_config->primary.CRLfile;
const char *hostname = connssl->hostname;
const char *hostname = connssl->peer.hostname;
int ret = -1;
char errorbuf[128];
@ -639,9 +639,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
mbedtls_ssl_conf_own_cert(&backend->config,
&backend->clicert, &backend->pk);
}
{
char *snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) {
if(connssl->peer.sni) {
if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni)) {
/* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and
the name to set in the SNI extension. So even if curl connects to a
host specified as an IP address, this function must be used. */

View File

@ -2107,22 +2107,6 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
return FALSE;
}
static CURLcode
ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
X509 *server_cert, const char *hostname,
const char *dispname);
CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
X509 *server_cert)
{
const char *hostname, *dispname;
int port;
(void)conn;
Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
return ossl_verifyhost(data, conn, server_cert, hostname, dispname);
}
/* Quote from RFC2818 section 3.1 "Server Identity"
If a subjectAltName extension of type dNSName is present, that MUST
@ -2145,10 +2129,8 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
This function is now used from ngtcp2 (QUIC) as well.
*/
static CURLcode
ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
X509 *server_cert, const char *hostname,
const char *dispname)
CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
struct ssl_peer *peer, X509 *server_cert)
{
bool matched = FALSE;
int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
@ -2165,25 +2147,21 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
size_t hostlen;
(void)conn;
hostlen = strlen(hostname);
#ifndef ENABLE_IPV6
/* Silence compiler warnings for unused params */
(void) conn;
#endif
hostlen = strlen(peer->hostname);
if(peer->is_ip_address) {
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
Curl_inet_pton(AF_INET6, hostname, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in6_addr);
}
else
#endif
if(Curl_inet_pton(AF_INET, hostname, &addr)) {
if(conn->bits.ipv6_ip &&
Curl_inet_pton(AF_INET6, peer->hostname, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
addrlen = sizeof(struct in6_addr);
}
else
#endif
if(Curl_inet_pton(AF_INET, peer->hostname, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
}
}
/* get a "list" of alternative names */
altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
@ -2233,9 +2211,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
if((altlen == strlen(altptr)) &&
/* if this isn't true, there was an embedded zero in the name
string and we cannot match it. */
subj_alt_hostcheck(data,
altptr,
altlen, hostname, hostlen, dispname)) {
subj_alt_hostcheck(data, altptr, altlen,
peer->hostname, hostlen,
peer->dispname)) {
dnsmatched = TRUE;
}
break;
@ -2247,7 +2225,7 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
ipmatched = TRUE;
infof(data,
" subjectAltName: host \"%s\" matched cert's IP address!",
dispname);
peer->dispname);
}
break;
}
@ -2263,9 +2241,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
/* an alternative name matched */
;
else if(dNSName || iPAddress) {
infof(data, " subjectAltName does not match %s", dispname);
infof(data, " subjectAltName does not match %s", peer->dispname);
failf(data, "SSL: no alternative certificate subject name matches "
"target host name '%s'", dispname);
"target host name '%s'", peer->dispname);
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
@ -2329,9 +2307,9 @@ ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
result = CURLE_PEER_FAILED_VERIFICATION;
}
else if(!Curl_cert_hostcheck((const char *)peer_CN,
peerlen, hostname, hostlen)) {
peerlen, peer->hostname, hostlen)) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'", peer_CN, dispname);
"target host name '%s'", peer_CN, peer->dispname);
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
@ -2740,12 +2718,6 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
#ifdef USE_OPENSSL
/* ====================================================== */
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
# define use_sni(x) sni = (x)
#else
# define use_sni(x) Curl_nop_stmt
#endif
/* Check for OpenSSL 1.0.2 which has ALPN support. */
#undef HAS_ALPN
#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
@ -3490,17 +3462,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
BIO *bio;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
bool sni;
const char *hostname = connssl->hostname;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
#endif
const long int ssl_version = conn_config->version;
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
@ -3535,7 +3496,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
#else
req_method = SSLv23_client_method();
#endif
use_sni(TRUE);
break;
case CURL_SSLVERSION_SSLv2:
failf(data, "No SSLv2 support");
@ -3828,13 +3788,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
backend->server_cert = 0x0;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
#endif
sni) {
char *snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) {
if(connssl->peer.sni) {
if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) {
failf(data, "Failed set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
@ -4016,7 +3971,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
Curl_strerror(sockerr, extramsg, sizeof(extramsg));
failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
connssl->hostname, connssl->port);
connssl->peer.hostname, connssl->port);
return result;
}
@ -4257,8 +4212,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
BIO_free(mem);
if(conn_config->verifyhost) {
result = ossl_verifyhost(data, conn, backend->server_cert,
connssl->hostname, connssl->dispname);
result = Curl_ossl_verifyhost(data, conn, &connssl->peer,
backend->server_cert);
if(result) {
X509_free(backend->server_cert);
backend->server_cert = NULL;

View File

@ -43,6 +43,7 @@
*/
struct x509_st;
CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
struct ssl_peer *peer,
struct x509_st *server_cert);
extern const struct Curl_ssl Curl_ssl_openssl;
@ -65,5 +66,9 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
SSL_CTX *ssl_ctx);
CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
struct Curl_easy *data,
SSL_CTX *ssl_ctx);
#endif /* USE_OPENSSL */
#endif /* HEADER_CURL_SSLUSE_H */

View File

@ -386,7 +386,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
/* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
(ca_info_blob ? NULL : conn_config->CAfile);
const bool verifypeer = conn_config->verifypeer;
const char *hostname = connssl->hostname;
const char *hostname = connssl->peer.hostname;
char errorbuf[256];
size_t errorlen;
int result;
@ -458,12 +458,11 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
backend->config = rustls_client_config_builder_build(config_builder);
DEBUGASSERT(rconn == NULL);
{
char *snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost) {
failf(data, "rustls: failed to get SNI");
return CURLE_SSL_CONNECT_ERROR;
}
result = rustls_client_connection_new(backend->config, snihost, &rconn);
/* rustls claims to manage ip address hostnames as well here. So,
* if we have an SNI, we use it, otherwise we pass the hostname */
char *server = connssl->peer.sni?
connssl->peer.sni : connssl->peer.hostname;
result = rustls_client_connection_new(backend->config, server, &rconn);
}
if(result != RUSTLS_RESULT_OK) {
rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);

View File

@ -1063,12 +1063,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#endif
SECURITY_STATUS sspi_status = SEC_E_OK;
struct Curl_schannel_cred *old_cred = NULL;
struct in_addr addr;
#ifdef ENABLE_IPV6
struct in6_addr addr6;
#endif
CURLcode result;
const char *hostname = connssl->hostname;
const char *hostname = connssl->peer.hostname;
DEBUGASSERT(backend);
DEBUGF(infof(data,
@ -1154,22 +1150,14 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* A hostname associated with the credential is needed by
InitializeSecurityContext for SNI and other reasons. */
snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost) {
failf(data, "Failed to set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
snihost = connssl->peer.sni? connssl->peer.sni : connssl->peer.hostname;
backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost);
if(!backend->cred->sni_hostname)
return CURLE_OUT_OF_MEMORY;
}
/* Warn if SNI is disabled due to use of an IP address */
if(Curl_inet_pton(AF_INET, hostname, &addr)
#ifdef ENABLE_IPV6
|| Curl_inet_pton(AF_INET6, hostname, &addr6)
#endif
) {
if(connssl->peer.is_ip_address) {
infof(data, "schannel: using IP address, SNI is not supported by OS.");
}
@ -1346,7 +1334,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %d (step 2/3)",
connssl->hostname, connssl->port));
connssl->peer.hostname, connssl->port));
if(!backend->cred || !backend->ctxt)
return CURLE_SSL_CONNECT_ERROR;
@ -1700,7 +1688,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %d (step 3/3)",
connssl->hostname, connssl->port));
connssl->peer.hostname, connssl->port));
if(!backend->cred)
return CURLE_SSL_CONNECT_ERROR;
@ -2498,7 +2486,7 @@ static int schannel_shutdown(struct Curl_cfilter *cf,
if(backend->ctxt) {
infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
connssl->hostname, connssl->port);
connssl->peer.hostname, connssl->port);
}
if(backend->cred && backend->ctxt) {

View File

@ -470,7 +470,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
CERT_CONTEXT *pCertContextServer = NULL;
TCHAR *cert_hostname_buff = NULL;
size_t cert_hostname_buff_index = 0;
const char *conn_hostname = connssl->hostname;
const char *conn_hostname = connssl->peer.hostname;
size_t hostlen = strlen(conn_hostname);
DWORD len = 0;
DWORD actual_len = 0;

View File

@ -1652,11 +1652,6 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
const bool verifypeer = conn_config->verifypeer;
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif /* ENABLE_IPV6 */
char *ciphers;
OSStatus err = noErr;
#if CURL_BUILD_MAC
@ -2004,13 +1999,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
* Both hostname check and SNI require SSLSetPeerDomainName().
* Also: the verifyhost setting influences SNI usage */
if(conn_config->verifyhost) {
size_t snilen;
char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen);
if(!snihost) {
failf(data, "Failed to set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen);
char *server = connssl->peer.sni?
connssl->peer.sni : connssl->peer.hostname;
err = SSLSetPeerDomainName(backend->ssl_ctx, server, strlen(server));
if(err != noErr) {
failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d",
@ -2018,11 +2009,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
}
if((Curl_inet_pton(AF_INET, connssl->hostname, &addr))
#ifdef ENABLE_IPV6
|| (Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
#endif
) {
if(connssl->peer.is_ip_address) {
infof(data, "WARNING: using IP address, SNI is being disabled by "
"the OS.");
}
@ -2080,7 +2067,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
ssl_sessionid =
aprintf("%s:%d:%d:%s:%d",
ssl_cafile ? ssl_cafile : "(blob memory)",
verifypeer, conn_config->verifyhost, connssl->hostname,
verifypeer, conn_config->verifyhost, connssl->peer.hostname,
connssl->port);
ssl_sessionid_len = strlen(ssl_sessionid);
@ -2666,7 +2653,7 @@ check_handshake:
host name: */
case errSSLHostNameMismatch:
failf(data, "SSL certificate peer verification failed, the "
"certificate did not match \"%s\"\n", connssl->dispname);
"certificate did not match \"%s\"\n", connssl->peer.dispname);
return CURLE_PEER_FAILED_VERIFICATION;
/* Problem with SSL / TLS negotiation */
@ -2758,7 +2745,7 @@ check_handshake:
default:
/* May also return codes listed in Security Framework Result Codes */
failf(data, "Unknown SSL protocol error in connection to %s:%d",
connssl->hostname, err);
connssl->peer.hostname, err);
break;
}
return CURLE_SSL_CONNECT_ERROR;

View File

@ -67,6 +67,7 @@
#include "warnless.h"
#include "curl_base64.h"
#include "curl_printf.h"
#include "inet_pton.h"
#include "strdup.h"
/* The last #include files should be: */
@ -566,7 +567,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
if(!check->sessionid)
/* not session ID means blank entry */
continue;
if(strcasecompare(connssl->hostname, check->name) &&
if(strcasecompare(connssl->peer.hostname, check->name) &&
((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
(cf->conn->bits.conn_to_host && check->conn_to_host &&
strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
@ -590,7 +591,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
no_match? "Didn't find": "Found",
Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
cf->conn->handler->scheme, connssl->hostname, connssl->port));
cf->conn->handler->scheme, connssl->peer.hostname,
connssl->port));
return no_match;
}
@ -666,7 +668,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
(void)ssl_config;
DEBUGASSERT(ssl_config->primary.sessionid);
clone_host = strdup(connssl->hostname);
clone_host = strdup(connssl->peer.hostname);
if(!clone_host)
return CURLE_OUT_OF_MEMORY; /* bail out */
@ -918,32 +920,6 @@ CURLcode Curl_ssl_random(struct Curl_easy *data,
return Curl_ssl->random(data, entropy, length);
}
/*
* Curl_ssl_snihost() converts the input host name to a suitable SNI name put
* in data->state.buffer. Returns a pointer to the name (or NULL if a problem)
* and stores the new length in 'olen'.
*
* SNI fields must not have any trailing dot and while RFC 6066 section 3 says
* the SNI field is case insensitive, browsers always send the data lowercase
* and subsequently there are numerous servers out there that don't work
* unless the name is lowercased.
*/
char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen)
{
size_t len = strlen(host);
if(len && (host[len-1] == '.'))
len--;
if(len >= data->set.buffer_size)
return NULL;
Curl_strntolower(data->state.buffer, host, len);
data->state.buffer[len] = 0;
if(olen)
*olen = len;
return data->state.buffer;
}
/*
* Public key pem to der conversion
*/
@ -1542,12 +1518,14 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
#ifdef USE_SSL
static void free_hostname(struct ssl_connect_data *connssl)
void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
{
if(connssl->dispname != connssl->hostname)
free(connssl->dispname);
free(connssl->hostname);
connssl->hostname = connssl->dispname = NULL;
if(peer->dispname != peer->hostname)
free(peer->dispname);
free(peer->sni);
free(peer->hostname);
peer->hostname = peer->sni = peer->dispname = NULL;
peer->is_ip_address = FALSE;
}
static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@ -1556,12 +1534,26 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
if(connssl) {
Curl_ssl->close(cf, data);
connssl->state = ssl_connection_none;
free_hostname(connssl);
Curl_ssl_peer_cleanup(&connssl->peer);
}
cf->connected = FALSE;
}
static CURLcode reinit_hostname(struct Curl_cfilter *cf)
static int is_ip_address(const char *hostname)
{
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
return (hostname && hostname[0] && (Curl_inet_pton(AF_INET, hostname, &addr)
#ifdef ENABLE_IPV6
|| Curl_inet_pton(AF_INET6, hostname, &addr)
#endif
));
}
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf)
{
struct ssl_connect_data *connssl = cf->ctx;
const char *ehostname, *edispname;
@ -1587,23 +1579,43 @@ static CURLcode reinit_hostname(struct Curl_cfilter *cf)
}
/* change if ehostname changed */
if(ehostname && (!connssl->hostname
|| strcmp(ehostname, connssl->hostname))) {
free_hostname(connssl);
connssl->hostname = strdup(ehostname);
if(!connssl->hostname) {
free_hostname(connssl);
if(ehostname && (!peer->hostname
|| strcmp(ehostname, peer->hostname))) {
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))
connssl->dispname = connssl->hostname;
peer->dispname = peer->hostname;
else {
connssl->dispname = strdup(edispname);
if(!connssl->dispname) {
free_hostname(connssl);
peer->dispname = strdup(edispname);
if(!peer->dispname) {
Curl_ssl_peer_cleanup(peer);
return CURLE_OUT_OF_MEMORY;
}
}
peer->sni = NULL;
peer->is_ip_address = is_ip_address(peer->hostname)? TRUE : FALSE;
if(peer->hostname[0] && !peer->is_ip_address) {
/* 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;
}
}
}
connssl->port = eport;
return CURLE_OK;
@ -1658,7 +1670,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
goto out;
*done = FALSE;
result = reinit_hostname(cf);
result = Curl_ssl_peer_init(&connssl->peer, cf);
if(result)
goto out;

View File

@ -65,8 +65,6 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */
#endif
char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen);
curl_sslbackend Curl_ssl_backend(void);
/**
@ -106,6 +104,15 @@ bool Curl_ssl_conn_config_match(struct Curl_easy *data,
* `verifyhost` and `verifystatus`. */
void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
/**
* Init SSL peer information for filter. Can be called repeatedly.
*/
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf);
/**
* Free all allocated data and reset peer information.
*/
void Curl_ssl_peer_cleanup(struct ssl_peer *peer);
#ifdef USE_SSL
int Curl_ssl_init(void);
void Curl_ssl_cleanup(void);

View File

@ -68,8 +68,7 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
struct ssl_connect_data {
ssl_connection_state state;
ssl_connect_state connecting_state;
char *hostname; /* hostname for verification */
char *dispname; /* display version of hostname */
struct ssl_peer peer;
const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
void *backend; /* vtls backend specific props */
struct cf_call_data call_data; /* data handle used in current call */

View File

@ -609,24 +609,12 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
SSL_VERIFY_NONE, NULL);
#ifdef HAVE_SNI
if(sni) {
struct in_addr addr4;
#ifdef ENABLE_IPV6
struct in6_addr addr6;
#endif
size_t hostname_len = strlen(connssl->hostname);
if((hostname_len < USHRT_MAX) &&
!Curl_inet_pton(AF_INET, connssl->hostname, &addr4)
#ifdef ENABLE_IPV6
&& !Curl_inet_pton(AF_INET6, connssl->hostname, &addr6)
#endif
) {
size_t snilen;
char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen);
if(!snihost ||
wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost,
(unsigned short)snilen) != 1) {
if(sni && connssl->peer.sni) {
size_t sni_len = strlen(connssl->peer.sni);
if((sni_len < USHRT_MAX)) {
if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME,
connssl->peer.sni,
(unsigned short)sni_len) != 1) {
failf(data, "Failed to set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
@ -764,9 +752,9 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Enable RFC2818 checks */
if(conn_config->verifyhost) {
char *snihost = Curl_ssl_snihost(data, connssl->hostname, NULL);
if(!snihost ||
(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE))
char *snihost = connssl->peer.sni?
connssl->peer.sni : connssl->peer.hostname;
if(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)
return CURLE_SSL_CONNECT_ERROR;
}
@ -814,7 +802,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
else if(DOMAIN_NAME_MISMATCH == detail) {
#if 1
failf(data, " subject alt name(s) or common name do not match \"%s\"",
connssl->dispname);
connssl->peer.dispname);
return CURLE_PEER_FAILED_VERIFICATION;
#else
/* When the wolfssl_check_domain_name() is used and you desire to

View File

@ -1317,16 +1317,16 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
if(Curl_parseX509(&cert, beg, end))
return CURLE_PEER_FAILED_VERIFICATION;
hostlen = strlen(connssl->hostname);
hostlen = strlen(connssl->peer.hostname);
/* Get the server IP address. */
#ifdef ENABLE_IPV6
if(cf->conn->bits.ipv6_ip &&
Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
Curl_inet_pton(AF_INET6, connssl->peer.hostname, &addr))
addrlen = sizeof(struct in6_addr);
else
#endif
if(Curl_inet_pton(AF_INET, connssl->hostname, &addr))
if(Curl_inet_pton(AF_INET, connssl->peer.hostname, &addr))
addrlen = sizeof(struct in_addr);
/* Process extensions. */
@ -1361,7 +1361,7 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
name.beg, name.end);
if(len > 0 && (size_t)len == strlen(dnsname))
matched = Curl_cert_hostcheck(dnsname, (size_t)len,
connssl->hostname, hostlen);
connssl->peer.hostname, hostlen);
else
matched = 0;
free(dnsname);
@ -1421,7 +1421,7 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */
failf(data, "SSL: illegal cert name field");
else if(Curl_cert_hostcheck((const char *) dnsname,
len, connssl->hostname, hostlen)) {
len, connssl->peer.hostname, hostlen)) {
infof(data, " common name: %s (matched)", dnsname);
free(dnsname);
return CURLE_OK;