Skip to content

Commit 50432e4

Browse files
authored
Merge pull request #10057 from minosgalanakis/feature_merge_defragmentation_dev
Merge defragmentation feature branch onto development
2 parents eb20c1f + a4c9233 commit 50432e4

File tree

14 files changed

+249
-48
lines changed

14 files changed

+249
-48
lines changed

ChangeLog.d/tls-hs-defrag-in.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Bugfix
2+
* Support re-assembly of fragmented handshake messages in TLS (both
3+
1.2 and 1.3). The lack of support was causing handshake failures with
4+
some servers, especially with TLS 1.3 in practice. There are a few
5+
limitations, notably a fragmented ClientHello is only supported when
6+
TLS 1.3 support is enabled. See the documentation of
7+
mbedtls_ssl_handshake() for details.

include/mbedtls/ssl.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,6 +1782,8 @@ struct mbedtls_ssl_context {
17821782

17831783
size_t MBEDTLS_PRIVATE(in_hslen); /*!< current handshake message length,
17841784
including the handshake header */
1785+
size_t MBEDTLS_PRIVATE(in_hsfraglen); /*!< accumulated length of hs fragments
1786+
(up to in_hslen) */
17851787
int MBEDTLS_PRIVATE(nb_zero); /*!< # of 0-length encrypted messages */
17861788

17871789
int MBEDTLS_PRIVATE(keep_current_message); /*!< drop or reuse current message
@@ -4302,6 +4304,10 @@ void mbedtls_ssl_conf_cert_req_ca_list(mbedtls_ssl_config *conf,
43024304
* with \c mbedtls_ssl_read()), not handshake messages.
43034305
* With DTLS, this affects both ApplicationData and handshake.
43044306
*
4307+
* \note Defragmentation of TLS handshake messages is supported
4308+
* with some limitations. See the documentation of
4309+
* mbedtls_ssl_handshake() for details.
4310+
*
43054311
* \note This sets the maximum length for a record's payload,
43064312
* excluding record overhead that will be added to it, see
43074313
* \c mbedtls_ssl_get_record_expansion().
@@ -4791,6 +4797,24 @@ int mbedtls_ssl_get_session(const mbedtls_ssl_context *ssl,
47914797
* currently being processed might or might not contain further
47924798
* DTLS records.
47934799
*
4800+
* \note In TLS, reception of fragmented handshake messages is
4801+
* supported with some limitations (those limitations do
4802+
* not apply to DTLS, where defragmentation is fully
4803+
* supported):
4804+
* - On an Mbed TLS server that only accepts TLS 1.2,
4805+
* the initial ClientHello message must not be fragmented.
4806+
* A TLS 1.2 ClientHello may be fragmented if the server
4807+
* also accepts TLS 1.3 connections (meaning
4808+
* that #MBEDTLS_SSL_PROTO_TLS1_3 enabled, and the
4809+
* accepted versions have not been restricted with
4810+
* mbedtls_ssl_conf_max_tls_version() or the like).
4811+
* - The first fragment of a handshake message must be
4812+
* at least 4 bytes long.
4813+
* - Non-handshake records must not be interleaved between
4814+
* the fragments of a handshake message. (This is permitted
4815+
* in TLS 1.2 but not in TLS 1.3, but Mbed TLS rejects it
4816+
* even in TLS 1.2.)
4817+
*
47944818
* \note The PSA crypto subsystem must have been initialized by
47954819
* calling psa_crypto_init() before calling this function.
47964820
*/

library/ssl_misc.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,10 +1752,11 @@ void mbedtls_ssl_set_timer(mbedtls_ssl_context *ssl, uint32_t millisecs);
17521752
MBEDTLS_CHECK_RETURN_CRITICAL
17531753
int mbedtls_ssl_check_timer(mbedtls_ssl_context *ssl);
17541754

1755-
void mbedtls_ssl_reset_in_out_pointers(mbedtls_ssl_context *ssl);
1755+
void mbedtls_ssl_reset_in_pointers(mbedtls_ssl_context *ssl);
1756+
void mbedtls_ssl_update_in_pointers(mbedtls_ssl_context *ssl);
1757+
void mbedtls_ssl_reset_out_pointers(mbedtls_ssl_context *ssl);
17561758
void mbedtls_ssl_update_out_pointers(mbedtls_ssl_context *ssl,
17571759
mbedtls_ssl_transform *transform);
1758-
void mbedtls_ssl_update_in_pointers(mbedtls_ssl_context *ssl);
17591760

17601761
MBEDTLS_CHECK_RETURN_CRITICAL
17611762
int mbedtls_ssl_session_reset_int(mbedtls_ssl_context *ssl, int partial);

library/ssl_msg.c

Lines changed: 159 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2962,19 +2962,34 @@ static uint32_t ssl_get_hs_total_len(mbedtls_ssl_context const *ssl)
29622962

29632963
int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl)
29642964
{
2965-
if (ssl->in_msglen < mbedtls_ssl_hs_hdr_len(ssl)) {
2966-
MBEDTLS_SSL_DEBUG_MSG(1, ("handshake message too short: %" MBEDTLS_PRINTF_SIZET,
2967-
ssl->in_msglen));
2968-
return MBEDTLS_ERR_SSL_INVALID_RECORD;
2969-
}
2965+
if (ssl->in_hsfraglen == 0) {
2966+
/* The handshake message must at least include the header.
2967+
* We may not have the full message yet in case of fragmentation.
2968+
* To simplify the code, we insist on having the header (and in
2969+
* particular the handshake message length) in the first
2970+
* fragment. */
2971+
if (ssl->in_msglen < mbedtls_ssl_hs_hdr_len(ssl)) {
2972+
MBEDTLS_SSL_DEBUG_MSG(1, ("handshake message too short: %" MBEDTLS_PRINTF_SIZET,
2973+
ssl->in_msglen));
2974+
return MBEDTLS_ERR_SSL_INVALID_RECORD;
2975+
}
29702976

2971-
ssl->in_hslen = mbedtls_ssl_hs_hdr_len(ssl) + ssl_get_hs_total_len(ssl);
2977+
ssl->in_hslen = mbedtls_ssl_hs_hdr_len(ssl) + ssl_get_hs_total_len(ssl);
2978+
}
29722979

29732980
MBEDTLS_SSL_DEBUG_MSG(3, ("handshake message: msglen ="
29742981
" %" MBEDTLS_PRINTF_SIZET ", type = %u, hslen = %"
29752982
MBEDTLS_PRINTF_SIZET,
29762983
ssl->in_msglen, ssl->in_msg[0], ssl->in_hslen));
29772984

2985+
if (ssl->transform_in != NULL) {
2986+
MBEDTLS_SSL_DEBUG_MSG(4, ("decrypted handshake message:"
2987+
" iv-buf=%d hdr-buf=%d hdr-buf=%d",
2988+
(int) (ssl->in_iv - ssl->in_buf),
2989+
(int) (ssl->in_hdr - ssl->in_buf),
2990+
(int) (ssl->in_msg - ssl->in_buf)));
2991+
}
2992+
29782993
#if defined(MBEDTLS_SSL_PROTO_DTLS)
29792994
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
29802995
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
@@ -3034,10 +3049,105 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl)
30343049
}
30353050
} else
30363051
#endif /* MBEDTLS_SSL_PROTO_DTLS */
3037-
/* With TLS we don't handle fragmentation (for now) */
3038-
if (ssl->in_msglen < ssl->in_hslen) {
3039-
MBEDTLS_SSL_DEBUG_MSG(1, ("TLS handshake fragmentation not supported"));
3040-
return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
3052+
{
3053+
unsigned char *const reassembled_record_start =
3054+
ssl->in_buf + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN;
3055+
unsigned char *const payload_start =
3056+
reassembled_record_start + mbedtls_ssl_in_hdr_len(ssl);
3057+
unsigned char *payload_end = payload_start + ssl->in_hsfraglen;
3058+
/* How many more bytes we want to have a complete handshake message. */
3059+
const size_t hs_remain = ssl->in_hslen - ssl->in_hsfraglen;
3060+
/* How many bytes of the current record are part of the first
3061+
* handshake message. There may be more handshake messages (possibly
3062+
* incomplete) in the same record; if so, we leave them after the
3063+
* current record, and ssl_consume_current_message() will take
3064+
* care of consuming the next handshake message. */
3065+
const size_t hs_this_fragment_len =
3066+
ssl->in_msglen > hs_remain ? hs_remain : ssl->in_msglen;
3067+
(void) hs_this_fragment_len;
3068+
3069+
MBEDTLS_SSL_DEBUG_MSG(3,
3070+
("%s handshake fragment: %" MBEDTLS_PRINTF_SIZET
3071+
", %" MBEDTLS_PRINTF_SIZET
3072+
"..%" MBEDTLS_PRINTF_SIZET
3073+
" of %" MBEDTLS_PRINTF_SIZET,
3074+
(ssl->in_hsfraglen != 0 ?
3075+
"subsequent" :
3076+
hs_this_fragment_len == ssl->in_hslen ?
3077+
"sole" :
3078+
"initial"),
3079+
ssl->in_msglen,
3080+
ssl->in_hsfraglen,
3081+
ssl->in_hsfraglen + hs_this_fragment_len,
3082+
ssl->in_hslen));
3083+
3084+
/* Move the received handshake fragment to have the whole message
3085+
* (at least the part received so far) in a single segment at a
3086+
* known offset in the input buffer.
3087+
* - When receiving a non-initial handshake fragment, append it to
3088+
* the initial segment.
3089+
* - Even the initial handshake fragment is moved, if it was
3090+
* encrypted with an explicit IV: decryption leaves the payload
3091+
* after the explicit IV, but here we move it to start where the
3092+
* IV was.
3093+
*/
3094+
#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH)
3095+
size_t const in_buf_len = ssl->in_buf_len;
3096+
#else
3097+
size_t const in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN;
3098+
#endif
3099+
if (payload_end + ssl->in_msglen > ssl->in_buf + in_buf_len) {
3100+
MBEDTLS_SSL_DEBUG_MSG(1,
3101+
("Shouldn't happen: no room to move handshake fragment %"
3102+
MBEDTLS_PRINTF_SIZET " from %p to %p (buf=%p len=%"
3103+
MBEDTLS_PRINTF_SIZET ")",
3104+
ssl->in_msglen,
3105+
(void *) ssl->in_msg, (void *) payload_end,
3106+
(void *) ssl->in_buf, in_buf_len));
3107+
return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
3108+
}
3109+
memmove(payload_end, ssl->in_msg, ssl->in_msglen);
3110+
3111+
ssl->in_hsfraglen += ssl->in_msglen;
3112+
payload_end += ssl->in_msglen;
3113+
3114+
if (ssl->in_hsfraglen < ssl->in_hslen) {
3115+
MBEDTLS_SSL_DEBUG_MSG(3, ("Prepare: waiting for more handshake fragments %"
3116+
MBEDTLS_PRINTF_SIZET "/%"
3117+
MBEDTLS_PRINTF_SIZET,
3118+
ssl->in_hsfraglen, ssl->in_hslen));
3119+
ssl->in_hdr = payload_end;
3120+
ssl->in_msglen = 0;
3121+
mbedtls_ssl_update_in_pointers(ssl);
3122+
return MBEDTLS_ERR_SSL_CONTINUE_PROCESSING;
3123+
} else {
3124+
ssl->in_msglen = ssl->in_hsfraglen;
3125+
ssl->in_hsfraglen = 0;
3126+
ssl->in_hdr = reassembled_record_start;
3127+
mbedtls_ssl_update_in_pointers(ssl);
3128+
3129+
/* Update the record length in the fully reassembled record */
3130+
if (ssl->in_msglen > 0xffff) {
3131+
MBEDTLS_SSL_DEBUG_MSG(1,
3132+
("Shouldn't happen: in_msglen=%"
3133+
MBEDTLS_PRINTF_SIZET " > 0xffff",
3134+
ssl->in_msglen));
3135+
return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
3136+
}
3137+
MBEDTLS_PUT_UINT16_BE(ssl->in_msglen, ssl->in_len, 0);
3138+
3139+
size_t record_len = mbedtls_ssl_in_hdr_len(ssl) + ssl->in_msglen;
3140+
(void) record_len;
3141+
MBEDTLS_SSL_DEBUG_BUF(4, "reassembled record",
3142+
ssl->in_hdr, record_len);
3143+
if (ssl->in_hslen < ssl->in_msglen) {
3144+
MBEDTLS_SSL_DEBUG_MSG(3,
3145+
("More handshake messages in the record: "
3146+
"%" MBEDTLS_PRINTF_SIZET " + %" MBEDTLS_PRINTF_SIZET,
3147+
ssl->in_hslen,
3148+
ssl->in_msglen - ssl->in_hslen));
3149+
}
3150+
}
30413151
}
30423152

30433153
return 0;
@@ -4382,6 +4492,14 @@ static int ssl_consume_current_message(mbedtls_ssl_context *ssl)
43824492
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
43834493
}
43844494

