Fix protocol downgrade bug in case of fragmented packets

CVE-2014-3511

Reviewed-by: Emilia Käsper <emilia@openssl.org>
Reviewed-by: Bodo Möller <bodo@openssl.org>

(Imported from upstream's 280b1f1ad12131defcd986676a8fc9717aaa601b)

Change-Id: I39894b340d2a03751b221631f8cd0ee220cf5b67
Reviewed-on: https://boringssl-review.googlesource.com/1451
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c
index 3a40819..ecf999d 100644
--- a/ssl/s23_srvr.c
+++ b/ssl/s23_srvr.c
@@ -340,23 +340,19 @@
 			 * Client Hello message, this would be difficult, and we'd have
 			 * to read more records to find out.
 			 * No known SSL 3.0 client fragments ClientHello like this,
-			 * so we simply assume TLS 1.0 to avoid protocol version downgrade
-			 * attacks. */
+			 * so we simply reject such connections to avoid
+			 * protocol version downgrade attacks. */
 			if (p[3] == 0 && p[4] < 6)
 				{
-#if 0
-				OPENSSL_PUT_ERROR(SSL, XXX, SSL_R_RECORD_TOO_SMALL);
+				OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_TOO_SMALL);
 				goto err;
-#else
-				v[1] = TLS1_VERSION_MINOR;
-#endif
 				}
 			/* if major version number > 3 set minor to a value
 			 * which will use the highest version 3 we support.
 			 * If TLS 2.0 ever appears we will need to revise
 			 * this....
 			 */
-			else if (p[9] > SSL3_VERSION_MAJOR)
+			if (p[9] > SSL3_VERSION_MAJOR)
 				v[1]=0xff;
 			else
 				v[1]=p[10]; /* minor version according to client_version */
@@ -433,14 +429,34 @@
 		v[0] = p[3]; /* == SSL3_VERSION_MAJOR */
 		v[1] = p[4];
 
+		/* An SSLv3/TLSv1 backwards-compatible CLIENT-HELLO in an SSLv2
+		 * header is sent directly on the wire, not wrapped as a TLS
+		 * record. It's format is:
+		 * Byte  Content
+		 * 0-1   msg_length
+		 * 2     msg_type
+		 * 3-4   version
+		 * 5-6   cipher_spec_length
+		 * 7-8   session_id_length
+		 * 9-10  challenge_length
+		 * ...   ...
+		 */
 		n=((p[0]&0x7f)<<8)|p[1];
 		if (n > (1024*4))
 			{
 			OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_TOO_LARGE);
 			goto err;
 			}
+		if (n < 9)
+			{
+			OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_LENGTH_MISMATCH);
+			goto err;
+			}
 
 		j=ssl23_read_bytes(s,n+2);
+		/* We previously read 11 bytes, so if j > 0, we must have
+		 * j == n+2 == s->packet_length. We have at least 11 valid
+		 * packet bytes. */
 		if (j <= 0) return(j);
 
 		ssl3_finish_mac(s, s->packet+2, s->packet_length-2);