Esempio n. 1
0
/*
 *	The S flag is set only within the EAP-TLS start message sent
 *	from the EAP server to the peer.
 *
 *	Similarly, when the EAP server receives an EAP-Response with
 *	the M bit set, it MUST respond with an EAP-Request with
 *	EAP-Type=EAP-TLS and no data. This serves as a fragment
 *	ACK. The EAP peer MUST wait.
 */
static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
{
	EAP_DS			*eap_ds = handler->eap_ds;
	tls_session_t		*tls_session = handler->opaque;
	EAP_DS			*prev_eap_ds = handler->prev_eap_ds;
	eaptls_packet_t		*eaptls_packet;
	REQUEST			*request = handler->request;
	size_t			frag_len;

	/*
	 *	We don't check ANY of the input parameters.  It's all
	 *	code which works together, so if something is wrong,
	 *	we SHOULD core dump.
	 *
	 *	e.g. if eap_ds is NULL, of if eap_ds->response is
	 *	NULL, of if it's NOT an EAP-Response, or if the packet
	 *	is too short.  See eap_validation()., in ../../eap.c
	 */
	eaptls_packet = (eaptls_packet_t *)eap_ds->response->type.data;

	/*
	 *	First output the flags (for debugging)
	 */
	RDEBUG3("Peer sent flags %c%c%c",
		TLS_START(eaptls_packet->flags) ? 'S' : '-',
		TLS_MORE_FRAGMENTS(eaptls_packet->flags) ? 'M' : '-',
		TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 'L' : '-');

	/*
	 *	check for ACK
	 *
	 *	If there's no TLS data, or there's 1 byte of TLS data,
	 *	with the flags set to zero, then it's an ACK.
	 *
	 *	Find if this is a reply to the previous request sent
	 */
	if ((!eaptls_packet) ||
	    ((eap_ds->response->length == EAP_HEADER_LEN + 2) &&
	     ((eaptls_packet->flags & 0xc0) == 0x00))) {
		if (prev_eap_ds && (prev_eap_ds->request->id == eap_ds->response->id)) {
			return tls_ack_handler(handler->opaque, request);
		} else {
			REDEBUG("Received Invalid TLS ACK");
			return FR_TLS_INVALID;
		}
	}

	/*
	 *	We send TLS_START, but do not receive it.
	 */
	if (TLS_START(eaptls_packet->flags)) {
		REDEBUG("Peer sent EAP-TLS Start message (only the server is allowed to do this)");
		return FR_TLS_INVALID;
	}

	/*
	 *	Calculate this fragment's length
	 */
	frag_len = eap_ds->response->length -
		   (EAP_HEADER_LEN + (TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 6 : 2));

	/*
	 *	The L bit (length included) is set to indicate the
	 *	presence of the four octet TLS Message Length field,
	 *	and MUST be set for the first fragment of a fragmented
	 *	TLS message or set of messages.
	 *
	 *	The M bit (more fragments) is set on all but the last
	 *	fragment.
	 *
	 *	The S bit (EAP-TLS start) is set in an EAP-TLS Start
	 *	message. This differentiates the EAP-TLS Start message
	 *	from a fragment acknowledgement.
	 */
	if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) {
		size_t total_len = eaptls_packet->data[2] * 256 | eaptls_packet->data[3];

		if (frag_len > total_len) {
			REDEBUG("TLS fragment length (%zu bytes) greater than TLS record length (%zu bytes)", frag_len,
				total_len);
			return FR_TLS_INVALID;
		}

		if (tls_session->tls_record_transfer_started) {
			REDEBUG("TLS Length Included (L) flag set, which indicates a new fragment transfer, "
				"but previous transfer was not complete");
			return FR_TLS_INVALID;
		}

		/*
		 *	This is the first fragment of a fragmented TLS record transfer.
		 */
		RDEBUG2("Peer indicated complete TLS record size will be %zu bytes", total_len);
		if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
			/*
			 *	The supplicant is free to send fragments of wildly varying
			 *	lengths, but the vast majority won't.
			 *
			 *	In this calculation we take into account the fact that the future
			 *	fragments are likely to be 4 bytes larger than the initial one
			 *	as they won't contain the length field.
			 */
			if (frag_len + 4) {	/* check for wrap, else clang scan gets excited */
				RDEBUG2("Expecting %i TLS record fragments",
					(int)((((total_len - frag_len) + ((frag_len + 4) - 1)) / (frag_len + 4)) + 1));
			}

			/*
			 *	First fragment. tls_record_transfer_started bool was false,
			 *	and we received a length included + more fragments packet.
			 */
			RDEBUG2("Got first TLS record fragment (%zu bytes).  Peer indicated more fragments "
				"to follow", frag_len);
			tls_session->tls_record_in_total_len = total_len;
			tls_session->tls_record_in_recvd_len = frag_len;
			tls_session->tls_record_transfer_started = true;

			return FR_TLS_FIRST_FRAGMENT;
		}

		/*
		 *	Else this is the complete TLS record.
		 */
		if (total_len != frag_len) {
			REDEBUG("Peer indicated no more fragments, but TLS record length (%zu bytes) "
				"does not match EAP-TLS data length (%zu bytes)", total_len, frag_len);
			return FR_TLS_INVALID;
		}

		/*
		 *	RFC5216 doesn't specify explicitly whether a non-fragmented
		 *	packet should include the length or not.
		 *
		 *	We support both options for maximum compatibility.
		 */
		RDEBUG2("Got complete TLS record, with length (%zu bytes)", frag_len);
		return FR_TLS_LENGTH_INCLUDED;
	}

	if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
		/*
		 *	If this is not an ongoing transfer, and we have the M flag
		 *	then this record transfer is invalid.
		 */
		if (!tls_session->tls_record_transfer_started) {
			REDEBUG("TLS More (M) flag set, but no fragmented record transfer was in progress");
			return FR_TLS_INVALID;
		}

		/*
		 *	If this is an ongoing transfer, and we have the M flag,
		 *	then this is just an additional fragment.
		 */
		RDEBUG2("Got additional TLS record fragment (%zu bytes).  Peer indicated more fragments to follow",
			frag_len);
		tls_session->tls_record_in_recvd_len += frag_len;
		if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) {
			REDEBUG("Total received TLS record fragments (%zu bytes), exceeds "
				"indicated TLS record length (%zu bytes)",
				tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len);
			return FR_TLS_INVALID;
		}
		return FR_TLS_MORE_FRAGMENTS;
	}

	/*
	 *	No L flag and no M flag. This is either the final fragment,
	 *	or a new transfer that was not started with a L flag, which
	 *	RFC5216 hints, may be acceptable.
	 *
	 *	If it's an in-progress record transfer, check we now have
	 *	the complete record.
	 */
	if (tls_session->tls_record_transfer_started) {
		tls_session->tls_record_transfer_started = false;

		RDEBUG2("Got final TLS record fragment (%zu bytes)", frag_len);
		tls_session->tls_record_in_recvd_len += frag_len;
		if (tls_session->tls_record_in_recvd_len != tls_session->tls_record_in_total_len) {
			REDEBUG("Total received TLS record fragments (%zu bytes), does not equal indicated "
				"TLS record length (%zu bytes)",
				tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len);
			return FR_TLS_INVALID;
		}
		return FR_TLS_OK;
	}

	/*
	 *	None of the flags are set, it wasn't an in progress transfer,
	 *	but it's still a valid EAP-TLS packet.
	 */
	RDEBUG2("Got complete TLS record (%zu bytes)", frag_len);

	return FR_TLS_OK;
}
Esempio n. 2
0
/*
 *	The S flag is set only within the EAP-TLS start message sent
 *	from the EAP server to the peer.
 *
 *	Similarly, when the EAP server receives an EAP-Response with
 *	the M bit set, it MUST respond with an EAP-Request with
 *	EAP-Type=EAP-TLS and no data. This serves as a fragment
 *	ACK. The EAP peer MUST wait.
 */