4495+
if (ssl->in_hsfraglen != 0) {
4496+
/* Not all handshake fragments have arrived, do not consume. */
4497+
MBEDTLS_SSL_DEBUG_MSG(3, ("Consume: waiting for more handshake fragments %"
4498+
MBEDTLS_PRINTF_SIZET "/%" MBEDTLS_PRINTF_SIZET,
4499+
ssl->in_hsfraglen, ssl->in_hslen));
4500+
return 0;
4501+
}
4502+
43854503
/*
43864504
* Get next Handshake message in the current record
43874505
*/
@@ -4407,6 +4525,7 @@ static int ssl_consume_current_message(mbedtls_ssl_context *ssl)
44074525
ssl->in_msglen -= ssl->in_hslen;
44084526
memmove(ssl->in_msg, ssl->in_msg + ssl->in_hslen,
44094527
ssl->in_msglen);
4528+
MBEDTLS_PUT_UINT16_BE(ssl->in_msglen, ssl->in_len, 0);
44104529

44114530
MBEDTLS_SSL_DEBUG_BUF(4, "remaining content in record",
44124531
ssl->in_msg, ssl->in_msglen);
@@ -4770,6 +4889,18 @@ int mbedtls_ssl_handle_message_type(mbedtls_ssl_context *ssl)
47704889
{
47714890
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
47724891

4892+
/* If we're in the middle of a fragmented TLS handshake message,
4893+
* we don't accept any other message type. For TLS 1.3, the spec forbids
4894+
* interleaving other message types between handshake fragments. For TLS
4895+
* 1.2, the spec does not forbid it but we do. */
4896+
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_STREAM &&
4897+
ssl->in_hsfraglen != 0 &&
4898+
ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) {
4899+
MBEDTLS_SSL_DEBUG_MSG(1, ("non-handshake message in the middle"
4900+
" of a fragmented handshake message"));
4901+
return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE;
4902+
}
4903+
47734904
/*
47744905
* Handle particular types of records
47754906
*/
@@ -5081,7 +5212,7 @@ void mbedtls_ssl_update_in_pointers(mbedtls_ssl_context *ssl)
50815212
} else
50825213
#endif
50835214
{
5084-
ssl->in_ctr = ssl->in_hdr - MBEDTLS_SSL_SEQUENCE_NUMBER_LEN;
5215+
ssl->in_ctr = ssl->in_buf;
50855216
ssl->in_len = ssl->in_hdr + 3;
50865217
#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
50875218
ssl->in_cid = ssl->in_len;
@@ -5097,24 +5228,35 @@ void mbedtls_ssl_update_in_pointers(mbedtls_ssl_context *ssl)
50975228
* Setup an SSL context
50985229
*/
50995230

5100-
void mbedtls_ssl_reset_in_out_pointers(mbedtls_ssl_context *ssl)
5231+
void mbedtls_ssl_reset_in_pointers(mbedtls_ssl_context *ssl)
5232+
{
5233+
#if defined(MBEDTLS_SSL_PROTO_DTLS)
5234+
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
5235+
ssl->in_hdr = ssl->in_buf;
5236+
} else
5237+
#endif /* MBEDTLS_SSL_PROTO_DTLS */
5238+
{
5239+
ssl->in_hdr = ssl->in_buf + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN;
5240+
}
5241+
5242+
/* Derive other internal pointers. */
5243+
mbedtls_ssl_update_in_pointers(ssl);
5244+
}
5245+
5246+
void mbedtls_ssl_reset_out_pointers(mbedtls_ssl_context *ssl)
51015247
{
51025248
/* Set the incoming and outgoing record pointers. */
51035249
#if defined(MBEDTLS_SSL_PROTO_DTLS)
51045250
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
51055251
ssl->out_hdr = ssl->out_buf;
5106-
ssl->in_hdr = ssl->in_buf;
51075252
} else
51085253
#endif /* MBEDTLS_SSL_PROTO_DTLS */
51095254
{
51105255
ssl->out_ctr = ssl->out_buf;
5111-
ssl->out_hdr = ssl->out_buf + 8;
5112-
ssl->in_hdr = ssl->in_buf + 8;
5256+
ssl->out_hdr = ssl->out_buf + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN;
51135257
}
5114-
51155258
/* Derive other internal pointers. */
51165259
mbedtls_ssl_update_out_pointers(ssl, NULL /* no transform enabled */);
5117-
mbedtls_ssl_update_in_pointers(ssl);
51185260
}
51195261

