Пример #1
0
static int
_gnutls_buffer_insert_data (gnutls_buffer_st * dest, int pos, const void *str,
                            size_t str_size)
{
  size_t orig_length = dest->length;
  int ret;

  ret = _gnutls_buffer_resize (dest, dest->length + str_size);  /* resize to make space */
  if (ret < 0)
    return ret;

  memmove (&dest->data[pos + str_size], &dest->data[pos], orig_length - pos);

  memcpy (&dest->data[pos], str, str_size);
  dest->length += str_size;

  return 0;
}
Пример #2
0
/* 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).
 * Only used in DTLS.
 */
static int merge_handshake_packet(gnutls_session_t session,
				  handshake_buffer_st * hsk)
{
	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 (!exists)
		pos = session->internals.handshake_recv_buffer_size;

	if (pos >= MAX_HANDSHAKE_MSGS)
		return
		    gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);

	if (!exists) {
		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++;

		/* rewrite headers to make them look as each packet came as a single fragment */
		_gnutls_write_uint24(hsk->length, &hsk->header[1]);
		_gnutls_write_uint24(0, &hsk->header[6]);
		_gnutls_write_uint24(hsk->length, &hsk->header[9]);

		_gnutls_handshake_buffer_move(&session->internals.
					      handshake_recv_buffer[pos],
					      hsk);

	} else {
		if (hsk->start_offset <
		    session->internals.handshake_recv_buffer[pos].
		    start_offset
		    && hsk->end_offset + 1 >=
		    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;
}
Пример #3
0
/**
  * gnutls_init - initialize the session to null (null encryption etc...).
  * @con_end: indicate if this session is to be used for server or client.
  * @session: is a pointer to a #gnutls_session_t structure.
  *
  * This function initializes the current session to null. Every
  * session must be initialized before use, so internal structures can
  * be allocated.  This function allocates structures which can only
  * be free'd by calling gnutls_deinit().  Returns zero on success.
  *
  * @con_end can be one of %GNUTLS_CLIENT and %GNUTLS_SERVER.
  *
  * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
  **/
int
gnutls_init (gnutls_session_t * session, gnutls_connection_end_t con_end)
{
  *session = gnutls_calloc (1, sizeof (struct gnutls_session_int));
  if (*session == NULL)
    return GNUTLS_E_MEMORY_ERROR;

  (*session)->security_parameters.entity = con_end;

  /* the default certificate type for TLS */
  (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE;

/* Set the defaults for initial handshake */
  (*session)->security_parameters.read_bulk_cipher_algorithm =
    (*session)->security_parameters.write_bulk_cipher_algorithm =
    GNUTLS_CIPHER_NULL;

  (*session)->security_parameters.read_mac_algorithm =
    (*session)->security_parameters.write_mac_algorithm = GNUTLS_MAC_NULL;

  (*session)->security_parameters.read_compression_algorithm =
    GNUTLS_COMP_NULL;
  (*session)->security_parameters.write_compression_algorithm =
    GNUTLS_COMP_NULL;

  (*session)->internals.enable_private = 0;

  /* Initialize buffers */
  _gnutls_buffer_init (&(*session)->internals.application_data_buffer);
  _gnutls_buffer_init (&(*session)->internals.handshake_data_buffer);
  _gnutls_buffer_init (&(*session)->internals.handshake_hash_buffer);
  _gnutls_buffer_init (&(*session)->internals.ia_data_buffer);

  _gnutls_buffer_init (&(*session)->internals.record_send_buffer);
  _gnutls_buffer_init (&(*session)->internals.record_recv_buffer);

  _gnutls_buffer_init (&(*session)->internals.handshake_send_buffer);
  _gnutls_buffer_init (&(*session)->internals.handshake_recv_buffer);

  (*session)->key = gnutls_calloc (1, sizeof (struct gnutls_key_st));
  if ((*session)->key == NULL)
    {
    cleanup_session:
      gnutls_free (*session);
      *session = NULL;
      return GNUTLS_E_MEMORY_ERROR;
    }

  (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME;	/* one hour default */

  gnutls_dh_set_prime_bits ((*session), MIN_DH_BITS);

  gnutls_transport_set_lowat ((*session), DEFAULT_LOWAT);	/* the default for tcp */

  gnutls_handshake_set_max_packet_length ((*session),
					  MAX_HANDSHAKE_PACKET_SIZE);

  /* Allocate a minimum size for recv_data
   * This is allocated in order to avoid small messages, making
   * the receive procedure slow.
   */
  if (_gnutls_buffer_resize (&(*session)->internals.record_recv_buffer,
			     INITIAL_RECV_BUFFER_SIZE))
    {
      gnutls_free ((*session)->key);
      goto cleanup_session;
    }

  /* set the socket pointers to -1;
   */
  (*session)->internals.transport_recv_ptr = (gnutls_transport_ptr_t) - 1;
  (*session)->internals.transport_send_ptr = (gnutls_transport_ptr_t) - 1;

  /* set the default maximum record size for TLS
   */
  (*session)->security_parameters.max_record_recv_size =
    DEFAULT_MAX_RECORD_SIZE;
  (*session)->security_parameters.max_record_send_size =
    DEFAULT_MAX_RECORD_SIZE;

  /* everything else not initialized here is initialized
   * as NULL or 0. This is why calloc is used.
   */

  _gnutls_handshake_internal_state_clear (*session);

  return 0;
}
Пример #4
0
/*
 * Processes a heartbeat message. 
 */
int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel)
{
	int ret;
	unsigned type;
	unsigned pos;
	uint8_t *msg = _mbuffer_get_udata_ptr(bufel);
	size_t hb_len, len = _mbuffer_get_udata_size(bufel);

	if (gnutls_heartbeat_allowed
	    (session, GNUTLS_HB_PEER_ALLOWED_TO_SEND) == 0)
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

	if (len < 4)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	pos = 0;
	type = msg[pos++];

	hb_len = _gnutls_read_uint16(&msg[pos]);
	if (hb_len > len - 3)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	pos += 2;

	switch (type) {
	case HEARTBEAT_REQUEST:
		_gnutls_buffer_reset(&session->internals.hb_remote_data);

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

		if (hb_len > 0)
			memcpy(session->internals.hb_remote_data.data,
			       &msg[pos], hb_len);
		session->internals.hb_remote_data.length = hb_len;

		return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED);

	case HEARTBEAT_RESPONSE:

		if (hb_len != session->internals.hb_local_data.length)
			return
			    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

		if (hb_len > 0 &&
		    memcmp(&msg[pos],
			   session->internals.hb_local_data.data,
			   hb_len) != 0) {
			if (IS_DTLS(session))
				return gnutls_assert_val(GNUTLS_E_AGAIN);	/* ignore it */
			else
				return
				    gnutls_assert_val
				    (GNUTLS_E_UNEXPECTED_PACKET);
		}

		_gnutls_buffer_reset(&session->internals.hb_local_data);

		return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED);
	default:
		_gnutls_record_log
		    ("REC[%p]: HB: received unknown type %u\n", session,
		     type);
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
	}
}
Пример #5
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;
}
Пример #6
0
/* This function is like recv(with MSG_PEEK). But it does not return -1 on error.
 * It does return gnutls_errno instead.
 * This function reads data from the socket and keeps them in a buffer, of up to
 * MAX_RECV_SIZE. 
 *
 * This is not a general purpose function. It returns EXACTLY the data requested,
 * which are stored in a local (in the session) buffer. A pointer (iptr) to this buffer is returned.
 *
 */
