1189251Ssam/*
2189251Ssam * EAPOL supplicant state machines
3189251Ssam * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam
17189251Ssam#include "common.h"
18189251Ssam#include "state_machine.h"
19189251Ssam#include "wpabuf.h"
20214734Srpaulo#include "eloop.h"
21214734Srpaulo#include "crypto/crypto.h"
22214734Srpaulo#include "crypto/md5.h"
23214734Srpaulo#include "common/eapol_common.h"
24214734Srpaulo#include "eap_peer/eap.h"
25214734Srpaulo#include "eapol_supp_sm.h"
26189251Ssam
27189251Ssam#define STATE_MACHINE_DATA struct eapol_sm
28189251Ssam#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
29189251Ssam
30189251Ssam
31189251Ssam/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
32189251Ssam
33189251Ssam/**
34189251Ssam * struct eapol_sm - Internal data for EAPOL state machines
35189251Ssam */
36189251Ssamstruct eapol_sm {
37189251Ssam	/* Timers */
38189251Ssam	unsigned int authWhile;
39189251Ssam	unsigned int heldWhile;
40189251Ssam	unsigned int startWhen;
41189251Ssam	unsigned int idleWhile; /* for EAP state machine */
42189251Ssam	int timer_tick_enabled;
43189251Ssam
44189251Ssam	/* Global variables */
45189251Ssam	Boolean eapFail;
46189251Ssam	Boolean eapolEap;
47189251Ssam	Boolean eapSuccess;
48189251Ssam	Boolean initialize;
49189251Ssam	Boolean keyDone;
50189251Ssam	Boolean keyRun;
51189251Ssam	PortControl portControl;
52189251Ssam	Boolean portEnabled;
53189251Ssam	PortStatus suppPortStatus;  /* dot1xSuppControlledPortStatus */
54189251Ssam	Boolean portValid;
55189251Ssam	Boolean suppAbort;
56189251Ssam	Boolean suppFail;
57189251Ssam	Boolean suppStart;
58189251Ssam	Boolean suppSuccess;
59189251Ssam	Boolean suppTimeout;
60189251Ssam
61189251Ssam	/* Supplicant PAE state machine */
62189251Ssam	enum {
63189251Ssam		SUPP_PAE_UNKNOWN = 0,
64189251Ssam		SUPP_PAE_DISCONNECTED = 1,
65189251Ssam		SUPP_PAE_LOGOFF = 2,
66189251Ssam		SUPP_PAE_CONNECTING = 3,
67189251Ssam		SUPP_PAE_AUTHENTICATING = 4,
68189251Ssam		SUPP_PAE_AUTHENTICATED = 5,
69189251Ssam		/* unused(6) */
70189251Ssam		SUPP_PAE_HELD = 7,
71189251Ssam		SUPP_PAE_RESTART = 8,
72189251Ssam		SUPP_PAE_S_FORCE_AUTH = 9,
73189251Ssam		SUPP_PAE_S_FORCE_UNAUTH = 10
74189251Ssam	} SUPP_PAE_state; /* dot1xSuppPaeState */
75189251Ssam	/* Variables */
76189251Ssam	Boolean userLogoff;
77189251Ssam	Boolean logoffSent;
78189251Ssam	unsigned int startCount;
79189251Ssam	Boolean eapRestart;
80189251Ssam	PortControl sPortMode;
81189251Ssam	/* Constants */
82189251Ssam	unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
83189251Ssam	unsigned int startPeriod; /* dot1xSuppStartPeriod */
84189251Ssam	unsigned int maxStart; /* dot1xSuppMaxStart */
85189251Ssam
86189251Ssam	/* Key Receive state machine */
87189251Ssam	enum {
88189251Ssam		KEY_RX_UNKNOWN = 0,
89189251Ssam		KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
90189251Ssam	} KEY_RX_state;
91189251Ssam	/* Variables */
92189251Ssam	Boolean rxKey;
93189251Ssam
94189251Ssam	/* Supplicant Backend state machine */
95189251Ssam	enum {
96189251Ssam		SUPP_BE_UNKNOWN = 0,
97189251Ssam		SUPP_BE_INITIALIZE = 1,
98189251Ssam		SUPP_BE_IDLE = 2,
99189251Ssam		SUPP_BE_REQUEST = 3,
100189251Ssam		SUPP_BE_RECEIVE = 4,
101189251Ssam		SUPP_BE_RESPONSE = 5,
102189251Ssam		SUPP_BE_FAIL = 6,
103189251Ssam		SUPP_BE_TIMEOUT = 7,
104189251Ssam		SUPP_BE_SUCCESS = 8
105189251Ssam	} SUPP_BE_state; /* dot1xSuppBackendPaeState */
106189251Ssam	/* Variables */
107189251Ssam	Boolean eapNoResp;
108189251Ssam	Boolean eapReq;
109189251Ssam	Boolean eapResp;
110189251Ssam	/* Constants */
111189251Ssam	unsigned int authPeriod; /* dot1xSuppAuthPeriod */
112189251Ssam
113189251Ssam	/* Statistics */
114189251Ssam	unsigned int dot1xSuppEapolFramesRx;
115189251Ssam	unsigned int dot1xSuppEapolFramesTx;
116189251Ssam	unsigned int dot1xSuppEapolStartFramesTx;
117189251Ssam	unsigned int dot1xSuppEapolLogoffFramesTx;
118189251Ssam	unsigned int dot1xSuppEapolRespFramesTx;
119189251Ssam	unsigned int dot1xSuppEapolReqIdFramesRx;
120189251Ssam	unsigned int dot1xSuppEapolReqFramesRx;
121189251Ssam	unsigned int dot1xSuppInvalidEapolFramesRx;
122189251Ssam	unsigned int dot1xSuppEapLengthErrorFramesRx;
123189251Ssam	unsigned int dot1xSuppLastEapolFrameVersion;
124189251Ssam	unsigned char dot1xSuppLastEapolFrameSource[6];
125189251Ssam
126189251Ssam	/* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
127189251Ssam	Boolean changed;
128189251Ssam	struct eap_sm *eap;
129189251Ssam	struct eap_peer_config *config;
130189251Ssam	Boolean initial_req;
131189251Ssam	u8 *last_rx_key;
132189251Ssam	size_t last_rx_key_len;
133189251Ssam	struct wpabuf *eapReqData; /* for EAP */
134189251Ssam	Boolean altAccept; /* for EAP */
135189251Ssam	Boolean altReject; /* for EAP */
136189251Ssam	Boolean replay_counter_valid;
137189251Ssam	u8 last_replay_counter[16];
138189251Ssam	struct eapol_config conf;
139189251Ssam	struct eapol_ctx *ctx;
140189251Ssam	enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
141189251Ssam		cb_status;
142189251Ssam	Boolean cached_pmk;
143189251Ssam
144189251Ssam	Boolean unicast_key_received, broadcast_key_received;
145189251Ssam};
146189251Ssam
147189251Ssam
148189251Ssam#define IEEE8021X_REPLAY_COUNTER_LEN 8
149189251Ssam#define IEEE8021X_KEY_SIGN_LEN 16
150189251Ssam#define IEEE8021X_KEY_IV_LEN 16
151189251Ssam
152189251Ssam#define IEEE8021X_KEY_INDEX_FLAG 0x80
153189251Ssam#define IEEE8021X_KEY_INDEX_MASK 0x03
154189251Ssam
155189251Ssam#ifdef _MSC_VER
156189251Ssam#pragma pack(push, 1)
157189251Ssam#endif /* _MSC_VER */
158189251Ssam
159189251Ssamstruct ieee802_1x_eapol_key {
160189251Ssam	u8 type;
161189251Ssam	/* Note: key_length is unaligned */
162189251Ssam	u8 key_length[2];
163189251Ssam	/* does not repeat within the life of the keying material used to
164189251Ssam	 * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
165189251Ssam	u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
166189251Ssam	u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
167189251Ssam	u8 key_index; /* key flag in the most significant bit:
168189251Ssam		       * 0 = broadcast (default key),
169189251Ssam		       * 1 = unicast (key mapping key); key index is in the
170189251Ssam		       * 7 least significant bits */
171189251Ssam	/* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
172189251Ssam	 * the key */
173189251Ssam	u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
174189251Ssam
175189251Ssam	/* followed by key: if packet body length = 44 + key length, then the
176189251Ssam	 * key field (of key_length bytes) contains the key in encrypted form;
177189251Ssam	 * if packet body length = 44, key field is absent and key_length
178189251Ssam	 * represents the number of least significant octets from
179189251Ssam	 * MS-MPPE-Send-Key attribute to be used as the keying material;
180189251Ssam	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
181189251Ssam} STRUCT_PACKED;
182189251Ssam
183189251Ssam#ifdef _MSC_VER
184189251Ssam#pragma pack(pop)
185189251Ssam#endif /* _MSC_VER */
186189251Ssam
187189251Ssam
188189251Ssamstatic void eapol_sm_txLogoff(struct eapol_sm *sm);
189189251Ssamstatic void eapol_sm_txStart(struct eapol_sm *sm);
190189251Ssamstatic void eapol_sm_processKey(struct eapol_sm *sm);
191189251Ssamstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm);
192189251Ssamstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm);
193189251Ssamstatic void eapol_sm_abortSupp(struct eapol_sm *sm);
194189251Ssamstatic void eapol_sm_abort_cached(struct eapol_sm *sm);
195189251Ssamstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
196214734Srpaulostatic void eapol_sm_set_port_authorized(struct eapol_sm *sm);
197214734Srpaulostatic void eapol_sm_set_port_unauthorized(struct eapol_sm *sm);
198189251Ssam
199189251Ssam
200189251Ssam/* Port Timers state machine - implemented as a function that will be called
201189251Ssam * once a second as a registered event loop timeout */
202189251Ssamstatic void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
203189251Ssam{
204189251Ssam	struct eapol_sm *sm = timeout_ctx;
205189251Ssam
206189251Ssam	if (sm->authWhile > 0) {
207189251Ssam		sm->authWhile--;
208189251Ssam		if (sm->authWhile == 0)
209189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
210189251Ssam	}
211189251Ssam	if (sm->heldWhile > 0) {
212189251Ssam		sm->heldWhile--;
213189251Ssam		if (sm->heldWhile == 0)
214189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
215189251Ssam	}
216189251Ssam	if (sm->startWhen > 0) {
217189251Ssam		sm->startWhen--;
218189251Ssam		if (sm->startWhen == 0)
219189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
220189251Ssam	}
221189251Ssam	if (sm->idleWhile > 0) {
222189251Ssam		sm->idleWhile--;
223189251Ssam		if (sm->idleWhile == 0)
224189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
225189251Ssam	}
226189251Ssam
227189251Ssam	if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
228189251Ssam		eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
229189251Ssam				       sm);
230189251Ssam	} else {
231189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
232189251Ssam		sm->timer_tick_enabled = 0;
233189251Ssam	}
234189251Ssam	eapol_sm_step(sm);
235189251Ssam}
236189251Ssam
237189251Ssam
238189251Ssamstatic void eapol_enable_timer_tick(struct eapol_sm *sm)
239189251Ssam{
240189251Ssam	if (sm->timer_tick_enabled)
241189251Ssam		return;
242189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
243189251Ssam	sm->timer_tick_enabled = 1;
244189251Ssam	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
245189251Ssam	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
246189251Ssam}
247189251Ssam
248189251Ssam
249189251SsamSM_STATE(SUPP_PAE, LOGOFF)
250189251Ssam{
251189251Ssam	SM_ENTRY(SUPP_PAE, LOGOFF);
252189251Ssam	eapol_sm_txLogoff(sm);
253189251Ssam	sm->logoffSent = TRUE;
254189251Ssam	sm->suppPortStatus = Unauthorized;
255214734Srpaulo	eapol_sm_set_port_unauthorized(sm);
256189251Ssam}
257189251Ssam
258189251Ssam
259189251SsamSM_STATE(SUPP_PAE, DISCONNECTED)
260189251Ssam{
261189251Ssam	SM_ENTRY(SUPP_PAE, DISCONNECTED);
262189251Ssam	sm->sPortMode = Auto;
263189251Ssam	sm->startCount = 0;
264189251Ssam	sm->logoffSent = FALSE;
265189251Ssam	sm->suppPortStatus = Unauthorized;
266214734Srpaulo	eapol_sm_set_port_unauthorized(sm);
267189251Ssam	sm->suppAbort = TRUE;
268189251Ssam
269189251Ssam	sm->unicast_key_received = FALSE;
270189251Ssam	sm->broadcast_key_received = FALSE;
271189251Ssam}
272189251Ssam
273189251Ssam
274189251SsamSM_STATE(SUPP_PAE, CONNECTING)
275189251Ssam{
276189251Ssam	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
277189251Ssam	SM_ENTRY(SUPP_PAE, CONNECTING);
278189251Ssam	if (send_start) {
279189251Ssam		sm->startWhen = sm->startPeriod;
280189251Ssam		sm->startCount++;
281189251Ssam	} else {
282189251Ssam		/*
283189251Ssam		 * Do not send EAPOL-Start immediately since in most cases,
284189251Ssam		 * Authenticator is going to start authentication immediately
285189251Ssam		 * after association and an extra EAPOL-Start is just going to
286189251Ssam		 * delay authentication. Use a short timeout to send the first
287189251Ssam		 * EAPOL-Start if Authenticator does not start authentication.
288189251Ssam		 */
289209158Srpaulo#ifdef CONFIG_WPS
290209158Srpaulo		/* Reduce latency on starting WPS negotiation. */
291209158Srpaulo		sm->startWhen = 1;
292209158Srpaulo#else /* CONFIG_WPS */
293189251Ssam		sm->startWhen = 3;
294209158Srpaulo#endif /* CONFIG_WPS */
295189251Ssam	}
296189251Ssam	eapol_enable_timer_tick(sm);
297189251Ssam	sm->eapolEap = FALSE;
298189251Ssam	if (send_start)
299189251Ssam		eapol_sm_txStart(sm);
300189251Ssam}
301189251Ssam
302189251Ssam
303189251SsamSM_STATE(SUPP_PAE, AUTHENTICATING)
304189251Ssam{
305189251Ssam	SM_ENTRY(SUPP_PAE, AUTHENTICATING);
306189251Ssam	sm->startCount = 0;
307189251Ssam	sm->suppSuccess = FALSE;
308189251Ssam	sm->suppFail = FALSE;
309189251Ssam	sm->suppTimeout = FALSE;
310189251Ssam	sm->keyRun = FALSE;
311189251Ssam	sm->keyDone = FALSE;
312189251Ssam	sm->suppStart = TRUE;
313189251Ssam}
314189251Ssam
315189251Ssam
316189251SsamSM_STATE(SUPP_PAE, HELD)
317189251Ssam{
318189251Ssam	SM_ENTRY(SUPP_PAE, HELD);
319189251Ssam	sm->heldWhile = sm->heldPeriod;
320189251Ssam	eapol_enable_timer_tick(sm);
321189251Ssam	sm->suppPortStatus = Unauthorized;
322214734Srpaulo	eapol_sm_set_port_unauthorized(sm);
323189251Ssam	sm->cb_status = EAPOL_CB_FAILURE;
324189251Ssam}
325189251Ssam
326189251Ssam
327189251SsamSM_STATE(SUPP_PAE, AUTHENTICATED)
328189251Ssam{
329189251Ssam	SM_ENTRY(SUPP_PAE, AUTHENTICATED);
330189251Ssam	sm->suppPortStatus = Authorized;
331214734Srpaulo	eapol_sm_set_port_authorized(sm);
332189251Ssam	sm->cb_status = EAPOL_CB_SUCCESS;
333189251Ssam}
334189251Ssam
335189251Ssam
336189251SsamSM_STATE(SUPP_PAE, RESTART)
337189251Ssam{
338189251Ssam	SM_ENTRY(SUPP_PAE, RESTART);
339189251Ssam	sm->eapRestart = TRUE;
340189251Ssam}
341189251Ssam
342189251Ssam
343189251SsamSM_STATE(SUPP_PAE, S_FORCE_AUTH)
344189251Ssam{
345189251Ssam	SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
346189251Ssam	sm->suppPortStatus = Authorized;
347214734Srpaulo	eapol_sm_set_port_authorized(sm);
348189251Ssam	sm->sPortMode = ForceAuthorized;
349189251Ssam}
350189251Ssam
351189251Ssam
352189251SsamSM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
353189251Ssam{
354189251Ssam	SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
355189251Ssam	sm->suppPortStatus = Unauthorized;
356214734Srpaulo	eapol_sm_set_port_unauthorized(sm);
357189251Ssam	sm->sPortMode = ForceUnauthorized;
358189251Ssam	eapol_sm_txLogoff(sm);
359189251Ssam}
360189251Ssam
361189251Ssam
362189251SsamSM_STEP(SUPP_PAE)
363189251Ssam{
364189251Ssam	if ((sm->userLogoff && !sm->logoffSent) &&
365189251Ssam	    !(sm->initialize || !sm->portEnabled))
366189251Ssam		SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
367189251Ssam	else if (((sm->portControl == Auto) &&
368189251Ssam		  (sm->sPortMode != sm->portControl)) ||
369189251Ssam		 sm->initialize || !sm->portEnabled)
370189251Ssam		SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
371189251Ssam	else if ((sm->portControl == ForceAuthorized) &&
372189251Ssam		 (sm->sPortMode != sm->portControl) &&
373189251Ssam		 !(sm->initialize || !sm->portEnabled))
374189251Ssam		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
375189251Ssam	else if ((sm->portControl == ForceUnauthorized) &&
376189251Ssam		 (sm->sPortMode != sm->portControl) &&
377189251Ssam		 !(sm->initialize || !sm->portEnabled))
378189251Ssam		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
379189251Ssam	else switch (sm->SUPP_PAE_state) {
380189251Ssam	case SUPP_PAE_UNKNOWN:
381189251Ssam		break;
382189251Ssam	case SUPP_PAE_LOGOFF:
383189251Ssam		if (!sm->userLogoff)
384189251Ssam			SM_ENTER(SUPP_PAE, DISCONNECTED);
385189251Ssam		break;
386189251Ssam	case SUPP_PAE_DISCONNECTED:
387189251Ssam		SM_ENTER(SUPP_PAE, CONNECTING);
388189251Ssam		break;
389189251Ssam	case SUPP_PAE_CONNECTING:
390189251Ssam		if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
391189251Ssam			SM_ENTER(SUPP_PAE, CONNECTING);
392189251Ssam		else if (sm->startWhen == 0 &&
393189251Ssam			 sm->startCount >= sm->maxStart &&
394189251Ssam			 sm->portValid)
395189251Ssam			SM_ENTER(SUPP_PAE, AUTHENTICATED);
396189251Ssam		else if (sm->eapSuccess || sm->eapFail)
397189251Ssam			SM_ENTER(SUPP_PAE, AUTHENTICATING);
398189251Ssam		else if (sm->eapolEap)
399189251Ssam			SM_ENTER(SUPP_PAE, RESTART);
400189251Ssam		else if (sm->startWhen == 0 &&
401189251Ssam			 sm->startCount >= sm->maxStart &&
402189251Ssam			 !sm->portValid)
403189251Ssam			SM_ENTER(SUPP_PAE, HELD);
404189251Ssam		break;
405189251Ssam	case SUPP_PAE_AUTHENTICATING:
406189251Ssam		if (sm->eapSuccess && !sm->portValid &&
407189251Ssam		    sm->conf.accept_802_1x_keys &&
408189251Ssam		    sm->conf.required_keys == 0) {
409189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
410189251Ssam				   "plaintext connection; no EAPOL-Key frames "
411189251Ssam				   "required");
412189251Ssam			sm->portValid = TRUE;
413189251Ssam			if (sm->ctx->eapol_done_cb)
414189251Ssam				sm->ctx->eapol_done_cb(sm->ctx->ctx);
415189251Ssam		}
416189251Ssam		if (sm->eapSuccess && sm->portValid)
417189251Ssam			SM_ENTER(SUPP_PAE, AUTHENTICATED);
418189251Ssam		else if (sm->eapFail || (sm->keyDone && !sm->portValid))
419189251Ssam			SM_ENTER(SUPP_PAE, HELD);
420189251Ssam		else if (sm->suppTimeout)
421189251Ssam			SM_ENTER(SUPP_PAE, CONNECTING);
422189251Ssam		break;
423189251Ssam	case SUPP_PAE_HELD:
424189251Ssam		if (sm->heldWhile == 0)
425189251Ssam			SM_ENTER(SUPP_PAE, CONNECTING);
426189251Ssam		else if (sm->eapolEap)
427189251Ssam			SM_ENTER(SUPP_PAE, RESTART);
428189251Ssam		break;
429189251Ssam	case SUPP_PAE_AUTHENTICATED:
430189251Ssam		if (sm->eapolEap && sm->portValid)
431189251Ssam			SM_ENTER(SUPP_PAE, RESTART);
432189251Ssam		else if (!sm->portValid)
433189251Ssam			SM_ENTER(SUPP_PAE, DISCONNECTED);
434189251Ssam		break;
435189251Ssam	case SUPP_PAE_RESTART:
436189251Ssam		if (!sm->eapRestart)
437189251Ssam			SM_ENTER(SUPP_PAE, AUTHENTICATING);
438189251Ssam		break;
439189251Ssam	case SUPP_PAE_S_FORCE_AUTH:
440189251Ssam		break;
441189251Ssam	case SUPP_PAE_S_FORCE_UNAUTH:
442189251Ssam		break;
443189251Ssam	}
444189251Ssam}
445189251Ssam
446189251Ssam
447189251SsamSM_STATE(KEY_RX, NO_KEY_RECEIVE)
448189251Ssam{
449189251Ssam	SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
450189251Ssam}
451189251Ssam
452189251Ssam
453189251SsamSM_STATE(KEY_RX, KEY_RECEIVE)
454189251Ssam{
455189251Ssam	SM_ENTRY(KEY_RX, KEY_RECEIVE);
456189251Ssam	eapol_sm_processKey(sm);
457189251Ssam	sm->rxKey = FALSE;
458189251Ssam}
459189251Ssam
460189251Ssam
461189251SsamSM_STEP(KEY_RX)
462189251Ssam{
463189251Ssam	if (sm->initialize || !sm->portEnabled)
464189251Ssam		SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
465189251Ssam	switch (sm->KEY_RX_state) {
466189251Ssam	case KEY_RX_UNKNOWN:
467189251Ssam		break;
468189251Ssam	case KEY_RX_NO_KEY_RECEIVE:
469189251Ssam		if (sm->rxKey)
470189251Ssam			SM_ENTER(KEY_RX, KEY_RECEIVE);
471189251Ssam		break;
472189251Ssam	case KEY_RX_KEY_RECEIVE:
473189251Ssam		if (sm->rxKey)
474189251Ssam			SM_ENTER(KEY_RX, KEY_RECEIVE);
475189251Ssam		break;
476189251Ssam	}
477189251Ssam}
478189251Ssam
479189251Ssam
480189251SsamSM_STATE(SUPP_BE, REQUEST)
481189251Ssam{
482189251Ssam	SM_ENTRY(SUPP_BE, REQUEST);
483189251Ssam	sm->authWhile = 0;
484189251Ssam	sm->eapReq = TRUE;
485189251Ssam	eapol_sm_getSuppRsp(sm);
486189251Ssam}
487189251Ssam
488189251Ssam
489189251SsamSM_STATE(SUPP_BE, RESPONSE)
490189251Ssam{
491189251Ssam	SM_ENTRY(SUPP_BE, RESPONSE);
492189251Ssam	eapol_sm_txSuppRsp(sm);
493189251Ssam	sm->eapResp = FALSE;
494189251Ssam}
495189251Ssam
496189251Ssam
497189251SsamSM_STATE(SUPP_BE, SUCCESS)
498189251Ssam{
499189251Ssam	SM_ENTRY(SUPP_BE, SUCCESS);
500189251Ssam	sm->keyRun = TRUE;
501189251Ssam	sm->suppSuccess = TRUE;
502189251Ssam
503189251Ssam	if (eap_key_available(sm->eap)) {
504189251Ssam		/* New key received - clear IEEE 802.1X EAPOL-Key replay
505189251Ssam		 * counter */
506189251Ssam		sm->replay_counter_valid = FALSE;
507189251Ssam	}
508189251Ssam}
509189251Ssam
510189251Ssam
511189251SsamSM_STATE(SUPP_BE, FAIL)
512189251Ssam{
513189251Ssam	SM_ENTRY(SUPP_BE, FAIL);
514189251Ssam	sm->suppFail = TRUE;
515189251Ssam}
516189251Ssam
517189251Ssam
518189251SsamSM_STATE(SUPP_BE, TIMEOUT)
519189251Ssam{
520189251Ssam	SM_ENTRY(SUPP_BE, TIMEOUT);
521189251Ssam	sm->suppTimeout = TRUE;
522189251Ssam}
523189251Ssam
524189251Ssam
525189251SsamSM_STATE(SUPP_BE, IDLE)
526189251Ssam{
527189251Ssam	SM_ENTRY(SUPP_BE, IDLE);
528189251Ssam	sm->suppStart = FALSE;
529189251Ssam	sm->initial_req = TRUE;
530189251Ssam}
531189251Ssam
532189251Ssam
533189251SsamSM_STATE(SUPP_BE, INITIALIZE)
534189251Ssam{
535189251Ssam	SM_ENTRY(SUPP_BE, INITIALIZE);
536189251Ssam	eapol_sm_abortSupp(sm);
537189251Ssam	sm->suppAbort = FALSE;
538189251Ssam}
539189251Ssam
540189251Ssam
541189251SsamSM_STATE(SUPP_BE, RECEIVE)
542189251Ssam{
543189251Ssam	SM_ENTRY(SUPP_BE, RECEIVE);
544189251Ssam	sm->authWhile = sm->authPeriod;
545189251Ssam	eapol_enable_timer_tick(sm);
546189251Ssam	sm->eapolEap = FALSE;
547189251Ssam	sm->eapNoResp = FALSE;
548189251Ssam	sm->initial_req = FALSE;
549189251Ssam}
550189251Ssam
551189251Ssam
552189251SsamSM_STEP(SUPP_BE)
553189251Ssam{
554189251Ssam	if (sm->initialize || sm->suppAbort)
555189251Ssam		SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
556189251Ssam	else switch (sm->SUPP_BE_state) {
557189251Ssam	case SUPP_BE_UNKNOWN:
558189251Ssam		break;
559189251Ssam	case SUPP_BE_REQUEST:
560189251Ssam		/*
561189251Ssam		 * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
562189251Ssam		 * and SUCCESS based on eapFail and eapSuccess, respectively.
563189251Ssam		 * However, IEEE Std 802.1X-2004 is also specifying that
564189251Ssam		 * eapNoResp should be set in conjuction with eapSuccess and
565189251Ssam		 * eapFail which would mean that more than one of the
566189251Ssam		 * transitions here would be activated at the same time.
567189251Ssam		 * Skipping RESPONSE and/or RECEIVE states in these cases can
568189251Ssam		 * cause problems and the direct transitions to do not seem
569189251Ssam		 * correct. Because of this, the conditions for these
570189251Ssam		 * transitions are verified only after eapNoResp. They are
571189251Ssam		 * unlikely to be used since eapNoResp should always be set if
572189251Ssam		 * either of eapSuccess or eapFail is set.
573189251Ssam		 */
574189251Ssam		if (sm->eapResp && sm->eapNoResp) {
575189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
576189251Ssam				   "eapResp and eapNoResp set?!");
577189251Ssam		}
578189251Ssam		if (sm->eapResp)
579189251Ssam			SM_ENTER(SUPP_BE, RESPONSE);
580189251Ssam		else if (sm->eapNoResp)
581189251Ssam			SM_ENTER(SUPP_BE, RECEIVE);
582189251Ssam		else if (sm->eapFail)
583189251Ssam			SM_ENTER(SUPP_BE, FAIL);
584189251Ssam		else if (sm->eapSuccess)
585189251Ssam			SM_ENTER(SUPP_BE, SUCCESS);
586189251Ssam		break;
587189251Ssam	case SUPP_BE_RESPONSE:
588189251Ssam		SM_ENTER(SUPP_BE, RECEIVE);
589189251Ssam		break;
590189251Ssam	case SUPP_BE_SUCCESS:
591189251Ssam		SM_ENTER(SUPP_BE, IDLE);
592189251Ssam		break;
593189251Ssam	case SUPP_BE_FAIL:
594189251Ssam		SM_ENTER(SUPP_BE, IDLE);
595189251Ssam		break;
596189251Ssam	case SUPP_BE_TIMEOUT:
597189251Ssam		SM_ENTER(SUPP_BE, IDLE);
598189251Ssam		break;
599189251Ssam	case SUPP_BE_IDLE:
600189251Ssam		if (sm->eapFail && sm->suppStart)
601189251Ssam			SM_ENTER(SUPP_BE, FAIL);
602189251Ssam		else if (sm->eapolEap && sm->suppStart)
603189251Ssam			SM_ENTER(SUPP_BE, REQUEST);
604189251Ssam		else if (sm->eapSuccess && sm->suppStart)
605189251Ssam			SM_ENTER(SUPP_BE, SUCCESS);
606189251Ssam		break;
607189251Ssam	case SUPP_BE_INITIALIZE:
608189251Ssam		SM_ENTER(SUPP_BE, IDLE);
609189251Ssam		break;
610189251Ssam	case SUPP_BE_RECEIVE:
611189251Ssam		if (sm->eapolEap)
612189251Ssam			SM_ENTER(SUPP_BE, REQUEST);
613189251Ssam		else if (sm->eapFail)
614189251Ssam			SM_ENTER(SUPP_BE, FAIL);
615189251Ssam		else if (sm->authWhile == 0)
616189251Ssam			SM_ENTER(SUPP_BE, TIMEOUT);
617189251Ssam		else if (sm->eapSuccess)
618189251Ssam			SM_ENTER(SUPP_BE, SUCCESS);
619189251Ssam		break;
620189251Ssam	}
621189251Ssam}
622189251Ssam
623189251Ssam
624189251Ssamstatic void eapol_sm_txLogoff(struct eapol_sm *sm)
625189251Ssam{
626189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
627189251Ssam	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
628189251Ssam			    IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
629189251Ssam	sm->dot1xSuppEapolLogoffFramesTx++;
630189251Ssam	sm->dot1xSuppEapolFramesTx++;
631189251Ssam}
632189251Ssam
633189251Ssam
634189251Ssamstatic void eapol_sm_txStart(struct eapol_sm *sm)
635189251Ssam{
636189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: txStart");
637189251Ssam	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
638189251Ssam			    IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
639189251Ssam	sm->dot1xSuppEapolStartFramesTx++;
640189251Ssam	sm->dot1xSuppEapolFramesTx++;
641189251Ssam}
642189251Ssam
643189251Ssam
644189251Ssam#define IEEE8021X_ENCR_KEY_LEN 32
645189251Ssam#define IEEE8021X_SIGN_KEY_LEN 32
646189251Ssam
647189251Ssamstruct eap_key_data {
648189251Ssam	u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
649189251Ssam	u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
650189251Ssam};
651189251Ssam
652189251Ssam
653189251Ssamstatic void eapol_sm_processKey(struct eapol_sm *sm)
654189251Ssam{
655189251Ssam	struct ieee802_1x_hdr *hdr;
656189251Ssam	struct ieee802_1x_eapol_key *key;
657189251Ssam	struct eap_key_data keydata;
658189251Ssam	u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
659189251Ssam	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
660189251Ssam	int key_len, res, sign_key_len, encr_key_len;
661189251Ssam	u16 rx_key_length;
662189251Ssam
663189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: processKey");
664189251Ssam	if (sm->last_rx_key == NULL)
665189251Ssam		return;
666189251Ssam
667189251Ssam	if (!sm->conf.accept_802_1x_keys) {
668189251Ssam		wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
669189251Ssam			   " even though this was not accepted - "
670189251Ssam			   "ignoring this packet");
671189251Ssam		return;
672189251Ssam	}
673189251Ssam
674189251Ssam	hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
675189251Ssam	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
676189251Ssam	if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
677189251Ssam		wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
678189251Ssam		return;
679189251Ssam	}
680189251Ssam	rx_key_length = WPA_GET_BE16(key->key_length);
681189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
682189251Ssam		   "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
683189251Ssam		   hdr->version, hdr->type, be_to_host16(hdr->length),
684189251Ssam		   key->type, rx_key_length, key->key_index);
685189251Ssam
686189251Ssam	eapol_sm_notify_lower_layer_success(sm, 1);
687189251Ssam	sign_key_len = IEEE8021X_SIGN_KEY_LEN;
688189251Ssam	encr_key_len = IEEE8021X_ENCR_KEY_LEN;
689189251Ssam	res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
690189251Ssam	if (res < 0) {
691189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
692189251Ssam			   "decrypting EAPOL-Key keys");
693189251Ssam		return;
694189251Ssam	}
695189251Ssam	if (res == 16) {
696189251Ssam		/* LEAP derives only 16 bytes of keying material. */
697189251Ssam		res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
698189251Ssam		if (res) {
699189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
700189251Ssam				   "master key for decrypting EAPOL-Key keys");
701189251Ssam			return;
702189251Ssam		}
703189251Ssam		sign_key_len = 16;
704189251Ssam		encr_key_len = 16;
705189251Ssam		os_memcpy(keydata.sign_key, keydata.encr_key, 16);
706189251Ssam	} else if (res) {
707189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
708189251Ssam			   "data for decrypting EAPOL-Key keys (res=%d)", res);
709189251Ssam		return;
710189251Ssam	}
711189251Ssam
712189251Ssam	/* The key replay_counter must increase when same master key */
713189251Ssam	if (sm->replay_counter_valid &&
714189251Ssam	    os_memcmp(sm->last_replay_counter, key->replay_counter,
715189251Ssam		      IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
716189251Ssam		wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
717189251Ssam			   "not increase - ignoring key");
718189251Ssam		wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
719189251Ssam			    sm->last_replay_counter,
720189251Ssam			    IEEE8021X_REPLAY_COUNTER_LEN);
721189251Ssam		wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
722189251Ssam			    key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
723189251Ssam		return;
724189251Ssam	}
725189251Ssam
726189251Ssam	/* Verify key signature (HMAC-MD5) */
727189251Ssam	os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
728189251Ssam	os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
729189251Ssam	hmac_md5(keydata.sign_key, sign_key_len,
730189251Ssam		 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
731189251Ssam		 key->key_signature);
732189251Ssam	if (os_memcmp(orig_key_sign, key->key_signature,
733189251Ssam		      IEEE8021X_KEY_SIGN_LEN) != 0) {
734189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
735189251Ssam			   "EAPOL-Key packet");
736189251Ssam		os_memcpy(key->key_signature, orig_key_sign,
737189251Ssam			  IEEE8021X_KEY_SIGN_LEN);
738189251Ssam		return;
739189251Ssam	}
740189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
741189251Ssam
742189251Ssam	key_len = be_to_host16(hdr->length) - sizeof(*key);
743189251Ssam	if (key_len > 32 || rx_key_length > 32) {
744189251Ssam		wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
745189251Ssam			   key_len ? key_len : rx_key_length);
746189251Ssam		return;
747189251Ssam	}
748189251Ssam	if (key_len == rx_key_length) {
749189251Ssam		os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
750189251Ssam		os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
751189251Ssam			  encr_key_len);
752189251Ssam		os_memcpy(datakey, key + 1, key_len);
753209158Srpaulo		rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
754209158Srpaulo			 datakey, key_len);
755189251Ssam		wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
756189251Ssam				datakey, key_len);
757189251Ssam	} else if (key_len == 0) {
758189251Ssam		/*
759189251Ssam		 * IEEE 802.1X-2004 specifies that least significant Key Length
760189251Ssam		 * octets from MS-MPPE-Send-Key are used as the key if the key
761189251Ssam		 * data is not present. This seems to be meaning the beginning
762189251Ssam		 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
763189251Ssam		 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
764189251Ssam		 * Anyway, taking the beginning of the keying material from EAP
765189251Ssam		 * seems to interoperate with Authenticators.
766189251Ssam		 */
767189251Ssam		key_len = rx_key_length;
768189251Ssam		os_memcpy(datakey, keydata.encr_key, key_len);
769189251Ssam		wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
770189251Ssam				"material data encryption key",
771189251Ssam				datakey, key_len);
772189251Ssam	} else {
773189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
774189251Ssam			   "(key_length=%d)", key_len, rx_key_length);
775189251Ssam		return;
776189251Ssam	}
777189251Ssam
778189251Ssam	sm->replay_counter_valid = TRUE;
779189251Ssam	os_memcpy(sm->last_replay_counter, key->replay_counter,
780189251Ssam		  IEEE8021X_REPLAY_COUNTER_LEN);
781189251Ssam
782189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
783189251Ssam		   "len %d",
784189251Ssam		   key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
785189251Ssam		   "unicast" : "broadcast",
786189251Ssam		   key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
787189251Ssam
788189251Ssam	if (sm->ctx->set_wep_key &&
789189251Ssam	    sm->ctx->set_wep_key(sm->ctx->ctx,
790189251Ssam				 key->key_index & IEEE8021X_KEY_INDEX_FLAG,
791189251Ssam				 key->key_index & IEEE8021X_KEY_INDEX_MASK,
792189251Ssam				 datakey, key_len) < 0) {
793189251Ssam		wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
794189251Ssam			   " driver.");
795189251Ssam	} else {
796189251Ssam		if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
797189251Ssam			sm->unicast_key_received = TRUE;
798189251Ssam		else
799189251Ssam			sm->broadcast_key_received = TRUE;
800189251Ssam
801189251Ssam		if ((sm->unicast_key_received ||
802189251Ssam		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
803189251Ssam		    (sm->broadcast_key_received ||
804189251Ssam		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
805189251Ssam		{
806189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
807189251Ssam				   "frames received");
808189251Ssam			sm->portValid = TRUE;
809189251Ssam			if (sm->ctx->eapol_done_cb)
810189251Ssam				sm->ctx->eapol_done_cb(sm->ctx->ctx);
811189251Ssam		}
812189251Ssam	}
813189251Ssam}
814189251Ssam
815189251Ssam
816189251Ssamstatic void eapol_sm_getSuppRsp(struct eapol_sm *sm)
817189251Ssam{
818189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
819189251Ssam	/* EAP layer processing; no special code is needed, since Supplicant
820189251Ssam	 * Backend state machine is waiting for eapNoResp or eapResp to be set
821189251Ssam	 * and these are only set in the EAP state machine when the processing
822189251Ssam	 * has finished. */
823189251Ssam}
824189251Ssam
825189251Ssam
826189251Ssamstatic void eapol_sm_txSuppRsp(struct eapol_sm *sm)
827189251Ssam{
828189251Ssam	struct wpabuf *resp;
829189251Ssam
830189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
831189251Ssam	resp = eap_get_eapRespData(sm->eap);
832189251Ssam	if (resp == NULL) {
833189251Ssam		wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
834189251Ssam			   "not available");
835189251Ssam		return;
836189251Ssam	}
837189251Ssam
838189251Ssam	/* Send EAP-Packet from the EAP layer to the Authenticator */
839189251Ssam	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
840189251Ssam			    IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
841189251Ssam			    wpabuf_len(resp));
842189251Ssam
843189251Ssam	/* eapRespData is not used anymore, so free it here */
844189251Ssam	wpabuf_free(resp);
845189251Ssam
846189251Ssam	if (sm->initial_req)
847189251Ssam		sm->dot1xSuppEapolReqIdFramesRx++;
848189251Ssam	else
849189251Ssam		sm->dot1xSuppEapolReqFramesRx++;
850189251Ssam	sm->dot1xSuppEapolRespFramesTx++;
851189251Ssam	sm->dot1xSuppEapolFramesTx++;
852189251Ssam}
853189251Ssam
854189251Ssam
855189251Ssamstatic void eapol_sm_abortSupp(struct eapol_sm *sm)
856189251Ssam{
857189251Ssam	/* release system resources that may have been allocated for the
858189251Ssam	 * authentication session */
859189251Ssam	os_free(sm->last_rx_key);
860189251Ssam	sm->last_rx_key = NULL;
861189251Ssam	wpabuf_free(sm->eapReqData);
862189251Ssam	sm->eapReqData = NULL;
863189251Ssam	eap_sm_abort(sm->eap);
864189251Ssam}
865189251Ssam
866189251Ssam
867189251Ssamstatic void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
868189251Ssam{
869189251Ssam	eapol_sm_step(timeout_ctx);
870189251Ssam}
871189251Ssam
872189251Ssam
873214734Srpaulostatic void eapol_sm_set_port_authorized(struct eapol_sm *sm)
874214734Srpaulo{
875214734Srpaulo	if (sm->ctx->port_cb)
876214734Srpaulo		sm->ctx->port_cb(sm->ctx->ctx, 1);
877214734Srpaulo}
878214734Srpaulo
879214734Srpaulo
880214734Srpaulostatic void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
881214734Srpaulo{
882214734Srpaulo	if (sm->ctx->port_cb)
883214734Srpaulo		sm->ctx->port_cb(sm->ctx->ctx, 0);
884214734Srpaulo}
885214734Srpaulo
886214734Srpaulo
887189251Ssam/**
888189251Ssam * eapol_sm_step - EAPOL state machine step function
889189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
890189251Ssam *
891189251Ssam * This function is called to notify the state machine about changed external
892189251Ssam * variables. It will step through the EAPOL state machines in loop to process
893189251Ssam * all triggered state changes.
894189251Ssam */
895189251Ssamvoid eapol_sm_step(struct eapol_sm *sm)
896189251Ssam{
897189251Ssam	int i;
898189251Ssam
899189251Ssam	/* In theory, it should be ok to run this in loop until !changed.
900189251Ssam	 * However, it is better to use a limit on number of iterations to
901189251Ssam	 * allow events (e.g., SIGTERM) to stop the program cleanly if the
902189251Ssam	 * state machine were to generate a busy loop. */
903189251Ssam	for (i = 0; i < 100; i++) {
904189251Ssam		sm->changed = FALSE;
905189251Ssam		SM_STEP_RUN(SUPP_PAE);
906189251Ssam		SM_STEP_RUN(KEY_RX);
907189251Ssam		SM_STEP_RUN(SUPP_BE);
908189251Ssam		if (eap_peer_sm_step(sm->eap))
909189251Ssam			sm->changed = TRUE;
910189251Ssam		if (!sm->changed)
911189251Ssam			break;
912189251Ssam	}
913189251Ssam
914189251Ssam	if (sm->changed) {
915189251Ssam		/* restart EAPOL state machine step from timeout call in order
916189251Ssam		 * to allow other events to be processed. */
917189251Ssam		eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
918189251Ssam		eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
919189251Ssam	}
920189251Ssam
921189251Ssam	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
922189251Ssam		int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
923189251Ssam		sm->cb_status = EAPOL_CB_IN_PROGRESS;
924189251Ssam		sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
925189251Ssam	}
926189251Ssam}
927189251Ssam
928189251Ssam
929189251Ssam#ifdef CONFIG_CTRL_IFACE
930189251Ssamstatic const char *eapol_supp_pae_state(int state)
931189251Ssam{
932189251Ssam	switch (state) {
933189251Ssam	case SUPP_PAE_LOGOFF:
934189251Ssam		return "LOGOFF";
935189251Ssam	case SUPP_PAE_DISCONNECTED:
936189251Ssam		return "DISCONNECTED";
937189251Ssam	case SUPP_PAE_CONNECTING:
938189251Ssam		return "CONNECTING";
939189251Ssam	case SUPP_PAE_AUTHENTICATING:
940189251Ssam		return "AUTHENTICATING";
941189251Ssam	case SUPP_PAE_HELD:
942189251Ssam		return "HELD";
943189251Ssam	case SUPP_PAE_AUTHENTICATED:
944189251Ssam		return "AUTHENTICATED";
945189251Ssam	case SUPP_PAE_RESTART:
946189251Ssam		return "RESTART";
947189251Ssam	default:
948189251Ssam		return "UNKNOWN";
949189251Ssam	}
950189251Ssam}
951189251Ssam
952189251Ssam
953189251Ssamstatic const char *eapol_supp_be_state(int state)
954189251Ssam{
955189251Ssam	switch (state) {
956189251Ssam	case SUPP_BE_REQUEST:
957189251Ssam		return "REQUEST";
958189251Ssam	case SUPP_BE_RESPONSE:
959189251Ssam		return "RESPONSE";
960189251Ssam	case SUPP_BE_SUCCESS:
961189251Ssam		return "SUCCESS";
962189251Ssam	case SUPP_BE_FAIL:
963189251Ssam		return "FAIL";
964189251Ssam	case SUPP_BE_TIMEOUT:
965189251Ssam		return "TIMEOUT";
966189251Ssam	case SUPP_BE_IDLE:
967189251Ssam		return "IDLE";
968189251Ssam	case SUPP_BE_INITIALIZE:
969189251Ssam		return "INITIALIZE";
970189251Ssam	case SUPP_BE_RECEIVE:
971189251Ssam		return "RECEIVE";
972189251Ssam	default:
973189251Ssam		return "UNKNOWN";
974189251Ssam	}
975189251Ssam}
976189251Ssam
977189251Ssam
978189251Ssamstatic const char * eapol_port_status(PortStatus status)
979189251Ssam{
980189251Ssam	if (status == Authorized)
981189251Ssam		return "Authorized";
982189251Ssam	else
983189251Ssam		return "Unauthorized";
984189251Ssam}
985189251Ssam#endif /* CONFIG_CTRL_IFACE */
986189251Ssam
987189251Ssam
988189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
989189251Ssamstatic const char * eapol_port_control(PortControl ctrl)
990189251Ssam{
991189251Ssam	switch (ctrl) {
992189251Ssam	case Auto:
993189251Ssam		return "Auto";
994189251Ssam	case ForceUnauthorized:
995189251Ssam		return "ForceUnauthorized";
996189251Ssam	case ForceAuthorized:
997189251Ssam		return "ForceAuthorized";
998189251Ssam	default:
999189251Ssam		return "Unknown";
1000189251Ssam	}
1001189251Ssam}
1002189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
1003189251Ssam
1004189251Ssam
1005189251Ssam/**
1006189251Ssam * eapol_sm_configure - Set EAPOL variables
1007189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1008189251Ssam * @heldPeriod: dot1xSuppHeldPeriod
1009189251Ssam * @authPeriod: dot1xSuppAuthPeriod
1010189251Ssam * @startPeriod: dot1xSuppStartPeriod
1011189251Ssam * @maxStart: dot1xSuppMaxStart
1012189251Ssam *
1013189251Ssam * Set configurable EAPOL state machine variables. Each variable can be set to
1014189251Ssam * the given value or ignored if set to -1 (to set only some of the variables).
1015189251Ssam */
1016189251Ssamvoid eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
1017189251Ssam			int startPeriod, int maxStart)
1018189251Ssam{
1019189251Ssam	if (sm == NULL)
1020189251Ssam		return;
1021189251Ssam	if (heldPeriod >= 0)
1022189251Ssam		sm->heldPeriod = heldPeriod;
1023189251Ssam	if (authPeriod >= 0)
1024189251Ssam		sm->authPeriod = authPeriod;
1025189251Ssam	if (startPeriod >= 0)
1026189251Ssam		sm->startPeriod = startPeriod;
1027189251Ssam	if (maxStart >= 0)
1028189251Ssam		sm->maxStart = maxStart;
1029189251Ssam}
1030189251Ssam
1031189251Ssam
1032189251Ssam#ifdef CONFIG_CTRL_IFACE
1033189251Ssam/**
1034189251Ssam * eapol_sm_get_status - Get EAPOL state machine status
1035189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1036189251Ssam * @buf: Buffer for status information
1037189251Ssam * @buflen: Maximum buffer length
1038189251Ssam * @verbose: Whether to include verbose status information
1039189251Ssam * Returns: Number of bytes written to buf.
1040189251Ssam *
1041189251Ssam * Query EAPOL state machine for status information. This function fills in a
1042189251Ssam * text area with current status information from the EAPOL state machine. If
1043189251Ssam * the buffer (buf) is not large enough, status information will be truncated
1044189251Ssam * to fit the buffer.
1045189251Ssam */
1046189251Ssamint eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
1047189251Ssam			int verbose)
1048189251Ssam{
1049189251Ssam	int len, ret;
1050189251Ssam	if (sm == NULL)
1051189251Ssam		return 0;
1052189251Ssam
1053189251Ssam	len = os_snprintf(buf, buflen,
1054189251Ssam			  "Supplicant PAE state=%s\n"
1055189251Ssam			  "suppPortStatus=%s\n",
1056189251Ssam			  eapol_supp_pae_state(sm->SUPP_PAE_state),
1057189251Ssam			  eapol_port_status(sm->suppPortStatus));
1058189251Ssam	if (len < 0 || (size_t) len >= buflen)
1059189251Ssam		return 0;
1060189251Ssam
1061189251Ssam	if (verbose) {
1062189251Ssam		ret = os_snprintf(buf + len, buflen - len,
1063189251Ssam				  "heldPeriod=%u\n"
1064189251Ssam				  "authPeriod=%u\n"
1065189251Ssam				  "startPeriod=%u\n"
1066189251Ssam				  "maxStart=%u\n"
1067189251Ssam				  "portControl=%s\n"
1068189251Ssam				  "Supplicant Backend state=%s\n",
1069189251Ssam				  sm->heldPeriod,
1070189251Ssam				  sm->authPeriod,
1071189251Ssam				  sm->startPeriod,
1072189251Ssam				  sm->maxStart,
1073189251Ssam				  eapol_port_control(sm->portControl),
1074189251Ssam				  eapol_supp_be_state(sm->SUPP_BE_state));
1075189251Ssam		if (ret < 0 || (size_t) ret >= buflen - len)
1076189251Ssam			return len;
1077189251Ssam		len += ret;
1078189251Ssam	}
1079189251Ssam
1080189251Ssam	len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
1081189251Ssam
1082189251Ssam	return len;
1083189251Ssam}
1084189251Ssam
1085189251Ssam
1086189251Ssam/**
1087189251Ssam * eapol_sm_get_mib - Get EAPOL state machine MIBs
1088189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1089189251Ssam * @buf: Buffer for MIB information
1090189251Ssam * @buflen: Maximum buffer length
1091189251Ssam * Returns: Number of bytes written to buf.
1092189251Ssam *
1093189251Ssam * Query EAPOL state machine for MIB information. This function fills in a
1094189251Ssam * text area with current MIB information from the EAPOL state machine. If
1095189251Ssam * the buffer (buf) is not large enough, MIB information will be truncated to
1096189251Ssam * fit the buffer.
1097189251Ssam */
1098189251Ssamint eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
1099189251Ssam{
1100189251Ssam	size_t len;
1101189251Ssam	int ret;
1102189251Ssam
1103189251Ssam	if (sm == NULL)
1104189251Ssam		return 0;
1105189251Ssam	ret = os_snprintf(buf, buflen,
1106189251Ssam			  "dot1xSuppPaeState=%d\n"
1107189251Ssam			  "dot1xSuppHeldPeriod=%u\n"
1108189251Ssam			  "dot1xSuppAuthPeriod=%u\n"
1109189251Ssam			  "dot1xSuppStartPeriod=%u\n"
1110189251Ssam			  "dot1xSuppMaxStart=%u\n"
1111189251Ssam			  "dot1xSuppSuppControlledPortStatus=%s\n"
1112189251Ssam			  "dot1xSuppBackendPaeState=%d\n",
1113189251Ssam			  sm->SUPP_PAE_state,
1114189251Ssam			  sm->heldPeriod,
1115189251Ssam			  sm->authPeriod,
1116189251Ssam			  sm->startPeriod,
1117189251Ssam			  sm->maxStart,
1118189251Ssam			  sm->suppPortStatus == Authorized ?
1119189251Ssam			  "Authorized" : "Unauthorized",
1120189251Ssam			  sm->SUPP_BE_state);
1121189251Ssam
1122189251Ssam	if (ret < 0 || (size_t) ret >= buflen)
1123189251Ssam		return 0;
1124189251Ssam	len = ret;
1125189251Ssam
1126189251Ssam	ret = os_snprintf(buf + len, buflen - len,
1127189251Ssam			  "dot1xSuppEapolFramesRx=%u\n"
1128189251Ssam			  "dot1xSuppEapolFramesTx=%u\n"
1129189251Ssam			  "dot1xSuppEapolStartFramesTx=%u\n"
1130189251Ssam			  "dot1xSuppEapolLogoffFramesTx=%u\n"
1131189251Ssam			  "dot1xSuppEapolRespFramesTx=%u\n"
1132189251Ssam			  "dot1xSuppEapolReqIdFramesRx=%u\n"
1133189251Ssam			  "dot1xSuppEapolReqFramesRx=%u\n"
1134189251Ssam			  "dot1xSuppInvalidEapolFramesRx=%u\n"
1135189251Ssam			  "dot1xSuppEapLengthErrorFramesRx=%u\n"
1136189251Ssam			  "dot1xSuppLastEapolFrameVersion=%u\n"
1137189251Ssam			  "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
1138189251Ssam			  sm->dot1xSuppEapolFramesRx,
1139189251Ssam			  sm->dot1xSuppEapolFramesTx,
1140189251Ssam			  sm->dot1xSuppEapolStartFramesTx,
1141189251Ssam			  sm->dot1xSuppEapolLogoffFramesTx,
1142189251Ssam			  sm->dot1xSuppEapolRespFramesTx,
1143189251Ssam			  sm->dot1xSuppEapolReqIdFramesRx,
1144189251Ssam			  sm->dot1xSuppEapolReqFramesRx,
1145189251Ssam			  sm->dot1xSuppInvalidEapolFramesRx,
1146189251Ssam			  sm->dot1xSuppEapLengthErrorFramesRx,
1147189251Ssam			  sm->dot1xSuppLastEapolFrameVersion,
1148189251Ssam			  MAC2STR(sm->dot1xSuppLastEapolFrameSource));
1149189251Ssam
1150189251Ssam	if (ret < 0 || (size_t) ret >= buflen - len)
1151189251Ssam		return len;
1152189251Ssam	len += ret;
1153189251Ssam
1154189251Ssam	return len;
1155189251Ssam}
1156189251Ssam#endif /* CONFIG_CTRL_IFACE */
1157189251Ssam
1158189251Ssam
1159189251Ssam/**
1160189251Ssam * eapol_sm_rx_eapol - Process received EAPOL frames
1161189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1162189251Ssam * @src: Source MAC address of the EAPOL packet
1163189251Ssam * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
1164189251Ssam * @len: Length of the EAPOL frame
1165189251Ssam * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
1166189251Ssam * -1 failure
1167189251Ssam */
1168189251Ssamint eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
1169189251Ssam		      size_t len)
1170189251Ssam{
1171189251Ssam	const struct ieee802_1x_hdr *hdr;
1172189251Ssam	const struct ieee802_1x_eapol_key *key;
1173189251Ssam	int data_len;
1174189251Ssam	int res = 1;
1175189251Ssam	size_t plen;
1176189251Ssam
1177189251Ssam	if (sm == NULL)
1178189251Ssam		return 0;
1179189251Ssam	sm->dot1xSuppEapolFramesRx++;
1180189251Ssam	if (len < sizeof(*hdr)) {
1181189251Ssam		sm->dot1xSuppInvalidEapolFramesRx++;
1182189251Ssam		return 0;
1183189251Ssam	}
1184189251Ssam	hdr = (const struct ieee802_1x_hdr *) buf;
1185189251Ssam	sm->dot1xSuppLastEapolFrameVersion = hdr->version;
1186189251Ssam	os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
1187189251Ssam	if (hdr->version < EAPOL_VERSION) {
1188189251Ssam		/* TODO: backwards compatibility */
1189189251Ssam	}
1190189251Ssam	plen = be_to_host16(hdr->length);
1191189251Ssam	if (plen > len - sizeof(*hdr)) {
1192189251Ssam		sm->dot1xSuppEapLengthErrorFramesRx++;
1193189251Ssam		return 0;
1194189251Ssam	}
1195189251Ssam#ifdef CONFIG_WPS
1196189251Ssam	if (sm->conf.workaround &&
1197189251Ssam	    plen < len - sizeof(*hdr) &&
1198189251Ssam	    hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
1199189251Ssam	    len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
1200189251Ssam		const struct eap_hdr *ehdr =
1201189251Ssam			(const struct eap_hdr *) (hdr + 1);
1202189251Ssam		u16 elen;
1203189251Ssam
1204189251Ssam		elen = be_to_host16(ehdr->length);
1205189251Ssam		if (elen > plen && elen <= len - sizeof(*hdr)) {
1206189251Ssam			/*
1207189251Ssam			 * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
1208189251Ssam			 * packets with too short EAPOL header length field
1209189251Ssam			 * (14 octets). This is fixed in firmware Ver.1.49.
1210189251Ssam			 * As a workaround, fix the EAPOL header based on the
1211189251Ssam			 * correct length in the EAP packet.
1212189251Ssam			 */
1213189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
1214189251Ssam				   "payload length based on EAP header: "
1215189251Ssam				   "%d -> %d", (int) plen, elen);
1216189251Ssam			plen = elen;
1217189251Ssam		}
1218189251Ssam	}
1219189251Ssam#endif /* CONFIG_WPS */
1220189251Ssam	data_len = plen + sizeof(*hdr);
1221189251Ssam
1222189251Ssam	switch (hdr->type) {
1223189251Ssam	case IEEE802_1X_TYPE_EAP_PACKET:
1224189251Ssam		if (sm->cached_pmk) {
1225189251Ssam			/* Trying to use PMKSA caching, but Authenticator did
1226189251Ssam			 * not seem to have a matching entry. Need to restart
1227189251Ssam			 * EAPOL state machines.
1228189251Ssam			 */
1229189251Ssam			eapol_sm_abort_cached(sm);
1230189251Ssam		}
1231189251Ssam		wpabuf_free(sm->eapReqData);
1232189251Ssam		sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
1233189251Ssam		if (sm->eapReqData) {
1234189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
1235189251Ssam				   "frame");
1236189251Ssam			sm->eapolEap = TRUE;
1237189251Ssam			eapol_sm_step(sm);
1238189251Ssam		}
1239189251Ssam		break;
1240189251Ssam	case IEEE802_1X_TYPE_EAPOL_KEY:
1241189251Ssam		if (plen < sizeof(*key)) {
1242189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
1243189251Ssam				   "frame received");
1244189251Ssam			break;
1245189251Ssam		}
1246189251Ssam		key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
1247189251Ssam		if (key->type == EAPOL_KEY_TYPE_WPA ||
1248189251Ssam		    key->type == EAPOL_KEY_TYPE_RSN) {
1249189251Ssam			/* WPA Supplicant takes care of this frame. */
1250189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
1251189251Ssam				   "frame in EAPOL state machines");
1252189251Ssam			res = 0;
1253189251Ssam			break;
1254189251Ssam		}
1255189251Ssam		if (key->type != EAPOL_KEY_TYPE_RC4) {
1256189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
1257189251Ssam				   "EAPOL-Key type %d", key->type);
1258189251Ssam			break;
1259189251Ssam		}
1260189251Ssam		os_free(sm->last_rx_key);
1261189251Ssam		sm->last_rx_key = os_malloc(data_len);
1262189251Ssam		if (sm->last_rx_key) {
1263189251Ssam			wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
1264189251Ssam				   "frame");
1265189251Ssam			os_memcpy(sm->last_rx_key, buf, data_len);
1266189251Ssam			sm->last_rx_key_len = data_len;
1267189251Ssam			sm->rxKey = TRUE;
1268189251Ssam			eapol_sm_step(sm);
1269189251Ssam		}
1270189251Ssam		break;
1271189251Ssam	default:
1272189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
1273189251Ssam			   hdr->type);
1274189251Ssam		sm->dot1xSuppInvalidEapolFramesRx++;
1275189251Ssam		break;
1276189251Ssam	}
1277189251Ssam
1278189251Ssam	return res;
1279189251Ssam}
1280189251Ssam
1281189251Ssam
1282189251Ssam/**
1283189251Ssam * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
1284189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1285189251Ssam *
1286189251Ssam * Notify EAPOL state machine about transmitted EAPOL packet from an external
1287189251Ssam * component, e.g., WPA. This will update the statistics.
1288189251Ssam */
1289189251Ssamvoid eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
1290189251Ssam{
1291189251Ssam	if (sm)
1292189251Ssam		sm->dot1xSuppEapolFramesTx++;
1293189251Ssam}
1294189251Ssam
1295189251Ssam
1296189251Ssam/**
1297189251Ssam * eapol_sm_notify_portEnabled - Notification about portEnabled change
1298189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1299189251Ssam * @enabled: New portEnabled value
1300189251Ssam *
1301189251Ssam * Notify EAPOL state machine about new portEnabled value.
1302189251Ssam */
1303189251Ssamvoid eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
1304189251Ssam{
1305189251Ssam	if (sm == NULL)
1306189251Ssam		return;
1307189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1308189251Ssam		   "portEnabled=%d", enabled);
1309189251Ssam	sm->portEnabled = enabled;
1310189251Ssam	eapol_sm_step(sm);
1311189251Ssam}
1312189251Ssam
1313189251Ssam
1314189251Ssam/**
1315189251Ssam * eapol_sm_notify_portValid - Notification about portValid change
1316189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1317189251Ssam * @valid: New portValid value
1318189251Ssam *
1319189251Ssam * Notify EAPOL state machine about new portValid value.
1320189251Ssam */
1321189251Ssamvoid eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
1322189251Ssam{
1323189251Ssam	if (sm == NULL)
1324189251Ssam		return;
1325189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1326189251Ssam		   "portValid=%d", valid);
1327189251Ssam	sm->portValid = valid;
1328189251Ssam	eapol_sm_step(sm);
1329189251Ssam}
1330189251Ssam
1331189251Ssam
1332189251Ssam/**
1333189251Ssam * eapol_sm_notify_eap_success - Notification of external EAP success trigger
1334189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1335189251Ssam * @success: %TRUE = set success, %FALSE = clear success
1336189251Ssam *
1337189251Ssam * Notify the EAPOL state machine that external event has forced EAP state to
1338189251Ssam * success (success = %TRUE). This can be cleared by setting success = %FALSE.
1339189251Ssam *
1340189251Ssam * This function is called to update EAP state when WPA-PSK key handshake has
1341189251Ssam * been completed successfully since WPA-PSK does not use EAP state machine.
1342189251Ssam */
1343189251Ssamvoid eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
1344189251Ssam{
1345189251Ssam	if (sm == NULL)
1346189251Ssam		return;
1347189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1348189251Ssam		   "EAP success=%d", success);
1349189251Ssam	sm->eapSuccess = success;
1350189251Ssam	sm->altAccept = success;
1351189251Ssam	if (success)
1352189251Ssam		eap_notify_success(sm->eap);
1353189251Ssam	eapol_sm_step(sm);
1354189251Ssam}
1355189251Ssam
1356189251Ssam
1357189251Ssam/**
1358189251Ssam * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
1359189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1360189251Ssam * @fail: %TRUE = set failure, %FALSE = clear failure
1361189251Ssam *
1362189251Ssam * Notify EAPOL state machine that external event has forced EAP state to
1363189251Ssam * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
1364189251Ssam */
1365189251Ssamvoid eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
1366189251Ssam{
1367189251Ssam	if (sm == NULL)
1368189251Ssam		return;
1369189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1370189251Ssam		   "EAP fail=%d", fail);
1371189251Ssam	sm->eapFail = fail;
1372189251Ssam	sm->altReject = fail;
1373189251Ssam	eapol_sm_step(sm);
1374189251Ssam}
1375189251Ssam
1376189251Ssam
1377189251Ssam/**
1378189251Ssam * eapol_sm_notify_config - Notification of EAPOL configuration change
1379189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1380189251Ssam * @config: Pointer to current network EAP configuration
1381189251Ssam * @conf: Pointer to EAPOL configuration data
1382189251Ssam *
1383189251Ssam * Notify EAPOL state machine that configuration has changed. config will be
1384189251Ssam * stored as a backpointer to network configuration. This can be %NULL to clear
1385189251Ssam * the stored pointed. conf will be copied to local EAPOL/EAP configuration
1386189251Ssam * data. If conf is %NULL, this part of the configuration change will be
1387189251Ssam * skipped.
1388189251Ssam */
1389189251Ssamvoid eapol_sm_notify_config(struct eapol_sm *sm,
1390189251Ssam			    struct eap_peer_config *config,
1391189251Ssam			    const struct eapol_config *conf)
1392189251Ssam{
1393189251Ssam	if (sm == NULL)
1394189251Ssam		return;
1395189251Ssam
1396189251Ssam	sm->config = config;
1397189251Ssam
1398189251Ssam	if (conf == NULL)
1399189251Ssam		return;
1400189251Ssam
1401189251Ssam	sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
1402189251Ssam	sm->conf.required_keys = conf->required_keys;
1403189251Ssam	sm->conf.fast_reauth = conf->fast_reauth;
1404189251Ssam	sm->conf.workaround = conf->workaround;
1405189251Ssam	if (sm->eap) {
1406189251Ssam		eap_set_fast_reauth(sm->eap, conf->fast_reauth);
1407189251Ssam		eap_set_workaround(sm->eap, conf->workaround);
1408189251Ssam		eap_set_force_disabled(sm->eap, conf->eap_disabled);
1409189251Ssam	}
1410189251Ssam}
1411189251Ssam
1412189251Ssam
1413189251Ssam/**
1414189251Ssam * eapol_sm_get_key - Get master session key (MSK) from EAP
1415189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1416189251Ssam * @key: Pointer for key buffer
1417189251Ssam * @len: Number of bytes to copy to key
1418189251Ssam * Returns: 0 on success (len of key available), maximum available key len
1419189251Ssam * (>0) if key is available but it is shorter than len, or -1 on failure.
1420189251Ssam *
1421189251Ssam * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
1422189251Ssam * is available only after a successful authentication.
1423189251Ssam */
1424189251Ssamint eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
1425189251Ssam{
1426189251Ssam	const u8 *eap_key;
1427189251Ssam	size_t eap_len;
1428189251Ssam
1429189251Ssam	if (sm == NULL || !eap_key_available(sm->eap)) {
1430189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
1431189251Ssam		return -1;
1432189251Ssam	}
1433189251Ssam	eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
1434189251Ssam	if (eap_key == NULL) {
1435189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
1436189251Ssam		return -1;
1437189251Ssam	}
1438189251Ssam	if (len > eap_len) {
1439189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
1440189251Ssam			   "available (len=%lu)",
1441189251Ssam			   (unsigned long) len, (unsigned long) eap_len);
1442189251Ssam		return eap_len;
1443189251Ssam	}
1444189251Ssam	os_memcpy(key, eap_key, len);
1445189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
1446189251Ssam		   (unsigned long) len);
1447189251Ssam	return 0;
1448189251Ssam}
1449189251Ssam
1450189251Ssam
1451189251Ssam/**
1452189251Ssam * eapol_sm_notify_logoff - Notification of logon/logoff commands
1453189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1454189251Ssam * @logoff: Whether command was logoff
1455189251Ssam *
1456189251Ssam * Notify EAPOL state machines that user requested logon/logoff.
1457189251Ssam */
1458189251Ssamvoid eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
1459189251Ssam{
1460189251Ssam	if (sm) {
1461189251Ssam		sm->userLogoff = logoff;
1462189251Ssam		eapol_sm_step(sm);
1463189251Ssam	}
1464189251Ssam}
1465189251Ssam
1466189251Ssam
1467189251Ssam/**
1468189251Ssam * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
1469189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1470189251Ssam *
1471189251Ssam * Notify EAPOL state machines that PMKSA caching was successful. This is used
1472189251Ssam * to move EAPOL and EAP state machines into authenticated/successful state.
1473189251Ssam */
1474189251Ssamvoid eapol_sm_notify_cached(struct eapol_sm *sm)
1475189251Ssam{
1476189251Ssam	if (sm == NULL)
1477189251Ssam		return;
1478189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
1479189251Ssam	sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
1480189251Ssam	sm->suppPortStatus = Authorized;
1481214734Srpaulo	eapol_sm_set_port_authorized(sm);
1482189251Ssam	sm->portValid = TRUE;
1483189251Ssam	eap_notify_success(sm->eap);
1484189251Ssam	eapol_sm_step(sm);
1485189251Ssam}
1486189251Ssam
1487189251Ssam
1488189251Ssam/**
1489189251Ssam * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
1490189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1491189251Ssam * @attempt: Whether PMKSA caching is tried
1492189251Ssam *
1493189251Ssam * Notify EAPOL state machines whether PMKSA caching is used.
1494189251Ssam */
1495189251Ssamvoid eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
1496189251Ssam{
1497189251Ssam	if (sm == NULL)
1498189251Ssam		return;
1499189251Ssam	if (attempt) {
1500189251Ssam		wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
1501189251Ssam		sm->cached_pmk = TRUE;
1502189251Ssam	} else {
1503189251Ssam		wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
1504189251Ssam		sm->cached_pmk = FALSE;
1505189251Ssam	}
1506189251Ssam}
1507189251Ssam
1508189251Ssam
1509189251Ssamstatic void eapol_sm_abort_cached(struct eapol_sm *sm)
1510189251Ssam{
1511189251Ssam	wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
1512189251Ssam		   "doing full EAP authentication");
1513189251Ssam	if (sm == NULL)
1514189251Ssam		return;
1515189251Ssam	sm->cached_pmk = FALSE;
1516189251Ssam	sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
1517189251Ssam	sm->suppPortStatus = Unauthorized;
1518214734Srpaulo	eapol_sm_set_port_unauthorized(sm);
1519189251Ssam
1520189251Ssam	/* Make sure we do not start sending EAPOL-Start frames first, but
1521189251Ssam	 * instead move to RESTART state to start EAPOL authentication. */
1522189251Ssam	sm->startWhen = 3;
1523189251Ssam	eapol_enable_timer_tick(sm);
1524189251Ssam
1525189251Ssam	if (sm->ctx->aborted_cached)
1526189251Ssam		sm->ctx->aborted_cached(sm->ctx->ctx);
1527189251Ssam}
1528189251Ssam
1529189251Ssam
1530189251Ssam/**
1531189251Ssam * eapol_sm_register_scard_ctx - Notification of smart card context
1532189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1533189251Ssam * @ctx: Context data for smart card operations
1534189251Ssam *
1535189251Ssam * Notify EAPOL state machines of context data for smart card operations. This
1536189251Ssam * context data will be used as a parameter for scard_*() functions.
1537189251Ssam */
1538189251Ssamvoid eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
1539189251Ssam{
1540189251Ssam	if (sm) {
1541189251Ssam		sm->ctx->scard_ctx = ctx;
1542189251Ssam		eap_register_scard_ctx(sm->eap, ctx);
1543189251Ssam	}
1544189251Ssam}
1545189251Ssam
1546189251Ssam
1547189251Ssam/**
1548189251Ssam * eapol_sm_notify_portControl - Notification of portControl changes
1549189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1550189251Ssam * @portControl: New value for portControl variable
1551189251Ssam *
1552189251Ssam * Notify EAPOL state machines that portControl variable has changed.
1553189251Ssam */
1554189251Ssamvoid eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
1555189251Ssam{
1556189251Ssam	if (sm == NULL)
1557189251Ssam		return;
1558189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1559189251Ssam		   "portControl=%s", eapol_port_control(portControl));
1560189251Ssam	sm->portControl = portControl;
1561189251Ssam	eapol_sm_step(sm);
1562189251Ssam}
1563189251Ssam
1564189251Ssam
1565189251Ssam/**
1566189251Ssam * eapol_sm_notify_ctrl_attached - Notification of attached monitor
1567189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1568189251Ssam *
1569189251Ssam * Notify EAPOL state machines that a monitor was attached to the control
1570189251Ssam * interface to trigger re-sending of pending requests for user input.
1571189251Ssam */
1572189251Ssamvoid eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
1573189251Ssam{
1574189251Ssam	if (sm == NULL)
1575189251Ssam		return;
1576189251Ssam	eap_sm_notify_ctrl_attached(sm->eap);
1577189251Ssam}
1578189251Ssam
1579189251Ssam
1580189251Ssam/**
1581189251Ssam * eapol_sm_notify_ctrl_response - Notification of received user input
1582189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1583189251Ssam *
1584189251Ssam * Notify EAPOL state machines that a control response, i.e., user
1585189251Ssam * input, was received in order to trigger retrying of a pending EAP request.
1586189251Ssam */
1587189251Ssamvoid eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
1588189251Ssam{
1589189251Ssam	if (sm == NULL)
1590189251Ssam		return;
1591189251Ssam	if (sm->eapReqData && !sm->eapReq) {
1592189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
1593189251Ssam			   "input) notification - retrying pending EAP "
1594189251Ssam			   "Request");
1595189251Ssam		sm->eapolEap = TRUE;
1596189251Ssam		sm->eapReq = TRUE;
1597189251Ssam		eapol_sm_step(sm);
1598189251Ssam	}
1599189251Ssam}
1600189251Ssam
1601189251Ssam
1602189251Ssam/**
1603189251Ssam * eapol_sm_request_reauth - Request reauthentication
1604189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1605189251Ssam *
1606189251Ssam * This function can be used to request EAPOL reauthentication, e.g., when the
1607189251Ssam * current PMKSA entry is nearing expiration.
1608189251Ssam */
1609189251Ssamvoid eapol_sm_request_reauth(struct eapol_sm *sm)
1610189251Ssam{
1611189251Ssam	if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
1612189251Ssam		return;
1613189251Ssam	eapol_sm_txStart(sm);
1614189251Ssam}
1615189251Ssam
1616189251Ssam
1617189251Ssam/**
1618189251Ssam * eapol_sm_notify_lower_layer_success - Notification of lower layer success
1619189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1620189251Ssam * @in_eapol_sm: Whether the caller is already running inside EAPOL state
1621189251Ssam * machine loop (eapol_sm_step())
1622189251Ssam *
1623189251Ssam * Notify EAPOL (and EAP) state machines that a lower layer has detected a
1624189251Ssam * successful authentication. This is used to recover from dropped EAP-Success
1625189251Ssam * messages.
1626189251Ssam */
1627189251Ssamvoid eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
1628189251Ssam{
1629189251Ssam	if (sm == NULL)
1630189251Ssam		return;
1631189251Ssam	eap_notify_lower_layer_success(sm->eap);
1632189251Ssam	if (!in_eapol_sm)
1633189251Ssam		eapol_sm_step(sm);
1634189251Ssam}
1635189251Ssam
1636189251Ssam
1637189251Ssam/**
1638189251Ssam * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
1639189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1640189251Ssam */
1641189251Ssamvoid eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
1642189251Ssam{
1643189251Ssam	if (sm)
1644189251Ssam		eap_invalidate_cached_session(sm->eap);
1645189251Ssam}
1646189251Ssam
1647189251Ssam
1648189251Ssamstatic struct eap_peer_config * eapol_sm_get_config(void *ctx)
1649189251Ssam{
1650189251Ssam	struct eapol_sm *sm = ctx;
1651189251Ssam	return sm ? sm->config : NULL;
1652189251Ssam}
1653189251Ssam
1654189251Ssam
1655189251Ssamstatic struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
1656189251Ssam{
1657189251Ssam	struct eapol_sm *sm = ctx;
1658189251Ssam	if (sm == NULL || sm->eapReqData == NULL)
1659189251Ssam		return NULL;
1660189251Ssam
1661189251Ssam	return sm->eapReqData;
1662189251Ssam}
1663189251Ssam
1664189251Ssam
1665189251Ssamstatic Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
1666189251Ssam{
1667189251Ssam	struct eapol_sm *sm = ctx;
1668189251Ssam	if (sm == NULL)
1669189251Ssam		return FALSE;
1670189251Ssam	switch (variable) {
1671189251Ssam	case EAPOL_eapSuccess:
1672189251Ssam		return sm->eapSuccess;
1673189251Ssam	case EAPOL_eapRestart:
1674189251Ssam		return sm->eapRestart;
1675189251Ssam	case EAPOL_eapFail:
1676189251Ssam		return sm->eapFail;
1677189251Ssam	case EAPOL_eapResp:
1678189251Ssam		return sm->eapResp;
1679189251Ssam	case EAPOL_eapNoResp:
1680189251Ssam		return sm->eapNoResp;
1681189251Ssam	case EAPOL_eapReq:
1682189251Ssam		return sm->eapReq;
1683189251Ssam	case EAPOL_portEnabled:
1684189251Ssam		return sm->portEnabled;
1685189251Ssam	case EAPOL_altAccept:
1686189251Ssam		return sm->altAccept;
1687189251Ssam	case EAPOL_altReject:
1688189251Ssam		return sm->altReject;
1689189251Ssam	}
1690189251Ssam	return FALSE;
1691189251Ssam}
1692189251Ssam
1693189251Ssam
1694189251Ssamstatic void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
1695189251Ssam			      Boolean value)
1696189251Ssam{
1697189251Ssam	struct eapol_sm *sm = ctx;
1698189251Ssam	if (sm == NULL)
1699189251Ssam		return;
1700189251Ssam	switch (variable) {
1701189251Ssam	case EAPOL_eapSuccess:
1702189251Ssam		sm->eapSuccess = value;
1703189251Ssam		break;
1704189251Ssam	case EAPOL_eapRestart:
1705189251Ssam		sm->eapRestart = value;
1706189251Ssam		break;
1707189251Ssam	case EAPOL_eapFail:
1708189251Ssam		sm->eapFail = value;
1709189251Ssam		break;
1710189251Ssam	case EAPOL_eapResp:
1711189251Ssam		sm->eapResp = value;
1712189251Ssam		break;
1713189251Ssam	case EAPOL_eapNoResp:
1714189251Ssam		sm->eapNoResp = value;
1715189251Ssam		break;
1716189251Ssam	case EAPOL_eapReq:
1717189251Ssam		sm->eapReq = value;
1718189251Ssam		break;
1719189251Ssam	case EAPOL_portEnabled:
1720189251Ssam		sm->portEnabled = value;
1721189251Ssam		break;
1722189251Ssam	case EAPOL_altAccept:
1723189251Ssam		sm->altAccept = value;
1724189251Ssam		break;
1725189251Ssam	case EAPOL_altReject:
1726189251Ssam		sm->altReject = value;
1727189251Ssam		break;
1728189251Ssam	}
1729189251Ssam}
1730189251Ssam
1731189251Ssam
1732189251Ssamstatic unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
1733189251Ssam{
1734189251Ssam	struct eapol_sm *sm = ctx;
1735189251Ssam	if (sm == NULL)
1736189251Ssam		return 0;
1737189251Ssam	switch (variable) {
1738189251Ssam	case EAPOL_idleWhile:
1739189251Ssam		return sm->idleWhile;
1740189251Ssam	}
1741189251Ssam	return 0;
1742189251Ssam}
1743189251Ssam
1744189251Ssam
1745189251Ssamstatic void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
1746189251Ssam			     unsigned int value)
1747189251Ssam{
1748189251Ssam	struct eapol_sm *sm = ctx;
1749189251Ssam	if (sm == NULL)
1750189251Ssam		return;
1751189251Ssam	switch (variable) {
1752189251Ssam	case EAPOL_idleWhile:
1753189251Ssam		sm->idleWhile = value;
1754189251Ssam		eapol_enable_timer_tick(sm);
1755189251Ssam		break;
1756189251Ssam	}
1757189251Ssam}
1758189251Ssam
1759189251Ssam
1760189251Ssamstatic void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
1761189251Ssam{
1762189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS
1763189251Ssam	struct eapol_sm *sm = ctx;
1764189251Ssam	if (sm && sm->ctx && sm->ctx->set_config_blob)
1765189251Ssam		sm->ctx->set_config_blob(sm->ctx->ctx, blob);
1766189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */
1767189251Ssam}
1768189251Ssam
1769189251Ssam
1770189251Ssamstatic const struct wpa_config_blob *
1771189251Ssameapol_sm_get_config_blob(void *ctx, const char *name)
1772189251Ssam{
1773189251Ssam#ifndef CONFIG_NO_CONFIG_BLOBS
1774189251Ssam	struct eapol_sm *sm = ctx;
1775189251Ssam	if (sm && sm->ctx && sm->ctx->get_config_blob)
1776189251Ssam		return sm->ctx->get_config_blob(sm->ctx->ctx, name);
1777189251Ssam	else
1778189251Ssam		return NULL;
1779189251Ssam#else /* CONFIG_NO_CONFIG_BLOBS */
1780189251Ssam	return NULL;
1781189251Ssam#endif /* CONFIG_NO_CONFIG_BLOBS */
1782189251Ssam}
1783189251Ssam
1784189251Ssam
1785189251Ssamstatic void eapol_sm_notify_pending(void *ctx)
1786189251Ssam{
1787189251Ssam	struct eapol_sm *sm = ctx;
1788189251Ssam	if (sm == NULL)
1789189251Ssam		return;
1790189251Ssam	if (sm->eapReqData && !sm->eapReq) {
1791189251Ssam		wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
1792189251Ssam			   "state machine - retrying pending EAP Request");
1793189251Ssam		sm->eapolEap = TRUE;
1794189251Ssam		sm->eapReq = TRUE;
1795189251Ssam		eapol_sm_step(sm);
1796189251Ssam	}
1797189251Ssam}
1798189251Ssam
1799189251Ssam
1800189251Ssam#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
1801189251Ssamstatic void eapol_sm_eap_param_needed(void *ctx, const char *field,
1802189251Ssam				      const char *txt)
1803189251Ssam{
1804189251Ssam	struct eapol_sm *sm = ctx;
1805189251Ssam	wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
1806189251Ssam	if (sm->ctx->eap_param_needed)
1807189251Ssam		sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
1808189251Ssam}
1809189251Ssam#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
1810189251Ssam#define eapol_sm_eap_param_needed NULL
1811189251Ssam#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
1812189251Ssam
1813189251Ssam
1814189251Ssamstatic struct eapol_callbacks eapol_cb =
1815189251Ssam{
1816189251Ssam	eapol_sm_get_config,
1817189251Ssam	eapol_sm_get_bool,
1818189251Ssam	eapol_sm_set_bool,
1819189251Ssam	eapol_sm_get_int,
1820189251Ssam	eapol_sm_set_int,
1821189251Ssam	eapol_sm_get_eapReqData,
1822189251Ssam	eapol_sm_set_config_blob,
1823189251Ssam	eapol_sm_get_config_blob,
1824189251Ssam	eapol_sm_notify_pending,
1825189251Ssam	eapol_sm_eap_param_needed
1826189251Ssam};
1827189251Ssam
1828189251Ssam
1829189251Ssam/**
1830189251Ssam * eapol_sm_init - Initialize EAPOL state machine
1831189251Ssam * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
1832189251Ssam * and EAPOL state machine will free it in eapol_sm_deinit()
1833189251Ssam * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
1834189251Ssam *
1835189251Ssam * Allocate and initialize an EAPOL state machine.
1836189251Ssam */
1837189251Ssamstruct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
1838189251Ssam{
1839189251Ssam	struct eapol_sm *sm;
1840189251Ssam	struct eap_config conf;
1841189251Ssam	sm = os_zalloc(sizeof(*sm));
1842189251Ssam	if (sm == NULL)
1843189251Ssam		return NULL;
1844189251Ssam	sm->ctx = ctx;
1845189251Ssam
1846189251Ssam	sm->portControl = Auto;
1847189251Ssam
1848189251Ssam	/* Supplicant PAE state machine */
1849189251Ssam	sm->heldPeriod = 60;
1850189251Ssam	sm->startPeriod = 30;
1851189251Ssam	sm->maxStart = 3;
1852189251Ssam
1853189251Ssam	/* Supplicant Backend state machine */
1854189251Ssam	sm->authPeriod = 30;
1855189251Ssam
1856189251Ssam	os_memset(&conf, 0, sizeof(conf));
1857189251Ssam	conf.opensc_engine_path = ctx->opensc_engine_path;
1858189251Ssam	conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
1859189251Ssam	conf.pkcs11_module_path = ctx->pkcs11_module_path;
1860189251Ssam	conf.wps = ctx->wps;
1861189251Ssam
1862189251Ssam	sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
1863189251Ssam	if (sm->eap == NULL) {
1864189251Ssam		os_free(sm);
1865189251Ssam		return NULL;
1866189251Ssam	}
1867189251Ssam
1868189251Ssam	/* Initialize EAPOL state machines */
1869189251Ssam	sm->initialize = TRUE;
1870189251Ssam	eapol_sm_step(sm);
1871189251Ssam	sm->initialize = FALSE;
1872189251Ssam	eapol_sm_step(sm);
1873189251Ssam
1874189251Ssam	sm->timer_tick_enabled = 1;
1875189251Ssam	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
1876189251Ssam
1877189251Ssam	return sm;
1878189251Ssam}
1879189251Ssam
1880189251Ssam
1881189251Ssam/**
1882189251Ssam * eapol_sm_deinit - Deinitialize EAPOL state machine
1883189251Ssam * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1884189251Ssam *
1885189251Ssam * Deinitialize and free EAPOL state machine.
1886189251Ssam */
1887189251Ssamvoid eapol_sm_deinit(struct eapol_sm *sm)
1888189251Ssam{
1889189251Ssam	if (sm == NULL)
1890189251Ssam		return;
1891189251Ssam	eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
1892189251Ssam	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
1893189251Ssam	eap_peer_sm_deinit(sm->eap);
1894189251Ssam	os_free(sm->last_rx_key);
1895189251Ssam	wpabuf_free(sm->eapReqData);
1896189251Ssam	os_free(sm->ctx);
1897189251Ssam	os_free(sm);
1898189251Ssam}
1899