SSL_CONFIG: new struct for sheddable handshake configuration.

|SSL_CONFIG| is a container for bits of configuration that are
unneeded after the handshake completes.  By default it is retained for
the life of the |SSL|, but it may be shed at the caller's option by
calling SSL_set_shed_handshake_config().  This is incompatible with
renegotiation, and with SSL_clear().

|SSL_CONFIG| is reachable by |ssl->config| and by |hs->config|.  The
latter is always non-NULL.  To avoid null checks, I've changed the
signature of a number of functions from |SSL*| arguments to
|SSL_HANDSHAKE*| arguments.

When configuration has been shed, setters that touch |SSL_CONFIG|
return an error value if that is possible.  Setters that return |void|
do nothing.

Getters that request |SSL_CONFIG| values will fail with an |assert| if
the configuration has been shed.  When asserts are compiled out, they
will return an error value.

The aim of this commit is to simplify analysis of split-handshakes by
making it obvious that some bits of state have no effects beyond the
handshake.  It also cuts down on memory usage.

Of note: |SSL_CTX| is still reachable after the configuration has been
shed, and a couple things need to be retained only for the sake of
post-handshake hooks.  Perhaps these can be fixed in time.

Change-Id: Idf09642e0518945b81a1e9fcd7331cc9cf7cc2d6
Bug: 123
Reviewed-on: https://boringssl-review.googlesource.com/27644
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index 272fc55..b992e9a 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -381,13 +381,13 @@
   if (version >= TLS1_3_VERSION) {
     // TLS 1.3 uses tickets as authenticators, so we are willing to use them for
     // longer.
-    session->timeout = ssl->session_ctx->session_psk_dhe_timeout;
+    session->timeout = hs->config->session_ctx->session_psk_dhe_timeout;
     session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
   } else {
     // TLS 1.2 resumption does not incorporate new key material, so we use a
     // much shorter timeout.
-    session->timeout = ssl->session_ctx->session_timeout;
-    session->auth_timeout = ssl->session_ctx->session_timeout;
+    session->timeout = hs->config->session_ctx->session_timeout;
+    session->auth_timeout = hs->config->session_ctx->session_timeout;
   }
 
   if (is_server) {
@@ -405,13 +405,13 @@
     session->session_id_length = 0;
   }
 
