aboutsummaryrefslogtreecommitdiff
path: root/lib/vtls
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vtls')
-rw-r--r--lib/vtls/bearssl.c86
-rw-r--r--lib/vtls/gtls.c131
-rw-r--r--lib/vtls/gtls.h6
-rw-r--r--lib/vtls/keylog.c9
-rw-r--r--lib/vtls/mbedtls.c41
-rw-r--r--lib/vtls/mbedtls_threadlock.c2
-rw-r--r--lib/vtls/openssl.c578
-rw-r--r--lib/vtls/openssl.h21
-rw-r--r--lib/vtls/rustls.c125
-rw-r--r--lib/vtls/schannel.c232
-rw-r--r--lib/vtls/schannel_int.h17
-rw-r--r--lib/vtls/schannel_verify.c68
-rw-r--r--lib/vtls/sectransp.c56
-rw-r--r--lib/vtls/vtls.c509
-rw-r--r--lib/vtls/vtls.h82
-rw-r--r--lib/vtls/vtls_int.h40
-rw-r--r--lib/vtls/wolfssl.c64
-rw-r--r--lib/vtls/x509asn1.c899
18 files changed, 1596 insertions, 1370 deletions
diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c
index 934149c1b..58394bab9 100644
--- a/lib/vtls/bearssl.c
+++ b/lib/vtls/bearssl.c
@@ -509,7 +509,6 @@ static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
{
uint16_t selected_ciphers[NUM_OF_CIPHERS];
size_t selected_count = 0;
- char cipher_name[CIPHER_NAME_BUF_LEN];
const char *cipher_start = ciphers;
const char *cipher_end;
size_t i, j;
@@ -518,41 +517,48 @@ static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
return CURLE_SSL_CIPHER;
while(true) {
+ const char *cipher;
+ size_t clen;
+
/* Extract the next cipher name from the ciphers string */
while(is_separator(*cipher_start))
++cipher_start;
- if(*cipher_start == '\0')
+ if(!*cipher_start)
break;
cipher_end = cipher_start;
- while(*cipher_end != '\0' && !is_separator(*cipher_end))
+ while(*cipher_end && !is_separator(*cipher_end))
++cipher_end;
- j = cipher_end - cipher_start < CIPHER_NAME_BUF_LEN - 1 ?
- cipher_end - cipher_start : CIPHER_NAME_BUF_LEN - 1;
- strncpy(cipher_name, cipher_start, j);
- cipher_name[j] = '\0';
+
+ clen = cipher_end - cipher_start;
+ cipher = cipher_start;
+
cipher_start = cipher_end;
/* Lookup the cipher name in the table of available ciphers. If the cipher
name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try
to match cipher name by an (OpenSSL) alias. */
- if(strncasecompare(cipher_name, "TLS_", 4)) {
+ if(strncasecompare(cipher, "TLS_", 4)) {
for(i = 0; i < NUM_OF_CIPHERS &&
- !strcasecompare(cipher_name, ciphertable[i].name); ++i);
+ (strlen(ciphertable[i].name) == clen) &&
+ !strncasecompare(cipher, ciphertable[i].name, clen); ++i);
}
else {
for(i = 0; i < NUM_OF_CIPHERS &&
- !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i);
+ (strlen(ciphertable[i].alias_name) == clen) &&
+ !strncasecompare(cipher, ciphertable[i].alias_name, clen); ++i);
}
if(i == NUM_OF_CIPHERS) {
- infof(data, "BearSSL: unknown cipher in list: %s", cipher_name);
+ infof(data, "BearSSL: unknown cipher in list: %.*s",
+ (int)clen, cipher);
continue;
}
/* No duplicates allowed */
for(j = 0; j < selected_count &&
- selected_ciphers[j] != ciphertable[i].num; j++);
+ selected_ciphers[j] != ciphertable[i].num; j++);
if(j < selected_count) {
- infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name);
+ infof(data, "BearSSL: duplicate cipher in list: %.*s",
+ (int)clen, cipher);
continue;
}
@@ -582,17 +588,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 +707,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 +716,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");
}
@@ -749,26 +745,26 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OK;
}
-static int bearssl_get_select_socks(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- curl_socket_t *socks)
+static void bearssl_adjust_pollset(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct easy_pollset *ps)
{
- struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
-
- if(sock == CURL_SOCKET_BAD)
- return GETSOCK_BLANK;
- else {
- struct bearssl_ssl_backend_data *backend =
- (struct bearssl_ssl_backend_data *)connssl->backend;
- unsigned state = br_ssl_engine_current_state(&backend->ctx.eng);
- if(state & BR_SSL_SENDREC) {
- socks[0] = sock;
- return GETSOCK_WRITESOCK(0);
+ if(!cf->connected) {
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+ if(sock != CURL_SOCKET_BAD) {
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct bearssl_ssl_backend_data *backend =
+ (struct bearssl_ssl_backend_data *)connssl->backend;
+ unsigned state = br_ssl_engine_current_state(&backend->ctx.eng);
+
+ if(state & BR_SSL_SENDREC) {
+ Curl_pollset_set_out_only(data, ps, sock);
+ }
+ else {
+ Curl_pollset_set_in_only(data, ps, sock);
+ }
}
}
- socks[0] = sock;
- return GETSOCK_READSOCK(0);
}
static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
@@ -1210,7 +1206,7 @@ const struct Curl_ssl Curl_ssl_bearssl = {
Curl_none_cert_status_request, /* cert_status_request */
bearssl_connect, /* connect */
bearssl_connect_nonblocking, /* connect_nonblocking */
- bearssl_get_select_socks, /* getsock */
+ bearssl_adjust_pollset, /* adjust_pollset */
bearssl_get_internals, /* get_internals */
bearssl_close, /* close_one */
Curl_none_close_all, /* close_all */
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index c538a966e..b95c5be3c 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -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;
@@ -460,50 +455,60 @@ CURLcode gtls_client_init(struct Curl_easy *data,
}
#endif
- if(config->CAfile) {
- /* set the trusted CA cert bundle file */
- gnutls_certificate_set_verify_flags(gtls->cred,
- GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+ if(config->verifypeer) {
+ bool imported_native_ca = false;
- rc = gnutls_certificate_set_x509_trust_file(gtls->cred,
- config->CAfile,
- GNUTLS_X509_FMT_PEM);
- if(rc < 0) {
- infof(data, "error reading ca cert file %s (%s)",
- config->CAfile, gnutls_strerror(rc));
- if(config->verifypeer) {
- *pverifyresult = rc;
- return CURLE_SSL_CACERT_BADFILE;
+ if(ssl_config->native_ca_store) {
+ rc = gnutls_certificate_set_x509_system_trust(gtls->cred);
+ if(rc < 0)
+ infof(data, "error reading native ca store (%s), continuing anyway",
+ gnutls_strerror(rc));
+ else {
+ infof(data, "found %d certificates in native ca store", rc);
+ if(rc > 0)
+ imported_native_ca = true;
}
}
- else
- infof(data, "found %d certificates in %s", rc, config->CAfile);
- }
- if(config->CApath) {
- /* set the trusted CA cert directory */
- rc = gnutls_certificate_set_x509_trust_dir(gtls->cred,
- config->CApath,
- GNUTLS_X509_FMT_PEM);
- if(rc < 0) {
- infof(data, "error reading ca cert file %s (%s)",
- config->CApath, gnutls_strerror(rc));
- if(config->verifypeer) {
- *pverifyresult = rc;
- return CURLE_SSL_CACERT_BADFILE;
+ if(config->CAfile) {
+ /* set the trusted CA cert bundle file */
+ gnutls_certificate_set_verify_flags(gtls->cred,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+
+ rc = gnutls_certificate_set_x509_trust_file(gtls->cred,
+ config->CAfile,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ infof(data, "error reading ca cert file %s (%s)%s",
+ config->CAfile, gnutls_strerror(rc),
+ (imported_native_ca ? ", continuing anyway" : ""));
+ if(!imported_native_ca) {
+ *pverifyresult = rc;
+ return CURLE_SSL_CACERT_BADFILE;
+ }
}
+ else
+ infof(data, "found %d certificates in %s", rc, config->CAfile);
}
- else
- infof(data, "found %d certificates in %s", rc, config->CApath);
- }
-#ifdef CURL_CA_FALLBACK
- /* use system ca certificate store as fallback */
- if(config->verifypeer && !(config->CAfile || config->CApath)) {
- /* this ignores errors on purpose */
- gnutls_certificate_set_x509_system_trust(gtls->cred);
+ if(config->CApath) {
+ /* set the trusted CA cert directory */
+ rc = gnutls_certificate_set_x509_trust_dir(gtls->cred,
+ config->CApath,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ infof(data, "error reading ca cert file %s (%s)%s",
+ config->CApath, gnutls_strerror(rc),
+ (imported_native_ca ? ", continuing anyway" : ""));
+ if(!imported_native_ca) {
+ *pverifyresult = rc;
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+ else
+ infof(data, "found %d certificates in %s", rc, config->CApath);
+ }
}
-#endif
if(config->CRLfile) {
/* set the CRL list file */
@@ -537,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;
}
@@ -585,13 +584,9 @@ CURLcode gtls_client_init(struct Curl_easy *data,
/* Only add SRP to the cipher list if SRP is requested. Otherwise
* GnuTLS will disable TLS 1.3 support. */
if(config->username) {
- size_t len = strlen(prioritylist);
-
- char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
+ char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist);
if(!prioritysrp)
return CURLE_OUT_OF_MEMORY;
- strcpy(prioritysrp, prioritylist);
- strcpy(prioritysrp + len, ":" GNUTLS_SRP);
rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
free(prioritysrp);
@@ -699,7 +694,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;
@@ -811,8 +806,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;
@@ -824,16 +818,17 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
char certname[65] = ""; /* limited to 64 chars by ASN.1 */
size_t size;
time_t certclock;
- const char *ptr;
int rc;
CURLcode result = CURLE_OK;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ const char *ptr;
unsigned int algo;
unsigned int bits;
gnutls_protocol_t version = gnutls_protocol_get_version(session);
#endif
long * const certverifyresult = &ssl_config->certverifyresult;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
/* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session),
gnutls_cipher_get(session),
@@ -841,6 +836,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
infof(data, "SSL connection using %s / %s",
gnutls_protocol_get_name(version), ptr);
+#endif
/* This function will return the peer's raw certificate (chain) as sent by
the peer. These certificates are in raw format (DER encoded for
@@ -1068,7 +1064,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. */
@@ -1081,10 +1077,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
@@ -1114,13 +1110,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);
@@ -1253,8 +1249,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;
@@ -1662,7 +1657,7 @@ const struct Curl_ssl Curl_ssl_gnutls = {
gtls_cert_status_request, /* cert_status_request */
gtls_connect, /* connect */
gtls_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_get_select_socks, /* getsock */
+ Curl_ssl_adjust_pollset, /* adjust_pollset */
gtls_get_internals, /* get_internals */
gtls_close, /* close_one */
Curl_none_close_all, /* close_all */
diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h
index ac141e1c6..1a81c01e9 100644
--- a/lib/vtls/gtls.h
+++ b/lib/vtls/gtls.h
@@ -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;
diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c
index d37bb183e..fbcb25cfb 100644
--- a/lib/vtls/keylog.c
+++ b/lib/vtls/keylog.c
@@ -23,6 +23,11 @@
***************************************************************************/
#include "curl_setup.h"
+#if defined(USE_OPENSSL) || \
+ defined(USE_WOLFSSL) || \
+ (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
+ defined(USE_QUICHE)
+
#include "keylog.h"
#include <curl/curl.h>
@@ -55,7 +60,7 @@ Curl_tls_keylog_open(void)
if(keylog_file_name) {
keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT);
if(keylog_file_fp) {
-#ifdef WIN32
+#ifdef _WIN32
if(setvbuf(keylog_file_fp, NULL, _IONBF, 0))
#else
if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096))
@@ -157,3 +162,5 @@ Curl_tls_keylog_write(const char *label,
fputs(line, keylog_file_fp);
return true;
}
+
+#endif /* TLS or QUIC backend */
diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
index 2f994d741..7d70de53b 100644
--- a/lib/vtls/mbedtls.c
+++ b/lib/vtls/mbedtls.c
@@ -36,6 +36,13 @@
/* Define this to enable lots of debugging for mbedTLS */
/* #define MBEDTLS_DEBUG */
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+/* mbedTLS (as of v3.5.1) has a duplicate function declaration
+ in its public headers. Disable the warning that detects it. */
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER >= 0x02040000
#include <mbedtls/net_sockets.h>
@@ -56,6 +63,10 @@
# endif
#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
#include "urldata.h"
#include "sendf.h"
#include "inet_pton.h"
@@ -67,6 +78,7 @@
#include "select.h"
#include "multiif.h"
#include "mbedtls_threadlock.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -153,7 +165,6 @@ static void mbed_debug(void *context, int level, const char *f_name,
infof(data, "%s", line);
(void) level;
}
-#else
#endif
static int mbedtls_bio_cf_write(void *bio,
@@ -165,6 +176,9 @@ static int mbedtls_bio_cf_write(void *bio,
CURLcode result;
DEBUGASSERT(data);
+ if(!data)
+ return 0;
+
nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
blen, nwritten, result);
@@ -182,6 +196,8 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen)
CURLcode result;
DEBUGASSERT(data);
+ if(!data)
+ return 0;
/* OpenSSL catches this case, so should we. */
if(!buf)
return 0;
@@ -322,7 +338,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];
@@ -367,11 +383,10 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
terminated even when provided the exact length, forcing us to waste
extra memory here. */
- unsigned char *newblob = malloc(ca_info_blob->len + 1);
+ unsigned char *newblob = Curl_memdup0(ca_info_blob->data,
+ ca_info_blob->len);
if(!newblob)
return CURLE_OUT_OF_MEMORY;
- memcpy(newblob, ca_info_blob->data, ca_info_blob->len);
- newblob[ca_info_blob->len] = 0; /* null terminate */
ret = mbedtls_x509_crt_parse(&backend->cacert, newblob,
ca_info_blob->len + 1);
free(newblob);
@@ -441,11 +456,10 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
terminated even when provided the exact length, forcing us to waste
extra memory here. */
- unsigned char *newblob = malloc(ssl_cert_blob->len + 1);
+ unsigned char *newblob = Curl_memdup0(ssl_cert_blob->data,
+ ssl_cert_blob->len);
if(!newblob)
return CURLE_OUT_OF_MEMORY;
- memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len);
- newblob[ssl_cert_blob->len] = 0; /* null terminate */
ret = mbedtls_x509_crt_parse(&backend->clicert, newblob,
ssl_cert_blob->len + 1);
free(newblob);
@@ -639,9 +653,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. */
@@ -1207,6 +1221,9 @@ static int mbedtls_init(void)
static void mbedtls_cleanup(void)
{
+#ifdef THREADING_SUPPORT
+ mbedtls_entropy_free(&ts_entropy);
+#endif /* THREADING_SUPPORT */
(void)Curl_mbedtlsthreadlock_thread_cleanup();
}
@@ -1274,7 +1291,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
Curl_none_cert_status_request, /* cert_status_request */
mbedtls_connect, /* connect */
mbedtls_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_get_select_socks, /* getsock */
+ Curl_ssl_adjust_pollset, /* adjust_pollset */
mbedtls_get_internals, /* get_internals */
mbedtls_close, /* close_one */
mbedtls_close_all, /* close_all */
diff --git a/lib/vtls/mbedtls_threadlock.c b/lib/vtls/mbedtls_threadlock.c
index bcb7106a6..22b1b221e 100644
--- a/lib/vtls/mbedtls_threadlock.c
+++ b/lib/vtls/mbedtls_threadlock.c
@@ -51,7 +51,7 @@ int Curl_mbedtlsthreadlock_thread_setup(void)
{
int i;
- mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1);
+ mutex_buf = calloc(1, NUMT * sizeof(MBEDTLS_MUTEX_T));
if(!mutex_buf)
return 0; /* error, no number of threads defined */
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 9f9c8d136..8d6087022 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -79,6 +79,8 @@
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/pkcs12.h>
+#include <openssl/tls1.h>
+#include <openssl/evp.h>
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
#include <openssl/ocsp.h>
@@ -96,6 +98,9 @@
#include "curl_memory.h"
#include "memdebug.h"
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
renegotiations when built with BoringSSL. Renegotiating is non-compliant
@@ -173,8 +178,6 @@
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
#define HAVE_EVP_PKEY_GET_PARAMS 1
-#else
-#define SSL_get1_peer_certificate SSL_get_peer_certificate
#endif
#ifdef HAVE_EVP_PKEY_GET_PARAMS
@@ -235,7 +238,11 @@
#elif defined(OPENSSL_IS_AWSLC)
#define OSSL_PACKAGE "AWS-LC"
#else
-#define OSSL_PACKAGE "OpenSSL"
+# if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
+# define OSSL_PACKAGE "quictls"
+# else
+# define OSSL_PACKAGE "OpenSSL"
+#endif
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
@@ -538,9 +545,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
#else
RSA_get0_key(rsa, &n, &e, NULL);
#endif /* HAVE_EVP_PKEY_GET_PARAMS */
- BIO_printf(mem, "%d", BN_num_bits(n));
+ BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0);
#else
- BIO_printf(mem, "%d", BN_num_bits(rsa->n));
+ BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0);
#endif /* HAVE_OPAQUE_RSA_DSA_DH */
push_certinfo("RSA Public Key", i);
print_pubkey_BN(rsa, n, i);
@@ -947,8 +954,9 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
#endif
if(!*buf) {
- strncpy(buf, (error ? "Unknown error" : "No error"), size);
- buf[size - 1] = '\0';
+ const char *msg = error ? "Unknown error" : "No error";
+ if(strlen(msg) < size)
+ strcpy(buf, msg);
}
return buf;
@@ -1080,6 +1088,7 @@ static int ssl_ui_reader(UI *ui, UI_STRING *uis)
UI_set_result(ui, uis, password);
return 1;
}
+ FALLTHROUGH();
default:
break;
}
@@ -1098,6 +1107,7 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis)
(UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
return 1;
}
+ FALLTHROUGH();
default:
break;
}
@@ -1515,7 +1525,7 @@ fail:
case SSL_FILETYPE_PEM:
if(cert_done)
break;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case SSL_FILETYPE_ASN1:
cert_use_result = key_blob ?
SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) :
@@ -1745,7 +1755,7 @@ static int ossl_init(void)
static void ossl_cleanup(void)
{
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- !defined(LIBRESSL_VERSION_NUMBER)
+ (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
/* OpenSSL 1.1 deprecates all these cleanup functions and
turns them into no-ops in OpenSSL 1.0 compatibility mode */
#else
@@ -2098,22 +2108,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
@@ -2136,10 +2130,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 */
@@ -2156,25 +2148,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);
@@ -2224,9 +2212,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;
@@ -2238,7 +2226,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;
}
@@ -2254,9 +2242,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 {
@@ -2320,9 +2308,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 {
@@ -2731,12 +2719,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 \
@@ -2869,7 +2851,7 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
return CURLE_NOT_BUILT_IN;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_2:
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
@@ -2877,7 +2859,7 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
return CURLE_NOT_BUILT_IN;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_1:
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1;
@@ -2885,7 +2867,7 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
return CURLE_NOT_BUILT_IN;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_0:
case CURL_SSLVERSION_TLSv1:
break;
@@ -2896,12 +2878,12 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_1:
#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_2;
#endif
- /* FALLTHROUGH */
+ FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_2:
#ifdef TLS1_3_VERSION
*ctx_options |= SSL_OP_NO_TLSv1_3;
@@ -3032,6 +3014,151 @@ static CURLcode load_cacert_from_memory(X509_STORE *store,
return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE;
}
+#if defined(USE_WIN32_CRYPTO)
+static CURLcode import_windows_cert_store(struct Curl_easy *data,
+ const char *name,
+ X509_STORE *store,
+ bool *imported)
+{
+ CURLcode result = CURLE_OK;
+ HCERTSTORE hStore;
+
+ *imported = false;
+
+ hStore = CertOpenSystemStoreA(0, name);
+ if(hStore) {
+ PCCERT_CONTEXT pContext = NULL;
+ /* The array of enhanced key usage OIDs will vary per certificate and
+ is declared outside of the loop so that rather than malloc/free each
+ iteration we can grow it with realloc, when necessary. */
+ CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+ DWORD enhkey_usage_size = 0;
+
+ /* This loop makes a best effort to import all valid certificates from
+ the MS root store. If a certificate cannot be imported it is
+ skipped. 'result' is used to store only hard-fail conditions (such
+ as out of memory) that cause an early break. */
+ result = CURLE_OK;
+ for(;;) {
+ X509 *x509;
+ FILETIME now;
+ BYTE key_usage[2];
+ DWORD req_size;
+ const unsigned char *encoded_cert;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char cert_name[256];
+#endif
+
+ pContext = CertEnumCertificatesInStore(hStore, pContext);
+ if(!pContext)
+ break;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+ NULL, cert_name, sizeof(cert_name))) {
+ strcpy(cert_name, "Unknown");
+ }
+ infof(data, "SSL: Checking cert \"%s\"", cert_name);
+#endif
+ encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+ if(!encoded_cert)
+ continue;
+
+ GetSystemTimeAsFileTime(&now);
+ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
+ continue;
+
+ /* If key usage exists check for signing attribute */
+ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+ pContext->pCertInfo,
+ key_usage, sizeof(key_usage))) {
+ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ continue;
+ }
+ else if(GetLastError())
+ continue;
+
+ /* If enhanced key usage exists check for server auth attribute.
+ *
+ * Note "In a Microsoft environment, a certificate might also have
+ * EKU extended properties that specify valid uses for the
+ * certificate." The call below checks both, and behavior varies
+ * depending on what is found. For more details see
+ * CertGetEnhancedKeyUsage doc.
+ */
+ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+ if(req_size && req_size > enhkey_usage_size) {
+ void *tmp = realloc(enhkey_usage, req_size);
+
+ if(!tmp) {
+ failf(data, "SSL: Out of memory allocating for OID list");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+ enhkey_usage_size = req_size;
+ }
+
+ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+ if(!enhkey_usage->cUsageIdentifier) {
+ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate
+ is good for all uses. If it returns zero, the certificate
+ has no valid uses." */
+ if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
+ continue;
+ }
+ else {
+ DWORD i;
+ bool found = false;
+
+ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+ enhkey_usage->rgpszUsageIdentifier[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ continue;
+ }
+ }
+ else
+ continue;
+ }
+ else
+ continue;
+
+ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if(!x509)
+ continue;
+
+ /* Try to import the certificate. This may fail for legitimate
+ reasons such as duplicate certificate, which is allowed by MS but
+ not OpenSSL. */
+ if(X509_STORE_add_cert(store, x509) == 1) {
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ infof(data, "SSL: Imported cert \"%s\"", cert_name);
+#endif
+ *imported = true;
+ }
+ X509_free(x509);
+ }
+
+ free(enhkey_usage);
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
+
+ if(result)
+ return result;
+ }
+
+ return result;
+}
+#endif
+
static CURLcode populate_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
X509_STORE *store)
@@ -3050,6 +3177,8 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
bool imported_native_ca = false;
bool imported_ca_info_blob = false;
+ CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ ssl_cafile? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
@@ -3061,140 +3190,25 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
https://datatracker.ietf.org/doc/html/rfc5280 */
if(ssl_config->native_ca_store) {
- HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
-
- if(hStore) {
- PCCERT_CONTEXT pContext = NULL;
- /* The array of enhanced key usage OIDs will vary per certificate and
- is declared outside of the loop so that rather than malloc/free each
- iteration we can grow it with realloc, when necessary. */
- CERT_ENHKEY_USAGE *enhkey_usage = NULL;
- DWORD enhkey_usage_size = 0;
-
- /* This loop makes a best effort to import all valid certificates from
- the MS root store. If a certificate cannot be imported it is
- skipped. 'result' is used to store only hard-fail conditions (such
- as out of memory) that cause an early break. */
- result = CURLE_OK;
- for(;;) {
- X509 *x509;
- FILETIME now;
- BYTE key_usage[2];
- DWORD req_size;
- const unsigned char *encoded_cert;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char cert_name[256];
-#endif
-
- pContext = CertEnumCertificatesInStore(hStore, pContext);
- if(!pContext)
- break;
-
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
- NULL, cert_name, sizeof(cert_name))) {
- strcpy(cert_name, "Unknown");
- }
- infof(data, "SSL: Checking cert \"%s\"", cert_name);
-#endif
- encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
- if(!encoded_cert)
- continue;
-
- GetSystemTimeAsFileTime(&now);
- if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
- CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
- continue;
-
- /* If key usage exists check for signing attribute */
- if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
- pContext->pCertInfo,
- key_usage, sizeof(key_usage))) {
- if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
- continue;
- }
- else if(GetLastError())
- continue;
-
- /* If enhanced key usage exists check for server auth attribute.
- *
- * Note "In a Microsoft environment, a certificate might also have
- * EKU extended properties that specify valid uses for the
- * certificate." The call below checks both, and behavior varies
- * depending on what is found. For more details see
- * CertGetEnhancedKeyUsage doc.
- */
- if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
- if(req_size && req_size > enhkey_usage_size) {
- void *tmp = realloc(enhkey_usage, req_size);
-
- if(!tmp) {
- failf(data, "SSL: Out of memory allocating for OID list");
- result = CURLE_OUT_OF_MEMORY;
- break;
- }
-
- enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
- enhkey_usage_size = req_size;
- }
-
- if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
- if(!enhkey_usage->cUsageIdentifier) {
- /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate
- is good for all uses. If it returns zero, the certificate
- has no valid uses." */
- if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
- continue;
- }
- else {
- DWORD i;
- bool found = false;
-
- for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
- if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
- enhkey_usage->rgpszUsageIdentifier[i])) {
- found = true;
- break;
- }
- }
-
- if(!found)
- continue;
- }
- }
- else
- continue;
- }
- else
- continue;
-
- x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
- if(!x509)
- continue;
-
- /* Try to import the certificate. This may fail for legitimate
- reasons such as duplicate certificate, which is allowed by MS but
- not OpenSSL. */
- if(X509_STORE_add_cert(store, x509) == 1) {
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- infof(data, "SSL: Imported cert \"%s\"", cert_name);
-#endif
- imported_native_ca = true;
- }
- X509_free(x509);
- }
-
- free(enhkey_usage);
- CertFreeCertificateContext(pContext);
- CertCloseStore(hStore, 0);
-
+ const char *storeNames[] = {
+ "ROOT", /* Trusted Root Certification Authorities */
+ "CA" /* Intermediate Certification Authorities */
+ };
+ size_t i;
+ for(i = 0; i < ARRAYSIZE(storeNames); ++i) {
+ bool imported = false;
+ result = import_windows_cert_store(data, storeNames[i], store,
+ &imported);
if(result)
return result;
+ if(imported) {
+ infof(data, "successfully imported Windows %s store", storeNames[i]);
+ imported_native_ca = true;
+ }
+ else
+ infof(data, "error importing Windows %s store, continuing anyway",
+ storeNames[i]);
}
- if(imported_native_ca)
- infof(data, "successfully imported Windows CA store");
- else
- infof(data, "error importing Windows CA store, continuing anyway");
}
#endif
if(ca_info_blob) {
@@ -3210,7 +3224,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
}
if(ssl_cafile || ssl_capath) {
-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
/* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) {
if(!imported_native_ca && !imported_ca_info_blob) {
@@ -3339,6 +3353,7 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
X509_STORE *store = NULL;
+ DEBUGASSERT(multi);
if(multi &&
multi->ssl_backend_data &&
multi->ssl_backend_data->store &&
@@ -3358,6 +3373,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
struct multi_ssl_backend_data *mbackend;
+ DEBUGASSERT(multi);
if(!multi)
return;
@@ -3449,17 +3465,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;
@@ -3494,7 +3499,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");
@@ -3787,13 +3791,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;
}
@@ -3802,6 +3801,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
SSL_set_app_data(backend->handle, cf);
+ connssl->reused_session = FALSE;
if(ssl_config->primary.sessionid) {
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
@@ -3815,6 +3815,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
}
/* Informational message */
infof(data, "SSL reusing session ID");
+ connssl->reused_session = TRUE;
}
Curl_ssl_sessionid_unlock(data);
}
@@ -3975,7 +3976,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;
}
@@ -3986,13 +3987,28 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
}
}
else {
+ int psigtype_nid = NID_undef;
+ const char *negotiated_group_name = NULL;
+
/* we connected fine, we're not waiting for anything else. */
connssl->connecting_state = ssl_connect_3;
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+ SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid);
+#if (OPENSSL_VERSION_NUMBER >= 0x30200000L)
+ negotiated_group_name = SSL_get0_group_name(backend->handle);
+#else
+ negotiated_group_name =
+ OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF);
+#endif
+#endif
+
/* Informational message */
- infof(data, "SSL connection using %s / %s",
+ infof(data, "SSL connection using %s / %s / %s / %s",
SSL_get_version(backend->handle),
- SSL_get_cipher(backend->handle));
+ SSL_get_cipher(backend->handle),
+ negotiated_group_name? negotiated_group_name : "[blank]",
+ OBJ_nid2sn(psigtype_nid));
#ifdef HAS_ALPN
/* Sets data and len to negotiated protocol, len is 0 if no protocol was
@@ -4069,6 +4085,75 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
return result;
}
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x3060000fL) && \
+ !defined(OPENSSL_IS_BORINGSSL) && \
+ !defined(OPENSSL_IS_AWSLC) && \
+ !defined(CURL_DISABLE_VERBOSE_STRINGS)
+static void infof_certstack(struct Curl_easy *data, const SSL *ssl)
+{
+ STACK_OF(X509) *certstack;
+ long verify_result;
+ int num_cert_levels;
+ int cert_level;
+
+ verify_result = SSL_get_verify_result(ssl);
+ if(verify_result != X509_V_OK)
+ certstack = SSL_get_peer_cert_chain(ssl);
+ else
+ certstack = SSL_get0_verified_chain(ssl);
+ num_cert_levels = sk_X509_num(certstack);
+
+ for(cert_level = 0; cert_level < num_cert_levels; cert_level++) {
+ char cert_algorithm[80] = "";
+ char group_name_final[80] = "";
+ const X509_ALGOR *palg_cert = NULL;
+ const ASN1_OBJECT *paobj_cert = NULL;
+ X509 *current_cert;
+ EVP_PKEY *current_pkey;
+ int key_bits;
+ int key_sec_bits;
+ int get_group_name;
+ const char *type_name;
+
+ current_cert = sk_X509_value(certstack, cert_level);
+
+ X509_get0_signature(NULL, &palg_cert, current_cert);
+ X509_ALGOR_get0(&paobj_cert, NULL, NULL, palg_cert);
+ OBJ_obj2txt(cert_algorithm, sizeof(cert_algorithm), paobj_cert, 0);
+
+ current_pkey = X509_get0_pubkey(current_cert);
+ key_bits = EVP_PKEY_bits(current_pkey);
+#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
+#define EVP_PKEY_get_security_bits EVP_PKEY_security_bits
+#endif
+ key_sec_bits = EVP_PKEY_get_security_bits(current_pkey);
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+ {
+ char group_name[80] = "";
+ get_group_name = EVP_PKEY_get_group_name(current_pkey, group_name,
+ sizeof(group_name), NULL);
+ msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name);
+ }
+ type_name = EVP_PKEY_get0_type_name(current_pkey);
+#else
+ get_group_name = 0;
+ type_name = NULL;
+#endif
+
+ infof(data,
+ " Certificate level %d: "
+ "Public key type %s%s (%d/%d Bits/secBits), signed using %s",
+ cert_level, type_name ? type_name : "?",
+ get_group_name == 0 ? "" : group_name_final,
+ key_bits, key_sec_bits, cert_algorithm);
+ }
+}
+#else
+#define infof_certstack(data, ssl)
+#endif
+
/*
* Get the server cert, verify it and show it, etc., only call failf() if the
* 'strict' argument is TRUE as otherwise all this is for informational
@@ -4147,8 +4232,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;
@@ -4258,11 +4343,28 @@ static CURLcode servercert(struct Curl_cfilter *cf,
infof(data, " SSL certificate verify ok.");
}
+ infof_certstack(data, backend->handle);
+
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
- if(conn_config->verifystatus) {
+ if(conn_config->verifystatus && !connssl->reused_session) {
+ /* don't do this after Session ID reuse */
result = verifystatus(cf, data);
if(result) {
+ /* when verifystatus failed, remove the session id from the cache again
+ if present */
+ if(!Curl_ssl_cf_is_proxy(cf)) {
+ void *old_ssl_sessionid = NULL;
+ bool incache;
+ Curl_ssl_sessionid_lock(data);
+ incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+ if(incache) {
+ infof(data, "Remove session ID again from cache");
+ Curl_ssl_delsessionid(data, old_ssl_sessionid);
+ }
+ Curl_ssl_sessionid_unlock(data);
+ }
+
X509_free(backend->server_cert);
backend->server_cert = NULL;
return result;
@@ -4509,10 +4611,10 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
else if(sockerr)
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
- else {
- strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
- error_buffer[sizeof(error_buffer) - 1] = '\0';
- }
+ else
+ msnprintf(error_buffer, sizeof(error_buffer), "%s",
+ SSL_ERROR_to_str(err));
+
failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
error_buffer, sockerr);
*curlcode = CURLE_SEND_ERROR;
@@ -4522,22 +4624,9 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
case SSL_ERROR_SSL: {
/* A failure in the SSL library occurred, usually a protocol error.
The OpenSSL error queue contains more information on the error. */
- struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
- struct ssl_connect_data *connssl_next = cf_ssl_next?
- cf_ssl_next->ctx : NULL;
sslerror = ERR_get_error();
- if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL &&
- ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET &&
- connssl->state == ssl_connection_complete &&
- (connssl_next && connssl_next->state == ssl_connection_complete)
- ) {
- char ver[120];
- (void)ossl_version(ver, sizeof(ver));
- failf(data, "Error: %s does not support double SSL tunneling.", ver);
- }
- else
- failf(data, "SSL_write() error: %s",
- ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
+ failf(data, "SSL_write() error: %s",
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
*curlcode = CURLE_SEND_ERROR;
rc = -1;
goto out;
@@ -4618,10 +4707,9 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
else if(sockerr && err == SSL_ERROR_SYSCALL)
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
- else {
- strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
- error_buffer[sizeof(error_buffer) - 1] = '\0';
- }
+ else
+ msnprintf(error_buffer, sizeof(error_buffer), "%s",
+ SSL_ERROR_to_str(err));
failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d",
error_buffer, sockerr);
*curlcode = CURLE_RECV_ERROR;
@@ -4842,7 +4930,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
ossl_cert_status_request, /* cert_status_request */
ossl_connect, /* connect */
ossl_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_get_select_socks,/* getsock */
+ Curl_ssl_adjust_pollset, /* adjust_pollset */
ossl_get_internals, /* get_internals */
ossl_close, /* close_one */
ossl_close_all, /* close_all */
diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h
index 950faab88..e802363a4 100644
--- a/lib/vtls/openssl.h
+++ b/lib/vtls/openssl.h
@@ -31,24 +31,21 @@
* This header should only be needed to get included by vtls.c, openssl.c
* and ngtcp2.c
*/
+#include <openssl/ossl_typ.h>
#include <openssl/ssl.h>
#include "urldata.h"
-/*
- * In an effort to avoid using 'X509 *' here, we instead use the struct
- * x509_st version of the type so that we can forward-declare it here without
- * having to include <openssl/x509v3.h>. Including that header causes name
- * conflicts when libcurl is built with both Schannel and OpenSSL support.
- */
-struct x509_st;
+#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
+#define SSL_get1_peer_certificate SSL_get_peer_certificate
+#endif
+
CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
- struct x509_st *server_cert);
+ struct ssl_peer *peer, X509 *server_cert);
extern const struct Curl_ssl Curl_ssl_openssl;
-struct ssl_ctx_st;
CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data,
- struct ssl_ctx_st *ctx, char *cert_file,
+ SSL_CTX *ctx, char *cert_file,
const struct curl_blob *cert_blob,
const char *cert_type, char *key_file,
const struct curl_blob *key_blob,
@@ -65,5 +62,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 */
diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c
index a3e9d964c..d58970910 100644
--- a/lib/vtls/rustls.c
+++ b/lib/vtls/rustls.c
@@ -39,6 +39,7 @@
#include "select.h"
#include "strerror.h"
#include "multiif.h"
+#include "connect.h" /* for the connect timeout */
struct rustls_ssl_backend_data
{
@@ -75,14 +76,6 @@ cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
return backend->data_pending;
}
-static CURLcode
-cr_connect(struct Curl_cfilter *cf UNUSED_PARAM,
- struct Curl_easy *data UNUSED_PARAM)
-{
- infof(data, "rustls_connect: unimplemented");
- return CURLE_SSL_CONNECT_ERROR;
-}
-
struct io_ctx {
struct Curl_cfilter *cf;
struct Curl_easy *data;
@@ -163,7 +156,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
size_t errorlen;
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
failf(data, "rustls_connection_process_new_packets: %.*s",
- errorlen, errorbuf);
+ (int)errorlen, errorbuf);
*err = map_error(rresult);
return -1;
}
@@ -232,7 +225,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char errorbuf[255];
size_t errorlen;
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_read: %.*s", errorlen, errorbuf);
+ failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
*err = CURLE_READ_ERROR;
nread = -1;
goto out;
@@ -308,7 +301,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
&plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_write: %.*s", errorlen, errorbuf);
+ failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
*err = CURLE_WRITE_ERROR;
return -1;
}
@@ -386,7 +379,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,16 +451,15 @@ 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);
- failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
+ failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
return CURLE_COULDNT_CONNECT;
}
rustls_connection_set_userdata(rconn, backend);
@@ -486,9 +478,20 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
Curl_alpn_set_negotiated(cf, data, protocol, len);
}
+/* Given an established network connection, do a TLS handshake.
+ *
+ * If `blocking` is true, this function will block until the handshake is
+ * complete. Otherwise it will return as soon as I/O would block.
+ *
+ * For the non-blocking I/O case, this function will set `*done` to true
+ * once the handshake is complete. This function never reads the value of
+ * `*done*`.
+ */
static CURLcode
-cr_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data, bool *done)
+cr_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking,
+ bool *done)
{
struct ssl_connect_data *const connssl = cf->ctx;
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
@@ -502,6 +505,8 @@ cr_connect_nonblocking(struct Curl_cfilter *cf,
bool wants_write;
curl_socket_t writefd;
curl_socket_t readfd;
+ timediff_t timeout_ms;
+ timediff_t socket_check_timeout;
DEBUGASSERT(backend);
@@ -539,12 +544,29 @@ cr_connect_nonblocking(struct Curl_cfilter *cf,
writefd = wants_write?sockfd:CURL_SOCKET_BAD;
readfd = wants_read?sockfd:CURL_SOCKET_BAD;
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0);
+ /* check allowed time left */
+ timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "rustls: operation timed out before socket check");
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ socket_check_timeout = blocking?timeout_ms:0;
+
+ what = Curl_socket_check(
+ readfd, CURL_SOCKET_BAD, writefd, socket_check_timeout);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
return CURLE_SSL_CONNECT_ERROR;
}
+ if(blocking && 0 == what) {
+ failf(data, "rustls connection timeout after %"
+ CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout);
+ return CURLE_OPERATION_TIMEDOUT;
+ }
if(0 == what) {
infof(data, "Curl_socket_check: %s would block",
wants_read&&wants_write ? "writing and reading" :
@@ -589,32 +611,43 @@ cr_connect_nonblocking(struct Curl_cfilter *cf,
DEBUGASSERT(false);
}
-/* returns a bitmap of flags for this connection's first socket indicating
- whether we want to read or write */
-static int
-cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
- curl_socket_t *socks)
+static CURLcode
+cr_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
{
- struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- struct rustls_ssl_backend_data *const backend =
- (struct rustls_ssl_backend_data *)connssl->backend;
- struct rustls_connection *rconn = NULL;
+ return cr_connect_common(cf, data, false, done);
+}
- (void)data;
- DEBUGASSERT(backend);
- rconn = backend->conn;
+static CURLcode
+cr_connect_blocking(struct Curl_cfilter *cf UNUSED_PARAM,
+ struct Curl_easy *data UNUSED_PARAM)
+{
+ bool done; /* unused */
+ return cr_connect_common(cf, data, true, &done);
+}
- if(rustls_connection_wants_write(rconn)) {
- socks[0] = sockfd;
- return GETSOCK_WRITESOCK(0);
- }
- if(rustls_connection_wants_read(rconn)) {
- socks[0] = sockfd;
- return GETSOCK_READSOCK(0);
+static void cr_adjust_pollset(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct easy_pollset *ps)
+{
+ if(!cf->connected) {
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+ struct ssl_connect_data *const connssl = cf->ctx;
+ struct rustls_ssl_backend_data *const backend =
+ (struct rustls_ssl_backend_data *)connssl->backend;
+ struct rustls_connection *rconn = NULL;
+
+ (void)data;
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ if(rustls_connection_wants_write(rconn)) {
+ Curl_pollset_add_out(data, ps, sock);
+ }
+ if(rustls_connection_wants_read(rconn)) {
+ Curl_pollset_add_in(data, ps, sock);
+ }
}
-
- return GETSOCK_BLANK;
}
static void *
@@ -675,9 +708,9 @@ const struct Curl_ssl Curl_ssl_rustls = {
cr_data_pending, /* data_pending */
Curl_none_random, /* random */
Curl_none_cert_status_request, /* cert_status_request */
- cr_connect, /* connect */
+ cr_connect_blocking, /* connect */
cr_connect_nonblocking, /* connect_nonblocking */
- cr_get_select_socks, /* get_select_socks */
+ cr_adjust_pollset, /* adjust_pollset */
cr_get_internals, /* get_internals */
cr_close, /* close_one */
Curl_none_close_all, /* close_all */
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index 410a5c4ec..45c337371 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -439,6 +439,12 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
return CURLE_OK;
}
#endif
+
+static bool algo(const char *check, char *namep, size_t nlen)
+{
+ return (strlen(check) == nlen) && !strncmp(check, namep, nlen);
+}
+
static CURLcode
schannel_acquire_credential_handle(struct Curl_cfilter *cf,
struct Curl_easy *data)
@@ -660,7 +666,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
cert_showfilename_error);
else
failf(data, "schannel: Failed to import cert file %s, "
- "last error is 0x%x",
+ "last error is 0x%lx",
cert_showfilename_error, errorcode);
return CURLE_SSL_CERTPROBLEM;
}
@@ -671,7 +677,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
if(!client_certs[0]) {
failf(data, "schannel: Failed to get certificate from file %s"
- ", last error is 0x%x",
+ ", last error is 0x%lx",
cert_showfilename_error, GetLastError());
CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM;
@@ -684,10 +690,15 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name,
cert_store_path);
if(!cert_store) {
- failf(data, "schannel: Failed to open cert store %x %s, "
- "last error is 0x%x",
- cert_store_name, cert_store_path, GetLastError());
+ char *path_utf8 =
+ curlx_convert_tchar_to_UTF8(cert_store_path);
+ failf(data, "schannel: Failed to open cert store %lx %s, "
+ "last error is 0x%lx",
+ cert_store_name,
+ (path_utf8 ? path_utf8 : "(unknown)"),
+ GetLastError());
free(cert_store_path);
+ curlx_unicodefree(path_utf8);
curlx_unicodefree(cert_path);
return CURLE_SSL_CERTPROBLEM;
}
@@ -790,9 +801,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
char *startCur = ciphers13;
int algCount = 0;
- char tmp[LONGEST_ALG_ID] = { 0 };
char *nameEnd;
- size_t n;
disable_aes_gcm_sha384 = TRUE;
disable_aes_gcm_sha256 = TRUE;
@@ -801,40 +810,34 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
disable_aes_ccm_sha256 = TRUE;
while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
+ size_t n;
+ char *namep;
nameEnd = strchr(startCur, ':');
n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
+ namep = startCur;
- /* reject too-long cipher names */
- if(n > (LONGEST_ALG_ID - 1)) {
- failf(data, "schannel: Cipher name too long, not checked");
- return CURLE_SSL_CIPHER;
- }
-
- strncpy(tmp, startCur, n);
- tmp[n] = 0;
-
- if(disable_aes_gcm_sha384
- && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) {
+ if(disable_aes_gcm_sha384 &&
+ algo("TLS_AES_256_GCM_SHA384", namep, n)) {
disable_aes_gcm_sha384 = FALSE;
}
else if(disable_aes_gcm_sha256
- && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) {
+ && algo("TLS_AES_128_GCM_SHA256", namep, n)) {
disable_aes_gcm_sha256 = FALSE;
}
else if(disable_chacha_poly
- && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) {
+ && algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) {
disable_chacha_poly = FALSE;
}
else if(disable_aes_ccm_8_sha256
- && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) {
+ && algo("TLS_AES_128_CCM_8_SHA256", namep, n)) {
disable_aes_ccm_8_sha256 = FALSE;
}
else if(disable_aes_ccm_sha256
- && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) {
+ && algo("TLS_AES_128_CCM_SHA256", namep, n)) {
disable_aes_ccm_sha256 = FALSE;
}
else {
- failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp);
+ failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep);
return CURLE_SSL_CIPHER;
}
@@ -1063,17 +1066,12 @@ 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;
DEBUGASSERT(backend);
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %d (step 1/3)",
- hostname, connssl->port));
+ connssl->peer.hostname, connssl->port));
if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT,
VERSION_LESS_THAN_EQUAL)) {
@@ -1154,22 +1152,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.");
}
@@ -1208,9 +1198,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
cur += proto.len;
*list_len = curlx_uitous(cur - list_start_index);
- *extension_len = *list_len +
- (unsigned short)sizeof(unsigned int) +
- (unsigned short)sizeof(unsigned short);
+ *extension_len = (unsigned int)(*list_len +
+ sizeof(unsigned int) + sizeof(unsigned short));
InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
@@ -1346,7 +1335,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 +1689,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;
@@ -2345,10 +2334,10 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
else {
#ifndef CURL_DISABLE_VERBOSE_STRINGS
char buffer[STRERROR_LEN];
-#endif
- *err = CURLE_RECV_ERROR;
infof(data, "schannel: failed to read data from server: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+#endif
+ *err = CURLE_RECV_ERROR;
goto cleanup;
}
}
@@ -2498,7 +2487,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) {
@@ -2754,6 +2743,151 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl,
return &backend->ctxt->ctxt_handle;
}
+HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ struct schannel_multi_ssl_backend_data *mbackend;
+ const struct ssl_general_config *cfg = &data->set.general_ssl;
+ timediff_t timeout_ms;
+ timediff_t elapsed_ms;
+ struct curltime now;
+ unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH];
+
+ DEBUGASSERT(multi);
+
+ if(!multi || !multi->ssl_backend_data) {
+ return NULL;
+ }
+
+ mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data;
+ if(!mbackend->cert_store) {
+ return NULL;
+ }
+
+ /* zero ca_cache_timeout completely disables caching */
+ if(!cfg->ca_cache_timeout) {
+ return NULL;
+ }
+
+ /* check for cache timeout by using the cached_x509_store_expired timediff
+ calculation pattern from openssl.c.
+ negative timeout means retain forever. */
+ timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
+ if(timeout_ms >= 0) {
+ now = Curl_now();
+ elapsed_ms = Curl_timediff(now, mbackend->time);
+ if(elapsed_ms >= timeout_ms) {
+ return NULL;
+ }
+ }
+
+ if(ca_info_blob) {
+ if(!mbackend->CAinfo_blob_digest) {
+ return NULL;
+ }
+ if(mbackend->CAinfo_blob_size != ca_info_blob->len) {
+ return NULL;
+ }
+ schannel_sha256sum((const unsigned char *)ca_info_blob->data,
+ ca_info_blob->len,
+ info_blob_digest,
+ CURL_SHA256_DIGEST_LENGTH);
+ if(memcmp(mbackend->CAinfo_blob_digest,
+ info_blob_digest,
+ CURL_SHA256_DIGEST_LENGTH)) {
+ return NULL;
+ }
+ }
+ else {
+ if(!conn_config->CAfile || !mbackend->CAfile ||
+ strcmp(mbackend->CAfile, conn_config->CAfile)) {
+ return NULL;
+ }
+ }
+
+ return mbackend->cert_store;
+}
+
+bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ HCERTSTORE cert_store)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ struct schannel_multi_ssl_backend_data *mbackend;
+ unsigned char *CAinfo_blob_digest = NULL;
+ size_t CAinfo_blob_size = 0;
+ char *CAfile = NULL;
+
+ DEBUGASSERT(multi);
+
+ if(!multi) {
+ return false;
+ }
+
+ if(!multi->ssl_backend_data) {
+ multi->ssl_backend_data =
+ calloc(1, sizeof(struct schannel_multi_ssl_backend_data));
+ if(!multi->ssl_backend_data) {
+ return false;
+ }
+ }
+
+ mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data;
+
+
+ if(ca_info_blob) {
+ CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH);
+ if(!CAinfo_blob_digest) {
+ return false;
+ }
+ schannel_sha256sum((const unsigned char *)ca_info_blob->data,
+ ca_info_blob->len,
+ CAinfo_blob_digest,
+ CURL_SHA256_DIGEST_LENGTH);
+ CAinfo_blob_size = ca_info_blob->len;
+ }
+ else {
+ if(conn_config->CAfile) {
+ CAfile = strdup(conn_config->CAfile);
+ if(!CAfile) {
+ return false;
+ }
+ }
+ }
+
+ /* free old cache data */
+ if(mbackend->cert_store) {
+ CertCloseStore(mbackend->cert_store, 0);
+ }
+ free(mbackend->CAinfo_blob_digest);
+ free(mbackend->CAfile);
+
+ mbackend->time = Curl_now();
+ mbackend->cert_store = cert_store;
+ mbackend->CAinfo_blob_digest = CAinfo_blob_digest;
+ mbackend->CAinfo_blob_size = CAinfo_blob_size;
+ mbackend->CAfile = CAfile;
+ return true;
+}
+
+static void schannel_free_multi_ssl_backend_data(
+ struct multi_ssl_backend_data *msbd)
+{
+ struct schannel_multi_ssl_backend_data *mbackend =
+ (struct schannel_multi_ssl_backend_data*)msbd;
+ if(mbackend->cert_store) {
+ CertCloseStore(mbackend->cert_store, 0);
+ }
+ free(mbackend->CAinfo_blob_digest);
+ free(mbackend->CAfile);
+ free(mbackend);
+}
+
const struct Curl_ssl Curl_ssl_schannel = {
{ CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
@@ -2777,7 +2911,7 @@ const struct Curl_ssl Curl_ssl_schannel = {
Curl_none_cert_status_request, /* cert_status_request */
schannel_connect, /* connect */
schannel_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_get_select_socks, /* getsock */
+ Curl_ssl_adjust_pollset, /* adjust_pollset */
schannel_get_internals, /* get_internals */
schannel_close, /* close_one */
Curl_none_close_all, /* close_all */
@@ -2789,7 +2923,7 @@ const struct Curl_ssl Curl_ssl_schannel = {
schannel_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
+ schannel_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
schannel_recv, /* recv decrypted data */
schannel_send, /* send data to encrypt */
};
diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h
index a128e04f6..fe7450d45 100644
--- a/lib/vtls/schannel_int.h
+++ b/lib/vtls/schannel_int.h
@@ -149,5 +149,22 @@ struct schannel_ssl_backend_data {
#endif
};
+struct schannel_multi_ssl_backend_data {
+ unsigned char *CAinfo_blob_digest; /* CA info blob digest */
+ size_t CAinfo_blob_size; /* CA info blob size */
+ char *CAfile; /* CAfile path used to generate
+ certificate store */
+ HCERTSTORE cert_store; /* cached certificate store or
+ NULL if none */
+ struct curltime time; /* when the cached store was created */
+};
+
+HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data);
+
+bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ HCERTSTORE cert_store);
+
#endif /* USE_SCHANNEL */
#endif /* HEADER_CURL_SCHANNEL_INT_H */
diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c
index a5d5c98bb..24146d0bd 100644
--- a/lib/vtls/schannel_verify.c
+++ b/lib/vtls/schannel_verify.c
@@ -172,7 +172,7 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
/* Sanity check that the cert_context object is the right type */
if(CERT_QUERY_CONTENT_CERT != actual_content_type) {
failf(data,
- "schannel: unexpected content type '%d' when extracting "
+ "schannel: unexpected content type '%lu' when extracting "
"certificate from CA file '%s'",
actual_content_type, ca_file_text);
result = CURLE_SSL_CACERT_BADFILE;
@@ -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;
@@ -600,6 +600,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
const CERT_CHAIN_CONTEXT *pChainContext = NULL;
HCERTCHAINENGINE cert_chain_engine = NULL;
HCERTSTORE trust_store = NULL;
+ HCERTSTORE own_trust_store = NULL;
DEBUGASSERT(BACKEND);
@@ -630,31 +631,46 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
result = CURLE_SSL_CACERT_BADFILE;
}
else {
- /* Open the certificate store */
- trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
- 0,
- (HCRYPTPROV)NULL,
- CERT_STORE_CREATE_NEW_FLAG,
- NULL);
- if(!trust_store) {
- char buffer[STRERROR_LEN];
- failf(data, "schannel: failed to create certificate store: %s",
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
- result = CURLE_SSL_CACERT_BADFILE;
+ /* try cache */
+ trust_store = Curl_schannel_get_cached_cert_store(cf, data);
+
+ if(trust_store) {
+ infof(data, "schannel: reusing certificate store from cache");
}
else {
- const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
- if(ca_info_blob) {
- result = add_certs_data_to_store(trust_store,
- (const char *)ca_info_blob->data,
- ca_info_blob->len,
- "(memory blob)",
- data);
+ /* Open the certificate store */
+ trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
+ 0,
+ (HCRYPTPROV)NULL,
+ CERT_STORE_CREATE_NEW_FLAG,
+ NULL);
+ if(!trust_store) {
+ char buffer[STRERROR_LEN];
+ failf(data, "schannel: failed to create certificate store: %s",
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ result = CURLE_SSL_CACERT_BADFILE;
}
else {
- result = add_certs_file_to_store(trust_store,
- conn_config->CAfile,
- data);
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ own_trust_store = trust_store;
+
+ if(ca_info_blob) {
+ result = add_certs_data_to_store(trust_store,
+ (const char *)ca_info_blob->data,
+ ca_info_blob->len,
+ "(memory blob)",
+ data);
+ }
+ else {
+ result = add_certs_file_to_store(trust_store,
+ conn_config->CAfile,
+ data);
+ }
+ if(result == CURLE_OK) {
+ if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) {
+ own_trust_store = NULL;
+ }
+ }
}
}
}
@@ -737,7 +753,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
failf(data, "schannel: CertGetCertificateChain trust error"
" CERT_TRUST_REVOCATION_STATUS_UNKNOWN");
else
- failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+ failf(data, "schannel: CertGetCertificateChain error mask: 0x%08lx",
dwTrustErrorMask);
result = CURLE_PEER_FAILED_VERIFICATION;
}
@@ -754,8 +770,8 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CertFreeCertificateChainEngine(cert_chain_engine);
}
- if(trust_store) {
- CertCloseStore(trust_store, 0);
+ if(own_trust_store) {
+ CertCloseStore(own_trust_store, 0);
}
if(pChainContext)
diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c
index 3378f7619..1f37305ce 100644
--- a/lib/vtls/sectransp.c
+++ b/lib/vtls/sectransp.c
@@ -46,8 +46,10 @@
#endif /* __clang__ */
#ifdef __GNUC__
+#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Waddress"
#pragma GCC diagnostic ignored "-Wundef"
+#pragma GCC diagnostic ignored "-Wunreachable-code"
#endif
#include <limits.h>
@@ -904,7 +906,6 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
return rtn;
}
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
{
/* The first ciphers in the ciphertable are continuous. Here we do small
@@ -923,7 +924,6 @@ CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
}
return ciphertable[SSL_NULL_WITH_NULL_NULL].name;
}
-#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
#if CURL_BUILD_MAC
CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
@@ -1013,7 +1013,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data,
}
else {
size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1;
- cbuf = calloc(cbuf_size, 1);
+ cbuf = calloc(1, cbuf_size);
if(cbuf) {
if(!CFStringGetCString(c, cbuf, cbuf_size,
kCFStringEncodingUTF8)) {
@@ -1651,11 +1651,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
@@ -2003,13 +1998,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",
@@ -2017,11 +2008,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.");
}
@@ -2079,7 +2066,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);
@@ -2380,19 +2367,15 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
const struct curl_blob *ca_info_blob,
SSLContextRef ctx)
{
- int result;
+ CURLcode result;
unsigned char *certbuf;
size_t buflen;
+ bool free_certbuf = FALSE;
if(ca_info_blob) {
CURL_TRC_CF(data, cf, "verify_peer, CA from config blob");
- certbuf = (unsigned char *)malloc(ca_info_blob->len + 1);
- if(!certbuf) {
- return CURLE_OUT_OF_MEMORY;
- }
+ certbuf = ca_info_blob->data;
buflen = ca_info_blob->len;
- memcpy(certbuf, ca_info_blob->data, ca_info_blob->len);
- certbuf[ca_info_blob->len]='\0';
}
else if(cafile) {
CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile);
@@ -2400,12 +2383,14 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
failf(data, "SSL: failed to read or invalid CA certificate");
return CURLE_SSL_CACERT_BADFILE;
}
+ free_certbuf = TRUE;
}
else
return CURLE_SSL_CACERT_BADFILE;
result = verify_cert_buf(cf, data, certbuf, buflen, ctx);
- free(certbuf);
+ if(free_certbuf)
+ free(certbuf);
return result;
}
@@ -2665,7 +2650,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 */
@@ -2757,7 +2742,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;
@@ -3415,7 +3400,6 @@ again:
}
*curlcode = CURLE_AGAIN;
return -1L;
- break;
/* errSSLClosedGraceful - server gracefully shut down the SSL session
errSSLClosedNoNotify - server hung up on us instead of sending a
@@ -3425,7 +3409,6 @@ again:
case errSSLClosedNoNotify:
*curlcode = CURLE_OK;
return 0;
- break;
/* The below is errSSLPeerAuthCompleted; it's not defined in
Leopard's headers */
@@ -3445,7 +3428,6 @@ again:
failf(data, "SSLRead() return error %d", err);
*curlcode = CURLE_RECV_ERROR;
return -1L;
- break;
}
}
return (ssize_t)processed;
@@ -3483,7 +3465,7 @@ const struct Curl_ssl Curl_ssl_sectransp = {
Curl_none_cert_status_request, /* cert_status_request */
sectransp_connect, /* connect */
sectransp_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_get_select_socks, /* getsock */
+ Curl_ssl_adjust_pollset, /* adjust_pollset */
sectransp_get_internals, /* get_internals */
sectransp_close, /* close_one */
Curl_none_close_all, /* close_all */
@@ -3500,6 +3482,10 @@ const struct Curl_ssl Curl_ssl_sectransp = {
sectransp_send, /* send data to encrypt */
};
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
#ifdef __clang__
#pragma clang diagnostic pop
#endif
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index 494b660a9..e928ba5d0 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -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: */
@@ -131,9 +132,6 @@ static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
}
#ifdef USE_SSL
-static const struct alpn_spec ALPN_SPEC_H10 = {
- { ALPN_HTTP_1_0 }, 1
-};
static const struct alpn_spec ALPN_SPEC_H11 = {
{ ALPN_HTTP_1_1 }, 1
};
@@ -147,51 +145,83 @@ static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn)
{
if(!use_alpn)
return NULL;
- if(httpwant == CURL_HTTP_VERSION_1_0)
- return &ALPN_SPEC_H10;
#ifdef USE_HTTP2
if(httpwant >= CURL_HTTP_VERSION_2)
return &ALPN_SPEC_H2_H11;
+#else
+ (void)httpwant;
#endif
+ /* Use the ALPN protocol "http/1.1" for HTTP/1.x.
+ Avoid "http/1.0" because some servers don't support it. */
return &ALPN_SPEC_H11;
}
#endif /* USE_SSL */
-bool
-Curl_ssl_config_matches(struct ssl_primary_config *data,
- struct ssl_primary_config *needle)
-{
- if((data->version == needle->version) &&
- (data->version_max == needle->version_max) &&
- (data->ssl_options == needle->ssl_options) &&
- (data->verifypeer == needle->verifypeer) &&
- (data->verifyhost == needle->verifyhost) &&
- (data->verifystatus == needle->verifystatus) &&
- blobcmp(data->cert_blob, needle->cert_blob) &&
- blobcmp(data->ca_info_blob, needle->ca_info_blob) &&
- blobcmp(data->issuercert_blob, needle->issuercert_blob) &&
- Curl_safecmp(data->CApath, needle->CApath) &&
- Curl_safecmp(data->CAfile, needle->CAfile) &&
- Curl_safecmp(data->issuercert, needle->issuercert) &&
- Curl_safecmp(data->clientcert, needle->clientcert) &&
+void Curl_ssl_easy_config_init(struct Curl_easy *data)
+{
+ /*
+ * libcurl 7.10 introduced SSL verification *by default*! This needs to be
+ * switched off unless wanted.
+ */
+ data->set.ssl.primary.verifypeer = TRUE;
+ data->set.ssl.primary.verifyhost = TRUE;
+ data->set.ssl.primary.sessionid = TRUE; /* session ID caching by default */
+#ifndef CURL_DISABLE_PROXY
+ data->set.proxy_ssl = data->set.ssl;
+#endif
+}
+
+static bool
+match_ssl_primary_config(struct Curl_easy *data,
+ struct ssl_primary_config *c1,
+ struct ssl_primary_config *c2)
+{
+ (void)data;
+ if((c1->version == c2->version) &&
+ (c1->version_max == c2->version_max) &&
+ (c1->ssl_options == c2->ssl_options) &&
+ (c1->verifypeer == c2->verifypeer) &&
+ (c1->verifyhost == c2->verifyhost) &&
+ (c1->verifystatus == c2->verifystatus) &&
+ blobcmp(c1->cert_blob, c2->cert_blob) &&
+ blobcmp(c1->ca_info_blob, c2->ca_info_blob) &&
+ blobcmp(c1->issuercert_blob, c2->issuercert_blob) &&
+ Curl_safecmp(c1->CApath, c2->CApath) &&
+ Curl_safecmp(c1->CAfile, c2->CAfile) &&
+ Curl_safecmp(c1->issuercert, c2->issuercert) &&
+ Curl_safecmp(c1->clientcert, c2->clientcert) &&
#ifdef USE_TLS_SRP
- !Curl_timestrcmp(data->username, needle->username) &&
- !Curl_timestrcmp(data->password, needle->password) &&
+ !Curl_timestrcmp(c1->username, c2->username) &&
+ !Curl_timestrcmp(c1->password, c2->password) &&
#endif
- strcasecompare(data->cipher_list, needle->cipher_list) &&
- strcasecompare(data->cipher_list13, needle->cipher_list13) &&
- strcasecompare(data->curves, needle->curves) &&
- strcasecompare(data->CRLfile, needle->CRLfile) &&
- strcasecompare(data->pinned_key, needle->pinned_key))
+ strcasecompare(c1->cipher_list, c2->cipher_list) &&
+ strcasecompare(c1->cipher_list13, c2->cipher_list13) &&
+ strcasecompare(c1->curves, c2->curves) &&
+ strcasecompare(c1->CRLfile, c2->CRLfile) &&
+ strcasecompare(c1->pinned_key, c2->pinned_key))
return TRUE;
return FALSE;
}
-bool
-Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
- struct ssl_primary_config *dest)
+bool Curl_ssl_conn_config_match(struct Curl_easy *data,
+ struct connectdata *candidate,
+ bool proxy)
+{
+#ifndef CURL_DISABLE_PROXY
+ if(proxy)
+ return match_ssl_primary_config(data, &data->set.proxy_ssl.primary,
+ &candidate->proxy_ssl_config);
+#else
+ (void)proxy;
+#endif
+ return match_ssl_primary_config(data, &data->set.ssl.primary,
+ &candidate->ssl_config);
+}
+
+static bool clone_ssl_primary_config(struct ssl_primary_config *source,
+ struct ssl_primary_config *dest)
{
dest->version = source->version;
dest->version_max = source->version_max;
@@ -221,7 +251,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
return TRUE;
}
-void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
+static void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
{
Curl_safefree(sslc->CApath);
Curl_safefree(sslc->CAfile);
@@ -241,6 +271,111 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
#endif
}
+CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data)
+{
+ data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH];
+ data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
+ data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE];
+ data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
+ data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
+ data->set.ssl.primary.cipher_list =
+ data->set.str[STRING_SSL_CIPHER_LIST];
+ data->set.ssl.primary.cipher_list13 =
+ data->set.str[STRING_SSL_CIPHER13_LIST];
+ data->set.ssl.primary.pinned_key =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT];
+ data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO];
+ data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES];
+#ifdef USE_TLS_SRP
+ data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME];
+ data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD];
+#endif
+ data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE];
+ data->set.ssl.key = data->set.str[STRING_KEY];
+ data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE];
+ data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD];
+ data->set.ssl.primary.clientcert = data->set.str[STRING_CERT];
+ data->set.ssl.key_blob = data->set.blobs[BLOB_KEY];
+
+#ifndef CURL_DISABLE_PROXY
+ data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
+ data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
+ data->set.proxy_ssl.primary.cipher_list =
+ data->set.str[STRING_SSL_CIPHER_LIST_PROXY];
+ data->set.proxy_ssl.primary.cipher_list13 =
+ data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
+ data->set.proxy_ssl.primary.pinned_key =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
+ data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
+ data->set.proxy_ssl.primary.ca_info_blob =
+ data->set.blobs[BLOB_CAINFO_PROXY];
+ data->set.proxy_ssl.primary.issuercert =
+ data->set.str[STRING_SSL_ISSUERCERT_PROXY];
+ data->set.proxy_ssl.primary.issuercert_blob =
+ data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
+ data->set.proxy_ssl.primary.CRLfile =
+ data->set.str[STRING_SSL_CRLFILE_PROXY];
+ data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
+ data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY];
+ data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
+ data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
+ data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY];
+ data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY];
+#ifdef USE_TLS_SRP
+ data->set.proxy_ssl.primary.username =
+ data->set.str[STRING_TLSAUTH_USERNAME_PROXY];
+ data->set.proxy_ssl.primary.password =
+ data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
+#endif
+#endif /* CURL_DISABLE_PROXY */
+
+ return CURLE_OK;
+}
+
+CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ /* Clone "primary" SSL configurations from the esay handle to
+ * the connection. They are used for connection cache matching and
+ * probably outlive the easy handle */
+ if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config))
+ return CURLE_OUT_OF_MEMORY;
+#ifndef CURL_DISABLE_PROXY
+ if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary,
+ &conn->proxy_ssl_config))
+ return CURLE_OUT_OF_MEMORY;
+#endif
+ return CURLE_OK;
+}
+
+void Curl_ssl_conn_config_cleanup(struct connectdata *conn)
+{
+ Curl_free_primary_ssl_config(&conn->ssl_config);
+#ifndef CURL_DISABLE_PROXY
+ Curl_free_primary_ssl_config(&conn->proxy_ssl_config);
+#endif
+}
+
+void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy)
+{
+ /* May be called on an easy that has no connection yet */
+ if(data->conn) {
+ struct ssl_primary_config *src, *dest;
+#ifndef CURL_DISABLE_PROXY
+ src = for_proxy? &data->set.proxy_ssl.primary : &data->set.ssl.primary;
+ dest = for_proxy? &data->conn->proxy_ssl_config : &data->conn->ssl_config;
+#else
+ (void)for_proxy;
+ src = &data->set.ssl.primary;
+ dest = &data->conn->ssl_config;
+#endif
+ dest->verifyhost = src->verifyhost;
+ dest->verifypeer = src->verifypeer;
+ dest->verifystatus = src->verifystatus;
+ }
+}
+
#ifdef USE_SSL
static int multissl_setup(const struct Curl_ssl *backend);
#endif
@@ -432,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))) &&
@@ -441,7 +576,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
cf->conn->conn_to_port == check->conn_to_port)) &&
(connssl->port == check->remote_port) &&
strcasecompare(cf->conn->handler->scheme, check->scheme) &&
- Curl_ssl_config_matches(conn_config, &check->ssl_config)) {
+ match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
/* yes, we have a session ID! */
(*general_age)++; /* increase general age */
check->age = *general_age; /* set this as used in this age */
@@ -456,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;
}
@@ -532,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 */
@@ -590,7 +726,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
store->remote_port = connssl->port;
store->scheme = cf->conn->handler->scheme;
- if(!Curl_clone_primary_ssl_config(conn_config, &store->ssl_config)) {
+ if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
Curl_free_primary_ssl_config(&store->ssl_config);
store->sessionid = NULL; /* let caller free sessionid */
free(clone_host);
@@ -629,22 +765,21 @@ void Curl_ssl_close_all(struct Curl_easy *data)
Curl_ssl->close_all(data);
}
-int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
- curl_socket_t *socks)
+void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
+ struct easy_pollset *ps)
{
- struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
-
- if(sock == CURL_SOCKET_BAD)
- return GETSOCK_BLANK;
-
- if(connssl->connecting_state == ssl_connect_2_writing) {
- /* we are only interested in writing */
- socks[0] = sock;
- return GETSOCK_WRITESOCK(0);
+ if(!cf->connected) {
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+ if(sock != CURL_SOCKET_BAD) {
+ if(connssl->connecting_state == ssl_connect_2_writing) {
+ Curl_pollset_set_out_only(data, ps, sock);
+ }
+ else {
+ Curl_pollset_set_in_only(data, ps, sock);
+ }
+ }
}
- socks[0] = sock;
- return GETSOCK_READSOCK(0);
}
/* Selects an SSL crypto engine
@@ -748,28 +883,21 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
size_t valuelen)
{
struct curl_certinfo *ci = &data->info.certs;
- char *output;
struct curl_slist *nl;
CURLcode result = CURLE_OK;
- size_t labellen = strlen(label);
- size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
-
- output = malloc(outlen);
- if(!output)
- return CURLE_OUT_OF_MEMORY;
+ struct dynbuf build;
- /* sprintf the label and colon */
- msnprintf(output, outlen, "%s:", label);
+ Curl_dyn_init(&build, 10000);
- /* memcpy the value (it might not be null-terminated) */
- memcpy(&output[labellen + 1], value, valuelen);
-
- /* null-terminate the output */
- output[labellen + 1 + valuelen] = 0;
+ if(Curl_dyn_add(&build, label) ||
+ Curl_dyn_addn(&build, ":", 1) ||
+ Curl_dyn_addn(&build, value, valuelen))
+ return CURLE_OUT_OF_MEMORY;
- nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
+ nl = Curl_slist_append_nodup(ci->certinfo[certnum],
+ Curl_dyn_ptr(&build));
if(!nl) {
- free(output);
+ Curl_dyn_free(&build);
curl_slist_free_all(ci->certinfo[certnum]);
result = CURLE_OUT_OF_MEMORY;
}
@@ -786,32 +914,6 @@ CURLcode Curl_ssl_random(struct Curl_easy *data,
}
/*
- * 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
*/
@@ -893,7 +995,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
/* only do this if pinnedpubkey starts with "sha256//", length 8 */
if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
CURLcode encode;
- size_t encodedlen = 0, pinkeylen;
+ size_t encodedlen = 0;
char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos;
unsigned char *sha256sumdigest;
@@ -921,13 +1023,11 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
infof(data, " public key hash: sha256//%s", encoded);
/* it starts with sha256//, copy so we can modify it */
- pinkeylen = strlen(pinnedpubkey) + 1;
- pinkeycopy = malloc(pinkeylen);
+ pinkeycopy = strdup(pinnedpubkey);
if(!pinkeycopy) {
Curl_safefree(encoded);
return CURLE_OUT_OF_MEMORY;
}
- memcpy(pinkeycopy, pinnedpubkey, pinkeylen);
/* point begin_pos to the copy, and start extracting keys */
begin_pos = pinkeycopy;
do {
@@ -1156,13 +1256,13 @@ static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf,
return Curl_ssl->connect_nonblocking(cf, data, done);
}
-static int multissl_get_select_socks(struct Curl_cfilter *cf,
+static void multissl_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
- curl_socket_t *socks)
+ struct easy_pollset *ps)
{
if(multissl_setup(NULL))
- return 0;
- return Curl_ssl->get_select_socks(cf, data, socks);
+ return;
+ Curl_ssl->adjust_pollset(cf, data, ps);
}
static void *multissl_get_internals(struct ssl_connect_data *connssl,
@@ -1214,7 +1314,7 @@ static const struct Curl_ssl Curl_ssl_multi = {
Curl_none_cert_status_request, /* cert_status_request */
multissl_connect, /* connect */
multissl_connect_nonblocking, /* connect_nonblocking */
- multissl_get_select_socks, /* getsock */
+ multissl_adjust_pollset, /* adjust_pollset */
multissl_get_internals, /* get_internals */
multissl_close, /* close_one */
Curl_none_close_all, /* close_all */
@@ -1313,17 +1413,13 @@ static size_t multissl_version(char *buffer, size_t size)
backends_len = p - backends;
}
- if(!size)
- return 0;
-
- if(size <= backends_len) {
- strncpy(buffer, backends, size - 1);
- buffer[size - 1] = '\0';
- return size - 1;
+ if(size) {
+ if(backends_len < size)
+ strcpy(buffer, backends);
+ else
+ *buffer = 0; /* did not fit */
}
-
- strcpy(buffer, backends);
- return backends_len;
+ return 0;
}
static int multissl_setup(const struct Curl_ssl *backend)
@@ -1409,12 +1505,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)
@@ -1423,12 +1521,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;
@@ -1454,23 +1566,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;
@@ -1525,7 +1657,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;
@@ -1583,38 +1715,49 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
{
struct cf_call_data save;
ssize_t nread;
+ size_t ntotal = 0;
CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
- if(nread > 0) {
- DEBUGASSERT((size_t)nread <= len);
- }
- else if(nread == 0) {
- /* eof */
+ /* Do receive until we fill the buffer somehwhat or EGAIN, error or EOF */
+ while(!ntotal || (len - ntotal) > (4*1024)) {
*err = CURLE_OK;
+ nread = Curl_ssl->recv_plain(cf, data, buf + ntotal, len - ntotal, err);
+ if(nread < 0) {
+ if(*err == CURLE_AGAIN && ntotal > 0) {
+ /* we EAGAINed after having reed data, return the success amount */
+ *err = CURLE_OK;
+ break;
+ }
+ /* we have a an error to report */
+ goto out;
+ }
+ else if(nread == 0) {
+ /* eof */
+ break;
+ }
+ ntotal += (size_t)nread;
+ DEBUGASSERT((size_t)ntotal <= len);
}
- CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err);
+ nread = (ssize_t)ntotal;
+out:
+ CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
+ nread, *err);
CF_DATA_RESTORE(cf, save);
return nread;
}
-static int ssl_cf_get_select_socks(struct Curl_cfilter *cf,
+static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
- curl_socket_t *socks)
+ struct easy_pollset *ps)
{
struct cf_call_data save;
- int fds = GETSOCK_BLANK;
- if(!cf->next->connected) {
- fds = cf->next->cft->get_select_socks(cf->next, data, socks);
- }
- else if(!cf->connected) {
+ if(!cf->connected) {
CF_DATA_SAVE(save, cf, data);
- fds = Curl_ssl->get_select_socks(cf, data, socks);
+ Curl_ssl->adjust_pollset(cf, data, ps);
CF_DATA_RESTORE(cf, save);
}
- return fds;
}
static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
@@ -1705,7 +1848,7 @@ struct Curl_cftype Curl_cft_ssl = {
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
- ssl_cf_get_select_socks,
+ ssl_cf_adjust_pollset,
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
@@ -1715,6 +1858,8 @@ struct Curl_cftype Curl_cft_ssl = {
ssl_cf_query,
};
+#ifndef CURL_DISABLE_PROXY
+
struct Curl_cftype Curl_cft_ssl_proxy = {
"SSL-PROXY",
CF_TYPE_SSL,
@@ -1723,7 +1868,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = {
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
- ssl_cf_get_select_socks,
+ ssl_cf_adjust_pollset,
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
@@ -1733,6 +1878,8 @@ struct Curl_cftype Curl_cft_ssl_proxy = {
Curl_cf_def_query,
};
+#endif /* !CURL_DISABLE_PROXY */
+
static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn)
@@ -1837,6 +1984,20 @@ bool Curl_ssl_supports(struct Curl_easy *data, int option)
return (Curl_ssl->supports & option)? TRUE : FALSE;
}
+static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf)
+{
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_ssl)
+ return cf;
+#ifndef CURL_DISABLE_PROXY
+ if(cf->cft == &Curl_cft_ssl_proxy)
+ return cf;
+#endif
+ }
+ return NULL;
+}
+
+
void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
CURLINFO info, int n)
{
@@ -1844,8 +2005,8 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
(void)n;
if(data->conn) {
struct Curl_cfilter *cf;
- /* get first filter in chain, if any is present */
- cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]);
+ /* get first SSL filter in chain, if any is present */
+ cf = get_ssl_filter(data->conn->cfilter[sockindex]);
if(cf) {
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
@@ -1875,26 +2036,14 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
return result;
}
-static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn,
- int sockindex)
-{
- struct Curl_cfilter *cf, *lowest_ssl_cf = NULL;
-
- for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) {
- if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) {
- lowest_ssl_cf = cf;
- if(cf->connected || (cf->next && cf->next->connected)) {
- /* connected or about to start */
- return cf;
- }
- }
- }
- return lowest_ssl_cf;
-}
-
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
{
+#ifndef CURL_DISABLE_PROXY
return (cf->cft == &Curl_cft_ssl_proxy);
+#else
+ (void)cf;
+ return FALSE;
+#endif
}
struct ssl_config_data *
@@ -1908,17 +2057,6 @@ Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data)
#endif
}
-struct ssl_config_data *
-Curl_ssl_get_config(struct Curl_easy *data, int sockindex)
-{
- struct Curl_cfilter *cf;
-
- (void)data;
- DEBUGASSERT(data->conn);
- cf = get_ssl_cf_engaged(data->conn, sockindex);
- return cf? Curl_ssl_cf_get_config(cf, data) : &data->set.ssl;
-}
-
struct ssl_primary_config *
Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf)
{
@@ -1930,15 +2068,6 @@ Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf)
#endif
}
-struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf)
-{
- for(; cf; cf = cf->next) {
- if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy)
- return cf;
- }
- return NULL;
-}
-
CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
const struct alpn_spec *spec)
{
@@ -2005,10 +2134,6 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
!memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) {
*palpn = CURL_HTTP_VERSION_1_1;
}
- else if(proto_len == ALPN_HTTP_1_0_LENGTH &&
- !memcmp(ALPN_HTTP_1_0, proto, ALPN_HTTP_1_0_LENGTH)) {
- *palpn = CURL_HTTP_VERSION_1_0;
- }
#ifdef USE_HTTP2
else if(proto_len == ALPN_H2_LENGTH &&
!memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) {
diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h
index 8ad1cf6de..744bbf8fd 100644
--- a/lib/vtls/vtls.h
+++ b/lib/vtls/vtls.h
@@ -65,15 +65,54 @@ 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);
-bool Curl_ssl_config_matches(struct ssl_primary_config *data,
- struct ssl_primary_config *needle);
-bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
- struct ssl_primary_config *dest);
-void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc);
-
curl_sslbackend Curl_ssl_backend(void);
+/**
+ * Init ssl config for a new easy handle.
+ */
+void Curl_ssl_easy_config_init(struct Curl_easy *data);
+
+/**
+ * Init the `data->set.ssl` and `data->set.proxy_ssl` for
+ * connection matching use.
+ */
+CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data);
+
+/**
+ * Init SSL configs (main + proxy) for a new connection from the easy handle.
+ */
+CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/**
+ * Free allocated resources in SSL configs (main + proxy) for
+ * the given connection.
+ */
+void Curl_ssl_conn_config_cleanup(struct connectdata *conn);
+
+/**
+ * Return TRUE iff SSL configuration from `conn` is functionally the
+ * same as the one on `candidate`.
+ * @param proxy match the proxy SSL config or the main one
+ */
+bool Curl_ssl_conn_config_match(struct Curl_easy *data,
+ struct connectdata *candidate,
+ bool proxy);
+
+/* Update certain connection SSL config flags after they have
+ * been changed on the easy handle. Will work for `verifypeer`,
+ * `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);
@@ -160,18 +199,6 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
#endif /* !CURL_DISABLE_PROXY */
/**
- * Get the SSL configuration that is used on the connection.
- * This returns NULL if no SSL is configured.
- * Otherwise it returns the config of the first (highest) one that is
- * either connected, in handshake or about to start
- * (e.g. all filters below it are connected). If SSL filters are present,
- * but neither can start operating, return the config of the lowest one
- * that will first come into effect when connecting.
- */
-struct ssl_config_data *Curl_ssl_get_config(struct Curl_easy *data,
- int sockindex);
-
-/**
* True iff the underlying SSL implementation supports the option.
* Option is one of the defined SSLSUPP_* values.
* `data` maybe NULL for the features of the default implementation.
@@ -188,8 +215,22 @@ bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option);
void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
CURLINFO info, int n);
+/**
+ * Get the ssl_config_data in `data` that is relevant for cfilter `cf`.
+ */
+struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
+/**
+ * Get the primary config relevant for the filter from its connection.
+ */
+struct ssl_primary_config *
+ Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf);
+
extern struct Curl_cftype Curl_cft_ssl;
+#ifndef CURL_DISABLE_PROXY
extern struct Curl_cftype Curl_cft_ssl_proxy;
+#endif
#else /* if not USE_SSL */
@@ -209,8 +250,9 @@ extern struct Curl_cftype Curl_cft_ssl_proxy;
#define Curl_ssl_get_internals(a,b,c,d) NULL
#define Curl_ssl_supports(a,b) FALSE
#define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
-#define Curl_ssl_get_config(a,b) NULL
#define Curl_ssl_cfilter_remove(a,b) CURLE_OK
+#define Curl_ssl_cf_get_config(a,b) NULL
+#define Curl_ssl_cf_get_primary_config(a) NULL
#endif
#endif /* HEADER_CURL_VTLS_H */
diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h
index a6e4544a8..af7ae552e 100644
--- a/lib/vtls/vtls_int.h
+++ b/lib/vtls/vtls_int.h
@@ -32,8 +32,6 @@
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
#define ALPN_HTTP_1_1_LENGTH 8
#define ALPN_HTTP_1_1 "http/1.1"
-#define ALPN_HTTP_1_0_LENGTH 8
-#define ALPN_HTTP_1_0 "http/1.0"
#define ALPN_H2_LENGTH 2
#define ALPN_H2 "h2"
#define ALPN_H3_LENGTH 2
@@ -70,14 +68,14 @@ 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 */
struct curltime handshake_done; /* time when handshake finished */
int port; /* remote port at origin */
BIT(use_alpn); /* if ALPN shall be used in handshake */
+ BIT(reused_session); /* session-ID was reused for this */
};
@@ -118,14 +116,11 @@ struct Curl_ssl {
struct Curl_easy *data,
bool *done);
- /* If the SSL backend wants to read or write on this connection during a
- handshake, set socks[0] to the connection's FIRSTSOCKET, and return
- a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or
- GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK.
- Mandatory. */
- int (*get_select_socks)(struct Curl_cfilter *cf, struct Curl_easy *data,
- curl_socket_t *socks);
-
+ /* During handshake, adjust the pollset to include the socket
+ * for POLLOUT or POLLIN as needed.
+ * Mandatory. */
+ void (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data,
+ struct easy_pollset *ps);
void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info);
void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data);
void (*close_all)(struct Curl_easy *data);
@@ -169,25 +164,8 @@ CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine);
CURLcode Curl_none_set_engine_default(struct Curl_easy *data);
struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
bool Curl_none_false_start(void);
-int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
- curl_socket_t *socks);
-
-/**
- * Get the ssl_config_data in `data` that is relevant for cfilter `cf`.
- */
-struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf,
- struct Curl_easy *data);
-
-/**
- * Get the primary config relevant for the filter from its connection.
- */
-struct ssl_primary_config *
- Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf);
-
-/**
- * Get the first SSL filter in the chain starting with `cf`, or NULL.
- */
-struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf);
+void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
+ struct easy_pollset *ps);
/**
* Get the SSL filter below the given one or NULL if there is none.
diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
index b1384a644..a3c017cea 100644
--- a/lib/vtls/wolfssl.c
+++ b/lib/vtls/wolfssl.c
@@ -480,6 +480,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
#endif
+ default:
break;
}
@@ -513,7 +514,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
-#ifndef NO_FILESYSTEM
+#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS)
/* load native CA certificates */
if(ssl_config->native_ca_store) {
if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) {
@@ -582,12 +583,25 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
if(ssl_config->primary.clientcert && ssl_config->key) {
int file_type = do_file_type(ssl_config->cert_type);
- if(wolfSSL_CTX_use_certificate_file(backend->ctx,
- ssl_config->primary.clientcert,
- file_type) != 1) {
- failf(data, "unable to use client certificate (no key or wrong pass"
- " phrase?)");
- return CURLE_SSL_CONNECT_ERROR;
+ if(file_type == WOLFSSL_FILETYPE_PEM) {
+ if(wolfSSL_CTX_use_certificate_chain_file(backend->ctx,
+ ssl_config->primary.clientcert)
+ != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else if(file_type == WOLFSSL_FILETYPE_ASN1) {
+ if(wolfSSL_CTX_use_certificate_file(backend->ctx,
+ ssl_config->primary.clientcert,
+ file_type) != 1) {
+ failf(data, "unable to use client certificate");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+ failf(data, "unknown cert type");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
file_type = do_file_type(ssl_config->key_type);
@@ -608,24 +622,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;
}
@@ -763,9 +765,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;
}
@@ -813,7 +815,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
@@ -1095,9 +1097,7 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
*curlcode = CURLE_OK;
return 0;
case SSL_ERROR_NONE:
- /* FALLTHROUGH */
case SSL_ERROR_WANT_READ:
- /* FALLTHROUGH */
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke wolfSSL_read() */
CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
@@ -1398,7 +1398,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
Curl_none_cert_status_request, /* cert_status_request */
wolfssl_connect, /* connect */
wolfssl_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_get_select_socks, /* getsock */
+ Curl_ssl_adjust_pollset, /* adjust_pollset */
wolfssl_get_internals, /* get_internals */
wolfssl_close, /* close_one */
Curl_none_close_all, /* close_all */
diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c
index c3fd3a30b..da079361d 100644
--- a/lib/vtls/x509asn1.c
+++ b/lib/vtls/x509asn1.c
@@ -97,6 +97,11 @@
#define CURL_ASN1_CHARACTER_STRING 29
#define CURL_ASN1_BMP_STRING 30
+/* Max sixes */
+
+#define MAX_X509_STR 10000
+#define MAX_X509_CERT 100000
+
#ifdef WANT_EXTRACT_CERTINFO
/* ASN.1 OID table entry. */
struct Curl_OID {
@@ -255,61 +260,61 @@ static const struct Curl_OID *searchOID(const char *oid)
}
/*
- * Convert an ASN.1 Boolean value into its string representation. Return the
- * dynamically allocated string, or NULL if source is not an ASN.1 Boolean
- * value.
+ * Convert an ASN.1 Boolean value into its string representation.
+ *
+ * Return error code.
*/
-static const char *bool2str(const char *beg, const char *end)
+static CURLcode bool2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
if(end - beg != 1)
- return NULL;
- return strdup(*beg? "TRUE": "FALSE");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return Curl_dyn_add(store, *beg? "TRUE": "FALSE");
}
/*
* Convert an ASN.1 octet string to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error code.
*/
-static const char *octet2str(const char *beg, const char *end)
+static CURLcode octet2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
- struct dynbuf buf;
- CURLcode result;
-
- Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1);
- result = Curl_dyn_addn(&buf, "", 0);
+ CURLcode result = CURLE_OK;
while(!result && beg < end)
- result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++);
+ result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++);
- return Curl_dyn_ptr(&buf);
+ return result;
}
-static const char *bit2str(const char *beg, const char *end)
+static CURLcode bit2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
- /* Convert an ASN.1 bit string to a printable string.
- Return the dynamically allocated string, or NULL if an error occurs. */
+ /* Convert an ASN.1 bit string to a printable string. */
if(++beg > end)
- return NULL;
- return octet2str(beg, end);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return octet2str(store, beg, end);
}
/*
* Convert an ASN.1 integer value into its string representation.
- * Return the dynamically allocated string, or NULL if source is not an
- * ASN.1 integer value.
+ *
+ * Returns error.
*/
-static const char *int2str(const char *beg, const char *end)
+static CURLcode int2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
unsigned int val = 0;
size_t n = end - beg;
if(!n)
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
if(n > 4)
- return octet2str(beg, end);
+ return octet2str(store, beg, end);
/* Represent integers <= 32-bit as a single value. */
if(*beg & 0x80)
@@ -318,25 +323,24 @@ static const char *int2str(const char *beg, const char *end)
do
val = (val << 8) | *(const unsigned char *) beg++;
while(beg < end);
- return curl_maprintf("%s%x", val >= 10? "0x": "", val);
+ return Curl_dyn_addf(store, "%s%x", val >= 10? "0x": "", val);
}
/*
- * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the
- * destination buffer dynamically. The allocation size will normally be too
- * large: this is to avoid buffer overflows.
- * Terminate the string with a nul byte and return the converted
- * string length.
+ * Convert from an ASN.1 typed string to UTF8.
+ *
+ * The result is stored in a dynbuf that is inited by the user of this
+ * function.
+ *
+ * Returns error.
*/
-static ssize_t
-utf8asn1str(char **to, int type, const char *from, const char *end)
+static CURLcode
+utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
{
size_t inlength = end - from;
int size = 1;
- size_t outlength;
- char *buf;
+ CURLcode result = CURLE_OK;
- *to = NULL;
switch(type) {
case CURL_ASN1_BMP_STRING:
size = 2;
@@ -352,133 +356,85 @@ utf8asn1str(char **to, int type, const char *from, const char *end)
case CURL_ASN1_UTF8_STRING:
break;
default:
- return -1; /* Conversion not supported. */
+ return CURLE_BAD_FUNCTION_ARGUMENT; /* Conversion not supported. */
}
if(inlength % size)
- return -1; /* Length inconsistent with character size. */
- if(inlength / size > (SIZE_T_MAX - 1) / 4)
- return -1; /* Too big. */
- buf = malloc(4 * (inlength / size) + 1);
- if(!buf)
- return -1; /* Not enough memory. */
+ /* Length inconsistent with character size. */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
if(type == CURL_ASN1_UTF8_STRING) {
/* Just copy. */
- outlength = inlength;
- if(outlength)
- memcpy(buf, from, outlength);
+ if(inlength)
+ result = Curl_dyn_addn(to, from, inlength);
}
else {
- for(outlength = 0; from < end;) {
- int charsize;
- unsigned int wc;
+ while(!result && (from < end)) {
+ char buf[4]; /* decode buffer */
+ int charsize = 1;
+ unsigned int wc = 0;
- wc = 0;
switch(size) {
case 4:
wc = (wc << 8) | *(const unsigned char *) from++;
wc = (wc << 8) | *(const unsigned char *) from++;
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 2:
wc = (wc << 8) | *(const unsigned char *) from++;
- /* FALLTHROUGH */
+ FALLTHROUGH();
default: /* case 1: */
wc = (wc << 8) | *(const unsigned char *) from++;
}
- charsize = 1;
if(wc >= 0x00000080) {
if(wc >= 0x00000800) {
if(wc >= 0x00010000) {
if(wc >= 0x00200000) {
free(buf);
- return -1; /* Invalid char. size for target encoding. */
+ /* Invalid char. size for target encoding. */
+ return CURLE_WEIRD_SERVER_REPLY;
}
- buf[outlength + 3] = (char) (0x80 | (wc & 0x3F));
+ buf[3] = (char) (0x80 | (wc & 0x3F));
wc = (wc >> 6) | 0x00010000;
charsize++;
}
- buf[outlength + 2] = (char) (0x80 | (wc & 0x3F));
+ buf[2] = (char) (0x80 | (wc & 0x3F));
wc = (wc >> 6) | 0x00000800;
charsize++;
}
- buf[outlength + 1] = (char) (0x80 | (wc & 0x3F));
+ buf[1] = (char) (0x80 | (wc & 0x3F));
wc = (wc >> 6) | 0x000000C0;
charsize++;
}
- buf[outlength] = (char) wc;
- outlength += charsize;
+ buf[0] = (char) wc;
+ result = Curl_dyn_addn(to, buf, charsize);
}
}
- buf[outlength] = '\0';
- *to = buf;
- return outlength;
-}
-
-/*
- * Convert an ASN.1 String into its UTF-8 string representation.
- * Return the dynamically allocated string, or NULL if an error occurs.
- */
-static const char *string2str(int type, const char *beg, const char *end)
-{
- char *buf;
- if(utf8asn1str(&buf, type, beg, end) < 0)
- return NULL;
- return buf;
-}
-
-/*
- * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at
- * buf. Return the total number of encoded digits, even if larger than
- * `buflen'.
- */
-static size_t encodeUint(char *buf, size_t buflen, unsigned int x)
-{
- size_t i = 0;
- unsigned int y = x / 10;
-
- if(y) {
- i = encodeUint(buf, buflen, y);
- x -= y * 10;
- }
- if(i < buflen)
- buf[i] = (char) ('0' + x);
- i++;
- if(i < buflen)
- buf[i] = '\0'; /* Store a terminator if possible. */
- return i;
+ return result;
}
/*
* Convert an ASN.1 OID into its dotted string representation.
- * Store the result in th `n'-byte buffer at `buf'.
- * Return the converted string length, or 0 on errors.
+ *
+ * Return error code.
*/
-static size_t encodeOID(char *buf, size_t buflen,
- const char *beg, const char *end)
+static CURLcode encodeOID(struct dynbuf *store,
+ const char *beg, const char *end)
{
- size_t i;
unsigned int x;
unsigned int y;
+ CURLcode result = CURLE_OK;
/* Process the first two numbers. */
y = *(const unsigned char *) beg++;
x = y / 40;
y -= x * 40;
- i = encodeUint(buf, buflen, x);
- if(i < buflen)
- buf[i] = '.';
- i++;
- if(i >= buflen)
- i += encodeUint(NULL, 0, y);
- else
- i += encodeUint(buf + i, buflen - i, y);
+
+ result = Curl_dyn_addf(store, "%u.%u", x, y);
+ if(result)
+ return result;
/* Process the trailing numbers. */
while(beg < end) {
- if(i < buflen)
- buf[i] = '.';
- i++;
x = 0;
do {
if(x & 0xFF000000)
@@ -486,46 +442,42 @@ static size_t encodeOID(char *buf, size_t buflen,
y = *(const unsigned char *) beg++;
x = (x << 7) | (y & 0x7F);
} while(y & 0x80);
- if(i >= buflen)
- i += encodeUint(NULL, 0, x);
- else
- i += encodeUint(buf + i, buflen - i, x);
+ result = Curl_dyn_addf(store, ".%u", x);
}
- if(i < buflen)
- buf[i] = '\0';
- return i;
+ return result;
}
/*
* Convert an ASN.1 OID into its dotted or symbolic string representation.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error code.
*/
-static const char *OID2str(const char *beg, const char *end, bool symbolic)
+static CURLcode OID2str(struct dynbuf *store,
+ const char *beg, const char *end, bool symbolic)
{
- char *buf = NULL;
+ CURLcode result = CURLE_OK;
if(beg < end) {
- size_t buflen = encodeOID(NULL, 0, beg, end);
- if(buflen) {
- buf = malloc(buflen + 1); /* one extra for the zero byte */
- if(buf) {
- encodeOID(buf, buflen, beg, end);
- buf[buflen] = '\0';
-
- if(symbolic) {
- const struct Curl_OID *op = searchOID(buf);
- if(op) {
- free(buf);
- buf = strdup(op->textoid);
- }
- }
+ if(symbolic) {
+ struct dynbuf buf;
+ Curl_dyn_init(&buf, MAX_X509_STR);
+ result = encodeOID(&buf, beg, end);
+
+ if(!result) {
+ const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf));
+ if(op)
+ result = Curl_dyn_add(store, op->textoid);
+ Curl_dyn_free(&buf);
}
}
+ else
+ result = encodeOID(store, beg, end);
}
- return buf;
+ return result;
}
-static const char *GTime2str(const char *beg, const char *end)
+static CURLcode GTime2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
const char *tzp;
const char *fracp;
@@ -548,12 +500,12 @@ static const char *GTime2str(const char *beg, const char *end)
break;
case 2:
sec1 = fracp[-2];
- /* FALLTHROUGH */
+ FALLTHROUGH();
case 1:
sec2 = fracp[-1];
break;
default:
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
/* Scan for timezone, measure fractional seconds. */
@@ -582,7 +534,8 @@ static const char *GTime2str(const char *beg, const char *end)
}
tzl = end - tzp;
- return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
+ return Curl_dyn_addf(store,
+ "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
beg, beg + 4, beg + 6,
beg + 8, beg + 10, sec1, sec2,
fracl? ".": "", (int)fracl, fracp,
@@ -590,10 +543,12 @@ static const char *GTime2str(const char *beg, const char *end)
}
/*
- * Convert an ASN.1 UTC time to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ * Convert an ASN.1 UTC time to a printable string.
+ *
+ * Return error code.
*/
-static const char *UTime2str(const char *beg, const char *end)
+static CURLcode UTime2str(struct dynbuf *store,
+ const char *beg, const char *end)
{
const char *tzp;
size_t tzl;
@@ -606,15 +561,16 @@ static const char *UTime2str(const char *beg, const char *end)
switch(tzp - sec) {
case 0:
sec = "00";
+ FALLTHROUGH();
case 2:
break;
default:
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
/* Process timezone. */
if(tzp >= end)
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
if(*tzp == 'Z') {
tzp = "GMT";
end = tzp + 3;
@@ -623,7 +579,7 @@ static const char *UTime2str(const char *beg, const char *end)
tzp++;
tzl = end - tzp;
- return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+ return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
20 - (*beg >= '5'), beg, beg + 2, beg + 4,
beg + 6, beg + 8, sec,
(int)tzl, tzp);
@@ -631,34 +587,45 @@ static const char *UTime2str(const char *beg, const char *end)
/*
* Convert an ASN.1 element to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error
*/
-static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
+static CURLcode ASN1tostr(struct dynbuf *store,
+ struct Curl_asn1Element *elem, int type)
{
+ CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
if(elem->constructed)
- return NULL; /* No conversion of structured elements. */
+ return CURLE_OK; /* No conversion of structured elements. */
if(!type)
type = elem->tag; /* Type not forced: use element tag as type. */
switch(type) {
case CURL_ASN1_BOOLEAN:
- return bool2str(elem->beg, elem->end);
+ result = bool2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_INTEGER:
case CURL_ASN1_ENUMERATED:
- return int2str(elem->beg, elem->end);
+ result = int2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_BIT_STRING:
- return bit2str(elem->beg, elem->end);
+ result = bit2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_OCTET_STRING:
- return octet2str(elem->beg, elem->end);
+ result = octet2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_NULL:
- return strdup("");
+ result = Curl_dyn_addn(store, "", 1);
+ break;
case CURL_ASN1_OBJECT_IDENTIFIER:
- return OID2str(elem->beg, elem->end, TRUE);
+ result = OID2str(store, elem->beg, elem->end, TRUE);
+ break;
case CURL_ASN1_UTC_TIME:
- return UTime2str(elem->beg, elem->end);
+ result = UTime2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_GENERALIZED_TIME:
- return GTime2str(elem->beg, elem->end);
+ result = GTime2str(store, elem->beg, elem->end);
+ break;
case CURL_ASN1_UTF8_STRING:
case CURL_ASN1_NUMERIC_STRING:
case CURL_ASN1_PRINTABLE_STRING:
@@ -667,87 +634,96 @@ static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
case CURL_ASN1_VISIBLE_STRING:
case CURL_ASN1_UNIVERSAL_STRING:
case CURL_ASN1_BMP_STRING:
- return string2str(type, elem->beg, elem->end);
+ result = utf8asn1str(store, type, elem->beg, elem->end);
+ break;
}
- return NULL; /* Unsupported. */
+ return result;
}
/*
- * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at
- * `buf'.
+ * ASCII encode distinguished name at `dn' into the store dynbuf.
*
- * Returns the total string length, even if larger than `buflen' or -1 on
- * error.
+ * Returns error.
*/
-static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn)
+static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
{
struct Curl_asn1Element rdn;
struct Curl_asn1Element atv;
struct Curl_asn1Element oid;
struct Curl_asn1Element value;
- size_t l = 0;
const char *p1;
const char *p2;
const char *p3;
const char *str;
+ CURLcode result = CURLE_OK;
+ bool added = FALSE;
+ struct dynbuf temp;
+ Curl_dyn_init(&temp, MAX_X509_STR);
for(p1 = dn->beg; p1 < dn->end;) {
p1 = getASN1Element(&rdn, p1, dn->end);
- if(!p1)
- return -1;
+ if(!p1) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
for(p2 = rdn.beg; p2 < rdn.end;) {
p2 = getASN1Element(&atv, p2, rdn.end);
- if(!p2)
- return -1;
+ if(!p2) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
p3 = getASN1Element(&oid, atv.beg, atv.end);
- if(!p3)
- return -1;
- if(!getASN1Element(&value, p3, atv.end))
- return -1;
- str = ASN1tostr(&oid, 0);
- if(!str)
- return -1;
+ if(!p3) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
+ if(!getASN1Element(&value, p3, atv.end)) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto error;
+ }
+ Curl_dyn_reset(&temp);
+ result = ASN1tostr(&temp, &oid, 0);
+ if(result)
+ goto error;
+
+ str = Curl_dyn_ptr(&temp);
/* Encode delimiter.
If attribute has a short uppercase name, delimiter is ", ". */
- if(l) {
- for(p3 = str; ISUPPER(*p3); p3++)
- ;
- for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) {
- if(l < buflen)
- buf[l] = *p3;
- l++;
- }
+ for(p3 = str; ISUPPER(*p3); p3++)
+ ;
+ if(added) {
+ if(p3 - str > 2)
+ result = Curl_dyn_addn(store, "/", 1);
+ else
+ result = Curl_dyn_addn(store, ", ", 2);
+ if(result)
+ goto error;
}
/* Encode attribute name. */
- for(p3 = str; *p3; p3++) {
- if(l < buflen)
- buf[l] = *p3;
- l++;
- }
- free((char *) str);
+ result = Curl_dyn_add(store, str);
+ if(result)
+ goto error;
/* Generate equal sign. */
- if(l < buflen)
- buf[l] = '=';
- l++;
+ result = Curl_dyn_addn(store, "=", 1);
+ if(result)
+ goto error;
/* Generate value. */
- str = ASN1tostr(&value, 0);
- if(!str)
- return -1;
- for(p3 = str; *p3; p3++) {
- if(l < buflen)
- buf[l] = *p3;
- l++;
- }
- free((char *) str);
+ result = ASN1tostr(store, &value, 0);
+ if(result)
+ goto error;
+ Curl_dyn_reset(&temp);
+ added = TRUE; /* use separator for next */
}
}
+error:
+ Curl_dyn_free(&temp);
- return l;
+ return result;
}
#endif /* WANT_EXTRACT_CERTINFO */
@@ -876,25 +852,9 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
#ifdef WANT_EXTRACT_CERTINFO
-/*
- * Copy at most 64-characters, terminate with a newline and returns the
- * effective number of stored characters.
- */
-static size_t copySubstring(char *to, const char *from)
-{
- size_t i;
- for(i = 0; i < 64; i++) {
- to[i] = *from;
- if(!*from++)
- break;
- }
-
- to[i++] = '\n';
- return i;
-}
-
-static const char *dumpAlgo(struct Curl_asn1Element *param,
- const char *beg, const char *end)
+static CURLcode dumpAlgo(struct dynbuf *store,
+ struct Curl_asn1Element *param,
+ const char *beg, const char *end)
{
struct Curl_asn1Element oid;
@@ -902,14 +862,16 @@ static const char *dumpAlgo(struct Curl_asn1Element *param,
beg = getASN1Element(&oid, beg, end);
if(!beg)
- return NULL;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
param->header = NULL;
param->tag = 0;
param->beg = param->end = end;
- if(beg < end)
- if(!getASN1Element(param, beg, end))
- return NULL;
- return OID2str(oid.beg, oid.end, TRUE);
+ if(beg < end) {
+ const char *p = getASN1Element(param, beg, end);
+ if(!p)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ return OID2str(store, oid.beg, oid.end, TRUE);
}
/*
@@ -926,24 +888,47 @@ static CURLcode ssl_push_certinfo(struct Curl_easy *data,
return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
}
-/* return 0 on success, 1 on error */
-static int do_pubkey_field(struct Curl_easy *data, int certnum,
- const char *label, struct Curl_asn1Element *elem)
+/*
+ * This is a convenience function for push_certinfo_len that takes a
+ * dynbuf value.
+ *
+ * It also does the verbose output if !certnum.
+ */
+static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data,
+ int certnum,
+ const char *label,
+ struct dynbuf *ptr)
{
- const char *output;
- CURLcode result = CURLE_OK;
+ size_t valuelen = Curl_dyn_len(ptr);
+ char *value = Curl_dyn_ptr(ptr);
+
+ CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label,
+ value, valuelen);
+
+ if(!certnum && !result)
+ infof(data, " %s: %s", label, value);
+
+ return result;
+}
+
+static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
+ const char *label,
+ struct Curl_asn1Element *elem)
+{
+ CURLcode result;
+ struct dynbuf out;
+
+ Curl_dyn_init(&out, MAX_X509_STR);
/* Generate a certificate information record for the public key. */
- output = ASN1tostr(elem, 0);
- if(output) {
+ result = ASN1tostr(&out, elem, 0);
+ if(!result) {
if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, label, output);
- if(!certnum && !result)
- infof(data, " %s: %s", label, output);
- free((char *) output);
+ result = ssl_push_certinfo_dyn(data, certnum, label, &out);
+ Curl_dyn_free(&out);
}
- return result ? 1 : 0;
+ return result;
}
/* return 0 on success, 1 on error */
@@ -964,7 +949,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
*/
const size_t len = ((pubkey->end - pubkey->beg - 2) * 4);
if(!certnum)
- infof(data, " ECC Public Key (%lu bits)", len);
+ infof(data, " ECC Public Key (%zu bits)", len);
if(data->set.ssl.certinfo) {
char q[sizeof(len) * 8 / 3 + 1];
(void)msnprintf(q, sizeof(q), "%zu", len);
@@ -998,7 +983,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
if(len > 32)
elem.beg = q; /* Strip leading zero bytes. */
if(!certnum)
- infof(data, " RSA Public Key (%lu bits)", len);
+ infof(data, " RSA Public Key (%zu bits)", len);
if(data->set.ssl.certinfo) {
char r[sizeof(len) * 8 / 3 + 1];
msnprintf(r, sizeof(r), "%zu", len);
@@ -1049,24 +1034,12 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
/*
* Convert an ASN.1 distinguished name into a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ * Return error.
*/
-static const char *DNtostr(struct Curl_asn1Element *dn)
+static CURLcode DNtostr(struct dynbuf *store,
+ struct Curl_asn1Element *dn)
{
- char *buf = NULL;
- ssize_t buflen = encodeDN(NULL, 0, dn);
-
- if(buflen >= 0) {
- buf = malloc(buflen + 1);
- if(buf) {
- if(encodeDN(buf, buflen + 1, dn) == -1) {
- free(buf);
- return NULL;
- }
- buf[buflen] = '\0';
- }
- }
- return buf;
+ return encodeDN(store, dn);
}
CURLcode Curl_extract_certinfo(struct Curl_easy *data,
@@ -1076,19 +1049,19 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
{
struct Curl_X509certificate cert;
struct Curl_asn1Element param;
- const char *ccp;
- char *cp1;
- size_t cl1;
- char *cp2;
+ char *certptr;
+ size_t clen;
+ struct dynbuf out;
CURLcode result = CURLE_OK;
unsigned int version;
- size_t i;
- size_t j;
+ const char *ptr;
+ int rc;
if(!data->set.ssl.certinfo)
if(certnum)
return CURLE_OK;
+ Curl_dyn_init(&out, MAX_X509_STR);
/* Prepare the certificate information for curl_easy_getinfo(). */
/* Extract the certificate ASN.1 elements. */
@@ -1096,135 +1069,126 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
return CURLE_PEER_FAILED_VERIFICATION;
/* Subject. */
- ccp = DNtostr(&cert.subject);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
+ result = DNtostr(&out, &cert.subject);
+ if(result)
+ goto done;
if(data->set.ssl.certinfo) {
- result = ssl_push_certinfo(data, certnum, "Subject", ccp);
+ result = ssl_push_certinfo_dyn(data, certnum, "Subject", &out);
if(result)
- return result;
+ goto done;
}
- if(!certnum)
- infof(data, "%2d Subject: %s", certnum, ccp);
- free((char *) ccp);
+ Curl_dyn_reset(&out);
/* Issuer. */
- ccp = DNtostr(&cert.issuer);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
+ result = DNtostr(&out, &cert.issuer);
+ if(result)
+ goto done;
if(data->set.ssl.certinfo) {
- result = ssl_push_certinfo(data, certnum, "Issuer", ccp);
+ result = ssl_push_certinfo_dyn(data, certnum, "Issuer", &out);
+ if(result)
+ goto done;
}
- if(!certnum)
- infof(data, " Issuer: %s", ccp);
- free((char *) ccp);
- if(result)
- return result;
+ Curl_dyn_reset(&out);
/* Version (always fits in less than 32 bits). */
version = 0;
- for(ccp = cert.version.beg; ccp < cert.version.end; ccp++)
- version = (version << 8) | *(const unsigned char *) ccp;
+ for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
+ version = (version << 8) | *(const unsigned char *) ptr;
if(data->set.ssl.certinfo) {
- ccp = curl_maprintf("%x", version);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- result = ssl_push_certinfo(data, certnum, "Version", ccp);
- free((char *) ccp);
+ result = Curl_dyn_addf(&out, "%x", version);
+ if(result)
+ goto done;
+ result = ssl_push_certinfo_dyn(data, certnum, "Version", &out);
if(result)
- return result;
+ goto done;
+ Curl_dyn_reset(&out);
}
- if(!certnum)
- infof(data, " Version: %u (0x%x)", version + 1, version);
/* Serial number. */
- ccp = ASN1tostr(&cert.serialNumber, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Serial Number", ccp);
- if(!certnum)
- infof(data, " Serial Number: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.serialNumber, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Serial Number", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Signature algorithm .*/
- ccp = dumpAlgo(&param, cert.signatureAlgorithm.beg,
- cert.signatureAlgorithm.end);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
- if(!certnum)
- infof(data, " Signature Algorithm: %s", ccp);
- free((char *) ccp);
+ result = dumpAlgo(&out, &param, cert.signatureAlgorithm.beg,
+ cert.signatureAlgorithm.end);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm",
+ &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Start Date. */
- ccp = ASN1tostr(&cert.notBefore, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Start Date", ccp);
- if(!certnum)
- infof(data, " Start Date: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.notBefore, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Start Date", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Expire Date. */
- ccp = ASN1tostr(&cert.notAfter, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Expire Date", ccp);
- if(!certnum)
- infof(data, " Expire Date: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.notAfter, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Expire Date", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Public Key Algorithm. */
- ccp = dumpAlgo(&param, cert.subjectPublicKeyAlgorithm.beg,
- cert.subjectPublicKeyAlgorithm.end);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Public Key Algorithm",
- ccp);
- if(!result) {
- int ret;
- if(!certnum)
- infof(data, " Public Key Algorithm: %s", ccp);
- ret = do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
- if(ret)
- result = CURLE_OUT_OF_MEMORY; /* the most likely error */
- }
- free((char *) ccp);
+ result = dumpAlgo(&out, &param, cert.subjectPublicKeyAlgorithm.beg,
+ cert.subjectPublicKeyAlgorithm.end);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Public Key Algorithm",
+ &out);
+ if(result)
+ goto done;
+ }
+
+ rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out),
+ &param, &cert.subjectPublicKey);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY; /* the most likely error */
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Signature. */
- ccp = ASN1tostr(&cert.signature, 0);
- if(!ccp)
- return CURLE_OUT_OF_MEMORY;
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Signature", ccp);
- if(!certnum)
- infof(data, " Signature: %s", ccp);
- free((char *) ccp);
+ result = ASN1tostr(&out, &cert.signature, 0);
if(result)
- return result;
+ goto done;
+ if(data->set.ssl.certinfo) {
+ result = ssl_push_certinfo_dyn(data, certnum, "Signature", &out);
+ if(result)
+ goto done;
+ }
+ Curl_dyn_reset(&out);
/* Generate PEM certificate. */
result = Curl_base64_encode(cert.certificate.beg,
cert.certificate.end - cert.certificate.beg,
- &cp1, &cl1);
+ &certptr, &clen);
if(result)
- return result;
- /* Compute the number of characters in final certificate string. Format is:
+ goto done;
+
+ /* Generate the final output certificate string. Format is:
-----BEGIN CERTIFICATE-----\n
<max 64 base64 characters>\n
.
@@ -1232,207 +1196,34 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
.
-----END CERTIFICATE-----\n
*/
- i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26;
- cp2 = malloc(i + 1);
- if(!cp2) {
- free(cp1);
- return CURLE_OUT_OF_MEMORY;
- }
- /* Build the certificate string. */
- i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----");
- for(j = 0; j < cl1; j += 64)
- i += copySubstring(cp2 + i, cp1 + j);
- i += copySubstring(cp2 + i, "-----END CERTIFICATE-----");
- cp2[i] = '\0';
- free(cp1);
- if(data->set.ssl.certinfo)
- result = ssl_push_certinfo(data, certnum, "Cert", cp2);
- if(!certnum)
- infof(data, "%s", cp2);
- free(cp2);
- return result;
-}
-#endif /* WANT_EXTRACT_CERTINFO */
-
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+ Curl_dyn_reset(&out);
-#ifdef WANT_VERIFYHOST
-
-static const char *checkOID(const char *beg, const char *end,
- const char *oid)
-{
- struct Curl_asn1Element e;
- const char *ccp;
- const char *p;
- bool matched;
-
- /* Check if first ASN.1 element at `beg' is the given OID.
- Return a pointer in the source after the OID if found, else NULL. */
-
- ccp = getASN1Element(&e, beg, end);
- if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER)
- return NULL;
-
- p = OID2str(e.beg, e.end, FALSE);
- if(!p)
- return NULL;
-
- matched = !strcmp(p, oid);
- free((char *) p);
- return matched? ccp: NULL;
-}
-
-CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char *beg, const char *end)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- struct Curl_X509certificate cert;
- struct Curl_asn1Element dn;
- struct Curl_asn1Element elem;
- struct Curl_asn1Element ext;
- struct Curl_asn1Element name;
- const char *p;
- const char *q;
- char *dnsname;
- int matched = -1;
- size_t addrlen = (size_t) -1;
- ssize_t len;
- size_t hostlen;
-
-#ifdef ENABLE_IPV6
- struct in6_addr addr;
-#else
- struct in_addr addr;
-#endif
-
- /* Verify that connection server matches info in X509 certificate at
- `beg'..`end'. */
-
- if(!conn_config->verifyhost)
- return CURLE_OK;
-
- if(Curl_parseX509(&cert, beg, end))
- return CURLE_PEER_FAILED_VERIFICATION;
-
- hostlen = strlen(connssl->hostname);
-
- /* Get the server IP address. */
-#ifdef ENABLE_IPV6
- if(cf->conn->bits.ipv6_ip &&
- Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
- addrlen = sizeof(struct in6_addr);
- else
-#endif
- if(Curl_inet_pton(AF_INET, connssl->hostname, &addr))
- addrlen = sizeof(struct in_addr);
-
- /* Process extensions. */
- for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) {
- p = getASN1Element(&ext, p, cert.extensions.end);
- if(!p)
- return CURLE_PEER_FAILED_VERIFICATION;
-
- /* Check if extension is a subjectAlternativeName. */
- ext.beg = checkOID(ext.beg, ext.end, sanOID);
- if(ext.beg) {
- ext.beg = getASN1Element(&elem, ext.beg, ext.end);
- if(!ext.beg)
- return CURLE_PEER_FAILED_VERIFICATION;
- /* Skip critical if present. */
- if(elem.tag == CURL_ASN1_BOOLEAN) {
- ext.beg = getASN1Element(&elem, ext.beg, ext.end);
- if(!ext.beg)
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- /* Parse the octet string contents: is a single sequence. */
- if(!getASN1Element(&elem, elem.beg, elem.end))
- return CURLE_PEER_FAILED_VERIFICATION;
- /* Check all GeneralNames. */
- for(q = elem.beg; matched != 1 && q < elem.end;) {
- q = getASN1Element(&name, q, elem.end);
- if(!q)
- break;
- switch(name.tag) {
- case 2: /* DNS name. */
- len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING,
- name.beg, name.end);
- if(len > 0 && (size_t)len == strlen(dnsname))
- matched = Curl_cert_hostcheck(dnsname, (size_t)len,
- connssl->hostname, hostlen);
- else
- matched = 0;
- free(dnsname);
- break;
-
- case 7: /* IP address. */
- matched = (size_t)(name.end - name.beg) == addrlen &&
- !memcmp(&addr, name.beg, addrlen);
- break;
- }
- }
- }
- }
-
- switch(matched) {
- case 1:
- /* an alternative name matched the server hostname */
- infof(data, " subjectAltName: %s matched", connssl->dispname);
- return CURLE_OK;
- case 0:
- /* an alternative name field existed, but didn't match and then
- we MUST fail */
- infof(data, " subjectAltName does not match %s", connssl->dispname);
- return CURLE_PEER_FAILED_VERIFICATION;
- }
-
- /* Process subject. */
- name.header = NULL;
- name.beg = name.end = "";
- q = cert.subject.beg;
- /* we have to look to the last occurrence of a commonName in the
- distinguished one to get the most significant one. */
- while(q < cert.subject.end) {
- q = getASN1Element(&dn, q, cert.subject.end);
- if(!q)
- break;
- for(p = dn.beg; p < dn.end;) {
- p = getASN1Element(&elem, p, dn.end);
- if(!p)
- return CURLE_PEER_FAILED_VERIFICATION;
- /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */
- elem.beg = checkOID(elem.beg, elem.end, cnOID);
- if(elem.beg)
- name = elem; /* Latch CN. */
- }
- }
-
- /* Check the CN if found. */
- if(!getASN1Element(&elem, name.beg, name.end))
- failf(data, "SSL: unable to obtain common name from peer certificate");
- else {
- len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end);
- if(len < 0) {
- free(dnsname);
- return CURLE_OUT_OF_MEMORY;
- }
- 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)) {
- infof(data, " common name: %s (matched)", dnsname);
- free(dnsname);
- return CURLE_OK;
+ /* Build the certificate string. */
+ result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
+ if(!result) {
+ size_t j = 0;
+
+ while(!result && (j < clen)) {
+ size_t chunksize = (clen - j) > 64 ? 64 : (clen - j);
+ result = Curl_dyn_addn(&out, &certptr[j], chunksize);
+ if(!result)
+ result = Curl_dyn_addn(&out, "\n", 1);
+ j += chunksize;
}
- else
- failf(data, "SSL: certificate subject name '%s' does not match "
- "target host name '%s'", dnsname, connssl->dispname);
- free(dnsname);
+ if(!result)
+ result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n");
}
+ free(certptr);
+ if(!result)
+ if(data->set.ssl.certinfo)
+ result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out);
- return CURLE_PEER_FAILED_VERIFICATION;
+done:
+ Curl_dyn_free(&out);
+ return result;
}
-#endif /* WANT_VERIFYHOST */
+#endif /* WANT_EXTRACT_CERTINFO */
+
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */