Skip to content

Commit 80a6913

Browse files
authored
fix: parse fragmented sslv2 client hellos (#4425)
1 parent fd49fb8 commit 80a6913

File tree

6 files changed

+243
-42
lines changed

6 files changed

+243
-42
lines changed

tests/unit/s2n_client_hello_recv_test.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,45 @@ int main(int argc, char **argv)
509509
EXPECT_SUCCESS(s2n_disable_tls13_in_test());
510510
};
511511

512+
/* Test: Parse fragmented sslv2 client hello.
513+
*
514+
* Even if the sslv2 client hello is sent in one packet, there is no requirement
515+
* that our first call to conn->recv returns the whole message. sslv2 uses separate
516+
* record parsing code, so we need to ensure that those paths can handle partial reads.
517+
*/
518+
{
519+
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
520+
s2n_connection_ptr_free);
521+
EXPECT_SUCCESS(s2n_connection_set_config(server, tls12_config));
522+
523+
struct s2n_stuffer server_in = { 0 };
524+
uint8_t sslv2_client_hello[] = {
525+
SSLv2_CLIENT_HELLO_HEADER,
526+
SSLv2_CLIENT_HELLO_PREFIX,
527+
SSLv2_CLIENT_HELLO_CIPHER_SUITES,
528+
SSLv2_CLIENT_HELLO_CHALLENGE,
529+
};
530+
EXPECT_SUCCESS(s2n_blob_init(&server_in.blob,
531+
sslv2_client_hello, sizeof(sslv2_client_hello)));
532+
EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&server_in, server));
533+
534+
/* Read message one byte at a time */
535+
s2n_blocked_status blocked = S2N_NOT_BLOCKED;
536+
for (size_t i = 0; i < sizeof(sslv2_client_hello); i++) {
537+
EXPECT_ERROR_WITH_ERRNO(
538+
s2n_negotiate_until_message(server, &blocked, SERVER_HELLO),
539+
S2N_ERR_IO_BLOCKED);
540+
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ);
541+
EXPECT_SUCCESS(s2n_stuffer_skip_write(&server_in, 1));
542+
}
543+
544+
/* Successfully read the full message */
545+
EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO));
546+
EXPECT_EQUAL(server->client_protocol_version, S2N_TLS12);
547+
EXPECT_EQUAL(server->client_hello_version, S2N_SSLv2);
548+
EXPECT_TRUE(server->client_hello.sslv2);
549+
};
550+
512551
s2n_config_free(tls12_config);
513552
s2n_config_free(tls13_config);
514553
s2n_cert_chain_and_key_free(chain_and_key);

