Implement parsing of CID-based records

Previously, ssl_get_next_record() would fetch 13 Bytes for the
record header and hand over to ssl_parse_record_header() to parse
and validate these. With the introduction of CID-based records, the
record length is not known in advance, and parsing and validating
must happen at the same time. ssl_parse_record_header() is therefore
rewritten in the following way:
1. Fetch and validate record content type and version.
2. If the record content type indicates a record including a CID,
   adjust the record header pointers accordingly; here, we use the
   statically configured length of incoming CIDs, avoiding any
   elaborate CID parsing mechanism or dependency on the record
   epoch, as explained in the previous commit.
3. Fetch the rest of the record header (note: this doesn't actually
   fetch anything, but makes sure that the datagram fetched in the
   earlier call to ssl_fetch_input() contains enough data).
4. Parse and validate the rest of the record header as before.
This commit is contained in:
Hanno Becker 2019-05-08 12:03:28 +01:00
parent 6430faf098
commit ca59c2b486

View file

@ -4850,19 +4850,38 @@ static int ssl_check_record_type( uint8_t record_type )
static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
{
int major_ver, minor_ver;
int ret;
MBEDTLS_SSL_DEBUG_BUF( 4, "input record header", ssl->in_hdr, mbedtls_ssl_in_hdr_len( ssl ) );
/* Parse and validate record content type and version */
ssl->in_msgtype = ssl->in_hdr[0];
ssl->in_msglen = ( ssl->in_len[0] << 8 ) | ssl->in_len[1];
mbedtls_ssl_read_version( &major_ver, &minor_ver, ssl->conf->transport, ssl->in_hdr + 1 );
MBEDTLS_SSL_DEBUG_MSG( 3, ( "input record: msgtype = %d, "
"version = [%d:%d], msglen = %d",
ssl->in_msgtype,
major_ver, minor_ver, ssl->in_msglen ) );
/* Check record type */
#if defined(MBEDTLS_SSL_CID)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
ssl->in_msgtype == MBEDTLS_SSL_MSG_CID &&
ssl->conf->cid_len != 0 )
{
/* Shift pointers to account for record header including CID
* struct {
* ContentType special_type = tls12_cid;
* ProtocolVersion version;
* uint16 epoch;
* uint48 sequence_number;
* opaque cid[cid_length]; // New field
* uint16 length;
* opaque enc_content[DTLSCiphertext.length];
* } DTLSCiphertext;
*/
/* So far, we only support static CID lengths
* fixed in the configuration. */
ssl->in_len = ssl->in_cid + ssl->conf->cid_len;
ssl->in_iv = ssl->in_msg = ssl->in_len + 2;
}
else
#endif /* MBEDTLS_SSL_CID */
if( ssl_check_record_type( ssl->in_msgtype ) )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "unknown record type" ) );
@ -4891,7 +4910,24 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
/* Check length against the size of our buffer */
/* Now that the total length of the record header is known, ensure
* that the current datagram is large enough to hold it.
* This would fail, for example, if we received a datagram of
* size 13 + n Bytes where n is less than the size of incoming CIDs. */
ret = mbedtls_ssl_fetch_input( ssl, mbedtls_ssl_in_hdr_len( ssl ) );
if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_fetch_input", ret );
return( ret );
}
MBEDTLS_SSL_DEBUG_BUF( 4, "input record header", ssl->in_hdr, mbedtls_ssl_in_hdr_len( ssl ) );
/* Parse and validate record length
* This must happen after the CID parsing because
* its position in the record header depends on
* the presence of a CID. */
ssl->in_msglen = ( ssl->in_len[0] << 8 ) | ssl->in_len[1];
if( ssl->in_msglen > MBEDTLS_SSL_IN_BUFFER_LEN
- (size_t)( ssl->in_msg - ssl->in_buf ) )
{
@ -4899,6 +4935,11 @@ static int ssl_parse_record_header( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
MBEDTLS_SSL_DEBUG_MSG( 3, ( "input record: msgtype = %d, "
"version = [%d:%d], msglen = %d",
ssl->in_msgtype,
major_ver, minor_ver, ssl->in_msglen ) );
/*
* DTLS-related tests.
* Check epoch before checking length constraint because
@ -5861,7 +5902,16 @@ static int ssl_get_next_record( mbedtls_ssl_context *ssl )
return( ret );
#endif /* MBEDTLS_SSL_PROTO_DTLS */
if( ( ret = mbedtls_ssl_fetch_input( ssl, mbedtls_ssl_in_hdr_len( ssl ) ) ) != 0 )
/* Reset in pointers to default state for TLS/DTLS records,
* assuming no CID and no offset between record content and
* record plaintext. */
ssl_update_in_pointers( ssl );
/* Ensure that we have enough space available for the default form
* of TLS / DTLS record headers (5 Bytes for TLS, 13 Bytes for DTLS,
* with no space for CIDs counted in). */
ret = mbedtls_ssl_fetch_input( ssl, mbedtls_ssl_in_hdr_len( ssl ) );
if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_fetch_input", ret );
return( ret );