Add |X509_METHOD| and, using it, move many functions to ssl_x509.c.

Change-Id: I266af0c2bdcebcc1dd1026f816b9ef6ece5a592f
Reviewed-on: https://boringssl-review.googlesource.com/13581
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/ssl/dtls_method.c b/ssl/dtls_method.c
index 7a35b39..6084789 100644
--- a/ssl/dtls_method.c
+++ b/ssl/dtls_method.c
@@ -165,6 +165,7 @@
   static const SSL_METHOD kMethod = {
       0,
       &kDTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -175,6 +176,7 @@
   static const SSL_METHOD kMethod = {
       DTLS1_2_VERSION,
       &kDTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -183,6 +185,7 @@
   static const SSL_METHOD kMethod = {
       DTLS1_VERSION,
       &kDTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
diff --git a/ssl/internal.h b/ssl/internal.h
index b3f3b0f..a53bd07 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1256,6 +1256,10 @@
    * operations. */
   const SSL_PRIVATE_KEY_METHOD *key_method;
 
+  /* x509_method contains pointers to functions that might deal with |X509|
+   * compatibility, or might be a no-op, depending on the application. */
+  const SSL_X509_METHOD *x509_method;
+
   DH *dh_tmp;
   DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
 
@@ -1287,6 +1291,9 @@
   /* method is the underlying SSL_PROTOCOL_METHOD that initializes the
    * SSL_CTX. */
   const SSL_PROTOCOL_METHOD *method;
+  /* x509_method contains pointers to functions that might deal with |X509|
+   * compatibility, or might be a no-op, depending on the application. */
+  const SSL_X509_METHOD *x509_method;
 };
 
 /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
@@ -1368,6 +1375,21 @@
   int (*set_write_state)(SSL *ssl, SSL_AEAD_CTX *aead_ctx);
 };
 
+struct ssl_x509_method_st {
+  /* cert_clear frees and NULLs all X509-related state. */
+  void (*cert_clear)(CERT *cert);
+  /* cert_flush_cached_chain drops any cached |X509|-based certificate chain
+   * from |cert|. */
+  void (*cert_flush_cached_chain)(CERT *cert);
+  /* cert_flush_cached_chain drops any cached |X509|-based leaf certificate
+   * from |cert|. */
+  void (*cert_flush_cached_leaf)(CERT *cert);
+};
+
+/* ssl_crypto_x509_method provides the |ssl_x509_method_st| functions using
+ * crypto/x509. */
+extern const struct ssl_x509_method_st ssl_crypto_x509_method;
+
 /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
  * of a mess of functions, but hell, think of it as an opaque structure. */
 typedef struct ssl3_enc_method {
@@ -1855,13 +1877,11 @@
 #define SSL_KEY_UPDATE_NOT_REQUESTED 0
 #define SSL_KEY_UPDATE_REQUESTED 1
 
-CERT *ssl_cert_new(void);
+CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method);
 CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
-CRYPTO_BUFFER *x509_to_buffer(X509 *x509);
-void ssl_cert_flush_cached_x509_leaf(CERT *cert);
-int ssl_cert_cache_leaf_cert(CERT *cert);
+int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
 /* ssl_compare_public_and_private_key returns one if |pubkey| is the public
  * counterpart to |privkey|. Otherwise it returns zero and pushes a helpful
  * message on the error queue. */
@@ -1931,7 +1951,7 @@
     const SSL *ssl);
 
 int ssl_verify_cert_chain(SSL *ssl, long *out_verify_result,
-                          STACK_OF(X509) * cert_chain);
+                          STACK_OF(X509) *cert_chain);
 void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
 
 int ssl_verify_alarm_type(long type);
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 5c5fc54..1a79327 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -141,13 +141,14 @@
   return 0;
 }
 
-CERT *ssl_cert_new(void) {
+CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method) {
   CERT *ret = OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return NULL;
   }
   OPENSSL_memset(ret, 0, sizeof(CERT));
+  ret->x509_method = x509_method;
 
   return ret;
 }
@@ -174,6 +175,7 @@
   }
 
   ret->key_method = cert->key_method;
+  ret->x509_method = cert->x509_method;
 
   if (cert->dh_tmp != NULL) {
     ret->dh_tmp = DHparams_dup(cert->dh_tmp);
@@ -208,27 +210,13 @@
   return NULL;
 }
 
