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