Esempio n. 1
0
/* Takes a buffer in multiple chunks and puts all the data in a single
 * contiguous segment, ensuring that the @align_pos is 16-byte aligned.
 *
 * Returns 0 on success or an error code otherwise.
 *
 * Cost: O(n)
 * n: number of segments initially in the buffer
 */
int _mbuffer_linearize_align16(mbuffer_head_st * buf, unsigned align_pos)
{
	mbuffer_st *bufel, *cur;
	gnutls_datum_t msg;
	size_t pos = 0;

	if (buf->length == 0) {
		/* Nothing to do */
		return 0;
	}
	
	bufel = _mbuffer_head_get_first(buf, NULL);
	if (buf->length == 1 && is_aligned16(bufel, align_pos))
		return 0;

	bufel = _mbuffer_alloc_align16(buf->byte_length, align_pos);
	if (!bufel) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}

	for (cur = _mbuffer_head_get_first(buf, &msg);
	     msg.data != NULL; cur = _mbuffer_head_get_next(cur, &msg)) {
		memcpy(&bufel->msg.data[pos], msg.data, msg.size);
		bufel->msg.size += msg.size;
		pos += msg.size;
	}

	_mbuffer_head_clear(buf);
	_mbuffer_enqueue(buf, bufel);

	return 0;
}
Esempio n. 2
0
/* This function writes the data that are left in the
 * Handshake write buffer (ie. because the previous write was
 * interrupted.
 *
 */
ssize_t
_gnutls_handshake_io_write_flush (gnutls_session_t session)
{
  mbuffer_head_st *const send_buffer =
    &session->internals.handshake_send_buffer;
  gnutls_datum_t msg;
  int ret;
  uint16_t epoch;
  ssize_t total = 0;
  mbuffer_st *cur;

  _gnutls_write_log ("HWRITE FLUSH: %d bytes in buffer.\n",
                     (int) send_buffer->byte_length);

  if (IS_DTLS(session))
    return _dtls_transmit(session);

  for (cur = _mbuffer_head_get_first (send_buffer, &msg);
       cur != NULL; cur = _mbuffer_head_get_first (send_buffer, &msg))
    {
      epoch = cur->epoch;

      ret = _gnutls_send_int (session, cur->type,
                              cur->htype,
                              epoch,
                              msg.data, msg.size, 0);

      if (ret >= 0)
        {
          total += ret;
          
          ret = _mbuffer_head_remove_bytes (send_buffer, ret);
          if (ret == 1)
            _gnutls_epoch_refcount_dec(session, epoch);

          _gnutls_write_log ("HWRITE: wrote %d bytes, %d bytes left.\n",
                             ret, (int) send_buffer->byte_length);

        }
      else
        {
          _gnutls_write_log ("HWRITE error: code %d, %d bytes left.\n",
                             ret, (int) send_buffer->byte_length);

          gnutls_assert ();
          return ret;
        }
    }

  return _gnutls_io_write_flush (session);
}
Esempio n. 3
0
int
_gnutls_record_buffer_get (content_type_t type,
                           gnutls_session_t session, opaque * data,
                           size_t length, opaque seq[8])
{
gnutls_datum_t msg;
mbuffer_st* bufel;

  if (length == 0 || data == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg);
  if (bufel == NULL)
    return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

  if (type != bufel->type)
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

  if (msg.size <= length)
    length = msg.size;

  if (seq)
    memcpy(seq, bufel->record_sequence.i, 8);

  memcpy(data, msg.data, length);
  _mbuffer_head_remove_bytes(&session->internals.record_buffer, length);
  
  return length;
}
Esempio n. 4
0
/* This function writes the data that are left in the
 * TLS write buffer (ie. because the previous write was
 * interrupted.
 */
