Ejemplo n.º 1
0
/* Receive TLS/IA data.  Store received TLS/IA message type in
   *MSG_TYPE, and the data in DATA of max SIZEOFDATA size.  Return the
   number of bytes read, or an error code. */
static ssize_t
_gnutls_recv_inner_application (gnutls_session_t session,
				gnutls_ia_apptype_t * msg_type,
				opaque * data, size_t sizeofdata)
{
  ssize_t len;
  uint32_t len24;
  opaque pkt[4];

  len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, pkt, 4);
  if (len != 4)
    {
      gnutls_assert ();
      return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
    }

  *msg_type = pkt[0];
  len24 = _gnutls_read_uint24 (&pkt[1]);

  if (*msg_type != GNUTLS_IA_APPLICATION_PAYLOAD && len24 != CHECKSUM_SIZE)
    {
      gnutls_assert ();
      return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
    }

  if (sizeofdata < len24)
    {
      /* XXX push back pkt to IA buffer? */
      gnutls_assert ();
      return GNUTLS_E_SHORT_MEMORY_BUFFER;
    }

  if (len24 > 0)
    {
      uint32_t tmplen = len24;

      len24 = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1,
				data, tmplen);
      if (len24 != tmplen)
	{
	  gnutls_assert ();
	  /* XXX Correct? */
	  return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
	}
    }

  return len24;
}
Ejemplo n.º 2
0
/**
 * gnutls_bye:
 * @session: is a #gnutls_session_t structure.
 * @how: is an integer
 *
 * Terminates the current TLS/SSL connection. The connection should
 * have been initiated using gnutls_handshake().  @how should be one
 * of %GNUTLS_SHUT_RDWR, %GNUTLS_SHUT_WR.
 *
 * In case of %GNUTLS_SHUT_RDWR the TLS session gets
 * terminated and further receives and sends will be disallowed.  If
 * the return value is zero you may continue using the underlying
 * transport layer. %GNUTLS_SHUT_RDWR sends an alert containing a close
 * request and waits for the peer to reply with the same message.
 *
 * In case of %GNUTLS_SHUT_WR the TLS session gets terminated
 * and further sends will be disallowed. In order to reuse the
 * connection you should wait for an EOF from the peer.
 * %GNUTLS_SHUT_WR sends an alert containing a close request.
 *
 * Note that not all implementations will properly terminate a TLS
 * connection.  Some of them, usually for performance reasons, will
 * terminate only the underlying transport layer, and thus not
 * distinguishing between a malicious party prematurely terminating 
 * the connection and normal termination. 
 *
 * This function may also return %GNUTLS_E_AGAIN or
 * %GNUTLS_E_INTERRUPTED; cf.  gnutls_record_get_direction().
 *
 * Returns: %GNUTLS_E_SUCCESS on success, or an error code, see
 *   function documentation for entire semantics.
 **/
