1 /* 2 * EAP server method: EAP-TNC (Trusted Network Connect) 3 * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "includes.h" 10 11 #include "common.h" 12 #include "eap_i.h" 13 #include "tncs.h" 14 15 16 struct eap_tnc_data { 17 enum eap_tnc_state { 18 START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, 19 FAIL 20 } state; 21 enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation; 22 struct tncs_data *tncs; 23 struct wpabuf *in_buf; 24 struct wpabuf *out_buf; 25 size_t out_used; 26 size_t fragment_size; 27 unsigned int was_done:1; 28 unsigned int was_fail:1; 29 }; 30 31 32 /* EAP-TNC Flags */ 33 #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 34 #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 35 #define EAP_TNC_FLAGS_START 0x20 36 #define EAP_TNC_VERSION_MASK 0x07 37 38 #define EAP_TNC_VERSION 1 39 40 41 static const char * eap_tnc_state_txt(enum eap_tnc_state state) 42 { 43 switch (state) { 44 case START: 45 return "START"; 46 case CONTINUE: 47 return "CONTINUE"; 48 case RECOMMENDATION: 49 return "RECOMMENDATION"; 50 case FRAG_ACK: 51 return "FRAG_ACK"; 52 case WAIT_FRAG_ACK: 53 return "WAIT_FRAG_ACK"; 54 case DONE: 55 return "DONE"; 56 case FAIL: 57 return "FAIL"; 58 } 59 return "??"; 60 } 61 62 63 static void eap_tnc_set_state(struct eap_tnc_data *data, 64 enum eap_tnc_state new_state) 65 { 66 wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s", 67 eap_tnc_state_txt(data->state), 68 eap_tnc_state_txt(new_state)); 69 data->state = new_state; 70 } 71 72 73 static void * eap_tnc_init(struct eap_sm *sm) 74 { 75 struct eap_tnc_data *data; 76 77 data = os_zalloc(sizeof(*data)); 78 if (data == NULL) 79 return NULL; 80 eap_tnc_set_state(data, START); 81 data->tncs = tncs_init(); 82 if (data->tncs == NULL) { 83 os_free(data); 84 return NULL; 85 } 86 87 data->fragment_size = sm->fragment_size > 100 ? 88 sm->fragment_size - 98 : 1300; 89 90 return data; 91 } 92 93 94 static void eap_tnc_reset(struct eap_sm *sm, void *priv) 95 { 96 struct eap_tnc_data *data = priv; 97 wpabuf_free(data->in_buf); 98 wpabuf_free(data->out_buf); 99 tncs_deinit(data->tncs); 100 os_free(data); 101 } 102 103 104 static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, 105 struct eap_tnc_data *data, u8 id) 106 { 107 struct wpabuf *req; 108 109 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST, 110 id); 111 if (req == NULL) { 112 wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " 113 "request"); 114 eap_tnc_set_state(data, FAIL); 115 return NULL; 116 } 117 118 wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); 119 120 eap_tnc_set_state(data, CONTINUE); 121 122 return req; 123 } 124 125 126 static struct wpabuf * eap_tnc_build(struct eap_sm *sm, 127 struct eap_tnc_data *data) 128 { 129 struct wpabuf *req; 130 u8 *rpos, *rpos1; 131 size_t rlen; 132 char *start_buf, *end_buf; 133 size_t start_len, end_len; 134 size_t imv_len; 135 136 imv_len = tncs_total_send_len(data->tncs); 137 138 start_buf = tncs_if_tnccs_start(data->tncs); 139 if (start_buf == NULL) 140 return NULL; 141 start_len = os_strlen(start_buf); 142 end_buf = tncs_if_tnccs_end(); 143 if (end_buf == NULL) { 144 os_free(start_buf); 145 return NULL; 146 } 147 end_len = os_strlen(end_buf); 148 149 rlen = start_len + imv_len + end_len; 150 req = wpabuf_alloc(rlen); 151 if (req == NULL) { 152 os_free(start_buf); 153 os_free(end_buf); 154 return NULL; 155 } 156 157 wpabuf_put_data(req, start_buf, start_len); 158 os_free(start_buf); 159 160 rpos1 = wpabuf_put(req, 0); 161 rpos = tncs_copy_send_buf(data->tncs, rpos1); 162 wpabuf_put(req, rpos - rpos1); 163 164 wpabuf_put_data(req, end_buf, end_len); 165 os_free(end_buf); 166 167 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request", 168 wpabuf_head(req), wpabuf_len(req)); 169 170 return req; 171 } 172 173 174 static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, 175 struct eap_tnc_data *data) 176 { 177 switch (data->recommendation) { 178 case ALLOW: 179 eap_tnc_set_state(data, DONE); 180 break; 181 case ISOLATE: 182 eap_tnc_set_state(data, FAIL); 183 /* TODO: support assignment to a different VLAN */ 184 break; 185 case NO_ACCESS: 186 eap_tnc_set_state(data, FAIL); 187 break; 188 case NO_RECOMMENDATION: 189 eap_tnc_set_state(data, DONE); 190 break; 191 default: 192 wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); 193 return NULL; 194 } 195 196 return eap_tnc_build(sm, data); 197 } 198 199 200 static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) 201 { 202 struct wpabuf *msg; 203 204 msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); 205 if (msg == NULL) { 206 wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " 207 "for fragment ack"); 208 return NULL; 209 } 210 wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ 211 212 wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); 213 214 return msg; 215 } 216 217 218 static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) 219 { 220 struct wpabuf *req; 221 u8 flags; 222 size_t send_len, plen; 223 224 wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request"); 225 226 flags = EAP_TNC_VERSION; 227 send_len = wpabuf_len(data->out_buf) - data->out_used; 228 if (1 + send_len > data->fragment_size) { 229 send_len = data->fragment_size - 1; 230 flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; 231 if (data->out_used == 0) { 232 flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; 233 send_len -= 4; 234 } 235 } 236 237 plen = 1 + send_len; 238 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 239 plen += 4; 240 req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, 241 EAP_CODE_REQUEST, id); 242 if (req == NULL) 243 return NULL; 244 245 wpabuf_put_u8(req, flags); /* Flags */ 246 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 247 wpabuf_put_be32(req, wpabuf_len(data->out_buf)); 248 249 wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, 250 send_len); 251 data->out_used += send_len; 252 253 if (data->out_used == wpabuf_len(data->out_buf)) { 254 wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 255 "(message sent completely)", 256 (unsigned long) send_len); 257 wpabuf_free(data->out_buf); 258 data->out_buf = NULL; 259 data->out_used = 0; 260 if (data->was_fail) 261 eap_tnc_set_state(data, FAIL); 262 else if (data->was_done) 263 eap_tnc_set_state(data, DONE); 264 } else { 265 wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 266 "(%lu more to send)", (unsigned long) send_len, 267 (unsigned long) wpabuf_len(data->out_buf) - 268 data->out_used); 269 if (data->state == FAIL) 270 data->was_fail = 1; 271 else if (data->state == DONE) 272 data->was_done = 1; 273 eap_tnc_set_state(data, WAIT_FRAG_ACK); 274 } 275 276 return req; 277 } 278 279 280 static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id) 281 { 282 struct eap_tnc_data *data = priv; 283 284 switch (data->state) { 285 case START: 286 tncs_init_connection(data->tncs); 287 return eap_tnc_build_start(sm, data, id); 288 case CONTINUE: 289 if (data->out_buf == NULL) { 290 data->out_buf = eap_tnc_build(sm, data); 291 if (data->out_buf == NULL) { 292 wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " 293 "generate message"); 294 return NULL; 295 } 296 data->out_used = 0; 297 } 298 return eap_tnc_build_msg(data, id); 299 case RECOMMENDATION: 300 if (data->out_buf == NULL) { 301 data->out_buf = eap_tnc_build_recommendation(sm, data); 302 if (data->out_buf == NULL) { 303 wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " 304 "generate recommendation message"); 305 return NULL; 306 } 307 data->out_used = 0; 308 } 309 return eap_tnc_build_msg(data, id); 310 case WAIT_FRAG_ACK: 311 return eap_tnc_build_msg(data, id); 312 case FRAG_ACK: 313 return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST); 314 case DONE: 315 case FAIL: 316 return NULL; 317 } 318 319 return NULL; 320 } 321 322 323 static Boolean eap_tnc_check(struct eap_sm *sm, void *priv, 324 struct wpabuf *respData) 325 { 326 struct eap_tnc_data *data = priv; 327 const u8 *pos; 328 size_t len; 329 330 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, 331 &len); 332 if (pos == NULL) { 333 wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame"); 334 return TRUE; 335 } 336 337 if (len == 0 && data->state != WAIT_FRAG_ACK) { 338 wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)"); 339 return TRUE; 340 } 341 342 if (len == 0) 343 return FALSE; /* Fragment ACK does not include flags */ 344 345 if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { 346 wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", 347 *pos & EAP_TNC_VERSION_MASK); 348 return TRUE; 349 } 350 351 if (*pos & EAP_TNC_FLAGS_START) { 352 wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag"); 353 return TRUE; 354 } 355 356 return FALSE; 357 } 358 359 360 static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) 361 { 362 enum tncs_process_res res; 363 364 res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf), 365 wpabuf_len(inbuf)); 366 switch (res) { 367 case TNCCS_RECOMMENDATION_ALLOW: 368 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); 369 eap_tnc_set_state(data, RECOMMENDATION); 370 data->recommendation = ALLOW; 371 break; 372 case TNCCS_RECOMMENDATION_NO_RECOMMENDATION: 373 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation"); 374 eap_tnc_set_state(data, RECOMMENDATION); 375 data->recommendation = NO_RECOMMENDATION; 376 break; 377 case TNCCS_RECOMMENDATION_ISOLATE: 378 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation"); 379 eap_tnc_set_state(data, RECOMMENDATION); 380 data->recommendation = ISOLATE; 381 break; 382 case TNCCS_RECOMMENDATION_NO_ACCESS: 383 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); 384 eap_tnc_set_state(data, RECOMMENDATION); 385 data->recommendation = NO_ACCESS; 386 break; 387 case TNCCS_PROCESS_ERROR: 388 wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); 389 eap_tnc_set_state(data, FAIL); 390 break; 391 default: 392 break; 393 } 394 } 395 396 397 static int eap_tnc_process_cont(struct eap_tnc_data *data, 398 const u8 *buf, size_t len) 399 { 400 /* Process continuation of a pending message */ 401 if (len > wpabuf_tailroom(data->in_buf)) { 402 wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); 403 eap_tnc_set_state(data, FAIL); 404 return -1; 405 } 406 407 wpabuf_put_data(data->in_buf, buf, len); 408 wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu " 409 "bytes more", (unsigned long) len, 410 (unsigned long) wpabuf_tailroom(data->in_buf)); 411 412 return 0; 413 } 414 415 416 static int eap_tnc_process_fragment(struct eap_tnc_data *data, 417 u8 flags, u32 message_length, 418 const u8 *buf, size_t len) 419 { 420 /* Process a fragment that is not the last one of the message */ 421 if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { 422 wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " 423 "fragmented packet"); 424 return -1; 425 } 426 427 if (data->in_buf == NULL) { 428 /* First fragment of the message */ 429 data->in_buf = wpabuf_alloc(message_length); 430 if (data->in_buf == NULL) { 431 wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " 432 "message"); 433 return -1; 434 } 435 wpabuf_put_data(data->in_buf, buf, len); 436 wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " 437 "fragment, waiting for %lu bytes more", 438 (unsigned long) len, 439 (unsigned long) wpabuf_tailroom(data->in_buf)); 440 } 441 442 return 0; 443 } 444 445 446 static void eap_tnc_process(struct eap_sm *sm, void *priv, 447 struct wpabuf *respData) 448 { 449 struct eap_tnc_data *data = priv; 450 const u8 *pos, *end; 451 size_t len; 452 u8 flags; 453 u32 message_length = 0; 454 struct wpabuf tmpbuf; 455 456 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); 457 if (pos == NULL) 458 return; /* Should not happen; message already verified */ 459 460 end = pos + len; 461 462 if (len == 1 && (data->state == DONE || data->state == FAIL)) { 463 wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last " 464 "message"); 465 return; 466 } 467 468 if (len == 0) { 469 /* fragment ack */ 470 flags = 0; 471 } else 472 flags = *pos++; 473 474 if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { 475 if (end - pos < 4) { 476 wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); 477 eap_tnc_set_state(data, FAIL); 478 return; 479 } 480 message_length = WPA_GET_BE32(pos); 481 pos += 4; 482 483 if (message_length < (u32) (end - pos)) { 484 wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " 485 "Length (%d; %ld remaining in this msg)", 486 message_length, (long) (end - pos)); 487 eap_tnc_set_state(data, FAIL); 488 return; 489 } 490 } 491 wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " 492 "Message Length %u", flags, message_length); 493 494 if (data->state == WAIT_FRAG_ACK) { 495 if (len > 1) { 496 wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " 497 "in WAIT_FRAG_ACK state"); 498 eap_tnc_set_state(data, FAIL); 499 return; 500 } 501 wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); 502 eap_tnc_set_state(data, CONTINUE); 503 return; 504 } 505 506 if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { 507 eap_tnc_set_state(data, FAIL); 508 return; 509 } 510 511 if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { 512 if (eap_tnc_process_fragment(data, flags, message_length, 513 pos, end - pos) < 0) 514 eap_tnc_set_state(data, FAIL); 515 else 516 eap_tnc_set_state(data, FRAG_ACK); 517 return; 518 } else if (data->state == FRAG_ACK) { 519 wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); 520 eap_tnc_set_state(data, CONTINUE); 521 } 522 523 if (data->in_buf == NULL) { 524 /* Wrap unfragmented messages as wpabuf without extra copy */ 525 wpabuf_set(&tmpbuf, pos, end - pos); 526 data->in_buf = &tmpbuf; 527 } 528 529 wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload", 530 wpabuf_head(data->in_buf), wpabuf_len(data->in_buf)); 531 tncs_process(data, data->in_buf); 532 533 if (data->in_buf != &tmpbuf) 534 wpabuf_free(data->in_buf); 535 data->in_buf = NULL; 536 } 537 538 539 static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) 540 { 541 struct eap_tnc_data *data = priv; 542 return data->state == DONE || data->state == FAIL; 543 } 544 545 546 static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) 547 { 548 struct eap_tnc_data *data = priv; 549 return data->state == DONE; 550 } 551 552 553 int eap_server_tnc_register(void) 554 { 555 struct eap_method *eap; 556 int ret; 557 558 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 559 EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); 560 if (eap == NULL) 561 return -1; 562 563 eap->init = eap_tnc_init; 564 eap->reset = eap_tnc_reset; 565 eap->buildReq = eap_tnc_buildReq; 566 eap->check = eap_tnc_check; 567 eap->process = eap_tnc_process; 568 eap->isDone = eap_tnc_isDone; 569 eap->isSuccess = eap_tnc_isSuccess; 570 571 ret = eap_server_method_register(eap); 572 if (ret) 573 eap_server_method_free(eap); 574 return ret; 575 } 576