summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/vtls/rustls.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/vtls/rustls.c')
-rw-r--r--libs/libcurl/src/vtls/rustls.c983
1 files changed, 661 insertions, 322 deletions
diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c
index b10328d4aa..4876175212 100644
--- a/libs/libcurl/src/vtls/rustls.c
+++ b/libs/libcurl/src/vtls/rustls.c
@@ -8,6 +8,7 @@
* Copyright (C) Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
* Copyright (C) kpcyrd, <kpcyrd@archlinux.org>
+ * Copyright (C) Daniel McCarney, <daniel@binaryparadox.net>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,27 +24,24 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_RUSTLS
-#include "curl_printf.h"
+#include "../curl_printf.h"
-#include <errno.h>
#include <rustls.h>
-#include "inet_pton.h"
-#include "urldata.h"
-#include "sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
#include "vtls.h"
#include "vtls_int.h"
#include "rustls.h"
-#include "select.h"
-#include "strerror.h"
-#include "multiif.h"
-#include "connect.h" /* for the connect timeout */
+#include "keylog.h"
+#include "../strerror.h"
#include "cipher_suite.h"
-#include "rand.h"
+#include "x509asn1.h"
struct rustls_ssl_backend_data
{
@@ -55,7 +53,7 @@ struct rustls_ssl_backend_data
};
/* For a given rustls_result error code, return the best-matching CURLcode. */
-static CURLcode map_error(rustls_result r)
+static CURLcode map_error(const rustls_result r)
{
if(rustls_result_is_cert_error(r)) {
return CURLE_PEER_FAILED_VERIFICATION;
@@ -70,10 +68,19 @@ static CURLcode map_error(rustls_result r)
}
}
+static void
+rustls_failf(struct Curl_easy *data, const rustls_result rr, const char *msg)
+{
+ char errorbuf[STRERROR_LEN];
+ size_t errorlen;
+ rustls_error(rr, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "%s: %.*s", msg, (int)errorlen, errorbuf);
+}
+
static bool
cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
{
- struct ssl_connect_data *ctx = cf->ctx;
+ const struct ssl_connect_data *ctx = cf->ctx;
struct rustls_ssl_backend_data *backend;
(void)data;
@@ -90,7 +97,7 @@ struct io_ctx {
static int
read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
- struct io_ctx *io_ctx = userdata;
+ const struct io_ctx *io_ctx = userdata;
struct ssl_connect_data *const connssl = io_ctx->cf->ctx;
CURLcode result;
int ret = 0;
@@ -98,6 +105,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
(char *)buf, len, &result);
if(nread < 0) {
nread = 0;
+ /* !checksrc! disable ERRNOVAR 4 */
if(CURLE_AGAIN == result)
ret = EAGAIN;
else
@@ -114,7 +122,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
static int
write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
- struct io_ctx *io_ctx = userdata;
+ const struct io_ctx *io_ctx = userdata;
CURLcode result;
int ret = 0;
ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
@@ -136,7 +144,7 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
static ssize_t tls_recv_more(struct Curl_cfilter *cf,
struct Curl_easy *data, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct io_ctx io_ctx;
@@ -162,11 +170,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
rresult = rustls_connection_process_new_packets(backend->conn);
if(rresult != RUSTLS_RESULT_OK) {
- char errorbuf[255];
- size_t errorlen;
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_process_new_packets: %.*s",
- (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_process_new_packets");
*err = map_error(rresult);
return -1;
}
@@ -192,7 +196,7 @@ static ssize_t
cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *plainbuf, size_t plainlen, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct rustls_connection *rconn = NULL;
@@ -232,10 +236,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
}
else if(rresult != RUSTLS_RESULT_OK) {
/* n always equals 0 in this case, do not need to check it */
- char errorbuf[255];
- size_t errorlen;
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_read");
*err = CURLE_RECV_ERROR;
nread = -1;
goto out;
@@ -278,7 +279,6 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
rustls_io_result io_error;
size_t tlswritten = 0;
size_t tlswritten_total = 0;
- CURLcode result = CURLE_OK;
io_ctx.cf = cf;
io_ctx.data = data;
@@ -304,7 +304,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
tlswritten_total += tlswritten;
}
- return result;
+ return CURLE_OK;
}
/*
@@ -321,14 +321,11 @@ static ssize_t
cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *plainbuf, size_t plainlen, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct rustls_connection *rconn = NULL;
size_t plainwritten = 0;
- rustls_result rresult;
- char errorbuf[256];
- size_t errorlen;
const unsigned char *buf = plainbuf;
size_t blen = plainlen;
ssize_t nwritten = 0;
@@ -360,11 +357,11 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(blen > 0) {
+ rustls_result rresult;
CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to Rustls", blen);
rresult = rustls_connection_write(rconn, buf, blen, &plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_write");
*err = CURLE_WRITE_ERROR;
return -1;
}
@@ -418,9 +415,9 @@ read_file_into(const char *filename,
while(!feof(f)) {
uint8_t buf[256];
- size_t rr = fread(buf, 1, sizeof(buf), f);
+ const size_t rr = fread(buf, 1, sizeof(buf), f);
if(rr == 0 ||
- CURLE_OK != Curl_dyn_addn(out, buf, rr)) {
+ CURLE_OK != curlx_dyn_addn(out, buf, rr)) {
fclose(f);
return 0;
}
@@ -436,8 +433,8 @@ cr_get_selected_ciphers(struct Curl_easy *data,
const struct rustls_supported_ciphersuite **selected,
size_t *selected_size)
{
- size_t supported_len = *selected_size;
- size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
+ const size_t supported_len = *selected_size;
+ const size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
const struct rustls_supported_ciphersuite *entry;
const char *ciphers = ciphers12;
size_t count = 0, default13_count = 0, i, j;
@@ -522,247 +519,608 @@ add_ciphers:
*selected_size = count;
}
+static void
+cr_keylog_log_cb(struct rustls_str label,
+ const uint8_t *client_random, size_t client_random_len,
+ const uint8_t *secret, size_t secret_len)
+{
+ char clabel[KEYLOG_LABEL_MAXLEN];
+ (void)client_random_len;
+ DEBUGASSERT(client_random_len == CLIENT_RANDOM_SIZE);
+ /* Turning a "rustls_str" into a null delimited "c" string */
+ msnprintf(clabel, label.len + 1, "%.*s", (int)label.len, label.data);
+ Curl_tls_keylog_write(clabel, client_random, secret, secret_len);
+}
+
static CURLcode
-cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
- struct rustls_ssl_backend_data *const backend)
+init_config_builder(struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ struct rustls_client_config_builder **config_builder)
{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const struct rustls_supported_ciphersuite **cipher_suites = NULL;
struct rustls_crypto_provider_builder *custom_provider_builder = NULL;
const struct rustls_crypto_provider *custom_provider = NULL;
- struct rustls_connection *rconn = NULL;
- struct rustls_client_config_builder *config_builder = NULL;
- const struct rustls_root_cert_store *roots = NULL;
- struct rustls_root_cert_store_builder *roots_builder = NULL;
- struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
- struct rustls_server_cert_verifier *server_cert_verifier = 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 bool verifypeer = conn_config->verifypeer;
- char errorbuf[256];
- size_t errorlen;
- rustls_result result;
- DEBUGASSERT(backend);
- rconn = backend->conn;
+ uint16_t tls_versions[2] = {
+ RUSTLS_TLS_VERSION_TLSV1_2,
+ RUSTLS_TLS_VERSION_TLSV1_3,
+ };
+ size_t tls_versions_len = 2;
+ size_t cipher_suites_len =
+ rustls_default_crypto_provider_ciphersuites_len();
- {
- uint16_t tls_versions[2] = {
- RUSTLS_TLS_VERSION_TLSV1_2,
- RUSTLS_TLS_VERSION_TLSV1_3,
- };
- size_t tls_versions_len = 2;
- const struct rustls_supported_ciphersuite **cipher_suites;
- size_t cipher_suites_len =
- rustls_default_crypto_provider_ciphersuites_len();
-
- switch(conn_config->version) {
- case CURL_SSLVERSION_DEFAULT:
- case CURL_SSLVERSION_TLSv1:
- case CURL_SSLVERSION_TLSv1_0:
- case CURL_SSLVERSION_TLSv1_1:
- case CURL_SSLVERSION_TLSv1_2:
- break;
- case CURL_SSLVERSION_TLSv1_3:
- tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ tls_versions_len = 1;
+ break;
+ default:
+ failf(data, "rustls: unsupported minimum TLS version value");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto cleanup;
+ }
+
+ switch(conn_config->version_max) {
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
tls_versions_len = 1;
break;
- default:
- failf(data, "rustls: unsupported minimum TLS version value");
- return CURLE_BAD_FUNCTION_ARGUMENT;
}
+ FALLTHROUGH();
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ default:
+ failf(data, "rustls: unsupported maximum TLS version value");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto cleanup;
+ }
- switch(conn_config->version_max) {
- case CURL_SSLVERSION_MAX_DEFAULT:
- case CURL_SSLVERSION_MAX_NONE:
- case CURL_SSLVERSION_MAX_TLSv1_3:
- break;
- case CURL_SSLVERSION_MAX_TLSv1_2:
- if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
- tls_versions_len = 1;
- break;
- }
- FALLTHROUGH();
- case CURL_SSLVERSION_MAX_TLSv1_1:
- case CURL_SSLVERSION_MAX_TLSv1_0:
- default:
- failf(data, "rustls: unsupported maximum TLS version value");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
+#if defined(USE_ECH)
+ if(ECH_ENABLED(data)) {
+ tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ tls_versions_len = 1;
+ infof(data, "rustls: ECH enabled, forcing TLSv1.3");
+ }
+#endif /* USE_ECH */
- cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
- if(!cipher_suites)
- return CURLE_OUT_OF_MEMORY;
-
- cr_get_selected_ciphers(data,
- conn_config->cipher_list,
- conn_config->cipher_list13,
- cipher_suites, &cipher_suites_len);
- if(cipher_suites_len == 0) {
- failf(data, "rustls: no supported cipher in list");
- free(cipher_suites);
- return CURLE_SSL_CIPHER;
- }
+ cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
+ if(!cipher_suites) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
- result = rustls_crypto_provider_builder_new_from_default(
- &custom_provider_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data,
- "rustls: failed to create crypto provider builder from default");
- return CURLE_SSL_CIPHER;
- }
+ cr_get_selected_ciphers(data,
+ conn_config->cipher_list,
+ conn_config->cipher_list13,
+ cipher_suites, &cipher_suites_len);
+ if(cipher_suites_len == 0) {
+ failf(data, "rustls: no supported cipher in list");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result =
- rustls_crypto_provider_builder_set_cipher_suites(
- custom_provider_builder,
- cipher_suites,
- cipher_suites_len);
- if(result != RUSTLS_RESULT_OK) {
- failf(data,
- "rustls: failed to set ciphersuites for crypto provider builder");
- rustls_crypto_provider_builder_free(custom_provider_builder);
- return CURLE_SSL_CIPHER;
- }
+ rr = rustls_crypto_provider_builder_new_from_default(
+ &custom_provider_builder);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr,
+ "failed to create crypto provider builder from default");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result = rustls_crypto_provider_builder_build(
- custom_provider_builder, &custom_provider);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build custom crypto provider");
- rustls_crypto_provider_builder_free(custom_provider_builder);
- return CURLE_SSL_CIPHER;
- }
+ rr =
+ rustls_crypto_provider_builder_set_cipher_suites(
+ custom_provider_builder,
+ cipher_suites,
+ cipher_suites_len);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr,
+ "failed to set ciphersuites for crypto provider builder");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result = rustls_client_config_builder_new_custom(custom_provider,
+ rr = rustls_crypto_provider_builder_build(
+ custom_provider_builder, &custom_provider);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build custom crypto provider");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
+
+ rr = rustls_client_config_builder_new_custom(custom_provider,
tls_versions,
tls_versions_len,
- &config_builder);
+ config_builder);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to create client config builder");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
+
+cleanup:
+ if(cipher_suites) {
free(cipher_suites);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to create client config");
- return CURLE_SSL_CIPHER;
- }
}
+ if(custom_provider_builder) {
+ rustls_crypto_provider_builder_free(custom_provider_builder);
+ }
+ if(custom_provider) {
+ rustls_crypto_provider_free(custom_provider);
+ }
+ return result;
+}
- rustls_crypto_provider_builder_free(custom_provider_builder);
- rustls_crypto_provider_free(custom_provider);
+static void
+init_config_builder_alpn(struct Curl_easy *data,
+ const struct ssl_connect_data *connssl,
+ struct rustls_client_config_builder *config_builder) {
+ struct alpn_proto_buf proto;
+ rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
+ size_t i;
- if(connssl->alpn) {
- struct alpn_proto_buf proto;
- rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
- size_t i;
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
+ alpn[i].len = strlen(connssl->alpn->entries[i]);
+ }
+ rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
+ connssl->alpn->count);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+}
- for(i = 0; i < connssl->alpn->count; ++i) {
- alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
- alpn[i].len = strlen(connssl->alpn->entries[i]);
- }
- rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
- connssl->alpn->count);
- Curl_alpn_to_proto_str(&proto, connssl->alpn);
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+static CURLcode
+init_config_builder_verifier_crl(
+ struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ struct rustls_web_pki_server_cert_verifier_builder *builder)
+{
+ CURLcode result = CURLE_OK;
+ struct dynbuf crl_contents;
+ rustls_result rr;
+
+ curlx_dyn_init(&crl_contents, DYN_CRLFILE_SIZE);
+ if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
+ failf(data, "rustls: failed to read revocation list file");
+ result = CURLE_SSL_CRL_BADFILE;
+ goto cleanup;
}
- if(!verifypeer) {
- rustls_client_config_builder_dangerous_set_certificate_verifier(
- config_builder, cr_verify_none);
+
+ rr = rustls_web_pki_server_cert_verifier_builder_add_crl(
+ builder,
+ curlx_dyn_uptr(&crl_contents),
+ curlx_dyn_len(&crl_contents));
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to parse revocation list");
+ result = CURLE_SSL_CRL_BADFILE;
+ goto cleanup;
}
- else if(ca_info_blob || ssl_cafile) {
- roots_builder = rustls_root_cert_store_builder_new();
-
- if(ca_info_blob) {
- /* Enable strict parsing only if verification is not disabled. */
- result = rustls_root_cert_store_builder_add_pem(roots_builder,
- ca_info_blob->data,
- ca_info_blob->len,
- verifypeer);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to parse trusted certificates from blob");
- rustls_root_cert_store_builder_free(roots_builder);
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
- }
+
+cleanup:
+ curlx_dyn_free(&crl_contents);
+ return result;
+}
+
+static CURLcode
+init_config_builder_verifier(struct Curl_easy *data,
+ struct rustls_client_config_builder *builder,
+ const struct ssl_primary_config *conn_config,
+ const struct curl_blob *ca_info_blob,
+ const char * const ssl_cafile) {
+ const struct rustls_root_cert_store *roots = NULL;
+ struct rustls_root_cert_store_builder *roots_builder = NULL;
+ struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
+ struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+ rustls_result rr = RUSTLS_RESULT_OK;
+ CURLcode result = CURLE_OK;
+
+ roots_builder = rustls_root_cert_store_builder_new();
+ if(ca_info_blob) {
+ rr = rustls_root_cert_store_builder_add_pem(roots_builder,
+ ca_info_blob->data,
+ ca_info_blob->len,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to parse trusted certificates from blob");
+
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
}
- else if(ssl_cafile) {
- /* Enable strict parsing only if verification is not disabled. */
- result = rustls_root_cert_store_builder_load_roots_from_file(
- roots_builder, ssl_cafile, verifypeer);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to load trusted certificates");
- rustls_root_cert_store_builder_free(roots_builder);
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
- }
+ }
+ else if(ssl_cafile) {
+ rr = rustls_root_cert_store_builder_load_roots_from_file(roots_builder,
+ ssl_cafile,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to load trusted certificates");
+
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
}
+ }
- result = rustls_root_cert_store_builder_build(roots_builder, &roots);
- rustls_root_cert_store_builder_free(roots_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build trusted root certificate store");
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
+ rr = rustls_root_cert_store_builder_build(roots_builder, &roots);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build trusted root certificate store");
+ result = CURLE_SSL_CACERT_BADFILE;
+ }
+
+ verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+
+ if(conn_config->CRLfile) {
+ result = init_config_builder_verifier_crl(data,
+ conn_config,
+ verifier_builder);
+ if(result != CURLE_OK) {
+ goto cleanup;
}
+ }
- verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+ rr = rustls_web_pki_server_cert_verifier_builder_build(
+ verifier_builder, &server_cert_verifier);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build certificate verifier");
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ rustls_client_config_builder_set_server_verifier(builder,
+ server_cert_verifier);
+cleanup:
+ if(roots_builder) {
+ rustls_root_cert_store_builder_free(roots_builder);
+ }
+ if(roots) {
rustls_root_cert_store_free(roots);
+ }
+ if(verifier_builder) {
+ rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
+ }
+ if(server_cert_verifier) {
+ rustls_server_cert_verifier_free(server_cert_verifier);
+ }
- if(conn_config->CRLfile) {
- struct dynbuf crl_contents;
- Curl_dyn_init(&crl_contents, SIZE_MAX);
- if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
- failf(data, "rustls: failed to read revocation list file");
- Curl_dyn_free(&crl_contents);
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- return CURLE_SSL_CRL_BADFILE;
- }
+ return result;
+}
- result = rustls_web_pki_server_cert_verifier_builder_add_crl(
- verifier_builder,
- Curl_dyn_uptr(&crl_contents),
- Curl_dyn_len(&crl_contents));
- Curl_dyn_free(&crl_contents);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to parse revocation list");
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- return CURLE_SSL_CRL_BADFILE;
- }
+static CURLcode
+init_config_builder_platform_verifier(
+ struct Curl_easy *data,
+ struct rustls_client_config_builder *builder)
+{
+ struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ rr = rustls_platform_server_cert_verifier(&server_cert_verifier);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to create platform certificate verifier");
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ rustls_client_config_builder_set_server_verifier(builder,
+ server_cert_verifier);
+
+cleanup:
+ if(server_cert_verifier) {
+ rustls_server_cert_verifier_free(server_cert_verifier);
+ }
+ return result;
+}
+
+static CURLcode
+init_config_builder_keylog(struct Curl_easy *data,
+ struct rustls_client_config_builder *builder)
+{
+ rustls_result rr;
+
+ Curl_tls_keylog_open();
+ if(!Curl_tls_keylog_enabled()) {
+ return CURLE_OK;
+ }
+
+ rr = rustls_client_config_builder_set_key_log(builder,
+ cr_keylog_log_cb,
+ NULL);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls_client_config_builder_set_key_log");
+ Curl_tls_keylog_close();
+ return map_error(rr);
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode
+init_config_builder_client_auth(struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ const struct ssl_config_data *ssl_config,
+ struct rustls_client_config_builder *builder)
+{
+ struct dynbuf cert_contents;
+ struct dynbuf key_contents;
+ rustls_result rr;
+ const struct rustls_certified_key *certified_key = NULL;
+ CURLcode result = CURLE_OK;
+
+ if(conn_config->clientcert && !ssl_config->key) {
+ failf(data, "rustls: must provide key with certificate '%s'",
+ conn_config->clientcert);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ else if(!conn_config->clientcert && ssl_config->key) {
+ failf(data, "rustls: must provide certificate with key '%s'",
+ conn_config->clientcert);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ curlx_dyn_init(&cert_contents, DYN_CERTFILE_SIZE);
+ curlx_dyn_init(&key_contents, DYN_KEYFILE_SIZE);
+
+ if(!read_file_into(conn_config->clientcert, &cert_contents)) {
+ failf(data, "rustls: failed to read client certificate file: '%s'",
+ conn_config->clientcert);
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ if(!read_file_into(ssl_config->key, &key_contents)) {
+ failf(data, "rustls: failed to read key file: '%s'", ssl_config->key);
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_certified_key_build(curlx_dyn_uptr(&cert_contents),
+ curlx_dyn_len(&cert_contents),
+ curlx_dyn_uptr(&key_contents),
+ curlx_dyn_len(&key_contents),
+ &certified_key);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to build certified key");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_certified_key_keys_match(certified_key);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data,
+ rr,
+ "rustls: client certificate and keypair files do not match:");
+
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_client_config_builder_set_certified_key(builder,
+ &certified_key,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to set certified key");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+cleanup:
+ curlx_dyn_free(&cert_contents);
+ curlx_dyn_free(&key_contents);
+ if(certified_key) {
+ rustls_certified_key_free(certified_key);
+ }
+ return result;
+}
+
+#if defined(USE_ECH)
+static CURLcode
+init_config_builder_ech(struct Curl_easy *data,
+ const struct ssl_connect_data *connssl,
+ struct rustls_client_config_builder *builder)
+{
+ const rustls_hpke *hpke = rustls_supported_hpke();
+ unsigned char *ech_config = NULL;
+ size_t ech_config_len = 0;
+ struct Curl_dns_entry *dns = NULL;
+ struct Curl_https_rrinfo *rinfo = NULL;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ if(!hpke) {
+ failf(data,
+ "rustls: ECH unavailable, rustls-ffi built without "
+ "HPKE compatible crypto provider");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+
+ if(data->set.str[STRING_ECH_PUBLIC]) {
+ failf(data, "rustls: ECH outername not supported");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+
+ if(data->set.tls_ech == CURLECH_GREASE) {
+ rr = rustls_client_config_builder_enable_ech_grease(builder, hpke);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to configure ECH GREASE");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
}
+ return CURLE_OK;
+ }
- result = rustls_web_pki_server_cert_verifier_builder_build(
- verifier_builder, &server_cert_verifier);
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build certificate verifier");
- rustls_server_cert_verifier_free(server_cert_verifier);
+ if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+ const char *b64 = data->set.str[STRING_ECH_CONFIG];
+ size_t decode_result;
+ if(!b64) {
+ infof(data, "rustls: ECHConfig from command line empty");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ /* rustls-ffi expects the raw TLS encoded ECHConfigList bytes */
+ decode_result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
+ if(decode_result || !ech_config) {
+ infof(data, "rustls: cannot base64 decode ECHConfig from command line");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ }
+ else {
+ if(connssl->peer.hostname) {
+ dns = Curl_dnscache_get(data, connssl->peer.hostname,
+ connssl->peer.port, data->conn->ip_version);
+ }
+ if(!dns) {
+ failf(data, "rustls: ECH requested but no DNS info available");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ rinfo = dns->hinfo;
+ if(!rinfo || !rinfo->echconfiglist) {
+ failf(data, "rustls: ECH requested but no ECHConfig available");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ ech_config = rinfo->echconfiglist;
+ ech_config_len = rinfo->echconfiglist_len;
+ }
+
+ rr = rustls_client_config_builder_enable_ech(builder,
+ ech_config,
+ ech_config_len,
+ hpke);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to configure ECH");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+cleanup:
+ /* if we base64 decoded, we can free now */
+ if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+ free(ech_config);
+ }
+ if(dns) {
+ Curl_resolv_unlink(data, &dns);
+ }
+ return result;
+}
+#endif /* USE_ECH */
+
+static CURLcode
+cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
+ struct rustls_ssl_backend_data *const backend)
+{
+ const struct ssl_connect_data *connssl = cf->ctx;
+ const 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);
+ struct rustls_connection *rconn = NULL;
+ struct rustls_client_config_builder *config_builder = 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);
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ result = init_config_builder(data, conn_config, &config_builder);
+ if(result != CURLE_OK) {
+ return result;
+ }
+
+ if(connssl->alpn) {
+ init_config_builder_alpn(data, connssl, config_builder);
+ }
+
+ if(!conn_config->verifypeer) {
+ rustls_client_config_builder_dangerous_set_certificate_verifier(
+ config_builder, cr_verify_none);
+ }
+ else if(ssl_config->native_ca_store) {
+ result = init_config_builder_platform_verifier(data, config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+ else if(ca_info_blob || ssl_cafile) {
+ result = init_config_builder_verifier(data,
+ config_builder,
+ conn_config,
+ ca_info_blob,
+ ssl_cafile);
+ if(result != CURLE_OK) {
rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
+ return result;
}
+ }
- rustls_client_config_builder_set_server_verifier(config_builder,
- server_cert_verifier);
- rustls_server_cert_verifier_free(server_cert_verifier);
+ if(conn_config->clientcert || ssl_config->key) {
+ result = init_config_builder_client_auth(data,
+ conn_config,
+ ssl_config,
+ config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+
+#if defined(USE_ECH)
+ if(ECH_ENABLED(data)) {
+ result = init_config_builder_ech(data, connssl, config_builder);
+ if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+#endif /* USE_ECH */
+
+ result = init_config_builder_keylog(data, config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
}
- result = rustls_client_config_builder_build(
+ rr = rustls_client_config_builder_build(
config_builder,
&backend->config);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build client config");
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build client config");
+ rustls_client_config_builder_free(config_builder);
rustls_client_config_free(backend->config);
return CURLE_SSL_CONNECT_ERROR;
}
DEBUGASSERT(rconn == NULL);
- result = rustls_client_connection_new(backend->config,
- connssl->peer.hostname, &rconn);
- if(result != RUSTLS_RESULT_OK) {
- rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
+ rr = rustls_client_connection_new(backend->config,
+ connssl->peer.hostname,
+ &rconn);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, result, "rustls_client_connection_new");
return CURLE_COULDNT_CONNECT;
}
DEBUGASSERT(rconn);
rustls_connection_set_userdata(rconn, backend);
backend->conn = rconn;
- return CURLE_OK;
+
+ return result;
}
static void
@@ -779,48 +1137,36 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
/* Given an established network connection, do a TLS handshake.
*
- * If `blocking` is true, this function will block until the handshake is
- * complete. Otherwise it will return as soon as I/O would block.
- *
- * For the non-blocking I/O case, this function will set `*done` to true
- * once the handshake is complete. This function never reads the value of
- * `*done*`.
+ * This function will set `*done` to true once the handshake is complete.
+ * This function never reads the value of `*done*`.
*/
static CURLcode
-cr_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool blocking,
- bool *done)
+cr_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
{
struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- struct rustls_ssl_backend_data *const backend =
+ const struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
- struct rustls_connection *rconn = NULL;
+ const struct rustls_connection *rconn = NULL;
CURLcode tmperr = CURLE_OK;
int result;
- int what;
bool wants_read;
bool wants_write;
- curl_socket_t writefd;
- curl_socket_t readfd;
- timediff_t timeout_ms;
- timediff_t socket_check_timeout;
DEBUGASSERT(backend);
- CURL_TRC_CF(data, cf, "cr_connect_common, state=%d", connssl->state);
+ CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state);
*done = FALSE;
+
if(!backend->conn) {
result = cr_init_backend(cf, data,
(struct rustls_ssl_backend_data *)connssl->backend);
- CURL_TRC_CF(data, cf, "cr_connect_common, init backend -> %d", result);
+ CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result);
if(result != CURLE_OK) {
return result;
}
connssl->state = ssl_connection_negotiating;
}
-
rconn = backend->conn;
/* Read/write data until the handshake is done or the socket would block. */
@@ -846,8 +1192,10 @@ cr_connect_common(struct Curl_cfilter *cf,
}
/* REALLY Done with the handshake. */
{
- uint16_t proto = rustls_connection_get_protocol_version(rconn);
- uint16_t cipher = rustls_connection_get_negotiated_ciphersuite(rconn);
+ const uint16_t proto =
+ rustls_connection_get_protocol_version(rconn);
+ const uint16_t cipher =
+ rustls_connection_get_negotiated_ciphersuite(rconn);
char buf[64] = "";
const char *ver = "TLS version unknown";
if(proto == RUSTLS_TLS_VERSION_TLSV1_3)
@@ -858,6 +1206,43 @@ cr_connect_common(struct Curl_cfilter *cf,
infof(data, "rustls: handshake complete, %s, cipher: %s",
ver, buf);
}
+ if(data->set.ssl.certinfo) {
+ size_t num_certs = 0;
+ while(rustls_connection_get_peer_certificate(rconn, (int)num_certs)) {
+ num_certs++;
+ }
+ result = Curl_ssl_init_certinfo(data, (int)num_certs);
+ if(result)
+ return result;
+ for(size_t i = 0; i < num_certs; i++) {
+ const rustls_certificate *cert;
+ const unsigned char *der_data;
+ size_t der_len;
+ rustls_result rresult = RUSTLS_RESULT_OK;
+ cert = rustls_connection_get_peer_certificate(rconn, i);
+ DEBUGASSERT(cert); /* Should exist since we counted already */
+ rresult = rustls_certificate_get_der(cert, &der_data, &der_len);
+ if(rresult != RUSTLS_RESULT_OK) {
+ char errorbuf[255];
+ size_t errorlen;
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data,
+ "Failed getting DER of server certificate #%ld: %.*s", i,
+ (int)errorlen, errorbuf);
+ return map_error(rresult);
+ }
+ {
+ const char *beg;
+ const char *end;
+ beg = (const char *)der_data;
+ end = (const char *)(der_data + der_len);
+ result = Curl_extract_certinfo(data, (int)i, beg, end);
+ if(result)
+ return result;
+ }
+ }
+ }
+
connssl->state = ssl_connection_complete;
*done = TRUE;
return CURLE_OK;
@@ -868,50 +1253,14 @@ cr_connect_common(struct Curl_cfilter *cf,
wants_write = rustls_connection_wants_write(rconn) ||
backend->plain_out_buffered;
DEBUGASSERT(wants_read || wants_write);
- writefd = wants_write ? sockfd : CURL_SOCKET_BAD;
- readfd = wants_read ? sockfd : CURL_SOCKET_BAD;
-
- /* check allowed time left */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "rustls: operation timed out before socket check");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- socket_check_timeout = blocking ? timeout_ms : 0;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- socket_check_timeout);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- if(blocking && 0 == what) {
- failf(data, "rustls: connection timeout after %" FMT_TIMEDIFF_T " ms",
- socket_check_timeout);
- return CURLE_OPERATION_TIMEDOUT;
- }
- if(0 == what) {
- CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block",
- wants_read && wants_write ? "writing and reading" :
- wants_write ? "writing" : "reading");
- if(wants_write)
- connssl->io_need |= CURL_SSL_IO_NEED_SEND;
- if(wants_read)
- connssl->io_need |= CURL_SSL_IO_NEED_RECV;
- return CURLE_OK;
- }
- /* socket is readable or writable */
if(wants_write) {
CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls.");
cr_send(cf, data, NULL, 0, &tmperr);
if(tmperr == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "writing would block");
- /* fall through */
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ return CURLE_OK;
}
else if(tmperr != CURLE_OK) {
return tmperr;
@@ -923,7 +1272,8 @@ cr_connect_common(struct Curl_cfilter *cf,
if(tls_recv_more(cf, data, &tmperr) < 0) {
if(tmperr == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "reading would block");
- /* fall through */
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ return CURLE_OK;
}
else if(tmperr == CURLE_RECV_ERROR) {
return CURLE_SSL_CONNECT_ERROR;
@@ -940,20 +1290,6 @@ cr_connect_common(struct Curl_cfilter *cf,
DEBUGASSERT(FALSE);
}
-static CURLcode
-cr_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data, bool *done)
-{
- return cr_connect_common(cf, data, false, done);
-}
-
-static CURLcode
-cr_connect_blocking(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- bool done; /* unused */
- return cr_connect_common(cf, data, true, &done);
-}
-
static void *
cr_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
@@ -967,14 +1303,13 @@ cr_get_internals(struct ssl_connect_data *connssl,
static CURLcode
cr_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool send_shutdown, bool *done)
+ const bool send_shutdown, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
struct rustls_ssl_backend_data *backend =
(struct rustls_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
ssize_t nwritten, nread;
- char buf[1024];
size_t i;
DEBUGASSERT(backend);
@@ -1007,6 +1342,7 @@ cr_shutdown(struct Curl_cfilter *cf,
}
for(i = 0; i < 10; ++i) {
+ char buf[1024];
nread = cr_recv(cf, data, buf, (int)sizeof(buf), &result);
if(nread <= 0)
break;
@@ -1036,7 +1372,7 @@ out:
static void
cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct ssl_connect_data *connssl = cf->ctx;
+ const struct ssl_connect_data *connssl = cf->ctx;
struct rustls_ssl_backend_data *backend =
(struct rustls_ssl_backend_data *)connssl->backend;
@@ -1054,7 +1390,7 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static size_t cr_version(char *buffer, size_t size)
{
- struct rustls_str ver = rustls_version();
+ const struct rustls_str ver = rustls_version();
return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
}
@@ -1068,35 +1404,38 @@ cr_random(struct Curl_easy *data, unsigned char *entropy, size_t length)
return map_error(rresult);
}
+static void cr_cleanup(void)
+{
+ Curl_tls_keylog_close();
+}
+
const struct Curl_ssl Curl_ssl_rustls = {
{ CURLSSLBACKEND_RUSTLS, "rustls" },
SSLSUPP_CAINFO_BLOB | /* supports */
SSLSUPP_HTTPS_PROXY |
SSLSUPP_CIPHER_LIST |
- SSLSUPP_TLS13_CIPHERSUITES,
+ SSLSUPP_TLS13_CIPHERSUITES |
+ SSLSUPP_CERTINFO |
+ SSLSUPP_ECH,
sizeof(struct rustls_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ cr_cleanup, /* cleanup */
cr_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
cr_shutdown, /* shutdown */
cr_data_pending, /* data_pending */
cr_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
- cr_connect_blocking, /* connect */
- cr_connect_nonblocking, /* connect_nonblocking */
+ NULL, /* cert_status_request */
+ cr_connect, /* connect */
Curl_ssl_adjust_pollset, /* adjust_pollset */
cr_get_internals, /* get_internals */
cr_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
cr_recv, /* recv decrypted data */
cr_send, /* send data to encrypt */
NULL, /* get_channel_binding */