Discard user_canceled alerts in TLS 1.3.

Warning alerts do not exist in TLS 1.3, but RFC 8446 section 6.1
continues to define user_canceled as a signal to cancel the handshake,
without specifying how to handle it. JDK11 misuses it to signal
full-duplex connection close after the handshake. As a workaround, skip
user_canceled as in TLS 1.2. This matches NSS and OpenSSL.

Bug: b/135941563
Change-Id: I7ef546f1f166741b9f112686c75e6757331948f0
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/38605
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 660be0a..fc68ce9 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -601,6 +601,9 @@
 	// sendWarningAlerts is the number of consecutive warning alerts to send
 	// before each test message.
 	sendWarningAlerts int
+	// sendUserCanceledAlerts is the number of consecutive user_canceled alerts to
+	// send before each test message.
+	sendUserCanceledAlerts int
 	// sendBogusAlertType, if true, causes a bogus alert of invalid type to
 	// be sent before each test message.
 	sendBogusAlertType bool
@@ -1005,6 +1008,10 @@
 			tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage)
 		}
 
+		for i := 0; i < test.sendUserCanceledAlerts; i++ {
+			tlsConn.SendAlert(alertLevelWarning, alertUserCanceled)
+		}
+
 		if test.sendBogusAlertType {
 			tlsConn.SendAlert(0x42, alertUnexpectedMessage)
 		}
@@ -2590,8 +2597,28 @@
 			expectedError:      ":BAD_ALERT:",
 			expectedLocalError: "remote error: error decoding message",
 		},
+		// Although TLS 1.3 intended to remove warning alerts, it left in
+		// user_canceled. JDK11 misuses this alert as a post-handshake
+		// full-duplex signal. As a workaround, skip user_canceled as in
+		// TLS 1.2, which is consistent with NSS and OpenSSL.
 		{
-			name: "SendWarningAlerts",
+			name: "SendUserCanceledAlerts-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			sendUserCanceledAlerts: 4,
+		},
+		{
+			name: "SendUserCanceledAlerts-TooMany-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			sendUserCanceledAlerts: 5,
+			shouldFail:             true,
+			expectedError:          ":TOO_MANY_WARNING_ALERTS:",
+		},
+		{
+			name: "SendWarningAlerts-TooMany",
 			config: Config{
 				MaxVersion: VersionTLS12,
 			},
@@ -2600,7 +2627,7 @@
 			expectedError:     ":TOO_MANY_WARNING_ALERTS:",
 		},
 		{
-			name: "SendWarningAlerts-Async",
+			name: "SendWarningAlerts-TooMany-Async",
 			config: Config{
 				MaxVersion: VersionTLS12,
 			},
diff --git a/ssl/tls_record.cc b/ssl/tls_record.cc
index c1f9e7f..464c5c5 100644
--- a/ssl/tls_record.cc
+++ b/ssl/tls_record.cc
@@ -566,9 +566,14 @@
       return ssl_open_record_close_notify;
     }
 
-    // Warning alerts do not exist in TLS 1.3.
+    // Warning alerts do not exist in TLS 1.3, but RFC 8446 section 6.1
+    // continues to define user_canceled as a signal to cancel the handshake,
+    // without specifying how to handle it. JDK11 misuses it to signal
+    // full-duplex connection close after the handshake. As a workaround, skip
+    // user_canceled as in TLS 1.2. This matches NSS and OpenSSL.
     if (ssl->s3->have_version &&
-        ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+        ssl_protocol_version(ssl) >= TLS1_3_VERSION &&
+        alert_descr != SSL_AD_USER_CANCELLED) {
       *out_alert = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
       return ssl_open_record_error;