Esempio n. 1
0
/*
 * EAPTLS_PACKET
 * code   =  EAP-code
 * id     =  EAP-id
 * length = code + id + length + flags + tlsdata
 *        =  1   +  1 +   2    +  1    +  X
 * length = EAP-length - 1(EAP-Type = 1 octet)
 * flags  = EAP-typedata[0] (1 octet)
 * dlen   = EAP-typedata[1-4] (4 octets), if L flag set
 *        = length - 5(code+id+length+flags), otherwise
 * data   = EAP-typedata[5-n], if L flag set
 *        = EAP-typedata[1-n], otherwise
 * packet = EAP-typedata (complete typedata)
 *
 * Points to consider during EAP-TLS data extraction
 * 1. In the received packet, No data will be present incase of ACK-NAK
 * 2. Incase if more fragments need to be received then ACK after retreiving this fragment.
 *
 *  RFC 2716 Section 4.2.  PPP EAP TLS Request Packet
 *
 *  0                   1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |     Code      |   Identifier  |            Length             |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |     Type      |     Flags     |      TLS Message Length
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |     TLS Message Length        |       TLS Data...
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *  The Length field is two octets and indicates the length of the EAP
 *  packet including the Code, Identifir, Length, Type, and TLS data
 *  fields.
 */
static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_status_t status)
{
	EAPTLS_PACKET	*tlspacket;
	uint32_t	data_len = 0;
	uint32_t	len = 0;
	uint8_t		*data = NULL;

	if (status  == EAPTLS_INVALID)
		return NULL;

	/*
	 *	The main EAP code & eaptls_verify() take care of
	 *	ensuring that the packet is OK, and that we can
	 *	extract the various fields we want.
	 *
	 *	e.g. a TLS packet with zero data is allowed as an ACK,
	 *	but we will never see it here, as we will simply
	 *	send another fragment, instead of trying to extract
	 *	the data.
	 *
	 *	MUST have TLS type octet, followed by flags, followed
	 *	by data.
	 */
	assert(eap_ds->response->length > 2);

	tlspacket = eaptls_alloc();
	if (tlspacket == NULL) return NULL;

	/*
	 *	Code & id for EAPTLS & EAP are same
	 *	but eaptls_length = eap_length - 1(EAP-Type = 1 octet)
	 *
	 *	length = code + id + length + type + tlsdata
	 *	       =  1   +  1 +   2    +  1    +  X
	 */
	tlspacket->code = eap_ds->response->code;
	tlspacket->id = eap_ds->response->id;
	tlspacket->length = eap_ds->response->length - 1; /* EAP type */
	tlspacket->flags = eap_ds->response->type.data[0];

	/*
	 *	A quick sanity check of the flags.  If we've been told
	 *	that there's a length, and there isn't one, then stop.
	 */
	if (TLS_LENGTH_INCLUDED(tlspacket->flags) &&
	    (tlspacket->length < 5)) { /* flags + TLS message length */
		RDEBUG("Invalid EAP-TLS packet received.  (Length bit is set, but no length was found.)");
		eaptls_free(&tlspacket);
		return NULL;
	}

	/*
	 *	If the final TLS packet is larger than we can handle, die
	 *	now.
	 *
	 *	Likewise, if the EAP packet says N bytes, and the TLS
	 *	packet says there's fewer bytes, it's a problem.
	 *
	 *	FIXME: Try to ensure that the claimed length is
	 *	consistent across multiple TLS fragments.
	 */
	if (TLS_LENGTH_INCLUDED(tlspacket->flags)) {
		memcpy(&data_len, &eap_ds->response->type.data[1], 4);
		data_len = ntohl(data_len);
		if (data_len > MAX_RECORD_SIZE) {
			RDEBUG("The EAP-TLS packet will contain more data than we can process.");
			eaptls_free(&tlspacket);
			return NULL;
		}

#if 0
		DEBUG2(" TLS: %d %d\n", data_len, tlspacket->length);

		if (data_len < tlspacket->length) {
			RDEBUG("EAP-TLS packet claims to be smaller than the encapsulating EAP packet.");
			eaptls_free(&tlspacket);
			return NULL;
		}
#endif
	}

	switch (status) {
	/*
	 *	The TLS Message Length field is four octets, and
	 *	provides the total length of the TLS message or set of
	 *	messages that is being fragmented; this simplifies
	 *	buffer allocation.
	 *
	 *	Dynamic allocation of buffers as & when we know the
	 *	length should solve the problem.
	 */
	case EAPTLS_FIRST_FRAGMENT:
	case EAPTLS_LENGTH_INCLUDED:
	case EAPTLS_MORE_FRAGMENTS_WITH_LENGTH:
		if (tlspacket->length < 5) { /* flags + TLS message length */
			RDEBUG("Invalid EAP-TLS packet received.  (Expected length, got none.)");
			eaptls_free(&tlspacket);
			return NULL;
		}

		/*
		 *	Extract all the TLS fragments from the
		 *	previous eap_ds Start appending this
		 *	fragment to the above ds
		 */
		memcpy(&data_len, &eap_ds->response->type.data[1], sizeof(uint32_t));
		data_len = ntohl(data_len);
		data = (eap_ds->response->type.data + 5/*flags+TLS-Length*/);
		len = eap_ds->response->type.length - 5/*flags+TLS-Length*/;

		/*
		 *	Hmm... this should be an error, too.
		 */
		if (data_len > len) {
			data_len = len;
		}
		break;

		/*
		 *	Data length is implicit, from the EAP header.
		 */
	case EAPTLS_MORE_FRAGMENTS:
	case EAPTLS_OK:
		data_len = eap_ds->response->type.length - 1/*flags*/;
		data = eap_ds->response->type.data + 1/*flags*/;
		break;

	default:
		RDEBUG("Invalid EAP-TLS packet received");
		eaptls_free(&tlspacket);
		return NULL;
	}

	tlspacket->dlen = data_len;
	if (data_len) {
		tlspacket->data = (unsigned char *)malloc(data_len);
		if (tlspacket->data == NULL) {
			RDEBUG("out of memory");
			eaptls_free(&tlspacket);
			return NULL;
		}
		memcpy(tlspacket->data, data, data_len);
	}

	return tlspacket;
}
Esempio n. 2
0
/*
 *	Process an EAP request
 */
