Merge remote-tracking branch 'restricted/iotssl-1398_backport-1.3' into mbedtls-1.3-restricted

* restricted/iotssl-1398_backport-1.3:
  Add ChangeLog entry
  Ensure application data records are not kept when fully processed
  Add hard assertion to ssl_read_record
  Fix mbedtls_ssl_read
  Simplify retaining of messages for future processing
This commit is contained in:
Manuel Pégourié-Gonnard 2017-06-09 17:06:43 +02:00
commit b870179c3c
4 changed files with 123 additions and 93 deletions

View file

@ -3,6 +3,11 @@ mbed TLS ChangeLog (Sorted per branch, date)
= mbed TLS 1.3.20 released xxxx-xx-xx = mbed TLS 1.3.20 released xxxx-xx-xx
Security Security
* Fixed unlimited overread of heap-based buffer in ssl_read().
The issue could only happen client-side with renegotiation enabled.
Could result in DoS (application crash) or information leak
(if the application layer sent data read from ssl_read()
back to the server or to a third party). Can be triggered remotely.
* Add exponent blinding to RSA private operations as a countermeasure * Add exponent blinding to RSA private operations as a countermeasure
against side-channel attacks like the cache attack described in against side-channel attacks like the cache attack described in
https://arxiv.org/abs/1702.08719v2. https://arxiv.org/abs/1702.08719v2.

View file

@ -846,7 +846,9 @@ struct _ssl_context
size_t in_hslen; /*!< current handshake message length */ size_t in_hslen; /*!< current handshake message length */
int nb_zero; /*!< # of 0-length encrypted messages */ int nb_zero; /*!< # of 0-length encrypted messages */
int record_read; /*!< record is already present */
int keep_current_message; /*!< drop or reuse current message
on next call to record layer? */
/* /*
* Record layer (outgoing data) * Record layer (outgoing data)

View file

@ -1195,6 +1195,8 @@ static int ssl_parse_server_hello( ssl_context *ssl )
} }
SSL_DEBUG_MSG( 1, ( "non-handshake message during renego" ) ); SSL_DEBUG_MSG( 1, ( "non-handshake message during renego" ) );
ssl->keep_current_message = 1;
return( POLARSSL_ERR_SSL_WAITING_SERVER_HELLO_RENEGO ); return( POLARSSL_ERR_SSL_WAITING_SERVER_HELLO_RENEGO );
} }
#endif /* POLARSSL_SSL_RENEGOTIATION */ #endif /* POLARSSL_SSL_RENEGOTIATION */
@ -1943,7 +1945,9 @@ static int ssl_parse_server_key_exchange( ssl_context *ssl )
if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK ||
ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK ) ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK )
{ {
ssl->record_read = 1; /* Current message is probably either
* CertificateRequest or ServerHelloDone */
ssl->keep_current_message = 1;
goto exit; goto exit;
} }
@ -2260,8 +2264,7 @@ static int ssl_parse_certificate_request( ssl_context *ssl )
* n+4 .. ... Distinguished Name #1 * n+4 .. ... Distinguished Name #1
* ... .. ... length of DN 2, etc. * ... .. ... length of DN 2, etc.
*/ */
if( ssl->record_read == 0 )
{
if( ( ret = ssl_read_record( ssl ) ) != 0 ) if( ( ret = ssl_read_record( ssl ) ) != 0 )
{ {
SSL_DEBUG_RET( 1, "ssl_read_record", ret ); SSL_DEBUG_RET( 1, "ssl_read_record", ret );
@ -2274,22 +2277,18 @@ static int ssl_parse_certificate_request( ssl_context *ssl )
return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE );
} }
ssl->record_read = 1;
}
ssl->client_auth = 0;
ssl->state++; ssl->state++;
ssl->client_auth = ( ssl->in_msg[0] == SSL_HS_CERTIFICATE_REQUEST );
if( ssl->in_msg[0] == SSL_HS_CERTIFICATE_REQUEST )
ssl->client_auth++;
SSL_DEBUG_MSG( 3, ( "got %s certificate request", SSL_DEBUG_MSG( 3, ( "got %s certificate request",
ssl->client_auth ? "a" : "no" ) ); ssl->client_auth ? "a" : "no" ) );
if( ssl->client_auth == 0 ) if( ssl->client_auth == 0 )
{
/* Current message is probably the ServerHelloDone */
ssl->keep_current_message = 1;
goto exit; goto exit;
}
ssl->record_read = 0;
// TODO: handshake_failure alert for an anonymous server to request // TODO: handshake_failure alert for an anonymous server to request
// client authentication // client authentication
@ -2386,8 +2385,6 @@ static int ssl_parse_server_hello_done( ssl_context *ssl )
SSL_DEBUG_MSG( 2, ( "=> parse server hello done" ) ); SSL_DEBUG_MSG( 2, ( "=> parse server hello done" ) );
if( ssl->record_read == 0 )
{
if( ( ret = ssl_read_record( ssl ) ) != 0 ) if( ( ret = ssl_read_record( ssl ) ) != 0 )
{ {
SSL_DEBUG_RET( 1, "ssl_read_record", ret ); SSL_DEBUG_RET( 1, "ssl_read_record", ret );
@ -2399,8 +2396,6 @@ static int ssl_parse_server_hello_done( ssl_context *ssl )
SSL_DEBUG_MSG( 1, ( "bad server hello done message" ) ); SSL_DEBUG_MSG( 1, ( "bad server hello done message" ) );
return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE );
} }
}
ssl->record_read = 0;
if( ssl->in_hslen != 4 || if( ssl->in_hslen != 4 ||
ssl->in_msg[0] != SSL_HS_SERVER_HELLO_DONE ) ssl->in_msg[0] != SSL_HS_SERVER_HELLO_DONE )

