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