Import of hostapd 0.4.9
[dragonfly.git] / contrib / hostapd-0.4.9 / eap.c
bloba20147e79e7885e122394892b3e996a57f046c50
1 /*
2 * hostapd / EAP Standalone Authenticator state machine
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
10 * license.
12 * See README and COPYING for more details.
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <unistd.h>
18 #include <netinet/in.h>
19 #include <string.h>
20 #include <sys/socket.h>
22 #include "hostapd.h"
23 #include "eloop.h"
24 #include "sta_info.h"
25 #include "eap_i.h"
27 #define EAP_MAX_AUTH_ROUNDS 50
29 extern const struct eap_method eap_method_identity;
30 #ifdef EAP_MD5
31 extern const struct eap_method eap_method_md5;
32 #endif /* EAP_MD5 */
33 #ifdef EAP_TLS
34 extern const struct eap_method eap_method_tls;
35 #endif /* EAP_TLS */
36 #ifdef EAP_MSCHAPv2
37 extern const struct eap_method eap_method_mschapv2;
38 #endif /* EAP_MSCHAPv2 */
39 #ifdef EAP_PEAP
40 extern const struct eap_method eap_method_peap;
41 #endif /* EAP_PEAP */
42 #ifdef EAP_TLV
43 extern const struct eap_method eap_method_tlv;
44 #endif /* EAP_TLV */
45 #ifdef EAP_GTC
46 extern const struct eap_method eap_method_gtc;
47 #endif /* EAP_GTC */
48 #ifdef EAP_TTLS
49 extern const struct eap_method eap_method_ttls;
50 #endif /* EAP_TTLS */
51 #ifdef EAP_SIM
52 extern const struct eap_method eap_method_sim;
53 #endif /* EAP_SIM */
54 #ifdef EAP_PAX
55 extern const struct eap_method eap_method_pax;
56 #endif /* EAP_PAX */
57 #ifdef EAP_PSK
58 extern const struct eap_method eap_method_psk;
59 #endif /* EAP_PSK */
61 static const struct eap_method *eap_methods[] =
63 &eap_method_identity,
64 #ifdef EAP_MD5
65 &eap_method_md5,
66 #endif /* EAP_MD5 */
67 #ifdef EAP_TLS
68 &eap_method_tls,
69 #endif /* EAP_TLS */
70 #ifdef EAP_MSCHAPv2
71 &eap_method_mschapv2,
72 #endif /* EAP_MSCHAPv2 */
73 #ifdef EAP_PEAP
74 &eap_method_peap,
75 #endif /* EAP_PEAP */
76 #ifdef EAP_TTLS
77 &eap_method_ttls,
78 #endif /* EAP_TTLS */
79 #ifdef EAP_TLV
80 &eap_method_tlv,
81 #endif /* EAP_TLV */
82 #ifdef EAP_GTC
83 &eap_method_gtc,
84 #endif /* EAP_GTC */
85 #ifdef EAP_SIM
86 &eap_method_sim,
87 #endif /* EAP_SIM */
88 #ifdef EAP_PAX
89 &eap_method_pax,
90 #endif /* EAP_PAX */
91 #ifdef EAP_PSK
92 &eap_method_psk,
93 #endif /* EAP_PSK */
95 #define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0]))
98 const struct eap_method * eap_sm_get_eap_methods(int method)
100 int i;
101 for (i = 0; i < NUM_EAP_METHODS; i++) {
102 if (eap_methods[i]->method == method)
103 return eap_methods[i];
105 return NULL;
108 static void eap_user_free(struct eap_user *user);
111 /* EAP state machines are described in draft-ietf-eap-statemachine-05.txt */
113 static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
114 int eapSRTT, int eapRTTVAR,
115 int methodTimeout);
116 static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len);
117 static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len);
118 static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len);
119 static int eap_sm_nextId(struct eap_sm *sm, int id);
120 static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len);
121 static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm);
122 static int eap_sm_Policy_getDecision(struct eap_sm *sm);
123 static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
126 /* Definitions for clarifying state machine implementation */
127 #define SM_STATE(machine, state) \
128 static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \
129 int global)
131 #define SM_ENTRY(machine, state) \
132 if (!global || sm->machine ## _state != machine ## _ ## state) { \
133 sm->changed = TRUE; \
134 wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \
136 sm->machine ## _state = machine ## _ ## state;
138 #define SM_ENTER(machine, state) \
139 sm_ ## machine ## _ ## state ## _Enter(sm, 0)
140 #define SM_ENTER_GLOBAL(machine, state) \
141 sm_ ## machine ## _ ## state ## _Enter(sm, 1)
143 #define SM_STEP(machine) \
144 static void sm_ ## machine ## _Step(struct eap_sm *sm)
146 #define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
149 static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
151 return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
155 static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
156 Boolean value)
158 sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
162 static void eapol_set_eapReqData(struct eap_sm *sm,
163 const u8 *eapReqData, size_t eapReqDataLen)
165 wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL",
166 sm->eapReqData, sm->eapReqDataLen);
167 sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen);
171 static void eapol_set_eapKeyData(struct eap_sm *sm,
172 const u8 *eapKeyData, size_t eapKeyDataLen)
174 wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL",
175 sm->eapKeyData, sm->eapKeyDataLen);
176 sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen);
180 int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
181 int phase2)
183 struct eap_user *user;
185 if (sm == NULL || sm->eapol_cb == NULL ||
186 sm->eapol_cb->get_eap_user == NULL)
187 return -1;
189 eap_user_free(sm->user);
190 sm->user = NULL;
192 user = malloc(sizeof(*user));
193 if (user == NULL)
194 return -1;
195 memset(user, 0, sizeof(*user));
197 if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
198 identity_len, phase2, user) != 0) {
199 eap_user_free(user);
200 return -1;
203 sm->user = user;
204 sm->user_eap_method_index = 0;
206 return 0;
210 SM_STATE(EAP, DISABLED)
212 SM_ENTRY(EAP, DISABLED);
213 sm->num_rounds = 0;
217 SM_STATE(EAP, INITIALIZE)
219 SM_ENTRY(EAP, INITIALIZE);
221 sm->currentId = -1;
222 eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
223 eapol_set_bool(sm, EAPOL_eapFail, FALSE);
224 eapol_set_bool(sm, EAPOL_eapTimeout, FALSE);
225 free(sm->eapKeyData);
226 sm->eapKeyData = NULL;
227 sm->eapKeyDataLen = 0;
228 /* eapKeyAvailable = FALSE */
229 eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
231 /* This is not defined in draft-ietf-eap-statemachine-05.txt, but
232 * method state needs to be reseted here so that it does not remain in
233 * success state when re-authentication starts. */
234 if (sm->m && sm->eap_method_priv) {
235 sm->m->reset(sm, sm->eap_method_priv);
236 sm->eap_method_priv = NULL;
238 sm->m = NULL;
239 sm->user_eap_method_index = 0;
241 if (sm->backend_auth) {
242 sm->currentMethod = EAP_TYPE_NONE;
243 /* parse rxResp, respId, respMethod */
244 eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
245 if (sm->rxResp) {
246 sm->currentId = sm->respId;
249 sm->num_rounds = 0;
253 SM_STATE(EAP, PICK_UP_METHOD)
255 SM_ENTRY(EAP, PICK_UP_METHOD);
257 if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
258 sm->currentMethod = sm->respMethod;
259 if (sm->m && sm->eap_method_priv) {
260 sm->m->reset(sm, sm->eap_method_priv);
261 sm->eap_method_priv = NULL;
263 sm->m = eap_sm_get_eap_methods(sm->currentMethod);
264 if (sm->m && sm->m->initPickUp) {
265 sm->eap_method_priv = sm->m->initPickUp(sm);
266 if (sm->eap_method_priv == NULL) {
267 wpa_printf(MSG_DEBUG, "EAP: Failed to "
268 "initialize EAP method %d",
269 sm->currentMethod);
270 sm->m = NULL;
271 sm->currentMethod = EAP_TYPE_NONE;
273 } else {
274 sm->m = NULL;
275 sm->currentMethod = EAP_TYPE_NONE;
281 SM_STATE(EAP, IDLE)
283 SM_ENTRY(EAP, IDLE);
285 sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount,
286 sm->eapSRTT, sm->eapRTTVAR,
287 sm->methodTimeout);
291 SM_STATE(EAP, RETRANSMIT)
293 SM_ENTRY(EAP, RETRANSMIT);
295 /* TODO: Is this needed since EAPOL state machines take care of
296 * retransmit? */
300 SM_STATE(EAP, RECEIVED)
302 SM_ENTRY(EAP, RECEIVED);
304 /* parse rxResp, respId, respMethod */
305 eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen);
306 sm->num_rounds++;
310 SM_STATE(EAP, DISCARD)
312 SM_ENTRY(EAP, DISCARD);
313 eapol_set_bool(sm, EAPOL_eapResp, FALSE);
314 eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
318 SM_STATE(EAP, SEND_REQUEST)
320 SM_ENTRY(EAP, SEND_REQUEST);
322 sm->retransCount = 0;
323 if (sm->eapReqData) {
324 eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
325 free(sm->lastReqData);
326 sm->lastReqData = sm->eapReqData;
327 sm->lastReqDataLen = sm->eapReqDataLen;
328 sm->eapReqData = NULL;
329 sm->eapReqDataLen = 0;
330 eapol_set_bool(sm, EAPOL_eapResp, FALSE);
331 eapol_set_bool(sm, EAPOL_eapReq, TRUE);
332 } else {
333 wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
334 eapol_set_bool(sm, EAPOL_eapResp, FALSE);
335 eapol_set_bool(sm, EAPOL_eapReq, FALSE);
336 eapol_set_bool(sm, EAPOL_eapNoReq, TRUE);
341 SM_STATE(EAP, INTEGRITY_CHECK)
343 SM_ENTRY(EAP, INTEGRITY_CHECK);
345 if (sm->m->check) {
346 sm->ignore = sm->m->check(sm, sm->eap_method_priv,
347 sm->eapRespData, sm->eapRespDataLen);
352 SM_STATE(EAP, METHOD_REQUEST)
354 SM_ENTRY(EAP, METHOD_REQUEST);
356 if (sm->m == NULL) {
357 wpa_printf(MSG_DEBUG, "EAP: method not initialized");
358 return;
361 sm->currentId = eap_sm_nextId(sm, sm->currentId);
362 wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
363 sm->currentId);
364 sm->lastId = sm->currentId;
365 free(sm->eapReqData);
366 sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
367 sm->currentId, &sm->eapReqDataLen);
368 if (sm->m->getTimeout)
369 sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
370 else
371 sm->methodTimeout = 0;
375 SM_STATE(EAP, METHOD_RESPONSE)
377 SM_ENTRY(EAP, METHOD_RESPONSE);
379 sm->m->process(sm, sm->eap_method_priv, sm->eapRespData,
380 sm->eapRespDataLen);
381 if (sm->m->isDone(sm, sm->eap_method_priv)) {
382 eap_sm_Policy_update(sm, NULL, 0);
383 free(sm->eapKeyData);
384 if (sm->m->getKey) {
385 sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
386 &sm->eapKeyDataLen);
387 } else {
388 sm->eapKeyData = NULL;
389 sm->eapKeyDataLen = 0;
391 sm->methodState = METHOD_END;
392 } else {
393 sm->methodState = METHOD_CONTINUE;
398 SM_STATE(EAP, PROPOSE_METHOD)
400 SM_ENTRY(EAP, PROPOSE_METHOD);
402 sm->currentMethod = eap_sm_Policy_getNextMethod(sm);
403 if (sm->m && sm->eap_method_priv) {
404 sm->m->reset(sm, sm->eap_method_priv);
405 sm->eap_method_priv = NULL;
407 sm->m = eap_sm_get_eap_methods(sm->currentMethod);
408 if (sm->m) {
409 sm->eap_method_priv = sm->m->init(sm);
410 if (sm->eap_method_priv == NULL) {
411 wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
412 "method %d", sm->currentMethod);
413 sm->m = NULL;
414 sm->currentMethod = EAP_TYPE_NONE;
417 if (sm->currentMethod == EAP_TYPE_IDENTITY ||
418 sm->currentMethod == EAP_TYPE_NOTIFICATION)
419 sm->methodState = METHOD_CONTINUE;
420 else
421 sm->methodState = METHOD_PROPOSED;
425 SM_STATE(EAP, NAK)
427 struct eap_hdr *nak;
428 size_t len = 0;
429 u8 *pos, *nak_list = NULL;
431 SM_ENTRY(EAP, NAK);
433 if (sm->eap_method_priv) {
434 sm->m->reset(sm, sm->eap_method_priv);
435 sm->eap_method_priv = NULL;
437 sm->m = NULL;
439 nak = (struct eap_hdr *) sm->eapRespData;
440 if (nak && sm->eapRespDataLen > sizeof(*nak)) {
441 len = ntohs(nak->length);
442 if (len > sm->eapRespDataLen)
443 len = sm->eapRespDataLen;
444 pos = (u8 *) (nak + 1);
445 len -= sizeof(*nak);
446 if (*pos == EAP_TYPE_NAK) {
447 pos++;
448 len--;
449 nak_list = pos;
452 eap_sm_Policy_update(sm, nak_list, len);
456 SM_STATE(EAP, SELECT_ACTION)
458 SM_ENTRY(EAP, SELECT_ACTION);
460 sm->decision = eap_sm_Policy_getDecision(sm);
464 SM_STATE(EAP, TIMEOUT_FAILURE)
466 SM_ENTRY(EAP, TIMEOUT_FAILURE);
468 eapol_set_bool(sm, EAPOL_eapTimeout, TRUE);
472 SM_STATE(EAP, FAILURE)
474 SM_ENTRY(EAP, FAILURE);
476 free(sm->eapReqData);
477 sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId,
478 &sm->eapReqDataLen);
479 if (sm->eapReqData) {
480 eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
481 free(sm->eapReqData);
482 sm->eapReqData = NULL;
483 sm->eapReqDataLen = 0;
485 free(sm->lastReqData);
486 sm->lastReqData = NULL;
487 sm->lastReqDataLen = 0;
488 eapol_set_bool(sm, EAPOL_eapFail, TRUE);
492 SM_STATE(EAP, SUCCESS)
494 SM_ENTRY(EAP, SUCCESS);
496 free(sm->eapReqData);
497 sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId,
498 &sm->eapReqDataLen);
499 if (sm->eapReqData) {
500 eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen);
501 free(sm->eapReqData);
502 sm->eapReqData = NULL;
503 sm->eapReqDataLen = 0;
505 free(sm->lastReqData);
506 sm->lastReqData = NULL;
507 sm->lastReqDataLen = 0;
508 if (sm->eapKeyData) {
509 eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen);
511 eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
515 SM_STEP(EAP)
517 if (eapol_get_bool(sm, EAPOL_eapRestart) &&
518 eapol_get_bool(sm, EAPOL_portEnabled))
519 SM_ENTER_GLOBAL(EAP, INITIALIZE);
520 else if (!eapol_get_bool(sm, EAPOL_portEnabled))
521 SM_ENTER_GLOBAL(EAP, DISABLED);
522 else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
523 if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
524 wpa_printf(MSG_DEBUG, "EAP: more than %d "
525 "authentication rounds - abort",
526 EAP_MAX_AUTH_ROUNDS);
527 sm->num_rounds++;
528 SM_ENTER_GLOBAL(EAP, FAILURE);
530 } else switch (sm->EAP_state) {
531 case EAP_INITIALIZE:
532 if (sm->backend_auth) {
533 if (!sm->rxResp)
534 SM_ENTER(EAP, SELECT_ACTION);
535 else if (sm->rxResp &&
536 (sm->respMethod == EAP_TYPE_NAK ||
537 sm->respMethod == EAP_TYPE_EXPANDED_NAK))
538 SM_ENTER(EAP, NAK);
539 else
540 SM_ENTER(EAP, PICK_UP_METHOD);
541 } else {
542 SM_ENTER(EAP, SELECT_ACTION);
544 break;
545 case EAP_PICK_UP_METHOD:
546 if (sm->currentMethod == EAP_TYPE_NONE) {
547 SM_ENTER(EAP, SELECT_ACTION);
548 } else {
549 SM_ENTER(EAP, METHOD_RESPONSE);
551 break;
552 case EAP_DISABLED:
553 if (eapol_get_bool(sm, EAPOL_portEnabled))
554 SM_ENTER(EAP, INITIALIZE);
555 break;
556 case EAP_IDLE:
557 if (sm->retransWhile == 0)
558 SM_ENTER(EAP, RETRANSMIT);
559 else if (eapol_get_bool(sm, EAPOL_eapResp))
560 SM_ENTER(EAP, RECEIVED);
561 break;
562 case EAP_RETRANSMIT:
563 if (sm->retransCount > sm->MaxRetrans)
564 SM_ENTER(EAP, TIMEOUT_FAILURE);
565 else
566 SM_ENTER(EAP, IDLE);
567 break;
568 case EAP_RECEIVED:
569 if (sm->rxResp && (sm->respId == sm->currentId) &&
570 (sm->respMethod == EAP_TYPE_NAK ||
571 sm->respMethod == EAP_TYPE_EXPANDED_NAK)
572 && (sm->methodState == METHOD_PROPOSED))
573 SM_ENTER(EAP, NAK);
574 else if (sm->rxResp && (sm->respId == sm->currentId) &&
575 (sm->respMethod == sm->currentMethod))
576 SM_ENTER(EAP, INTEGRITY_CHECK);
577 else
578 SM_ENTER(EAP, DISCARD);
579 break;
580 case EAP_DISCARD:
581 SM_ENTER(EAP, IDLE);
582 break;
583 case EAP_SEND_REQUEST:
584 SM_ENTER(EAP, IDLE);
585 break;
586 case EAP_INTEGRITY_CHECK:
587 if (sm->ignore)
588 SM_ENTER(EAP, DISCARD);
589 else
590 SM_ENTER(EAP, METHOD_RESPONSE);
591 break;
592 case EAP_METHOD_REQUEST:
593 SM_ENTER(EAP, SEND_REQUEST);
594 break;
595 case EAP_METHOD_RESPONSE:
596 if (sm->methodState == METHOD_END)
597 SM_ENTER(EAP, SELECT_ACTION);
598 else
599 SM_ENTER(EAP, METHOD_REQUEST);
600 break;
601 case EAP_PROPOSE_METHOD:
602 SM_ENTER(EAP, METHOD_REQUEST);
603 break;
604 case EAP_NAK:
605 SM_ENTER(EAP, SELECT_ACTION);
606 break;
607 case EAP_SELECT_ACTION:
608 if (sm->decision == DECISION_FAILURE)
609 SM_ENTER(EAP, FAILURE);
610 else if (sm->decision == DECISION_SUCCESS)
611 SM_ENTER(EAP, SUCCESS);
612 else
613 SM_ENTER(EAP, PROPOSE_METHOD);
614 break;
615 case EAP_TIMEOUT_FAILURE:
616 break;
617 case EAP_FAILURE:
618 break;
619 case EAP_SUCCESS:
620 break;
625 static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
626 int eapSRTT, int eapRTTVAR,
627 int methodTimeout)
629 /* For now, retransmission is done in EAPOL state machines, so make
630 * sure EAP state machine does not end up trying to retransmit packets.
632 return 1;
636 static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len)
638 struct eap_hdr *hdr;
639 size_t plen;
641 /* parse rxResp, respId, respMethod */
642 sm->rxResp = FALSE;
643 sm->respId = -1;
644 sm->respMethod = EAP_TYPE_NONE;
646 if (resp == NULL || len < sizeof(*hdr))
647 return;
649 hdr = (struct eap_hdr *) resp;
650 plen = ntohs(hdr->length);
651 if (plen > len) {
652 wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
653 "(len=%lu plen=%lu)", (unsigned long) len,
654 (unsigned long) plen);
655 return;
658 sm->respId = hdr->identifier;
660 if (hdr->code == EAP_CODE_RESPONSE)
661 sm->rxResp = TRUE;
663 if (len > sizeof(*hdr))
664 sm->respMethod = *((u8 *) (hdr + 1));
666 wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
667 "respMethod=%d", sm->rxResp, sm->respId, sm->respMethod);
671 static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len)
673 struct eap_hdr *resp;
674 wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
676 *len = sizeof(*resp);
677 resp = malloc(*len);
678 if (resp == NULL)
679 return NULL;
680 resp->code = EAP_CODE_SUCCESS;
681 resp->identifier = id;
682 resp->length = htons(*len);
684 return (u8 *) resp;
688 static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len)
690 struct eap_hdr *resp;
691 wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
693 *len = sizeof(*resp);
694 resp = malloc(*len);
695 if (resp == NULL)
696 return NULL;
697 resp->code = EAP_CODE_FAILURE;
698 resp->identifier = id;
699 resp->length = htons(*len);
701 return (u8 *) resp;
705 static int eap_sm_nextId(struct eap_sm *sm, int id)
707 if (id < 0) {
708 /* RFC 3748 Ch 4.1: recommended to initalize Identifier with a
709 * random number */
710 id = rand() & 0xff;
711 if (id != sm->lastId)
712 return id;
714 return (id + 1) & 0xff;
718 void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len)
720 int i, j;
722 wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
723 "index %d)", sm->user_eap_method_index);
725 wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
726 sm->user->methods, EAP_MAX_METHODS);
727 wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
728 nak_list, len);
730 i = sm->user_eap_method_index;
731 while (i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE) {
732 for (j = 0; j < len; j++) {
733 if (nak_list[j] == sm->user->methods[i]) {
734 break;
738 if (j < len) {
739 /* found */
740 i++;
741 continue;
744 /* not found - remove from the list */
745 memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
746 EAP_MAX_METHODS - i - 1);
747 sm->user->methods[EAP_MAX_METHODS - 1] = EAP_TYPE_NONE;
750 wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
751 sm->user->methods, EAP_MAX_METHODS);
755 static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len)
757 if (nak_list == NULL || sm == NULL || sm->user == NULL)
758 return;
760 if (sm->user->phase2) {
761 wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
762 " info was selected - reject");
763 sm->decision = DECISION_FAILURE;
764 return;
767 eap_sm_process_nak(sm, nak_list, len);
771 static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm)
773 EapType next;
775 /* In theory, there should be no problems with starting
776 * re-authentication with something else than EAP-Request/Identity and
777 * this does indeed work with wpa_supplicant. However, at least Funk
778 * Supplicant seemed to ignore re-auth if it skipped
779 * EAP-Request/Identity.
780 * Re-auth sets currentId == -1, so that can be used here to select
781 * whether Identity needs to be requested again. */
782 if (sm->identity == NULL || sm->currentId == -1) {
783 next = EAP_TYPE_IDENTITY;
784 sm->update_user = TRUE;
785 } else if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
786 sm->user->methods[sm->user_eap_method_index] !=
787 EAP_TYPE_NONE) {
788 next = sm->user->methods[sm->user_eap_method_index++];
789 } else {
790 next = EAP_TYPE_NONE;
792 wpa_printf(MSG_DEBUG, "EAP: getNextMethod: type %d", next);
793 return next;
797 static int eap_sm_Policy_getDecision(struct eap_sm *sm)
799 if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
800 sm->m->isSuccess(sm, sm->eap_method_priv)) {
801 wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
802 "SUCCESS");
803 sm->update_user = TRUE;
804 return DECISION_SUCCESS;
807 if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
808 !sm->m->isSuccess(sm, sm->eap_method_priv)) {
809 wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
810 "FAILURE");
811 sm->update_user = TRUE;
812 return DECISION_FAILURE;
815 if ((sm->user == NULL || sm->update_user) && sm->identity) {
816 if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
817 wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
818 "found from database -> FAILURE");
819 return DECISION_FAILURE;
821 sm->update_user = FALSE;
824 if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
825 sm->user->methods[sm->user_eap_method_index] != EAP_TYPE_NONE) {
826 wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
827 "available -> CONTINUE");
828 return DECISION_CONTINUE;
831 if (sm->identity == NULL || sm->currentId == -1) {
832 wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
833 "yet -> CONTINUE");
834 return DECISION_CONTINUE;
837 wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
838 "FAILURE");
839 return DECISION_FAILURE;
843 static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
845 return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
849 int eap_sm_step(struct eap_sm *sm)
851 int res = 0;
852 do {
853 sm->changed = FALSE;
854 SM_STEP_RUN(EAP);
855 if (sm->changed)
856 res = 1;
857 } while (sm->changed);
858 return res;
862 u8 eap_get_type(const char *name)
864 int i;
865 for (i = 0; i < NUM_EAP_METHODS; i++) {
866 if (strcmp(eap_methods[i]->name, name) == 0)
867 return eap_methods[i]->method;
869 return EAP_TYPE_NONE;
873 void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData,
874 size_t eapRespDataLen)
876 if (sm == NULL)
877 return;
878 free(sm->eapRespData);
879 sm->eapRespData = malloc(eapRespDataLen);
880 if (sm->eapRespData == NULL)
881 return;
882 memcpy(sm->eapRespData, eapRespData, eapRespDataLen);
883 sm->eapRespDataLen = eapRespDataLen;
884 wpa_hexdump(MSG_MSGDUMP, "EAP: EAP-Response received",
885 eapRespData, eapRespDataLen);
889 static void eap_user_free(struct eap_user *user)
891 if (user == NULL)
892 return;
893 free(user->password);
894 user->password = NULL;
895 free(user);
899 struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
900 struct eap_config *eap_conf)
902 struct eap_sm *sm;
904 sm = malloc(sizeof(*sm));
905 if (sm == NULL)
906 return NULL;
907 memset(sm, 0, sizeof(*sm));
908 sm->eapol_ctx = eapol_ctx;
909 sm->eapol_cb = eapol_cb;
910 sm->MaxRetrans = 10;
911 sm->ssl_ctx = eap_conf->ssl_ctx;
912 sm->eap_sim_db_priv = eap_conf->eap_sim_db_priv;
913 sm->backend_auth = eap_conf->backend_auth;
915 wpa_printf(MSG_DEBUG, "EAP: State machine created");
917 return sm;
921 void eap_sm_deinit(struct eap_sm *sm)
923 if (sm == NULL)
924 return;
925 wpa_printf(MSG_DEBUG, "EAP: State machine removed");
926 if (sm->m && sm->eap_method_priv)
927 sm->m->reset(sm, sm->eap_method_priv);
928 free(sm->eapReqData);
929 free(sm->eapKeyData);
930 free(sm->lastReqData);
931 free(sm->eapRespData);
932 free(sm->identity);
933 eap_user_free(sm->user);
934 free(sm);
938 void eap_sm_notify_cached(struct eap_sm *sm)
940 if (sm == NULL)
941 return;
943 sm->EAP_state = EAP_SUCCESS;