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 "l2_packet.h"
29 /* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
32 * struct eapol_sm - Internal data for EAPOL state machines
36 unsigned int authWhile
;
37 unsigned int heldWhile
;
38 unsigned int startWhen
;
39 unsigned int idleWhile
; /* for EAP state machine */
41 /* Global variables */
48 PortControl portControl
;
50 PortStatus suppPortStatus
; /* dot1xSuppControlledPortStatus */
58 /* Supplicant PAE state machine */
61 SUPP_PAE_DISCONNECTED
= 1,
63 SUPP_PAE_CONNECTING
= 3,
64 SUPP_PAE_AUTHENTICATING
= 4,
65 SUPP_PAE_AUTHENTICATED
= 5,
69 SUPP_PAE_S_FORCE_AUTH
= 9,
70 SUPP_PAE_S_FORCE_UNAUTH
= 10
71 } SUPP_PAE_state
; /* dot1xSuppPaeState */
75 unsigned int startCount
;
77 PortControl sPortMode
;
79 unsigned int heldPeriod
; /* dot1xSuppHeldPeriod */
80 unsigned int startPeriod
; /* dot1xSuppStartPeriod */
81 unsigned int maxStart
; /* dot1xSuppMaxStart */
83 /* Key Receive state machine */
86 KEY_RX_NO_KEY_RECEIVE
, KEY_RX_KEY_RECEIVE
91 /* Supplicant Backend state machine */
94 SUPP_BE_INITIALIZE
= 1,
102 } SUPP_BE_state
; /* dot1xSuppBackendPaeState */
108 unsigned int authPeriod
; /* dot1xSuppAuthPeriod */
111 unsigned int dot1xSuppEapolFramesRx
;
112 unsigned int dot1xSuppEapolFramesTx
;
113 unsigned int dot1xSuppEapolStartFramesTx
;
114 unsigned int dot1xSuppEapolLogoffFramesTx
;
115 unsigned int dot1xSuppEapolRespFramesTx
;
116 unsigned int dot1xSuppEapolReqIdFramesRx
;
117 unsigned int dot1xSuppEapolReqFramesRx
;
118 unsigned int dot1xSuppInvalidEapolFramesRx
;
119 unsigned int dot1xSuppEapLengthErrorFramesRx
;
120 unsigned int dot1xSuppLastEapolFrameVersion
;
121 unsigned char dot1xSuppLastEapolFrameSource
[6];
123 /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
126 struct wpa_ssid
*config
;
129 size_t last_rx_key_len
;
130 u8
*eapReqData
; /* for EAP */
131 size_t eapReqDataLen
; /* for EAP */
132 Boolean altAccept
; /* for EAP */
133 Boolean altReject
; /* for EAP */
134 Boolean replay_counter_valid
;
135 u8 last_replay_counter
[16];
136 struct eapol_config conf
;
137 struct eapol_ctx
*ctx
;
138 enum { EAPOL_CB_IN_PROGRESS
= 0, EAPOL_CB_SUCCESS
, EAPOL_CB_FAILURE
}
142 Boolean unicast_key_received
, broadcast_key_received
;
146 #define IEEE8021X_REPLAY_COUNTER_LEN 8
147 #define IEEE8021X_KEY_SIGN_LEN 16
148 #define IEEE8021X_KEY_IV_LEN 16
150 #define IEEE8021X_KEY_INDEX_FLAG 0x80
151 #define IEEE8021X_KEY_INDEX_MASK 0x03
153 struct ieee802_1x_eapol_key
{
155 /* Note: key_length is unaligned */
157 /* does not repeat within the life of the keying material used to
158 * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
159 u8 replay_counter
[IEEE8021X_REPLAY_COUNTER_LEN
];
160 u8 key_iv
[IEEE8021X_KEY_IV_LEN
]; /* cryptographically random number */
161 u8 key_index
; /* key flag in the most significant bit:
162 * 0 = broadcast (default key),
163 * 1 = unicast (key mapping key); key index is in the
164 * 7 least significant bits */
165 /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
167 u8 key_signature
[IEEE8021X_KEY_SIGN_LEN
];
169 /* followed by key: if packet body length = 44 + key length, then the
170 * key field (of key_length bytes) contains the key in encrypted form;
171 * if packet body length = 44, key field is absent and key_length
172 * represents the number of least significant octets from
173 * MS-MPPE-Send-Key attribute to be used as the keying material;
174 * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
175 } __attribute__ ((packed
));
178 static void eapol_sm_txLogoff(struct eapol_sm
*sm
);
179 static void eapol_sm_txStart(struct eapol_sm
*sm
);
180 static void eapol_sm_processKey(struct eapol_sm
*sm
);
181 static void eapol_sm_getSuppRsp(struct eapol_sm
*sm
);
182 static void eapol_sm_txSuppRsp(struct eapol_sm
*sm
);
183 static void eapol_sm_abortSupp(struct eapol_sm
*sm
);
184 static void eapol_sm_abort_cached(struct eapol_sm
*sm
);
185 static void eapol_sm_step_timeout(void *eloop_ctx
, void *timeout_ctx
);
188 /* Definitions for clarifying state machine implementation */
189 #define SM_STATE(machine, state) \
190 static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_sm *sm, \
193 #define SM_ENTRY(machine, state) \
194 if (!global || sm->machine ## _state != machine ## _ ## state) { \
195 sm->changed = TRUE; \
196 wpa_printf(MSG_DEBUG, "EAPOL: " #machine " entering state " #state); \
198 sm->machine ## _state = machine ## _ ## state;
200 #define SM_ENTER(machine, state) \
201 sm_ ## machine ## _ ## state ## _Enter(sm, 0)
202 #define SM_ENTER_GLOBAL(machine, state) \
203 sm_ ## machine ## _ ## state ## _Enter(sm, 1)
205 #define SM_STEP(machine) \
206 static void sm_ ## machine ## _Step(struct eapol_sm *sm)
208 #define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
211 /* Port Timers state machine - implemented as a function that will be called
212 * once a second as a registered event loop timeout */
213 static void eapol_port_timers_tick(void *eloop_ctx
, void *timeout_ctx
)
215 struct eapol_sm
*sm
= timeout_ctx
;
217 if (sm
->authWhile
> 0) {
219 if (sm
->authWhile
== 0)
220 wpa_printf(MSG_DEBUG
, "EAPOL: authWhile --> 0");
222 if (sm
->heldWhile
> 0) {
224 if (sm
->heldWhile
== 0)
225 wpa_printf(MSG_DEBUG
, "EAPOL: heldWhile --> 0");
227 if (sm
->startWhen
> 0) {
229 if (sm
->startWhen
== 0)
230 wpa_printf(MSG_DEBUG
, "EAPOL: startWhen --> 0");
232 if (sm
->idleWhile
> 0) {
234 if (sm
->idleWhile
== 0)
235 wpa_printf(MSG_DEBUG
, "EAPOL: idleWhile --> 0");
238 eloop_register_timeout(1, 0, eapol_port_timers_tick
, eloop_ctx
, sm
);
243 SM_STATE(SUPP_PAE
, LOGOFF
)
245 SM_ENTRY(SUPP_PAE
, LOGOFF
);
246 eapol_sm_txLogoff(sm
);
247 sm
->logoffSent
= TRUE
;
248 sm
->suppPortStatus
= Unauthorized
;
252 SM_STATE(SUPP_PAE
, DISCONNECTED
)
254 SM_ENTRY(SUPP_PAE
, DISCONNECTED
);
255 sm
->sPortMode
= Auto
;
257 sm
->logoffSent
= FALSE
;
258 sm
->suppPortStatus
= Unauthorized
;
259 sm
->suppAbort
= TRUE
;
261 sm
->unicast_key_received
= FALSE
;
262 sm
->broadcast_key_received
= FALSE
;
266 SM_STATE(SUPP_PAE
, CONNECTING
)
268 int send_start
= sm
->SUPP_PAE_state
== SUPP_PAE_CONNECTING
;
269 SM_ENTRY(SUPP_PAE
, CONNECTING
);
271 sm
->startWhen
= sm
->startPeriod
;
275 * Do not send EAPOL-Start immediately since in most cases,
276 * Authenticator is going to start authentication immediately
277 * after association and an extra EAPOL-Start is just going to
278 * delay authentication. Use a short timeout to send the first
279 * EAPOL-Start if Authenticator does not start authentication.
283 sm
->eapolEap
= FALSE
;
285 eapol_sm_txStart(sm
);
289 SM_STATE(SUPP_PAE
, AUTHENTICATING
)
291 SM_ENTRY(SUPP_PAE
, AUTHENTICATING
);
293 sm
->suppSuccess
= FALSE
;
294 sm
->suppFail
= FALSE
;
295 sm
->suppTimeout
= FALSE
;
298 sm
->suppStart
= TRUE
;
302 SM_STATE(SUPP_PAE
, HELD
)
304 SM_ENTRY(SUPP_PAE
, HELD
);
305 sm
->heldWhile
= sm
->heldPeriod
;
306 sm
->suppPortStatus
= Unauthorized
;
307 sm
->cb_status
= EAPOL_CB_FAILURE
;
311 SM_STATE(SUPP_PAE
, AUTHENTICATED
)
313 SM_ENTRY(SUPP_PAE
, AUTHENTICATED
);
314 sm
->suppPortStatus
= Authorized
;
315 sm
->cb_status
= EAPOL_CB_SUCCESS
;
319 SM_STATE(SUPP_PAE
, RESTART
)
321 SM_ENTRY(SUPP_PAE
, RESTART
);
322 sm
->eapRestart
= TRUE
;
326 SM_STATE(SUPP_PAE
, S_FORCE_AUTH
)
328 SM_ENTRY(SUPP_PAE
, S_FORCE_AUTH
);
329 sm
->suppPortStatus
= Authorized
;
330 sm
->sPortMode
= ForceAuthorized
;
334 SM_STATE(SUPP_PAE
, S_FORCE_UNAUTH
)
336 SM_ENTRY(SUPP_PAE
, S_FORCE_UNAUTH
);
337 sm
->suppPortStatus
= Unauthorized
;
338 sm
->sPortMode
= ForceUnauthorized
;
339 eapol_sm_txLogoff(sm
);
345 if ((sm
->userLogoff
&& !sm
->logoffSent
) &&
346 !(sm
->initialize
|| !sm
->portEnabled
))
347 SM_ENTER_GLOBAL(SUPP_PAE
, LOGOFF
);
348 else if (((sm
->portControl
== Auto
) &&
349 (sm
->sPortMode
!= sm
->portControl
)) ||
350 sm
->initialize
|| !sm
->portEnabled
)
351 SM_ENTER_GLOBAL(SUPP_PAE
, DISCONNECTED
);
352 else if ((sm
->portControl
== ForceAuthorized
) &&
353 (sm
->sPortMode
!= sm
->portControl
) &&
354 !(sm
->initialize
|| !sm
->portEnabled
))
355 SM_ENTER_GLOBAL(SUPP_PAE
, S_FORCE_AUTH
);
356 else if ((sm
->portControl
== ForceUnauthorized
) &&
357 (sm
->sPortMode
!= sm
->portControl
) &&
358 !(sm
->initialize
|| !sm
->portEnabled
))
359 SM_ENTER_GLOBAL(SUPP_PAE
, S_FORCE_UNAUTH
);
360 else switch (sm
->SUPP_PAE_state
) {
361 case SUPP_PAE_UNKNOWN
:
363 case SUPP_PAE_LOGOFF
:
365 SM_ENTER(SUPP_PAE
, DISCONNECTED
);
367 case SUPP_PAE_DISCONNECTED
:
368 SM_ENTER(SUPP_PAE
, CONNECTING
);
370 case SUPP_PAE_CONNECTING
:
371 if (sm
->startWhen
== 0 && sm
->startCount
< sm
->maxStart
)
372 SM_ENTER(SUPP_PAE
, CONNECTING
);
373 else if (sm
->startWhen
== 0 &&
374 sm
->startCount
>= sm
->maxStart
&&
376 SM_ENTER(SUPP_PAE
, AUTHENTICATED
);
377 else if (sm
->eapSuccess
|| sm
->eapFail
)
378 SM_ENTER(SUPP_PAE
, AUTHENTICATING
);
379 else if (sm
->eapolEap
)
380 SM_ENTER(SUPP_PAE
, RESTART
);
381 else if (sm
->startWhen
== 0 &&
382 sm
->startCount
>= sm
->maxStart
&&
384 SM_ENTER(SUPP_PAE
, HELD
);
386 case SUPP_PAE_AUTHENTICATING
:
387 if (sm
->eapSuccess
&& !sm
->portValid
&&
388 sm
->conf
.accept_802_1x_keys
&&
389 sm
->conf
.required_keys
== 0) {
390 wpa_printf(MSG_DEBUG
, "EAPOL: IEEE 802.1X for "
391 "plaintext connection; no EAPOL-Key frames "
393 sm
->portValid
= TRUE
;
394 if (sm
->ctx
->eapol_done_cb
)
395 sm
->ctx
->eapol_done_cb(sm
->ctx
->ctx
);
397 if (sm
->eapSuccess
&& sm
->portValid
)
398 SM_ENTER(SUPP_PAE
, AUTHENTICATED
);
399 else if (sm
->eapFail
|| (sm
->keyDone
&& !sm
->portValid
))
400 SM_ENTER(SUPP_PAE
, HELD
);
401 else if (sm
->suppTimeout
)
402 SM_ENTER(SUPP_PAE
, CONNECTING
);
405 if (sm
->heldWhile
== 0)
406 SM_ENTER(SUPP_PAE
, CONNECTING
);
407 else if (sm
->eapolEap
)
408 SM_ENTER(SUPP_PAE
, RESTART
);
410 case SUPP_PAE_AUTHENTICATED
:
411 if (sm
->eapolEap
&& sm
->portValid
)
412 SM_ENTER(SUPP_PAE
, RESTART
);
413 else if (!sm
->portValid
)
414 SM_ENTER(SUPP_PAE
, DISCONNECTED
);
416 case SUPP_PAE_RESTART
:
418 SM_ENTER(SUPP_PAE
, AUTHENTICATING
);
420 case SUPP_PAE_S_FORCE_AUTH
:
422 case SUPP_PAE_S_FORCE_UNAUTH
:
428 SM_STATE(KEY_RX
, NO_KEY_RECEIVE
)
430 SM_ENTRY(KEY_RX
, NO_KEY_RECEIVE
);
434 SM_STATE(KEY_RX
, KEY_RECEIVE
)
436 SM_ENTRY(KEY_RX
, KEY_RECEIVE
);
437 eapol_sm_processKey(sm
);
444 if (sm
->initialize
|| !sm
->portEnabled
)
445 SM_ENTER_GLOBAL(KEY_RX
, NO_KEY_RECEIVE
);
446 switch (sm
->KEY_RX_state
) {
449 case KEY_RX_NO_KEY_RECEIVE
:
451 SM_ENTER(KEY_RX
, KEY_RECEIVE
);
453 case KEY_RX_KEY_RECEIVE
:
455 SM_ENTER(KEY_RX
, KEY_RECEIVE
);
461 SM_STATE(SUPP_BE
, REQUEST
)
463 SM_ENTRY(SUPP_BE
, REQUEST
);
466 eapol_sm_getSuppRsp(sm
);
470 SM_STATE(SUPP_BE
, RESPONSE
)
472 SM_ENTRY(SUPP_BE
, RESPONSE
);
473 eapol_sm_txSuppRsp(sm
);
478 SM_STATE(SUPP_BE
, SUCCESS
)
480 SM_ENTRY(SUPP_BE
, SUCCESS
);
482 sm
->suppSuccess
= TRUE
;
484 if (eap_key_available(sm
->eap
)) {
485 /* New key received - clear IEEE 802.1X EAPOL-Key replay
487 sm
->replay_counter_valid
= FALSE
;
492 SM_STATE(SUPP_BE
, FAIL
)
494 SM_ENTRY(SUPP_BE
, FAIL
);
499 SM_STATE(SUPP_BE
, TIMEOUT
)
501 SM_ENTRY(SUPP_BE
, TIMEOUT
);
502 sm
->suppTimeout
= TRUE
;
506 SM_STATE(SUPP_BE
, IDLE
)
508 SM_ENTRY(SUPP_BE
, IDLE
);
509 sm
->suppStart
= FALSE
;
510 sm
->initial_req
= TRUE
;
514 SM_STATE(SUPP_BE
, INITIALIZE
)
516 SM_ENTRY(SUPP_BE
, INITIALIZE
);
517 eapol_sm_abortSupp(sm
);
518 sm
->suppAbort
= FALSE
;
522 SM_STATE(SUPP_BE
, RECEIVE
)
524 SM_ENTRY(SUPP_BE
, RECEIVE
);
525 sm
->authWhile
= sm
->authPeriod
;
526 sm
->eapolEap
= FALSE
;
527 sm
->eapNoResp
= FALSE
;
528 sm
->initial_req
= FALSE
;
534 if (sm
->initialize
|| sm
->suppAbort
)
535 SM_ENTER_GLOBAL(SUPP_BE
, INITIALIZE
);
536 else switch (sm
->SUPP_BE_state
) {
537 case SUPP_BE_UNKNOWN
:
539 case SUPP_BE_REQUEST
:
540 if (sm
->eapResp
&& sm
->eapNoResp
) {
541 wpa_printf(MSG_DEBUG
, "EAPOL: SUPP_BE REQUEST: both "
542 "eapResp and eapNoResp set?!");
545 SM_ENTER(SUPP_BE
, RESPONSE
);
546 else if (sm
->eapNoResp
)
547 SM_ENTER(SUPP_BE
, RECEIVE
);
549 case SUPP_BE_RESPONSE
:
550 SM_ENTER(SUPP_BE
, RECEIVE
);
552 case SUPP_BE_SUCCESS
:
553 SM_ENTER(SUPP_BE
, IDLE
);
556 SM_ENTER(SUPP_BE
, IDLE
);
558 case SUPP_BE_TIMEOUT
:
559 SM_ENTER(SUPP_BE
, IDLE
);
562 if (sm
->eapFail
&& sm
->suppStart
)
563 SM_ENTER(SUPP_BE
, FAIL
);
564 else if (sm
->eapolEap
&& sm
->suppStart
)
565 SM_ENTER(SUPP_BE
, REQUEST
);
566 else if (sm
->eapSuccess
&& sm
->suppStart
)
567 SM_ENTER(SUPP_BE
, SUCCESS
);
569 case SUPP_BE_INITIALIZE
:
570 SM_ENTER(SUPP_BE
, IDLE
);
572 case SUPP_BE_RECEIVE
:
574 SM_ENTER(SUPP_BE
, REQUEST
);
575 else if (sm
->eapFail
)
576 SM_ENTER(SUPP_BE
, FAIL
);
577 else if (sm
->authWhile
== 0)
578 SM_ENTER(SUPP_BE
, TIMEOUT
);
579 else if (sm
->eapSuccess
)
580 SM_ENTER(SUPP_BE
, SUCCESS
);
586 static void eapol_sm_txLogoff(struct eapol_sm
*sm
)
588 wpa_printf(MSG_DEBUG
, "EAPOL: txLogoff");
589 sm
->ctx
->eapol_send(sm
->ctx
->eapol_send_ctx
,
590 IEEE802_1X_TYPE_EAPOL_LOGOFF
, (u8
*) "", 0);
591 sm
->dot1xSuppEapolLogoffFramesTx
++;
592 sm
->dot1xSuppEapolFramesTx
++;
596 static void eapol_sm_txStart(struct eapol_sm
*sm
)
598 wpa_printf(MSG_DEBUG
, "EAPOL: txStart");
599 sm
->ctx
->eapol_send(sm
->ctx
->eapol_send_ctx
,
600 IEEE802_1X_TYPE_EAPOL_START
, (u8
*) "", 0);
601 sm
->dot1xSuppEapolStartFramesTx
++;
602 sm
->dot1xSuppEapolFramesTx
++;
606 #define IEEE8021X_ENCR_KEY_LEN 32
607 #define IEEE8021X_SIGN_KEY_LEN 32
609 struct eap_key_data
{
610 u8 encr_key
[IEEE8021X_ENCR_KEY_LEN
];
611 u8 sign_key
[IEEE8021X_SIGN_KEY_LEN
];
615 static void eapol_sm_processKey(struct eapol_sm
*sm
)
617 struct ieee802_1x_hdr
*hdr
;
618 struct ieee802_1x_eapol_key
*key
;
619 struct eap_key_data keydata
;
620 u8 orig_key_sign
[IEEE8021X_KEY_SIGN_LEN
], datakey
[32];
621 u8 ekey
[IEEE8021X_KEY_IV_LEN
+ IEEE8021X_ENCR_KEY_LEN
];
622 int key_len
, res
, sign_key_len
, encr_key_len
;
625 wpa_printf(MSG_DEBUG
, "EAPOL: processKey");
626 if (sm
->last_rx_key
== NULL
)
629 if (!sm
->conf
.accept_802_1x_keys
) {
630 wpa_printf(MSG_WARNING
, "EAPOL: Received IEEE 802.1X EAPOL-Key"
631 " even though this was not accepted - "
632 "ignoring this packet");
636 hdr
= (struct ieee802_1x_hdr
*) sm
->last_rx_key
;
637 key
= (struct ieee802_1x_eapol_key
*) (hdr
+ 1);
638 if (sizeof(*hdr
) + be_to_host16(hdr
->length
) > sm
->last_rx_key_len
) {
639 wpa_printf(MSG_WARNING
, "EAPOL: Too short EAPOL-Key frame");
642 rx_key_length
= WPA_GET_BE16(key
->key_length
);
643 wpa_printf(MSG_DEBUG
, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
644 "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
645 hdr
->version
, hdr
->type
, be_to_host16(hdr
->length
),
646 key
->type
, rx_key_length
, key
->key_index
);
648 eapol_sm_notify_lower_layer_success(sm
);
649 sign_key_len
= IEEE8021X_SIGN_KEY_LEN
;
650 encr_key_len
= IEEE8021X_ENCR_KEY_LEN
;
651 res
= eapol_sm_get_key(sm
, (u8
*) &keydata
, sizeof(keydata
));
653 wpa_printf(MSG_DEBUG
, "EAPOL: Could not get master key for "
654 "decrypting EAPOL-Key keys");
658 /* LEAP derives only 16 bytes of keying material. */
659 res
= eapol_sm_get_key(sm
, (u8
*) &keydata
, 16);
661 wpa_printf(MSG_DEBUG
, "EAPOL: Could not get LEAP "
662 "master key for decrypting EAPOL-Key keys");
667 memcpy(keydata
.sign_key
, keydata
.encr_key
, 16);
669 wpa_printf(MSG_DEBUG
, "EAPOL: Could not get enough master key "
670 "data for decrypting EAPOL-Key keys (res=%d)", res
);
674 /* The key replay_counter must increase when same master key */
675 if (sm
->replay_counter_valid
&&
676 memcmp(sm
->last_replay_counter
, key
->replay_counter
,
677 IEEE8021X_REPLAY_COUNTER_LEN
) >= 0) {
678 wpa_printf(MSG_WARNING
, "EAPOL: EAPOL-Key replay counter did "
679 "not increase - ignoring key");
680 wpa_hexdump(MSG_DEBUG
, "EAPOL: last replay counter",
681 sm
->last_replay_counter
,
682 IEEE8021X_REPLAY_COUNTER_LEN
);
683 wpa_hexdump(MSG_DEBUG
, "EAPOL: received replay counter",
684 key
->replay_counter
, IEEE8021X_REPLAY_COUNTER_LEN
);
688 /* Verify key signature (HMAC-MD5) */
689 memcpy(orig_key_sign
, key
->key_signature
, IEEE8021X_KEY_SIGN_LEN
);
690 memset(key
->key_signature
, 0, IEEE8021X_KEY_SIGN_LEN
);
691 hmac_md5(keydata
.sign_key
, sign_key_len
,
692 sm
->last_rx_key
, sizeof(*hdr
) + be_to_host16(hdr
->length
),
694 if (memcmp(orig_key_sign
, key
->key_signature
, IEEE8021X_KEY_SIGN_LEN
)
696 wpa_printf(MSG_DEBUG
, "EAPOL: Invalid key signature in "
698 memcpy(key
->key_signature
, orig_key_sign
,
699 IEEE8021X_KEY_SIGN_LEN
);
702 wpa_printf(MSG_DEBUG
, "EAPOL: EAPOL-Key key signature verified");
704 key_len
= be_to_host16(hdr
->length
) - sizeof(*key
);
705 if (key_len
> 32 || rx_key_length
> 32) {
706 wpa_printf(MSG_WARNING
, "EAPOL: Too long key data length %d",
707 key_len
? key_len
: rx_key_length
);
710 if (key_len
== rx_key_length
) {
711 memcpy(ekey
, key
->key_iv
, IEEE8021X_KEY_IV_LEN
);
712 memcpy(ekey
+ IEEE8021X_KEY_IV_LEN
, keydata
.encr_key
,
714 memcpy(datakey
, key
+ 1, key_len
);
715 rc4(datakey
, key_len
, ekey
,
716 IEEE8021X_KEY_IV_LEN
+ encr_key_len
);
717 wpa_hexdump_key(MSG_DEBUG
, "EAPOL: Decrypted(RC4) key",
719 } else if (key_len
== 0) {
721 * IEEE 802.1X-2004 specifies that least significant Key Length
722 * octets from MS-MPPE-Send-Key are used as the key if the key
723 * data is not present. This seems to be meaning the beginning
724 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
725 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
726 * Anyway, taking the beginning of the keying material from EAP
727 * seems to interoperate with Authenticators.
729 key_len
= rx_key_length
;
730 memcpy(datakey
, keydata
.encr_key
, key_len
);
731 wpa_hexdump_key(MSG_DEBUG
, "EAPOL: using part of EAP keying "
732 "material data encryption key",
735 wpa_printf(MSG_DEBUG
, "EAPOL: Invalid key data length %d "
736 "(key_length=%d)", key_len
, rx_key_length
);
740 sm
->replay_counter_valid
= TRUE
;
741 memcpy(sm
->last_replay_counter
, key
->replay_counter
,
742 IEEE8021X_REPLAY_COUNTER_LEN
);
744 wpa_printf(MSG_DEBUG
, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
746 key
->key_index
& IEEE8021X_KEY_INDEX_FLAG
?
747 "unicast" : "broadcast",
748 key
->key_index
& IEEE8021X_KEY_INDEX_MASK
, key_len
);
750 if (sm
->ctx
->set_wep_key
&&
751 sm
->ctx
->set_wep_key(sm
->ctx
->ctx
,
752 key
->key_index
& IEEE8021X_KEY_INDEX_FLAG
,
753 key
->key_index
& IEEE8021X_KEY_INDEX_MASK
,
754 datakey
, key_len
) < 0) {
755 wpa_printf(MSG_WARNING
, "EAPOL: Failed to set WEP key to the "
758 if (key
->key_index
& IEEE8021X_KEY_INDEX_FLAG
)
759 sm
->unicast_key_received
= TRUE
;
761 sm
->broadcast_key_received
= TRUE
;
763 if ((sm
->unicast_key_received
||
764 !(sm
->conf
.required_keys
& EAPOL_REQUIRE_KEY_UNICAST
)) &&
765 (sm
->broadcast_key_received
||
766 !(sm
->conf
.required_keys
& EAPOL_REQUIRE_KEY_BROADCAST
)))
768 wpa_printf(MSG_DEBUG
, "EAPOL: all required EAPOL-Key "
770 sm
->portValid
= TRUE
;
771 if (sm
->ctx
->eapol_done_cb
)
772 sm
->ctx
->eapol_done_cb(sm
->ctx
->ctx
);
778 static void eapol_sm_getSuppRsp(struct eapol_sm
*sm
)
780 wpa_printf(MSG_DEBUG
, "EAPOL: getSuppRsp");
781 /* EAP layer processing; no special code is needed, since Supplicant
782 * Backend state machine is waiting for eapNoResp or eapResp to be set
783 * and these are only set in the EAP state machine when the processing
788 static void eapol_sm_txSuppRsp(struct eapol_sm
*sm
)
793 wpa_printf(MSG_DEBUG
, "EAPOL: txSuppRsp");
794 resp
= eap_get_eapRespData(sm
->eap
, &resp_len
);
796 wpa_printf(MSG_WARNING
, "EAPOL: txSuppRsp - EAP response data "
801 /* Send EAP-Packet from the EAP layer to the Authenticator */
802 sm
->ctx
->eapol_send(sm
->ctx
->eapol_send_ctx
,
803 IEEE802_1X_TYPE_EAP_PACKET
, resp
, resp_len
);
805 /* eapRespData is not used anymore, so free it here */
809 sm
->dot1xSuppEapolReqIdFramesRx
++;
811 sm
->dot1xSuppEapolReqFramesRx
++;
812 sm
->dot1xSuppEapolRespFramesTx
++;
813 sm
->dot1xSuppEapolFramesTx
++;
817 static void eapol_sm_abortSupp(struct eapol_sm
*sm
)
819 /* release system resources that may have been allocated for the
820 * authentication session */
821 free(sm
->last_rx_key
);
822 sm
->last_rx_key
= NULL
;
823 free(sm
->eapReqData
);
824 sm
->eapReqData
= NULL
;
825 eap_sm_abort(sm
->eap
);
829 static void eapol_sm_step_timeout(void *eloop_ctx
, void *timeout_ctx
)
831 eapol_sm_step(timeout_ctx
);
836 * eapol_sm_step - EAPOL state machine step function
837 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
839 * This function is called to notify the state machine about changed external
840 * variables. It will step through the EAPOL state machines in loop to process
841 * all triggered state changes.
843 void eapol_sm_step(struct eapol_sm
*sm
)
847 /* In theory, it should be ok to run this in loop until !changed.
848 * However, it is better to use a limit on number of iterations to
849 * allow events (e.g., SIGTERM) to stop the program cleanly if the
850 * state machine were to generate a busy loop. */
851 for (i
= 0; i
< 100; i
++) {
853 SM_STEP_RUN(SUPP_PAE
);
855 SM_STEP_RUN(SUPP_BE
);
856 if (eap_sm_step(sm
->eap
))
861 /* restart EAPOL state machine step from timeout call in order
862 * to allow other events to be processed. */
863 eloop_cancel_timeout(eapol_sm_step_timeout
, NULL
, sm
);
864 eloop_register_timeout(0, 0, eapol_sm_step_timeout
, NULL
, sm
);
867 if (sm
->ctx
->cb
&& sm
->cb_status
!= EAPOL_CB_IN_PROGRESS
) {
868 int success
= sm
->cb_status
== EAPOL_CB_SUCCESS
? 1 : 0;
869 sm
->cb_status
= EAPOL_CB_IN_PROGRESS
;
870 sm
->ctx
->cb(sm
, success
, sm
->ctx
->cb_ctx
);
875 static const char *eapol_supp_pae_state(int state
)
878 case SUPP_PAE_LOGOFF
:
880 case SUPP_PAE_DISCONNECTED
:
881 return "DISCONNECTED";
882 case SUPP_PAE_CONNECTING
:
884 case SUPP_PAE_AUTHENTICATING
:
885 return "AUTHENTICATING";
888 case SUPP_PAE_AUTHENTICATED
:
889 return "AUTHENTICATED";
890 case SUPP_PAE_RESTART
:
898 static const char *eapol_supp_be_state(int state
)
901 case SUPP_BE_REQUEST
:
903 case SUPP_BE_RESPONSE
:
905 case SUPP_BE_SUCCESS
:
909 case SUPP_BE_TIMEOUT
:
913 case SUPP_BE_INITIALIZE
:
915 case SUPP_BE_RECEIVE
:
923 static const char * eapol_port_status(PortStatus status
)
925 if (status
== Authorized
)
928 return "Unauthorized";
932 static const char * eapol_port_control(PortControl ctrl
)
937 case ForceUnauthorized
:
938 return "ForceUnauthorized";
939 case ForceAuthorized
:
940 return "ForceAuthorized";
948 * eapol_sm_configure - Set EAPOL variables
949 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
950 * @heldPeriod: dot1xSuppHeldPeriod
951 * @authPeriod: dot1xSuppAuthPeriod
952 * @startPeriod: dot1xSuppStartPeriod
953 * @maxStart: dot1xSuppMaxStart
955 * Set configurable EAPOL state machine variables. Each variable can be set to
956 * the given value or ignored if set to -1 (to set only some of the variables).
958 void eapol_sm_configure(struct eapol_sm
*sm
, int heldPeriod
, int authPeriod
,
959 int startPeriod
, int maxStart
)
964 sm
->heldPeriod
= heldPeriod
;
966 sm
->authPeriod
= authPeriod
;
967 if (startPeriod
>= 0)
968 sm
->startPeriod
= startPeriod
;
970 sm
->maxStart
= maxStart
;
975 * eapol_sm_get_status - Get EAPOL state machine status
976 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
977 * @buf: Buffer for status information
978 * @buflen: Maximum buffer length
979 * @verbose: Whether to include verbose status information
980 * Returns: Number of bytes written to buf.
982 * Query EAPOL state machine for status information. This function fills in a
983 * text area with current status information from the EAPOL state machine. If
984 * the buffer (buf) is not large enough, status information will be truncated
987 int eapol_sm_get_status(struct eapol_sm
*sm
, char *buf
, size_t buflen
,
994 len
= snprintf(buf
, buflen
,
995 "Supplicant PAE state=%s\n"
996 "suppPortStatus=%s\n",
997 eapol_supp_pae_state(sm
->SUPP_PAE_state
),
998 eapol_port_status(sm
->suppPortStatus
));
1001 len
+= snprintf(buf
+ len
, buflen
- len
,
1007 "Supplicant Backend state=%s\n",
1012 eapol_port_control(sm
->portControl
),
1013 eapol_supp_be_state(sm
->SUPP_BE_state
));
1016 len
+= eap_sm_get_status(sm
->eap
, buf
+ len
, buflen
- len
, verbose
);
1023 * eapol_sm_get_mib - Get EAPOL state machine MIBs
1024 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1025 * @buf: Buffer for MIB information
1026 * @buflen: Maximum buffer length
1027 * Returns: Number of bytes written to buf.
1029 * Query EAPOL state machine for MIB information. This function fills in a
1030 * text area with current MIB information from the EAPOL state machine. If
1031 * the buffer (buf) is not large enough, MIB information will be truncated to
1034 int eapol_sm_get_mib(struct eapol_sm
*sm
, char *buf
, size_t buflen
)
1039 len
= snprintf(buf
, buflen
,
1040 "dot1xSuppPaeState=%d\n"
1041 "dot1xSuppHeldPeriod=%u\n"
1042 "dot1xSuppAuthPeriod=%u\n"
1043 "dot1xSuppStartPeriod=%u\n"
1044 "dot1xSuppMaxStart=%u\n"
1045 "dot1xSuppSuppControlledPortStatus=%s\n"
1046 "dot1xSuppBackendPaeState=%d\n"
1047 "dot1xSuppEapolFramesRx=%u\n"
1048 "dot1xSuppEapolFramesTx=%u\n"
1049 "dot1xSuppEapolStartFramesTx=%u\n"
1050 "dot1xSuppEapolLogoffFramesTx=%u\n"
1051 "dot1xSuppEapolRespFramesTx=%u\n"
1052 "dot1xSuppEapolReqIdFramesRx=%u\n"
1053 "dot1xSuppEapolReqFramesRx=%u\n"
1054 "dot1xSuppInvalidEapolFramesRx=%u\n"
1055 "dot1xSuppEapLengthErrorFramesRx=%u\n"
1056 "dot1xSuppLastEapolFrameVersion=%u\n"
1057 "dot1xSuppLastEapolFrameSource=" MACSTR
"\n",
1063 sm
->suppPortStatus
== Authorized
?
1064 "Authorized" : "Unauthorized",
1066 sm
->dot1xSuppEapolFramesRx
,
1067 sm
->dot1xSuppEapolFramesTx
,
1068 sm
->dot1xSuppEapolStartFramesTx
,
1069 sm
->dot1xSuppEapolLogoffFramesTx
,
1070 sm
->dot1xSuppEapolRespFramesTx
,
1071 sm
->dot1xSuppEapolReqIdFramesRx
,
1072 sm
->dot1xSuppEapolReqFramesRx
,
1073 sm
->dot1xSuppInvalidEapolFramesRx
,
1074 sm
->dot1xSuppEapLengthErrorFramesRx
,
1075 sm
->dot1xSuppLastEapolFrameVersion
,
1076 MAC2STR(sm
->dot1xSuppLastEapolFrameSource
));
1082 * eapol_sm_rx_eapol - Process received EAPOL frames
1083 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1084 * @src: Source MAC address of the EAPOL packet
1085 * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
1086 * @len: Length of the EAPOL frame
1087 * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
1090 int eapol_sm_rx_eapol(struct eapol_sm
*sm
, const u8
*src
, const u8
*buf
,
1093 const struct ieee802_1x_hdr
*hdr
;
1094 const struct ieee802_1x_eapol_key
*key
;
1100 sm
->dot1xSuppEapolFramesRx
++;
1101 if (len
< sizeof(*hdr
)) {
1102 sm
->dot1xSuppInvalidEapolFramesRx
++;
1105 hdr
= (const struct ieee802_1x_hdr
*) buf
;
1106 sm
->dot1xSuppLastEapolFrameVersion
= hdr
->version
;
1107 memcpy(sm
->dot1xSuppLastEapolFrameSource
, src
, ETH_ALEN
);
1108 if (hdr
->version
< EAPOL_VERSION
) {
1109 /* TODO: backwards compatibility */
1111 plen
= be_to_host16(hdr
->length
);
1112 if (plen
> len
- sizeof(*hdr
)) {
1113 sm
->dot1xSuppEapLengthErrorFramesRx
++;
1116 data_len
= plen
+ sizeof(*hdr
);
1118 switch (hdr
->type
) {
1119 case IEEE802_1X_TYPE_EAP_PACKET
:
1120 if (sm
->cached_pmk
) {
1121 /* Trying to use PMKSA caching, but Authenticator did
1122 * not seem to have a matching entry. Need to restart
1123 * EAPOL state machines.
1125 eapol_sm_abort_cached(sm
);
1127 free(sm
->eapReqData
);
1128 sm
->eapReqDataLen
= plen
;
1129 sm
->eapReqData
= malloc(sm
->eapReqDataLen
);
1130 if (sm
->eapReqData
) {
1131 wpa_printf(MSG_DEBUG
, "EAPOL: Received EAP-Packet "
1133 memcpy(sm
->eapReqData
, (u8
*) (hdr
+ 1),
1135 sm
->eapolEap
= TRUE
;
1139 case IEEE802_1X_TYPE_EAPOL_KEY
:
1140 if (plen
< sizeof(*key
)) {
1141 wpa_printf(MSG_DEBUG
, "EAPOL: Too short EAPOL-Key "
1145 key
= (const struct ieee802_1x_eapol_key
*) (hdr
+ 1);
1146 if (key
->type
== EAPOL_KEY_TYPE_WPA
||
1147 key
->type
== EAPOL_KEY_TYPE_RSN
) {
1148 /* WPA Supplicant takes care of this frame. */
1149 wpa_printf(MSG_DEBUG
, "EAPOL: Ignoring WPA EAPOL-Key "
1150 "frame in EAPOL state machines");
1154 if (key
->type
!= EAPOL_KEY_TYPE_RC4
) {
1155 wpa_printf(MSG_DEBUG
, "EAPOL: Ignored unknown "
1156 "EAPOL-Key type %d", key
->type
);
1159 free(sm
->last_rx_key
);
1160 sm
->last_rx_key
= malloc(data_len
);
1161 if (sm
->last_rx_key
) {
1162 wpa_printf(MSG_DEBUG
, "EAPOL: Received EAPOL-Key "
1164 memcpy(sm
->last_rx_key
, buf
, data_len
);
1165 sm
->last_rx_key_len
= data_len
;
1171 wpa_printf(MSG_DEBUG
, "EAPOL: Received unknown EAPOL type %d",
1173 sm
->dot1xSuppInvalidEapolFramesRx
++;
1182 * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
1183 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1185 * Notify EAPOL station machine about transmitted EAPOL packet from an external
1186 * component, e.g., WPA. This will update the statistics.
1188 void eapol_sm_notify_tx_eapol_key(struct eapol_sm
*sm
)
1191 sm
->dot1xSuppEapolFramesTx
++;
1196 * eapol_sm_notify_portEnabled - Notification about portEnabled change
1197 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1198 * @enabled: New portEnabled value
1200 * Notify EAPOL station machine about new portEnabled value.
1202 void eapol_sm_notify_portEnabled(struct eapol_sm
*sm
, Boolean enabled
)
1206 wpa_printf(MSG_DEBUG
, "EAPOL: External notification - "
1207 "portEnabled=%d", enabled
);
1208 sm
->portEnabled
= enabled
;
1214 * eapol_sm_notify_portValid - Notification about portValid change
1215 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1216 * @valid: New portValid value
1218 * Notify EAPOL station machine about new portValid value.
1220 void eapol_sm_notify_portValid(struct eapol_sm
*sm
, Boolean valid
)
1224 wpa_printf(MSG_DEBUG
, "EAPOL: External notification - "
1225 "portValid=%d", valid
);
1226 sm
->portValid
= valid
;
1232 * eapol_sm_notify_eap_success - Notification of external EAP success trigger
1233 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1234 * @success: %TRUE = set success, %FALSE = clear success
1236 * Notify EAPOL station machine that external event has forced EAP state to
1237 * success (success = %TRUE). This can be cleared by setting success = %FALSE.
1239 * This function is called to update EAP state when WPA-PSK key handshake has
1240 * been completed successfully since WPA-PSK does not use EAP state machine.
1242 void eapol_sm_notify_eap_success(struct eapol_sm
*sm
, Boolean success
)
1246 wpa_printf(MSG_DEBUG
, "EAPOL: External notification - "
1247 "EAP success=%d", success
);
1248 sm
->eapSuccess
= success
;
1249 sm
->altAccept
= success
;
1251 eap_notify_success(sm
->eap
);
1257 * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
1258 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1259 * @fail: %TRUE = set failure, %FALSE = clear failure
1261 * Notify EAPOL station machine that external event has forced EAP state to
1262 * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
1264 void eapol_sm_notify_eap_fail(struct eapol_sm
*sm
, Boolean fail
)
1268 wpa_printf(MSG_DEBUG
, "EAPOL: External notification - "
1269 "EAP fail=%d", fail
);
1271 sm
->altReject
= fail
;
1277 * eapol_sm_notify_config - Notification of EAPOL configuration change
1278 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1279 * @config: Pointer to current network configuration
1280 * @conf: Pointer to EAPOL configuration data
1282 * Notify EAPOL station machine that configuration has changed. config will be
1283 * stored as a backpointer to network configuration. This can be %NULL to clear
1284 * the stored pointed. conf will be copied to local EAPOL/EAP configuration
1285 * data. If conf is %NULL, this part of the configuration change will be
1288 void eapol_sm_notify_config(struct eapol_sm
*sm
, struct wpa_ssid
*config
,
1289 const struct eapol_config
*conf
)
1294 sm
->config
= config
;
1299 sm
->conf
.accept_802_1x_keys
= conf
->accept_802_1x_keys
;
1300 sm
->conf
.required_keys
= conf
->required_keys
;
1301 sm
->conf
.fast_reauth
= conf
->fast_reauth
;
1303 eap_set_fast_reauth(sm
->eap
, conf
->fast_reauth
);
1304 eap_set_workaround(sm
->eap
, conf
->workaround
);
1305 eap_set_force_disabled(sm
->eap
, conf
->eap_disabled
);
1311 * eapol_sm_get_key - Get master session key (MSK) from EAP
1312 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1313 * @key: Pointer for key buffer
1314 * @len: Number of bytes to copy to key
1315 * Returns: 0 on success (len of key available), maximum available key len
1316 * (>0) if key is available but it is shorter than len, or -1 on failure.
1318 * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
1319 * is available only after a successful authentication.
1321 int eapol_sm_get_key(struct eapol_sm
*sm
, u8
*key
, size_t len
)
1326 if (sm
== NULL
|| !eap_key_available(sm
->eap
))
1328 eap_key
= eap_get_eapKeyData(sm
->eap
, &eap_len
);
1329 if (eap_key
== NULL
)
1333 memcpy(key
, eap_key
, len
);
1339 * eapol_sm_notify_logoff - Notification of logon/logoff commands
1340 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1341 * @logoff: Whether command was logoff
1343 * Notify EAPOL state machines that user requested logon/logoff.
1345 void eapol_sm_notify_logoff(struct eapol_sm
*sm
, Boolean logoff
)
1348 sm
->userLogoff
= logoff
;
1355 * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
1356 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1358 * Notify EAPOL state machines that PMKSA caching was successful. This is used
1359 * to move EAPOL and EAP state machines into authenticated/successful state.
1361 void eapol_sm_notify_cached(struct eapol_sm
*sm
)
1365 sm
->SUPP_PAE_state
= SUPP_PAE_AUTHENTICATED
;
1366 sm
->suppPortStatus
= Authorized
;
1367 eap_notify_success(sm
->eap
);
1372 * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
1373 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1374 * @attempt: Whether PMKSA caching is tried
1376 * Notify EAPOL state machines whether PMKSA caching is used.
1378 void eapol_sm_notify_pmkid_attempt(struct eapol_sm
*sm
, int attempt
)
1383 wpa_printf(MSG_DEBUG
, "RSN: Trying to use cached PMKSA");
1384 sm
->cached_pmk
= TRUE
;
1386 wpa_printf(MSG_DEBUG
, "RSN: Do not try to use cached PMKSA");
1387 sm
->cached_pmk
= FALSE
;
1392 static void eapol_sm_abort_cached(struct eapol_sm
*sm
)
1394 wpa_printf(MSG_DEBUG
, "RSN: Authenticator did not accept PMKID, "
1395 "doing full EAP authentication");
1398 sm
->cached_pmk
= FALSE
;
1399 sm
->SUPP_PAE_state
= SUPP_PAE_CONNECTING
;
1400 sm
->suppPortStatus
= Unauthorized
;
1402 /* Make sure we do not start sending EAPOL-Start frames first, but
1403 * instead move to RESTART state to start EAPOL authentication. */
1406 if (sm
->ctx
->aborted_cached
)
1407 sm
->ctx
->aborted_cached(sm
->ctx
->ctx
);
1412 * eapol_sm_register_scard_ctx - Notification of smart card context
1413 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1414 * @ctx: Context data for smart card operations
1416 * Notify EAPOL state machines of context data for smart card operations. This
1417 * context data will be used as a parameter for scard_*() functions.
1419 void eapol_sm_register_scard_ctx(struct eapol_sm
*sm
, void *ctx
)
1422 sm
->ctx
->scard_ctx
= ctx
;
1423 eap_register_scard_ctx(sm
->eap
, ctx
);
1429 * eapol_sm_notify_portControl - Notification of portControl changes
1430 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1431 * @portControl: New value for portControl variable
1433 * Notify EAPOL state machines that portControl variable has changed.
1435 void eapol_sm_notify_portControl(struct eapol_sm
*sm
, PortControl portControl
)
1439 wpa_printf(MSG_DEBUG
, "EAPOL: External notification - "
1440 "portControl=%s", eapol_port_control(portControl
));
1441 sm
->portControl
= portControl
;
1447 * eapol_sm_notify_ctrl_attached - Notification of attached monitor
1448 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1450 * Notify EAPOL state machines that a monitor was attached to the control
1451 * interface to trigger re-sending of pending requests for user input.
1453 void eapol_sm_notify_ctrl_attached(struct eapol_sm
*sm
)
1457 eap_sm_notify_ctrl_attached(sm
->eap
);
1462 * eapol_sm_notify_ctrl_response - Notification of received user input
1463 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1465 * Notify EAPOL state machines that a control response, i.e., user
1466 * input, was received in order to trigger retrying of a pending EAP request.
1468 void eapol_sm_notify_ctrl_response(struct eapol_sm
*sm
)
1472 if (sm
->eapReqData
&& !sm
->eapReq
) {
1473 wpa_printf(MSG_DEBUG
, "EAPOL: received control response (user "
1474 "input) notification - retrying pending EAP "
1476 sm
->eapolEap
= TRUE
;
1484 * eapol_sm_request_reauth - Request reauthentication
1485 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1487 * This function can be used to request EAPOL reauthentication, e.g., when the
1488 * current PMKSA entry is nearing expiration.
1490 void eapol_sm_request_reauth(struct eapol_sm
*sm
)
1492 if (sm
== NULL
|| sm
->SUPP_PAE_state
!= SUPP_PAE_AUTHENTICATED
)
1494 eapol_sm_txStart(sm
);
1499 * eapol_sm_notify_lower_layer_success - Notification of lower layer success
1500 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1502 * Notify EAPOL (and EAP) state machines that a lower layer has detected a
1503 * successful authentication. This is used to recover from dropped EAP-Success
1506 void eapol_sm_notify_lower_layer_success(struct eapol_sm
*sm
)
1510 eap_notify_lower_layer_success(sm
->eap
);
1514 static struct wpa_ssid
* eapol_sm_get_config(void *ctx
)
1516 struct eapol_sm
*sm
= ctx
;
1517 return sm
? sm
->config
: NULL
;
1521 static u8
* eapol_sm_get_eapReqData(void *ctx
, size_t *len
)
1523 struct eapol_sm
*sm
= ctx
;
1524 if (sm
== NULL
|| sm
->eapReqData
== NULL
) {
1529 *len
= sm
->eapReqDataLen
;
1530 return sm
->eapReqData
;
1534 static Boolean
eapol_sm_get_bool(void *ctx
, enum eapol_bool_var variable
)
1536 struct eapol_sm
*sm
= ctx
;
1540 case EAPOL_eapSuccess
:
1541 return sm
->eapSuccess
;
1542 case EAPOL_eapRestart
:
1543 return sm
->eapRestart
;
1548 case EAPOL_eapNoResp
:
1549 return sm
->eapNoResp
;
1552 case EAPOL_portEnabled
:
1553 return sm
->portEnabled
;
1554 case EAPOL_altAccept
:
1555 return sm
->altAccept
;
1556 case EAPOL_altReject
:
1557 return sm
->altReject
;
1563 static void eapol_sm_set_bool(void *ctx
, enum eapol_bool_var variable
,
1566 struct eapol_sm
*sm
= ctx
;
1570 case EAPOL_eapSuccess
:
1571 sm
->eapSuccess
= value
;
1573 case EAPOL_eapRestart
:
1574 sm
->eapRestart
= value
;
1577 sm
->eapFail
= value
;
1580 sm
->eapResp
= value
;
1582 case EAPOL_eapNoResp
:
1583 sm
->eapNoResp
= value
;
1588 case EAPOL_portEnabled
:
1589 sm
->portEnabled
= value
;
1591 case EAPOL_altAccept
:
1592 sm
->altAccept
= value
;
1594 case EAPOL_altReject
:
1595 sm
->altReject
= value
;
1601 static unsigned int eapol_sm_get_int(void *ctx
, enum eapol_int_var variable
)
1603 struct eapol_sm
*sm
= ctx
;
1607 case EAPOL_idleWhile
:
1608 return sm
->idleWhile
;
1614 static void eapol_sm_set_int(void *ctx
, enum eapol_int_var variable
,
1617 struct eapol_sm
*sm
= ctx
;
1621 case EAPOL_idleWhile
:
1622 sm
->idleWhile
= value
;
1628 static void eapol_sm_set_config_blob(void *ctx
, struct wpa_config_blob
*blob
)
1630 struct eapol_sm
*sm
= ctx
;
1631 if (sm
&& sm
->ctx
&& sm
->ctx
->set_config_blob
)
1632 sm
->ctx
->set_config_blob(sm
->ctx
->ctx
, blob
);
1636 static const struct wpa_config_blob
*
1637 eapol_sm_get_config_blob(void *ctx
, const char *name
)
1639 struct eapol_sm
*sm
= ctx
;
1640 if (sm
&& sm
->ctx
&& sm
->ctx
->get_config_blob
)
1641 return sm
->ctx
->get_config_blob(sm
->ctx
->ctx
, name
);
1647 static struct eapol_callbacks eapol_cb
=
1649 .get_config
= eapol_sm_get_config
,
1650 .get_bool
= eapol_sm_get_bool
,
1651 .set_bool
= eapol_sm_set_bool
,
1652 .get_int
= eapol_sm_get_int
,
1653 .set_int
= eapol_sm_set_int
,
1654 .get_eapReqData
= eapol_sm_get_eapReqData
,
1655 .set_config_blob
= eapol_sm_set_config_blob
,
1656 .get_config_blob
= eapol_sm_get_config_blob
,
1661 * eapol_sm_init - Initialize EAPOL state machine
1662 * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
1663 * and EAPOL state machine will free it in eapol_sm_deinit()
1664 * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
1666 * Allocate and initialize an EAPOL state machine.
1668 struct eapol_sm
*eapol_sm_init(struct eapol_ctx
*ctx
)
1670 struct eapol_sm
*sm
;
1671 struct eap_config conf
;
1672 sm
= malloc(sizeof(*sm
));
1675 memset(sm
, 0, sizeof(*sm
));
1678 sm
->portControl
= Auto
;
1680 /* Supplicant PAE state machine */
1681 sm
->heldPeriod
= 60;
1682 sm
->startPeriod
= 30;
1685 /* Supplicant Backend state machine */
1686 sm
->authPeriod
= 30;
1688 memset(&conf
, 0, sizeof(conf
));
1689 conf
.opensc_engine_path
= ctx
->opensc_engine_path
;
1690 conf
.pkcs11_engine_path
= ctx
->pkcs11_engine_path
;
1691 conf
.pkcs11_module_path
= ctx
->pkcs11_module_path
;
1693 sm
->eap
= eap_sm_init(sm
, &eapol_cb
, sm
->ctx
->msg_ctx
, &conf
);
1694 if (sm
->eap
== NULL
) {
1699 /* Initialize EAPOL state machines */
1700 sm
->initialize
= TRUE
;
1702 sm
->initialize
= FALSE
;
1705 eloop_register_timeout(1, 0, eapol_port_timers_tick
, NULL
, sm
);
1712 * eapol_sm_deinit - Deinitialize EAPOL state machine
1713 * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
1715 * Deinitialize and free EAPOL state machine.
1717 void eapol_sm_deinit(struct eapol_sm
*sm
)
1721 eloop_cancel_timeout(eapol_sm_step_timeout
, NULL
, sm
);
1722 eloop_cancel_timeout(eapol_port_timers_tick
, NULL
, sm
);
1723 eap_sm_deinit(sm
->eap
);
1724 free(sm
->last_rx_key
);
1725 free(sm
->eapReqData
);