Support setting per-connection SCT list

Right now the only way to set an SCT list is the per-context function
SSL_CTX_set_signed_cert_timestamp_list. However this assumes that all the
SSLs generated from a SSL_CTX share the same SCT list, which is wrong.

In order to avoid memory duplication in case SSL_CTX has its own list, a
CRYPTO_BUFFER is used for both SSL_CTX and SSL.

Change-Id: Id20e6f128c33cf3e5bff1be390645441be6518c6
Reviewed-on: https://boringssl-review.googlesource.com/13642
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/include/openssl/ssl.h b/include/openssl/ssl.h
index 01aa3e8..e34636e 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -904,6 +904,14 @@
                                                           const uint8_t *list,
                                                           size_t list_len);
 
+/* SSL_set_signed_cert_timestamp_list sets the list of signed certificate
+ * timestamps that is sent to clients that request is. The same format as the
+ * one used for |SSL_CTX_set_signed_cert_timestamp_list| applies. The caller
+ * retains ownership of |list|. */
+OPENSSL_EXPORT int SSL_set_signed_cert_timestamp_list(SSL *ctx,
+                                                      const uint8_t *list,
+                                                      size_t list_len);
+
 /* SSL_CTX_set_ocsp_response sets the OCSP response that is sent to clients
  * which request it. It returns one on success and zero on error. The caller
  * retains ownership of |response|. */
@@ -4050,8 +4058,7 @@
   EVP_PKEY *tlsext_channel_id_private;
 
   /* Signed certificate timestamp list to be sent to the client, if requested */
-  uint8_t *signed_cert_timestamp_list;
-  size_t signed_cert_timestamp_list_length;
+  CRYPTO_BUFFER *signed_cert_timestamp_list;
 
   /* OCSP response to be sent to the client, if requested. */
   CRYPTO_BUFFER *ocsp_response;
diff --git a/ssl/internal.h b/ssl/internal.h
index 50ab3cd..b3f3b0f 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1836,6 +1836,9 @@
    * session space. Only effective on the server side. */
   unsigned retain_only_sha256_of_client_certs:1;
 
+  /* Signed certificate timestamp list to be sent to the client, if requested */
+  CRYPTO_BUFFER *signed_cert_timestamp_list;
+
   /* OCSP response to be sent to the client, if requested. */
   CRYPTO_BUFFER *ocsp_response;
 };
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 912d934..96ea64b 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -362,8 +362,8 @@
   OPENSSL_free(ctx->psk_identity_hint);
   OPENSSL_free(ctx->supported_group_list);
   OPENSSL_free(ctx->alpn_client_proto_list);
+  CRYPTO_BUFFER_free(ctx->signed_cert_timestamp_list);
   CRYPTO_BUFFER_free(ctx->ocsp_response);
-  OPENSSL_free(ctx->signed_cert_timestamp_list);
   EVP_PKEY_free(ctx->tlsext_channel_id_private);
 
   OPENSSL_free(ctx);
@@ -471,6 +471,12 @@
   ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled;
   ssl->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
 
+  /* If the context has an SCT list, use it. */
+  if (ctx->signed_cert_timestamp_list != NULL) {
+    CRYPTO_BUFFER_up_ref(ctx->signed_cert_timestamp_list);
+    ssl->signed_cert_timestamp_list = ctx->signed_cert_timestamp_list;
+  }
+
   /* If the context has an OCSP response, use it. */
   if (ctx->ocsp_response != NULL) {
     CRYPTO_BUFFER_up_ref(ctx->ocsp_response);
@@ -515,6 +521,7 @@
   OPENSSL_free(ssl->psk_identity_hint);
   sk_X509_NAME_pop_free(ssl->client_CA, X509_NAME_free);
   sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles);
+  CRYPTO_BUFFER_free(ssl->signed_cert_timestamp_list);
   CRYPTO_BUFFER_free(ssl->ocsp_response);
 
   if (ssl->method != NULL) {
@@ -1625,8 +1632,27 @@
     return 0;
   }
 
-  return CBS_stow(&sct_list, &ctx->signed_cert_timestamp_list,
-                  &ctx->signed_cert_timestamp_list_length);
+  CRYPTO_BUFFER_free(ctx->signed_cert_timestamp_list);
+  ctx->signed_cert_timestamp_list = CRYPTO_BUFFER_new(CBS_data(&sct_list),
+                                                      CBS_len(&sct_list),
+                                                      NULL);
+  return ctx->signed_cert_timestamp_list != NULL;
+}
+
+int SSL_set_signed_cert_timestamp_list(SSL *ssl, const uint8_t *list,
+                                       size_t list_len) {
+  CBS sct_list;
+  CBS_init(&sct_list, list, list_len);
+  if (!ssl_is_sct_list_valid(&sct_list)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SCT_LIST);
+    return 0;
+  }
+
+  CRYPTO_BUFFER_free(ssl->signed_cert_timestamp_list);
+  ssl->signed_cert_timestamp_list = CRYPTO_BUFFER_new(CBS_data(&sct_list),
+                                                      CBS_len(&sct_list),
+                                                      NULL);
+  return ssl->signed_cert_timestamp_list != NULL;
 }
 
 int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 8091787..d66a2e6 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1371,15 +1371,16 @@
   /* The extension shouldn't be sent when resuming sessions. */
   if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
       ssl->s3->session_reused ||
-      ssl->ctx->signed_cert_timestamp_list_length == 0) {
+      ssl->signed_cert_timestamp_list == NULL) {
     return 1;
   }
 
   CBB contents;
   return CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) &&
          CBB_add_u16_length_prefixed(out, &contents) &&
-         CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list,
-                       ssl->ctx->signed_cert_timestamp_list_length) &&
+         CBB_add_bytes(&contents,
+                       CRYPTO_BUFFER_data(ssl->signed_cert_timestamp_list),
+                       CRYPTO_BUFFER_len(ssl->signed_cert_timestamp_list)) &&
          CBB_flush(out);
 }
 
diff --git a/ssl/tls13_both.c b/ssl/tls13_both.c
index a49ee14..1c4168c 100644
--- a/ssl/tls13_both.c
+++ b/ssl/tls13_both.c
@@ -451,13 +451,13 @@
     goto err;
   }
 
-  if (hs->scts_requested &&
-      ssl->ctx->signed_cert_timestamp_list_length != 0) {
+  if (hs->scts_requested && ssl->signed_cert_timestamp_list != NULL) {
     CBB contents;
     if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) ||
         !CBB_add_u16_length_prefixed(&extensions, &contents) ||
-        !CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list,
-                       ssl->ctx->signed_cert_timestamp_list_length) ||
+        !CBB_add_bytes(&contents,
+                       CRYPTO_BUFFER_data(ssl->signed_cert_timestamp_list),
+                       CRYPTO_BUFFER_len(ssl->signed_cert_timestamp_list)) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       goto err;