-  if (ssl->cert->sid_ctx_length > sizeof(session->sid_ctx)) {
+  if (hs->config->cert->sid_ctx_length > sizeof(session->sid_ctx)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
-  OPENSSL_memcpy(session->sid_ctx, ssl->cert->sid_ctx,
-                 ssl->cert->sid_ctx_length);
-  session->sid_ctx_length = ssl->cert->sid_ctx_length;
+  OPENSSL_memcpy(session->sid_ctx, hs->config->cert->sid_ctx,
+                 hs->config->cert->sid_ctx_length);
+  session->sid_ctx_length = hs->config->cert->sid_ctx_length;
 
   // The session is marked not resumable until it is completely filled in.
   session->not_resumable = 1;
@@ -475,7 +475,7 @@
   return 1;
 }
 
-static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out,
+static int ssl_encrypt_ticket_with_cipher_ctx(SSL_HANDSHAKE *hs, CBB *out,
                                               const uint8_t *session_buf,
                                               size_t session_len) {
   ScopedEVP_CIPHER_CTX ctx;
@@ -493,11 +493,11 @@
 
   // Initialize HMAC and cipher contexts. If callback present it does all the
   // work otherwise use generated values from parent ctx.
-  SSL_CTX *tctx = ssl->session_ctx;
+  SSL_CTX *tctx = hs->config->session_ctx;
   uint8_t iv[EVP_MAX_IV_LENGTH];
   uint8_t key_name[16];
   if (tctx->tlsext_ticket_key_cb != NULL) {
-    if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, ctx.get(), hctx.get(),
+    if (tctx->tlsext_ticket_key_cb(hs->ssl, key_name, iv, ctx.get(), hctx.get(),
                                    1 /* encrypt */) < 0) {
       return 0;
     }
@@ -554,11 +554,12 @@
   return 1;
 }
 
-static int ssl_encrypt_ticket_with_method(SSL *ssl, CBB *out,
+static int ssl_encrypt_ticket_with_method(SSL_HANDSHAKE *hs, CBB *out,
                                           const uint8_t *session_buf,
                                           size_t session_len) {
-  const SSL_TICKET_AEAD_METHOD *method = ssl->session_ctx->ticket_aead_method;
-  const size_t max_overhead = method->max_overhead(ssl);
+  const SSL_TICKET_AEAD_METHOD *method =
+      hs->config->session_ctx->ticket_aead_method;
+  const size_t max_overhead = method->max_overhead(hs->ssl);
   const size_t max_out = session_len + max_overhead;
   if (max_out < max_overhead) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
@@ -571,7 +572,8 @@
   }
 
   size_t out_len;
-  if (!method->seal(ssl, ptr, &out_len, max_out, session_buf, session_len)) {
+  if (!method->seal(hs->ssl, ptr, &out_len, max_out, session_buf,
+                    session_len)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_TICKET_ENCRYPTION_FAILED);
     return 0;
   }
@@ -583,7 +585,8 @@
   return 1;
 }
 
-int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
+int ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out,
+                       const SSL_SESSION *session) {
   // Serialize the SSL_SESSION to be encoded into the ticket.
   uint8_t *session_buf = NULL;
   size_t session_len;
@@ -592,25 +595,25 @@
   }
 
   int ret = 0;
-  if (ssl->session_ctx->ticket_aead_method) {
-    ret = ssl_encrypt_ticket_with_method(ssl, out, session_buf, session_len);
+  if (hs->config->session_ctx->ticket_aead_method) {
+    ret = ssl_encrypt_ticket_with_method(hs, out, session_buf, session_len);
   } else {
-    ret =
-        ssl_encrypt_ticket_with_cipher_ctx(ssl, out, session_buf, session_len);
+    ret = ssl_encrypt_ticket_with_cipher_ctx(hs, out, session_buf, session_len);
   }
 
   OPENSSL_free(session_buf);
   return ret;
 }
 
-int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session) {
+int ssl_session_is_context_valid(const SSL_HANDSHAKE *hs,
+                                 const SSL_SESSION *session) {
   if (session == NULL) {
     return 0;
   }
 
-  return session->sid_ctx_length == ssl->cert->sid_ctx_length &&
-         OPENSSL_memcmp(session->sid_ctx, ssl->cert->sid_ctx,
-                        ssl->cert->sid_ctx_length) == 0;
+  return session->sid_ctx_length == hs->config->cert->sid_ctx_length &&
+         OPENSSL_memcmp(session->sid_ctx, hs->config->cert->sid_ctx,
+                        hs->config->cert->sid_ctx_length) == 0;
 }
 
 int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session) {
@@ -632,14 +635,14 @@
 int ssl_session_is_resumable(const SSL_HANDSHAKE *hs,
                              const SSL_SESSION *session) {
   const SSL *const ssl = hs->ssl;
-  return ssl_session_is_context_valid(ssl, session) &&
+  return ssl_session_is_context_valid(hs, session) &&
          // The session must have been created by the same type of end point as
          // we're now using it with.
          ssl->server == session->is_server &&
          // The session must not be expired.
          ssl_session_is_time_valid(ssl, session) &&
          /* Only resume if the session's version matches the negotiated
-           * version. */
+          * version. */
          ssl->version == session->ssl_version &&
          // Only resume if the session's cipher matches the negotiated one.
          hs->new_cipher == session->cipher &&
@@ -649,14 +652,14 @@
          ((sk_CRYPTO_BUFFER_num(session->certs) == 0 &&
            !session->peer_sha256_valid) ||
           session->peer_sha256_valid ==
-              ssl->retain_only_sha256_of_client_certs);
+              hs->config->retain_only_sha256_of_client_certs);
 }
 
 // ssl_lookup_session looks up |session_id| in the session cache and sets
 // |*out_session| to an |SSL_SESSION| object if found.
 static enum ssl_hs_wait_t ssl_lookup_session(
-    SSL *ssl, UniquePtr<SSL_SESSION> *out_session, const uint8_t *session_id,
-    size_t session_id_len) {
+    SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
+    const uint8_t *session_id, size_t session_id_len) {
   out_session->reset();
 
   if (session_id_len == 0 || session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
@@ -665,15 +668,16 @@
 
   UniquePtr<SSL_SESSION> session;
   // Try the internal cache, if it exists.
-  if (!(ssl->session_ctx->session_cache_mode &
+  if (!(hs->config->session_ctx->session_cache_mode &
         SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
     SSL_SESSION data;
-    data.ssl_version = ssl->version;
+    data.ssl_version = hs->ssl->version;
     data.session_id_length = session_id_len;
     OPENSSL_memcpy(data.session_id, session_id, session_id_len);
 
-    MutexReadLock lock(&ssl->session_ctx->lock);
-    session.reset(lh_SSL_SESSION_retrieve(ssl->session_ctx->sessions, &data));
+    MutexReadLock lock(&hs->config->session_ctx->lock);
+    session.reset(
+        lh_SSL_SESSION_retrieve(hs->config->session_ctx->sessions, &data));
     if (session) {
       // |lh_SSL_SESSION_retrieve| returns a non-owning pointer.
       SSL_SESSION_up_ref(session.get());
@@ -682,10 +686,10 @@
   }
 
   // Fall back to the external cache, if it exists.
-  if (!session && ssl->session_ctx->get_session_cb != nullptr) {
+  if (!session && hs->config->session_ctx->get_session_cb != nullptr) {
     int copy = 1;
-    session.reset(ssl->session_ctx->get_session_cb(ssl, session_id,
-                                                   session_id_len, &copy));
+    session.reset(hs->config->session_ctx->get_session_cb(
+        hs->ssl, session_id, session_id_len, &copy));
     if (!session) {
       return ssl_hs_ok;
     }
@@ -704,15 +708,15 @@
     }
 
     // Add the externally cached session to the internal cache if necessary.
-    if (!(ssl->session_ctx->session_cache_mode &
+    if (!(hs->config->session_ctx->session_cache_mode &
           SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
-      SSL_CTX_add_session(ssl->session_ctx, session.get());
+      SSL_CTX_add_session(hs->config->session_ctx, session.get());
     }
   }
 
-  if (session && !ssl_session_is_time_valid(ssl, session.get())) {
+  if (session && !ssl_session_is_time_valid(hs->ssl, session.get())) {
     // The session was from the cache, so remove it.
-    SSL_CTX_remove_session(ssl->session_ctx, session.get());
+    SSL_CTX_remove_session(hs->config->session_ctx, session.get());
     session.reset();
   }
 
@@ -720,13 +724,13 @@
   return ssl_hs_ok;
 }
 
-enum ssl_hs_wait_t ssl_get_prev_session(SSL *ssl,
+enum ssl_hs_wait_t ssl_get_prev_session(SSL_HANDSHAKE *hs,
                                         UniquePtr<SSL_SESSION> *out_session,
                                         bool *out_tickets_supported,
                                         bool *out_renew_ticket,
                                         const SSL_CLIENT_HELLO *client_hello) {
   // This is used only by servers.
-  assert(ssl->server);
+  assert(hs->ssl->server);
   UniquePtr<SSL_SESSION> session;
   bool renew_ticket = false;
 
@@ -734,12 +738,12 @@
   const uint8_t *ticket = NULL;
   size_t ticket_len = 0;
   const bool tickets_supported =
-      !(SSL_get_options(ssl) & SSL_OP_NO_TICKET) &&
-      ssl->version > SSL3_VERSION &&
+      !(SSL_get_options(hs->ssl) & SSL_OP_NO_TICKET) &&
+      hs->ssl->version > SSL3_VERSION &&
       SSL_early_callback_ctx_extension_get(
           client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len);
   if (tickets_supported && ticket_len > 0) {
-    switch (ssl_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
+    switch (ssl_process_ticket(hs, &session, &renew_ticket, ticket, ticket_len,
                                client_hello->session_id,
                                client_hello->session_id_len)) {
       case ssl_ticket_aead_success:
@@ -755,7 +759,7 @@
   } else {
     // The client didn't send a ticket, so the session ID is a real ID.
     enum ssl_hs_wait_t lookup_ret = ssl_lookup_session(
-        ssl, &session, client_hello->session_id, client_hello->session_id_len);
+        hs, &session, client_hello->session_id, client_hello->session_id_len);
     if (lookup_ret != ssl_hs_ok) {
       return lookup_ret;
     }