xref: /netbsd-src/external/bsd/wpa/dist/src/eap_server/eap_server.c (revision 45d3cc13f775755ee348416d64536fb30df46e06)
18dbcf02cSchristos /*
28dbcf02cSchristos  * hostapd / EAP Full Authenticator state machine (RFC 4137)
3bb610346Schristos  * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
48dbcf02cSchristos  *
5e604d861Schristos  * This software may be distributed under the terms of the BSD license.
6e604d861Schristos  * See README for more details.
78dbcf02cSchristos  *
88dbcf02cSchristos  * This state machine is based on the full authenticator state machine defined
98dbcf02cSchristos  * in RFC 4137. However, to support backend authentication in RADIUS
108dbcf02cSchristos  * authentication server functionality, parts of backend authenticator (also
118dbcf02cSchristos  * from RFC 4137) are mixed in. This functionality is enabled by setting
12*45d3cc13Schristos  * backend_auth configuration variable to true.
138dbcf02cSchristos  */
148dbcf02cSchristos 
158dbcf02cSchristos #include "includes.h"
168dbcf02cSchristos 
178dbcf02cSchristos #include "common.h"
18bb610346Schristos #include "crypto/sha256.h"
198dbcf02cSchristos #include "eap_i.h"
208dbcf02cSchristos #include "state_machine.h"
218dbcf02cSchristos #include "common/wpa_ctrl.h"
228dbcf02cSchristos 
238dbcf02cSchristos #define STATE_MACHINE_DATA struct eap_sm
248dbcf02cSchristos #define STATE_MACHINE_DEBUG_PREFIX "EAP"
258dbcf02cSchristos 
268dbcf02cSchristos /* EAP state machines are described in RFC 4137 */
278dbcf02cSchristos 
288dbcf02cSchristos static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
298dbcf02cSchristos 				   int eapSRTT, int eapRTTVAR,
308dbcf02cSchristos 				   int methodTimeout);
318dbcf02cSchristos static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
328dbcf02cSchristos static int eap_sm_getId(const struct wpabuf *data);
338dbcf02cSchristos static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
348dbcf02cSchristos static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
358dbcf02cSchristos static int eap_sm_nextId(struct eap_sm *sm, int id);
368dbcf02cSchristos static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
378dbcf02cSchristos 				 size_t len);
38*45d3cc13Schristos static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm,
39*45d3cc13Schristos 						 int *vendor);
408dbcf02cSchristos static int eap_sm_Policy_getDecision(struct eap_sm *sm);
41*45d3cc13Schristos static bool eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method);
428dbcf02cSchristos 
438dbcf02cSchristos 
44bb610346Schristos static int eap_get_erp_send_reauth_start(struct eap_sm *sm)
45bb610346Schristos {
46bb610346Schristos 	if (sm->eapol_cb->get_erp_send_reauth_start)
47bb610346Schristos 		return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx);
48bb610346Schristos 	return 0;
49bb610346Schristos }
50bb610346Schristos 
51bb610346Schristos 
52bb610346Schristos static const char * eap_get_erp_domain(struct eap_sm *sm)
53bb610346Schristos {
54bb610346Schristos 	if (sm->eapol_cb->get_erp_domain)
55bb610346Schristos 		return sm->eapol_cb->get_erp_domain(sm->eapol_ctx);
56bb610346Schristos 	return NULL;
57bb610346Schristos }
58bb610346Schristos 
59bb610346Schristos 
60bb610346Schristos #ifdef CONFIG_ERP
61bb610346Schristos 
62bb610346Schristos static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm,
63bb610346Schristos 						   const char *keyname)
64bb610346Schristos {
65bb610346Schristos 	if (sm->eapol_cb->erp_get_key)
66bb610346Schristos 		return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname);
67bb610346Schristos 	return NULL;
68bb610346Schristos }
69bb610346Schristos 
70bb610346Schristos 
71bb610346Schristos static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp)
72bb610346Schristos {
73bb610346Schristos 	if (sm->eapol_cb->erp_add_key)
74bb610346Schristos 		return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp);
75bb610346Schristos 	return -1;
76bb610346Schristos }
77bb610346Schristos 
78bb610346Schristos #endif /* CONFIG_ERP */
79bb610346Schristos 
80bb610346Schristos 
81bb610346Schristos static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
82bb610346Schristos 						       u8 id)
83bb610346Schristos {
84bb610346Schristos 	const char *domain;
85bb610346Schristos 	size_t plen = 1;
86bb610346Schristos 	struct wpabuf *msg;
87bb610346Schristos 	size_t domain_len = 0;
88bb610346Schristos 
89bb610346Schristos 	domain = eap_get_erp_domain(sm);
90bb610346Schristos 	if (domain) {
91bb610346Schristos 		domain_len = os_strlen(domain);
92bb610346Schristos 		plen += 2 + domain_len;
93bb610346Schristos 	}
94bb610346Schristos 
95ecc36642Schristos 	msg = eap_msg_alloc(EAP_VENDOR_IETF,
96*45d3cc13Schristos 			    (enum eap_type) EAP_ERP_TYPE_REAUTH_START, plen,
97bb610346Schristos 			    EAP_CODE_INITIATE, id);
98bb610346Schristos 	if (msg == NULL)
99bb610346Schristos 		return NULL;
100bb610346Schristos 	wpabuf_put_u8(msg, 0); /* Reserved */
101bb610346Schristos 	if (domain) {
102bb610346Schristos 		/* Domain name TLV */
103bb610346Schristos 		wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME);
104bb610346Schristos 		wpabuf_put_u8(msg, domain_len);
105bb610346Schristos 		wpabuf_put_data(msg, domain, domain_len);
106bb610346Schristos 	}
107bb610346Schristos 
108bb610346Schristos 	return msg;
109bb610346Schristos }
110bb610346Schristos 
111bb610346Schristos 
1128dbcf02cSchristos static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
1138dbcf02cSchristos {
1148dbcf02cSchristos 	if (src == NULL)
1158dbcf02cSchristos 		return -1;
1168dbcf02cSchristos 
1178dbcf02cSchristos 	wpabuf_free(*dst);
1188dbcf02cSchristos 	*dst = wpabuf_dup(src);
1198dbcf02cSchristos 	return *dst ? 0 : -1;
1208dbcf02cSchristos }
1218dbcf02cSchristos 
1228dbcf02cSchristos 
1238dbcf02cSchristos static int eap_copy_data(u8 **dst, size_t *dst_len,
1248dbcf02cSchristos 			 const u8 *src, size_t src_len)
1258dbcf02cSchristos {
1268dbcf02cSchristos 	if (src == NULL)
1278dbcf02cSchristos 		return -1;
1288dbcf02cSchristos 
1298dbcf02cSchristos 	os_free(*dst);
1308dbcf02cSchristos 	*dst = os_malloc(src_len);
1318dbcf02cSchristos 	if (*dst) {
1328dbcf02cSchristos 		os_memcpy(*dst, src, src_len);
1338dbcf02cSchristos 		*dst_len = src_len;
1348dbcf02cSchristos 		return 0;
1358dbcf02cSchristos 	} else {
1368dbcf02cSchristos 		*dst_len = 0;
1378dbcf02cSchristos 		return -1;
1388dbcf02cSchristos 	}
1398dbcf02cSchristos }
1408dbcf02cSchristos 
1418dbcf02cSchristos #define EAP_COPY(dst, src) \
1428dbcf02cSchristos 	eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
1438dbcf02cSchristos 
1448dbcf02cSchristos 
1458dbcf02cSchristos /**
1468dbcf02cSchristos  * eap_user_get - Fetch user information from the database
1478dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1488dbcf02cSchristos  * @identity: Identity (User-Name) of the user
1498dbcf02cSchristos  * @identity_len: Length of identity in bytes
1508dbcf02cSchristos  * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
1518dbcf02cSchristos  * Returns: 0 on success, or -1 on failure
1528dbcf02cSchristos  *
1538dbcf02cSchristos  * This function is used to fetch user information for EAP. The user will be
1548dbcf02cSchristos  * selected based on the specified identity. sm->user and
1558dbcf02cSchristos  * sm->user_eap_method_index are updated for the new user when a matching user
1568dbcf02cSchristos  * is found. sm->user can be used to get user information (e.g., password).
1578dbcf02cSchristos  */
1588dbcf02cSchristos int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
1598dbcf02cSchristos 		 int phase2)
1608dbcf02cSchristos {
1618dbcf02cSchristos 	struct eap_user *user;
1628dbcf02cSchristos 
1638dbcf02cSchristos 	if (sm == NULL || sm->eapol_cb == NULL ||
1648dbcf02cSchristos 	    sm->eapol_cb->get_eap_user == NULL)
1658dbcf02cSchristos 		return -1;
1668dbcf02cSchristos 
1678dbcf02cSchristos 	eap_user_free(sm->user);
1688dbcf02cSchristos 	sm->user = NULL;
1698dbcf02cSchristos 
1708dbcf02cSchristos 	user = os_zalloc(sizeof(*user));
1718dbcf02cSchristos 	if (user == NULL)
1728dbcf02cSchristos 	    return -1;
1738dbcf02cSchristos 
1748dbcf02cSchristos 	if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
1758dbcf02cSchristos 				       identity_len, phase2, user) != 0) {
1768dbcf02cSchristos 		eap_user_free(user);
1778dbcf02cSchristos 		return -1;
1788dbcf02cSchristos 	}
1798dbcf02cSchristos 
1808dbcf02cSchristos 	sm->user = user;
1818dbcf02cSchristos 	sm->user_eap_method_index = 0;
1828dbcf02cSchristos 
1838dbcf02cSchristos 	return 0;
1848dbcf02cSchristos }
1858dbcf02cSchristos 
1868dbcf02cSchristos 
1873c260e60Schristos void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
1883c260e60Schristos {
1893c260e60Schristos 	va_list ap;
1903c260e60Schristos 	char *buf;
1913c260e60Schristos 	int buflen;
1923c260e60Schristos 
1933c260e60Schristos 	if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL)
1943c260e60Schristos 		return;
1953c260e60Schristos 
1963c260e60Schristos 	va_start(ap, fmt);
1973c260e60Schristos 	buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
1983c260e60Schristos 	va_end(ap);
1993c260e60Schristos 
2003c260e60Schristos 	buf = os_malloc(buflen);
2013c260e60Schristos 	if (buf == NULL)
2023c260e60Schristos 		return;
2033c260e60Schristos 	va_start(ap, fmt);
2043c260e60Schristos 	vsnprintf(buf, buflen, fmt, ap);
2053c260e60Schristos 	va_end(ap);
2063c260e60Schristos 
2073c260e60Schristos 	sm->eapol_cb->log_msg(sm->eapol_ctx, buf);
2083c260e60Schristos 
2093c260e60Schristos 	os_free(buf);
2103c260e60Schristos }
2113c260e60Schristos 
2123c260e60Schristos 
2138dbcf02cSchristos SM_STATE(EAP, DISABLED)
2148dbcf02cSchristos {
2158dbcf02cSchristos 	SM_ENTRY(EAP, DISABLED);
2168dbcf02cSchristos 	sm->num_rounds = 0;
217*45d3cc13Schristos 	sm->num_rounds_short = 0;
2188dbcf02cSchristos }
2198dbcf02cSchristos 
2208dbcf02cSchristos 
2218dbcf02cSchristos SM_STATE(EAP, INITIALIZE)
2228dbcf02cSchristos {
2238dbcf02cSchristos 	SM_ENTRY(EAP, INITIALIZE);
2248dbcf02cSchristos 
225*45d3cc13Schristos 	if (sm->eap_if.eapRestart && !sm->cfg->eap_server && sm->identity) {
226111b9fd8Schristos 		/*
227111b9fd8Schristos 		 * Need to allow internal Identity method to be used instead
228111b9fd8Schristos 		 * of passthrough at the beginning of reauthentication.
229111b9fd8Schristos 		 */
230111b9fd8Schristos 		eap_server_clear_identity(sm);
231111b9fd8Schristos 	}
232111b9fd8Schristos 
233*45d3cc13Schristos 	sm->try_initiate_reauth = false;
2348dbcf02cSchristos 	sm->currentId = -1;
235*45d3cc13Schristos 	sm->eap_if.eapSuccess = false;
236*45d3cc13Schristos 	sm->eap_if.eapFail = false;
237*45d3cc13Schristos 	sm->eap_if.eapTimeout = false;
2383c260e60Schristos 	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
2398dbcf02cSchristos 	sm->eap_if.eapKeyData = NULL;
2408dbcf02cSchristos 	sm->eap_if.eapKeyDataLen = 0;
241bb610346Schristos 	os_free(sm->eap_if.eapSessionId);
242bb610346Schristos 	sm->eap_if.eapSessionId = NULL;
243bb610346Schristos 	sm->eap_if.eapSessionIdLen = 0;
244*45d3cc13Schristos 	sm->eap_if.eapKeyAvailable = false;
245*45d3cc13Schristos 	sm->eap_if.eapRestart = false;
2468dbcf02cSchristos 
2478dbcf02cSchristos 	/*
2488dbcf02cSchristos 	 * This is not defined in RFC 4137, but method state needs to be
2498dbcf02cSchristos 	 * reseted here so that it does not remain in success state when
2508dbcf02cSchristos 	 * re-authentication starts.
2518dbcf02cSchristos 	 */
2528dbcf02cSchristos 	if (sm->m && sm->eap_method_priv) {
2538dbcf02cSchristos 		sm->m->reset(sm, sm->eap_method_priv);
2548dbcf02cSchristos 		sm->eap_method_priv = NULL;
2558dbcf02cSchristos 	}
2568dbcf02cSchristos 	sm->m = NULL;
2578dbcf02cSchristos 	sm->user_eap_method_index = 0;
2588dbcf02cSchristos 
259*45d3cc13Schristos 	if (sm->cfg->backend_auth) {
2608dbcf02cSchristos 		sm->currentMethod = EAP_TYPE_NONE;
2618dbcf02cSchristos 		/* parse rxResp, respId, respMethod */
2628dbcf02cSchristos 		eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
2638dbcf02cSchristos 		if (sm->rxResp) {
2648dbcf02cSchristos 			sm->currentId = sm->respId;
2658dbcf02cSchristos 		}
2668dbcf02cSchristos 	}
2678dbcf02cSchristos 	sm->num_rounds = 0;
268*45d3cc13Schristos 	sm->num_rounds_short = 0;
2698dbcf02cSchristos 	sm->method_pending = METHOD_PENDING_NONE;
2708dbcf02cSchristos 
271*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
2728dbcf02cSchristos 		MACSTR, MAC2STR(sm->peer_addr));
2738dbcf02cSchristos }
2748dbcf02cSchristos 
2758dbcf02cSchristos 
2768dbcf02cSchristos SM_STATE(EAP, PICK_UP_METHOD)
2778dbcf02cSchristos {
2788dbcf02cSchristos 	SM_ENTRY(EAP, PICK_UP_METHOD);
2798dbcf02cSchristos 
2808dbcf02cSchristos 	if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
2818dbcf02cSchristos 		sm->currentMethod = sm->respMethod;
2828dbcf02cSchristos 		if (sm->m && sm->eap_method_priv) {
2838dbcf02cSchristos 			sm->m->reset(sm, sm->eap_method_priv);
2848dbcf02cSchristos 			sm->eap_method_priv = NULL;
2858dbcf02cSchristos 		}
2868dbcf02cSchristos 		sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
2878dbcf02cSchristos 						  sm->currentMethod);
2888dbcf02cSchristos 		if (sm->m && sm->m->initPickUp) {
2898dbcf02cSchristos 			sm->eap_method_priv = sm->m->initPickUp(sm);
2908dbcf02cSchristos 			if (sm->eap_method_priv == NULL) {
2918dbcf02cSchristos 				wpa_printf(MSG_DEBUG, "EAP: Failed to "
2928dbcf02cSchristos 					   "initialize EAP method %d",
2938dbcf02cSchristos 					   sm->currentMethod);
2948dbcf02cSchristos 				sm->m = NULL;
2958dbcf02cSchristos 				sm->currentMethod = EAP_TYPE_NONE;
2968dbcf02cSchristos 			}
2978dbcf02cSchristos 		} else {
2988dbcf02cSchristos 			sm->m = NULL;
2998dbcf02cSchristos 			sm->currentMethod = EAP_TYPE_NONE;
3008dbcf02cSchristos 		}
3018dbcf02cSchristos 	}
3028dbcf02cSchristos 
303*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
3048dbcf02cSchristos 		"method=%u", sm->currentMethod);
3058dbcf02cSchristos }
3068dbcf02cSchristos 
3078dbcf02cSchristos 
3088dbcf02cSchristos SM_STATE(EAP, IDLE)
3098dbcf02cSchristos {
3108dbcf02cSchristos 	SM_ENTRY(EAP, IDLE);
3118dbcf02cSchristos 
3128dbcf02cSchristos 	sm->eap_if.retransWhile = eap_sm_calculateTimeout(
3138dbcf02cSchristos 		sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
3148dbcf02cSchristos 		sm->methodTimeout);
3158dbcf02cSchristos }
3168dbcf02cSchristos 
3178dbcf02cSchristos 
3188dbcf02cSchristos SM_STATE(EAP, RETRANSMIT)
3198dbcf02cSchristos {
3208dbcf02cSchristos 	SM_ENTRY(EAP, RETRANSMIT);
3218dbcf02cSchristos 
3228dbcf02cSchristos 	sm->retransCount++;
3238dbcf02cSchristos 	if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
3248dbcf02cSchristos 		if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
325*45d3cc13Schristos 			sm->eap_if.eapReq = true;
3268dbcf02cSchristos 	}
327be6b3c4dSchristos 
328*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR,
329be6b3c4dSchristos 		MAC2STR(sm->peer_addr));
3308dbcf02cSchristos }
3318dbcf02cSchristos 
3328dbcf02cSchristos 
3338dbcf02cSchristos SM_STATE(EAP, RECEIVED)
3348dbcf02cSchristos {
3358dbcf02cSchristos 	SM_ENTRY(EAP, RECEIVED);
3368dbcf02cSchristos 
3378dbcf02cSchristos 	/* parse rxResp, respId, respMethod */
3388dbcf02cSchristos 	eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
3398dbcf02cSchristos 	sm->num_rounds++;
340*45d3cc13Schristos 	if (!sm->eap_if.eapRespData || wpabuf_len(sm->eap_if.eapRespData) < 20)
341*45d3cc13Schristos 		sm->num_rounds_short++;
342*45d3cc13Schristos 	else
343*45d3cc13Schristos 		sm->num_rounds_short = 0;
3448dbcf02cSchristos }
3458dbcf02cSchristos 
3468dbcf02cSchristos 
3478dbcf02cSchristos SM_STATE(EAP, DISCARD)
3488dbcf02cSchristos {
3498dbcf02cSchristos 	SM_ENTRY(EAP, DISCARD);
350*45d3cc13Schristos 	sm->eap_if.eapResp = false;
351*45d3cc13Schristos 	sm->eap_if.eapNoReq = true;
3528dbcf02cSchristos }
3538dbcf02cSchristos 
3548dbcf02cSchristos 
3558dbcf02cSchristos SM_STATE(EAP, SEND_REQUEST)
3568dbcf02cSchristos {
3578dbcf02cSchristos 	SM_ENTRY(EAP, SEND_REQUEST);
3588dbcf02cSchristos 
3598dbcf02cSchristos 	sm->retransCount = 0;
3608dbcf02cSchristos 	if (sm->eap_if.eapReqData) {
361*45d3cc13Schristos 		if (wpabuf_len(sm->eap_if.eapReqData) >= 20)
362*45d3cc13Schristos 			sm->num_rounds_short = 0;
3638dbcf02cSchristos 		if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
3648dbcf02cSchristos 		{
365*45d3cc13Schristos 			sm->eap_if.eapResp = false;
366*45d3cc13Schristos 			sm->eap_if.eapReq = true;
3678dbcf02cSchristos 		} else {
368*45d3cc13Schristos 			sm->eap_if.eapResp = false;
369*45d3cc13Schristos 			sm->eap_if.eapReq = false;
3708dbcf02cSchristos 		}
3718dbcf02cSchristos 	} else {
3728dbcf02cSchristos 		wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
373*45d3cc13Schristos 		sm->eap_if.eapResp = false;
374*45d3cc13Schristos 		sm->eap_if.eapReq = false;
375*45d3cc13Schristos 		sm->eap_if.eapNoReq = true;
3768dbcf02cSchristos 	}
3778dbcf02cSchristos }
3788dbcf02cSchristos 
3798dbcf02cSchristos 
3808dbcf02cSchristos SM_STATE(EAP, INTEGRITY_CHECK)
3818dbcf02cSchristos {
3828dbcf02cSchristos 	SM_ENTRY(EAP, INTEGRITY_CHECK);
3838dbcf02cSchristos 
384e604d861Schristos 	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
385*45d3cc13Schristos 		sm->ignore = true;
386e604d861Schristos 		return;
387e604d861Schristos 	}
388e604d861Schristos 
3898dbcf02cSchristos 	if (sm->m->check) {
3908dbcf02cSchristos 		sm->ignore = sm->m->check(sm, sm->eap_method_priv,
3918dbcf02cSchristos 					  sm->eap_if.eapRespData);
3928dbcf02cSchristos 	}
3938dbcf02cSchristos }
3948dbcf02cSchristos 
3958dbcf02cSchristos 
3968dbcf02cSchristos SM_STATE(EAP, METHOD_REQUEST)
3978dbcf02cSchristos {
3988dbcf02cSchristos 	SM_ENTRY(EAP, METHOD_REQUEST);
3998dbcf02cSchristos 
4008dbcf02cSchristos 	if (sm->m == NULL) {
4018dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: method not initialized");
4028dbcf02cSchristos 		return;
4038dbcf02cSchristos 	}
4048dbcf02cSchristos 
4058dbcf02cSchristos 	sm->currentId = eap_sm_nextId(sm, sm->currentId);
4068dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
4078dbcf02cSchristos 		   sm->currentId);
4088dbcf02cSchristos 	sm->lastId = sm->currentId;
4098dbcf02cSchristos 	wpabuf_free(sm->eap_if.eapReqData);
4108dbcf02cSchristos 	sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
4118dbcf02cSchristos 						sm->currentId);
4128dbcf02cSchristos 	if (sm->m->getTimeout)
4138dbcf02cSchristos 		sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
4148dbcf02cSchristos 	else
4158dbcf02cSchristos 		sm->methodTimeout = 0;
4168dbcf02cSchristos }
4178dbcf02cSchristos 
4188dbcf02cSchristos 
419bb610346Schristos static void eap_server_erp_init(struct eap_sm *sm)
420bb610346Schristos {
421bb610346Schristos #ifdef CONFIG_ERP
422bb610346Schristos 	u8 *emsk = NULL;
423bb610346Schristos 	size_t emsk_len = 0;
424bb610346Schristos 	u8 EMSKname[EAP_EMSK_NAME_LEN];
425be6b3c4dSchristos 	u8 len[2], ctx[3];
426bb610346Schristos 	const char *domain;
427bb610346Schristos 	size_t domain_len, nai_buf_len;
428bb610346Schristos 	struct eap_server_erp_key *erp = NULL;
429bb610346Schristos 	int pos;
430bb610346Schristos 
431bb610346Schristos 	domain = eap_get_erp_domain(sm);
432bb610346Schristos 	if (!domain)
433bb610346Schristos 		return;
434bb610346Schristos 
435bb610346Schristos 	domain_len = os_strlen(domain);
436bb610346Schristos 
437bb610346Schristos 	nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len;
438bb610346Schristos 	if (nai_buf_len > 253) {
439bb610346Schristos 		/*
440bb610346Schristos 		 * keyName-NAI has a maximum length of 253 octet to fit in
441bb610346Schristos 		 * RADIUS attributes.
442bb610346Schristos 		 */
443bb610346Schristos 		wpa_printf(MSG_DEBUG,
444bb610346Schristos 			   "EAP: Too long realm for ERP keyName-NAI maximum length");
445bb610346Schristos 		return;
446bb610346Schristos 	}
447bb610346Schristos 	nai_buf_len++; /* null termination */
448bb610346Schristos 	erp = os_zalloc(sizeof(*erp) + nai_buf_len);
449bb610346Schristos 	if (erp == NULL)
450bb610346Schristos 		goto fail;
451bb610346Schristos 	erp->recv_seq = (u32) -1;
452bb610346Schristos 
453bb610346Schristos 	emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
454bb610346Schristos 	if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
455bb610346Schristos 		wpa_printf(MSG_DEBUG,
456bb610346Schristos 			   "EAP: No suitable EMSK available for ERP");
457bb610346Schristos 		goto fail;
458bb610346Schristos 	}
459bb610346Schristos 
460bb610346Schristos 	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
461bb610346Schristos 
462be6b3c4dSchristos 	WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN);
463bb610346Schristos 	if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen,
464bb610346Schristos 			    "EMSK", len, sizeof(len),
465bb610346Schristos 			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
466bb610346Schristos 		wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
467bb610346Schristos 		goto fail;
468bb610346Schristos 	}
469bb610346Schristos 	wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
470bb610346Schristos 
471bb610346Schristos 	pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
472bb610346Schristos 			       EMSKname, EAP_EMSK_NAME_LEN);
473bb610346Schristos 	erp->keyname_nai[pos] = '@';
474bb610346Schristos 	os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len);
475bb610346Schristos 
476bb610346Schristos 	WPA_PUT_BE16(len, emsk_len);
477bb610346Schristos 	if (hmac_sha256_kdf(emsk, emsk_len,
478bb610346Schristos 			    "EAP Re-authentication Root Key@ietf.org",
479bb610346Schristos 			    len, sizeof(len), erp->rRK, emsk_len) < 0) {
480bb610346Schristos 		wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
481bb610346Schristos 		goto fail;
482bb610346Schristos 	}
483bb610346Schristos 	erp->rRK_len = emsk_len;
484bb610346Schristos 	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
485bb610346Schristos 
486be6b3c4dSchristos 	ctx[0] = EAP_ERP_CS_HMAC_SHA256_128;
487be6b3c4dSchristos 	WPA_PUT_BE16(&ctx[1], erp->rRK_len);
488bb610346Schristos 	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
489be6b3c4dSchristos 			    "Re-authentication Integrity Key@ietf.org",
490be6b3c4dSchristos 			    ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) {
491bb610346Schristos 		wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
492bb610346Schristos 		goto fail;
493bb610346Schristos 	}
494bb610346Schristos 	erp->rIK_len = erp->rRK_len;
495bb610346Schristos 	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
496bb610346Schristos 
497bb610346Schristos 	if (eap_erp_add_key(sm, erp) == 0) {
498bb610346Schristos 		wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s",
499bb610346Schristos 			   erp->keyname_nai);
500bb610346Schristos 		erp = NULL;
501bb610346Schristos 	}
502bb610346Schristos 
503bb610346Schristos fail:
504bb610346Schristos 	bin_clear_free(emsk, emsk_len);
505bb610346Schristos 	bin_clear_free(erp, sizeof(*erp));
506bb610346Schristos #endif /* CONFIG_ERP */
507bb610346Schristos }
508bb610346Schristos 
509bb610346Schristos 
5108dbcf02cSchristos SM_STATE(EAP, METHOD_RESPONSE)
5118dbcf02cSchristos {
5128dbcf02cSchristos 	SM_ENTRY(EAP, METHOD_RESPONSE);
5138dbcf02cSchristos 
514e604d861Schristos 	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
515e604d861Schristos 		return;
516e604d861Schristos 
5178dbcf02cSchristos 	sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
5188dbcf02cSchristos 	if (sm->m->isDone(sm, sm->eap_method_priv)) {
5198dbcf02cSchristos 		eap_sm_Policy_update(sm, NULL, 0);
5203c260e60Schristos 		bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
5218dbcf02cSchristos 		if (sm->m->getKey) {
5228dbcf02cSchristos 			sm->eap_if.eapKeyData = sm->m->getKey(
5238dbcf02cSchristos 				sm, sm->eap_method_priv,
5248dbcf02cSchristos 				&sm->eap_if.eapKeyDataLen);
5258dbcf02cSchristos 		} else {
5268dbcf02cSchristos 			sm->eap_if.eapKeyData = NULL;
5278dbcf02cSchristos 			sm->eap_if.eapKeyDataLen = 0;
5288dbcf02cSchristos 		}
529bb610346Schristos 		os_free(sm->eap_if.eapSessionId);
530bb610346Schristos 		sm->eap_if.eapSessionId = NULL;
531bb610346Schristos 		if (sm->m->getSessionId) {
532bb610346Schristos 			sm->eap_if.eapSessionId = sm->m->getSessionId(
533bb610346Schristos 				sm, sm->eap_method_priv,
534bb610346Schristos 				&sm->eap_if.eapSessionIdLen);
535bb610346Schristos 			wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
536bb610346Schristos 				    sm->eap_if.eapSessionId,
537bb610346Schristos 				    sm->eap_if.eapSessionIdLen);
538bb610346Schristos 		}
539*45d3cc13Schristos 		if (sm->cfg->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
540bb610346Schristos 			eap_server_erp_init(sm);
5418dbcf02cSchristos 		sm->methodState = METHOD_END;
5428dbcf02cSchristos 	} else {
5438dbcf02cSchristos 		sm->methodState = METHOD_CONTINUE;
5448dbcf02cSchristos 	}
5458dbcf02cSchristos }
5468dbcf02cSchristos 
5478dbcf02cSchristos 
5488dbcf02cSchristos SM_STATE(EAP, PROPOSE_METHOD)
5498dbcf02cSchristos {
5508dbcf02cSchristos 	int vendor;
551*45d3cc13Schristos 	enum eap_type type;
5528dbcf02cSchristos 
5538dbcf02cSchristos 	SM_ENTRY(EAP, PROPOSE_METHOD);
5548dbcf02cSchristos 
555*45d3cc13Schristos 	sm->try_initiate_reauth = false;
5563c260e60Schristos try_another_method:
5578dbcf02cSchristos 	type = eap_sm_Policy_getNextMethod(sm, &vendor);
5588dbcf02cSchristos 	if (vendor == EAP_VENDOR_IETF)
5598dbcf02cSchristos 		sm->currentMethod = type;
5608dbcf02cSchristos 	else
5618dbcf02cSchristos 		sm->currentMethod = EAP_TYPE_EXPANDED;
5628dbcf02cSchristos 	if (sm->m && sm->eap_method_priv) {
5638dbcf02cSchristos 		sm->m->reset(sm, sm->eap_method_priv);
5648dbcf02cSchristos 		sm->eap_method_priv = NULL;
5658dbcf02cSchristos 	}
5668dbcf02cSchristos 	sm->m = eap_server_get_eap_method(vendor, type);
5678dbcf02cSchristos 	if (sm->m) {
5688dbcf02cSchristos 		sm->eap_method_priv = sm->m->init(sm);
5698dbcf02cSchristos 		if (sm->eap_method_priv == NULL) {
5708dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
5718dbcf02cSchristos 				   "method %d", sm->currentMethod);
5728dbcf02cSchristos 			sm->m = NULL;
5738dbcf02cSchristos 			sm->currentMethod = EAP_TYPE_NONE;
5743c260e60Schristos 			goto try_another_method;
5758dbcf02cSchristos 		}
5768dbcf02cSchristos 	}
5773c260e60Schristos 	if (sm->m == NULL) {
5783c260e60Schristos 		wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method");
5793c260e60Schristos 		eap_log_msg(sm, "Could not find suitable EAP method");
5803c260e60Schristos 		sm->decision = DECISION_FAILURE;
5813c260e60Schristos 		return;
5823c260e60Schristos 	}
5838dbcf02cSchristos 	if (sm->currentMethod == EAP_TYPE_IDENTITY ||
5848dbcf02cSchristos 	    sm->currentMethod == EAP_TYPE_NOTIFICATION)
5858dbcf02cSchristos 		sm->methodState = METHOD_CONTINUE;
5868dbcf02cSchristos 	else
5878dbcf02cSchristos 		sm->methodState = METHOD_PROPOSED;
5888dbcf02cSchristos 
589*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
5908dbcf02cSchristos 		"vendor=%u method=%u", vendor, sm->currentMethod);
5913c260e60Schristos 	eap_log_msg(sm, "Propose EAP method vendor=%u method=%u",
5923c260e60Schristos 		    vendor, sm->currentMethod);
5938dbcf02cSchristos }
5948dbcf02cSchristos 
5958dbcf02cSchristos 
5968dbcf02cSchristos SM_STATE(EAP, NAK)
5978dbcf02cSchristos {
5988dbcf02cSchristos 	const struct eap_hdr *nak;
5998dbcf02cSchristos 	size_t len = 0;
6008dbcf02cSchristos 	const u8 *pos;
6018dbcf02cSchristos 	const u8 *nak_list = NULL;
6028dbcf02cSchristos 
6038dbcf02cSchristos 	SM_ENTRY(EAP, NAK);
6048dbcf02cSchristos 
6058dbcf02cSchristos 	if (sm->eap_method_priv) {
6068dbcf02cSchristos 		sm->m->reset(sm, sm->eap_method_priv);
6078dbcf02cSchristos 		sm->eap_method_priv = NULL;
6088dbcf02cSchristos 	}
6098dbcf02cSchristos 	sm->m = NULL;
6108dbcf02cSchristos 
611e604d861Schristos 	if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
612e604d861Schristos 		return;
613e604d861Schristos 
6148dbcf02cSchristos 	nak = wpabuf_head(sm->eap_if.eapRespData);
6158dbcf02cSchristos 	if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
6168dbcf02cSchristos 		len = be_to_host16(nak->length);
6178dbcf02cSchristos 		if (len > wpabuf_len(sm->eap_if.eapRespData))
6188dbcf02cSchristos 			len = wpabuf_len(sm->eap_if.eapRespData);
6198dbcf02cSchristos 		pos = (const u8 *) (nak + 1);
6208dbcf02cSchristos 		len -= sizeof(*nak);
6218dbcf02cSchristos 		if (*pos == EAP_TYPE_NAK) {
6228dbcf02cSchristos 			pos++;
6238dbcf02cSchristos 			len--;
6248dbcf02cSchristos 			nak_list = pos;
6258dbcf02cSchristos 		}
6268dbcf02cSchristos 	}
6278dbcf02cSchristos 	eap_sm_Policy_update(sm, nak_list, len);
6288dbcf02cSchristos }
6298dbcf02cSchristos 
6308dbcf02cSchristos 
6318dbcf02cSchristos SM_STATE(EAP, SELECT_ACTION)
6328dbcf02cSchristos {
6338dbcf02cSchristos 	SM_ENTRY(EAP, SELECT_ACTION);
6348dbcf02cSchristos 
6358dbcf02cSchristos 	sm->decision = eap_sm_Policy_getDecision(sm);
6368dbcf02cSchristos }
6378dbcf02cSchristos 
6388dbcf02cSchristos 
6398dbcf02cSchristos SM_STATE(EAP, TIMEOUT_FAILURE)
6408dbcf02cSchristos {
6418dbcf02cSchristos 	SM_ENTRY(EAP, TIMEOUT_FAILURE);
6428dbcf02cSchristos 
643*45d3cc13Schristos 	sm->eap_if.eapTimeout = true;
644be6b3c4dSchristos 
645*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO,
646*45d3cc13Schristos 		WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR, MAC2STR(sm->peer_addr));
6478dbcf02cSchristos }
6488dbcf02cSchristos 
6498dbcf02cSchristos 
6508dbcf02cSchristos SM_STATE(EAP, FAILURE)
6518dbcf02cSchristos {
6528dbcf02cSchristos 	SM_ENTRY(EAP, FAILURE);
6538dbcf02cSchristos 
6548dbcf02cSchristos 	wpabuf_free(sm->eap_if.eapReqData);
6558dbcf02cSchristos 	sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
6568dbcf02cSchristos 	wpabuf_free(sm->lastReqData);
6578dbcf02cSchristos 	sm->lastReqData = NULL;
658*45d3cc13Schristos 	sm->eap_if.eapFail = true;
6598dbcf02cSchristos 
660*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
6618dbcf02cSchristos 		MACSTR, MAC2STR(sm->peer_addr));
6628dbcf02cSchristos }
6638dbcf02cSchristos 
6648dbcf02cSchristos 
6658dbcf02cSchristos SM_STATE(EAP, SUCCESS)
6668dbcf02cSchristos {
6678dbcf02cSchristos 	SM_ENTRY(EAP, SUCCESS);
6688dbcf02cSchristos 
6698dbcf02cSchristos 	wpabuf_free(sm->eap_if.eapReqData);
6708dbcf02cSchristos 	sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
6718dbcf02cSchristos 	wpabuf_free(sm->lastReqData);
6728dbcf02cSchristos 	sm->lastReqData = NULL;
6738dbcf02cSchristos 	if (sm->eap_if.eapKeyData)
674*45d3cc13Schristos 		sm->eap_if.eapKeyAvailable = true;
675*45d3cc13Schristos 	sm->eap_if.eapSuccess = true;
6768dbcf02cSchristos 
677*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
6788dbcf02cSchristos 		MACSTR, MAC2STR(sm->peer_addr));
6798dbcf02cSchristos }
6808dbcf02cSchristos 
6818dbcf02cSchristos 
682bb610346Schristos SM_STATE(EAP, INITIATE_REAUTH_START)
683bb610346Schristos {
684bb610346Schristos 	SM_ENTRY(EAP, INITIATE_REAUTH_START);
685bb610346Schristos 
686*45d3cc13Schristos 	sm->initiate_reauth_start_sent = true;
687*45d3cc13Schristos 	sm->try_initiate_reauth = true;
688bb610346Schristos 	sm->currentId = eap_sm_nextId(sm, sm->currentId);
689bb610346Schristos 	wpa_printf(MSG_DEBUG,
690bb610346Schristos 		   "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d",
691bb610346Schristos 		   sm->currentId);
692bb610346Schristos 	sm->lastId = sm->currentId;
693bb610346Schristos 	wpabuf_free(sm->eap_if.eapReqData);
694bb610346Schristos 	sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm,
695bb610346Schristos 								sm->currentId);
696bb610346Schristos 	wpabuf_free(sm->lastReqData);
697bb610346Schristos 	sm->lastReqData = NULL;
698bb610346Schristos }
699bb610346Schristos 
700bb610346Schristos 
701bb610346Schristos #ifdef CONFIG_ERP
702bb610346Schristos 
703bb610346Schristos static void erp_send_finish_reauth(struct eap_sm *sm,
704bb610346Schristos 				   struct eap_server_erp_key *erp, u8 id,
705bb610346Schristos 				   u8 flags, u16 seq, const char *nai)
706bb610346Schristos {
707bb610346Schristos 	size_t plen;
708bb610346Schristos 	struct wpabuf *msg;
709bb610346Schristos 	u8 hash[SHA256_MAC_LEN];
710bb610346Schristos 	size_t hash_len;
711bb610346Schristos 	u8 seed[4];
712bb610346Schristos 
713bb610346Schristos 	if (erp) {
714bb610346Schristos 		switch (erp->cryptosuite) {
715bb610346Schristos 		case EAP_ERP_CS_HMAC_SHA256_256:
716bb610346Schristos 			hash_len = 32;
717bb610346Schristos 			break;
718bb610346Schristos 		case EAP_ERP_CS_HMAC_SHA256_128:
719bb610346Schristos 			hash_len = 16;
720bb610346Schristos 			break;
721bb610346Schristos 		default:
722bb610346Schristos 			return;
723bb610346Schristos 		}
724bb610346Schristos 	} else
725bb610346Schristos 		hash_len = 0;
726bb610346Schristos 
727bb610346Schristos 	plen = 1 + 2 + 2 + os_strlen(nai);
728bb610346Schristos 	if (hash_len)
729bb610346Schristos 		plen += 1 + hash_len;
730*45d3cc13Schristos 	msg = eap_msg_alloc(EAP_VENDOR_IETF,
731*45d3cc13Schristos 			    (enum eap_type) EAP_ERP_TYPE_REAUTH,
732ecc36642Schristos 			    plen, EAP_CODE_FINISH, id);
733bb610346Schristos 	if (msg == NULL)
734bb610346Schristos 		return;
735bb610346Schristos 	wpabuf_put_u8(msg, flags);
736bb610346Schristos 	wpabuf_put_be16(msg, seq);
737bb610346Schristos 
738bb610346Schristos 	wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
739bb610346Schristos 	wpabuf_put_u8(msg, os_strlen(nai));
740bb610346Schristos 	wpabuf_put_str(msg, nai);
741bb610346Schristos 
742bb610346Schristos 	if (erp) {
743bb610346Schristos 		wpabuf_put_u8(msg, erp->cryptosuite);
744bb610346Schristos 		if (hmac_sha256(erp->rIK, erp->rIK_len,
745bb610346Schristos 				wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
746bb610346Schristos 			wpabuf_free(msg);
747bb610346Schristos 			return;
748bb610346Schristos 		}
749bb610346Schristos 		wpabuf_put_data(msg, hash, hash_len);
750bb610346Schristos 	}
751bb610346Schristos 
752bb610346Schristos 	wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)",
753bb610346Schristos 		   flags & 0x80 ? "failure" : "success");
754bb610346Schristos 
755bb610346Schristos 	sm->lastId = sm->currentId;
756bb610346Schristos 	sm->currentId = id;
757bb610346Schristos 	wpabuf_free(sm->eap_if.eapReqData);
758bb610346Schristos 	sm->eap_if.eapReqData = msg;
759bb610346Schristos 	wpabuf_free(sm->lastReqData);
760bb610346Schristos 	sm->lastReqData = NULL;
761bb610346Schristos 
762ecc36642Schristos 	if ((flags & 0x80) || !erp) {
763*45d3cc13Schristos 		sm->eap_if.eapFail = true;
764*45d3cc13Schristos 		wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
765bb610346Schristos 			MACSTR, MAC2STR(sm->peer_addr));
766bb610346Schristos 		return;
767bb610346Schristos 	}
768bb610346Schristos 
769bb610346Schristos 	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
770bb610346Schristos 	sm->eap_if.eapKeyDataLen = 0;
771bb610346Schristos 	sm->eap_if.eapKeyData = os_malloc(erp->rRK_len);
772bb610346Schristos 	if (!sm->eap_if.eapKeyData)
773bb610346Schristos 		return;
774bb610346Schristos 
775bb610346Schristos 	WPA_PUT_BE16(seed, seq);
776bb610346Schristos 	WPA_PUT_BE16(&seed[2], erp->rRK_len);
777bb610346Schristos 	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
778bb610346Schristos 			    "Re-authentication Master Session Key@ietf.org",
779bb610346Schristos 			    seed, sizeof(seed),
780bb610346Schristos 			    sm->eap_if.eapKeyData, erp->rRK_len) < 0) {
781bb610346Schristos 		wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
782bb610346Schristos 		bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len);
783bb610346Schristos 		sm->eap_if.eapKeyData = NULL;
784bb610346Schristos 		return;
785bb610346Schristos 	}
786bb610346Schristos 	sm->eap_if.eapKeyDataLen = erp->rRK_len;
787*45d3cc13Schristos 	sm->eap_if.eapKeyAvailable = true;
788bb610346Schristos 	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
789bb610346Schristos 			sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
790*45d3cc13Schristos 	sm->eap_if.eapSuccess = true;
791bb610346Schristos 
792*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
793bb610346Schristos 		MACSTR, MAC2STR(sm->peer_addr));
794bb610346Schristos }
795bb610346Schristos 
796bb610346Schristos 
797bb610346Schristos SM_STATE(EAP, INITIATE_RECEIVED)
798bb610346Schristos {
799bb610346Schristos 	const u8 *pos, *end, *start, *tlvs, *hdr;
800bb610346Schristos 	const struct eap_hdr *ehdr;
801bb610346Schristos 	size_t len;
802bb610346Schristos 	u8 flags;
803bb610346Schristos 	u16 seq;
804bb610346Schristos 	char nai[254];
805bb610346Schristos 	struct eap_server_erp_key *erp;
806bb610346Schristos 	int max_len;
807bb610346Schristos 	u8 hash[SHA256_MAC_LEN];
808bb610346Schristos 	size_t hash_len;
809bb610346Schristos 	struct erp_tlvs parse;
810bb610346Schristos 	u8 resp_flags = 0x80; /* default to failure; cleared on success */
811bb610346Schristos 
812bb610346Schristos 	SM_ENTRY(EAP, INITIATE_RECEIVED);
813bb610346Schristos 
814*45d3cc13Schristos 	sm->rxInitiate = false;
815bb610346Schristos 
816*45d3cc13Schristos 	pos = eap_hdr_validate(EAP_VENDOR_IETF,
817*45d3cc13Schristos 			       (enum eap_type) EAP_ERP_TYPE_REAUTH,
818bb610346Schristos 			       sm->eap_if.eapRespData, &len);
819bb610346Schristos 	if (pos == NULL) {
820bb610346Schristos 		wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
821bb610346Schristos 		goto fail;
822bb610346Schristos 	}
823bb610346Schristos 	hdr = wpabuf_head(sm->eap_if.eapRespData);
824bb610346Schristos 	ehdr = wpabuf_head(sm->eap_if.eapRespData);
825bb610346Schristos 
826bb610346Schristos 	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len);
827bb610346Schristos 	if (len < 4) {
828bb610346Schristos 		wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth");
829bb610346Schristos 		goto fail;
830bb610346Schristos 	}
831bb610346Schristos 	end = pos + len;
832bb610346Schristos 
833bb610346Schristos 	flags = *pos++;
834bb610346Schristos 	seq = WPA_GET_BE16(pos);
835bb610346Schristos 	pos += 2;
836bb610346Schristos 	wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
837bb610346Schristos 	tlvs = pos;
838bb610346Schristos 
839bb610346Schristos 	/*
840bb610346Schristos 	 * Parse TVs/TLVs. Since we do not yet know the length of the
841bb610346Schristos 	 * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
842bb610346Schristos 	 * just try to find the keyName-NAI first so that we can check the
843bb610346Schristos 	 * Authentication Tag.
844bb610346Schristos 	 */
845bb610346Schristos 	if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0)
846bb610346Schristos 		goto fail;
847bb610346Schristos 
848bb610346Schristos 	if (!parse.keyname) {
849bb610346Schristos 		wpa_printf(MSG_DEBUG,
850bb610346Schristos 			   "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet");
851bb610346Schristos 		goto fail;
852bb610346Schristos 	}
853bb610346Schristos 
854bb610346Schristos 	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI",
855bb610346Schristos 			  parse.keyname, parse.keyname_len);
856bb610346Schristos 	if (parse.keyname_len > 253) {
857bb610346Schristos 		wpa_printf(MSG_DEBUG,
858bb610346Schristos 			   "EAP: Too long keyName-NAI in EAP-Initiate/Re-auth");
859bb610346Schristos 		goto fail;
860bb610346Schristos 	}
861bb610346Schristos 	os_memcpy(nai, parse.keyname, parse.keyname_len);
862bb610346Schristos 	nai[parse.keyname_len] = '\0';
863bb610346Schristos 
864*45d3cc13Schristos 	if (!sm->cfg->eap_server) {
865bb610346Schristos 		/*
866bb610346Schristos 		 * In passthrough case, EAP-Initiate/Re-auth replaces
867bb610346Schristos 		 * EAP Identity exchange. Use keyName-NAI as the user identity
868bb610346Schristos 		 * and forward EAP-Initiate/Re-auth to the backend
869bb610346Schristos 		 * authentication server.
870bb610346Schristos 		 */
871bb610346Schristos 		wpa_printf(MSG_DEBUG,
872bb610346Schristos 			   "EAP: Use keyName-NAI as user identity for backend authentication");
873bb610346Schristos 		eap_server_clear_identity(sm);
874bb610346Schristos 		sm->identity = (u8 *) dup_binstr(parse.keyname,
875bb610346Schristos 						 parse.keyname_len);
876bb610346Schristos 		if (!sm->identity)
877bb610346Schristos 			goto fail;
878bb610346Schristos 		sm->identity_len = parse.keyname_len;
879bb610346Schristos 		return;
880bb610346Schristos 	}
881bb610346Schristos 
882bb610346Schristos 	erp = eap_erp_get_key(sm, nai);
883bb610346Schristos 	if (!erp) {
884bb610346Schristos 		wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
885bb610346Schristos 			   nai);
886bb610346Schristos 		goto report_error;
887bb610346Schristos 	}
888bb610346Schristos 
889bb610346Schristos 	if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) {
890bb610346Schristos 		wpa_printf(MSG_DEBUG,
891bb610346Schristos 			   "EAP: SEQ=%u replayed (already received SEQ=%u)",
892bb610346Schristos 			   seq, erp->recv_seq);
893bb610346Schristos 		goto fail;
894bb610346Schristos 	}
895bb610346Schristos 
896bb610346Schristos 	/* Is there enough room for Cryptosuite and Authentication Tag? */
897bb610346Schristos 	start = parse.keyname + parse.keyname_len;
898bb610346Schristos 	max_len = end - start;
899bb610346Schristos 	if (max_len <
900bb610346Schristos 	    1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) {
901bb610346Schristos 		wpa_printf(MSG_DEBUG,
902bb610346Schristos 			   "EAP: Not enough room for Authentication Tag");
903bb610346Schristos 		goto fail;
904bb610346Schristos 	}
905bb610346Schristos 
906bb610346Schristos 	switch (erp->cryptosuite) {
907bb610346Schristos 	case EAP_ERP_CS_HMAC_SHA256_256:
908bb610346Schristos 		if (end[-33] != erp->cryptosuite) {
909bb610346Schristos 			wpa_printf(MSG_DEBUG,
910bb610346Schristos 				   "EAP: Different Cryptosuite used");
911bb610346Schristos 			goto fail;
912bb610346Schristos 		}
913bb610346Schristos 		hash_len = 32;
914bb610346Schristos 		break;
915bb610346Schristos 	case EAP_ERP_CS_HMAC_SHA256_128:
916bb610346Schristos 		if (end[-17] != erp->cryptosuite) {
917bb610346Schristos 			wpa_printf(MSG_DEBUG,
918bb610346Schristos 				   "EAP: Different Cryptosuite used");
919bb610346Schristos 			goto fail;
920bb610346Schristos 		}
921bb610346Schristos 		hash_len = 16;
922bb610346Schristos 		break;
923bb610346Schristos 	default:
924bb610346Schristos 		hash_len = 0;
925bb610346Schristos 		break;
926bb610346Schristos 	}
927bb610346Schristos 
928bb610346Schristos 	if (hash_len) {
929bb610346Schristos 		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
930bb610346Schristos 				end - hdr - hash_len, hash) < 0)
931bb610346Schristos 			goto fail;
932bb610346Schristos 		if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
933bb610346Schristos 			wpa_printf(MSG_DEBUG,
934bb610346Schristos 				   "EAP: Authentication Tag mismatch");
935bb610346Schristos 			goto fail;
936bb610346Schristos 		}
937bb610346Schristos 	}
938bb610346Schristos 
939bb610346Schristos 	/* Check if any supported CS results in matching tag */
940bb610346Schristos 	if (!hash_len && max_len >= 1 + 32 &&
941bb610346Schristos 	    end[-33] == EAP_ERP_CS_HMAC_SHA256_256) {
942bb610346Schristos 		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
943bb610346Schristos 				end - hdr - 32, hash) < 0)
944bb610346Schristos 			goto fail;
945bb610346Schristos 		if (os_memcmp(end - 32, hash, 32) == 0) {
946bb610346Schristos 			wpa_printf(MSG_DEBUG,
947bb610346Schristos 				   "EAP: Authentication Tag match using HMAC-SHA256-256");
948bb610346Schristos 			hash_len = 32;
949bb610346Schristos 			erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256;
950bb610346Schristos 		}
951bb610346Schristos 	}
952bb610346Schristos 
953bb610346Schristos 	if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) {
954bb610346Schristos 		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
955bb610346Schristos 				end - hdr - 16, hash) < 0)
956bb610346Schristos 			goto fail;
957bb610346Schristos 		if (os_memcmp(end - 16, hash, 16) == 0) {
958bb610346Schristos 			wpa_printf(MSG_DEBUG,
959bb610346Schristos 				   "EAP: Authentication Tag match using HMAC-SHA256-128");
960bb610346Schristos 			hash_len = 16;
961bb610346Schristos 			erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128;
962bb610346Schristos 		}
963bb610346Schristos 	}
964bb610346Schristos 
965bb610346Schristos 	if (!hash_len) {
966bb610346Schristos 		wpa_printf(MSG_DEBUG,
967bb610346Schristos 			   "EAP: No supported cryptosuite matched Authentication Tag");
968bb610346Schristos 		goto fail;
969bb610346Schristos 	}
970bb610346Schristos 	end -= 1 + hash_len;
971bb610346Schristos 
972bb610346Schristos 	/*
973bb610346Schristos 	 * Parse TVs/TLVs again now that we know the exact part of the buffer
974bb610346Schristos 	 * that contains them.
975bb610346Schristos 	 */
976bb610346Schristos 	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs",
977bb610346Schristos 		    tlvs, end - tlvs);
978bb610346Schristos 	if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0)
979bb610346Schristos 		goto fail;
980bb610346Schristos 
981bb610346Schristos 	wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u",
982bb610346Schristos 		   erp->keyname_nai, seq);
983bb610346Schristos 	erp->recv_seq = seq;
984bb610346Schristos 	resp_flags &= ~0x80; /* R=0 - success */
985bb610346Schristos 
986bb610346Schristos report_error:
987bb610346Schristos 	erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai);
988bb610346Schristos 	return;
989bb610346Schristos 
990bb610346Schristos fail:
991*45d3cc13Schristos 	sm->ignore = true;
992bb610346Schristos }
993bb610346Schristos 
994bb610346Schristos #endif /* CONFIG_ERP */
995bb610346Schristos 
996bb610346Schristos 
9978dbcf02cSchristos SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
9988dbcf02cSchristos {
9998dbcf02cSchristos 	SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
10008dbcf02cSchristos 
10018dbcf02cSchristos 	wpabuf_free(sm->eap_if.aaaEapRespData);
10028dbcf02cSchristos 	sm->eap_if.aaaEapRespData = NULL;
1003*45d3cc13Schristos 	sm->try_initiate_reauth = false;
10048dbcf02cSchristos }
10058dbcf02cSchristos 
10068dbcf02cSchristos 
10078dbcf02cSchristos SM_STATE(EAP, IDLE2)
10088dbcf02cSchristos {
10098dbcf02cSchristos 	SM_ENTRY(EAP, IDLE2);
10108dbcf02cSchristos 
10118dbcf02cSchristos 	sm->eap_if.retransWhile = eap_sm_calculateTimeout(
10128dbcf02cSchristos 		sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
10138dbcf02cSchristos 		sm->methodTimeout);
10148dbcf02cSchristos }
10158dbcf02cSchristos 
10168dbcf02cSchristos 
10178dbcf02cSchristos SM_STATE(EAP, RETRANSMIT2)
10188dbcf02cSchristos {
10198dbcf02cSchristos 	SM_ENTRY(EAP, RETRANSMIT2);
10208dbcf02cSchristos 
10218dbcf02cSchristos 	sm->retransCount++;
10228dbcf02cSchristos 	if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
10238dbcf02cSchristos 		if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
1024*45d3cc13Schristos 			sm->eap_if.eapReq = true;
10258dbcf02cSchristos 	}
1026be6b3c4dSchristos 
1027*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR,
1028be6b3c4dSchristos 		MAC2STR(sm->peer_addr));
10298dbcf02cSchristos }
10308dbcf02cSchristos 
10318dbcf02cSchristos 
10328dbcf02cSchristos SM_STATE(EAP, RECEIVED2)
10338dbcf02cSchristos {
10348dbcf02cSchristos 	SM_ENTRY(EAP, RECEIVED2);
10358dbcf02cSchristos 
10368dbcf02cSchristos 	/* parse rxResp, respId, respMethod */
10378dbcf02cSchristos 	eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
10388dbcf02cSchristos }
10398dbcf02cSchristos 
10408dbcf02cSchristos 
10418dbcf02cSchristos SM_STATE(EAP, DISCARD2)
10428dbcf02cSchristos {
10438dbcf02cSchristos 	SM_ENTRY(EAP, DISCARD2);
1044*45d3cc13Schristos 	sm->eap_if.eapResp = false;
1045*45d3cc13Schristos 	sm->eap_if.eapNoReq = true;
10468dbcf02cSchristos }
10478dbcf02cSchristos 
10488dbcf02cSchristos 
10498dbcf02cSchristos SM_STATE(EAP, SEND_REQUEST2)
10508dbcf02cSchristos {
10518dbcf02cSchristos 	SM_ENTRY(EAP, SEND_REQUEST2);
10528dbcf02cSchristos 
10538dbcf02cSchristos 	sm->retransCount = 0;
10548dbcf02cSchristos 	if (sm->eap_if.eapReqData) {
10558dbcf02cSchristos 		if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
10568dbcf02cSchristos 		{
1057*45d3cc13Schristos 			sm->eap_if.eapResp = false;
1058*45d3cc13Schristos 			sm->eap_if.eapReq = true;
10598dbcf02cSchristos 		} else {
1060*45d3cc13Schristos 			sm->eap_if.eapResp = false;
1061*45d3cc13Schristos 			sm->eap_if.eapReq = false;
10628dbcf02cSchristos 		}
10638dbcf02cSchristos 	} else {
10648dbcf02cSchristos 		wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
1065*45d3cc13Schristos 		sm->eap_if.eapResp = false;
1066*45d3cc13Schristos 		sm->eap_if.eapReq = false;
1067*45d3cc13Schristos 		sm->eap_if.eapNoReq = true;
10688dbcf02cSchristos 	}
10698dbcf02cSchristos }
10708dbcf02cSchristos 
10718dbcf02cSchristos 
10728dbcf02cSchristos SM_STATE(EAP, AAA_REQUEST)
10738dbcf02cSchristos {
10748dbcf02cSchristos 	SM_ENTRY(EAP, AAA_REQUEST);
10758dbcf02cSchristos 
10768dbcf02cSchristos 	if (sm->eap_if.eapRespData == NULL) {
10778dbcf02cSchristos 		wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
10788dbcf02cSchristos 		return;
10798dbcf02cSchristos 	}
10808dbcf02cSchristos 
10818dbcf02cSchristos 	/*
10828dbcf02cSchristos 	 * if (respMethod == IDENTITY)
10838dbcf02cSchristos 	 *	aaaIdentity = eapRespData
10848dbcf02cSchristos 	 * This is already taken care of by the EAP-Identity method which
10858dbcf02cSchristos 	 * stores the identity into sm->identity.
10868dbcf02cSchristos 	 */
10878dbcf02cSchristos 
10888dbcf02cSchristos 	eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
10898dbcf02cSchristos }
10908dbcf02cSchristos 
10918dbcf02cSchristos 
10928dbcf02cSchristos SM_STATE(EAP, AAA_RESPONSE)
10938dbcf02cSchristos {
10948dbcf02cSchristos 	SM_ENTRY(EAP, AAA_RESPONSE);
10958dbcf02cSchristos 
10968dbcf02cSchristos 	eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
10978dbcf02cSchristos 	sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
10988dbcf02cSchristos 	sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
10998dbcf02cSchristos }
11008dbcf02cSchristos 
11018dbcf02cSchristos 
11028dbcf02cSchristos SM_STATE(EAP, AAA_IDLE)
11038dbcf02cSchristos {
11048dbcf02cSchristos 	SM_ENTRY(EAP, AAA_IDLE);
11058dbcf02cSchristos 
1106*45d3cc13Schristos 	sm->eap_if.aaaFail = false;
1107*45d3cc13Schristos 	sm->eap_if.aaaSuccess = false;
1108*45d3cc13Schristos 	sm->eap_if.aaaEapReq = false;
1109*45d3cc13Schristos 	sm->eap_if.aaaEapNoReq = false;
1110*45d3cc13Schristos 	sm->eap_if.aaaEapResp = true;
11118dbcf02cSchristos }
11128dbcf02cSchristos 
11138dbcf02cSchristos 
11148dbcf02cSchristos SM_STATE(EAP, TIMEOUT_FAILURE2)
11158dbcf02cSchristos {
11168dbcf02cSchristos 	SM_ENTRY(EAP, TIMEOUT_FAILURE2);
11178dbcf02cSchristos 
1118*45d3cc13Schristos 	sm->eap_if.eapTimeout = true;
1119be6b3c4dSchristos 
1120*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO,
1121*45d3cc13Schristos 		WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR, MAC2STR(sm->peer_addr));
11228dbcf02cSchristos }
11238dbcf02cSchristos 
11248dbcf02cSchristos 
11258dbcf02cSchristos SM_STATE(EAP, FAILURE2)
11268dbcf02cSchristos {
11278dbcf02cSchristos 	SM_ENTRY(EAP, FAILURE2);
11288dbcf02cSchristos 
11298dbcf02cSchristos 	eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
1130*45d3cc13Schristos 	sm->eap_if.eapFail = true;
1131be6b3c4dSchristos 
1132*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR,
1133be6b3c4dSchristos 		MAC2STR(sm->peer_addr));
11348dbcf02cSchristos }
11358dbcf02cSchristos 
11368dbcf02cSchristos 
11378dbcf02cSchristos SM_STATE(EAP, SUCCESS2)
11388dbcf02cSchristos {
11398dbcf02cSchristos 	SM_ENTRY(EAP, SUCCESS2);
11408dbcf02cSchristos 
11418dbcf02cSchristos 	eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
11428dbcf02cSchristos 
11438dbcf02cSchristos 	sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
11448dbcf02cSchristos 	if (sm->eap_if.aaaEapKeyAvailable) {
11458dbcf02cSchristos 		EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
11468dbcf02cSchristos 	} else {
11473c260e60Schristos 		bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
11488dbcf02cSchristos 		sm->eap_if.eapKeyData = NULL;
11498dbcf02cSchristos 		sm->eap_if.eapKeyDataLen = 0;
11508dbcf02cSchristos 	}
11518dbcf02cSchristos 
1152*45d3cc13Schristos 	sm->eap_if.eapSuccess = true;
11538dbcf02cSchristos 
11548dbcf02cSchristos 	/*
11558dbcf02cSchristos 	 * Start reauthentication with identity request even though we know the
11568dbcf02cSchristos 	 * previously used identity. This is needed to get reauthentication
11578dbcf02cSchristos 	 * started properly.
11588dbcf02cSchristos 	 */
1159*45d3cc13Schristos 	sm->start_reauth = true;
1160be6b3c4dSchristos 
1161*45d3cc13Schristos 	wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR,
1162be6b3c4dSchristos 		MAC2STR(sm->peer_addr));
11638dbcf02cSchristos }
11648dbcf02cSchristos 
11658dbcf02cSchristos 
11668dbcf02cSchristos SM_STEP(EAP)
11678dbcf02cSchristos {
11688dbcf02cSchristos 	if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
11698dbcf02cSchristos 		SM_ENTER_GLOBAL(EAP, INITIALIZE);
11708dbcf02cSchristos 	else if (!sm->eap_if.portEnabled)
11718dbcf02cSchristos 		SM_ENTER_GLOBAL(EAP, DISABLED);
1172*45d3cc13Schristos 	else if (sm->num_rounds > sm->cfg->max_auth_rounds) {
1173*45d3cc13Schristos 		if (sm->num_rounds == sm->cfg->max_auth_rounds + 1) {
11748dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: more than %d "
11758dbcf02cSchristos 				   "authentication rounds - abort",
1176*45d3cc13Schristos 				   sm->cfg->max_auth_rounds);
11778dbcf02cSchristos 			sm->num_rounds++;
11788dbcf02cSchristos 			SM_ENTER_GLOBAL(EAP, FAILURE);
11798dbcf02cSchristos 		}
1180*45d3cc13Schristos 	} else if (sm->num_rounds_short > sm->cfg->max_auth_rounds_short) {
1181*45d3cc13Schristos 		if (sm->num_rounds_short ==
1182*45d3cc13Schristos 		    sm->cfg->max_auth_rounds_short + 1) {
1183*45d3cc13Schristos 			wpa_printf(MSG_DEBUG,
1184*45d3cc13Schristos 				   "EAP: more than %d authentication rounds (short) - abort",
1185*45d3cc13Schristos 				   sm->cfg->max_auth_rounds_short);
1186*45d3cc13Schristos 			sm->num_rounds_short++;
1187*45d3cc13Schristos 			SM_ENTER_GLOBAL(EAP, FAILURE);
1188*45d3cc13Schristos 		}
11898dbcf02cSchristos 	} else switch (sm->EAP_state) {
11908dbcf02cSchristos 	case EAP_INITIALIZE:
1191*45d3cc13Schristos 		if (sm->cfg->backend_auth) {
11928dbcf02cSchristos 			if (!sm->rxResp)
11938dbcf02cSchristos 				SM_ENTER(EAP, SELECT_ACTION);
11948dbcf02cSchristos 			else if (sm->rxResp &&
11958dbcf02cSchristos 				 (sm->respMethod == EAP_TYPE_NAK ||
11968dbcf02cSchristos 				  (sm->respMethod == EAP_TYPE_EXPANDED &&
11978dbcf02cSchristos 				   sm->respVendor == EAP_VENDOR_IETF &&
11988dbcf02cSchristos 				   sm->respVendorMethod == EAP_TYPE_NAK)))
11998dbcf02cSchristos 				SM_ENTER(EAP, NAK);
12008dbcf02cSchristos 			else
12018dbcf02cSchristos 				SM_ENTER(EAP, PICK_UP_METHOD);
12028dbcf02cSchristos 		} else {
12038dbcf02cSchristos 			SM_ENTER(EAP, SELECT_ACTION);
12048dbcf02cSchristos 		}
12058dbcf02cSchristos 		break;
12068dbcf02cSchristos 	case EAP_PICK_UP_METHOD:
12078dbcf02cSchristos 		if (sm->currentMethod == EAP_TYPE_NONE) {
12088dbcf02cSchristos 			SM_ENTER(EAP, SELECT_ACTION);
12098dbcf02cSchristos 		} else {
12108dbcf02cSchristos 			SM_ENTER(EAP, METHOD_RESPONSE);
12118dbcf02cSchristos 		}
12128dbcf02cSchristos 		break;
12138dbcf02cSchristos 	case EAP_DISABLED:
12148dbcf02cSchristos 		if (sm->eap_if.portEnabled)
12158dbcf02cSchristos 			SM_ENTER(EAP, INITIALIZE);
12168dbcf02cSchristos 		break;
12178dbcf02cSchristos 	case EAP_IDLE:
1218bb610346Schristos 		if (sm->eap_if.retransWhile == 0) {
1219bb610346Schristos 			if (sm->try_initiate_reauth) {
1220*45d3cc13Schristos 				sm->try_initiate_reauth = false;
1221bb610346Schristos 				SM_ENTER(EAP, SELECT_ACTION);
1222bb610346Schristos 			} else {
12238dbcf02cSchristos 				SM_ENTER(EAP, RETRANSMIT);
1224bb610346Schristos 			}
1225bb610346Schristos 		} else if (sm->eap_if.eapResp)
12268dbcf02cSchristos 			SM_ENTER(EAP, RECEIVED);
12278dbcf02cSchristos 		break;
12288dbcf02cSchristos 	case EAP_RETRANSMIT:
12298dbcf02cSchristos 		if (sm->retransCount > sm->MaxRetrans)
12308dbcf02cSchristos 			SM_ENTER(EAP, TIMEOUT_FAILURE);
12318dbcf02cSchristos 		else
12328dbcf02cSchristos 			SM_ENTER(EAP, IDLE);
12338dbcf02cSchristos 		break;
12348dbcf02cSchristos 	case EAP_RECEIVED:
12358dbcf02cSchristos 		if (sm->rxResp && (sm->respId == sm->currentId) &&
12368dbcf02cSchristos 		    (sm->respMethod == EAP_TYPE_NAK ||
12378dbcf02cSchristos 		     (sm->respMethod == EAP_TYPE_EXPANDED &&
12388dbcf02cSchristos 		      sm->respVendor == EAP_VENDOR_IETF &&
12398dbcf02cSchristos 		      sm->respVendorMethod == EAP_TYPE_NAK))
12408dbcf02cSchristos 		    && (sm->methodState == METHOD_PROPOSED))
12418dbcf02cSchristos 			SM_ENTER(EAP, NAK);
12428dbcf02cSchristos 		else if (sm->rxResp && (sm->respId == sm->currentId) &&
12438dbcf02cSchristos 			 ((sm->respMethod == sm->currentMethod) ||
12448dbcf02cSchristos 			  (sm->respMethod == EAP_TYPE_EXPANDED &&
12458dbcf02cSchristos 			   sm->respVendor == EAP_VENDOR_IETF &&
12468dbcf02cSchristos 			   sm->respVendorMethod == sm->currentMethod)))
12478dbcf02cSchristos 			SM_ENTER(EAP, INTEGRITY_CHECK);
1248bb610346Schristos #ifdef CONFIG_ERP
1249bb610346Schristos 		else if (sm->rxInitiate)
1250bb610346Schristos 			SM_ENTER(EAP, INITIATE_RECEIVED);
1251bb610346Schristos #endif /* CONFIG_ERP */
12528dbcf02cSchristos 		else {
12538dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
12548dbcf02cSchristos 				   "rxResp=%d respId=%d currentId=%d "
12558dbcf02cSchristos 				   "respMethod=%d currentMethod=%d",
12568dbcf02cSchristos 				   sm->rxResp, sm->respId, sm->currentId,
12578dbcf02cSchristos 				   sm->respMethod, sm->currentMethod);
12583c260e60Schristos 			eap_log_msg(sm, "Discard received EAP message");
12598dbcf02cSchristos 			SM_ENTER(EAP, DISCARD);
12608dbcf02cSchristos 		}
12618dbcf02cSchristos 		break;
12628dbcf02cSchristos 	case EAP_DISCARD:
12638dbcf02cSchristos 		SM_ENTER(EAP, IDLE);
12648dbcf02cSchristos 		break;
12658dbcf02cSchristos 	case EAP_SEND_REQUEST:
12668dbcf02cSchristos 		SM_ENTER(EAP, IDLE);
12678dbcf02cSchristos 		break;
12688dbcf02cSchristos 	case EAP_INTEGRITY_CHECK:
12698dbcf02cSchristos 		if (sm->ignore)
12708dbcf02cSchristos 			SM_ENTER(EAP, DISCARD);
12718dbcf02cSchristos 		else
12728dbcf02cSchristos 			SM_ENTER(EAP, METHOD_RESPONSE);
12738dbcf02cSchristos 		break;
12748dbcf02cSchristos 	case EAP_METHOD_REQUEST:
12753c260e60Schristos 		if (sm->m == NULL) {
12763c260e60Schristos 			/*
12773c260e60Schristos 			 * This transition is not mentioned in RFC 4137, but it
12783c260e60Schristos 			 * is needed to handle cleanly a case where EAP method
12793c260e60Schristos 			 * initialization fails.
12803c260e60Schristos 			 */
12813c260e60Schristos 			SM_ENTER(EAP, FAILURE);
12823c260e60Schristos 			break;
12833c260e60Schristos 		}
12848dbcf02cSchristos 		SM_ENTER(EAP, SEND_REQUEST);
1285ecc36642Schristos 		if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) {
1286ecc36642Schristos 			/*
1287ecc36642Schristos 			 * This transition is not mentioned in RFC 4137, but it
1288ecc36642Schristos 			 * is needed to handle cleanly a case where EAP method
1289ecc36642Schristos 			 * buildReq fails.
1290ecc36642Schristos 			 */
1291ecc36642Schristos 			wpa_printf(MSG_DEBUG,
1292ecc36642Schristos 				   "EAP: Method did not return a request");
1293ecc36642Schristos 			SM_ENTER(EAP, FAILURE);
1294ecc36642Schristos 			break;
1295ecc36642Schristos 		}
12968dbcf02cSchristos 		break;
12978dbcf02cSchristos 	case EAP_METHOD_RESPONSE:
12988dbcf02cSchristos 		/*
12998dbcf02cSchristos 		 * Note: Mechanism to allow EAP methods to wait while going
13008dbcf02cSchristos 		 * through pending processing is an extension to RFC 4137
13018dbcf02cSchristos 		 * which only defines the transits to SELECT_ACTION and
13028dbcf02cSchristos 		 * METHOD_REQUEST from this METHOD_RESPONSE state.
13038dbcf02cSchristos 		 */
13048dbcf02cSchristos 		if (sm->methodState == METHOD_END)
13058dbcf02cSchristos 			SM_ENTER(EAP, SELECT_ACTION);
13068dbcf02cSchristos 		else if (sm->method_pending == METHOD_PENDING_WAIT) {
13078dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: Method has pending "
13088dbcf02cSchristos 				   "processing - wait before proceeding to "
13098dbcf02cSchristos 				   "METHOD_REQUEST state");
13108dbcf02cSchristos 		} else if (sm->method_pending == METHOD_PENDING_CONT) {
13118dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: Method has completed "
13128dbcf02cSchristos 				   "pending processing - reprocess pending "
13138dbcf02cSchristos 				   "EAP message");
13148dbcf02cSchristos 			sm->method_pending = METHOD_PENDING_NONE;
13158dbcf02cSchristos 			SM_ENTER(EAP, METHOD_RESPONSE);
13168dbcf02cSchristos 		} else
13178dbcf02cSchristos 			SM_ENTER(EAP, METHOD_REQUEST);
13188dbcf02cSchristos 		break;
13198dbcf02cSchristos 	case EAP_PROPOSE_METHOD:
13208dbcf02cSchristos 		/*
13218dbcf02cSchristos 		 * Note: Mechanism to allow EAP methods to wait while going
13228dbcf02cSchristos 		 * through pending processing is an extension to RFC 4137
13238dbcf02cSchristos 		 * which only defines the transit to METHOD_REQUEST from this
13248dbcf02cSchristos 		 * PROPOSE_METHOD state.
13258dbcf02cSchristos 		 */
13268dbcf02cSchristos 		if (sm->method_pending == METHOD_PENDING_WAIT) {
13278dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: Method has pending "
13288dbcf02cSchristos 				   "processing - wait before proceeding to "
13298dbcf02cSchristos 				   "METHOD_REQUEST state");
13308dbcf02cSchristos 			if (sm->user_eap_method_index > 0)
13318dbcf02cSchristos 				sm->user_eap_method_index--;
13328dbcf02cSchristos 		} else if (sm->method_pending == METHOD_PENDING_CONT) {
13338dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: Method has completed "
13348dbcf02cSchristos 				   "pending processing - reprocess pending "
13358dbcf02cSchristos 				   "EAP message");
13368dbcf02cSchristos 			sm->method_pending = METHOD_PENDING_NONE;
13378dbcf02cSchristos 			SM_ENTER(EAP, PROPOSE_METHOD);
13388dbcf02cSchristos 		} else
13398dbcf02cSchristos 			SM_ENTER(EAP, METHOD_REQUEST);
13408dbcf02cSchristos 		break;
13418dbcf02cSchristos 	case EAP_NAK:
13428dbcf02cSchristos 		SM_ENTER(EAP, SELECT_ACTION);
13438dbcf02cSchristos 		break;
13448dbcf02cSchristos 	case EAP_SELECT_ACTION:
13458dbcf02cSchristos 		if (sm->decision == DECISION_FAILURE)
13468dbcf02cSchristos 			SM_ENTER(EAP, FAILURE);
13478dbcf02cSchristos 		else if (sm->decision == DECISION_SUCCESS)
13488dbcf02cSchristos 			SM_ENTER(EAP, SUCCESS);
13498dbcf02cSchristos 		else if (sm->decision == DECISION_PASSTHROUGH)
13508dbcf02cSchristos 			SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
1351bb610346Schristos 		else if (sm->decision == DECISION_INITIATE_REAUTH_START)
1352bb610346Schristos 			SM_ENTER(EAP, INITIATE_REAUTH_START);
1353bb610346Schristos #ifdef CONFIG_ERP
1354*45d3cc13Schristos 		else if (sm->cfg->eap_server && sm->cfg->erp && sm->rxInitiate)
1355bb610346Schristos 			SM_ENTER(EAP, INITIATE_RECEIVED);
1356bb610346Schristos #endif /* CONFIG_ERP */
13578dbcf02cSchristos 		else
13588dbcf02cSchristos 			SM_ENTER(EAP, PROPOSE_METHOD);
13598dbcf02cSchristos 		break;
1360bb610346Schristos 	case EAP_INITIATE_REAUTH_START:
1361bb610346Schristos 		SM_ENTER(EAP, SEND_REQUEST);
1362bb610346Schristos 		break;
1363bb610346Schristos 	case EAP_INITIATE_RECEIVED:
1364*45d3cc13Schristos 		if (!sm->cfg->eap_server)
1365bb610346Schristos 			SM_ENTER(EAP, SELECT_ACTION);
1366bb610346Schristos 		break;
13678dbcf02cSchristos 	case EAP_TIMEOUT_FAILURE:
13688dbcf02cSchristos 		break;
13698dbcf02cSchristos 	case EAP_FAILURE:
13708dbcf02cSchristos 		break;
13718dbcf02cSchristos 	case EAP_SUCCESS:
13728dbcf02cSchristos 		break;
13738dbcf02cSchristos 
13748dbcf02cSchristos 	case EAP_INITIALIZE_PASSTHROUGH:
13758dbcf02cSchristos 		if (sm->currentId == -1)
13768dbcf02cSchristos 			SM_ENTER(EAP, AAA_IDLE);
13778dbcf02cSchristos 		else
13788dbcf02cSchristos 			SM_ENTER(EAP, AAA_REQUEST);
13798dbcf02cSchristos 		break;
13808dbcf02cSchristos 	case EAP_IDLE2:
13818dbcf02cSchristos 		if (sm->eap_if.eapResp)
13828dbcf02cSchristos 			SM_ENTER(EAP, RECEIVED2);
13838dbcf02cSchristos 		else if (sm->eap_if.retransWhile == 0)
13848dbcf02cSchristos 			SM_ENTER(EAP, RETRANSMIT2);
13858dbcf02cSchristos 		break;
13868dbcf02cSchristos 	case EAP_RETRANSMIT2:
13878dbcf02cSchristos 		if (sm->retransCount > sm->MaxRetrans)
13888dbcf02cSchristos 			SM_ENTER(EAP, TIMEOUT_FAILURE2);
13898dbcf02cSchristos 		else
13908dbcf02cSchristos 			SM_ENTER(EAP, IDLE2);
13918dbcf02cSchristos 		break;
13928dbcf02cSchristos 	case EAP_RECEIVED2:
13938dbcf02cSchristos 		if (sm->rxResp && (sm->respId == sm->currentId))
13948dbcf02cSchristos 			SM_ENTER(EAP, AAA_REQUEST);
13958dbcf02cSchristos 		else
13968dbcf02cSchristos 			SM_ENTER(EAP, DISCARD2);
13978dbcf02cSchristos 		break;
13988dbcf02cSchristos 	case EAP_DISCARD2:
13998dbcf02cSchristos 		SM_ENTER(EAP, IDLE2);
14008dbcf02cSchristos 		break;
14018dbcf02cSchristos 	case EAP_SEND_REQUEST2:
14028dbcf02cSchristos 		SM_ENTER(EAP, IDLE2);
14038dbcf02cSchristos 		break;
14048dbcf02cSchristos 	case EAP_AAA_REQUEST:
14058dbcf02cSchristos 		SM_ENTER(EAP, AAA_IDLE);
14068dbcf02cSchristos 		break;
14078dbcf02cSchristos 	case EAP_AAA_RESPONSE:
14088dbcf02cSchristos 		SM_ENTER(EAP, SEND_REQUEST2);
14098dbcf02cSchristos 		break;
14108dbcf02cSchristos 	case EAP_AAA_IDLE:
14118dbcf02cSchristos 		if (sm->eap_if.aaaFail)
14128dbcf02cSchristos 			SM_ENTER(EAP, FAILURE2);
14138dbcf02cSchristos 		else if (sm->eap_if.aaaSuccess)
14148dbcf02cSchristos 			SM_ENTER(EAP, SUCCESS2);
14158dbcf02cSchristos 		else if (sm->eap_if.aaaEapReq)
14168dbcf02cSchristos 			SM_ENTER(EAP, AAA_RESPONSE);
14178dbcf02cSchristos 		else if (sm->eap_if.aaaTimeout)
14188dbcf02cSchristos 			SM_ENTER(EAP, TIMEOUT_FAILURE2);
14198dbcf02cSchristos 		break;
14208dbcf02cSchristos 	case EAP_TIMEOUT_FAILURE2:
14218dbcf02cSchristos 		break;
14228dbcf02cSchristos 	case EAP_FAILURE2:
14238dbcf02cSchristos 		break;
14248dbcf02cSchristos 	case EAP_SUCCESS2:
14258dbcf02cSchristos 		break;
14268dbcf02cSchristos 	}
14278dbcf02cSchristos }
14288dbcf02cSchristos 
14298dbcf02cSchristos 
14308dbcf02cSchristos static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
14318dbcf02cSchristos 				   int eapSRTT, int eapRTTVAR,
14328dbcf02cSchristos 				   int methodTimeout)
14338dbcf02cSchristos {
14348dbcf02cSchristos 	int rto, i;
14358dbcf02cSchristos 
1436bb610346Schristos 	if (sm->try_initiate_reauth) {
1437bb610346Schristos 		wpa_printf(MSG_DEBUG,
1438bb610346Schristos 			   "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start");
1439bb610346Schristos 		return 1;
1440bb610346Schristos 	}
1441bb610346Schristos 
14428dbcf02cSchristos 	if (methodTimeout) {
14438dbcf02cSchristos 		/*
14448dbcf02cSchristos 		 * EAP method (either internal or through AAA server, provided
14458dbcf02cSchristos 		 * timeout hint. Use that as-is as a timeout for retransmitting
14468dbcf02cSchristos 		 * the EAP request if no response is received.
14478dbcf02cSchristos 		 */
14488dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
14498dbcf02cSchristos 			   "(from EAP method hint)", methodTimeout);
14508dbcf02cSchristos 		return methodTimeout;
14518dbcf02cSchristos 	}
14528dbcf02cSchristos 
14538dbcf02cSchristos 	/*
14548dbcf02cSchristos 	 * RFC 3748 recommends algorithms described in RFC 2988 for estimation
14558dbcf02cSchristos 	 * of the retransmission timeout. This should be implemented once
14568dbcf02cSchristos 	 * round-trip time measurements are available. For nowm a simple
14578dbcf02cSchristos 	 * backoff mechanism is used instead if there are no EAP method
14588dbcf02cSchristos 	 * specific hints.
14598dbcf02cSchristos 	 *
14608dbcf02cSchristos 	 * SRTT = smoothed round-trip time
14618dbcf02cSchristos 	 * RTTVAR = round-trip time variation
14628dbcf02cSchristos 	 * RTO = retransmission timeout
14638dbcf02cSchristos 	 */
14648dbcf02cSchristos 
14658dbcf02cSchristos 	/*
14668dbcf02cSchristos 	 * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
14678dbcf02cSchristos 	 * initial retransmission and then double the RTO to provide back off
14688dbcf02cSchristos 	 * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
14698dbcf02cSchristos 	 * modified RTOmax.
14708dbcf02cSchristos 	 */
14718dbcf02cSchristos 	rto = 3;
14728dbcf02cSchristos 	for (i = 0; i < retransCount; i++) {
14738dbcf02cSchristos 		rto *= 2;
14748dbcf02cSchristos 		if (rto >= 20) {
14758dbcf02cSchristos 			rto = 20;
14768dbcf02cSchristos 			break;
14778dbcf02cSchristos 		}
14788dbcf02cSchristos 	}
14798dbcf02cSchristos 
14808dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
14818dbcf02cSchristos 		   "(from dynamic back off; retransCount=%d)",
14828dbcf02cSchristos 		   rto, retransCount);
14838dbcf02cSchristos 
14848dbcf02cSchristos 	return rto;
14858dbcf02cSchristos }
14868dbcf02cSchristos 
14878dbcf02cSchristos 
14888dbcf02cSchristos static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
14898dbcf02cSchristos {
14908dbcf02cSchristos 	const struct eap_hdr *hdr;
14918dbcf02cSchristos 	size_t plen;
14928dbcf02cSchristos 
14938dbcf02cSchristos 	/* parse rxResp, respId, respMethod */
1494*45d3cc13Schristos 	sm->rxResp = false;
1495*45d3cc13Schristos 	sm->rxInitiate = false;
14968dbcf02cSchristos 	sm->respId = -1;
14978dbcf02cSchristos 	sm->respMethod = EAP_TYPE_NONE;
14988dbcf02cSchristos 	sm->respVendor = EAP_VENDOR_IETF;
14998dbcf02cSchristos 	sm->respVendorMethod = EAP_TYPE_NONE;
15008dbcf02cSchristos 
15018dbcf02cSchristos 	if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
15028dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
15038dbcf02cSchristos 			   "len=%lu", resp,
15048dbcf02cSchristos 			   resp ? (unsigned long) wpabuf_len(resp) : 0);
15058dbcf02cSchristos 		return;
15068dbcf02cSchristos 	}
15078dbcf02cSchristos 
15088dbcf02cSchristos 	hdr = wpabuf_head(resp);
15098dbcf02cSchristos 	plen = be_to_host16(hdr->length);
15108dbcf02cSchristos 	if (plen > wpabuf_len(resp)) {
15118dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
15128dbcf02cSchristos 			   "(len=%lu plen=%lu)",
15138dbcf02cSchristos 			   (unsigned long) wpabuf_len(resp),
15148dbcf02cSchristos 			   (unsigned long) plen);
15158dbcf02cSchristos 		return;
15168dbcf02cSchristos 	}
15178dbcf02cSchristos 
15188dbcf02cSchristos 	sm->respId = hdr->identifier;
15198dbcf02cSchristos 
15208dbcf02cSchristos 	if (hdr->code == EAP_CODE_RESPONSE)
1521*45d3cc13Schristos 		sm->rxResp = true;
1522bb610346Schristos 	else if (hdr->code == EAP_CODE_INITIATE)
1523*45d3cc13Schristos 		sm->rxInitiate = true;
15248dbcf02cSchristos 
15258dbcf02cSchristos 	if (plen > sizeof(*hdr)) {
15268dbcf02cSchristos 		u8 *pos = (u8 *) (hdr + 1);
15278dbcf02cSchristos 		sm->respMethod = *pos++;
15288dbcf02cSchristos 		if (sm->respMethod == EAP_TYPE_EXPANDED) {
15298dbcf02cSchristos 			if (plen < sizeof(*hdr) + 8) {
15308dbcf02cSchristos 				wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
15318dbcf02cSchristos 					   "expanded EAP-Packet (plen=%lu)",
15328dbcf02cSchristos 					   (unsigned long) plen);
15338dbcf02cSchristos 				return;
15348dbcf02cSchristos 			}
15358dbcf02cSchristos 			sm->respVendor = WPA_GET_BE24(pos);
15368dbcf02cSchristos 			pos += 3;
15378dbcf02cSchristos 			sm->respVendorMethod = WPA_GET_BE32(pos);
15388dbcf02cSchristos 		}
15398dbcf02cSchristos 	}
15408dbcf02cSchristos 
1541bb610346Schristos 	wpa_printf(MSG_DEBUG,
1542bb610346Schristos 		   "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u",
1543bb610346Schristos 		   sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod,
1544bb610346Schristos 		   sm->respVendor, sm->respVendorMethod);
15458dbcf02cSchristos }
15468dbcf02cSchristos 
15478dbcf02cSchristos 
15488dbcf02cSchristos static int eap_sm_getId(const struct wpabuf *data)
15498dbcf02cSchristos {
15508dbcf02cSchristos 	const struct eap_hdr *hdr;
15518dbcf02cSchristos 
15528dbcf02cSchristos 	if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
15538dbcf02cSchristos 		return -1;
15548dbcf02cSchristos 
15558dbcf02cSchristos 	hdr = wpabuf_head(data);
15568dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
15578dbcf02cSchristos 	return hdr->identifier;
15588dbcf02cSchristos }
15598dbcf02cSchristos 
15608dbcf02cSchristos 
15618dbcf02cSchristos static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
15628dbcf02cSchristos {
15638dbcf02cSchristos 	struct wpabuf *msg;
15648dbcf02cSchristos 	struct eap_hdr *resp;
15658dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
15668dbcf02cSchristos 
15678dbcf02cSchristos 	msg = wpabuf_alloc(sizeof(*resp));
15688dbcf02cSchristos 	if (msg == NULL)
15698dbcf02cSchristos 		return NULL;
15708dbcf02cSchristos 	resp = wpabuf_put(msg, sizeof(*resp));
15718dbcf02cSchristos 	resp->code = EAP_CODE_SUCCESS;
15728dbcf02cSchristos 	resp->identifier = id;
15738dbcf02cSchristos 	resp->length = host_to_be16(sizeof(*resp));
15748dbcf02cSchristos 
15758dbcf02cSchristos 	return msg;
15768dbcf02cSchristos }
15778dbcf02cSchristos 
15788dbcf02cSchristos 
15798dbcf02cSchristos static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
15808dbcf02cSchristos {
15818dbcf02cSchristos 	struct wpabuf *msg;
15828dbcf02cSchristos 	struct eap_hdr *resp;
15838dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
15848dbcf02cSchristos 
15858dbcf02cSchristos 	msg = wpabuf_alloc(sizeof(*resp));
15868dbcf02cSchristos 	if (msg == NULL)
15878dbcf02cSchristos 		return NULL;
15888dbcf02cSchristos 	resp = wpabuf_put(msg, sizeof(*resp));
15898dbcf02cSchristos 	resp->code = EAP_CODE_FAILURE;
15908dbcf02cSchristos 	resp->identifier = id;
15918dbcf02cSchristos 	resp->length = host_to_be16(sizeof(*resp));
15928dbcf02cSchristos 
15938dbcf02cSchristos 	return msg;
15948dbcf02cSchristos }
15958dbcf02cSchristos 
15968dbcf02cSchristos 
15978dbcf02cSchristos static int eap_sm_nextId(struct eap_sm *sm, int id)
15988dbcf02cSchristos {
15998dbcf02cSchristos 	if (id < 0) {
16008dbcf02cSchristos 		/* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
16018dbcf02cSchristos 		 * random number */
16028dbcf02cSchristos 		id = rand() & 0xff;
16038dbcf02cSchristos 		if (id != sm->lastId)
16048dbcf02cSchristos 			return id;
16058dbcf02cSchristos 	}
16068dbcf02cSchristos 	return (id + 1) & 0xff;
16078dbcf02cSchristos }
16088dbcf02cSchristos 
16098dbcf02cSchristos 
16108dbcf02cSchristos /**
16118dbcf02cSchristos  * eap_sm_process_nak - Process EAP-Response/Nak
16128dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
16138dbcf02cSchristos  * @nak_list: Nak list (allowed methods) from the supplicant
16148dbcf02cSchristos  * @len: Length of nak_list in bytes
16158dbcf02cSchristos  *
16168dbcf02cSchristos  * This function is called when EAP-Response/Nak is received from the
16178dbcf02cSchristos  * supplicant. This can happen for both phase 1 and phase 2 authentications.
16188dbcf02cSchristos  */
16198dbcf02cSchristos void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
16208dbcf02cSchristos {
16218dbcf02cSchristos 	int i;
16228dbcf02cSchristos 	size_t j;
16238dbcf02cSchristos 
16248dbcf02cSchristos 	if (sm->user == NULL)
16258dbcf02cSchristos 		return;
16268dbcf02cSchristos 
16278dbcf02cSchristos 	wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
16288dbcf02cSchristos 		   "index %d)", sm->user_eap_method_index);
16298dbcf02cSchristos 
16308dbcf02cSchristos 	wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
16318dbcf02cSchristos 		    (u8 *) sm->user->methods,
16328dbcf02cSchristos 		    EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
16338dbcf02cSchristos 	wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
16348dbcf02cSchristos 		    nak_list, len);
16358dbcf02cSchristos 
16368dbcf02cSchristos 	i = sm->user_eap_method_index;
16378dbcf02cSchristos 	while (i < EAP_MAX_METHODS &&
16388dbcf02cSchristos 	       (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
16398dbcf02cSchristos 		sm->user->methods[i].method != EAP_TYPE_NONE)) {
16408dbcf02cSchristos 		if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
16418dbcf02cSchristos 			goto not_found;
16428dbcf02cSchristos 		for (j = 0; j < len; j++) {
16438dbcf02cSchristos 			if (nak_list[j] == sm->user->methods[i].method) {
16448dbcf02cSchristos 				break;
16458dbcf02cSchristos 			}
16468dbcf02cSchristos 		}
16478dbcf02cSchristos 
16488dbcf02cSchristos 		if (j < len) {
16498dbcf02cSchristos 			/* found */
16508dbcf02cSchristos 			i++;
16518dbcf02cSchristos 			continue;
16528dbcf02cSchristos 		}
16538dbcf02cSchristos 
16548dbcf02cSchristos 	not_found:
16558dbcf02cSchristos 		/* not found - remove from the list */
1656111b9fd8Schristos 		if (i + 1 < EAP_MAX_METHODS) {
1657111b9fd8Schristos 			os_memmove(&sm->user->methods[i],
1658111b9fd8Schristos 				   &sm->user->methods[i + 1],
16598dbcf02cSchristos 				   (EAP_MAX_METHODS - i - 1) *
16608dbcf02cSchristos 				   sizeof(sm->user->methods[0]));
1661111b9fd8Schristos 		}
16628dbcf02cSchristos 		sm->user->methods[EAP_MAX_METHODS - 1].vendor =
16638dbcf02cSchristos 			EAP_VENDOR_IETF;
16648dbcf02cSchristos 		sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
16658dbcf02cSchristos 	}
16668dbcf02cSchristos 
16678dbcf02cSchristos 	wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
16688dbcf02cSchristos 		    (u8 *) sm->user->methods, EAP_MAX_METHODS *
16698dbcf02cSchristos 		    sizeof(sm->user->methods[0]));
16708dbcf02cSchristos }
16718dbcf02cSchristos 
16728dbcf02cSchristos 
16738dbcf02cSchristos static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
16748dbcf02cSchristos 				 size_t len)
16758dbcf02cSchristos {
16768dbcf02cSchristos 	if (nak_list == NULL || sm == NULL || sm->user == NULL)
16778dbcf02cSchristos 		return;
16788dbcf02cSchristos 
16798dbcf02cSchristos 	if (sm->user->phase2) {
16808dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
16818dbcf02cSchristos 			   " info was selected - reject");
16828dbcf02cSchristos 		sm->decision = DECISION_FAILURE;
16838dbcf02cSchristos 		return;
16848dbcf02cSchristos 	}
16858dbcf02cSchristos 
16868dbcf02cSchristos 	eap_sm_process_nak(sm, nak_list, len);
16878dbcf02cSchristos }
16888dbcf02cSchristos 
16898dbcf02cSchristos 
1690*45d3cc13Schristos static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
16918dbcf02cSchristos {
1692*45d3cc13Schristos 	enum eap_type next;
16938dbcf02cSchristos 	int idx = sm->user_eap_method_index;
16948dbcf02cSchristos 
16958dbcf02cSchristos 	/* In theory, there should be no problems with starting
16968dbcf02cSchristos 	 * re-authentication with something else than EAP-Request/Identity and
16978dbcf02cSchristos 	 * this does indeed work with wpa_supplicant. However, at least Funk
16988dbcf02cSchristos 	 * Supplicant seemed to ignore re-auth if it skipped
16998dbcf02cSchristos 	 * EAP-Request/Identity.
17008dbcf02cSchristos 	 * Re-auth sets currentId == -1, so that can be used here to select
17018dbcf02cSchristos 	 * whether Identity needs to be requested again. */
17028dbcf02cSchristos 	if (sm->identity == NULL || sm->currentId == -1) {
17038dbcf02cSchristos 		*vendor = EAP_VENDOR_IETF;
17048dbcf02cSchristos 		next = EAP_TYPE_IDENTITY;
1705*45d3cc13Schristos 		sm->update_user = true;
17068dbcf02cSchristos 	} else if (sm->user && idx < EAP_MAX_METHODS &&
17078dbcf02cSchristos 		   (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
17088dbcf02cSchristos 		    sm->user->methods[idx].method != EAP_TYPE_NONE)) {
17098dbcf02cSchristos 		*vendor = sm->user->methods[idx].vendor;
17108dbcf02cSchristos 		next = sm->user->methods[idx].method;
17118dbcf02cSchristos 		sm->user_eap_method_index++;
17128dbcf02cSchristos 	} else {
17138dbcf02cSchristos 		*vendor = EAP_VENDOR_IETF;
17148dbcf02cSchristos 		next = EAP_TYPE_NONE;
17158dbcf02cSchristos 	}
17168dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
17178dbcf02cSchristos 		   *vendor, next);
17188dbcf02cSchristos 	return next;
17198dbcf02cSchristos }
17208dbcf02cSchristos 
17218dbcf02cSchristos 
17228dbcf02cSchristos static int eap_sm_Policy_getDecision(struct eap_sm *sm)
17238dbcf02cSchristos {
1724*45d3cc13Schristos 	if (!sm->cfg->eap_server && sm->identity && !sm->start_reauth) {
17258dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
17268dbcf02cSchristos 		return DECISION_PASSTHROUGH;
17278dbcf02cSchristos 	}
17288dbcf02cSchristos 
17298dbcf02cSchristos 	if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
17308dbcf02cSchristos 	    sm->m->isSuccess(sm, sm->eap_method_priv)) {
17318dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
17328dbcf02cSchristos 			   "SUCCESS");
1733*45d3cc13Schristos 		sm->update_user = true;
17348dbcf02cSchristos 		return DECISION_SUCCESS;
17358dbcf02cSchristos 	}
17368dbcf02cSchristos 
17378dbcf02cSchristos 	if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
17388dbcf02cSchristos 	    !sm->m->isSuccess(sm, sm->eap_method_priv)) {
17398dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
17408dbcf02cSchristos 			   "FAILURE");
1741*45d3cc13Schristos 		sm->update_user = true;
17428dbcf02cSchristos 		return DECISION_FAILURE;
17438dbcf02cSchristos 	}
17448dbcf02cSchristos 
17458dbcf02cSchristos 	if ((sm->user == NULL || sm->update_user) && sm->identity &&
17468dbcf02cSchristos 	    !sm->start_reauth) {
17478dbcf02cSchristos 		/*
17488dbcf02cSchristos 		 * Allow Identity method to be started once to allow identity
17498dbcf02cSchristos 		 * selection hint to be sent from the authentication server,
17508dbcf02cSchristos 		 * but prevent a loop of Identity requests by only allowing
17518dbcf02cSchristos 		 * this to happen once.
17528dbcf02cSchristos 		 */
17538dbcf02cSchristos 		int id_req = 0;
17548dbcf02cSchristos 		if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
17558dbcf02cSchristos 		    sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
17568dbcf02cSchristos 		    sm->user->methods[0].method == EAP_TYPE_IDENTITY)
17578dbcf02cSchristos 			id_req = 1;
17588dbcf02cSchristos 		if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
17598dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
17608dbcf02cSchristos 				   "found from database -> FAILURE");
17618dbcf02cSchristos 			return DECISION_FAILURE;
17628dbcf02cSchristos 		}
17638dbcf02cSchristos 		if (id_req && sm->user &&
17648dbcf02cSchristos 		    sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
17658dbcf02cSchristos 		    sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
17668dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
17678dbcf02cSchristos 				   "identity request loop -> FAILURE");
1768*45d3cc13Schristos 			sm->update_user = true;
17698dbcf02cSchristos 			return DECISION_FAILURE;
17708dbcf02cSchristos 		}
1771*45d3cc13Schristos 		sm->update_user = false;
17728dbcf02cSchristos 	}
1773*45d3cc13Schristos 	sm->start_reauth = false;
17748dbcf02cSchristos 
17758dbcf02cSchristos 	if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
17768dbcf02cSchristos 	    (sm->user->methods[sm->user_eap_method_index].vendor !=
17778dbcf02cSchristos 	     EAP_VENDOR_IETF ||
17788dbcf02cSchristos 	     sm->user->methods[sm->user_eap_method_index].method !=
17798dbcf02cSchristos 	     EAP_TYPE_NONE)) {
17808dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
17818dbcf02cSchristos 			   "available -> CONTINUE");
17828dbcf02cSchristos 		return DECISION_CONTINUE;
17838dbcf02cSchristos 	}
17848dbcf02cSchristos 
1785bb610346Schristos 	if (!sm->identity && eap_get_erp_send_reauth_start(sm) &&
1786bb610346Schristos 	    !sm->initiate_reauth_start_sent) {
1787bb610346Schristos 		wpa_printf(MSG_DEBUG,
1788bb610346Schristos 			   "EAP: getDecision: send EAP-Initiate/Re-auth-Start");
1789bb610346Schristos 		return DECISION_INITIATE_REAUTH_START;
1790bb610346Schristos 	}
1791bb610346Schristos 
17928dbcf02cSchristos 	if (sm->identity == NULL || sm->currentId == -1) {
17938dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
17948dbcf02cSchristos 			   "yet -> CONTINUE");
17958dbcf02cSchristos 		return DECISION_CONTINUE;
17968dbcf02cSchristos 	}
17978dbcf02cSchristos 
17988dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
17998dbcf02cSchristos 		   "FAILURE");
18008dbcf02cSchristos 	return DECISION_FAILURE;
18018dbcf02cSchristos }
18028dbcf02cSchristos 
18038dbcf02cSchristos 
1804*45d3cc13Schristos static bool eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method)
18058dbcf02cSchristos {
1806*45d3cc13Schristos 	return method == EAP_TYPE_IDENTITY;
18078dbcf02cSchristos }
18088dbcf02cSchristos 
18098dbcf02cSchristos 
18108dbcf02cSchristos /**
18118dbcf02cSchristos  * eap_server_sm_step - Step EAP server state machine
18128dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
18138dbcf02cSchristos  * Returns: 1 if EAP state was changed or 0 if not
18148dbcf02cSchristos  *
18158dbcf02cSchristos  * This function advances EAP state machine to a new state to match with the
18168dbcf02cSchristos  * current variables. This should be called whenever variables used by the EAP
18178dbcf02cSchristos  * state machine have changed.
18188dbcf02cSchristos  */
18198dbcf02cSchristos int eap_server_sm_step(struct eap_sm *sm)
18208dbcf02cSchristos {
18218dbcf02cSchristos 	int res = 0;
18228dbcf02cSchristos 	do {
1823*45d3cc13Schristos 		sm->changed = false;
18248dbcf02cSchristos 		SM_STEP_RUN(EAP);
18258dbcf02cSchristos 		if (sm->changed)
18268dbcf02cSchristos 			res = 1;
18278dbcf02cSchristos 	} while (sm->changed);
18288dbcf02cSchristos 	return res;
18298dbcf02cSchristos }
18308dbcf02cSchristos 
18318dbcf02cSchristos 
1832460bb4fcSchristos void eap_user_free(struct eap_user *user)
18338dbcf02cSchristos {
18348dbcf02cSchristos 	if (user == NULL)
18358dbcf02cSchristos 		return;
18363c260e60Schristos 	bin_clear_free(user->password, user->password_len);
18378dbcf02cSchristos 	user->password = NULL;
1838be6b3c4dSchristos 	bin_clear_free(user->salt, user->salt_len);
1839be6b3c4dSchristos 	user->salt = NULL;
18408dbcf02cSchristos 	os_free(user);
18418dbcf02cSchristos }
18428dbcf02cSchristos 
18438dbcf02cSchristos 
18448dbcf02cSchristos /**
18458dbcf02cSchristos  * eap_server_sm_init - Allocate and initialize EAP server state machine
18468dbcf02cSchristos  * @eapol_ctx: Context data to be used with eapol_cb calls
18478dbcf02cSchristos  * @eapol_cb: Pointer to EAPOL callback functions
18488dbcf02cSchristos  * @conf: EAP configuration
18498dbcf02cSchristos  * Returns: Pointer to the allocated EAP state machine or %NULL on failure
18508dbcf02cSchristos  *
18518dbcf02cSchristos  * This function allocates and initializes an EAP state machine.
18528dbcf02cSchristos  */
18538dbcf02cSchristos struct eap_sm * eap_server_sm_init(void *eapol_ctx,
1854ecc36642Schristos 				   const struct eapol_callbacks *eapol_cb,
1855*45d3cc13Schristos 				   const struct eap_config *conf,
1856*45d3cc13Schristos 				   const struct eap_session_data *sess)
18578dbcf02cSchristos {
18588dbcf02cSchristos 	struct eap_sm *sm;
18598dbcf02cSchristos 
18608dbcf02cSchristos 	sm = os_zalloc(sizeof(*sm));
18618dbcf02cSchristos 	if (sm == NULL)
18628dbcf02cSchristos 		return NULL;
18638dbcf02cSchristos 	sm->eapol_ctx = eapol_ctx;
18648dbcf02cSchristos 	sm->eapol_cb = eapol_cb;
18658dbcf02cSchristos 	sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
1866*45d3cc13Schristos 	sm->cfg = conf;
1867*45d3cc13Schristos 	if (sess->assoc_wps_ie)
1868*45d3cc13Schristos 		sm->assoc_wps_ie = wpabuf_dup(sess->assoc_wps_ie);
1869*45d3cc13Schristos 	if (sess->assoc_p2p_ie)
1870*45d3cc13Schristos 		sm->assoc_p2p_ie = wpabuf_dup(sess->assoc_p2p_ie);
1871*45d3cc13Schristos 	if (sess->peer_addr)
1872*45d3cc13Schristos 		os_memcpy(sm->peer_addr, sess->peer_addr, ETH_ALEN);
18733c260e60Schristos #ifdef CONFIG_TESTING_OPTIONS
1874*45d3cc13Schristos 	sm->tls_test_flags = sess->tls_test_flags;
18753c260e60Schristos #endif /* CONFIG_TESTING_OPTIONS */
18768dbcf02cSchristos 
18778dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
18788dbcf02cSchristos 
18798dbcf02cSchristos 	return sm;
18808dbcf02cSchristos }
18818dbcf02cSchristos 
18828dbcf02cSchristos 
18838dbcf02cSchristos /**
18848dbcf02cSchristos  * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
18858dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
18868dbcf02cSchristos  *
18878dbcf02cSchristos  * This function deinitializes EAP state machine and frees all allocated
18888dbcf02cSchristos  * resources.
18898dbcf02cSchristos  */
18908dbcf02cSchristos void eap_server_sm_deinit(struct eap_sm *sm)
18918dbcf02cSchristos {
18928dbcf02cSchristos 	if (sm == NULL)
18938dbcf02cSchristos 		return;
18948dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
18958dbcf02cSchristos 	if (sm->m && sm->eap_method_priv)
18968dbcf02cSchristos 		sm->m->reset(sm, sm->eap_method_priv);
18978dbcf02cSchristos 	wpabuf_free(sm->eap_if.eapReqData);
18983c260e60Schristos 	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
1899bb610346Schristos 	os_free(sm->eap_if.eapSessionId);
19008dbcf02cSchristos 	wpabuf_free(sm->lastReqData);
19018dbcf02cSchristos 	wpabuf_free(sm->eap_if.eapRespData);
19028dbcf02cSchristos 	os_free(sm->identity);
1903be6b3c4dSchristos 	os_free(sm->serial_num);
19048dbcf02cSchristos 	wpabuf_free(sm->eap_if.aaaEapReqData);
19058dbcf02cSchristos 	wpabuf_free(sm->eap_if.aaaEapRespData);
19063c260e60Schristos 	bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen);
19078dbcf02cSchristos 	eap_user_free(sm->user);
19088dbcf02cSchristos 	wpabuf_free(sm->assoc_wps_ie);
1909111b9fd8Schristos 	wpabuf_free(sm->assoc_p2p_ie);
19108dbcf02cSchristos 	os_free(sm);
19118dbcf02cSchristos }
19128dbcf02cSchristos 
19138dbcf02cSchristos 
19148dbcf02cSchristos /**
19158dbcf02cSchristos  * eap_sm_notify_cached - Notify EAP state machine of cached PMK
19168dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
19178dbcf02cSchristos  *
19188dbcf02cSchristos  * This function is called when PMKSA caching is used to skip EAP
19198dbcf02cSchristos  * authentication.
19208dbcf02cSchristos  */
19218dbcf02cSchristos void eap_sm_notify_cached(struct eap_sm *sm)
19228dbcf02cSchristos {
19238dbcf02cSchristos 	if (sm == NULL)
19248dbcf02cSchristos 		return;
19258dbcf02cSchristos 
19268dbcf02cSchristos 	sm->EAP_state = EAP_SUCCESS;
19278dbcf02cSchristos }
19288dbcf02cSchristos 
19298dbcf02cSchristos 
19308dbcf02cSchristos /**
19318dbcf02cSchristos  * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
19328dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
19338dbcf02cSchristos  *
19348dbcf02cSchristos  * This function is called when data for a pending EAP-Request is received.
19358dbcf02cSchristos  */
19368dbcf02cSchristos void eap_sm_pending_cb(struct eap_sm *sm)
19378dbcf02cSchristos {
19388dbcf02cSchristos 	if (sm == NULL)
19398dbcf02cSchristos 		return;
19408dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
19418dbcf02cSchristos 	if (sm->method_pending == METHOD_PENDING_WAIT)
19428dbcf02cSchristos 		sm->method_pending = METHOD_PENDING_CONT;
19438dbcf02cSchristos }
19448dbcf02cSchristos 
19458dbcf02cSchristos 
19468dbcf02cSchristos /**
19478dbcf02cSchristos  * eap_sm_method_pending - Query whether EAP method is waiting for pending data
19488dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
19498dbcf02cSchristos  * Returns: 1 if method is waiting for pending data or 0 if not
19508dbcf02cSchristos  */
19518dbcf02cSchristos int eap_sm_method_pending(struct eap_sm *sm)
19528dbcf02cSchristos {
19538dbcf02cSchristos 	if (sm == NULL)
19548dbcf02cSchristos 		return 0;
19558dbcf02cSchristos 	return sm->method_pending == METHOD_PENDING_WAIT;
19568dbcf02cSchristos }
19578dbcf02cSchristos 
19588dbcf02cSchristos 
19598dbcf02cSchristos /**
19608dbcf02cSchristos  * eap_get_identity - Get the user identity (from EAP-Response/Identity)
19618dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
19628dbcf02cSchristos  * @len: Buffer for returning identity length
19638dbcf02cSchristos  * Returns: Pointer to the user identity or %NULL if not available
19648dbcf02cSchristos  */
19658dbcf02cSchristos const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
19668dbcf02cSchristos {
19678dbcf02cSchristos 	*len = sm->identity_len;
19688dbcf02cSchristos 	return sm->identity;
19698dbcf02cSchristos }
19708dbcf02cSchristos 
19718dbcf02cSchristos 
19728dbcf02cSchristos /**
1973be6b3c4dSchristos  * eap_get_serial_num - Get the serial number of user certificate
1974be6b3c4dSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1975be6b3c4dSchristos  * Returns: Pointer to the serial number or %NULL if not available
1976be6b3c4dSchristos  */
1977be6b3c4dSchristos const char * eap_get_serial_num(struct eap_sm *sm)
1978be6b3c4dSchristos {
1979be6b3c4dSchristos 	return sm->serial_num;
1980be6b3c4dSchristos }
1981be6b3c4dSchristos 
1982be6b3c4dSchristos 
1983460bb4fcSchristos /**
1984460bb4fcSchristos  * eap_get_method - Get the used EAP method
1985460bb4fcSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1986460bb4fcSchristos  * Returns: Pointer to the method name or %NULL if not available
1987460bb4fcSchristos  */
1988460bb4fcSchristos const char * eap_get_method(struct eap_sm *sm)
1989460bb4fcSchristos {
1990460bb4fcSchristos 	if (!sm || !sm->m)
1991460bb4fcSchristos 		return NULL;
1992460bb4fcSchristos 	return sm->m->name;
1993460bb4fcSchristos }
1994460bb4fcSchristos 
1995460bb4fcSchristos 
1996460bb4fcSchristos /**
1997460bb4fcSchristos  * eap_get_imsi - Get IMSI of the user
1998460bb4fcSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1999460bb4fcSchristos  * Returns: Pointer to IMSI or %NULL if not available
2000460bb4fcSchristos  */
2001460bb4fcSchristos const char * eap_get_imsi(struct eap_sm *sm)
2002460bb4fcSchristos {
2003460bb4fcSchristos 	if (!sm || sm->imsi[0] == '\0')
2004460bb4fcSchristos 		return NULL;
2005460bb4fcSchristos 	return sm->imsi;
2006460bb4fcSchristos }
2007460bb4fcSchristos 
2008460bb4fcSchristos 
2009be6b3c4dSchristos void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len)
2010be6b3c4dSchristos {
2011be6b3c4dSchristos #ifdef CONFIG_ERP
2012be6b3c4dSchristos 	const struct eap_hdr *hdr;
2013be6b3c4dSchristos 	const u8 *pos, *end;
2014be6b3c4dSchristos 	struct erp_tlvs parse;
2015be6b3c4dSchristos 
2016be6b3c4dSchristos 	if (len < sizeof(*hdr) + 1)
2017be6b3c4dSchristos 		return;
2018be6b3c4dSchristos 	hdr = (const struct eap_hdr *) eap;
2019be6b3c4dSchristos 	end = eap + len;
2020be6b3c4dSchristos 	pos = (const u8 *) (hdr + 1);
2021be6b3c4dSchristos 	if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH)
2022be6b3c4dSchristos 		return;
2023be6b3c4dSchristos 	pos++;
2024be6b3c4dSchristos 	if (pos + 3 > end)
2025be6b3c4dSchristos 		return;
2026be6b3c4dSchristos 
2027be6b3c4dSchristos 	/* Skip Flags and SEQ */
2028be6b3c4dSchristos 	pos += 3;
2029be6b3c4dSchristos 
2030be6b3c4dSchristos 	if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname)
2031be6b3c4dSchristos 		return;
2032be6b3c4dSchristos 	wpa_hexdump_ascii(MSG_DEBUG,
2033be6b3c4dSchristos 			  "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI",
2034be6b3c4dSchristos 			  parse.keyname, parse.keyname_len);
2035be6b3c4dSchristos 	os_free(sm->identity);
2036be6b3c4dSchristos 	sm->identity = os_malloc(parse.keyname_len);
2037be6b3c4dSchristos 	if (sm->identity) {
2038be6b3c4dSchristos 		os_memcpy(sm->identity, parse.keyname, parse.keyname_len);
2039be6b3c4dSchristos 		sm->identity_len = parse.keyname_len;
2040be6b3c4dSchristos 	} else {
2041be6b3c4dSchristos 		sm->identity_len = 0;
2042be6b3c4dSchristos 	}
2043be6b3c4dSchristos #endif /* CONFIG_ERP */
2044be6b3c4dSchristos }
2045be6b3c4dSchristos 
2046be6b3c4dSchristos 
2047be6b3c4dSchristos /**
20488dbcf02cSchristos  * eap_get_interface - Get pointer to EAP-EAPOL interface data
20498dbcf02cSchristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
20508dbcf02cSchristos  * Returns: Pointer to the EAP-EAPOL interface data
20518dbcf02cSchristos  */
20528dbcf02cSchristos struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
20538dbcf02cSchristos {
20548dbcf02cSchristos 	return &sm->eap_if;
20558dbcf02cSchristos }
2056111b9fd8Schristos 
2057111b9fd8Schristos 
2058111b9fd8Schristos /**
2059111b9fd8Schristos  * eap_server_clear_identity - Clear EAP identity information
2060111b9fd8Schristos  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
2061111b9fd8Schristos  *
2062111b9fd8Schristos  * This function can be used to clear the EAP identity information in the EAP
2063111b9fd8Schristos  * server context. This allows the EAP/Identity method to be used again after
2064111b9fd8Schristos  * EAPOL-Start or EAPOL-Logoff.
2065111b9fd8Schristos  */
2066111b9fd8Schristos void eap_server_clear_identity(struct eap_sm *sm)
2067111b9fd8Schristos {
2068111b9fd8Schristos 	os_free(sm->identity);
2069111b9fd8Schristos 	sm->identity = NULL;
2070111b9fd8Schristos }
2071ecc36642Schristos 
2072ecc36642Schristos 
2073ecc36642Schristos #ifdef CONFIG_TESTING_OPTIONS
2074ecc36642Schristos void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
2075ecc36642Schristos 				   const u8 *username, size_t username_len,
2076ecc36642Schristos 				   const u8 *challenge, const u8 *response)
2077ecc36642Schristos {
2078ecc36642Schristos 	char hex_challenge[30], hex_response[90], user[100];
2079ecc36642Schristos 
2080ecc36642Schristos 	/* Print out Challenge and Response in format supported by asleap. */
2081ecc36642Schristos 	if (username)
2082ecc36642Schristos 		printf_encode(user, sizeof(user), username, username_len);
2083ecc36642Schristos 	else
2084ecc36642Schristos 		user[0] = '\0';
2085ecc36642Schristos 	wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge),
2086ecc36642Schristos 			     challenge, sizeof(challenge), ':');
2087ecc36642Schristos 	wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24,
2088ecc36642Schristos 			     ':');
2089ecc36642Schristos 	wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s",
2090ecc36642Schristos 		   source, user, hex_challenge, hex_response);
2091ecc36642Schristos }
2092ecc36642Schristos #endif /* CONFIG_TESTING_OPTIONS */
2093*45d3cc13Schristos 
2094*45d3cc13Schristos 
2095*45d3cc13Schristos void eap_server_config_free(struct eap_config *cfg)
2096*45d3cc13Schristos {
2097*45d3cc13Schristos 	if (!cfg)
2098*45d3cc13Schristos 		return;
2099*45d3cc13Schristos 	os_free(cfg->pac_opaque_encr_key);
2100*45d3cc13Schristos 	os_free(cfg->eap_fast_a_id);
2101*45d3cc13Schristos 	os_free(cfg->eap_fast_a_id_info);
2102*45d3cc13Schristos 	os_free(cfg->server_id);
2103*45d3cc13Schristos 	os_free(cfg);
2104*45d3cc13Schristos }
2105