コード例 #1
0
ファイル: gnutls_record.c プロジェクト: Distrotech/gnutls
/* @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, record_params, 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_align16(&session->internals.record_recv_buffer, 
		get_total_headers2(session, record_params));
	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. Moreover we add space for any pad and the MAC (in case
	 * they are encrypted).
	 */
	ret = max_decrypted_size(session) + MAX_PAD_SIZE + MAX_HASH_SIZE;
	decrypted = _mbuffer_alloc_align16(ret, 0);
	if (decrypted == NULL)
		return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

	_mbuffer_set_udata_size(decrypted, ret);
	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_record_log
			    ("REC[%p]: Discarded duplicate message[%u.%u]: %s\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));
			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);

	/* decrypted 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) && (ret == GNUTLS_E_DECRYPTION_FAILED ||
		ret == GNUTLS_E_UNSUPPORTED_VERSION_PACKET ||
		ret == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
		ret == GNUTLS_E_UNEXPECTED_PACKET ||
		ret == GNUTLS_E_ERROR_IN_FINISHED_PACKET ||
		ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)) {
		goto discard;
	}

	session_invalidate(session);
	session_unresumable(session);

	if (ret == 0)
		return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
	else
		return ret;
}
コード例 #2
0
ファイル: buffers.c プロジェクト: gnutls/gnutls
/* 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;
	}
}
コード例 #3
0
ファイル: gnutls_record.c プロジェクト: Distrotech/gnutls
static int recv_headers(gnutls_session_t session, 
			record_parameters_st *record_params,
			content_type_t type,
			gnutls_handshake_description_t htype,
			struct tls_record_st *record, unsigned int *ms)
{
	int ret;
	gnutls_datum_t raw;	/* raw headers */
	/* Read the headers.
	 */
	record->header_size = record->packet_size =
	    RECORD_HEADER_SIZE(session);

	ret =
	    _gnutls_io_read_buffered(session, record->header_size, -1, ms);
	if (ret != record->header_size) {
		if (ret < 0 && gnutls_error_is_fatal(ret) == 0)
			return ret;

		if (ret > 0)
			ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
		else if (ret == 0)
			ret = GNUTLS_E_PREMATURE_TERMINATION;

		return gnutls_assert_val(ret);
	}

	ret = _mbuffer_linearize_align16(&session->internals.record_recv_buffer, 
		get_total_headers2(session, record_params));
	if (ret < 0)
		return gnutls_assert_val(ret);

	_mbuffer_head_get_first(&session->internals.record_recv_buffer,
				&raw);
	if (raw.size < RECORD_HEADER_SIZE(session))
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	record_read_headers(session, raw.data, type, htype, record);

	/* Check if the DTLS epoch is valid */
	if (IS_DTLS(session)) {
		if (_gnutls_epoch_is_valid(session, record->epoch) == 0) {
			_gnutls_audit_log(session,
					  "Discarded message[%u] with invalid epoch %u.\n",
					  (unsigned int)
					  _gnutls_uint64touint32(&record->
								 sequence),
					  (unsigned int) record->sequence.
					  i[0] * 256 +
					  (unsigned int) record->sequence.
					  i[1]);
			gnutls_assert();
			/* doesn't matter, just a fatal error */
			return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
		}
	}

	/* Here we check if the Type of the received packet is
	 * ok. 
	 */
	if ((ret = check_recv_type(session, record->type)) < 0)
		return gnutls_assert_val(ret);

	/* Here we check if the advertized version is the one we
	 * negotiated in the handshake.
	 */
	if ((ret =
	     record_check_version(session, htype, record->version)) < 0)
		return gnutls_assert_val(ret);

	if (record->length > max_record_recv_size(session)) {
		_gnutls_audit_log
		    (session, "Received packet with illegal length: %u\n",
		     (unsigned int) record->length);
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
	}

	_gnutls_record_log
	    ("REC[%p]: Expected Packet %s(%d)\n", session,
	     _gnutls_packet2str(type), type);
	_gnutls_record_log
	    ("REC[%p]: Received Packet %s(%d) with length: %d\n", session,
	     _gnutls_packet2str(record->type), record->type,
	     record->length);


	return 0;
}