ssize_t
_gnutls_io_write_flush (gnutls_session_t session)
{
  gnutls_datum_t msg;
  mbuffer_head_st *send_buffer = &session->internals.record_send_buffer;
  int ret;
  ssize_t sent = 0, tosend = 0;
  giovec_t iovec[MAX_QUEUE];
  int i = 0;
  mbuffer_st *cur;

  _gnutls_write_log ("WRITE FLUSH: %d bytes in buffer.\n",
                     (int) send_buffer->byte_length);

  for (cur = _mbuffer_head_get_first (send_buffer, &msg);
       cur != NULL; cur = _mbuffer_head_get_next (cur, &msg))
    {
      iovec[i].iov_base = msg.data;
      iovec[i++].iov_len = msg.size;
      tosend += msg.size;

      /* we buffer up to MAX_QUEUE messages */
      if (i >= MAX_QUEUE)
        {
          gnutls_assert ();
          return GNUTLS_E_INTERNAL_ERROR;
        }
    }

  ret = _gnutls_writev (session, iovec, i);
  if (ret >= 0)
    {
      _mbuffer_head_remove_bytes (send_buffer, ret);
      _gnutls_write_log ("WRITE: wrote %d bytes, %d bytes left.\n",
                         ret, (int) send_buffer->byte_length);

      sent += ret;
    }
  else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
    {
      _gnutls_write_log ("WRITE interrupted: %d bytes left.\n",
                         (int) send_buffer->byte_length);
      return ret;
    }
  else
    {
      _gnutls_write_log ("WRITE error: code %d, %d bytes left.\n",
                         ret, (int) send_buffer->byte_length);

      gnutls_assert ();
      return ret;
    }

  if (sent < tosend)
    {
      return gnutls_assert_val(GNUTLS_E_AGAIN);
    }

  return sent;
}
Esempio n. 5
0
int
_gnutls_record_buffer_get(content_type_t type,
			  gnutls_session_t session, uint8_t * data,
			  size_t length, uint8_t seq[8])
{
	gnutls_datum_t msg;
	mbuffer_st *bufel;

	if (length == 0 || data == NULL) {
		gnutls_assert();
		return GNUTLS_E_INVALID_REQUEST;
	}

	bufel =
	    _mbuffer_head_get_first(&session->internals.record_buffer,
				    &msg);
	if (bufel == NULL)
		return
		    gnutls_assert_val
		    (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

	if (type != bufel->type) {
		if (IS_DTLS(session))
			_gnutls_audit_log(session,
					  "Discarded unexpected %s (%d) packet (expecting: %s (%d))\n",
					  _gnutls_packet2str(bufel->type),
					  (int) bufel->type,
					  _gnutls_packet2str(type),
					  (int) type);
		else
			_gnutls_debug_log("received unexpected packet: %s(%d)\n",
						_gnutls_packet2str(bufel->type), (int)bufel->type);

		_mbuffer_head_remove_bytes(&session->internals.
					   record_buffer, msg.size);
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
	}

	if (msg.size <= length)
		length = msg.size;

	if (seq)
		memcpy(seq, bufel->record_sequence.i, 8);

	memcpy(data, msg.data, length);
	_mbuffer_head_remove_bytes(&session->internals.record_buffer,
				   length);

	return length;
}
Esempio n. 6
0
/* This is a receive function for the gnutls handshake 
 * protocol. Makes sure that we have received all data.
 *
 * htype is the next handshake packet expected.
 */
int _gnutls_parse_record_buffered_msgs(gnutls_session_t session)
{
	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;

	bufel =
	    _mbuffer_head_get_first(&session->internals.record_buffer,
				    &msg);
	if (bufel == NULL)
		return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;

	if (!IS_DTLS(session)) {
		ssize_t append, header_size;

		do {
			if (bufel->type != GNUTLS_HANDSHAKE)
				return
				    gnutls_assert_val
				    (GNUTLS_E_UNEXPECTED_PACKET);

			if (unlikely
			    (session->internals.handshake_recv_buffer_size == 0 &&
			     msg.size < HANDSHAKE_HEADER_SIZE(session) &&
			     session->internals.handshake_header_recv_buffer.byte_length <
			     HANDSHAKE_HEADER_SIZE(session) - msg.size)) {
				bufel = _mbuffer_head_pop_first(&session->internals.record_buffer);
				_mbuffer_enqueue(&session->internals.handshake_header_recv_buffer,
						 bufel);
				break;
			} else if (session->internals.handshake_recv_buffer_size >
				   0 && recv_buf[0].length > recv_buf[0].data.length) {
				/* this is the rest of a previous message */
				append = MIN(msg.size,
					     recv_buf[0].length -
					     recv_buf[0].data.length);

				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 */
				if (unlikely
				    (session->internals.
				     handshake_header_recv_buffer.length > 0)) {
					bufel = _mbuffer_head_pop_first(&session->internals.
									record_buffer);
					_mbuffer_enqueue(&session->internals.
							 handshake_header_recv_buffer,
							 bufel);
					ret = _mbuffer_linearize_align16(&session->internals.
									 handshake_header_recv_buffer,
									 get_total_headers(session));
					if (ret < 0)
						return gnutls_assert_val(ret);
					bufel = _mbuffer_head_pop_first(&session->internals.
									handshake_header_recv_buffer);
					_mbuffer_head_push_first(&session->internals.
								 record_buffer,
								 bufel);
				}

				ret =
				    parse_handshake_header(session, bufel,
							   &recv_buf[0]);
				if (ret < 0)
					return gnutls_assert_val(ret);

				header_size = ret;
				session->internals.
				    handshake_recv_buffer_size = 1;

				_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_set_uhead_size(bufel, 0);
				_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 0;
			}
			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 */

		handshake_buffer_st tmp;

		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 {
				if (bufel->type != GNUTLS_HANDSHAKE) {
					gnutls_assert();
					goto next;	/* ignore packet */
				}

				_gnutls_handshake_buffer_init(&tmp);

				ret =
				    parse_handshake_header(session, bufel,
							   &tmp);
				if (ret < 0) {
					gnutls_assert();
					_gnutls_audit_log(session,
							  "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);

			}
			while (_mbuffer_get_udata_size(bufel) > 0);

			prev = bufel;
			bufel =
			    _mbuffer_dequeue(&session->internals.
					     record_buffer, bufel);

			_mbuffer_xfree(&prev);
			continue;

		      next:
			bufel = _mbuffer_head_get_next(bufel, NULL);
		}
		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) {
			_gnutls_audit_log(session,
					  "Discarded replayed handshake packet with sequence %d\n",
					  recv_buf[LAST_ELEMENT].sequence);
			_gnutls_handshake_buffer_clear(&recv_buf
						       [LAST_ELEMENT]);
			session->internals.handshake_recv_buffer_size--;
		}

		return 0;
	}
}
Esempio n. 7
0
/* @ms: is the number of milliseconds to wait for data. Use zero for indefinite.
 *
 * This will receive record layer packets and add them to 
 * application_data_buffer and handshake_data_buffer.
 *
 * If the htype is not -1 then handshake timeouts
 * will be enforced.
 */
ssize_t
_gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type,
                         gnutls_handshake_description_t htype, unsigned int ms)
{
  uint64 *packet_sequence;
  gnutls_datum_t ciphertext;
  mbuffer_st* bufel = NULL, *decrypted = NULL;
  gnutls_datum_t t;
  int ret;
  unsigned int empty_fragments = 0;
  record_parameters_st *record_params;
  record_state_st *record_state;
  struct tls_record_st record;

begin:

  if (empty_fragments > session->internals.priorities.max_empty_records)
    {
      gnutls_assert ();
      return GNUTLS_E_TOO_MANY_EMPTY_PACKETS;
    }

  if (session->internals.read_eof != 0)
    {
      /* if we have already read an EOF
       */
      return 0;
    }
  else if (session_is_valid (session) != 0
           || session->internals.may_not_read != 0)
    return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);

  /* get the record state parameters */
  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
  if (ret < 0)
    return gnutls_assert_val (ret);

  /* Safeguard against processing data with an incomplete cipher state. */
  if (!record_params->initialized)
    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);

  record_state = &record_params->read;

  /* receive headers */
  ret = recv_headers(session, type, htype, &record, &ms);
  if (ret < 0)
    {
      ret = gnutls_assert_val_fatal(ret);
      goto recv_error;
    }

  if (IS_DTLS(session)) 
    packet_sequence = &record.sequence;
  else
    packet_sequence = &record_state->sequence_number;

  /* Read the packet data and insert it to record_recv_buffer.
   */
  ret =
       _gnutls_io_read_buffered (session, record.packet_size,
                                 record.type, &ms);
  if (ret != record.packet_size)
    {
      gnutls_assert();
      goto recv_error;
    }

  /* ok now we are sure that we have read all the data - so
   * move on !
   */
  ret = _mbuffer_linearize (&session->internals.record_recv_buffer);
  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);

  /* We allocate the maximum possible to allow few compressed bytes to expand to a
   * full record.
   */
  decrypted = _mbuffer_alloc(record.length, record.length);
  if (decrypted == NULL)
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

  ciphertext.data = (uint8_t*)_mbuffer_get_udata_ptr(bufel) + record.header_size;
  ciphertext.size = record.length;

  /* decrypt the data we got. 
   */
  t.data = _mbuffer_get_udata_ptr(decrypted);
  t.size = _mbuffer_get_udata_size(decrypted);
  ret =
    _gnutls_decrypt (session, &ciphertext, &t,
		     record.type, record_params, packet_sequence);
  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();
      _gnutls_audit_log(session, "Discarded message[%u] due to invalid decryption\n", 
            (unsigned int)_gnutls_uint64touint32 (packet_sequence));
      goto sanity_check_error;
    }

  /* check for duplicates. We check after the message
   * is processed and authenticated to avoid someone
   * messing with our windows.
   */
  if (IS_DTLS(session) && session->internals.no_replay_protection == 0)
    {
      ret = _dtls_record_check(record_params, packet_sequence);
      if (ret < 0)
        {
          _gnutls_audit_log(session, "Discarded duplicate message[%u.%u]: %s\n",
            (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1],
            (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type));
          goto sanity_check_error;
        }
      _gnutls_record_log
        ("REC[%p]: Decrypted Packet[%u.%u] %s(%d) with length: %d\n", session,
        (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1],
        (unsigned int) _gnutls_uint64touint32 (packet_sequence),
        _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted));
    }
  else
    {
      _gnutls_record_log
        ("REC[%p]: Decrypted Packet[%u] %s(%d) with length: %d\n", session,
        (unsigned int) _gnutls_uint64touint32 (packet_sequence),
        _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted));
    }

  /* increase sequence number 
   */
  if (!IS_DTLS(session) && sequence_increment (session, &record_state->sequence_number) != 0)
    {
      session_invalidate (session);
      gnutls_assert ();
      ret = GNUTLS_E_RECORD_LIMIT_REACHED;
      goto sanity_check_error;
    }

