]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
rustls: tidy up
authorDaniel McCarney <daniel@binaryparadox.net>
Fri, 21 Mar 2025 18:43:07 +0000 (14:43 -0400)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 24 Mar 2025 22:45:02 +0000 (23:45 +0100)
Closes #16796

lib/vtls/rustls.c

index 963d23d1fcf7aab181f0361974eb87e3e4cea34c..6117ada0d255d905632ed62227b5944aa67e3248 100644 (file)
@@ -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
 #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 "cipher_suite.h"
-#include "rand.h"
 #include "x509asn1.h"
 
 struct rustls_ssl_backend_data
@@ -55,7 +52,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 +67,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 +96,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;
@@ -115,7 +121,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,
@@ -137,7 +143,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;
@@ -163,11 +169,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;
   }
@@ -193,7 +195,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;
@@ -233,10 +235,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;
@@ -279,7 +278,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;
@@ -305,7 +303,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;
 }
 
 /*
@@ -322,14 +320,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;
@@ -361,11 +356,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;
     }
@@ -419,7 +414,7 @@ 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)) {
       fclose(f);
@@ -437,8 +432,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;
@@ -524,246 +519,329 @@ add_ciphers:
 }
 
 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;
-    }
+  cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
+  if(!cipher_suites) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto cleanup;
+  }
 
-    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;
-    }
+  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_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;
-    }
+  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_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_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_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_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;
+  }
 
-    result = rustls_client_config_builder_new_custom(custom_provider,
+  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;
+
+  Curl_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,
+    Curl_dyn_uptr(&crl_contents),
+    Curl_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:
+  Curl_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;
     }
+  }
+
+  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;
+  }
 
-    verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+  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, DYN_CRLFILE_SIZE);
-      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
+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 rustls_connection *rconn = NULL;
+  struct rustls_client_config_builder *config_builder = NULL;
 
-    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);
+  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(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);
   }
 
-  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
@@ -788,9 +866,9 @@ cr_connect(struct Curl_cfilter *cf,
            struct Curl_easy *data, bool *done)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  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;
   bool wants_read;
@@ -835,8 +913,10 @@ cr_connect(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)
@@ -944,14 +1024,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);
@@ -984,6 +1063,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;
@@ -1013,7 +1093,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;
 
@@ -1031,7 +1111,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);
 }