mirror of https://github.com/ipxe/ipxe.git
[tls] Do not access beyond the end of a 24-bit integer
The current implementation handles big-endian 24-bit integers (which occur in several TLS record types) by treating them as big-endian 32-bit integers which are shifted by 8 bits. This can result in "Invalid read" errors when running under valgrind, if the 24-bit field happens to be exactly at the end of an I/O buffer. Fix by ensuring that we touch only the three bytes which comprise the 24-bit integer. Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/39/head
parent
2849932c48
commit
1ac7434111
|
@ -179,20 +179,29 @@ static void tls_clear_cipher ( struct tls_session *tls,
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** A TLS 24-bit integer
|
||||||
|
*
|
||||||
|
* TLS uses 24-bit integers in several places, which are awkward to
|
||||||
|
* parse in C.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/** High byte */
|
||||||
|
uint8_t high;
|
||||||
|
/** Low word */
|
||||||
|
uint16_t low;
|
||||||
|
} __attribute__ (( packed )) tls24_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract 24-bit field value
|
* Extract 24-bit field value
|
||||||
*
|
*
|
||||||
* @v field24 24-bit field
|
* @v field24 24-bit field
|
||||||
* @ret value Field value
|
* @ret value Field value
|
||||||
*
|
*
|
||||||
* TLS uses 24-bit integers in several places, which are awkward to
|
|
||||||
* parse in C.
|
|
||||||
*/
|
*/
|
||||||
static inline __attribute__ (( always_inline )) unsigned long
|
static inline __attribute__ (( always_inline )) unsigned long
|
||||||
tls_uint24 ( const uint8_t field24[3] ) {
|
tls_uint24 ( const tls24_t *field24 ) {
|
||||||
const uint32_t *field32 __attribute__ (( may_alias )) =
|
|
||||||
( ( const void * ) field24 );
|
return ( ( field24->high << 16 ) | be16_to_cpu ( field24->low ) );
|
||||||
return ( be32_to_cpu ( *field32 ) >> 8 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -200,13 +209,11 @@ tls_uint24 ( const uint8_t field24[3] ) {
|
||||||
*
|
*
|
||||||
* @v field24 24-bit field
|
* @v field24 24-bit field
|
||||||
* @v value Field value
|
* @v value Field value
|
||||||
*
|
|
||||||
* The field must be pre-zeroed.
|
|
||||||
*/
|
*/
|
||||||
static void tls_set_uint24 ( uint8_t field24[3], unsigned long value ) {
|
static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) {
|
||||||
uint32_t *field32 __attribute__ (( may_alias )) =
|
|
||||||
( ( void * ) field24 );
|
field24->high = ( value >> 16 );
|
||||||
*field32 |= cpu_to_be32 ( value << 8 );
|
field24->low = cpu_to_be16 ( value );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1038,9 +1045,9 @@ static int tls_send_client_hello ( struct tls_session *tls ) {
|
||||||
static int tls_send_certificate ( struct tls_session *tls ) {
|
static int tls_send_certificate ( struct tls_session *tls ) {
|
||||||
struct {
|
struct {
|
||||||
uint32_t type_length;
|
uint32_t type_length;
|
||||||
uint8_t length[3];
|
tls24_t length;
|
||||||
struct {
|
struct {
|
||||||
uint8_t length[3];
|
tls24_t length;
|
||||||
uint8_t data[ tls->cert->raw.len ];
|
uint8_t data[ tls->cert->raw.len ];
|
||||||
} __attribute__ (( packed )) certificates[1];
|
} __attribute__ (( packed )) certificates[1];
|
||||||
} __attribute__ (( packed )) *certificate;
|
} __attribute__ (( packed )) *certificate;
|
||||||
|
@ -1058,9 +1065,9 @@ static int tls_send_certificate ( struct tls_session *tls ) {
|
||||||
( cpu_to_le32 ( TLS_CERTIFICATE ) |
|
( cpu_to_le32 ( TLS_CERTIFICATE ) |
|
||||||
htonl ( sizeof ( *certificate ) -
|
htonl ( sizeof ( *certificate ) -
|
||||||
sizeof ( certificate->type_length ) ) );
|
sizeof ( certificate->type_length ) ) );
|
||||||
tls_set_uint24 ( certificate->length,
|
tls_set_uint24 ( &certificate->length,
|
||||||
sizeof ( certificate->certificates ) );
|
sizeof ( certificate->certificates ) );
|
||||||
tls_set_uint24 ( certificate->certificates[0].length,
|
tls_set_uint24 ( &certificate->certificates[0].length,
|
||||||
sizeof ( certificate->certificates[0].data ) );
|
sizeof ( certificate->certificates[0].data ) );
|
||||||
memcpy ( certificate->certificates[0].data,
|
memcpy ( certificate->certificates[0].data,
|
||||||
tls->cert->raw.data,
|
tls->cert->raw.data,
|
||||||
|
@ -1412,7 +1419,7 @@ static int tls_parse_chain ( struct tls_session *tls,
|
||||||
const void *data, size_t len ) {
|
const void *data, size_t len ) {
|
||||||
const void *end = ( data + len );
|
const void *end = ( data + len );
|
||||||
const struct {
|
const struct {
|
||||||
uint8_t length[3];
|
tls24_t length;
|
||||||
uint8_t data[0];
|
uint8_t data[0];
|
||||||
} __attribute__ (( packed )) *certificate;
|
} __attribute__ (( packed )) *certificate;
|
||||||
size_t certificate_len;
|
size_t certificate_len;
|
||||||
|
@ -1436,7 +1443,7 @@ static int tls_parse_chain ( struct tls_session *tls,
|
||||||
|
|
||||||
/* Extract raw certificate data */
|
/* Extract raw certificate data */
|
||||||
certificate = data;
|
certificate = data;
|
||||||
certificate_len = tls_uint24 ( certificate->length );
|
certificate_len = tls_uint24 ( &certificate->length );
|
||||||
next = ( certificate->data + certificate_len );
|
next = ( certificate->data + certificate_len );
|
||||||
if ( next > end ) {
|
if ( next > end ) {
|
||||||
DBGC ( tls, "TLS %p overlength certificate:\n", tls );
|
DBGC ( tls, "TLS %p overlength certificate:\n", tls );
|
||||||
|
@ -1482,10 +1489,10 @@ static int tls_parse_chain ( struct tls_session *tls,
|
||||||
static int tls_new_certificate ( struct tls_session *tls,
|
static int tls_new_certificate ( struct tls_session *tls,
|
||||||
const void *data, size_t len ) {
|
const void *data, size_t len ) {
|
||||||
const struct {
|
const struct {
|
||||||
uint8_t length[3];
|
tls24_t length;
|
||||||
uint8_t certificates[0];
|
uint8_t certificates[0];
|
||||||
} __attribute__ (( packed )) *certificate = data;
|
} __attribute__ (( packed )) *certificate = data;
|
||||||
size_t certificates_len = tls_uint24 ( certificate->length );
|
size_t certificates_len = tls_uint24 ( &certificate->length );
|
||||||
const void *end = ( certificate->certificates + certificates_len );
|
const void *end = ( certificate->certificates + certificates_len );
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
@ -1634,11 +1641,11 @@ static int tls_new_handshake ( struct tls_session *tls,
|
||||||
while ( data != end ) {
|
while ( data != end ) {
|
||||||
const struct {
|
const struct {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t length[3];
|
tls24_t length;
|
||||||
uint8_t payload[0];
|
uint8_t payload[0];
|
||||||
} __attribute__ (( packed )) *handshake = data;
|
} __attribute__ (( packed )) *handshake = data;
|
||||||
const void *payload = &handshake->payload;
|
const void *payload = &handshake->payload;
|
||||||
size_t payload_len = tls_uint24 ( handshake->length );
|
size_t payload_len = tls_uint24 ( &handshake->length );
|
||||||
const void *next = ( payload + payload_len );
|
const void *next = ( payload + payload_len );
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
|
|
Loading…
Reference in New Issue