Add |SSL_CTX_set0_buffer_pool|.

This currently only works for certificates parsed from the network, but
if making several connections that share certificates, some KB of memory
might be saved.

BUG=chromium:671420

Change-Id: I1c7a71d84e1976138641f71830aafff87f795f9d
Reviewed-on: https://boringssl-review.googlesource.com/12706
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index dc806e0..5949020 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -1044,7 +1044,8 @@
 
   uint8_t alert;
   sk_CRYPTO_BUFFER_pop_free(ssl->s3->new_session->certs, CRYPTO_BUFFER_free);
-  ssl->s3->new_session->certs = ssl_parse_cert_chain(&alert, NULL, &cbs);
+  ssl->s3->new_session->certs =
+      ssl_parse_cert_chain(&alert, NULL, &cbs, ssl->ctx->pool);
   if (ssl->s3->new_session->certs == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return -1;
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 9b13023..8847251 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -1473,7 +1473,7 @@
       ssl_parse_cert_chain(&alert, ssl->retain_only_sha256_of_client_certs
                                        ? ssl->s3->new_session->peer_sha256
                                        : NULL,
-                           &certificate_msg);
+                           &certificate_msg, ssl->ctx->pool);
   if (ssl->s3->new_session->certs == NULL) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return -1;
diff --git a/ssl/internal.h b/ssl/internal.h
index cd5db9e..3640b65 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -762,7 +762,8 @@
  * |out_leaf_sha256|. */
 STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert,
                                               uint8_t *out_leaf_sha256,
-                                              CBS *cbs);
+                                              CBS *cbs,
+                                              CRYPTO_BUFFER_POOL *pool);
 
 /* ssl_add_cert_to_cbb adds |x509| to |cbb|. It returns one on success and zero
  * on error. */
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index fa2fbc0..a12af77 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -677,6 +677,7 @@
     }
 
     if (has_peer) {
+      /* TODO(agl): this should use the |SSL_CTX|'s pool. */
       CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&peer, NULL);
       if (buffer == NULL ||
           !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) {
@@ -694,6 +695,7 @@
         goto err;
       }
 
+      /* TODO(agl): this should use the |SSL_CTX|'s pool. */
       CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, NULL);
       if (buffer == NULL ||
           !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) {
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 66ee068..7711cd1 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -464,7 +464,8 @@
 
 STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert,
                                               uint8_t *out_leaf_sha256,
-                                              CBS *cbs) {
+                                              CBS *cbs,
+                                              CRYPTO_BUFFER_POOL *pool) {
   STACK_OF(CRYPTO_BUFFER) *ret = sk_CRYPTO_BUFFER_new_null();
   if (ret == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
@@ -493,7 +494,8 @@
       SHA256(CBS_data(&certificate), CBS_len(&certificate), out_leaf_sha256);
     }
 
-    CRYPTO_BUFFER *buf = CRYPTO_BUFFER_new_from_CBS(&certificate, NULL);
+    CRYPTO_BUFFER *buf =
+        CRYPTO_BUFFER_new_from_CBS(&certificate, pool);
     if (buf == NULL) {
       *out_alert = SSL_AD_DECODE_ERROR;
       goto err;
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 8638396..e5c0559 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1070,6 +1070,10 @@
 
 uint32_t SSL_get_mode(const SSL *ssl) { return ssl->mode; }
 
+void SSL_CTX_set0_buffer_pool(SSL_CTX *ctx, CRYPTO_BUFFER_POOL *pool) {
+  ctx->pool = pool;
+}
+
 X509 *SSL_get_peer_certificate(const SSL *ssl) {
   if (ssl == NULL) {
     return NULL;
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 140e666..3ad906b 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -66,6 +66,8 @@
 #include "test_config.h"
 
 
+static CRYPTO_BUFFER_POOL *g_pool = nullptr;
+
 #if !defined(OPENSSL_WINDOWS)
 static int closesocket(int sock) {
   return close(sock);
@@ -909,6 +911,8 @@
     return nullptr;
   }
 
+  SSL_CTX_set0_buffer_pool(ssl_ctx.get(), g_pool);
+
   // Enable TLS 1.3 for tests.
   if (!config->is_dtls &&
       !SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION)) {
@@ -1915,6 +1919,8 @@
     return Usage(argv[0]);
   }
 
+  g_pool = CRYPTO_BUFFER_POOL_new();
+
   // Some code treats the zero time special, so initialize the clock to a
   // non-zero time.
   g_clock.tv_sec = 1234;
diff --git a/ssl/tls13_both.c b/ssl/tls13_both.c
index a47dd26..dd4a041 100644
--- a/ssl/tls13_both.c
+++ b/ssl/tls13_both.c
@@ -206,7 +206,8 @@
              ssl->s3->new_session->peer_sha256);
     }
 
-    CRYPTO_BUFFER *buf = CRYPTO_BUFFER_new_from_CBS(&certificate, NULL);
+    CRYPTO_BUFFER *buf =
+        CRYPTO_BUFFER_new_from_CBS(&certificate, ssl->ctx->pool);
     if (buf == NULL ||
         !sk_CRYPTO_BUFFER_push(certs, buf)) {
       CRYPTO_BUFFER_free(buf);