eaptls_status_t eaptls_process(EAP_HANDLER *handler)
{
	tls_session_t *tls_session = (tls_session_t *) handler->opaque;
	EAPTLS_PACKET	*tlspacket;
	eaptls_status_t	status;
	REQUEST *request = handler->request;

	RDEBUG2("processing EAP-TLS");

	/* This case is when SSL generates Alert then we
	 * send that alert to the client and then send the EAP-Failure
	 */
	status = eaptls_verify(handler);
	RDEBUG2("eaptls_verify returned %d\n", status);

	switch (status) {
	default:
	case EAPTLS_INVALID:
	case EAPTLS_FAIL:

		/*
		 *	Success means that we're done the initial
		 *	handshake.  For TTLS, this means send stuff
		 *	back to the client, and the client sends us
		 *	more tunneled data.
		 */
	case EAPTLS_SUCCESS:
		return status;
		break;

		/*
		 *	Normal TLS request, continue with the "get rest
		 *	of fragments" phase.
		 */
	case EAPTLS_REQUEST:
		eaptls_request(handler->eap_ds, tls_session);
		return EAPTLS_HANDLED;
		break;

		/*
		 *	The handshake is done, and we're in the "tunnel
		 *	data" phase.
		 */
	case EAPTLS_OK:
		RDEBUG2("Done initial handshake");

		/*
		 *	Get the rest of the fragments.
		 */
	case EAPTLS_FIRST_FRAGMENT:
	case EAPTLS_MORE_FRAGMENTS:
	case EAPTLS_LENGTH_INCLUDED:
	case EAPTLS_MORE_FRAGMENTS_WITH_LENGTH:
		break;
	}

	/*
	 *	Extract the TLS packet from the buffer.
	 */
	if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL)
		return EAPTLS_FAIL;

	/*
	 *	Get the session struct from the handler
	 *
	 *	update the dirty_in buffer
	 *
	 *	NOTE: This buffer will contain partial data when M bit is set.
	 *
	 * 	CAUTION while reinitializing this buffer, it should be
	 * 	reinitialized only when this M bit is NOT set.
	 */
	if (tlspacket->dlen !=
	    (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) {
		eaptls_free(&tlspacket);
		RDEBUG("Exceeded maximum record size");
		return EAPTLS_FAIL;
	}

	/*
	 *	No longer needed.
	 */
	eaptls_free(&tlspacket);

	/*
	 *	SSL initalization is done.  Return.
	 *
	 *	The TLS data will be in the tls_session structure.
	 */
	if (SSL_is_init_finished(tls_session->ssl)) {
		int err;

		/*
		 *	The initialization may be finished, but if
		 *	there more fragments coming, then send ACK,
		 *	and get the caller to continue the
		 *	conversation.
		 */	
	        if ((status == EAPTLS_MORE_FRAGMENTS) ||
        	    (status == EAPTLS_MORE_FRAGMENTS_WITH_LENGTH) ||
            	    (status == EAPTLS_FIRST_FRAGMENT)) {
			/*
			 *	Send the ACK.
			 */
			eaptls_send_ack(handler->eap_ds,
					tls_session->peap_flag);
			RDEBUG2("Init is done, but tunneled data is fragmented");
			return EAPTLS_HANDLED;
		}

		/*	
		 *	Decrypt the complete record.
		 */
		BIO_write(tls_session->into_ssl, tls_session->dirty_in.data,
			  tls_session->dirty_in.used);

		/*
		 *      Clear the dirty buffer now that we are done with it
		 *      and init the clean_out buffer to store decrypted data
		 */
		(tls_session->record_init)(&tls_session->dirty_in);
		(tls_session->record_init)(&tls_session->clean_out);

		/*
		 *      Read (and decrypt) the tunneled data from the
		 *      SSL session, and put it into the decrypted
		 *      data buffer.
		 */
		err = SSL_read(tls_session->ssl, tls_session->clean_out.data,
			       sizeof(tls_session->clean_out.data));

		if (err < 0) {
			RDEBUG("SSL_read Error");

			switch (SSL_get_error(tls_session->ssl, err)) {
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
				RDEBUG("Error in fragmentation logic");
				break;
			default:
				/*
				 *	FIXME: Call int_ssl_check?
				 */
				break;
			}
			return EAPTLS_FAIL;
		}

		if (err == 0) {
			RDEBUG("WARNING: No data inside of the tunnel.");
		}
	
		/*
		 *	Passed all checks, successfully decrypted data
		 */
		tls_session->clean_out.used = err;
		
		return EAPTLS_OK;
	}

	/*
	 *	Continue the handshake.
	 */
	return eaptls_operation(status, handler);
}
Esempio n. 3
0
/*
 *	Process an EAP request
 */
