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 = 1300; 95 96 return data; 97 } 98 99 100 static void eap_tnc_reset(struct eap_sm *sm, void *priv) 101 { 102 struct eap_tnc_data *data = priv; 103 wpabuf_free(data->in_buf); 104 wpabuf_free(data->out_buf); 105 tncs_deinit(data->tncs); 106 os_free(data); 107 } 108 109 110 static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, 111 struct eap_tnc_data *data, u8 id) 112 { 113 struct wpabuf *req; 114 115 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST, 116 id); 117 if (req == NULL) { 118 wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " 119 "request"); 120 eap_tnc_set_state(data, FAIL); 121 return NULL; 122 } 123 124 wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); 125 126 eap_tnc_set_state(data, CONTINUE); 127 128 return req; 129 } 130 131 132 static struct wpabuf * eap_tnc_build(struct eap_sm *sm, 133 struct eap_tnc_data *data) 134 { 135 struct wpabuf *req; 136 u8 *rpos, *rpos1; 137 size_t rlen; 138 char *start_buf, *end_buf; 139 size_t start_len, end_len; 140 size_t imv_len; 141 142 imv_len = tncs_total_send_len(data->tncs); 143 144 start_buf = tncs_if_tnccs_start(data->tncs); 145 if (start_buf == NULL) 146 return NULL; 147 start_len = os_strlen(start_buf); 148 end_buf = tncs_if_tnccs_end(); 149 if (end_buf == NULL) { 150 os_free(start_buf); 151 return NULL; 152 } 153 end_len = os_strlen(end_buf); 154 155 rlen = start_len + imv_len + end_len; 156 req = wpabuf_alloc(rlen); 157 if (req == NULL) { 158 os_free(start_buf); 159 os_free(end_buf); 160 return NULL; 161 } 162 163 wpabuf_put_data(req, start_buf, start_len); 164 os_free(start_buf); 165 166 rpos1 = wpabuf_put(req, 0); 167 rpos = tncs_copy_send_buf(data->tncs, rpos1); 168 wpabuf_put(req, rpos - rpos1); 169 170 wpabuf_put_data(req, end_buf, end_len); 171 os_free(end_buf); 172 173 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request", 174 wpabuf_head(req), wpabuf_len(req)); 175 176 return req; 177 } 178 179 180 static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, 181 struct eap_tnc_data *data) 182 { 183 switch (data->recommendation) { 184 case ALLOW: 185 eap_tnc_set_state(data, DONE); 186 break; 187 case ISOLATE: 188 eap_tnc_set_state(data, FAIL); 189 /* TODO: support assignment to a different VLAN */ 190 break; 191 case NO_ACCESS: 192 eap_tnc_set_state(data, FAIL); 193 break; 194 case NO_RECOMMENDATION: 195 eap_tnc_set_state(data, DONE); 196 break; 197 default: 198 wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); 199 return NULL; 200 } 201 202 return eap_tnc_build(sm, data); 203 } 204 205 206 static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) 207 { 208 struct wpabuf *msg; 209 210 msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); 211 if (msg == NULL) { 212 wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " 213 "for fragment ack"); 214 return NULL; 215 } 216 wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ 217 218 wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); 219 220 return msg; 221 } 222 223 224 static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) 225 { 226 struct wpabuf *req; 227 u8 flags; 228 size_t send_len, plen; 229 230 wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request"); 231 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; 239 send_len -= 4; 240 } 241 } 242 243 plen = 1 + send_len; 244 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 245 plen += 4; 246 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, 247 EAP_CODE_REQUEST, id); 248 if (req == NULL) 249 return NULL; 250 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)); 254 255 wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, 256 send_len); 257 data->out_used += send_len; 258 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; 265 data->out_used = 0; 266 if (data->was_fail) 267 eap_tnc_set_state(data, FAIL); 268 else if (data->was_done) 269 eap_tnc_set_state(data, DONE); 270 } else { 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) - 274 data->out_used); 275 if (data->state == FAIL) 276 data->was_fail = 1; 277 else if (data->state == DONE) 278 data->was_done = 1; 279 eap_tnc_set_state(data, WAIT_FRAG_ACK); 280 } 281 282 return req; 283 } 284 285 286 static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id) 287 { 288 struct eap_tnc_data *data = priv; 289 290 switch (data->state) { 291 case START: 292 tncs_init_connection(data->tncs); 293 return eap_tnc_build_start(sm, data, id); 294 case CONTINUE: 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 " 299 "generate message"); 300 return NULL; 301 } 302 data->out_used = 0; 303 } 304 return eap_tnc_build_msg(data, id); 305 case RECOMMENDATION: 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"); 311 return NULL; 312 } 313 data->out_used = 0; 314 } 315 return eap_tnc_build_msg(data, id); 316 case WAIT_FRAG_ACK: 317 return eap_tnc_build_msg(data, id); 318 case FRAG_ACK: 319 return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST); 320 case DONE: 321 case FAIL: 322 return NULL; 323 } 324 325 return NULL; 326 } 327 328 329 static Boolean eap_tnc_check(struct eap_sm *sm, void *priv, 330 struct wpabuf *respData) 331 { 332 struct eap_tnc_data *data = priv; 333 const u8 *pos; 334 size_t len; 335 336 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, 337 &len); 338 if (pos == NULL) { 339 wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame"); 340 return TRUE; 341 } 342 343 if (len == 0 && data->state != WAIT_FRAG_ACK) { 344 wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)"); 345 return TRUE; 346 } 347 348 if (len == 0) 349 return FALSE; /* Fragment ACK does not include flags */ 350 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); 354 return TRUE; 355 } 356 357 if (*pos & EAP_TNC_FLAGS_START) { 358 wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag"); 359 return TRUE; 360 } 361 362 return FALSE; 363 } 364 365 366 static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) 367 { 368 enum tncs_process_res res; 369 370 res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf), 371 wpabuf_len(inbuf)); 372 switch (res) { 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; 377 break; 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; 382 break; 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; 387 break; 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; 392 break; 393 case TNCCS_PROCESS_ERROR: 394 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); 395 eap_tnc_set_state(data, FAIL); 396 break; 397 default: 398 break; 399 } 400 } 401 402 403 static int eap_tnc_process_cont(struct eap_tnc_data *data, 404 const u8 *buf, size_t len) 405 { 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); 410 return -1; 411 } 412 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)); 417 418 return 0; 419 } 420 421 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) 425 { 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"); 430 return -1; 431 } 432 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 " 438 "message"); 439 return -1; 440 } 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", 444 (unsigned long) len, 445 (unsigned long) wpabuf_tailroom(data->in_buf)); 446 } 447 448 return 0; 449 } 450 451 452 static void eap_tnc_process(struct eap_sm *sm, void *priv, 453 struct wpabuf *respData) 454 { 455 struct eap_tnc_data *data = priv; 456 const u8 *pos, *end; 457 size_t len; 458 u8 flags; 459 u32 message_length = 0; 460 struct wpabuf tmpbuf; 461 462 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); 463 if (pos == NULL) 464 return; /* Should not happen; message already verified */ 465 466 end = pos + len; 467 468 if (len == 1 && (data->state == DONE || data->state == FAIL)) { 469 wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last " 470 "message"); 471 return; 472 } 473 474 if (len == 0) { 475 /* fragment ack */ 476 flags = 0; 477 } else 478 flags = *pos++; 479 480 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { 481 if (end - pos < 4) { 482 wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); 483 eap_tnc_set_state(data, FAIL); 484 return; 485 } 486 message_length = WPA_GET_BE32(pos); 487 pos += 4; 488 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); 494 return; 495 } 496 } 497 wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " 498 "Message Length %u", flags, message_length); 499 500 if (data->state == WAIT_FRAG_ACK) { 501 if (len > 1) { 502 wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " 503 "in WAIT_FRAG_ACK state"); 504 eap_tnc_set_state(data, FAIL); 505 return; 506 } 507 wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); 508 eap_tnc_set_state(data, CONTINUE); 509 return; 510 } 511 512 if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { 513 eap_tnc_set_state(data, FAIL); 514 return; 515 } 516 517 if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { 518 if (eap_tnc_process_fragment(data, flags, message_length, 519 pos, end - pos) < 0) 520 eap_tnc_set_state(data, FAIL); 521 else 522 eap_tnc_set_state(data, FRAG_ACK); 523 return; 524 } else if (data->state == FRAG_ACK) { 525 wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); 526 eap_tnc_set_state(data, CONTINUE); 527 } 528 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; 533 } 534 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); 538 539 if (data->in_buf != &tmpbuf) 540 wpabuf_free(data->in_buf); 541 data->in_buf = NULL; 542 } 543 544 545 static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) 546 { 547 struct eap_tnc_data *data = priv; 548 return data->state == DONE || data->state == FAIL; 549 } 550 551 552 static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) 553 { 554 struct eap_tnc_data *data = priv; 555 return data->state == DONE; 556 } 557 558 559 int eap_server_tnc_register(void) 560 { 561 struct eap_method *eap; 562 int ret; 563 564 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 565 EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); 566 if (eap == NULL) 567 return -1; 568 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; 576 577 ret = eap_server_method_register(eap); 578 if (ret) 579 eap_server_method_free(eap); 580 return ret; 581 } 582