Exemple #1
0
int _gnutls13_send_key_update(gnutls_session_t session, unsigned again, unsigned flags /* GNUTLS_KU_* */)
{
	int ret;
	mbuffer_st *bufel = NULL;
	uint8_t val;

	if (again == 0) {
		if (flags & GNUTLS_KU_PEER) {
			/* mark that we asked a key update to prevent an
			 * infinite ping pong when receiving the reply */
			session->internals.hsk_flags |= HSK_KEY_UPDATE_ASKED;
			val = 0x01;
		} else {
			val = 0x00;
		}

		_gnutls_handshake_log("HSK[%p]: sending key update (%u)\n", session, (unsigned)val);

		bufel = _gnutls_handshake_alloc(session, 1);
		if (bufel == NULL)
			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

		_mbuffer_set_udata_size(bufel, 0);
		ret = _mbuffer_append_data(bufel, &val, 1);
		if (ret < 0) {
			gnutls_assert();
			goto cleanup;
		}

	}

	return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_KEY_UPDATE);

cleanup:
	_mbuffer_xfree(&bufel);
	return ret;
}
Exemple #2
0
static ssize_t
_gnutls_stream_read(gnutls_session_t session, mbuffer_st ** bufel,
		    size_t size, gnutls_pull_func pull_func,
		    unsigned int *ms)
{
	size_t left;
	ssize_t i = 0;
	size_t max_size = max_record_recv_size(session);
	uint8_t *ptr;
	gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
	int ret;
	struct timespec t1, t2;
	unsigned int diff;

	session->internals.direction = 0;

	*bufel = _mbuffer_alloc_align16(MAX(max_size, size), get_total_headers(session));
	if (!*bufel) {
		gnutls_assert();
		return GNUTLS_E_MEMORY_ERROR;
	}
	ptr = (*bufel)->msg.data;

	left = size;
	while (left > 0) {
		if (ms && *ms > 0) {
			ret = _gnutls_io_check_recv(session, *ms);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			gnutls_gettime(&t1);
		}

		reset_errno(session);

		i = pull_func(fd, &ptr[size - left], left);

		if (i < 0) {
			int err = get_errno(session);

			_gnutls_read_log
			    ("READ: %d returned from %p, errno=%d gerrno=%d\n",
			     (int) i, fd, errno,
			     session->internals.errnum);

			if (err == EAGAIN || err == EINTR) {
				if (size - left > 0) {

					_gnutls_read_log
					    ("READ: returning %d bytes from %p\n",
					     (int) (size - left), fd);

					goto finish;
				}

				ret = errno_to_gerr(err, 0);
				goto cleanup;
			} else {
				gnutls_assert();
				ret = GNUTLS_E_PULL_ERROR;
				goto cleanup;
			}
		} else {

			_gnutls_read_log("READ: Got %d bytes from %p\n",
					 (int) i, fd);

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

		left -= i;
		(*bufel)->msg.size += i;

		if (ms && *ms > 0 && *ms != GNUTLS_INDEFINITE_TIMEOUT) {
			gnutls_gettime(&t2);
			diff = timespec_sub_ms(&t2, &t1);
			if (diff < *ms)
				*ms -= diff;
			else {
				ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
				goto cleanup;
			}
		}
	}

      finish:

	_gnutls_read_log("READ: read %d bytes from %p\n",
			 (int) (size - left), fd);

	if (size - left == 0)
		_mbuffer_xfree(bufel);

	return (size - left);

      cleanup:
	_mbuffer_xfree(bufel);
	return ret;
}
Exemple #3
0
static ssize_t
_gnutls_dgram_read(gnutls_session_t session, mbuffer_st ** bufel,
		   gnutls_pull_func pull_func, unsigned int *ms)
{
	ssize_t i, ret;
	uint8_t *ptr;
	struct timespec t1, t2;
	size_t max_size, recv_size;
	gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;
	unsigned int diff;

	max_size = max_record_recv_size(session);
	recv_size = max_size;

	session->internals.direction = 0;

	if (ms && *ms > 0) {
		ret = _gnutls_io_check_recv(session, *ms);
		if (ret < 0)
			return gnutls_assert_val(ret);
		gnutls_gettime(&t1);
	}

	*bufel = _mbuffer_alloc_align16(max_size, get_total_headers(session));
	if (*bufel == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	ptr = (*bufel)->msg.data;

	reset_errno(session);
	i = pull_func(fd, ptr, recv_size);

	if (i < 0) {
		int err = get_errno(session);

		_gnutls_read_log("READ: %d returned from %p, errno=%d\n",
				 (int) i, fd, err);

		ret = errno_to_gerr(err, 1);
		goto cleanup;
	} else {
		_gnutls_read_log("READ: Got %d bytes from %p\n", (int) i,
				 fd);
		if (i == 0) {
			/* If we get here, we likely have a stream socket.
			 * That assumption may not work on DCCP. */
			gnutls_assert();
			ret = 0;
			goto cleanup;
		}

		_mbuffer_set_udata_size(*bufel, i);
	}

	if (ms && *ms > 0) {
		gnutls_gettime(&t2);
		diff = timespec_sub_ms(&t2, &t1);
		if (diff < *ms)
			*ms -= diff;
		else {
			ret = gnutls_assert_val(GNUTLS_E_TIMEDOUT);
			goto cleanup;
		}
	}

	_gnutls_read_log("READ: read %d bytes from %p\n", (int) i, fd);

	return i;

      cleanup:
	_mbuffer_xfree(bufel);
	return ret;
}
Exemple #4
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;
	}
}
Exemple #5
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;

}
Exemple #6
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 #7
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);
    }
}
Exemple #8
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.
 *
 */
