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.
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
;
38 #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
39 #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
40 #define EAP_TNC_FLAGS_START 0x20
41 #define EAP_TNC_VERSION_MASK 0x07
43 #define EAP_TNC_VERSION 1
46 static void * eap_tnc_init(struct eap_sm
*sm
)
48 struct eap_tnc_data
*data
;
50 data
= os_zalloc(sizeof(*data
));
54 data
->tncs
= tncs_init();
55 if (data
->tncs
== NULL
) {
60 data
->fragment_size
= 1300;
66 static void eap_tnc_reset(struct eap_sm
*sm
, void *priv
)
68 struct eap_tnc_data
*data
= priv
;
69 wpabuf_free(data
->in_buf
);
70 wpabuf_free(data
->out_buf
);
71 tncs_deinit(data
->tncs
);
76 static struct wpabuf
* eap_tnc_build_start(struct eap_sm
*sm
,
77 struct eap_tnc_data
*data
, u8 id
)
81 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, 1, EAP_CODE_REQUEST
,
84 wpa_printf(MSG_ERROR
, "EAP-TNC: Failed to allocate memory for "
90 wpabuf_put_u8(req
, EAP_TNC_FLAGS_START
| EAP_TNC_VERSION
);
92 data
->state
= CONTINUE
;
98 static struct wpabuf
* eap_tnc_build(struct eap_sm
*sm
,
99 struct eap_tnc_data
*data
)
104 char *start_buf
, *end_buf
;
105 size_t start_len
, end_len
;
108 imv_len
= tncs_total_send_len(data
->tncs
);
110 start_buf
= tncs_if_tnccs_start(data
->tncs
);
111 if (start_buf
== NULL
)
113 start_len
= os_strlen(start_buf
);
114 end_buf
= tncs_if_tnccs_end();
115 if (end_buf
== NULL
) {
119 end_len
= os_strlen(end_buf
);
121 rlen
= start_len
+ imv_len
+ end_len
;
122 req
= wpabuf_alloc(rlen
);
129 wpabuf_put_data(req
, start_buf
, start_len
);
132 rpos1
= wpabuf_put(req
, 0);
133 rpos
= tncs_copy_send_buf(data
->tncs
, rpos1
);
134 wpabuf_put(req
, rpos
- rpos1
);
136 wpabuf_put_data(req
, end_buf
, end_len
);
139 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-TNC: Request",
140 wpabuf_head(req
), wpabuf_len(req
));
146 static struct wpabuf
* eap_tnc_build_recommendation(struct eap_sm
*sm
,
147 struct eap_tnc_data
*data
)
149 switch (data
->recommendation
) {
155 /* TODO: support assignment to a different VLAN */
160 case NO_RECOMMENDATION
:
164 wpa_printf(MSG_DEBUG
, "EAP-TNC: Unknown recommendation");
168 return eap_tnc_build(sm
, data
);
172 static struct wpabuf
* eap_tnc_build_frag_ack(u8 id
, u8 code
)
176 msg
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, 1, code
, id
);
178 wpa_printf(MSG_ERROR
, "EAP-TNC: Failed to allocate memory "
182 wpabuf_put_u8(msg
, EAP_TNC_VERSION
); /* Flags */
184 wpa_printf(MSG_DEBUG
, "EAP-TNC: Send fragment ack");
190 static struct wpabuf
* eap_tnc_build_msg(struct eap_tnc_data
*data
, u8 id
)
194 size_t send_len
, plen
;
196 wpa_printf(MSG_DEBUG
, "EAP-TNC: Generating Request");
198 flags
= EAP_TNC_VERSION
;
199 send_len
= wpabuf_len(data
->out_buf
) - data
->out_used
;
200 if (1 + send_len
> data
->fragment_size
) {
201 send_len
= data
->fragment_size
- 1;
202 flags
|= EAP_TNC_FLAGS_MORE_FRAGMENTS
;
203 if (data
->out_used
== 0) {
204 flags
|= EAP_TNC_FLAGS_LENGTH_INCLUDED
;
210 if (flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
)
212 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, plen
,
213 EAP_CODE_REQUEST
, id
);
217 wpabuf_put_u8(req
, flags
); /* Flags */
218 if (flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
)
219 wpabuf_put_be32(req
, wpabuf_len(data
->out_buf
));
221 wpabuf_put_data(req
, wpabuf_head_u8(data
->out_buf
) + data
->out_used
,
223 data
->out_used
+= send_len
;
225 if (data
->out_used
== wpabuf_len(data
->out_buf
)) {
226 wpa_printf(MSG_DEBUG
, "EAP-TNC: Sending out %lu bytes "
227 "(message sent completely)",
228 (unsigned long) send_len
);
229 wpabuf_free(data
->out_buf
);
230 data
->out_buf
= NULL
;
233 wpa_printf(MSG_DEBUG
, "EAP-TNC: Sending out %lu bytes "
234 "(%lu more to send)", (unsigned long) send_len
,
235 (unsigned long) wpabuf_len(data
->out_buf
) -
237 if (data
->state
== FAIL
)
239 else if (data
->state
== DONE
)
241 data
->state
= WAIT_FRAG_ACK
;
248 static struct wpabuf
* eap_tnc_buildReq(struct eap_sm
*sm
, void *priv
, u8 id
)
250 struct eap_tnc_data
*data
= priv
;
252 switch (data
->state
) {
254 tncs_init_connection(data
->tncs
);
255 return eap_tnc_build_start(sm
, data
, id
);
257 if (data
->out_buf
== NULL
) {
258 data
->out_buf
= eap_tnc_build(sm
, data
);
259 if (data
->out_buf
== NULL
) {
260 wpa_printf(MSG_DEBUG
, "EAP-TNC: Failed to "
266 return eap_tnc_build_msg(data
, id
);
268 if (data
->out_buf
== NULL
) {
269 data
->out_buf
= eap_tnc_build_recommendation(sm
, data
);
270 if (data
->out_buf
== NULL
) {
271 wpa_printf(MSG_DEBUG
, "EAP-TNC: Failed to "
272 "generate recommendation message");
277 return eap_tnc_build_msg(data
, id
);
279 return eap_tnc_build_msg(data
, id
);
281 return eap_tnc_build_frag_ack(id
, EAP_CODE_REQUEST
);
291 static Boolean
eap_tnc_check(struct eap_sm
*sm
, void *priv
,
292 struct wpabuf
*respData
)
294 struct eap_tnc_data
*data
= priv
;
298 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, respData
,
301 wpa_printf(MSG_INFO
, "EAP-TNC: Invalid frame");
305 if (len
== 0 && data
->state
!= WAIT_FRAG_ACK
) {
306 wpa_printf(MSG_INFO
, "EAP-TNC: Invalid frame (empty)");
311 return FALSE
; /* Fragment ACK does not include flags */
313 if ((*pos
& EAP_TNC_VERSION_MASK
) != EAP_TNC_VERSION
) {
314 wpa_printf(MSG_DEBUG
, "EAP-TNC: Unsupported version %d",
315 *pos
& EAP_TNC_VERSION_MASK
);
319 if (*pos
& EAP_TNC_FLAGS_START
) {
320 wpa_printf(MSG_DEBUG
, "EAP-TNC: Peer used Start flag");
328 static void tncs_process(struct eap_tnc_data
*data
, struct wpabuf
*inbuf
)
330 enum tncs_process_res res
;
332 res
= tncs_process_if_tnccs(data
->tncs
, wpabuf_head(inbuf
),
335 case TNCCS_RECOMMENDATION_ALLOW
:
336 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS allowed access");
337 data
->state
= RECOMMENDATION
;
338 data
->recommendation
= ALLOW
;
340 case TNCCS_RECOMMENDATION_NO_RECOMMENDATION
:
341 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS has no recommendation");
342 data
->state
= RECOMMENDATION
;
343 data
->recommendation
= NO_RECOMMENDATION
;
345 case TNCCS_RECOMMENDATION_ISOLATE
:
346 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS requested isolation");
347 data
->state
= RECOMMENDATION
;
348 data
->recommendation
= ISOLATE
;
350 case TNCCS_RECOMMENDATION_NO_ACCESS
:
351 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS rejected access");
352 data
->state
= RECOMMENDATION
;
353 data
->recommendation
= NO_ACCESS
;
355 case TNCCS_PROCESS_ERROR
:
356 wpa_printf(MSG_DEBUG
, "EAP-TNC: TNCS processing error");
365 static int eap_tnc_process_cont(struct eap_tnc_data
*data
,
366 const u8
*buf
, size_t len
)
368 /* Process continuation of a pending message */
369 if (len
> wpabuf_tailroom(data
->in_buf
)) {
370 wpa_printf(MSG_DEBUG
, "EAP-TNC: Fragment overflow");
375 wpabuf_put_data(data
->in_buf
, buf
, len
);
376 wpa_printf(MSG_DEBUG
, "EAP-TNC: Received %lu bytes, waiting for %lu "
377 "bytes more", (unsigned long) len
,
378 (unsigned long) wpabuf_tailroom(data
->in_buf
));
384 static int eap_tnc_process_fragment(struct eap_tnc_data
*data
,
385 u8 flags
, u32 message_length
,
386 const u8
*buf
, size_t len
)
388 /* Process a fragment that is not the last one of the message */
389 if (data
->in_buf
== NULL
&& !(flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
)) {
390 wpa_printf(MSG_DEBUG
, "EAP-TNC: No Message Length field in a "
391 "fragmented packet");
395 if (data
->in_buf
== NULL
) {
396 /* First fragment of the message */
397 data
->in_buf
= wpabuf_alloc(message_length
);
398 if (data
->in_buf
== NULL
) {
399 wpa_printf(MSG_DEBUG
, "EAP-TNC: No memory for "
403 wpabuf_put_data(data
->in_buf
, buf
, len
);
404 wpa_printf(MSG_DEBUG
, "EAP-TNC: Received %lu bytes in first "
405 "fragment, waiting for %lu bytes more",
407 (unsigned long) wpabuf_tailroom(data
->in_buf
));
414 static void eap_tnc_process(struct eap_sm
*sm
, void *priv
,
415 struct wpabuf
*respData
)
417 struct eap_tnc_data
*data
= priv
;
421 u32 message_length
= 0;
422 struct wpabuf tmpbuf
;
424 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_TNC
, respData
, &len
);
426 return; /* Should not happen; message already verified */
430 if (len
== 1 && (data
->state
== DONE
|| data
->state
== FAIL
)) {
431 wpa_printf(MSG_DEBUG
, "EAP-TNC: Peer acknowledged the last "
442 if (flags
& EAP_TNC_FLAGS_LENGTH_INCLUDED
) {
444 wpa_printf(MSG_DEBUG
, "EAP-TNC: Message underflow");
448 message_length
= WPA_GET_BE32(pos
);
451 if (message_length
< (u32
) (end
- pos
)) {
452 wpa_printf(MSG_DEBUG
, "EAP-TNC: Invalid Message "
453 "Length (%d; %ld remaining in this msg)",
454 message_length
, (long) (end
- pos
));
459 wpa_printf(MSG_DEBUG
, "EAP-TNC: Received packet: Flags 0x%x "
460 "Message Length %u", flags
, message_length
);
462 if (data
->state
== WAIT_FRAG_ACK
) {
464 wpa_printf(MSG_DEBUG
, "EAP-TNC: Unexpected payload "
465 "in WAIT_FRAG_ACK state");
469 wpa_printf(MSG_DEBUG
, "EAP-TNC: Fragment acknowledged");
472 else if (data
->was_done
)
475 data
->state
= CONTINUE
;
479 if (data
->in_buf
&& eap_tnc_process_cont(data
, pos
, end
- pos
) < 0) {
484 if (flags
& EAP_TNC_FLAGS_MORE_FRAGMENTS
) {
485 if (eap_tnc_process_fragment(data
, flags
, message_length
,
489 data
->state
= FRAG_ACK
;
491 } else if (data
->state
== FRAG_ACK
) {
492 wpa_printf(MSG_DEBUG
, "EAP-TNC: All fragments received");
493 data
->state
= CONTINUE
;
496 if (data
->in_buf
== NULL
) {
497 /* Wrap unfragmented messages as wpabuf without extra copy */
498 wpabuf_set(&tmpbuf
, pos
, end
- pos
);
499 data
->in_buf
= &tmpbuf
;
502 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-TNC: Received payload",
503 wpabuf_head(data
->in_buf
), wpabuf_len(data
->in_buf
));
504 tncs_process(data
, data
->in_buf
);
506 if (data
->in_buf
!= &tmpbuf
)
507 wpabuf_free(data
->in_buf
);
512 static Boolean
eap_tnc_isDone(struct eap_sm
*sm
, void *priv
)
514 struct eap_tnc_data
*data
= priv
;
515 return data
->state
== DONE
|| data
->state
== FAIL
;
519 static Boolean
eap_tnc_isSuccess(struct eap_sm
*sm
, void *priv
)
521 struct eap_tnc_data
*data
= priv
;
522 return data
->state
== DONE
;
526 int eap_server_tnc_register(void)
528 struct eap_method
*eap
;
531 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
532 EAP_VENDOR_IETF
, EAP_TYPE_TNC
, "TNC");
536 eap
->init
= eap_tnc_init
;
537 eap
->reset
= eap_tnc_reset
;
538 eap
->buildReq
= eap_tnc_buildReq
;
539 eap
->check
= eap_tnc_check
;
540 eap
->process
= eap_tnc_process
;
541 eap
->isDone
= eap_tnc_isDone
;
542 eap
->isSuccess
= eap_tnc_isSuccess
;
544 ret
= eap_server_method_register(eap
);
546 eap_server_method_free(eap
);