fr_tls_status_t eaptls_process(EAP_HANDLER *handler)
{
	tls_session_t *tls_session = (tls_session_t *) handler->opaque;
	EAPTLS_PACKET	*tlspacket;
	fr_tls_status_t	status;
	REQUEST *request = handler->request;

	if (!request) return FR_TLS_FAIL;

	RDEBUG2("processing EAP-TLS");
	SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request);

	if (handler->certs) pairadd(&request->packet->vps,
				    paircopy(handler->certs));

	/* This case is when SSL generates Alert then we
	 * send that alert to the client and then send the EAP-Failure
	 */
	status = eaptls_verify(handler);
	RDEBUG2("eaptls_verify returned %d\n", status);

	switch (status) {
	default:
	case FR_TLS_INVALID:
	case FR_TLS_FAIL:

		/*
		 *	Success means that we're done the initial
		 *	handshake.  For TTLS, this means send stuff
		 *	back to the client, and the client sends us
		 *	more tunneled data.
		 */
	case FR_TLS_SUCCESS:
		goto done;

		/*
		 *	Normal TLS request, continue with the "get rest
		 *	of fragments" phase.
		 */
	case FR_TLS_REQUEST:
		eaptls_request(handler->eap_ds, tls_session);
		status = FR_TLS_HANDLED;
		goto done;

		/*
		 *	The handshake is done, and we're in the "tunnel
		 *	data" phase.
		 */
	case FR_TLS_OK:
		RDEBUG2("Done initial handshake");

		/*
		 *	Get the rest of the fragments.
		 */
	case FR_TLS_FIRST_FRAGMENT:
	case FR_TLS_MORE_FRAGMENTS:
	case FR_TLS_LENGTH_INCLUDED:
	case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH:
		break;
	}

	/*
	 *	Extract the TLS packet from the buffer.
	 */
	if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) {
		status = FR_TLS_FAIL;
		goto done;
	}

	/*
	 *	Get the session struct from the handler
	 *
	 *	update the dirty_in buffer
	 *
	 *	NOTE: This buffer will contain partial data when M bit is set.
	 *
	 * 	CAUTION while reinitializing this buffer, it should be
	 * 	reinitialized only when this M bit is NOT set.
	 */
	if (tlspacket->dlen !=
	    (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) {
		eaptls_free(&tlspacket);
		RDEBUG("Exceeded maximum record size");
		status =FR_TLS_FAIL;
		goto done;
	}

	/*
	 *	No longer needed.
	 */
	eaptls_free(&tlspacket);

	/*
	 *	SSL initalization is done.  Return.
	 *
	 *	The TLS data will be in the tls_session structure.
	 */
	if (SSL_is_init_finished(tls_session->ssl)) {
		/*
		 *	The initialization may be finished, but if
		 *	there more fragments coming, then send ACK,
		 *	and get the caller to continue the
		 *	conversation.
		 */	
	        if ((status == FR_TLS_MORE_FRAGMENTS) ||
        	    (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) ||
            	    (status == FR_TLS_FIRST_FRAGMENT)) {
			/*
			 *	Send the ACK.
			 */
			eaptls_send_ack(handler->eap_ds,
					tls_session->peap_flag);
			RDEBUG2("Init is done, but tunneled data is fragmented");
			status = FR_TLS_HANDLED;
			goto done;
		}

		status = tls_application_data(tls_session, request);
		goto done;
	}

	/*
	 *	Continue the handshake.
	 */
	status = eaptls_operation(status, handler);

 done:
	SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL);

	return status;
}