X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=libeap%2Fsrc%2Feap_peer%2Feap.c;fp=libeap%2Fsrc%2Feap_peer%2Feap.c;h=9110ca5b9cfd64fc7dd92351f593d47c951aaf65;hb=88e34eecb5769da6526361b35df00fdbe823eac5;hp=482934ae8e94b695c2f3c30566e5a75f371d0565;hpb=2394142e4bf89e0a9e2a0091ac16bd6442e3d4c7;p=mech_eap.git diff --git a/libeap/src/eap_peer/eap.c b/libeap/src/eap_peer/eap.c index 482934a..9110ca5 100644 --- a/libeap/src/eap_peer/eap.c +++ b/libeap/src/eap_peer/eap.c @@ -1,15 +1,9 @@ /* * EAP peer state machines (RFC 4137) - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements the Peer State Machine as defined in RFC 4137. The used * states and state transitions match mostly with the RFC. However, there are @@ -26,8 +20,10 @@ #include "common.h" #include "pcsc_funcs.h" #include "state_machine.h" +#include "ext_password.h" #include "crypto/crypto.h" #include "crypto/tls.h" +#include "crypto/sha256.h" #include "common/wpa_ctrl.h" #include "eap_common/eap_wsc_common.h" #include "eap_i.h" @@ -37,6 +33,7 @@ #define STATE_MACHINE_DEBUG_PREFIX "EAP" #define EAP_MAX_AUTH_ROUNDS 50 +#define EAP_CLIENT_TIMEOUT_DEFAULT 60 static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, @@ -51,6 +48,8 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); static const char * eap_sm_method_state_txt(EapMethodState state); static const char * eap_sm_decision_txt(EapDecision decision); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, + const char *msg, size_t msglen); @@ -86,8 +85,30 @@ static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) } +static void eap_notify_status(struct eap_sm *sm, const char *status, + const char *parameter) +{ + wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)", + status, parameter); + if (sm->eapol_cb->notify_status) + sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter); +} + + +static void eap_sm_free_key(struct eap_sm *sm) +{ + if (sm->eapKeyData) { + bin_clear_free(sm->eapKeyData, sm->eapKeyDataLen); + sm->eapKeyData = NULL; + } +} + + static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) { + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + if (sm->m == NULL || sm->eap_method_priv == NULL) return; @@ -135,22 +156,26 @@ SM_STATE(EAP, INITIALIZE) SM_ENTRY(EAP, INITIALIZE); if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && sm->m->has_reauth_data(sm, sm->eap_method_priv) && - !sm->prev_failure) { + !sm->prev_failure && + sm->last_config == eap_get_config(sm)) { wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " "fast reauthentication"); sm->m->deinit_for_reauth(sm, sm->eap_method_priv); } else { + sm->last_config = eap_get_config(sm); eap_deinit_prev_method(sm, "INITIALIZE"); } sm->selectedMethod = EAP_TYPE_NONE; sm->methodState = METHOD_NONE; sm->allowNotifications = TRUE; sm->decision = DECISION_FAIL; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); eapol_set_bool(sm, EAPOL_eapFail, FALSE); - os_free(sm->eapKeyData); - sm->eapKeyData = NULL; + eap_sm_free_key(sm); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; sm->eapKeyAvailable = FALSE; eapol_set_bool(sm, EAPOL_eapRestart, FALSE); sm->lastId = -1; /* new session - make sure this does not match with @@ -165,8 +190,19 @@ SM_STATE(EAP, INITIALIZE) */ eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + /* + * RFC 4137 does not reset ignore here, but since it is possible for + * some method code paths to end up not setting ignore=FALSE, clear the + * value here to avoid issues if a previous authentication attempt + * failed with ignore=TRUE being left behind in the last + * m.check(eapReqData) operation. + */ + sm->ignore = 0; sm->num_rounds = 0; sm->prev_failure = 0; + sm->expected_failure = 0; + sm->reauthInit = FALSE; + sm->erp_seq = (u32) -1; } @@ -179,6 +215,12 @@ SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0; + /* + * RFC 4137 does not describe clearing of idleWhile here, but doing so + * allows the timer tick to be stopped more quickly when EAP is not in + * use. + */ + eapol_set_int(sm, EAPOL_idleWhile, 0); } @@ -217,6 +259,7 @@ SM_STATE(EAP, GET_METHOD) { int reinit; EapType method; + const struct eap_method *eap_method; SM_ENTRY(EAP, GET_METHOD); @@ -225,18 +268,24 @@ SM_STATE(EAP, GET_METHOD) else method = sm->reqMethod; + eap_method = eap_peer_get_eap_method(sm->reqVendor, method); + if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", sm->reqVendor, method); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u -> NAK", sm->reqVendor, method); + eap_notify_status(sm, "refuse proposed method", + eap_method ? eap_method->name : "unknown"); goto nak; } wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%u method=%u", sm->reqVendor, method); + eap_notify_status(sm, "accept proposed method", + eap_method ? eap_method->name : "unknown"); /* * RFC 4137 does not define specific operation for fast * re-authentication (session resumption). The design here is to allow @@ -260,7 +309,7 @@ SM_STATE(EAP, GET_METHOD) sm->selectedMethod = sm->reqMethod; if (sm->m == NULL) - sm->m = eap_peer_get_eap_method(sm->reqVendor, method); + sm->m = eap_method; if (!sm->m) { wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " "vendor %d method %d", @@ -268,14 +317,19 @@ SM_STATE(EAP, GET_METHOD) goto nak; } + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; + wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " "vendor %u method %u (%s)", sm->reqVendor, method, sm->m->name); - if (reinit) + if (reinit) { sm->eap_method_priv = sm->m->init_for_reauth( sm, sm->eap_method_priv); - else + } else { + sm->waiting_ext_cert_check = 0; + sm->ext_cert_check = 0; sm->eap_method_priv = sm->m->init(sm); + } if (sm->eap_method_priv == NULL) { struct eap_peer_config *config = eap_get_config(sm); @@ -315,6 +369,267 @@ nak: } +#ifdef CONFIG_ERP + +static char * eap_home_realm(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + char *realm; + size_t i, realm_len; + + if (!config) + return NULL; + + if (config->identity) { + for (i = 0; i < config->identity_len; i++) { + if (config->identity[i] == '@') + break; + } + if (i < config->identity_len) { + realm_len = config->identity_len - i - 1; + realm = os_malloc(realm_len + 1); + if (realm == NULL) + return NULL; + os_memcpy(realm, &config->identity[i + 1], realm_len); + realm[realm_len] = '\0'; + return realm; + } + } + + if (config->anonymous_identity) { + for (i = 0; i < config->anonymous_identity_len; i++) { + if (config->anonymous_identity[i] == '@') + break; + } + if (i < config->anonymous_identity_len) { + realm_len = config->anonymous_identity_len - i - 1; + realm = os_malloc(realm_len + 1); + if (realm == NULL) + return NULL; + os_memcpy(realm, &config->anonymous_identity[i + 1], + realm_len); + realm[realm_len] = '\0'; + return realm; + } + } + + return os_strdup(""); +} + + +static struct eap_erp_key * +eap_erp_get_key(struct eap_sm *sm, const char *realm) +{ + struct eap_erp_key *erp; + + dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) { + char *pos; + + pos = os_strchr(erp->keyname_nai, '@'); + if (!pos) + continue; + pos++; + if (os_strcmp(pos, realm) == 0) + return erp; + } + + return NULL; +} + + +static struct eap_erp_key * +eap_erp_get_key_nai(struct eap_sm *sm, const char *nai) +{ + struct eap_erp_key *erp; + + dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) { + if (os_strcmp(erp->keyname_nai, nai) == 0) + return erp; + } + + return NULL; +} + + +static void eap_peer_erp_free_key(struct eap_erp_key *erp) +{ + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); +} + + +static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm) +{ + struct eap_erp_key *erp; + + while ((erp = eap_erp_get_key(sm, realm)) != NULL) { + wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s", + erp->keyname_nai); + eap_peer_erp_free_key(erp); + } +} + +#endif /* CONFIG_ERP */ + + +void eap_peer_erp_free_keys(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + struct eap_erp_key *erp, *tmp; + + dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list) + eap_peer_erp_free_key(erp); +#endif /* CONFIG_ERP */ +} + + +static void eap_peer_erp_init(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + u8 *emsk = NULL; + size_t emsk_len = 0; + u8 EMSKname[EAP_EMSK_NAME_LEN]; + u8 len[2]; + char *realm; + size_t realm_len, nai_buf_len; + struct eap_erp_key *erp = NULL; + int pos; + + realm = eap_home_realm(sm); + if (!realm) + return; + realm_len = os_strlen(realm); + wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm); + eap_erp_remove_keys_realm(sm, realm); + + nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len; + if (nai_buf_len > 253) { + /* + * keyName-NAI has a maximum length of 253 octet to fit in + * RADIUS attributes. + */ + wpa_printf(MSG_DEBUG, + "EAP: Too long realm for ERP keyName-NAI maximum length"); + goto fail; + } + nai_buf_len++; /* null termination */ + erp = os_zalloc(sizeof(*erp) + nai_buf_len); + if (erp == NULL) + goto fail; + + emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP: No suitable EMSK available for ERP"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); + + WPA_PUT_BE16(len, 8); + if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK", + len, sizeof(len), + EMSKname, EAP_EMSK_NAME_LEN) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN); + + pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len, + EMSKname, EAP_EMSK_NAME_LEN); + erp->keyname_nai[pos] = '@'; + os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len); + + WPA_PUT_BE16(len, emsk_len); + if (hmac_sha256_kdf(emsk, emsk_len, + "EAP Re-authentication Root Key@ietf.org", + len, sizeof(len), erp->rRK, emsk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP"); + goto fail; + } + erp->rRK_len = emsk_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "EAP Re-authentication Integrity Key@ietf.org", + len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); + goto fail; + } + erp->rIK_len = erp->rRK_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len); + + wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai); + dl_list_add(&sm->erp_keys, &erp->list); + erp = NULL; +fail: + bin_clear_free(emsk, emsk_len); + bin_clear_free(erp, sizeof(*erp)); + os_free(realm); +#endif /* CONFIG_ERP */ +} + + +#ifdef CONFIG_ERP +static int eap_peer_erp_reauth_start(struct eap_sm *sm, + const struct eap_hdr *hdr, size_t len) +{ + char *realm; + struct eap_erp_key *erp; + struct wpabuf *msg; + u8 hash[SHA256_MAC_LEN]; + + realm = eap_home_realm(sm); + if (!realm) + return -1; + + erp = eap_erp_get_key(sm, realm); + os_free(realm); + realm = NULL; + if (!erp) + return -1; + + if (erp->next_seq >= 65536) + return -1; /* SEQ has range of 0..65535 */ + + /* TODO: check rRK lifetime expiration */ + + wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)", + erp->keyname_nai, erp->next_seq); + + msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, + 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16, + EAP_CODE_INITIATE, hdr->identifier); + if (msg == NULL) + return -1; + + wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */ + wpabuf_put_be16(msg, erp->next_seq); + + wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI); + wpabuf_put_u8(msg, os_strlen(erp->keyname_nai)); + wpabuf_put_str(msg, erp->keyname_nai); + + wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */ + + if (hmac_sha256(erp->rIK, erp->rIK_len, + wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { + wpabuf_free(msg); + return -1; + } + wpabuf_put_data(msg, hash, 16); + + wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth"); + sm->erp_seq = erp->next_seq; + erp->next_seq++; + wpabuf_free(sm->eapRespData); + sm->eapRespData = msg; + sm->reauthInit = TRUE; + return 0; +} +#endif /* CONFIG_ERP */ + + /* * The method processing happens here. The request from the authenticator is * processed, and an appropriate response packet is built. @@ -323,6 +638,7 @@ SM_STATE(EAP, METHOD) { struct wpabuf *eapReqData; struct eap_method_ret ret; + int min_len = 1; SM_ENTRY(EAP, METHOD); if (sm->m == NULL) { @@ -331,6 +647,10 @@ SM_STATE(EAP, METHOD) } eapReqData = eapol_get_eapReqData(sm); + if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP) + min_len = 0; /* LEAP uses EAP-Success without payload */ + if (!eap_hdr_len_valid(eapReqData, min_len)) + return; /* * Get ignore, methodState, decision, allowNotifications, and @@ -356,10 +676,11 @@ SM_STATE(EAP, METHOD) sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, eapReqData); wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " - "methodState=%s decision=%s", + "methodState=%s decision=%s eapRespData=%p", ret.ignore ? "TRUE" : "FALSE", eap_sm_method_state_txt(ret.methodState), - eap_sm_decision_txt(ret.decision)); + eap_sm_decision_txt(ret.decision), + sm->eapRespData); sm->ignore = ret.ignore; if (sm->ignore) @@ -370,9 +691,22 @@ SM_STATE(EAP, METHOD) if (sm->m->isKeyAvailable && sm->m->getKey && sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { - os_free(sm->eapKeyData); + struct eap_peer_config *config = eap_get_config(sm); + + eap_sm_free_key(sm); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; + if (sm->m->getSessionId) { + sm->eapSessionId = sm->m->getSessionId( + sm, sm->eap_method_priv, + &sm->eapSessionIdLen); + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eapSessionId, sm->eapSessionIdLen); + } + if (config->erp && sm->m->get_emsk && sm->eapSessionId) + eap_peer_erp_init(sm); } } @@ -387,14 +721,17 @@ SM_STATE(EAP, SEND_RESPONSE) wpabuf_free(sm->lastRespData); if (sm->eapRespData) { if (sm->workaround) - os_memcpy(sm->last_md5, sm->req_md5, 16); + os_memcpy(sm->last_sha1, sm->req_sha1, 20); sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); - } else + } else { + wpa_printf(MSG_DEBUG, "EAP: No eapRespData available"); sm->lastRespData = NULL; + } eapol_set_bool(sm, EAPOL_eapReq, FALSE); eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); + sm->reauthInit = FALSE; } @@ -419,6 +756,8 @@ SM_STATE(EAP, IDENTITY) SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processIdentity(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -435,6 +774,8 @@ SM_STATE(EAP, NOTIFICATION) SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processNotify(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -586,12 +927,12 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm) duplicate = (sm->reqId == sm->lastId) && sm->rxReq; if (sm->workaround && duplicate && - os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) { /* * RFC 4137 uses (reqId == lastId) as the only verification for * duplicate EAP requests. However, this misses cases where the * AS is incorrectly using the same id again; and - * unfortunately, such implementations exist. Use MD5 hash as + * unfortunately, such implementations exist. Use SHA1 hash as * an extra verification for the packets being duplicate to * workaround these issues. */ @@ -606,6 +947,15 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm) } +static int eap_peer_sm_allow_canned(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + + return config && config->phase1 && + os_strstr(config->phase1, "allow_canned_success=1"); +} + + static void eap_peer_sm_step_received(struct eap_sm *sm) { int duplicate = eap_peer_req_is_duplicate(sm); @@ -619,6 +969,17 @@ static void eap_peer_sm_step_received(struct eap_sm *sm) (sm->reqId == sm->lastId || eap_success_workaround(sm, sm->reqId, sm->lastId))) SM_ENTER(EAP, SUCCESS); + else if (sm->workaround && sm->lastId == -1 && sm->rxSuccess && + !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */ + else if (sm->workaround && sm->lastId == -1 && sm->rxFailure && + !sm->rxReq && sm->methodState != METHOD_CONT && + eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */ + else if (sm->workaround && sm->rxSuccess && !sm->rxFailure && + !sm->rxReq && sm->methodState != METHOD_CONT && + eap_peer_sm_allow_canned(sm)) + SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */ else if (sm->methodState != METHOD_CONT && ((sm->rxFailure && sm->decision != DECISION_UNCOND_SUCC) || @@ -650,6 +1011,8 @@ static void eap_peer_sm_step_received(struct eap_sm *sm) else if (sm->selectedMethod == EAP_TYPE_LEAP && (sm->rxSuccess || sm->rxResp)) SM_ENTER(EAP, METHOD); + else if (sm->reauthInit) + SM_ENTER(EAP, SEND_RESPONSE); else SM_ENTER(EAP, DISCARD); } @@ -679,8 +1042,19 @@ static void eap_peer_sm_step_local(struct eap_sm *sm) SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_METHOD: + /* + * Note: RFC 4137 uses methodState == DONE && decision == FAIL + * as the condition. eapRespData == NULL here is used to allow + * final EAP method response to be sent without having to change + * all methods to either use methodState MAY_CONT or leaving + * decision to something else than FAIL in cases where the only + * expected response is EAP-Failure. + */ if (sm->ignore) SM_ENTER(EAP, DISCARD); + else if (sm->methodState == METHOD_DONE && + sm->decision == DECISION_FAIL && !sm->eapRespData) + SM_ENTER(EAP, FAILURE); else SM_ENTER(EAP, SEND_RESPONSE); break; @@ -852,12 +1226,17 @@ static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) { - const struct eap_hdr *hdr = wpabuf_head(req); - const u8 *pos = (const u8 *) (hdr + 1); - pos++; + const u8 *pos; + size_t msg_len; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); + eap_notify_status(sm, "started", ""); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, + &msg_len); + if (pos == NULL) + return; /* * RFC 3748 - 5.1: Identity @@ -869,15 +1248,80 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) /* TODO: could save displayable message so that it can be shown to the * user in case of interaction is required */ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", - pos, be_to_host16(hdr->length) - 5); + pos, msg_len); } #ifdef PCSC_FUNCS + +/* + * Rules for figuring out MNC length based on IMSI for SIM cards that do not + * include MNC length field. + */ +static int mnc_len_from_imsi(const char *imsi) +{ + char mcc_str[4]; + unsigned int mcc; + + os_memcpy(mcc_str, imsi, 3); + mcc_str[3] = '\0'; + mcc = atoi(mcc_str); + + if (mcc == 228) + return 2; /* Networks in Switzerland use 2-digit MNC */ + if (mcc == 244) + return 2; /* Networks in Finland use 2-digit MNC */ + + return -1; +} + + +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, + size_t max_len, size_t *imsi_len) +{ + int mnc_len; + char *pos, mnc[4]; + + if (*imsi_len + 36 > max_len) { + wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); + return -1; + } + + /* MNC (2 or 3 digits) */ + mnc_len = scard_get_mnc_len(sm->scard_ctx); + if (mnc_len < 0) + mnc_len = mnc_len_from_imsi(imsi); + if (mnc_len < 0) { + wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " + "assuming 3"); + mnc_len = 3; + } + + if (mnc_len == 2) { + mnc[0] = '0'; + mnc[1] = imsi[3]; + mnc[2] = imsi[4]; + } else if (mnc_len == 3) { + mnc[0] = imsi[3]; + mnc[1] = imsi[4]; + mnc[2] = imsi[5]; + } + mnc[3] = '\0'; + + pos = imsi + *imsi_len; + pos += os_snprintf(pos, imsi + max_len - pos, + "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", + mnc, imsi[0], imsi[1], imsi[2]); + *imsi_len = pos - imsi; + + return 0; +} + + static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { - int aka = 0; + enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM; char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; @@ -891,11 +1335,28 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + if (imsi_len < 7) { + wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity"); + return -1; + } + + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { + wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len); + for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF || m[i].method != EAP_TYPE_NONE); i++) { if (m[i].vendor == EAP_VENDOR_IETF && + m[i].method == EAP_TYPE_AKA_PRIME) { + method = EAP_SM_AKA_PRIME; + break; + } + + if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA) { - aka = 1; + method = EAP_SM_AKA; break; } } @@ -908,19 +1369,27 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, return -1; } - conf->identity[0] = aka ? '0' : '1'; + switch (method) { + case EAP_SM_SIM: + conf->identity[0] = '1'; + break; + case EAP_SM_AKA: + conf->identity[0] = '0'; + break; + case EAP_SM_AKA_PRIME: + conf->identity[0] = '6'; + break; + } os_memcpy(conf->identity + 1, imsi, imsi_len); conf->identity_len = 1 + imsi_len; return 0; } -#endif /* PCSC_FUNCS */ static int eap_sm_set_scard_pin(struct eap_sm *sm, struct eap_peer_config *conf) { -#ifdef PCSC_FUNCS if (scard_set_pin(sm->scard_ctx, conf->pin)) { /* * Make sure the same PIN is not tried again in order to avoid @@ -934,24 +1403,20 @@ static int eap_sm_set_scard_pin(struct eap_sm *sm, return -1; } return 0; -#else /* PCSC_FUNCS */ - return -1; -#endif /* PCSC_FUNCS */ } + static int eap_sm_get_scard_identity(struct eap_sm *sm, struct eap_peer_config *conf) { -#ifdef PCSC_FUNCS if (eap_sm_set_scard_pin(sm, conf)) return -1; return eap_sm_imsi_identity(sm, conf); -#else /* PCSC_FUNCS */ - return -1; -#endif /* PCSC_FUNCS */ } +#endif /* PCSC_FUNCS */ + /** * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network @@ -994,23 +1459,27 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) identity, identity_len); } - if (identity == NULL) { - wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " - "configuration was not available"); - if (config->pcsc) { + if (config->pcsc) { +#ifdef PCSC_FUNCS + if (!identity) { if (eap_sm_get_scard_identity(sm, config) < 0) return NULL; identity = config->identity; identity_len = config->identity_len; - wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " - "IMSI", identity, identity_len); - } else { - eap_sm_request_identity(sm); + wpa_hexdump_ascii(MSG_DEBUG, + "permanent identity from IMSI", + identity, identity_len); + } else if (eap_sm_set_scard_pin(sm, config) < 0) { return NULL; } - } else if (config->pcsc) { - if (eap_sm_set_scard_pin(sm, config) < 0) - return NULL; +#else /* PCSC_FUNCS */ + return NULL; +#endif /* PCSC_FUNCS */ + } else if (!identity) { + wpa_printf(MSG_WARNING, + "EAP: buildIdentity: identity configuration was not available"); + eap_sm_request_identity(sm); + return NULL; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, @@ -1051,15 +1520,222 @@ static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) static struct wpabuf * eap_sm_buildNotify(int id) { - struct wpabuf *resp; - wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); - resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, - EAP_CODE_RESPONSE, id); - if (resp == NULL) - return NULL; + return eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, + EAP_CODE_RESPONSE, id); +} - return resp; + +static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr, + size_t len) +{ +#ifdef CONFIG_ERP + const u8 *pos = (const u8 *) (hdr + 1); + const u8 *end = ((const u8 *) hdr) + len; + struct erp_tlvs parse; + + if (len < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate"); + return; + } + + if (*pos != EAP_ERP_TYPE_REAUTH_START) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored unexpected EAP-Initiate Type=%u", + *pos); + return; + } + + pos++; + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "EAP: Too short EAP-Initiate/Re-auth-Start"); + return; + } + pos++; /* Reserved */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs", + pos, end - pos); + + if (erp_parse_tlvs(pos, end, &parse, 0) < 0) + goto invalid; + + if (parse.domain) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP: EAP-Initiate/Re-auth-Start - Domain name", + parse.domain, parse.domain_len); + /* TODO: Derivation of domain specific keys for local ER */ + } + + if (eap_peer_erp_reauth_start(sm, hdr, len) == 0) + return; + +invalid: +#endif /* CONFIG_ERP */ + wpa_printf(MSG_DEBUG, + "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication"); + eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE); +} + + +static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, + size_t len) +{ +#ifdef CONFIG_ERP + const u8 *pos = (const u8 *) (hdr + 1); + const u8 *end = ((const u8 *) hdr) + len; + const u8 *start; + struct erp_tlvs parse; + u8 flags; + u16 seq; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + struct eap_erp_key *erp; + int max_len; + char nai[254]; + u8 seed[4]; + int auth_tag_ok = 0; + + if (len < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish"); + return; + } + + if (*pos != EAP_ERP_TYPE_REAUTH) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored unexpected EAP-Finish Type=%u", *pos); + return; + } + + if (len < sizeof(*hdr) + 4) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored too short EAP-Finish/Re-auth"); + return; + } + + pos++; + flags = *pos++; + seq = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq); + + if (seq != sm->erp_seq) { + wpa_printf(MSG_DEBUG, + "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq); + return; + } + + /* + * Parse TVs/TLVs. Since we do not yet know the length of the + * Authentication Tag, stop parsing if an unknown TV/TLV is seen and + * just try to find the keyName-NAI first so that we can check the + * Authentication Tag. + */ + if (erp_parse_tlvs(pos, end, &parse, 1) < 0) + return; + + if (!parse.keyname) { + wpa_printf(MSG_DEBUG, + "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet"); + return; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI", + parse.keyname, parse.keyname_len); + if (parse.keyname_len > 253) { + wpa_printf(MSG_DEBUG, + "EAP: Too long keyName-NAI in EAP-Finish/Re-auth"); + return; + } + os_memcpy(nai, parse.keyname, parse.keyname_len); + nai[parse.keyname_len] = '\0'; + + erp = eap_erp_get_key_nai(sm, nai); + if (!erp) { + wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s", + nai); + return; + } + + /* Is there enough room for Cryptosuite and Authentication Tag? */ + start = parse.keyname + parse.keyname_len; + max_len = end - start; + hash_len = 16; + if (max_len < 1 + (int) hash_len) { + wpa_printf(MSG_DEBUG, + "EAP: Not enough room for Authentication Tag"); + if (flags & 0x80) + goto no_auth_tag; + return; + } + if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) { + wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used"); + if (flags & 0x80) + goto no_auth_tag; + return; + } + + if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr, + end - ((const u8 *) hdr) - hash_len, hash) < 0) + return; + if (os_memcmp(end - hash_len, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag mismatch"); + return; + } + auth_tag_ok = 1; + end -= 1 + hash_len; + +no_auth_tag: + /* + * Parse TVs/TLVs again now that we know the exact part of the buffer + * that contains them. + */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs", + pos, end - pos); + if (erp_parse_tlvs(pos, end, &parse, 0) < 0) + return; + + if (flags & 0x80 || !auth_tag_ok) { + wpa_printf(MSG_DEBUG, + "EAP: EAP-Finish/Re-auth indicated failure"); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + "EAP authentication failed"); + sm->prev_failure = 1; + wpa_printf(MSG_DEBUG, + "EAP: Drop ERP key to try full authentication on next attempt"); + eap_peer_erp_free_key(erp); + return; + } + + eap_sm_free_key(sm); + sm->eapKeyDataLen = 0; + sm->eapKeyData = os_malloc(erp->rRK_len); + if (!sm->eapKeyData) + return; + sm->eapKeyDataLen = erp->rRK_len; + + WPA_PUT_BE16(seed, seq); + WPA_PUT_BE16(&seed[2], erp->rRK_len); + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "Re-authentication Master Session Key@ietf.org", + seed, sizeof(seed), + sm->eapKeyData, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP"); + eap_sm_free_key(sm); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK", + sm->eapKeyData, sm->eapKeyDataLen); + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP re-authentication completed successfully"); +#endif /* CONFIG_ERP */ } @@ -1093,7 +1769,7 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) if (sm->workaround) { const u8 *addr[1]; addr[0] = wpabuf_head(req); - md5_vector(1, addr, &plen, sm->req_md5); + sha1_vector(1, addr, &plen, sm->req_sha1); } switch (hdr->code) { @@ -1146,12 +1822,20 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + eap_notify_status(sm, "completion", "success"); sm->rxSuccess = TRUE; break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + eap_notify_status(sm, "completion", "failure"); sm->rxFailure = TRUE; break; + case EAP_CODE_INITIATE: + eap_peer_initiate(sm, hdr, plen); + break; + case EAP_CODE_FINISH: + eap_peer_finish(sm, hdr, plen); + break; default: wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " "code %d", hdr->code); @@ -1165,9 +1849,17 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, { struct eap_sm *sm = ctx; char *hash_hex = NULL; - char *cert_hex = NULL; switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + eap_notify_status(sm, "remote certificate verification", + "success"); + if (sm->ext_cert_check) { + sm->waiting_ext_cert_check = 1; + eap_sm_request(sm, WPA_CTRL_REQ_EXT_CERT_CHECK, + NULL, 0); + } + break; case TLS_CERT_CHAIN_FAILURE: wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR "reason=%d depth=%d subject='%s' err='%s'", @@ -1175,8 +1867,13 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, data->cert_fail.depth, data->cert_fail.subject, data->cert_fail.reason_txt); + eap_notify_status(sm, "remote certificate verification", + data->cert_fail.reason_txt); break; case TLS_PEER_CERTIFICATE: + if (!sm->eapol_cb->notify_cert) + break; + if (data->peer_cert.hash) { size_t len = data->peer_cert.hash_len * 2 + 1; hash_hex = os_malloc(len); @@ -1186,31 +1883,25 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, data->peer_cert.hash_len); } } - wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT - "depth=%d subject='%s'%s%s", - data->peer_cert.depth, data->peer_cert.subject, - hash_hex ? " hash=" : "", hash_hex ? hash_hex : ""); - - if (data->peer_cert.cert) { - size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1; - cert_hex = os_malloc(len); - if (cert_hex == NULL) - break; - wpa_snprintf_hex(cert_hex, len, - wpabuf_head(data->peer_cert.cert), - wpabuf_len(data->peer_cert.cert)); - wpa_msg_ctrl(sm->msg_ctx, MSG_INFO, - WPA_EVENT_EAP_PEER_CERT - "depth=%d subject='%s' cert=%s", - data->peer_cert.depth, - data->peer_cert.subject, - cert_hex); - } + + sm->eapol_cb->notify_cert(sm->eapol_ctx, + data->peer_cert.depth, + data->peer_cert.subject, + data->peer_cert.altsubject, + data->peer_cert.num_altsubject, + hash_hex, data->peer_cert.cert); + break; + case TLS_ALERT: + if (data->alert.is_local) + eap_notify_status(sm, "local TLS alert", + data->alert.description); + else + eap_notify_status(sm, "remote TLS alert", + data->alert.description); break; } os_free(hash_hex); - os_free(cert_hex); } @@ -1229,7 +1920,7 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, * structure remains alive while the EAP state machine is active. */ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, + const struct eapol_callbacks *eapol_cb, void *msg_ctx, struct eap_config *conf) { struct eap_sm *sm; @@ -1241,18 +1932,21 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->msg_ctx = msg_ctx; - sm->ClientTimeout = 60; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; sm->wps = conf->wps; + dl_list_init(&sm->erp_keys); os_memset(&tlsconf, 0, sizeof(tlsconf)); tlsconf.opensc_engine_path = conf->opensc_engine_path; tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; tlsconf.pkcs11_module_path = conf->pkcs11_module_path; + tlsconf.openssl_ciphers = conf->openssl_ciphers; #ifdef CONFIG_FIPS tlsconf.fips_mode = 1; #endif /* CONFIG_FIPS */ tlsconf.event_cb = eap_peer_sm_tls_event; tlsconf.cb_ctx = sm; + tlsconf.cert_in_cb = conf->cert_in_cb; sm->ssl_ctx = tls_init(&tlsconf); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " @@ -1261,6 +1955,13 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, return NULL; } + sm->ssl_ctx2 = tls_init(&tlsconf); + if (sm->ssl_ctx2 == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS " + "context (2)."); + /* Run without separate TLS context within TLS tunnel */ + } + return sm; } @@ -1278,7 +1979,10 @@ void eap_peer_sm_deinit(struct eap_sm *sm) return; eap_deinit_prev_method(sm, "EAP deinit"); eap_sm_abort(sm); + if (sm->ssl_ctx2) + tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); + eap_peer_erp_free_keys(sm); os_free(sm); } @@ -1318,8 +2022,9 @@ void eap_sm_abort(struct eap_sm *sm) sm->lastRespData = NULL; wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; - os_free(sm->eapKeyData); - sm->eapKeyData = NULL; + eap_sm_free_key(sm); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; /* This is not clearly specified in the EAP statemachines draft, but * it seems necessary to make sure that some of the EAPOL variables get @@ -1426,7 +2131,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) len = os_snprintf(buf, buflen, "EAP state=%s\n", eap_sm_state_txt(sm->EAP_state)); - if (len < 0 || (size_t) len >= buflen) + if (os_snprintf_error(buflen, len)) return 0; if (sm->selectedMethod != EAP_TYPE_NONE) { @@ -1445,7 +2150,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) ret = os_snprintf(buf + len, buflen - len, "selectedMethod=%d (EAP-%s)\n", sm->selectedMethod, name); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1466,7 +2171,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) eap_sm_method_state_txt(sm->methodState), eap_sm_decision_txt(sm->decision), sm->ClientTimeout); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } @@ -1476,17 +2181,13 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) #endif /* CONFIG_CTRL_IFACE */ -#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) -typedef enum { - TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD, - TYPE_PASSPHRASE -} eap_ctrl_req_type; - -static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) struct eap_peer_config *config; - char *field, *txt, *tmp; + const char *txt = NULL; + char *tmp; if (sm == NULL) return; @@ -1494,29 +2195,20 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, if (config == NULL) return; - switch (type) { - case TYPE_IDENTITY: - field = "IDENTITY"; - txt = "Identity"; + switch (field) { + case WPA_CTRL_REQ_EAP_IDENTITY: config->pending_req_identity++; break; - case TYPE_PASSWORD: - field = "PASSWORD"; - txt = "Password"; + case WPA_CTRL_REQ_EAP_PASSWORD: config->pending_req_password++; break; - case TYPE_NEW_PASSWORD: - field = "NEW_PASSWORD"; - txt = "New Password"; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: config->pending_req_new_password++; break; - case TYPE_PIN: - field = "PIN"; - txt = "PIN"; + case WPA_CTRL_REQ_EAP_PIN: config->pending_req_pin++; break; - case TYPE_OTP: - field = "OTP"; + case WPA_CTRL_REQ_EAP_OTP: if (msg) { tmp = os_malloc(msglen + 3); if (tmp == NULL) @@ -1535,21 +2227,30 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, txt = config->pending_req_otp; } break; - case TYPE_PASSPHRASE: - field = "PASSPHRASE"; - txt = "Private key passphrase"; + case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; + case WPA_CTRL_REQ_SIM: + txt = msg; + break; + case WPA_CTRL_REQ_EXT_CERT_CHECK: + break; default: return; } if (sm->eapol_cb->eap_param_needed) sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); -} -#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ -#define eap_sm_request(sm, type, msg, msglen) do { } while (0) #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +} + + +const char * eap_sm_get_method_name(struct eap_sm *sm) +{ + if (sm->m == NULL) + return "UNKNOWN"; + return sm->m->name; +} /** @@ -1563,7 +2264,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, */ void eap_sm_request_identity(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_IDENTITY, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); } @@ -1578,7 +2279,7 @@ void eap_sm_request_identity(struct eap_sm *sm) */ void eap_sm_request_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); } @@ -1593,7 +2294,7 @@ void eap_sm_request_password(struct eap_sm *sm) */ void eap_sm_request_new_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); } @@ -1608,7 +2309,7 @@ void eap_sm_request_new_password(struct eap_sm *sm) */ void eap_sm_request_pin(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PIN, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); } @@ -1624,7 +2325,7 @@ void eap_sm_request_pin(struct eap_sm *sm) */ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) { - eap_sm_request(sm, TYPE_OTP, msg, msg_len); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); } @@ -1639,7 +2340,18 @@ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) */ void eap_sm_request_passphrase(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0); +} + + +/** + * eap_sm_request_sim - Request external SIM processing + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @req: EAP method specific request + */ +void eap_sm_request_sim(struct eap_sm *sm, const char *req) +{ + eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req)); } @@ -1698,7 +2410,7 @@ static int eap_allowed_phase2_type(int vendor, int type) u32 eap_get_phase2_type(const char *name, int *vendor) { int v; - u8 type = eap_peer_get_type(name, &v); + u32 type = eap_peer_get_type(name, &v); if (eap_allowed_phase2_type(v, type)) { *vendor = v; return type; @@ -1806,6 +2518,27 @@ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) } +static int eap_get_ext_password(struct eap_sm *sm, + struct eap_peer_config *config) +{ + char *name; + + if (config->password == NULL) + return -1; + + name = os_zalloc(config->password_len + 1); + if (name == NULL) + return -1; + os_memcpy(name, config->password, config->password_len); + + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); + os_free(name); + + return sm->ext_pw_buf == NULL ? -1 : 0; +} + + /** * eap_get_config_password - Get password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -1817,6 +2550,14 @@ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; return config->password; } @@ -1836,6 +2577,16 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) struct eap_peer_config *config = eap_get_config(sm); if (config == NULL) return NULL; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + if (hash) + *hash = 0; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; if (hash) *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); @@ -1989,6 +2740,28 @@ void eap_notify_lower_layer_success(struct eap_sm *sm) /** + * eap_get_eapSessionId - Get Session-Id from EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Pointer to variable that will be set to number of bytes in the session + * Returns: Pointer to the EAP Session-Id or %NULL on failure + * + * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available + * only after a successful authentication. EAP state machine continues to manage + * the Session-Id and the caller must not change or free the returned data. + */ +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapSessionId == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapSessionIdLen; + return sm->eapSessionId; +} + + +/** * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @len: Pointer to variable that will be set to number of bytes in the key @@ -2097,6 +2870,17 @@ void eap_set_force_disabled(struct eap_sm *sm, int disabled) } +/** + * eap_set_external_sim - Set external_sim flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @external_sim: Whether external SIM/USIM processing is used + */ +void eap_set_external_sim(struct eap_sm *sm, int external_sim) +{ + sm->external_sim = external_sim; +} + + /** * eap_notify_pending - Notify that EAP method is ready to re-process a request * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -2147,3 +2931,30 @@ int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) return 1; } + + +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) +{ + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + sm->ext_pw = ext; +} + + +/** + * eap_set_anon_id - Set or add anonymous identity + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) +{ + if (sm->eapol_cb->set_anon_id) + sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); +} + + +int eap_peer_was_failure_expected(struct eap_sm *sm) +{ + return sm->expected_failure; +}