Hold CA names as |CRYPTO_BUFFER|s.

This change converts the CA names that are parsed from a server's
CertificateRequest, as well as the CA names that are configured for
sending to clients in the same, to use |CRYPTO_BUFFER|.

The |X509_NAME|-based interfaces are turned into compatibility wrappers.

Change-Id: I95304ecc988ee39320499739a0866c7f8ff5ed98
Reviewed-on: https://boringssl-review.googlesource.com/13585
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 3131539..ef22e8f 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -3969,7 +3969,11 @@
   void (*info_callback)(const SSL *ssl, int type, int value);
 
   /* what we put in client cert requests */
-  STACK_OF(X509_NAME) *client_CA;
+  STACK_OF(CRYPTO_BUFFER) *client_CA;
+
+  /* cached_x509_client_CA is a cache of parsed versions of the elements of
+   * |client_CA|. */
+  STACK_OF(X509_NAME) *cached_x509_client_CA;
 
 
   /* Default values to use in SSL structures follow (these are copied by
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index c4f5e8e..fcc65bc 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -1403,22 +1403,24 @@
   }
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
-  STACK_OF(X509_NAME) *ca_sk = ssl_parse_client_CA_list(ssl, &alert, &cbs);
-  if (ca_sk == NULL) {
+  STACK_OF(CRYPTO_BUFFER) *ca_names =
+      ssl_parse_client_CA_list(ssl, &alert, &cbs);
+  if (ca_names == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return -1;
   }
 
   if (CBS_len(&cbs) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    sk_X509_NAME_pop_free(ca_sk, X509_NAME_free);
+    sk_CRYPTO_BUFFER_pop_free(ca_names, CRYPTO_BUFFER_free);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return -1;
   }
 
   hs->cert_request = 1;
-  sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
-  hs->ca_names = ca_sk;
+  sk_CRYPTO_BUFFER_pop_free(hs->ca_names, CRYPTO_BUFFER_free);
+  hs->ca_names = ca_names;
+  ssl->ctx->x509_method->hs_flush_cached_ca_names(hs);
   return 1;
 }
 
diff --git a/ssl/internal.h b/ssl/internal.h
index a6dfad5..db526a8 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -844,9 +844,9 @@
 
 /* ssl_parse_client_CA_list parses a CA list from |cbs| in the format used by a
  * TLS CertificateRequest message. On success, it returns a newly-allocated
- * |X509_NAME| list and advances |cbs|. Otherwise, it returns NULL and sets
+ * |CRYPTO_BUFFER| list and advances |cbs|. Otherwise, it returns NULL and sets
  * |*out_alert| to an alert to send to the peer. */
-STACK_OF(X509_NAME) *
+STACK_OF(CRYPTO_BUFFER) *
     ssl_parse_client_CA_list(SSL *ssl, uint8_t *out_alert, CBS *cbs);
 
 /* ssl_add_client_CA_list adds the configured CA list to |cbb| in the format
@@ -1037,7 +1037,11 @@
 
   /* ca_names, on the client, contains the list of CAs received in a
    * CertificateRequest message. */
-  STACK_OF(X509_NAME) *ca_names;
+  STACK_OF(CRYPTO_BUFFER) *ca_names;
+
+  /* cached_x509_ca_names contains a cache of parsed versions of the elements
+   * of |ca_names|. */
+  STACK_OF(X509_NAME) *cached_x509_ca_names;
 
   /* certificate_types, on the client, contains the set of certificate types
    * received in a CertificateRequest message. */
@@ -1460,6 +1464,13 @@
   int (*session_dup)(SSL_SESSION *new_session, const SSL_SESSION *session);
   /* session_clear frees any X509-related state from |session|. */
   void (*session_clear)(SSL_SESSION *session);
+
+  /* hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|. */
+  void (*hs_flush_cached_ca_names)(SSL_HANDSHAKE *hs);
+  /* ssl_flush_cached_client_CA drops any cached |X509_NAME|s from |ssl|. */
+  void (*ssl_flush_cached_client_CA)(SSL *ssl);
+  /* ssl_ctx_flush_cached_client_CA drops any cached |X509_NAME|s from |ctx|. */
+  void (*ssl_ctx_flush_cached_client_CA)(SSL_CTX *ssl);
 };
 
 /* ssl_noop_x509_method is implements the |ssl_x509_method_st| functions by
@@ -1832,7 +1843,11 @@
   CRYPTO_EX_DATA ex_data;
 
   /* for server side, keep the list of CA_dn we can use */
