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