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