/* (originally for) TLS 1.0 CBC protection. 
 * Actually this code is called if we just received
 * an empty packet. An empty TLS packet is usually
 * sent to protect some vulnerabilities in the CBC mode.
 * In that case we go to the beginning and start reading
 * the next packet.
 */
  if (_mbuffer_get_udata_size(decrypted) == 0)
    {
      _mbuffer_xfree(&decrypted);
      empty_fragments++;
      goto begin;
    }

  if (record.v2)
    decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;
  else
    {
      uint8_t * p = _mbuffer_get_udata_ptr(decrypted);
      decrypted->htype = p[0];
    }

  ret =
    record_add_to_buffers (session, &record, type, htype, 
                           packet_sequence, decrypted);

  /* bufel is now either deinitialized or buffered somewhere else */

  if (ret < 0)
    return gnutls_assert_val(ret);

  return ret;

discard:
  session->internals.dtls.packets_dropped++;

  /* discard the whole received fragment. */
  bufel = _mbuffer_head_pop_first(&session->internals.record_recv_buffer);
  _mbuffer_xfree(&bufel);
  return gnutls_assert_val(GNUTLS_E_AGAIN);

sanity_check_error:
  if (IS_DTLS(session))
    {
      session->internals.dtls.packets_dropped++;
      ret = gnutls_assert_val(GNUTLS_E_AGAIN);
      goto cleanup;
    }

  session_unresumable (session);
  session_invalidate (session);