tests/unit/s2n_record_read_test.c

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
#include "s2n_test.h"
17+
#include "testlib/s2n_testlib.h"
18+
#include "tls/s2n_record.h"
19+
#include "tls/s2n_tls.h"
20+
#include "utils/s2n_safety.h"
21+
22+
#define SSLV2_MIN_SIZE 3
23+
24+
int main(int argc, char *argv[])
25+
{
26+
BEGIN_TEST();
27+
28+
/* Test s2n_sslv2_record_header_parse */
29+
{
30+
const struct {
31+
uint8_t bytes[S2N_TLS_RECORD_HEADER_LENGTH];
32+
uint16_t length;
33+
uint8_t type;
34+
uint8_t version;
35+
} test_cases[] = {
36+
{
37+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, SSLV2_MIN_SIZE, TLS_CLIENT_HELLO, 0x03, 0x03 },
38+
.length = 0,
39+
.type = TLS_CLIENT_HELLO,
40+
.version = S2N_TLS12,
41+
},
42+
{
43+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, SSLV2_MIN_SIZE + 1, TLS_CLIENT_HELLO, 0x03, 0x03 },
44+
.length = 1,
45+
.type = TLS_CLIENT_HELLO,
46+
.version = S2N_TLS12,
47+
},
48+
{
49+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, 0xFF, TLS_CLIENT_HELLO, 0x03, 0x03 },
50+
.length = 0xFF - SSLV2_MIN_SIZE,
51+
.type = TLS_CLIENT_HELLO,
52+
.version = S2N_TLS12,
53+
},
54+
{
55+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, 0x84, TLS_CLIENT_HELLO, 0x03, 0x03 },
56+
.length = 0x84 - SSLV2_MIN_SIZE,
57+
.type = TLS_CLIENT_HELLO,
58+
.version = S2N_TLS12,
59+
},
60+
{
61+
.bytes = { 0x84, 0x84, TLS_CLIENT_HELLO, 0x03, 0x03 },
62+
.length = 0x484 - SSLV2_MIN_SIZE,
63+
.type = TLS_CLIENT_HELLO,
64+
.version = S2N_TLS12,
65+
},
66+
{
67+
.bytes = { 0xFF, 0xFF, TLS_CLIENT_HELLO, 0x03, 0x03 },
68+
.length = 0x7FFF - SSLV2_MIN_SIZE,
69+
.type = TLS_CLIENT_HELLO,
70+
.version = S2N_TLS12,
71+
},
72+
{
73+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, SSLV2_MIN_SIZE, 0, 0x03, 0x03 },
74+
.length = 0,
75+
.type = 0,
76+
.version = S2N_TLS12,
77+
},
78+
{
79+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, SSLV2_MIN_SIZE, 77, 0x03, 0x03 },
80+
.length = 0,
81+
.type = 77,
82+
.version = S2N_TLS12,
83+
},
84+
{
85+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, SSLV2_MIN_SIZE, TLS_SERVER_HELLO, 0x03, 0x03 },
86+
.length = 0,
87+
.type = TLS_SERVER_HELLO,
88+
.version = S2N_TLS12,
89+
},
90+
{
91+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, SSLV2_MIN_SIZE, TLS_CLIENT_HELLO, 0x03, 0x04 },
92+
.length = 0,
93+
.type = TLS_CLIENT_HELLO,
94+
.version = S2N_TLS13,
95+
},
96+
{
97+
.bytes = { S2N_TLS_SSLV2_HEADER_FLAG, SSLV2_MIN_SIZE, TLS_CLIENT_HELLO, 0, 0 },
98+
.length = 0,
99+
.type = TLS_CLIENT_HELLO,
100+
.version = 0,
101+
},
102+
};
103+
104+
/* Test: parse valid record headers */
105+
for (size_t i = 0; i < s2n_array_len(test_cases); i++) {
106+
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
107+
s2n_connection_ptr_free);
108+
EXPECT_NOT_NULL(conn);
109+
110+
EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->header_in,
111+
test_cases[i].bytes, sizeof(test_cases[i].bytes)));
112+
113+
uint8_t type = 0, version = 0;
114+
uint16_t length = 0;
115+
EXPECT_SUCCESS(s2n_sslv2_record_header_parse(conn, &type, &version, &length));
116+
EXPECT_EQUAL(test_cases[i].type, type);
117+
EXPECT_EQUAL(test_cases[i].version, version);
118+
EXPECT_EQUAL(test_cases[i].length, length);
119+
120+
EXPECT_BYTEARRAY_EQUAL(conn->header_in_data,
121+
test_cases[i].bytes, sizeof(test_cases[i].bytes));
122+
123+
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->header_in), S2N_TLS_RECORD_HEADER_LENGTH);
124+
}
125+
126+
/* Test: parse header with bad length
127+
*
128+
* We already read 3 bytes by reading the header, so logically the message
129+
* is at least 3 bytes long.
130+
*/
131+
{
132+
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
133+
s2n_connection_ptr_free);
134+
EXPECT_NOT_NULL(conn);
135+
EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH));
136+
conn->header_in_data[0] = S2N_TLS_SSLV2_HEADER_FLAG;
137+
138+
uint8_t type = 0, version = 0;
139+
uint16_t length = 0;
140+
141+
conn->header_in_data[1] = SSLV2_MIN_SIZE - 1;
142+
EXPECT_FAILURE_WITH_ERRNO(
143+
s2n_sslv2_record_header_parse(conn, &type, &version, &length),
144+
S2N_ERR_BAD_MESSAGE);
145+
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->header_in), 3);
146+
EXPECT_SUCCESS(s2n_stuffer_reread(&conn->header_in));
147+
148+
conn->header_in_data[1] = SSLV2_MIN_SIZE;
149+
EXPECT_SUCCESS(s2n_sslv2_record_header_parse(conn, &type, &version, &length));
150+
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->header_in), S2N_TLS_RECORD_HEADER_LENGTH);
151+
EXPECT_SUCCESS(s2n_stuffer_reread(&conn->header_in));
152+
153+
conn->header_in_data[1] = 0;
154+
EXPECT_FAILURE_WITH_ERRNO(
155+
s2n_sslv2_record_header_parse(conn, &type, &version, &length),
156+
S2N_ERR_BAD_MESSAGE);
157+
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->header_in), 3);
158+
EXPECT_SUCCESS(s2n_stuffer_reread(&conn->header_in));
159+
};
160+
};
161+
162+
END_TEST();
163+
}

