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