summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2011-03-05 19:28:46 +0100
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2011-03-06 22:26:50 +0100
commitb6b52f6c19ef13337892be120d29f62526fbb15d (patch)
treefde54eaa8c1106f05fe193a6f047511f7b72d2c2 /lib
parent789b253b7946c1c0136c4f795afa37ffc75fdd80 (diff)
downloadgnutls-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.c32
-rw-r--r--lib/gnutls_buffers.c436
-rw-r--r--lib/gnutls_buffers.h55
-rw-r--r--lib/gnutls_cipher.c3
-rw-r--r--lib/gnutls_dtls.c4
-rw-r--r--lib/gnutls_errors.c2
-rw-r--r--lib/gnutls_handshake.c353
-rw-r--r--lib/gnutls_handshake.h6
-rw-r--r--lib/gnutls_int.h72
-rw-r--r--lib/gnutls_kx.c253
-rw-r--r--lib/gnutls_mbuffers.c70
-rw-r--r--lib/gnutls_mbuffers.h26
-rw-r--r--lib/gnutls_record.c68
-rw-r--r--lib/gnutls_state.c9
-rw-r--r--lib/gnutls_str.c2
-rw-r--r--lib/gnutls_v2_compat.c3
-rw-r--r--lib/includes/gnutls/gnutls.h.in5
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, &params);
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.