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