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