int
gnutls_bye (gnutls_session_t session, gnutls_close_request_t how)
{
  int ret = 0;

  switch (STATE)
    {
    case STATE0:
    case STATE60:
      ret = _gnutls_io_write_flush (session);
      STATE = STATE60;
      if (ret < 0)
        {
          gnutls_assert ();
          return ret;
        }

    case STATE61:
      ret =
        gnutls_alert_send (session, GNUTLS_AL_WARNING, GNUTLS_A_CLOSE_NOTIFY);
      STATE = STATE61;
      if (ret < 0)
        {
          gnutls_assert ();
          return ret;
        }

    case STATE62:
      STATE = STATE62;
      if (how == GNUTLS_SHUT_RDWR)
        {
          do
            {
              ret = _gnutls_recv_int (session, GNUTLS_ALERT, -1, NULL, 0, NULL, 
                                      session->internals.record_timeout_ms);
            }
          while (ret == GNUTLS_E_GOT_APPLICATION_DATA);

          if (ret >= 0)
            session->internals.may_not_read = 1;

          if (ret < 0)
            {
              gnutls_assert ();
              return ret;
            }
        }
      STATE = STATE62;

      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  STATE = STATE0;

  session->internals.may_not_write = 1;
  return 0;
}
Ejemplo n.º 3
0
/**
 * gnutls_record_recv:
 * @session: is a #gnutls_session_t structure.
 * @data: the buffer that the data will be read into
 * @data_size: the number of requested bytes
 *
 * This function has the similar semantics with recv().  The only
 * difference is that it accepts a GnuTLS session, and uses different
 * error codes.
 * In the special case that a server requests a renegotiation, the
 * client may receive an error code of %GNUTLS_E_REHANDSHAKE.  This
 * message may be simply ignored, replied with an alert
 * %GNUTLS_A_NO_RENEGOTIATION, or replied with a new handshake,
 * depending on the client's will.
 * If %EINTR is returned by the internal push function (the default
 * is recv()) then %GNUTLS_E_INTERRUPTED will be returned.  If
 * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must
 * call this function again to get the data.  See also
 * gnutls_record_get_direction().
 * A server may also receive %GNUTLS_E_REHANDSHAKE when a client has
 * initiated a handshake. In that case the server can only initiate a
 * handshake or terminate the connection.
 *
 * Returns: The number of bytes received and zero on EOF (for stream
 * connections).  A negative error code is returned in case of an error.  
 * The number of bytes received might be less than the requested @data_size.
 **/
ssize_t
gnutls_record_recv(gnutls_session_t session, void *data, size_t data_size)
{
	return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, NULL,
				data, data_size, NULL,
				session->internals.record_timeout_ms);
}
Ejemplo n.º 4
0
/**
 * gnutls_record_recv_seq:
 * @session: is a #gnutls_session_t structure.
 * @data: the buffer that the data will be read into
 * @data_size: the number of requested bytes
 * @seq: is the packet's 64-bit sequence number. Should have space for 8 bytes.
 *
 * This function is the same as gnutls_record_recv(), except that
 * it returns in addition to data, the sequence number of the data.
 * This is useful in DTLS where record packets might be received
 * out-of-order. The returned 8-byte sequence number is an
 * integer in big-endian format and should be
 * treated as a unique message identification. 
 *
 * Returns: The number of bytes received and zero on EOF.  A negative
 *   error code is returned in case of an error.  The number of bytes
 *   received might be less than @data_size.
 *
 * Since: 3.0
 **/
ssize_t
gnutls_record_recv_seq (gnutls_session_t session, void *data, size_t data_size,
  unsigned char *seq)
{
  return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data,
                           data_size, seq, session->internals.record_timeout_ms);
}
Ejemplo n.º 5
0
/**
 * gnutls_record_recv_packet:
 * @session: is a #gnutls_session_t structure.
 * @packet: the structure that will hold the packet data
 *
 * This is a lower-level function than gnutls_record_recv() and allows
 * to directly receive the whole decrypted packet. That avoids a
 * memory copy, and is mostly applicable to applications seeking high
 * performance.
 *
 * The received packet is accessed using gnutls_packet_get() and 
 * must be deinitialized using gnutls_packet_deinit(). The returned
 * packet will be %NULL if the return value is zero (EOF).
 *
 * Returns: The number of bytes received and zero on EOF (for stream
 * connections).  A negative error code is returned in case of an error.  
 *
 * Since: 3.3.5
 **/
ssize_t
gnutls_record_recv_packet(gnutls_session_t session, 
		   	  gnutls_packet_t *packet)
{
	return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA, -1, packet,
				NULL, 0, NULL,
				session->internals.record_timeout_ms);
}
Ejemplo n.º 6
0
/**
 * gnutls_record_recv:
 * @session: is a #gnutls_session_t structure.
 * @data: the buffer that the data will be read into
 * @data_size: the number of requested bytes
 *
 * This function has the similar semantics with recv().  The only
 * difference is that it accepts a GnuTLS session, and uses different
 * error codes.
 * In the special case that a server requests a renegotiation, the
 * client may receive an error code of %GNUTLS_E_REHANDSHAKE.  This
 * message may be simply ignored, replied with an alert
 * %GNUTLS_A_NO_RENEGOTIATION, or replied with a new handshake,
 * depending on the client's will.
 * If %EINTR is returned by the internal push function (the default
 * is recv()) then %GNUTLS_E_INTERRUPTED will be returned.  If
 * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must
 * call this function again to get the data.  See also
 * gnutls_record_get_direction().
 * A server may also receive %GNUTLS_E_REHANDSHAKE when a client has
 * initiated a handshake. In that case the server can only initiate a
 * handshake or terminate the connection.
 *
 * Returns: The number of bytes received and zero on EOF (for stream
 * connections).  A negative error code is returned in case of an error.  
 * The number of bytes received might be less than the requested @data_size.
 **/
