Allow renego and config shedding to coexist more smoothly.

Chrome needs to support renegotiation at TLS 1.2 + HTTP/1.1, but we're
free to shed the handshake configuration at TLS 1.3 or HTTP/2.

Rather than making config shedding implicitly disable renegotiation,
make the actual shedding dependent on a combination of the two settings.
If config shedding is enabled, but so is renegotiation (including
whether we are a client, etc.), leave the config around. If the
renegotiation setting gets disabled again after the handshake,
re-evaluate and shed the config then.

Bug: 123
Change-Id: Ie833f413b3f15b8f0ede617991e3fef239d4a323
Reviewed-on: https://boringssl-review.googlesource.com/27904
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Matt Braithwaite <mab@google.com>
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 1e5b3d1..7b80a03 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -459,6 +459,50 @@
   ctx->handoff = on;
 }
 
+static bool ssl_can_renegotiate(const SSL *ssl) {
+  if (ssl->server || SSL_is_dtls(ssl)) {
+    return false;
+  }
+
+  // We do not accept at SSL 3.0. SSL 3.0 will be removed entirely in the future
+  // and requires retaining more data for renegotiation_info.
+  uint16_t version = ssl_protocol_version(ssl);
+  if (version == SSL3_VERSION || version >= TLS1_3_VERSION) {
+    return false;
+  }
+
+  // The config has already been shed.
+  if (!ssl->config) {
+    return false;
+  }
+
+  switch (ssl->renegotiate_mode) {
+    case ssl_renegotiate_ignore:
+    case ssl_renegotiate_never:
+      return false;
+
+    case ssl_renegotiate_freely:
+      return true;
+    case ssl_renegotiate_once:
+      return ssl->s3->total_renegotiations == 0;
+  }
+
+  assert(0);
+  return false;
+}
+
+static void ssl_maybe_shed_handshake_config(SSL *ssl) {
+  if (ssl->s3->hs != nullptr ||
+      ssl->config == nullptr ||
+      !ssl->config->shed_handshake_config ||
+      ssl_can_renegotiate(ssl)) {
+    return;
+  }
+
+  Delete(ssl->config);
+  ssl->config = nullptr;
+}
+
 }  // namespace bssl
 
 using namespace bssl;
@@ -881,13 +925,8 @@
 
   // Destroy the handshake object if the handshake has completely finished.
   if (!early_return) {
-    if (hs->config->shed_handshake_config) {
-      assert(ssl->config == hs->config);
-      Delete(ssl->config);
-      ssl->config = nullptr;
-      hs->config = nullptr;
-    }
     ssl->s3->hs.reset();
+    ssl_maybe_shed_handshake_config(ssl);
   }
 
   return 1;
@@ -916,11 +955,12 @@
     return tls13_post_handshake(ssl, msg);
   }
 
-  // We do not accept renegotiations as a server or SSL 3.0. SSL 3.0 will be
-  // removed entirely in the future and requires retaining more data for
-  // renegotiation_info.
-  if (ssl->server || ssl->version == SSL3_VERSION) {
-    goto no_renegotiation;
+  // Check for renegotiation on the server before parsing to use the correct
+  // error. Renegotiation is triggered by a different message for servers.
+  if (ssl->server) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
+    return 0;
   }
 
   if (msg.type != SSL3_MT_HELLO_REQUEST || CBS_len(&msg.body) != 0) {
@@ -929,36 +969,20 @@
     return 0;
   }
 
-  switch (ssl->renegotiate_mode) {
-    case ssl_renegotiate_ignore:
-      // Ignore the HelloRequest.
-      return 1;
-
-    case ssl_renegotiate_once:
-      if (ssl->s3->total_renegotiations != 0) {
-        goto no_renegotiation;
-      }
-      break;
-
-    case ssl_renegotiate_never:
-      goto no_renegotiation;
-
-    case ssl_renegotiate_freely:
-      break;
+  if (ssl->renegotiate_mode == ssl_renegotiate_ignore) {
+    return 1;  // Ignore the HelloRequest.
   }
 
-  // Reject renegotiation when the handshake config has been shed.
-  if (!ssl->config) {
-    goto no_renegotiation;
-  }
-
-  // Renegotiation is only supported at quiescent points in the application
-  // protocol, namely in HTTPS, just before reading the HTTP response. Require
-  // the record-layer be idle and avoid complexities of sending a handshake
-  // record while an application_data record is being written.
-  if (!ssl->s3->write_buffer.empty() ||
+  if (!ssl_can_renegotiate(ssl) ||
+      // Renegotiation is only supported at quiescent points in the application
+      // protocol, namely in HTTPS, just before reading the HTTP response.
+      // Require the record-layer be idle and avoid complexities of sending a
+      // handshake record while an application_data record is being written.
+      !ssl->s3->write_buffer.empty() ||
       ssl->s3->write_shutdown != ssl_shutdown_none) {
-    goto no_renegotiation;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
+    return 0;
   }
 
   // Begin a new handshake.
@@ -973,11 +997,6 @@
 
   ssl->s3->total_renegotiations++;
   return 1;
-
-no_renegotiation:
-  OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
-  ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
-  return 0;
 }
 
 static int ssl_read_impl(SSL *ssl) {
@@ -2643,6 +2662,11 @@
 
 void SSL_set_renegotiate_mode(SSL *ssl, enum ssl_renegotiate_mode_t mode) {
   ssl->renegotiate_mode = mode;
+
+  // Check if |ssl_can_renegotiate| has changed and the configuration may now be
+  // shed. HTTP clients may initially allow renegotiation for HTTP/1.1, and then
+  // disable after the handshake once the ALPN protocol is known to be HTTP/2.
+  ssl_maybe_shed_handshake_config(ssl);
 }
 
 int SSL_get_ivs(const SSL *ssl, const uint8_t **out_read_iv,