2 * EAP server method: EAP-TNC (Trusted Network Connect)
3 * Copyright (c) 2007-2008, 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.
24 enum { START
, CONTINUE
, RECOMMENDATION
, FRAG_ACK
, WAIT_FRAG_ACK
, DONE
,
26 enum { ALLOW
, ISOLATE
, NO_ACCESS
, NO_RECOMMENDATION
} recommendation
;
27 struct tncs_data
*tncs
;
28 struct wpabuf
*in_buf
;
29 struct wpabuf
*out_buf
;
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
41 #define EAP_TNC_VERSION 1
44 static void * eap_tnc_init(struct eap_sm
*sm
)
46 struct eap_tnc_data
*data
;
48 data
= os_zalloc(sizeof(*data
));
52 data
->tncs
= tncs_init();
53 if (data
->tncs
== NULL
) {
58 data
->fragment_size
= 1300;
64 static void eap_tnc_reset(struct eap_sm
*sm
, void *priv
)
66 struct eap_tnc_data
*data
= priv
;
67 wpabuf_free(data
->in_buf
);
68 wpabuf_free(data
->out_buf
);
69 tncs_deinit(data
->tncs
);
74 static struct wpabuf
* eap_tnc_build_start(struct eap_sm
*sm
,
75 struct eap_tnc_data
*data
, u8 id
)
79 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, 1, EAP_CODE_REQUEST
,
82 wpa_printf(MSG_ERROR
, "EAP-TNC: Failed to allocate memory for "
88 wpabuf_put_u8(req
, EAP_TNC_FLAGS_START
| EAP_TNC_VERSION
);
90 data
->state
= CONTINUE
;
96 static struct wpabuf
* eap_tnc_build(struct eap_sm
*sm
,
97 struct eap_tnc_data
*data
)
102 char *start_buf
, *end_buf
;
103 size_t start_len
, end_len
;
106 imv_len
= tncs_total_send_len(data
->tncs
);
108 start_buf
= tncs_if_tnccs_start(data
->tncs
);
109 if (start_buf
== NULL
)
111 start_len
= os_strlen(start_buf
);
112 end_buf
= tncs_if_tnccs_end();
113 if (end_buf
== NULL
) {
117 end_len
= os_strlen(end_buf
);
119 rlen
= start_len
+ imv_len
+ end_len
;
120 req
= wpabuf_alloc(rlen
);
127 wpabuf_put_data(req
, start_buf
, start_len
);
130 rpos1
= wpabuf_put(req
, 0);
131 rpos
= tncs_copy_send_buf(data
->tncs
, rpos1
);
132 wpabuf_put(req
, rpos
- rpos1
);
134 wpabuf_put_data(req
, end_buf
, end_len
);
137 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-TNC: Request",
138 wpabuf_head(req
), wpabuf_len(req
));
144 static struct wpabuf
* eap_tnc_build_recommendation(struct eap_sm
*sm
,
145 struct eap_tnc_data
*data
)
147 switch (data
->recommendation
) {
153 /* TODO: support assignment to a different VLAN */
158 case NO_RECOMMENDATION
:
162 wpa_printf(MSG_DEBUG
, "EAP-TNC: Unknown recommendation");
166 return eap_tnc_build(sm
, data
);
170 static struct wpabuf
* eap_tnc_build_frag_ack(u8 id
, u8 code
)
174 msg
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, 0, code
, id
);
176 wpa_printf(MSG_ERROR
, "EAP-TNC: Failed to allocate memory "
181 wpa_printf(MSG_DEBUG
, "EAP-TNC: Send fragment ack");
187 static struct wpabuf
* eap_tnc_build_msg(struct eap_tnc_data
*data
, u8 id
)
191 size_t send_len
, plen
;
193 wpa_printf(MSG_DEBUG
, "EAP-TNC: Generating Request");
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
;
207 if (flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
)
209 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, plen
,
210 EAP_CODE_REQUEST
, id
);
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
));
218 wpabuf_put_data(req
, wpabuf_head_u8(data
->out_buf
) + data
->out_used
,
220 data
->out_used
+= send_len
;
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
;
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
) -
234 data
->state
= WAIT_FRAG_ACK
;
241 static struct wpabuf
* eap_tnc_buildReq(struct eap_sm
*sm
, void *priv
, u8 id
)
243 struct eap_tnc_data
*data
= priv
;
245 switch (data
->state
) {
247 tncs_init_connection(data
->tncs
);
248 return eap_tnc_build_start(sm
, data
, id
);
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 "
259 return eap_tnc_build_msg(data
, id
);
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");
270 return eap_tnc_build_msg(data
, id
);
272 return eap_tnc_build_msg(data
, id
);
274 return eap_tnc_build_frag_ack(id
, EAP_CODE_REQUEST
);
284 static Boolean
eap_tnc_check(struct eap_sm
*sm
, void *priv
,
285 struct wpabuf
*respData
)
287 struct eap_tnc_data
*data
= priv
;
291 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, respData
,
294 wpa_printf(MSG_INFO
, "EAP-TNC: Invalid frame");
298 if (len
== 0 && data
->state
!= WAIT_FRAG_ACK
) {
299 wpa_printf(MSG_INFO
, "EAP-TNC: Invalid frame (empty)");
304 return FALSE
; /* Fragment ACK does not include flags */
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
);
312 if (*pos
& EAP_TNC_FLAGS_START
) {
313 wpa_printf(MSG_DEBUG
, "EAP-TNC: Peer used Start flag");
321 static void tncs_process(struct eap_tnc_data
*data
, struct wpabuf
*inbuf
)
323 enum tncs_process_res res
;
325 res
= tncs_process_if_tnccs(data
->tncs
, wpabuf_head(inbuf
),
328 case TNCCS_RECOMMENDATION_ALLOW
:
329 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS allowed access");
330 data
->state
= RECOMMENDATION
;
331 data
->recommendation
= ALLOW
;
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
;
338 case TNCCS_RECOMMENDATION_ISOLATE
:
339 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS requested isolation");
340 data
->state
= RECOMMENDATION
;
341 data
->recommendation
= ISOLATE
;
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
;
348 case TNCCS_PROCESS_ERROR
:
349 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS processing error");
358 static int eap_tnc_process_cont(struct eap_tnc_data
*data
,
359 const u8
*buf
, size_t len
)
361 /* Process continuation of a pending message */
362 if (len
> wpabuf_tailroom(data
->in_buf
)) {
363 wpa_printf(MSG_DEBUG
, "EAP-TNC: Fragment overflow");
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
));
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
)
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");
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 "
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",
400 (unsigned long) wpabuf_tailroom(data
->in_buf
));
407 static void eap_tnc_process(struct eap_sm
*sm
, void *priv
,
408 struct wpabuf
*respData
)
410 struct eap_tnc_data
*data
= priv
;
414 u32 message_length
= 0;
415 struct wpabuf tmpbuf
;
417 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, respData
, &len
);
419 return; /* Should not happen; message already verified */
423 if (len
== 1 && (data
->state
== DONE
|| data
->state
== FAIL
)) {
424 wpa_printf(MSG_DEBUG
, "EAP-TNC: Peer acknowledged the last "
435 if (flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
) {
437 wpa_printf(MSG_DEBUG
, "EAP-TNC: Message underflow");
441 message_length
= WPA_GET_BE32(pos
);
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
));
452 wpa_printf(MSG_DEBUG
, "EAP-TNC: Received packet: Flags 0x%x "
453 "Message Length %u", flags
, message_length
);
455 if (data
->state
== WAIT_FRAG_ACK
) {
457 wpa_printf(MSG_DEBUG
, "EAP-TNC: Unexpected payload "
458 "in WAIT_FRAG_ACK state");
462 wpa_printf(MSG_DEBUG
, "EAP-TNC: Fragment acknowledged");
463 data
->state
= CONTINUE
;
467 if (data
->in_buf
&& eap_tnc_process_cont(data
, pos
, end
- pos
) < 0) {
472 if (flags
& EAP_TNC_FLAGS_MORE_FRAGMENTS
) {
473 if (eap_tnc_process_fragment(data
, flags
, message_length
,
477 data
->state
= FRAG_ACK
;
479 } else if (data
->state
== FRAG_ACK
) {
480 wpa_printf(MSG_DEBUG
, "EAP-TNC: All fragments received");
481 data
->state
= CONTINUE
;
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
;
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
);
494 if (data
->in_buf
!= &tmpbuf
)
495 wpabuf_free(data
->in_buf
);
500 static Boolean
eap_tnc_isDone(struct eap_sm
*sm
, void *priv
)
502 struct eap_tnc_data
*data
= priv
;
503 return data
->state
== DONE
;
507 static Boolean
eap_tnc_isSuccess(struct eap_sm
*sm
, void *priv
)
509 struct eap_tnc_data
*data
= priv
;
510 return data
->state
== DONE
;
514 int eap_server_tnc_register(void)
516 struct eap_method
*eap
;
519 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
520 EAP_VENDOR_IETF
, EAP_TYPE_TNC
, "TNC");
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
;
532 ret
= eap_server_method_register(eap
);
534 eap_server_method_free(eap
);