Detect WatchGuard's TLS 1.3 interference failure mode.

WatchGuard's bug is very distinctive. Report a dedicated error code out
of BoringSSL so we can better track this.

Bug: chromium:733223
Change-Id: Ia42abd8654e7987b1d43c63a4f454f35f6aa873b
Reviewed-on: https://boringssl-review.googlesource.com/17328
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index 641d873..7949cc7 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -1,4 +1,5 @@
 SSL,277,ALPN_MISMATCH_ON_EARLY_DATA
+SSL,281,APPLICATION_DATA_INSTEAD_OF_HANDSHAKE
 SSL,100,APP_DATA_IN_HANDSHAKE
 SSL,101,ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT
 SSL,102,BAD_ALERT
@@ -178,6 +179,7 @@
 SSL,219,TOO_MANY_EMPTY_FRAGMENTS
 SSL,260,TOO_MANY_KEY_UPDATES
 SSL,220,TOO_MANY_WARNING_ALERTS
+SSL,1117,TOO_MUCH_READ_EARLY_DATA
 SSL,270,TOO_MUCH_SKIPPED_EARLY_DATA
 SSL,221,UNABLE_TO_FIND_ECDH_PARAMETERS
 SSL,222,UNEXPECTED_EXTENSION
@@ -210,4 +212,3 @@
 SSL,278,WRONG_VERSION_ON_EARLY_DATA
 SSL,248,X509_LIB
 SSL,249,X509_VERIFICATION_SETUP_PROBLEMS
-SSL,1117,TOO_MUCH_READ_EARLY_DATA
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 295f381..b5f1c57 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4678,6 +4678,7 @@
 #define SSL_R_WRONG_VERSION_ON_EARLY_DATA 278
 #define SSL_R_CHANNEL_ID_ON_EARLY_DATA 279
 #define SSL_R_NO_SUPPORTED_VERSIONS_ENABLED 280
+#define SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE 281
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c
index b624135..445f882 100644
--- a/ssl/s3_pkt.c
+++ b/ssl/s3_pkt.c
@@ -511,6 +511,17 @@
       }
     }
 
+    /* WatchGuard's TLS 1.3 interference bug is very distinctive: they drop the
+     * ServerHello and send the remaining encrypted application data records
+     * as-is. This manifests as an application data record when we expect
+     * handshake. Report a dedicated error code for this case. */
+    if (!ssl->server && rr->type == SSL3_RT_APPLICATION_DATA &&
+        ssl->s3->aead_read_ctx == NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      return -1;
+    }
+
     if (rr->type != SSL3_RT_HANDSHAKE) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 62e0056..d9218f2 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -1577,7 +1577,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":UNEXPECTED_RECORD:",
+			expectedError: ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			name: "AppDataBeforeHandshake-Empty",
@@ -1587,7 +1587,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":UNEXPECTED_RECORD:",
+			expectedError: ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			protocol: dtls,
@@ -1757,7 +1757,7 @@
 			},
 			shimWritesFirst: true,
 			shouldFail:      true,
-			expectedError:   ":UNEXPECTED_RECORD:",
+			expectedError:   ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			name: "FalseStart-SkipServerSecondLeg-Implicit",
@@ -1778,7 +1778,7 @@
 				"-advertise-alpn", "\x03foo",
 			},
 			shouldFail:    true,
-			expectedError: ":UNEXPECTED_RECORD:",
+			expectedError: ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			testType:           serverTest,