cleanup:
  _mbuffer_xfree(&decrypted);
  return ret;

recv_error:
  if (ret < 0 && (gnutls_error_is_fatal (ret) == 0 || ret == GNUTLS_E_TIMEDOUT))
    return ret;

  if (type == GNUTLS_ALERT) /* we were expecting close notify */
    {
      session_invalidate (session);
      gnutls_assert ();
      return 0;             
    }

  if (IS_DTLS(session))
    {
      goto discard;
    }

  session_invalidate (session);
  session_unresumable (session);

  if (ret == 0)
    return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
  else
    return ret;
}
Esempio n. 8
0
static int recv_headers( gnutls_session_t session, content_type_t type, 
                         gnutls_handshake_description_t htype, 
                         struct tls_record_st* record,
                         unsigned int *ms)
{
int ret;
gnutls_datum_t raw; /* raw headers */
  /* Read the headers.
   */
  record->header_size = record->packet_size = RECORD_HEADER_SIZE(session);

  ret =
       _gnutls_io_read_buffered (session, record->header_size, -1, ms);
  if (ret != record->header_size)
    {
      if (ret < 0 && gnutls_error_is_fatal (ret) == 0)
        return ret;
      
      if (ret > 0)
        ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
      else if (ret == 0)
        ret = GNUTLS_E_PREMATURE_TERMINATION;

      return gnutls_assert_val(ret);
    }

  ret = _mbuffer_linearize (&session->internals.record_recv_buffer);
  if (ret < 0)
    return gnutls_assert_val(ret);

  _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);

  /* Check if the DTLS epoch is valid */
  if (IS_DTLS(session)) 
    {
      if (_gnutls_epoch_is_valid(session, record->epoch) == 0)
        {
          _gnutls_audit_log(session, "Discarded message[%u] with invalid epoch %u.\n",
            (unsigned int)_gnutls_uint64touint32 (&record->sequence), 
            (unsigned int)record->sequence.i[0]*256+(unsigned int)record->sequence.i[1]);
          gnutls_assert();
          /* doesn't matter, just a fatal error */
          return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
        }
    }

  /* Here we check if the Type of the received packet is
   * ok. 
   */
  if ((ret = check_recv_type (session, record->type)) < 0)
    return gnutls_assert_val(ret);

  /* Here we check if the advertized version is the one we
   * negotiated in the handshake.
   */
  if ((ret = record_check_version (session, htype, record->version)) < 0)
    return gnutls_assert_val(ret);

  if (record->length > MAX_RECV_SIZE(session))
    {
      _gnutls_audit_log
        (session, "Received packet with illegal length: %u\n", (unsigned int)record->length);
      return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
    }

  _gnutls_record_log
    ("REC[%p]: Expected Packet %s(%d)\n", session,
     _gnutls_packet2str (type), type);
  _gnutls_record_log ("REC[%p]: Received Packet %s(%d) with length: %d\n",
                      session,
                      _gnutls_packet2str (record->type), record->type, record->length);

  
  return 0;
}
Esempio n. 9
0
/*
 * Return zero if session tickets haven't been enabled.
 */
