]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added intermediate handshake layer that will order handshake packets and drop duplicates.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 5 Mar 2011 18:28:46 +0000 (19:28 +0100)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 6 Mar 2011 21:26:50 +0000 (22:26 +0100)
17 files changed:
lib/ext_session_ticket.c
lib/gnutls_buffers.c
lib/gnutls_buffers.h
lib/gnutls_cipher.c
lib/gnutls_dtls.c
lib/gnutls_errors.c
lib/gnutls_handshake.c
lib/gnutls_handshake.h
lib/gnutls_int.h
lib/gnutls_kx.c
lib/gnutls_mbuffers.c
lib/gnutls_mbuffers.h
lib/gnutls_record.c
lib/gnutls_state.c
lib/gnutls_str.c
lib/gnutls_v2_compat.c
lib/includes/gnutls/gnutls.h.in

index f51c127e7b2611c64c0779608e53cf4c3de33089..74fe1741307fd8d324ca56a212efbdeeb9d28798 100644 (file)
@@ -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
index 5c883196637a380ceb51765cf62574a28d50cd0e..c8ad1dc270dc750c05fe0e24dfaf98737c567cf7 100644 (file)
@@ -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;
-}
-
index 2cb42f3fba7ebeefa3eff90fd59899888459789c..0293477541e3251dc50e466ac5647b0432f7314e 100644 (file)
@@ -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
index 64dbbd2e5f2e5b95d6a5609a30928fda06507ed0..d3551519d05152f5cd930700b2fcc9f489ccfe97 100644 (file)
@@ -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;
 }
index 30ecfc5971c7f481db7ac337743c537221f48375..36262c95aedfcfbe29fd0191fd6bf0080913dea7 100644 (file)
@@ -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",
index b5811c04a9879cece2d257e07c24c7372466462a..4d499ab3ac8db69ac5a13f48b5d63eef44a55851 100644 (file)
@@ -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_
index e8c427ff4bb9d57eca420295bfef3a7883eeb37a..d8dcea4c313b36b4dbf9b7ad108c40777e6571d7 100644 (file)
@@ -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);
 
index e3200443cbf153933498b5a6173a3cb903496f80..6792bbe0cea2aecda9a625ea575b2dc3d2426a10 100644 (file)
@@ -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);
index 21d9484333c6a4c09b214a35c8f827b59a1270be..74f6e5f518ebf2e1fb207ab1e90c1a17d134ff96 100644 (file)
@@ -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).
index 7c390f0ec1909aa98cf2abbcf97a050c21fe4949..b9e5b3edbac5138631af105e0eee4bfac4adaa21 100644 (file)
@@ -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;
 }
index a150bce927cc286bea04491f74c153347a3c2295..2eff937b370f6a5ac16c820ad962e35d06a04f5e 100644 (file)
@@ -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;
index 96df86e31589f5033e5c0d773beb64589476f0ca..46ffe60619b450a50da5e3dd41eab77a84f24bac 100644 (file)
@@ -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
index 61bbc8bb7c03fad0cb28744f436a360e4c20e4c5..15405be135ce52f35f8622ffe92ae6ca4210ac6e 100644 (file)
@@ -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:
index e22dada9eabcabf40d1dc3909872ae80286cbca2..e29749ed041071a3e0d815f6d38fda43a394c27b 100644 (file)
@@ -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)
index edcbb868df646e7e7e069ba82334dd31114d896e..c2d53e5adc28516c34555385b854ebf8d7f99886 100644 (file)
@@ -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);
index 020ced24569ea0033671acd6a7880a90fb352eeb..abc17b7e4b8f8a5e0a7740f043f294449f9fec42 100644 (file)
@@ -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
index 6363df93413ca6f410b6e83489cef5b717a636ca..c686db67f88c40270739643ea5a096b0ce5abe2e 100644 (file)
@@ -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.