-void ssl_cert_flush_cached_x509_leaf(CERT *cert) {
-  X509_free(cert->x509_leaf);
-  cert->x509_leaf = NULL;
-}
-
-static void ssl_cert_flush_cached_x509_chain(CERT *cert) {
-  sk_X509_pop_free(cert->x509_chain, X509_free);
-  cert->x509_chain = NULL;
-}
-
 /* Free up and clear all certificates and chains */
 void ssl_cert_clear_certs(CERT *cert) {
   if (cert == NULL) {
     return;
   }
 
-  ssl_cert_flush_cached_x509_leaf(cert);
-  ssl_cert_flush_cached_x509_chain(cert);
-
-  X509_free(cert->x509_stash);
-  cert->x509_stash = NULL;
+  cert->x509_method->cert_clear(cert);
 
   sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
   cert->chain = NULL;
@@ -251,151 +239,6 @@
   OPENSSL_free(c);
 }
 
-/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
-static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
-  STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
-  if (chain == NULL) {
-    return NULL;
-  }
-
-  if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
-    sk_CRYPTO_BUFFER_free(chain);
-    return NULL;
-  }
-
-  return chain;
-}
-
-/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
- * contents of |x509|. */
-CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
-  uint8_t *buf = NULL;
-  int cert_len = i2d_X509(x509, &buf);
-  if (cert_len <= 0) {
-    return 0;
-  }
-
-  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
-  OPENSSL_free(buf);
-
-  return buffer;
-}
-
-/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
- * forms of elements of |chain|. It returns one on success or zero on error, in
- * which case no change to |cert->chain| is made. It preverses the existing
- * leaf from |cert->chain|, if any. */
-static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
-  STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
-
-  if (cert->chain != NULL) {
-    new_chain = sk_CRYPTO_BUFFER_new_null();
-    if (new_chain == NULL) {
-      return 0;
-    }
-
-    CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
-    if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
-      goto err;
-    }
-    /* |leaf| might be NULL if it's a “leafless” chain. */
-    if (leaf != NULL) {
-      CRYPTO_BUFFER_up_ref(leaf);
-    }
-  }
-
-  for (size_t i = 0; i < sk_X509_num(chain); i++) {
-    if (new_chain == NULL) {
-      new_chain = new_leafless_chain();
-      if (new_chain == NULL) {
-        goto err;
-      }
-    }
-
-    CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
-    if (buffer == NULL ||
-        !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
-      CRYPTO_BUFFER_free(buffer);
-      goto err;
-    }
-  }
-
-  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
-  cert->chain = new_chain;
-
-  return 1;
-
-err:
-  sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
-  return 0;
-}
-
-static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
-  if (!ssl_cert_set_chain(cert, chain)) {
-    return 0;
-  }
-
-  sk_X509_pop_free(chain, X509_free);
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
-static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
-  if (!ssl_cert_set_chain(cert, chain)) {
-    return 0;
-  }
-
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
-static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
-  CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
-  if (buffer == NULL) {
-    return 0;
-  }
-
-  if (cert->chain != NULL) {
-    if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
-      CRYPTO_BUFFER_free(buffer);
-      return 0;
-    }
-
-    return 1;
-  }
-
-  cert->chain = new_leafless_chain();
-  if (cert->chain == NULL ||
-      !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
-    CRYPTO_BUFFER_free(buffer);
-    sk_CRYPTO_BUFFER_free(cert->chain);
-    cert->chain = NULL;
-    return 0;
-  }
-
-  return 1;
-}
-
-static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
-  if (!ssl_cert_append_cert(cert, x509)) {
-    return 0;
-  }
-
-  X509_free(cert->x509_stash);
-  cert->x509_stash = x509;
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
-static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
-  if (!ssl_cert_append_cert(cert, x509)) {
-    return 0;
-  }
-
-  ssl_cert_flush_cached_x509_chain(cert);
-  return 1;
-}
-
 static void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg),
                                  void *arg) {
   c->cert_cb = cb;
@@ -653,49 +496,6 @@
   return 0;
 }
 
