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