-  STACK_OF(X509_NAME) *client_CA;
+  STACK_OF(CRYPTO_BUFFER) *client_CA;
+
+  /* cached_x509_client_CA is a cache of parsed versions of the elements of
+   * |client_CA|. */
+  STACK_OF(X509_NAME) *cached_x509_client_CA;
 
   uint32_t options; /* protocol behaviour */
   uint32_t mode;    /* API behaviour */
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 7fd09c6..6b03030 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -173,7 +173,8 @@
   OPENSSL_free(hs->peer_key);
   OPENSSL_free(hs->server_params);
   OPENSSL_free(hs->peer_psk_identity_hint);
-  sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
+  sk_CRYPTO_BUFFER_pop_free(hs->ca_names, CRYPTO_BUFFER_free);
+  hs->ssl->ctx->x509_method->hs_flush_cached_ca_names(hs);
   OPENSSL_free(hs->certificate_types);
 
   if (hs->key_block != NULL) {
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index c60c6fa..c334ea6 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -406,96 +406,6 @@
   return ret;
 }
 
-static void set_client_CA_list(STACK_OF(X509_NAME) **ca_list,
-                               STACK_OF(X509_NAME) *name_list) {
-  sk_X509_NAME_pop_free(*ca_list, X509_NAME_free);
-  *ca_list = name_list;
-}
-
-STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *list) {
-  STACK_OF(X509_NAME) *ret = sk_X509_NAME_new_null();
-  if (ret == NULL) {
-    return NULL;
-  }
-
-  for (size_t i = 0; i < sk_X509_NAME_num(list); i++) {
-      X509_NAME *name = X509_NAME_dup(sk_X509_NAME_value(list, i));
-    if (name == NULL || !sk_X509_NAME_push(ret, name)) {
-      X509_NAME_free(name);
-      sk_X509_NAME_pop_free(ret, X509_NAME_free);
-      return NULL;
-    }
-  }
-
-  return ret;
-}
-
-void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list) {
-  set_client_CA_list(&ssl->client_CA, name_list);
-}
-
-void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list) {
-  set_client_CA_list(&ctx->client_CA, name_list);
-}
-
-STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) {
-  return ctx->client_CA;
-}
-
-STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl) {
-  /* For historical reasons, this function is used both to query configuration
-   * state on a server as well as handshake state on a client. However, whether
-   * |ssl| is a client or server is not known until explicitly configured with
-   * |SSL_set_connect_state|. If |handshake_func| is NULL, |ssl| is in an
-   * indeterminate mode and |ssl->server| is unset. */
-  if (ssl->handshake_func != NULL && !ssl->server) {
-    if (ssl->s3->hs != NULL) {
-      return ssl->s3->hs->ca_names;
-    }
-
-    return NULL;
-  }
-
-  if (ssl->client_CA != NULL) {
-    return ssl->client_CA;
-  }
-  return ssl->ctx->client_CA;
-}
-
-static int add_client_CA(STACK_OF(X509_NAME) **sk, X509 *x509) {
-  X509_NAME *name;
-
-  if (x509 == NULL) {
-    return 0;
-  }
-  if (*sk == NULL) {
-    *sk = sk_X509_NAME_new_null();
-    if (*sk == NULL) {
-      return 0;
-    }
-  }
-
-  name = X509_NAME_dup(X509_get_subject_name(x509));
-  if (name == NULL) {
-    return 0;
-  }
-
-  if (!sk_X509_NAME_push(*sk, name)) {
-    X509_NAME_free(name);
-    return 0;
-  }
-
-  return 1;
-}
-
-int SSL_add_client_CA(SSL *ssl, X509 *x509) {
-  return add_client_CA(&ssl->client_CA, x509);
-}
-
-int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x509) {
-  return add_client_CA(&ctx->client_CA, x509);
-}
-
 int ssl_has_certificate(const SSL *ssl) {
   return ssl->cert->chain != NULL &&
          sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0) != NULL &&
