2 * hostapd / EAP Full Authenticator state machine (RFC 4137)
3 * Copyright (c) 2004-2007, Jouni Malinen <j@w1.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.
14 * This state machine is based on the full authenticator state machine defined
15 * in RFC 4137. However, to support backend authentication in RADIUS
16 * authentication server functionality, parts of backend authenticator (also
17 * from RFC 4137) are mixed in. This functionality is enabled by setting
18 * backend_auth configuration variable to TRUE.
25 #include "state_machine.h"
27 #define STATE_MACHINE_DATA struct eap_sm
28 #define STATE_MACHINE_DEBUG_PREFIX "EAP"
30 #define EAP_MAX_AUTH_ROUNDS 50
32 static void eap_user_free(struct eap_user
*user
);
35 /* EAP state machines are described in RFC 4137 */
37 static int eap_sm_calculateTimeout(struct eap_sm
*sm
, int retransCount
,
38 int eapSRTT
, int eapRTTVAR
,
40 static void eap_sm_parseEapResp(struct eap_sm
*sm
, const struct wpabuf
*resp
);
41 static int eap_sm_getId(const struct wpabuf
*data
);
42 static struct wpabuf
* eap_sm_buildSuccess(struct eap_sm
*sm
, u8 id
);
43 static struct wpabuf
* eap_sm_buildFailure(struct eap_sm
*sm
, u8 id
);
44 static int eap_sm_nextId(struct eap_sm
*sm
, int id
);
45 static void eap_sm_Policy_update(struct eap_sm
*sm
, const u8
*nak_list
,
47 static EapType
eap_sm_Policy_getNextMethod(struct eap_sm
*sm
, int *vendor
);
48 static int eap_sm_Policy_getDecision(struct eap_sm
*sm
);
49 static Boolean
eap_sm_Policy_doPickUp(struct eap_sm
*sm
, EapType method
);
52 static int eap_copy_buf(struct wpabuf
**dst
, const struct wpabuf
*src
)
58 *dst
= wpabuf_dup(src
);
63 static int eap_copy_data(u8
**dst
, size_t *dst_len
,
64 const u8
*src
, size_t src_len
)
70 *dst
= os_malloc(src_len
);
72 os_memcpy(*dst
, src
, src_len
);
81 #define EAP_COPY(dst, src) \
82 eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
86 * eap_user_get - Fetch user information from the database
87 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
88 * @identity: Identity (User-Name) of the user
89 * @identity_len: Length of identity in bytes
90 * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
91 * Returns: 0 on success, or -1 on failure
93 * This function is used to fetch user information for EAP. The user will be
94 * selected based on the specified identity. sm->user and
95 * sm->user_eap_method_index are updated for the new user when a matching user
96 * is found. sm->user can be used to get user information (e.g., password).
98 int eap_user_get(struct eap_sm
*sm
, const u8
*identity
, size_t identity_len
,
101 struct eap_user
*user
;
103 if (sm
== NULL
|| sm
->eapol_cb
== NULL
||
104 sm
->eapol_cb
->get_eap_user
== NULL
)
107 eap_user_free(sm
->user
);
110 user
= os_zalloc(sizeof(*user
));
114 if (sm
->eapol_cb
->get_eap_user(sm
->eapol_ctx
, identity
,
115 identity_len
, phase2
, user
) != 0) {
121 sm
->user_eap_method_index
= 0;
127 SM_STATE(EAP
, DISABLED
)
129 SM_ENTRY(EAP
, DISABLED
);
134 SM_STATE(EAP
, INITIALIZE
)
136 SM_ENTRY(EAP
, INITIALIZE
);
139 sm
->eap_if
.eapSuccess
= FALSE
;
140 sm
->eap_if
.eapFail
= FALSE
;
141 sm
->eap_if
.eapTimeout
= FALSE
;
142 os_free(sm
->eap_if
.eapKeyData
);
143 sm
->eap_if
.eapKeyData
= NULL
;
144 sm
->eap_if
.eapKeyDataLen
= 0;
145 sm
->eap_if
.eapKeyAvailable
= FALSE
;
146 sm
->eap_if
.eapRestart
= FALSE
;
149 * This is not defined in RFC 4137, but method state needs to be
150 * reseted here so that it does not remain in success state when
151 * re-authentication starts.
153 if (sm
->m
&& sm
->eap_method_priv
) {
154 sm
->m
->reset(sm
, sm
->eap_method_priv
);
155 sm
->eap_method_priv
= NULL
;
158 sm
->user_eap_method_index
= 0;
160 if (sm
->backend_auth
) {
161 sm
->currentMethod
= EAP_TYPE_NONE
;
162 /* parse rxResp, respId, respMethod */
163 eap_sm_parseEapResp(sm
, sm
->eap_if
.eapRespData
);
165 sm
->currentId
= sm
->respId
;
169 sm
->method_pending
= METHOD_PENDING_NONE
;
173 SM_STATE(EAP
, PICK_UP_METHOD
)
175 SM_ENTRY(EAP
, PICK_UP_METHOD
);
177 if (eap_sm_Policy_doPickUp(sm
, sm
->respMethod
)) {
178 sm
->currentMethod
= sm
->respMethod
;
179 if (sm
->m
&& sm
->eap_method_priv
) {
180 sm
->m
->reset(sm
, sm
->eap_method_priv
);
181 sm
->eap_method_priv
= NULL
;
183 sm
->m
= eap_server_get_eap_method(EAP_VENDOR_IETF
,
185 if (sm
->m
&& sm
->m
->initPickUp
) {
186 sm
->eap_method_priv
= sm
->m
->initPickUp(sm
);
187 if (sm
->eap_method_priv
== NULL
) {
188 wpa_printf(MSG_DEBUG
, "EAP: Failed to "
189 "initialize EAP method %d",
192 sm
->currentMethod
= EAP_TYPE_NONE
;
196 sm
->currentMethod
= EAP_TYPE_NONE
;
206 sm
->eap_if
.retransWhile
= eap_sm_calculateTimeout(
207 sm
, sm
->retransCount
, sm
->eap_if
.eapSRTT
, sm
->eap_if
.eapRTTVAR
,
212 SM_STATE(EAP
, RETRANSMIT
)
214 SM_ENTRY(EAP
, RETRANSMIT
);
217 if (sm
->retransCount
<= sm
->MaxRetrans
&& sm
->lastReqData
) {
218 if (eap_copy_buf(&sm
->eap_if
.eapReqData
, sm
->lastReqData
) == 0)
219 sm
->eap_if
.eapReq
= TRUE
;
224 SM_STATE(EAP
, RECEIVED
)
226 SM_ENTRY(EAP
, RECEIVED
);
228 /* parse rxResp, respId, respMethod */
229 eap_sm_parseEapResp(sm
, sm
->eap_if
.eapRespData
);
234 SM_STATE(EAP
, DISCARD
)
236 SM_ENTRY(EAP
, DISCARD
);
237 sm
->eap_if
.eapResp
= FALSE
;
238 sm
->eap_if
.eapNoReq
= TRUE
;
242 SM_STATE(EAP
, SEND_REQUEST
)
244 SM_ENTRY(EAP
, SEND_REQUEST
);
246 sm
->retransCount
= 0;
247 if (sm
->eap_if
.eapReqData
) {
248 if (eap_copy_buf(&sm
->lastReqData
, sm
->eap_if
.eapReqData
) == 0)
250 sm
->eap_if
.eapResp
= FALSE
;
251 sm
->eap_if
.eapReq
= TRUE
;
253 sm
->eap_if
.eapResp
= FALSE
;
254 sm
->eap_if
.eapReq
= FALSE
;
257 wpa_printf(MSG_INFO
, "EAP: SEND_REQUEST - no eapReqData");
258 sm
->eap_if
.eapResp
= FALSE
;
259 sm
->eap_if
.eapReq
= FALSE
;
260 sm
->eap_if
.eapNoReq
= TRUE
;
265 SM_STATE(EAP
, INTEGRITY_CHECK
)
267 SM_ENTRY(EAP
, INTEGRITY_CHECK
);
270 sm
->ignore
= sm
->m
->check(sm
, sm
->eap_method_priv
,
271 sm
->eap_if
.eapRespData
);
276 SM_STATE(EAP
, METHOD_REQUEST
)
278 SM_ENTRY(EAP
, METHOD_REQUEST
);
281 wpa_printf(MSG_DEBUG
, "EAP: method not initialized");
285 sm
->currentId
= eap_sm_nextId(sm
, sm
->currentId
);
286 wpa_printf(MSG_DEBUG
, "EAP: building EAP-Request: Identifier %d",
288 sm
->lastId
= sm
->currentId
;
289 wpabuf_free(sm
->eap_if
.eapReqData
);
290 sm
->eap_if
.eapReqData
= sm
->m
->buildReq(sm
, sm
->eap_method_priv
,
292 if (sm
->m
->getTimeout
)
293 sm
->methodTimeout
= sm
->m
->getTimeout(sm
, sm
->eap_method_priv
);
295 sm
->methodTimeout
= 0;
299 SM_STATE(EAP
, METHOD_RESPONSE
)
301 SM_ENTRY(EAP
, METHOD_RESPONSE
);
303 sm
->m
->process(sm
, sm
->eap_method_priv
, sm
->eap_if
.eapRespData
);
304 if (sm
->m
->isDone(sm
, sm
->eap_method_priv
)) {
305 eap_sm_Policy_update(sm
, NULL
, 0);
306 os_free(sm
->eap_if
.eapKeyData
);
308 sm
->eap_if
.eapKeyData
= sm
->m
->getKey(
309 sm
, sm
->eap_method_priv
,
310 &sm
->eap_if
.eapKeyDataLen
);
312 sm
->eap_if
.eapKeyData
= NULL
;
313 sm
->eap_if
.eapKeyDataLen
= 0;
315 sm
->methodState
= METHOD_END
;
317 sm
->methodState
= METHOD_CONTINUE
;
322 SM_STATE(EAP
, PROPOSE_METHOD
)
327 SM_ENTRY(EAP
, PROPOSE_METHOD
);
329 type
= eap_sm_Policy_getNextMethod(sm
, &vendor
);
330 if (vendor
== EAP_VENDOR_IETF
)
331 sm
->currentMethod
= type
;
333 sm
->currentMethod
= EAP_TYPE_EXPANDED
;
334 if (sm
->m
&& sm
->eap_method_priv
) {
335 sm
->m
->reset(sm
, sm
->eap_method_priv
);
336 sm
->eap_method_priv
= NULL
;
338 sm
->m
= eap_server_get_eap_method(vendor
, type
);
340 sm
->eap_method_priv
= sm
->m
->init(sm
);
341 if (sm
->eap_method_priv
== NULL
) {
342 wpa_printf(MSG_DEBUG
, "EAP: Failed to initialize EAP "
343 "method %d", sm
->currentMethod
);
345 sm
->currentMethod
= EAP_TYPE_NONE
;
348 if (sm
->currentMethod
== EAP_TYPE_IDENTITY
||
349 sm
->currentMethod
== EAP_TYPE_NOTIFICATION
)
350 sm
->methodState
= METHOD_CONTINUE
;
352 sm
->methodState
= METHOD_PROPOSED
;
358 const struct eap_hdr
*nak
;
361 const u8
*nak_list
= NULL
;
365 if (sm
->eap_method_priv
) {
366 sm
->m
->reset(sm
, sm
->eap_method_priv
);
367 sm
->eap_method_priv
= NULL
;
371 nak
= wpabuf_head(sm
->eap_if
.eapRespData
);
372 if (nak
&& wpabuf_len(sm
->eap_if
.eapRespData
) > sizeof(*nak
)) {
373 len
= be_to_host16(nak
->length
);
374 if (len
> wpabuf_len(sm
->eap_if
.eapRespData
))
375 len
= wpabuf_len(sm
->eap_if
.eapRespData
);
376 pos
= (const u8
*) (nak
+ 1);
378 if (*pos
== EAP_TYPE_NAK
) {
384 eap_sm_Policy_update(sm
, nak_list
, len
);
388 SM_STATE(EAP
, SELECT_ACTION
)
390 SM_ENTRY(EAP
, SELECT_ACTION
);
392 sm
->decision
= eap_sm_Policy_getDecision(sm
);
396 SM_STATE(EAP
, TIMEOUT_FAILURE
)
398 SM_ENTRY(EAP
, TIMEOUT_FAILURE
);
400 sm
->eap_if
.eapTimeout
= TRUE
;
404 SM_STATE(EAP
, FAILURE
)
406 SM_ENTRY(EAP
, FAILURE
);
408 wpabuf_free(sm
->eap_if
.eapReqData
);
409 sm
->eap_if
.eapReqData
= eap_sm_buildFailure(sm
, sm
->currentId
);
410 wpabuf_free(sm
->lastReqData
);
411 sm
->lastReqData
= NULL
;
412 sm
->eap_if
.eapFail
= TRUE
;
416 SM_STATE(EAP
, SUCCESS
)
418 SM_ENTRY(EAP
, SUCCESS
);
420 wpabuf_free(sm
->eap_if
.eapReqData
);
421 sm
->eap_if
.eapReqData
= eap_sm_buildSuccess(sm
, sm
->currentId
);
422 wpabuf_free(sm
->lastReqData
);
423 sm
->lastReqData
= NULL
;
424 if (sm
->eap_if
.eapKeyData
)
425 sm
->eap_if
.eapKeyAvailable
= TRUE
;
426 sm
->eap_if
.eapSuccess
= TRUE
;
430 SM_STATE(EAP
, INITIALIZE_PASSTHROUGH
)
432 SM_ENTRY(EAP
, INITIALIZE_PASSTHROUGH
);
434 wpabuf_free(sm
->eap_if
.aaaEapRespData
);
435 sm
->eap_if
.aaaEapRespData
= NULL
;
441 SM_ENTRY(EAP
, IDLE2
);
443 sm
->eap_if
.retransWhile
= eap_sm_calculateTimeout(
444 sm
, sm
->retransCount
, sm
->eap_if
.eapSRTT
, sm
->eap_if
.eapRTTVAR
,
449 SM_STATE(EAP
, RETRANSMIT2
)
451 SM_ENTRY(EAP
, RETRANSMIT2
);
454 if (sm
->retransCount
<= sm
->MaxRetrans
&& sm
->lastReqData
) {
455 if (eap_copy_buf(&sm
->eap_if
.eapReqData
, sm
->lastReqData
) == 0)
456 sm
->eap_if
.eapReq
= TRUE
;
461 SM_STATE(EAP
, RECEIVED2
)
463 SM_ENTRY(EAP
, RECEIVED2
);
465 /* parse rxResp, respId, respMethod */
466 eap_sm_parseEapResp(sm
, sm
->eap_if
.eapRespData
);
470 SM_STATE(EAP
, DISCARD2
)
472 SM_ENTRY(EAP
, DISCARD2
);
473 sm
->eap_if
.eapResp
= FALSE
;
474 sm
->eap_if
.eapNoReq
= TRUE
;
478 SM_STATE(EAP
, SEND_REQUEST2
)
480 SM_ENTRY(EAP
, SEND_REQUEST2
);
482 sm
->retransCount
= 0;
483 if (sm
->eap_if
.eapReqData
) {
484 if (eap_copy_buf(&sm
->lastReqData
, sm
->eap_if
.eapReqData
) == 0)
486 sm
->eap_if
.eapResp
= FALSE
;
487 sm
->eap_if
.eapReq
= TRUE
;
489 sm
->eap_if
.eapResp
= FALSE
;
490 sm
->eap_if
.eapReq
= FALSE
;
493 wpa_printf(MSG_INFO
, "EAP: SEND_REQUEST2 - no eapReqData");
494 sm
->eap_if
.eapResp
= FALSE
;
495 sm
->eap_if
.eapReq
= FALSE
;
496 sm
->eap_if
.eapNoReq
= TRUE
;
501 SM_STATE(EAP
, AAA_REQUEST
)
503 SM_ENTRY(EAP
, AAA_REQUEST
);
505 if (sm
->eap_if
.eapRespData
== NULL
) {
506 wpa_printf(MSG_INFO
, "EAP: AAA_REQUEST - no eapRespData");
511 * if (respMethod == IDENTITY)
512 * aaaIdentity = eapRespData
513 * This is already taken care of by the EAP-Identity method which
514 * stores the identity into sm->identity.
517 eap_copy_buf(&sm
->eap_if
.aaaEapRespData
, sm
->eap_if
.eapRespData
);
521 SM_STATE(EAP
, AAA_RESPONSE
)
523 SM_ENTRY(EAP
, AAA_RESPONSE
);
525 eap_copy_buf(&sm
->eap_if
.eapReqData
, sm
->eap_if
.aaaEapReqData
);
526 sm
->currentId
= eap_sm_getId(sm
->eap_if
.eapReqData
);
527 sm
->methodTimeout
= sm
->eap_if
.aaaMethodTimeout
;
531 SM_STATE(EAP
, AAA_IDLE
)
533 SM_ENTRY(EAP
, AAA_IDLE
);
535 sm
->eap_if
.aaaFail
= FALSE
;
536 sm
->eap_if
.aaaSuccess
= FALSE
;
537 sm
->eap_if
.aaaEapReq
= FALSE
;
538 sm
->eap_if
.aaaEapNoReq
= FALSE
;
539 sm
->eap_if
.aaaEapResp
= TRUE
;
543 SM_STATE(EAP
, TIMEOUT_FAILURE2
)
545 SM_ENTRY(EAP
, TIMEOUT_FAILURE2
);
547 sm
->eap_if
.eapTimeout
= TRUE
;
551 SM_STATE(EAP
, FAILURE2
)
553 SM_ENTRY(EAP
, FAILURE2
);
555 eap_copy_buf(&sm
->eap_if
.eapReqData
, sm
->eap_if
.aaaEapReqData
);
556 sm
->eap_if
.eapFail
= TRUE
;
560 SM_STATE(EAP
, SUCCESS2
)
562 SM_ENTRY(EAP
, SUCCESS2
);
564 eap_copy_buf(&sm
->eap_if
.eapReqData
, sm
->eap_if
.aaaEapReqData
);
566 sm
->eap_if
.eapKeyAvailable
= sm
->eap_if
.aaaEapKeyAvailable
;
567 if (sm
->eap_if
.aaaEapKeyAvailable
) {
568 EAP_COPY(&sm
->eap_if
.eapKeyData
, sm
->eap_if
.aaaEapKeyData
);
570 os_free(sm
->eap_if
.eapKeyData
);
571 sm
->eap_if
.eapKeyData
= NULL
;
572 sm
->eap_if
.eapKeyDataLen
= 0;
575 sm
->eap_if
.eapSuccess
= TRUE
;
578 * Start reauthentication with identity request even though we know the
579 * previously used identity. This is needed to get reauthentication
582 sm
->start_reauth
= TRUE
;
588 if (sm
->eap_if
.eapRestart
&& sm
->eap_if
.portEnabled
)
589 SM_ENTER_GLOBAL(EAP
, INITIALIZE
);
590 else if (!sm
->eap_if
.portEnabled
)
591 SM_ENTER_GLOBAL(EAP
, DISABLED
);
592 else if (sm
->num_rounds
> EAP_MAX_AUTH_ROUNDS
) {
593 if (sm
->num_rounds
== EAP_MAX_AUTH_ROUNDS
+ 1) {
594 wpa_printf(MSG_DEBUG
, "EAP: more than %d "
595 "authentication rounds - abort",
596 EAP_MAX_AUTH_ROUNDS
);
598 SM_ENTER_GLOBAL(EAP
, FAILURE
);
600 } else switch (sm
->EAP_state
) {
602 if (sm
->backend_auth
) {
604 SM_ENTER(EAP
, SELECT_ACTION
);
605 else if (sm
->rxResp
&&
606 (sm
->respMethod
== EAP_TYPE_NAK
||
607 (sm
->respMethod
== EAP_TYPE_EXPANDED
&&
608 sm
->respVendor
== EAP_VENDOR_IETF
&&
609 sm
->respVendorMethod
== EAP_TYPE_NAK
)))
612 SM_ENTER(EAP
, PICK_UP_METHOD
);
614 SM_ENTER(EAP
, SELECT_ACTION
);
617 case EAP_PICK_UP_METHOD
:
618 if (sm
->currentMethod
== EAP_TYPE_NONE
) {
619 SM_ENTER(EAP
, SELECT_ACTION
);
621 SM_ENTER(EAP
, METHOD_RESPONSE
);
625 if (sm
->eap_if
.portEnabled
)
626 SM_ENTER(EAP
, INITIALIZE
);
629 if (sm
->eap_if
.retransWhile
== 0)
630 SM_ENTER(EAP
, RETRANSMIT
);
631 else if (sm
->eap_if
.eapResp
)
632 SM_ENTER(EAP
, RECEIVED
);
635 if (sm
->retransCount
> sm
->MaxRetrans
)
636 SM_ENTER(EAP
, TIMEOUT_FAILURE
);
641 if (sm
->rxResp
&& (sm
->respId
== sm
->currentId
) &&
642 (sm
->respMethod
== EAP_TYPE_NAK
||
643 (sm
->respMethod
== EAP_TYPE_EXPANDED
&&
644 sm
->respVendor
== EAP_VENDOR_IETF
&&
645 sm
->respVendorMethod
== EAP_TYPE_NAK
))
646 && (sm
->methodState
== METHOD_PROPOSED
))
648 else if (sm
->rxResp
&& (sm
->respId
== sm
->currentId
) &&
649 ((sm
->respMethod
== sm
->currentMethod
) ||
650 (sm
->respMethod
== EAP_TYPE_EXPANDED
&&
651 sm
->respVendor
== EAP_VENDOR_IETF
&&
652 sm
->respVendorMethod
== sm
->currentMethod
)))
653 SM_ENTER(EAP
, INTEGRITY_CHECK
);
655 wpa_printf(MSG_DEBUG
, "EAP: RECEIVED->DISCARD: "
656 "rxResp=%d respId=%d currentId=%d "
657 "respMethod=%d currentMethod=%d",
658 sm
->rxResp
, sm
->respId
, sm
->currentId
,
659 sm
->respMethod
, sm
->currentMethod
);
660 SM_ENTER(EAP
, DISCARD
);
666 case EAP_SEND_REQUEST
:
669 case EAP_INTEGRITY_CHECK
:
671 SM_ENTER(EAP
, DISCARD
);
673 SM_ENTER(EAP
, METHOD_RESPONSE
);
675 case EAP_METHOD_REQUEST
:
676 SM_ENTER(EAP
, SEND_REQUEST
);
678 case EAP_METHOD_RESPONSE
:
680 * Note: Mechanism to allow EAP methods to wait while going
681 * through pending processing is an extension to RFC 4137
682 * which only defines the transits to SELECT_ACTION and
683 * METHOD_REQUEST from this METHOD_RESPONSE state.
685 if (sm
->methodState
== METHOD_END
)
686 SM_ENTER(EAP
, SELECT_ACTION
);
687 else if (sm
->method_pending
== METHOD_PENDING_WAIT
) {
688 wpa_printf(MSG_DEBUG
, "EAP: Method has pending "
689 "processing - wait before proceeding to "
690 "METHOD_REQUEST state");
691 } else if (sm
->method_pending
== METHOD_PENDING_CONT
) {
692 wpa_printf(MSG_DEBUG
, "EAP: Method has completed "
693 "pending processing - reprocess pending "
695 sm
->method_pending
= METHOD_PENDING_NONE
;
696 SM_ENTER(EAP
, METHOD_RESPONSE
);
698 SM_ENTER(EAP
, METHOD_REQUEST
);
700 case EAP_PROPOSE_METHOD
:
702 * Note: Mechanism to allow EAP methods to wait while going
703 * through pending processing is an extension to RFC 4137
704 * which only defines the transit to METHOD_REQUEST from this
705 * PROPOSE_METHOD state.
707 if (sm
->method_pending
== METHOD_PENDING_WAIT
) {
708 wpa_printf(MSG_DEBUG
, "EAP: Method has pending "
709 "processing - wait before proceeding to "
710 "METHOD_REQUEST state");
711 if (sm
->user_eap_method_index
> 0)
712 sm
->user_eap_method_index
--;
713 } else if (sm
->method_pending
== METHOD_PENDING_CONT
) {
714 wpa_printf(MSG_DEBUG
, "EAP: Method has completed "
715 "pending processing - reprocess pending "
717 sm
->method_pending
= METHOD_PENDING_NONE
;
718 SM_ENTER(EAP
, PROPOSE_METHOD
);
720 SM_ENTER(EAP
, METHOD_REQUEST
);
723 SM_ENTER(EAP
, SELECT_ACTION
);
725 case EAP_SELECT_ACTION
:
726 if (sm
->decision
== DECISION_FAILURE
)
727 SM_ENTER(EAP
, FAILURE
);
728 else if (sm
->decision
== DECISION_SUCCESS
)
729 SM_ENTER(EAP
, SUCCESS
);
730 else if (sm
->decision
== DECISION_PASSTHROUGH
)
731 SM_ENTER(EAP
, INITIALIZE_PASSTHROUGH
);
733 SM_ENTER(EAP
, PROPOSE_METHOD
);
735 case EAP_TIMEOUT_FAILURE
:
742 case EAP_INITIALIZE_PASSTHROUGH
:
743 if (sm
->currentId
== -1)
744 SM_ENTER(EAP
, AAA_IDLE
);
746 SM_ENTER(EAP
, AAA_REQUEST
);
749 if (sm
->eap_if
.eapResp
)
750 SM_ENTER(EAP
, RECEIVED2
);
751 else if (sm
->eap_if
.retransWhile
== 0)
752 SM_ENTER(EAP
, RETRANSMIT2
);
754 case EAP_RETRANSMIT2
:
755 if (sm
->retransCount
> sm
->MaxRetrans
)
756 SM_ENTER(EAP
, TIMEOUT_FAILURE2
);
758 SM_ENTER(EAP
, IDLE2
);
761 if (sm
->rxResp
&& (sm
->respId
== sm
->currentId
))
762 SM_ENTER(EAP
, AAA_REQUEST
);
764 SM_ENTER(EAP
, DISCARD2
);
767 SM_ENTER(EAP
, IDLE2
);
769 case EAP_SEND_REQUEST2
:
770 SM_ENTER(EAP
, IDLE2
);
772 case EAP_AAA_REQUEST
:
773 SM_ENTER(EAP
, AAA_IDLE
);
775 case EAP_AAA_RESPONSE
:
776 SM_ENTER(EAP
, SEND_REQUEST2
);
779 if (sm
->eap_if
.aaaFail
)
780 SM_ENTER(EAP
, FAILURE2
);
781 else if (sm
->eap_if
.aaaSuccess
)
782 SM_ENTER(EAP
, SUCCESS2
);
783 else if (sm
->eap_if
.aaaEapReq
)
784 SM_ENTER(EAP
, AAA_RESPONSE
);
785 else if (sm
->eap_if
.aaaTimeout
)
786 SM_ENTER(EAP
, TIMEOUT_FAILURE2
);
788 case EAP_TIMEOUT_FAILURE2
:
798 static int eap_sm_calculateTimeout(struct eap_sm
*sm
, int retransCount
,
799 int eapSRTT
, int eapRTTVAR
,
806 * EAP method (either internal or through AAA server, provided
807 * timeout hint. Use that as-is as a timeout for retransmitting
808 * the EAP request if no response is received.
810 wpa_printf(MSG_DEBUG
, "EAP: retransmit timeout %d seconds "
811 "(from EAP method hint)", methodTimeout
);
812 return methodTimeout
;
816 * RFC 3748 recommends algorithms described in RFC 2988 for estimation
817 * of the retransmission timeout. This should be implemented once
818 * round-trip time measurements are available. For nowm a simple
819 * backoff mechanism is used instead if there are no EAP method
822 * SRTT = smoothed round-trip time
823 * RTTVAR = round-trip time variation
824 * RTO = retransmission timeout
828 * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
829 * initial retransmission and then double the RTO to provide back off
830 * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
834 for (i
= 0; i
< retransCount
; i
++) {
842 wpa_printf(MSG_DEBUG
, "EAP: retransmit timeout %d seconds "
843 "(from dynamic back off; retransCount=%d)",
850 static void eap_sm_parseEapResp(struct eap_sm
*sm
, const struct wpabuf
*resp
)
852 const struct eap_hdr
*hdr
;
855 /* parse rxResp, respId, respMethod */
858 sm
->respMethod
= EAP_TYPE_NONE
;
859 sm
->respVendor
= EAP_VENDOR_IETF
;
860 sm
->respVendorMethod
= EAP_TYPE_NONE
;
862 if (resp
== NULL
|| wpabuf_len(resp
) < sizeof(*hdr
)) {
863 wpa_printf(MSG_DEBUG
, "EAP: parseEapResp: invalid resp=%p "
865 resp
? (unsigned long) wpabuf_len(resp
) : 0);
869 hdr
= wpabuf_head(resp
);
870 plen
= be_to_host16(hdr
->length
);
871 if (plen
> wpabuf_len(resp
)) {
872 wpa_printf(MSG_DEBUG
, "EAP: Ignored truncated EAP-Packet "
873 "(len=%lu plen=%lu)",
874 (unsigned long) wpabuf_len(resp
),
875 (unsigned long) plen
);
879 sm
->respId
= hdr
->identifier
;
881 if (hdr
->code
== EAP_CODE_RESPONSE
)
884 if (plen
> sizeof(*hdr
)) {
885 u8
*pos
= (u8
*) (hdr
+ 1);
886 sm
->respMethod
= *pos
++;
887 if (sm
->respMethod
== EAP_TYPE_EXPANDED
) {
888 if (plen
< sizeof(*hdr
) + 8) {
889 wpa_printf(MSG_DEBUG
, "EAP: Ignored truncated "
890 "expanded EAP-Packet (plen=%lu)",
891 (unsigned long) plen
);
894 sm
->respVendor
= WPA_GET_BE24(pos
);
896 sm
->respVendorMethod
= WPA_GET_BE32(pos
);
900 wpa_printf(MSG_DEBUG
, "EAP: parseEapResp: rxResp=%d respId=%d "
901 "respMethod=%u respVendor=%u respVendorMethod=%u",
902 sm
->rxResp
, sm
->respId
, sm
->respMethod
, sm
->respVendor
,
903 sm
->respVendorMethod
);
907 static int eap_sm_getId(const struct wpabuf
*data
)
909 const struct eap_hdr
*hdr
;
911 if (data
== NULL
|| wpabuf_len(data
) < sizeof(*hdr
))
914 hdr
= wpabuf_head(data
);
915 wpa_printf(MSG_DEBUG
, "EAP: getId: id=%d", hdr
->identifier
);
916 return hdr
->identifier
;
920 static struct wpabuf
* eap_sm_buildSuccess(struct eap_sm
*sm
, u8 id
)
923 struct eap_hdr
*resp
;
924 wpa_printf(MSG_DEBUG
, "EAP: Building EAP-Success (id=%d)", id
);
926 msg
= wpabuf_alloc(sizeof(*resp
));
929 resp
= wpabuf_put(msg
, sizeof(*resp
));
930 resp
->code
= EAP_CODE_SUCCESS
;
931 resp
->identifier
= id
;
932 resp
->length
= host_to_be16(sizeof(*resp
));
938 static struct wpabuf
* eap_sm_buildFailure(struct eap_sm
*sm
, u8 id
)
941 struct eap_hdr
*resp
;
942 wpa_printf(MSG_DEBUG
, "EAP: Building EAP-Failure (id=%d)", id
);
944 msg
= wpabuf_alloc(sizeof(*resp
));
947 resp
= wpabuf_put(msg
, sizeof(*resp
));
948 resp
->code
= EAP_CODE_FAILURE
;
949 resp
->identifier
= id
;
950 resp
->length
= host_to_be16(sizeof(*resp
));
956 static int eap_sm_nextId(struct eap_sm
*sm
, int id
)
959 /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
962 if (id
!= sm
->lastId
)
965 return (id
+ 1) & 0xff;
970 * eap_sm_process_nak - Process EAP-Response/Nak
971 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
972 * @nak_list: Nak list (allowed methods) from the supplicant
973 * @len: Length of nak_list in bytes
975 * This function is called when EAP-Response/Nak is received from the
976 * supplicant. This can happen for both phase 1 and phase 2 authentications.
978 void eap_sm_process_nak(struct eap_sm
*sm
, const u8
*nak_list
, size_t len
)
983 if (sm
->user
== NULL
)
986 wpa_printf(MSG_MSGDUMP
, "EAP: processing NAK (current EAP method "
987 "index %d)", sm
->user_eap_method_index
);
989 wpa_hexdump(MSG_MSGDUMP
, "EAP: configured methods",
990 (u8
*) sm
->user
->methods
,
991 EAP_MAX_METHODS
* sizeof(sm
->user
->methods
[0]));
992 wpa_hexdump(MSG_MSGDUMP
, "EAP: list of methods supported by the peer",
995 i
= sm
->user_eap_method_index
;
996 while (i
< EAP_MAX_METHODS
&&
997 (sm
->user
->methods
[i
].vendor
!= EAP_VENDOR_IETF
||
998 sm
->user
->methods
[i
].method
!= EAP_TYPE_NONE
)) {
999 if (sm
->user
->methods
[i
].vendor
!= EAP_VENDOR_IETF
)
1001 for (j
= 0; j
< len
; j
++) {
1002 if (nak_list
[j
] == sm
->user
->methods
[i
].method
) {
1014 /* not found - remove from the list */
1015 os_memmove(&sm
->user
->methods
[i
], &sm
->user
->methods
[i
+ 1],
1016 (EAP_MAX_METHODS
- i
- 1) *
1017 sizeof(sm
->user
->methods
[0]));
1018 sm
->user
->methods
[EAP_MAX_METHODS
- 1].vendor
=
1020 sm
->user
->methods
[EAP_MAX_METHODS
- 1].method
= EAP_TYPE_NONE
;
1023 wpa_hexdump(MSG_MSGDUMP
, "EAP: new list of configured methods",
1024 (u8
*) sm
->user
->methods
, EAP_MAX_METHODS
*
1025 sizeof(sm
->user
->methods
[0]));
1029 static void eap_sm_Policy_update(struct eap_sm
*sm
, const u8
*nak_list
,
1032 if (nak_list
== NULL
|| sm
== NULL
|| sm
->user
== NULL
)
1035 if (sm
->user
->phase2
) {
1036 wpa_printf(MSG_DEBUG
, "EAP: EAP-Nak received after Phase2 user"
1037 " info was selected - reject");
1038 sm
->decision
= DECISION_FAILURE
;
1042 eap_sm_process_nak(sm
, nak_list
, len
);
1046 static EapType
eap_sm_Policy_getNextMethod(struct eap_sm
*sm
, int *vendor
)
1049 int idx
= sm
->user_eap_method_index
;
1051 /* In theory, there should be no problems with starting
1052 * re-authentication with something else than EAP-Request/Identity and
1053 * this does indeed work with wpa_supplicant. However, at least Funk
1054 * Supplicant seemed to ignore re-auth if it skipped
1055 * EAP-Request/Identity.
1056 * Re-auth sets currentId == -1, so that can be used here to select
1057 * whether Identity needs to be requested again. */
1058 if (sm
->identity
== NULL
|| sm
->currentId
== -1) {
1059 *vendor
= EAP_VENDOR_IETF
;
1060 next
= EAP_TYPE_IDENTITY
;
1061 sm
->update_user
= TRUE
;
1062 } else if (sm
->user
&& idx
< EAP_MAX_METHODS
&&
1063 (sm
->user
->methods
[idx
].vendor
!= EAP_VENDOR_IETF
||
1064 sm
->user
->methods
[idx
].method
!= EAP_TYPE_NONE
)) {
1065 *vendor
= sm
->user
->methods
[idx
].vendor
;
1066 next
= sm
->user
->methods
[idx
].method
;
1067 sm
->user_eap_method_index
++;
1069 *vendor
= EAP_VENDOR_IETF
;
1070 next
= EAP_TYPE_NONE
;
1072 wpa_printf(MSG_DEBUG
, "EAP: getNextMethod: vendor %d type %d",
1078 static int eap_sm_Policy_getDecision(struct eap_sm
*sm
)
1080 if (!sm
->eap_server
&& sm
->identity
&& !sm
->start_reauth
) {
1081 wpa_printf(MSG_DEBUG
, "EAP: getDecision: -> PASSTHROUGH");
1082 return DECISION_PASSTHROUGH
;
1085 if (sm
->m
&& sm
->currentMethod
!= EAP_TYPE_IDENTITY
&&
1086 sm
->m
->isSuccess(sm
, sm
->eap_method_priv
)) {
1087 wpa_printf(MSG_DEBUG
, "EAP: getDecision: method succeeded -> "
1089 sm
->update_user
= TRUE
;
1090 return DECISION_SUCCESS
;
1093 if (sm
->m
&& sm
->m
->isDone(sm
, sm
->eap_method_priv
) &&
1094 !sm
->m
->isSuccess(sm
, sm
->eap_method_priv
)) {
1095 wpa_printf(MSG_DEBUG
, "EAP: getDecision: method failed -> "
1097 sm
->update_user
= TRUE
;
1098 return DECISION_FAILURE
;
1101 if ((sm
->user
== NULL
|| sm
->update_user
) && sm
->identity
&&
1102 !sm
->start_reauth
) {
1104 * Allow Identity method to be started once to allow identity
1105 * selection hint to be sent from the authentication server,
1106 * but prevent a loop of Identity requests by only allowing
1107 * this to happen once.
1110 if (sm
->user
&& sm
->currentMethod
== EAP_TYPE_IDENTITY
&&
1111 sm
->user
->methods
[0].vendor
== EAP_VENDOR_IETF
&&
1112 sm
->user
->methods
[0].method
== EAP_TYPE_IDENTITY
)
1114 if (eap_user_get(sm
, sm
->identity
, sm
->identity_len
, 0) != 0) {
1115 wpa_printf(MSG_DEBUG
, "EAP: getDecision: user not "
1116 "found from database -> FAILURE");
1117 return DECISION_FAILURE
;
1119 if (id_req
&& sm
->user
&&
1120 sm
->user
->methods
[0].vendor
== EAP_VENDOR_IETF
&&
1121 sm
->user
->methods
[0].method
== EAP_TYPE_IDENTITY
) {
1122 wpa_printf(MSG_DEBUG
, "EAP: getDecision: stop "
1123 "identity request loop -> FAILURE");
1124 sm
->update_user
= TRUE
;
1125 return DECISION_FAILURE
;
1127 sm
->update_user
= FALSE
;
1129 sm
->start_reauth
= FALSE
;
1131 if (sm
->user
&& sm
->user_eap_method_index
< EAP_MAX_METHODS
&&
1132 (sm
->user
->methods
[sm
->user_eap_method_index
].vendor
!=
1134 sm
->user
->methods
[sm
->user_eap_method_index
].method
!=
1136 wpa_printf(MSG_DEBUG
, "EAP: getDecision: another method "
1137 "available -> CONTINUE");
1138 return DECISION_CONTINUE
;
1141 if (sm
->identity
== NULL
|| sm
->currentId
== -1) {
1142 wpa_printf(MSG_DEBUG
, "EAP: getDecision: no identity known "
1144 return DECISION_CONTINUE
;
1147 wpa_printf(MSG_DEBUG
, "EAP: getDecision: no more methods available -> "
1149 return DECISION_FAILURE
;
1153 static Boolean
eap_sm_Policy_doPickUp(struct eap_sm
*sm
, EapType method
)
1155 return method
== EAP_TYPE_IDENTITY
? TRUE
: FALSE
;
1160 * eap_server_sm_step - Step EAP server state machine
1161 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1162 * Returns: 1 if EAP state was changed or 0 if not
1164 * This function advances EAP state machine to a new state to match with the
1165 * current variables. This should be called whenever variables used by the EAP
1166 * state machine have changed.
1168 int eap_server_sm_step(struct eap_sm
*sm
)
1172 sm
->changed
= FALSE
;
1176 } while (sm
->changed
);
1181 static void eap_user_free(struct eap_user
*user
)
1185 os_free(user
->password
);
1186 user
->password
= NULL
;
1192 * eap_server_sm_init - Allocate and initialize EAP server state machine
1193 * @eapol_ctx: Context data to be used with eapol_cb calls
1194 * @eapol_cb: Pointer to EAPOL callback functions
1195 * @conf: EAP configuration
1196 * Returns: Pointer to the allocated EAP state machine or %NULL on failure
1198 * This function allocates and initializes an EAP state machine.
1200 struct eap_sm
* eap_server_sm_init(void *eapol_ctx
,
1201 struct eapol_callbacks
*eapol_cb
,
1202 struct eap_config
*conf
)
1206 sm
= os_zalloc(sizeof(*sm
));
1209 sm
->eapol_ctx
= eapol_ctx
;
1210 sm
->eapol_cb
= eapol_cb
;
1211 sm
->MaxRetrans
= 5; /* RFC 3748: max 3-5 retransmissions suggested */
1212 sm
->ssl_ctx
= conf
->ssl_ctx
;
1213 sm
->eap_sim_db_priv
= conf
->eap_sim_db_priv
;
1214 sm
->backend_auth
= conf
->backend_auth
;
1215 sm
->eap_server
= conf
->eap_server
;
1216 if (conf
->pac_opaque_encr_key
) {
1217 sm
->pac_opaque_encr_key
= os_malloc(16);
1218 if (sm
->pac_opaque_encr_key
) {
1219 os_memcpy(sm
->pac_opaque_encr_key
,
1220 conf
->pac_opaque_encr_key
, 16);
1223 if (conf
->eap_fast_a_id
) {
1224 sm
->eap_fast_a_id
= os_malloc(conf
->eap_fast_a_id_len
);
1225 if (sm
->eap_fast_a_id
) {
1226 os_memcpy(sm
->eap_fast_a_id
, conf
->eap_fast_a_id
,
1227 conf
->eap_fast_a_id_len
);
1228 sm
->eap_fast_a_id_len
= conf
->eap_fast_a_id_len
;
1231 if (conf
->eap_fast_a_id_info
)
1232 sm
->eap_fast_a_id_info
= os_strdup(conf
->eap_fast_a_id_info
);
1233 sm
->eap_fast_prov
= conf
->eap_fast_prov
;
1234 sm
->pac_key_lifetime
= conf
->pac_key_lifetime
;
1235 sm
->pac_key_refresh_time
= conf
->pac_key_refresh_time
;
1236 sm
->eap_sim_aka_result_ind
= conf
->eap_sim_aka_result_ind
;
1237 sm
->tnc
= conf
->tnc
;
1238 sm
->wps
= conf
->wps
;
1239 if (conf
->assoc_wps_ie
)
1240 sm
->assoc_wps_ie
= wpabuf_dup(conf
->assoc_wps_ie
);
1241 if (conf
->peer_addr
)
1242 os_memcpy(sm
->peer_addr
, conf
->peer_addr
, ETH_ALEN
);
1244 wpa_printf(MSG_DEBUG
, "EAP: Server state machine created");
1251 * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
1252 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1254 * This function deinitializes EAP state machine and frees all allocated
1257 void eap_server_sm_deinit(struct eap_sm
*sm
)
1261 wpa_printf(MSG_DEBUG
, "EAP: Server state machine removed");
1262 if (sm
->m
&& sm
->eap_method_priv
)
1263 sm
->m
->reset(sm
, sm
->eap_method_priv
);
1264 wpabuf_free(sm
->eap_if
.eapReqData
);
1265 os_free(sm
->eap_if
.eapKeyData
);
1266 wpabuf_free(sm
->lastReqData
);
1267 wpabuf_free(sm
->eap_if
.eapRespData
);
1268 os_free(sm
->identity
);
1269 os_free(sm
->pac_opaque_encr_key
);
1270 os_free(sm
->eap_fast_a_id
);
1271 os_free(sm
->eap_fast_a_id_info
);
1272 wpabuf_free(sm
->eap_if
.aaaEapReqData
);
1273 wpabuf_free(sm
->eap_if
.aaaEapRespData
);
1274 os_free(sm
->eap_if
.aaaEapKeyData
);
1275 eap_user_free(sm
->user
);
1276 wpabuf_free(sm
->assoc_wps_ie
);
1282 * eap_sm_notify_cached - Notify EAP state machine of cached PMK
1283 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1285 * This function is called when PMKSA caching is used to skip EAP
1288 void eap_sm_notify_cached(struct eap_sm
*sm
)
1293 sm
->EAP_state
= EAP_SUCCESS
;
1298 * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
1299 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1301 * This function is called when data for a pending EAP-Request is received.
1303 void eap_sm_pending_cb(struct eap_sm
*sm
)
1307 wpa_printf(MSG_DEBUG
, "EAP: Callback for pending request received");
1308 if (sm
->method_pending
== METHOD_PENDING_WAIT
)
1309 sm
->method_pending
= METHOD_PENDING_CONT
;
1314 * eap_sm_method_pending - Query whether EAP method is waiting for pending data
1315 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1316 * Returns: 1 if method is waiting for pending data or 0 if not
1318 int eap_sm_method_pending(struct eap_sm
*sm
)
1322 return sm
->method_pending
== METHOD_PENDING_WAIT
;
1327 * eap_get_identity - Get the user identity (from EAP-Response/Identity)
1328 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1329 * @len: Buffer for returning identity length
1330 * Returns: Pointer to the user identity or %NULL if not available
1332 const u8
* eap_get_identity(struct eap_sm
*sm
, size_t *len
)
1334 *len
= sm
->identity_len
;
1335 return sm
->identity
;
1340 * eap_get_interface - Get pointer to EAP-EAPOL interface data
1341 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
1342 * Returns: Pointer to the EAP-EAPOL interface data
1344 struct eap_eapol_interface
* eap_get_interface(struct eap_sm
*sm
)