51205262
/*

library/ssl_tls.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,13 @@ static void handle_buffer_resizing(mbedtls_ssl_context *ssl, int downsizing,
339339
size_t out_buf_new_len)
340340
{
341341
int modified = 0;
342-
size_t written_in = 0, iv_offset_in = 0, len_offset_in = 0;
342+
size_t written_in = 0, iv_offset_in = 0, len_offset_in = 0, hdr_in = 0;
343343
size_t written_out = 0, iv_offset_out = 0, len_offset_out = 0;
344344
if (ssl->in_buf != NULL) {
345345
written_in = ssl->in_msg - ssl->in_buf;
346346
iv_offset_in = ssl->in_iv - ssl->in_buf;
347347
len_offset_in = ssl->in_len - ssl->in_buf;
348+
hdr_in = ssl->in_hdr - ssl->in_buf;
348349
if (downsizing ?
349350
ssl->in_buf_len > in_buf_new_len && ssl->in_left < in_buf_new_len :
350351
ssl->in_buf_len < in_buf_new_len) {
@@ -376,7 +377,10 @@ static void handle_buffer_resizing(mbedtls_ssl_context *ssl, int downsizing,
376377
}
377378
if (modified) {
378379
/* Update pointers here to avoid doing it twice. */
379-
mbedtls_ssl_reset_in_out_pointers(ssl);
380+
ssl->in_hdr = ssl->in_buf + hdr_in;
381+
mbedtls_ssl_update_in_pointers(ssl);
382+
mbedtls_ssl_reset_out_pointers(ssl);
383+
380384
/* Fields below might not be properly updated with record
381385
* splitting or with CID, so they are manually updated here. */
382386
ssl->out_msg = ssl->out_buf + written_out;
@@ -1274,7 +1278,8 @@ int mbedtls_ssl_setup(mbedtls_ssl_context *ssl,
12741278
goto error;
12751279
}
12761280