View file

@ -2169,14 +2169,30 @@ int ssl_read_record( ssl_context *ssl )
SSL_DEBUG_MSG( 2, ( "=> read record" ) ); SSL_DEBUG_MSG( 2, ( "=> read record" ) );
if( ssl->in_hslen != 0 && if( ssl->keep_current_message == 1 )
ssl->in_hslen < ssl->in_msglen )
{ {
SSL_DEBUG_MSG( 2, ( "reuse previously read message" ) );
SSL_DEBUG_MSG( 2, ( "<= read record" ) );
ssl->keep_current_message = 0;
return( 0 );
}
if( ssl->in_hslen != 0 )
{
if( ssl->in_offt != NULL )
{
SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( POLARSSL_ERR_SSL_INTERNAL_ERROR );
}
/* /*
* Get next Handshake message in the current record * Get next Handshake message in the current record
*/ */
ssl->in_msglen -= ssl->in_hslen;
if( ssl->in_hslen < ssl->in_msglen )
{
ssl->in_msglen -= ssl->in_hslen;
memmove( ssl->in_msg, ssl->in_msg + ssl->in_hslen, memmove( ssl->in_msg, ssl->in_msg + ssl->in_hslen,
ssl->in_msglen ); ssl->in_msglen );
@ -2205,11 +2221,30 @@ int ssl_read_record( ssl_context *ssl )
return( 0 ); return( 0 );
} }
ssl->in_msglen = 0;
ssl->in_hslen = 0; ssl->in_hslen = 0;
}
else if( ssl->in_offt != NULL )
{
return( 0 );
}
else
{
ssl->in_msglen = 0;
}
/* /*
* Read the record header and validate it * Fetch and decode new record if current one is fully consumed.
*/ */
if( ssl->in_msglen > 0 )
{
/* There's something left to be processed in the current record. */
return( 0 );
}
/* Need to fetch a new record */
read_record_header: read_record_header:
if( ( ret = ssl_fetch_input( ssl, 5 ) ) != 0 ) if( ( ret = ssl_fetch_input( ssl, 5 ) ) != 0 )
{ {
@ -3750,7 +3785,7 @@ int ssl_session_reset( ssl_context *ssl )
ssl->in_hslen = 0; ssl->in_hslen = 0;
ssl->nb_zero = 0; ssl->nb_zero = 0;
ssl->record_read = 0; ssl->keep_current_message = 0;
ssl->out_msg = ssl->out_ctr + 13; ssl->out_msg = ssl->out_ctr + 13;
ssl->out_msgtype = 0; ssl->out_msgtype = 0;
@ -4642,13 +4677,15 @@ static int ssl_check_ctr_renegotiate( ssl_context *ssl )
*/ */
int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len ) int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len )
{ {
int ret, record_read = 0; int ret;
size_t n; size_t n;
SSL_DEBUG_MSG( 2, ( "=> read" ) ); SSL_DEBUG_MSG( 2, ( "=> read" ) );
#if defined(POLARSSL_SSL_RENEGOTIATION) #if defined(POLARSSL_SSL_RENEGOTIATION)
if( ( ret = ssl_check_ctr_renegotiate( ssl ) ) != 0 ) ret = ssl_check_ctr_renegotiate( ssl );
if( ret != POLARSSL_ERR_SSL_WAITING_SERVER_HELLO_RENEGO &&
ret != 0 )
{ {
SSL_DEBUG_RET( 1, "ssl_check_ctr_renegotiate", ret ); SSL_DEBUG_RET( 1, "ssl_check_ctr_renegotiate", ret );
return( ret ); return( ret );
@ -4658,11 +4695,8 @@ int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len )
if( ssl->state != SSL_HANDSHAKE_OVER ) if( ssl->state != SSL_HANDSHAKE_OVER )
{ {
ret = ssl_handshake( ssl ); ret = ssl_handshake( ssl );
if( ret == POLARSSL_ERR_SSL_WAITING_SERVER_HELLO_RENEGO ) if( ret != POLARSSL_ERR_SSL_WAITING_SERVER_HELLO_RENEGO &&
{ ret != 0 )
record_read = 1;
}
else if( ret != 0 )
{ {
SSL_DEBUG_RET( 1, "ssl_handshake", ret ); SSL_DEBUG_RET( 1, "ssl_handshake", ret );
return( ret ); return( ret );
@ -4670,8 +4704,6 @@ int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len )
} }
if( ssl->in_offt == NULL ) if( ssl->in_offt == NULL )
{
if( ! record_read )
{ {
if( ( ret = ssl_read_record( ssl ) ) != 0 ) if( ( ret = ssl_read_record( ssl ) ) != 0 )
{ {
@ -4681,7 +4713,6 @@ int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len )
SSL_DEBUG_RET( 1, "ssl_read_record", ret ); SSL_DEBUG_RET( 1, "ssl_read_record", ret );
return( ret ); return( ret );
} }
}
if( ssl->in_msglen == 0 && if( ssl->in_msglen == 0 &&
ssl->in_msgtype == SSL_MSG_APPLICATION_DATA ) ssl->in_msgtype == SSL_MSG_APPLICATION_DATA )
@ -4754,20 +4785,14 @@ int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len )
else else
{ {
ret = ssl_start_renegotiation( ssl ); ret = ssl_start_renegotiation( ssl );
if( ret == POLARSSL_ERR_SSL_WAITING_SERVER_HELLO_RENEGO ) if( ret != POLARSSL_ERR_SSL_WAITING_SERVER_HELLO_RENEGO &&
{ ret != 0 )
record_read = 1;
}
else if( ret != 0 )
{ {
SSL_DEBUG_RET( 1, "ssl_start_renegotiation", ret ); SSL_DEBUG_RET( 1, "ssl_start_renegotiation", ret );
return( ret ); return( ret );
} }
} }
/* If a non-handshake record was read during renego, fallthrough,
* else tell the user they should call ssl_read() again */
if( ! record_read )
return( POLARSSL_ERR_NET_WANT_READ ); return( POLARSSL_ERR_NET_WANT_READ );
} }
else if( ssl->renegotiation == SSL_RENEGOTIATION_PENDING ) else if( ssl->renegotiation == SSL_RENEGOTIATION_PENDING )
@ -4807,8 +4832,11 @@ int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len )
ssl->in_msglen -= n; ssl->in_msglen -= n;
if( ssl->in_msglen == 0 ) if( ssl->in_msglen == 0 )
{
/* all bytes consumed */ /* all bytes consumed */
ssl->in_offt = NULL; ssl->in_offt = NULL;
ssl->keep_current_message = 0;
}
else else
/* more data available */ /* more data available */
ssl->in_offt += n; ssl->in_offt += n;