Ejemplo n.º 1
0
/* This function transmits the flight that has been previously
 * buffered.
 *
 * This function is called from the handshake layer and calls the
 * record layer.
 */
int
_dtls_transmit (gnutls_session_t session)
{
int ret;
uint8_t* buf = NULL;
unsigned int timeout;

  /* PREPARING -> SENDING state transition */
  mbuffer_head_st *const send_buffer =
    &session->internals.handshake_send_buffer;
  mbuffer_st *cur;
  gnutls_handshake_description_t last_type = 0;
  unsigned int diff;
  struct timespec now;
  
  gettime(&now);

  /* If we have already sent a flight and we are operating in a 
   * non blocking way, check if it is time to retransmit or just
   * return.
   */
  if (session->internals.dtls.flight_init != 0 && session->internals.dtls.blocking == 0)
    {
      /* just in case previous run was interrupted */
      ret = _gnutls_io_write_flush (session);
      if (ret < 0)
        {
          gnutls_assert();
          goto cleanup;
        }

      if (session->internals.dtls.last_flight == 0 || !_dtls_is_async(session))
        {
          /* check for ACK */
          ret = _gnutls_io_check_recv(session, 0);
          if (ret == GNUTLS_E_TIMEDOUT)
            {
              /* if no retransmission is required yet just return 
               */
              if (_dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit) < TIMER_WINDOW)
                {
                  gnutls_assert();
                  goto nb_timeout;
                }
            }
          else /* received something */
            {
              if (ret == 0)
                {
                  ret = is_next_hpacket_expected(session);
                  if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
                    goto nb_timeout;
                  if (ret < 0 && ret != GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
                    {
                      gnutls_assert();
                      goto cleanup;
                    }
                  if (ret == 0) goto end_flight;
                  /* if ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET retransmit */
                }
              else
                goto nb_timeout;
            }
        }
    }

  do 
    {
      timeout = TIMER_WINDOW;

      diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.handshake_start_time);
      if (diff >= session->internals.dtls.total_timeout_ms) 
        {
          _gnutls_dtls_log("Session timeout: %u ms\n", diff);
          ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
          goto end_flight;
        }

      diff = _dtls_timespec_sub_ms(&now, &session->internals.dtls.last_retransmit);
      if (session->internals.dtls.flight_init == 0 || diff >= TIMER_WINDOW)
        {
          _gnutls_dtls_log ("DTLS[%p]: %sStart of flight transmission.\n", session,  (session->internals.dtls.flight_init == 0)?"":"re-");
          for (cur = send_buffer->head;
               cur != NULL; cur = cur->next)
            {
              ret = transmit_message (session, cur, &buf);
              if (ret < 0)
                {
                  gnutls_assert();
                  goto end_flight;
                }

              last_type = cur->htype;
            }
          gettime(&session->internals.dtls.last_retransmit);

          if (session->internals.dtls.flight_init == 0)
            {
              session->internals.dtls.flight_init = 1;
              RESET_TIMER;
              timeout = TIMER_WINDOW;

              if (last_type == GNUTLS_HANDSHAKE_FINISHED)
                {
              /* On the last flight we cannot ensure retransmission
               * from here. _dtls_wait_and_retransmit() is being called
               * by handshake.
               */
                  session->internals.dtls.last_flight = 1;
                }
              else
                session->internals.dtls.last_flight = 0;
            }
          else
            {
              UPDATE_TIMER;
            }
        }

      ret = _gnutls_io_write_flush (session);
      if (ret < 0)
        {
          ret = gnutls_assert_val(ret);
          goto cleanup;
        }

      /* last message in handshake -> no ack */
      if (session->internals.dtls.last_flight != 0)
        {
          /* we don't wait here. We just return 0 and
           * if a retransmission occurs because peer didn't receive it
           * we rely on the record or handshake
           * layer calling this function again.
           */
          ret = 0;
          goto cleanup;
        }
      else /* all other messages -> implicit ack (receive of next flight) */
        {
          if (session->internals.dtls.blocking != 0)
            ret = _gnutls_io_check_recv(session, timeout);
          else
            {
              ret = _gnutls_io_check_recv(session, 0);
              if (ret == GNUTLS_E_TIMEDOUT)
                {
                  goto nb_timeout;
                }
            }
          
          if (ret == 0)
            {
              ret = is_next_hpacket_expected(session);
              if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
                goto nb_timeout;

              if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)
                {
                  ret = GNUTLS_E_TIMEDOUT;
                  goto keep_up;
                }
              if (ret < 0)
                {
                  gnutls_assert();
                  goto cleanup;
                }
              goto end_flight;
            }
        }

keep_up:
      gettime(&now);
    } while(ret == GNUTLS_E_TIMEDOUT);

  if (ret < 0)
    {
      ret = gnutls_assert_val(ret);
      goto end_flight;
    }

  ret = 0;

end_flight:
  _gnutls_dtls_log ("DTLS[%p]: End of flight transmission.\n", session);
  _dtls_reset_hsk_state(session);

cleanup:
  if (buf != NULL)
    gnutls_free(buf);

  /* SENDING -> WAITING state transition */
  return ret;

nb_timeout:
  if (buf != NULL)
    gnutls_free(buf);

  RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
/* This function will check if the received record type is
 * the one we actually expect and adds it to the proper
 * buffer. The bufel will be deinitialized after calling
 * this function, even if it fails.
 */