1277-
mbedtls_ssl_reset_in_out_pointers(ssl);
1281+
mbedtls_ssl_reset_in_pointers(ssl);
1282+
mbedtls_ssl_reset_out_pointers(ssl);
12781283

12791284
#if defined(MBEDTLS_SSL_DTLS_SRTP)
12801285
memset(&ssl->dtls_srtp_info, 0, sizeof(ssl->dtls_srtp_info));
@@ -1339,14 +1344,16 @@ void mbedtls_ssl_session_reset_msg_layer(mbedtls_ssl_context *ssl,
13391344
/* Cancel any possibly running timer */
13401345
mbedtls_ssl_set_timer(ssl, 0);
13411346

1342-
mbedtls_ssl_reset_in_out_pointers(ssl);
1347+
mbedtls_ssl_reset_in_pointers(ssl);
1348+
mbedtls_ssl_reset_out_pointers(ssl);
13431349

13441350
/* Reset incoming message parsing */
13451351
ssl->in_offt = NULL;
13461352
ssl->nb_zero = 0;
13471353
ssl->in_msgtype = 0;
13481354
ssl->in_msglen = 0;
13491355
ssl->in_hslen = 0;
1356+
ssl->in_hsfraglen = 0;
13501357
ssl->keep_current_message = 0;
13511358
ssl->transform_in = NULL;
13521359

library/ssl_tls12_server.c

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,28 +1015,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl)
10151015
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message"));
10161016
return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE;
10171017
}
1018-
{
1019-
size_t handshake_len = MBEDTLS_GET_UINT24_BE(buf, 1);
1020-
MBEDTLS_SSL_DEBUG_MSG(3, ("client hello v3, handshake len.: %u",
1021-
(unsigned) handshake_len));
1022-
1023-
/* The record layer has a record size limit of 2^14 - 1 and
1024-
* fragmentation is not supported, so buf[1] should be zero. */
1025-
if (buf[1] != 0) {
1026-
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message: %u != 0",
1027-
(unsigned) buf[1]));
1028-
return MBEDTLS_ERR_SSL_DECODE_ERROR;
1029-
}
1030-
1031-
/* We don't support fragmentation of ClientHello (yet?) */
1032-
if (msg_len != mbedtls_ssl_hs_hdr_len(ssl) + handshake_len) {
1033-
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message: %u != %u + %u",
1034-
(unsigned) msg_len,
1035-
(unsigned) mbedtls_ssl_hs_hdr_len(ssl),
1036-
(unsigned) handshake_len));
1037-
return MBEDTLS_ERR_SSL_DECODE_ERROR;
1038-
}
1039-
}
10401018

10411019
#if defined(MBEDTLS_SSL_PROTO_DTLS)
10421020
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {

0 commit comments

Comments
 (0)