ssize_t
gnutls_record_recv (gnutls_session_t session, void *data, size_t data_size) {
    return _gnutls_recv_int (session, GNUTLS_APPLICATION_DATA, -1, data, data_size, NULL);
}
Ejemplo n.º 7
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,
                               content_type_t type,
                               gnutls_handshake_description_t htype,
                               void *iptr, size_t sizeOfPtr)
{
    size_t left;
    ssize_t i;
    opaque *ptr;
    size_t dsize;

    ptr = iptr;
    left = sizeOfPtr;

    if (sizeOfPtr == 0 || iptr == NULL)
    {
        gnutls_assert ();
        return GNUTLS_E_INVALID_REQUEST;
    }

    if (session->internals.handshake_recv_buffer.length > 0)
    {
        /* if we have already received some data */
        if (sizeOfPtr <= session->internals.handshake_recv_buffer.length)
        {
            /* if requested less data then return it.
             */
            gnutls_assert ();
            memcpy (iptr, session->internals.handshake_recv_buffer.data,
                    sizeOfPtr);

            session->internals.handshake_recv_buffer.length -= sizeOfPtr;

            memmove (session->internals.handshake_recv_buffer.data,
                     &session->internals.handshake_recv_buffer.
                     data[sizeOfPtr],
                     session->internals.handshake_recv_buffer.length);

            return sizeOfPtr;
        }
        gnutls_assert ();
        memcpy (iptr, session->internals.handshake_recv_buffer.data,
                session->internals.handshake_recv_buffer.length);

        htype = session->internals.handshake_recv_buffer_htype;
        type = session->internals.handshake_recv_buffer_type;

        left -= session->internals.handshake_recv_buffer.length;

        session->internals.handshake_recv_buffer.length = 0;
    }

    while (left > 0)
    {
        dsize = sizeOfPtr - left;
        i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left);
        if (i < 0)
        {

            if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN))
            {
                gnutls_assert ();

                session->internals.handshake_recv_buffer.data =
                    gnutls_realloc_fast (session->internals.
                                         handshake_recv_buffer.data, dsize);
                if (session->internals.handshake_recv_buffer.data == NULL)
                {
                    gnutls_assert ();
                    return GNUTLS_E_MEMORY_ERROR;
                }

                memcpy (session->internals.handshake_recv_buffer.data, iptr,
                        dsize);

                session->internals.handshake_recv_buffer_htype = htype;
                session->internals.handshake_recv_buffer_type = type;

                session->internals.handshake_recv_buffer.length = dsize;
            }
            else
                session->internals.handshake_recv_buffer.length = 0;

            gnutls_assert ();

            return i;
        }
        else
        {
            if (i == 0)
                break;		/* EOF */
        }

        left -= i;

    }

    session->internals.handshake_recv_buffer.length = 0;

    return sizeOfPtr - left;
}
Ejemplo n.º 8
0
/**
 * gnutls_heartbeat_ping:
 * @session: is a #gnutls_session_t structure.
 * @data_size: is the length of the ping payload.
 * @max_tries: if flags is %GNUTLS_HEARTBEAT_WAIT then this sets the number of retransmissions. Use zero for indefinite (until timeout).
 * @flags: if %GNUTLS_HEARTBEAT_WAIT then wait for pong or timeout instead of returning immediately.
 *
 * This function sends a ping to the peer. If the @flags is set
 * to %GNUTLS_HEARTBEAT_WAIT then it waits for a reply from the peer.
 * 
 * Note that it is highly recommended to use this function with the
 * flag %GNUTLS_HEARTBEAT_WAIT, or you need to handle retransmissions
 * and timeouts manually.
 *
 * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
 *
 * Since: 3.1.2
 **/
