summaryrefslogtreecommitdiff
path: root/lib/vtls
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vtls')
-rw-r--r--lib/vtls/bearssl.c371
-rw-r--r--lib/vtls/bearssl.h4
-rw-r--r--lib/vtls/gskit.c110
-rw-r--r--lib/vtls/gskit.h4
-rw-r--r--lib/vtls/gtls.c131
-rw-r--r--lib/vtls/gtls.h4
-rw-r--r--lib/vtls/hostcheck.c142
-rw-r--r--lib/vtls/hostcheck.h (renamed from lib/vtls/mesalink.h)19
-rw-r--r--lib/vtls/keylog.c5
-rw-r--r--lib/vtls/keylog.h4
-rw-r--r--lib/vtls/mbedtls.c146
-rw-r--r--lib/vtls/mbedtls.h4
-rw-r--r--lib/vtls/mbedtls_threadlock.c4
-rw-r--r--lib/vtls/mbedtls_threadlock.h4
-rw-r--r--lib/vtls/mesalink.c679
-rw-r--r--lib/vtls/nss.c139
-rw-r--r--lib/vtls/nssg.h4
-rw-r--r--lib/vtls/openssl.c1117
-rw-r--r--lib/vtls/openssl.h25
-rw-r--r--lib/vtls/rustls.c88
-rw-r--r--lib/vtls/rustls.h4
-rw-r--r--lib/vtls/schannel.c590
-rw-r--r--lib/vtls/schannel.h98
-rw-r--r--lib/vtls/schannel_verify.c17
-rw-r--r--lib/vtls/sectransp.c68
-rw-r--r--lib/vtls/sectransp.h4
-rw-r--r--lib/vtls/vtls.c103
-rw-r--r--lib/vtls/vtls.h24
-rw-r--r--lib/vtls/wolfssl.c88
-rw-r--r--lib/vtls/wolfssl.h4
-rw-r--r--lib/vtls/x509asn1.c1426
-rw-r--r--lib/vtls/x509asn1.h80
32 files changed, 3768 insertions, 1742 deletions
diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c
index 9b772d064..1221ce8c8 100644
--- a/lib/vtls/bearssl.c
+++ b/lib/vtls/bearssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2021, Michael Forney, <mforney@mforney.org>
+ * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,6 +18,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
@@ -35,12 +37,15 @@
#include "multiif.h"
#include "curl_printf.h"
#include "curl_memory.h"
+#include "strcase.h"
struct x509_context {
const br_x509_class *vtable;
br_x509_minimal_context minimal;
+ br_x509_decoder_context decoder;
bool verifyhost;
bool verifypeer;
+ int cert_num;
};
struct ssl_backend_data {
@@ -71,9 +76,9 @@ struct cafile_parser {
#define CAFILE_SOURCE_PATH 1
#define CAFILE_SOURCE_BLOB 2
struct cafile_source {
- const int type;
- const char * const data;
- const size_t len;
+ int type;
+ const char *data;
+ size_t len;
};
static void append_dn(void *ctx, const void *buf, size_t len)
@@ -159,6 +164,18 @@ static CURLcode load_cafile(struct cafile_source *source,
if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE"))
break;
br_x509_decoder_init(&ca.xc, append_dn, &ca);
+ ca.in_cert = TRUE;
+ ca.dn_len = 0;
+ break;
+ case BR_PEM_END_OBJ:
+ if(!ca.in_cert)
+ break;
+ ca.in_cert = FALSE;
+ if(br_x509_decoder_last_error(&ca.xc)) {
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+ goto fail;
+ }
+ /* add trust anchor */
if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) {
ca.err = CURLE_OUT_OF_MEMORY;
goto fail;
@@ -172,19 +189,8 @@ static CURLcode load_cafile(struct cafile_source *source,
}
ca.anchors = new_anchors;
ca.anchors_len = new_anchors_len;
- ca.in_cert = TRUE;
- ca.dn_len = 0;
ta = &ca.anchors[ca.anchors_len - 1];
ta->dn.data = NULL;
- break;
- case BR_PEM_END_OBJ:
- if(!ca.in_cert)
- break;
- ca.in_cert = FALSE;
- if(br_x509_decoder_last_error(&ca.xc)) {
- ca.err = CURLE_SSL_CACERT_BADFILE;
- goto fail;
- }
ta->flags = 0;
if(br_x509_decoder_isCA(&ca.xc))
ta->flags |= BR_X509_TA_CA;
@@ -238,6 +244,8 @@ static CURLcode load_cafile(struct cafile_source *source,
} while(source->type != CAFILE_SOURCE_BLOB);
if(fp && ferror(fp))
ca.err = CURLE_READ_ERROR;
+ else if(ca.in_cert)
+ ca.err = CURLE_SSL_CACERT_BADFILE;
fail:
if(fp)
@@ -260,6 +268,11 @@ static void x509_start_chain(const br_x509_class **ctx,
{
struct x509_context *x509 = (struct x509_context *)ctx;
+ if(!x509->verifypeer) {
+ x509->cert_num = 0;
+ return;
+ }
+
if(!x509->verifyhost)
server_name = NULL;
x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name);
@@ -269,6 +282,13 @@ static void x509_start_cert(const br_x509_class **ctx, uint32_t length)
{
struct x509_context *x509 = (struct x509_context *)ctx;
+ if(!x509->verifypeer) {
+ /* Only decode the first cert in the chain to obtain the public key */
+ if(x509->cert_num == 0)
+ br_x509_decoder_init(&x509->decoder, NULL, NULL);
+ return;
+ }
+
x509->minimal.vtable->start_cert(&x509->minimal.vtable, length);
}
@@ -277,6 +297,12 @@ static void x509_append(const br_x509_class **ctx, const unsigned char *buf,
{
struct x509_context *x509 = (struct x509_context *)ctx;
+ if(!x509->verifypeer) {
+ if(x509->cert_num == 0)
+ br_x509_decoder_push(&x509->decoder, buf, len);
+ return;
+ }
+
x509->minimal.vtable->append(&x509->minimal.vtable, buf, len);
}
@@ -284,21 +310,23 @@ static void x509_end_cert(const br_x509_class **ctx)
{
struct x509_context *x509 = (struct x509_context *)ctx;
+ if(!x509->verifypeer) {
+ x509->cert_num++;
+ return;
+ }
+
x509->minimal.vtable->end_cert(&x509->minimal.vtable);
}
static unsigned x509_end_chain(const br_x509_class **ctx)
{
struct x509_context *x509 = (struct x509_context *)ctx;
- unsigned err;
- err = x509->minimal.vtable->end_chain(&x509->minimal.vtable);
- if(err && !x509->verifypeer) {
- /* ignore any X.509 errors */
- err = BR_ERR_OK;
+ if(!x509->verifypeer) {
+ return br_x509_decoder_last_error(&x509->decoder);
}
- return err;
+ return x509->minimal.vtable->end_chain(&x509->minimal.vtable);
}
static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
@@ -306,6 +334,15 @@ static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
{
struct x509_context *x509 = (struct x509_context *)ctx;
+ if(!x509->verifypeer) {
+ /* Nothing in the chain is verified, just return the public key of the
+ first certificate and allow its usage for both TLS_RSA_* and
+ TLS_ECDHE_* */
+ if(usages)
+ *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN;
+ return br_x509_decoder_get_pkey(&x509->decoder);
+ }
+
return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages);
}
@@ -319,6 +356,216 @@ static const br_x509_class x509_vtable = {
x509_get_pkey
};
+struct st_cipher {
+ const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */
+ const char *alias_name; /* Alias name is the same as OpenSSL cipher name */
+ uint16_t num; /* BearSSL cipher suite */
+};
+
+/* Macro to initialize st_cipher data structure */
+#define CIPHER_DEF(num, alias) { #num, alias, BR_##num }
+
+static const struct st_cipher ciphertable[] = {
+ /* RFC 2246 TLS 1.0 */
+ CIPHER_DEF(TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */
+ "DES-CBC3-SHA"),
+
+ /* RFC 3268 TLS 1.0 AES */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */
+ "AES128-SHA"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */
+ "AES256-SHA"),
+
+ /* RFC 5246 TLS 1.2 */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */
+ "AES128-SHA256"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */
+ "AES256-SHA256"),
+
+ /* RFC 5288 TLS 1.2 AES GCM */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */
+ "AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */
+ "AES256-GCM-SHA384"),
+
+ /* RFC 4492 TLS 1.0 ECC */
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */
+ "ECDH-ECDSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */
+ "ECDH-ECDSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */
+ "ECDH-ECDSA-AES256-SHA"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */
+ "ECDHE-ECDSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */
+ "ECDHE-ECDSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */
+ "ECDHE-ECDSA-AES256-SHA"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */
+ "ECDH-RSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */
+ "ECDH-RSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */
+ "ECDH-RSA-AES256-SHA"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */
+ "ECDHE-RSA-DES-CBC3-SHA"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */
+ "ECDHE-RSA-AES128-SHA"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */
+ "ECDHE-RSA-AES256-SHA"),
+
+ /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */
+ "ECDHE-ECDSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */
+ "ECDHE-ECDSA-AES256-SHA384"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */
+ "ECDH-ECDSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */
+ "ECDH-ECDSA-AES256-SHA384"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */
+ "ECDHE-RSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */
+ "ECDHE-RSA-AES256-SHA384"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */
+ "ECDH-RSA-AES128-SHA256"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */
+ "ECDH-RSA-AES256-SHA384"),
+
+ /* RFC 5289 TLS 1.2 GCM */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */
+ "ECDHE-ECDSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */
+ "ECDHE-ECDSA-AES256-GCM-SHA384"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */
+ "ECDH-ECDSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */
+ "ECDH-ECDSA-AES256-GCM-SHA384"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */
+ "ECDHE-RSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */
+ "ECDHE-RSA-AES256-GCM-SHA384"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */
+ "ECDH-RSA-AES128-GCM-SHA256"),
+ CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */
+ "ECDH-RSA-AES256-GCM-SHA384"),
+#ifdef BR_TLS_RSA_WITH_AES_128_CCM
+
+ /* RFC 6655 TLS 1.2 CCM
+ Supported since BearSSL 0.6 */
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */
+ "AES128-CCM"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */
+ "AES256-CCM"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */
+ "AES128-CCM8"),
+ CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */
+ "AES256-CCM8"),
+
+ /* RFC 7251 TLS 1.2 ECC CCM
+ Supported since BearSSL 0.6 */
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */
+ "ECDHE-ECDSA-AES128-CCM"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */
+ "ECDHE-ECDSA-AES256-CCM"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */
+ "ECDHE-ECDSA-AES128-CCM8"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */
+ "ECDHE-ECDSA-AES256-CCM8"),
+#endif
+
+ /* RFC 7905 TLS 1.2 ChaCha20-Poly1305
+ Supported since BearSSL 0.2 */
+ CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */
+ "ECDHE-RSA-CHACHA20-POLY1305"),
+ CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */
+ "ECDHE-ECDSA-CHACHA20-POLY1305"),
+};
+
+#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0]))
+#define CIPHER_NAME_BUF_LEN 64
+
+static bool is_separator(char c)
+{
+ /* Return whether character is a cipher list separator. */
+ switch(c) {
+ case ' ':
+ case '\t':
+ case ':':
+ case ',':
+ case ';':
+ return true;
+ }
+ return false;
+}
+
+static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
+ br_ssl_engine_context *ssl_eng,
+ const char *ciphers)
+{
+ 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;
+
+ if(!cipher_start)
+ return CURLE_SSL_CIPHER;
+
+ while(true) {
+ /* Extract the next cipher name from the ciphers string */
+ while(is_separator(*cipher_start))
+ ++cipher_start;
+ if(*cipher_start == '\0')
+ break;
+ cipher_end = cipher_start;
+ while(*cipher_end != '\0' && !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';
+ 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)) {
+ for(i = 0; i < NUM_OF_CIPHERS &&
+ !strcasecompare(cipher_name, ciphertable[i].name); ++i);
+ }
+ else {
+ for(i = 0; i < NUM_OF_CIPHERS &&
+ !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i);
+ }
+ if(i == NUM_OF_CIPHERS) {
+ infof(data, "BearSSL: unknown cipher in list: %s", cipher_name);
+ continue;
+ }
+
+ /* No duplicates allowed */
+ for(j = 0; j < selected_count &&
+ selected_ciphers[j] != ciphertable[i].num; j++);
+ if(j < selected_count) {
+ infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name);
+ continue;
+ }
+
+ DEBUGASSERT(selected_count < NUM_OF_CIPHERS);
+ selected_ciphers[selected_count] = ciphertable[i].num;
+ ++selected_count;
+ }
+
+ if(selected_count == 0) {
+ failf(data, "BearSSL: no supported cipher in list");
+ return CURLE_SSL_CIPHER;
+ }
+
+ br_ssl_engine_set_suites(ssl_eng, selected_ciphers, selected_count);
+ return CURLE_OK;
+}
+
static CURLcode bearssl_connect_step1(struct Curl_easy *data,
struct connectdata *conn, int sockindex)
{
@@ -339,6 +586,8 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
struct in_addr addr;
#endif
+ DEBUGASSERT(backend);
+
switch(SSL_CONN_CONFIG(version)) {
case CURL_SSLVERSION_SSLv2:
failf(data, "BearSSL does not support SSLv2");
@@ -369,11 +618,11 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
}
if(ca_info_blob) {
- struct cafile_source source = {
- CAFILE_SOURCE_BLOB,
- ca_info_blob->data,
- ca_info_blob->len,
- };
+ struct cafile_source source;
+ source.type = CAFILE_SOURCE_BLOB;
+ source.data = ca_info_blob->data;
+ source.len = ca_info_blob->len;
+
ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
if(ret != CURLE_OK) {
if(verifypeer) {
@@ -386,11 +635,11 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
}
if(ssl_cafile) {
- struct cafile_source source = {
- CAFILE_SOURCE_PATH,
- ssl_cafile,
- 0,
- };
+ struct cafile_source source;
+ source.type = CAFILE_SOURCE_PATH;
+ source.data = ssl_cafile;
+ source.len = 0;
+
ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
if(ret != CURLE_OK) {
if(verifypeer) {
@@ -410,6 +659,15 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf,
sizeof(backend->buf), 1);
+ if(SSL_CONN_CONFIG(cipher_list)) {
+ /* Override the ciphers as specified. For the default cipher list see the
+ BearSSL source code of br_ssl_client_init_full() */
+ ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng,
+ SSL_CONN_CONFIG(cipher_list));
+ if(ret)
+ return ret;
+ }
+
/* initialize X.509 context */
backend->x509.vtable = &x509_vtable;
backend->x509.verifypeer = verifypeer;
@@ -442,12 +700,12 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
#endif
) {
backend->protocols[cur++] = ALPN_H2;
- infof(data, "ALPN, offering %s", ALPN_H2);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
}
#endif
backend->protocols[cur++] = ALPN_HTTP_1_1;
- infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
br_ssl_engine_set_protocol_names(&backend->ctx.eng,
backend->protocols, cur);
@@ -465,8 +723,28 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
}
hostname = NULL;
}
+ else {
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ hostname = snihost;
+ }
- if(!br_ssl_client_reset(&backend->ctx, hostname, 0))
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ ret = (*data->set.ssl.fsslctx)(data, &backend->ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(ret) {
+ failf(data, "BearSSL: error signaled by ssl ctx callback");
+ return ret;
+ }
+ }
+
+ if(!br_ssl_client_reset(&backend->ctx, hostname, 1))
return CURLE_FAILED_INIT;
backend->active = TRUE;
@@ -488,6 +766,8 @@ static CURLcode bearssl_run_until(struct Curl_easy *data,
ssize_t ret;
int err;
+ DEBUGASSERT(backend);
+
for(;;) {
state = br_ssl_engine_current_state(&backend->ctx.eng);
if(state & BR_SSL_CLOSED) {
@@ -560,6 +840,8 @@ static CURLcode bearssl_connect_step2(struct Curl_easy *data,
struct ssl_backend_data *backend = connssl->backend;
CURLcode ret;
+ DEBUGASSERT(backend);
+
ret = bearssl_run_until(data, conn, sockindex,
BR_SSL_SENDAPP | BR_SSL_RECVAPP);
if(ret == CURLE_AGAIN)
@@ -582,28 +864,29 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data,
CURLcode ret;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
if(conn->bits.tls_enable_alpn) {
const char *protocol;
protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
if(protocol) {
- infof(data, "ALPN, server accepted to use %s", protocol);
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol);
#ifdef USE_HTTP2
if(!strcmp(protocol, ALPN_H2))
- conn->negnpn = CURL_HTTP_VERSION_2;
+ conn->alpn = CURL_HTTP_VERSION_2;
else
#endif
if(!strcmp(protocol, ALPN_HTTP_1_1))
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ conn->alpn = CURL_HTTP_VERSION_1_1;
else
infof(data, "ALPN, unrecognized protocol %s", protocol);
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
else
- infof(data, "ALPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
}
if(SSL_SET_OPTION(primary.sessionid)) {
@@ -647,6 +930,8 @@ static ssize_t bearssl_send(struct Curl_easy *data, int sockindex,
unsigned char *app;
size_t applen;
+ DEBUGASSERT(backend);
+
for(;;) {
*err = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP);
if (*err != CURLE_OK)
@@ -680,6 +965,8 @@ static ssize_t bearssl_recv(struct Curl_easy *data, int sockindex,
unsigned char *app;
size_t applen;
+ DEBUGASSERT(backend);
+
*err = bearssl_run_until(data, conn, sockindex, BR_SSL_RECVAPP);
if(*err != CURLE_OK)
return -1;
@@ -805,6 +1092,7 @@ static bool bearssl_data_pending(const struct connectdata *conn,
{
const struct ssl_connect_data *connssl = &conn->ssl[connindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP;
}
@@ -854,6 +1142,7 @@ static void *bearssl_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
{
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
return &backend->ctx;
}
@@ -864,6 +1153,8 @@ static void bearssl_close(struct Curl_easy *data,
struct ssl_backend_data *backend = connssl->backend;
size_t i;
+ DEBUGASSERT(backend);
+
if(backend->active) {
br_ssl_engine_close(&backend->ctx.eng);
(void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED);
@@ -893,7 +1184,7 @@ static CURLcode bearssl_sha256sum(const unsigned char *input,
const struct Curl_ssl Curl_ssl_bearssl = {
{ CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
- SSLSUPP_CAINFO_BLOB,
+ SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX,
sizeof(struct ssl_backend_data),
Curl_none_init, /* init */
diff --git a/lib/vtls/bearssl.h b/lib/vtls/bearssl.h
index d72b7d0e2..512535996 100644
--- a/lib/vtls/bearssl.h
+++ b/lib/vtls/bearssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2020, Michael Forney, <mforney@mforney.org>
+ * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c
index e451f6aeb..4ee4edea6 100644
--- a/lib/vtls/gskit.c
+++ b/lib/vtls/gskit.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,6 +18,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
@@ -28,6 +30,7 @@
#include <qsoasync.h>
#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */
#include "socketpair.h"
+#include "strerror.h"
/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */
#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST
@@ -73,6 +76,7 @@
#include "connect.h" /* for the connect timeout */
#include "select.h"
#include "strcase.h"
+#include "timediff.h"
#include "x509asn1.h"
#include "curl_printf.h"
@@ -247,10 +251,10 @@ static CURLcode set_enum(struct Curl_easy *data, gsk_handle h,
static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h,
- GSK_BUF_ID id, const char *buffer, bool unsupported_ok)
+ GSK_BUF_ID id, const char *buf, bool unsupported_ok)
{
char buffer[STRERROR_LEN];
- int rc = gsk_attribute_set_buffer(h, id, buffer, 0);
+ int rc = gsk_attribute_set_buffer(h, id, buf, 0);
switch(rc) {
case GSK_OK:
@@ -291,27 +295,6 @@ static CURLcode set_numeric(struct Curl_easy *data,
}
-static CURLcode set_callback(struct Curl_easy *data,
- gsk_handle h, GSK_CALLBACK_ID id, void *info)
-{
- char buffer[STRERROR_LEN];
- int rc = gsk_attribute_set_callback(h, id, info);
-
- switch(rc) {
- case GSK_OK:
- return CURLE_OK;
- case GSK_ERROR_IO:
- failf(data, "gsk_attribute_set_callback() I/O error: %s",
- Curl_strerror(errno, buffer, sizeof(buffer)));
- break;
- default:
- failf(data, "gsk_attribute_set_callback(): %s", gsk_strerror(rc));
- break;
- }
- return CURLE_SSL_CONNECT_ERROR;
-}
-
-
static CURLcode set_ciphers(struct Curl_easy *data,
gsk_handle h, unsigned int *protoflags)
{
@@ -448,8 +431,7 @@ static CURLcode set_ciphers(struct Curl_easy *data,
static int gskit_init(void)
{
- /* No initialisation needed. */
-
+ /* No initialization needed. */
return 1;
}
@@ -513,6 +495,8 @@ static void cancel_async_handshake(struct connectdata *conn, int sockindex)
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
Qso_OverlappedIO_t cstat;
+ DEBUGASSERT(BACKEND);
+
if(QsoCancelOperation(conn->sock[sockindex], 0) > 0)
QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL);
}
@@ -520,6 +504,7 @@ static void cancel_async_handshake(struct connectdata *conn, int sockindex)
static void close_async_handshake(struct ssl_connect_data *connssl)
{
+ DEBUGASSERT(BACKEND);
QsoDestroyIOCompletionPort(BACKEND->iocport);
BACKEND->iocport = -1;
}
@@ -530,36 +515,36 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex,
#ifndef CURL_DISABLE_PROXY
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_connect_data *connproxyssl = &conn->proxy_ssl[sockindex];
- fd_set fds_read;
- fd_set fds_write;
+ struct pollfd fds[2];
int n;
int m;
int i;
int ret = 0;
char buf[CURL_MAX_WRITE_SIZE];
+ DEBUGASSERT(BACKEND);
+ DEBUGASSERT(connproxyssl->backend);
+
if(!connssl->use || !connproxyssl->use)
return 0; /* No SSL over SSL: OK. */
- FD_ZERO(&fds_read);
- FD_ZERO(&fds_write);
- n = -1;
+ n = 1;
+ fds[0].fd = BACKEND->remotefd;
+ fds[1].fd = conn->sock[sockindex];
+
if(directions & SOS_READ) {
- FD_SET(BACKEND->remotefd, &fds_write);
- n = BACKEND->remotefd;
+ fds[0].events |= POLLOUT;
}
if(directions & SOS_WRITE) {
- FD_SET(BACKEND->remotefd, &fds_read);
- n = BACKEND->remotefd;
- FD_SET(conn->sock[sockindex], &fds_write);
- if(n < conn->sock[sockindex])
- n = conn->sock[sockindex];
+ n = 2;
+ fds[0].events |= POLLIN;
+ fds[1].events |= POLLOUT;
}
- i = Curl_select(n + 1, &fds_read, &fds_write, NULL, 0);
+ i = Curl_poll(fds, n, 0);
if(i < 0)
return -1; /* Select error. */
- if(FD_ISSET(BACKEND->remotefd, &fds_write)) {
+ if(fds[0].revents & POLLOUT) {
/* Try getting data from HTTPS proxy and pipe it upstream. */
n = 0;
i = gsk_secure_soc_read(connproxyssl->backend->handle,
@@ -581,8 +566,7 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex,
}
}
- if(FD_ISSET(BACKEND->remotefd, &fds_read) &&
- FD_ISSET(conn->sock[sockindex], &fds_write)) {
+ if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) {
/* Pipe data to HTTPS proxy. */
n = read(BACKEND->remotefd, buf, sizeof(buf));
if(n < 0)
@@ -605,6 +589,7 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex,
static void close_one(struct ssl_connect_data *connssl, struct Curl_easy *data,
struct connectdata *conn, int sockindex)
{
+ DEBUGASSERT(BACKEND);
if(BACKEND->handle) {
gskit_status(data, gsk_secure_soc_close(&BACKEND->handle),
"gsk_secure_soc_close()", 0);
@@ -636,6 +621,8 @@ static ssize_t gskit_send(struct Curl_easy *data, int sockindex,
CURLcode cc = CURLE_SEND_ERROR;
int written;
+ DEBUGASSERT(BACKEND);
+
if(pipe_ssloverssl(conn, sockindex, SOS_WRITE) >= 0) {
cc = gskit_status(data,
gsk_secure_soc_write(BACKEND->handle,
@@ -661,6 +648,8 @@ static ssize_t gskit_recv(struct Curl_easy *data, int num, char *buf,
int nread;
CURLcode cc = CURLE_RECV_ERROR;
+ DEBUGASSERT(BACKEND);
+
if(pipe_ssloverssl(conn, num, SOS_READ) >= 0) {
int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize;
cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle,
@@ -734,6 +723,7 @@ static CURLcode gskit_connect_step1(struct Curl_easy *data,
#endif
/* Create SSL environment, start (preferably asynchronous) handshake. */
+ DEBUGASSERT(BACKEND);
BACKEND->handle = (gsk_handle) NULL;
BACKEND->iocport = -1;
@@ -787,13 +777,13 @@ static CURLcode gskit_connect_step1(struct Curl_easy *data,
BACKEND->localfd = sockpair[0];
BACKEND->remotefd = sockpair[1];
setsockopt(BACKEND->localfd, SOL_SOCKET, SO_RCVBUF,
- (void *) sobufsize, sizeof(sobufsize));
+ (void *) &sobufsize, sizeof(sobufsize));
setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_RCVBUF,
- (void *) sobufsize, sizeof(sobufsize));
+ (void *) &sobufsize, sizeof(sobufsize));
setsockopt(BACKEND->localfd, SOL_SOCKET, SO_SNDBUF,
- (void *) sobufsize, sizeof(sobufsize));
+ (void *) &sobufsize, sizeof(sobufsize));
setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_SNDBUF,
- (void *) sobufsize, sizeof(sobufsize));
+ (void *) &sobufsize, sizeof(sobufsize));
curlx_nonblock(BACKEND->localfd, TRUE);
curlx_nonblock(BACKEND->remotefd, TRUE);
}
@@ -830,8 +820,13 @@ static CURLcode gskit_connect_step1(struct Curl_easy *data,
/* Process SNI. Ignore if not supported (on OS400 < V7R1). */
if(sni) {
+ char *snihost = Curl_ssl_snihost(data, sni, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
result = set_buffer(data, BACKEND->handle,
- GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, TRUE);
+ GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE);
if(result == CURLE_UNSUPPORTED_PROTOCOL)
result = CURLE_OK;
}
@@ -958,14 +953,16 @@ static CURLcode gskit_connect_step2(struct Curl_easy *data,
CURLcode result;
/* Poll or wait for end of SSL asynchronous handshake. */
+ DEBUGASSERT(BACKEND);
for(;;) {
timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE);
+ stmv.tv_sec = 0;
+ stmv.tv_usec = 0;
if(timeout_ms < 0)
timeout_ms = 0;
- stmv.tv_sec = timeout_ms / 1000;
- stmv.tv_usec = (timeout_ms - stmv.tv_sec * 1000) * 1000;
- switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, &stmv)) {
+ switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat,
+ curlx_mstotv(&stmv, timeout_ms))) {
case 1: /* Operation complete. */
break;
case -1: /* An error occurred: handshake still in progress. */
@@ -1014,6 +1011,7 @@ static CURLcode gskit_connect_step3(struct Curl_easy *data,
CURLcode result;
/* SSL handshake done: gather certificate info and verify host. */
+ DEBUGASSERT(BACKEND);
if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle,
GSK_PARTNER_CERT_INFO,
@@ -1070,15 +1068,16 @@ static CURLcode gskit_connect_step3(struct Curl_easy *data,
/* Check pinned public key. */
ptr = SSL_PINNED_PUB_KEY();
if(!result && ptr) {
- curl_X509certificate x509;
- curl_asn1Element *p;
+ struct Curl_X509certificate x509;
+ struct Curl_asn1Element *p;
+ memset(&x509, 0, sizeof(x509));
if(Curl_parseX509(&x509, cert, certend))
return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
p = &x509.subjectPublicKeyInfo;
result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header);
if(result) {
- failf(data, "SSL: public key does not match pinned public key!");
+ failf(data, "SSL: public key does not match pinned public key");
return result;
}
}
@@ -1205,6 +1204,8 @@ static int gskit_shutdown(struct Curl_easy *data,
char buf[120];
int loop = 10; /* don't get stuck */
+ DEBUGASSERT(BACKEND);
+
if(!BACKEND->handle)
return 0;
@@ -1268,6 +1269,7 @@ static int gskit_check_cxn(struct connectdata *cxn)
int errlen;
/* The only thing that can be tested here is at the socket level. */
+ DEBUGASSERT(BACKEND);
if(!BACKEND->handle)
return 0; /* connection has been closed */
@@ -1287,6 +1289,7 @@ static void *gskit_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
{
(void)info;
+ DEBUGASSERT(BACKEND);
return BACKEND->handle;
}
@@ -1308,6 +1311,7 @@ const struct Curl_ssl Curl_ssl_gskit = {
Curl_none_cert_status_request, /* cert_status_request */
gskit_connect, /* connect */
gskit_connect_nonblocking, /* connect_nonblocking */
+ Curl_ssl_getsock, /* getsock */
gskit_get_internals, /* get_internals */
gskit_close, /* close_one */
Curl_none_close_all, /* close_all */
diff --git a/lib/vtls/gskit.h b/lib/vtls/gskit.h
index 202df7e07..cf923f6b8 100644
--- a/lib/vtls/gskit.h
+++ b/lib/vtls/gskit.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index 18864aa4b..cf3dbc523 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,6 +18,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -43,6 +45,7 @@
#include "inet_pton.h"
#include "gtls.h"
#include "vtls.h"
+#include "vauth/vauth.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
#include "select.h"
@@ -55,6 +58,14 @@
/* The last #include file should be: */
#include "memdebug.h"
+#ifdef HAVE_GNUTLS_SRP
+/* the function exists */
+#ifdef USE_TLS_SRP
+/* the functionality is not disabled */
+#define USE_GNUTLS_SRP
+#endif
+#endif
+
/* Enable GnuTLS debugging by defining GTLSDEBUG */
/*#define GTLSDEBUG */
@@ -75,7 +86,7 @@ static bool gtls_inited = FALSE;
struct ssl_backend_data {
gnutls_session_t session;
gnutls_certificate_credentials_t cred;
-#ifdef HAVE_GNUTLS_SRP
+#ifdef USE_GNUTLS_SRP
gnutls_srp_client_credentials_t srp_client_cred;
#endif
};
@@ -202,9 +213,12 @@ static CURLcode handshake(struct Curl_easy *data,
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
- gnutls_session_t session = backend->session;
+ gnutls_session_t session;
curl_socket_t sockfd = conn->sock[sockindex];
+ DEBUGASSERT(backend);
+ session = backend->session;
+
for(;;) {
timediff_t timeout_ms;
int rc;
@@ -406,6 +420,8 @@ gtls_connect_step1(struct Curl_easy *data,
const char *tls13support;
CURLcode result;
+ DEBUGASSERT(backend);
+
if(connssl->state == ssl_connection_complete)
/* to make us tolerant against being called more than once for the
same connection */
@@ -431,12 +447,13 @@ gtls_connect_step1(struct Curl_easy *data,
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef HAVE_GNUTLS_SRP
- if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) {
- infof(data, "Using TLS-SRP username: %s", SSL_SET_OPTION(username));
+#ifdef USE_GNUTLS_SRP
+ if((SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) &&
+ Curl_auth_allowed_to_host(data)) {
+ infof(data, "Using TLS-SRP username: %s",
+ SSL_SET_OPTION(primary.username));
- rc = gnutls_srp_allocate_client_credentials(
- &backend->srp_client_cred);
+ rc = gnutls_srp_allocate_client_credentials(&backend->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
gnutls_strerror(rc));
@@ -444,8 +461,8 @@ gtls_connect_step1(struct Curl_easy *data,
}
rc = gnutls_srp_set_client_credentials(backend->srp_client_cred,
- SSL_SET_OPTION(username),
- SSL_SET_OPTION(password));
+ SSL_SET_OPTION(primary.username),
+ SSL_SET_OPTION(primary.password));
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_srp_set_client_cred() failed: %s",
gnutls_strerror(rc));
@@ -502,19 +519,19 @@ gtls_connect_step1(struct Curl_easy *data,
}
#endif
- if(SSL_SET_OPTION(CRLfile)) {
+ if(SSL_SET_OPTION(primary.CRLfile)) {
/* set the CRL list file */
rc = gnutls_certificate_set_x509_crl_file(backend->cred,
- SSL_SET_OPTION(CRLfile),
+ SSL_SET_OPTION(primary.CRLfile),
GNUTLS_X509_FMT_PEM);
if(rc < 0) {
failf(data, "error reading crl file %s (%s)",
- SSL_SET_OPTION(CRLfile), gnutls_strerror(rc));
+ SSL_SET_OPTION(primary.CRLfile), gnutls_strerror(rc));
return CURLE_SSL_CRL_BADFILE;
}
else
infof(data, "found %d CRL in %s",
- rc, SSL_SET_OPTION(CRLfile));
+ rc, SSL_SET_OPTION(primary.CRLfile));
}
/* Initialize TLS session as a client */
@@ -542,11 +559,15 @@ gtls_connect_step1(struct Curl_easy *data,
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
#endif
- sni &&
- (gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname,
- strlen(hostname)) < 0))
- infof(data, "WARNING: failed to configure server name indication (SNI) "
- "TLS extension");
+ sni) {
+ size_t snilen;
+ char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
+ if(!snihost || gnutls_server_name_set(session, GNUTLS_NAME_DNS, snihost,
+ snilen) < 0) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
/* Use default priorities */
rc = gnutls_set_default_priority(session);
@@ -578,10 +599,10 @@ gtls_connect_step1(struct Curl_easy *data,
if(result)
return result;
-#ifdef HAVE_GNUTLS_SRP
+#ifdef USE_GNUTLS_SRP
/* Only add SRP to the cipher list if SRP is requested. Otherwise
* GnuTLS will disable TLS 1.3 support. */
- if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) {
+ if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) {
size_t len = strlen(prioritylist);
char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
@@ -600,7 +621,7 @@ gtls_connect_step1(struct Curl_easy *data,
#endif
infof(data, "GnuTLS ciphers: %s", prioritylist);
rc = gnutls_priority_set_direct(session, prioritylist, &err);
-#ifdef HAVE_GNUTLS_SRP
+#ifdef USE_GNUTLS_SRP
}
#endif
@@ -623,14 +644,14 @@ gtls_connect_step1(struct Curl_easy *data,
protocols[cur].data = (unsigned char *)ALPN_H2;
protocols[cur].size = ALPN_H2_LENGTH;
cur++;
- infof(data, "ALPN, offering %.*s", ALPN_H2_LENGTH, ALPN_H2);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
}
#endif
protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
cur++;
- infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) {
failf(data, "failed setting ALPN");
@@ -674,9 +695,9 @@ gtls_connect_step1(struct Curl_easy *data,
}
}
-#ifdef HAVE_GNUTLS_SRP
+#ifdef USE_GNUTLS_SRP
/* put the credentials to the current session */
- if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) {
+ if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) {
rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP,
backend->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
@@ -697,7 +718,10 @@ gtls_connect_step1(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
if(conn->proxy_ssl[sockindex].use) {
- transport_ptr = conn->proxy_ssl[sockindex].backend->session;
+ struct ssl_backend_data *proxy_backend;
+ proxy_backend = conn->proxy_ssl[sockindex].backend;
+ DEBUGASSERT(proxy_backend);
+ transport_ptr = proxy_backend->session;
gnutls_transport_push = gtls_push_ssl;
gnutls_transport_pull = gtls_pull_ssl;
}
@@ -854,9 +878,9 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
if(SSL_CONN_CONFIG(verifypeer) ||
SSL_CONN_CONFIG(verifyhost) ||
SSL_CONN_CONFIG(issuercert)) {
-#ifdef HAVE_GNUTLS_SRP
- if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP
- && SSL_SET_OPTION(username) != NULL
+#ifdef USE_GNUTLS_SRP
+ if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP
+ && SSL_SET_OPTION(primary.username)
&& !SSL_CONN_CONFIG(verifypeer)
&& gnutls_cipher_get(session)) {
/* no peer cert, but auth is ok if we have SRP user and cipher and no
@@ -867,7 +891,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
failf(data, "failed to get server cert");
*certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND;
return CURLE_PEER_FAILED_VERIFICATION;
-#ifdef HAVE_GNUTLS_SRP
+#ifdef USE_GNUTLS_SRP
}
#endif
}
@@ -914,7 +938,8 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
failf(data, "server certificate verification failed. CAfile: %s "
"CRLfile: %s", SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile):
"none",
- SSL_SET_OPTION(CRLfile)?SSL_SET_OPTION(CRLfile):"none");
+ SSL_SET_OPTION(primary.CRLfile) ?
+ SSL_SET_OPTION(primary.CRLfile) : "none");
return CURLE_PEER_FAILED_VERIFICATION;
}
else
@@ -1186,7 +1211,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
if(ptr) {
result = pkp_pin_peer_pubkey(data, x509_cert, ptr);
if(result != CURLE_OK) {
- failf(data, "SSL: public key does not match pinned public key!");
+ failf(data, "SSL: public key does not match pinned public key");
gnutls_x509_crt_deinit(x509_cert);
return result;
}
@@ -1243,26 +1268,26 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
if(conn->bits.tls_enable_alpn) {
rc = gnutls_alpn_get_selected_protocol(session, &proto);
if(rc == 0) {
- infof(data, "ALPN, server accepted to use %.*s", proto.size,
- proto.data);
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size,
+ proto.data);
#ifdef USE_HTTP2
if(proto.size == ALPN_H2_LENGTH &&
!memcmp(ALPN_H2, proto.data,
ALPN_H2_LENGTH)) {
- conn->negnpn = CURL_HTTP_VERSION_2;
+ conn->alpn = CURL_HTTP_VERSION_2;
}
else
#endif
if(proto.size == ALPN_HTTP_1_1_LENGTH &&
!memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) {
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ conn->alpn = CURL_HTTP_VERSION_1_1;
}
}
else
- infof(data, "ALPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
@@ -1352,7 +1377,9 @@ gtls_connect_common(struct Curl_easy *data,
/* Finish connecting once the handshake is done */
if(ssl_connect_1 == connssl->connecting_state) {
struct ssl_backend_data *backend = connssl->backend;
- gnutls_session_t session = backend->session;
+ gnutls_session_t session;
+ DEBUGASSERT(backend);
+ session = backend->session;
rc = Curl_gtls_verifyserver(data, conn, session, sockindex);
if(rc)
return rc;
@@ -1393,6 +1420,9 @@ static bool gtls_data_pending(const struct connectdata *conn,
const struct ssl_connect_data *connssl = &conn->ssl[connindex];
bool res = FALSE;
struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
if(backend->session &&
0 != gnutls_record_check_pending(backend->session))
res = TRUE;
@@ -1400,6 +1430,7 @@ static bool gtls_data_pending(const struct connectdata *conn,
#ifndef CURL_DISABLE_PROXY
connssl = &conn->proxy_ssl[connindex];
backend = connssl->backend;
+ DEBUGASSERT(backend);
if(backend->session &&
0 != gnutls_record_check_pending(backend->session))
res = TRUE;
@@ -1417,7 +1448,10 @@ static ssize_t gtls_send(struct Curl_easy *data,
struct connectdata *conn = data->conn;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
- ssize_t rc = gnutls_record_send(backend->session, mem, len);
+ ssize_t rc;
+
+ DEBUGASSERT(backend);
+ rc = gnutls_record_send(backend->session, mem, len);
if(rc < 0) {
*curlcode = (rc == GNUTLS_E_AGAIN)
@@ -1433,6 +1467,8 @@ static ssize_t gtls_send(struct Curl_easy *data,
static void close_one(struct ssl_connect_data *connssl)
{
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
if(backend->session) {
char buf[32];
/* Maybe the server has already sent a close notify alert.
@@ -1446,7 +1482,7 @@ static void close_one(struct ssl_connect_data *connssl)
gnutls_certificate_free_credentials(backend->cred);
backend->cred = NULL;
}
-#ifdef HAVE_GNUTLS_SRP
+#ifdef USE_GNUTLS_SRP
if(backend->srp_client_cred) {
gnutls_srp_free_client_credentials(backend->srp_client_cred);
backend->srp_client_cred = NULL;
@@ -1475,6 +1511,8 @@ static int gtls_shutdown(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *backend = connssl->backend;
int retval = 0;
+ DEBUGASSERT(backend);
+
#ifndef CURL_DISABLE_FTP
/* This has only been tested on the proftpd server, and the mod_tls code
sends a close notify alert without waiting for a close notify alert in
@@ -1530,9 +1568,9 @@ static int gtls_shutdown(struct Curl_easy *data, struct connectdata *conn,
}
gnutls_certificate_free_credentials(backend->cred);
-#ifdef HAVE_GNUTLS_SRP
- if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP
- && SSL_SET_OPTION(username) != NULL)
+#ifdef USE_GNUTLS_SRP
+ if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP
+ && SSL_SET_OPTION(primary.username) != NULL)
gnutls_srp_free_client_credentials(backend->srp_client_cred);
#endif
@@ -1553,6 +1591,8 @@ static ssize_t gtls_recv(struct Curl_easy *data, /* connection data */
struct ssl_backend_data *backend = connssl->backend;
ssize_t ret;
+ DEBUGASSERT(backend);
+
ret = gnutls_record_recv(backend->session, buf, buffersize);
if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
*curlcode = CURLE_AGAIN;
@@ -1624,6 +1664,7 @@ static void *gtls_get_internals(struct ssl_connect_data *connssl,
{
struct ssl_backend_data *backend = connssl->backend;
(void)info;
+ DEBUGASSERT(backend);
return backend->session;
}
diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h
index 642d5f093..abade73f8 100644
--- a/lib/vtls/gtls.h
+++ b/lib/vtls/gtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/hostcheck.c b/lib/vtls/hostcheck.c
new file mode 100644
index 000000000..2a648f20a
--- /dev/null
+++ b/lib/vtls/hostcheck.c
@@ -0,0 +1,142 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_OPENSSL) \
+ || defined(USE_GSKIT) \
+ || defined(USE_SCHANNEL)
+/* these backends use functions from this file */
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#include "curl_memrchr.h"
+
+#include "hostcheck.h"
+#include "strcase.h"
+#include "hostip.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* check the two input strings with given length, but do not
+ assume they end in nul-bytes */
+static bool pmatch(const char *hostname, size_t hostlen,
+ const char *pattern, size_t patternlen)
+{
+ if(hostlen != patternlen)
+ return FALSE;
+ return strncasecompare(hostname, pattern, hostlen);
+}
+
+/*
+ * Match a hostname against a wildcard pattern.
+ * E.g.
+ * "foo.host.com" matches "*.host.com".
+ *
+ * We use the matching rule described in RFC6125, section 6.4.3.
+ * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3
+ *
+ * In addition: ignore trailing dots in the host names and wildcards, so that
+ * the names are used normalized. This is what the browsers do.
+ *
+ * Do not allow wildcard matching on IP numbers. There are apparently
+ * certificates being used with an IP address in the CN field, thus making no
+ * apparent distinction between a name and an IP. We need to detect the use of
+ * an IP address and not wildcard match on such names.
+ *
+ * Return TRUE on a match. FALSE if not.
+ */
+
+static bool hostmatch(const char *hostname,
+ size_t hostlen,
+ const char *pattern,
+ size_t patternlen)
+{
+ const char *pattern_label_end, *wildcard, *hostname_label_end;
+ size_t prefixlen, suffixlen;
+
+ /* normalize pattern and hostname by stripping off trailing dots */
+ DEBUGASSERT(patternlen);
+ if(hostname[hostlen-1]=='.')
+ hostlen--;
+ if(pattern[patternlen-1]=='.')
+ patternlen--;
+
+ wildcard = memchr(pattern, '*', patternlen);
+ if(!wildcard)
+ return pmatch(hostname, hostlen, pattern, patternlen);
+
+ /* detect IP address as hostname and fail the match if so */
+ if(Curl_host_is_ipnum(hostname))
+ return FALSE;
+
+ /* We require at least 2 dots in the pattern to avoid too wide wildcard
+ match. */
+ pattern_label_end = memchr(pattern, '.', patternlen);
+ if(!pattern_label_end ||
+ (memrchr(pattern, '.', patternlen) == pattern_label_end) ||
+ strncasecompare(pattern, "xn--", 4))
+ return pmatch(hostname, hostlen, pattern, patternlen);
+
+ hostname_label_end = memchr(hostname, '.', hostlen);
+ if(!hostname_label_end)
+ return FALSE;
+ else {
+ size_t skiphost = hostname_label_end - hostname;
+ size_t skiplen = pattern_label_end - pattern;
+ if(!pmatch(hostname_label_end, hostlen - skiphost,
+ pattern_label_end, patternlen - skiplen))
+ return FALSE;
+ }
+ /* The wildcard must match at least one character, so the left-most
+ label of the hostname is at least as large as the left-most label
+ of the pattern. */
+ if(hostname_label_end - hostname < pattern_label_end - pattern)
+ return FALSE;
+
+ prefixlen = wildcard - pattern;
+ suffixlen = pattern_label_end - (wildcard + 1);
+ return strncasecompare(pattern, hostname, prefixlen) &&
+ strncasecompare(wildcard + 1, hostname_label_end - suffixlen,
+ suffixlen) ? TRUE : FALSE;
+}
+
+/*
+ * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not.
+ */
+bool Curl_cert_hostcheck(const char *match, size_t matchlen,
+ const char *hostname, size_t hostlen)
+{
+ if(match && *match && hostname && *hostname)
+ return hostmatch(hostname, hostlen, match, matchlen);
+ return FALSE;
+}
+
+#endif /* OPENSSL, GSKIT or schannel+wince */
diff --git a/lib/vtls/mesalink.h b/lib/vtls/hostcheck.h
index 03f520c1d..d3c4eab56 100644
--- a/lib/vtls/mesalink.h
+++ b/lib/vtls/hostcheck.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_MESALINK_H
-#define HEADER_CURL_MESALINK_H
+#ifndef HEADER_CURL_HOSTCHECK_H
+#define HEADER_CURL_HOSTCHECK_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,8 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com>
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -21,12 +20,14 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
-#include "curl_setup.h"
-#ifdef USE_MESALINK
+#include <curl/curl.h>
-extern const struct Curl_ssl Curl_ssl_mesalink;
+/* returns TRUE if there's a match */
+bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen,
+ const char *hostname, size_t hostlen);
-#endif /* USE_MESALINK */
-#endif /* HEADER_CURL_MESALINK_H */
+#endif /* HEADER_CURL_HOSTCHECK_H */
diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c
index a45945f8f..1952a690c 100644
--- a/lib/vtls/keylog.c
+++ b/lib/vtls/keylog.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,10 +18,13 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
#include "keylog.h"
+#include <curl/curl.h>
/* The last #include files should be: */
#include "curl_memory.h"
diff --git a/lib/vtls/keylog.h b/lib/vtls/keylog.h
index 63626da90..5d3c675b3 100644
--- a/lib/vtls/keylog.h
+++ b/lib/vtls/keylog.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
index 1d209b273..fbde8976e 100644
--- a/lib/vtls/mbedtls.c
+++ b/lib/vtls/mbedtls.c
@@ -19,6 +19,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -41,9 +43,6 @@
#include <mbedtls/net.h>
#endif
#include <mbedtls/ssl.h>
-#if MBEDTLS_VERSION_NUMBER < 0x03000000
-#include <mbedtls/certs.h>
-#endif
#include <mbedtls/x509.h>
#include <mbedtls/error.h>
@@ -73,17 +72,28 @@
#include "curl_memory.h"
#include "memdebug.h"
+/* ALPN for http2 */
+#ifdef USE_HTTP2
+# undef HAS_ALPN
+# ifdef MBEDTLS_SSL_ALPN
+# define HAS_ALPN
+# endif
+#endif
+
struct ssl_backend_data {
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_context entropy;
mbedtls_ssl_context ssl;
- int server_fd;
mbedtls_x509_crt cacert;
mbedtls_x509_crt clicert;
+#ifdef MBEDTLS_X509_CRL_PARSE_C
mbedtls_x509_crl crl;
+#endif
mbedtls_pk_context pk;
mbedtls_ssl_config config;
+#ifdef HAS_ALPN
const char *protocols[3];
+#endif
};
/* apply threading? */
@@ -145,15 +155,6 @@ static void mbed_debug(void *context, int level, const char *f_name,
#else
#endif
-/* ALPN for http2? */
-#ifdef USE_NGHTTP2
-# undef HAS_ALPN
-# ifdef MBEDTLS_SSL_ALPN
-# define HAS_ALPN
-# endif
-#endif
-
-
/*
* profile
*/
@@ -231,6 +232,8 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn,
long ssl_version_max = SSL_CONN_CONFIG(version_max);
CURLcode result = CURLE_OK;
+ DEBUGASSERT(backend);
+
switch(ssl_version) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@@ -278,7 +281,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob);
- const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
+ const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile);
const char * const hostname = SSL_HOST_NAME();
#ifndef CURL_DISABLE_VERBOSE_STRINGS
const long int port = SSL_HOST_PORT();
@@ -286,6 +289,8 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
int ret = -1;
char errorbuf[128];
+ DEBUGASSERT(backend);
+
if((SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) ||
(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3)) {
failf(data, "Not supported SSL version");
@@ -300,8 +305,9 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
&ts_entropy, NULL, 0);
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s",
+ failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s",
-ret, errorbuf);
+ return CURLE_FAILED_INIT;
}
#else
mbedtls_entropy_init(&backend->entropy);
@@ -311,8 +317,9 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
&backend->entropy, NULL, 0);
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s",
+ failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s",
-ret, errorbuf);
+ return CURLE_FAILED_INIT;
}
#endif /* THREADING_SUPPORT */
@@ -335,11 +342,12 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s",
-ret, errorbuf);
- return ret;
+ return CURLE_SSL_CERTPROBLEM;
}
}
if(ssl_cafile && verifypeer) {
+#ifdef MBEDTLS_FS_IO
ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile);
if(ret<0) {
@@ -348,9 +356,14 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
ssl_cafile, -ret, errorbuf);
return CURLE_SSL_CACERT_BADFILE;
}
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
}
if(ssl_capath) {
+#ifdef MBEDTLS_FS_IO
ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath);
if(ret<0) {
@@ -361,12 +374,17 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
if(verifypeer)
return CURLE_SSL_CACERT_BADFILE;
}
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
}
/* Load the client certificate */
mbedtls_x509_crt_init(&backend->clicert);
if(ssl_cert) {
+#ifdef MBEDTLS_FS_IO
ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert);
if(ret) {
@@ -376,6 +394,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
return CURLE_SSL_CERTPROBLEM;
}
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
}
if(ssl_cert_blob) {
@@ -388,7 +410,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
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);
+ ssl_cert_blob->len + 1);
free(newblob);
if(ret) {
@@ -404,6 +426,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) {
if(SSL_SET_OPTION(key)) {
+#ifdef MBEDTLS_FS_IO
#if MBEDTLS_VERSION_NUMBER >= 0x03000000
ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key),
SSL_SET_OPTION(key_passwd),
@@ -420,6 +443,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
SSL_SET_OPTION(key), -ret, errorbuf);
return CURLE_SSL_CERTPROBLEM;
}
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
}
else {
const struct curl_blob *ssl_key_blob = SSL_SET_OPTION(key_blob);
@@ -452,9 +479,11 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
}
/* Load the CRL */
+#ifdef MBEDTLS_X509_CRL_PARSE_C
mbedtls_x509_crl_init(&backend->crl);
if(ssl_crlfile) {
+#ifdef MBEDTLS_FS_IO
ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile);
if(ret) {
@@ -464,17 +493,21 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
return CURLE_SSL_CRL_BADFILE;
}
+#else
+ failf(data, "mbedtls: functions that use the filesystem not built in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+#else
+ if(ssl_crlfile) {
+ failf(data, "mbedtls: crl support not built in");
+ return CURLE_NOT_BUILT_IN;
}
+#endif
infof(data, "mbedTLS: Connecting to %s:%ld", hostname, port);
mbedtls_ssl_config_init(&backend->config);
-
- mbedtls_ssl_init(&backend->ssl);
- if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) {
- failf(data, "mbedTLS: ssl_init failed");
- return CURLE_SSL_CONNECT_ERROR;
- }
ret = mbedtls_ssl_config_defaults(&backend->config,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
@@ -484,6 +517,12 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
return CURLE_SSL_CONNECT_ERROR;
}
+ mbedtls_ssl_init(&backend->ssl);
+ if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) {
+ failf(data, "mbedTLS: ssl_init failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
/* new profile with RSA min key len = 1024 ... */
mbedtls_ssl_conf_cert_profile(&backend->config,
&mbedtls_x509_crt_profile_fr);
@@ -555,26 +594,33 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
mbedtls_ssl_conf_ca_chain(&backend->config,
&backend->cacert,
+#ifdef MBEDTLS_X509_CRL_PARSE_C
&backend->crl);
+#else
+ NULL);
+#endif
if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) {
mbedtls_ssl_conf_own_cert(&backend->config,
&backend->clicert, &backend->pk);
}
- if(mbedtls_ssl_set_hostname(&backend->ssl, hostname)) {
- /* 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. */
- failf(data, "couldn't set hostname in mbedTLS");
- return CURLE_SSL_CONNECT_ERROR;
+ {
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) {
+ /* 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. */
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
}
#ifdef HAS_ALPN
if(conn->bits.tls_enable_alpn) {
const char **p = &backend->protocols[0];
-#ifdef USE_NGHTTP2
+#ifdef USE_HTTP2
if(data->state.httpwant >= CURL_HTTP_VERSION_2)
- *p++ = NGHTTP2_PROTO_VERSION_ID;
+ *p++ = ALPN_H2;
#endif
*p++ = ALPN_HTTP_1_1;
*p = NULL;
@@ -586,7 +632,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
return CURLE_SSL_CONNECT_ERROR;
}
for(p = &backend->protocols[0]; *p; ++p)
- infof(data, "ALPN, offering %s", *p);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p);
}
#endif
@@ -627,6 +673,8 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn,
const mbedtls_x509_crt *peercert;
const char * const pinnedpubkey = SSL_PINNED_PUB_KEY();
+ DEBUGASSERT(backend);
+
conn->recv[sockindex] = mbed_recv;
conn->send[sockindex] = mbed_send;
@@ -769,24 +817,23 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn,
const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
if(next_protocol) {
- infof(data, "ALPN, server accepted to use %s", next_protocol);
-#ifdef USE_NGHTTP2
- if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
- NGHTTP2_PROTO_VERSION_ID_LEN) &&
- !next_protocol[NGHTTP2_PROTO_VERSION_ID_LEN]) {
- conn->negnpn = CURL_HTTP_VERSION_2;
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol);
+#ifdef USE_HTTP2
+ if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) &&
+ !next_protocol[ALPN_H2_LENGTH]) {
+ conn->alpn = CURL_HTTP_VERSION_2;
}
else
#endif
if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) &&
!next_protocol[ALPN_HTTP_1_1_LENGTH]) {
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ conn->alpn = CURL_HTTP_VERSION_1_1;
}
}
else {
- infof(data, "ALPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
}
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -806,6 +853,7 @@ mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *backend = connssl->backend;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
if(SSL_SET_OPTION(primary.sessionid)) {
int ret;
@@ -862,6 +910,8 @@ static ssize_t mbed_send(struct Curl_easy *data, int sockindex,
struct ssl_backend_data *backend = connssl->backend;
int ret = -1;
+ DEBUGASSERT(backend);
+
ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
if(ret < 0) {
@@ -886,6 +936,8 @@ static void mbedtls_close(struct Curl_easy *data,
char buf[32];
(void) data;
+ DEBUGASSERT(backend);
+
/* Maybe the server has already sent a close notify alert.
Read it to avoid an RST on the TCP connection. */
(void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf));
@@ -893,7 +945,9 @@ static void mbedtls_close(struct Curl_easy *data,
mbedtls_pk_free(&backend->pk);
mbedtls_x509_crt_free(&backend->clicert);
mbedtls_x509_crt_free(&backend->cacert);
+#ifdef MBEDTLS_X509_CRL_PARSE_C
mbedtls_x509_crl_free(&backend->crl);
+#endif
mbedtls_ssl_config_free(&backend->config);
mbedtls_ssl_free(&backend->ssl);
mbedtls_ctr_drbg_free(&backend->ctr_drbg);
@@ -912,6 +966,8 @@ static ssize_t mbed_recv(struct Curl_easy *data, int num,
int ret = -1;
ssize_t len = -1;
+ DEBUGASSERT(backend);
+
ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf,
buffersize);
@@ -963,7 +1019,7 @@ static CURLcode mbedtls_random(struct Curl_easy *data,
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "Failed - mbedTLS: ctr_drbg_seed returned (-0x%04X) %s",
+ failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s",
-ret, errorbuf);
}
else {
@@ -971,7 +1027,7 @@ static CURLcode mbedtls_random(struct Curl_easy *data,
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "mbedTLS: ctr_drbg_init returned (-0x%04X) %s",
+ failf(data, "mbedtls_ctr_drbg_random returned (-0x%04X) %s",
-ret, errorbuf);
}
}
@@ -1146,6 +1202,7 @@ static bool mbedtls_data_pending(const struct connectdata *conn,
{
const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0;
}
@@ -1175,6 +1232,7 @@ static void *mbedtls_get_internals(struct ssl_connect_data *connssl,
{
struct ssl_backend_data *backend = connssl->backend;
(void)info;
+ DEBUGASSERT(backend);
return &backend->ssl;
}
diff --git a/lib/vtls/mbedtls.h b/lib/vtls/mbedtls.h
index 1abd331ea..ec3b43bf9 100644
--- a/lib/vtls/mbedtls.h
+++ b/lib/vtls/mbedtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
@@ -21,6 +21,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/mbedtls_threadlock.c b/lib/vtls/mbedtls_threadlock.c
index 751755c23..3971e69b2 100644
--- a/lib/vtls/mbedtls_threadlock.c
+++ b/lib/vtls/mbedtls_threadlock.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2013 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
@@ -19,6 +19,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/mbedtls_threadlock.h b/lib/vtls/mbedtls_threadlock.h
index e40dfc8d6..3a50d0381 100644
--- a/lib/vtls/mbedtls_threadlock.h
+++ b/lib/vtls/mbedtls_threadlock.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2013 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
@@ -21,6 +21,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/mesalink.c b/lib/vtls/mesalink.c
deleted file mode 100644
index 35a916586..000000000
--- a/lib/vtls/mesalink.c
+++ /dev/null
@@ -1,679 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com>
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all MesaLink-specific code for the TLS/SSL layer. No code
- * but vtls.c should ever call or use these functions.
- *
- */
-
-/*
- * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * Thanks for code and inspiration!
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_MESALINK
-
-#include <mesalink/options.h>
-#include <mesalink/version.h>
-
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
-#include "vtls.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "strcase.h"
-#include "x509asn1.h"
-#include "curl_printf.h"
-
-#include "mesalink.h"
-#include <mesalink/openssl/ssl.h>
-#include <mesalink/openssl/err.h>
-
-/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#define MESALINK_MAX_ERROR_SZ 80
-
-struct ssl_backend_data
-{
- SSL_CTX *ctx;
- SSL *handle;
-};
-
-static Curl_recv mesalink_recv;
-static Curl_send mesalink_send;
-
-static int do_file_type(const char *type)
-{
- if(!type || !type[0])
- return SSL_FILETYPE_PEM;
- if(strcasecompare(type, "PEM"))
- return SSL_FILETYPE_PEM;
- if(strcasecompare(type, "DER"))
- return SSL_FILETYPE_ASN1;
- return -1;
-}
-
-/*
- * This function loads all the client/CA certificates and CRLs. Setup the TLS
- * layer and do all necessary magic.
- */
-static CURLcode
-mesalink_connect_step1(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
-{
- char *ciphers;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct in_addr addr4;
-#ifdef ENABLE_IPV6
- struct in6_addr addr6;
-#endif
- const char * const hostname = SSL_HOST_NAME();
- size_t hostname_len = strlen(hostname);
- SSL_METHOD *req_method = NULL;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct ssl_backend_data *backend = connssl->backend;
-
- if(connssl->state == ssl_connection_complete)
- return CURLE_OK;
-
- if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) {
- failf(data, "MesaLink does not support to set maximum SSL/TLS version");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- switch(SSL_CONN_CONFIG(version)) {
- case CURL_SSLVERSION_SSLv3:
- case CURL_SSLVERSION_TLSv1:
- case CURL_SSLVERSION_TLSv1_0:
- case CURL_SSLVERSION_TLSv1_1:
- failf(data, "MesaLink does not support SSL 3.0, TLS 1.0, or TLS 1.1");
- return CURLE_NOT_BUILT_IN;
- case CURL_SSLVERSION_DEFAULT:
- case CURL_SSLVERSION_TLSv1_2:
- req_method = TLSv1_2_client_method();
- break;
- case CURL_SSLVERSION_TLSv1_3:
- req_method = TLSv1_3_client_method();
- break;
- case CURL_SSLVERSION_SSLv2:
- failf(data, "MesaLink does not support SSLv2");
- return CURLE_SSL_CONNECT_ERROR;
- default:
- failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- if(!req_method) {
- failf(data, "SSL: couldn't create a method!");
- return CURLE_OUT_OF_MEMORY;
- }
-
- if(backend->ctx)
- SSL_CTX_free(backend->ctx);
- backend->ctx = SSL_CTX_new(req_method);
-
- if(!backend->ctx) {
- failf(data, "SSL: couldn't create a context!");
- return CURLE_OUT_OF_MEMORY;
- }
-
- SSL_CTX_set_verify(
- backend->ctx, SSL_CONN_CONFIG(verifypeer) ?
- SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
-
- if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath)) {
- if(!SSL_CTX_load_verify_locations(backend->ctx, SSL_CONN_CONFIG(CAfile),
- SSL_CONN_CONFIG(CApath))) {
- if(SSL_CONN_CONFIG(verifypeer)) {
- failf(data,
- "error setting certificate verify locations: "
- " CAfile: %s CApath: %s",
- SSL_CONN_CONFIG(CAfile) ?
- SSL_CONN_CONFIG(CAfile) : "none",
- SSL_CONN_CONFIG(CApath) ?
- SSL_CONN_CONFIG(CApath) : "none");
- return CURLE_SSL_CACERT_BADFILE;
- }
- infof(data,
- "error setting certificate verify locations,"
- " continuing anyway:");
- }
- else {
- infof(data, "successfully set certificate verify locations:");
- }
- infof(data, " CAfile: %s",
- SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none");
- infof(data, " CApath: %s",
- SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none");
- }
-
- if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) {
- int file_type = do_file_type(SSL_SET_OPTION(cert_type));
-
- if(SSL_CTX_use_certificate_chain_file(backend->ctx,
- SSL_SET_OPTION(primary.clientcert),
- file_type) != 1) {
- failf(data, "unable to use client certificate (no key or wrong pass"
- " phrase?)");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- file_type = do_file_type(SSL_SET_OPTION(key_type));
- if(SSL_CTX_use_PrivateKey_file(backend->ctx, SSL_SET_OPTION(key),
- file_type) != 1) {
- failf(data, "unable to set private key");
- return CURLE_SSL_CONNECT_ERROR;
- }
- infof(data,
- "client cert: %s",
- SSL_CONN_CONFIG(clientcert)?
- SSL_CONN_CONFIG(clientcert): "none");
- }
-
- ciphers = SSL_CONN_CONFIG(cipher_list);
- if(ciphers) {
-#ifdef MESALINK_HAVE_CIPHER
- if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
- failf(data, "failed setting cipher list: %s", ciphers);
- return CURLE_SSL_CIPHER;
- }
-#endif
- infof(data, "Cipher selection: %s", ciphers);
- }
-
- if(backend->handle)
- SSL_free(backend->handle);
- backend->handle = SSL_new(backend->ctx);
- if(!backend->handle) {
- failf(data, "SSL: couldn't create a context (handle)!");
- return CURLE_OUT_OF_MEMORY;
- }
-
- if((hostname_len < USHRT_MAX) &&
- (0 == Curl_inet_pton(AF_INET, hostname, &addr4))
-#ifdef ENABLE_IPV6
- && (0 == Curl_inet_pton(AF_INET6, hostname, &addr6))
-#endif
- ) {
- /* hostname is not a valid IP address */
- if(SSL_set_tlsext_host_name(backend->handle, hostname) != SSL_SUCCESS) {
- failf(data,
- "WARNING: failed to configure server name indication (SNI) "
- "TLS extension\n");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
- else {
-#ifdef CURLDEBUG
- /* Check if the hostname is 127.0.0.1 or [::1];
- * otherwise reject because MesaLink always wants a valid DNS Name
- * specified in RFC 5280 Section 7.2 */
- if(strncmp(hostname, "127.0.0.1", 9) == 0
-#ifdef ENABLE_IPV6
- || strncmp(hostname, "[::1]", 5) == 0
-#endif
- ) {
- SSL_set_tlsext_host_name(backend->handle, "localhost");
- }
- else
-#endif
- {
- failf(data,
- "ERROR: MesaLink does not accept an IP address as a hostname\n");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
-#ifdef MESALINK_HAVE_SESSION
- if(SSL_SET_OPTION(primary.sessionid)) {
- void *ssl_sessionid = NULL;
-
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(data, conn,
- SSL_IS_PROXY() ? TRUE : FALSE,
- &ssl_sessionid, NULL, sockindex)) {
- /* we got a session id, use it! */
- if(!SSL_set_session(backend->handle, ssl_sessionid)) {
- Curl_ssl_sessionid_unlock(data);
- failf(
- data,
- "SSL: SSL_set_session failed: %s",
- ERR_error_string(SSL_get_error(backend->handle, 0), error_buffer));
- return CURLE_SSL_CONNECT_ERROR;
- }
- /* Informational message */
- infof(data, "SSL re-using session ID");
- }
- Curl_ssl_sessionid_unlock(data);
- }
-#endif /* MESALINK_HAVE_SESSION */
-
- if(SSL_set_fd(backend->handle, (int)sockfd) != SSL_SUCCESS) {
- failf(data, "SSL: SSL_set_fd failed");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- connssl->connecting_state = ssl_connect_2;
- return CURLE_OK;
-}
-
-static CURLcode
-mesalink_connect_step2(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
-{
- int ret = -1;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct ssl_backend_data *backend = connssl->backend;
-
- conn->recv[sockindex] = mesalink_recv;
- conn->send[sockindex] = mesalink_send;
-
- ret = SSL_connect(backend->handle);
- if(ret != SSL_SUCCESS) {
- int detail = SSL_get_error(backend->handle, ret);
-
- if(SSL_ERROR_WANT_CONNECT == detail || SSL_ERROR_WANT_READ == detail) {
- connssl->connecting_state = ssl_connect_2_reading;
- return CURLE_OK;
- }
- else {
- char error_buffer[MESALINK_MAX_ERROR_SZ];
- failf(data,
- "SSL_connect failed with error %d: %s",
- detail,
- ERR_error_string_n(detail, error_buffer, sizeof(error_buffer)));
- ERR_print_errors_fp(stderr);
- if(detail && SSL_CONN_CONFIG(verifypeer)) {
- detail &= ~0xFF;
- if(detail == TLS_ERROR_WEBPKI_ERRORS) {
- failf(data, "Cert verify failed");
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- }
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
- connssl->connecting_state = ssl_connect_3;
- infof(data,
- "SSL connection using %s / %s",
- SSL_get_version(backend->handle),
- SSL_get_cipher_name(backend->handle));
-
- return CURLE_OK;
-}
-
-static CURLcode
-mesalink_connect_step3(struct connectdata *conn, int sockindex)
-{
- CURLcode result = CURLE_OK;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
- DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
-
-#ifdef MESALINK_HAVE_SESSION
- if(SSL_SET_OPTION(primary.sessionid)) {
- bool incache;
- SSL_SESSION *our_ssl_sessionid;
- void *old_ssl_sessionid = NULL;
- bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE;
- struct ssl_backend_data *backend = connssl->backend;
-
- our_ssl_sessionid = SSL_get_session(backend->handle);
-
- Curl_ssl_sessionid_lock(data);
- incache =
- !(Curl_ssl_getsessionid(data, conn, isproxy, &old_ssl_sessionid, NULL,
- sockindex));
- if(incache) {
- if(old_ssl_sessionid != our_ssl_sessionid) {
- infof(data, "old SSL session ID is stale, removing");
- Curl_ssl_delsessionid(data, old_ssl_sessionid);
- incache = FALSE;
- }
- }
-
- if(!incache) {
- result =
- Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, 0,
- sockindex, NULL);
- if(result) {
- Curl_ssl_sessionid_unlock(data);
- failf(data, "failed to store ssl session");
- return result;
- }
- }
- Curl_ssl_sessionid_unlock(data);
- }
-#endif /* MESALINK_HAVE_SESSION */
-
- connssl->connecting_state = ssl_connect_done;
-
- return result;
-}
-
-static ssize_t
-mesalink_send(struct Curl_easy *data, int sockindex, const void *mem,
- size_t len, CURLcode *curlcode)
-{
- struct connectdata *conn = data->conn;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct ssl_backend_data *backend = connssl->backend;
- char error_buffer[MESALINK_MAX_ERROR_SZ];
- int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
- int rc = SSL_write(backend->handle, mem, memlen);
-
- if(rc < 0) {
- int err = SSL_get_error(backend->handle, rc);
- switch(err) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- /* there's data pending, re-invoke SSL_write() */
- *curlcode = CURLE_AGAIN;
- return -1;
- default:
- failf(data,
- "SSL write: %s, errno %d",
- ERR_error_string_n(err, error_buffer, sizeof(error_buffer)),
- SOCKERRNO);
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- }
- return rc;
-}
-
-static void
-mesalink_close(struct Curl_easy *data, struct connectdata *conn, int sockindex)
-{
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct ssl_backend_data *backend = connssl->backend;
-
- (void) data;
-
- if(backend->handle) {
- (void)SSL_shutdown(backend->handle);
- SSL_free(backend->handle);
- backend->handle = NULL;
- }
- if(backend->ctx) {
- SSL_CTX_free(backend->ctx);
- backend->ctx = NULL;
- }
-}
-
-static ssize_t
-mesalink_recv(struct Curl_easy *data, int num, char *buf, size_t buffersize,
- CURLcode *curlcode)
-{
- struct connectdata *conn = data->conn;
- struct ssl_connect_data *connssl = &conn->ssl[num];
- struct ssl_backend_data *backend = connssl->backend;
- char error_buffer[MESALINK_MAX_ERROR_SZ];
- int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
- int nread = SSL_read(backend->handle, buf, buffsize);
-
- if(nread <= 0) {
- int err = SSL_get_error(backend->handle, nread);
-
- switch(err) {
- case SSL_ERROR_ZERO_RETURN: /* no more data */
- case IO_ERROR_CONNECTION_ABORTED:
- break;
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- /* there's data pending, re-invoke SSL_read() */
- *curlcode = CURLE_AGAIN;
- return -1;
- default:
- failf(data,
- "SSL read: %s, errno %d",
- ERR_error_string_n(err, error_buffer, sizeof(error_buffer)),
- SOCKERRNO);
- *curlcode = CURLE_RECV_ERROR;
- return -1;
- }
- }
- return nread;
-}
-
-static size_t
-mesalink_version(char *buffer, size_t size)
-{
- return msnprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING);
-}
-
-static int
-mesalink_init(void)
-{
- return (SSL_library_init() == SSL_SUCCESS);
-}
-
-/*
- * This function is called to shut down the SSL layer but keep the
- * socket open (CCC - Clear Command Channel)
- */
-static int
-mesalink_shutdown(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
-{
- int retval = 0;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct ssl_backend_data *backend = connssl->backend;
-
- (void) data;
-
- if(backend->handle) {
- SSL_free(backend->handle);
- backend->handle = NULL;
- }
- return retval;
-}
-
-static CURLcode
-mesalink_connect_common(struct Curl_easy *data, struct connectdata *conn,
- int sockindex, bool nonblocking, bool *done)
-{
- CURLcode result;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- curl_socket_t sockfd = conn->sock[sockindex];
- timediff_t timeout_ms;
- int what;
-
- /* check if the connection has already been established */
- if(ssl_connection_complete == connssl->state) {
- *done = TRUE;
- return CURLE_OK;
- }
-
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we're allowed */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- result = mesalink_connect_step1(data, conn, sockindex);
- if(result)
- return result;
- }
-
- while(ssl_connect_2 == connssl->connecting_state ||
- ssl_connect_2_reading == connssl->connecting_state ||
- ssl_connect_2_writing == connssl->connecting_state) {
-
- /* 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, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it's available. */
- if(connssl->connecting_state == ssl_connect_2_reading ||
- connssl->connecting_state == ssl_connect_2_writing) {
-
- curl_socket_t writefd =
- ssl_connect_2_writing == connssl->connecting_state ? sockfd
- : CURL_SOCKET_BAD;
- curl_socket_t readfd = ssl_connect_2_reading == connssl->connecting_state
- ? sockfd
- : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if
- * this connection is part of a multi handle and this loop would
- * execute again. This permits the owner of a multi handle to
- * abort a connection attempt before step2 has completed while
- * ensuring that a client using select() or epoll() will always
- * have a valid fdset to wait on.
- */
- result = mesalink_connect_step2(data, conn, sockindex);
-
- if(result ||
- (nonblocking && (ssl_connect_2 == connssl->connecting_state ||
- ssl_connect_2_reading == connssl->connecting_state ||
- ssl_connect_2_writing == connssl->connecting_state))) {
- return result;
- }
- } /* repeat step2 until all transactions are done. */
-
- if(ssl_connect_3 == connssl->connecting_state) {
- result = mesalink_connect_step3(conn, sockindex);
- if(result)
- return result;
- }
-
- if(ssl_connect_done == connssl->connecting_state) {
- connssl->state = ssl_connection_complete;
- conn->recv[sockindex] = mesalink_recv;
- conn->send[sockindex] = mesalink_send;
- *done = TRUE;
- }
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
-
- return CURLE_OK;
-}
-
-static CURLcode
-mesalink_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
- int sockindex, bool *done)
-{
- return mesalink_connect_common(data, conn, sockindex, TRUE, done);
-}
-
-static CURLcode
-mesalink_connect(struct Curl_easy *data, struct connectdata *conn,
- int sockindex)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = mesalink_connect_common(data, conn, sockindex, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
-static void *
-mesalink_get_internals(struct ssl_connect_data *connssl,
- CURLINFO info UNUSED_PARAM)
-{
- struct ssl_backend_data *backend = connssl->backend;
- (void)info;
- return backend->handle;
-}
-
-const struct Curl_ssl Curl_ssl_mesalink = {
- { CURLSSLBACKEND_MESALINK, "MesaLink" }, /* info */
-
- SSLSUPP_SSL_CTX,
-
- sizeof(struct ssl_backend_data),
-
- mesalink_init, /* init */
- Curl_none_cleanup, /* cleanup */
- mesalink_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
- mesalink_shutdown, /* shutdown */
- Curl_none_data_pending, /* data_pending */
- Curl_none_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
- mesalink_connect, /* connect */
- mesalink_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_getsock, /* getsock */
- mesalink_get_internals, /* get_internals */
- mesalink_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_session_free, /* session_free */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
- NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL /* disassociate_connection */
-};
-
-#endif
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index 2b44f0512..12cf618f5 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,6 +18,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -334,7 +336,7 @@ static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model,
char name[MAX_CIPHER_LENGTH + 1];
size_t len;
bool found = FALSE;
- while((*cipher) && (ISSPACE(*cipher)))
+ while((*cipher) && (ISBLANK(*cipher)))
++cipher;
end = strpbrk(cipher, ":, ");
@@ -434,7 +436,7 @@ static char *dup_nickname(struct Curl_easy *data, const char *str)
/* search the first slash; we require at least one slash in a file name */
n = strchr(str, '/');
if(!n) {
- infof(data, "warning: certificate file name \"%s\" handled as nickname; "
+ infof(data, "WARNING: certificate file name \"%s\" handled as nickname; "
"please use \"./%s\" to force file name", str, str);
return strdup(str);
}
@@ -488,6 +490,9 @@ static CURLcode nss_create_object(struct ssl_connect_data *connssl,
const int slot_id = (cacert) ? 0 : 1;
char *slot_name = aprintf("PEM Token #%d", slot_id);
struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
if(!slot_name)
return CURLE_OUT_OF_MEMORY;
@@ -845,7 +850,7 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
unsigned int buflen;
SSLNextProtoState state;
- if(!conn->bits.tls_enable_npn && !conn->bits.tls_enable_alpn) {
+ if(!conn->bits.tls_enable_alpn) {
return;
}
@@ -859,31 +864,37 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
#endif
case SSL_NEXT_PROTO_NO_SUPPORT:
case SSL_NEXT_PROTO_NO_OVERLAP:
- infof(data, "ALPN/NPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
return;
#ifdef SSL_ENABLE_ALPN
case SSL_NEXT_PROTO_SELECTED:
- infof(data, "ALPN, server accepted to use %.*s", buflen, buf);
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf);
break;
#endif
- case SSL_NEXT_PROTO_NEGOTIATED:
- infof(data, "NPN, server accepted to use %.*s", buflen, buf);
+ default:
+ /* ignore SSL_NEXT_PROTO_NEGOTIATED */
break;
}
-#ifdef USE_NGHTTP2
+#ifdef USE_HTTP2
if(buflen == ALPN_H2_LENGTH &&
!memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) {
- conn->negnpn = CURL_HTTP_VERSION_2;
+ conn->alpn = CURL_HTTP_VERSION_2;
}
else
#endif
if(buflen == ALPN_HTTP_1_1_LENGTH &&
!memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) {
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ conn->alpn = CURL_HTTP_VERSION_1_1;
}
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+
+ /* This callback might get called when PR_Recv() is used within
+ * close_one() during a connection shutdown. At that point there might not
+ * be any "bundle" associated with the connection anymore.
+ */
+ if(conn->bundle)
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
}
@@ -925,8 +936,8 @@ static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data,
if(cipherInfo.symCipher != ssl_calg_aes_gcm)
goto end;
- /* Enforce ALPN or NPN to do False Start, as an indicator of server
- * compatibility. */
+ /* Enforce ALPN to do False Start, as an indicator of server
+ compatibility. */
rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn,
&negotiatedExtension);
if(rv != SECSuccess || !negotiatedExtension) {
@@ -974,6 +985,9 @@ static void display_cert_info(struct Curl_easy *data,
PR_Free(common_name);
}
+/* A number of certs that will never occur in a real server handshake */
+#define TOO_MANY_CERTS 300
+
static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock)
{
CURLcode result = CURLE_OK;
@@ -1009,6 +1023,11 @@ static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock)
cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
while(cert2) {
i++;
+ if(i >= TOO_MANY_CERTS) {
+ CERT_DestroyCertificate(cert2);
+ failf(data, "certificate loop");
+ return CURLE_SSL_CERTPROBLEM;
+ }
if(cert2->isRoot) {
CERT_DestroyCertificate(cert2);
break;
@@ -1105,9 +1124,12 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl,
{
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
struct ssl_backend_data *backend = connssl->backend;
- struct Curl_easy *data = backend->data;
+ struct Curl_easy *data = NULL;
CERTCertificate *cert;
+ DEBUGASSERT(backend);
+ data = backend->data;
+
if(!pinnedpubkey)
/* no pinned public key specified */
return CURLE_OK;
@@ -1134,7 +1156,7 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl,
/* report the resulting status */
switch(result) {
case CURLE_OK:
- infof(data, "pinned public key verified successfully!");
+ infof(data, "pinned public key verified successfully");
break;
case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
failf(data, "failed to verify pinned public key");
@@ -1158,10 +1180,15 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
{
struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
struct ssl_backend_data *backend = connssl->backend;
- struct Curl_easy *data = backend->data;
- const char *nickname = backend->client_nickname;
+ struct Curl_easy *data = NULL;
+ const char *nickname = NULL;
static const char pem_slotname[] = "PEM Token #1";
+ DEBUGASSERT(backend);
+
+ data = backend->data;
+ nickname = backend->client_nickname;
+
if(backend->obj_clicert) {
/* use the cert/key provided by PEM reader */
SECItem cert_der = { 0, NULL, 0 };
@@ -1529,6 +1556,8 @@ static int nss_check_cxn(struct connectdata *conn)
int rc;
char buf;
+ DEBUGASSERT(backend);
+
rc =
PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK,
PR_SecondsToInterval(1));
@@ -1545,7 +1574,11 @@ static void close_one(struct ssl_connect_data *connssl)
{
/* before the cleanup, check whether we are using a client certificate */
struct ssl_backend_data *backend = connssl->backend;
- const bool client_cert = (backend->client_nickname != NULL)
+ bool client_cert = true;
+
+ DEBUGASSERT(backend);
+
+ client_cert = (backend->client_nickname != NULL)
|| (backend->obj_clicert != NULL);
if(backend->handle) {
@@ -1587,8 +1620,13 @@ static void nss_close(struct Curl_easy *data, struct connectdata *conn,
struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex];
#endif
struct ssl_backend_data *backend = connssl->backend;
-
(void)data;
+
+ DEBUGASSERT(backend);
+#ifndef CURL_DISABLE_PROXY
+ DEBUGASSERT(connssl_proxy->backend != NULL);
+#endif
+
if(backend->handle
#ifndef CURL_DISABLE_PROXY
|| connssl_proxy->backend->handle
@@ -1720,7 +1758,7 @@ static CURLcode nss_load_ca_certificates(struct Curl_easy *data,
PR_CloseDir(dir);
}
else
- infof(data, "warning: CURLOPT_CAPATH not a directory (%s)", capath);
+ infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath);
}
return CURLE_OK;
@@ -1816,6 +1854,8 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl,
{
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
if(is_nss_error(curlerr)) {
/* read NSPR error code */
PRErrorCode err = PR_GetError();
@@ -1842,6 +1882,9 @@ static CURLcode nss_set_blocking(struct ssl_connect_data *connssl,
{
PRSocketOptionData sock_opt;
struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
sock_opt.option = PR_SockOpt_Nonblocking;
sock_opt.value.non_blocking = !blocking;
@@ -1865,7 +1908,6 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
CURLcode result;
bool second_layer = FALSE;
SSLVersionRange sslver_supported;
-
SSLVersionRange sslver = {
SSL_LIBRARY_VERSION_TLS_1_0, /* min */
#ifdef SSL_LIBRARY_VERSION_TLS_1_3
@@ -1878,6 +1920,13 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
SSL_LIBRARY_VERSION_TLS_1_0
#endif
};
+ char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ DEBUGASSERT(backend);
backend->data = data;
@@ -1946,11 +1995,11 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
/* unless the user explicitly asks to allow the protocol vulnerability, we
use the work-around */
if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess)
- infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d",
+ infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d",
ssl_cbc_random_iv);
#else
if(ssl_cbc_random_iv)
- infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in");
+ infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in");
#endif
if(SSL_CONN_CONFIG(cipher_list)) {
@@ -1961,7 +2010,7 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
}
if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost))
- infof(data, "warning: ignoring value of ssl.verifyhost");
+ infof(data, "WARNING: ignoring value of ssl.verifyhost");
/* bypass the default SSL_AuthCertificate() hook in case we do not want to
* verify peer */
@@ -1981,20 +2030,20 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
const CURLcode rv = nss_load_ca_certificates(data, conn, sockindex);
if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer))
/* not a fatal error because we are not going to verify the peer */
- infof(data, "warning: CA certificates failed to load");
+ infof(data, "WARNING: CA certificates failed to load");
else if(rv) {
result = rv;
goto error;
}
}
- if(SSL_SET_OPTION(CRLfile)) {
- const CURLcode rv = nss_load_crl(SSL_SET_OPTION(CRLfile));
+ if(SSL_SET_OPTION(primary.CRLfile)) {
+ const CURLcode rv = nss_load_crl(SSL_SET_OPTION(primary.CRLfile));
if(rv) {
result = rv;
goto error;
}
- infof(data, " CRLfile: %s", SSL_SET_OPTION(CRLfile));
+ infof(data, " CRLfile: %s", SSL_SET_OPTION(primary.CRLfile));
}
if(SSL_SET_OPTION(primary.clientcert)) {
@@ -2028,9 +2077,12 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
if(conn->proxy_ssl[sockindex].use) {
+ struct ssl_backend_data *proxy_backend;
+ proxy_backend = conn->proxy_ssl[sockindex].backend;
DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
- DEBUGASSERT(conn->proxy_ssl[sockindex].backend->handle != NULL);
- nspr_io = conn->proxy_ssl[sockindex].backend->handle;
+ DEBUGASSERT(proxy_backend);
+ DEBUGASSERT(proxy_backend->handle);
+ nspr_io = proxy_backend->handle;
second_layer = TRUE;
}
#endif
@@ -2084,12 +2136,6 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
}
#endif
-#ifdef SSL_ENABLE_NPN
- if(SSL_OptionSet(backend->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn
- ? PR_TRUE : PR_FALSE) != SECSuccess)
- goto error;
-#endif
-
#ifdef SSL_ENABLE_ALPN
if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn
? PR_TRUE : PR_FALSE) != SECSuccess)
@@ -2108,15 +2154,15 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
}
#endif
-#if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN)
- if(conn->bits.tls_enable_npn || conn->bits.tls_enable_alpn) {
+#if defined(SSL_ENABLE_ALPN)
+ if(conn->bits.tls_enable_alpn) {
int cur = 0;
unsigned char protocols[128];
#ifdef USE_HTTP2
if(data->state.httpwant >= CURL_HTTP_VERSION_2
#ifndef CURL_DISABLE_PROXY
- && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
+ && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
#endif
) {
protocols[cur++] = ALPN_H2_LENGTH;
@@ -2140,11 +2186,11 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
goto error;
/* propagate hostname to the TLS layer */
- if(SSL_SetURL(backend->handle, SSL_HOST_NAME()) != SECSuccess)
+ if(SSL_SetURL(backend->handle, snihost) != SECSuccess)
goto error;
/* prevent NSS from re-using the session for a different hostname */
- if(SSL_SetSockPeerID(backend->handle, SSL_HOST_NAME()) != SECSuccess)
+ if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess)
goto error;
return CURLE_OK;
@@ -2172,6 +2218,8 @@ static CURLcode nss_do_connect(struct Curl_easy *data,
goto error;
}
+ DEBUGASSERT(backend);
+
/* Force the handshake now */
timeout = PR_MillisecondsToInterval((PRUint32) time_left);
if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) {
@@ -2305,6 +2353,8 @@ static ssize_t nss_send(struct Curl_easy *data, /* transfer */
struct ssl_backend_data *backend = connssl->backend;
ssize_t rc;
+ DEBUGASSERT(backend);
+
/* The SelectClientCert() hook uses this for infof() and failf() but the
handle stored in nss_setup_connect() could have already been freed. */
backend->data = data;
@@ -2344,6 +2394,8 @@ static ssize_t nss_recv(struct Curl_easy *data, /* transfer */
struct ssl_backend_data *backend = connssl->backend;
ssize_t nread;
+ DEBUGASSERT(backend);
+
/* The SelectClientCert() hook uses this for infof() and failf() but the
handle stored in nss_setup_connect() could have already been freed. */
backend->data = data;
@@ -2442,6 +2494,7 @@ static void *nss_get_internals(struct ssl_connect_data *connssl,
{
struct ssl_backend_data *backend = connssl->backend;
(void)info;
+ DEBUGASSERT(backend);
return backend->handle;
}
diff --git a/lib/vtls/nssg.h b/lib/vtls/nssg.h
index 37b364647..454a38f1f 100644
--- a/lib/vtls/nssg.h
+++ b/lib/vtls/nssg.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index f836c63b0..ad2efa558 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,6 +18,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -27,7 +29,7 @@
#include "curl_setup.h"
-#ifdef USE_OPENSSL
+#if defined(USE_QUICHE) || defined(USE_OPENSSL)
#include <limits.h>
@@ -53,6 +55,7 @@
#include "slist.h"
#include "select.h"
#include "vtls.h"
+#include "vauth/vauth.h"
#include "keylog.h"
#include "strcase.h"
#include "hostcheck.h"
@@ -76,10 +79,6 @@
#include <openssl/buffer.h>
#include <openssl/pkcs12.h>
-#ifdef USE_AMISSL
-#include "amigaos.h"
-#endif
-
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
#include <openssl/ocsp.h>
#endif
@@ -91,7 +90,6 @@
#endif
#include "warnless.h"
-#include "non-ascii.h" /* for Curl_convert_from_utf8 prototype */
/* The last #include files should be: */
#include "curl_memory.h"
@@ -209,9 +207,17 @@
!defined(OPENSSL_IS_BORINGSSL))
#define HAVE_SSL_CTX_SET_CIPHERSUITES
#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
-/* SET_EC_CURVES is available under the same preconditions: see
- * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html
+#endif
+
+/*
+ * Whether SSL_CTX_set1_curves_list is available.
+ * OpenSSL: supported since 1.0.2, see
+ * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html
+ * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30)
+ * LibreSSL: since 2.5.3 (April 12, 2017)
*/
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
+ defined(OPENSSL_IS_BORINGSSL)
#define HAVE_SSL_CTX_SET_EC_CURVES
#endif
@@ -266,7 +272,345 @@ struct ssl_backend_data {
#endif
};
-static void ossl_associate_connection(struct Curl_easy *data,
+#define push_certinfo(_label, _num) \
+do { \
+ long info_len = BIO_get_mem_data(mem, &ptr); \
+ Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \
+ if(1 != BIO_reset(mem)) \
+ break; \
+} while(0)
+
+static void pubkey_show(struct Curl_easy *data,
+ BIO *mem,
+ int num,
+ const char *type,
+ const char *name,
+ const BIGNUM *bn)
+{
+ char *ptr;
+ char namebuf[32];
+
+ msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
+
+ if(bn)
+ BN_print(mem, bn);
+ push_certinfo(namebuf, num);
+}
+
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+#define print_pubkey_BN(_type, _name, _num) \
+ pubkey_show(data, mem, _num, #_type, #_name, _name)
+
+#else
+#define print_pubkey_BN(_type, _name, _num) \
+do { \
+ if(_type->_name) { \
+ pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \
+ } \
+} while(0)
+#endif
+
+static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
+{
+ int i, ilen;
+
+ ilen = (int)len;
+ if(ilen < 0)
+ return 1; /* buffer too big */
+
+ i = i2t_ASN1_OBJECT(buf, ilen, a);
+
+ if(i >= ilen)
+ return 1; /* buffer too small */
+
+ return 0;
+}
+
+static void X509V3_ext(struct Curl_easy *data,
+ int certnum,
+ CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+{
+ int i;
+
+ if((int)sk_X509_EXTENSION_num(exts) <= 0)
+ /* no extensions, bail out */
+ return;
+
+ for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
+ ASN1_OBJECT *obj;
+ X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+ BUF_MEM *biomem;
+ char namebuf[128];
+ BIO *bio_out = BIO_new(BIO_s_mem());
+
+ if(!bio_out)
+ return;
+
+ obj = X509_EXTENSION_get_object(ext);
+
+ asn1_object_dump(obj, namebuf, sizeof(namebuf));
+
+ if(!X509V3_EXT_print(bio_out, ext, 0, 0))
+ ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext));
+
+ BIO_get_mem_ptr(bio_out, &biomem);
+ Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
+ biomem->length);
+ BIO_free(bio_out);
+ }
+}
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef size_t numcert_t;
+#else
+typedef int numcert_t;
+#endif
+
+CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
+{
+ CURLcode result;
+ STACK_OF(X509) *sk;
+ int i;
+ numcert_t numcerts;
+ BIO *mem;
+
+ DEBUGASSERT(ssl);
+
+ sk = SSL_get_peer_cert_chain(ssl);
+ if(!sk) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ numcerts = sk_X509_num(sk);
+
+ result = Curl_ssl_init_certinfo(data, (int)numcerts);
+ if(result) {
+ return result;
+ }
+
+ mem = BIO_new(BIO_s_mem());
+ if(!mem) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ for(i = 0; i < (int)numcerts; i++) {
+ ASN1_INTEGER *num;
+ X509 *x = sk_X509_value(sk, i);
+ EVP_PKEY *pubkey = NULL;
+ int j;
+ char *ptr;
+ const ASN1_BIT_STRING *psig = NULL;
+
+ X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE);
+ push_certinfo("Subject", i);
+
+ X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
+ push_certinfo("Issuer", i);
+
+ BIO_printf(mem, "%lx", X509_get_version(x));
+ push_certinfo("Version", i);
+
+ num = X509_get_serialNumber(x);
+ if(num->type == V_ASN1_NEG_INTEGER)
+ BIO_puts(mem, "-");
+ for(j = 0; j < num->length; j++)
+ BIO_printf(mem, "%02x", num->data[j]);
+ push_certinfo("Serial Number", i);
+
+#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
+ {
+ const X509_ALGOR *sigalg = NULL;
+ X509_PUBKEY *xpubkey = NULL;
+ ASN1_OBJECT *pubkeyoid = NULL;
+
+ X509_get0_signature(&psig, &sigalg, x);
+ if(sigalg) {
+ i2a_ASN1_OBJECT(mem, sigalg->algorithm);
+ push_certinfo("Signature Algorithm", i);
+ }
+
+ xpubkey = X509_get_X509_PUBKEY(x);
+ if(xpubkey) {
+ X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
+ if(pubkeyoid) {
+ i2a_ASN1_OBJECT(mem, pubkeyoid);
+ push_certinfo("Public Key Algorithm", i);
+ }
+ }
+
+ X509V3_ext(data, i, X509_get0_extensions(x));
+ }
+#else
+ {
+ /* before OpenSSL 1.0.2 */
+ X509_CINF *cinf = x->cert_info;
+
+ i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
+ push_certinfo("Signature Algorithm", i);
+
+ i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
+ push_certinfo("Public Key Algorithm", i);
+
+ X509V3_ext(data, i, cinf->extensions);
+
+ psig = x->signature;
+ }
+#endif
+
+ ASN1_TIME_print(mem, X509_get0_notBefore(x));
+ push_certinfo("Start date", i);
+
+ ASN1_TIME_print(mem, X509_get0_notAfter(x));
+ push_certinfo("Expire date", i);
+
+ pubkey = X509_get_pubkey(x);
+ if(!pubkey)
+ infof(data, " Unable to load public key");
+ else {
+ int pktype;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ pktype = EVP_PKEY_id(pubkey);
+#else
+ pktype = pubkey->type;
+#endif
+ switch(pktype) {
+ case EVP_PKEY_RSA:
+ {
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+ RSA *rsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ rsa = EVP_PKEY_get0_RSA(pubkey);
+#else
+ rsa = pubkey->pkey.rsa;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+
+ {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ DECLARE_PKEY_PARAM_BIGNUM(n);
+ DECLARE_PKEY_PARAM_BIGNUM(e);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e);
+#else
+ RSA_get0_key(rsa, &n, &e, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+ BIO_printf(mem, "%d", BN_num_bits(n));
+#else
+ BIO_printf(mem, "%d", BN_num_bits(rsa->n));
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+ push_certinfo("RSA Public Key", i);
+ print_pubkey_BN(rsa, n, i);
+ print_pubkey_BN(rsa, e, i);
+ FREE_PKEY_PARAM_BIGNUM(n);
+ FREE_PKEY_PARAM_BIGNUM(e);
+ }
+
+ break;
+ }
+ case EVP_PKEY_DSA:
+ {
+#ifndef OPENSSL_NO_DSA
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+ DSA *dsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ dsa = EVP_PKEY_get0_DSA(pubkey);
+#else
+ dsa = pubkey->pkey.dsa;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+ {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ DECLARE_PKEY_PARAM_BIGNUM(p);
+ DECLARE_PKEY_PARAM_BIGNUM(q);
+ DECLARE_PKEY_PARAM_BIGNUM(g);
+ DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
+ DSA_get0_pqg(dsa, &p, &q, &g);
+ DSA_get0_key(dsa, &pub_key, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+ print_pubkey_BN(dsa, p, i);
+ print_pubkey_BN(dsa, q, i);
+ print_pubkey_BN(dsa, g, i);
+ print_pubkey_BN(dsa, pub_key, i);
+ FREE_PKEY_PARAM_BIGNUM(p);
+ FREE_PKEY_PARAM_BIGNUM(q);
+ FREE_PKEY_PARAM_BIGNUM(g);
+ FREE_PKEY_PARAM_BIGNUM(pub_key);
+ }
+#endif /* !OPENSSL_NO_DSA */
+ break;
+ }
+ case EVP_PKEY_DH:
+ {
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+ DH *dh;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ dh = EVP_PKEY_get0_DH(pubkey);
+#else
+ dh = pubkey->pkey.dh;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+ {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ DECLARE_PKEY_PARAM_BIGNUM(p);
+ DECLARE_PKEY_PARAM_BIGNUM(q);
+ DECLARE_PKEY_PARAM_BIGNUM(g);
+ DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+ EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
+ DH_get0_pqg(dh, &p, &q, &g);
+ DH_get0_key(dh, &pub_key, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+ print_pubkey_BN(dh, p, i);
+ print_pubkey_BN(dh, q, i);
+ print_pubkey_BN(dh, g, i);
+#else
+ print_pubkey_BN(dh, p, i);
+ print_pubkey_BN(dh, g, i);
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+ print_pubkey_BN(dh, pub_key, i);
+ FREE_PKEY_PARAM_BIGNUM(p);
+ FREE_PKEY_PARAM_BIGNUM(q);
+ FREE_PKEY_PARAM_BIGNUM(g);
+ FREE_PKEY_PARAM_BIGNUM(pub_key);
+ }
+ break;
+ }
+ }
+ EVP_PKEY_free(pubkey);
+ }
+
+ if(psig) {
+ for(j = 0; j < psig->length; j++)
+ BIO_printf(mem, "%02x:", psig->data[j]);
+ push_certinfo("Signature", i);
+ }
+
+ PEM_write_bio_X509(mem, x);
+ push_certinfo("Cert", i);
+ }
+
+ BIO_free(mem);
+
+ return CURLE_OK;
+}
+
+#endif /* quiche or OpenSSL */
+
+#ifdef USE_OPENSSL
+
+static bool ossl_associate_connection(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
@@ -477,36 +821,19 @@ static CURLcode ossl_seed(struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
#else
-#ifndef RANDOM_FILE
- /* if RANDOM_FILE isn't defined, we only perform this if an option tells
- us to! */
- if(data->set.str[STRING_SSL_RANDOM_FILE])
-#define RANDOM_FILE "" /* doesn't matter won't be used */
+#ifdef RANDOM_FILE
+ RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH);
+ if(rand_enough())
+ return CURLE_OK;
#endif
- {
- /* let the option override the define */
- RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
- data->set.str[STRING_SSL_RANDOM_FILE]:
- RANDOM_FILE),
- RAND_LOAD_LENGTH);
- if(rand_enough())
- return CURLE_OK;
- }
-#if defined(HAVE_RAND_EGD)
- /* only available in OpenSSL 0.9.5 and later */
+#if defined(HAVE_RAND_EGD) && defined(EGD_SOCKET)
+ /* available in OpenSSL 0.9.5 and later */
/* EGD_SOCKET is set at configure time or not at all */
-#ifndef EGD_SOCKET
- /* If we don't have the define set, we only do this if the egd-option
- is set */
- if(data->set.str[STRING_SSL_EGDSOCKET])
-#define EGD_SOCKET "" /* doesn't matter won't be used */
-#endif
{
/* If there's an option and a define, the option overrides the
define */
- int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
- data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
+ int ret = RAND_egd(EGD_SOCKET);
if(-1 != ret) {
if(rand_enough())
return CURLE_OK;
@@ -549,7 +876,7 @@ static CURLcode ossl_seed(struct Curl_easy *data)
}
}
- infof(data, "libcurl is now using a weak random seed!");
+ infof(data, "libcurl is now using a weak random seed");
return (rand_enough() ? CURLE_OK :
CURLE_SSL_CONNECT_ERROR /* confusing error code */);
#endif
@@ -805,9 +1132,10 @@ int cert_stuff(struct Curl_easy *data,
SSL_CTX_use_certificate_chain_file(ctx, cert_file);
if(cert_use_result != 1) {
failf(data,
- "could not load PEM client certificate, " OSSL_PACKAGE
+ "could not load PEM client certificate from %s, " OSSL_PACKAGE
" error %s, "
"(no key found, wrong pass phrase, or wrong file format?)",
+ (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file),
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
return 0;
@@ -825,9 +1153,10 @@ int cert_stuff(struct Curl_easy *data,
SSL_CTX_use_certificate_file(ctx, cert_file, file_type);
if(cert_use_result != 1) {
failf(data,
- "could not load ASN1 client certificate, " OSSL_PACKAGE
+ "could not load ASN1 client certificate from %s, " OSSL_PACKAGE
" error %s, "
"(no key found, wrong pass phrase, or wrong file format?)",
+ (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file),
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
return 0;
@@ -880,8 +1209,9 @@ int cert_stuff(struct Curl_easy *data,
}
if(SSL_CTX_use_certificate(ctx, params.cert) != 1) {
- failf(data, "unable to set client certificate");
- X509_free(params.cert);
+ failf(data, "unable to set client certificate [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
return 0;
}
X509_free(params.cert); /* we don't need the handle any more... */
@@ -1004,11 +1334,7 @@ int cert_stuff(struct Curl_easy *data,
fail:
EVP_PKEY_free(pri);
X509_free(x509);
-#ifdef USE_AMISSL
- sk_X509_pop_free(ca, Curl_amiga_X509_free);
-#else
sk_X509_pop_free(ca, X509_free);
-#endif
if(!cert_done)
return 0; /* failure! */
break;
@@ -1159,6 +1485,22 @@ int cert_stuff(struct Curl_easy *data,
return 1;
}
+CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data, 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,
+ const char *key_type, char *key_passwd)
+{
+ int rv = cert_stuff(data, ctx, cert_file, cert_blob, cert_type, key_file,
+ key_blob, key_type, key_passwd);
+ if(rv != 1) {
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ return CURLE_OK;
+}
+
/* returns non-zero on failure */
static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
{
@@ -1432,6 +1774,9 @@ static void ossl_closeone(struct Curl_easy *data,
struct ssl_connect_data *connssl)
{
struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
+
if(backend->handle) {
char buf[32];
set_logger(conn, data);
@@ -1489,6 +1834,8 @@ static int ossl_shutdown(struct Curl_easy *data,
struct ssl_backend_data *backend = connssl->backend;
int loop = 10;
+ DEBUGASSERT(backend);
+
#ifndef CURL_DISABLE_FTP
/* This has only been tested on the proftpd server, and the mod_tls code
sends a close notify alert without waiting for a close notify alert in
@@ -1610,54 +1957,26 @@ static void ossl_close_all(struct Curl_easy *data)
/* ====================================================== */
/*
- * Match subjectAltName against the host name. This requires a conversion
- * in CURL_DOES_CONVERSIONS builds.
+ * Match subjectAltName against the host name.
*/
static bool subj_alt_hostcheck(struct Curl_easy *data,
- const char *match_pattern, const char *hostname,
+ const char *match_pattern,
+ size_t matchlen,
+ const char *hostname,
+ size_t hostlen,
const char *dispname)
-#ifdef CURL_DOES_CONVERSIONS
-{
- bool res = FALSE;
-
- /* Curl_cert_hostcheck uses host encoding, but we get ASCII from
- OpenSSl.
- */
- char *match_pattern2 = strdup(match_pattern);
-
- if(match_pattern2) {
- if(Curl_convert_from_network(data, match_pattern2,
- strlen(match_pattern2)) == CURLE_OK) {
- if(Curl_cert_hostcheck(match_pattern2, hostname)) {
- res = TRUE;
- infof(data,
- " subjectAltName: host \"%s\" matched cert's \"%s\"",
- dispname, match_pattern2);
- }
- }
- free(match_pattern2);
- }
- else {
- failf(data,
- "SSL: out of memory when allocating temporary for subjectAltName");
- }
- return res;
-}
-#else
{
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)dispname;
(void)data;
#endif
- if(Curl_cert_hostcheck(match_pattern, hostname)) {
+ if(Curl_cert_hostcheck(match_pattern, matchlen, hostname, hostlen)) {
infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"",
dispname, match_pattern);
return TRUE;
}
return FALSE;
}
-#endif
-
/* Quote from RFC2818 section 3.1 "Server Identity"
@@ -1698,6 +2017,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */
const char * const hostname = SSL_HOST_NAME();
const char * const dispname = SSL_HOST_DISPNAME();
+ size_t hostlen = strlen(hostname);
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
@@ -1760,7 +2080,9 @@ CURLcode Curl_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, hostname, dispname)) {
+ subj_alt_hostcheck(data,
+ altptr,
+ altlen, hostname, hostlen, dispname)) {
dnsmatched = TRUE;
}
break;
@@ -1796,17 +2118,17 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
else {
/* we have to look to the last occurrence of a commonName in the
distinguished one to get the most significant one. */
- int j, i = -1;
+ int i = -1;
+ unsigned char *peer_CN = NULL;
+ int peerlen = 0;
/* The following is done because of a bug in 0.9.6b */
-
- unsigned char *nulstr = (unsigned char *)"";
- unsigned char *peer_CN = nulstr;
-
X509_NAME *name = X509_get_subject_name(server_cert);
- if(name)
+ if(name) {
+ int j;
while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0)
i = j;
+ }
/* we have the name entry and we will now convert this to a string
that we can use for comparison. Doing this we support BMPstring,
@@ -1822,19 +2144,21 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
conditional in the future when OpenSSL has been fixed. */
if(tmp) {
if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
- j = ASN1_STRING_length(tmp);
- if(j >= 0) {
- peer_CN = OPENSSL_malloc(j + 1);
+ peerlen = ASN1_STRING_length(tmp);
+ if(peerlen >= 0) {
+ peer_CN = OPENSSL_malloc(peerlen + 1);
if(peer_CN) {
- memcpy(peer_CN, ASN1_STRING_get0_data(tmp), j);
- peer_CN[j] = '\0';
+ memcpy(peer_CN, ASN1_STRING_get0_data(tmp), peerlen);
+ peer_CN[peerlen] = '\0';
}
+ else
+ result = CURLE_OUT_OF_MEMORY;
}
}
else /* not a UTF8 name */
- j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
+ peerlen = ASN1_STRING_to_UTF8(&peer_CN, tmp);
- if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != j)) {
+ if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != peerlen)) {
/* there was a terminating zero before the end of string, this
cannot match and we return failure! */
failf(data, "SSL: illegal cert name field");
@@ -1843,19 +2167,6 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
}
}
- if(peer_CN == nulstr)
- peer_CN = NULL;
- else {
- /* convert peer_CN from UTF8 */
- CURLcode rc = Curl_convert_from_utf8(data, (char *)peer_CN,
- strlen((char *)peer_CN));
- /* Curl_convert_from_utf8 calls failf if unsuccessful */
- if(rc) {
- OPENSSL_free(peer_CN);
- return rc;
- }
- }
-
if(result)
/* error already detected, pass through */
;
@@ -1864,7 +2175,8 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
"SSL: unable to obtain common name from peer certificate");
result = CURLE_PEER_FAILED_VERIFICATION;
}
- else if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) {
+ else if(!Curl_cert_hostcheck((const char *)peer_CN,
+ peerlen, hostname, hostlen)) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'", peer_CN, dispname);
result = CURLE_PEER_FAILED_VERIFICATION;
@@ -1898,8 +2210,11 @@ static CURLcode verifystatus(struct Curl_easy *data,
int cert_status, crl_reason;
ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
int ret;
+ long len;
- long len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status);
+ DEBUGASSERT(backend);
+
+ len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status);
if(!status) {
failf(data, "No OCSP response received");
@@ -1930,6 +2245,11 @@ static CURLcode verifystatus(struct Curl_easy *data,
}
ch = SSL_get_peer_cert_chain(backend->handle);
+ if(!ch) {
+ failf(data, "Could not get peer certificate chain");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
st = SSL_CTX_get_cert_store(backend->ctx);
#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
@@ -2158,7 +2478,10 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
struct connectdata *conn = userp;
struct ssl_connect_data *connssl = &conn->ssl[0];
struct ssl_backend_data *backend = connssl->backend;
- struct Curl_easy *data = backend->logger;
+ struct Curl_easy *data = NULL;
+
+ DEBUGASSERT(backend);
+ data = backend->logger;
if(!conn || !data || !data->set.fdebug ||
(direction != 0 && direction != 1))
@@ -2270,72 +2593,6 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
# define HAS_ALPN 1
#endif
-/* Check for OpenSSL 1.0.1 which has NPN support. */
-#undef HAS_NPN
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L \
- && !defined(OPENSSL_NO_TLSEXT) \
- && !defined(OPENSSL_NO_NEXTPROTONEG)
-# define HAS_NPN 1
-#endif
-
-#ifdef HAS_NPN
-
-/*
- * in is a list of length prefixed strings. this function has to select
- * the protocol we want to use from the list and write its string into out.
- */
-
-static int
-select_next_protocol(unsigned char **out, unsigned char *outlen,
- const unsigned char *in, unsigned int inlen,
- const char *key, unsigned int keylen)
-{
- unsigned int i;
- for(i = 0; i + keylen <= inlen; i += in[i] + 1) {
- if(memcmp(&in[i + 1], key, keylen) == 0) {
- *out = (unsigned char *) &in[i + 1];
- *outlen = in[i];
- return 0;
- }
- }
- return -1;
-}
-
-static int
-select_next_proto_cb(SSL *ssl,
- unsigned char **out, unsigned char *outlen,
- const unsigned char *in, unsigned int inlen,
- void *arg)
-{
- struct Curl_easy *data = (struct Curl_easy *)arg;
- struct connectdata *conn = data->conn;
- (void)ssl;
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2 &&
- !select_next_protocol(out, outlen, in, inlen, ALPN_H2, ALPN_H2_LENGTH)) {
- infof(data, "NPN, negotiated HTTP2 (%s)", ALPN_H2);
- conn->negnpn = CURL_HTTP_VERSION_2;
- return SSL_TLSEXT_ERR_OK;
- }
-#endif
-
- if(!select_next_protocol(out, outlen, in, inlen, ALPN_HTTP_1_1,
- ALPN_HTTP_1_1_LENGTH)) {
- infof(data, "NPN, negotiated HTTP1.1");
- conn->negnpn = CURL_HTTP_VERSION_1_1;
- return SSL_TLSEXT_ERR_OK;
- }
-
- infof(data, "NPN, no overlap, use HTTP1.1");
- *out = (unsigned char *)ALPN_HTTP_1_1;
- *outlen = ALPN_HTTP_1_1_LENGTH;
- conn->negnpn = CURL_HTTP_VERSION_1_1;
-
- return SSL_TLSEXT_ERR_OK;
-}
-#endif /* HAS_NPN */
-
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
static CURLcode
set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
@@ -2363,10 +2620,12 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
case CURL_SSLVERSION_TLSv1_2:
ossl_ssl_version_min = TLS1_2_VERSION;
break;
-#ifdef TLS1_3_VERSION
case CURL_SSLVERSION_TLSv1_3:
+#ifdef TLS1_3_VERSION
ossl_ssl_version_min = TLS1_3_VERSION;
break;
+#else
+ return CURLE_NOT_BUILT_IN;
#endif
}
@@ -2422,6 +2681,8 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
#ifdef OPENSSL_IS_BORINGSSL
typedef uint32_t ctx_option_t;
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
+typedef uint64_t ctx_option_t;
#else
typedef long ctx_option_t;
#endif
@@ -2442,6 +2703,8 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
#ifdef TLS1_3_VERSION
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
*ctx_options |= SSL_OP_NO_TLSv1_2;
}
@@ -2521,13 +2784,12 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
return 0;
conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx);
- if(!conn)
- return 0;
-
data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx);
-
/* The sockindex has been stored as a pointer to an array element */
sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx);
+ if(!conn || !data || !sockindex_ptr)
+ return 0;
+
sockindex = (int)(sockindex_ptr - conn->sock);
isproxy = SSL_get_ex_data(ssl, proxy_idx) ? TRUE : FALSE;
@@ -2653,7 +2915,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
#endif
const long int ssl_version = SSL_CONN_CONFIG(version);
#ifdef USE_OPENSSL_SRP
- const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(authtype);
+ const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(primary.authtype);
#endif
char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob);
@@ -2664,12 +2926,13 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
(ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile));
const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
- const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
+ const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile);
char error_buffer[256];
struct ssl_backend_data *backend = connssl->backend;
bool imported_native_ca = false;
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+ DEBUGASSERT(backend);
/* Make funny stuff to get random input */
result = ossl_seed(data);
@@ -2736,8 +2999,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
implementations is desired."
The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to
- disable "rfc4507bis session ticket support". rfc4507bis was later turned
- into the proper RFC5077 it seems: https://tools.ietf.org/html/rfc5077
+ disable "rfc4507bis session ticket support". rfc4507bis was later turned
+ into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077
The enabled extension concerns the session management. I wonder how often
libcurl stops a connection and then resumes a TLS session. Also, sending
@@ -2820,11 +3083,6 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
SSL_CTX_set_options(backend->ctx, ctx_options);
-#ifdef HAS_NPN
- if(conn->bits.tls_enable_npn)
- SSL_CTX_set_next_proto_select_cb(backend->ctx, select_next_proto_cb, data);
-#endif
-
#ifdef HAS_ALPN
if(conn->bits.tls_enable_alpn) {
int cur = 0;
@@ -2840,14 +3098,14 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
cur += ALPN_H2_LENGTH;
- infof(data, "ALPN, offering %s", ALPN_H2);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
}
#endif
protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
cur += ALPN_HTTP_1_1_LENGTH;
- infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
/* expects length prefixed preference ordered list of protocols in wire
* format
@@ -2913,16 +3171,17 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
#endif
#ifdef USE_OPENSSL_SRP
- if(ssl_authtype == CURL_TLSAUTH_SRP) {
- char * const ssl_username = SSL_SET_OPTION(username);
-
+ if((ssl_authtype == CURL_TLSAUTH_SRP) &&
+ Curl_auth_allowed_to_host(data)) {
+ char * const ssl_username = SSL_SET_OPTION(primary.username);
+ char * const ssl_password = SSL_SET_OPTION(primary.password);
infof(data, "Using TLS-SRP username: %s", ssl_username);
if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) {
failf(data, "Unable to set SRP user name");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
- if(!SSL_CTX_set_srp_password(backend->ctx, SSL_SET_OPTION(password))) {
+ if(!SSL_CTX_set_srp_password(backend->ctx, ssl_password)) {
failf(data, "failed setting SRP password");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
@@ -2942,7 +3201,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
/* Import certificates from the Windows root certificate store if requested.
https://stackoverflow.com/questions/9507184/
https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
- https://tools.ietf.org/html/rfc5280 */
+ https://datatracker.ietf.org/doc/html/rfc5280 */
if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) &&
(SSL_SET_OPTION(native_ca_store))) {
X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
@@ -3220,7 +3479,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
SSL_free(backend->handle);
backend->handle = SSL_new(backend->ctx);
if(!backend->handle) {
- failf(data, "SSL: couldn't create a context (handle)!");
+ failf(data, "SSL: couldn't create a context (handle)");
return CURLE_OUT_OF_MEMORY;
}
@@ -3243,44 +3502,48 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
(0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
#endif
sni) {
- size_t nlen = strlen(hostname);
- if((long)nlen >= data->set.buffer_size)
- /* this is seriously messed up */
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) {
+ failf(data, "Failed set SNI");
return CURLE_SSL_CONNECT_ERROR;
-
- /* RFC 6066 section 3 says the SNI field is case insensitive, but browsers
- send the data lowercase and subsequently there are now numerous servers
- out there that don't work unless the name is lowercased */
- Curl_strntolower(data->state.buffer, hostname, nlen);
- data->state.buffer[nlen] = 0;
- if(!SSL_set_tlsext_host_name(backend->handle, data->state.buffer))
- infof(data, "WARNING: failed to configure server name indication (SNI) "
- "TLS extension");
+ }
}
#endif
- ossl_associate_connection(data, conn, sockindex);
+ if(!ossl_associate_connection(data, conn, sockindex)) {
+ /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */
+ failf(data, "SSL: ossl_associate_connection failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
- &ssl_sessionid, NULL, sockindex)) {
- /* we got a session id, use it! */
- if(!SSL_set_session(backend->handle, ssl_sessionid)) {
- Curl_ssl_sessionid_unlock(data);
- failf(data, "SSL: SSL_set_session failed: %s",
- ossl_strerror(ERR_get_error(), error_buffer,
- sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ Curl_ssl_sessionid_lock(data);
+ if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
+ &ssl_sessionid, NULL, sockindex)) {
+ /* we got a session id, use it! */
+ if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+ Curl_ssl_sessionid_unlock(data);
+ failf(data, "SSL: SSL_set_session failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof(data, "SSL re-using session ID");
}
- /* Informational message */
- infof(data, "SSL re-using session ID");
+ Curl_ssl_sessionid_unlock(data);
}
- Curl_ssl_sessionid_unlock(data);
#ifndef CURL_DISABLE_PROXY
if(conn->proxy_ssl[sockindex].use) {
BIO *const bio = BIO_new(BIO_f_ssl());
- SSL *handle = conn->proxy_ssl[sockindex].backend->handle;
+ struct ssl_backend_data *proxy_backend;
+ SSL* handle = NULL;
+ proxy_backend = conn->proxy_ssl[sockindex].backend;
+ DEBUGASSERT(proxy_backend);
+ handle = proxy_backend->handle;
DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
DEBUGASSERT(handle != NULL);
DEBUGASSERT(bio != NULL);
@@ -3310,6 +3573,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
+ DEBUGASSERT(backend);
ERR_clear_error();
@@ -3441,24 +3705,24 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
unsigned int len;
SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
if(len) {
- infof(data, "ALPN, server accepted to use %.*s", len, neg_protocol);
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol);
#ifdef USE_HTTP2
if(len == ALPN_H2_LENGTH &&
!memcmp(ALPN_H2, neg_protocol, len)) {
- conn->negnpn = CURL_HTTP_VERSION_2;
+ conn->alpn = CURL_HTTP_VERSION_2;
}
else
#endif
if(len == ALPN_HTTP_1_1_LENGTH &&
!memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ conn->alpn = CURL_HTTP_VERSION_1_1;
}
}
else
- infof(data, "ALPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -3467,340 +3731,6 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
}
}
-static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
-{
- int i, ilen;
-
- ilen = (int)len;
- if(ilen < 0)
- return 1; /* buffer too big */
-
- i = i2t_ASN1_OBJECT(buf, ilen, a);
-
- if(i >= ilen)
- return 1; /* buffer too small */
-
- return 0;
-}
-
-#define push_certinfo(_label, _num) \
-do { \
- long info_len = BIO_get_mem_data(mem, &ptr); \
- Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \
- if(1 != BIO_reset(mem)) \
- break; \
-} while(0)
-
-static void pubkey_show(struct Curl_easy *data,
- BIO *mem,
- int num,
- const char *type,
- const char *name,
- const BIGNUM *bn)
-{
- char *ptr;
- char namebuf[32];
-
- msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
-
- if(bn)
- BN_print(mem, bn);
- push_certinfo(namebuf, num);
-}
-
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
-#define print_pubkey_BN(_type, _name, _num) \
- pubkey_show(data, mem, _num, #_type, #_name, _name)
-
-#else
-#define print_pubkey_BN(_type, _name, _num) \
-do { \
- if(_type->_name) { \
- pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \
- } \
-} while(0)
-#endif
-
-static void X509V3_ext(struct Curl_easy *data,
- int certnum,
- CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
-{
- int i;
-
- if((int)sk_X509_EXTENSION_num(exts) <= 0)
- /* no extensions, bail out */
- return;
-
- for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
- ASN1_OBJECT *obj;
- X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
- BUF_MEM *biomem;
- char namebuf[128];
- BIO *bio_out = BIO_new(BIO_s_mem());
-
- if(!bio_out)
- return;
-
- obj = X509_EXTENSION_get_object(ext);
-
- asn1_object_dump(obj, namebuf, sizeof(namebuf));
-
- if(!X509V3_EXT_print(bio_out, ext, 0, 0))
- ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext));
-
- BIO_get_mem_ptr(bio_out, &biomem);
- Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
- biomem->length);
- BIO_free(bio_out);
- }
-}
-
-#ifdef OPENSSL_IS_BORINGSSL
-typedef size_t numcert_t;
-#else
-typedef int numcert_t;
-#endif
-
-static CURLcode get_cert_chain(struct Curl_easy *data,
- struct ssl_connect_data *connssl)
-{
- CURLcode result;
- STACK_OF(X509) *sk;
- int i;
- numcert_t numcerts;
- BIO *mem;
- struct ssl_backend_data *backend = connssl->backend;
-
- sk = SSL_get_peer_cert_chain(backend->handle);
- if(!sk) {
- return CURLE_OUT_OF_MEMORY;
- }
-
- numcerts = sk_X509_num(sk);
-
- result = Curl_ssl_init_certinfo(data, (int)numcerts);
- if(result) {
- return result;
- }
-
- mem = BIO_new(BIO_s_mem());
- if(!mem) {
- return CURLE_OUT_OF_MEMORY;
- }
-
- for(i = 0; i < (int)numcerts; i++) {
- ASN1_INTEGER *num;
- X509 *x = sk_X509_value(sk, i);
- EVP_PKEY *pubkey = NULL;
- int j;
- char *ptr;
- const ASN1_BIT_STRING *psig = NULL;
-
- X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE);
- push_certinfo("Subject", i);
-
- X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
- push_certinfo("Issuer", i);
-
- BIO_printf(mem, "%lx", X509_get_version(x));
- push_certinfo("Version", i);
-
- num = X509_get_serialNumber(x);
- if(num->type == V_ASN1_NEG_INTEGER)
- BIO_puts(mem, "-");
- for(j = 0; j < num->length; j++)
- BIO_printf(mem, "%02x", num->data[j]);
- push_certinfo("Serial Number", i);
-
-#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
- {
- const X509_ALGOR *sigalg = NULL;
- X509_PUBKEY *xpubkey = NULL;
- ASN1_OBJECT *pubkeyoid = NULL;
-
- X509_get0_signature(&psig, &sigalg, x);
- if(sigalg) {
- i2a_ASN1_OBJECT(mem, sigalg->algorithm);
- push_certinfo("Signature Algorithm", i);
- }
-
- xpubkey = X509_get_X509_PUBKEY(x);
- if(xpubkey) {
- X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
- if(pubkeyoid) {
- i2a_ASN1_OBJECT(mem, pubkeyoid);
- push_certinfo("Public Key Algorithm", i);
- }
- }
-
- X509V3_ext(data, i, X509_get0_extensions(x));
- }
-#else
- {
- /* before OpenSSL 1.0.2 */
- X509_CINF *cinf = x->cert_info;
-
- i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
- push_certinfo("Signature Algorithm", i);
-
- i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
- push_certinfo("Public Key Algorithm", i);
-
- X509V3_ext(data, i, cinf->extensions);
-
- psig = x->signature;
- }
-#endif
-
- ASN1_TIME_print(mem, X509_get0_notBefore(x));
- push_certinfo("Start date", i);
-
- ASN1_TIME_print(mem, X509_get0_notAfter(x));
- push_certinfo("Expire date", i);
-
- pubkey = X509_get_pubkey(x);
- if(!pubkey)
- infof(data, " Unable to load public key");
- else {
- int pktype;
-#ifdef HAVE_OPAQUE_EVP_PKEY
- pktype = EVP_PKEY_id(pubkey);
-#else
- pktype = pubkey->type;
-#endif
- switch(pktype) {
- case EVP_PKEY_RSA:
- {
-#ifndef HAVE_EVP_PKEY_GET_PARAMS
- RSA *rsa;
-#ifdef HAVE_OPAQUE_EVP_PKEY
- rsa = EVP_PKEY_get0_RSA(pubkey);
-#else
- rsa = pubkey->pkey.rsa;
-#endif /* HAVE_OPAQUE_EVP_PKEY */
-#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
-
- {
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
- DECLARE_PKEY_PARAM_BIGNUM(n);
- DECLARE_PKEY_PARAM_BIGNUM(e);
-#ifdef HAVE_EVP_PKEY_GET_PARAMS
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n);
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e);
-#else
- RSA_get0_key(rsa, &n, &e, NULL);
-#endif /* HAVE_EVP_PKEY_GET_PARAMS */
- BIO_printf(mem, "%d", BN_num_bits(n));
-#else
- BIO_printf(mem, "%d", BN_num_bits(rsa->n));
-#endif /* HAVE_OPAQUE_RSA_DSA_DH */
- push_certinfo("RSA Public Key", i);
- print_pubkey_BN(rsa, n, i);
- print_pubkey_BN(rsa, e, i);
- FREE_PKEY_PARAM_BIGNUM(n);
- FREE_PKEY_PARAM_BIGNUM(e);
- }
-
- break;
- }
- case EVP_PKEY_DSA:
- {
-#ifndef OPENSSL_NO_DSA
-#ifndef HAVE_EVP_PKEY_GET_PARAMS
- DSA *dsa;
-#ifdef HAVE_OPAQUE_EVP_PKEY
- dsa = EVP_PKEY_get0_DSA(pubkey);
-#else
- dsa = pubkey->pkey.dsa;
-#endif /* HAVE_OPAQUE_EVP_PKEY */
-#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
- {
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
- DECLARE_PKEY_PARAM_BIGNUM(p);
- DECLARE_PKEY_PARAM_BIGNUM(q);
- DECLARE_PKEY_PARAM_BIGNUM(g);
- DECLARE_PKEY_PARAM_BIGNUM(pub_key);
-#ifdef HAVE_EVP_PKEY_GET_PARAMS
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
-#else
- DSA_get0_pqg(dsa, &p, &q, &g);
- DSA_get0_key(dsa, &pub_key, NULL);
-#endif /* HAVE_EVP_PKEY_GET_PARAMS */
-#endif /* HAVE_OPAQUE_RSA_DSA_DH */
- print_pubkey_BN(dsa, p, i);
- print_pubkey_BN(dsa, q, i);
- print_pubkey_BN(dsa, g, i);
- print_pubkey_BN(dsa, pub_key, i);
- FREE_PKEY_PARAM_BIGNUM(p);
- FREE_PKEY_PARAM_BIGNUM(q);
- FREE_PKEY_PARAM_BIGNUM(g);
- FREE_PKEY_PARAM_BIGNUM(pub_key);
- }
-#endif /* !OPENSSL_NO_DSA */
- break;
- }
- case EVP_PKEY_DH:
- {
-#ifndef HAVE_EVP_PKEY_GET_PARAMS
- DH *dh;
-#ifdef HAVE_OPAQUE_EVP_PKEY
- dh = EVP_PKEY_get0_DH(pubkey);
-#else
- dh = pubkey->pkey.dh;
-#endif /* HAVE_OPAQUE_EVP_PKEY */
-#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
- {
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
- DECLARE_PKEY_PARAM_BIGNUM(p);
- DECLARE_PKEY_PARAM_BIGNUM(q);
- DECLARE_PKEY_PARAM_BIGNUM(g);
- DECLARE_PKEY_PARAM_BIGNUM(pub_key);
-#ifdef HAVE_EVP_PKEY_GET_PARAMS
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
- EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
-#else
- DH_get0_pqg(dh, &p, &q, &g);
- DH_get0_key(dh, &pub_key, NULL);
-#endif /* HAVE_EVP_PKEY_GET_PARAMS */
- print_pubkey_BN(dh, p, i);
- print_pubkey_BN(dh, q, i);
- print_pubkey_BN(dh, g, i);
-#else
- print_pubkey_BN(dh, p, i);
- print_pubkey_BN(dh, g, i);
-#endif /* HAVE_OPAQUE_RSA_DSA_DH */
- print_pubkey_BN(dh, pub_key, i);
- FREE_PKEY_PARAM_BIGNUM(p);
- FREE_PKEY_PARAM_BIGNUM(q);
- FREE_PKEY_PARAM_BIGNUM(g);
- FREE_PKEY_PARAM_BIGNUM(pub_key);
- }
- break;
- }
- }
- EVP_PKEY_free(pubkey);
- }
-
- if(psig) {
- for(j = 0; j < psig->length; j++)
- BIO_printf(mem, "%02x:", psig->data[j]);
- push_certinfo("Signature", i);
- }
-
- PEM_write_bio_X509(mem, x);
- push_certinfo("Cert", i);
- }
-
- BIO_free(mem);
-
- return CURLE_OK;
-}
-
/*
* Heavily modified from:
* https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
@@ -3883,6 +3813,8 @@ static CURLcode servercert(struct Curl_easy *data,
BIO *mem = BIO_new(BIO_s_mem());
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
if(!mem) {
failf(data,
"BIO_new return NULL, " OSSL_PACKAGE
@@ -3893,8 +3825,8 @@ static CURLcode servercert(struct Curl_easy *data,
}
if(data->set.ssl.certinfo)
- /* we've been asked to gather certificate info! */
- (void)get_cert_chain(data, connssl);
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, connssl->backend->handle);
backend->server_cert = SSL_get1_peer_certificate(backend->handle);
if(!backend->server_cert) {
@@ -3902,7 +3834,7 @@ static CURLcode servercert(struct Curl_easy *data,
if(!strict)
return CURLE_OK;
- failf(data, "SSL: couldn't get peer certificate!");
+ failf(data, "SSL: couldn't get peer certificate");
return CURLE_PEER_FAILED_VERIFICATION;
}
@@ -3942,7 +3874,7 @@ static CURLcode servercert(struct Curl_easy *data,
buffer, sizeof(buffer));
if(rc) {
if(strict)
- failf(data, "SSL: couldn't get X509-issuer name!");
+ failf(data, "SSL: couldn't get X509-issuer name");
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
@@ -3953,9 +3885,20 @@ static CURLcode servercert(struct Curl_easy *data,
/* e.g. match issuer name with provided issuer certificate */
if(SSL_CONN_CONFIG(issuercert) || SSL_CONN_CONFIG(issuercert_blob)) {
- if(SSL_CONN_CONFIG(issuercert_blob))
+ if(SSL_CONN_CONFIG(issuercert_blob)) {
fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data,
(int)SSL_CONN_CONFIG(issuercert_blob)->len);
+ if(!fp) {
+ failf(data,
+ "BIO_new_mem_buf NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
else {
fp = BIO_new(BIO_s_file());
if(!fp) {
@@ -4049,7 +3992,7 @@ static CURLcode servercert(struct Curl_easy *data,
if(!result && ptr) {
result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
if(result)
- failf(data, "SSL: public key does not match pinned public key!");
+ failf(data, "SSL: public key does not match pinned public key");
}
X509_free(backend->server_cert);
@@ -4222,11 +4165,13 @@ static bool ossl_data_pending(const struct connectdata *conn,
int connindex)
{
const struct ssl_connect_data *connssl = &conn->ssl[connindex];
+ DEBUGASSERT(connssl->backend);
if(connssl->backend->handle && SSL_pending(connssl->backend->handle))
return TRUE;
#ifndef CURL_DISABLE_PROXY
{
const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex];
+ DEBUGASSERT(proxyssl->backend);
if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle))
return TRUE;
}
@@ -4253,6 +4198,8 @@ static ssize_t ossl_send(struct Curl_easy *data,
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
ERR_clear_error();
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
@@ -4332,6 +4279,8 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */
struct ssl_connect_data *connssl = &conn->ssl[num];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
ERR_clear_error();
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
@@ -4420,7 +4369,7 @@ static size_t ossl_version(char *buffer, size_t size)
}
count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver);
for(p = buffer; *p; ++p) {
- if(ISSPACE(*p))
+ if(ISBLANK(*p))
*p = '_';
}
return count;
@@ -4432,7 +4381,13 @@ static size_t ossl_version(char *buffer, size_t size)
(LIBRESSL_VERSION_NUMBER>>12)&0xff);
#endif
#elif defined(OPENSSL_IS_BORINGSSL)
+#ifdef CURL_BORINGSSL_VERSION
+ return msnprintf(buffer, size, "%s/%s",
+ OSSL_PACKAGE,
+ CURL_BORINGSSL_VERSION);
+#else
return msnprintf(buffer, size, OSSL_PACKAGE);
+#endif
#elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING)
return msnprintf(buffer, size, "%s/%s",
OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING));
@@ -4531,20 +4486,22 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
{
/* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
return info == CURLINFO_TLS_SESSION ?
(void *)backend->ctx : (void *)backend->handle;
}
-static void ossl_associate_connection(struct Curl_easy *data,
+static bool ossl_associate_connection(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
/* If we don't have SSL context, do nothing. */
if(!backend->handle)
- return;
+ return FALSE;
if(SSL_SET_OPTION(primary.sessionid)) {
int data_idx = ossl_get_ssl_data_index();
@@ -4554,19 +4511,26 @@ static void ossl_associate_connection(struct Curl_easy *data,
if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
proxy_idx >= 0) {
+ int data_status, conn_status, sockindex_status, proxy_status;
+
/* Store the data needed for the "new session" callback.
* The sockindex is stored as a pointer to an array element. */
- SSL_set_ex_data(backend->handle, data_idx, data);
- SSL_set_ex_data(backend->handle, connectdata_idx, conn);
- SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
+ data_status = SSL_set_ex_data(backend->handle, data_idx, data);
+ conn_status = SSL_set_ex_data(backend->handle, connectdata_idx, conn);
+ sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx,
+ conn->sock + sockindex);
#ifndef CURL_DISABLE_PROXY
- SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
- NULL);
+ proxy_status = SSL_set_ex_data(backend->handle, proxy_idx,
+ SSL_IS_PROXY() ? (void *) 1 : NULL);
#else
- SSL_set_ex_data(backend->handle, proxy_idx, NULL);
+ proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL);
#endif
+ if(data_status && conn_status && sockindex_status && proxy_status)
+ return TRUE;
}
+ return FALSE;
}
+ return TRUE;
}
/*
@@ -4583,6 +4547,7 @@ static void ossl_disassociate_connection(struct Curl_easy *data,
struct connectdata *conn = data->conn;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
/* If we don't have SSL context, do nothing. */
if(!backend->handle)
diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h
index 28058453c..9df4ecddb 100644
--- a/lib/vtls/openssl.h
+++ b/lib/vtls/openssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
@@ -29,13 +31,30 @@
* This header should only be needed to get included by vtls.c, openssl.c
* and ngtcp2.c
*/
+#include <openssl/ssl.h>
-#include <openssl/x509v3.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;
CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
- X509 *server_cert);
+ struct x509_st *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,
+ const struct curl_blob *cert_blob,
+ const char *cert_type, char *key_file,
+ const struct curl_blob *key_blob,
+ const char *key_type, char *key_passwd);
+
+CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl);
+
#endif /* USE_OPENSSL */
#endif /* HEADER_CURL_SSLUSE_H */
diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c
index 6dbb1ef3c..77a49f1ab 100644
--- a/lib/vtls/rustls.c
+++ b/lib/vtls/rustls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2021, Jacob Hoffman-Andrews,
+ * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
*
* This software is licensed as described in the file COPYING, which
@@ -19,6 +19,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
@@ -65,6 +67,7 @@ cr_data_pending(const struct connectdata *conn, int sockindex)
{
const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
return backend->data_pending;
}
@@ -118,7 +121,8 @@ cr_recv(struct Curl_easy *data, int sockindex,
struct connectdata *conn = data->conn;
struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
struct ssl_backend_data *const backend = connssl->backend;
- struct rustls_connection *const rconn = backend->conn;
+ struct rustls_connection *rconn = NULL;
+
size_t n = 0;
size_t tls_bytes_read = 0;
size_t plain_bytes_copied = 0;
@@ -126,6 +130,9 @@ cr_recv(struct Curl_easy *data, int sockindex,
char errorbuf[255];
rustls_io_result io_error;
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
io_error = rustls_connection_read_tls(rconn, read_cb,
&conn->sock[sockindex], &tls_bytes_read);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
@@ -215,13 +222,16 @@ cr_send(struct Curl_easy *data, int sockindex,
struct connectdata *conn = data->conn;
struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
struct ssl_backend_data *const backend = connssl->backend;
- struct rustls_connection *const rconn = backend->conn;
+ struct rustls_connection *rconn = NULL;
size_t plainwritten = 0;
size_t tlswritten = 0;
size_t tlswritten_total = 0;
rustls_result rresult;
rustls_io_result io_error;
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
infof(data, "cr_send %ld bytes of plaintext", plainlen);
if(plainlen > 0) {
@@ -295,9 +305,13 @@ static CURLcode
cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *const backend)
{
- struct rustls_connection *rconn = backend->conn;
+ struct rustls_connection *rconn = NULL;
struct rustls_client_config_builder *config_builder = NULL;
- const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile);
+ struct rustls_root_cert_store *roots = NULL;
+ const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob);
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile));
const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
const char *hostname = conn->host.name;
char errorbuf[256];
@@ -308,14 +322,17 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
{ (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH },
};
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
config_builder = rustls_client_config_builder_new();
#ifdef USE_HTTP2
- infof(data, "offering ALPN for HTTP/1.1 and HTTP/2");
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2);
#else
- infof(data, "offering ALPN for HTTP/1.1 only");
rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1);
#endif
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
if(!verifypeer) {
rustls_client_config_builder_dangerous_set_certificate_verifier(
config_builder, cr_verify_none);
@@ -328,6 +345,29 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
hostname = "example.invalid";
}
}
+ else if(ca_info_blob) {
+ roots = rustls_root_cert_store_new();
+
+ /* Enable strict parsing only if verification isn't disabled. */
+ result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data,
+ ca_info_blob->len, verifypeer);
+ if(result != RUSTLS_RESULT_OK) {
+ failf(data, "failed to parse trusted certificates from blob");
+ rustls_root_cert_store_free(roots);
+ rustls_client_config_free(
+ rustls_client_config_builder_build(config_builder));
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+
+ result = rustls_client_config_builder_use_roots(config_builder, roots);
+ rustls_root_cert_store_free(roots);
+ if(result != RUSTLS_RESULT_OK) {
+ failf(data, "failed to load trusted certificates");
+ rustls_client_config_free(
+ rustls_client_config_builder_build(config_builder));
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
else if(ssl_cafile) {
result = rustls_client_config_builder_load_roots_from_file(
config_builder, ssl_cafile);
@@ -341,7 +381,14 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
backend->config = rustls_client_config_builder_build(config_builder);
DEBUGASSERT(rconn == NULL);
- result = rustls_client_connection_new(backend->config, hostname, &rconn);
+ {
+ char *snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ result = rustls_client_connection_new(backend->config, snihost, &rconn);
+ }
if(result != RUSTLS_RESULT_OK) {
rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
@@ -361,27 +408,27 @@ cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn,
rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
if(!protocol) {
- infof(data, "ALPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
return;
}
#ifdef USE_HTTP2
if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
- infof(data, "ALPN, negotiated h2");
- conn->negnpn = CURL_HTTP_VERSION_2;
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2);
+ conn->alpn = CURL_HTTP_VERSION_2;
}
else
#endif
if(len == ALPN_HTTP_1_1_LENGTH &&
0 == memcmp(ALPN_HTTP_1_1, protocol, len)) {
- infof(data, "ALPN, negotiated http/1.1");
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1);
+ conn->alpn = CURL_HTTP_VERSION_1_1;
}
else {
infof(data, "ALPN, negotiated an unrecognized protocol");
}
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
@@ -401,6 +448,8 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t writefd;
curl_socket_t readfd;
+ DEBUGASSERT(backend);
+
if(ssl_connection_none == connssl->state) {
result = cr_init_backend(data, conn, connssl->backend);
if(result != CURLE_OK) {
@@ -495,7 +544,10 @@ cr_getsock(struct connectdata *conn, curl_socket_t *socks)
struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET];
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
struct ssl_backend_data *const backend = connssl->backend;
- struct rustls_connection *rconn = backend->conn;
+ struct rustls_connection *rconn = NULL;
+
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
if(rustls_connection_wants_write(rconn)) {
socks[0] = sockfd;
@@ -514,6 +566,7 @@ cr_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
{
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
return &backend->conn;
}
@@ -526,6 +579,8 @@ cr_close(struct Curl_easy *data, struct connectdata *conn,
CURLcode tmperr = CURLE_OK;
ssize_t n = 0;
+ DEBUGASSERT(backend);
+
if(backend->conn) {
rustls_connection_send_close_notify(backend->conn);
n = cr_send(data, sockindex, NULL, 0, &tmperr);
@@ -550,7 +605,8 @@ static size_t cr_version(char *buffer, size_t size)
const struct Curl_ssl Curl_ssl_rustls = {
{ CURLSSLBACKEND_RUSTLS, "rustls" },
- SSLSUPP_TLS13_CIPHERSUITES, /* supports */
+ SSLSUPP_CAINFO_BLOB | /* supports */
+ SSLSUPP_TLS13_CIPHERSUITES,
sizeof(struct ssl_backend_data),
Curl_none_init, /* init */
diff --git a/lib/vtls/rustls.h b/lib/vtls/rustls.h
index 056211dd8..6b393dd63 100644
--- a/lib/vtls/rustls.h
+++ b/lib/vtls/rustls.h
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2021, Jacob Hoffman-Andrews,
+ * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
*
* This software is licensed as described in the file COPYING, which
@@ -19,6 +19,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#ifndef HEADER_CURL_RUSTLS_H
#define HEADER_CURL_RUSTLS_H
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index 0a8e60610..454eb7967 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
*
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -51,6 +53,7 @@
#include "curl_printf.h"
#include "multiif.h"
#include "version_win32.h"
+#include "rand.h"
/* The last #include file should be: */
#include "curl_memory.h"
@@ -81,8 +84,35 @@
#endif
#endif
-#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX)
-#define HAS_CLIENT_CERT_PATH
+#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
+#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
+#endif
+
+#ifndef BCRYPT_CHAIN_MODE_CCM
+#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM"
+#endif
+
+#ifndef BCRYPT_CHAIN_MODE_GCM
+#define BCRYPT_CHAIN_MODE_GCM L"ChainingModeGCM"
+#endif
+
+#ifndef BCRYPT_AES_ALGORITHM
+#define BCRYPT_AES_ALGORITHM L"AES"
+#endif
+
+#ifndef BCRYPT_SHA256_ALGORITHM
+#define BCRYPT_SHA256_ALGORITHM L"SHA256"
+#endif
+
+#ifndef BCRYPT_SHA384_ALGORITHM
+#define BCRYPT_SHA384_ALGORITHM L"SHA384"
+#endif
+
+/* Workaround broken compilers like MinGW.
+ Return the number of elements in a statically sized array.
+*/
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
#endif
#ifdef HAS_CLIENT_CERT_PATH
@@ -117,6 +147,10 @@
#define SP_PROT_TLS1_2_CLIENT 0x00000800
#endif
+#ifndef SP_PROT_TLS1_3_CLIENT
+#define SP_PROT_TLS1_3_CLIENT 0x00002000
+#endif
+
#ifndef SCH_USE_STRONG_CRYPTO
#define SCH_USE_STRONG_CRYPTO 0x00400000
#endif
@@ -147,6 +181,10 @@
#define ALG_CLASS_DHASH ALG_CLASS_HASH
#endif
+#ifndef PKCS12_NO_PERSIST_KEY
+#define PKCS12_NO_PERSIST_KEY 0x00008000
+#endif
+
static Curl_recv schannel_recv;
static Curl_send schannel_send;
@@ -171,7 +209,7 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
}
static CURLcode
-set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct Curl_easy *data,
+set_ssl_version_min_max(DWORD *enabled_protocols, struct Curl_easy *data,
struct connectdata *conn)
{
long ssl_version = SSL_CONN_CONFIG(version);
@@ -181,23 +219,44 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct Curl_easy *data,
switch(ssl_version_max) {
case CURL_SSLVERSION_MAX_NONE:
case CURL_SSLVERSION_MAX_DEFAULT:
- ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+
+ /* Windows Server 2022 and newer (including Windows 11) support TLS 1.3
+ built-in. Previous builds of Windows 10 had broken TLS 1.3
+ implementations that could be enabled via registry.
+ */
+ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3;
+ }
+ else /* Windows 10 and older */
+ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+
break;
}
+
for(; i <= (ssl_version_max >> 16); ++i) {
switch(i) {
case CURL_SSLVERSION_TLSv1_0:
- schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT;
+ (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT;
break;
case CURL_SSLVERSION_TLSv1_1:
- schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT;
+ (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT;
break;
case CURL_SSLVERSION_TLSv1_2:
- schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT;
+ (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT;
break;
case CURL_SSLVERSION_TLSv1_3:
- failf(data, "schannel: TLS 1.3 is not yet supported");
- return CURLE_SSL_CONNECT_ERROR;
+
+ /* Windows Server 2022 and newer */
+ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT;
+ break;
+ }
+ else { /* Windows 10 and older */
+ failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
}
}
return CURLE_OK;
@@ -214,8 +273,12 @@ get_alg_id_by_name(char *name)
{
char tmp[LONGEST_ALG_ID] = { 0 };
char *nameEnd = strchr(name, ':');
- size_t n = nameEnd ? min((size_t)(nameEnd - name), LONGEST_ALG_ID - 1) : \
- min(strlen(name), LONGEST_ALG_ID - 1);
+ size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
+
+ /* reject too-long alg names */
+ if(n > (LONGEST_ALG_ID - 1))
+ return 0;
+
strncpy(tmp, name, n);
tmp[n] = 0;
CIPHEROPTION(CALG_MD2);
@@ -325,13 +388,15 @@ get_alg_id_by_name(char *name)
return 0;
}
+#define NUM_CIPHERS 47 /* There are 47 options listed above */
+
static CURLcode
set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
ALG_ID *algIds)
{
char *startCur = ciphers;
int algCount = 0;
- while(startCur && (0 != *startCur) && (algCount < NUMOF_CIPHERS)) {
+ while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) {
long alg = strtol(startCur, 0, 0);
if(!alg)
alg = get_alg_id_by_name(startCur);
@@ -381,13 +446,13 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0)
*store_name = CERT_SYSTEM_STORE_USERS;
else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"),
- store_name_len) == 0)
+ store_name_len) == 0)
*store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY;
else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"),
- store_name_len) == 0)
+ store_name_len) == 0)
*store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY;
else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"),
- store_name_len) == 0)
+ store_name_len) == 0)
*store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE;
else
return CURLE_SSL_CERTPROBLEM;
@@ -417,46 +482,52 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- SCHANNEL_CRED schannel_cred;
+
+#ifdef HAS_CLIENT_CERT_PATH
PCCERT_CONTEXT client_certs[1] = { NULL };
+ HCERTSTORE client_cert_store = NULL;
+#endif
SECURITY_STATUS sspi_status = SEC_E_OK;
CURLcode result;
- struct ssl_backend_data *backend = connssl->backend;
/* setup Schannel API options */
- memset(&schannel_cred, 0, sizeof(schannel_cred));
- schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+ DWORD flags = 0;
+ DWORD enabled_protocols = 0;
+
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(backend);
if(conn->ssl_config.verifypeer) {
#ifdef HAS_MANUAL_VERIFY_API
if(backend->use_manual_cred_validation)
- schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
+ flags = SCH_CRED_MANUAL_CRED_VALIDATION;
else
#endif
- schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
+ flags = SCH_CRED_AUTO_CRED_VALIDATION;
if(SSL_SET_OPTION(no_revoke)) {
- schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
SCH_CRED_IGNORE_REVOCATION_OFFLINE;
DEBUGF(infof(data, "schannel: disabled server certificate revocation "
"checks"));
}
else if(SSL_SET_OPTION(revoke_best_effort)) {
- schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN;
DEBUGF(infof(data, "schannel: ignore revocation offline errors"));
}
else {
- schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
+ flags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
DEBUGF(infof(data,
"schannel: checking server certificate revocation"));
}
}
else {
- schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
+ flags = SCH_CRED_MANUAL_CRED_VALIDATION |
SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
SCH_CRED_IGNORE_REVOCATION_OFFLINE;
DEBUGF(infof(data,
@@ -464,15 +535,15 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
}
if(!conn->ssl_config.verifyhost) {
- schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
+ flags |= SCH_CRED_NO_SERVERNAME_CHECK;
DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from "
"comparing the supplied target name with the subject "
"names in server certificates."));
}
if(!SSL_SET_OPTION(auto_client_cert)) {
- schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS;
- schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+ flags &= ~SCH_CRED_USE_DEFAULT_CREDS;
+ flags |= SCH_CRED_NO_DEFAULT_CREDS;
infof(data, "schannel: disabled automatic use of client certificate");
}
else
@@ -486,7 +557,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
case CURL_SSLVERSION_TLSv1_2:
case CURL_SSLVERSION_TLSv1_3:
{
- result = set_ssl_version_min_max(&schannel_cred, data, conn);
+ result = set_ssl_version_min_max(&enabled_protocols, data, conn);
if(result != CURLE_OK)
return result;
break;
@@ -500,16 +571,6 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
return CURLE_SSL_CONNECT_ERROR;
}
- if(SSL_CONN_CONFIG(cipher_list)) {
- result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list),
- backend->algIds);
- if(CURLE_OK != result) {
- failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
- return result;
- }
- }
-
-
#ifdef HAS_CLIENT_CERT_PATH
/* client certificate */
if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
@@ -535,7 +596,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
result = get_cert_location(cert_path, &cert_store_name,
- &cert_store_path, &cert_thumbprint_str);
+ &cert_store_path, &cert_thumbprint_str);
if(result && (data->set.ssl.primary.clientcert[0]!='\0'))
fInCert = fopen(data->set.ssl.primary.clientcert, "rb");
@@ -550,18 +611,18 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
}
if((fInCert || blob) && (data->set.ssl.cert_type) &&
- (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
+ (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
failf(data, "schannel: certificate format compatibility error "
- " for %s",
- blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
+ " for %s",
+ blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
curlx_unicodefree(cert_path);
return CURLE_SSL_CERTPROBLEM;
}
if(fInCert || blob) {
/* Reading a .P12 or .pfx file, like the example at bottom of
- https://social.msdn.microsoft.com/Forums/windowsdesktop/
- en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
+ https://social.msdn.microsoft.com/Forums/windowsdesktop/
+ en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
*/
CRYPT_DATA_BLOB datablob;
WCHAR* pszPassword;
@@ -589,7 +650,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
fclose(fInCert);
if(!continue_reading) {
failf(data, "schannel: Failed to read cert file %s",
- data->set.ssl.primary.clientcert);
+ data->set.ssl.primary.clientcert);
free(certdata);
return CURLE_SSL_CERTPROBLEM;
}
@@ -605,16 +666,23 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
if(pszPassword) {
if(pwd_len > 0)
str_w_len = MultiByteToWideChar(CP_UTF8,
- MB_ERR_INVALID_CHARS,
- data->set.ssl.key_passwd, (int)pwd_len,
- pszPassword, (int)(pwd_len + 1));
+ MB_ERR_INVALID_CHARS,
+ data->set.ssl.key_passwd,
+ (int)pwd_len,
+ pszPassword, (int)(pwd_len + 1));
if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
pszPassword[str_w_len] = 0;
else
pszPassword[0] = 0;
- cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
+ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ cert_store = PFXImportCertStore(&datablob, pszPassword,
+ PKCS12_NO_PERSIST_KEY);
+ else
+ cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
+
free(pszPassword);
}
if(!blob)
@@ -643,9 +711,6 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM;
}
-
- schannel_cred.cCreds = 1;
- schannel_cred.paCred = client_certs;
}
else {
cert_store =
@@ -683,17 +748,13 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
curlx_unicodefree(cert_path);
- if(client_certs[0]) {
- schannel_cred.cCreds = 1;
- schannel_cred.paCred = client_certs;
- }
- else {
+ if(!client_certs[0]) {
/* CRYPT_E_NOT_FOUND / E_INVALIDARG */
CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM;
}
}
- CertCloseStore(cert_store, 0);
+ client_cert_store = cert_store;
}
#else
if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
@@ -708,22 +769,279 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
if(!backend->cred) {
failf(data, "schannel: unable to allocate memory");
+#ifdef HAS_CLIENT_CERT_PATH
if(client_certs[0])
CertFreeCertificateContext(client_certs[0]);
+ if(client_cert_store)
+ CertCloseStore(client_cert_store, 0);
+#endif
return CURLE_OUT_OF_MEMORY;
}
backend->cred->refcount = 1;
- sspi_status =
- s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
- SECPKG_CRED_OUTBOUND, NULL,
- &schannel_cred, NULL, NULL,
- &backend->cred->cred_handle,
- &backend->cred->time_stamp);
+#ifdef HAS_CLIENT_CERT_PATH
+ /* Since we did not persist the key, we need to extend the store's
+ * lifetime until the end of the connection
+ */
+ backend->cred->client_cert_store = client_cert_store;
+#endif
+
+ /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */
+ if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+
+ char *ciphers13 = 0;
+
+ bool disable_aes_gcm_sha384 = FALSE;
+ bool disable_aes_gcm_sha256 = FALSE;
+ bool disable_chacha_poly = FALSE;
+ bool disable_aes_ccm_8_sha256 = FALSE;
+ bool disable_aes_ccm_sha256 = FALSE;
+
+ SCH_CREDENTIALS credentials = { 0 };
+ TLS_PARAMETERS tls_parameters = { 0 };
+ CRYPTO_SETTINGS crypto_settings[4] = { 0 };
+ UNICODE_STRING blocked_ccm_modes[1] = { 0 };
+ UNICODE_STRING blocked_gcm_modes[1] = { 0 };
+
+ int crypto_settings_idx = 0;
+
+
+ /* If TLS 1.3 ciphers are explicitly listed, then
+ * disable all the ciphers and re-enable which
+ * ciphers the user has provided.
+ */
+ ciphers13 = SSL_CONN_CONFIG(cipher_list13);
+ if(ciphers13) {
+ const int remaining_ciphers = 5;
+
+ /* detect which remaining ciphers to enable
+ and then disable everything else.
+ */
+
+ 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;
+ disable_chacha_poly = TRUE;
+ disable_aes_ccm_8_sha256 = TRUE;
+ disable_aes_ccm_sha256 = TRUE;
+
+ while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
+ nameEnd = strchr(startCur, ':');
+ n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
+
+ /* reject too-long cipher names */
+ if(n > (LONGEST_ALG_ID - 1)) {
+ failf(data, "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)) {
+ disable_aes_gcm_sha384 = FALSE;
+ }
+ else if(disable_aes_gcm_sha256
+ && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) {
+ disable_aes_gcm_sha256 = FALSE;
+ }
+ else if(disable_chacha_poly
+ && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) {
+ disable_chacha_poly = FALSE;
+ }
+ else if(disable_aes_ccm_8_sha256
+ && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) {
+ disable_aes_ccm_8_sha256 = FALSE;
+ }
+ else if(disable_aes_ccm_sha256
+ && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) {
+ disable_aes_ccm_sha256 = FALSE;
+ }
+ else {
+ failf(data, "Passed in an unknown TLS 1.3 cipher.");
+ return CURLE_SSL_CIPHER;
+ }
+ startCur = nameEnd;
+ if(startCur)
+ startCur++;
+
+ algCount++;
+ }
+ }
+
+ if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256
+ && disable_chacha_poly && disable_aes_ccm_8_sha256
+ && disable_aes_ccm_sha256) {
+ failf(data, "All available TLS 1.3 ciphers were disabled.");
+ return CURLE_SSL_CIPHER;
+ }
+
+ /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */
+ if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) {
+ /*
+ Disallow AES_CCM algorithm.
+ */
+ blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM);
+ blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM);
+ blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM;
+
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageCipher;
+ crypto_settings[crypto_settings_idx].rgstrChainingModes =
+ blocked_ccm_modes;
+ crypto_settings[crypto_settings_idx].cChainingModes =
+ ARRAYSIZE(blocked_ccm_modes);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)BCRYPT_AES_ALGORITHM;
+
+ /* only disabling one of the CCM modes */
+ if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) {
+ if(disable_aes_ccm_8_sha256)
+ crypto_settings[crypto_settings_idx].dwMinBitLength = 128;
+ else /* disable_aes_ccm_sha256 */
+ crypto_settings[crypto_settings_idx].dwMaxBitLength = 64;
+ }
+
+ crypto_settings_idx++;
+ }
+
+ /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */
+ if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) {
+
+ /*
+ Disallow AES_GCM algorithm
+ */
+ blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM);
+ blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM);
+ blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM;
+
+ /* if only one is disabled, then explicitly disable the
+ digest cipher suite (sha384 or sha256) */
+ if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) {
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageDigest;
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(disable_aes_gcm_sha384 ?
+ BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(disable_aes_gcm_sha384 ?
+ BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)(disable_aes_gcm_sha384 ?
+ BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+ }
+ else { /* Disable both AES_GCM ciphers */
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageCipher;
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(BCRYPT_AES_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)BCRYPT_AES_ALGORITHM;
+ }
+
+ crypto_settings[crypto_settings_idx].rgstrChainingModes =
+ blocked_gcm_modes;
+ crypto_settings[crypto_settings_idx].cChainingModes = 1;
+
+ crypto_settings_idx++;
+ }
+
+ /*
+ Disable ChaCha20-Poly1305.
+ */
+ if(disable_chacha_poly) {
+ crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+ TlsParametersCngAlgUsageCipher;
+ crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+ sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+ sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
+ crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+ (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM;
+ crypto_settings_idx++;
+ }
+
+ tls_parameters.pDisabledCrypto = crypto_settings;
+
+ /* The number of blocked suites */
+ tls_parameters.cDisabledCrypto = crypto_settings_idx;
+ credentials.pTlsParameters = &tls_parameters;
+ credentials.cTlsParameters = 1;
+
+ credentials.dwVersion = SCH_CREDENTIALS_VERSION;
+ credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO;
+
+ credentials.pTlsParameters->grbitDisabledProtocols =
+ (DWORD)~enabled_protocols;
+
+#ifdef HAS_CLIENT_CERT_PATH
+ if(client_certs[0]) {
+ credentials.cCreds = 1;
+ credentials.paCred = client_certs;
+ }
+#endif
+
+ sspi_status =
+ s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+ SECPKG_CRED_OUTBOUND, NULL,
+ &credentials, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
+ }
+ else {
+ /* Pre-Windows 10 1809 */
+ ALG_ID algIds[NUM_CIPHERS];
+ char *ciphers = SSL_CONN_CONFIG(cipher_list);
+ SCHANNEL_CRED schannel_cred = { 0 };
+ schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+ schannel_cred.dwFlags = flags;
+ schannel_cred.grbitEnabledProtocols = enabled_protocols;
+
+ if(ciphers) {
+ result = set_ssl_ciphers(&schannel_cred, ciphers, algIds);
+ if(CURLE_OK != result) {
+ failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
+ return result;
+ }
+ }
+ else {
+ schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO;
+ }
+
+#ifdef HAS_CLIENT_CERT_PATH
+ if(client_certs[0]) {
+ schannel_cred.cCreds = 1;
+ schannel_cred.paCred = client_certs;
+ }
+#endif
+
+ sspi_status =
+ s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+ SECPKG_CRED_OUTBOUND, NULL,
+ &schannel_cred, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
+ }
+
+#ifdef HAS_CLIENT_CERT_PATH
if(client_certs[0])
CertFreeCertificateContext(client_certs[0]);
+#endif
if(sspi_status != SEC_E_OK) {
char buffer[STRERROR_LEN];
@@ -765,11 +1083,12 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
#ifdef ENABLE_IPV6
struct in6_addr addr6;
#endif
- TCHAR *host_name;
CURLcode result;
char * const hostname = SSL_HOST_NAME();
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %hu (step 1/3)",
hostname, conn->remote_port));
@@ -846,10 +1165,21 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
}
if(!backend->cred) {
+ char *snihost;
result = schannel_acquire_credential_handle(data, conn, sockindex);
if(result != CURLE_OK) {
return result;
}
+ /* A hostname associated with the credential is needed by
+ InitializeSecurityContext for SNI and other reasons. */
+ snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ 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 */
@@ -891,14 +1221,14 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
alpn_buffer[cur++] = ALPN_H2_LENGTH;
memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH);
cur += ALPN_H2_LENGTH;
- infof(data, "schannel: ALPN, offering %s", ALPN_H2);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
}
#endif
alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
cur += ALPN_HTTP_1_1_LENGTH;
- infof(data, "schannel: ALPN, offering %s", ALPN_HTTP_1_1);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
*list_len = curlx_uitous(cur - list_start_index);
*extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
@@ -936,10 +1266,6 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
return CURLE_OUT_OF_MEMORY;
}
- host_name = curlx_convert_UTF8_to_tchar(hostname);
- if(!host_name)
- return CURLE_OUT_OF_MEMORY;
-
/* Schannel InitializeSecurityContext:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
@@ -948,13 +1274,12 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
us problems with inbuf regardless. https://github.com/curl/curl/issues/983
*/
sspi_status = s_pSecFn->InitializeSecurityContext(
- &backend->cred->cred_handle, NULL, host_name, backend->req_flags, 0, 0,
+ &backend->cred->cred_handle, NULL, backend->cred->sni_hostname,
+ backend->req_flags, 0, 0,
(backend->use_alpn ? &inbuf_desc : NULL),
0, &backend->ctxt->ctxt_handle,
&outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp);
- curlx_unicodefree(host_name);
-
if(sspi_status != SEC_I_CONTINUE_NEEDED) {
char buffer[STRERROR_LEN];
Curl_safefree(backend->ctxt);
@@ -1004,6 +1329,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
backend->recv_unrecoverable_err = CURLE_OK;
backend->recv_sspi_close_notify = false;
backend->recv_connection_closed = false;
+ backend->recv_renegotiating = false;
backend->encdata_is_incomplete = false;
/* continue to second handshake step */
@@ -1027,15 +1353,16 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
SECURITY_STATUS sspi_status = SEC_E_OK;
CURLcode result;
bool doread;
- char * const hostname = SSL_HOST_NAME();
const char *pubkey_ptr;
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %hu (step 2/3)",
- hostname, conn->remote_port));
+ SSL_HOST_NAME(), conn->remote_port));
if(!backend->cred || !backend->ctxt)
return CURLE_SSL_CONNECT_ERROR;
@@ -1083,7 +1410,6 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
}
for(;;) {
- TCHAR *host_name;
if(doread) {
/* read encrypted handshake data from socket */
result = Curl_read_plain(conn->sock[sockindex],
@@ -1136,17 +1462,12 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
memcpy(inbuf[0].pvBuffer, backend->encdata_buffer,
backend->encdata_offset);
- host_name = curlx_convert_UTF8_to_tchar(hostname);
- if(!host_name)
- return CURLE_OUT_OF_MEMORY;
-
sspi_status = s_pSecFn->InitializeSecurityContext(
&backend->cred->cred_handle, &backend->ctxt->ctxt_handle,
- host_name, backend->req_flags, 0, 0, &inbuf_desc, 0, NULL,
+ backend->cred->sni_hostname, backend->req_flags,
+ 0, 0, &inbuf_desc, 0, NULL,
&outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp);
- curlx_unicodefree(host_name);
-
/* free buffer for received handshake data */
Curl_safefree(inbuf[0].pvBuffer);
@@ -1279,7 +1600,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
if(pubkey_ptr) {
result = pkp_pin_peer_pubkey(data, conn, sockindex, pubkey_ptr);
if(result) {
- failf(data, "SSL: public key does not match pinned public key!");
+ failf(data, "SSL: public key does not match pinned public key");
return result;
}
}
@@ -1370,6 +1691,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *backend = connssl->backend;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %hu (step 3/3)",
@@ -1407,27 +1729,41 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
if(alpn_result.ProtoNegoStatus ==
SecApplicationProtocolNegotiationStatus_Success) {
+ unsigned char alpn = 0;
- infof(data, "schannel: ALPN, server accepted to use %.*s",
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
#ifdef USE_HTTP2
if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
!memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
- conn->negnpn = CURL_HTTP_VERSION_2;
+ alpn = CURL_HTTP_VERSION_2;
}
else
#endif
if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
!memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
ALPN_HTTP_1_1_LENGTH)) {
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ alpn = CURL_HTTP_VERSION_1_1;
+ }
+ if(backend->recv_renegotiating) {
+ if(alpn != conn->alpn) {
+ failf(data, "schannel: server selected an ALPN protocol too late");
+ return CURLE_SSL_CONNECT_ERROR;
}
+ }
+ else
+ conn->alpn = alpn;
+ }
+ else {
+ if(!backend->recv_renegotiating)
+ infof(data, VTLS_INFOF_NO_ALPN);
+ }
+
+ if(!backend->recv_renegotiating) {
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
- else
- infof(data, "ALPN, server did not agree to a protocol");
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -1599,8 +1935,15 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn,
if(ssl_connect_done == connssl->connecting_state) {
connssl->state = ssl_connection_complete;
- conn->recv[sockindex] = schannel_recv;
- conn->send[sockindex] = schannel_send;
+ if(!connssl->backend->recv_renegotiating) {
+ /* On renegotiation, we don't want to reset the existing recv/send
+ * function pointers. They will have been set after the initial TLS
+ * handshake was completed. If they were subsequently modified, as
+ * is the case with HTTP/2, we don't want to override that change.
+ */
+ conn->recv[sockindex] = schannel_recv;
+ conn->send[sockindex] = schannel_send;
+ }
#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
/* When SSPI is used in combination with Schannel
@@ -1610,6 +1953,7 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn,
*/
{
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
conn->sslContext = &backend->ctxt->ctxt_handle;
}
#endif
@@ -1640,6 +1984,8 @@ schannel_send(struct Curl_easy *data, int sockindex,
CURLcode result;
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
/* check if the maximum stream sizes were queried */
if(backend->stream_sizes.cbMaximumMessage == 0) {
sspi_status = s_pSecFn->QueryContextAttributes(
@@ -1788,6 +2134,8 @@ schannel_recv(struct Curl_easy *data, int sockindex,
size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
/****************************************************************************
* Don't return or set backend->recv_unrecoverable_err unless in the cleanup.
* The pattern for return error is set *err, optional infof, goto cleanup.
@@ -1977,17 +2325,14 @@ schannel_recv(struct Curl_easy *data, int sockindex,
infof(data, "schannel: can't renegotiate, an error is pending");
goto cleanup;
}
- if(backend->encdata_offset) {
- *err = CURLE_RECV_ERROR;
- infof(data, "schannel: can't renegotiate, "
- "encrypted data available");
- goto cleanup;
- }
+
/* begin renegotiation */
infof(data, "schannel: renegotiating SSL/TLS connection");
connssl->state = ssl_connection_negotiating;
connssl->connecting_state = ssl_connect_2_writing;
+ backend->recv_renegotiating = true;
*err = schannel_connect_common(data, conn, sockindex, FALSE, &done);
+ backend->recv_renegotiating = false;
if(*err) {
infof(data, "schannel: renegotiation failed");
goto cleanup;
@@ -2122,6 +2467,8 @@ static bool schannel_data_pending(const struct connectdata *conn,
const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
+
if(connssl->use) /* SSL/TLS is in use */
return (backend->decdata_offset > 0 ||
(backend->encdata_offset > 0 && !backend->encdata_is_incomplete));
@@ -2138,6 +2485,13 @@ static void schannel_session_free(void *ptr)
cred->refcount--;
if(cred->refcount == 0) {
s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+ curlx_unicodefree(cred->sni_hostname);
+#ifdef HAS_CLIENT_CERT_PATH
+ if(cred->client_cert_store) {
+ CertCloseStore(cred->client_cert_store, 0);
+ cred->client_cert_store = NULL;
+ }
+#endif
Curl_safefree(cred);
}
}
@@ -2157,6 +2511,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *backend = connssl->backend;
DEBUGASSERT(data);
+ DEBUGASSERT(backend);
if(connssl->use) {
infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu",
@@ -2170,7 +2525,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
SecBuffer outbuf;
SecBufferDesc outbuf_desc;
CURLcode result;
- TCHAR *host_name;
DWORD dwshut = SCHANNEL_SHUTDOWN;
InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
@@ -2185,10 +2539,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
}
- host_name = curlx_convert_UTF8_to_tchar(hostname);
- if(!host_name)
- return CURLE_OUT_OF_MEMORY;
-
/* setup output buffer */
InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
@@ -2196,7 +2546,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
sspi_status = s_pSecFn->InitializeSecurityContext(
&backend->cred->cred_handle,
&backend->ctxt->ctxt_handle,
- host_name,
+ backend->cred->sni_hostname,
backend->req_flags,
0,
0,
@@ -2207,8 +2557,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
&backend->ret_flags,
&backend->ctxt->time_stamp);
- curlx_unicodefree(host_name);
-
if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
/* send close message which is in output buffer */
ssize_t written;
@@ -2286,21 +2634,9 @@ static size_t schannel_version(char *buffer, size_t size)
static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM,
unsigned char *entropy, size_t length)
{
- HCRYPTPROV hCryptProv = 0;
-
(void)data;
- if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
- CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
- return CURLE_FAILED_INIT;
-
- if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
- CryptReleaseContext(hCryptProv, 0UL);
- return CURLE_FAILED_INIT;
- }
-
- CryptReleaseContext(hCryptProv, 0UL);
- return CURLE_OK;
+ return Curl_win32_random(entropy, length);
}
static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
@@ -2314,6 +2650,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
/* Result is returned to caller */
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ DEBUGASSERT(backend);
+
/* if a path wasn't specified, don't pin */
if(!pinnedpubkey)
return CURLE_OK;
@@ -2359,7 +2697,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
(const unsigned char *)pubkey->header,
(size_t)(pubkey->end - pubkey->header));
if(result) {
- failf(data, "SSL: public key does not match pinned public key!");
+ failf(data, "SSL: public key does not match pinned public key");
}
} while(0);
@@ -2434,6 +2772,7 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl,
{
struct ssl_backend_data *backend = connssl->backend;
(void)info;
+ DEBUGASSERT(backend);
return &backend->ctxt->ctxt_handle;
}
@@ -2444,7 +2783,8 @@ const struct Curl_ssl Curl_ssl_schannel = {
#ifdef HAS_MANUAL_VERIFY_API
SSLSUPP_CAINFO_BLOB |
#endif
- SSLSUPP_PINNEDPUBKEY,
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_TLS13_CIPHERSUITES,
sizeof(struct ssl_backend_data),
diff --git a/lib/vtls/schannel.h b/lib/vtls/schannel.h
index 77853aa30..24d7eff25 100644
--- a/lib/vtls/schannel.h
+++ b/lib/vtls/schannel.h
@@ -8,7 +8,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -21,11 +21,35 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_SCHANNEL
+#define SCHANNEL_USE_BLACKLISTS 1
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4201)
+#endif
+#include <subauth.h>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+/* Undefine wincrypt conflicting symbols for BoringSSL. */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
#include <schnlsp.h>
#include <schannel.h>
#include "curl_sspi.h"
@@ -59,23 +83,87 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data,
/* structs to expose only in schannel.c and schannel_verify.c */
#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+#include <wincrypt.h>
+
#ifdef __MINGW32__
-#include <_mingw.h>
#ifdef __MINGW64_VERSION_MAJOR
#define HAS_MANUAL_VERIFY_API
#endif
#else
-#include <wincrypt.h>
#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN
#define HAS_MANUAL_VERIFY_API
#endif
#endif
-#define NUMOF_CIPHERS 45 /* There are 45 listed in the MS headers */
+#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) \
+ && !defined(DISABLE_SCHANNEL_CLIENT_CERT)
+#define HAS_CLIENT_CERT_PATH
+#endif
+
+#ifndef SCH_CREDENTIALS_VERSION
+
+#define SCH_CREDENTIALS_VERSION 0x00000005
+
+typedef enum _eTlsAlgorithmUsage
+{
+ TlsParametersCngAlgUsageKeyExchange,
+ TlsParametersCngAlgUsageSignature,
+ TlsParametersCngAlgUsageCipher,
+ TlsParametersCngAlgUsageDigest,
+ TlsParametersCngAlgUsageCertSig
+} eTlsAlgorithmUsage;
+
+typedef struct _CRYPTO_SETTINGS
+{
+ eTlsAlgorithmUsage eAlgorithmUsage;
+ UNICODE_STRING strCngAlgId;
+ DWORD cChainingModes;
+ PUNICODE_STRING rgstrChainingModes;
+ DWORD dwMinBitLength;
+ DWORD dwMaxBitLength;
+} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS;
+
+typedef struct _TLS_PARAMETERS
+{
+ DWORD cAlpnIds;
+ PUNICODE_STRING rgstrAlpnIds;
+ DWORD grbitDisabledProtocols;
+ DWORD cDisabledCrypto;
+ PCRYPTO_SETTINGS pDisabledCrypto;
+ DWORD dwFlags;
+} TLS_PARAMETERS, * PTLS_PARAMETERS;
+
+typedef struct _SCH_CREDENTIALS
+{
+ DWORD dwVersion;
+ DWORD dwCredFormat;
+ DWORD cCreds;
+ PCCERT_CONTEXT* paCred;
+ HCERTSTORE hRootStore;
+
+ DWORD cMappers;
+ struct _HMAPPER **aphMappers;
+
+ DWORD dwSessionLifespan;
+ DWORD dwFlags;
+ DWORD cTlsParameters;
+ PTLS_PARAMETERS pTlsParameters;
+} SCH_CREDENTIALS, * PSCH_CREDENTIALS;
+
+#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
+#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
+#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
+#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
+
+#endif
struct Curl_schannel_cred {
CredHandle cred_handle;
TimeStamp time_stamp;
+ TCHAR *sni_hostname;
+#ifdef HAS_CLIENT_CERT_PATH
+ HCERTSTORE client_cert_store;
+#endif
int refcount;
};
@@ -100,11 +188,11 @@ struct ssl_backend_data {
CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
bool recv_sspi_close_notify; /* true if connection closed by close_notify */
bool recv_connection_closed; /* true if connection closed, regardless how */
+ bool recv_renegotiating; /* true if recv is doing renegotiation */
bool use_alpn; /* true if ALPN is used for this connection */
#ifdef HAS_MANUAL_VERIFY_API
bool use_manual_cred_validation; /* true if manual cred validation is used */
#endif
- ALG_ID algIds[NUMOF_CIPHERS];
};
#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */
diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c
index 4966cd494..1ac1d3eaf 100644
--- a/lib/vtls/schannel_verify.c
+++ b/lib/vtls/schannel_verify.c
@@ -7,7 +7,7 @@
*
* Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -286,7 +288,6 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
goto cleanup;
}
- result = CURLE_OK;
while(total_bytes_read < ca_file_bufsize) {
DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read);
DWORD bytes_read = 0;
@@ -313,9 +314,6 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
/* Null terminate the buffer */
ca_file_buffer[ca_file_bufsize] = '\0';
- if(result != CURLE_OK) {
- goto cleanup;
- }
result = add_certs_data_to_store(trust_store,
ca_file_buffer, ca_file_bufsize,
ca_file,
@@ -465,6 +463,7 @@ static CURLcode verify_host(struct Curl_easy *data,
CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
TCHAR *cert_hostname_buff = NULL;
size_t cert_hostname_buff_index = 0;
+ size_t hostlen = strlen(conn_hostname);
DWORD len = 0;
DWORD actual_len = 0;
@@ -520,10 +519,8 @@ static CURLcode verify_host(struct Curl_easy *data,
result = CURLE_OUT_OF_MEMORY;
}
else {
- int match_result;
-
- match_result = Curl_cert_hostcheck(cert_hostname, conn_hostname);
- if(match_result == CURL_HOST_MATCH) {
+ if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname),
+ conn_hostname, hostlen)) {
infof(data,
"schannel: connection hostname (%s) validated "
"against certificate name (%s)",
@@ -577,6 +574,8 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data,
HCERTSTORE trust_store = NULL;
const char * const conn_hostname = SSL_HOST_NAME();
+ DEBUGASSERT(BACKEND);
+
sspi_status =
s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT,
diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c
index f7a20b20b..c764e3631 100644
--- a/lib/vtls/sectransp.c
+++ b/lib/vtls/sectransp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>.
*
* This software is licensed as described in the file COPYING, which
@@ -19,6 +19,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -603,7 +605,7 @@ const static struct st_cipher ciphertable[] = {
CIPHER_WEAK_RC_ENCRYPTION),
CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */
"ECDH-ECDSA-DES-CBC3-SHA",
- CIPHER_STRONG_ENOUGH),
+ CIPHER_WEAK_3DES_ENCRYPTION),
CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */
"ECDH-ECDSA-AES128-SHA",
CIPHER_STRONG_ENOUGH),
@@ -837,12 +839,14 @@ static OSStatus SocketRead(SSLConnectionRef connection,
/*int sock = *(int *)connection;*/
struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection;
struct ssl_backend_data *backend = connssl->backend;
- int sock = backend->ssl_sockfd;
+ int sock;
OSStatus rtn = noErr;
size_t bytesRead;
ssize_t rrtn;
int theErr;
+ DEBUGASSERT(backend);
+ sock = backend->ssl_sockfd;
*dataLength = 0;
for(;;) {
@@ -898,13 +902,15 @@ static OSStatus SocketWrite(SSLConnectionRef connection,
/*int sock = *(int *)connection;*/
struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection;
struct ssl_backend_data *backend = connssl->backend;
- int sock = backend->ssl_sockfd;
+ int sock;
ssize_t length;
size_t dataLen = *dataLength;
const UInt8 *dataPtr = (UInt8 *)data;
OSStatus ortn;
int theErr;
+ DEBUGASSERT(backend);
+ sock = backend->ssl_sockfd;
*dataLength = 0;
do {
@@ -934,9 +940,9 @@ static OSStatus SocketWrite(SSLConnectionRef connection,
#ifndef CURL_DISABLE_VERBOSE_STRINGS
CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
{
- /* The first ciphers in the ciphertable are continuos. Here we do small
+ /* The first ciphers in the ciphertable are continuous. Here we do small
optimization and instead of loop directly get SSL name by cipher number.
- */
+ */
if(cipher <= SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) {
return ciphertable[cipher].name;
}
@@ -1376,6 +1382,8 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn,
long ssl_version_max = SSL_CONN_CONFIG(version_max);
long max_supported_version_by_os;
+ DEBUGASSERT(backend);
+
/* macOS 10.5-10.7 supported TLS 1.0 only.
macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2.
macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */
@@ -1684,6 +1692,8 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
#if CURL_BUILD_MAC
int darwinver_maj = 0, darwinver_min = 0;
+ DEBUGASSERT(backend);
+
GetDarwinVersionNumber(&darwinver_maj, &darwinver_min);
#endif /* CURL_BUILD_MAC */
@@ -1693,7 +1703,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
CFRelease(backend->ssl_ctx);
backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
if(!backend->ssl_ctx) {
- failf(data, "SSL: couldn't create a context!");
+ failf(data, "SSL: couldn't create a context");
return CURLE_OUT_OF_MEMORY;
}
}
@@ -1843,12 +1853,12 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
#endif
) {
CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2));
- infof(data, "ALPN, offering %s", ALPN_H2);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
}
#endif
CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1));
- infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
/* expects length prefixed preference ordered list of protocols in wire
* format
@@ -2028,12 +2038,18 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
* Both hostname check and SNI require SSLSetPeerDomainName().
* Also: the verifyhost setting influences SNI usage */
if(conn->ssl_config.verifyhost) {
- err = SSLSetPeerDomainName(backend->ssl_ctx, hostname,
- strlen(hostname));
+ size_t snilen;
+ char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
+ if(!snihost) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen);
if(err != noErr) {
- infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d",
+ failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d",
err);
+ return CURLE_SSL_CONNECT_ERROR;
}
if((Curl_inet_pton(AF_INET, hostname, &addr))
@@ -2542,6 +2558,7 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn,
DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
+ DEBUGASSERT(backend);
/* Here goes nothing: */
err = SSLHandshake(backend->ssl_ctx);
@@ -2774,7 +2791,7 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn,
pkp_pin_peer_pubkey(data, backend->ssl_ctx,
data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
if(result) {
- failf(data, "SSL: public key does not match pinned public key!");
+ failf(data, "SSL: public key does not match pinned public key");
return result;
}
}
@@ -2830,18 +2847,18 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn,
#ifdef USE_HTTP2
if(chosenProtocol &&
!CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) {
- conn->negnpn = CURL_HTTP_VERSION_2;
+ conn->alpn = CURL_HTTP_VERSION_2;
}
else
#endif
if(chosenProtocol &&
!CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) {
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ conn->alpn = CURL_HTTP_VERSION_1_1;
}
else
- infof(data, "ALPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
/* chosenProtocol is a reference to the string within alpnArr
@@ -2918,6 +2935,8 @@ collect_server_cert(struct Curl_easy *data,
CFIndex i, count;
SecTrustRef trust = NULL;
+ DEBUGASSERT(backend);
+
if(!show_verbose_server_cert && !data->set.ssl.certinfo)
return CURLE_OK;
@@ -2947,7 +2966,7 @@ collect_server_cert(struct Curl_easy *data,
private API and doesn't work as expected. So we have to look for
a different symbol to make sure this code is only executed under
Lion or later. */
- if(SecTrustEvaluateAsync) {
+ if(SecTrustCopyPublicKey) {
#pragma unused(server_certs)
err = SSLCopyPeerTrust(backend->ssl_ctx, &trust);
/* For some reason, SSLCopyPeerTrust() can return noErr and yet return
@@ -3162,6 +3181,8 @@ static void sectransp_close(struct Curl_easy *data, struct connectdata *conn,
(void) data;
+ DEBUGASSERT(backend);
+
if(backend->ssl_ctx) {
(void)SSLClose(backend->ssl_ctx);
#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
@@ -3190,6 +3211,8 @@ static int sectransp_shutdown(struct Curl_easy *data,
char buf[120];
int loop = 10; /* avoid getting stuck */
+ DEBUGASSERT(backend);
+
if(!backend->ssl_ctx)
return 0;
@@ -3269,6 +3292,8 @@ static int sectransp_check_cxn(struct connectdata *conn)
OSStatus err;
SSLSessionState state;
+ DEBUGASSERT(backend);
+
if(backend->ssl_ctx) {
err = SSLGetSessionState(backend->ssl_ctx, &state);
if(err == noErr)
@@ -3286,6 +3311,8 @@ static bool sectransp_data_pending(const struct connectdata *conn,
OSStatus err;
size_t buffer;
+ DEBUGASSERT(backend);
+
if(backend->ssl_ctx) { /* SSL is in use */
err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
if(err == noErr)
@@ -3347,6 +3374,8 @@ static ssize_t sectransp_send(struct Curl_easy *data,
size_t processed = 0UL;
OSStatus err;
+ DEBUGASSERT(backend);
+
/* The SSLWrite() function works a little differently than expected. The
fourth argument (processed) is currently documented in Apple's
documentation as: "On return, the length, in bytes, of the data actually
@@ -3414,6 +3443,8 @@ static ssize_t sectransp_recv(struct Curl_easy *data,
size_t processed = 0UL;
OSStatus err;
+ DEBUGASSERT(backend);
+
again:
err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed);
@@ -3463,6 +3494,7 @@ static void *sectransp_get_internals(struct ssl_connect_data *connssl,
{
struct ssl_backend_data *backend = connssl->backend;
(void)info;
+ DEBUGASSERT(backend);
return backend->ssl_ctx;
}
diff --git a/lib/vtls/sectransp.h b/lib/vtls/sectransp.h
index 0febd6613..2d53b7c48 100644
--- a/lib/vtls/sectransp.h
+++ b/lib/vtls/sectransp.h
@@ -8,7 +8,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
- * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -21,6 +21,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index 6007bbba0..9dee5aa3b 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,6 +18,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/* This file is for implementing all "generic" SSL functions that all libcurl
@@ -125,15 +127,6 @@ static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
return !memcmp(first->data, second->data, first->len); /* same data */
}
-static bool safecmp(char *a, char *b)
-{
- if(a && b)
- return !strcmp(a, b);
- else if(!a && !b)
- return TRUE; /* match */
- return FALSE; /* no match */
-}
-
bool
Curl_ssl_config_matches(struct ssl_primary_config *data,
@@ -141,21 +134,26 @@ Curl_ssl_config_matches(struct ssl_primary_config *data,
{
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) &&
- safecmp(data->CApath, needle->CApath) &&
- safecmp(data->CAfile, needle->CAfile) &&
- safecmp(data->issuercert, needle->issuercert) &&
- safecmp(data->clientcert, needle->clientcert) &&
- safecmp(data->random_file, needle->random_file) &&
- safecmp(data->egdsocket, needle->egdsocket) &&
+ Curl_safecmp(data->CApath, needle->CApath) &&
+ Curl_safecmp(data->CAfile, needle->CAfile) &&
+ Curl_safecmp(data->issuercert, needle->issuercert) &&
+ Curl_safecmp(data->clientcert, needle->clientcert) &&
+#ifdef USE_TLS_SRP
+ !Curl_timestrcmp(data->username, needle->username) &&
+ !Curl_timestrcmp(data->password, needle->password) &&
+ (data->authtype == needle->authtype) &&
+#endif
Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) &&
Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) &&
Curl_safe_strcasecompare(data->curves, needle->curves) &&
+ Curl_safe_strcasecompare(data->CRLfile, needle->CRLfile) &&
Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key))
return TRUE;
@@ -172,6 +170,10 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
dest->verifyhost = source->verifyhost;
dest->verifystatus = source->verifystatus;
dest->sessionid = source->sessionid;
+ dest->ssl_options = source->ssl_options;
+#ifdef USE_TLS_SRP
+ dest->authtype = source->authtype;
+#endif
CLONE_BLOB(cert_blob);
CLONE_BLOB(ca_info_blob);
@@ -180,12 +182,15 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
CLONE_STRING(CAfile);
CLONE_STRING(issuercert);
CLONE_STRING(clientcert);
- CLONE_STRING(random_file);
- CLONE_STRING(egdsocket);
CLONE_STRING(cipher_list);
CLONE_STRING(cipher_list13);
CLONE_STRING(pinned_key);
CLONE_STRING(curves);
+ CLONE_STRING(CRLfile);
+#ifdef USE_TLS_SRP
+ CLONE_STRING(username);
+ CLONE_STRING(password);
+#endif
return TRUE;
}
@@ -196,8 +201,6 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
Curl_safefree(sslc->CAfile);
Curl_safefree(sslc->issuercert);
Curl_safefree(sslc->clientcert);
- Curl_safefree(sslc->random_file);
- Curl_safefree(sslc->egdsocket);
Curl_safefree(sslc->cipher_list);
Curl_safefree(sslc->cipher_list13);
Curl_safefree(sslc->pinned_key);
@@ -205,19 +208,24 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
Curl_safefree(sslc->ca_info_blob);
Curl_safefree(sslc->issuercert_blob);
Curl_safefree(sslc->curves);
+ Curl_safefree(sslc->CRLfile);
+#ifdef USE_TLS_SRP
+ Curl_safefree(sslc->username);
+ Curl_safefree(sslc->password);
+#endif
}
#ifdef USE_SSL
static int multissl_setup(const struct Curl_ssl *backend);
#endif
-int Curl_ssl_backend(void)
+curl_sslbackend Curl_ssl_backend(void)
{
#ifdef USE_SSL
multissl_setup(NULL);
return Curl_ssl->info.id;
#else
- return (int)CURLSSLBACKEND_NONE;
+ return CURLSSLBACKEND_NONE;
#endif
}
@@ -300,6 +308,8 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex)
pbdata = conn->proxy_ssl[sockindex].backend;
conn->proxy_ssl[sockindex] = conn->ssl[sockindex];
+ DEBUGASSERT(pbdata != NULL);
+
memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex]));
memset(pbdata, 0, Curl_ssl->sizeof_ssl_backend_data);
@@ -628,7 +638,8 @@ void Curl_ssl_associate_conn(struct Curl_easy *data,
{
if(Curl_ssl->associate_connection) {
Curl_ssl->associate_connection(data, conn, FIRSTSOCKET);
- if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
+ if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) &&
+ conn->bits.sock_accepted)
Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET);
}
}
@@ -638,7 +649,8 @@ void Curl_ssl_detach_conn(struct Curl_easy *data,
{
if(Curl_ssl->disassociate_connection) {
Curl_ssl->disassociate_connection(data, FIRSTSOCKET);
- if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted)
+ if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) &&
+ conn->bits.sock_accepted)
Curl_ssl->disassociate_connection(data, SECONDARYSOCKET);
}
}
@@ -872,6 +884,32 @@ 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
*/
@@ -969,7 +1007,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
if(encode != CURLE_OK)
return encode;
- encode = Curl_base64_encode(data, (char *)sha256sumdigest,
+ encode = Curl_base64_encode((char *)sha256sumdigest,
CURL_SHA256_DIGEST_LENGTH, &encoded,
&encodedlen);
Curl_safefree(sha256sumdigest);
@@ -1296,8 +1334,6 @@ const struct Curl_ssl *Curl_ssl =
&Curl_ssl_openssl;
#elif defined(USE_SCHANNEL)
&Curl_ssl_schannel;
-#elif defined(USE_MESALINK)
- &Curl_ssl_mesalink;
#elif defined(USE_BEARSSL)
&Curl_ssl_bearssl;
#else
@@ -1329,9 +1365,6 @@ static const struct Curl_ssl *available_backends[] = {
#if defined(USE_SCHANNEL)
&Curl_ssl_schannel,
#endif
-#if defined(USE_MESALINK)
- &Curl_ssl_mesalink,
-#endif
#if defined(USE_BEARSSL)
&Curl_ssl_bearssl,
#endif
@@ -1423,8 +1456,10 @@ static int multissl_setup(const struct Curl_ssl *backend)
return 0;
}
-CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
- const curl_ssl_backend ***avail)
+/* This function is used to select the SSL backend to use. It is called by
+ curl_global_sslset (easy.c) which uses the global init lock. */
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail)
{
int i;
@@ -1453,8 +1488,8 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
}
#else /* USE_SSL */
-CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
- const curl_ssl_backend ***avail)
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail)
{
(void)id;
(void)name;
diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h
index c7bbba082..50c53b3fb 100644
--- a/lib/vtls/vtls.h
+++ b/lib/vtls/vtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
@@ -34,6 +36,17 @@ struct ssl_connect_data;
#define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */
#define SSLSUPP_CAINFO_BLOB (1<<6)
+#define ALPN_ACCEPTED "ALPN: server accepted "
+
+#define VTLS_INFOF_NO_ALPN \
+ "ALPN: server did not agree on a protocol. Uses default."
+#define VTLS_INFOF_ALPN_OFFER_1STR \
+ "ALPN: offers %s"
+#define VTLS_INFOF_ALPN_ACCEPTED_1STR \
+ ALPN_ACCEPTED "%s"
+#define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \
+ ALPN_ACCEPTED "%.*s"
+
struct Curl_ssl {
/*
* This *must* be the first entry to allow returning the list of available
@@ -85,7 +98,7 @@ struct Curl_ssl {
CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
unsigned char *sha256sum, size_t sha256sumlen);
- void (*associate_connection)(struct Curl_easy *data,
+ bool (*associate_connection)(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
void (*disassociate_connection)(struct Curl_easy *data, int sockindex);
@@ -112,6 +125,9 @@ struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
bool Curl_none_false_start(void);
bool Curl_ssl_tls13_ciphersuites(void);
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail);
+
#include "openssl.h" /* OpenSSL versions */
#include "gtls.h" /* GnuTLS versions */
#include "nssg.h" /* NSS versions */
@@ -120,7 +136,6 @@ bool Curl_ssl_tls13_ciphersuites(void);
#include "schannel.h" /* Schannel SSPI version */
#include "sectransp.h" /* SecureTransport (Darwin) version */
#include "mbedtls.h" /* mbedTLS versions */
-#include "mesalink.h" /* MesaLink versions */
#include "bearssl.h" /* BearSSL versions */
#include "rustls.h" /* rustls versions */
@@ -173,6 +188,7 @@ bool Curl_ssl_tls13_ciphersuites(void);
data->set.str[STRING_SSL_PINNEDPUBLICKEY]
#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,
@@ -184,7 +200,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc);
ssl_connect_2_writing. */
int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks);
-int Curl_ssl_backend(void);
+curl_sslbackend Curl_ssl_backend(void);
#ifdef USE_SSL
int Curl_ssl_init(void);
diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
index 8c5b9157b..594c39a32 100644
--- a/lib/vtls/wolfssl.c
+++ b/lib/vtls/wolfssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -18,6 +18,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
/*
@@ -263,6 +265,8 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
#define use_sni(x) Curl_nop_stmt
#endif
+ DEBUGASSERT(backend);
+
if(connssl->state == ssl_connection_complete)
return CURLE_OK;
@@ -322,7 +326,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
}
if(!req_method) {
- failf(data, "SSL: couldn't create a method!");
+ failf(data, "SSL: couldn't create a method");
return CURLE_OUT_OF_MEMORY;
}
@@ -331,7 +335,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
backend->ctx = SSL_CTX_new(req_method);
if(!backend->ctx) {
- failf(data, "SSL: couldn't create a context!");
+ failf(data, "SSL: couldn't create a context");
return CURLE_OUT_OF_MEMORY;
}
@@ -460,14 +464,19 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
const char * const hostname = SSL_HOST_NAME();
size_t hostname_len = strlen(hostname);
if((hostname_len < USHRT_MAX) &&
- (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) &&
+ !Curl_inet_pton(AF_INET, hostname, &addr4)
#ifdef ENABLE_IPV6
- (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) &&
+ && !Curl_inet_pton(AF_INET6, hostname, &addr6)
#endif
- (wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, hostname,
- (unsigned short)hostname_len) != 1)) {
- infof(data, "WARNING: failed to configure server name indication (SNI) "
- "TLS extension");
+ ) {
+ size_t snilen;
+ char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
+ if(!snihost ||
+ wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost,
+ (unsigned short)snilen) != 1) {
+ failf(data, "Failed to set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
}
}
#endif
@@ -496,7 +505,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
SSL_free(backend->handle);
backend->handle = SSL_new(backend->ctx);
if(!backend->handle) {
- failf(data, "SSL: couldn't create a context (handle)!");
+ failf(data, "SSL: couldn't create a handle");
return CURLE_OUT_OF_MEMORY;
}
@@ -519,12 +528,12 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
#ifdef USE_HTTP2
if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
strcpy(protocols + strlen(protocols), ALPN_H2 ",");
- infof(data, "ALPN, offering %s", ALPN_H2);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
}
#endif
strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1);
- infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
if(wolfSSL_UseALPN(backend->handle, protocols,
(unsigned)strlen(protocols),
@@ -590,10 +599,11 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn,
int ret = -1;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_backend_data *backend = connssl->backend;
- const char * const hostname = SSL_HOST_NAME();
const char * const dispname = SSL_HOST_DISPNAME();
const char * const pinnedpubkey = SSL_PINNED_PUB_KEY();
+ DEBUGASSERT(backend);
+
ERR_clear_error();
conn->recv[sockindex] = wolfssl_recv;
@@ -601,9 +611,10 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn,
/* Enable RFC2818 checks */
if(SSL_CONN_CONFIG(verifyhost)) {
- ret = wolfSSL_check_domain_name(backend->handle, hostname);
- if(ret == SSL_FAILURE)
- return CURLE_OUT_OF_MEMORY;
+ char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL);
+ if(!snihost ||
+ (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE))
+ return CURLE_SSL_CONNECT_ERROR;
}
ret = SSL_connect(backend->handle);
@@ -730,7 +741,7 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn,
(const unsigned char *)pubkey->header,
(size_t)(pubkey->end - pubkey->header));
if(result) {
- failf(data, "SSL: public key does not match pinned public key!");
+ failf(data, "SSL: public key does not match pinned public key");
return result;
}
#else
@@ -748,26 +759,25 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn,
rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
if(rc == SSL_SUCCESS) {
- infof(data, "ALPN, server accepted to use %.*s", protocol_len,
- protocol);
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol);
if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
!memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH))
- conn->negnpn = CURL_HTTP_VERSION_1_1;
+ conn->alpn = CURL_HTTP_VERSION_1_1;
#ifdef USE_HTTP2
else if(data->state.httpwant >= CURL_HTTP_VERSION_2 &&
protocol_len == ALPN_H2_LENGTH &&
!memcmp(protocol, ALPN_H2, ALPN_H2_LENGTH))
- conn->negnpn = CURL_HTTP_VERSION_2;
+ conn->alpn = CURL_HTTP_VERSION_2;
#endif
else
infof(data, "ALPN, unrecognized protocol %.*s", protocol_len,
protocol);
- Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
else if(rc == SSL_ALPN_NOT_FOUND)
- infof(data, "ALPN, server did not agree to a protocol");
+ infof(data, VTLS_INFOF_NO_ALPN);
else {
failf(data, "ALPN, failure getting protocol, error %d", rc);
return CURLE_SSL_CONNECT_ERROR;
@@ -797,11 +807,14 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *backend = connssl->backend;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+ DEBUGASSERT(backend);
if(SSL_SET_OPTION(primary.sessionid)) {
bool incache;
+ bool added = FALSE;
void *old_ssl_sessionid = NULL;
- SSL_SESSION *our_ssl_sessionid = SSL_get_session(backend->handle);
+ /* SSL_get1_session allocates memory that has to be freed. */
+ SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle);
bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE;
if(our_ssl_sessionid) {
@@ -821,11 +834,20 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn,
0, sockindex, NULL);
if(result) {
Curl_ssl_sessionid_unlock(data);
+ SSL_SESSION_free(our_ssl_sessionid);
failf(data, "failed to store ssl session");
return result;
}
+ else {
+ added = TRUE;
+ }
}
Curl_ssl_sessionid_unlock(data);
+
+ if(!added) {
+ /* If the session info wasn't added to the cache, free our copy. */
+ SSL_SESSION_free(our_ssl_sessionid);
+ }
}
}
@@ -848,6 +870,8 @@ static ssize_t wolfssl_send(struct Curl_easy *data,
int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
int rc;
+ DEBUGASSERT(backend);
+
ERR_clear_error();
rc = SSL_write(backend->handle, mem, memlen);
@@ -880,6 +904,8 @@ static void wolfssl_close(struct Curl_easy *data, struct connectdata *conn,
(void) data;
+ DEBUGASSERT(backend);
+
if(backend->handle) {
char buf[32];
/* Maybe the server has already sent a close notify alert.
@@ -908,17 +934,22 @@ static ssize_t wolfssl_recv(struct Curl_easy *data,
int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
int nread;
+ DEBUGASSERT(backend);
+
ERR_clear_error();
nread = SSL_read(backend->handle, buf, buffsize);
- if(nread < 0) {
+ if(nread <= 0) {
int err = SSL_get_error(backend->handle, nread);
switch(err) {
case SSL_ERROR_ZERO_RETURN: /* no more data */
break;
+ case SSL_ERROR_NONE:
+ /* FALLTHROUGH */
case SSL_ERROR_WANT_READ:
+ /* FALLTHROUGH */
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke SSL_read() */
*curlcode = CURLE_AGAIN;
@@ -936,8 +967,7 @@ static ssize_t wolfssl_recv(struct Curl_easy *data,
static void wolfssl_session_free(void *ptr)
{
- (void)ptr;
- /* wolfSSL reuses sessions on own, no free */
+ SSL_SESSION_free(ptr);
}
@@ -974,6 +1004,7 @@ static bool wolfssl_data_pending(const struct connectdata *conn,
{
const struct ssl_connect_data *connssl = &conn->ssl[connindex];
struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(backend);
if(backend->handle) /* SSL is in use */
return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE;
else
@@ -994,6 +1025,8 @@ static int wolfssl_shutdown(struct Curl_easy *data, struct connectdata *conn,
(void) data;
+ DEBUGASSERT(backend);
+
if(backend->handle) {
ERR_clear_error();
SSL_free(backend->handle);
@@ -1173,6 +1206,7 @@ static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
{
struct ssl_backend_data *backend = connssl->backend;
(void)info;
+ DEBUGASSERT(backend);
return backend->handle;
}
diff --git a/lib/vtls/wolfssl.h b/lib/vtls/wolfssl.h
index d411e6913..b2e7c3fde 100644
--- a/lib/vtls/wolfssl.h
+++ b/lib/vtls/wolfssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,6 +20,8 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
+ * SPDX-License-Identifier: curl
+ *
***************************************************************************/
#include "curl_setup.h"
diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c
new file mode 100644
index 000000000..0cfcbe87d
--- /dev/null
+++ b/lib/vtls/x509asn1.c
@@ -0,0 +1,1426 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
+ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+
+#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+#define WANT_PARSEX509 /* uses Curl_parseX509() */
+#endif
+
+#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
+ defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */
+#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
+#endif
+
+#if defined(USE_GSKIT)
+#define WANT_VERIFYHOST /* uses Curl_verifyhost () */
+#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
+#endif
+
+#include <curl/curl.h>
+#include "urldata.h"
+#include "strcase.h"
+#include "curl_ctype.h"
+#include "hostcheck.h"
+#include "vtls/vtls.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "curl_base64.h"
+#include "x509asn1.h"
+#include "dynbuf.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Constants.
+ */
+
+/* Largest supported ASN.1 structure. */
+#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */
+
+/* ASN.1 classes. */
+#define CURL_ASN1_UNIVERSAL 0
+#define CURL_ASN1_APPLICATION 1
+#define CURL_ASN1_CONTEXT_SPECIFIC 2
+#define CURL_ASN1_PRIVATE 3
+
+/* ASN.1 types. */
+#define CURL_ASN1_BOOLEAN 1
+#define CURL_ASN1_INTEGER 2
+#define CURL_ASN1_BIT_STRING 3
+#define CURL_ASN1_OCTET_STRING 4
+#define CURL_ASN1_NULL 5
+#define CURL_ASN1_OBJECT_IDENTIFIER 6
+#define CURL_ASN1_OBJECT_DESCRIPTOR 7
+#define CURL_ASN1_INSTANCE_OF 8
+#define CURL_ASN1_REAL 9
+#define CURL_ASN1_ENUMERATED 10
+#define CURL_ASN1_EMBEDDED 11
+#define CURL_ASN1_UTF8_STRING 12
+#define CURL_ASN1_RELATIVE_OID 13
+#define CURL_ASN1_SEQUENCE 16
+#define CURL_ASN1_SET 17
+#define CURL_ASN1_NUMERIC_STRING 18
+#define CURL_ASN1_PRINTABLE_STRING 19
+#define CURL_ASN1_TELETEX_STRING 20
+#define CURL_ASN1_VIDEOTEX_STRING 21
+#define CURL_ASN1_IA5_STRING 22
+#define CURL_ASN1_UTC_TIME 23
+#define CURL_ASN1_GENERALIZED_TIME 24
+#define CURL_ASN1_GRAPHIC_STRING 25
+#define CURL_ASN1_VISIBLE_STRING 26
+#define CURL_ASN1_GENERAL_STRING 27
+#define CURL_ASN1_UNIVERSAL_STRING 28
+#define CURL_ASN1_CHARACTER_STRING 29
+#define CURL_ASN1_BMP_STRING 30
+
+#ifdef WANT_EXTRACT_CERTINFO
+/* ASN.1 OID table entry. */
+struct Curl_OID {
+ const char *numoid; /* Dotted-numeric OID. */
+ const char *textoid; /* OID name. */
+};
+
+/* ASN.1 OIDs. */
+static const char cnOID[] = "2.5.4.3"; /* Common name. */
+static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */
+
+static const struct Curl_OID OIDtable[] = {
+ { "1.2.840.10040.4.1", "dsa" },
+ { "1.2.840.10040.4.3", "dsa-with-sha1" },
+ { "1.2.840.10045.2.1", "ecPublicKey" },
+ { "1.2.840.10045.3.0.1", "c2pnb163v1" },
+ { "1.2.840.10045.4.1", "ecdsa-with-SHA1" },
+ { "1.2.840.10046.2.1", "dhpublicnumber" },
+ { "1.2.840.113549.1.1.1", "rsaEncryption" },
+ { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" },
+ { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" },
+ { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" },
+ { "1.2.840.113549.1.1.10", "RSASSA-PSS" },
+ { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" },
+ { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" },
+ { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" },
+ { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" },
+ { "1.2.840.113549.2.2", "md2" },
+ { "1.2.840.113549.2.5", "md5" },
+ { "1.3.14.3.2.26", "sha1" },
+ { cnOID, "CN" },
+ { "2.5.4.4", "SN" },
+ { "2.5.4.5", "serialNumber" },
+ { "2.5.4.6", "C" },
+ { "2.5.4.7", "L" },
+ { "2.5.4.8", "ST" },
+ { "2.5.4.9", "streetAddress" },
+ { "2.5.4.10", "O" },
+ { "2.5.4.11", "OU" },
+ { "2.5.4.12", "title" },
+ { "2.5.4.13", "description" },
+ { "2.5.4.17", "postalCode" },
+ { "2.5.4.41", "name" },
+ { "2.5.4.42", "givenName" },
+ { "2.5.4.43", "initials" },
+ { "2.5.4.44", "generationQualifier" },
+ { "2.5.4.45", "X500UniqueIdentifier" },
+ { "2.5.4.46", "dnQualifier" },
+ { "2.5.4.65", "pseudonym" },
+ { "1.2.840.113549.1.9.1", "emailAddress" },
+ { "2.5.4.72", "role" },
+ { sanOID, "subjectAltName" },
+ { "2.5.29.18", "issuerAltName" },
+ { "2.5.29.19", "basicConstraints" },
+ { "2.16.840.1.101.3.4.2.4", "sha224" },
+ { "2.16.840.1.101.3.4.2.1", "sha256" },
+ { "2.16.840.1.101.3.4.2.2", "sha384" },
+ { "2.16.840.1.101.3.4.2.3", "sha512" },
+ { (const char *) NULL, (const char *) NULL }
+};
+
+#endif /* WANT_EXTRACT_CERTINFO */
+
+/*
+ * Lightweight ASN.1 parser.
+ * In particular, it does not check for syntactic/lexical errors.
+ * It is intended to support certificate information gathering for SSL backends
+ * that offer a mean to get certificates as a whole, but do not supply
+ * entry points to get particular certificate sub-fields.
+ * Please note there is no pretention here to rewrite a full SSL library.
+ */
+
+static const char *getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+ WARN_UNUSED_RESULT;
+
+static const char *getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ unsigned char b;
+ unsigned long len;
+ struct Curl_asn1Element lelem;
+
+ /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg'
+ ending at `end'.
+ Returns a pointer in source string after the parsed element, or NULL
+ if an error occurs. */
+ if(!beg || !end || beg >= end || !*beg ||
+ (size_t)(end - beg) > CURL_ASN1_MAX)
+ return NULL;
+
+ /* Process header byte. */
+ elem->header = beg;
+ b = (unsigned char) *beg++;
+ elem->constructed = (b & 0x20) != 0;
+ elem->class = (b >> 6) & 3;
+ b &= 0x1F;
+ if(b == 0x1F)
+ return NULL; /* Long tag values not supported here. */
+ elem->tag = b;
+
+ /* Process length. */
+ if(beg >= end)
+ return NULL;
+ b = (unsigned char) *beg++;
+ if(!(b & 0x80))
+ len = b;
+ else if(!(b &= 0x7F)) {
+ /* Unspecified length. Since we have all the data, we can determine the
+ effective length by skipping element until an end element is found. */
+ if(!elem->constructed)
+ return NULL;
+ elem->beg = beg;
+ while(beg < end && *beg) {
+ beg = getASN1Element(&lelem, beg, end);
+ if(!beg)
+ return NULL;
+ }
+ if(beg >= end)
+ return NULL;
+ elem->end = beg;
+ return beg + 1;
+ }
+ else if((unsigned)b > (size_t)(end - beg))
+ return NULL; /* Does not fit in source. */
+ else {
+ /* Get long length. */
+ len = 0;
+ do {
+ if(len & 0xFF000000L)
+ return NULL; /* Lengths > 32 bits are not supported. */
+ len = (len << 8) | (unsigned char) *beg++;
+ } while(--b);
+ }
+ if(len > (size_t)(end - beg))
+ return NULL; /* Element data does not fit in source. */
+ elem->beg = beg;
+ elem->end = beg + len;
+ return elem->end;
+}
+
+#ifdef WANT_EXTRACT_CERTINFO
+
+/*
+ * Search the null terminated OID or OID identifier in local table.
+ * Return the table entry pointer or NULL if not found.
+ */
+static const struct Curl_OID *searchOID(const char *oid)
+{
+ const struct Curl_OID *op;
+ for(op = OIDtable; op->numoid; op++)
+ if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid))
+ return op;
+
+ return NULL;
+}
+
+/*
+ * 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.
+ */
+
+static const char *bool2str(const char *beg, const char *end)
+{
+ if(end - beg != 1)
+ return NULL;
+ return strdup(*beg? "TRUE": "FALSE");
+}
+
+/*
+ * Convert an ASN.1 octet string to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *octet2str(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);
+
+ while(!result && beg < end)
+ result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++);
+
+ return Curl_dyn_ptr(&buf);
+}
+
+static const char *bit2str(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. */
+
+ if(++beg > end)
+ return NULL;
+ return octet2str(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.
+ */
+static const char *int2str(const char *beg, const char *end)
+{
+ unsigned long val = 0;
+ size_t n = end - beg;
+
+ if(!n)
+ return NULL;
+
+ if(n > 4)
+ return octet2str(beg, end);
+
+ /* Represent integers <= 32-bit as a single value. */
+ if(*beg & 0x80)
+ val = ~val;
+
+ do
+ val = (val << 8) | *(const unsigned char *) beg++;
+ while(beg < end);
+ return curl_maprintf("%s%lx", 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.
+ */
+static ssize_t
+utf8asn1str(char **to, int type, const char *from, const char *end)
+{
+ size_t inlength = end - from;
+ int size = 1;
+ size_t outlength;
+ char *buf;
+
+ *to = NULL;
+ switch(type) {
+ case CURL_ASN1_BMP_STRING:
+ size = 2;
+ break;
+ case CURL_ASN1_UNIVERSAL_STRING:
+ size = 4;
+ break;
+ case CURL_ASN1_NUMERIC_STRING:
+ case CURL_ASN1_PRINTABLE_STRING:
+ case CURL_ASN1_TELETEX_STRING:
+ case CURL_ASN1_IA5_STRING:
+ case CURL_ASN1_VISIBLE_STRING:
+ case CURL_ASN1_UTF8_STRING:
+ break;
+ default:
+ return -1; /* 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. */
+
+ if(type == CURL_ASN1_UTF8_STRING) {
+ /* Just copy. */
+ outlength = inlength;
+ if(outlength)
+ memcpy(buf, from, outlength);
+ }
+ else {
+ for(outlength = 0; from < end;) {
+ int charsize;
+ unsigned int wc;
+
+ wc = 0;
+ switch(size) {
+ case 4:
+ wc = (wc << 8) | *(const unsigned char *) from++;
+ wc = (wc << 8) | *(const unsigned char *) from++;
+ /* FALLTHROUGH */
+ case 2:
+ wc = (wc << 8) | *(const unsigned char *) from++;
+ /* 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. */
+ }
+ buf[outlength + 3] = (char) (0x80 | (wc & 0x3F));
+ wc = (wc >> 6) | 0x00010000;
+ charsize++;
+ }
+ buf[outlength + 2] = (char) (0x80 | (wc & 0x3F));
+ wc = (wc >> 6) | 0x00000800;
+ charsize++;
+ }
+ buf[outlength + 1] = (char) (0x80 | (wc & 0x3F));
+ wc = (wc >> 6) | 0x000000C0;
+ charsize++;
+ }
+ buf[outlength] = (char) wc;
+ outlength += 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;
+}
+
+/*
+ * 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.
+ */
+static size_t encodeOID(char *buf, size_t buflen,
+ const char *beg, const char *end)
+{
+ size_t i;
+ unsigned int x;
+ unsigned int y;
+
+ /* 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);
+
+ /* Process the trailing numbers. */
+ while(beg < end) {
+ if(i < buflen)
+ buf[i] = '.';
+ i++;
+ x = 0;
+ do {
+ if(x & 0xFF000000)
+ return 0;
+ 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);
+ }
+ if(i < buflen)
+ buf[i] = '\0';
+ return i;
+}
+
+/*
+ * Convert an ASN.1 OID into its dotted or symbolic string representation.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+
+static const char *OID2str(const char *beg, const char *end, bool symbolic)
+{
+ char *buf = NULL;
+ 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);
+ }
+ }
+ }
+ }
+ }
+ return buf;
+}
+
+static const char *GTime2str(const char *beg, const char *end)
+{
+ const char *tzp;
+ const char *fracp;
+ char sec1, sec2;
+ size_t fracl;
+ size_t tzl;
+ const char *sep = "";
+
+ /* Convert an ASN.1 Generalized time to a printable string.
+ Return the dynamically allocated string, or NULL if an error occurs. */
+
+ for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++)
+ ;
+
+ /* Get seconds digits. */
+ sec1 = '0';
+ switch(fracp - beg - 12) {
+ case 0:
+ sec2 = '0';
+ break;
+ case 2:
+ sec1 = fracp[-2];
+ /* FALLTHROUGH */
+ case 1:
+ sec2 = fracp[-1];
+ break;
+ default:
+ return NULL;
+ }
+
+ /* Scan for timezone, measure fractional seconds. */
+ tzp = fracp;
+ fracl = 0;
+ if(fracp < end && (*fracp == '.' || *fracp == ',')) {
+ fracp++;
+ do
+ tzp++;
+ while(tzp < end && *tzp >= '0' && *tzp <= '9');
+ /* Strip leading zeroes in fractional seconds. */
+ for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--)
+ ;
+ }
+
+ /* Process timezone. */
+ if(tzp >= end)
+ ; /* Nothing to do. */
+ else if(*tzp == 'Z') {
+ tzp = " GMT";
+ end = tzp + 4;
+ }
+ else {
+ sep = " ";
+ tzp++;
+ }
+
+ tzl = end - tzp;
+ return curl_maprintf("%.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,
+ sep, (int)tzl, tzp);
+}
+
+/*
+ * Convert an ASN.1 UTC time to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *UTime2str(const char *beg, const char *end)
+{
+ const char *tzp;
+ size_t tzl;
+ const char *sec;
+
+ for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++)
+ ;
+ /* Get the seconds. */
+ sec = beg + 10;
+ switch(tzp - sec) {
+ case 0:
+ sec = "00";
+ case 2:
+ break;
+ default:
+ return NULL;
+ }
+
+ /* Process timezone. */
+ if(tzp >= end)
+ return NULL;
+ if(*tzp == 'Z') {
+ tzp = "GMT";
+ end = tzp + 3;
+ }
+ else
+ tzp++;
+
+ tzl = end - tzp;
+ return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+ 20 - (*beg >= '5'), beg, beg + 2, beg + 4,
+ beg + 6, beg + 8, sec,
+ (int)tzl, tzp);
+}
+
+/*
+ * Convert an ASN.1 element to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
+{
+ if(elem->constructed)
+ return NULL; /* 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);
+ case CURL_ASN1_INTEGER:
+ case CURL_ASN1_ENUMERATED:
+ return int2str(elem->beg, elem->end);
+ case CURL_ASN1_BIT_STRING:
+ return bit2str(elem->beg, elem->end);
+ case CURL_ASN1_OCTET_STRING:
+ return octet2str(elem->beg, elem->end);
+ case CURL_ASN1_NULL:
+ return strdup("");
+ case CURL_ASN1_OBJECT_IDENTIFIER:
+ return OID2str(elem->beg, elem->end, TRUE);
+ case CURL_ASN1_UTC_TIME:
+ return UTime2str(elem->beg, elem->end);
+ case CURL_ASN1_GENERALIZED_TIME:
+ return GTime2str(elem->beg, elem->end);
+ case CURL_ASN1_UTF8_STRING:
+ case CURL_ASN1_NUMERIC_STRING:
+ case CURL_ASN1_PRINTABLE_STRING:
+ case CURL_ASN1_TELETEX_STRING:
+ case CURL_ASN1_IA5_STRING:
+ case CURL_ASN1_VISIBLE_STRING:
+ case CURL_ASN1_UNIVERSAL_STRING:
+ case CURL_ASN1_BMP_STRING:
+ return string2str(type, elem->beg, elem->end);
+ }
+
+ return NULL; /* Unsupported. */
+}
+
+/*
+ * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at
+ * `buf'.
+ *
+ * Returns the total string length, even if larger than `buflen' or -1 on
+ * error.
+ */
+static ssize_t encodeDN(char *buf, size_t buflen, 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;
+
+ for(p1 = dn->beg; p1 < dn->end;) {
+ p1 = getASN1Element(&rdn, p1, dn->end);
+ if(!p1)
+ return -1;
+ for(p2 = rdn.beg; p2 < rdn.end;) {
+ p2 = getASN1Element(&atv, p2, rdn.end);
+ if(!p2)
+ return -1;
+ 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;
+
+ /* 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++;
+ }
+ }
+
+ /* Encode attribute name. */
+ for(p3 = str; *p3; p3++) {
+ if(l < buflen)
+ buf[l] = *p3;
+ l++;
+ }
+ free((char *) str);
+
+ /* Generate equal sign. */
+ if(l < buflen)
+ buf[l] = '=';
+ l++;
+
+ /* 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);
+ }
+ }
+
+ return l;
+}
+
+#endif /* WANT_EXTRACT_CERTINFO */
+
+#ifdef WANT_PARSEX509
+/*
+ * ASN.1 parse an X509 certificate into structure subfields.
+ * Syntax is assumed to have already been checked by the SSL backend.
+ * See RFC 5280.
+ */
+int Curl_parseX509(struct Curl_X509certificate *cert,
+ const char *beg, const char *end)
+{
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element tbsCertificate;
+ const char *ccp;
+ static const char defaultVersion = 0; /* v1. */
+
+ cert->certificate.header = NULL;
+ cert->certificate.beg = beg;
+ cert->certificate.end = end;
+
+ /* Get the sequence content. */
+ if(!getASN1Element(&elem, beg, end))
+ return -1; /* Invalid bounds/size. */
+ beg = elem.beg;
+ end = elem.end;
+
+ /* Get tbsCertificate. */
+ beg = getASN1Element(&tbsCertificate, beg, end);
+ if(!beg)
+ return -1;
+ /* Skip the signatureAlgorithm. */
+ beg = getASN1Element(&cert->signatureAlgorithm, beg, end);
+ if(!beg)
+ return -1;
+ /* Get the signatureValue. */
+ if(!getASN1Element(&cert->signature, beg, end))
+ return -1;
+
+ /* Parse TBSCertificate. */
+ beg = tbsCertificate.beg;
+ end = tbsCertificate.end;
+ /* Get optional version, get serialNumber. */
+ cert->version.header = NULL;
+ cert->version.beg = &defaultVersion;
+ cert->version.end = &defaultVersion + sizeof(defaultVersion);
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ if(elem.tag == 0) {
+ if(!getASN1Element(&cert->version, elem.beg, elem.end))
+ return -1;
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ cert->serialNumber = elem;
+ /* Get signature algorithm. */
+ beg = getASN1Element(&cert->signatureAlgorithm, beg, end);
+ /* Get issuer. */
+ beg = getASN1Element(&cert->issuer, beg, end);
+ if(!beg)
+ return -1;
+ /* Get notBefore and notAfter. */
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end);
+ if(!ccp)
+ return -1;
+ if(!getASN1Element(&cert->notAfter, ccp, elem.end))
+ return -1;
+ /* Get subject. */
+ beg = getASN1Element(&cert->subject, beg, end);
+ if(!beg)
+ return -1;
+ /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */
+ beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end);
+ if(!beg)
+ return -1;
+ ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm,
+ cert->subjectPublicKeyInfo.beg,
+ cert->subjectPublicKeyInfo.end);
+ if(!ccp)
+ return -1;
+ if(!getASN1Element(&cert->subjectPublicKey, ccp,
+ cert->subjectPublicKeyInfo.end))
+ return -1;
+ /* Get optional issuerUiqueID, subjectUniqueID and extensions. */
+ cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0;
+ cert->extensions.tag = elem.tag = 0;
+ cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL;
+ cert->issuerUniqueID.beg = cert->issuerUniqueID.end = "";
+ cert->subjectUniqueID.beg = cert->subjectUniqueID.end = "";
+ cert->extensions.header = NULL;
+ cert->extensions.beg = cert->extensions.end = "";
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ if(elem.tag == 1) {
+ cert->issuerUniqueID = elem;
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ }
+ if(elem.tag == 2) {
+ cert->subjectUniqueID = elem;
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+ return -1;
+ }
+ }
+ if(elem.tag == 3)
+ if(!getASN1Element(&cert->extensions, elem.beg, elem.end))
+ return -1;
+ return 0;
+}
+
+#endif /* WANT_PARSEX509 */
+
+#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)
+{
+ struct Curl_asn1Element oid;
+
+ /* Get algorithm parameters and return algorithm name. */
+
+ beg = getASN1Element(&oid, beg, end);
+ if(!beg)
+ return NULL;
+ 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);
+}
+
+/* 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)
+{
+ const char *output;
+ CURLcode result = CURLE_OK;
+
+ /* Generate a certificate information record for the public key. */
+
+ output = ASN1tostr(elem, 0);
+ if(output) {
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, label, output);
+ if(!certnum && !result)
+ infof(data, " %s: %s", label, output);
+ free((char *) output);
+ }
+ return result ? 1 : 0;
+}
+
+/* return 0 on success, 1 on error */
+static int do_pubkey(struct Curl_easy *data, int certnum,
+ const char *algo, struct Curl_asn1Element *param,
+ struct Curl_asn1Element *pubkey)
+{
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element pk;
+ const char *p;
+
+ /* Generate all information records for the public key. */
+
+ if(strcasecompare(algo, "ecPublicKey")) {
+ /*
+ * ECC public key is all the data, a value of type BIT STRING mapped to
+ * OCTET STRING and should not be parsed as an ASN.1 value.
+ */
+ const unsigned long len =
+ (unsigned long)((pubkey->end - pubkey->beg - 2) * 4);
+ if(!certnum)
+ infof(data, " ECC Public Key (%lu bits)", len);
+ if(data->set.ssl.certinfo) {
+ char q[sizeof(len) * 8 / 3 + 1];
+ (void)msnprintf(q, sizeof(q), "%lu", len);
+ if(Curl_ssl_push_certinfo(data, certnum, "ECC Public Key", q))
+ return 1;
+ }
+ return do_pubkey_field(data, certnum, "ecPublicKey", pubkey);
+ }
+
+ /* Get the public key (single element). */
+ if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end))
+ return 1;
+
+ if(strcasecompare(algo, "rsaEncryption")) {
+ const char *q;
+ unsigned long len;
+
+ p = getASN1Element(&elem, pk.beg, pk.end);
+ if(!p)
+ return 1;
+
+ /* Compute key length. */
+ for(q = elem.beg; !*q && q < elem.end; q++)
+ ;
+ len = (unsigned long)((elem.end - q) * 8);
+ if(len) {
+ unsigned int i;
+ for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1)
+ len--;
+ }
+ if(len > 32)
+ elem.beg = q; /* Strip leading zero bytes. */
+ if(!certnum)
+ infof(data, " RSA Public Key (%lu bits)", len);
+ if(data->set.ssl.certinfo) {
+ char r[sizeof(len) * 8 / 3 + 1];
+ msnprintf(r, sizeof(r), "%lu", len);
+ if(Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", r))
+ return 1;
+ }
+ /* Generate coefficients. */
+ if(do_pubkey_field(data, certnum, "rsa(n)", &elem))
+ return 1;
+ if(!getASN1Element(&elem, p, pk.end))
+ return 1;
+ if(do_pubkey_field(data, certnum, "rsa(e)", &elem))
+ return 1;
+ }
+ else if(strcasecompare(algo, "dsa")) {
+ p = getASN1Element(&elem, param->beg, param->end);
+ if(p) {
+ if(do_pubkey_field(data, certnum, "dsa(p)", &elem))
+ return 1;
+ p = getASN1Element(&elem, p, param->end);
+ if(p) {
+ if(do_pubkey_field(data, certnum, "dsa(q)", &elem))
+ return 1;
+ if(getASN1Element(&elem, p, param->end)) {
+ if(do_pubkey_field(data, certnum, "dsa(g)", &elem))
+ return 1;
+ if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk))
+ return 1;
+ }
+ }
+ }
+ }
+ else if(strcasecompare(algo, "dhpublicnumber")) {
+ p = getASN1Element(&elem, param->beg, param->end);
+ if(p) {
+ if(do_pubkey_field(data, certnum, "dh(p)", &elem))
+ return 1;
+ if(getASN1Element(&elem, param->beg, param->end)) {
+ if(do_pubkey_field(data, certnum, "dh(g)", &elem))
+ return 1;
+ if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk))
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Convert an ASN.1 distinguished name into a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+static const char *DNtostr(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;
+}
+
+CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ int certnum,
+ const char *beg,
+ const char *end)
+{
+ struct Curl_X509certificate cert;
+ struct Curl_asn1Element param;
+ const char *ccp;
+ char *cp1;
+ size_t cl1;
+ char *cp2;
+ CURLcode result = CURLE_OK;
+ unsigned long version;
+ size_t i;
+ size_t j;
+
+ if(!data->set.ssl.certinfo)
+ if(certnum)
+ return CURLE_OK;
+
+ /* Prepare the certificate information for curl_easy_getinfo(). */
+
+ /* Extract the certificate ASN.1 elements. */
+ if(Curl_parseX509(&cert, beg, end))
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* Subject. */
+ ccp = DNtostr(&cert.subject);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo) {
+ result = Curl_ssl_push_certinfo(data, certnum, "Subject", ccp);
+ if(result)
+ return result;
+ }
+ if(!certnum)
+ infof(data, "%2d Subject: %s", certnum, ccp);
+ free((char *) ccp);
+
+ /* Issuer. */
+ ccp = DNtostr(&cert.issuer);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo) {
+ result = Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp);
+ }
+ if(!certnum)
+ infof(data, " Issuer: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* 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;
+ if(data->set.ssl.certinfo) {
+ ccp = curl_maprintf("%lx", version);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+ }
+ if(!certnum)
+ infof(data, " Version: %lu (0x%lx)", version + 1, version);
+
+ /* Serial number. */
+ ccp = ASN1tostr(&cert.serialNumber, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp);
+ if(!certnum)
+ infof(data, " Serial Number: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Signature algorithm .*/
+ ccp = dumpAlgo(&param, cert.signatureAlgorithm.beg,
+ cert.signatureAlgorithm.end);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
+ if(!certnum)
+ infof(data, " Signature Algorithm: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Start Date. */
+ ccp = ASN1tostr(&cert.notBefore, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp);
+ if(!certnum)
+ infof(data, " Start Date: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Expire Date. */
+ ccp = ASN1tostr(&cert.notAfter, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp);
+ if(!certnum)
+ infof(data, " Expire Date: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* 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 = Curl_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);
+ if(result)
+ return result;
+
+ /* Signature. */
+ ccp = ASN1tostr(&cert.signature, 0);
+ if(!ccp)
+ return CURLE_OUT_OF_MEMORY;
+ if(data->set.ssl.certinfo)
+ result = Curl_ssl_push_certinfo(data, certnum, "Signature", ccp);
+ if(!certnum)
+ infof(data, " Signature: %s", ccp);
+ free((char *) ccp);
+ if(result)
+ return result;
+
+ /* Generate PEM certificate. */
+ result = Curl_base64_encode(cert.certificate.beg,
+ cert.certificate.end - cert.certificate.beg,
+ &cp1, &cl1);
+ if(result)
+ return result;
+ /* Compute the number of characters in final certificate string. Format is:
+ -----BEGIN CERTIFICATE-----\n
+ <max 64 base64 characters>\n
+ .
+ .
+ .
+ -----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 = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2);
+ if(!certnum)
+ infof(data, "%s", cp2);
+ free(cp2);
+ return result;
+}
+
+#endif /* WANT_EXTRACT_CERTINFO */
+
+#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
+ * or USE_SECTRANSP */
+
+#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_easy *data, struct connectdata *conn,
+ const char *beg, const char *end)
+{
+ 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;
+ const char * const hostname = SSL_HOST_NAME();
+ const char * const dispname = SSL_HOST_DISPNAME();
+ size_t hostlen = strlen(hostname);
+#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(!SSL_CONN_CONFIG(verifyhost))
+ return CURLE_OK;
+
+ if(Curl_parseX509(&cert, beg, end))
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* Get the server IP address. */
+#ifdef ENABLE_IPV6
+ if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, hostname, &addr))
+ addrlen = sizeof(struct in6_addr);
+ else
+#endif
+ if(Curl_inet_pton(AF_INET, 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, 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", 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", 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, hostname, hostlen)) {
+ infof(data, " common name: %s (matched)", dnsname);
+ free(dnsname);
+ return CURLE_OK;
+ }
+ else
+ failf(data, "SSL: certificate subject name '%s' does not match "
+ "target host name '%s'", dnsname, dispname);
+ free(dnsname);
+ }
+
+ return CURLE_PEER_FAILED_VERIFICATION;
+}
+
+#endif /* WANT_VERIFYHOST */
diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h
new file mode 100644
index 000000000..a18fa1153
--- /dev/null
+++ b/lib/vtls/x509asn1.h
@@ -0,0 +1,80 @@
+#ifndef HEADER_CURL_X509ASN1_H
+#define HEADER_CURL_X509ASN1_H
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
+ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+
+#include "urldata.h"
+
+/*
+ * Types.
+ */
+
+/* ASN.1 parsed element. */
+struct Curl_asn1Element {
+ const char *header; /* Pointer to header byte. */
+ const char *beg; /* Pointer to element data. */
+ const char *end; /* Pointer to 1st byte after element. */
+ unsigned char class; /* ASN.1 element class. */
+ unsigned char tag; /* ASN.1 element tag. */
+ bool constructed; /* Element is constructed. */
+};
+
+/* X509 certificate: RFC 5280. */
+struct Curl_X509certificate {
+ struct Curl_asn1Element certificate;
+ struct Curl_asn1Element version;
+ struct Curl_asn1Element serialNumber;
+ struct Curl_asn1Element signatureAlgorithm;
+ struct Curl_asn1Element signature;
+ struct Curl_asn1Element issuer;
+ struct Curl_asn1Element notBefore;
+ struct Curl_asn1Element notAfter;
+ struct Curl_asn1Element subject;
+ struct Curl_asn1Element subjectPublicKeyInfo;
+ struct Curl_asn1Element subjectPublicKeyAlgorithm;
+ struct Curl_asn1Element subjectPublicKey;
+ struct Curl_asn1Element issuerUniqueID;
+ struct Curl_asn1Element subjectUniqueID;
+ struct Curl_asn1Element extensions;
+};
+
+/*
+ * Prototypes.
+ */
+
+int Curl_parseX509(struct Curl_X509certificate *cert,
+ const char *beg, const char *end);
+CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum,
+ const char *beg, const char *end);
+CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ const char *beg, const char *end);
+#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
+ * or USE_SECTRANSP */
+#endif /* HEADER_CURL_X509ASN1_H */