xref: /freebsd/contrib/wpa/src/eapol_supp/eapol_supp_sm.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * EAPOL supplicant state machines
3f05cddf9SRui Paulo  * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "state_machine.h"
1339beb93cSSam Leffler #include "wpabuf.h"
14e28a4053SRui Paulo #include "eloop.h"
15e28a4053SRui Paulo #include "crypto/crypto.h"
16e28a4053SRui Paulo #include "crypto/md5.h"
17e28a4053SRui Paulo #include "common/eapol_common.h"
18e28a4053SRui Paulo #include "eap_peer/eap.h"
1985732ac8SCy Schubert #include "eap_peer/eap_config.h"
205b9c547cSRui Paulo #include "eap_peer/eap_proxy.h"
21e28a4053SRui Paulo #include "eapol_supp_sm.h"
2239beb93cSSam Leffler 
2339beb93cSSam Leffler #define STATE_MACHINE_DATA struct eapol_sm
2439beb93cSSam Leffler #define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
2539beb93cSSam Leffler 
2639beb93cSSam Leffler 
2739beb93cSSam Leffler /* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
2839beb93cSSam Leffler 
2939beb93cSSam Leffler /**
3039beb93cSSam Leffler  * struct eapol_sm - Internal data for EAPOL state machines
3139beb93cSSam Leffler  */
3239beb93cSSam Leffler struct eapol_sm {
3339beb93cSSam Leffler 	/* Timers */
3439beb93cSSam Leffler 	unsigned int authWhile;
3539beb93cSSam Leffler 	unsigned int heldWhile;
3639beb93cSSam Leffler 	unsigned int startWhen;
3739beb93cSSam Leffler 	unsigned int idleWhile; /* for EAP state machine */
3839beb93cSSam Leffler 	int timer_tick_enabled;
3939beb93cSSam Leffler 
4039beb93cSSam Leffler 	/* Global variables */
41c1d255d3SCy Schubert 	bool eapFail;
42c1d255d3SCy Schubert 	bool eapolEap;
43c1d255d3SCy Schubert 	bool eapSuccess;
44c1d255d3SCy Schubert 	bool initialize;
45c1d255d3SCy Schubert 	bool keyDone;
46c1d255d3SCy Schubert 	bool keyRun;
4739beb93cSSam Leffler 	PortControl portControl;
48c1d255d3SCy Schubert 	bool portEnabled;
4939beb93cSSam Leffler 	PortStatus suppPortStatus;  /* dot1xSuppControlledPortStatus */
50c1d255d3SCy Schubert 	bool portValid;
51c1d255d3SCy Schubert 	bool suppAbort;
52c1d255d3SCy Schubert 	bool suppFail;
53c1d255d3SCy Schubert 	bool suppStart;
54c1d255d3SCy Schubert 	bool suppSuccess;
55c1d255d3SCy Schubert 	bool suppTimeout;
5639beb93cSSam Leffler 
5739beb93cSSam Leffler 	/* Supplicant PAE state machine */
5839beb93cSSam Leffler 	enum {
5939beb93cSSam Leffler 		SUPP_PAE_UNKNOWN = 0,
6039beb93cSSam Leffler 		SUPP_PAE_DISCONNECTED = 1,
6139beb93cSSam Leffler 		SUPP_PAE_LOGOFF = 2,
6239beb93cSSam Leffler 		SUPP_PAE_CONNECTING = 3,
6339beb93cSSam Leffler 		SUPP_PAE_AUTHENTICATING = 4,
6439beb93cSSam Leffler 		SUPP_PAE_AUTHENTICATED = 5,
6539beb93cSSam Leffler 		/* unused(6) */
6639beb93cSSam Leffler 		SUPP_PAE_HELD = 7,
6739beb93cSSam Leffler 		SUPP_PAE_RESTART = 8,
6839beb93cSSam Leffler 		SUPP_PAE_S_FORCE_AUTH = 9,
6939beb93cSSam Leffler 		SUPP_PAE_S_FORCE_UNAUTH = 10
7039beb93cSSam Leffler 	} SUPP_PAE_state; /* dot1xSuppPaeState */
7139beb93cSSam Leffler 	/* Variables */
72c1d255d3SCy Schubert 	bool userLogoff;
73c1d255d3SCy Schubert 	bool logoffSent;
7439beb93cSSam Leffler 	unsigned int startCount;
75c1d255d3SCy Schubert 	bool eapRestart;
7639beb93cSSam Leffler 	PortControl sPortMode;
7739beb93cSSam Leffler 	/* Constants */
7839beb93cSSam Leffler 	unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
7939beb93cSSam Leffler 	unsigned int startPeriod; /* dot1xSuppStartPeriod */
8039beb93cSSam Leffler 	unsigned int maxStart; /* dot1xSuppMaxStart */
8139beb93cSSam Leffler 
8239beb93cSSam Leffler 	/* Key Receive state machine */
8339beb93cSSam Leffler 	enum {
8439beb93cSSam Leffler 		KEY_RX_UNKNOWN = 0,
8539beb93cSSam Leffler 		KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
8639beb93cSSam Leffler 	} KEY_RX_state;
8739beb93cSSam Leffler 	/* Variables */
88c1d255d3SCy Schubert 	bool rxKey;
8939beb93cSSam Leffler 
9039beb93cSSam Leffler 	/* Supplicant Backend state machine */
9139beb93cSSam Leffler 	enum {
9239beb93cSSam Leffler 		SUPP_BE_UNKNOWN = 0,
9339beb93cSSam Leffler 		SUPP_BE_INITIALIZE = 1,
9439beb93cSSam Leffler 		SUPP_BE_IDLE = 2,
9539beb93cSSam Leffler 		SUPP_BE_REQUEST = 3,
9639beb93cSSam Leffler 		SUPP_BE_RECEIVE = 4,
9739beb93cSSam Leffler 		SUPP_BE_RESPONSE = 5,
9839beb93cSSam Leffler 		SUPP_BE_FAIL = 6,
9939beb93cSSam Leffler 		SUPP_BE_TIMEOUT = 7,
10039beb93cSSam Leffler 		SUPP_BE_SUCCESS = 8
10139beb93cSSam Leffler 	} SUPP_BE_state; /* dot1xSuppBackendPaeState */
10239beb93cSSam Leffler 	/* Variables */
103c1d255d3SCy Schubert 	bool eapNoResp;
104c1d255d3SCy Schubert 	bool eapReq;
105c1d255d3SCy Schubert 	bool eapResp;
10639beb93cSSam Leffler 	/* Constants */
10739beb93cSSam Leffler 	unsigned int authPeriod; /* dot1xSuppAuthPeriod */
10839beb93cSSam Leffler 
10939beb93cSSam Leffler 	/* Statistics */
11039beb93cSSam Leffler 	unsigned int dot1xSuppEapolFramesRx;
11139beb93cSSam Leffler 	unsigned int dot1xSuppEapolFramesTx;
11239beb93cSSam Leffler 	unsigned int dot1xSuppEapolStartFramesTx;
11339beb93cSSam Leffler 	unsigned int dot1xSuppEapolLogoffFramesTx;
11439beb93cSSam Leffler 	unsigned int dot1xSuppEapolRespFramesTx;
11539beb93cSSam Leffler 	unsigned int dot1xSuppEapolReqIdFramesRx;
11639beb93cSSam Leffler 	unsigned int dot1xSuppEapolReqFramesRx;
11739beb93cSSam Leffler 	unsigned int dot1xSuppInvalidEapolFramesRx;
11839beb93cSSam Leffler 	unsigned int dot1xSuppEapLengthErrorFramesRx;
11939beb93cSSam Leffler 	unsigned int dot1xSuppLastEapolFrameVersion;
12039beb93cSSam Leffler 	unsigned char dot1xSuppLastEapolFrameSource[6];
12139beb93cSSam Leffler 
12239beb93cSSam Leffler 	/* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
123c1d255d3SCy Schubert 	bool changed;
12439beb93cSSam Leffler 	struct eap_sm *eap;
12539beb93cSSam Leffler 	struct eap_peer_config *config;
126c1d255d3SCy Schubert 	bool initial_req;
12739beb93cSSam Leffler 	u8 *last_rx_key;
12839beb93cSSam Leffler 	size_t last_rx_key_len;
12939beb93cSSam Leffler 	struct wpabuf *eapReqData; /* for EAP */
130c1d255d3SCy Schubert 	bool altAccept; /* for EAP */
131c1d255d3SCy Schubert 	bool altReject; /* for EAP */
132c1d255d3SCy Schubert 	bool eapTriggerStart;
133c1d255d3SCy Schubert 	bool replay_counter_valid;
13439beb93cSSam Leffler 	u8 last_replay_counter[16];
13539beb93cSSam Leffler 	struct eapol_config conf;
13639beb93cSSam Leffler 	struct eapol_ctx *ctx;
13739beb93cSSam Leffler 	enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
13839beb93cSSam Leffler 		cb_status;
139c1d255d3SCy Schubert 	bool cached_pmk;
14039beb93cSSam Leffler 
141c1d255d3SCy Schubert 	bool unicast_key_received, broadcast_key_received;
1425b9c547cSRui Paulo 
143c1d255d3SCy Schubert 	bool force_authorized_update;
1445b9c547cSRui Paulo 
1455b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
146c1d255d3SCy Schubert 	bool use_eap_proxy;
1475b9c547cSRui Paulo 	struct eap_proxy_sm *eap_proxy;
1485b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
14939beb93cSSam Leffler };
15039beb93cSSam Leffler 
15139beb93cSSam Leffler 
15239beb93cSSam Leffler static void eapol_sm_txLogoff(struct eapol_sm *sm);
15339beb93cSSam Leffler static void eapol_sm_txStart(struct eapol_sm *sm);
15439beb93cSSam Leffler static void eapol_sm_processKey(struct eapol_sm *sm);
15539beb93cSSam Leffler static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
15639beb93cSSam Leffler static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
15739beb93cSSam Leffler static void eapol_sm_abortSupp(struct eapol_sm *sm);
15839beb93cSSam Leffler static void eapol_sm_abort_cached(struct eapol_sm *sm);
15939beb93cSSam Leffler static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
160e28a4053SRui Paulo static void eapol_sm_set_port_authorized(struct eapol_sm *sm);
161e28a4053SRui Paulo static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm);
16239beb93cSSam Leffler 
16339beb93cSSam Leffler 
16439beb93cSSam Leffler /* Port Timers state machine - implemented as a function that will be called
16539beb93cSSam Leffler  * once a second as a registered event loop timeout */
eapol_port_timers_tick(void * eloop_ctx,void * timeout_ctx)16639beb93cSSam Leffler static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
16739beb93cSSam Leffler {
16839beb93cSSam Leffler 	struct eapol_sm *sm = timeout_ctx;
16939beb93cSSam Leffler 
17039beb93cSSam Leffler 	if (sm->authWhile > 0) {
17139beb93cSSam Leffler 		sm->authWhile--;
17239beb93cSSam Leffler 		if (sm->authWhile == 0)
17339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
17439beb93cSSam Leffler 	}
17539beb93cSSam Leffler 	if (sm->heldWhile > 0) {
17639beb93cSSam Leffler 		sm->heldWhile--;
17739beb93cSSam Leffler 		if (sm->heldWhile == 0)
17839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
17939beb93cSSam Leffler 	}
18039beb93cSSam Leffler 	if (sm->startWhen > 0) {
18139beb93cSSam Leffler 		sm->startWhen--;
18239beb93cSSam Leffler 		if (sm->startWhen == 0)
18339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
18439beb93cSSam Leffler 	}
18539beb93cSSam Leffler 	if (sm->idleWhile > 0) {
18639beb93cSSam Leffler 		sm->idleWhile--;
18739beb93cSSam Leffler 		if (sm->idleWhile == 0)
18839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
18939beb93cSSam Leffler 	}
19039beb93cSSam Leffler 
19139beb93cSSam Leffler 	if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
1924bc52338SCy Schubert 		if (eloop_register_timeout(1, 0, eapol_port_timers_tick,
1934bc52338SCy Schubert 					   eloop_ctx, sm) < 0)
1944bc52338SCy Schubert 			sm->timer_tick_enabled = 0;
19539beb93cSSam Leffler 	} else {
19639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
19739beb93cSSam Leffler 		sm->timer_tick_enabled = 0;
19839beb93cSSam Leffler 	}
19939beb93cSSam Leffler 	eapol_sm_step(sm);
20039beb93cSSam Leffler }
20139beb93cSSam Leffler 
20239beb93cSSam Leffler 
eapol_sm_confirm_auth(struct eapol_sm * sm)203c1d255d3SCy Schubert static int eapol_sm_confirm_auth(struct eapol_sm *sm)
204c1d255d3SCy Schubert {
205c1d255d3SCy Schubert 	if (!sm->ctx->confirm_auth_cb)
206c1d255d3SCy Schubert 		return 0;
207c1d255d3SCy Schubert 
208c1d255d3SCy Schubert 	return sm->ctx->confirm_auth_cb(sm->ctx->ctx);
209c1d255d3SCy Schubert }
210c1d255d3SCy Schubert 
211c1d255d3SCy Schubert 
eapol_enable_timer_tick(struct eapol_sm * sm)21239beb93cSSam Leffler static void eapol_enable_timer_tick(struct eapol_sm *sm)
21339beb93cSSam Leffler {
21439beb93cSSam Leffler 	if (sm->timer_tick_enabled)
21539beb93cSSam Leffler 		return;
21639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
21739beb93cSSam Leffler 	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
2184bc52338SCy Schubert 	if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0)
2194bc52338SCy Schubert 		sm->timer_tick_enabled = 1;
22039beb93cSSam Leffler }
22139beb93cSSam Leffler 
22239beb93cSSam Leffler 
SM_STATE(SUPP_PAE,LOGOFF)22339beb93cSSam Leffler SM_STATE(SUPP_PAE, LOGOFF)
22439beb93cSSam Leffler {
22539beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, LOGOFF);
22639beb93cSSam Leffler 	eapol_sm_txLogoff(sm);
227c1d255d3SCy Schubert 	sm->logoffSent = true;
228e28a4053SRui Paulo 	eapol_sm_set_port_unauthorized(sm);
22939beb93cSSam Leffler }
23039beb93cSSam Leffler 
23139beb93cSSam Leffler 
SM_STATE(SUPP_PAE,DISCONNECTED)23239beb93cSSam Leffler SM_STATE(SUPP_PAE, DISCONNECTED)
23339beb93cSSam Leffler {
23439beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, DISCONNECTED);
23539beb93cSSam Leffler 	sm->sPortMode = Auto;
23639beb93cSSam Leffler 	sm->startCount = 0;
237c1d255d3SCy Schubert 	sm->eapTriggerStart = false;
238c1d255d3SCy Schubert 	sm->logoffSent = false;
239e28a4053SRui Paulo 	eapol_sm_set_port_unauthorized(sm);
240c1d255d3SCy Schubert 	sm->suppAbort = true;
24139beb93cSSam Leffler 
242c1d255d3SCy Schubert 	sm->unicast_key_received = false;
243c1d255d3SCy Schubert 	sm->broadcast_key_received = false;
244f05cddf9SRui Paulo 
245f05cddf9SRui Paulo 	/*
246f05cddf9SRui Paulo 	 * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so
247f05cddf9SRui Paulo 	 * allows the timer tick to be stopped more quickly when the port is
248f05cddf9SRui Paulo 	 * not enabled. Since this variable is used only within HELD state,
249f05cddf9SRui Paulo 	 * clearing it on initialization does not change actual state machine
250f05cddf9SRui Paulo 	 * behavior.
251f05cddf9SRui Paulo 	 */
252f05cddf9SRui Paulo 	sm->heldWhile = 0;
25339beb93cSSam Leffler }
25439beb93cSSam Leffler 
25539beb93cSSam Leffler 
SM_STATE(SUPP_PAE,CONNECTING)25639beb93cSSam Leffler SM_STATE(SUPP_PAE, CONNECTING)
25739beb93cSSam Leffler {
258325151a3SRui Paulo 	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING ||
259325151a3SRui Paulo 		sm->SUPP_PAE_state == SUPP_PAE_HELD;
26039beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, CONNECTING);
2615b9c547cSRui Paulo 
2625b9c547cSRui Paulo 	if (sm->eapTriggerStart)
2635b9c547cSRui Paulo 		send_start = 1;
26485732ac8SCy Schubert 	if (sm->ctx->preauth)
26585732ac8SCy Schubert 		send_start = 1;
266c1d255d3SCy Schubert 	sm->eapTriggerStart = false;
2675b9c547cSRui Paulo 
26839beb93cSSam Leffler 	if (send_start) {
26939beb93cSSam Leffler 		sm->startWhen = sm->startPeriod;
27039beb93cSSam Leffler 		sm->startCount++;
27139beb93cSSam Leffler 	} else {
27239beb93cSSam Leffler 		/*
27339beb93cSSam Leffler 		 * Do not send EAPOL-Start immediately since in most cases,
27439beb93cSSam Leffler 		 * Authenticator is going to start authentication immediately
27539beb93cSSam Leffler 		 * after association and an extra EAPOL-Start is just going to
27639beb93cSSam Leffler 		 * delay authentication. Use a short timeout to send the first
27739beb93cSSam Leffler 		 * EAPOL-Start if Authenticator does not start authentication.
27839beb93cSSam Leffler 		 */
2795b9c547cSRui Paulo 		if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) {
2803157ba21SRui Paulo 			/* Reduce latency on starting WPS negotiation. */
2815b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG,
2825b9c547cSRui Paulo 				   "EAPOL: Using shorter startWhen for WPS");
2833157ba21SRui Paulo 			sm->startWhen = 1;
2845b9c547cSRui Paulo 		} else {
2855b9c547cSRui Paulo 			sm->startWhen = 2;
2865b9c547cSRui Paulo 		}
28739beb93cSSam Leffler 	}
28839beb93cSSam Leffler 	eapol_enable_timer_tick(sm);
289c1d255d3SCy Schubert 	sm->eapolEap = false;
29039beb93cSSam Leffler 	if (send_start)
29139beb93cSSam Leffler 		eapol_sm_txStart(sm);
29239beb93cSSam Leffler }
29339beb93cSSam Leffler 
29439beb93cSSam Leffler 
SM_STATE(SUPP_PAE,AUTHENTICATING)29539beb93cSSam Leffler SM_STATE(SUPP_PAE, AUTHENTICATING)
29639beb93cSSam Leffler {
29739beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, AUTHENTICATING);
29839beb93cSSam Leffler 	sm->startCount = 0;
299c1d255d3SCy Schubert 	sm->suppSuccess = false;
300c1d255d3SCy Schubert 	sm->suppFail = false;
301c1d255d3SCy Schubert 	sm->suppTimeout = false;
302c1d255d3SCy Schubert 	sm->keyRun = false;
303c1d255d3SCy Schubert 	sm->keyDone = false;
304c1d255d3SCy Schubert 	sm->suppStart = true;
30539beb93cSSam Leffler }
30639beb93cSSam Leffler 
30739beb93cSSam Leffler 
SM_STATE(SUPP_PAE,HELD)30839beb93cSSam Leffler SM_STATE(SUPP_PAE, HELD)
30939beb93cSSam Leffler {
31039beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, HELD);
31139beb93cSSam Leffler 	sm->heldWhile = sm->heldPeriod;
31239beb93cSSam Leffler 	eapol_enable_timer_tick(sm);
313e28a4053SRui Paulo 	eapol_sm_set_port_unauthorized(sm);
31439beb93cSSam Leffler 	sm->cb_status = EAPOL_CB_FAILURE;
31539beb93cSSam Leffler }
31639beb93cSSam Leffler 
31739beb93cSSam Leffler 
SM_STATE(SUPP_PAE,AUTHENTICATED)31839beb93cSSam Leffler SM_STATE(SUPP_PAE, AUTHENTICATED)
31939beb93cSSam Leffler {
32039beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, AUTHENTICATED);
321e28a4053SRui Paulo 	eapol_sm_set_port_authorized(sm);
32239beb93cSSam Leffler 	sm->cb_status = EAPOL_CB_SUCCESS;
32339beb93cSSam Leffler }
32439beb93cSSam Leffler 
32539beb93cSSam Leffler 
SM_STATE(SUPP_PAE,RESTART)32639beb93cSSam Leffler SM_STATE(SUPP_PAE, RESTART)
32739beb93cSSam Leffler {
328c1d255d3SCy Schubert 	if (eapol_sm_confirm_auth(sm)) {
329c1d255d3SCy Schubert 		/* Don't process restart, we are already reconnecting */
330c1d255d3SCy Schubert 		return;
331c1d255d3SCy Schubert 	}
332c1d255d3SCy Schubert 
33339beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, RESTART);
334c1d255d3SCy Schubert 	sm->eapRestart = true;
335780fb4a2SCy Schubert 	if (sm->altAccept) {
336780fb4a2SCy Schubert 		/*
337780fb4a2SCy Schubert 		 * Prevent EAP peer state machine from failing due to prior
338c1d255d3SCy Schubert 		 * external EAP success notification (altSuccess=true in the
339780fb4a2SCy Schubert 		 * IDLE state could result in a transition to the FAILURE state.
340780fb4a2SCy Schubert 		 */
341780fb4a2SCy Schubert 		wpa_printf(MSG_DEBUG, "EAPOL: Clearing prior altAccept TRUE");
342c1d255d3SCy Schubert 		sm->eapSuccess = false;
343c1d255d3SCy Schubert 		sm->altAccept = false;
344780fb4a2SCy Schubert 	}
34539beb93cSSam Leffler }
34639beb93cSSam Leffler 
34739beb93cSSam Leffler 
SM_STATE(SUPP_PAE,S_FORCE_AUTH)34839beb93cSSam Leffler SM_STATE(SUPP_PAE, S_FORCE_AUTH)
34939beb93cSSam Leffler {
35039beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
351e28a4053SRui Paulo 	eapol_sm_set_port_authorized(sm);
35239beb93cSSam Leffler 	sm->sPortMode = ForceAuthorized;
35339beb93cSSam Leffler }
35439beb93cSSam Leffler 
35539beb93cSSam Leffler 
SM_STATE(SUPP_PAE,S_FORCE_UNAUTH)35639beb93cSSam Leffler SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
35739beb93cSSam Leffler {
35839beb93cSSam Leffler 	SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
359e28a4053SRui Paulo 	eapol_sm_set_port_unauthorized(sm);
36039beb93cSSam Leffler 	sm->sPortMode = ForceUnauthorized;
36139beb93cSSam Leffler 	eapol_sm_txLogoff(sm);
36239beb93cSSam Leffler }
36339beb93cSSam Leffler 
36439beb93cSSam Leffler 
SM_STEP(SUPP_PAE)36539beb93cSSam Leffler SM_STEP(SUPP_PAE)
36639beb93cSSam Leffler {
36739beb93cSSam Leffler 	if ((sm->userLogoff && !sm->logoffSent) &&
36839beb93cSSam Leffler 	    !(sm->initialize || !sm->portEnabled))
36939beb93cSSam Leffler 		SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
37039beb93cSSam Leffler 	else if (((sm->portControl == Auto) &&
37139beb93cSSam Leffler 		  (sm->sPortMode != sm->portControl)) ||
37239beb93cSSam Leffler 		 sm->initialize || !sm->portEnabled)
37339beb93cSSam Leffler 		SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
37439beb93cSSam Leffler 	else if ((sm->portControl == ForceAuthorized) &&
37539beb93cSSam Leffler 		 (sm->sPortMode != sm->portControl) &&
37639beb93cSSam Leffler 		 !(sm->initialize || !sm->portEnabled))
37739beb93cSSam Leffler 		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
37839beb93cSSam Leffler 	else if ((sm->portControl == ForceUnauthorized) &&
37939beb93cSSam Leffler 		 (sm->sPortMode != sm->portControl) &&
38039beb93cSSam Leffler 		 !(sm->initialize || !sm->portEnabled))
38139beb93cSSam Leffler 		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
38239beb93cSSam Leffler 	else switch (sm->SUPP_PAE_state) {
38339beb93cSSam Leffler 	case SUPP_PAE_UNKNOWN:
38439beb93cSSam Leffler 		break;
38539beb93cSSam Leffler 	case SUPP_PAE_LOGOFF:
38639beb93cSSam Leffler 		if (!sm->userLogoff)
38739beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, DISCONNECTED);
38839beb93cSSam Leffler 		break;
38939beb93cSSam Leffler 	case SUPP_PAE_DISCONNECTED:
39039beb93cSSam Leffler 		SM_ENTER(SUPP_PAE, CONNECTING);
39139beb93cSSam Leffler 		break;
39239beb93cSSam Leffler 	case SUPP_PAE_CONNECTING:
39339beb93cSSam Leffler 		if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
39439beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, CONNECTING);
39539beb93cSSam Leffler 		else if (sm->startWhen == 0 &&
39639beb93cSSam Leffler 			 sm->startCount >= sm->maxStart &&
39739beb93cSSam Leffler 			 sm->portValid)
39839beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, AUTHENTICATED);
39939beb93cSSam Leffler 		else if (sm->eapSuccess || sm->eapFail)
40039beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, AUTHENTICATING);
40139beb93cSSam Leffler 		else if (sm->eapolEap)
40239beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, RESTART);
40339beb93cSSam Leffler 		else if (sm->startWhen == 0 &&
40439beb93cSSam Leffler 			 sm->startCount >= sm->maxStart &&
40539beb93cSSam Leffler 			 !sm->portValid)
40639beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, HELD);
40739beb93cSSam Leffler 		break;
40839beb93cSSam Leffler 	case SUPP_PAE_AUTHENTICATING:
40939beb93cSSam Leffler 		if (sm->eapSuccess && !sm->portValid &&
41039beb93cSSam Leffler 		    sm->conf.accept_802_1x_keys &&
41139beb93cSSam Leffler 		    sm->conf.required_keys == 0) {
41239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
41339beb93cSSam Leffler 				   "plaintext connection; no EAPOL-Key frames "
41439beb93cSSam Leffler 				   "required");
415c1d255d3SCy Schubert 			sm->portValid = true;
41639beb93cSSam Leffler 			if (sm->ctx->eapol_done_cb)
41739beb93cSSam Leffler 				sm->ctx->eapol_done_cb(sm->ctx->ctx);
41839beb93cSSam Leffler 		}
41939beb93cSSam Leffler 		if (sm->eapSuccess && sm->portValid)
42039beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, AUTHENTICATED);
42139beb93cSSam Leffler 		else if (sm->eapFail || (sm->keyDone && !sm->portValid))
42239beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, HELD);
42339beb93cSSam Leffler 		else if (sm->suppTimeout)
42439beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, CONNECTING);
4255b9c547cSRui Paulo 		else if (sm->eapTriggerStart)
4265b9c547cSRui Paulo 			SM_ENTER(SUPP_PAE, CONNECTING);
42739beb93cSSam Leffler 		break;
42839beb93cSSam Leffler 	case SUPP_PAE_HELD:
42939beb93cSSam Leffler 		if (sm->heldWhile == 0)
43039beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, CONNECTING);
43139beb93cSSam Leffler 		else if (sm->eapolEap)
43239beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, RESTART);
43339beb93cSSam Leffler 		break;
43439beb93cSSam Leffler 	case SUPP_PAE_AUTHENTICATED:
43539beb93cSSam Leffler 		if (sm->eapolEap && sm->portValid)
43639beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, RESTART);
43739beb93cSSam Leffler 		else if (!sm->portValid)
43839beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, DISCONNECTED);
43939beb93cSSam Leffler 		break;
44039beb93cSSam Leffler 	case SUPP_PAE_RESTART:
44139beb93cSSam Leffler 		if (!sm->eapRestart)
44239beb93cSSam Leffler 			SM_ENTER(SUPP_PAE, AUTHENTICATING);
44339beb93cSSam Leffler 		break;
44439beb93cSSam Leffler 	case SUPP_PAE_S_FORCE_AUTH:
44539beb93cSSam Leffler 		break;
44639beb93cSSam Leffler 	case SUPP_PAE_S_FORCE_UNAUTH:
44739beb93cSSam Leffler 		break;
44839beb93cSSam Leffler 	}
44939beb93cSSam Leffler }
45039beb93cSSam Leffler 
45139beb93cSSam Leffler 
SM_STATE(KEY_RX,NO_KEY_RECEIVE)45239beb93cSSam Leffler SM_STATE(KEY_RX, NO_KEY_RECEIVE)
45339beb93cSSam Leffler {
45439beb93cSSam Leffler 	SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
45539beb93cSSam Leffler }
45639beb93cSSam Leffler 
45739beb93cSSam Leffler 
SM_STATE(KEY_RX,KEY_RECEIVE)45839beb93cSSam Leffler SM_STATE(KEY_RX, KEY_RECEIVE)
45939beb93cSSam Leffler {
46039beb93cSSam Leffler 	SM_ENTRY(KEY_RX, KEY_RECEIVE);
46139beb93cSSam Leffler 	eapol_sm_processKey(sm);
462c1d255d3SCy Schubert 	sm->rxKey = false;
46339beb93cSSam Leffler }
46439beb93cSSam Leffler 
46539beb93cSSam Leffler 
SM_STEP(KEY_RX)46639beb93cSSam Leffler SM_STEP(KEY_RX)
46739beb93cSSam Leffler {
46839beb93cSSam Leffler 	if (sm->initialize || !sm->portEnabled)
46939beb93cSSam Leffler 		SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
47039beb93cSSam Leffler 	switch (sm->KEY_RX_state) {
47139beb93cSSam Leffler 	case KEY_RX_UNKNOWN:
47239beb93cSSam Leffler 		break;
47339beb93cSSam Leffler 	case KEY_RX_NO_KEY_RECEIVE:
47439beb93cSSam Leffler 		if (sm->rxKey)
47539beb93cSSam Leffler 			SM_ENTER(KEY_RX, KEY_RECEIVE);
47639beb93cSSam Leffler 		break;
47739beb93cSSam Leffler 	case KEY_RX_KEY_RECEIVE:
47839beb93cSSam Leffler 		if (sm->rxKey)
47939beb93cSSam Leffler 			SM_ENTER(KEY_RX, KEY_RECEIVE);
48039beb93cSSam Leffler 		break;
48139beb93cSSam Leffler 	}
48239beb93cSSam Leffler }
48339beb93cSSam Leffler 
48439beb93cSSam Leffler 
SM_STATE(SUPP_BE,REQUEST)48539beb93cSSam Leffler SM_STATE(SUPP_BE, REQUEST)
48639beb93cSSam Leffler {
48739beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, REQUEST);
48839beb93cSSam Leffler 	sm->authWhile = 0;
489c1d255d3SCy Schubert 	sm->eapReq = true;
49039beb93cSSam Leffler 	eapol_sm_getSuppRsp(sm);
49139beb93cSSam Leffler }
49239beb93cSSam Leffler 
49339beb93cSSam Leffler 
SM_STATE(SUPP_BE,RESPONSE)49439beb93cSSam Leffler SM_STATE(SUPP_BE, RESPONSE)
49539beb93cSSam Leffler {
49639beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, RESPONSE);
49739beb93cSSam Leffler 	eapol_sm_txSuppRsp(sm);
498c1d255d3SCy Schubert 	sm->eapResp = false;
49939beb93cSSam Leffler }
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 
SM_STATE(SUPP_BE,SUCCESS)50239beb93cSSam Leffler SM_STATE(SUPP_BE, SUCCESS)
50339beb93cSSam Leffler {
50439beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, SUCCESS);
505c1d255d3SCy Schubert 	sm->keyRun = true;
506c1d255d3SCy Schubert 	sm->suppSuccess = true;
50739beb93cSSam Leffler 
5085b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
5095b9c547cSRui Paulo 	if (sm->use_eap_proxy) {
5105b9c547cSRui Paulo 		if (eap_proxy_key_available(sm->eap_proxy)) {
51185732ac8SCy Schubert 			u8 *session_id, *emsk;
51285732ac8SCy Schubert 			size_t session_id_len, emsk_len;
51385732ac8SCy Schubert 
5145b9c547cSRui Paulo 			/* New key received - clear IEEE 802.1X EAPOL-Key replay
5155b9c547cSRui Paulo 			 * counter */
516c1d255d3SCy Schubert 			sm->replay_counter_valid = false;
51785732ac8SCy Schubert 
51885732ac8SCy Schubert 			session_id = eap_proxy_get_eap_session_id(
51985732ac8SCy Schubert 				sm->eap_proxy, &session_id_len);
52085732ac8SCy Schubert 			emsk = eap_proxy_get_emsk(sm->eap_proxy, &emsk_len);
52185732ac8SCy Schubert 			if (sm->config->erp && session_id && emsk) {
52285732ac8SCy Schubert 				eap_peer_erp_init(sm->eap, session_id,
52385732ac8SCy Schubert 						  session_id_len, emsk,
52485732ac8SCy Schubert 						  emsk_len);
52585732ac8SCy Schubert 			} else {
52685732ac8SCy Schubert 				os_free(session_id);
52785732ac8SCy Schubert 				bin_clear_free(emsk, emsk_len);
52885732ac8SCy Schubert 			}
5295b9c547cSRui Paulo 		}
5305b9c547cSRui Paulo 		return;
5315b9c547cSRui Paulo 	}
5325b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
5335b9c547cSRui Paulo 
53439beb93cSSam Leffler 	if (eap_key_available(sm->eap)) {
53539beb93cSSam Leffler 		/* New key received - clear IEEE 802.1X EAPOL-Key replay
53639beb93cSSam Leffler 		 * counter */
537c1d255d3SCy Schubert 		sm->replay_counter_valid = false;
53839beb93cSSam Leffler 	}
53939beb93cSSam Leffler }
54039beb93cSSam Leffler 
54139beb93cSSam Leffler 
SM_STATE(SUPP_BE,FAIL)54239beb93cSSam Leffler SM_STATE(SUPP_BE, FAIL)
54339beb93cSSam Leffler {
54439beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, FAIL);
545c1d255d3SCy Schubert 	sm->suppFail = true;
54639beb93cSSam Leffler }
54739beb93cSSam Leffler 
54839beb93cSSam Leffler 
SM_STATE(SUPP_BE,TIMEOUT)54939beb93cSSam Leffler SM_STATE(SUPP_BE, TIMEOUT)
55039beb93cSSam Leffler {
55139beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, TIMEOUT);
552c1d255d3SCy Schubert 	sm->suppTimeout = true;
55339beb93cSSam Leffler }
55439beb93cSSam Leffler 
55539beb93cSSam Leffler 
SM_STATE(SUPP_BE,IDLE)55639beb93cSSam Leffler SM_STATE(SUPP_BE, IDLE)
55739beb93cSSam Leffler {
55839beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, IDLE);
559c1d255d3SCy Schubert 	sm->suppStart = false;
560c1d255d3SCy Schubert 	sm->initial_req = true;
56139beb93cSSam Leffler }
56239beb93cSSam Leffler 
56339beb93cSSam Leffler 
SM_STATE(SUPP_BE,INITIALIZE)56439beb93cSSam Leffler SM_STATE(SUPP_BE, INITIALIZE)
56539beb93cSSam Leffler {
56639beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, INITIALIZE);
56739beb93cSSam Leffler 	eapol_sm_abortSupp(sm);
568c1d255d3SCy Schubert 	sm->suppAbort = false;
569f05cddf9SRui Paulo 
570f05cddf9SRui Paulo 	/*
571f05cddf9SRui Paulo 	 * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so
572f05cddf9SRui Paulo 	 * allows the timer tick to be stopped more quickly when the port is
573f05cddf9SRui Paulo 	 * not enabled. Since this variable is used only within RECEIVE state,
574f05cddf9SRui Paulo 	 * clearing it on initialization does not change actual state machine
575f05cddf9SRui Paulo 	 * behavior.
576f05cddf9SRui Paulo 	 */
577f05cddf9SRui Paulo 	sm->authWhile = 0;
57839beb93cSSam Leffler }
57939beb93cSSam Leffler 
58039beb93cSSam Leffler 
SM_STATE(SUPP_BE,RECEIVE)58139beb93cSSam Leffler SM_STATE(SUPP_BE, RECEIVE)
58239beb93cSSam Leffler {
58339beb93cSSam Leffler 	SM_ENTRY(SUPP_BE, RECEIVE);
58439beb93cSSam Leffler 	sm->authWhile = sm->authPeriod;
58539beb93cSSam Leffler 	eapol_enable_timer_tick(sm);
586c1d255d3SCy Schubert 	sm->eapolEap = false;
587c1d255d3SCy Schubert 	sm->eapNoResp = false;
588c1d255d3SCy Schubert 	sm->initial_req = false;
58939beb93cSSam Leffler }
59039beb93cSSam Leffler 
59139beb93cSSam Leffler 
SM_STEP(SUPP_BE)59239beb93cSSam Leffler SM_STEP(SUPP_BE)
59339beb93cSSam Leffler {
59439beb93cSSam Leffler 	if (sm->initialize || sm->suppAbort)
59539beb93cSSam Leffler 		SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
59639beb93cSSam Leffler 	else switch (sm->SUPP_BE_state) {
59739beb93cSSam Leffler 	case SUPP_BE_UNKNOWN:
59839beb93cSSam Leffler 		break;
59939beb93cSSam Leffler 	case SUPP_BE_REQUEST:
60039beb93cSSam Leffler 		/*
60139beb93cSSam Leffler 		 * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
60239beb93cSSam Leffler 		 * and SUCCESS based on eapFail and eapSuccess, respectively.
60339beb93cSSam Leffler 		 * However, IEEE Std 802.1X-2004 is also specifying that
604f05cddf9SRui Paulo 		 * eapNoResp should be set in conjunction with eapSuccess and
60539beb93cSSam Leffler 		 * eapFail which would mean that more than one of the
60639beb93cSSam Leffler 		 * transitions here would be activated at the same time.
60739beb93cSSam Leffler 		 * Skipping RESPONSE and/or RECEIVE states in these cases can
60839beb93cSSam Leffler 		 * cause problems and the direct transitions to do not seem
60939beb93cSSam Leffler 		 * correct. Because of this, the conditions for these
61039beb93cSSam Leffler 		 * transitions are verified only after eapNoResp. They are
61139beb93cSSam Leffler 		 * unlikely to be used since eapNoResp should always be set if
61239beb93cSSam Leffler 		 * either of eapSuccess or eapFail is set.
61339beb93cSSam Leffler 		 */
61439beb93cSSam Leffler 		if (sm->eapResp && sm->eapNoResp) {
61539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
61639beb93cSSam Leffler 				   "eapResp and eapNoResp set?!");
61739beb93cSSam Leffler 		}
61839beb93cSSam Leffler 		if (sm->eapResp)
61939beb93cSSam Leffler 			SM_ENTER(SUPP_BE, RESPONSE);
62039beb93cSSam Leffler 		else if (sm->eapNoResp)
62139beb93cSSam Leffler 			SM_ENTER(SUPP_BE, RECEIVE);
62239beb93cSSam Leffler 		else if (sm->eapFail)
62339beb93cSSam Leffler 			SM_ENTER(SUPP_BE, FAIL);
62439beb93cSSam Leffler 		else if (sm->eapSuccess)
62539beb93cSSam Leffler 			SM_ENTER(SUPP_BE, SUCCESS);
62639beb93cSSam Leffler 		break;
62739beb93cSSam Leffler 	case SUPP_BE_RESPONSE:
62839beb93cSSam Leffler 		SM_ENTER(SUPP_BE, RECEIVE);
62939beb93cSSam Leffler 		break;
63039beb93cSSam Leffler 	case SUPP_BE_SUCCESS:
63139beb93cSSam Leffler 		SM_ENTER(SUPP_BE, IDLE);
63239beb93cSSam Leffler 		break;
63339beb93cSSam Leffler 	case SUPP_BE_FAIL:
63439beb93cSSam Leffler 		SM_ENTER(SUPP_BE, IDLE);
63539beb93cSSam Leffler 		break;
63639beb93cSSam Leffler 	case SUPP_BE_TIMEOUT:
63739beb93cSSam Leffler 		SM_ENTER(SUPP_BE, IDLE);
63839beb93cSSam Leffler 		break;
63939beb93cSSam Leffler 	case SUPP_BE_IDLE:
64039beb93cSSam Leffler 		if (sm->eapFail && sm->suppStart)
64139beb93cSSam Leffler 			SM_ENTER(SUPP_BE, FAIL);
64239beb93cSSam Leffler 		else if (sm->eapolEap && sm->suppStart)
64339beb93cSSam Leffler 			SM_ENTER(SUPP_BE, REQUEST);
64439beb93cSSam Leffler 		else if (sm->eapSuccess && sm->suppStart)
64539beb93cSSam Leffler 			SM_ENTER(SUPP_BE, SUCCESS);
64639beb93cSSam Leffler 		break;
64739beb93cSSam Leffler 	case SUPP_BE_INITIALIZE:
64839beb93cSSam Leffler 		SM_ENTER(SUPP_BE, IDLE);
64939beb93cSSam Leffler 		break;
65039beb93cSSam Leffler 	case SUPP_BE_RECEIVE:
65139beb93cSSam Leffler 		if (sm->eapolEap)
65239beb93cSSam Leffler 			SM_ENTER(SUPP_BE, REQUEST);
65339beb93cSSam Leffler 		else if (sm->eapFail)
65439beb93cSSam Leffler 			SM_ENTER(SUPP_BE, FAIL);
65539beb93cSSam Leffler 		else if (sm->authWhile == 0)
65639beb93cSSam Leffler 			SM_ENTER(SUPP_BE, TIMEOUT);
65739beb93cSSam Leffler 		else if (sm->eapSuccess)
65839beb93cSSam Leffler 			SM_ENTER(SUPP_BE, SUCCESS);
65939beb93cSSam Leffler 		break;
66039beb93cSSam Leffler 	}
66139beb93cSSam Leffler }
66239beb93cSSam Leffler 
66339beb93cSSam Leffler 
eapol_sm_txLogoff(struct eapol_sm * sm)66439beb93cSSam Leffler static void eapol_sm_txLogoff(struct eapol_sm *sm)
66539beb93cSSam Leffler {
66639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
66739beb93cSSam Leffler 	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
66839beb93cSSam Leffler 			    IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
66939beb93cSSam Leffler 	sm->dot1xSuppEapolLogoffFramesTx++;
67039beb93cSSam Leffler 	sm->dot1xSuppEapolFramesTx++;
67139beb93cSSam Leffler }
67239beb93cSSam Leffler 
67339beb93cSSam Leffler 
eapol_sm_txStart(struct eapol_sm * sm)67439beb93cSSam Leffler static void eapol_sm_txStart(struct eapol_sm *sm)
67539beb93cSSam Leffler {
67639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: txStart");
67739beb93cSSam Leffler 	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
67839beb93cSSam Leffler 			    IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
67939beb93cSSam Leffler 	sm->dot1xSuppEapolStartFramesTx++;
68039beb93cSSam Leffler 	sm->dot1xSuppEapolFramesTx++;
68139beb93cSSam Leffler }
68239beb93cSSam Leffler 
68339beb93cSSam Leffler 
68439beb93cSSam Leffler #define IEEE8021X_ENCR_KEY_LEN 32
68539beb93cSSam Leffler #define IEEE8021X_SIGN_KEY_LEN 32
68639beb93cSSam Leffler 
68739beb93cSSam Leffler struct eap_key_data {
68839beb93cSSam Leffler 	u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
68939beb93cSSam Leffler 	u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
69039beb93cSSam Leffler };
69139beb93cSSam Leffler 
69239beb93cSSam Leffler 
eapol_sm_processKey(struct eapol_sm * sm)69339beb93cSSam Leffler static void eapol_sm_processKey(struct eapol_sm *sm)
69439beb93cSSam Leffler {
695c1d255d3SCy Schubert #ifdef CONFIG_WEP
696f05cddf9SRui Paulo #ifndef CONFIG_FIPS
69739beb93cSSam Leffler 	struct ieee802_1x_hdr *hdr;
69839beb93cSSam Leffler 	struct ieee802_1x_eapol_key *key;
69939beb93cSSam Leffler 	struct eap_key_data keydata;
70039beb93cSSam Leffler 	u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
701325151a3SRui Paulo #ifndef CONFIG_NO_RC4
70239beb93cSSam Leffler 	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
703325151a3SRui Paulo #endif /* CONFIG_NO_RC4 */
70439beb93cSSam Leffler 	int key_len, res, sign_key_len, encr_key_len;
70539beb93cSSam Leffler 	u16 rx_key_length;
706f05cddf9SRui Paulo 	size_t plen;
70739beb93cSSam Leffler 
70839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: processKey");
70939beb93cSSam Leffler 	if (sm->last_rx_key == NULL)
71039beb93cSSam Leffler 		return;
71139beb93cSSam Leffler 
71239beb93cSSam Leffler 	if (!sm->conf.accept_802_1x_keys) {
71339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
71439beb93cSSam Leffler 			   " even though this was not accepted - "
71539beb93cSSam Leffler 			   "ignoring this packet");
71639beb93cSSam Leffler 		return;
71739beb93cSSam Leffler 	}
71839beb93cSSam Leffler 
719f05cddf9SRui Paulo 	if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key))
720f05cddf9SRui Paulo 		return;
72139beb93cSSam Leffler 	hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
72239beb93cSSam Leffler 	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
723f05cddf9SRui Paulo 	plen = be_to_host16(hdr->length);
724f05cddf9SRui Paulo 	if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) {
72539beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
72639beb93cSSam Leffler 		return;
72739beb93cSSam Leffler 	}
72839beb93cSSam Leffler 	rx_key_length = WPA_GET_BE16(key->key_length);
72939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
73039beb93cSSam Leffler 		   "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
73139beb93cSSam Leffler 		   hdr->version, hdr->type, be_to_host16(hdr->length),
73239beb93cSSam Leffler 		   key->type, rx_key_length, key->key_index);
73339beb93cSSam Leffler 
73439beb93cSSam Leffler 	eapol_sm_notify_lower_layer_success(sm, 1);
73539beb93cSSam Leffler 	sign_key_len = IEEE8021X_SIGN_KEY_LEN;
73639beb93cSSam Leffler 	encr_key_len = IEEE8021X_ENCR_KEY_LEN;
73739beb93cSSam Leffler 	res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
73839beb93cSSam Leffler 	if (res < 0) {
73939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
74039beb93cSSam Leffler 			   "decrypting EAPOL-Key keys");
74139beb93cSSam Leffler 		return;
74239beb93cSSam Leffler 	}
74339beb93cSSam Leffler 	if (res == 16) {
74439beb93cSSam Leffler 		/* LEAP derives only 16 bytes of keying material. */
74539beb93cSSam Leffler 		res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
74639beb93cSSam Leffler 		if (res) {
74739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
74839beb93cSSam Leffler 				   "master key for decrypting EAPOL-Key keys");
74939beb93cSSam Leffler 			return;
75039beb93cSSam Leffler 		}
75139beb93cSSam Leffler 		sign_key_len = 16;
75239beb93cSSam Leffler 		encr_key_len = 16;
75339beb93cSSam Leffler 		os_memcpy(keydata.sign_key, keydata.encr_key, 16);
75439beb93cSSam Leffler 	} else if (res) {
75539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
75639beb93cSSam Leffler 			   "data for decrypting EAPOL-Key keys (res=%d)", res);
75739beb93cSSam Leffler 		return;
75839beb93cSSam Leffler 	}
75939beb93cSSam Leffler 
76039beb93cSSam Leffler 	/* The key replay_counter must increase when same master key */
76139beb93cSSam Leffler 	if (sm->replay_counter_valid &&
76239beb93cSSam Leffler 	    os_memcmp(sm->last_replay_counter, key->replay_counter,
76339beb93cSSam Leffler 		      IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
76439beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
76539beb93cSSam Leffler 			   "not increase - ignoring key");
76639beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
76739beb93cSSam Leffler 			    sm->last_replay_counter,
76839beb93cSSam Leffler 			    IEEE8021X_REPLAY_COUNTER_LEN);
76939beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
77039beb93cSSam Leffler 			    key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
77139beb93cSSam Leffler 		return;
77239beb93cSSam Leffler 	}
77339beb93cSSam Leffler 
77439beb93cSSam Leffler 	/* Verify key signature (HMAC-MD5) */
77539beb93cSSam Leffler 	os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
77639beb93cSSam Leffler 	os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
77739beb93cSSam Leffler 	hmac_md5(keydata.sign_key, sign_key_len,
77839beb93cSSam Leffler 		 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
77939beb93cSSam Leffler 		 key->key_signature);
7805b9c547cSRui Paulo 	if (os_memcmp_const(orig_key_sign, key->key_signature,
78139beb93cSSam Leffler 			    IEEE8021X_KEY_SIGN_LEN) != 0) {
78239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
78339beb93cSSam Leffler 			   "EAPOL-Key packet");
78439beb93cSSam Leffler 		os_memcpy(key->key_signature, orig_key_sign,
78539beb93cSSam Leffler 			  IEEE8021X_KEY_SIGN_LEN);
78639beb93cSSam Leffler 		return;
78739beb93cSSam Leffler 	}
78839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
78939beb93cSSam Leffler 
790f05cddf9SRui Paulo 	key_len = plen - sizeof(*key);
79139beb93cSSam Leffler 	if (key_len > 32 || rx_key_length > 32) {
79239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
79339beb93cSSam Leffler 			   key_len ? key_len : rx_key_length);
79439beb93cSSam Leffler 		return;
79539beb93cSSam Leffler 	}
79639beb93cSSam Leffler 	if (key_len == rx_key_length) {
797325151a3SRui Paulo #ifdef CONFIG_NO_RC4
798325151a3SRui Paulo 		if (encr_key_len) {
799325151a3SRui Paulo 			/* otherwise unused */
800325151a3SRui Paulo 		}
801325151a3SRui Paulo 		wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build");
802325151a3SRui Paulo 		return;
803325151a3SRui Paulo #else /* CONFIG_NO_RC4 */
80439beb93cSSam Leffler 		os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
80539beb93cSSam Leffler 		os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
80639beb93cSSam Leffler 			  encr_key_len);
80739beb93cSSam Leffler 		os_memcpy(datakey, key + 1, key_len);
8083157ba21SRui Paulo 		rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
8093157ba21SRui Paulo 			 datakey, key_len);
81039beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
81139beb93cSSam Leffler 				datakey, key_len);
812325151a3SRui Paulo #endif /* CONFIG_NO_RC4 */
81339beb93cSSam Leffler 	} else if (key_len == 0) {
81439beb93cSSam Leffler 		/*
81539beb93cSSam Leffler 		 * IEEE 802.1X-2004 specifies that least significant Key Length
81639beb93cSSam Leffler 		 * octets from MS-MPPE-Send-Key are used as the key if the key
81739beb93cSSam Leffler 		 * data is not present. This seems to be meaning the beginning
81839beb93cSSam Leffler 		 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
81939beb93cSSam Leffler 		 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
82039beb93cSSam Leffler 		 * Anyway, taking the beginning of the keying material from EAP
82139beb93cSSam Leffler 		 * seems to interoperate with Authenticators.
82239beb93cSSam Leffler 		 */
82339beb93cSSam Leffler 		key_len = rx_key_length;
82439beb93cSSam Leffler 		os_memcpy(datakey, keydata.encr_key, key_len);
82539beb93cSSam Leffler 		wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
82639beb93cSSam Leffler 				"material data encryption key",
82739beb93cSSam Leffler 				datakey, key_len);
82839beb93cSSam Leffler 	} else {
82939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
83039beb93cSSam Leffler 			   "(key_length=%d)", key_len, rx_key_length);
83139beb93cSSam Leffler 		return;
83239beb93cSSam Leffler 	}
83339beb93cSSam Leffler 
834c1d255d3SCy Schubert 	sm->replay_counter_valid = true;
83539beb93cSSam Leffler 	os_memcpy(sm->last_replay_counter, key->replay_counter,
83639beb93cSSam Leffler 		  IEEE8021X_REPLAY_COUNTER_LEN);
83739beb93cSSam Leffler 
83839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
83939beb93cSSam Leffler 		   "len %d",
84039beb93cSSam Leffler 		   key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
84139beb93cSSam Leffler 		   "unicast" : "broadcast",
84239beb93cSSam Leffler 		   key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
84339beb93cSSam Leffler 
84439beb93cSSam Leffler 	if (sm->ctx->set_wep_key &&
84539beb93cSSam Leffler 	    sm->ctx->set_wep_key(sm->ctx->ctx,
846c1d255d3SCy Schubert 				 !!(key->key_index & IEEE8021X_KEY_INDEX_FLAG),
84739beb93cSSam Leffler 				 key->key_index & IEEE8021X_KEY_INDEX_MASK,
84839beb93cSSam Leffler 				 datakey, key_len) < 0) {
84939beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
85039beb93cSSam Leffler 			   " driver.");
85139beb93cSSam Leffler 	} else {
85239beb93cSSam Leffler 		if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
853c1d255d3SCy Schubert 			sm->unicast_key_received = true;
85439beb93cSSam Leffler 		else
855c1d255d3SCy Schubert 			sm->broadcast_key_received = true;
85639beb93cSSam Leffler 
85739beb93cSSam Leffler 		if ((sm->unicast_key_received ||
85839beb93cSSam Leffler 		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
85939beb93cSSam Leffler 		    (sm->broadcast_key_received ||
86039beb93cSSam Leffler 		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
86139beb93cSSam Leffler 		{
86239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
86339beb93cSSam Leffler 				   "frames received");
864c1d255d3SCy Schubert 			sm->portValid = true;
86539beb93cSSam Leffler 			if (sm->ctx->eapol_done_cb)
86639beb93cSSam Leffler 				sm->ctx->eapol_done_cb(sm->ctx->ctx);
86739beb93cSSam Leffler 		}
86839beb93cSSam Leffler 	}
869f05cddf9SRui Paulo #endif /* CONFIG_FIPS */
870c1d255d3SCy Schubert #endif /* CONFIG_WEP */
87139beb93cSSam Leffler }
87239beb93cSSam Leffler 
87339beb93cSSam Leffler 
eapol_sm_getSuppRsp(struct eapol_sm * sm)87439beb93cSSam Leffler static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
87539beb93cSSam Leffler {
87639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
87739beb93cSSam Leffler 	/* EAP layer processing; no special code is needed, since Supplicant
87839beb93cSSam Leffler 	 * Backend state machine is waiting for eapNoResp or eapResp to be set
87939beb93cSSam Leffler 	 * and these are only set in the EAP state machine when the processing
88039beb93cSSam Leffler 	 * has finished. */
88139beb93cSSam Leffler }
88239beb93cSSam Leffler 
88339beb93cSSam Leffler 
eapol_sm_txSuppRsp(struct eapol_sm * sm)88439beb93cSSam Leffler static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
88539beb93cSSam Leffler {
88639beb93cSSam Leffler 	struct wpabuf *resp;
88739beb93cSSam Leffler 
88839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
8895b9c547cSRui Paulo 
8905b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
8915b9c547cSRui Paulo 	if (sm->use_eap_proxy) {
8925b9c547cSRui Paulo 		/* Get EAP Response from EAP Proxy */
8935b9c547cSRui Paulo 		resp = eap_proxy_get_eapRespData(sm->eap_proxy);
8945b9c547cSRui Paulo 		if (resp == NULL) {
8955b9c547cSRui Paulo 			wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy "
8965b9c547cSRui Paulo 				   "response data not available");
8975b9c547cSRui Paulo 			return;
8985b9c547cSRui Paulo 		}
8995b9c547cSRui Paulo 	} else
9005b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
9015b9c547cSRui Paulo 
90239beb93cSSam Leffler 	resp = eap_get_eapRespData(sm->eap);
90339beb93cSSam Leffler 	if (resp == NULL) {
90439beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
90539beb93cSSam Leffler 			   "not available");
90639beb93cSSam Leffler 		return;
90739beb93cSSam Leffler 	}
90839beb93cSSam Leffler 
90939beb93cSSam Leffler 	/* Send EAP-Packet from the EAP layer to the Authenticator */
91039beb93cSSam Leffler 	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
91139beb93cSSam Leffler 			    IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
91239beb93cSSam Leffler 			    wpabuf_len(resp));
91339beb93cSSam Leffler 
91439beb93cSSam Leffler 	/* eapRespData is not used anymore, so free it here */
91539beb93cSSam Leffler 	wpabuf_free(resp);
91639beb93cSSam Leffler 
91739beb93cSSam Leffler 	if (sm->initial_req)
91839beb93cSSam Leffler 		sm->dot1xSuppEapolReqIdFramesRx++;
91939beb93cSSam Leffler 	else
92039beb93cSSam Leffler 		sm->dot1xSuppEapolReqFramesRx++;
92139beb93cSSam Leffler 	sm->dot1xSuppEapolRespFramesTx++;
92239beb93cSSam Leffler 	sm->dot1xSuppEapolFramesTx++;
92339beb93cSSam Leffler }
92439beb93cSSam Leffler 
92539beb93cSSam Leffler 
eapol_sm_abortSupp(struct eapol_sm * sm)92639beb93cSSam Leffler static void eapol_sm_abortSupp(struct eapol_sm *sm)
92739beb93cSSam Leffler {
92839beb93cSSam Leffler 	/* release system resources that may have been allocated for the
92939beb93cSSam Leffler 	 * authentication session */
93039beb93cSSam Leffler 	os_free(sm->last_rx_key);
93139beb93cSSam Leffler 	sm->last_rx_key = NULL;
93239beb93cSSam Leffler 	wpabuf_free(sm->eapReqData);
93339beb93cSSam Leffler 	sm->eapReqData = NULL;
93439beb93cSSam Leffler 	eap_sm_abort(sm->eap);
93585732ac8SCy Schubert #ifdef CONFIG_EAP_PROXY
93685732ac8SCy Schubert 	eap_proxy_sm_abort(sm->eap_proxy);
93785732ac8SCy Schubert #endif /* CONFIG_EAP_PROXY */
93839beb93cSSam Leffler }
93939beb93cSSam Leffler 
94039beb93cSSam Leffler 
eapol_sm_step_timeout(void * eloop_ctx,void * timeout_ctx)94139beb93cSSam Leffler static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
94239beb93cSSam Leffler {
94339beb93cSSam Leffler 	eapol_sm_step(timeout_ctx);
94439beb93cSSam Leffler }
94539beb93cSSam Leffler 
94639beb93cSSam Leffler 
eapol_sm_set_port_authorized(struct eapol_sm * sm)947e28a4053SRui Paulo static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
948e28a4053SRui Paulo {
9495b9c547cSRui Paulo 	int cb;
9505b9c547cSRui Paulo 
9515b9c547cSRui Paulo 	cb = sm->suppPortStatus != Authorized || sm->force_authorized_update;
952c1d255d3SCy Schubert 	sm->force_authorized_update = false;
9535b9c547cSRui Paulo 	sm->suppPortStatus = Authorized;
9545b9c547cSRui Paulo 	if (cb && sm->ctx->port_cb)
955e28a4053SRui Paulo 		sm->ctx->port_cb(sm->ctx->ctx, 1);
956e28a4053SRui Paulo }
957e28a4053SRui Paulo 
958e28a4053SRui Paulo 
eapol_sm_set_port_unauthorized(struct eapol_sm * sm)959e28a4053SRui Paulo static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
960e28a4053SRui Paulo {
9615b9c547cSRui Paulo 	int cb;
9625b9c547cSRui Paulo 
9635b9c547cSRui Paulo 	cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update;
964c1d255d3SCy Schubert 	sm->force_authorized_update = false;
9655b9c547cSRui Paulo 	sm->suppPortStatus = Unauthorized;
9665b9c547cSRui Paulo 	if (cb && sm->ctx->port_cb)
967e28a4053SRui Paulo 		sm->ctx->port_cb(sm->ctx->ctx, 0);
968e28a4053SRui Paulo }
969e28a4053SRui Paulo 
970e28a4053SRui Paulo 
97139beb93cSSam Leffler /**
97239beb93cSSam Leffler  * eapol_sm_step - EAPOL state machine step function
97339beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
97439beb93cSSam Leffler  *
97539beb93cSSam Leffler  * This function is called to notify the state machine about changed external
97639beb93cSSam Leffler  * variables. It will step through the EAPOL state machines in loop to process
97739beb93cSSam Leffler  * all triggered state changes.
97839beb93cSSam Leffler  */
eapol_sm_step(struct eapol_sm * sm)97939beb93cSSam Leffler void eapol_sm_step(struct eapol_sm *sm)
98039beb93cSSam Leffler {
98139beb93cSSam Leffler 	int i;
98239beb93cSSam Leffler 
98339beb93cSSam Leffler 	/* In theory, it should be ok to run this in loop until !changed.
98439beb93cSSam Leffler 	 * However, it is better to use a limit on number of iterations to
98539beb93cSSam Leffler 	 * allow events (e.g., SIGTERM) to stop the program cleanly if the
98639beb93cSSam Leffler 	 * state machine were to generate a busy loop. */
98739beb93cSSam Leffler 	for (i = 0; i < 100; i++) {
988c1d255d3SCy Schubert 		sm->changed = false;
98939beb93cSSam Leffler 		SM_STEP_RUN(SUPP_PAE);
99039beb93cSSam Leffler 		SM_STEP_RUN(KEY_RX);
99139beb93cSSam Leffler 		SM_STEP_RUN(SUPP_BE);
9925b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
9935b9c547cSRui Paulo 		if (sm->use_eap_proxy) {
9945b9c547cSRui Paulo 			/* Drive the EAP proxy state machine */
9955b9c547cSRui Paulo 			if (eap_proxy_sm_step(sm->eap_proxy, sm->eap))
996c1d255d3SCy Schubert 				sm->changed = true;
9975b9c547cSRui Paulo 		} else
9985b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
99939beb93cSSam Leffler 		if (eap_peer_sm_step(sm->eap))
1000c1d255d3SCy Schubert 			sm->changed = true;
100139beb93cSSam Leffler 		if (!sm->changed)
100239beb93cSSam Leffler 			break;
100339beb93cSSam Leffler 	}
100439beb93cSSam Leffler 
100539beb93cSSam Leffler 	if (sm->changed) {
100639beb93cSSam Leffler 		/* restart EAPOL state machine step from timeout call in order
100739beb93cSSam Leffler 		 * to allow other events to be processed. */
100839beb93cSSam Leffler 		eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
100939beb93cSSam Leffler 		eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
101039beb93cSSam Leffler 	}
101139beb93cSSam Leffler 
101239beb93cSSam Leffler 	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
10135b9c547cSRui Paulo 		enum eapol_supp_result result;
10145b9c547cSRui Paulo 		if (sm->cb_status == EAPOL_CB_SUCCESS)
10155b9c547cSRui Paulo 			result = EAPOL_SUPP_RESULT_SUCCESS;
10165b9c547cSRui Paulo 		else if (eap_peer_was_failure_expected(sm->eap))
10175b9c547cSRui Paulo 			result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
10185b9c547cSRui Paulo 		else
10195b9c547cSRui Paulo 			result = EAPOL_SUPP_RESULT_FAILURE;
102039beb93cSSam Leffler 		sm->cb_status = EAPOL_CB_IN_PROGRESS;
10215b9c547cSRui Paulo 		sm->ctx->cb(sm, result, sm->ctx->cb_ctx);
102239beb93cSSam Leffler 	}
102339beb93cSSam Leffler }
102439beb93cSSam Leffler 
102539beb93cSSam Leffler 
102639beb93cSSam Leffler #ifdef CONFIG_CTRL_IFACE
eapol_supp_pae_state(int state)102739beb93cSSam Leffler static const char *eapol_supp_pae_state(int state)
102839beb93cSSam Leffler {
102939beb93cSSam Leffler 	switch (state) {
103039beb93cSSam Leffler 	case SUPP_PAE_LOGOFF:
103139beb93cSSam Leffler 		return "LOGOFF";
103239beb93cSSam Leffler 	case SUPP_PAE_DISCONNECTED:
103339beb93cSSam Leffler 		return "DISCONNECTED";
103439beb93cSSam Leffler 	case SUPP_PAE_CONNECTING:
103539beb93cSSam Leffler 		return "CONNECTING";
103639beb93cSSam Leffler 	case SUPP_PAE_AUTHENTICATING:
103739beb93cSSam Leffler 		return "AUTHENTICATING";
103839beb93cSSam Leffler 	case SUPP_PAE_HELD:
103939beb93cSSam Leffler 		return "HELD";
104039beb93cSSam Leffler 	case SUPP_PAE_AUTHENTICATED:
104139beb93cSSam Leffler 		return "AUTHENTICATED";
104239beb93cSSam Leffler 	case SUPP_PAE_RESTART:
104339beb93cSSam Leffler 		return "RESTART";
104439beb93cSSam Leffler 	default:
104539beb93cSSam Leffler 		return "UNKNOWN";
104639beb93cSSam Leffler 	}
104739beb93cSSam Leffler }
104839beb93cSSam Leffler 
104939beb93cSSam Leffler 
eapol_supp_be_state(int state)105039beb93cSSam Leffler static const char *eapol_supp_be_state(int state)
105139beb93cSSam Leffler {
105239beb93cSSam Leffler 	switch (state) {
105339beb93cSSam Leffler 	case SUPP_BE_REQUEST:
105439beb93cSSam Leffler 		return "REQUEST";
105539beb93cSSam Leffler 	case SUPP_BE_RESPONSE:
105639beb93cSSam Leffler 		return "RESPONSE";
105739beb93cSSam Leffler 	case SUPP_BE_SUCCESS:
105839beb93cSSam Leffler 		return "SUCCESS";
105939beb93cSSam Leffler 	case SUPP_BE_FAIL:
106039beb93cSSam Leffler 		return "FAIL";
106139beb93cSSam Leffler 	case SUPP_BE_TIMEOUT:
106239beb93cSSam Leffler 		return "TIMEOUT";
106339beb93cSSam Leffler 	case SUPP_BE_IDLE:
106439beb93cSSam Leffler 		return "IDLE";
106539beb93cSSam Leffler 	case SUPP_BE_INITIALIZE:
106639beb93cSSam Leffler 		return "INITIALIZE";
106739beb93cSSam Leffler 	case SUPP_BE_RECEIVE:
106839beb93cSSam Leffler 		return "RECEIVE";
106939beb93cSSam Leffler 	default:
107039beb93cSSam Leffler 		return "UNKNOWN";
107139beb93cSSam Leffler 	}
107239beb93cSSam Leffler }
107339beb93cSSam Leffler 
107439beb93cSSam Leffler 
eapol_port_status(PortStatus status)107539beb93cSSam Leffler static const char * eapol_port_status(PortStatus status)
107639beb93cSSam Leffler {
107739beb93cSSam Leffler 	if (status == Authorized)
107839beb93cSSam Leffler 		return "Authorized";
107939beb93cSSam Leffler 	else
108039beb93cSSam Leffler 		return "Unauthorized";
108139beb93cSSam Leffler }
108239beb93cSSam Leffler #endif /* CONFIG_CTRL_IFACE */
108339beb93cSSam Leffler 
108439beb93cSSam Leffler 
108539beb93cSSam Leffler #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
eapol_port_control(PortControl ctrl)108639beb93cSSam Leffler static const char * eapol_port_control(PortControl ctrl)
108739beb93cSSam Leffler {
108839beb93cSSam Leffler 	switch (ctrl) {
108939beb93cSSam Leffler 	case Auto:
109039beb93cSSam Leffler 		return "Auto";
109139beb93cSSam Leffler 	case ForceUnauthorized:
109239beb93cSSam Leffler 		return "ForceUnauthorized";
109339beb93cSSam Leffler 	case ForceAuthorized:
109439beb93cSSam Leffler 		return "ForceAuthorized";
109539beb93cSSam Leffler 	default:
109639beb93cSSam Leffler 		return "Unknown";
109739beb93cSSam Leffler 	}
109839beb93cSSam Leffler }
109939beb93cSSam Leffler #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
110039beb93cSSam Leffler 
110139beb93cSSam Leffler 
110239beb93cSSam Leffler /**
110339beb93cSSam Leffler  * eapol_sm_configure - Set EAPOL variables
110439beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
110539beb93cSSam Leffler  * @heldPeriod: dot1xSuppHeldPeriod
110639beb93cSSam Leffler  * @authPeriod: dot1xSuppAuthPeriod
110739beb93cSSam Leffler  * @startPeriod: dot1xSuppStartPeriod
110839beb93cSSam Leffler  * @maxStart: dot1xSuppMaxStart
110939beb93cSSam Leffler  *
111039beb93cSSam Leffler  * Set configurable EAPOL state machine variables. Each variable can be set to
111139beb93cSSam Leffler  * the given value or ignored if set to -1 (to set only some of the variables).
111239beb93cSSam Leffler  */
eapol_sm_configure(struct eapol_sm * sm,int heldPeriod,int authPeriod,int startPeriod,int maxStart)111339beb93cSSam Leffler void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
111439beb93cSSam Leffler 			int startPeriod, int maxStart)
111539beb93cSSam Leffler {
111639beb93cSSam Leffler 	if (sm == NULL)
111739beb93cSSam Leffler 		return;
111839beb93cSSam Leffler 	if (heldPeriod >= 0)
111939beb93cSSam Leffler 		sm->heldPeriod = heldPeriod;
112039beb93cSSam Leffler 	if (authPeriod >= 0)
112139beb93cSSam Leffler 		sm->authPeriod = authPeriod;
112239beb93cSSam Leffler 	if (startPeriod >= 0)
112339beb93cSSam Leffler 		sm->startPeriod = startPeriod;
112439beb93cSSam Leffler 	if (maxStart >= 0)
112539beb93cSSam Leffler 		sm->maxStart = maxStart;
112639beb93cSSam Leffler }
112739beb93cSSam Leffler 
112839beb93cSSam Leffler 
1129f05cddf9SRui Paulo /**
1130f05cddf9SRui Paulo  * eapol_sm_get_method_name - Get EAPOL method name
1131f05cddf9SRui Paulo  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1132f05cddf9SRui Paulo  * Returns: Static string containing name of current eap method or NULL
1133f05cddf9SRui Paulo  */
eapol_sm_get_method_name(struct eapol_sm * sm)1134f05cddf9SRui Paulo const char * eapol_sm_get_method_name(struct eapol_sm *sm)
1135f05cddf9SRui Paulo {
1136f05cddf9SRui Paulo 	if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED ||
1137f05cddf9SRui Paulo 	    sm->suppPortStatus != Authorized)
1138f05cddf9SRui Paulo 		return NULL;
1139f05cddf9SRui Paulo 
1140f05cddf9SRui Paulo 	return eap_sm_get_method_name(sm->eap);
1141f05cddf9SRui Paulo }
1142f05cddf9SRui Paulo 
1143f05cddf9SRui Paulo 
114439beb93cSSam Leffler #ifdef CONFIG_CTRL_IFACE
114539beb93cSSam Leffler /**
114639beb93cSSam Leffler  * eapol_sm_get_status - Get EAPOL state machine status
114739beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
114839beb93cSSam Leffler  * @buf: Buffer for status information
114939beb93cSSam Leffler  * @buflen: Maximum buffer length
115039beb93cSSam Leffler  * @verbose: Whether to include verbose status information
115139beb93cSSam Leffler  * Returns: Number of bytes written to buf.
115239beb93cSSam Leffler  *
115339beb93cSSam Leffler  * Query EAPOL state machine for status information. This function fills in a
115439beb93cSSam Leffler  * text area with current status information from the EAPOL state machine. If
115539beb93cSSam Leffler  * the buffer (buf) is not large enough, status information will be truncated
115639beb93cSSam Leffler  * to fit the buffer.
115739beb93cSSam Leffler  */
eapol_sm_get_status(struct eapol_sm * sm,char * buf,size_t buflen,int verbose)115839beb93cSSam Leffler int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
115939beb93cSSam Leffler 			int verbose)
116039beb93cSSam Leffler {
116139beb93cSSam Leffler 	int len, ret;
116239beb93cSSam Leffler 	if (sm == NULL)
116339beb93cSSam Leffler 		return 0;
116439beb93cSSam Leffler 
116539beb93cSSam Leffler 	len = os_snprintf(buf, buflen,
116639beb93cSSam Leffler 			  "Supplicant PAE state=%s\n"
116739beb93cSSam Leffler 			  "suppPortStatus=%s\n",
116839beb93cSSam Leffler 			  eapol_supp_pae_state(sm->SUPP_PAE_state),
116939beb93cSSam Leffler 			  eapol_port_status(sm->suppPortStatus));
11705b9c547cSRui Paulo 	if (os_snprintf_error(buflen, len))
117139beb93cSSam Leffler 		return 0;
117239beb93cSSam Leffler 
117339beb93cSSam Leffler 	if (verbose) {
117439beb93cSSam Leffler 		ret = os_snprintf(buf + len, buflen - len,
117539beb93cSSam Leffler 				  "heldPeriod=%u\n"
117639beb93cSSam Leffler 				  "authPeriod=%u\n"
117739beb93cSSam Leffler 				  "startPeriod=%u\n"
117839beb93cSSam Leffler 				  "maxStart=%u\n"
117939beb93cSSam Leffler 				  "portControl=%s\n"
118039beb93cSSam Leffler 				  "Supplicant Backend state=%s\n",
118139beb93cSSam Leffler 				  sm->heldPeriod,
118239beb93cSSam Leffler 				  sm->authPeriod,
118339beb93cSSam Leffler 				  sm->startPeriod,
118439beb93cSSam Leffler 				  sm->maxStart,
118539beb93cSSam Leffler 				  eapol_port_control(sm->portControl),
118639beb93cSSam Leffler 				  eapol_supp_be_state(sm->SUPP_BE_state));
11875b9c547cSRui Paulo 		if (os_snprintf_error(buflen - len, ret))
118839beb93cSSam Leffler 			return len;
118939beb93cSSam Leffler 		len += ret;
119039beb93cSSam Leffler 	}
119139beb93cSSam Leffler 
11925b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
11935b9c547cSRui Paulo 	if (sm->use_eap_proxy)
11945b9c547cSRui Paulo 		len += eap_proxy_sm_get_status(sm->eap_proxy,
11955b9c547cSRui Paulo 					       buf + len, buflen - len,
11965b9c547cSRui Paulo 					       verbose);
11975b9c547cSRui Paulo 	else
11985b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
119939beb93cSSam Leffler 	len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
120039beb93cSSam Leffler 
120139beb93cSSam Leffler 	return len;
120239beb93cSSam Leffler }
120339beb93cSSam Leffler 
120439beb93cSSam Leffler 
120539beb93cSSam Leffler /**
120639beb93cSSam Leffler  * eapol_sm_get_mib - Get EAPOL state machine MIBs
120739beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
120839beb93cSSam Leffler  * @buf: Buffer for MIB information
120939beb93cSSam Leffler  * @buflen: Maximum buffer length
121039beb93cSSam Leffler  * Returns: Number of bytes written to buf.
121139beb93cSSam Leffler  *
121239beb93cSSam Leffler  * Query EAPOL state machine for MIB information. This function fills in a
121339beb93cSSam Leffler  * text area with current MIB information from the EAPOL state machine. If
121439beb93cSSam Leffler  * the buffer (buf) is not large enough, MIB information will be truncated to
121539beb93cSSam Leffler  * fit the buffer.
121639beb93cSSam Leffler  */
eapol_sm_get_mib(struct eapol_sm * sm,char * buf,size_t buflen)121739beb93cSSam Leffler int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
121839beb93cSSam Leffler {
121939beb93cSSam Leffler 	size_t len;
122039beb93cSSam Leffler 	int ret;
122139beb93cSSam Leffler 
122239beb93cSSam Leffler 	if (sm == NULL)
122339beb93cSSam Leffler 		return 0;
122439beb93cSSam Leffler 	ret = os_snprintf(buf, buflen,
122539beb93cSSam Leffler 			  "dot1xSuppPaeState=%d\n"
122639beb93cSSam Leffler 			  "dot1xSuppHeldPeriod=%u\n"
122739beb93cSSam Leffler 			  "dot1xSuppAuthPeriod=%u\n"
122839beb93cSSam Leffler 			  "dot1xSuppStartPeriod=%u\n"
122939beb93cSSam Leffler 			  "dot1xSuppMaxStart=%u\n"
123039beb93cSSam Leffler 			  "dot1xSuppSuppControlledPortStatus=%s\n"
123139beb93cSSam Leffler 			  "dot1xSuppBackendPaeState=%d\n",
123239beb93cSSam Leffler 			  sm->SUPP_PAE_state,
123339beb93cSSam Leffler 			  sm->heldPeriod,
123439beb93cSSam Leffler 			  sm->authPeriod,
123539beb93cSSam Leffler 			  sm->startPeriod,
123639beb93cSSam Leffler 			  sm->maxStart,
123739beb93cSSam Leffler 			  sm->suppPortStatus == Authorized ?
123839beb93cSSam Leffler 			  "Authorized" : "Unauthorized",
123939beb93cSSam Leffler 			  sm->SUPP_BE_state);
124039beb93cSSam Leffler 
12415b9c547cSRui Paulo 	if (os_snprintf_error(buflen, ret))
124239beb93cSSam Leffler 		return 0;
124339beb93cSSam Leffler 	len = ret;
124439beb93cSSam Leffler 
124539beb93cSSam Leffler 	ret = os_snprintf(buf + len, buflen - len,
124639beb93cSSam Leffler 			  "dot1xSuppEapolFramesRx=%u\n"
124739beb93cSSam Leffler 			  "dot1xSuppEapolFramesTx=%u\n"
124839beb93cSSam Leffler 			  "dot1xSuppEapolStartFramesTx=%u\n"
124939beb93cSSam Leffler 			  "dot1xSuppEapolLogoffFramesTx=%u\n"
125039beb93cSSam Leffler 			  "dot1xSuppEapolRespFramesTx=%u\n"
125139beb93cSSam Leffler 			  "dot1xSuppEapolReqIdFramesRx=%u\n"
125239beb93cSSam Leffler 			  "dot1xSuppEapolReqFramesRx=%u\n"
125339beb93cSSam Leffler 			  "dot1xSuppInvalidEapolFramesRx=%u\n"
125439beb93cSSam Leffler 			  "dot1xSuppEapLengthErrorFramesRx=%u\n"
125539beb93cSSam Leffler 			  "dot1xSuppLastEapolFrameVersion=%u\n"
125639beb93cSSam Leffler 			  "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
125739beb93cSSam Leffler 			  sm->dot1xSuppEapolFramesRx,
125839beb93cSSam Leffler 			  sm->dot1xSuppEapolFramesTx,
125939beb93cSSam Leffler 			  sm->dot1xSuppEapolStartFramesTx,
126039beb93cSSam Leffler 			  sm->dot1xSuppEapolLogoffFramesTx,
126139beb93cSSam Leffler 			  sm->dot1xSuppEapolRespFramesTx,
126239beb93cSSam Leffler 			  sm->dot1xSuppEapolReqIdFramesRx,
126339beb93cSSam Leffler 			  sm->dot1xSuppEapolReqFramesRx,
126439beb93cSSam Leffler 			  sm->dot1xSuppInvalidEapolFramesRx,
126539beb93cSSam Leffler 			  sm->dot1xSuppEapLengthErrorFramesRx,
126639beb93cSSam Leffler 			  sm->dot1xSuppLastEapolFrameVersion,
126739beb93cSSam Leffler 			  MAC2STR(sm->dot1xSuppLastEapolFrameSource));
126839beb93cSSam Leffler 
12695b9c547cSRui Paulo 	if (os_snprintf_error(buflen - len, ret))
127039beb93cSSam Leffler 		return len;
127139beb93cSSam Leffler 	len += ret;
127239beb93cSSam Leffler 
127339beb93cSSam Leffler 	return len;
127439beb93cSSam Leffler }
127539beb93cSSam Leffler #endif /* CONFIG_CTRL_IFACE */
127639beb93cSSam Leffler 
127739beb93cSSam Leffler 
127839beb93cSSam Leffler /**
127939beb93cSSam Leffler  * eapol_sm_rx_eapol - Process received EAPOL frames
128039beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
128139beb93cSSam Leffler  * @src: Source MAC address of the EAPOL packet
128239beb93cSSam Leffler  * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
128339beb93cSSam Leffler  * @len: Length of the EAPOL frame
1284*a90b9d01SCy Schubert  * @encrypted: Whether the frame was encrypted
128539beb93cSSam Leffler  * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
128639beb93cSSam Leffler  * -1 failure
128739beb93cSSam Leffler  */
eapol_sm_rx_eapol(struct eapol_sm * sm,const u8 * src,const u8 * buf,size_t len,enum frame_encryption encrypted)128839beb93cSSam Leffler int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
1289*a90b9d01SCy Schubert 		      size_t len, enum frame_encryption encrypted)
129039beb93cSSam Leffler {
129139beb93cSSam Leffler 	const struct ieee802_1x_hdr *hdr;
129239beb93cSSam Leffler 	const struct ieee802_1x_eapol_key *key;
129339beb93cSSam Leffler 	int data_len;
129439beb93cSSam Leffler 	int res = 1;
129539beb93cSSam Leffler 	size_t plen;
129639beb93cSSam Leffler 
129739beb93cSSam Leffler 	if (sm == NULL)
129839beb93cSSam Leffler 		return 0;
1299*a90b9d01SCy Schubert 
1300*a90b9d01SCy Schubert 	if (encrypted == FRAME_NOT_ENCRYPTED && sm->ctx->encryption_required &&
1301*a90b9d01SCy Schubert 	    sm->ctx->encryption_required(sm->ctx->ctx)) {
1302*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
1303*a90b9d01SCy Schubert 			   "EAPOL: Discard unencrypted EAPOL frame when encryption since encryption was expected");
1304*a90b9d01SCy Schubert 		return 0;
1305*a90b9d01SCy Schubert 	}
1306*a90b9d01SCy Schubert 
130739beb93cSSam Leffler 	sm->dot1xSuppEapolFramesRx++;
130839beb93cSSam Leffler 	if (len < sizeof(*hdr)) {
130939beb93cSSam Leffler 		sm->dot1xSuppInvalidEapolFramesRx++;
131039beb93cSSam Leffler 		return 0;
131139beb93cSSam Leffler 	}
131239beb93cSSam Leffler 	hdr = (const struct ieee802_1x_hdr *) buf;
131339beb93cSSam Leffler 	sm->dot1xSuppLastEapolFrameVersion = hdr->version;
131439beb93cSSam Leffler 	os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
131539beb93cSSam Leffler 	if (hdr->version < EAPOL_VERSION) {
131639beb93cSSam Leffler 		/* TODO: backwards compatibility */
131739beb93cSSam Leffler 	}
131839beb93cSSam Leffler 	plen = be_to_host16(hdr->length);
131939beb93cSSam Leffler 	if (plen > len - sizeof(*hdr)) {
132039beb93cSSam Leffler 		sm->dot1xSuppEapLengthErrorFramesRx++;
132139beb93cSSam Leffler 		return 0;
132239beb93cSSam Leffler 	}
132339beb93cSSam Leffler #ifdef CONFIG_WPS
13245b9c547cSRui Paulo 	if (sm->conf.wps && sm->conf.workaround &&
132539beb93cSSam Leffler 	    plen < len - sizeof(*hdr) &&
132639beb93cSSam Leffler 	    hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
132739beb93cSSam Leffler 	    len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
132839beb93cSSam Leffler 		const struct eap_hdr *ehdr =
132939beb93cSSam Leffler 			(const struct eap_hdr *) (hdr + 1);
133039beb93cSSam Leffler 		u16 elen;
133139beb93cSSam Leffler 
133239beb93cSSam Leffler 		elen = be_to_host16(ehdr->length);
133339beb93cSSam Leffler 		if (elen > plen && elen <= len - sizeof(*hdr)) {
133439beb93cSSam Leffler 			/*
133539beb93cSSam Leffler 			 * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
133639beb93cSSam Leffler 			 * packets with too short EAPOL header length field
133739beb93cSSam Leffler 			 * (14 octets). This is fixed in firmware Ver.1.49.
133839beb93cSSam Leffler 			 * As a workaround, fix the EAPOL header based on the
133939beb93cSSam Leffler 			 * correct length in the EAP packet.
134039beb93cSSam Leffler 			 */
134139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
134239beb93cSSam Leffler 				   "payload length based on EAP header: "
134339beb93cSSam Leffler 				   "%d -> %d", (int) plen, elen);
134439beb93cSSam Leffler 			plen = elen;
134539beb93cSSam Leffler 		}
134639beb93cSSam Leffler 	}
134739beb93cSSam Leffler #endif /* CONFIG_WPS */
134839beb93cSSam Leffler 	data_len = plen + sizeof(*hdr);
134939beb93cSSam Leffler 
135039beb93cSSam Leffler 	switch (hdr->type) {
135139beb93cSSam Leffler 	case IEEE802_1X_TYPE_EAP_PACKET:
13525b9c547cSRui Paulo 		if (sm->conf.workaround) {
13535b9c547cSRui Paulo 			/*
13545b9c547cSRui Paulo 			 * An AP has been reported to send out EAP message with
13555b9c547cSRui Paulo 			 * undocumented code 10 at some point near the
13565b9c547cSRui Paulo 			 * completion of EAP authentication. This can result in
13575b9c547cSRui Paulo 			 * issues with the unexpected EAP message triggering
13585b9c547cSRui Paulo 			 * restart of EAPOL authentication. Avoid this by
13595b9c547cSRui Paulo 			 * skipping the message without advancing the state
13605b9c547cSRui Paulo 			 * machine.
13615b9c547cSRui Paulo 			 */
13625b9c547cSRui Paulo 			const struct eap_hdr *ehdr =
13635b9c547cSRui Paulo 				(const struct eap_hdr *) (hdr + 1);
13645b9c547cSRui Paulo 			if (plen >= sizeof(*ehdr) && ehdr->code == 10) {
13655b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10");
13665b9c547cSRui Paulo 				break;
13675b9c547cSRui Paulo 			}
13685b9c547cSRui Paulo 		}
13695b9c547cSRui Paulo 
137039beb93cSSam Leffler 		if (sm->cached_pmk) {
137139beb93cSSam Leffler 			/* Trying to use PMKSA caching, but Authenticator did
137239beb93cSSam Leffler 			 * not seem to have a matching entry. Need to restart
137339beb93cSSam Leffler 			 * EAPOL state machines.
137439beb93cSSam Leffler 			 */
137539beb93cSSam Leffler 			eapol_sm_abort_cached(sm);
137639beb93cSSam Leffler 		}
137739beb93cSSam Leffler 		wpabuf_free(sm->eapReqData);
137839beb93cSSam Leffler 		sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
137939beb93cSSam Leffler 		if (sm->eapReqData) {
138039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
138139beb93cSSam Leffler 				   "frame");
1382c1d255d3SCy Schubert 			sm->eapolEap = true;
13835b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
13845b9c547cSRui Paulo 			if (sm->use_eap_proxy) {
13855b9c547cSRui Paulo 				eap_proxy_packet_update(
13865b9c547cSRui Paulo 					sm->eap_proxy,
13875b9c547cSRui Paulo 					wpabuf_mhead_u8(sm->eapReqData),
13885b9c547cSRui Paulo 					wpabuf_len(sm->eapReqData));
13895b9c547cSRui Paulo 				wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy "
13905b9c547cSRui Paulo 					   "EAP Req updated");
13915b9c547cSRui Paulo 			}
13925b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
139339beb93cSSam Leffler 			eapol_sm_step(sm);
139439beb93cSSam Leffler 		}
139539beb93cSSam Leffler 		break;
139639beb93cSSam Leffler 	case IEEE802_1X_TYPE_EAPOL_KEY:
139739beb93cSSam Leffler 		if (plen < sizeof(*key)) {
139839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
139939beb93cSSam Leffler 				   "frame received");
140039beb93cSSam Leffler 			break;
140139beb93cSSam Leffler 		}
140239beb93cSSam Leffler 		key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
140339beb93cSSam Leffler 		if (key->type == EAPOL_KEY_TYPE_WPA ||
140439beb93cSSam Leffler 		    key->type == EAPOL_KEY_TYPE_RSN) {
140539beb93cSSam Leffler 			/* WPA Supplicant takes care of this frame. */
140639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
140739beb93cSSam Leffler 				   "frame in EAPOL state machines");
140839beb93cSSam Leffler 			res = 0;
140939beb93cSSam Leffler 			break;
141039beb93cSSam Leffler 		}
141139beb93cSSam Leffler 		if (key->type != EAPOL_KEY_TYPE_RC4) {
141239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
141339beb93cSSam Leffler 				   "EAPOL-Key type %d", key->type);
141439beb93cSSam Leffler 			break;
141539beb93cSSam Leffler 		}
141639beb93cSSam Leffler 		os_free(sm->last_rx_key);
141739beb93cSSam Leffler 		sm->last_rx_key = os_malloc(data_len);
141839beb93cSSam Leffler 		if (sm->last_rx_key) {
141939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
142039beb93cSSam Leffler 				   "frame");
142139beb93cSSam Leffler 			os_memcpy(sm->last_rx_key, buf, data_len);
142239beb93cSSam Leffler 			sm->last_rx_key_len = data_len;
1423c1d255d3SCy Schubert 			sm->rxKey = true;
142439beb93cSSam Leffler 			eapol_sm_step(sm);
142539beb93cSSam Leffler 		}
142639beb93cSSam Leffler 		break;
14275b9c547cSRui Paulo #ifdef CONFIG_MACSEC
14285b9c547cSRui Paulo 	case IEEE802_1X_TYPE_EAPOL_MKA:
14295b9c547cSRui Paulo 		wpa_printf(MSG_EXCESSIVE,
14305b9c547cSRui Paulo 			   "EAPOL type %d will be handled by MKA",
14315b9c547cSRui Paulo 			   hdr->type);
14325b9c547cSRui Paulo 		break;
14335b9c547cSRui Paulo #endif /* CONFIG_MACSEC */
143439beb93cSSam Leffler 	default:
143539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
143639beb93cSSam Leffler 			   hdr->type);
143739beb93cSSam Leffler 		sm->dot1xSuppInvalidEapolFramesRx++;
143839beb93cSSam Leffler 		break;
143939beb93cSSam Leffler 	}
144039beb93cSSam Leffler 
144139beb93cSSam Leffler 	return res;
144239beb93cSSam Leffler }
144339beb93cSSam Leffler 
144439beb93cSSam Leffler 
144539beb93cSSam Leffler /**
144639beb93cSSam Leffler  * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
144739beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
144839beb93cSSam Leffler  *
144939beb93cSSam Leffler  * Notify EAPOL state machine about transmitted EAPOL packet from an external
145039beb93cSSam Leffler  * component, e.g., WPA. This will update the statistics.
145139beb93cSSam Leffler  */
eapol_sm_notify_tx_eapol_key(struct eapol_sm * sm)145239beb93cSSam Leffler void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
145339beb93cSSam Leffler {
145439beb93cSSam Leffler 	if (sm)
145539beb93cSSam Leffler 		sm->dot1xSuppEapolFramesTx++;
145639beb93cSSam Leffler }
145739beb93cSSam Leffler 
145839beb93cSSam Leffler 
145939beb93cSSam Leffler /**
146039beb93cSSam Leffler  * eapol_sm_notify_portEnabled - Notification about portEnabled change
146139beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
146239beb93cSSam Leffler  * @enabled: New portEnabled value
146339beb93cSSam Leffler  *
146439beb93cSSam Leffler  * Notify EAPOL state machine about new portEnabled value.
146539beb93cSSam Leffler  */
eapol_sm_notify_portEnabled(struct eapol_sm * sm,bool enabled)1466c1d255d3SCy Schubert void eapol_sm_notify_portEnabled(struct eapol_sm *sm, bool enabled)
146739beb93cSSam Leffler {
146839beb93cSSam Leffler 	if (sm == NULL)
146939beb93cSSam Leffler 		return;
147039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
147139beb93cSSam Leffler 		   "portEnabled=%d", enabled);
14725b9c547cSRui Paulo 	if (sm->portEnabled != enabled)
1473c1d255d3SCy Schubert 		sm->force_authorized_update = true;
147439beb93cSSam Leffler 	sm->portEnabled = enabled;
147539beb93cSSam Leffler 	eapol_sm_step(sm);
147639beb93cSSam Leffler }
147739beb93cSSam Leffler 
147839beb93cSSam Leffler 
147939beb93cSSam Leffler /**
148039beb93cSSam Leffler  * eapol_sm_notify_portValid - Notification about portValid change
148139beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
148239beb93cSSam Leffler  * @valid: New portValid value
148339beb93cSSam Leffler  *
148439beb93cSSam Leffler  * Notify EAPOL state machine about new portValid value.
148539beb93cSSam Leffler  */
eapol_sm_notify_portValid(struct eapol_sm * sm,bool valid)1486c1d255d3SCy Schubert void eapol_sm_notify_portValid(struct eapol_sm *sm, bool valid)
148739beb93cSSam Leffler {
148839beb93cSSam Leffler 	if (sm == NULL)
148939beb93cSSam Leffler 		return;
149039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
149139beb93cSSam Leffler 		   "portValid=%d", valid);
149239beb93cSSam Leffler 	sm->portValid = valid;
149339beb93cSSam Leffler 	eapol_sm_step(sm);
149439beb93cSSam Leffler }
149539beb93cSSam Leffler 
149639beb93cSSam Leffler 
149739beb93cSSam Leffler /**
149839beb93cSSam Leffler  * eapol_sm_notify_eap_success - Notification of external EAP success trigger
149939beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1500c1d255d3SCy Schubert  * @success: %true = set success, %false = clear success
150139beb93cSSam Leffler  *
150239beb93cSSam Leffler  * Notify the EAPOL state machine that external event has forced EAP state to
1503c1d255d3SCy Schubert  * success (success = %true). This can be cleared by setting success = %false.
150439beb93cSSam Leffler  *
150539beb93cSSam Leffler  * This function is called to update EAP state when WPA-PSK key handshake has
150639beb93cSSam Leffler  * been completed successfully since WPA-PSK does not use EAP state machine.
150739beb93cSSam Leffler  */
eapol_sm_notify_eap_success(struct eapol_sm * sm,bool success)1508c1d255d3SCy Schubert void eapol_sm_notify_eap_success(struct eapol_sm *sm, bool success)
150939beb93cSSam Leffler {
151039beb93cSSam Leffler 	if (sm == NULL)
151139beb93cSSam Leffler 		return;
151239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
151339beb93cSSam Leffler 		   "EAP success=%d", success);
151439beb93cSSam Leffler 	sm->eapSuccess = success;
151539beb93cSSam Leffler 	sm->altAccept = success;
151639beb93cSSam Leffler 	if (success)
151739beb93cSSam Leffler 		eap_notify_success(sm->eap);
151839beb93cSSam Leffler 	eapol_sm_step(sm);
151939beb93cSSam Leffler }
152039beb93cSSam Leffler 
152139beb93cSSam Leffler 
152239beb93cSSam Leffler /**
152339beb93cSSam Leffler  * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
152439beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1525c1d255d3SCy Schubert  * @fail: %true = set failure, %false = clear failure
152639beb93cSSam Leffler  *
152739beb93cSSam Leffler  * Notify EAPOL state machine that external event has forced EAP state to
1528c1d255d3SCy Schubert  * failure (fail = %true). This can be cleared by setting fail = %false.
152939beb93cSSam Leffler  */
eapol_sm_notify_eap_fail(struct eapol_sm * sm,bool fail)1530c1d255d3SCy Schubert void eapol_sm_notify_eap_fail(struct eapol_sm *sm, bool fail)
153139beb93cSSam Leffler {
153239beb93cSSam Leffler 	if (sm == NULL)
153339beb93cSSam Leffler 		return;
153439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
153539beb93cSSam Leffler 		   "EAP fail=%d", fail);
153639beb93cSSam Leffler 	sm->eapFail = fail;
153739beb93cSSam Leffler 	sm->altReject = fail;
153839beb93cSSam Leffler 	eapol_sm_step(sm);
153939beb93cSSam Leffler }
154039beb93cSSam Leffler 
154139beb93cSSam Leffler 
154239beb93cSSam Leffler /**
154339beb93cSSam Leffler  * eapol_sm_notify_config - Notification of EAPOL configuration change
154439beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
154539beb93cSSam Leffler  * @config: Pointer to current network EAP configuration
154639beb93cSSam Leffler  * @conf: Pointer to EAPOL configuration data
154739beb93cSSam Leffler  *
154839beb93cSSam Leffler  * Notify EAPOL state machine that configuration has changed. config will be
154939beb93cSSam Leffler  * stored as a backpointer to network configuration. This can be %NULL to clear
155039beb93cSSam Leffler  * the stored pointed. conf will be copied to local EAPOL/EAP configuration
155139beb93cSSam Leffler  * data. If conf is %NULL, this part of the configuration change will be
155239beb93cSSam Leffler  * skipped.
155339beb93cSSam Leffler  */
eapol_sm_notify_config(struct eapol_sm * sm,struct eap_peer_config * config,const struct eapol_config * conf)155439beb93cSSam Leffler void eapol_sm_notify_config(struct eapol_sm *sm,
155539beb93cSSam Leffler 			    struct eap_peer_config *config,
155639beb93cSSam Leffler 			    const struct eapol_config *conf)
155739beb93cSSam Leffler {
155839beb93cSSam Leffler 	if (sm == NULL)
155939beb93cSSam Leffler 		return;
156039beb93cSSam Leffler 
156139beb93cSSam Leffler 	sm->config = config;
15625b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
15635b9c547cSRui Paulo 	sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0;
15645b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
156539beb93cSSam Leffler 
156639beb93cSSam Leffler 	if (conf == NULL)
156739beb93cSSam Leffler 		return;
156839beb93cSSam Leffler 
156939beb93cSSam Leffler 	sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
157039beb93cSSam Leffler 	sm->conf.required_keys = conf->required_keys;
157139beb93cSSam Leffler 	sm->conf.fast_reauth = conf->fast_reauth;
157239beb93cSSam Leffler 	sm->conf.workaround = conf->workaround;
15735b9c547cSRui Paulo 	sm->conf.wps = conf->wps;
15745b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
15755b9c547cSRui Paulo 	if (sm->use_eap_proxy) {
15765b9c547cSRui Paulo 		/* Using EAP Proxy, so skip EAP state machine update */
15775b9c547cSRui Paulo 		return;
15785b9c547cSRui Paulo 	}
15795b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
158039beb93cSSam Leffler 	if (sm->eap) {
158139beb93cSSam Leffler 		eap_set_fast_reauth(sm->eap, conf->fast_reauth);
158239beb93cSSam Leffler 		eap_set_workaround(sm->eap, conf->workaround);
158339beb93cSSam Leffler 		eap_set_force_disabled(sm->eap, conf->eap_disabled);
15845b9c547cSRui Paulo 		eap_set_external_sim(sm->eap, conf->external_sim);
158539beb93cSSam Leffler 	}
158639beb93cSSam Leffler }
158739beb93cSSam Leffler 
158839beb93cSSam Leffler 
158939beb93cSSam Leffler /**
159039beb93cSSam Leffler  * eapol_sm_get_key - Get master session key (MSK) from EAP
159139beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
159239beb93cSSam Leffler  * @key: Pointer for key buffer
159339beb93cSSam Leffler  * @len: Number of bytes to copy to key
159439beb93cSSam Leffler  * Returns: 0 on success (len of key available), maximum available key len
159539beb93cSSam Leffler  * (>0) if key is available but it is shorter than len, or -1 on failure.
159639beb93cSSam Leffler  *
159739beb93cSSam Leffler  * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
159839beb93cSSam Leffler  * is available only after a successful authentication.
159939beb93cSSam Leffler  */
eapol_sm_get_key(struct eapol_sm * sm,u8 * key,size_t len)160039beb93cSSam Leffler int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
160139beb93cSSam Leffler {
160239beb93cSSam Leffler 	const u8 *eap_key;
160339beb93cSSam Leffler 	size_t eap_len;
160439beb93cSSam Leffler 
16055b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
16065b9c547cSRui Paulo 	if (sm && sm->use_eap_proxy) {
16075b9c547cSRui Paulo 		/* Get key from EAP proxy */
16085b9c547cSRui Paulo 		if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) {
16095b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
16105b9c547cSRui Paulo 			return -1;
16115b9c547cSRui Paulo 		}
16125b9c547cSRui Paulo 		eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len);
16135b9c547cSRui Paulo 		if (eap_key == NULL) {
16145b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "EAPOL: Failed to get "
16155b9c547cSRui Paulo 				   "eapKeyData");
16165b9c547cSRui Paulo 			return -1;
16175b9c547cSRui Paulo 		}
16185b9c547cSRui Paulo 		goto key_fetched;
16195b9c547cSRui Paulo 	}
16205b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
162139beb93cSSam Leffler 	if (sm == NULL || !eap_key_available(sm->eap)) {
162239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
162339beb93cSSam Leffler 		return -1;
162439beb93cSSam Leffler 	}
162539beb93cSSam Leffler 	eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
162639beb93cSSam Leffler 	if (eap_key == NULL) {
162739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
162839beb93cSSam Leffler 		return -1;
162939beb93cSSam Leffler 	}
16305b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
16315b9c547cSRui Paulo key_fetched:
16325b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
163339beb93cSSam Leffler 	if (len > eap_len) {
163439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
163539beb93cSSam Leffler 			   "available (len=%lu)",
163639beb93cSSam Leffler 			   (unsigned long) len, (unsigned long) eap_len);
163739beb93cSSam Leffler 		return eap_len;
163839beb93cSSam Leffler 	}
163939beb93cSSam Leffler 	os_memcpy(key, eap_key, len);
164039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
164139beb93cSSam Leffler 		   (unsigned long) len);
164239beb93cSSam Leffler 	return 0;
164339beb93cSSam Leffler }
164439beb93cSSam Leffler 
164539beb93cSSam Leffler 
164639beb93cSSam Leffler /**
16475b9c547cSRui Paulo  * eapol_sm_get_session_id - Get EAP Session-Id
16485b9c547cSRui Paulo  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
16495b9c547cSRui Paulo  * @len: Pointer to variable that will be set to number of bytes in the session
16505b9c547cSRui Paulo  * Returns: Pointer to the EAP Session-Id or %NULL on failure
16515b9c547cSRui Paulo  *
16525b9c547cSRui Paulo  * The Session-Id is available only after a successful authentication.
16535b9c547cSRui Paulo  */
eapol_sm_get_session_id(struct eapol_sm * sm,size_t * len)16545b9c547cSRui Paulo const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
16555b9c547cSRui Paulo {
16565b9c547cSRui Paulo 	if (sm == NULL || !eap_key_available(sm->eap)) {
16575b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAPOL: EAP Session-Id not available");
16585b9c547cSRui Paulo 		return NULL;
16595b9c547cSRui Paulo 	}
16605b9c547cSRui Paulo 	return eap_get_eapSessionId(sm->eap, len);
16615b9c547cSRui Paulo }
16625b9c547cSRui Paulo 
16635b9c547cSRui Paulo 
16645b9c547cSRui Paulo /**
166539beb93cSSam Leffler  * eapol_sm_notify_logoff - Notification of logon/logoff commands
166639beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
166739beb93cSSam Leffler  * @logoff: Whether command was logoff
166839beb93cSSam Leffler  *
166939beb93cSSam Leffler  * Notify EAPOL state machines that user requested logon/logoff.
167039beb93cSSam Leffler  */
eapol_sm_notify_logoff(struct eapol_sm * sm,bool logoff)1671c1d255d3SCy Schubert void eapol_sm_notify_logoff(struct eapol_sm *sm, bool logoff)
167239beb93cSSam Leffler {
167339beb93cSSam Leffler 	if (sm) {
167439beb93cSSam Leffler 		sm->userLogoff = logoff;
16755b9c547cSRui Paulo 		if (!logoff) {
16765b9c547cSRui Paulo 			/* If there is a delayed txStart queued, start now. */
16775b9c547cSRui Paulo 			sm->startWhen = 0;
16785b9c547cSRui Paulo 		}
167939beb93cSSam Leffler 		eapol_sm_step(sm);
168039beb93cSSam Leffler 	}
168139beb93cSSam Leffler }
168239beb93cSSam Leffler 
168339beb93cSSam Leffler 
168439beb93cSSam Leffler /**
168539beb93cSSam Leffler  * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
168639beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
168739beb93cSSam Leffler  *
168839beb93cSSam Leffler  * Notify EAPOL state machines that PMKSA caching was successful. This is used
168939beb93cSSam Leffler  * to move EAPOL and EAP state machines into authenticated/successful state.
169039beb93cSSam Leffler  */
eapol_sm_notify_cached(struct eapol_sm * sm)169139beb93cSSam Leffler void eapol_sm_notify_cached(struct eapol_sm *sm)
169239beb93cSSam Leffler {
169339beb93cSSam Leffler 	if (sm == NULL)
169439beb93cSSam Leffler 		return;
169539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
1696c1d255d3SCy Schubert 	sm->eapSuccess = true;
169739beb93cSSam Leffler 	eap_notify_success(sm->eap);
169839beb93cSSam Leffler 	eapol_sm_step(sm);
169939beb93cSSam Leffler }
170039beb93cSSam Leffler 
170139beb93cSSam Leffler 
170239beb93cSSam Leffler /**
170339beb93cSSam Leffler  * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
170439beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
170539beb93cSSam Leffler  *
17065b9c547cSRui Paulo  * Notify EAPOL state machines if PMKSA caching is used.
170739beb93cSSam Leffler  */
eapol_sm_notify_pmkid_attempt(struct eapol_sm * sm)17085b9c547cSRui Paulo void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
170939beb93cSSam Leffler {
171039beb93cSSam Leffler 	if (sm == NULL)
171139beb93cSSam Leffler 		return;
171239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
1713c1d255d3SCy Schubert 	sm->cached_pmk = true;
171439beb93cSSam Leffler }
171539beb93cSSam Leffler 
171639beb93cSSam Leffler 
eapol_sm_abort_cached(struct eapol_sm * sm)171739beb93cSSam Leffler static void eapol_sm_abort_cached(struct eapol_sm *sm)
171839beb93cSSam Leffler {
171939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
172039beb93cSSam Leffler 		   "doing full EAP authentication");
172139beb93cSSam Leffler 	if (sm == NULL)
172239beb93cSSam Leffler 		return;
1723c1d255d3SCy Schubert 	sm->cached_pmk = false;
172439beb93cSSam Leffler 	sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
1725e28a4053SRui Paulo 	eapol_sm_set_port_unauthorized(sm);
172639beb93cSSam Leffler 
172739beb93cSSam Leffler 	/* Make sure we do not start sending EAPOL-Start frames first, but
172839beb93cSSam Leffler 	 * instead move to RESTART state to start EAPOL authentication. */
172939beb93cSSam Leffler 	sm->startWhen = 3;
173039beb93cSSam Leffler 	eapol_enable_timer_tick(sm);
173139beb93cSSam Leffler 
173239beb93cSSam Leffler 	if (sm->ctx->aborted_cached)
173339beb93cSSam Leffler 		sm->ctx->aborted_cached(sm->ctx->ctx);
173439beb93cSSam Leffler }
173539beb93cSSam Leffler 
173639beb93cSSam Leffler 
173739beb93cSSam Leffler /**
173839beb93cSSam Leffler  * eapol_sm_register_scard_ctx - Notification of smart card context
173939beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
174039beb93cSSam Leffler  * @ctx: Context data for smart card operations
174139beb93cSSam Leffler  *
174239beb93cSSam Leffler  * Notify EAPOL state machines of context data for smart card operations. This
174339beb93cSSam Leffler  * context data will be used as a parameter for scard_*() functions.
174439beb93cSSam Leffler  */
eapol_sm_register_scard_ctx(struct eapol_sm * sm,void * ctx)174539beb93cSSam Leffler void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
174639beb93cSSam Leffler {
174739beb93cSSam Leffler 	if (sm) {
174839beb93cSSam Leffler 		sm->ctx->scard_ctx = ctx;
174939beb93cSSam Leffler 		eap_register_scard_ctx(sm->eap, ctx);
175039beb93cSSam Leffler 	}
175139beb93cSSam Leffler }
175239beb93cSSam Leffler 
175339beb93cSSam Leffler 
175439beb93cSSam Leffler /**
175539beb93cSSam Leffler  * eapol_sm_notify_portControl - Notification of portControl changes
175639beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
175739beb93cSSam Leffler  * @portControl: New value for portControl variable
175839beb93cSSam Leffler  *
175939beb93cSSam Leffler  * Notify EAPOL state machines that portControl variable has changed.
176039beb93cSSam Leffler  */
eapol_sm_notify_portControl(struct eapol_sm * sm,PortControl portControl)176139beb93cSSam Leffler void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
176239beb93cSSam Leffler {
176339beb93cSSam Leffler 	if (sm == NULL)
176439beb93cSSam Leffler 		return;
176539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
176639beb93cSSam Leffler 		   "portControl=%s", eapol_port_control(portControl));
176739beb93cSSam Leffler 	sm->portControl = portControl;
176839beb93cSSam Leffler 	eapol_sm_step(sm);
176939beb93cSSam Leffler }
177039beb93cSSam Leffler 
177139beb93cSSam Leffler 
177239beb93cSSam Leffler /**
177339beb93cSSam Leffler  * eapol_sm_notify_ctrl_attached - Notification of attached monitor
177439beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
177539beb93cSSam Leffler  *
177639beb93cSSam Leffler  * Notify EAPOL state machines that a monitor was attached to the control
177739beb93cSSam Leffler  * interface to trigger re-sending of pending requests for user input.
177839beb93cSSam Leffler  */
eapol_sm_notify_ctrl_attached(struct eapol_sm * sm)177939beb93cSSam Leffler void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
178039beb93cSSam Leffler {
178139beb93cSSam Leffler 	if (sm == NULL)
178239beb93cSSam Leffler 		return;
178339beb93cSSam Leffler 	eap_sm_notify_ctrl_attached(sm->eap);
178439beb93cSSam Leffler }
178539beb93cSSam Leffler 
178639beb93cSSam Leffler 
178739beb93cSSam Leffler /**
178839beb93cSSam Leffler  * eapol_sm_notify_ctrl_response - Notification of received user input
178939beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
179039beb93cSSam Leffler  *
179139beb93cSSam Leffler  * Notify EAPOL state machines that a control response, i.e., user
179239beb93cSSam Leffler  * input, was received in order to trigger retrying of a pending EAP request.
179339beb93cSSam Leffler  */
eapol_sm_notify_ctrl_response(struct eapol_sm * sm)179439beb93cSSam Leffler void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
179539beb93cSSam Leffler {
179639beb93cSSam Leffler 	if (sm == NULL)
179739beb93cSSam Leffler 		return;
179839beb93cSSam Leffler 	if (sm->eapReqData && !sm->eapReq) {
179939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
180039beb93cSSam Leffler 			   "input) notification - retrying pending EAP "
180139beb93cSSam Leffler 			   "Request");
1802c1d255d3SCy Schubert 		sm->eapolEap = true;
1803c1d255d3SCy Schubert 		sm->eapReq = true;
180439beb93cSSam Leffler 		eapol_sm_step(sm);
180539beb93cSSam Leffler 	}
180639beb93cSSam Leffler }
180739beb93cSSam Leffler 
180839beb93cSSam Leffler 
180939beb93cSSam Leffler /**
181039beb93cSSam Leffler  * eapol_sm_request_reauth - Request reauthentication
181139beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
181239beb93cSSam Leffler  *
181339beb93cSSam Leffler  * This function can be used to request EAPOL reauthentication, e.g., when the
181439beb93cSSam Leffler  * current PMKSA entry is nearing expiration.
181539beb93cSSam Leffler  */
eapol_sm_request_reauth(struct eapol_sm * sm)181639beb93cSSam Leffler void eapol_sm_request_reauth(struct eapol_sm *sm)
181739beb93cSSam Leffler {
181839beb93cSSam Leffler 	if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
181939beb93cSSam Leffler 		return;
182039beb93cSSam Leffler 	eapol_sm_txStart(sm);
182139beb93cSSam Leffler }
182239beb93cSSam Leffler 
182339beb93cSSam Leffler 
182439beb93cSSam Leffler /**
182539beb93cSSam Leffler  * eapol_sm_notify_lower_layer_success - Notification of lower layer success
182639beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
182739beb93cSSam Leffler  * @in_eapol_sm: Whether the caller is already running inside EAPOL state
182839beb93cSSam Leffler  * machine loop (eapol_sm_step())
182939beb93cSSam Leffler  *
183039beb93cSSam Leffler  * Notify EAPOL (and EAP) state machines that a lower layer has detected a
183139beb93cSSam Leffler  * successful authentication. This is used to recover from dropped EAP-Success
183239beb93cSSam Leffler  * messages.
183339beb93cSSam Leffler  */
eapol_sm_notify_lower_layer_success(struct eapol_sm * sm,int in_eapol_sm)183439beb93cSSam Leffler void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
183539beb93cSSam Leffler {
183639beb93cSSam Leffler 	if (sm == NULL)
183739beb93cSSam Leffler 		return;
183839beb93cSSam Leffler 	eap_notify_lower_layer_success(sm->eap);
183939beb93cSSam Leffler 	if (!in_eapol_sm)
184039beb93cSSam Leffler 		eapol_sm_step(sm);
184139beb93cSSam Leffler }
184239beb93cSSam Leffler 
184339beb93cSSam Leffler 
184439beb93cSSam Leffler /**
184539beb93cSSam Leffler  * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
184639beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
184739beb93cSSam Leffler  */
eapol_sm_invalidate_cached_session(struct eapol_sm * sm)184839beb93cSSam Leffler void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
184939beb93cSSam Leffler {
185039beb93cSSam Leffler 	if (sm)
185139beb93cSSam Leffler 		eap_invalidate_cached_session(sm->eap);
185239beb93cSSam Leffler }
185339beb93cSSam Leffler 
185439beb93cSSam Leffler 
eapol_sm_get_config(void * ctx)185539beb93cSSam Leffler static struct eap_peer_config * eapol_sm_get_config(void *ctx)
185639beb93cSSam Leffler {
185739beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
185839beb93cSSam Leffler 	return sm ? sm->config : NULL;
185939beb93cSSam Leffler }
186039beb93cSSam Leffler 
186139beb93cSSam Leffler 
eapol_sm_get_eapReqData(void * ctx)186239beb93cSSam Leffler static struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
186339beb93cSSam Leffler {
186439beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
186539beb93cSSam Leffler 	if (sm == NULL || sm->eapReqData == NULL)
186639beb93cSSam Leffler 		return NULL;
186739beb93cSSam Leffler 
186839beb93cSSam Leffler 	return sm->eapReqData;
186939beb93cSSam Leffler }
187039beb93cSSam Leffler 
187139beb93cSSam Leffler 
eapol_sm_get_bool(void * ctx,enum eapol_bool_var variable)1872c1d255d3SCy Schubert static bool eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
187339beb93cSSam Leffler {
187439beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
187539beb93cSSam Leffler 	if (sm == NULL)
1876c1d255d3SCy Schubert 		return false;
187739beb93cSSam Leffler 	switch (variable) {
187839beb93cSSam Leffler 	case EAPOL_eapSuccess:
187939beb93cSSam Leffler 		return sm->eapSuccess;
188039beb93cSSam Leffler 	case EAPOL_eapRestart:
188139beb93cSSam Leffler 		return sm->eapRestart;
188239beb93cSSam Leffler 	case EAPOL_eapFail:
188339beb93cSSam Leffler 		return sm->eapFail;
188439beb93cSSam Leffler 	case EAPOL_eapResp:
188539beb93cSSam Leffler 		return sm->eapResp;
188639beb93cSSam Leffler 	case EAPOL_eapNoResp:
188739beb93cSSam Leffler 		return sm->eapNoResp;
188839beb93cSSam Leffler 	case EAPOL_eapReq:
188939beb93cSSam Leffler 		return sm->eapReq;
189039beb93cSSam Leffler 	case EAPOL_portEnabled:
189139beb93cSSam Leffler 		return sm->portEnabled;
189239beb93cSSam Leffler 	case EAPOL_altAccept:
189339beb93cSSam Leffler 		return sm->altAccept;
189439beb93cSSam Leffler 	case EAPOL_altReject:
189539beb93cSSam Leffler 		return sm->altReject;
18965b9c547cSRui Paulo 	case EAPOL_eapTriggerStart:
18975b9c547cSRui Paulo 		return sm->eapTriggerStart;
189839beb93cSSam Leffler 	}
1899c1d255d3SCy Schubert 	return false;
190039beb93cSSam Leffler }
190139beb93cSSam Leffler 
190239beb93cSSam Leffler 
eapol_sm_set_bool(void * ctx,enum eapol_bool_var variable,bool value)190339beb93cSSam Leffler static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
1904c1d255d3SCy Schubert 			      bool value)
190539beb93cSSam Leffler {
190639beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
190739beb93cSSam Leffler 	if (sm == NULL)
190839beb93cSSam Leffler 		return;
190939beb93cSSam Leffler 	switch (variable) {
191039beb93cSSam Leffler 	case EAPOL_eapSuccess:
191139beb93cSSam Leffler 		sm->eapSuccess = value;
191239beb93cSSam Leffler 		break;
191339beb93cSSam Leffler 	case EAPOL_eapRestart:
191439beb93cSSam Leffler 		sm->eapRestart = value;
191539beb93cSSam Leffler 		break;
191639beb93cSSam Leffler 	case EAPOL_eapFail:
191739beb93cSSam Leffler 		sm->eapFail = value;
191839beb93cSSam Leffler 		break;
191939beb93cSSam Leffler 	case EAPOL_eapResp:
192039beb93cSSam Leffler 		sm->eapResp = value;
192139beb93cSSam Leffler 		break;
192239beb93cSSam Leffler 	case EAPOL_eapNoResp:
192339beb93cSSam Leffler 		sm->eapNoResp = value;
192439beb93cSSam Leffler 		break;
192539beb93cSSam Leffler 	case EAPOL_eapReq:
192639beb93cSSam Leffler 		sm->eapReq = value;
192739beb93cSSam Leffler 		break;
192839beb93cSSam Leffler 	case EAPOL_portEnabled:
192939beb93cSSam Leffler 		sm->portEnabled = value;
193039beb93cSSam Leffler 		break;
193139beb93cSSam Leffler 	case EAPOL_altAccept:
193239beb93cSSam Leffler 		sm->altAccept = value;
193339beb93cSSam Leffler 		break;
193439beb93cSSam Leffler 	case EAPOL_altReject:
193539beb93cSSam Leffler 		sm->altReject = value;
193639beb93cSSam Leffler 		break;
19375b9c547cSRui Paulo 	case EAPOL_eapTriggerStart:
19385b9c547cSRui Paulo 		sm->eapTriggerStart = value;
19395b9c547cSRui Paulo 		break;
194039beb93cSSam Leffler 	}
194139beb93cSSam Leffler }
194239beb93cSSam Leffler 
194339beb93cSSam Leffler 
eapol_sm_get_int(void * ctx,enum eapol_int_var variable)194439beb93cSSam Leffler static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
194539beb93cSSam Leffler {
194639beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
194739beb93cSSam Leffler 	if (sm == NULL)
194839beb93cSSam Leffler 		return 0;
194939beb93cSSam Leffler 	switch (variable) {
195039beb93cSSam Leffler 	case EAPOL_idleWhile:
195139beb93cSSam Leffler 		return sm->idleWhile;
195239beb93cSSam Leffler 	}
195339beb93cSSam Leffler 	return 0;
195439beb93cSSam Leffler }
195539beb93cSSam Leffler 
195639beb93cSSam Leffler 
eapol_sm_set_int(void * ctx,enum eapol_int_var variable,unsigned int value)195739beb93cSSam Leffler static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
195839beb93cSSam Leffler 			     unsigned int value)
195939beb93cSSam Leffler {
196039beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
196139beb93cSSam Leffler 	if (sm == NULL)
196239beb93cSSam Leffler 		return;
196339beb93cSSam Leffler 	switch (variable) {
196439beb93cSSam Leffler 	case EAPOL_idleWhile:
196539beb93cSSam Leffler 		sm->idleWhile = value;
1966f05cddf9SRui Paulo 		if (sm->idleWhile > 0)
196739beb93cSSam Leffler 			eapol_enable_timer_tick(sm);
196839beb93cSSam Leffler 		break;
196939beb93cSSam Leffler 	}
197039beb93cSSam Leffler }
197139beb93cSSam Leffler 
197239beb93cSSam Leffler 
eapol_sm_set_config_blob(void * ctx,struct wpa_config_blob * blob)197339beb93cSSam Leffler static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
197439beb93cSSam Leffler {
197539beb93cSSam Leffler #ifndef CONFIG_NO_CONFIG_BLOBS
197639beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
197739beb93cSSam Leffler 	if (sm && sm->ctx && sm->ctx->set_config_blob)
197839beb93cSSam Leffler 		sm->ctx->set_config_blob(sm->ctx->ctx, blob);
197939beb93cSSam Leffler #endif /* CONFIG_NO_CONFIG_BLOBS */
198039beb93cSSam Leffler }
198139beb93cSSam Leffler 
198239beb93cSSam Leffler 
198339beb93cSSam Leffler static const struct wpa_config_blob *
eapol_sm_get_config_blob(void * ctx,const char * name)198439beb93cSSam Leffler eapol_sm_get_config_blob(void *ctx, const char *name)
198539beb93cSSam Leffler {
198639beb93cSSam Leffler #ifndef CONFIG_NO_CONFIG_BLOBS
198739beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
198839beb93cSSam Leffler 	if (sm && sm->ctx && sm->ctx->get_config_blob)
198939beb93cSSam Leffler 		return sm->ctx->get_config_blob(sm->ctx->ctx, name);
199039beb93cSSam Leffler 	else
199139beb93cSSam Leffler 		return NULL;
199239beb93cSSam Leffler #else /* CONFIG_NO_CONFIG_BLOBS */
199339beb93cSSam Leffler 	return NULL;
199439beb93cSSam Leffler #endif /* CONFIG_NO_CONFIG_BLOBS */
199539beb93cSSam Leffler }
199639beb93cSSam Leffler 
199739beb93cSSam Leffler 
eapol_sm_notify_pending(void * ctx)199839beb93cSSam Leffler static void eapol_sm_notify_pending(void *ctx)
199939beb93cSSam Leffler {
200039beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
200139beb93cSSam Leffler 	if (sm == NULL)
200239beb93cSSam Leffler 		return;
200339beb93cSSam Leffler 	if (sm->eapReqData && !sm->eapReq) {
200439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
200539beb93cSSam Leffler 			   "state machine - retrying pending EAP Request");
2006c1d255d3SCy Schubert 		sm->eapolEap = true;
2007c1d255d3SCy Schubert 		sm->eapReq = true;
200839beb93cSSam Leffler 		eapol_sm_step(sm);
200939beb93cSSam Leffler 	}
201039beb93cSSam Leffler }
201139beb93cSSam Leffler 
201239beb93cSSam Leffler 
201339beb93cSSam Leffler #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
eapol_sm_eap_param_needed(void * ctx,enum wpa_ctrl_req_type field,const char * txt)2014f05cddf9SRui Paulo static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
201539beb93cSSam Leffler 				      const char *txt)
201639beb93cSSam Leffler {
201739beb93cSSam Leffler 	struct eapol_sm *sm = ctx;
201839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
201939beb93cSSam Leffler 	if (sm->ctx->eap_param_needed)
202039beb93cSSam Leffler 		sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
202139beb93cSSam Leffler }
202239beb93cSSam Leffler #else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
202339beb93cSSam Leffler #define eapol_sm_eap_param_needed NULL
202439beb93cSSam Leffler #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
202539beb93cSSam Leffler 
eapol_sm_notify_cert(void * ctx,struct tls_cert_data * cert,const char * cert_hash)2026206b73d0SCy Schubert static void eapol_sm_notify_cert(void *ctx, struct tls_cert_data *cert,
2027206b73d0SCy Schubert 				 const char *cert_hash)
2028f05cddf9SRui Paulo {
2029f05cddf9SRui Paulo 	struct eapol_sm *sm = ctx;
2030f05cddf9SRui Paulo 	if (sm->ctx->cert_cb)
2031206b73d0SCy Schubert 		sm->ctx->cert_cb(sm->ctx->ctx, cert, cert_hash);
2032f05cddf9SRui Paulo }
2033f05cddf9SRui Paulo 
2034f05cddf9SRui Paulo 
eapol_sm_notify_status(void * ctx,const char * status,const char * parameter)2035f05cddf9SRui Paulo static void eapol_sm_notify_status(void *ctx, const char *status,
2036f05cddf9SRui Paulo 				   const char *parameter)
2037f05cddf9SRui Paulo {
2038f05cddf9SRui Paulo 	struct eapol_sm *sm = ctx;
2039f05cddf9SRui Paulo 
2040f05cddf9SRui Paulo 	if (sm->ctx->status_cb)
2041f05cddf9SRui Paulo 		sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
2042f05cddf9SRui Paulo }
2043f05cddf9SRui Paulo 
2044f05cddf9SRui Paulo 
eapol_sm_notify_eap_error(void * ctx,int error_code)204585732ac8SCy Schubert static void eapol_sm_notify_eap_error(void *ctx, int error_code)
204685732ac8SCy Schubert {
204785732ac8SCy Schubert 	struct eapol_sm *sm = ctx;
204885732ac8SCy Schubert 
204985732ac8SCy Schubert 	if (sm->ctx->eap_error_cb)
205085732ac8SCy Schubert 		sm->ctx->eap_error_cb(sm->ctx->ctx, error_code);
205185732ac8SCy Schubert }
205285732ac8SCy Schubert 
205385732ac8SCy Schubert 
20545b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
205585732ac8SCy Schubert 
eapol_sm_eap_proxy_cb(void * ctx)20565b9c547cSRui Paulo static void eapol_sm_eap_proxy_cb(void *ctx)
20575b9c547cSRui Paulo {
20585b9c547cSRui Paulo 	struct eapol_sm *sm = ctx;
20595b9c547cSRui Paulo 
20605b9c547cSRui Paulo 	if (sm->ctx->eap_proxy_cb)
20615b9c547cSRui Paulo 		sm->ctx->eap_proxy_cb(sm->ctx->ctx);
20625b9c547cSRui Paulo }
206385732ac8SCy Schubert 
206485732ac8SCy Schubert 
206585732ac8SCy Schubert static void
eapol_sm_eap_proxy_notify_sim_status(void * ctx,enum eap_proxy_sim_state sim_state)206685732ac8SCy Schubert eapol_sm_eap_proxy_notify_sim_status(void *ctx,
206785732ac8SCy Schubert 				     enum eap_proxy_sim_state sim_state)
206885732ac8SCy Schubert {
206985732ac8SCy Schubert 	struct eapol_sm *sm = ctx;
207085732ac8SCy Schubert 
207185732ac8SCy Schubert 	if (sm->ctx->eap_proxy_notify_sim_status)
207285732ac8SCy Schubert 		sm->ctx->eap_proxy_notify_sim_status(sm->ctx->ctx, sim_state);
207385732ac8SCy Schubert }
207485732ac8SCy Schubert 
20755b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
20765b9c547cSRui Paulo 
20775b9c547cSRui Paulo 
eapol_sm_set_anon_id(void * ctx,const u8 * id,size_t len)2078f05cddf9SRui Paulo static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
2079f05cddf9SRui Paulo {
2080f05cddf9SRui Paulo 	struct eapol_sm *sm = ctx;
2081f05cddf9SRui Paulo 
2082f05cddf9SRui Paulo 	if (sm->ctx->set_anon_id)
2083f05cddf9SRui Paulo 		sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
2084f05cddf9SRui Paulo }
2085f05cddf9SRui Paulo 
208639beb93cSSam Leffler 
2087325151a3SRui Paulo static const struct eapol_callbacks eapol_cb =
208839beb93cSSam Leffler {
208939beb93cSSam Leffler 	eapol_sm_get_config,
209039beb93cSSam Leffler 	eapol_sm_get_bool,
209139beb93cSSam Leffler 	eapol_sm_set_bool,
209239beb93cSSam Leffler 	eapol_sm_get_int,
209339beb93cSSam Leffler 	eapol_sm_set_int,
209439beb93cSSam Leffler 	eapol_sm_get_eapReqData,
209539beb93cSSam Leffler 	eapol_sm_set_config_blob,
209639beb93cSSam Leffler 	eapol_sm_get_config_blob,
209739beb93cSSam Leffler 	eapol_sm_notify_pending,
2098f05cddf9SRui Paulo 	eapol_sm_eap_param_needed,
2099f05cddf9SRui Paulo 	eapol_sm_notify_cert,
2100f05cddf9SRui Paulo 	eapol_sm_notify_status,
210185732ac8SCy Schubert 	eapol_sm_notify_eap_error,
21025b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
21035b9c547cSRui Paulo 	eapol_sm_eap_proxy_cb,
210485732ac8SCy Schubert 	eapol_sm_eap_proxy_notify_sim_status,
210585732ac8SCy Schubert 	eapol_sm_get_eap_proxy_imsi,
21065b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
2107f05cddf9SRui Paulo 	eapol_sm_set_anon_id
210839beb93cSSam Leffler };
210939beb93cSSam Leffler 
211039beb93cSSam Leffler 
211139beb93cSSam Leffler /**
211239beb93cSSam Leffler  * eapol_sm_init - Initialize EAPOL state machine
211339beb93cSSam Leffler  * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
211439beb93cSSam Leffler  * and EAPOL state machine will free it in eapol_sm_deinit()
211539beb93cSSam Leffler  * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
211639beb93cSSam Leffler  *
211739beb93cSSam Leffler  * Allocate and initialize an EAPOL state machine.
211839beb93cSSam Leffler  */
eapol_sm_init(struct eapol_ctx * ctx)211939beb93cSSam Leffler struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
212039beb93cSSam Leffler {
212139beb93cSSam Leffler 	struct eapol_sm *sm;
212239beb93cSSam Leffler 	struct eap_config conf;
212339beb93cSSam Leffler 	sm = os_zalloc(sizeof(*sm));
212439beb93cSSam Leffler 	if (sm == NULL)
212539beb93cSSam Leffler 		return NULL;
212639beb93cSSam Leffler 	sm->ctx = ctx;
212739beb93cSSam Leffler 
212839beb93cSSam Leffler 	sm->portControl = Auto;
212939beb93cSSam Leffler 
213039beb93cSSam Leffler 	/* Supplicant PAE state machine */
213139beb93cSSam Leffler 	sm->heldPeriod = 60;
213239beb93cSSam Leffler 	sm->startPeriod = 30;
213339beb93cSSam Leffler 	sm->maxStart = 3;
213439beb93cSSam Leffler 
213539beb93cSSam Leffler 	/* Supplicant Backend state machine */
213639beb93cSSam Leffler 	sm->authPeriod = 30;
213739beb93cSSam Leffler 
213839beb93cSSam Leffler 	os_memset(&conf, 0, sizeof(conf));
2139*a90b9d01SCy Schubert #ifndef CONFIG_OPENSC_ENGINE_PATH
214039beb93cSSam Leffler 	conf.opensc_engine_path = ctx->opensc_engine_path;
2141*a90b9d01SCy Schubert #endif /* CONFIG_OPENSC_ENGINE_PATH */
2142*a90b9d01SCy Schubert #ifndef CONFIG_PKCS11_ENGINE_PATH
214339beb93cSSam Leffler 	conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
2144*a90b9d01SCy Schubert #endif /* CONFIG_PKCS11_ENGINE_PATH */
2145*a90b9d01SCy Schubert #ifndef CONFIG_PKCS11_MODULE_PATH
214639beb93cSSam Leffler 	conf.pkcs11_module_path = ctx->pkcs11_module_path;
2147*a90b9d01SCy Schubert #endif /* CONFIG_PKCS11_MODULE_PATH */
21485b9c547cSRui Paulo 	conf.openssl_ciphers = ctx->openssl_ciphers;
214939beb93cSSam Leffler 	conf.wps = ctx->wps;
2150f05cddf9SRui Paulo 	conf.cert_in_cb = ctx->cert_in_cb;
215139beb93cSSam Leffler 
215239beb93cSSam Leffler 	sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
215339beb93cSSam Leffler 	if (sm->eap == NULL) {
215439beb93cSSam Leffler 		os_free(sm);
215539beb93cSSam Leffler 		return NULL;
215639beb93cSSam Leffler 	}
215739beb93cSSam Leffler 
21585b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
2159c1d255d3SCy Schubert 	sm->use_eap_proxy = false;
21605b9c547cSRui Paulo 	sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx);
21615b9c547cSRui Paulo 	if (sm->eap_proxy == NULL) {
21625b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy");
21635b9c547cSRui Paulo 	}
21645b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
21655b9c547cSRui Paulo 
216639beb93cSSam Leffler 	/* Initialize EAPOL state machines */
2167c1d255d3SCy Schubert 	sm->force_authorized_update = true;
2168c1d255d3SCy Schubert 	sm->initialize = true;
216939beb93cSSam Leffler 	eapol_sm_step(sm);
2170c1d255d3SCy Schubert 	sm->initialize = false;
217139beb93cSSam Leffler 	eapol_sm_step(sm);
217239beb93cSSam Leffler 
21734bc52338SCy Schubert 	if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0)
217439beb93cSSam Leffler 		sm->timer_tick_enabled = 1;
217539beb93cSSam Leffler 
217639beb93cSSam Leffler 	return sm;
217739beb93cSSam Leffler }
217839beb93cSSam Leffler 
217939beb93cSSam Leffler 
218039beb93cSSam Leffler /**
218139beb93cSSam Leffler  * eapol_sm_deinit - Deinitialize EAPOL state machine
218239beb93cSSam Leffler  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
218339beb93cSSam Leffler  *
218439beb93cSSam Leffler  * Deinitialize and free EAPOL state machine.
218539beb93cSSam Leffler  */
eapol_sm_deinit(struct eapol_sm * sm)218639beb93cSSam Leffler void eapol_sm_deinit(struct eapol_sm *sm)
218739beb93cSSam Leffler {
218839beb93cSSam Leffler 	if (sm == NULL)
218939beb93cSSam Leffler 		return;
219039beb93cSSam Leffler 	eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
219139beb93cSSam Leffler 	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
219239beb93cSSam Leffler 	eap_peer_sm_deinit(sm->eap);
21935b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
21945b9c547cSRui Paulo 	eap_proxy_deinit(sm->eap_proxy);
21955b9c547cSRui Paulo #endif /* CONFIG_EAP_PROXY */
219639beb93cSSam Leffler 	os_free(sm->last_rx_key);
219739beb93cSSam Leffler 	wpabuf_free(sm->eapReqData);
219839beb93cSSam Leffler 	os_free(sm->ctx);
219939beb93cSSam Leffler 	os_free(sm);
220039beb93cSSam Leffler }
2201f05cddf9SRui Paulo 
2202f05cddf9SRui Paulo 
eapol_sm_set_ext_pw_ctx(struct eapol_sm * sm,struct ext_password_data * ext)2203f05cddf9SRui Paulo void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
2204f05cddf9SRui Paulo 			     struct ext_password_data *ext)
2205f05cddf9SRui Paulo {
2206f05cddf9SRui Paulo 	if (sm && sm->eap)
2207f05cddf9SRui Paulo 		eap_sm_set_ext_pw_ctx(sm->eap, ext);
2208f05cddf9SRui Paulo }
2209f05cddf9SRui Paulo 
2210f05cddf9SRui Paulo 
eapol_sm_failed(struct eapol_sm * sm)2211f05cddf9SRui Paulo int eapol_sm_failed(struct eapol_sm *sm)
2212f05cddf9SRui Paulo {
2213f05cddf9SRui Paulo 	if (sm == NULL)
2214f05cddf9SRui Paulo 		return 0;
2215f05cddf9SRui Paulo 	return !sm->eapSuccess && sm->eapFail;
2216f05cddf9SRui Paulo }
22175b9c547cSRui Paulo 
22185b9c547cSRui Paulo 
22195b9c547cSRui Paulo #ifdef CONFIG_EAP_PROXY
eapol_sm_get_eap_proxy_imsi(void * ctx,int sim_num,char * imsi,size_t * len)222085732ac8SCy Schubert int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, size_t *len)
222185732ac8SCy Schubert {
222285732ac8SCy Schubert 	struct eapol_sm *sm = ctx;
222385732ac8SCy Schubert 
22245b9c547cSRui Paulo 	if (sm->eap_proxy == NULL)
22255b9c547cSRui Paulo 		return -1;
222685732ac8SCy Schubert 	return eap_proxy_get_imsi(sm->eap_proxy, sim_num, imsi, len);
22275b9c547cSRui Paulo }
222885732ac8SCy Schubert #endif /* CONFIG_EAP_PROXY */
22295b9c547cSRui Paulo 
22305b9c547cSRui Paulo 
eapol_sm_erp_flush(struct eapol_sm * sm)22315b9c547cSRui Paulo void eapol_sm_erp_flush(struct eapol_sm *sm)
22325b9c547cSRui Paulo {
22335b9c547cSRui Paulo 	if (sm)
22345b9c547cSRui Paulo 		eap_peer_erp_free_keys(sm->eap);
22355b9c547cSRui Paulo }
223685732ac8SCy Schubert 
223785732ac8SCy Schubert 
eapol_sm_build_erp_reauth_start(struct eapol_sm * sm)223885732ac8SCy Schubert struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm)
223985732ac8SCy Schubert {
224085732ac8SCy Schubert #ifdef CONFIG_ERP
224185732ac8SCy Schubert 	if (!sm)
224285732ac8SCy Schubert 		return NULL;
224385732ac8SCy Schubert 	return eap_peer_build_erp_reauth_start(sm->eap, 0);
224485732ac8SCy Schubert #else /* CONFIG_ERP */
224585732ac8SCy Schubert 	return NULL;
224685732ac8SCy Schubert #endif /* CONFIG_ERP */
224785732ac8SCy Schubert }
224885732ac8SCy Schubert 
224985732ac8SCy Schubert 
eapol_sm_process_erp_finish(struct eapol_sm * sm,const u8 * buf,size_t len)225085732ac8SCy Schubert void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
225185732ac8SCy Schubert 				 size_t len)
225285732ac8SCy Schubert {
225385732ac8SCy Schubert #ifdef CONFIG_ERP
225485732ac8SCy Schubert 	if (!sm)
225585732ac8SCy Schubert 		return;
225685732ac8SCy Schubert 	eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len);
225785732ac8SCy Schubert #endif /* CONFIG_ERP */
225885732ac8SCy Schubert }
225985732ac8SCy Schubert 
226085732ac8SCy Schubert 
eapol_sm_update_erp_next_seq_num(struct eapol_sm * sm,u16 next_seq_num)226185732ac8SCy Schubert int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num)
226285732ac8SCy Schubert {
226385732ac8SCy Schubert #ifdef CONFIG_ERP
226485732ac8SCy Schubert 	if (!sm)
226585732ac8SCy Schubert 		return -1;
226685732ac8SCy Schubert 	return eap_peer_update_erp_next_seq_num(sm->eap, next_seq_num);
226785732ac8SCy Schubert #else /* CONFIG_ERP */
226885732ac8SCy Schubert 	return -1;
226985732ac8SCy Schubert #endif /* CONFIG_ERP */
227085732ac8SCy Schubert }
227185732ac8SCy Schubert 
227285732ac8SCy Schubert 
eapol_sm_get_erp_info(struct eapol_sm * sm,struct eap_peer_config * config,const u8 ** username,size_t * username_len,const u8 ** realm,size_t * realm_len,u16 * erp_next_seq_num,const u8 ** rrk,size_t * rrk_len)227385732ac8SCy Schubert int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config,
227485732ac8SCy Schubert 			  const u8 **username, size_t *username_len,
227585732ac8SCy Schubert 			  const u8 **realm, size_t *realm_len,
227685732ac8SCy Schubert 			  u16 *erp_next_seq_num, const u8 **rrk,
227785732ac8SCy Schubert 			  size_t *rrk_len)
227885732ac8SCy Schubert {
227985732ac8SCy Schubert #ifdef CONFIG_ERP
228085732ac8SCy Schubert 	if (!sm)
228185732ac8SCy Schubert 		return -1;
228285732ac8SCy Schubert 	return eap_peer_get_erp_info(sm->eap, config, username, username_len,
228385732ac8SCy Schubert 				     realm, realm_len, erp_next_seq_num, rrk,
228485732ac8SCy Schubert 				     rrk_len);
228585732ac8SCy Schubert #else /* CONFIG_ERP */
228685732ac8SCy Schubert 	return -1;
228785732ac8SCy Schubert #endif /* CONFIG_ERP */
228885732ac8SCy Schubert }
2289