diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-03-05 19:28:46 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-03-06 22:26:50 +0100 |
commit | b6b52f6c19ef13337892be120d29f62526fbb15d (patch) | |
tree | fde54eaa8c1106f05fe193a6f047511f7b72d2c2 /lib | |
parent | 789b253b7946c1c0136c4f795afa37ffc75fdd80 (diff) | |
download | gnutls-b6b52f6c19ef13337892be120d29f62526fbb15d.tar.gz |
Added intermediate handshake layer that will order handshake packets and drop duplicates.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ext_session_ticket.c | 32 | ||||
-rw-r--r-- | lib/gnutls_buffers.c | 436 | ||||
-rw-r--r-- | lib/gnutls_buffers.h | 55 | ||||
-rw-r--r-- | lib/gnutls_cipher.c | 3 | ||||
-rw-r--r-- | lib/gnutls_dtls.c | 4 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 2 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 353 | ||||
-rw-r--r-- | lib/gnutls_handshake.h | 6 | ||||
-rw-r--r-- | lib/gnutls_int.h | 72 | ||||
-rw-r--r-- | lib/gnutls_kx.c | 253 | ||||
-rw-r--r-- | lib/gnutls_mbuffers.c | 70 | ||||
-rw-r--r-- | lib/gnutls_mbuffers.h | 26 | ||||
-rw-r--r-- | lib/gnutls_record.c | 68 | ||||
-rw-r--r-- | lib/gnutls_state.c | 9 | ||||
-rw-r--r-- | lib/gnutls_str.c | 2 | ||||
-rw-r--r-- | lib/gnutls_v2_compat.c | 3 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 5 |
17 files changed, 772 insertions, 627 deletions
diff --git a/lib/ext_session_ticket.c b/lib/ext_session_ticket.c index f51c127e7b..74fe174130 100644 --- a/lib/ext_session_ticket.c +++ b/lib/ext_session_ticket.c @@ -690,8 +690,9 @@ _gnutls_send_new_session_ticket (gnutls_session_t session, int again) int _gnutls_recv_new_session_ticket (gnutls_session_t session) { - uint8_t *data = NULL, *p; + uint8_t *p; int data_size; + gnutls_buffer_st buf; uint32_t lifetime_hint; uint16_t ticket_len; int ret; @@ -711,34 +712,35 @@ _gnutls_recv_new_session_ticket (gnutls_session_t session) if (!priv->session_ticket_renew) return 0; - ret = _gnutls_recv_handshake (session, &data, &data_size, + ret = _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, - MANDATORY_PACKET); + MANDATORY_PACKET, &buf); if (ret < 0) { gnutls_assert (); return ret; } - p = data; - DECR_LENGTH_COM (data_size, 4, goto error); + p = buf.data; + data_size = buf.length; + + DECR_LENGTH_COM (data_size, 4, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); lifetime_hint = _gnutls_read_uint32 (p); p += 4; - DECR_LENGTH_COM (data_size, 2, goto error); + DECR_LENGTH_COM (data_size, 2, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); ticket_len = _gnutls_read_uint16 (p); p += 2; - DECR_LENGTH_COM (data_size, ticket_len, goto error); + DECR_LENGTH_COM (data_size, ticket_len, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); priv->session_ticket = gnutls_realloc (priv->session_ticket, ticket_len); if (!priv->session_ticket) { gnutls_assert (); - gnutls_free (data); - return GNUTLS_E_MEMORY_ERROR; + ret = GNUTLS_E_MEMORY_ERROR; + goto error; } memcpy (priv->session_ticket, p, ticket_len); - gnutls_free (data); priv->session_ticket_len = ticket_len; /* Discard the current session ID. (RFC5077 3.4) */ @@ -750,13 +752,15 @@ _gnutls_recv_new_session_ticket (gnutls_session_t session) gnutls_assert (); gnutls_free (priv->session_ticket); priv->session_ticket = NULL; - return GNUTLS_E_INTERNAL_ERROR; + ret = GNUTLS_E_INTERNAL_ERROR; + goto error; } - return 0; + ret = 0; error: - gnutls_free (data); - return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + _gnutls_buffer_clear (&buf); + + return ret; } #endif diff --git a/lib/gnutls_buffers.c b/lib/gnutls_buffers.c index 5c88319663..c8ad1dc270 100644 --- a/lib/gnutls_buffers.c +++ b/lib/gnutls_buffers.c @@ -168,9 +168,9 @@ _gnutls_dgram_read (gnutls_session_t session, mbuffer_st **bufel, gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr; if (recv_size > max_size) - return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + recv_size = max_size; - *bufel = _mbuffer_alloc (0, _gnutls_get_max_decrypted_data(session)); + *bufel = _mbuffer_alloc (0, max_size); if (*bufel == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); @@ -179,7 +179,6 @@ _gnutls_dgram_read (gnutls_session_t session, mbuffer_st **bufel, session->internals.direction = 0; reset_errno (session); - i = pull_func (fd, ptr, recv_size); if (i < 0) @@ -214,7 +213,7 @@ _gnutls_dgram_read (gnutls_session_t session, mbuffer_st **bufel, /* If we get here, we likely have a stream socket. * FIXME: this probably breaks DCCP. */ gnutls_assert (); - ret = GNUTLS_E_INTERNAL_ERROR; + ret = 0; goto cleanup; } @@ -478,14 +477,6 @@ _gnutls_io_read_buffered (gnutls_session_t session, size_t total, } } - if(_gnutls_is_dtls(session) - && session->internals.record_recv_buffer.byte_length != 0) - { - /* Attempt to read across records while using DTLS. */ - gnutls_assert(); - return GNUTLS_E_INVALID_REQUEST; - } - /* min is over zero. recvdata is the data we must * receive in order to return the requested data. */ @@ -736,6 +727,9 @@ _gnutls_io_check_recv (gnutls_session_t session, void* data, size_t data_size, u else return GNUTLS_E_TIMEDOUT; } +/* HANDSHAKE buffers part + */ + /* This function writes the data that are left in the * Handshake write buffer (ie. because the previous write was * interrupted. @@ -831,109 +825,385 @@ _gnutls_handshake_io_cache_int (gnutls_session_t session, return 0; } -/* Skips a handshake packet +static int handshake_compare(const void* _e1, const void* _e2) +{ +const handshake_buffer_st* e1 = _e1; +const handshake_buffer_st* e2 = _e2; + + if (e1->sequence <= e2->sequence) + return 1; + else + return -1; +} + +#define SSL2_HEADERS 1 +static int +parse_handshake_header (gnutls_session_t session, mbuffer_st* bufel, gnutls_handshake_description_t htype, + handshake_buffer_st* hsk) +{ + uint8_t *dataptr = NULL; /* for realloc */ + size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session), data_size; + + /* Note: SSL2_HEADERS == 1 */ + if (_mbuffer_get_udata_size(bufel) < handshake_header_size) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + dataptr = _mbuffer_get_udata_ptr(bufel); + + /* if reading a client hello of SSLv2 */ + if (!IS_DTLS(session) && htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && + bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2) + { + hsk->length = _mbuffer_get_udata_size(bufel) - SSL2_HEADERS; /* we've read the first byte */ + + handshake_header_size = SSL2_HEADERS; /* we've already read one byte */ + + if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + + hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; + + hsk->sequence = 0; + hsk->start_offset = 0; + hsk->end_offset = hsk->length; + } + else /* TLS handshake headers */ + { + + hsk->htype = dataptr[0]; + + /* we do not use DECR_LEN because we know + * that the packet has enough data. + */ + hsk->length = _gnutls_read_uint24 (&dataptr[1]); + handshake_header_size = HANDSHAKE_HEADER_SIZE(session); + + if (IS_DTLS(session)) + { + hsk->sequence = _gnutls_read_uint16 (&dataptr[4]); + hsk->start_offset = _gnutls_read_uint24 (&dataptr[6]); + hsk->end_offset = hsk->start_offset + _gnutls_read_uint24 (&dataptr[9]); + } + else + { + hsk->sequence = 0; + hsk->start_offset = 0; + hsk->end_offset = _mbuffer_get_udata_size(bufel) - handshake_header_size; + } + } + + /* make the length offset */ + if (hsk->end_offset > 0) hsk->end_offset--; + + _gnutls_handshake_log ("HSK[%p]: %s was received. Length %d, frag offset %d, frag length: %d, sequence: %d\n", + session, _gnutls_handshake2str (hsk->htype), + (int) hsk->length, hsk->start_offset, hsk->end_offset-hsk->start_offset+1, (int)hsk->sequence); + + hsk->header_size = handshake_header_size; + memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel), handshake_header_size); + + data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; + + if (hsk->length > 0 && (hsk->start_offset >= hsk->end_offset || + hsk->end_offset-hsk->start_offset >= data_size || + hsk->end_offset >= hsk->length)) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + else if (hsk->length == 0 && hsk->end_offset != 0 && hsk->start_offset != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + return handshake_header_size; +} + +static void _gnutls_handshake_buffer_move(handshake_buffer_st* dst, handshake_buffer_st* src) +{ + memcpy(dst, src, sizeof(*dst)); + memset(src, 0, sizeof(*src)); + src->htype = -1; +} + +/* will merge the given handshake_buffer_st to the handshake_recv_buffer + * list. The given hsk packet will be released in any case (success or failure). */ -int -_gnutls_handshake_io_recv_skip (gnutls_session_t session, - content_type_t type, - gnutls_handshake_description_t htype, - size_t ptr_size) +static int merge_handshake_packet(gnutls_session_t session, handshake_buffer_st* hsk) { - opaque * ptr; - int ret; +int exists = 0, i, pos = 0; +int ret; + + for (i=0;i<session->internals.handshake_recv_buffer_size;i++) + { + if (session->internals.handshake_recv_buffer[i].htype == hsk->htype) + { + exists = 1; + pos = i; + break; + } + } - if (ptr_size == 0) return 0; + if (exists == 0) + pos = session->internals.handshake_recv_buffer_size; - ptr = gnutls_malloc(ptr_size); - if (ptr == NULL) - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + if (pos > MAX_HANDSHAKE_MSGS) + return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS); - ret = _gnutls_handshake_io_recv_int(session, type, htype, ptr, ptr_size); - gnutls_free(ptr); + if (exists == 0) + { + if (hsk->length > 0 && hsk->end_offset > 0 && hsk->end_offset-hsk->start_offset+1 != hsk->length) + { + ret = _gnutls_buffer_resize(&hsk->data, hsk->length); + if (ret < 0) + return gnutls_assert_val(ret); + + hsk->data.length = hsk->length; + + memmove(&hsk->data.data[hsk->start_offset], hsk->data.data, hsk->end_offset-hsk->start_offset+1); + } + + session->internals.handshake_recv_buffer_size++; + _gnutls_handshake_buffer_move(&session->internals.handshake_recv_buffer[pos], hsk); - if (ret < 0) - return gnutls_assert_val(ret); + /* rewrite headers to make them look as each packet came as a single fragment */ + _gnutls_write_uint24(0, &hsk->header[6]); + _gnutls_write_uint24(hsk->length, &hsk->header[9]); + } + else + { + if (hsk->start_offset < session->internals.handshake_recv_buffer[pos].start_offset && + hsk->end_offset >= session->internals.handshake_recv_buffer[pos].start_offset) + { + memcpy(&session->internals.handshake_recv_buffer[pos].data.data[hsk->start_offset], + hsk->data.data, hsk->data.length); + session->internals.handshake_recv_buffer[pos].start_offset = hsk->start_offset; + session->internals.handshake_recv_buffer[pos].end_offset = + MIN(hsk->end_offset, session->internals.handshake_recv_buffer[pos].end_offset); + } + else if (hsk->end_offset > session->internals.handshake_recv_buffer[pos].end_offset && + hsk->start_offset <= session->internals.handshake_recv_buffer[pos].end_offset+1) + { + memcpy(&session->internals.handshake_recv_buffer[pos].data.data[hsk->start_offset], + hsk->data.data, hsk->data.length); + + session->internals.handshake_recv_buffer[pos].end_offset = hsk->end_offset; + session->internals.handshake_recv_buffer[pos].start_offset = + MIN(hsk->start_offset, session->internals.handshake_recv_buffer[pos].start_offset); + } + _gnutls_handshake_buffer_clear(hsk); + } return 0; } +#define LAST_ELEMENT (session->internals.handshake_recv_buffer_size-1) + +/* returns the last stored handshake packet. + */ +static int get_last_packet(gnutls_session_t session, gnutls_handshake_description_t htype, + handshake_buffer_st * hsk) +{ +handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer; + + if (IS_DTLS(session)) + { + if (session->internals.handshake_recv_buffer_size == 0 || + (session->internals.dtls.hsk_read_seq != recv_buf[LAST_ELEMENT].sequence)) + return gnutls_assert_val(GNUTLS_E_AGAIN); + + if (htype != recv_buf[LAST_ELEMENT].htype) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); + + else if ((recv_buf[LAST_ELEMENT].start_offset == 0 && + recv_buf[LAST_ELEMENT].end_offset == recv_buf[LAST_ELEMENT].length -1) || + recv_buf[LAST_ELEMENT].length == 0) + { + session->internals.dtls.hsk_read_seq++; + _gnutls_handshake_buffer_move(hsk, &recv_buf[LAST_ELEMENT]); + session->internals.handshake_recv_buffer_size--; + + return 0; + } + else + return gnutls_assert_val(GNUTLS_E_AGAIN); + } + else /* TLS */ + { + if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length == recv_buf[0].data.length) + { + _gnutls_handshake_buffer_move(hsk, &recv_buf[0]); + return 0; + } + else + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } +} + /* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. */ ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t session, - content_type_t type, gnutls_handshake_description_t htype, - void *iptr, size_t ptr_size) + handshake_buffer_st * hsk) { - size_t left; - ssize_t i; - opaque *ptr; - size_t dsize; + gnutls_datum_t msg; + mbuffer_st* bufel = NULL, *prev = NULL; + int ret; + size_t data_size; + handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer; - ptr = iptr; - left = ptr_size; + ret = get_last_packet(session, htype, hsk); + if (ret >= 0) + return ret; - if (ptr_size == 0 || iptr == NULL) - { - gnutls_assert (); - return GNUTLS_E_INVALID_REQUEST; - } + /* if we don't have a complete message waiting for us, try + * receiving more */ + ret = _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype); + if (ret < 0) + return gnutls_assert_val(ret); + + bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); + if (bufel == NULL) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); - if (session->internals.handshake_recv_buffer.length > 0) + if (!IS_DTLS(session)) { - size_t tmp; + ssize_t remain, append, header_size; - /* if we have already received some data */ - if (ptr_size <= session->internals.handshake_recv_buffer.length) + do { - /* if requested less data then return it. + if (bufel->type != GNUTLS_HANDSHAKE) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + + /* if we have a half received message the complete it. */ - gnutls_assert (); + remain = recv_buf[0].length - + recv_buf[0].data.length; - tmp = ptr_size; - _gnutls_buffer_pop_data (&session->internals.handshake_recv_buffer, - iptr, &tmp); - return tmp; - } - gnutls_assert (); + /* this is the rest of a previous message */ + if (recv_buf[0].length > 0 && remain > 0) + { + if (msg.size <= remain) + append = msg.size; + else + append = remain; + + ret = _gnutls_buffer_append_data(&recv_buf[0].data, msg.data, append); + if (ret < 0) + return gnutls_assert_val(ret); + + _mbuffer_head_remove_bytes(&session->internals.record_buffer, append); + } + else /* received new message */ + { + ret = parse_handshake_header(session, bufel, htype, &recv_buf[0]); + if (ret < 0) + return gnutls_assert_val(ret); - tmp = ptr_size; - _gnutls_buffer_pop_data (&session->internals.handshake_recv_buffer, - iptr, &tmp); - left -= tmp; - } + header_size = ret; + session->internals.handshake_recv_buffer_size = 1; - while (left > 0) + if (htype != recv_buf[0].htype) + { /* an unexpected packet */ + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); + } + + _mbuffer_set_uhead_size(bufel, header_size); + + data_size = MIN(recv_buf[0].length, _mbuffer_get_udata_size(bufel)); + ret = _gnutls_buffer_append_data(&recv_buf[0].data, _mbuffer_get_udata_ptr(bufel), data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + _mbuffer_head_remove_bytes(&session->internals.record_buffer, data_size+header_size); + } + + /* if packet is complete then return it + */ + if (recv_buf[0].length == + recv_buf[0].data.length) + { + return get_last_packet(session, htype, hsk); + } + bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); + } + while(bufel != NULL); + + /* if we are here it means that the received packets were not + * enough to complete the handshake packet. + */ + return gnutls_assert_val(GNUTLS_E_AGAIN); + } + else /* DTLS */ { - dsize = ptr_size - left; - i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left, NULL); - if (i < 0) - { + handshake_buffer_st tmp; - if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN)) + do + { + /* we now + * 0. parse headers + * 1. insert to handshake_recv_buffer + * 2. sort handshake_recv_buffer on sequence numbers + * 3. return first packet if completed or GNUTLS_E_AGAIN. + */ + do { - gnutls_assert (); + if (bufel->type != GNUTLS_HANDSHAKE) + { + gnutls_assert(); + goto next; /* ignore packet */ + } + + _gnutls_handshake_buffer_init(&tmp); + + ret = parse_handshake_header(session, bufel, htype, &tmp); + if (ret < 0) + { + gnutls_assert(); + _gnutls_audit_log("Invalid handshake packet headers. Discarding.\n"); + break; + } + + _mbuffer_consume(&session->internals.record_buffer, bufel, ret); + + data_size = MIN(tmp.length, tmp.end_offset-tmp.start_offset+1); + + ret = _gnutls_buffer_append_data(&tmp.data, _mbuffer_get_udata_ptr(bufel), data_size); + if (ret < 0) + return gnutls_assert_val(ret); + + _mbuffer_consume(&session->internals.record_buffer, bufel, data_size); + + ret = merge_handshake_packet(session, &tmp); + if (ret < 0) + return gnutls_assert_val(ret); - _gnutls_buffer_append_data (&session->internals. - handshake_recv_buffer, iptr, dsize); } + while(_mbuffer_get_udata_size(bufel) > 0); + + prev = bufel; + bufel = _mbuffer_dequeue(&session->internals.record_buffer, bufel); - return i; + _mbuffer_xfree(&prev); + continue; + +next: + bufel = _mbuffer_head_get_next(bufel, NULL); } - else + while(bufel != NULL); + + /* sort in descending order */ + if (session->internals.handshake_recv_buffer_size > 1) + qsort(recv_buf, session->internals.handshake_recv_buffer_size, + sizeof(recv_buf[0]), handshake_compare); + + while(session->internals.handshake_recv_buffer_size > 0 && + recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq) { - if (i == 0) - break; /* EOF */ + _gnutls_audit_log("Discarded replayed handshake packet with sequence %d\n", tmp.sequence); + _gnutls_handshake_buffer_clear(&recv_buf[LAST_ELEMENT]); + session->internals.handshake_recv_buffer_size--; } - left -= i; - + return get_last_packet(session, htype, hsk); } - - session->internals.handshake_recv_buffer.length = 0; - - return ptr_size - left; } /* Buffer for handshake packets. Keeps the packets in order @@ -1005,15 +1275,3 @@ _gnutls_handshake_buffer_empty (gnutls_session_t session) return 0; } - - -int -_gnutls_handshake_buffer_clear (gnutls_session_t session) -{ - - _gnutls_buffers_log ("BUF[HSK]: Cleared Data from buffer\n"); - _gnutls_buffer_clear (&session->internals.handshake_hash_buffer); - - return 0; -} - diff --git a/lib/gnutls_buffers.h b/lib/gnutls_buffers.h index 2cb42f3fba..0293477541 100644 --- a/lib/gnutls_buffers.h +++ b/lib/gnutls_buffers.h @@ -49,30 +49,61 @@ ssize_t _gnutls_io_write_buffered (gnutls_session_t session, int _gnutls_handshake_buffer_get_size (gnutls_session_t session); int _gnutls_handshake_buffer_put (gnutls_session_t session, opaque * data, size_t length); -int _gnutls_handshake_buffer_clear (gnutls_session_t session); int _gnutls_handshake_buffer_empty (gnutls_session_t session); int _gnutls_handshake_buffer_get_ptr (gnutls_session_t session, opaque ** data_ptr, size_t * length); -#define _gnutls_handshake_io_buffer_clear( session) \ - _mbuffer_head_clear( &session->internals.handshake_send_buffer); \ - _gnutls_buffer_clear( &session->internals.handshake_recv_buffer); - -ssize_t _gnutls_handshake_io_recv_int (gnutls_session_t, content_type_t, - gnutls_handshake_description_t, void *, - size_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, + +ssize_t +_gnutls_handshake_io_recv_int (gnutls_session_t session, gnutls_handshake_description_t htype, - size_t ptr_size); + handshake_buffer_st * hsk); 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); ssize_t _gnutls_handshake_io_write_flush (gnutls_session_t session); +inline static void _gnutls_handshake_buffer_clear(handshake_buffer_st* hsk) +{ + _gnutls_buffer_clear(&hsk->data); + hsk->htype = -1; +} + +inline static void _gnutls_handshake_buffer_init(handshake_buffer_st* hsk) +{ + memset(hsk, 0, sizeof(*hsk)); + _gnutls_buffer_init(&hsk->data); + hsk->htype = -1; +} + +inline static void _gnutls_handshake_recv_buffer_clear(gnutls_session_t session) +{ +int i; + for (i=0;i<session->internals.handshake_recv_buffer_size;i++) + _gnutls_handshake_buffer_clear(&session->internals.handshake_recv_buffer[i]); + session->internals.handshake_recv_buffer_size = 0; +} + +inline static void _gnutls_handshake_recv_buffer_init(gnutls_session_t session) +{ +int i; + for (i=0;i<MAX_HANDSHAKE_MSGS;i++) + { + _gnutls_handshake_buffer_init(&session->internals.handshake_recv_buffer[i]); + } + session->internals.handshake_recv_buffer_size = 0; +} + +ssize_t +_gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type, + gnutls_handshake_description_t htype); + +#define _gnutls_handshake_io_buffer_clear( session) \ + _mbuffer_head_clear( &session->internals.handshake_send_buffer); \ + _gnutls_handshake_recv_buffer_clear( session); + #endif diff --git a/lib/gnutls_cipher.c b/lib/gnutls_cipher.c index 64dbbd2e5f..d3551519d0 100644 --- a/lib/gnutls_cipher.c +++ b/lib/gnutls_cipher.c @@ -612,7 +612,8 @@ _gnutls_ciphertext2compressed (gnutls_session_t session, if (compress_size < length) return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED); - memmove (compress_data, ciphertext.data, length); + if (compress_data != ciphertext.data) + memcpy (compress_data, ciphertext.data, length); return length; } diff --git a/lib/gnutls_dtls.c b/lib/gnutls_dtls.c index 30ecfc5971..36262c95ae 100644 --- a/lib/gnutls_dtls.c +++ b/lib/gnutls_dtls.c @@ -46,7 +46,7 @@ transmit_message (gnutls_session_t session, opaque *data, *mtu_data; int ret = 0; unsigned int offset, frag_len, data_size; - const uint mtu = session->internals.dtls.mtu; + const uint mtu = gnutls_dtls_get_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE; if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) { @@ -96,7 +96,7 @@ transmit_message (gnutls_session_t session, /* Fragment length */ _gnutls_write_uint24 (frag_len, &mtu_data[9]); - memcpy (&mtu_data[12], data+offset, frag_len); + memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len); _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with " "length: %u, offset: %u, fragment length: %u\n", diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index b5811c04a9..4d499ab3ac 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -201,6 +201,8 @@ static const gnutls_error_entry error_algorithms[] = { ERROR_ENTRY (N_("Too many empty record packets have been received."), GNUTLS_E_TOO_MANY_EMPTY_PACKETS, 1), + ERROR_ENTRY (N_("Too many handshake packets have been received."), + GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS, 1), ERROR_ENTRY (N_("The initialization of GnuTLS-extra has failed."), GNUTLS_E_INIT_LIBEXTRA, 1), ERROR_ENTRY (N_ diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index e8c427ff4b..d8dcea4c31 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -103,7 +103,6 @@ _gnutls_handshake_hash_buffers_clear (gnutls_session_t session) } session->security_parameters.handshake_mac_handle_type = 0; session->internals.handshake_mac_handle_init = 0; - _gnutls_handshake_buffer_clear (session); } /* this will copy the required values for resuming to @@ -428,10 +427,6 @@ _gnutls_read_client_hello (gnutls_session_t session, opaque * data, int len = datalen; opaque rnd[GNUTLS_RANDOM_SIZE], *suite_ptr, *comp_ptr, *session_id; - if (session->internals.v2_hello != 0) - { /* version 2.0 */ - return _gnutls_read_client_hello_v2 (session, data, datalen); - } DECR_LEN (len, 2); _gnutls_handshake_log ("HSK[%p]: Client's version: %d.%d\n", session, @@ -761,13 +756,14 @@ static int _gnutls_recv_finished (gnutls_session_t session) { uint8_t data[MAX_VERIFY_DATA_SIZE], *vrfy; + gnutls_buffer_st buf; int data_size; int ret; - int vrfysize; + int vrfy_size; ret = - _gnutls_recv_handshake (session, &vrfy, &vrfysize, - GNUTLS_HANDSHAKE_FINISHED, MANDATORY_PACKET); + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_FINISHED, + MANDATORY_PACKET, &buf); if (ret < 0) { ERR ("recv finished int", ret); @@ -775,6 +771,8 @@ _gnutls_recv_finished (gnutls_session_t session) return ret; } + vrfy = buf.data; + vrfy_size = buf.length; if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) { @@ -785,11 +783,11 @@ _gnutls_recv_finished (gnutls_session_t session) data_size = 12; } - if (vrfysize != data_size) + if (vrfy_size != data_size) { gnutls_assert (); - gnutls_free (vrfy); - return GNUTLS_E_ERROR_IN_FINISHED_PACKET; + ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET; + goto cleanup; } if (gnutls_protocol_get_version (session) == GNUTLS_SSL3) @@ -810,22 +808,21 @@ _gnutls_recv_finished (gnutls_session_t session) if (ret < 0) { gnutls_assert (); - gnutls_free (vrfy); - return ret; + goto cleanup; } if (memcmp (vrfy, data, data_size) != 0) { gnutls_assert (); ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET; + goto cleanup; } - gnutls_free (vrfy); ret = _gnutls_ext_sr_finished (session, data, data_size, 1); if (ret < 0) { gnutls_assert (); - return ret; + goto cleanup; } if ((session->internals.resumed == RESUME_TRUE @@ -840,8 +837,12 @@ _gnutls_recv_finished (gnutls_session_t session) session->internals.cb_tls_unique_len = data_size; } + session->internals.initial_negotiation_completed = 1; +cleanup: + _gnutls_buffer_clear(&buf); + return ret; } @@ -1279,185 +1280,6 @@ _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. - * - * FIXME: This function is complex. - */ -#define SSL2_HEADERS 1 -static int -_gnutls_recv_handshake_header (gnutls_session_t session, - gnutls_handshake_description_t type, - gnutls_handshake_description_t * recv_type) -{ - int ret; - uint32_t length32 = 0; - uint8_t *dataptr = NULL; /* for realloc */ - size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session); - - /* if we have data into the buffer then return them, do not read the next packet. - * In order to return we need a full TLS handshake header, or in case of a version 2 - * packet, then we return the first byte. - */ - if (session->internals.handshake_header_buffer.header_size == - handshake_header_size || (session->internals.v2_hello != 0 - && type == GNUTLS_HANDSHAKE_CLIENT_HELLO - && session->internals.handshake_header_buffer. - packet_length > 0)) - { - - *recv_type = session->internals.handshake_header_buffer.recv_type; - - if (*recv_type != type) - { - gnutls_assert (); - _gnutls_handshake_log - ("HSK[%p]: Handshake type mismatch (under attack?)\n", session); - return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; - } - - return session->internals.handshake_header_buffer.packet_length; - } - - /* Note: SSL2_HEADERS == 1 */ - - dataptr = session->internals.handshake_header_buffer.header; - - /* If we haven't already read the handshake headers. - */ - if (session->internals.handshake_header_buffer.header_size < SSL2_HEADERS) - { - ret = - _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE, - type, dataptr, SSL2_HEADERS); - - if (ret < 0) - { - return ret; - } - - /* The case ret==0 is caught here. - */ - if (ret != SSL2_HEADERS) - { - gnutls_assert (); - return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - } - session->internals.handshake_header_buffer.header_size = SSL2_HEADERS; - } - - if (session->internals.v2_hello == 0 - || type != GNUTLS_HANDSHAKE_CLIENT_HELLO) - { - ret = - _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE, - type, - &dataptr - [session-> - internals.handshake_header_buffer. - header_size], - HANDSHAKE_HEADER_SIZE(session) - - session-> - internals.handshake_header_buffer. - header_size); - if (ret <= 0) - { - gnutls_assert (); - return (ret < 0) ? ret : GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - } - if ((size_t) ret != - HANDSHAKE_HEADER_SIZE(session) - - session->internals.handshake_header_buffer.header_size) - { - gnutls_assert (); - return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - } - *recv_type = dataptr[0]; - - /* we do not use DECR_LEN because we know - * that the packet has enough data. - */ - 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))); - - } - else - { /* v2 hello */ - length32 = session->internals.v2_hello - SSL2_HEADERS; /* we've read the first byte */ - - handshake_header_size = SSL2_HEADERS; /* we've already read one byte */ - - *recv_type = dataptr[0]; - - _gnutls_handshake_log ("HSK[%p]: %s(v2) was received [%ld bytes]\n", - session, _gnutls_handshake2str (*recv_type), - (long int) (length32 + handshake_header_size)); - - /* 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; - } - } - - /* put the packet into the buffer */ - session->internals.handshake_header_buffer.header_size = - handshake_header_size; - session->internals.handshake_header_buffer.packet_length = length32; - session->internals.handshake_header_buffer.recv_type = *recv_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), - _gnutls_handshake2str (type)); - gnutls_assert (); - return GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET; - } - - return length32; -} - /* This function will hash the handshake headers and the * handshake data. */ @@ -1516,94 +1338,44 @@ _gnutls_handshake_hash_add_recvd (gnutls_session_t session, * passed to _gnutls_recv_hello(). */ int -_gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, - int *datalen, gnutls_handshake_description_t type, - optional_t optional) +_gnutls_recv_handshake (gnutls_session_t session, + gnutls_handshake_description_t type, + optional_t optional, gnutls_buffer_st* buf) { int ret; - uint32_t length32 = 0; - opaque *dataptr = NULL; - gnutls_handshake_description_t recv_type; + handshake_buffer_st hsk; - ret = _gnutls_recv_handshake_header (session, type, &recv_type); + ret = + _gnutls_handshake_io_recv_int (session, type, &hsk); if (ret < 0) { - if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET - && optional == OPTIONAL_PACKET) - { - if (datalen != NULL) - *datalen = 0; - if (data != NULL) - *data = NULL; - return 0; /* ok just ignore the packet */ - } - - return ret; - } - - session->internals.last_handshake_in = recv_type; - - length32 = ret; - - if (length32 > 0) - dataptr = gnutls_malloc (length32); - else if (recv_type != GNUTLS_HANDSHAKE_SERVER_HELLO_DONE) - { - gnutls_assert (); - return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - } - - if (dataptr == NULL && length32 > 0) - { - gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; - } - - if (datalen != NULL) - *datalen = length32; - - if (length32 > 0) - { - ret = - _gnutls_handshake_io_recv_int (session, GNUTLS_HANDSHAKE, - type, dataptr, length32); - if (ret <= 0) + if (optional == OPTIONAL_PACKET && ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) { - gnutls_assert (); - if (ret == 0) - ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; - goto cleanup; + if (buf) _gnutls_buffer_init(buf); + return 0; } + return gnutls_assert_val(ret); } - if (data != NULL && length32 > 0) - *data = dataptr; - - ret = _gnutls_handshake_hash_add_recvd (session, recv_type, - session-> - internals.handshake_header_buffer. - header, - session-> - internals.handshake_header_buffer. - header_size, dataptr, length32); - - /* If we fail before this then we will reuse the handshake header - * have have received above. if we get here the we clear the handshake - * header we received. - */ - _gnutls_handshake_header_buffer_clear (session); - + ret = _gnutls_handshake_hash_add_recvd (session, hsk.htype, + hsk.header, hsk.header_size, + hsk.data.data, hsk.data.length); if (ret < 0) { gnutls_assert (); goto cleanup; } - switch (recv_type) + switch (hsk.htype) { + case GNUTLS_HANDSHAKE_CLIENT_HELLO_V2: case GNUTLS_HANDSHAKE_CLIENT_HELLO: case GNUTLS_HANDSHAKE_SERVER_HELLO: - ret = _gnutls_recv_hello (session, dataptr, length32); + if (hsk.htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2) + ret = _gnutls_read_client_hello_v2 (session, hsk.data.data, hsk.data.length); + else + ret = _gnutls_recv_hello (session, hsk.data.data, hsk.data.length); + if (ret < 0) { gnutls_assert(); @@ -1622,25 +1394,26 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, break; case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST: - ret = _gnutls_recv_hello_verify_request (session, dataptr, length32); + ret = _gnutls_recv_hello_verify_request (session, hsk.data.data, hsk.data.length); if (ret < 0) { gnutls_assert(); goto cleanup; } else - /* Signal our caller we have received a verification cookie - and ClientHello needs to be sent again. */ + /* Signal our caller we have received a verification cookie + and ClientHello needs to be sent again. */ ret = 1; goto cleanup; /* caller doesn't need dataptr */ break; case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: - if (length32 == 0) + if (hsk.data.length == 0) ret = 0; else { + gnutls_assert(); ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto cleanup; } @@ -1653,7 +1426,7 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: case GNUTLS_HANDSHAKE_SUPPLEMENTAL: case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: - ret = length32; + ret = hsk.data.length; break; default: gnutls_assert (); @@ -1661,12 +1434,14 @@ _gnutls_recv_handshake (gnutls_session_t session, uint8_t ** data, goto cleanup; } - return ret; + if (buf) + { + *buf = hsk.data; + return ret; + } cleanup: - gnutls_free (dataptr); - if (data != NULL) - *data = NULL; + _gnutls_handshake_buffer_clear (&hsk); return ret; } @@ -2732,29 +2507,28 @@ _gnutls_send_supplemental (gnutls_session_t session, int again) static int _gnutls_recv_supplemental (gnutls_session_t session) { - uint8_t *data = NULL; - int datalen = 0; + gnutls_buffer_st buf; int ret; _gnutls_debug_log ("EXT[%p]: Expecting supplemental data\n", session); - ret = _gnutls_recv_handshake (session, &data, &datalen, - GNUTLS_HANDSHAKE_SUPPLEMENTAL, - OPTIONAL_PACKET); + ret = _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_SUPPLEMENTAL, + OPTIONAL_PACKET, &buf); if (ret < 0) { gnutls_assert (); return ret; } - ret = _gnutls_parse_supplemental (session, data, datalen); + ret = _gnutls_parse_supplemental (session, buf.data, buf.length); if (ret < 0) { gnutls_assert (); - return ret; + goto cleanup; } - gnutls_free (data); +cleanup: + _gnutls_buffer_clear(&buf); return ret; } @@ -2899,9 +2673,9 @@ _gnutls_handshake_client (gnutls_session_t session) if (_gnutls_is_dtls (session)) { ret = - _gnutls_recv_handshake (session, NULL, NULL, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST, - OPTIONAL_PACKET); + OPTIONAL_PACKET, NULL); STATE = STATE11; IMED_RET ("recv hello verify", ret, 1); @@ -2914,9 +2688,9 @@ _gnutls_handshake_client (gnutls_session_t session) case STATE2: /* receive the server hello */ ret = - _gnutls_recv_handshake (session, NULL, NULL, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_SERVER_HELLO, - MANDATORY_PACKET); + MANDATORY_PACKET, NULL); STATE = STATE2; IMED_RET ("recv hello", ret, 1); @@ -2955,12 +2729,11 @@ _gnutls_handshake_client (gnutls_session_t session) /* receive the server hello done */ if (session->internals.resumed == RESUME_FALSE) /* if we are not resuming */ ret = - _gnutls_recv_handshake (session, NULL, NULL, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_SERVER_HELLO_DONE, - MANDATORY_PACKET); + MANDATORY_PACKET, NULL); STATE = STATE6; IMED_RET ("recv server hello done", ret, 1); - case STATE71: if (session->security_parameters.do_send_supplemental) { @@ -3171,9 +2944,9 @@ _gnutls_handshake_server (gnutls_session_t session) case STATE0: case STATE1: ret = - _gnutls_recv_handshake (session, NULL, NULL, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_CLIENT_HELLO, - MANDATORY_PACKET); + MANDATORY_PACKET, NULL); STATE = STATE1; IMED_RET ("recv hello", ret, 1); diff --git a/lib/gnutls_handshake.h b/lib/gnutls_handshake.h index e3200443cb..6792bbe0ce 100644 --- a/lib/gnutls_handshake.h +++ b/lib/gnutls_handshake.h @@ -32,9 +32,9 @@ int _gnutls_recv_hello_request (gnutls_session_t session, void *data, uint32_t data_size); int _gnutls_send_hello (gnutls_session_t session, int again); int _gnutls_recv_hello (gnutls_session_t session, opaque * data, int datalen); -int _gnutls_recv_handshake (gnutls_session_t session, uint8_t **, int *, - gnutls_handshake_description_t, - optional_t optional); +int _gnutls_recv_handshake (gnutls_session_t session, + gnutls_handshake_description_t type, + optional_t optional, gnutls_buffer_st* buf); int _gnutls_generate_session_id (opaque * session_id, uint8_t * len); int _gnutls_handshake_common (gnutls_session_t session); int _gnutls_handshake_client (gnutls_session_t session); diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 21d9484333..74f6e5f518 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -153,7 +153,6 @@ typedef enum transport_t #define MAX_PAD_SIZE 255 #define EXTRA_COMP_SIZE 2048 #define MAX_RECORD_OVERHEAD (MAX_CIPHER_BLOCK_SIZE/*iv*/+MAX_PAD_SIZE+EXTRA_COMP_SIZE+MAX_HASH_SIZE/*MAC*/) -#define MAX_RECORD_OVERHEAD_RT(session) (2*MAX_CIPHER_BLOCK_SIZE/*iv+pad*/+MAX_HASH_SIZE) #define MAX_RECV_SIZE(session) (MAX_RECORD_OVERHEAD+MAX_RECORD_RECV_SIZE(session)+RECORD_HEADER_SIZE(session)) #define TLS_HANDSHAKE_HEADER_SIZE 4 @@ -246,32 +245,65 @@ typedef enum content_type_t /* Message buffers (mbuffers) structures */ +/* this is actually the maximum number of distinct handshake + * messages that can arrive in a single flight + */ +#define MAX_HANDSHAKE_MSGS 6 +typedef struct +{ + /* Handshake layer type and sequence of message */ + gnutls_handshake_description_t htype; + uint32_t length; + + /* valid in DTLS */ + uint16_t sequence; + + /* indicate whether that message is complete. + * complete means start_offset == 0 and end_offset == length + */ + uint32_t start_offset; + uint32_t end_offset; + + opaque header[MAX_HANDSHAKE_HEADER_SIZE]; + int header_size; + + gnutls_buffer_st data; +} handshake_buffer_st; + typedef struct mbuffer_st { + /* when used in mbuffer_head_st */ struct mbuffer_st *next; + struct mbuffer_st *prev; - gnutls_datum_t msg; /* msg->size - mark = number of bytes left to process in this message. Mark should only be non-zero when this buffer is the head of the queue. */ size_t mark; - unsigned int user_mark; /* only used during fill in */ + + + /* the data */ + gnutls_datum_t msg; size_t maximum_size; + /* used during fill in, to separate header from data + * body. */ + unsigned int user_mark; + /* Filled in by record layer on recv: * type, record_sequence */ - /* Filled in by handshake layer on send: - * type, epoch, htype, handshake_sequence - */ - /* record layer content type */ content_type_t type; /* record layer sequence */ uint64 record_sequence; + /* Filled in by handshake layer on send: + * type, epoch, htype, handshake_sequence + */ + /* Record layer epoch of message */ uint16_t epoch; @@ -283,7 +315,7 @@ typedef struct mbuffer_st typedef struct mbuffer_head_st { mbuffer_st *head; - mbuffer_st **tail; + mbuffer_st *tail; unsigned int length; size_t byte_length; @@ -569,21 +601,6 @@ typedef struct } dtls_st; -typedef struct -{ - opaque header[MAX_HANDSHAKE_HEADER_SIZE]; - /* this holds the number of bytes in the handshake_header[] */ - size_t header_size; - /* 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 { void *ptr; @@ -652,7 +669,8 @@ typedef struct * protocol only. freed using _gnutls_handshake_io_buffer_clear(); */ mbuffer_head_st handshake_send_buffer; - gnutls_buffer_st handshake_recv_buffer; + handshake_buffer_st handshake_recv_buffer[MAX_HANDSHAKE_MSGS]; + int handshake_recv_buffer_size; /* this buffer holds a record packet -mostly used for * non blocking IO. @@ -674,12 +692,6 @@ typedef struct int expire_time; /* after expire_time seconds this session will expire */ struct mod_auth_st_int *auth_struct; /* used in handshake packets and KX algorithms */ - int v2_hello; /* 0 if the client hello is v3+. - * non-zero if we got a v2 hello. - */ - /* keeps the headers of the handshake packet - */ - handshake_header_buffer_st handshake_header_buffer; /* this is the highest version available * to the peer. (advertized version). diff --git a/lib/gnutls_kx.c b/lib/gnutls_kx.c index 7c390f0ec1..b9e5b3edba 100644 --- a/lib/gnutls_kx.c +++ b/lib/gnutls_kx.c @@ -449,8 +449,7 @@ cleanup: int _gnutls_recv_server_kx_message (gnutls_session_t session) { - uint8_t *data = NULL; - int datasize; + gnutls_buffer_st buf; int ret = 0; optional_t optflag = MANDATORY_PACKET; @@ -471,10 +470,9 @@ _gnutls_recv_server_kx_message (gnutls_session_t session) optflag = OPTIONAL_PACKET; ret = - _gnutls_recv_handshake (session, &data, - &datasize, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE, - optflag); + optflag, &buf); if (ret < 0) { gnutls_assert (); @@ -483,9 +481,9 @@ _gnutls_recv_server_kx_message (gnutls_session_t session) ret = session->internals.auth_struct->gnutls_process_server_kx (session, - data, - datasize); - gnutls_free (data); + buf.data, + buf.length); + _gnutls_buffer_clear(&buf); if (ret < 0) { @@ -500,8 +498,7 @@ _gnutls_recv_server_kx_message (gnutls_session_t session) int _gnutls_recv_server_certificate_request (gnutls_session_t session) { - uint8_t *data; - int datasize; + gnutls_buffer_st buf; int ret = 0; if (session->internals. @@ -509,21 +506,23 @@ _gnutls_recv_server_certificate_request (gnutls_session_t session) { ret = - _gnutls_recv_handshake (session, &data, - &datasize, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, - OPTIONAL_PACKET); + OPTIONAL_PACKET, &buf); if (ret < 0) return ret; - if (ret == 0 && datasize == 0) - return 0; /* ignored */ + if (ret == 0 && buf.length == 0) + { + _gnutls_buffer_clear(&buf); + return 0; /* ignored */ + } ret = session->internals. - auth_struct->gnutls_process_server_certificate_request (session, data, - datasize); - gnutls_free (data); + auth_struct->gnutls_process_server_certificate_request (session, buf.data, + buf.length); + _gnutls_buffer_clear (&buf); if (ret < 0) return ret; @@ -534,8 +533,7 @@ _gnutls_recv_server_certificate_request (gnutls_session_t session) int _gnutls_recv_client_kx_message (gnutls_session_t session) { - uint8_t *data; - int datasize; + gnutls_buffer_st buf; int ret = 0; @@ -544,18 +542,17 @@ _gnutls_recv_client_kx_message (gnutls_session_t session) { ret = - _gnutls_recv_handshake (session, &data, - &datasize, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE, - MANDATORY_PACKET); + MANDATORY_PACKET, &buf); if (ret < 0) return ret; ret = session->internals.auth_struct->gnutls_process_client_kx (session, - data, - datasize); - gnutls_free (data); + buf.data, + buf.length); + _gnutls_buffer_clear (&buf); if (ret < 0) return ret; @@ -565,107 +562,102 @@ _gnutls_recv_client_kx_message (gnutls_session_t session) } - - int _gnutls_recv_client_certificate (gnutls_session_t session) { - int datasize; - opaque *data; + gnutls_buffer_st buf; int ret = 0; int optional; - if (session->internals.auth_struct->gnutls_process_client_certificate != + if (session->internals.auth_struct->gnutls_process_client_certificate == NULL) - { + return 0; - /* if we have not requested a certificate then just return - */ - if (session->internals.send_cert_req == 0) - { - return 0; - } + /* if we have not requested a certificate then just return + */ + if (session->internals.send_cert_req == 0) + { + return 0; + } - if (session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) - optional = MANDATORY_PACKET; - else - optional = OPTIONAL_PACKET; + if (session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) + optional = MANDATORY_PACKET; + else + optional = OPTIONAL_PACKET; - ret = - _gnutls_recv_handshake (session, &data, - &datasize, - GNUTLS_HANDSHAKE_CERTIFICATE_PKT, optional); + ret = + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, + optional, &buf); - if (ret < 0) + if (ret < 0) + { + /* Handle the case of old SSL3 clients who send + * a warning alert instead of an empty certificate to indicate + * no certificate. + */ + if (optional == OPTIONAL_PACKET && + ret == GNUTLS_E_WARNING_ALERT_RECEIVED && + gnutls_protocol_get_version (session) == GNUTLS_SSL3 && + gnutls_alert_get (session) == GNUTLS_A_SSL3_NO_CERTIFICATE) { - /* Handle the case of old SSL3 clients who send - * a warning alert instead of an empty certificate to indicate - * no certificate. - */ - if (optional == OPTIONAL_PACKET && - ret == GNUTLS_E_WARNING_ALERT_RECEIVED && - gnutls_protocol_get_version (session) == GNUTLS_SSL3 && - gnutls_alert_get (session) == GNUTLS_A_SSL3_NO_CERTIFICATE) - { - - /* SSL3 does not send an empty certificate, - * but this alert. So we just ignore it. - */ - gnutls_assert (); - return 0; - } - - /* certificate was required - */ - if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED - || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) - && optional == MANDATORY_PACKET) - { - gnutls_assert (); - return GNUTLS_E_NO_CERTIFICATE_FOUND; - } - - return ret; - } - if (ret == 0 && datasize == 0 && optional == OPTIONAL_PACKET) - { - /* Client has not sent the certificate message. - * well I'm not sure we should accept this - * behaviour. + /* SSL3 does not send an empty certificate, + * but this alert. So we just ignore it. */ gnutls_assert (); return 0; } - ret = - session->internals. - auth_struct->gnutls_process_client_certificate (session, data, - datasize); - gnutls_free (data); - if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND) + /* certificate was required + */ + if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED + || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) + && optional == MANDATORY_PACKET) { gnutls_assert (); - return ret; + return GNUTLS_E_NO_CERTIFICATE_FOUND; } - /* ok we should expect a certificate verify message now + return ret; + } + + if (ret == 0 && buf.length == 0 && optional == OPTIONAL_PACKET) + { + /* Client has not sent the certificate message. + * well I'm not sure we should accept this + * behaviour. */ - if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND && optional == OPTIONAL_PACKET) - ret = 0; - else - session->key->certificate_requested = 1; + gnutls_assert (); + ret = 0; + goto cleanup; + } + ret = + session->internals. + auth_struct->gnutls_process_client_certificate (session, buf.data, + buf.length); + if (ret < 0 && ret != GNUTLS_E_NO_CERTIFICATE_FOUND) + { + gnutls_assert (); + goto cleanup; } + /* ok we should expect a certificate verify message now + */ + if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND && optional == OPTIONAL_PACKET) + ret = 0; + else + session->key->certificate_requested = 1; + +cleanup: + _gnutls_buffer_clear(&buf); return ret; } int _gnutls_recv_server_certificate (gnutls_session_t session) { - int datasize; - opaque *data; + gnutls_buffer_st buf; int ret = 0; if (session->internals.auth_struct->gnutls_process_server_certificate != @@ -673,10 +665,9 @@ _gnutls_recv_server_certificate (gnutls_session_t session) { ret = - _gnutls_recv_handshake (session, &data, - &datasize, + _gnutls_recv_handshake (session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, - MANDATORY_PACKET); + MANDATORY_PACKET, &buf); if (ret < 0) { gnutls_assert (); @@ -685,9 +676,9 @@ _gnutls_recv_server_certificate (gnutls_session_t session) ret = session->internals. - auth_struct->gnutls_process_server_certificate (session, data, - datasize); - gnutls_free (data); + auth_struct->gnutls_process_server_certificate (session, buf.data, + buf.length); + _gnutls_buffer_clear(&buf); if (ret < 0) { gnutls_assert (); @@ -705,45 +696,41 @@ _gnutls_recv_server_certificate (gnutls_session_t session) int _gnutls_recv_client_certificate_verify_message (gnutls_session_t session) { - uint8_t *data; - int datasize; + gnutls_buffer_st buf; int ret = 0; - if (session->internals.auth_struct->gnutls_process_client_cert_vrfy != NULL) - { - - if (session->internals.send_cert_req == 0 || - session->key->certificate_requested == 0) - { - return 0; - } - - ret = - _gnutls_recv_handshake (session, &data, - &datasize, - GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, - OPTIONAL_PACKET); - if (ret < 0) - return ret; + if (session->internals.auth_struct->gnutls_process_client_cert_vrfy == NULL) + return 0; - if (ret == 0 && datasize == 0 - && session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) - { - /* certificate was required */ - gnutls_assert (); - return GNUTLS_E_NO_CERTIFICATE_FOUND; - } + if (session->internals.send_cert_req == 0 || + session->key->certificate_requested == 0) + { + return 0; + } - ret = - session->internals. - auth_struct->gnutls_process_client_cert_vrfy (session, data, - datasize); - gnutls_free (data); - if (ret < 0) - return ret; + ret = + _gnutls_recv_handshake (session, + GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, + OPTIONAL_PACKET, &buf); + if (ret < 0) + return ret; + if (ret == 0 && buf.length == 0 + && session->internals.send_cert_req == GNUTLS_CERT_REQUIRE) + { + /* certificate was required */ + gnutls_assert (); + ret = GNUTLS_E_NO_CERTIFICATE_FOUND; + goto cleanup; } + ret = + session->internals. + auth_struct->gnutls_process_client_cert_vrfy (session, buf.data, + buf.length); + +cleanup: + _gnutls_buffer_clear(&buf); return ret; } diff --git a/lib/gnutls_mbuffers.c b/lib/gnutls_mbuffers.c index a150bce927..2eff937b37 100644 --- a/lib/gnutls_mbuffers.c +++ b/lib/gnutls_mbuffers.c @@ -57,7 +57,7 @@ void _mbuffer_head_init (mbuffer_head_st * buf) { buf->head = NULL; - buf->tail = &buf->head; + buf->tail = NULL; buf->length = 0; buf->byte_length = 0; @@ -94,8 +94,40 @@ _mbuffer_enqueue (mbuffer_head_st * buf, mbuffer_st * bufel) buf->length++; buf->byte_length += bufel->msg.size - bufel->mark; - *(buf->tail) = bufel; - buf->tail = &bufel->next; + bufel->prev = buf->tail; + if (buf->tail != NULL) + buf->tail->next = bufel; + else + buf->head = bufel; + buf->tail = bufel; +} + +/* Remove a segment from the buffer. + * + * Cost: O(1) + * + * Returns the buffer following it. + */ +mbuffer_st * +_mbuffer_dequeue (mbuffer_head_st * buf, mbuffer_st * bufel) +{ +mbuffer_st* ret = bufel->next; + + if (buf->tail == bufel) /* if last */ + buf->tail = bufel->prev; + + if (buf->head == bufel) /* if first */ + buf->head = bufel->next; + + if (bufel->prev) + bufel->prev->next = bufel->next; + + buf->length--; + buf->byte_length -= bufel->msg.size - bufel->mark; + + bufel->next = bufel->prev = NULL; + + return ret; } /* Get a reference to the first segment of the buffer and @@ -110,13 +142,18 @@ _mbuffer_head_pop_first (mbuffer_head_st * buf) { mbuffer_st *bufel = buf->head; + if (buf->head == NULL) + return NULL; + buf->head = bufel->next; + if (bufel->next) + bufel->next->prev = NULL; buf->byte_length -= (bufel->msg.size - bufel->mark); buf->length -= 1; if (!buf->head) - buf->tail = &buf->head; + buf->tail = NULL; return bufel; } @@ -159,15 +196,18 @@ _mbuffer_head_get_next (mbuffer_st * cur, gnutls_datum_t * msg) { mbuffer_st *bufel = cur->next; - if (bufel) - { - msg->data = bufel->msg.data + bufel->mark; - msg->size = bufel->msg.size - bufel->mark; - } - else + if (msg) { - msg->data = NULL; - msg->size = 0; + if (bufel) + { + msg->data = bufel->msg.data + bufel->mark; + msg->size = bufel->msg.size - bufel->mark; + } + else + { + msg->data = NULL; + msg->size = 0; + } } return bufel; } @@ -189,13 +229,15 @@ remove_front (mbuffer_head_st * buf) bufel = buf->head; buf->head = bufel->next; + if (bufel->next) + bufel->next->prev = NULL; buf->byte_length -= (bufel->msg.size - bufel->mark); buf->length -= 1; gnutls_free (bufel); if (!buf->head) - buf->tail = &buf->head; + buf->tail = NULL; } /* Remove a specified number of bytes from the start of the buffer. @@ -266,7 +308,7 @@ _mbuffer_alloc (size_t payload_size, size_t maximum_size) return NULL; } - //payload points after the mbuffer_st structure + /* payload points after the mbuffer_st structure */ st->msg.data = (opaque *) st + sizeof (mbuffer_st); st->msg.size = payload_size; st->mark = 0; diff --git a/lib/gnutls_mbuffers.h b/lib/gnutls_mbuffers.h index 96df86e315..46ffe60619 100644 --- a/lib/gnutls_mbuffers.h +++ b/lib/gnutls_mbuffers.h @@ -31,6 +31,7 @@ void _mbuffer_head_init (mbuffer_head_st * buf); void _mbuffer_head_clear (mbuffer_head_st * buf); void _mbuffer_enqueue (mbuffer_head_st * buf, mbuffer_st * bufel); +mbuffer_st* _mbuffer_dequeue (mbuffer_head_st * buf, mbuffer_st * bufel); int _mbuffer_head_remove_bytes (mbuffer_head_st * buf, size_t bytes); mbuffer_st *_mbuffer_alloc (size_t payload_size, size_t maximum_size); @@ -54,32 +55,45 @@ int _mbuffer_linearize (mbuffer_head_st * buf); inline static void _mbuffer_set_udata (mbuffer_st * bufel, void *data, size_t data_size) { - memcpy (bufel->msg.data + bufel->user_mark, data, data_size); - bufel->msg.size = data_size + bufel->user_mark; + memcpy (bufel->msg.data + bufel->mark + bufel->user_mark, data, data_size); + bufel->msg.size = data_size + bufel->user_mark + bufel->mark; } inline static void * _mbuffer_get_uhead_ptr (mbuffer_st * bufel) { - return bufel->msg.data; + return bufel->msg.data + bufel->mark; } inline static void * _mbuffer_get_udata_ptr (mbuffer_st * bufel) { - return bufel->msg.data + bufel->user_mark; + return bufel->msg.data + bufel->user_mark + bufel->mark; } inline static void _mbuffer_set_udata_size (mbuffer_st * bufel, size_t size) { - bufel->msg.size = size + bufel->user_mark; + bufel->msg.size = size + bufel->user_mark + bufel->mark; } inline static size_t _mbuffer_get_udata_size (mbuffer_st * bufel) { - return bufel->msg.size - bufel->user_mark; + return bufel->msg.size - bufel->user_mark - bufel->mark; +} + +/* discards size bytes from the begging of the buffer */ +inline static void +_mbuffer_consume (mbuffer_head_st* buf, mbuffer_st * bufel, size_t size) +{ + bufel->user_mark = 0; + if (bufel->mark+size < bufel->msg.size) + bufel->mark += size; + else + bufel->mark = bufel->msg.size; + + buf->byte_length -= size; } inline static size_t diff --git a/lib/gnutls_record.c b/lib/gnutls_record.c index 61bbc8bb7c..15405be135 100644 --- a/lib/gnutls_record.c +++ b/lib/gnutls_record.c @@ -780,6 +780,7 @@ struct tls_record_st { uint16_t length; uint16_t packet_size; /* header_size + length */ content_type_t type; + int v2:1; /* whether an SSLv2 client hello */ /* the data */ }; @@ -818,15 +819,19 @@ record_read_headers (gnutls_session_t session, /* in order to assist the handshake protocol. * V2 compatibility is a mess. */ - session->internals.v2_hello = record->length; + record->v2 = 1; - _gnutls_record_log ("REC[%p]: V2 packet received. Length: %d\n", - session, record->length); + _gnutls_record_log ("REC[%p]: SSL 2.0 %s packet received. Length: %d\n", + session, + _gnutls_packet2str (record->type), + record->length); } else { /* dtls version 1.0 and TLS version 1.x */ + record->v2 = 0; + record->type = headers[0]; record->version[0] = headers[1]; record->version[1] = headers[2]; @@ -838,6 +843,12 @@ record_read_headers (gnutls_session_t session, } else record->length = _gnutls_read_uint16 (&headers[3]); + + _gnutls_record_log ("REC[%p]: SSL %d.%d %s packet received. Length: %d\n", + session, (int)record->version[0], (int)record->version[1], + _gnutls_packet2str (record->type), + record->length); + } record->packet_size += record->length; @@ -871,6 +882,8 @@ gnutls_datum_t raw; /* raw headers */ } _mbuffer_head_get_first (&session->internals.record_recv_buffer, &raw); + if (raw.size < RECORD_HEADER_SIZE(session)) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); record_read_headers (session, raw.data, type, htype, record); @@ -932,13 +945,13 @@ gnutls_datum_t raw; /* raw headers */ /* This will receive record layer packets and add them to * application_data_buffer and handshake_data_buffer. */ -static ssize_t +ssize_t _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype) { uint64 *packet_sequence; uint8_t *ciphertext; - mbuffer_st* bufel = NULL; + mbuffer_st* bufel = NULL, *decrypted = NULL; int ret; int empty_packet = 0; record_parameters_st *record_params; @@ -1007,30 +1020,35 @@ begin: * move on ! */ ret = _mbuffer_linearize (&session->internals.record_recv_buffer); - if (ret != 0) - { - gnutls_assert (); - return ret; - } - bufel = _mbuffer_head_pop_first (&session->internals.record_recv_buffer); - ciphertext = &bufel->msg.data[record.header_size]; + if (ret < 0) + return gnutls_assert_val(ret); + + bufel = _mbuffer_head_get_first (&session->internals.record_recv_buffer, NULL); + if (bufel == NULL) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + decrypted = _mbuffer_alloc(record.length+EXTRA_COMP_SIZE, record.length+EXTRA_COMP_SIZE); + if (decrypted == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ciphertext = (opaque*)_mbuffer_get_udata_ptr(bufel) + record.header_size; /* decrypt the data we got. */ ret = - _gnutls_decrypt (session, ciphertext, record.length, bufel->msg.data, bufel->maximum_size, + _gnutls_decrypt (session, ciphertext, record.length, + _mbuffer_get_udata_ptr(decrypted), _mbuffer_get_udata_size(decrypted), record.type, record_params, packet_sequence); - bufel->msg.size = ret; + if (ret >= 0) _mbuffer_set_udata_size(decrypted, ret); + _mbuffer_head_remove_bytes (&session->internals.record_recv_buffer, + record.header_size + record.length); if (ret < 0) { gnutls_assert(); goto sanity_check_error; } - /* bufel now holds the decrypted data - */ - /* check for duplicates. We check after the message * is processed and authenticated to avoid someone * messing with our windows. @@ -1049,7 +1067,7 @@ begin: _gnutls_record_log ("REC[%p]: Decrypted Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (packet_sequence), - _gnutls_packet2str (record.type), record.type, bufel->msg.size); + _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted)); /* increase sequence number */ @@ -1068,16 +1086,21 @@ begin: * In that case we go to the beginning and start reading * the next packet. */ - if (bufel->msg.size == 0) + if (_mbuffer_get_udata_size(decrypted) == 0) { - _mbuffer_xfree(&bufel); + _mbuffer_xfree(&decrypted); empty_packet++; goto begin; } + if (record.v2) + decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; + else + decrypted->htype = -1; + ret = record_add_to_buffers (session, record.type, type, htype, - packet_sequence, bufel); + packet_sequence, decrypted); /* bufel is now either deinitialized or buffered somewhere else */ @@ -1094,6 +1117,7 @@ begin: return ret; discard: + /* discard the whole received fragment. */ bufel = _mbuffer_head_pop_first(&session->internals.record_recv_buffer); _mbuffer_xfree(&bufel); return GNUTLS_E_AGAIN; @@ -1111,7 +1135,7 @@ sanity_check_error: session_invalidate (session); cleanup: - _mbuffer_xfree(&bufel); + _mbuffer_xfree(&decrypted); return ret; recv_error: diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index e22dada9ea..e29749ed04 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -246,9 +246,6 @@ _gnutls_handshake_internal_state_init (gnutls_session_t session) /* by default no selected certificate */ session->internals.adv_version_major = 0; session->internals.adv_version_minor = 0; - session->internals.v2_hello = 0; - memset (&session->internals.handshake_header_buffer, 0, - sizeof (handshake_header_buffer_st)); session->internals.direction = 0; /* use out of band data for the last @@ -259,7 +256,7 @@ _gnutls_handshake_internal_state_init (gnutls_session_t session) session->internals.resumable = RESUME_TRUE; - session->internals.dtls.hsk_read_seq = -1; + session->internals.dtls.hsk_read_seq = 0; session->internals.dtls.hsk_write_seq = 0; } @@ -323,7 +320,7 @@ gnutls_init (gnutls_session_t * session, gnutls_connection_end_t con_end) _mbuffer_head_init (&(*session)->internals.record_recv_buffer); _mbuffer_head_init (&(*session)->internals.handshake_send_buffer); - _gnutls_buffer_init (&(*session)->internals.handshake_recv_buffer); + _gnutls_handshake_recv_buffer_init(*session); (*session)->key = gnutls_calloc (1, sizeof (struct gnutls_key_st)); if ((*session)->key == NULL) @@ -1352,7 +1349,7 @@ record_parameters_st *params; int total = 0, ret, iv_size; if (session->internals.initial_negotiation_completed == 0) - return 0; + return RECORD_HEADER_SIZE(session); ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, ¶ms); if (ret < 0) diff --git a/lib/gnutls_str.c b/lib/gnutls_str.c index edcbb868df..c2d53e5adc 100644 --- a/lib/gnutls_str.c +++ b/lib/gnutls_str.c @@ -127,6 +127,8 @@ _gnutls_buffer_append_data (gnutls_buffer_st * dest, const void *data, { size_t tot_len = data_size + dest->length; + if (data_size == 0) return 0; + if (dest->max_length >= tot_len) { size_t unused = MEMSUB (dest->data, dest->allocd); diff --git a/lib/gnutls_v2_compat.c b/lib/gnutls_v2_compat.c index 020ced2456..abc17b7e4b 100644 --- a/lib/gnutls_v2_compat.c +++ b/lib/gnutls_v2_compat.c @@ -105,9 +105,6 @@ _gnutls_read_client_hello_v2 (gnutls_session_t session, opaque * data, uint16_t challenge; opaque session_id[TLS_MAX_SESSION_ID_SIZE]; - /* we only want to get here once - only in client hello */ - session->internals.v2_hello = 0; - DECR_LEN (len, 2); _gnutls_handshake_log diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 6363df9341..c686db67f8 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -414,7 +414,8 @@ extern "C" GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE = 16, GNUTLS_HANDSHAKE_FINISHED = 20, GNUTLS_HANDSHAKE_SUPPLEMENTAL = 23, - GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC = 254 + GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC = 254, + GNUTLS_HANDSHAKE_CLIENT_HELLO_V2 = 1024, } gnutls_handshake_description_t; /** @@ -1635,7 +1636,7 @@ extern "C" #define GNUTLS_E_FILE_ERROR -64 #define GNUTLS_E_TOO_MANY_EMPTY_PACKETS -78 #define GNUTLS_E_UNKNOWN_PK_ALGORITHM -80 - +#define GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS -81 /* returned if libextra functionality was requested but * gnutls_global_init_extra() was not called. |