1214501Srpaulo/* 2214501Srpaulo * EAP server method: EAP-TNC (Trusted Network Connect) 3214501Srpaulo * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "includes.h" 10214501Srpaulo 11214501Srpaulo#include "common.h" 12214501Srpaulo#include "eap_i.h" 13214501Srpaulo#include "tncs.h" 14214501Srpaulo 15214501Srpaulo 16214501Srpaulostruct eap_tnc_data { 17214501Srpaulo enum eap_tnc_state { 18214501Srpaulo START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, 19214501Srpaulo FAIL 20214501Srpaulo } state; 21214501Srpaulo enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation; 22214501Srpaulo struct tncs_data *tncs; 23214501Srpaulo struct wpabuf *in_buf; 24214501Srpaulo struct wpabuf *out_buf; 25214501Srpaulo size_t out_used; 26214501Srpaulo size_t fragment_size; 27214501Srpaulo unsigned int was_done:1; 28214501Srpaulo unsigned int was_fail:1; 29214501Srpaulo}; 30214501Srpaulo 31214501Srpaulo 32214501Srpaulo/* EAP-TNC Flags */ 33214501Srpaulo#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 34214501Srpaulo#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 35214501Srpaulo#define EAP_TNC_FLAGS_START 0x20 36214501Srpaulo#define EAP_TNC_VERSION_MASK 0x07 37214501Srpaulo 38214501Srpaulo#define EAP_TNC_VERSION 1 39214501Srpaulo 40214501Srpaulo 41214501Srpaulostatic const char * eap_tnc_state_txt(enum eap_tnc_state state) 42214501Srpaulo{ 43214501Srpaulo switch (state) { 44214501Srpaulo case START: 45214501Srpaulo return "START"; 46214501Srpaulo case CONTINUE: 47214501Srpaulo return "CONTINUE"; 48214501Srpaulo case RECOMMENDATION: 49214501Srpaulo return "RECOMMENDATION"; 50214501Srpaulo case FRAG_ACK: 51214501Srpaulo return "FRAG_ACK"; 52214501Srpaulo case WAIT_FRAG_ACK: 53214501Srpaulo return "WAIT_FRAG_ACK"; 54214501Srpaulo case DONE: 55214501Srpaulo return "DONE"; 56214501Srpaulo case FAIL: 57214501Srpaulo return "FAIL"; 58214501Srpaulo } 59214501Srpaulo return "??"; 60214501Srpaulo} 61214501Srpaulo 62214501Srpaulo 63214501Srpaulostatic void eap_tnc_set_state(struct eap_tnc_data *data, 64214501Srpaulo enum eap_tnc_state new_state) 65214501Srpaulo{ 66214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s", 67214501Srpaulo eap_tnc_state_txt(data->state), 68214501Srpaulo eap_tnc_state_txt(new_state)); 69214501Srpaulo data->state = new_state; 70214501Srpaulo} 71214501Srpaulo 72214501Srpaulo 73214501Srpaulostatic void * eap_tnc_init(struct eap_sm *sm) 74214501Srpaulo{ 75214501Srpaulo struct eap_tnc_data *data; 76214501Srpaulo 77214501Srpaulo data = os_zalloc(sizeof(*data)); 78214501Srpaulo if (data == NULL) 79214501Srpaulo return NULL; 80214501Srpaulo eap_tnc_set_state(data, START); 81214501Srpaulo data->tncs = tncs_init(); 82214501Srpaulo if (data->tncs == NULL) { 83214501Srpaulo os_free(data); 84214501Srpaulo return NULL; 85214501Srpaulo } 86214501Srpaulo 87252726Srpaulo data->fragment_size = sm->fragment_size > 100 ? 88252726Srpaulo sm->fragment_size - 98 : 1300; 89214501Srpaulo 90214501Srpaulo return data; 91214501Srpaulo} 92214501Srpaulo 93214501Srpaulo 94214501Srpaulostatic void eap_tnc_reset(struct eap_sm *sm, void *priv) 95214501Srpaulo{ 96214501Srpaulo struct eap_tnc_data *data = priv; 97214501Srpaulo wpabuf_free(data->in_buf); 98214501Srpaulo wpabuf_free(data->out_buf); 99214501Srpaulo tncs_deinit(data->tncs); 100214501Srpaulo os_free(data); 101214501Srpaulo} 102214501Srpaulo 103214501Srpaulo 104214501Srpaulostatic struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, 105214501Srpaulo struct eap_tnc_data *data, u8 id) 106214501Srpaulo{ 107214501Srpaulo struct wpabuf *req; 108214501Srpaulo 109214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST, 110214501Srpaulo id); 111214501Srpaulo if (req == NULL) { 112214501Srpaulo wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " 113214501Srpaulo "request"); 114214501Srpaulo eap_tnc_set_state(data, FAIL); 115214501Srpaulo return NULL; 116214501Srpaulo } 117214501Srpaulo 118214501Srpaulo wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); 119214501Srpaulo 120214501Srpaulo eap_tnc_set_state(data, CONTINUE); 121214501Srpaulo 122214501Srpaulo return req; 123214501Srpaulo} 124214501Srpaulo 125214501Srpaulo 126214501Srpaulostatic struct wpabuf * eap_tnc_build(struct eap_sm *sm, 127214501Srpaulo struct eap_tnc_data *data) 128214501Srpaulo{ 129214501Srpaulo struct wpabuf *req; 130214501Srpaulo u8 *rpos, *rpos1; 131214501Srpaulo size_t rlen; 132214501Srpaulo char *start_buf, *end_buf; 133214501Srpaulo size_t start_len, end_len; 134214501Srpaulo size_t imv_len; 135214501Srpaulo 136214501Srpaulo imv_len = tncs_total_send_len(data->tncs); 137214501Srpaulo 138214501Srpaulo start_buf = tncs_if_tnccs_start(data->tncs); 139214501Srpaulo if (start_buf == NULL) 140214501Srpaulo return NULL; 141214501Srpaulo start_len = os_strlen(start_buf); 142214501Srpaulo end_buf = tncs_if_tnccs_end(); 143214501Srpaulo if (end_buf == NULL) { 144214501Srpaulo os_free(start_buf); 145214501Srpaulo return NULL; 146214501Srpaulo } 147214501Srpaulo end_len = os_strlen(end_buf); 148214501Srpaulo 149214501Srpaulo rlen = start_len + imv_len + end_len; 150214501Srpaulo req = wpabuf_alloc(rlen); 151214501Srpaulo if (req == NULL) { 152214501Srpaulo os_free(start_buf); 153214501Srpaulo os_free(end_buf); 154214501Srpaulo return NULL; 155214501Srpaulo } 156214501Srpaulo 157214501Srpaulo wpabuf_put_data(req, start_buf, start_len); 158214501Srpaulo os_free(start_buf); 159214501Srpaulo 160214501Srpaulo rpos1 = wpabuf_put(req, 0); 161214501Srpaulo rpos = tncs_copy_send_buf(data->tncs, rpos1); 162214501Srpaulo wpabuf_put(req, rpos - rpos1); 163214501Srpaulo 164214501Srpaulo wpabuf_put_data(req, end_buf, end_len); 165214501Srpaulo os_free(end_buf); 166214501Srpaulo 167214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request", 168214501Srpaulo wpabuf_head(req), wpabuf_len(req)); 169214501Srpaulo 170214501Srpaulo return req; 171214501Srpaulo} 172214501Srpaulo 173214501Srpaulo 174214501Srpaulostatic struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, 175214501Srpaulo struct eap_tnc_data *data) 176214501Srpaulo{ 177214501Srpaulo switch (data->recommendation) { 178214501Srpaulo case ALLOW: 179214501Srpaulo eap_tnc_set_state(data, DONE); 180214501Srpaulo break; 181214501Srpaulo case ISOLATE: 182214501Srpaulo eap_tnc_set_state(data, FAIL); 183214501Srpaulo /* TODO: support assignment to a different VLAN */ 184214501Srpaulo break; 185214501Srpaulo case NO_ACCESS: 186214501Srpaulo eap_tnc_set_state(data, FAIL); 187214501Srpaulo break; 188214501Srpaulo case NO_RECOMMENDATION: 189214501Srpaulo eap_tnc_set_state(data, DONE); 190214501Srpaulo break; 191214501Srpaulo default: 192214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); 193214501Srpaulo return NULL; 194214501Srpaulo } 195214501Srpaulo 196214501Srpaulo return eap_tnc_build(sm, data); 197214501Srpaulo} 198214501Srpaulo 199214501Srpaulo 200214501Srpaulostatic struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) 201214501Srpaulo{ 202214501Srpaulo struct wpabuf *msg; 203214501Srpaulo 204214501Srpaulo msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); 205214501Srpaulo if (msg == NULL) { 206214501Srpaulo wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " 207214501Srpaulo "for fragment ack"); 208214501Srpaulo return NULL; 209214501Srpaulo } 210214501Srpaulo wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ 211214501Srpaulo 212214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); 213214501Srpaulo 214214501Srpaulo return msg; 215214501Srpaulo} 216214501Srpaulo 217214501Srpaulo 218214501Srpaulostatic struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) 219214501Srpaulo{ 220214501Srpaulo struct wpabuf *req; 221214501Srpaulo u8 flags; 222214501Srpaulo size_t send_len, plen; 223214501Srpaulo 224214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request"); 225214501Srpaulo 226214501Srpaulo flags = EAP_TNC_VERSION; 227214501Srpaulo send_len = wpabuf_len(data->out_buf) - data->out_used; 228214501Srpaulo if (1 + send_len > data->fragment_size) { 229214501Srpaulo send_len = data->fragment_size - 1; 230214501Srpaulo flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; 231214501Srpaulo if (data->out_used == 0) { 232214501Srpaulo flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; 233214501Srpaulo send_len -= 4; 234214501Srpaulo } 235214501Srpaulo } 236214501Srpaulo 237214501Srpaulo plen = 1 + send_len; 238214501Srpaulo if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 239214501Srpaulo plen += 4; 240214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, 241214501Srpaulo EAP_CODE_REQUEST, id); 242214501Srpaulo if (req == NULL) 243214501Srpaulo return NULL; 244214501Srpaulo 245214501Srpaulo wpabuf_put_u8(req, flags); /* Flags */ 246214501Srpaulo if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) 247214501Srpaulo wpabuf_put_be32(req, wpabuf_len(data->out_buf)); 248214501Srpaulo 249214501Srpaulo wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, 250214501Srpaulo send_len); 251214501Srpaulo data->out_used += send_len; 252214501Srpaulo 253214501Srpaulo if (data->out_used == wpabuf_len(data->out_buf)) { 254214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 255214501Srpaulo "(message sent completely)", 256214501Srpaulo (unsigned long) send_len); 257214501Srpaulo wpabuf_free(data->out_buf); 258214501Srpaulo data->out_buf = NULL; 259214501Srpaulo data->out_used = 0; 260214501Srpaulo if (data->was_fail) 261214501Srpaulo eap_tnc_set_state(data, FAIL); 262214501Srpaulo else if (data->was_done) 263214501Srpaulo eap_tnc_set_state(data, DONE); 264214501Srpaulo } else { 265214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " 266214501Srpaulo "(%lu more to send)", (unsigned long) send_len, 267214501Srpaulo (unsigned long) wpabuf_len(data->out_buf) - 268214501Srpaulo data->out_used); 269214501Srpaulo if (data->state == FAIL) 270214501Srpaulo data->was_fail = 1; 271214501Srpaulo else if (data->state == DONE) 272214501Srpaulo data->was_done = 1; 273214501Srpaulo eap_tnc_set_state(data, WAIT_FRAG_ACK); 274214501Srpaulo } 275214501Srpaulo 276214501Srpaulo return req; 277214501Srpaulo} 278214501Srpaulo 279214501Srpaulo 280214501Srpaulostatic struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id) 281214501Srpaulo{ 282214501Srpaulo struct eap_tnc_data *data = priv; 283214501Srpaulo 284214501Srpaulo switch (data->state) { 285214501Srpaulo case START: 286214501Srpaulo tncs_init_connection(data->tncs); 287214501Srpaulo return eap_tnc_build_start(sm, data, id); 288214501Srpaulo case CONTINUE: 289214501Srpaulo if (data->out_buf == NULL) { 290214501Srpaulo data->out_buf = eap_tnc_build(sm, data); 291214501Srpaulo if (data->out_buf == NULL) { 292214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " 293214501Srpaulo "generate message"); 294214501Srpaulo return NULL; 295214501Srpaulo } 296214501Srpaulo data->out_used = 0; 297214501Srpaulo } 298214501Srpaulo return eap_tnc_build_msg(data, id); 299214501Srpaulo case RECOMMENDATION: 300214501Srpaulo if (data->out_buf == NULL) { 301214501Srpaulo data->out_buf = eap_tnc_build_recommendation(sm, data); 302214501Srpaulo if (data->out_buf == NULL) { 303214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " 304214501Srpaulo "generate recommendation message"); 305214501Srpaulo return NULL; 306214501Srpaulo } 307214501Srpaulo data->out_used = 0; 308214501Srpaulo } 309214501Srpaulo return eap_tnc_build_msg(data, id); 310214501Srpaulo case WAIT_FRAG_ACK: 311214501Srpaulo return eap_tnc_build_msg(data, id); 312214501Srpaulo case FRAG_ACK: 313214501Srpaulo return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST); 314214501Srpaulo case DONE: 315214501Srpaulo case FAIL: 316214501Srpaulo return NULL; 317214501Srpaulo } 318214501Srpaulo 319214501Srpaulo return NULL; 320214501Srpaulo} 321214501Srpaulo 322214501Srpaulo 323214501Srpaulostatic Boolean eap_tnc_check(struct eap_sm *sm, void *priv, 324214501Srpaulo struct wpabuf *respData) 325214501Srpaulo{ 326214501Srpaulo struct eap_tnc_data *data = priv; 327214501Srpaulo const u8 *pos; 328214501Srpaulo size_t len; 329214501Srpaulo 330214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, 331214501Srpaulo &len); 332214501Srpaulo if (pos == NULL) { 333214501Srpaulo wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame"); 334214501Srpaulo return TRUE; 335214501Srpaulo } 336214501Srpaulo 337214501Srpaulo if (len == 0 && data->state != WAIT_FRAG_ACK) { 338214501Srpaulo wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)"); 339214501Srpaulo return TRUE; 340214501Srpaulo } 341214501Srpaulo 342214501Srpaulo if (len == 0) 343214501Srpaulo return FALSE; /* Fragment ACK does not include flags */ 344214501Srpaulo 345214501Srpaulo if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { 346214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", 347214501Srpaulo *pos & EAP_TNC_VERSION_MASK); 348214501Srpaulo return TRUE; 349214501Srpaulo } 350214501Srpaulo 351214501Srpaulo if (*pos & EAP_TNC_FLAGS_START) { 352214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag"); 353214501Srpaulo return TRUE; 354214501Srpaulo } 355214501Srpaulo 356214501Srpaulo return FALSE; 357214501Srpaulo} 358214501Srpaulo 359214501Srpaulo 360214501Srpaulostatic void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) 361214501Srpaulo{ 362214501Srpaulo enum tncs_process_res res; 363214501Srpaulo 364214501Srpaulo res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf), 365214501Srpaulo wpabuf_len(inbuf)); 366214501Srpaulo switch (res) { 367214501Srpaulo case TNCCS_RECOMMENDATION_ALLOW: 368214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); 369214501Srpaulo eap_tnc_set_state(data, RECOMMENDATION); 370214501Srpaulo data->recommendation = ALLOW; 371214501Srpaulo break; 372214501Srpaulo case TNCCS_RECOMMENDATION_NO_RECOMMENDATION: 373214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation"); 374214501Srpaulo eap_tnc_set_state(data, RECOMMENDATION); 375214501Srpaulo data->recommendation = NO_RECOMMENDATION; 376214501Srpaulo break; 377214501Srpaulo case TNCCS_RECOMMENDATION_ISOLATE: 378214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation"); 379214501Srpaulo eap_tnc_set_state(data, RECOMMENDATION); 380214501Srpaulo data->recommendation = ISOLATE; 381214501Srpaulo break; 382214501Srpaulo case TNCCS_RECOMMENDATION_NO_ACCESS: 383214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); 384214501Srpaulo eap_tnc_set_state(data, RECOMMENDATION); 385214501Srpaulo data->recommendation = NO_ACCESS; 386214501Srpaulo break; 387214501Srpaulo case TNCCS_PROCESS_ERROR: 388214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); 389214501Srpaulo eap_tnc_set_state(data, FAIL); 390214501Srpaulo break; 391214501Srpaulo default: 392214501Srpaulo break; 393214501Srpaulo } 394214501Srpaulo} 395214501Srpaulo 396214501Srpaulo 397214501Srpaulostatic int eap_tnc_process_cont(struct eap_tnc_data *data, 398214501Srpaulo const u8 *buf, size_t len) 399214501Srpaulo{ 400214501Srpaulo /* Process continuation of a pending message */ 401214501Srpaulo if (len > wpabuf_tailroom(data->in_buf)) { 402214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); 403214501Srpaulo eap_tnc_set_state(data, FAIL); 404214501Srpaulo return -1; 405214501Srpaulo } 406214501Srpaulo 407214501Srpaulo wpabuf_put_data(data->in_buf, buf, len); 408214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu " 409214501Srpaulo "bytes more", (unsigned long) len, 410214501Srpaulo (unsigned long) wpabuf_tailroom(data->in_buf)); 411214501Srpaulo 412214501Srpaulo return 0; 413214501Srpaulo} 414214501Srpaulo 415214501Srpaulo 416214501Srpaulostatic int eap_tnc_process_fragment(struct eap_tnc_data *data, 417214501Srpaulo u8 flags, u32 message_length, 418214501Srpaulo const u8 *buf, size_t len) 419214501Srpaulo{ 420214501Srpaulo /* Process a fragment that is not the last one of the message */ 421214501Srpaulo if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { 422214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " 423214501Srpaulo "fragmented packet"); 424214501Srpaulo return -1; 425214501Srpaulo } 426214501Srpaulo 427214501Srpaulo if (data->in_buf == NULL) { 428214501Srpaulo /* First fragment of the message */ 429214501Srpaulo data->in_buf = wpabuf_alloc(message_length); 430214501Srpaulo if (data->in_buf == NULL) { 431214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " 432214501Srpaulo "message"); 433214501Srpaulo return -1; 434214501Srpaulo } 435214501Srpaulo wpabuf_put_data(data->in_buf, buf, len); 436214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " 437214501Srpaulo "fragment, waiting for %lu bytes more", 438214501Srpaulo (unsigned long) len, 439214501Srpaulo (unsigned long) wpabuf_tailroom(data->in_buf)); 440214501Srpaulo } 441214501Srpaulo 442214501Srpaulo return 0; 443214501Srpaulo} 444214501Srpaulo 445214501Srpaulo 446214501Srpaulostatic void eap_tnc_process(struct eap_sm *sm, void *priv, 447214501Srpaulo struct wpabuf *respData) 448214501Srpaulo{ 449214501Srpaulo struct eap_tnc_data *data = priv; 450214501Srpaulo const u8 *pos, *end; 451214501Srpaulo size_t len; 452214501Srpaulo u8 flags; 453214501Srpaulo u32 message_length = 0; 454214501Srpaulo struct wpabuf tmpbuf; 455214501Srpaulo 456214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); 457214501Srpaulo if (pos == NULL) 458214501Srpaulo return; /* Should not happen; message already verified */ 459214501Srpaulo 460214501Srpaulo end = pos + len; 461214501Srpaulo 462214501Srpaulo if (len == 1 && (data->state == DONE || data->state == FAIL)) { 463214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last " 464214501Srpaulo "message"); 465214501Srpaulo return; 466214501Srpaulo } 467214501Srpaulo 468214501Srpaulo if (len == 0) { 469214501Srpaulo /* fragment ack */ 470214501Srpaulo flags = 0; 471214501Srpaulo } else 472214501Srpaulo flags = *pos++; 473214501Srpaulo 474214501Srpaulo if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { 475214501Srpaulo if (end - pos < 4) { 476214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); 477214501Srpaulo eap_tnc_set_state(data, FAIL); 478214501Srpaulo return; 479214501Srpaulo } 480214501Srpaulo message_length = WPA_GET_BE32(pos); 481214501Srpaulo pos += 4; 482214501Srpaulo 483214501Srpaulo if (message_length < (u32) (end - pos)) { 484214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " 485214501Srpaulo "Length (%d; %ld remaining in this msg)", 486214501Srpaulo message_length, (long) (end - pos)); 487214501Srpaulo eap_tnc_set_state(data, FAIL); 488214501Srpaulo return; 489214501Srpaulo } 490214501Srpaulo } 491214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " 492214501Srpaulo "Message Length %u", flags, message_length); 493214501Srpaulo 494214501Srpaulo if (data->state == WAIT_FRAG_ACK) { 495214501Srpaulo if (len > 1) { 496214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " 497214501Srpaulo "in WAIT_FRAG_ACK state"); 498214501Srpaulo eap_tnc_set_state(data, FAIL); 499214501Srpaulo return; 500214501Srpaulo } 501214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); 502214501Srpaulo eap_tnc_set_state(data, CONTINUE); 503214501Srpaulo return; 504214501Srpaulo } 505214501Srpaulo 506214501Srpaulo if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { 507214501Srpaulo eap_tnc_set_state(data, FAIL); 508214501Srpaulo return; 509214501Srpaulo } 510214501Srpaulo 511214501Srpaulo if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { 512214501Srpaulo if (eap_tnc_process_fragment(data, flags, message_length, 513214501Srpaulo pos, end - pos) < 0) 514214501Srpaulo eap_tnc_set_state(data, FAIL); 515214501Srpaulo else 516214501Srpaulo eap_tnc_set_state(data, FRAG_ACK); 517214501Srpaulo return; 518214501Srpaulo } else if (data->state == FRAG_ACK) { 519214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); 520214501Srpaulo eap_tnc_set_state(data, CONTINUE); 521214501Srpaulo } 522214501Srpaulo 523214501Srpaulo if (data->in_buf == NULL) { 524214501Srpaulo /* Wrap unfragmented messages as wpabuf without extra copy */ 525214501Srpaulo wpabuf_set(&tmpbuf, pos, end - pos); 526214501Srpaulo data->in_buf = &tmpbuf; 527214501Srpaulo } 528214501Srpaulo 529214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload", 530214501Srpaulo wpabuf_head(data->in_buf), wpabuf_len(data->in_buf)); 531214501Srpaulo tncs_process(data, data->in_buf); 532214501Srpaulo 533214501Srpaulo if (data->in_buf != &tmpbuf) 534214501Srpaulo wpabuf_free(data->in_buf); 535214501Srpaulo data->in_buf = NULL; 536214501Srpaulo} 537214501Srpaulo 538214501Srpaulo 539214501Srpaulostatic Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) 540214501Srpaulo{ 541214501Srpaulo struct eap_tnc_data *data = priv; 542214501Srpaulo return data->state == DONE || data->state == FAIL; 543214501Srpaulo} 544214501Srpaulo 545214501Srpaulo 546214501Srpaulostatic Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) 547214501Srpaulo{ 548214501Srpaulo struct eap_tnc_data *data = priv; 549214501Srpaulo return data->state == DONE; 550214501Srpaulo} 551214501Srpaulo 552214501Srpaulo 553214501Srpauloint eap_server_tnc_register(void) 554214501Srpaulo{ 555214501Srpaulo struct eap_method *eap; 556214501Srpaulo int ret; 557214501Srpaulo 558214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 559214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); 560214501Srpaulo if (eap == NULL) 561214501Srpaulo return -1; 562214501Srpaulo 563214501Srpaulo eap->init = eap_tnc_init; 564214501Srpaulo eap->reset = eap_tnc_reset; 565214501Srpaulo eap->buildReq = eap_tnc_buildReq; 566214501Srpaulo eap->check = eap_tnc_check; 567214501Srpaulo eap->process = eap_tnc_process; 568214501Srpaulo eap->isDone = eap_tnc_isDone; 569214501Srpaulo eap->isSuccess = eap_tnc_isSuccess; 570214501Srpaulo 571214501Srpaulo ret = eap_server_method_register(eap); 572214501Srpaulo if (ret) 573214501Srpaulo eap_server_method_free(eap); 574214501Srpaulo return ret; 575214501Srpaulo} 576