Exemple #1
0
/* 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,
                               gnutls_handshake_description_t htype,
                               handshake_buffer_st * hsk)
{
  int ret;

  ret = get_last_packet(session, htype, hsk);
  if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
    {
      return gnutls_assert_val(ret);
    }

  /* try using the already existing records before
   * trying to receive.
   */
  ret = parse_record_buffered_msgs(session, htype, hsk);
  if (IS_DTLS(session))
    {
      if (ret >= 0)
        return ret;
    }
  else
    {
      if ((ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && ret < 0) || ret >= 0)
        return gnutls_assert_val(ret);
    }

  /* 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_fatal(ret);

  return parse_record_buffered_msgs(session, htype, hsk); 
}
Exemple #2
0
/* 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,
			      gnutls_handshake_description_t htype,
			      handshake_buffer_st * hsk,
			      unsigned int optional)
{
	int ret;
	unsigned int tleft = 0;
	int retries = 7;

	ret = get_last_packet(session, htype, hsk, optional);
	if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED &&
	    ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE &&
	    ret != GNUTLS_E_INT_CHECK_AGAIN) {
		return gnutls_assert_val(ret);
	}

	/* try using the already existing records before
	 * trying to receive.
	 */
	ret = _gnutls_parse_record_buffered_msgs(session);

	if (ret == 0) {
		ret = get_last_packet(session, htype, hsk, optional);
	}

	if (IS_DTLS(session)) {
		if (ret >= 0)
			return ret;
	} else {
		if ((ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
		     && ret < 0) || ret >= 0)
			return gnutls_assert_val(ret);
	}

	if (htype != (gnutls_handshake_description_t) -1) {
		ret = handshake_remaining_time(session);
		if (ret < 0)
			return gnutls_assert_val(ret);
		tleft = ret;
	}

	do {
		/* if we don't have a complete message waiting for us, try 
		 * receiving more */
		ret =
		    _gnutls_recv_in_buffers(session, GNUTLS_HANDSHAKE, htype,
					    tleft);
		if (ret < 0)
			return gnutls_assert_val_fatal(ret);

		ret = _gnutls_parse_record_buffered_msgs(session);
		if (ret == 0) {
			ret = get_last_packet(session, htype, hsk, optional);
		}
		/* we put an upper limit (retries) to the number of partial handshake
		 * messages in a record packet. */
	} while(IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN && retries-- > 0);

	if (unlikely(IS_DTLS(session) && ret == GNUTLS_E_INT_CHECK_AGAIN)) {
		ret = gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
	}

	return ret;
}
Exemple #3
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;
}
Exemple #4
0
int _gnutls_recv_server_certificate_status(gnutls_session_t session)
{
	uint8_t *data;
	int data_size;
	size_t r_size;
	gnutls_buffer_st buf;
	int ret;
	status_request_ext_st *priv = NULL;
	extension_priv_data_t epriv;

	ret =
	    _gnutls_ext_get_session_data(session,
					 GNUTLS_EXTENSION_STATUS_REQUEST,
					 &epriv);
	if (ret < 0)
		return 0;

	priv = epriv.ptr;

	if (!priv->expect_cstatus)
		return 0;

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

	priv->expect_cstatus = 0;

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

	/* minimum message is type (1) + response (3) + data */
	if (data_size == 0)
		return 0;
	else if (data_size < 4)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	if (data[0] != 0x01) {
		gnutls_assert();
		_gnutls_handshake_log("EXT[%p]: unknown status_type %d\n",
				      session, data[0]);
		return 0;
	}
	DECR_LENGTH_COM(data_size, 1, ret =
			GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
			goto error);
	data++;

	DECR_LENGTH_COM(data_size, 3, ret =
			GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
			goto error);
	r_size = _gnutls_read_uint24(data);
	data += 3;

	DECR_LENGTH_COM(data_size, r_size, ret =
			GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
			goto error);

	ret = _gnutls_set_datum(&priv->response, data, r_size);
	if (ret < 0)
		goto error;

	ret = 0;

      error:
	_gnutls_buffer_clear(&buf);

	return ret;
}
Exemple #5
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;
}
Exemple #6
0
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;
    extension_priv_data_t epriv;

    ret =
        _gnutls_ext_get_session_data(session,
                                     GNUTLS_EXTENSION_SESSION_TICKET,
                                     &epriv);
    if (ret < 0) {
        gnutls_assert();
        return 0;
    }
    priv = epriv;

    if (!priv->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) &&
            (gnutls_record_check_pending(session) +
             record_check_unprocessed(session)) == 0) {
        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->session_ticket =
        gnutls_realloc_fast(priv->session_ticket, ticket_len);
    if (!priv->session_ticket) {
        gnutls_assert();
        ret = GNUTLS_E_MEMORY_ERROR;
        goto error;
    }
    memcpy(priv->session_ticket, p, ticket_len);
    priv->session_ticket_len = ticket_len;

    /* 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();
        gnutls_free(priv->session_ticket);
        priv->session_ticket = NULL;
        ret = GNUTLS_E_INTERNAL_ERROR;
        goto error;
    }
    ret = 0;

error:
    _gnutls_buffer_clear(&buf);

    return ret;
}