summaryrefslogtreecommitdiff
path: root/Utilities/cmcurl/lib/vtls/openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/vtls/openssl.c')
-rw-r--r--Utilities/cmcurl/lib/vtls/openssl.c1445
1 files changed, 909 insertions, 536 deletions
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index 0dc695d7f4..c5085be3c3 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -55,6 +55,7 @@
#include "slist.h"
#include "select.h"
#include "vtls.h"
+#include "vtls_int.h"
#include "vauth/vauth.h"
#include "keylog.h"
#include "strcase.h"
@@ -261,6 +262,28 @@
#define HAVE_OPENSSL_VERSION
#endif
+/*
+ * Whether the OpenSSL version has the API needed to support sharing an
+ * X509_STORE between connections. The API is:
+ * * `X509_STORE_up_ref` -- Introduced: OpenSSL 1.1.0.
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* OpenSSL >= 1.1.0 */
+#define HAVE_SSL_X509_STORE_SHARE
+#endif
+
+/* FIXME: On a specific machine using LCC 1.23, OpenSSL 2.0.0
+ * is found but does not seem to have X509_STORE_up_ref. */
+#if defined(__LCC__) && defined(__EDG__) && (__LCC__ == 123)
+#undef HAVE_SSL_X509_STORE_SHARE
+#endif
+
+/* What API version do we use? */
+#if defined(LIBRESSL_VERSION_NUMBER)
+#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
+#else /* !LIBRESSL_VERSION_NUMBER */
+#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
+#endif /* !LIBRESSL_VERSION_NUMBER */
+
struct ssl_backend_data {
struct Curl_easy *logger; /* transfer handle to pass trace logs to, only
using sockindex 0 */
@@ -268,12 +291,21 @@ struct ssl_backend_data {
SSL_CTX* ctx;
SSL* handle;
X509* server_cert;
+ CURLcode io_result; /* result of last BIO cfilter operation */
#ifndef HAVE_KEYLOG_CALLBACK
/* Set to true once a valid keylog entry has been created to avoid dupes. */
bool keylog_done;
#endif
};
+#if defined(HAVE_SSL_X509_STORE_SHARE)
+struct multi_ssl_backend_data {
+ char *CAfile; /* CAfile path used to generate X509 store */
+ X509_STORE *store; /* cached X509 store or NULL if none */
+ struct curltime time; /* when the cached store was created */
+};
+#endif /* HAVE_SSL_X509_STORE_SHARE */
+
#define push_certinfo(_label, _num) \
do { \
long info_len = BIO_get_mem_data(mem, &ptr); \
@@ -612,9 +644,160 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
#ifdef USE_OPENSSL
-static bool ossl_associate_connection(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex);
+#if USE_PRE_1_1_API
+#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL
+#define BIO_set_init(x,v) ((x)->init=(v))
+#define BIO_get_data(x) ((x)->ptr)
+#define BIO_set_data(x,v) ((x)->ptr=(v))
+#endif
+#define BIO_get_shutdown(x) ((x)->shutdown)
+#define BIO_set_shutdown(x,v) ((x)->shutdown=(v))
+#endif /* USE_PRE_1_1_API */
+
+static int bio_cf_create(BIO *bio)
+{
+ BIO_set_shutdown(bio, 1);
+ BIO_set_init(bio, 1);
+#if USE_PRE_1_1_API
+ bio->num = -1;
+#endif
+ BIO_set_data(bio, NULL);
+ return 1;
+}
+
+static int bio_cf_destroy(BIO *bio)
+{
+ if(!bio)
+ return 0;
+ return 1;
+}
+
+static long bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+ struct Curl_cfilter *cf = BIO_get_data(bio);
+ long ret = 1;
+
+ (void)cf;
+ (void)ptr;
+ switch(cmd) {
+ case BIO_CTRL_GET_CLOSE:
+ ret = (long)BIO_get_shutdown(bio);
+ break;
+ case BIO_CTRL_SET_CLOSE:
+ BIO_set_shutdown(bio, (int)num);
+ break;
+ case BIO_CTRL_FLUSH:
+ /* we do no delayed writes, but if we ever would, this
+ * needs to trigger it. */
+ ret = 1;
+ break;
+ case BIO_CTRL_DUP:
+ ret = 1;
+ break;
+#ifdef BIO_CTRL_EOF
+ case BIO_CTRL_EOF:
+ /* EOF has been reached on input? */
+ return (!cf->next || !cf->next->connected);
+#endif
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
+{
+ struct Curl_cfilter *cf = BIO_get_data(bio);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nwritten;
+ CURLcode result = CURLE_SEND_ERROR;
+
+ DEBUGASSERT(data);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+ /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
+ blen, (int)nwritten, result)); */
+ BIO_clear_retry_flags(bio);
+ connssl->backend->io_result = result;
+ if(nwritten < 0) {
+ if(CURLE_AGAIN == result)
+ BIO_set_retry_write(bio);
+ }
+ return (int)nwritten;
+}
+
+static int bio_cf_in_read(BIO *bio, char *buf, int blen)
+{
+ struct Curl_cfilter *cf = BIO_get_data(bio);
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct Curl_easy *data = connssl->call_data;
+ ssize_t nread;
+ CURLcode result = CURLE_RECV_ERROR;
+
+ DEBUGASSERT(data);
+ /* OpenSSL catches this case, so should we. */
+ if(!buf)
+ return 0;
+
+ nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+ /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
+ blen, (int)nread, result)); */
+ BIO_clear_retry_flags(bio);
+ connssl->backend->io_result = result;
+ if(nread < 0) {
+ if(CURLE_AGAIN == result)
+ BIO_set_retry_read(bio);
+ }
+ return (int)nread;
+}
+
+static BIO_METHOD *bio_cf_method = NULL;
+
+#if USE_PRE_1_1_API
+
+static BIO_METHOD bio_cf_meth_1_0 = {
+ BIO_TYPE_MEM,
+ "OpenSSL CF BIO",
+ bio_cf_out_write,
+ bio_cf_in_read,
+ NULL, /* puts is never called */
+ NULL, /* gets is never called */
+ bio_cf_ctrl,
+ bio_cf_create,
+ bio_cf_destroy,
+ NULL
+};
+
+static void bio_cf_init_methods(void)
+{
+ bio_cf_method = &bio_cf_meth_1_0;
+}
+
+#define bio_cf_free_methods() Curl_nop_stmt
+
+#else
+
+static void bio_cf_init_methods(void)
+{
+ bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
+ BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
+ BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
+ BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
+ BIO_meth_set_create(bio_cf_method, &bio_cf_create);
+ BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+}
+
+static void bio_cf_free_methods(void)
+{
+ BIO_meth_free(bio_cf_method);
+}
+
+#endif
+
+
+static bool ossl_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
/*
* Number of bytes to read from the random number seed file. This must be
@@ -713,12 +896,25 @@ static const char *SSL_ERROR_to_str(int err)
}
}
+static size_t ossl_version(char *buffer, size_t size);
+
/* Return error string for last OpenSSL error
*/
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
{
- if(size)
+ size_t len;
+ DEBUGASSERT(size);
+ *buf = '\0';
+
+ len = ossl_version(buf, size);
+ DEBUGASSERT(len < (size - 2));
+ if(len < (size - 2)) {
+ buf += len;
+ size -= (len + 2);
+ *buf++ = ':';
+ *buf++ = ' ';
*buf = '\0';
+ }
#ifdef OPENSSL_IS_BORINGSSL
ERR_error_string_n((uint32_t)error, buf, size);
@@ -726,7 +922,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
ERR_error_string_n(error, buf, size);
#endif
- if(size > 1 && !*buf) {
+ if(!*buf) {
strncpy(buf, (error ? "Unknown error" : "No error"), size);
buf[size - 1] = '\0';
}
@@ -746,16 +942,16 @@ static int ossl_get_ssl_data_index(void)
return ssl_ex_data_data_index;
}
-/* Return an extra data index for the connection data.
+/* Return an extra data index for the associated Curl_cfilter instance.
* This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
*/
-static int ossl_get_ssl_conn_index(void)
+static int ossl_get_ssl_cf_index(void)
{
- static int ssl_ex_data_conn_index = -1;
- if(ssl_ex_data_conn_index < 0) {
- ssl_ex_data_conn_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ static int ssl_ex_data_cf_index = -1;
+ if(ssl_ex_data_cf_index < 0) {
+ ssl_ex_data_cf_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
}
- return ssl_ex_data_conn_index;
+ return ssl_ex_data_cf_index;
}
/* Return an extra data index for the sockindex.
@@ -1580,10 +1776,11 @@ static int ossl_init(void)
OpenSSL_add_all_algorithms();
#endif
+ bio_cf_init_methods();
Curl_tls_keylog_open();
/* Initialize the extra data indexes */
- if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_conn_index() < 0 ||
+ if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_cf_index() < 0 ||
ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0)
return 0;
@@ -1625,6 +1822,7 @@ static void ossl_cleanup(void)
#endif
Curl_tls_keylog_close();
+ bio_cf_free_methods();
}
/*
@@ -1635,15 +1833,16 @@ static void ossl_cleanup(void)
* 0 means the connection has been closed
* -1 means the connection status is unknown
*/
-static int ossl_check_cxn(struct connectdata *conn)
+static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
{
/* SSL_peek takes data out of the raw recv buffer without peeking so we use
recv MSG_PEEK instead. Bug #795 */
#ifdef MSG_PEEK
char buf;
ssize_t nread;
- nread = recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
- (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK);
+ nread = recv((RECV_TYPE_ARG1)cf->conn->sock[cf->sockindex],
+ (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1,
+ (RECV_TYPE_ARG4)MSG_PEEK);
if(nread == 0)
return 0; /* connection has been closed */
if(nread == 1)
@@ -1676,6 +1875,7 @@ static int ossl_check_cxn(struct connectdata *conn)
return 0; /* connection has been closed */
}
#endif
+ (void)data;
return -1; /* connection status unknown */
}
@@ -1768,33 +1968,28 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
return list;
}
-#define set_logger(conn, data) \
- conn->ssl[0].backend->logger = data
+#define set_logger(connssl, data) \
+ connssl->backend->logger = data
-static void ossl_closeone(struct Curl_easy *data,
- struct connectdata *conn,
- struct ssl_connect_data *connssl)
+static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
DEBUGASSERT(backend);
if(backend->handle) {
- char buf[32];
- set_logger(conn, data);
- /*
- * The conn->sock[0] socket is passed to openssl with SSL_set_fd(). Make
- * sure the socket is not closed before calling OpenSSL functions that
- * will use it.
- */
- DEBUGASSERT(conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD);
+ set_logger(connssl, data);
- /* Maybe the server has already sent a close notify alert.
- Read it to avoid an RST on the TCP connection. */
- (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
+ if(cf->next && cf->next->connected) {
+ char buf[32];
+ /* Maybe the server has already sent a close notify alert.
+ Read it to avoid an RST on the TCP connection. */
+ (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
- (void)SSL_shutdown(backend->handle);
- SSL_set_connect_state(backend->handle);
+ (void)SSL_shutdown(backend->handle);
+ SSL_set_connect_state(backend->handle);
+ }
SSL_free(backend->handle);
backend->handle = NULL;
@@ -1806,30 +2001,18 @@ static void ossl_closeone(struct Curl_easy *data,
}
/*
- * This function is called when an SSL connection is closed.
- */
-static void ossl_close(struct Curl_easy *data, struct connectdata *conn,
- int sockindex)
-{
- ossl_closeone(data, conn, &conn->ssl[sockindex]);
-#ifndef CURL_DISABLE_PROXY
- ossl_closeone(data, conn, &conn->proxy_ssl[sockindex]);
-#endif
-}
-
-/*
* This function is called to shut down the SSL layer but keep the
* socket open (CCC - Clear Command Channel)
*/
-static int ossl_shutdown(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
+static int ossl_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
int retval = 0;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_connect_data *connssl = cf->ctx;
char buf[256]; /* We will use this for the OpenSSL error buffer, so it has
to be at least 256 bytes long. */
unsigned long sslerror;
- ssize_t nread;
+ int nread;
int buffsize;
int err;
bool done = FALSE;
@@ -1851,15 +2034,15 @@ static int ossl_shutdown(struct Curl_easy *data,
if(backend->handle) {
buffsize = (int)sizeof(buf);
while(!done && loop--) {
- int what = SOCKET_READABLE(conn->sock[sockindex],
+ int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
ERR_clear_error();
/* Something to read, let's do it and hope that it is the close
notify alert from the server */
- nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
- err = SSL_get_error(backend->handle, (int)nread);
+ nread = SSL_read(backend->handle, buf, buffsize);
+ err = SSL_get_error(backend->handle, nread);
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
@@ -2017,9 +2200,18 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
CURLcode result = CURLE_OK;
bool dNSName = FALSE; /* if a dNSName field exists in the cert */
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);
+ const char *hostname, *dispname;
+ int port;
+ size_t hostlen;
+
+ (void)conn;
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
+ hostlen = strlen(hostname);
+
+#ifndef ENABLE_IPV6
+ /* Silence compiler warnings for unused params */
+ (void) conn;
+#endif
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
@@ -2195,9 +2387,10 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
-static CURLcode verifystatus(struct Curl_easy *data,
- struct ssl_connect_data *connssl)
+static CURLcode verifystatus(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
+ struct ssl_connect_data *connssl = cf->ctx;
int i, ocsp_status;
unsigned char *status;
const unsigned char *p;
@@ -2478,18 +2671,24 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
char unknown[32];
const char *verstr = NULL;
struct connectdata *conn = userp;
- struct ssl_connect_data *connssl = &conn->ssl[0];
- struct ssl_backend_data *backend = connssl->backend;
+ int cf_idx = ossl_get_ssl_cf_index();
+ struct ssl_connect_data *connssl;
struct Curl_easy *data = NULL;
+ struct Curl_cfilter *cf;
- DEBUGASSERT(backend);
- data = backend->logger;
+ DEBUGASSERT(cf_idx >= 0);
+ cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
+ DEBUGASSERT(cf);
+ connssl = cf->ctx;
+ DEBUGASSERT(connssl);
+ DEBUGASSERT(connssl->backend);
+ data = connssl->backend->logger;
if(!conn || !data || !data->set.fdebug ||
(direction != 0 && direction != 1))
return;
- switch(ssl_ver) {
+ switch(ssl_ver) {
#ifdef SSL2_VERSION /* removed in recent versions */
case SSL2_VERSION:
verstr = "SSLv2";
@@ -2565,7 +2764,8 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
msg_name = ssl_msg_type(ssl_ver, msg_type);
}
- txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n",
+ txt_len = msnprintf(ssl_buf, sizeof(ssl_buf),
+ CFMSG(cf, "%s (%s), %s, %s (%d):\n"),
verstr, direction?"OUT":"IN",
tls_rt_name, msg_name, msg_type);
if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) {
@@ -2605,10 +2805,11 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
#ifdef HAS_MODERN_SET_PROTO_VER
static CURLcode
-set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
+set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx)
{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
/* first, TLS min version... */
- long curl_ssl_version_min = SSL_CONN_CONFIG(version);
+ long curl_ssl_version_min = conn_config->version;
long curl_ssl_version_max;
/* convert curl min SSL version option to OpenSSL constant */
@@ -2652,7 +2853,7 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
}
/* ... then, TLS max version */
- curl_ssl_version_max = SSL_CONN_CONFIG(version_max);
+ curl_ssl_version_max = conn_config->version_max;
/* convert curl max SSL version option to OpenSSL constant */
switch(curl_ssl_version_max) {
@@ -2700,11 +2901,12 @@ typedef long ctx_option_t;
#if !defined(HAS_MODERN_SET_PROTO_VER)
static CURLcode
set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
- struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- long ssl_version = SSL_CONN_CONFIG(version);
- long ssl_version_max = SSL_CONN_CONFIG(version_max);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ long ssl_version = conn_config->version;
+ long ssl_version_max = conn_config->version_max;
(void) data; /* In case it's unused. */
@@ -2712,14 +2914,12 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
case CURL_SSLVERSION_TLSv1_3:
#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);
+ struct ssl_connect_data *connssl = cf->ctx;
+ DEBUGASSERT(connssl->backend);
+ SSL_CTX_set_max_proto_version(connssl->backend->ctx, TLS1_3_VERSION);
*ctx_options |= SSL_OP_NO_TLSv1_2;
}
#else
- (void)sockindex;
(void)ctx_options;
failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
return CURLE_NOT_BUILT_IN;
@@ -2780,31 +2980,30 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
{
int res = 0;
- struct connectdata *conn;
struct Curl_easy *data;
- int sockindex;
+ struct Curl_cfilter *cf;
+ const struct ssl_config_data *config;
curl_socket_t *sockindex_ptr;
int data_idx = ossl_get_ssl_data_index();
- int connectdata_idx = ossl_get_ssl_conn_index();
+ int cf_idx = ossl_get_ssl_cf_index();
int sockindex_idx = ossl_get_ssl_sockindex_index();
int proxy_idx = ossl_get_proxy_index();
bool isproxy;
- if(data_idx < 0 || connectdata_idx < 0 || sockindex_idx < 0 || proxy_idx < 0)
+ if(data_idx < 0 || cf_idx < 0 || sockindex_idx < 0 || proxy_idx < 0)
return 0;
- conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx);
+ cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
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)
+ if(!cf || !data || !sockindex_ptr)
return 0;
- sockindex = (int)(sockindex_ptr - conn->sock);
+ isproxy = Curl_ssl_cf_is_proxy(cf);
- isproxy = SSL_get_ex_data(ssl, proxy_idx) ? TRUE : FALSE;
-
- if(SSL_SET_OPTION(primary.sessionid)) {
+ config = Curl_ssl_cf_get_config(cf, data);
+ if(config->primary.sessionid) {
bool incache;
bool added = FALSE;
void *old_ssl_sessionid = NULL;
@@ -2813,8 +3012,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
if(isproxy)
incache = FALSE;
else
- incache = !(Curl_ssl_getsessionid(data, conn, isproxy,
- &old_ssl_sessionid, NULL, sockindex));
+ incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
if(incache) {
if(old_ssl_sessionid != ssl_sessionid) {
infof(data, "old SSL session ID is stale, removing");
@@ -2824,8 +3022,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
}
if(!incache) {
- if(!Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid,
- 0 /* unknown size */, sockindex, &added)) {
+ if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid,
+ 0 /* unknown size */, &added)) {
if(added) {
/* the session has been put into the session cache */
res = 1;
@@ -2840,7 +3038,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
return res;
}
-static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
+static CURLcode load_cacert_from_memory(X509_STORE *store,
const struct curl_blob *ca_info_blob)
{
/* these need to be freed at the end */
@@ -2849,16 +3047,11 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
/* everything else is just a reference */
int i, count = 0;
- X509_STORE *cts = NULL;
X509_INFO *itmp = NULL;
if(ca_info_blob->len > (size_t)INT_MAX)
return CURLE_SSL_CACERT_BADFILE;
- cts = SSL_CTX_get_cert_store(ctx);
- if(!cts)
- return CURLE_OUT_OF_MEMORY;
-
cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len);
if(!cbio)
return CURLE_OUT_OF_MEMORY;
@@ -2873,7 +3066,7 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) {
itmp = sk_X509_INFO_value(inf, i);
if(itmp->x509) {
- if(X509_STORE_add_cert(cts, itmp->x509)) {
+ if(X509_STORE_add_cert(store, itmp->x509)) {
++count;
}
else {
@@ -2883,7 +3076,7 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
}
}
if(itmp->crl) {
- if(X509_STORE_add_crl(cts, itmp->crl)) {
+ if(X509_STORE_add_crl(store, itmp->crl)) {
++count;
}
else {
@@ -2901,21 +3094,414 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE);
}
-static CURLcode ossl_connect_step1(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
+static CURLcode populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ X509_STORE *store)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = CURLE_OK;
+ X509_LOOKUP *lookup = NULL;
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : conn_config->CAfile);
+ const char * const ssl_capath = conn_config->CApath;
+ const char * const ssl_crlfile = ssl_config->primary.CRLfile;
+ const bool verifypeer = conn_config->verifypeer;
+ bool imported_native_ca = false;
+
+ if(!store)
+ return CURLE_OUT_OF_MEMORY;
+
+#if defined(USE_WIN32_CRYPTO)
+ /* 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://datatracker.ietf.org/doc/html/rfc5280 */
+ if((conn_config->verifypeer || conn_config->verifyhost) &&
+ (ssl_config->native_ca_store)) {
+ HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
+
+ if(hStore) {
+ PCCERT_CONTEXT pContext = NULL;
+ /* The array of enhanced key usage OIDs will vary per certificate and is
+ declared outside of the loop so that rather than malloc/free each
+ iteration we can grow it with realloc, when necessary. */
+ CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+ DWORD enhkey_usage_size = 0;
+
+ /* This loop makes a best effort to import all valid certificates from
+ the MS root store. If a certificate cannot be imported it is skipped.
+ 'result' is used to store only hard-fail conditions (such as out of
+ memory) that cause an early break. */
+ result = CURLE_OK;
+ for(;;) {
+ X509 *x509;
+ FILETIME now;
+ BYTE key_usage[2];
+ DWORD req_size;
+ const unsigned char *encoded_cert;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char cert_name[256];
+#endif
+
+ pContext = CertEnumCertificatesInStore(hStore, pContext);
+ if(!pContext)
+ break;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+ NULL, cert_name, sizeof(cert_name))) {
+ strcpy(cert_name, "Unknown");
+ }
+ infof(data, "SSL: Checking cert \"%s\"", cert_name);
+#endif
+
+ encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+ if(!encoded_cert)
+ continue;
+
+ GetSystemTimeAsFileTime(&now);
+ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
+ continue;
+
+ /* If key usage exists check for signing attribute */
+ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+ pContext->pCertInfo,
+ key_usage, sizeof(key_usage))) {
+ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ continue;
+ }
+ else if(GetLastError())
+ continue;
+
+ /* If enhanced key usage exists check for server auth attribute.
+ *
+ * Note "In a Microsoft environment, a certificate might also have EKU
+ * extended properties that specify valid uses for the certificate."
+ * The call below checks both, and behavior varies depending on what is
+ * found. For more details see CertGetEnhancedKeyUsage doc.
+ */
+ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+ if(req_size && req_size > enhkey_usage_size) {
+ void *tmp = realloc(enhkey_usage, req_size);
+
+ if(!tmp) {
+ failf(data, "SSL: Out of memory allocating for OID list");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+ enhkey_usage_size = req_size;
+ }
+
+ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+ if(!enhkey_usage->cUsageIdentifier) {
+ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
+ good for all uses. If it returns zero, the certificate has no
+ valid uses." */
+ if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
+ continue;
+ }
+ else {
+ DWORD i;
+ bool found = false;
+
+ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+ enhkey_usage->rgpszUsageIdentifier[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ continue;
+ }
+ }
+ else
+ continue;
+ }
+ else
+ continue;
+
+ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if(!x509)
+ continue;
+
+ /* Try to import the certificate. This may fail for legitimate reasons
+ such as duplicate certificate, which is allowed by MS but not
+ OpenSSL. */
+ if(X509_STORE_add_cert(store, x509) == 1) {
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ infof(data, "SSL: Imported cert \"%s\"", cert_name);
+#endif
+ imported_native_ca = true;
+ }
+ X509_free(x509);
+ }
+
+ free(enhkey_usage);
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
+
+ if(result)
+ return result;
+ }
+ if(imported_native_ca)
+ infof(data, "successfully imported Windows CA store");
+ else
+ infof(data, "error importing Windows CA store, continuing anyway");
+ }
+#endif
+
+ if(ca_info_blob) {
+ result = load_cacert_from_memory(store, ca_info_blob);
+ if(result) {
+ if(result == CURLE_OUT_OF_MEMORY ||
+ (verifypeer && !imported_native_ca)) {
+ failf(data, "error importing CA certificate blob");
+ return result;
+ }
+ /* Only warn if no certificate verification is required. */
+ infof(data, "error importing CA certificate blob, continuing anyway");
+ }
+ }
+
+ if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) {
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+ /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
+ if(ssl_cafile &&
+ !X509_STORE_load_file(store, ssl_cafile)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate file: %s", ssl_cafile);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ if(ssl_capath &&
+ !X509_STORE_load_path(store, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate path: %s", ssl_capath);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+#else
+ /* tell OpenSSL where to find CA certificates that are used to verify the
+ server's certificate. */
+ if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+#endif
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+
+#ifdef CURL_CA_FALLBACK
+ if(verifypeer &&
+ !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) {
+ /* verifying the peer without any CA certificates won't
+ work so use openssl's built-in default as fallback */
+ X509_STORE_set_default_paths(store);
+ }
+#endif
+
+ if(ssl_crlfile) {
+ /* tell OpenSSL where to find CRL file that is used to check certificate
+ * revocation */
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+ if(!lookup ||
+ (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) {
+ failf(data, "error loading CRL file: %s", ssl_crlfile);
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ /* Everything is fine. */
+ infof(data, "successfully loaded CRL file:");
+ X509_STORE_set_flags(store,
+ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+
+ infof(data, " CRLfile: %s", ssl_crlfile);
+ }
+
+ if(verifypeer) {
+ /* Try building a chain using issuers in the trusted store first to avoid
+ problems with server-sent legacy intermediates. Newer versions of
+ OpenSSL do alternate chain checking by default but we do not know how to
+ determine that in a reliable manner.
+ https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
+ */
+#if defined(X509_V_FLAG_TRUSTED_FIRST)
+ X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST);
+#endif
+#ifdef X509_V_FLAG_PARTIAL_CHAIN
+ if(!ssl_config->no_partialchain && !ssl_crlfile) {
+ /* Have intermediate certificates in the trust store be treated as
+ trust-anchors, in the same way as self-signed root CA certificates
+ are. This allows users to verify servers using the intermediate cert
+ only, instead of needing the whole chain.
+
+ Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we
+ cannot do partial chains with a CRL check.
+ */
+ X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN);
+ }
+#endif
+ }
+
+ return result;
+}
+
+#if defined(HAVE_SSL_X509_STORE_SHARE)
+static bool cached_x509_store_expired(const struct Curl_easy *data,
+ const struct multi_ssl_backend_data *mb)
+{
+ const struct ssl_general_config *cfg = &data->set.general_ssl;
+ struct curltime now = Curl_now();
+ timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+ timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
+
+ if(timeout_ms < 0)
+ return false;
+
+ return elapsed_ms >= timeout_ms;
+}
+
+static bool cached_x509_store_different(
+ struct Curl_cfilter *cf,
+ const struct multi_ssl_backend_data *mb)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!mb->CAfile || !conn_config->CAfile)
+ return mb->CAfile != conn_config->CAfile;
+
+ return strcmp(mb->CAfile, conn_config->CAfile);
+}
+
+static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+ X509_STORE *store = NULL;
+
+ if(multi &&
+ multi->ssl_backend_data &&
+ multi->ssl_backend_data->store &&
+ !cached_x509_store_expired(data, multi->ssl_backend_data) &&
+ !cached_x509_store_different(cf, multi->ssl_backend_data)) {
+ store = multi->ssl_backend_data->store;
+ }
+
+ return store;
+}
+
+static void set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ X509_STORE *store)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+ struct multi_ssl_backend_data *mbackend;
+
+ if(!multi)
+ return;
+
+ if(!multi->ssl_backend_data) {
+ multi->ssl_backend_data = calloc(1, sizeof(struct multi_ssl_backend_data));
+ if(!multi->ssl_backend_data)
+ return;
+ }
+
+ mbackend = multi->ssl_backend_data;
+
+ if(X509_STORE_up_ref(store)) {
+ char *CAfile = NULL;
+
+ if(conn_config->CAfile) {
+ CAfile = strdup(conn_config->CAfile);
+ if(!CAfile) {
+ X509_STORE_free(store);
+ return;
+ }
+ }
+
+ if(mbackend->store) {
+ X509_STORE_free(mbackend->store);
+ free(mbackend->CAfile);
+ }
+
+ mbackend->time = Curl_now();
+ mbackend->store = store;
+ mbackend->CAfile = CAfile;
+ }
+}
+
+static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_backend_data *backend)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = CURLE_OK;
+ X509_STORE *cached_store;
+ bool cache_criteria_met;
+
+ /* Consider the X509 store cacheable if it comes exclusively from a CAfile,
+ or no source is provided and we are falling back to openssl's built-in
+ default. */
+ cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) &&
+ conn_config->verifypeer &&
+ !conn_config->CApath &&
+ !conn_config->ca_info_blob &&
+ !ssl_config->primary.CRLfile &&
+ !ssl_config->native_ca_store;
+
+ cached_store = get_cached_x509_store(cf, data);
+ if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) {
+ SSL_CTX_set_cert_store(backend->ctx, cached_store);
+ }
+ else {
+ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+
+ result = populate_x509_store(cf, data, store);
+ if(result == CURLE_OK && cache_criteria_met) {
+ set_cached_x509_store(cf, data, store);
+ }
+ }
+
+ return result;
+}
+#else /* HAVE_SSL_X509_STORE_SHARE */
+static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_backend_data *backend)
+{
+ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+
+ return populate_x509_store(cf, data, store);
+}
+#endif /* HAVE_SSL_X509_STORE_SHARE */
+
+static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
char *ciphers;
SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
- X509_LOOKUP *lookup = NULL;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_connect_data *connssl = cf->ctx;
ctx_option_t ctx_options = 0;
void *ssl_sessionid = NULL;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ BIO *bio;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
bool sni;
- const char * const hostname = SSL_HOST_NAME();
+ const char *hostname = connssl->hostname;
#ifdef ENABLE_IPV6
struct in6_addr addr;
@@ -2923,23 +3509,16 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
struct in_addr addr;
#endif
#endif
- const long int ssl_version = SSL_CONN_CONFIG(version);
+ const long int ssl_version = conn_config->version;
#ifdef USE_OPENSSL_SRP
- const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(primary.authtype);
+ const enum CURL_TLSAUTH ssl_authtype = ssl_config->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);
- const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob);
- const char * const ssl_cert_type = SSL_SET_OPTION(cert_type);
- const char * const ssl_cafile =
- /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
- (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(primary.CRLfile);
+ char * const ssl_cert = ssl_config->primary.clientcert;
+ const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+ const char * const ssl_cert_type = ssl_config->cert_type;
+ const bool verifypeer = conn_config->verifypeer;
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);
@@ -2949,7 +3528,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
if(result)
return result;
- SSL_SET_OPTION_LVALUE(certverifyresult) = !X509_V_OK;
+ ssl_config->certverifyresult = !X509_V_OK;
/* check to see if we've been told to use an explicit SSL/TLS version */
@@ -2979,7 +3558,12 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
return CURLE_SSL_CONNECT_ERROR;
}
- DEBUGASSERT(!backend->ctx);
+ if(backend->ctx) {
+ /* This happens when an error was encountered before in this
+ * step and we are called to do it again. Get rid of any leftover
+ * from the previous call. */
+ ossl_close(cf, data);
+ }
backend->ctx = SSL_CTX_new(req_method);
if(!backend->ctx) {
@@ -2996,8 +3580,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
if(data->set.fdebug && data->set.verbose) {
/* the SSL trace callback is only used for verbose logging */
SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
- SSL_CTX_set_msg_callback_arg(backend->ctx, conn);
- set_logger(conn, data);
+ SSL_CTX_set_msg_callback_arg(backend->ctx, cf->conn);
+ set_logger(connssl, data);
}
#endif
@@ -3055,7 +3639,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
/* unless the user explicitly asks to allow the protocol vulnerability we
use the work-around */
- if(!SSL_SET_OPTION(enable_beast))
+ if(!ssl_config->enable_beast)
ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#endif
@@ -3077,10 +3661,9 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
ctx_options |= SSL_OP_NO_SSLv3;
#if HAS_MODERN_SET_PROTO_VER /* 1.1.0 */
- result = set_ssl_version_min_max(backend->ctx, conn);
+ result = set_ssl_version_min_max(cf, backend->ctx);
#else
- result = set_ssl_version_min_max_legacy(&ctx_options, data, conn,
- sockindex);
+ result = set_ssl_version_min_max_legacy(&ctx_options, cf, data);
#endif
if(result != CURLE_OK)
return result;
@@ -3094,14 +3677,14 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
SSL_CTX_set_options(backend->ctx, ctx_options);
#ifdef HAS_ALPN
- if(conn->bits.tls_enable_alpn) {
+ if(cf->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)
+ && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
#endif
) {
protocols[cur++] = ALPN_H2_LENGTH;
@@ -3131,15 +3714,15 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
if(!result &&
!cert_stuff(data, backend->ctx,
ssl_cert, ssl_cert_blob, ssl_cert_type,
- SSL_SET_OPTION(key), SSL_SET_OPTION(key_blob),
- SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)))
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd))
result = CURLE_SSL_CERTPROBLEM;
if(result)
/* failf() is already done in cert_stuff() */
return result;
}
- ciphers = SSL_CONN_CONFIG(cipher_list);
+ ciphers = conn_config->cipher_list;
if(!ciphers)
ciphers = (char *)DEFAULT_CIPHER_SELECTION;
if(ciphers) {
@@ -3152,7 +3735,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
{
- char *ciphers13 = SSL_CONN_CONFIG(cipher_list13);
+ char *ciphers13 = conn_config->cipher_list13;
if(ciphers13) {
if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) {
failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
@@ -3170,7 +3753,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
#ifdef HAVE_SSL_CTX_SET_EC_CURVES
{
- char *curves = SSL_CONN_CONFIG(curves);
+ char *curves = conn_config->curves;
if(curves) {
if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
failf(data, "failed setting curves list: '%s'", curves);
@@ -3183,8 +3766,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
#ifdef USE_OPENSSL_SRP
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);
+ char * const ssl_username = ssl_config->primary.username;
+ char * const ssl_password = ssl_config->primary.password;
infof(data, "Using TLS-SRP username: %s", ssl_username);
if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) {
@@ -3195,7 +3778,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
failf(data, "failed setting SRP password");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
- if(!SSL_CONN_CONFIG(cipher_list)) {
+ if(!conn_config->cipher_list) {
infof(data, "Setting cipher list SRP");
if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) {
@@ -3206,249 +3789,9 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
}
#endif
-
-#if defined(USE_WIN32_CRYPTO)
- /* 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://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);
- HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
-
- if(hStore) {
- PCCERT_CONTEXT pContext = NULL;
- /* The array of enhanced key usage OIDs will vary per certificate and is
- declared outside of the loop so that rather than malloc/free each
- iteration we can grow it with realloc, when necessary. */
- CERT_ENHKEY_USAGE *enhkey_usage = NULL;
- DWORD enhkey_usage_size = 0;
-
- /* This loop makes a best effort to import all valid certificates from
- the MS root store. If a certificate cannot be imported it is skipped.
- 'result' is used to store only hard-fail conditions (such as out of
- memory) that cause an early break. */
- result = CURLE_OK;
- for(;;) {
- X509 *x509;
- FILETIME now;
- BYTE key_usage[2];
- DWORD req_size;
- const unsigned char *encoded_cert;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char cert_name[256];
-#endif
-
- pContext = CertEnumCertificatesInStore(hStore, pContext);
- if(!pContext)
- break;
-
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
- NULL, cert_name, sizeof(cert_name))) {
- strcpy(cert_name, "Unknown");
- }
- infof(data, "SSL: Checking cert \"%s\"", cert_name);
-#endif
-
- encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
- if(!encoded_cert)
- continue;
-
- GetSystemTimeAsFileTime(&now);
- if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
- CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
- continue;
-
- /* If key usage exists check for signing attribute */
- if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
- pContext->pCertInfo,
- key_usage, sizeof(key_usage))) {
- if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
- continue;
- }
- else if(GetLastError())
- continue;
-
- /* If enhanced key usage exists check for server auth attribute.
- *
- * Note "In a Microsoft environment, a certificate might also have EKU
- * extended properties that specify valid uses for the certificate."
- * The call below checks both, and behavior varies depending on what is
- * found. For more details see CertGetEnhancedKeyUsage doc.
- */
- if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
- if(req_size && req_size > enhkey_usage_size) {
- void *tmp = realloc(enhkey_usage, req_size);
-
- if(!tmp) {
- failf(data, "SSL: Out of memory allocating for OID list");
- result = CURLE_OUT_OF_MEMORY;
- break;
- }
-
- enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
- enhkey_usage_size = req_size;
- }
-
- if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
- if(!enhkey_usage->cUsageIdentifier) {
- /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
- good for all uses. If it returns zero, the certificate has no
- valid uses." */
- if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
- continue;
- }
- else {
- DWORD i;
- bool found = false;
-
- for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
- if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
- enhkey_usage->rgpszUsageIdentifier[i])) {
- found = true;
- break;
- }
- }
-
- if(!found)
- continue;
- }
- }
- else
- continue;
- }
- else
- continue;
-
- x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
- if(!x509)
- continue;
-
- /* Try to import the certificate. This may fail for legitimate reasons
- such as duplicate certificate, which is allowed by MS but not
- OpenSSL. */
- if(X509_STORE_add_cert(store, x509) == 1) {
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- infof(data, "SSL: Imported cert \"%s\"", cert_name);
-#endif
- imported_native_ca = true;
- }
- X509_free(x509);
- }
-
- free(enhkey_usage);
- CertFreeCertificateContext(pContext);
- CertCloseStore(hStore, 0);
-
- if(result)
- return result;
- }
- if(imported_native_ca)
- infof(data, "successfully imported Windows CA store");
- else
- infof(data, "error importing Windows CA store, continuing anyway");
- }
-#endif
-
- if(ca_info_blob) {
- result = load_cacert_from_memory(backend->ctx, ca_info_blob);
- if(result) {
- if(result == CURLE_OUT_OF_MEMORY ||
- (verifypeer && !imported_native_ca)) {
- failf(data, "error importing CA certificate blob");
- return result;
- }
- /* Only warn if no certificate verification is required. */
- infof(data, "error importing CA certificate blob, continuing anyway");
- }
- }
-
- if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) {
-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
- /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
- if(ssl_cafile &&
- !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate file: %s", ssl_cafile);
- return CURLE_SSL_CACERT_BADFILE;
- }
- if(ssl_capath &&
- !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate path: %s", ssl_capath);
- return CURLE_SSL_CACERT_BADFILE;
- }
-#else
- /* tell OpenSSL where to find CA certificates that are used to verify the
- server's certificate. */
- if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return CURLE_SSL_CACERT_BADFILE;
- }
-#endif
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-
-#ifdef CURL_CA_FALLBACK
- if(verifypeer &&
- !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) {
- /* verifying the peer without any CA certificates won't
- work so use openssl's built-in default as fallback */
- SSL_CTX_set_default_verify_paths(backend->ctx);
- }
-#endif
-
- if(ssl_crlfile) {
- /* tell OpenSSL where to find CRL file that is used to check certificate
- * revocation */
- lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx),
- X509_LOOKUP_file());
- if(!lookup ||
- (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) {
- failf(data, "error loading CRL file: %s", ssl_crlfile);
- return CURLE_SSL_CRL_BADFILE;
- }
- /* Everything is fine. */
- infof(data, "successfully loaded CRL file:");
- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
- X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
-
- infof(data, " CRLfile: %s", ssl_crlfile);
- }
-
- if(verifypeer) {
- /* Try building a chain using issuers in the trusted store first to avoid
- problems with server-sent legacy intermediates. Newer versions of
- OpenSSL do alternate chain checking by default but we do not know how to
- determine that in a reliable manner.
- https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
- */
-#if defined(X509_V_FLAG_TRUSTED_FIRST)
- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
- X509_V_FLAG_TRUSTED_FIRST);
-#endif
-#ifdef X509_V_FLAG_PARTIAL_CHAIN
- if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) {
- /* Have intermediate certificates in the trust store be treated as
- trust-anchors, in the same way as self-signed root CA certificates
- are. This allows users to verify servers using the intermediate cert
- only, instead of needing the whole chain.
-
- Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we
- cannot do partial chains with a CRL check.
- */
- X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
- X509_V_FLAG_PARTIAL_CHAIN);
- }
-#endif
- }
+ result = set_up_x509_store(cf, data, backend);
+ if(result)
+ return result;
/* OpenSSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
@@ -3495,7 +3838,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
- if(SSL_CONN_CONFIG(verifystatus))
+ if(conn_config->verifystatus)
SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp);
#endif
@@ -3520,18 +3863,17 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
}
#endif
- if(!ossl_associate_connection(data, conn, sockindex)) {
+ if(!ossl_attach_data(cf, data)) {
/* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */
- failf(data, "SSL: ossl_associate_connection failed: %s",
+ failf(data, "SSL: ossl_attach_data failed: %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
- if(SSL_SET_OPTION(primary.sessionid)) {
+ if(ssl_config->primary.sessionid) {
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
- &ssl_sessionid, NULL, sockindex)) {
+ if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
/* we got a session id, use it! */
if(!SSL_set_session(backend->handle, ssl_sessionid)) {
Curl_ssl_sessionid_unlock(data);
@@ -3546,40 +3888,25 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
Curl_ssl_sessionid_unlock(data);
}
-#ifndef CURL_DISABLE_PROXY
- if(conn->proxy_ssl[sockindex].use) {
- BIO *const bio = BIO_new(BIO_f_ssl());
- 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);
- BIO_set_ssl(bio, handle, FALSE);
- SSL_set_bio(backend->handle, bio, bio);
- }
- else
-#endif
- if(!SSL_set_fd(backend->handle, (int)sockfd)) {
- /* pass the raw socket into the SSL layers */
- failf(data, "SSL: SSL_set_fd failed: %s",
- ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
- }
+ bio = BIO_new(bio_cf_method);
+ if(!bio)
+ return CURLE_OUT_OF_MEMORY;
+
+ BIO_set_data(bio, cf);
+ SSL_set_bio(backend->handle, bio, bio);
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
-static CURLcode ossl_connect_step2(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
+static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
int err;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
@@ -3617,6 +3944,9 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
return CURLE_OK;
}
#endif
+ else if(backend->io_result == CURLE_AGAIN) {
+ return CURLE_OK;
+ }
else {
/* untreated error */
unsigned long errdetail;
@@ -3644,7 +3974,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
lerr = SSL_get_verify_result(backend->handle);
if(lerr != X509_V_OK) {
- SSL_SET_OPTION_LVALUE(certverifyresult) = lerr;
+ ssl_config->certverifyresult = lerr;
msnprintf(error_buffer, sizeof(error_buffer),
"SSL certificate problem: %s",
X509_verify_cert_error_string(lerr));
@@ -3679,15 +4009,14 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
* the SO_ERROR is also lost.
*/
if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
- const char * const hostname = SSL_HOST_NAME();
- const long int port = SSL_HOST_PORT();
char extramsg[80]="";
int sockerr = SOCKERRNO;
+
if(sockerr && detail == SSL_ERROR_SYSCALL)
Curl_strerror(sockerr, extramsg, sizeof(extramsg));
- failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ",
+ failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
- hostname, port);
+ connssl->hostname, connssl->port);
return result;
}
@@ -3710,7 +4039,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
/* Sets data and len to negotiated protocol, len is 0 if no protocol was
* negotiated
*/
- if(conn->bits.tls_enable_alpn) {
+ if(cf->conn->bits.tls_enable_alpn) {
const unsigned char *neg_protocol;
unsigned int len;
SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
@@ -3720,19 +4049,19 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
#ifdef USE_HTTP2
if(len == ALPN_H2_LENGTH &&
!memcmp(ALPN_H2, neg_protocol, len)) {
- conn->alpn = CURL_HTTP_VERSION_2;
+ cf->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->alpn = CURL_HTTP_VERSION_1_1;
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
}
}
else
infof(data, VTLS_INFOF_NO_ALPN);
- Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
+ Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -3807,11 +4136,14 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
* We check certificates to authenticate the server; otherwise we risk
* man-in-the-middle attack.
*/
-static CURLcode servercert(struct Curl_easy *data,
- struct connectdata *conn,
- struct ssl_connect_data *connssl,
+static CURLcode servercert(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
bool strict)
{
+ struct connectdata *conn = cf->conn;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
CURLcode result = CURLE_OK;
int rc;
long lerr;
@@ -3848,7 +4180,8 @@ static CURLcode servercert(struct Curl_easy *data,
return CURLE_PEER_FAILED_VERIFICATION;
}
- infof(data, "%s certificate:", SSL_IS_PROXY() ? "Proxy" : "Server");
+ infof(data, "%s certificate:",
+ Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server");
rc = x509_name_oneline(X509_get_subject_name(backend->server_cert),
buffer, sizeof(buffer));
@@ -3871,7 +4204,7 @@ static CURLcode servercert(struct Curl_easy *data,
BIO_free(mem);
- if(SSL_CONN_CONFIG(verifyhost)) {
+ if(conn_config->verifyhost) {
result = Curl_ossl_verifyhost(data, conn, backend->server_cert);
if(result) {
X509_free(backend->server_cert);
@@ -3894,10 +4227,10 @@ static CURLcode servercert(struct Curl_easy *data,
deallocating the certificate. */
/* 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)) {
- fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data,
- (int)SSL_CONN_CONFIG(issuercert_blob)->len);
+ if(conn_config->issuercert || conn_config->issuercert_blob) {
+ if(conn_config->issuercert_blob) {
+ fp = BIO_new_mem_buf(conn_config->issuercert_blob->data,
+ (int)conn_config->issuercert_blob->len);
if(!fp) {
failf(data,
"BIO_new_mem_buf NULL, " OSSL_PACKAGE
@@ -3922,10 +4255,10 @@ static CURLcode servercert(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
}
- if(BIO_read_filename(fp, SSL_CONN_CONFIG(issuercert)) <= 0) {
+ if(BIO_read_filename(fp, conn_config->issuercert) <= 0) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
- SSL_CONN_CONFIG(issuercert));
+ conn_config->issuercert);
BIO_free(fp);
X509_free(backend->server_cert);
backend->server_cert = NULL;
@@ -3937,7 +4270,7 @@ static CURLcode servercert(struct Curl_easy *data,
if(!issuer) {
if(strict)
failf(data, "SSL: Unable to read issuer cert (%s)",
- SSL_CONN_CONFIG(issuercert));
+ conn_config->issuercert);
BIO_free(fp);
X509_free(issuer);
X509_free(backend->server_cert);
@@ -3948,7 +4281,7 @@ static CURLcode servercert(struct Curl_easy *data,
if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) {
if(strict)
failf(data, "SSL: Certificate issuer check failed (%s)",
- SSL_CONN_CONFIG(issuercert));
+ conn_config->issuercert);
BIO_free(fp);
X509_free(issuer);
X509_free(backend->server_cert);
@@ -3957,15 +4290,15 @@ static CURLcode servercert(struct Curl_easy *data,
}
infof(data, " SSL certificate issuer check ok (%s)",
- SSL_CONN_CONFIG(issuercert));
+ conn_config->issuercert);
BIO_free(fp);
X509_free(issuer);
}
lerr = SSL_get_verify_result(backend->handle);
- SSL_SET_OPTION_LVALUE(certverifyresult) = lerr;
+ ssl_config->certverifyresult = lerr;
if(lerr != X509_V_OK) {
- if(SSL_CONN_CONFIG(verifypeer)) {
+ if(conn_config->verifypeer) {
/* We probably never reach this, because SSL_connect() will fail
and we return earlier if verifypeer is set? */
if(strict)
@@ -3984,8 +4317,8 @@ static CURLcode servercert(struct Curl_easy *data,
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
- if(SSL_CONN_CONFIG(verifystatus)) {
- result = verifystatus(data, connssl);
+ if(conn_config->verifystatus) {
+ result = verifystatus(cf, data);
if(result) {
X509_free(backend->server_cert);
backend->server_cert = NULL;
@@ -3998,7 +4331,9 @@ static CURLcode servercert(struct Curl_easy *data,
/* when not strict, we don't bother about the verify cert problems */
result = CURLE_OK;
- ptr = SSL_PINNED_PUB_KEY();
+ ptr = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
if(!result && ptr) {
result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
if(result)
@@ -4012,11 +4347,12 @@ static CURLcode servercert(struct Curl_easy *data,
return result;
}
-static CURLcode ossl_connect_step3(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
+static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -4027,8 +4363,8 @@ static CURLcode ossl_connect_step3(struct Curl_easy *data,
* operations.
*/
- result = servercert(data, conn, connssl, (SSL_CONN_CONFIG(verifypeer) ||
- SSL_CONN_CONFIG(verifyhost)));
+ result = servercert(cf, data, conn_config->verifypeer ||
+ conn_config->verifyhost);
if(!result)
connssl->connecting_state = ssl_connect_done;
@@ -4036,18 +4372,14 @@ static CURLcode ossl_connect_step3(struct Curl_easy *data,
return result;
}
-static Curl_recv ossl_recv;
-static Curl_send ossl_send;
-
-static CURLcode ossl_connect_common(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
+static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
bool nonblocking,
bool *done)
{
- CURLcode result;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- curl_socket_t sockfd = conn->sock[sockindex];
+ CURLcode result = CURLE_OK;
+ struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
int what;
/* check if the connection has already been established */
@@ -4066,9 +4398,9 @@ static CURLcode ossl_connect_common(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT;
}
- result = ossl_connect_step1(data, conn, sockindex);
+ result = ossl_connect_step1(cf, data);
if(result)
- return result;
+ goto out;
}
while(ssl_connect_2 == connssl->connecting_state ||
@@ -4081,7 +4413,8 @@ static CURLcode ossl_connect_common(struct Curl_easy *data,
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto out;
}
/* if ssl is expecting something, check if it's available. */
@@ -4098,16 +4431,19 @@ static CURLcode ossl_connect_common(struct Curl_easy *data,
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(0 == what) {
if(nonblocking) {
*done = FALSE;
- return CURLE_OK;
+ result = CURLE_OK;
+ goto out;
}
/* timeout */
failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto out;
}
/* socket is readable or writable */
}
@@ -4118,25 +4454,23 @@ static CURLcode ossl_connect_common(struct Curl_easy *data,
* before step2 has completed while ensuring that a client using select()
* or epoll() will always have a valid fdset to wait on.
*/
- result = ossl_connect_step2(data, conn, sockindex);
+ result = ossl_connect_step2(cf, data);
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;
+ goto out;
} /* repeat step2 until all transactions are done. */
if(ssl_connect_3 == connssl->connecting_state) {
- result = ossl_connect_step3(data, conn, sockindex);
+ result = ossl_connect_step3(cf, data);
if(result)
- return result;
+ goto out;
}
if(ssl_connect_done == connssl->connecting_state) {
connssl->state = ssl_connection_complete;
- conn->recv[sockindex] = ossl_recv;
- conn->send[sockindex] = ossl_send;
*done = TRUE;
}
else
@@ -4145,24 +4479,24 @@ static CURLcode ossl_connect_common(struct Curl_easy *data,
/* Reset our connect state machine */
connssl->connecting_state = ssl_connect_1;
- return CURLE_OK;
+out:
+ return result;
}
-static CURLcode ossl_connect_nonblocking(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
+static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
bool *done)
{
- return ossl_connect_common(data, conn, sockindex, TRUE, done);
+ return ossl_connect_common(cf, data, TRUE, done);
}
-static CURLcode ossl_connect(struct Curl_easy *data, struct connectdata *conn,
- int sockindex)
+static CURLcode ossl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
CURLcode result;
bool done = FALSE;
- result = ossl_connect_common(data, conn, sockindex, FALSE, &done);
+ result = ossl_connect_common(cf, data, FALSE, &done);
if(result)
return result;
@@ -4171,28 +4505,20 @@ static CURLcode ossl_connect(struct Curl_easy *data, struct connectdata *conn,
return CURLE_OK;
}
-static bool ossl_data_pending(const struct connectdata *conn,
- int connindex)
+static bool ossl_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
- const struct ssl_connect_data *connssl = &conn->ssl[connindex];
- DEBUGASSERT(connssl->backend);
- if(connssl->backend->handle && SSL_pending(connssl->backend->handle))
+ struct ssl_connect_data *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx && ctx->backend);
+ if(ctx->backend->handle && SSL_pending(ctx->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;
- }
-#endif
return FALSE;
}
-static size_t ossl_version(char *buffer, size_t size);
-
-static ssize_t ossl_send(struct Curl_easy *data,
- int sockindex,
+static ssize_t ossl_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
const void *mem,
size_t len,
CURLcode *curlcode)
@@ -4204,16 +4530,16 @@ static ssize_t ossl_send(struct Curl_easy *data,
unsigned long sslerror;
int memlen;
int rc;
- struct connectdata *conn = data->conn;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
+ (void)data;
DEBUGASSERT(backend);
ERR_clear_error();
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
- set_logger(conn, data);
+ set_logger(connssl, data);
rc = SSL_write(backend->handle, mem, memlen);
if(rc <= 0) {
@@ -4226,10 +4552,17 @@ static ssize_t ossl_send(struct Curl_easy *data,
should be called again later. This is basically an EWOULDBLOCK
equivalent. */
*curlcode = CURLE_AGAIN;
- return -1;
+ rc = -1;
+ goto out;
case SSL_ERROR_SYSCALL:
{
int sockerr = SOCKERRNO;
+
+ if(backend->io_result == CURLE_AGAIN) {
+ *curlcode = CURLE_AGAIN;
+ rc = -1;
+ goto out;
+ }
sslerror = ERR_get_error();
if(sslerror)
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
@@ -4242,18 +4575,20 @@ static ssize_t ossl_send(struct Curl_easy *data,
failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
error_buffer, sockerr);
*curlcode = CURLE_SEND_ERROR;
- return -1;
+ rc = -1;
+ goto out;
}
- case SSL_ERROR_SSL:
+ case SSL_ERROR_SSL: {
/* A failure in the SSL library occurred, usually a protocol error.
The OpenSSL error queue contains more information on the error. */
+ struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
+ struct ssl_connect_data *connssl_next = cf_ssl_next?
+ cf_ssl_next->ctx : NULL;
sslerror = ERR_get_error();
if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL &&
ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET &&
- conn->ssl[sockindex].state == ssl_connection_complete
-#ifndef CURL_DISABLE_PROXY
- && conn->proxy_ssl[sockindex].state == ssl_connection_complete
-#endif
+ connssl->state == ssl_connection_complete &&
+ (connssl_next && connssl_next->state == ssl_connection_complete)
) {
char ver[120];
(void)ossl_version(ver, sizeof(ver));
@@ -4263,20 +4598,26 @@ static ssize_t ossl_send(struct Curl_easy *data,
failf(data, "SSL_write() error: %s",
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
*curlcode = CURLE_SEND_ERROR;
- return -1;
+ rc = -1;
+ goto out;
+ }
+ default:
+ /* a true error */
+ failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
+ SSL_ERROR_to_str(err), SOCKERRNO);
+ *curlcode = CURLE_SEND_ERROR;
+ rc = -1;
+ goto out;
}
- /* a true error */
- failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
- SSL_ERROR_to_str(err), SOCKERRNO);
- *curlcode = CURLE_SEND_ERROR;
- return -1;
}
*curlcode = CURLE_OK;
+
+out:
return (ssize_t)rc; /* number of bytes */
}
-static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */
- int num, /* socketindex */
+static ssize_t ossl_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
char *buf, /* store read data here */
size_t buffersize, /* max amount to read */
CURLcode *curlcode)
@@ -4285,17 +4626,19 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */
unsigned long sslerror;
ssize_t nread;
int buffsize;
- struct connectdata *conn = data->conn;
- struct ssl_connect_data *connssl = &conn->ssl[num];
+ struct connectdata *conn = cf->conn;
+ struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
+ (void)data;
DEBUGASSERT(backend);
ERR_clear_error();
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
- set_logger(conn, data);
+ set_logger(connssl, data);
nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
+
if(nread <= 0) {
/* failed SSL_read */
int err = SSL_get_error(backend->handle, (int)nread);
@@ -4305,7 +4648,7 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */
break;
case SSL_ERROR_ZERO_RETURN: /* no more data */
/* close_notify alert */
- if(num == FIRSTSOCKET)
+ if(cf->sockindex == FIRSTSOCKET)
/* mark the connection for close if it is indeed the control
connection */
connclose(conn, "TLS close_notify");
@@ -4314,11 +4657,17 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke SSL_read() */
*curlcode = CURLE_AGAIN;
- return -1;
+ nread = -1;
+ goto out;
default:
/* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
value/errno" */
/* https://www.openssl.org/docs/crypto/ERR_get_error.html */
+ if(backend->io_result == CURLE_AGAIN) {
+ *curlcode = CURLE_AGAIN;
+ nread = -1;
+ goto out;
+ }
sslerror = ERR_get_error();
if((nread < 0) || sslerror) {
/* If the return code was negative or there actually is an error in the
@@ -4335,7 +4684,8 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */
failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d",
error_buffer, sockerr);
*curlcode = CURLE_RECV_ERROR;
- return -1;
+ nread = -1;
+ goto out;
}
/* For debug builds be a little stricter and error on any
SSL_ERROR_SYSCALL. For example a server may have closed the connection
@@ -4358,11 +4708,14 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */
" (Fatal because this is a curl debug build)",
error_buffer, sockerr);
*curlcode = CURLE_RECV_ERROR;
- return -1;
+ nread = -1;
+ goto out;
}
#endif
}
}
+
+out:
return nread;
}
@@ -4374,7 +4727,7 @@ static size_t ossl_version(char *buffer, size_t size)
int count;
const char *ver = OpenSSL_version(OPENSSL_VERSION);
const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */
- if(Curl_strncasecompare(ver, expected, sizeof(expected) - 1)) {
+ if(strncasecompare(ver, expected, sizeof(expected) - 1)) {
ver += sizeof(expected) - 1;
}
count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver);
@@ -4501,41 +4854,44 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
(void *)backend->ctx : (void *)backend->handle;
}
-static bool ossl_associate_connection(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
+static bool ossl_attach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
+ const struct ssl_config_data *config;
+
DEBUGASSERT(backend);
/* If we don't have SSL context, do nothing. */
if(!backend->handle)
return FALSE;
- if(SSL_SET_OPTION(primary.sessionid)) {
+ config = Curl_ssl_cf_get_config(cf, data);
+ if(config->primary.sessionid) {
int data_idx = ossl_get_ssl_data_index();
- int connectdata_idx = ossl_get_ssl_conn_index();
+ int cf_idx = ossl_get_ssl_cf_index();
int sockindex_idx = ossl_get_ssl_sockindex_index();
int proxy_idx = ossl_get_proxy_index();
- if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
+ if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
proxy_idx >= 0) {
- int data_status, conn_status, sockindex_status, proxy_status;
+ int data_status, cf_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. */
data_status = SSL_set_ex_data(backend->handle, data_idx, data);
- conn_status = SSL_set_ex_data(backend->handle, connectdata_idx, conn);
+ cf_status = SSL_set_ex_data(backend->handle, cf_idx, cf);
sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx,
- conn->sock + sockindex);
+ cf->conn->sock + cf->sockindex);
#ifndef CURL_DISABLE_PROXY
proxy_status = SSL_set_ex_data(backend->handle, proxy_idx,
- SSL_IS_PROXY() ? (void *) 1 : NULL);
+ Curl_ssl_cf_is_proxy(cf)?
+ (void *) 1 : NULL);
#else
proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL);
#endif
- if(data_status && conn_status && sockindex_status && proxy_status)
+ if(data_status && cf_status && sockindex_status && proxy_status)
return TRUE;
}
return FALSE;
@@ -4551,11 +4907,11 @@ static bool ossl_associate_connection(struct Curl_easy *data,
* transfer that might still be using the same connection.
*/
-static void ossl_disassociate_connection(struct Curl_easy *data,
- int sockindex)
+static void ossl_detach_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
DEBUGASSERT(backend);
@@ -4563,24 +4919,38 @@ static void ossl_disassociate_connection(struct Curl_easy *data,
if(!backend->handle)
return;
- if(SSL_SET_OPTION(primary.sessionid)) {
+ if(ssl_config->primary.sessionid) {
int data_idx = ossl_get_ssl_data_index();
- int connectdata_idx = ossl_get_ssl_conn_index();
+ int cf_idx = ossl_get_ssl_cf_index();
int sockindex_idx = ossl_get_ssl_sockindex_index();
int proxy_idx = ossl_get_proxy_index();
- if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
+ if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
proxy_idx >= 0) {
/* Disable references to data in "new session" callback to avoid
* accessing a stale pointer. */
SSL_set_ex_data(backend->handle, data_idx, NULL);
- SSL_set_ex_data(backend->handle, connectdata_idx, NULL);
+ SSL_set_ex_data(backend->handle, cf_idx, NULL);
SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
SSL_set_ex_data(backend->handle, proxy_idx, NULL);
}
}
}
+static void ossl_free_multi_ssl_backend_data(
+ struct multi_ssl_backend_data *mbackend)
+{
+#if defined(HAVE_SSL_X509_STORE_SHARE)
+ if(mbackend->store) {
+ X509_STORE_free(mbackend->store);
+ }
+ free(mbackend->CAfile);
+ free(mbackend);
+#else /* HAVE_SSL_X509_STORE_SHARE */
+ (void)mbackend;
+#endif /* HAVE_SSL_X509_STORE_SHARE */
+}
+
const struct Curl_ssl Curl_ssl_openssl = {
{ CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */
@@ -4606,7 +4976,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
ossl_cert_status_request, /* cert_status_request */
ossl_connect, /* connect */
ossl_connect_nonblocking, /* connect_nonblocking */
- Curl_ssl_getsock, /* getsock */
+ Curl_ssl_get_select_socks,/* getsock */
ossl_get_internals, /* get_internals */
ossl_close, /* close_one */
ossl_close_all, /* close_all */
@@ -4620,8 +4990,11 @@ const struct Curl_ssl Curl_ssl_openssl = {
#else
NULL, /* sha256sum */
#endif
- ossl_associate_connection, /* associate_connection */
- ossl_disassociate_connection /* disassociate_connection */
+ ossl_attach_data, /* use of data in this connection */
+ ossl_detach_data, /* remote of data from this connection */
+ ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
+ ossl_recv, /* recv decrypted data */
+ ossl_send, /* send data to encrypt */
};
#endif /* USE_OPENSSL */