2 * EAP server method: EAP-TNC (Trusted Network Connect)
3 * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
25 START
, CONTINUE
, RECOMMENDATION
, FRAG_ACK
, WAIT_FRAG_ACK
, DONE
,
28 enum { ALLOW
, ISOLATE
, NO_ACCESS
, NO_RECOMMENDATION
} recommendation
;
29 struct tncs_data
*tncs
;
30 struct wpabuf
*in_buf
;
31 struct wpabuf
*out_buf
;
34 unsigned int was_done
:1;
35 unsigned int was_fail
:1;
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
45 #define EAP_TNC_VERSION 1
48 static const char * eap_tnc_state_txt(enum eap_tnc_state state
)
56 return "RECOMMENDATION";
60 return "WAIT_FRAG_ACK";
70 static void eap_tnc_set_state(struct eap_tnc_data
*data
,
71 enum eap_tnc_state new_state
)
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
;
80 static void * eap_tnc_init(struct eap_sm
*sm
)
82 struct eap_tnc_data
*data
;
84 data
= os_zalloc(sizeof(*data
));
87 eap_tnc_set_state(data
, START
);
88 data
->tncs
= tncs_init();
89 if (data
->tncs
== NULL
) {
94 data
->fragment_size
= 1300;
100 static void eap_tnc_reset(struct eap_sm
*sm
, void *priv
)
102 struct eap_tnc_data
*data
= priv
;
103 wpabuf_free(data
->in_buf
);
104 wpabuf_free(data
->out_buf
);
105 tncs_deinit(data
->tncs
);
110 static struct wpabuf
* eap_tnc_build_start(struct eap_sm
*sm
,
111 struct eap_tnc_data
*data
, u8 id
)
115 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, 1, EAP_CODE_REQUEST
,
118 wpa_printf(MSG_ERROR
, "EAP-TNC: Failed to allocate memory for "
120 eap_tnc_set_state(data
, FAIL
);
124 wpabuf_put_u8(req
, EAP_TNC_FLAGS_START
| EAP_TNC_VERSION
);
126 eap_tnc_set_state(data
, CONTINUE
);
132 static struct wpabuf
* eap_tnc_build(struct eap_sm
*sm
,
133 struct eap_tnc_data
*data
)
138 char *start_buf
, *end_buf
;
139 size_t start_len
, end_len
;
142 imv_len
= tncs_total_send_len(data
->tncs
);
144 start_buf
= tncs_if_tnccs_start(data
->tncs
);
145 if (start_buf
== NULL
)
147 start_len
= os_strlen(start_buf
);
148 end_buf
= tncs_if_tnccs_end();
149 if (end_buf
== NULL
) {
153 end_len
= os_strlen(end_buf
);
155 rlen
= start_len
+ imv_len
+ end_len
;
156 req
= wpabuf_alloc(rlen
);
163 wpabuf_put_data(req
, start_buf
, start_len
);
166 rpos1
= wpabuf_put(req
, 0);
167 rpos
= tncs_copy_send_buf(data
->tncs
, rpos1
);
168 wpabuf_put(req
, rpos
- rpos1
);
170 wpabuf_put_data(req
, end_buf
, end_len
);
173 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-TNC: Request",
174 wpabuf_head(req
), wpabuf_len(req
));
180 static struct wpabuf
* eap_tnc_build_recommendation(struct eap_sm
*sm
,
181 struct eap_tnc_data
*data
)
183 switch (data
->recommendation
) {
185 eap_tnc_set_state(data
, DONE
);
188 eap_tnc_set_state(data
, FAIL
);
189 /* TODO: support assignment to a different VLAN */
192 eap_tnc_set_state(data
, FAIL
);
194 case NO_RECOMMENDATION
:
195 eap_tnc_set_state(data
, DONE
);
198 wpa_printf(MSG_DEBUG
, "EAP-TNC: Unknown recommendation");
202 return eap_tnc_build(sm
, data
);
206 static struct wpabuf
* eap_tnc_build_frag_ack(u8 id
, u8 code
)
210 msg
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, 1, code
, id
);
212 wpa_printf(MSG_ERROR
, "EAP-TNC: Failed to allocate memory "
216 wpabuf_put_u8(msg
, EAP_TNC_VERSION
); /* Flags */
218 wpa_printf(MSG_DEBUG
, "EAP-TNC: Send fragment ack");
224 static struct wpabuf
* eap_tnc_build_msg(struct eap_tnc_data
*data
, u8 id
)
228 size_t send_len
, plen
;
230 wpa_printf(MSG_DEBUG
, "EAP-TNC: Generating Request");
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
;
244 if (flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
)
246 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, plen
,
247 EAP_CODE_REQUEST
, id
);
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
));
255 wpabuf_put_data(req
, wpabuf_head_u8(data
->out_buf
) + data
->out_used
,
257 data
->out_used
+= send_len
;
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
;
267 eap_tnc_set_state(data
, FAIL
);
268 else if (data
->was_done
)
269 eap_tnc_set_state(data
, DONE
);
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
) -
275 if (data
->state
== FAIL
)
277 else if (data
->state
== DONE
)
279 eap_tnc_set_state(data
, WAIT_FRAG_ACK
);
286 static struct wpabuf
* eap_tnc_buildReq(struct eap_sm
*sm
, void *priv
, u8 id
)
288 struct eap_tnc_data
*data
= priv
;
290 switch (data
->state
) {
292 tncs_init_connection(data
->tncs
);
293 return eap_tnc_build_start(sm
, data
, id
);
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 "
304 return eap_tnc_build_msg(data
, id
);
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");
315 return eap_tnc_build_msg(data
, id
);
317 return eap_tnc_build_msg(data
, id
);
319 return eap_tnc_build_frag_ack(id
, EAP_CODE_REQUEST
);
329 static Boolean
eap_tnc_check(struct eap_sm
*sm
, void *priv
,
330 struct wpabuf
*respData
)
332 struct eap_tnc_data
*data
= priv
;
336 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, respData
,
339 wpa_printf(MSG_INFO
, "EAP-TNC: Invalid frame");
343 if (len
== 0 && data
->state
!= WAIT_FRAG_ACK
) {
344 wpa_printf(MSG_INFO
, "EAP-TNC: Invalid frame (empty)");
349 return FALSE
; /* Fragment ACK does not include flags */
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
);
357 if (*pos
& EAP_TNC_FLAGS_START
) {
358 wpa_printf(MSG_DEBUG
, "EAP-TNC: Peer used Start flag");
366 static void tncs_process(struct eap_tnc_data
*data
, struct wpabuf
*inbuf
)
368 enum tncs_process_res res
;
370 res
= tncs_process_if_tnccs(data
->tncs
, wpabuf_head(inbuf
),
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
;
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
;
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
;
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
;
393 case TNCCS_PROCESS_ERROR
:
394 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS processing error");
395 eap_tnc_set_state(data
, FAIL
);
403 static int eap_tnc_process_cont(struct eap_tnc_data
*data
,
404 const u8
*buf
, size_t len
)
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
);
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
));
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
)
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");
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 "
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",
445 (unsigned long) wpabuf_tailroom(data
->in_buf
));
452 static void eap_tnc_process(struct eap_sm
*sm
, void *priv
,
453 struct wpabuf
*respData
)
455 struct eap_tnc_data
*data
= priv
;
459 u32 message_length
= 0;
460 struct wpabuf tmpbuf
;
462 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, respData
, &len
);
464 return; /* Should not happen; message already verified */
468 if (len
== 1 && (data
->state
== DONE
|| data
->state
== FAIL
)) {
469 wpa_printf(MSG_DEBUG
, "EAP-TNC: Peer acknowledged the last "
480 if (flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
) {
482 wpa_printf(MSG_DEBUG
, "EAP-TNC: Message underflow");
483 eap_tnc_set_state(data
, FAIL
);
486 message_length
= WPA_GET_BE32(pos
);
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
);
497 wpa_printf(MSG_DEBUG
, "EAP-TNC: Received packet: Flags 0x%x "
498 "Message Length %u", flags
, message_length
);
500 if (data
->state
== WAIT_FRAG_ACK
) {
502 wpa_printf(MSG_DEBUG
, "EAP-TNC: Unexpected payload "
503 "in WAIT_FRAG_ACK state");
504 eap_tnc_set_state(data
, FAIL
);
507 wpa_printf(MSG_DEBUG
, "EAP-TNC: Fragment acknowledged");
508 eap_tnc_set_state(data
, CONTINUE
);
512 if (data
->in_buf
&& eap_tnc_process_cont(data
, pos
, end
- pos
) < 0) {
513 eap_tnc_set_state(data
, FAIL
);
517 if (flags
& EAP_TNC_FLAGS_MORE_FRAGMENTS
) {
518 if (eap_tnc_process_fragment(data
, flags
, message_length
,
520 eap_tnc_set_state(data
, FAIL
);
522 eap_tnc_set_state(data
, FRAG_ACK
);
524 } else if (data
->state
== FRAG_ACK
) {
525 wpa_printf(MSG_DEBUG
, "EAP-TNC: All fragments received");
526 eap_tnc_set_state(data
, CONTINUE
);
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
;
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
);
539 if (data
->in_buf
!= &tmpbuf
)
540 wpabuf_free(data
->in_buf
);
545 static Boolean
eap_tnc_isDone(struct eap_sm
*sm
, void *priv
)
547 struct eap_tnc_data
*data
= priv
;
548 return data
->state
== DONE
|| data
->state
== FAIL
;
552 static Boolean
eap_tnc_isSuccess(struct eap_sm
*sm
, void *priv
)
554 struct eap_tnc_data
*data
= priv
;
555 return data
->state
== DONE
;
559 int eap_server_tnc_register(void)
561 struct eap_method
*eap
;
564 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
565 EAP_VENDOR_IETF
, EAP_TYPE_TNC
, "TNC");
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
;
577 ret
= eap_server_method_register(eap
);
579 eap_server_method_free(eap
);