int _gnutls_recv_new_session_ticket(gnutls_session_t session)
{
	uint8_t *p;
	int data_size;
	gnutls_buffer_st buf;
	uint16_t ticket_len;
	int ret;
	session_ticket_ext_st *priv = NULL;
	gnutls_ext_priv_data_t epriv;

	if (session->internals.flags & GNUTLS_NO_TICKETS)
		return 0;
	if (!session->internals.session_ticket_renew)
		return 0;

	/* This is the last flight and peer cannot be sure
	 * we have received it unless we notify him. So we
	 * wait for a message and retransmit if needed. */
	if (IS_DTLS(session) && !_dtls_is_async(session)) {
		unsigned have;
		mbuffer_st *bufel = NULL;

		have = gnutls_record_check_pending(session) +
		       record_check_unprocessed(session);

		if (have != 0) {
			bufel = _mbuffer_head_get_first(&session->internals.record_buffer, NULL);
		}

		if (have == 0 || (bufel && bufel->type != GNUTLS_HANDSHAKE)) {
			ret = _dtls_wait_and_retransmit(session);
			if (ret < 0)
				return gnutls_assert_val(ret);
		}
	}

	ret = _gnutls_recv_handshake(session,
				     GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
				     0, &buf);
	if (ret < 0)
		return gnutls_assert_val_fatal(ret);

	p = buf.data;
	data_size = buf.length;

	DECR_LENGTH_COM(data_size, 4, ret =
			GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
			goto error);
	/* skip over lifetime hint */
	p += 4;

	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, ret =
			GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
			goto error);

	priv = gnutls_calloc(1, sizeof(*priv));
	if (!priv) {
		gnutls_assert();
		ret = GNUTLS_E_MEMORY_ERROR;
		goto error;
	}
	priv->session_ticket =
	    gnutls_realloc_fast(priv->session_ticket, ticket_len);
	if (!priv->session_ticket) {
		gnutls_free(priv);
		gnutls_assert();
		ret = GNUTLS_E_MEMORY_ERROR;
		goto error;
	}
	memcpy(priv->session_ticket, p, ticket_len);
	priv->session_ticket_len = ticket_len;
	epriv = priv;

	/* Discard the current session ID.  (RFC5077 3.4) */
	ret =
	    _gnutls_generate_session_id(session->security_parameters.
					session_id,
					&session->security_parameters.
					session_id_size);
	if (ret < 0) {
		gnutls_assert();
		session_ticket_deinit_data(epriv);
		ret = GNUTLS_E_INTERNAL_ERROR;
		goto error;
	}
	ret = 0;

	_gnutls_handshake_log
		    ("HSK[%p]: received session ticket\n", session);
	session->internals.hsk_flags |= HSK_TICKET_RECEIVED;

	_gnutls_hello_ext_set_priv(session,
			GNUTLS_EXTENSION_SESSION_TICKET,
			epriv);

      error:
	_gnutls_buffer_clear(&buf);

	return ret;
}
Esempio n. 10
0
/* This is a receive function for the gnutls handshake 
 * protocol. Makes sure that we have received all data.
 */
