/* 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 */
struct cf_ngtcp2_ctx {
struct cf_quic_ctx q;
+ struct ssl_peer peer;
ngtcp2_path connected_path;
ngtcp2_conn *qconn;
ngtcp2_cid dcid;
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);
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;
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;
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;
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;
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");
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;
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)
struct cf_quiche_ctx {
struct cf_quic_ctx q;
+ struct ssl_peer peer;
quiche_conn *qconn;
quiche_config *cfg;
quiche_h3_conn *h3c;
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));
}
}
{
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)
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;
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;
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");
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");
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");
}
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;
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;
}
return CURLE_OK;
result = gtls_client_init(data, conn_config, ssl_config,
- connssl->hostname,
+ &connssl->peer,
&backend->gtls, pverifyresult);
if(result)
return result;
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;
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. */
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
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);
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;
struct Curl_cfilter;
struct ssl_primary_config;
struct ssl_config_data;
+struct ssl_peer;
struct gtls_instance {
gnutls_session_t session;
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);
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;
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];
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. */
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
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 */
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);
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;
ipmatched = TRUE;
infof(data,
" subjectAltName: host \"%s\" matched cert's IP address!",
- dispname);
+ peer->dispname);
}
break;
}
/* 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 {
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 {
#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 \
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;
#else
req_method = SSLv23_client_method();
#endif
- use_sni(TRUE);
break;
case CURL_SSLVERSION_SSLv2:
failf(data, "No SSLv2 support");
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;
}
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;
}
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;
*/
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;
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 */
/* 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;
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);
#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,
/* 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.");
}
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;
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;
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) {
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;
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
* 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",
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.");
}
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);
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 */
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;
#include "warnless.h"
#include "curl_base64.h"
#include "curl_printf.h"
+#include "inet_pton.h"
#include "strdup.h"
/* The last #include files should be: */
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))) &&
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;
}
(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 */
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
*/
#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)
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;
}
/* 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;
goto out;
*done = FALSE;
- result = reinit_hostname(cf);
+ result = Curl_ssl_peer_init(&connssl->peer, cf);
if(result)
goto out;
#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);
/**
* `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);
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 */
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;
}
/* 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;
}
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
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. */
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);
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;