-int ssl_auto_chain_if_needed(SSL *ssl) {
-  /* Only build a chain if there are no intermediates configured and the feature
-   * isn't disabled. */
-  if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
-      !ssl_has_certificate(ssl) ||
-      ssl->cert->chain == NULL ||
-      sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
-    return 1;
-  }
-
-  X509 *leaf =
-      X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
-  if (!leaf) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
-    return 0;
-  }
-
-  X509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
-    X509_free(leaf);
-    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
-    return 0;
-  }
-
-  /* Attempt to build a chain, ignoring the result. */
-  X509_verify_cert(&ctx);
-  X509_free(leaf);
-  ERR_clear_error();
-
-  /* Remove the leaf from the generated chain. */
-  X509_free(sk_X509_shift(ctx.chain));
-
-  const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
-  X509_STORE_CTX_cleanup(&ctx);
-  if (!ok) {
-    return 0;
-  }
-
-  ssl_cert_flush_cached_x509_chain(ssl->cert);
-
-  return 1;
-}
-
 /* ssl_cert_skip_to_spki parses a DER-encoded, X.509 certificate from |in| and
  * positions |*out_tbs_cert| to cover the TBSCertificate, starting at the
  * subjectPublicKeyInfo. */
@@ -987,54 +787,6 @@
   return set_cert_store(&ssl->cert->verify_store, store, 1);
 }
 
-int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
-  return ssl_cert_set0_chain(ctx->cert, chain);
-}
-
-int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
-  return ssl_cert_set1_chain(ctx->cert, chain);
-}
-
-int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
-  return ssl_cert_set0_chain(ssl->cert, chain);
-}
-
-int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
-  return ssl_cert_set1_chain(ssl->cert, chain);
-}
-
-int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  return ssl_cert_add0_chain_cert(ctx->cert, x509);
-}
-
-int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  return ssl_cert_add1_chain_cert(ctx->cert, x509);
-}
-
-int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
-  return SSL_CTX_add0_chain_cert(ctx, x509);
-}
-
-int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
-  return ssl_cert_add0_chain_cert(ssl->cert, x509);
-}
-
-int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
-  return ssl_cert_add1_chain_cert(ssl->cert, x509);
-}
-
-int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
-  return SSL_CTX_set0_chain(ctx, NULL);
-}
-
-int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) {
-  return SSL_CTX_clear_chain_certs(ctx);
-}
-
-int SSL_clear_chain_certs(SSL *ssl) {
-  return SSL_set0_chain(ssl, NULL);
-}
-
 void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
                          void *arg) {
   ssl_cert_set_cert_cb(ctx->cert, cb, arg);
@@ -1044,80 +796,6 @@
   ssl_cert_set_cert_cb(ssl->cert, cb, arg);
 }
 
-/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
- * first element of |cert->chain|. */
-int ssl_cert_cache_leaf_cert(CERT *cert) {
-  if (cert->x509_leaf != NULL ||
-      cert->chain == NULL) {
-    return 1;
-  }
-
-  CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
-  if (!leaf) {
-    return 1;
-  }
-
-  cert->x509_leaf = X509_parse_from_buffer(leaf);
-  return cert->x509_leaf != NULL;
-}
-
-/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
- * |cert->chain|. */
-static int ssl_cert_cache_chain_certs(CERT *cert) {
-  if (cert->x509_chain != NULL ||
-      cert->chain == NULL ||
-      sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
-    return 1;
-  }
-
-  STACK_OF(X509) *chain = sk_X509_new_null();
-  if (chain == NULL) {
-    return 0;
-  }
-
-  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
-    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
-    X509 *x509 = X509_parse_from_buffer(buffer);
-    if (x509 == NULL ||
-        !sk_X509_push(chain, x509)) {
-      X509_free(x509);
-      goto err;
-    }
-  }
-
-  cert->x509_chain = chain;
-  return 1;
-
-err:
-  sk_X509_pop_free(chain, X509_free);
-  return 0;
-}
-
-int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
-  if (!ssl_cert_cache_chain_certs(ctx->cert)) {
-    *out_chain = NULL;
-    return 0;
-  }
-
-  *out_chain = ctx->cert->x509_chain;
-  return 1;
-}
-
-int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx,
-                                  STACK_OF(X509) **out_chain) {
-  return SSL_CTX_get0_chain_certs(ctx, out_chain);
-}
-
-int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
-  if (!ssl_cert_cache_chain_certs(ssl->cert)) {
-    *out_chain = NULL;
-    return 0;
-  }
-
-  *out_chain = ssl->cert->x509_chain;
-  return 1;
-}
-
 int ssl_check_leaf_certificate(SSL *ssl, EVP_PKEY *pkey,
                                const CRYPTO_BUFFER *leaf) {
   assert(ssl3_protocol_version(ssl) < TLS1_3_VERSION);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 96ea64b..7fc7233 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -248,6 +248,7 @@
   OPENSSL_memset(ret, 0, sizeof(SSL_CTX));
 
   ret->method = method->method;
+  ret->x509_method = method->x509_method;
 
   CRYPTO_MUTEX_init(&ret->lock);
 
@@ -261,7 +262,7 @@
 
   ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT;
   ret->verify_mode = SSL_VERIFY_NONE;
-  ret->cert = ssl_cert_new();
+  ret->cert = ssl_cert_new(method->x509_method);
   if (ret->cert == NULL) {
     goto err;
   }
@@ -2058,6 +2059,12 @@
     return ssl->ctx;
   }
 
