139beb93cSSam Leffler /* 239beb93cSSam Leffler * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) 339beb93cSSam Leffler * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler */ 839beb93cSSam Leffler 939beb93cSSam Leffler #include "includes.h" 1039beb93cSSam Leffler 1139beb93cSSam Leffler #include "common.h" 1239beb93cSSam Leffler #include "crypto/sha1.h" 13e28a4053SRui Paulo #include "crypto/tls.h" 14e28a4053SRui Paulo #include "eap_common/eap_tlv_common.h" 15e28a4053SRui Paulo #include "eap_common/eap_peap_common.h" 1639beb93cSSam Leffler #include "eap_i.h" 1739beb93cSSam Leffler #include "eap_tls_common.h" 1839beb93cSSam Leffler #include "eap_config.h" 1939beb93cSSam Leffler #include "tncc.h" 2039beb93cSSam Leffler 2139beb93cSSam Leffler 2239beb93cSSam Leffler /* Maximum supported PEAP version 2339beb93cSSam Leffler * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt 2439beb93cSSam Leffler * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt 2539beb93cSSam Leffler */ 2639beb93cSSam Leffler #define EAP_PEAP_VERSION 1 2739beb93cSSam Leffler 2839beb93cSSam Leffler 2939beb93cSSam Leffler static void eap_peap_deinit(struct eap_sm *sm, void *priv); 3039beb93cSSam Leffler 3139beb93cSSam Leffler 3239beb93cSSam Leffler struct eap_peap_data { 3339beb93cSSam Leffler struct eap_ssl_data ssl; 3439beb93cSSam Leffler 3539beb93cSSam Leffler int peap_version, force_peap_version, force_new_label; 3639beb93cSSam Leffler 3739beb93cSSam Leffler const struct eap_method *phase2_method; 3839beb93cSSam Leffler void *phase2_priv; 3939beb93cSSam Leffler int phase2_success; 4039beb93cSSam Leffler int phase2_eap_success; 4139beb93cSSam Leffler int phase2_eap_started; 4239beb93cSSam Leffler 4339beb93cSSam Leffler struct eap_method_type phase2_type; 4439beb93cSSam Leffler struct eap_method_type *phase2_types; 4539beb93cSSam Leffler size_t num_phase2_types; 4639beb93cSSam Leffler 4739beb93cSSam Leffler int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner 4839beb93cSSam Leffler * EAP-Success 4939beb93cSSam Leffler * 1 = reply with tunneled EAP-Success to inner 5039beb93cSSam Leffler * EAP-Success and expect AS to send outer 5139beb93cSSam Leffler * (unencrypted) EAP-Success after this 5239beb93cSSam Leffler * 2 = reply with PEAP/TLS ACK to inner 5339beb93cSSam Leffler * EAP-Success and expect AS to send outer 5439beb93cSSam Leffler * (unencrypted) EAP-Success after this */ 5539beb93cSSam Leffler int resuming; /* starting a resumed session */ 5639beb93cSSam Leffler int reauth; /* reauthentication */ 5739beb93cSSam Leffler u8 *key_data; 58*5b9c547cSRui Paulo u8 *session_id; 59*5b9c547cSRui Paulo size_t id_len; 6039beb93cSSam Leffler 6139beb93cSSam Leffler struct wpabuf *pending_phase2_req; 6239beb93cSSam Leffler enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; 6339beb93cSSam Leffler int crypto_binding_used; 6439beb93cSSam Leffler u8 binding_nonce[32]; 6539beb93cSSam Leffler u8 ipmk[40]; 6639beb93cSSam Leffler u8 cmk[20]; 6739beb93cSSam Leffler int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) 6839beb93cSSam Leffler * is enabled. */ 6939beb93cSSam Leffler }; 7039beb93cSSam Leffler 7139beb93cSSam Leffler 7239beb93cSSam Leffler static int eap_peap_parse_phase1(struct eap_peap_data *data, 7339beb93cSSam Leffler const char *phase1) 7439beb93cSSam Leffler { 7539beb93cSSam Leffler const char *pos; 7639beb93cSSam Leffler 7739beb93cSSam Leffler pos = os_strstr(phase1, "peapver="); 7839beb93cSSam Leffler if (pos) { 7939beb93cSSam Leffler data->force_peap_version = atoi(pos + 8); 8039beb93cSSam Leffler data->peap_version = data->force_peap_version; 8139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d", 8239beb93cSSam Leffler data->force_peap_version); 8339beb93cSSam Leffler } 8439beb93cSSam Leffler 8539beb93cSSam Leffler if (os_strstr(phase1, "peaplabel=1")) { 8639beb93cSSam Leffler data->force_new_label = 1; 8739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key " 8839beb93cSSam Leffler "derivation"); 8939beb93cSSam Leffler } 9039beb93cSSam Leffler 9139beb93cSSam Leffler if (os_strstr(phase1, "peap_outer_success=0")) { 9239beb93cSSam Leffler data->peap_outer_success = 0; 9339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on " 9439beb93cSSam Leffler "tunneled EAP-Success"); 9539beb93cSSam Leffler } else if (os_strstr(phase1, "peap_outer_success=1")) { 9639beb93cSSam Leffler data->peap_outer_success = 1; 9739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success " 9839beb93cSSam Leffler "after receiving tunneled EAP-Success"); 9939beb93cSSam Leffler } else if (os_strstr(phase1, "peap_outer_success=2")) { 10039beb93cSSam Leffler data->peap_outer_success = 2; 10139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after " 10239beb93cSSam Leffler "receiving tunneled EAP-Success"); 10339beb93cSSam Leffler } 10439beb93cSSam Leffler 10539beb93cSSam Leffler if (os_strstr(phase1, "crypto_binding=0")) { 10639beb93cSSam Leffler data->crypto_binding = NO_BINDING; 10739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding"); 10839beb93cSSam Leffler } else if (os_strstr(phase1, "crypto_binding=1")) { 10939beb93cSSam Leffler data->crypto_binding = OPTIONAL_BINDING; 11039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding"); 11139beb93cSSam Leffler } else if (os_strstr(phase1, "crypto_binding=2")) { 11239beb93cSSam Leffler data->crypto_binding = REQUIRE_BINDING; 11339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); 11439beb93cSSam Leffler } 11539beb93cSSam Leffler 11639beb93cSSam Leffler #ifdef EAP_TNC 11739beb93cSSam Leffler if (os_strstr(phase1, "tnc=soh2")) { 11839beb93cSSam Leffler data->soh = 2; 11939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); 12039beb93cSSam Leffler } else if (os_strstr(phase1, "tnc=soh1")) { 12139beb93cSSam Leffler data->soh = 1; 12239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled"); 12339beb93cSSam Leffler } else if (os_strstr(phase1, "tnc=soh")) { 12439beb93cSSam Leffler data->soh = 2; 12539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); 12639beb93cSSam Leffler } 12739beb93cSSam Leffler #endif /* EAP_TNC */ 12839beb93cSSam Leffler 12939beb93cSSam Leffler return 0; 13039beb93cSSam Leffler } 13139beb93cSSam Leffler 13239beb93cSSam Leffler 13339beb93cSSam Leffler static void * eap_peap_init(struct eap_sm *sm) 13439beb93cSSam Leffler { 13539beb93cSSam Leffler struct eap_peap_data *data; 13639beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 13739beb93cSSam Leffler 13839beb93cSSam Leffler data = os_zalloc(sizeof(*data)); 13939beb93cSSam Leffler if (data == NULL) 14039beb93cSSam Leffler return NULL; 14139beb93cSSam Leffler sm->peap_done = FALSE; 14239beb93cSSam Leffler data->peap_version = EAP_PEAP_VERSION; 14339beb93cSSam Leffler data->force_peap_version = -1; 14439beb93cSSam Leffler data->peap_outer_success = 2; 14539beb93cSSam Leffler data->crypto_binding = OPTIONAL_BINDING; 14639beb93cSSam Leffler 14739beb93cSSam Leffler if (config && config->phase1 && 14839beb93cSSam Leffler eap_peap_parse_phase1(data, config->phase1) < 0) { 14939beb93cSSam Leffler eap_peap_deinit(sm, data); 15039beb93cSSam Leffler return NULL; 15139beb93cSSam Leffler } 15239beb93cSSam Leffler 15339beb93cSSam Leffler if (eap_peer_select_phase2_methods(config, "auth=", 15439beb93cSSam Leffler &data->phase2_types, 15539beb93cSSam Leffler &data->num_phase2_types) < 0) { 15639beb93cSSam Leffler eap_peap_deinit(sm, data); 15739beb93cSSam Leffler return NULL; 15839beb93cSSam Leffler } 15939beb93cSSam Leffler 16039beb93cSSam Leffler data->phase2_type.vendor = EAP_VENDOR_IETF; 16139beb93cSSam Leffler data->phase2_type.method = EAP_TYPE_NONE; 16239beb93cSSam Leffler 163f05cddf9SRui Paulo if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { 16439beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); 16539beb93cSSam Leffler eap_peap_deinit(sm, data); 16639beb93cSSam Leffler return NULL; 16739beb93cSSam Leffler } 16839beb93cSSam Leffler 16939beb93cSSam Leffler return data; 17039beb93cSSam Leffler } 17139beb93cSSam Leffler 17239beb93cSSam Leffler 173*5b9c547cSRui Paulo static void eap_peap_free_key(struct eap_peap_data *data) 174*5b9c547cSRui Paulo { 175*5b9c547cSRui Paulo if (data->key_data) { 176*5b9c547cSRui Paulo bin_clear_free(data->key_data, EAP_TLS_KEY_LEN); 177*5b9c547cSRui Paulo data->key_data = NULL; 178*5b9c547cSRui Paulo } 179*5b9c547cSRui Paulo } 180*5b9c547cSRui Paulo 181*5b9c547cSRui Paulo 18239beb93cSSam Leffler static void eap_peap_deinit(struct eap_sm *sm, void *priv) 18339beb93cSSam Leffler { 18439beb93cSSam Leffler struct eap_peap_data *data = priv; 18539beb93cSSam Leffler if (data == NULL) 18639beb93cSSam Leffler return; 18739beb93cSSam Leffler if (data->phase2_priv && data->phase2_method) 18839beb93cSSam Leffler data->phase2_method->deinit(sm, data->phase2_priv); 18939beb93cSSam Leffler os_free(data->phase2_types); 19039beb93cSSam Leffler eap_peer_tls_ssl_deinit(sm, &data->ssl); 191*5b9c547cSRui Paulo eap_peap_free_key(data); 192*5b9c547cSRui Paulo os_free(data->session_id); 19339beb93cSSam Leffler wpabuf_free(data->pending_phase2_req); 19439beb93cSSam Leffler os_free(data); 19539beb93cSSam Leffler } 19639beb93cSSam Leffler 19739beb93cSSam Leffler 19839beb93cSSam Leffler /** 19939beb93cSSam Leffler * eap_tlv_build_nak - Build EAP-TLV NAK message 20039beb93cSSam Leffler * @id: EAP identifier for the header 20139beb93cSSam Leffler * @nak_type: TLV type (EAP_TLV_*) 20239beb93cSSam Leffler * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure 20339beb93cSSam Leffler * 204f05cddf9SRui Paulo * This function builds an EAP-TLV NAK message. The caller is responsible for 20539beb93cSSam Leffler * freeing the returned buffer. 20639beb93cSSam Leffler */ 20739beb93cSSam Leffler static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type) 20839beb93cSSam Leffler { 20939beb93cSSam Leffler struct wpabuf *msg; 21039beb93cSSam Leffler 21139beb93cSSam Leffler msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10, 21239beb93cSSam Leffler EAP_CODE_RESPONSE, id); 21339beb93cSSam Leffler if (msg == NULL) 21439beb93cSSam Leffler return NULL; 21539beb93cSSam Leffler 21639beb93cSSam Leffler wpabuf_put_u8(msg, 0x80); /* Mandatory */ 21739beb93cSSam Leffler wpabuf_put_u8(msg, EAP_TLV_NAK_TLV); 21839beb93cSSam Leffler wpabuf_put_be16(msg, 6); /* Length */ 21939beb93cSSam Leffler wpabuf_put_be32(msg, 0); /* Vendor-Id */ 22039beb93cSSam Leffler wpabuf_put_be16(msg, nak_type); /* NAK-Type */ 22139beb93cSSam Leffler 22239beb93cSSam Leffler return msg; 22339beb93cSSam Leffler } 22439beb93cSSam Leffler 22539beb93cSSam Leffler 22639beb93cSSam Leffler static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data, 22739beb93cSSam Leffler u8 *isk, size_t isk_len) 22839beb93cSSam Leffler { 22939beb93cSSam Leffler u8 *key; 23039beb93cSSam Leffler size_t key_len; 23139beb93cSSam Leffler 23239beb93cSSam Leffler os_memset(isk, 0, isk_len); 23339beb93cSSam Leffler if (data->phase2_method == NULL || data->phase2_priv == NULL || 23439beb93cSSam Leffler data->phase2_method->isKeyAvailable == NULL || 23539beb93cSSam Leffler data->phase2_method->getKey == NULL) 23639beb93cSSam Leffler return 0; 23739beb93cSSam Leffler 23839beb93cSSam Leffler if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || 23939beb93cSSam Leffler (key = data->phase2_method->getKey(sm, data->phase2_priv, 24039beb93cSSam Leffler &key_len)) == NULL) { 24139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " 24239beb93cSSam Leffler "from Phase 2"); 24339beb93cSSam Leffler return -1; 24439beb93cSSam Leffler } 24539beb93cSSam Leffler 24639beb93cSSam Leffler if (key_len > isk_len) 24739beb93cSSam Leffler key_len = isk_len; 24839beb93cSSam Leffler os_memcpy(isk, key, key_len); 24939beb93cSSam Leffler os_free(key); 25039beb93cSSam Leffler 25139beb93cSSam Leffler return 0; 25239beb93cSSam Leffler } 25339beb93cSSam Leffler 25439beb93cSSam Leffler 25539beb93cSSam Leffler static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) 25639beb93cSSam Leffler { 25739beb93cSSam Leffler u8 *tk; 25839beb93cSSam Leffler u8 isk[32], imck[60]; 25939beb93cSSam Leffler 26039beb93cSSam Leffler /* 26139beb93cSSam Leffler * Tunnel key (TK) is the first 60 octets of the key generated by 26239beb93cSSam Leffler * phase 1 of PEAP (based on TLS). 26339beb93cSSam Leffler */ 26439beb93cSSam Leffler tk = data->key_data; 26539beb93cSSam Leffler if (tk == NULL) 26639beb93cSSam Leffler return -1; 26739beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); 26839beb93cSSam Leffler 26939beb93cSSam Leffler if (data->reauth && 27039beb93cSSam Leffler tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { 27139beb93cSSam Leffler /* Fast-connect: IPMK|CMK = TK */ 27239beb93cSSam Leffler os_memcpy(data->ipmk, tk, 40); 27339beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", 27439beb93cSSam Leffler data->ipmk, 40); 27539beb93cSSam Leffler os_memcpy(data->cmk, tk + 40, 20); 27639beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", 27739beb93cSSam Leffler data->cmk, 20); 27839beb93cSSam Leffler return 0; 27939beb93cSSam Leffler } 28039beb93cSSam Leffler 28139beb93cSSam Leffler if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) 28239beb93cSSam Leffler return -1; 28339beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); 28439beb93cSSam Leffler 28539beb93cSSam Leffler /* 28639beb93cSSam Leffler * IPMK Seed = "Inner Methods Compound Keys" | ISK 28739beb93cSSam Leffler * TempKey = First 40 octets of TK 28839beb93cSSam Leffler * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) 28939beb93cSSam Leffler * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space 29039beb93cSSam Leffler * in the end of the label just before ISK; is that just a typo?) 29139beb93cSSam Leffler */ 29239beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); 293f05cddf9SRui Paulo if (peap_prfplus(data->peap_version, tk, 40, 294f05cddf9SRui Paulo "Inner Methods Compound Keys", 295f05cddf9SRui Paulo isk, sizeof(isk), imck, sizeof(imck)) < 0) 296f05cddf9SRui Paulo return -1; 29739beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", 29839beb93cSSam Leffler imck, sizeof(imck)); 29939beb93cSSam Leffler 30039beb93cSSam Leffler os_memcpy(data->ipmk, imck, 40); 30139beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); 30239beb93cSSam Leffler os_memcpy(data->cmk, imck + 40, 20); 30339beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); 30439beb93cSSam Leffler 30539beb93cSSam Leffler return 0; 30639beb93cSSam Leffler } 30739beb93cSSam Leffler 30839beb93cSSam Leffler 30939beb93cSSam Leffler static int eap_tlv_add_cryptobinding(struct eap_sm *sm, 31039beb93cSSam Leffler struct eap_peap_data *data, 31139beb93cSSam Leffler struct wpabuf *buf) 31239beb93cSSam Leffler { 31339beb93cSSam Leffler u8 *mac; 31439beb93cSSam Leffler u8 eap_type = EAP_TYPE_PEAP; 31539beb93cSSam Leffler const u8 *addr[2]; 31639beb93cSSam Leffler size_t len[2]; 31739beb93cSSam Leffler u16 tlv_type; 31839beb93cSSam Leffler 31939beb93cSSam Leffler /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ 32039beb93cSSam Leffler addr[0] = wpabuf_put(buf, 0); 32139beb93cSSam Leffler len[0] = 60; 32239beb93cSSam Leffler addr[1] = &eap_type; 32339beb93cSSam Leffler len[1] = 1; 32439beb93cSSam Leffler 32539beb93cSSam Leffler tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; 32639beb93cSSam Leffler wpabuf_put_be16(buf, tlv_type); 32739beb93cSSam Leffler wpabuf_put_be16(buf, 56); 32839beb93cSSam Leffler 32939beb93cSSam Leffler wpabuf_put_u8(buf, 0); /* Reserved */ 33039beb93cSSam Leffler wpabuf_put_u8(buf, data->peap_version); /* Version */ 33139beb93cSSam Leffler wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */ 33239beb93cSSam Leffler wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ 33339beb93cSSam Leffler wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ 33439beb93cSSam Leffler mac = wpabuf_put(buf, 20); /* Compound_MAC */ 33539beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); 33639beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", 33739beb93cSSam Leffler addr[0], len[0]); 33839beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", 33939beb93cSSam Leffler addr[1], len[1]); 34039beb93cSSam Leffler hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); 34139beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); 34239beb93cSSam Leffler data->crypto_binding_used = 1; 34339beb93cSSam Leffler 34439beb93cSSam Leffler return 0; 34539beb93cSSam Leffler } 34639beb93cSSam Leffler 34739beb93cSSam Leffler 34839beb93cSSam Leffler /** 34939beb93cSSam Leffler * eap_tlv_build_result - Build EAP-TLV Result message 35039beb93cSSam Leffler * @id: EAP identifier for the header 35139beb93cSSam Leffler * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) 35239beb93cSSam Leffler * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure 35339beb93cSSam Leffler * 354f05cddf9SRui Paulo * This function builds an EAP-TLV Result message. The caller is responsible 355f05cddf9SRui Paulo * for freeing the returned buffer. 35639beb93cSSam Leffler */ 35739beb93cSSam Leffler static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm, 35839beb93cSSam Leffler struct eap_peap_data *data, 35939beb93cSSam Leffler int crypto_tlv_used, 36039beb93cSSam Leffler int id, u16 status) 36139beb93cSSam Leffler { 36239beb93cSSam Leffler struct wpabuf *msg; 36339beb93cSSam Leffler size_t len; 36439beb93cSSam Leffler 36539beb93cSSam Leffler if (data->crypto_binding == NO_BINDING) 36639beb93cSSam Leffler crypto_tlv_used = 0; 36739beb93cSSam Leffler 36839beb93cSSam Leffler len = 6; 36939beb93cSSam Leffler if (crypto_tlv_used) 37039beb93cSSam Leffler len += 60; /* Cryptobinding TLV */ 37139beb93cSSam Leffler msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, 37239beb93cSSam Leffler EAP_CODE_RESPONSE, id); 37339beb93cSSam Leffler if (msg == NULL) 37439beb93cSSam Leffler return NULL; 37539beb93cSSam Leffler 37639beb93cSSam Leffler wpabuf_put_u8(msg, 0x80); /* Mandatory */ 37739beb93cSSam Leffler wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV); 37839beb93cSSam Leffler wpabuf_put_be16(msg, 2); /* Length */ 37939beb93cSSam Leffler wpabuf_put_be16(msg, status); /* Status */ 38039beb93cSSam Leffler 38139beb93cSSam Leffler if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { 38239beb93cSSam Leffler wpabuf_free(msg); 38339beb93cSSam Leffler return NULL; 38439beb93cSSam Leffler } 38539beb93cSSam Leffler 38639beb93cSSam Leffler return msg; 38739beb93cSSam Leffler } 38839beb93cSSam Leffler 38939beb93cSSam Leffler 39039beb93cSSam Leffler static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, 39139beb93cSSam Leffler struct eap_peap_data *data, 39239beb93cSSam Leffler const u8 *crypto_tlv, 39339beb93cSSam Leffler size_t crypto_tlv_len) 39439beb93cSSam Leffler { 39539beb93cSSam Leffler u8 buf[61], mac[SHA1_MAC_LEN]; 39639beb93cSSam Leffler const u8 *pos; 39739beb93cSSam Leffler 39839beb93cSSam Leffler if (eap_peap_derive_cmk(sm, data) < 0) { 39939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); 40039beb93cSSam Leffler return -1; 40139beb93cSSam Leffler } 40239beb93cSSam Leffler 40339beb93cSSam Leffler if (crypto_tlv_len != 4 + 56) { 40439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " 40539beb93cSSam Leffler "length %d", (int) crypto_tlv_len); 40639beb93cSSam Leffler return -1; 40739beb93cSSam Leffler } 40839beb93cSSam Leffler 40939beb93cSSam Leffler pos = crypto_tlv; 41039beb93cSSam Leffler pos += 4; /* TLV header */ 41139beb93cSSam Leffler if (pos[1] != data->peap_version) { 41239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " 41339beb93cSSam Leffler "mismatch (was %d; expected %d)", 41439beb93cSSam Leffler pos[1], data->peap_version); 41539beb93cSSam Leffler return -1; 41639beb93cSSam Leffler } 41739beb93cSSam Leffler 41839beb93cSSam Leffler if (pos[3] != 0) { 41939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " 42039beb93cSSam Leffler "SubType %d", pos[3]); 42139beb93cSSam Leffler return -1; 42239beb93cSSam Leffler } 42339beb93cSSam Leffler pos += 4; 42439beb93cSSam Leffler os_memcpy(data->binding_nonce, pos, 32); 42539beb93cSSam Leffler pos += 32; /* Nonce */ 42639beb93cSSam Leffler 42739beb93cSSam Leffler /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ 42839beb93cSSam Leffler os_memcpy(buf, crypto_tlv, 60); 42939beb93cSSam Leffler os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ 43039beb93cSSam Leffler buf[60] = EAP_TYPE_PEAP; 43139beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data", 43239beb93cSSam Leffler buf, sizeof(buf)); 43339beb93cSSam Leffler hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); 43439beb93cSSam Leffler 435*5b9c547cSRui Paulo if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) { 43639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " 43739beb93cSSam Leffler "cryptobinding TLV"); 43839beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC", 43939beb93cSSam Leffler pos, SHA1_MAC_LEN); 44039beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC", 44139beb93cSSam Leffler mac, SHA1_MAC_LEN); 44239beb93cSSam Leffler return -1; 44339beb93cSSam Leffler } 44439beb93cSSam Leffler 44539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); 44639beb93cSSam Leffler 44739beb93cSSam Leffler return 0; 44839beb93cSSam Leffler } 44939beb93cSSam Leffler 45039beb93cSSam Leffler 45139beb93cSSam Leffler /** 45239beb93cSSam Leffler * eap_tlv_process - Process a received EAP-TLV message and generate a response 45339beb93cSSam Leffler * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() 45439beb93cSSam Leffler * @ret: Return values from EAP request validation and processing 45539beb93cSSam Leffler * @req: EAP-TLV request to be processed. The caller must have validated that 45639beb93cSSam Leffler * the buffer is large enough to contain full request (hdr->length bytes) and 45739beb93cSSam Leffler * that the EAP type is EAP_TYPE_TLV. 45839beb93cSSam Leffler * @resp: Buffer to return a pointer to the allocated response message. This 45939beb93cSSam Leffler * field should be initialized to %NULL before the call. The value will be 46039beb93cSSam Leffler * updated if a response message is generated. The caller is responsible for 46139beb93cSSam Leffler * freeing the allocated message. 46239beb93cSSam Leffler * @force_failure: Force negotiation to fail 46339beb93cSSam Leffler * Returns: 0 on success, -1 on failure 46439beb93cSSam Leffler */ 46539beb93cSSam Leffler static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, 46639beb93cSSam Leffler struct eap_method_ret *ret, 46739beb93cSSam Leffler const struct wpabuf *req, struct wpabuf **resp, 46839beb93cSSam Leffler int force_failure) 46939beb93cSSam Leffler { 47039beb93cSSam Leffler size_t left, tlv_len; 47139beb93cSSam Leffler const u8 *pos; 47239beb93cSSam Leffler const u8 *result_tlv = NULL, *crypto_tlv = NULL; 47339beb93cSSam Leffler size_t result_tlv_len = 0, crypto_tlv_len = 0; 47439beb93cSSam Leffler int tlv_type, mandatory; 47539beb93cSSam Leffler 47639beb93cSSam Leffler /* Parse TLVs */ 47739beb93cSSam Leffler pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left); 47839beb93cSSam Leffler if (pos == NULL) 47939beb93cSSam Leffler return -1; 48039beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); 48139beb93cSSam Leffler while (left >= 4) { 48239beb93cSSam Leffler mandatory = !!(pos[0] & 0x80); 48339beb93cSSam Leffler tlv_type = WPA_GET_BE16(pos) & 0x3fff; 48439beb93cSSam Leffler pos += 2; 48539beb93cSSam Leffler tlv_len = WPA_GET_BE16(pos); 48639beb93cSSam Leffler pos += 2; 48739beb93cSSam Leffler left -= 4; 48839beb93cSSam Leffler if (tlv_len > left) { 48939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " 49039beb93cSSam Leffler "(tlv_len=%lu left=%lu)", 49139beb93cSSam Leffler (unsigned long) tlv_len, 49239beb93cSSam Leffler (unsigned long) left); 49339beb93cSSam Leffler return -1; 49439beb93cSSam Leffler } 49539beb93cSSam Leffler switch (tlv_type) { 49639beb93cSSam Leffler case EAP_TLV_RESULT_TLV: 49739beb93cSSam Leffler result_tlv = pos; 49839beb93cSSam Leffler result_tlv_len = tlv_len; 49939beb93cSSam Leffler break; 50039beb93cSSam Leffler case EAP_TLV_CRYPTO_BINDING_TLV: 50139beb93cSSam Leffler crypto_tlv = pos; 50239beb93cSSam Leffler crypto_tlv_len = tlv_len; 50339beb93cSSam Leffler break; 50439beb93cSSam Leffler default: 50539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " 50639beb93cSSam Leffler "%d%s", tlv_type, 50739beb93cSSam Leffler mandatory ? " (mandatory)" : ""); 50839beb93cSSam Leffler if (mandatory) { 50939beb93cSSam Leffler /* NAK TLV and ignore all TLVs in this packet. 51039beb93cSSam Leffler */ 51139beb93cSSam Leffler *resp = eap_tlv_build_nak(eap_get_id(req), 51239beb93cSSam Leffler tlv_type); 51339beb93cSSam Leffler return *resp == NULL ? -1 : 0; 51439beb93cSSam Leffler } 51539beb93cSSam Leffler /* Ignore this TLV, but process other TLVs */ 51639beb93cSSam Leffler break; 51739beb93cSSam Leffler } 51839beb93cSSam Leffler 51939beb93cSSam Leffler pos += tlv_len; 52039beb93cSSam Leffler left -= tlv_len; 52139beb93cSSam Leffler } 52239beb93cSSam Leffler if (left) { 52339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " 52439beb93cSSam Leffler "Request (left=%lu)", (unsigned long) left); 52539beb93cSSam Leffler return -1; 52639beb93cSSam Leffler } 52739beb93cSSam Leffler 52839beb93cSSam Leffler /* Process supported TLVs */ 52939beb93cSSam Leffler if (crypto_tlv && data->crypto_binding != NO_BINDING) { 53039beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", 53139beb93cSSam Leffler crypto_tlv, crypto_tlv_len); 53239beb93cSSam Leffler if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, 53339beb93cSSam Leffler crypto_tlv_len + 4) < 0) { 53439beb93cSSam Leffler if (result_tlv == NULL) 53539beb93cSSam Leffler return -1; 53639beb93cSSam Leffler force_failure = 1; 53739beb93cSSam Leffler crypto_tlv = NULL; /* do not include Cryptobinding TLV 53839beb93cSSam Leffler * in response, if the received 53939beb93cSSam Leffler * cryptobinding was invalid. */ 54039beb93cSSam Leffler } 54139beb93cSSam Leffler } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) { 54239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); 54339beb93cSSam Leffler return -1; 54439beb93cSSam Leffler } 54539beb93cSSam Leffler 54639beb93cSSam Leffler if (result_tlv) { 54739beb93cSSam Leffler int status, resp_status; 54839beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", 54939beb93cSSam Leffler result_tlv, result_tlv_len); 55039beb93cSSam Leffler if (result_tlv_len < 2) { 55139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " 55239beb93cSSam Leffler "(len=%lu)", 55339beb93cSSam Leffler (unsigned long) result_tlv_len); 55439beb93cSSam Leffler return -1; 55539beb93cSSam Leffler } 55639beb93cSSam Leffler status = WPA_GET_BE16(result_tlv); 55739beb93cSSam Leffler if (status == EAP_TLV_RESULT_SUCCESS) { 55839beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " 55939beb93cSSam Leffler "- EAP-TLV/Phase2 Completed"); 56039beb93cSSam Leffler if (force_failure) { 56139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure" 56239beb93cSSam Leffler " - force failed Phase 2"); 56339beb93cSSam Leffler resp_status = EAP_TLV_RESULT_FAILURE; 56439beb93cSSam Leffler ret->decision = DECISION_FAIL; 56539beb93cSSam Leffler } else { 56639beb93cSSam Leffler resp_status = EAP_TLV_RESULT_SUCCESS; 56739beb93cSSam Leffler ret->decision = DECISION_UNCOND_SUCC; 56839beb93cSSam Leffler } 56939beb93cSSam Leffler } else if (status == EAP_TLV_RESULT_FAILURE) { 57039beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); 57139beb93cSSam Leffler resp_status = EAP_TLV_RESULT_FAILURE; 57239beb93cSSam Leffler ret->decision = DECISION_FAIL; 57339beb93cSSam Leffler } else { 57439beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " 57539beb93cSSam Leffler "Status %d", status); 57639beb93cSSam Leffler resp_status = EAP_TLV_RESULT_FAILURE; 57739beb93cSSam Leffler ret->decision = DECISION_FAIL; 57839beb93cSSam Leffler } 57939beb93cSSam Leffler ret->methodState = METHOD_DONE; 58039beb93cSSam Leffler 58139beb93cSSam Leffler *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL, 58239beb93cSSam Leffler eap_get_id(req), resp_status); 58339beb93cSSam Leffler } 58439beb93cSSam Leffler 58539beb93cSSam Leffler return 0; 58639beb93cSSam Leffler } 58739beb93cSSam Leffler 58839beb93cSSam Leffler 58939beb93cSSam Leffler static int eap_peap_phase2_request(struct eap_sm *sm, 59039beb93cSSam Leffler struct eap_peap_data *data, 59139beb93cSSam Leffler struct eap_method_ret *ret, 59239beb93cSSam Leffler struct wpabuf *req, 59339beb93cSSam Leffler struct wpabuf **resp) 59439beb93cSSam Leffler { 59539beb93cSSam Leffler struct eap_hdr *hdr = wpabuf_mhead(req); 59639beb93cSSam Leffler size_t len = be_to_host16(hdr->length); 59739beb93cSSam Leffler u8 *pos; 59839beb93cSSam Leffler struct eap_method_ret iret; 59939beb93cSSam Leffler struct eap_peer_config *config = eap_get_config(sm); 60039beb93cSSam Leffler 60139beb93cSSam Leffler if (len <= sizeof(struct eap_hdr)) { 60239beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: too short " 60339beb93cSSam Leffler "Phase 2 request (len=%lu)", (unsigned long) len); 60439beb93cSSam Leffler return -1; 60539beb93cSSam Leffler } 60639beb93cSSam Leffler pos = (u8 *) (hdr + 1); 60739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); 60839beb93cSSam Leffler switch (*pos) { 60939beb93cSSam Leffler case EAP_TYPE_IDENTITY: 61039beb93cSSam Leffler *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); 61139beb93cSSam Leffler break; 61239beb93cSSam Leffler case EAP_TYPE_TLV: 61339beb93cSSam Leffler os_memset(&iret, 0, sizeof(iret)); 61439beb93cSSam Leffler if (eap_tlv_process(sm, data, &iret, req, resp, 61539beb93cSSam Leffler data->phase2_eap_started && 61639beb93cSSam Leffler !data->phase2_eap_success)) { 61739beb93cSSam Leffler ret->methodState = METHOD_DONE; 61839beb93cSSam Leffler ret->decision = DECISION_FAIL; 61939beb93cSSam Leffler return -1; 62039beb93cSSam Leffler } 62139beb93cSSam Leffler if (iret.methodState == METHOD_DONE || 62239beb93cSSam Leffler iret.methodState == METHOD_MAY_CONT) { 62339beb93cSSam Leffler ret->methodState = iret.methodState; 62439beb93cSSam Leffler ret->decision = iret.decision; 62539beb93cSSam Leffler data->phase2_success = 1; 62639beb93cSSam Leffler } 62739beb93cSSam Leffler break; 62839beb93cSSam Leffler case EAP_TYPE_EXPANDED: 62939beb93cSSam Leffler #ifdef EAP_TNC 63039beb93cSSam Leffler if (data->soh) { 63139beb93cSSam Leffler const u8 *epos; 63239beb93cSSam Leffler size_t eleft; 63339beb93cSSam Leffler 63439beb93cSSam Leffler epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, 63539beb93cSSam Leffler req, &eleft); 63639beb93cSSam Leffler if (epos) { 63739beb93cSSam Leffler struct wpabuf *buf; 63839beb93cSSam Leffler wpa_printf(MSG_DEBUG, 63939beb93cSSam Leffler "EAP-PEAP: SoH EAP Extensions"); 64039beb93cSSam Leffler buf = tncc_process_soh_request(data->soh, 64139beb93cSSam Leffler epos, eleft); 64239beb93cSSam Leffler if (buf) { 64339beb93cSSam Leffler *resp = eap_msg_alloc( 64439beb93cSSam Leffler EAP_VENDOR_MICROSOFT, 0x21, 64539beb93cSSam Leffler wpabuf_len(buf), 64639beb93cSSam Leffler EAP_CODE_RESPONSE, 64739beb93cSSam Leffler hdr->identifier); 64839beb93cSSam Leffler if (*resp == NULL) { 64939beb93cSSam Leffler ret->methodState = METHOD_DONE; 65039beb93cSSam Leffler ret->decision = DECISION_FAIL; 65139beb93cSSam Leffler return -1; 65239beb93cSSam Leffler } 65339beb93cSSam Leffler wpabuf_put_buf(*resp, buf); 65439beb93cSSam Leffler wpabuf_free(buf); 65539beb93cSSam Leffler break; 65639beb93cSSam Leffler } 65739beb93cSSam Leffler } 65839beb93cSSam Leffler } 65939beb93cSSam Leffler #endif /* EAP_TNC */ 66039beb93cSSam Leffler /* fall through */ 66139beb93cSSam Leffler default: 66239beb93cSSam Leffler if (data->phase2_type.vendor == EAP_VENDOR_IETF && 66339beb93cSSam Leffler data->phase2_type.method == EAP_TYPE_NONE) { 66439beb93cSSam Leffler size_t i; 66539beb93cSSam Leffler for (i = 0; i < data->num_phase2_types; i++) { 66639beb93cSSam Leffler if (data->phase2_types[i].vendor != 66739beb93cSSam Leffler EAP_VENDOR_IETF || 66839beb93cSSam Leffler data->phase2_types[i].method != *pos) 66939beb93cSSam Leffler continue; 67039beb93cSSam Leffler 67139beb93cSSam Leffler data->phase2_type.vendor = 67239beb93cSSam Leffler data->phase2_types[i].vendor; 67339beb93cSSam Leffler data->phase2_type.method = 67439beb93cSSam Leffler data->phase2_types[i].method; 67539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " 67639beb93cSSam Leffler "Phase 2 EAP vendor %d method %d", 67739beb93cSSam Leffler data->phase2_type.vendor, 67839beb93cSSam Leffler data->phase2_type.method); 67939beb93cSSam Leffler break; 68039beb93cSSam Leffler } 68139beb93cSSam Leffler } 68239beb93cSSam Leffler if (*pos != data->phase2_type.method || 68339beb93cSSam Leffler *pos == EAP_TYPE_NONE) { 68439beb93cSSam Leffler if (eap_peer_tls_phase2_nak(data->phase2_types, 68539beb93cSSam Leffler data->num_phase2_types, 68639beb93cSSam Leffler hdr, resp)) 68739beb93cSSam Leffler return -1; 68839beb93cSSam Leffler return 0; 68939beb93cSSam Leffler } 69039beb93cSSam Leffler 69139beb93cSSam Leffler if (data->phase2_priv == NULL) { 69239beb93cSSam Leffler data->phase2_method = eap_peer_get_eap_method( 69339beb93cSSam Leffler data->phase2_type.vendor, 69439beb93cSSam Leffler data->phase2_type.method); 69539beb93cSSam Leffler if (data->phase2_method) { 69639beb93cSSam Leffler sm->init_phase2 = 1; 69739beb93cSSam Leffler data->phase2_priv = 69839beb93cSSam Leffler data->phase2_method->init(sm); 69939beb93cSSam Leffler sm->init_phase2 = 0; 70039beb93cSSam Leffler } 70139beb93cSSam Leffler } 70239beb93cSSam Leffler if (data->phase2_priv == NULL || data->phase2_method == NULL) { 70339beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " 70439beb93cSSam Leffler "Phase 2 EAP method %d", *pos); 70539beb93cSSam Leffler ret->methodState = METHOD_DONE; 70639beb93cSSam Leffler ret->decision = DECISION_FAIL; 70739beb93cSSam Leffler return -1; 70839beb93cSSam Leffler } 70939beb93cSSam Leffler data->phase2_eap_started = 1; 71039beb93cSSam Leffler os_memset(&iret, 0, sizeof(iret)); 71139beb93cSSam Leffler *resp = data->phase2_method->process(sm, data->phase2_priv, 71239beb93cSSam Leffler &iret, req); 71339beb93cSSam Leffler if ((iret.methodState == METHOD_DONE || 71439beb93cSSam Leffler iret.methodState == METHOD_MAY_CONT) && 71539beb93cSSam Leffler (iret.decision == DECISION_UNCOND_SUCC || 71639beb93cSSam Leffler iret.decision == DECISION_COND_SUCC)) { 71739beb93cSSam Leffler data->phase2_eap_success = 1; 71839beb93cSSam Leffler data->phase2_success = 1; 71939beb93cSSam Leffler } 72039beb93cSSam Leffler break; 72139beb93cSSam Leffler } 72239beb93cSSam Leffler 72339beb93cSSam Leffler if (*resp == NULL && 72439beb93cSSam Leffler (config->pending_req_identity || config->pending_req_password || 72539beb93cSSam Leffler config->pending_req_otp || config->pending_req_new_password)) { 72639beb93cSSam Leffler wpabuf_free(data->pending_phase2_req); 72739beb93cSSam Leffler data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); 72839beb93cSSam Leffler } 72939beb93cSSam Leffler 73039beb93cSSam Leffler return 0; 73139beb93cSSam Leffler } 73239beb93cSSam Leffler 73339beb93cSSam Leffler 73439beb93cSSam Leffler static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, 73539beb93cSSam Leffler struct eap_method_ret *ret, 73639beb93cSSam Leffler const struct eap_hdr *req, 73739beb93cSSam Leffler const struct wpabuf *in_data, 73839beb93cSSam Leffler struct wpabuf **out_data) 73939beb93cSSam Leffler { 74039beb93cSSam Leffler struct wpabuf *in_decrypted = NULL; 74139beb93cSSam Leffler int res, skip_change = 0; 74239beb93cSSam Leffler struct eap_hdr *hdr, *rhdr; 74339beb93cSSam Leffler struct wpabuf *resp = NULL; 74439beb93cSSam Leffler size_t len; 74539beb93cSSam Leffler 74639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" 74739beb93cSSam Leffler " Phase 2", (unsigned long) wpabuf_len(in_data)); 74839beb93cSSam Leffler 74939beb93cSSam Leffler if (data->pending_phase2_req) { 75039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " 75139beb93cSSam Leffler "skip decryption and use old data"); 75239beb93cSSam Leffler /* Clear TLS reassembly state. */ 75339beb93cSSam Leffler eap_peer_tls_reset_input(&data->ssl); 75439beb93cSSam Leffler in_decrypted = data->pending_phase2_req; 75539beb93cSSam Leffler data->pending_phase2_req = NULL; 75639beb93cSSam Leffler skip_change = 1; 75739beb93cSSam Leffler goto continue_req; 75839beb93cSSam Leffler } 75939beb93cSSam Leffler 76039beb93cSSam Leffler if (wpabuf_len(in_data) == 0 && sm->workaround && 76139beb93cSSam Leffler data->phase2_success) { 76239beb93cSSam Leffler /* 76339beb93cSSam Leffler * Cisco ACS seems to be using TLS ACK to terminate 76439beb93cSSam Leffler * EAP-PEAPv0/GTC. Try to reply with TLS ACK. 76539beb93cSSam Leffler */ 76639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " 76739beb93cSSam Leffler "expected data - acknowledge with TLS ACK since " 76839beb93cSSam Leffler "Phase 2 has been completed"); 76939beb93cSSam Leffler ret->decision = DECISION_COND_SUCC; 77039beb93cSSam Leffler ret->methodState = METHOD_DONE; 77139beb93cSSam Leffler return 1; 77239beb93cSSam Leffler } else if (wpabuf_len(in_data) == 0) { 77339beb93cSSam Leffler /* Received TLS ACK - requesting more fragments */ 77439beb93cSSam Leffler return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, 77539beb93cSSam Leffler data->peap_version, 77639beb93cSSam Leffler req->identifier, NULL, out_data); 77739beb93cSSam Leffler } 77839beb93cSSam Leffler 77939beb93cSSam Leffler res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); 78039beb93cSSam Leffler if (res) 78139beb93cSSam Leffler return res; 78239beb93cSSam Leffler 78339beb93cSSam Leffler continue_req: 78439beb93cSSam Leffler wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", 78539beb93cSSam Leffler in_decrypted); 78639beb93cSSam Leffler 78739beb93cSSam Leffler hdr = wpabuf_mhead(in_decrypted); 78839beb93cSSam Leffler if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST && 78939beb93cSSam Leffler be_to_host16(hdr->length) == 5 && 79039beb93cSSam Leffler eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) { 79139beb93cSSam Leffler /* At least FreeRADIUS seems to send full EAP header with 79239beb93cSSam Leffler * EAP Request Identity */ 79339beb93cSSam Leffler skip_change = 1; 79439beb93cSSam Leffler } 79539beb93cSSam Leffler if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST && 79639beb93cSSam Leffler eap_get_type(in_decrypted) == EAP_TYPE_TLV) { 79739beb93cSSam Leffler skip_change = 1; 79839beb93cSSam Leffler } 79939beb93cSSam Leffler 80039beb93cSSam Leffler if (data->peap_version == 0 && !skip_change) { 80139beb93cSSam Leffler struct eap_hdr *nhdr; 80239beb93cSSam Leffler struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + 80339beb93cSSam Leffler wpabuf_len(in_decrypted)); 80439beb93cSSam Leffler if (nmsg == NULL) { 80539beb93cSSam Leffler wpabuf_free(in_decrypted); 80639beb93cSSam Leffler return 0; 80739beb93cSSam Leffler } 80839beb93cSSam Leffler nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); 80939beb93cSSam Leffler wpabuf_put_buf(nmsg, in_decrypted); 81039beb93cSSam Leffler nhdr->code = req->code; 81139beb93cSSam Leffler nhdr->identifier = req->identifier; 81239beb93cSSam Leffler nhdr->length = host_to_be16(sizeof(struct eap_hdr) + 81339beb93cSSam Leffler wpabuf_len(in_decrypted)); 81439beb93cSSam Leffler 81539beb93cSSam Leffler wpabuf_free(in_decrypted); 81639beb93cSSam Leffler in_decrypted = nmsg; 81739beb93cSSam Leffler } 81839beb93cSSam Leffler 81939beb93cSSam Leffler hdr = wpabuf_mhead(in_decrypted); 82039beb93cSSam Leffler if (wpabuf_len(in_decrypted) < sizeof(*hdr)) { 82139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " 82239beb93cSSam Leffler "EAP frame (len=%lu)", 82339beb93cSSam Leffler (unsigned long) wpabuf_len(in_decrypted)); 82439beb93cSSam Leffler wpabuf_free(in_decrypted); 82539beb93cSSam Leffler return 0; 82639beb93cSSam Leffler } 82739beb93cSSam Leffler len = be_to_host16(hdr->length); 82839beb93cSSam Leffler if (len > wpabuf_len(in_decrypted)) { 82939beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " 83039beb93cSSam Leffler "Phase 2 EAP frame (len=%lu hdr->length=%lu)", 83139beb93cSSam Leffler (unsigned long) wpabuf_len(in_decrypted), 83239beb93cSSam Leffler (unsigned long) len); 83339beb93cSSam Leffler wpabuf_free(in_decrypted); 83439beb93cSSam Leffler return 0; 83539beb93cSSam Leffler } 83639beb93cSSam Leffler if (len < wpabuf_len(in_decrypted)) { 83739beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " 83839beb93cSSam Leffler "shorter length than full decrypted data " 83939beb93cSSam Leffler "(%lu < %lu)", 84039beb93cSSam Leffler (unsigned long) len, 84139beb93cSSam Leffler (unsigned long) wpabuf_len(in_decrypted)); 84239beb93cSSam Leffler } 84339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " 84439beb93cSSam Leffler "identifier=%d length=%lu", hdr->code, hdr->identifier, 84539beb93cSSam Leffler (unsigned long) len); 84639beb93cSSam Leffler switch (hdr->code) { 84739beb93cSSam Leffler case EAP_CODE_REQUEST: 84839beb93cSSam Leffler if (eap_peap_phase2_request(sm, data, ret, in_decrypted, 84939beb93cSSam Leffler &resp)) { 85039beb93cSSam Leffler wpabuf_free(in_decrypted); 85139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " 85239beb93cSSam Leffler "processing failed"); 85339beb93cSSam Leffler return 0; 85439beb93cSSam Leffler } 85539beb93cSSam Leffler break; 85639beb93cSSam Leffler case EAP_CODE_SUCCESS: 85739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); 85839beb93cSSam Leffler if (data->peap_version == 1) { 85939beb93cSSam Leffler /* EAP-Success within TLS tunnel is used to indicate 86039beb93cSSam Leffler * shutdown of the TLS channel. The authentication has 86139beb93cSSam Leffler * been completed. */ 86239beb93cSSam Leffler if (data->phase2_eap_started && 86339beb93cSSam Leffler !data->phase2_eap_success) { 86439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " 86539beb93cSSam Leffler "Success used to indicate success, " 86639beb93cSSam Leffler "but Phase 2 EAP was not yet " 86739beb93cSSam Leffler "completed successfully"); 86839beb93cSSam Leffler ret->methodState = METHOD_DONE; 86939beb93cSSam Leffler ret->decision = DECISION_FAIL; 87039beb93cSSam Leffler wpabuf_free(in_decrypted); 87139beb93cSSam Leffler return 0; 87239beb93cSSam Leffler } 87339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " 87439beb93cSSam Leffler "EAP-Success within TLS tunnel - " 87539beb93cSSam Leffler "authentication completed"); 87639beb93cSSam Leffler ret->decision = DECISION_UNCOND_SUCC; 87739beb93cSSam Leffler ret->methodState = METHOD_DONE; 87839beb93cSSam Leffler data->phase2_success = 1; 87939beb93cSSam Leffler if (data->peap_outer_success == 2) { 88039beb93cSSam Leffler wpabuf_free(in_decrypted); 88139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " 88239beb93cSSam Leffler "to finish authentication"); 88339beb93cSSam Leffler return 1; 88439beb93cSSam Leffler } else if (data->peap_outer_success == 1) { 88539beb93cSSam Leffler /* Reply with EAP-Success within the TLS 88639beb93cSSam Leffler * channel to complete the authentication. */ 88739beb93cSSam Leffler resp = wpabuf_alloc(sizeof(struct eap_hdr)); 88839beb93cSSam Leffler if (resp) { 88939beb93cSSam Leffler rhdr = wpabuf_put(resp, sizeof(*rhdr)); 89039beb93cSSam Leffler rhdr->code = EAP_CODE_SUCCESS; 89139beb93cSSam Leffler rhdr->identifier = hdr->identifier; 89239beb93cSSam Leffler rhdr->length = 89339beb93cSSam Leffler host_to_be16(sizeof(*rhdr)); 89439beb93cSSam Leffler } 89539beb93cSSam Leffler } else { 89639beb93cSSam Leffler /* No EAP-Success expected for Phase 1 (outer, 89739beb93cSSam Leffler * unencrypted auth), so force EAP state 89839beb93cSSam Leffler * machine to SUCCESS state. */ 89939beb93cSSam Leffler sm->peap_done = TRUE; 90039beb93cSSam Leffler } 90139beb93cSSam Leffler } else { 90239beb93cSSam Leffler /* FIX: ? */ 90339beb93cSSam Leffler } 90439beb93cSSam Leffler break; 90539beb93cSSam Leffler case EAP_CODE_FAILURE: 90639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); 90739beb93cSSam Leffler ret->decision = DECISION_FAIL; 90839beb93cSSam Leffler ret->methodState = METHOD_MAY_CONT; 90939beb93cSSam Leffler ret->allowNotifications = FALSE; 91039beb93cSSam Leffler /* Reply with EAP-Failure within the TLS channel to complete 91139beb93cSSam Leffler * failure reporting. */ 91239beb93cSSam Leffler resp = wpabuf_alloc(sizeof(struct eap_hdr)); 91339beb93cSSam Leffler if (resp) { 91439beb93cSSam Leffler rhdr = wpabuf_put(resp, sizeof(*rhdr)); 91539beb93cSSam Leffler rhdr->code = EAP_CODE_FAILURE; 91639beb93cSSam Leffler rhdr->identifier = hdr->identifier; 91739beb93cSSam Leffler rhdr->length = host_to_be16(sizeof(*rhdr)); 91839beb93cSSam Leffler } 91939beb93cSSam Leffler break; 92039beb93cSSam Leffler default: 92139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " 92239beb93cSSam Leffler "Phase 2 EAP header", hdr->code); 92339beb93cSSam Leffler break; 92439beb93cSSam Leffler } 92539beb93cSSam Leffler 92639beb93cSSam Leffler wpabuf_free(in_decrypted); 92739beb93cSSam Leffler 92839beb93cSSam Leffler if (resp) { 92939beb93cSSam Leffler int skip_change2 = 0; 93039beb93cSSam Leffler struct wpabuf *rmsg, buf; 93139beb93cSSam Leffler 93239beb93cSSam Leffler wpa_hexdump_buf_key(MSG_DEBUG, 93339beb93cSSam Leffler "EAP-PEAP: Encrypting Phase 2 data", resp); 93439beb93cSSam Leffler /* PEAP version changes */ 93539beb93cSSam Leffler if (wpabuf_len(resp) >= 5 && 93639beb93cSSam Leffler wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && 93739beb93cSSam Leffler eap_get_type(resp) == EAP_TYPE_TLV) 93839beb93cSSam Leffler skip_change2 = 1; 93939beb93cSSam Leffler rmsg = resp; 94039beb93cSSam Leffler if (data->peap_version == 0 && !skip_change2) { 94139beb93cSSam Leffler wpabuf_set(&buf, wpabuf_head_u8(resp) + 94239beb93cSSam Leffler sizeof(struct eap_hdr), 94339beb93cSSam Leffler wpabuf_len(resp) - sizeof(struct eap_hdr)); 94439beb93cSSam Leffler rmsg = &buf; 94539beb93cSSam Leffler } 94639beb93cSSam Leffler 94739beb93cSSam Leffler if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, 94839beb93cSSam Leffler data->peap_version, req->identifier, 94939beb93cSSam Leffler rmsg, out_data)) { 95039beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " 95139beb93cSSam Leffler "a Phase 2 frame"); 95239beb93cSSam Leffler } 95339beb93cSSam Leffler wpabuf_free(resp); 95439beb93cSSam Leffler } 95539beb93cSSam Leffler 95639beb93cSSam Leffler return 0; 95739beb93cSSam Leffler } 95839beb93cSSam Leffler 95939beb93cSSam Leffler 96039beb93cSSam Leffler static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, 96139beb93cSSam Leffler struct eap_method_ret *ret, 96239beb93cSSam Leffler const struct wpabuf *reqData) 96339beb93cSSam Leffler { 96439beb93cSSam Leffler const struct eap_hdr *req; 96539beb93cSSam Leffler size_t left; 96639beb93cSSam Leffler int res; 96739beb93cSSam Leffler u8 flags, id; 96839beb93cSSam Leffler struct wpabuf *resp; 96939beb93cSSam Leffler const u8 *pos; 97039beb93cSSam Leffler struct eap_peap_data *data = priv; 97139beb93cSSam Leffler 97239beb93cSSam Leffler pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, 97339beb93cSSam Leffler reqData, &left, &flags); 97439beb93cSSam Leffler if (pos == NULL) 97539beb93cSSam Leffler return NULL; 97639beb93cSSam Leffler req = wpabuf_head(reqData); 97739beb93cSSam Leffler id = req->identifier; 97839beb93cSSam Leffler 97939beb93cSSam Leffler if (flags & EAP_TLS_FLAGS_START) { 98039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " 981e28a4053SRui Paulo "ver=%d)", flags & EAP_TLS_VERSION_MASK, 98239beb93cSSam Leffler data->peap_version); 983e28a4053SRui Paulo if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version) 984e28a4053SRui Paulo data->peap_version = flags & EAP_TLS_VERSION_MASK; 98539beb93cSSam Leffler if (data->force_peap_version >= 0 && 98639beb93cSSam Leffler data->force_peap_version != data->peap_version) { 98739beb93cSSam Leffler wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " 98839beb93cSSam Leffler "forced PEAP version %d", 98939beb93cSSam Leffler data->force_peap_version); 99039beb93cSSam Leffler ret->methodState = METHOD_DONE; 99139beb93cSSam Leffler ret->decision = DECISION_FAIL; 99239beb93cSSam Leffler ret->allowNotifications = FALSE; 99339beb93cSSam Leffler return NULL; 99439beb93cSSam Leffler } 99539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", 99639beb93cSSam Leffler data->peap_version); 99739beb93cSSam Leffler left = 0; /* make sure that this frame is empty, even though it 99839beb93cSSam Leffler * should always be, anyway */ 99939beb93cSSam Leffler } 100039beb93cSSam Leffler 100139beb93cSSam Leffler resp = NULL; 100239beb93cSSam Leffler if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 100339beb93cSSam Leffler !data->resuming) { 100439beb93cSSam Leffler struct wpabuf msg; 100539beb93cSSam Leffler wpabuf_set(&msg, pos, left); 100639beb93cSSam Leffler res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); 100739beb93cSSam Leffler } else { 100839beb93cSSam Leffler res = eap_peer_tls_process_helper(sm, &data->ssl, 100939beb93cSSam Leffler EAP_TYPE_PEAP, 101039beb93cSSam Leffler data->peap_version, id, pos, 101139beb93cSSam Leffler left, &resp); 101239beb93cSSam Leffler 101339beb93cSSam Leffler if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { 101439beb93cSSam Leffler char *label; 101539beb93cSSam Leffler wpa_printf(MSG_DEBUG, 101639beb93cSSam Leffler "EAP-PEAP: TLS done, proceed to Phase 2"); 1017*5b9c547cSRui Paulo eap_peap_free_key(data); 101839beb93cSSam Leffler /* draft-josefsson-ppext-eap-tls-eap-05.txt 101939beb93cSSam Leffler * specifies that PEAPv1 would use "client PEAP 102039beb93cSSam Leffler * encryption" as the label. However, most existing 102139beb93cSSam Leffler * PEAPv1 implementations seem to be using the old 102239beb93cSSam Leffler * label, "client EAP encryption", instead. Use the old 102339beb93cSSam Leffler * label by default, but allow it to be configured with 102439beb93cSSam Leffler * phase1 parameter peaplabel=1. */ 1025*5b9c547cSRui Paulo if (data->force_new_label) 102639beb93cSSam Leffler label = "client PEAP encryption"; 102739beb93cSSam Leffler else 102839beb93cSSam Leffler label = "client EAP encryption"; 102939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " 103039beb93cSSam Leffler "key derivation", label); 103139beb93cSSam Leffler data->key_data = 103239beb93cSSam Leffler eap_peer_tls_derive_key(sm, &data->ssl, label, 103339beb93cSSam Leffler EAP_TLS_KEY_LEN); 103439beb93cSSam Leffler if (data->key_data) { 103539beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, 103639beb93cSSam Leffler "EAP-PEAP: Derived key", 103739beb93cSSam Leffler data->key_data, 103839beb93cSSam Leffler EAP_TLS_KEY_LEN); 103939beb93cSSam Leffler } else { 104039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " 104139beb93cSSam Leffler "derive key"); 104239beb93cSSam Leffler } 104339beb93cSSam Leffler 1044*5b9c547cSRui Paulo os_free(data->session_id); 1045*5b9c547cSRui Paulo data->session_id = 1046*5b9c547cSRui Paulo eap_peer_tls_derive_session_id(sm, &data->ssl, 1047*5b9c547cSRui Paulo EAP_TYPE_PEAP, 1048*5b9c547cSRui Paulo &data->id_len); 1049*5b9c547cSRui Paulo if (data->session_id) { 1050*5b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, 1051*5b9c547cSRui Paulo "EAP-PEAP: Derived Session-Id", 1052*5b9c547cSRui Paulo data->session_id, data->id_len); 1053*5b9c547cSRui Paulo } else { 1054*5b9c547cSRui Paulo wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to " 1055*5b9c547cSRui Paulo "derive Session-Id"); 1056*5b9c547cSRui Paulo } 1057*5b9c547cSRui Paulo 105839beb93cSSam Leffler if (sm->workaround && data->resuming) { 105939beb93cSSam Leffler /* 106039beb93cSSam Leffler * At least few RADIUS servers (Aegis v1.1.6; 106139beb93cSSam Leffler * but not v1.1.4; and Cisco ACS) seem to be 106239beb93cSSam Leffler * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco 106339beb93cSSam Leffler * ACS) session resumption with outer 106439beb93cSSam Leffler * EAP-Success. This does not seem to follow 106539beb93cSSam Leffler * draft-josefsson-pppext-eap-tls-eap-05.txt 106639beb93cSSam Leffler * section 4.2, so only allow this if EAP 106739beb93cSSam Leffler * workarounds are enabled. 106839beb93cSSam Leffler */ 106939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " 107039beb93cSSam Leffler "allow outer EAP-Success to " 107139beb93cSSam Leffler "terminate PEAP resumption"); 107239beb93cSSam Leffler ret->decision = DECISION_COND_SUCC; 107339beb93cSSam Leffler data->phase2_success = 1; 107439beb93cSSam Leffler } 107539beb93cSSam Leffler 107639beb93cSSam Leffler data->resuming = 0; 107739beb93cSSam Leffler } 107839beb93cSSam Leffler 107939beb93cSSam Leffler if (res == 2) { 108039beb93cSSam Leffler struct wpabuf msg; 108139beb93cSSam Leffler /* 108239beb93cSSam Leffler * Application data included in the handshake message. 108339beb93cSSam Leffler */ 108439beb93cSSam Leffler wpabuf_free(data->pending_phase2_req); 108539beb93cSSam Leffler data->pending_phase2_req = resp; 108639beb93cSSam Leffler resp = NULL; 108739beb93cSSam Leffler wpabuf_set(&msg, pos, left); 108839beb93cSSam Leffler res = eap_peap_decrypt(sm, data, ret, req, &msg, 108939beb93cSSam Leffler &resp); 109039beb93cSSam Leffler } 109139beb93cSSam Leffler } 109239beb93cSSam Leffler 109339beb93cSSam Leffler if (ret->methodState == METHOD_DONE) { 109439beb93cSSam Leffler ret->allowNotifications = FALSE; 109539beb93cSSam Leffler } 109639beb93cSSam Leffler 109739beb93cSSam Leffler if (res == 1) { 109839beb93cSSam Leffler wpabuf_free(resp); 109939beb93cSSam Leffler return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, 110039beb93cSSam Leffler data->peap_version); 110139beb93cSSam Leffler } 110239beb93cSSam Leffler 110339beb93cSSam Leffler return resp; 110439beb93cSSam Leffler } 110539beb93cSSam Leffler 110639beb93cSSam Leffler 110739beb93cSSam Leffler static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) 110839beb93cSSam Leffler { 110939beb93cSSam Leffler struct eap_peap_data *data = priv; 111039beb93cSSam Leffler return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && 111139beb93cSSam Leffler data->phase2_success; 111239beb93cSSam Leffler } 111339beb93cSSam Leffler 111439beb93cSSam Leffler 111539beb93cSSam Leffler static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) 111639beb93cSSam Leffler { 111739beb93cSSam Leffler struct eap_peap_data *data = priv; 111839beb93cSSam Leffler wpabuf_free(data->pending_phase2_req); 111939beb93cSSam Leffler data->pending_phase2_req = NULL; 112039beb93cSSam Leffler data->crypto_binding_used = 0; 112139beb93cSSam Leffler } 112239beb93cSSam Leffler 112339beb93cSSam Leffler 112439beb93cSSam Leffler static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) 112539beb93cSSam Leffler { 112639beb93cSSam Leffler struct eap_peap_data *data = priv; 1127*5b9c547cSRui Paulo eap_peap_free_key(data); 1128*5b9c547cSRui Paulo os_free(data->session_id); 1129*5b9c547cSRui Paulo data->session_id = NULL; 113039beb93cSSam Leffler if (eap_peer_tls_reauth_init(sm, &data->ssl)) { 113139beb93cSSam Leffler os_free(data); 113239beb93cSSam Leffler return NULL; 113339beb93cSSam Leffler } 113439beb93cSSam Leffler if (data->phase2_priv && data->phase2_method && 113539beb93cSSam Leffler data->phase2_method->init_for_reauth) 113639beb93cSSam Leffler data->phase2_method->init_for_reauth(sm, data->phase2_priv); 113739beb93cSSam Leffler data->phase2_success = 0; 113839beb93cSSam Leffler data->phase2_eap_success = 0; 113939beb93cSSam Leffler data->phase2_eap_started = 0; 114039beb93cSSam Leffler data->resuming = 1; 114139beb93cSSam Leffler data->reauth = 1; 114239beb93cSSam Leffler sm->peap_done = FALSE; 114339beb93cSSam Leffler return priv; 114439beb93cSSam Leffler } 114539beb93cSSam Leffler 114639beb93cSSam Leffler 114739beb93cSSam Leffler static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, 114839beb93cSSam Leffler size_t buflen, int verbose) 114939beb93cSSam Leffler { 115039beb93cSSam Leffler struct eap_peap_data *data = priv; 115139beb93cSSam Leffler int len, ret; 115239beb93cSSam Leffler 115339beb93cSSam Leffler len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); 115439beb93cSSam Leffler if (data->phase2_method) { 115539beb93cSSam Leffler ret = os_snprintf(buf + len, buflen - len, 115639beb93cSSam Leffler "EAP-PEAPv%d Phase2 method=%s\n", 115739beb93cSSam Leffler data->peap_version, 115839beb93cSSam Leffler data->phase2_method->name); 1159*5b9c547cSRui Paulo if (os_snprintf_error(buflen - len, ret)) 116039beb93cSSam Leffler return len; 116139beb93cSSam Leffler len += ret; 116239beb93cSSam Leffler } 116339beb93cSSam Leffler return len; 116439beb93cSSam Leffler } 116539beb93cSSam Leffler 116639beb93cSSam Leffler 116739beb93cSSam Leffler static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) 116839beb93cSSam Leffler { 116939beb93cSSam Leffler struct eap_peap_data *data = priv; 117039beb93cSSam Leffler return data->key_data != NULL && data->phase2_success; 117139beb93cSSam Leffler } 117239beb93cSSam Leffler 117339beb93cSSam Leffler 117439beb93cSSam Leffler static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) 117539beb93cSSam Leffler { 117639beb93cSSam Leffler struct eap_peap_data *data = priv; 117739beb93cSSam Leffler u8 *key; 117839beb93cSSam Leffler 117939beb93cSSam Leffler if (data->key_data == NULL || !data->phase2_success) 118039beb93cSSam Leffler return NULL; 118139beb93cSSam Leffler 118239beb93cSSam Leffler key = os_malloc(EAP_TLS_KEY_LEN); 118339beb93cSSam Leffler if (key == NULL) 118439beb93cSSam Leffler return NULL; 118539beb93cSSam Leffler 118639beb93cSSam Leffler *len = EAP_TLS_KEY_LEN; 118739beb93cSSam Leffler 118839beb93cSSam Leffler if (data->crypto_binding_used) { 118939beb93cSSam Leffler u8 csk[128]; 119039beb93cSSam Leffler /* 119139beb93cSSam Leffler * Note: It looks like Microsoft implementation requires null 119239beb93cSSam Leffler * termination for this label while the one used for deriving 119339beb93cSSam Leffler * IPMK|CMK did not use null termination. 119439beb93cSSam Leffler */ 1195f05cddf9SRui Paulo if (peap_prfplus(data->peap_version, data->ipmk, 40, 119639beb93cSSam Leffler "Session Key Generating Function", 1197f05cddf9SRui Paulo (u8 *) "\00", 1, csk, sizeof(csk)) < 0) { 1198f05cddf9SRui Paulo os_free(key); 1199f05cddf9SRui Paulo return NULL; 1200f05cddf9SRui Paulo } 120139beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); 120239beb93cSSam Leffler os_memcpy(key, csk, EAP_TLS_KEY_LEN); 120339beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", 120439beb93cSSam Leffler key, EAP_TLS_KEY_LEN); 120539beb93cSSam Leffler } else 120639beb93cSSam Leffler os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); 120739beb93cSSam Leffler 120839beb93cSSam Leffler return key; 120939beb93cSSam Leffler } 121039beb93cSSam Leffler 121139beb93cSSam Leffler 1212*5b9c547cSRui Paulo static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 1213*5b9c547cSRui Paulo { 1214*5b9c547cSRui Paulo struct eap_peap_data *data = priv; 1215*5b9c547cSRui Paulo u8 *id; 1216*5b9c547cSRui Paulo 1217*5b9c547cSRui Paulo if (data->session_id == NULL || !data->phase2_success) 1218*5b9c547cSRui Paulo return NULL; 1219*5b9c547cSRui Paulo 1220*5b9c547cSRui Paulo id = os_malloc(data->id_len); 1221*5b9c547cSRui Paulo if (id == NULL) 1222*5b9c547cSRui Paulo return NULL; 1223*5b9c547cSRui Paulo 1224*5b9c547cSRui Paulo *len = data->id_len; 1225*5b9c547cSRui Paulo os_memcpy(id, data->session_id, data->id_len); 1226*5b9c547cSRui Paulo 1227*5b9c547cSRui Paulo return id; 1228*5b9c547cSRui Paulo } 1229*5b9c547cSRui Paulo 1230*5b9c547cSRui Paulo 123139beb93cSSam Leffler int eap_peer_peap_register(void) 123239beb93cSSam Leffler { 123339beb93cSSam Leffler struct eap_method *eap; 123439beb93cSSam Leffler int ret; 123539beb93cSSam Leffler 123639beb93cSSam Leffler eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 123739beb93cSSam Leffler EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); 123839beb93cSSam Leffler if (eap == NULL) 123939beb93cSSam Leffler return -1; 124039beb93cSSam Leffler 124139beb93cSSam Leffler eap->init = eap_peap_init; 124239beb93cSSam Leffler eap->deinit = eap_peap_deinit; 124339beb93cSSam Leffler eap->process = eap_peap_process; 124439beb93cSSam Leffler eap->isKeyAvailable = eap_peap_isKeyAvailable; 124539beb93cSSam Leffler eap->getKey = eap_peap_getKey; 124639beb93cSSam Leffler eap->get_status = eap_peap_get_status; 124739beb93cSSam Leffler eap->has_reauth_data = eap_peap_has_reauth_data; 124839beb93cSSam Leffler eap->deinit_for_reauth = eap_peap_deinit_for_reauth; 124939beb93cSSam Leffler eap->init_for_reauth = eap_peap_init_for_reauth; 1250*5b9c547cSRui Paulo eap->getSessionId = eap_peap_get_session_id; 125139beb93cSSam Leffler 125239beb93cSSam Leffler ret = eap_peer_method_register(eap); 125339beb93cSSam Leffler if (ret) 125439beb93cSSam Leffler eap_peer_method_free(eap); 125539beb93cSSam Leffler return ret; 125639beb93cSSam Leffler } 1257