2 * WPA Supplicant / EAP 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.
21 #include "wpa_supplicant.h"
22 #include "config_ssid.h"
27 #define EAP_MAX_AUTH_ROUNDS 50
31 extern const struct eap_method eap_method_md5;
34 extern const struct eap_method eap_method_tls;
37 extern const struct eap_method eap_method_mschapv2;
40 extern const struct eap_method eap_method_peap;
43 extern const struct eap_method eap_method_ttls;
46 extern const struct eap_method eap_method_gtc;
49 extern const struct eap_method eap_method_otp;
52 extern const struct eap_method eap_method_sim;
55 extern const struct eap_method eap_method_leap;
58 extern const struct eap_method eap_method_psk;
61 extern const struct eap_method eap_method_aka;
64 extern const struct eap_method eap_method_fast;
67 static const struct eap_method *eap_methods[] =
106 #define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0]))
109 const struct eap_method * eap_sm_get_eap_methods(int method)
112 for (i = 0; i < NUM_EAP_METHODS; i++) {
113 if (eap_methods[i]->method == method)
114 return eap_methods[i];
120 static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method);
121 static u8 * eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len);
122 static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len);
123 static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len);
124 static u8 * eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len);
125 static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len);
126 static const char * eap_sm_method_state_txt(int state);
127 static const char * eap_sm_decision_txt(int decision);
130 /* Definitions for clarifying state machine implementation */
131 #define SM_STATE(machine, state) \
132 static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \
135 #define SM_ENTRY(machine, state) \
136 if (!global || sm->machine ## _state != machine ## _ ## state) { \
137 sm->changed = TRUE; \
138 wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \
140 sm->machine ## _state = machine ## _ ## state;
142 #define SM_ENTER(machine, state) \
143 sm_ ## machine ## _ ## state ## _Enter(sm, 0)
144 #define SM_ENTER_GLOBAL(machine, state) \
145 sm_ ## machine ## _ ## state ## _Enter(sm, 1)
147 #define SM_STEP(machine) \
148 static void sm_ ## machine ## _Step(struct eap_sm *sm)
150 #define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
153 static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
155 return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
159 static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
162 sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
166 static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var)
168 return sm->eapol_cb->get_int(sm->eapol_ctx, var);
172 static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var,
175 sm->eapol_cb->set_int(sm->eapol_ctx, var, value);
179 static u8 * eapol_get_eapReqData(struct eap_sm *sm, size_t *len)
181 return sm->eapol_cb->get_eapReqData(sm->eapol_ctx, len);
185 static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
187 if (sm->m == NULL || sm->eap_method_priv == NULL)
190 wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
191 "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
192 sm->m->deinit(sm, sm->eap_method_priv);
193 sm->eap_method_priv = NULL;
198 SM_STATE(EAP, INITIALIZE)
200 SM_ENTRY(EAP, INITIALIZE);
201 if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
202 sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
203 wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
204 "fast reauthentication");
205 sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
207 eap_deinit_prev_method(sm, "INITIALIZE");
209 sm->selectedMethod = EAP_TYPE_NONE;
210 sm->methodState = METHOD_NONE;
211 sm->allowNotifications = TRUE;
212 sm->decision = DECISION_FAIL;
213 eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
214 eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
215 eapol_set_bool(sm, EAPOL_eapFail, FALSE);
216 free(sm->eapKeyData);
217 sm->eapKeyData = NULL;
218 sm->eapKeyAvailable = FALSE;
219 eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
220 sm->lastId = -1; /* new session - make sure this does not match with
221 * the first EAP-Packet */
222 /* draft-ietf-eap-statemachine-02.pdf does not reset eapResp and
223 * eapNoResp here. However, this seemed to be able to trigger cases
224 * where both were set and if EAPOL state machine uses eapNoResp first,
225 * it may end up not sending a real reply correctly. This occurred
226 * when the workaround in FAIL state set eapNoResp = TRUE.. Maybe that
227 * workaround needs to be fixed to do something else(?) */
228 eapol_set_bool(sm, EAPOL_eapResp, FALSE);
229 eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
234 SM_STATE(EAP, DISABLED)
236 SM_ENTRY(EAP, DISABLED);
247 SM_STATE(EAP, RECEIVED)
250 size_t eapReqDataLen;
252 SM_ENTRY(EAP, RECEIVED);
253 eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
254 /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
255 eap_sm_parseEapReq(sm, eapReqData, eapReqDataLen);
260 SM_STATE(EAP, GET_METHOD)
262 SM_ENTRY(EAP, GET_METHOD);
263 if (eap_sm_allowMethod(sm, sm->reqMethod)) {
265 if (sm->fast_reauth &&
266 sm->m && sm->m->method == sm->reqMethod &&
267 sm->m->has_reauth_data &&
268 sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
269 wpa_printf(MSG_DEBUG, "EAP: using previous method data"
270 " for fast re-authentication");
273 eap_deinit_prev_method(sm, "GET_METHOD");
274 sm->selectedMethod = sm->reqMethod;
276 sm->m = eap_sm_get_eap_methods(sm->selectedMethod);
278 wpa_printf(MSG_DEBUG, "EAP: initialize selected EAP "
280 sm->selectedMethod, sm->m->name);
282 sm->eap_method_priv = sm->m->init_for_reauth(
283 sm, sm->eap_method_priv);
285 sm->eap_method_priv = sm->m->init(sm);
286 if (sm->eap_method_priv == NULL) {
287 wpa_printf(MSG_DEBUG, "EAP: Failed to "
288 "initialize EAP method %d",
291 sm->methodState = METHOD_NONE;
292 sm->selectedMethod = EAP_TYPE_NONE;
294 sm->methodState = METHOD_INIT;
300 free(sm->eapRespData);
301 sm->eapRespData = eap_sm_buildNak(sm, sm->reqId, &sm->eapRespDataLen);
305 SM_STATE(EAP, METHOD)
308 size_t eapReqDataLen;
309 struct eap_method_ret ret;
311 SM_ENTRY(EAP, METHOD);
313 wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
317 eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
319 /* Get ignore, methodState, decision, allowNotifications, and
321 memset(&ret, 0, sizeof(ret));
322 ret.ignore = sm->ignore;
323 ret.methodState = sm->methodState;
324 ret.decision = sm->decision;
325 ret.allowNotifications = sm->allowNotifications;
326 free(sm->eapRespData);
327 sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
328 eapReqData, eapReqDataLen,
329 &sm->eapRespDataLen);
330 wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
331 "methodState=%s decision=%s",
332 ret.ignore ? "TRUE" : "FALSE",
333 eap_sm_method_state_txt(ret.methodState),
334 eap_sm_decision_txt(ret.decision));
336 sm->ignore = ret.ignore;
339 sm->methodState = ret.methodState;
340 sm->decision = ret.decision;
341 sm->allowNotifications = ret.allowNotifications;
343 if (sm->m->isKeyAvailable && sm->m->getKey &&
344 sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
345 free(sm->eapKeyData);
346 sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
352 SM_STATE(EAP, SEND_RESPONSE)
354 SM_ENTRY(EAP, SEND_RESPONSE);
355 free(sm->lastRespData);
356 if (sm->eapRespData) {
358 memcpy(sm->last_md5, sm->req_md5, 16);
359 sm->lastId = sm->reqId;
360 sm->lastRespData = malloc(sm->eapRespDataLen);
361 if (sm->lastRespData) {
362 memcpy(sm->lastRespData, sm->eapRespData,
364 sm->lastRespDataLen = sm->eapRespDataLen;
366 eapol_set_bool(sm, EAPOL_eapResp, TRUE);
368 sm->lastRespData = NULL;
369 eapol_set_bool(sm, EAPOL_eapReq, FALSE);
370 eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
374 SM_STATE(EAP, DISCARD)
376 SM_ENTRY(EAP, DISCARD);
377 eapol_set_bool(sm, EAPOL_eapReq, FALSE);
378 eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
382 SM_STATE(EAP, IDENTITY)
385 size_t eapReqDataLen;
387 SM_ENTRY(EAP, IDENTITY);
388 eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
389 eap_sm_processIdentity(sm, eapReqData, eapReqDataLen);
390 free(sm->eapRespData);
391 sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId,
392 &sm->eapRespDataLen, 0);
396 SM_STATE(EAP, NOTIFICATION)
399 size_t eapReqDataLen;
401 SM_ENTRY(EAP, NOTIFICATION);
402 eapReqData = eapol_get_eapReqData(sm, &eapReqDataLen);
403 eap_sm_processNotify(sm, eapReqData, eapReqDataLen);
404 free(sm->eapRespData);
405 sm->eapRespData = eap_sm_buildNotify(sm, sm->reqId,
406 &sm->eapRespDataLen);
410 SM_STATE(EAP, RETRANSMIT)
412 SM_ENTRY(EAP, RETRANSMIT);
413 free(sm->eapRespData);
414 if (sm->lastRespData) {
415 sm->eapRespData = malloc(sm->lastRespDataLen);
416 if (sm->eapRespData) {
417 memcpy(sm->eapRespData, sm->lastRespData,
418 sm->lastRespDataLen);
419 sm->eapRespDataLen = sm->lastRespDataLen;
422 sm->eapRespData = NULL;
426 SM_STATE(EAP, SUCCESS)
428 SM_ENTRY(EAP, SUCCESS);
429 if (sm->eapKeyData != NULL)
430 sm->eapKeyAvailable = TRUE;
431 eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
432 /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but
433 * this seems to be required to avoid processing the same request
434 * twice when state machine is initialized. */
435 eapol_set_bool(sm, EAPOL_eapReq, FALSE);
436 /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here, but
437 * this seems to be required to get EAPOL Supplicant backend state
438 * machine into SUCCESS state. In addition, either eapResp or eapNoResp
439 * is required to be set after processing the received EAP frame. */
440 eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
444 SM_STATE(EAP, FAILURE)
446 SM_ENTRY(EAP, FAILURE);
447 eapol_set_bool(sm, EAPOL_eapFail, TRUE);
448 /* draft-ietf-eap-statemachine-02.pdf does not clear eapReq here, but
449 * this seems to be required to avoid processing the same request
450 * twice when state machine is initialized. */
451 eapol_set_bool(sm, EAPOL_eapReq, FALSE);
452 /* draft-ietf-eap-statemachine-02.pdf does not set eapNoResp here.
453 * However, either eapResp or eapNoResp is required to be set after
454 * processing the received EAP frame. */
455 eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
459 static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
461 /* At least Microsoft IAS and Meetinghouse Aegis seem to be sending
462 * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
463 * draft-ietf-eap-statemachine-05.pdf require that reqId == lastId.
464 * Accept this kind of Id if EAP workarounds are enabled. These are
465 * unauthenticated plaintext messages, so this should have minimal
466 * security implications (bit easier to fake EAP-Success/Failure). */
467 if (sm->workaround && reqId == ((lastId + 1) & 0xff)) {
468 wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
469 "identifier field in EAP Success: "
470 "reqId=%d lastId=%d (these are supposed to be "
471 "same)", reqId, lastId);
482 if (eapol_get_bool(sm, EAPOL_eapRestart) &&
483 eapol_get_bool(sm, EAPOL_portEnabled))
484 SM_ENTER_GLOBAL(EAP, INITIALIZE);
485 else if (!eapol_get_bool(sm, EAPOL_portEnabled))
486 SM_ENTER_GLOBAL(EAP, DISABLED);
487 else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
488 if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
489 wpa_printf(MSG_DEBUG, "EAP: more than %d "
490 "authentication rounds - abort",
491 EAP_MAX_AUTH_ROUNDS);
493 SM_ENTER_GLOBAL(EAP, FAILURE);
495 } else switch (sm->EAP_state) {
500 if (eapol_get_bool(sm, EAPOL_portEnabled))
501 SM_ENTER(EAP, INITIALIZE);
504 if (eapol_get_bool(sm, EAPOL_eapReq))
505 SM_ENTER(EAP, RECEIVED);
506 else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
507 sm->decision != DECISION_FAIL) ||
508 (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
509 sm->decision == DECISION_UNCOND_SUCC))
510 SM_ENTER(EAP, SUCCESS);
511 else if (eapol_get_bool(sm, EAPOL_altReject) ||
512 (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
513 sm->decision != DECISION_UNCOND_SUCC) ||
514 (eapol_get_bool(sm, EAPOL_altAccept) &&
515 sm->methodState != METHOD_CONT &&
516 sm->decision == DECISION_FAIL))
517 SM_ENTER(EAP, FAILURE);
518 else if (sm->selectedMethod == EAP_TYPE_LEAP &&
519 sm->leap_done && sm->decision != DECISION_FAIL &&
520 sm->methodState == METHOD_DONE)
521 SM_ENTER(EAP, SUCCESS);
522 else if (sm->selectedMethod == EAP_TYPE_PEAP &&
523 sm->peap_done && sm->decision != DECISION_FAIL &&
524 sm->methodState == METHOD_DONE)
525 SM_ENTER(EAP, SUCCESS);
528 duplicate = sm->reqId == sm->lastId;
529 if (sm->workaround && duplicate &&
530 memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
531 /* draft-ietf-eap-statemachine-05.txt uses
532 * (reqId == lastId) as the only verification for
533 * duplicate EAP requests. However, this misses cases
534 * where the AS is incorrectly using the same id again;
535 * and unfortunately, such implementations exist. Use
536 * MD5 hash as an extra verification for the packets
537 * being duplicate to workaround these issues. */
538 wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again,"
539 " but EAP packets were not identical");
540 wpa_printf(MSG_DEBUG, "EAP: workaround - assume this "
541 "is not a duplicate packet");
546 (sm->reqId == sm->lastId ||
547 eap_success_workaround(sm, sm->reqId, sm->lastId)) &&
548 sm->decision != DECISION_FAIL)
549 SM_ENTER(EAP, SUCCESS);
550 else if (sm->methodState != METHOD_CONT &&
552 sm->decision != DECISION_UNCOND_SUCC) ||
553 (sm->rxSuccess && sm->decision == DECISION_FAIL)) &&
554 (sm->reqId == sm->lastId ||
555 eap_success_workaround(sm, sm->reqId, sm->lastId)))
556 SM_ENTER(EAP, FAILURE);
557 else if (sm->rxReq && duplicate)
558 SM_ENTER(EAP, RETRANSMIT);
559 else if (sm->rxReq && !duplicate &&
560 sm->reqMethod == EAP_TYPE_NOTIFICATION &&
561 sm->allowNotifications)
562 SM_ENTER(EAP, NOTIFICATION);
563 else if (sm->rxReq && !duplicate &&
564 sm->selectedMethod == EAP_TYPE_NONE &&
565 sm->reqMethod == EAP_TYPE_IDENTITY)
566 SM_ENTER(EAP, IDENTITY);
567 else if (sm->rxReq && !duplicate &&
568 sm->selectedMethod == EAP_TYPE_NONE &&
569 sm->reqMethod != EAP_TYPE_IDENTITY &&
570 sm->reqMethod != EAP_TYPE_NOTIFICATION)
571 SM_ENTER(EAP, GET_METHOD);
572 else if (sm->rxReq && !duplicate &&
573 sm->reqMethod == sm->selectedMethod &&
574 sm->methodState != METHOD_DONE)
575 SM_ENTER(EAP, METHOD);
576 else if (sm->selectedMethod == EAP_TYPE_LEAP &&
577 (sm->rxSuccess || sm->rxResp))
578 SM_ENTER(EAP, METHOD);
580 SM_ENTER(EAP, DISCARD);
583 if (sm->selectedMethod == sm->reqMethod)
584 SM_ENTER(EAP, METHOD);
586 SM_ENTER(EAP, SEND_RESPONSE);
590 SM_ENTER(EAP, DISCARD);
592 SM_ENTER(EAP, SEND_RESPONSE);
594 case EAP_SEND_RESPONSE:
601 SM_ENTER(EAP, SEND_RESPONSE);
603 case EAP_NOTIFICATION:
604 SM_ENTER(EAP, SEND_RESPONSE);
607 SM_ENTER(EAP, SEND_RESPONSE);
617 static Boolean eap_sm_allowMethod(struct eap_sm *sm, EapType method)
619 struct wpa_ssid *config = eap_get_config(sm);
622 if (!wpa_config_allowed_eap_method(config, method))
624 for (i = 0; i < NUM_EAP_METHODS; i++) {
625 if (eap_methods[i]->method == method)
632 static u8 *eap_sm_buildNak(struct eap_sm *sm, int id, size_t *len)
634 struct wpa_ssid *config = eap_get_config(sm);
635 struct eap_hdr *resp;
639 wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %d not "
640 "allowed)", sm->reqMethod);
641 *len = sizeof(struct eap_hdr) + 1;
642 resp = malloc(*len + NUM_EAP_METHODS);
646 resp->code = EAP_CODE_RESPONSE;
647 resp->identifier = id;
648 pos = (u8 *) (resp + 1);
649 *pos++ = EAP_TYPE_NAK;
651 for (i = 0; i < NUM_EAP_METHODS; i++) {
652 if (wpa_config_allowed_eap_method(config,
653 eap_methods[i]->method)) {
654 *pos++ = eap_methods[i]->method;
660 *pos = EAP_TYPE_NONE;
663 wpa_hexdump(MSG_DEBUG, "EAP: allowed methods",
664 ((u8 *) (resp + 1)) + 1, found);
666 resp->length = host_to_be16(*len);
672 static void eap_sm_processIdentity(struct eap_sm *sm, u8 *req, size_t len)
674 struct eap_hdr *hdr = (struct eap_hdr *) req;
675 u8 *pos = (u8 *) (hdr + 1);
677 /* TODO: could save displayable message so that it can be shown to the
678 * user in case of interaction is required */
679 wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
680 pos, be_to_host16(hdr->length) - 5);
684 u8 *eap_sm_buildIdentity(struct eap_sm *sm, int id, size_t *len,
687 struct wpa_ssid *config = eap_get_config(sm);
688 struct eap_hdr *resp;
693 if (config == NULL) {
694 wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
695 "was not available");
699 if (sm->m && sm->m->get_identity &&
700 (identity = sm->m->get_identity(sm, sm->eap_method_priv,
701 &identity_len)) != NULL) {
702 wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
703 "identity", identity, identity_len);
704 } else if (!encrypted && config->anonymous_identity) {
705 identity = config->anonymous_identity;
706 identity_len = config->anonymous_identity_len;
707 wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
708 identity, identity_len);
710 identity = config->identity;
711 identity_len = config->identity_len;
712 wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
713 identity, identity_len);
716 if (identity == NULL) {
717 wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
718 "configuration was not available");
719 eap_sm_request_identity(sm, config);
724 *len = sizeof(struct eap_hdr) + 1 + identity_len;
729 resp->code = EAP_CODE_RESPONSE;
730 resp->identifier = id;
731 resp->length = host_to_be16(*len);
732 pos = (u8 *) (resp + 1);
733 *pos++ = EAP_TYPE_IDENTITY;
734 memcpy(pos, identity, identity_len);
740 static void eap_sm_processNotify(struct eap_sm *sm, u8 *req, size_t len)
742 struct eap_hdr *hdr = (struct eap_hdr *) req;
743 u8 *pos = (u8 *) (hdr + 1);
745 /* TODO: log the Notification Request and make it available for UI */
746 wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
747 pos, be_to_host16(hdr->length) - 5);
751 static u8 *eap_sm_buildNotify(struct eap_sm *sm, int id, size_t *len)
753 struct eap_hdr *resp;
756 wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
757 *len = sizeof(struct eap_hdr) + 1;
762 resp->code = EAP_CODE_RESPONSE;
763 resp->identifier = id;
764 resp->length = host_to_be16(*len);
765 pos = (u8 *) (resp + 1);
766 *pos = EAP_TYPE_NOTIFICATION;
772 static void eap_sm_parseEapReq(struct eap_sm *sm, u8 *req, size_t len)
778 sm->rxReq = sm->rxSuccess = sm->rxFailure = FALSE;
780 sm->reqMethod = EAP_TYPE_NONE;
782 if (req == NULL || len < sizeof(*hdr))
785 hdr = (struct eap_hdr *) req;
786 plen = be_to_host16(hdr->length);
788 wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
789 "(len=%lu plen=%lu)",
790 (unsigned long) len, (unsigned long) plen);
794 sm->reqId = hdr->identifier;
796 if (sm->workaround) {
798 MD5Update(&context, req, len);
799 MD5Final(sm->req_md5, &context);
803 case EAP_CODE_REQUEST:
805 if (plen > sizeof(*hdr))
806 sm->reqMethod = *((u8 *) (hdr + 1));
807 wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request method=%d "
808 "id=%d", sm->reqMethod, sm->reqId);
810 case EAP_CODE_RESPONSE:
811 if (sm->selectedMethod == EAP_TYPE_LEAP) {
813 if (plen > sizeof(*hdr))
814 sm->reqMethod = *((u8 *) (hdr + 1));
815 wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
816 "LEAP method=%d id=%d",
817 sm->reqMethod, sm->reqId);
820 wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
822 case EAP_CODE_SUCCESS:
823 wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
824 sm->rxSuccess = TRUE;
826 case EAP_CODE_FAILURE:
827 wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
828 sm->rxFailure = TRUE;
831 wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
832 "code %d", hdr->code);
838 struct eap_sm *eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
843 sm = malloc(sizeof(*sm));
846 memset(sm, 0, sizeof(*sm));
847 sm->eapol_ctx = eapol_ctx;
848 sm->eapol_cb = eapol_cb;
849 sm->msg_ctx = msg_ctx;
850 sm->ClientTimeout = 60;
852 sm->ssl_ctx = tls_init();
853 if (sm->ssl_ctx == NULL) {
854 wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
864 void eap_sm_deinit(struct eap_sm *sm)
868 eap_deinit_prev_method(sm, "EAP deinit");
869 free(sm->lastRespData);
870 free(sm->eapRespData);
871 free(sm->eapKeyData);
872 tls_deinit(sm->ssl_ctx);
877 int eap_sm_step(struct eap_sm *sm)
885 } while (sm->changed);
890 void eap_sm_abort(struct eap_sm *sm)
892 /* release system resources that may have been allocated for the
893 * authentication session */
894 free(sm->eapRespData);
895 sm->eapRespData = NULL;
896 free(sm->eapKeyData);
897 sm->eapKeyData = NULL;
901 static const char * eap_sm_state_txt(int state)
916 case EAP_SEND_RESPONSE:
917 return "SEND_RESPONSE";
922 case EAP_NOTIFICATION:
923 return "NOTIFICATION";
936 static const char * eap_sm_method_state_txt(int state)
945 case METHOD_MAY_CONT:
955 static const char * eap_sm_decision_txt(int decision)
960 case DECISION_COND_SUCC:
962 case DECISION_UNCOND_SUCC:
963 return "UNCOND_SUCC";
970 int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
977 len = snprintf(buf, buflen,
979 eap_sm_state_txt(sm->EAP_state));
981 if (sm->selectedMethod != EAP_TYPE_NONE) {
986 const struct eap_method *m =
987 eap_sm_get_eap_methods(sm->selectedMethod);
993 len += snprintf(buf + len, buflen - len,
994 "selectedMethod=%d (EAP-%s)\n",
995 sm->selectedMethod, name);
997 if (sm->m && sm->m->get_status) {
998 len += sm->m->get_status(sm, sm->eap_method_priv,
999 buf + len, buflen - len,
1005 len += snprintf(buf + len, buflen - len,
1009 "ClientTimeout=%d\n",
1011 eap_sm_method_state_txt(sm->methodState),
1012 eap_sm_decision_txt(sm->decision),
1020 typedef enum { TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP } eap_ctrl_req_type;
1022 static void eap_sm_request(struct eap_sm *sm, struct wpa_ssid *config,
1023 eap_ctrl_req_type type, char *msg, size_t msglen)
1031 if (config == NULL || sm == NULL)
1038 config->pending_req_identity++;
1043 config->pending_req_password++;
1048 tmp = malloc(msglen + 3);
1052 memcpy(tmp + 1, msg, msglen);
1053 tmp[msglen + 1] = ']';
1054 tmp[msglen + 2] = '\0';
1056 free(config->pending_req_otp);
1057 config->pending_req_otp = tmp;
1058 config->pending_req_otp_len = msglen + 3;
1060 if (config->pending_req_otp == NULL)
1062 txt = config->pending_req_otp;
1069 buflen = 100 + strlen(txt) + config->ssid_len;
1070 buf = malloc(buflen);
1073 len = snprintf(buf, buflen, "CTRL-REQ-%s-%d:%s needed for SSID ",
1074 field, config->id, txt);
1075 if (config->ssid && buflen > len + config->ssid_len) {
1076 memcpy(buf + len, config->ssid, config->ssid_len);
1077 len += config->ssid_len;
1080 wpa_msg(sm->msg_ctx, MSG_INFO, buf);
1085 void eap_sm_request_identity(struct eap_sm *sm, struct wpa_ssid *config)
1087 eap_sm_request(sm, config, TYPE_IDENTITY, NULL, 0);
1091 void eap_sm_request_password(struct eap_sm *sm, struct wpa_ssid *config)
1093 eap_sm_request(sm, config, TYPE_PASSWORD, NULL, 0);
1097 void eap_sm_request_otp(struct eap_sm *sm, struct wpa_ssid *config,
1098 char *msg, size_t msg_len)
1100 eap_sm_request(sm, config, TYPE_OTP, msg, msg_len);
1104 void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
1106 struct wpa_ssid *config = eap_get_config(sm);
1111 /* Re-send any pending requests for user data since a new control
1112 * interface was added. This handles cases where the EAP authentication
1113 * starts immediately after system startup when the user interface is
1114 * not yet running. */
1115 if (config->pending_req_identity)
1116 eap_sm_request_identity(sm, config);
1117 if (config->pending_req_password)
1118 eap_sm_request_password(sm, config);
1119 if (config->pending_req_otp)
1120 eap_sm_request_otp(sm, config, NULL, 0);
1124 u8 eap_get_type(const char *name)
1127 for (i = 0; i < NUM_EAP_METHODS; i++) {
1128 if (strcmp(eap_methods[i]->name, name) == 0)
1129 return eap_methods[i]->method;
1131 return EAP_TYPE_NONE;
1135 static int eap_allowed_phase2_type(int type)
1137 return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
1138 type != EAP_TYPE_FAST;
1142 u8 eap_get_phase2_type(const char *name)
1144 u8 type = eap_get_type(name);
1145 if (eap_allowed_phase2_type(type))
1147 return EAP_TYPE_NONE;
1151 u8 *eap_get_phase2_types(struct wpa_ssid *config, size_t *count)
1157 buf = malloc(NUM_EAP_METHODS);
1161 for (i = 0; i < NUM_EAP_METHODS; i++) {
1162 method = eap_methods[i]->method;
1163 if (eap_allowed_phase2_type(method)) {
1164 if (method == EAP_TYPE_TLS && config &&
1165 config->private_key2 == NULL)
1167 buf[*count] = method;
1176 void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
1178 sm->fast_reauth = enabled;
1182 void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
1184 sm->workaround = workaround;
1188 struct wpa_ssid * eap_get_config(struct eap_sm *sm)
1190 return sm->eapol_cb->get_config(sm->eapol_ctx);
1194 int eap_key_available(struct eap_sm *sm)
1196 return sm ? sm->eapKeyAvailable : 0;
1200 void eap_notify_success(struct eap_sm *sm)
1203 sm->decision = DECISION_COND_SUCC;
1204 sm->EAP_state = EAP_SUCCESS;
1209 u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
1211 if (sm == NULL || sm->eapKeyData == NULL) {
1216 *len = sm->eapKeyDataLen;
1217 return sm->eapKeyData;
1221 u8 * eap_get_eapRespData(struct eap_sm *sm, size_t *len)
1225 if (sm == NULL || sm->eapRespData == NULL) {
1230 resp = sm->eapRespData;
1231 *len = sm->eapRespDataLen;
1232 sm->eapRespData = NULL;
1233 sm->eapRespDataLen = 0;
1239 void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
1242 sm->scard_ctx = ctx;