13ff40c12SJohn Marino /*
23ff40c12SJohn Marino  * EAP server method: EAP-TNC (Trusted Network Connect)
33ff40c12SJohn Marino  * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
73ff40c12SJohn Marino  */
83ff40c12SJohn Marino 
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino 
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "eap_i.h"
133ff40c12SJohn Marino #include "tncs.h"
143ff40c12SJohn Marino 
153ff40c12SJohn Marino 
163ff40c12SJohn Marino struct eap_tnc_data {
173ff40c12SJohn Marino 	enum eap_tnc_state {
183ff40c12SJohn Marino 		START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
193ff40c12SJohn Marino 		FAIL
203ff40c12SJohn Marino 	} state;
213ff40c12SJohn Marino 	enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
223ff40c12SJohn Marino 	struct tncs_data *tncs;
233ff40c12SJohn Marino 	struct wpabuf *in_buf;
243ff40c12SJohn Marino 	struct wpabuf *out_buf;
253ff40c12SJohn Marino 	size_t out_used;
263ff40c12SJohn Marino 	size_t fragment_size;
273ff40c12SJohn Marino 	unsigned int was_done:1;
283ff40c12SJohn Marino 	unsigned int was_fail:1;
293ff40c12SJohn Marino };
303ff40c12SJohn Marino 
313ff40c12SJohn Marino 
323ff40c12SJohn Marino /* EAP-TNC Flags */
333ff40c12SJohn Marino #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
343ff40c12SJohn Marino #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
353ff40c12SJohn Marino #define EAP_TNC_FLAGS_START 0x20
363ff40c12SJohn Marino #define EAP_TNC_VERSION_MASK 0x07
373ff40c12SJohn Marino 
383ff40c12SJohn Marino #define EAP_TNC_VERSION 1
393ff40c12SJohn Marino 
403ff40c12SJohn Marino 
eap_tnc_state_txt(enum eap_tnc_state state)413ff40c12SJohn Marino static const char * eap_tnc_state_txt(enum eap_tnc_state state)
423ff40c12SJohn Marino {
433ff40c12SJohn Marino 	switch (state) {
443ff40c12SJohn Marino 	case START:
453ff40c12SJohn Marino 		return "START";
463ff40c12SJohn Marino 	case CONTINUE:
473ff40c12SJohn Marino 		return "CONTINUE";
483ff40c12SJohn Marino 	case RECOMMENDATION:
493ff40c12SJohn Marino 		return "RECOMMENDATION";
503ff40c12SJohn Marino 	case FRAG_ACK:
513ff40c12SJohn Marino 		return "FRAG_ACK";
523ff40c12SJohn Marino 	case WAIT_FRAG_ACK:
533ff40c12SJohn Marino 		return "WAIT_FRAG_ACK";
543ff40c12SJohn Marino 	case DONE:
553ff40c12SJohn Marino 		return "DONE";
563ff40c12SJohn Marino 	case FAIL:
573ff40c12SJohn Marino 		return "FAIL";
583ff40c12SJohn Marino 	}
593ff40c12SJohn Marino 	return "??";
603ff40c12SJohn Marino }
613ff40c12SJohn Marino 
623ff40c12SJohn Marino 
eap_tnc_set_state(struct eap_tnc_data * data,enum eap_tnc_state new_state)633ff40c12SJohn Marino static void eap_tnc_set_state(struct eap_tnc_data *data,
643ff40c12SJohn Marino 			      enum eap_tnc_state new_state)
653ff40c12SJohn Marino {
663ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
673ff40c12SJohn Marino 		   eap_tnc_state_txt(data->state),
683ff40c12SJohn Marino 		   eap_tnc_state_txt(new_state));
693ff40c12SJohn Marino 	data->state = new_state;
703ff40c12SJohn Marino }
713ff40c12SJohn Marino 
723ff40c12SJohn Marino 
eap_tnc_init(struct eap_sm * sm)733ff40c12SJohn Marino static void * eap_tnc_init(struct eap_sm *sm)
743ff40c12SJohn Marino {
753ff40c12SJohn Marino 	struct eap_tnc_data *data;
763ff40c12SJohn Marino 
773ff40c12SJohn Marino 	data = os_zalloc(sizeof(*data));
783ff40c12SJohn Marino 	if (data == NULL)
793ff40c12SJohn Marino 		return NULL;
803ff40c12SJohn Marino 	eap_tnc_set_state(data, START);
813ff40c12SJohn Marino 	data->tncs = tncs_init();
823ff40c12SJohn Marino 	if (data->tncs == NULL) {
833ff40c12SJohn Marino 		os_free(data);
843ff40c12SJohn Marino 		return NULL;
853ff40c12SJohn Marino 	}
863ff40c12SJohn Marino 
873ff40c12SJohn Marino 	data->fragment_size = sm->fragment_size > 100 ?
883ff40c12SJohn Marino 		sm->fragment_size - 98 : 1300;
893ff40c12SJohn Marino 
903ff40c12SJohn Marino 	return data;
913ff40c12SJohn Marino }
923ff40c12SJohn Marino 
933ff40c12SJohn Marino 
eap_tnc_reset(struct eap_sm * sm,void * priv)943ff40c12SJohn Marino static void eap_tnc_reset(struct eap_sm *sm, void *priv)
953ff40c12SJohn Marino {
963ff40c12SJohn Marino 	struct eap_tnc_data *data = priv;
973ff40c12SJohn Marino 	wpabuf_free(data->in_buf);
983ff40c12SJohn Marino 	wpabuf_free(data->out_buf);
993ff40c12SJohn Marino 	tncs_deinit(data->tncs);
1003ff40c12SJohn Marino 	os_free(data);
1013ff40c12SJohn Marino }
1023ff40c12SJohn Marino 
1033ff40c12SJohn Marino 
eap_tnc_build_start(struct eap_sm * sm,struct eap_tnc_data * data,u8 id)1043ff40c12SJohn Marino static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
1053ff40c12SJohn Marino 					   struct eap_tnc_data *data, u8 id)
1063ff40c12SJohn Marino {
1073ff40c12SJohn Marino 	struct wpabuf *req;
1083ff40c12SJohn Marino 
1093ff40c12SJohn Marino 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
1103ff40c12SJohn Marino 			    id);
1113ff40c12SJohn Marino 	if (req == NULL) {
1123ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
1133ff40c12SJohn Marino 			   "request");
1143ff40c12SJohn Marino 		eap_tnc_set_state(data, FAIL);
1153ff40c12SJohn Marino 		return NULL;
1163ff40c12SJohn Marino 	}
1173ff40c12SJohn Marino 
1183ff40c12SJohn Marino 	wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
1193ff40c12SJohn Marino 
1203ff40c12SJohn Marino 	eap_tnc_set_state(data, CONTINUE);
1213ff40c12SJohn Marino 
1223ff40c12SJohn Marino 	return req;
1233ff40c12SJohn Marino }
1243ff40c12SJohn Marino 
1253ff40c12SJohn Marino 
eap_tnc_build(struct eap_sm * sm,struct eap_tnc_data * data)1263ff40c12SJohn Marino static struct wpabuf * eap_tnc_build(struct eap_sm *sm,
1273ff40c12SJohn Marino 				     struct eap_tnc_data *data)
1283ff40c12SJohn Marino {
1293ff40c12SJohn Marino 	struct wpabuf *req;
1303ff40c12SJohn Marino 	u8 *rpos, *rpos1;
1313ff40c12SJohn Marino 	size_t rlen;
1323ff40c12SJohn Marino 	char *start_buf, *end_buf;
1333ff40c12SJohn Marino 	size_t start_len, end_len;
1343ff40c12SJohn Marino 	size_t imv_len;
1353ff40c12SJohn Marino 
1363ff40c12SJohn Marino 	imv_len = tncs_total_send_len(data->tncs);
1373ff40c12SJohn Marino 
1383ff40c12SJohn Marino 	start_buf = tncs_if_tnccs_start(data->tncs);
1393ff40c12SJohn Marino 	if (start_buf == NULL)
1403ff40c12SJohn Marino 		return NULL;
1413ff40c12SJohn Marino 	start_len = os_strlen(start_buf);
1423ff40c12SJohn Marino 	end_buf = tncs_if_tnccs_end();
1433ff40c12SJohn Marino 	if (end_buf == NULL) {
1443ff40c12SJohn Marino 		os_free(start_buf);
1453ff40c12SJohn Marino 		return NULL;
1463ff40c12SJohn Marino 	}
1473ff40c12SJohn Marino 	end_len = os_strlen(end_buf);
1483ff40c12SJohn Marino 
1493ff40c12SJohn Marino 	rlen = start_len + imv_len + end_len;
1503ff40c12SJohn Marino 	req = wpabuf_alloc(rlen);
1513ff40c12SJohn Marino 	if (req == NULL) {
1523ff40c12SJohn Marino 		os_free(start_buf);
1533ff40c12SJohn Marino 		os_free(end_buf);
1543ff40c12SJohn Marino 		return NULL;
1553ff40c12SJohn Marino 	}
1563ff40c12SJohn Marino 
1573ff40c12SJohn Marino 	wpabuf_put_data(req, start_buf, start_len);
1583ff40c12SJohn Marino 	os_free(start_buf);
1593ff40c12SJohn Marino 
1603ff40c12SJohn Marino 	rpos1 = wpabuf_put(req, 0);
1613ff40c12SJohn Marino 	rpos = tncs_copy_send_buf(data->tncs, rpos1);
1623ff40c12SJohn Marino 	wpabuf_put(req, rpos - rpos1);
1633ff40c12SJohn Marino 
1643ff40c12SJohn Marino 	wpabuf_put_data(req, end_buf, end_len);
1653ff40c12SJohn Marino 	os_free(end_buf);
1663ff40c12SJohn Marino 
1673ff40c12SJohn Marino 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
1683ff40c12SJohn Marino 			  wpabuf_head(req), wpabuf_len(req));
1693ff40c12SJohn Marino 
1703ff40c12SJohn Marino 	return req;
1713ff40c12SJohn Marino }
1723ff40c12SJohn Marino 
1733ff40c12SJohn Marino 
eap_tnc_build_recommendation(struct eap_sm * sm,struct eap_tnc_data * data)1743ff40c12SJohn Marino static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
1753ff40c12SJohn Marino 						    struct eap_tnc_data *data)
1763ff40c12SJohn Marino {
1773ff40c12SJohn Marino 	switch (data->recommendation) {
1783ff40c12SJohn Marino 	case ALLOW:
1793ff40c12SJohn Marino 		eap_tnc_set_state(data, DONE);
1803ff40c12SJohn Marino 		break;
1813ff40c12SJohn Marino 	case ISOLATE:
1823ff40c12SJohn Marino 		eap_tnc_set_state(data, FAIL);
1833ff40c12SJohn Marino 		/* TODO: support assignment to a different VLAN */
1843ff40c12SJohn Marino 		break;
1853ff40c12SJohn Marino 	case NO_ACCESS:
1863ff40c12SJohn Marino 		eap_tnc_set_state(data, FAIL);
1873ff40c12SJohn Marino 		break;
1883ff40c12SJohn Marino 	case NO_RECOMMENDATION:
1893ff40c12SJohn Marino 		eap_tnc_set_state(data, DONE);
1903ff40c12SJohn Marino 		break;
1913ff40c12SJohn Marino 	default:
1923ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
1933ff40c12SJohn Marino 		return NULL;
1943ff40c12SJohn Marino 	}
1953ff40c12SJohn Marino 
1963ff40c12SJohn Marino 	return eap_tnc_build(sm, data);
1973ff40c12SJohn Marino }
1983ff40c12SJohn Marino 
1993ff40c12SJohn Marino 
eap_tnc_build_frag_ack(u8 id,u8 code)2003ff40c12SJohn Marino static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
2013ff40c12SJohn Marino {
2023ff40c12SJohn Marino 	struct wpabuf *msg;
2033ff40c12SJohn Marino 
2043ff40c12SJohn Marino 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
2053ff40c12SJohn Marino 	if (msg == NULL) {
2063ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
2073ff40c12SJohn Marino 			   "for fragment ack");
2083ff40c12SJohn Marino 		return NULL;
2093ff40c12SJohn Marino 	}
2103ff40c12SJohn Marino 	wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
2113ff40c12SJohn Marino 
2123ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
2133ff40c12SJohn Marino 
2143ff40c12SJohn Marino 	return msg;
2153ff40c12SJohn Marino }
2163ff40c12SJohn Marino 
2173ff40c12SJohn Marino 
eap_tnc_build_msg(struct eap_tnc_data * data,u8 id)2183ff40c12SJohn Marino static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
2193ff40c12SJohn Marino {
2203ff40c12SJohn Marino 	struct wpabuf *req;
2213ff40c12SJohn Marino 	u8 flags;
2223ff40c12SJohn Marino 	size_t send_len, plen;
2233ff40c12SJohn Marino 
2243ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
2253ff40c12SJohn Marino 
2263ff40c12SJohn Marino 	flags = EAP_TNC_VERSION;
2273ff40c12SJohn Marino 	send_len = wpabuf_len(data->out_buf) - data->out_used;
2283ff40c12SJohn Marino 	if (1 + send_len > data->fragment_size) {
2293ff40c12SJohn Marino 		send_len = data->fragment_size - 1;
2303ff40c12SJohn Marino 		flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
2313ff40c12SJohn Marino 		if (data->out_used == 0) {
2323ff40c12SJohn Marino 			flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
2333ff40c12SJohn Marino 			send_len -= 4;
2343ff40c12SJohn Marino 		}
2353ff40c12SJohn Marino 	}
2363ff40c12SJohn Marino 
2373ff40c12SJohn Marino 	plen = 1 + send_len;
2383ff40c12SJohn Marino 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
2393ff40c12SJohn Marino 		plen += 4;
2403ff40c12SJohn Marino 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
2413ff40c12SJohn Marino 			    EAP_CODE_REQUEST, id);
2423ff40c12SJohn Marino 	if (req == NULL)
2433ff40c12SJohn Marino 		return NULL;
2443ff40c12SJohn Marino 
2453ff40c12SJohn Marino 	wpabuf_put_u8(req, flags); /* Flags */
2463ff40c12SJohn Marino 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
2473ff40c12SJohn Marino 		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
2483ff40c12SJohn Marino 
2493ff40c12SJohn Marino 	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
2503ff40c12SJohn Marino 			send_len);
2513ff40c12SJohn Marino 	data->out_used += send_len;
2523ff40c12SJohn Marino 
2533ff40c12SJohn Marino 	if (data->out_used == wpabuf_len(data->out_buf)) {
2543ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
2553ff40c12SJohn Marino 			   "(message sent completely)",
2563ff40c12SJohn Marino 			   (unsigned long) send_len);
2573ff40c12SJohn Marino 		wpabuf_free(data->out_buf);
2583ff40c12SJohn Marino 		data->out_buf = NULL;
2593ff40c12SJohn Marino 		data->out_used = 0;
2603ff40c12SJohn Marino 		if (data->was_fail)
2613ff40c12SJohn Marino 			eap_tnc_set_state(data, FAIL);
2623ff40c12SJohn Marino 		else if (data->was_done)
2633ff40c12SJohn Marino 			eap_tnc_set_state(data, DONE);
2643ff40c12SJohn Marino 	} else {
2653ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
2663ff40c12SJohn Marino 			   "(%lu more to send)", (unsigned long) send_len,
2673ff40c12SJohn Marino 			   (unsigned long) wpabuf_len(data->out_buf) -
2683ff40c12SJohn Marino 			   data->out_used);
2693ff40c12SJohn Marino 		if (data->state == FAIL)
2703ff40c12SJohn Marino 			data->was_fail = 1;
2713ff40c12SJohn Marino 		else if (data->state == DONE)
2723ff40c12SJohn Marino 			data->was_done = 1;
2733ff40c12SJohn Marino 		eap_tnc_set_state(data, WAIT_FRAG_ACK);
2743ff40c12SJohn Marino 	}
2753ff40c12SJohn Marino 
2763ff40c12SJohn Marino 	return req;
2773ff40c12SJohn Marino }
2783ff40c12SJohn Marino 
2793ff40c12SJohn Marino 
eap_tnc_buildReq(struct eap_sm * sm,void * priv,u8 id)2803ff40c12SJohn Marino static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
2813ff40c12SJohn Marino {
2823ff40c12SJohn Marino 	struct eap_tnc_data *data = priv;
2833ff40c12SJohn Marino 
2843ff40c12SJohn Marino 	switch (data->state) {
2853ff40c12SJohn Marino 	case START:
2863ff40c12SJohn Marino 		tncs_init_connection(data->tncs);
2873ff40c12SJohn Marino 		return eap_tnc_build_start(sm, data, id);
2883ff40c12SJohn Marino 	case CONTINUE:
2893ff40c12SJohn Marino 		if (data->out_buf == NULL) {
2903ff40c12SJohn Marino 			data->out_buf = eap_tnc_build(sm, data);
2913ff40c12SJohn Marino 			if (data->out_buf == NULL) {
2923ff40c12SJohn Marino 				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
2933ff40c12SJohn Marino 					   "generate message");
2943ff40c12SJohn Marino 				return NULL;
2953ff40c12SJohn Marino 			}
2963ff40c12SJohn Marino 			data->out_used = 0;
2973ff40c12SJohn Marino 		}
2983ff40c12SJohn Marino 		return eap_tnc_build_msg(data, id);
2993ff40c12SJohn Marino 	case RECOMMENDATION:
3003ff40c12SJohn Marino 		if (data->out_buf == NULL) {
3013ff40c12SJohn Marino 			data->out_buf = eap_tnc_build_recommendation(sm, data);
3023ff40c12SJohn Marino 			if (data->out_buf == NULL) {
3033ff40c12SJohn Marino 				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
3043ff40c12SJohn Marino 					   "generate recommendation message");
3053ff40c12SJohn Marino 				return NULL;
3063ff40c12SJohn Marino 			}
3073ff40c12SJohn Marino 			data->out_used = 0;
3083ff40c12SJohn Marino 		}
3093ff40c12SJohn Marino 		return eap_tnc_build_msg(data, id);
3103ff40c12SJohn Marino 	case WAIT_FRAG_ACK:
3113ff40c12SJohn Marino 		return eap_tnc_build_msg(data, id);
3123ff40c12SJohn Marino 	case FRAG_ACK:
3133ff40c12SJohn Marino 		return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
3143ff40c12SJohn Marino 	case DONE:
3153ff40c12SJohn Marino 	case FAIL:
3163ff40c12SJohn Marino 		return NULL;
3173ff40c12SJohn Marino 	}
3183ff40c12SJohn Marino 
3193ff40c12SJohn Marino 	return NULL;
3203ff40c12SJohn Marino }
3213ff40c12SJohn Marino 
3223ff40c12SJohn Marino 
eap_tnc_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)3233ff40c12SJohn Marino static Boolean eap_tnc_check(struct eap_sm *sm, void *priv,
3243ff40c12SJohn Marino 			     struct wpabuf *respData)
3253ff40c12SJohn Marino {
3263ff40c12SJohn Marino 	struct eap_tnc_data *data = priv;
3273ff40c12SJohn Marino 	const u8 *pos;
3283ff40c12SJohn Marino 	size_t len;
3293ff40c12SJohn Marino 
3303ff40c12SJohn Marino 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
3313ff40c12SJohn Marino 			       &len);
3323ff40c12SJohn Marino 	if (pos == NULL) {
3333ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
3343ff40c12SJohn Marino 		return TRUE;
3353ff40c12SJohn Marino 	}
3363ff40c12SJohn Marino 
3373ff40c12SJohn Marino 	if (len == 0 && data->state != WAIT_FRAG_ACK) {
3383ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
3393ff40c12SJohn Marino 		return TRUE;
3403ff40c12SJohn Marino 	}
3413ff40c12SJohn Marino 
3423ff40c12SJohn Marino 	if (len == 0)
3433ff40c12SJohn Marino 		return FALSE; /* Fragment ACK does not include flags */
3443ff40c12SJohn Marino 
3453ff40c12SJohn Marino 	if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
3463ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
3473ff40c12SJohn Marino 			   *pos & EAP_TNC_VERSION_MASK);
3483ff40c12SJohn Marino 		return TRUE;
3493ff40c12SJohn Marino 	}
3503ff40c12SJohn Marino 
3513ff40c12SJohn Marino 	if (*pos & EAP_TNC_FLAGS_START) {
3523ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
3533ff40c12SJohn Marino 		return TRUE;
3543ff40c12SJohn Marino 	}
3553ff40c12SJohn Marino 
3563ff40c12SJohn Marino 	return FALSE;
3573ff40c12SJohn Marino }
3583ff40c12SJohn Marino 
3593ff40c12SJohn Marino 
tncs_process(struct eap_tnc_data * data,struct wpabuf * inbuf)3603ff40c12SJohn Marino static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
3613ff40c12SJohn Marino {
3623ff40c12SJohn Marino 	enum tncs_process_res res;
3633ff40c12SJohn Marino 
3643ff40c12SJohn Marino 	res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
3653ff40c12SJohn Marino 				    wpabuf_len(inbuf));
3663ff40c12SJohn Marino 	switch (res) {
3673ff40c12SJohn Marino 	case TNCCS_RECOMMENDATION_ALLOW:
3683ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
3693ff40c12SJohn Marino 		eap_tnc_set_state(data, RECOMMENDATION);
3703ff40c12SJohn Marino 		data->recommendation = ALLOW;
3713ff40c12SJohn Marino 		break;
3723ff40c12SJohn Marino 	case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
3733ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
3743ff40c12SJohn Marino 		eap_tnc_set_state(data, RECOMMENDATION);
3753ff40c12SJohn Marino 		data->recommendation = NO_RECOMMENDATION;
3763ff40c12SJohn Marino 		break;
3773ff40c12SJohn Marino 	case TNCCS_RECOMMENDATION_ISOLATE:
3783ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
3793ff40c12SJohn Marino 		eap_tnc_set_state(data, RECOMMENDATION);
3803ff40c12SJohn Marino 		data->recommendation = ISOLATE;
3813ff40c12SJohn Marino 		break;
3823ff40c12SJohn Marino 	case TNCCS_RECOMMENDATION_NO_ACCESS:
3833ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
3843ff40c12SJohn Marino 		eap_tnc_set_state(data, RECOMMENDATION);
3853ff40c12SJohn Marino 		data->recommendation = NO_ACCESS;
3863ff40c12SJohn Marino 		break;
3873ff40c12SJohn Marino 	case TNCCS_PROCESS_ERROR:
3883ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
3893ff40c12SJohn Marino 		eap_tnc_set_state(data, FAIL);
3903ff40c12SJohn Marino 		break;
3913ff40c12SJohn Marino 	default:
3923ff40c12SJohn Marino 		break;
3933ff40c12SJohn Marino 	}
3943ff40c12SJohn Marino }
3953ff40c12SJohn Marino 
3963ff40c12SJohn Marino 
eap_tnc_process_cont(struct eap_tnc_data * data,const u8 * buf,size_t len)3973ff40c12SJohn Marino static int eap_tnc_process_cont(struct eap_tnc_data *data,
3983ff40c12SJohn Marino 				const u8 *buf, size_t len)
3993ff40c12SJohn Marino {
4003ff40c12SJohn Marino 	/* Process continuation of a pending message */
4013ff40c12SJohn Marino 	if (len > wpabuf_tailroom(data->in_buf)) {
4023ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
4033ff40c12SJohn Marino 		eap_tnc_set_state(data, FAIL);
4043ff40c12SJohn Marino 		return -1;
4053ff40c12SJohn Marino 	}
4063ff40c12SJohn Marino 
4073ff40c12SJohn Marino 	wpabuf_put_data(data->in_buf, buf, len);
4083ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
4093ff40c12SJohn Marino 		   "bytes more", (unsigned long) len,
4103ff40c12SJohn Marino 		   (unsigned long) wpabuf_tailroom(data->in_buf));
4113ff40c12SJohn Marino 
4123ff40c12SJohn Marino 	return 0;
4133ff40c12SJohn Marino }
4143ff40c12SJohn Marino 
4153ff40c12SJohn Marino 
eap_tnc_process_fragment(struct eap_tnc_data * data,u8 flags,u32 message_length,const u8 * buf,size_t len)4163ff40c12SJohn Marino static int eap_tnc_process_fragment(struct eap_tnc_data *data,
4173ff40c12SJohn Marino 				    u8 flags, u32 message_length,
4183ff40c12SJohn Marino 				    const u8 *buf, size_t len)
4193ff40c12SJohn Marino {
4203ff40c12SJohn Marino 	/* Process a fragment that is not the last one of the message */
4213ff40c12SJohn Marino 	if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
4223ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
4233ff40c12SJohn Marino 			   "fragmented packet");
4243ff40c12SJohn Marino 		return -1;
4253ff40c12SJohn Marino 	}
4263ff40c12SJohn Marino 
4273ff40c12SJohn Marino 	if (data->in_buf == NULL) {
4283ff40c12SJohn Marino 		/* First fragment of the message */
4293ff40c12SJohn Marino 		data->in_buf = wpabuf_alloc(message_length);
4303ff40c12SJohn Marino 		if (data->in_buf == NULL) {
4313ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
4323ff40c12SJohn Marino 				   "message");
4333ff40c12SJohn Marino 			return -1;
4343ff40c12SJohn Marino 		}
4353ff40c12SJohn Marino 		wpabuf_put_data(data->in_buf, buf, len);
4363ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
4373ff40c12SJohn Marino 			   "fragment, waiting for %lu bytes more",
4383ff40c12SJohn Marino 			   (unsigned long) len,
4393ff40c12SJohn Marino 			   (unsigned long) wpabuf_tailroom(data->in_buf));
4403ff40c12SJohn Marino 	}
4413ff40c12SJohn Marino 
4423ff40c12SJohn Marino 	return 0;
4433ff40c12SJohn Marino }
4443ff40c12SJohn Marino 
4453ff40c12SJohn Marino 
eap_tnc_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)4463ff40c12SJohn Marino static void eap_tnc_process(struct eap_sm *sm, void *priv,
4473ff40c12SJohn Marino 			    struct wpabuf *respData)
4483ff40c12SJohn Marino {
4493ff40c12SJohn Marino 	struct eap_tnc_data *data = priv;
4503ff40c12SJohn Marino 	const u8 *pos, *end;
4513ff40c12SJohn Marino 	size_t len;
4523ff40c12SJohn Marino 	u8 flags;
4533ff40c12SJohn Marino 	u32 message_length = 0;
4543ff40c12SJohn Marino 	struct wpabuf tmpbuf;
4553ff40c12SJohn Marino 
4563ff40c12SJohn Marino 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
4573ff40c12SJohn Marino 	if (pos == NULL)
4583ff40c12SJohn Marino 		return; /* Should not happen; message already verified */
4593ff40c12SJohn Marino 
4603ff40c12SJohn Marino 	end = pos + len;
4613ff40c12SJohn Marino 
4623ff40c12SJohn Marino 	if (len == 1 && (data->state == DONE || data->state == FAIL)) {
4633ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
4643ff40c12SJohn Marino 			   "message");
4653ff40c12SJohn Marino 		return;
4663ff40c12SJohn Marino 	}
4673ff40c12SJohn Marino 
4683ff40c12SJohn Marino 	if (len == 0) {
4693ff40c12SJohn Marino 		/* fragment ack */
4703ff40c12SJohn Marino 		flags = 0;
4713ff40c12SJohn Marino 	} else
4723ff40c12SJohn Marino 		flags = *pos++;
4733ff40c12SJohn Marino 
4743ff40c12SJohn Marino 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
4753ff40c12SJohn Marino 		if (end - pos < 4) {
4763ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
4773ff40c12SJohn Marino 			eap_tnc_set_state(data, FAIL);
4783ff40c12SJohn Marino 			return;
4793ff40c12SJohn Marino 		}
4803ff40c12SJohn Marino 		message_length = WPA_GET_BE32(pos);
4813ff40c12SJohn Marino 		pos += 4;
4823ff40c12SJohn Marino 
483*a1157835SDaniel Fojt 		if (message_length < (u32) (end - pos) ||
484*a1157835SDaniel Fojt 		    message_length > 75000) {
4853ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
4863ff40c12SJohn Marino 				   "Length (%d; %ld remaining in this msg)",
4873ff40c12SJohn Marino 				   message_length, (long) (end - pos));
4883ff40c12SJohn Marino 			eap_tnc_set_state(data, FAIL);
4893ff40c12SJohn Marino 			return;
4903ff40c12SJohn Marino 		}
4913ff40c12SJohn Marino 	}
4923ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
4933ff40c12SJohn Marino 		   "Message Length %u", flags, message_length);
4943ff40c12SJohn Marino 
4953ff40c12SJohn Marino 	if (data->state == WAIT_FRAG_ACK) {
4963ff40c12SJohn Marino 		if (len > 1) {
4973ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
4983ff40c12SJohn Marino 				   "in WAIT_FRAG_ACK state");
4993ff40c12SJohn Marino 			eap_tnc_set_state(data, FAIL);
5003ff40c12SJohn Marino 			return;
5013ff40c12SJohn Marino 		}
5023ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
5033ff40c12SJohn Marino 		eap_tnc_set_state(data, CONTINUE);
5043ff40c12SJohn Marino 		return;
5053ff40c12SJohn Marino 	}
5063ff40c12SJohn Marino 
5073ff40c12SJohn Marino 	if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
5083ff40c12SJohn Marino 		eap_tnc_set_state(data, FAIL);
5093ff40c12SJohn Marino 		return;
5103ff40c12SJohn Marino 	}
5113ff40c12SJohn Marino 
5123ff40c12SJohn Marino 	if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
5133ff40c12SJohn Marino 		if (eap_tnc_process_fragment(data, flags, message_length,
5143ff40c12SJohn Marino 					     pos, end - pos) < 0)
5153ff40c12SJohn Marino 			eap_tnc_set_state(data, FAIL);
5163ff40c12SJohn Marino 		else
5173ff40c12SJohn Marino 			eap_tnc_set_state(data, FRAG_ACK);
5183ff40c12SJohn Marino 		return;
5193ff40c12SJohn Marino 	} else if (data->state == FRAG_ACK) {
5203ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
5213ff40c12SJohn Marino 		eap_tnc_set_state(data, CONTINUE);
5223ff40c12SJohn Marino 	}
5233ff40c12SJohn Marino 
5243ff40c12SJohn Marino 	if (data->in_buf == NULL) {
5253ff40c12SJohn Marino 		/* Wrap unfragmented messages as wpabuf without extra copy */
5263ff40c12SJohn Marino 		wpabuf_set(&tmpbuf, pos, end - pos);
5273ff40c12SJohn Marino 		data->in_buf = &tmpbuf;
5283ff40c12SJohn Marino 	}
5293ff40c12SJohn Marino 
5303ff40c12SJohn Marino 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
5313ff40c12SJohn Marino 			  wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
5323ff40c12SJohn Marino 	tncs_process(data, data->in_buf);
5333ff40c12SJohn Marino 
5343ff40c12SJohn Marino 	if (data->in_buf != &tmpbuf)
5353ff40c12SJohn Marino 		wpabuf_free(data->in_buf);
5363ff40c12SJohn Marino 	data->in_buf = NULL;
5373ff40c12SJohn Marino }
5383ff40c12SJohn Marino 
5393ff40c12SJohn Marino 
eap_tnc_isDone(struct eap_sm * sm,void * priv)5403ff40c12SJohn Marino static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv)
5413ff40c12SJohn Marino {
5423ff40c12SJohn Marino 	struct eap_tnc_data *data = priv;
5433ff40c12SJohn Marino 	return data->state == DONE || data->state == FAIL;
5443ff40c12SJohn Marino }
5453ff40c12SJohn Marino 
5463ff40c12SJohn Marino 
eap_tnc_isSuccess(struct eap_sm * sm,void * priv)5473ff40c12SJohn Marino static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
5483ff40c12SJohn Marino {
5493ff40c12SJohn Marino 	struct eap_tnc_data *data = priv;
5503ff40c12SJohn Marino 	return data->state == DONE;
5513ff40c12SJohn Marino }
5523ff40c12SJohn Marino 
5533ff40c12SJohn Marino 
eap_server_tnc_register(void)5543ff40c12SJohn Marino int eap_server_tnc_register(void)
5553ff40c12SJohn Marino {
5563ff40c12SJohn Marino 	struct eap_method *eap;
5573ff40c12SJohn Marino 
5583ff40c12SJohn Marino 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
5593ff40c12SJohn Marino 				      EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
5603ff40c12SJohn Marino 	if (eap == NULL)
5613ff40c12SJohn Marino 		return -1;
5623ff40c12SJohn Marino 
5633ff40c12SJohn Marino 	eap->init = eap_tnc_init;
5643ff40c12SJohn Marino 	eap->reset = eap_tnc_reset;
5653ff40c12SJohn Marino 	eap->buildReq = eap_tnc_buildReq;
5663ff40c12SJohn Marino 	eap->check = eap_tnc_check;
5673ff40c12SJohn Marino 	eap->process = eap_tnc_process;
5683ff40c12SJohn Marino 	eap->isDone = eap_tnc_isDone;
5693ff40c12SJohn Marino 	eap->isSuccess = eap_tnc_isSuccess;
5703ff40c12SJohn Marino 
571*a1157835SDaniel Fojt 	return eap_server_method_register(eap);
5723ff40c12SJohn Marino }
573