int
gnutls_heartbeat_ping(gnutls_session_t session, size_t data_size,
		      unsigned int max_tries, unsigned int flags)
{
	int ret;
	unsigned int retries = 1, diff;
	struct timespec now;

	if (data_size > MAX_HEARTBEAT_LENGTH)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	if (gnutls_heartbeat_allowed
	    (session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) == 0)
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

	/* resume previous call if interrupted */
	if (session->internals.record_send_buffer.byte_length > 0 &&
	    session->internals.record_send_buffer.head != NULL &&
	    session->internals.record_send_buffer.head->type ==
	    GNUTLS_HEARTBEAT)
		return _gnutls_io_write_flush(session);

	switch (session->internals.hb_state) {
	case SHB_SEND1:
		if (data_size > DEFAULT_PAYLOAD_SIZE)
			data_size -= DEFAULT_PAYLOAD_SIZE;
		else
			data_size = 0;

		_gnutls_buffer_reset(&session->internals.hb_local_data);

		ret =
		    _gnutls_buffer_resize(&session->internals.
					  hb_local_data, data_size);
		if (ret < 0)
			return gnutls_assert_val(ret);

		ret =
		    _gnutls_rnd(GNUTLS_RND_NONCE,
				session->internals.hb_local_data.data,
				data_size);
		if (ret < 0)
			return gnutls_assert_val(ret);

		gettime(&session->internals.hb_ping_start);
		session->internals.hb_local_data.length = data_size;
		session->internals.hb_state = SHB_SEND2;
	case SHB_SEND2:
		session->internals.hb_actual_retrans_timeout_ms =
		    session->internals.hb_retrans_timeout_ms;
	      retry:
		ret =
		    heartbeat_send_data(session,
					session->internals.hb_local_data.
					data,
					session->internals.hb_local_data.
					length, HEARTBEAT_REQUEST);
		if (ret < 0)
			return gnutls_assert_val(ret);

		gettime(&session->internals.hb_ping_sent);

		if (!(flags & GNUTLS_HEARTBEAT_WAIT)) {
			session->internals.hb_state = SHB_SEND1;
			break;
		}

		session->internals.hb_state = SHB_RECV;

	case SHB_RECV:
		ret =
		    _gnutls_recv_int(session, GNUTLS_HEARTBEAT, -1, NULL,
				     0, NULL,
				     session->internals.
				     hb_actual_retrans_timeout_ms);
		if (ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) {
			session->internals.hb_state = SHB_SEND1;
			break;
		} else if (ret == GNUTLS_E_TIMEDOUT) {
			retries++;
			if (max_tries > 0 && retries > max_tries) {
				session->internals.hb_state = SHB_SEND1;
				return gnutls_assert_val(ret);
			}

			gettime(&now);
			diff =
			    timespec_sub_ms(&now,
					    &session->internals.
					    hb_ping_start);
			if (diff > session->internals.hb_total_timeout_ms) {
				session->internals.hb_state = SHB_SEND1;
				return
				    gnutls_assert_val(GNUTLS_E_TIMEDOUT);
			}

			session->internals.hb_actual_retrans_timeout_ms *=
			    2;
			session->internals.hb_actual_retrans_timeout_ms %=
			    MAX_DTLS_TIMEOUT;

			session->internals.hb_state = SHB_SEND2;
			goto retry;
		} else if (ret < 0) {
			session->internals.hb_state = SHB_SEND1;
			return gnutls_assert_val(ret);
		}
	}

	return 0;
}
Ejemplo n.º 9
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,
			       content_type_t type,
			       gnutls_handshake_description_t htype,
			       void *iptr, size_t sizeOfPtr)
{
  size_t left;
  ssize_t i;
  opaque *ptr;
  size_t dsize;

  ptr = iptr;
  left = sizeOfPtr;

  if (sizeOfPtr == 0 || iptr == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  if (session->internals.handshake_recv_buffer.length > 0)
    {
      size_t tmp;
      
      /* if we have already received some data */
      if (sizeOfPtr <= session->internals.handshake_recv_buffer.length)
	{
	  /* if requested less data then return it.
	   */
	  gnutls_assert ();
	  
	  tmp = sizeOfPtr;
	  _gnutls_string_get_data( &session->internals.handshake_recv_buffer, iptr, &tmp);
	  return tmp;
	}
      gnutls_assert ();

      tmp = sizeOfPtr; 
      _gnutls_string_get_data( &session->internals.handshake_recv_buffer, iptr, &tmp);
      left -= tmp;

      htype = session->internals.handshake_recv_buffer_htype;
      type = session->internals.handshake_recv_buffer_type;
    }

  while (left > 0)
    {
      dsize = sizeOfPtr - left;
      i = _gnutls_recv_int (session, type, htype, &ptr[dsize], left);
      if (i < 0)
	{

	  if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN))
	    {
	      gnutls_assert ();

	      _gnutls_buffer_append (&session->internals.handshake_recv_buffer, iptr,
		      dsize);

	      session->internals.handshake_recv_buffer_htype = htype;
	      session->internals.handshake_recv_buffer_type = type;
	    }

	  gnutls_assert ();

	  return i;
	}
      else
	{
	  if (i == 0)
	    break;		/* EOF */
	}

      left -= i;

    }

  session->internals.handshake_recv_buffer.length = 0;

  return sizeOfPtr - left;
}