Verifying resumption cipher validity with current configuration.

BUG=chromium:659593

Change-Id: I73a4751609b85df7cd40f0f60dc3f3046a490940
Reviewed-on: https://boringssl-review.googlesource.com/11861
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_server.c b/ssl/handshake_server.c
index ad3ede3..8db9bee 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -771,7 +771,10 @@
           ssl->version == session->ssl_version &&
           /* If the client offers the EMS extension, but the previous session
            * didn't use it, then negotiate a new session. */
-          have_extended_master_secret == session->extended_master_secret;
+          have_extended_master_secret == session->extended_master_secret &&
+          /* Only resume if the session's cipher is still valid under the
+           * current configuration. */
+          ssl_is_valid_cipher(ssl, session->cipher);
     }
 
     if (has_session) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 2772e1b..114c810 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1695,6 +1695,11 @@
 int ssl3_write_app_data(SSL *ssl, const void *buf, int len);
 int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
 int ssl3_output_cert_chain(SSL *ssl);
+
+/* ssl_is_valid_cipher checks that |cipher| is valid according to the current
+ * server configuration in |ssl|. It returns 1 if valid, and 0 otherwise. */
+int ssl_is_valid_cipher(SSL *ssl, const SSL_CIPHER *cipher);
+
 const SSL_CIPHER *ssl3_choose_cipher(
     SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
     const struct ssl_cipher_preference_list_st *srvr);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 69d3a9d..ad7a544 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -240,6 +240,17 @@
   return NULL;
 }
 
+int ssl_is_valid_cipher(SSL *ssl, const SSL_CIPHER *cipher) {
+  /* Check the TLS version. */
+  if (SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
+      SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl)) {
+    return 0;
+  }
+
+  return sk_SSL_CIPHER_find(ssl_get_cipher_preferences(ssl)->ciphers,
+                            NULL, cipher);
+}
+
 const SSL_CIPHER *ssl3_choose_cipher(
     SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
     const struct ssl_cipher_preference_list_st *server_pref) {
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 76caa4e..2eee888 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1456,6 +1456,11 @@
   if (config->max_cert_list > 0) {
     SSL_set_max_cert_list(ssl.get(), config->max_cert_list);
   }
+  if (is_resume &&
+      !config->resume_cipher.empty() &&
+      !SSL_set_cipher_list(ssl.get(), config->resume_cipher.c_str())) {
+    return false;
+  }
 
   int sock = Connect(config->port);
   if (sock == -1) {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index ab5fdee..03a0ef9 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -5367,6 +5367,31 @@
 		}
 	}
 
+	// Sessions with disabled ciphers are not resumed.
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-CipherMismatch",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS12,
+		},
+		flags:                []string{"-cipher", "AES128", "-resume-cipher", "AES256"},
+		shouldFail:           false,
+		expectResumeRejected: true,
+	})
+
+	testCases = append(testCases, testCase{
+		testType:      serverTest,
+		name:          "Resume-Server-CipherMismatch-TLS13",
+		resumeSession: true,
+		config: Config{
+			MaxVersion: VersionTLS13,
+		},
+		flags:                []string{"-cipher", "AES128", "-resume-cipher", "AES256"},
+		shouldFail:           false,
+		expectResumeRejected: true,
+	})
+
 	testCases = append(testCases, testCase{
 		name:          "Resume-Client-CipherMismatch",
 		resumeSession: true,
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 425664d..43b28a2 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -128,6 +128,7 @@
   { "-cipher", &TestConfig::cipher },
   { "-cipher-tls10", &TestConfig::cipher_tls10 },
   { "-cipher-tls11", &TestConfig::cipher_tls11 },
+  { "-resume-cipher", &TestConfig::resume_cipher },
   { "-export-label", &TestConfig::export_label },
   { "-export-context", &TestConfig::export_context },
 };
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 9f74297..8aa6785 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -76,6 +76,7 @@
   std::string cipher;
   std::string cipher_tls10;
   std::string cipher_tls11;
+  std::string resume_cipher;
   bool handshake_never_done = false;
   int export_keying_material = 0;
   std::string export_label;
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index fac4364..2280a2b 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -123,7 +123,8 @@
       /* Only resume if the session's version matches. */
       (session->ssl_version != ssl->version ||
        !ssl_client_cipher_list_contains_cipher(
-           &client_hello, (uint16_t)SSL_CIPHER_get_id(session->cipher)))) {
+           &client_hello, (uint16_t)SSL_CIPHER_get_id(session->cipher)) ||
+       !ssl_is_valid_cipher(ssl, session->cipher))) {
     SSL_SESSION_free(session);
     session = NULL;
   }