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]; 39281806Srpaulo u8 mid[EAP_PAX_MID_LEN]; 40214501Srpaulo int keys_set; 41214501Srpaulo char *cid; 42214501Srpaulo size_t cid_len; 43214501Srpaulo}; 44214501Srpaulo 45214501Srpaulo 46214501Srpaulostatic void * eap_pax_init(struct eap_sm *sm) 47214501Srpaulo{ 48214501Srpaulo struct eap_pax_data *data; 49214501Srpaulo 50214501Srpaulo data = os_zalloc(sizeof(*data)); 51214501Srpaulo if (data == NULL) 52214501Srpaulo return NULL; 53214501Srpaulo data->state = PAX_STD_1; 54214501Srpaulo /* 55214501Srpaulo * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is 56214501Srpaulo * supported 57214501Srpaulo */ 58214501Srpaulo data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; 59214501Srpaulo 60214501Srpaulo return data; 61214501Srpaulo} 62214501Srpaulo 63214501Srpaulo 64214501Srpaulostatic void eap_pax_reset(struct eap_sm *sm, void *priv) 65214501Srpaulo{ 66214501Srpaulo struct eap_pax_data *data = priv; 67214501Srpaulo os_free(data->cid); 68281806Srpaulo bin_clear_free(data, sizeof(*data)); 69214501Srpaulo} 70214501Srpaulo 71214501Srpaulo 72214501Srpaulostatic struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, 73214501Srpaulo struct eap_pax_data *data, u8 id) 74214501Srpaulo{ 75214501Srpaulo struct wpabuf *req; 76214501Srpaulo struct eap_pax_hdr *pax; 77214501Srpaulo u8 *pos; 78214501Srpaulo 79214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); 80214501Srpaulo 81252726Srpaulo if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) { 82214501Srpaulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); 83214501Srpaulo data->state = FAILURE; 84214501Srpaulo return NULL; 85214501Srpaulo } 86214501Srpaulo 87214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 88214501Srpaulo sizeof(*pax) + 2 + EAP_PAX_RAND_LEN + 89214501Srpaulo EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); 90214501Srpaulo if (req == NULL) { 91214501Srpaulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " 92214501Srpaulo "request"); 93214501Srpaulo data->state = FAILURE; 94214501Srpaulo return NULL; 95214501Srpaulo } 96214501Srpaulo 97214501Srpaulo pax = wpabuf_put(req, sizeof(*pax)); 98214501Srpaulo pax->op_code = EAP_PAX_OP_STD_1; 99214501Srpaulo pax->flags = 0; 100214501Srpaulo pax->mac_id = data->mac_id; 101214501Srpaulo pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; 102214501Srpaulo pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; 103214501Srpaulo 104214501Srpaulo wpabuf_put_be16(req, EAP_PAX_RAND_LEN); 105214501Srpaulo wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN); 106214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", 107214501Srpaulo data->rand.r.x, EAP_PAX_RAND_LEN); 108214501Srpaulo 109214501Srpaulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 110346981Scy if (eap_pax_mac(data->mac_id, (u8 *) "", 0, 111346981Scy wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, 112346981Scy NULL, 0, NULL, 0, pos) < 0) { 113346981Scy wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); 114346981Scy data->state = FAILURE; 115346981Scy wpabuf_free(req); 116346981Scy return NULL; 117346981Scy } 118214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 119214501Srpaulo 120214501Srpaulo return req; 121214501Srpaulo} 122214501Srpaulo 123214501Srpaulo 124214501Srpaulostatic struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, 125214501Srpaulo struct eap_pax_data *data, u8 id) 126214501Srpaulo{ 127214501Srpaulo struct wpabuf *req; 128214501Srpaulo struct eap_pax_hdr *pax; 129214501Srpaulo u8 *pos; 130214501Srpaulo 131214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); 132214501Srpaulo 133214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 134214501Srpaulo sizeof(*pax) + 2 + EAP_PAX_MAC_LEN + 135214501Srpaulo EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); 136214501Srpaulo if (req == NULL) { 137214501Srpaulo wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " 138214501Srpaulo "request"); 139214501Srpaulo data->state = FAILURE; 140214501Srpaulo return NULL; 141214501Srpaulo } 142214501Srpaulo 143214501Srpaulo pax = wpabuf_put(req, sizeof(*pax)); 144214501Srpaulo pax->op_code = EAP_PAX_OP_STD_3; 145214501Srpaulo pax->flags = 0; 146214501Srpaulo pax->mac_id = data->mac_id; 147214501Srpaulo pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; 148214501Srpaulo pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; 149214501Srpaulo 150214501Srpaulo wpabuf_put_be16(req, EAP_PAX_MAC_LEN); 151214501Srpaulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 152346981Scy if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 153346981Scy data->rand.r.y, EAP_PAX_RAND_LEN, 154346981Scy (u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) { 155346981Scy wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC"); 156346981Scy data->state = FAILURE; 157346981Scy wpabuf_free(req); 158346981Scy return NULL; 159346981Scy } 160214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", 161214501Srpaulo pos, EAP_PAX_MAC_LEN); 162214501Srpaulo 163214501Srpaulo /* Optional ADE could be added here, if needed */ 164214501Srpaulo 165214501Srpaulo pos = wpabuf_put(req, EAP_PAX_MAC_LEN); 166346981Scy if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 167346981Scy wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, 168346981Scy NULL, 0, NULL, 0, pos) < 0) { 169346981Scy wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV"); 170346981Scy data->state = FAILURE; 171346981Scy wpabuf_free(req); 172346981Scy return NULL; 173346981Scy } 174214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 175214501Srpaulo 176214501Srpaulo return req; 177214501Srpaulo} 178214501Srpaulo 179214501Srpaulo 180214501Srpaulostatic struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id) 181214501Srpaulo{ 182214501Srpaulo struct eap_pax_data *data = priv; 183214501Srpaulo 184214501Srpaulo switch (data->state) { 185214501Srpaulo case PAX_STD_1: 186214501Srpaulo return eap_pax_build_std_1(sm, data, id); 187214501Srpaulo case PAX_STD_3: 188214501Srpaulo return eap_pax_build_std_3(sm, data, id); 189214501Srpaulo default: 190214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", 191214501Srpaulo data->state); 192214501Srpaulo break; 193214501Srpaulo } 194214501Srpaulo return NULL; 195214501Srpaulo} 196214501Srpaulo 197214501Srpaulo 198214501Srpaulostatic Boolean eap_pax_check(struct eap_sm *sm, void *priv, 199214501Srpaulo struct wpabuf *respData) 200214501Srpaulo{ 201214501Srpaulo struct eap_pax_data *data = priv; 202214501Srpaulo struct eap_pax_hdr *resp; 203214501Srpaulo const u8 *pos; 204214501Srpaulo size_t len, mlen; 205214501Srpaulo u8 icvbuf[EAP_PAX_ICV_LEN], *icv; 206214501Srpaulo 207214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 208346981Scy if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) { 209214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); 210214501Srpaulo return TRUE; 211214501Srpaulo } 212214501Srpaulo 213214501Srpaulo mlen = sizeof(struct eap_hdr) + 1 + len; 214214501Srpaulo resp = (struct eap_pax_hdr *) pos; 215214501Srpaulo 216214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " 217214501Srpaulo "flags 0x%x mac_id 0x%x dh_group_id 0x%x " 218214501Srpaulo "public_key_id 0x%x", 219214501Srpaulo resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, 220214501Srpaulo resp->public_key_id); 221214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", 222214501Srpaulo (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); 223214501Srpaulo 224214501Srpaulo if (data->state == PAX_STD_1 && 225214501Srpaulo resp->op_code != EAP_PAX_OP_STD_2) { 226214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " 227214501Srpaulo "ignore op %d", resp->op_code); 228214501Srpaulo return TRUE; 229214501Srpaulo } 230214501Srpaulo 231214501Srpaulo if (data->state == PAX_STD_3 && 232214501Srpaulo resp->op_code != EAP_PAX_OP_ACK) { 233214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " 234214501Srpaulo "ignore op %d", resp->op_code); 235214501Srpaulo return TRUE; 236214501Srpaulo } 237214501Srpaulo 238214501Srpaulo if (resp->op_code != EAP_PAX_OP_STD_2 && 239214501Srpaulo resp->op_code != EAP_PAX_OP_ACK) { 240214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", 241214501Srpaulo resp->op_code); 242214501Srpaulo } 243214501Srpaulo 244214501Srpaulo if (data->mac_id != resp->mac_id) { 245214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " 246214501Srpaulo "received 0x%x", data->mac_id, resp->mac_id); 247214501Srpaulo return TRUE; 248214501Srpaulo } 249214501Srpaulo 250214501Srpaulo if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { 251214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " 252214501Srpaulo "received 0x%x", EAP_PAX_DH_GROUP_NONE, 253214501Srpaulo resp->dh_group_id); 254214501Srpaulo return TRUE; 255214501Srpaulo } 256214501Srpaulo 257214501Srpaulo if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { 258214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " 259214501Srpaulo "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, 260214501Srpaulo resp->public_key_id); 261214501Srpaulo return TRUE; 262214501Srpaulo } 263214501Srpaulo 264214501Srpaulo if (resp->flags & EAP_PAX_FLAGS_MF) { 265214501Srpaulo /* TODO: add support for reassembling fragments */ 266214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); 267214501Srpaulo return TRUE; 268214501Srpaulo } 269214501Srpaulo 270214501Srpaulo if (resp->flags & EAP_PAX_FLAGS_CE) { 271214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); 272214501Srpaulo return TRUE; 273214501Srpaulo } 274214501Srpaulo 275214501Srpaulo if (data->keys_set) { 276214501Srpaulo if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { 277214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); 278214501Srpaulo return TRUE; 279214501Srpaulo } 280214501Srpaulo icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; 281214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); 282346981Scy if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 283346981Scy wpabuf_mhead(respData), 284346981Scy wpabuf_len(respData) - EAP_PAX_ICV_LEN, 285351611Scy NULL, 0, NULL, 0, icvbuf) < 0) { 286351611Scy wpa_printf(MSG_INFO, 287351611Scy "EAP-PAX: Failed to calculate ICV"); 288351611Scy return TRUE; 289351611Scy } 290351611Scy 291351611Scy if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { 292214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); 293214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", 294214501Srpaulo icvbuf, EAP_PAX_ICV_LEN); 295214501Srpaulo return TRUE; 296214501Srpaulo } 297214501Srpaulo } 298214501Srpaulo 299214501Srpaulo return FALSE; 300214501Srpaulo} 301214501Srpaulo 302214501Srpaulo 303214501Srpaulostatic void eap_pax_process_std_2(struct eap_sm *sm, 304214501Srpaulo struct eap_pax_data *data, 305214501Srpaulo struct wpabuf *respData) 306214501Srpaulo{ 307214501Srpaulo struct eap_pax_hdr *resp; 308214501Srpaulo u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; 309214501Srpaulo const u8 *pos; 310281806Srpaulo size_t len, left, cid_len; 311214501Srpaulo int i; 312214501Srpaulo 313214501Srpaulo if (data->state != PAX_STD_1) 314214501Srpaulo return; 315214501Srpaulo 316214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); 317214501Srpaulo 318214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 319214501Srpaulo if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) 320214501Srpaulo return; 321214501Srpaulo 322214501Srpaulo resp = (struct eap_pax_hdr *) pos; 323214501Srpaulo pos = (u8 *) (resp + 1); 324214501Srpaulo left = len - sizeof(*resp); 325214501Srpaulo 326214501Srpaulo if (left < 2 + EAP_PAX_RAND_LEN || 327214501Srpaulo WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { 328214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); 329214501Srpaulo return; 330214501Srpaulo } 331214501Srpaulo pos += 2; 332214501Srpaulo left -= 2; 333214501Srpaulo os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); 334214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", 335214501Srpaulo data->rand.r.y, EAP_PAX_RAND_LEN); 336214501Srpaulo pos += EAP_PAX_RAND_LEN; 337214501Srpaulo left -= EAP_PAX_RAND_LEN; 338214501Srpaulo 339214501Srpaulo if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) { 340214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); 341214501Srpaulo return; 342214501Srpaulo } 343281806Srpaulo cid_len = WPA_GET_BE16(pos); 344281806Srpaulo if (cid_len > 1500) { 345281806Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too long CID"); 346281806Srpaulo return; 347281806Srpaulo } 348281806Srpaulo data->cid_len = cid_len; 349214501Srpaulo os_free(data->cid); 350346981Scy data->cid = os_memdup(pos + 2, data->cid_len); 351214501Srpaulo if (data->cid == NULL) { 352214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " 353214501Srpaulo "CID"); 354214501Srpaulo return; 355214501Srpaulo } 356214501Srpaulo pos += 2 + data->cid_len; 357214501Srpaulo left -= 2 + data->cid_len; 358214501Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", 359214501Srpaulo (u8 *) data->cid, data->cid_len); 360214501Srpaulo 361214501Srpaulo if (left < 2 + EAP_PAX_MAC_LEN || 362214501Srpaulo WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { 363214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); 364214501Srpaulo return; 365214501Srpaulo } 366214501Srpaulo pos += 2; 367214501Srpaulo left -= 2; 368214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", 369214501Srpaulo pos, EAP_PAX_MAC_LEN); 370214501Srpaulo 371214501Srpaulo if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { 372214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", 373214501Srpaulo (u8 *) data->cid, data->cid_len); 374214501Srpaulo data->state = FAILURE; 375214501Srpaulo return; 376214501Srpaulo } 377214501Srpaulo 378214501Srpaulo for (i = 0; 379214501Srpaulo i < EAP_MAX_METHODS && 380214501Srpaulo (sm->user->methods[i].vendor != EAP_VENDOR_IETF || 381214501Srpaulo sm->user->methods[i].method != EAP_TYPE_NONE); 382214501Srpaulo i++) { 383214501Srpaulo if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && 384214501Srpaulo sm->user->methods[i].method == EAP_TYPE_PAX) 385214501Srpaulo break; 386214501Srpaulo } 387214501Srpaulo 388214501Srpaulo if (i >= EAP_MAX_METHODS || 389214501Srpaulo sm->user->methods[i].vendor != EAP_VENDOR_IETF || 390214501Srpaulo sm->user->methods[i].method != EAP_TYPE_PAX) { 391214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, 392214501Srpaulo "EAP-PAX: EAP-PAX not enabled for CID", 393214501Srpaulo (u8 *) data->cid, data->cid_len); 394214501Srpaulo data->state = FAILURE; 395214501Srpaulo return; 396214501Srpaulo } 397214501Srpaulo 398214501Srpaulo if (sm->user->password == NULL || 399214501Srpaulo sm->user->password_len != EAP_PAX_AK_LEN) { 400214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " 401214501Srpaulo "user database for CID", 402214501Srpaulo (u8 *) data->cid, data->cid_len); 403214501Srpaulo data->state = FAILURE; 404214501Srpaulo return; 405214501Srpaulo } 406214501Srpaulo os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); 407214501Srpaulo 408214501Srpaulo if (eap_pax_initial_key_derivation(data->mac_id, data->ak, 409214501Srpaulo data->rand.e, data->mk, data->ck, 410281806Srpaulo data->ick, data->mid) < 0) { 411214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " 412214501Srpaulo "key derivation"); 413214501Srpaulo data->state = FAILURE; 414214501Srpaulo return; 415214501Srpaulo } 416214501Srpaulo data->keys_set = 1; 417214501Srpaulo 418346981Scy if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 419346981Scy data->rand.r.x, EAP_PAX_RAND_LEN, 420346981Scy data->rand.r.y, EAP_PAX_RAND_LEN, 421351611Scy (u8 *) data->cid, data->cid_len, mac) < 0) { 422351611Scy wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK"); 423351611Scy data->state = FAILURE; 424351611Scy return; 425351611Scy } 426351611Scy 427351611Scy if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) { 428214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " 429214501Srpaulo "PAX_STD-2"); 430214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", 431214501Srpaulo mac, EAP_PAX_MAC_LEN); 432214501Srpaulo data->state = FAILURE; 433214501Srpaulo return; 434214501Srpaulo } 435214501Srpaulo 436214501Srpaulo pos += EAP_PAX_MAC_LEN; 437214501Srpaulo left -= EAP_PAX_MAC_LEN; 438214501Srpaulo 439214501Srpaulo if (left < EAP_PAX_ICV_LEN) { 440214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " 441214501Srpaulo "PAX_STD-2", (unsigned long) left); 442214501Srpaulo return; 443214501Srpaulo } 444214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); 445346981Scy if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 446346981Scy wpabuf_head(respData), 447346981Scy wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, 448351611Scy NULL, 0, icvbuf) < 0) { 449351611Scy wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV"); 450351611Scy return; 451351611Scy } 452351611Scy 453351611Scy if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { 454214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); 455214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", 456214501Srpaulo icvbuf, EAP_PAX_ICV_LEN); 457214501Srpaulo return; 458214501Srpaulo } 459214501Srpaulo pos += EAP_PAX_ICV_LEN; 460214501Srpaulo left -= EAP_PAX_ICV_LEN; 461214501Srpaulo 462214501Srpaulo if (left > 0) { 463214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 464214501Srpaulo pos, left); 465214501Srpaulo } 466214501Srpaulo 467214501Srpaulo data->state = PAX_STD_3; 468214501Srpaulo} 469214501Srpaulo 470214501Srpaulo 471214501Srpaulostatic void eap_pax_process_ack(struct eap_sm *sm, 472214501Srpaulo struct eap_pax_data *data, 473214501Srpaulo struct wpabuf *respData) 474214501Srpaulo{ 475214501Srpaulo if (data->state != PAX_STD_3) 476214501Srpaulo return; 477214501Srpaulo 478214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " 479214501Srpaulo "completed successfully"); 480214501Srpaulo data->state = SUCCESS; 481214501Srpaulo} 482214501Srpaulo 483214501Srpaulo 484214501Srpaulostatic void eap_pax_process(struct eap_sm *sm, void *priv, 485214501Srpaulo struct wpabuf *respData) 486214501Srpaulo{ 487214501Srpaulo struct eap_pax_data *data = priv; 488214501Srpaulo struct eap_pax_hdr *resp; 489214501Srpaulo const u8 *pos; 490214501Srpaulo size_t len; 491214501Srpaulo 492214501Srpaulo if (sm->user == NULL || sm->user->password == NULL) { 493214501Srpaulo wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not " 494214501Srpaulo "configured"); 495214501Srpaulo data->state = FAILURE; 496214501Srpaulo return; 497214501Srpaulo } 498214501Srpaulo 499214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); 500214501Srpaulo if (pos == NULL || len < sizeof(*resp)) 501214501Srpaulo return; 502214501Srpaulo 503214501Srpaulo resp = (struct eap_pax_hdr *) pos; 504214501Srpaulo 505214501Srpaulo switch (resp->op_code) { 506214501Srpaulo case EAP_PAX_OP_STD_2: 507214501Srpaulo eap_pax_process_std_2(sm, data, respData); 508214501Srpaulo break; 509214501Srpaulo case EAP_PAX_OP_ACK: 510214501Srpaulo eap_pax_process_ack(sm, data, respData); 511214501Srpaulo break; 512214501Srpaulo } 513214501Srpaulo} 514214501Srpaulo 515214501Srpaulo 516214501Srpaulostatic Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) 517214501Srpaulo{ 518214501Srpaulo struct eap_pax_data *data = priv; 519214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 520214501Srpaulo} 521214501Srpaulo 522214501Srpaulo 523214501Srpaulostatic u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) 524214501Srpaulo{ 525214501Srpaulo struct eap_pax_data *data = priv; 526214501Srpaulo u8 *key; 527214501Srpaulo 528214501Srpaulo if (data->state != SUCCESS) 529214501Srpaulo return NULL; 530214501Srpaulo 531214501Srpaulo key = os_malloc(EAP_MSK_LEN); 532214501Srpaulo if (key == NULL) 533214501Srpaulo return NULL; 534214501Srpaulo 535214501Srpaulo *len = EAP_MSK_LEN; 536214501Srpaulo eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 537214501Srpaulo "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, 538214501Srpaulo EAP_MSK_LEN, key); 539214501Srpaulo 540214501Srpaulo return key; 541214501Srpaulo} 542214501Srpaulo 543214501Srpaulo 544214501Srpaulostatic u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 545214501Srpaulo{ 546214501Srpaulo struct eap_pax_data *data = priv; 547214501Srpaulo u8 *key; 548214501Srpaulo 549214501Srpaulo if (data->state != SUCCESS) 550214501Srpaulo return NULL; 551214501Srpaulo 552214501Srpaulo key = os_malloc(EAP_EMSK_LEN); 553214501Srpaulo if (key == NULL) 554214501Srpaulo return NULL; 555214501Srpaulo 556214501Srpaulo *len = EAP_EMSK_LEN; 557214501Srpaulo eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 558214501Srpaulo "Extended Master Session Key", 559214501Srpaulo data->rand.e, 2 * EAP_PAX_RAND_LEN, 560214501Srpaulo EAP_EMSK_LEN, key); 561214501Srpaulo 562214501Srpaulo return key; 563214501Srpaulo} 564214501Srpaulo 565214501Srpaulo 566214501Srpaulostatic Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) 567214501Srpaulo{ 568214501Srpaulo struct eap_pax_data *data = priv; 569214501Srpaulo return data->state == SUCCESS; 570214501Srpaulo} 571214501Srpaulo 572214501Srpaulo 573281806Srpaulostatic u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 574281806Srpaulo{ 575281806Srpaulo struct eap_pax_data *data = priv; 576281806Srpaulo u8 *sid; 577281806Srpaulo 578281806Srpaulo if (data->state != SUCCESS) 579281806Srpaulo return NULL; 580281806Srpaulo 581281806Srpaulo sid = os_malloc(1 + EAP_PAX_MID_LEN); 582281806Srpaulo if (sid == NULL) 583281806Srpaulo return NULL; 584281806Srpaulo 585281806Srpaulo *len = 1 + EAP_PAX_MID_LEN; 586281806Srpaulo sid[0] = EAP_TYPE_PAX; 587281806Srpaulo os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN); 588281806Srpaulo 589281806Srpaulo return sid; 590281806Srpaulo} 591281806Srpaulo 592281806Srpaulo 593214501Srpauloint eap_server_pax_register(void) 594214501Srpaulo{ 595214501Srpaulo struct eap_method *eap; 596214501Srpaulo 597214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 598214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); 599214501Srpaulo if (eap == NULL) 600214501Srpaulo return -1; 601214501Srpaulo 602214501Srpaulo eap->init = eap_pax_init; 603214501Srpaulo eap->reset = eap_pax_reset; 604214501Srpaulo eap->buildReq = eap_pax_buildReq; 605214501Srpaulo eap->check = eap_pax_check; 606214501Srpaulo eap->process = eap_pax_process; 607214501Srpaulo eap->isDone = eap_pax_isDone; 608214501Srpaulo eap->getKey = eap_pax_getKey; 609214501Srpaulo eap->isSuccess = eap_pax_isSuccess; 610214501Srpaulo eap->get_emsk = eap_pax_get_emsk; 611281806Srpaulo eap->getSessionId = eap_pax_get_session_id; 612214501Srpaulo 613337817Scy return eap_server_method_register(eap); 614214501Srpaulo} 615