Allow users of the |CRYPTO_BUFFER|-based methods to verify certs after the handshake.

Previously, the |CRYPTO_BUFFER|-based methods always rejected
certificate chains because none of the current callbacks is suitable to
use. In the medium-term, we want an async callback for this but, for
now, we would like to get Chromium working. Chromium already installs a
no-op callback (except for the logic that was moved into BoringSSL in
a58baaf9e68f887a9e1daf88cb25d911a376851f) and so this hack will suffice
for Chromium.

Change-Id: Ie44b7b32b9e42f503c47b072e958507754136d72
Reviewed-on: https://boringssl-review.googlesource.com/14125
Commit-Queue: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@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 6b39096..c938a41 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -2266,6 +2266,13 @@
     SSL_CTX *ctx, int (*callback)(X509_STORE_CTX *store_ctx, void *arg),
     void *arg);
 
+/* SSL_CTX_i_promise_to_verify_certs_after_the_handshake indicates that the
+ * caller understands that the |CRYPTO_BUFFER|-based methods currently require
+ * post-handshake verification of certificates and thus it's ok to accept any
+ * certificates during the handshake. */
+OPENSSL_EXPORT void SSL_CTX_i_promise_to_verify_certs_after_the_handshake(
+    SSL_CTX *ctx);
+
 /* SSL_enable_signed_cert_timestamps causes |ssl| (which must be the client end
  * of a connection) to request SCTs from the server. See
  * https://tools.ietf.org/html/rfc6962.
@@ -4137,6 +4144,12 @@
   /* grease_enabled is one if draft-davidben-tls-grease-01 is enabled and zero
    * otherwise. */
   unsigned grease_enabled:1;
+
+  /* i_promise_to_verify_certs_after_the_handshake indicates that the
+   * application is using the |CRYPTO_BUFFER|-based methods and understands
+   * that this currently requires post-handshake verification of
+   * certificates. */
+  unsigned i_promise_to_verify_certs_after_the_handshake:1;
 };
 
 
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 7ead554..856fba2 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1598,6 +1598,10 @@
   ctx->signed_cert_timestamps_enabled = 1;
 }
 
+void SSL_CTX_i_promise_to_verify_certs_after_the_handshake(SSL_CTX *ctx) {
+  ctx->i_promise_to_verify_certs_after_the_handshake = 1;
+}
+
 void SSL_enable_signed_cert_timestamps(SSL *ssl) {
   ssl->signed_cert_timestamps_enabled = 1;
 }
diff --git a/ssl/tls_method.c b/ssl/tls_method.c
index 7313dad..6144f86 100644
--- a/ssl/tls_method.c
+++ b/ssl/tls_method.c
@@ -278,8 +278,14 @@
 static void ssl_noop_x509_session_clear(SSL_SESSION *session) {}
 static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session,
                                                    SSL *ssl) {
-  OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
-  return 0;
+  if (!ssl->ctx->i_promise_to_verify_certs_after_the_handshake) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNKNOWN_CA);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
+    return 0;
+  }
+
+  session->verify_result = X509_V_OK;
+  return 1;
 }
 
 static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {}