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