tests/unit/s2n_record_test.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -411,24 +411,6 @@ int main(int argc, char **argv)
411411
EXPECT_FAILURE_WITH_ERRNO(s2n_record_parse(conn), S2N_ERR_DECRYPT);
412412
};
413413

414-
/* Test s2n_sslv2_record_header_parse fails when fragment_length < 3 */
415-
{
416-
EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in));
417-
418-
uint8_t record_type = 0;
419-
uint8_t protocol_version = 0;
420-
uint16_t fragment_length = 0;
421-
422-
/* First two bytes are the fragment length */
423-
uint8_t header_bytes[] = { 0x00, 0x00, 0x00, 0x00, 0x00 };
424-
EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->header_in, header_bytes, sizeof(header_bytes)));
425-
426-
EXPECT_FAILURE_WITH_ERRNO(s2n_sslv2_record_header_parse(conn, &record_type, &protocol_version, &fragment_length), S2N_ERR_SAFETY);
427-
428-
/* Check the rest of the stuffer has not been read yet */
429-
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->header_in), 3);
430-
};
431-
432414
/* Record version is recorded for the first message received (Client Hello) */
433415
{
434416
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),

tls/s2n_record.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222

2323
#define S2N_TLS_CONTENT_TYPE_LENGTH 1
2424

25+
#define S2N_TLS_SSLV2_HEADER_FLAG (0x80)
26+
#define S2N_TLS_SSLV2_HEADER_FLAG_UINT16 (S2N_TLS_SSLV2_HEADER_FLAG << 8)
27+
2528
/* All versions of TLS define the record header the same:
2629
* ContentType + ProtocolVersion + length
2730
*/

