summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/vtls/openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/vtls/openssl.c')
-rw-r--r--libs/libcurl/src/vtls/openssl.c395
1 files changed, 277 insertions, 118 deletions
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c
index 69329a7e4b..4aea58d87c 100644
--- a/libs/libcurl/src/vtls/openssl.c
+++ b/libs/libcurl/src/vtls/openssl.c
@@ -314,29 +314,36 @@ typedef unsigned long sslerr_t;
#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif /* !LIBRESSL_VERSION_NUMBER */
-#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 CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl);
+
+static CURLcode push_certinfo(struct Curl_easy *data,
+ BIO *mem, const char *label, int num)
+ WARN_UNUSED_RESULT;
-static void pubkey_show(struct Curl_easy *data,
- BIO *mem,
- int num,
- const char *type,
- const char *name,
- const BIGNUM *bn)
+static CURLcode push_certinfo(struct Curl_easy *data,
+ BIO *mem, const char *label, int num)
{
char *ptr;
+ long len = BIO_get_mem_data(mem, &ptr);
+ CURLcode result = Curl_ssl_push_certinfo_len(data, num, label, ptr, len);
+ (void)BIO_reset(mem);
+ return result;
+}
+
+static CURLcode pubkey_show(struct Curl_easy *data,
+ BIO *mem,
+ int num,
+ const char *type,
+ const char *name,
+ const BIGNUM *bn)
+{
char namebuf[32];
msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
if(bn)
BN_print(mem, bn);
- push_certinfo(namebuf, num);
+ return push_certinfo(data, mem, namebuf, num);
}
#ifdef HAVE_OPAQUE_RSA_DSA_DH
@@ -368,15 +375,16 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
return 0;
}
-static void X509V3_ext(struct Curl_easy *data,
- int certnum,
- CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+static CURLcode X509V3_ext(struct Curl_easy *data,
+ int certnum,
+ CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
{
int i;
+ CURLcode result = CURLE_OK;
if((int)sk_X509_EXTENSION_num(exts) <= 0)
/* no extensions, bail out */
- return;
+ return result;
for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
ASN1_OBJECT *obj;
@@ -386,7 +394,7 @@ static void X509V3_ext(struct Curl_easy *data,
BIO *bio_out = BIO_new(BIO_s_mem());
if(!bio_out)
- return;
+ return result;
obj = X509_EXTENSION_get_object(ext);
@@ -396,13 +404,16 @@ static void X509V3_ext(struct Curl_easy *data,
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);
+ result = Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
+ biomem->length);
BIO_free(bio_out);
+ if(result)
+ break;
}
+ return result;
}
-CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
+static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
{
CURLcode result;
STACK_OF(X509) *sk;
@@ -420,38 +431,43 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
numcerts = sk_X509_num(sk);
result = Curl_ssl_init_certinfo(data, (int)numcerts);
- if(result) {
+ if(result)
return result;
- }
mem = BIO_new(BIO_s_mem());
- if(!mem) {
- return CURLE_OUT_OF_MEMORY;
- }
+ if(!mem)
+ result = CURLE_OUT_OF_MEMORY;
- for(i = 0; i < (int)numcerts; i++) {
+ for(i = 0; !result && (i < (int)numcerts); i++) {
ASN1_INTEGER *num;
X509 *x = sk_X509_value(sk, (ossl_valsize_t)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);
+ result = push_certinfo(data, mem, "Subject", i);
+ if(result)
+ break;
X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
- push_certinfo("Issuer", i);
+ result = push_certinfo(data, mem, "Issuer", i);
+ if(result)
+ break;
BIO_printf(mem, "%lx", X509_get_version(x));
- push_certinfo("Version", i);
+ result = push_certinfo(data, mem, "Version", i);
+ if(result)
+ break;
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);
+ result = push_certinfo(data, mem, "Serial Number", i);
+ if(result)
+ break;
#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
{
@@ -464,7 +480,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
const ASN1_OBJECT *sigalgoid = NULL;
X509_ALGOR_get0(&sigalgoid, NULL, NULL, sigalg);
i2a_ASN1_OBJECT(mem, sigalgoid);
- push_certinfo("Signature Algorithm", i);
+ result = push_certinfo(data, mem, "Signature Algorithm", i);
+ if(result)
+ break;
}
xpubkey = X509_get_X509_PUBKEY(x);
@@ -472,11 +490,15 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
if(pubkeyoid) {
i2a_ASN1_OBJECT(mem, pubkeyoid);
- push_certinfo("Public Key Algorithm", i);
+ result = push_certinfo(data, mem, "Public Key Algorithm", i);
+ if(result)
+ break;
}
}
- X509V3_ext(data, i, X509_get0_extensions(x));
+ result = X509V3_ext(data, i, X509_get0_extensions(x));
+ if(result)
+ break;
}
#else
{
@@ -484,22 +506,32 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
X509_CINF *cinf = x->cert_info;
i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
- push_certinfo("Signature Algorithm", i);
+ result = push_certinfo(data, mem, "Signature Algorithm", i);
- i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
- push_certinfo("Public Key Algorithm", i);
+ if(!result) {
+ i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
+ result = push_certinfo(data, mem, "Public Key Algorithm", i);
+ }
- X509V3_ext(data, i, cinf->extensions);
+ if(!result)
+ result = X509V3_ext(data, i, cinf->extensions);
+
+ if(result)
+ break;
psig = x->signature;
}
#endif
ASN1_TIME_print(mem, X509_get0_notBefore(x));
- push_certinfo("Start date", i);
+ result = push_certinfo(data, mem, "Start date", i);
+ if(result)
+ break;
ASN1_TIME_print(mem, X509_get0_notAfter(x));
- push_certinfo("Expire date", i);
+ result = push_certinfo(data, mem, "Expire date", i);
+ if(result)
+ break;
pubkey = X509_get_pubkey(x);
if(!pubkey)
@@ -512,8 +544,7 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
pktype = pubkey->type;
#endif
switch(pktype) {
- case EVP_PKEY_RSA:
- {
+ case EVP_PKEY_RSA: {
#ifndef HAVE_EVP_PKEY_GET_PARAMS
RSA *rsa;
#ifdef HAVE_OPAQUE_EVP_PKEY
@@ -537,7 +568,9 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
#else
BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0);
#endif /* HAVE_OPAQUE_RSA_DSA_DH */
- push_certinfo("RSA Public Key", i);
+ result = push_certinfo(data, mem, "RSA Public Key", i);
+ if(result)
+ break;
print_pubkey_BN(rsa, n, i);
print_pubkey_BN(rsa, e, i);
FREE_PKEY_PARAM_BIGNUM(n);
@@ -585,8 +618,7 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
#endif /* !OPENSSL_NO_DSA */
break;
}
- case EVP_PKEY_DH:
- {
+ case EVP_PKEY_DH: {
#ifndef HAVE_EVP_PKEY_GET_PARAMS
DH *dh;
#ifdef HAVE_OPAQUE_EVP_PKEY
@@ -629,19 +661,25 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
EVP_PKEY_free(pubkey);
}
- if(psig) {
+ if(!result && psig) {
for(j = 0; j < psig->length; j++)
BIO_printf(mem, "%02x:", psig->data[j]);
- push_certinfo("Signature", i);
+ result = push_certinfo(data, mem, "Signature", i);
}
- PEM_write_bio_X509(mem, x);
- push_certinfo("Cert", i);
+ if(!result) {
+ PEM_write_bio_X509(mem, x);
+ result = push_certinfo(data, mem, "Cert", i);
+ }
}
BIO_free(mem);
- return CURLE_OK;
+ if(result)
+ /* cleanup all leftovers */
+ Curl_ssl_free_certinfo(data);
+
+ return result;
}
#endif /* quiche or OpenSSL */
@@ -723,7 +761,8 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen)
if(blen < 0)
return 0;
- nwritten = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, &result);
+ nwritten = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, FALSE,
+ &result);
CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d",
blen, (int)nwritten, result);
BIO_clear_retry_flags(bio);
@@ -997,12 +1036,6 @@ static CURLcode ossl_seed(struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
#else
-#ifdef RANDOM_FILE
- RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH);
- if(rand_enough())
- return CURLE_OK;
-#endif
-
/* fallback to a custom seeding of the PRNG using a hash based on a current
time */
do {
@@ -1049,7 +1082,7 @@ static CURLcode ossl_seed(struct Curl_easy *data)
#ifndef SSL_FILETYPE_PKCS12
#define SSL_FILETYPE_PKCS12 43
#endif
-static int do_file_type(const char *type)
+static int ossl_do_file_type(const char *type)
{
if(!type || !type[0])
return SSL_FILETYPE_PEM;
@@ -1271,7 +1304,7 @@ int cert_stuff(struct Curl_easy *data,
char error_buffer[256];
bool check_privkey = TRUE;
- int file_type = do_file_type(cert_type);
+ int file_type = ossl_do_file_type(cert_type);
if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) {
SSL *ssl;
@@ -1512,7 +1545,7 @@ fail:
key_blob = cert_blob;
}
else
- file_type = do_file_type(key_type);
+ file_type = ossl_do_file_type(key_type);
switch(file_type) {
case SSL_FILETYPE_PEM:
@@ -1646,22 +1679,6 @@ fail:
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)
{
@@ -1878,7 +1895,7 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
CURLcode result = CURLE_OK;
char buf[1024];
- int nread, err;
+ int nread = -1, err;
unsigned long sslerr;
size_t i;
@@ -1919,15 +1936,26 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
goto out;
}
}
- if(send_shutdown && SSL_shutdown(octx->ssl) == 1) {
+ }
+
+ /* SSL should now have started the shutdown from our side. Since it
+ * was not complete, we are lacking the close notify from the server. */
+ if(send_shutdown) {
+ ERR_clear_error();
+ if(SSL_shutdown(octx->ssl) == 1) {
CURL_TRC_CF(data, cf, "SSL shutdown finished");
*done = TRUE;
goto out;
}
+ if(SSL_ERROR_WANT_WRITE == SSL_get_error(octx->ssl, nread)) {
+ CURL_TRC_CF(data, cf, "SSL shutdown still wants to send");
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ goto out;
+ }
+ /* Having sent the close notify, we use SSL_read() to get the
+ * missing close notify from the server. */
}
- /* SSL should now have started the shutdown from our side. Since it
- * was not complete, we are lacking the close notify from the server. */
for(i = 0; i < 10; ++i) {
ERR_clear_error();
nread = SSL_read(octx->ssl, buf, (int)sizeof(buf));
@@ -1935,11 +1963,6 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
if(nread <= 0)
break;
}
- if(SSL_get_shutdown(octx->ssl) & SSL_RECEIVED_SHUTDOWN) {
- CURL_TRC_CF(data, cf, "SSL shutdown received, finished");
- *done = TRUE;
- goto out;
- }
err = SSL_get_error(octx->ssl, nread);
switch(err) {
case SSL_ERROR_ZERO_RETURN: /* no more data */
@@ -2003,7 +2026,7 @@ static void ossl_session_free(void *sessionid, size_t idsize)
{
/* free the ID */
(void)idsize;
- SSL_SESSION_free(sessionid);
+ free(sessionid);
}
/*
@@ -2077,8 +2100,9 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
This function is now used from ngtcp2 (QUIC) as well.
*/
-CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
- struct ssl_peer *peer, X509 *server_cert)
+static CURLcode ossl_verifyhost(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct ssl_peer *peer, X509 *server_cert)
{
bool matched = FALSE;
int target; /* target type, GEN_DNS or GEN_IPADD */
@@ -2868,6 +2892,9 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
{
const struct ssl_config_data *config;
CURLcode result = CURLE_OK;
+ size_t der_session_size;
+ unsigned char *der_session_buf;
+ unsigned char *der_session_ptr;
if(!cf || !data)
goto out;
@@ -2875,16 +2902,32 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
config = Curl_ssl_cf_get_config(cf, data);
if(config->primary.cache_session) {
+ der_session_size = i2d_SSL_SESSION(session, NULL);
+ if(der_session_size == 0) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ der_session_buf = der_session_ptr = malloc(der_session_size);
+ if(!der_session_buf) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ der_session_size = i2d_SSL_SESSION(session, &der_session_ptr);
+ if(der_session_size == 0) {
+ result = CURLE_OUT_OF_MEMORY;
+ free(der_session_buf);
+ goto out;
+ }
+
Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, session, 0,
- ossl_session_free);
- session = NULL; /* call has taken ownership */
+ result = Curl_ssl_set_sessionid(cf, data, peer, der_session_buf,
+ der_session_size, ossl_session_free);
Curl_ssl_sessionid_unlock(data);
}
out:
- if(session)
- ossl_session_free(session, 0);
return result;
}
@@ -2901,7 +2944,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
connssl = cf? cf->ctx : NULL;
data = connssl? CF_DATA_CURRENT(cf) : NULL;
Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
- return 1;
+ return 0;
}
static CURLcode load_cacert_from_memory(X509_STORE *store,
@@ -3451,7 +3494,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
const char *ciphers;
SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
ctx_option_t ctx_options = 0;
- void *ssl_sessionid = NULL;
+ SSL_SESSION *ssl_session = NULL;
+ const unsigned char *der_sessionid = NULL;
+ size_t der_sessionid_size = 0;
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);
const long int ssl_version_min = conn_config->version;
@@ -3631,6 +3676,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
SSL_CTX_set_options(octx->ssl_ctx, ctx_options);
+#ifdef SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
+ /* We do retry writes sometimes from another buffer address */
+ SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+#endif
+
#ifdef HAS_ALPN
if(alpn && alpn_len) {
if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
@@ -3897,7 +3947,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
- Curl_resolv_unlock(data, dns);
+ Curl_resolv_unlink(data, &dns);
}
}
# ifdef OPENSSL_IS_BORINGSSL
@@ -3931,18 +3981,29 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
octx->reused_session = FALSE;
if(ssl_config->primary.cache_session && transport == TRNSPRT_TCP) {
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) {
+ if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid,
+ &der_sessionid_size)) {
/* we got a session id, use it! */
- if(!SSL_set_session(octx->ssl, 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;
+ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
+ (long)der_sessionid_size);
+ if(ssl_session) {
+ if(!SSL_set_session(octx->ssl, ssl_session)) {
+ Curl_ssl_sessionid_unlock(data);
+ SSL_SESSION_free(ssl_session);
+ failf(data, "SSL: SSL_set_session failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ SSL_SESSION_free(ssl_session);
+ /* Informational message */
+ infof(data, "SSL reusing session ID");
+ octx->reused_session = TRUE;
+ }
+ else {
+ Curl_ssl_sessionid_unlock(data);
+ return CURLE_SSL_CONNECT_ERROR;
}
- /* Informational message */
- infof(data, "SSL reusing session ID");
- octx->reused_session = TRUE;
}
Curl_ssl_sessionid_unlock(data);
}
@@ -4110,30 +4171,34 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
<0 is "handshake was not successful, because a fatal error occurred" */
if(1 != err) {
int detail = SSL_get_error(octx->ssl, err);
+ CURL_TRC_CF(data, cf, "SSL_connect() -> err=%d, detail=%d", err, detail);
if(SSL_ERROR_WANT_READ == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want recv");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
return CURLE_OK;
}
if(SSL_ERROR_WANT_WRITE == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want send");
connssl->io_need = CURL_SSL_IO_NEED_SEND;
return CURLE_OK;
}
#ifdef SSL_ERROR_WANT_ASYNC
if(SSL_ERROR_WANT_ASYNC == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want async");
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
#endif
#ifdef SSL_ERROR_WANT_RETRY_VERIFY
if(SSL_ERROR_WANT_RETRY_VERIFY == detail) {
+ CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify");
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
#endif
- if(octx->io_result == CURLE_AGAIN) {
- return CURLE_OK;
- }
else {
/* untreated error */
sslerr_t errdetail;
@@ -4488,7 +4553,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
if(data->set.ssl.certinfo)
/* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, octx->ssl);
+ (void)ossl_certchain(data, octx->ssl);
octx->server_cert = SSL_get1_peer_certificate(octx->ssl);
if(!octx->server_cert) {
@@ -4525,7 +4590,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
BIO_free(mem);
if(conn_config->verifyhost) {
- result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert);
+ result = ossl_verifyhost(data, conn, peer, octx->server_cert);
if(result) {
X509_free(octx->server_cert);
octx->server_cert = NULL;
@@ -4721,6 +4786,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
*done = TRUE;
@@ -4867,6 +4933,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
ERR_clear_error();
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
rc = SSL_write(octx->ssl, mem, memlen);
@@ -4875,10 +4942,11 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
switch(err) {
case SSL_ERROR_WANT_READ:
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ *curlcode = CURLE_AGAIN;
+ rc = -1;
+ goto out;
case SSL_ERROR_WANT_WRITE:
- /* The operation did not complete; the same TLS/SSL I/O function
- should be called again later. This is basically an EWOULDBLOCK
- equivalent. */
*curlcode = CURLE_AGAIN;
rc = -1;
goto out;
@@ -4950,6 +5018,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
ERR_clear_error();
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize);
@@ -4968,8 +5037,11 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
connclose(conn, "TLS close_notify");
break;
case SSL_ERROR_WANT_READ:
+ *curlcode = CURLE_AGAIN;
+ nread = -1;
+ goto out;
case SSL_ERROR_WANT_WRITE:
- /* there is data pending, re-invoke SSL_read() */
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
*curlcode = CURLE_AGAIN;
nread = -1;
goto out;
@@ -5032,6 +5104,91 @@ out:
return nread;
}
+static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
+ struct dynbuf *binding)
+{
+ /* required for X509_get_signature_nid support */
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+ X509 *cert;
+ int algo_nid;
+ const EVP_MD *algo_type;
+ const char *algo_name;
+ unsigned int length;
+ unsigned char buf[EVP_MAX_MD_SIZE];
+
+ const char prefix[] = "tls-server-end-point:";
+ struct connectdata *conn = data->conn;
+ struct Curl_cfilter *cf = conn->cfilter[sockindex];
+ struct ossl_ctx *octx = NULL;
+
+ do {
+ const struct Curl_cftype *cft = cf->cft;
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ if(cft->name && !strcmp(cft->name, "SSL")) {
+ octx = (struct ossl_ctx *)connssl->backend;
+ break;
+ }
+
+ if(cf->next)
+ cf = cf->next;
+
+ } while(cf->next);
+
+ if(!octx) {
+ failf(data,
+ "Failed to find SSL backend for endpoint");
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+
+ cert = SSL_get1_peer_certificate(octx->ssl);
+ if(!cert) {
+ /* No server certificate, don't do channel binding */
+ return CURLE_OK;
+ }
+
+ if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) {
+ failf(data,
+ "Unable to find digest NID for certificate signature algorithm");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */
+ if(algo_nid == NID_md5 || algo_nid == NID_sha1) {
+ algo_type = EVP_sha256();
+ }
+ else {
+ algo_type = EVP_get_digestbynid(algo_nid);
+ if(!algo_type) {
+ algo_name = OBJ_nid2sn(algo_nid);
+ failf(data, "Could not find digest algorithm %s (NID %d)",
+ algo_name ? algo_name : "(null)", algo_nid);
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+ }
+
+ if(!X509_digest(cert, algo_type, buf, &length)) {
+ failf(data, "X509_digest() failed");
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+
+ /* Append "tls-server-end-point:" */
+ if(Curl_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
+ return CURLE_OUT_OF_MEMORY;
+ /* Append digest */
+ if(Curl_dyn_addn(binding, buf, length))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+#else
+ /* No X509_get_signature_nid support */
+ (void)data; /* unused */
+ (void)sockindex; /* unused */
+ (void)binding; /* unused */
+ return CURLE_OK;
+#endif
+}
+
static size_t ossl_version(char *buffer, size_t size)
{
#ifdef LIBRESSL_VERSION_NUMBER
@@ -5189,7 +5346,8 @@ const struct Curl_ssl Curl_ssl_openssl = {
SSLSUPP_ECH |
#endif
SSLSUPP_CA_CACHE |
- SSLSUPP_HTTPS_PROXY,
+ SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST,
sizeof(struct ossl_ctx),
@@ -5220,6 +5378,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
NULL, /* remote of data from this connection */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
+ ossl_get_channel_binding /* get_channel_binding */
};
#endif /* USE_OPENSSL */