2 * WPA Supplicant / EAPOL state machines
3 * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
23 #include "wpa_supplicant.h"
24 #include "l2_packet.h"
30 /* IEEE 802.1aa/D6.1 - Supplicant */
34 unsigned int authWhile;
35 unsigned int heldWhile;
36 unsigned int startWhen;
37 unsigned int idleWhile; /* for EAP state machine */
39 /* Global variables */
46 PortControl portControl;
48 PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */
56 /* Supplicant PAE state machine */
59 SUPP_PAE_DISCONNECTED = 1,
61 SUPP_PAE_CONNECTING = 3,
62 SUPP_PAE_AUTHENTICATING = 4,
63 SUPP_PAE_AUTHENTICATED = 5,
67 SUPP_PAE_S_FORCE_AUTH = 9,
68 SUPP_PAE_S_FORCE_UNAUTH = 10
69 } SUPP_PAE_state; /* dot1xSuppPaeState */
73 unsigned int startCount;
75 PortControl sPortMode;
77 unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
78 unsigned int startPeriod; /* dot1xSuppStartPeriod */
79 unsigned int maxStart; /* dot1xSuppMaxStart */
81 /* Key Receive state machine */
84 KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
89 /* Supplicant Backend state machine */
92 SUPP_BE_INITIALIZE = 1,
100 } SUPP_BE_state; /* dot1xSuppBackendPaeState */
106 unsigned int authPeriod; /* dot1xSuppAuthPeriod */
109 unsigned int dot1xSuppEapolFramesRx;
110 unsigned int dot1xSuppEapolFramesTx;
111 unsigned int dot1xSuppEapolStartFramesTx;
112 unsigned int dot1xSuppEapolLogoffFramesTx;
113 unsigned int dot1xSuppEapolRespFramesTx;
114 unsigned int dot1xSuppEapolReqIdFramesRx;
115 unsigned int dot1xSuppEapolReqFramesRx;
116 unsigned int dot1xSuppInvalidEapolFramesRx;
117 unsigned int dot1xSuppEapLengthErrorFramesRx;
118 unsigned int dot1xSuppLastEapolFrameVersion;
119 unsigned char dot1xSuppLastEapolFrameSource[6];
121 /* Miscellaneous variables (not defined in IEEE 802.1aa/D6.1) */
124 struct wpa_ssid *config;
127 size_t last_rx_key_len;
128 u8 *eapReqData; /* for EAP */
129 size_t eapReqDataLen; /* for EAP */
130 Boolean altAccept; /* for EAP */
131 Boolean altReject; /* for EAP */
132 Boolean replay_counter_valid;
133 u8 last_replay_counter[16];
134 struct eapol_config conf;
135 struct eapol_ctx *ctx;
136 enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
140 Boolean unicast_key_received, broadcast_key_received;
144 static void eapol_sm_txLogoff(struct eapol_sm *sm);
145 static void eapol_sm_txStart(struct eapol_sm *sm);
146 static void eapol_sm_processKey(struct eapol_sm *sm);
147 static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
148 static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
149 static void eapol_sm_abortSupp(struct eapol_sm *sm);
150 static void eapol_sm_abort_cached(struct eapol_sm *sm);
151 static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
153 static struct eapol_callbacks eapol_cb;
156 /* Definitions for clarifying state machine implementation */
157 #define SM_STATE(machine, state) \
158 static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_sm *sm, \
161 #define SM_ENTRY(machine, state) \
162 if (!global || sm->machine ## _state != machine ## _ ## state) { \
163 sm->changed = TRUE; \
164 wpa_printf(MSG_DEBUG, "EAPOL: " #machine " entering state " #state); \
166 sm->machine ## _state = machine ## _ ## state;
168 #define SM_ENTER(machine, state) \
169 sm_ ## machine ## _ ## state ## _Enter(sm, 0)
170 #define SM_ENTER_GLOBAL(machine, state) \
171 sm_ ## machine ## _ ## state ## _Enter(sm, 1)
173 #define SM_STEP(machine) \
174 static void sm_ ## machine ## _Step(struct eapol_sm *sm)
176 #define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
179 /* Port Timers state machine - implemented as a function that will be called
180 * once a second as a registered event loop timeout */
181 static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
183 struct eapol_sm *sm = timeout_ctx;
185 if (sm->authWhile > 0)
187 if (sm->heldWhile > 0)
189 if (sm->startWhen > 0)
191 if (sm->idleWhile > 0)
193 wpa_printf(MSG_MSGDUMP, "EAPOL: Port Timers tick - authWhile=%d "
194 "heldWhile=%d startWhen=%d idleWhile=%d",
195 sm->authWhile, sm->heldWhile, sm->startWhen, sm->idleWhile);
197 eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm);
202 SM_STATE(SUPP_PAE, LOGOFF)
204 SM_ENTRY(SUPP_PAE, LOGOFF);
205 eapol_sm_txLogoff(sm);
206 sm->logoffSent = TRUE;
207 sm->suppPortStatus = Unauthorized;
211 SM_STATE(SUPP_PAE, DISCONNECTED)
213 SM_ENTRY(SUPP_PAE, DISCONNECTED);
214 sm->sPortMode = Auto;
216 sm->logoffSent = FALSE;
217 sm->suppPortStatus = Unauthorized;
218 sm->suppAbort = TRUE;
220 sm->unicast_key_received = FALSE;
221 sm->broadcast_key_received = FALSE;
225 SM_STATE(SUPP_PAE, CONNECTING)
227 SM_ENTRY(SUPP_PAE, CONNECTING);
228 sm->startWhen = sm->startPeriod;
230 sm->eapolEap = FALSE;
231 eapol_sm_txStart(sm);
235 SM_STATE(SUPP_PAE, AUTHENTICATING)
237 SM_ENTRY(SUPP_PAE, AUTHENTICATING);
239 sm->suppSuccess = FALSE;
240 sm->suppFail = FALSE;
241 sm->suppTimeout = FALSE;
244 sm->suppStart = TRUE;
248 SM_STATE(SUPP_PAE, HELD)
250 SM_ENTRY(SUPP_PAE, HELD);
251 sm->heldWhile = sm->heldPeriod;
252 sm->suppPortStatus = Unauthorized;
253 sm->cb_status = EAPOL_CB_FAILURE;
257 SM_STATE(SUPP_PAE, AUTHENTICATED)
259 SM_ENTRY(SUPP_PAE, AUTHENTICATED);
260 sm->suppPortStatus = Authorized;
261 sm->cb_status = EAPOL_CB_SUCCESS;
265 SM_STATE(SUPP_PAE, RESTART)
267 SM_ENTRY(SUPP_PAE, RESTART);
268 sm->eapRestart = TRUE;
272 SM_STATE(SUPP_PAE, S_FORCE_AUTH)
274 SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
275 sm->suppPortStatus = Authorized;
276 sm->sPortMode = ForceAuthorized;
280 SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
282 SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
283 sm->suppPortStatus = Unauthorized;
284 sm->sPortMode = ForceUnauthorized;
285 eapol_sm_txLogoff(sm);
291 if ((sm->userLogoff && !sm->logoffSent) &&
292 !(sm->initialize || !sm->portEnabled))
293 SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
294 else if (((sm->portControl == Auto) &&
295 (sm->sPortMode != sm->portControl)) ||
296 sm->initialize || !sm->portEnabled)
297 SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
298 else if ((sm->portControl == ForceAuthorized) &&
299 (sm->sPortMode != sm->portControl) &&
300 !(sm->initialize || !sm->portEnabled))
301 SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
302 else if ((sm->portControl == ForceUnauthorized) &&
303 (sm->sPortMode != sm->portControl) &&
304 !(sm->initialize || !sm->portEnabled))
305 SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
306 else switch (sm->SUPP_PAE_state) {
307 case SUPP_PAE_UNKNOWN:
309 case SUPP_PAE_LOGOFF:
311 SM_ENTER(SUPP_PAE, DISCONNECTED);
313 case SUPP_PAE_DISCONNECTED:
314 SM_ENTER(SUPP_PAE, CONNECTING);
316 case SUPP_PAE_CONNECTING:
317 if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
318 SM_ENTER(SUPP_PAE, CONNECTING);
319 else if (sm->startWhen == 0 &&
320 sm->startCount >= sm->maxStart &&
322 SM_ENTER(SUPP_PAE, AUTHENTICATED);
323 else if (sm->eapSuccess || sm->eapFail)
324 SM_ENTER(SUPP_PAE, AUTHENTICATING);
325 else if (sm->eapolEap)
326 SM_ENTER(SUPP_PAE, RESTART);
327 else if (sm->startWhen == 0 &&
328 sm->startCount >= sm->maxStart &&
330 SM_ENTER(SUPP_PAE, HELD);
332 case SUPP_PAE_AUTHENTICATING:
333 if (sm->eapSuccess && !sm->portValid &&
334 sm->conf.accept_802_1x_keys &&
335 sm->conf.required_keys == 0) {
336 wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
337 "plaintext connection; no EAPOL-Key frames "
339 sm->portValid = TRUE;
340 if (sm->ctx->eapol_done_cb)
341 sm->ctx->eapol_done_cb(sm->ctx->ctx);
343 if (sm->eapSuccess && sm->portValid)
344 SM_ENTER(SUPP_PAE, AUTHENTICATED);
345 else if (sm->eapFail || (sm->keyDone && !sm->portValid))
346 SM_ENTER(SUPP_PAE, HELD);
347 else if (sm->suppTimeout)
348 SM_ENTER(SUPP_PAE, CONNECTING);
351 if (sm->heldWhile == 0)
352 SM_ENTER(SUPP_PAE, CONNECTING);
353 else if (sm->eapolEap)
354 SM_ENTER(SUPP_PAE, RESTART);
356 case SUPP_PAE_AUTHENTICATED:
357 if (sm->eapolEap && sm->portValid)
358 SM_ENTER(SUPP_PAE, RESTART);
359 else if (!sm->portValid)
360 SM_ENTER(SUPP_PAE, DISCONNECTED);
362 case SUPP_PAE_RESTART:
364 SM_ENTER(SUPP_PAE, AUTHENTICATING);
366 case SUPP_PAE_S_FORCE_AUTH:
368 case SUPP_PAE_S_FORCE_UNAUTH:
374 SM_STATE(KEY_RX, NO_KEY_RECEIVE)
376 SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
380 SM_STATE(KEY_RX, KEY_RECEIVE)
382 SM_ENTRY(KEY_RX, KEY_RECEIVE);
383 eapol_sm_processKey(sm);
390 if (sm->initialize || !sm->portEnabled)
391 SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
392 switch (sm->KEY_RX_state) {
395 case KEY_RX_NO_KEY_RECEIVE:
397 SM_ENTER(KEY_RX, KEY_RECEIVE);
399 case KEY_RX_KEY_RECEIVE:
401 SM_ENTER(KEY_RX, KEY_RECEIVE);
407 SM_STATE(SUPP_BE, REQUEST)
409 SM_ENTRY(SUPP_BE, REQUEST);
412 eapol_sm_getSuppRsp(sm);
416 SM_STATE(SUPP_BE, RESPONSE)
418 SM_ENTRY(SUPP_BE, RESPONSE);
419 eapol_sm_txSuppRsp(sm);
424 SM_STATE(SUPP_BE, SUCCESS)
426 SM_ENTRY(SUPP_BE, SUCCESS);
428 sm->suppSuccess = TRUE;
430 if (eap_key_available(sm->eap)) {
431 /* New key received - clear IEEE 802.1X EAPOL-Key replay
433 sm->replay_counter_valid = FALSE;
438 SM_STATE(SUPP_BE, FAIL)
440 SM_ENTRY(SUPP_BE, FAIL);
445 SM_STATE(SUPP_BE, TIMEOUT)
447 SM_ENTRY(SUPP_BE, TIMEOUT);
448 sm->suppTimeout = TRUE;
452 SM_STATE(SUPP_BE, IDLE)
454 SM_ENTRY(SUPP_BE, IDLE);
455 sm->suppStart = FALSE;
456 sm->initial_req = TRUE;
460 SM_STATE(SUPP_BE, INITIALIZE)
462 SM_ENTRY(SUPP_BE, INITIALIZE);
463 eapol_sm_abortSupp(sm);
464 sm->suppAbort = FALSE;
468 SM_STATE(SUPP_BE, RECEIVE)
470 SM_ENTRY(SUPP_BE, RECEIVE);
471 sm->authWhile = sm->authPeriod;
472 sm->eapolEap = FALSE;
473 sm->eapNoResp = FALSE;
474 sm->initial_req = FALSE;
480 if (sm->initialize || sm->suppAbort)
481 SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
482 else switch (sm->SUPP_BE_state) {
483 case SUPP_BE_UNKNOWN:
485 case SUPP_BE_REQUEST:
486 if (sm->eapResp && sm->eapNoResp) {
487 wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
488 "eapResp and eapNoResp set?!");
491 SM_ENTER(SUPP_BE, RESPONSE);
492 else if (sm->eapNoResp)
493 SM_ENTER(SUPP_BE, RECEIVE);
495 case SUPP_BE_RESPONSE:
496 SM_ENTER(SUPP_BE, RECEIVE);
498 case SUPP_BE_SUCCESS:
499 SM_ENTER(SUPP_BE, IDLE);
502 SM_ENTER(SUPP_BE, IDLE);
504 case SUPP_BE_TIMEOUT:
505 SM_ENTER(SUPP_BE, IDLE);
508 if (sm->eapFail && sm->suppStart)
509 SM_ENTER(SUPP_BE, FAIL);
510 else if (sm->eapolEap && sm->suppStart)
511 SM_ENTER(SUPP_BE, REQUEST);
512 else if (sm->eapSuccess && sm->suppStart)
513 SM_ENTER(SUPP_BE, SUCCESS);
515 case SUPP_BE_INITIALIZE:
516 SM_ENTER(SUPP_BE, IDLE);
518 case SUPP_BE_RECEIVE:
520 SM_ENTER(SUPP_BE, REQUEST);
521 else if (sm->eapFail)
522 SM_ENTER(SUPP_BE, FAIL);
523 else if (sm->authWhile == 0)
524 SM_ENTER(SUPP_BE, TIMEOUT);
525 else if (sm->eapSuccess)
526 SM_ENTER(SUPP_BE, SUCCESS);
532 static void eapol_sm_txLogoff(struct eapol_sm *sm)
534 wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
535 sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_LOGOFF,
537 sm->dot1xSuppEapolLogoffFramesTx++;
538 sm->dot1xSuppEapolFramesTx++;
542 static void eapol_sm_txStart(struct eapol_sm *sm)
544 wpa_printf(MSG_DEBUG, "EAPOL: txStart");
545 sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_START,
547 sm->dot1xSuppEapolStartFramesTx++;
548 sm->dot1xSuppEapolFramesTx++;
552 #define IEEE8021X_ENCR_KEY_LEN 32
553 #define IEEE8021X_SIGN_KEY_LEN 32
555 struct eap_key_data {
556 u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
557 u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
561 static void eapol_sm_processKey(struct eapol_sm *sm)
563 struct ieee802_1x_hdr *hdr;
564 struct ieee802_1x_eapol_key *key;
565 struct eap_key_data keydata;
566 u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
567 u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
568 int key_len, res, sign_key_len, encr_key_len;
570 wpa_printf(MSG_DEBUG, "EAPOL: processKey");
571 if (sm->last_rx_key == NULL)
574 if (!sm->conf.accept_802_1x_keys) {
575 wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
576 " even though this was not accepted - "
577 "ignoring this packet");
581 hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
582 key = (struct ieee802_1x_eapol_key *) (hdr + 1);
583 if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
584 wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
587 wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
588 "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
589 hdr->version, hdr->type, be_to_host16(hdr->length),
590 key->type, be_to_host16(key->key_length), key->key_index);
592 sign_key_len = IEEE8021X_SIGN_KEY_LEN;
593 encr_key_len = IEEE8021X_ENCR_KEY_LEN;
594 res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
596 wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
597 "decrypting EAPOL-Key keys");
601 /* LEAP derives only 16 bytes of keying material. */
602 res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
604 wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
605 "master key for decrypting EAPOL-Key keys");
610 memcpy(keydata.sign_key, keydata.encr_key, 16);
612 wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
613 "data for decrypting EAPOL-Key keys (res=%d)", res);
617 /* The key replay_counter must increase when same master key */
618 if (sm->replay_counter_valid &&
619 memcmp(sm->last_replay_counter, key->replay_counter,
620 IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
621 wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
622 "not increase - ignoring key");
623 wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
624 sm->last_replay_counter,
625 IEEE8021X_REPLAY_COUNTER_LEN);
626 wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
627 key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
631 /* Verify key signature (HMAC-MD5) */
632 memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
633 memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
634 hmac_md5(keydata.sign_key, sign_key_len,
635 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
637 if (memcmp(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN)
639 wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
641 memcpy(key->key_signature, orig_key_sign,
642 IEEE8021X_KEY_SIGN_LEN);
645 wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
647 key_len = be_to_host16(hdr->length) - sizeof(*key);
648 if (key_len > 32 || be_to_host16(key->key_length) > 32) {
649 wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
650 key_len ? key_len : be_to_host16(key->key_length));
653 if (key_len == be_to_host16(key->key_length)) {
654 memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
655 memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
657 memcpy(datakey, key + 1, key_len);
658 rc4(datakey, key_len, ekey,
659 IEEE8021X_KEY_IV_LEN + encr_key_len);
660 wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
662 } else if (key_len == 0) {
663 /* IEEE 802.1X-REV specifies that least significant Key Length
664 * octets from MS-MPPE-Send-Key are used as the key if the key
665 * data is not present. This seems to be meaning the beginning
666 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
667 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
668 * Anyway, taking the beginning of the keying material from EAP
669 * seems to interoperate with Authenticators. */
670 key_len = be_to_host16(key->key_length);
671 memcpy(datakey, keydata.encr_key, key_len);
672 wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
673 "material data encryption key",
676 wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
677 "(key_length=%d)", key_len,
678 be_to_host16(key->key_length));
682 sm->replay_counter_valid = TRUE;
683 memcpy(sm->last_replay_counter, key->replay_counter,
684 IEEE8021X_REPLAY_COUNTER_LEN);
686 wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
688 key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
689 "unicast" : "broadcast",
690 key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
692 if (sm->ctx->set_wep_key &&
693 sm->ctx->set_wep_key(sm->ctx->ctx,
694 key->key_index & IEEE8021X_KEY_INDEX_FLAG,
695 key->key_index & IEEE8021X_KEY_INDEX_MASK,
696 datakey, key_len) < 0) {
697 wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
700 if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
701 sm->unicast_key_received = TRUE;
703 sm->broadcast_key_received = TRUE;
705 if ((sm->unicast_key_received ||
706 !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
707 (sm->broadcast_key_received ||
708 !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
710 wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
712 sm->portValid = TRUE;
713 if (sm->ctx->eapol_done_cb)
714 sm->ctx->eapol_done_cb(sm->ctx->ctx);
720 static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
722 wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
723 /* EAP layer processing; no special code is needed, since Supplicant
724 * Backend state machine is waiting for eapNoResp or eapResp to be set
725 * and these are only set in the EAP state machine when the processing
730 static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
735 wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
736 resp = eap_get_eapRespData(sm->eap, &resp_len);
738 wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
743 /* Send EAP-Packet from the EAP layer to the Authenticator */
744 sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAP_PACKET,
747 /* eapRespData is not used anymore, so free it here */
751 sm->dot1xSuppEapolReqIdFramesRx++;
753 sm->dot1xSuppEapolReqFramesRx++;
754 sm->dot1xSuppEapolRespFramesTx++;
755 sm->dot1xSuppEapolFramesTx++;
759 static void eapol_sm_abortSupp(struct eapol_sm *sm)
761 /* release system resources that may have been allocated for the
762 * authentication session */
763 free(sm->last_rx_key);
764 sm->last_rx_key = NULL;
765 free(sm->eapReqData);
766 sm->eapReqData = NULL;
767 eap_sm_abort(sm->eap);
771 struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
774 sm = malloc(sizeof(*sm));
777 memset(sm, 0, sizeof(*sm));
780 sm->portControl = Auto;
782 /* Supplicant PAE state machine */
784 sm->startPeriod = 30;
787 /* Supplicant Backend state machine */
790 sm->eap = eap_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx);
791 if (sm->eap == NULL) {
796 /* Initialize EAPOL state machines */
797 sm->initialize = TRUE;
799 sm->initialize = FALSE;
802 eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
808 void eapol_sm_deinit(struct eapol_sm *sm)
812 eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
813 eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
814 eap_sm_deinit(sm->eap);
815 free(sm->last_rx_key);
816 free(sm->eapReqData);
822 static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
824 eapol_sm_step(timeout_ctx);
828 void eapol_sm_step(struct eapol_sm *sm)
832 /* In theory, it should be ok to run this in loop until !changed.
833 * However, it is better to use a limit on number of iterations to
834 * allow events (e.g., SIGTERM) to stop the program cleanly if the
835 * state machine were to generate a busy loop. */
836 for (i = 0; i < 100; i++) {
838 SM_STEP_RUN(SUPP_PAE);
840 SM_STEP_RUN(SUPP_BE);
841 if (eap_sm_step(sm->eap))
846 /* restart EAPOL state machine step from timeout call in order
847 * to allow other events to be processed. */
848 eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
849 eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
852 if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
853 int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
854 sm->cb_status = EAPOL_CB_IN_PROGRESS;
855 sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
860 static const char *eapol_supp_pae_state(int state)
863 case SUPP_PAE_LOGOFF:
865 case SUPP_PAE_DISCONNECTED:
866 return "DISCONNECTED";
867 case SUPP_PAE_CONNECTING:
869 case SUPP_PAE_AUTHENTICATING:
870 return "AUTHENTICATING";
873 case SUPP_PAE_AUTHENTICATED:
874 return "AUTHENTICATED";
875 case SUPP_PAE_RESTART:
883 static const char *eapol_supp_be_state(int state)
886 case SUPP_BE_REQUEST:
888 case SUPP_BE_RESPONSE:
890 case SUPP_BE_SUCCESS:
894 case SUPP_BE_TIMEOUT:
898 case SUPP_BE_INITIALIZE:
900 case SUPP_BE_RECEIVE:
908 static const char * eapol_port_status(PortStatus status)
910 if (status == Authorized)
913 return "Unauthorized";
917 static const char * eapol_port_control(PortControl ctrl)
922 case ForceUnauthorized:
923 return "ForceUnauthorized";
924 case ForceAuthorized:
925 return "ForceAuthorized";
932 void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
933 int startPeriod, int maxStart)
938 sm->heldPeriod = heldPeriod;
940 sm->authPeriod = authPeriod;
941 if (startPeriod >= 0)
942 sm->startPeriod = startPeriod;
944 sm->maxStart = maxStart;
948 int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
955 len = snprintf(buf, buflen,
956 "Supplicant PAE state=%s\n"
957 "suppPortStatus=%s\n",
958 eapol_supp_pae_state(sm->SUPP_PAE_state),
959 eapol_port_status(sm->suppPortStatus));
962 len += snprintf(buf + len, buflen - len,
968 "Supplicant Backend state=%s\n",
973 eapol_port_control(sm->portControl),
974 eapol_supp_be_state(sm->SUPP_BE_state));
977 len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
983 int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
988 len = snprintf(buf, buflen,
989 "dot1xSuppPaeState=%d\n"
990 "dot1xSuppHeldPeriod=%d\n"
991 "dot1xSuppAuthPeriod=%d\n"
992 "dot1xSuppStartPeriod=%d\n"
993 "dot1xSuppMaxStart=%d\n"
994 "dot1xSuppSuppControlledPortStatus=%s\n"
995 "dot1xSuppBackendPaeState=%d\n"
996 "dot1xSuppEapolFramesRx=%d\n"
997 "dot1xSuppEapolFramesTx=%d\n"
998 "dot1xSuppEapolStartFramesTx=%d\n"
999 "dot1xSuppEapolLogoffFramesTx=%d\n"
1000 "dot1xSuppEapolRespFramesTx=%d\n"
1001 "dot1xSuppEapolReqIdFramesRx=%d\n"
1002 "dot1xSuppEapolReqFramesRx=%d\n"
1003 "dot1xSuppInvalidEapolFramesRx=%d\n"
1004 "dot1xSuppEapLengthErrorFramesRx=%d\n"
1005 "dot1xSuppLastEapolFrameVersion=%d\n"
1006 "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
1012 sm->suppPortStatus == Authorized ?
1013 "Authorized" : "Unauthorized",
1015 sm->dot1xSuppEapolFramesRx,
1016 sm->dot1xSuppEapolFramesTx,
1017 sm->dot1xSuppEapolStartFramesTx,
1018 sm->dot1xSuppEapolLogoffFramesTx,
1019 sm->dot1xSuppEapolRespFramesTx,
1020 sm->dot1xSuppEapolReqIdFramesRx,
1021 sm->dot1xSuppEapolReqFramesRx,
1022 sm->dot1xSuppInvalidEapolFramesRx,
1023 sm->dot1xSuppEapLengthErrorFramesRx,
1024 sm->dot1xSuppLastEapolFrameVersion,
1025 MAC2STR(sm->dot1xSuppLastEapolFrameSource));
1030 void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len)
1032 struct ieee802_1x_hdr *hdr;
1033 struct ieee802_1x_eapol_key *key;
1038 sm->dot1xSuppEapolFramesRx++;
1039 if (len < sizeof(*hdr)) {
1040 sm->dot1xSuppInvalidEapolFramesRx++;
1043 hdr = (struct ieee802_1x_hdr *) buf;
1044 sm->dot1xSuppLastEapolFrameVersion = hdr->version;
1045 memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
1046 if (hdr->version < EAPOL_VERSION) {
1047 /* TODO: backwards compatibility */
1049 plen = be_to_host16(hdr->length);
1050 if (plen > len - sizeof(*hdr)) {
1051 sm->dot1xSuppEapLengthErrorFramesRx++;
1054 data_len = plen + sizeof(*hdr);
1056 switch (hdr->type) {
1057 case IEEE802_1X_TYPE_EAP_PACKET:
1058 if (sm->cached_pmk) {
1059 /* Trying to use PMKSA caching, but Authenticator did
1060 * not seem to have a matching entry. Need to restart
1061 * EAPOL state machines.
1063 eapol_sm_abort_cached(sm);
1065 free(sm->eapReqData);
1066 sm->eapReqDataLen = plen;
1067 sm->eapReqData = malloc(sm->eapReqDataLen);
1068 if (sm->eapReqData) {
1069 wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
1071 memcpy(sm->eapReqData, (u8 *) (hdr + 1),
1073 sm->eapolEap = TRUE;
1077 case IEEE802_1X_TYPE_EAPOL_KEY:
1078 if (plen < sizeof(*key)) {
1079 wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
1083 key = (struct ieee802_1x_eapol_key *) (hdr + 1);
1084 if (key->type == EAPOL_KEY_TYPE_WPA ||
1085 key->type == EAPOL_KEY_TYPE_RSN) {
1086 /* WPA Supplicant takes care of this frame. */
1087 wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
1088 "frame in EAPOL state machines");
1091 if (key->type != EAPOL_KEY_TYPE_RC4) {
1092 wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
1093 "EAPOL-Key type %d", key->type);
1096 free(sm->last_rx_key);
1097 sm->last_rx_key = malloc(data_len);
1098 if (sm->last_rx_key) {
1099 wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
1101 memcpy(sm->last_rx_key, buf, data_len);
1102 sm->last_rx_key_len = data_len;
1108 wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
1110 sm->dot1xSuppInvalidEapolFramesRx++;
1116 void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
1119 sm->dot1xSuppEapolFramesTx++;
1123 void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
1127 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1128 "portEnabled=%d", enabled);
1129 sm->portEnabled = enabled;
1134 void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
1138 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1139 "portValid=%d", valid);
1140 sm->portValid = valid;
1145 void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
1149 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1150 "EAP success=%d", success);
1151 sm->eapSuccess = success;
1152 sm->altAccept = success;
1154 eap_notify_success(sm->eap);
1159 void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
1163 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1164 "EAP fail=%d", fail);
1166 sm->altReject = fail;
1171 void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config,
1172 struct eapol_config *conf)
1177 sm->config = config;
1182 sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
1183 sm->conf.required_keys = conf->required_keys;
1184 sm->conf.fast_reauth = conf->fast_reauth;
1186 eap_set_fast_reauth(sm->eap, conf->fast_reauth);
1187 eap_set_workaround(sm->eap, conf->workaround);
1192 int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
1197 if (sm == NULL || !eap_key_available(sm->eap))
1199 eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
1200 if (eap_key == NULL)
1204 memcpy(key, eap_key, len);
1209 void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
1212 sm->userLogoff = logoff;
1218 void eapol_sm_notify_cached(struct eapol_sm *sm)
1222 sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
1223 sm->suppPortStatus = Authorized;
1224 eap_notify_success(sm->eap);
1228 void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
1233 wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
1234 sm->cached_pmk = TRUE;
1236 wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
1237 sm->cached_pmk = FALSE;
1242 static void eapol_sm_abort_cached(struct eapol_sm *sm)
1244 wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
1245 "doing full EAP authentication");
1248 sm->cached_pmk = FALSE;
1249 sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
1250 sm->suppPortStatus = Unauthorized;
1251 sm->eapRestart= TRUE;
1255 void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
1258 sm->ctx->scard_ctx = ctx;
1259 eap_register_scard_ctx(sm->eap, ctx);
1264 void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
1268 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1269 "portControl=%s", eapol_port_control(portControl));
1270 sm->portControl = portControl;
1275 void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
1279 eap_sm_notify_ctrl_attached(sm->eap);
1283 void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
1287 if (sm->eapReqData && !sm->eapReq) {
1288 wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
1289 "input) notification - retrying pending EAP "
1291 sm->eapolEap = TRUE;
1298 static struct wpa_ssid * eapol_sm_get_config(void *ctx)
1300 struct eapol_sm *sm = ctx;
1301 return sm ? sm->config : NULL;
1305 static u8 * eapol_sm_get_eapReqData(void *ctx, size_t *len)
1307 struct eapol_sm *sm = ctx;
1308 if (sm == NULL || sm->eapReqData == NULL) {
1313 *len = sm->eapReqDataLen;
1314 return sm->eapReqData;
1318 static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
1320 struct eapol_sm *sm = ctx;
1324 case EAPOL_eapSuccess:
1325 return sm->eapSuccess;
1326 case EAPOL_eapRestart:
1327 return sm->eapRestart;
1332 case EAPOL_eapNoResp:
1333 return sm->eapNoResp;
1336 case EAPOL_portEnabled:
1337 return sm->portEnabled;
1338 case EAPOL_altAccept:
1339 return sm->altAccept;
1340 case EAPOL_altReject:
1341 return sm->altReject;
1347 static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
1350 struct eapol_sm *sm = ctx;
1354 case EAPOL_eapSuccess:
1355 sm->eapSuccess = value;
1357 case EAPOL_eapRestart:
1358 sm->eapRestart = value;
1361 sm->eapFail = value;
1364 sm->eapResp = value;
1366 case EAPOL_eapNoResp:
1367 sm->eapNoResp = value;
1372 case EAPOL_portEnabled:
1373 sm->portEnabled = value;
1375 case EAPOL_altAccept:
1376 sm->altAccept = value;
1378 case EAPOL_altReject:
1379 sm->altReject = value;
1385 static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
1387 struct eapol_sm *sm = ctx;
1391 case EAPOL_idleWhile:
1392 return sm->idleWhile;
1398 static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
1401 struct eapol_sm *sm = ctx;
1405 case EAPOL_idleWhile:
1406 sm->idleWhile = value;
1412 static struct eapol_callbacks eapol_cb =
1414 .get_config = eapol_sm_get_config,
1415 .get_bool = eapol_sm_get_bool,
1416 .set_bool = eapol_sm_set_bool,
1417 .get_int = eapol_sm_get_int,
1418 .set_int = eapol_sm_set_int,
1419 .get_eapReqData = eapol_sm_get_eapReqData,