static int
record_add_to_buffers (gnutls_session_t session,
                   struct tls_record_st *recv, content_type_t type,
                   gnutls_handshake_description_t htype, 
                   uint64* seq,
                   mbuffer_st* bufel)
{

  int ret;

  if ((recv->type == type)
      && (type == GNUTLS_APPLICATION_DATA ||
          type == GNUTLS_CHANGE_CIPHER_SPEC ||
          type == GNUTLS_HANDSHAKE))
    {
      _gnutls_record_buffer_put (session, type, seq, bufel);
      
      /* if we received application data as expected then we
       * deactivate the async timer */
      _dtls_async_timer_delete(session);
    }
  else
    {
      /* if the expected type is different than the received 
       */
      switch (recv->type)
        {
        case GNUTLS_ALERT:
          if (bufel->msg.size < 2)
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
              goto unexpected_packet;
            }

          _gnutls_record_log
            ("REC[%p]: Alert[%d|%d] - %s - was received\n", session,
             bufel->msg.data[0], bufel->msg.data[1], gnutls_alert_get_name ((int) bufel->msg.data[1]));

          session->internals.last_alert = bufel->msg.data[1];

          /* if close notify is received and
           * the alert is not fatal
           */
          if (bufel->msg.data[1] == GNUTLS_A_CLOSE_NOTIFY && bufel->msg.data[0] != GNUTLS_AL_FATAL)
            {
              /* If we have been expecting for an alert do 
               */
              session->internals.read_eof = 1;
              ret = GNUTLS_E_SESSION_EOF;
              goto cleanup;
            }
          else
            {
              /* if the alert is FATAL or WARNING
               * return the apropriate message
               */

              gnutls_assert ();
              ret = GNUTLS_E_WARNING_ALERT_RECEIVED;
              if (bufel->msg.data[0] == GNUTLS_AL_FATAL)
                {
                  session_unresumable (session);
                  session_invalidate (session);
                  ret = gnutls_assert_val(GNUTLS_E_FATAL_ALERT_RECEIVED);
                }
              goto cleanup;
            }
          break;

        case GNUTLS_CHANGE_CIPHER_SPEC:
          if (!(IS_DTLS(session)))
            return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
            
          _gnutls_record_buffer_put (session, recv->type, seq, bufel);

          break;

#ifdef ENABLE_HEARTBEAT
        case GNUTLS_HEARTBEAT:
	    ret = _gnutls_heartbeat_handle (session, bufel);
	    goto cleanup;
#endif

        case GNUTLS_APPLICATION_DATA:
          if (session->internals.initial_negotiation_completed == 0)
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
              goto unexpected_packet;
            }


          /* the got_application data is only returned
           * if expecting client hello (for rehandshake
           * reasons). Otherwise it is an unexpected packet
           */
          if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO
                                       && type == GNUTLS_HANDSHAKE))
            {
              /* even if data is unexpected put it into the buffer */
              if ((ret =
                   _gnutls_record_buffer_put (session, recv->type, seq,
                                              bufel)) < 0)
                {
                  gnutls_assert ();
                  goto cleanup;
                }

              return gnutls_assert_val(GNUTLS_E_GOT_APPLICATION_DATA);
            }
          else
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
              goto unexpected_packet;
            }

          break;

        case GNUTLS_HANDSHAKE:
          /* In DTLS we might receive a handshake replay from the peer to indicate
           * the our last TLS handshake messages were not received.
           */
          if (IS_DTLS(session))
            {
              if (type == GNUTLS_CHANGE_CIPHER_SPEC)
                {
                  ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
                  goto unexpected_packet;
                }

              if (_dtls_is_async(session) && _dtls_async_timer_active(session))
                {
                  if (session->security_parameters.entity == GNUTLS_SERVER &&
                      bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
                    {
                      /* client requested rehandshake. Delete the timer */
                      _dtls_async_timer_delete(session);
                    }
                  else
                    {
                      session->internals.recv_state = RECV_STATE_DTLS_RETRANSMIT;
                      ret = _dtls_retransmit(session);
                      if (ret == 0) 
                        {
                          session->internals.recv_state = RECV_STATE_0;
                          ret = gnutls_assert_val(GNUTLS_E_AGAIN);
                          goto unexpected_packet;
                        }
                      goto cleanup;
                    }
                }
            }

          /* This is legal if HELLO_REQUEST is received - and we are a client.
           * If we are a server, a client may initiate a renegotiation at any time.
           */
          if (session->security_parameters.entity == GNUTLS_SERVER &&
              bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
            {
              gnutls_assert ();
              ret =
                _gnutls_record_buffer_put (session, recv->type, seq, bufel);
              if (ret < 0)
                {
                  gnutls_assert ();
                  goto cleanup;
                }
              return GNUTLS_E_REHANDSHAKE;
            }

          /* If we are already in a handshake then a Hello
           * Request is illegal. But here we don't really care
           * since this message will never make it up here.
           */

          /* So we accept it, if it is a Hello. If not, this will
           * fail and trigger flight retransmissions after some time. */
          ret = _gnutls_recv_hello_request (session, bufel->msg.data, bufel->msg.size);
          goto unexpected_packet;

          break;
        default:

          _gnutls_record_log
            ("REC[%p]: Received unexpected packet %d (%s) expecting %d (%s)\n",
             session, recv->type, _gnutls_packet2str(recv->type), type, _gnutls_packet2str(type));

          gnutls_assert ();
          ret = GNUTLS_E_UNEXPECTED_PACKET;
          goto unexpected_packet;
        }
    }

  return 0;

unexpected_packet:
  if (IS_DTLS(session) && ret != GNUTLS_E_REHANDSHAKE)
    {
      _mbuffer_xfree(&bufel);
      RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
    }

cleanup:
  _mbuffer_xfree(&bufel);
  return ret;

}
Ejemplo n.º 4
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;
}