ssize_t
_gnutls_io_read_buffered (gnutls_session_t session, opaque ** iptr,
			  size_t sizeOfPtr, content_type_t recv_type)
{
  ssize_t ret = 0, ret2 = 0;
  size_t min;
  int buf_pos;
  opaque *buf;
  int recvlowat;
  int recvdata;

  *iptr = session->internals.record_recv_buffer.data;

  if (sizeOfPtr > MAX_RECV_SIZE || sizeOfPtr == 0)
    {
      gnutls_assert ();		/* internal error */
      return GNUTLS_E_INVALID_REQUEST;
    }

  /* If an external pull function is used, then do not leave
   * any data into the kernel buffer.
   */
  if (session->internals._gnutls_pull_func != NULL)
    {
      recvlowat = 0;
    }
  else
    {
      /* leave peeked data to the kernel space only if application data
       * is received and we don't have any peeked 
       * data in gnutls session.
       */
      if (recv_type != GNUTLS_APPLICATION_DATA
	  && session->internals.have_peeked_data == 0)
	recvlowat = 0;
      else
	recvlowat = RCVLOWAT;
    }



  /* calculate the actual size, ie. get the minimum of the
   * buffered data and the requested data.
   */
  min = MIN (session->internals.record_recv_buffer.length, sizeOfPtr);
  if (min > 0)
    {
      /* if we have enough buffered data
       * then just return them.
       */
      if (min == sizeOfPtr)
	{
	  return min;
	}
    }

  /* min is over zero. recvdata is the data we must
   * receive in order to return the requested data.
   */
  recvdata = sizeOfPtr - min;

  /* Check if the previously read data plus the new data to
   * receive are longer than the maximum receive buffer size.
   */
  if ((session->internals.record_recv_buffer.length + recvdata) >
      MAX_RECV_SIZE)
    {
      gnutls_assert ();		/* internal error */
      return GNUTLS_E_INVALID_REQUEST;
    }

  /* Allocate the data required to store the new packet.
   */
  ret = _gnutls_buffer_resize( &session->internals.record_recv_buffer, 
      recvdata + session->internals.record_recv_buffer.length); 

  if (ret < 0)
    {
      gnutls_assert();
      return ret;
    }

  buf_pos = session->internals.record_recv_buffer.length;
  buf = session->internals.record_recv_buffer.data;
  *iptr = buf;

  /* READ DATA - but leave RCVLOWAT bytes in the kernel buffer.
   */
  if (recvdata - recvlowat > 0)
    {
      ret = _gnutls_read (session, &buf[buf_pos], recvdata - recvlowat, 0);

      /* return immediately if we got an interrupt or eagain
       * error.
       */
      if (ret < 0 && gnutls_error_is_fatal (ret) == 0)
	{
	  return ret;
	}
    }

  /* copy fresh data to our buffer.
   */
  if (ret > 0)
    {
      _gnutls_read_log
	("RB: Have %d bytes into buffer. Adding %d bytes.\n",
	 session->internals.record_recv_buffer.length, ret);
      _gnutls_read_log ("RB: Requested %d bytes\n", sizeOfPtr);
      session->internals.record_recv_buffer.length += ret;
    }

  buf_pos = session->internals.record_recv_buffer.length;

  /* This is hack in order for select to work. Just leave recvlowat data,
   * into the kernel buffer (using a read with MSG_PEEK), thus making
   * select think, that the socket is ready for reading.
   * MSG_PEEK is only used with berkeley style sockets.
   */
  if (ret == (recvdata - recvlowat) && recvlowat > 0)
    {
      ret2 = _gnutls_read (session, &buf[buf_pos], recvlowat, MSG_PEEK);

      if (ret2 < 0 && gnutls_error_is_fatal (ret2) == 0)
	{
	  return ret2;
	}

      if (ret2 > 0)
	{
	  _gnutls_read_log ("RB-PEEK: Read %d bytes in PEEK MODE.\n", ret2);
	  _gnutls_read_log
	    ("RB-PEEK: Have %d bytes into buffer. Adding %d bytes.\nRB: Requested %d bytes\n",
	     session->internals.record_recv_buffer.length, ret2, sizeOfPtr);
	  session->internals.have_peeked_data = 1;
	  session->internals.record_recv_buffer.length += ret2;

	}
    }

  if (ret < 0 || ret2 < 0)
    {
      gnutls_assert ();
      /* that's because they are initialized to 0 */
      return MIN (ret, ret2);
    }

  ret += ret2;

  if (ret > 0 && ret < recvlowat)
    {
      gnutls_assert ();
      return GNUTLS_E_AGAIN;
    }

  if (ret == 0)
    {				/* EOF */
      gnutls_assert ();
      return 0;
    }

  ret = session->internals.record_recv_buffer.length;

  if ((ret > 0) && ((size_t) ret < sizeOfPtr))
    {
      /* Short Read */
      gnutls_assert ();
      return GNUTLS_E_AGAIN;
    }
  else
    {
      return ret;
    }
}