xref: /freebsd/contrib/wpa/src/eap_server/eap_server_tnc.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * EAP server method: EAP-TNC (Trusted Network Connect)
3e28a4053SRui Paulo  * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "common.h"
12e28a4053SRui Paulo #include "eap_i.h"
13e28a4053SRui Paulo #include "tncs.h"
14e28a4053SRui Paulo 
15e28a4053SRui Paulo 
16e28a4053SRui Paulo struct eap_tnc_data {
17e28a4053SRui Paulo 	enum eap_tnc_state {
18e28a4053SRui Paulo 		START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
19e28a4053SRui Paulo 		FAIL
20e28a4053SRui Paulo 	} state;
21e28a4053SRui Paulo 	enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
22e28a4053SRui Paulo 	struct tncs_data *tncs;
23e28a4053SRui Paulo 	struct wpabuf *in_buf;
24e28a4053SRui Paulo 	struct wpabuf *out_buf;
25e28a4053SRui Paulo 	size_t out_used;
26e28a4053SRui Paulo 	size_t fragment_size;
27e28a4053SRui Paulo 	unsigned int was_done:1;
28e28a4053SRui Paulo 	unsigned int was_fail:1;
29e28a4053SRui Paulo };
30e28a4053SRui Paulo 
31e28a4053SRui Paulo 
32e28a4053SRui Paulo /* EAP-TNC Flags */
33e28a4053SRui Paulo #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
34e28a4053SRui Paulo #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
35e28a4053SRui Paulo #define EAP_TNC_FLAGS_START 0x20
36e28a4053SRui Paulo #define EAP_TNC_VERSION_MASK 0x07
37e28a4053SRui Paulo 
38e28a4053SRui Paulo #define EAP_TNC_VERSION 1
39e28a4053SRui Paulo 
40e28a4053SRui Paulo 
eap_tnc_state_txt(enum eap_tnc_state state)41e28a4053SRui Paulo static const char * eap_tnc_state_txt(enum eap_tnc_state state)
42e28a4053SRui Paulo {
43e28a4053SRui Paulo 	switch (state) {
44e28a4053SRui Paulo 	case START:
45e28a4053SRui Paulo 		return "START";
46e28a4053SRui Paulo 	case CONTINUE:
47e28a4053SRui Paulo 		return "CONTINUE";
48e28a4053SRui Paulo 	case RECOMMENDATION:
49e28a4053SRui Paulo 		return "RECOMMENDATION";
50e28a4053SRui Paulo 	case FRAG_ACK:
51e28a4053SRui Paulo 		return "FRAG_ACK";
52e28a4053SRui Paulo 	case WAIT_FRAG_ACK:
53e28a4053SRui Paulo 		return "WAIT_FRAG_ACK";
54e28a4053SRui Paulo 	case DONE:
55e28a4053SRui Paulo 		return "DONE";
56e28a4053SRui Paulo 	case FAIL:
57e28a4053SRui Paulo 		return "FAIL";
58e28a4053SRui Paulo 	}
59e28a4053SRui Paulo 	return "??";
60e28a4053SRui Paulo }
61e28a4053SRui Paulo 
62e28a4053SRui Paulo 
eap_tnc_set_state(struct eap_tnc_data * data,enum eap_tnc_state new_state)63e28a4053SRui Paulo static void eap_tnc_set_state(struct eap_tnc_data *data,
64e28a4053SRui Paulo 			      enum eap_tnc_state new_state)
65e28a4053SRui Paulo {
66e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
67e28a4053SRui Paulo 		   eap_tnc_state_txt(data->state),
68e28a4053SRui Paulo 		   eap_tnc_state_txt(new_state));
69e28a4053SRui Paulo 	data->state = new_state;
70e28a4053SRui Paulo }
71e28a4053SRui Paulo 
72e28a4053SRui Paulo 
eap_tnc_init(struct eap_sm * sm)73e28a4053SRui Paulo static void * eap_tnc_init(struct eap_sm *sm)
74e28a4053SRui Paulo {
75e28a4053SRui Paulo 	struct eap_tnc_data *data;
76e28a4053SRui Paulo 
77e28a4053SRui Paulo 	data = os_zalloc(sizeof(*data));
78e28a4053SRui Paulo 	if (data == NULL)
79e28a4053SRui Paulo 		return NULL;
80e28a4053SRui Paulo 	eap_tnc_set_state(data, START);
81e28a4053SRui Paulo 	data->tncs = tncs_init();
82e28a4053SRui Paulo 	if (data->tncs == NULL) {
83e28a4053SRui Paulo 		os_free(data);
84e28a4053SRui Paulo 		return NULL;
85e28a4053SRui Paulo 	}
86e28a4053SRui Paulo 
87*c1d255d3SCy Schubert 	data->fragment_size = sm->cfg->fragment_size > 100 ?
88*c1d255d3SCy Schubert 		sm->cfg->fragment_size - 98 : 1300;
89e28a4053SRui Paulo 
90e28a4053SRui Paulo 	return data;
91e28a4053SRui Paulo }
92e28a4053SRui Paulo 
93e28a4053SRui Paulo 
eap_tnc_reset(struct eap_sm * sm,void * priv)94e28a4053SRui Paulo static void eap_tnc_reset(struct eap_sm *sm, void *priv)
95e28a4053SRui Paulo {
96e28a4053SRui Paulo 	struct eap_tnc_data *data = priv;
97e28a4053SRui Paulo 	wpabuf_free(data->in_buf);
98e28a4053SRui Paulo 	wpabuf_free(data->out_buf);
99e28a4053SRui Paulo 	tncs_deinit(data->tncs);
100e28a4053SRui Paulo 	os_free(data);
101e28a4053SRui Paulo }
102e28a4053SRui Paulo 
103e28a4053SRui Paulo 
eap_tnc_build_start(struct eap_sm * sm,struct eap_tnc_data * data,u8 id)104e28a4053SRui Paulo static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
105e28a4053SRui Paulo 					   struct eap_tnc_data *data, u8 id)
106e28a4053SRui Paulo {
107e28a4053SRui Paulo 	struct wpabuf *req;
108e28a4053SRui Paulo 
109e28a4053SRui Paulo 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
110e28a4053SRui Paulo 			    id);
111e28a4053SRui Paulo 	if (req == NULL) {
112e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
113e28a4053SRui Paulo 			   "request");
114e28a4053SRui Paulo 		eap_tnc_set_state(data, FAIL);
115e28a4053SRui Paulo 		return NULL;
116e28a4053SRui Paulo 	}
117e28a4053SRui Paulo 
118e28a4053SRui Paulo 	wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
119e28a4053SRui Paulo 
120e28a4053SRui Paulo 	eap_tnc_set_state(data, CONTINUE);
121e28a4053SRui Paulo 
122e28a4053SRui Paulo 	return req;
123e28a4053SRui Paulo }
124e28a4053SRui Paulo 
125e28a4053SRui Paulo 
eap_tnc_build(struct eap_sm * sm,struct eap_tnc_data * data)126e28a4053SRui Paulo static struct wpabuf * eap_tnc_build(struct eap_sm *sm,
127e28a4053SRui Paulo 				     struct eap_tnc_data *data)
128e28a4053SRui Paulo {
129e28a4053SRui Paulo 	struct wpabuf *req;
130e28a4053SRui Paulo 	u8 *rpos, *rpos1;
131e28a4053SRui Paulo 	size_t rlen;
132e28a4053SRui Paulo 	char *start_buf, *end_buf;
133e28a4053SRui Paulo 	size_t start_len, end_len;
134e28a4053SRui Paulo 	size_t imv_len;
135e28a4053SRui Paulo 
136e28a4053SRui Paulo 	imv_len = tncs_total_send_len(data->tncs);
137e28a4053SRui Paulo 
138e28a4053SRui Paulo 	start_buf = tncs_if_tnccs_start(data->tncs);
139e28a4053SRui Paulo 	if (start_buf == NULL)
140e28a4053SRui Paulo 		return NULL;
141e28a4053SRui Paulo 	start_len = os_strlen(start_buf);
142e28a4053SRui Paulo 	end_buf = tncs_if_tnccs_end();
143e28a4053SRui Paulo 	if (end_buf == NULL) {
144e28a4053SRui Paulo 		os_free(start_buf);
145e28a4053SRui Paulo 		return NULL;
146e28a4053SRui Paulo 	}
147e28a4053SRui Paulo 	end_len = os_strlen(end_buf);
148e28a4053SRui Paulo 
149e28a4053SRui Paulo 	rlen = start_len + imv_len + end_len;
150e28a4053SRui Paulo 	req = wpabuf_alloc(rlen);
151e28a4053SRui Paulo 	if (req == NULL) {
152e28a4053SRui Paulo 		os_free(start_buf);
153e28a4053SRui Paulo 		os_free(end_buf);
154e28a4053SRui Paulo 		return NULL;
155e28a4053SRui Paulo 	}
156e28a4053SRui Paulo 
157e28a4053SRui Paulo 	wpabuf_put_data(req, start_buf, start_len);
158e28a4053SRui Paulo 	os_free(start_buf);
159e28a4053SRui Paulo 
160e28a4053SRui Paulo 	rpos1 = wpabuf_put(req, 0);
161e28a4053SRui Paulo 	rpos = tncs_copy_send_buf(data->tncs, rpos1);
162e28a4053SRui Paulo 	wpabuf_put(req, rpos - rpos1);
163e28a4053SRui Paulo 
164e28a4053SRui Paulo 	wpabuf_put_data(req, end_buf, end_len);
165e28a4053SRui Paulo 	os_free(end_buf);
166e28a4053SRui Paulo 
167e28a4053SRui Paulo 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
168e28a4053SRui Paulo 			  wpabuf_head(req), wpabuf_len(req));
169e28a4053SRui Paulo 
170e28a4053SRui Paulo 	return req;
171e28a4053SRui Paulo }
172e28a4053SRui Paulo 
173e28a4053SRui Paulo 
eap_tnc_build_recommendation(struct eap_sm * sm,struct eap_tnc_data * data)174e28a4053SRui Paulo static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
175e28a4053SRui Paulo 						    struct eap_tnc_data *data)
176e28a4053SRui Paulo {
177e28a4053SRui Paulo 	switch (data->recommendation) {
178e28a4053SRui Paulo 	case ALLOW:
179e28a4053SRui Paulo 		eap_tnc_set_state(data, DONE);
180e28a4053SRui Paulo 		break;
181e28a4053SRui Paulo 	case ISOLATE:
182e28a4053SRui Paulo 		eap_tnc_set_state(data, FAIL);
183e28a4053SRui Paulo 		/* TODO: support assignment to a different VLAN */
184e28a4053SRui Paulo 		break;
185e28a4053SRui Paulo 	case NO_ACCESS:
186e28a4053SRui Paulo 		eap_tnc_set_state(data, FAIL);
187e28a4053SRui Paulo 		break;
188e28a4053SRui Paulo 	case NO_RECOMMENDATION:
189e28a4053SRui Paulo 		eap_tnc_set_state(data, DONE);
190e28a4053SRui Paulo 		break;
191e28a4053SRui Paulo 	default:
192e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
193e28a4053SRui Paulo 		return NULL;
194e28a4053SRui Paulo 	}
195e28a4053SRui Paulo 
196e28a4053SRui Paulo 	return eap_tnc_build(sm, data);
197e28a4053SRui Paulo }
198e28a4053SRui Paulo 
199e28a4053SRui Paulo 
eap_tnc_build_frag_ack(u8 id,u8 code)200e28a4053SRui Paulo static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
201e28a4053SRui Paulo {
202e28a4053SRui Paulo 	struct wpabuf *msg;
203e28a4053SRui Paulo 
204e28a4053SRui Paulo 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
205e28a4053SRui Paulo 	if (msg == NULL) {
206e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
207e28a4053SRui Paulo 			   "for fragment ack");
208e28a4053SRui Paulo 		return NULL;
209e28a4053SRui Paulo 	}
210e28a4053SRui Paulo 	wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
211e28a4053SRui Paulo 
212e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
213e28a4053SRui Paulo 
214e28a4053SRui Paulo 	return msg;
215e28a4053SRui Paulo }
216e28a4053SRui Paulo 
217e28a4053SRui Paulo 
eap_tnc_build_msg(struct eap_tnc_data * data,u8 id)218e28a4053SRui Paulo static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
219e28a4053SRui Paulo {
220e28a4053SRui Paulo 	struct wpabuf *req;
221e28a4053SRui Paulo 	u8 flags;
222e28a4053SRui Paulo 	size_t send_len, plen;
223e28a4053SRui Paulo 
224e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
225e28a4053SRui Paulo 
226e28a4053SRui Paulo 	flags = EAP_TNC_VERSION;
227e28a4053SRui Paulo 	send_len = wpabuf_len(data->out_buf) - data->out_used;
228e28a4053SRui Paulo 	if (1 + send_len > data->fragment_size) {
229e28a4053SRui Paulo 		send_len = data->fragment_size - 1;
230e28a4053SRui Paulo 		flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
231e28a4053SRui Paulo 		if (data->out_used == 0) {
232e28a4053SRui Paulo 			flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
233e28a4053SRui Paulo 			send_len -= 4;
234e28a4053SRui Paulo 		}
235e28a4053SRui Paulo 	}
236e28a4053SRui Paulo 
237e28a4053SRui Paulo 	plen = 1 + send_len;
238e28a4053SRui Paulo 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
239e28a4053SRui Paulo 		plen += 4;
240e28a4053SRui Paulo 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
241e28a4053SRui Paulo 			    EAP_CODE_REQUEST, id);
242e28a4053SRui Paulo 	if (req == NULL)
243e28a4053SRui Paulo 		return NULL;
244e28a4053SRui Paulo 
245e28a4053SRui Paulo 	wpabuf_put_u8(req, flags); /* Flags */
246e28a4053SRui Paulo 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
247e28a4053SRui Paulo 		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
248e28a4053SRui Paulo 
249e28a4053SRui Paulo 	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
250e28a4053SRui Paulo 			send_len);
251e28a4053SRui Paulo 	data->out_used += send_len;
252e28a4053SRui Paulo 
253e28a4053SRui Paulo 	if (data->out_used == wpabuf_len(data->out_buf)) {
254e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
255e28a4053SRui Paulo 			   "(message sent completely)",
256e28a4053SRui Paulo 			   (unsigned long) send_len);
257e28a4053SRui Paulo 		wpabuf_free(data->out_buf);
258e28a4053SRui Paulo 		data->out_buf = NULL;
259e28a4053SRui Paulo 		data->out_used = 0;
260e28a4053SRui Paulo 		if (data->was_fail)
261e28a4053SRui Paulo 			eap_tnc_set_state(data, FAIL);
262e28a4053SRui Paulo 		else if (data->was_done)
263e28a4053SRui Paulo 			eap_tnc_set_state(data, DONE);
264e28a4053SRui Paulo 	} else {
265e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
266e28a4053SRui Paulo 			   "(%lu more to send)", (unsigned long) send_len,
267e28a4053SRui Paulo 			   (unsigned long) wpabuf_len(data->out_buf) -
268e28a4053SRui Paulo 			   data->out_used);
269e28a4053SRui Paulo 		if (data->state == FAIL)
270e28a4053SRui Paulo 			data->was_fail = 1;
271e28a4053SRui Paulo 		else if (data->state == DONE)
272e28a4053SRui Paulo 			data->was_done = 1;
273e28a4053SRui Paulo 		eap_tnc_set_state(data, WAIT_FRAG_ACK);
274e28a4053SRui Paulo 	}
275e28a4053SRui Paulo 
276e28a4053SRui Paulo 	return req;
277e28a4053SRui Paulo }
278e28a4053SRui Paulo 
279e28a4053SRui Paulo 
eap_tnc_buildReq(struct eap_sm * sm,void * priv,u8 id)280e28a4053SRui Paulo static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
281e28a4053SRui Paulo {
282e28a4053SRui Paulo 	struct eap_tnc_data *data = priv;
283e28a4053SRui Paulo 
284e28a4053SRui Paulo 	switch (data->state) {
285e28a4053SRui Paulo 	case START:
286e28a4053SRui Paulo 		tncs_init_connection(data->tncs);
287e28a4053SRui Paulo 		return eap_tnc_build_start(sm, data, id);
288e28a4053SRui Paulo 	case CONTINUE:
289e28a4053SRui Paulo 		if (data->out_buf == NULL) {
290e28a4053SRui Paulo 			data->out_buf = eap_tnc_build(sm, data);
291e28a4053SRui Paulo 			if (data->out_buf == NULL) {
292e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
293e28a4053SRui Paulo 					   "generate message");
294e28a4053SRui Paulo 				return NULL;
295e28a4053SRui Paulo 			}
296e28a4053SRui Paulo 			data->out_used = 0;
297e28a4053SRui Paulo 		}
298e28a4053SRui Paulo 		return eap_tnc_build_msg(data, id);
299e28a4053SRui Paulo 	case RECOMMENDATION:
300e28a4053SRui Paulo 		if (data->out_buf == NULL) {
301e28a4053SRui Paulo 			data->out_buf = eap_tnc_build_recommendation(sm, data);
302e28a4053SRui Paulo 			if (data->out_buf == NULL) {
303e28a4053SRui Paulo 				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
304e28a4053SRui Paulo 					   "generate recommendation message");
305e28a4053SRui Paulo 				return NULL;
306e28a4053SRui Paulo 			}
307e28a4053SRui Paulo 			data->out_used = 0;
308e28a4053SRui Paulo 		}
309e28a4053SRui Paulo 		return eap_tnc_build_msg(data, id);
310e28a4053SRui Paulo 	case WAIT_FRAG_ACK:
311e28a4053SRui Paulo 		return eap_tnc_build_msg(data, id);
312e28a4053SRui Paulo 	case FRAG_ACK:
313e28a4053SRui Paulo 		return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
314e28a4053SRui Paulo 	case DONE:
315e28a4053SRui Paulo 	case FAIL:
316e28a4053SRui Paulo 		return NULL;
317e28a4053SRui Paulo 	}
318e28a4053SRui Paulo 
319e28a4053SRui Paulo 	return NULL;
320e28a4053SRui Paulo }
321e28a4053SRui Paulo 
322e28a4053SRui Paulo 
eap_tnc_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)323*c1d255d3SCy Schubert static bool eap_tnc_check(struct eap_sm *sm, void *priv,
324e28a4053SRui Paulo 			  struct wpabuf *respData)
325e28a4053SRui Paulo {
326e28a4053SRui Paulo 	struct eap_tnc_data *data = priv;
327e28a4053SRui Paulo 	const u8 *pos;
328e28a4053SRui Paulo 	size_t len;
329e28a4053SRui Paulo 
330e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
331e28a4053SRui Paulo 			       &len);
332e28a4053SRui Paulo 	if (pos == NULL) {
333e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
334*c1d255d3SCy Schubert 		return true;
335e28a4053SRui Paulo 	}
336e28a4053SRui Paulo 
337e28a4053SRui Paulo 	if (len == 0 && data->state != WAIT_FRAG_ACK) {
338e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
339*c1d255d3SCy Schubert 		return true;
340e28a4053SRui Paulo 	}
341e28a4053SRui Paulo 
342e28a4053SRui Paulo 	if (len == 0)
343*c1d255d3SCy Schubert 		return false; /* Fragment ACK does not include flags */
344e28a4053SRui Paulo 
345e28a4053SRui Paulo 	if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
346e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
347e28a4053SRui Paulo 			   *pos & EAP_TNC_VERSION_MASK);
348*c1d255d3SCy Schubert 		return true;
349e28a4053SRui Paulo 	}
350e28a4053SRui Paulo 
351e28a4053SRui Paulo 	if (*pos & EAP_TNC_FLAGS_START) {
352e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
353*c1d255d3SCy Schubert 		return true;
354e28a4053SRui Paulo 	}
355e28a4053SRui Paulo 
356*c1d255d3SCy Schubert 	return false;
357e28a4053SRui Paulo }
358e28a4053SRui Paulo 
359e28a4053SRui Paulo 
tncs_process(struct eap_tnc_data * data,struct wpabuf * inbuf)360e28a4053SRui Paulo static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
361e28a4053SRui Paulo {
362e28a4053SRui Paulo 	enum tncs_process_res res;
363e28a4053SRui Paulo 
364e28a4053SRui Paulo 	res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
365e28a4053SRui Paulo 				    wpabuf_len(inbuf));
366e28a4053SRui Paulo 	switch (res) {
367e28a4053SRui Paulo 	case TNCCS_RECOMMENDATION_ALLOW:
368e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
369e28a4053SRui Paulo 		eap_tnc_set_state(data, RECOMMENDATION);
370e28a4053SRui Paulo 		data->recommendation = ALLOW;
371e28a4053SRui Paulo 		break;
372e28a4053SRui Paulo 	case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
373e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
374e28a4053SRui Paulo 		eap_tnc_set_state(data, RECOMMENDATION);
375e28a4053SRui Paulo 		data->recommendation = NO_RECOMMENDATION;
376e28a4053SRui Paulo 		break;
377e28a4053SRui Paulo 	case TNCCS_RECOMMENDATION_ISOLATE:
378e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
379e28a4053SRui Paulo 		eap_tnc_set_state(data, RECOMMENDATION);
380e28a4053SRui Paulo 		data->recommendation = ISOLATE;
381e28a4053SRui Paulo 		break;
382e28a4053SRui Paulo 	case TNCCS_RECOMMENDATION_NO_ACCESS:
383e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
384e28a4053SRui Paulo 		eap_tnc_set_state(data, RECOMMENDATION);
385e28a4053SRui Paulo 		data->recommendation = NO_ACCESS;
386e28a4053SRui Paulo 		break;
387e28a4053SRui Paulo 	case TNCCS_PROCESS_ERROR:
388e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
389e28a4053SRui Paulo 		eap_tnc_set_state(data, FAIL);
390e28a4053SRui Paulo 		break;
391e28a4053SRui Paulo 	default:
392e28a4053SRui Paulo 		break;
393e28a4053SRui Paulo 	}
394e28a4053SRui Paulo }
395e28a4053SRui Paulo 
396e28a4053SRui Paulo 
eap_tnc_process_cont(struct eap_tnc_data * data,const u8 * buf,size_t len)397e28a4053SRui Paulo static int eap_tnc_process_cont(struct eap_tnc_data *data,
398e28a4053SRui Paulo 				const u8 *buf, size_t len)
399e28a4053SRui Paulo {
400e28a4053SRui Paulo 	/* Process continuation of a pending message */
401e28a4053SRui Paulo 	if (len > wpabuf_tailroom(data->in_buf)) {
402e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
403e28a4053SRui Paulo 		eap_tnc_set_state(data, FAIL);
404e28a4053SRui Paulo 		return -1;
405e28a4053SRui Paulo 	}
406e28a4053SRui Paulo 
407e28a4053SRui Paulo 	wpabuf_put_data(data->in_buf, buf, len);
408e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
409e28a4053SRui Paulo 		   "bytes more", (unsigned long) len,
410e28a4053SRui Paulo 		   (unsigned long) wpabuf_tailroom(data->in_buf));
411e28a4053SRui Paulo 
412e28a4053SRui Paulo 	return 0;
413e28a4053SRui Paulo }
414e28a4053SRui Paulo 
415e28a4053SRui Paulo 
eap_tnc_process_fragment(struct eap_tnc_data * data,u8 flags,u32 message_length,const u8 * buf,size_t len)416e28a4053SRui Paulo static int eap_tnc_process_fragment(struct eap_tnc_data *data,
417e28a4053SRui Paulo 				    u8 flags, u32 message_length,
418e28a4053SRui Paulo 				    const u8 *buf, size_t len)
419e28a4053SRui Paulo {
420e28a4053SRui Paulo 	/* Process a fragment that is not the last one of the message */
421e28a4053SRui Paulo 	if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
422e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
423e28a4053SRui Paulo 			   "fragmented packet");
424e28a4053SRui Paulo 		return -1;
425e28a4053SRui Paulo 	}
426e28a4053SRui Paulo 
427e28a4053SRui Paulo 	if (data->in_buf == NULL) {
428e28a4053SRui Paulo 		/* First fragment of the message */
429e28a4053SRui Paulo 		data->in_buf = wpabuf_alloc(message_length);
430e28a4053SRui Paulo 		if (data->in_buf == NULL) {
431e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
432e28a4053SRui Paulo 				   "message");
433e28a4053SRui Paulo 			return -1;
434e28a4053SRui Paulo 		}
435e28a4053SRui Paulo 		wpabuf_put_data(data->in_buf, buf, len);
436e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
437e28a4053SRui Paulo 			   "fragment, waiting for %lu bytes more",
438e28a4053SRui Paulo 			   (unsigned long) len,
439e28a4053SRui Paulo 			   (unsigned long) wpabuf_tailroom(data->in_buf));
440e28a4053SRui Paulo 	}
441e28a4053SRui Paulo 
442e28a4053SRui Paulo 	return 0;
443e28a4053SRui Paulo }
444e28a4053SRui Paulo 
445e28a4053SRui Paulo 
eap_tnc_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)446e28a4053SRui Paulo static void eap_tnc_process(struct eap_sm *sm, void *priv,
447e28a4053SRui Paulo 			    struct wpabuf *respData)
448e28a4053SRui Paulo {
449e28a4053SRui Paulo 	struct eap_tnc_data *data = priv;
450e28a4053SRui Paulo 	const u8 *pos, *end;
451e28a4053SRui Paulo 	size_t len;
452e28a4053SRui Paulo 	u8 flags;
453e28a4053SRui Paulo 	u32 message_length = 0;
454e28a4053SRui Paulo 	struct wpabuf tmpbuf;
455e28a4053SRui Paulo 
456e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
457e28a4053SRui Paulo 	if (pos == NULL)
458e28a4053SRui Paulo 		return; /* Should not happen; message already verified */
459e28a4053SRui Paulo 
460e28a4053SRui Paulo 	end = pos + len;
461e28a4053SRui Paulo 
462e28a4053SRui Paulo 	if (len == 1 && (data->state == DONE || data->state == FAIL)) {
463e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
464e28a4053SRui Paulo 			   "message");
465e28a4053SRui Paulo 		return;
466e28a4053SRui Paulo 	}
467e28a4053SRui Paulo 
468e28a4053SRui Paulo 	if (len == 0) {
469e28a4053SRui Paulo 		/* fragment ack */
470e28a4053SRui Paulo 		flags = 0;
471e28a4053SRui Paulo 	} else
472e28a4053SRui Paulo 		flags = *pos++;
473e28a4053SRui Paulo 
474e28a4053SRui Paulo 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
475e28a4053SRui Paulo 		if (end - pos < 4) {
476e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
477e28a4053SRui Paulo 			eap_tnc_set_state(data, FAIL);
478e28a4053SRui Paulo 			return;
479e28a4053SRui Paulo 		}
480e28a4053SRui Paulo 		message_length = WPA_GET_BE32(pos);
481e28a4053SRui Paulo 		pos += 4;
482e28a4053SRui Paulo 
4835b9c547cSRui Paulo 		if (message_length < (u32) (end - pos) ||
4845b9c547cSRui Paulo 		    message_length > 75000) {
485e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
486e28a4053SRui Paulo 				   "Length (%d; %ld remaining in this msg)",
487e28a4053SRui Paulo 				   message_length, (long) (end - pos));
488e28a4053SRui Paulo 			eap_tnc_set_state(data, FAIL);
489e28a4053SRui Paulo 			return;
490e28a4053SRui Paulo 		}
491e28a4053SRui Paulo 	}
492e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
493e28a4053SRui Paulo 		   "Message Length %u", flags, message_length);
494e28a4053SRui Paulo 
495e28a4053SRui Paulo 	if (data->state == WAIT_FRAG_ACK) {
496e28a4053SRui Paulo 		if (len > 1) {
497e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
498e28a4053SRui Paulo 				   "in WAIT_FRAG_ACK state");
499e28a4053SRui Paulo 			eap_tnc_set_state(data, FAIL);
500e28a4053SRui Paulo 			return;
501e28a4053SRui Paulo 		}
502e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
503e28a4053SRui Paulo 		eap_tnc_set_state(data, CONTINUE);
504e28a4053SRui Paulo 		return;
505e28a4053SRui Paulo 	}
506e28a4053SRui Paulo 
507e28a4053SRui Paulo 	if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
508e28a4053SRui Paulo 		eap_tnc_set_state(data, FAIL);
509e28a4053SRui Paulo 		return;
510e28a4053SRui Paulo 	}
511e28a4053SRui Paulo 
512e28a4053SRui Paulo 	if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
513e28a4053SRui Paulo 		if (eap_tnc_process_fragment(data, flags, message_length,
514e28a4053SRui Paulo 					     pos, end - pos) < 0)
515e28a4053SRui Paulo 			eap_tnc_set_state(data, FAIL);
516e28a4053SRui Paulo 		else
517e28a4053SRui Paulo 			eap_tnc_set_state(data, FRAG_ACK);
518e28a4053SRui Paulo 		return;
519e28a4053SRui Paulo 	} else if (data->state == FRAG_ACK) {
520e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
521e28a4053SRui Paulo 		eap_tnc_set_state(data, CONTINUE);
522e28a4053SRui Paulo 	}
523e28a4053SRui Paulo 
524e28a4053SRui Paulo 	if (data->in_buf == NULL) {
525e28a4053SRui Paulo 		/* Wrap unfragmented messages as wpabuf without extra copy */
526e28a4053SRui Paulo 		wpabuf_set(&tmpbuf, pos, end - pos);
527e28a4053SRui Paulo 		data->in_buf = &tmpbuf;
528e28a4053SRui Paulo 	}
529e28a4053SRui Paulo 
530e28a4053SRui Paulo 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
531e28a4053SRui Paulo 			  wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
532e28a4053SRui Paulo 	tncs_process(data, data->in_buf);
533e28a4053SRui Paulo 
534e28a4053SRui Paulo 	if (data->in_buf != &tmpbuf)
535e28a4053SRui Paulo 		wpabuf_free(data->in_buf);
536e28a4053SRui Paulo 	data->in_buf = NULL;
537e28a4053SRui Paulo }
538e28a4053SRui Paulo 
539e28a4053SRui Paulo 
eap_tnc_isDone(struct eap_sm * sm,void * priv)540*c1d255d3SCy Schubert static bool eap_tnc_isDone(struct eap_sm *sm, void *priv)
541e28a4053SRui Paulo {
542e28a4053SRui Paulo 	struct eap_tnc_data *data = priv;
543e28a4053SRui Paulo 	return data->state == DONE || data->state == FAIL;
544e28a4053SRui Paulo }
545e28a4053SRui Paulo 
546e28a4053SRui Paulo 
eap_tnc_isSuccess(struct eap_sm * sm,void * priv)547*c1d255d3SCy Schubert static bool eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
548e28a4053SRui Paulo {
549e28a4053SRui Paulo 	struct eap_tnc_data *data = priv;
550e28a4053SRui Paulo 	return data->state == DONE;
551e28a4053SRui Paulo }
552e28a4053SRui Paulo 
553e28a4053SRui Paulo 
eap_server_tnc_register(void)554e28a4053SRui Paulo int eap_server_tnc_register(void)
555e28a4053SRui Paulo {
556e28a4053SRui Paulo 	struct eap_method *eap;
557e28a4053SRui Paulo 
558e28a4053SRui Paulo 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
559e28a4053SRui Paulo 				      EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
560e28a4053SRui Paulo 	if (eap == NULL)
561e28a4053SRui Paulo 		return -1;
562e28a4053SRui Paulo 
563e28a4053SRui Paulo 	eap->init = eap_tnc_init;
564e28a4053SRui Paulo 	eap->reset = eap_tnc_reset;
565e28a4053SRui Paulo 	eap->buildReq = eap_tnc_buildReq;
566e28a4053SRui Paulo 	eap->check = eap_tnc_check;
567e28a4053SRui Paulo 	eap->process = eap_tnc_process;
568e28a4053SRui Paulo 	eap->isDone = eap_tnc_isDone;
569e28a4053SRui Paulo 	eap->isSuccess = eap_tnc_isSuccess;
570e28a4053SRui Paulo 
571780fb4a2SCy Schubert 	return eap_server_method_register(eap);
572e28a4053SRui Paulo }
573