+  /* One cannot change the X.509 callbacks during a connection. */
+  if (ssl->ctx->x509_method != ctx->x509_method) {
+    assert(0);
+    return NULL;
+  }
+
   if (ctx == NULL) {
     ctx = ssl->initial_ctx;
   }
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 6ad2b71..f3e8bf3 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -70,29 +70,12 @@
 #include "internal.h"
 
 
-static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
 static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey);
 
 static int is_key_type_supported(int key_type) {
   return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC;
 }
 
-int SSL_use_certificate(SSL *ssl, X509 *x) {
-  if (x == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
-    return 0;
-  }
-
-  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
-  if (buffer == NULL) {
-    return 0;
-  }
-
-  const int ok = ssl_set_cert(ssl->cert, buffer);
-  CRYPTO_BUFFER_free(buffer);
-  return ok;
-}
-
 int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
   CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
   if (buffer == NULL) {
@@ -179,23 +162,7 @@
   return ret;
 }
 
-int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
-  if (x == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
-    return 0;
-  }
-
-  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
-  if (buffer == NULL) {
-    return 0;
-  }
-
-  const int ok = ssl_set_cert(ctx->cert, buffer);
-  CRYPTO_BUFFER_free(buffer);
-  return ok;
-}
-
-static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
+int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
   CBS cert_cbs;
   CRYPTO_BUFFER_init_CBS(buffer, &cert_cbs);
   EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
@@ -235,7 +202,7 @@
 
   EVP_PKEY_free(pubkey);
 
-  ssl_cert_flush_cached_x509_leaf(cert);
+  cert->x509_method->cert_flush_cached_leaf(cert);
 
   if (cert->chain != NULL) {
     CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain, 0));
diff --git a/ssl/ssl_x509.c b/ssl/ssl_x509.c
index 5d78deb..9077b61 100644
--- a/ssl/ssl_x509.c
+++ b/ssl/ssl_x509.c
@@ -140,8 +140,11 @@
 
 #include <openssl/ssl.h>
 
+#include <assert.h>
+
 #include <openssl/stack.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 #include <openssl/x509_vfy.h>
 
 #include "internal.h"
@@ -281,23 +284,6 @@
   X509_VERIFY_PARAM_set_depth(ctx->param, depth);
 }
 
-static X509 *ssl_cert_get0_leaf(CERT *cert) {
-  if (cert->x509_leaf == NULL &&
-      !ssl_cert_cache_leaf_cert(cert)) {
-    return NULL;
-  }
-
-  return cert->x509_leaf;
-}
-
-X509 *SSL_get_certificate(const SSL *ssl) {
-  return ssl_cert_get0_leaf(ssl->cert);
-}
-
-X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
-  return ssl_cert_get0_leaf(ctx->cert);
-}
-
 int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) {
   return X509_STORE_set_default_paths(ctx->cert_store);
 }
@@ -329,3 +315,384 @@
   X509_STORE_free(ctx->cert_store);
   ctx->cert_store = store;
 }
