1214501Srpaulo/*
2214501Srpaulo * IEEE 802.1X-2004 Authenticator - EAPOL state machine
3214501Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "eloop.h"
13214501Srpaulo#include "state_machine.h"
14214501Srpaulo#include "common/eapol_common.h"
15214501Srpaulo#include "eap_common/eap_defs.h"
16214501Srpaulo#include "eap_common/eap_common.h"
17214501Srpaulo#include "eap_server/eap.h"
18214501Srpaulo#include "eapol_auth_sm.h"
19214501Srpaulo#include "eapol_auth_sm_i.h"
20214501Srpaulo
21214501Srpaulo#define STATE_MACHINE_DATA struct eapol_state_machine
22214501Srpaulo#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X"
23214501Srpaulo#define STATE_MACHINE_ADDR sm->addr
24214501Srpaulo
25214501Srpaulostatic struct eapol_callbacks eapol_cb;
26214501Srpaulo
27214501Srpaulo/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */
28214501Srpaulo
29214501Srpaulo#define setPortAuthorized() \
30214501Srpaulosm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1)
31214501Srpaulo#define setPortUnauthorized() \
32214501Srpaulosm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0)
33214501Srpaulo
34214501Srpaulo/* procedures */
35214501Srpaulo#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0)
36214501Srpaulo#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1)
37214501Srpaulo#define txReq() eapol_auth_tx_req(sm)
38214501Srpaulo#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta)
39214501Srpaulo#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta)
40214501Srpaulo#define processKey() do { } while (0)
41214501Srpaulo
42214501Srpaulo
43214501Srpaulostatic void eapol_sm_step_run(struct eapol_state_machine *sm);
44214501Srpaulostatic void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx);
45214501Srpaulostatic void eapol_auth_initialize(struct eapol_state_machine *sm);
46214501Srpaulo
47214501Srpaulo
48214501Srpaulostatic void eapol_auth_logger(struct eapol_authenticator *eapol,
49214501Srpaulo			      const u8 *addr, eapol_logger_level level,
50214501Srpaulo			      const char *txt)
51214501Srpaulo{
52214501Srpaulo	if (eapol->cb.logger == NULL)
53214501Srpaulo		return;
54214501Srpaulo	eapol->cb.logger(eapol->conf.ctx, addr, level, txt);
55214501Srpaulo}
56214501Srpaulo
57214501Srpaulo
58214501Srpaulostatic void eapol_auth_vlogger(struct eapol_authenticator *eapol,
59214501Srpaulo			       const u8 *addr, eapol_logger_level level,
60214501Srpaulo			       const char *fmt, ...)
61214501Srpaulo{
62214501Srpaulo	char *format;
63214501Srpaulo	int maxlen;
64214501Srpaulo	va_list ap;
65214501Srpaulo
66214501Srpaulo	if (eapol->cb.logger == NULL)
67214501Srpaulo		return;
68214501Srpaulo
69214501Srpaulo	maxlen = os_strlen(fmt) + 100;
70214501Srpaulo	format = os_malloc(maxlen);
71214501Srpaulo	if (!format)
72214501Srpaulo		return;
73214501Srpaulo
74214501Srpaulo	va_start(ap, fmt);
75214501Srpaulo	vsnprintf(format, maxlen, fmt, ap);
76214501Srpaulo	va_end(ap);
77214501Srpaulo
78214501Srpaulo	eapol_auth_logger(eapol, addr, level, format);
79214501Srpaulo
80214501Srpaulo	os_free(format);
81214501Srpaulo}
82214501Srpaulo
83214501Srpaulo
84214501Srpaulostatic void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm,
85214501Srpaulo				     int success)
86214501Srpaulo{
87214501Srpaulo	struct eap_hdr eap;
88214501Srpaulo
89214501Srpaulo	os_memset(&eap, 0, sizeof(eap));
90214501Srpaulo
91214501Srpaulo	eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
92214501Srpaulo	eap.identifier = ++sm->last_eap_id;
93214501Srpaulo	eap.length = host_to_be16(sizeof(eap));
94214501Srpaulo
95214501Srpaulo	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
96214501Srpaulo			   "Sending canned EAP packet %s (identifier %d)",
97214501Srpaulo			   success ? "SUCCESS" : "FAILURE", eap.identifier);
98214501Srpaulo	sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
99214501Srpaulo				 IEEE802_1X_TYPE_EAP_PACKET,
100214501Srpaulo				 (u8 *) &eap, sizeof(eap));
101214501Srpaulo	sm->dot1xAuthEapolFramesTx++;
102214501Srpaulo}
103214501Srpaulo
104214501Srpaulo
105214501Srpaulostatic void eapol_auth_tx_req(struct eapol_state_machine *sm)
106214501Srpaulo{
107214501Srpaulo	if (sm->eap_if->eapReqData == NULL ||
108214501Srpaulo	    wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) {
109214501Srpaulo		eapol_auth_logger(sm->eapol, sm->addr,
110214501Srpaulo				  EAPOL_LOGGER_DEBUG,
111214501Srpaulo				  "TxReq called, but there is no EAP request "
112214501Srpaulo				  "from authentication server");
113214501Srpaulo		return;
114214501Srpaulo	}
115214501Srpaulo
116214501Srpaulo	if (sm->flags & EAPOL_SM_WAIT_START) {
117214501Srpaulo		wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR
118214501Srpaulo			   " while waiting for EAPOL-Start",
119214501Srpaulo			   MAC2STR(sm->addr));
120214501Srpaulo		return;
121214501Srpaulo	}
122214501Srpaulo
123214501Srpaulo	sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData);
124214501Srpaulo	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
125214501Srpaulo			   "Sending EAP Packet (identifier %d)",
126214501Srpaulo			   sm->last_eap_id);
127214501Srpaulo	sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
128214501Srpaulo				 IEEE802_1X_TYPE_EAP_PACKET,
129214501Srpaulo				 wpabuf_head(sm->eap_if->eapReqData),
130214501Srpaulo				 wpabuf_len(sm->eap_if->eapReqData));
131214501Srpaulo	sm->dot1xAuthEapolFramesTx++;
132214501Srpaulo	if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY)
133214501Srpaulo		sm->dot1xAuthEapolReqIdFramesTx++;
134214501Srpaulo	else
135214501Srpaulo		sm->dot1xAuthEapolReqFramesTx++;
136214501Srpaulo}
137214501Srpaulo
138214501Srpaulo
139214501Srpaulo/**
140214501Srpaulo * eapol_port_timers_tick - Port Timers state machine
141214501Srpaulo * @eloop_ctx: struct eapol_state_machine *
142214501Srpaulo * @timeout_ctx: Not used
143214501Srpaulo *
144214501Srpaulo * This statemachine is implemented as a function that will be called
145214501Srpaulo * once a second as a registered event loop timeout.
146214501Srpaulo */
147214501Srpaulostatic void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
148214501Srpaulo{
149214501Srpaulo	struct eapol_state_machine *state = timeout_ctx;
150214501Srpaulo
151214501Srpaulo	if (state->aWhile > 0) {
152214501Srpaulo		state->aWhile--;
153214501Srpaulo		if (state->aWhile == 0) {
154214501Srpaulo			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
155214501Srpaulo				   " - aWhile --> 0",
156214501Srpaulo				   MAC2STR(state->addr));
157214501Srpaulo		}
158214501Srpaulo	}
159214501Srpaulo
160214501Srpaulo	if (state->quietWhile > 0) {
161214501Srpaulo		state->quietWhile--;
162214501Srpaulo		if (state->quietWhile == 0) {
163214501Srpaulo			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
164214501Srpaulo				   " - quietWhile --> 0",
165214501Srpaulo				   MAC2STR(state->addr));
166214501Srpaulo		}
167214501Srpaulo	}
168214501Srpaulo
169214501Srpaulo	if (state->reAuthWhen > 0) {
170214501Srpaulo		state->reAuthWhen--;
171214501Srpaulo		if (state->reAuthWhen == 0) {
172214501Srpaulo			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
173214501Srpaulo				   " - reAuthWhen --> 0",
174214501Srpaulo				   MAC2STR(state->addr));
175214501Srpaulo		}
176214501Srpaulo	}
177214501Srpaulo
178214501Srpaulo	if (state->eap_if->retransWhile > 0) {
179214501Srpaulo		state->eap_if->retransWhile--;
180214501Srpaulo		if (state->eap_if->retransWhile == 0) {
181214501Srpaulo			wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
182214501Srpaulo				   " - (EAP) retransWhile --> 0",
183214501Srpaulo				   MAC2STR(state->addr));
184214501Srpaulo		}
185214501Srpaulo	}
186214501Srpaulo
187214501Srpaulo	eapol_sm_step_run(state);
188214501Srpaulo
189214501Srpaulo	eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state);
190214501Srpaulo}
191214501Srpaulo
192214501Srpaulo
193214501Srpaulo
194214501Srpaulo/* Authenticator PAE state machine */
195214501Srpaulo
196214501SrpauloSM_STATE(AUTH_PAE, INITIALIZE)
197214501Srpaulo{
198214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae);
199214501Srpaulo	sm->portMode = Auto;
200214501Srpaulo}
201214501Srpaulo
202214501Srpaulo
203214501SrpauloSM_STATE(AUTH_PAE, DISCONNECTED)
204214501Srpaulo{
205214501Srpaulo	int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE;
206214501Srpaulo
207214501Srpaulo	if (sm->eapolLogoff) {
208214501Srpaulo		if (sm->auth_pae_state == AUTH_PAE_CONNECTING)
209214501Srpaulo			sm->authEapLogoffsWhileConnecting++;
210214501Srpaulo		else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED)
211214501Srpaulo			sm->authAuthEapLogoffWhileAuthenticated++;
212214501Srpaulo	}
213214501Srpaulo
214214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae);
215214501Srpaulo
216214501Srpaulo	sm->authPortStatus = Unauthorized;
217214501Srpaulo	setPortUnauthorized();
218214501Srpaulo	sm->reAuthCount = 0;
219214501Srpaulo	sm->eapolLogoff = FALSE;
220214501Srpaulo	if (!from_initialize) {
221214501Srpaulo		sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
222214501Srpaulo				       sm->flags & EAPOL_SM_PREAUTH);
223214501Srpaulo	}
224214501Srpaulo}
225214501Srpaulo
226214501Srpaulo
227214501SrpauloSM_STATE(AUTH_PAE, RESTART)
228214501Srpaulo{
229214501Srpaulo	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) {
230214501Srpaulo		if (sm->reAuthenticate)
231214501Srpaulo			sm->authAuthReauthsWhileAuthenticated++;
232214501Srpaulo		if (sm->eapolStart)
233214501Srpaulo			sm->authAuthEapStartsWhileAuthenticated++;
234214501Srpaulo		if (sm->eapolLogoff)
235214501Srpaulo			sm->authAuthEapLogoffWhileAuthenticated++;
236214501Srpaulo	}
237214501Srpaulo
238214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae);
239214501Srpaulo
240214501Srpaulo	sm->eap_if->eapRestart = TRUE;
241214501Srpaulo}
242214501Srpaulo
243214501Srpaulo
244214501SrpauloSM_STATE(AUTH_PAE, CONNECTING)
245214501Srpaulo{
246214501Srpaulo	if (sm->auth_pae_state != AUTH_PAE_CONNECTING)
247214501Srpaulo		sm->authEntersConnecting++;
248214501Srpaulo
249214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae);
250214501Srpaulo
251214501Srpaulo	sm->reAuthenticate = FALSE;
252214501Srpaulo	sm->reAuthCount++;
253214501Srpaulo}
254214501Srpaulo
255214501Srpaulo
256214501SrpauloSM_STATE(AUTH_PAE, HELD)
257214501Srpaulo{
258214501Srpaulo	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail)
259214501Srpaulo		sm->authAuthFailWhileAuthenticating++;
260214501Srpaulo
261214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae);
262214501Srpaulo
263214501Srpaulo	sm->authPortStatus = Unauthorized;
264214501Srpaulo	setPortUnauthorized();
265214501Srpaulo	sm->quietWhile = sm->quietPeriod;
266214501Srpaulo	sm->eapolLogoff = FALSE;
267214501Srpaulo
268214501Srpaulo	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING,
269214501Srpaulo			   "authentication failed - EAP type: %d (%s)",
270214501Srpaulo			   sm->eap_type_authsrv,
271214501Srpaulo			   eap_server_get_name(0, sm->eap_type_authsrv));
272214501Srpaulo	if (sm->eap_type_authsrv != sm->eap_type_supp) {
273214501Srpaulo		eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
274214501Srpaulo				   "Supplicant used different EAP type: "
275214501Srpaulo				   "%d (%s)", sm->eap_type_supp,
276214501Srpaulo				   eap_server_get_name(0, sm->eap_type_supp));
277214501Srpaulo	}
278214501Srpaulo	sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
279214501Srpaulo			       sm->flags & EAPOL_SM_PREAUTH);
280214501Srpaulo}
281214501Srpaulo
282214501Srpaulo
283214501SrpauloSM_STATE(AUTH_PAE, AUTHENTICATED)
284214501Srpaulo{
285214501Srpaulo	char *extra = "";
286214501Srpaulo
287214501Srpaulo	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
288214501Srpaulo		sm->authAuthSuccessesWhileAuthenticating++;
289214501Srpaulo
290214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae);
291214501Srpaulo
292214501Srpaulo	sm->authPortStatus = Authorized;
293214501Srpaulo	setPortAuthorized();
294214501Srpaulo	sm->reAuthCount = 0;
295214501Srpaulo	if (sm->flags & EAPOL_SM_PREAUTH)
296214501Srpaulo		extra = " (pre-authentication)";
297214501Srpaulo	else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE)
298214501Srpaulo		extra = " (PMKSA cache)";
299214501Srpaulo	eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
300214501Srpaulo			   "authenticated - EAP type: %d (%s)%s",
301214501Srpaulo			   sm->eap_type_authsrv,
302214501Srpaulo			   eap_server_get_name(0, sm->eap_type_authsrv),
303214501Srpaulo			   extra);
304214501Srpaulo	sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1,
305214501Srpaulo			       sm->flags & EAPOL_SM_PREAUTH);
306214501Srpaulo}
307214501Srpaulo
308214501Srpaulo
309214501SrpauloSM_STATE(AUTH_PAE, AUTHENTICATING)
310214501Srpaulo{
311214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae);
312214501Srpaulo
313214501Srpaulo	sm->eapolStart = FALSE;
314214501Srpaulo	sm->authSuccess = FALSE;
315214501Srpaulo	sm->authFail = FALSE;
316214501Srpaulo	sm->authTimeout = FALSE;
317214501Srpaulo	sm->authStart = TRUE;
318214501Srpaulo	sm->keyRun = FALSE;
319214501Srpaulo	sm->keyDone = FALSE;
320214501Srpaulo}
321214501Srpaulo
322214501Srpaulo
323214501SrpauloSM_STATE(AUTH_PAE, ABORTING)
324214501Srpaulo{
325214501Srpaulo	if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) {
326214501Srpaulo		if (sm->authTimeout)
327214501Srpaulo			sm->authAuthTimeoutsWhileAuthenticating++;
328214501Srpaulo		if (sm->eapolStart)
329214501Srpaulo			sm->authAuthEapStartsWhileAuthenticating++;
330214501Srpaulo		if (sm->eapolLogoff)
331214501Srpaulo			sm->authAuthEapLogoffWhileAuthenticating++;
332214501Srpaulo	}
333214501Srpaulo
334214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae);
335214501Srpaulo
336214501Srpaulo	sm->authAbort = TRUE;
337214501Srpaulo	sm->keyRun = FALSE;
338214501Srpaulo	sm->keyDone = FALSE;
339214501Srpaulo}
340214501Srpaulo
341214501Srpaulo
342214501SrpauloSM_STATE(AUTH_PAE, FORCE_AUTH)
343214501Srpaulo{
344214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae);
345214501Srpaulo
346214501Srpaulo	sm->authPortStatus = Authorized;
347214501Srpaulo	setPortAuthorized();
348214501Srpaulo	sm->portMode = ForceAuthorized;
349214501Srpaulo	sm->eapolStart = FALSE;
350214501Srpaulo	txCannedSuccess();
351214501Srpaulo}
352214501Srpaulo
353214501Srpaulo
354214501SrpauloSM_STATE(AUTH_PAE, FORCE_UNAUTH)
355214501Srpaulo{
356214501Srpaulo	SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae);
357214501Srpaulo
358214501Srpaulo	sm->authPortStatus = Unauthorized;
359214501Srpaulo	setPortUnauthorized();
360214501Srpaulo	sm->portMode = ForceUnauthorized;
361214501Srpaulo	sm->eapolStart = FALSE;
362214501Srpaulo	txCannedFail();
363214501Srpaulo}
364214501Srpaulo
365214501Srpaulo
366214501SrpauloSM_STEP(AUTH_PAE)
367214501Srpaulo{
368214501Srpaulo	if ((sm->portControl == Auto && sm->portMode != sm->portControl) ||
369214501Srpaulo	    sm->initialize || !sm->eap_if->portEnabled)
370214501Srpaulo		SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE);
371214501Srpaulo	else if (sm->portControl == ForceAuthorized &&
372214501Srpaulo		 sm->portMode != sm->portControl &&
373214501Srpaulo		 !(sm->initialize || !sm->eap_if->portEnabled))
374214501Srpaulo		SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH);
375214501Srpaulo	else if (sm->portControl == ForceUnauthorized &&
376214501Srpaulo		 sm->portMode != sm->portControl &&
377214501Srpaulo		 !(sm->initialize || !sm->eap_if->portEnabled))
378214501Srpaulo		SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH);
379214501Srpaulo	else {
380214501Srpaulo		switch (sm->auth_pae_state) {
381214501Srpaulo		case AUTH_PAE_INITIALIZE:
382214501Srpaulo			SM_ENTER(AUTH_PAE, DISCONNECTED);
383214501Srpaulo			break;
384214501Srpaulo		case AUTH_PAE_DISCONNECTED:
385214501Srpaulo			SM_ENTER(AUTH_PAE, RESTART);
386214501Srpaulo			break;
387214501Srpaulo		case AUTH_PAE_RESTART:
388214501Srpaulo			if (!sm->eap_if->eapRestart)
389214501Srpaulo				SM_ENTER(AUTH_PAE, CONNECTING);
390214501Srpaulo			break;
391214501Srpaulo		case AUTH_PAE_HELD:
392214501Srpaulo			if (sm->quietWhile == 0)
393214501Srpaulo				SM_ENTER(AUTH_PAE, RESTART);
394214501Srpaulo			break;
395214501Srpaulo		case AUTH_PAE_CONNECTING:
396214501Srpaulo			if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax)
397214501Srpaulo				SM_ENTER(AUTH_PAE, DISCONNECTED);
398214501Srpaulo			else if ((sm->eap_if->eapReq &&
399214501Srpaulo				  sm->reAuthCount <= sm->reAuthMax) ||
400214501Srpaulo				 sm->eap_if->eapSuccess || sm->eap_if->eapFail)
401214501Srpaulo				SM_ENTER(AUTH_PAE, AUTHENTICATING);
402214501Srpaulo			break;
403214501Srpaulo		case AUTH_PAE_AUTHENTICATED:
404214501Srpaulo			if (sm->eapolStart || sm->reAuthenticate)
405214501Srpaulo				SM_ENTER(AUTH_PAE, RESTART);
406214501Srpaulo			else if (sm->eapolLogoff || !sm->portValid)
407214501Srpaulo				SM_ENTER(AUTH_PAE, DISCONNECTED);
408214501Srpaulo			break;
409214501Srpaulo		case AUTH_PAE_AUTHENTICATING:
410214501Srpaulo			if (sm->authSuccess && sm->portValid)
411214501Srpaulo				SM_ENTER(AUTH_PAE, AUTHENTICATED);
412214501Srpaulo			else if (sm->authFail ||
413214501Srpaulo				 (sm->keyDone && !sm->portValid))
414214501Srpaulo				SM_ENTER(AUTH_PAE, HELD);
415214501Srpaulo			else if (sm->eapolStart || sm->eapolLogoff ||
416214501Srpaulo				 sm->authTimeout)
417214501Srpaulo				SM_ENTER(AUTH_PAE, ABORTING);
418214501Srpaulo			break;
419214501Srpaulo		case AUTH_PAE_ABORTING:
420214501Srpaulo			if (sm->eapolLogoff && !sm->authAbort)
421214501Srpaulo				SM_ENTER(AUTH_PAE, DISCONNECTED);
422214501Srpaulo			else if (!sm->eapolLogoff && !sm->authAbort)
423214501Srpaulo				SM_ENTER(AUTH_PAE, RESTART);
424214501Srpaulo			break;
425214501Srpaulo		case AUTH_PAE_FORCE_AUTH:
426214501Srpaulo			if (sm->eapolStart)
427214501Srpaulo				SM_ENTER(AUTH_PAE, FORCE_AUTH);
428214501Srpaulo			break;
429214501Srpaulo		case AUTH_PAE_FORCE_UNAUTH:
430214501Srpaulo			if (sm->eapolStart)
431214501Srpaulo				SM_ENTER(AUTH_PAE, FORCE_UNAUTH);
432214501Srpaulo			break;
433214501Srpaulo		}
434214501Srpaulo	}
435214501Srpaulo}
436214501Srpaulo
437214501Srpaulo
438214501Srpaulo
439214501Srpaulo/* Backend Authentication state machine */
440214501Srpaulo
441214501SrpauloSM_STATE(BE_AUTH, INITIALIZE)
442214501Srpaulo{
443214501Srpaulo	SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth);
444214501Srpaulo
445214501Srpaulo	abortAuth();
446214501Srpaulo	sm->eap_if->eapNoReq = FALSE;
447214501Srpaulo	sm->authAbort = FALSE;
448214501Srpaulo}
449214501Srpaulo
450214501Srpaulo
451214501SrpauloSM_STATE(BE_AUTH, REQUEST)
452214501Srpaulo{
453214501Srpaulo	SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth);
454214501Srpaulo
455214501Srpaulo	txReq();
456214501Srpaulo	sm->eap_if->eapReq = FALSE;
457214501Srpaulo	sm->backendOtherRequestsToSupplicant++;
458214501Srpaulo
459214501Srpaulo	/*
460214501Srpaulo	 * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but
461214501Srpaulo	 * it looks like this would be logical thing to do there since the old
462214501Srpaulo	 * EAP response would not be valid anymore after the new EAP request
463214501Srpaulo	 * was sent out.
464214501Srpaulo	 *
465214501Srpaulo	 * A race condition has been reported, in which hostapd ended up
466214501Srpaulo	 * sending out EAP-Response/Identity as a response to the first
467214501Srpaulo	 * EAP-Request from the main EAP method. This can be avoided by
468214501Srpaulo	 * clearing eapolEap here.
469214501Srpaulo	 */
470214501Srpaulo	sm->eapolEap = FALSE;
471214501Srpaulo}
472214501Srpaulo
473214501Srpaulo
474214501SrpauloSM_STATE(BE_AUTH, RESPONSE)
475214501Srpaulo{
476214501Srpaulo	SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth);
477214501Srpaulo
478214501Srpaulo	sm->authTimeout = FALSE;
479214501Srpaulo	sm->eapolEap = FALSE;
480214501Srpaulo	sm->eap_if->eapNoReq = FALSE;
481214501Srpaulo	sm->aWhile = sm->serverTimeout;
482214501Srpaulo	sm->eap_if->eapResp = TRUE;
483214501Srpaulo	/* sendRespToServer(); */
484214501Srpaulo	sm->backendResponses++;
485214501Srpaulo}
486214501Srpaulo
487214501Srpaulo
488214501SrpauloSM_STATE(BE_AUTH, SUCCESS)
489214501Srpaulo{
490214501Srpaulo	SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth);
491214501Srpaulo
492214501Srpaulo	txReq();
493214501Srpaulo	sm->authSuccess = TRUE;
494214501Srpaulo	sm->keyRun = TRUE;
495214501Srpaulo}
496214501Srpaulo
497214501Srpaulo
498214501SrpauloSM_STATE(BE_AUTH, FAIL)
499214501Srpaulo{
500214501Srpaulo	SM_ENTRY_MA(BE_AUTH, FAIL, be_auth);
501214501Srpaulo
502214501Srpaulo	txReq();
503214501Srpaulo	sm->authFail = TRUE;
504214501Srpaulo}
505214501Srpaulo
506214501Srpaulo
507214501SrpauloSM_STATE(BE_AUTH, TIMEOUT)
508214501Srpaulo{
509214501Srpaulo	SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth);
510214501Srpaulo
511214501Srpaulo	sm->authTimeout = TRUE;
512214501Srpaulo}
513214501Srpaulo
514214501Srpaulo
515214501SrpauloSM_STATE(BE_AUTH, IDLE)
516214501Srpaulo{
517214501Srpaulo	SM_ENTRY_MA(BE_AUTH, IDLE, be_auth);
518214501Srpaulo
519214501Srpaulo	sm->authStart = FALSE;
520214501Srpaulo}
521214501Srpaulo
522214501Srpaulo
523214501SrpauloSM_STATE(BE_AUTH, IGNORE)
524214501Srpaulo{
525214501Srpaulo	SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth);
526214501Srpaulo
527214501Srpaulo	sm->eap_if->eapNoReq = FALSE;
528214501Srpaulo}
529214501Srpaulo
530214501Srpaulo
531214501SrpauloSM_STEP(BE_AUTH)
532214501Srpaulo{
533214501Srpaulo	if (sm->portControl != Auto || sm->initialize || sm->authAbort) {
534214501Srpaulo		SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE);
535214501Srpaulo		return;
536214501Srpaulo	}
537214501Srpaulo
538214501Srpaulo	switch (sm->be_auth_state) {
539214501Srpaulo	case BE_AUTH_INITIALIZE:
540214501Srpaulo		SM_ENTER(BE_AUTH, IDLE);
541214501Srpaulo		break;
542214501Srpaulo	case BE_AUTH_REQUEST:
543214501Srpaulo		if (sm->eapolEap)
544214501Srpaulo			SM_ENTER(BE_AUTH, RESPONSE);
545214501Srpaulo		else if (sm->eap_if->eapReq)
546214501Srpaulo			SM_ENTER(BE_AUTH, REQUEST);
547214501Srpaulo		else if (sm->eap_if->eapTimeout)
548214501Srpaulo			SM_ENTER(BE_AUTH, TIMEOUT);
549214501Srpaulo		break;
550214501Srpaulo	case BE_AUTH_RESPONSE:
551214501Srpaulo		if (sm->eap_if->eapNoReq)
552214501Srpaulo			SM_ENTER(BE_AUTH, IGNORE);
553214501Srpaulo		if (sm->eap_if->eapReq) {
554214501Srpaulo			sm->backendAccessChallenges++;
555214501Srpaulo			SM_ENTER(BE_AUTH, REQUEST);
556214501Srpaulo		} else if (sm->aWhile == 0)
557214501Srpaulo			SM_ENTER(BE_AUTH, TIMEOUT);
558214501Srpaulo		else if (sm->eap_if->eapFail) {
559214501Srpaulo			sm->backendAuthFails++;
560214501Srpaulo			SM_ENTER(BE_AUTH, FAIL);
561214501Srpaulo		} else if (sm->eap_if->eapSuccess) {
562214501Srpaulo			sm->backendAuthSuccesses++;
563214501Srpaulo			SM_ENTER(BE_AUTH, SUCCESS);
564214501Srpaulo		}
565214501Srpaulo		break;
566214501Srpaulo	case BE_AUTH_SUCCESS:
567214501Srpaulo		SM_ENTER(BE_AUTH, IDLE);
568214501Srpaulo		break;
569214501Srpaulo	case BE_AUTH_FAIL:
570214501Srpaulo		SM_ENTER(BE_AUTH, IDLE);
571214501Srpaulo		break;
572214501Srpaulo	case BE_AUTH_TIMEOUT:
573214501Srpaulo		SM_ENTER(BE_AUTH, IDLE);
574214501Srpaulo		break;
575214501Srpaulo	case BE_AUTH_IDLE:
576214501Srpaulo		if (sm->eap_if->eapFail && sm->authStart)
577214501Srpaulo			SM_ENTER(BE_AUTH, FAIL);
578214501Srpaulo		else if (sm->eap_if->eapReq && sm->authStart)
579214501Srpaulo			SM_ENTER(BE_AUTH, REQUEST);
580214501Srpaulo		else if (sm->eap_if->eapSuccess && sm->authStart)
581214501Srpaulo			SM_ENTER(BE_AUTH, SUCCESS);
582214501Srpaulo		break;
583214501Srpaulo	case BE_AUTH_IGNORE:
584214501Srpaulo		if (sm->eapolEap)
585214501Srpaulo			SM_ENTER(BE_AUTH, RESPONSE);
586214501Srpaulo		else if (sm->eap_if->eapReq)
587214501Srpaulo			SM_ENTER(BE_AUTH, REQUEST);
588214501Srpaulo		else if (sm->eap_if->eapTimeout)
589214501Srpaulo			SM_ENTER(BE_AUTH, TIMEOUT);
590214501Srpaulo		break;
591214501Srpaulo	}
592214501Srpaulo}
593214501Srpaulo
594214501Srpaulo
595214501Srpaulo
596214501Srpaulo/* Reauthentication Timer state machine */
597214501Srpaulo
598214501SrpauloSM_STATE(REAUTH_TIMER, INITIALIZE)
599214501Srpaulo{
600214501Srpaulo	SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer);
601214501Srpaulo
602214501Srpaulo	sm->reAuthWhen = sm->reAuthPeriod;
603214501Srpaulo}
604214501Srpaulo
605214501Srpaulo
606214501SrpauloSM_STATE(REAUTH_TIMER, REAUTHENTICATE)
607214501Srpaulo{
608214501Srpaulo	SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer);
609214501Srpaulo
610214501Srpaulo	sm->reAuthenticate = TRUE;
611214501Srpaulo	sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
612214501Srpaulo				  EAPOL_AUTH_REAUTHENTICATE);
613214501Srpaulo}
614214501Srpaulo
615214501Srpaulo
616214501SrpauloSM_STEP(REAUTH_TIMER)
617214501Srpaulo{
618214501Srpaulo	if (sm->portControl != Auto || sm->initialize ||
619214501Srpaulo	    sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) {
620214501Srpaulo		SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE);
621214501Srpaulo		return;
622214501Srpaulo	}
623214501Srpaulo
624214501Srpaulo	switch (sm->reauth_timer_state) {
625214501Srpaulo	case REAUTH_TIMER_INITIALIZE:
626214501Srpaulo		if (sm->reAuthWhen == 0)
627214501Srpaulo			SM_ENTER(REAUTH_TIMER, REAUTHENTICATE);
628214501Srpaulo		break;
629214501Srpaulo	case REAUTH_TIMER_REAUTHENTICATE:
630214501Srpaulo		SM_ENTER(REAUTH_TIMER, INITIALIZE);
631214501Srpaulo		break;
632214501Srpaulo	}
633214501Srpaulo}
634214501Srpaulo
635214501Srpaulo
636214501Srpaulo
637214501Srpaulo/* Authenticator Key Transmit state machine */
638214501Srpaulo
639214501SrpauloSM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT)
640214501Srpaulo{
641214501Srpaulo	SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx);
642214501Srpaulo}
643214501Srpaulo
644214501Srpaulo
645214501SrpauloSM_STATE(AUTH_KEY_TX, KEY_TRANSMIT)
646214501Srpaulo{
647214501Srpaulo	SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx);
648214501Srpaulo
649214501Srpaulo	txKey();
650214501Srpaulo	sm->eap_if->eapKeyAvailable = FALSE;
651214501Srpaulo	sm->keyDone = TRUE;
652214501Srpaulo}
653214501Srpaulo
654214501Srpaulo
655214501SrpauloSM_STEP(AUTH_KEY_TX)
656214501Srpaulo{
657214501Srpaulo	if (sm->initialize || sm->portControl != Auto) {
658214501Srpaulo		SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT);
659214501Srpaulo		return;
660214501Srpaulo	}
661214501Srpaulo
662214501Srpaulo	switch (sm->auth_key_tx_state) {
663214501Srpaulo	case AUTH_KEY_TX_NO_KEY_TRANSMIT:
664214501Srpaulo		if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable &&
665214501Srpaulo		    sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA))
666214501Srpaulo			SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
667214501Srpaulo		break;
668214501Srpaulo	case AUTH_KEY_TX_KEY_TRANSMIT:
669214501Srpaulo		if (!sm->keyTxEnabled || !sm->keyRun)
670214501Srpaulo			SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT);
671214501Srpaulo		else if (sm->eap_if->eapKeyAvailable)
672214501Srpaulo			SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
673214501Srpaulo		break;
674214501Srpaulo	}
675214501Srpaulo}
676214501Srpaulo
677214501Srpaulo
678214501Srpaulo
679214501Srpaulo/* Key Receive state machine */
680214501Srpaulo
681214501SrpauloSM_STATE(KEY_RX, NO_KEY_RECEIVE)
682214501Srpaulo{
683214501Srpaulo	SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx);
684214501Srpaulo}
685214501Srpaulo
686214501Srpaulo
687214501SrpauloSM_STATE(KEY_RX, KEY_RECEIVE)
688214501Srpaulo{
689214501Srpaulo	SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx);
690214501Srpaulo
691214501Srpaulo	processKey();
692214501Srpaulo	sm->rxKey = FALSE;
693214501Srpaulo}
694214501Srpaulo
695214501Srpaulo
696214501SrpauloSM_STEP(KEY_RX)
697214501Srpaulo{
698214501Srpaulo	if (sm->initialize || !sm->eap_if->portEnabled) {
699214501Srpaulo		SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
700214501Srpaulo		return;
701214501Srpaulo	}
702214501Srpaulo
703214501Srpaulo	switch (sm->key_rx_state) {
704214501Srpaulo	case KEY_RX_NO_KEY_RECEIVE:
705214501Srpaulo		if (sm->rxKey)
706214501Srpaulo			SM_ENTER(KEY_RX, KEY_RECEIVE);
707214501Srpaulo		break;
708214501Srpaulo	case KEY_RX_KEY_RECEIVE:
709214501Srpaulo		if (sm->rxKey)
710214501Srpaulo			SM_ENTER(KEY_RX, KEY_RECEIVE);
711214501Srpaulo		break;
712214501Srpaulo	}
713214501Srpaulo}
714214501Srpaulo
715214501Srpaulo
716214501Srpaulo
717214501Srpaulo/* Controlled Directions state machine */
718214501Srpaulo
719214501SrpauloSM_STATE(CTRL_DIR, FORCE_BOTH)
720214501Srpaulo{
721214501Srpaulo	SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir);
722214501Srpaulo	sm->operControlledDirections = Both;
723214501Srpaulo}
724214501Srpaulo
725214501Srpaulo
726214501SrpauloSM_STATE(CTRL_DIR, IN_OR_BOTH)
727214501Srpaulo{
728214501Srpaulo	SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir);
729214501Srpaulo	sm->operControlledDirections = sm->adminControlledDirections;
730214501Srpaulo}
731214501Srpaulo
732214501Srpaulo
733214501SrpauloSM_STEP(CTRL_DIR)
734214501Srpaulo{
735214501Srpaulo	if (sm->initialize) {
736214501Srpaulo		SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH);
737214501Srpaulo		return;
738214501Srpaulo	}
739214501Srpaulo
740214501Srpaulo	switch (sm->ctrl_dir_state) {
741214501Srpaulo	case CTRL_DIR_FORCE_BOTH:
742214501Srpaulo		if (sm->eap_if->portEnabled && sm->operEdge)
743214501Srpaulo			SM_ENTER(CTRL_DIR, IN_OR_BOTH);
744214501Srpaulo		break;
745214501Srpaulo	case CTRL_DIR_IN_OR_BOTH:
746214501Srpaulo		if (sm->operControlledDirections !=
747214501Srpaulo		    sm->adminControlledDirections)
748214501Srpaulo			SM_ENTER(CTRL_DIR, IN_OR_BOTH);
749214501Srpaulo		if (!sm->eap_if->portEnabled || !sm->operEdge)
750214501Srpaulo			SM_ENTER(CTRL_DIR, FORCE_BOTH);
751214501Srpaulo		break;
752214501Srpaulo	}
753214501Srpaulo}
754214501Srpaulo
755214501Srpaulo
756214501Srpaulo
757214501Srpaulostruct eapol_state_machine *
758214501Srpauloeapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
759252726Srpaulo		 int flags, const struct wpabuf *assoc_wps_ie,
760252726Srpaulo		 const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
761252726Srpaulo		 const char *identity, const char *radius_cui)
762214501Srpaulo{
763214501Srpaulo	struct eapol_state_machine *sm;
764214501Srpaulo	struct eap_config eap_conf;
765214501Srpaulo
766214501Srpaulo	if (eapol == NULL)
767214501Srpaulo		return NULL;
768214501Srpaulo
769214501Srpaulo	sm = os_zalloc(sizeof(*sm));
770214501Srpaulo	if (sm == NULL) {
771214501Srpaulo		wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation "
772214501Srpaulo			   "failed");
773214501Srpaulo		return NULL;
774214501Srpaulo	}
775214501Srpaulo	sm->radius_identifier = -1;
776214501Srpaulo	os_memcpy(sm->addr, addr, ETH_ALEN);
777214501Srpaulo	sm->flags = flags;
778214501Srpaulo
779214501Srpaulo	sm->eapol = eapol;
780214501Srpaulo	sm->sta = sta_ctx;
781214501Srpaulo
782214501Srpaulo	/* Set default values for state machine constants */
783214501Srpaulo	sm->auth_pae_state = AUTH_PAE_INITIALIZE;
784214501Srpaulo	sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod;
785214501Srpaulo	sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax;
786214501Srpaulo
787214501Srpaulo	sm->be_auth_state = BE_AUTH_INITIALIZE;
788214501Srpaulo	sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout;
789214501Srpaulo
790214501Srpaulo	sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE;
791214501Srpaulo	sm->reAuthPeriod = eapol->conf.eap_reauth_period;
792214501Srpaulo	sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE;
793214501Srpaulo
794214501Srpaulo	sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT;
795214501Srpaulo
796214501Srpaulo	sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE;
797214501Srpaulo
798214501Srpaulo	sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH;
799214501Srpaulo
800214501Srpaulo	sm->portControl = Auto;
801214501Srpaulo
802214501Srpaulo	if (!eapol->conf.wpa &&
803214501Srpaulo	    (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0))
804214501Srpaulo		sm->keyTxEnabled = TRUE;
805214501Srpaulo	else
806214501Srpaulo		sm->keyTxEnabled = FALSE;
807214501Srpaulo	if (eapol->conf.wpa)
808214501Srpaulo		sm->portValid = FALSE;
809214501Srpaulo	else
810214501Srpaulo		sm->portValid = TRUE;
811214501Srpaulo
812214501Srpaulo	os_memset(&eap_conf, 0, sizeof(eap_conf));
813214501Srpaulo	eap_conf.eap_server = eapol->conf.eap_server;
814214501Srpaulo	eap_conf.ssl_ctx = eapol->conf.ssl_ctx;
815214501Srpaulo	eap_conf.msg_ctx = eapol->conf.msg_ctx;
816214501Srpaulo	eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv;
817214501Srpaulo	eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key;
818214501Srpaulo	eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id;
819214501Srpaulo	eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len;
820214501Srpaulo	eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info;
821214501Srpaulo	eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov;
822214501Srpaulo	eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime;
823214501Srpaulo	eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time;
824214501Srpaulo	eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind;
825214501Srpaulo	eap_conf.tnc = eapol->conf.tnc;
826214501Srpaulo	eap_conf.wps = eapol->conf.wps;
827214501Srpaulo	eap_conf.assoc_wps_ie = assoc_wps_ie;
828252726Srpaulo	eap_conf.assoc_p2p_ie = assoc_p2p_ie;
829214501Srpaulo	eap_conf.peer_addr = addr;
830252726Srpaulo	eap_conf.fragment_size = eapol->conf.fragment_size;
831252726Srpaulo	eap_conf.pwd_group = eapol->conf.pwd_group;
832252726Srpaulo	eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
833214501Srpaulo	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
834214501Srpaulo	if (sm->eap == NULL) {
835214501Srpaulo		eapol_auth_free(sm);
836214501Srpaulo		return NULL;
837214501Srpaulo	}
838214501Srpaulo	sm->eap_if = eap_get_interface(sm->eap);
839214501Srpaulo
840214501Srpaulo	eapol_auth_initialize(sm);
841214501Srpaulo
842252726Srpaulo	if (identity) {
843252726Srpaulo		sm->identity = (u8 *) os_strdup(identity);
844252726Srpaulo		if (sm->identity)
845252726Srpaulo			sm->identity_len = os_strlen(identity);
846252726Srpaulo	}
847252726Srpaulo	if (radius_cui)
848252726Srpaulo		sm->radius_cui = wpabuf_alloc_copy(radius_cui,
849252726Srpaulo						   os_strlen(radius_cui));
850252726Srpaulo
851214501Srpaulo	return sm;
852214501Srpaulo}
853214501Srpaulo
854214501Srpaulo
855214501Srpaulovoid eapol_auth_free(struct eapol_state_machine *sm)
856214501Srpaulo{
857214501Srpaulo	if (sm == NULL)
858214501Srpaulo		return;
859214501Srpaulo
860214501Srpaulo	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
861214501Srpaulo	eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL);
862214501Srpaulo	if (sm->eap)
863214501Srpaulo		eap_server_sm_deinit(sm->eap);
864214501Srpaulo	os_free(sm);
865214501Srpaulo}
866214501Srpaulo
867214501Srpaulo
868214501Srpaulostatic int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol,
869214501Srpaulo				    const u8 *addr)
870214501Srpaulo{
871214501Srpaulo	return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr);
872214501Srpaulo}
873214501Srpaulo
874214501Srpaulo
875214501Srpaulostatic void eapol_sm_step_run(struct eapol_state_machine *sm)
876214501Srpaulo{
877214501Srpaulo	struct eapol_authenticator *eapol = sm->eapol;
878214501Srpaulo	u8 addr[ETH_ALEN];
879214501Srpaulo	unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer,
880214501Srpaulo		prev_auth_key_tx, prev_key_rx, prev_ctrl_dir;
881214501Srpaulo	int max_steps = 100;
882214501Srpaulo
883214501Srpaulo	os_memcpy(addr, sm->addr, ETH_ALEN);
884214501Srpaulo
885214501Srpaulo	/*
886214501Srpaulo	 * Allow EAPOL state machines to run as long as there are state
887214501Srpaulo	 * changes, but exit and return here through event loop if more than
888214501Srpaulo	 * 100 steps is needed as a precaution against infinite loops inside
889214501Srpaulo	 * eloop callback.
890214501Srpaulo	 */
891214501Srpaulorestart:
892214501Srpaulo	prev_auth_pae = sm->auth_pae_state;
893214501Srpaulo	prev_be_auth = sm->be_auth_state;
894214501Srpaulo	prev_reauth_timer = sm->reauth_timer_state;
895214501Srpaulo	prev_auth_key_tx = sm->auth_key_tx_state;
896214501Srpaulo	prev_key_rx = sm->key_rx_state;
897214501Srpaulo	prev_ctrl_dir = sm->ctrl_dir_state;
898214501Srpaulo
899214501Srpaulo	SM_STEP_RUN(AUTH_PAE);
900214501Srpaulo	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
901214501Srpaulo		SM_STEP_RUN(BE_AUTH);
902214501Srpaulo	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
903214501Srpaulo		SM_STEP_RUN(REAUTH_TIMER);
904214501Srpaulo	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
905214501Srpaulo		SM_STEP_RUN(AUTH_KEY_TX);
906214501Srpaulo	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
907214501Srpaulo		SM_STEP_RUN(KEY_RX);
908214501Srpaulo	if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
909214501Srpaulo		SM_STEP_RUN(CTRL_DIR);
910214501Srpaulo
911214501Srpaulo	if (prev_auth_pae != sm->auth_pae_state ||
912214501Srpaulo	    prev_be_auth != sm->be_auth_state ||
913214501Srpaulo	    prev_reauth_timer != sm->reauth_timer_state ||
914214501Srpaulo	    prev_auth_key_tx != sm->auth_key_tx_state ||
915214501Srpaulo	    prev_key_rx != sm->key_rx_state ||
916214501Srpaulo	    prev_ctrl_dir != sm->ctrl_dir_state) {
917214501Srpaulo		if (--max_steps > 0)
918214501Srpaulo			goto restart;
919214501Srpaulo		/* Re-run from eloop timeout */
920214501Srpaulo		eapol_auth_step(sm);
921214501Srpaulo		return;
922214501Srpaulo	}
923214501Srpaulo
924214501Srpaulo	if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) {
925214501Srpaulo		if (eap_server_sm_step(sm->eap)) {
926214501Srpaulo			if (--max_steps > 0)
927214501Srpaulo				goto restart;
928214501Srpaulo			/* Re-run from eloop timeout */
929214501Srpaulo			eapol_auth_step(sm);
930214501Srpaulo			return;
931214501Srpaulo		}
932214501Srpaulo
933214501Srpaulo		/* TODO: find a better location for this */
934214501Srpaulo		if (sm->eap_if->aaaEapResp) {
935214501Srpaulo			sm->eap_if->aaaEapResp = FALSE;
936214501Srpaulo			if (sm->eap_if->aaaEapRespData == NULL) {
937214501Srpaulo				wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, "
938214501Srpaulo					   "but no aaaEapRespData available");
939214501Srpaulo				return;
940214501Srpaulo			}
941214501Srpaulo			sm->eapol->cb.aaa_send(
942214501Srpaulo				sm->eapol->conf.ctx, sm->sta,
943214501Srpaulo				wpabuf_head(sm->eap_if->aaaEapRespData),
944214501Srpaulo				wpabuf_len(sm->eap_if->aaaEapRespData));
945214501Srpaulo		}
946214501Srpaulo	}
947214501Srpaulo
948214501Srpaulo	if (eapol_sm_sta_entry_alive(eapol, addr))
949214501Srpaulo		sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
950214501Srpaulo					  EAPOL_AUTH_SM_CHANGE);
951214501Srpaulo}
952214501Srpaulo
953214501Srpaulo
954214501Srpaulostatic void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx)
955214501Srpaulo{
956214501Srpaulo	struct eapol_state_machine *sm = eloop_ctx;
957214501Srpaulo	eapol_sm_step_run(sm);
958214501Srpaulo}
959214501Srpaulo
960214501Srpaulo
961214501Srpaulo/**
962214501Srpaulo * eapol_auth_step - Advance EAPOL state machines
963214501Srpaulo * @sm: EAPOL state machine
964214501Srpaulo *
965214501Srpaulo * This function is called to advance EAPOL state machines after any change
966214501Srpaulo * that could affect their state.
967214501Srpaulo */
968214501Srpaulovoid eapol_auth_step(struct eapol_state_machine *sm)
969214501Srpaulo{
970214501Srpaulo	/*
971214501Srpaulo	 * Run eapol_sm_step_run from a registered timeout to make sure that
972214501Srpaulo	 * other possible timeouts/events are processed and to avoid long
973214501Srpaulo	 * function call chains.
974214501Srpaulo	 */
975214501Srpaulo
976214501Srpaulo	eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL);
977214501Srpaulo}
978214501Srpaulo
979214501Srpaulo
980214501Srpaulostatic void eapol_auth_initialize(struct eapol_state_machine *sm)
981214501Srpaulo{
982214501Srpaulo	sm->initializing = TRUE;
983214501Srpaulo	/* Initialize the state machines by asserting initialize and then
984214501Srpaulo	 * deasserting it after one step */
985214501Srpaulo	sm->initialize = TRUE;
986214501Srpaulo	eapol_sm_step_run(sm);
987214501Srpaulo	sm->initialize = FALSE;
988214501Srpaulo	eapol_sm_step_run(sm);
989214501Srpaulo	sm->initializing = FALSE;
990214501Srpaulo
991214501Srpaulo	/* Start one second tick for port timers state machine */
992214501Srpaulo	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
993214501Srpaulo	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
994214501Srpaulo}
995214501Srpaulo
996214501Srpaulo
997214501Srpaulostatic int eapol_sm_get_eap_user(void *ctx, const u8 *identity,
998214501Srpaulo				 size_t identity_len, int phase2,
999214501Srpaulo				 struct eap_user *user)
1000214501Srpaulo{
1001214501Srpaulo	struct eapol_state_machine *sm = ctx;
1002214501Srpaulo	return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
1003214501Srpaulo					  identity_len, phase2, user);
1004214501Srpaulo}
1005214501Srpaulo
1006214501Srpaulo
1007214501Srpaulostatic const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len)
1008214501Srpaulo{
1009214501Srpaulo	struct eapol_state_machine *sm = ctx;
1010214501Srpaulo	*len = sm->eapol->conf.eap_req_id_text_len;
1011214501Srpaulo	return sm->eapol->conf.eap_req_id_text;
1012214501Srpaulo}
1013214501Srpaulo
1014214501Srpaulo
1015214501Srpaulostatic struct eapol_callbacks eapol_cb =
1016214501Srpaulo{
1017214501Srpaulo	eapol_sm_get_eap_user,
1018214501Srpaulo	eapol_sm_get_eap_req_id_text
1019214501Srpaulo};
1020214501Srpaulo
1021214501Srpaulo
1022214501Srpauloint eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
1023214501Srpaulo{
1024252726Srpaulo	if (sm == NULL || ctx == NULL || ctx != sm->eap)
1025214501Srpaulo		return -1;
1026214501Srpaulo
1027214501Srpaulo	eap_sm_pending_cb(sm->eap);
1028214501Srpaulo	eapol_auth_step(sm);
1029214501Srpaulo
1030214501Srpaulo	return 0;
1031214501Srpaulo}
1032214501Srpaulo
1033214501Srpaulo
1034214501Srpaulostatic int eapol_auth_conf_clone(struct eapol_auth_config *dst,
1035214501Srpaulo				 struct eapol_auth_config *src)
1036214501Srpaulo{
1037214501Srpaulo	dst->ctx = src->ctx;
1038214501Srpaulo	dst->eap_reauth_period = src->eap_reauth_period;
1039214501Srpaulo	dst->wpa = src->wpa;
1040214501Srpaulo	dst->individual_wep_key_len = src->individual_wep_key_len;
1041214501Srpaulo	dst->eap_server = src->eap_server;
1042214501Srpaulo	dst->ssl_ctx = src->ssl_ctx;
1043214501Srpaulo	dst->msg_ctx = src->msg_ctx;
1044214501Srpaulo	dst->eap_sim_db_priv = src->eap_sim_db_priv;
1045214501Srpaulo	os_free(dst->eap_req_id_text);
1046252726Srpaulo	dst->pwd_group = src->pwd_group;
1047252726Srpaulo	dst->pbc_in_m1 = src->pbc_in_m1;
1048214501Srpaulo	if (src->eap_req_id_text) {
1049214501Srpaulo		dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
1050214501Srpaulo		if (dst->eap_req_id_text == NULL)
1051214501Srpaulo			return -1;
1052214501Srpaulo		os_memcpy(dst->eap_req_id_text, src->eap_req_id_text,
1053214501Srpaulo			  src->eap_req_id_text_len);
1054214501Srpaulo		dst->eap_req_id_text_len = src->eap_req_id_text_len;
1055214501Srpaulo	} else {
1056214501Srpaulo		dst->eap_req_id_text = NULL;
1057214501Srpaulo		dst->eap_req_id_text_len = 0;
1058214501Srpaulo	}
1059214501Srpaulo	if (src->pac_opaque_encr_key) {
1060214501Srpaulo		dst->pac_opaque_encr_key = os_malloc(16);
1061214501Srpaulo		os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
1062214501Srpaulo			  16);
1063214501Srpaulo	} else
1064214501Srpaulo		dst->pac_opaque_encr_key = NULL;
1065214501Srpaulo	if (src->eap_fast_a_id) {
1066214501Srpaulo		dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
1067214501Srpaulo		if (dst->eap_fast_a_id == NULL) {
1068214501Srpaulo			os_free(dst->eap_req_id_text);
1069214501Srpaulo			return -1;
1070214501Srpaulo		}
1071214501Srpaulo		os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
1072214501Srpaulo			  src->eap_fast_a_id_len);
1073214501Srpaulo		dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
1074214501Srpaulo	} else
1075214501Srpaulo		dst->eap_fast_a_id = NULL;
1076214501Srpaulo	if (src->eap_fast_a_id_info) {
1077214501Srpaulo		dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
1078214501Srpaulo		if (dst->eap_fast_a_id_info == NULL) {
1079214501Srpaulo			os_free(dst->eap_req_id_text);
1080214501Srpaulo			os_free(dst->eap_fast_a_id);
1081214501Srpaulo			return -1;
1082214501Srpaulo		}
1083214501Srpaulo	} else
1084214501Srpaulo		dst->eap_fast_a_id_info = NULL;
1085214501Srpaulo	dst->eap_fast_prov = src->eap_fast_prov;
1086214501Srpaulo	dst->pac_key_lifetime = src->pac_key_lifetime;
1087214501Srpaulo	dst->pac_key_refresh_time = src->pac_key_refresh_time;
1088214501Srpaulo	dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
1089214501Srpaulo	dst->tnc = src->tnc;
1090214501Srpaulo	dst->wps = src->wps;
1091252726Srpaulo	dst->fragment_size = src->fragment_size;
1092214501Srpaulo	return 0;
1093214501Srpaulo}
1094214501Srpaulo
1095214501Srpaulo
1096214501Srpaulostatic void eapol_auth_conf_free(struct eapol_auth_config *conf)
1097214501Srpaulo{
1098214501Srpaulo	os_free(conf->eap_req_id_text);
1099214501Srpaulo	conf->eap_req_id_text = NULL;
1100214501Srpaulo	os_free(conf->pac_opaque_encr_key);
1101214501Srpaulo	conf->pac_opaque_encr_key = NULL;
1102214501Srpaulo	os_free(conf->eap_fast_a_id);
1103214501Srpaulo	conf->eap_fast_a_id = NULL;
1104214501Srpaulo	os_free(conf->eap_fast_a_id_info);
1105214501Srpaulo	conf->eap_fast_a_id_info = NULL;
1106214501Srpaulo}
1107214501Srpaulo
1108214501Srpaulo
1109214501Srpaulostruct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
1110214501Srpaulo					     struct eapol_auth_cb *cb)
1111214501Srpaulo{
1112214501Srpaulo	struct eapol_authenticator *eapol;
1113214501Srpaulo
1114214501Srpaulo	eapol = os_zalloc(sizeof(*eapol));
1115214501Srpaulo	if (eapol == NULL)
1116214501Srpaulo		return NULL;
1117214501Srpaulo
1118214501Srpaulo	if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) {
1119214501Srpaulo		os_free(eapol);
1120214501Srpaulo		return NULL;
1121214501Srpaulo	}
1122214501Srpaulo
1123214501Srpaulo	if (conf->individual_wep_key_len > 0) {
1124214501Srpaulo		/* use key0 in individual key and key1 in broadcast key */
1125214501Srpaulo		eapol->default_wep_key_idx = 1;
1126214501Srpaulo	}
1127214501Srpaulo
1128214501Srpaulo	eapol->cb.eapol_send = cb->eapol_send;
1129214501Srpaulo	eapol->cb.aaa_send = cb->aaa_send;
1130214501Srpaulo	eapol->cb.finished = cb->finished;
1131214501Srpaulo	eapol->cb.get_eap_user = cb->get_eap_user;
1132214501Srpaulo	eapol->cb.sta_entry_alive = cb->sta_entry_alive;
1133214501Srpaulo	eapol->cb.logger = cb->logger;
1134214501Srpaulo	eapol->cb.set_port_authorized = cb->set_port_authorized;
1135214501Srpaulo	eapol->cb.abort_auth = cb->abort_auth;
1136214501Srpaulo	eapol->cb.tx_key = cb->tx_key;
1137214501Srpaulo	eapol->cb.eapol_event = cb->eapol_event;
1138214501Srpaulo
1139214501Srpaulo	return eapol;
1140214501Srpaulo}
1141214501Srpaulo
1142214501Srpaulo
1143214501Srpaulovoid eapol_auth_deinit(struct eapol_authenticator *eapol)
1144214501Srpaulo{
1145214501Srpaulo	if (eapol == NULL)
1146214501Srpaulo		return;
1147214501Srpaulo
1148214501Srpaulo	eapol_auth_conf_free(&eapol->conf);
1149214501Srpaulo	os_free(eapol->default_wep_key);
1150214501Srpaulo	os_free(eapol);
1151214501Srpaulo}
1152