mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2026-10-08 18:07:24 +00:00
Merge pull request #10576 from ronald-cron-arm/dtls-client-hello-defragmentation
Add support for (D)TLS 1.2 client hello defragmentation
This commit is contained in:
+49
-6
@@ -2967,6 +2967,49 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl)
|
||||
return MBEDTLS_ERR_SSL_INVALID_RECORD;
|
||||
}
|
||||
|
||||
if (ssl->in_msg[0] == MBEDTLS_SSL_HS_CLIENT_HELLO &&
|
||||
ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) {
|
||||
if (ssl->state == MBEDTLS_SSL_CLIENT_HELLO
|
||||
#if defined(MBEDTLS_SSL_RENEGOTIATION)
|
||||
&& ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE
|
||||
#endif
|
||||
) {
|
||||
/*
|
||||
* When establishing the connection, the client may go through
|
||||
* a series of ClientHello and HelloVerifyRequest requests and
|
||||
* responses. The server intentionally does not keep trace of
|
||||
* these initial round trips: minimum allocated ressources as
|
||||
* long as the reachability of the client has not been
|
||||
* confirmed. When receiving the "first ClientHello" from
|
||||
* server perspective, we may thus need to adapt the next
|
||||
* expected `message_seq` for the incoming and outgoing
|
||||
* handshake messages.
|
||||
*/
|
||||
ssl->handshake->in_msg_seq = recv_msg_seq;
|
||||
ssl->handshake->out_msg_seq = recv_msg_seq;
|
||||
|
||||
/* Epoch should be 0 for initial handshakes */
|
||||
if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message"));
|
||||
return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER;
|
||||
}
|
||||
|
||||
memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2,
|
||||
sizeof(ssl->cur_out_ctr) - 2);
|
||||
} else if (mbedtls_ssl_is_handshake_over(ssl) == 1) {
|
||||
/* In case of a post-handshake ClientHello that initiates a
|
||||
* renegotiation check that the handshake message sequence
|
||||
* number is zero.
|
||||
*/
|
||||
if (recv_msg_seq != 0) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message_seq: "
|
||||
"%u (expected 0)",
|
||||
recv_msg_seq));
|
||||
return MBEDTLS_ERR_SSL_DECODE_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ssl->handshake != NULL &&
|
||||
((mbedtls_ssl_is_handshake_over(ssl) == 0 &&
|
||||
recv_msg_seq != ssl->handshake->in_msg_seq) ||
|
||||
@@ -4843,14 +4886,9 @@ static int ssl_get_next_record(mbedtls_ssl_context *ssl)
|
||||
/* The record content type may change during decryption,
|
||||
* so re-read it. */
|
||||
ssl->in_msgtype = rec.type;
|
||||
/* Also update the input buffer, because unfortunately
|
||||
* the server-side ssl_parse_client_hello() reparses the
|
||||
* record header when receiving a ClientHello initiating
|
||||
* a renegotiation. */
|
||||
ssl->in_hdr[0] = rec.type;
|
||||
|
||||
ssl->in_msg = rec.buf + rec.data_offset;
|
||||
ssl->in_msglen = rec.data_len;
|
||||
MBEDTLS_PUT_UINT16_BE(rec.data_len, ssl->in_len, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5504,6 +5542,11 @@ static int ssl_tls12_handle_hs_message_post_handshake(mbedtls_ssl_context *ssl)
|
||||
ssl->renego_status = MBEDTLS_SSL_RENEGOTIATION_PENDING;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Keep the ClientHello message for ssl_parse_client_hello() */
|
||||
if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) {
|
||||
ssl->keep_current_message = 1;
|
||||
}
|
||||
ret = mbedtls_ssl_start_renegotiation(ssl);
|
||||
if (ret != MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO &&
|
||||
ret != 0) {
|
||||
|
||||
@@ -2892,13 +2892,6 @@ size_t mbedtls_ssl_get_output_max_frag_len(const mbedtls_ssl_context *ssl)
|
||||
#if defined(MBEDTLS_SSL_PROTO_DTLS)
|
||||
size_t mbedtls_ssl_get_current_mtu(const mbedtls_ssl_context *ssl)
|
||||
{
|
||||
/* Return unlimited mtu for client hello messages to avoid fragmentation. */
|
||||
if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT &&
|
||||
(ssl->state == MBEDTLS_SSL_CLIENT_HELLO ||
|
||||
ssl->state == MBEDTLS_SSL_SERVER_HELLO)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ssl->handshake == NULL || ssl->handshake->mtu == 0) {
|
||||
return ssl->mtu;
|
||||
}
|
||||
|
||||
+26
-170
@@ -869,131 +869,35 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl)
|
||||
|
||||
MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse client hello"));
|
||||
|
||||
int renegotiating;
|
||||
|
||||
#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY)
|
||||
read_record_header:
|
||||
#endif
|
||||
/*
|
||||
* If renegotiating, then the input was read with mbedtls_ssl_read_record(),
|
||||
* otherwise read it ourselves manually in order to support SSLv2
|
||||
* ClientHello, which doesn't use the same record layer format.
|
||||
* Otherwise in a scenario of TLS 1.3/TLS 1.2 version negotiation, the
|
||||
* ClientHello has been already fully fetched by the TLS 1.3 code and the
|
||||
* flag ssl->keep_current_message is raised.
|
||||
* Fetch the expected ClientHello handshake message. Do not ask
|
||||
* mbedtls_ssl_read_record() to update the handshake digest, because the
|
||||
* ClientHello may already have been read in ssl_tls13_process_client_hello()
|
||||
* or as a post-handshake message (renegotiation). In those cases we need
|
||||
* to update the digest ourselves, and it is simpler to do so
|
||||
* unconditionally than to track whether it is needed.
|
||||
*/
|
||||
renegotiating = 0;
|
||||
#if defined(MBEDTLS_SSL_RENEGOTIATION)
|
||||
renegotiating = (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE);
|
||||
#endif
|
||||
if (!renegotiating && !ssl->keep_current_message) {
|
||||
if ((ret = mbedtls_ssl_fetch_input(ssl, 5)) != 0) {
|
||||
/* No alert on a read error. */
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) {
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record ", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf = ssl->in_hdr;
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "record header", buf, mbedtls_ssl_in_hdr_len(ssl));
|
||||
|
||||
/*
|
||||
* TLS Client Hello
|
||||
* Update the handshake checksum.
|
||||
*
|
||||
* Record layer:
|
||||
* 0 . 0 message type
|
||||
* 1 . 2 protocol version
|
||||
* 3 . 11 DTLS: epoch + record sequence number
|
||||
* 3 . 4 message length
|
||||
* Note that the checksum must be updated before parsing the extensions
|
||||
* because ssl_parse_session_ticket_ext() may decrypt the ticket in place
|
||||
* and therefore modify the ClientHello message. This occurs when using
|
||||
* the Mbed TLS ssl_ticket.c implementation.
|
||||
*/
|
||||
MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message type: %d",
|
||||
buf[0]));
|
||||
|
||||
if (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message"));
|
||||
return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE;
|
||||
}
|
||||
|
||||
MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message len.: %d",
|
||||
MBEDTLS_GET_UINT16_BE(ssl->in_len, 0)));
|
||||
|
||||
MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, protocol version: [%d:%d]",
|
||||
buf[1], buf[2]));
|
||||
|
||||
/* For DTLS if this is the initial handshake, remember the client sequence
|
||||
* number to use it in our next message (RFC 6347 4.2.1) */
|
||||
#if defined(MBEDTLS_SSL_PROTO_DTLS)
|
||||
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM
|
||||
#if defined(MBEDTLS_SSL_RENEGOTIATION)
|
||||
&& ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE
|
||||
#endif
|
||||
) {
|
||||
/* Epoch should be 0 for initial handshakes */
|
||||
if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message"));
|
||||
return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER;
|
||||
}
|
||||
|
||||
memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2,
|
||||
sizeof(ssl->cur_out_ctr) - 2);
|
||||
|
||||
#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY)
|
||||
if (mbedtls_ssl_dtls_replay_check(ssl) != 0) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("replayed record, discarding"));
|
||||
ssl->next_record_offset = 0;
|
||||
ssl->in_left = 0;
|
||||
goto read_record_header;
|
||||
}
|
||||
|
||||
/* No MAC to check yet, so we can update right now */
|
||||
mbedtls_ssl_dtls_replay_update(ssl);
|
||||
#endif
|
||||
}
|
||||
#endif /* MBEDTLS_SSL_PROTO_DTLS */
|
||||
|
||||
msg_len = MBEDTLS_GET_UINT16_BE(ssl->in_len, 0);
|
||||
|
||||
#if defined(MBEDTLS_SSL_RENEGOTIATION)
|
||||
if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) {
|
||||
/* Set by mbedtls_ssl_read_record() */
|
||||
msg_len = ssl->in_hslen;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (ssl->keep_current_message) {
|
||||
ssl->keep_current_message = 0;
|
||||
} else {
|
||||
if (msg_len > MBEDTLS_SSL_IN_CONTENT_LEN) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message"));
|
||||
return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER;
|
||||
}
|
||||
|
||||
if ((ret = mbedtls_ssl_fetch_input(ssl,
|
||||
mbedtls_ssl_in_hdr_len(ssl) + msg_len)) != 0) {
|
||||
MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Done reading this record, get ready for the next one */
|
||||
#if defined(MBEDTLS_SSL_PROTO_DTLS)
|
||||
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
|
||||
ssl->next_record_offset = msg_len + mbedtls_ssl_in_hdr_len(ssl);
|
||||
} else
|
||||
#endif
|
||||
ssl->in_left = 0;
|
||||
}
|
||||
ret = mbedtls_ssl_update_handshake_status(ssl);
|
||||
if (0 != ret) {
|
||||
MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf = ssl->in_msg;
|
||||
|
||||
MBEDTLS_SSL_DEBUG_BUF(4, "record contents", buf, msg_len);
|
||||
|
||||
ret = ssl->handshake->update_checksum(ssl, buf, msg_len);
|
||||
if (0 != ret) {
|
||||
MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret);
|
||||
return ret;
|
||||
}
|
||||
msg_len = ssl->in_hslen;
|
||||
|
||||
/*
|
||||
* Handshake layer:
|
||||
@@ -1003,64 +907,12 @@ read_record_header:
|
||||
* 6 . 8 DTLS only: fragment offset
|
||||
* 9 . 11 DTLS only: fragment length
|
||||
*/
|
||||
if (msg_len < mbedtls_ssl_hs_hdr_len(ssl)) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message"));
|
||||
return MBEDTLS_ERR_SSL_DECODE_ERROR;
|
||||
}
|
||||
|
||||
MBEDTLS_SSL_DEBUG_MSG(3, ("client hello v3, handshake type: %d", buf[0]));
|
||||
|
||||
if (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO) {
|
||||
if ((ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) ||
|
||||
(buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO)) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message"));
|
||||
return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_SSL_PROTO_DTLS)
|
||||
if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) {
|
||||
/*
|
||||
* Copy the client's handshake message_seq on initial handshakes,
|
||||
* check sequence number on renego.
|
||||
*/
|
||||
#if defined(MBEDTLS_SSL_RENEGOTIATION)
|
||||
if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) {
|
||||
/* This couldn't be done in ssl_prepare_handshake_record() */
|
||||
unsigned int cli_msg_seq = (unsigned int) MBEDTLS_GET_UINT16_BE(ssl->in_msg, 4);
|
||||
if (cli_msg_seq != ssl->handshake->in_msg_seq) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message_seq: "
|
||||
"%u (expected %u)", cli_msg_seq,
|
||||
ssl->handshake->in_msg_seq));
|
||||
return MBEDTLS_ERR_SSL_DECODE_ERROR;
|
||||
}
|
||||
|
||||
ssl->handshake->in_msg_seq++;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
unsigned int cli_msg_seq = (unsigned int) MBEDTLS_GET_UINT16_BE(ssl->in_msg, 4);
|
||||
ssl->handshake->out_msg_seq = cli_msg_seq;
|
||||
ssl->handshake->in_msg_seq = cli_msg_seq + 1;
|
||||
}
|
||||
{
|
||||
/*
|
||||
* For now we don't support fragmentation, so make sure
|
||||
* fragment_offset == 0 and fragment_length == length
|
||||
*/
|
||||
size_t fragment_offset, fragment_length, length;
|
||||
fragment_offset = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 6);
|
||||
fragment_length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 9);
|
||||
length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 1);
|
||||
MBEDTLS_SSL_DEBUG_MSG(
|
||||
4, ("fragment_offset=%u fragment_length=%u length=%u",
|
||||
(unsigned) fragment_offset, (unsigned) fragment_length,
|
||||
(unsigned) length));
|
||||
if (fragment_offset != 0 || length != fragment_length) {
|
||||
MBEDTLS_SSL_DEBUG_MSG(1, ("ClientHello fragmentation not supported"));
|
||||
return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* MBEDTLS_SSL_PROTO_DTLS */
|
||||
|
||||
buf += mbedtls_ssl_hs_hdr_len(ssl);
|
||||
msg_len -= mbedtls_ssl_hs_hdr_len(ssl);
|
||||
|
||||
@@ -1390,7 +1242,11 @@ read_record_header:
|
||||
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
|
||||
case MBEDTLS_TLS_EXT_SESSION_TICKET:
|
||||
MBEDTLS_SSL_DEBUG_MSG(3, ("found session ticket extension"));
|
||||
|
||||
/*
|
||||
* If the Mbed TLS ssl_ticket.c implementation is used, the
|
||||
* ticket is decrypted in place. This modifies the ClientHello
|
||||
* message in the input buffer.
|
||||
*/
|
||||
ret = ssl_parse_session_ticket_ext(ssl, ext + 4, ext_size);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user