@@ -779,14 +689,11 @@
   return 0;
 }
 
-static int ca_dn_cmp(const X509_NAME **a, const X509_NAME **b) {
-  return X509_NAME_cmp(*a, *b);
-}
-
-STACK_OF(X509_NAME) *
+STACK_OF(CRYPTO_BUFFER) *
     ssl_parse_client_CA_list(SSL *ssl, uint8_t *out_alert, CBS *cbs) {
-  STACK_OF(X509_NAME) *ret = sk_X509_NAME_new(ca_dn_cmp);
-  X509_NAME *name = NULL;
+  CRYPTO_BUFFER_POOL *const pool = ssl->ctx->pool;
+
+  STACK_OF(CRYPTO_BUFFER) *ret = sk_CRYPTO_BUFFER_new_null();
   if (ret == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
@@ -808,29 +715,21 @@
       goto err;
     }
 
-    const uint8_t *ptr = CBS_data(&distinguished_name);
-    /* A u16 length cannot overflow a long. */
-    name = d2i_X509_NAME(NULL, &ptr, (long)CBS_len(&distinguished_name));
-    if (name == NULL ||
-        ptr != CBS_data(&distinguished_name) + CBS_len(&distinguished_name)) {
-      *out_alert = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto err;
-    }
-
-    if (!sk_X509_NAME_push(ret, name)) {
+    CRYPTO_BUFFER *buffer =
+        CRYPTO_BUFFER_new_from_CBS(&distinguished_name, pool);
+    if (buffer == NULL ||
+        !sk_CRYPTO_BUFFER_push(ret, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
       *out_alert = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
     }
-    name = NULL;
   }
 
   return ret;
 
 err:
-  X509_NAME_free(name);
-  sk_X509_NAME_pop_free(ret, X509_NAME_free);
+  sk_CRYPTO_BUFFER_pop_free(ret, CRYPTO_BUFFER_free);
   return NULL;
 }
 
@@ -840,21 +739,20 @@
     return 0;
   }
 
-  STACK_OF(X509_NAME) *sk = SSL_get_client_CA_list(ssl);
-  if (sk == NULL) {
+  STACK_OF(CRYPTO_BUFFER) *names = ssl->client_CA;
+  if (names == NULL) {
+    names = ssl->ctx->client_CA;
+  }
+  if (names == NULL) {
     return CBB_flush(cbb);
   }
 
-  for (size_t i = 0; i < sk_X509_NAME_num(sk); i++) {
-    X509_NAME *name = sk_X509_NAME_value(sk, i);
-    int len = i2d_X509_NAME(name, NULL);
-    if (len < 0) {
-      return 0;
-    }
-    uint8_t *ptr;
+  for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(names); i++) {
+    const CRYPTO_BUFFER *name = sk_CRYPTO_BUFFER_value(names, i);
+
     if (!CBB_add_u16_length_prefixed(&child, &name_cbb) ||
-        !CBB_add_space(&name_cbb, &ptr, (size_t)len) ||
-        (len > 0 && i2d_X509_NAME(name, &ptr) < 0)) {
+        !CBB_add_bytes(&name_cbb, CRYPTO_BUFFER_data(name),
+                       CRYPTO_BUFFER_len(name))) {
       return 0;
     }
   }
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index e37f9f9..cd9d4c4 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -289,7 +289,7 @@
     goto err;
   }
 
-  ret->client_CA = sk_X509_NAME_new_null();
+  ret->client_CA = sk_CRYPTO_BUFFER_new_null();
   if (ret->client_CA == NULL) {
     goto err;
   }
@@ -358,7 +358,8 @@
                                    SSL_CUSTOM_EXTENSION_free);
   sk_SSL_CUSTOM_EXTENSION_pop_free(ctx->server_custom_extensions,
                                    SSL_CUSTOM_EXTENSION_free);
-  sk_X509_NAME_pop_free(ctx->client_CA, X509_NAME_free);
+  sk_CRYPTO_BUFFER_pop_free(ctx->client_CA, CRYPTO_BUFFER_free);
+  ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx);
   sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles);
   OPENSSL_free(ctx->psk_identity_hint);
   OPENSSL_free(ctx->supported_group_list);