+
+static void ssl_crypto_x509_flush_cached_leaf(CERT *cert) {
+  X509_free(cert->x509_leaf);
+  cert->x509_leaf = NULL;
+}
+
+static void ssl_crypto_x509_flush_cached_chain(CERT *cert) {
+  sk_X509_pop_free(cert->x509_chain, X509_free);
+  cert->x509_chain = NULL;
+}
+
+static void ssl_crypto_x509_clear(CERT *cert) {
+  ssl_crypto_x509_flush_cached_leaf(cert);
+  ssl_crypto_x509_flush_cached_chain(cert);
+
+  X509_free(cert->x509_stash);
+  cert->x509_stash = NULL;
+}
+
+const SSL_X509_METHOD ssl_crypto_x509_method = {
+  ssl_crypto_x509_clear,
+  ssl_crypto_x509_flush_cached_chain,
+  ssl_crypto_x509_flush_cached_leaf,
+};
+
+/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
+ * contents of |x509|. */
+static CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
+  uint8_t *buf = NULL;
+  int cert_len = i2d_X509(x509, &buf);
+  if (cert_len <= 0) {
+    return 0;
+  }
+
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
+  OPENSSL_free(buf);
+
+  return buffer;
+}
+
+static int ssl_use_certificate(CERT *cert, X509 *x) {
+  if (x == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  const int ok = ssl_set_cert(cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
+}
+
+int SSL_use_certificate(SSL *ssl, X509 *x) {
+  return ssl_use_certificate(ssl->cert, x);
+}
+
+int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
+  return ssl_use_certificate(ctx->cert, x);
+}
+
+/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
+ * first element of |cert->chain|. */
+static int ssl_cert_cache_leaf_cert(CERT *cert) {
+  assert(cert->x509_method);
+
+  if (cert->x509_leaf != NULL ||
+      cert->chain == NULL) {
+    return 1;
+  }
+
+  CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+  if (!leaf) {
+    return 1;
+  }
+
+  cert->x509_leaf = X509_parse_from_buffer(leaf);
+  return cert->x509_leaf != NULL;
+}
+
+static X509 *ssl_cert_get0_leaf(CERT *cert) {
+  if (cert->x509_leaf == NULL &&
+      !ssl_cert_cache_leaf_cert(cert)) {
+    return NULL;
+  }
+
+  return cert->x509_leaf;
+}
+
+X509 *SSL_get_certificate(const SSL *ssl) {
+  return ssl_cert_get0_leaf(ssl->cert);
+}
+
+X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
+  return ssl_cert_get0_leaf(ctx->cert);
+}
+
+/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
+static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
+  STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
+  if (chain == NULL) {
+    return NULL;
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
+    sk_CRYPTO_BUFFER_free(chain);
+    return NULL;
+  }
+
+  return chain;
+}
+
+/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
+ * forms of elements of |chain|. It returns one on success or zero on error, in
+ * which case no change to |cert->chain| is made. It preverses the existing
+ * leaf from |cert->chain|, if any. */
+static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
+  STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
+
+  if (cert->chain != NULL) {
+    new_chain = sk_CRYPTO_BUFFER_new_null();
+    if (new_chain == NULL) {
+      return 0;
+    }
+
+    CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+    if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
+      goto err;
+    }
+    /* |leaf| might be NULL if it's a “leafless” chain. */
+    if (leaf != NULL) {
+      CRYPTO_BUFFER_up_ref(leaf);
+    }
+  }
+
+  for (size_t i = 0; i < sk_X509_num(chain); i++) {
+    if (new_chain == NULL) {
+      new_chain = new_leafless_chain();
+      if (new_chain == NULL) {
+        goto err;
+      }
+    }
+
+    CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
+    if (buffer == NULL ||
+        !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      goto err;
+    }
+  }
+
+  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+  cert->chain = new_chain;
+
+  return 1;
+
+err:
+  sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
+  return 0;
+}
+
+static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
+  if (!ssl_cert_set_chain(cert, chain)) {
+    return 0;
+  }
+
+  sk_X509_pop_free(chain, X509_free);
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
+  if (!ssl_cert_set_chain(cert, chain)) {
+    return 0;
+  }
+
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
+  assert(cert->x509_method);
+
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  if (cert->chain != NULL) {
+    if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      return 0;
+    }
+
+    return 1;
+  }
+
+  cert->chain = new_leafless_chain();
+  if (cert->chain == NULL ||
+      !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+    CRYPTO_BUFFER_free(buffer);
+    sk_CRYPTO_BUFFER_free(cert->chain);
+    cert->chain = NULL;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
+  if (!ssl_cert_append_cert(cert, x509)) {
+    return 0;
+  }
+
+  X509_free(cert->x509_stash);
+  cert->x509_stash = x509;
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
+  if (!ssl_cert_append_cert(cert, x509)) {
+    return 0;
+  }
+
+  ssl_crypto_x509_flush_cached_chain(cert);
+  return 1;
+}
+
+int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
+  return ssl_cert_set0_chain(ctx->cert, chain);
+}
+
+int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
+  return ssl_cert_set1_chain(ctx->cert, chain);
+}
+
+int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
+  return ssl_cert_set0_chain(ssl->cert, chain);
+}
+
+int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
+  return ssl_cert_set1_chain(ssl->cert, chain);
+}
+
+int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
+  return ssl_cert_add0_chain_cert(ctx->cert, x509);
+}
+
+int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
+  return ssl_cert_add1_chain_cert(ctx->cert, x509);
+}
+
+int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
+  return SSL_CTX_add0_chain_cert(ctx, x509);
+}
+
+int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
+  return ssl_cert_add0_chain_cert(ssl->cert, x509);
+}
+
+int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
+  return ssl_cert_add1_chain_cert(ssl->cert, x509);
+}
+
+int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
+  return SSL_CTX_set0_chain(ctx, NULL);
+}
+
+int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) {
+  return SSL_CTX_clear_chain_certs(ctx);
+}
+
+int SSL_clear_chain_certs(SSL *ssl) {
+  return SSL_set0_chain(ssl, NULL);
+}
+
+int ssl_auto_chain_if_needed(SSL *ssl) {
+  /* Only build a chain if there are no intermediates configured and the feature
+   * isn't disabled. */
+  if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
+      !ssl_has_certificate(ssl) ||
+      ssl->cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
+    return 1;
+  }
+
+  X509 *leaf =
+      X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
+  if (!leaf) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+    return 0;
+  }
+
+  X509_STORE_CTX ctx;
+  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
+    X509_free(leaf);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+    return 0;
+  }
+
+  /* Attempt to build a chain, ignoring the result. */
+  X509_verify_cert(&ctx);
+  X509_free(leaf);
+  ERR_clear_error();
+
+  /* Remove the leaf from the generated chain. */
+  X509_free(sk_X509_shift(ctx.chain));
+
+  const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
+  X509_STORE_CTX_cleanup(&ctx);
+  if (!ok) {
+    return 0;
+  }
+
+  ssl_crypto_x509_flush_cached_chain(ssl->cert);
+
+  return 1;
+}
+
+/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
+ * |cert->chain|. */
+static int ssl_cert_cache_chain_certs(CERT *cert) {
+  assert(cert->x509_method);
+
+  if (cert->x509_chain != NULL ||
+      cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
+    return 1;
+  }
+
+  STACK_OF(X509) *chain = sk_X509_new_null();
+  if (chain == NULL) {
+    return 0;
+  }
+
+  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
+    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
+    X509 *x509 = X509_parse_from_buffer(buffer);
+    if (x509 == NULL ||
+        !sk_X509_push(chain, x509)) {
+      X509_free(x509);
+      goto err;
+    }
+  }
+
+  cert->x509_chain = chain;
+  return 1;
+
+err:
+  sk_X509_pop_free(chain, X509_free);
+  return 0;
+}
+
+int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
+  *out_chain = ctx->cert->x509_chain;
+  return 1;
+}
+
+int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx,
+                                  STACK_OF(X509) **out_chain) {
+  return SSL_CTX_get0_chain_certs(ctx, out_chain);
+}
+
+int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ssl->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
+  *out_chain = ssl->cert->x509_chain;
+  return 1;
+}
diff --git a/ssl/tls_method.c b/ssl/tls_method.c
index 70683e4..5533dfd 100644
--- a/ssl/tls_method.c
+++ b/ssl/tls_method.c
@@ -161,6 +161,7 @@
   static const SSL_METHOD kMethod = {
       0,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -175,6 +176,7 @@
   static const SSL_METHOD kMethod = {
       TLS1_2_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -183,6 +185,7 @@
   static const SSL_METHOD kMethod = {
       TLS1_1_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -191,6 +194,7 @@
   static const SSL_METHOD kMethod = {
       TLS1_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }
@@ -199,6 +203,7 @@
   static const SSL_METHOD kMethod = {
       SSL3_VERSION,
       &kTLSProtocolMethod,
+      &ssl_crypto_x509_method,
   };
   return &kMethod;
 }