Exemplo n.º 1
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;
	}
}
Exemplo n.º 2
0
static int
parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel,
		       handshake_buffer_st * hsk)
{
	uint8_t *dataptr = NULL;	/* for realloc */
	size_t handshake_header_size =
	    HANDSHAKE_HEADER_SIZE(session), data_size, frag_size;

	/* Note: SSL2_HEADERS == 1 */
	if (_mbuffer_get_udata_size(bufel) < handshake_header_size)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	dataptr = _mbuffer_get_udata_ptr(bufel);

	/* if reading a client hello of SSLv2 */
#ifdef ENABLE_SSL2
	if (unlikely
	    (!IS_DTLS(session)
	     && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) {
		handshake_header_size = SSL2_HEADERS;	/* we've already read one byte */

		frag_size = _mbuffer_get_udata_size(bufel) - handshake_header_size;	/* we've read the first byte */

		if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO)
			return
			    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

		hsk->rtype = hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;

		hsk->sequence = 0;
		hsk->start_offset = 0;
		hsk->length = frag_size;
	} else
#endif
	{	/* TLS or DTLS handshake headers */


		hsk->rtype = hsk->htype = dataptr[0];

		/* we do not use DECR_LEN because we know
		 * that the packet has enough data.
		 */
		hsk->length = _gnutls_read_uint24(&dataptr[1]);

		if (IS_DTLS(session)) {
			hsk->sequence = _gnutls_read_uint16(&dataptr[4]);
			hsk->start_offset =
			    _gnutls_read_uint24(&dataptr[6]);
			frag_size =
			    _gnutls_read_uint24(&dataptr[9]);
		} else {
			hsk->sequence = 0;
			hsk->start_offset = 0;
			frag_size =
			    MIN((_mbuffer_get_udata_size(bufel) -
				 handshake_header_size), hsk->length);
		}

		/* TLS1.3: distinguish server hello versus hello retry request.
		 * The epitome of slick protocol design. */
		if (hsk->htype == GNUTLS_HANDSHAKE_SERVER_HELLO && hsk->start_offset == 0 && !IS_DTLS(session)) {
			if (_mbuffer_get_udata_size(bufel) > handshake_header_size+2+GNUTLS_RANDOM_SIZE &&
			    memcmp(dataptr+handshake_header_size+2, HRR_RANDOM, GNUTLS_RANDOM_SIZE) == 0) {
				hsk->htype = GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST;
			}
		}
	}
	data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size;

	if (frag_size > 0)
		hsk->end_offset = hsk->start_offset + frag_size - 1;
	else
		hsk->end_offset = 0;

	_gnutls_handshake_log
	    ("HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n",
	     session, _gnutls_handshake2str(hsk->htype),
	     (unsigned) hsk->htype, (int) hsk->length, (int) data_size,
	     hsk->start_offset, (int) frag_size,
	     (int) hsk->sequence);

	hsk->header_size = handshake_header_size;
	memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel),
	       handshake_header_size);

	if (hsk->length > 0 && (frag_size > data_size ||
				(frag_size > 0 &&
				 hsk->end_offset >= hsk->length))) {
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
	}
	else if (hsk->length == 0 && hsk->end_offset != 0
		 && hsk->start_offset != 0)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	return handshake_header_size;
}
Exemplo n.º 3
0
static int
parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel,
		       handshake_buffer_st * hsk)
{
	uint8_t *dataptr = NULL;	/* for realloc */
	size_t handshake_header_size =
	    HANDSHAKE_HEADER_SIZE(session), data_size;

	/* Note: SSL2_HEADERS == 1 */
	if (_mbuffer_get_udata_size(bufel) < handshake_header_size)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	dataptr = _mbuffer_get_udata_ptr(bufel);

	/* if reading a client hello of SSLv2 */
#ifdef ENABLE_SSL2
	if (unlikely
	    (!IS_DTLS(session)
	     && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) {
		handshake_header_size = SSL2_HEADERS;	/* we've already read one byte */

		hsk->length = _mbuffer_get_udata_size(bufel) - handshake_header_size;	/* we've read the first byte */

		if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO)
			return
			    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

		hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;

		hsk->sequence = 0;
		hsk->start_offset = 0;
		hsk->end_offset = hsk->length;
	} else
#endif
	{		/* TLS or DTLS handshake headers */


		hsk->htype = dataptr[0];

		/* we do not use DECR_LEN because we know
		 * that the packet has enough data.
		 */
		hsk->length = _gnutls_read_uint24(&dataptr[1]);
		handshake_header_size = HANDSHAKE_HEADER_SIZE(session);

		if (IS_DTLS(session)) {
			hsk->sequence = _gnutls_read_uint16(&dataptr[4]);
			hsk->start_offset =
			    _gnutls_read_uint24(&dataptr[6]);
			hsk->end_offset =
			    hsk->start_offset +
			    _gnutls_read_uint24(&dataptr[9]);
		} else {
			hsk->sequence = 0;
			hsk->start_offset = 0;
			hsk->end_offset =
			    MIN((_mbuffer_get_udata_size(bufel) -
				 handshake_header_size), hsk->length);
		}
	}
	data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size;

	/* make the length offset */
	if (hsk->end_offset > 0)
		hsk->end_offset--;

	_gnutls_handshake_log
	    ("HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n",
	     session, _gnutls_handshake2str(hsk->htype),
	     (unsigned) hsk->htype, (int) hsk->length, (int) data_size,
	     hsk->start_offset, hsk->end_offset - hsk->start_offset + 1,
	     (int) hsk->sequence);

	hsk->header_size = handshake_header_size;
	memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel),
	       handshake_header_size);

	if (hsk->length > 0 &&
	    (hsk->end_offset - hsk->start_offset >= data_size))
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	if (hsk->length > 0 && (hsk->start_offset >= hsk->end_offset ||
				hsk->end_offset - hsk->start_offset >=
				data_size
				|| hsk->end_offset >= hsk->length))
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
	else if (hsk->length == 0 && hsk->end_offset != 0
		 && hsk->start_offset != 0)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	return handshake_header_size;
}