From ca59c2b486a04dd41b97bd03f10081c457a938ec Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 8 May 2019 12:03:28 +0100 Subject: [PATCH] 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. --- library/ssl_tls.c | 68 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 61157432f..968ce13fa 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -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 );