static int
parse_record_buffered_msgs (gnutls_session_t session,
                               gnutls_handshake_description_t htype,
                               handshake_buffer_st * hsk)
{
  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;

  bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg);
  if (bufel == NULL)
    return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;

  if (!IS_DTLS(session))
    {
      ssize_t remain, append, header_size;

      do
        {
          if (bufel->type != GNUTLS_HANDSHAKE)
            return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

          /* if we have a half received message the complete it.
           */
          remain =  recv_buf[0].length -
                recv_buf[0].data.length;

          /* this is the rest of a previous message */
          if (session->internals.handshake_recv_buffer_size > 0 && 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);

              header_size = ret;
              session->internals.handshake_recv_buffer_size = 1;

              _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_set_uhead_size(bufel, 0);
              _mbuffer_head_remove_bytes(&session->internals.record_buffer, data_size+header_size);

              if (cmp_hsk_types(htype, recv_buf[0].htype) == 0)
                { /* an unexpected packet */
                  hsk->htype = recv_buf[0].htype;
                  return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET);
                }

            }

          /* 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 */
    {
      handshake_buffer_st tmp;

      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
            {
              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);

            }
          while(_mbuffer_get_udata_size(bufel) > 0);

          prev = bufel;
          bufel = _mbuffer_dequeue(&session->internals.record_buffer, bufel);

          _mbuffer_xfree(&prev);
          continue;

next:
          bufel = _mbuffer_head_get_next(bufel, NULL);
        }
      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)
        {
          _gnutls_audit_log("Discarded replayed handshake packet with sequence %d\n", recv_buf[LAST_ELEMENT].sequence);
          _gnutls_handshake_buffer_clear(&recv_buf[LAST_ELEMENT]);
          session->internals.handshake_recv_buffer_size--;
        }

        return get_last_packet(session, htype, hsk);
    }
}