/*
* hostapd / EAP Full Authenticator state machine (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
- * 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 state machine is based on the full authenticator state machine defined
* in RFC 4137. However, to support backend authentication in RADIUS
#include "includes.h"
#include "common.h"
+#include "crypto/sha256.h"
#include "eap_i.h"
#include "state_machine.h"
#include "common/wpa_ctrl.h"
static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
+static int eap_get_erp_send_reauth_start(struct eap_sm *sm)
+{
+ if (sm->eapol_cb->get_erp_send_reauth_start)
+ return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx);
+ return 0;
+}
+
+
+static const char * eap_get_erp_domain(struct eap_sm *sm)
+{
+ if (sm->eapol_cb->get_erp_domain)
+ return sm->eapol_cb->get_erp_domain(sm->eapol_ctx);
+ return NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm,
+ const char *keyname)
+{
+ if (sm->eapol_cb->erp_get_key)
+ return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname);
+ return NULL;
+}
+
+
+static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp)
+{
+ if (sm->eapol_cb->erp_add_key)
+ return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp);
+ return -1;
+}
+
+#endif /* CONFIG_ERP */
+
+
+static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
+ u8 id)
+{
+ const char *domain;
+ size_t plen = 1;
+ struct wpabuf *msg;
+ size_t domain_len = 0;
+
+ domain = eap_get_erp_domain(sm);
+ if (domain) {
+ domain_len = os_strlen(domain);
+ plen += 2 + domain_len;
+ }
+
+ msg = eap_msg_alloc(EAP_VENDOR_IETF,
+ (EapType) EAP_ERP_TYPE_REAUTH_START, plen,
+ EAP_CODE_INITIATE, id);
+ if (msg == NULL)
+ return NULL;
+ wpabuf_put_u8(msg, 0); /* Reserved */
+ if (domain) {
+ /* Domain name TLV */
+ wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME);
+ wpabuf_put_u8(msg, domain_len);
+ wpabuf_put_data(msg, domain, domain_len);
+ }
+
+ return msg;
+}
+
+
static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
{
if (src == NULL)
}
+void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ int buflen;
+
+ if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL)
+ return;
+
+ va_start(ap, fmt);
+ buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+ va_end(ap);
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ va_start(ap, fmt);
+ vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+
+ sm->eapol_cb->log_msg(sm->eapol_ctx, buf);
+
+ os_free(buf);
+}
+
+
SM_STATE(EAP, DISABLED)
{
SM_ENTRY(EAP, DISABLED);
{
SM_ENTRY(EAP, INITIALIZE);
+ if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
+ /*
+ * Need to allow internal Identity method to be used instead
+ * of passthrough at the beginning of reauthentication.
+ */
+ eap_server_clear_identity(sm);
+ }
+
+ sm->try_initiate_reauth = FALSE;
sm->currentId = -1;
sm->eap_if.eapSuccess = FALSE;
sm->eap_if.eapFail = FALSE;
sm->eap_if.eapTimeout = FALSE;
- os_free(sm->eap_if.eapKeyData);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
sm->eap_if.eapKeyData = NULL;
sm->eap_if.eapKeyDataLen = 0;
+ os_free(sm->eap_if.eapSessionId);
+ sm->eap_if.eapSessionId = NULL;
+ sm->eap_if.eapSessionIdLen = 0;
sm->eap_if.eapKeyAvailable = FALSE;
sm->eap_if.eapRestart = FALSE;
/*
- * Start reauthentication with identity request even if we know the
- * previously used identity. This is needed to get reauthentication
- * started properly.
- */
- sm->start_reauth = TRUE;
-
- /*
* This is not defined in RFC 4137, but method state needs to be
* reseted here so that it does not remain in success state when
* re-authentication starts.
{
SM_ENTRY(EAP, INTEGRITY_CHECK);
+ if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
+ sm->ignore = TRUE;
+ return;
+ }
+
if (sm->m->check) {
sm->ignore = sm->m->check(sm, sm->eap_method_priv,
sm->eap_if.eapRespData);
}
+static void eap_server_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];
+ const char *domain;
+ size_t domain_len, nai_buf_len;
+ struct eap_server_erp_key *erp = NULL;
+ int pos;
+
+ domain = eap_get_erp_domain(sm);
+ if (!domain)
+ return;
+
+ domain_len = os_strlen(domain);
+
+ nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_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");
+ return;
+ }
+ nai_buf_len++; /* null termination */
+ erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+ if (erp == NULL)
+ goto fail;
+ erp->recv_seq = (u32) -1;
+
+ 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->eap_if.eapSessionId, sm->eap_if.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], domain, domain_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);
+
+ if (eap_erp_add_key(sm, erp) == 0) {
+ wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s",
+ erp->keyname_nai);
+ erp = NULL;
+ }
+
+fail:
+ bin_clear_free(emsk, emsk_len);
+ bin_clear_free(erp, sizeof(*erp));
+#endif /* CONFIG_ERP */
+}
+
+
SM_STATE(EAP, METHOD_RESPONSE)
{
SM_ENTRY(EAP, METHOD_RESPONSE);
+ if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+ return;
+
sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
if (sm->m->isDone(sm, sm->eap_method_priv)) {
eap_sm_Policy_update(sm, NULL, 0);
- os_free(sm->eap_if.eapKeyData);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
if (sm->m->getKey) {
sm->eap_if.eapKeyData = sm->m->getKey(
sm, sm->eap_method_priv,
sm->eap_if.eapKeyData = NULL;
sm->eap_if.eapKeyDataLen = 0;
}
+ os_free(sm->eap_if.eapSessionId);
+ sm->eap_if.eapSessionId = NULL;
+ if (sm->m->getSessionId) {
+ sm->eap_if.eapSessionId = sm->m->getSessionId(
+ sm, sm->eap_method_priv,
+ &sm->eap_if.eapSessionIdLen);
+ wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
+ sm->eap_if.eapSessionId,
+ sm->eap_if.eapSessionIdLen);
+ }
+ if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
+ eap_server_erp_init(sm);
sm->methodState = METHOD_END;
} else {
sm->methodState = METHOD_CONTINUE;
SM_ENTRY(EAP, PROPOSE_METHOD);
+ sm->try_initiate_reauth = FALSE;
+try_another_method:
type = eap_sm_Policy_getNextMethod(sm, &vendor);
if (vendor == EAP_VENDOR_IETF)
sm->currentMethod = type;
"method %d", sm->currentMethod);
sm->m = NULL;
sm->currentMethod = EAP_TYPE_NONE;
+ goto try_another_method;
}
}
+ if (sm->m == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method");
+ eap_log_msg(sm, "Could not find suitable EAP method");
+ sm->decision = DECISION_FAILURE;
+ return;
+ }
if (sm->currentMethod == EAP_TYPE_IDENTITY ||
sm->currentMethod == EAP_TYPE_NOTIFICATION)
sm->methodState = METHOD_CONTINUE;
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
"vendor=%u method=%u", vendor, sm->currentMethod);
+ eap_log_msg(sm, "Propose EAP method vendor=%u method=%u",
+ vendor, sm->currentMethod);
}
}
sm->m = NULL;
+ if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+ return;
+
nak = wpabuf_head(sm->eap_if.eapRespData);
if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
len = be_to_host16(nak->length);
}
+SM_STATE(EAP, INITIATE_REAUTH_START)
+{
+ SM_ENTRY(EAP, INITIATE_REAUTH_START);
+
+ sm->initiate_reauth_start_sent = TRUE;
+ sm->try_initiate_reauth = TRUE;
+ sm->currentId = eap_sm_nextId(sm, sm->currentId);
+ wpa_printf(MSG_DEBUG,
+ "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d",
+ sm->currentId);
+ sm->lastId = sm->currentId;
+ wpabuf_free(sm->eap_if.eapReqData);
+ sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm,
+ sm->currentId);
+ wpabuf_free(sm->lastReqData);
+ sm->lastReqData = NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static void erp_send_finish_reauth(struct eap_sm *sm,
+ struct eap_server_erp_key *erp, u8 id,
+ u8 flags, u16 seq, const char *nai)
+{
+ size_t plen;
+ struct wpabuf *msg;
+ u8 hash[SHA256_MAC_LEN];
+ size_t hash_len;
+ u8 seed[4];
+
+ if (erp) {
+ switch (erp->cryptosuite) {
+ case EAP_ERP_CS_HMAC_SHA256_256:
+ hash_len = 32;
+ break;
+ case EAP_ERP_CS_HMAC_SHA256_128:
+ hash_len = 16;
+ break;
+ default:
+ return;
+ }
+ } else
+ hash_len = 0;
+
+ plen = 1 + 2 + 2 + os_strlen(nai);
+ if (hash_len)
+ plen += 1 + hash_len;
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+ plen, EAP_CODE_FINISH, id);
+ if (msg == NULL)
+ return;
+ wpabuf_put_u8(msg, flags);
+ wpabuf_put_be16(msg, seq);
+
+ wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+ wpabuf_put_u8(msg, os_strlen(nai));
+ wpabuf_put_str(msg, nai);
+
+ if (erp) {
+ wpabuf_put_u8(msg, erp->cryptosuite);
+ if (hmac_sha256(erp->rIK, erp->rIK_len,
+ wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+ wpabuf_free(msg);
+ return;
+ }
+ wpabuf_put_data(msg, hash, hash_len);
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)",
+ flags & 0x80 ? "failure" : "success");
+
+ sm->lastId = sm->currentId;
+ sm->currentId = id;
+ wpabuf_free(sm->eap_if.eapReqData);
+ sm->eap_if.eapReqData = msg;
+ wpabuf_free(sm->lastReqData);
+ sm->lastReqData = NULL;
+
+ if ((flags & 0x80) || !erp) {
+ sm->eap_if.eapFail = TRUE;
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+ MACSTR, MAC2STR(sm->peer_addr));
+ return;
+ }
+
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+ sm->eap_if.eapKeyDataLen = 0;
+ sm->eap_if.eapKeyData = os_malloc(erp->rRK_len);
+ if (!sm->eap_if.eapKeyData)
+ return;
+
+ 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->eap_if.eapKeyData, erp->rRK_len) < 0) {
+ wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+ bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len);
+ sm->eap_if.eapKeyData = NULL;
+ return;
+ }
+ sm->eap_if.eapKeyDataLen = erp->rRK_len;
+ sm->eap_if.eapKeyAvailable = TRUE;
+ wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+ sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+ sm->eap_if.eapSuccess = TRUE;
+
+ wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+ MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, INITIATE_RECEIVED)
+{
+ const u8 *pos, *end, *start, *tlvs, *hdr;
+ const struct eap_hdr *ehdr;
+ size_t len;
+ u8 flags;
+ u16 seq;
+ char nai[254];
+ struct eap_server_erp_key *erp;
+ int max_len;
+ u8 hash[SHA256_MAC_LEN];
+ size_t hash_len;
+ struct erp_tlvs parse;
+ u8 resp_flags = 0x80; /* default to failure; cleared on success */
+
+ SM_ENTRY(EAP, INITIATE_RECEIVED);
+
+ sm->rxInitiate = FALSE;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+ sm->eap_if.eapRespData, &len);
+ if (pos == NULL) {
+ wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
+ goto fail;
+ }
+ hdr = wpabuf_head(sm->eap_if.eapRespData);
+ ehdr = wpabuf_head(sm->eap_if.eapRespData);
+
+ wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len);
+ if (len < 4) {
+ wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth");
+ goto fail;
+ }
+ end = pos + len;
+
+ flags = *pos++;
+ seq = WPA_GET_BE16(pos);
+ pos += 2;
+ wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+ tlvs = pos;
+
+ /*
+ * 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(tlvs, end, &parse, 1) < 0)
+ goto fail;
+
+ if (!parse.keyname) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet");
+ goto fail;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/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-Initiate/Re-auth");
+ goto fail;
+ }
+ os_memcpy(nai, parse.keyname, parse.keyname_len);
+ nai[parse.keyname_len] = '\0';
+
+ if (!sm->eap_server) {
+ /*
+ * In passthrough case, EAP-Initiate/Re-auth replaces
+ * EAP Identity exchange. Use keyName-NAI as the user identity
+ * and forward EAP-Initiate/Re-auth to the backend
+ * authentication server.
+ */
+ wpa_printf(MSG_DEBUG,
+ "EAP: Use keyName-NAI as user identity for backend authentication");
+ eap_server_clear_identity(sm);
+ sm->identity = (u8 *) dup_binstr(parse.keyname,
+ parse.keyname_len);
+ if (!sm->identity)
+ goto fail;
+ sm->identity_len = parse.keyname_len;
+ return;
+ }
+
+ erp = eap_erp_get_key(sm, nai);
+ if (!erp) {
+ wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+ nai);
+ goto report_error;
+ }
+
+ if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: SEQ=%u replayed (already received SEQ=%u)",
+ seq, erp->recv_seq);
+ goto fail;
+ }
+
+ /* Is there enough room for Cryptosuite and Authentication Tag? */
+ start = parse.keyname + parse.keyname_len;
+ max_len = end - start;
+ if (max_len <
+ 1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Not enough room for Authentication Tag");
+ goto fail;
+ }
+
+ switch (erp->cryptosuite) {
+ case EAP_ERP_CS_HMAC_SHA256_256:
+ if (end[-33] != erp->cryptosuite) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Different Cryptosuite used");
+ goto fail;
+ }
+ hash_len = 32;
+ break;
+ case EAP_ERP_CS_HMAC_SHA256_128:
+ if (end[-17] != erp->cryptosuite) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Different Cryptosuite used");
+ goto fail;
+ }
+ hash_len = 16;
+ break;
+ default:
+ hash_len = 0;
+ break;
+ }
+
+ if (hash_len) {
+ if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+ end - hdr - hash_len, hash) < 0)
+ goto fail;
+ if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Authentication Tag mismatch");
+ goto fail;
+ }
+ }
+
+ /* Check if any supported CS results in matching tag */
+ if (!hash_len && max_len >= 1 + 32 &&
+ end[-33] == EAP_ERP_CS_HMAC_SHA256_256) {
+ if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+ end - hdr - 32, hash) < 0)
+ goto fail;
+ if (os_memcmp(end - 32, hash, 32) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Authentication Tag match using HMAC-SHA256-256");
+ hash_len = 32;
+ erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256;
+ }
+ }
+
+ if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) {
+ if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+ end - hdr - 16, hash) < 0)
+ goto fail;
+ if (os_memcmp(end - 16, hash, 16) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: Authentication Tag match using HMAC-SHA256-128");
+ hash_len = 16;
+ erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128;
+ }
+ }
+
+ if (!hash_len) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: No supported cryptosuite matched Authentication Tag");
+ goto fail;
+ }
+ end -= 1 + hash_len;
+
+ /*
+ * Parse TVs/TLVs again now that we know the exact part of the buffer
+ * that contains them.
+ */
+ wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs",
+ tlvs, end - tlvs);
+ if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u",
+ erp->keyname_nai, seq);
+ erp->recv_seq = seq;
+ resp_flags &= ~0x80; /* R=0 - success */
+
+report_error:
+ erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai);
+ return;
+
+fail:
+ sm->ignore = TRUE;
+}
+
+#endif /* CONFIG_ERP */
+
+
SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
{
SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
wpabuf_free(sm->eap_if.aaaEapRespData);
sm->eap_if.aaaEapRespData = NULL;
+ sm->try_initiate_reauth = FALSE;
}
if (sm->eap_if.aaaEapKeyAvailable) {
EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
} else {
- os_free(sm->eap_if.eapKeyData);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
sm->eap_if.eapKeyData = NULL;
sm->eap_if.eapKeyDataLen = 0;
}
SM_ENTER(EAP, INITIALIZE);
break;
case EAP_IDLE:
- if (sm->eap_if.retransWhile == 0)
- SM_ENTER(EAP, RETRANSMIT);
- else if (sm->eap_if.eapResp)
+ if (sm->eap_if.retransWhile == 0) {
+ if (sm->try_initiate_reauth) {
+ sm->try_initiate_reauth = FALSE;
+ SM_ENTER(EAP, SELECT_ACTION);
+ } else {
+ SM_ENTER(EAP, RETRANSMIT);
+ }
+ } else if (sm->eap_if.eapResp)
SM_ENTER(EAP, RECEIVED);
break;
case EAP_RETRANSMIT:
sm->respVendor == EAP_VENDOR_IETF &&
sm->respVendorMethod == sm->currentMethod)))
SM_ENTER(EAP, INTEGRITY_CHECK);
+#ifdef CONFIG_ERP
+ else if (sm->rxInitiate)
+ SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
else {
wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
"rxResp=%d respId=%d currentId=%d "
"respMethod=%d currentMethod=%d",
sm->rxResp, sm->respId, sm->currentId,
sm->respMethod, sm->currentMethod);
+ eap_log_msg(sm, "Discard received EAP message");
SM_ENTER(EAP, DISCARD);
}
break;
SM_ENTER(EAP, METHOD_RESPONSE);
break;
case EAP_METHOD_REQUEST:
+ if (sm->m == NULL) {
+ /*
+ * This transition is not mentioned in RFC 4137, but it
+ * is needed to handle cleanly a case where EAP method
+ * initialization fails.
+ */
+ SM_ENTER(EAP, FAILURE);
+ break;
+ }
SM_ENTER(EAP, SEND_REQUEST);
+ if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) {
+ /*
+ * This transition is not mentioned in RFC 4137, but it
+ * is needed to handle cleanly a case where EAP method
+ * buildReq fails.
+ */
+ wpa_printf(MSG_DEBUG,
+ "EAP: Method did not return a request");
+ SM_ENTER(EAP, FAILURE);
+ break;
+ }
break;
case EAP_METHOD_RESPONSE:
/*
SM_ENTER(EAP, SUCCESS);
else if (sm->decision == DECISION_PASSTHROUGH)
SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
+ else if (sm->decision == DECISION_INITIATE_REAUTH_START)
+ SM_ENTER(EAP, INITIATE_REAUTH_START);
+#ifdef CONFIG_ERP
+ else if (sm->eap_server && sm->erp && sm->rxInitiate)
+ SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
else
SM_ENTER(EAP, PROPOSE_METHOD);
break;
+ case EAP_INITIATE_REAUTH_START:
+ SM_ENTER(EAP, SEND_REQUEST);
+ break;
+ case EAP_INITIATE_RECEIVED:
+ if (!sm->eap_server)
+ SM_ENTER(EAP, SELECT_ACTION);
+ break;
case EAP_TIMEOUT_FAILURE:
break;
case EAP_FAILURE:
{
int rto, i;
+ if (sm->try_initiate_reauth) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start");
+ return 1;
+ }
+
if (methodTimeout) {
/*
* EAP method (either internal or through AAA server, provided
/* parse rxResp, respId, respMethod */
sm->rxResp = FALSE;
+ sm->rxInitiate = FALSE;
sm->respId = -1;
sm->respMethod = EAP_TYPE_NONE;
sm->respVendor = EAP_VENDOR_IETF;
if (hdr->code == EAP_CODE_RESPONSE)
sm->rxResp = TRUE;
+ else if (hdr->code == EAP_CODE_INITIATE)
+ sm->rxInitiate = TRUE;
if (plen > sizeof(*hdr)) {
u8 *pos = (u8 *) (hdr + 1);
}
}
- wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
- "respMethod=%u respVendor=%u respVendorMethod=%u",
- sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
- sm->respVendorMethod);
+ wpa_printf(MSG_DEBUG,
+ "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u",
+ sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod,
+ sm->respVendor, sm->respVendorMethod);
}
not_found:
/* not found - remove from the list */
- os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
- (EAP_MAX_METHODS - i - 1) *
- sizeof(sm->user->methods[0]));
+ if (i + 1 < EAP_MAX_METHODS) {
+ os_memmove(&sm->user->methods[i],
+ &sm->user->methods[i + 1],
+ (EAP_MAX_METHODS - i - 1) *
+ sizeof(sm->user->methods[0]));
+ }
sm->user->methods[EAP_MAX_METHODS - 1].vendor =
EAP_VENDOR_IETF;
sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
return DECISION_CONTINUE;
}
+ if (!sm->identity && eap_get_erp_send_reauth_start(sm) &&
+ !sm->initiate_reauth_start_sent) {
+ wpa_printf(MSG_DEBUG,
+ "EAP: getDecision: send EAP-Initiate/Re-auth-Start");
+ return DECISION_INITIATE_REAUTH_START;
+ }
+
if (sm->identity == NULL || sm->currentId == -1) {
wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
"yet -> CONTINUE");
{
if (user == NULL)
return;
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
user->password = NULL;
os_free(user);
}
* This function allocates and initializes an EAP state machine.
*/
struct eap_sm * eap_server_sm_init(void *eapol_ctx,
- struct eapol_callbacks *eapol_cb,
+ const struct eapol_callbacks *eapol_cb,
struct eap_config *conf)
{
struct eap_sm *sm;
os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
sm->fragment_size = conf->fragment_size;
sm->pwd_group = conf->pwd_group;
+ sm->pbc_in_m1 = conf->pbc_in_m1;
+ sm->server_id = conf->server_id;
+ sm->server_id_len = conf->server_id_len;
+ sm->erp = conf->erp;
+ sm->tls_session_lifetime = conf->tls_session_lifetime;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ sm->tls_test_flags = conf->tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
if (sm->m && sm->eap_method_priv)
sm->m->reset(sm, sm->eap_method_priv);
wpabuf_free(sm->eap_if.eapReqData);
- os_free(sm->eap_if.eapKeyData);
+ bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+ os_free(sm->eap_if.eapSessionId);
wpabuf_free(sm->lastReqData);
wpabuf_free(sm->eap_if.eapRespData);
os_free(sm->identity);
os_free(sm->eap_fast_a_id_info);
wpabuf_free(sm->eap_if.aaaEapReqData);
wpabuf_free(sm->eap_if.aaaEapRespData);
- os_free(sm->eap_if.aaaEapKeyData);
+ bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen);
eap_user_free(sm->user);
wpabuf_free(sm->assoc_wps_ie);
wpabuf_free(sm->assoc_p2p_ie);
{
return &sm->eap_if;
}
+
+
+/**
+ * eap_server_clear_identity - Clear EAP identity information
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function can be used to clear the EAP identity information in the EAP
+ * server context. This allows the EAP/Identity method to be used again after
+ * EAPOL-Start or EAPOL-Logoff.
+ */
+void eap_server_clear_identity(struct eap_sm *sm)
+{
+ os_free(sm->identity);
+ sm->identity = NULL;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
+ const u8 *username, size_t username_len,
+ const u8 *challenge, const u8 *response)
+{
+ char hex_challenge[30], hex_response[90], user[100];
+
+ /* Print out Challenge and Response in format supported by asleap. */
+ if (username)
+ printf_encode(user, sizeof(user), username, username_len);
+ else
+ user[0] = '\0';
+ wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge),
+ challenge, sizeof(challenge), ':');
+ wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24,
+ ':');
+ wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s",
+ source, user, hex_challenge, hex_response);
+}
+#endif /* CONFIG_TESTING_OPTIONS */