@@ -503,7 +504,8 @@
   OPENSSL_free(ssl->alpn_client_proto_list);
   EVP_PKEY_free(ssl->tlsext_channel_id_private);
   OPENSSL_free(ssl->psk_identity_hint);
-  sk_X509_NAME_pop_free(ssl->client_CA, X509_NAME_free);
+  sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free);
+  ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
   sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles);
 
   if (ssl->method != NULL) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 97bcab3..4180463 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -990,12 +990,25 @@
   bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
   ASSERT_TRUE(ssl);
 
-  STACK_OF(X509_NAME) *stack = sk_X509_NAME_new_null();
-  ASSERT_TRUE(stack);
-  // |SSL_set_client_CA_list| takes ownership.
-  SSL_set_client_CA_list(ssl.get(), stack);
+  bssl::UniquePtr<X509_NAME> name(X509_NAME_new());
+  ASSERT_TRUE(name);
 
-  EXPECT_EQ(stack, SSL_get_client_CA_list(ssl.get()));
+  bssl::UniquePtr<X509_NAME> name_dup(X509_NAME_dup(name.get()));
+  ASSERT_TRUE(name_dup);
+
+  bssl::UniquePtr<STACK_OF(X509_NAME)> stack(sk_X509_NAME_new_null());
+  ASSERT_TRUE(stack);
+
+  ASSERT_TRUE(sk_X509_NAME_push(stack.get(), name_dup.get()));
+  name_dup.release();
+
+  // |SSL_set_client_CA_list| takes ownership.
+  SSL_set_client_CA_list(ssl.get(), stack.release());
+
+  STACK_OF(X509_NAME) *result = SSL_get_client_CA_list(ssl.get());
+  ASSERT_TRUE(result);
+  ASSERT_EQ(1u, sk_X509_NAME_num(result));
+  EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(result, 0), name.get()));
 }
 
 static void AppendSession(SSL_SESSION *session, void *arg) {
diff --git a/ssl/ssl_x509.c b/ssl/ssl_x509.c
index 2955c21..8a132ff 100644
--- a/ssl/ssl_x509.c
+++ b/ssl/ssl_x509.c
@@ -152,6 +152,7 @@
 #include <openssl/x509_vfy.h>
 
 #include "internal.h"
+#include "../crypto/internal.h"
 
 
 X509 *SSL_get_peer_certificate(const SSL *ssl) {
@@ -410,6 +411,21 @@
   session->x509_chain_without_leaf = NULL;
 }
 
+static void ssl_crypto_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {
+  sk_X509_NAME_pop_free(hs->cached_x509_ca_names, X509_NAME_free);
+  hs->cached_x509_ca_names = NULL;
+}
+
+static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL *ssl) {
+  sk_X509_NAME_pop_free(ssl->cached_x509_client_CA, X509_NAME_free);
+  ssl->cached_x509_client_CA = NULL;
+}
+
+static void ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {
+  sk_X509_NAME_pop_free(ctx->cached_x509_client_CA, X509_NAME_free);
+  ctx->cached_x509_client_CA = NULL;
+}
+
 const SSL_X509_METHOD ssl_crypto_x509_method = {
   ssl_crypto_x509_clear,
   ssl_crypto_x509_flush_cached_chain,
@@ -417,6 +433,9 @@
   ssl_crypto_x509_session_cache_objects,
   ssl_crypto_x509_session_dup,
   ssl_crypto_x509_session_clear,
+  ssl_crypto_x509_hs_flush_cached_ca_names,
+  ssl_crypto_x509_ssl_flush_cached_client_CA,
+  ssl_crypto_x509_ssl_ctx_flush_cached_client_CA,
 };
 
 /* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
@@ -491,7 +510,10 @@
 }
 
 X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
-  return ssl_cert_get0_leaf(ctx->cert);
+  CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock);
+  X509 *ret = ssl_cert_get0_leaf(ctx->cert);
+  CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock);
+  return ret;
 }
 
 /* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
@@ -752,7 +774,11 @@
 }
 
 int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
-  if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+  CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock);
+  const int ret = ssl_cert_cache_chain_certs(ctx->cert);
+  CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock);
+
+  if (!ret) {
     *out_chain = NULL;
     return 0;
   }
@@ -813,3 +839,179 @@
   *pp = CBS_data(&cbs);
   return ret;
 }
+
+STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *list) {
+  return sk_X509_NAME_deep_copy(list, X509_NAME_dup, X509_NAME_free);
+}
+
+static void set_client_CA_list(STACK_OF(CRYPTO_BUFFER) **ca_list,
+                               const STACK_OF(X509_NAME) *name_list,
+                               CRYPTO_BUFFER_POOL *pool) {
+  STACK_OF(CRYPTO_BUFFER) *buffers = sk_CRYPTO_BUFFER_new_null();
+  if (buffers == NULL) {
+    return;
+  }
+
+  for (size_t i = 0; i < sk_X509_NAME_num(name_list); i++) {
+    X509_NAME *name = sk_X509_NAME_value(name_list, i);
+    uint8_t *outp = NULL;
+    int len = i2d_X509_NAME(name, &outp);
+    if (len < 0) {
+      goto err;
+    }
+
+    CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(outp, len, pool);
+    OPENSSL_free(outp);
+    if (buffer == NULL ||
+        !sk_CRYPTO_BUFFER_push(buffers, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      goto err;
+    }
+  }
+
+  sk_CRYPTO_BUFFER_pop_free(*ca_list, CRYPTO_BUFFER_free);
+  *ca_list = buffers;
+  return;
+
+err:
+  sk_CRYPTO_BUFFER_pop_free(buffers, CRYPTO_BUFFER_free);
+}
+
+void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list) {
+  ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
+  set_client_CA_list(&ssl->client_CA, name_list, ssl->ctx->pool);
+  sk_X509_NAME_pop_free(name_list, X509_NAME_free);
+}
+
+void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list) {
+  ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx);
+  set_client_CA_list(&ctx->client_CA, name_list, ctx->pool);
+  sk_X509_NAME_pop_free(name_list, X509_NAME_free);
+}
+
+static STACK_OF(X509_NAME) *
+    buffer_names_to_x509(const STACK_OF(CRYPTO_BUFFER) *names,
+                         STACK_OF(X509_NAME) **cached) {
+  if (names == NULL) {
+    return NULL;
+  }
+
+  if (*cached != NULL) {
+    return *cached;
+  }
+
+  STACK_OF(X509_NAME) *new_cache = sk_X509_NAME_new_null();
+  if (new_cache == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+
+  for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(names); i++) {
+    const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(names, i);
+    const uint8_t *inp = CRYPTO_BUFFER_data(buffer);
+    X509_NAME *name = d2i_X509_NAME(NULL, &inp, CRYPTO_BUFFER_len(buffer));
+    if (name == NULL ||
+        inp != CRYPTO_BUFFER_data(buffer) + CRYPTO_BUFFER_len(buffer) ||
+        !sk_X509_NAME_push(new_cache, name)) {
+      X509_NAME_free(name);
+      goto err;
+    }
+  }
+
+  *cached = new_cache;
+  return new_cache;
+
+err:
+  sk_X509_NAME_pop_free(new_cache, X509_NAME_free);
+  return NULL;
+}
+
+STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl) {
+  /* For historical reasons, this function is used both to query configuration
+   * state on a server as well as handshake state on a client. However, whether
+   * |ssl| is a client or server is not known until explicitly configured with
+   * |SSL_set_connect_state|. If |handshake_func| is NULL, |ssl| is in an
+   * indeterminate mode and |ssl->server| is unset. */
+  if (ssl->handshake_func != NULL && !ssl->server) {
+    if (ssl->s3->hs != NULL) {
+      return buffer_names_to_x509(ssl->s3->hs->ca_names,
+                                  &ssl->s3->hs->cached_x509_ca_names);
+    }
+
+    return NULL;
+  }
+
+  if (ssl->client_CA != NULL) {
+    return buffer_names_to_x509(
+        ssl->client_CA, (STACK_OF(X509_NAME) **)&ssl->cached_x509_client_CA);
+  }
+  return buffer_names_to_x509(ssl->ctx->client_CA,
+                              &ssl->ctx->cached_x509_client_CA);
+}
+
+STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) {
+  CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock);
+  STACK_OF(X509_NAME) *ret = buffer_names_to_x509(
+      ctx->client_CA, (STACK_OF(X509_NAME) **)&ctx->cached_x509_client_CA);
+  CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock);
+  return ret;
+}
+
+static int add_client_CA(STACK_OF(CRYPTO_BUFFER) **names, X509 *x509,
+                         CRYPTO_BUFFER_POOL *pool) {
+  if (x509 == NULL) {
+    return 0;
+  }
+
+  uint8_t *outp = NULL;
+  int len = i2d_X509_NAME(X509_get_subject_name(x509), &outp);
+  if (len < 0) {
+    return 0;
+  }
+
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(outp, len, pool);
+  OPENSSL_free(outp);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  int alloced = 0;
+  if (*names == NULL) {
+    *names = sk_CRYPTO_BUFFER_new_null();
+    alloced = 1;
+
+    if (*names == NULL) {
+      CRYPTO_BUFFER_free(buffer);
+      return 0;
+    }
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(*names, buffer)) {
+    CRYPTO_BUFFER_free(buffer);
+    if (alloced) {
+      sk_CRYPTO_BUFFER_pop_free(*names, CRYPTO_BUFFER_free);
+      *names = NULL;
+    }
+    return 0;
+  }
+
+  return 1;
+}
+
+int SSL_add_client_CA(SSL *ssl, X509 *x509) {
+  if (!add_client_CA(&ssl->client_CA, x509, ssl->ctx->pool)) {
+    return 0;
+  }
+
+  ssl_crypto_x509_ssl_flush_cached_client_CA(ssl);
+  return 1;
+}
+
+int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x509) {
+  if (!add_client_CA(&ctx->client_CA, x509, ctx->pool)) {
+    return 0;
+  }
+
+  ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(ctx);
+  return 1;
+}
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index 8e994e5..ac70348 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -402,8 +402,9 @@
   }
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
-  STACK_OF(X509_NAME) *ca_sk = ssl_parse_client_CA_list(ssl, &alert, &cbs);
-  if (ca_sk == NULL) {
+  STACK_OF(CRYPTO_BUFFER) *ca_names =
+      ssl_parse_client_CA_list(ssl, &alert, &cbs);
+  if (ca_names == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
@@ -413,14 +414,15 @@
   if (!CBS_get_u16_length_prefixed(&cbs, &extensions) ||
       CBS_len(&cbs) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    sk_X509_NAME_pop_free(ca_sk, X509_NAME_free);
+    sk_CRYPTO_BUFFER_pop_free(ca_names, CRYPTO_BUFFER_free);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
   }
 
   hs->cert_request = 1;
-  sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
-  hs->ca_names = ca_sk;
+  sk_CRYPTO_BUFFER_pop_free(hs->ca_names, CRYPTO_BUFFER_free);
+  hs->ca_names = ca_names;
+  ssl->ctx->x509_method->hs_flush_cached_ca_names(hs);
 
   if (!ssl_hash_current_message(hs)) {
     return ssl_hs_error;
diff --git a/ssl/tls_method.c b/ssl/tls_method.c
index eaad2ca..d7e4701 100644
--- a/ssl/tls_method.c
+++ b/ssl/tls_method.c
@@ -270,6 +270,10 @@
 }
 static void ssl_noop_x509_session_clear(SSL_SESSION *session) {}
 
+static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {}
+static void ssl_noop_x509_ssl_flush_cached_client_CA(SSL *ssl) {}
+static void ssl_noop_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {}
+
 const SSL_X509_METHOD ssl_noop_x509_method = {
   ssl_noop_x509_clear,
   ssl_noop_x509_flush_cached_chain,
@@ -277,4 +281,7 @@
   ssl_noop_x509_session_cache_objects,
   ssl_noop_x509_session_dup,
   ssl_noop_x509_session_clear,
+  ssl_noop_x509_hs_flush_cached_ca_names,
+  ssl_noop_x509_ssl_flush_cached_client_CA,
+  ssl_noop_x509_ssl_ctx_flush_cached_client_CA,
 };