1214501Srpaulo/* 2214501Srpaulo * hostapd / EAP-PAX (RFC 4746) server 3214501Srpaulo * Copyright (c) 2005-2007, 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" 12252726Srpaulo#include "crypto/random.h" 13214501Srpaulo#include "eap_server/eap_i.h" 14214501Srpaulo#include "eap_common/eap_pax_common.h" 15214501Srpaulo 16214501Srpaulo/* 17214501Srpaulo * Note: only PAX_STD subprotocol is currently supported 18214501Srpaulo * 19214501Srpaulo * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite 20214501Srpaulo * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and 21214501Srpaulo * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), 22214501Srpaulo * RSAES-OAEP). 23214501Srpaulo */ 24214501Srpaulo 25214501Srpaulostruct eap_pax_data { 26214501Srpaulo enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state; 27214501Srpaulo u8 mac_id; 28214501Srpaulo union { 29214501Srpaulo u8 e[2 * EAP_PAX_RAND_LEN]; 30214501Srpaulo struct { 31214501Srpaulo u8 x[EAP_PAX_RAND_LEN]; /* server rand */ 32214501Srpaulo u8 y[EAP_PAX_RAND_LEN]; /* client rand */ 33214501Srpaulo } r; 34214501Srpaulo } rand; 35214501Srpaulo u8 ak[EAP_PAX_AK_LEN]; 36214501Srpaulo u8 mk[EAP_PAX_MK_LEN]; 37214501Srpaulo u8 ck[EAP_PAX_CK_LEN]; 38214501Srpaulo u8 ick[EAP_PAX_ICK_LEN]; 39214501Srpaulo int keys_set; 40214501Srpaulo char *cid; 41214501Srpaulo size_t cid_len; 42214501Srpaulo}; 43214501Srpaulo 44214501Srpaulo 45214501Srpaulostatic void * eap_pax_init(struct eap_sm *sm) 46214501Srpaulo{ 47214501Srpaulo struct eap_pax_data *data; 48214501Srpaulo 49214501Srpaulo data = os_zalloc(sizeof(*data)); 50214501Srpaulo if (data == NULL) 51214501Srpaulo return NULL; 52214501Srpaulo data->state = PAX_STD_1; 53214501Srpaulo /* 54214501Srpaulo * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is 55214501Srpaulo * supported 56214501Srpaulo */ 57214501Srpaulo data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; 58214501Srpaulo 59214501Srpaulo return data; 60214501Srpaulo} 61214501Srpaulo 62214501Srpaulo 63214501Srpaulostatic void eap_pax_reset(struct eap_sm *sm, void *priv) 64214501Srpaulo{ 65214501Srpaulo struct eap_pax_data *data = priv; 66214501Srpaulo os_free(data->cid); 67214501Srpaulo os_free(data); 68214501Srpaulo} 69214501Srpaulo 70214501Srpaulo 71214501Srpaulostatic struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, 72214501Srpaulo struct eap_pax_data *data, u8 id) 73214501Srpaulo{ 74214501Srpaulo struct wpabuf *req; 75214501Srpaulo struct eap_pax_hdr *pax; 76214501Srpaulo u8 *pos; 77214501Srpaulo 78214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); 79214501Srpaulo 80252726Srpaulo if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) { 81214501Srpaulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); 82214501Srpaulo data->state = FAILURE; 83214501Srpaulo return NULL; 84214501Srpaulo } 85214501Srpaulo 86214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 87214501Srpaulo sizeof(*pax) + 2 + EAP_PAX_RAND_LEN + 88214501Srpaulo EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); 89214501Srpaulo if (req == NULL) { 90214501Srpaulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " 91214501Srpaulo "request"); 92214501Srpaulo data->state = FAILURE; 93214501Srpaulo return NULL; 94214501Srpaulo } 95214501Srpaulo 96214501Srpaulo pax = wpabuf_put(req, sizeof(*pax)); 97214501Srpaulo pax->op_code = EAP_PAX_OP_STD_1; 98214501Srpaulo pax->flags = 0; 99214501Srpaulo pax->mac_id = data->mac_id; 100214501Srpaulo pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; 101214501Srpaulo pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; 102214501Srpaulo 103214501Srpaulo wpabuf_put_be16(req, EAP_PAX_RAND_LEN); 104214501Srpaulo wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN); 105214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", 106214501Srpaulo data->rand.r.x, EAP_PAX_RAND_LEN); 107214501Srpaulo 108214501Srpaulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 109214501Srpaulo eap_pax_mac(data->mac_id, (u8 *) "", 0, 110214501Srpaulo wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, 111214501Srpaulo NULL, 0, NULL, 0, pos); 112214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 113214501Srpaulo 114214501Srpaulo return req; 115214501Srpaulo} 116214501Srpaulo 117214501Srpaulo 118214501Srpaulostatic struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, 119214501Srpaulo struct eap_pax_data *data, u8 id) 120214501Srpaulo{ 121214501Srpaulo struct wpabuf *req; 122214501Srpaulo struct eap_pax_hdr *pax; 123214501Srpaulo u8 *pos; 124214501Srpaulo 125214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); 126214501Srpaulo 127214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 128214501Srpaulo sizeof(*pax) + 2 + EAP_PAX_MAC_LEN + 129214501Srpaulo EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); 130214501Srpaulo if (req == NULL) { 131214501Srpaulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " 132214501Srpaulo "request"); 133214501Srpaulo data->state = FAILURE; 134214501Srpaulo return NULL; 135214501Srpaulo } 136214501Srpaulo 137214501Srpaulo pax = wpabuf_put(req, sizeof(*pax)); 138214501Srpaulo pax->op_code = EAP_PAX_OP_STD_3; 139214501Srpaulo pax->flags = 0; 140214501Srpaulo pax->mac_id = data->mac_id; 141214501Srpaulo pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; 142214501Srpaulo pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; 143214501Srpaulo 144214501Srpaulo wpabuf_put_be16(req, EAP_PAX_MAC_LEN); 145214501Srpaulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 146214501Srpaulo eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 147214501Srpaulo data->rand.r.y, EAP_PAX_RAND_LEN, 148214501Srpaulo (u8 *) data->cid, data->cid_len, NULL, 0, pos); 149214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", 150214501Srpaulo pos, EAP_PAX_MAC_LEN); 151214501Srpaulo pos += EAP_PAX_MAC_LEN; 152214501Srpaulo 153214501Srpaulo /* Optional ADE could be added here, if needed */ 154214501Srpaulo 155214501Srpaulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 156214501Srpaulo eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 157214501Srpaulo wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, 158214501Srpaulo NULL, 0, NULL, 0, pos); 159214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 160214501Srpaulo 161214501Srpaulo return req; 162214501Srpaulo} 163214501Srpaulo 164214501Srpaulo 165214501Srpaulostatic struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id) 166214501Srpaulo{ 167214501Srpaulo struct eap_pax_data *data = priv; 168214501Srpaulo 169214501Srpaulo switch (data->state) { 170214501Srpaulo case PAX_STD_1: 171214501Srpaulo return eap_pax_build_std_1(sm, data, id); 172214501Srpaulo case PAX_STD_3: 173214501Srpaulo return eap_pax_build_std_3(sm, data, id); 174214501Srpaulo default: 175214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", 176214501Srpaulo data->state); 177214501Srpaulo break; 178214501Srpaulo } 179214501Srpaulo return NULL; 180214501Srpaulo} 181214501Srpaulo 182214501Srpaulo 183214501Srpaulostatic Boolean eap_pax_check(struct eap_sm *sm, void *priv, 184214501Srpaulo struct wpabuf *respData) 185214501Srpaulo{ 186214501Srpaulo struct eap_pax_data *data = priv; 187214501Srpaulo struct eap_pax_hdr *resp; 188214501Srpaulo const u8 *pos; 189214501Srpaulo size_t len, mlen; 190214501Srpaulo u8 icvbuf[EAP_PAX_ICV_LEN], *icv; 191214501Srpaulo 192214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 193214501Srpaulo if (pos == NULL || len < sizeof(*resp)) { 194214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); 195214501Srpaulo return TRUE; 196214501Srpaulo } 197214501Srpaulo 198214501Srpaulo mlen = sizeof(struct eap_hdr) + 1 + len; 199214501Srpaulo resp = (struct eap_pax_hdr *) pos; 200214501Srpaulo 201214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " 202214501Srpaulo "flags 0x%x mac_id 0x%x dh_group_id 0x%x " 203214501Srpaulo "public_key_id 0x%x", 204214501Srpaulo resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, 205214501Srpaulo resp->public_key_id); 206214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", 207214501Srpaulo (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); 208214501Srpaulo 209214501Srpaulo if (data->state == PAX_STD_1 && 210214501Srpaulo resp->op_code != EAP_PAX_OP_STD_2) { 211214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " 212214501Srpaulo "ignore op %d", resp->op_code); 213214501Srpaulo return TRUE; 214214501Srpaulo } 215214501Srpaulo 216214501Srpaulo if (data->state == PAX_STD_3 && 217214501Srpaulo resp->op_code != EAP_PAX_OP_ACK) { 218214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " 219214501Srpaulo "ignore op %d", resp->op_code); 220214501Srpaulo return TRUE; 221214501Srpaulo } 222214501Srpaulo 223214501Srpaulo if (resp->op_code != EAP_PAX_OP_STD_2 && 224214501Srpaulo resp->op_code != EAP_PAX_OP_ACK) { 225214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", 226214501Srpaulo resp->op_code); 227214501Srpaulo } 228214501Srpaulo 229214501Srpaulo if (data->mac_id != resp->mac_id) { 230214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " 231214501Srpaulo "received 0x%x", data->mac_id, resp->mac_id); 232214501Srpaulo return TRUE; 233214501Srpaulo } 234214501Srpaulo 235214501Srpaulo if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { 236214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " 237214501Srpaulo "received 0x%x", EAP_PAX_DH_GROUP_NONE, 238214501Srpaulo resp->dh_group_id); 239214501Srpaulo return TRUE; 240214501Srpaulo } 241214501Srpaulo 242214501Srpaulo if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { 243214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " 244214501Srpaulo "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, 245214501Srpaulo resp->public_key_id); 246214501Srpaulo return TRUE; 247214501Srpaulo } 248214501Srpaulo 249214501Srpaulo if (resp->flags & EAP_PAX_FLAGS_MF) { 250214501Srpaulo /* TODO: add support for reassembling fragments */ 251214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); 252214501Srpaulo return TRUE; 253214501Srpaulo } 254214501Srpaulo 255214501Srpaulo if (resp->flags & EAP_PAX_FLAGS_CE) { 256214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); 257214501Srpaulo return TRUE; 258214501Srpaulo } 259214501Srpaulo 260214501Srpaulo if (data->keys_set) { 261214501Srpaulo if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { 262214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); 263214501Srpaulo return TRUE; 264214501Srpaulo } 265214501Srpaulo icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; 266214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); 267214501Srpaulo eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 268214501Srpaulo wpabuf_mhead(respData), 269214501Srpaulo wpabuf_len(respData) - EAP_PAX_ICV_LEN, 270214501Srpaulo NULL, 0, NULL, 0, icvbuf); 271214501Srpaulo if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { 272214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); 273214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", 274214501Srpaulo icvbuf, EAP_PAX_ICV_LEN); 275214501Srpaulo return TRUE; 276214501Srpaulo } 277214501Srpaulo } 278214501Srpaulo 279214501Srpaulo return FALSE; 280214501Srpaulo} 281214501Srpaulo 282214501Srpaulo 283214501Srpaulostatic void eap_pax_process_std_2(struct eap_sm *sm, 284214501Srpaulo struct eap_pax_data *data, 285214501Srpaulo struct wpabuf *respData) 286214501Srpaulo{ 287214501Srpaulo struct eap_pax_hdr *resp; 288214501Srpaulo u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; 289214501Srpaulo const u8 *pos; 290214501Srpaulo size_t len, left; 291214501Srpaulo int i; 292214501Srpaulo 293214501Srpaulo if (data->state != PAX_STD_1) 294214501Srpaulo return; 295214501Srpaulo 296214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); 297214501Srpaulo 298214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 299214501Srpaulo if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) 300214501Srpaulo return; 301214501Srpaulo 302214501Srpaulo resp = (struct eap_pax_hdr *) pos; 303214501Srpaulo pos = (u8 *) (resp + 1); 304214501Srpaulo left = len - sizeof(*resp); 305214501Srpaulo 306214501Srpaulo if (left < 2 + EAP_PAX_RAND_LEN || 307214501Srpaulo WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { 308214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); 309214501Srpaulo return; 310214501Srpaulo } 311214501Srpaulo pos += 2; 312214501Srpaulo left -= 2; 313214501Srpaulo os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); 314214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", 315214501Srpaulo data->rand.r.y, EAP_PAX_RAND_LEN); 316214501Srpaulo pos += EAP_PAX_RAND_LEN; 317214501Srpaulo left -= EAP_PAX_RAND_LEN; 318214501Srpaulo 319214501Srpaulo if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) { 320214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); 321214501Srpaulo return; 322214501Srpaulo } 323214501Srpaulo data->cid_len = WPA_GET_BE16(pos); 324214501Srpaulo os_free(data->cid); 325214501Srpaulo data->cid = os_malloc(data->cid_len); 326214501Srpaulo if (data->cid == NULL) { 327214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " 328214501Srpaulo "CID"); 329214501Srpaulo return; 330214501Srpaulo } 331214501Srpaulo os_memcpy(data->cid, pos + 2, data->cid_len); 332214501Srpaulo pos += 2 + data->cid_len; 333214501Srpaulo left -= 2 + data->cid_len; 334214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", 335214501Srpaulo (u8 *) data->cid, data->cid_len); 336214501Srpaulo 337214501Srpaulo if (left < 2 + EAP_PAX_MAC_LEN || 338214501Srpaulo WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { 339214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); 340214501Srpaulo return; 341214501Srpaulo } 342214501Srpaulo pos += 2; 343214501Srpaulo left -= 2; 344214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", 345214501Srpaulo pos, EAP_PAX_MAC_LEN); 346214501Srpaulo 347214501Srpaulo if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { 348214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", 349214501Srpaulo (u8 *) data->cid, data->cid_len); 350214501Srpaulo data->state = FAILURE; 351214501Srpaulo return; 352214501Srpaulo } 353214501Srpaulo 354214501Srpaulo for (i = 0; 355214501Srpaulo i < EAP_MAX_METHODS && 356214501Srpaulo (sm->user->methods[i].vendor != EAP_VENDOR_IETF || 357214501Srpaulo sm->user->methods[i].method != EAP_TYPE_NONE); 358214501Srpaulo i++) { 359214501Srpaulo if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && 360214501Srpaulo sm->user->methods[i].method == EAP_TYPE_PAX) 361214501Srpaulo break; 362214501Srpaulo } 363214501Srpaulo 364214501Srpaulo if (i >= EAP_MAX_METHODS || 365214501Srpaulo sm->user->methods[i].vendor != EAP_VENDOR_IETF || 366214501Srpaulo sm->user->methods[i].method != EAP_TYPE_PAX) { 367214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, 368214501Srpaulo "EAP-PAX: EAP-PAX not enabled for CID", 369214501Srpaulo (u8 *) data->cid, data->cid_len); 370214501Srpaulo data->state = FAILURE; 371214501Srpaulo return; 372214501Srpaulo } 373214501Srpaulo 374214501Srpaulo if (sm->user->password == NULL || 375214501Srpaulo sm->user->password_len != EAP_PAX_AK_LEN) { 376214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " 377214501Srpaulo "user database for CID", 378214501Srpaulo (u8 *) data->cid, data->cid_len); 379214501Srpaulo data->state = FAILURE; 380214501Srpaulo return; 381214501Srpaulo } 382214501Srpaulo os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); 383214501Srpaulo 384214501Srpaulo if (eap_pax_initial_key_derivation(data->mac_id, data->ak, 385214501Srpaulo data->rand.e, data->mk, data->ck, 386214501Srpaulo data->ick) < 0) { 387214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " 388214501Srpaulo "key derivation"); 389214501Srpaulo data->state = FAILURE; 390214501Srpaulo return; 391214501Srpaulo } 392214501Srpaulo data->keys_set = 1; 393214501Srpaulo 394214501Srpaulo eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 395214501Srpaulo data->rand.r.x, EAP_PAX_RAND_LEN, 396214501Srpaulo data->rand.r.y, EAP_PAX_RAND_LEN, 397214501Srpaulo (u8 *) data->cid, data->cid_len, mac); 398214501Srpaulo if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { 399214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " 400214501Srpaulo "PAX_STD-2"); 401214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", 402214501Srpaulo mac, EAP_PAX_MAC_LEN); 403214501Srpaulo data->state = FAILURE; 404214501Srpaulo return; 405214501Srpaulo } 406214501Srpaulo 407214501Srpaulo pos += EAP_PAX_MAC_LEN; 408214501Srpaulo left -= EAP_PAX_MAC_LEN; 409214501Srpaulo 410214501Srpaulo if (left < EAP_PAX_ICV_LEN) { 411214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " 412214501Srpaulo "PAX_STD-2", (unsigned long) left); 413214501Srpaulo return; 414214501Srpaulo } 415214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 416214501Srpaulo eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 417214501Srpaulo wpabuf_head(respData), 418214501Srpaulo wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, 419214501Srpaulo icvbuf); 420214501Srpaulo if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { 421214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); 422214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", 423214501Srpaulo icvbuf, EAP_PAX_ICV_LEN); 424214501Srpaulo return; 425214501Srpaulo } 426214501Srpaulo pos += EAP_PAX_ICV_LEN; 427214501Srpaulo left -= EAP_PAX_ICV_LEN; 428214501Srpaulo 429214501Srpaulo if (left > 0) { 430214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 431214501Srpaulo pos, left); 432214501Srpaulo } 433214501Srpaulo 434214501Srpaulo data->state = PAX_STD_3; 435214501Srpaulo} 436214501Srpaulo 437214501Srpaulo 438214501Srpaulostatic void eap_pax_process_ack(struct eap_sm *sm, 439214501Srpaulo struct eap_pax_data *data, 440214501Srpaulo struct wpabuf *respData) 441214501Srpaulo{ 442214501Srpaulo if (data->state != PAX_STD_3) 443214501Srpaulo return; 444214501Srpaulo 445214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " 446214501Srpaulo "completed successfully"); 447214501Srpaulo data->state = SUCCESS; 448214501Srpaulo} 449214501Srpaulo 450214501Srpaulo 451214501Srpaulostatic void eap_pax_process(struct eap_sm *sm, void *priv, 452214501Srpaulo struct wpabuf *respData) 453214501Srpaulo{ 454214501Srpaulo struct eap_pax_data *data = priv; 455214501Srpaulo struct eap_pax_hdr *resp; 456214501Srpaulo const u8 *pos; 457214501Srpaulo size_t len; 458214501Srpaulo 459214501Srpaulo if (sm->user == NULL || sm->user->password == NULL) { 460214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not " 461214501Srpaulo "configured"); 462214501Srpaulo data->state = FAILURE; 463214501Srpaulo return; 464214501Srpaulo } 465214501Srpaulo 466214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 467214501Srpaulo if (pos == NULL || len < sizeof(*resp)) 468214501Srpaulo return; 469214501Srpaulo 470214501Srpaulo resp = (struct eap_pax_hdr *) pos; 471214501Srpaulo 472214501Srpaulo switch (resp->op_code) { 473214501Srpaulo case EAP_PAX_OP_STD_2: 474214501Srpaulo eap_pax_process_std_2(sm, data, respData); 475214501Srpaulo break; 476214501Srpaulo case EAP_PAX_OP_ACK: 477214501Srpaulo eap_pax_process_ack(sm, data, respData); 478214501Srpaulo break; 479214501Srpaulo } 480214501Srpaulo} 481214501Srpaulo 482214501Srpaulo 483214501Srpaulostatic Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) 484214501Srpaulo{ 485214501Srpaulo struct eap_pax_data *data = priv; 486214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 487214501Srpaulo} 488214501Srpaulo 489214501Srpaulo 490214501Srpaulostatic u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) 491214501Srpaulo{ 492214501Srpaulo struct eap_pax_data *data = priv; 493214501Srpaulo u8 *key; 494214501Srpaulo 495214501Srpaulo if (data->state != SUCCESS) 496214501Srpaulo return NULL; 497214501Srpaulo 498214501Srpaulo key = os_malloc(EAP_MSK_LEN); 499214501Srpaulo if (key == NULL) 500214501Srpaulo return NULL; 501214501Srpaulo 502214501Srpaulo *len = EAP_MSK_LEN; 503214501Srpaulo eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 504214501Srpaulo "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, 505214501Srpaulo EAP_MSK_LEN, key); 506214501Srpaulo 507214501Srpaulo return key; 508214501Srpaulo} 509214501Srpaulo 510214501Srpaulo 511214501Srpaulostatic u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 512214501Srpaulo{ 513214501Srpaulo struct eap_pax_data *data = priv; 514214501Srpaulo u8 *key; 515214501Srpaulo 516214501Srpaulo if (data->state != SUCCESS) 517214501Srpaulo return NULL; 518214501Srpaulo 519214501Srpaulo key = os_malloc(EAP_EMSK_LEN); 520214501Srpaulo if (key == NULL) 521214501Srpaulo return NULL; 522214501Srpaulo 523214501Srpaulo *len = EAP_EMSK_LEN; 524214501Srpaulo eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 525214501Srpaulo "Extended Master Session Key", 526214501Srpaulo data->rand.e, 2 * EAP_PAX_RAND_LEN, 527214501Srpaulo EAP_EMSK_LEN, key); 528214501Srpaulo 529214501Srpaulo return key; 530214501Srpaulo} 531214501Srpaulo 532214501Srpaulo 533214501Srpaulostatic Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) 534214501Srpaulo{ 535214501Srpaulo struct eap_pax_data *data = priv; 536214501Srpaulo return data->state == SUCCESS; 537214501Srpaulo} 538214501Srpaulo 539214501Srpaulo 540214501Srpauloint eap_server_pax_register(void) 541214501Srpaulo{ 542214501Srpaulo struct eap_method *eap; 543214501Srpaulo int ret; 544214501Srpaulo 545214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 546214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); 547214501Srpaulo if (eap == NULL) 548214501Srpaulo return -1; 549214501Srpaulo 550214501Srpaulo eap->init = eap_pax_init; 551214501Srpaulo eap->reset = eap_pax_reset; 552214501Srpaulo eap->buildReq = eap_pax_buildReq; 553214501Srpaulo eap->check = eap_pax_check; 554214501Srpaulo eap->process = eap_pax_process; 555214501Srpaulo eap->isDone = eap_pax_isDone; 556214501Srpaulo eap->getKey = eap_pax_getKey; 557214501Srpaulo eap->isSuccess = eap_pax_isSuccess; 558214501Srpaulo eap->get_emsk = eap_pax_get_emsk; 559214501Srpaulo 560214501Srpaulo ret = eap_server_method_register(eap); 561214501Srpaulo if (ret) 562214501Srpaulo eap_server_method_free(eap); 563214501Srpaulo return ret; 564214501Srpaulo} 565