$search
00001 /* 00002 * EAP server method: EAP-TNC (Trusted Network Connect) 00003 * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License version 2 as 00007 * published by the Free Software Foundation. 00008 * 00009 * Alternatively, this software may be distributed under the terms of BSD 00010 * license. 00011 * 00012 * See README and COPYING for more details. 00013 */ 00014 00015 #include "includes.h" 00016 00017 #include "common.h" 00018 #include "base64.h" 00019 #include "eap_i.h" 00020 #include "tncs.h" 00021 00022 00023 struct eap_tnc_data { 00024 enum eap_tnc_state { 00025 START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, 00026 FAIL 00027 } state; 00028 enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation; 00029 struct tncs_data *tncs; 00030 struct wpabuf *in_buf; 00031 struct wpabuf *out_buf; 00032 size_t out_used; 00033 size_t fragment_size; 00034 unsigned int was_done:1; 00035 unsigned int was_fail:1; 00036 }; 00037 00038 00039 /* EAP-TNC Flags */ 00040 #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 00041 #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 00042 #define EAP_TNC_FLAGS_START 0x20 00043 #define EAP_TNC_VERSION_MASK 0x07 00044 00045 #define EAP_TNC_VERSION 1 00046 00047 00048 static const char * eap_tnc_state_txt(enum eap_tnc_state state) 00049 { 00050 switch (state) { 00051 case START: 00052 return "START"; 00053 case CONTINUE: 00054 return "CONTINUE"; 00055 case RECOMMENDATION: 00056 return "RECOMMENDATION"; 00057 case FRAG_ACK: 00058 return "FRAG_ACK"; 00059 case WAIT_FRAG_ACK: 00060 return "WAIT_FRAG_ACK"; 00061 case DONE: 00062 return "DONE"; 00063 case FAIL: 00064 return "FAIL"; 00065 } 00066 return "??"; 00067 } 00068 00069 00070 static void eap_tnc_set_state(struct eap_tnc_data *data, 00071 enum eap_tnc_state new_state) 00072 { 00073 wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s", 00074 eap_tnc_state_txt(data->state), 00075 eap_tnc_state_txt(new_state)); 00076 data->state = new_state; 00077 } 00078 00079 00080 static void * eap_tnc_init(struct eap_sm *sm) 00081 { 00082 struct eap_tnc_data *data; 00083 00084 data = os_zalloc(sizeof(*data)); 00085 if (data == NULL) 00086 return NULL; 00087 eap_tnc_set_state(data, START); 00088 data->tncs = tncs_init(); 00089 if (data->tncs == NULL) { 00090 os_free(data); 00091 return NULL; 00092 } 00093 00094 data->fragment_size = 1300; 00095 00096 return data; 00097 } 00098 00099 00100 static void eap_tnc_reset(struct eap_sm *sm, void *priv) 00101 { 00102 struct eap_tnc_data *data = priv; 00103 wpabuf_free(data->in_buf); 00104 wpabuf_free(data->out_buf); 00105 tncs_deinit(data->tncs); 00106 os_free(data); 00107 } 00108 00109 00110 static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, 00111 struct eap_tnc_data *data, u8 id) 00112 { 00113 struct wpabuf *req; 00114 00115 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST, 00116 id); 00117 if (req == NULL) { 00118 wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " 00119 "request"); 00120 eap_tnc_set_state(data, FAIL); 00121 return NULL; 00122 } 00123 00124 wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); 00125 00126 eap_tnc_set_state(data, CONTINUE); 00127 00128 return req; 00129 } 00130 00131 00132 static struct wpabuf * eap_tnc_build(struct eap_sm *sm, 00133 struct eap_tnc_data *data) 00134 { 00135 struct wpabuf *req; 00136 u8 *rpos, *rpos1; 00137 size_t rlen; 00138 char *start_buf, *end_buf; 00139 size_t start_len, end_len; 00140 size_t imv_len; 00141 00142 imv_len = tncs_total_send_len(data->tncs); 00143 00144 start_buf = tncs_if_tnccs_start(data->tncs); 00145 if (start_buf == NULL) 00146 return NULL; 00147 start_len = os_strlen(start_buf); 00148 end_buf = tncs_if_tnccs_end(); 00149 if (end_buf == NULL) { 00150 os_free(start_buf); 00151 return NULL; 00152 } 00153 end_len = os_strlen(end_buf); 00154 00155 rlen = start_len + imv_len + end_len; 00156 req = wpabuf_alloc(rlen); 00157 if (req == NULL) { 00158 os_free(start_buf); 00159 os_free(end_buf); 00160 return NULL; 00161 } 00162 00163 wpabuf_put_data(req, start_buf, start_len); 00164 os_free(start_buf); 00165 00166 rpos1 = wpabuf_put(req, 0); 00167 rpos = tncs_copy_send_buf(data->tncs, rpos1); 00168 wpabuf_put(req, rpos - rpos1); 00169 00170 wpabuf_put_data(req, end_buf, end_len); 00171 os_free(end_buf); 00172 00173 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request", 00174 wpabuf_head(req), wpabuf_len(req)); 00175 00176 return req; 00177 } 00178 00179 00180 static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, 00181 struct eap_tnc_data *data) 00182 { 00183 switch (data->recommendation) { 00184 case ALLOW: 00185 eap_tnc_set_state(data, DONE); 00186 break; 00187 case ISOLATE: 00188 eap_tnc_set_state(data, FAIL); 00189 /* TODO: support assignment to a different VLAN */ 00190 break; 00191 case NO_ACCESS: 00192 eap_tnc_set_state(data, FAIL); 00193 break; 00194 case NO_RECOMMENDATION: 00195 eap_tnc_set_state(data, DONE); 00196 break; 00197 default: 00198 wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); 00199 return NULL; 00200 } 00201 00202 return eap_tnc_build(sm, data); 00203 } 00204 00205 00206 static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) 00207 { 00208 struct wpabuf *msg; 00209 00210 msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); 00211 if (msg == NULL) { 00212 wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " 00213 "for fragment ack"); 00214 return NULL; 00215 } 00216 wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ 00217 00218 wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); 00219 00220 return msg; 00221 } 00222 00223 00224 static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) 00225 { 00226 struct wpabuf *req; 00227 u8 flags; 00228 size_t send_len, plen; 00229 00230 wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request"); 00231 00232 flags = EAP_TNC_VERSION; 00233 send_len = wpabuf_len(data->out_buf) - data->out_used; 00234 if (1 + send_len > data->fragment_size) { 00235 send_len = data->fragment_size - 1; 00236 flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; 00237 if (data->out_used == 0) { 00238 flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; 00239 send_len -= 4; 00240 } 00241 } 00242 00243 plen = 1 + send_len; 00244 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 00245 plen += 4; 00246 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, 00247 EAP_CODE_REQUEST, id); 00248 if (req == NULL) 00249 return NULL; 00250 00251 wpabuf_put_u8(req, flags); /* Flags */ 00252 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 00253 wpabuf_put_be32(req, wpabuf_len(data->out_buf)); 00254 00255 wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, 00256 send_len); 00257 data->out_used += send_len; 00258 00259 if (data->out_used == wpabuf_len(data->out_buf)) { 00260 wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 00261 "(message sent completely)", 00262 (unsigned long) send_len); 00263 wpabuf_free(data->out_buf); 00264 data->out_buf = NULL; 00265 data->out_used = 0; 00266 if (data->was_fail) 00267 eap_tnc_set_state(data, FAIL); 00268 else if (data->was_done) 00269 eap_tnc_set_state(data, DONE); 00270 } else { 00271 wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 00272 "(%lu more to send)", (unsigned long) send_len, 00273 (unsigned long) wpabuf_len(data->out_buf) - 00274 data->out_used); 00275 if (data->state == FAIL) 00276 data->was_fail = 1; 00277 else if (data->state == DONE) 00278 data->was_done = 1; 00279 eap_tnc_set_state(data, WAIT_FRAG_ACK); 00280 } 00281 00282 return req; 00283 } 00284 00285 00286 static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id) 00287 { 00288 struct eap_tnc_data *data = priv; 00289 00290 switch (data->state) { 00291 case START: 00292 tncs_init_connection(data->tncs); 00293 return eap_tnc_build_start(sm, data, id); 00294 case CONTINUE: 00295 if (data->out_buf == NULL) { 00296 data->out_buf = eap_tnc_build(sm, data); 00297 if (data->out_buf == NULL) { 00298 wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " 00299 "generate message"); 00300 return NULL; 00301 } 00302 data->out_used = 0; 00303 } 00304 return eap_tnc_build_msg(data, id); 00305 case RECOMMENDATION: 00306 if (data->out_buf == NULL) { 00307 data->out_buf = eap_tnc_build_recommendation(sm, data); 00308 if (data->out_buf == NULL) { 00309 wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " 00310 "generate recommendation message"); 00311 return NULL; 00312 } 00313 data->out_used = 0; 00314 } 00315 return eap_tnc_build_msg(data, id); 00316 case WAIT_FRAG_ACK: 00317 return eap_tnc_build_msg(data, id); 00318 case FRAG_ACK: 00319 return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST); 00320 case DONE: 00321 case FAIL: 00322 return NULL; 00323 } 00324 00325 return NULL; 00326 } 00327 00328 00329 static Boolean eap_tnc_check(struct eap_sm *sm, void *priv, 00330 struct wpabuf *respData) 00331 { 00332 struct eap_tnc_data *data = priv; 00333 const u8 *pos; 00334 size_t len; 00335 00336 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, 00337 &len); 00338 if (pos == NULL) { 00339 wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame"); 00340 return TRUE; 00341 } 00342 00343 if (len == 0 && data->state != WAIT_FRAG_ACK) { 00344 wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)"); 00345 return TRUE; 00346 } 00347 00348 if (len == 0) 00349 return FALSE; /* Fragment ACK does not include flags */ 00350 00351 if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { 00352 wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", 00353 *pos & EAP_TNC_VERSION_MASK); 00354 return TRUE; 00355 } 00356 00357 if (*pos & EAP_TNC_FLAGS_START) { 00358 wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag"); 00359 return TRUE; 00360 } 00361 00362 return FALSE; 00363 } 00364 00365 00366 static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) 00367 { 00368 enum tncs_process_res res; 00369 00370 res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf), 00371 wpabuf_len(inbuf)); 00372 switch (res) { 00373 case TNCCS_RECOMMENDATION_ALLOW: 00374 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); 00375 eap_tnc_set_state(data, RECOMMENDATION); 00376 data->recommendation = ALLOW; 00377 break; 00378 case TNCCS_RECOMMENDATION_NO_RECOMMENDATION: 00379 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation"); 00380 eap_tnc_set_state(data, RECOMMENDATION); 00381 data->recommendation = NO_RECOMMENDATION; 00382 break; 00383 case TNCCS_RECOMMENDATION_ISOLATE: 00384 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation"); 00385 eap_tnc_set_state(data, RECOMMENDATION); 00386 data->recommendation = ISOLATE; 00387 break; 00388 case TNCCS_RECOMMENDATION_NO_ACCESS: 00389 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); 00390 eap_tnc_set_state(data, RECOMMENDATION); 00391 data->recommendation = NO_ACCESS; 00392 break; 00393 case TNCCS_PROCESS_ERROR: 00394 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); 00395 eap_tnc_set_state(data, FAIL); 00396 break; 00397 default: 00398 break; 00399 } 00400 } 00401 00402 00403 static int eap_tnc_process_cont(struct eap_tnc_data *data, 00404 const u8 *buf, size_t len) 00405 { 00406 /* Process continuation of a pending message */ 00407 if (len > wpabuf_tailroom(data->in_buf)) { 00408 wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); 00409 eap_tnc_set_state(data, FAIL); 00410 return -1; 00411 } 00412 00413 wpabuf_put_data(data->in_buf, buf, len); 00414 wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu " 00415 "bytes more", (unsigned long) len, 00416 (unsigned long) wpabuf_tailroom(data->in_buf)); 00417 00418 return 0; 00419 } 00420 00421 00422 static int eap_tnc_process_fragment(struct eap_tnc_data *data, 00423 u8 flags, u32 message_length, 00424 const u8 *buf, size_t len) 00425 { 00426 /* Process a fragment that is not the last one of the message */ 00427 if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { 00428 wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " 00429 "fragmented packet"); 00430 return -1; 00431 } 00432 00433 if (data->in_buf == NULL) { 00434 /* First fragment of the message */ 00435 data->in_buf = wpabuf_alloc(message_length); 00436 if (data->in_buf == NULL) { 00437 wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " 00438 "message"); 00439 return -1; 00440 } 00441 wpabuf_put_data(data->in_buf, buf, len); 00442 wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " 00443 "fragment, waiting for %lu bytes more", 00444 (unsigned long) len, 00445 (unsigned long) wpabuf_tailroom(data->in_buf)); 00446 } 00447 00448 return 0; 00449 } 00450 00451 00452 static void eap_tnc_process(struct eap_sm *sm, void *priv, 00453 struct wpabuf *respData) 00454 { 00455 struct eap_tnc_data *data = priv; 00456 const u8 *pos, *end; 00457 size_t len; 00458 u8 flags; 00459 u32 message_length = 0; 00460 struct wpabuf tmpbuf; 00461 00462 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); 00463 if (pos == NULL) 00464 return; /* Should not happen; message already verified */ 00465 00466 end = pos + len; 00467 00468 if (len == 1 && (data->state == DONE || data->state == FAIL)) { 00469 wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last " 00470 "message"); 00471 return; 00472 } 00473 00474 if (len == 0) { 00475 /* fragment ack */ 00476 flags = 0; 00477 } else 00478 flags = *pos++; 00479 00480 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { 00481 if (end - pos < 4) { 00482 wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); 00483 eap_tnc_set_state(data, FAIL); 00484 return; 00485 } 00486 message_length = WPA_GET_BE32(pos); 00487 pos += 4; 00488 00489 if (message_length < (u32) (end - pos)) { 00490 wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " 00491 "Length (%d; %ld remaining in this msg)", 00492 message_length, (long) (end - pos)); 00493 eap_tnc_set_state(data, FAIL); 00494 return; 00495 } 00496 } 00497 wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " 00498 "Message Length %u", flags, message_length); 00499 00500 if (data->state == WAIT_FRAG_ACK) { 00501 if (len > 1) { 00502 wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " 00503 "in WAIT_FRAG_ACK state"); 00504 eap_tnc_set_state(data, FAIL); 00505 return; 00506 } 00507 wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); 00508 eap_tnc_set_state(data, CONTINUE); 00509 return; 00510 } 00511 00512 if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { 00513 eap_tnc_set_state(data, FAIL); 00514 return; 00515 } 00516 00517 if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { 00518 if (eap_tnc_process_fragment(data, flags, message_length, 00519 pos, end - pos) < 0) 00520 eap_tnc_set_state(data, FAIL); 00521 else 00522 eap_tnc_set_state(data, FRAG_ACK); 00523 return; 00524 } else if (data->state == FRAG_ACK) { 00525 wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); 00526 eap_tnc_set_state(data, CONTINUE); 00527 } 00528 00529 if (data->in_buf == NULL) { 00530 /* Wrap unfragmented messages as wpabuf without extra copy */ 00531 wpabuf_set(&tmpbuf, pos, end - pos); 00532 data->in_buf = &tmpbuf; 00533 } 00534 00535 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload", 00536 wpabuf_head(data->in_buf), wpabuf_len(data->in_buf)); 00537 tncs_process(data, data->in_buf); 00538 00539 if (data->in_buf != &tmpbuf) 00540 wpabuf_free(data->in_buf); 00541 data->in_buf = NULL; 00542 } 00543 00544 00545 static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) 00546 { 00547 struct eap_tnc_data *data = priv; 00548 return data->state == DONE || data->state == FAIL; 00549 } 00550 00551 00552 static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) 00553 { 00554 struct eap_tnc_data *data = priv; 00555 return data->state == DONE; 00556 } 00557 00558 00559 int eap_server_tnc_register(void) 00560 { 00561 struct eap_method *eap; 00562 int ret; 00563 00564 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 00565 EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); 00566 if (eap == NULL) 00567 return -1; 00568 00569 eap->init = eap_tnc_init; 00570 eap->reset = eap_tnc_reset; 00571 eap->buildReq = eap_tnc_buildReq; 00572 eap->check = eap_tnc_check; 00573 eap->process = eap_tnc_process; 00574 eap->isDone = eap_tnc_isDone; 00575 eap->isSuccess = eap_tnc_isSuccess; 00576 00577 ret = eap_server_method_register(eap); 00578 if (ret) 00579 eap_server_method_free(eap); 00580 return ret; 00581 }