Hang ssl_auto_chain_if_needed off |X509_METHOD|.

Change-Id: I6609b0e62ea13d14578ccbd945803a27e01ed488
Reviewed-on: https://boringssl-review.googlesource.com/13972
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/ssl_x509.c b/ssl/ssl_x509.c
index 0393714..dab0094 100644
--- a/ssl/ssl_x509.c
+++ b/ssl/ssl_x509.c
@@ -367,6 +367,85 @@
   ctx->cert_store = store;
 }
 
+/* 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;
+}
+
+/* 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 void ssl_crypto_x509_flush_cached_leaf(CERT *cert) {
   X509_free(cert->x509_leaf);
   cert->x509_leaf = NULL;
@@ -570,6 +649,49 @@
   X509_VERIFY_PARAM_free(ssl->param);
 }
 
+static int ssl_crypto_x509_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;
+}
+
 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;
@@ -602,26 +724,12 @@
   ssl_crypto_x509_ssl_new,
   ssl_crypto_x509_ssl_free,
   ssl_crypto_x509_ssl_flush_cached_client_CA,
+  ssl_crypto_x509_ssl_auto_chain_if_needed,
   ssl_crypto_x509_ssl_ctx_new,
   ssl_crypto_x509_ssl_ctx_free,
   ssl_crypto_x509_ssl_ctx_flush_cached_client_CA,
 };
 
-/* 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);
@@ -689,70 +797,6 @@
   return ret;
 }
 
-/* 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;
@@ -881,49 +925,6 @@
   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) {