From: Nikos Mavrogiannopoulos Date: Sun, 20 Feb 2011 16:01:43 +0000 (+0100) Subject: skip replays in handshake packets. X-Git-Tag: gnutls_2_99_0~208 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4faf7eeb97f7c8bc56cf67b3d927183a9c36e35f;p=thirdparty%2Fgnutls.git skip replays in handshake packets. --- diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c index 3ecfcb74df..0cd367f964 100644 --- a/lib/gnutls_buffers.c +++ b/lib/gnutls_buffers.c @@ -895,8 +895,34 @@ _gnutls_handshake_io_cache_int (gnutls_session_t session, _mbuffer_enqueue (send_buffer, bufel); _gnutls_write_log - ("HWRITE: enqueued %d. Total %d bytes.\n", - (int) bufel->msg.size, (int) send_buffer->byte_length); + ("HWRITE: enqueued [%s] %d. Total %d bytes.\n", + _gnutls_handshake2str (bufel->htype), (int) bufel->msg.size, (int) send_buffer->byte_length); + + return 0; +} + +/* Skips a handshake packet + */ +int +_gnutls_handshake_io_recv_skip (gnutls_session_t session, + content_type_t type, + gnutls_handshake_description_t htype, + size_t ptr_size) +{ + opaque * ptr; + int ret; + + if (ptr_size == 0) return 0; + + ptr = gnutls_malloc(ptr_size); + if (ptr == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ret = _gnutls_handshake_io_recv_int(session, type, htype, ptr, ptr_size); + gnutls_free(ptr); + + if (ret < 0) + return gnutls_assert_val(ret); return 0; } @@ -908,7 +934,7 @@ ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, - void *iptr, size_t sizeOfPtr) + void *iptr, size_t ptr_size) { size_t left; ssize_t i; @@ -916,9 +942,9 @@ _gnutls_handshake_io_recv_int (gnutls_session_t session, size_t dsize; ptr = iptr; - left = sizeOfPtr; + left = ptr_size; - if (sizeOfPtr == 0 || iptr == NULL) + if (ptr_size == 0 || iptr == NULL) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; @@ -929,20 +955,20 @@ _gnutls_handshake_io_recv_int (gnutls_session_t session, size_t tmp; /* if we have already received some data */ - if (sizeOfPtr <= session->internals.handshake_recv_buffer.length) + if (ptr_size <= session->internals.handshake_recv_buffer.length) { /* if requested less data then return it. */ gnutls_assert (); - tmp = sizeOfPtr; + tmp = ptr_size; _gnutls_buffer_pop_data (&session->internals.handshake_recv_buffer, iptr, &tmp); return tmp; } gnutls_assert (); - tmp = sizeOfPtr; + tmp = ptr_size; _gnutls_buffer_pop_data (&session->internals.handshake_recv_buffer, iptr, &tmp); left -= tmp; @@ -950,7 +976,7 @@ _gnutls_handshake_io_recv_int (gnutls_session_t session, while (left > 0) { - dsize = sizeOfPtr - left; + dsize = ptr_size - left; i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left); if (i < 0) { @@ -977,7 +1003,7 @@ _gnutls_handshake_io_recv_int (gnutls_session_t session, session->internals.handshake_recv_buffer.length = 0; - return sizeOfPtr - left; + return ptr_size - left; } /* Buffer for handshake packets. Keeps the packets in order diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h index 37371ba6fb..6e1b901272 100644 --- a/lib/gnutls_buffers.h +++ b/lib/gnutls_buffers.h @@ -59,6 +59,12 @@ ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t, content_type_t, int _gnutls_handshake_io_cache_int (gnutls_session_t, gnutls_handshake_description_t, mbuffer_st * bufel); +int +_gnutls_handshake_io_recv_skip (gnutls_session_t session, + content_type_t type, + gnutls_handshake_description_t htype, + size_t ptr_size); + ssize_t _gnutls_io_write_flush (gnutls_session_t session); int _gnutls_io_check_recv (gnutls_session_t session, void* data, size_t data_size, unsigned int ms); diff --git a/lib/gnutls_dtls.c b/lib/gnutls_dtls.c index a2004362ee..1f3d9bd108 100644 --- a/lib/gnutls_dtls.c +++ b/lib/gnutls_dtls.c @@ -50,6 +50,12 @@ transmit_message (gnutls_session_t session, if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) { + _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with " + "length: %u, offset: %u, fragment length: %u\n", + session, bufel->sequence, + _gnutls_handshake2str (bufel->htype), + bufel->htype, data_size, offset, frag_len); + return _gnutls_send_int (session, bufel->type, -1, bufel->epoch, _mbuffer_get_uhead_ptr(bufel), diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 5be063b81d..2d0aec7878 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -1279,6 +1279,11 @@ _gnutls_send_handshake (gnutls_session_t session, mbuffer_st * bufel, return ret; } +#define _gnutls_handshake_header_buffer_clear( session) session->internals.handshake_header_buffer.header_size = 0; \ + session->internals.handshake_header_buffer.sequence = -1; \ + session->internals.handshake_header_buffer.frag_offset = 0; \ + session->internals.handshake_header_buffer.frag_length = 0 + /* This function will read the handshake header and return it to the caller. If the * received handshake packet is not the one expected then it buffers the header, and * returns UNEXPECTED_HANDSHAKE_PACKET. @@ -1381,6 +1386,14 @@ _gnutls_recv_handshake_header (gnutls_session_t session, length32 = _gnutls_read_uint24 (&dataptr[1]); handshake_header_size = HANDSHAKE_HEADER_SIZE(session); + if (IS_DTLS(session)) + { + session->internals.handshake_header_buffer.sequence = _gnutls_read_uint16 (&dataptr[4]); + session->internals.handshake_header_buffer.frag_offset = _gnutls_read_uint24 (&dataptr[6]); + session->internals.handshake_header_buffer.frag_length = _gnutls_read_uint24 (&dataptr[9]); + } + + _gnutls_handshake_log ("HSK[%p]: %s was received [%ld bytes]\n", session, _gnutls_handshake2str (dataptr[0]), (long int) (length32 + HANDSHAKE_HEADER_SIZE(session))); @@ -1398,7 +1411,10 @@ _gnutls_recv_handshake_header (gnutls_session_t session, session, _gnutls_handshake2str (*recv_type), (long int) (length32 + handshake_header_size)); - if (*recv_type != GNUTLS_HANDSHAKE_CLIENT_HELLO) + /* The IS_DTLS() check is redundant since the record layer will + * prevent us from reaching here. + */ + if (IS_DTLS(session) || *recv_type != GNUTLS_HANDSHAKE_CLIENT_HELLO) { /* it should be one or nothing */ gnutls_assert (); return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; @@ -1411,11 +1427,26 @@ _gnutls_recv_handshake_header (gnutls_session_t session, session->internals.handshake_header_buffer.packet_length = length32; session->internals.handshake_header_buffer.recv_type = *recv_type; - if (_gnutls_is_dtls (session) - && type == GNUTLS_HANDSHAKE_SERVER_HELLO - && *recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) - return length32; - else if (*recv_type != type) + if (IS_DTLS(session)) + { + if ((int)session->internals.handshake_header_buffer.sequence <= session->internals.dtls.hsk_read_seq) + { + _gnutls_audit_log("Dropping replayed handshake packet with sequence %d\n", session->internals.handshake_header_buffer.sequence); + ret = _gnutls_handshake_io_recv_skip(session, GNUTLS_HANDSHAKE, *recv_type, length32); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_handshake_header_buffer_clear (session); + return GNUTLS_E_AGAIN; + } + session->internals.dtls.hsk_read_seq = session->internals.handshake_header_buffer.sequence; + + if (type == GNUTLS_HANDSHAKE_SERVER_HELLO + && *recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) + return length32; + } + + if (*recv_type != type) { _gnutls_handshake_log ("HSK[%p]: %s was received, expected %s\n", session, _gnutls_handshake2str (*recv_type), @@ -1427,10 +1458,6 @@ _gnutls_recv_handshake_header (gnutls_session_t session, return length32; } -#define _gnutls_handshake_header_buffer_clear( session) session->internals.handshake_header_buffer.header_size = 0 - - - /* This function will hash the handshake headers and the * handshake data. */ @@ -1501,7 +1528,6 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, ret = _gnutls_recv_handshake_header (session, type, &recv_type); if (ret < 0) { - if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET && optional == OPTIONAL_PACKET) { @@ -1553,7 +1579,6 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, if (data != NULL && length32 > 0) *data = dataptr; - ret = _gnutls_handshake_hash_add_recvd (session, recv_type, session-> internals.handshake_header_buffer. diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 30618fae2c..47978e6e19 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -551,7 +551,7 @@ typedef struct /* For DTLS handshake fragmentation and reassembly. */ uint16_t hsk_write_seq; - uint16_t hsk_read_seq; + int hsk_read_seq; uint16_t mtu; unsigned int retrans_timeout; @@ -569,6 +569,11 @@ typedef struct /* this holds the length of the handshake packet */ size_t packet_length; gnutls_handshake_description_t recv_type; + + /* DTLS fields */ + uint16_t sequence; + size_t frag_offset; + size_t frag_length; } handshake_header_buffer_st; typedef union diff --git a/lib/gnutls_num.c b/lib/gnutls_num.c index 2528447564..2e5c04974c 100644 --- a/lib/gnutls_num.c +++ b/lib/gnutls_num.c @@ -69,15 +69,25 @@ _gnutls_uint64pp (uint64 * x) int _gnutls_uint48pp (uint64 * x) { - int ret; - - ret = _gnutls_uint64pp(x); - if (ret != 0) - return ret; - - if (x->i[6] != 0) - return -1; - + register int i, y = 0; + + for (i = 5; i >= 0; i--) + { + y = 0; + if (x->i[i] == 0xff) + { + x->i[i] = 0; + y = 1; + } + else + x->i[i]++; + + if (y == 0) + break; + } + if (y != 0) + return -1; /* over 48 bits */ + return 0; } diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index cffbb099d9..2ba28af94d 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -258,6 +258,9 @@ _gnutls_handshake_internal_state_init (gnutls_session_t session) session->internals.last_handshake_out = -1; session->internals.resumable = RESUME_TRUE; + + session->internals.dtls.hsk_read_seq = -1; + session->internals.dtls.hsk_write_seq = 0; } void @@ -405,8 +408,8 @@ gnutls_init_dtls (gnutls_session_t * session, (*session)->internals.dtls.mtu = DTLS_DEFAULT_MTU; (*session)->internals.transport = GNUTLS_DGRAM; - (*session)->internals.dtls.retrans_timeout = 1000; - (*session)->internals.dtls.total_timeout = 4000; + (*session)->internals.dtls.retrans_timeout = 300; + (*session)->internals.dtls.total_timeout = 6000; return 0; }