From 3be94d84fcff2a154d6eca8e7807b5813587fb24 Mon Sep 17 00:00:00 2001 From: "Stephen M. Coakley" Date: Sun, 9 Jan 2022 15:59:30 -0600 Subject: rustls: add CURLOPT_CAINFO_BLOB support Add support for `CURLOPT_CAINFO_BLOB` `CURLOPT_PROXY_CAINFO_BLOB` to the rustls TLS backend. Multiple certificates in a single PEM string are supported just like OpenSSL does with this option. This is compatible at least with rustls-ffi 0.8+ which is our new minimum version anyway. I was able to build and run this on Windows, pulling trusted certs from the system and then add them to rustls by setting `CURLOPT_CAINFO_BLOB`. Handy! Closes #8255 --- docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 | 2 +- docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 | 2 +- lib/vtls/rustls.c | 34 ++++++++++++++++++++++++--- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 index 777b2e336..deae4ee06 100644 --- a/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 +++ b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.3 @@ -62,7 +62,7 @@ if(curl) { Added in 7.77.0. This option is supported by the BearSSL (since 7.79.0), mbedTLS (since 7.81.0), -OpenSSL, Secure Transport and Schannel backends. +rustls (since 7.82.0), OpenSSL, Secure Transport and Schannel backends. .SH RETURN VALUE Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or CURLE_OUT_OF_MEMORY if there was insufficient heap space. diff --git a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 index df6ebe16f..a85552e56 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 +++ b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.3 @@ -65,7 +65,7 @@ if(curl) { .SH AVAILABILITY Added in 7.77.0. -This option is supported by the OpenSSL, Secure +This option is supported by the rustls (since 7.82.0), OpenSSL, Secure Transport and Schannel backends. .SH RETURN VALUE Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 6dbb1ef3c..51230b196 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2021, Jacob Hoffman-Andrews, + * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, * * * This software is licensed as described in the file COPYING, which @@ -297,7 +297,11 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, { struct rustls_connection *rconn = backend->conn; struct rustls_client_config_builder *config_builder = NULL; - const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile); + struct rustls_root_cert_store *roots = NULL; + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const char *hostname = conn->host.name; char errorbuf[256]; @@ -328,6 +332,29 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, hostname = "example.invalid"; } } + else if(ca_info_blob) { + roots = rustls_root_cert_store_new(); + + /* Enable strict parsing only if verification isn't disabled. */ + result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, + ca_info_blob->len, verifypeer); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to parse trusted certificates from blob"); + rustls_root_cert_store_free(roots); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + + result = rustls_client_config_builder_use_roots(config_builder, roots); + rustls_root_cert_store_free(roots); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to load trusted certificates"); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } else if(ssl_cafile) { result = rustls_client_config_builder_load_roots_from_file( config_builder, ssl_cafile); @@ -550,7 +577,8 @@ static size_t cr_version(char *buffer, size_t size) const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, - SSLSUPP_TLS13_CIPHERSUITES, /* supports */ + SSLSUPP_CAINFO_BLOB | /* supports */ + SSLSUPP_TLS13_CIPHERSUITES, sizeof(struct ssl_backend_data), Curl_none_init, /* init */ -- cgit v1.2.1