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