eap_server_tnc.c revision 214501
1214501Srpaulo/*
2214501Srpaulo * EAP server method: EAP-TNC (Trusted Network Connect)
3214501Srpaulo * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5214501Srpaulo * This program is free software; you can redistribute it and/or modify
6214501Srpaulo * it under the terms of the GNU General Public License version 2 as
7214501Srpaulo * published by the Free Software Foundation.
8214501Srpaulo *
9214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD
10214501Srpaulo * license.
11214501Srpaulo *
12214501Srpaulo * See README and COPYING for more details.
13214501Srpaulo */
14214501Srpaulo
15214501Srpaulo#include "includes.h"
16214501Srpaulo
17214501Srpaulo#include "common.h"
18214501Srpaulo#include "base64.h"
19214501Srpaulo#include "eap_i.h"
20214501Srpaulo#include "tncs.h"
21214501Srpaulo
22214501Srpaulo
23214501Srpaulostruct eap_tnc_data {
24214501Srpaulo	enum eap_tnc_state {
25214501Srpaulo		START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
26214501Srpaulo		FAIL
27214501Srpaulo	} state;
28214501Srpaulo	enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
29214501Srpaulo	struct tncs_data *tncs;
30214501Srpaulo	struct wpabuf *in_buf;
31214501Srpaulo	struct wpabuf *out_buf;
32214501Srpaulo	size_t out_used;
33214501Srpaulo	size_t fragment_size;
34214501Srpaulo	unsigned int was_done:1;
35214501Srpaulo	unsigned int was_fail:1;
36214501Srpaulo};
37214501Srpaulo
38214501Srpaulo
39214501Srpaulo/* EAP-TNC Flags */
40214501Srpaulo#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
41214501Srpaulo#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
42214501Srpaulo#define EAP_TNC_FLAGS_START 0x20
43214501Srpaulo#define EAP_TNC_VERSION_MASK 0x07
44214501Srpaulo
45214501Srpaulo#define EAP_TNC_VERSION 1
46214501Srpaulo
47214501Srpaulo
48214501Srpaulostatic const char * eap_tnc_state_txt(enum eap_tnc_state state)
49214501Srpaulo{
50214501Srpaulo	switch (state) {
51214501Srpaulo	case START:
52214501Srpaulo		return "START";
53214501Srpaulo	case CONTINUE:
54214501Srpaulo		return "CONTINUE";
55214501Srpaulo	case RECOMMENDATION:
56214501Srpaulo		return "RECOMMENDATION";
57214501Srpaulo	case FRAG_ACK:
58214501Srpaulo		return "FRAG_ACK";
59214501Srpaulo	case WAIT_FRAG_ACK:
60214501Srpaulo		return "WAIT_FRAG_ACK";
61214501Srpaulo	case DONE:
62214501Srpaulo		return "DONE";
63214501Srpaulo	case FAIL:
64214501Srpaulo		return "FAIL";
65214501Srpaulo	}
66214501Srpaulo	return "??";
67214501Srpaulo}
68214501Srpaulo
69214501Srpaulo
70214501Srpaulostatic void eap_tnc_set_state(struct eap_tnc_data *data,
71214501Srpaulo			      enum eap_tnc_state new_state)
72214501Srpaulo{
73214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
74214501Srpaulo		   eap_tnc_state_txt(data->state),
75214501Srpaulo		   eap_tnc_state_txt(new_state));
76214501Srpaulo	data->state = new_state;
77214501Srpaulo}
78214501Srpaulo
79214501Srpaulo
80214501Srpaulostatic void * eap_tnc_init(struct eap_sm *sm)
81214501Srpaulo{
82214501Srpaulo	struct eap_tnc_data *data;
83214501Srpaulo
84214501Srpaulo	data = os_zalloc(sizeof(*data));
85214501Srpaulo	if (data == NULL)
86214501Srpaulo		return NULL;
87214501Srpaulo	eap_tnc_set_state(data, START);
88214501Srpaulo	data->tncs = tncs_init();
89214501Srpaulo	if (data->tncs == NULL) {
90214501Srpaulo		os_free(data);
91214501Srpaulo		return NULL;
92214501Srpaulo	}
93214501Srpaulo
94214501Srpaulo	data->fragment_size = 1300;
95214501Srpaulo
96214501Srpaulo	return data;
97214501Srpaulo}
98214501Srpaulo
99214501Srpaulo
100214501Srpaulostatic void eap_tnc_reset(struct eap_sm *sm, void *priv)
101214501Srpaulo{
102214501Srpaulo	struct eap_tnc_data *data = priv;
103214501Srpaulo	wpabuf_free(data->in_buf);
104214501Srpaulo	wpabuf_free(data->out_buf);
105214501Srpaulo	tncs_deinit(data->tncs);
106214501Srpaulo	os_free(data);
107214501Srpaulo}
108214501Srpaulo
109214501Srpaulo
110214501Srpaulostatic struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
111214501Srpaulo					   struct eap_tnc_data *data, u8 id)
112214501Srpaulo{
113214501Srpaulo	struct wpabuf *req;
114214501Srpaulo
115214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
116214501Srpaulo			    id);
117214501Srpaulo	if (req == NULL) {
118214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
119214501Srpaulo			   "request");
120214501Srpaulo		eap_tnc_set_state(data, FAIL);
121214501Srpaulo		return NULL;
122214501Srpaulo	}
123214501Srpaulo
124214501Srpaulo	wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
125214501Srpaulo
126214501Srpaulo	eap_tnc_set_state(data, CONTINUE);
127214501Srpaulo
128214501Srpaulo	return req;
129214501Srpaulo}
130214501Srpaulo
131214501Srpaulo
132214501Srpaulostatic struct wpabuf * eap_tnc_build(struct eap_sm *sm,
133214501Srpaulo				     struct eap_tnc_data *data)
134214501Srpaulo{
135214501Srpaulo	struct wpabuf *req;
136214501Srpaulo	u8 *rpos, *rpos1;
137214501Srpaulo	size_t rlen;
138214501Srpaulo	char *start_buf, *end_buf;
139214501Srpaulo	size_t start_len, end_len;
140214501Srpaulo	size_t imv_len;
141214501Srpaulo
142214501Srpaulo	imv_len = tncs_total_send_len(data->tncs);
143214501Srpaulo
144214501Srpaulo	start_buf = tncs_if_tnccs_start(data->tncs);
145214501Srpaulo	if (start_buf == NULL)
146214501Srpaulo		return NULL;
147214501Srpaulo	start_len = os_strlen(start_buf);
148214501Srpaulo	end_buf = tncs_if_tnccs_end();
149214501Srpaulo	if (end_buf == NULL) {
150214501Srpaulo		os_free(start_buf);
151214501Srpaulo		return NULL;
152214501Srpaulo	}
153214501Srpaulo	end_len = os_strlen(end_buf);
154214501Srpaulo
155214501Srpaulo	rlen = start_len + imv_len + end_len;
156214501Srpaulo	req = wpabuf_alloc(rlen);
157214501Srpaulo	if (req == NULL) {
158214501Srpaulo		os_free(start_buf);
159214501Srpaulo		os_free(end_buf);
160214501Srpaulo		return NULL;
161214501Srpaulo	}
162214501Srpaulo
163214501Srpaulo	wpabuf_put_data(req, start_buf, start_len);
164214501Srpaulo	os_free(start_buf);
165214501Srpaulo
166214501Srpaulo	rpos1 = wpabuf_put(req, 0);
167214501Srpaulo	rpos = tncs_copy_send_buf(data->tncs, rpos1);
168214501Srpaulo	wpabuf_put(req, rpos - rpos1);
169214501Srpaulo
170214501Srpaulo	wpabuf_put_data(req, end_buf, end_len);
171214501Srpaulo	os_free(end_buf);
172214501Srpaulo
173214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
174214501Srpaulo			  wpabuf_head(req), wpabuf_len(req));
175214501Srpaulo
176214501Srpaulo	return req;
177214501Srpaulo}
178214501Srpaulo
179214501Srpaulo
180214501Srpaulostatic struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
181214501Srpaulo						    struct eap_tnc_data *data)
182214501Srpaulo{
183214501Srpaulo	switch (data->recommendation) {
184214501Srpaulo	case ALLOW:
185214501Srpaulo		eap_tnc_set_state(data, DONE);
186214501Srpaulo		break;
187214501Srpaulo	case ISOLATE:
188214501Srpaulo		eap_tnc_set_state(data, FAIL);
189214501Srpaulo		/* TODO: support assignment to a different VLAN */
190214501Srpaulo		break;
191214501Srpaulo	case NO_ACCESS:
192214501Srpaulo		eap_tnc_set_state(data, FAIL);
193214501Srpaulo		break;
194214501Srpaulo	case NO_RECOMMENDATION:
195214501Srpaulo		eap_tnc_set_state(data, DONE);
196214501Srpaulo		break;
197214501Srpaulo	default:
198214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
199214501Srpaulo		return NULL;
200214501Srpaulo	}
201214501Srpaulo
202214501Srpaulo	return eap_tnc_build(sm, data);
203214501Srpaulo}
204214501Srpaulo
205214501Srpaulo
206214501Srpaulostatic struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
207214501Srpaulo{
208214501Srpaulo	struct wpabuf *msg;
209214501Srpaulo
210214501Srpaulo	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
211214501Srpaulo	if (msg == NULL) {
212214501Srpaulo		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
213214501Srpaulo			   "for fragment ack");
214214501Srpaulo		return NULL;
215214501Srpaulo	}
216214501Srpaulo	wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
217214501Srpaulo
218214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
219214501Srpaulo
220214501Srpaulo	return msg;
221214501Srpaulo}
222214501Srpaulo
223214501Srpaulo
224214501Srpaulostatic struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
225214501Srpaulo{
226214501Srpaulo	struct wpabuf *req;
227214501Srpaulo	u8 flags;
228214501Srpaulo	size_t send_len, plen;
229214501Srpaulo
230214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
231214501Srpaulo
232214501Srpaulo	flags = EAP_TNC_VERSION;
233214501Srpaulo	send_len = wpabuf_len(data->out_buf) - data->out_used;
234214501Srpaulo	if (1 + send_len > data->fragment_size) {
235214501Srpaulo		send_len = data->fragment_size - 1;
236214501Srpaulo		flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
237214501Srpaulo		if (data->out_used == 0) {
238214501Srpaulo			flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
239214501Srpaulo			send_len -= 4;
240214501Srpaulo		}
241214501Srpaulo	}
242214501Srpaulo
243214501Srpaulo	plen = 1 + send_len;
244214501Srpaulo	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
245214501Srpaulo		plen += 4;
246214501Srpaulo	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
247214501Srpaulo			    EAP_CODE_REQUEST, id);
248214501Srpaulo	if (req == NULL)
249214501Srpaulo		return NULL;
250214501Srpaulo
251214501Srpaulo	wpabuf_put_u8(req, flags); /* Flags */
252214501Srpaulo	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
253214501Srpaulo		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
254214501Srpaulo
255214501Srpaulo	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
256214501Srpaulo			send_len);
257214501Srpaulo	data->out_used += send_len;
258214501Srpaulo
259214501Srpaulo	if (data->out_used == wpabuf_len(data->out_buf)) {
260214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
261214501Srpaulo			   "(message sent completely)",
262214501Srpaulo			   (unsigned long) send_len);
263214501Srpaulo		wpabuf_free(data->out_buf);
264214501Srpaulo		data->out_buf = NULL;
265214501Srpaulo		data->out_used = 0;
266214501Srpaulo		if (data->was_fail)
267214501Srpaulo			eap_tnc_set_state(data, FAIL);
268214501Srpaulo		else if (data->was_done)
269214501Srpaulo			eap_tnc_set_state(data, DONE);
270214501Srpaulo	} else {
271214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
272214501Srpaulo			   "(%lu more to send)", (unsigned long) send_len,
273214501Srpaulo			   (unsigned long) wpabuf_len(data->out_buf) -
274214501Srpaulo			   data->out_used);
275214501Srpaulo		if (data->state == FAIL)
276214501Srpaulo			data->was_fail = 1;
277214501Srpaulo		else if (data->state == DONE)
278214501Srpaulo			data->was_done = 1;
279214501Srpaulo		eap_tnc_set_state(data, WAIT_FRAG_ACK);
280214501Srpaulo	}
281214501Srpaulo
282214501Srpaulo	return req;
283214501Srpaulo}
284214501Srpaulo
285214501Srpaulo
286214501Srpaulostatic struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
287214501Srpaulo{
288214501Srpaulo	struct eap_tnc_data *data = priv;
289214501Srpaulo
290214501Srpaulo	switch (data->state) {
291214501Srpaulo	case START:
292214501Srpaulo		tncs_init_connection(data->tncs);
293214501Srpaulo		return eap_tnc_build_start(sm, data, id);
294214501Srpaulo	case CONTINUE:
295214501Srpaulo		if (data->out_buf == NULL) {
296214501Srpaulo			data->out_buf = eap_tnc_build(sm, data);
297214501Srpaulo			if (data->out_buf == NULL) {
298214501Srpaulo				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
299214501Srpaulo					   "generate message");
300214501Srpaulo				return NULL;
301214501Srpaulo			}
302214501Srpaulo			data->out_used = 0;
303214501Srpaulo		}
304214501Srpaulo		return eap_tnc_build_msg(data, id);
305214501Srpaulo	case RECOMMENDATION:
306214501Srpaulo		if (data->out_buf == NULL) {
307214501Srpaulo			data->out_buf = eap_tnc_build_recommendation(sm, data);
308214501Srpaulo			if (data->out_buf == NULL) {
309214501Srpaulo				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
310214501Srpaulo					   "generate recommendation message");
311214501Srpaulo				return NULL;
312214501Srpaulo			}
313214501Srpaulo			data->out_used = 0;
314214501Srpaulo		}
315214501Srpaulo		return eap_tnc_build_msg(data, id);
316214501Srpaulo	case WAIT_FRAG_ACK:
317214501Srpaulo		return eap_tnc_build_msg(data, id);
318214501Srpaulo	case FRAG_ACK:
319214501Srpaulo		return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
320214501Srpaulo	case DONE:
321214501Srpaulo	case FAIL:
322214501Srpaulo		return NULL;
323214501Srpaulo	}
324214501Srpaulo
325214501Srpaulo	return NULL;
326214501Srpaulo}
327214501Srpaulo
328214501Srpaulo
329214501Srpaulostatic Boolean eap_tnc_check(struct eap_sm *sm, void *priv,
330214501Srpaulo			     struct wpabuf *respData)
331214501Srpaulo{
332214501Srpaulo	struct eap_tnc_data *data = priv;
333214501Srpaulo	const u8 *pos;
334214501Srpaulo	size_t len;
335214501Srpaulo
336214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
337214501Srpaulo			       &len);
338214501Srpaulo	if (pos == NULL) {
339214501Srpaulo		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
340214501Srpaulo		return TRUE;
341214501Srpaulo	}
342214501Srpaulo
343214501Srpaulo	if (len == 0 && data->state != WAIT_FRAG_ACK) {
344214501Srpaulo		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
345214501Srpaulo		return TRUE;
346214501Srpaulo	}
347214501Srpaulo
348214501Srpaulo	if (len == 0)
349214501Srpaulo		return FALSE; /* Fragment ACK does not include flags */
350214501Srpaulo
351214501Srpaulo	if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
352214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
353214501Srpaulo			   *pos & EAP_TNC_VERSION_MASK);
354214501Srpaulo		return TRUE;
355214501Srpaulo	}
356214501Srpaulo
357214501Srpaulo	if (*pos & EAP_TNC_FLAGS_START) {
358214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
359214501Srpaulo		return TRUE;
360214501Srpaulo	}
361214501Srpaulo
362214501Srpaulo	return FALSE;
363214501Srpaulo}
364214501Srpaulo
365214501Srpaulo
366214501Srpaulostatic void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
367214501Srpaulo{
368214501Srpaulo	enum tncs_process_res res;
369214501Srpaulo
370214501Srpaulo	res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
371214501Srpaulo				    wpabuf_len(inbuf));
372214501Srpaulo	switch (res) {
373214501Srpaulo	case TNCCS_RECOMMENDATION_ALLOW:
374214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
375214501Srpaulo		eap_tnc_set_state(data, RECOMMENDATION);
376214501Srpaulo		data->recommendation = ALLOW;
377214501Srpaulo		break;
378214501Srpaulo	case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
379214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
380214501Srpaulo		eap_tnc_set_state(data, RECOMMENDATION);
381214501Srpaulo		data->recommendation = NO_RECOMMENDATION;
382214501Srpaulo		break;
383214501Srpaulo	case TNCCS_RECOMMENDATION_ISOLATE:
384214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
385214501Srpaulo		eap_tnc_set_state(data, RECOMMENDATION);
386214501Srpaulo		data->recommendation = ISOLATE;
387214501Srpaulo		break;
388214501Srpaulo	case TNCCS_RECOMMENDATION_NO_ACCESS:
389214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
390214501Srpaulo		eap_tnc_set_state(data, RECOMMENDATION);
391214501Srpaulo		data->recommendation = NO_ACCESS;
392214501Srpaulo		break;
393214501Srpaulo	case TNCCS_PROCESS_ERROR:
394214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
395214501Srpaulo		eap_tnc_set_state(data, FAIL);
396214501Srpaulo		break;
397214501Srpaulo	default:
398214501Srpaulo		break;
399214501Srpaulo	}
400214501Srpaulo}
401214501Srpaulo
402214501Srpaulo
403214501Srpaulostatic int eap_tnc_process_cont(struct eap_tnc_data *data,
404214501Srpaulo				const u8 *buf, size_t len)
405214501Srpaulo{
406214501Srpaulo	/* Process continuation of a pending message */
407214501Srpaulo	if (len > wpabuf_tailroom(data->in_buf)) {
408214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
409214501Srpaulo		eap_tnc_set_state(data, FAIL);
410214501Srpaulo		return -1;
411214501Srpaulo	}
412214501Srpaulo
413214501Srpaulo	wpabuf_put_data(data->in_buf, buf, len);
414214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
415214501Srpaulo		   "bytes more", (unsigned long) len,
416214501Srpaulo		   (unsigned long) wpabuf_tailroom(data->in_buf));
417214501Srpaulo
418214501Srpaulo	return 0;
419214501Srpaulo}
420214501Srpaulo
421214501Srpaulo
422214501Srpaulostatic int eap_tnc_process_fragment(struct eap_tnc_data *data,
423214501Srpaulo				    u8 flags, u32 message_length,
424214501Srpaulo				    const u8 *buf, size_t len)
425214501Srpaulo{
426214501Srpaulo	/* Process a fragment that is not the last one of the message */
427214501Srpaulo	if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
428214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
429214501Srpaulo			   "fragmented packet");
430214501Srpaulo		return -1;
431214501Srpaulo	}
432214501Srpaulo
433214501Srpaulo	if (data->in_buf == NULL) {
434214501Srpaulo		/* First fragment of the message */
435214501Srpaulo		data->in_buf = wpabuf_alloc(message_length);
436214501Srpaulo		if (data->in_buf == NULL) {
437214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
438214501Srpaulo				   "message");
439214501Srpaulo			return -1;
440214501Srpaulo		}
441214501Srpaulo		wpabuf_put_data(data->in_buf, buf, len);
442214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
443214501Srpaulo			   "fragment, waiting for %lu bytes more",
444214501Srpaulo			   (unsigned long) len,
445214501Srpaulo			   (unsigned long) wpabuf_tailroom(data->in_buf));
446214501Srpaulo	}
447214501Srpaulo
448214501Srpaulo	return 0;
449214501Srpaulo}
450214501Srpaulo
451214501Srpaulo
452214501Srpaulostatic void eap_tnc_process(struct eap_sm *sm, void *priv,
453214501Srpaulo			    struct wpabuf *respData)
454214501Srpaulo{
455214501Srpaulo	struct eap_tnc_data *data = priv;
456214501Srpaulo	const u8 *pos, *end;
457214501Srpaulo	size_t len;
458214501Srpaulo	u8 flags;
459214501Srpaulo	u32 message_length = 0;
460214501Srpaulo	struct wpabuf tmpbuf;
461214501Srpaulo
462214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
463214501Srpaulo	if (pos == NULL)
464214501Srpaulo		return; /* Should not happen; message already verified */
465214501Srpaulo
466214501Srpaulo	end = pos + len;
467214501Srpaulo
468214501Srpaulo	if (len == 1 && (data->state == DONE || data->state == FAIL)) {
469214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
470214501Srpaulo			   "message");
471214501Srpaulo		return;
472214501Srpaulo	}
473214501Srpaulo
474214501Srpaulo	if (len == 0) {
475214501Srpaulo		/* fragment ack */
476214501Srpaulo		flags = 0;
477214501Srpaulo	} else
478214501Srpaulo		flags = *pos++;
479214501Srpaulo
480214501Srpaulo	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
481214501Srpaulo		if (end - pos < 4) {
482214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
483214501Srpaulo			eap_tnc_set_state(data, FAIL);
484214501Srpaulo			return;
485214501Srpaulo		}
486214501Srpaulo		message_length = WPA_GET_BE32(pos);
487214501Srpaulo		pos += 4;
488214501Srpaulo
489214501Srpaulo		if (message_length < (u32) (end - pos)) {
490214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
491214501Srpaulo				   "Length (%d; %ld remaining in this msg)",
492214501Srpaulo				   message_length, (long) (end - pos));
493214501Srpaulo			eap_tnc_set_state(data, FAIL);
494214501Srpaulo			return;
495214501Srpaulo		}
496214501Srpaulo	}
497214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
498214501Srpaulo		   "Message Length %u", flags, message_length);
499214501Srpaulo
500214501Srpaulo	if (data->state == WAIT_FRAG_ACK) {
501214501Srpaulo		if (len > 1) {
502214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
503214501Srpaulo				   "in WAIT_FRAG_ACK state");
504214501Srpaulo			eap_tnc_set_state(data, FAIL);
505214501Srpaulo			return;
506214501Srpaulo		}
507214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
508214501Srpaulo		eap_tnc_set_state(data, CONTINUE);
509214501Srpaulo		return;
510214501Srpaulo	}
511214501Srpaulo
512214501Srpaulo	if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
513214501Srpaulo		eap_tnc_set_state(data, FAIL);
514214501Srpaulo		return;
515214501Srpaulo	}
516214501Srpaulo
517214501Srpaulo	if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
518214501Srpaulo		if (eap_tnc_process_fragment(data, flags, message_length,
519214501Srpaulo					     pos, end - pos) < 0)
520214501Srpaulo			eap_tnc_set_state(data, FAIL);
521214501Srpaulo		else
522214501Srpaulo			eap_tnc_set_state(data, FRAG_ACK);
523214501Srpaulo		return;
524214501Srpaulo	} else if (data->state == FRAG_ACK) {
525214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
526214501Srpaulo		eap_tnc_set_state(data, CONTINUE);
527214501Srpaulo	}
528214501Srpaulo
529214501Srpaulo	if (data->in_buf == NULL) {
530214501Srpaulo		/* Wrap unfragmented messages as wpabuf without extra copy */
531214501Srpaulo		wpabuf_set(&tmpbuf, pos, end - pos);
532214501Srpaulo		data->in_buf = &tmpbuf;
533214501Srpaulo	}
534214501Srpaulo
535214501Srpaulo	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
536214501Srpaulo			  wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
537214501Srpaulo	tncs_process(data, data->in_buf);
538214501Srpaulo
539214501Srpaulo	if (data->in_buf != &tmpbuf)
540214501Srpaulo		wpabuf_free(data->in_buf);
541214501Srpaulo	data->in_buf = NULL;
542214501Srpaulo}
543214501Srpaulo
544214501Srpaulo
545214501Srpaulostatic Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv)
546214501Srpaulo{
547214501Srpaulo	struct eap_tnc_data *data = priv;
548214501Srpaulo	return data->state == DONE || data->state == FAIL;
549214501Srpaulo}
550214501Srpaulo
551214501Srpaulo
552214501Srpaulostatic Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
553214501Srpaulo{
554214501Srpaulo	struct eap_tnc_data *data = priv;
555214501Srpaulo	return data->state == DONE;
556214501Srpaulo}
557214501Srpaulo
558214501Srpaulo
559214501Srpauloint eap_server_tnc_register(void)
560214501Srpaulo{
561214501Srpaulo	struct eap_method *eap;
562214501Srpaulo	int ret;
563214501Srpaulo
564214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
565214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
566214501Srpaulo	if (eap == NULL)
567214501Srpaulo		return -1;
568214501Srpaulo
569214501Srpaulo	eap->init = eap_tnc_init;
570214501Srpaulo	eap->reset = eap_tnc_reset;
571214501Srpaulo	eap->buildReq = eap_tnc_buildReq;
572214501Srpaulo	eap->check = eap_tnc_check;
573214501Srpaulo	eap->process = eap_tnc_process;
574214501Srpaulo	eap->isDone = eap_tnc_isDone;
575214501Srpaulo	eap->isSuccess = eap_tnc_isSuccess;
576214501Srpaulo
577214501Srpaulo	ret = eap_server_method_register(eap);
578214501Srpaulo	if (ret)
579214501Srpaulo		eap_server_method_free(eap);
580214501Srpaulo	return ret;
581214501Srpaulo}
582