summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/vtls/mbedtls.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/vtls/mbedtls.c')
-rw-r--r--libs/libcurl/src/vtls/mbedtls.c325
1 files changed, 258 insertions, 67 deletions
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c
index d4820c4c50..a6286f82d6 100644
--- a/libs/libcurl/src/vtls/mbedtls.c
+++ b/libs/libcurl/src/vtls/mbedtls.c
@@ -75,6 +75,7 @@
#include "mbedtls.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "x509asn1.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
#include "select.h"
@@ -110,6 +111,8 @@ struct mbed_ssl_backend_data {
const char *protocols[3];
#endif
int *ciphersuites;
+ BIT(initialized); /* mbedtls_ssl_context is initialized */
+ BIT(sent_shutdown);
};
/* apply threading? */
@@ -482,6 +485,20 @@ mbed_set_selected_ciphers(struct Curl_easy *data,
return CURLE_OK;
}
+#ifdef TLS13_SUPPORT
+static int mbed_no_verify(void *udata, mbedtls_x509_crt *crt,
+ int depth, uint32_t *flags)
+{
+ (void)udata;
+ (void)crt;
+ (void)depth;
+ /* we clear any faults the mbedtls' own verification found.
+ * See <https://github.com/Mbed-TLS/mbedtls/issues/9210> */
+ *flags = 0;
+ return 0;
+}
+#endif
+
static CURLcode
mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@@ -504,6 +521,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
char errorbuf[128];
DEBUGASSERT(backend);
+ DEBUGASSERT(!backend->initialized);
if((conn_config->version == CURL_SSLVERSION_SSLv2) ||
(conn_config->version == CURL_SSLVERSION_SSLv3)) {
@@ -636,7 +654,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
+ failf(data, "Error reading client cert data %s - mbedTLS: (-0x%04X) %s",
ssl_config->key, -ret, errorbuf);
return CURLE_SSL_CERTPROBLEM;
}
@@ -737,8 +755,19 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
failf(data, "mbedTLS: ssl_config failed");
return CURLE_SSL_CONNECT_ERROR;
}
+#ifdef TLS13_SUPPORT
+ if(!verifypeer) {
+ /* Default verify behaviour changed in mbedtls v3.6.0 with TLS v1.3.
+ * On 1.3 connections, the handshake fails by default without trust
+ * anchors. We override this questionable change by installing our
+ * own verify callback that clears all errors. */
+ mbedtls_ssl_conf_verify(&backend->config, mbed_no_verify, cf);
+ }
+#endif
+
mbedtls_ssl_init(&backend->ssl);
+ backend->initialized = TRUE;
/* new profile with RSA min key len = 1024 ... */
mbedtls_ssl_conf_cert_profile(&backend->config,
@@ -785,10 +814,11 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
NULL /* rev_timeout() */);
if(conn_config->cipher_list) {
- ret = mbed_set_selected_ciphers(data, backend, conn_config->cipher_list);
- if(ret) {
+ CURLcode result = mbed_set_selected_ciphers(data, backend,
+ conn_config->cipher_list);
+ if(result != CURLE_OK) {
failf(data, "mbedTLS: failed to set cipher suites");
- return ret;
+ return result;
}
}
else {
@@ -807,8 +837,8 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
MBEDTLS_SSL_SESSION_TICKETS_DISABLED);
#endif
- /* Check if there's a cached ID we can/should use here! */
- if(ssl_config->primary.sessionid) {
+ /* Check if there is a cached ID we can/should use here! */
+ if(ssl_config->primary.cache_session) {
void *old_session = NULL;
Curl_ssl_sessionid_lock(data);
@@ -854,7 +884,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
for(i = 0; i < connssl->alpn->count; ++i) {
backend->protocols[i] = connssl->alpn->entries[i];
}
- /* this function doesn't clone the protocols array, which is why we need
+ /* this function does not clone the protocols array, which is why we need
to keep it around */
if(mbedtls_ssl_conf_alpn_protocols(&backend->config,
&backend->protocols[0])) {
@@ -880,11 +910,11 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* give application a chance to interfere with mbedTLS set up. */
if(data->set.ssl.fsslctx) {
- ret = (*data->set.ssl.fsslctx)(data, &backend->config,
- data->set.ssl.fsslctxp);
- if(ret) {
+ CURLcode result = (*data->set.ssl.fsslctx)(data, &backend->config,
+ data->set.ssl.fsslctxp);
+ if(result != CURLE_OK) {
failf(data, "error signaled by ssl ctx callback");
- return ret;
+ return result;
}
}
@@ -893,6 +923,60 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_OK;
}
+static int count_server_cert(const mbedtls_x509_crt *peercert)
+{
+ int count = 1;
+
+ DEBUGASSERT(peercert);
+
+ while(peercert->next) {
+ ++count;
+ peercert = peercert->next;
+ }
+ return count;
+}
+
+static CURLcode collect_server_cert_single(struct Curl_easy *data,
+ const mbedtls_x509_crt *server_cert,
+ int idx)
+{
+ const char *beg, *end;
+
+ DEBUGASSERT(server_cert);
+
+ beg = (const char *)server_cert->raw.p;
+ end = beg + server_cert->raw.len;
+ return Curl_extract_certinfo(data, idx, beg, end);
+}
+
+static CURLcode collect_server_cert(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct mbedtls_x509_crt *peercert)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ const bool show_verbose_server_cert = data->set.verbose;
+#else
+ const bool show_verbose_server_cert = false;
+#endif
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+ int i, count;
+
+ if(!show_verbose_server_cert && !ssl_config->certinfo)
+ return CURLE_OK;
+
+ if(!peercert)
+ return result;
+
+ count = count_server_cert(peercert);
+ result = Curl_ssl_init_certinfo(data, count);
+ for(i = 0 ; !result && peercert ; i++) {
+ result = collect_server_cert_single(data, peercert, i);
+ peercert = peercert->next;
+ }
+ return result;
+}
+
static CURLcode
mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@@ -902,8 +986,6 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
(struct mbed_ssl_backend_data *)connssl->backend;
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
const mbedtls_x509_crt *peercert;
- char cipher_str[64];
- uint16_t cipher_id;
#ifndef CURL_DISABLE_PROXY
const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
@@ -917,26 +999,41 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
ret = mbedtls_ssl_handshake(&backend->ssl);
if(ret == MBEDTLS_ERR_SSL_WANT_READ) {
- connssl->connecting_state = ssl_connect_2_reading;
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
return CURLE_OK;
}
else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
- connssl->connecting_state = ssl_connect_2_writing;
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
return CURLE_OK;
}
+ else if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
+ failf(data, "peer certificate could not be verified");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
else if(ret) {
char errorbuf[128];
+#if MBEDTLS_VERSION_NUMBER >= 0x03020000
+ CURL_TRC_CF(data, cf, "TLS version %04X",
+ mbedtls_ssl_get_version_number(&backend->ssl));
+#endif
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s",
+ failf(data, "ssl_handshake returned: (-0x%04X) %s",
-ret, errorbuf);
return CURLE_SSL_CONNECT_ERROR;
}
- cipher_id = (uint16_t)
- mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl);
- mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true);
- infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str);
-
+#if MBEDTLS_VERSION_NUMBER >= 0x03020000
+ {
+ char cipher_str[64];
+ uint16_t cipher_id;
+ cipher_id = (uint16_t)
+ mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl);
+ mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), true);
+ infof(data, "mbedTLS: Handshake complete, cipher is %s", cipher_str);
+ }
+#else
+ infof(data, "mbedTLS: Handshake complete");
+#endif
ret = mbedtls_ssl_get_verify_result(&backend->ssl);
if(!conn_config->verifyhost)
@@ -964,6 +1061,12 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
peercert = mbedtls_ssl_get_peer_cert(&backend->ssl);
+ if(peercert) {
+ const CURLcode result = collect_server_cert(cf, data, peercert);
+ if(result)
+ return result;
+ }
+
if(peercert && data->set.verbose) {
#ifndef MBEDTLS_X509_REMOVE_INFO
const size_t bufsize = 16384;
@@ -1088,10 +1191,9 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
DEBUGASSERT(backend);
- if(ssl_config->primary.sessionid) {
+ if(ssl_config->primary.cache_session) {
int ret;
mbedtls_ssl_session *our_ssl_sessionid;
- void *old_ssl_sessionid = NULL;
our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session));
if(!our_ssl_sessionid)
@@ -1108,15 +1210,11 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
- /* If there's already a matching session in the cache, delete it */
+ /* If there is already a matching session in the cache, delete it */
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- &old_ssl_sessionid, NULL))
- Curl_ssl_delsessionid(data, old_ssl_sessionid);
-
- retcode = Curl_ssl_addsessionid(cf, data, &connssl->peer,
- our_ssl_sessionid, 0,
- mbedtls_session_free);
+ retcode = Curl_ssl_set_sessionid(cf, data, &connssl->peer,
+ our_ssl_sessionid, 0,
+ mbedtls_session_free);
Curl_ssl_sessionid_unlock(data);
if(retcode)
return retcode;
@@ -1141,8 +1239,13 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data,
ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
if(ret < 0) {
- *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ?
- CURLE_AGAIN : CURLE_SEND_ERROR;
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X",
+ len, -ret);
+ *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE)
+#ifdef TLS13_SUPPORT
+ || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
+#endif
+ )? CURLE_AGAIN : CURLE_SEND_ERROR;
ret = -1;
}
@@ -1154,33 +1257,120 @@ static void mbedtls_close_all(struct Curl_easy *data)
(void)data;
}
-static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool send_shutdown, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
struct mbed_ssl_backend_data *backend =
(struct mbed_ssl_backend_data *)connssl->backend;
- char buf[32];
+ unsigned char buf[1024];
+ CURLcode result = CURLE_OK;
+ int ret;
+ size_t i;
- (void)data;
DEBUGASSERT(backend);
- /* Maybe the server has already sent a close notify alert.
- Read it to avoid an RST on the TCP connection. */
- (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf));
+ if(!backend->initialized || cf->shutdown) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ *done = FALSE;
+
+ if(!backend->sent_shutdown) {
+ /* do this only once */
+ backend->sent_shutdown = TRUE;
+ if(send_shutdown) {
+ ret = mbedtls_ssl_close_notify(&backend->ssl);
+ switch(ret) {
+ case 0: /* we sent it, receive from the server */
+ break;
+ case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: /* server also closed */
+ *done = TRUE;
+ goto out;
+ case MBEDTLS_ERR_SSL_WANT_READ:
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ goto out;
+ case MBEDTLS_ERR_SSL_WANT_WRITE:
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ goto out;
+ default:
+ CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret);
+ result = CURLE_RECV_ERROR;
+ goto out;
+ }
+ }
+ }
+
+ /* 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) {
+ ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf));
+ /* This seems to be a bug in mbedTLS TLSv1.3 where it reports
+ * WANT_READ, but has not encountered an EAGAIN. */
+ if(ret == MBEDTLS_ERR_SSL_WANT_READ)
+ ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf));
+#ifdef TLS13_SUPPORT
+ if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
+ continue;
+#endif
+ if(ret <= 0)
+ break;
+ }
- mbedtls_pk_free(&backend->pk);
- mbedtls_x509_crt_free(&backend->clicert);
- mbedtls_x509_crt_free(&backend->cacert);
+ if(ret > 0) {
+ /* still data coming in? */
+ CURL_TRC_CF(data, cf, "mbedtls_shutdown, still getting data");
+ }
+ else if(ret == 0 || (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)) {
+ /* We got the close notify alert and are done. */
+ CURL_TRC_CF(data, cf, "mbedtls_shutdown done");
+ *done = TRUE;
+ }
+ else if(ret == MBEDTLS_ERR_SSL_WANT_READ) {
+ CURL_TRC_CF(data, cf, "mbedtls_shutdown, need RECV");
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ }
+ else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ CURL_TRC_CF(data, cf, "mbedtls_shutdown, need SEND");
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ }
+ else {
+ CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret);
+ result = CURLE_RECV_ERROR;
+ }
+
+out:
+ cf->shutdown = (result || *done);
+ return result;
+}
+
+static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct mbed_ssl_backend_data *backend =
+ (struct mbed_ssl_backend_data *)connssl->backend;
+
+ (void)data;
+ DEBUGASSERT(backend);
+ if(backend->initialized) {
+ mbedtls_pk_free(&backend->pk);
+ mbedtls_x509_crt_free(&backend->clicert);
+ mbedtls_x509_crt_free(&backend->cacert);
#ifdef MBEDTLS_X509_CRL_PARSE_C
- mbedtls_x509_crl_free(&backend->crl);
+ mbedtls_x509_crl_free(&backend->crl);
#endif
- Curl_safefree(backend->ciphersuites);
- mbedtls_ssl_config_free(&backend->config);
- mbedtls_ssl_free(&backend->ssl);
- mbedtls_ctr_drbg_free(&backend->ctr_drbg);
+ Curl_safefree(backend->ciphersuites);
+ mbedtls_ssl_config_free(&backend->config);
+ mbedtls_ssl_free(&backend->ssl);
+ mbedtls_ctr_drbg_free(&backend->ctr_drbg);
#ifndef THREADING_SUPPORT
- mbedtls_entropy_free(&backend->entropy);
+ mbedtls_entropy_free(&backend->entropy);
#endif /* THREADING_SUPPORT */
+ backend->initialized = FALSE;
+ }
}
static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -1198,16 +1388,21 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf,
buffersize);
-
if(ret <= 0) {
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X",
+ buffersize, -ret);
if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
return 0;
-
*curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_READ)
#ifdef TLS13_SUPPORT
|| (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
#endif
) ? CURLE_AGAIN : CURLE_RECV_ERROR;
+ if(*curlcode != CURLE_AGAIN) {
+ char errorbuf[128];
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+ failf(data, "ssl_read returned: (-0x%04X) %s", -ret, errorbuf);
+ }
return -1;
}
@@ -1290,7 +1485,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we're allowed */
+ /* Find out how much more time we are allowed */
timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(timeout_ms < 0) {
@@ -1303,9 +1498,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
return retcode;
}
- while(ssl_connect_2 == connssl->connecting_state ||
- ssl_connect_2_reading == connssl->connecting_state ||
- ssl_connect_2_writing == connssl->connecting_state) {
+ while(ssl_connect_2 == connssl->connecting_state) {
/* check allowed time left */
timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -1316,14 +1509,13 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT;
}
- /* if ssl is expecting something, check if it's available. */
- if(connssl->connecting_state == ssl_connect_2_reading
- || connssl->connecting_state == ssl_connect_2_writing) {
+ /* if ssl is expecting something, check if it is available. */
+ if(connssl->io_need) {
- curl_socket_t writefd = ssl_connect_2_writing ==
- connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
- curl_socket_t readfd = ssl_connect_2_reading ==
- connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
+ curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND)?
+ sockfd:CURL_SOCKET_BAD;
+ curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV)?
+ sockfd:CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
nonblocking ? 0 : timeout_ms);
@@ -1353,11 +1545,10 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
* ensuring that a client using select() or epoll() will always
* have a valid fdset to wait on.
*/
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
retcode = mbed_connect_step2(cf, data);
- if(retcode || (nonblocking &&
- (ssl_connect_2 == connssl->connecting_state ||
- ssl_connect_2_reading == connssl->connecting_state ||
- ssl_connect_2_writing == connssl->connecting_state)))
+ if(retcode ||
+ (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
return retcode;
} /* repeat step2 until all transactions are done. */
@@ -1474,6 +1665,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
SSLSUPP_CA_PATH |
SSLSUPP_CAINFO_BLOB |
+ SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_SSL_CTX |
SSLSUPP_HTTPS_PROXY,
@@ -1484,7 +1676,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
mbedtls_cleanup, /* cleanup */
mbedtls_version, /* version */
Curl_none_check_cxn, /* check_cxn */
- Curl_none_shutdown, /* shutdown */
+ mbedtls_shutdown, /* shutdown */
mbedtls_data_pending, /* data_pending */
mbedtls_random, /* random */
Curl_none_cert_status_request, /* cert_status_request */
@@ -1501,7 +1693,6 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
mbedtls_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
mbed_recv, /* recv decrypted data */
mbed_send, /* send data to encrypt */
};