tls/s2n_record_read.c

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,36 +34,51 @@ int s2n_sslv2_record_header_parse(
3434
uint8_t *client_protocol_version,
3535
uint16_t *fragment_length)
3636
{
37-
struct s2n_stuffer *in = &conn->header_in;
37+
struct s2n_stuffer *header_in = &conn->header_in;
3838

39-
S2N_ERROR_IF(s2n_stuffer_data_available(in) < S2N_TLS_RECORD_HEADER_LENGTH, S2N_ERR_BAD_MESSAGE);
39+
POSIX_ENSURE(s2n_stuffer_data_available(header_in) >= S2N_TLS_RECORD_HEADER_LENGTH,
40+
S2N_ERR_BAD_MESSAGE);
4041

41-
POSIX_GUARD(s2n_stuffer_read_uint16(in, fragment_length));
42+
POSIX_GUARD(s2n_stuffer_read_uint16(header_in, fragment_length));
4243

43-
/* The SSLv2 header is only a 2 byte record length (technically 3 bytes if
44-
* padding is included, but s2n-tls assumes no padding).
45-
* See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt.
44+
/* The first bit of the SSLv2 message would usually indicate whether the
45+
* length is 2 bytes long or 3 bytes long.
46+
* See https://www.ietf.org/archive/id/draft-hickman-netscape-ssl-00.txt
47+
*
48+
* However, s2n-tls only supports SSLv2 for ClientHellos as defined in the
49+
* TLS1.2 RFC. In that case, the first bit must always be set to distinguish
50+
* SSLv2 from non-SSLv2 headers. The length is always 2 bytes.
51+
* See https://datatracker.ietf.org/doc/html/rfc5246#appendix-E.2
52+
*
53+
* Since the first bit is not actually used to indicate length, we need to
54+
* remove it from the length.
4655
*
47-
* So by reading 5 bytes for a standard header we have also read the first
48-
* 3 bytes of the record payload. s2n-tls only supports SSLv2 ClientHellos,
49-
* so we assume that those 3 bytes are the first two fields of the
50-
* SSLv2 ClientHello.
56+
*= https://tools.ietf.org/rfc/rfc5246#appendix-E.2
57+
*# msg_length
58+
*# The highest bit MUST be 1; the remaining bits contain the length
59+
*# of the following data in bytes.
5160
*/
52-
53-
/* Because we already read 3 bytes of the record payload while trying to
54-
* read a standard header, we need to adjust the length so that we only
55-
* try to read the remainder of the record payload.
61+
POSIX_ENSURE(*fragment_length & S2N_TLS_SSLV2_HEADER_FLAG_UINT16, S2N_ERR_BAD_MESSAGE);
62+
*fragment_length ^= S2N_TLS_SSLV2_HEADER_FLAG_UINT16;
63+
64+
/* We read 5 bytes into header_in because we expected a standard, non-SSLv2 record header
65+
* instead of an SSLv2 message. We have therefore already read 3 bytes of the payload.
66+
* We need to adjust "fragment_length" to account for the bytes we have already
67+
* read so that we will only attempt to read the remainder of the payload on
68+
* our next call to conn->recv.
5669
*/
57-
POSIX_ENSURE_GTE(*fragment_length, 3);
58-
*fragment_length -= 3;
70+
POSIX_ENSURE(*fragment_length >= s2n_stuffer_data_available(header_in), S2N_ERR_BAD_MESSAGE);
71+
*fragment_length -= s2n_stuffer_data_available(header_in);
5972

60-
/*
61-
* The first field of an SSLv2 ClientHello is the msg_type.
73+
/* By reading 5 bytes for a standard header we have also read the first
74+
* 3 bytes of the SSLv2 ClientHello message.
75+
* So we now need to parse those three bytes.
6276
*
77+
* The first field of an SSLv2 ClientHello is the msg_type.
6378
* This is always '1', matching the ClientHello msg_type used by later
6479
* handshake messages.
6580
*/
66-
POSIX_GUARD(s2n_stuffer_read_uint8(in, record_type));
81+
POSIX_GUARD(s2n_stuffer_read_uint8(header_in, record_type));
6782

6883
/*
6984
* The second field of an SSLv2 ClientHello is the version.
@@ -73,10 +88,10 @@ int s2n_sslv2_record_header_parse(
7388
* See s2n_sslv2_client_hello_recv.
7489
*/
7590
uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 };
76-
POSIX_GUARD(s2n_stuffer_read_bytes(in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN));
77-
91+
POSIX_GUARD(s2n_stuffer_read_bytes(header_in, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN));
7892
*client_protocol_version = (protocol_version[0] * 10) + protocol_version[1];
7993

94+
POSIX_GUARD(s2n_stuffer_reread(header_in));
8095
return 0;
8196
}
8297

tls/s2n_recv.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,14 @@ int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int
7070
POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->in, S2N_LARGE_FRAGMENT_LENGTH));
7171

7272
/* Read the record until we at least have a header */
73+
POSIX_GUARD(s2n_stuffer_reread(&conn->header_in));
7374
POSIX_GUARD_RESULT(s2n_read_in_bytes(conn, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH));
7475

7576
uint16_t fragment_length;
7677

7778
/* If the first bit is set then this is an SSLv2 record */
78-
if (conn->header_in.blob.data[0] & 0x80) {
79-
conn->header_in.blob.data[0] &= 0x7f;
79+
if (conn->header_in.blob.data[0] & S2N_TLS_SSLV2_HEADER_FLAG) {
8080
*isSSLv2 = 1;
81-
8281
WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_sslv2_record_header_parse(conn, record_type, &conn->client_protocol_version, &fragment_length)));
8382
} else {
8483
WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_header_parse(conn, record_type, &fragment_length)));

0 commit comments

Comments
 (0)