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);
199 eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm);
203 SM_STATE(SUPP_PAE, LOGOFF)
205 SM_ENTRY(SUPP_PAE, LOGOFF);
206 eapol_sm_txLogoff(sm);
207 sm->logoffSent = TRUE;
208 sm->suppPortStatus = Unauthorized;
212 SM_STATE(SUPP_PAE, DISCONNECTED)
214 SM_ENTRY(SUPP_PAE, DISCONNECTED);
215 sm->sPortMode = Auto;
217 sm->logoffSent = FALSE;
218 sm->suppPortStatus = Unauthorized;
219 sm->suppAbort = TRUE;
221 sm->unicast_key_received = FALSE;
222 sm->broadcast_key_received = FALSE;
226 SM_STATE(SUPP_PAE, CONNECTING)
228 SM_ENTRY(SUPP_PAE, CONNECTING);
229 sm->startWhen = sm->startPeriod;
231 sm->eapolEap = FALSE;
232 eapol_sm_txStart(sm);
236 SM_STATE(SUPP_PAE, AUTHENTICATING)
238 SM_ENTRY(SUPP_PAE, AUTHENTICATING);
240 sm->suppSuccess = FALSE;
241 sm->suppFail = FALSE;
242 sm->suppTimeout = FALSE;
245 sm->suppStart = TRUE;
249 SM_STATE(SUPP_PAE, HELD)
251 SM_ENTRY(SUPP_PAE, HELD);
252 sm->heldWhile = sm->heldPeriod;
253 sm->suppPortStatus = Unauthorized;
254 sm->cb_status = EAPOL_CB_FAILURE;
258 SM_STATE(SUPP_PAE, AUTHENTICATED)
260 SM_ENTRY(SUPP_PAE, AUTHENTICATED);
261 sm->suppPortStatus = Authorized;
262 sm->cb_status = EAPOL_CB_SUCCESS;
266 SM_STATE(SUPP_PAE, RESTART)
268 SM_ENTRY(SUPP_PAE, RESTART);
269 sm->eapRestart = TRUE;
273 SM_STATE(SUPP_PAE, S_FORCE_AUTH)
275 SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
276 sm->suppPortStatus = Authorized;
277 sm->sPortMode = ForceAuthorized;
281 SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
283 SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
284 sm->suppPortStatus = Unauthorized;
285 sm->sPortMode = ForceUnauthorized;
286 eapol_sm_txLogoff(sm);
292 if ((sm->userLogoff && !sm->logoffSent) &&
293 !(sm->initialize || !sm->portEnabled))
294 SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
295 else if (((sm->portControl == Auto) &&
296 (sm->sPortMode != sm->portControl)) ||
297 sm->initialize || !sm->portEnabled)
298 SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
299 else if ((sm->portControl == ForceAuthorized) &&
300 (sm->sPortMode != sm->portControl) &&
301 !(sm->initialize || !sm->portEnabled))
302 SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
303 else if ((sm->portControl == ForceUnauthorized) &&
304 (sm->sPortMode != sm->portControl) &&
305 !(sm->initialize || !sm->portEnabled))
306 SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
307 else switch (sm->SUPP_PAE_state) {
308 case SUPP_PAE_UNKNOWN:
310 case SUPP_PAE_LOGOFF:
312 SM_ENTER(SUPP_PAE, DISCONNECTED);
314 case SUPP_PAE_DISCONNECTED:
315 SM_ENTER(SUPP_PAE, CONNECTING);
317 case SUPP_PAE_CONNECTING:
318 if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
319 SM_ENTER(SUPP_PAE, CONNECTING);
320 else if (sm->startWhen == 0 &&
321 sm->startCount >= sm->maxStart &&
323 SM_ENTER(SUPP_PAE, AUTHENTICATED);
324 else if (sm->eapSuccess || sm->eapFail)
325 SM_ENTER(SUPP_PAE, AUTHENTICATING);
326 else if (sm->eapolEap)
327 SM_ENTER(SUPP_PAE, RESTART);
328 else if (sm->startWhen == 0 &&
329 sm->startCount >= sm->maxStart &&
331 SM_ENTER(SUPP_PAE, HELD);
333 case SUPP_PAE_AUTHENTICATING:
334 if (sm->eapSuccess && !sm->portValid &&
335 sm->conf.accept_802_1x_keys &&
336 sm->conf.required_keys == 0) {
337 wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
338 "plaintext connection; no EAPOL-Key frames "
340 sm->portValid = TRUE;
341 if (sm->ctx->eapol_done_cb)
342 sm->ctx->eapol_done_cb(sm->ctx->ctx);
344 if (sm->eapSuccess && sm->portValid)
345 SM_ENTER(SUPP_PAE, AUTHENTICATED);
346 else if (sm->eapFail || (sm->keyDone && !sm->portValid))
347 SM_ENTER(SUPP_PAE, HELD);
348 else if (sm->suppTimeout)
349 SM_ENTER(SUPP_PAE, CONNECTING);
352 if (sm->heldWhile == 0)
353 SM_ENTER(SUPP_PAE, CONNECTING);
354 else if (sm->eapolEap)
355 SM_ENTER(SUPP_PAE, RESTART);
357 case SUPP_PAE_AUTHENTICATED:
358 if (sm->eapolEap && sm->portValid)
359 SM_ENTER(SUPP_PAE, RESTART);
360 else if (!sm->portValid)
361 SM_ENTER(SUPP_PAE, DISCONNECTED);
363 case SUPP_PAE_RESTART:
365 SM_ENTER(SUPP_PAE, AUTHENTICATING);
367 case SUPP_PAE_S_FORCE_AUTH:
369 case SUPP_PAE_S_FORCE_UNAUTH:
375 SM_STATE(KEY_RX, NO_KEY_RECEIVE)
377 SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
381 SM_STATE(KEY_RX, KEY_RECEIVE)
383 SM_ENTRY(KEY_RX, KEY_RECEIVE);
384 eapol_sm_processKey(sm);
391 if (sm->initialize || !sm->portEnabled)
392 SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
393 switch (sm->KEY_RX_state) {
396 case KEY_RX_NO_KEY_RECEIVE:
398 SM_ENTER(KEY_RX, KEY_RECEIVE);
400 case KEY_RX_KEY_RECEIVE:
402 SM_ENTER(KEY_RX, KEY_RECEIVE);
408 SM_STATE(SUPP_BE, REQUEST)
410 SM_ENTRY(SUPP_BE, REQUEST);
413 eapol_sm_getSuppRsp(sm);
417 SM_STATE(SUPP_BE, RESPONSE)
419 SM_ENTRY(SUPP_BE, RESPONSE);
420 eapol_sm_txSuppRsp(sm);
425 SM_STATE(SUPP_BE, SUCCESS)
427 SM_ENTRY(SUPP_BE, SUCCESS);
429 sm->suppSuccess = TRUE;
431 if (eap_key_available(sm->eap)) {
432 /* New key received - clear IEEE 802.1X EAPOL-Key replay
434 sm->replay_counter_valid = FALSE;
439 SM_STATE(SUPP_BE, FAIL)
441 SM_ENTRY(SUPP_BE, FAIL);
446 SM_STATE(SUPP_BE, TIMEOUT)
448 SM_ENTRY(SUPP_BE, TIMEOUT);
449 sm->suppTimeout = TRUE;
453 SM_STATE(SUPP_BE, IDLE)
455 SM_ENTRY(SUPP_BE, IDLE);
456 sm->suppStart = FALSE;
457 sm->initial_req = TRUE;
461 SM_STATE(SUPP_BE, INITIALIZE)
463 SM_ENTRY(SUPP_BE, INITIALIZE);
464 eapol_sm_abortSupp(sm);
465 sm->suppAbort = FALSE;
469 SM_STATE(SUPP_BE, RECEIVE)
471 SM_ENTRY(SUPP_BE, RECEIVE);
472 sm->authWhile = sm->authPeriod;
473 sm->eapolEap = FALSE;
474 sm->eapNoResp = FALSE;
475 sm->initial_req = FALSE;
481 if (sm->initialize || sm->suppAbort)
482 SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
483 else switch (sm->SUPP_BE_state) {
484 case SUPP_BE_UNKNOWN:
486 case SUPP_BE_REQUEST:
487 if (sm->eapResp && sm->eapNoResp) {
488 wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
489 "eapResp and eapNoResp set?!");
492 SM_ENTER(SUPP_BE, RESPONSE);
493 else if (sm->eapNoResp)
494 SM_ENTER(SUPP_BE, RECEIVE);
496 case SUPP_BE_RESPONSE:
497 SM_ENTER(SUPP_BE, RECEIVE);
499 case SUPP_BE_SUCCESS:
500 SM_ENTER(SUPP_BE, IDLE);
503 SM_ENTER(SUPP_BE, IDLE);
505 case SUPP_BE_TIMEOUT:
506 SM_ENTER(SUPP_BE, IDLE);
509 if (sm->eapFail && sm->suppStart)
510 SM_ENTER(SUPP_BE, FAIL);
511 else if (sm->eapolEap && sm->suppStart)
512 SM_ENTER(SUPP_BE, REQUEST);
513 else if (sm->eapSuccess && sm->suppStart)
514 SM_ENTER(SUPP_BE, SUCCESS);
516 case SUPP_BE_INITIALIZE:
517 SM_ENTER(SUPP_BE, IDLE);
519 case SUPP_BE_RECEIVE:
521 SM_ENTER(SUPP_BE, REQUEST);
522 else if (sm->eapFail)
523 SM_ENTER(SUPP_BE, FAIL);
524 else if (sm->authWhile == 0)
525 SM_ENTER(SUPP_BE, TIMEOUT);
526 else if (sm->eapSuccess)
527 SM_ENTER(SUPP_BE, SUCCESS);
533 static void eapol_sm_txLogoff(struct eapol_sm *sm)
535 wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
536 sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_LOGOFF,
538 sm->dot1xSuppEapolLogoffFramesTx++;
539 sm->dot1xSuppEapolFramesTx++;
543 static void eapol_sm_txStart(struct eapol_sm *sm)
545 wpa_printf(MSG_DEBUG, "EAPOL: txStart");
546 sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAPOL_START,
548 sm->dot1xSuppEapolStartFramesTx++;
549 sm->dot1xSuppEapolFramesTx++;
553 #define IEEE8021X_ENCR_KEY_LEN 32
554 #define IEEE8021X_SIGN_KEY_LEN 32
556 struct eap_key_data {
557 u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
558 u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
562 static void eapol_sm_processKey(struct eapol_sm *sm)
564 struct ieee802_1x_hdr *hdr;
565 struct ieee802_1x_eapol_key *key;
566 struct eap_key_data keydata;
567 u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
568 u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
569 int key_len, res, sign_key_len, encr_key_len;
571 wpa_printf(MSG_DEBUG, "EAPOL: processKey");
572 if (sm->last_rx_key == NULL)
575 if (!sm->conf.accept_802_1x_keys) {
576 wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
577 " even though this was not accepted - "
578 "ignoring this packet");
582 hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
583 key = (struct ieee802_1x_eapol_key *) (hdr + 1);
584 if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
585 wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
588 wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
589 "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
590 hdr->version, hdr->type, be_to_host16(hdr->length),
591 key->type, be_to_host16(key->key_length), key->key_index);
593 sign_key_len = IEEE8021X_SIGN_KEY_LEN;
594 encr_key_len = IEEE8021X_ENCR_KEY_LEN;
595 res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
597 wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
598 "decrypting EAPOL-Key keys");
602 /* LEAP derives only 16 bytes of keying material. */
603 res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
605 wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
606 "master key for decrypting EAPOL-Key keys");
611 memcpy(keydata.sign_key, keydata.encr_key, 16);
613 wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
614 "data for decrypting EAPOL-Key keys (res=%d)", res);
618 /* The key replay_counter must increase when same master key */
619 if (sm->replay_counter_valid &&
620 memcmp(sm->last_replay_counter, key->replay_counter,
621 IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
622 wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
623 "not increase - ignoring key");
624 wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
625 sm->last_replay_counter,
626 IEEE8021X_REPLAY_COUNTER_LEN);
627 wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
628 key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
632 /* Verify key signature (HMAC-MD5) */
633 memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
634 memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
635 hmac_md5(keydata.sign_key, sign_key_len,
636 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
638 if (memcmp(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN)
640 wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
642 memcpy(key->key_signature, orig_key_sign,
643 IEEE8021X_KEY_SIGN_LEN);
646 wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
648 key_len = be_to_host16(hdr->length) - sizeof(*key);
649 if (key_len > 32 || be_to_host16(key->key_length) > 32) {
650 wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
651 key_len ? key_len : be_to_host16(key->key_length));
654 if (key_len == be_to_host16(key->key_length)) {
655 memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
656 memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
658 memcpy(datakey, key + 1, key_len);
659 rc4(datakey, key_len, ekey,
660 IEEE8021X_KEY_IV_LEN + encr_key_len);
661 wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
663 } else if (key_len == 0) {
664 /* IEEE 802.1X-REV specifies that least significant Key Length
665 * octets from MS-MPPE-Send-Key are used as the key if the key
666 * data is not present. This seems to be meaning the beginning
667 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
668 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
669 * Anyway, taking the beginning of the keying material from EAP
670 * seems to interoperate with Authenticators. */
671 key_len = be_to_host16(key->key_length);
672 memcpy(datakey, keydata.encr_key, key_len);
673 wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
674 "material data encryption key",
677 wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
678 "(key_length=%d)", key_len,
679 be_to_host16(key->key_length));
683 sm->replay_counter_valid = TRUE;
684 memcpy(sm->last_replay_counter, key->replay_counter,
685 IEEE8021X_REPLAY_COUNTER_LEN);
687 wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
689 key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
690 "unicast" : "broadcast",
691 key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
693 if (sm->ctx->set_wep_key &&
694 sm->ctx->set_wep_key(sm->ctx->ctx,
695 key->key_index & IEEE8021X_KEY_INDEX_FLAG,
696 key->key_index & IEEE8021X_KEY_INDEX_MASK,
697 datakey, key_len) < 0) {
698 wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
701 if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
702 sm->unicast_key_received = TRUE;
704 sm->broadcast_key_received = TRUE;
706 if ((sm->unicast_key_received ||
707 !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
708 (sm->broadcast_key_received ||
709 !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
711 wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
713 sm->portValid = TRUE;
714 if (sm->ctx->eapol_done_cb)
715 sm->ctx->eapol_done_cb(sm->ctx->ctx);
721 static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
723 wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
724 /* EAP layer processing; no special code is needed, since Supplicant
725 * Backend state machine is waiting for eapNoResp or eapResp to be set
726 * and these are only set in the EAP state machine when the processing
731 static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
736 wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
737 resp = eap_get_eapRespData(sm->eap, &resp_len);
739 wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
744 /* Send EAP-Packet from the EAP layer to the Authenticator */
745 sm->ctx->eapol_send(sm->ctx->ctx, IEEE802_1X_TYPE_EAP_PACKET,
748 /* eapRespData is not used anymore, so free it here */
752 sm->dot1xSuppEapolReqIdFramesRx++;
754 sm->dot1xSuppEapolReqFramesRx++;
755 sm->dot1xSuppEapolRespFramesTx++;
756 sm->dot1xSuppEapolFramesTx++;
760 static void eapol_sm_abortSupp(struct eapol_sm *sm)
762 /* release system resources that may have been allocated for the
763 * authentication session */
764 free(sm->last_rx_key);
765 sm->last_rx_key = NULL;
766 free(sm->eapReqData);
767 sm->eapReqData = NULL;
768 eap_sm_abort(sm->eap);
772 struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
775 sm = malloc(sizeof(*sm));
778 memset(sm, 0, sizeof(*sm));
781 sm->portControl = Auto;
783 /* Supplicant PAE state machine */
785 sm->startPeriod = 30;
788 /* Supplicant Backend state machine */
791 sm->eap = eap_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx);
792 if (sm->eap == NULL) {
797 /* Initialize EAPOL state machines */
798 sm->initialize = TRUE;
800 sm->initialize = FALSE;
803 eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
809 void eapol_sm_deinit(struct eapol_sm *sm)
813 eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
814 eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
815 eap_sm_deinit(sm->eap);
816 free(sm->last_rx_key);
817 free(sm->eapReqData);
823 static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
825 eapol_sm_step(timeout_ctx);
829 void eapol_sm_step(struct eapol_sm *sm)
833 /* In theory, it should be ok to run this in loop until !changed.
834 * However, it is better to use a limit on number of iterations to
835 * allow events (e.g., SIGTERM) to stop the program cleanly if the
836 * state machine were to generate a busy loop. */
837 for (i = 0; i < 100; i++) {
839 SM_STEP_RUN(SUPP_PAE);
841 SM_STEP_RUN(SUPP_BE);
842 if (eap_sm_step(sm->eap))
847 /* restart EAPOL state machine step from timeout call in order
848 * to allow other events to be processed. */
849 eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
850 eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
853 if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
854 int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
855 sm->cb_status = EAPOL_CB_IN_PROGRESS;
856 sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
861 static const char *eapol_supp_pae_state(int state)
864 case SUPP_PAE_LOGOFF:
866 case SUPP_PAE_DISCONNECTED:
867 return "DISCONNECTED";
868 case SUPP_PAE_CONNECTING:
870 case SUPP_PAE_AUTHENTICATING:
871 return "AUTHENTICATING";
874 case SUPP_PAE_AUTHENTICATED:
875 return "AUTHENTICATED";
876 case SUPP_PAE_RESTART:
884 static const char *eapol_supp_be_state(int state)
887 case SUPP_BE_REQUEST:
889 case SUPP_BE_RESPONSE:
891 case SUPP_BE_SUCCESS:
895 case SUPP_BE_TIMEOUT:
899 case SUPP_BE_INITIALIZE:
901 case SUPP_BE_RECEIVE:
909 static const char * eapol_port_status(PortStatus status)
911 if (status == Authorized)
914 return "Unauthorized";
918 static const char * eapol_port_control(PortControl ctrl)
923 case ForceUnauthorized:
924 return "ForceUnauthorized";
925 case ForceAuthorized:
926 return "ForceAuthorized";
933 void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
934 int startPeriod, int maxStart)
939 sm->heldPeriod = heldPeriod;
941 sm->authPeriod = authPeriod;
942 if (startPeriod >= 0)
943 sm->startPeriod = startPeriod;
945 sm->maxStart = maxStart;
949 int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
956 len = snprintf(buf, buflen,
957 "Supplicant PAE state=%s\n"
958 "suppPortStatus=%s\n",
959 eapol_supp_pae_state(sm->SUPP_PAE_state),
960 eapol_port_status(sm->suppPortStatus));
963 len += snprintf(buf + len, buflen - len,
969 "Supplicant Backend state=%s\n",
974 eapol_port_control(sm->portControl),
975 eapol_supp_be_state(sm->SUPP_BE_state));
978 len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
984 int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
989 len = snprintf(buf, buflen,
990 "dot1xSuppPaeState=%d\n"
991 "dot1xSuppHeldPeriod=%d\n"
992 "dot1xSuppAuthPeriod=%d\n"
993 "dot1xSuppStartPeriod=%d\n"
994 "dot1xSuppMaxStart=%d\n"
995 "dot1xSuppSuppControlledPortStatus=%s\n"
996 "dot1xSuppBackendPaeState=%d\n"
997 "dot1xSuppEapolFramesRx=%d\n"
998 "dot1xSuppEapolFramesTx=%d\n"
999 "dot1xSuppEapolStartFramesTx=%d\n"
1000 "dot1xSuppEapolLogoffFramesTx=%d\n"
1001 "dot1xSuppEapolRespFramesTx=%d\n"
1002 "dot1xSuppEapolReqIdFramesRx=%d\n"
1003 "dot1xSuppEapolReqFramesRx=%d\n"
1004 "dot1xSuppInvalidEapolFramesRx=%d\n"
1005 "dot1xSuppEapLengthErrorFramesRx=%d\n"
1006 "dot1xSuppLastEapolFrameVersion=%d\n"
1007 "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
1013 sm->suppPortStatus == Authorized ?
1014 "Authorized" : "Unauthorized",
1016 sm->dot1xSuppEapolFramesRx,
1017 sm->dot1xSuppEapolFramesTx,
1018 sm->dot1xSuppEapolStartFramesTx,
1019 sm->dot1xSuppEapolLogoffFramesTx,
1020 sm->dot1xSuppEapolRespFramesTx,
1021 sm->dot1xSuppEapolReqIdFramesRx,
1022 sm->dot1xSuppEapolReqFramesRx,
1023 sm->dot1xSuppInvalidEapolFramesRx,
1024 sm->dot1xSuppEapLengthErrorFramesRx,
1025 sm->dot1xSuppLastEapolFrameVersion,
1026 MAC2STR(sm->dot1xSuppLastEapolFrameSource));
1031 void eapol_sm_rx_eapol(struct eapol_sm *sm, u8 *src, u8 *buf, size_t len)
1033 struct ieee802_1x_hdr *hdr;
1034 struct ieee802_1x_eapol_key *key;
1039 sm->dot1xSuppEapolFramesRx++;
1040 if (len < sizeof(*hdr)) {
1041 sm->dot1xSuppInvalidEapolFramesRx++;
1044 hdr = (struct ieee802_1x_hdr *) buf;
1045 sm->dot1xSuppLastEapolFrameVersion = hdr->version;
1046 memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
1047 if (hdr->version < EAPOL_VERSION) {
1048 /* TODO: backwards compatibility */
1050 plen = be_to_host16(hdr->length);
1051 if (plen > len - sizeof(*hdr)) {
1052 sm->dot1xSuppEapLengthErrorFramesRx++;
1055 data_len = plen + sizeof(*hdr);
1057 switch (hdr->type) {
1058 case IEEE802_1X_TYPE_EAP_PACKET:
1059 if (sm->cached_pmk) {
1060 /* Trying to use PMKSA caching, but Authenticator did
1061 * not seem to have a matching entry. Need to restart
1062 * EAPOL state machines.
1064 eapol_sm_abort_cached(sm);
1066 free(sm->eapReqData);
1067 sm->eapReqDataLen = plen;
1068 sm->eapReqData = malloc(sm->eapReqDataLen);
1069 if (sm->eapReqData) {
1070 wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
1072 memcpy(sm->eapReqData, (u8 *) (hdr + 1),
1074 sm->eapolEap = TRUE;
1078 case IEEE802_1X_TYPE_EAPOL_KEY:
1079 if (plen < sizeof(*key)) {
1080 wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
1084 key = (struct ieee802_1x_eapol_key *) (hdr + 1);
1085 if (key->type == EAPOL_KEY_TYPE_WPA ||
1086 key->type == EAPOL_KEY_TYPE_RSN) {
1087 /* WPA Supplicant takes care of this frame. */
1088 wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
1089 "frame in EAPOL state machines");
1092 if (key->type != EAPOL_KEY_TYPE_RC4) {
1093 wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
1094 "EAPOL-Key type %d", key->type);
1097 free(sm->last_rx_key);
1098 sm->last_rx_key = malloc(data_len);
1099 if (sm->last_rx_key) {
1100 wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
1102 memcpy(sm->last_rx_key, buf, data_len);
1103 sm->last_rx_key_len = data_len;
1109 wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
1111 sm->dot1xSuppInvalidEapolFramesRx++;
1117 void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
1120 sm->dot1xSuppEapolFramesTx++;
1124 void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
1128 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1129 "portEnabled=%d", enabled);
1130 sm->portEnabled = enabled;
1135 void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
1139 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1140 "portValid=%d", valid);
1141 sm->portValid = valid;
1146 void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
1150 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1151 "EAP success=%d", success);
1152 sm->eapSuccess = success;
1153 sm->altAccept = success;
1155 eap_notify_success(sm->eap);
1160 void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
1164 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1165 "EAP fail=%d", fail);
1167 sm->altReject = fail;
1172 void eapol_sm_notify_config(struct eapol_sm *sm, struct wpa_ssid *config,
1173 struct eapol_config *conf)
1178 sm->config = config;
1183 sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
1184 sm->conf.required_keys = conf->required_keys;
1185 sm->conf.fast_reauth = conf->fast_reauth;
1187 eap_set_fast_reauth(sm->eap, conf->fast_reauth);
1188 eap_set_workaround(sm->eap, conf->workaround);
1193 int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
1198 if (sm == NULL || !eap_key_available(sm->eap))
1200 eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
1201 if (eap_key == NULL)
1205 memcpy(key, eap_key, len);
1210 void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
1213 sm->userLogoff = logoff;
1219 void eapol_sm_notify_cached(struct eapol_sm *sm)
1223 sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
1224 sm->suppPortStatus = Authorized;
1225 eap_notify_success(sm->eap);
1229 void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
1234 wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
1235 sm->cached_pmk = TRUE;
1237 wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
1238 sm->cached_pmk = FALSE;
1243 static void eapol_sm_abort_cached(struct eapol_sm *sm)
1245 wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
1246 "doing full EAP authentication");
1249 sm->cached_pmk = FALSE;
1250 sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
1251 sm->suppPortStatus = Unauthorized;
1252 sm->eapRestart= TRUE;
1256 void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
1259 sm->ctx->scard_ctx = ctx;
1260 eap_register_scard_ctx(sm->eap, ctx);
1265 void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
1269 wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
1270 "portControl=%s", eapol_port_control(portControl));
1271 sm->portControl = portControl;
1276 void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
1280 eap_sm_notify_ctrl_attached(sm->eap);
1284 void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
1288 if (sm->eapReqData && !sm->eapReq) {
1289 wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
1290 "input) notification - retrying pending EAP "
1292 sm->eapolEap = TRUE;
1299 static struct wpa_ssid * eapol_sm_get_config(void *ctx)
1301 struct eapol_sm *sm = ctx;
1302 return sm ? sm->config : NULL;
1306 static u8 * eapol_sm_get_eapReqData(void *ctx, size_t *len)
1308 struct eapol_sm *sm = ctx;
1309 if (sm == NULL || sm->eapReqData == NULL) {
1314 *len = sm->eapReqDataLen;
1315 return sm->eapReqData;
1319 static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
1321 struct eapol_sm *sm = ctx;
1325 case EAPOL_eapSuccess:
1326 return sm->eapSuccess;
1327 case EAPOL_eapRestart:
1328 return sm->eapRestart;
1333 case EAPOL_eapNoResp:
1334 return sm->eapNoResp;
1337 case EAPOL_portEnabled:
1338 return sm->portEnabled;
1339 case EAPOL_altAccept:
1340 return sm->altAccept;
1341 case EAPOL_altReject:
1342 return sm->altReject;
1348 static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
1351 struct eapol_sm *sm = ctx;
1355 case EAPOL_eapSuccess:
1356 sm->eapSuccess = value;
1358 case EAPOL_eapRestart:
1359 sm->eapRestart = value;
1362 sm->eapFail = value;
1365 sm->eapResp = value;
1367 case EAPOL_eapNoResp:
1368 sm->eapNoResp = value;
1373 case EAPOL_portEnabled:
1374 sm->portEnabled = value;
1376 case EAPOL_altAccept:
1377 sm->altAccept = value;
1379 case EAPOL_altReject:
1380 sm->altReject = value;
1386 static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
1388 struct eapol_sm *sm = ctx;
1392 case EAPOL_idleWhile:
1393 return sm->idleWhile;
1399 static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
1402 struct eapol_sm *sm = ctx;
1406 case EAPOL_idleWhile:
1407 sm->idleWhile = value;
1413 static struct eapol_callbacks eapol_cb =
1415 .get_config = eapol_sm_get_config,
1416 .get_bool = eapol_sm_get_bool,
1417 .set_bool = eapol_sm_set_bool,
1418 .get_int = eapol_sm_get_int,
1419 .set_int = eapol_sm_set_int,
1420 .get_eapReqData = eapol_sm_get_eapReqData,