ssize_t
_gnutls_io_read_buffered (gnutls_session_t session, size_t total,
                          content_type_t recv_type)
{
  ssize_t ret = 0;
  size_t min;
  mbuffer_st *bufel = NULL;
  size_t recvdata, readsize;

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

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

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

  /* 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.byte_length + recvdata) >
      MAX_RECV_SIZE(session))
    {
      gnutls_assert ();         /* internal error */
      return GNUTLS_E_INVALID_REQUEST;
    }

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

  /* READ DATA - but leave RCVLOWAT bytes in the kernel buffer.
   */
  if (readsize > 0)
    {
      ret =
        _gnutls_read (session, &bufel, readsize,
                      session->internals.pull_func);

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

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

      _mbuffer_enqueue (&session->internals.record_recv_buffer, bufel);
    }
  else
    _mbuffer_xfree (&bufel);

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

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

  if(IS_DTLS(session))
    ret = MIN(total, session->internals.record_recv_buffer.byte_length);
  else
    ret = session->internals.record_recv_buffer.byte_length;

  if ((ret > 0) && ((size_t) ret < total))
    {
      /* Short Read */
      return gnutls_assert_val(GNUTLS_E_AGAIN);
    }
  else
    {
      return ret;
    }
}
Exemple #9
0
static ssize_t
_gnutls_dgram_read (gnutls_session_t session, mbuffer_st **bufel,
		    gnutls_pull_func pull_func)
{
  ssize_t i, ret;
  char *ptr;
  size_t max_size = _gnutls_get_max_decrypted_data(session);
  size_t recv_size = MAX_RECV_SIZE(session);
  gnutls_transport_ptr_t fd = session->internals.transport_recv_ptr;

  if (recv_size > max_size)
    recv_size = max_size;

  *bufel = _mbuffer_alloc (0, max_size);
  if (*bufel == NULL)
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

  ptr = (*bufel)->msg.data;

  session->internals.direction = 0;

  reset_errno (session);
  i = pull_func (fd, ptr, recv_size);

  if (i < 0)
    {
      int err = get_errno (session);

      _gnutls_read_log ("READ: %d returned from %p, errno=%d gerrno=%d\n",
			(int) i, fd, errno, session->internals.errnum);

      if (err == EAGAIN)
        {
          ret = GNUTLS_E_AGAIN;
          goto cleanup;
        }
      else if (err == EINTR)
        {
          ret = GNUTLS_E_INTERRUPTED;
          goto cleanup;
        }
      else
        {
          gnutls_assert ();
          ret = GNUTLS_E_PULL_ERROR;
          goto cleanup;
        }
    }
  else
    {
      _gnutls_read_log ("READ: Got %d bytes from %p\n", (int) i, fd);
      if (i == 0) 
        {
          /* If we get here, we likely have a stream socket.
           * FIXME: this probably breaks DCCP. */
          gnutls_assert ();
          ret = 0;
          goto cleanup;
        }

      _mbuffer_set_udata_size (*bufel, i);
    }

  _gnutls_read_log ("READ: read %d bytes from %p\n", (int) i, fd);
  
  return i;
  
cleanup:
  _mbuffer_xfree(bufel);
  return ret;
}
Exemple #10
0
int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned nr, unsigned again)
{
	int ret = 0;
	mbuffer_st *bufel = NULL;
	gnutls_buffer_st buf;
	tls13_ticket_st ticket;
	unsigned i;

	/* Client does not send a NewSessionTicket */
	if (unlikely(session->security_parameters.entity == GNUTLS_CLIENT))
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	/* Session resumption has not been enabled */
	if (session->internals.flags & GNUTLS_NO_TICKETS)
		return gnutls_assert_val(0);

	/* If we received the psk_key_exchange_modes extension which
	 * does not have overlap with the server configuration, don't
	 * send a session ticket */
	if (session->internals.hsk_flags & HSK_PSK_KE_MODE_INVALID)
		return gnutls_assert_val(0);

	if (again == 0) {
		for (i=0;i<nr;i++) {
			unsigned init_pos;

			memset(&ticket, 0, sizeof(tls13_ticket_st));
			bufel = NULL;

			ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
			if (ret < 0)
				return gnutls_assert_val(ret);

			ret = generate_session_ticket(session, &ticket);
			if (ret < 0) {
				if (ret == GNUTLS_E_INT_RET_0) {
					ret = gnutls_assert_val(0);
					goto cleanup;
				}
				gnutls_assert();
				goto cleanup;
			}

			ret = _gnutls_buffer_append_prefix(&buf, 32, ticket.lifetime);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			ret = _gnutls_buffer_append_prefix(&buf, 32, ticket.age_add);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			/* append ticket_nonce */
			ret = _gnutls_buffer_append_data_prefix(&buf, 8, ticket.nonce, ticket.nonce_size);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			/* append ticket */
			ret = _gnutls_buffer_append_data_prefix(&buf, 16, ticket.ticket.data, ticket.ticket.size);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			_gnutls_free_datum(&ticket.ticket);

			/* append extensions */
			ret = _gnutls_extv_append_init(&buf);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}
			init_pos = ret;

			ret = _gnutls_extv_append(&buf, ext_mod_early_data.tls_id, session,
						  (extv_append_func)append_nst_extension);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			ret = _gnutls_extv_append_final(&buf, init_pos, 0);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			bufel = _gnutls_buffer_to_mbuffer(&buf);

			ret = _gnutls_send_handshake2(session, bufel,
						      GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, 1);
			if (ret < 0) {
				gnutls_assert();
				goto cleanup;
			}

			session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT;
		}
	}

	ret = _gnutls_handshake_io_write_flush(session);

	return ret;

cleanup:
	_gnutls_free_datum(&ticket.ticket);
	_mbuffer_xfree(&bufel);
	_gnutls_buffer_clear(&buf);

	return ret;
}