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.

Change-Id: I0ea4589d7a8b5c41df225ad7f282b6d1376a8db4
Reviewed-on: https://boringssl-review.googlesource.com/12164
Reviewed-by: Adam Langley <alangley@gmail.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 165dca1..689fa82 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -730,6 +730,16 @@
  * modes enabled for |ssl|. */
 OPENSSL_EXPORT uint32_t SSL_get_mode(const SSL *ssl);
 
+/* SSL_CTX_set0_buffer_pool sets a |CRYPTO_BUFFER_POOL| that will be used to
+ * store certificates. This can allow multiple connections to share
+ * certificates and thus save memory.
+ *
+ * The SSL_CTX does not take ownership of |pool| and the caller must ensure
+ * that |pool| outlives |ctx| and all objects linked to it, including |SSL|,
+ * |X509| and |SSL_SESSION| objects. Basically, don't ever free |pool|. */
+OPENSSL_EXPORT void SSL_CTX_set0_buffer_pool(SSL_CTX *ctx,
+                                             CRYPTO_BUFFER_POOL *pool);
+
 
 /* Configuring certificates and private keys.
  *
@@ -4073,6 +4083,10 @@
    * TODO(agl): remove once node.js no longer references this. */
   STACK_OF(X509)* extra_certs;
   int freelist_max_len;
+
+  /* pool is used for all |CRYPTO_BUFFER|s in case we wish to share certificate
+   * memory. */
+  CRYPTO_BUFFER_POOL *pool;
 };
 
 typedef struct ssl_handshake_st SSL_HANDSHAKE;
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 39c52ed..a984e8e 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -685,6 +685,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) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
@@ -719,6 +720,7 @@
         }
       }
 
+      /* TODO(agl): this should use the |SSL_CTX|'s pool. */
       CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, NULL);
       if (buffer == NULL) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 1ddf69a..4297245 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -481,7 +481,8 @@
       SHA256(CBS_data(&certificate), CBS_len(&certificate), out_leaf_sha256);
     }
 
-    CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&certificate, NULL);
+    CRYPTO_BUFFER *buffer =
+        CRYPTO_BUFFER_new_from_CBS(&certificate, ssl->ctx->pool);
     if (buffer == NULL) {
       *out_alert = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index ac5a7dd..ad4d1b2 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1080,6 +1080,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/tls13_both.c b/ssl/tls13_both.c
index 8639089..85d804f 100644
--- a/ssl/tls13_both.c
+++ b/ssl/tls13_both.c
@@ -207,7 +207,8 @@
              ssl->s3->new_session->peer_sha256);
     }
 
-    CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&certificate, NULL);
+    CRYPTO_BUFFER *buffer =
+        CRYPTO_BUFFER_new_from_CBS(&certificate, ssl->ctx->pool);
     if (buffer == NULL) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);