|
|
d47c41 |
diff -up nss/gtests/ssl_gtest/ssl_gather_unittest.cc.ssl3gthr nss/gtests/ssl_gtest/ssl_gather_unittest.cc
|
|
|
d47c41 |
--- nss/gtests/ssl_gtest/ssl_gather_unittest.cc.ssl3gthr 2017-04-28 14:40:23.579583263 +0200
|
|
|
d47c41 |
+++ nss/gtests/ssl_gtest/ssl_gather_unittest.cc 2017-04-28 14:40:23.579583263 +0200
|
|
|
d47c41 |
@@ -0,0 +1,153 @@
|
|
|
d47c41 |
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
d47c41 |
+/* vim: set ts=2 et sw=2 tw=80: */
|
|
|
d47c41 |
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
d47c41 |
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
d47c41 |
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+#include "gtest_utils.h"
|
|
|
d47c41 |
+#include "tls_connect.h"
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+namespace nss_test {
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+class GatherV2ClientHelloTest : public TlsConnectTestBase {
|
|
|
d47c41 |
+ public:
|
|
|
d47c41 |
+ GatherV2ClientHelloTest() : TlsConnectTestBase(STREAM, 0) {}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ void ConnectExpectMalformedClientHello(const DataBuffer &data) {
|
|
|
d47c41 |
+ EnsureTlsSetup();
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ auto alert_recorder = new TlsAlertRecorder();
|
|
|
d47c41 |
+ server_->SetPacketFilter(alert_recorder);
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ client_->SendDirect(data);
|
|
|
d47c41 |
+ server_->StartConnect();
|
|
|
d47c41 |
+ server_->Handshake();
|
|
|
d47c41 |
+ ASSERT_TRUE_WAIT(
|
|
|
d47c41 |
+ (server_->error_code() == SSL_ERROR_RX_MALFORMED_CLIENT_HELLO), 2000);
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
|
|
|
d47c41 |
+ EXPECT_EQ(illegal_parameter, alert_recorder->description());
|
|
|
d47c41 |
+ }
|
|
|
d47c41 |
+};
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 5-byte v3 record, with a zero fragment length. The empty handshake
|
|
|
d47c41 |
+// message should be ignored, and the connection will succeed afterwards.
|
|
|
d47c41 |
+TEST_F(TlsConnectTest, GatherEmptyV3Record) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x16, 1); // handshake
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x0301, 2); // record_version
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 2); // length=0
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ EnsureTlsSetup();
|
|
|
d47c41 |
+ client_->SendDirect(buffer);
|
|
|
d47c41 |
+ Connect();
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 5-byte v3 record, with a fragment length exceeding the maximum.
|
|
|
d47c41 |
+TEST_F(TlsConnectTest, GatherExcessiveV3Record) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x16, 1); // handshake
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x0301, 2); // record_version
|
|
|
d47c41 |
+ (void)buffer.Write(idx, MAX_FRAGMENT_LENGTH + 2048 + 1, 2); // length=max+1
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ EnsureTlsSetup();
|
|
|
d47c41 |
+ auto alert_recorder = new TlsAlertRecorder();
|
|
|
d47c41 |
+ server_->SetPacketFilter(alert_recorder);
|
|
|
d47c41 |
+ client_->SendDirect(buffer);
|
|
|
d47c41 |
+ server_->StartConnect();
|
|
|
d47c41 |
+ server_->Handshake();
|
|
|
d47c41 |
+ ASSERT_TRUE_WAIT((server_->error_code() == SSL_ERROR_RX_RECORD_TOO_LONG),
|
|
|
d47c41 |
+ 2000);
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
|
|
|
d47c41 |
+ EXPECT_EQ(record_overflow, alert_recorder->description());
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 3-byte v2 header, with a fragment length of 2.
|
|
|
d47c41 |
+TEST_F(GatherV2ClientHelloTest, GatherV2RecordLongHeader) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x0002, 2); // length=2 (long header)
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0U, 1); // padding=0
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 2); // data
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectMalformedClientHello(buffer);
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 3-byte v2 header, with a fragment length of 1.
|
|
|
d47c41 |
+TEST_F(GatherV2ClientHelloTest, GatherV2RecordLongHeader2) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x0001, 2); // length=1 (long header)
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0U, 1); // padding=0
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0U, 1); // data
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 1); // surplus (need 5 bytes total)
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectMalformedClientHello(buffer);
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 3-byte v2 header, with a zero fragment length.
|
|
|
d47c41 |
+TEST_F(GatherV2ClientHelloTest, GatherEmptyV2RecordLongHeader) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0U, 2); // length=0 (long header)
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0U, 1); // padding=0
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 2); // surplus (need 5 bytes total)
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectMalformedClientHello(buffer);
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 2-byte v2 header, with a fragment length of 3.
|
|
|
d47c41 |
+TEST_F(GatherV2ClientHelloTest, GatherV2RecordShortHeader) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x8003, 2); // length=3 (short header)
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 3); // data
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectMalformedClientHello(buffer);
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 2-byte v2 header, with a fragment length of 2.
|
|
|
d47c41 |
+TEST_F(GatherV2ClientHelloTest, GatherEmptyV2RecordShortHeader2) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x8002, 2); // length=2 (short header)
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0U, 2); // data
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 1); // surplus (need 5 bytes total)
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectMalformedClientHello(buffer);
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 2-byte v2 header, with a fragment length of 1.
|
|
|
d47c41 |
+TEST_F(GatherV2ClientHelloTest, GatherEmptyV2RecordShortHeader3) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x8001, 2); // length=1 (short header)
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0U, 1); // data
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 2); // surplus (need 5 bytes total)
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectMalformedClientHello(buffer);
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+// Gather a 2-byte v2 header, with a zero fragment length.
|
|
|
d47c41 |
+TEST_F(GatherV2ClientHelloTest, GatherEmptyV2RecordShortHeader) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x8000, 2); // length=0 (short header)
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 3); // surplus (need 5 bytes total)
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectMalformedClientHello(buffer);
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+} // namespace nss_test
|
|
|
d47c41 |
diff -up nss/gtests/ssl_gtest/ssl_gtest.gyp.ssl3gthr nss/gtests/ssl_gtest/ssl_gtest.gyp
|
|
|
d47c41 |
--- nss/gtests/ssl_gtest/ssl_gtest.gyp.ssl3gthr 2017-04-28 14:40:23.579583263 +0200
|
|
|
d47c41 |
+++ nss/gtests/ssl_gtest/ssl_gtest.gyp 2017-04-28 14:42:07.853153503 +0200
|
|
|
d47c41 |
@@ -25,6 +25,7 @@
|
|
|
d47c41 |
'ssl_exporter_unittest.cc',
|
|
|
d47c41 |
'ssl_extension_unittest.cc',
|
|
|
d47c41 |
'ssl_fuzz_unittest.cc',
|
|
|
d47c41 |
+ 'ssl_gather_unittest.cc',
|
|
|
d47c41 |
'ssl_gtest.cc',
|
|
|
d47c41 |
'ssl_hrr_unittest.cc',
|
|
|
d47c41 |
'ssl_loopback_unittest.cc',
|
|
|
d47c41 |
diff -up nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc.ssl3gthr nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc
|
|
|
d47c41 |
--- nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc.ssl3gthr 2017-04-05 14:23:56.000000000 +0200
|
|
|
d47c41 |
+++ nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc 2017-04-28 14:40:23.579583263 +0200
|
|
|
d47c41 |
@@ -202,6 +202,28 @@ TEST_P(SSLv2ClientHelloTest, Connect) {
|
|
|
d47c41 |
Connect();
|
|
|
d47c41 |
}
|
|
|
d47c41 |
|
|
|
d47c41 |
+// Sending a v2 ClientHello after a no-op v3 record must fail.
|
|
|
d47c41 |
+TEST_P(SSLv2ClientHelloTest, ConnectAfterEmptyV3Record) {
|
|
|
d47c41 |
+ DataBuffer buffer;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ size_t idx = 0;
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x16, 1); // handshake
|
|
|
d47c41 |
+ idx = buffer.Write(idx, 0x0301, 2); // record_version
|
|
|
d47c41 |
+ (void)buffer.Write(idx, 0U, 2); // length=0
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
|
|
|
d47c41 |
+ EnsureTlsSetup();
|
|
|
d47c41 |
+ client_->SendDirect(buffer);
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ // Need padding so the connection doesn't just time out. With a v2
|
|
|
d47c41 |
+ // ClientHello parsed as a v3 record we will use the record version
|
|
|
d47c41 |
+ // as the record length.
|
|
|
d47c41 |
+ SetPadding(255);
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ ConnectExpectFail();
|
|
|
d47c41 |
+ EXPECT_EQ(SSL_ERROR_BAD_CLIENT, server_->error_code());
|
|
|
d47c41 |
+}
|
|
|
d47c41 |
+
|
|
|
d47c41 |
// Test negotiating TLS 1.3.
|
|
|
d47c41 |
TEST_F(SSLv2ClientHelloTestF, Connect13) {
|
|
|
d47c41 |
EnsureTlsSetup();
|
|
|
d47c41 |
diff -up nss/lib/ssl/ssl3gthr.c.ssl3gthr nss/lib/ssl/ssl3gthr.c
|
|
|
d47c41 |
--- nss/lib/ssl/ssl3gthr.c.ssl3gthr 2017-04-05 14:23:56.000000000 +0200
|
|
|
d47c41 |
+++ nss/lib/ssl/ssl3gthr.c 2017-04-28 14:40:23.579583263 +0200
|
|
|
d47c41 |
@@ -32,6 +32,7 @@ ssl3_InitGather(sslGather *gs)
|
|
|
d47c41 |
gs->readOffset = 0;
|
|
|
d47c41 |
gs->dtlsPacketOffset = 0;
|
|
|
d47c41 |
gs->dtlsPacket.len = 0;
|
|
|
d47c41 |
+ gs->rejectV2Records = PR_FALSE;
|
|
|
d47c41 |
status = sslBuffer_Grow(&gs->buf, 4096);
|
|
|
d47c41 |
return status;
|
|
|
d47c41 |
}
|
|
|
d47c41 |
@@ -147,8 +148,11 @@ ssl3_GatherData(sslSocket *ss, sslGather
|
|
|
d47c41 |
switch (gs->state) {
|
|
|
d47c41 |
case GS_HEADER:
|
|
|
d47c41 |
/* Check for SSLv2 handshakes. Always assume SSLv3 on clients,
|
|
|
d47c41 |
- * support SSLv2 handshakes only when ssl2gs != NULL. */
|
|
|
d47c41 |
- if (!ssl2gs || ssl3_isLikelyV3Hello(gs->hdr)) {
|
|
|
d47c41 |
+ * support SSLv2 handshakes only when ssl2gs != NULL.
|
|
|
d47c41 |
+ * Always assume v3 after we received the first record. */
|
|
|
d47c41 |
+ if (!ssl2gs ||
|
|
|
d47c41 |
+ ss->gs.rejectV2Records ||
|
|
|
d47c41 |
+ ssl3_isLikelyV3Hello(gs->hdr)) {
|
|
|
d47c41 |
/* Should have a non-SSLv2 record header in gs->hdr. Extract
|
|
|
d47c41 |
* the length of the following encrypted data, and then
|
|
|
d47c41 |
* read in the rest of the record into gs->inbuf. */
|
|
|
d47c41 |
@@ -183,7 +187,7 @@ ssl3_GatherData(sslSocket *ss, sslGather
|
|
|
d47c41 |
/* This is the max length for an encrypted SSLv3+ fragment. */
|
|
|
d47c41 |
if (!v2HdrLength &&
|
|
|
d47c41 |
gs->remainder > (MAX_FRAGMENT_LENGTH + 2048)) {
|
|
|
d47c41 |
- SSL3_SendAlert(ss, alert_fatal, unexpected_message);
|
|
|
d47c41 |
+ SSL3_SendAlert(ss, alert_fatal, record_overflow);
|
|
|
d47c41 |
gs->state = GS_INIT;
|
|
|
d47c41 |
PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG);
|
|
|
d47c41 |
return SECFailure;
|
|
|
d47c41 |
@@ -205,13 +209,28 @@ ssl3_GatherData(sslSocket *ss, sslGather
|
|
|
d47c41 |
* many into the gs->hdr[] buffer. Copy them over into inbuf so
|
|
|
d47c41 |
* that we can properly process the hello record later. */
|
|
|
d47c41 |
if (v2HdrLength) {
|
|
|
d47c41 |
+ /* Reject v2 records that don't even carry enough data to
|
|
|
d47c41 |
+ * resemble a valid ClientHello header. */
|
|
|
d47c41 |
+ if (gs->remainder < SSL_HL_CLIENT_HELLO_HBYTES) {
|
|
|
d47c41 |
+ SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
|
|
|
d47c41 |
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
|
|
|
d47c41 |
+ return SECFailure;
|
|
|
d47c41 |
+ }
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ PORT_Assert(lbp);
|
|
|
d47c41 |
gs->inbuf.len = 5 - v2HdrLength;
|
|
|
d47c41 |
PORT_Memcpy(lbp, gs->hdr + v2HdrLength, gs->inbuf.len);
|
|
|
d47c41 |
gs->remainder -= gs->inbuf.len;
|
|
|
d47c41 |
lbp += gs->inbuf.len;
|
|
|
d47c41 |
}
|
|
|
d47c41 |
|
|
|
d47c41 |
- break; /* End this case. Continue around the loop. */
|
|
|
d47c41 |
+ if (gs->remainder > 0) {
|
|
|
d47c41 |
+ break; /* End this case. Continue around the loop. */
|
|
|
d47c41 |
+ }
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ /* FALL THROUGH if (gs->remainder == 0) as we just received
|
|
|
d47c41 |
+ * an empty record and there's really no point in calling
|
|
|
d47c41 |
+ * ssl_DefRecv() with buf=NULL and len=0. */
|
|
|
d47c41 |
|
|
|
d47c41 |
case GS_DATA:
|
|
|
d47c41 |
/*
|
|
|
d47c41 |
@@ -219,6 +238,10 @@ ssl3_GatherData(sslSocket *ss, sslGather
|
|
|
d47c41 |
*/
|
|
|
d47c41 |
SSL_TRC(10, ("%d: SSL[%d]: got record of %d bytes",
|
|
|
d47c41 |
SSL_GETPID(), ss->fd, gs->inbuf.len));
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ /* reject any v2 records from now on */
|
|
|
d47c41 |
+ ss->gs.rejectV2Records = PR_TRUE;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
gs->state = GS_INIT;
|
|
|
d47c41 |
return 1;
|
|
|
d47c41 |
}
|
|
|
d47c41 |
diff -up nss/lib/ssl/ssldef.c.ssl3gthr nss/lib/ssl/ssldef.c
|
|
|
d47c41 |
--- nss/lib/ssl/ssldef.c.ssl3gthr 2017-04-05 14:23:56.000000000 +0200
|
|
|
d47c41 |
+++ nss/lib/ssl/ssldef.c 2017-04-28 14:40:23.579583263 +0200
|
|
|
d47c41 |
@@ -66,6 +66,8 @@ ssl_DefRecv(sslSocket *ss, unsigned char
|
|
|
d47c41 |
PRFileDesc *lower = ss->fd->lower;
|
|
|
d47c41 |
int rv;
|
|
|
d47c41 |
|
|
|
d47c41 |
+ PORT_Assert(buf && len > 0);
|
|
|
d47c41 |
+
|
|
|
d47c41 |
rv = lower->methods->recv(lower, (void *)buf, len, flags, ss->rTimeout);
|
|
|
d47c41 |
if (rv < 0) {
|
|
|
d47c41 |
DEFINE_ERROR
|
|
|
d47c41 |
diff -up nss/lib/ssl/sslimpl.h.ssl3gthr nss/lib/ssl/sslimpl.h
|
|
|
d47c41 |
--- nss/lib/ssl/sslimpl.h.ssl3gthr 2017-04-28 14:40:23.566583566 +0200
|
|
|
d47c41 |
+++ nss/lib/ssl/sslimpl.h 2017-04-28 14:40:23.580583240 +0200
|
|
|
d47c41 |
@@ -367,6 +367,10 @@ struct sslGatherStr {
|
|
|
d47c41 |
|
|
|
d47c41 |
/* the start of the buffered DTLS record in dtlsPacket */
|
|
|
d47c41 |
unsigned int dtlsPacketOffset;
|
|
|
d47c41 |
+
|
|
|
d47c41 |
+ /* tracks whether we've seen a v3-type record before and must reject
|
|
|
d47c41 |
+ * any further v2-type records. */
|
|
|
d47c41 |
+ PRBool rejectV2Records;
|
|
|
d47c41 |
};
|
|
|
d47c41 |
|
|
|
d47c41 |
/* sslGather.state */
|