eapol_supp_sm.c revision 209158
1193326Sed/*
2193326Sed * EAPOL supplicant state machines
3193326Sed * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4193326Sed *
5193326Sed * This program is free software; you can redistribute it and/or modify
6193326Sed * it under the terms of the GNU General Public License version 2 as
7193326Sed * published by the Free Software Foundation.
8193326Sed *
9193326Sed * Alternatively, this software may be distributed under the terms of BSD
10205219Srdivacky * license.
11205219Srdivacky *
12193326Sed * See README and COPYING for more details.
13193326Sed */
14193326Sed
15193326Sed#include "includes.h"
16193326Sed
17193326Sed#include "common.h"
18249423Sdim#include "eapol_supp_sm.h"
19249423Sdim#include "eap_peer/eap.h"
20198092Srdivacky#include "eloop.h"
21234353Sdim#include "eapol_common.h"
22212904Sdim#include "md5.h"
23234353Sdim#include "rc4.h"
24234353Sdim#include "state_machine.h"
25198092Srdivacky#include "wpabuf.h"
26234353Sdim
27193326Sed#define STATE_MACHINE_DATA struct eapol_sm
28193326Sed#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
29193326Sed
30193326Sed
31198092Srdivacky/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
32198092Srdivacky
33198092Srdivacky/**
34193326Sed * struct eapol_sm - Internal data for EAPOL state machines
35198092Srdivacky */
36193326Sedstruct eapol_sm {
37193326Sed	/* Timers */
38198092Srdivacky	unsigned int authWhile;
39198092Srdivacky	unsigned int heldWhile;
40206084Srdivacky	unsigned int startWhen;
41218893Sdim	unsigned int idleWhile; /* for EAP state machine */
42205219Srdivacky	int timer_tick_enabled;
43234353Sdim
44239462Sdim	/* Global variables */
45234353Sdim	Boolean eapFail;
46198092Srdivacky	Boolean eapolEap;
47195099Sed	Boolean eapSuccess;
48195099Sed	Boolean initialize;
49195099Sed	Boolean keyDone;
50198092Srdivacky	Boolean keyRun;
51195341Sed	PortControl portControl;
52198092Srdivacky	Boolean portEnabled;
53195099Sed	PortStatus suppPortStatus;  /* dot1xSuppControlledPortStatus */
54195099Sed	Boolean portValid;
55195099Sed	Boolean suppAbort;
56198092Srdivacky	Boolean suppFail;
57198092Srdivacky	Boolean suppStart;
58195099Sed	Boolean suppSuccess;
59195099Sed	Boolean suppTimeout;
60198092Srdivacky
61195099Sed	/* Supplicant PAE state machine */
62195099Sed	enum {
63198092Srdivacky		SUPP_PAE_UNKNOWN = 0,
64198092Srdivacky		SUPP_PAE_DISCONNECTED = 1,
65195341Sed		SUPP_PAE_LOGOFF = 2,
66195341Sed		SUPP_PAE_CONNECTING = 3,
67195099Sed		SUPP_PAE_AUTHENTICATING = 4,
68198092Srdivacky		SUPP_PAE_AUTHENTICATED = 5,
69195099Sed		/* unused(6) */
70195099Sed		SUPP_PAE_HELD = 7,
71195099Sed		SUPP_PAE_RESTART = 8,
72195341Sed		SUPP_PAE_S_FORCE_AUTH = 9,
73195341Sed		SUPP_PAE_S_FORCE_UNAUTH = 10
74195341Sed	} SUPP_PAE_state; /* dot1xSuppPaeState */
75195341Sed	/* Variables */
76195341Sed	Boolean userLogoff;
77198092Srdivacky	Boolean logoffSent;
78195341Sed	unsigned int startCount;
79195341Sed	Boolean eapRestart;
80195341Sed	PortControl sPortMode;
81195341Sed	/* Constants */
82195341Sed	unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
83198092Srdivacky	unsigned int startPeriod; /* dot1xSuppStartPeriod */
84195341Sed	unsigned int maxStart; /* dot1xSuppMaxStart */
85195341Sed
86198092Srdivacky	/* Key Receive state machine */
87195099Sed	enum {
88195099Sed		KEY_RX_UNKNOWN = 0,
89195099Sed		KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
90198092Srdivacky	} KEY_RX_state;
91239462Sdim	/* Variables */
92210299Sed	Boolean rxKey;
93210299Sed
94210299Sed	/* Supplicant Backend state machine */
95210299Sed	enum {
96210299Sed		SUPP_BE_UNKNOWN = 0,
97210299Sed		SUPP_BE_INITIALIZE = 1,
98210299Sed		SUPP_BE_IDLE = 2,
99210299Sed		SUPP_BE_REQUEST = 3,
100210299Sed		SUPP_BE_RECEIVE = 4,
101210299Sed		SUPP_BE_RESPONSE = 5,
102234353Sdim		SUPP_BE_FAIL = 6,
103239462Sdim		SUPP_BE_TIMEOUT = 7,
104210299Sed		SUPP_BE_SUCCESS = 8
105210299Sed	} SUPP_BE_state; /* dot1xSuppBackendPaeState */
106210299Sed	/* Variables */
107210299Sed	Boolean eapNoResp;
108210299Sed	Boolean eapReq;
109210299Sed	Boolean eapResp;
110210299Sed	/* Constants */
111218893Sdim	unsigned int authPeriod; /* dot1xSuppAuthPeriod */
112218893Sdim
113210299Sed	/* Statistics */
114239462Sdim	unsigned int dot1xSuppEapolFramesRx;
115210299Sed	unsigned int dot1xSuppEapolFramesTx;
116239462Sdim	unsigned int dot1xSuppEapolStartFramesTx;
117210299Sed	unsigned int dot1xSuppEapolLogoffFramesTx;
118210299Sed	unsigned int dot1xSuppEapolRespFramesTx;
119239462Sdim	unsigned int dot1xSuppEapolReqIdFramesRx;
120210299Sed	unsigned int dot1xSuppEapolReqFramesRx;
121239462Sdim	unsigned int dot1xSuppInvalidEapolFramesRx;
122210299Sed	unsigned int dot1xSuppEapLengthErrorFramesRx;
123210299Sed	unsigned int dot1xSuppLastEapolFrameVersion;
124234353Sdim	unsigned char dot1xSuppLastEapolFrameSource[6];
125210299Sed
126210299Sed	/* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
127210299Sed	Boolean changed;
128210299Sed	struct eap_sm *eap;
129210299Sed	struct eap_peer_config *config;
130210299Sed	Boolean initial_req;
131210299Sed	u8 *last_rx_key;
132210299Sed	size_t last_rx_key_len;
133234353Sdim	struct wpabuf *eapReqData; /* for EAP */
134210299Sed	Boolean altAccept; /* for EAP */
135210299Sed	Boolean altReject; /* for EAP */
136210299Sed	Boolean replay_counter_valid;
137210299Sed	u8 last_replay_counter[16];
138210299Sed	struct eapol_config conf;
139210299Sed	struct eapol_ctx *ctx;
140210299Sed	enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
141239462Sdim		cb_status;
142193326Sed	Boolean cached_pmk;
143193326Sed
144193326Sed	Boolean unicast_key_received, broadcast_key_received;
145193326Sed};
146193326Sed
147193326Sed
148193326Sed#define IEEE8021X_REPLAY_COUNTER_LEN 8
149193326Sed#define IEEE8021X_KEY_SIGN_LEN 16
150193326Sed#define IEEE8021X_KEY_IV_LEN 16
151193326Sed
152193326Sed#define IEEE8021X_KEY_INDEX_FLAG 0x80
153193326Sed#define IEEE8021X_KEY_INDEX_MASK 0x03
154193326Sed
155193326Sed#ifdef _MSC_VER
156193326Sed#pragma pack(push, 1)
157193326Sed#endif /* _MSC_VER */
158193326Sed
159193326Sedstruct ieee802_1x_eapol_key {
160193326Sed	u8 type;
161193326Sed	/* Note: key_length is unaligned */
162193326Sed	u8 key_length[2];
163218893Sdim	/* does not repeat within the life of the keying material used to
164218893Sdim	 * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
165218893Sdim	u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
166234353Sdim	u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
167239462Sdim	u8 key_index; /* key flag in the most significant bit:
168193326Sed		       * 0 = broadcast (default key),
169193326Sed		       * 1 = unicast (key mapping key); key index is in the
170193326Sed		       * 7 least significant bits */
171193326Sed	/* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
172193326Sed	 * the key */
173193326Sed	u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
174198092Srdivacky
175193326Sed	/* followed by key: if packet body length = 44 + key length, then the
176193326Sed	 * key field (of key_length bytes) contains the key in encrypted form;
177193326Sed	 * if packet body length = 44, key field is absent and key_length
178193326Sed	 * represents the number of least significant octets from
179193326Sed	 * MS-MPPE-Send-Key attribute to be used as the keying material;
180193326Sed	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
181193326Sed} STRUCT_PACKED;
182218893Sdim
183218893Sdim#ifdef _MSC_VER
184218893Sdim#pragma pack(pop)
185218893Sdim#endif /* _MSC_VER */
186212904Sdim
187212904Sdim
188212904Sdimstatic void eapol_sm_txLogoff(struct eapol_sm *sm);
189212904Sdimstatic void eapol_sm_txStart(struct eapol_sm *sm);
190198092Srdivackystatic void eapol_sm_processKey(struct eapol_sm *sm);
191193326Sedstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm);
192193326Sedstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm);
193193326Sedstatic void eapol_sm_abortSupp(struct eapol_sm *sm);
194212904Sdimstatic void eapol_sm_abort_cached(struct eapol_sm *sm);
195218893Sdimstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
196234353Sdim
197218893Sdim
198193326Sed/* Port Timers state machine - implemented as a function that will be called
199193326Sed * once a second as a registered event loop timeout */
200193326Sedstatic void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
201234353Sdim{
202234353Sdim	struct eapol_sm *sm = timeout_ctx;
203234353Sdim
204198092Srdivacky	if (sm->authWhile > 0) {
205193326Sed		sm->authWhile--;
206193326Sed		if (sm->authWhile == 0)
207193326Sed			wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
208193326Sed	}
209212904Sdim	if (sm->heldWhile > 0) {
210203955Srdivacky		sm->heldWhile--;
211203955Srdivacky		if (sm->heldWhile == 0)
212234353Sdim			wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
213218893Sdim	}
214218893Sdim	if (sm->startWhen > 0) {
215218893Sdim		sm->startWhen--;
216218893Sdim		if (sm->startWhen == 0)
217218893Sdim			wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
218218893Sdim	}
219218893Sdim	if (sm->idleWhile > 0) {
220218893Sdim		sm->idleWhile--;
221218893Sdim		if (sm->idleWhile == 0)
222218893Sdim			wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
223218893Sdim	}
224218893Sdim
225218893Sdim	if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
226218893Sdim		eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
227218893Sdim				       sm);
228218893Sdim	} else {
229193326Sed		wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
230193326Sed		sm->timer_tick_enabled = 0;
231193326Sed	}
232193326Sed	eapol_sm_step(sm);
233193326Sed}
234198092Srdivacky
235193326Sed
236193326Sedstatic void eapol_enable_timer_tick(struct eapol_sm *sm)
237193326Sed{
238198092Srdivacky	if (sm->timer_tick_enabled)
239193326Sed		return;
240193326Sed	wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
241193326Sed	sm->timer_tick_enabled = 1;
242193326Sed	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
243193326Sed	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
244193326Sed}
245193326Sed
246193326Sed
247193326SedSM_STATE(SUPP_PAE, LOGOFF)
248193326Sed{
249193326Sed	SM_ENTRY(SUPP_PAE, LOGOFF);
250193326Sed	eapol_sm_txLogoff(sm);
251212904Sdim	sm->logoffSent = TRUE;
252212904Sdim	sm->suppPortStatus = Unauthorized;
253212904Sdim}
254212904Sdim
255193326Sed
256193326SedSM_STATE(SUPP_PAE, DISCONNECTED)
257251662Sdim{
258251662Sdim	SM_ENTRY(SUPP_PAE, DISCONNECTED);
259251662Sdim	sm->sPortMode = Auto;
260251662Sdim	sm->startCount = 0;
261251662Sdim	sm->logoffSent = FALSE;
262251662Sdim	sm->suppPortStatus = Unauthorized;
263251662Sdim	sm->suppAbort = TRUE;
264251662Sdim
265251662Sdim	sm->unicast_key_received = FALSE;
266251662Sdim	sm->broadcast_key_received = FALSE;
267193326Sed}
268193326Sed
269193326Sed
270193326SedSM_STATE(SUPP_PAE, CONNECTING)
271193326Sed{
272203955Srdivacky	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
273193326Sed	SM_ENTRY(SUPP_PAE, CONNECTING);
274249423Sdim	if (send_start) {
275249423Sdim		sm->startWhen = sm->startPeriod;
276249423Sdim		sm->startCount++;
277249423Sdim	} else {
278249423Sdim		/*
279249423Sdim		 * Do not send EAPOL-Start immediately since in most cases,
280249423Sdim		 * Authenticator is going to start authentication immediately
281249423Sdim		 * after association and an extra EAPOL-Start is just going to
282249423Sdim		 * delay authentication. Use a short timeout to send the first
283249423Sdim		 * EAPOL-Start if Authenticator does not start authentication.
284249423Sdim		 */
285203955Srdivacky#ifdef CONFIG_WPS
286203955Srdivacky		/* Reduce latency on starting WPS negotiation. */
287193326Sed		sm->startWhen = 1;
288249423Sdim#else /* CONFIG_WPS */
289203955Srdivacky		sm->startWhen = 3;
290193326Sed#endif /* CONFIG_WPS */
291249423Sdim	}
292249423Sdim	eapol_enable_timer_tick(sm);
293193326Sed	sm->eapolEap = FALSE;
294203955Srdivacky	if (send_start)
295203955Srdivacky		eapol_sm_txStart(sm);
296193326Sed}
297203955Srdivacky
298203955Srdivacky
299198092SrdivackySM_STATE(SUPP_PAE, AUTHENTICATING)
300203955Srdivacky{
301203955Srdivacky	SM_ENTRY(SUPP_PAE, AUTHENTICATING);
302203955Srdivacky	sm->startCount = 0;
303203955Srdivacky	sm->suppSuccess = FALSE;
304203955Srdivacky	sm->suppFail = FALSE;
305198092Srdivacky	sm->suppTimeout = FALSE;
306203955Srdivacky	sm->keyRun = FALSE;
307203955Srdivacky	sm->keyDone = FALSE;
308203955Srdivacky	sm->suppStart = TRUE;
309198092Srdivacky}
310203955Srdivacky
311203955Srdivacky
312203955SrdivackySM_STATE(SUPP_PAE, HELD)
313198092Srdivacky{
314221345Sdim	SM_ENTRY(SUPP_PAE, HELD);
315221345Sdim	sm->heldWhile = sm->heldPeriod;
316221345Sdim	eapol_enable_timer_tick(sm);
317221345Sdim	sm->suppPortStatus = Unauthorized;
318221345Sdim	sm->cb_status = EAPOL_CB_FAILURE;
319221345Sdim}
320234353Sdim
321234353Sdim
322221345SdimSM_STATE(SUPP_PAE, AUTHENTICATED)
323221345Sdim{
324221345Sdim	SM_ENTRY(SUPP_PAE, AUTHENTICATED);
325221345Sdim	sm->suppPortStatus = Authorized;
326221345Sdim	sm->cb_status = EAPOL_CB_SUCCESS;
327221345Sdim}
328221345Sdim
329221345Sdim
330221345SdimSM_STATE(SUPP_PAE, RESTART)
331221345Sdim{
332221345Sdim	SM_ENTRY(SUPP_PAE, RESTART);
333221345Sdim	sm->eapRestart = TRUE;
334221345Sdim}
335221345Sdim
336221345Sdim
337221345SdimSM_STATE(SUPP_PAE, S_FORCE_AUTH)
338221345Sdim{
339221345Sdim	SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
340221345Sdim	sm->suppPortStatus = Authorized;
341221345Sdim	sm->sPortMode = ForceAuthorized;
342221345Sdim}
343221345Sdim
344221345Sdim
345221345SdimSM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
346223017Sdim{
347223017Sdim	SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
348234353Sdim	sm->suppPortStatus = Unauthorized;
349234353Sdim	sm->sPortMode = ForceUnauthorized;
350234353Sdim	eapol_sm_txLogoff(sm);
351234353Sdim}
352239462Sdim
353239462Sdim
354239462SdimSM_STEP(SUPP_PAE)
355249423Sdim{
356249423Sdim	if ((sm->userLogoff && !sm->logoffSent) &&
357249423Sdim	    !(sm->initialize || !sm->portEnabled))
358249423Sdim		SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
359249423Sdim	else if (((sm->portControl == Auto) &&
360249423Sdim		  (sm->sPortMode != sm->portControl)) ||
361249423Sdim		 sm->initialize || !sm->portEnabled)
362249423Sdim		SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
363249423Sdim	else if ((sm->portControl == ForceAuthorized) &&
364249423Sdim		 (sm->sPortMode != sm->portControl) &&
365249423Sdim		 !(sm->initialize || !sm->portEnabled))
366249423Sdim		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
367249423Sdim	else if ((sm->portControl == ForceUnauthorized) &&
368249423Sdim		 (sm->sPortMode != sm->portControl) &&
369249423Sdim		 !(sm->initialize || !sm->portEnabled))
370249423Sdim		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
371249423Sdim	else switch (sm->SUPP_PAE_state) {
372249423Sdim	case SUPP_PAE_UNKNOWN:
373249423Sdim		break;
374249423Sdim	case SUPP_PAE_LOGOFF:
375249423Sdim		if (!sm->userLogoff)
376249423Sdim			SM_ENTER(SUPP_PAE, DISCONNECTED);
377249423Sdim		break;
378249423Sdim	case SUPP_PAE_DISCONNECTED:
379203955Srdivacky		SM_ENTER(SUPP_PAE, CONNECTING);
380249423Sdim		break;
381249423Sdim	case SUPP_PAE_CONNECTING:
382249423Sdim		if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
383198092Srdivacky			SM_ENTER(SUPP_PAE, CONNECTING);
384249423Sdim		else if (sm->startWhen == 0 &&
385249423Sdim			 sm->startCount >= sm->maxStart &&
386249423Sdim			 sm->portValid)
387249423Sdim			SM_ENTER(SUPP_PAE, AUTHENTICATED);
388249423Sdim		else if (sm->eapSuccess || sm->eapFail)
389249423Sdim			SM_ENTER(SUPP_PAE, AUTHENTICATING);
390249423Sdim		else if (sm->eapolEap)
391249423Sdim			SM_ENTER(SUPP_PAE, RESTART);
392249423Sdim		else if (sm->startWhen == 0 &&
393249423Sdim			 sm->startCount >= sm->maxStart &&
394249423Sdim			 !sm->portValid)
395249423Sdim			SM_ENTER(SUPP_PAE, HELD);
396226633Sdim		break;
397234353Sdim	case SUPP_PAE_AUTHENTICATING:
398234353Sdim		if (sm->eapSuccess && !sm->portValid &&
399226633Sdim		    sm->conf.accept_802_1x_keys &&
400221345Sdim		    sm->conf.required_keys == 0) {
401234353Sdim			wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
402234353Sdim				   "plaintext connection; no EAPOL-Key frames "
403234353Sdim				   "required");
404234353Sdim			sm->portValid = TRUE;
405234353Sdim			if (sm->ctx->eapol_done_cb)
406234353Sdim				sm->ctx->eapol_done_cb(sm->ctx->ctx);
407234353Sdim		}
408234353Sdim		if (sm->eapSuccess && sm->portValid)
409221345Sdim			SM_ENTER(SUPP_PAE, AUTHENTICATED);
410234353Sdim		else if (sm->eapFail || (sm->keyDone && !sm->portValid))
411234353Sdim			SM_ENTER(SUPP_PAE, HELD);
412221345Sdim		else if (sm->suppTimeout)
413221345Sdim			SM_ENTER(SUPP_PAE, CONNECTING);
414203955Srdivacky		break;
415203955Srdivacky	case SUPP_PAE_HELD:
416203955Srdivacky		if (sm->heldWhile == 0)
417210299Sed			SM_ENTER(SUPP_PAE, CONNECTING);
418249423Sdim		else if (sm->eapolEap)
419223017Sdim			SM_ENTER(SUPP_PAE, RESTART);
420223017Sdim		break;
421223017Sdim	case SUPP_PAE_AUTHENTICATED:
422249423Sdim		if (sm->eapolEap && sm->portValid)
423249423Sdim			SM_ENTER(SUPP_PAE, RESTART);
424249423Sdim		else if (!sm->portValid)
425210299Sed			SM_ENTER(SUPP_PAE, DISCONNECTED);
426249423Sdim		break;
427249423Sdim	case SUPP_PAE_RESTART:
428249423Sdim		if (!sm->eapRestart)
429223017Sdim			SM_ENTER(SUPP_PAE, AUTHENTICATING);
430249423Sdim		break;
431249423Sdim	case SUPP_PAE_S_FORCE_AUTH:
432249423Sdim		break;
433234353Sdim	case SUPP_PAE_S_FORCE_UNAUTH:
434249423Sdim		break;
435249423Sdim	}
436249423Sdim}
437223017Sdim
438249423Sdim
439249423SdimSM_STATE(KEY_RX, NO_KEY_RECEIVE)
440249423Sdim{
441234353Sdim	SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
442226633Sdim}
443226633Sdim
444226633Sdim
445226633SdimSM_STATE(KEY_RX, KEY_RECEIVE)
446226633Sdim{
447226633Sdim	SM_ENTRY(KEY_RX, KEY_RECEIVE);
448226633Sdim	eapol_sm_processKey(sm);
449226633Sdim	sm->rxKey = FALSE;
450234353Sdim}
451234353Sdim
452234353Sdim
453218893SdimSM_STEP(KEY_RX)
454218893Sdim{
455234353Sdim	if (sm->initialize || !sm->portEnabled)
456218893Sdim		SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
457218893Sdim	switch (sm->KEY_RX_state) {
458218893Sdim	case KEY_RX_UNKNOWN:
459203955Srdivacky		break;
460203955Srdivacky	case KEY_RX_NO_KEY_RECEIVE:
461218893Sdim		if (sm->rxKey)
462193326Sed			SM_ENTER(KEY_RX, KEY_RECEIVE);
463203955Srdivacky		break;
464218893Sdim	case KEY_RX_KEY_RECEIVE:
465198092Srdivacky		if (sm->rxKey)
466203955Srdivacky			SM_ENTER(KEY_RX, KEY_RECEIVE);
467203955Srdivacky		break;
468203955Srdivacky	}
469203955Srdivacky}
470249423Sdim
471193326Sed
472203955SrdivackySM_STATE(SUPP_BE, REQUEST)
473203955Srdivacky{
474203955Srdivacky	SM_ENTRY(SUPP_BE, REQUEST);
475203955Srdivacky	sm->authWhile = 0;
476203955Srdivacky	sm->eapReq = TRUE;
477249423Sdim	eapol_sm_getSuppRsp(sm);
478203955Srdivacky}
479203955Srdivacky
480203955Srdivacky
481203955SrdivackySM_STATE(SUPP_BE, RESPONSE)
482205219Srdivacky{
483205219Srdivacky	SM_ENTRY(SUPP_BE, RESPONSE);
484205219Srdivacky	eapol_sm_txSuppRsp(sm);
485205219Srdivacky	sm->eapResp = FALSE;
486205219Srdivacky}
487234353Sdim
488218893Sdim
489239462SdimSM_STATE(SUPP_BE, SUCCESS)
490239462Sdim{
491239462Sdim	SM_ENTRY(SUPP_BE, SUCCESS);
492218893Sdim	sm->keyRun = TRUE;
493218893Sdim	sm->suppSuccess = TRUE;
494234353Sdim
495218893Sdim	if (eap_key_available(sm->eap)) {
496239462Sdim		/* New key received - clear IEEE 802.1X EAPOL-Key replay
497239462Sdim		 * counter */
498239462Sdim		sm->replay_counter_valid = FALSE;
499218893Sdim	}
500239462Sdim}
501239462Sdim
502239462Sdim
503239462SdimSM_STATE(SUPP_BE, FAIL)
504203955Srdivacky{
505203955Srdivacky	SM_ENTRY(SUPP_BE, FAIL);
506234353Sdim	sm->suppFail = TRUE;
507234353Sdim}
508234353Sdim
509234353Sdim
510243830SdimSM_STATE(SUPP_BE, TIMEOUT)
511234353Sdim{
512243830Sdim	SM_ENTRY(SUPP_BE, TIMEOUT);
513243830Sdim	sm->suppTimeout = TRUE;
514234353Sdim}
515234353Sdim
516234353Sdim
517234353SdimSM_STATE(SUPP_BE, IDLE)
518234353Sdim{
519234353Sdim	SM_ENTRY(SUPP_BE, IDLE);
520234353Sdim	sm->suppStart = FALSE;
521234353Sdim	sm->initial_req = TRUE;
522234353Sdim}
523234353Sdim
524234353Sdim
525234353SdimSM_STATE(SUPP_BE, INITIALIZE)
526234353Sdim{
527234353Sdim	SM_ENTRY(SUPP_BE, INITIALIZE);
528234353Sdim	eapol_sm_abortSupp(sm);
529234353Sdim	sm->suppAbort = FALSE;
530234353Sdim}
531234353Sdim
532234353Sdim
533234353SdimSM_STATE(SUPP_BE, RECEIVE)
534234353Sdim{
535234353Sdim	SM_ENTRY(SUPP_BE, RECEIVE);
536234353Sdim	sm->authWhile = sm->authPeriod;
537234353Sdim	eapol_enable_timer_tick(sm);
538234353Sdim	sm->eapolEap = FALSE;
539234353Sdim	sm->eapNoResp = FALSE;
540234353Sdim	sm->initial_req = FALSE;
541234353Sdim}
542234353Sdim
543234353Sdim
544234353SdimSM_STEP(SUPP_BE)
545234353Sdim{
546243830Sdim	if (sm->initialize || sm->suppAbort)
547243830Sdim		SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
548243830Sdim	else switch (sm->SUPP_BE_state) {
549243830Sdim	case SUPP_BE_UNKNOWN:
550234353Sdim		break;
551234353Sdim	case SUPP_BE_REQUEST:
552203955Srdivacky		/*
553203955Srdivacky		 * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
554203955Srdivacky		 * and SUCCESS based on eapFail and eapSuccess, respectively.
555203955Srdivacky		 * However, IEEE Std 802.1X-2004 is also specifying that
556203955Srdivacky		 * eapNoResp should be set in conjuction with eapSuccess and
557203955Srdivacky		 * eapFail which would mean that more than one of the
558203955Srdivacky		 * transitions here would be activated at the same time.
559203955Srdivacky		 * Skipping RESPONSE and/or RECEIVE states in these cases can
560203955Srdivacky		 * cause problems and the direct transitions to do not seem
561234353Sdim		 * correct. Because of this, the conditions for these
562234353Sdim		 * transitions are verified only after eapNoResp. They are
563234353Sdim		 * unlikely to be used since eapNoResp should always be set if
564234353Sdim		 * either of eapSuccess or eapFail is set.
565234353Sdim		 */
566234353Sdim		if (sm->eapResp && sm->eapNoResp) {
567234353Sdim			wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
568198092Srdivacky				   "eapResp and eapNoResp set?!");
569193326Sed		}
570193326Sed		if (sm->eapResp)
571198092Srdivacky			SM_ENTER(SUPP_BE, RESPONSE);
572193326Sed		else if (sm->eapNoResp)
573193326Sed			SM_ENTER(SUPP_BE, RECEIVE);
574193326Sed		else if (sm->eapFail)
575193326Sed			SM_ENTER(SUPP_BE, FAIL);
576234353Sdim		else if (sm->eapSuccess)
577198092Srdivacky			SM_ENTER(SUPP_BE, SUCCESS);
578198092Srdivacky		break;
579193326Sed	case SUPP_BE_RESPONSE:
580206084Srdivacky		SM_ENTER(SUPP_BE, RECEIVE);
581218893Sdim		break;
582234353Sdim	case SUPP_BE_SUCCESS:
583234353Sdim		SM_ENTER(SUPP_BE, IDLE);
584249423Sdim		break;
585249423Sdim	case SUPP_BE_FAIL:
586249423Sdim		SM_ENTER(SUPP_BE, IDLE);
587249423Sdim		break;
588218893Sdim	case SUPP_BE_TIMEOUT:
589218893Sdim		SM_ENTER(SUPP_BE, IDLE);
590234353Sdim		break;
591218893Sdim	case SUPP_BE_IDLE:
592218893Sdim		if (sm->eapFail && sm->suppStart)
593218893Sdim			SM_ENTER(SUPP_BE, FAIL);
594218893Sdim		else if (sm->eapolEap && sm->suppStart)
595218893Sdim			SM_ENTER(SUPP_BE, REQUEST);
596218893Sdim		else if (sm->eapSuccess && sm->suppStart)
597234353Sdim			SM_ENTER(SUPP_BE, SUCCESS);
598234353Sdim		break;
599234353Sdim	case SUPP_BE_INITIALIZE:
600193326Sed		SM_ENTER(SUPP_BE, IDLE);
601193326Sed		break;
602221345Sdim	case SUPP_BE_RECEIVE:
603221345Sdim		if (sm->eapolEap)
604193326Sed			SM_ENTER(SUPP_BE, REQUEST);
605193326Sed		else if (sm->eapFail)
606193326Sed			SM_ENTER(SUPP_BE, FAIL);
607198092Srdivacky		else if (sm->authWhile == 0)
608193326Sed			SM_ENTER(SUPP_BE, TIMEOUT);
609193326Sed		else if (sm->eapSuccess)
610193326Sed			SM_ENTER(SUPP_BE, SUCCESS);
611198092Srdivacky		break;
612193326Sed	}
613193326Sed}
614198092Srdivacky
615198092Srdivacky
616198092Srdivackystatic void eapol_sm_txLogoff(struct eapol_sm *sm)
617198092Srdivacky{
618198092Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
619198092Srdivacky	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
620198092Srdivacky			    IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
621198092Srdivacky	sm->dot1xSuppEapolLogoffFramesTx++;
622198092Srdivacky	sm->dot1xSuppEapolFramesTx++;
623198092Srdivacky}
624198092Srdivacky
625198092Srdivacky
626198092Srdivackystatic void eapol_sm_txStart(struct eapol_sm *sm)
627199482Srdivacky{
628199482Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: txStart");
629199482Srdivacky	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
630234353Sdim			    IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
631234353Sdim	sm->dot1xSuppEapolStartFramesTx++;
632234353Sdim	sm->dot1xSuppEapolFramesTx++;
633210299Sed}
634234353Sdim
635234353Sdim
636210299Sed#define IEEE8021X_ENCR_KEY_LEN 32
637198092Srdivacky#define IEEE8021X_SIGN_KEY_LEN 32
638234353Sdim
639234353Sdimstruct eap_key_data {
640234353Sdim	u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
641234353Sdim	u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
642234353Sdim};
643234353Sdim
644234353Sdim
645203955Srdivackystatic void eapol_sm_processKey(struct eapol_sm *sm)
646203955Srdivacky{
647203955Srdivacky	struct ieee802_1x_hdr *hdr;
648203955Srdivacky	struct ieee802_1x_eapol_key *key;
649203955Srdivacky	struct eap_key_data keydata;
650203955Srdivacky	u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
651203955Srdivacky	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
652218893Sdim	int key_len, res, sign_key_len, encr_key_len;
653221345Sdim	u16 rx_key_length;
654221345Sdim
655193326Sed	wpa_printf(MSG_DEBUG, "EAPOL: processKey");
656234353Sdim	if (sm->last_rx_key == NULL)
657243830Sdim		return;
658243830Sdim
659234353Sdim	if (!sm->conf.accept_802_1x_keys) {
660198092Srdivacky		wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
661198092Srdivacky			   " even though this was not accepted - "
662203955Srdivacky			   "ignoring this packet");
663198092Srdivacky		return;
664198092Srdivacky	}
665193326Sed
666203955Srdivacky	hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
667193326Sed	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
668193326Sed	if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
669193326Sed		wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
670203955Srdivacky		return;
671193326Sed	}
672218893Sdim	rx_key_length = WPA_GET_BE16(key->key_length);
673218893Sdim	wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
674203955Srdivacky		   "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
675203955Srdivacky		   hdr->version, hdr->type, be_to_host16(hdr->length),
676203955Srdivacky		   key->type, rx_key_length, key->key_index);
677203955Srdivacky
678198092Srdivacky	eapol_sm_notify_lower_layer_success(sm, 1);
679198092Srdivacky	sign_key_len = IEEE8021X_SIGN_KEY_LEN;
680198092Srdivacky	encr_key_len = IEEE8021X_ENCR_KEY_LEN;
681198092Srdivacky	res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
682198092Srdivacky	if (res < 0) {
683198092Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
684198092Srdivacky			   "decrypting EAPOL-Key keys");
685198092Srdivacky		return;
686198092Srdivacky	}
687198092Srdivacky	if (res == 16) {
688198092Srdivacky		/* LEAP derives only 16 bytes of keying material. */
689198092Srdivacky		res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
690193326Sed		if (res) {
691198092Srdivacky			wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
692198092Srdivacky				   "master key for decrypting EAPOL-Key keys");
693203955Srdivacky			return;
694198092Srdivacky		}
695218893Sdim		sign_key_len = 16;
696218893Sdim		encr_key_len = 16;
697203955Srdivacky		os_memcpy(keydata.sign_key, keydata.encr_key, 16);
698203955Srdivacky	} else if (res) {
699203955Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
700203955Srdivacky			   "data for decrypting EAPOL-Key keys (res=%d)", res);
701198092Srdivacky		return;
702198092Srdivacky	}
703198092Srdivacky
704198092Srdivacky	/* The key replay_counter must increase when same master key */
705198092Srdivacky	if (sm->replay_counter_valid &&
706198092Srdivacky	    os_memcmp(sm->last_replay_counter, key->replay_counter,
707198092Srdivacky		      IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
708198092Srdivacky		wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
709198092Srdivacky			   "not increase - ignoring key");
710198092Srdivacky		wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
711198092Srdivacky			    sm->last_replay_counter,
712198092Srdivacky			    IEEE8021X_REPLAY_COUNTER_LEN);
713198092Srdivacky		wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
714249423Sdim			    key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
715249423Sdim		return;
716202379Srdivacky	}
717202379Srdivacky
718198092Srdivacky	/* Verify key signature (HMAC-MD5) */
719198092Srdivacky	os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
720198092Srdivacky	os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
721198092Srdivacky	hmac_md5(keydata.sign_key, sign_key_len,
722198092Srdivacky		 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
723198092Srdivacky		 key->key_signature);
724198092Srdivacky	if (os_memcmp(orig_key_sign, key->key_signature,
725198092Srdivacky		      IEEE8021X_KEY_SIGN_LEN) != 0) {
726198092Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
727198092Srdivacky			   "EAPOL-Key packet");
728198092Srdivacky		os_memcpy(key->key_signature, orig_key_sign,
729198092Srdivacky			  IEEE8021X_KEY_SIGN_LEN);
730198092Srdivacky		return;
731198092Srdivacky	}
732198092Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
733198092Srdivacky
734198092Srdivacky	key_len = be_to_host16(hdr->length) - sizeof(*key);
735198092Srdivacky	if (key_len > 32 || rx_key_length > 32) {
736198092Srdivacky		wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
737198092Srdivacky			   key_len ? key_len : rx_key_length);
738198092Srdivacky		return;
739198092Srdivacky	}
740198092Srdivacky	if (key_len == rx_key_length) {
741198092Srdivacky		os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
742198092Srdivacky		os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
743205219Srdivacky			  encr_key_len);
744205219Srdivacky		os_memcpy(datakey, key + 1, key_len);
745205219Srdivacky		rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
746205219Srdivacky			 datakey, key_len);
747205219Srdivacky		wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
748205219Srdivacky				datakey, key_len);
749205219Srdivacky	} else if (key_len == 0) {
750207619Srdivacky		/*
751207619Srdivacky		 * IEEE 802.1X-2004 specifies that least significant Key Length
752207619Srdivacky		 * octets from MS-MPPE-Send-Key are used as the key if the key
753207619Srdivacky		 * data is not present. This seems to be meaning the beginning
754207619Srdivacky		 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
755249423Sdim		 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
756249423Sdim		 * Anyway, taking the beginning of the keying material from EAP
757249423Sdim		 * seems to interoperate with Authenticators.
758249423Sdim		 */
759249423Sdim		key_len = rx_key_length;
760249423Sdim		os_memcpy(datakey, keydata.encr_key, key_len);
761249423Sdim		wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
762249423Sdim				"material data encryption key",
763249423Sdim				datakey, key_len);
764249423Sdim	} else {
765249423Sdim		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
766249423Sdim			   "(key_length=%d)", key_len, rx_key_length);
767249423Sdim		return;
768249423Sdim	}
769249423Sdim
770249423Sdim	sm->replay_counter_valid = TRUE;
771249423Sdim	os_memcpy(sm->last_replay_counter, key->replay_counter,
772249423Sdim		  IEEE8021X_REPLAY_COUNTER_LEN);
773249423Sdim
774249423Sdim	wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
775249423Sdim		   "len %d",
776249423Sdim		   key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
777249423Sdim		   "unicast" : "broadcast",
778223017Sdim		   key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
779223017Sdim
780210299Sed	if (sm->ctx->set_wep_key &&
781210299Sed	    sm->ctx->set_wep_key(sm->ctx->ctx,
782223017Sdim				 key->key_index & IEEE8021X_KEY_INDEX_FLAG,
783234353Sdim				 key->key_index & IEEE8021X_KEY_INDEX_MASK,
784249423Sdim				 datakey, key_len) < 0) {
785223017Sdim		wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
786223017Sdim			   " driver.");
787193326Sed	} else {
788193326Sed		if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
789193326Sed			sm->unicast_key_received = TRUE;
790198092Srdivacky		else
791203955Srdivacky			sm->broadcast_key_received = TRUE;
792198092Srdivacky
793193326Sed		if ((sm->unicast_key_received ||
794223017Sdim		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
795223017Sdim		    (sm->broadcast_key_received ||
796223017Sdim		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
797223017Sdim		{
798223017Sdim			wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
799223017Sdim				   "frames received");
800193326Sed			sm->portValid = TRUE;
801193326Sed			if (sm->ctx->eapol_done_cb)
802193326Sed				sm->ctx->eapol_done_cb(sm->ctx->ctx);
803193326Sed		}
804249423Sdim	}
805193326Sed}
806193326Sed
807249423Sdim
808249423Sdimstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm)
809249423Sdim{
810249423Sdim	wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
811210299Sed	/* EAP layer processing; no special code is needed, since Supplicant
812223017Sdim	 * Backend state machine is waiting for eapNoResp or eapResp to be set
813249423Sdim	 * and these are only set in the EAP state machine when the processing
814249423Sdim	 * has finished. */
815249423Sdim}
816249423Sdim
817249423Sdim
818249423Sdimstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm)
819249423Sdim{
820249423Sdim	struct wpabuf *resp;
821249423Sdim
822249423Sdim	wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
823249423Sdim	resp = eap_get_eapRespData(sm->eap);
824249423Sdim	if (resp == NULL) {
825249423Sdim		wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
826249423Sdim			   "not available");
827249423Sdim		return;
828249423Sdim	}
829249423Sdim
830249423Sdim	/* Send EAP-Packet from the EAP layer to the Authenticator */
831249423Sdim	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
832249423Sdim			    IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
833223017Sdim			    wpabuf_len(resp));
834223017Sdim
835223017Sdim	/* eapRespData is not used anymore, so free it here */
836223017Sdim	wpabuf_free(resp);
837249423Sdim
838249423Sdim	if (sm->initial_req)
839223017Sdim		sm->dot1xSuppEapolReqIdFramesRx++;
840223017Sdim	else
841223017Sdim		sm->dot1xSuppEapolReqFramesRx++;
842223017Sdim	sm->dot1xSuppEapolRespFramesTx++;
843223017Sdim	sm->dot1xSuppEapolFramesTx++;
844249423Sdim}
845223017Sdim
846223017Sdim
847249423Sdimstatic void eapol_sm_abortSupp(struct eapol_sm *sm)
848249423Sdim{
849249423Sdim	/* release system resources that may have been allocated for the
850249423Sdim	 * authentication session */
851223017Sdim	os_free(sm->last_rx_key);
852223017Sdim	sm->last_rx_key = NULL;
853226633Sdim	wpabuf_free(sm->eapReqData);
854226633Sdim	sm->eapReqData = NULL;
855226633Sdim	eap_sm_abort(sm->eap);
856226633Sdim}
857226633Sdim
858226633Sdim
859226633Sdimstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
860226633Sdim{
861226633Sdim	eapol_sm_step(timeout_ctx);
862226633Sdim}
863226633Sdim
864226633Sdim
865226633Sdim/**
866226633Sdim * eapol_sm_step - EAPOL state machine step function
867226633Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
868226633Sdim *
869249423Sdim * This function is called to notify the state machine about changed external
870226633Sdim * variables. It will step through the EAPOL state machines in loop to process
871226633Sdim * all triggered state changes.
872226633Sdim */
873249423Sdimvoid eapol_sm_step(struct eapol_sm *sm)
874249423Sdim{
875226633Sdim	int i;
876226633Sdim
877249423Sdim	/* In theory, it should be ok to run this in loop until !changed.
878249423Sdim	 * However, it is better to use a limit on number of iterations to
879249423Sdim	 * allow events (e.g., SIGTERM) to stop the program cleanly if the
880249423Sdim	 * state machine were to generate a busy loop. */
881249423Sdim	for (i = 0; i < 100; i++) {
882249423Sdim		sm->changed = FALSE;
883193326Sed		SM_STEP_RUN(SUPP_PAE);
884193326Sed		SM_STEP_RUN(KEY_RX);
885193326Sed		SM_STEP_RUN(SUPP_BE);
886193326Sed		if (eap_peer_sm_step(sm->eap))
887249423Sdim			sm->changed = TRUE;
888193326Sed		if (!sm->changed)
889193326Sed			break;
890249423Sdim	}
891249423Sdim
892249423Sdim	if (sm->changed) {
893249423Sdim		/* restart EAPOL state machine step from timeout call in order
894210299Sed		 * to allow other events to be processed. */
895223017Sdim		eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
896249423Sdim		eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
897249423Sdim	}
898249423Sdim
899249423Sdim	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
900249423Sdim		int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
901249423Sdim		sm->cb_status = EAPOL_CB_IN_PROGRESS;
902249423Sdim		sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
903249423Sdim	}
904249423Sdim}
905249423Sdim
906249423Sdim
907249423Sdim#ifdef CONFIG_CTRL_IFACE
908249423Sdimstatic const char *eapol_supp_pae_state(int state)
909249423Sdim{
910249423Sdim	switch (state) {
911249423Sdim	case SUPP_PAE_LOGOFF:
912249423Sdim		return "LOGOFF";
913249423Sdim	case SUPP_PAE_DISCONNECTED:
914249423Sdim		return "DISCONNECTED";
915249423Sdim	case SUPP_PAE_CONNECTING:
916249423Sdim		return "CONNECTING";
917223017Sdim	case SUPP_PAE_AUTHENTICATING:
918223017Sdim		return "AUTHENTICATING";
919223017Sdim	case SUPP_PAE_HELD:
920249423Sdim		return "HELD";
921223017Sdim	case SUPP_PAE_AUTHENTICATED:
922223017Sdim		return "AUTHENTICATED";
923249423Sdim	case SUPP_PAE_RESTART:
924249423Sdim		return "RESTART";
925249423Sdim	default:
926249423Sdim		return "UNKNOWN";
927223017Sdim	}
928223017Sdim}
929226633Sdim
930226633Sdim
931226633Sdimstatic const char *eapol_supp_be_state(int state)
932226633Sdim{
933226633Sdim	switch (state) {
934226633Sdim	case SUPP_BE_REQUEST:
935226633Sdim		return "REQUEST";
936226633Sdim	case SUPP_BE_RESPONSE:
937226633Sdim		return "RESPONSE";
938226633Sdim	case SUPP_BE_SUCCESS:
939226633Sdim		return "SUCCESS";
940226633Sdim	case SUPP_BE_FAIL:
941226633Sdim		return "FAIL";
942226633Sdim	case SUPP_BE_TIMEOUT:
943226633Sdim		return "TIMEOUT";
944226633Sdim	case SUPP_BE_IDLE:
945226633Sdim		return "IDLE";
946249423Sdim	case SUPP_BE_INITIALIZE:
947226633Sdim		return "INITIALIZE";
948226633Sdim	case SUPP_BE_RECEIVE:
949226633Sdim		return "RECEIVE";
950249423Sdim	default:
951249423Sdim		return "UNKNOWN";
952226633Sdim	}
953226633Sdim}
954249423Sdim
955249423Sdim
956249423Sdimstatic const char * eapol_port_status(PortStatus status)
957249423Sdim{
958249423Sdim	if (status == Authorized)
959249423Sdim		return "Authorized";
960193326Sed	else
961193326Sed		return "Unauthorized";
962193326Sed}
963203955Srdivacky#endif /* CONFIG_CTRL_IFACE */
964249423Sdim
965203955Srdivacky
966193326Sed#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
967249423Sdimstatic const char * eapol_port_control(PortControl ctrl)
968249423Sdim{
969249423Sdim	switch (ctrl) {
970249423Sdim	case Auto:
971249423Sdim		return "Auto";
972218893Sdim	case ForceUnauthorized:
973249423Sdim		return "ForceUnauthorized";
974249423Sdim	case ForceAuthorized:
975249423Sdim		return "ForceAuthorized";
976249423Sdim	default:
977249423Sdim		return "Unknown";
978249423Sdim	}
979234353Sdim}
980234353Sdim#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
981249423Sdim
982234353Sdim
983234353Sdim/**
984234353Sdim * eapol_sm_configure - Set EAPOL variables
985234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
986234353Sdim * @heldPeriod: dot1xSuppHeldPeriod
987234353Sdim * @authPeriod: dot1xSuppAuthPeriod
988234353Sdim * @startPeriod: dot1xSuppStartPeriod
989234353Sdim * @maxStart: dot1xSuppMaxStart
990234353Sdim *
991234353Sdim * Set configurable EAPOL state machine variables. Each variable can be set to
992234353Sdim * the given value or ignored if set to -1 (to set only some of the variables).
993234353Sdim */
994234353Sdimvoid eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
995234353Sdim			int startPeriod, int maxStart)
996234353Sdim{
997234353Sdim	if (sm == NULL)
998234353Sdim		return;
999234353Sdim	if (heldPeriod >= 0)
1000234353Sdim		sm->heldPeriod = heldPeriod;
1001234353Sdim	if (authPeriod >= 0)
1002249423Sdim		sm->authPeriod = authPeriod;
1003203955Srdivacky	if (startPeriod >= 0)
1004249423Sdim		sm->startPeriod = startPeriod;
1005203955Srdivacky	if (maxStart >= 0)
1006203955Srdivacky		sm->maxStart = maxStart;
1007249423Sdim}
1008203955Srdivacky
1009199990Srdivacky
1010206084Srdivacky#ifdef CONFIG_CTRL_IFACE
1011206084Srdivacky/**
1012206084Srdivacky * eapol_sm_get_status - Get EAPOL state machine status
1013206084Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1014206084Srdivacky * @buf: Buffer for status information
1015198092Srdivacky * @buflen: Maximum buffer length
1016198092Srdivacky * @verbose: Whether to include verbose status information
1017249423Sdim * Returns: Number of bytes written to buf.
1018249423Sdim *
1019199990Srdivacky * Query EAPOL state machine for status information. This function fills in a
1020193326Sed * text area with current status information from the EAPOL state machine. If
1021193326Sed * the buffer (buf) is not large enough, status information will be truncated
1022193326Sed * to fit the buffer.
1023193326Sed */
1024203955Srdivackyint eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
1025193326Sed			int verbose)
1026239462Sdim{
1027239462Sdim	int len, ret;
1028239462Sdim	if (sm == NULL)
1029239462Sdim		return 0;
1030249423Sdim
1031249423Sdim	len = os_snprintf(buf, buflen,
1032249423Sdim			  "Supplicant PAE state=%s\n"
1033249423Sdim			  "suppPortStatus=%s\n",
1034249423Sdim			  eapol_supp_pae_state(sm->SUPP_PAE_state),
1035249423Sdim			  eapol_port_status(sm->suppPortStatus));
1036249423Sdim	if (len < 0 || (size_t) len >= buflen)
1037249423Sdim		return 0;
1038249423Sdim
1039249423Sdim	if (verbose) {
1040249423Sdim		ret = os_snprintf(buf + len, buflen - len,
1041249423Sdim				  "heldPeriod=%u\n"
1042249423Sdim				  "authPeriod=%u\n"
1043249423Sdim				  "startPeriod=%u\n"
1044193326Sed				  "maxStart=%u\n"
1045193326Sed				  "portControl=%s\n"
1046193326Sed				  "Supplicant Backend state=%s\n",
1047193326Sed				  sm->heldPeriod,
1048249423Sdim				  sm->authPeriod,
1049249423Sdim				  sm->startPeriod,
1050203955Srdivacky				  sm->maxStart,
1051193326Sed				  eapol_port_control(sm->portControl),
1052234353Sdim				  eapol_supp_be_state(sm->SUPP_BE_state));
1053234353Sdim		if (ret < 0 || (size_t) ret >= buflen - len)
1054234353Sdim			return len;
1055234353Sdim		len += ret;
1056198092Srdivacky	}
1057198092Srdivacky
1058198092Srdivacky	len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
1059198092Srdivacky
1060203955Srdivacky	return len;
1061198092Srdivacky}
1062193326Sed
1063193326Sed
1064203955Srdivacky/**
1065193326Sed * eapol_sm_get_mib - Get EAPOL state machine MIBs
1066193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1067193326Sed * @buf: Buffer for MIB information
1068203955Srdivacky * @buflen: Maximum buffer length
1069198092Srdivacky * Returns: Number of bytes written to buf.
1070221345Sdim *
1071221345Sdim * Query EAPOL state machine for MIB information. This function fills in a
1072221345Sdim * text area with current MIB information from the EAPOL state machine. If
1073221345Sdim * the buffer (buf) is not large enough, MIB information will be truncated to
1074223017Sdim * fit the buffer.
1075223017Sdim */
1076223017Sdimint eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
1077234353Sdim{
1078249423Sdim	size_t len;
1079249423Sdim	int ret;
1080223017Sdim
1081249423Sdim	if (sm == NULL)
1082249423Sdim		return 0;
1083223017Sdim	ret = os_snprintf(buf, buflen,
1084198092Srdivacky			  "dot1xSuppPaeState=%d\n"
1085249423Sdim			  "dot1xSuppHeldPeriod=%u\n"
1086249423Sdim			  "dot1xSuppAuthPeriod=%u\n"
1087249423Sdim			  "dot1xSuppStartPeriod=%u\n"
1088249423Sdim			  "dot1xSuppMaxStart=%u\n"
1089249423Sdim			  "dot1xSuppSuppControlledPortStatus=%s\n"
1090249423Sdim			  "dot1xSuppBackendPaeState=%d\n",
1091249423Sdim			  sm->SUPP_PAE_state,
1092249423Sdim			  sm->heldPeriod,
1093249423Sdim			  sm->authPeriod,
1094249423Sdim			  sm->startPeriod,
1095226633Sdim			  sm->maxStart,
1096234353Sdim			  sm->suppPortStatus == Authorized ?
1097249423Sdim			  "Authorized" : "Unauthorized",
1098234353Sdim			  sm->SUPP_BE_state);
1099221345Sdim
1100221345Sdim	if (ret < 0 || (size_t) ret >= buflen)
1101249423Sdim		return 0;
1102249423Sdim	len = ret;
1103234353Sdim
1104239462Sdim	ret = os_snprintf(buf + len, buflen - len,
1105239462Sdim			  "dot1xSuppEapolFramesRx=%u\n"
1106234353Sdim			  "dot1xSuppEapolFramesTx=%u\n"
1107234353Sdim			  "dot1xSuppEapolStartFramesTx=%u\n"
1108249423Sdim			  "dot1xSuppEapolLogoffFramesTx=%u\n"
1109234353Sdim			  "dot1xSuppEapolRespFramesTx=%u\n"
1110234353Sdim			  "dot1xSuppEapolReqIdFramesRx=%u\n"
1111249423Sdim			  "dot1xSuppEapolReqFramesRx=%u\n"
1112239462Sdim			  "dot1xSuppInvalidEapolFramesRx=%u\n"
1113234353Sdim			  "dot1xSuppEapLengthErrorFramesRx=%u\n"
1114234353Sdim			  "dot1xSuppLastEapolFrameVersion=%u\n"
1115249423Sdim			  "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
1116249423Sdim			  sm->dot1xSuppEapolFramesRx,
1117203955Srdivacky			  sm->dot1xSuppEapolFramesTx,
1118249423Sdim			  sm->dot1xSuppEapolStartFramesTx,
1119203955Srdivacky			  sm->dot1xSuppEapolLogoffFramesTx,
1120198092Srdivacky			  sm->dot1xSuppEapolRespFramesTx,
1121249423Sdim			  sm->dot1xSuppEapolReqIdFramesRx,
1122249423Sdim			  sm->dot1xSuppEapolReqFramesRx,
1123249423Sdim			  sm->dot1xSuppInvalidEapolFramesRx,
1124249423Sdim			  sm->dot1xSuppEapLengthErrorFramesRx,
1125249423Sdim			  sm->dot1xSuppLastEapolFrameVersion,
1126249423Sdim			  MAC2STR(sm->dot1xSuppLastEapolFrameSource));
1127249423Sdim
1128249423Sdim	if (ret < 0 || (size_t) ret >= buflen - len)
1129249423Sdim		return len;
1130221345Sdim	len += ret;
1131249423Sdim
1132249423Sdim	return len;
1133221345Sdim}
1134221345Sdim#endif /* CONFIG_CTRL_IFACE */
1135249423Sdim
1136249423Sdim
1137249423Sdim/**
1138249423Sdim * eapol_sm_rx_eapol - Process received EAPOL frames
1139249423Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1140249423Sdim * @src: Source MAC address of the EAPOL packet
1141249423Sdim * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
1142249423Sdim * @len: Length of the EAPOL frame
1143249423Sdim * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
1144249423Sdim * -1 failure
1145203955Srdivacky */
1146249423Sdimint eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
1147203955Srdivacky		      size_t len)
1148198092Srdivacky{
1149249423Sdim	const struct ieee802_1x_hdr *hdr;
1150249423Sdim	const struct ieee802_1x_eapol_key *key;
1151249423Sdim	int data_len;
1152249423Sdim	int res = 1;
1153249423Sdim	size_t plen;
1154249423Sdim
1155249423Sdim	if (sm == NULL)
1156249423Sdim		return 0;
1157249423Sdim	sm->dot1xSuppEapolFramesRx++;
1158221345Sdim	if (len < sizeof(*hdr)) {
1159249423Sdim		sm->dot1xSuppInvalidEapolFramesRx++;
1160249423Sdim		return 0;
1161221345Sdim	}
1162221345Sdim	hdr = (const struct ieee802_1x_hdr *) buf;
1163249423Sdim	sm->dot1xSuppLastEapolFrameVersion = hdr->version;
1164249423Sdim	os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
1165249423Sdim	if (hdr->version < EAPOL_VERSION) {
1166249423Sdim		/* TODO: backwards compatibility */
1167249423Sdim	}
1168249423Sdim	plen = be_to_host16(hdr->length);
1169249423Sdim	if (plen > len - sizeof(*hdr)) {
1170198092Srdivacky		sm->dot1xSuppEapLengthErrorFramesRx++;
1171249423Sdim		return 0;
1172249423Sdim	}
1173249423Sdim#ifdef CONFIG_WPS
1174249423Sdim	if (sm->conf.workaround &&
1175249423Sdim	    plen < len - sizeof(*hdr) &&
1176249423Sdim	    hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
1177249423Sdim	    len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
1178249423Sdim		const struct eap_hdr *ehdr =
1179249423Sdim			(const struct eap_hdr *) (hdr + 1);
1180249423Sdim		u16 elen;
1181249423Sdim
1182249423Sdim		elen = be_to_host16(ehdr->length);
1183234353Sdim		if (elen > plen && elen <= len - sizeof(*hdr)) {
1184234353Sdim			/*
1185234353Sdim			 * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
1186234353Sdim			 * packets with too short EAPOL header length field
1187234353Sdim			 * (14 octets). This is fixed in firmware Ver.1.49.
1188234353Sdim			 * As a workaround, fix the EAPOL header based on the
1189234353Sdim			 * correct length in the EAP packet.
1190234353Sdim			 */
1191234353Sdim			wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
1192221345Sdim				   "payload length based on EAP header: "
1193221345Sdim				   "%d -> %d", (int) plen, elen);
1194221345Sdim			plen = elen;
1195221345Sdim		}
1196221345Sdim	}
1197223017Sdim#endif /* CONFIG_WPS */
1198221345Sdim	data_len = plen + sizeof(*hdr);
1199221345Sdim
1200223017Sdim	switch (hdr->type) {
1201223017Sdim	case IEEE802_1X_TYPE_EAP_PACKET:
1202223017Sdim		if (sm->cached_pmk) {
1203223017Sdim			/* Trying to use PMKSA caching, but Authenticator did
1204223017Sdim			 * not seem to have a matching entry. Need to restart
1205223017Sdim			 * EAPOL state machines.
1206223017Sdim			 */
1207223017Sdim			eapol_sm_abort_cached(sm);
1208223017Sdim		}
1209226633Sdim		wpabuf_free(sm->eapReqData);
1210226633Sdim		sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
1211234353Sdim		if (sm->eapReqData) {
1212226633Sdim			wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
1213234353Sdim				   "frame");
1214226633Sdim			sm->eapolEap = TRUE;
1215226633Sdim			eapol_sm_step(sm);
1216226633Sdim		}
1217226633Sdim		break;
1218226633Sdim	case IEEE802_1X_TYPE_EAPOL_KEY:
1219234353Sdim		if (plen < sizeof(*key)) {
1220234353Sdim			wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
1221226633Sdim				   "frame received");
1222234353Sdim			break;
1223234353Sdim		}
1224226633Sdim		key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
1225226633Sdim		if (key->type == EAPOL_KEY_TYPE_WPA ||
1226234353Sdim		    key->type == EAPOL_KEY_TYPE_RSN) {
1227234353Sdim			/* WPA Supplicant takes care of this frame. */
1228226633Sdim			wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
1229226633Sdim				   "frame in EAPOL state machines");
1230226633Sdim			res = 0;
1231193326Sed			break;
1232193326Sed		}
1233193326Sed		if (key->type != EAPOL_KEY_TYPE_RC4) {
1234193326Sed			wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
1235193326Sed				   "EAPOL-Key type %d", key->type);
1236193326Sed			break;
1237239462Sdim		}
1238193326Sed		os_free(sm->last_rx_key);
1239193326Sed		sm->last_rx_key = os_malloc(data_len);
1240193326Sed		if (sm->last_rx_key) {
1241193326Sed			wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
1242239462Sdim				   "frame");
1243193326Sed			os_memcpy(sm->last_rx_key, buf, data_len);
1244193326Sed			sm->last_rx_key_len = data_len;
1245193326Sed			sm->rxKey = TRUE;
1246193326Sed			eapol_sm_step(sm);
1247193326Sed		}
1248193326Sed		break;
1249193326Sed	default:
1250198092Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
1251234353Sdim			   hdr->type);
1252198092Srdivacky		sm->dot1xSuppInvalidEapolFramesRx++;
1253198092Srdivacky		break;
1254198092Srdivacky	}
1255249423Sdim
1256249423Sdim	return res;
1257249423Sdim}
1258234353Sdim
1259193326Sed
1260193326Sed/**
1261198092Srdivacky * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
1262198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1263193326Sed *
1264193326Sed * Notify EAPOL state machine about transmitted EAPOL packet from an external
1265193326Sed * component, e.g., WPA. This will update the statistics.
1266193326Sed */
1267193326Sedvoid eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
1268193326Sed{
1269193326Sed	if (sm)
1270193326Sed		sm->dot1xSuppEapolFramesTx++;
1271193326Sed}
1272193326Sed
1273193326Sed
1274193326Sed/**
1275193326Sed * eapol_sm_notify_portEnabled - Notification about portEnabled change
1276193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1277193326Sed * @enabled: New portEnabled value
1278193326Sed *
1279193326Sed * Notify EAPOL state machine about new portEnabled value.
1280193326Sed */
1281193326Sedvoid eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
1282193326Sed{
1283198092Srdivacky	if (sm == NULL)
1284198092Srdivacky		return;
1285198092Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1286200583Srdivacky		   "portEnabled=%d", enabled);
1287234353Sdim	sm->portEnabled = enabled;
1288198092Srdivacky	eapol_sm_step(sm);
1289198092Srdivacky}
1290198092Srdivacky
1291193326Sed
1292210299Sed/**
1293198092Srdivacky * eapol_sm_notify_portValid - Notification about portValid change
1294195099Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1295195099Sed * @valid: New portValid value
1296195099Sed *
1297195099Sed * Notify EAPOL state machine about new portValid value.
1298195099Sed */
1299198092Srdivackyvoid eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
1300195099Sed{
1301195099Sed	if (sm == NULL)
1302198092Srdivacky		return;
1303249423Sdim	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1304249423Sdim		   "portValid=%d", valid);
1305249423Sdim	sm->portValid = valid;
1306249423Sdim	eapol_sm_step(sm);
1307198092Srdivacky}
1308198092Srdivacky
1309198092Srdivacky
1310198092Srdivacky/**
1311198092Srdivacky * eapol_sm_notify_eap_success - Notification of external EAP success trigger
1312198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1313198092Srdivacky * @success: %TRUE = set success, %FALSE = clear success
1314198092Srdivacky *
1315198092Srdivacky * Notify the EAPOL state machine that external event has forced EAP state to
1316198092Srdivacky * success (success = %TRUE). This can be cleared by setting success = %FALSE.
1317218893Sdim *
1318234353Sdim * This function is called to update EAP state when WPA-PSK key handshake has
1319198092Srdivacky * been completed successfully since WPA-PSK does not use EAP state machine.
1320198092Srdivacky */
1321198092Srdivackyvoid eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
1322198092Srdivacky{
1323198092Srdivacky	if (sm == NULL)
1324198092Srdivacky		return;
1325198092Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1326198092Srdivacky		   "EAP success=%d", success);
1327198092Srdivacky	sm->eapSuccess = success;
1328198092Srdivacky	sm->altAccept = success;
1329198092Srdivacky	if (success)
1330198092Srdivacky		eap_notify_success(sm->eap);
1331198092Srdivacky	eapol_sm_step(sm);
1332198092Srdivacky}
1333234353Sdim
1334234353Sdim
1335218893Sdim/**
1336200583Srdivacky * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
1337204643Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1338204643Srdivacky * @fail: %TRUE = set failure, %FALSE = clear failure
1339204643Srdivacky *
1340204643Srdivacky * Notify EAPOL state machine that external event has forced EAP state to
1341204643Srdivacky * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
1342204643Srdivacky */
1343204643Srdivackyvoid eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
1344204643Srdivacky{
1345204643Srdivacky	if (sm == NULL)
1346204643Srdivacky		return;
1347204643Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1348204643Srdivacky		   "EAP fail=%d", fail);
1349204643Srdivacky	sm->eapFail = fail;
1350239462Sdim	sm->altReject = fail;
1351204643Srdivacky	eapol_sm_step(sm);
1352200583Srdivacky}
1353200583Srdivacky
1354200583Srdivacky
1355200583Srdivacky/**
1356200583Srdivacky * eapol_sm_notify_config - Notification of EAPOL configuration change
1357200583Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1358243830Sdim * @config: Pointer to current network EAP configuration
1359200583Srdivacky * @conf: Pointer to EAPOL configuration data
1360200583Srdivacky *
1361200583Srdivacky * Notify EAPOL state machine that configuration has changed. config will be
1362200583Srdivacky * stored as a backpointer to network configuration. This can be %NULL to clear
1363200583Srdivacky * the stored pointed. conf will be copied to local EAPOL/EAP configuration
1364200583Srdivacky * data. If conf is %NULL, this part of the configuration change will be
1365200583Srdivacky * skipped.
1366200583Srdivacky */
1367200583Srdivackyvoid eapol_sm_notify_config(struct eapol_sm *sm,
1368200583Srdivacky			    struct eap_peer_config *config,
1369234353Sdim			    const struct eapol_config *conf)
1370200583Srdivacky{
1371200583Srdivacky	if (sm == NULL)
1372200583Srdivacky		return;
1373200583Srdivacky
1374200583Srdivacky	sm->config = config;
1375234353Sdim
1376234353Sdim	if (conf == NULL)
1377198092Srdivacky		return;
1378198092Srdivacky
1379234353Sdim	sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
1380198092Srdivacky	sm->conf.required_keys = conf->required_keys;
1381198092Srdivacky	sm->conf.fast_reauth = conf->fast_reauth;
1382234353Sdim	sm->conf.workaround = conf->workaround;
1383198092Srdivacky	if (sm->eap) {
1384198092Srdivacky		eap_set_fast_reauth(sm->eap, conf->fast_reauth);
1385198092Srdivacky		eap_set_workaround(sm->eap, conf->workaround);
1386198092Srdivacky		eap_set_force_disabled(sm->eap, conf->eap_disabled);
1387198092Srdivacky	}
1388198092Srdivacky}
1389199482Srdivacky
1390198092Srdivacky
1391198092Srdivacky/**
1392234353Sdim * eapol_sm_get_key - Get master session key (MSK) from EAP
1393198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1394198092Srdivacky * @key: Pointer for key buffer
1395198092Srdivacky * @len: Number of bytes to copy to key
1396234353Sdim * Returns: 0 on success (len of key available), maximum available key len
1397198092Srdivacky * (>0) if key is available but it is shorter than len, or -1 on failure.
1398234353Sdim *
1399198092Srdivacky * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
1400198092Srdivacky * is available only after a successful authentication.
1401198092Srdivacky */
1402198092Srdivackyint eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
1403198092Srdivacky{
1404198092Srdivacky	const u8 *eap_key;
1405198092Srdivacky	size_t eap_len;
1406198092Srdivacky
1407198092Srdivacky	if (sm == NULL || !eap_key_available(sm->eap)) {
1408198092Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
1409198092Srdivacky		return -1;
1410198092Srdivacky	}
1411198092Srdivacky	eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
1412198092Srdivacky	if (eap_key == NULL) {
1413198092Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
1414199482Srdivacky		return -1;
1415234353Sdim	}
1416198092Srdivacky	if (len > eap_len) {
1417198092Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
1418198092Srdivacky			   "available (len=%lu)",
1419198092Srdivacky			   (unsigned long) len, (unsigned long) eap_len);
1420198092Srdivacky		return eap_len;
1421198092Srdivacky	}
1422198092Srdivacky	os_memcpy(key, eap_key, len);
1423199482Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
1424199482Srdivacky		   (unsigned long) len);
1425204643Srdivacky	return 0;
1426204643Srdivacky}
1427204643Srdivacky
1428204643Srdivacky
1429204643Srdivacky/**
1430204643Srdivacky * eapol_sm_notify_logoff - Notification of logon/logoff commands
1431204643Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1432204643Srdivacky * @logoff: Whether command was logoff
1433204643Srdivacky *
1434204643Srdivacky * Notify EAPOL state machines that user requested logon/logoff.
1435204643Srdivacky */
1436204643Srdivackyvoid eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
1437234353Sdim{
1438198092Srdivacky	if (sm) {
1439198092Srdivacky		sm->userLogoff = logoff;
1440198092Srdivacky		eapol_sm_step(sm);
1441198092Srdivacky	}
1442198092Srdivacky}
1443198092Srdivacky
1444199482Srdivacky
1445199482Srdivacky/**
1446198092Srdivacky * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
1447198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1448198092Srdivacky *
1449198092Srdivacky * Notify EAPOL state machines that PMKSA caching was successful. This is used
1450198092Srdivacky * to move EAPOL and EAP state machines into authenticated/successful state.
1451198092Srdivacky */
1452198092Srdivackyvoid eapol_sm_notify_cached(struct eapol_sm *sm)
1453199482Srdivacky{
1454199482Srdivacky	if (sm == NULL)
1455234353Sdim		return;
1456198092Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
1457198092Srdivacky	sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
1458198092Srdivacky	sm->suppPortStatus = Authorized;
1459198092Srdivacky	sm->portValid = TRUE;
1460198092Srdivacky	eap_notify_success(sm->eap);
1461198092Srdivacky	eapol_sm_step(sm);
1462199482Srdivacky}
1463198092Srdivacky
1464198092Srdivacky
1465206084Srdivacky/**
1466206084Srdivacky * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
1467206084Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1468206084Srdivacky * @attempt: Whether PMKSA caching is tried
1469206084Srdivacky *
1470206084Srdivacky * Notify EAPOL state machines whether PMKSA caching is used.
1471218893Sdim */
1472218893Sdimvoid eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
1473218893Sdim{
1474193326Sed	if (sm == NULL)
1475193326Sed		return;
1476193326Sed	if (attempt) {
1477193326Sed		wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
1478193326Sed		sm->cached_pmk = TRUE;
1479202879Srdivacky	} else {
1480202879Srdivacky		wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
1481202879Srdivacky		sm->cached_pmk = FALSE;
1482202879Srdivacky	}
1483202879Srdivacky}
1484202879Srdivacky
1485202879Srdivacky
1486202879Srdivackystatic void eapol_sm_abort_cached(struct eapol_sm *sm)
1487202879Srdivacky{
1488249423Sdim	wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
1489249423Sdim		   "doing full EAP authentication");
1490249423Sdim	if (sm == NULL)
1491249423Sdim		return;
1492218893Sdim	sm->cached_pmk = FALSE;
1493218893Sdim	sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
1494218893Sdim	sm->suppPortStatus = Unauthorized;
1495234353Sdim
1496218893Sdim	/* Make sure we do not start sending EAPOL-Start frames first, but
1497234353Sdim	 * instead move to RESTART state to start EAPOL authentication. */
1498218893Sdim	sm->startWhen = 3;
1499218893Sdim	eapol_enable_timer_tick(sm);
1500218893Sdim
1501218893Sdim	if (sm->ctx->aborted_cached)
1502218893Sdim		sm->ctx->aborted_cached(sm->ctx->ctx);
1503234353Sdim}
1504218893Sdim
1505218893Sdim
1506218893Sdim/**
1507218893Sdim * eapol_sm_register_scard_ctx - Notification of smart card context
1508218893Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1509218893Sdim * @ctx: Context data for smart card operations
1510218893Sdim *
1511218893Sdim * Notify EAPOL state machines of context data for smart card operations. This
1512234353Sdim * context data will be used as a parameter for scard_*() functions.
1513234353Sdim */
1514234353Sdimvoid eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
1515234353Sdim{
1516234353Sdim	if (sm) {
1517234353Sdim		sm->ctx->scard_ctx = ctx;
1518234353Sdim		eap_register_scard_ctx(sm->eap, ctx);
1519234353Sdim	}
1520234353Sdim}
1521234353Sdim
1522234353Sdim
1523218893Sdim/**
1524234353Sdim * eapol_sm_notify_portControl - Notification of portControl changes
1525234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1526234353Sdim * @portControl: New value for portControl variable
1527234353Sdim *
1528234353Sdim * Notify EAPOL state machines that portControl variable has changed.
1529234353Sdim */
1530234353Sdimvoid eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
1531234353Sdim{
1532234353Sdim	if (sm == NULL)
1533234353Sdim		return;
1534234353Sdim	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1535234353Sdim		   "portControl=%s", eapol_port_control(portControl));
1536234353Sdim	sm->portControl = portControl;
1537234353Sdim	eapol_sm_step(sm);
1538234353Sdim}
1539234353Sdim
1540234353Sdim
1541234353Sdim/**
1542234353Sdim * eapol_sm_notify_ctrl_attached - Notification of attached monitor
1543234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1544234353Sdim *
1545249423Sdim * Notify EAPOL state machines that a monitor was attached to the control
1546249423Sdim * interface to trigger re-sending of pending requests for user input.
1547249423Sdim */
1548234353Sdimvoid eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
1549234353Sdim{
1550234353Sdim	if (sm == NULL)
1551234353Sdim		return;
1552234353Sdim	eap_sm_notify_ctrl_attached(sm->eap);
1553234353Sdim}
1554234353Sdim
1555234353Sdim
1556234353Sdim/**
1557234353Sdim * eapol_sm_notify_ctrl_response - Notification of received user input
1558234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1559234353Sdim *
1560234353Sdim * Notify EAPOL state machines that a control response, i.e., user
1561234353Sdim * input, was received in order to trigger retrying of a pending EAP request.
1562243830Sdim */
1563243830Sdimvoid eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
1564243830Sdim{
1565243830Sdim	if (sm == NULL)
1566243830Sdim		return;
1567203955Srdivacky	if (sm->eapReqData && !sm->eapReq) {
1568203955Srdivacky		wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
1569210299Sed			   "input) notification - retrying pending EAP "
1570193326Sed			   "Request");
1571210299Sed		sm->eapolEap = TRUE;
1572212904Sdim		sm->eapReq = TRUE;
1573212904Sdim		eapol_sm_step(sm);
1574218893Sdim	}
1575218893Sdim}
1576193326Sed
1577193326Sed
1578193326Sed/**
1579193326Sed * eapol_sm_request_reauth - Request reauthentication
1580193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1581234353Sdim *
1582193326Sed * This function can be used to request EAPOL reauthentication, e.g., when the
1583221345Sdim * current PMKSA entry is nearing expiration.
1584212904Sdim */
1585212904Sdimvoid eapol_sm_request_reauth(struct eapol_sm *sm)
1586249423Sdim{
1587226633Sdim	if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
1588221345Sdim		return;
1589249423Sdim	eapol_sm_txStart(sm);
1590234353Sdim}
1591234353Sdim
1592234353Sdim
1593193326Sed/**
1594193326Sed * eapol_sm_notify_lower_layer_success - Notification of lower layer success
1595193326Sed * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1596221345Sdim * @in_eapol_sm: Whether the caller is already running inside EAPOL state
1597212904Sdim * machine loop (eapol_sm_step())
1598212904Sdim *
1599249423Sdim * Notify EAPOL (and EAP) state machines that a lower layer has detected a
1600221345Sdim * successful authentication. This is used to recover from dropped EAP-Success
1601226633Sdim * messages.
1602221345Sdim */
1603198092Srdivackyvoid eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
1604234353Sdim{
1605249423Sdim	if (sm == NULL)
1606249423Sdim		return;
1607193326Sed	eap_notify_lower_layer_success(sm->eap);
1608193326Sed	if (!in_eapol_sm)
1609243830Sdim		eapol_sm_step(sm);
1610243830Sdim}
1611239462Sdim
1612198092Srdivacky
1613234353Sdim/**
1614198092Srdivacky * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
1615198092Srdivacky * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1616243830Sdim */
1617243830Sdimvoid eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
1618243830Sdim{
1619198092Srdivacky	if (sm)
1620234353Sdim		eap_invalidate_cached_session(sm->eap);
1621198092Srdivacky}
1622193326Sed
1623206084Srdivacky
1624198092Srdivackystatic struct eap_peer_config * eapol_sm_get_config(void *ctx)
1625198092Srdivacky{
1626198092Srdivacky	struct eapol_sm *sm = ctx;
1627198092Srdivacky	return sm ? sm->config : NULL;
1628234353Sdim}
1629207619Srdivacky
1630207619Srdivacky
1631207619Srdivackystatic struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
1632223017Sdim{
1633223017Sdim	struct eapol_sm *sm = ctx;
1634223017Sdim	if (sm == NULL || sm->eapReqData == NULL)
1635234353Sdim		return NULL;
1636198092Srdivacky
1637198092Srdivacky	return sm->eapReqData;
1638198092Srdivacky}
1639198092Srdivacky
1640198092Srdivacky
1641198092Srdivackystatic Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
1642223017Sdim{
1643239462Sdim	struct eapol_sm *sm = ctx;
1644239462Sdim	if (sm == NULL)
1645223017Sdim		return FALSE;
1646223017Sdim	switch (variable) {
1647223017Sdim	case EAPOL_eapSuccess:
1648234353Sdim		return sm->eapSuccess;
1649198092Srdivacky	case EAPOL_eapRestart:
1650198092Srdivacky		return sm->eapRestart;
1651193326Sed	case EAPOL_eapFail:
1652234353Sdim		return sm->eapFail;
1653198092Srdivacky	case EAPOL_eapResp:
1654193326Sed		return sm->eapResp;
1655193326Sed	case EAPOL_eapNoResp:
1656210299Sed		return sm->eapNoResp;
1657198092Srdivacky	case EAPOL_eapReq:
1658193326Sed		return sm->eapReq;
1659193326Sed	case EAPOL_portEnabled:
1660198092Srdivacky		return sm->portEnabled;
1661198092Srdivacky	case EAPOL_altAccept:
1662193326Sed		return sm->altAccept;
1663198092Srdivacky	case EAPOL_altReject:
1664193326Sed		return sm->altReject;
1665193326Sed	}
1666198092Srdivacky	return FALSE;
1667193326Sed}
1668193326Sed
1669193326Sed
1670193326Sedstatic void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
1671193326Sed			      Boolean value)
1672193326Sed{
1673193326Sed	struct eapol_sm *sm = ctx;
1674193326Sed	if (sm == NULL)
1675193326Sed		return;
1676198092Srdivacky	switch (variable) {
1677193326Sed	case EAPOL_eapSuccess:
1678193326Sed		sm->eapSuccess = value;
1679218893Sdim		break;
1680218893Sdim	case EAPOL_eapRestart:
1681218893Sdim		sm->eapRestart = value;
1682218893Sdim		break;
1683239462Sdim	case EAPOL_eapFail:
1684218893Sdim		sm->eapFail = value;
1685218893Sdim		break;
1686218893Sdim	case EAPOL_eapResp:
1687218893Sdim		sm->eapResp = value;
1688218893Sdim		break;
1689239462Sdim	case EAPOL_eapNoResp:
1690218893Sdim		sm->eapNoResp = value;
1691218893Sdim		break;
1692218893Sdim	case EAPOL_eapReq:
1693234353Sdim		sm->eapReq = value;
1694200583Srdivacky		break;
1695200583Srdivacky	case EAPOL_portEnabled:
1696234353Sdim		sm->portEnabled = value;
1697234353Sdim		break;
1698234353Sdim	case EAPOL_altAccept:
1699234353Sdim		sm->altAccept = value;
1700234353Sdim		break;
1701234353Sdim	case EAPOL_altReject:
1702234353Sdim		sm->altReject = value;
1703234353Sdim		break;
1704239462Sdim	}
1705239462Sdim}
1706239462Sdim
1707239462Sdim
1708239462Sdimstatic unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
1709239462Sdim{
1710239462Sdim	struct eapol_sm *sm = ctx;
1711239462Sdim	if (sm == NULL)
1712239462Sdim		return 0;
1713239462Sdim	switch (variable) {
1714239462Sdim	case EAPOL_idleWhile:
1715239462Sdim		return sm->idleWhile;
1716239462Sdim	}
1717239462Sdim	return 0;
1718239462Sdim}
1719239462Sdim
1720239462Sdim
1721193326Sedstatic void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
1722203955Srdivacky			     unsigned int value)
1723203955Srdivacky{
1724210299Sed	struct eapol_sm *sm = ctx;
1725193326Sed	if (sm == NULL)
1726193326Sed		return;
1727193326Sed	switch (variable) {
1728218893Sdim	case EAPOL_idleWhile:
1729193326Sed		sm->idleWhile = value;
1730193326Sed		eapol_enable_timer_tick(sm);
1731193326Sed		break;
1732193326Sed	}
1733193326Sed}
1734193326Sed
1735193326Sed
1736193326Sedstatic void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
1737193326Sed{
1738193326Sed#ifndef CONFIG_NO_CONFIG_BLOBS
1739193326Sed	struct eapol_sm *sm = ctx;
1740193326Sed	if (sm && sm->ctx && sm->ctx->set_config_blob)
1741194613Sed		sm->ctx->set_config_blob(sm->ctx->ctx, blob);
1742218893Sdim#endif /* CONFIG_NO_CONFIG_BLOBS */
1743234353Sdim}
1744234353Sdim
1745234353Sdim
1746234353Sdimstatic const struct wpa_config_blob *
1747218893Sdimeapol_sm_get_config_blob(void *ctx, const char *name)
1748234353Sdim{
1749218893Sdim#ifndef CONFIG_NO_CONFIG_BLOBS
1750221345Sdim	struct eapol_sm *sm = ctx;
1751221345Sdim	if (sm && sm->ctx && sm->ctx->get_config_blob)
1752221345Sdim		return sm->ctx->get_config_blob(sm->ctx->ctx, name);
1753218893Sdim	else
1754234353Sdim		return NULL;
1755203955Srdivacky#else /* CONFIG_NO_CONFIG_BLOBS */
1756203955Srdivacky	return NULL;
1757203955Srdivacky#endif /* CONFIG_NO_CONFIG_BLOBS */
1758234353Sdim}
1759200583Srdivacky
1760200583Srdivacky
1761193326Sedstatic void eapol_sm_notify_pending(void *ctx)
1762198092Srdivacky{
1763198092Srdivacky	struct eapol_sm *sm = ctx;
1764198092Srdivacky	if (sm == NULL)
1765234353Sdim		return;
1766234353Sdim	if (sm->eapReqData && !sm->eapReq) {
1767234353Sdim		wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
1768234353Sdim			   "state machine - retrying pending EAP Request");
1769208600Srdivacky		sm->eapolEap = TRUE;
1770208600Srdivacky		sm->eapReq = TRUE;
1771208600Srdivacky		eapol_sm_step(sm);
1772208600Srdivacky	}
1773208600Srdivacky}
1774208600Srdivacky
1775208600Srdivacky
1776218893Sdim#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
1777208600Srdivackystatic void eapol_sm_eap_param_needed(void *ctx, const char *field,
1778208600Srdivacky				      const char *txt)
1779208600Srdivacky{
1780208600Srdivacky	struct eapol_sm *sm = ctx;
1781208600Srdivacky	wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
1782234353Sdim	if (sm->ctx->eap_param_needed)
1783208600Srdivacky		sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
1784218893Sdim}
1785218893Sdim#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
1786218893Sdim#define eapol_sm_eap_param_needed NULL
1787234353Sdim#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
1788193326Sed
1789218893Sdim
1790198092Srdivackystatic struct eapol_callbacks eapol_cb =
1791218893Sdim{
1792218893Sdim	eapol_sm_get_config,
1793218893Sdim	eapol_sm_get_bool,
1794193326Sed	eapol_sm_set_bool,
1795218893Sdim	eapol_sm_get_int,
1796198092Srdivacky	eapol_sm_set_int,
1797218893Sdim	eapol_sm_get_eapReqData,
1798218893Sdim	eapol_sm_set_config_blob,
1799218893Sdim	eapol_sm_get_config_blob,
1800193326Sed	eapol_sm_notify_pending,
1801221345Sdim	eapol_sm_eap_param_needed
1802218893Sdim};
1803218893Sdim
1804218893Sdim
1805218893Sdim/**
1806218893Sdim * eapol_sm_init - Initialize EAPOL state machine
1807221345Sdim * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
1808221345Sdim * and EAPOL state machine will free it in eapol_sm_deinit()
1809234353Sdim * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
1810234353Sdim *
1811221345Sdim * Allocate and initialize an EAPOL state machine.
1812234353Sdim */
1813208600Srdivackystruct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
1814218893Sdim{
1815218893Sdim	struct eapol_sm *sm;
1816218893Sdim	struct eap_config conf;
1817218893Sdim	sm = os_zalloc(sizeof(*sm));
1818234353Sdim	if (sm == NULL)
1819193326Sed		return NULL;
1820193326Sed	sm->ctx = ctx;
1821234353Sdim
1822234353Sdim	sm->portControl = Auto;
1823234353Sdim
1824193326Sed	/* Supplicant PAE state machine */
1825193326Sed	sm->heldPeriod = 60;
1826193326Sed	sm->startPeriod = 30;
1827218893Sdim	sm->maxStart = 3;
1828193326Sed
1829234353Sdim	/* Supplicant Backend state machine */
1830218893Sdim	sm->authPeriod = 30;
1831218893Sdim
1832218893Sdim	os_memset(&conf, 0, sizeof(conf));
1833218893Sdim#ifdef EAP_TLS_OPENSSL
1834218893Sdim	conf.opensc_engine_path = ctx->opensc_engine_path;
1835218893Sdim	conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
1836218893Sdim	conf.pkcs11_module_path = ctx->pkcs11_module_path;
1837223017Sdim#endif /* EAP_TLS_OPENSSL */
1838223017Sdim	conf.wps = ctx->wps;
1839223017Sdim
1840223017Sdim	sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
1841251662Sdim	if (sm->eap == NULL) {
1842223017Sdim		os_free(sm);
1843223017Sdim		return NULL;
1844221345Sdim	}
1845221345Sdim
1846221345Sdim	/* Initialize EAPOL state machines */
1847234353Sdim	sm->initialize = TRUE;
1848221345Sdim	eapol_sm_step(sm);
1849221345Sdim	sm->initialize = FALSE;
1850218893Sdim	eapol_sm_step(sm);
1851234353Sdim
1852234353Sdim	sm->timer_tick_enabled = 1;
1853218893Sdim	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
1854234353Sdim
1855218893Sdim	return sm;
1856218893Sdim}
1857218893Sdim
1858218893Sdim
1859218893Sdim/**
1860234353Sdim * eapol_sm_deinit - Deinitialize EAPOL state machine
1861234353Sdim * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1862200583Srdivacky *
1863200583Srdivacky * Deinitialize and free EAPOL state machine.
1864200583Srdivacky */
1865193326Sedvoid eapol_sm_deinit(struct eapol_sm *sm)
1866200583Srdivacky{
1867200583Srdivacky	if (sm == NULL)
1868200583Srdivacky		return;
1869207619Srdivacky	eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
1870207619Srdivacky	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
1871207619Srdivacky	eap_peer_sm_deinit(sm->eap);
1872207619Srdivacky	os_free(sm->last_rx_key);
1873234353Sdim	wpabuf_free(sm->eapReqData);
1874207619Srdivacky	os_free(sm->ctx);
1875207619Srdivacky	os_free(sm);
1876207619Srdivacky}
1877234353Sdim