1e28a4053SRui Paulo /* 2e28a4053SRui Paulo * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) 34bc52338SCy Schubert * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> 4e28a4053SRui Paulo * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 7e28a4053SRui Paulo */ 8e28a4053SRui Paulo 9e28a4053SRui Paulo #include "includes.h" 10e28a4053SRui Paulo 11e28a4053SRui Paulo #include "common.h" 12e28a4053SRui Paulo #include "crypto/sha1.h" 13e28a4053SRui Paulo #include "crypto/tls.h" 14f05cddf9SRui Paulo #include "crypto/random.h" 15e28a4053SRui Paulo #include "eap_i.h" 16e28a4053SRui Paulo #include "eap_tls_common.h" 17e28a4053SRui Paulo #include "eap_common/eap_tlv_common.h" 18e28a4053SRui Paulo #include "eap_common/eap_peap_common.h" 19e28a4053SRui Paulo #include "tncs.h" 20e28a4053SRui Paulo 21e28a4053SRui Paulo 22e28a4053SRui Paulo /* Maximum supported PEAP version 23e28a4053SRui Paulo * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt 24e28a4053SRui Paulo * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt 25e28a4053SRui Paulo */ 26e28a4053SRui Paulo #define EAP_PEAP_VERSION 1 27e28a4053SRui Paulo 28e28a4053SRui Paulo 29e28a4053SRui Paulo static void eap_peap_reset(struct eap_sm *sm, void *priv); 30e28a4053SRui Paulo 31e28a4053SRui Paulo 32e28a4053SRui Paulo struct eap_peap_data { 33e28a4053SRui Paulo struct eap_ssl_data ssl; 34e28a4053SRui Paulo enum { 35e28a4053SRui Paulo START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID, 36e28a4053SRui Paulo PHASE2_METHOD, PHASE2_SOH, 37e28a4053SRui Paulo PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE 38e28a4053SRui Paulo } state; 39e28a4053SRui Paulo 40e28a4053SRui Paulo int peap_version; 41e28a4053SRui Paulo int recv_version; 42e28a4053SRui Paulo const struct eap_method *phase2_method; 43e28a4053SRui Paulo void *phase2_priv; 44e28a4053SRui Paulo int force_version; 45e28a4053SRui Paulo struct wpabuf *pending_phase2_resp; 46e28a4053SRui Paulo enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; 47e28a4053SRui Paulo int crypto_binding_sent; 48e28a4053SRui Paulo int crypto_binding_used; 49e28a4053SRui Paulo enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; 50e28a4053SRui Paulo u8 binding_nonce[32]; 51e28a4053SRui Paulo u8 ipmk[40]; 52e28a4053SRui Paulo u8 cmk[20]; 53e28a4053SRui Paulo u8 *phase2_key; 54e28a4053SRui Paulo size_t phase2_key_len; 55e28a4053SRui Paulo struct wpabuf *soh_response; 56e28a4053SRui Paulo }; 57e28a4053SRui Paulo 58e28a4053SRui Paulo 59*a90b9d01SCy Schubert static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, 60*a90b9d01SCy Schubert int vendor, enum eap_type eap_type); 61*a90b9d01SCy Schubert 62*a90b9d01SCy Schubert 63e28a4053SRui Paulo static const char * eap_peap_state_txt(int state) 64e28a4053SRui Paulo { 65e28a4053SRui Paulo switch (state) { 66e28a4053SRui Paulo case START: 67e28a4053SRui Paulo return "START"; 68e28a4053SRui Paulo case PHASE1: 69e28a4053SRui Paulo return "PHASE1"; 70e28a4053SRui Paulo case PHASE1_ID2: 71e28a4053SRui Paulo return "PHASE1_ID2"; 72e28a4053SRui Paulo case PHASE2_START: 73e28a4053SRui Paulo return "PHASE2_START"; 74e28a4053SRui Paulo case PHASE2_ID: 75e28a4053SRui Paulo return "PHASE2_ID"; 76e28a4053SRui Paulo case PHASE2_METHOD: 77e28a4053SRui Paulo return "PHASE2_METHOD"; 78e28a4053SRui Paulo case PHASE2_SOH: 79e28a4053SRui Paulo return "PHASE2_SOH"; 80e28a4053SRui Paulo case PHASE2_TLV: 81e28a4053SRui Paulo return "PHASE2_TLV"; 82e28a4053SRui Paulo case SUCCESS_REQ: 83e28a4053SRui Paulo return "SUCCESS_REQ"; 84e28a4053SRui Paulo case FAILURE_REQ: 85e28a4053SRui Paulo return "FAILURE_REQ"; 86e28a4053SRui Paulo case SUCCESS: 87e28a4053SRui Paulo return "SUCCESS"; 88e28a4053SRui Paulo case FAILURE: 89e28a4053SRui Paulo return "FAILURE"; 90e28a4053SRui Paulo default: 91e28a4053SRui Paulo return "Unknown?!"; 92e28a4053SRui Paulo } 93e28a4053SRui Paulo } 94e28a4053SRui Paulo 95e28a4053SRui Paulo 96e28a4053SRui Paulo static void eap_peap_state(struct eap_peap_data *data, int state) 97e28a4053SRui Paulo { 98e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", 99e28a4053SRui Paulo eap_peap_state_txt(data->state), 100e28a4053SRui Paulo eap_peap_state_txt(state)); 101e28a4053SRui Paulo data->state = state; 102325151a3SRui Paulo if (state == FAILURE || state == FAILURE_REQ) 103325151a3SRui Paulo tls_connection_remove_session(data->ssl.conn); 104325151a3SRui Paulo } 105325151a3SRui Paulo 106325151a3SRui Paulo 107325151a3SRui Paulo static void eap_peap_valid_session(struct eap_sm *sm, 108325151a3SRui Paulo struct eap_peap_data *data) 109325151a3SRui Paulo { 110325151a3SRui Paulo struct wpabuf *buf; 111325151a3SRui Paulo 112c1d255d3SCy Schubert if (!sm->cfg->tls_session_lifetime || 113c1d255d3SCy Schubert tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn)) 114325151a3SRui Paulo return; 115325151a3SRui Paulo 116325151a3SRui Paulo buf = wpabuf_alloc(1 + 1 + sm->identity_len); 117325151a3SRui Paulo if (!buf) 118325151a3SRui Paulo return; 119325151a3SRui Paulo wpabuf_put_u8(buf, EAP_TYPE_PEAP); 120325151a3SRui Paulo if (sm->identity) { 121325151a3SRui Paulo u8 id_len; 122325151a3SRui Paulo 123325151a3SRui Paulo if (sm->identity_len <= 255) 124325151a3SRui Paulo id_len = sm->identity_len; 125325151a3SRui Paulo else 126325151a3SRui Paulo id_len = 255; 127325151a3SRui Paulo wpabuf_put_u8(buf, id_len); 128325151a3SRui Paulo wpabuf_put_data(buf, sm->identity, id_len); 129325151a3SRui Paulo } else { 130325151a3SRui Paulo wpabuf_put_u8(buf, 0); 131325151a3SRui Paulo } 132325151a3SRui Paulo tls_connection_set_success_data(data->ssl.conn, buf); 133e28a4053SRui Paulo } 134e28a4053SRui Paulo 135e28a4053SRui Paulo 136e28a4053SRui Paulo static void eap_peap_req_success(struct eap_sm *sm, 137e28a4053SRui Paulo struct eap_peap_data *data) 138e28a4053SRui Paulo { 139e28a4053SRui Paulo if (data->state == FAILURE || data->state == FAILURE_REQ) { 140e28a4053SRui Paulo eap_peap_state(data, FAILURE); 141e28a4053SRui Paulo return; 142e28a4053SRui Paulo } 143e28a4053SRui Paulo 144e28a4053SRui Paulo if (data->peap_version == 0) { 145e28a4053SRui Paulo data->tlv_request = TLV_REQ_SUCCESS; 146e28a4053SRui Paulo eap_peap_state(data, PHASE2_TLV); 147e28a4053SRui Paulo } else { 148e28a4053SRui Paulo eap_peap_state(data, SUCCESS_REQ); 149e28a4053SRui Paulo } 150e28a4053SRui Paulo } 151e28a4053SRui Paulo 152e28a4053SRui Paulo 153e28a4053SRui Paulo static void eap_peap_req_failure(struct eap_sm *sm, 154e28a4053SRui Paulo struct eap_peap_data *data) 155e28a4053SRui Paulo { 156e28a4053SRui Paulo if (data->state == FAILURE || data->state == FAILURE_REQ || 157e28a4053SRui Paulo data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) { 158e28a4053SRui Paulo eap_peap_state(data, FAILURE); 159e28a4053SRui Paulo return; 160e28a4053SRui Paulo } 161e28a4053SRui Paulo 162e28a4053SRui Paulo if (data->peap_version == 0) { 163e28a4053SRui Paulo data->tlv_request = TLV_REQ_FAILURE; 164e28a4053SRui Paulo eap_peap_state(data, PHASE2_TLV); 165e28a4053SRui Paulo } else { 166e28a4053SRui Paulo eap_peap_state(data, FAILURE_REQ); 167e28a4053SRui Paulo } 168e28a4053SRui Paulo } 169e28a4053SRui Paulo 170e28a4053SRui Paulo 171e28a4053SRui Paulo static void * eap_peap_init(struct eap_sm *sm) 172e28a4053SRui Paulo { 173e28a4053SRui Paulo struct eap_peap_data *data; 174e28a4053SRui Paulo 175e28a4053SRui Paulo data = os_zalloc(sizeof(*data)); 176e28a4053SRui Paulo if (data == NULL) 177e28a4053SRui Paulo return NULL; 178e28a4053SRui Paulo data->peap_version = EAP_PEAP_VERSION; 179e28a4053SRui Paulo data->force_version = -1; 180e28a4053SRui Paulo if (sm->user && sm->user->force_version >= 0) { 181e28a4053SRui Paulo data->force_version = sm->user->force_version; 182e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", 183e28a4053SRui Paulo data->force_version); 184e28a4053SRui Paulo data->peap_version = data->force_version; 185e28a4053SRui Paulo } 186e28a4053SRui Paulo data->state = START; 187e28a4053SRui Paulo data->crypto_binding = OPTIONAL_BINDING; 188e28a4053SRui Paulo 189325151a3SRui Paulo if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) { 190e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); 191e28a4053SRui Paulo eap_peap_reset(sm, data); 192e28a4053SRui Paulo return NULL; 193e28a4053SRui Paulo } 194e28a4053SRui Paulo 195e28a4053SRui Paulo return data; 196e28a4053SRui Paulo } 197e28a4053SRui Paulo 198e28a4053SRui Paulo 199e28a4053SRui Paulo static void eap_peap_reset(struct eap_sm *sm, void *priv) 200e28a4053SRui Paulo { 201e28a4053SRui Paulo struct eap_peap_data *data = priv; 202e28a4053SRui Paulo if (data == NULL) 203e28a4053SRui Paulo return; 204e28a4053SRui Paulo if (data->phase2_priv && data->phase2_method) 205e28a4053SRui Paulo data->phase2_method->reset(sm, data->phase2_priv); 206e28a4053SRui Paulo eap_server_tls_ssl_deinit(sm, &data->ssl); 207e28a4053SRui Paulo wpabuf_free(data->pending_phase2_resp); 208e28a4053SRui Paulo os_free(data->phase2_key); 209e28a4053SRui Paulo wpabuf_free(data->soh_response); 2105b9c547cSRui Paulo bin_clear_free(data, sizeof(*data)); 211e28a4053SRui Paulo } 212e28a4053SRui Paulo 213e28a4053SRui Paulo 214e28a4053SRui Paulo static struct wpabuf * eap_peap_build_start(struct eap_sm *sm, 215e28a4053SRui Paulo struct eap_peap_data *data, u8 id) 216e28a4053SRui Paulo { 217e28a4053SRui Paulo struct wpabuf *req; 218e28a4053SRui Paulo 219e28a4053SRui Paulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1, 220e28a4053SRui Paulo EAP_CODE_REQUEST, id); 221e28a4053SRui Paulo if (req == NULL) { 222e28a4053SRui Paulo wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" 223e28a4053SRui Paulo " request"); 224e28a4053SRui Paulo eap_peap_state(data, FAILURE); 225e28a4053SRui Paulo return NULL; 226e28a4053SRui Paulo } 227e28a4053SRui Paulo 228e28a4053SRui Paulo wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version); 229e28a4053SRui Paulo 230e28a4053SRui Paulo eap_peap_state(data, PHASE1); 231e28a4053SRui Paulo 232e28a4053SRui Paulo return req; 233e28a4053SRui Paulo } 234e28a4053SRui Paulo 235e28a4053SRui Paulo 236e28a4053SRui Paulo static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, 237e28a4053SRui Paulo struct eap_peap_data *data, 238e28a4053SRui Paulo u8 id) 239e28a4053SRui Paulo { 240e28a4053SRui Paulo struct wpabuf *buf, *encr_req, msgbuf; 241e28a4053SRui Paulo const u8 *req; 242e28a4053SRui Paulo size_t req_len; 243e28a4053SRui Paulo 244e28a4053SRui Paulo if (data->phase2_method == NULL || data->phase2_priv == NULL) { 245e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready"); 246e28a4053SRui Paulo return NULL; 247e28a4053SRui Paulo } 248e28a4053SRui Paulo buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); 249e28a4053SRui Paulo if (buf == NULL) 250e28a4053SRui Paulo return NULL; 251e28a4053SRui Paulo 252e28a4053SRui Paulo req = wpabuf_head(buf); 253e28a4053SRui Paulo req_len = wpabuf_len(buf); 254e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", 255e28a4053SRui Paulo req, req_len); 256e28a4053SRui Paulo 257e28a4053SRui Paulo if (data->peap_version == 0 && 258e28a4053SRui Paulo data->phase2_method->method != EAP_TYPE_TLV) { 259e28a4053SRui Paulo req += sizeof(struct eap_hdr); 260e28a4053SRui Paulo req_len -= sizeof(struct eap_hdr); 261e28a4053SRui Paulo } 262e28a4053SRui Paulo 263e28a4053SRui Paulo wpabuf_set(&msgbuf, req, req_len); 264e28a4053SRui Paulo encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); 265e28a4053SRui Paulo wpabuf_free(buf); 266e28a4053SRui Paulo 267e28a4053SRui Paulo return encr_req; 268e28a4053SRui Paulo } 269e28a4053SRui Paulo 270e28a4053SRui Paulo 271e28a4053SRui Paulo #ifdef EAP_SERVER_TNC 272e28a4053SRui Paulo static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, 273e28a4053SRui Paulo struct eap_peap_data *data, 274e28a4053SRui Paulo u8 id) 275e28a4053SRui Paulo { 276e28a4053SRui Paulo struct wpabuf *buf1, *buf, *encr_req, msgbuf; 277e28a4053SRui Paulo const u8 *req; 278e28a4053SRui Paulo size_t req_len; 279e28a4053SRui Paulo 280e28a4053SRui Paulo buf1 = tncs_build_soh_request(); 281e28a4053SRui Paulo if (buf1 == NULL) 282e28a4053SRui Paulo return NULL; 283e28a4053SRui Paulo 284e28a4053SRui Paulo buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1), 285e28a4053SRui Paulo EAP_CODE_REQUEST, id); 286e28a4053SRui Paulo if (buf == NULL) { 287e28a4053SRui Paulo wpabuf_free(buf1); 288e28a4053SRui Paulo return NULL; 289e28a4053SRui Paulo } 290e28a4053SRui Paulo wpabuf_put_buf(buf, buf1); 291e28a4053SRui Paulo wpabuf_free(buf1); 292e28a4053SRui Paulo 293e28a4053SRui Paulo req = wpabuf_head(buf); 294e28a4053SRui Paulo req_len = wpabuf_len(buf); 295e28a4053SRui Paulo 296e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data", 297e28a4053SRui Paulo req, req_len); 298e28a4053SRui Paulo 299e28a4053SRui Paulo req += sizeof(struct eap_hdr); 300e28a4053SRui Paulo req_len -= sizeof(struct eap_hdr); 301e28a4053SRui Paulo wpabuf_set(&msgbuf, req, req_len); 302e28a4053SRui Paulo 303e28a4053SRui Paulo encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); 304e28a4053SRui Paulo wpabuf_free(buf); 305e28a4053SRui Paulo 306e28a4053SRui Paulo return encr_req; 307e28a4053SRui Paulo } 308e28a4053SRui Paulo #endif /* EAP_SERVER_TNC */ 309e28a4053SRui Paulo 310e28a4053SRui Paulo 311e28a4053SRui Paulo static void eap_peap_get_isk(struct eap_peap_data *data, 312e28a4053SRui Paulo u8 *isk, size_t isk_len) 313e28a4053SRui Paulo { 314e28a4053SRui Paulo size_t key_len; 315e28a4053SRui Paulo 316e28a4053SRui Paulo os_memset(isk, 0, isk_len); 317e28a4053SRui Paulo if (data->phase2_key == NULL) 318e28a4053SRui Paulo return; 319e28a4053SRui Paulo 320e28a4053SRui Paulo key_len = data->phase2_key_len; 321e28a4053SRui Paulo if (key_len > isk_len) 322e28a4053SRui Paulo key_len = isk_len; 323e28a4053SRui Paulo os_memcpy(isk, data->phase2_key, key_len); 324e28a4053SRui Paulo } 325e28a4053SRui Paulo 326e28a4053SRui Paulo 327e28a4053SRui Paulo static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) 328e28a4053SRui Paulo { 329e28a4053SRui Paulo u8 *tk; 330e28a4053SRui Paulo u8 isk[32], imck[60]; 3314bc52338SCy Schubert int res; 332c1d255d3SCy Schubert const char *label; 333c1d255d3SCy Schubert const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP }; 334c1d255d3SCy Schubert const u8 *context = NULL; 335c1d255d3SCy Schubert size_t context_len = 0; 336c1d255d3SCy Schubert 337c1d255d3SCy Schubert if (data->ssl.tls_v13) { 338c1d255d3SCy Schubert label = "EXPORTER_EAP_TLS_Key_Material"; 339c1d255d3SCy Schubert context = eap_tls13_context; 340c1d255d3SCy Schubert context_len = sizeof(eap_tls13_context); 341c1d255d3SCy Schubert } else { 342c1d255d3SCy Schubert /* TODO: PEAPv1 - different label in some cases */ 343c1d255d3SCy Schubert label = "client EAP encryption"; 344c1d255d3SCy Schubert } 345e28a4053SRui Paulo 346e28a4053SRui Paulo /* 347e28a4053SRui Paulo * Tunnel key (TK) is the first 60 octets of the key generated by 348e28a4053SRui Paulo * phase 1 of PEAP (based on TLS). 349e28a4053SRui Paulo */ 350c1d255d3SCy Schubert tk = eap_server_tls_derive_key(sm, &data->ssl, label, 351c1d255d3SCy Schubert context, context_len, 352c1d255d3SCy Schubert EAP_TLS_KEY_LEN); 353e28a4053SRui Paulo if (tk == NULL) 354e28a4053SRui Paulo return -1; 355e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); 356e28a4053SRui Paulo 357c1d255d3SCy Schubert if (tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn)) { 358780fb4a2SCy Schubert /* Fast-connect: IPMK|CMK = TK */ 359780fb4a2SCy Schubert os_memcpy(data->ipmk, tk, 40); 360780fb4a2SCy Schubert wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", 361780fb4a2SCy Schubert data->ipmk, 40); 362780fb4a2SCy Schubert os_memcpy(data->cmk, tk + 40, 20); 363780fb4a2SCy Schubert wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", 364780fb4a2SCy Schubert data->cmk, 20); 365780fb4a2SCy Schubert os_free(tk); 366780fb4a2SCy Schubert return 0; 367780fb4a2SCy Schubert } 368780fb4a2SCy Schubert 369e28a4053SRui Paulo eap_peap_get_isk(data, isk, sizeof(isk)); 370e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); 371e28a4053SRui Paulo 372e28a4053SRui Paulo /* 373e28a4053SRui Paulo * IPMK Seed = "Inner Methods Compound Keys" | ISK 374e28a4053SRui Paulo * TempKey = First 40 octets of TK 375e28a4053SRui Paulo * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) 376e28a4053SRui Paulo * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space 377e28a4053SRui Paulo * in the end of the label just before ISK; is that just a typo?) 378e28a4053SRui Paulo */ 379e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); 3804bc52338SCy Schubert res = peap_prfplus(data->peap_version, tk, 40, 381f05cddf9SRui Paulo "Inner Methods Compound Keys", 3824bc52338SCy Schubert isk, sizeof(isk), imck, sizeof(imck)); 383206b73d0SCy Schubert forced_memzero(isk, sizeof(isk)); 3844bc52338SCy Schubert if (res < 0) { 385f05cddf9SRui Paulo os_free(tk); 386f05cddf9SRui Paulo return -1; 387f05cddf9SRui Paulo } 388e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", 389e28a4053SRui Paulo imck, sizeof(imck)); 390e28a4053SRui Paulo 391e28a4053SRui Paulo os_free(tk); 392e28a4053SRui Paulo 393e28a4053SRui Paulo os_memcpy(data->ipmk, imck, 40); 394e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); 395e28a4053SRui Paulo os_memcpy(data->cmk, imck + 40, 20); 396e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); 397206b73d0SCy Schubert forced_memzero(imck, sizeof(imck)); 398e28a4053SRui Paulo 399e28a4053SRui Paulo return 0; 400e28a4053SRui Paulo } 401e28a4053SRui Paulo 402e28a4053SRui Paulo 403e28a4053SRui Paulo static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, 404e28a4053SRui Paulo struct eap_peap_data *data, 405e28a4053SRui Paulo u8 id) 406e28a4053SRui Paulo { 407e28a4053SRui Paulo struct wpabuf *buf, *encr_req; 408e28a4053SRui Paulo size_t mlen; 409e28a4053SRui Paulo 410e28a4053SRui Paulo mlen = 6; /* Result TLV */ 4115b9c547cSRui Paulo if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && 4125b9c547cSRui Paulo data->crypto_binding != NO_BINDING) { 413e28a4053SRui Paulo mlen += 60; /* Cryptobinding TLV */ 414e28a4053SRui Paulo #ifdef EAP_SERVER_TNC 415e28a4053SRui Paulo if (data->soh_response) 416e28a4053SRui Paulo mlen += wpabuf_len(data->soh_response); 417e28a4053SRui Paulo #endif /* EAP_SERVER_TNC */ 4185b9c547cSRui Paulo } 419e28a4053SRui Paulo 420e28a4053SRui Paulo buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen, 421e28a4053SRui Paulo EAP_CODE_REQUEST, id); 422e28a4053SRui Paulo if (buf == NULL) 423e28a4053SRui Paulo return NULL; 424e28a4053SRui Paulo 425e28a4053SRui Paulo wpabuf_put_u8(buf, 0x80); /* Mandatory */ 426e28a4053SRui Paulo wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV); 427e28a4053SRui Paulo /* Length */ 428e28a4053SRui Paulo wpabuf_put_be16(buf, 2); 429e28a4053SRui Paulo /* Status */ 430e28a4053SRui Paulo wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ? 431e28a4053SRui Paulo EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE); 432e28a4053SRui Paulo 433e28a4053SRui Paulo if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && 434e28a4053SRui Paulo data->crypto_binding != NO_BINDING) { 435e28a4053SRui Paulo u8 *mac; 436e28a4053SRui Paulo u8 eap_type = EAP_TYPE_PEAP; 437e28a4053SRui Paulo const u8 *addr[2]; 438e28a4053SRui Paulo size_t len[2]; 439e28a4053SRui Paulo u16 tlv_type; 440e28a4053SRui Paulo 441e28a4053SRui Paulo #ifdef EAP_SERVER_TNC 442e28a4053SRui Paulo if (data->soh_response) { 443e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH " 444e28a4053SRui Paulo "Response TLV"); 445e28a4053SRui Paulo wpabuf_put_buf(buf, data->soh_response); 446e28a4053SRui Paulo wpabuf_free(data->soh_response); 447e28a4053SRui Paulo data->soh_response = NULL; 448e28a4053SRui Paulo } 449e28a4053SRui Paulo #endif /* EAP_SERVER_TNC */ 450e28a4053SRui Paulo 451e28a4053SRui Paulo if (eap_peap_derive_cmk(sm, data) < 0 || 452f05cddf9SRui Paulo random_get_bytes(data->binding_nonce, 32)) { 453e28a4053SRui Paulo wpabuf_free(buf); 454e28a4053SRui Paulo return NULL; 455e28a4053SRui Paulo } 456e28a4053SRui Paulo 457e28a4053SRui Paulo /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ 458e28a4053SRui Paulo addr[0] = wpabuf_put(buf, 0); 459e28a4053SRui Paulo len[0] = 60; 460e28a4053SRui Paulo addr[1] = &eap_type; 461e28a4053SRui Paulo len[1] = 1; 462e28a4053SRui Paulo 463e28a4053SRui Paulo tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; 464e28a4053SRui Paulo wpabuf_put_be16(buf, tlv_type); 465e28a4053SRui Paulo wpabuf_put_be16(buf, 56); 466e28a4053SRui Paulo 467e28a4053SRui Paulo wpabuf_put_u8(buf, 0); /* Reserved */ 468e28a4053SRui Paulo wpabuf_put_u8(buf, data->peap_version); /* Version */ 469e28a4053SRui Paulo wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */ 470e28a4053SRui Paulo wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */ 471e28a4053SRui Paulo wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ 472e28a4053SRui Paulo mac = wpabuf_put(buf, 20); /* Compound_MAC */ 473e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", 474e28a4053SRui Paulo data->cmk, 20); 475e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", 476e28a4053SRui Paulo addr[0], len[0]); 477e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", 478e28a4053SRui Paulo addr[1], len[1]); 479e28a4053SRui Paulo hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); 480e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", 481e28a4053SRui Paulo mac, SHA1_MAC_LEN); 482e28a4053SRui Paulo data->crypto_binding_sent = 1; 483e28a4053SRui Paulo } 484e28a4053SRui Paulo 485e28a4053SRui Paulo wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", 486e28a4053SRui Paulo buf); 487e28a4053SRui Paulo 488e28a4053SRui Paulo encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); 489e28a4053SRui Paulo wpabuf_free(buf); 490e28a4053SRui Paulo 491e28a4053SRui Paulo return encr_req; 492e28a4053SRui Paulo } 493e28a4053SRui Paulo 494e28a4053SRui Paulo 495e28a4053SRui Paulo static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, 496e28a4053SRui Paulo struct eap_peap_data *data, 497e28a4053SRui Paulo u8 id, int success) 498e28a4053SRui Paulo { 499e28a4053SRui Paulo struct wpabuf *encr_req, msgbuf; 500e28a4053SRui Paulo size_t req_len; 501e28a4053SRui Paulo struct eap_hdr *hdr; 502e28a4053SRui Paulo 503e28a4053SRui Paulo req_len = sizeof(*hdr); 504e28a4053SRui Paulo hdr = os_zalloc(req_len); 505e28a4053SRui Paulo if (hdr == NULL) 506e28a4053SRui Paulo return NULL; 507e28a4053SRui Paulo 508e28a4053SRui Paulo hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; 509e28a4053SRui Paulo hdr->identifier = id; 510e28a4053SRui Paulo hdr->length = host_to_be16(req_len); 511e28a4053SRui Paulo 512e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", 513e28a4053SRui Paulo (u8 *) hdr, req_len); 514e28a4053SRui Paulo 515e28a4053SRui Paulo wpabuf_set(&msgbuf, hdr, req_len); 516e28a4053SRui Paulo encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); 517e28a4053SRui Paulo os_free(hdr); 518e28a4053SRui Paulo 519c1d255d3SCy Schubert if (!data->ssl.tls_v13 || 520c1d255d3SCy Schubert !tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn)) { 521c1d255d3SCy Schubert wpabuf_free(data->ssl.tls_out); 522c1d255d3SCy Schubert data->ssl.tls_out_pos = 0; 523e28a4053SRui Paulo return encr_req; 524e28a4053SRui Paulo } 525e28a4053SRui Paulo 526c1d255d3SCy Schubert if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr_req)) < 0) { 527c1d255d3SCy Schubert wpa_printf(MSG_INFO, 528c1d255d3SCy Schubert "EAP-PEAP: Failed to resize output buffer"); 529c1d255d3SCy Schubert wpabuf_free(encr_req); 530c1d255d3SCy Schubert return NULL; 531c1d255d3SCy Schubert } 532c1d255d3SCy Schubert wpabuf_put_buf(data->ssl.tls_out, encr_req); 533c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 534c1d255d3SCy Schubert "EAP-PEAP: Data appended to the message", encr_req); 535c1d255d3SCy Schubert os_free(encr_req); 536c1d255d3SCy Schubert 537c1d255d3SCy Schubert return data->ssl.tls_out; 538c1d255d3SCy Schubert } 539c1d255d3SCy Schubert 540e28a4053SRui Paulo 541e28a4053SRui Paulo static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) 542e28a4053SRui Paulo { 543e28a4053SRui Paulo struct eap_peap_data *data = priv; 544e28a4053SRui Paulo 545e28a4053SRui Paulo if (data->ssl.state == FRAG_ACK) { 546e28a4053SRui Paulo return eap_server_tls_build_ack(id, EAP_TYPE_PEAP, 547e28a4053SRui Paulo data->peap_version); 548e28a4053SRui Paulo } 549e28a4053SRui Paulo 550e28a4053SRui Paulo if (data->ssl.state == WAIT_FRAG_ACK) { 551e28a4053SRui Paulo return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, 552e28a4053SRui Paulo data->peap_version, id); 553e28a4053SRui Paulo } 554e28a4053SRui Paulo 555e28a4053SRui Paulo switch (data->state) { 556e28a4053SRui Paulo case START: 557e28a4053SRui Paulo return eap_peap_build_start(sm, data, id); 558e28a4053SRui Paulo case PHASE1: 559e28a4053SRui Paulo case PHASE1_ID2: 560c1d255d3SCy Schubert if (tls_connection_established(sm->cfg->ssl_ctx, 561c1d255d3SCy Schubert data->ssl.conn)) { 562e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " 563e28a4053SRui Paulo "starting Phase2"); 564e28a4053SRui Paulo eap_peap_state(data, PHASE2_START); 565*a90b9d01SCy Schubert if (data->ssl.tls_v13 && data->ssl.tls_out && 566*a90b9d01SCy Schubert wpabuf_len(data->ssl.tls_out) == 0) { 567*a90b9d01SCy Schubert /* This can happen with TLS 1.3 when a new 568*a90b9d01SCy Schubert * session ticket is not generated and the 569*a90b9d01SCy Schubert * Finished message from the peer terminates 570*a90b9d01SCy Schubert * Phase 1. */ 571*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 572*a90b9d01SCy Schubert "EAP-PEAP: No pending data to send - move directly to Phase 2 ID query"); 573*a90b9d01SCy Schubert eap_peap_state(data, PHASE2_ID); 574*a90b9d01SCy Schubert eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, 575*a90b9d01SCy Schubert EAP_TYPE_IDENTITY); 576*a90b9d01SCy Schubert goto phase2_id; 577*a90b9d01SCy Schubert } 578e28a4053SRui Paulo } 579e28a4053SRui Paulo break; 580e28a4053SRui Paulo case PHASE2_ID: 581e28a4053SRui Paulo case PHASE2_METHOD: 582*a90b9d01SCy Schubert phase2_id: 583e28a4053SRui Paulo wpabuf_free(data->ssl.tls_out); 584e28a4053SRui Paulo data->ssl.tls_out_pos = 0; 585e28a4053SRui Paulo data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id); 586e28a4053SRui Paulo break; 587e28a4053SRui Paulo #ifdef EAP_SERVER_TNC 588e28a4053SRui Paulo case PHASE2_SOH: 589e28a4053SRui Paulo wpabuf_free(data->ssl.tls_out); 590e28a4053SRui Paulo data->ssl.tls_out_pos = 0; 591e28a4053SRui Paulo data->ssl.tls_out = eap_peap_build_phase2_soh(sm, data, id); 592e28a4053SRui Paulo break; 593e28a4053SRui Paulo #endif /* EAP_SERVER_TNC */ 594e28a4053SRui Paulo case PHASE2_TLV: 595e28a4053SRui Paulo wpabuf_free(data->ssl.tls_out); 596e28a4053SRui Paulo data->ssl.tls_out_pos = 0; 597e28a4053SRui Paulo data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id); 598e28a4053SRui Paulo break; 599e28a4053SRui Paulo case SUCCESS_REQ: 600e28a4053SRui Paulo data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, 601e28a4053SRui Paulo 1); 602e28a4053SRui Paulo break; 603e28a4053SRui Paulo case FAILURE_REQ: 604e28a4053SRui Paulo wpabuf_free(data->ssl.tls_out); 605e28a4053SRui Paulo data->ssl.tls_out_pos = 0; 606e28a4053SRui Paulo data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, 607e28a4053SRui Paulo 0); 608e28a4053SRui Paulo break; 609e28a4053SRui Paulo default: 610e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", 611e28a4053SRui Paulo __func__, data->state); 612e28a4053SRui Paulo return NULL; 613e28a4053SRui Paulo } 614e28a4053SRui Paulo 615e28a4053SRui Paulo return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, 616e28a4053SRui Paulo data->peap_version, id); 617e28a4053SRui Paulo } 618e28a4053SRui Paulo 619e28a4053SRui Paulo 620c1d255d3SCy Schubert static bool eap_peap_check(struct eap_sm *sm, void *priv, 621e28a4053SRui Paulo struct wpabuf *respData) 622e28a4053SRui Paulo { 623e28a4053SRui Paulo const u8 *pos; 624e28a4053SRui Paulo size_t len; 625e28a4053SRui Paulo 626e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &len); 627e28a4053SRui Paulo if (pos == NULL || len < 1) { 628e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); 629c1d255d3SCy Schubert return true; 630e28a4053SRui Paulo } 631e28a4053SRui Paulo 632c1d255d3SCy Schubert return false; 633e28a4053SRui Paulo } 634e28a4053SRui Paulo 635e28a4053SRui Paulo 636e28a4053SRui Paulo static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, 637c1d255d3SCy Schubert int vendor, enum eap_type eap_type) 638e28a4053SRui Paulo { 639e28a4053SRui Paulo if (data->phase2_priv && data->phase2_method) { 640e28a4053SRui Paulo data->phase2_method->reset(sm, data->phase2_priv); 641e28a4053SRui Paulo data->phase2_method = NULL; 642e28a4053SRui Paulo data->phase2_priv = NULL; 643e28a4053SRui Paulo } 644325151a3SRui Paulo data->phase2_method = eap_server_get_eap_method(vendor, eap_type); 645e28a4053SRui Paulo if (!data->phase2_method) 646e28a4053SRui Paulo return -1; 647e28a4053SRui Paulo 648e28a4053SRui Paulo sm->init_phase2 = 1; 649e28a4053SRui Paulo data->phase2_priv = data->phase2_method->init(sm); 650e28a4053SRui Paulo sm->init_phase2 = 0; 651e28a4053SRui Paulo return 0; 652e28a4053SRui Paulo } 653e28a4053SRui Paulo 654e28a4053SRui Paulo 655e28a4053SRui Paulo static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, 656e28a4053SRui Paulo struct eap_peap_data *data, 657e28a4053SRui Paulo const u8 *crypto_tlv, 658e28a4053SRui Paulo size_t crypto_tlv_len) 659e28a4053SRui Paulo { 660e28a4053SRui Paulo u8 buf[61], mac[SHA1_MAC_LEN]; 661e28a4053SRui Paulo const u8 *pos; 662e28a4053SRui Paulo 663e28a4053SRui Paulo if (crypto_tlv_len != 4 + 56) { 664e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " 665e28a4053SRui Paulo "length %d", (int) crypto_tlv_len); 666e28a4053SRui Paulo return -1; 667e28a4053SRui Paulo } 668e28a4053SRui Paulo 669e28a4053SRui Paulo pos = crypto_tlv; 670e28a4053SRui Paulo pos += 4; /* TLV header */ 671e28a4053SRui Paulo if (pos[1] != data->peap_version) { 672e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " 673e28a4053SRui Paulo "mismatch (was %d; expected %d)", 674e28a4053SRui Paulo pos[1], data->peap_version); 675e28a4053SRui Paulo return -1; 676e28a4053SRui Paulo } 677e28a4053SRui Paulo 678e28a4053SRui Paulo if (pos[3] != 1) { 679e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " 680e28a4053SRui Paulo "SubType %d", pos[3]); 681e28a4053SRui Paulo return -1; 682e28a4053SRui Paulo } 683e28a4053SRui Paulo pos += 4; 684e28a4053SRui Paulo pos += 32; /* Nonce */ 685e28a4053SRui Paulo 686e28a4053SRui Paulo /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ 687e28a4053SRui Paulo os_memcpy(buf, crypto_tlv, 60); 688e28a4053SRui Paulo os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ 689e28a4053SRui Paulo buf[60] = EAP_TYPE_PEAP; 690e28a4053SRui Paulo hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); 691e28a4053SRui Paulo 6925b9c547cSRui Paulo if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) { 693e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " 694e28a4053SRui Paulo "cryptobinding TLV"); 695e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20); 696e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data", 697e28a4053SRui Paulo buf, 61); 698e28a4053SRui Paulo return -1; 699e28a4053SRui Paulo } 700e28a4053SRui Paulo 701e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); 702e28a4053SRui Paulo 703e28a4053SRui Paulo return 0; 704e28a4053SRui Paulo } 705e28a4053SRui Paulo 706e28a4053SRui Paulo 707e28a4053SRui Paulo static void eap_peap_process_phase2_tlv(struct eap_sm *sm, 708e28a4053SRui Paulo struct eap_peap_data *data, 709e28a4053SRui Paulo struct wpabuf *in_data) 710e28a4053SRui Paulo { 711e28a4053SRui Paulo const u8 *pos; 712e28a4053SRui Paulo size_t left; 713e28a4053SRui Paulo const u8 *result_tlv = NULL, *crypto_tlv = NULL; 714e28a4053SRui Paulo size_t result_tlv_len = 0, crypto_tlv_len = 0; 715e28a4053SRui Paulo int tlv_type, mandatory, tlv_len; 716e28a4053SRui Paulo 717e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left); 718e28a4053SRui Paulo if (pos == NULL) { 719e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid EAP-TLV header"); 720e28a4053SRui Paulo return; 721e28a4053SRui Paulo } 722e28a4053SRui Paulo 723e28a4053SRui Paulo /* Parse TLVs */ 724e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left); 725e28a4053SRui Paulo while (left >= 4) { 726e28a4053SRui Paulo mandatory = !!(pos[0] & 0x80); 727e28a4053SRui Paulo tlv_type = pos[0] & 0x3f; 728e28a4053SRui Paulo tlv_type = (tlv_type << 8) | pos[1]; 729e28a4053SRui Paulo tlv_len = ((int) pos[2] << 8) | pos[3]; 730e28a4053SRui Paulo pos += 4; 731e28a4053SRui Paulo left -= 4; 732e28a4053SRui Paulo if ((size_t) tlv_len > left) { 733e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " 734e28a4053SRui Paulo "(tlv_len=%d left=%lu)", tlv_len, 735e28a4053SRui Paulo (unsigned long) left); 736e28a4053SRui Paulo eap_peap_state(data, FAILURE); 737e28a4053SRui Paulo return; 738e28a4053SRui Paulo } 739e28a4053SRui Paulo switch (tlv_type) { 740e28a4053SRui Paulo case EAP_TLV_RESULT_TLV: 741e28a4053SRui Paulo result_tlv = pos; 742e28a4053SRui Paulo result_tlv_len = tlv_len; 743e28a4053SRui Paulo break; 744e28a4053SRui Paulo case EAP_TLV_CRYPTO_BINDING_TLV: 745e28a4053SRui Paulo crypto_tlv = pos; 746e28a4053SRui Paulo crypto_tlv_len = tlv_len; 747e28a4053SRui Paulo break; 748e28a4053SRui Paulo default: 749e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " 750e28a4053SRui Paulo "%d%s", tlv_type, 751e28a4053SRui Paulo mandatory ? " (mandatory)" : ""); 752e28a4053SRui Paulo if (mandatory) { 753e28a4053SRui Paulo eap_peap_state(data, FAILURE); 754e28a4053SRui Paulo return; 755e28a4053SRui Paulo } 756e28a4053SRui Paulo /* Ignore this TLV, but process other TLVs */ 757e28a4053SRui Paulo break; 758e28a4053SRui Paulo } 759e28a4053SRui Paulo 760e28a4053SRui Paulo pos += tlv_len; 761e28a4053SRui Paulo left -= tlv_len; 762e28a4053SRui Paulo } 763e28a4053SRui Paulo if (left) { 764e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " 765e28a4053SRui Paulo "Request (left=%lu)", (unsigned long) left); 766e28a4053SRui Paulo eap_peap_state(data, FAILURE); 767e28a4053SRui Paulo return; 768e28a4053SRui Paulo } 769e28a4053SRui Paulo 770e28a4053SRui Paulo /* Process supported TLVs */ 771e28a4053SRui Paulo if (crypto_tlv && data->crypto_binding_sent) { 772e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", 773e28a4053SRui Paulo crypto_tlv, crypto_tlv_len); 774e28a4053SRui Paulo if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, 775e28a4053SRui Paulo crypto_tlv_len + 4) < 0) { 776e28a4053SRui Paulo eap_peap_state(data, FAILURE); 777e28a4053SRui Paulo return; 778e28a4053SRui Paulo } 779e28a4053SRui Paulo data->crypto_binding_used = 1; 780e28a4053SRui Paulo } else if (!crypto_tlv && data->crypto_binding_sent && 781e28a4053SRui Paulo data->crypto_binding == REQUIRE_BINDING) { 782e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); 783e28a4053SRui Paulo eap_peap_state(data, FAILURE); 784e28a4053SRui Paulo return; 785e28a4053SRui Paulo } 786e28a4053SRui Paulo 787e28a4053SRui Paulo if (result_tlv) { 788e28a4053SRui Paulo int status; 789e28a4053SRui Paulo const char *requested; 790e28a4053SRui Paulo 791e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV", 792e28a4053SRui Paulo result_tlv, result_tlv_len); 793e28a4053SRui Paulo if (result_tlv_len < 2) { 794e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV " 795e28a4053SRui Paulo "(len=%lu)", 796e28a4053SRui Paulo (unsigned long) result_tlv_len); 797e28a4053SRui Paulo eap_peap_state(data, FAILURE); 798e28a4053SRui Paulo return; 799e28a4053SRui Paulo } 800e28a4053SRui Paulo requested = data->tlv_request == TLV_REQ_SUCCESS ? "Success" : 801e28a4053SRui Paulo "Failure"; 802e28a4053SRui Paulo status = WPA_GET_BE16(result_tlv); 803e28a4053SRui Paulo if (status == EAP_TLV_RESULT_SUCCESS) { 804e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " 805e28a4053SRui Paulo "- requested %s", requested); 806325151a3SRui Paulo if (data->tlv_request == TLV_REQ_SUCCESS) { 807e28a4053SRui Paulo eap_peap_state(data, SUCCESS); 808325151a3SRui Paulo eap_peap_valid_session(sm, data); 809325151a3SRui Paulo } else { 810e28a4053SRui Paulo eap_peap_state(data, FAILURE); 811325151a3SRui Paulo } 812e28a4053SRui Paulo 813e28a4053SRui Paulo } else if (status == EAP_TLV_RESULT_FAILURE) { 814e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " 815e28a4053SRui Paulo "- requested %s", requested); 816e28a4053SRui Paulo eap_peap_state(data, FAILURE); 817e28a4053SRui Paulo } else { 818e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result " 819e28a4053SRui Paulo "Status %d", status); 820e28a4053SRui Paulo eap_peap_state(data, FAILURE); 821e28a4053SRui Paulo } 822e28a4053SRui Paulo } 823e28a4053SRui Paulo } 824e28a4053SRui Paulo 825e28a4053SRui Paulo 826e28a4053SRui Paulo #ifdef EAP_SERVER_TNC 827e28a4053SRui Paulo static void eap_peap_process_phase2_soh(struct eap_sm *sm, 828e28a4053SRui Paulo struct eap_peap_data *data, 829e28a4053SRui Paulo struct wpabuf *in_data) 830e28a4053SRui Paulo { 831e28a4053SRui Paulo const u8 *pos, *vpos; 832e28a4053SRui Paulo size_t left; 833e28a4053SRui Paulo const u8 *soh_tlv = NULL; 834e28a4053SRui Paulo size_t soh_tlv_len = 0; 835e28a4053SRui Paulo int tlv_type, mandatory, tlv_len, vtlv_len; 836325151a3SRui Paulo u32 next_type; 837e28a4053SRui Paulo u32 vendor_id; 838e28a4053SRui Paulo 839e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left); 840e28a4053SRui Paulo if (pos == NULL) { 841e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP " 842e28a4053SRui Paulo "Extensions Method header - skip TNC"); 843e28a4053SRui Paulo goto auth_method; 844e28a4053SRui Paulo } 845e28a4053SRui Paulo 846e28a4053SRui Paulo /* Parse TLVs */ 847e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", pos, left); 848e28a4053SRui Paulo while (left >= 4) { 849e28a4053SRui Paulo mandatory = !!(pos[0] & 0x80); 850e28a4053SRui Paulo tlv_type = pos[0] & 0x3f; 851e28a4053SRui Paulo tlv_type = (tlv_type << 8) | pos[1]; 852e28a4053SRui Paulo tlv_len = ((int) pos[2] << 8) | pos[3]; 853e28a4053SRui Paulo pos += 4; 854e28a4053SRui Paulo left -= 4; 855e28a4053SRui Paulo if ((size_t) tlv_len > left) { 856e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " 857e28a4053SRui Paulo "(tlv_len=%d left=%lu)", tlv_len, 858e28a4053SRui Paulo (unsigned long) left); 859e28a4053SRui Paulo eap_peap_state(data, FAILURE); 860e28a4053SRui Paulo return; 861e28a4053SRui Paulo } 862e28a4053SRui Paulo switch (tlv_type) { 863e28a4053SRui Paulo case EAP_TLV_VENDOR_SPECIFIC_TLV: 864e28a4053SRui Paulo if (tlv_len < 4) { 865e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short " 866e28a4053SRui Paulo "vendor specific TLV (len=%d)", 867e28a4053SRui Paulo (int) tlv_len); 868e28a4053SRui Paulo eap_peap_state(data, FAILURE); 869e28a4053SRui Paulo return; 870e28a4053SRui Paulo } 871e28a4053SRui Paulo 872e28a4053SRui Paulo vendor_id = WPA_GET_BE32(pos); 873e28a4053SRui Paulo if (vendor_id != EAP_VENDOR_MICROSOFT) { 874e28a4053SRui Paulo if (mandatory) { 875e28a4053SRui Paulo eap_peap_state(data, FAILURE); 876e28a4053SRui Paulo return; 877e28a4053SRui Paulo } 878e28a4053SRui Paulo break; 879e28a4053SRui Paulo } 880e28a4053SRui Paulo 881e28a4053SRui Paulo vpos = pos + 4; 882e28a4053SRui Paulo mandatory = !!(vpos[0] & 0x80); 883e28a4053SRui Paulo tlv_type = vpos[0] & 0x3f; 884e28a4053SRui Paulo tlv_type = (tlv_type << 8) | vpos[1]; 885e28a4053SRui Paulo vtlv_len = ((int) vpos[2] << 8) | vpos[3]; 886e28a4053SRui Paulo vpos += 4; 887e28a4053SRui Paulo if (vpos + vtlv_len > pos + left) { 888e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV " 889e28a4053SRui Paulo "underrun"); 890e28a4053SRui Paulo eap_peap_state(data, FAILURE); 891e28a4053SRui Paulo return; 892e28a4053SRui Paulo } 893e28a4053SRui Paulo 894e28a4053SRui Paulo if (tlv_type == 1) { 895e28a4053SRui Paulo soh_tlv = vpos; 896e28a4053SRui Paulo soh_tlv_len = vtlv_len; 897e28a4053SRui Paulo break; 898e28a4053SRui Paulo } 899e28a4053SRui Paulo 900e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV " 901e28a4053SRui Paulo "Type %d%s", tlv_type, 902e28a4053SRui Paulo mandatory ? " (mandatory)" : ""); 903e28a4053SRui Paulo if (mandatory) { 904e28a4053SRui Paulo eap_peap_state(data, FAILURE); 905e28a4053SRui Paulo return; 906e28a4053SRui Paulo } 907e28a4053SRui Paulo /* Ignore this TLV, but process other TLVs */ 908e28a4053SRui Paulo break; 909e28a4053SRui Paulo default: 910e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " 911e28a4053SRui Paulo "%d%s", tlv_type, 912e28a4053SRui Paulo mandatory ? " (mandatory)" : ""); 913e28a4053SRui Paulo if (mandatory) { 914e28a4053SRui Paulo eap_peap_state(data, FAILURE); 915e28a4053SRui Paulo return; 916e28a4053SRui Paulo } 917e28a4053SRui Paulo /* Ignore this TLV, but process other TLVs */ 918e28a4053SRui Paulo break; 919e28a4053SRui Paulo } 920e28a4053SRui Paulo 921e28a4053SRui Paulo pos += tlv_len; 922e28a4053SRui Paulo left -= tlv_len; 923e28a4053SRui Paulo } 924e28a4053SRui Paulo if (left) { 925e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " 926e28a4053SRui Paulo "Request (left=%lu)", (unsigned long) left); 927e28a4053SRui Paulo eap_peap_state(data, FAILURE); 928e28a4053SRui Paulo return; 929e28a4053SRui Paulo } 930e28a4053SRui Paulo 931e28a4053SRui Paulo /* Process supported TLVs */ 932e28a4053SRui Paulo if (soh_tlv) { 933e28a4053SRui Paulo int failure = 0; 934e28a4053SRui Paulo wpabuf_free(data->soh_response); 935e28a4053SRui Paulo data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len, 936e28a4053SRui Paulo &failure); 937e28a4053SRui Paulo if (failure) { 938e28a4053SRui Paulo eap_peap_state(data, FAILURE); 939e28a4053SRui Paulo return; 940e28a4053SRui Paulo } 941e28a4053SRui Paulo } else { 942e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received"); 943e28a4053SRui Paulo eap_peap_state(data, FAILURE); 944e28a4053SRui Paulo return; 945e28a4053SRui Paulo } 946e28a4053SRui Paulo 947e28a4053SRui Paulo auth_method: 948e28a4053SRui Paulo eap_peap_state(data, PHASE2_METHOD); 949e28a4053SRui Paulo next_type = sm->user->methods[0].method; 950e28a4053SRui Paulo sm->user_eap_method_index = 1; 951325151a3SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type %d", 952325151a3SRui Paulo sm->user->methods[0].vendor, next_type); 953325151a3SRui Paulo eap_peap_phase2_init(sm, data, sm->user->methods[0].vendor, next_type); 954e28a4053SRui Paulo } 955e28a4053SRui Paulo #endif /* EAP_SERVER_TNC */ 956e28a4053SRui Paulo 957e28a4053SRui Paulo 958e28a4053SRui Paulo static void eap_peap_process_phase2_response(struct eap_sm *sm, 959e28a4053SRui Paulo struct eap_peap_data *data, 960e28a4053SRui Paulo struct wpabuf *in_data) 961e28a4053SRui Paulo { 962325151a3SRui Paulo int next_vendor = EAP_VENDOR_IETF; 963325151a3SRui Paulo u32 next_type = EAP_TYPE_NONE; 964e28a4053SRui Paulo const struct eap_hdr *hdr; 965e28a4053SRui Paulo const u8 *pos; 966e28a4053SRui Paulo size_t left; 967e28a4053SRui Paulo 968e28a4053SRui Paulo if (data->state == PHASE2_TLV) { 969e28a4053SRui Paulo eap_peap_process_phase2_tlv(sm, data, in_data); 970e28a4053SRui Paulo return; 971e28a4053SRui Paulo } 972e28a4053SRui Paulo 973e28a4053SRui Paulo #ifdef EAP_SERVER_TNC 974e28a4053SRui Paulo if (data->state == PHASE2_SOH) { 975e28a4053SRui Paulo eap_peap_process_phase2_soh(sm, data, in_data); 976e28a4053SRui Paulo return; 977e28a4053SRui Paulo } 978e28a4053SRui Paulo #endif /* EAP_SERVER_TNC */ 979e28a4053SRui Paulo 980e28a4053SRui Paulo if (data->phase2_priv == NULL) { 981e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " 982e28a4053SRui Paulo "initialized?!", __func__); 983e28a4053SRui Paulo return; 984e28a4053SRui Paulo } 985e28a4053SRui Paulo 986e28a4053SRui Paulo hdr = wpabuf_head(in_data); 987e28a4053SRui Paulo pos = (const u8 *) (hdr + 1); 988e28a4053SRui Paulo 989e28a4053SRui Paulo if (wpabuf_len(in_data) > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { 990e28a4053SRui Paulo left = wpabuf_len(in_data) - sizeof(*hdr); 991e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; " 992e28a4053SRui Paulo "allowed types", pos + 1, left - 1); 993e28a4053SRui Paulo eap_sm_process_nak(sm, pos + 1, left - 1); 994e28a4053SRui Paulo if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && 995325151a3SRui Paulo (sm->user->methods[sm->user_eap_method_index].vendor != 996325151a3SRui Paulo EAP_VENDOR_IETF || 997e28a4053SRui Paulo sm->user->methods[sm->user_eap_method_index].method != 998325151a3SRui Paulo EAP_TYPE_NONE)) { 999325151a3SRui Paulo next_vendor = sm->user->methods[ 1000325151a3SRui Paulo sm->user_eap_method_index].vendor; 1001e28a4053SRui Paulo next_type = sm->user->methods[ 1002e28a4053SRui Paulo sm->user_eap_method_index++].method; 1003325151a3SRui Paulo wpa_printf(MSG_DEBUG, 1004325151a3SRui Paulo "EAP-PEAP: try EAP vendor %d type 0x%x", 1005325151a3SRui Paulo next_vendor, next_type); 1006e28a4053SRui Paulo } else { 1007e28a4053SRui Paulo eap_peap_req_failure(sm, data); 1008325151a3SRui Paulo next_vendor = EAP_VENDOR_IETF; 1009e28a4053SRui Paulo next_type = EAP_TYPE_NONE; 1010e28a4053SRui Paulo } 1011325151a3SRui Paulo eap_peap_phase2_init(sm, data, next_vendor, next_type); 1012e28a4053SRui Paulo return; 1013e28a4053SRui Paulo } 1014e28a4053SRui Paulo 1015e28a4053SRui Paulo if (data->phase2_method->check(sm, data->phase2_priv, in_data)) { 1016e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to " 1017e28a4053SRui Paulo "ignore the packet"); 1018e28a4053SRui Paulo return; 1019e28a4053SRui Paulo } 1020e28a4053SRui Paulo 1021e28a4053SRui Paulo data->phase2_method->process(sm, data->phase2_priv, in_data); 1022e28a4053SRui Paulo 1023e28a4053SRui Paulo if (sm->method_pending == METHOD_PENDING_WAIT) { 1024e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method is in " 1025e28a4053SRui Paulo "pending wait state - save decrypted response"); 1026e28a4053SRui Paulo wpabuf_free(data->pending_phase2_resp); 1027e28a4053SRui Paulo data->pending_phase2_resp = wpabuf_dup(in_data); 1028e28a4053SRui Paulo } 1029e28a4053SRui Paulo 1030e28a4053SRui Paulo if (!data->phase2_method->isDone(sm, data->phase2_priv)) 1031e28a4053SRui Paulo return; 1032e28a4053SRui Paulo 1033e28a4053SRui Paulo if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { 1034e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); 1035e28a4053SRui Paulo eap_peap_req_failure(sm, data); 1036325151a3SRui Paulo next_vendor = EAP_VENDOR_IETF; 1037e28a4053SRui Paulo next_type = EAP_TYPE_NONE; 1038325151a3SRui Paulo eap_peap_phase2_init(sm, data, next_vendor, next_type); 1039e28a4053SRui Paulo return; 1040e28a4053SRui Paulo } 1041e28a4053SRui Paulo 1042e28a4053SRui Paulo os_free(data->phase2_key); 1043e28a4053SRui Paulo if (data->phase2_method->getKey) { 1044e28a4053SRui Paulo data->phase2_key = data->phase2_method->getKey( 1045e28a4053SRui Paulo sm, data->phase2_priv, &data->phase2_key_len); 1046e28a4053SRui Paulo if (data->phase2_key == NULL) { 1047e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " 1048e28a4053SRui Paulo "failed"); 1049e28a4053SRui Paulo eap_peap_req_failure(sm, data); 1050325151a3SRui Paulo eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, 1051325151a3SRui Paulo EAP_TYPE_NONE); 1052e28a4053SRui Paulo return; 1053e28a4053SRui Paulo } 1054e28a4053SRui Paulo } 1055e28a4053SRui Paulo 1056e28a4053SRui Paulo switch (data->state) { 1057e28a4053SRui Paulo case PHASE1_ID2: 1058e28a4053SRui Paulo case PHASE2_ID: 1059e28a4053SRui Paulo case PHASE2_SOH: 1060e28a4053SRui Paulo if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { 1061e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 " 1062e28a4053SRui Paulo "Identity not found in the user " 1063e28a4053SRui Paulo "database", 1064e28a4053SRui Paulo sm->identity, sm->identity_len); 1065e28a4053SRui Paulo eap_peap_req_failure(sm, data); 1066325151a3SRui Paulo next_vendor = EAP_VENDOR_IETF; 1067e28a4053SRui Paulo next_type = EAP_TYPE_NONE; 1068e28a4053SRui Paulo break; 1069e28a4053SRui Paulo } 1070e28a4053SRui Paulo 1071e28a4053SRui Paulo #ifdef EAP_SERVER_TNC 1072c1d255d3SCy Schubert if (data->state != PHASE2_SOH && sm->cfg->tnc && 1073e28a4053SRui Paulo data->peap_version == 0) { 1074e28a4053SRui Paulo eap_peap_state(data, PHASE2_SOH); 1075e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize " 1076e28a4053SRui Paulo "TNC (NAP SOH)"); 1077325151a3SRui Paulo next_vendor = EAP_VENDOR_IETF; 1078e28a4053SRui Paulo next_type = EAP_TYPE_NONE; 1079e28a4053SRui Paulo break; 1080e28a4053SRui Paulo } 1081e28a4053SRui Paulo #endif /* EAP_SERVER_TNC */ 1082e28a4053SRui Paulo 1083e28a4053SRui Paulo eap_peap_state(data, PHASE2_METHOD); 1084325151a3SRui Paulo next_vendor = sm->user->methods[0].vendor; 1085e28a4053SRui Paulo next_type = sm->user->methods[0].method; 1086e28a4053SRui Paulo sm->user_eap_method_index = 1; 1087325151a3SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type 0x%x", 1088325151a3SRui Paulo next_vendor, next_type); 1089e28a4053SRui Paulo break; 1090e28a4053SRui Paulo case PHASE2_METHOD: 1091e28a4053SRui Paulo eap_peap_req_success(sm, data); 1092325151a3SRui Paulo next_vendor = EAP_VENDOR_IETF; 1093e28a4053SRui Paulo next_type = EAP_TYPE_NONE; 1094e28a4053SRui Paulo break; 1095e28a4053SRui Paulo case FAILURE: 1096e28a4053SRui Paulo break; 1097e28a4053SRui Paulo default: 1098e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", 1099e28a4053SRui Paulo __func__, data->state); 1100e28a4053SRui Paulo break; 1101e28a4053SRui Paulo } 1102e28a4053SRui Paulo 1103325151a3SRui Paulo eap_peap_phase2_init(sm, data, next_vendor, next_type); 1104e28a4053SRui Paulo } 1105e28a4053SRui Paulo 1106e28a4053SRui Paulo 1107e28a4053SRui Paulo static void eap_peap_process_phase2(struct eap_sm *sm, 1108e28a4053SRui Paulo struct eap_peap_data *data, 1109e28a4053SRui Paulo const struct wpabuf *respData, 1110e28a4053SRui Paulo struct wpabuf *in_buf) 1111e28a4053SRui Paulo { 1112e28a4053SRui Paulo struct wpabuf *in_decrypted; 1113e28a4053SRui Paulo const struct eap_hdr *hdr; 1114e28a4053SRui Paulo size_t len; 1115e28a4053SRui Paulo 1116e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" 1117e28a4053SRui Paulo " Phase 2", (unsigned long) wpabuf_len(in_buf)); 1118e28a4053SRui Paulo 1119e28a4053SRui Paulo if (data->pending_phase2_resp) { 1120e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " 1121e28a4053SRui Paulo "skip decryption and use old data"); 1122e28a4053SRui Paulo eap_peap_process_phase2_response(sm, data, 1123e28a4053SRui Paulo data->pending_phase2_resp); 1124e28a4053SRui Paulo wpabuf_free(data->pending_phase2_resp); 1125e28a4053SRui Paulo data->pending_phase2_resp = NULL; 1126e28a4053SRui Paulo return; 1127e28a4053SRui Paulo } 1128e28a4053SRui Paulo 1129c1d255d3SCy Schubert in_decrypted = tls_connection_decrypt(sm->cfg->ssl_ctx, data->ssl.conn, 1130e28a4053SRui Paulo in_buf); 1131e28a4053SRui Paulo if (in_decrypted == NULL) { 1132e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " 1133e28a4053SRui Paulo "data"); 1134e28a4053SRui Paulo eap_peap_state(data, FAILURE); 1135e28a4053SRui Paulo return; 1136e28a4053SRui Paulo } 1137e28a4053SRui Paulo 1138e28a4053SRui Paulo wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", 1139e28a4053SRui Paulo in_decrypted); 1140e28a4053SRui Paulo 1141e28a4053SRui Paulo if (data->peap_version == 0 && data->state != PHASE2_TLV) { 1142e28a4053SRui Paulo const struct eap_hdr *resp; 1143e28a4053SRui Paulo struct eap_hdr *nhdr; 1144e28a4053SRui Paulo struct wpabuf *nbuf = 1145e28a4053SRui Paulo wpabuf_alloc(sizeof(struct eap_hdr) + 1146e28a4053SRui Paulo wpabuf_len(in_decrypted)); 1147e28a4053SRui Paulo if (nbuf == NULL) { 1148e28a4053SRui Paulo wpabuf_free(in_decrypted); 1149e28a4053SRui Paulo return; 1150e28a4053SRui Paulo } 1151e28a4053SRui Paulo 1152e28a4053SRui Paulo resp = wpabuf_head(respData); 1153e28a4053SRui Paulo nhdr = wpabuf_put(nbuf, sizeof(*nhdr)); 1154e28a4053SRui Paulo nhdr->code = resp->code; 1155e28a4053SRui Paulo nhdr->identifier = resp->identifier; 1156e28a4053SRui Paulo nhdr->length = host_to_be16(sizeof(struct eap_hdr) + 1157e28a4053SRui Paulo wpabuf_len(in_decrypted)); 1158e28a4053SRui Paulo wpabuf_put_buf(nbuf, in_decrypted); 1159e28a4053SRui Paulo wpabuf_free(in_decrypted); 1160e28a4053SRui Paulo 1161e28a4053SRui Paulo in_decrypted = nbuf; 1162e28a4053SRui Paulo } 1163e28a4053SRui Paulo 1164e28a4053SRui Paulo hdr = wpabuf_head(in_decrypted); 1165e28a4053SRui Paulo if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) { 1166e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " 1167e28a4053SRui Paulo "EAP frame (len=%lu)", 1168e28a4053SRui Paulo (unsigned long) wpabuf_len(in_decrypted)); 1169e28a4053SRui Paulo wpabuf_free(in_decrypted); 1170e28a4053SRui Paulo eap_peap_req_failure(sm, data); 1171e28a4053SRui Paulo return; 1172e28a4053SRui Paulo } 1173e28a4053SRui Paulo len = be_to_host16(hdr->length); 1174e28a4053SRui Paulo if (len > wpabuf_len(in_decrypted)) { 1175e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " 1176e28a4053SRui Paulo "Phase 2 EAP frame (len=%lu hdr->length=%lu)", 1177e28a4053SRui Paulo (unsigned long) wpabuf_len(in_decrypted), 1178e28a4053SRui Paulo (unsigned long) len); 1179e28a4053SRui Paulo wpabuf_free(in_decrypted); 1180e28a4053SRui Paulo eap_peap_req_failure(sm, data); 1181e28a4053SRui Paulo return; 1182e28a4053SRui Paulo } 1183e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " 1184e28a4053SRui Paulo "identifier=%d length=%lu", hdr->code, hdr->identifier, 1185e28a4053SRui Paulo (unsigned long) len); 1186e28a4053SRui Paulo switch (hdr->code) { 1187e28a4053SRui Paulo case EAP_CODE_RESPONSE: 1188e28a4053SRui Paulo eap_peap_process_phase2_response(sm, data, in_decrypted); 1189e28a4053SRui Paulo break; 1190e28a4053SRui Paulo case EAP_CODE_SUCCESS: 1191e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); 1192e28a4053SRui Paulo if (data->state == SUCCESS_REQ) { 1193e28a4053SRui Paulo eap_peap_state(data, SUCCESS); 1194325151a3SRui Paulo eap_peap_valid_session(sm, data); 1195e28a4053SRui Paulo } 1196e28a4053SRui Paulo break; 1197e28a4053SRui Paulo case EAP_CODE_FAILURE: 1198e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); 1199e28a4053SRui Paulo eap_peap_state(data, FAILURE); 1200e28a4053SRui Paulo break; 1201e28a4053SRui Paulo default: 1202e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " 1203e28a4053SRui Paulo "Phase 2 EAP header", hdr->code); 1204e28a4053SRui Paulo break; 1205e28a4053SRui Paulo } 1206e28a4053SRui Paulo 1207e28a4053SRui Paulo wpabuf_free(in_decrypted); 1208e28a4053SRui Paulo } 1209e28a4053SRui Paulo 1210e28a4053SRui Paulo 1211e28a4053SRui Paulo static int eap_peap_process_version(struct eap_sm *sm, void *priv, 1212e28a4053SRui Paulo int peer_version) 1213e28a4053SRui Paulo { 1214e28a4053SRui Paulo struct eap_peap_data *data = priv; 1215e28a4053SRui Paulo 1216e28a4053SRui Paulo data->recv_version = peer_version; 1217e28a4053SRui Paulo if (data->force_version >= 0 && peer_version != data->force_version) { 1218e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" 1219e28a4053SRui Paulo " version (forced=%d peer=%d) - reject", 1220e28a4053SRui Paulo data->force_version, peer_version); 1221e28a4053SRui Paulo return -1; 1222e28a4053SRui Paulo } 1223e28a4053SRui Paulo if (peer_version < data->peap_version) { 1224e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; " 1225e28a4053SRui Paulo "use version %d", 1226e28a4053SRui Paulo peer_version, data->peap_version, peer_version); 1227e28a4053SRui Paulo data->peap_version = peer_version; 1228e28a4053SRui Paulo } 1229e28a4053SRui Paulo 1230e28a4053SRui Paulo return 0; 1231e28a4053SRui Paulo } 1232e28a4053SRui Paulo 1233e28a4053SRui Paulo 1234e28a4053SRui Paulo static void eap_peap_process_msg(struct eap_sm *sm, void *priv, 1235e28a4053SRui Paulo const struct wpabuf *respData) 1236e28a4053SRui Paulo { 1237e28a4053SRui Paulo struct eap_peap_data *data = priv; 1238e28a4053SRui Paulo 1239e28a4053SRui Paulo switch (data->state) { 1240e28a4053SRui Paulo case PHASE1: 1241e28a4053SRui Paulo if (eap_server_tls_phase1(sm, &data->ssl) < 0) { 1242e28a4053SRui Paulo eap_peap_state(data, FAILURE); 1243e28a4053SRui Paulo break; 1244e28a4053SRui Paulo } 1245e28a4053SRui Paulo break; 1246e28a4053SRui Paulo case PHASE2_START: 1247e28a4053SRui Paulo eap_peap_state(data, PHASE2_ID); 1248325151a3SRui Paulo eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF, 1249325151a3SRui Paulo EAP_TYPE_IDENTITY); 1250e28a4053SRui Paulo break; 1251e28a4053SRui Paulo case PHASE1_ID2: 1252e28a4053SRui Paulo case PHASE2_ID: 1253e28a4053SRui Paulo case PHASE2_METHOD: 1254e28a4053SRui Paulo case PHASE2_SOH: 1255e28a4053SRui Paulo case PHASE2_TLV: 1256e28a4053SRui Paulo eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in); 1257e28a4053SRui Paulo break; 1258e28a4053SRui Paulo case SUCCESS_REQ: 1259e28a4053SRui Paulo eap_peap_state(data, SUCCESS); 1260325151a3SRui Paulo eap_peap_valid_session(sm, data); 1261e28a4053SRui Paulo break; 1262e28a4053SRui Paulo case FAILURE_REQ: 1263e28a4053SRui Paulo eap_peap_state(data, FAILURE); 1264e28a4053SRui Paulo break; 1265e28a4053SRui Paulo default: 1266e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s", 1267e28a4053SRui Paulo data->state, __func__); 1268e28a4053SRui Paulo break; 1269e28a4053SRui Paulo } 1270e28a4053SRui Paulo } 1271e28a4053SRui Paulo 1272e28a4053SRui Paulo 1273e28a4053SRui Paulo static void eap_peap_process(struct eap_sm *sm, void *priv, 1274e28a4053SRui Paulo struct wpabuf *respData) 1275e28a4053SRui Paulo { 1276e28a4053SRui Paulo struct eap_peap_data *data = priv; 1277325151a3SRui Paulo const struct wpabuf *buf; 1278325151a3SRui Paulo const u8 *pos; 1279325151a3SRui Paulo u8 id_len; 1280325151a3SRui Paulo 1281e28a4053SRui Paulo if (eap_server_tls_process(sm, &data->ssl, respData, data, 1282e28a4053SRui Paulo EAP_TYPE_PEAP, eap_peap_process_version, 1283325151a3SRui Paulo eap_peap_process_msg) < 0) { 1284e28a4053SRui Paulo eap_peap_state(data, FAILURE); 1285325151a3SRui Paulo return; 1286325151a3SRui Paulo } 1287325151a3SRui Paulo 1288325151a3SRui Paulo if (data->state == SUCCESS || 1289c1d255d3SCy Schubert !tls_connection_established(sm->cfg->ssl_ctx, data->ssl.conn) || 1290c1d255d3SCy Schubert !tls_connection_resumed(sm->cfg->ssl_ctx, data->ssl.conn)) 1291325151a3SRui Paulo return; 1292325151a3SRui Paulo 1293325151a3SRui Paulo buf = tls_connection_get_success_data(data->ssl.conn); 1294325151a3SRui Paulo if (!buf || wpabuf_len(buf) < 2) { 1295325151a3SRui Paulo wpa_printf(MSG_DEBUG, 1296325151a3SRui Paulo "EAP-PEAP: No success data in resumed session - reject attempt"); 1297325151a3SRui Paulo eap_peap_state(data, FAILURE); 1298325151a3SRui Paulo return; 1299325151a3SRui Paulo } 1300325151a3SRui Paulo 1301325151a3SRui Paulo pos = wpabuf_head(buf); 1302325151a3SRui Paulo if (*pos != EAP_TYPE_PEAP) { 1303325151a3SRui Paulo wpa_printf(MSG_DEBUG, 1304325151a3SRui Paulo "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt", 1305325151a3SRui Paulo *pos); 1306325151a3SRui Paulo eap_peap_state(data, FAILURE); 1307325151a3SRui Paulo return; 1308325151a3SRui Paulo } 1309325151a3SRui Paulo 1310325151a3SRui Paulo pos++; 1311325151a3SRui Paulo id_len = *pos++; 1312325151a3SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session", 1313325151a3SRui Paulo pos, id_len); 1314325151a3SRui Paulo os_free(sm->identity); 1315325151a3SRui Paulo sm->identity = os_malloc(id_len ? id_len : 1); 1316325151a3SRui Paulo if (!sm->identity) { 1317325151a3SRui Paulo sm->identity_len = 0; 1318325151a3SRui Paulo eap_peap_state(data, FAILURE); 1319325151a3SRui Paulo return; 1320325151a3SRui Paulo } 1321325151a3SRui Paulo 1322325151a3SRui Paulo os_memcpy(sm->identity, pos, id_len); 1323325151a3SRui Paulo sm->identity_len = id_len; 1324325151a3SRui Paulo 1325325151a3SRui Paulo if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { 1326325151a3SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database", 1327325151a3SRui Paulo sm->identity, sm->identity_len); 1328325151a3SRui Paulo eap_peap_state(data, FAILURE); 1329325151a3SRui Paulo return; 1330325151a3SRui Paulo } 1331325151a3SRui Paulo 1332325151a3SRui Paulo wpa_printf(MSG_DEBUG, 1333325151a3SRui Paulo "EAP-PEAP: Resuming previous session - skip Phase2"); 1334780fb4a2SCy Schubert eap_peap_req_success(sm, data); 1335780fb4a2SCy Schubert if (data->state == SUCCESS_REQ) 1336325151a3SRui Paulo tls_connection_set_success_data_resumed(data->ssl.conn); 1337e28a4053SRui Paulo } 1338e28a4053SRui Paulo 1339e28a4053SRui Paulo 1340c1d255d3SCy Schubert static bool eap_peap_isDone(struct eap_sm *sm, void *priv) 1341e28a4053SRui Paulo { 1342e28a4053SRui Paulo struct eap_peap_data *data = priv; 1343e28a4053SRui Paulo return data->state == SUCCESS || data->state == FAILURE; 1344e28a4053SRui Paulo } 1345e28a4053SRui Paulo 1346e28a4053SRui Paulo 1347e28a4053SRui Paulo static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) 1348e28a4053SRui Paulo { 1349e28a4053SRui Paulo struct eap_peap_data *data = priv; 1350e28a4053SRui Paulo u8 *eapKeyData; 1351c1d255d3SCy Schubert const char *label; 1352c1d255d3SCy Schubert const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP }; 1353c1d255d3SCy Schubert const u8 *context = NULL; 1354c1d255d3SCy Schubert size_t context_len = 0; 1355e28a4053SRui Paulo 1356e28a4053SRui Paulo if (data->state != SUCCESS) 1357e28a4053SRui Paulo return NULL; 1358e28a4053SRui Paulo 1359e28a4053SRui Paulo if (data->crypto_binding_used) { 1360e28a4053SRui Paulo u8 csk[128]; 1361e28a4053SRui Paulo /* 1362e28a4053SRui Paulo * Note: It looks like Microsoft implementation requires null 1363e28a4053SRui Paulo * termination for this label while the one used for deriving 1364e28a4053SRui Paulo * IPMK|CMK did not use null termination. 1365e28a4053SRui Paulo */ 1366f05cddf9SRui Paulo if (peap_prfplus(data->peap_version, data->ipmk, 40, 1367e28a4053SRui Paulo "Session Key Generating Function", 1368f05cddf9SRui Paulo (u8 *) "\00", 1, csk, sizeof(csk)) < 0) 1369f05cddf9SRui Paulo return NULL; 1370e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); 1371e28a4053SRui Paulo eapKeyData = os_malloc(EAP_TLS_KEY_LEN); 1372e28a4053SRui Paulo if (eapKeyData) { 1373e28a4053SRui Paulo os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN); 1374e28a4053SRui Paulo *len = EAP_TLS_KEY_LEN; 1375e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", 1376e28a4053SRui Paulo eapKeyData, EAP_TLS_KEY_LEN); 1377e28a4053SRui Paulo } else { 1378e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive " 1379e28a4053SRui Paulo "key"); 1380e28a4053SRui Paulo } 1381e28a4053SRui Paulo 1382206b73d0SCy Schubert forced_memzero(csk, sizeof(csk)); 13834bc52338SCy Schubert 1384e28a4053SRui Paulo return eapKeyData; 1385e28a4053SRui Paulo } 1386e28a4053SRui Paulo 1387c1d255d3SCy Schubert if (data->ssl.tls_v13) { 1388c1d255d3SCy Schubert label = "EXPORTER_EAP_TLS_Key_Material"; 1389c1d255d3SCy Schubert context = eap_tls13_context; 1390c1d255d3SCy Schubert context_len = sizeof(eap_tls13_context); 1391c1d255d3SCy Schubert } else { 1392e28a4053SRui Paulo /* TODO: PEAPv1 - different label in some cases */ 1393c1d255d3SCy Schubert label = "client EAP encryption"; 1394c1d255d3SCy Schubert } 1395c1d255d3SCy Schubert 1396e28a4053SRui Paulo eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, 1397c1d255d3SCy Schubert label, context, context_len, 13984bc52338SCy Schubert EAP_TLS_KEY_LEN + EAP_EMSK_LEN); 1399e28a4053SRui Paulo if (eapKeyData) { 14004bc52338SCy Schubert os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN); 1401e28a4053SRui Paulo *len = EAP_TLS_KEY_LEN; 1402e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", 1403e28a4053SRui Paulo eapKeyData, EAP_TLS_KEY_LEN); 1404e28a4053SRui Paulo } else { 1405e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key"); 1406e28a4053SRui Paulo } 1407e28a4053SRui Paulo 1408e28a4053SRui Paulo return eapKeyData; 1409e28a4053SRui Paulo } 1410e28a4053SRui Paulo 1411e28a4053SRui Paulo 14124bc52338SCy Schubert static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 14134bc52338SCy Schubert { 14144bc52338SCy Schubert struct eap_peap_data *data = priv; 14154bc52338SCy Schubert u8 *eapKeyData, *emsk; 1416c1d255d3SCy Schubert const char *label; 1417c1d255d3SCy Schubert const u8 eap_tls13_context[1] = { EAP_TYPE_PEAP }; 1418c1d255d3SCy Schubert const u8 *context = NULL; 1419c1d255d3SCy Schubert size_t context_len = 0; 14204bc52338SCy Schubert 14214bc52338SCy Schubert if (data->state != SUCCESS) 14224bc52338SCy Schubert return NULL; 14234bc52338SCy Schubert 14244bc52338SCy Schubert if (data->crypto_binding_used) { 14254bc52338SCy Schubert /* [MS-PEAP] does not define EMSK derivation */ 14264bc52338SCy Schubert return NULL; 14274bc52338SCy Schubert } 14284bc52338SCy Schubert 1429c1d255d3SCy Schubert if (data->ssl.tls_v13) { 1430c1d255d3SCy Schubert label = "EXPORTER_EAP_TLS_Key_Material"; 1431c1d255d3SCy Schubert context = eap_tls13_context; 1432c1d255d3SCy Schubert context_len = sizeof(eap_tls13_context); 1433c1d255d3SCy Schubert } else { 14344bc52338SCy Schubert /* TODO: PEAPv1 - different label in some cases */ 1435c1d255d3SCy Schubert label = "client EAP encryption"; 1436c1d255d3SCy Schubert } 1437c1d255d3SCy Schubert 14384bc52338SCy Schubert eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, 1439c1d255d3SCy Schubert label, context, context_len, 14404bc52338SCy Schubert EAP_TLS_KEY_LEN + EAP_EMSK_LEN); 14414bc52338SCy Schubert if (eapKeyData) { 14424bc52338SCy Schubert emsk = os_memdup(eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); 14434bc52338SCy Schubert bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); 14444bc52338SCy Schubert if (!emsk) 14454bc52338SCy Schubert return NULL; 14464bc52338SCy Schubert *len = EAP_EMSK_LEN; 14474bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived EMSK", 14484bc52338SCy Schubert emsk, EAP_EMSK_LEN); 14494bc52338SCy Schubert } else { 14504bc52338SCy Schubert wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive EMSK"); 14514bc52338SCy Schubert emsk = NULL; 14524bc52338SCy Schubert } 14534bc52338SCy Schubert 14544bc52338SCy Schubert return emsk; 14554bc52338SCy Schubert } 14564bc52338SCy Schubert 14574bc52338SCy Schubert 1458c1d255d3SCy Schubert static bool eap_peap_isSuccess(struct eap_sm *sm, void *priv) 1459e28a4053SRui Paulo { 1460e28a4053SRui Paulo struct eap_peap_data *data = priv; 1461e28a4053SRui Paulo return data->state == SUCCESS; 1462e28a4053SRui Paulo } 1463e28a4053SRui Paulo 1464e28a4053SRui Paulo 14655b9c547cSRui Paulo static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 14665b9c547cSRui Paulo { 14675b9c547cSRui Paulo struct eap_peap_data *data = priv; 14685b9c547cSRui Paulo 14695b9c547cSRui Paulo if (data->state != SUCCESS) 14705b9c547cSRui Paulo return NULL; 14715b9c547cSRui Paulo 14725b9c547cSRui Paulo return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_PEAP, 14735b9c547cSRui Paulo len); 14745b9c547cSRui Paulo } 14755b9c547cSRui Paulo 14765b9c547cSRui Paulo 1477e28a4053SRui Paulo int eap_server_peap_register(void) 1478e28a4053SRui Paulo { 1479e28a4053SRui Paulo struct eap_method *eap; 1480e28a4053SRui Paulo 1481e28a4053SRui Paulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 1482e28a4053SRui Paulo EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); 1483e28a4053SRui Paulo if (eap == NULL) 1484e28a4053SRui Paulo return -1; 1485e28a4053SRui Paulo 1486e28a4053SRui Paulo eap->init = eap_peap_init; 1487e28a4053SRui Paulo eap->reset = eap_peap_reset; 1488e28a4053SRui Paulo eap->buildReq = eap_peap_buildReq; 1489e28a4053SRui Paulo eap->check = eap_peap_check; 1490e28a4053SRui Paulo eap->process = eap_peap_process; 1491e28a4053SRui Paulo eap->isDone = eap_peap_isDone; 1492e28a4053SRui Paulo eap->getKey = eap_peap_getKey; 14934bc52338SCy Schubert eap->get_emsk = eap_peap_get_emsk; 1494e28a4053SRui Paulo eap->isSuccess = eap_peap_isSuccess; 14955b9c547cSRui Paulo eap->getSessionId = eap_peap_get_session_id; 1496e28a4053SRui Paulo 1497780fb4a2SCy Schubert return eap_server_method_register(eap); 1498e28a4053SRui Paulo } 1499