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;