text: /*
   * WPA Supplicant / EAPOL state machines
   * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * Alternatively, this software may be distributed under the terms of BSD
   * license.
   *
   * See README and COPYING for more details.
   */
  
  #include "includes.h"
  
  #include "common.h"
  #include "eapol_sm.h"
  #include "eap.h"
  #include "eloop.h"
  #include "l2_packet.h"
  #include "wpa.h"
  #include "md5.h"
  #include "rc4.h"
  #include "state_machine.h"
  
  #define STATE_MACHINE_DATA struct eapol_sm
  #define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
  
  
  /* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
  
  /**
   * struct eapol_sm - Internal data for EAPOL state machines
   */
  struct eapol_sm {
  	/* Timers */
  	unsigned int authWhile;
  	unsigned int heldWhile;
  	unsigned int startWhen;
  	unsigned int idleWhile; /* for EAP state machine */
  
  	/* Global variables */
  	Boolean eapFail;
  	Boolean eapolEap;
  	Boolean eapSuccess;
  	Boolean initialize;
  	Boolean keyDone;
  	Boolean keyRun;
  	PortControl portControl;
  	Boolean portEnabled;
  	PortStatus suppPortStatus;  /* dot1xSuppControlledPortStatus */
  	Boolean portValid;
  	Boolean suppAbort;
  	Boolean suppFail;
  	Boolean suppStart;
  	Boolean suppSuccess;
  	Boolean suppTimeout;
  
  	/* Supplicant PAE state machine */
  	enum {
  		SUPP_PAE_UNKNOWN = 0,
  		SUPP_PAE_DISCONNECTED = 1,
  		SUPP_PAE_LOGOFF = 2,
  		SUPP_PAE_CONNECTING = 3,
  		SUPP_PAE_AUTHENTICATING = 4,
  		SUPP_PAE_AUTHENTICATED = 5,
  		/* unused(6) */
  		SUPP_PAE_HELD = 7,
  		SUPP_PAE_RESTART = 8,
  		SUPP_PAE_S_FORCE_AUTH = 9,
  		SUPP_PAE_S_FORCE_UNAUTH = 10
  	} SUPP_PAE_state; /* dot1xSuppPaeState */
  	/* Variables */
  	Boolean userLogoff;
  	Boolean logoffSent;
  	unsigned int startCount;
  	Boolean eapRestart;
  	PortControl sPortMode;
  	/* Constants */
  	unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
  	unsigned int startPeriod; /* dot1xSuppStartPeriod */
  	unsigned int maxStart; /* dot1xSuppMaxStart */
  
  	/* Key Receive state machine */
  	enum {
  		KEY_RX_UNKNOWN = 0,
  		KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
  	} KEY_RX_state;
  	/* Variables */
  	Boolean rxKey;
  
  	/* Supplicant Backend state machine */
  	enum {
  		SUPP_BE_UNKNOWN = 0,
  		SUPP_BE_INITIALIZE = 1,
  		SUPP_BE_IDLE = 2,
  		SUPP_BE_REQUEST = 3,
  		SUPP_BE_RECEIVE = 4,
  		SUPP_BE_RESPONSE = 5,
  		SUPP_BE_FAIL = 6,
  		SUPP_BE_TIMEOUT = 7, 
  		SUPP_BE_SUCCESS = 8
  	} SUPP_BE_state; /* dot1xSuppBackendPaeState */
  	/* Variables */
  	Boolean eapNoResp;
  	Boolean eapReq;
  	Boolean eapResp;
  	/* Constants */
  	unsigned int authPeriod; /* dot1xSuppAuthPeriod */
  
  	/* Statistics */
  	unsigned int dot1xSuppEapolFramesRx;
  	unsigned int dot1xSuppEapolFramesTx;
  	unsigned int dot1xSuppEapolStartFramesTx;
  	unsigned int dot1xSuppEapolLogoffFramesTx;
  	unsigned int dot1xSuppEapolRespFramesTx;
  	unsigned int dot1xSuppEapolReqIdFramesRx;
  	unsigned int dot1xSuppEapolReqFramesRx;
  	unsigned int dot1xSuppInvalidEapolFramesRx;
  	unsigned int dot1xSuppEapLengthErrorFramesRx;
  	unsigned int dot1xSuppLastEapolFrameVersion;
  	unsigned char dot1xSuppLastEapolFrameSource[6];
  
  	/* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
  	Boolean changed;
  	struct eap_sm *eap;
  	struct wpa_ssid *config;
  	Boolean initial_req;
  	u8 *last_rx_key;
  	size_t last_rx_key_len;
  	u8 *eapReqData; /* for EAP */
  	size_t eapReqDataLen; /* for EAP */
  	Boolean altAccept; /* for EAP */
  	Boolean altReject; /* for EAP */
  	Boolean replay_counter_valid;
  	u8 last_replay_counter[16];
  	struct eapol_config conf;
  	struct eapol_ctx *ctx;
  	enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
  		cb_status;
  	Boolean cached_pmk;
  
  	Boolean unicast_key_received, broadcast_key_received;
  };
  
  
  #define IEEE8021X_REPLAY_COUNTER_LEN 8
  #define IEEE8021X_KEY_SIGN_LEN 16
  #define IEEE8021X_KEY_IV_LEN 16
  
  #define IEEE8021X_KEY_INDEX_FLAG 0x80
  #define IEEE8021X_KEY_INDEX_MASK 0x03
  
  #ifdef _MSC_VER
  #pragma pack(push, 1)
  #endif /* _MSC_VER */
  
  struct ieee802_1x_eapol_key {
  	u8 type;
  	/* Note: key_length is unaligned */
  	u8 key_length[2];
  	/* does not repeat within the life of the keying material used to
  	 * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
  	u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
  	u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
  	u8 key_index; /* key flag in the most significant bit:
  		       * 0 = broadcast (default key),
  		       * 1 = unicast (key mapping key); key index is in the
  		       * 7 least significant bits */
  	/* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
  	 * the key */
  	u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
  
  	/* followed by key: if packet body length = 44 + key length, then the
  	 * key field (of key_length bytes) contains the key in encrypted form;
  	 * if packet body length = 44, key field is absent and key_length
  	 * represents the number of least significant octets from
  	 * MS-MPPE-Send-Key attribute to be used as the keying material;
  	 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
  } STRUCT_PACKED;
  
  #ifdef _MSC_VER
  #pragma pack(pop)
  #endif /* _MSC_VER */
  
  
  static void eapol_sm_txLogoff(struct eapol_sm *sm);
  static void eapol_sm_txStart(struct eapol_sm *sm);
  static void eapol_sm_processKey(struct eapol_sm *sm);
  static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
  static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
  static void eapol_sm_abortSupp(struct eapol_sm *sm);
  static void eapol_sm_abort_cached(struct eapol_sm *sm);
  static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
  
  
  /* Port Timers state machine - implemented as a function that will be called
   * once a second as a registered event loop timeout */
  static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
  {
  	struct eapol_sm *sm = timeout_ctx;
  
  	if (sm->authWhile > 0) {
  		sm->authWhile--;
  		if (sm->authWhile == 0)
  			wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
  	}
  	if (sm->heldWhile > 0) {
  		sm->heldWhile--;
  		if (sm->heldWhile == 0)
  			wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
  	}
  	if (sm->startWhen > 0) {
  		sm->startWhen--;
  		if (sm->startWhen == 0)
  			wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
  	}
  	if (sm->idleWhile > 0) {
  		sm->idleWhile--;
  		if (sm->idleWhile == 0)
  			wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
  	}
  
  	eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm);
  	eapol_sm_step(sm);
  }
  
  
  SM_STATE(SUPP_PAE, LOGOFF)
  {
  	SM_ENTRY(SUPP_PAE, LOGOFF);
  	eapol_sm_txLogoff(sm);
  	sm->logoffSent = TRUE;
  	sm->suppPortStatus = Unauthorized;
  }
  
  
  SM_STATE(SUPP_PAE, DISCONNECTED)
  {
  	SM_ENTRY(SUPP_PAE, DISCONNECTED);
  	sm->sPortMode = Auto;
  	sm->startCount = 0;
  	sm->logoffSent = FALSE;
  	sm->suppPortStatus = Unauthorized;
  	sm->suppAbort = TRUE;
  
  	sm->unicast_key_received = FALSE;
  	sm->broadcast_key_received = FALSE;
  }
  
  
  SM_STATE(SUPP_PAE, CONNECTING)
  {
  	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
  	SM_ENTRY(SUPP_PAE, CONNECTING);
  	if (send_start) {
  		sm->startWhen = sm->startPeriod;
  		sm->startCount++;
  	} else {
  		/*
  		 * Do not send EAPOL-Start immediately since in most cases,
  		 * Authenticator is going to start authentication immediately
  		 * after association and an extra EAPOL-Start is just going to
  		 * delay authentication. Use a short timeout to send the first
  		 * EAPOL-Start if Authenticator does not start authentication.
  		 */
  		sm->startWhen = 3;
  	}
  	sm->eapolEap = FALSE;
  	if (send_start)
  		eapol_sm_txStart(sm);
  }
  
  
  SM_STATE(SUPP_PAE, AUTHENTICATING)
  {
  	SM_ENTRY(SUPP_PAE, AUTHENTICATING);
  	sm->startCount = 0;
  	sm->suppSuccess = FALSE;
  	sm->suppFail = FALSE;
  	sm->suppTimeout = FALSE;
  	sm->keyRun = FALSE;
  	sm->keyDone = FALSE;
  	sm->suppStart = TRUE;
  }
  
  
  SM_STATE(SUPP_PAE, HELD)
  {
  	SM_ENTRY(SUPP_PAE, HELD);
  	sm->heldWhile = sm->heldPeriod;
  	sm->suppPortStatus = Unauthorized;
  	sm->cb_status = EAPOL_CB_FAILURE;
  }
  
  
  SM_STATE(SUPP_PAE, AUTHENTICATED)
  {
  	SM_ENTRY(SUPP_PAE, AUTHENTICATED);
  	sm->suppPortStatus = Authorized;
  	sm->cb_status = EAPOL_CB_SUCCESS;
  }
  
  
  SM_STATE(SUPP_PAE, RESTART)
  {
  	SM_ENTRY(SUPP_PAE, RESTART);
  	sm->eapRestart = TRUE;
  }
  
  
  SM_STATE(SUPP_PAE, S_FORCE_AUTH)
  {
  	SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
  	sm->suppPortStatus = Authorized;
  	sm->sPortMode = ForceAuthorized;
  }
  
  
  SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
  {
  	SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
  	sm->suppPortStatus = Unauthorized;
  	sm->sPortMode = ForceUnauthorized;
  	eapol_sm_txLogoff(sm);
  }
  
  
  SM_STEP(SUPP_PAE)
  {
  	if ((sm->userLogoff && !sm->logoffSent) &&
  	    !(sm->initialize || !sm->portEnabled))
  		SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
  	else if (((sm->portControl == Auto) &&
  		  (sm->sPortMode != sm->portControl)) ||
  		 sm->initialize || !sm->portEnabled)
  		SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
  	else if ((sm->portControl == ForceAuthorized) &&
  		 (sm->sPortMode != sm->portControl) &&
  		 !(sm->initialize || !sm->portEnabled))
  		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
  	else if ((sm->portControl == ForceUnauthorized) &&
  		 (sm->sPortMode != sm->portControl) &&
  		 !(sm->initialize || !sm->portEnabled))
  		SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
  	else switch (sm->SUPP_PAE_state) {
  	case SUPP_PAE_UNKNOWN:
  		break;
  	case SUPP_PAE_LOGOFF:
  		if (!sm->userLogoff)
  			SM_ENTER(SUPP_PAE, DISCONNECTED);
  		break;
  	case SUPP_PAE_DISCONNECTED:
  		SM_ENTER(SUPP_PAE, CONNECTING);
  		break;
  	case SUPP_PAE_CONNECTING:
  		if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
  			SM_ENTER(SUPP_PAE, CONNECTING);
  		else if (sm->startWhen == 0 &&
  			 sm->startCount >= sm->maxStart &&
  			 sm->portValid)
  			SM_ENTER(SUPP_PAE, AUTHENTICATED);
  		else if (sm->eapSuccess || sm->eapFail)
  			SM_ENTER(SUPP_PAE, AUTHENTICATING);
  		else if (sm->eapolEap)
  			SM_ENTER(SUPP_PAE, RESTART);
  		else if (sm->startWhen == 0 &&
  			 sm->startCount >= sm->maxStart &&
  			 !sm->portValid)
  			SM_ENTER(SUPP_PAE, HELD);
  		break;
  	case SUPP_PAE_AUTHENTICATING:
  		if (sm->eapSuccess && !sm->portValid &&
  		    sm->conf.accept_802_1x_keys &&
  		    sm->conf.required_keys == 0) {
  			wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
  				   "plaintext connection; no EAPOL-Key frames "
  				   "required");
  			sm->portValid = TRUE;
  			if (sm->ctx->eapol_done_cb)
  				sm->ctx->eapol_done_cb(sm->ctx->ctx);
  		}
  		if (sm->eapSuccess && sm->portValid)
  			SM_ENTER(SUPP_PAE, AUTHENTICATED);
  		else if (sm->eapFail || (sm->keyDone && !sm->portValid))
  			SM_ENTER(SUPP_PAE, HELD);
  		else if (sm->suppTimeout)
  			SM_ENTER(SUPP_PAE, CONNECTING);
  		break;
  	case SUPP_PAE_HELD:
  		if (sm->heldWhile == 0)
  			SM_ENTER(SUPP_PAE, CONNECTING);
  		else if (sm->eapolEap)
  			SM_ENTER(SUPP_PAE, RESTART);
  		break;
  	case SUPP_PAE_AUTHENTICATED:
  		if (sm->eapolEap && sm->portValid)
  			SM_ENTER(SUPP_PAE, RESTART);
  		else if (!sm->portValid)
  			SM_ENTER(SUPP_PAE, DISCONNECTED);
  		break;
  	case SUPP_PAE_RESTART:
  		if (!sm->eapRestart)
  			SM_ENTER(SUPP_PAE, AUTHENTICATING);
  		break;
  	case SUPP_PAE_S_FORCE_AUTH:
  		break;
  	case SUPP_PAE_S_FORCE_UNAUTH:
  		break;
  	}
  }
  
  
  SM_STATE(KEY_RX, NO_KEY_RECEIVE)
  {
  	SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
  }
  
  
  SM_STATE(KEY_RX, KEY_RECEIVE)
  {
  	SM_ENTRY(KEY_RX, KEY_RECEIVE);
  	eapol_sm_processKey(sm);
  	sm->rxKey = FALSE;
  }
  
  
  SM_STEP(KEY_RX)
  {
  	if (sm->initialize || !sm->portEnabled)
  		SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
  	switch (sm->KEY_RX_state) {
  	case KEY_RX_UNKNOWN:
  		break;
  	case KEY_RX_NO_KEY_RECEIVE:
  		if (sm->rxKey)
  			SM_ENTER(KEY_RX, KEY_RECEIVE);
  		break;
  	case KEY_RX_KEY_RECEIVE:
  		if (sm->rxKey)
  			SM_ENTER(KEY_RX, KEY_RECEIVE);
  		break;
  	}
  }
  
  
  SM_STATE(SUPP_BE, REQUEST)
  {
  	SM_ENTRY(SUPP_BE, REQUEST);
  	sm->authWhile = 0;
  	sm->eapReq = TRUE;
  	eapol_sm_getSuppRsp(sm);
  }
  
  
  SM_STATE(SUPP_BE, RESPONSE)
  {
  	SM_ENTRY(SUPP_BE, RESPONSE);
  	eapol_sm_txSuppRsp(sm);
  	sm->eapResp = FALSE;
  }
  
  
  SM_STATE(SUPP_BE, SUCCESS)
  {
  	SM_ENTRY(SUPP_BE, SUCCESS);
  	sm->keyRun = TRUE;
  	sm->suppSuccess = TRUE;
  
  	if (eap_key_available(sm->eap)) {
  		/* New key received - clear IEEE 802.1X EAPOL-Key replay
  		 * counter */
  		sm->replay_counter_valid = FALSE;
  	}
  }
  
  
  SM_STATE(SUPP_BE, FAIL)
  {
  	SM_ENTRY(SUPP_BE, FAIL);
  	sm->suppFail = TRUE;
  }
  
  
  SM_STATE(SUPP_BE, TIMEOUT)
  {
  	SM_ENTRY(SUPP_BE, TIMEOUT);
  	sm->suppTimeout = TRUE;
  }
  
  
  SM_STATE(SUPP_BE, IDLE)
  {
  	SM_ENTRY(SUPP_BE, IDLE);
  	sm->suppStart = FALSE;
  	sm->initial_req = TRUE;
  }
  
  
  SM_STATE(SUPP_BE, INITIALIZE)
  {
  	SM_ENTRY(SUPP_BE, INITIALIZE);
  	eapol_sm_abortSupp(sm);
  	sm->suppAbort = FALSE;
  }
  
  
  SM_STATE(SUPP_BE, RECEIVE)
  {
  	SM_ENTRY(SUPP_BE, RECEIVE);
  	sm->authWhile = sm->authPeriod;
  	sm->eapolEap = FALSE;
  	sm->eapNoResp = FALSE;
  	sm->initial_req = FALSE;
  }
  
  
  SM_STEP(SUPP_BE)
  {
  	if (sm->initialize || sm->suppAbort)
  		SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
  	else switch (sm->SUPP_BE_state) {
  	case SUPP_BE_UNKNOWN:
  		break;
  	case SUPP_BE_REQUEST:
  		/*
  		 * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
  		 * and SUCCESS based on eapFail and eapSuccess, respectively.
  		 * However, IEEE Std 802.1X-2004 is also specifying that
  		 * eapNoResp should be set in conjuction with eapSuccess and
  		 * eapFail which would mean that more than one of the
  		 * transitions here would be activated at the same time.
  		 * Skipping RESPONSE and/or RECEIVE states in these cases can
  		 * cause problems and the direct transitions to do not seem
  		 * correct. Because of this, the conditions for these
  		 * transitions are verified only after eapNoResp. They are
  		 * unlikely to be used since eapNoResp should always be set if
  		 * either of eapSuccess or eapFail is set.
  		 */
  		if (sm->eapResp && sm->eapNoResp) {
  			wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
  				   "eapResp and eapNoResp set?!");
  		}
  		if (sm->eapResp)
  			SM_ENTER(SUPP_BE, RESPONSE);
  		else if (sm->eapNoResp)
  			SM_ENTER(SUPP_BE, RECEIVE);
  		else if (sm->eapFail)
  			SM_ENTER(SUPP_BE, FAIL);
  		else if (sm->eapSuccess)
  			SM_ENTER(SUPP_BE, SUCCESS);
  		break;
  	case SUPP_BE_RESPONSE:
  		SM_ENTER(SUPP_BE, RECEIVE);
  		break;
  	case SUPP_BE_SUCCESS:
  		SM_ENTER(SUPP_BE, IDLE);
  		break;
  	case SUPP_BE_FAIL:
  		SM_ENTER(SUPP_BE, IDLE);
  		break;
  	case SUPP_BE_TIMEOUT:
  		SM_ENTER(SUPP_BE, IDLE);
  		break;
  	case SUPP_BE_IDLE:
  		if (sm->eapFail && sm->suppStart)
  			SM_ENTER(SUPP_BE, FAIL);
  		else if (sm->eapolEap && sm->suppStart)
  			SM_ENTER(SUPP_BE, REQUEST);
  		else if (sm->eapSuccess && sm->suppStart)
  			SM_ENTER(SUPP_BE, SUCCESS);
  		break;
  	case SUPP_BE_INITIALIZE:
  		SM_ENTER(SUPP_BE, IDLE);
  		break;
  	case SUPP_BE_RECEIVE:
  		if (sm->eapolEap)
  			SM_ENTER(SUPP_BE, REQUEST);
  		else if (sm->eapFail)
  			SM_ENTER(SUPP_BE, FAIL);
  		else if (sm->authWhile == 0)
  			SM_ENTER(SUPP_BE, TIMEOUT);
  		else if (sm->eapSuccess)
  			SM_ENTER(SUPP_BE, SUCCESS);
  		break;
  	}
  }
  
  
  static void eapol_sm_txLogoff(struct eapol_sm *sm)
  {
  	wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
  	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
  			    IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
  	sm->dot1xSuppEapolLogoffFramesTx++;
  	sm->dot1xSuppEapolFramesTx++;
  }
  
  
  static void eapol_sm_txStart(struct eapol_sm *sm)
  {
  	wpa_printf(MSG_DEBUG, "EAPOL: txStart");
  	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
  			    IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
  	sm->dot1xSuppEapolStartFramesTx++;
  	sm->dot1xSuppEapolFramesTx++;
  }
  
  
  #define IEEE8021X_ENCR_KEY_LEN 32
  #define IEEE8021X_SIGN_KEY_LEN 32
  
  struct eap_key_data {
  	u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
  	u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
  };
  
  
  static void eapol_sm_processKey(struct eapol_sm *sm)
  {
  	struct ieee802_1x_hdr *hdr;
  	struct ieee802_1x_eapol_key *key;
  	struct eap_key_data keydata;
  	u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
  	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
  	int key_len, res, sign_key_len, encr_key_len;
  	u16 rx_key_length;
  
  	wpa_printf(MSG_DEBUG, "EAPOL: processKey");
  	if (sm->last_rx_key == NULL)
  		return;
  
  	if (!sm->conf.accept_802_1x_keys) {
  		wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
  			   " even though this was not accepted - "
  			   "ignoring this packet");
  		return;
  	}
  
  	hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
  	key = (struct ieee802_1x_eapol_key *) (hdr + 1);
  	if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
  		wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
  		return;
  	}
  	rx_key_length = WPA_GET_BE16(key->key_length);
  	wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
  		   "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
  		   hdr->version, hdr->type, be_to_host16(hdr->length),
  		   key->type, rx_key_length, key->key_index);
  
  	eapol_sm_notify_lower_layer_success(sm);
  	sign_key_len = IEEE8021X_SIGN_KEY_LEN;
  	encr_key_len = IEEE8021X_ENCR_KEY_LEN;
  	res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
  	if (res < 0) {
  		wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
  			   "decrypting EAPOL-Key keys");
  		return;
  	}
  	if (res == 16) {
  		/* LEAP derives only 16 bytes of keying material. */
  		res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
  		if (res) {
  			wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
  				   "master key for decrypting EAPOL-Key keys");
  			return;
  		}
  		sign_key_len = 16;
  		encr_key_len = 16;
  		os_memcpy(keydata.sign_key, keydata.encr_key, 16);
  	} else if (res) {
  		wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
  			   "data for decrypting EAPOL-Key keys (res=%d)", res);
  		return;
  	}
  
  	/* The key replay_counter must increase when same master key */
  	if (sm->replay_counter_valid &&
  	    os_memcmp(sm->last_replay_counter, key->replay_counter,
  		      IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
  		wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
  			   "not increase - ignoring key");
  		wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
  			    sm->last_replay_counter,
  			    IEEE8021X_REPLAY_COUNTER_LEN);
  		wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
  			    key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
  		return;
  	}
  
  	/* Verify key signature (HMAC-MD5) */
  	os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
  	os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
  	hmac_md5(keydata.sign_key, sign_key_len,
  		 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
  		 key->key_signature);
  	if (os_memcmp(orig_key_sign, key->key_signature,
  		      IEEE8021X_KEY_SIGN_LEN) != 0) {
  		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
  			   "EAPOL-Key packet");
  		os_memcpy(key->key_signature, orig_key_sign,
  			  IEEE8021X_KEY_SIGN_LEN);
  		return;
  	}
  	wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
  
  	key_len = be_to_host16(hdr->length) - sizeof(*key);
  	if (key_len > 32 || rx_key_length > 32) {
  		wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
  			   key_len ? key_len : rx_key_length);
  		return;
  	}
  	if (key_len == rx_key_length) {
  		os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
  		os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
  			  encr_key_len);
  		os_memcpy(datakey, key + 1, key_len);
  		rc4(datakey, key_len, ekey,
  		    IEEE8021X_KEY_IV_LEN + encr_key_len);
  		wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
  				datakey, key_len);
  	} else if (key_len == 0) {
  		/*
  		 * IEEE 802.1X-2004 specifies that least significant Key Length
  		 * octets from MS-MPPE-Send-Key are used as the key if the key
  		 * data is not present. This seems to be meaning the beginning
  		 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
  		 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
  		 * Anyway, taking the beginning of the keying material from EAP
  		 * seems to interoperate with Authenticators.
  		 */
  		key_len = rx_key_length;
  		os_memcpy(datakey, keydata.encr_key, key_len);
  		wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
  				"material data encryption key",
  				datakey, key_len);
  	} else {
  		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
  			   "(key_length=%d)", key_len, rx_key_length);
  		return;
  	}
  
  	sm->replay_counter_valid = TRUE;
  	os_memcpy(sm->last_replay_counter, key->replay_counter,
  		  IEEE8021X_REPLAY_COUNTER_LEN);
  
  	wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
  		   "len %d",
  		   key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
  		   "unicast" : "broadcast",
  		   key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
  
  	if (sm->ctx->set_wep_key &&
  	    sm->ctx->set_wep_key(sm->ctx->ctx,
  				 key->key_index & IEEE8021X_KEY_INDEX_FLAG,
  				 key->key_index & IEEE8021X_KEY_INDEX_MASK,
  				 datakey, key_len) < 0) {
  		wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
  			   " driver.");
  	} else {
  		if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
  			sm->unicast_key_received = TRUE;
  		else
  			sm->broadcast_key_received = TRUE;
  
  		if ((sm->unicast_key_received ||
  		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
  		    (sm->broadcast_key_received ||
  		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
  		{
  			wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
  				   "frames received");
  			sm->portValid = TRUE;
  			if (sm->ctx->eapol_done_cb)
  				sm->ctx->eapol_done_cb(sm->ctx->ctx);
  		}
  	}
  }
  
  
  static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
  {
  	wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
  	/* EAP layer processing; no special code is needed, since Supplicant
  	 * Backend state machine is waiting for eapNoResp or eapResp to be set
  	 * and these are only set in the EAP state machine when the processing
  	 * has finished. */
  }
  
  
  static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
  {
  	u8 *resp;
  	size_t resp_len;
  
  	wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
  	resp = eap_get_eapRespData(sm->eap, &resp_len);
  	if (resp == NULL) {
  		wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
  			   "not available");
  		return;
  	}
  
  	/* Send EAP-Packet from the EAP layer to the Authenticator */
  	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
  			    IEEE802_1X_TYPE_EAP_PACKET, resp, resp_len);
  
  	/* eapRespData is not used anymore, so free it here */
  	os_free(resp);
  
  	if (sm->initial_req)
  		sm->dot1xSuppEapolReqIdFramesRx++;
  	else
  		sm->dot1xSuppEapolReqFramesRx++;
  	sm->dot1xSuppEapolRespFramesTx++;
  	sm->dot1xSuppEapolFramesTx++;
  }
  
  
  static void eapol_sm_abortSupp(struct eapol_sm *sm)
  {
  	/* release system resources that may have been allocated for the
  	 * authentication session */
  	os_free(sm->last_rx_key);
  	sm->last_rx_key = NULL;
  	os_free(sm->eapReqData);
  	sm->eapReqData = NULL;
  	eap_sm_abort(sm->eap);
  }
  
  
  static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
  {
  	eapol_sm_step(timeout_ctx);
  }
  
  
  /**
   * eapol_sm_step - EAPOL state machine step function
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * This function is called to notify the state machine about changed external
   * variables. It will step through the EAPOL state machines in loop to process
   * all triggered state changes.
   */
  void eapol_sm_step(struct eapol_sm *sm)
  {
  	int i;
  
  	/* In theory, it should be ok to run this in loop until !changed.
  	 * However, it is better to use a limit on number of iterations to
  	 * allow events (e.g., SIGTERM) to stop the program cleanly if the
  	 * state machine were to generate a busy loop. */
  	for (i = 0; i < 100; i++) {
  		sm->changed = FALSE;
  		SM_STEP_RUN(SUPP_PAE);
  		SM_STEP_RUN(KEY_RX);
  		SM_STEP_RUN(SUPP_BE);
  		if (eap_sm_step(sm->eap))
  			sm->changed = TRUE;
  		if (!sm->changed)
  			break;
  	}
  
  	if (sm->changed) {
  		/* restart EAPOL state machine step from timeout call in order
  		 * to allow other events to be processed. */
  		eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
  		eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
  	}
  
  	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
  		int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
  		sm->cb_status = EAPOL_CB_IN_PROGRESS;
  		sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
  	}
  }
  
  
  #ifdef CONFIG_CTRL_IFACE
  static const char *eapol_supp_pae_state(int state)
  {
  	switch (state) {
  	case SUPP_PAE_LOGOFF:
  		return "LOGOFF";
  	case SUPP_PAE_DISCONNECTED:
  		return "DISCONNECTED";
  	case SUPP_PAE_CONNECTING:
  		return "CONNECTING";
  	case SUPP_PAE_AUTHENTICATING:
  		return "AUTHENTICATING";
  	case SUPP_PAE_HELD:
  		return "HELD";
  	case SUPP_PAE_AUTHENTICATED:
  		return "AUTHENTICATED";
  	case SUPP_PAE_RESTART:
  		return "RESTART";
  	default:
  		return "UNKNOWN";
  	}
  }
  
  
  static const char *eapol_supp_be_state(int state)
  {
  	switch (state) {
  	case SUPP_BE_REQUEST:
  		return "REQUEST";
  	case SUPP_BE_RESPONSE:
  		return "RESPONSE";
  	case SUPP_BE_SUCCESS:
  		return "SUCCESS";
  	case SUPP_BE_FAIL:
  		return "FAIL";
  	case SUPP_BE_TIMEOUT:
  		return "TIMEOUT";
  	case SUPP_BE_IDLE:
  		return "IDLE";
  	case SUPP_BE_INITIALIZE:
  		return "INITIALIZE";
  	case SUPP_BE_RECEIVE:
  		return "RECEIVE";
  	default:
  		return "UNKNOWN";
  	}
  }
  
  
  static const char * eapol_port_status(PortStatus status)
  {
  	if (status == Authorized)
  		return "Authorized";
  	else
  		return "Unauthorized";
  }
  #endif /* CONFIG_CTRL_IFACE */
  
  
  #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
  static const char * eapol_port_control(PortControl ctrl)
  {
  	switch (ctrl) {
  	case Auto:
  		return "Auto";
  	case ForceUnauthorized:
  		return "ForceUnauthorized";
  	case ForceAuthorized:
  		return "ForceAuthorized";
  	default:
  		return "Unknown";
  	}
  }
  #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
  
  
  /**
   * eapol_sm_configure - Set EAPOL variables
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @heldPeriod: dot1xSuppHeldPeriod
   * @authPeriod: dot1xSuppAuthPeriod
   * @startPeriod: dot1xSuppStartPeriod
   * @maxStart: dot1xSuppMaxStart
   *
   * Set configurable EAPOL state machine variables. Each variable can be set to
   * the given value or ignored if set to -1 (to set only some of the variables).
   */
  void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
  			int startPeriod, int maxStart)
  {
  	if (sm == NULL)
  		return;
  	if (heldPeriod >= 0)
  		sm->heldPeriod = heldPeriod;
  	if (authPeriod >= 0)
  		sm->authPeriod = authPeriod;
  	if (startPeriod >= 0)
  		sm->startPeriod = startPeriod;
  	if (maxStart >= 0)
  		sm->maxStart = maxStart;
  }
  
  
  #ifdef CONFIG_CTRL_IFACE
  /**
   * eapol_sm_get_status - Get EAPOL state machine status
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @buf: Buffer for status information
   * @buflen: Maximum buffer length
   * @verbose: Whether to include verbose status information
   * Returns: Number of bytes written to buf.
   *
   * Query EAPOL state machine for status information. This function fills in a
   * text area with current status information from the EAPOL state machine. If
   * the buffer (buf) is not large enough, status information will be truncated
   * to fit the buffer.
   */
  int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
  			int verbose)
  {
  	int len, ret;
  	if (sm == NULL)
  		return 0;
  
  	len = os_snprintf(buf, buflen,
  			  "Supplicant PAE state=%s\n"
  			  "suppPortStatus=%s\n",
  			  eapol_supp_pae_state(sm->SUPP_PAE_state),
  			  eapol_port_status(sm->suppPortStatus));
  	if (len < 0 || (size_t) len >= buflen)
  		return 0;
  
  	if (verbose) {
  		ret = os_snprintf(buf + len, buflen - len,
  				  "heldPeriod=%u\n"
  				  "authPeriod=%u\n"
  				  "startPeriod=%u\n"
  				  "maxStart=%u\n"
  				  "portControl=%s\n"
  				  "Supplicant Backend state=%s\n",
  				  sm->heldPeriod,
  				  sm->authPeriod,
  				  sm->startPeriod,
  				  sm->maxStart,
  				  eapol_port_control(sm->portControl),
  				  eapol_supp_be_state(sm->SUPP_BE_state));
  		if (ret < 0 || (size_t) ret >= buflen - len)
  			return len;
  		len += ret;
  	}
  
  	len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
  
  	return len;
  }
  
  
  /**
   * eapol_sm_get_mib - Get EAPOL state machine MIBs
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @buf: Buffer for MIB information
   * @buflen: Maximum buffer length
   * Returns: Number of bytes written to buf.
   *
   * Query EAPOL state machine for MIB information. This function fills in a
   * text area with current MIB information from the EAPOL state machine. If
   * the buffer (buf) is not large enough, MIB information will be truncated to
   * fit the buffer.
   */
  int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
  {
  	size_t len;
  	int ret;
  
  	if (sm == NULL)
  		return 0;
  	ret = os_snprintf(buf, buflen,
  			  "dot1xSuppPaeState=%d\n"
  			  "dot1xSuppHeldPeriod=%u\n"
  			  "dot1xSuppAuthPeriod=%u\n"
  			  "dot1xSuppStartPeriod=%u\n"
  			  "dot1xSuppMaxStart=%u\n"
  			  "dot1xSuppSuppControlledPortStatus=%s\n"
  			  "dot1xSuppBackendPaeState=%d\n",
  			  sm->SUPP_PAE_state,
  			  sm->heldPeriod,
  			  sm->authPeriod,
  			  sm->startPeriod,
  			  sm->maxStart,
  			  sm->suppPortStatus == Authorized ?
  			  "Authorized" : "Unauthorized",
  			  sm->SUPP_BE_state);
  
  	if (ret < 0 || (size_t) ret >= buflen)
  		return 0;
  	len = ret;
  
  	ret = os_snprintf(buf + len, buflen - len,
  			  "dot1xSuppEapolFramesRx=%u\n"
  			  "dot1xSuppEapolFramesTx=%u\n"
  			  "dot1xSuppEapolStartFramesTx=%u\n"
  			  "dot1xSuppEapolLogoffFramesTx=%u\n"
  			  "dot1xSuppEapolRespFramesTx=%u\n"
  			  "dot1xSuppEapolReqIdFramesRx=%u\n"
  			  "dot1xSuppEapolReqFramesRx=%u\n"
  			  "dot1xSuppInvalidEapolFramesRx=%u\n"
  			  "dot1xSuppEapLengthErrorFramesRx=%u\n"
  			  "dot1xSuppLastEapolFrameVersion=%u\n"
  			  "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
  			  sm->dot1xSuppEapolFramesRx,
  			  sm->dot1xSuppEapolFramesTx,
  			  sm->dot1xSuppEapolStartFramesTx,
  			  sm->dot1xSuppEapolLogoffFramesTx,
  			  sm->dot1xSuppEapolRespFramesTx,
  			  sm->dot1xSuppEapolReqIdFramesRx,
  			  sm->dot1xSuppEapolReqFramesRx,
  			  sm->dot1xSuppInvalidEapolFramesRx,
  			  sm->dot1xSuppEapLengthErrorFramesRx,
  			  sm->dot1xSuppLastEapolFrameVersion,
  			  MAC2STR(sm->dot1xSuppLastEapolFrameSource));
  
  	if (ret < 0 || (size_t) ret >= buflen - len)
  		return len;
  	len += ret;
  
  	return len;
  }
  #endif /* CONFIG_CTRL_IFACE */
  
  
  /**
   * eapol_sm_rx_eapol - Process received EAPOL frames
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @src: Source MAC address of the EAPOL packet
   * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
   * @len: Length of the EAPOL frame
   * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
   * -1 failure
   */
  int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
  		      size_t len)
  {
  	const struct ieee802_1x_hdr *hdr;
  	const struct ieee802_1x_eapol_key *key;
  	int data_len;
  	int res = 1;
  	size_t plen;
  
  	if (sm == NULL)
  		return 0;
  	sm->dot1xSuppEapolFramesRx++;
  	if (len < sizeof(*hdr)) {
  		sm->dot1xSuppInvalidEapolFramesRx++;
  		return 0;
  	}
  	hdr = (const struct ieee802_1x_hdr *) buf;
  	sm->dot1xSuppLastEapolFrameVersion = hdr->version;
  	os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
  	if (hdr->version < EAPOL_VERSION) {
  		/* TODO: backwards compatibility */
  	}
  	plen = be_to_host16(hdr->length);
  	if (plen > len - sizeof(*hdr)) {
  		sm->dot1xSuppEapLengthErrorFramesRx++;
  		return 0;
  	}
  	data_len = plen + sizeof(*hdr);
  
  	switch (hdr->type) {
  	case IEEE802_1X_TYPE_EAP_PACKET:
  		if (sm->cached_pmk) {
  			/* Trying to use PMKSA caching, but Authenticator did
  			 * not seem to have a matching entry. Need to restart
  			 * EAPOL state machines.
  			 */
  			eapol_sm_abort_cached(sm);
  		}
  		os_free(sm->eapReqData);
  		sm->eapReqDataLen = plen;
  		sm->eapReqData = os_malloc(sm->eapReqDataLen);
  		if (sm->eapReqData) {
  			wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
  				   "frame");
  			os_memcpy(sm->eapReqData, (u8 *) (hdr + 1),
  				  sm->eapReqDataLen);
  			sm->eapolEap = TRUE;
  			eapol_sm_step(sm);
  		}
  		break;
  	case IEEE802_1X_TYPE_EAPOL_KEY:
  		if (plen < sizeof(*key)) {
  			wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
  				   "frame received");
  			break;
  		}
  		key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
  		if (key->type == EAPOL_KEY_TYPE_WPA ||
  		    key->type == EAPOL_KEY_TYPE_RSN) {
  			/* WPA Supplicant takes care of this frame. */
  			wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
  				   "frame in EAPOL state machines");
  			res = 0;
  			break;
  		}
  		if (key->type != EAPOL_KEY_TYPE_RC4) {
  			wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
  				   "EAPOL-Key type %d", key->type);
  			break;
  		}
  		os_free(sm->last_rx_key);
  		sm->last_rx_key = os_malloc(data_len);
  		if (sm->last_rx_key) {
  			wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
  				   "frame");
  			os_memcpy(sm->last_rx_key, buf, data_len);
  			sm->last_rx_key_len = data_len;
  			sm->rxKey = TRUE;
  			eapol_sm_step(sm);
  		}
  		break;
  	default:
  		wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
  			   hdr->type);
  		sm->dot1xSuppInvalidEapolFramesRx++;
  		break;
  	}
  
  	return res;
  }
  
  
  /**
   * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * Notify EAPOL state machine about transmitted EAPOL packet from an external
   * component, e.g., WPA. This will update the statistics.
   */
  void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
  {
  	if (sm)
  		sm->dot1xSuppEapolFramesTx++;
  }
  
  
  /**
   * eapol_sm_notify_portEnabled - Notification about portEnabled change
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @enabled: New portEnabled value
   *
   * Notify EAPOL state machine about new portEnabled value.
   */
  void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
  {
  	if (sm == NULL)
  		return;
  	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
  		   "portEnabled=%d", enabled);
  	sm->portEnabled = enabled;
  	eapol_sm_step(sm);
  }
  
  
  /**
   * eapol_sm_notify_portValid - Notification about portValid change
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @valid: New portValid value
   *
   * Notify EAPOL state machine about new portValid value.
   */
  void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
  {
  	if (sm == NULL)
  		return;
  	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
  		   "portValid=%d", valid);
  	sm->portValid = valid;
  	eapol_sm_step(sm);
  }
  
  
  /**
   * eapol_sm_notify_eap_success - Notification of external EAP success trigger
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @success: %TRUE = set success, %FALSE = clear success
   *
   * Notify the EAPOL state machine that external event has forced EAP state to
   * success (success = %TRUE). This can be cleared by setting success = %FALSE.
   *
   * This function is called to update EAP state when WPA-PSK key handshake has
   * been completed successfully since WPA-PSK does not use EAP state machine.
   */
  void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
  {
  	if (sm == NULL)
  		return;
  	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
  		   "EAP success=%d", success);
  	sm->eapSuccess = success;
  	sm->altAccept = success;
  	if (success)
  		eap_notify_success(sm->eap);
  	eapol_sm_step(sm);
  }
  
  
  /**
   * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @fail: %TRUE = set failure, %FALSE = clear failure
   *
   * Notify EAPOL state machine that external event has forced EAP state to
   * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
   */
  void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
  {
  	if (sm == NULL)
  		return;
  	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
  		   "EAP fail=%d", fail);
  	sm->eapFail = fail;
  	sm->altReject = fail;
  	eapol_sm_step(sm);
  }
  
  
  /**
   * eapol_sm_notify_config - Notification of EAPOL configuration change
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @config: Pointer to current network configuration
   * @conf: Pointer to EAPOL configuration data
   *
   * Notify EAPOL state machine that configuration has changed. config will be
   * stored as a backpointer to network configuration. This can be %NULL to clear
   * the stored pointed. conf will be copied to local EAPOL/EAP configuration
   * data. If conf is %NULL, this part of the configuration change will be
   * skipped.
   */
  void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config,
  			    const struct eapol_config *conf)
  {
  	if (sm == NULL)
  		return;
  
  	sm->config = config;
  
  	if (conf == NULL)
  		return;
  
  	sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
  	sm->conf.required_keys = conf->required_keys;
  	sm->conf.fast_reauth = conf->fast_reauth;
  	if (sm->eap) {
  		eap_set_fast_reauth(sm->eap, conf->fast_reauth);
  		eap_set_workaround(sm->eap, conf->workaround);
  		eap_set_force_disabled(sm->eap, conf->eap_disabled);
  	}
  }
  
  
  /**
   * eapol_sm_get_key - Get master session key (MSK) from EAP
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @key: Pointer for key buffer
   * @len: Number of bytes to copy to key
   * Returns: 0 on success (len of key available), maximum available key len
   * (>0) if key is available but it is shorter than len, or -1 on failure.
   *
   * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
   * is available only after a successful authentication.
   */
  int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
  {
  	const u8 *eap_key;
  	size_t eap_len;
  
  	if (sm == NULL || !eap_key_available(sm->eap))
  		return -1;
  	eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
  	if (eap_key == NULL)
  		return -1;
  	if (len > eap_len)
  		return eap_len;
  	os_memcpy(key, eap_key, len);
  	return 0;
  }
  
  
  /**
   * eapol_sm_notify_logoff - Notification of logon/logoff commands
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @logoff: Whether command was logoff
   *
   * Notify EAPOL state machines that user requested logon/logoff.
   */
  void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
  {
  	if (sm) {
  		sm->userLogoff = logoff;
  		eapol_sm_step(sm);
  	}
  }
  
  
  /**
   * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * Notify EAPOL state machines that PMKSA caching was successful. This is used
   * to move EAPOL and EAP state machines into authenticated/successful state.
   */
  void eapol_sm_notify_cached(struct eapol_sm *sm)
  {
  	if (sm == NULL)
  		return;
  	sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
  	sm->suppPortStatus = Authorized;
  	eap_notify_success(sm->eap);
  }
  
  
  /**
   * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @attempt: Whether PMKSA caching is tried
   *
   * Notify EAPOL state machines whether PMKSA caching is used.
   */
  void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
  {
  	if (sm == NULL)
  		return;
  	if (attempt) {
  		wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
  		sm->cached_pmk = TRUE;
  	} else {
  		wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
  		sm->cached_pmk = FALSE;
  	}
  }
  
  
  static void eapol_sm_abort_cached(struct eapol_sm *sm)
  {
  	wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
  		   "doing full EAP authentication");
  	if (sm == NULL)
  		return;
  	sm->cached_pmk = FALSE;
  	sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
  	sm->suppPortStatus = Unauthorized;
  
  	/* Make sure we do not start sending EAPOL-Start frames first, but
  	 * instead move to RESTART state to start EAPOL authentication. */
  	sm->startWhen = 3;
  
  	if (sm->ctx->aborted_cached)
  		sm->ctx->aborted_cached(sm->ctx->ctx);
  }
  
  
  /**
   * eapol_sm_register_scard_ctx - Notification of smart card context
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @ctx: Context data for smart card operations
   *
   * Notify EAPOL state machines of context data for smart card operations. This
   * context data will be used as a parameter for scard_*() functions.
   */
  void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
  {
  	if (sm) {
  		sm->ctx->scard_ctx = ctx;
  		eap_register_scard_ctx(sm->eap, ctx);
  	}
  }
  
  
  /**
   * eapol_sm_notify_portControl - Notification of portControl changes
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   * @portControl: New value for portControl variable
   *
   * Notify EAPOL state machines that portControl variable has changed.
   */
  void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
  {
  	if (sm == NULL)
  		return;
  	wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
  		   "portControl=%s", eapol_port_control(portControl));
  	sm->portControl = portControl;
  	eapol_sm_step(sm);
  }
  
  
  /**
   * eapol_sm_notify_ctrl_attached - Notification of attached monitor
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * Notify EAPOL state machines that a monitor was attached to the control
   * interface to trigger re-sending of pending requests for user input.
   */
  void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
  {
  	if (sm == NULL)
  		return;
  	eap_sm_notify_ctrl_attached(sm->eap);
  }
  
  
  /**
   * eapol_sm_notify_ctrl_response - Notification of received user input
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * Notify EAPOL state machines that a control response, i.e., user
   * input, was received in order to trigger retrying of a pending EAP request.
   */
  void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
  {
  	if (sm == NULL)
  		return;
  	if (sm->eapReqData && !sm->eapReq) {
  		wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
  			   "input) notification - retrying pending EAP "
  			   "Request");
  		sm->eapolEap = TRUE;
  		sm->eapReq = TRUE;
  		eapol_sm_step(sm);
  	}
  }
  
  
  /**
   * eapol_sm_request_reauth - Request reauthentication
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * This function can be used to request EAPOL reauthentication, e.g., when the
   * current PMKSA entry is nearing expiration.
   */
  void eapol_sm_request_reauth(struct eapol_sm *sm)
  {
  	if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
  		return;
  	eapol_sm_txStart(sm);
  }
  
  
  /**
   * eapol_sm_notify_lower_layer_success - Notification of lower layer success
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * Notify EAPOL (and EAP) state machines that a lower layer has detected a
   * successful authentication. This is used to recover from dropped EAP-Success
   * messages.
   */
  void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm)
  {
  	if (sm == NULL)
  		return;
  	eap_notify_lower_layer_success(sm->eap);
  }
  
  
  /**
   * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   */
  void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
  {
  	if (sm)
  		eap_invalidate_cached_session(sm->eap);
  }
  
  
  static struct wpa_ssid * eapol_sm_get_config(void *ctx)
  {
  	struct eapol_sm *sm = ctx;
  	return sm ? sm->config : NULL;
  }
  
  
  static u8 * eapol_sm_get_eapReqData(void *ctx, size_t *len)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm == NULL || sm->eapReqData == NULL) {
  		*len = 0;
  		return NULL;
  	}
  
  	*len = sm->eapReqDataLen;
  	return sm->eapReqData;
  }
  
  
  static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm == NULL)
  		return FALSE;
  	switch (variable) {
  	case EAPOL_eapSuccess:
  		return sm->eapSuccess;
  	case EAPOL_eapRestart:
  		return sm->eapRestart;
  	case EAPOL_eapFail:
  		return sm->eapFail;
  	case EAPOL_eapResp:
  		return sm->eapResp;
  	case EAPOL_eapNoResp:
  		return sm->eapNoResp;
  	case EAPOL_eapReq:
  		return sm->eapReq;
  	case EAPOL_portEnabled:
  		return sm->portEnabled;
  	case EAPOL_altAccept:
  		return sm->altAccept;
  	case EAPOL_altReject:
  		return sm->altReject;
  	}
  	return FALSE;
  }
  
  
  static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
  			      Boolean value)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm == NULL)
  		return;
  	switch (variable) {
  	case EAPOL_eapSuccess:
  		sm->eapSuccess = value;
  		break;
  	case EAPOL_eapRestart:
  		sm->eapRestart = value;
  		break;
  	case EAPOL_eapFail:
  		sm->eapFail = value;
  		break;
  	case EAPOL_eapResp:
  		sm->eapResp = value;
  		break;
  	case EAPOL_eapNoResp:
  		sm->eapNoResp = value;
  		break;
  	case EAPOL_eapReq:
  		sm->eapReq = value;
  		break;
  	case EAPOL_portEnabled:
  		sm->portEnabled = value;
  		break;
  	case EAPOL_altAccept:
  		sm->altAccept = value;
  		break;
  	case EAPOL_altReject:
  		sm->altReject = value;
  		break;
  	}
  }
  
  
  static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm == NULL)
  		return 0;
  	switch (variable) {
  	case EAPOL_idleWhile:
  		return sm->idleWhile;
  	}
  	return 0;
  }
  
  
  static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
  			     unsigned int value)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm == NULL)
  		return;
  	switch (variable) {
  	case EAPOL_idleWhile:
  		sm->idleWhile = value;
  		break;
  	}
  }
  
  
  static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm && sm->ctx && sm->ctx->set_config_blob)
  		sm->ctx->set_config_blob(sm->ctx->ctx, blob);
  }
  
  
  static const struct wpa_config_blob *
  eapol_sm_get_config_blob(void *ctx, const char *name)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm && sm->ctx && sm->ctx->get_config_blob)
  		return sm->ctx->get_config_blob(sm->ctx->ctx, name);
  	else
  		return NULL;
  }
  
  
  static void eapol_sm_notify_pending(void *ctx)
  {
  	struct eapol_sm *sm = ctx;
  	if (sm == NULL)
  		return;
  	if (sm->eapReqData && !sm->eapReq) {
  		wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
  			   "state machine - retrying pending EAP Request");
  		sm->eapolEap = TRUE;
  		sm->eapReq = TRUE;
  		eapol_sm_step(sm);
  	}
  }
  
  
  static struct eapol_callbacks eapol_cb =
  {
  	eapol_sm_get_config,
  	eapol_sm_get_bool,
  	eapol_sm_set_bool,
  	eapol_sm_get_int,
  	eapol_sm_set_int,
  	eapol_sm_get_eapReqData,
  	eapol_sm_set_config_blob,
  	eapol_sm_get_config_blob,
  	eapol_sm_notify_pending
  };
  
  
  /**
   * eapol_sm_init - Initialize EAPOL state machine
   * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
   * and EAPOL state machine will free it in eapol_sm_deinit()
   * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
   *
   * Allocate and initialize an EAPOL state machine.
   */
  struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
  {
  	struct eapol_sm *sm;
  	struct eap_config conf;
  	sm = os_zalloc(sizeof(*sm));
  	if (sm == NULL)
  		return NULL;
  	sm->ctx = ctx;
  
  	sm->portControl = Auto;
  
  	/* Supplicant PAE state machine */
  	sm->heldPeriod = 60;
  	sm->startPeriod = 30;
  	sm->maxStart = 3;
  
  	/* Supplicant Backend state machine */
  	sm->authPeriod = 30;
  
  	os_memset(&conf, 0, sizeof(conf));
  	conf.opensc_engine_path = ctx->opensc_engine_path;
  	conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
  	conf.pkcs11_module_path = ctx->pkcs11_module_path;
  
  	sm->eap = eap_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
  	if (sm->eap == NULL) {
  		os_free(sm);
  		return NULL;
  	}
  
  	/* Initialize EAPOL state machines */
  	sm->initialize = TRUE;
  	eapol_sm_step(sm);
  	sm->initialize = FALSE;
  	eapol_sm_step(sm);
  
  	eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
  
  	return sm;
  }
  
  
  /**
   * eapol_sm_deinit - Deinitialize EAPOL state machine
   * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
   *
   * Deinitialize and free EAPOL state machine.
   */
  void eapol_sm_deinit(struct eapol_sm *sm)
  {
  	if (sm == NULL)
  		return;
  	eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
  	eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
  	eap_sm_deinit(sm->eap);
  	os_free(sm->last_rx_key);
  	os_free(sm->eapReqData);
  	os_free(sm->ctx);
  	os_free(sm);
  }
  