static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
{
	EAP_DS *eap_ds = handler->eap_ds;
	EAP_DS *prev_eap_ds = handler->prev_eapds;
	eaptls_packet_t	*eaptls_packet, *eaptls_prev = NULL;
	REQUEST *request = handler->request;

	/*
	 *	We don't check ANY of the input parameters.  It's all
	 *	code which works together, so if something is wrong,
	 *	we SHOULD core dump.
	 *
	 *	e.g. if eap_ds is NULL, of if eap_ds->response is
	 *	NULL, of if it's NOT an EAP-Response, or if the packet
	 *	is too short.  See eap_validation()., in ../../eap.c
	 *
	 *	Also, eap_method_select() takes care of selecting the
	 *	appropriate type, so we don't need to check
	 *	eap_ds->response->type.num == PW_EAP_TLS, or anything
	 *	else.
	 */
	eaptls_packet = (eaptls_packet_t *)eap_ds->response->type.data;
	if (prev_eap_ds && prev_eap_ds->response)
		eaptls_prev = (eaptls_packet_t *)prev_eap_ds->response->type.data;

	/*
	 *	check for ACK
	 *
	 *	If there's no TLS data, or there's 1 byte of TLS data,
	 *	with the flags set to zero, then it's an ACK.
	 *
	 *	Find if this is a reply to the previous request sent
	 */
	if ((!eaptls_packet) ||
	    ((eap_ds->response->length == EAP_HEADER_LEN + 2) &&
	     ((eaptls_packet->flags & 0xc0) == 0x00))) {

		if (prev_eap_ds &&
		    (prev_eap_ds->request->id == eap_ds->response->id)) {
			/*
			 *	Run the ACK handler directly from here.
			 */
			RDEBUG2("Received TLS ACK");
			return tls_ack_handler(handler->opaque, request);
		} else {
			RERROR("Received Invalid TLS ACK");
			return FR_TLS_INVALID;
		}
	}

	/*
	 *	We send TLS_START, but do not receive it.
	 */
	if (TLS_START(eaptls_packet->flags)) {
		RDEBUG("Received unexpected EAP-TLS Start message");
		return FR_TLS_INVALID;
	}

	/*
	 *	The L bit (length included) is set to indicate the
	 *	presence of the four octet TLS Message Length field,
	 *	and MUST be set for the first fragment of a fragmented
	 *	TLS message or set of messages.
	 *
	 *	The M bit (more fragments) is set on all but the last
	 *	fragment.
	 *
	 *	The S bit (EAP-TLS start) is set in an EAP-TLS Start
	 *	message. This differentiates the EAP-TLS Start message
	 *	from a fragment acknowledgement.
	 */
	if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) {
		DEBUG2("  TLS Length %d",
		       eaptls_packet->data[2] * 256 | eaptls_packet->data[3]);
		if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
			/*
			 * FIRST_FRAGMENT is identified
			 * 1. If there is no previous EAP-response received.
			 * 2. If EAP-response received, then its M bit not set.
			 * 	(It is because Last fragment will not have M bit set)
			 */
			if (!prev_eap_ds ||
			    (!prev_eap_ds->response) ||
			    (!eaptls_prev) ||
			    !TLS_MORE_FRAGMENTS(eaptls_prev->flags)) {

				RDEBUG2("Received EAP-TLS First Fragment of the message");
				return FR_TLS_FIRST_FRAGMENT;
			} else {

				RDEBUG2("More Fragments with length included");
				return FR_TLS_MORE_FRAGMENTS_WITH_LENGTH;
			}
		} else {
			RDEBUG2("Length Included");
			return FR_TLS_LENGTH_INCLUDED;
		}
	}

	if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
		RDEBUG2("More fragments to follow");
		return FR_TLS_MORE_FRAGMENTS;
	}

	/*
	 *	None of the flags are set, but it's still a valid
	 *	EAPTLS packet.
	 */
	return FR_TLS_OK;
}