From: Nikos Mavrogiannopoulos Date: Fri, 22 Nov 2013 21:28:38 +0000 (+0100) Subject: Corrected bug which affected compressed records. X-Git-Tag: gnutls_3_3_0pre0~558 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=172ae00887559fa5ba9a3bdc41d9eccb4844b077;p=thirdparty%2Fgnutls.git Corrected bug which affected compressed records. Less space was provided for decryption than the required causing disconnection issues when compression was used. The issue was pointed by Frank Zschockelt. Also replaced the macros MAX_RECORD_RECV_SIZE and MAX_RECV_SIZE with max_decrypted_size() and max_record_recv_size(). --- diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c index 4ea0cc94b6..7f3b5a4147 100644 --- a/lib/gnutls_buffers.c +++ b/lib/gnutls_buffers.c @@ -185,8 +185,8 @@ _gnutls_dgram_read(gnutls_session_t session, mbuffer_st ** bufel, ssize_t i, ret; uint8_t *ptr; struct timespec t1, t2; - size_t max_size = get_max_decrypted_data(session); - size_t recv_size = MAX_RECV_SIZE(session); + size_t max_size = max_record_recv_size(session); + size_t recv_size = max_record_recv_size(session); gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr; unsigned int diff; @@ -261,7 +261,7 @@ _gnutls_stream_read(gnutls_session_t session, mbuffer_st ** bufel, { size_t left; ssize_t i = 0; - size_t max_size = get_max_decrypted_data(session); + size_t max_size = max_record_recv_size(session); uint8_t *ptr; gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr; int ret; @@ -436,7 +436,7 @@ _gnutls_writev(gnutls_session_t session, const giovec_t * giovec, * This function is like recv(with MSG_PEEK). But it does not return -1 on error. * It does return gnutls_errno instead. * This function reads data from the socket and keeps them in a buffer, of up to - * MAX_RECV_SIZE. + * max_record_recv_size. * * This is not a general purpose function. It returns EXACTLY the data requested, * which are stored in a local (in the session) buffer. @@ -454,7 +454,7 @@ _gnutls_io_read_buffered(gnutls_session_t session, size_t total, mbuffer_st *bufel = NULL; size_t recvdata, readsize; - if (total > MAX_RECV_SIZE(session) || total == 0) { + if (total > max_record_recv_size(session) || total == 0) { gnutls_assert(); /* internal error */ return GNUTLS_E_INVALID_REQUEST; } @@ -483,7 +483,7 @@ _gnutls_io_read_buffered(gnutls_session_t session, size_t total, * receive are longer than the maximum receive buffer size. */ if ((session->internals.record_recv_buffer.byte_length + - recvdata) > MAX_RECV_SIZE(session)) { + recvdata) > max_record_recv_size(session)) { gnutls_assert(); /* internal error */ return GNUTLS_E_INVALID_REQUEST; } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index c1ce4b88a3..21d2fc938b 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -177,12 +177,14 @@ typedef enum record_flush_t { #define RECORD_HEADER_SIZE(session) (IS_DTLS(session) ? DTLS_RECORD_HEADER_SIZE : TLS_RECORD_HEADER_SIZE) #define MAX_RECORD_HEADER_SIZE DTLS_RECORD_HEADER_SIZE -#define MAX_RECORD_SEND_SIZE(session) (IS_DTLS(session)?((size_t)gnutls_dtls_get_mtu(session)):(size_t)session->security_parameters.max_record_send_size+MAX_RECORD_OVERHEAD(session)) -#define MAX_RECORD_RECV_SIZE(session) ((size_t)session->security_parameters.max_record_recv_size) +/* The following macro is used to calculate the overhead when sending. + * when receiving we use a different way as there are implementations that + * store more data than allowed. + */ +#define MAX_RECORD_SEND_OVERHEAD(session) (MAX_CIPHER_BLOCK_SIZE/*iv*/+MAX_PAD_SIZE+(gnutls_compression_get(session)!=GNUTLS_COMP_NULL)?EXTRA_COMP_SIZE:0+MAX_HASH_SIZE/*MAC*/) +#define MAX_RECORD_SEND_SIZE(session) (IS_DTLS(session)?((size_t)gnutls_dtls_get_mtu(session)):(size_t)session->security_parameters.max_record_send_size+MAX_RECORD_SEND_OVERHEAD(session)) #define MAX_PAD_SIZE 255 #define EXTRA_COMP_SIZE 2048 -#define MAX_RECORD_OVERHEAD(session) (MAX_CIPHER_BLOCK_SIZE/*iv*/+MAX_PAD_SIZE+(gnutls_compression_get(session)!=GNUTLS_COMP_NULL)?EXTRA_COMP_SIZE:0+MAX_HASH_SIZE/*MAC*/) -#define MAX_RECV_SIZE(session) (MAX_RECORD_OVERHEAD(session)+MAX_RECORD_RECV_SIZE(session)+RECORD_HEADER_SIZE(session)) #define TLS_HANDSHAKE_HEADER_SIZE 4 #define DTLS_HANDSHAKE_HEADER_SIZE (TLS_HANDSHAKE_HEADER_SIZE+8) diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c index b597637884..68d868abad 100644 --- a/lib/gnutls_record.c +++ b/lib/gnutls_record.c @@ -1088,7 +1088,7 @@ static int recv_headers(gnutls_session_t session, content_type_t type, record_check_version(session, htype, record->version)) < 0) return gnutls_assert_val(ret); - if (record->length > MAX_RECV_SIZE(session)) { + if (record->length > max_record_recv_size(session)) { _gnutls_audit_log (session, "Received packet with illegal length: %u\n", (unsigned int) record->length); @@ -1195,9 +1195,11 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type, return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); /* We allocate the maximum possible to allow few compressed bytes to expand to a - * full record. + * full record. Moreover we add space for any pad and the MAC (in case + * they are encrypted). */ - decrypted = _mbuffer_alloc(record.length, record.length); + ret = max_decrypted_size(session) + MAX_PAD_SIZE + MAX_HASH_SIZE; + decrypted = _mbuffer_alloc(ret, ret); if (decrypted == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); diff --git a/lib/gnutls_record.h b/lib/gnutls_record.h index 1a515610b4..12fcc9b362 100644 --- a/lib/gnutls_record.h +++ b/lib/gnutls_record.h @@ -47,17 +47,30 @@ ssize_t _gnutls_recv_int(gnutls_session_t session, content_type_t type, gnutls_handshake_description_t, uint8_t * data, size_t sizeofdata, void *seq, unsigned int ms); -inline static int get_max_decrypted_data(gnutls_session_t session) +inline static unsigned max_record_recv_size(gnutls_session_t session) { - int ret; + unsigned size; - ret = MAX_RECORD_RECV_SIZE(session) + MAX_RECORD_OVERHEAD(session); + size = MAX_CIPHER_BLOCK_SIZE /*iv*/ + MAX_PAD_SIZE + MAX_HASH_SIZE/*MAC*/; + + if (gnutls_compression_get(session)!=GNUTLS_COMP_NULL || session->internals.priorities.allow_large_records != 0) + size += EXTRA_COMP_SIZE; - if (session->internals.priorities.allow_large_records != 0 && - gnutls_compression_get(session) == GNUTLS_COMP_NULL) - ret += EXTRA_COMP_SIZE; + size += session->security_parameters.max_record_recv_size + RECORD_HEADER_SIZE(session); - return ret; + return size; +} + +inline static unsigned max_decrypted_size(gnutls_session_t session) +{ + unsigned size = 0; + + if (session->internals.priorities.allow_large_records != 0) + size += EXTRA_COMP_SIZE; + + size += session->security_parameters.max_record_recv_size; + + return size; } #endif